diff --git a/.env.example.complete b/.env.example.complete index e8520a24cae..18e7bd00d9c 100644 --- a/.env.example.complete +++ b/.env.example.complete @@ -36,10 +36,14 @@ APP_LANG=en # APP_LANG will be used if such a header is not provided. APP_AUTO_LANG_PUBLIC=true -# Application timezone -# Used where dates are displayed such as on exported content. +# Application timezones +# The first option is used to determine what timezone is used for date storage. +# Leaving that as "UTC" is advised. +# The second option is used to set the timezone which will be used for date +# formatting and display. This defaults to the "APP_TIMEZONE" value. # Valid timezone values can be found here: https://www.php.net/manual/en/timezones.php APP_TIMEZONE=UTC +APP_DISPLAY_TIMEZONE=UTC # Application theme # Used to specific a themes/ folder where BookStack UI @@ -56,6 +60,7 @@ APP_PROXIES=null # Database details # Host can contain a port (localhost:3306) or a separate DB_PORT option can be used. +# An ipv6 address can be used via the square bracket format ([::1]). DB_HOST=localhost DB_PORT=3306 DB_DATABASE=database_database @@ -215,10 +220,11 @@ LDAP_SERVER=false LDAP_BASE_DN=false LDAP_DN=false LDAP_PASS=false -LDAP_USER_FILTER=false +LDAP_USER_FILTER="(&(uid={user}))" LDAP_VERSION=false LDAP_START_TLS=false LDAP_TLS_INSECURE=false +LDAP_TLS_CA_CERT=false LDAP_ID_ATTRIBUTE=uid LDAP_EMAIL_ATTRIBUTE=mail LDAP_DISPLAY_NAME_ATTRIBUTE=cn @@ -267,6 +273,7 @@ OIDC_ISSUER_DISCOVER=false OIDC_PUBLIC_KEY=null OIDC_AUTH_ENDPOINT=null OIDC_TOKEN_ENDPOINT=null +OIDC_USERINFO_ENDPOINT=null OIDC_ADDITIONAL_SCOPES=null OIDC_DUMP_USER_DETAILS=false OIDC_USER_TO_GROUPS=false @@ -324,6 +331,19 @@ FILE_UPLOAD_SIZE_LIMIT=50 # Can be 'a4' or 'letter'. EXPORT_PAGE_SIZE=a4 +# Export PDF Command +# Set a command which can be used to convert a HTML file into a PDF file. +# When false this will not be used. +# String values represent the command to be called for conversion. +# Supports '{input_html_path}' and '{output_pdf_path}' placeholder values. +# Example: EXPORT_PDF_COMMAND="/scripts/convert.sh {input_html_path} {output_pdf_path}" +EXPORT_PDF_COMMAND=false + +# Export PDF Command Timeout +# The number of seconds that the export PDF command will run before a timeout occurs. +# Only applies for the EXPORT_PDF_COMMAND option, not for DomPDF or wkhtmltopdf. +EXPORT_PDF_COMMAND_TIMEOUT=15 + # Set path to wkhtmltopdf binary for PDF generation. # Can be 'false' or a path path like: '/home/bins/wkhtmltopdf' # When false, BookStack will attempt to find a wkhtmltopdf in the application diff --git a/.github/ISSUE_TEMPLATE/support_request.yml b/.github/ISSUE_TEMPLATE/support_request.yml index 4db8d1cedc5..ae808a0b600 100644 --- a/.github/ISSUE_TEMPLATE/support_request.yml +++ b/.github/ISSUE_TEMPLATE/support_request.yml @@ -42,6 +42,7 @@ body: label: Log Content description: If the issue has produced an error, provide any [BookStack or server log](https://www.bookstackapp.com/docs/admin/debugging/) content below. placeholder: Be sure to remove any confidential details in your logs + render: text validations: required: false - type: textarea diff --git a/.github/ISSUE_TEMPLATE/z_blank_request.yml b/.github/ISSUE_TEMPLATE/z_blank_request.yml new file mode 100644 index 00000000000..5cb5ed2a4d5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/z_blank_request.yml @@ -0,0 +1,9 @@ +name: Blank Request (Maintainers Only) +description: For maintainers only - Start a blank request +body: + - type: markdown + attributes: + value: "**This blank request option is only for existing official maintainers of the project!** Please instead use a different request option. If you use this your issue will be closed off." + - type: textarea + attributes: + label: Description \ No newline at end of file diff --git a/.github/translators.txt b/.github/translators.txt index 950c9d69598..cd08062056c 100644 --- a/.github/translators.txt +++ b/.github/translators.txt @@ -141,7 +141,7 @@ Kauê Sena (kaue.sena.ks) :: Portuguese, Brazilian MatthieuParis :: French Douradinho :: Portuguese, Brazilian; Portuguese Gaku Yaguchi (tama11) :: Japanese -johnroyer :: Chinese Traditional +Zero Huang (johnroyer) :: Chinese Traditional jackaaa :: Chinese Traditional Irfan Hukama Arsyad (IrfanArsyad) :: Indonesian Jeff Huang (s8321414) :: Chinese Traditional @@ -347,7 +347,7 @@ Taygun Yıldırım (yildirimtaygun) :: Turkish robing29 :: German Bruno Eduardo de Jesus Barroso (brunoejb) :: Portuguese, Brazilian Igor V Belousov (biv) :: Russian -David Bauer (davbauer) :: German +David Bauer (davbauer) :: German; German Informal Guttorm Hveem (guttormhveem) :: Norwegian Nynorsk; Norwegian Bokmal Minh Giang Truong (minhgiang1204) :: Vietnamese Ioannis Ioannides (i.ioannides) :: Greek @@ -374,7 +374,7 @@ balmag :: Hungarian Antti-Jussi Nygård (ajnyga) :: Finnish Eduard Ereza Martínez (Ereza) :: Catalan Jabir Lang (amar.almrad) :: Arabic -Jaroslav Koblizek (foretix) :: Czech; French +Jaroslav Kobližek (foretix) :: Czech; French Wiktor Adamczyk (adamczyk.wiktor) :: Polish Abdulmajeed Alshuaibi (4Majeed) :: Arabic NotSmartZakk :: Czech @@ -389,7 +389,119 @@ Marc Hagen (MarcHagen) :: Dutch Kasper Alsøe (zeonos) :: Danish sultani :: Persian renge :: Korean -TheGatesDev (thegatesdev) :: Dutch +Tim (thegatesdev) :: Dutch; German Informal; French; Romanian; Catalan; Czech; Danish; German; Finnish; Hungarian; Italian; Japanese; Korean; Polish; Russian; Ukrainian; Chinese Simplified; Chinese Traditional; Portuguese, Brazilian; Persian; Spanish, Argentina; Croatian; Norwegian Nynorsk; Estonian; Uzbek; Norwegian Bokmal Irdi (irdiOL) :: Albanian KateBarber :: Welsh Twister (theuncles75) :: Hebrew +algernon19 :: Hungarian +Ivan Krstic (ikrstic) :: Serbian (Cyrillic) +Show :: Russian +xBahamut :: Portuguese, Brazilian +Pavle Knežević (pavleknezzevic) :: Serbian (Cyrillic) +Vanja Cvelbar (b100w11) :: Slovenian +simonpct :: French +Honza Nagy (honza.nagy) :: Czech +asd20752 :: Norwegian Bokmal +Jan Picka (polipones) :: Czech +diogoalex991 :: Portuguese +Ehsan Sadeghi (ehsansadeghi) :: Persian +ka_picit :: Danish +cracrayol :: French +CapuaSC :: Dutch +Guardian75 :: German Informal +mr-kanister :: German +Michele Bastianelli (makoblaster) :: Italian +jespernissen :: Danish +Andrey (avmaksimov) :: Russian +Gonzalo Loyola (AlFcl) :: Spanish, Argentina; Spanish +grobert63 :: French +wusst. (Supporti) :: German +MaximMaximS :: Czech +damian-klima :: Slovak +crow_ :: Latvian +JocelynDelalande :: French +Jan (JW-CH) :: German Informal +Timo B (lommes) :: German Informal +Erik Lundstedt (Erik.Lundstedt) :: Swedish +yngams (younessmouhid) :: Arabic +Ohadp :: Hebrew +cbridi :: Portuguese, Brazilian +nanangsb :: Indonesian +Michal Melich (michalmelich) :: Czech +David (david-prv) :: German; German Informal +Larry (lahoje) :: Swedish +Marcia dos Santos (marciab80) :: Portuguese +Ricard López Torres (richilpez.torres) :: Catalan +sarahalves7 :: Portuguese, Brazilian +petr.husak :: Czech +javadataherian :: Persian +Ludo-code :: French +hollsten :: Swedish +Ngoc Lan Phung (lanpncz) :: Vietnamese +Worive :: Catalan; French +Илья Скаба (skabailya) :: Russian +Irjan Olsen (Irch) :: Norwegian Bokmal +Aleksandar Jovanovic (jovanoviczaleksandar) :: Serbian (Cyrillic) +Red (RedVortex) :: Hebrew +xgrug :: Chinese Simplified +HrCalmar :: Danish +Avishay Rapp (AvishayRapp) :: Hebrew +matthias4217 :: French +Berke BOYLU2 (berkeboylu2) :: Turkish +etwas7B :: German +Mohammed srhiri (m.sghiri20) :: Arabic +YongMin Kim (kym0118) :: Korean +Rivo Zängov (Eraser) :: Estonian +Francisco Rafael Fonseca (chicoraf) :: Portuguese, Brazilian +ИEØ_ΙΙØZ (NEO_IIOZ) :: Chinese Traditional +madnjpn (madnjpn.) :: Georgian +Ásgeir Shiny Ásgeirsson (AsgeirShiny) :: Icelandic +Mohammad Aftab Uddin (chirohorit) :: Bengali +Yannis Karlaftis (meliseus) :: Greek +felixxx :: German Informal +randi (randi65535) :: Korean +test65428 :: Greek +zeronell :: Chinese Simplified +julien Vinber (julienVinber) :: French +Hyunwoo Park (oksure) :: Korean +aram.rafeq.7 (aramrafeq2) :: Kurdish +Raphael Moreno (RaphaelMoreno) :: Portuguese, Brazilian +yn (user99) :: Arabic +Pavel Zlatarov (pzlatarov) :: Bulgarian +ingelres :: French +mabdullah :: Arabic +Skrabák Csaba (kekcsi) :: Hungarian +Evert Meulie (Evert) :: Norwegian Bokmal +Jasper Backer (jasperb) :: Dutch +Alexandar Cavdarovski (ace.200112) :: Swedish +구닥다리TV (yjj8353) :: Korean +Onur Oskay (o.oskay) :: Turkish +Sébastien Merveille (SebastienMerv) :: French +Maxim Kouznetsov (masya.work) :: Hebrew +neodvisnost :: Slovenian +Soubi Agatsuma (bisouya) :: Hebrew +Ilya Shaulov (ishaulov) :: Russian +Konstantin Bobkov (b.konstantv) :: Russian +Ruben Sutter (rubensutter) :: German +jellium :: French +Qxlkdr :: Swedish +Hari (muhhari) :: Indonesian +仙君御 (xjy) :: Chinese Simplified +TapioM :: Finnish +lingb58 :: Chinese Traditional +Angel Pandey (angel-pandey) :: Nepali +Supriya Shrestha (supriyashrestha) :: Nepali +gprabhat :: Nepali +CellCat :: Chinese Simplified +Al Desrahim (aldesrahim) :: Indonesian +ahmad abbaspour (deshneh.dar.diss) :: Persian +Erjon K. (ekr) :: Albanian +LiZerui (iamzrli) :: Chinese Traditional +Ticker (ticker.com) :: Hebrew +CrazyComputer :: Chinese Simplified +Firr (FirrV) :: Russian +João Faro (FaroJoaoFaro) :: Portuguese +Danilo dos Santos Barbosa (bozochegou) :: Portuguese, Brazilian +Chris (furesoft) :: German +Silvia Isern (eiendragon) :: Catalan +Dennis Kron Pedersen (ahjdp) :: Danish diff --git a/.github/workflows/analyse-php.yml b/.github/workflows/analyse-php.yml index 4ddf519328c..647835aeb2f 100644 --- a/.github/workflows/analyse-php.yml +++ b/.github/workflows/analyse-php.yml @@ -11,14 +11,14 @@ on: jobs: build: if: ${{ github.ref != 'refs/heads/l10n_development' }} - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: 8.1 + php-version: 8.3 extensions: gd, mbstring, json, curl, xml, mysql, ldap - name: Get Composer Cache Directory @@ -27,10 +27,10 @@ jobs: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Cache composer packages - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ${{ steps.composer-cache.outputs.dir }} - key: ${{ runner.os }}-composer-8.1 + key: ${{ runner.os }}-composer-8.3 restore-keys: ${{ runner.os }}-composer- - name: Install composer dependencies diff --git a/.github/workflows/lint-js.yml b/.github/workflows/lint-js.yml index 0391ce5b5c6..9aceea2a26c 100644 --- a/.github/workflows/lint-js.yml +++ b/.github/workflows/lint-js.yml @@ -13,9 +13,9 @@ on: jobs: build: if: ${{ github.ref != 'refs/heads/l10n_development' }} - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v4 - name: Install NPM deps run: npm ci diff --git a/.github/workflows/lint-php.yml b/.github/workflows/lint-php.yml index b2f8b3d2b4a..cb9dedcb25a 100644 --- a/.github/workflows/lint-php.yml +++ b/.github/workflows/lint-php.yml @@ -11,14 +11,14 @@ on: jobs: build: if: ${{ github.ref != 'refs/heads/l10n_development' }} - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: 8.1 + php-version: 8.3 tools: phpcs - name: Run formatting check diff --git a/.github/workflows/test-js.yml b/.github/workflows/test-js.yml new file mode 100644 index 00000000000..13f9a8a9819 --- /dev/null +++ b/.github/workflows/test-js.yml @@ -0,0 +1,29 @@ +name: test-js + +on: + push: + paths: + - '**.js' + - '**.ts' + - '**.json' + pull_request: + paths: + - '**.js' + - '**.ts' + - '**.json' + +jobs: + build: + if: ${{ github.ref != 'refs/heads/l10n_development' }} + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + + - name: Install NPM deps + run: npm ci + + - name: Run TypeScript type checking + run: npm run ts:lint + + - name: Run JavaScript tests + run: npm run test \ No newline at end of file diff --git a/.github/workflows/test-migrations.yml b/.github/workflows/test-migrations.yml index 63f52a29506..23e58a772c2 100644 --- a/.github/workflows/test-migrations.yml +++ b/.github/workflows/test-migrations.yml @@ -13,12 +13,12 @@ on: jobs: build: if: ${{ github.ref != 'refs/heads/l10n_development' }} - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 strategy: matrix: - php: ['8.0', '8.1', '8.2', '8.3'] + php: ['8.2', '8.3', '8.4'] steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 @@ -32,7 +32,7 @@ jobs: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Cache composer packages - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ${{ steps.composer-cache.outputs.dir }} key: ${{ runner.os }}-composer-${{ matrix.php }} diff --git a/.github/workflows/test-php.yml b/.github/workflows/test-php.yml index 89ebf0deceb..64132f673a4 100644 --- a/.github/workflows/test-php.yml +++ b/.github/workflows/test-php.yml @@ -13,12 +13,12 @@ on: jobs: build: if: ${{ github.ref != 'refs/heads/l10n_development' }} - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 strategy: matrix: - php: ['8.0', '8.1', '8.2', '8.3'] + php: ['8.2', '8.3', '8.4'] steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 @@ -32,7 +32,7 @@ jobs: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Cache composer packages - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ${{ steps.composer-cache.outputs.dir }} key: ${{ runner.os }}-composer-${{ matrix.php }} diff --git a/.gitignore b/.gitignore index a8d2e6af7d5..e0cc3229538 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ /node_modules /.vscode /composer +/coverage Homestead.yaml .env .idea @@ -31,3 +32,4 @@ webpack-stats.json phpstan.neon esbuild-meta.json .phpactor.json +/*.zip diff --git a/LICENSE b/LICENSE index 7727542f4ae..9b94d9be3f3 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2015-2023, Dan Brown and the BookStack Project contributors. +Copyright (c) 2015-2025, Dan Brown and the BookStack project contributors. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/app/Access/Controllers/ConfirmEmailController.php b/app/Access/Controllers/ConfirmEmailController.php index 94647e06e59..d71b8f45068 100644 --- a/app/Access/Controllers/ConfirmEmailController.php +++ b/app/Access/Controllers/ConfirmEmailController.php @@ -32,13 +32,17 @@ public function show() /** * Shows a notice that a user's email address has not been confirmed, - * Also has the option to re-send the confirmation email. + * along with the option to re-send the confirmation email. */ public function showAwaiting() { $user = $this->loginService->getLastLoginAttemptUser(); + if ($user === null) { + $this->showErrorNotification(trans('errors.login_user_not_found')); + return redirect('/login'); + } - return view('auth.user-unconfirmed', ['user' => $user]); + return view('auth.register-confirm-awaiting'); } /** @@ -90,19 +94,24 @@ public function confirm(Request $request) /** * Resend the confirmation email. */ - public function resend(Request $request) + public function resend() { - $this->validate($request, [ - 'email' => ['required', 'email', 'exists:users,email'], - ]); - $user = $this->userRepo->getByEmail($request->get('email')); + $user = $this->loginService->getLastLoginAttemptUser(); + if ($user === null) { + $this->showErrorNotification(trans('errors.login_user_not_found')); + return redirect('/login'); + } try { $this->emailConfirmationService->sendConfirmation($user); + } catch (ConfirmationEmailException $e) { + $this->showErrorNotification($e->getMessage()); + + return redirect('/login'); } catch (Exception $e) { $this->showErrorNotification(trans('auth.email_confirm_send_error')); - return redirect('/register/confirm'); + return redirect('/register/awaiting'); } $this->showSuccessNotification(trans('auth.email_confirm_resent')); diff --git a/app/Access/Controllers/ForgotPasswordController.php b/app/Access/Controllers/ForgotPasswordController.php index 86fbe8fa367..36dd977558b 100644 --- a/app/Access/Controllers/ForgotPasswordController.php +++ b/app/Access/Controllers/ForgotPasswordController.php @@ -6,6 +6,7 @@ use BookStack\Http\Controller; use Illuminate\Http\Request; use Illuminate\Support\Facades\Password; +use Illuminate\Support\Sleep; class ForgotPasswordController extends Controller { @@ -32,6 +33,10 @@ public function sendResetLinkEmail(Request $request) 'email' => ['required', 'email'], ]); + // Add random pause to the response to help avoid time-base sniffing + // of valid resets via slower email send handling. + Sleep::for(random_int(1000, 3000))->milliseconds(); + // We will send the password reset link to this user. Once we have attempted // to send the link, we will examine the response then see the message we // need to show to the user. Finally, we'll send out a proper response. diff --git a/app/Access/Controllers/HandlesPartialLogins.php b/app/Access/Controllers/HandlesPartialLogins.php index c5554e47300..47a63d19b0c 100644 --- a/app/Access/Controllers/HandlesPartialLogins.php +++ b/app/Access/Controllers/HandlesPartialLogins.php @@ -17,7 +17,7 @@ protected function currentOrLastAttemptedUser(): User $user = auth()->user() ?? $loginService->getLastLoginAttemptUser(); if (!$user) { - throw new NotFoundException('A user for this action could not be found'); + throw new NotFoundException(trans('errors.login_user_not_found')); } return $user; diff --git a/app/Access/Controllers/MfaTotpController.php b/app/Access/Controllers/MfaTotpController.php index f60644b84cc..5202fedc04f 100644 --- a/app/Access/Controllers/MfaTotpController.php +++ b/app/Access/Controllers/MfaTotpController.php @@ -19,20 +19,25 @@ class MfaTotpController extends Controller protected const SETUP_SECRET_SESSION_KEY = 'mfa-setup-totp-secret'; + public function __construct( + protected TotpService $totp + ) { + } + /** * Show a view that generates and displays a TOTP QR code. */ - public function generate(TotpService $totp) + public function generate() { if (session()->has(static::SETUP_SECRET_SESSION_KEY)) { $totpSecret = decrypt(session()->get(static::SETUP_SECRET_SESSION_KEY)); } else { - $totpSecret = $totp->generateSecret(); + $totpSecret = $this->totp->generateSecret(); session()->put(static::SETUP_SECRET_SESSION_KEY, encrypt($totpSecret)); } - $qrCodeUrl = $totp->generateUrl($totpSecret, $this->currentOrLastAttemptedUser()); - $svg = $totp->generateQrCodeSvg($qrCodeUrl); + $qrCodeUrl = $this->totp->generateUrl($totpSecret, $this->currentOrLastAttemptedUser()); + $svg = $this->totp->generateQrCodeSvg($qrCodeUrl); $this->setPageTitle(trans('auth.mfa_gen_totp_title')); @@ -56,7 +61,7 @@ public function confirm(Request $request) 'code' => [ 'required', 'max:12', 'min:4', - new TotpValidationRule($totpSecret), + new TotpValidationRule($totpSecret, $this->totp), ], ]); @@ -87,7 +92,7 @@ public function verify(Request $request, LoginService $loginService, MfaSession 'code' => [ 'required', 'max:12', 'min:4', - new TotpValidationRule($totpSecret), + new TotpValidationRule($totpSecret, $this->totp), ], ]); diff --git a/app/Access/Controllers/RegisterController.php b/app/Access/Controllers/RegisterController.php index 13b97f03c96..e9812aa5d06 100644 --- a/app/Access/Controllers/RegisterController.php +++ b/app/Access/Controllers/RegisterController.php @@ -15,24 +15,13 @@ class RegisterController extends Controller { - protected SocialDriverManager $socialDriverManager; - protected RegistrationService $registrationService; - protected LoginService $loginService; - - /** - * Create a new controller instance. - */ public function __construct( - SocialDriverManager $socialDriverManager, - RegistrationService $registrationService, - LoginService $loginService + protected SocialDriverManager $socialDriverManager, + protected RegistrationService $registrationService, + protected LoginService $loginService ) { $this->middleware('guest'); $this->middleware('guard:standard'); - - $this->socialDriverManager = $socialDriverManager; - $this->registrationService = $registrationService; - $this->loginService = $loginService; } /** @@ -87,6 +76,8 @@ protected function validator(array $data): ValidatorContract 'name' => ['required', 'min:2', 'max:100'], 'email' => ['required', 'email', 'max:255', 'unique:users'], 'password' => ['required', Password::default()], + // Basic honey for bots that must not be filled in + 'username' => ['prohibited'], ]); } } diff --git a/app/Access/Controllers/ResetPasswordController.php b/app/Access/Controllers/ResetPasswordController.php index a8a45dddf1f..3af65d17fb6 100644 --- a/app/Access/Controllers/ResetPasswordController.php +++ b/app/Access/Controllers/ResetPasswordController.php @@ -15,14 +15,11 @@ class ResetPasswordController extends Controller { - protected LoginService $loginService; - - public function __construct(LoginService $loginService) - { + public function __construct( + protected LoginService $loginService + ) { $this->middleware('guest'); $this->middleware('guard:standard'); - - $this->loginService = $loginService; } /** diff --git a/app/Access/EmailConfirmationService.php b/app/Access/EmailConfirmationService.php index de9ea69b882..1a5156d3e7a 100644 --- a/app/Access/EmailConfirmationService.php +++ b/app/Access/EmailConfirmationService.php @@ -17,7 +17,7 @@ class EmailConfirmationService extends UserTokenService * * @throws ConfirmationEmailException */ - public function sendConfirmation(User $user) + public function sendConfirmation(User $user): void { if ($user->email_confirmed) { throw new ConfirmationEmailException(trans('errors.email_already_confirmed'), '/login'); diff --git a/app/Access/ExternalBaseUserProvider.php b/app/Access/ExternalBaseUserProvider.php index d3ece983fb8..2165fd4591e 100644 --- a/app/Access/ExternalBaseUserProvider.php +++ b/app/Access/ExternalBaseUserProvider.php @@ -2,60 +2,26 @@ namespace BookStack\Access; +use BookStack\Users\Models\User; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Contracts\Auth\UserProvider; -use Illuminate\Database\Eloquent\Model; class ExternalBaseUserProvider implements UserProvider { - /** - * The user model. - * - * @var string - */ - protected $model; - - /** - * LdapUserProvider constructor. - */ - public function __construct(string $model) - { - $this->model = $model; - } - - /** - * Create a new instance of the model. - * - * @return Model - */ - public function createModel() - { - $class = '\\' . ltrim($this->model, '\\'); - - return new $class(); - } - /** * Retrieve a user by their unique identifier. - * - * @param mixed $identifier - * - * @return Authenticatable|null */ - public function retrieveById($identifier) + public function retrieveById(mixed $identifier): ?Authenticatable { - return $this->createModel()->newQuery()->find($identifier); + return User::query()->find($identifier); } /** * Retrieve a user by their unique identifier and "remember me" token. * - * @param mixed $identifier * @param string $token - * - * @return Authenticatable|null */ - public function retrieveByToken($identifier, $token) + public function retrieveByToken(mixed $identifier, $token): null { return null; } @@ -75,32 +41,25 @@ public function updateRememberToken(Authenticatable $user, $token) /** * Retrieve a user by the given credentials. - * - * @param array $credentials - * - * @return Authenticatable|null */ - public function retrieveByCredentials(array $credentials) + public function retrieveByCredentials(array $credentials): ?Authenticatable { - // Search current user base by looking up a uid - $model = $this->createModel(); - - return $model->newQuery() + return User::query() ->where('external_auth_id', $credentials['external_auth_id']) ->first(); } /** * Validate a user against the given credentials. - * - * @param Authenticatable $user - * @param array $credentials - * - * @return bool */ - public function validateCredentials(Authenticatable $user, array $credentials) + public function validateCredentials(Authenticatable $user, array $credentials): bool { // Should be done in the guard. return false; } + + public function rehashPasswordIfRequired(Authenticatable $user, #[\SensitiveParameter] array $credentials, bool $force = false) + { + // No action to perform, any passwords are external in the auth system + } } diff --git a/app/Access/Guards/AsyncExternalBaseSessionGuard.php b/app/Access/Guards/AsyncExternalBaseSessionGuard.php index ab982a6b088..b66fbe95e09 100644 --- a/app/Access/Guards/AsyncExternalBaseSessionGuard.php +++ b/app/Access/Guards/AsyncExternalBaseSessionGuard.php @@ -3,23 +3,18 @@ namespace BookStack\Access\Guards; /** - * Saml2 Session Guard. + * External Auth Session Guard. * - * The saml2 login process is async in nature meaning it does not fit very well - * into the default laravel 'Guard' auth flow. Instead most of the logic is done - * via the Saml2 controller & Saml2Service. This class provides a safer, thin - * version of SessionGuard. + * The login process for external auth (SAML2/OIDC) is async in nature, meaning it does not fit very well + * into the default laravel 'Guard' auth flow. Instead, most of the logic is done via the relevant + * controller and services. This class provides a safer, thin version of SessionGuard. */ class AsyncExternalBaseSessionGuard extends ExternalBaseSessionGuard { /** * Validate a user's credentials. - * - * @param array $credentials - * - * @return bool */ - public function validate(array $credentials = []) + public function validate(array $credentials = []): bool { return false; } @@ -27,12 +22,9 @@ public function validate(array $credentials = []) /** * Attempt to authenticate a user using the given credentials. * - * @param array $credentials * @param bool $remember - * - * @return bool */ - public function attempt(array $credentials = [], $remember = false) + public function attempt(array $credentials = [], $remember = false): bool { return false; } diff --git a/app/Access/Guards/ExternalBaseSessionGuard.php b/app/Access/Guards/ExternalBaseSessionGuard.php index 2d15422a5ae..91239599ba9 100644 --- a/app/Access/Guards/ExternalBaseSessionGuard.php +++ b/app/Access/Guards/ExternalBaseSessionGuard.php @@ -4,7 +4,7 @@ use BookStack\Access\RegistrationService; use Illuminate\Auth\GuardHelpers; -use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; +use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Contracts\Auth\StatefulGuard; use Illuminate\Contracts\Auth\UserProvider; use Illuminate\Contracts\Session\Session; @@ -24,43 +24,31 @@ class ExternalBaseSessionGuard implements StatefulGuard * The name of the Guard. Typically "session". * * Corresponds to guard name in authentication configuration. - * - * @var string */ - protected $name; + protected readonly string $name; /** * The user we last attempted to retrieve. - * - * @var \Illuminate\Contracts\Auth\Authenticatable */ - protected $lastAttempted; + protected Authenticatable|null $lastAttempted; /** * The session used by the guard. - * - * @var \Illuminate\Contracts\Session\Session */ - protected $session; + protected Session $session; /** * Indicates if the logout method has been called. - * - * @var bool */ - protected $loggedOut = false; + protected bool $loggedOut = false; /** * Service to handle common registration actions. - * - * @var RegistrationService */ - protected $registrationService; + protected RegistrationService $registrationService; /** * Create a new authentication guard. - * - * @return void */ public function __construct(string $name, UserProvider $provider, Session $session, RegistrationService $registrationService) { @@ -72,13 +60,11 @@ public function __construct(string $name, UserProvider $provider, Session $sessi /** * Get the currently authenticated user. - * - * @return \Illuminate\Contracts\Auth\Authenticatable|null */ - public function user() + public function user(): Authenticatable|null { if ($this->loggedOut) { - return; + return null; } // If we've already retrieved the user for the current request we can just @@ -101,13 +87,11 @@ public function user() /** * Get the ID for the currently authenticated user. - * - * @return int|null */ - public function id() + public function id(): int|null { if ($this->loggedOut) { - return; + return null; } return $this->user() @@ -117,12 +101,8 @@ public function id() /** * Log a user into the application without sessions or cookies. - * - * @param array $credentials - * - * @return bool */ - public function once(array $credentials = []) + public function once(array $credentials = []): bool { if ($this->validate($credentials)) { $this->setUser($this->lastAttempted); @@ -135,12 +115,8 @@ public function once(array $credentials = []) /** * Log the given user ID into the application without sessions or cookies. - * - * @param mixed $id - * - * @return \Illuminate\Contracts\Auth\Authenticatable|false */ - public function onceUsingId($id) + public function onceUsingId($id): Authenticatable|false { if (!is_null($user = $this->provider->retrieveById($id))) { $this->setUser($user); @@ -153,38 +129,26 @@ public function onceUsingId($id) /** * Validate a user's credentials. - * - * @param array $credentials - * - * @return bool */ - public function validate(array $credentials = []) + public function validate(array $credentials = []): bool { return false; } /** * Attempt to authenticate a user using the given credentials. - * - * @param array $credentials - * @param bool $remember - * - * @return bool + * @param bool $remember */ - public function attempt(array $credentials = [], $remember = false) + public function attempt(array $credentials = [], $remember = false): bool { return false; } /** * Log the given user ID into the application. - * - * @param mixed $id * @param bool $remember - * - * @return \Illuminate\Contracts\Auth\Authenticatable|false */ - public function loginUsingId($id, $remember = false) + public function loginUsingId(mixed $id, $remember = false): Authenticatable|false { // Always return false as to disable this method, // Logins should route through LoginService. @@ -194,12 +158,9 @@ public function loginUsingId($id, $remember = false) /** * Log a user into the application. * - * @param \Illuminate\Contracts\Auth\Authenticatable $user - * @param bool $remember - * - * @return void + * @param bool $remember */ - public function login(AuthenticatableContract $user, $remember = false) + public function login(Authenticatable $user, $remember = false): void { $this->updateSession($user->getAuthIdentifier()); @@ -208,12 +169,8 @@ public function login(AuthenticatableContract $user, $remember = false) /** * Update the session with the given ID. - * - * @param string $id - * - * @return void */ - protected function updateSession($id) + protected function updateSession(string|int $id): void { $this->session->put($this->getName(), $id); @@ -222,10 +179,8 @@ protected function updateSession($id) /** * Log the user out of the application. - * - * @return void */ - public function logout() + public function logout(): void { $this->clearUserDataFromStorage(); @@ -239,62 +194,48 @@ public function logout() /** * Remove the user data from the session and cookies. - * - * @return void */ - protected function clearUserDataFromStorage() + protected function clearUserDataFromStorage(): void { $this->session->remove($this->getName()); } /** * Get the last user we attempted to authenticate. - * - * @return \Illuminate\Contracts\Auth\Authenticatable */ - public function getLastAttempted() + public function getLastAttempted(): Authenticatable { return $this->lastAttempted; } /** * Get a unique identifier for the auth session value. - * - * @return string */ - public function getName() + public function getName(): string { return 'login_' . $this->name . '_' . sha1(static::class); } /** * Determine if the user was authenticated via "remember me" cookie. - * - * @return bool */ - public function viaRemember() + public function viaRemember(): bool { return false; } /** * Return the currently cached user. - * - * @return \Illuminate\Contracts\Auth\Authenticatable|null */ - public function getUser() + public function getUser(): Authenticatable|null { return $this->user; } /** * Set the current user. - * - * @param \Illuminate\Contracts\Auth\Authenticatable $user - * - * @return $this */ - public function setUser(AuthenticatableContract $user) + public function setUser(Authenticatable $user): self { $this->user = $user; diff --git a/app/Access/Guards/LdapSessionGuard.php b/app/Access/Guards/LdapSessionGuard.php index a417d3e61cb..9455d530dfe 100644 --- a/app/Access/Guards/LdapSessionGuard.php +++ b/app/Access/Guards/LdapSessionGuard.php @@ -35,13 +35,9 @@ public function __construct( /** * Validate a user's credentials. * - * @param array $credentials - * * @throws LdapException - * - * @return bool */ - public function validate(array $credentials = []) + public function validate(array $credentials = []): bool { $userDetails = $this->ldapService->getUserDetails($credentials['username']); @@ -57,16 +53,13 @@ public function validate(array $credentials = []) /** * Attempt to authenticate a user using the given credentials. * - * @param array $credentials * @param bool $remember * - * @throws LdapException*@throws \BookStack\Exceptions\JsonDebugException + * @throws LdapException * @throws LoginAttemptException * @throws JsonDebugException - * - * @return bool */ - public function attempt(array $credentials = [], $remember = false) + public function attempt(array $credentials = [], $remember = false): bool { $username = $credentials['username']; $userDetails = $this->ldapService->getUserDetails($username); diff --git a/app/Access/Ldap.php b/app/Access/Ldap.php index 12a3d1e7122..d14f688218c 100644 --- a/app/Access/Ldap.php +++ b/app/Access/Ldap.php @@ -52,13 +52,25 @@ public function setVersion($ldapConnection, int $version): bool * * @param resource|\LDAP\Connection $ldapConnection * - * @return resource|\LDAP\Result + * @return \LDAP\Result|array|false */ - public function search($ldapConnection, string $baseDn, string $filter, array $attributes = null) + public function search($ldapConnection, string $baseDn, string $filter, array $attributes = []) { return ldap_search($ldapConnection, $baseDn, $filter, $attributes); } + /** + * Read an entry from the LDAP tree. + * + * @param resource|\Ldap\Connection $ldapConnection + * + * @return \LDAP\Result|array|false + */ + public function read($ldapConnection, string $baseDn, string $filter, array $attributes = []) + { + return ldap_read($ldapConnection, $baseDn, $filter, $attributes); + } + /** * Get entries from an LDAP search result. * @@ -75,7 +87,7 @@ public function getEntries($ldapConnection, $ldapSearchResult): array|false * * @param resource|\LDAP\Connection $ldapConnection */ - public function searchAndGetEntries($ldapConnection, string $baseDn, string $filter, array $attributes = null): array|false + public function searchAndGetEntries($ldapConnection, string $baseDn, string $filter, array $attributes = []): array|false { $search = $this->search($ldapConnection, $baseDn, $filter, $attributes); @@ -87,7 +99,7 @@ public function searchAndGetEntries($ldapConnection, string $baseDn, string $fil * * @param resource|\LDAP\Connection $ldapConnection */ - public function bind($ldapConnection, string $bindRdn = null, string $bindPassword = null): bool + public function bind($ldapConnection, ?string $bindRdn = null, ?string $bindPassword = null): bool { return ldap_bind($ldapConnection, $bindRdn, $bindPassword); } diff --git a/app/Access/LdapService.php b/app/Access/LdapService.php index 9d266763531..0f456efc247 100644 --- a/app/Access/LdapService.php +++ b/app/Access/LdapService.php @@ -71,6 +71,26 @@ private function getUserWithAttributes(string $userName, array $attributes): ?ar return $users[0]; } + /** + * Build the user display name from the (potentially multiple) attributes defined by the configuration. + */ + protected function getUserDisplayName(array $userDetails, array $displayNameAttrs, string $defaultValue): string + { + $displayNameParts = []; + foreach ($displayNameAttrs as $dnAttr) { + $dnComponent = $this->getUserResponseProperty($userDetails, $dnAttr, null); + if ($dnComponent) { + $displayNameParts[] = $dnComponent; + } + } + + if (empty($displayNameParts)) { + return $defaultValue; + } + + return implode(' ', $displayNameParts); + } + /** * Get the details of a user from LDAP using the given username. * User found via configurable user filter. @@ -81,21 +101,25 @@ public function getUserDetails(string $userName): ?array { $idAttr = $this->config['id_attribute']; $emailAttr = $this->config['email_attribute']; - $displayNameAttr = $this->config['display_name_attribute']; + $displayNameAttrs = explode('|', $this->config['display_name_attribute']); $thumbnailAttr = $this->config['thumbnail_attribute']; $user = $this->getUserWithAttributes($userName, array_filter([ - 'cn', 'dn', $idAttr, $emailAttr, $displayNameAttr, $thumbnailAttr, + 'cn', 'dn', $idAttr, $emailAttr, ...$displayNameAttrs, $thumbnailAttr, ])); if (is_null($user)) { return null; } - $userCn = $this->getUserResponseProperty($user, 'cn', null); + $nameDefault = $this->getUserResponseProperty($user, 'cn', null); + if (is_null($nameDefault)) { + $nameDefault = ldap_explode_dn($user['dn'], 1)[0] ?? $user['dn']; + } + $formatted = [ 'uid' => $this->getUserResponseProperty($user, $idAttr, $user['dn']), - 'name' => $this->getUserResponseProperty($user, $displayNameAttr, $userCn), + 'name' => $this->getUserDisplayName($user, $displayNameAttrs, $nameDefault), 'dn' => $user['dn'], 'email' => $this->getUserResponseProperty($user, $emailAttr, null), 'avatar' => $thumbnailAttr ? $this->getUserResponseProperty($user, $thumbnailAttr, null) : null, @@ -209,6 +233,12 @@ protected function getConnection() $this->ldap->setOption(null, LDAP_OPT_X_TLS_REQUIRE_CERT, LDAP_OPT_X_TLS_NEVER); } + // Configure any user-provided CA cert files for LDAP. + // This option works globally and must be set before a connection is created. + if ($this->config['tls_ca_cert']) { + $this->configureTlsCaCerts($this->config['tls_ca_cert']); + } + $ldapHost = $this->parseServerString($this->config['server']); $ldapConnection = $this->ldap->connect($ldapHost); @@ -223,7 +253,14 @@ protected function getConnection() // Start and verify TLS if it's enabled if ($this->config['start_tls']) { - $started = $this->ldap->startTls($ldapConnection); + try { + $started = $this->ldap->startTls($ldapConnection); + } catch (\Exception $exception) { + $error = $exception->getMessage() . ' :: ' . ldap_error($ldapConnection); + ldap_get_option($ldapConnection, LDAP_OPT_DIAGNOSTIC_MESSAGE, $detail); + Log::info("LDAP STARTTLS failure: {$error} {$detail}"); + throw new LdapException('Could not start TLS connection. Further details in the application log.'); + } if (!$started) { throw new LdapException('Could not start TLS connection'); } @@ -234,6 +271,33 @@ protected function getConnection() return $this->ldapConnection; } + /** + * Configure TLS CA certs globally for ldap use. + * This will detect if the given path is a directory or file, and set the relevant + * LDAP TLS options appropriately otherwise throw an exception if no file/folder found. + * + * Note: When using a folder, certificates are expected to be correctly named by hash + * which can be done via the c_rehash utility. + * + * @throws LdapException + */ + protected function configureTlsCaCerts(string $caCertPath): void + { + $errMessage = "Provided path [{$caCertPath}] for LDAP TLS CA certs could not be resolved to an existing location"; + $path = realpath($caCertPath); + if ($path === false) { + throw new LdapException($errMessage); + } + + if (is_dir($path)) { + $this->ldap->setOption(null, LDAP_OPT_X_TLS_CACERTDIR, $path); + } else if (is_file($path)) { + $this->ldap->setOption(null, LDAP_OPT_X_TLS_CACERTFILE, $path); + } else { + throw new LdapException($errMessage); + } + } + /** * Parse an LDAP server string and return the host suitable for a connection. * Is flexible to formats such as 'ldap.example.com:8069' or 'ldaps://ldap.example.com'. @@ -249,13 +313,18 @@ protected function parseServerString(string $serverString): string /** * Build a filter string by injecting common variables. + * Both "${var}" and "{var}" style placeholders are supported. + * Dollar based are old format but supported for compatibility. */ protected function buildFilter(string $filterString, array $attrs): string { $newAttrs = []; foreach ($attrs as $key => $attrText) { - $newKey = '${' . $key . '}'; - $newAttrs[$newKey] = $this->ldap->escape($attrText); + $escapedText = $this->ldap->escape($attrText); + $oldVarKey = '${' . $key . '}'; + $newVarKey = '{' . $key . '}'; + $newAttrs[$oldVarKey] = $escapedText; + $newAttrs[$newVarKey] = $escapedText; } return strtr($filterString, $newAttrs); @@ -276,94 +345,105 @@ public function getUserGroups(string $userName): array return []; } - $userGroups = $this->groupFilter($user); + $userGroups = $this->extractGroupsFromSearchResponseEntry($user); $allGroups = $this->getGroupsRecursive($userGroups, []); + $formattedGroups = $this->extractGroupNamesFromLdapGroupDns($allGroups); if ($this->config['dump_user_groups']) { throw new JsonDebugException([ - 'details_from_ldap' => $user, - 'parsed_direct_user_groups' => $userGroups, - 'parsed_recursive_user_groups' => $allGroups, + 'details_from_ldap' => $user, + 'parsed_direct_user_groups' => $userGroups, + 'parsed_recursive_user_groups' => $allGroups, + 'parsed_resulting_group_names' => $formattedGroups, ]); } - return $allGroups; + return $formattedGroups; + } + + protected function extractGroupNamesFromLdapGroupDns(array $groupDNs): array + { + $names = []; + + foreach ($groupDNs as $groupDN) { + $exploded = $this->ldap->explodeDn($groupDN, 1); + if ($exploded !== false && count($exploded) > 0) { + $names[] = $exploded[0]; + } + } + + return array_unique($names); } /** - * Get the parent groups of an array of groups. + * Build an array of all relevant groups DNs after recursively scanning + * across parents of the groups given. * * @throws LdapException */ - private function getGroupsRecursive(array $groupsArray, array $checked): array + protected function getGroupsRecursive(array $groupDNs, array $checked): array { $groupsToAdd = []; - foreach ($groupsArray as $groupName) { - if (in_array($groupName, $checked)) { + foreach ($groupDNs as $groupDN) { + if (in_array($groupDN, $checked)) { continue; } - $parentGroups = $this->getGroupGroups($groupName); + $parentGroups = $this->getParentsOfGroup($groupDN); $groupsToAdd = array_merge($groupsToAdd, $parentGroups); - $checked[] = $groupName; + $checked[] = $groupDN; } - $groupsArray = array_unique(array_merge($groupsArray, $groupsToAdd), SORT_REGULAR); + $uniqueDNs = array_unique(array_merge($groupDNs, $groupsToAdd), SORT_REGULAR); if (empty($groupsToAdd)) { - return $groupsArray; + return $uniqueDNs; } - return $this->getGroupsRecursive($groupsArray, $checked); + return $this->getGroupsRecursive($uniqueDNs, $checked); } /** - * Get the parent groups of a single group. - * * @throws LdapException */ - private function getGroupGroups(string $groupName): array + protected function getParentsOfGroup(string $groupDN): array { + $groupsAttr = strtolower($this->config['group_attribute']); $ldapConnection = $this->getConnection(); $this->bindSystemUser($ldapConnection); $followReferrals = $this->config['follow_referrals'] ? 1 : 0; $this->ldap->setOption($ldapConnection, LDAP_OPT_REFERRALS, $followReferrals); - - $baseDn = $this->config['base_dn']; - $groupsAttr = strtolower($this->config['group_attribute']); - - $groupFilter = 'CN=' . $this->ldap->escape($groupName); - $groups = $this->ldap->searchAndGetEntries($ldapConnection, $baseDn, $groupFilter, [$groupsAttr]); - if ($groups['count'] === 0) { + $read = $this->ldap->read($ldapConnection, $groupDN, '(objectClass=*)', [$groupsAttr]); + $results = $this->ldap->getEntries($ldapConnection, $read); + if ($results['count'] === 0) { return []; } - return $this->groupFilter($groups[0]); + return $this->extractGroupsFromSearchResponseEntry($results[0]); } /** - * Filter out LDAP CN and DN language in a ldap search return. - * Gets the base CN (common name) of the string. + * Extract an array of group DN values from the given LDAP search response entry */ - protected function groupFilter(array $userGroupSearchResponse): array + protected function extractGroupsFromSearchResponseEntry(array $ldapEntry): array { $groupsAttr = strtolower($this->config['group_attribute']); - $ldapGroups = []; + $groupDNs = []; $count = 0; - if (isset($userGroupSearchResponse[$groupsAttr]['count'])) { - $count = (int) $userGroupSearchResponse[$groupsAttr]['count']; + if (isset($ldapEntry[$groupsAttr]['count'])) { + $count = (int) $ldapEntry[$groupsAttr]['count']; } for ($i = 0; $i < $count; $i++) { - $dnComponents = $this->ldap->explodeDn($userGroupSearchResponse[$groupsAttr][$i], 1); - if (!in_array($dnComponents[0], $ldapGroups)) { - $ldapGroups[] = $dnComponents[0]; + $dn = $ldapEntry[$groupsAttr][$i]; + if (!in_array($dn, $groupDNs)) { + $groupDNs[] = $dn; } } - return $ldapGroups; + return $groupDNs; } /** diff --git a/app/Access/LoginService.php b/app/Access/LoginService.php index cc48e0f9bab..c81e955722c 100644 --- a/app/Access/LoginService.php +++ b/app/Access/LoginService.php @@ -5,9 +5,11 @@ use BookStack\Access\Mfa\MfaSession; use BookStack\Activity\ActivityType; use BookStack\Exceptions\LoginAttemptException; +use BookStack\Exceptions\LoginAttemptInvalidUserException; use BookStack\Exceptions\StoppedAuthenticationException; use BookStack\Facades\Activity; use BookStack\Facades\Theme; +use BookStack\Permissions\Permission; use BookStack\Theming\ThemeEvents; use BookStack\Users\Models\User; use Exception; @@ -29,10 +31,14 @@ public function __construct( * a reason to (MFA or Unconfirmed Email). * Returns a boolean to indicate the current login result. * - * @throws StoppedAuthenticationException + * @throws StoppedAuthenticationException|LoginAttemptInvalidUserException */ public function login(User $user, string $method, bool $remember = false): void { + if ($user->isGuest()) { + throw new LoginAttemptInvalidUserException('Login not allowed for guest user'); + } + if ($this->awaitingEmailConfirmation($user) || $this->needsMfaVerification($user)) { $this->setLastLoginAttemptedForUser($user, $method, $remember); @@ -45,7 +51,7 @@ public function login(User $user, string $method, bool $remember = false): void Theme::dispatch(ThemeEvents::AUTH_LOGIN, $method, $user); // Authenticate on all session guards if a likely admin - if ($user->can('users-manage') && $user->can('user-roles-manage')) { + if ($user->can(Permission::UsersManage) && $user->can(Permission::UserRolesManage)) { $guards = ['standard', 'ldap', 'saml2', 'oidc']; foreach ($guards as $guard) { auth($guard)->login($user); @@ -58,7 +64,7 @@ public function login(User $user, string $method, bool $remember = false): void * * @throws Exception */ - public function reattemptLoginFor(User $user) + public function reattemptLoginFor(User $user): void { if ($user->id !== ($this->getLastLoginAttemptUser()->id ?? null)) { throw new Exception('Login reattempt user does align with current session state'); @@ -90,7 +96,7 @@ protected function getLastLoginAttemptDetails(): array { $value = session()->get(self::LAST_LOGIN_ATTEMPTED_SESSION_KEY); if (!$value) { - return ['user_id' => null, 'method' => null]; + return ['user_id' => null, 'method' => null, 'remember' => false]; } [$id, $method, $remember, $time] = explode(':', $value); @@ -98,18 +104,18 @@ protected function getLastLoginAttemptDetails(): array if ($time < $hourAgo) { $this->clearLastLoginAttempted(); - return ['user_id' => null, 'method' => null]; + return ['user_id' => null, 'method' => null, 'remember' => false]; } return ['user_id' => $id, 'method' => $method, 'remember' => boolval($remember)]; } /** - * Set the last login attempted user. + * Set the last login-attempted user. * Must be only used when credentials are correct and a login could be - * achieved but a secondary factor has stopped the login. + * achieved, but a secondary factor has stopped the login. */ - protected function setLastLoginAttemptedForUser(User $user, string $method, bool $remember) + protected function setLastLoginAttemptedForUser(User $user, string $method, bool $remember): void { session()->put( self::LAST_LOGIN_ATTEMPTED_SESSION_KEY, @@ -152,16 +158,40 @@ public function awaitingEmailConfirmation(User $user): bool */ public function attempt(array $credentials, string $method, bool $remember = false): bool { + if ($this->areCredentialsForGuest($credentials)) { + return false; + } + $result = auth()->attempt($credentials, $remember); if ($result) { $user = auth()->user(); auth()->logout(); - $this->login($user, $method, $remember); + try { + $this->login($user, $method, $remember); + } catch (LoginAttemptInvalidUserException $e) { + // Catch and return false for non-login accounts + // so it looks like a normal invalid login. + return false; + } } return $result; } + /** + * Check if the given credentials are likely for the system guest account. + */ + protected function areCredentialsForGuest(array $credentials): bool + { + if (isset($credentials['email'])) { + return User::query()->where('email', '=', $credentials['email']) + ->where('system_name', '=', 'public') + ->exists(); + } + + return false; + } + /** * Logs the current user out of the application. * Returns an app post-redirect path. diff --git a/app/Access/Mfa/TotpValidationRule.php b/app/Access/Mfa/TotpValidationRule.php index c38bde90b75..63b575f198b 100644 --- a/app/Access/Mfa/TotpValidationRule.php +++ b/app/Access/Mfa/TotpValidationRule.php @@ -2,36 +2,26 @@ namespace BookStack\Access\Mfa; -use Illuminate\Contracts\Validation\Rule; +use Closure; +use Illuminate\Contracts\Validation\ValidationRule; -class TotpValidationRule implements Rule +class TotpValidationRule implements ValidationRule { - protected $secret; - protected $totpService; - /** * Create a new rule instance. * Takes the TOTP secret that must be system provided, not user provided. */ - public function __construct(string $secret) - { - $this->secret = $secret; - $this->totpService = app()->make(TotpService::class); + public function __construct( + protected string $secret, + protected TotpService $totpService, + ) { } - /** - * Determine if the validation rule passes. - */ - public function passes($attribute, $value) - { - return $this->totpService->verifyCode($value, $this->secret); - } - - /** - * Get the validation error message. - */ - public function message() + public function validate(string $attribute, mixed $value, Closure $fail): void { - return trans('validation.totp'); + $passes = $this->totpService->verifyCode($value, $this->secret); + if (!$passes) { + $fail(trans('validation.totp')); + } } } diff --git a/app/Access/Oidc/OidcIdToken.php b/app/Access/Oidc/OidcIdToken.php index 5a395022a4e..68a8aa61168 100644 --- a/app/Access/Oidc/OidcIdToken.php +++ b/app/Access/Oidc/OidcIdToken.php @@ -2,58 +2,8 @@ namespace BookStack\Access\Oidc; -class OidcIdToken +class OidcIdToken extends OidcJwtWithClaims implements ProvidesClaims { - protected array $header; - protected array $payload; - protected string $signature; - protected string $issuer; - protected array $tokenParts = []; - - /** - * @var array[]|string[] - */ - protected array $keys; - - public function __construct(string $token, string $issuer, array $keys) - { - $this->keys = $keys; - $this->issuer = $issuer; - $this->parse($token); - } - - /** - * Parse the token content into its components. - */ - protected function parse(string $token): void - { - $this->tokenParts = explode('.', $token); - $this->header = $this->parseEncodedTokenPart($this->tokenParts[0]); - $this->payload = $this->parseEncodedTokenPart($this->tokenParts[1] ?? ''); - $this->signature = $this->base64UrlDecode($this->tokenParts[2] ?? '') ?: ''; - } - - /** - * Parse a Base64-JSON encoded token part. - * Returns the data as a key-value array or empty array upon error. - */ - protected function parseEncodedTokenPart(string $part): array - { - $json = $this->base64UrlDecode($part) ?: '{}'; - $decoded = json_decode($json, true); - - return is_array($decoded) ? $decoded : []; - } - - /** - * Base64URL decode. Needs some character conversions to be compatible - * with PHP's default base64 handling. - */ - protected function base64UrlDecode(string $encoded): string - { - return base64_decode(strtr($encoded, '-_', '+/')); - } - /** * Validate all possible parts of the id token. * @@ -61,91 +11,12 @@ protected function base64UrlDecode(string $encoded): string */ public function validate(string $clientId): bool { - $this->validateTokenStructure(); - $this->validateTokenSignature(); + parent::validateCommonTokenDetails($clientId); $this->validateTokenClaims($clientId); return true; } - /** - * Fetch a specific claim from this token. - * Returns null if it is null or does not exist. - * - * @return mixed|null - */ - public function getClaim(string $claim) - { - return $this->payload[$claim] ?? null; - } - - /** - * Get all returned claims within the token. - */ - public function getAllClaims(): array - { - return $this->payload; - } - - /** - * Replace the existing claim data of this token with that provided. - */ - public function replaceClaims(array $claims): void - { - $this->payload = $claims; - } - - /** - * Validate the structure of the given token and ensure we have the required pieces. - * As per https://datatracker.ietf.org/doc/html/rfc7519#section-7.2. - * - * @throws OidcInvalidTokenException - */ - protected function validateTokenStructure(): void - { - foreach (['header', 'payload'] as $prop) { - if (empty($this->$prop) || !is_array($this->$prop)) { - throw new OidcInvalidTokenException("Could not parse out a valid {$prop} within the provided token"); - } - } - - if (empty($this->signature) || !is_string($this->signature)) { - throw new OidcInvalidTokenException('Could not parse out a valid signature within the provided token'); - } - } - - /** - * Validate the signature of the given token and ensure it validates against the provided key. - * - * @throws OidcInvalidTokenException - */ - protected function validateTokenSignature(): void - { - if ($this->header['alg'] !== 'RS256') { - throw new OidcInvalidTokenException("Only RS256 signature validation is supported. Token reports using {$this->header['alg']}"); - } - - $parsedKeys = array_map(function ($key) { - try { - return new OidcJwtSigningKey($key); - } catch (OidcInvalidKeyException $e) { - throw new OidcInvalidTokenException('Failed to read signing key with error: ' . $e->getMessage()); - } - }, $this->keys); - - $parsedKeys = array_filter($parsedKeys); - - $contentToSign = $this->tokenParts[0] . '.' . $this->tokenParts[1]; - /** @var OidcJwtSigningKey $parsedKey */ - foreach ($parsedKeys as $parsedKey) { - if ($parsedKey->verify($contentToSign, $this->signature)) { - return; - } - } - - throw new OidcInvalidTokenException('Token signature could not be validated using the provided keys'); - } - /** * Validate the claims of the token. * As per https://openid.net/specs/openid-connect-basic-1_0.html#IDTokenValidation. @@ -156,27 +27,18 @@ protected function validateTokenClaims(string $clientId): void { // 1. The Issuer Identifier for the OpenID Provider (which is typically obtained during Discovery) // MUST exactly match the value of the iss (issuer) Claim. - if (empty($this->payload['iss']) || $this->issuer !== $this->payload['iss']) { - throw new OidcInvalidTokenException('Missing or non-matching token issuer value'); - } + // Already done in parent. // 2. The Client MUST validate that the aud (audience) Claim contains its client_id value registered // at the Issuer identified by the iss (issuer) Claim as an audience. The ID Token MUST be rejected // if the ID Token does not list the Client as a valid audience, or if it contains additional // audiences not trusted by the Client. - if (empty($this->payload['aud'])) { - throw new OidcInvalidTokenException('Missing token audience value'); - } - + // Partially done in parent. $aud = is_string($this->payload['aud']) ? [$this->payload['aud']] : $this->payload['aud']; if (count($aud) !== 1) { throw new OidcInvalidTokenException('Token audience value has ' . count($aud) . ' values, Expected 1'); } - if ($aud[0] !== $clientId) { - throw new OidcInvalidTokenException('Token audience value did not match the expected client_id'); - } - // 3. If the ID Token contains multiple audiences, the Client SHOULD verify that an azp Claim is present. // NOTE: Addressed by enforcing a count of 1 above. diff --git a/app/Access/Oidc/OidcJwtWithClaims.php b/app/Access/Oidc/OidcJwtWithClaims.php new file mode 100644 index 00000000000..06c04d81eb7 --- /dev/null +++ b/app/Access/Oidc/OidcJwtWithClaims.php @@ -0,0 +1,174 @@ +keys = $keys; + $this->issuer = $issuer; + $this->parse($token); + } + + /** + * Parse the token content into its components. + */ + protected function parse(string $token): void + { + $this->tokenParts = explode('.', $token); + $this->header = $this->parseEncodedTokenPart($this->tokenParts[0]); + $this->payload = $this->parseEncodedTokenPart($this->tokenParts[1] ?? ''); + $this->signature = $this->base64UrlDecode($this->tokenParts[2] ?? '') ?: ''; + } + + /** + * Parse a Base64-JSON encoded token part. + * Returns the data as a key-value array or empty array upon error. + */ + protected function parseEncodedTokenPart(string $part): array + { + $json = $this->base64UrlDecode($part) ?: '{}'; + $decoded = json_decode($json, true); + + return is_array($decoded) ? $decoded : []; + } + + /** + * Base64URL decode. Needs some character conversions to be compatible + * with PHP's default base64 handling. + */ + protected function base64UrlDecode(string $encoded): string + { + return base64_decode(strtr($encoded, '-_', '+/')); + } + + /** + * Validate common parts of OIDC JWT tokens. + * + * @throws OidcInvalidTokenException + */ + public function validateCommonTokenDetails(string $clientId): bool + { + $this->validateTokenStructure(); + $this->validateTokenSignature(); + $this->validateCommonClaims($clientId); + + return true; + } + + /** + * Fetch a specific claim from this token. + * Returns null if it is null or does not exist. + */ + public function getClaim(string $claim): mixed + { + return $this->payload[$claim] ?? null; + } + + /** + * Get all returned claims within the token. + */ + public function getAllClaims(): array + { + return $this->payload; + } + + /** + * Replace the existing claim data of this token with that provided. + */ + public function replaceClaims(array $claims): void + { + $this->payload = $claims; + } + + /** + * Validate the structure of the given token and ensure we have the required pieces. + * As per https://datatracker.ietf.org/doc/html/rfc7519#section-7.2. + * + * @throws OidcInvalidTokenException + */ + protected function validateTokenStructure(): void + { + foreach (['header', 'payload'] as $prop) { + if (empty($this->$prop) || !is_array($this->$prop)) { + throw new OidcInvalidTokenException("Could not parse out a valid {$prop} within the provided token"); + } + } + + if (empty($this->signature) || !is_string($this->signature)) { + throw new OidcInvalidTokenException('Could not parse out a valid signature within the provided token'); + } + } + + /** + * Validate the signature of the given token and ensure it validates against the provided key. + * + * @throws OidcInvalidTokenException + */ + protected function validateTokenSignature(): void + { + if ($this->header['alg'] !== 'RS256') { + throw new OidcInvalidTokenException("Only RS256 signature validation is supported. Token reports using {$this->header['alg']}"); + } + + $parsedKeys = array_map(function ($key) { + try { + return new OidcJwtSigningKey($key); + } catch (OidcInvalidKeyException $e) { + throw new OidcInvalidTokenException('Failed to read signing key with error: ' . $e->getMessage()); + } + }, $this->keys); + + $parsedKeys = array_filter($parsedKeys); + + $contentToSign = $this->tokenParts[0] . '.' . $this->tokenParts[1]; + /** @var OidcJwtSigningKey $parsedKey */ + foreach ($parsedKeys as $parsedKey) { + if ($parsedKey->verify($contentToSign, $this->signature)) { + return; + } + } + + throw new OidcInvalidTokenException('Token signature could not be validated using the provided keys'); + } + + /** + * Validate common claims for OIDC JWT tokens. + * As per https://openid.net/specs/openid-connect-basic-1_0.html#IDTokenValidation + * and https://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse + * + * @throws OidcInvalidTokenException + */ + protected function validateCommonClaims(string $clientId): void + { + // 1. The Issuer Identifier for the OpenID Provider (which is typically obtained during Discovery) + // MUST exactly match the value of the iss (issuer) Claim. + if (empty($this->payload['iss']) || $this->issuer !== $this->payload['iss']) { + throw new OidcInvalidTokenException('Missing or non-matching token issuer value'); + } + + // 2. The Client MUST validate that the aud (audience) Claim contains its client_id value registered + // at the Issuer identified by the iss (issuer) Claim as an audience. The ID Token MUST be rejected + // if the ID Token does not list the Client as a valid audience. + if (empty($this->payload['aud'])) { + throw new OidcInvalidTokenException('Missing token audience value'); + } + + $aud = is_string($this->payload['aud']) ? [$this->payload['aud']] : $this->payload['aud']; + if (!in_array($clientId, $aud, true)) { + throw new OidcInvalidTokenException('Token audience value did not match the expected client_id'); + } + } +} diff --git a/app/Access/Oidc/OidcOAuthProvider.php b/app/Access/Oidc/OidcOAuthProvider.php index d2dc829b729..371bfcecb54 100644 --- a/app/Access/Oidc/OidcOAuthProvider.php +++ b/app/Access/Oidc/OidcOAuthProvider.php @@ -83,15 +83,9 @@ protected function getScopeSeparator(): string /** * Checks a provider response for errors. - * - * @param ResponseInterface $response - * @param array|string $data Parsed response data - * * @throws IdentityProviderException - * - * @return void */ - protected function checkResponse(ResponseInterface $response, $data) + protected function checkResponse(ResponseInterface $response, $data): void { if ($response->getStatusCode() >= 400 || isset($data['error'])) { throw new IdentityProviderException( @@ -105,13 +99,8 @@ protected function checkResponse(ResponseInterface $response, $data) /** * Generates a resource owner object from a successful resource owner * details request. - * - * @param array $response - * @param AccessToken $token - * - * @return ResourceOwnerInterface */ - protected function createResourceOwner(array $response, AccessToken $token) + protected function createResourceOwner(array $response, AccessToken $token): ResourceOwnerInterface { return new GenericResourceOwner($response, ''); } @@ -121,14 +110,18 @@ protected function createResourceOwner(array $response, AccessToken $token) * * The grant that was used to fetch the response can be used to provide * additional context. - * - * @param array $response - * @param AbstractGrant $grant - * - * @return OidcAccessToken */ - protected function createAccessToken(array $response, AbstractGrant $grant) + protected function createAccessToken(array $response, AbstractGrant $grant): OidcAccessToken { return new OidcAccessToken($response); } + + /** + * Get the method used for PKCE code verifier hashing, which is passed + * in the "code_challenge_method" parameter in the authorization request. + */ + protected function getPkceMethod(): string + { + return static::PKCE_METHOD_S256; + } } diff --git a/app/Access/Oidc/OidcProviderSettings.php b/app/Access/Oidc/OidcProviderSettings.php index bea6a523e77..71c3b573421 100644 --- a/app/Access/Oidc/OidcProviderSettings.php +++ b/app/Access/Oidc/OidcProviderSettings.php @@ -18,10 +18,10 @@ class OidcProviderSettings public string $issuer; public string $clientId; public string $clientSecret; - public ?string $redirectUri; public ?string $authorizationEndpoint; public ?string $tokenEndpoint; public ?string $endSessionEndpoint; + public ?string $userinfoEndpoint; /** * @var string[]|array[] @@ -37,7 +37,7 @@ public function __construct(array $settings) /** * Apply an array of settings to populate setting properties within this class. */ - protected function applySettingsFromArray(array $settingsArray) + protected function applySettingsFromArray(array $settingsArray): void { foreach ($settingsArray as $key => $value) { if (property_exists($this, $key)) { @@ -51,9 +51,9 @@ protected function applySettingsFromArray(array $settingsArray) * * @throws InvalidArgumentException */ - protected function validateInitial() + protected function validateInitial(): void { - $required = ['clientId', 'clientSecret', 'redirectUri', 'issuer']; + $required = ['clientId', 'clientSecret', 'issuer']; foreach ($required as $prop) { if (empty($this->$prop)) { throw new InvalidArgumentException("Missing required configuration \"{$prop}\" value"); @@ -73,12 +73,20 @@ protected function validateInitial() public function validate(): void { $this->validateInitial(); + $required = ['keys', 'tokenEndpoint', 'authorizationEndpoint']; foreach ($required as $prop) { if (empty($this->$prop)) { throw new InvalidArgumentException("Missing required configuration \"{$prop}\" value"); } } + + $endpointProperties = ['tokenEndpoint', 'authorizationEndpoint', 'userinfoEndpoint']; + foreach ($endpointProperties as $prop) { + if (is_string($this->$prop) && !str_starts_with($this->$prop, 'https://')) { + throw new InvalidArgumentException("Endpoint value for \"{$prop}\" must start with https://"); + } + } } /** @@ -86,7 +94,7 @@ public function validate(): void * * @throws OidcIssuerDiscoveryException */ - public function discoverFromIssuer(ClientInterface $httpClient, Repository $cache, int $cacheMinutes) + public function discoverFromIssuer(ClientInterface $httpClient, Repository $cache, int $cacheMinutes): void { try { $cacheKey = 'oidc-discovery::' . $this->issuer; @@ -128,6 +136,10 @@ protected function loadSettingsFromIssuerDiscovery(ClientInterface $httpClient): $discoveredSettings['tokenEndpoint'] = $result['token_endpoint']; } + if (!empty($result['userinfo_endpoint'])) { + $discoveredSettings['userinfoEndpoint'] = $result['userinfo_endpoint']; + } + if (!empty($result['jwks_uri'])) { $keys = $this->loadKeysFromUri($result['jwks_uri'], $httpClient); $discoveredSettings['keys'] = $this->filterKeys($keys); @@ -175,9 +187,9 @@ protected function loadKeysFromUri(string $uri, ClientInterface $httpClient): ar /** * Get the settings needed by an OAuth provider, as a key=>value array. */ - public function arrayForProvider(): array + public function arrayForOAuthProvider(): array { - $settingKeys = ['clientId', 'clientSecret', 'redirectUri', 'authorizationEndpoint', 'tokenEndpoint']; + $settingKeys = ['clientId', 'clientSecret', 'authorizationEndpoint', 'tokenEndpoint', 'userinfoEndpoint']; $settings = []; foreach ($settingKeys as $setting) { $settings[$setting] = $this->$setting; diff --git a/app/Access/Oidc/OidcService.php b/app/Access/Oidc/OidcService.php index f1e5b25af14..d6f6ef156e4 100644 --- a/app/Access/Oidc/OidcService.php +++ b/app/Access/Oidc/OidcService.php @@ -11,8 +11,8 @@ use BookStack\Facades\Theme; use BookStack\Http\HttpRequestService; use BookStack\Theming\ThemeEvents; +use BookStack\Uploads\UserAvatars; use BookStack\Users\Models\User; -use Illuminate\Support\Arr; use Illuminate\Support\Facades\Cache; use League\OAuth2\Client\OptionProvider\HttpBasicAuthOptionProvider; use League\OAuth2\Client\Provider\Exception\IdentityProviderException; @@ -27,12 +27,15 @@ public function __construct( protected RegistrationService $registrationService, protected LoginService $loginService, protected HttpRequestService $http, - protected GroupSyncService $groupService + protected GroupSyncService $groupService, + protected UserAvatars $userAvatars ) { } /** * Initiate an authorization flow. + * Provides back an authorize redirect URL, in addition to other + * details which may be required for the auth flow. * * @throws OidcException * @@ -42,8 +45,12 @@ public function login(): array { $settings = $this->getProviderSettings(); $provider = $this->getProvider($settings); + + $url = $provider->getAuthorizationUrl(); + session()->put('oidc_pkce_code', $provider->getPkceCode() ?? ''); + return [ - 'url' => $provider->getAuthorizationUrl(), + 'url' => $url, 'state' => $provider->getState(), ]; } @@ -63,6 +70,10 @@ public function processAuthorizeResponse(?string $authorizationCode): User $settings = $this->getProviderSettings(); $provider = $this->getProvider($settings); + // Set PKCE code flashed at login + $pkceCode = session()->pull('oidc_pkce_code', ''); + $provider->setPkceCode($pkceCode); + // Try to exchange authorization code for access token $accessToken = $provider->getAccessToken('authorization_code', [ 'code' => $authorizationCode, @@ -81,10 +92,10 @@ protected function getProviderSettings(): OidcProviderSettings 'issuer' => $config['issuer'], 'clientId' => $config['client_id'], 'clientSecret' => $config['client_secret'], - 'redirectUri' => url('/oidc/callback'), 'authorizationEndpoint' => $config['authorization_endpoint'], 'tokenEndpoint' => $config['token_endpoint'], 'endSessionEndpoint' => is_string($config['end_session_endpoint']) ? $config['end_session_endpoint'] : null, + 'userinfoEndpoint' => $config['userinfo_endpoint'], ]); // Use keys if configured @@ -119,7 +130,10 @@ protected function getProviderSettings(): OidcProviderSettings */ protected function getProvider(OidcProviderSettings $settings): OidcOAuthProvider { - $provider = new OidcOAuthProvider($settings->arrayForProvider(), [ + $provider = new OidcOAuthProvider([ + ...$settings->arrayForOAuthProvider(), + 'redirectUri' => url('/oidc/callback'), + ], [ 'httpClient' => $this->http->buildClient(5), 'optionProvider' => new HttpBasicAuthOptionProvider(), ]); @@ -146,69 +160,6 @@ protected function getAdditionalScopes(): array return array_filter($scopeArr); } - /** - * Calculate the display name. - */ - protected function getUserDisplayName(OidcIdToken $token, string $defaultValue): string - { - $displayNameAttrString = $this->config()['display_name_claims'] ?? ''; - $displayNameAttrs = explode('|', $displayNameAttrString); - - $displayName = []; - foreach ($displayNameAttrs as $dnAttr) { - $dnComponent = $token->getClaim($dnAttr) ?? ''; - if ($dnComponent !== '') { - $displayName[] = $dnComponent; - } - } - - if (count($displayName) == 0) { - $displayName[] = $defaultValue; - } - - return implode(' ', $displayName); - } - - /** - * Extract the assigned groups from the id token. - * - * @return string[] - */ - protected function getUserGroups(OidcIdToken $token): array - { - $groupsAttr = $this->config()['groups_claim']; - if (empty($groupsAttr)) { - return []; - } - - $groupsList = Arr::get($token->getAllClaims(), $groupsAttr); - if (!is_array($groupsList)) { - return []; - } - - return array_values(array_filter($groupsList, function ($val) { - return is_string($val); - })); - } - - /** - * Extract the details of a user from an ID token. - * - * @return array{name: string, email: string, external_id: string, groups: string[]} - */ - protected function getUserDetails(OidcIdToken $token): array - { - $idClaim = $this->config()['external_id_claim']; - $id = $token->getClaim($idClaim); - - return [ - 'external_id' => $id, - 'email' => $token->getClaim('email'), - 'name' => $this->getUserDisplayName($token, $id), - 'groups' => $this->getUserGroups($token), - ]; - } - /** * Processes a received access token for a user. Login the user when * they exist, optionally registering them automatically. @@ -245,34 +196,39 @@ protected function processAccessTokenCallback(OidcAccessToken $accessToken, Oidc try { $idToken->validate($settings->clientId); } catch (OidcInvalidTokenException $exception) { - throw new OidcException("ID token validate failed with error: {$exception->getMessage()}"); + throw new OidcException("ID token validation failed with error: {$exception->getMessage()}"); } - $userDetails = $this->getUserDetails($idToken); - $isLoggedIn = auth()->check(); - - if (empty($userDetails['email'])) { + $userDetails = $this->getUserDetailsFromToken($idToken, $accessToken, $settings); + if (empty($userDetails->email)) { throw new OidcException(trans('errors.oidc_no_email_address')); } + if (empty($userDetails->name)) { + $userDetails->name = $userDetails->externalId; + } + $isLoggedIn = auth()->check(); if ($isLoggedIn) { throw new OidcException(trans('errors.oidc_already_logged_in')); } try { $user = $this->registrationService->findOrRegister( - $userDetails['name'], - $userDetails['email'], - $userDetails['external_id'] + $userDetails->name, + $userDetails->email, + $userDetails->externalId ); } catch (UserRegistrationException $exception) { throw new OidcException($exception->getMessage()); } + if ($this->config()['fetch_avatar'] && !$user->avatar()->exists() && $userDetails->picture) { + $this->userAvatars->assignToUserFromUrl($user, $userDetails->picture); + } + if ($this->shouldSyncGroups()) { - $groups = $userDetails['groups']; $detachExisting = $this->config()['remove_from_groups']; - $this->groupService->syncUserWithFoundGroups($user, $groups, $detachExisting); + $this->groupService->syncUserWithFoundGroups($user, $userDetails->groups ?? [], $detachExisting); } $this->loginService->login($user, 'oidc'); @@ -280,6 +236,45 @@ protected function processAccessTokenCallback(OidcAccessToken $accessToken, Oidc return $user; } + /** + * @throws OidcException + */ + protected function getUserDetailsFromToken(OidcIdToken $idToken, OidcAccessToken $accessToken, OidcProviderSettings $settings): OidcUserDetails + { + $userDetails = new OidcUserDetails(); + $userDetails->populate( + $idToken, + $this->config()['external_id_claim'], + $this->config()['display_name_claims'] ?? '', + $this->config()['groups_claim'] ?? '' + ); + + if (!$userDetails->isFullyPopulated($this->shouldSyncGroups()) && !empty($settings->userinfoEndpoint)) { + $provider = $this->getProvider($settings); + $request = $provider->getAuthenticatedRequest('GET', $settings->userinfoEndpoint, $accessToken->getToken()); + $response = new OidcUserinfoResponse( + $provider->getResponse($request), + $settings->issuer, + $settings->keys, + ); + + try { + $response->validate($idToken->getClaim('sub'), $settings->clientId); + } catch (OidcInvalidTokenException $exception) { + throw new OidcException("Userinfo endpoint response validation failed with error: {$exception->getMessage()}"); + } + + $userDetails->populate( + $response, + $this->config()['external_id_claim'], + $this->config()['display_name_claims'] ?? '', + $this->config()['groups_claim'] ?? '' + ); + } + + return $userDetails; + } + /** * Get the OIDC config from the application. */ diff --git a/app/Access/Oidc/OidcUserDetails.php b/app/Access/Oidc/OidcUserDetails.php new file mode 100644 index 00000000000..7a422a58de2 --- /dev/null +++ b/app/Access/Oidc/OidcUserDetails.php @@ -0,0 +1,87 @@ +externalId) + || empty($this->email) + || empty($this->name) + || ($groupSyncActive && $this->groups === null); + + return !$hasEmpty; + } + + /** + * Populate user details from the given claim data. + */ + public function populate( + ProvidesClaims $claims, + string $idClaim, + string $displayNameClaims, + string $groupsClaim, + ): void { + $this->externalId = $claims->getClaim($idClaim) ?? $this->externalId; + $this->email = $claims->getClaim('email') ?? $this->email; + $this->name = static::getUserDisplayName($displayNameClaims, $claims) ?? $this->name; + $this->groups = static::getUserGroups($groupsClaim, $claims) ?? $this->groups; + $this->picture = static::getPicture($claims) ?: $this->picture; + } + + protected static function getUserDisplayName(string $displayNameClaims, ProvidesClaims $claims): string + { + $displayNameClaimParts = explode('|', $displayNameClaims); + + $displayName = []; + foreach ($displayNameClaimParts as $claim) { + $component = $claims->getClaim(trim($claim)) ?? ''; + if ($component !== '') { + $displayName[] = $component; + } + } + + return implode(' ', $displayName); + } + + protected static function getUserGroups(string $groupsClaim, ProvidesClaims $claims): ?array + { + if (empty($groupsClaim)) { + return null; + } + + $groupsList = Arr::get($claims->getAllClaims(), $groupsClaim); + if (!is_array($groupsList)) { + return null; + } + + return array_values(array_filter($groupsList, function ($val) { + return is_string($val); + })); + } + + protected static function getPicture(ProvidesClaims $claims): ?string + { + $picture = $claims->getClaim('picture'); + if (is_string($picture) && str_starts_with($picture, 'http')) { + return $picture; + } + + return null; + } +} diff --git a/app/Access/Oidc/OidcUserinfoResponse.php b/app/Access/Oidc/OidcUserinfoResponse.php new file mode 100644 index 00000000000..33b8ec80665 --- /dev/null +++ b/app/Access/Oidc/OidcUserinfoResponse.php @@ -0,0 +1,69 @@ +getHeader('Content-Type')[0] ?? ''; + $contentType = strtolower(trim(explode(';', $contentTypeHeaderValue, 2)[0])); + + if ($contentType === 'application/json') { + $this->claims = json_decode($response->getBody()->getContents(), true); + } + + if ($contentType === 'application/jwt') { + $this->jwt = new OidcJwtWithClaims($response->getBody()->getContents(), $issuer, $keys); + $this->claims = $this->jwt->getAllClaims(); + } + } + + /** + * @throws OidcInvalidTokenException + */ + public function validate(string $idTokenSub, string $clientId): bool + { + if (!is_null($this->jwt)) { + $this->jwt->validateCommonTokenDetails($clientId); + } + + $sub = $this->getClaim('sub'); + + // Spec: v1.0 5.3.2: The sub (subject) Claim MUST always be returned in the UserInfo Response. + if (!is_string($sub) || empty($sub)) { + throw new OidcInvalidTokenException("No valid subject value found in userinfo data"); + } + + // Spec: v1.0 5.3.2: The sub Claim in the UserInfo Response MUST be verified to exactly match the sub Claim in the ID Token; + // if they do not match, the UserInfo Response values MUST NOT be used. + if ($idTokenSub !== $sub) { + throw new OidcInvalidTokenException("Subject value provided in the userinfo endpoint does not match the provided ID token value"); + } + + // Spec v1.0 5.3.4 Defines the following: + // Verify that the OP that responded was the intended OP through a TLS server certificate check, per RFC 6125 [RFC6125]. + // This is effectively done as part of the HTTP request we're making through CURLOPT_SSL_VERIFYHOST on the request. + // If the Client has provided a userinfo_encrypted_response_alg parameter during Registration, decrypt the UserInfo Response using the keys specified during Registration. + // We don't currently support JWT encryption for OIDC + // If the response was signed, the Client SHOULD validate the signature according to JWS [JWS]. + // This is done as part of the validateCommonClaims above. + + return true; + } + + public function getClaim(string $claim): mixed + { + return $this->claims[$claim] ?? null; + } + + public function getAllClaims(): array + { + return $this->claims; + } +} diff --git a/app/Access/Oidc/ProvidesClaims.php b/app/Access/Oidc/ProvidesClaims.php new file mode 100644 index 00000000000..a3cf5165510 --- /dev/null +++ b/app/Access/Oidc/ProvidesClaims.php @@ -0,0 +1,17 @@ +userRepo = $userRepo; - $this->emailConfirmationService = $emailConfirmationService; + public function __construct( + protected UserRepo $userRepo, + protected EmailConfirmationService $emailConfirmationService, + ) { } /** - * Check whether or not registrations are allowed in the app settings. + * Check if registrations are allowed in the app settings. * * @throws UserRegistrationException */ @@ -84,6 +78,7 @@ public function findOrRegister(string $name, string $email, string $externalId): public function registerUser(array $userData, ?SocialAccount $socialAccount = null, bool $emailConfirmed = false): User { $userEmail = $userData['email']; + $authSystem = $socialAccount ? $socialAccount->driver : auth()->getDefaultDriver(); // Email restriction $this->ensureEmailDomainAllowed($userEmail); @@ -94,6 +89,12 @@ public function registerUser(array $userData, ?SocialAccount $socialAccount = nu throw new UserRegistrationException(trans('errors.error_user_exists_different_creds', ['email' => $userEmail]), '/login'); } + /** @var ?bool $shouldRegister */ + $shouldRegister = Theme::dispatch(ThemeEvents::AUTH_PRE_REGISTER, $authSystem, $userData); + if ($shouldRegister === false) { + throw new UserRegistrationException(trans('errors.auth_pre_register_theme_prevention'), '/login'); + } + // Create the user $newUser = $this->userRepo->createWithoutActivity($userData, $emailConfirmed); $newUser->attachDefaultRole(); @@ -104,7 +105,7 @@ public function registerUser(array $userData, ?SocialAccount $socialAccount = nu } Activity::add(ActivityType::AUTH_REGISTER, $socialAccount ?? $newUser); - Theme::dispatch(ThemeEvents::AUTH_REGISTER, $socialAccount ? $socialAccount->driver : auth()->getDefaultDriver(), $newUser); + Theme::dispatch(ThemeEvents::AUTH_REGISTER, $authSystem, $newUser); // Start email confirmation flow if required if ($this->emailConfirmationService->confirmationRequired() && !$emailConfirmed) { @@ -138,7 +139,7 @@ protected function ensureEmailDomainAllowed(string $userEmail): void } $restrictedEmailDomains = explode(',', str_replace(' ', '', $registrationRestrict)); - $userEmailDomain = $domain = mb_substr(mb_strrchr($userEmail, '@'), 1); + $userEmailDomain = mb_substr(mb_strrchr($userEmail, '@'), 1); if (!in_array($userEmailDomain, $restrictedEmailDomains)) { $redirect = $this->registrationAllowed() ? '/register' : '/login'; diff --git a/app/Access/Saml2Service.php b/app/Access/Saml2Service.php index 664b77aba45..106a7a22906 100644 --- a/app/Access/Saml2Service.php +++ b/app/Access/Saml2Service.php @@ -51,7 +51,7 @@ public function login(): array * Returns the SAML2 request ID, and the URL to redirect the user to. * * @throws Error - * @returns array{url: string, id: ?string} + * @return array{url: string, id: ?string} */ public function logout(User $user): array { @@ -133,6 +133,7 @@ public function processSlsResponse(?string $requestId): string // value so that the exact encoding format is matched when checking the signature. // This is primarily due to ADFS encoding query params with lowercase percent encoding while // PHP (And most other sensible providers) standardise on uppercase. + /** @var ?string $samlRedirect */ $samlRedirect = $toolkit->processSLO(true, $requestId, true, null, true); $errors = $toolkit->getErrors(); diff --git a/app/Access/SocialDriverManager.php b/app/Access/SocialDriverManager.php index 536b2e63d37..efafab560ce 100644 --- a/app/Access/SocialDriverManager.php +++ b/app/Access/SocialDriverManager.php @@ -55,7 +55,7 @@ public function isAutoConfirmEmailEnabled(string $driver): bool /** * Gets the names of the active social drivers, keyed by driver id. - * @returns array + * @return array */ public function getActive(): array { @@ -92,7 +92,7 @@ public function addSocialDriver( string $driverName, array $config, string $socialiteHandler, - callable $configureForRedirect = null + ?callable $configureForRedirect = null ) { $this->validDrivers[] = $driverName; config()->set('services.' . $driverName, $config); diff --git a/app/Access/UserInviteException.php b/app/Access/UserInviteException.php new file mode 100644 index 00000000000..70e7a787241 --- /dev/null +++ b/app/Access/UserInviteException.php @@ -0,0 +1,10 @@ +deleteByUser($user); $token = $this->createTokenForUser($user); - $user->notify(new UserInviteNotification($token)); + + try { + $user->notify(new UserInviteNotification($token)); + } catch (\Exception $exception) { + throw new UserInviteException($exception->getMessage(), $exception->getCode(), $exception); + } } } diff --git a/app/Activity/ActivityQueries.php b/app/Activity/ActivityQueries.php index c69cf7a36e7..d5b047937fb 100644 --- a/app/Activity/ActivityQueries.php +++ b/app/Activity/ActivityQueries.php @@ -7,18 +7,19 @@ use BookStack\Entities\Models\Chapter; use BookStack\Entities\Models\Entity; use BookStack\Entities\Models\Page; +use BookStack\Entities\Tools\MixedEntityListLoader; use BookStack\Permissions\PermissionApplicator; use BookStack\Users\Models\User; use Illuminate\Database\Eloquent\Builder; +use Illuminate\Database\Eloquent\Relations\MorphTo; use Illuminate\Database\Eloquent\Relations\Relation; class ActivityQueries { - protected PermissionApplicator $permissions; - - public function __construct(PermissionApplicator $permissions) - { - $this->permissions = $permissions; + public function __construct( + protected PermissionApplicator $permissions, + protected MixedEntityListLoader $listLoader, + ) { } /** @@ -27,13 +28,15 @@ public function __construct(PermissionApplicator $permissions) public function latest(int $count = 20, int $page = 0): array { $activityList = $this->permissions - ->restrictEntityRelationQuery(Activity::query(), 'activities', 'entity_id', 'entity_type') + ->restrictEntityRelationQuery(Activity::query(), 'activities', 'loggable_id', 'loggable_type') ->orderBy('created_at', 'desc') - ->with(['user', 'entity']) + ->with(['user']) ->skip($count * $page) ->take($count) ->get(); + $this->listLoader->loadIntoRelations($activityList->all(), 'loggable', false); + return $this->filterSimilar($activityList); } @@ -57,14 +60,15 @@ public function entityActivity(Entity $entity, int $count = 20, int $page = 1): $query->where(function (Builder $query) use ($queryIds) { foreach ($queryIds as $morphClass => $idArr) { $query->orWhere(function (Builder $innerQuery) use ($morphClass, $idArr) { - $innerQuery->where('entity_type', '=', $morphClass) - ->whereIn('entity_id', $idArr); + $innerQuery->where('loggable_type', '=', $morphClass) + ->whereIn('loggable_id', $idArr); }); } }); $activity = $query->orderBy('created_at', 'desc') - ->with(['entity' => function (Relation $query) { + ->with(['loggable' => function (Relation $query) { + /** @var MorphTo $query */ $query->withTrashed(); }, 'user.avatar']) ->skip($count * ($page - 1)) @@ -80,7 +84,7 @@ public function entityActivity(Entity $entity, int $count = 20, int $page = 1): public function userActivity(User $user, int $count = 20, int $page = 0): array { $activityList = $this->permissions - ->restrictEntityRelationQuery(Activity::query(), 'activities', 'entity_id', 'entity_type') + ->restrictEntityRelationQuery(Activity::query(), 'activities', 'loggable_id', 'loggable_type') ->orderBy('created_at', 'desc') ->where('user_id', '=', $user->id) ->skip($count * $page) diff --git a/app/Activity/ActivityType.php b/app/Activity/ActivityType.php index 09b2ae73c56..a7f129f71d4 100644 --- a/app/Activity/ActivityType.php +++ b/app/Activity/ActivityType.php @@ -67,6 +67,14 @@ class ActivityType const WEBHOOK_UPDATE = 'webhook_update'; const WEBHOOK_DELETE = 'webhook_delete'; + const IMPORT_CREATE = 'import_create'; + const IMPORT_RUN = 'import_run'; + const IMPORT_DELETE = 'import_delete'; + + const SORT_RULE_CREATE = 'sort_rule_create'; + const SORT_RULE_UPDATE = 'sort_rule_update'; + const SORT_RULE_DELETE = 'sort_rule_delete'; + /** * Get all the possible values. */ diff --git a/app/Activity/CommentRepo.php b/app/Activity/CommentRepo.php index ce2950e4d79..7005f8fcf83 100644 --- a/app/Activity/CommentRepo.php +++ b/app/Activity/CommentRepo.php @@ -4,8 +4,10 @@ use BookStack\Activity\Models\Comment; use BookStack\Entities\Models\Entity; +use BookStack\Exceptions\NotifyException; +use BookStack\Exceptions\PrettyException; use BookStack\Facades\Activity as ActivityService; -use League\CommonMark\CommonMarkConverter; +use BookStack\Util\HtmlDescriptionFilter; class CommentRepo { @@ -20,17 +22,17 @@ public function getById(int $id): Comment /** * Create a new comment on an entity. */ - public function create(Entity $entity, string $text, ?int $parent_id): Comment + public function create(Entity $entity, string $html, ?int $parentId, string $contentRef): Comment { $userId = user()->id; $comment = new Comment(); - $comment->text = $text; - $comment->html = $this->commentToHtml($text); + $comment->html = HtmlDescriptionFilter::filterFromString($html); $comment->created_by = $userId; $comment->updated_by = $userId; $comment->local_id = $this->getNextLocalId($entity); - $comment->parent_id = $parent_id; + $comment->parent_id = $parentId; + $comment->content_ref = preg_match('/^bkmrk-(.*?):\d+:(\d*-\d*)?$/', $contentRef) === 1 ? $contentRef : ''; $entity->comments()->save($comment); ActivityService::add(ActivityType::COMMENT_CREATE, $comment); @@ -42,11 +44,10 @@ public function create(Entity $entity, string $text, ?int $parent_id): Comment /** * Update an existing comment. */ - public function update(Comment $comment, string $text): Comment + public function update(Comment $comment, string $html): Comment { $comment->updated_by = user()->id; - $comment->text = $text; - $comment->html = $this->commentToHtml($text); + $comment->html = HtmlDescriptionFilter::filterFromString($html); $comment->save(); ActivityService::add(ActivityType::COMMENT_UPDATE, $comment); @@ -54,28 +55,49 @@ public function update(Comment $comment, string $text): Comment return $comment; } + /** - * Delete a comment from the system. + * Archive an existing comment. */ - public function delete(Comment $comment): void + public function archive(Comment $comment): Comment { - $comment->delete(); + if ($comment->parent_id) { + throw new NotifyException('Only top-level comments can be archived.', '/', 400); + } - ActivityService::add(ActivityType::COMMENT_DELETE, $comment); + $comment->archived = true; + $comment->save(); + + ActivityService::add(ActivityType::COMMENT_UPDATE, $comment); + + return $comment; } /** - * Convert the given comment Markdown to HTML. + * Un-archive an existing comment. */ - public function commentToHtml(string $commentText): string + public function unarchive(Comment $comment): Comment { - $converter = new CommonMarkConverter([ - 'html_input' => 'strip', - 'max_nesting_level' => 10, - 'allow_unsafe_links' => false, - ]); + if ($comment->parent_id) { + throw new NotifyException('Only top-level comments can be un-archived.', '/', 400); + } - return $converter->convert($commentText); + $comment->archived = false; + $comment->save(); + + ActivityService::add(ActivityType::COMMENT_UPDATE, $comment); + + return $comment; + } + + /** + * Delete a comment from the system. + */ + public function delete(Comment $comment): void + { + $comment->delete(); + + ActivityService::add(ActivityType::COMMENT_DELETE, $comment); } /** diff --git a/app/Activity/Controllers/AuditLogApiController.php b/app/Activity/Controllers/AuditLogApiController.php new file mode 100644 index 00000000000..0cb4d9cb6da --- /dev/null +++ b/app/Activity/Controllers/AuditLogApiController.php @@ -0,0 +1,29 @@ +checkPermission(Permission::SettingsManage); + $this->checkPermission(Permission::UsersManage); + + $query = Activity::query()->with(['user']); + + return $this->apiListingResponse($query, [ + 'id', 'type', 'detail', 'user_id', 'loggable_id', 'loggable_type', 'ip', 'created_at', + ]); + } +} diff --git a/app/Activity/Controllers/AuditLogController.php b/app/Activity/Controllers/AuditLogController.php index c3910a26b4c..c4f9b91edb8 100644 --- a/app/Activity/Controllers/AuditLogController.php +++ b/app/Activity/Controllers/AuditLogController.php @@ -5,6 +5,8 @@ use BookStack\Activity\ActivityType; use BookStack\Activity\Models\Activity; use BookStack\Http\Controller; +use BookStack\Permissions\Permission; +use BookStack\Sorting\SortUrl; use BookStack\Util\SimpleListOptions; use Illuminate\Http\Request; @@ -12,8 +14,8 @@ class AuditLogController extends Controller { public function index(Request $request) { - $this->checkPermission('settings-manage'); - $this->checkPermission('users-manage'); + $this->checkPermission(Permission::SettingsManage); + $this->checkPermission(Permission::UsersManage); $sort = $request->get('sort', 'activity_date'); $order = $request->get('order', 'desc'); @@ -32,7 +34,7 @@ public function index(Request $request) $query = Activity::query() ->with([ - 'entity' => fn ($query) => $query->withTrashed(), + 'loggable' => fn ($query) => $query->withTrashed(), 'user', ]) ->orderBy($listOptions->getSort(), $listOptions->getOrder()); @@ -65,6 +67,7 @@ public function index(Request $request) 'filters' => $filters, 'listOptions' => $listOptions, 'activityTypes' => $types, + 'filterSortUrl' => new SortUrl('settings/audit', array_filter($request->except('page'))) ]); } } diff --git a/app/Activity/Controllers/CommentController.php b/app/Activity/Controllers/CommentController.php index 516bcac759a..fd5463dff18 100644 --- a/app/Activity/Controllers/CommentController.php +++ b/app/Activity/Controllers/CommentController.php @@ -3,15 +3,19 @@ namespace BookStack\Activity\Controllers; use BookStack\Activity\CommentRepo; -use BookStack\Entities\Models\Page; +use BookStack\Activity\Tools\CommentTree; +use BookStack\Activity\Tools\CommentTreeNode; +use BookStack\Entities\Queries\PageQueries; use BookStack\Http\Controller; +use BookStack\Permissions\Permission; use Illuminate\Http\Request; use Illuminate\Validation\ValidationException; class CommentController extends Controller { public function __construct( - protected CommentRepo $commentRepo + protected CommentRepo $commentRepo, + protected PageQueries $pageQueries, ) { } @@ -22,12 +26,13 @@ public function __construct( */ public function savePageComment(Request $request, int $pageId) { - $this->validate($request, [ - 'text' => ['required', 'string'], + $input = $this->validate($request, [ + 'html' => ['required', 'string'], 'parent_id' => ['nullable', 'integer'], + 'content_ref' => ['string'], ]); - $page = Page::visible()->find($pageId); + $page = $this->pageQueries->findVisibleById($pageId); if ($page === null) { return response('Not found', 404); } @@ -38,15 +43,13 @@ public function savePageComment(Request $request, int $pageId) } // Create a new comment. - $this->checkPermission('comment-create-all'); - $comment = $this->commentRepo->create($page, $request->get('text'), $request->get('parent_id')); + $this->checkPermission(Permission::CommentCreateAll); + $contentRef = $input['content_ref'] ?? ''; + $comment = $this->commentRepo->create($page, $input['html'], $input['parent_id'] ?? null, $contentRef); return view('comments.comment-branch', [ 'readOnly' => false, - 'branch' => [ - 'comment' => $comment, - 'children' => [], - ] + 'branch' => new CommentTreeNode($comment, 0, []), ]); } @@ -57,17 +60,60 @@ public function savePageComment(Request $request, int $pageId) */ public function update(Request $request, int $commentId) { - $this->validate($request, [ - 'text' => ['required', 'string'], + $input = $this->validate($request, [ + 'html' => ['required', 'string'], ]); $comment = $this->commentRepo->getById($commentId); - $this->checkOwnablePermission('page-view', $comment->entity); - $this->checkOwnablePermission('comment-update', $comment); + $this->checkOwnablePermission(Permission::PageView, $comment->entity); + $this->checkOwnablePermission(Permission::CommentUpdate, $comment); - $comment = $this->commentRepo->update($comment, $request->get('text')); + $comment = $this->commentRepo->update($comment, $input['html']); - return view('comments.comment', ['comment' => $comment, 'readOnly' => false]); + return view('comments.comment', [ + 'comment' => $comment, + 'readOnly' => false, + ]); + } + + /** + * Mark a comment as archived. + */ + public function archive(int $id) + { + $comment = $this->commentRepo->getById($id); + $this->checkOwnablePermission(Permission::PageView, $comment->entity); + if (!userCan(Permission::CommentUpdate, $comment) && !userCan(Permission::CommentDelete, $comment)) { + $this->showPermissionError(); + } + + $this->commentRepo->archive($comment); + + $tree = new CommentTree($comment->entity); + return view('comments.comment-branch', [ + 'readOnly' => false, + 'branch' => $tree->getCommentNodeForId($id), + ]); + } + + /** + * Unmark a comment as archived. + */ + public function unarchive(int $id) + { + $comment = $this->commentRepo->getById($id); + $this->checkOwnablePermission(Permission::PageView, $comment->entity); + if (!userCan(Permission::CommentUpdate, $comment) && !userCan(Permission::CommentDelete, $comment)) { + $this->showPermissionError(); + } + + $this->commentRepo->unarchive($comment); + + $tree = new CommentTree($comment->entity); + return view('comments.comment-branch', [ + 'readOnly' => false, + 'branch' => $tree->getCommentNodeForId($id), + ]); } /** @@ -76,7 +122,7 @@ public function update(Request $request, int $commentId) public function destroy(int $id) { $comment = $this->commentRepo->getById($id); - $this->checkOwnablePermission('comment-delete', $comment); + $this->checkOwnablePermission(Permission::CommentDelete, $comment); $this->commentRepo->delete($comment); diff --git a/app/Activity/Controllers/FavouriteController.php b/app/Activity/Controllers/FavouriteController.php index b3aff1cef42..deeb4b0afb4 100644 --- a/app/Activity/Controllers/FavouriteController.php +++ b/app/Activity/Controllers/FavouriteController.php @@ -2,7 +2,7 @@ namespace BookStack\Activity\Controllers; -use BookStack\Entities\Queries\TopFavourites; +use BookStack\Entities\Queries\QueryTopFavourites; use BookStack\Entities\Tools\MixedEntityRequestHelper; use BookStack\Http\Controller; use Illuminate\Http\Request; @@ -17,11 +17,11 @@ public function __construct( /** * Show a listing of all favourite items for the current user. */ - public function index(Request $request) + public function index(Request $request, QueryTopFavourites $topFavourites) { $viewCount = 20; $page = intval($request->get('page', 1)); - $favourites = (new TopFavourites())->run($viewCount + 1, (($page - 1) * $viewCount)); + $favourites = $topFavourites->run($viewCount + 1, (($page - 1) * $viewCount)); $hasMoreLink = ($favourites->count() > $viewCount) ? url('/favourites?page=' . ($page + 1)) : null; diff --git a/app/Activity/Controllers/WatchController.php b/app/Activity/Controllers/WatchController.php index 5df75da39cd..b77a893ea58 100644 --- a/app/Activity/Controllers/WatchController.php +++ b/app/Activity/Controllers/WatchController.php @@ -5,13 +5,14 @@ use BookStack\Activity\Tools\UserEntityWatchOptions; use BookStack\Entities\Tools\MixedEntityRequestHelper; use BookStack\Http\Controller; +use BookStack\Permissions\Permission; use Illuminate\Http\Request; class WatchController extends Controller { public function update(Request $request, MixedEntityRequestHelper $entityHelper) { - $this->checkPermission('receive-notifications'); + $this->checkPermission(Permission::ReceiveNotifications); $this->preventGuestAccess(); $requestData = $this->validate($request, array_merge([ diff --git a/app/Activity/Controllers/WebhookController.php b/app/Activity/Controllers/WebhookController.php index dcca1dc413f..6a65b836361 100644 --- a/app/Activity/Controllers/WebhookController.php +++ b/app/Activity/Controllers/WebhookController.php @@ -6,6 +6,7 @@ use BookStack\Activity\Models\Webhook; use BookStack\Activity\Queries\WebhooksAllPaginatedAndSorted; use BookStack\Http\Controller; +use BookStack\Permissions\Permission; use BookStack\Util\SimpleListOptions; use Illuminate\Http\Request; @@ -14,7 +15,7 @@ class WebhookController extends Controller public function __construct() { $this->middleware([ - 'can:settings-manage', + Permission::SettingsManage->middleware() ]); } diff --git a/app/Activity/Models/Activity.php b/app/Activity/Models/Activity.php index 5fad9f1d3a2..ac9fec5178b 100644 --- a/app/Activity/Models/Activity.php +++ b/app/Activity/Models/Activity.php @@ -15,26 +15,24 @@ /** * @property string $type * @property User $user - * @property Entity $entity + * @property Entity $loggable * @property string $detail - * @property string $entity_type - * @property int $entity_id + * @property string $loggable_type + * @property int $loggable_id * @property int $user_id * @property Carbon $created_at - * @property Carbon $updated_at */ class Activity extends Model { /** - * Get the entity for this activity. + * Get the loggable model related to this activity. + * Currently only used for entities (previously entity_[id/type] columns). + * Could be used for others but will need an audit of uses where assumed + * to be entities. */ - public function entity(): MorphTo + public function loggable(): MorphTo { - if ($this->entity_type === '') { - $this->entity_type = null; - } - - return $this->morphTo('entity'); + return $this->morphTo('loggable'); } /** @@ -47,8 +45,8 @@ public function user(): BelongsTo public function jointPermissions(): HasMany { - return $this->hasMany(JointPermission::class, 'entity_id', 'entity_id') - ->whereColumn('activities.entity_type', '=', 'joint_permissions.entity_type'); + return $this->hasMany(JointPermission::class, 'entity_id', 'loggable_id') + ->whereColumn('activities.loggable_type', '=', 'joint_permissions.entity_type'); } /** @@ -74,6 +72,6 @@ public function isForEntity(): bool */ public function isSimilarTo(self $activityB): bool { - return [$this->type, $this->entity_type, $this->entity_id] === [$activityB->type, $activityB->entity_type, $activityB->entity_id]; + return [$this->type, $this->loggable_type, $this->loggable_id] === [$activityB->type, $activityB->loggable_type, $activityB->loggable_id]; } } diff --git a/app/Activity/Models/Comment.php b/app/Activity/Models/Comment.php index 6efa3df6ff0..0cb83d61e8d 100644 --- a/app/Activity/Models/Comment.php +++ b/app/Activity/Models/Comment.php @@ -4,28 +4,30 @@ use BookStack\App\Model; use BookStack\Users\Models\HasCreatorAndUpdater; +use BookStack\Users\Models\OwnableInterface; +use BookStack\Users\Models\User; +use BookStack\Util\HtmlContentFilter; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\MorphTo; /** * @property int $id - * @property string $text + * @property string $text - Deprecated & now unused (#4821) * @property string $html * @property int|null $parent_id - Relates to local_id, not id * @property int $local_id * @property string $entity_type * @property int $entity_id - * @property int $created_by - * @property int $updated_by + * @property string $content_ref + * @property bool $archived */ -class Comment extends Model implements Loggable +class Comment extends Model implements Loggable, OwnableInterface { use HasFactory; use HasCreatorAndUpdater; - protected $fillable = ['text', 'parent_id']; - protected $appends = ['created', 'updated']; + protected $fillable = ['parent_id']; /** * Get the entity that this comment belongs to. @@ -37,6 +39,7 @@ public function entity(): MorphTo /** * Get the parent comment this is in reply to (if existing). + * @return BelongsTo */ public function parent(): BelongsTo { @@ -53,24 +56,13 @@ public function isUpdated(): bool return $this->updated_at->timestamp > $this->created_at->timestamp; } - /** - * Get created date as a relative diff. - */ - public function getCreatedAttribute(): string - { - return $this->created_at->diffForHumans(); - } - - /** - * Get updated date as a relative diff. - */ - public function getUpdatedAttribute(): string + public function logDescriptor(): string { - return $this->updated_at->diffForHumans(); + return "Comment #{$this->local_id} (ID: {$this->id}) for {$this->entity_type} (ID: {$this->entity_id})"; } - public function logDescriptor(): string + public function safeHtml(): string { - return "Comment #{$this->local_id} (ID: {$this->id}) for {$this->entity_type} (ID: {$this->entity_id})"; + return HtmlContentFilter::removeScriptsFromHtmlString($this->html ?? ''); } } diff --git a/app/Activity/Models/Tag.php b/app/Activity/Models/Tag.php index 0af0a65ac76..0e7c68a270a 100644 --- a/app/Activity/Models/Tag.php +++ b/app/Activity/Models/Tag.php @@ -12,6 +12,8 @@ * @property int $id * @property string $name * @property string $value + * @property int $entity_id + * @property string $entity_type * @property int $order */ class Tag extends Model diff --git a/app/Activity/Notifications/Handlers/BaseNotificationHandler.php b/app/Activity/Notifications/Handlers/BaseNotificationHandler.php index b5f339b2ce0..bc6f2e22fc0 100644 --- a/app/Activity/Notifications/Handlers/BaseNotificationHandler.php +++ b/app/Activity/Notifications/Handlers/BaseNotificationHandler.php @@ -5,8 +5,10 @@ use BookStack\Activity\Models\Loggable; use BookStack\Activity\Notifications\Messages\BaseActivityNotification; use BookStack\Entities\Models\Entity; +use BookStack\Permissions\Permission; use BookStack\Permissions\PermissionApplicator; use BookStack\Users\Models\User; +use Illuminate\Support\Facades\Log; abstract class BaseNotificationHandler implements NotificationHandler { @@ -25,7 +27,7 @@ protected function sendNotificationToUserIds(string $notification, array $userId } // Prevent sending of the user does not have notification permissions - if (!$user->can('receive-notifications')) { + if (!$user->can(Permission::ReceiveNotifications)) { continue; } @@ -36,7 +38,11 @@ protected function sendNotificationToUserIds(string $notification, array $userId } // Send the notification - $user->notify(new $notification($detail, $initiator)); + try { + $user->notify(new $notification($detail, $initiator)); + } catch (\Exception $exception) { + Log::error("Failed to send email notification to user [id:{$user->id}] with error: {$exception->getMessage()}"); + } } } } diff --git a/app/Activity/Notifications/Handlers/PageUpdateNotificationHandler.php b/app/Activity/Notifications/Handlers/PageUpdateNotificationHandler.php index 744aba18f63..20dc0fc4d8e 100644 --- a/app/Activity/Notifications/Handlers/PageUpdateNotificationHandler.php +++ b/app/Activity/Notifications/Handlers/PageUpdateNotificationHandler.php @@ -20,7 +20,8 @@ public function handle(Activity $activity, Loggable|string $detail, User $user): throw new \InvalidArgumentException("Detail for page update notifications must be a page"); } - // Get last update from activity + // Get the last update from activity + /** @var ?Activity $lastUpdate */ $lastUpdate = $detail->activity() ->where('type', '=', ActivityType::PAGE_UPDATE) ->where('id', '!=', $activity->id) diff --git a/app/Activity/Notifications/Messages/BaseActivityNotification.php b/app/Activity/Notifications/Messages/BaseActivityNotification.php index ca86eb81bf8..067cd8f66e6 100644 --- a/app/Activity/Notifications/Messages/BaseActivityNotification.php +++ b/app/Activity/Notifications/Messages/BaseActivityNotification.php @@ -43,7 +43,7 @@ public function toArray($notifiable) protected function buildReasonFooterLine(LocaleDefinition $locale): LinkedMailMessageLine { return new LinkedMailMessageLine( - url('/preferences/notifications'), + url('/my-account/notifications'), $locale->trans('notifications.footer_reason'), $locale->trans('notifications.footer_reason_link'), ); diff --git a/app/Activity/TagRepo.php b/app/Activity/TagRepo.php index 4f2dbed5936..82c26b00e28 100644 --- a/app/Activity/TagRepo.php +++ b/app/Activity/TagRepo.php @@ -38,7 +38,8 @@ public function queryWithTotals(SimpleListOptions $listOptions, string $nameFilt DB::raw('SUM(IF(entity_type = \'book\', 1, 0)) as book_count'), DB::raw('SUM(IF(entity_type = \'bookshelf\', 1, 0)) as shelf_count'), ]) - ->orderBy($sort, $listOptions->getOrder()); + ->orderBy($sort, $listOptions->getOrder()) + ->whereHas('entity'); if ($nameFilter) { $query->where('name', '=', $nameFilter); diff --git a/app/Activity/Tools/ActivityLogger.php b/app/Activity/Tools/ActivityLogger.php index adda36c1b81..415d1108494 100644 --- a/app/Activity/Tools/ActivityLogger.php +++ b/app/Activity/Tools/ActivityLogger.php @@ -32,8 +32,8 @@ public function add(string $type, string|Loggable $detail = ''): void $activity->detail = $detailToStore; if ($detail instanceof Entity) { - $activity->entity_id = $detail->id; - $activity->entity_type = $detail->getMorphClass(); + $activity->loggable_id = $detail->id; + $activity->loggable_type = $detail->getMorphClass(); } $activity->save(); @@ -64,9 +64,9 @@ protected function newActivityForUser(string $type): Activity public function removeEntity(Entity $entity): void { $entity->activity()->update([ - 'detail' => $entity->name, - 'entity_id' => null, - 'entity_type' => null, + 'detail' => $entity->name, + 'loggable_id' => null, + 'loggable_type' => null, ]); } diff --git a/app/Activity/Tools/CommentTree.php b/app/Activity/Tools/CommentTree.php index 3303add39b8..66df2943086 100644 --- a/app/Activity/Tools/CommentTree.php +++ b/app/Activity/Tools/CommentTree.php @@ -4,12 +4,13 @@ use BookStack\Activity\Models\Comment; use BookStack\Entities\Models\Page; +use BookStack\Permissions\Permission; class CommentTree { /** * The built nested tree structure array. - * @var array{comment: Comment, depth: int, children: array}[] + * @var CommentTreeNode[] */ protected array $tree; protected array $comments; @@ -28,7 +29,7 @@ public function enabled(): bool public function empty(): bool { - return count($this->tree) === 0; + return count($this->getActive()) === 0; } public function count(): int @@ -36,13 +37,51 @@ public function count(): int return count($this->comments); } - public function get(): array + public function getActive(): array { - return $this->tree; + return array_filter($this->tree, fn (CommentTreeNode $node) => !$node->comment->archived); + } + + public function activeThreadCount(): int + { + return count($this->getActive()); + } + + public function getArchived(): array + { + return array_filter($this->tree, fn (CommentTreeNode $node) => $node->comment->archived); + } + + public function archivedThreadCount(): int + { + return count($this->getArchived()); + } + + public function getCommentNodeForId(int $commentId): ?CommentTreeNode + { + foreach ($this->tree as $node) { + if ($node->comment->id === $commentId) { + return $node; + } + } + + return null; + } + + public function canUpdateAny(): bool + { + foreach ($this->comments as $comment) { + if (userCan(Permission::CommentUpdate, $comment)) { + return true; + } + } + + return false; } /** * @param Comment[] $comments + * @return CommentTreeNode[] */ protected function createTree(array $comments): array { @@ -66,26 +105,22 @@ protected function createTree(array $comments): array $tree = []; foreach ($childMap[0] ?? [] as $childId) { - $tree[] = $this->createTreeForId($childId, 0, $byId, $childMap); + $tree[] = $this->createTreeNodeForId($childId, 0, $byId, $childMap); } return $tree; } - protected function createTreeForId(int $id, int $depth, array &$byId, array &$childMap): array + protected function createTreeNodeForId(int $id, int $depth, array &$byId, array &$childMap): CommentTreeNode { $childIds = $childMap[$id] ?? []; $children = []; foreach ($childIds as $childId) { - $children[] = $this->createTreeForId($childId, $depth + 1, $byId, $childMap); + $children[] = $this->createTreeNodeForId($childId, $depth + 1, $byId, $childMap); } - return [ - 'comment' => $byId[$id], - 'depth' => $depth, - 'children' => $children, - ]; + return new CommentTreeNode($byId[$id], $depth, $children); } protected function loadComments(): array diff --git a/app/Activity/Tools/CommentTreeNode.php b/app/Activity/Tools/CommentTreeNode.php new file mode 100644 index 00000000000..7b280bd2d95 --- /dev/null +++ b/app/Activity/Tools/CommentTreeNode.php @@ -0,0 +1,23 @@ +comment = $comment; + $this->depth = $depth; + $this->children = $children; + } +} diff --git a/app/Activity/Tools/TagClassGenerator.php b/app/Activity/Tools/TagClassGenerator.php index 1a1bd16c881..0f7aa1fe0c4 100644 --- a/app/Activity/Tools/TagClassGenerator.php +++ b/app/Activity/Tools/TagClassGenerator.php @@ -3,17 +3,16 @@ namespace BookStack\Activity\Tools; use BookStack\Activity\Models\Tag; +use BookStack\Entities\Models\BookChild; +use BookStack\Entities\Models\Entity; +use BookStack\Entities\Models\Page; +use BookStack\Permissions\Permission; class TagClassGenerator { - protected array $tags; - - /** - * @param Tag[] $tags - */ - public function __construct(array $tags) - { - $this->tags = $tags; + public function __construct( + protected Entity $entity + ) { } /** @@ -22,14 +21,23 @@ public function __construct(array $tags) public function generate(): array { $classes = []; + $tags = $this->entity->tags->all(); + + foreach ($tags as $tag) { + array_push($classes, ...$this->generateClassesForTag($tag)); + } + + if ($this->entity instanceof BookChild && userCan(Permission::BookView, $this->entity->book)) { + $bookTags = $this->entity->book->tags; + foreach ($bookTags as $bookTag) { + array_push($classes, ...$this->generateClassesForTag($bookTag, 'book-')); + } + } - foreach ($this->tags as $tag) { - $name = $this->normalizeTagClassString($tag->name); - $value = $this->normalizeTagClassString($tag->value); - $classes[] = 'tag-name-' . $name; - if ($value) { - $classes[] = 'tag-value-' . $value; - $classes[] = 'tag-pair-' . $name . '-' . $value; + if ($this->entity instanceof Page && $this->entity->chapter && userCan(Permission::ChapterView, $this->entity->chapter)) { + $chapterTags = $this->entity->chapter->tags; + foreach ($chapterTags as $chapterTag) { + array_push($classes, ...$this->generateClassesForTag($chapterTag, 'chapter-')); } } @@ -41,6 +49,22 @@ public function generateAsString(): string return implode(' ', $this->generate()); } + /** + * @return string[] + */ + protected function generateClassesForTag(Tag $tag, string $prefix = ''): array + { + $classes = []; + $name = $this->normalizeTagClassString($tag->name); + $value = $this->normalizeTagClassString($tag->value); + $classes[] = "{$prefix}tag-name-{$name}"; + if ($value) { + $classes[] = "{$prefix}tag-value-{$value}"; + $classes[] = "{$prefix}tag-pair-{$name}-{$value}"; + } + return $classes; + } + protected function normalizeTagClassString(string $value): string { $value = str_replace(' ', '', strtolower($value)); diff --git a/app/Activity/Tools/UserEntityWatchOptions.php b/app/Activity/Tools/UserEntityWatchOptions.php index 559d7903d6f..8e5f70758af 100644 --- a/app/Activity/Tools/UserEntityWatchOptions.php +++ b/app/Activity/Tools/UserEntityWatchOptions.php @@ -7,6 +7,7 @@ use BookStack\Entities\Models\BookChild; use BookStack\Entities\Models\Entity; use BookStack\Entities\Models\Page; +use BookStack\Permissions\Permission; use BookStack\Users\Models\User; use Illuminate\Database\Eloquent\Builder; @@ -22,7 +23,7 @@ public function __construct( public function canWatch(): bool { - return $this->user->can('receive-notifications') && !$this->user->isGuest(); + return $this->user->can(Permission::ReceiveNotifications) && !$this->user->isGuest(); } public function getWatchLevel(): string diff --git a/app/Activity/Tools/WebhookFormatter.php b/app/Activity/Tools/WebhookFormatter.php index 6ccb084b8de..cb4e9cb0a9c 100644 --- a/app/Activity/Tools/WebhookFormatter.php +++ b/app/Activity/Tools/WebhookFormatter.php @@ -50,7 +50,7 @@ public function format(): array } if ($this->detail instanceof Model) { - $data['related_item'] = $this->formatModel(); + $data['related_item'] = $this->formatModel($this->detail); } return $data; @@ -83,10 +83,8 @@ public function addDefaultModelFormatters(): void ); } - protected function formatModel(): array + protected function formatModel(Model $model): array { - /** @var Model $model */ - $model = $this->detail; $model->unsetRelations(); foreach ($this->modelFormatters as $formatter) { diff --git a/app/Activity/WatchLevels.php b/app/Activity/WatchLevels.php index de3c5e12288..edbece2d371 100644 --- a/app/Activity/WatchLevels.php +++ b/app/Activity/WatchLevels.php @@ -36,7 +36,7 @@ class WatchLevels /** * Get all the possible values as an option_name => value array. - * @returns array + * @return array */ public static function all(): array { @@ -50,7 +50,7 @@ public static function all(): array /** * Get the watch options suited for the given entity. - * @returns array + * @return array */ public static function allSuitedFor(Entity $entity): array { diff --git a/app/Api/ApiDocsGenerator.php b/app/Api/ApiDocsGenerator.php index bffc38623bf..287c8387790 100644 --- a/app/Api/ApiDocsGenerator.php +++ b/app/Api/ApiDocsGenerator.php @@ -2,12 +2,12 @@ namespace BookStack\Api; +use BookStack\App\AppVersion; use BookStack\Http\ApiController; use Exception; use Illuminate\Contracts\Container\BindingResolutionException; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Cache; -use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Route; use Illuminate\Support\Str; use Illuminate\Validation\Rules\Password; @@ -26,7 +26,7 @@ class ApiDocsGenerator */ public static function generateConsideringCache(): Collection { - $appVersion = trim(file_get_contents(base_path('version'))); + $appVersion = AppVersion::get(); $cacheKey = 'api-docs::' . $appVersion; $isProduction = config('app.env') === 'production'; $cacheVal = $isProduction ? Cache::get($cacheKey) : null; diff --git a/app/Api/ApiEntityListFormatter.php b/app/Api/ApiEntityListFormatter.php index 436d66d598e..3c94d96ee60 100644 --- a/app/Api/ApiEntityListFormatter.php +++ b/app/Api/ApiEntityListFormatter.php @@ -2,7 +2,9 @@ namespace BookStack\Api; +use BookStack\Entities\Models\BookChild; use BookStack\Entities\Models\Entity; +use BookStack\Entities\Models\Page; class ApiEntityListFormatter { @@ -20,8 +22,16 @@ class ApiEntityListFormatter * @var array */ protected array $fields = [ - 'id', 'name', 'slug', 'book_id', 'chapter_id', 'draft', - 'template', 'priority', 'created_at', 'updated_at', + 'id', + 'name', + 'slug', + 'book_id', + 'chapter_id', + 'draft', + 'template', + 'priority', + 'created_at', + 'updated_at', ]; public function __construct(array $list) @@ -62,6 +72,28 @@ public function withTags(): self return $this; } + /** + * Include parent book/chapter info in the formatted data. + */ + public function withParents(): self + { + $this->withField('book', function (Entity $entity) { + if ($entity instanceof BookChild && $entity->book) { + return $entity->book->only(['id', 'name', 'slug']); + } + return null; + }); + + $this->withField('chapter', function (Entity $entity) { + if ($entity instanceof Page && $entity->chapter) { + return $entity->chapter->only(['id', 'name', 'slug']); + } + return null; + }); + + return $this; + } + /** * Format the data and return an array of formatted content. * @return array[] diff --git a/app/Api/ApiTokenGuard.php b/app/Api/ApiTokenGuard.php index 6302884a912..9f4537b296b 100644 --- a/app/Api/ApiTokenGuard.php +++ b/app/Api/ApiTokenGuard.php @@ -4,6 +4,7 @@ use BookStack\Access\LoginService; use BookStack\Exceptions\ApiAuthException; +use BookStack\Permissions\Permission; use Illuminate\Auth\GuardHelpers; use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Contracts\Auth\Guard; @@ -146,7 +147,7 @@ protected function validateToken(?ApiToken $token, string $secret): void throw new ApiAuthException(trans('errors.api_user_token_expired'), 403); } - if (!$token->user->can('access-api')) { + if (!$token->user->can(Permission::AccessApi)) { throw new ApiAuthException(trans('errors.api_user_no_api_permission'), 403); } } diff --git a/app/Api/UserApiTokenController.php b/app/Api/UserApiTokenController.php index 3606e826094..2ca9e22352e 100644 --- a/app/Api/UserApiTokenController.php +++ b/app/Api/UserApiTokenController.php @@ -4,6 +4,7 @@ use BookStack\Activity\ActivityType; use BookStack\Http\Controller; +use BookStack\Permissions\Permission; use BookStack\Users\Models\User; use Illuminate\Http\Request; use Illuminate\Support\Facades\Hash; @@ -16,8 +17,8 @@ class UserApiTokenController extends Controller */ public function create(Request $request, int $userId) { - $this->checkPermission('access-api'); - $this->checkPermissionOrCurrentUser('users-manage', $userId); + $this->checkPermission(Permission::AccessApi); + $this->checkPermissionOrCurrentUser(Permission::UsersManage, $userId); $this->updateContext($request); $user = User::query()->findOrFail($userId); @@ -35,8 +36,8 @@ public function create(Request $request, int $userId) */ public function store(Request $request, int $userId) { - $this->checkPermission('access-api'); - $this->checkPermissionOrCurrentUser('users-manage', $userId); + $this->checkPermission(Permission::AccessApi); + $this->checkPermissionOrCurrentUser(Permission::UsersManage, $userId); $this->validate($request, [ 'name' => ['required', 'max:250'], @@ -143,8 +144,8 @@ public function destroy(int $userId, int $tokenId) */ protected function checkPermissionAndFetchUserToken(int $userId, int $tokenId): array { - $this->checkPermissionOr('users-manage', function () use ($userId) { - return $userId === user()->id && userCan('access-api'); + $this->checkPermissionOr(Permission::UsersManage, function () use ($userId) { + return $userId === user()->id && userCan(Permission::AccessApi); }); $user = User::query()->findOrFail($userId); diff --git a/app/App/AppVersion.php b/app/App/AppVersion.php new file mode 100644 index 00000000000..af422f64114 --- /dev/null +++ b/app/App/AppVersion.php @@ -0,0 +1,24 @@ +latest(10); $draftPages = []; if ($this->isSignedIn()) { - $draftPages = Page::visible() - ->where('draft', '=', true) - ->where('created_by', '=', user()->id) + $draftPages = $this->queries->pages->currentUserDraftsForList() ->orderBy('updated_at', 'desc') ->with('book') ->take(6) @@ -37,14 +41,13 @@ public function index(Request $request, ActivityQueries $activities) $recentFactor = count($draftPages) > 0 ? 0.5 : 1; $recents = $this->isSignedIn() ? - (new RecentlyViewed())->run(12 * $recentFactor, 1) - : Book::visible()->orderBy('created_at', 'desc')->take(12 * $recentFactor)->get(); - $favourites = (new TopFavourites())->run(6); - $recentlyUpdatedPages = Page::visible()->with('book') + $recentlyViewed->run(12 * $recentFactor, 1) + : $this->queries->books->visibleForList()->orderBy('created_at', 'desc')->take(12 * $recentFactor)->get(); + $favourites = $topFavourites->run(6); + $recentlyUpdatedPages = $this->queries->pages->visibleForList() ->where('draft', false) ->orderBy('updated_at', 'desc') ->take($favourites->count() > 0 ? 5 : 10) - ->select(Page::$listAttributes) ->get(); $homepageOptions = ['default', 'books', 'bookshelves', 'page']; @@ -78,14 +81,18 @@ public function index(Request $request, ActivityQueries $activities) } if ($homepageOption === 'bookshelves') { - $shelves = app()->make(BookshelfRepo::class)->getAllPaginated(18, $commonData['listOptions']->getSort(), $commonData['listOptions']->getOrder()); + $shelves = $this->queries->shelves->visibleForListWithCover() + ->orderBy($commonData['listOptions']->getSort(), $commonData['listOptions']->getOrder()) + ->paginate(18); $data = array_merge($commonData, ['shelves' => $shelves]); return view('home.shelves', $data); } if ($homepageOption === 'books') { - $books = app()->make(BookRepo::class)->getAllPaginated(18, $commonData['listOptions']->getSort(), $commonData['listOptions']->getOrder()); + $books = $this->queries->books->visibleForListWithCover() + ->orderBy($commonData['listOptions']->getSort(), $commonData['listOptions']->getOrder()) + ->paginate(18); $data = array_merge($commonData, ['books' => $books]); return view('home.books', $data); @@ -95,7 +102,7 @@ public function index(Request $request, ActivityQueries $activities) $homepageSetting = setting('app-homepage', '0:'); $id = intval(explode(':', $homepageSetting)[0]); /** @var Page $customHomepage */ - $customHomepage = Page::query()->where('draft', '=', false)->findOrFail($id); + $customHomepage = $this->queries->pages->start()->where('draft', '=', false)->findOrFail($id); $pageContent = new PageContent($customHomepage); $customHomepage->html = $pageContent->render(false); @@ -104,48 +111,4 @@ public function index(Request $request, ActivityQueries $activities) return view('home.default', $commonData); } - - /** - * Show the view for /robots.txt. - */ - public function robots() - { - $sitePublic = setting('app-public', false); - $allowRobots = config('app.allow_robots'); - - if ($allowRobots === null) { - $allowRobots = $sitePublic; - } - - return response() - ->view('misc.robots', ['allowRobots' => $allowRobots]) - ->header('Content-Type', 'text/plain'); - } - - /** - * Show the route for 404 responses. - */ - public function notFound() - { - return response()->view('errors.404', [], 404); - } - - /** - * Serve the application favicon. - * Ensures a 'favicon.ico' file exists at the web root location (if writable) to be served - * directly by the webserver in the future. - */ - public function favicon(FaviconHandler $favicons) - { - $exists = $favicons->restoreOriginalIfNotExists(); - return response()->file($exists ? $favicons->getPath() : $favicons->getOriginalPath()); - } - - /** - * Serve a PWA application manifest. - */ - public function pwaManifest(PwaManifestBuilder $manifestBuilder) - { - return response()->json($manifestBuilder->build()); - } } diff --git a/app/App/MetaController.php b/app/App/MetaController.php new file mode 100644 index 00000000000..a94334c5851 --- /dev/null +++ b/app/App/MetaController.php @@ -0,0 +1,77 @@ +view('misc.robots', ['allowRobots' => $allowRobots]) + ->header('Content-Type', 'text/plain'); + } + + /** + * Show the route for 404 responses. + */ + public function notFound() + { + return response()->view('errors.404', [], 404); + } + + /** + * Serve the application favicon. + * Ensures a 'favicon.ico' file exists at the web root location (if writable) to be served + * directly by the webserver in the future. + */ + public function favicon(FaviconHandler $favicons) + { + $exists = $favicons->restoreOriginalIfNotExists(); + return response()->file($exists ? $favicons->getPath() : $favicons->getOriginalPath()); + } + + /** + * Serve a PWA application manifest. + */ + public function pwaManifest(PwaManifestBuilder $manifestBuilder) + { + return response()->json($manifestBuilder->build()); + } + + /** + * Show license information for the application. + */ + public function licenses() + { + $this->setPageTitle(trans('settings.licenses')); + + return view('help.licenses', [ + 'license' => file_get_contents(base_path('LICENSE')), + 'phpLibData' => file_get_contents(base_path('dev/licensing/php-library-licenses.txt')), + 'jsLibData' => file_get_contents(base_path('dev/licensing/js-library-licenses.txt')), + ]); + } + + /** + * Show the view for /opensearch.xml. + */ + public function opensearch() + { + return response() + ->view('misc.opensearch') + ->header('Content-Type', 'application/opensearchdescription+xml'); + } +} diff --git a/app/App/Model.php b/app/App/Model.php index 8de5a276205..e1c7511c14f 100644 --- a/app/App/Model.php +++ b/app/App/Model.php @@ -8,7 +8,7 @@ class Model extends EloquentModel { /** * Provides public access to get the raw attribute value from the model. - * Used in areas where no mutations are required but performance is critical. + * Used in areas where no mutations are required, but performance is critical. * * @return mixed */ diff --git a/app/App/Providers/AppServiceProvider.php b/app/App/Providers/AppServiceProvider.php index 0f4dc55dd2f..9012a07ebf4 100644 --- a/app/App/Providers/AppServiceProvider.php +++ b/app/App/Providers/AppServiceProvider.php @@ -25,7 +25,7 @@ class AppServiceProvider extends ServiceProvider * Custom container bindings to register. * @var string[] */ - public $bindings = [ + public array $bindings = [ ExceptionRenderer::class => BookStackExceptionHandlerPage::class, ]; @@ -33,7 +33,7 @@ class AppServiceProvider extends ServiceProvider * Custom singleton bindings to register. * @var string[] */ - public $singletons = [ + public array $singletons = [ 'activity' => ActivityLogger::class, SettingService::class => SettingService::class, SocialDriverManager::class => SocialDriverManager::class, @@ -41,12 +41,20 @@ class AppServiceProvider extends ServiceProvider HttpRequestService::class => HttpRequestService::class, ]; + /** + * Register any application services. + */ + public function register(): void + { + $this->app->singleton(PermissionApplicator::class, function ($app) { + return new PermissionApplicator(null); + }); + } + /** * Bootstrap any application services. - * - * @return void */ - public function boot() + public function boot(): void { // Set root URL $appUrl = config('app.url'); @@ -67,16 +75,4 @@ public function boot() 'page' => Page::class, ]); } - - /** - * Register any application services. - * - * @return void - */ - public function register() - { - $this->app->singleton(PermissionApplicator::class, function ($app) { - return new PermissionApplicator(null); - }); - } } diff --git a/app/App/Providers/AuthServiceProvider.php b/app/App/Providers/AuthServiceProvider.php index 26d1803107d..6a816252131 100644 --- a/app/App/Providers/AuthServiceProvider.php +++ b/app/App/Providers/AuthServiceProvider.php @@ -18,10 +18,8 @@ class AuthServiceProvider extends ServiceProvider { /** * Bootstrap the application services. - * - * @return void */ - public function boot() + public function boot(): void { // Password Configuration // Changes here must be reflected in ApiDocsGenerate@getValidationAsString. @@ -58,13 +56,11 @@ public function boot() /** * Register the application services. - * - * @return void */ - public function register() + public function register(): void { - Auth::provider('external-users', function ($app, array $config) { - return new ExternalBaseUserProvider($config['model']); + Auth::provider('external-users', function () { + return new ExternalBaseUserProvider(); }); // Bind and provide the default system user as a singleton to the app instance when needed. diff --git a/app/App/Providers/EventServiceProvider.php b/app/App/Providers/EventServiceProvider.php index 4ec9facdfd7..60e78efe0e9 100644 --- a/app/App/Providers/EventServiceProvider.php +++ b/app/App/Providers/EventServiceProvider.php @@ -15,7 +15,7 @@ class EventServiceProvider extends ServiceProvider /** * The event listener mappings for the application. * - * @var array> + * @var array> */ protected $listen = [ SocialiteWasCalled::class => [ @@ -29,21 +29,25 @@ class EventServiceProvider extends ServiceProvider /** * Register any events for your application. - * - * @return void */ - public function boot() + public function boot(): void { // } /** * Determine if events and listeners should be automatically discovered. - * - * @return bool */ - public function shouldDiscoverEvents() + public function shouldDiscoverEvents(): bool { return false; } + + /** + * Overrides the registration of Laravel's default email verification system + */ + protected function configureEmailVerification(): void + { + // + } } diff --git a/app/App/Providers/RouteServiceProvider.php b/app/App/Providers/RouteServiceProvider.php index abd5562449b..97c3e7c770d 100644 --- a/app/App/Providers/RouteServiceProvider.php +++ b/app/App/Providers/RouteServiceProvider.php @@ -24,10 +24,8 @@ class RouteServiceProvider extends ServiceProvider /** * Define your route model bindings, pattern filters, etc. - * - * @return void */ - public function boot() + public function boot(): void { $this->configureRateLimiting(); @@ -41,10 +39,8 @@ public function boot() * Define the "web" routes for the application. * * These routes all receive session state, CSRF protection, etc. - * - * @return void */ - protected function mapWebRoutes() + protected function mapWebRoutes(): void { Route::group([ 'middleware' => 'web', @@ -65,10 +61,8 @@ protected function mapWebRoutes() * Define the "api" routes for the application. * * These routes are typically stateless. - * - * @return void */ - protected function mapApiRoutes() + protected function mapApiRoutes(): void { Route::group([ 'middleware' => 'api', @@ -81,13 +75,22 @@ protected function mapApiRoutes() /** * Configure the rate limiters for the application. - * - * @return void */ - protected function configureRateLimiting() + protected function configureRateLimiting(): void { RateLimiter::for('api', function (Request $request) { return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip()); }); + + RateLimiter::for('public', function (Request $request) { + return Limit::perMinute(10)->by($request->ip()); + }); + + RateLimiter::for('exports', function (Request $request) { + $user = user(); + $attempts = $user->isGuest() ? 4 : 10; + $key = $user->isGuest() ? $request->ip() : $user->id; + return Limit::perMinute($attempts)->by($key); + }); } } diff --git a/app/App/Providers/ThemeServiceProvider.php b/app/App/Providers/ThemeServiceProvider.php index c15b43a6b4c..2cf581d3863 100644 --- a/app/App/Providers/ThemeServiceProvider.php +++ b/app/App/Providers/ThemeServiceProvider.php @@ -4,17 +4,14 @@ use BookStack\Theming\ThemeEvents; use BookStack\Theming\ThemeService; -use Illuminate\Support\Facades\Route; use Illuminate\Support\ServiceProvider; class ThemeServiceProvider extends ServiceProvider { /** * Register services. - * - * @return void */ - public function register() + public function register(): void { // Register the ThemeService as a singleton $this->app->singleton(ThemeService::class, fn ($app) => new ThemeService()); @@ -22,10 +19,8 @@ public function register() /** * Bootstrap services. - * - * @return void */ - public function boot() + public function boot(): void { // Boot up the theme system $themeService = $this->app->make(ThemeService::class); diff --git a/app/App/Providers/TranslationServiceProvider.php b/app/App/Providers/TranslationServiceProvider.php index c1752f38e3e..b838129a601 100644 --- a/app/App/Providers/TranslationServiceProvider.php +++ b/app/App/Providers/TranslationServiceProvider.php @@ -11,10 +11,8 @@ class TranslationServiceProvider extends BaseProvider { /** * Register the service provider. - * - * @return void */ - public function register() + public function register(): void { $this->registerLoader(); @@ -41,10 +39,8 @@ public function register() /** * Register the translation line loader. * Overrides the default register action from Laravel so a custom loader can be used. - * - * @return void */ - protected function registerLoader() + protected function registerLoader(): void { $this->app->singleton('translation.loader', function ($app) { return new FileLoader($app['files'], $app['path.lang']); diff --git a/app/App/Providers/ViewTweaksServiceProvider.php b/app/App/Providers/ViewTweaksServiceProvider.php index 10593ac8bf8..6771e513fa6 100644 --- a/app/App/Providers/ViewTweaksServiceProvider.php +++ b/app/App/Providers/ViewTweaksServiceProvider.php @@ -3,6 +3,7 @@ namespace BookStack\App\Providers; use BookStack\Entities\BreadcrumbsViewComposer; +use BookStack\Util\DateFormatter; use Illuminate\Pagination\Paginator; use Illuminate\Support\Facades\Blade; use Illuminate\Support\Facades\View; @@ -10,12 +11,19 @@ class ViewTweaksServiceProvider extends ServiceProvider { + public function register() + { + $this->app->singleton(DateFormatter::class, function ($app) { + return new DateFormatter( + $app['config']->get('app.display_timezone'), + ); + }); + } + /** * Bootstrap services. - * - * @return void */ - public function boot() + public function boot(): void { // Set paginator to use bootstrap-style pagination Paginator::useBootstrap(); @@ -23,6 +31,9 @@ public function boot() // View Composers View::composer('entities.breadcrumbs', BreadcrumbsViewComposer::class); + // View Globals + View::share('dates', $this->app->make(DateFormatter::class)); + // Custom blade view directives Blade::directive('icon', function ($expression) { return "toHtml(); ?>"; diff --git a/app/App/PwaManifestBuilder.php b/app/App/PwaManifestBuilder.php index ba4331a0f14..81ab2fcd711 100644 --- a/app/App/PwaManifestBuilder.php +++ b/app/App/PwaManifestBuilder.php @@ -26,7 +26,7 @@ public function build(): array "launch_handler" => [ "client_mode" => "focus-existing" ], - "orientation" => "portrait", + "orientation" => "any", "icons" => [ [ "src" => setting('app-icon-32') ?: url('/icon-32.png'), diff --git a/app/App/Sluggable.php b/app/App/SluggableInterface.php similarity index 75% rename from app/App/Sluggable.php rename to app/App/SluggableInterface.php index f8da2e26517..96af49cd323 100644 --- a/app/App/Sluggable.php +++ b/app/App/SluggableInterface.php @@ -5,11 +5,8 @@ /** * Assigned to models that can have slugs. * Must have the below properties. - * - * @property int $id - * @property string $name */ -interface Sluggable +interface SluggableInterface { /** * Regenerate the slug for this model. diff --git a/app/App/SystemApiController.php b/app/App/SystemApiController.php new file mode 100644 index 00000000000..c476a74e998 --- /dev/null +++ b/app/App/SystemApiController.php @@ -0,0 +1,31 @@ +json([ + 'version' => AppVersion::get(), + 'instance_id' => setting('instance-id'), + 'app_name' => setting('app-name'), + 'app_logo' => $logo, + 'base_url' => url('/'), + ]); + } +} diff --git a/app/App/helpers.php b/app/App/helpers.php index af6dbcfc397..0e357e36aee 100644 --- a/app/App/helpers.php +++ b/app/App/helpers.php @@ -1,6 +1,9 @@ can($permission); } @@ -58,7 +56,7 @@ function userCan(string $permission, Model $ownable = null): bool * Check if the current user can perform the given action on any items in the system. * Can be provided the class name of an entity to filter ability to that specific entity type. */ -function userCanOnAny(string $action, string $entityClass = ''): bool +function userCanOnAny(string|Permission $action, string $entityClass = ''): bool { $permissions = app()->make(PermissionApplicator::class); @@ -70,7 +68,7 @@ function userCanOnAny(string $action, string $entityClass = ''): bool * * @return mixed|SettingService */ -function setting(string $key = null, $default = null) +function setting(?string $key = null, mixed $default = null): mixed { $settingService = app()->make(SettingService::class); @@ -88,43 +86,10 @@ function setting(string $key = null, $default = null) */ function theme_path(string $path = ''): ?string { - $theme = config('view.theme'); - + $theme = Theme::getTheme(); if (!$theme) { return null; } return base_path('themes/' . $theme . ($path ? DIRECTORY_SEPARATOR . $path : $path)); } - -/** - * Generate a URL with multiple parameters for sorting purposes. - * Works out the logic to set the correct sorting direction - * Discards empty parameters and allows overriding. - */ -function sortUrl(string $path, array $data, array $overrideData = []): string -{ - $queryStringSections = []; - $queryData = array_merge($data, $overrideData); - - // Change sorting direction is already sorted on current attribute - if (isset($overrideData['sort']) && $overrideData['sort'] === $data['sort']) { - $queryData['order'] = ($data['order'] === 'asc') ? 'desc' : 'asc'; - } elseif (isset($overrideData['sort'])) { - $queryData['order'] = 'asc'; - } - - foreach ($queryData as $name => $value) { - $trimmedVal = trim($value); - if ($trimmedVal === '') { - continue; - } - $queryStringSections[] = urlencode($name) . '=' . urlencode($trimmedVal); - } - - if (count($queryStringSections) === 0) { - return url($path); - } - - return url($path . '?' . implode('&', $queryStringSections)); -} diff --git a/app/Config/app.php b/app/Config/app.php index fc913eb8fee..40e542d3e16 100644 --- a/app/Config/app.php +++ b/app/Config/app.php @@ -9,6 +9,7 @@ */ use Illuminate\Support\Facades\Facade; +use Illuminate\Support\ServiceProvider; return [ @@ -69,8 +70,8 @@ // A list of the sources/hostnames that can be reached by application SSR calls. // This is used wherever users can provide URLs/hosts in-platform, like for webhooks. // Host-specific functionality (usually controlled via other options) like auth - // or user avatars for example, won't use this list. - // Space seperated if multiple. Can use '*' as a wildcard. + // or user avatars, for example, won't use this list. + // Space separated if multiple. Can use '*' as a wildcard. // Values will be compared prefix-matched, case-insensitive, against called SSR urls. // Defaults to allow all hosts. 'ssr_hosts' => env('ALLOWED_SSR_HOSTS', '*'), @@ -79,8 +80,10 @@ // Integer value between 0 (IP hidden) to 4 (Full IP usage) 'ip_address_precision' => env('IP_ADDRESS_PRECISION', 4), - // Application timezone for back-end date functions. + // Application timezone for stored date/time values. 'timezone' => env('APP_TIMEZONE', 'UTC'), + // Application timezone for displayed date/time values in the UI. + 'display_timezone' => env('APP_DISPLAY_TIMEZONE', env('APP_TIMEZONE', 'UTC')), // Default locale to use // A default variant is also stored since Laravel can overwrite @@ -113,46 +116,20 @@ ], // Application Service Providers - 'providers' => [ - - // Laravel Framework Service Providers... - Illuminate\Auth\AuthServiceProvider::class, - Illuminate\Broadcasting\BroadcastServiceProvider::class, - Illuminate\Bus\BusServiceProvider::class, - Illuminate\Cache\CacheServiceProvider::class, - Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class, - Illuminate\Cookie\CookieServiceProvider::class, - Illuminate\Database\DatabaseServiceProvider::class, - Illuminate\Encryption\EncryptionServiceProvider::class, - Illuminate\Filesystem\FilesystemServiceProvider::class, - Illuminate\Foundation\Providers\FoundationServiceProvider::class, - Illuminate\Hashing\HashServiceProvider::class, - Illuminate\Mail\MailServiceProvider::class, - Illuminate\Notifications\NotificationServiceProvider::class, - Illuminate\Pagination\PaginationServiceProvider::class, - Illuminate\Pipeline\PipelineServiceProvider::class, - Illuminate\Queue\QueueServiceProvider::class, - Illuminate\Redis\RedisServiceProvider::class, - Illuminate\Auth\Passwords\PasswordResetServiceProvider::class, - Illuminate\Session\SessionServiceProvider::class, - Illuminate\Validation\ValidationServiceProvider::class, - Illuminate\View\ViewServiceProvider::class, - + 'providers' => ServiceProvider::defaultProviders()->merge([ // Third party service providers - Barryvdh\DomPDF\ServiceProvider::class, - Barryvdh\Snappy\ServiceProvider::class, SocialiteProviders\Manager\ServiceProvider::class, // BookStack custom service providers - \BookStack\App\Providers\ThemeServiceProvider::class, - \BookStack\App\Providers\AppServiceProvider::class, - \BookStack\App\Providers\AuthServiceProvider::class, - \BookStack\App\Providers\EventServiceProvider::class, - \BookStack\App\Providers\RouteServiceProvider::class, - \BookStack\App\Providers\TranslationServiceProvider::class, - \BookStack\App\Providers\ValidationRuleServiceProvider::class, - \BookStack\App\Providers\ViewTweaksServiceProvider::class, - ], + BookStack\App\Providers\ThemeServiceProvider::class, + BookStack\App\Providers\AppServiceProvider::class, + BookStack\App\Providers\AuthServiceProvider::class, + BookStack\App\Providers\EventServiceProvider::class, + BookStack\App\Providers\RouteServiceProvider::class, + BookStack\App\Providers\TranslationServiceProvider::class, + BookStack\App\Providers\ValidationRuleServiceProvider::class, + BookStack\App\Providers\ViewTweaksServiceProvider::class, + ])->toArray(), // Class Aliases // This array of class aliases to be registered on application start. diff --git a/app/Config/broadcasting.php b/app/Config/broadcasting.php deleted file mode 100644 index 3b95698ccc2..00000000000 --- a/app/Config/broadcasting.php +++ /dev/null @@ -1,37 +0,0 @@ - 'null', - - // Broadcast Connections - // Here you may define all of the broadcast connections that will be used - // to broadcast events to other systems or over websockets. Samples of - // each available type of connection are provided inside this array. - 'connections' => [ - - // Default options removed since we don't use broadcasting. - - 'log' => [ - 'driver' => 'log', - ], - - 'null' => [ - 'driver' => 'null', - ], - - ], - -]; diff --git a/app/Config/cache.php b/app/Config/cache.php index d1d47ab16c2..01c822a653b 100644 --- a/app/Config/cache.php +++ b/app/Config/cache.php @@ -35,10 +35,6 @@ // Available caches stores 'stores' => [ - 'apc' => [ - 'driver' => 'apc', - ], - 'array' => [ 'driver' => 'array', 'serialize' => false, @@ -49,11 +45,13 @@ 'table' => 'cache', 'connection' => null, 'lock_connection' => null, + 'lock_table' => null, ], 'file' => [ 'driver' => 'file', 'path' => storage_path('framework/cache'), + 'lock_path' => storage_path('framework/cache'), ], 'memcached' => [ @@ -87,6 +85,6 @@ | */ - 'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_') . '_cache_'), + 'prefix' => env('CACHE_PREFIX', 'bookstack_cache_'), ]; diff --git a/app/Config/clockwork.php b/app/Config/clockwork.php index 394af845172..bd59eaf71d8 100644 --- a/app/Config/clockwork.php +++ b/app/Config/clockwork.php @@ -173,6 +173,8 @@ // List of URIs that should not be collected 'except' => [ + '/uploads/images/.*', // BookStack image requests + '/horizon/.*', // Laravel Horizon requests '/telescope/.*', // Laravel Telescope requests '/_debugbar/.*', // Laravel DebugBar requests diff --git a/app/Config/database.php b/app/Config/database.php index 59ac0f31beb..5edafa777c8 100644 --- a/app/Config/database.php +++ b/app/Config/database.php @@ -40,12 +40,16 @@ // MYSQL // Split out port from host if set -$mysql_host = env('DB_HOST', 'localhost'); -$mysql_host_exploded = explode(':', $mysql_host); -$mysql_port = env('DB_PORT', 3306); -if (count($mysql_host_exploded) > 1) { - $mysql_host = $mysql_host_exploded[0]; - $mysql_port = intval($mysql_host_exploded[1]); +$mysqlHost = env('DB_HOST', 'localhost'); +$mysqlHostExploded = explode(':', $mysqlHost); +$mysqlPort = env('DB_PORT', 3306); +$mysqlHostIpv6 = str_starts_with($mysqlHost, '['); +if ($mysqlHostIpv6 && str_contains($mysqlHost, ']:')) { + $mysqlHost = implode(':', array_slice($mysqlHostExploded, 0, -1)); + $mysqlPort = intval(end($mysqlHostExploded)); +} else if (!$mysqlHostIpv6 && count($mysqlHostExploded) > 1) { + $mysqlHost = $mysqlHostExploded[0]; + $mysqlPort = intval($mysqlHostExploded[1]); } return [ @@ -61,17 +65,17 @@ 'mysql' => [ 'driver' => 'mysql', 'url' => env('DATABASE_URL'), - 'host' => $mysql_host, + 'host' => $mysqlHost, 'database' => env('DB_DATABASE', 'forge'), 'username' => env('DB_USERNAME', 'forge'), 'password' => env('DB_PASSWORD', ''), 'unix_socket' => env('DB_SOCKET', ''), - 'port' => $mysql_port, + 'port' => $mysqlPort, 'charset' => 'utf8mb4', 'collation' => 'utf8mb4_unicode_ci', // Prefixes are only semi-supported and may be unstable // since they are not tested as part of our automated test suite. - // If used, the prefix should not be changed otherwise you will likely receive errors. + // If used, the prefix should not be changed; otherwise you will likely receive errors. 'prefix' => env('DB_TABLE_PREFIX', ''), 'prefix_indexes' => true, 'strict' => false, @@ -88,7 +92,7 @@ 'database' => 'bookstack-test', 'username' => env('MYSQL_USER', 'bookstack-test'), 'password' => env('MYSQL_PASSWORD', 'bookstack-test'), - 'port' => $mysql_port, + 'port' => $mysqlPort, 'charset' => 'utf8mb4', 'collation' => 'utf8mb4_unicode_ci', 'prefix' => '', @@ -99,9 +103,7 @@ ], // Migration Repository Table - // This table keeps track of all the migrations that have already run for - // your application. Using this information, we can determine which of - // the migrations on disk haven't actually been run in the database. + // This table keeps track of all the migrations that have already run for the application. 'migrations' => 'migrations', // Redis configuration to use if set diff --git a/app/Config/dompdf.php b/app/Config/exports.php similarity index 89% rename from app/Config/dompdf.php rename to app/Config/exports.php index 09dd91bcc3c..2e22bc759e3 100644 --- a/app/Config/dompdf.php +++ b/app/Config/exports.php @@ -1,23 +1,49 @@ 'A4', + 'letter' => 'Letter', +]; + $dompdfPaperSizeMap = [ 'a4' => 'a4', 'letter' => 'letter', ]; +$exportPageSize = env('EXPORT_PAGE_SIZE', 'a4'); + return [ - 'show_warnings' => false, // Throw an Exception on warnings from dompdf + // Set a command which can be used to convert a HTML file into a PDF file. + // When false this will not be used. + // String values represent the command to be called for conversion. + // Supports '{input_html_path}' and '{output_pdf_path}' placeholder values. + // Example: EXPORT_PDF_COMMAND="/scripts/convert.sh {input_html_path} {output_pdf_path}" + 'pdf_command' => env('EXPORT_PDF_COMMAND', false), + + // The amount of time allowed for PDF generation command to run + // before the process times out and is stopped. + 'pdf_command_timeout' => env('EXPORT_PDF_COMMAND_TIMEOUT', 15), + + // 2024-04: Snappy/WKHTMLtoPDF now considered deprecated in regard to BookStack support. + 'snappy' => [ + 'pdf_binary' => env('WKHTMLTOPDF', false), + 'options' => [ + 'print-media-type' => true, + 'outline' => true, + 'page-size' => $snappyPaperSizeMap[$exportPageSize] ?? 'A4', + ], + ], - 'options' => [ + 'dompdf' => [ /** * The location of the DOMPDF font directory. * @@ -88,6 +114,7 @@ * @var array */ 'allowed_protocols' => [ + "data://" => ["rules" => []], 'file://' => ['rules' => []], 'http://' => ['rules' => []], 'https://' => ['rules' => []], @@ -101,7 +128,7 @@ /** * Whether to enable font subsetting or not. */ - 'enable_fontsubsetting' => false, + 'enable_font_subsetting' => false, /** * The PDF rendering backend to use. @@ -165,7 +192,7 @@ * * @see CPDF_Adapter::PAPER_SIZES for valid sizes ('letter', 'legal', 'A4', etc.) */ - 'default_paper_size' => $dompdfPaperSizeMap[env('EXPORT_PAGE_SIZE', 'a4')] ?? 'a4', + 'default_paper_size' => $dompdfPaperSizeMap[$exportPageSize] ?? 'a4', /** * The default paper orientation. @@ -268,15 +295,6 @@ */ 'font_height_ratio' => 1.1, - /** - * Enable CSS float. - * - * Allows people to disabled CSS float support - * - * @var bool - */ - 'enable_css_float' => true, - /** * Use the HTML5 Lib parser. * @@ -286,5 +304,4 @@ */ 'enable_html5_parser' => true, ], - ]; diff --git a/app/Config/filesystems.php b/app/Config/filesystems.php index e6ae0fed364..ab73fec2968 100644 --- a/app/Config/filesystems.php +++ b/app/Config/filesystems.php @@ -32,20 +32,22 @@ 'local' => [ 'driver' => 'local', 'root' => public_path(), - 'visibility' => 'public', + 'serve' => false, 'throw' => true, + 'directory_visibility' => 'public', ], 'local_secure_attachments' => [ 'driver' => 'local', 'root' => storage_path('uploads/files/'), + 'serve' => false, 'throw' => true, ], 'local_secure_images' => [ 'driver' => 'local', 'root' => storage_path('uploads/images/'), - 'visibility' => 'public', + 'serve' => false, 'throw' => true, ], @@ -58,6 +60,7 @@ 'endpoint' => env('STORAGE_S3_ENDPOINT', null), 'use_path_style_endpoint' => env('STORAGE_S3_ENDPOINT', null) !== null, 'throw' => true, + 'stream_reads' => false, ], ], diff --git a/app/Config/hashing.php b/app/Config/hashing.php index 585ee094ccb..91d0db16b9e 100644 --- a/app/Config/hashing.php +++ b/app/Config/hashing.php @@ -21,7 +21,8 @@ // passwords are hashed using the Bcrypt algorithm. This will allow you // to control the amount of time it takes to hash the given password. 'bcrypt' => [ - 'rounds' => env('BCRYPT_ROUNDS', 10), + 'rounds' => env('BCRYPT_ROUNDS', 12), + 'verify' => true, ], // Argon Options diff --git a/app/Config/logging.php b/app/Config/logging.php index d49cd727d12..f5cbd5ffc01 100644 --- a/app/Config/logging.php +++ b/app/Config/logging.php @@ -4,6 +4,7 @@ use Monolog\Handler\ErrorLogHandler; use Monolog\Handler\NullHandler; use Monolog\Handler\StreamHandler; +use Monolog\Processor\PsrLogMessageProcessor; /** * Logging configuration options. @@ -49,6 +50,7 @@ 'path' => storage_path('logs/laravel.log'), 'level' => 'debug', 'days' => 14, + 'replace_placeholders' => true, ], 'daily' => [ @@ -56,6 +58,7 @@ 'path' => storage_path('logs/laravel.log'), 'level' => 'debug', 'days' => 7, + 'replace_placeholders' => true, ], 'stderr' => [ @@ -65,16 +68,20 @@ 'with' => [ 'stream' => 'php://stderr', ], + 'processors' => [PsrLogMessageProcessor::class], ], 'syslog' => [ 'driver' => 'syslog', 'level' => 'debug', + 'facility' => LOG_USER, + 'replace_placeholders' => true, ], 'errorlog' => [ 'driver' => 'errorlog', 'level' => 'debug', + 'replace_placeholders' => true, ], // Custom errorlog implementation that logs out a plain, @@ -88,6 +95,7 @@ 'formatter_with' => [ 'format' => '%message%', ], + 'replace_placeholders' => true, ], 'null' => [ diff --git a/app/Config/mail.php b/app/Config/mail.php index 2906d769afd..7256ce8848e 100644 --- a/app/Config/mail.php +++ b/app/Config/mail.php @@ -11,6 +11,7 @@ // Configured mail encryption method. // STARTTLS should still be attempted, but tls/ssl forces TLS usage. $mailEncryption = env('MAIL_ENCRYPTION', null); +$mailPort = intval(env('MAIL_PORT', 587)); return [ @@ -33,13 +34,13 @@ 'transport' => 'smtp', 'scheme' => null, 'host' => env('MAIL_HOST', 'smtp.mailgun.org'), - 'port' => env('MAIL_PORT', 587), + 'port' => $mailPort, 'username' => env('MAIL_USERNAME'), 'password' => env('MAIL_PASSWORD'), 'verify_peer' => env('MAIL_VERIFY_SSL', true), 'timeout' => null, - 'local_domain' => env('MAIL_EHLO_DOMAIN'), - 'tls_required' => ($mailEncryption === 'tls' || $mailEncryption === 'ssl'), + 'local_domain' => null, + 'require_tls' => ($mailEncryption === 'tls' || $mailEncryption === 'ssl' || $mailPort === 465), ], 'sendmail' => [ @@ -64,12 +65,4 @@ ], ], ], - - // Email markdown configuration - 'markdown' => [ - 'theme' => 'default', - 'paths' => [ - resource_path('views/vendor/mail'), - ], - ], ]; diff --git a/app/Config/oidc.php b/app/Config/oidc.php index 7f8f40d4190..16bec873c9c 100644 --- a/app/Config/oidc.php +++ b/app/Config/oidc.php @@ -35,6 +35,7 @@ // OAuth2 endpoints. 'authorization_endpoint' => env('OIDC_AUTH_ENDPOINT', null), 'token_endpoint' => env('OIDC_TOKEN_ENDPOINT', null), + 'userinfo_endpoint' => env('OIDC_USERINFO_ENDPOINT', null), // OIDC RP-Initiated Logout endpoint URL. // A false value force-disables RP-Initiated Logout. @@ -46,6 +47,12 @@ // Multiple values can be provided comma seperated. 'additional_scopes' => env('OIDC_ADDITIONAL_SCOPES', null), + // Enable fetching of the user's avatar from the 'picture' claim on login. + // Will only be fetched if the user doesn't already have an avatar image assigned. + // This can be a security risk due to performing server-side fetching (with up to 3 redirects) of + // data from external URLs. Only enable if you trust the OIDC auth provider to provide safe URLs for user images. + 'fetch_avatar' => env('OIDC_FETCH_AVATAR', false), + // Group sync options // Enable syncing, upon login, of OIDC groups to BookStack roles 'user_to_groups' => env('OIDC_USER_TO_GROUPS', false), diff --git a/app/Config/queue.php b/app/Config/queue.php index a14799f354d..08f3a5baab5 100644 --- a/app/Config/queue.php +++ b/app/Config/queue.php @@ -23,6 +23,7 @@ 'database' => [ 'driver' => 'database', + 'connection' => null, 'table' => 'jobs', 'queue' => 'default', 'retry_after' => 90, @@ -40,6 +41,12 @@ ], + // Job batching + 'batching' => [ + 'database' => 'mysql', + 'table' => 'job_batches', + ], + // Failed queue job logging 'failed' => [ 'driver' => 'database-uuids', diff --git a/app/Config/services.php b/app/Config/services.php index a035f105695..d7345823150 100644 --- a/app/Config/services.php +++ b/app/Config/services.php @@ -123,7 +123,7 @@ 'dn' => env('LDAP_DN', false), 'pass' => env('LDAP_PASS', false), 'base_dn' => env('LDAP_BASE_DN', false), - 'user_filter' => env('LDAP_USER_FILTER', '(&(uid=${user}))'), + 'user_filter' => env('LDAP_USER_FILTER', '(&(uid={user}))'), 'version' => env('LDAP_VERSION', false), 'id_attribute' => env('LDAP_ID_ATTRIBUTE', 'uid'), 'email_attribute' => env('LDAP_EMAIL_ATTRIBUTE', 'mail'), @@ -133,6 +133,7 @@ 'group_attribute' => env('LDAP_GROUP_ATTRIBUTE', 'memberOf'), 'remove_from_groups' => env('LDAP_REMOVE_FROM_GROUPS', false), 'tls_insecure' => env('LDAP_TLS_INSECURE', false), + 'tls_ca_cert' => env('LDAP_TLS_CA_CERT', false), 'start_tls' => env('LDAP_START_TLS', false), 'thumbnail_attribute' => env('LDAP_THUMBNAIL_ATTRIBUTE', null), ], diff --git a/app/Config/session.php b/app/Config/session.php index a00d7580712..f2ec2509fc8 100644 --- a/app/Config/session.php +++ b/app/Config/session.php @@ -85,4 +85,11 @@ // do not enable this as other CSRF protection services are in place. // Options: lax, strict, none 'same_site' => 'lax', + + + // Partitioned Cookies + // Setting this value to true will tie the cookie to the top-level site for + // a cross-site context. Partitioned cookies are accepted by the browser + // when flagged "secure" and the Same-Site attribute is set to "none". + 'partitioned' => false, ]; diff --git a/app/Config/snappy.php b/app/Config/snappy.php deleted file mode 100644 index a87ce805f92..00000000000 --- a/app/Config/snappy.php +++ /dev/null @@ -1,34 +0,0 @@ - 'A4', - 'letter' => 'Letter', -]; - -return [ - 'pdf' => [ - 'enabled' => true, - 'binary' => file_exists(base_path('wkhtmltopdf')) ? base_path('wkhtmltopdf') : env('WKHTMLTOPDF', false), - 'timeout' => false, - 'options' => [ - 'outline' => true, - 'page-size' => $snappyPaperSizeMap[env('EXPORT_PAGE_SIZE', 'a4')] ?? 'A4', - ], - 'env' => [], - ], - 'image' => [ - 'enabled' => false, - 'binary' => '/usr/local/bin/wkhtmltoimage', - 'timeout' => false, - 'options' => [], - 'env' => [], - ], -]; diff --git a/app/Console/Commands/AssignSortRuleCommand.php b/app/Console/Commands/AssignSortRuleCommand.php new file mode 100644 index 00000000000..c438d078326 --- /dev/null +++ b/app/Console/Commands/AssignSortRuleCommand.php @@ -0,0 +1,99 @@ +argument('sort-rule')) ?? 0; + if ($sortRuleId === 0) { + return $this->listSortRules(); + } + + $rule = SortRule::query()->find($sortRuleId); + if ($this->option('all-books')) { + $query = Book::query(); + } else if ($this->option('books-without-sort')) { + $query = Book::query()->whereNull('sort_rule_id'); + } else if ($this->option('books-with-sort')) { + $sortId = intval($this->option('books-with-sort')) ?: 0; + if (!$sortId) { + $this->error("Provided --books-with-sort option value is invalid"); + return 1; + } + $query = Book::query()->where('sort_rule_id', $sortId); + } else { + $this->error("No option provided to specify target. Run with the -h option to see all available options."); + return 1; + } + + if (!$rule) { + $this->error("Sort rule of provided id {$sortRuleId} not found!"); + return 1; + } + + $count = $query->clone()->count(); + $this->warn("This will apply sort rule [{$rule->id}: {$rule->name}] to {$count} book(s) and run the sort on each."); + $confirmed = $this->confirm("Are you sure you want to continue?"); + + if (!$confirmed) { + return 1; + } + + $processed = 0; + $query->chunkById(10, function ($books) use ($rule, $sorter, $count, &$processed) { + $max = min($count, ($processed + 10)); + $this->info("Applying to {$processed}-{$max} of {$count} books"); + foreach ($books as $book) { + $book->sort_rule_id = $rule->id; + $book->save(); + $sorter->runBookAutoSort($book); + } + $processed = $max; + }); + + $this->info("Sort applied to {$processed} book(s)!"); + + return 0; + } + + protected function listSortRules(): int + { + + $rules = SortRule::query()->orderBy('id', 'asc')->get(); + $this->error("Sort rule ID required!"); + $this->warn("\nAvailable sort rules:"); + foreach ($rules as $rule) { + $this->info("{$rule->id}: {$rule->name}"); + } + + return 1; + } +} diff --git a/app/Console/Commands/ClearActivityCommand.php b/app/Console/Commands/ClearActivityCommand.php index 54085c12be3..6ec2e1a2aaa 100644 --- a/app/Console/Commands/ClearActivityCommand.php +++ b/app/Console/Commands/ClearActivityCommand.php @@ -19,7 +19,7 @@ class ClearActivityCommand extends Command * * @var string */ - protected $description = 'Clear user activity from the system'; + protected $description = 'Clear user (audit-log) activity from the system'; /** * Execute the console command. diff --git a/app/Console/Commands/CopyShelfPermissionsCommand.php b/app/Console/Commands/CopyShelfPermissionsCommand.php index fc11484bd4d..c5e2d504e75 100644 --- a/app/Console/Commands/CopyShelfPermissionsCommand.php +++ b/app/Console/Commands/CopyShelfPermissionsCommand.php @@ -2,7 +2,7 @@ namespace BookStack\Console\Commands; -use BookStack\Entities\Models\Bookshelf; +use BookStack\Entities\Queries\BookshelfQueries; use BookStack\Entities\Tools\PermissionsUpdater; use Illuminate\Console\Command; @@ -28,7 +28,7 @@ class CopyShelfPermissionsCommand extends Command /** * Execute the console command. */ - public function handle(PermissionsUpdater $permissionsUpdater): int + public function handle(PermissionsUpdater $permissionsUpdater, BookshelfQueries $queries): int { $shelfSlug = $this->option('slug'); $cascadeAll = $this->option('all'); @@ -51,11 +51,11 @@ public function handle(PermissionsUpdater $permissionsUpdater): int return 0; } - $shelves = Bookshelf::query()->get(['id']); + $shelves = $queries->start()->get(['id']); } if ($shelfSlug) { - $shelves = Bookshelf::query()->where('slug', '=', $shelfSlug)->get(['id']); + $shelves = $queries->start()->where('slug', '=', $shelfSlug)->get(['id']); if ($shelves->count() === 0) { $this->info('No shelves found with the given slug.'); } diff --git a/app/Console/Commands/CreateAdminCommand.php b/app/Console/Commands/CreateAdminCommand.php index 82b6e50aa52..bf72553f72a 100644 --- a/app/Console/Commands/CreateAdminCommand.php +++ b/app/Console/Commands/CreateAdminCommand.php @@ -8,7 +8,6 @@ use Illuminate\Support\Facades\Validator; use Illuminate\Support\Str; use Illuminate\Validation\Rules\Password; -use Illuminate\Validation\Rules\Unique; class CreateAdminCommand extends Command { @@ -21,7 +20,9 @@ class CreateAdminCommand extends Command {--email= : The email address for the new admin user} {--name= : The name of the new admin user} {--password= : The password to assign to the new admin user} - {--external-auth-id= : The external authentication system id for the new admin user (SAML2/LDAP/OIDC)}'; + {--external-auth-id= : The external authentication system id for the new admin user (SAML2/LDAP/OIDC)} + {--generate-password : Generate a random password for the new admin user} + {--initial : Indicate if this should set/update the details of the initial admin user}'; /** * The console command description. @@ -35,26 +36,12 @@ class CreateAdminCommand extends Command */ public function handle(UserRepo $userRepo): int { - $details = $this->snakeCaseOptions(); - - if (empty($details['email'])) { - $details['email'] = $this->ask('Please specify an email address for the new admin user'); - } - - if (empty($details['name'])) { - $details['name'] = $this->ask('Please specify a name for the new admin user'); - } - - if (empty($details['password'])) { - if (empty($details['external_auth_id'])) { - $details['password'] = $this->ask('Please specify a password for the new admin user (8 characters min)'); - } else { - $details['password'] = Str::random(32); - } - } + $initialAdminOnly = $this->option('initial'); + $shouldGeneratePassword = $this->option('generate-password'); + $details = $this->gatherDetails($shouldGeneratePassword, $initialAdminOnly); $validator = Validator::make($details, [ - 'email' => ['required', 'email', 'min:5', new Unique('users', 'email')], + 'email' => ['required', 'email', 'min:5'], 'name' => ['required', 'min:2'], 'password' => ['required_without:external_auth_id', Password::default()], 'external_auth_id' => ['required_without:password'], @@ -68,16 +55,101 @@ public function handle(UserRepo $userRepo): int return 1; } + $adminRole = Role::getSystemRole('admin'); + + if ($initialAdminOnly) { + $handled = $this->handleInitialAdminIfExists($userRepo, $details, $shouldGeneratePassword, $adminRole); + if ($handled !== null) { + return $handled; + } + } + + $emailUsed = $userRepo->getByEmail($details['email']) !== null; + if ($emailUsed) { + $this->error("Could not create admin account."); + $this->error("An account with the email address \"{$details['email']}\" already exists."); + return 1; + } + $user = $userRepo->createWithoutActivity($validator->validated()); - $user->attachRole(Role::getSystemRole('admin')); + $user->attachRole($adminRole); $user->email_confirmed = true; $user->save(); - $this->info("Admin account with email \"{$user->email}\" successfully created!"); + if ($shouldGeneratePassword) { + $this->line($details['password']); + } else { + $this->info("Admin account with email \"{$user->email}\" successfully created!"); + } return 0; } + /** + * Handle updates to the original admin account if it exists. + * Returns an int return status if handled, otherwise returns null if not handled (new user to be created). + */ + protected function handleInitialAdminIfExists(UserRepo $userRepo, array $data, bool $generatePassword, Role $adminRole): int|null + { + $defaultAdmin = $userRepo->getByEmail('admin@admin.com'); + if ($defaultAdmin && $defaultAdmin->hasSystemRole('admin')) { + if ($defaultAdmin->email !== $data['email'] && $userRepo->getByEmail($data['email']) !== null) { + $this->error("Could not create admin account."); + $this->error("An account with the email address \"{$data['email']}\" already exists."); + return 1; + } + + $userRepo->updateWithoutActivity($defaultAdmin, $data, true); + if ($generatePassword) { + $this->line($data['password']); + } else { + $this->info("The default admin user has been updated with the provided details!"); + } + + return 0; + } else if ($adminRole->users()->count() > 0) { + $this->warn('Non-default admin user already exists. Skipping creation of new admin user.'); + return 2; + } + + return null; + } + + protected function gatherDetails(bool $generatePassword, bool $initialAdmin): array + { + $details = $this->snakeCaseOptions(); + + if (empty($details['email'])) { + if ($initialAdmin) { + $details['email'] = 'admin@example.com'; + } else { + $details['email'] = $this->ask('Please specify an email address for the new admin user'); + } + } + + if (empty($details['name'])) { + if ($initialAdmin) { + $details['name'] = 'Admin'; + } else { + $details['name'] = $this->ask('Please specify a name for the new admin user'); + } + } + + if (empty($details['password'])) { + if (empty($details['external_auth_id'])) { + if ($generatePassword) { + $details['password'] = Str::random(32); + } else { + $details['password'] = $this->ask('Please specify a password for the new admin user (8 characters min)'); + } + } else { + $details['password'] = Str::random(32); + } + } + + return $details; + } + protected function snakeCaseOptions(): array { $returnOpts = []; diff --git a/app/Console/Commands/RegenerateCommentContentCommand.php b/app/Console/Commands/RegenerateCommentContentCommand.php deleted file mode 100644 index f7ec426209f..00000000000 --- a/app/Console/Commands/RegenerateCommentContentCommand.php +++ /dev/null @@ -1,49 +0,0 @@ -option('database') !== null) { - DB::setDefaultConnection($this->option('database')); - } - - Comment::query()->chunk(100, function ($comments) use ($commentRepo) { - foreach ($comments as $comment) { - $comment->html = $commentRepo->commentToHtml($comment->text); - $comment->save(); - } - }); - - DB::setDefaultConnection($connection); - $this->comment('Comment HTML content has been regenerated'); - - return 0; - } -} diff --git a/app/Console/Commands/UpdateUrlCommand.php b/app/Console/Commands/UpdateUrlCommand.php index 0c95b0a3c8a..71f0b92fe41 100644 --- a/app/Console/Commands/UpdateUrlCommand.php +++ b/app/Console/Commands/UpdateUrlCommand.php @@ -49,9 +49,10 @@ public function handle(Connection $db): int 'chapters' => ['description_html'], 'books' => ['description_html'], 'bookshelves' => ['description_html'], + 'page_revisions' => ['html', 'text', 'markdown'], 'images' => ['url'], 'settings' => ['value'], - 'comments' => ['html', 'text'], + 'comments' => ['html'], ]; foreach ($columnsToUpdateByTable as $table => $columns) { @@ -77,6 +78,12 @@ public function handle(Connection $db): int $this->info('URL update procedure complete.'); $this->info('============================================================================'); $this->info('Be sure to run "php artisan cache:clear" to clear any old URLs in the cache.'); + + if (!str_starts_with($newUrl, url('/'))) { + $this->warn('You still need to update your APP_URL env value. This is currently set to:'); + $this->warn(url('/')); + } + $this->info('============================================================================'); return 0; diff --git a/app/Entities/Controllers/BookApiController.php b/app/Entities/Controllers/BookApiController.php index aa21aea472a..5baea163fd6 100644 --- a/app/Entities/Controllers/BookApiController.php +++ b/app/Entities/Controllers/BookApiController.php @@ -6,16 +6,21 @@ use BookStack\Entities\Models\Book; use BookStack\Entities\Models\Chapter; use BookStack\Entities\Models\Entity; +use BookStack\Entities\Queries\BookQueries; +use BookStack\Entities\Queries\PageQueries; use BookStack\Entities\Repos\BookRepo; use BookStack\Entities\Tools\BookContents; use BookStack\Http\ApiController; +use BookStack\Permissions\Permission; use Illuminate\Http\Request; use Illuminate\Validation\ValidationException; class BookApiController extends ApiController { public function __construct( - protected BookRepo $bookRepo + protected BookRepo $bookRepo, + protected BookQueries $queries, + protected PageQueries $pageQueries, ) { } @@ -24,7 +29,10 @@ public function __construct( */ public function list() { - $books = Book::visible(); + $books = $this->queries + ->visibleForList() + ->with(['cover:id,name,url']) + ->addSelect(['created_by', 'updated_by']); return $this->apiListingResponse($books, [ 'id', 'name', 'slug', 'description', 'created_at', 'updated_at', 'created_by', 'updated_by', 'owned_by', @@ -40,7 +48,7 @@ public function list() */ public function create(Request $request) { - $this->checkPermission('book-create-all'); + $this->checkPermission(Permission::BookCreateAll); $requestData = $this->validate($request, $this->rules()['create']); $book = $this->bookRepo->create($requestData); @@ -56,7 +64,7 @@ public function create(Request $request) */ public function read(string $id) { - $book = Book::visible()->findOrFail($id); + $book = $this->queries->findVisibleByIdOrFail(intval($id)); $book = $this->forJsonDisplay($book); $book->load(['createdBy', 'updatedBy', 'ownedBy']); @@ -65,7 +73,8 @@ public function read(string $id) ->withType() ->withField('pages', function (Entity $entity) { if ($entity instanceof Chapter) { - return (new ApiEntityListFormatter($entity->pages->all()))->format(); + $pages = $this->pageQueries->visibleForChapterList($entity->id)->get()->all(); + return (new ApiEntityListFormatter($pages))->format(); } return null; })->format(); @@ -83,8 +92,8 @@ public function read(string $id) */ public function update(Request $request, string $id) { - $book = Book::visible()->findOrFail($id); - $this->checkOwnablePermission('book-update', $book); + $book = $this->queries->findVisibleByIdOrFail(intval($id)); + $this->checkOwnablePermission(Permission::BookUpdate, $book); $requestData = $this->validate($request, $this->rules()['update']); $book = $this->bookRepo->update($book, $requestData); @@ -100,8 +109,8 @@ public function update(Request $request, string $id) */ public function delete(string $id) { - $book = Book::visible()->findOrFail($id); - $this->checkOwnablePermission('book-delete', $book); + $book = $this->queries->findVisibleByIdOrFail(intval($id)); + $this->checkOwnablePermission(Permission::BookDelete, $book); $this->bookRepo->destroy($book); diff --git a/app/Entities/Controllers/BookController.php b/app/Entities/Controllers/BookController.php index 412feca2fe5..cbf7ffb7984 100644 --- a/app/Entities/Controllers/BookController.php +++ b/app/Entities/Controllers/BookController.php @@ -6,7 +6,8 @@ use BookStack\Activity\ActivityType; use BookStack\Activity\Models\View; use BookStack\Activity\Tools\UserEntityWatchOptions; -use BookStack\Entities\Models\Bookshelf; +use BookStack\Entities\Queries\BookQueries; +use BookStack\Entities\Queries\BookshelfQueries; use BookStack\Entities\Repos\BookRepo; use BookStack\Entities\Tools\BookContents; use BookStack\Entities\Tools\Cloner; @@ -16,7 +17,9 @@ use BookStack\Exceptions\NotFoundException; use BookStack\Facades\Activity; use BookStack\Http\Controller; +use BookStack\Permissions\Permission; use BookStack\References\ReferenceFetcher; +use BookStack\Util\DatabaseTransaction; use BookStack\Util\SimpleListOptions; use Illuminate\Http\Request; use Illuminate\Validation\ValidationException; @@ -27,7 +30,9 @@ class BookController extends Controller public function __construct( protected ShelfContext $shelfContext, protected BookRepo $bookRepo, - protected ReferenceFetcher $referenceFetcher + protected BookQueries $queries, + protected BookshelfQueries $shelfQueries, + protected ReferenceFetcher $referenceFetcher, ) { } @@ -43,10 +48,12 @@ public function index(Request $request) 'updated_at' => trans('common.sort_updated_at'), ]); - $books = $this->bookRepo->getAllPaginated(18, $listOptions->getSort(), $listOptions->getOrder()); - $recents = $this->isSignedIn() ? $this->bookRepo->getRecentlyViewed(4) : false; - $popular = $this->bookRepo->getPopular(4); - $new = $this->bookRepo->getRecentlyCreated(4); + $books = $this->queries->visibleForListWithCover() + ->orderBy($listOptions->getSort(), $listOptions->getOrder()) + ->paginate(18); + $recents = $this->isSignedIn() ? $this->queries->recentlyViewedForCurrentUser()->take(4)->get() : false; + $popular = $this->queries->popularForList()->take(4)->get(); + $new = $this->queries->visibleForList()->orderBy('created_at', 'desc')->take(4)->get(); $this->shelfContext->clearShelfContext(); @@ -65,14 +72,14 @@ public function index(Request $request) /** * Show the form for creating a new book. */ - public function create(string $shelfSlug = null) + public function create(?string $shelfSlug = null) { - $this->checkPermission('book-create-all'); + $this->checkPermission(Permission::BookCreateAll); $bookshelf = null; if ($shelfSlug !== null) { - $bookshelf = Bookshelf::visible()->where('slug', '=', $shelfSlug)->firstOrFail(); - $this->checkOwnablePermission('bookshelf-update', $bookshelf); + $bookshelf = $this->shelfQueries->findVisibleBySlugOrFail($shelfSlug); + $this->checkOwnablePermission(Permission::BookshelfUpdate, $bookshelf); } $this->setPageTitle(trans('entities.books_create')); @@ -88,9 +95,9 @@ public function create(string $shelfSlug = null) * @throws ImageUploadException * @throws ValidationException */ - public function store(Request $request, string $shelfSlug = null) + public function store(Request $request, ?string $shelfSlug = null) { - $this->checkPermission('book-create-all'); + $this->checkPermission(Permission::BookCreateAll); $validated = $this->validate($request, [ 'name' => ['required', 'string', 'max:255'], 'description_html' => ['string', 'max:2000'], @@ -101,8 +108,8 @@ public function store(Request $request, string $shelfSlug = null) $bookshelf = null; if ($shelfSlug !== null) { - $bookshelf = Bookshelf::visible()->where('slug', '=', $shelfSlug)->firstOrFail(); - $this->checkOwnablePermission('bookshelf-update', $bookshelf); + $bookshelf = $this->shelfQueries->findVisibleBySlugOrFail($shelfSlug); + $this->checkOwnablePermission(Permission::BookshelfUpdate, $bookshelf); } $book = $this->bookRepo->create($validated); @@ -120,7 +127,7 @@ public function store(Request $request, string $shelfSlug = null) */ public function show(Request $request, ActivityQueries $activities, string $slug) { - $book = $this->bookRepo->getBySlug($slug); + $book = $this->queries->findVisibleBySlugOrFail($slug); $bookChildren = (new BookContents($book))->getTree(true); $bookParentShelves = $book->shelves()->scopes('visible')->get(); @@ -147,8 +154,8 @@ public function show(Request $request, ActivityQueries $activities, string $slug */ public function edit(string $slug) { - $book = $this->bookRepo->getBySlug($slug); - $this->checkOwnablePermission('book-update', $book); + $book = $this->queries->findVisibleBySlugOrFail($slug); + $this->checkOwnablePermission(Permission::BookUpdate, $book); $this->setPageTitle(trans('entities.books_edit_named', ['bookName' => $book->getShortName()])); return view('books.edit', ['book' => $book, 'current' => $book]); @@ -163,8 +170,8 @@ public function edit(string $slug) */ public function update(Request $request, string $slug) { - $book = $this->bookRepo->getBySlug($slug); - $this->checkOwnablePermission('book-update', $book); + $book = $this->queries->findVisibleBySlugOrFail($slug); + $this->checkOwnablePermission(Permission::BookUpdate, $book); $validated = $this->validate($request, [ 'name' => ['required', 'string', 'max:255'], @@ -190,8 +197,8 @@ public function update(Request $request, string $slug) */ public function showDelete(string $bookSlug) { - $book = $this->bookRepo->getBySlug($bookSlug); - $this->checkOwnablePermission('book-delete', $book); + $book = $this->queries->findVisibleBySlugOrFail($bookSlug); + $this->checkOwnablePermission(Permission::BookDelete, $book); $this->setPageTitle(trans('entities.books_delete_named', ['bookName' => $book->getShortName()])); return view('books.delete', ['book' => $book, 'current' => $book]); @@ -204,8 +211,8 @@ public function showDelete(string $bookSlug) */ public function destroy(string $bookSlug) { - $book = $this->bookRepo->getBySlug($bookSlug); - $this->checkOwnablePermission('book-delete', $book); + $book = $this->queries->findVisibleBySlugOrFail($bookSlug); + $this->checkOwnablePermission(Permission::BookDelete, $book); $this->bookRepo->destroy($book); @@ -219,8 +226,8 @@ public function destroy(string $bookSlug) */ public function showCopy(string $bookSlug) { - $book = $this->bookRepo->getBySlug($bookSlug); - $this->checkOwnablePermission('book-view', $book); + $book = $this->queries->findVisibleBySlugOrFail($bookSlug); + $this->checkOwnablePermission(Permission::BookView, $book); session()->flashInput(['name' => $book->name]); @@ -236,9 +243,9 @@ public function showCopy(string $bookSlug) */ public function copy(Request $request, Cloner $cloner, string $bookSlug) { - $book = $this->bookRepo->getBySlug($bookSlug); - $this->checkOwnablePermission('book-view', $book); - $this->checkPermission('book-create-all'); + $book = $this->queries->findVisibleBySlugOrFail($bookSlug); + $this->checkOwnablePermission(Permission::BookView, $book); + $this->checkPermission(Permission::BookCreateAll); $newName = $request->get('name') ?: $book->name; $bookCopy = $cloner->cloneBook($book, $newName); @@ -252,13 +259,15 @@ public function copy(Request $request, Cloner $cloner, string $bookSlug) */ public function convertToShelf(HierarchyTransformer $transformer, string $bookSlug) { - $book = $this->bookRepo->getBySlug($bookSlug); - $this->checkOwnablePermission('book-update', $book); - $this->checkOwnablePermission('book-delete', $book); - $this->checkPermission('bookshelf-create-all'); - $this->checkPermission('book-create-all'); - - $shelf = $transformer->transformBookToShelf($book); + $book = $this->queries->findVisibleBySlugOrFail($bookSlug); + $this->checkOwnablePermission(Permission::BookUpdate, $book); + $this->checkOwnablePermission(Permission::BookDelete, $book); + $this->checkPermission(Permission::BookshelfCreateAll); + $this->checkPermission(Permission::BookCreateAll); + + $shelf = (new DatabaseTransaction(function () use ($book, $transformer) { + return $transformer->transformBookToShelf($book); + }))->run(); return redirect($shelf->getUrl()); } diff --git a/app/Entities/Controllers/BookSortController.php b/app/Entities/Controllers/BookSortController.php deleted file mode 100644 index f2310e2055d..00000000000 --- a/app/Entities/Controllers/BookSortController.php +++ /dev/null @@ -1,73 +0,0 @@ -bookRepo = $bookRepo; - } - - /** - * Shows the view which allows pages to be re-ordered and sorted. - */ - public function show(string $bookSlug) - { - $book = $this->bookRepo->getBySlug($bookSlug); - $this->checkOwnablePermission('book-update', $book); - - $bookChildren = (new BookContents($book))->getTree(false); - - $this->setPageTitle(trans('entities.books_sort_named', ['bookName' => $book->getShortName()])); - - return view('books.sort', ['book' => $book, 'current' => $book, 'bookChildren' => $bookChildren]); - } - - /** - * Shows the sort box for a single book. - * Used via AJAX when loading in extra books to a sort. - */ - public function showItem(string $bookSlug) - { - $book = $this->bookRepo->getBySlug($bookSlug); - $bookChildren = (new BookContents($book))->getTree(); - - return view('books.parts.sort-box', ['book' => $book, 'bookChildren' => $bookChildren]); - } - - /** - * Sorts a book using a given mapping array. - */ - public function update(Request $request, string $bookSlug) - { - $book = $this->bookRepo->getBySlug($bookSlug); - $this->checkOwnablePermission('book-update', $book); - - // Return if no map sent - if (!$request->filled('sort-tree')) { - return redirect($book->getUrl()); - } - - $sortMap = BookSortMap::fromJson($request->get('sort-tree')); - $bookContents = new BookContents($book); - $booksInvolved = $bookContents->sortUsingMap($sortMap); - - // Rebuild permissions and add activity for involved books. - foreach ($booksInvolved as $bookInvolved) { - Activity::add(ActivityType::BOOK_SORT, $bookInvolved); - } - - return redirect($book->getUrl()); - } -} diff --git a/app/Entities/Controllers/BookshelfApiController.php b/app/Entities/Controllers/BookshelfApiController.php index a12dc90ac62..f4bd394a9e7 100644 --- a/app/Entities/Controllers/BookshelfApiController.php +++ b/app/Entities/Controllers/BookshelfApiController.php @@ -3,8 +3,10 @@ namespace BookStack\Entities\Controllers; use BookStack\Entities\Models\Bookshelf; +use BookStack\Entities\Queries\BookshelfQueries; use BookStack\Entities\Repos\BookshelfRepo; use BookStack\Http\ApiController; +use BookStack\Permissions\Permission; use Exception; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Http\Request; @@ -13,7 +15,8 @@ class BookshelfApiController extends ApiController { public function __construct( - protected BookshelfRepo $bookshelfRepo + protected BookshelfRepo $bookshelfRepo, + protected BookshelfQueries $queries, ) { } @@ -22,7 +25,10 @@ public function __construct( */ public function list() { - $shelves = Bookshelf::visible(); + $shelves = $this->queries + ->visibleForList() + ->with(['cover:id,name,url']) + ->addSelect(['created_by', 'updated_by']); return $this->apiListingResponse($shelves, [ 'id', 'name', 'slug', 'description', 'created_at', 'updated_at', 'created_by', 'updated_by', 'owned_by', @@ -40,7 +46,7 @@ public function list() */ public function create(Request $request) { - $this->checkPermission('bookshelf-create-all'); + $this->checkPermission(Permission::BookshelfCreateAll); $requestData = $this->validate($request, $this->rules()['create']); $bookIds = $request->get('books', []); @@ -54,7 +60,7 @@ public function create(Request $request) */ public function read(string $id) { - $shelf = Bookshelf::visible()->findOrFail($id); + $shelf = $this->queries->findVisibleByIdOrFail(intval($id)); $shelf = $this->forJsonDisplay($shelf); $shelf->load([ 'createdBy', 'updatedBy', 'ownedBy', @@ -78,8 +84,8 @@ public function read(string $id) */ public function update(Request $request, string $id) { - $shelf = Bookshelf::visible()->findOrFail($id); - $this->checkOwnablePermission('bookshelf-update', $shelf); + $shelf = $this->queries->findVisibleByIdOrFail(intval($id)); + $this->checkOwnablePermission(Permission::BookshelfUpdate, $shelf); $requestData = $this->validate($request, $this->rules()['update']); $bookIds = $request->get('books', null); @@ -97,8 +103,8 @@ public function update(Request $request, string $id) */ public function delete(string $id) { - $shelf = Bookshelf::visible()->findOrFail($id); - $this->checkOwnablePermission('bookshelf-delete', $shelf); + $shelf = $this->queries->findVisibleByIdOrFail(intval($id)); + $this->checkOwnablePermission(Permission::BookshelfDelete, $shelf); $this->bookshelfRepo->destroy($shelf); diff --git a/app/Entities/Controllers/BookshelfController.php b/app/Entities/Controllers/BookshelfController.php index 2f5461cdb0b..f47742ffaa6 100644 --- a/app/Entities/Controllers/BookshelfController.php +++ b/app/Entities/Controllers/BookshelfController.php @@ -4,12 +4,14 @@ use BookStack\Activity\ActivityQueries; use BookStack\Activity\Models\View; -use BookStack\Entities\Models\Book; +use BookStack\Entities\Queries\BookQueries; +use BookStack\Entities\Queries\BookshelfQueries; use BookStack\Entities\Repos\BookshelfRepo; use BookStack\Entities\Tools\ShelfContext; use BookStack\Exceptions\ImageUploadException; use BookStack\Exceptions\NotFoundException; use BookStack\Http\Controller; +use BookStack\Permissions\Permission; use BookStack\References\ReferenceFetcher; use BookStack\Util\SimpleListOptions; use Exception; @@ -20,8 +22,10 @@ class BookshelfController extends Controller { public function __construct( protected BookshelfRepo $shelfRepo, + protected BookshelfQueries $queries, + protected BookQueries $bookQueries, protected ShelfContext $shelfContext, - protected ReferenceFetcher $referenceFetcher + protected ReferenceFetcher $referenceFetcher, ) { } @@ -37,10 +41,15 @@ public function index(Request $request) 'updated_at' => trans('common.sort_updated_at'), ]); - $shelves = $this->shelfRepo->getAllPaginated(18, $listOptions->getSort(), $listOptions->getOrder()); - $recents = $this->isSignedIn() ? $this->shelfRepo->getRecentlyViewed(4) : false; - $popular = $this->shelfRepo->getPopular(4); - $new = $this->shelfRepo->getRecentlyCreated(4); + $shelves = $this->queries->visibleForListWithCover() + ->orderBy($listOptions->getSort(), $listOptions->getOrder()) + ->paginate(18); + $recents = $this->isSignedIn() ? $this->queries->recentlyViewedForCurrentUser()->get() : false; + $popular = $this->queries->popularForList()->get(); + $new = $this->queries->visibleForList() + ->orderBy('created_at', 'desc') + ->take(4) + ->get(); $this->shelfContext->clearShelfContext(); $this->setPageTitle(trans('entities.shelves')); @@ -60,8 +69,8 @@ public function index(Request $request) */ public function create() { - $this->checkPermission('bookshelf-create-all'); - $books = Book::visible()->orderBy('name')->get(['name', 'id', 'slug', 'created_at', 'updated_at']); + $this->checkPermission(Permission::BookshelfCreateAll); + $books = $this->bookQueries->visibleForList()->orderBy('name')->get(['name', 'id', 'slug', 'created_at', 'updated_at']); $this->setPageTitle(trans('entities.shelves_create')); return view('shelves.create', ['books' => $books]); @@ -75,7 +84,7 @@ public function create() */ public function store(Request $request) { - $this->checkPermission('bookshelf-create-all'); + $this->checkPermission(Permission::BookshelfCreateAll); $validated = $this->validate($request, [ 'name' => ['required', 'string', 'max:255'], 'description_html' => ['string', 'max:2000'], @@ -96,8 +105,8 @@ public function store(Request $request) */ public function show(Request $request, ActivityQueries $activities, string $slug) { - $shelf = $this->shelfRepo->getBySlug($slug); - $this->checkOwnablePermission('bookshelf-view', $shelf); + $shelf = $this->queries->findVisibleBySlugOrFail($slug); + $this->checkOwnablePermission(Permission::BookshelfView, $shelf); $listOptions = SimpleListOptions::fromRequest($request, 'shelf_books')->withSortOptions([ 'default' => trans('common.sort_default'), @@ -134,11 +143,14 @@ public function show(Request $request, ActivityQueries $activities, string $slug */ public function edit(string $slug) { - $shelf = $this->shelfRepo->getBySlug($slug); - $this->checkOwnablePermission('bookshelf-update', $shelf); + $shelf = $this->queries->findVisibleBySlugOrFail($slug); + $this->checkOwnablePermission(Permission::BookshelfUpdate, $shelf); $shelfBookIds = $shelf->books()->get(['id'])->pluck('id'); - $books = Book::visible()->whereNotIn('id', $shelfBookIds)->orderBy('name')->get(['name', 'id', 'slug', 'created_at', 'updated_at']); + $books = $this->bookQueries->visibleForList() + ->whereNotIn('id', $shelfBookIds) + ->orderBy('name') + ->get(['name', 'id', 'slug', 'created_at', 'updated_at']); $this->setPageTitle(trans('entities.shelves_edit_named', ['name' => $shelf->getShortName()])); @@ -157,8 +169,8 @@ public function edit(string $slug) */ public function update(Request $request, string $slug) { - $shelf = $this->shelfRepo->getBySlug($slug); - $this->checkOwnablePermission('bookshelf-update', $shelf); + $shelf = $this->queries->findVisibleBySlugOrFail($slug); + $this->checkOwnablePermission(Permission::BookshelfUpdate, $shelf); $validated = $this->validate($request, [ 'name' => ['required', 'string', 'max:255'], 'description_html' => ['string', 'max:2000'], @@ -183,8 +195,8 @@ public function update(Request $request, string $slug) */ public function showDelete(string $slug) { - $shelf = $this->shelfRepo->getBySlug($slug); - $this->checkOwnablePermission('bookshelf-delete', $shelf); + $shelf = $this->queries->findVisibleBySlugOrFail($slug); + $this->checkOwnablePermission(Permission::BookshelfDelete, $shelf); $this->setPageTitle(trans('entities.shelves_delete_named', ['name' => $shelf->getShortName()])); @@ -198,8 +210,8 @@ public function showDelete(string $slug) */ public function destroy(string $slug) { - $shelf = $this->shelfRepo->getBySlug($slug); - $this->checkOwnablePermission('bookshelf-delete', $shelf); + $shelf = $this->queries->findVisibleBySlugOrFail($slug); + $this->checkOwnablePermission(Permission::BookshelfDelete, $shelf); $this->shelfRepo->destroy($shelf); diff --git a/app/Entities/Controllers/ChapterApiController.php b/app/Entities/Controllers/ChapterApiController.php index 85c81c2485c..80eab7bb8c1 100644 --- a/app/Entities/Controllers/ChapterApiController.php +++ b/app/Entities/Controllers/ChapterApiController.php @@ -4,36 +4,42 @@ use BookStack\Entities\Models\Book; use BookStack\Entities\Models\Chapter; +use BookStack\Entities\Queries\ChapterQueries; +use BookStack\Entities\Queries\EntityQueries; use BookStack\Entities\Repos\ChapterRepo; use BookStack\Exceptions\PermissionsException; use BookStack\Http\ApiController; +use BookStack\Permissions\Permission; use Exception; -use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Http\Request; class ChapterApiController extends ApiController { - protected $rules = [ + protected array $rules = [ 'create' => [ - 'book_id' => ['required', 'integer'], - 'name' => ['required', 'string', 'max:255'], - 'description' => ['string', 'max:1900'], - 'description_html' => ['string', 'max:2000'], - 'tags' => ['array'], - 'priority' => ['integer'], + 'book_id' => ['required', 'integer'], + 'name' => ['required', 'string', 'max:255'], + 'description' => ['string', 'max:1900'], + 'description_html' => ['string', 'max:2000'], + 'tags' => ['array'], + 'priority' => ['integer'], + 'default_template_id' => ['nullable', 'integer'], ], 'update' => [ - 'book_id' => ['integer'], - 'name' => ['string', 'min:1', 'max:255'], - 'description' => ['string', 'max:1900'], - 'description_html' => ['string', 'max:2000'], - 'tags' => ['array'], - 'priority' => ['integer'], + 'book_id' => ['integer'], + 'name' => ['string', 'min:1', 'max:255'], + 'description' => ['string', 'max:1900'], + 'description_html' => ['string', 'max:2000'], + 'tags' => ['array'], + 'priority' => ['integer'], + 'default_template_id' => ['nullable', 'integer'], ], ]; public function __construct( - protected ChapterRepo $chapterRepo + protected ChapterRepo $chapterRepo, + protected ChapterQueries $queries, + protected EntityQueries $entityQueries, ) { } @@ -42,7 +48,8 @@ public function __construct( */ public function list() { - $chapters = Chapter::visible(); + $chapters = $this->queries->visibleForList() + ->addSelect(['created_by', 'updated_by']); return $this->apiListingResponse($chapters, [ 'id', 'book_id', 'name', 'slug', 'description', 'priority', @@ -58,8 +65,8 @@ public function create(Request $request) $requestData = $this->validate($request, $this->rules['create']); $bookId = $request->get('book_id'); - $book = Book::visible()->findOrFail($bookId); - $this->checkOwnablePermission('chapter-create', $book); + $book = $this->entityQueries->books->findVisibleByIdOrFail(intval($bookId)); + $this->checkOwnablePermission(Permission::ChapterCreate, $book); $chapter = $this->chapterRepo->create($requestData, $book); @@ -71,15 +78,17 @@ public function create(Request $request) */ public function read(string $id) { - $chapter = Chapter::visible()->findOrFail($id); + $chapter = $this->queries->findVisibleByIdOrFail(intval($id)); $chapter = $this->forJsonDisplay($chapter); - $chapter->load([ - 'createdBy', 'updatedBy', 'ownedBy', - 'pages' => function (HasMany $query) { - $query->scopes('visible')->get(['id', 'name', 'slug']); - } - ]); + $chapter->load(['createdBy', 'updatedBy', 'ownedBy']); + + // Note: More fields than usual here, for backwards compatibility, + // due to previously accidentally including more fields that desired. + $pages = $this->entityQueries->pages->visibleForChapterList($chapter->id) + ->addSelect(['created_by', 'updated_by', 'revision_count', 'editor']) + ->get(); + $chapter->setRelation('pages', $pages); return response()->json($chapter); } @@ -92,11 +101,11 @@ public function read(string $id) public function update(Request $request, string $id) { $requestData = $this->validate($request, $this->rules()['update']); - $chapter = Chapter::visible()->findOrFail($id); - $this->checkOwnablePermission('chapter-update', $chapter); + $chapter = $this->queries->findVisibleByIdOrFail(intval($id)); + $this->checkOwnablePermission(Permission::ChapterUpdate, $chapter); if ($request->has('book_id') && $chapter->book_id !== intval($requestData['book_id'])) { - $this->checkOwnablePermission('chapter-delete', $chapter); + $this->checkOwnablePermission(Permission::ChapterDelete, $chapter); try { $this->chapterRepo->move($chapter, "book:{$requestData['book_id']}"); @@ -120,8 +129,8 @@ public function update(Request $request, string $id) */ public function delete(string $id) { - $chapter = Chapter::visible()->findOrFail($id); - $this->checkOwnablePermission('chapter-delete', $chapter); + $chapter = $this->queries->findVisibleByIdOrFail(intval($id)); + $this->checkOwnablePermission(Permission::ChapterDelete, $chapter); $this->chapterRepo->destroy($chapter); @@ -136,7 +145,10 @@ protected function forJsonDisplay(Chapter $chapter): Chapter $chapter->load(['tags']); $chapter->makeVisible('description_html'); $chapter->setAttribute('description_html', $chapter->descriptionHtml()); - $chapter->setAttribute('book_slug', $chapter->book()->first()->slug); + + /** @var Book $book */ + $book = $chapter->book()->first(); + $chapter->setAttribute('book_slug', $book->slug); return $chapter; } diff --git a/app/Entities/Controllers/ChapterController.php b/app/Entities/Controllers/ChapterController.php index 28ad35fa4b3..9335e0a7034 100644 --- a/app/Entities/Controllers/ChapterController.php +++ b/app/Entities/Controllers/ChapterController.php @@ -5,6 +5,8 @@ use BookStack\Activity\Models\View; use BookStack\Activity\Tools\UserEntityWatchOptions; use BookStack\Entities\Models\Book; +use BookStack\Entities\Queries\ChapterQueries; +use BookStack\Entities\Queries\EntityQueries; use BookStack\Entities\Repos\ChapterRepo; use BookStack\Entities\Tools\BookContents; use BookStack\Entities\Tools\Cloner; @@ -15,7 +17,9 @@ use BookStack\Exceptions\NotifyException; use BookStack\Exceptions\PermissionsException; use BookStack\Http\Controller; +use BookStack\Permissions\Permission; use BookStack\References\ReferenceFetcher; +use BookStack\Util\DatabaseTransaction; use Illuminate\Http\Request; use Illuminate\Validation\ValidationException; use Throwable; @@ -24,7 +28,9 @@ class ChapterController extends Controller { public function __construct( protected ChapterRepo $chapterRepo, - protected ReferenceFetcher $referenceFetcher + protected ChapterQueries $queries, + protected EntityQueries $entityQueries, + protected ReferenceFetcher $referenceFetcher, ) { } @@ -33,12 +39,15 @@ public function __construct( */ public function create(string $bookSlug) { - $book = Book::visible()->where('slug', '=', $bookSlug)->firstOrFail(); - $this->checkOwnablePermission('chapter-create', $book); + $book = $this->entityQueries->books->findVisibleBySlugOrFail($bookSlug); + $this->checkOwnablePermission(Permission::ChapterCreate, $book); $this->setPageTitle(trans('entities.chapters_create')); - return view('chapters.create', ['book' => $book, 'current' => $book]); + return view('chapters.create', [ + 'book' => $book, + 'current' => $book, + ]); } /** @@ -49,13 +58,14 @@ public function create(string $bookSlug) public function store(Request $request, string $bookSlug) { $validated = $this->validate($request, [ - 'name' => ['required', 'string', 'max:255'], - 'description_html' => ['string', 'max:2000'], - 'tags' => ['array'], + 'name' => ['required', 'string', 'max:255'], + 'description_html' => ['string', 'max:2000'], + 'tags' => ['array'], + 'default_template_id' => ['nullable', 'integer'], ]); - $book = Book::visible()->where('slug', '=', $bookSlug)->firstOrFail(); - $this->checkOwnablePermission('chapter-create', $book); + $book = $this->entityQueries->books->findVisibleBySlugOrFail($bookSlug); + $this->checkOwnablePermission(Permission::ChapterCreate, $book); $chapter = $this->chapterRepo->create($validated, $book); @@ -67,11 +77,11 @@ public function store(Request $request, string $bookSlug) */ public function show(string $bookSlug, string $chapterSlug) { - $chapter = $this->chapterRepo->getBySlug($bookSlug, $chapterSlug); - $this->checkOwnablePermission('chapter-view', $chapter); + $chapter = $this->queries->findVisibleBySlugsOrFail($bookSlug, $chapterSlug); $sidebarTree = (new BookContents($chapter->book))->getTree(); - $pages = $chapter->getVisiblePages(); + $pages = $this->entityQueries->pages->visibleForChapterList($chapter->id)->get(); + $nextPreviousLocator = new NextPreviousContentLocator($chapter, $sidebarTree); View::incrementFor($chapter); @@ -95,8 +105,8 @@ public function show(string $bookSlug, string $chapterSlug) */ public function edit(string $bookSlug, string $chapterSlug) { - $chapter = $this->chapterRepo->getBySlug($bookSlug, $chapterSlug); - $this->checkOwnablePermission('chapter-update', $chapter); + $chapter = $this->queries->findVisibleBySlugsOrFail($bookSlug, $chapterSlug); + $this->checkOwnablePermission(Permission::ChapterUpdate, $chapter); $this->setPageTitle(trans('entities.chapters_edit_named', ['chapterName' => $chapter->getShortName()])); @@ -111,13 +121,14 @@ public function edit(string $bookSlug, string $chapterSlug) public function update(Request $request, string $bookSlug, string $chapterSlug) { $validated = $this->validate($request, [ - 'name' => ['required', 'string', 'max:255'], - 'description_html' => ['string', 'max:2000'], - 'tags' => ['array'], + 'name' => ['required', 'string', 'max:255'], + 'description_html' => ['string', 'max:2000'], + 'tags' => ['array'], + 'default_template_id' => ['nullable', 'integer'], ]); - $chapter = $this->chapterRepo->getBySlug($bookSlug, $chapterSlug); - $this->checkOwnablePermission('chapter-update', $chapter); + $chapter = $this->queries->findVisibleBySlugsOrFail($bookSlug, $chapterSlug); + $this->checkOwnablePermission(Permission::ChapterUpdate, $chapter); $this->chapterRepo->update($chapter, $validated); @@ -131,8 +142,8 @@ public function update(Request $request, string $bookSlug, string $chapterSlug) */ public function showDelete(string $bookSlug, string $chapterSlug) { - $chapter = $this->chapterRepo->getBySlug($bookSlug, $chapterSlug); - $this->checkOwnablePermission('chapter-delete', $chapter); + $chapter = $this->queries->findVisibleBySlugsOrFail($bookSlug, $chapterSlug); + $this->checkOwnablePermission(Permission::ChapterDelete, $chapter); $this->setPageTitle(trans('entities.chapters_delete_named', ['chapterName' => $chapter->getShortName()])); @@ -147,8 +158,8 @@ public function showDelete(string $bookSlug, string $chapterSlug) */ public function destroy(string $bookSlug, string $chapterSlug) { - $chapter = $this->chapterRepo->getBySlug($bookSlug, $chapterSlug); - $this->checkOwnablePermission('chapter-delete', $chapter); + $chapter = $this->queries->findVisibleBySlugsOrFail($bookSlug, $chapterSlug); + $this->checkOwnablePermission(Permission::ChapterDelete, $chapter); $this->chapterRepo->destroy($chapter); @@ -162,10 +173,10 @@ public function destroy(string $bookSlug, string $chapterSlug) */ public function showMove(string $bookSlug, string $chapterSlug) { - $chapter = $this->chapterRepo->getBySlug($bookSlug, $chapterSlug); + $chapter = $this->queries->findVisibleBySlugsOrFail($bookSlug, $chapterSlug); $this->setPageTitle(trans('entities.chapters_move_named', ['chapterName' => $chapter->getShortName()])); - $this->checkOwnablePermission('chapter-update', $chapter); - $this->checkOwnablePermission('chapter-delete', $chapter); + $this->checkOwnablePermission(Permission::ChapterUpdate, $chapter); + $this->checkOwnablePermission(Permission::ChapterDelete, $chapter); return view('chapters.move', [ 'chapter' => $chapter, @@ -180,9 +191,9 @@ public function showMove(string $bookSlug, string $chapterSlug) */ public function move(Request $request, string $bookSlug, string $chapterSlug) { - $chapter = $this->chapterRepo->getBySlug($bookSlug, $chapterSlug); - $this->checkOwnablePermission('chapter-update', $chapter); - $this->checkOwnablePermission('chapter-delete', $chapter); + $chapter = $this->queries->findVisibleBySlugsOrFail($bookSlug, $chapterSlug); + $this->checkOwnablePermission(Permission::ChapterUpdate, $chapter); + $this->checkOwnablePermission(Permission::ChapterDelete, $chapter); $entitySelection = $request->get('entity_selection', null); if ($entitySelection === null || $entitySelection === '') { @@ -209,8 +220,7 @@ public function move(Request $request, string $bookSlug, string $chapterSlug) */ public function showCopy(string $bookSlug, string $chapterSlug) { - $chapter = $this->chapterRepo->getBySlug($bookSlug, $chapterSlug); - $this->checkOwnablePermission('chapter-view', $chapter); + $chapter = $this->queries->findVisibleBySlugsOrFail($bookSlug, $chapterSlug); session()->flashInput(['name' => $chapter->name]); @@ -228,19 +238,18 @@ public function showCopy(string $bookSlug, string $chapterSlug) */ public function copy(Request $request, Cloner $cloner, string $bookSlug, string $chapterSlug) { - $chapter = $this->chapterRepo->getBySlug($bookSlug, $chapterSlug); - $this->checkOwnablePermission('chapter-view', $chapter); + $chapter = $this->queries->findVisibleBySlugsOrFail($bookSlug, $chapterSlug); $entitySelection = $request->get('entity_selection') ?: null; - $newParentBook = $entitySelection ? $this->chapterRepo->findParentByIdentifier($entitySelection) : $chapter->getParent(); + $newParentBook = $entitySelection ? $this->entityQueries->findVisibleByStringIdentifier($entitySelection) : $chapter->getParent(); - if (is_null($newParentBook)) { + if (!$newParentBook instanceof Book) { $this->showErrorNotification(trans('errors.selected_book_not_found')); return redirect($chapter->getUrl('/copy')); } - $this->checkOwnablePermission('chapter-create', $newParentBook); + $this->checkOwnablePermission(Permission::ChapterCreate, $newParentBook); $newName = $request->get('name') ?: $chapter->name; $chapterCopy = $cloner->cloneChapter($chapter, $newParentBook, $newName); @@ -254,12 +263,14 @@ public function copy(Request $request, Cloner $cloner, string $bookSlug, string */ public function convertToBook(HierarchyTransformer $transformer, string $bookSlug, string $chapterSlug) { - $chapter = $this->chapterRepo->getBySlug($bookSlug, $chapterSlug); - $this->checkOwnablePermission('chapter-update', $chapter); - $this->checkOwnablePermission('chapter-delete', $chapter); - $this->checkPermission('book-create-all'); - - $book = $transformer->transformChapterToBook($chapter); + $chapter = $this->queries->findVisibleBySlugsOrFail($bookSlug, $chapterSlug); + $this->checkOwnablePermission(Permission::ChapterUpdate, $chapter); + $this->checkOwnablePermission(Permission::ChapterDelete, $chapter); + $this->checkPermission(Permission::BookCreateAll); + + $book = (new DatabaseTransaction(function () use ($chapter, $transformer) { + return $transformer->transformChapterToBook($chapter); + }))->run(); return redirect($book->getUrl()); } diff --git a/app/Entities/Controllers/PageApiController.php b/app/Entities/Controllers/PageApiController.php index d2947f1bb72..033c19a7a33 100644 --- a/app/Entities/Controllers/PageApiController.php +++ b/app/Entities/Controllers/PageApiController.php @@ -2,18 +2,18 @@ namespace BookStack\Entities\Controllers; -use BookStack\Entities\Models\Book; -use BookStack\Entities\Models\Chapter; -use BookStack\Entities\Models\Page; +use BookStack\Entities\Queries\EntityQueries; +use BookStack\Entities\Queries\PageQueries; use BookStack\Entities\Repos\PageRepo; use BookStack\Exceptions\PermissionsException; use BookStack\Http\ApiController; +use BookStack\Permissions\Permission; use Exception; use Illuminate\Http\Request; class PageApiController extends ApiController { - protected $rules = [ + protected array $rules = [ 'create' => [ 'book_id' => ['required_without:chapter_id', 'integer'], 'chapter_id' => ['required_without:book_id', 'integer'], @@ -35,7 +35,9 @@ class PageApiController extends ApiController ]; public function __construct( - protected PageRepo $pageRepo + protected PageRepo $pageRepo, + protected PageQueries $queries, + protected EntityQueries $entityQueries, ) { } @@ -44,7 +46,8 @@ public function __construct( */ public function list() { - $pages = Page::visible(); + $pages = $this->queries->visibleForList() + ->addSelect(['created_by', 'updated_by', 'revision_count', 'editor']); return $this->apiListingResponse($pages, [ 'id', 'book_id', 'chapter_id', 'name', 'slug', 'priority', @@ -70,11 +73,11 @@ public function create(Request $request) $this->validate($request, $this->rules['create']); if ($request->has('chapter_id')) { - $parent = Chapter::visible()->findOrFail($request->get('chapter_id')); + $parent = $this->entityQueries->chapters->findVisibleByIdOrFail(intval($request->get('chapter_id'))); } else { - $parent = Book::visible()->findOrFail($request->get('book_id')); + $parent = $this->entityQueries->books->findVisibleByIdOrFail(intval($request->get('book_id'))); } - $this->checkOwnablePermission('page-create', $parent); + $this->checkOwnablePermission(Permission::PageCreate, $parent); $draft = $this->pageRepo->getNewDraftPage($parent); $this->pageRepo->publishDraft($draft, $request->only(array_keys($this->rules['create']))); @@ -97,7 +100,7 @@ public function create(Request $request) */ public function read(string $id) { - $page = $this->pageRepo->getById($id, []); + $page = $this->queries->findVisibleByIdOrFail($id); return response()->json($page->forJsonDisplay()); } @@ -113,18 +116,18 @@ public function update(Request $request, string $id) { $requestData = $this->validate($request, $this->rules['update']); - $page = $this->pageRepo->getById($id, []); - $this->checkOwnablePermission('page-update', $page); + $page = $this->queries->findVisibleByIdOrFail($id); + $this->checkOwnablePermission(Permission::PageUpdate, $page); $parent = null; if ($request->has('chapter_id')) { - $parent = Chapter::visible()->findOrFail($request->get('chapter_id')); + $parent = $this->entityQueries->chapters->findVisibleByIdOrFail(intval($request->get('chapter_id'))); } elseif ($request->has('book_id')) { - $parent = Book::visible()->findOrFail($request->get('book_id')); + $parent = $this->entityQueries->books->findVisibleByIdOrFail(intval($request->get('book_id'))); } if ($parent && !$parent->matches($page->getParent())) { - $this->checkOwnablePermission('page-delete', $page); + $this->checkOwnablePermission(Permission::PageDelete, $page); try { $this->pageRepo->move($page, $parent->getType() . ':' . $parent->id); @@ -148,8 +151,8 @@ public function update(Request $request, string $id) */ public function delete(string $id) { - $page = $this->pageRepo->getById($id, []); - $this->checkOwnablePermission('page-delete', $page); + $page = $this->queries->findVisibleByIdOrFail($id); + $this->checkOwnablePermission(Permission::PageDelete, $page); $this->pageRepo->destroy($page); diff --git a/app/Entities/Controllers/PageController.php b/app/Entities/Controllers/PageController.php index adafcdc7bd9..67ecb0bb377 100644 --- a/app/Entities/Controllers/PageController.php +++ b/app/Entities/Controllers/PageController.php @@ -6,7 +6,9 @@ use BookStack\Activity\Tools\CommentTree; use BookStack\Activity\Tools\UserEntityWatchOptions; use BookStack\Entities\Models\Book; -use BookStack\Entities\Models\Page; +use BookStack\Entities\Models\Chapter; +use BookStack\Entities\Queries\EntityQueries; +use BookStack\Entities\Queries\PageQueries; use BookStack\Entities\Repos\PageRepo; use BookStack\Entities\Tools\BookContents; use BookStack\Entities\Tools\Cloner; @@ -15,8 +17,10 @@ use BookStack\Entities\Tools\PageEditActivity; use BookStack\Entities\Tools\PageEditorData; use BookStack\Exceptions\NotFoundException; +use BookStack\Exceptions\NotifyException; use BookStack\Exceptions\PermissionsException; use BookStack\Http\Controller; +use BookStack\Permissions\Permission; use BookStack\References\ReferenceFetcher; use Exception; use Illuminate\Database\Eloquent\Relations\BelongsTo; @@ -28,6 +32,8 @@ class PageController extends Controller { public function __construct( protected PageRepo $pageRepo, + protected PageQueries $queries, + protected EntityQueries $entityQueries, protected ReferenceFetcher $referenceFetcher ) { } @@ -37,10 +43,15 @@ public function __construct( * * @throws Throwable */ - public function create(string $bookSlug, string $chapterSlug = null) + public function create(string $bookSlug, ?string $chapterSlug = null) { - $parent = $this->pageRepo->getParentFromSlugs($bookSlug, $chapterSlug); - $this->checkOwnablePermission('page-create', $parent); + if ($chapterSlug) { + $parent = $this->entityQueries->chapters->findVisibleBySlugsOrFail($bookSlug, $chapterSlug); + } else { + $parent = $this->entityQueries->books->findVisibleBySlugOrFail($bookSlug); + } + + $this->checkOwnablePermission(Permission::PageCreate, $parent); // Redirect to draft edit screen if signed in if ($this->isSignedIn()) { @@ -60,14 +71,19 @@ public function create(string $bookSlug, string $chapterSlug = null) * * @throws ValidationException */ - public function createAsGuest(Request $request, string $bookSlug, string $chapterSlug = null) + public function createAsGuest(Request $request, string $bookSlug, ?string $chapterSlug = null) { $this->validate($request, [ 'name' => ['required', 'string', 'max:255'], ]); - $parent = $this->pageRepo->getParentFromSlugs($bookSlug, $chapterSlug); - $this->checkOwnablePermission('page-create', $parent); + if ($chapterSlug) { + $parent = $this->entityQueries->chapters->findVisibleBySlugsOrFail($bookSlug, $chapterSlug); + } else { + $parent = $this->entityQueries->books->findVisibleBySlugOrFail($bookSlug); + } + + $this->checkOwnablePermission(Permission::PageCreate, $parent); $page = $this->pageRepo->getNewDraftPage($parent); $this->pageRepo->publishDraft($page, [ @@ -84,10 +100,10 @@ public function createAsGuest(Request $request, string $bookSlug, string $chapte */ public function editDraft(Request $request, string $bookSlug, int $pageId) { - $draft = $this->pageRepo->getById($pageId); - $this->checkOwnablePermission('page-create', $draft->getParent()); + $draft = $this->queries->findVisibleByIdOrFail($pageId); + $this->checkOwnablePermission(Permission::PageCreate, $draft->getParent()); - $editorData = new PageEditorData($draft, $this->pageRepo, $request->query('editor', '')); + $editorData = new PageEditorData($draft, $this->entityQueries, $request->query('editor', '')); $this->setPageTitle(trans('entities.pages_edit_draft')); return view('pages.edit', $editorData->getViewData()); @@ -104,8 +120,8 @@ public function store(Request $request, string $bookSlug, int $pageId) $this->validate($request, [ 'name' => ['required', 'string', 'max:255'], ]); - $draftPage = $this->pageRepo->getById($pageId); - $this->checkOwnablePermission('page-create', $draftPage->getParent()); + $draftPage = $this->queries->findVisibleByIdOrFail($pageId); + $this->checkOwnablePermission(Permission::PageCreate, $draftPage->getParent()); $page = $this->pageRepo->publishDraft($draftPage, $request->all()); @@ -121,19 +137,18 @@ public function store(Request $request, string $bookSlug, int $pageId) public function show(string $bookSlug, string $pageSlug) { try { - $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug); + $page = $this->queries->findVisibleBySlugsOrFail($bookSlug, $pageSlug); } catch (NotFoundException $e) { - $page = $this->pageRepo->getByOldSlug($bookSlug, $pageSlug); + $revision = $this->entityQueries->revisions->findLatestVersionBySlugs($bookSlug, $pageSlug); + $page = $revision->page ?? null; - if ($page === null) { + if (is_null($page)) { throw $e; } return redirect($page->getUrl()); } - $this->checkOwnablePermission('page-view', $page); - $pageContent = (new PageContent($page)); $page->html = $pageContent->render(); $pageNav = $pageContent->getNavigation($page->html); @@ -166,7 +181,7 @@ public function show(string $bookSlug, string $pageSlug) */ public function getPageAjax(int $pageId) { - $page = $this->pageRepo->getById($pageId); + $page = $this->queries->findVisibleByIdOrFail($pageId); $page->setHidden(array_diff($page->getHidden(), ['html', 'markdown'])); $page->makeHidden(['book']); @@ -180,10 +195,10 @@ public function getPageAjax(int $pageId) */ public function edit(Request $request, string $bookSlug, string $pageSlug) { - $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug); - $this->checkOwnablePermission('page-update', $page); + $page = $this->queries->findVisibleBySlugsOrFail($bookSlug, $pageSlug); + $this->checkOwnablePermission(Permission::PageUpdate, $page, $page->getUrl()); - $editorData = new PageEditorData($page, $this->pageRepo, $request->query('editor', '')); + $editorData = new PageEditorData($page, $this->entityQueries, $request->query('editor', '')); if ($editorData->getWarnings()) { $this->showWarningNotification(implode("\n", $editorData->getWarnings())); } @@ -204,8 +219,8 @@ public function update(Request $request, string $bookSlug, string $pageSlug) $this->validate($request, [ 'name' => ['required', 'string', 'max:255'], ]); - $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug); - $this->checkOwnablePermission('page-update', $page); + $page = $this->queries->findVisibleBySlugsOrFail($bookSlug, $pageSlug); + $this->checkOwnablePermission(Permission::PageUpdate, $page); $this->pageRepo->update($page, $request->all()); @@ -219,8 +234,8 @@ public function update(Request $request, string $bookSlug, string $pageSlug) */ public function saveDraft(Request $request, int $pageId) { - $page = $this->pageRepo->getById($pageId); - $this->checkOwnablePermission('page-update', $page); + $page = $this->queries->findVisibleByIdOrFail($pageId); + $this->checkOwnablePermission(Permission::PageUpdate, $page); if (!$this->isSignedIn()) { return $this->jsonError(trans('errors.guests_cannot_save_drafts'), 500); @@ -244,7 +259,7 @@ public function saveDraft(Request $request, int $pageId) */ public function redirectFromLink(int $pageId) { - $page = $this->pageRepo->getById($pageId); + $page = $this->queries->findVisibleByIdOrFail($pageId); return redirect($page->getUrl()); } @@ -256,10 +271,12 @@ public function redirectFromLink(int $pageId) */ public function showDelete(string $bookSlug, string $pageSlug) { - $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug); - $this->checkOwnablePermission('page-delete', $page); + $page = $this->queries->findVisibleBySlugsOrFail($bookSlug, $pageSlug); + $this->checkOwnablePermission(Permission::PageDelete, $page); $this->setPageTitle(trans('entities.pages_delete_named', ['pageName' => $page->getShortName()])); - $usedAsTemplate = Book::query()->where('default_template_id', '=', $page->id)->count() > 0; + $usedAsTemplate = + $this->entityQueries->books->start()->where('default_template_id', '=', $page->id)->count() > 0 || + $this->entityQueries->chapters->start()->where('default_template_id', '=', $page->id)->count() > 0; return view('pages.delete', [ 'book' => $page->book, @@ -276,10 +293,12 @@ public function showDelete(string $bookSlug, string $pageSlug) */ public function showDeleteDraft(string $bookSlug, int $pageId) { - $page = $this->pageRepo->getById($pageId); - $this->checkOwnablePermission('page-update', $page); + $page = $this->queries->findVisibleByIdOrFail($pageId); + $this->checkOwnablePermission(Permission::PageUpdate, $page); $this->setPageTitle(trans('entities.pages_delete_draft_named', ['pageName' => $page->getShortName()])); - $usedAsTemplate = Book::query()->where('default_template_id', '=', $page->id)->count() > 0; + $usedAsTemplate = + $this->entityQueries->books->start()->where('default_template_id', '=', $page->id)->count() > 0 || + $this->entityQueries->chapters->start()->where('default_template_id', '=', $page->id)->count() > 0; return view('pages.delete', [ 'book' => $page->book, @@ -297,8 +316,8 @@ public function showDeleteDraft(string $bookSlug, int $pageId) */ public function destroy(string $bookSlug, string $pageSlug) { - $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug); - $this->checkOwnablePermission('page-delete', $page); + $page = $this->queries->findVisibleBySlugsOrFail($bookSlug, $pageSlug); + $this->checkOwnablePermission(Permission::PageDelete, $page); $parent = $page->getParent(); $this->pageRepo->destroy($page); @@ -314,16 +333,16 @@ public function destroy(string $bookSlug, string $pageSlug) */ public function destroyDraft(string $bookSlug, int $pageId) { - $page = $this->pageRepo->getById($pageId); + $page = $this->queries->findVisibleByIdOrFail($pageId); $book = $page->book; $chapter = $page->chapter; - $this->checkOwnablePermission('page-update', $page); + $this->checkOwnablePermission(Permission::PageUpdate, $page); $this->pageRepo->destroy($page); $this->showSuccessNotification(trans('entities.pages_delete_draft_success')); - if ($chapter && userCan('view', $chapter)) { + if ($chapter && userCan(Permission::ChapterView, $chapter)) { return redirect($chapter->getUrl()); } @@ -339,7 +358,9 @@ public function showRecentlyUpdated() $query->scopes('visible'); }; - $pages = Page::visible()->with(['updatedBy', 'book' => $visibleBelongsScope, 'chapter' => $visibleBelongsScope]) + $pages = $this->queries->visibleForList() + ->addSelect('updated_by') + ->with(['updatedBy', 'book' => $visibleBelongsScope, 'chapter' => $visibleBelongsScope]) ->orderBy('updated_at', 'desc') ->paginate(20) ->setPath(url('/pages/recently-updated')); @@ -361,9 +382,9 @@ public function showRecentlyUpdated() */ public function showMove(string $bookSlug, string $pageSlug) { - $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug); - $this->checkOwnablePermission('page-update', $page); - $this->checkOwnablePermission('page-delete', $page); + $page = $this->queries->findVisibleBySlugsOrFail($bookSlug, $pageSlug); + $this->checkOwnablePermission(Permission::PageUpdate, $page); + $this->checkOwnablePermission(Permission::PageDelete, $page); return view('pages.move', [ 'book' => $page->book, @@ -379,9 +400,9 @@ public function showMove(string $bookSlug, string $pageSlug) */ public function move(Request $request, string $bookSlug, string $pageSlug) { - $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug); - $this->checkOwnablePermission('page-update', $page); - $this->checkOwnablePermission('page-delete', $page); + $page = $this->queries->findVisibleBySlugsOrFail($bookSlug, $pageSlug); + $this->checkOwnablePermission(Permission::PageUpdate, $page); + $this->checkOwnablePermission(Permission::PageDelete, $page); $entitySelection = $request->get('entity_selection', null); if ($entitySelection === null || $entitySelection === '') { @@ -408,8 +429,7 @@ public function move(Request $request, string $bookSlug, string $pageSlug) */ public function showCopy(string $bookSlug, string $pageSlug) { - $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug); - $this->checkOwnablePermission('page-view', $page); + $page = $this->queries->findVisibleBySlugsOrFail($bookSlug, $pageSlug); session()->flashInput(['name' => $page->name]); return view('pages.copy', [ @@ -426,19 +446,19 @@ public function showCopy(string $bookSlug, string $pageSlug) */ public function copy(Request $request, Cloner $cloner, string $bookSlug, string $pageSlug) { - $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug); - $this->checkOwnablePermission('page-view', $page); + $page = $this->queries->findVisibleBySlugsOrFail($bookSlug, $pageSlug); + $this->checkOwnablePermission(Permission::PageView, $page); $entitySelection = $request->get('entity_selection') ?: null; - $newParent = $entitySelection ? $this->pageRepo->findParentByIdentifier($entitySelection) : $page->getParent(); + $newParent = $entitySelection ? $this->entityQueries->findVisibleByStringIdentifier($entitySelection) : $page->getParent(); - if (is_null($newParent)) { + if (!$newParent instanceof Book && !$newParent instanceof Chapter) { $this->showErrorNotification(trans('errors.selected_book_chapter_not_found')); return redirect($page->getUrl('/copy')); } - $this->checkOwnablePermission('page-create', $newParent); + $this->checkOwnablePermission(Permission::PageCreate, $newParent); $newName = $request->get('name') ?: $page->name; $pageCopy = $cloner->clonePage($page, $newParent, $newName); diff --git a/app/Entities/Controllers/PageRevisionController.php b/app/Entities/Controllers/PageRevisionController.php index a3190a0fc43..35f1e8daf0e 100644 --- a/app/Entities/Controllers/PageRevisionController.php +++ b/app/Entities/Controllers/PageRevisionController.php @@ -4,12 +4,14 @@ use BookStack\Activity\ActivityType; use BookStack\Entities\Models\PageRevision; +use BookStack\Entities\Queries\PageQueries; use BookStack\Entities\Repos\PageRepo; use BookStack\Entities\Repos\RevisionRepo; use BookStack\Entities\Tools\PageContent; use BookStack\Exceptions\NotFoundException; use BookStack\Facades\Activity; use BookStack\Http\Controller; +use BookStack\Permissions\Permission; use BookStack\Util\SimpleListOptions; use Illuminate\Http\Request; use Ssddanbrown\HtmlDiff\Diff; @@ -18,6 +20,7 @@ class PageRevisionController extends Controller { public function __construct( protected PageRepo $pageRepo, + protected PageQueries $pageQueries, protected RevisionRepo $revisionRepo, ) { } @@ -29,7 +32,7 @@ public function __construct( */ public function index(Request $request, string $bookSlug, string $pageSlug) { - $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug); + $page = $this->pageQueries->findVisibleBySlugsOrFail($bookSlug, $pageSlug); $listOptions = SimpleListOptions::fromRequest($request, 'page_revisions', true)->withSortOptions([ 'id' => trans('entities.pages_revisions_sort_number') ]); @@ -41,7 +44,6 @@ public function index(Request $request, string $bookSlug, string $pageSlug) ->selectRaw("IF(markdown = '', false, true) as is_markdown") ->with(['page.book', 'createdBy']) ->reorder('id', $listOptions->getOrder()) - ->reorder('created_at', $listOptions->getOrder()) ->paginate(50); $this->setPageTitle(trans('entities.pages_revisions_named', ['pageName' => $page->getShortName()])); @@ -50,6 +52,7 @@ public function index(Request $request, string $bookSlug, string $pageSlug) 'revisions' => $revisions, 'page' => $page, 'listOptions' => $listOptions, + 'oldestRevisionId' => $page->revisions()->min('id'), ]); } @@ -60,7 +63,7 @@ public function index(Request $request, string $bookSlug, string $pageSlug) */ public function show(string $bookSlug, string $pageSlug, int $revisionId) { - $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug); + $page = $this->pageQueries->findVisibleBySlugsOrFail($bookSlug, $pageSlug); /** @var ?PageRevision $revision */ $revision = $page->revisions()->where('id', '=', $revisionId)->first(); if ($revision === null) { @@ -89,14 +92,14 @@ public function show(string $bookSlug, string $pageSlug, int $revisionId) */ public function changes(string $bookSlug, string $pageSlug, int $revisionId) { - $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug); + $page = $this->pageQueries->findVisibleBySlugsOrFail($bookSlug, $pageSlug); /** @var ?PageRevision $revision */ $revision = $page->revisions()->where('id', '=', $revisionId)->first(); if ($revision === null) { throw new NotFoundException(); } - $prev = $revision->getPrevious(); + $prev = $revision->getPreviousRevision(); $prevContent = $prev->html ?? ''; $diff = Diff::excecute($prevContent, $revision->html); @@ -121,8 +124,8 @@ public function changes(string $bookSlug, string $pageSlug, int $revisionId) */ public function restore(string $bookSlug, string $pageSlug, int $revisionId) { - $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug); - $this->checkOwnablePermission('page-update', $page); + $page = $this->pageQueries->findVisibleBySlugsOrFail($bookSlug, $pageSlug); + $this->checkOwnablePermission(Permission::PageUpdate, $page); $page = $this->pageRepo->restoreRevision($page, $revisionId); @@ -136,8 +139,8 @@ public function restore(string $bookSlug, string $pageSlug, int $revisionId) */ public function destroy(string $bookSlug, string $pageSlug, int $revId) { - $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug); - $this->checkOwnablePermission('page-delete', $page); + $page = $this->pageQueries->findVisibleBySlugsOrFail($bookSlug, $pageSlug); + $this->checkOwnablePermission(Permission::PageDelete, $page); $revision = $page->revisions()->where('id', '=', $revId)->first(); if ($revision === null) { @@ -162,7 +165,7 @@ public function destroy(string $bookSlug, string $pageSlug, int $revId) */ public function destroyUserDraft(string $pageId) { - $page = $this->pageRepo->getById($pageId); + $page = $this->pageQueries->findVisibleByIdOrFail($pageId); $this->revisionRepo->deleteDraftsForCurrentUser($page); return response('', 200); diff --git a/app/Entities/Controllers/PageTemplateController.php b/app/Entities/Controllers/PageTemplateController.php index e4e7b568064..c0b97214856 100644 --- a/app/Entities/Controllers/PageTemplateController.php +++ b/app/Entities/Controllers/PageTemplateController.php @@ -2,6 +2,7 @@ namespace BookStack\Entities\Controllers; +use BookStack\Entities\Queries\PageQueries; use BookStack\Entities\Repos\PageRepo; use BookStack\Exceptions\NotFoundException; use BookStack\Http\Controller; @@ -9,14 +10,10 @@ class PageTemplateController extends Controller { - protected $pageRepo; - - /** - * PageTemplateController constructor. - */ - public function __construct(PageRepo $pageRepo) - { - $this->pageRepo = $pageRepo; + public function __construct( + protected PageRepo $pageRepo, + protected PageQueries $pageQueries, + ) { } /** @@ -26,7 +23,19 @@ public function list(Request $request) { $page = $request->get('page', 1); $search = $request->get('search', ''); - $templates = $this->pageRepo->getTemplates(10, $page, $search); + $count = 10; + + $query = $this->pageQueries->visibleTemplates() + ->orderBy('name', 'asc') + ->skip(($page - 1) * $count) + ->take($count); + + if ($search) { + $query->where('name', 'like', '%' . $search . '%'); + } + + $templates = $query->paginate($count, ['*'], 'page', $page); + $templates->withPath('/templates'); if ($search) { $templates->appends(['search' => $search]); @@ -44,7 +53,7 @@ public function list(Request $request) */ public function get(int $templateId) { - $page = $this->pageRepo->getById($templateId); + $page = $this->pageQueries->findVisibleByIdOrFail($templateId); if (!$page->template) { throw new NotFoundException(); diff --git a/app/Entities/Controllers/RecycleBinApiController.php b/app/Entities/Controllers/RecycleBinApiController.php index bf22d7dcd0c..6146851366b 100644 --- a/app/Entities/Controllers/RecycleBinApiController.php +++ b/app/Entities/Controllers/RecycleBinApiController.php @@ -6,18 +6,20 @@ use BookStack\Entities\Models\BookChild; use BookStack\Entities\Models\Chapter; use BookStack\Entities\Models\Deletion; +use BookStack\Entities\Models\Page; use BookStack\Entities\Repos\DeletionRepo; use BookStack\Http\ApiController; -use Closure; +use BookStack\Permissions\Permission; use Illuminate\Database\Eloquent\Builder; +use Illuminate\Database\Eloquent\Relations\HasMany; class RecycleBinApiController extends ApiController { public function __construct() { $this->middleware(function ($request, $next) { - $this->checkPermission('settings-manage'); - $this->checkPermission('restrictions-manage-all'); + $this->checkPermission(Permission::SettingsManage); + $this->checkPermission(Permission::RestrictionsManageAll); return $next($request); }); @@ -40,7 +42,7 @@ public function list() 'updated_at', 'deletable_type', 'deletable_id', - ], [Closure::fromCallable([$this, 'listFormatter'])]); + ], [$this->listFormatter(...)]); } /** @@ -69,10 +71,9 @@ public function destroy(DeletionRepo $deletionRepo, string $deletionId) /** * Load some related details for the deletion listing. */ - protected function listFormatter(Deletion $deletion) + protected function listFormatter(Deletion $deletion): void { $deletable = $deletion->deletable; - $withTrashedQuery = fn (Builder $query) => $query->withTrashed(); if ($deletable instanceof BookChild) { $parent = $deletable->getParent(); @@ -81,11 +82,19 @@ protected function listFormatter(Deletion $deletion) } if ($deletable instanceof Book || $deletable instanceof Chapter) { - $countsToLoad = ['pages' => $withTrashedQuery]; + $countsToLoad = ['pages' => static::withTrashedQuery(...)]; if ($deletable instanceof Book) { - $countsToLoad['chapters'] = $withTrashedQuery; + $countsToLoad['chapters'] = static::withTrashedQuery(...); } $deletable->loadCount($countsToLoad); } } + + /** + * @param Builder $query + */ + protected static function withTrashedQuery(Builder $query): void + { + $query->withTrashed(); + } } diff --git a/app/Entities/Controllers/RecycleBinController.php b/app/Entities/Controllers/RecycleBinController.php index 78f86a5ae50..f3c2b6a01cc 100644 --- a/app/Entities/Controllers/RecycleBinController.php +++ b/app/Entities/Controllers/RecycleBinController.php @@ -8,6 +8,7 @@ use BookStack\Entities\Repos\DeletionRepo; use BookStack\Entities\Tools\TrashCan; use BookStack\Http\Controller; +use BookStack\Permissions\Permission; class RecycleBinController extends Controller { @@ -20,8 +21,8 @@ class RecycleBinController extends Controller public function __construct() { $this->middleware(function ($request, $next) { - $this->checkPermission('settings-manage'); - $this->checkPermission('restrictions-manage-all'); + $this->checkPermission(Permission::SettingsManage); + $this->checkPermission(Permission::RestrictionsManageAll); return $next($request); }); @@ -116,9 +117,9 @@ public function destroy(DeletionRepo $deletionRepo, string $id) * * @throws \Exception */ - public function empty() + public function empty(TrashCan $trash) { - $deleteCount = (new TrashCan())->empty(); + $deleteCount = $trash->empty(); $this->logActivity(ActivityType::RECYCLE_BIN_EMPTY); $this->showSuccessNotification(trans('settings.recycle_bin_destroy_notification', ['count' => $deleteCount])); diff --git a/app/Entities/Models/Book.php b/app/Entities/Models/Book.php index 14cb790c5c5..5f54e0f6af6 100644 --- a/app/Entities/Models/Book.php +++ b/app/Entities/Models/Book.php @@ -2,6 +2,7 @@ namespace BookStack\Entities\Models; +use BookStack\Sorting\SortRule; use BookStack\Uploads\Image; use Exception; use Illuminate\Database\Eloquent\Factories\HasFactory; @@ -16,17 +17,19 @@ * @property string $description * @property int $image_id * @property ?int $default_template_id + * @property ?int $sort_rule_id * @property Image|null $cover * @property \Illuminate\Database\Eloquent\Collection $chapters * @property \Illuminate\Database\Eloquent\Collection $pages * @property \Illuminate\Database\Eloquent\Collection $directPages * @property \Illuminate\Database\Eloquent\Collection $shelves * @property ?Page $defaultTemplate + * @property ?SortRule $sortRule */ -class Book extends Entity implements HasCoverImage +class Book extends Entity implements CoverImageInterface, HtmlDescriptionInterface { use HasFactory; - use HasHtmlDescription; + use HtmlDescriptionTrait; public float $searchFactor = 1.2; @@ -82,8 +85,17 @@ public function defaultTemplate(): BelongsTo return $this->belongsTo(Page::class, 'default_template_id'); } + /** + * Get the sort set assigned to this book, if existing. + */ + public function sortRule(): BelongsTo + { + return $this->belongsTo(SortRule::class); + } + /** * Get all pages within this book. + * @return HasMany */ public function pages(): HasMany { @@ -100,6 +112,7 @@ public function directPages(): HasMany /** * Get all chapters within this book. + * @return HasMany */ public function chapters(): HasMany { @@ -117,20 +130,11 @@ public function shelves(): BelongsToMany /** * Get the direct child items within this book. */ - public function getDirectChildren(): Collection + public function getDirectVisibleChildren(): Collection { $pages = $this->directPages()->scopes('visible')->get(); $chapters = $this->chapters()->scopes('visible')->get(); return $pages->concat($chapters)->sortBy('priority')->sortByDesc('draft'); } - - /** - * Get a visible book by its slug. - * @throws \Illuminate\Database\Eloquent\ModelNotFoundException - */ - public static function getBySlug(string $slug): self - { - return static::visible()->where('slug', '=', $slug)->firstOrFail(); - } } diff --git a/app/Entities/Models/BookChild.php b/app/Entities/Models/BookChild.php index 18735e56b72..ad54fb926a9 100644 --- a/app/Entities/Models/BookChild.php +++ b/app/Entities/Models/BookChild.php @@ -13,38 +13,9 @@ * @property int $priority * @property string $book_slug * @property Book $book - * - * @method Builder whereSlugs(string $bookSlug, string $childSlug) */ abstract class BookChild extends Entity { - protected static function boot() - { - parent::boot(); - - // Load book slugs onto these models by default during query-time - static::addGlobalScope('book_slug', function (Builder $builder) { - $builder->addSelect(['book_slug' => function ($builder) { - $builder->select('slug') - ->from('books') - ->whereColumn('books.id', '=', 'book_id'); - }]); - }); - } - - /** - * Scope a query to find items where the child has the given childSlug - * where its parent has the bookSlug. - */ - public function scopeWhereSlugs(Builder $query, string $bookSlug, string $childSlug) - { - return $query->with('book') - ->whereHas('book', function (Builder $query) use ($bookSlug) { - $query->where('slug', '=', $bookSlug); - }) - ->where('slug', '=', $childSlug); - } - /** * Get the book this page sits in. */ diff --git a/app/Entities/Models/Bookshelf.php b/app/Entities/Models/Bookshelf.php index 68f074f0fc2..ff81e14fdb0 100644 --- a/app/Entities/Models/Bookshelf.php +++ b/app/Entities/Models/Bookshelf.php @@ -8,10 +8,10 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsToMany; -class Bookshelf extends Entity implements HasCoverImage +class Bookshelf extends Entity implements CoverImageInterface, HtmlDescriptionInterface { use HasFactory; - use HasHtmlDescription; + use HtmlDescriptionTrait; protected $table = 'bookshelves'; @@ -70,6 +70,7 @@ public function getBookCover(int $width = 440, int $height = 250): string /** * Get the cover image of the shelf. + * @return BelongsTo */ public function cover(): BelongsTo { diff --git a/app/Entities/Models/Chapter.php b/app/Entities/Models/Chapter.php index f30d77b5c5c..d70a49e7a98 100644 --- a/app/Entities/Models/Chapter.php +++ b/app/Entities/Models/Chapter.php @@ -2,6 +2,7 @@ namespace BookStack\Entities\Models; +use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Support\Collection; @@ -10,12 +11,13 @@ * Class Chapter. * * @property Collection $pages - * @property string $description + * @property ?int $default_template_id + * @property ?Page $defaultTemplate */ -class Chapter extends BookChild +class Chapter extends BookChild implements HtmlDescriptionInterface { use HasFactory; - use HasHtmlDescription; + use HtmlDescriptionTrait; public float $searchFactor = 1.2; @@ -25,7 +27,7 @@ class Chapter extends BookChild /** * Get the pages that this chapter contains. * - * @return HasMany + * @return HasMany */ public function pages(string $dir = 'ASC'): HasMany { @@ -48,8 +50,17 @@ public function getUrl(string $path = ''): string return url('/' . implode('/', $parts)); } + /** + * Get the Page that is used as default template for newly created pages within this Chapter. + */ + public function defaultTemplate(): BelongsTo + { + return $this->belongsTo(Page::class, 'default_template_id'); + } + /** * Get the visible pages in this chapter. + * @return Collection */ public function getVisiblePages(): Collection { @@ -59,13 +70,4 @@ public function getVisiblePages(): Collection ->orderBy('priority', 'asc') ->get(); } - - /** - * Get a visible chapter by its book and page slugs. - * @throws \Illuminate\Database\Eloquent\ModelNotFoundException - */ - public static function getBySlugs(string $bookSlug, string $chapterSlug): self - { - return static::visible()->whereSlugs($bookSlug, $chapterSlug)->firstOrFail(); - } } diff --git a/app/Entities/Models/HasCoverImage.php b/app/Entities/Models/CoverImageInterface.php similarity index 92% rename from app/Entities/Models/HasCoverImage.php rename to app/Entities/Models/CoverImageInterface.php index f665efce6d2..5f781fe02ab 100644 --- a/app/Entities/Models/HasCoverImage.php +++ b/app/Entities/Models/CoverImageInterface.php @@ -4,7 +4,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; -interface HasCoverImage +interface CoverImageInterface { /** * Get the cover image for this item. diff --git a/app/Entities/Models/Deletable.php b/app/Entities/Models/DeletableInterface.php similarity index 90% rename from app/Entities/Models/Deletable.php rename to app/Entities/Models/DeletableInterface.php index a2c7fad81b5..f771d9c6913 100644 --- a/app/Entities/Models/Deletable.php +++ b/app/Entities/Models/DeletableInterface.php @@ -8,7 +8,7 @@ * A model that can be deleted in a manner that deletions * are tracked to be part of the recycle bin system. */ -interface Deletable +interface DeletableInterface { public function deletions(): MorphMany; } diff --git a/app/Entities/Models/Deletion.php b/app/Entities/Models/Deletion.php index a73437c94a4..55589f61ec9 100644 --- a/app/Entities/Models/Deletion.php +++ b/app/Entities/Models/Deletion.php @@ -13,7 +13,7 @@ * @property int $deleted_by * @property string $deletable_type * @property int $deletable_id - * @property Deletable $deletable + * @property DeletableInterface $deletable */ class Deletion extends Model implements Loggable { diff --git a/app/Entities/Models/Entity.php b/app/Entities/Models/Entity.php index f07d372c3e9..31511aa832d 100644 --- a/app/Entities/Models/Entity.php +++ b/app/Entities/Models/Entity.php @@ -12,7 +12,7 @@ use BookStack\Activity\Models\Viewable; use BookStack\Activity\Models\Watch; use BookStack\App\Model; -use BookStack\App\Sluggable; +use BookStack\App\SluggableInterface; use BookStack\Entities\Tools\SlugGenerator; use BookStack\Permissions\JointPermissionBuilder; use BookStack\Permissions\Models\EntityPermission; @@ -22,10 +22,12 @@ use BookStack\Search\SearchIndex; use BookStack\Search\SearchTerm; use BookStack\Users\Models\HasCreatorAndUpdater; -use BookStack\Users\Models\HasOwner; +use BookStack\Users\Models\OwnableInterface; +use BookStack\Users\Models\User; use Carbon\Carbon; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Collection; +use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\MorphMany; use Illuminate\Database\Eloquent\SoftDeletes; @@ -42,17 +44,23 @@ * @property Carbon $deleted_at * @property int $created_by * @property int $updated_by + * @property int $owned_by * @property Collection $tags * * @method static Entity|Builder visible() * @method static Builder withLastView() * @method static Builder withViewCount() */ -abstract class Entity extends Model implements Sluggable, Favouritable, Viewable, Deletable, Loggable +abstract class Entity extends Model implements + SluggableInterface, + Favouritable, + Viewable, + DeletableInterface, + OwnableInterface, + Loggable { use SoftDeletes; use HasCreatorAndUpdater; - use HasOwner; /** * @var string - Name of property where the main text content is found @@ -137,7 +145,7 @@ public function matchesOrContains(self $entity): bool */ public function activity(): MorphMany { - return $this->morphMany(Activity::class, 'entity') + return $this->morphMany(Activity::class, 'loggable') ->orderBy('created_at', 'desc'); } @@ -199,6 +207,20 @@ public function jointPermissions(): MorphMany return $this->morphMany(JointPermission::class, 'entity'); } + /** + * Get the user who owns this entity. + * @return BelongsTo + */ + public function ownedBy(): BelongsTo + { + return $this->belongsTo(User::class, 'owned_by'); + } + + public function getOwnerFieldName(): string + { + return 'owned_by'; + } + /** * Get the related delete records for this entity. */ @@ -283,10 +305,14 @@ abstract public function getUrl(string $path = '/'): string; public function getParent(): ?self { if ($this instanceof Page) { - return $this->chapter_id ? $this->chapter()->withTrashed()->first() : $this->book()->withTrashed()->first(); + /** @var BelongsTo $builder */ + $builder = $this->chapter_id ? $this->chapter() : $this->book(); + return $builder->withTrashed()->first(); } if ($this instanceof Chapter) { - return $this->book()->withTrashed()->first(); + /** @var BelongsTo $builder */ + $builder = $this->book(); + return $builder->withTrashed()->first(); } return null; @@ -295,7 +321,7 @@ public function getParent(): ?self /** * Rebuild the permissions for this entity. */ - public function rebuildPermissions() + public function rebuildPermissions(): void { app()->make(JointPermissionBuilder::class)->rebuildForEntity(clone $this); } @@ -303,7 +329,7 @@ public function rebuildPermissions() /** * Index the current entity for search. */ - public function indexForSearch() + public function indexForSearch(): void { app()->make(SearchIndex::class)->indexEntity(clone $this); } @@ -313,7 +339,7 @@ public function indexForSearch() */ public function refreshSlug(): string { - $this->slug = app()->make(SlugGenerator::class)->generate($this); + $this->slug = app()->make(SlugGenerator::class)->generate($this, $this->name); return $this->slug; } diff --git a/app/Entities/Models/HasHtmlDescription.php b/app/Entities/Models/HasHtmlDescription.php deleted file mode 100644 index c9f08616dbd..00000000000 --- a/app/Entities/Models/HasHtmlDescription.php +++ /dev/null @@ -1,21 +0,0 @@ -description_html ?: '

' . nl2br(e($this->description)) . '

'; - return HtmlContentFilter::removeScriptsFromHtmlString($html); - } -} diff --git a/app/Entities/Models/HtmlDescriptionInterface.php b/app/Entities/Models/HtmlDescriptionInterface.php new file mode 100644 index 00000000000..ffe7f0c5ff1 --- /dev/null +++ b/app/Entities/Models/HtmlDescriptionInterface.php @@ -0,0 +1,17 @@ +description_html ?: '

' . nl2br(e($this->description)) . '

'; + if ($raw) { + return $html; + } + + return HtmlContentFilter::removeScriptsFromHtmlString($html); + } + + public function setDescriptionHtml(string $html, string|null $plaintext = null): void + { + $this->description_html = $html; + + if ($plaintext !== null) { + $this->description = $plaintext; + } + + if (empty($html) && !empty($plaintext)) { + $this->description_html = $this->descriptionHtml(); + } + } +} diff --git a/app/Entities/Models/Page.php b/app/Entities/Models/Page.php index 17d6f9a016f..499ef4d7288 100644 --- a/app/Entities/Models/Page.php +++ b/app/Entities/Models/Page.php @@ -3,6 +3,7 @@ namespace BookStack\Entities\Models; use BookStack\Entities\Tools\PageContent; +use BookStack\Entities\Tools\PageEditorType; use BookStack\Permissions\PermissionApplicator; use BookStack\Uploads\Attachment; use Illuminate\Database\Eloquent\Builder; @@ -32,9 +33,6 @@ class Page extends BookChild { use HasFactory; - public static $listAttributes = ['name', 'id', 'slug', 'book_id', 'chapter_id', 'draft', 'template', 'text', 'created_at', 'updated_at', 'priority']; - public static $contentAttributes = ['name', 'id', 'slug', 'book_id', 'chapter_id', 'draft', 'template', 'html', 'text', 'created_at', 'updated_at', 'priority']; - protected $fillable = ['name', 'priority']; public string $textField = 'text'; @@ -145,13 +143,4 @@ public function forJsonDisplay(): self return $refreshed; } - - /** - * Get a visible page by its book and page slugs. - * @throws \Illuminate\Database\Eloquent\ModelNotFoundException - */ - public static function getBySlugs(string $bookSlug, string $pageSlug): self - { - return static::visible()->whereSlugs($bookSlug, $pageSlug)->firstOrFail(); - } } diff --git a/app/Entities/Models/PageRevision.php b/app/Entities/Models/PageRevision.php index 10ff6d901c4..1a6c980e109 100644 --- a/app/Entities/Models/PageRevision.php +++ b/app/Entities/Models/PageRevision.php @@ -60,7 +60,7 @@ public function getUrl(string $path = ''): string /** * Get the previous revision for the same page if existing. */ - public function getPrevious(): ?PageRevision + public function getPreviousRevision(): ?PageRevision { $id = static::newQuery()->where('page_id', '=', $this->page_id) ->where('id', '<', $this->id) diff --git a/app/Entities/Queries/BookQueries.php b/app/Entities/Queries/BookQueries.php new file mode 100644 index 00000000000..2492f81318a --- /dev/null +++ b/app/Entities/Queries/BookQueries.php @@ -0,0 +1,78 @@ + + */ +class BookQueries implements ProvidesEntityQueries +{ + protected static array $listAttributes = [ + 'id', 'slug', 'name', 'description', + 'created_at', 'updated_at', 'image_id', 'owned_by', + ]; + + /** + * @return Builder + */ + public function start(): Builder + { + return Book::query(); + } + + public function findVisibleById(int $id): ?Book + { + return $this->start()->scopes('visible')->find($id); + } + + public function findVisibleByIdOrFail(int $id): Book + { + return $this->start()->scopes('visible')->findOrFail($id); + } + + public function findVisibleBySlugOrFail(string $slug): Book + { + /** @var ?Book $book */ + $book = $this->start() + ->scopes('visible') + ->where('slug', '=', $slug) + ->first(); + + if ($book === null) { + throw new NotFoundException(trans('errors.book_not_found')); + } + + return $book; + } + + public function visibleForList(): Builder + { + return $this->start()->scopes('visible') + ->select(static::$listAttributes); + } + + public function visibleForListWithCover(): Builder + { + return $this->visibleForList()->with('cover'); + } + + public function recentlyViewedForCurrentUser(): Builder + { + return $this->visibleForList() + ->scopes('withLastView') + ->having('last_viewed_at', '>', 0) + ->orderBy('last_viewed_at', 'desc'); + } + + public function popularForList(): Builder + { + return $this->visibleForList() + ->scopes('withViewCount') + ->having('view_count', '>', 0) + ->orderBy('view_count', 'desc'); + } +} diff --git a/app/Entities/Queries/BookshelfQueries.php b/app/Entities/Queries/BookshelfQueries.php new file mode 100644 index 00000000000..842011a8781 --- /dev/null +++ b/app/Entities/Queries/BookshelfQueries.php @@ -0,0 +1,83 @@ + + */ +class BookshelfQueries implements ProvidesEntityQueries +{ + protected static array $listAttributes = [ + 'id', 'slug', 'name', 'description', + 'created_at', 'updated_at', 'image_id', 'owned_by', + ]; + + /** + * @return Builder + */ + public function start(): Builder + { + return Bookshelf::query(); + } + + public function findVisibleById(int $id): ?Bookshelf + { + return $this->start()->scopes('visible')->find($id); + } + + public function findVisibleByIdOrFail(int $id): Bookshelf + { + $shelf = $this->findVisibleById($id); + + if (is_null($shelf)) { + throw new NotFoundException(trans('errors.bookshelf_not_found')); + } + + return $shelf; + } + + public function findVisibleBySlugOrFail(string $slug): Bookshelf + { + /** @var ?Bookshelf $shelf */ + $shelf = $this->start() + ->scopes('visible') + ->where('slug', '=', $slug) + ->first(); + + if ($shelf === null) { + throw new NotFoundException(trans('errors.bookshelf_not_found')); + } + + return $shelf; + } + + public function visibleForList(): Builder + { + return $this->start()->scopes('visible')->select(static::$listAttributes); + } + + public function visibleForListWithCover(): Builder + { + return $this->visibleForList()->with('cover'); + } + + public function recentlyViewedForCurrentUser(): Builder + { + return $this->visibleForList() + ->scopes('withLastView') + ->having('last_viewed_at', '>', 0) + ->orderBy('last_viewed_at', 'desc'); + } + + public function popularForList(): Builder + { + return $this->visibleForList() + ->scopes('withViewCount') + ->having('view_count', '>', 0) + ->orderBy('view_count', 'desc'); + } +} diff --git a/app/Entities/Queries/ChapterQueries.php b/app/Entities/Queries/ChapterQueries.php new file mode 100644 index 00000000000..9bf0ff65bfd --- /dev/null +++ b/app/Entities/Queries/ChapterQueries.php @@ -0,0 +1,72 @@ + + */ +class ChapterQueries implements ProvidesEntityQueries +{ + protected static array $listAttributes = [ + 'id', 'slug', 'name', 'description', 'priority', + 'book_id', 'created_at', 'updated_at', 'owned_by', + ]; + + public function start(): Builder + { + return Chapter::query(); + } + + public function findVisibleById(int $id): ?Chapter + { + return $this->start()->scopes('visible')->find($id); + } + + public function findVisibleByIdOrFail(int $id): Chapter + { + return $this->start()->scopes('visible')->findOrFail($id); + } + + public function findVisibleBySlugsOrFail(string $bookSlug, string $chapterSlug): Chapter + { + /** @var ?Chapter $chapter */ + $chapter = $this->start() + ->scopes('visible') + ->with('book') + ->whereHas('book', function (Builder $query) use ($bookSlug) { + $query->where('slug', '=', $bookSlug); + }) + ->where('slug', '=', $chapterSlug) + ->first(); + + if (is_null($chapter)) { + throw new NotFoundException(trans('errors.chapter_not_found')); + } + + return $chapter; + } + + public function usingSlugs(string $bookSlug, string $chapterSlug): Builder + { + return $this->start() + ->where('slug', '=', $chapterSlug) + ->whereHas('book', function (Builder $query) use ($bookSlug) { + $query->where('slug', '=', $bookSlug); + }); + } + + public function visibleForList(): Builder + { + return $this->start() + ->scopes('visible') + ->select(array_merge(static::$listAttributes, ['book_slug' => function ($builder) { + $builder->select('slug') + ->from('books') + ->whereColumn('books.id', '=', 'chapters.book_id'); + }])); + } +} diff --git a/app/Entities/Queries/EntityQueries.php b/app/Entities/Queries/EntityQueries.php new file mode 100644 index 00000000000..0d2cd7acf64 --- /dev/null +++ b/app/Entities/Queries/EntityQueries.php @@ -0,0 +1,62 @@ +getQueriesForType($entityType); + + return $queries->findVisibleById($entityId); + } + + /** + * Start a query of visible entities of the given type, + * suitable for listing display. + * @return Builder + */ + public function visibleForList(string $entityType): Builder + { + $queries = $this->getQueriesForType($entityType); + return $queries->visibleForList(); + } + + protected function getQueriesForType(string $type): ProvidesEntityQueries + { + $queries = match ($type) { + 'page' => $this->pages, + 'chapter' => $this->chapters, + 'book' => $this->books, + 'bookshelf' => $this->shelves, + default => null, + }; + + if (is_null($queries)) { + throw new InvalidArgumentException("No entity query class configured for {$type}"); + } + + return $queries; + } +} diff --git a/app/Entities/Queries/EntityQuery.php b/app/Entities/Queries/EntityQuery.php deleted file mode 100644 index 2246e13b179..00000000000 --- a/app/Entities/Queries/EntityQuery.php +++ /dev/null @@ -1,19 +0,0 @@ -make(PermissionApplicator::class); - } - - protected function entityProvider(): EntityProvider - { - return app()->make(EntityProvider::class); - } -} diff --git a/app/Entities/Queries/PageQueries.php b/app/Entities/Queries/PageQueries.php new file mode 100644 index 00000000000..ee7b201bc1a --- /dev/null +++ b/app/Entities/Queries/PageQueries.php @@ -0,0 +1,121 @@ + + */ +class PageQueries implements ProvidesEntityQueries +{ + protected static array $contentAttributes = [ + 'name', 'id', 'slug', 'book_id', 'chapter_id', 'draft', + 'template', 'html', 'text', 'created_at', 'updated_at', 'priority', + 'created_by', 'updated_by', 'owned_by', + ]; + protected static array $listAttributes = [ + 'name', 'id', 'slug', 'book_id', 'chapter_id', 'draft', + 'template', 'text', 'created_at', 'updated_at', 'priority', 'owned_by', + ]; + + /** + * @return Builder + */ + public function start(): Builder + { + return Page::query(); + } + + public function findVisibleById(int $id): ?Page + { + return $this->start()->scopes('visible')->find($id); + } + + public function findVisibleByIdOrFail(int $id): Page + { + $page = $this->findVisibleById($id); + + if (is_null($page)) { + throw new NotFoundException(trans('errors.page_not_found')); + } + + return $page; + } + + public function findVisibleBySlugsOrFail(string $bookSlug, string $pageSlug): Page + { + /** @var ?Page $page */ + $page = $this->start()->with('book') + ->scopes('visible') + ->whereHas('book', function (Builder $query) use ($bookSlug) { + $query->where('slug', '=', $bookSlug); + }) + ->where('slug', '=', $pageSlug) + ->first(); + + if (is_null($page)) { + throw new NotFoundException(trans('errors.page_not_found')); + } + + return $page; + } + + public function usingSlugs(string $bookSlug, string $pageSlug): Builder + { + return $this->start() + ->where('slug', '=', $pageSlug) + ->whereHas('book', function (Builder $query) use ($bookSlug) { + $query->where('slug', '=', $bookSlug); + }); + } + + /** + * @return Builder + */ + public function visibleForList(): Builder + { + return $this->start() + ->scopes('visible') + ->select($this->mergeBookSlugForSelect(static::$listAttributes)); + } + + public function visibleForChapterList(int $chapterId): Builder + { + return $this->visibleForList() + ->where('chapter_id', '=', $chapterId) + ->orderBy('draft', 'desc') + ->orderBy('priority', 'asc'); + } + + public function visibleWithContents(): Builder + { + return $this->start() + ->scopes('visible') + ->select($this->mergeBookSlugForSelect(static::$contentAttributes)); + } + + public function currentUserDraftsForList(): Builder + { + return $this->visibleForList() + ->where('draft', '=', true) + ->where('created_by', '=', user()->id); + } + + public function visibleTemplates(): Builder + { + return $this->visibleForList() + ->where('template', '=', true); + } + + protected function mergeBookSlugForSelect(array $columns): array + { + return array_merge($columns, ['book_slug' => function ($builder) { + $builder->select('slug') + ->from('books') + ->whereColumn('books.id', '=', 'pages.book_id'); + }]); + } +} diff --git a/app/Entities/Queries/PageRevisionQueries.php b/app/Entities/Queries/PageRevisionQueries.php new file mode 100644 index 00000000000..6e017a742dc --- /dev/null +++ b/app/Entities/Queries/PageRevisionQueries.php @@ -0,0 +1,44 @@ +whereHas('page', function (Builder $query) { + $query->scopes('visible'); + }) + ->where('slug', '=', $pageSlug) + ->where('type', '=', 'version') + ->where('book_slug', '=', $bookSlug) + ->orderBy('created_at', 'desc') + ->first(); + } + + public function findLatestCurrentUserDraftsForPageId(int $pageId): ?PageRevision + { + /** @var ?PageRevision $revision */ + $revision = $this->latestCurrentUserDraftsForPageId($pageId)->first(); + + return $revision; + } + + public function latestCurrentUserDraftsForPageId(int $pageId): Builder + { + return $this->start() + ->where('created_by', '=', user()->id) + ->where('type', 'update_draft') + ->where('page_id', '=', $pageId) + ->orderBy('created_at', 'desc'); + } +} diff --git a/app/Entities/Queries/Popular.php b/app/Entities/Queries/Popular.php deleted file mode 100644 index a934f346b95..00000000000 --- a/app/Entities/Queries/Popular.php +++ /dev/null @@ -1,46 +0,0 @@ -permissionService() - ->restrictEntityRelationQuery(View::query(), 'views', 'viewable_id', 'viewable_type') - ->select('*', 'viewable_id', 'viewable_type', DB::raw('SUM(views) as view_count')) - ->groupBy('viewable_id', 'viewable_type') - ->orderBy('view_count', 'desc'); - - if ($filterModels) { - $query->whereIn('viewable_type', $this->entityProvider()->getMorphClasses($filterModels)); - } - - $entities = $query->with('viewable') - ->skip($count * ($page - 1)) - ->take($count) - ->get() - ->pluck('viewable') - ->filter(); - - $this->loadBooksForChildren($entities); - - return $entities; - } - - protected function loadBooksForChildren(Collection $entities) - { - $bookChildren = $entities->filter(fn(Entity $entity) => $entity instanceof BookChild); - $eloquent = (new \Illuminate\Database\Eloquent\Collection($bookChildren)); - $eloquent->load(['book' => function (BelongsTo $query) { - $query->scopes('visible'); - }]); - } -} diff --git a/app/Entities/Queries/ProvidesEntityQueries.php b/app/Entities/Queries/ProvidesEntityQueries.php new file mode 100644 index 00000000000..79fc64b3ab8 --- /dev/null +++ b/app/Entities/Queries/ProvidesEntityQueries.php @@ -0,0 +1,38 @@ + + */ + public function start(): Builder; + + /** + * Find the entity of the given ID or return null if not found. + */ + public function findVisibleById(int $id): ?Entity; + + /** + * Start a query for items that are visible, with selection + * configured for list display of this item. + * @return Builder + */ + public function visibleForList(): Builder; +} diff --git a/app/Entities/Queries/QueryPopular.php b/app/Entities/Queries/QueryPopular.php new file mode 100644 index 00000000000..065ae82ef82 --- /dev/null +++ b/app/Entities/Queries/QueryPopular.php @@ -0,0 +1,42 @@ +permissions + ->restrictEntityRelationQuery(View::query(), 'views', 'viewable_id', 'viewable_type') + ->select('*', 'viewable_id', 'viewable_type', DB::raw('SUM(views) as view_count')) + ->groupBy('viewable_id', 'viewable_type') + ->orderBy('view_count', 'desc'); + + if (!empty($filterModels)) { + $query->whereIn('viewable_type', $this->entityProvider->getMorphClasses($filterModels)); + } + + $views = $query + ->skip($count * ($page - 1)) + ->take($count) + ->get(); + + $this->listLoader->loadIntoRelations($views->all(), 'viewable', true); + + return $views->pluck('viewable')->filter(); + } +} diff --git a/app/Entities/Queries/QueryRecentlyViewed.php b/app/Entities/Queries/QueryRecentlyViewed.php new file mode 100644 index 00000000000..f28b8f8652f --- /dev/null +++ b/app/Entities/Queries/QueryRecentlyViewed.php @@ -0,0 +1,43 @@ +isGuest()) { + return collect(); + } + + $query = $this->permissions->restrictEntityRelationQuery( + View::query(), + 'views', + 'viewable_id', + 'viewable_type' + ) + ->orderBy('views.updated_at', 'desc') + ->where('user_id', '=', user()->id); + + $views = $query + ->skip(($page - 1) * $count) + ->take($count) + ->get(); + + $this->listLoader->loadIntoRelations($views->all(), 'viewable', false); + + return $views->pluck('viewable')->filter(); + } +} diff --git a/app/Entities/Queries/TopFavourites.php b/app/Entities/Queries/QueryTopFavourites.php similarity index 63% rename from app/Entities/Queries/TopFavourites.php rename to app/Entities/Queries/QueryTopFavourites.php index a2f8d9ea1d2..6340e35ef18 100644 --- a/app/Entities/Queries/TopFavourites.php +++ b/app/Entities/Queries/QueryTopFavourites.php @@ -3,10 +3,18 @@ namespace BookStack\Entities\Queries; use BookStack\Activity\Models\Favourite; +use BookStack\Entities\Tools\MixedEntityListLoader; +use BookStack\Permissions\PermissionApplicator; use Illuminate\Database\Query\JoinClause; -class TopFavourites extends EntityQuery +class QueryTopFavourites { + public function __construct( + protected PermissionApplicator $permissions, + protected MixedEntityListLoader $listLoader, + ) { + } + public function run(int $count, int $skip = 0) { $user = user(); @@ -14,7 +22,7 @@ public function run(int $count, int $skip = 0) return collect(); } - $query = $this->permissionService() + $query = $this->permissions ->restrictEntityRelationQuery(Favourite::query(), 'favourites', 'favouritable_id', 'favouritable_type') ->select('favourites.*') ->leftJoin('views', function (JoinClause $join) { @@ -25,11 +33,13 @@ public function run(int $count, int $skip = 0) ->orderBy('views.views', 'desc') ->where('favourites.user_id', '=', user()->id); - return $query->with('favouritable') + $favourites = $query ->skip($skip) ->take($count) - ->get() - ->pluck('favouritable') - ->filter(); + ->get(); + + $this->listLoader->loadIntoRelations($favourites->all(), 'favouritable', false); + + return $favourites->pluck('favouritable')->filter(); } } diff --git a/app/Entities/Queries/RecentlyViewed.php b/app/Entities/Queries/RecentlyViewed.php deleted file mode 100644 index 5895b97a2ae..00000000000 --- a/app/Entities/Queries/RecentlyViewed.php +++ /dev/null @@ -1,33 +0,0 @@ -isGuest()) { - return collect(); - } - - $query = $this->permissionService()->restrictEntityRelationQuery( - View::query(), - 'views', - 'viewable_id', - 'viewable_type' - ) - ->orderBy('views.updated_at', 'desc') - ->where('user_id', '=', user()->id); - - return $query->with('viewable') - ->skip(($page - 1) * $count) - ->take($count) - ->get() - ->pluck('viewable') - ->filter(); - } -} diff --git a/app/Entities/Repos/BaseRepo.php b/app/Entities/Repos/BaseRepo.php index 27bf0016186..bfc01a58d1d 100644 --- a/app/Entities/Repos/BaseRepo.php +++ b/app/Entities/Repos/BaseRepo.php @@ -3,12 +3,18 @@ namespace BookStack\Entities\Repos; use BookStack\Activity\TagRepo; +use BookStack\Entities\Models\Book; +use BookStack\Entities\Models\BookChild; +use BookStack\Entities\Models\Chapter; use BookStack\Entities\Models\Entity; -use BookStack\Entities\Models\HasCoverImage; -use BookStack\Entities\Models\HasHtmlDescription; +use BookStack\Entities\Models\CoverImageInterface; +use BookStack\Entities\Models\HtmlDescriptionInterface; +use BookStack\Entities\Models\HtmlDescriptionTrait; +use BookStack\Entities\Queries\PageQueries; use BookStack\Exceptions\ImageUploadException; use BookStack\References\ReferenceStore; use BookStack\References\ReferenceUpdater; +use BookStack\Sorting\BookSorter; use BookStack\Uploads\ImageRepo; use BookStack\Util\HtmlDescriptionFilter; use Illuminate\Http\UploadedFile; @@ -20,6 +26,8 @@ public function __construct( protected ImageRepo $imageRepo, protected ReferenceUpdater $referenceUpdater, protected ReferenceStore $referenceStore, + protected PageQueries $pageQueries, + protected BookSorter $bookSorter, ) { } @@ -70,7 +78,6 @@ public function update(Entity $entity, array $input) $entity->touch(); } - $entity->rebuildPermissions(); $entity->indexForSearch(); $this->referenceStore->updateForEntity($entity); @@ -82,12 +89,10 @@ public function update(Entity $entity, array $input) /** * Update the given items' cover image, or clear it. * - * @param Entity&HasCoverImage $entity - * * @throws ImageUploadException * @throws \Exception */ - public function updateCoverImage($entity, ?UploadedFile $coverImage, bool $removeImage = false) + public function updateCoverImage(Entity&CoverImageInterface $entity, ?UploadedFile $coverImage, bool $removeImage = false) { if ($coverImage) { $imageType = $entity->coverImageTypeKey(); @@ -99,25 +104,62 @@ public function updateCoverImage($entity, ?UploadedFile $coverImage, bool $remov if ($removeImage) { $this->imageRepo->destroyImage($entity->cover()->first()); - $entity->image_id = 0; + $entity->cover()->dissociate(); + $entity->save(); + } + } + + /** + * Update the default page template used for this item. + * Checks that, if changing, the provided value is a valid template and the user + * has visibility of the provided page template id. + */ + public function updateDefaultTemplate(Book|Chapter $entity, int $templateId): void + { + $changing = $templateId !== intval($entity->default_template_id); + if (!$changing) { + return; + } + + if ($templateId === 0) { + $entity->default_template_id = null; $entity->save(); + return; + } + + $templateExists = $this->pageQueries->visibleTemplates() + ->where('id', '=', $templateId) + ->exists(); + + $entity->default_template_id = $templateExists ? $templateId : null; + $entity->save(); + } + + /** + * Sort the parent of the given entity, if any auto sort actions are set for it. + * Typically ran during create/update/insert events. + */ + public function sortParent(Entity $entity): void + { + if ($entity instanceof BookChild) { + $book = $entity->book; + $this->bookSorter->runBookAutoSort($book); } } protected function updateDescription(Entity $entity, array $input): void { - if (!in_array(HasHtmlDescription::class, class_uses($entity))) { + if (!($entity instanceof HtmlDescriptionInterface)) { return; } - /** @var HasHtmlDescription $entity */ if (isset($input['description_html'])) { - $entity->description_html = HtmlDescriptionFilter::filterFromString($input['description_html']); - $entity->description = html_entity_decode(strip_tags($input['description_html'])); + $entity->setDescriptionHtml( + HtmlDescriptionFilter::filterFromString($input['description_html']), + html_entity_decode(strip_tags($input['description_html'])) + ); } else if (isset($input['description'])) { - $entity->description = $input['description']; - $entity->description_html = ''; - $entity->description_html = $entity->descriptionHtml(); + $entity->setDescriptionHtml('', $input['description']); } } } diff --git a/app/Entities/Repos/BookRepo.php b/app/Entities/Repos/BookRepo.php index 03e1118b122..6d28d5d6aab 100644 --- a/app/Entities/Repos/BookRepo.php +++ b/app/Entities/Repos/BookRepo.php @@ -5,91 +5,46 @@ use BookStack\Activity\ActivityType; use BookStack\Activity\TagRepo; use BookStack\Entities\Models\Book; -use BookStack\Entities\Models\Page; use BookStack\Entities\Tools\TrashCan; use BookStack\Exceptions\ImageUploadException; -use BookStack\Exceptions\NotFoundException; use BookStack\Facades\Activity; +use BookStack\Sorting\SortRule; use BookStack\Uploads\ImageRepo; +use BookStack\Util\DatabaseTransaction; use Exception; -use Illuminate\Contracts\Pagination\LengthAwarePaginator; use Illuminate\Http\UploadedFile; -use Illuminate\Support\Collection; class BookRepo { public function __construct( protected BaseRepo $baseRepo, protected TagRepo $tagRepo, - protected ImageRepo $imageRepo + protected ImageRepo $imageRepo, + protected TrashCan $trashCan, ) { } - /** - * Get all books in a paginated format. - */ - public function getAllPaginated(int $count = 20, string $sort = 'name', string $order = 'asc'): LengthAwarePaginator - { - return Book::visible()->with('cover')->orderBy($sort, $order)->paginate($count); - } - - /** - * Get the books that were most recently viewed by this user. - */ - public function getRecentlyViewed(int $count = 20): Collection - { - return Book::visible()->withLastView() - ->having('last_viewed_at', '>', 0) - ->orderBy('last_viewed_at', 'desc') - ->take($count)->get(); - } - - /** - * Get the most popular books in the system. - */ - public function getPopular(int $count = 20): Collection - { - return Book::visible()->withViewCount() - ->having('view_count', '>', 0) - ->orderBy('view_count', 'desc') - ->take($count)->get(); - } - - /** - * Get the most recently created books from the system. - */ - public function getRecentlyCreated(int $count = 20): Collection - { - return Book::visible()->orderBy('created_at', 'desc') - ->take($count)->get(); - } - - /** - * Get a book by its slug. - */ - public function getBySlug(string $slug): Book - { - $book = Book::visible()->where('slug', '=', $slug)->first(); - - if ($book === null) { - throw new NotFoundException(trans('errors.book_not_found')); - } - - return $book; - } - /** * Create a new book in the system. */ public function create(array $input): Book { - $book = new Book(); - $this->baseRepo->create($book, $input); - $this->baseRepo->updateCoverImage($book, $input['image'] ?? null); - $this->updateBookDefaultTemplate($book, intval($input['default_template_id'] ?? null)); - Activity::add(ActivityType::BOOK_CREATE, $book); - - return $book; + return (new DatabaseTransaction(function () use ($input) { + $book = new Book(); + + $this->baseRepo->create($book, $input); + $this->baseRepo->updateCoverImage($book, $input['image'] ?? null); + $this->baseRepo->updateDefaultTemplate($book, intval($input['default_template_id'] ?? null)); + Activity::add(ActivityType::BOOK_CREATE, $book); + + $defaultBookSortSetting = intval(setting('sorting-book-default', '0')); + if ($defaultBookSortSetting && SortRule::query()->find($defaultBookSortSetting)) { + $book->sort_rule_id = $defaultBookSortSetting; + $book->save(); + } + + return $book; + }))->run(); } /** @@ -100,7 +55,7 @@ public function update(Book $book, array $input): Book $this->baseRepo->update($book, $input); if (array_key_exists('default_template_id', $input)) { - $this->updateBookDefaultTemplate($book, intval($input['default_template_id'])); + $this->baseRepo->updateDefaultTemplate($book, intval($input['default_template_id'])); } if (array_key_exists('image', $input)) { @@ -112,33 +67,6 @@ public function update(Book $book, array $input): Book return $book; } - /** - * Update the default page template used for this book. - * Checks that, if changing, the provided value is a valid template and the user - * has visibility of the provided page template id. - */ - protected function updateBookDefaultTemplate(Book $book, int $templateId): void - { - $changing = $templateId !== intval($book->default_template_id); - if (!$changing) { - return; - } - - if ($templateId === 0) { - $book->default_template_id = null; - $book->save(); - return; - } - - $templateExists = Page::query()->visible() - ->where('template', '=', true) - ->where('id', '=', $templateId) - ->exists(); - - $book->default_template_id = $templateExists ? $templateId : null; - $book->save(); - } - /** * Update the given book's cover image, or clear it. * @@ -157,10 +85,9 @@ public function updateCoverImage(Book $book, ?UploadedFile $coverImage, bool $re */ public function destroy(Book $book) { - $trashCan = new TrashCan(); - $trashCan->softDestroyBook($book); + $this->trashCan->softDestroyBook($book); Activity::add(ActivityType::BOOK_DELETE, $book); - $trashCan->autoClearOld(); + $this->trashCan->autoClearOld(); } } diff --git a/app/Entities/Repos/BookshelfRepo.php b/app/Entities/Repos/BookshelfRepo.php index 27333b5b15c..b870ec37747 100644 --- a/app/Entities/Repos/BookshelfRepo.php +++ b/app/Entities/Repos/BookshelfRepo.php @@ -3,81 +3,20 @@ namespace BookStack\Entities\Repos; use BookStack\Activity\ActivityType; -use BookStack\Entities\Models\Book; use BookStack\Entities\Models\Bookshelf; +use BookStack\Entities\Queries\BookQueries; use BookStack\Entities\Tools\TrashCan; -use BookStack\Exceptions\NotFoundException; use BookStack\Facades\Activity; +use BookStack\Util\DatabaseTransaction; use Exception; -use Illuminate\Contracts\Pagination\LengthAwarePaginator; -use Illuminate\Support\Collection; class BookshelfRepo { - protected $baseRepo; - - /** - * BookshelfRepo constructor. - */ - public function __construct(BaseRepo $baseRepo) - { - $this->baseRepo = $baseRepo; - } - - /** - * Get all bookshelves in a paginated format. - */ - public function getAllPaginated(int $count = 20, string $sort = 'name', string $order = 'asc'): LengthAwarePaginator - { - return Bookshelf::visible() - ->with(['visibleBooks', 'cover']) - ->orderBy($sort, $order) - ->paginate($count); - } - - /** - * Get the bookshelves that were most recently viewed by this user. - */ - public function getRecentlyViewed(int $count = 20): Collection - { - return Bookshelf::visible()->withLastView() - ->having('last_viewed_at', '>', 0) - ->orderBy('last_viewed_at', 'desc') - ->take($count)->get(); - } - - /** - * Get the most popular bookshelves in the system. - */ - public function getPopular(int $count = 20): Collection - { - return Bookshelf::visible()->withViewCount() - ->having('view_count', '>', 0) - ->orderBy('view_count', 'desc') - ->take($count)->get(); - } - - /** - * Get the most recently created bookshelves from the system. - */ - public function getRecentlyCreated(int $count = 20): Collection - { - return Bookshelf::visible()->orderBy('created_at', 'desc') - ->take($count)->get(); - } - - /** - * Get a shelf by its slug. - */ - public function getBySlug(string $slug): Bookshelf - { - $shelf = Bookshelf::visible()->where('slug', '=', $slug)->first(); - - if ($shelf === null) { - throw new NotFoundException(trans('errors.bookshelf_not_found')); - } - - return $shelf; + public function __construct( + protected BaseRepo $baseRepo, + protected BookQueries $bookQueries, + protected TrashCan $trashCan, + ) { } /** @@ -85,13 +24,14 @@ public function getBySlug(string $slug): Bookshelf */ public function create(array $input, array $bookIds): Bookshelf { - $shelf = new Bookshelf(); - $this->baseRepo->create($shelf, $input); - $this->baseRepo->updateCoverImage($shelf, $input['image'] ?? null); - $this->updateBooks($shelf, $bookIds); - Activity::add(ActivityType::BOOKSHELF_CREATE, $shelf); - - return $shelf; + return (new DatabaseTransaction(function () use ($input, $bookIds) { + $shelf = new Bookshelf(); + $this->baseRepo->create($shelf, $input); + $this->baseRepo->updateCoverImage($shelf, $input['image'] ?? null); + $this->updateBooks($shelf, $bookIds); + Activity::add(ActivityType::BOOKSHELF_CREATE, $shelf); + return $shelf; + }))->run(); } /** @@ -116,20 +56,37 @@ public function update(Bookshelf $shelf, array $input, ?array $bookIds): Bookshe /** * Update which books are assigned to this shelf by syncing the given book ids. - * Function ensures the books are visible to the current user and existing. + * Function ensures the managed books are visible to the current user and existing, + * and that the user does not alter the assignment of books that are not visible to them. */ - protected function updateBooks(Bookshelf $shelf, array $bookIds) + protected function updateBooks(Bookshelf $shelf, array $bookIds): void { $numericIDs = collect($bookIds)->map(function ($id) { return intval($id); }); - $syncData = Book::visible() + $existingBookIds = $shelf->books()->pluck('id')->toArray(); + $visibleExistingBookIds = $this->bookQueries->visibleForList() + ->whereIn('id', $existingBookIds) + ->pluck('id') + ->toArray(); + $nonVisibleExistingBookIds = array_values(array_diff($existingBookIds, $visibleExistingBookIds)); + + $newIdsToAssign = $this->bookQueries->visibleForList() ->whereIn('id', $bookIds) ->pluck('id') - ->mapWithKeys(function ($bookId) use ($numericIDs) { - return [$bookId => ['order' => $numericIDs->search($bookId)]]; - }); + ->toArray(); + + $maxNewIndex = max($numericIDs->keys()->toArray() ?: [0]); + + $syncData = []; + foreach ($newIdsToAssign as $id) { + $syncData[$id] = ['order' => $numericIDs->search($id)]; + } + + foreach ($nonVisibleExistingBookIds as $index => $id) { + $syncData[$id] = ['order' => $maxNewIndex + ($index + 1)]; + } $shelf->books()->sync($syncData); } @@ -141,9 +98,8 @@ protected function updateBooks(Bookshelf $shelf, array $bookIds) */ public function destroy(Bookshelf $shelf) { - $trashCan = new TrashCan(); - $trashCan->softDestroyShelf($shelf); + $this->trashCan->softDestroyShelf($shelf); Activity::add(ActivityType::BOOKSHELF_DELETE, $shelf); - $trashCan->autoClearOld(); + $this->trashCan->autoClearOld(); } } diff --git a/app/Entities/Repos/ChapterRepo.php b/app/Entities/Repos/ChapterRepo.php index 977193d85bb..5d4b5297841 100644 --- a/app/Entities/Repos/ChapterRepo.php +++ b/app/Entities/Repos/ChapterRepo.php @@ -5,50 +5,42 @@ use BookStack\Activity\ActivityType; use BookStack\Entities\Models\Book; use BookStack\Entities\Models\Chapter; -use BookStack\Entities\Models\Entity; +use BookStack\Entities\Queries\EntityQueries; use BookStack\Entities\Tools\BookContents; use BookStack\Entities\Tools\TrashCan; use BookStack\Exceptions\MoveOperationException; -use BookStack\Exceptions\NotFoundException; use BookStack\Exceptions\PermissionsException; use BookStack\Facades\Activity; +use BookStack\Permissions\Permission; +use BookStack\Util\DatabaseTransaction; use Exception; class ChapterRepo { public function __construct( - protected BaseRepo $baseRepo + protected BaseRepo $baseRepo, + protected EntityQueries $entityQueries, + protected TrashCan $trashCan, ) { } - /** - * Get a chapter via the slug. - * - * @throws NotFoundException - */ - public function getBySlug(string $bookSlug, string $chapterSlug): Chapter - { - $chapter = Chapter::visible()->whereSlugs($bookSlug, $chapterSlug)->first(); - - if ($chapter === null) { - throw new NotFoundException(trans('errors.chapter_not_found')); - } - - return $chapter; - } - /** * Create a new chapter in the system. */ public function create(array $input, Book $parentBook): Chapter { - $chapter = new Chapter(); - $chapter->book_id = $parentBook->id; - $chapter->priority = (new BookContents($parentBook))->getLastPriority() + 1; - $this->baseRepo->create($chapter, $input); - Activity::add(ActivityType::CHAPTER_CREATE, $chapter); - - return $chapter; + return (new DatabaseTransaction(function () use ($input, $parentBook) { + $chapter = new Chapter(); + $chapter->book_id = $parentBook->id; + $chapter->priority = (new BookContents($parentBook))->getLastPriority() + 1; + $this->baseRepo->create($chapter, $input); + $this->baseRepo->updateDefaultTemplate($chapter, intval($input['default_template_id'] ?? null)); + Activity::add(ActivityType::CHAPTER_CREATE, $chapter); + + $this->baseRepo->sortParent($chapter); + + return $chapter; + }))->run(); } /** @@ -57,8 +49,15 @@ public function create(array $input, Book $parentBook): Chapter public function update(Chapter $chapter, array $input): Chapter { $this->baseRepo->update($chapter, $input); + + if (array_key_exists('default_template_id', $input)) { + $this->baseRepo->updateDefaultTemplate($chapter, intval($input['default_template_id'])); + } + Activity::add(ActivityType::CHAPTER_UPDATE, $chapter); + $this->baseRepo->sortParent($chapter); + return $chapter; } @@ -69,10 +68,9 @@ public function update(Chapter $chapter, array $input): Chapter */ public function destroy(Chapter $chapter) { - $trashCan = new TrashCan(); - $trashCan->softDestroyChapter($chapter); + $this->trashCan->softDestroyChapter($chapter); Activity::add(ActivityType::CHAPTER_DELETE, $chapter); - $trashCan->autoClearOld(); + $this->trashCan->autoClearOld(); } /** @@ -85,39 +83,23 @@ public function destroy(Chapter $chapter) */ public function move(Chapter $chapter, string $parentIdentifier): Book { - $parent = $this->findParentByIdentifier($parentIdentifier); - if (is_null($parent)) { + $parent = $this->entityQueries->findVisibleByStringIdentifier($parentIdentifier); + if (!$parent instanceof Book) { throw new MoveOperationException('Book to move chapter into not found'); } - if (!userCan('chapter-create', $parent)) { + if (!userCan(Permission::ChapterCreate, $parent)) { throw new PermissionsException('User does not have permission to create a chapter within the chosen book'); } - $chapter->changeBook($parent->id); - $chapter->rebuildPermissions(); - Activity::add(ActivityType::CHAPTER_MOVE, $chapter); - - return $parent; - } - - /** - * Find a page parent entity via an identifier string in the format: - * {type}:{id} - * Example: (book:5). - * - * @throws MoveOperationException - */ - public function findParentByIdentifier(string $identifier): ?Book - { - $stringExploded = explode(':', $identifier); - $entityType = $stringExploded[0]; - $entityId = intval($stringExploded[1]); + return (new DatabaseTransaction(function () use ($chapter, $parent) { + $chapter->changeBook($parent->id); + $chapter->rebuildPermissions(); + Activity::add(ActivityType::CHAPTER_MOVE, $chapter); - if ($entityType !== 'book') { - throw new MoveOperationException('Chapters can only be in books'); - } + $this->baseRepo->sortParent($chapter); - return Book::visible()->where('id', '=', $entityId)->first(); + return $parent; + }))->run(); } } diff --git a/app/Entities/Repos/PageRepo.php b/app/Entities/Repos/PageRepo.php index 7b14ea7d278..76377f9a6dd 100644 --- a/app/Entities/Repos/PageRepo.php +++ b/app/Entities/Repos/PageRepo.php @@ -8,114 +8,32 @@ use BookStack\Entities\Models\Entity; use BookStack\Entities\Models\Page; use BookStack\Entities\Models\PageRevision; +use BookStack\Entities\Queries\EntityQueries; use BookStack\Entities\Tools\BookContents; use BookStack\Entities\Tools\PageContent; -use BookStack\Entities\Tools\PageEditorData; +use BookStack\Entities\Tools\PageEditorType; use BookStack\Entities\Tools\TrashCan; use BookStack\Exceptions\MoveOperationException; -use BookStack\Exceptions\NotFoundException; use BookStack\Exceptions\PermissionsException; use BookStack\Facades\Activity; +use BookStack\Permissions\Permission; use BookStack\References\ReferenceStore; use BookStack\References\ReferenceUpdater; +use BookStack\Util\DatabaseTransaction; use Exception; -use Illuminate\Pagination\LengthAwarePaginator; class PageRepo { public function __construct( protected BaseRepo $baseRepo, protected RevisionRepo $revisionRepo, + protected EntityQueries $entityQueries, protected ReferenceStore $referenceStore, - protected ReferenceUpdater $referenceUpdater + protected ReferenceUpdater $referenceUpdater, + protected TrashCan $trashCan, ) { } - /** - * Get a page by ID. - * - * @throws NotFoundException - */ - public function getById(int $id, array $relations = ['book']): Page - { - /** @var Page $page */ - $page = Page::visible()->with($relations)->find($id); - - if (!$page) { - throw new NotFoundException(trans('errors.page_not_found')); - } - - return $page; - } - - /** - * Get a page its book and own slug. - * - * @throws NotFoundException - */ - public function getBySlug(string $bookSlug, string $pageSlug): Page - { - $page = Page::visible()->whereSlugs($bookSlug, $pageSlug)->first(); - - if (!$page) { - throw new NotFoundException(trans('errors.page_not_found')); - } - - return $page; - } - - /** - * Get a page by its old slug but checking the revisions table - * for the last revision that matched the given page and book slug. - */ - public function getByOldSlug(string $bookSlug, string $pageSlug): ?Page - { - $revision = $this->revisionRepo->getBySlugs($bookSlug, $pageSlug); - - return $revision->page ?? null; - } - - /** - * Get pages that have been marked as a template. - */ - public function getTemplates(int $count = 10, int $page = 1, string $search = ''): LengthAwarePaginator - { - $query = Page::visible() - ->where('template', '=', true) - ->orderBy('name', 'asc') - ->skip(($page - 1) * $count) - ->take($count); - - if ($search) { - $query->where('name', 'like', '%' . $search . '%'); - } - - $paginator = $query->paginate($count, ['*'], 'page', $page); - $paginator->withPath('/templates'); - - return $paginator; - } - - /** - * Get a parent item via slugs. - */ - public function getParentFromSlugs(string $bookSlug, string $chapterSlug = null): Entity - { - if ($chapterSlug !== null) { - return Chapter::visible()->whereSlugs($bookSlug, $chapterSlug)->firstOrFail(); - } - - return Book::visible()->where('slug', '=', $bookSlug)->firstOrFail(); - } - - /** - * Get the draft copy of the given page for the current user. - */ - public function getUserDraft(Page $page): ?PageRevision - { - return $this->revisionRepo->getLatestDraftForCurrentUser($page); - } - /** * Get a new draft page belonging to the given parent entity. */ @@ -127,6 +45,7 @@ public function getNewDraftPage(Entity $parent) 'owned_by' => user()->id, 'updated_by' => user()->id, 'draft' => true, + 'editor' => PageEditorType::getSystemDefault()->value, ]); if ($parent instanceof Chapter) { @@ -136,16 +55,18 @@ public function getNewDraftPage(Entity $parent) $page->book_id = $parent->id; } - $defaultTemplate = $page->book->defaultTemplate; - if ($defaultTemplate && userCan('view', $defaultTemplate)) { + $defaultTemplate = $page->chapter->defaultTemplate ?? $page->book->defaultTemplate; + if ($defaultTemplate && userCan(Permission::PageView, $defaultTemplate)) { $page->forceFill([ 'html' => $defaultTemplate->html, 'markdown' => $defaultTemplate->markdown, ]); } - $page->save(); - $page->refresh()->rebuildPermissions(); + (new DatabaseTransaction(function () use ($page) { + $page->save(); + $page->refresh()->rebuildPermissions(); + }))->run(); return $page; } @@ -155,18 +76,34 @@ public function getNewDraftPage(Entity $parent) */ public function publishDraft(Page $draft, array $input): Page { - $draft->draft = false; - $draft->revision_count = 1; - $draft->priority = $this->getNewPriority($draft); - $this->updateTemplateStatusAndContentFromInput($draft, $input); - $this->baseRepo->update($draft, $input); - - $this->revisionRepo->storeNewForPage($draft, trans('entities.pages_initial_revision')); - $draft->refresh(); - - Activity::add(ActivityType::PAGE_CREATE, $draft); + return (new DatabaseTransaction(function () use ($draft, $input) { + $draft->draft = false; + $draft->revision_count = 1; + $draft->priority = $this->getNewPriority($draft); + $this->updateTemplateStatusAndContentFromInput($draft, $input); + $this->baseRepo->update($draft, $input); + $draft->rebuildPermissions(); + + $summary = trim($input['summary'] ?? '') ?: trans('entities.pages_initial_revision'); + $this->revisionRepo->storeNewForPage($draft, $summary); + $draft->refresh(); + + Activity::add(ActivityType::PAGE_CREATE, $draft); + $this->baseRepo->sortParent($draft); + + return $draft; + }))->run(); + } - return $draft; + /** + * Directly update the content for the given page from the provided input. + * Used for direct content access in a way that performs required changes + * (Search index and reference regen) without performing an official update. + */ + public function setContentFromInput(Page $page, array $input): void + { + $this->updateTemplateStatusAndContentFromInput($page, $input); + $this->baseRepo->update($page, []); } /** @@ -186,7 +123,7 @@ public function update(Page $page, array $input): Page $page->revision_count++; $page->save(); - // Remove all update drafts for this user & page. + // Remove all update drafts for this user and page. $this->revisionRepo->deleteDraftsForCurrentUser($page); // Save a revision after updating @@ -199,18 +136,21 @@ public function update(Page $page, array $input): Page } Activity::add(ActivityType::PAGE_UPDATE, $page); + $this->baseRepo->sortParent($page); return $page; } - protected function updateTemplateStatusAndContentFromInput(Page $page, array $input) + protected function updateTemplateStatusAndContentFromInput(Page $page, array $input): void { - if (isset($input['template']) && userCan('templates-manage')) { + if (isset($input['template']) && userCan(Permission::TemplatesManage)) { $page->template = ($input['template'] === 'true'); } $pageContent = new PageContent($page); - $currentEditor = $page->editor ?: PageEditorData::getSystemDefaultEditor(); + $defaultEditor = PageEditorType::getSystemDefault(); + $currentEditor = PageEditorType::forPage($page) ?: $defaultEditor; + $inputEditor = PageEditorType::fromRequestValue($input['editor'] ?? '') ?? $currentEditor; $newEditor = $currentEditor; $haveInput = isset($input['markdown']) || isset($input['html']); @@ -219,15 +159,17 @@ protected function updateTemplateStatusAndContentFromInput(Page $page, array $in if ($haveInput && $inputEmpty) { $pageContent->setNewHTML('', user()); } elseif (!empty($input['markdown']) && is_string($input['markdown'])) { - $newEditor = 'markdown'; + $newEditor = PageEditorType::Markdown; $pageContent->setNewMarkdown($input['markdown'], user()); } elseif (isset($input['html'])) { - $newEditor = 'wysiwyg'; + $newEditor = ($inputEditor->isHtmlBased() ? $inputEditor : null) ?? ($defaultEditor->isHtmlBased() ? $defaultEditor : null) ?? PageEditorType::WysiwygTinymce; $pageContent->setNewHTML($input['html'], user()); } - if ($newEditor !== $currentEditor && userCan('editor-change')) { - $page->editor = $newEditor; + if (($newEditor !== $currentEditor || empty($page->editor)) && userCan(Permission::EditorChange)) { + $page->editor = $newEditor->value; + } elseif (empty($page->editor)) { + $page->editor = $defaultEditor->value; } } @@ -269,10 +211,9 @@ public function updatePageDraft(Page $page, array $input) */ public function destroy(Page $page) { - $trashCan = new TrashCan(); - $trashCan->softDestroyPage($page); + $this->trashCan->softDestroyPage($page); Activity::add(ActivityType::PAGE_DELETE, $page); - $trashCan->autoClearOld(); + $this->trashCan->autoClearOld(); } /** @@ -311,6 +252,8 @@ public function restoreRevision(Page $page, int $revisionId): Page Activity::add(ActivityType::PAGE_RESTORE, $page); Activity::add(ActivityType::REVISION_RESTORE, $revision); + $this->baseRepo->sortParent($page); + return $page; } @@ -324,45 +267,27 @@ public function restoreRevision(Page $page, int $revisionId): Page */ public function move(Page $page, string $parentIdentifier): Entity { - $parent = $this->findParentByIdentifier($parentIdentifier); - if (is_null($parent)) { + $parent = $this->entityQueries->findVisibleByStringIdentifier($parentIdentifier); + if (!$parent instanceof Chapter && !$parent instanceof Book) { throw new MoveOperationException('Book or chapter to move page into not found'); } - if (!userCan('page-create', $parent)) { + if (!userCan(Permission::PageCreate, $parent)) { throw new PermissionsException('User does not have permission to create a page within the new parent'); } - $page->chapter_id = ($parent instanceof Chapter) ? $parent->id : null; - $newBookId = ($parent instanceof Chapter) ? $parent->book->id : $parent->id; - $page->changeBook($newBookId); - $page->rebuildPermissions(); - - Activity::add(ActivityType::PAGE_MOVE, $page); - - return $parent; - } - - /** - * Find a page parent entity via an identifier string in the format: - * {type}:{id} - * Example: (book:5). - * - * @throws MoveOperationException - */ - public function findParentByIdentifier(string $identifier): ?Entity - { - $stringExploded = explode(':', $identifier); - $entityType = $stringExploded[0]; - $entityId = intval($stringExploded[1]); + return (new DatabaseTransaction(function () use ($page, $parent) { + $page->chapter_id = ($parent instanceof Chapter) ? $parent->id : null; + $newBookId = ($parent instanceof Chapter) ? $parent->book->id : $parent->id; + $page->changeBook($newBookId); + $page->rebuildPermissions(); - if ($entityType !== 'book' && $entityType !== 'chapter') { - throw new MoveOperationException('Pages can only be in books or chapters'); - } + Activity::add(ActivityType::PAGE_MOVE, $page); - $parentClass = $entityType === 'book' ? Book::class : Chapter::class; + $this->baseRepo->sortParent($page); - return $parentClass::visible()->where('id', '=', $entityId)->first(); + return $parent; + }))->run(); } /** diff --git a/app/Entities/Repos/RevisionRepo.php b/app/Entities/Repos/RevisionRepo.php index 064327ee945..d5549a0f14a 100644 --- a/app/Entities/Repos/RevisionRepo.php +++ b/app/Entities/Repos/RevisionRepo.php @@ -4,39 +4,13 @@ use BookStack\Entities\Models\Page; use BookStack\Entities\Models\PageRevision; -use Illuminate\Database\Eloquent\Builder; +use BookStack\Entities\Queries\PageRevisionQueries; class RevisionRepo { - /** - * Get a revision by its stored book and page slug values. - */ - public function getBySlugs(string $bookSlug, string $pageSlug): ?PageRevision - { - /** @var ?PageRevision $revision */ - $revision = PageRevision::query() - ->whereHas('page', function (Builder $query) { - $query->scopes('visible'); - }) - ->where('slug', '=', $pageSlug) - ->where('type', '=', 'version') - ->where('book_slug', '=', $bookSlug) - ->orderBy('created_at', 'desc') - ->with('page') - ->first(); - - return $revision; - } - - /** - * Get the latest draft revision, for the given page, belonging to the current user. - */ - public function getLatestDraftForCurrentUser(Page $page): ?PageRevision - { - /** @var ?PageRevision $revision */ - $revision = $this->queryForCurrentUserDraft($page->id)->first(); - - return $revision; + public function __construct( + protected PageRevisionQueries $queries, + ) { } /** @@ -44,7 +18,7 @@ public function getLatestDraftForCurrentUser(Page $page): ?PageRevision */ public function deleteDraftsForCurrentUser(Page $page): void { - $this->queryForCurrentUserDraft($page->id)->delete(); + $this->queries->latestCurrentUserDraftsForPageId($page->id)->delete(); } /** @@ -53,7 +27,7 @@ public function deleteDraftsForCurrentUser(Page $page): void */ public function getNewDraftForCurrentUser(Page $page): PageRevision { - $draft = $this->getLatestDraftForCurrentUser($page); + $draft = $this->queries->findLatestCurrentUserDraftsForPageId($page->id); if ($draft) { return $draft; @@ -72,7 +46,7 @@ public function getNewDraftForCurrentUser(Page $page): PageRevision /** * Store a new revision in the system for the given page. */ - public function storeNewForPage(Page $page, string $summary = null): PageRevision + public function storeNewForPage(Page $page, ?string $summary = null): PageRevision { $revision = new PageRevision(); @@ -116,16 +90,4 @@ protected function deleteOldRevisions(Page $page) PageRevision::query()->whereIn('id', $revisionsToDelete->pluck('id'))->delete(); } } - - /** - * Query update draft revisions for the current user. - */ - protected function queryForCurrentUserDraft(int $pageId): Builder - { - return PageRevision::query() - ->where('created_by', '=', user()->id) - ->where('type', 'update_draft') - ->where('page_id', '=', $pageId) - ->orderBy('created_at', 'desc'); - } } diff --git a/app/Entities/Tools/BookContents.php b/app/Entities/Tools/BookContents.php index f45bdfcc1b9..7dd3f3e11ad 100644 --- a/app/Entities/Tools/BookContents.php +++ b/app/Entities/Tools/BookContents.php @@ -7,15 +7,19 @@ use BookStack\Entities\Models\Chapter; use BookStack\Entities\Models\Entity; use BookStack\Entities\Models\Page; +use BookStack\Entities\Queries\EntityQueries; +use BookStack\Sorting\BookSortMap; +use BookStack\Sorting\BookSortMapItem; use Illuminate\Support\Collection; class BookContents { - protected Book $book; + protected EntityQueries $queries; - public function __construct(Book $book) - { - $this->book = $book; + public function __construct( + protected Book $book, + ) { + $this->queries = app()->make(EntityQueries::class); } /** @@ -23,10 +27,12 @@ public function __construct(Book $book) */ public function getLastPriority(): int { - $maxPage = Page::visible()->where('book_id', '=', $this->book->id) + $maxPage = $this->book->pages() ->where('draft', '=', false) - ->where('chapter_id', '=', 0)->max('priority'); - $maxChapter = Chapter::visible()->where('book_id', '=', $this->book->id) + ->where('chapter_id', '=', 0) + ->max('priority'); + + $maxChapter = $this->book->chapters() ->max('priority'); return max($maxChapter, $maxPage, 1); @@ -38,7 +44,7 @@ public function getLastPriority(): int public function getTree(bool $showDrafts = false, bool $renderPages = false): Collection { $pages = $this->getPages($showDrafts, $renderPages); - $chapters = Chapter::visible()->where('book_id', '=', $this->book->id)->get(); + $chapters = $this->book->chapters()->scopes('visible')->get(); $all = collect()->concat($pages)->concat($chapters); $chapterMap = $chapters->keyBy('id'); $lonePages = collect(); @@ -87,221 +93,16 @@ protected function bookChildSortFunc(): callable */ protected function getPages(bool $showDrafts = false, bool $getPageContent = false): Collection { - $query = Page::visible() - ->select($getPageContent ? Page::$contentAttributes : Page::$listAttributes) - ->where('book_id', '=', $this->book->id); + if ($getPageContent) { + $query = $this->queries->pages->visibleWithContents(); + } else { + $query = $this->queries->pages->visibleForList(); + } if (!$showDrafts) { $query->where('draft', '=', false); } - return $query->get(); - } - - /** - * Sort the books content using the given sort map. - * Returns a list of books that were involved in the operation. - * - * @returns Book[] - */ - public function sortUsingMap(BookSortMap $sortMap): array - { - // Load models into map - $modelMap = $this->loadModelsFromSortMap($sortMap); - - // Sort our changes from our map to be chapters first - // Since they need to be process to ensure book alignment for child page changes. - $sortMapItems = $sortMap->all(); - usort($sortMapItems, function (BookSortMapItem $itemA, BookSortMapItem $itemB) { - $aScore = $itemA->type === 'page' ? 2 : 1; - $bScore = $itemB->type === 'page' ? 2 : 1; - - return $aScore - $bScore; - }); - - // Perform the sort - foreach ($sortMapItems as $item) { - $this->applySortUpdates($item, $modelMap); - } - - /** @var Book[] $booksInvolved */ - $booksInvolved = array_values(array_filter($modelMap, function (string $key) { - return strpos($key, 'book:') === 0; - }, ARRAY_FILTER_USE_KEY)); - - // Update permissions of books involved - foreach ($booksInvolved as $book) { - $book->rebuildPermissions(); - } - - return $booksInvolved; - } - - /** - * Using the given sort map item, detect changes for the related model - * and update it if required. Changes where permissions are lacking will - * be skipped and not throw an error. - * - * @param array $modelMap - */ - protected function applySortUpdates(BookSortMapItem $sortMapItem, array $modelMap): void - { - /** @var BookChild $model */ - $model = $modelMap[$sortMapItem->type . ':' . $sortMapItem->id] ?? null; - if (!$model) { - return; - } - - $priorityChanged = $model->priority !== $sortMapItem->sort; - $bookChanged = $model->book_id !== $sortMapItem->parentBookId; - $chapterChanged = ($model instanceof Page) && $model->chapter_id !== $sortMapItem->parentChapterId; - - // Stop if there's no change - if (!$priorityChanged && !$bookChanged && !$chapterChanged) { - return; - } - - $currentParentKey = 'book:' . $model->book_id; - if ($model instanceof Page && $model->chapter_id) { - $currentParentKey = 'chapter:' . $model->chapter_id; - } - - $currentParent = $modelMap[$currentParentKey] ?? null; - /** @var Book $newBook */ - $newBook = $modelMap['book:' . $sortMapItem->parentBookId] ?? null; - /** @var ?Chapter $newChapter */ - $newChapter = $sortMapItem->parentChapterId ? ($modelMap['chapter:' . $sortMapItem->parentChapterId] ?? null) : null; - - if (!$this->isSortChangePermissible($sortMapItem, $model, $currentParent, $newBook, $newChapter)) { - return; - } - - // Action the required changes - if ($bookChanged) { - $model->changeBook($newBook->id); - } - - if ($model instanceof Page && $chapterChanged) { - $model->chapter_id = $newChapter->id ?? 0; - } - - if ($priorityChanged) { - $model->priority = $sortMapItem->sort; - } - - if ($chapterChanged || $priorityChanged) { - $model->save(); - } - } - - /** - * Check if the current user has permissions to apply the given sorting change. - * Is quite complex since items can gain a different parent change. Acts as a: - * - Update of old parent element (Change of content/order). - * - Update of sorted/moved element. - * - Deletion of element (Relative to parent upon move). - * - Creation of element within parent (Upon move to new parent). - */ - protected function isSortChangePermissible(BookSortMapItem $sortMapItem, BookChild $model, ?Entity $currentParent, ?Entity $newBook, ?Entity $newChapter): bool - { - // Stop if we can't see the current parent or new book. - if (!$currentParent || !$newBook) { - return false; - } - - $hasNewParent = $newBook->id !== $model->book_id || ($model instanceof Page && $model->chapter_id !== ($sortMapItem->parentChapterId ?? 0)); - if ($model instanceof Chapter) { - $hasPermission = userCan('book-update', $currentParent) - && userCan('book-update', $newBook) - && userCan('chapter-update', $model) - && (!$hasNewParent || userCan('chapter-create', $newBook)) - && (!$hasNewParent || userCan('chapter-delete', $model)); - - if (!$hasPermission) { - return false; - } - } - - if ($model instanceof Page) { - $parentPermission = ($currentParent instanceof Chapter) ? 'chapter-update' : 'book-update'; - $hasCurrentParentPermission = userCan($parentPermission, $currentParent); - - // This needs to check if there was an intended chapter location in the original sort map - // rather than inferring from the $newChapter since that variable may be null - // due to other reasons (Visibility). - $newParent = $sortMapItem->parentChapterId ? $newChapter : $newBook; - if (!$newParent) { - return false; - } - - $hasPageEditPermission = userCan('page-update', $model); - $newParentInRightLocation = ($newParent instanceof Book || ($newParent instanceof Chapter && $newParent->book_id === $newBook->id)); - $newParentPermission = ($newParent instanceof Chapter) ? 'chapter-update' : 'book-update'; - $hasNewParentPermission = userCan($newParentPermission, $newParent); - - $hasDeletePermissionIfMoving = (!$hasNewParent || userCan('page-delete', $model)); - $hasCreatePermissionIfMoving = (!$hasNewParent || userCan('page-create', $newParent)); - - $hasPermission = $hasCurrentParentPermission - && $newParentInRightLocation - && $hasNewParentPermission - && $hasPageEditPermission - && $hasDeletePermissionIfMoving - && $hasCreatePermissionIfMoving; - - if (!$hasPermission) { - return false; - } - } - - return true; - } - - /** - * Load models from the database into the given sort map. - * - * @return array - */ - protected function loadModelsFromSortMap(BookSortMap $sortMap): array - { - $modelMap = []; - $ids = [ - 'chapter' => [], - 'page' => [], - 'book' => [], - ]; - - foreach ($sortMap->all() as $sortMapItem) { - $ids[$sortMapItem->type][] = $sortMapItem->id; - $ids['book'][] = $sortMapItem->parentBookId; - if ($sortMapItem->parentChapterId) { - $ids['chapter'][] = $sortMapItem->parentChapterId; - } - } - - $pages = Page::visible()->whereIn('id', array_unique($ids['page']))->get(Page::$listAttributes); - /** @var Page $page */ - foreach ($pages as $page) { - $modelMap['page:' . $page->id] = $page; - $ids['book'][] = $page->book_id; - if ($page->chapter_id) { - $ids['chapter'][] = $page->chapter_id; - } - } - - $chapters = Chapter::visible()->whereIn('id', array_unique($ids['chapter']))->get(); - /** @var Chapter $chapter */ - foreach ($chapters as $chapter) { - $modelMap['chapter:' . $chapter->id] = $chapter; - $ids['book'][] = $chapter->book_id; - } - - $books = Book::visible()->whereIn('id', array_unique($ids['book']))->get(); - /** @var Book $book */ - foreach ($books as $book) { - $modelMap['book:' . $book->id] = $book; - } - - return $modelMap; + return $query->where('book_id', '=', $this->book->id)->get(); } } diff --git a/app/Entities/Tools/Cloner.php b/app/Entities/Tools/Cloner.php index f7ed4b72dcb..05618fef461 100644 --- a/app/Entities/Tools/Cloner.php +++ b/app/Entities/Tools/Cloner.php @@ -7,28 +7,24 @@ use BookStack\Entities\Models\Bookshelf; use BookStack\Entities\Models\Chapter; use BookStack\Entities\Models\Entity; -use BookStack\Entities\Models\HasCoverImage; +use BookStack\Entities\Models\CoverImageInterface; use BookStack\Entities\Models\Page; use BookStack\Entities\Repos\BookRepo; use BookStack\Entities\Repos\ChapterRepo; use BookStack\Entities\Repos\PageRepo; +use BookStack\Permissions\Permission; use BookStack\Uploads\Image; use BookStack\Uploads\ImageService; use Illuminate\Http\UploadedFile; class Cloner { - protected PageRepo $pageRepo; - protected ChapterRepo $chapterRepo; - protected BookRepo $bookRepo; - protected ImageService $imageService; - - public function __construct(PageRepo $pageRepo, ChapterRepo $chapterRepo, BookRepo $bookRepo, ImageService $imageService) - { - $this->pageRepo = $pageRepo; - $this->chapterRepo = $chapterRepo; - $this->bookRepo = $bookRepo; - $this->imageService = $imageService; + public function __construct( + protected PageRepo $pageRepo, + protected ChapterRepo $chapterRepo, + protected BookRepo $bookRepo, + protected ImageService $imageService, + ) { } /** @@ -54,7 +50,7 @@ public function cloneChapter(Chapter $original, Book $parent, string $newName): $copyChapter = $this->chapterRepo->create($chapterDetails, $parent); - if (userCan('page-create', $copyChapter)) { + if (userCan(Permission::PageCreate, $copyChapter)) { /** @var Page $page */ foreach ($original->getVisiblePages() as $page) { $this->clonePage($page, $copyChapter, $page->name); @@ -66,7 +62,7 @@ public function cloneChapter(Chapter $original, Book $parent, string $newName): /** * Clone the given book. - * Clones all child chapters & pages. + * Clones all child chapters and pages. */ public function cloneBook(Book $original, string $newName): Book { @@ -77,13 +73,13 @@ public function cloneBook(Book $original, string $newName): Book $copyBook = $this->bookRepo->create($bookDetails); // Clone contents - $directChildren = $original->getDirectChildren(); + $directChildren = $original->getDirectVisibleChildren(); foreach ($directChildren as $child) { - if ($child instanceof Chapter && userCan('chapter-create', $copyBook)) { + if ($child instanceof Chapter && userCan(Permission::ChapterCreate, $copyBook)) { $this->cloneChapter($child, $copyBook, $child->name); } - if ($child instanceof Page && !$child->draft && userCan('page-create', $copyBook)) { + if ($child instanceof Page && !$child->draft && userCan(Permission::PageCreate, $copyBook)) { $this->clonePage($child, $copyBook, $child->name); } } @@ -91,7 +87,7 @@ public function cloneBook(Book $original, string $newName): Book // Clone bookshelf relationships /** @var Bookshelf $shelf */ foreach ($original->shelves as $shelf) { - if (userCan('bookshelf-update', $shelf)) { + if (userCan(Permission::BookshelfUpdate, $shelf)) { $shelf->appendBook($copyBook); } } @@ -110,7 +106,7 @@ public function entityToInputData(Entity $entity): array $inputData['tags'] = $this->entityTagsToInputArray($entity); // Add a cover to the data if existing on the original entity - if ($entity instanceof HasCoverImage) { + if ($entity instanceof CoverImageInterface) { $cover = $entity->cover()->first(); if ($cover) { $inputData['image'] = $this->imageToUploadedFile($cover); diff --git a/app/Entities/Tools/HierarchyTransformer.php b/app/Entities/Tools/HierarchyTransformer.php index cd6c548fe58..b0d8880f402 100644 --- a/app/Entities/Tools/HierarchyTransformer.php +++ b/app/Entities/Tools/HierarchyTransformer.php @@ -13,17 +13,12 @@ class HierarchyTransformer { - protected BookRepo $bookRepo; - protected BookshelfRepo $shelfRepo; - protected Cloner $cloner; - protected TrashCan $trashCan; - - public function __construct(BookRepo $bookRepo, BookshelfRepo $shelfRepo, Cloner $cloner, TrashCan $trashCan) - { - $this->bookRepo = $bookRepo; - $this->shelfRepo = $shelfRepo; - $this->cloner = $cloner; - $this->trashCan = $trashCan; + public function __construct( + protected BookRepo $bookRepo, + protected BookshelfRepo $shelfRepo, + protected Cloner $cloner, + protected TrashCan $trashCan + ) { } /** diff --git a/app/Entities/Tools/MixedEntityListLoader.php b/app/Entities/Tools/MixedEntityListLoader.php index 50079e3bfe8..f9a940b981b 100644 --- a/app/Entities/Tools/MixedEntityListLoader.php +++ b/app/Entities/Tools/MixedEntityListLoader.php @@ -3,20 +3,13 @@ namespace BookStack\Entities\Tools; use BookStack\App\Model; -use BookStack\Entities\EntityProvider; +use BookStack\Entities\Queries\EntityQueries; use Illuminate\Database\Eloquent\Relations\Relation; class MixedEntityListLoader { - protected array $listAttributes = [ - 'page' => ['id', 'name', 'slug', 'book_id', 'chapter_id', 'text', 'draft'], - 'chapter' => ['id', 'name', 'slug', 'book_id', 'description'], - 'book' => ['id', 'name', 'slug', 'description'], - 'bookshelf' => ['id', 'name', 'slug', 'description'], - ]; - public function __construct( - protected EntityProvider $entityProvider + protected EntityQueries $queries, ) { } @@ -26,7 +19,7 @@ public function __construct( * This will look for a model id and type via 'name_id' and 'name_type'. * @param Model[] $relations */ - public function loadIntoRelations(array $relations, string $relationName): void + public function loadIntoRelations(array $relations, string $relationName, bool $loadParents): void { $idsByType = []; foreach ($relations as $relation) { @@ -40,7 +33,7 @@ public function loadIntoRelations(array $relations, string $relationName): void $idsByType[$type][] = $id; } - $modelMap = $this->idsByTypeToModelMap($idsByType); + $modelMap = $this->idsByTypeToModelMap($idsByType, $loadParents); foreach ($relations as $relation) { $type = $relation->getAttribute($relationName . '_type'); @@ -56,21 +49,14 @@ public function loadIntoRelations(array $relations, string $relationName): void * @param array $idsByType * @return array> */ - protected function idsByTypeToModelMap(array $idsByType): array + protected function idsByTypeToModelMap(array $idsByType, bool $eagerLoadParents): array { $modelMap = []; foreach ($idsByType as $type => $ids) { - if (!isset($this->listAttributes[$type])) { - continue; - } - - $instance = $this->entityProvider->get($type); - $models = $instance->newQuery() - ->select($this->listAttributes[$type]) - ->scopes('visible') + $models = $this->queries->visibleForList($type) ->whereIn('id', $ids) - ->with($this->getRelationsToEagerLoad($type)) + ->with($eagerLoadParents ? $this->getRelationsToEagerLoad($type) : []) ->get(); if (count($models) > 0) { diff --git a/app/Entities/Tools/PageContent.php b/app/Entities/Tools/PageContent.php index 6a89ff62631..4b1d77db720 100644 --- a/app/Entities/Tools/PageContent.php +++ b/app/Entities/Tools/PageContent.php @@ -3,9 +3,11 @@ namespace BookStack\Entities\Tools; use BookStack\Entities\Models\Page; +use BookStack\Entities\Queries\PageQueries; use BookStack\Entities\Tools\Markdown\MarkdownToHtml; use BookStack\Exceptions\ImageUploadException; use BookStack\Facades\Theme; +use BookStack\Permissions\Permission; use BookStack\Theming\ThemeEvents; use BookStack\Uploads\ImageRepo; use BookStack\Uploads\ImageService; @@ -21,9 +23,12 @@ class PageContent { + protected PageQueries $pageQueries; + public function __construct( protected Page $page ) { + $this->pageQueries = app()->make(PageQueries::class); } /** @@ -118,7 +123,7 @@ protected function base64ImageUriToUploadedImageUrl(string $uri, User $updater): $imageInfo = $this->parseBase64ImageUri($uri); // Validate user has permission to create images - if (!$updater->can('image-create-all')) { + if (!$updater->can(Permission::ImageCreateAll)) { return ''; } @@ -325,13 +330,14 @@ public function render(bool $blankIncludes = false): string protected function getContentProviderClosure(bool $blankIncludes): Closure { $contextPage = $this->page; + $queries = $this->pageQueries; - return function (PageIncludeTag $tag) use ($blankIncludes, $contextPage): PageIncludeContent { + return function (PageIncludeTag $tag) use ($blankIncludes, $contextPage, $queries): PageIncludeContent { if ($blankIncludes) { return PageIncludeContent::fromHtmlAndTag('', $tag); } - $matchedPage = Page::visible()->find($tag->getPageId()); + $matchedPage = $queries->findVisibleById($tag->getPageId()); $content = PageIncludeContent::fromHtmlAndTag($matchedPage->html ?? '', $tag); if (Theme::hasListeners(ThemeEvents::PAGE_INCLUDE_PARSE)) { @@ -374,7 +380,7 @@ public function getNavigation(string $htmlContent): array protected function headerNodesToLevelList(DOMNodeList $nodeList): array { $tree = collect($nodeList)->map(function (DOMElement $header) { - $text = trim(str_replace("\xc2\xa0", '', $header->nodeValue)); + $text = trim(str_replace("\xc2\xa0", ' ', $header->nodeValue)); $text = mb_substr($text, 0, 100); return [ diff --git a/app/Entities/Tools/PageEditActivity.php b/app/Entities/Tools/PageEditActivity.php index 646b200f1da..22f89bf62be 100644 --- a/app/Entities/Tools/PageEditActivity.php +++ b/app/Entities/Tools/PageEditActivity.php @@ -4,19 +4,15 @@ use BookStack\Entities\Models\Page; use BookStack\Entities\Models\PageRevision; +use BookStack\Util\DateFormatter; use Carbon\Carbon; use Illuminate\Database\Eloquent\Builder; class PageEditActivity { - protected Page $page; - - /** - * PageEditActivity constructor. - */ - public function __construct(Page $page) - { - $this->page = $page; + public function __construct( + protected Page $page + ) { } /** @@ -50,11 +46,9 @@ public function activeEditingMessage(): string /** * Get any editor clash warning messages to show for the given draft revision. * - * @param PageRevision|Page $draft - * * @return string[] */ - public function getWarningMessagesForDraft($draft): array + public function getWarningMessagesForDraft(Page|PageRevision $draft): array { $warnings = []; @@ -82,7 +76,8 @@ protected function hasPageBeenUpdatedSinceDraftCreated(PageRevision $draft): boo */ public function getEditingActiveDraftMessage(PageRevision $draft): string { - $message = trans('entities.pages_editing_draft_notification', ['timeDiff' => $draft->updated_at->diffForHumans()]); + $formatter = resolve(DateFormatter::class); + $message = trans('entities.pages_editing_draft_notification', ['timeDiff' => $formatter->relative($draft->updated_at)]); if ($draft->page->updated_at->timestamp <= $draft->updated_at->timestamp) { return $message; } diff --git a/app/Entities/Tools/PageEditorData.php b/app/Entities/Tools/PageEditorData.php index 3c7c9e2eaf0..b41b319095e 100644 --- a/app/Entities/Tools/PageEditorData.php +++ b/app/Entities/Tools/PageEditorData.php @@ -4,9 +4,10 @@ use BookStack\Activity\Tools\CommentTree; use BookStack\Entities\Models\Page; -use BookStack\Entities\Repos\PageRepo; +use BookStack\Entities\Queries\EntityQueries; use BookStack\Entities\Tools\Markdown\HtmlToMarkdown; use BookStack\Entities\Tools\Markdown\MarkdownToHtml; +use BookStack\Permissions\Permission; class PageEditorData { @@ -15,7 +16,7 @@ class PageEditorData public function __construct( protected Page $page, - protected PageRepo $pageRepo, + protected EntityQueries $queries, protected string $requestedEditor ) { $this->viewData = $this->build(); @@ -35,7 +36,12 @@ protected function build(): array { $page = clone $this->page; $isDraft = boolval($this->page->draft); - $templates = $this->pageRepo->getTemplates(10); + $templates = $this->queries->pages->visibleTemplates() + ->orderBy('name', 'asc') + ->take(10) + ->paginate() + ->withPath('/templates'); + $draftsEnabled = auth()->check(); $isDraftRevision = false; @@ -47,8 +53,8 @@ protected function build(): array } // Check for a current draft version for this user - $userDraft = $this->pageRepo->getUserDraft($page); - if ($userDraft !== null) { + $userDraft = $this->queries->revisions->findLatestCurrentUserDraftsForPageId($page->id); + if (!is_null($userDraft)) { $page->forceFill($userDraft->only(['name', 'html', 'markdown'])); $isDraftRevision = true; $this->warnings[] = $editActivity->getEditingActiveDraftMessage($userDraft); @@ -69,17 +75,17 @@ protected function build(): array ]; } - protected function updateContentForEditor(Page $page, string $editorType): void + protected function updateContentForEditor(Page $page, PageEditorType $editorType): void { $isHtml = !empty($page->html) && empty($page->markdown); // HTML to markdown-clean conversion - if ($editorType === 'markdown' && $isHtml && $this->requestedEditor === 'markdown-clean') { + if ($editorType === PageEditorType::Markdown && $isHtml && $this->requestedEditor === 'markdown-clean') { $page->markdown = (new HtmlToMarkdown($page->html))->convert(); } // Markdown to HTML conversion if we don't have HTML - if ($editorType === 'wysiwyg' && !$isHtml) { + if ($editorType->isHtmlBased() && !$isHtml) { $page->html = (new MarkdownToHtml($page->markdown))->convert(); } } @@ -89,24 +95,16 @@ protected function updateContentForEditor(Page $page, string $editorType): void * Defaults based upon the current content of the page otherwise will fall back * to system default but will take a requested type (if provided) if permissions allow. */ - protected function getEditorType(Page $page): string + protected function getEditorType(Page $page): PageEditorType { - $editorType = $page->editor ?: self::getSystemDefaultEditor(); + $editorType = PageEditorType::forPage($page) ?: PageEditorType::getSystemDefault(); - // Use requested editor if valid and if we have permission - $requestedType = explode('-', $this->requestedEditor)[0]; - if (($requestedType === 'markdown' || $requestedType === 'wysiwyg') && userCan('editor-change')) { + // Use the requested editor if valid and if we have permission + $requestedType = PageEditorType::fromRequestValue($this->requestedEditor); + if ($requestedType && userCan(Permission::EditorChange)) { $editorType = $requestedType; } return $editorType; } - - /** - * Get the configured system default editor. - */ - public static function getSystemDefaultEditor(): string - { - return setting('app-editor') === 'markdown' ? 'markdown' : 'wysiwyg'; - } } diff --git a/app/Entities/Tools/PageEditorType.php b/app/Entities/Tools/PageEditorType.php new file mode 100644 index 00000000000..1c1d430e4a2 --- /dev/null +++ b/app/Entities/Tools/PageEditorType.php @@ -0,0 +1,37 @@ + true, + self::Markdown => false, + }; + } + + public static function fromRequestValue(string $value): static|null + { + $editor = explode('-', $value)[0]; + return static::tryFrom($editor); + } + + public static function forPage(Page $page): static|null + { + return static::tryFrom($page->editor); + } + + public static function getSystemDefault(): static + { + $setting = setting('app-editor'); + return static::tryFrom($setting) ?? static::WysiwygTinymce; + } +} diff --git a/app/Entities/Tools/PageIncludeParser.php b/app/Entities/Tools/PageIncludeParser.php index dad7c29e60e..af7ed4fc6a1 100644 --- a/app/Entities/Tools/PageIncludeParser.php +++ b/app/Entities/Tools/PageIncludeParser.php @@ -7,15 +7,14 @@ use DOMDocument; use DOMElement; use DOMNode; -use DOMText; class PageIncludeParser { protected static string $includeTagRegex = "/{{@\s?([0-9].*?)}}/"; /** - * Elements to clean up and remove if left empty after a parsing operation. - * @var DOMElement[] + * Nodes to clean up and remove if left empty after a parsing operation. + * @var DOMNode[] */ protected array $toCleanup = []; @@ -104,10 +103,10 @@ protected function splitTextNodesAtTags(DOMNode $textNode): array if ($currentOffset < $tagStartOffset) { $previousText = substr($text, $currentOffset, $tagStartOffset - $currentOffset); - $textNode->parentNode->insertBefore(new DOMText($previousText), $textNode); + $textNode->parentNode->insertBefore($this->doc->createTextNode($previousText), $textNode); } - $node = $textNode->parentNode->insertBefore(new DOMText($tagOuterContent), $textNode); + $node = $textNode->parentNode->insertBefore($this->doc->createTextNode($tagOuterContent), $textNode); $includeTags[] = new PageIncludeTag($tagInnerContent, $node); $currentOffset = $tagStartOffset + strlen($tagOuterContent); } @@ -159,7 +158,7 @@ protected function moveTagNodeToBesideParent(PageIncludeTag $tag, DOMNode $paren /** * Splits the given $parentNode at the location of the $domNode within it. - * Attempts replicate the original $parentNode, moving some of their parent + * Attempts to replicate the original $parentNode, moving some of their parent * children in where needed, before adding the $domNode between. */ protected function splitNodeAtChildNode(DOMElement $parentNode, DOMNode $domNode): void @@ -171,6 +170,10 @@ protected function splitNodeAtChildNode(DOMElement $parentNode, DOMNode $domNode } $parentClone = $parentNode->cloneNode(); + if (!($parentClone instanceof DOMElement)) { + return; + } + $parentNode->parentNode->insertBefore($parentClone, $parentNode); $parentClone->removeAttribute('id'); @@ -203,7 +206,7 @@ protected function getParentParagraph(DOMNode $parent): ?DOMNode } /** - * Cleanup after a parse operation. + * Clean up after a parse operation. * Removes stranded elements we may have left during the parse. */ protected function cleanup(): void diff --git a/app/Entities/Tools/PdfGenerator.php b/app/Entities/Tools/PdfGenerator.php deleted file mode 100644 index d0c9158a91c..00000000000 --- a/app/Entities/Tools/PdfGenerator.php +++ /dev/null @@ -1,38 +0,0 @@ -getActiveEngine() === self::ENGINE_WKHTML) { - $pdf = SnappyPDF::loadHTML($html); - $pdf->setOption('print-media-type', true); - } else { - $pdf = DomPDF::loadHTML($html); - } - - return $pdf->output(); - } - - /** - * Get the currently active PDF engine. - * Returns the value of an `ENGINE_` const on this class. - */ - public function getActiveEngine(): string - { - $useWKHTML = config('snappy.pdf.binary') !== false && config('app.allow_untrusted_server_fetching') === true; - - return $useWKHTML ? self::ENGINE_WKHTML : self::ENGINE_DOMPDF; - } -} diff --git a/app/Entities/Tools/PermissionsUpdater.php b/app/Entities/Tools/PermissionsUpdater.php index 9f3b8f95277..fa9ae753c51 100644 --- a/app/Entities/Tools/PermissionsUpdater.php +++ b/app/Entities/Tools/PermissionsUpdater.php @@ -8,6 +8,7 @@ use BookStack\Entities\Models\Entity; use BookStack\Facades\Activity; use BookStack\Permissions\Models\EntityPermission; +use BookStack\Permissions\Permission; use BookStack\Users\Models\Role; use BookStack\Users\Models\User; use Illuminate\Http\Request; @@ -93,8 +94,9 @@ protected function formatPermissionsFromRequestToEntityPermissions(array $permis foreach ($permissions as $roleId => $info) { $entityPermissionData = ['role_id' => $roleId]; - foreach (EntityPermission::PERMISSIONS as $permission) { - $entityPermissionData[$permission] = (($info[$permission] ?? false) === "true"); + foreach (Permission::genericForEntity() as $permission) { + $permName = $permission->value; + $entityPermissionData[$permName] = (($info[$permName] ?? false) === "true"); } $formatted[] = $entityPermissionData; } @@ -108,8 +110,9 @@ protected function formatPermissionsFromApiRequestToEntityPermissions(array $per foreach ($permissions as $requestPermissionData) { $entityPermissionData = ['role_id' => $requestPermissionData['role_id']]; - foreach (EntityPermission::PERMISSIONS as $permission) { - $entityPermissionData[$permission] = boolval($requestPermissionData[$permission] ?? false); + foreach (Permission::genericForEntity() as $permission) { + $permName = $permission->value; + $entityPermissionData[$permName] = boolval($requestPermissionData[$permName] ?? false); } $formatted[] = $entityPermissionData; } @@ -147,7 +150,7 @@ public function updateBookPermissionsFromShelf(Bookshelf $shelf, $checkUserPermi /** @var Book $book */ foreach ($shelfBooks as $book) { - if ($checkUserPermissions && !userCan('restrictions-manage', $book)) { + if ($checkUserPermissions && !userCan(Permission::RestrictionsManage, $book)) { continue; } $book->permissions()->delete(); diff --git a/app/Entities/Tools/ShelfContext.php b/app/Entities/Tools/ShelfContext.php index 50c7047d9e3..5ed33487854 100644 --- a/app/Entities/Tools/ShelfContext.php +++ b/app/Entities/Tools/ShelfContext.php @@ -4,10 +4,16 @@ use BookStack\Entities\Models\Book; use BookStack\Entities\Models\Bookshelf; +use BookStack\Entities\Queries\BookshelfQueries; class ShelfContext { - protected $KEY_SHELF_CONTEXT_ID = 'context_bookshelf_id'; + protected string $KEY_SHELF_CONTEXT_ID = 'context_bookshelf_id'; + + public function __construct( + protected BookshelfQueries $shelfQueries, + ) { + } /** * Get the current bookshelf context for the given book. @@ -20,8 +26,7 @@ public function getContextualShelfForBook(Book $book): ?Bookshelf return null; } - /** @var Bookshelf $shelf */ - $shelf = Bookshelf::visible()->find($contextBookshelfId); + $shelf = $this->shelfQueries->findVisibleById($contextBookshelfId); $shelfContainsBook = $shelf && $shelf->contains($book); return $shelfContainsBook ? $shelf : null; @@ -30,7 +35,7 @@ public function getContextualShelfForBook(Book $book): ?Bookshelf /** * Store the current contextual shelf ID. */ - public function setShelfContext(int $shelfId) + public function setShelfContext(int $shelfId): void { session()->put($this->KEY_SHELF_CONTEXT_ID, $shelfId); } @@ -38,7 +43,7 @@ public function setShelfContext(int $shelfId) /** * Clear the session stored shelf context id. */ - public function clearShelfContext() + public function clearShelfContext(): void { session()->forget($this->KEY_SHELF_CONTEXT_ID); } diff --git a/app/Entities/Tools/SiblingFetcher.php b/app/Entities/Tools/SiblingFetcher.php index 617ef4a620d..156209fd23e 100644 --- a/app/Entities/Tools/SiblingFetcher.php +++ b/app/Entities/Tools/SiblingFetcher.php @@ -7,10 +7,17 @@ use BookStack\Entities\Models\Bookshelf; use BookStack\Entities\Models\Chapter; use BookStack\Entities\Models\Page; +use BookStack\Entities\Queries\EntityQueries; use Illuminate\Support\Collection; class SiblingFetcher { + public function __construct( + protected EntityQueries $queries, + protected ShelfContext $shelfContext, + ) { + } + /** * Search among the siblings of the entity of given type and id. */ @@ -26,23 +33,23 @@ public function fetch(string $entityType, int $entityId): Collection // Page in book or chapter if (($entity instanceof Page && !$entity->chapter) || $entity instanceof Chapter) { - $entities = $entity->book->getDirectChildren(); + $entities = $entity->book->getDirectVisibleChildren(); } // Book // Gets just the books in a shelf if shelf is in context if ($entity instanceof Book) { - $contextShelf = (new ShelfContext())->getContextualShelfForBook($entity); + $contextShelf = $this->shelfContext->getContextualShelfForBook($entity); if ($contextShelf) { $entities = $contextShelf->visibleBooks()->get(); } else { - $entities = Book::visible()->get(); + $entities = $this->queries->books->visibleForList()->orderBy('name', 'asc')->get(); } } // Shelf if ($entity instanceof Bookshelf) { - $entities = Bookshelf::visible()->get(); + $entities = $this->queries->shelves->visibleForList()->orderBy('name', 'asc')->get(); } return $entities; diff --git a/app/Entities/Tools/SlugGenerator.php b/app/Entities/Tools/SlugGenerator.php index 5df300bb04c..fb912318750 100644 --- a/app/Entities/Tools/SlugGenerator.php +++ b/app/Entities/Tools/SlugGenerator.php @@ -3,7 +3,7 @@ namespace BookStack\Entities\Tools; use BookStack\App\Model; -use BookStack\App\Sluggable; +use BookStack\App\SluggableInterface; use BookStack\Entities\Models\BookChild; use Illuminate\Support\Str; @@ -13,9 +13,9 @@ class SlugGenerator * Generate a fresh slug for the given entity. * The slug will be generated so that it doesn't conflict within the same parent item. */ - public function generate(Sluggable $model): string + public function generate(SluggableInterface&Model $model, string $slugSource): string { - $slug = $this->formatNameAsSlug($model->name); + $slug = $this->formatNameAsSlug($slugSource); while ($this->slugInUse($slug, $model)) { $slug .= '-' . Str::random(3); } @@ -24,7 +24,7 @@ public function generate(Sluggable $model): string } /** - * Format a name as a url slug. + * Format a name as a URL slug. */ protected function formatNameAsSlug(string $name): string { @@ -39,10 +39,8 @@ protected function formatNameAsSlug(string $name): string /** * Check if a slug is already in-use for this * type of model within the same parent. - * - * @param Sluggable&Model $model */ - protected function slugInUse(string $slug, Sluggable $model): bool + protected function slugInUse(string $slug, SluggableInterface&Model $model): bool { $query = $model->newQuery()->where('slug', '=', $slug); diff --git a/app/Entities/Tools/TrashCan.php b/app/Entities/Tools/TrashCan.php index b2510398541..d457d4f48db 100644 --- a/app/Entities/Tools/TrashCan.php +++ b/app/Entities/Tools/TrashCan.php @@ -8,18 +8,25 @@ use BookStack\Entities\Models\Chapter; use BookStack\Entities\Models\Deletion; use BookStack\Entities\Models\Entity; -use BookStack\Entities\Models\HasCoverImage; +use BookStack\Entities\Models\CoverImageInterface; use BookStack\Entities\Models\Page; +use BookStack\Entities\Queries\EntityQueries; use BookStack\Exceptions\NotifyException; use BookStack\Facades\Activity; use BookStack\Uploads\AttachmentService; use BookStack\Uploads\ImageService; +use BookStack\Util\DatabaseTransaction; use Exception; use Illuminate\Database\Eloquent\Builder; use Illuminate\Support\Carbon; class TrashCan { + public function __construct( + protected EntityQueries $queries, + ) { + } + /** * Send a shelf to the recycle bin. * @@ -203,7 +210,13 @@ protected function destroyPage(Page $page): int } // Remove book template usages - Book::query()->where('default_template_id', '=', $page->id) + $this->queries->books->start() + ->where('default_template_id', '=', $page->id) + ->update(['default_template_id' => null]); + + // Remove chapter template usages + $this->queries->chapters->start() + ->where('default_template_id', '=', $page->id) ->update(['default_template_id' => null]); $page->forceDelete(); @@ -345,25 +358,26 @@ protected function restoreEntity(Entity $entity): int /** * Destroy the given entity. + * Returns the number of total entities destroyed in the operation. * * @throws Exception */ public function destroyEntity(Entity $entity): int { - if ($entity instanceof Page) { - return $this->destroyPage($entity); - } - if ($entity instanceof Chapter) { - return $this->destroyChapter($entity); - } - if ($entity instanceof Book) { - return $this->destroyBook($entity); - } - if ($entity instanceof Bookshelf) { - return $this->destroyShelf($entity); - } + $result = (new DatabaseTransaction(function () use ($entity) { + if ($entity instanceof Page) { + return $this->destroyPage($entity); + } else if ($entity instanceof Chapter) { + return $this->destroyChapter($entity); + } else if ($entity instanceof Book) { + return $this->destroyBook($entity); + } else if ($entity instanceof Bookshelf) { + return $this->destroyShelf($entity); + } + return null; + }))->run(); - return 0; + return $result ?? 0; } /** @@ -384,7 +398,7 @@ protected function destroyCommonRelations(Entity $entity) $entity->referencesTo()->delete(); $entity->referencesFrom()->delete(); - if ($entity instanceof HasCoverImage && $entity->cover()->exists()) { + if ($entity instanceof CoverImageInterface && $entity->cover()->exists()) { $imageService = app()->make(ImageService::class); $imageService->destroy($entity->cover()->first()); } diff --git a/app/Exceptions/BookStackExceptionHandlerPage.php b/app/Exceptions/BookStackExceptionHandlerPage.php index 78f586ad0c9..a53733ac7ee 100644 --- a/app/Exceptions/BookStackExceptionHandlerPage.php +++ b/app/Exceptions/BookStackExceptionHandlerPage.php @@ -2,6 +2,7 @@ namespace BookStack\Exceptions; +use BookStack\App\AppVersion; use Illuminate\Contracts\Foundation\ExceptionRenderer; class BookStackExceptionHandlerPage implements ExceptionRenderer @@ -30,9 +31,7 @@ protected function getEnvironment(): array return [ 'PHP Version' => phpversion(), 'BookStack Version' => $this->safeReturn(function () { - $versionFile = base_path('version'); - - return trim(file_get_contents($versionFile)); + return AppVersion::get(); }, 'unknown'), 'Theme Configured' => $this->safeReturn(function () { return config('view.theme'); diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php index 61e1263273b..08d326ad81c 100644 --- a/app/Exceptions/Handler.php +++ b/app/Exceptions/Handler.php @@ -2,7 +2,6 @@ namespace BookStack\Exceptions; -use Exception; use Illuminate\Auth\AuthenticationException; use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; @@ -12,6 +11,7 @@ use Illuminate\Http\Response; use Illuminate\Validation\ValidationException; use Symfony\Component\ErrorHandler\Error\FatalError; +use Symfony\Component\HttpFoundation\Response as SymfonyResponse; use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; use Throwable; @@ -20,7 +20,7 @@ class Handler extends ExceptionHandler /** * A list of the exception types that are not reported. * - * @var array> + * @var array> */ protected $dontReport = [ NotFoundException::class, @@ -50,11 +50,11 @@ class Handler extends ExceptionHandler /** * Report or log an exception. * - * @param \Throwable $exception - * - * @throws \Throwable + * @param Throwable $exception * * @return void + *@throws Throwable + * */ public function report(Throwable $exception) { @@ -64,12 +64,9 @@ public function report(Throwable $exception) /** * Render an exception into an HTTP response. * - * @param \Illuminate\Http\Request $request - * @param Exception $e - * - * @return \Illuminate\Http\Response + * @param Request $request */ - public function render($request, Throwable $e) + public function render($request, Throwable $e): SymfonyResponse { if ($e instanceof FatalError && str_contains($e->getMessage(), 'bytes exhausted (tried to allocate') && $this->onOutOfMemory) { $response = call_user_func($this->onOutOfMemory); @@ -94,7 +91,7 @@ public function render($request, Throwable $e) * If the callable returns a response, this response will be returned * to the request upon error. */ - public function prepareForOutOfMemory(callable $onOutOfMemory) + public function prepareForOutOfMemory(callable $onOutOfMemory): void { $this->onOutOfMemory = $onOutOfMemory; } @@ -102,7 +99,7 @@ public function prepareForOutOfMemory(callable $onOutOfMemory) /** * Forget the current out of memory handler, if existing. */ - public function forgetOutOfMemoryHandler() + public function forgetOutOfMemoryHandler(): void { $this->onOutOfMemory = null; } @@ -152,12 +149,9 @@ protected function renderApiException(Throwable $e): JsonResponse /** * Convert an authentication exception into an unauthenticated response. * - * @param \Illuminate\Http\Request $request - * @param \Illuminate\Auth\AuthenticationException $exception - * - * @return \Illuminate\Http\Response + * @param Request $request */ - protected function unauthenticated($request, AuthenticationException $exception) + protected function unauthenticated($request, AuthenticationException $exception): SymfonyResponse { if ($request->expectsJson()) { return response()->json(['error' => 'Unauthenticated.'], 401); @@ -169,12 +163,9 @@ protected function unauthenticated($request, AuthenticationException $exception) /** * Convert a validation exception into a JSON response. * - * @param \Illuminate\Http\Request $request - * @param \Illuminate\Validation\ValidationException $exception - * - * @return \Illuminate\Http\JsonResponse + * @param Request $request */ - protected function invalidJson($request, ValidationException $exception) + protected function invalidJson($request, ValidationException $exception): JsonResponse { return response()->json($exception->errors(), $exception->status); } diff --git a/app/Exceptions/LoginAttemptInvalidUserException.php b/app/Exceptions/LoginAttemptInvalidUserException.php new file mode 100644 index 00000000000..163484c5aa2 --- /dev/null +++ b/app/Exceptions/LoginAttemptInvalidUserException.php @@ -0,0 +1,7 @@ +user = $user; - $this->loginService = $loginService; + public function __construct( + protected User $user, + protected LoginService $loginService + ) { parent::__construct(); } diff --git a/app/Exceptions/ZipExportException.php b/app/Exceptions/ZipExportException.php new file mode 100644 index 00000000000..b2c811e0b33 --- /dev/null +++ b/app/Exceptions/ZipExportException.php @@ -0,0 +1,7 @@ +errors); + parent::__construct($message); + } +} diff --git a/app/Exceptions/ZipValidationException.php b/app/Exceptions/ZipValidationException.php new file mode 100644 index 00000000000..aaaee792ef0 --- /dev/null +++ b/app/Exceptions/ZipValidationException.php @@ -0,0 +1,12 @@ +exportFormatter = $exportFormatter; - $this->middleware('can:content-export'); + public function __construct( + protected ExportFormatter $exportFormatter, + protected BookQueries $queries, + ) { + $this->middleware(Permission::ContentExport->middleware()); } /** @@ -24,7 +25,7 @@ public function __construct(ExportFormatter $exportFormatter) */ public function exportPdf(int $id) { - $book = Book::visible()->findOrFail($id); + $book = $this->queries->findVisibleByIdOrFail($id); $pdfContent = $this->exportFormatter->bookToPdf($book); return $this->download()->directly($pdfContent, $book->slug . '.pdf'); @@ -37,7 +38,7 @@ public function exportPdf(int $id) */ public function exportHtml(int $id) { - $book = Book::visible()->findOrFail($id); + $book = $this->queries->findVisibleByIdOrFail($id); $htmlContent = $this->exportFormatter->bookToContainedHtml($book); return $this->download()->directly($htmlContent, $book->slug . '.html'); @@ -48,7 +49,7 @@ public function exportHtml(int $id) */ public function exportPlainText(int $id) { - $book = Book::visible()->findOrFail($id); + $book = $this->queries->findVisibleByIdOrFail($id); $textContent = $this->exportFormatter->bookToPlainText($book); return $this->download()->directly($textContent, $book->slug . '.txt'); @@ -59,9 +60,20 @@ public function exportPlainText(int $id) */ public function exportMarkdown(int $id) { - $book = Book::visible()->findOrFail($id); + $book = $this->queries->findVisibleByIdOrFail($id); $markdown = $this->exportFormatter->bookToMarkdown($book); return $this->download()->directly($markdown, $book->slug . '.md'); } + + /** + * Export a book as a contained ZIP export file. + */ + public function exportZip(int $id, ZipExportBuilder $builder) + { + $book = $this->queries->findVisibleByIdOrFail($id); + $zip = $builder->buildForBook($book); + + return $this->download()->streamedFileDirectly($zip, $book->slug . '.zip', true); + } } diff --git a/app/Entities/Controllers/BookExportController.php b/app/Exports/Controllers/BookExportController.php similarity index 52% rename from app/Entities/Controllers/BookExportController.php rename to app/Exports/Controllers/BookExportController.php index 1a6b20db966..f6bb66666dd 100644 --- a/app/Entities/Controllers/BookExportController.php +++ b/app/Exports/Controllers/BookExportController.php @@ -1,25 +1,23 @@ bookRepo = $bookRepo; - $this->exportFormatter = $exportFormatter; - $this->middleware('can:content-export'); + public function __construct( + protected BookQueries $queries, + protected ExportFormatter $exportFormatter, + ) { + $this->middleware(Permission::ContentExport->middleware()); + $this->middleware('throttle:exports'); } /** @@ -29,7 +27,7 @@ public function __construct(BookRepo $bookRepo, ExportFormatter $exportFormatter */ public function pdf(string $bookSlug) { - $book = $this->bookRepo->getBySlug($bookSlug); + $book = $this->queries->findVisibleBySlugOrFail($bookSlug); $pdfContent = $this->exportFormatter->bookToPdf($book); return $this->download()->directly($pdfContent, $bookSlug . '.pdf'); @@ -42,7 +40,7 @@ public function pdf(string $bookSlug) */ public function html(string $bookSlug) { - $book = $this->bookRepo->getBySlug($bookSlug); + $book = $this->queries->findVisibleBySlugOrFail($bookSlug); $htmlContent = $this->exportFormatter->bookToContainedHtml($book); return $this->download()->directly($htmlContent, $bookSlug . '.html'); @@ -53,7 +51,7 @@ public function html(string $bookSlug) */ public function plainText(string $bookSlug) { - $book = $this->bookRepo->getBySlug($bookSlug); + $book = $this->queries->findVisibleBySlugOrFail($bookSlug); $textContent = $this->exportFormatter->bookToPlainText($book); return $this->download()->directly($textContent, $bookSlug . '.txt'); @@ -64,9 +62,21 @@ public function plainText(string $bookSlug) */ public function markdown(string $bookSlug) { - $book = $this->bookRepo->getBySlug($bookSlug); + $book = $this->queries->findVisibleBySlugOrFail($bookSlug); $textContent = $this->exportFormatter->bookToMarkdown($book); return $this->download()->directly($textContent, $bookSlug . '.md'); } + + /** + * Export a book to a contained ZIP export file. + * @throws NotFoundException + */ + public function zip(string $bookSlug, ZipExportBuilder $builder) + { + $book = $this->queries->findVisibleBySlugOrFail($bookSlug); + $zip = $builder->buildForBook($book); + + return $this->download()->streamedFileDirectly($zip, $bookSlug . '.zip', true); + } } diff --git a/app/Entities/Controllers/ChapterExportApiController.php b/app/Exports/Controllers/ChapterExportApiController.php similarity index 56% rename from app/Entities/Controllers/ChapterExportApiController.php rename to app/Exports/Controllers/ChapterExportApiController.php index d1523e665c9..7e5a23c7059 100644 --- a/app/Entities/Controllers/ChapterExportApiController.php +++ b/app/Exports/Controllers/ChapterExportApiController.php @@ -1,23 +1,21 @@ exportFormatter = $exportFormatter; - $this->middleware('can:content-export'); + public function __construct( + protected ExportFormatter $exportFormatter, + protected ChapterQueries $queries, + ) { + $this->middleware(Permission::ContentExport->middleware()); } /** @@ -27,7 +25,7 @@ public function __construct(ExportFormatter $exportFormatter) */ public function exportPdf(int $id) { - $chapter = Chapter::visible()->findOrFail($id); + $chapter = $this->queries->findVisibleByIdOrFail($id); $pdfContent = $this->exportFormatter->chapterToPdf($chapter); return $this->download()->directly($pdfContent, $chapter->slug . '.pdf'); @@ -40,7 +38,7 @@ public function exportPdf(int $id) */ public function exportHtml(int $id) { - $chapter = Chapter::visible()->findOrFail($id); + $chapter = $this->queries->findVisibleByIdOrFail($id); $htmlContent = $this->exportFormatter->chapterToContainedHtml($chapter); return $this->download()->directly($htmlContent, $chapter->slug . '.html'); @@ -51,7 +49,7 @@ public function exportHtml(int $id) */ public function exportPlainText(int $id) { - $chapter = Chapter::visible()->findOrFail($id); + $chapter = $this->queries->findVisibleByIdOrFail($id); $textContent = $this->exportFormatter->chapterToPlainText($chapter); return $this->download()->directly($textContent, $chapter->slug . '.txt'); @@ -62,9 +60,20 @@ public function exportPlainText(int $id) */ public function exportMarkdown(int $id) { - $chapter = Chapter::visible()->findOrFail($id); + $chapter = $this->queries->findVisibleByIdOrFail($id); $markdown = $this->exportFormatter->chapterToMarkdown($chapter); return $this->download()->directly($markdown, $chapter->slug . '.md'); } + + /** + * Export a chapter as a contained ZIP file. + */ + public function exportZip(int $id, ZipExportBuilder $builder) + { + $chapter = $this->queries->findVisibleByIdOrFail($id); + $zip = $builder->buildForChapter($chapter); + + return $this->download()->streamedFileDirectly($zip, $chapter->slug . '.zip', true); + } } diff --git a/app/Entities/Controllers/ChapterExportController.php b/app/Exports/Controllers/ChapterExportController.php similarity index 56% rename from app/Entities/Controllers/ChapterExportController.php rename to app/Exports/Controllers/ChapterExportController.php index b67ec9b373b..fdb2bba9457 100644 --- a/app/Entities/Controllers/ChapterExportController.php +++ b/app/Exports/Controllers/ChapterExportController.php @@ -1,26 +1,23 @@ chapterRepo = $chapterRepo; - $this->exportFormatter = $exportFormatter; - $this->middleware('can:content-export'); + public function __construct( + protected ChapterQueries $queries, + protected ExportFormatter $exportFormatter, + ) { + $this->middleware(Permission::ContentExport->middleware()); + $this->middleware('throttle:exports'); } /** @@ -31,7 +28,7 @@ public function __construct(ChapterRepo $chapterRepo, ExportFormatter $exportFor */ public function pdf(string $bookSlug, string $chapterSlug) { - $chapter = $this->chapterRepo->getBySlug($bookSlug, $chapterSlug); + $chapter = $this->queries->findVisibleBySlugsOrFail($bookSlug, $chapterSlug); $pdfContent = $this->exportFormatter->chapterToPdf($chapter); return $this->download()->directly($pdfContent, $chapterSlug . '.pdf'); @@ -45,7 +42,7 @@ public function pdf(string $bookSlug, string $chapterSlug) */ public function html(string $bookSlug, string $chapterSlug) { - $chapter = $this->chapterRepo->getBySlug($bookSlug, $chapterSlug); + $chapter = $this->queries->findVisibleBySlugsOrFail($bookSlug, $chapterSlug); $containedHtml = $this->exportFormatter->chapterToContainedHtml($chapter); return $this->download()->directly($containedHtml, $chapterSlug . '.html'); @@ -58,7 +55,7 @@ public function html(string $bookSlug, string $chapterSlug) */ public function plainText(string $bookSlug, string $chapterSlug) { - $chapter = $this->chapterRepo->getBySlug($bookSlug, $chapterSlug); + $chapter = $this->queries->findVisibleBySlugsOrFail($bookSlug, $chapterSlug); $chapterText = $this->exportFormatter->chapterToPlainText($chapter); return $this->download()->directly($chapterText, $chapterSlug . '.txt'); @@ -71,9 +68,21 @@ public function plainText(string $bookSlug, string $chapterSlug) */ public function markdown(string $bookSlug, string $chapterSlug) { - $chapter = $this->chapterRepo->getBySlug($bookSlug, $chapterSlug); + $chapter = $this->queries->findVisibleBySlugsOrFail($bookSlug, $chapterSlug); $chapterText = $this->exportFormatter->chapterToMarkdown($chapter); return $this->download()->directly($chapterText, $chapterSlug . '.md'); } + + /** + * Export a book to a contained ZIP export file. + * @throws NotFoundException + */ + public function zip(string $bookSlug, string $chapterSlug, ZipExportBuilder $builder) + { + $chapter = $this->queries->findVisibleBySlugsOrFail($bookSlug, $chapterSlug); + $zip = $builder->buildForChapter($chapter); + + return $this->download()->streamedFileDirectly($zip, $chapterSlug . '.zip', true); + } } diff --git a/app/Exports/Controllers/ImportApiController.php b/app/Exports/Controllers/ImportApiController.php new file mode 100644 index 00000000000..f8eaea5a131 --- /dev/null +++ b/app/Exports/Controllers/ImportApiController.php @@ -0,0 +1,145 @@ +middleware(Permission::ContentImport->middleware()); + } + + /** + * List existing ZIP imports visible to the user. + * Requires permission to import content. + */ + public function list(): JsonResponse + { + $query = $this->imports->queryVisible(); + + return $this->apiListingResponse($query, [ + 'id', 'name', 'size', 'type', 'created_by', 'created_at', 'updated_at' + ]); + } + + /** + * Start a new import from a ZIP file. + * This does not actually run the import since that is performed via the "run" endpoint. + * This uploads, validates and stores the ZIP file so it's ready to be imported. + * + * This "file" parameter must be a BookStack-compatible ZIP file, and this must be + * sent via a 'multipart/form-data' type request. + * + * Requires permission to import content. + */ + public function create(Request $request): JsonResponse + { + $this->validate($request, $this->rules()['create']); + + $file = $request->file('file'); + + try { + $import = $this->imports->storeFromUpload($file); + } catch (ZipValidationException $exception) { + $message = "ZIP upload failed with the following validation errors: \n" . $this->formatErrors($exception->errors); + return $this->jsonError($message, 422); + } + + return response()->json($import); + } + + /** + * Read details of a pending ZIP import. + * The "details" property contains high-level metadata regarding the ZIP import content, + * and the structure of this will change depending on import "type". + * Requires permission to import content. + */ + public function read(int $id): JsonResponse + { + $import = $this->imports->findVisible($id); + + $import->setAttribute('details', $import->decodeMetadata()); + + return response()->json($import); + } + + /** + * Run the import process for an uploaded ZIP import. + * The "parent_id" and "parent_type" parameters are required when the import type is "chapter" or "page". + * On success, this endpoint returns the imported item. + * Requires permission to import content. + */ + public function run(int $id, Request $request): JsonResponse + { + $import = $this->imports->findVisible($id); + $parent = null; + $rules = $this->rules()['run']; + + if ($import->type === 'page' || $import->type === 'chapter') { + $rules['parent_type'][] = 'required'; + $rules['parent_id'][] = 'required'; + $data = $this->validate($request, $rules); + $parent = "{$data['parent_type']}:{$data['parent_id']}"; + } + + try { + $entity = $this->imports->runImport($import, $parent); + } catch (ZipImportException $exception) { + $message = "ZIP import failed with the following errors: \n" . $this->formatErrors($exception->errors); + return $this->jsonError($message); + } + + return response()->json($entity->withoutRelations()); + } + + /** + * Delete a pending ZIP import from the system. + * Requires permission to import content. + */ + public function delete(int $id): Response + { + $import = $this->imports->findVisible($id); + $this->imports->deleteImport($import); + + return response('', 204); + } + + protected function rules(): array + { + return [ + 'create' => [ + 'file' => ['required', ...AttachmentService::getFileValidationRules()], + ], + 'run' => [ + 'parent_type' => ['string', 'in:book,chapter'], + 'parent_id' => ['int'], + ], + ]; + } + + protected function formatErrors(array $errors): string + { + $parts = []; + foreach ($errors as $key => $error) { + if (is_string($key)) { + $parts[] = "[{$key}] {$error}"; + } else { + $parts[] = $error; + } + } + return implode("\n", $parts); + } +} diff --git a/app/Exports/Controllers/ImportController.php b/app/Exports/Controllers/ImportController.php new file mode 100644 index 00000000000..7ecc09a4132 --- /dev/null +++ b/app/Exports/Controllers/ImportController.php @@ -0,0 +1,111 @@ +middleware(Permission::ContentImport->middleware()); + } + + /** + * Show the view to start a new import, and also list out the existing + * in progress imports that are visible to the user. + */ + public function start() + { + $imports = $this->imports->getVisibleImports(); + + $this->setPageTitle(trans('entities.import')); + + return view('exports.import', [ + 'imports' => $imports, + 'zipErrors' => session()->pull('validation_errors') ?? [], + ]); + } + + /** + * Upload, validate and store an import file. + */ + public function upload(Request $request) + { + $this->validate($request, [ + 'file' => ['required', ...AttachmentService::getFileValidationRules()] + ]); + + $file = $request->file('file'); + try { + $import = $this->imports->storeFromUpload($file); + } catch (ZipValidationException $exception) { + return redirect('/import')->with('validation_errors', $exception->errors); + } + + return redirect($import->getUrl()); + } + + /** + * Show a pending import, with a form to allow progressing + * with the import process. + */ + public function show(int $id) + { + $import = $this->imports->findVisible($id); + + $this->setPageTitle(trans('entities.import_continue')); + + return view('exports.import-show', [ + 'import' => $import, + 'data' => $import->decodeMetadata(), + ]); + } + + /** + * Run the import process against an uploaded import ZIP. + */ + public function run(int $id, Request $request) + { + $import = $this->imports->findVisible($id); + $parent = null; + + if ($import->type === 'page' || $import->type === 'chapter') { + session()->setPreviousUrl($import->getUrl()); + $data = $this->validate($request, [ + 'parent' => ['required', 'string'], + ]); + $parent = $data['parent']; + } + + try { + $entity = $this->imports->runImport($import, $parent); + } catch (ZipImportException $exception) { + session()->forget(['success', 'warning']); + $this->showErrorNotification(trans('errors.import_zip_failed_notification')); + return redirect($import->getUrl())->with('import_errors', $exception->errors); + } + + return redirect($entity->getUrl()); + } + + /** + * Delete an active pending import from the filesystem and database. + */ + public function delete(int $id) + { + $import = $this->imports->findVisible($id); + $this->imports->deleteImport($import); + + return redirect('/import'); + } +} diff --git a/app/Entities/Controllers/PageExportApiController.php b/app/Exports/Controllers/PageExportApiController.php similarity index 55% rename from app/Entities/Controllers/PageExportApiController.php rename to app/Exports/Controllers/PageExportApiController.php index d936a0de2b4..c5b18628924 100644 --- a/app/Entities/Controllers/PageExportApiController.php +++ b/app/Exports/Controllers/PageExportApiController.php @@ -1,20 +1,21 @@ exportFormatter = $exportFormatter; - $this->middleware('can:content-export'); + public function __construct( + protected ExportFormatter $exportFormatter, + protected PageQueries $queries, + ) { + $this->middleware(Permission::ContentExport->middleware()); } /** @@ -24,7 +25,7 @@ public function __construct(ExportFormatter $exportFormatter) */ public function exportPdf(int $id) { - $page = Page::visible()->findOrFail($id); + $page = $this->queries->findVisibleByIdOrFail($id); $pdfContent = $this->exportFormatter->pageToPdf($page); return $this->download()->directly($pdfContent, $page->slug . '.pdf'); @@ -37,7 +38,7 @@ public function exportPdf(int $id) */ public function exportHtml(int $id) { - $page = Page::visible()->findOrFail($id); + $page = $this->queries->findVisibleByIdOrFail($id); $htmlContent = $this->exportFormatter->pageToContainedHtml($page); return $this->download()->directly($htmlContent, $page->slug . '.html'); @@ -48,7 +49,7 @@ public function exportHtml(int $id) */ public function exportPlainText(int $id) { - $page = Page::visible()->findOrFail($id); + $page = $this->queries->findVisibleByIdOrFail($id); $textContent = $this->exportFormatter->pageToPlainText($page); return $this->download()->directly($textContent, $page->slug . '.txt'); @@ -59,9 +60,20 @@ public function exportPlainText(int $id) */ public function exportMarkdown(int $id) { - $page = Page::visible()->findOrFail($id); + $page = $this->queries->findVisibleByIdOrFail($id); $markdown = $this->exportFormatter->pageToMarkdown($page); return $this->download()->directly($markdown, $page->slug . '.md'); } + + /** + * Export a page as a contained ZIP file. + */ + public function exportZip(int $id, ZipExportBuilder $builder) + { + $page = $this->queries->findVisibleByIdOrFail($id); + $zip = $builder->buildForPage($page); + + return $this->download()->streamedFileDirectly($zip, $page->slug . '.zip', true); + } } diff --git a/app/Entities/Controllers/PageExportController.php b/app/Exports/Controllers/PageExportController.php similarity index 59% rename from app/Entities/Controllers/PageExportController.php rename to app/Exports/Controllers/PageExportController.php index 31862c8acc0..9bc79f24751 100644 --- a/app/Entities/Controllers/PageExportController.php +++ b/app/Exports/Controllers/PageExportController.php @@ -1,27 +1,24 @@ pageRepo = $pageRepo; - $this->exportFormatter = $exportFormatter; - $this->middleware('can:content-export'); + public function __construct( + protected PageQueries $queries, + protected ExportFormatter $exportFormatter, + ) { + $this->middleware(Permission::ContentExport->middleware()); + $this->middleware('throttle:exports'); } /** @@ -33,7 +30,7 @@ public function __construct(PageRepo $pageRepo, ExportFormatter $exportFormatter */ public function pdf(string $bookSlug, string $pageSlug) { - $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug); + $page = $this->queries->findVisibleBySlugsOrFail($bookSlug, $pageSlug); $page->html = (new PageContent($page))->render(); $pdfContent = $this->exportFormatter->pageToPdf($page); @@ -48,7 +45,7 @@ public function pdf(string $bookSlug, string $pageSlug) */ public function html(string $bookSlug, string $pageSlug) { - $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug); + $page = $this->queries->findVisibleBySlugsOrFail($bookSlug, $pageSlug); $page->html = (new PageContent($page))->render(); $containedHtml = $this->exportFormatter->pageToContainedHtml($page); @@ -62,7 +59,7 @@ public function html(string $bookSlug, string $pageSlug) */ public function plainText(string $bookSlug, string $pageSlug) { - $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug); + $page = $this->queries->findVisibleBySlugsOrFail($bookSlug, $pageSlug); $pageText = $this->exportFormatter->pageToPlainText($page); return $this->download()->directly($pageText, $pageSlug . '.txt'); @@ -75,9 +72,21 @@ public function plainText(string $bookSlug, string $pageSlug) */ public function markdown(string $bookSlug, string $pageSlug) { - $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug); + $page = $this->queries->findVisibleBySlugsOrFail($bookSlug, $pageSlug); $pageText = $this->exportFormatter->pageToMarkdown($page); return $this->download()->directly($pageText, $pageSlug . '.md'); } + + /** + * Export a page to a contained ZIP export file. + * @throws NotFoundException + */ + public function zip(string $bookSlug, string $pageSlug, ZipExportBuilder $builder) + { + $page = $this->queries->findVisibleBySlugsOrFail($bookSlug, $pageSlug); + $zip = $builder->buildForPage($page); + + return $this->download()->streamedFileDirectly($zip, $pageSlug . '.zip', true); + } } diff --git a/app/Entities/Tools/ExportFormatter.php b/app/Exports/ExportFormatter.php similarity index 96% rename from app/Entities/Tools/ExportFormatter.php rename to app/Exports/ExportFormatter.php index beddfe8e6e0..85ac7d2c9e9 100644 --- a/app/Entities/Tools/ExportFormatter.php +++ b/app/Exports/ExportFormatter.php @@ -1,11 +1,13 @@ name . "\n\n"; - $text .= $chapter->description . "\n\n"; + + $description = (new HtmlToMarkdown($chapter->descriptionHtml()))->convert(); + if ($description) { + $text .= $description . "\n\n"; + } + foreach ($chapter->pages as $page) { $text .= $this->pageToMarkdown($page) . "\n\n"; } @@ -330,6 +337,12 @@ public function bookToMarkdown(Book $book): string { $bookTree = (new BookContents($book))->getTree(false, true); $text = '# ' . $book->name . "\n\n"; + + $description = (new HtmlToMarkdown($book->descriptionHtml()))->convert(); + if ($description) { + $text .= $description . "\n\n"; + } + foreach ($bookTree as $bookChild) { if ($bookChild instanceof Chapter) { $text .= $this->chapterToMarkdown($bookChild) . "\n\n"; diff --git a/app/Exports/Import.php b/app/Exports/Import.php new file mode 100644 index 00000000000..ca4f529815f --- /dev/null +++ b/app/Exports/Import.php @@ -0,0 +1,68 @@ +size / 1000000, 2); + return "{$mb} MB"; + } + + /** + * Get the URL to view/continue this import. + */ + public function getUrl(string $path = ''): string + { + $path = ltrim($path, '/'); + return url("/import/{$this->id}" . ($path ? '/' . $path : '')); + } + + public function logDescriptor(): string + { + return "({$this->id}) {$this->name}"; + } + + public function createdBy(): BelongsTo + { + return $this->belongsTo(User::class, 'created_by'); + } + + public function decodeMetadata(): ZipExportBook|ZipExportChapter|ZipExportPage|null + { + $metadataArray = json_decode($this->metadata, true); + return match ($this->type) { + 'book' => ZipExportBook::fromArray($metadataArray), + 'chapter' => ZipExportChapter::fromArray($metadataArray), + 'page' => ZipExportPage::fromArray($metadataArray), + default => null, + }; + } +} diff --git a/app/Exports/ImportRepo.php b/app/Exports/ImportRepo.php new file mode 100644 index 00000000000..79db69fca8f --- /dev/null +++ b/app/Exports/ImportRepo.php @@ -0,0 +1,147 @@ + + */ + public function getVisibleImports(): Collection + { + return $this->queryVisible()->get(); + } + + /** + * @return Builder + */ + public function queryVisible(): Builder + { + $query = Import::query(); + + if (!userCan(Permission::SettingsManage)) { + $query->where('created_by', user()->id); + } + + return $query; + } + + public function findVisible(int $id): Import + { + $query = Import::query(); + + if (!userCan(Permission::SettingsManage)) { + $query->where('created_by', user()->id); + } + + return $query->findOrFail($id); + } + + /** + * @throws FileUploadException + * @throws ZipValidationException + * @throws ZipExportException + */ + public function storeFromUpload(UploadedFile $file): Import + { + $zipPath = $file->getRealPath(); + $reader = new ZipExportReader($zipPath); + + $errors = (new ZipExportValidator($reader))->validate(); + if ($errors) { + throw new ZipValidationException($errors); + } + + $exportModel = $reader->decodeDataToExportModel(); + + $import = new Import(); + $import->type = match (get_class($exportModel)) { + ZipExportPage::class => 'page', + ZipExportChapter::class => 'chapter', + ZipExportBook::class => 'book', + }; + + $import->name = $exportModel->name; + $import->created_by = user()->id; + $import->size = filesize($zipPath); + + $exportModel->metadataOnly(); + $import->metadata = json_encode($exportModel); + + $path = $this->storage->uploadFile( + $file, + 'uploads/files/imports/', + '', + 'zip' + ); + + $import->path = $path; + $import->save(); + + Activity::add(ActivityType::IMPORT_CREATE, $import); + + return $import; + } + + /** + * @throws ZipImportException + */ + public function runImport(Import $import, ?string $parent = null): Entity + { + $parentModel = null; + if ($import->type === 'page' || $import->type === 'chapter') { + $parentModel = $parent ? $this->entityQueries->findVisibleByStringIdentifier($parent) : null; + } + + DB::beginTransaction(); + try { + $model = $this->importer->run($import, $parentModel); + } catch (ZipImportException $e) { + DB::rollBack(); + $this->importer->revertStoredFiles(); + throw $e; + } + + DB::commit(); + $this->deleteImport($import); + Activity::add(ActivityType::IMPORT_RUN, $import); + + return $model; + } + + public function deleteImport(Import $import): void + { + $this->storage->delete($import->path); + $import->delete(); + + Activity::add(ActivityType::IMPORT_DELETE, $import); + } +} diff --git a/app/Exports/PdfGenerator.php b/app/Exports/PdfGenerator.php new file mode 100644 index 00000000000..f31d8aad078 --- /dev/null +++ b/app/Exports/PdfGenerator.php @@ -0,0 +1,149 @@ +getActiveEngine()) { + self::ENGINE_COMMAND => $this->renderUsingCommand($html), + self::ENGINE_WKHTML => $this->renderUsingWkhtml($html), + default => $this->renderUsingDomPdf($html) + }; + } + + /** + * Get the currently active PDF engine. + * Returns the value of an `ENGINE_` const on this class. + */ + public function getActiveEngine(): string + { + if (config('exports.pdf_command')) { + return self::ENGINE_COMMAND; + } + + if ($this->getWkhtmlBinaryPath() && config('app.allow_untrusted_server_fetching') === true) { + return self::ENGINE_WKHTML; + } + + return self::ENGINE_DOMPDF; + } + + protected function getWkhtmlBinaryPath(): string + { + $wkhtmlBinaryPath = config('exports.snappy.pdf_binary'); + if (file_exists(base_path('wkhtmltopdf'))) { + $wkhtmlBinaryPath = base_path('wkhtmltopdf'); + } + + return $wkhtmlBinaryPath ?: ''; + } + + protected function renderUsingDomPdf(string $html): string + { + $options = config('exports.dompdf'); + $domPdf = new Dompdf($options); + $domPdf->setBasePath(base_path('public')); + + $domPdf->loadHTML($this->convertEntities($html)); + $domPdf->render(); + + return (string) $domPdf->output(); + } + + /** + * @throws PdfExportException + */ + protected function renderUsingCommand(string $html): string + { + $command = config('exports.pdf_command'); + $inputHtml = tempnam(sys_get_temp_dir(), 'bs-pdfgen-html-'); + $outputPdf = tempnam(sys_get_temp_dir(), 'bs-pdfgen-output-'); + + $replacementsByPlaceholder = [ + '{input_html_path}' => $inputHtml, + '{output_pdf_path}' => $outputPdf, + ]; + + foreach ($replacementsByPlaceholder as $placeholder => $replacement) { + $command = str_replace($placeholder, escapeshellarg($replacement), $command); + } + + file_put_contents($inputHtml, $html); + + $timeout = intval(config('exports.pdf_command_timeout')); + $process = Process::fromShellCommandline($command); + $process->setTimeout($timeout); + + $cleanup = function () use ($inputHtml, $outputPdf) { + foreach ([$inputHtml, $outputPdf] as $file) { + if (file_exists($file)) { + unlink($file); + } + } + }; + + try { + $process->run(); + } catch (ProcessTimedOutException $e) { + $cleanup(); + throw new PdfExportException("PDF Export via command failed due to timeout at {$timeout} second(s)"); + } + + if (!$process->isSuccessful()) { + $cleanup(); + throw new PdfExportException("PDF Export via command failed with exit code {$process->getExitCode()}, stdout: {$process->getOutput()}, stderr: {$process->getErrorOutput()}"); + } + + $pdfContents = file_get_contents($outputPdf); + $cleanup(); + + if ($pdfContents === false) { + throw new PdfExportException("PDF Export via command failed, unable to read PDF output file"); + } else if (empty($pdfContents)) { + throw new PdfExportException("PDF Export via command failed, PDF output file is empty"); + } + + return $pdfContents; + } + + protected function renderUsingWkhtml(string $html): string + { + $snappy = new SnappyPdf($this->getWkhtmlBinaryPath()); + $options = config('exports.snappy.options'); + return $snappy->getOutputFromHtml($html, $options); + } + + /** + * Taken from https://github.com/barryvdh/laravel-dompdf/blob/v2.1.1/src/PDF.php + * Copyright (c) 2021 barryvdh, MIT License + * https://github.com/barryvdh/laravel-dompdf/blob/v2.1.1/LICENSE + */ + protected function convertEntities(string $subject): string + { + $entities = [ + '€' => '€', + '£' => '£', + ]; + + foreach ($entities as $search => $replace) { + $subject = str_replace($search, $replace, $subject); + } + return $subject; + } +} diff --git a/app/Exports/ZipExports/Models/ZipExportAttachment.php b/app/Exports/ZipExports/Models/ZipExportAttachment.php new file mode 100644 index 00000000000..97995738ffe --- /dev/null +++ b/app/Exports/ZipExports/Models/ZipExportAttachment.php @@ -0,0 +1,66 @@ +link = $this->file = null; + } + + public static function fromModel(Attachment $model, ZipExportFiles $files): self + { + $instance = new self(); + $instance->id = $model->id; + $instance->name = $model->name; + + if ($model->external) { + $instance->link = $model->path; + } else { + $instance->file = $files->referenceForAttachment($model); + } + + return $instance; + } + + public static function fromModelArray(array $attachmentArray, ZipExportFiles $files): array + { + return array_values(array_map(function (Attachment $attachment) use ($files) { + return self::fromModel($attachment, $files); + }, $attachmentArray)); + } + + public static function validate(ZipValidationHelper $context, array $data): array + { + $rules = [ + 'id' => ['nullable', 'int', $context->uniqueIdRule('attachment')], + 'name' => ['required', 'string', 'min:1'], + 'link' => ['required_without:file', 'nullable', 'string'], + 'file' => ['required_without:link', 'nullable', 'string', $context->fileReferenceRule()], + ]; + + return $context->validateData($data, $rules); + } + + public static function fromArray(array $data): static + { + $model = new static(); + + $model->id = $data['id'] ?? null; + $model->name = $data['name']; + $model->link = $data['link'] ?? null; + $model->file = $data['file'] ?? null; + + return $model; + } +} diff --git a/app/Exports/ZipExports/Models/ZipExportBook.php b/app/Exports/ZipExports/Models/ZipExportBook.php new file mode 100644 index 00000000000..6c51ea3379c --- /dev/null +++ b/app/Exports/ZipExports/Models/ZipExportBook.php @@ -0,0 +1,118 @@ +description_html = $this->cover = null; + + foreach ($this->chapters as $chapter) { + $chapter->metadataOnly(); + } + foreach ($this->pages as $page) { + $page->metadataOnly(); + } + foreach ($this->tags as $tag) { + $tag->metadataOnly(); + } + } + + public function children(): array + { + $children = [ + ...$this->pages, + ...$this->chapters, + ]; + + usort($children, function ($a, $b) { + return ($a->priority ?? 0) - ($b->priority ?? 0); + }); + + return $children; + } + + public static function fromModel(Book $model, ZipExportFiles $files): self + { + $instance = new self(); + $instance->id = $model->id; + $instance->name = $model->name; + $instance->description_html = $model->descriptionHtml(); + + if ($model->cover) { + $instance->cover = $files->referenceForImage($model->cover); + } + + $instance->tags = ZipExportTag::fromModelArray($model->tags()->get()->all()); + + $chapters = []; + $pages = []; + + $children = $model->getDirectVisibleChildren()->all(); + foreach ($children as $child) { + if ($child instanceof Chapter) { + $chapters[] = $child; + } else if ($child instanceof Page && !$child->draft) { + $pages[] = $child; + } + } + + $instance->pages = ZipExportPage::fromModelArray($pages, $files); + $instance->chapters = ZipExportChapter::fromModelArray($chapters, $files); + + return $instance; + } + + public static function validate(ZipValidationHelper $context, array $data): array + { + $rules = [ + 'id' => ['nullable', 'int', $context->uniqueIdRule('book')], + 'name' => ['required', 'string', 'min:1'], + 'description_html' => ['nullable', 'string'], + 'cover' => ['nullable', 'string', $context->fileReferenceRule()], + 'tags' => ['array'], + 'pages' => ['array'], + 'chapters' => ['array'], + ]; + + $errors = $context->validateData($data, $rules); + $errors['tags'] = $context->validateRelations($data['tags'] ?? [], ZipExportTag::class); + $errors['pages'] = $context->validateRelations($data['pages'] ?? [], ZipExportPage::class); + $errors['chapters'] = $context->validateRelations($data['chapters'] ?? [], ZipExportChapter::class); + + return $errors; + } + + public static function fromArray(array $data): static + { + $model = new static(); + + $model->id = $data['id'] ?? null; + $model->name = $data['name']; + $model->description_html = $data['description_html'] ?? null; + $model->cover = $data['cover'] ?? null; + $model->tags = ZipExportTag::fromManyArray($data['tags'] ?? []); + $model->pages = ZipExportPage::fromManyArray($data['pages'] ?? []); + $model->chapters = ZipExportChapter::fromManyArray($data['chapters'] ?? []); + + return $model; + } +} diff --git a/app/Exports/ZipExports/Models/ZipExportChapter.php b/app/Exports/ZipExports/Models/ZipExportChapter.php new file mode 100644 index 00000000000..260191a3e78 --- /dev/null +++ b/app/Exports/ZipExports/Models/ZipExportChapter.php @@ -0,0 +1,95 @@ +description_html = null; + + foreach ($this->pages as $page) { + $page->metadataOnly(); + } + foreach ($this->tags as $tag) { + $tag->metadataOnly(); + } + } + + public function children(): array + { + return $this->pages; + } + + public static function fromModel(Chapter $model, ZipExportFiles $files): self + { + $instance = new self(); + $instance->id = $model->id; + $instance->name = $model->name; + $instance->description_html = $model->descriptionHtml(); + $instance->priority = $model->priority; + $instance->tags = ZipExportTag::fromModelArray($model->tags()->get()->all()); + + $pages = $model->getVisiblePages()->filter(fn (Page $page) => !$page->draft)->all(); + $instance->pages = ZipExportPage::fromModelArray($pages, $files); + + return $instance; + } + + /** + * @param Chapter[] $chapterArray + * @return self[] + */ + public static function fromModelArray(array $chapterArray, ZipExportFiles $files): array + { + return array_values(array_map(function (Chapter $chapter) use ($files) { + return self::fromModel($chapter, $files); + }, $chapterArray)); + } + + public static function validate(ZipValidationHelper $context, array $data): array + { + $rules = [ + 'id' => ['nullable', 'int', $context->uniqueIdRule('chapter')], + 'name' => ['required', 'string', 'min:1'], + 'description_html' => ['nullable', 'string'], + 'priority' => ['nullable', 'int'], + 'tags' => ['array'], + 'pages' => ['array'], + ]; + + $errors = $context->validateData($data, $rules); + $errors['tags'] = $context->validateRelations($data['tags'] ?? [], ZipExportTag::class); + $errors['pages'] = $context->validateRelations($data['pages'] ?? [], ZipExportPage::class); + + return $errors; + } + + public static function fromArray(array $data): static + { + $model = new static(); + + $model->id = $data['id'] ?? null; + $model->name = $data['name']; + $model->description_html = $data['description_html'] ?? null; + $model->priority = isset($data['priority']) ? intval($data['priority']) : null; + $model->tags = ZipExportTag::fromManyArray($data['tags'] ?? []); + $model->pages = ZipExportPage::fromManyArray($data['pages'] ?? []); + + return $model; + } +} diff --git a/app/Exports/ZipExports/Models/ZipExportImage.php b/app/Exports/ZipExports/Models/ZipExportImage.php new file mode 100644 index 00000000000..4c71af0c3a2 --- /dev/null +++ b/app/Exports/ZipExports/Models/ZipExportImage.php @@ -0,0 +1,57 @@ +id = $model->id; + $instance->name = $model->name; + $instance->type = $model->type; + $instance->file = $files->referenceForImage($model); + + return $instance; + } + + public function metadataOnly(): void + { + // + } + + public static function validate(ZipValidationHelper $context, array $data): array + { + $acceptedImageTypes = ['image/png', 'image/jpeg', 'image/gif', 'image/webp']; + $rules = [ + 'id' => ['nullable', 'int', $context->uniqueIdRule('image')], + 'name' => ['required', 'string', 'min:1'], + 'file' => ['required', 'string', $context->fileReferenceRule($acceptedImageTypes)], + 'type' => ['required', 'string', Rule::in(['gallery', 'drawio'])], + ]; + + return $context->validateData($data, $rules); + } + + public static function fromArray(array $data): static + { + $model = new static(); + + $model->id = $data['id'] ?? null; + $model->name = $data['name']; + $model->file = $data['file']; + $model->type = $data['type']; + + return $model; + } +} diff --git a/app/Exports/ZipExports/Models/ZipExportModel.php b/app/Exports/ZipExports/Models/ZipExportModel.php new file mode 100644 index 00000000000..38001c628c6 --- /dev/null +++ b/app/Exports/ZipExports/Models/ZipExportModel.php @@ -0,0 +1,57 @@ +__invoke($this); + return array_filter($publicProps, fn ($value) => $value !== null); + } + + /** + * Validate the given array of data intended for this model. + * Return an array of validation errors messages. + * Child items can be considered in the validation result by returning a keyed + * item in the array for its own validation messages. + */ + abstract public static function validate(ZipValidationHelper $context, array $data): array; + + /** + * Decode the array of data into this export model. + */ + abstract public static function fromArray(array $data): static; + + /** + * Decode an array of array data into an array of export models. + * @param array[] $data + * @return static[] + */ + public static function fromManyArray(array $data): array + { + $results = []; + foreach ($data as $item) { + $results[] = static::fromArray($item); + } + return $results; + } + + /** + * Remove additional content in this model to reduce it down + * to just essential id/name values for identification. + * + * The result of this may be something that does not pass validation, but is + * simple for the purpose of creating a contents. + */ + abstract public function metadataOnly(): void; +} diff --git a/app/Exports/ZipExports/Models/ZipExportPage.php b/app/Exports/ZipExports/Models/ZipExportPage.php new file mode 100644 index 00000000000..6de7f9446f3 --- /dev/null +++ b/app/Exports/ZipExports/Models/ZipExportPage.php @@ -0,0 +1,104 @@ +html = $this->markdown = null; + + foreach ($this->attachments as $attachment) { + $attachment->metadataOnly(); + } + foreach ($this->images as $image) { + $image->metadataOnly(); + } + foreach ($this->tags as $tag) { + $tag->metadataOnly(); + } + } + + public static function fromModel(Page $model, ZipExportFiles $files): self + { + $instance = new self(); + $instance->id = $model->id; + $instance->name = $model->name; + $instance->html = (new PageContent($model))->render(); + $instance->priority = $model->priority; + + if (!empty($model->markdown)) { + $instance->markdown = $model->markdown; + } + + $instance->tags = ZipExportTag::fromModelArray($model->tags()->get()->all()); + $instance->attachments = ZipExportAttachment::fromModelArray($model->attachments()->get()->all(), $files); + + return $instance; + } + + /** + * @param Page[] $pageArray + * @return self[] + */ + public static function fromModelArray(array $pageArray, ZipExportFiles $files): array + { + return array_values(array_map(function (Page $page) use ($files) { + return self::fromModel($page, $files); + }, $pageArray)); + } + + public static function validate(ZipValidationHelper $context, array $data): array + { + $rules = [ + 'id' => ['nullable', 'int', $context->uniqueIdRule('page')], + 'name' => ['required', 'string', 'min:1'], + 'html' => ['nullable', 'string'], + 'markdown' => ['nullable', 'string'], + 'priority' => ['nullable', 'int'], + 'attachments' => ['array'], + 'images' => ['array'], + 'tags' => ['array'], + ]; + + $errors = $context->validateData($data, $rules); + $errors['attachments'] = $context->validateRelations($data['attachments'] ?? [], ZipExportAttachment::class); + $errors['images'] = $context->validateRelations($data['images'] ?? [], ZipExportImage::class); + $errors['tags'] = $context->validateRelations($data['tags'] ?? [], ZipExportTag::class); + + return $errors; + } + + public static function fromArray(array $data): static + { + $model = new static(); + + $model->id = $data['id'] ?? null; + $model->name = $data['name']; + $model->html = $data['html'] ?? null; + $model->markdown = $data['markdown'] ?? null; + $model->priority = isset($data['priority']) ? intval($data['priority']) : null; + $model->attachments = ZipExportAttachment::fromManyArray($data['attachments'] ?? []); + $model->images = ZipExportImage::fromManyArray($data['images'] ?? []); + $model->tags = ZipExportTag::fromManyArray($data['tags'] ?? []); + + return $model; + } +} diff --git a/app/Exports/ZipExports/Models/ZipExportTag.php b/app/Exports/ZipExports/Models/ZipExportTag.php new file mode 100644 index 00000000000..8ac7f3c4d72 --- /dev/null +++ b/app/Exports/ZipExports/Models/ZipExportTag.php @@ -0,0 +1,51 @@ +value = null; + } + + public static function fromModel(Tag $model): self + { + $instance = new self(); + $instance->name = $model->name; + $instance->value = $model->value; + + return $instance; + } + + public static function fromModelArray(array $tagArray): array + { + return array_values(array_map(self::fromModel(...), $tagArray)); + } + + public static function validate(ZipValidationHelper $context, array $data): array + { + $rules = [ + 'name' => ['required', 'string', 'min:1'], + 'value' => ['nullable', 'string'], + ]; + + return $context->validateData($data, $rules); + } + + public static function fromArray(array $data): static + { + $model = new static(); + + $model->name = $data['name']; + $model->value = $data['value'] ?? null; + + return $model; + } +} diff --git a/app/Exports/ZipExports/ZipExportBuilder.php b/app/Exports/ZipExports/ZipExportBuilder.php new file mode 100644 index 00000000000..1a7d88020dd --- /dev/null +++ b/app/Exports/ZipExports/ZipExportBuilder.php @@ -0,0 +1,118 @@ +files); + $this->data['page'] = $exportPage; + + $this->references->addPage($exportPage); + + return $this->build(); + } + + /** + * @throws ZipExportException + */ + public function buildForChapter(Chapter $chapter): string + { + $exportChapter = ZipExportChapter::fromModel($chapter, $this->files); + $this->data['chapter'] = $exportChapter; + + $this->references->addChapter($exportChapter); + + return $this->build(); + } + + /** + * @throws ZipExportException + */ + public function buildForBook(Book $book): string + { + $exportBook = ZipExportBook::fromModel($book, $this->files); + $this->data['book'] = $exportBook; + + $this->references->addBook($exportBook); + + return $this->build(); + } + + /** + * @throws ZipExportException + */ + protected function build(): string + { + $this->references->buildReferences($this->files); + + $this->data['exported_at'] = date(DATE_ATOM); + $this->data['instance'] = [ + 'id' => setting('instance-id', ''), + 'version' => AppVersion::get(), + ]; + + $zipFile = tempnam(sys_get_temp_dir(), 'bszip-'); + $zip = new ZipArchive(); + $opened = $zip->open($zipFile, ZipArchive::OVERWRITE); + if ($opened !== true) { + throw new ZipExportException('Failed to create zip file for export.'); + } + + $zip->addFromString('data.json', json_encode($this->data)); + $zip->addEmptyDir('files'); + + $toRemove = []; + $addedNames = []; + + try { + $this->files->extractEach(function ($filePath, $fileRef) use ($zip, &$toRemove, &$addedNames) { + $entryName = "files/$fileRef"; + $zip->addFile($filePath, $entryName); + $toRemove[] = $filePath; + $addedNames[] = $entryName; + }); + } catch (\Exception $exception) { + // Cleanup the files we've processed so far and respond back with error + foreach ($toRemove as $file) { + unlink($file); + } + foreach ($addedNames as $name) { + $zip->deleteName($name); + } + $zip->close(); + unlink($zipFile); + throw new ZipExportException("Failed to add files for ZIP export, received error: " . $exception->getMessage()); + } + + $zip->close(); + + foreach ($toRemove as $file) { + unlink($file); + } + + return $zipFile; + } +} diff --git a/app/Exports/ZipExports/ZipExportFiles.php b/app/Exports/ZipExports/ZipExportFiles.php new file mode 100644 index 00000000000..8f0a6bd4031 --- /dev/null +++ b/app/Exports/ZipExports/ZipExportFiles.php @@ -0,0 +1,107 @@ + + */ + protected array $attachmentRefsById = []; + + /** + * References for images by image ID. + * @var array + */ + protected array $imageRefsById = []; + + public function __construct( + protected AttachmentService $attachmentService, + protected ImageService $imageService, + ) { + } + + /** + * Gain a reference to the given attachment instance. + * This is expected to be a file-based attachment that the user + * has visibility of, no permission/access checks are performed here. + */ + public function referenceForAttachment(Attachment $attachment): string + { + if (isset($this->attachmentRefsById[$attachment->id])) { + return $this->attachmentRefsById[$attachment->id]; + } + + $existingFiles = $this->getAllFileNames(); + do { + $fileName = Str::random(20) . '.' . $attachment->extension; + } while (in_array($fileName, $existingFiles)); + + $this->attachmentRefsById[$attachment->id] = $fileName; + + return $fileName; + } + + /** + * Gain a reference to the given image instance. + * This is expected to be an image that the user has visibility of, + * no permission/access checks are performed here. + */ + public function referenceForImage(Image $image): string + { + if (isset($this->imageRefsById[$image->id])) { + return $this->imageRefsById[$image->id]; + } + + $existingFiles = $this->getAllFileNames(); + $extension = pathinfo($image->path, PATHINFO_EXTENSION); + do { + $fileName = Str::random(20) . '.' . $extension; + } while (in_array($fileName, $existingFiles)); + + $this->imageRefsById[$image->id] = $fileName; + + return $fileName; + } + + protected function getAllFileNames(): array + { + return array_merge( + array_values($this->attachmentRefsById), + array_values($this->imageRefsById), + ); + } + + /** + * Extract each of the ZIP export tracked files. + * Calls the given callback for each tracked file, passing a temporary + * file reference of the file contents, and the zip-local tracked reference. + */ + public function extractEach(callable $callback): void + { + foreach ($this->attachmentRefsById as $attachmentId => $ref) { + $attachment = Attachment::query()->find($attachmentId); + $stream = $this->attachmentService->streamAttachmentFromStorage($attachment); + $tmpFile = tempnam(sys_get_temp_dir(), 'bszipfile-'); + $tmpFileStream = fopen($tmpFile, 'w'); + stream_copy_to_stream($stream, $tmpFileStream); + $callback($tmpFile, $ref); + } + + foreach ($this->imageRefsById as $imageId => $ref) { + $image = Image::query()->find($imageId); + $stream = $this->imageService->getImageStream($image); + $tmpFile = tempnam(sys_get_temp_dir(), 'bszipimage-'); + $tmpFileStream = fopen($tmpFile, 'w'); + stream_copy_to_stream($stream, $tmpFileStream); + $callback($tmpFile, $ref); + } + } +} diff --git a/app/Exports/ZipExports/ZipExportReader.php b/app/Exports/ZipExports/ZipExportReader.php new file mode 100644 index 00000000000..c3d5c23cfec --- /dev/null +++ b/app/Exports/ZipExports/ZipExportReader.php @@ -0,0 +1,111 @@ +zip = new ZipArchive(); + } + + /** + * @throws ZipExportException + */ + protected function open(): void + { + if ($this->open) { + return; + } + + // Validate file exists + if (!file_exists($this->zipPath) || !is_readable($this->zipPath)) { + throw new ZipExportException(trans('errors.import_zip_cant_read')); + } + + // Validate file is valid zip + $opened = $this->zip->open($this->zipPath, ZipArchive::RDONLY); + if ($opened !== true) { + throw new ZipExportException(trans('errors.import_zip_cant_read')); + } + + $this->open = true; + } + + public function close(): void + { + if ($this->open) { + $this->zip->close(); + $this->open = false; + } + } + + /** + * @throws ZipExportException + */ + public function readData(): array + { + $this->open(); + + // Validate json data exists, including metadata + $jsonData = $this->zip->getFromName('data.json') ?: ''; + $importData = json_decode($jsonData, true); + if (!$importData) { + throw new ZipExportException(trans('errors.import_zip_cant_decode_data')); + } + + return $importData; + } + + public function fileExists(string $fileName): bool + { + return $this->zip->statName("files/{$fileName}") !== false; + } + + /** + * @return false|resource + */ + public function streamFile(string $fileName) + { + return $this->zip->getStream("files/{$fileName}"); + } + + /** + * Sniff the mime type from the file of given name. + */ + public function sniffFileMime(string $fileName): string + { + $stream = $this->streamFile($fileName); + $sniffContent = fread($stream, 2000); + + return (new WebSafeMimeSniffer())->sniff($sniffContent); + } + + /** + * @throws ZipExportException + */ + public function decodeDataToExportModel(): ZipExportBook|ZipExportChapter|ZipExportPage + { + $data = $this->readData(); + if (isset($data['book'])) { + return ZipExportBook::fromArray($data['book']); + } else if (isset($data['chapter'])) { + return ZipExportChapter::fromArray($data['chapter']); + } else if (isset($data['page'])) { + return ZipExportPage::fromArray($data['page']); + } + + throw new ZipExportException("Could not identify content in ZIP file data."); + } +} diff --git a/app/Exports/ZipExports/ZipExportReferences.php b/app/Exports/ZipExports/ZipExportReferences.php new file mode 100644 index 00000000000..64107cf21aa --- /dev/null +++ b/app/Exports/ZipExports/ZipExportReferences.php @@ -0,0 +1,161 @@ + */ + protected array $pages = []; + /** @var array */ + protected array $chapters = []; + /** @var array */ + protected array $books = []; + + /** @var array */ + protected array $attachments = []; + + /** @var array */ + protected array $images = []; + + public function __construct( + protected ZipReferenceParser $parser, + ) { + } + + public function addPage(ZipExportPage $page): void + { + if ($page->id) { + $this->pages[$page->id] = $page; + } + + foreach ($page->attachments as $attachment) { + if ($attachment->id) { + $this->attachments[$attachment->id] = $attachment; + } + } + } + + public function addChapter(ZipExportChapter $chapter): void + { + if ($chapter->id) { + $this->chapters[$chapter->id] = $chapter; + } + + foreach ($chapter->pages as $page) { + $this->addPage($page); + } + } + + public function addBook(ZipExportBook $book): void + { + if ($book->id) { + $this->books[$book->id] = $book; + } + + foreach ($book->pages as $page) { + $this->addPage($page); + } + + foreach ($book->chapters as $chapter) { + $this->addChapter($chapter); + } + } + + public function buildReferences(ZipExportFiles $files): void + { + $createHandler = function (ZipExportModel $zipModel) use ($files) { + return function (Model $model) use ($files, $zipModel) { + return $this->handleModelReference($model, $zipModel, $files); + }; + }; + + // Parse page content first + foreach ($this->pages as $page) { + $handler = $createHandler($page); + $page->html = $this->parser->parseLinks($page->html ?? '', $handler); + if ($page->markdown) { + $page->markdown = $this->parser->parseLinks($page->markdown, $handler); + } + } + + // Parse chapter description HTML + foreach ($this->chapters as $chapter) { + if ($chapter->description_html) { + $handler = $createHandler($chapter); + $chapter->description_html = $this->parser->parseLinks($chapter->description_html, $handler); + } + } + + // Parse book description HTML + foreach ($this->books as $book) { + if ($book->description_html) { + $handler = $createHandler($book); + $book->description_html = $this->parser->parseLinks($book->description_html, $handler); + } + } + } + + protected function handleModelReference(Model $model, ZipExportModel $exportModel, ZipExportFiles $files): ?string + { + // Handle attachment references + // No permission check needed here since they would only already exist in this + // reference context if already allowed via their entity access. + if ($model instanceof Attachment) { + if (isset($this->attachments[$model->id])) { + return "[[bsexport:attachment:{$model->id}]]"; + } + return null; + } + + // Handle image references + if ($model instanceof Image) { + // Only handle gallery and drawio images + if ($model->type !== 'gallery' && $model->type !== 'drawio') { + return null; + } + + // Handle simple links outside of page content + if (!($exportModel instanceof ZipExportPage) && isset($this->images[$model->id])) { + return "[[bsexport:image:{$model->id}]]"; + } + + // Find and include images if in visibility + $page = $model->getPage(); + $pageExportModel = $this->pages[$page->id] ?? ($exportModel instanceof ZipExportPage ? $exportModel : null); + if (isset($this->images[$model->id]) || ($page && $pageExportModel && userCan(Permission::PageView, $page))) { + if (!isset($this->images[$model->id])) { + $exportImage = ZipExportImage::fromModel($model, $files); + $this->images[$model->id] = $exportImage; + $pageExportModel->images[] = $exportImage; + } + return "[[bsexport:image:{$model->id}]]"; + } + return null; + } + + // Handle entity references + if ($model instanceof Book && isset($this->books[$model->id])) { + return "[[bsexport:book:{$model->id}]]"; + } else if ($model instanceof Chapter && isset($this->chapters[$model->id])) { + return "[[bsexport:chapter:{$model->id}]]"; + } else if ($model instanceof Page && isset($this->pages[$model->id])) { + return "[[bsexport:page:{$model->id}]]"; + } + + return null; + } +} diff --git a/app/Exports/ZipExports/ZipExportValidator.php b/app/Exports/ZipExports/ZipExportValidator.php new file mode 100644 index 00000000000..889804f2013 --- /dev/null +++ b/app/Exports/ZipExports/ZipExportValidator.php @@ -0,0 +1,57 @@ +reader->readData(); + } catch (ZipExportException $exception) { + return ['format' => $exception->getMessage()]; + } + + $helper = new ZipValidationHelper($this->reader); + + if (isset($importData['book'])) { + $modelErrors = ZipExportBook::validate($helper, $importData['book']); + $keyPrefix = 'book'; + } else if (isset($importData['chapter'])) { + $modelErrors = ZipExportChapter::validate($helper, $importData['chapter']); + $keyPrefix = 'chapter'; + } else if (isset($importData['page'])) { + $modelErrors = ZipExportPage::validate($helper, $importData['page']); + $keyPrefix = 'page'; + } else { + return ['format' => trans('errors.import_zip_no_data')]; + } + + return $this->flattenModelErrors($modelErrors, $keyPrefix); + } + + protected function flattenModelErrors(array $errors, string $keyPrefix): array + { + $flattened = []; + + foreach ($errors as $key => $error) { + if (is_array($error)) { + $flattened = array_merge($flattened, $this->flattenModelErrors($error, $keyPrefix . '.' . $key)); + } else { + $flattened[$keyPrefix . '.' . $key] = $error; + } + } + + return $flattened; + } +} diff --git a/app/Exports/ZipExports/ZipFileReferenceRule.php b/app/Exports/ZipExports/ZipFileReferenceRule.php new file mode 100644 index 00000000000..90e78c060b0 --- /dev/null +++ b/app/Exports/ZipExports/ZipFileReferenceRule.php @@ -0,0 +1,37 @@ +context->zipReader->fileExists($value)) { + $fail('validation.zip_file')->translate(); + } + + if (!empty($this->acceptedMimes)) { + $fileMime = $this->context->zipReader->sniffFileMime($value); + if (!in_array($fileMime, $this->acceptedMimes)) { + $fail('validation.zip_file_mime')->translate([ + 'attribute' => $attribute, + 'validTypes' => implode(',', $this->acceptedMimes), + 'foundType' => $fileMime + ]); + } + } + } +} diff --git a/app/Exports/ZipExports/ZipImportReferences.php b/app/Exports/ZipExports/ZipImportReferences.php new file mode 100644 index 00000000000..662dfdf6c5b --- /dev/null +++ b/app/Exports/ZipExports/ZipImportReferences.php @@ -0,0 +1,182 @@ + + */ + protected array $referenceMap = []; + + /** @var array */ + protected array $zipExportPageMap = []; + /** @var array */ + protected array $zipExportChapterMap = []; + /** @var array */ + protected array $zipExportBookMap = []; + + public function __construct( + protected ZipReferenceParser $parser, + protected BaseRepo $baseRepo, + protected PageRepo $pageRepo, + protected ImageResizer $imageResizer, + ) { + } + + protected function addReference(string $type, Model $model, ?int $importId): void + { + if ($importId) { + $key = $type . ':' . $importId; + $this->referenceMap[$key] = $model; + } + } + + public function addPage(Page $page, ZipExportPage $exportPage): void + { + $this->pages[] = $page; + $this->zipExportPageMap[$page->id] = $exportPage; + $this->addReference('page', $page, $exportPage->id); + } + + public function addChapter(Chapter $chapter, ZipExportChapter $exportChapter): void + { + $this->chapters[] = $chapter; + $this->zipExportChapterMap[$chapter->id] = $exportChapter; + $this->addReference('chapter', $chapter, $exportChapter->id); + } + + public function addBook(Book $book, ZipExportBook $exportBook): void + { + $this->books[] = $book; + $this->zipExportBookMap[$book->id] = $exportBook; + $this->addReference('book', $book, $exportBook->id); + } + + public function addAttachment(Attachment $attachment, ?int $importId): void + { + $this->attachments[] = $attachment; + $this->addReference('attachment', $attachment, $importId); + } + + public function addImage(Image $image, ?int $importId): void + { + $this->images[] = $image; + $this->addReference('image', $image, $importId); + } + + protected function handleReference(string $type, int $id): ?string + { + $key = $type . ':' . $id; + $model = $this->referenceMap[$key] ?? null; + if ($model instanceof Entity) { + return $model->getUrl(); + } else if ($model instanceof Image) { + if ($model->type === 'gallery') { + $this->imageResizer->loadGalleryThumbnailsForImage($model, false); + return $model->thumbs['display'] ?? $model->url; + } + + return $model->url; + } else if ($model instanceof Attachment) { + return $model->getUrl(false); + } + + return null; + } + + protected function replaceDrawingIdReferences(string $content): string + { + $referenceRegex = '/\sdrawio-diagram=[\'"](\d+)[\'"]/'; + + $result = preg_replace_callback($referenceRegex, function ($matches) { + $key = 'image:' . $matches[1]; + $model = $this->referenceMap[$key] ?? null; + if ($model instanceof Image && $model->type === 'drawio') { + return ' drawio-diagram="' . $model->id . '"'; + } + return $matches[0]; + }, $content); + + return $result ?: $content; + } + + public function replaceReferences(): void + { + foreach ($this->books as $book) { + $exportBook = $this->zipExportBookMap[$book->id]; + $content = $exportBook->description_html ?? ''; + $parsed = $this->parser->parseReferences($content, $this->handleReference(...)); + + $this->baseRepo->update($book, [ + 'description_html' => $parsed, + ]); + } + + foreach ($this->chapters as $chapter) { + $exportChapter = $this->zipExportChapterMap[$chapter->id]; + $content = $exportChapter->description_html ?? ''; + $parsed = $this->parser->parseReferences($content, $this->handleReference(...)); + + $this->baseRepo->update($chapter, [ + 'description_html' => $parsed, + ]); + } + + foreach ($this->pages as $page) { + $exportPage = $this->zipExportPageMap[$page->id]; + $contentType = $exportPage->markdown ? 'markdown' : 'html'; + $content = $exportPage->markdown ?: ($exportPage->html ?: ''); + + $parsed = $this->parser->parseReferences($content, $this->handleReference(...)); + $parsed = $this->replaceDrawingIdReferences($parsed); + + $this->pageRepo->setContentFromInput($page, [ + $contentType => $parsed, + ]); + } + } + + + /** + * @return Image[] + */ + public function images(): array + { + return $this->images; + } + + /** + * @return Attachment[] + */ + public function attachments(): array + { + return $this->attachments; + } +} diff --git a/app/Exports/ZipExports/ZipImportRunner.php b/app/Exports/ZipExports/ZipImportRunner.php new file mode 100644 index 00000000000..eafb527e87e --- /dev/null +++ b/app/Exports/ZipExports/ZipImportRunner.php @@ -0,0 +1,365 @@ +getZipPath($import); + $reader = new ZipExportReader($zipPath); + + $errors = (new ZipExportValidator($reader))->validate(); + if ($errors) { + throw new ZipImportException([ + trans('errors.import_validation_failed'), + ...$errors, + ]); + } + + try { + $exportModel = $reader->decodeDataToExportModel(); + } catch (ZipExportException $e) { + throw new ZipImportException([$e->getMessage()]); + } + + // Validate parent type + if ($exportModel instanceof ZipExportBook && ($parent !== null)) { + throw new ZipImportException(["Must not have a parent set for a Book import."]); + } else if ($exportModel instanceof ZipExportChapter && !($parent instanceof Book)) { + throw new ZipImportException(["Parent book required for chapter import."]); + } else if ($exportModel instanceof ZipExportPage && !($parent instanceof Book || $parent instanceof Chapter)) { + throw new ZipImportException(["Parent book or chapter required for page import."]); + } + + $this->ensurePermissionsPermitImport($exportModel, $parent); + + if ($exportModel instanceof ZipExportBook) { + $entity = $this->importBook($exportModel, $reader); + } else if ($exportModel instanceof ZipExportChapter) { + $entity = $this->importChapter($exportModel, $parent, $reader); + } else if ($exportModel instanceof ZipExportPage) { + $entity = $this->importPage($exportModel, $parent, $reader); + } else { + throw new ZipImportException(['No importable data found in import data.']); + } + + $this->references->replaceReferences(); + + $reader->close(); + $this->cleanup(); + + return $entity; + } + + /** + * Revert any files which have been stored during this import process. + * Considers files only, and avoids the database under the + * assumption that the database may already have been + * reverted as part of a transaction rollback. + */ + public function revertStoredFiles(): void + { + foreach ($this->references->images() as $image) { + $this->imageService->destroyFileAtPath($image->type, $image->path); + } + + foreach ($this->references->attachments() as $attachment) { + if (!$attachment->external) { + $this->attachmentService->deleteFileInStorage($attachment); + } + } + + $this->cleanup(); + } + + protected function cleanup(): void + { + foreach ($this->tempFilesToCleanup as $file) { + unlink($file); + } + + $this->tempFilesToCleanup = []; + } + + protected function importBook(ZipExportBook $exportBook, ZipExportReader $reader): Book + { + $book = $this->bookRepo->create([ + 'name' => $exportBook->name, + 'description_html' => $exportBook->description_html ?? '', + 'image' => $exportBook->cover ? $this->zipFileToUploadedFile($exportBook->cover, $reader) : null, + 'tags' => $this->exportTagsToInputArray($exportBook->tags ?? []), + ]); + + if ($book->cover) { + $this->references->addImage($book->cover, null); + } + + $children = [ + ...$exportBook->chapters, + ...$exportBook->pages, + ]; + + usort($children, function (ZipExportPage|ZipExportChapter $a, ZipExportPage|ZipExportChapter $b) { + return ($a->priority ?? 0) - ($b->priority ?? 0); + }); + + foreach ($children as $child) { + if ($child instanceof ZipExportChapter) { + $this->importChapter($child, $book, $reader); + } else if ($child instanceof ZipExportPage) { + $this->importPage($child, $book, $reader); + } + } + + $this->references->addBook($book, $exportBook); + + return $book; + } + + protected function importChapter(ZipExportChapter $exportChapter, Book $parent, ZipExportReader $reader): Chapter + { + $chapter = $this->chapterRepo->create([ + 'name' => $exportChapter->name, + 'description_html' => $exportChapter->description_html ?? '', + 'tags' => $this->exportTagsToInputArray($exportChapter->tags ?? []), + ], $parent); + + $exportPages = $exportChapter->pages; + usort($exportPages, function (ZipExportPage $a, ZipExportPage $b) { + return ($a->priority ?? 0) - ($b->priority ?? 0); + }); + + foreach ($exportPages as $exportPage) { + $this->importPage($exportPage, $chapter, $reader); + } + + $this->references->addChapter($chapter, $exportChapter); + + return $chapter; + } + + protected function importPage(ZipExportPage $exportPage, Book|Chapter $parent, ZipExportReader $reader): Page + { + $page = $this->pageRepo->getNewDraftPage($parent); + + foreach ($exportPage->attachments as $exportAttachment) { + $this->importAttachment($exportAttachment, $page, $reader); + } + + foreach ($exportPage->images as $exportImage) { + $this->importImage($exportImage, $page, $reader); + } + + $this->pageRepo->publishDraft($page, [ + 'name' => $exportPage->name, + 'markdown' => $exportPage->markdown, + 'html' => $exportPage->html, + 'tags' => $this->exportTagsToInputArray($exportPage->tags ?? []), + ]); + + $this->references->addPage($page, $exportPage); + + return $page; + } + + protected function importAttachment(ZipExportAttachment $exportAttachment, Page $page, ZipExportReader $reader): Attachment + { + if ($exportAttachment->file) { + $file = $this->zipFileToUploadedFile($exportAttachment->file, $reader); + $attachment = $this->attachmentService->saveNewUpload($file, $page->id); + $attachment->name = $exportAttachment->name; + $attachment->save(); + } else { + $attachment = $this->attachmentService->saveNewFromLink( + $exportAttachment->name, + $exportAttachment->link ?? '', + $page->id, + ); + } + + $this->references->addAttachment($attachment, $exportAttachment->id); + + return $attachment; + } + + protected function importImage(ZipExportImage $exportImage, Page $page, ZipExportReader $reader): Image + { + $mime = $reader->sniffFileMime($exportImage->file); + $extension = explode('/', $mime)[1]; + + $file = $this->zipFileToUploadedFile($exportImage->file, $reader); + $image = $this->imageService->saveNewFromUpload( + $file, + $exportImage->type, + $page->id, + null, + null, + true, + $exportImage->name . '.' . $extension, + ); + + $image->name = $exportImage->name; + $image->save(); + + $this->references->addImage($image, $exportImage->id); + + return $image; + } + + protected function exportTagsToInputArray(array $exportTags): array + { + $tags = []; + + /** @var ZipExportTag $tag */ + foreach ($exportTags as $tag) { + $tags[] = ['name' => $tag->name, 'value' => $tag->value ?? '']; + } + + return $tags; + } + + protected function zipFileToUploadedFile(string $fileName, ZipExportReader $reader): UploadedFile + { + $tempPath = tempnam(sys_get_temp_dir(), 'bszipextract'); + $fileStream = $reader->streamFile($fileName); + $tempStream = fopen($tempPath, 'wb'); + stream_copy_to_stream($fileStream, $tempStream); + fclose($tempStream); + + $this->tempFilesToCleanup[] = $tempPath; + + return new UploadedFile($tempPath, $fileName); + } + + /** + * @throws ZipImportException + */ + protected function ensurePermissionsPermitImport(ZipExportPage|ZipExportChapter|ZipExportBook $exportModel, Book|Chapter|null $parent = null): void + { + $errors = []; + + $chapters = []; + $pages = []; + $images = []; + $attachments = []; + + if ($exportModel instanceof ZipExportBook) { + if (!userCan(Permission::BookCreateAll)) { + $errors[] = trans('errors.import_perms_books'); + } + array_push($pages, ...$exportModel->pages); + array_push($chapters, ...$exportModel->chapters); + } else if ($exportModel instanceof ZipExportChapter) { + $chapters[] = $exportModel; + } else if ($exportModel instanceof ZipExportPage) { + $pages[] = $exportModel; + } + + foreach ($chapters as $chapter) { + array_push($pages, ...$chapter->pages); + } + + if (count($chapters) > 0) { + $permission = 'chapter-create' . ($parent ? '' : '-all'); + if (!userCan($permission, $parent)) { + $errors[] = trans('errors.import_perms_chapters'); + } + } + + foreach ($pages as $page) { + array_push($attachments, ...$page->attachments); + array_push($images, ...$page->images); + } + + if (count($pages) > 0) { + if ($parent) { + if (!userCan(Permission::PageCreate, $parent)) { + $errors[] = trans('errors.import_perms_pages'); + } + } else { + $hasPermission = userCan(Permission::PageCreateAll) || userCan(Permission::PageCreateOwn); + if (!$hasPermission) { + $errors[] = trans('errors.import_perms_pages'); + } + } + } + + if (count($images) > 0) { + if (!userCan(Permission::ImageCreateAll)) { + $errors[] = trans('errors.import_perms_images'); + } + } + + if (count($attachments) > 0) { + if (!userCan(Permission::AttachmentCreateAll)) { + $errors[] = trans('errors.import_perms_attachments'); + } + } + + if (count($errors)) { + throw new ZipImportException($errors); + } + } + + protected function getZipPath(Import $import): string + { + if (!$this->storage->isRemote()) { + return $this->storage->getSystemPath($import->path); + } + + $tempFilePath = tempnam(sys_get_temp_dir(), 'bszip-import-'); + $tempFile = fopen($tempFilePath, 'wb'); + $stream = $this->storage->getReadStream($import->path); + stream_copy_to_stream($stream, $tempFile); + fclose($tempFile); + + $this->tempFilesToCleanup[] = $tempFilePath; + + return $tempFilePath; + } +} diff --git a/app/Exports/ZipExports/ZipReferenceParser.php b/app/Exports/ZipExports/ZipReferenceParser.php new file mode 100644 index 00000000000..a6560e3f289 --- /dev/null +++ b/app/Exports/ZipExports/ZipReferenceParser.php @@ -0,0 +1,140 @@ +getLinkRegex(); + $matches = []; + preg_match_all($linkRegex, $content, $matches); + + if (count($matches) < 2) { + return $content; + } + + foreach ($matches[1] as $link) { + $model = $this->linkToModel($link); + if ($model) { + $result = $handler($model); + if ($result !== null) { + $content = str_replace($link, $result, $content); + } + } + } + + return $content; + } + + /** + * Parse and replace references in the given content. + * Calls the handler for each reference detected and replaces the link + * with the handler return value if provided. + * Returns the resulting content string with references replaced. + * @param callable(string $type, int $id):(string|null) $handler + */ + public function parseReferences(string $content, callable $handler): string + { + $referenceRegex = '/\[\[bsexport:([a-z]+):(\d+)]]/'; + $matches = []; + preg_match_all($referenceRegex, $content, $matches); + + if (count($matches) < 3) { + return $content; + } + + for ($i = 0; $i < count($matches[0]); $i++) { + $referenceText = $matches[0][$i]; + $type = strtolower($matches[1][$i]); + $id = intval($matches[2][$i]); + $result = $handler($type, $id); + if ($result !== null) { + $content = str_replace($referenceText, $result, $content); + } + } + + return $content; + } + + + /** + * Attempt to resolve the given link to a model using the instance model resolvers. + */ + protected function linkToModel(string $link): ?Model + { + foreach ($this->getModelResolvers() as $resolver) { + $model = $resolver->resolve($link); + if (!is_null($model)) { + return $model; + } + } + + return null; + } + + protected function getModelResolvers(): array + { + if (isset($this->modelResolvers)) { + return $this->modelResolvers; + } + + $this->modelResolvers = [ + new PagePermalinkModelResolver($this->queries->pages), + new PageLinkModelResolver($this->queries->pages), + new ChapterLinkModelResolver($this->queries->chapters), + new BookLinkModelResolver($this->queries->books), + new ImageModelResolver(), + new AttachmentModelResolver(), + ]; + + return $this->modelResolvers; + } + + /** + * Build the regex to identify links we should handle in content. + */ + protected function getLinkRegex(): string + { + $urls = [rtrim(url('/'), '/')]; + $imageUrl = rtrim(ImageStorage::getPublicUrl('/'), '/'); + if ($urls[0] !== $imageUrl) { + $urls[] = $imageUrl; + } + + + $urlBaseRegex = implode('|', array_map(function ($url) { + return preg_quote($url, '/'); + }, $urls)); + + return "/(({$urlBaseRegex}).*?)[\\t\\n\\f>\"'=?#()]/"; + } +} diff --git a/app/Exports/ZipExports/ZipUniqueIdRule.php b/app/Exports/ZipExports/ZipUniqueIdRule.php new file mode 100644 index 00000000000..ea2b2539296 --- /dev/null +++ b/app/Exports/ZipExports/ZipUniqueIdRule.php @@ -0,0 +1,26 @@ +context->hasIdBeenUsed($this->modelType, $value)) { + $fail('validation.zip_unique')->translate(['attribute' => $attribute]); + } + } +} diff --git a/app/Exports/ZipExports/ZipValidationHelper.php b/app/Exports/ZipExports/ZipValidationHelper.php new file mode 100644 index 00000000000..fd9cd784472 --- /dev/null +++ b/app/Exports/ZipExports/ZipValidationHelper.php @@ -0,0 +1,77 @@ +:". Example: "book:2") + * which we can use to check uniqueness. + * @var array + */ + protected array $validatedIds = []; + + public function __construct( + public ZipExportReader $zipReader, + ) { + $this->validationFactory = app(Factory::class); + } + + public function validateData(array $data, array $rules): array + { + $messages = $this->validationFactory->make($data, $rules)->errors()->messages(); + + foreach ($messages as $key => $message) { + $messages[$key] = implode("\n", $message); + } + + return $messages; + } + + public function fileReferenceRule(array $acceptedMimes = []): ZipFileReferenceRule + { + return new ZipFileReferenceRule($this, $acceptedMimes); + } + + public function uniqueIdRule(string $type): ZipUniqueIdRule + { + return new ZipUniqueIdRule($this, $type); + } + + public function hasIdBeenUsed(string $type, mixed $id): bool + { + $key = $type . ':' . $id; + if (isset($this->validatedIds[$key])) { + return true; + } + + $this->validatedIds[$key] = true; + + return false; + } + + /** + * Validate an array of relation data arrays that are expected + * to be for the given ZipExportModel. + * @param class-string $model + */ + public function validateRelations(array $relations, string $model): array + { + $results = []; + + foreach ($relations as $key => $relationData) { + if (is_array($relationData)) { + $results[$key] = $model::validate($this, $relationData); + } else { + $results[$key] = [trans('validation.zip_model_expected', ['type' => gettype($relationData)])]; + } + } + + return $results; + } +} diff --git a/app/Http/ApiController.php b/app/Http/ApiController.php index c0dbe2fca4a..1a92afa3380 100644 --- a/app/Http/ApiController.php +++ b/app/Http/ApiController.php @@ -8,7 +8,7 @@ abstract class ApiController extends Controller { - protected $rules = []; + protected array $rules = []; /** * Provide a paginated listing JSON response in a standard format diff --git a/app/Http/Controller.php b/app/Http/Controller.php index 8facf5dab3c..5d3be4951ca 100644 --- a/app/Http/Controller.php +++ b/app/Http/Controller.php @@ -6,6 +6,7 @@ use BookStack\App\Model; use BookStack\Exceptions\NotifyException; use BookStack\Facades\Activity; +use BookStack\Permissions\Permission; use Illuminate\Foundation\Bus\DispatchesJobs; use Illuminate\Foundation\Validation\ValidatesRequests; use Illuminate\Http\JsonResponse; @@ -27,10 +28,9 @@ protected function isSignedIn(): bool } /** - * Stops the application and shows a permission error if - * the application is in demo mode. + * Stops the application and shows a permission error if the application is in demo mode. */ - protected function preventAccessInDemoMode() + protected function preventAccessInDemoMode(): void { if (config('app.env') === 'demo') { $this->showPermissionError(); @@ -40,28 +40,27 @@ protected function preventAccessInDemoMode() /** * Adds the page title into the view. */ - public function setPageTitle(string $title) + public function setPageTitle(string $title): void { view()->share('pageTitle', $title); } /** - * On a permission error redirect to home and display. - * the error as a notification. + * On a permission error redirect to home and display the error as a notification. * - * @return never + * @throws NotifyException */ - protected function showPermissionError() + protected function showPermissionError(string $redirectLocation = '/'): never { $message = request()->wantsJson() ? trans('errors.permissionJson') : trans('errors.permission'); - throw new NotifyException($message, '/', 403); + throw new NotifyException($message, $redirectLocation, 403); } /** * Checks that the current user has the given permission otherwise throw an exception. */ - protected function checkPermission(string $permission): void + protected function checkPermission(string|Permission $permission): void { if (!user() || !user()->can($permission)) { $this->showPermissionError(); @@ -81,10 +80,10 @@ protected function preventGuestAccess(): void /** * Check the current user's permissions against an ownable item otherwise throw an exception. */ - protected function checkOwnablePermission(string $permission, Model $ownable): void + protected function checkOwnablePermission(string|Permission $permission, Model $ownable, string $redirectLocation = '/'): void { if (!userCan($permission, $ownable)) { - $this->showPermissionError(); + $this->showPermissionError($redirectLocation); } } @@ -92,7 +91,7 @@ protected function checkOwnablePermission(string $permission, Model $ownable): v * Check if a user has a permission or bypass the permission * check if the given callback resolves true. */ - protected function checkPermissionOr(string $permission, callable $callback): void + protected function checkPermissionOr(string|Permission $permission, callable $callback): void { if ($callback() !== true) { $this->checkPermission($permission); @@ -103,7 +102,7 @@ protected function checkPermissionOr(string $permission, callable $callback): vo * Check if the current user has a permission or bypass if the provided user * id matches the current user. */ - protected function checkPermissionOrCurrentUser(string $permission, int $userId): void + protected function checkPermissionOrCurrentUser(string|Permission $permission, int $userId): void { $this->checkPermissionOr($permission, function () use ($userId) { return $userId === user()->id; @@ -111,7 +110,7 @@ protected function checkPermissionOrCurrentUser(string $permission, int $userId) } /** - * Send back a json error message. + * Send back a JSON error message. */ protected function jsonError(string $messageText = '', int $statusCode = 500): JsonResponse { @@ -127,7 +126,7 @@ protected function download(): DownloadResponseFactory } /** - * Show a positive, successful notification to the user on next view load. + * Show a positive, successful notification to the user on the next view load. */ protected function showSuccessNotification(string $message): void { @@ -135,7 +134,7 @@ protected function showSuccessNotification(string $message): void } /** - * Show a warning notification to the user on next view load. + * Show a warning notification to the user on the next view load. */ protected function showWarningNotification(string $message): void { @@ -143,7 +142,7 @@ protected function showWarningNotification(string $message): void } /** - * Show an error notification to the user on next view load. + * Show an error notification to the user on the next view load. */ protected function showErrorNotification(string $message): void { @@ -152,10 +151,8 @@ protected function showErrorNotification(string $message): void /** * Log an activity in the system. - * - * @param string|Loggable $detail */ - protected function logActivity(string $type, $detail = ''): void + protected function logActivity(string $type, string|Loggable $detail = ''): void { Activity::add($type, $detail); } @@ -165,7 +162,7 @@ protected function logActivity(string $type, $detail = ''): void */ protected function getImageValidationRules(): array { - return ['image_extension', 'mimes:jpeg,png,gif,webp', 'max:' . (config('app.upload_limit') * 1000)]; + return ['image_extension', 'mimes:jpeg,png,gif,webp,avif', 'max:' . (config('app.upload_limit') * 1000)]; } /** diff --git a/app/Http/DownloadResponseFactory.php b/app/Http/DownloadResponseFactory.php index 20032f52534..8384484ad62 100644 --- a/app/Http/DownloadResponseFactory.php +++ b/app/Http/DownloadResponseFactory.php @@ -2,18 +2,15 @@ namespace BookStack\Http; -use BookStack\Util\WebSafeMimeSniffer; use Illuminate\Http\Request; use Illuminate\Http\Response; use Symfony\Component\HttpFoundation\StreamedResponse; class DownloadResponseFactory { - protected Request $request; - - public function __construct(Request $request) - { - $this->request = $request; + public function __construct( + protected Request $request, + ) { } /** @@ -21,55 +18,95 @@ public function __construct(Request $request) */ public function directly(string $content, string $fileName): Response { - return response()->make($content, 200, $this->getHeaders($fileName)); + return response()->make($content, 200, $this->getHeaders($fileName, strlen($content))); } /** * Create a response that forces a download, from a given stream of content. */ - public function streamedDirectly($stream, string $fileName): StreamedResponse + public function streamedDirectly($stream, string $fileName, int $fileSize): StreamedResponse { - return response()->stream(function () use ($stream) { - - // End & flush the output buffer, if we're in one, otherwise we still use memory. - // Output buffer may or may not exist depending on PHP `output_buffering` setting. - // Ignore in testing since output buffers are used to gather a response. - if (!empty(ob_get_status()) && !app()->runningUnitTests()) { - ob_end_clean(); - } - - fpassthru($stream); - fclose($stream); - }, 200, $this->getHeaders($fileName)); + $rangeStream = new RangeSupportedStream($stream, $fileSize, $this->request); + $headers = array_merge($this->getHeaders($fileName, $fileSize), $rangeStream->getResponseHeaders()); + return response()->stream( + fn() => $rangeStream->outputAndClose(), + $rangeStream->getResponseStatus(), + $headers, + ); } + /** + * Create a response that downloads the given file via a stream. + * Has the option to delete the provided file once the stream is closed. + */ + public function streamedFileDirectly(string $filePath, string $fileName, bool $deleteAfter = false): StreamedResponse + { + $fileSize = filesize($filePath); + $stream = fopen($filePath, 'r'); + + if ($deleteAfter) { + // Delete the given file if it still exists after the app terminates + $callback = function () use ($filePath) { + if (file_exists($filePath)) { + unlink($filePath); + } + }; + + // We watch both app terminate and php shutdown to cover both normal app termination + // as well as other potential scenarios (connection termination). + app()->terminating($callback); + register_shutdown_function($callback); + } + + return $this->streamedDirectly($stream, $fileName, $fileSize); + } + + /** * Create a file download response that provides the file with a content-type * correct for the file, in a way so the browser can show the content in browser, * for a given content stream. */ - public function streamedInline($stream, string $fileName): StreamedResponse + public function streamedInline($stream, string $fileName, int $fileSize): StreamedResponse { - $sniffContent = fread($stream, 2000); - $mime = (new WebSafeMimeSniffer())->sniff($sniffContent); - - return response()->stream(function () use ($sniffContent, $stream) { - echo $sniffContent; - fpassthru($stream); - fclose($stream); - }, 200, $this->getHeaders($fileName, $mime)); + $rangeStream = new RangeSupportedStream($stream, $fileSize, $this->request); + $mime = $rangeStream->sniffMime(pathinfo($fileName, PATHINFO_EXTENSION)); + $headers = array_merge($this->getHeaders($fileName, $fileSize, $mime), $rangeStream->getResponseHeaders()); + + return response()->stream( + fn() => $rangeStream->outputAndClose(), + $rangeStream->getResponseStatus(), + $headers, + ); + } + + /** + * Create a response that provides the given file via a stream with detected content-type. + * Has the option to delete the provided file once the stream is closed. + */ + public function streamedFileInline(string $filePath, ?string $fileName = null): StreamedResponse + { + $fileSize = filesize($filePath); + $stream = fopen($filePath, 'r'); + + if ($fileName === null) { + $fileName = basename($filePath); + } + + return $this->streamedInline($stream, $fileName, $fileSize); } /** * Get the common headers to provide for a download response. */ - protected function getHeaders(string $fileName, string $mime = 'application/octet-stream'): array + protected function getHeaders(string $fileName, int $fileSize, string $mime = 'application/octet-stream'): array { $disposition = ($mime === 'application/octet-stream') ? 'attachment' : 'inline'; $downloadName = str_replace('"', '', $fileName); return [ 'Content-Type' => $mime, + 'Content-Length' => $fileSize, 'Content-Disposition' => "{$disposition}; filename=\"{$downloadName}\"", 'X-Content-Type-Options' => 'nosniff', ]; diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index 1b96ff3db66..00bf8cbe1c5 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -9,6 +9,8 @@ class Kernel extends HttpKernel /** * The application's global HTTP middleware stack. * These middleware are run during every request to your application. + * + * @var list */ protected $middleware = [ \BookStack\Http\Middleware\PreventRequestsDuringMaintenance::class, @@ -21,14 +23,14 @@ class Kernel extends HttpKernel /** * The application's route middleware groups. * - * @var array + * @var array> */ protected $middlewareGroups = [ 'web' => [ \BookStack\Http\Middleware\ApplyCspRules::class, \BookStack\Http\Middleware\EncryptCookies::class, \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, - \Illuminate\Session\Middleware\StartSession::class, + \BookStack\Http\Middleware\StartSessionExtended::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, \BookStack\Http\Middleware\VerifyCsrfToken::class, \BookStack\Http\Middleware\CheckEmailConfirmed::class, @@ -45,11 +47,11 @@ class Kernel extends HttpKernel ]; /** - * The application's route middleware. + * The application's middleware aliases. * - * @var array + * @var array */ - protected $routeMiddleware = [ + protected $middlewareAliases = [ 'auth' => \BookStack\Http\Middleware\Authenticate::class, 'can' => \BookStack\Http\Middleware\CheckUserHasPermission::class, 'guest' => \BookStack\Http\Middleware\RedirectIfAuthenticated::class, diff --git a/app/Http/Middleware/ApiAuthenticate.php b/app/Http/Middleware/ApiAuthenticate.php index 5f3ad3168a0..15b5a325a10 100644 --- a/app/Http/Middleware/ApiAuthenticate.php +++ b/app/Http/Middleware/ApiAuthenticate.php @@ -3,6 +3,7 @@ namespace BookStack\Http\Middleware; use BookStack\Exceptions\ApiAuthException; +use BookStack\Permissions\Permission; use Closure; use Illuminate\Http\Request; @@ -51,7 +52,7 @@ protected function ensureAuthorizedBySessionOrToken(): void */ protected function sessionUserHasApiAccess(): bool { - $hasApiPermission = user()->can('access-api'); + $hasApiPermission = user()->can(Permission::AccessApi); return $hasApiPermission && user()->hasAppAccess(); } diff --git a/app/Http/Middleware/CheckUserHasPermission.php b/app/Http/Middleware/CheckUserHasPermission.php index b5678e734fb..ea4ff652ae8 100644 --- a/app/Http/Middleware/CheckUserHasPermission.php +++ b/app/Http/Middleware/CheckUserHasPermission.php @@ -2,6 +2,7 @@ namespace BookStack\Http\Middleware; +use BookStack\Permissions\Permission; use Closure; use Illuminate\Http\Request; @@ -10,13 +11,9 @@ class CheckUserHasPermission /** * Handle an incoming request. * - * @param \Illuminate\Http\Request $request - * @param \Closure $next - * @param string $permission - * * @return mixed */ - public function handle($request, Closure $next, $permission) + public function handle(Request $request, Closure $next, string|Permission $permission) { if (!user()->can($permission)) { return $this->errorResponse($request); diff --git a/app/Http/Middleware/EncryptCookies.php b/app/Http/Middleware/EncryptCookies.php index 484c15ae87e..2d7d5d9d4e0 100644 --- a/app/Http/Middleware/EncryptCookies.php +++ b/app/Http/Middleware/EncryptCookies.php @@ -9,7 +9,7 @@ class EncryptCookies extends Middleware /** * The names of the cookies that should not be encrypted. * - * @var array + * @var array */ protected $except = [ // diff --git a/app/Http/Middleware/PreventRequestsDuringMaintenance.php b/app/Http/Middleware/PreventRequestsDuringMaintenance.php index dfb9592e106..c1a02c8cc1a 100644 --- a/app/Http/Middleware/PreventRequestsDuringMaintenance.php +++ b/app/Http/Middleware/PreventRequestsDuringMaintenance.php @@ -9,7 +9,7 @@ class PreventRequestsDuringMaintenance extends Middleware /** * The URIs that should be reachable while maintenance mode is enabled. * - * @var array + * @var array */ protected $except = [ // diff --git a/app/Http/Middleware/PreventResponseCaching.php b/app/Http/Middleware/PreventResponseCaching.php index c763b5fc1bb..a40150444b5 100644 --- a/app/Http/Middleware/PreventResponseCaching.php +++ b/app/Http/Middleware/PreventResponseCaching.php @@ -7,6 +7,13 @@ class PreventResponseCaching { + /** + * Paths to ignore when preventing response caching. + */ + protected array $ignoredPathPrefixes = [ + 'theme/', + ]; + /** * Handle an incoming request. * @@ -20,6 +27,13 @@ public function handle($request, Closure $next) /** @var Response $response */ $response = $next($request); + $path = $request->path(); + foreach ($this->ignoredPathPrefixes as $ignoredPath) { + if (str_starts_with($path, $ignoredPath)) { + return $response; + } + } + $response->headers->set('Cache-Control', 'no-cache, no-store, private'); $response->headers->set('Expires', 'Sun, 12 Jul 2015 19:01:00 GMT'); diff --git a/app/Http/Middleware/RedirectIfAuthenticated.php b/app/Http/Middleware/RedirectIfAuthenticated.php index 069973a95b7..745f7196df3 100644 --- a/app/Http/Middleware/RedirectIfAuthenticated.php +++ b/app/Http/Middleware/RedirectIfAuthenticated.php @@ -6,19 +6,16 @@ use Closure; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; +use Symfony\Component\HttpFoundation\Response; class RedirectIfAuthenticated { /** * Handle an incoming request. * - * @param \Illuminate\Http\Request $request - * @param \Closure $next - * @param string|null ...$guards - * - * @return mixed + * @param Closure(Request): (Response) $next */ - public function handle(Request $request, Closure $next, ...$guards) + public function handle(Request $request, Closure $next, string ...$guards): Response { $guards = empty($guards) ? [null] : $guards; diff --git a/app/Http/Middleware/StartSessionExtended.php b/app/Http/Middleware/StartSessionExtended.php new file mode 100644 index 00000000000..26cd250ac34 --- /dev/null +++ b/app/Http/Middleware/StartSessionExtended.php @@ -0,0 +1,34 @@ +path()); + foreach (static::$pathPrefixesExcludedFromHistory as $excludedPath) { + if (str_starts_with($requestPath, $excludedPath)) { + return; + } + } + + parent::storeCurrentUrl($request, $session); + } +} diff --git a/app/Http/Middleware/ThrottleApiRequests.php b/app/Http/Middleware/ThrottleApiRequests.php index c63d0c6033d..cef2d3906ef 100644 --- a/app/Http/Middleware/ThrottleApiRequests.php +++ b/app/Http/Middleware/ThrottleApiRequests.php @@ -9,7 +9,7 @@ class ThrottleApiRequests extends Middleware /** * Resolve the number of attempts if the user is authenticated or not. */ - protected function resolveMaxAttempts($request, $maxAttempts) + protected function resolveMaxAttempts($request, $maxAttempts): int { return (int) config('api.requests_per_minute'); } diff --git a/app/Http/Middleware/TrimStrings.php b/app/Http/Middleware/TrimStrings.php index cbdc88fb9b7..ec5725d67c7 100644 --- a/app/Http/Middleware/TrimStrings.php +++ b/app/Http/Middleware/TrimStrings.php @@ -9,7 +9,7 @@ class TrimStrings extends Middleware /** * The names of the attributes that should not be trimmed. * - * @var array + * @var array */ protected $except = [ 'password', diff --git a/app/Http/Middleware/TrustHosts.php b/app/Http/Middleware/TrustHosts.php index 7bd89ee51de..c13d631fed9 100644 --- a/app/Http/Middleware/TrustHosts.php +++ b/app/Http/Middleware/TrustHosts.php @@ -9,9 +9,9 @@ class TrustHosts extends Middleware /** * Get the host patterns that should be trusted. * - * @return array + * @return array */ - public function hosts() + public function hosts(): array { return [ $this->allSubdomainsOfApplicationUrl(), diff --git a/app/Http/Middleware/TrustProxies.php b/app/Http/Middleware/TrustProxies.php index 0fe0247b829..ecf6cb18aa2 100644 --- a/app/Http/Middleware/TrustProxies.php +++ b/app/Http/Middleware/TrustProxies.php @@ -11,7 +11,7 @@ class TrustProxies extends Middleware /** * The trusted proxies for this application. * - * @var array + * @var array|string|null */ protected $proxies; diff --git a/app/Http/Middleware/VerifyCsrfToken.php b/app/Http/Middleware/VerifyCsrfToken.php index 804a22bc09a..7c8689b8bcd 100644 --- a/app/Http/Middleware/VerifyCsrfToken.php +++ b/app/Http/Middleware/VerifyCsrfToken.php @@ -16,7 +16,7 @@ class VerifyCsrfToken extends Middleware /** * The URIs that should be excluded from CSRF verification. * - * @var array + * @var array */ protected $except = [ 'saml2/*', diff --git a/app/Http/RangeSupportedStream.php b/app/Http/RangeSupportedStream.php new file mode 100644 index 00000000000..c4b00778939 --- /dev/null +++ b/app/Http/RangeSupportedStream.php @@ -0,0 +1,134 @@ +responseLength = $this->fileSize; + $this->parseRequest($request); + } + + /** + * Sniff a mime type from the stream. + */ + public function sniffMime(string $extension = ''): string + { + $offset = min(2000, $this->fileSize); + $this->sniffContent = fread($this->stream, $offset); + + return (new WebSafeMimeSniffer())->sniff($this->sniffContent, $extension); + } + + /** + * Output the current stream to stdout before closing out the stream. + */ + public function outputAndClose(): void + { + // End & flush the output buffer, if we're in one, otherwise we still use memory. + // Output buffer may or may not exist depending on PHP `output_buffering` setting. + // Ignore in testing since output buffers are used to gather a response. + if (!empty(ob_get_status()) && !app()->runningUnitTests()) { + ob_end_clean(); + } + + $outStream = fopen('php://output', 'w'); + $sniffLength = strlen($this->sniffContent); + $bytesToWrite = $this->responseLength; + + if ($sniffLength > 0 && $this->responseOffset < $sniffLength) { + $sniffEnd = min($sniffLength, $bytesToWrite + $this->responseOffset); + $sniffOutLength = $sniffEnd - $this->responseOffset; + $sniffOutput = substr($this->sniffContent, $this->responseOffset, $sniffOutLength); + fwrite($outStream, $sniffOutput); + $bytesToWrite -= $sniffOutLength; + } else if ($this->responseOffset !== 0) { + fseek($this->stream, $this->responseOffset); + } + + stream_copy_to_stream($this->stream, $outStream, $bytesToWrite); + + fclose($this->stream); + fclose($outStream); + } + + public function getResponseHeaders(): array + { + return $this->responseHeaders; + } + + public function getResponseStatus(): int + { + return $this->responseStatus; + } + + protected function parseRequest(Request $request): void + { + $this->responseHeaders['Accept-Ranges'] = $request->isMethodSafe() ? 'bytes' : 'none'; + + $range = $this->getRangeFromRequest($request); + if ($range) { + [$start, $end] = $range; + if ($start < 0 || $start > $end) { + $this->responseStatus = 416; + $this->responseHeaders['Content-Range'] = sprintf('bytes */%s', $this->fileSize); + } else { + $this->responseLength = $end < $this->fileSize ? $end - $start + 1 : -1; + $this->responseOffset = $start; + $this->responseStatus = 206; + $this->responseHeaders['Content-Range'] = sprintf('bytes %s-%s/%s', $start, $end, $this->fileSize); + $this->responseHeaders['Content-Length'] = $end - $start + 1; + } + } + + if ($request->isMethod('HEAD')) { + $this->responseLength = 0; + } + } + + protected function getRangeFromRequest(Request $request): ?array + { + $range = $request->headers->get('Range'); + if (!$range || !$request->isMethod('GET') || !str_starts_with($range, 'bytes=')) { + return null; + } + + if ($request->headers->has('If-Range')) { + return null; + } + + [$start, $end] = explode('-', substr($range, 6), 2) + [0]; + + $end = ('' === $end) ? $this->fileSize - 1 : (int) $end; + + if ('' === $start) { + $start = $this->fileSize - $end; + $end = $this->fileSize - 1; + } else { + $start = (int) $start; + } + + $end = min($end, $this->fileSize - 1); + return [$start, $end]; + } +} diff --git a/app/Permissions/ContentPermissionApiController.php b/app/Permissions/ContentPermissionApiController.php index 23b75db359a..0720cc4782b 100644 --- a/app/Permissions/ContentPermissionApiController.php +++ b/app/Permissions/ContentPermissionApiController.php @@ -16,7 +16,7 @@ public function __construct( ) { } - protected $rules = [ + protected array $rules = [ 'update' => [ 'owner_id' => ['int'], @@ -51,7 +51,7 @@ public function read(string $contentType, string $contentId) $entity = $this->entities->get($contentType) ->newQuery()->scopes(['visible'])->findOrFail($contentId); - $this->checkOwnablePermission('restrictions-manage', $entity); + $this->checkOwnablePermission(Permission::RestrictionsManage, $entity); return response()->json($this->formattedPermissionDataForEntity($entity)); } @@ -71,7 +71,7 @@ public function update(Request $request, string $contentType, string $contentId) $entity = $this->entities->get($contentType) ->newQuery()->scopes(['visible'])->findOrFail($contentId); - $this->checkOwnablePermission('restrictions-manage', $entity); + $this->checkOwnablePermission(Permission::RestrictionsManage, $entity); $data = $this->validate($request, $this->rules()['update']); $this->permissionsUpdater->updateFromApiRequestData($entity, $data); diff --git a/app/Permissions/EntityPermissionEvaluator.php b/app/Permissions/EntityPermissionEvaluator.php index 98ec0330649..30c97b700c6 100644 --- a/app/Permissions/EntityPermissionEvaluator.php +++ b/app/Permissions/EntityPermissionEvaluator.php @@ -30,7 +30,7 @@ public function evaluateEntityForUser(Entity $entity, array $userRoleIds): ?bool } /** - * @param array> $permitsByType + * @param array> $permitsByType */ protected function evaluatePermitsByType(array $permitsByType): ?int { @@ -50,7 +50,7 @@ protected function evaluatePermitsByType(array $permitsByType): ?int /** * @param string[] $typeIdChain * @param array $permissionMapByTypeId - * @return array> + * @return array> */ protected function collapseAndCategorisePermissions(array $typeIdChain, array $permissionMapByTypeId): array { diff --git a/app/Permissions/JointPermissionBuilder.php b/app/Permissions/JointPermissionBuilder.php index 8c961fb1306..56b22ad1604 100644 --- a/app/Permissions/JointPermissionBuilder.php +++ b/app/Permissions/JointPermissionBuilder.php @@ -4,10 +4,10 @@ use BookStack\Entities\Models\Book; use BookStack\Entities\Models\BookChild; -use BookStack\Entities\Models\Bookshelf; use BookStack\Entities\Models\Chapter; use BookStack\Entities\Models\Entity; use BookStack\Entities\Models\Page; +use BookStack\Entities\Queries\EntityQueries; use BookStack\Permissions\Models\JointPermission; use BookStack\Users\Models\Role; use Illuminate\Database\Eloquent\Builder; @@ -20,10 +20,16 @@ */ class JointPermissionBuilder { + public function __construct( + protected EntityQueries $queries, + ) { + } + + /** * Re-generate all entity permission from scratch. */ - public function rebuildForAll() + public function rebuildForAll(): void { JointPermission::query()->truncate(); @@ -36,7 +42,7 @@ public function rebuildForAll() }); // Chunk through all bookshelves - Bookshelf::query()->withTrashed()->select(['id', 'owned_by']) + $this->queries->shelves->start()->withTrashed()->select(['id', 'owned_by']) ->chunk(50, function (EloquentCollection $shelves) use ($roles) { $this->createManyJointPermissions($shelves->all(), $roles); }); @@ -45,7 +51,7 @@ public function rebuildForAll() /** * Rebuild the entity jointPermissions for a particular entity. */ - public function rebuildForEntity(Entity $entity) + public function rebuildForEntity(Entity $entity): void { $entities = [$entity]; if ($entity instanceof Book) { @@ -88,7 +94,7 @@ public function rebuildForRole(Role $role) }); // Chunk through all bookshelves - Bookshelf::query()->select(['id', 'owned_by']) + $this->queries->shelves->start()->select(['id', 'owned_by']) ->chunk(100, function ($shelves) use ($roles) { $this->createManyJointPermissions($shelves->all(), $roles); }); @@ -99,7 +105,7 @@ public function rebuildForRole(Role $role) */ protected function bookFetchQuery(): Builder { - return Book::query()->withTrashed() + return $this->queries->books->start()->withTrashed() ->select(['id', 'owned_by'])->with([ 'chapters' => function ($query) { $query->withTrashed()->select(['id', 'owned_by', 'book_id']); @@ -113,7 +119,7 @@ protected function bookFetchQuery(): Builder /** * Build joint permissions for the given book and role combinations. */ - protected function buildJointPermissionsForBooks(EloquentCollection $books, array $roles, bool $deleteOld = false) + protected function buildJointPermissionsForBooks(EloquentCollection $books, array $roles, bool $deleteOld = false): void { $entities = clone $books; @@ -137,7 +143,7 @@ protected function buildJointPermissionsForBooks(EloquentCollection $books, arra /** * Rebuild the entity jointPermissions for a collection of entities. */ - protected function buildJointPermissionsForEntities(array $entities) + protected function buildJointPermissionsForEntities(array $entities): void { $roles = Role::query()->get()->values()->all(); $this->deleteManyJointPermissionsForEntities($entities); @@ -149,21 +155,19 @@ protected function buildJointPermissionsForEntities(array $entities) * * @param Entity[] $entities */ - protected function deleteManyJointPermissionsForEntities(array $entities) + protected function deleteManyJointPermissionsForEntities(array $entities): void { $simpleEntities = $this->entitiesToSimpleEntities($entities); $idsByType = $this->entitiesToTypeIdMap($simpleEntities); - DB::transaction(function () use ($idsByType) { - foreach ($idsByType as $type => $ids) { - foreach (array_chunk($ids, 1000) as $idChunk) { - DB::table('joint_permissions') - ->where('entity_type', '=', $type) - ->whereIn('entity_id', $idChunk) - ->delete(); - } + foreach ($idsByType as $type => $ids) { + foreach (array_chunk($ids, 1000) as $idChunk) { + DB::table('joint_permissions') + ->where('entity_type', '=', $type) + ->whereIn('entity_id', $idChunk) + ->delete(); } - }); + } } /** @@ -189,7 +193,7 @@ protected function entitiesToSimpleEntities(array $entities): array * @param Entity[] $originalEntities * @param Role[] $roles */ - protected function createManyJointPermissions(array $originalEntities, array $roles) + protected function createManyJointPermissions(array $originalEntities, array $roles): void { $entities = $this->entitiesToSimpleEntities($originalEntities); $jointPermissions = []; @@ -219,11 +223,9 @@ protected function createManyJointPermissions(array $originalEntities, array $ro } } - DB::transaction(function () use ($jointPermissions) { - foreach (array_chunk($jointPermissions, 1000) as $jointPermissionChunk) { - DB::table('joint_permissions')->insert($jointPermissionChunk); - } - }); + foreach (array_chunk($jointPermissions, 1000) as $jointPermissionChunk) { + DB::table('joint_permissions')->insert($jointPermissionChunk); + } } /** diff --git a/app/Permissions/Models/EntityPermission.php b/app/Permissions/Models/EntityPermission.php index 1ef9c2886f5..efad2ba39a0 100644 --- a/app/Permissions/Models/EntityPermission.php +++ b/app/Permissions/Models/EntityPermission.php @@ -18,8 +18,6 @@ */ class EntityPermission extends Model { - public const PERMISSIONS = ['view', 'create', 'update', 'delete']; - protected $fillable = ['role_id', 'view', 'create', 'update', 'delete']; public $timestamps = false; protected $hidden = ['entity_id', 'entity_type', 'id']; diff --git a/app/Permissions/Models/RolePermission.php b/app/Permissions/Models/RolePermission.php index d43313ea0d8..67b1c55f55d 100644 --- a/app/Permissions/Models/RolePermission.php +++ b/app/Permissions/Models/RolePermission.php @@ -9,7 +9,6 @@ /** * @property int $id * @property string $name - * @property string $display_name */ class RolePermission extends Model { diff --git a/app/Permissions/Permission.php b/app/Permissions/Permission.php new file mode 100644 index 00000000000..a434e54fd61 --- /dev/null +++ b/app/Permissions/Permission.php @@ -0,0 +1,144 @@ +value; + } +} diff --git a/app/Permissions/PermissionApplicator.php b/app/Permissions/PermissionApplicator.php index ce4a543fd83..23fdcfda9e5 100644 --- a/app/Permissions/PermissionApplicator.php +++ b/app/Permissions/PermissionApplicator.php @@ -7,8 +7,7 @@ use BookStack\Entities\Models\Entity; use BookStack\Entities\Models\Page; use BookStack\Permissions\Models\EntityPermission; -use BookStack\Users\Models\HasCreatorAndUpdater; -use BookStack\Users\Models\HasOwner; +use BookStack\Users\Models\OwnableInterface; use BookStack\Users\Models\User; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Query\Builder as QueryBuilder; @@ -24,14 +23,13 @@ public function __construct( /** * Checks if an entity has a restriction set upon it. - * - * @param Model&(HasCreatorAndUpdater|HasOwner) $ownable */ - public function checkOwnableUserAccess(Model $ownable, string $permission): bool + public function checkOwnableUserAccess(Model&OwnableInterface $ownable, string|Permission $permission): bool { - $explodedPermission = explode('-', $permission); + $permissionName = is_string($permission) ? $permission : $permission->value; + $explodedPermission = explode('-', $permissionName); $action = $explodedPermission[1] ?? $explodedPermission[0]; - $fullPermission = count($explodedPermission) > 1 ? $permission : $ownable->getMorphClass() . '-' . $permission; + $fullPermission = count($explodedPermission) > 1 ? $permissionName : $ownable->getMorphClass() . '-' . $permissionName; $user = $this->currentUser(); $userRoleIds = $this->getCurrentUserRoleIds(); @@ -39,7 +37,7 @@ public function checkOwnableUserAccess(Model $ownable, string $permission): bool $allRolePermission = $user->can($fullPermission . '-all'); $ownRolePermission = $user->can($fullPermission . '-own'); $nonJointPermissions = ['restrictions', 'image', 'attachment', 'comment']; - $ownerField = ($ownable instanceof Entity) ? 'owned_by' : 'created_by'; + $ownerField = $ownable->getOwnerFieldName(); $ownableFieldVal = $ownable->getAttribute($ownerField); if (is_null($ownableFieldVal)) { @@ -49,11 +47,15 @@ public function checkOwnableUserAccess(Model $ownable, string $permission): bool $isOwner = $user->id === $ownableFieldVal; $hasRolePermission = $allRolePermission || ($isOwner && $ownRolePermission); - // Handle non entity specific jointPermissions + // Handle non-entity-specific jointPermissions if (in_array($explodedPermission[0], $nonJointPermissions)) { return $hasRolePermission; } + if (!($ownable instanceof Entity)) { + return false; + } + $hasApplicableEntityPermissions = $this->hasEntityPermission($ownable, $userRoleIds, $action); return is_null($hasApplicableEntityPermissions) ? $hasRolePermission : $hasApplicableEntityPermissions; @@ -74,12 +76,13 @@ protected function hasEntityPermission(Entity $entity, array $userRoleIds, strin * Checks if a user has the given permission for any items in the system. * Can be passed an entity instance to filter on a specific type. */ - public function checkUserHasEntityPermissionOnAny(string $action, string $entityClass = ''): bool + public function checkUserHasEntityPermissionOnAny(string|Permission $action, string $entityClass = ''): bool { - $this->ensureValidEntityAction($action); + $permissionName = is_string($action) ? $action : $action->value; + $this->ensureValidEntityAction($permissionName); $permissionQuery = EntityPermission::query() - ->where($action, '=', true) + ->where($permissionName, '=', true) ->whereIn('role_id', $this->getCurrentUserRoleIds()); if (!empty($entityClass)) { @@ -234,8 +237,13 @@ protected function getCurrentUserRoleIds(): array */ protected function ensureValidEntityAction(string $action): void { - if (!in_array($action, EntityPermission::PERMISSIONS)) { - throw new InvalidArgumentException('Action should be a simple entity permission action, not a role permission'); + $allowed = Permission::genericForEntity(); + foreach ($allowed as $permission) { + if ($permission->value === $action) { + return; + } } + + throw new InvalidArgumentException('Action should be a simple entity permission action, not a role permission'); } } diff --git a/app/Permissions/PermissionsController.php b/app/Permissions/PermissionsController.php index f2014ea73f6..d93d9e718ae 100644 --- a/app/Permissions/PermissionsController.php +++ b/app/Permissions/PermissionsController.php @@ -2,32 +2,29 @@ namespace BookStack\Permissions; -use BookStack\Entities\Models\Book; -use BookStack\Entities\Models\Bookshelf; -use BookStack\Entities\Models\Chapter; -use BookStack\Entities\Models\Page; +use BookStack\Entities\Queries\EntityQueries; use BookStack\Entities\Tools\PermissionsUpdater; use BookStack\Http\Controller; use BookStack\Permissions\Models\EntityPermission; use BookStack\Users\Models\Role; +use BookStack\Util\DatabaseTransaction; use Illuminate\Http\Request; class PermissionsController extends Controller { - protected PermissionsUpdater $permissionsUpdater; - - public function __construct(PermissionsUpdater $permissionsUpdater) - { - $this->permissionsUpdater = $permissionsUpdater; + public function __construct( + protected PermissionsUpdater $permissionsUpdater, + protected EntityQueries $queries, + ) { } /** - * Show the Permissions view for a page. + * Show the permissions view for a page. */ public function showForPage(string $bookSlug, string $pageSlug) { - $page = Page::getBySlugs($bookSlug, $pageSlug); - $this->checkOwnablePermission('restrictions-manage', $page); + $page = $this->queries->pages->findVisibleBySlugsOrFail($bookSlug, $pageSlug); + $this->checkOwnablePermission(Permission::RestrictionsManage, $page); $this->setPageTitle(trans('entities.pages_permissions')); return view('pages.permissions', [ @@ -41,10 +38,12 @@ public function showForPage(string $bookSlug, string $pageSlug) */ public function updateForPage(Request $request, string $bookSlug, string $pageSlug) { - $page = Page::getBySlugs($bookSlug, $pageSlug); - $this->checkOwnablePermission('restrictions-manage', $page); + $page = $this->queries->pages->findVisibleBySlugsOrFail($bookSlug, $pageSlug); + $this->checkOwnablePermission(Permission::RestrictionsManage, $page); - $this->permissionsUpdater->updateFromPermissionsForm($page, $request); + (new DatabaseTransaction(function () use ($page, $request) { + $this->permissionsUpdater->updateFromPermissionsForm($page, $request); + }))->run(); $this->showSuccessNotification(trans('entities.pages_permissions_success')); @@ -52,12 +51,12 @@ public function updateForPage(Request $request, string $bookSlug, string $pageSl } /** - * Show the Restrictions view for a chapter. + * Show the permissions view for a chapter. */ public function showForChapter(string $bookSlug, string $chapterSlug) { - $chapter = Chapter::getBySlugs($bookSlug, $chapterSlug); - $this->checkOwnablePermission('restrictions-manage', $chapter); + $chapter = $this->queries->chapters->findVisibleBySlugsOrFail($bookSlug, $chapterSlug); + $this->checkOwnablePermission(Permission::RestrictionsManage, $chapter); $this->setPageTitle(trans('entities.chapters_permissions')); return view('chapters.permissions', [ @@ -67,14 +66,16 @@ public function showForChapter(string $bookSlug, string $chapterSlug) } /** - * Set the restrictions for a chapter. + * Set the permissions for a chapter. */ public function updateForChapter(Request $request, string $bookSlug, string $chapterSlug) { - $chapter = Chapter::getBySlugs($bookSlug, $chapterSlug); - $this->checkOwnablePermission('restrictions-manage', $chapter); + $chapter = $this->queries->chapters->findVisibleBySlugsOrFail($bookSlug, $chapterSlug); + $this->checkOwnablePermission(Permission::RestrictionsManage, $chapter); - $this->permissionsUpdater->updateFromPermissionsForm($chapter, $request); + (new DatabaseTransaction(function () use ($chapter, $request) { + $this->permissionsUpdater->updateFromPermissionsForm($chapter, $request); + }))->run(); $this->showSuccessNotification(trans('entities.chapters_permissions_success')); @@ -86,8 +87,8 @@ public function updateForChapter(Request $request, string $bookSlug, string $cha */ public function showForBook(string $slug) { - $book = Book::getBySlug($slug); - $this->checkOwnablePermission('restrictions-manage', $book); + $book = $this->queries->books->findVisibleBySlugOrFail($slug); + $this->checkOwnablePermission(Permission::RestrictionsManage, $book); $this->setPageTitle(trans('entities.books_permissions')); return view('books.permissions', [ @@ -97,14 +98,16 @@ public function showForBook(string $slug) } /** - * Set the restrictions for a book. + * Set the permissions for a book. */ public function updateForBook(Request $request, string $slug) { - $book = Book::getBySlug($slug); - $this->checkOwnablePermission('restrictions-manage', $book); + $book = $this->queries->books->findVisibleBySlugOrFail($slug); + $this->checkOwnablePermission(Permission::RestrictionsManage, $book); - $this->permissionsUpdater->updateFromPermissionsForm($book, $request); + (new DatabaseTransaction(function () use ($book, $request) { + $this->permissionsUpdater->updateFromPermissionsForm($book, $request); + }))->run(); $this->showSuccessNotification(trans('entities.books_permissions_updated')); @@ -116,8 +119,8 @@ public function updateForBook(Request $request, string $slug) */ public function showForShelf(string $slug) { - $shelf = Bookshelf::getBySlug($slug); - $this->checkOwnablePermission('restrictions-manage', $shelf); + $shelf = $this->queries->shelves->findVisibleBySlugOrFail($slug); + $this->checkOwnablePermission(Permission::RestrictionsManage, $shelf); $this->setPageTitle(trans('entities.shelves_permissions')); return view('shelves.permissions', [ @@ -131,10 +134,12 @@ public function showForShelf(string $slug) */ public function updateForShelf(Request $request, string $slug) { - $shelf = Bookshelf::getBySlug($slug); - $this->checkOwnablePermission('restrictions-manage', $shelf); + $shelf = $this->queries->shelves->findVisibleBySlugOrFail($slug); + $this->checkOwnablePermission(Permission::RestrictionsManage, $shelf); - $this->permissionsUpdater->updateFromPermissionsForm($shelf, $request); + (new DatabaseTransaction(function () use ($shelf, $request) { + $this->permissionsUpdater->updateFromPermissionsForm($shelf, $request); + }))->run(); $this->showSuccessNotification(trans('entities.shelves_permissions_updated')); @@ -146,10 +151,13 @@ public function updateForShelf(Request $request, string $slug) */ public function copyShelfPermissionsToBooks(string $slug) { - $shelf = Bookshelf::getBySlug($slug); - $this->checkOwnablePermission('restrictions-manage', $shelf); + $shelf = $this->queries->shelves->findVisibleBySlugOrFail($slug); + $this->checkOwnablePermission(Permission::RestrictionsManage, $shelf); + + $updateCount = (new DatabaseTransaction(function () use ($shelf) { + return $this->permissionsUpdater->updateBookPermissionsFromShelf($shelf); + }))->run(); - $updateCount = $this->permissionsUpdater->updateBookPermissionsFromShelf($shelf); $this->showSuccessNotification(trans('entities.shelves_copy_permission_success', ['count' => $updateCount])); return redirect($shelf->getUrl()); @@ -160,7 +168,7 @@ public function copyShelfPermissionsToBooks(string $slug) */ public function formRowForRole(string $entityType, string $roleId) { - $this->checkPermissionOr('restrictions-manage-all', fn() => userCan('restrictions-manage-own')); + $this->checkPermissionOr(Permission::RestrictionsManageAll, fn() => userCan(Permission::RestrictionsManageOwn)); $role = Role::query()->findOrFail($roleId); diff --git a/app/Permissions/PermissionsRepo.php b/app/Permissions/PermissionsRepo.php index b41612968b4..6ced7b7511c 100644 --- a/app/Permissions/PermissionsRepo.php +++ b/app/Permissions/PermissionsRepo.php @@ -7,6 +7,7 @@ use BookStack\Facades\Activity; use BookStack\Permissions\Models\RolePermission; use BookStack\Users\Models\Role; +use BookStack\Util\DatabaseTransaction; use Exception; use Illuminate\Database\Eloquent\Collection; @@ -48,38 +49,42 @@ public function getRoleById(int $id): Role */ public function saveNewRole(array $roleData): Role { - $role = new Role($roleData); - $role->mfa_enforced = boolval($roleData['mfa_enforced'] ?? false); - $role->save(); + return (new DatabaseTransaction(function () use ($roleData) { + $role = new Role($roleData); + $role->mfa_enforced = boolval($roleData['mfa_enforced'] ?? false); + $role->save(); - $permissions = $roleData['permissions'] ?? []; - $this->assignRolePermissions($role, $permissions); - $this->permissionBuilder->rebuildForRole($role); + $permissions = $roleData['permissions'] ?? []; + $this->assignRolePermissions($role, $permissions); + $this->permissionBuilder->rebuildForRole($role); - Activity::add(ActivityType::ROLE_CREATE, $role); + Activity::add(ActivityType::ROLE_CREATE, $role); - return $role; + return $role; + }))->run(); } /** * Updates an existing role. - * Ensures Admin system role always have core permissions. + * Ensures the Admin system role always has core permissions. */ public function updateRole($roleId, array $roleData): Role { $role = $this->getRoleById($roleId); - if (isset($roleData['permissions'])) { - $this->assignRolePermissions($role, $roleData['permissions']); - } + return (new DatabaseTransaction(function () use ($role, $roleData) { + if (isset($roleData['permissions'])) { + $this->assignRolePermissions($role, $roleData['permissions']); + } - $role->fill($roleData); - $role->save(); - $this->permissionBuilder->rebuildForRole($role); + $role->fill($roleData); + $role->save(); + $this->permissionBuilder->rebuildForRole($role); - Activity::add(ActivityType::ROLE_UPDATE, $role); + Activity::add(ActivityType::ROLE_UPDATE, $role); - return $role; + return $role; + }))->run(); } /** @@ -114,7 +119,7 @@ protected function assignRolePermissions(Role $role, array $permissionNameArray /** * Delete a role from the system. * Check it's not an admin role or set as default before deleting. - * If a migration Role ID is specified the users assign to the current role + * If a migration Role ID is specified, the users assigned to the current role * will be added to the role of the specified id. * * @throws PermissionsException @@ -131,17 +136,19 @@ public function deleteRole(int $roleId, int $migrateRoleId = 0): void throw new PermissionsException(trans('errors.role_registration_default_cannot_delete')); } - if ($migrateRoleId !== 0) { - $newRole = Role::query()->find($migrateRoleId); - if ($newRole) { - $users = $role->users()->pluck('id')->toArray(); - $newRole->users()->sync($users); + (new DatabaseTransaction(function () use ($migrateRoleId, $role) { + if ($migrateRoleId !== 0) { + $newRole = Role::query()->find($migrateRoleId); + if ($newRole) { + $users = $role->users()->pluck('id')->toArray(); + $newRole->users()->sync($users); + } } - } - $role->entityPermissions()->delete(); - $role->jointPermissions()->delete(); - Activity::add(ActivityType::ROLE_DELETE, $role); - $role->delete(); + $role->entityPermissions()->delete(); + $role->jointPermissions()->delete(); + Activity::add(ActivityType::ROLE_DELETE, $role); + $role->delete(); + }))->run(); } } diff --git a/app/References/CrossLinkParser.php b/app/References/CrossLinkParser.php index b9c3ad205ae..3fb00be84c4 100644 --- a/app/References/CrossLinkParser.php +++ b/app/References/CrossLinkParser.php @@ -3,6 +3,7 @@ namespace BookStack\References; use BookStack\App\Model; +use BookStack\Entities\Queries\EntityQueries; use BookStack\References\ModelResolvers\BookLinkModelResolver; use BookStack\References\ModelResolvers\BookshelfLinkModelResolver; use BookStack\References\ModelResolvers\ChapterLinkModelResolver; @@ -47,7 +48,7 @@ public function extractLinkedModels(string $html): array /** * Get a list of href values from the given document. * - * @returns string[] + * @return string[] */ protected function getLinksFromContent(string $html): array { @@ -85,12 +86,14 @@ protected function linkToModel(string $link): ?Model */ public static function createWithEntityResolvers(): self { + $queries = app()->make(EntityQueries::class); + return new self([ - new PagePermalinkModelResolver(), - new PageLinkModelResolver(), - new ChapterLinkModelResolver(), - new BookLinkModelResolver(), - new BookshelfLinkModelResolver(), + new PagePermalinkModelResolver($queries->pages), + new PageLinkModelResolver($queries->pages), + new ChapterLinkModelResolver($queries->chapters), + new BookLinkModelResolver($queries->books), + new BookshelfLinkModelResolver($queries->shelves), ]); } } diff --git a/app/References/ModelResolvers/AttachmentModelResolver.php b/app/References/ModelResolvers/AttachmentModelResolver.php new file mode 100644 index 00000000000..e870d515bbe --- /dev/null +++ b/app/References/ModelResolvers/AttachmentModelResolver.php @@ -0,0 +1,22 @@ +find($id); + } +} diff --git a/app/References/ModelResolvers/BookLinkModelResolver.php b/app/References/ModelResolvers/BookLinkModelResolver.php index d404fe2fd84..a671fbf5700 100644 --- a/app/References/ModelResolvers/BookLinkModelResolver.php +++ b/app/References/ModelResolvers/BookLinkModelResolver.php @@ -4,9 +4,15 @@ use BookStack\App\Model; use BookStack\Entities\Models\Book; +use BookStack\Entities\Queries\BookQueries; class BookLinkModelResolver implements CrossLinkModelResolver { + public function __construct( + protected BookQueries $queries + ) { + } + public function resolve(string $link): ?Model { $pattern = '/^' . preg_quote(url('/books'), '/') . '\/([\w-]+)' . '([#?\/]|$)/'; @@ -19,7 +25,7 @@ public function resolve(string $link): ?Model $bookSlug = $matches[1]; /** @var ?Book $model */ - $model = Book::query()->where('slug', '=', $bookSlug)->first(['id']); + $model = $this->queries->start()->where('slug', '=', $bookSlug)->first(['id']); return $model; } diff --git a/app/References/ModelResolvers/BookshelfLinkModelResolver.php b/app/References/ModelResolvers/BookshelfLinkModelResolver.php index cc9df034e60..d79c760e070 100644 --- a/app/References/ModelResolvers/BookshelfLinkModelResolver.php +++ b/app/References/ModelResolvers/BookshelfLinkModelResolver.php @@ -4,9 +4,14 @@ use BookStack\App\Model; use BookStack\Entities\Models\Bookshelf; +use BookStack\Entities\Queries\BookshelfQueries; class BookshelfLinkModelResolver implements CrossLinkModelResolver { + public function __construct( + protected BookshelfQueries $queries + ) { + } public function resolve(string $link): ?Model { $pattern = '/^' . preg_quote(url('/shelves'), '/') . '\/([\w-]+)' . '([#?\/]|$)/'; @@ -19,7 +24,7 @@ public function resolve(string $link): ?Model $shelfSlug = $matches[1]; /** @var ?Bookshelf $model */ - $model = Bookshelf::query()->where('slug', '=', $shelfSlug)->first(['id']); + $model = $this->queries->start()->where('slug', '=', $shelfSlug)->first(['id']); return $model; } diff --git a/app/References/ModelResolvers/ChapterLinkModelResolver.php b/app/References/ModelResolvers/ChapterLinkModelResolver.php index 4733c68eead..318b5ed35f4 100644 --- a/app/References/ModelResolvers/ChapterLinkModelResolver.php +++ b/app/References/ModelResolvers/ChapterLinkModelResolver.php @@ -4,9 +4,15 @@ use BookStack\App\Model; use BookStack\Entities\Models\Chapter; +use BookStack\Entities\Queries\ChapterQueries; class ChapterLinkModelResolver implements CrossLinkModelResolver { + public function __construct( + protected ChapterQueries $queries + ) { + } + public function resolve(string $link): ?Model { $pattern = '/^' . preg_quote(url('/books'), '/') . '\/([\w-]+)' . '\/chapter\/' . '([\w-]+)' . '([#?\/]|$)/'; @@ -20,7 +26,7 @@ public function resolve(string $link): ?Model $chapterSlug = $matches[2]; /** @var ?Chapter $model */ - $model = Chapter::query()->whereSlugs($bookSlug, $chapterSlug)->first(['id']); + $model = $this->queries->usingSlugs($bookSlug, $chapterSlug)->first(['id']); return $model; } diff --git a/app/References/ModelResolvers/ImageModelResolver.php b/app/References/ModelResolvers/ImageModelResolver.php new file mode 100644 index 00000000000..2c6c9fecd74 --- /dev/null +++ b/app/References/ModelResolvers/ImageModelResolver.php @@ -0,0 +1,58 @@ +getUrlPattern(); + $matches = []; + $match = preg_match($pattern, $link, $matches); + if (!$match) { + return null; + } + + $path = $matches[2]; + + // Strip thumbnail element from path if existing + $originalPathSplit = array_filter(explode('/', $path), function (string $part) { + $resizedDir = (str_starts_with($part, 'thumbs-') || str_starts_with($part, 'scaled-')); + $missingExtension = !str_contains($part, '.'); + + return !($resizedDir && $missingExtension); + }); + + // Build a database-format image path and search for the image entry + $fullPath = '/uploads/images/' . ltrim(implode('/', $originalPathSplit), '/'); + + return Image::query()->where('path', '=', $fullPath)->first(); + } + + /** + * Get the regex pattern to identify image URLs. + * Caches the pattern since it requires looking up to settings/config. + */ + protected function getUrlPattern(): string + { + if ($this->pattern) { + return $this->pattern; + } + + $urls = [url('/uploads/images')]; + $baseImageUrl = ImageStorage::getPublicUrl('/uploads/images'); + if ($baseImageUrl !== $urls[0]) { + $urls[] = $baseImageUrl; + } + + $imageUrlRegex = implode('|', array_map(fn ($url) => preg_quote($url, '/'), $urls)); + $this->pattern = '/^(' . $imageUrlRegex . ')\/(.+)/'; + + return $this->pattern; + } +} diff --git a/app/References/ModelResolvers/PageLinkModelResolver.php b/app/References/ModelResolvers/PageLinkModelResolver.php index 736382becdd..9a01416a445 100644 --- a/app/References/ModelResolvers/PageLinkModelResolver.php +++ b/app/References/ModelResolvers/PageLinkModelResolver.php @@ -4,9 +4,15 @@ use BookStack\App\Model; use BookStack\Entities\Models\Page; +use BookStack\Entities\Queries\PageQueries; class PageLinkModelResolver implements CrossLinkModelResolver { + public function __construct( + protected PageQueries $queries + ) { + } + public function resolve(string $link): ?Model { $pattern = '/^' . preg_quote(url('/books'), '/') . '\/([\w-]+)' . '\/page\/' . '([\w-]+)' . '([#?\/]|$)/'; @@ -20,7 +26,7 @@ public function resolve(string $link): ?Model $pageSlug = $matches[2]; /** @var ?Page $model */ - $model = Page::query()->whereSlugs($bookSlug, $pageSlug)->first(['id']); + $model = $this->queries->usingSlugs($bookSlug, $pageSlug)->first(['id']); return $model; } diff --git a/app/References/ModelResolvers/PagePermalinkModelResolver.php b/app/References/ModelResolvers/PagePermalinkModelResolver.php index 9ed00b36dd4..59d509f3399 100644 --- a/app/References/ModelResolvers/PagePermalinkModelResolver.php +++ b/app/References/ModelResolvers/PagePermalinkModelResolver.php @@ -4,9 +4,15 @@ use BookStack\App\Model; use BookStack\Entities\Models\Page; +use BookStack\Entities\Queries\PageQueries; class PagePermalinkModelResolver implements CrossLinkModelResolver { + public function __construct( + protected PageQueries $queries + ) { + } + public function resolve(string $link): ?Model { $pattern = '/^' . preg_quote(url('/link'), '/') . '\/(\d+)/'; @@ -18,7 +24,7 @@ public function resolve(string $link): ?Model $id = intval($matches[1]); /** @var ?Page $model */ - $model = Page::query()->find($id, ['id']); + $model = $this->queries->start()->find($id, ['id']); return $model; } diff --git a/app/References/ReferenceController.php b/app/References/ReferenceController.php index 991f47225b8..2fe86fd594b 100644 --- a/app/References/ReferenceController.php +++ b/app/References/ReferenceController.php @@ -2,16 +2,14 @@ namespace BookStack\References; -use BookStack\Entities\Models\Book; -use BookStack\Entities\Models\Bookshelf; -use BookStack\Entities\Models\Chapter; -use BookStack\Entities\Models\Page; +use BookStack\Entities\Queries\EntityQueries; use BookStack\Http\Controller; class ReferenceController extends Controller { public function __construct( - protected ReferenceFetcher $referenceFetcher + protected ReferenceFetcher $referenceFetcher, + protected EntityQueries $queries, ) { } @@ -20,7 +18,7 @@ public function __construct( */ public function page(string $bookSlug, string $pageSlug) { - $page = Page::getBySlugs($bookSlug, $pageSlug); + $page = $this->queries->pages->findVisibleBySlugsOrFail($bookSlug, $pageSlug); $references = $this->referenceFetcher->getReferencesToEntity($page); return view('pages.references', [ @@ -34,7 +32,7 @@ public function page(string $bookSlug, string $pageSlug) */ public function chapter(string $bookSlug, string $chapterSlug) { - $chapter = Chapter::getBySlugs($bookSlug, $chapterSlug); + $chapter = $this->queries->chapters->findVisibleBySlugsOrFail($bookSlug, $chapterSlug); $references = $this->referenceFetcher->getReferencesToEntity($chapter); return view('chapters.references', [ @@ -48,7 +46,7 @@ public function chapter(string $bookSlug, string $chapterSlug) */ public function book(string $slug) { - $book = Book::getBySlug($slug); + $book = $this->queries->books->findVisibleBySlugOrFail($slug); $references = $this->referenceFetcher->getReferencesToEntity($book); return view('books.references', [ @@ -62,7 +60,7 @@ public function book(string $slug) */ public function shelf(string $slug) { - $shelf = Bookshelf::getBySlug($slug); + $shelf = $this->queries->shelves->findVisibleBySlugOrFail($slug); $references = $this->referenceFetcher->getReferencesToEntity($shelf); return view('shelves.references', [ diff --git a/app/References/ReferenceFetcher.php b/app/References/ReferenceFetcher.php index 0d9883a3edc..1c9664f45a9 100644 --- a/app/References/ReferenceFetcher.php +++ b/app/References/ReferenceFetcher.php @@ -23,7 +23,7 @@ public function __construct( public function getReferencesToEntity(Entity $entity): Collection { $references = $this->queryReferencesToEntity($entity)->get(); - $this->mixedEntityListLoader->loadIntoRelations($references->all(), 'from'); + $this->mixedEntityListLoader->loadIntoRelations($references->all(), 'from', true); return $references; } @@ -41,7 +41,8 @@ protected function queryReferencesToEntity(Entity $entity): Builder { $baseQuery = Reference::query() ->where('to_type', '=', $entity->getMorphClass()) - ->where('to_id', '=', $entity->id); + ->where('to_id', '=', $entity->id) + ->whereHas('from'); return $this->permissions->restrictEntityRelationQuery( $baseQuery, diff --git a/app/References/ReferenceUpdater.php b/app/References/ReferenceUpdater.php index db355f21130..5f1d711e9c6 100644 --- a/app/References/ReferenceUpdater.php +++ b/app/References/ReferenceUpdater.php @@ -4,7 +4,8 @@ use BookStack\Entities\Models\Book; use BookStack\Entities\Models\Entity; -use BookStack\Entities\Models\HasHtmlDescription; +use BookStack\Entities\Models\HtmlDescriptionInterface; +use BookStack\Entities\Models\HtmlDescriptionTrait; use BookStack\Entities\Models\Page; use BookStack\Entities\Repos\RevisionRepo; use BookStack\Util\HtmlDocument; @@ -61,20 +62,18 @@ protected function updateReferencesWithinEntity(Entity $entity, string $oldLink, { if ($entity instanceof Page) { $this->updateReferencesWithinPage($entity, $oldLink, $newLink); - return; } - if (in_array(HasHtmlDescription::class, class_uses($entity))) { + if ($entity instanceof HtmlDescriptionInterface) { $this->updateReferencesWithinDescription($entity, $oldLink, $newLink); } } - protected function updateReferencesWithinDescription(Entity $entity, string $oldLink, string $newLink): void + protected function updateReferencesWithinDescription(Entity&HtmlDescriptionInterface $entity, string $oldLink, string $newLink): void { - /** @var HasHtmlDescription&Entity $entity */ $entity = (clone $entity)->refresh(); - $html = $this->updateLinksInHtml($entity->description_html ?: '', $oldLink, $newLink); - $entity->description_html = $html; + $html = $this->updateLinksInHtml($entity->descriptionHtml(true) ?: '', $oldLink, $newLink); + $entity->setDescriptionHtml($html); $entity->save(); } diff --git a/app/Search/Options/ExactSearchOption.php b/app/Search/Options/ExactSearchOption.php new file mode 100644 index 00000000000..5651fb99b09 --- /dev/null +++ b/app/Search/Options/ExactSearchOption.php @@ -0,0 +1,13 @@ +value); + $escaped = str_replace('"', '\"', $escaped); + return ($this->negated ? '-' : '') . '"' . $escaped . '"'; + } +} diff --git a/app/Search/Options/FilterSearchOption.php b/app/Search/Options/FilterSearchOption.php new file mode 100644 index 00000000000..1f64f4f9e04 --- /dev/null +++ b/app/Search/Options/FilterSearchOption.php @@ -0,0 +1,37 @@ +name = $name; + } + + public function toString(): string + { + $valueText = ($this->value ? ':' . $this->value : ''); + $filterBrace = '{' . $this->name . $valueText . '}'; + return ($this->negated ? '-' : '') . $filterBrace; + } + + public function getKey(): string + { + return $this->name; + } + + public static function fromContentString(string $value, bool $negated = false): self + { + $explodedFilter = explode(':', $value, 2); + $filterValue = (count($explodedFilter) > 1) ? $explodedFilter[1] : ''; + $filterName = $explodedFilter[0]; + return new self($filterValue, $filterName, $negated); + } +} diff --git a/app/Search/Options/SearchOption.php b/app/Search/Options/SearchOption.php new file mode 100644 index 00000000000..483f2123fb1 --- /dev/null +++ b/app/Search/Options/SearchOption.php @@ -0,0 +1,26 @@ +=', '=', '<', '>', 'like', '!=']; + + public function toString(): string + { + return ($this->negated ? '-' : '') . "[{$this->value}]"; + } + + /** + * @return array{name: string, operator: string, value: string} + */ + public function getParts(): array + { + $operatorRegex = implode('|', array_map(fn($op) => preg_quote($op), $this->queryOperators)); + preg_match('/^(.*?)((' . $operatorRegex . ')(.*?))?$/', $this->value, $tagSplit); + + $extractedOperator = count($tagSplit) > 2 ? $tagSplit[3] : ''; + $tagOperator = in_array($extractedOperator, $this->queryOperators) ? $extractedOperator : '='; + $tagValue = count($tagSplit) > 3 ? $tagSplit[4] : ''; + + return [ + 'name' => $tagSplit[1], + 'operator' => $tagOperator, + 'value' => $tagValue, + ]; + } +} diff --git a/app/Search/Options/TermSearchOption.php b/app/Search/Options/TermSearchOption.php new file mode 100644 index 00000000000..c78829fc87e --- /dev/null +++ b/app/Search/Options/TermSearchOption.php @@ -0,0 +1,11 @@ +value; + } +} diff --git a/app/Search/SearchApiController.php b/app/Search/SearchApiController.php index d1619e11867..cd4a14a3931 100644 --- a/app/Search/SearchApiController.php +++ b/app/Search/SearchApiController.php @@ -9,21 +9,18 @@ class SearchApiController extends ApiController { - protected SearchRunner $searchRunner; - protected SearchResultsFormatter $resultsFormatter; - - protected $rules = [ + protected array $rules = [ 'all' => [ - 'query' => ['required'], - 'page' => ['integer', 'min:1'], - 'count' => ['integer', 'min:1', 'max:100'], + 'query' => ['required'], + 'page' => ['integer', 'min:1'], + 'count' => ['integer', 'min:1', 'max:100'], ], ]; - public function __construct(SearchRunner $searchRunner, SearchResultsFormatter $resultsFormatter) - { - $this->searchRunner = $searchRunner; - $this->resultsFormatter = $resultsFormatter; + public function __construct( + protected SearchRunner $searchRunner, + protected SearchResultsFormatter $resultsFormatter + ) { } /** @@ -50,16 +47,16 @@ public function all(Request $request) $this->resultsFormatter->format($results['results']->all(), $options); $data = (new ApiEntityListFormatter($results['results']->all())) - ->withType()->withTags() + ->withType()->withTags()->withParents() ->withField('preview_html', function (Entity $entity) { return [ - 'name' => (string) $entity->getAttribute('preview_name'), + 'name' => (string) $entity->getAttribute('preview_name'), 'content' => (string) $entity->getAttribute('preview_content'), ]; })->format(); return response()->json([ - 'data' => $data, + 'data' => $data, 'total' => $results['total'], ]); } diff --git a/app/Search/SearchController.php b/app/Search/SearchController.php index 6cf12a57920..2fce6a3d53f 100644 --- a/app/Search/SearchController.php +++ b/app/Search/SearchController.php @@ -2,8 +2,8 @@ namespace BookStack\Search; -use BookStack\Entities\Models\Page; -use BookStack\Entities\Queries\Popular; +use BookStack\Entities\Queries\PageQueries; +use BookStack\Entities\Queries\QueryPopular; use BookStack\Entities\Tools\SiblingFetcher; use BookStack\Http\Controller; use Illuminate\Http\Request; @@ -11,7 +11,8 @@ class SearchController extends Controller { public function __construct( - protected SearchRunner $searchRunner + protected SearchRunner $searchRunner, + protected PageQueries $pageQueries, ) { } @@ -66,7 +67,7 @@ public function searchChapter(Request $request, int $chapterId) * Search for a list of entities and return a partial HTML response of matching entities. * Returns the most popular entities if no search is provided. */ - public function searchForSelector(Request $request) + public function searchForSelector(Request $request, QueryPopular $queryPopular) { $entityTypes = $request->filled('types') ? explode(',', $request->get('types')) : ['page', 'chapter', 'book']; $searchTerm = $request->get('term', false); @@ -77,7 +78,7 @@ public function searchForSelector(Request $request) $searchTerm .= ' {type:' . implode('|', $entityTypes) . '}'; $entities = $this->searchRunner->searchEntities(SearchOptions::fromString($searchTerm), 'all', 1, 20)['results']; } else { - $entities = (new Popular())->run(20, 0, $entityTypes); + $entities = $queryPopular->run(20, 0, $entityTypes); } return view('search.parts.entity-selector-list', ['entities' => $entities, 'permission' => $permission]); @@ -95,12 +96,11 @@ public function templatesForSelector(Request $request) $searchOptions->setFilter('is_template'); $entities = $this->searchRunner->searchEntities($searchOptions, 'page', 1, 20)['results']; } else { - $entities = Page::visible() - ->where('template', '=', true) + $entities = $this->pageQueries->visibleTemplates() ->where('draft', '=', false) ->orderBy('updated_at', 'desc') ->take(20) - ->get(Page::$listAttributes); + ->get(); } return view('search.parts.entity-selector-list', [ @@ -130,12 +130,12 @@ public function searchSuggestions(Request $request) /** * Search siblings items in the system. */ - public function searchSiblings(Request $request) + public function searchSiblings(Request $request, SiblingFetcher $siblingFetcher) { $type = $request->get('entity_type', null); $id = $request->get('entity_id', null); - $entities = (new SiblingFetcher())->fetch($type, $id); + $entities = $siblingFetcher->fetch($type, $id); return view('entities.list-basic', ['entities' => $entities, 'style' => 'compact']); } diff --git a/app/Search/SearchIndex.php b/app/Search/SearchIndex.php index d9fc4e7aadc..117d069ea77 100644 --- a/app/Search/SearchIndex.php +++ b/app/Search/SearchIndex.php @@ -16,7 +16,13 @@ class SearchIndex /** * A list of delimiter characters used to break-up parsed content into terms for indexing. */ - public static string $delimiters = " \n\t.,!?:;()[]{}<>`'\""; + public static string $delimiters = " \n\t.-,!?:;()[]{}<>`'\"«»"; + + /** + * A list of delimiter which could be commonly used within a single term and also indicate a break between terms. + * The indexer will index the full term with these delimiters, plus the terms split via these delimiters. + */ + public static string $softDelimiters = ".-"; public function __construct( protected EntityProvider $entityProvider @@ -30,7 +36,7 @@ public function indexEntity(Entity $entity): void { $this->deleteEntityTerms($entity); $terms = $this->entityToTermDataArray($entity); - SearchTerm::query()->insert($terms); + $this->insertTerms($terms); } /** @@ -46,10 +52,7 @@ public function indexEntities(array $entities): void array_push($terms, ...$entityTerms); } - $chunkedTerms = array_chunk($terms, 500); - foreach ($chunkedTerms as $termChunk) { - SearchTerm::query()->insert($termChunk); - } + $this->insertTerms($terms); } /** @@ -99,11 +102,24 @@ public function deleteEntityTerms(Entity $entity): void $entity->searchTerms()->delete(); } + /** + * Insert the given terms into the database. + * Chunks through the given terms to remain within database limits. + * @param array[] $terms + */ + protected function insertTerms(array $terms): void + { + $chunkedTerms = array_chunk($terms, 500); + foreach ($chunkedTerms as $termChunk) { + SearchTerm::query()->insert($termChunk); + } + } + /** * Create a scored term array from the given text, where the keys are the terms * and the values are their scores. * - * @returns array + * @return array */ protected function generateTermScoreMapFromText(string $text, float $scoreAdjustment = 1): array { @@ -120,7 +136,7 @@ protected function generateTermScoreMapFromText(string $text, float $scoreAdjust * Create a scored term array from the given HTML, where the keys are the terms * and the values are their scores. * - * @returns array + * @return array */ protected function generateTermScoreMapFromHtml(string $html): array { @@ -144,7 +160,9 @@ protected function generateTermScoreMapFromHtml(string $html): array /** @var DOMNode $child */ foreach ($doc->getBodyChildren() as $child) { $nodeName = $child->nodeName; - $termCounts = $this->textToTermCountMap(trim($child->textContent)); + $text = trim($child->textContent); + $text = str_replace("\u{00A0}", ' ', $text); + $termCounts = $this->textToTermCountMap($text); foreach ($termCounts as $term => $count) { $scoreChange = $count * ($elementScoreAdjustmentMap[$nodeName] ?? 1); $scoresByTerm[$term] = ($scoresByTerm[$term] ?? 0) + $scoreChange; @@ -159,7 +177,7 @@ protected function generateTermScoreMapFromHtml(string $html): array * * @param Tag[] $tags * - * @returns array + * @return array */ protected function generateTermScoreMapFromTags(array $tags): array { @@ -181,20 +199,41 @@ protected function generateTermScoreMapFromTags(array $tags): array * For the given text, return an array where the keys are the unique term words * and the values are the frequency of that term. * - * @returns array + * @return array */ protected function textToTermCountMap(string $text): array { $tokenMap = []; // {TextToken => OccurrenceCount} - $splitChars = static::$delimiters; - $token = strtok($text, $splitChars); + $softDelims = static::$softDelimiters; + $tokenizer = new SearchTextTokenizer($text, static::$delimiters); + $extendedToken = ''; + $extendedLen = 0; + + $token = $tokenizer->next(); while ($token !== false) { - if (!isset($tokenMap[$token])) { - $tokenMap[$token] = 0; + $delim = $tokenizer->previousDelimiter(); + + if ($delim && str_contains($softDelims, $delim) && $token !== '') { + $extendedToken .= $delim . $token; + $extendedLen++; + } else { + if ($extendedLen > 1) { + $tokenMap[$extendedToken] = ($tokenMap[$extendedToken] ?? 0) + 1; + } + $extendedToken = $token; + $extendedLen = 1; } - $tokenMap[$token]++; - $token = strtok($splitChars); + + if ($token) { + $tokenMap[$token] = ($tokenMap[$token] ?? 0) + 1; + } + + $token = $tokenizer->next(); + } + + if ($extendedLen > 1) { + $tokenMap[$extendedToken] = ($tokenMap[$extendedToken] ?? 0) + 1; } return $tokenMap; @@ -204,7 +243,7 @@ protected function textToTermCountMap(string $text): array * For the given entity, Generate an array of term data details. * Is the raw term data, not instances of SearchTerm models. * - * @returns array{term: string, score: float, entity_id: int, entity_type: string}[] + * @return array{term: string, score: float, entity_id: int, entity_type: string}[] */ protected function entityToTermDataArray(Entity $entity): array { @@ -240,7 +279,7 @@ protected function entityToTermDataArray(Entity $entity): array * * @param array[] ...$scoreMaps * - * @returns array + * @return array */ protected function mergeTermScoreMaps(...$scoreMaps): array { diff --git a/app/Search/SearchOptionSet.php b/app/Search/SearchOptionSet.php new file mode 100644 index 00000000000..844d145e6fc --- /dev/null +++ b/app/Search/SearchOptionSet.php @@ -0,0 +1,85 @@ +options = $options; + } + + public function toValueArray(): array + { + return array_map(fn(SearchOption $option) => $option->value, $this->options); + } + + public function toValueMap(): array + { + $map = []; + foreach ($this->options as $index => $option) { + $key = $option->getKey() ?? $index; + $map[$key] = $option->value; + } + return $map; + } + + public function merge(SearchOptionSet $set): self + { + return new self(array_merge($this->options, $set->options)); + } + + public function filterEmpty(): self + { + $filteredOptions = array_values(array_filter($this->options, fn (SearchOption $option) => !empty($option->value))); + return new self($filteredOptions); + } + + /** + * @param class-string $class + */ + public static function fromValueArray(array $values, string $class): self + { + $options = array_map(fn($val) => new $class($val), $values); + return new self($options); + } + + /** + * @return T[] + */ + public function all(): array + { + return $this->options; + } + + /** + * @return self + */ + public function negated(): self + { + $values = array_values(array_filter($this->options, fn (SearchOption $option) => $option->negated)); + return new self($values); + } + + /** + * @return self + */ + public function nonNegated(): self + { + $values = array_values(array_filter($this->options, fn (SearchOption $option) => !$option->negated)); + return new self($values); + } +} diff --git a/app/Search/SearchOptions.php b/app/Search/SearchOptions.php index fffa03db094..bf527d9c305 100644 --- a/app/Search/SearchOptions.php +++ b/app/Search/SearchOptions.php @@ -2,26 +2,39 @@ namespace BookStack\Search; +use BookStack\Search\Options\ExactSearchOption; +use BookStack\Search\Options\FilterSearchOption; +use BookStack\Search\Options\SearchOption; +use BookStack\Search\Options\TagSearchOption; +use BookStack\Search\Options\TermSearchOption; use Illuminate\Http\Request; class SearchOptions { - public array $searches = []; - public array $exacts = []; - public array $tags = []; - public array $filters = []; + /** @var SearchOptionSet */ + public SearchOptionSet $searches; + /** @var SearchOptionSet */ + public SearchOptionSet $exacts; + /** @var SearchOptionSet */ + public SearchOptionSet $tags; + /** @var SearchOptionSet */ + public SearchOptionSet $filters; + + public function __construct() + { + $this->searches = new SearchOptionSet(); + $this->exacts = new SearchOptionSet(); + $this->tags = new SearchOptionSet(); + $this->filters = new SearchOptionSet(); + } /** * Create a new instance from a search string. */ public static function fromString(string $search): self { - $decoded = static::decode($search); - $instance = new SearchOptions(); - foreach ($decoded as $type => $value) { - $instance->$type = $value; - } - + $instance = new self(); + $instance->addOptionsFromString($search); return $instance; } @@ -41,46 +54,64 @@ public static function fromRequest(Request $request): self } $instance = new SearchOptions(); - $inputs = $request->only(['search', 'types', 'filters', 'exact', 'tags']); + $inputs = $request->only(['search', 'types', 'filters', 'exact', 'tags', 'extras']); $parsedStandardTerms = static::parseStandardTermString($inputs['search'] ?? ''); - $instance->searches = array_filter($parsedStandardTerms['terms']); - $instance->exacts = array_filter($parsedStandardTerms['exacts']); - - array_push($instance->exacts, ...array_filter($inputs['exact'] ?? [])); - - $instance->tags = array_filter($inputs['tags'] ?? []); + $inputExacts = array_filter($inputs['exact'] ?? []); + $instance->searches = SearchOptionSet::fromValueArray(array_filter($parsedStandardTerms['terms']), TermSearchOption::class); + $instance->exacts = SearchOptionSet::fromValueArray(array_filter($parsedStandardTerms['exacts']), ExactSearchOption::class); + $instance->exacts = $instance->exacts->merge(SearchOptionSet::fromValueArray($inputExacts, ExactSearchOption::class)); + $instance->tags = SearchOptionSet::fromValueArray(array_filter($inputs['tags'] ?? []), TagSearchOption::class); + $cleanedFilters = []; foreach (($inputs['filters'] ?? []) as $filterKey => $filterVal) { if (empty($filterVal)) { continue; } - $instance->filters[$filterKey] = $filterVal === 'true' ? '' : $filterVal; + $cleanedFilterVal = $filterVal === 'true' ? '' : $filterVal; + $cleanedFilters[] = new FilterSearchOption($cleanedFilterVal, $filterKey); } if (isset($inputs['types']) && count($inputs['types']) < 4) { - $instance->filters['type'] = implode('|', $inputs['types']); + $cleanedFilters[] = new FilterSearchOption(implode('|', $inputs['types']), 'type'); + } + + $instance->filters = new SearchOptionSet($cleanedFilters); + + // Parse and merge in extras if provided + if (!empty($inputs['extras'])) { + $extras = static::fromString($inputs['extras']); + $instance->searches = $instance->searches->merge($extras->searches); + $instance->exacts = $instance->exacts->merge($extras->exacts); + $instance->tags = $instance->tags->merge($extras->tags); + $instance->filters = $instance->filters->merge($extras->filters); } return $instance; } /** - * Decode a search string into an array of terms. + * Decode a search string and add its contents to this instance. */ - protected static function decode(string $searchString): array + protected function addOptionsFromString(string $searchString): void { + /** @var array $terms */ $terms = [ - 'searches' => [], 'exacts' => [], 'tags' => [], 'filters' => [], ]; $patterns = [ - 'exacts' => '/"((?:\\\\.|[^"\\\\])*)"/', - 'tags' => '/\[(.*?)\]/', - 'filters' => '/\{(.*?)\}/', + 'exacts' => '/-?"((?:\\\\.|[^"\\\\])*)"/', + 'tags' => '/-?\[(.*?)\]/', + 'filters' => '/-?\{(.*?)\}/', + ]; + + $constructors = [ + 'exacts' => fn(string $value, bool $negated) => new ExactSearchOption($value, $negated), + 'tags' => fn(string $value, bool $negated) => new TagSearchOption($value, $negated), + 'filters' => fn(string $value, bool $negated) => FilterSearchOption::fromContentString($value, $negated), ]; // Parse special terms @@ -88,34 +119,32 @@ protected static function decode(string $searchString): array $matches = []; preg_match_all($pattern, $searchString, $matches); if (count($matches) > 0) { - $terms[$termType] = $matches[1]; + foreach ($matches[1] as $index => $value) { + $negated = str_starts_with($matches[0][$index], '-'); + $terms[$termType][] = $constructors[$termType]($value, $negated); + } $searchString = preg_replace($pattern, '', $searchString); } } // Unescape exacts and backslash escapes - foreach ($terms['exacts'] as $index => $exact) { - $terms['exacts'][$index] = static::decodeEscapes($exact); + foreach ($terms['exacts'] as $exact) { + $exact->value = static::decodeEscapes($exact->value); } // Parse standard terms $parsedStandardTerms = static::parseStandardTermString($searchString); - array_push($terms['searches'], ...$parsedStandardTerms['terms']); - array_push($terms['exacts'], ...$parsedStandardTerms['exacts']); - - // Split filter values out - $splitFilters = []; - foreach ($terms['filters'] as $filter) { - $explodedFilter = explode(':', $filter, 2); - $splitFilters[$explodedFilter[0]] = (count($explodedFilter) > 1) ? $explodedFilter[1] : ''; - } - $terms['filters'] = $splitFilters; - - // Filter down terms where required - $terms['exacts'] = array_filter($terms['exacts']); - $terms['searches'] = array_filter($terms['searches']); - - return $terms; + $this->searches = $this->searches + ->merge(SearchOptionSet::fromValueArray($parsedStandardTerms['terms'], TermSearchOption::class)) + ->filterEmpty(); + $this->exacts = $this->exacts + ->merge(new SearchOptionSet($terms['exacts'])) + ->merge(SearchOptionSet::fromValueArray($parsedStandardTerms['exacts'], ExactSearchOption::class)) + ->filterEmpty(); + + // Add tags & filters + $this->tags = $this->tags->merge(new SearchOptionSet($terms['tags'])); + $this->filters = $this->filters->merge(new SearchOptionSet($terms['filters'])); } /** @@ -152,7 +181,7 @@ protected static function decodeEscapes(string $input): string protected static function parseStandardTermString(string $termString): array { $terms = explode(' ', $termString); - $indexDelimiters = SearchIndex::$delimiters; + $indexDelimiters = implode('', array_diff(str_split(SearchIndex::$delimiters), str_split(SearchIndex::$softDelimiters))); $parsed = [ 'terms' => [], 'exacts' => [], @@ -175,7 +204,9 @@ protected static function parseStandardTermString(string $termString): array */ public function setFilter(string $filterName, string $filterValue = ''): void { - $this->filters[$filterName] = $filterValue; + $this->filters = $this->filters->merge( + new SearchOptionSet([new FilterSearchOption($filterValue, $filterName)]) + ); } /** @@ -183,22 +214,43 @@ public function setFilter(string $filterName, string $filterValue = ''): void */ public function toString(): string { - $parts = $this->searches; + $options = [ + ...$this->searches->all(), + ...$this->exacts->all(), + ...$this->tags->all(), + ...$this->filters->all(), + ]; - foreach ($this->exacts as $term) { - $escaped = str_replace('\\', '\\\\', $term); - $escaped = str_replace('"', '\"', $escaped); - $parts[] = '"' . $escaped . '"'; - } + $parts = array_map(fn(SearchOption $o) => $o->toString(), $options); - foreach ($this->tags as $term) { - $parts[] = "[{$term}]"; - } + return implode(' ', $parts); + } - foreach ($this->filters as $filterName => $filterVal) { - $parts[] = '{' . $filterName . ($filterVal ? ':' . $filterVal : '') . '}'; + /** + * Get the search options that don't have UI controls provided for. + * Provided back as a key => value array with the keys being expected + * input names for a search form, and values being the option value. + */ + public function getAdditionalOptionsString(): string + { + $options = []; + + // Handle filters without UI support + $userFilters = ['updated_by', 'created_by', 'owned_by']; + $unsupportedFilters = ['is_template', 'sort_by']; + foreach ($this->filters->all() as $filter) { + if (in_array($filter->getKey(), $userFilters, true) && $filter->value !== null && $filter->value !== 'me') { + $options[] = $filter; + } else if (in_array($filter->getKey(), $unsupportedFilters, true)) { + $options[] = $filter; + } } - return implode(' ', $parts); + // Negated items + array_push($options, ...$this->exacts->negated()->all()); + array_push($options, ...$this->tags->negated()->all()); + array_push($options, ...$this->filters->negated()->all()); + + return implode(' ', array_map(fn(SearchOption $o) => $o->toString(), $options)); } } diff --git a/app/Search/SearchResultsFormatter.php b/app/Search/SearchResultsFormatter.php index 02a40632e22..b06f81e0e05 100644 --- a/app/Search/SearchResultsFormatter.php +++ b/app/Search/SearchResultsFormatter.php @@ -25,11 +25,12 @@ public function format(array $results, SearchOptions $options): void * Update the given entity model to set attributes used for previews of the item * primarily within search result lists. */ - protected function setSearchPreview(Entity $entity, SearchOptions $options) + protected function setSearchPreview(Entity $entity, SearchOptions $options): void { $textProperty = $entity->textField; $textContent = $entity->$textProperty; - $terms = array_merge($options->exacts, $options->searches); + $relevantSearchOptions = $options->exacts->merge($options->searches); + $terms = $relevantSearchOptions->toValueArray(); $originalContentByNewAttribute = [ 'preview_name' => $entity->name, diff --git a/app/Search/SearchRunner.php b/app/Search/SearchRunner.php index aac9d10005b..c14d1c642d9 100644 --- a/app/Search/SearchRunner.php +++ b/app/Search/SearchRunner.php @@ -3,10 +3,11 @@ namespace BookStack\Search; use BookStack\Entities\EntityProvider; -use BookStack\Entities\Models\BookChild; use BookStack\Entities\Models\Entity; use BookStack\Entities\Models\Page; +use BookStack\Entities\Queries\EntityQueries; use BookStack\Permissions\PermissionApplicator; +use BookStack\Search\Options\TagSearchOption; use BookStack\Users\Models\User; use Illuminate\Database\Connection; use Illuminate\Database\Eloquent\Builder as EloquentBuilder; @@ -16,33 +17,21 @@ use Illuminate\Support\Collection; use Illuminate\Support\Facades\DB; use Illuminate\Support\Str; -use SplObjectStorage; +use WeakMap; class SearchRunner { - protected EntityProvider $entityProvider; - protected PermissionApplicator $permissions; - - /** - * Acceptable operators to be used in a query. - * - * @var string[] - */ - protected array $queryOperators = ['<=', '>=', '=', '<', '>', 'like', '!=']; - /** * Retain a cache of score adjusted terms for specific search options. - * From PHP>=8 this can be made into a WeakMap instead. - * - * @var SplObjectStorage */ - protected $termAdjustmentCache; - - public function __construct(EntityProvider $entityProvider, PermissionApplicator $permissions) - { - $this->entityProvider = $entityProvider; - $this->permissions = $permissions; - $this->termAdjustmentCache = new SplObjectStorage(); + protected WeakMap $termAdjustmentCache; + + public function __construct( + protected EntityProvider $entityProvider, + protected PermissionApplicator $permissions, + protected EntityQueries $entityQueries, + ) { + $this->termAdjustmentCache = new WeakMap(); } /** @@ -57,10 +46,11 @@ public function searchEntities(SearchOptions $searchOpts, string $entityType = ' $entityTypes = array_keys($this->entityProvider->all()); $entityTypesToSearch = $entityTypes; + $filterMap = $searchOpts->filters->toValueMap(); if ($entityType !== 'all') { $entityTypesToSearch = [$entityType]; - } elseif (isset($searchOpts->filters['type'])) { - $entityTypesToSearch = explode('|', $searchOpts->filters['type']); + } elseif (isset($filterMap['type'])) { + $entityTypesToSearch = explode('|', $filterMap['type']); } $results = collect(); @@ -72,10 +62,9 @@ public function searchEntities(SearchOptions $searchOpts, string $entityType = ' continue; } - $entityModelInstance = $this->entityProvider->get($entityType); - $searchQuery = $this->buildQuery($searchOpts, $entityModelInstance); + $searchQuery = $this->buildQuery($searchOpts, $entityType); $entityTotal = $searchQuery->count(); - $searchResults = $this->getPageOfDataFromQuery($searchQuery, $entityModelInstance, $page, $count); + $searchResults = $this->getPageOfDataFromQuery($searchQuery, $entityType, $page, $count); if ($entityTotal > ($page * $count)) { $hasMore = true; @@ -100,7 +89,8 @@ public function searchBook(int $bookId, string $searchString): Collection { $opts = SearchOptions::fromString($searchString); $entityTypes = ['page', 'chapter']; - $entityTypesToSearch = isset($opts->filters['type']) ? explode('|', $opts->filters['type']) : $entityTypes; + $filterMap = $opts->filters->toValueMap(); + $entityTypesToSearch = isset($filterMap['type']) ? explode('|', $filterMap['type']) : $entityTypes; $results = collect(); foreach ($entityTypesToSearch as $entityType) { @@ -108,8 +98,7 @@ public function searchBook(int $bookId, string $searchString): Collection continue; } - $entityModelInstance = $this->entityProvider->get($entityType); - $search = $this->buildQuery($opts, $entityModelInstance)->where('book_id', '=', $bookId)->take(20)->get(); + $search = $this->buildQuery($opts, $entityType)->where('book_id', '=', $bookId)->take(20)->get(); $results = $results->merge($search); } @@ -122,8 +111,7 @@ public function searchBook(int $bookId, string $searchString): Collection public function searchChapter(int $chapterId, string $searchString): Collection { $opts = SearchOptions::fromString($searchString); - $entityModelInstance = $this->entityProvider->get('page'); - $pages = $this->buildQuery($opts, $entityModelInstance)->where('chapter_id', '=', $chapterId)->take(20)->get(); + $pages = $this->buildQuery($opts, 'page')->where('chapter_id', '=', $chapterId)->take(20)->get(); return $pages->sortByDesc('score'); } @@ -131,17 +119,17 @@ public function searchChapter(int $chapterId, string $searchString): Collection /** * Get a page of result data from the given query based on the provided page parameters. */ - protected function getPageOfDataFromQuery(EloquentBuilder $query, Entity $entityModelInstance, int $page = 1, int $count = 20): EloquentCollection + protected function getPageOfDataFromQuery(EloquentBuilder $query, string $entityType, int $page = 1, int $count = 20): EloquentCollection { $relations = ['tags']; - if ($entityModelInstance instanceof BookChild) { + if ($entityType === 'page' || $entityType === 'chapter') { $relations['book'] = function (BelongsTo $query) { $query->scopes('visible'); }; } - if ($entityModelInstance instanceof Page) { + if ($entityType === 'page') { $relations['chapter'] = function (BelongsTo $query) { $query->scopes('visible'); }; @@ -157,38 +145,35 @@ protected function getPageOfDataFromQuery(EloquentBuilder $query, Entity $entity /** * Create a search query for an entity. */ - protected function buildQuery(SearchOptions $searchOpts, Entity $entityModelInstance): EloquentBuilder + protected function buildQuery(SearchOptions $searchOpts, string $entityType): EloquentBuilder { - $entityQuery = $entityModelInstance->newQuery()->scopes('visible'); - - if ($entityModelInstance instanceof Page) { - $entityQuery->select(array_merge($entityModelInstance::$listAttributes, ['owned_by'])); - } else { - $entityQuery->select(['*']); - } + $entityModelInstance = $this->entityProvider->get($entityType); + $entityQuery = $this->entityQueries->visibleForList($entityType); // Handle normal search terms - $this->applyTermSearch($entityQuery, $searchOpts, $entityModelInstance); + $this->applyTermSearch($entityQuery, $searchOpts, $entityType); // Handle exact term matching - foreach ($searchOpts->exacts as $inputTerm) { - $entityQuery->where(function (EloquentBuilder $query) use ($inputTerm, $entityModelInstance) { - $inputTerm = str_replace('\\', '\\\\', $inputTerm); + foreach ($searchOpts->exacts->all() as $exact) { + $filter = function (EloquentBuilder $query) use ($exact, $entityModelInstance) { + $inputTerm = str_replace('\\', '\\\\', $exact->value); $query->where('name', 'like', '%' . $inputTerm . '%') ->orWhere($entityModelInstance->textField, 'like', '%' . $inputTerm . '%'); - }); + }; + + $exact->negated ? $entityQuery->whereNot($filter) : $entityQuery->where($filter); } // Handle tag searches - foreach ($searchOpts->tags as $inputTerm) { - $this->applyTagSearch($entityQuery, $inputTerm); + foreach ($searchOpts->tags->all() as $tagOption) { + $this->applyTagSearch($entityQuery, $tagOption); } // Handle filters - foreach ($searchOpts->filters as $filterTerm => $filterValue) { - $functionName = Str::camel('filter_' . $filterTerm); + foreach ($searchOpts->filters->all() as $filterOption) { + $functionName = Str::camel('filter_' . $filterOption->getKey()); if (method_exists($this, $functionName)) { - $this->$functionName($entityQuery, $entityModelInstance, $filterValue); + $this->$functionName($entityQuery, $entityModelInstance, $filterOption->value, $filterOption->negated); } } @@ -198,9 +183,9 @@ protected function buildQuery(SearchOptions $searchOpts, Entity $entityModelInst /** * For the given search query, apply the queries for handling the regular search terms. */ - protected function applyTermSearch(EloquentBuilder $entityQuery, SearchOptions $options, Entity $entity): void + protected function applyTermSearch(EloquentBuilder $entityQuery, SearchOptions $options, string $entityType): void { - $terms = $options->searches; + $terms = $options->searches->toValueArray(); if (count($terms) === 0) { return; } @@ -216,11 +201,11 @@ protected function applyTermSearch(EloquentBuilder $entityQuery, SearchOptions $ $subQuery->addBinding($scoreSelect['bindings'], 'select'); - $subQuery->where('entity_type', '=', $entity->getMorphClass()); + $subQuery->where('entity_type', '=', $entityType); $subQuery->where(function (Builder $query) use ($terms) { foreach ($terms as $inputTerm) { - $inputTerm = str_replace('\\', '\\\\', $inputTerm); - $query->orWhere('term', 'like', $inputTerm . '%'); + $escapedTerm = str_replace('\\', '\\\\', $inputTerm); + $query->orWhere('term', 'like', $escapedTerm . '%'); } }); $subQuery->groupBy('entity_type', 'entity_id'); @@ -274,7 +259,7 @@ protected function getTermAdjustments(SearchOptions $options): array $whenStatements = []; $whenBindings = []; - foreach ($options->searches as $term) { + foreach ($options->searches->toValueArray() as $term) { $whenStatements[] = 'WHEN term LIKE ? THEN ?'; $whenBindings[] = $term . '%'; $whenBindings[] = $term; @@ -300,7 +285,7 @@ protected function getTermAdjustments(SearchOptions $options): array * * @param array $termCounts * - * @return array + * @return array */ protected function rawTermCountsToAdjustments(array $termCounts): array { @@ -320,179 +305,165 @@ protected function rawTermCountsToAdjustments(array $termCounts): array } /** - * Get the available query operators as a regex escaped list. + * Apply a tag search term onto an entity query. */ - protected function getRegexEscapedOperators(): string + protected function applyTagSearch(EloquentBuilder $query, TagSearchOption $option): void { - $escapedOperators = []; - foreach ($this->queryOperators as $operator) { - $escapedOperators[] = preg_quote($operator); - } + $filter = function (EloquentBuilder $query) use ($option): void { + $tagParts = $option->getParts(); + if (empty($tagParts['operator']) || empty($tagParts['value'])) { + $query->where('name', '=', $tagParts['name']); + return; + } - return implode('|', $escapedOperators); - } + if (!empty($tagParts['name'])) { + $query->where('name', '=', $tagParts['name']); + } - /** - * Apply a tag search term onto a entity query. - */ - protected function applyTagSearch(EloquentBuilder $query, string $tagTerm): EloquentBuilder - { - preg_match('/^(.*?)((' . $this->getRegexEscapedOperators() . ')(.*?))?$/', $tagTerm, $tagSplit); - $query->whereHas('tags', function (EloquentBuilder $query) use ($tagSplit) { - $tagName = $tagSplit[1]; - $tagOperator = count($tagSplit) > 2 ? $tagSplit[3] : ''; - $tagValue = count($tagSplit) > 3 ? $tagSplit[4] : ''; - $validOperator = in_array($tagOperator, $this->queryOperators); - if (!empty($tagOperator) && !empty($tagValue) && $validOperator) { - if (!empty($tagName)) { - $query->where('name', '=', $tagName); - } - if (is_numeric($tagValue) && $tagOperator !== 'like') { - // We have to do a raw sql query for this since otherwise PDO will quote the value and MySQL will - // search the value as a string which prevents being able to do number-based operations - // on the tag values. We ensure it has a numeric value and then cast it just to be sure. - /** @var Connection $connection */ - $connection = $query->getConnection(); - $tagValue = (float) trim($connection->getPdo()->quote($tagValue), "'"); - $query->whereRaw("value {$tagOperator} {$tagValue}"); - } else { - if ($tagOperator === 'like') { - $tagValue = str_replace('\\', '\\\\', $tagValue); - } - $query->where('value', $tagOperator, $tagValue); - } + if (is_numeric($tagParts['value']) && $tagParts['operator'] !== 'like') { + // We have to do a raw sql query for this since otherwise PDO will quote the value and MySQL will + // search the value as a string which prevents being able to do number-based operations + // on the tag values. We ensure it has a numeric value and then cast it just to be sure. + /** @var Connection $connection */ + $connection = $query->getConnection(); + $quotedValue = (float) trim($connection->getPdo()->quote($tagParts['value']), "'"); + $query->whereRaw("value {$tagParts['operator']} {$quotedValue}"); + } else if ($tagParts['operator'] === 'like') { + $query->where('value', $tagParts['operator'], str_replace('\\', '\\\\', $tagParts['value'])); } else { - $query->where('name', '=', $tagName); + $query->where('value', $tagParts['operator'], $tagParts['value']); } - }); + }; - return $query; + $option->negated ? $query->whereDoesntHave('tags', $filter) : $query->whereHas('tags', $filter); + } + + protected function applyNegatableWhere(EloquentBuilder $query, bool $negated, string $column, string $operator, mixed $value): void + { + if ($negated) { + $query->whereNot($column, $operator, $value); + } else { + $query->where($column, $operator, $value); + } } /** * Custom entity search filters. */ - protected function filterUpdatedAfter(EloquentBuilder $query, Entity $model, $input): void + protected function filterUpdatedAfter(EloquentBuilder $query, Entity $model, string $input, bool $negated): void { - try { - $date = date_create($input); - $query->where('updated_at', '>=', $date); - } catch (\Exception $e) { - } + $date = date_create($input); + $this->applyNegatableWhere($query, $negated, 'updated_at', '>=', $date); } - protected function filterUpdatedBefore(EloquentBuilder $query, Entity $model, $input): void + protected function filterUpdatedBefore(EloquentBuilder $query, Entity $model, string $input, bool $negated): void { - try { - $date = date_create($input); - $query->where('updated_at', '<', $date); - } catch (\Exception $e) { - } + $date = date_create($input); + $this->applyNegatableWhere($query, $negated, 'updated_at', '<', $date); } - protected function filterCreatedAfter(EloquentBuilder $query, Entity $model, $input): void + protected function filterCreatedAfter(EloquentBuilder $query, Entity $model, string $input, bool $negated): void { - try { - $date = date_create($input); - $query->where('created_at', '>=', $date); - } catch (\Exception $e) { - } + $date = date_create($input); + $this->applyNegatableWhere($query, $negated, 'created_at', '>=', $date); } - protected function filterCreatedBefore(EloquentBuilder $query, Entity $model, $input) + protected function filterCreatedBefore(EloquentBuilder $query, Entity $model, string $input, bool $negated) { - try { - $date = date_create($input); - $query->where('created_at', '<', $date); - } catch (\Exception $e) { - } + $date = date_create($input); + $this->applyNegatableWhere($query, $negated, 'created_at', '<', $date); } - protected function filterCreatedBy(EloquentBuilder $query, Entity $model, $input) + protected function filterCreatedBy(EloquentBuilder $query, Entity $model, string $input, bool $negated) { $userSlug = $input === 'me' ? user()->slug : trim($input); $user = User::query()->where('slug', '=', $userSlug)->first(['id']); if ($user) { - $query->where('created_by', '=', $user->id); + $this->applyNegatableWhere($query, $negated, 'created_by', '=', $user->id); } } - protected function filterUpdatedBy(EloquentBuilder $query, Entity $model, $input) + protected function filterUpdatedBy(EloquentBuilder $query, Entity $model, string $input, bool $negated) { $userSlug = $input === 'me' ? user()->slug : trim($input); $user = User::query()->where('slug', '=', $userSlug)->first(['id']); if ($user) { - $query->where('updated_by', '=', $user->id); + $this->applyNegatableWhere($query, $negated, 'updated_by', '=', $user->id); } } - protected function filterOwnedBy(EloquentBuilder $query, Entity $model, $input) + protected function filterOwnedBy(EloquentBuilder $query, Entity $model, string $input, bool $negated) { $userSlug = $input === 'me' ? user()->slug : trim($input); $user = User::query()->where('slug', '=', $userSlug)->first(['id']); if ($user) { - $query->where('owned_by', '=', $user->id); + $this->applyNegatableWhere($query, $negated, 'owned_by', '=', $user->id); } } - protected function filterInName(EloquentBuilder $query, Entity $model, $input) + protected function filterInName(EloquentBuilder $query, Entity $model, string $input, bool $negated) { - $query->where('name', 'like', '%' . $input . '%'); + $this->applyNegatableWhere($query, $negated, 'name', 'like', '%' . $input . '%'); } - protected function filterInTitle(EloquentBuilder $query, Entity $model, $input) + protected function filterInTitle(EloquentBuilder $query, Entity $model, string $input, bool $negated) { - $this->filterInName($query, $model, $input); + $this->filterInName($query, $model, $input, $negated); } - protected function filterInBody(EloquentBuilder $query, Entity $model, $input) + protected function filterInBody(EloquentBuilder $query, Entity $model, string $input, bool $negated) { - $query->where($model->textField, 'like', '%' . $input . '%'); + $this->applyNegatableWhere($query, $negated, $model->textField, 'like', '%' . $input . '%'); } - protected function filterIsRestricted(EloquentBuilder $query, Entity $model, $input) + protected function filterIsRestricted(EloquentBuilder $query, Entity $model, string $input, bool $negated) { - $query->whereHas('permissions'); + $negated ? $query->whereDoesntHave('permissions') : $query->whereHas('permissions'); } - protected function filterViewedByMe(EloquentBuilder $query, Entity $model, $input) + protected function filterViewedByMe(EloquentBuilder $query, Entity $model, string $input, bool $negated) { - $query->whereHas('views', function ($query) { + $filter = function ($query) { $query->where('user_id', '=', user()->id); - }); + }; + + $negated ? $query->whereDoesntHave('views', $filter) : $query->whereHas('views', $filter); } - protected function filterNotViewedByMe(EloquentBuilder $query, Entity $model, $input) + protected function filterNotViewedByMe(EloquentBuilder $query, Entity $model, string $input, bool $negated) { - $query->whereDoesntHave('views', function ($query) { + $filter = function ($query) { $query->where('user_id', '=', user()->id); - }); + }; + + $negated ? $query->whereHas('views', $filter) : $query->whereDoesntHave('views', $filter); } - protected function filterIsTemplate(EloquentBuilder $query, Entity $model, $input) + protected function filterIsTemplate(EloquentBuilder $query, Entity $model, string $input, bool $negated) { if ($model instanceof Page) { - $query->where('template', '=', true); + $this->applyNegatableWhere($query, $negated, 'template', '=', true); } } - protected function filterSortBy(EloquentBuilder $query, Entity $model, $input) + protected function filterSortBy(EloquentBuilder $query, Entity $model, string $input, bool $negated) { $functionName = Str::camel('sort_by_' . $input); if (method_exists($this, $functionName)) { - $this->$functionName($query, $model); + $this->$functionName($query, $model, $negated); } } /** * Sorting filter options. */ - protected function sortByLastCommented(EloquentBuilder $query, Entity $model) + protected function sortByLastCommented(EloquentBuilder $query, Entity $model, bool $negated) { $commentsTable = DB::getTablePrefix() . 'comments'; $morphClass = str_replace('\\', '\\\\', $model->getMorphClass()); $commentQuery = DB::raw('(SELECT c1.entity_id, c1.entity_type, c1.created_at as last_commented FROM ' . $commentsTable . ' c1 LEFT JOIN ' . $commentsTable . ' c2 ON (c1.entity_id = c2.entity_id AND c1.entity_type = c2.entity_type AND c1.created_at < c2.created_at) WHERE c1.entity_type = \'' . $morphClass . '\' AND c2.created_at IS NULL) as comments'); - $query->join($commentQuery, $model->getTable() . '.id', '=', 'comments.entity_id')->orderBy('last_commented', 'desc'); + $query->join($commentQuery, $model->getTable() . '.id', '=', DB::raw('comments.entity_id')) + ->orderBy('last_commented', $negated ? 'asc' : 'desc'); } } diff --git a/app/Search/SearchTextTokenizer.php b/app/Search/SearchTextTokenizer.php new file mode 100644 index 00000000000..f43fd56f113 --- /dev/null +++ b/app/Search/SearchTextTokenizer.php @@ -0,0 +1,70 @@ +length = strlen($this->text); + } + + /** + * Get the current delimiter to be found. + */ + public function currentDelimiter(): string + { + return $this->currentDelimiter; + } + + /** + * Get the previous delimiter found. + */ + public function previousDelimiter(): string + { + return $this->previousDelimiter; + } + + /** + * Get the next token between delimiters. + * Returns false if there's no further tokens. + */ + public function next(): string|false + { + $token = ''; + + for ($i = $this->currentIndex; $i < $this->length; $i++) { + $char = $this->text[$i]; + if (str_contains($this->delimiters, $char)) { + $this->previousDelimiter = $this->currentDelimiter; + $this->currentDelimiter = $char; + $this->currentIndex = $i + 1; + return $token; + } + + $token .= $char; + } + + if ($token) { + $this->currentIndex = $this->length; + $this->previousDelimiter = $this->currentDelimiter; + $this->currentDelimiter = ''; + return $token; + } + + return false; + } +} diff --git a/app/Settings/MaintenanceController.php b/app/Settings/MaintenanceController.php index 62eeecf39a9..b2b2226bf98 100644 --- a/app/Settings/MaintenanceController.php +++ b/app/Settings/MaintenanceController.php @@ -3,8 +3,10 @@ namespace BookStack\Settings; use BookStack\Activity\ActivityType; +use BookStack\App\AppVersion; use BookStack\Entities\Tools\TrashCan; use BookStack\Http\Controller; +use BookStack\Permissions\Permission; use BookStack\References\ReferenceStore; use BookStack\Uploads\ImageService; use Illuminate\Http\Request; @@ -14,19 +16,16 @@ class MaintenanceController extends Controller /** * Show the page for application maintenance. */ - public function index() + public function index(TrashCan $trashCan) { - $this->checkPermission('settings-manage'); + $this->checkPermission(Permission::SettingsManage); $this->setPageTitle(trans('settings.maint')); - // Get application version - $version = trim(file_get_contents(base_path('version'))); - // Recycle bin details - $recycleStats = (new TrashCan())->getTrashedCounts(); + $recycleStats = $trashCan->getTrashedCounts(); return view('settings.maintenance', [ - 'version' => $version, + 'version' => AppVersion::get(), 'recycleStats' => $recycleStats, ]); } @@ -36,7 +35,7 @@ public function index() */ public function cleanupImages(Request $request, ImageService $imageService) { - $this->checkPermission('settings-manage'); + $this->checkPermission(Permission::SettingsManage); $this->logActivity(ActivityType::MAINTENANCE_ACTION_RUN, 'cleanup-images'); $checkRevisions = !($request->get('ignore_revisions', 'false') === 'true'); @@ -64,7 +63,7 @@ public function cleanupImages(Request $request, ImageService $imageService) */ public function sendTestEmail() { - $this->checkPermission('settings-manage'); + $this->checkPermission(Permission::SettingsManage); $this->logActivity(ActivityType::MAINTENANCE_ACTION_RUN, 'send-test-email'); try { @@ -83,7 +82,7 @@ public function sendTestEmail() */ public function regenerateReferences(ReferenceStore $referenceStore) { - $this->checkPermission('settings-manage'); + $this->checkPermission(Permission::SettingsManage); $this->logActivity(ActivityType::MAINTENANCE_ACTION_RUN, 'regenerate-references'); try { diff --git a/app/Settings/SettingController.php b/app/Settings/SettingController.php index bdbc3c78a39..f89bc6dab6b 100644 --- a/app/Settings/SettingController.php +++ b/app/Settings/SettingController.php @@ -3,14 +3,14 @@ namespace BookStack\Settings; use BookStack\Activity\ActivityType; +use BookStack\App\AppVersion; use BookStack\Http\Controller; +use BookStack\Permissions\Permission; use BookStack\Users\Models\User; use Illuminate\Http\Request; class SettingController extends Controller { - protected array $settingCategories = ['features', 'customization', 'registration']; - /** * Handle requests to the settings index path. */ @@ -25,15 +25,12 @@ public function index() public function category(string $category) { $this->ensureCategoryExists($category); - $this->checkPermission('settings-manage'); + $this->checkPermission(Permission::SettingsManage); $this->setPageTitle(trans('settings.settings')); - // Get application version - $version = trim(file_get_contents(base_path('version'))); - - return view('settings.' . $category, [ + return view('settings.categories.' . $category, [ 'category' => $category, - 'version' => $version, + 'version' => AppVersion::get(), 'guestUser' => User::getGuest(), ]); } @@ -45,7 +42,7 @@ public function update(Request $request, AppSettingsStore $store, string $catego { $this->ensureCategoryExists($category); $this->preventAccessInDemoMode(); - $this->checkPermission('settings-manage'); + $this->checkPermission(Permission::SettingsManage); $this->validate($request, [ 'app_logo' => ['nullable', ...$this->getImageValidationRules()], 'app_icon' => ['nullable', ...$this->getImageValidationRules()], @@ -59,7 +56,7 @@ public function update(Request $request, AppSettingsStore $store, string $catego protected function ensureCategoryExists(string $category): void { - if (!in_array($category, $this->settingCategories)) { + if (!view()->exists('settings.categories.' . $category)) { abort(404); } } diff --git a/app/Sorting/BookSortController.php b/app/Sorting/BookSortController.php new file mode 100644 index 00000000000..7e2ee5465df --- /dev/null +++ b/app/Sorting/BookSortController.php @@ -0,0 +1,89 @@ +queries->findVisibleBySlugOrFail($bookSlug); + $this->checkOwnablePermission(Permission::BookUpdate, $book); + + $bookChildren = (new BookContents($book))->getTree(false); + + $this->setPageTitle(trans('entities.books_sort_named', ['bookName' => $book->getShortName()])); + + return view('books.sort', ['book' => $book, 'current' => $book, 'bookChildren' => $bookChildren]); + } + + /** + * Shows the sort box for a single book. + * Used via AJAX when loading in extra books to a sort. + */ + public function showItem(string $bookSlug) + { + $book = $this->queries->findVisibleBySlugOrFail($bookSlug); + $bookChildren = (new BookContents($book))->getTree(); + + return view('books.parts.sort-box', ['book' => $book, 'bookChildren' => $bookChildren]); + } + + /** + * Update the sort options of a book, setting the auto-sort and/or updating + * child order via mapping. + */ + public function update(Request $request, BookSorter $sorter, string $bookSlug) + { + $book = $this->queries->findVisibleBySlugOrFail($bookSlug); + $this->checkOwnablePermission(Permission::BookUpdate, $book); + $loggedActivityForBook = false; + + // Sort via map + if ($request->filled('sort-tree')) { + (new DatabaseTransaction(function () use ($book, $request, $sorter, &$loggedActivityForBook) { + $sortMap = BookSortMap::fromJson($request->get('sort-tree')); + $booksInvolved = $sorter->sortUsingMap($sortMap); + + // Add activity for involved books. + foreach ($booksInvolved as $bookInvolved) { + Activity::add(ActivityType::BOOK_SORT, $bookInvolved); + if ($bookInvolved->id === $book->id) { + $loggedActivityForBook = true; + } + } + }))->run(); + } + + if ($request->filled('auto-sort')) { + $sortSetId = intval($request->get('auto-sort')) ?: null; + if ($sortSetId && SortRule::query()->find($sortSetId) === null) { + $sortSetId = null; + } + $book->sort_rule_id = $sortSetId; + $book->save(); + $sorter->runBookAutoSort($book); + if (!$loggedActivityForBook) { + Activity::add(ActivityType::BOOK_SORT, $book); + } + } + + return redirect($book->getUrl()); + } +} diff --git a/app/Entities/Tools/BookSortMap.php b/app/Sorting/BookSortMap.php similarity index 96% rename from app/Entities/Tools/BookSortMap.php rename to app/Sorting/BookSortMap.php index ff1ec767f78..96c9d342a27 100644 --- a/app/Entities/Tools/BookSortMap.php +++ b/app/Sorting/BookSortMap.php @@ -1,6 +1,6 @@ books()->chunk(50, function ($books) { + foreach ($books as $book) { + $this->runBookAutoSort($book); + } + }); + } + + /** + * Runs the auto-sort for a book if the book has a sort set applied to it. + * This does not consider permissions since the sort operations are centrally + * managed by admins so considered permitted if existing and assigned. + */ + public function runBookAutoSort(Book $book): void + { + $set = $book->sortRule; + if (!$set) { + return; + } + + $sortFunctions = array_map(function (SortRuleOperation $op) { + return $op->getSortFunction(); + }, $set->getOperations()); + + $chapters = $book->chapters() + ->with('pages:id,name,priority,created_at,updated_at,chapter_id') + ->get(['id', 'name', 'priority', 'created_at', 'updated_at']); + + /** @var (Chapter|Book)[] $topItems */ + $topItems = [ + ...$book->directPages()->get(['id', 'name', 'priority', 'created_at', 'updated_at']), + ...$chapters, + ]; + + foreach ($sortFunctions as $sortFunction) { + usort($topItems, $sortFunction); + } + + foreach ($topItems as $index => $topItem) { + $topItem->priority = $index + 1; + $topItem::withoutTimestamps(fn () => $topItem->save()); + } + + foreach ($chapters as $chapter) { + $pages = $chapter->pages->all(); + foreach ($sortFunctions as $sortFunction) { + usort($pages, $sortFunction); + } + + foreach ($pages as $index => $page) { + $page->priority = $index + 1; + $page::withoutTimestamps(fn () => $page->save()); + } + } + } + + + /** + * Sort the books content using the given sort map. + * Returns a list of books that were involved in the operation. + * + * @return Book[] + */ + public function sortUsingMap(BookSortMap $sortMap): array + { + // Load models into map + $modelMap = $this->loadModelsFromSortMap($sortMap); + + // Sort our changes from our map to be chapters first + // Since they need to be process to ensure book alignment for child page changes. + $sortMapItems = $sortMap->all(); + usort($sortMapItems, function (BookSortMapItem $itemA, BookSortMapItem $itemB) { + $aScore = $itemA->type === 'page' ? 2 : 1; + $bScore = $itemB->type === 'page' ? 2 : 1; + + return $aScore - $bScore; + }); + + // Perform the sort + foreach ($sortMapItems as $item) { + $this->applySortUpdates($item, $modelMap); + } + + /** @var Book[] $booksInvolved */ + $booksInvolved = array_values(array_filter($modelMap, function (string $key) { + return str_starts_with($key, 'book:'); + }, ARRAY_FILTER_USE_KEY)); + + // Update permissions of books involved + foreach ($booksInvolved as $book) { + $book->rebuildPermissions(); + } + + return $booksInvolved; + } + + /** + * Using the given sort map item, detect changes for the related model + * and update it if required. Changes where permissions are lacking will + * be skipped and not throw an error. + * + * @param array $modelMap + */ + protected function applySortUpdates(BookSortMapItem $sortMapItem, array $modelMap): void + { + /** @var BookChild $model */ + $model = $modelMap[$sortMapItem->type . ':' . $sortMapItem->id] ?? null; + if (!$model) { + return; + } + + $priorityChanged = $model->priority !== $sortMapItem->sort; + $bookChanged = $model->book_id !== $sortMapItem->parentBookId; + $chapterChanged = ($model instanceof Page) && $model->chapter_id !== $sortMapItem->parentChapterId; + + // Stop if there's no change + if (!$priorityChanged && !$bookChanged && !$chapterChanged) { + return; + } + + $currentParentKey = 'book:' . $model->book_id; + if ($model instanceof Page && $model->chapter_id) { + $currentParentKey = 'chapter:' . $model->chapter_id; + } + + $currentParent = $modelMap[$currentParentKey] ?? null; + /** @var Book $newBook */ + $newBook = $modelMap['book:' . $sortMapItem->parentBookId] ?? null; + /** @var ?Chapter $newChapter */ + $newChapter = $sortMapItem->parentChapterId ? ($modelMap['chapter:' . $sortMapItem->parentChapterId] ?? null) : null; + + if (!$this->isSortChangePermissible($sortMapItem, $model, $currentParent, $newBook, $newChapter)) { + return; + } + + // Action the required changes + if ($bookChanged) { + $model->changeBook($newBook->id); + } + + if ($model instanceof Page && $chapterChanged) { + $model->chapter_id = $newChapter->id ?? 0; + } + + if ($priorityChanged) { + $model->priority = $sortMapItem->sort; + } + + if ($chapterChanged || $priorityChanged) { + $model::withoutTimestamps(fn () => $model->save()); + } + } + + /** + * Check if the current user has permissions to apply the given sorting change. + * Is quite complex since items can gain a different parent change. Acts as a: + * - Update of old parent element (Change of content/order). + * - Update of sorted/moved element. + * - Deletion of element (Relative to parent upon move). + * - Creation of element within parent (Upon move to new parent). + */ + protected function isSortChangePermissible(BookSortMapItem $sortMapItem, BookChild $model, ?Entity $currentParent, ?Entity $newBook, ?Entity $newChapter): bool + { + // Stop if we can't see the current parent or new book. + if (!$currentParent || !$newBook) { + return false; + } + + $hasNewParent = $newBook->id !== $model->book_id || ($model instanceof Page && $model->chapter_id !== ($sortMapItem->parentChapterId ?? 0)); + if ($model instanceof Chapter) { + $hasPermission = userCan(Permission::BookUpdate, $currentParent) + && userCan(Permission::BookUpdate, $newBook) + && userCan(Permission::ChapterUpdate, $model) + && (!$hasNewParent || userCan(Permission::ChapterCreate, $newBook)) + && (!$hasNewParent || userCan(Permission::ChapterDelete, $model)); + + if (!$hasPermission) { + return false; + } + } + + if ($model instanceof Page) { + $parentPermission = ($currentParent instanceof Chapter) ? 'chapter-update' : 'book-update'; + $hasCurrentParentPermission = userCan($parentPermission, $currentParent); + + // This needs to check if there was an intended chapter location in the original sort map + // rather than inferring from the $newChapter since that variable may be null + // due to other reasons (Visibility). + $newParent = $sortMapItem->parentChapterId ? $newChapter : $newBook; + if (!$newParent) { + return false; + } + + $hasPageEditPermission = userCan(Permission::PageUpdate, $model); + $newParentInRightLocation = ($newParent instanceof Book || ($newParent instanceof Chapter && $newParent->book_id === $newBook->id)); + $newParentPermission = ($newParent instanceof Chapter) ? 'chapter-update' : 'book-update'; + $hasNewParentPermission = userCan($newParentPermission, $newParent); + + $hasDeletePermissionIfMoving = (!$hasNewParent || userCan(Permission::PageDelete, $model)); + $hasCreatePermissionIfMoving = (!$hasNewParent || userCan(Permission::PageCreate, $newParent)); + + $hasPermission = $hasCurrentParentPermission + && $newParentInRightLocation + && $hasNewParentPermission + && $hasPageEditPermission + && $hasDeletePermissionIfMoving + && $hasCreatePermissionIfMoving; + + if (!$hasPermission) { + return false; + } + } + + return true; + } + + /** + * Load models from the database into the given sort map. + * + * @return array + */ + protected function loadModelsFromSortMap(BookSortMap $sortMap): array + { + $modelMap = []; + $ids = [ + 'chapter' => [], + 'page' => [], + 'book' => [], + ]; + + foreach ($sortMap->all() as $sortMapItem) { + $ids[$sortMapItem->type][] = $sortMapItem->id; + $ids['book'][] = $sortMapItem->parentBookId; + if ($sortMapItem->parentChapterId) { + $ids['chapter'][] = $sortMapItem->parentChapterId; + } + } + + $pages = $this->queries->pages->visibleForList()->whereIn('id', array_unique($ids['page']))->get(); + /** @var Page $page */ + foreach ($pages as $page) { + $modelMap['page:' . $page->id] = $page; + $ids['book'][] = $page->book_id; + if ($page->chapter_id) { + $ids['chapter'][] = $page->chapter_id; + } + } + + $chapters = $this->queries->chapters->visibleForList()->whereIn('id', array_unique($ids['chapter']))->get(); + /** @var Chapter $chapter */ + foreach ($chapters as $chapter) { + $modelMap['chapter:' . $chapter->id] = $chapter; + $ids['book'][] = $chapter->book_id; + } + + $books = $this->queries->books->visibleForList()->whereIn('id', array_unique($ids['book']))->get(); + /** @var Book $book */ + foreach ($books as $book) { + $modelMap['book:' . $book->id] = $book; + } + + return $modelMap; + } +} diff --git a/app/Sorting/SortRule.php b/app/Sorting/SortRule.php new file mode 100644 index 00000000000..45e5514fd5f --- /dev/null +++ b/app/Sorting/SortRule.php @@ -0,0 +1,63 @@ +sequence); + } + + /** + * @param SortRuleOperation[] $options + */ + public function setOperations(array $options): void + { + $values = array_map(fn (SortRuleOperation $opt) => $opt->value, $options); + $this->sequence = implode(',', $values); + } + + public function logDescriptor(): string + { + return "({$this->id}) {$this->name}"; + } + + public function getUrl(): string + { + return url("/settings/sorting/rules/{$this->id}"); + } + + public function books(): HasMany + { + return $this->hasMany(Book::class); + } + + public static function allByName(): Collection + { + return static::query() + ->withCount('books') + ->orderBy('name', 'asc') + ->get(); + } +} diff --git a/app/Sorting/SortRuleController.php b/app/Sorting/SortRuleController.php new file mode 100644 index 00000000000..bb5540a2a50 --- /dev/null +++ b/app/Sorting/SortRuleController.php @@ -0,0 +1,115 @@ +middleware(Permission::SettingsManage->middleware()); + } + + public function create() + { + $this->setPageTitle(trans('settings.sort_rule_create')); + + return view('settings.sort-rules.create'); + } + + public function store(Request $request) + { + $this->validate($request, [ + 'name' => ['required', 'string', 'min:1', 'max:200'], + 'sequence' => ['required', 'string', 'min:1'], + ]); + + $operations = SortRuleOperation::fromSequence($request->input('sequence')); + if (count($operations) === 0) { + return redirect('/settings/sorting/rules/new')->withInput()->withErrors(['sequence' => 'No operations set.']); + } + + $rule = new SortRule(); + $rule->name = $request->input('name'); + $rule->setOperations($operations); + $rule->save(); + + $this->logActivity(ActivityType::SORT_RULE_CREATE, $rule); + + return redirect('/settings/sorting'); + } + + public function edit(string $id) + { + $rule = SortRule::query()->findOrFail($id); + + $this->setPageTitle(trans('settings.sort_rule_edit')); + + return view('settings.sort-rules.edit', ['rule' => $rule]); + } + + public function update(string $id, Request $request, BookSorter $bookSorter) + { + $this->validate($request, [ + 'name' => ['required', 'string', 'min:1', 'max:200'], + 'sequence' => ['required', 'string', 'min:1'], + ]); + + $rule = SortRule::query()->findOrFail($id); + $operations = SortRuleOperation::fromSequence($request->input('sequence')); + if (count($operations) === 0) { + return redirect($rule->getUrl())->withInput()->withErrors(['sequence' => 'No operations set.']); + } + + $rule->name = $request->input('name'); + $rule->setOperations($operations); + $changedSequence = $rule->isDirty('sequence'); + $rule->save(); + + $this->logActivity(ActivityType::SORT_RULE_UPDATE, $rule); + + if ($changedSequence) { + $bookSorter->runBookAutoSortForAllWithSet($rule); + } + + return redirect('/settings/sorting'); + } + + public function destroy(string $id, Request $request) + { + $rule = SortRule::query()->findOrFail($id); + $confirmed = $request->input('confirm') === 'true'; + $booksAssigned = $rule->books()->count(); + $warnings = []; + + if ($booksAssigned > 0) { + if ($confirmed) { + $rule->books()->update(['sort_rule_id' => null]); + } else { + $warnings[] = trans('settings.sort_rule_delete_warn_books', ['count' => $booksAssigned]); + } + } + + $defaultBookSortSetting = intval(setting('sorting-book-default', '0')); + if ($defaultBookSortSetting === intval($id)) { + if ($confirmed) { + setting()->remove('sorting-book-default'); + } else { + $warnings[] = trans('settings.sort_rule_delete_warn_default'); + } + } + + if (count($warnings) > 0) { + return redirect($rule->getUrl() . '#delete')->withErrors(['delete' => $warnings]); + } + + $rule->delete(); + $this->logActivity(ActivityType::SORT_RULE_DELETE, $rule); + + return redirect('/settings/sorting'); + } +} diff --git a/app/Sorting/SortRuleOperation.php b/app/Sorting/SortRuleOperation.php new file mode 100644 index 00000000000..27bb9e3c0e2 --- /dev/null +++ b/app/Sorting/SortRuleOperation.php @@ -0,0 +1,69 @@ +value; + $label = ''; + if (str_ends_with($key, '_asc')) { + $key = substr($key, 0, -4); + $label = trans('settings.sort_rule_op_asc'); + } elseif (str_ends_with($key, '_desc')) { + $key = substr($key, 0, -5); + $label = trans('settings.sort_rule_op_desc'); + } + + $label = trans('settings.sort_rule_op_' . $key) . ' ' . $label; + return trim($label); + } + + public function getSortFunction(): callable + { + $camelValue = Str::camel($this->value); + return SortSetOperationComparisons::$camelValue(...); + } + + /** + * @return SortRuleOperation[] + */ + public static function allExcluding(array $operations): array + { + $all = SortRuleOperation::cases(); + $filtered = array_filter($all, function (SortRuleOperation $operation) use ($operations) { + return !in_array($operation, $operations); + }); + return array_values($filtered); + } + + /** + * Create a set of operations from a string sequence representation. + * (values seperated by commas). + * @return SortRuleOperation[] + */ + public static function fromSequence(string $sequence): array + { + $strOptions = explode(',', $sequence); + $options = array_map(fn ($val) => SortRuleOperation::tryFrom($val), $strOptions); + return array_filter($options); + } +} diff --git a/app/Sorting/SortSetOperationComparisons.php b/app/Sorting/SortSetOperationComparisons.php new file mode 100644 index 00000000000..cb20e1860f7 --- /dev/null +++ b/app/Sorting/SortSetOperationComparisons.php @@ -0,0 +1,72 @@ +name, null)) <=> strtolower(ASCII::to_transliterate($b->name, null)); + } + + public static function nameDesc(Entity $a, Entity $b): int + { + return strtolower(ASCII::to_transliterate($b->name, null)) <=> strtolower(ASCII::to_transliterate($a->name, null)); + } + + public static function nameNumericAsc(Entity $a, Entity $b): int + { + $numRegex = '/^\d+(\.\d+)?/'; + $aMatches = []; + $bMatches = []; + preg_match($numRegex, $a->name, $aMatches); + preg_match($numRegex, $b->name, $bMatches); + $aVal = floatval(($aMatches[0] ?? 0)); + $bVal = floatval(($bMatches[0] ?? 0)); + + return $aVal <=> $bVal; + } + + public static function nameNumericDesc(Entity $a, Entity $b): int + { + return -(static::nameNumericAsc($a, $b)); + } + + public static function createdDateAsc(Entity $a, Entity $b): int + { + return $a->created_at->unix() <=> $b->created_at->unix(); + } + + public static function createdDateDesc(Entity $a, Entity $b): int + { + return $b->created_at->unix() <=> $a->created_at->unix(); + } + + public static function updatedDateAsc(Entity $a, Entity $b): int + { + return $a->updated_at->unix() <=> $b->updated_at->unix(); + } + + public static function updatedDateDesc(Entity $a, Entity $b): int + { + return $b->updated_at->unix() <=> $a->updated_at->unix(); + } + + public static function chaptersFirst(Entity $a, Entity $b): int + { + return ($b instanceof Chapter ? 1 : 0) - (($a instanceof Chapter) ? 1 : 0); + } + + public static function chaptersLast(Entity $a, Entity $b): int + { + return ($a instanceof Chapter ? 1 : 0) - (($b instanceof Chapter) ? 1 : 0); + } +} diff --git a/app/Sorting/SortUrl.php b/app/Sorting/SortUrl.php new file mode 100644 index 00000000000..f01df2c36a0 --- /dev/null +++ b/app/Sorting/SortUrl.php @@ -0,0 +1,49 @@ +path, $this->data, $overrideData); + } + + public function build(): string + { + $queryStringSections = []; + $queryData = array_merge($this->data, $this->overrideData); + + // Change sorting direction if already sorted on current attribute + if (isset($this->overrideData['sort']) && $this->overrideData['sort'] === $this->data['sort']) { + $queryData['order'] = ($this->data['order'] === 'asc') ? 'desc' : 'asc'; + } elseif (isset($this->overrideData['sort'])) { + $queryData['order'] = 'asc'; + } + + foreach ($queryData as $name => $value) { + $trimmedVal = trim($value); + if ($trimmedVal !== '') { + $queryStringSections[] = urlencode($name) . '=' . urlencode($trimmedVal); + } + } + + if (count($queryStringSections) === 0) { + return url($this->path); + } + + return url($this->path . '?' . implode('&', $queryStringSections)); + } +} diff --git a/app/Theming/ThemeController.php b/app/Theming/ThemeController.php new file mode 100644 index 00000000000..1eecc697428 --- /dev/null +++ b/app/Theming/ThemeController.php @@ -0,0 +1,31 @@ +download()->streamedFileInline($filePath); + $response->setMaxAge(86400); + + return $response; + } +} diff --git a/app/Theming/ThemeEvents.php b/app/Theming/ThemeEvents.php index ff9e86e16f1..44630acaeb1 100644 --- a/app/Theming/ThemeEvents.php +++ b/app/Theming/ThemeEvents.php @@ -23,7 +23,7 @@ class ThemeEvents * The provided $detail can be a string or a loggable type of model. You should check * the type before making use of this parameter. * - * @param string $type + * @param string $type * @param string|\BookStack\Activity\Models\Loggable $detail */ const ACTIVITY_LOGGED = 'activity_logged'; @@ -42,18 +42,37 @@ class ThemeEvents * system as a standard app user. This includes a user becoming logged in * after registration. This is not emitted upon API usage. * - * @param string $authSystem + * @param string $authSystem * @param \BookStack\Users\Models\User $user */ const AUTH_LOGIN = 'auth_login'; + /** + * Auth pre-register event. + * Runs right before a new user account is registered in the system by any authentication + * system as a standard app user including auto-registration systems used by LDAP, + * SAML, OIDC and social systems. It only includes self-registrations, + * not accounts created by others in the UI or via the REST API. + * It runs after any other normal validation steps. + * Any account/email confirmation occurs post-registration. + * The provided $userData contains the main details that would be used to create + * the account, and may depend on authentication method. + * If false is returned from the event, registration will be prevented and the user + * will be returned to the login page. + * + * @param string $authSystem + * @param array $userData + * @return bool|null + */ + const AUTH_PRE_REGISTER = 'auth_pre_register'; + /** * Auth register event. * Runs right after a user is newly registered to the application by any authentication * system as a standard app user. This includes auto-registration systems used - * by LDAP, SAML and social systems. It only includes self-registrations. + * by LDAP, SAML, OIDC and social systems. It only includes self-registrations. * - * @param string $authSystem + * @param string $authSystem * @param \BookStack\Users\Models\User $user */ const AUTH_REGISTER = 'auth_register'; @@ -64,7 +83,7 @@ class ThemeEvents * If the listener returns a non-null value, that will be used as an environment instead. * * @param \League\CommonMark\Environment\Environment $environment - * @returns \League\CommonMark\Environment\Environment|null + * @return \League\CommonMark\Environment\Environment|null */ const COMMONMARK_ENVIRONMENT_CONFIGURE = 'commonmark_environment_configure'; @@ -77,7 +96,7 @@ class ThemeEvents * * @param array $idTokenData * @param array $accessTokenData - * @returns array|null + * @return array|null */ const OIDC_ID_TOKEN_PRE_VALIDATE = 'oidc_id_token_pre_validate'; @@ -91,8 +110,8 @@ class ThemeEvents * * @param string $tagReference * @param string $replacementHTML - * @param \BookStack\Entities\Models\Page $currentPage - * @param ?\BookStack\Entities\Models\Page $referencedPage + * @param \BookStack\Entities\Models\Page $currentPage + * @param ?\BookStack\Entities\Models\Page $referencedPage */ const PAGE_INCLUDE_PARSE = 'page_include_parse'; @@ -123,7 +142,7 @@ class ThemeEvents * Return values, if provided, will be used as a new response to use. * * @param \Illuminate\Http\Request $request - * @returns \Illuminate\Http\Response|null + * @return \Illuminate\Http\Response|null */ const WEB_MIDDLEWARE_BEFORE = 'web_middleware_before'; @@ -133,9 +152,9 @@ class ThemeEvents * Provides both the original request and the currently resolved response. * Return values, if provided, will be used as a new response to use. * - * @param \Illuminate\Http\Request $request + * @param \Illuminate\Http\Request $request * @param \Illuminate\Http\Response|\Symfony\Component\HttpFoundation\BinaryFileResponse $response - * @returns \Illuminate\Http\Response|null + * @return \Illuminate\Http\Response|null */ const WEB_MIDDLEWARE_AFTER = 'web_middleware_after'; @@ -149,12 +168,12 @@ class ThemeEvents * If the listener returns a non-null value, that will be used as the POST data instead * of the system default. * - * @param string $event - * @param \BookStack\Activity\Models\Webhook $webhook + * @param string $event + * @param \BookStack\Activity\Models\Webhook $webhook * @param string|\BookStack\Activity\Models\Loggable $detail - * @param \BookStack\Users\Models\User $initiator - * @param int $initiatedTime - * @returns array|null + * @param \BookStack\Users\Models\User $initiator + * @param int $initiatedTime + * @return array|null */ const WEBHOOK_CALL_BEFORE = 'webhook_call_before'; } diff --git a/app/Theming/ThemeService.php b/app/Theming/ThemeService.php index 94e4712176b..4bdb6836b02 100644 --- a/app/Theming/ThemeService.php +++ b/app/Theming/ThemeService.php @@ -15,6 +15,15 @@ class ThemeService */ protected array $listeners = []; + /** + * Get the currently configured theme. + * Returns an empty string if not configured. + */ + public function getTheme(): string + { + return config('view.theme') ?? ''; + } + /** * Listen to a given custom theme event, * setting up the action to be ran when the event occurs. @@ -84,7 +93,7 @@ public function readThemeActions(): void /** * @see SocialDriverManager::addSocialDriver */ - public function addSocialDriver(string $driverName, array $config, string $socialiteHandler, callable $configureForRedirect = null): void + public function addSocialDriver(string $driverName, array $config, string $socialiteHandler, ?callable $configureForRedirect = null): void { $driverManager = app()->make(SocialDriverManager::class); $driverManager->addSocialDriver($driverName, $config, $socialiteHandler, $configureForRedirect); diff --git a/app/Translation/FileLoader.php b/app/Translation/FileLoader.php index de112404618..1fec4d18bb1 100644 --- a/app/Translation/FileLoader.php +++ b/app/Translation/FileLoader.php @@ -8,16 +8,22 @@ class FileLoader extends BaseLoader { /** * Load the messages for the given locale. + * * Extends Laravel's translation FileLoader to look in multiple directories * so that we can load in translation overrides from the theme file if wanted. * + * Note: As of using Laravel 10, this may now be redundant since Laravel's + * file loader supports multiple paths. This needs further testing though + * to confirm if Laravel works how we expect, since we specifically need + * the theme folder to be able to partially override core lang files. + * * @param string $locale * @param string $group * @param string|null $namespace * * @return array */ - public function load($locale, $group, $namespace = null) + public function load($locale, $group, $namespace = null): array { if ($group === '*' && $namespace === '*') { return $this->loadJsonPaths($locale); @@ -25,8 +31,8 @@ public function load($locale, $group, $namespace = null) if (is_null($namespace) || $namespace === '*') { $themePath = theme_path('lang'); - $themeTranslations = $themePath ? $this->loadPath($themePath, $locale, $group) : []; - $originalTranslations = $this->loadPath($this->path, $locale, $group); + $themeTranslations = $themePath ? $this->loadPaths([$themePath], $locale, $group) : []; + $originalTranslations = $this->loadPaths($this->paths, $locale, $group); return array_merge($originalTranslations, $themeTranslations); } diff --git a/app/Translation/LocaleManager.php b/app/Translation/LocaleManager.php index ce97426b9b4..d23c2361004 100644 --- a/app/Translation/LocaleManager.php +++ b/app/Translation/LocaleManager.php @@ -21,6 +21,7 @@ class LocaleManager protected array $localeMap = [ 'ar' => 'ar', 'bg' => 'bg_BG', + 'bn' => 'bn_BD', 'bs' => 'bs_BA', 'ca' => 'ca', 'cs' => 'cs_CZ', @@ -41,12 +42,15 @@ class LocaleManager 'hr' => 'hr_HR', 'hu' => 'hu_HU', 'id' => 'id_ID', + 'is' => 'is_IS', 'it' => 'it_IT', 'ja' => 'ja', 'ka' => 'ka_GE', 'ko' => 'ko_KR', + 'ku' => 'ku_TR', 'lt' => 'lt_LT', 'lv' => 'lv_LV', + 'ne' => 'ne_NP', 'nb' => 'nb_NO', 'nl' => 'nl_NL', 'nn' => 'nn_NO', @@ -58,7 +62,9 @@ class LocaleManager 'sk' => 'sk_SK', 'sl' => 'sl_SI', 'sq' => 'sq_AL', + 'sr' => 'sr_RS', 'sv' => 'sv_SE', + 'tk' => 'tk_TM', 'tr' => 'tr_TR', 'uk' => 'uk_UA', 'uz' => 'uz_UZ', diff --git a/app/Uploads/Attachment.php b/app/Uploads/Attachment.php index 4fd6d4cdcb7..05227243a76 100644 --- a/app/Uploads/Attachment.php +++ b/app/Uploads/Attachment.php @@ -8,6 +8,7 @@ use BookStack\Permissions\Models\JointPermission; use BookStack\Permissions\PermissionApplicator; use BookStack\Users\Models\HasCreatorAndUpdater; +use BookStack\Users\Models\OwnableInterface; use BookStack\Users\Models\User; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Factories\HasFactory; @@ -27,7 +28,7 @@ * * @method static Entity|Builder visible() */ -class Attachment extends Model +class Attachment extends Model implements OwnableInterface { use HasCreatorAndUpdater; use HasFactory; @@ -77,7 +78,22 @@ public function getUrl($openInline = false): string } /** - * Generate a HTML link to this attachment. + * Get the representation of this attachment in a format suitable for the page editors. + * Detects and adapts video content to use an inline video embed. + */ + public function editorContent(): array + { + $videoExtensions = ['mp4', 'webm', 'mkv', 'ogg', 'avi']; + if (in_array(strtolower($this->extension), $videoExtensions)) { + $html = ''; + return ['text/html' => $html, 'text/plain' => $html]; + } + + return ['text/html' => $this->htmlLink(), 'text/plain' => $this->markdownLink()]; + } + + /** + * Generate the HTML link to this attachment. */ public function htmlLink(): string { @@ -85,7 +101,7 @@ public function htmlLink(): string } /** - * Generate a markdown link to this attachment. + * Generate a MarkDown link to this attachment. */ public function markdownLink(): string { diff --git a/app/Uploads/AttachmentService.php b/app/Uploads/AttachmentService.php index ddabec09f27..dabd537292f 100644 --- a/app/Uploads/AttachmentService.php +++ b/app/Uploads/AttachmentService.php @@ -4,75 +4,31 @@ use BookStack\Exceptions\FileUploadException; use Exception; -use Illuminate\Contracts\Filesystem\FileNotFoundException; -use Illuminate\Contracts\Filesystem\Filesystem as Storage; -use Illuminate\Filesystem\FilesystemManager; -use Illuminate\Support\Facades\Log; -use Illuminate\Support\Str; -use League\Flysystem\WhitespacePathNormalizer; use Symfony\Component\HttpFoundation\File\UploadedFile; class AttachmentService { - protected FilesystemManager $fileSystem; - - /** - * AttachmentService constructor. - */ - public function __construct(FilesystemManager $fileSystem) - { - $this->fileSystem = $fileSystem; - } - - /** - * Get the storage that will be used for storing files. - */ - protected function getStorageDisk(): Storage - { - return $this->fileSystem->disk($this->getStorageDiskName()); - } - - /** - * Get the name of the storage disk to use. - */ - protected function getStorageDiskName(): string - { - $storageType = config('filesystems.attachments'); - - // Change to our secure-attachment disk if any of the local options - // are used to prevent escaping that location. - if ($storageType === 'local' || $storageType === 'local_secure' || $storageType === 'local_secure_restricted') { - $storageType = 'local_secure_attachments'; - } - - return $storageType; + public function __construct( + protected FileStorage $storage, + ) { } /** - * Change the originally provided path to fit any disk-specific requirements. - * This also ensures the path is kept to the expected root folders. + * Stream an attachment from storage. + * + * @return resource|null */ - protected function adjustPathForStorageDisk(string $path): string + public function streamAttachmentFromStorage(Attachment $attachment) { - $path = (new WhitespacePathNormalizer())->normalizePath(str_replace('uploads/files/', '', $path)); - - if ($this->getStorageDiskName() === 'local_secure_attachments') { - return $path; - } - - return 'uploads/files/' . $path; + return $this->storage->getReadStream($attachment->path); } /** - * Stream an attachment from storage. - * - * @throws FileNotFoundException - * - * @return resource|null + * Read the file size of an attachment from storage, in bytes. */ - public function streamAttachmentFromStorage(Attachment $attachment) + public function getAttachmentFileSize(Attachment $attachment): int { - return $this->getStorageDisk()->readStream($this->adjustPathForStorageDisk($attachment->path)); + return $this->storage->getSize($attachment->path); } /** @@ -160,16 +116,18 @@ public function updateFileOrderWithinPage(array $attachmentOrder, string $pageId */ public function updateFile(Attachment $attachment, array $requestData): Attachment { - $attachment->name = $requestData['name']; - $link = trim($requestData['link'] ?? ''); + if (isset($requestData['name'])) { + $attachment->name = $requestData['name']; + } + $link = trim($requestData['link'] ?? ''); if (!empty($link)) { if (!$attachment->external) { $this->deleteFileInStorage($attachment); $attachment->external = true; $attachment->extension = ''; } - $attachment->path = $requestData['link']; + $attachment->path = $link; } $attachment->save(); @@ -195,15 +153,9 @@ public function deleteFile(Attachment $attachment) * Delete a file from the filesystem it sits on. * Cleans any empty leftover folders. */ - protected function deleteFileInStorage(Attachment $attachment) + public function deleteFileInStorage(Attachment $attachment): void { - $storage = $this->getStorageDisk(); - $dirPath = $this->adjustPathForStorageDisk(dirname($attachment->path)); - - $storage->delete($this->adjustPathForStorageDisk($attachment->path)); - if (count($storage->allFiles($dirPath)) === 0) { - $storage->deleteDirectory($dirPath); - } + $this->storage->delete($attachment->path); } /** @@ -213,32 +165,20 @@ protected function deleteFileInStorage(Attachment $attachment) */ protected function putFileInStorage(UploadedFile $uploadedFile): string { - $storage = $this->getStorageDisk(); $basePath = 'uploads/files/' . date('Y-m-M') . '/'; - $uploadFileName = Str::random(16) . '-' . $uploadedFile->getClientOriginalExtension(); - while ($storage->exists($this->adjustPathForStorageDisk($basePath . $uploadFileName))) { - $uploadFileName = Str::random(3) . $uploadFileName; - } - - $attachmentStream = fopen($uploadedFile->getRealPath(), 'r'); - $attachmentPath = $basePath . $uploadFileName; - - try { - $storage->writeStream($this->adjustPathForStorageDisk($attachmentPath), $attachmentStream); - } catch (Exception $e) { - Log::error('Error when attempting file upload:' . $e->getMessage()); - - throw new FileUploadException(trans('errors.path_not_writable', ['filePath' => $attachmentPath])); - } - - return $attachmentPath; + return $this->storage->uploadFile( + $uploadedFile, + $basePath, + $uploadedFile->getClientOriginalExtension(), + '' + ); } /** * Get the file validation rules for attachments. */ - public function getFileValidationRules(): array + public static function getFileValidationRules(): array { return ['file', 'max:' . (config('app.upload_limit') * 1000)]; } diff --git a/app/Uploads/Controllers/AttachmentApiController.php b/app/Uploads/Controllers/AttachmentApiController.php index 2e6d1620575..b47d6ff8dd1 100644 --- a/app/Uploads/Controllers/AttachmentApiController.php +++ b/app/Uploads/Controllers/AttachmentApiController.php @@ -2,9 +2,10 @@ namespace BookStack\Uploads\Controllers; -use BookStack\Entities\Models\Page; +use BookStack\Entities\Queries\PageQueries; use BookStack\Exceptions\FileUploadException; use BookStack\Http\ApiController; +use BookStack\Permissions\Permission; use BookStack\Uploads\Attachment; use BookStack\Uploads\AttachmentService; use Exception; @@ -15,7 +16,8 @@ class AttachmentApiController extends ApiController { public function __construct( - protected AttachmentService $attachmentService + protected AttachmentService $attachmentService, + protected PageQueries $pageQueries, ) { } @@ -44,12 +46,12 @@ public function list() */ public function create(Request $request) { - $this->checkPermission('attachment-create-all'); + $this->checkPermission(Permission::AttachmentCreateAll); $requestData = $this->validate($request, $this->rules()['create']); $pageId = $request->get('uploaded_to'); - $page = Page::visible()->findOrFail($pageId); - $this->checkOwnablePermission('page-update', $page); + $page = $this->pageQueries->findVisibleByIdOrFail($pageId); + $this->checkOwnablePermission(Permission::PageUpdate, $page); if ($request->hasFile('file')) { $uploadedFile = $request->file('file'); @@ -132,13 +134,13 @@ public function update(Request $request, string $id) $page = $attachment->page; if ($requestData['uploaded_to'] ?? false) { $pageId = $request->get('uploaded_to'); - $page = Page::visible()->findOrFail($pageId); + $page = $this->pageQueries->findVisibleByIdOrFail($pageId); $attachment->uploaded_to = $requestData['uploaded_to']; } - $this->checkOwnablePermission('page-view', $page); - $this->checkOwnablePermission('page-update', $page); - $this->checkOwnablePermission('attachment-update', $attachment); + $this->checkOwnablePermission(Permission::PageView, $page); + $this->checkOwnablePermission(Permission::PageUpdate, $page); + $this->checkOwnablePermission(Permission::AttachmentUpdate, $attachment); if ($request->hasFile('file')) { $uploadedFile = $request->file('file'); @@ -159,7 +161,7 @@ public function delete(string $id) { /** @var Attachment $attachment */ $attachment = Attachment::visible()->findOrFail($id); - $this->checkOwnablePermission('attachment-delete', $attachment); + $this->checkOwnablePermission(Permission::AttachmentDelete, $attachment); $this->attachmentService->deleteFile($attachment); @@ -170,16 +172,16 @@ protected function rules(): array { return [ 'create' => [ - 'name' => ['required', 'min:1', 'max:255', 'string'], + 'name' => ['required', 'string', 'min:1', 'max:255'], 'uploaded_to' => ['required', 'integer', 'exists:pages,id'], 'file' => array_merge(['required_without:link'], $this->attachmentService->getFileValidationRules()), - 'link' => ['required_without:file', 'min:1', 'max:2000', 'safe_url'], + 'link' => ['required_without:file', 'string', 'min:1', 'max:2000', 'safe_url'], ], 'update' => [ - 'name' => ['min:1', 'max:255', 'string'], + 'name' => ['string', 'min:1', 'max:255'], 'uploaded_to' => ['integer', 'exists:pages,id'], 'file' => $this->attachmentService->getFileValidationRules(), - 'link' => ['min:1', 'max:2000', 'safe_url'], + 'link' => ['string', 'min:1', 'max:2000', 'safe_url'], ], ]; } diff --git a/app/Uploads/Controllers/AttachmentController.php b/app/Uploads/Controllers/AttachmentController.php index 92f23465d22..0886193e46d 100644 --- a/app/Uploads/Controllers/AttachmentController.php +++ b/app/Uploads/Controllers/AttachmentController.php @@ -2,10 +2,12 @@ namespace BookStack\Uploads\Controllers; +use BookStack\Entities\Queries\PageQueries; use BookStack\Entities\Repos\PageRepo; use BookStack\Exceptions\FileUploadException; use BookStack\Exceptions\NotFoundException; use BookStack\Http\Controller; +use BookStack\Permissions\Permission; use BookStack\Uploads\Attachment; use BookStack\Uploads\AttachmentService; use Exception; @@ -18,6 +20,7 @@ class AttachmentController extends Controller { public function __construct( protected AttachmentService $attachmentService, + protected PageQueries $pageQueries, protected PageRepo $pageRepo ) { } @@ -36,10 +39,10 @@ public function upload(Request $request) ]); $pageId = $request->get('uploaded_to'); - $page = $this->pageRepo->getById($pageId); + $page = $this->pageQueries->findVisibleByIdOrFail($pageId); - $this->checkPermission('attachment-create-all'); - $this->checkOwnablePermission('page-update', $page); + $this->checkPermission(Permission::AttachmentCreateAll); + $this->checkOwnablePermission(Permission::PageUpdate, $page); $uploadedFile = $request->file('file'); @@ -65,9 +68,9 @@ public function uploadUpdate(Request $request, $attachmentId) /** @var Attachment $attachment */ $attachment = Attachment::query()->findOrFail($attachmentId); - $this->checkOwnablePermission('view', $attachment->page); - $this->checkOwnablePermission('page-update', $attachment->page); - $this->checkOwnablePermission('attachment-create', $attachment); + $this->checkOwnablePermission(Permission::PageView, $attachment->page); + $this->checkOwnablePermission(Permission::PageUpdate, $attachment->page); + $this->checkOwnablePermission(Permission::AttachmentUpdate, $attachment); $uploadedFile = $request->file('file'); @@ -88,8 +91,8 @@ public function getUpdateForm(string $attachmentId) /** @var Attachment $attachment */ $attachment = Attachment::query()->findOrFail($attachmentId); - $this->checkOwnablePermission('page-update', $attachment->page); - $this->checkOwnablePermission('attachment-create', $attachment); + $this->checkOwnablePermission(Permission::PageUpdate, $attachment->page); + $this->checkOwnablePermission(Permission::AttachmentCreate, $attachment); return view('attachments.manager-edit-form', [ 'attachment' => $attachment, @@ -116,9 +119,9 @@ public function update(Request $request, string $attachmentId) ]), 422); } - $this->checkOwnablePermission('page-view', $attachment->page); - $this->checkOwnablePermission('page-update', $attachment->page); - $this->checkOwnablePermission('attachment-update', $attachment); + $this->checkOwnablePermission(Permission::PageView, $attachment->page); + $this->checkOwnablePermission(Permission::PageUpdate, $attachment->page); + $this->checkOwnablePermission(Permission::AttachmentUpdate, $attachment); $attachment = $this->attachmentService->updateFile($attachment, [ 'name' => $request->get('attachment_edit_name'), @@ -152,10 +155,10 @@ public function attachLink(Request $request) ]), 422); } - $page = $this->pageRepo->getById($pageId); + $page = $this->pageQueries->findVisibleByIdOrFail($pageId); - $this->checkPermission('attachment-create-all'); - $this->checkOwnablePermission('page-update', $page); + $this->checkPermission(Permission::AttachmentCreateAll); + $this->checkOwnablePermission(Permission::PageUpdate, $page); $attachmentName = $request->get('attachment_link_name'); $link = $request->get('attachment_link_url'); @@ -173,8 +176,7 @@ public function attachLink(Request $request) */ public function listForPage(int $pageId) { - $page = $this->pageRepo->getById($pageId); - $this->checkOwnablePermission('page-view', $page); + $page = $this->pageQueries->findVisibleByIdOrFail($pageId); return view('attachments.manager-list', [ 'attachments' => $page->attachments->all(), @@ -192,8 +194,8 @@ public function sortForPage(Request $request, int $pageId) $this->validate($request, [ 'order' => ['required', 'array'], ]); - $page = $this->pageRepo->getById($pageId); - $this->checkOwnablePermission('page-update', $page); + $page = $this->pageQueries->findVisibleByIdOrFail($pageId); + $this->checkOwnablePermission(Permission::PageUpdate, $page); $attachmentOrder = $request->get('order'); $this->attachmentService->updateFileOrderWithinPage($attachmentOrder, $pageId); @@ -213,12 +215,12 @@ public function get(Request $request, string $attachmentId) $attachment = Attachment::query()->findOrFail($attachmentId); try { - $page = $this->pageRepo->getById($attachment->uploaded_to); + $page = $this->pageQueries->findVisibleByIdOrFail($attachment->uploaded_to); } catch (NotFoundException $exception) { throw new NotFoundException(trans('errors.attachment_not_found')); } - $this->checkOwnablePermission('page-view', $page); + $this->checkOwnablePermission(Permission::PageView, $page); if ($attachment->external) { return redirect($attachment->path); @@ -226,12 +228,13 @@ public function get(Request $request, string $attachmentId) $fileName = $attachment->getFileName(); $attachmentStream = $this->attachmentService->streamAttachmentFromStorage($attachment); + $attachmentSize = $this->attachmentService->getAttachmentFileSize($attachment); if ($request->get('open') === 'true') { - return $this->download()->streamedInline($attachmentStream, $fileName); + return $this->download()->streamedInline($attachmentStream, $fileName, $attachmentSize); } - return $this->download()->streamedDirectly($attachmentStream, $fileName); + return $this->download()->streamedDirectly($attachmentStream, $fileName, $attachmentSize); } /** @@ -243,7 +246,7 @@ public function delete(string $attachmentId) { /** @var Attachment $attachment */ $attachment = Attachment::query()->findOrFail($attachmentId); - $this->checkOwnablePermission('attachment-delete', $attachment); + $this->checkOwnablePermission(Permission::AttachmentDelete, $attachment); $this->attachmentService->deleteFile($attachment); return response()->json(['message' => trans('entities.attachments_deleted')]); diff --git a/app/Uploads/Controllers/DrawioImageController.php b/app/Uploads/Controllers/DrawioImageController.php index 6293da4f718..f44acd997d2 100644 --- a/app/Uploads/Controllers/DrawioImageController.php +++ b/app/Uploads/Controllers/DrawioImageController.php @@ -4,6 +4,7 @@ use BookStack\Exceptions\ImageUploadException; use BookStack\Http\Controller; +use BookStack\Permissions\Permission; use BookStack\Uploads\ImageRepo; use BookStack\Uploads\ImageResizer; use BookStack\Util\OutOfMemoryHandler; @@ -57,7 +58,7 @@ public function create(Request $request) 'uploaded_to' => ['required', 'integer'], ]); - $this->checkPermission('image-create-all'); + $this->checkPermission(Permission::ImageCreateAll); $imageBase64Data = $request->get('image'); try { @@ -81,7 +82,7 @@ public function getAsBase64($id) return $this->jsonError(trans('errors.drawing_data_not_found'), 404); } - if ($image->type !== 'drawio' || !userCan('page-view', $image->getPage())) { + if ($image->type !== 'drawio' || !userCan(Permission::PageView, $image->getPage())) { return $this->jsonError(trans('errors.drawing_data_not_found'), 404); } diff --git a/app/Uploads/Controllers/GalleryImageController.php b/app/Uploads/Controllers/GalleryImageController.php index 258f2bef6bd..745efcde812 100644 --- a/app/Uploads/Controllers/GalleryImageController.php +++ b/app/Uploads/Controllers/GalleryImageController.php @@ -4,12 +4,11 @@ use BookStack\Exceptions\ImageUploadException; use BookStack\Http\Controller; +use BookStack\Permissions\Permission; use BookStack\Uploads\ImageRepo; use BookStack\Uploads\ImageResizer; use BookStack\Util\OutOfMemoryHandler; use Illuminate\Http\Request; -use Illuminate\Support\Facades\App; -use Illuminate\Support\Facades\Log; use Illuminate\Validation\ValidationException; class GalleryImageController extends Controller @@ -54,7 +53,7 @@ public function list(Request $request, ImageResizer $resizer) */ public function create(Request $request) { - $this->checkPermission('image-create-all'); + $this->checkPermission(Permission::ImageCreateAll); try { $this->validate($request, [ diff --git a/app/Uploads/Controllers/ImageController.php b/app/Uploads/Controllers/ImageController.php index c68ffdf6bd0..da67639c16d 100644 --- a/app/Uploads/Controllers/ImageController.php +++ b/app/Uploads/Controllers/ImageController.php @@ -6,6 +6,7 @@ use BookStack\Exceptions\NotFoundException; use BookStack\Exceptions\NotifyException; use BookStack\Http\Controller; +use BookStack\Permissions\Permission; use BookStack\Uploads\Image; use BookStack\Uploads\ImageRepo; use BookStack\Uploads\ImageResizer; @@ -50,7 +51,7 @@ public function update(Request $request, string $id) $image = $this->imageRepo->getById($id); $this->checkImagePermission($image); - $this->checkOwnablePermission('image-update', $image); + $this->checkOwnablePermission(Permission::ImageUpdate, $image); $image = $this->imageRepo->updateImageDetails($image, $data); @@ -71,7 +72,7 @@ public function updateFile(Request $request, string $id) $image = $this->imageRepo->getById($id); $this->checkImagePermission($image); - $this->checkOwnablePermission('image-update', $image); + $this->checkOwnablePermission(Permission::ImageUpdate, $image); $file = $request->file('file'); new OutOfMemoryHandler(function () { @@ -125,7 +126,7 @@ public function edit(Request $request, string $id) public function destroy(string $id) { $image = $this->imageRepo->getById($id); - $this->checkOwnablePermission('image-delete', $image); + $this->checkOwnablePermission(Permission::ImageDelete, $image); $this->checkImagePermission($image); $this->imageRepo->destroyImage($image); @@ -140,7 +141,7 @@ public function rebuildThumbnails(string $id) { $image = $this->imageRepo->getById($id); $this->checkImagePermission($image); - $this->checkOwnablePermission('image-update', $image); + $this->checkOwnablePermission(Permission::ImageUpdate, $image); new OutOfMemoryHandler(function () { return $this->jsonError(trans('errors.image_thumbnail_memory_limit')); @@ -163,7 +164,7 @@ protected function checkImagePermission(Image $image): void $relatedPage = $image->getPage(); if ($relatedPage) { - $this->checkOwnablePermission('page-view', $relatedPage); + $this->checkOwnablePermission(Permission::PageView, $relatedPage); } } } diff --git a/app/Uploads/Controllers/ImageGalleryApiController.php b/app/Uploads/Controllers/ImageGalleryApiController.php index ec96e4593bc..59696dc9ae4 100644 --- a/app/Uploads/Controllers/ImageGalleryApiController.php +++ b/app/Uploads/Controllers/ImageGalleryApiController.php @@ -2,8 +2,9 @@ namespace BookStack\Uploads\Controllers; -use BookStack\Entities\Models\Page; +use BookStack\Entities\Queries\PageQueries; use BookStack\Http\ApiController; +use BookStack\Permissions\Permission; use BookStack\Uploads\Image; use BookStack\Uploads\ImageRepo; use BookStack\Uploads\ImageResizer; @@ -18,6 +19,7 @@ class ImageGalleryApiController extends ApiController public function __construct( protected ImageRepo $imageRepo, protected ImageResizer $imageResizer, + protected PageQueries $pageQueries, ) { } @@ -64,11 +66,11 @@ public function list() */ public function create(Request $request) { - $this->checkPermission('image-create-all'); + $this->checkPermission(Permission::ImageCreateAll); $data = $this->validate($request, $this->rules()['create']); - Page::visible()->findOrFail($data['uploaded_to']); + $page = $this->pageQueries->findVisibleByIdOrFail($data['uploaded_to']); - $image = $this->imageRepo->saveNew($data['image'], $data['type'], $data['uploaded_to']); + $image = $this->imageRepo->saveNew($data['image'], $data['type'], $page->id); if (isset($data['name'])) { $image->refresh(); @@ -101,8 +103,8 @@ public function update(Request $request, string $id) { $data = $this->validate($request, $this->rules()['update']); $image = $this->imageRepo->getById($id); - $this->checkOwnablePermission('page-view', $image->getPage()); - $this->checkOwnablePermission('image-update', $image); + $this->checkOwnablePermission(Permission::PageView, $image->getPage()); + $this->checkOwnablePermission(Permission::ImageUpdate, $image); $this->imageRepo->updateImageDetails($image, $data); if (isset($data['image'])) { @@ -120,8 +122,8 @@ public function update(Request $request, string $id) public function delete(string $id) { $image = $this->imageRepo->getById($id); - $this->checkOwnablePermission('page-view', $image->getPage()); - $this->checkOwnablePermission('image-delete', $image); + $this->checkOwnablePermission(Permission::PageView, $image->getPage()); + $this->checkOwnablePermission(Permission::ImageDelete, $image); $this->imageRepo->destroyImage($image); return response('', 204); @@ -145,10 +147,10 @@ protected function formatForSingleResponse(Image $image): array $data['content']['html'] = "
id}\">
"; $data['content']['markdown'] = $data['content']['html']; } else { - $escapedDisplayThumb = htmlentities($image->thumbs['display']); + $escapedDisplayThumb = htmlentities($image->getAttribute('thumbs')['display']); $data['content']['html'] = "\"{$escapedName}\""; $mdEscapedName = str_replace(']', '', str_replace('[', '', $image->name)); - $mdEscapedThumb = str_replace(']', '', str_replace('[', '', $image->thumbs['display'])); + $mdEscapedThumb = str_replace(']', '', str_replace('[', '', $image->getAttribute('thumbs')['display'])); $data['content']['markdown'] = "![{$mdEscapedName}]({$mdEscapedThumb})"; } diff --git a/app/Uploads/FileStorage.php b/app/Uploads/FileStorage.php new file mode 100644 index 00000000000..70040725a3d --- /dev/null +++ b/app/Uploads/FileStorage.php @@ -0,0 +1,132 @@ +getStorageDisk()->readStream($this->adjustPathForStorageDisk($path)); + } + + public function getSize(string $path): int + { + return $this->getStorageDisk()->size($this->adjustPathForStorageDisk($path)); + } + + public function delete(string $path, bool $removeEmptyDir = false): void + { + $storage = $this->getStorageDisk(); + $adjustedPath = $this->adjustPathForStorageDisk($path); + $dir = dirname($adjustedPath); + + $storage->delete($adjustedPath); + if ($removeEmptyDir && count($storage->allFiles($dir)) === 0) { + $storage->deleteDirectory($dir); + } + } + + /** + * @throws FileUploadException + */ + public function uploadFile(UploadedFile $file, string $subDirectory, string $suffix, string $extension): string + { + $storage = $this->getStorageDisk(); + $basePath = trim($subDirectory, '/') . '/'; + + $uploadFileName = Str::random(16) . ($suffix ? "-{$suffix}" : '') . ($extension ? ".{$extension}" : ''); + while ($storage->exists($this->adjustPathForStorageDisk($basePath . $uploadFileName))) { + $uploadFileName = Str::random(3) . $uploadFileName; + } + + $fileStream = fopen($file->getRealPath(), 'r'); + $filePath = $basePath . $uploadFileName; + + try { + $storage->writeStream($this->adjustPathForStorageDisk($filePath), $fileStream); + } catch (Exception $e) { + Log::error('Error when attempting file upload:' . $e->getMessage()); + + throw new FileUploadException(trans('errors.path_not_writable', ['filePath' => $filePath])); + } + + return $filePath; + } + + /** + * Check whether the configured storage is remote from the host of this app. + */ + public function isRemote(): bool + { + return $this->getStorageDiskName() === 's3'; + } + + /** + * Get the actual path on system for the given relative file path. + */ + public function getSystemPath(string $filePath): string + { + if ($this->isRemote()) { + return ''; + } + + return storage_path('uploads/files/' . ltrim($this->adjustPathForStorageDisk($filePath), '/')); + } + + /** + * Get the storage that will be used for storing files. + */ + protected function getStorageDisk(): Storage + { + return $this->fileSystem->disk($this->getStorageDiskName()); + } + + /** + * Get the name of the storage disk to use. + */ + protected function getStorageDiskName(): string + { + $storageType = trim(strtolower(config('filesystems.attachments'))); + + // Change to our secure-attachment disk if any of the local options + // are used to prevent escaping that location. + if ($storageType === 'local' || $storageType === 'local_secure' || $storageType === 'local_secure_restricted') { + $storageType = 'local_secure_attachments'; + } + + return $storageType; + } + + /** + * Change the originally provided path to fit any disk-specific requirements. + * This also ensures the path is kept to the expected root folders. + */ + protected function adjustPathForStorageDisk(string $path): string + { + $trimmed = str_replace('uploads/files/', '', $path); + $normalized = FilePathNormalizer::normalize($trimmed); + + if ($this->getStorageDiskName() === 'local_secure_attachments') { + return $normalized; + } + + return 'uploads/files/' . $normalized; + } +} diff --git a/app/Uploads/Image.php b/app/Uploads/Image.php index 0a267a64465..ec8f8be0fb1 100644 --- a/app/Uploads/Image.php +++ b/app/Uploads/Image.php @@ -7,6 +7,7 @@ use BookStack\Permissions\Models\JointPermission; use BookStack\Permissions\PermissionApplicator; use BookStack\Users\Models\HasCreatorAndUpdater; +use BookStack\Users\Models\OwnableInterface; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\HasMany; @@ -21,7 +22,7 @@ * @property int $created_by * @property int $updated_by */ -class Image extends Model +class Image extends Model implements OwnableInterface { use HasFactory; use HasCreatorAndUpdater; diff --git a/app/Uploads/ImageRepo.php b/app/Uploads/ImageRepo.php index 0e312d88327..a16b87bd75b 100644 --- a/app/Uploads/ImageRepo.php +++ b/app/Uploads/ImageRepo.php @@ -2,7 +2,7 @@ namespace BookStack\Uploads; -use BookStack\Entities\Models\Page; +use BookStack\Entities\Queries\PageQueries; use BookStack\Exceptions\ImageUploadException; use BookStack\Permissions\PermissionApplicator; use Exception; @@ -15,6 +15,7 @@ public function __construct( protected ImageService $imageService, protected PermissionApplicator $permissions, protected ImageResizer $imageResizer, + protected PageQueries $pageQueries, ) { } @@ -48,9 +49,9 @@ public function getPaginatedByType( string $type, int $page = 0, int $pageSize = 24, - int $uploadedTo = null, - string $search = null, - callable $whereClause = null + ?int $uploadedTo = null, + ?string $search = null, + ?callable $whereClause = null ): array { $imageQuery = Image::query()->where('type', '=', strtolower($type)); @@ -77,21 +78,20 @@ public function getPaginatedByType( */ public function getEntityFiltered( string $type, - string $filterType = null, - int $page = 0, - int $pageSize = 24, - int $uploadedTo = null, - string $search = null + ?string $filterType, + int $page, + int $pageSize, + int $uploadedTo, + ?string $search ): array { - /** @var Page $contextPage */ - $contextPage = Page::visible()->findOrFail($uploadedTo); + $contextPage = $this->pageQueries->findVisibleByIdOrFail($uploadedTo); $parentFilter = null; if ($filterType === 'book' || $filterType === 'page') { $parentFilter = function (Builder $query) use ($filterType, $contextPage) { if ($filterType === 'page') { $query->where('uploaded_to', '=', $contextPage->id); - } elseif ($filterType === 'book') { + } else if ($filterType === 'book') { $validPageIds = $contextPage->book->pages() ->scopes('visible') ->pluck('id') @@ -109,8 +109,14 @@ public function getEntityFiltered( * * @throws ImageUploadException */ - public function saveNew(UploadedFile $uploadFile, string $type, int $uploadedTo = 0, int $resizeWidth = null, int $resizeHeight = null, bool $keepRatio = true): Image - { + public function saveNew( + UploadedFile $uploadFile, + string $type, + int $uploadedTo = 0, + ?int $resizeWidth = null, + ?int $resizeHeight = null, + bool $keepRatio = true + ): Image { $image = $this->imageService->saveNewFromUpload($uploadFile, $type, $uploadedTo, $resizeWidth, $resizeHeight, $keepRatio); if ($type !== 'system') { @@ -166,7 +172,7 @@ public function updateImageDetails(Image $image, $updateDetails): Image */ public function updateImageFile(Image $image, UploadedFile $file): void { - if ($file->getClientOriginalExtension() !== pathinfo($image->path, PATHINFO_EXTENSION)) { + if (strtolower($file->getClientOriginalExtension()) !== strtolower(pathinfo($image->path, PATHINFO_EXTENSION))) { throw new ImageUploadException(trans('errors.image_upload_replace_type')); } @@ -184,7 +190,7 @@ public function updateImageFile(Image $image, UploadedFile $file): void * * @throws Exception */ - public function destroyImage(Image $image = null): void + public function destroyImage(?Image $image = null): void { if ($image) { $this->imageService->destroy($image); @@ -225,9 +231,9 @@ public function getImageData(Image $image): ?string */ public function getPagesUsingImage(Image $image): array { - $pages = Page::visible() + $pages = $this->pageQueries->visibleForList() ->where('html', 'like', '%' . $image->url . '%') - ->get(['id', 'name', 'slug', 'book_id']); + ->get(); foreach ($pages as $page) { $page->setAttribute('url', $page->getUrl()); diff --git a/app/Uploads/ImageResizer.php b/app/Uploads/ImageResizer.php index 4dc1b0b9922..8d7571c82d6 100644 --- a/app/Uploads/ImageResizer.php +++ b/app/Uploads/ImageResizer.php @@ -6,8 +6,15 @@ use Exception; use GuzzleHttp\Psr7\Utils; use Illuminate\Support\Facades\Cache; -use Intervention\Image\Gd\Driver; -use Intervention\Image\Image as InterventionImage; +use Illuminate\Support\Facades\Log; +use Intervention\Image\Decoders\BinaryImageDecoder; +use Intervention\Image\Drivers\Gd\Decoders\NativeObjectDecoder; +use Intervention\Image\Drivers\Gd\Driver; +use Intervention\Image\Encoders\AutoEncoder; +use Intervention\Image\Encoders\PngEncoder; +use Intervention\Image\Interfaces\ImageInterface as InterventionImage; +use Intervention\Image\ImageManager; +use Intervention\Image\Origin; class ImageResizer { @@ -87,15 +94,15 @@ public function resizeToThumbnailUrl( $imageData = $disk->get($imagePath); - // Do not resize apng images where we're not cropping - if ($keepRatio && $this->isApngData($image, $imageData)) { + // Do not resize animated images where we're not cropping + if ($keepRatio && $this->isAnimated($image, $imageData)) { Cache::put($thumbCacheKey, $image->path, static::THUMBNAIL_CACHE_TIME); return $this->storage->getPublicUrl($image->path); } // If not in cache and thumbnail does not exist, generate thumb and cache path - $thumbData = $this->resizeImageData($imageData, $width, $height, $keepRatio); + $thumbData = $this->resizeImageData($imageData, $width, $height, $keepRatio, $this->getExtension($image)); $disk->put($thumbFilePath, $thumbData, true); Cache::put($thumbCacheKey, $thumbFilePath, static::THUMBNAIL_CACHE_TIME); @@ -116,7 +123,7 @@ public function resizeImageData( ?string $format = null, ): string { try { - $thumb = $this->interventionFromImageData($imageData); + $thumb = $this->interventionFromImageData($imageData, $format); } catch (Exception $e) { throw new ImageUploadException(trans('errors.cannot_create_thumbs')); } @@ -124,15 +131,17 @@ public function resizeImageData( $this->orientImageToOriginalExif($thumb, $imageData); if ($keepRatio) { - $thumb->resize($width, $height, function ($constraint) { - $constraint->aspectRatio(); - $constraint->upsize(); - }); + $thumb->scaleDown($width, $height); } else { - $thumb->fit($width, $height); + $thumb->cover($width, $height); } - $thumbData = (string) $thumb->encode($format); + $encoder = match ($format) { + 'png' => new PngEncoder(), + default => new AutoEncoder(), + }; + + $thumbData = (string) $thumb->encode($encoder); // Use original image data if we're keeping the ratio // and the resizing does not save any space. @@ -148,10 +157,26 @@ public function resizeImageData( * Performs some manual library usage to ensure image is specifically loaded * from given binary data instead of data being misinterpreted. */ - protected function interventionFromImageData(string $imageData): InterventionImage + protected function interventionFromImageData(string $imageData, ?string $fileType): InterventionImage { - $driver = new Driver(); - return $driver->decoder->initFromBinary($imageData); + $manager = new ImageManager( + new Driver(), + autoOrientation: false, + ); + + // Ensure gif images are decoded natively instead of deferring to intervention GIF + // handling since we don't need the added animation support. + $isGif = $fileType === 'gif'; + $decoder = $isGif ? NativeObjectDecoder::class : BinaryImageDecoder::class; + $input = $isGif ? @imagecreatefromstring($imageData) : $imageData; + + $image = $manager->read($input, $decoder); + + if ($isGif) { + $image->setOrigin(new Origin('image/gif')); + } + + return $image; } /** @@ -202,21 +227,64 @@ protected function orientImageToOriginalExif(InterventionImage $image, string $o */ protected function isGif(Image $image): bool { - return strtolower(pathinfo($image->path, PATHINFO_EXTENSION)) === 'gif'; + return $this->getExtension($image) === 'gif'; + } + + /** + * Get the extension for the given image, normalised to lower-case. + */ + protected function getExtension(Image $image): string + { + return strtolower(pathinfo($image->path, PATHINFO_EXTENSION)); } /** * Check if the given image and image data is apng. */ - protected function isApngData(Image $image, string &$imageData): bool + protected function isApngData(string &$imageData): bool + { + $initialHeader = substr($imageData, 0, strpos($imageData, 'IDAT')); + + return str_contains($initialHeader, 'acTL'); + } + + /** + * Check if the given avif image data represents an animated image. + * This is based up the answer here: https://stackoverflow.com/a/79457313 + */ + protected function isAnimatedAvifData(string &$imageData): bool { - $isPng = strtolower(pathinfo($image->path, PATHINFO_EXTENSION)) === 'png'; - if (!$isPng) { + $stszPos = strpos($imageData, 'stsz'); + if ($stszPos === false) { return false; } - $initialHeader = substr($imageData, 0, strpos($imageData, 'IDAT')); + // Look 12 bytes after the start of 'stsz' + $start = $stszPos + 12; + $end = $start + 4; + if ($end > strlen($imageData) - 1) { + return false; + } - return str_contains($initialHeader, 'acTL'); + $data = substr($imageData, $start, 4); + $count = unpack('Nvalue', $data)['value']; + return $count > 1; + } + + /** + * Check if the given image is animated. + */ + protected function isAnimated(Image $image, string &$imageData): bool + { + $extension = strtolower(pathinfo($image->path, PATHINFO_EXTENSION)); + if ($extension === 'png') { + return $this->isApngData($imageData); + } + + if ($extension === 'avif') { + return $this->isAnimatedAvifData($imageData); + } + + return false; } } diff --git a/app/Uploads/ImageService.php b/app/Uploads/ImageService.php index 1655a4cc3aa..458f0102d77 100644 --- a/app/Uploads/ImageService.php +++ b/app/Uploads/ImageService.php @@ -2,9 +2,7 @@ namespace BookStack\Uploads; -use BookStack\Entities\Models\Book; -use BookStack\Entities\Models\Bookshelf; -use BookStack\Entities\Models\Page; +use BookStack\Entities\Queries\EntityQueries; use BookStack\Exceptions\ImageUploadException; use Exception; use Illuminate\Support\Facades\DB; @@ -15,11 +13,12 @@ class ImageService { - protected static array $supportedExtensions = ['jpg', 'jpeg', 'png', 'gif', 'webp']; + protected static array $supportedExtensions = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'avif']; public function __construct( protected ImageStorage $storage, protected ImageResizer $resizer, + protected EntityQueries $queries, ) { } @@ -32,11 +31,12 @@ public function saveNewFromUpload( UploadedFile $uploadedFile, string $type, int $uploadedTo = 0, - int $resizeWidth = null, - int $resizeHeight = null, - bool $keepRatio = true + ?int $resizeWidth = null, + ?int $resizeHeight = null, + bool $keepRatio = true, + string $imageName = '', ): Image { - $imageName = $uploadedFile->getClientOriginalName(); + $imageName = $imageName ?: $uploadedFile->getClientOriginalName(); $imageData = file_get_contents($uploadedFile->getRealPath()); if ($resizeWidth !== null || $resizeHeight !== null) { @@ -134,6 +134,19 @@ public function getImageData(Image $image): string return $disk->get($image->path); } + /** + * Get the raw data content from an image. + * + * @throws Exception + * @return ?resource + */ + public function getImageStream(Image $image): mixed + { + $disk = $this->storage->getDisk(); + + return $disk->stream($image->path); + } + /** * Destroy an image along with its revisions, thumbnails and remaining folders. * @@ -141,11 +154,19 @@ public function getImageData(Image $image): string */ public function destroy(Image $image): void { - $disk = $this->storage->getDisk($image->type); - $disk->destroyAllMatchingNameFromPath($image->path); + $this->destroyFileAtPath($image->type, $image->path); $image->delete(); } + /** + * Destroy the underlying image file at the given path. + */ + public function destroyFileAtPath(string $type, string $path): void + { + $disk = $this->storage->getDisk($type); + $disk->destroyAllMatchingNameFromPath($path); + } + /** * Delete gallery and drawings that are not within HTML content of pages or page revisions. * Checks based off of only the image name. @@ -278,15 +299,15 @@ protected function checkUserHasAccessToRelationOfImageAtPath(string $path): bool } if ($imageType === 'gallery' || $imageType === 'drawio') { - return Page::visible()->where('id', '=', $image->uploaded_to)->exists(); + return $this->queries->pages->visibleForList()->where('id', '=', $image->uploaded_to)->exists(); } if ($imageType === 'cover_book') { - return Book::visible()->where('id', '=', $image->uploaded_to)->exists(); + return $this->queries->books->visibleForList()->where('id', '=', $image->uploaded_to)->exists(); } if ($imageType === 'cover_bookshelf') { - return Bookshelf::visible()->where('id', '=', $image->uploaded_to)->exists(); + return $this->queries->shelves->visibleForList()->where('id', '=', $image->uploaded_to)->exists(); } return false; diff --git a/app/Uploads/ImageStorage.php b/app/Uploads/ImageStorage.php index dc4abc0f281..ddaa26a9400 100644 --- a/app/Uploads/ImageStorage.php +++ b/app/Uploads/ImageStorage.php @@ -110,10 +110,20 @@ public function urlToPath(string $url): ?string } /** - * Gets a public facing url for an image by checking relevant environment variables. + * Gets a public facing url for an image or location at the given path. + */ + public static function getPublicUrl(string $filePath): string + { + return static::getPublicBaseUrl() . '/' . ltrim($filePath, '/'); + } + + /** + * Get the public base URL used for images. + * Will not include any path element of the image file, just the base part + * from where the path is then expected to start from. * If s3-style store is in use it will default to guessing a public bucket URL. */ - public function getPublicUrl(string $filePath): string + protected static function getPublicBaseUrl(): string { $storageUrl = config('filesystems.url'); @@ -131,6 +141,6 @@ public function getPublicUrl(string $filePath): string $basePath = $storageUrl ?: url('/'); - return rtrim($basePath, '/') . $filePath; + return rtrim($basePath, '/'); } } diff --git a/app/Uploads/ImageStorageDisk.php b/app/Uploads/ImageStorageDisk.php index 798b72abdbf..36d6721de6c 100644 --- a/app/Uploads/ImageStorageDisk.php +++ b/app/Uploads/ImageStorageDisk.php @@ -2,9 +2,12 @@ namespace BookStack\Uploads; +use BookStack\Util\FilePathNormalizer; use Illuminate\Contracts\Filesystem\Filesystem; use Illuminate\Filesystem\FilesystemAdapter; -use League\Flysystem\WhitespacePathNormalizer; +use Illuminate\Support\Facades\Log; +use League\Flysystem\UnableToSetVisibility; +use League\Flysystem\Visibility; use Symfony\Component\HttpFoundation\StreamedResponse; class ImageStorageDisk @@ -30,13 +33,14 @@ public function usingSecureImages(): bool */ protected function adjustPathForDisk(string $path): string { - $path = (new WhitespacePathNormalizer())->normalizePath(str_replace('uploads/images/', '', $path)); + $trimmed = str_replace('uploads/images/', '', $path); + $normalized = FilePathNormalizer::normalize($trimmed); if ($this->usingSecureImages()) { - return $path; + return $normalized; } - return 'uploads/images/' . $path; + return 'uploads/images/' . $normalized; } /** @@ -55,6 +59,15 @@ public function get(string $path): ?string return $this->filesystem->get($this->adjustPathForDisk($path)); } + /** + * Get a stream to the file at the given path. + * @return ?resource + */ + public function stream(string $path): mixed + { + return $this->filesystem->readStream($this->adjustPathForDisk($path)); + } + /** * Save the given image data at the given path. Can choose to set * the image as public which will update its visibility after saving. @@ -64,12 +77,19 @@ public function put(string $path, string $data, bool $makePublic = false): void $path = $this->adjustPathForDisk($path); $this->filesystem->put($path, $data); - // Set visibility when a non-AWS-s3, s3-like storage option is in use. - // Done since this call can break s3-like services but desired for other image stores. - // Attempting to set ACL during above put request requires different permissions - // hence would technically be a breaking change for actual s3 usage. + // Set public visibility to ensure public access on S3, or that the file is accessible + // to other processes (like web-servers) for local file storage options. + // We avoid attempting this for (non-AWS) s3-like systems (even in a try-catch) as + // we've always avoided setting permissions for s3-like due to potential issues, + // with docs advising setting pre-configured permissions instead. + // We also don't do this as the default filesystem/driver level as that can technically + // require different ACLs for S3, and this provides us more logical control. if ($makePublic && !$this->isS3Like()) { - $this->filesystem->setVisibility($path, 'public'); + try { + $this->filesystem->setVisibility($path, Visibility::PUBLIC); + } catch (UnableToSetVisibility $e) { + Log::warning("Unable to set visibility for image upload with relative path: {$path}"); + } } } diff --git a/app/Uploads/UserAvatars.php b/app/Uploads/UserAvatars.php index c623247352b..0cc640f225c 100644 --- a/app/Uploads/UserAvatars.php +++ b/app/Uploads/UserAvatars.php @@ -5,6 +5,7 @@ use BookStack\Exceptions\HttpFetchException; use BookStack\Http\HttpRequestService; use BookStack\Users\Models\User; +use BookStack\Util\WebSafeMimeSniffer; use Exception; use GuzzleHttp\Psr7\Request; use Illuminate\Support\Facades\Log; @@ -53,6 +54,33 @@ public function assignToUserFromExistingData(User $user, string $imageData, stri } } + /** + * Assign a new avatar image to the given user by fetching from a remote URL. + */ + public function assignToUserFromUrl(User $user, string $avatarUrl): void + { + try { + $this->destroyAllForUser($user); + $imageData = $this->getAvatarImageData($avatarUrl); + + $mime = (new WebSafeMimeSniffer())->sniff($imageData); + [$format, $type] = explode('/', $mime, 2); + if ($format !== 'image' || !ImageService::isExtensionSupported($type)) { + return; + } + + $avatar = $this->createAvatarImageFromData($user, $imageData, $type); + $user->avatar()->associate($avatar); + $user->save(); + } catch (Exception $e) { + Log::error('Failed to save user avatar image from URL', [ + 'exception' => $e->getMessage(), + 'url' => $avatarUrl, + 'user_id' => $user->id, + ]); + } + } + /** * Destroy all user avatars uploaded to the given user. */ @@ -105,7 +133,7 @@ protected function createAvatarImageFromData(User $user, string $imageData, stri } /** - * Gets an image from url and returns it as a string of image data. + * Get an image from a URL and return it as a string of image data. * * @throws HttpFetchException */ @@ -113,7 +141,19 @@ protected function getAvatarImageData(string $url): string { try { $client = $this->http->buildClient(5); - $response = $client->sendRequest(new Request('GET', $url)); + $responseCount = 0; + + do { + $response = $client->sendRequest(new Request('GET', $url)); + $responseCount++; + $isRedirect = ($response->getStatusCode() === 301 || $response->getStatusCode() === 302); + $url = $response->getHeader('Location')[0] ?? ''; + } while ($responseCount < 3 && $isRedirect && is_string($url) && str_starts_with($url, 'http')); + + if ($responseCount === 3) { + throw new HttpFetchException("Failed to fetch image, max redirect limit of 3 tries reached. Last fetched URL: {$url}"); + } + if ($response->getStatusCode() !== 200) { throw new HttpFetchException(trans('errors.cannot_get_image_from_url', ['url' => $url])); } diff --git a/app/Users/Controllers/RoleApiController.php b/app/Users/Controllers/RoleApiController.php index 5f4f2999b79..93ecc549bb4 100644 --- a/app/Users/Controllers/RoleApiController.php +++ b/app/Users/Controllers/RoleApiController.php @@ -3,6 +3,7 @@ namespace BookStack\Users\Controllers; use BookStack\Http\ApiController; +use BookStack\Permissions\Permission; use BookStack\Permissions\PermissionsRepo; use BookStack\Users\Models\Role; use Illuminate\Http\Request; @@ -10,18 +11,16 @@ class RoleApiController extends ApiController { - protected PermissionsRepo $permissionsRepo; - protected array $fieldsToExpose = [ 'display_name', 'description', 'mfa_enforced', 'external_auth_id', 'created_at', 'updated_at', ]; - protected $rules = [ + protected array $rules = [ 'create' => [ 'display_name' => ['required', 'string', 'min:3', 'max:180'], 'description' => ['string', 'max:180'], 'mfa_enforced' => ['boolean'], - 'external_auth_id' => ['string'], + 'external_auth_id' => ['string', 'max:180'], 'permissions' => ['array'], 'permissions.*' => ['string'], ], @@ -29,19 +28,18 @@ class RoleApiController extends ApiController 'display_name' => ['string', 'min:3', 'max:180'], 'description' => ['string', 'max:180'], 'mfa_enforced' => ['boolean'], - 'external_auth_id' => ['string'], + 'external_auth_id' => ['string', 'max:180'], 'permissions' => ['array'], 'permissions.*' => ['string'], ] ]; - public function __construct(PermissionsRepo $permissionsRepo) - { - $this->permissionsRepo = $permissionsRepo; - + public function __construct( + protected PermissionsRepo $permissionsRepo + ) { // Checks for all endpoints in this controller $this->middleware(function ($request, $next) { - $this->checkPermission('user-roles-manage'); + $this->checkPermission(Permission::UserRolesManage); return $next($request); }); @@ -125,9 +123,9 @@ public function delete(string $id) } /** - * Format the given role model for single-result display. + * Format the given role model for a single-result display. */ - protected function singleFormatter(Role $role) + protected function singleFormatter(Role $role): void { $role->load('users:id,name,slug'); $role->unsetRelation('permissions'); diff --git a/app/Users/Controllers/RoleController.php b/app/Users/Controllers/RoleController.php index a874ce4d60f..549f6e0ac8f 100644 --- a/app/Users/Controllers/RoleController.php +++ b/app/Users/Controllers/RoleController.php @@ -4,6 +4,7 @@ use BookStack\Exceptions\PermissionsException; use BookStack\Http\Controller; +use BookStack\Permissions\Permission; use BookStack\Permissions\PermissionsRepo; use BookStack\Users\Models\Role; use BookStack\Users\Queries\RolesAllPaginatedAndSorted; @@ -23,7 +24,7 @@ public function __construct( */ public function index(Request $request) { - $this->checkPermission('user-roles-manage'); + $this->checkPermission(Permission::UserRolesManage); $listOptions = SimpleListOptions::fromRequest($request, 'roles')->withSortOptions([ 'display_name' => trans('common.sort_name'), @@ -49,7 +50,7 @@ public function index(Request $request) */ public function create(Request $request) { - $this->checkPermission('user-roles-manage'); + $this->checkPermission(Permission::UserRolesManage); /** @var ?Role $role */ $role = null; @@ -71,11 +72,11 @@ public function create(Request $request) */ public function store(Request $request) { - $this->checkPermission('user-roles-manage'); + $this->checkPermission(Permission::UserRolesManage); $data = $this->validate($request, [ 'display_name' => ['required', 'min:3', 'max:180'], 'description' => ['max:180'], - 'external_auth_id' => ['string'], + 'external_auth_id' => ['string', 'max:180'], 'permissions' => ['array'], 'mfa_enforced' => ['string'], ]); @@ -92,7 +93,7 @@ public function store(Request $request) */ public function edit(string $id) { - $this->checkPermission('user-roles-manage'); + $this->checkPermission(Permission::UserRolesManage); $role = $this->permissionsRepo->getRoleById($id); $this->setPageTitle(trans('settings.role_edit')); @@ -105,11 +106,11 @@ public function edit(string $id) */ public function update(Request $request, string $id) { - $this->checkPermission('user-roles-manage'); + $this->checkPermission(Permission::UserRolesManage); $data = $this->validate($request, [ 'display_name' => ['required', 'min:3', 'max:180'], 'description' => ['max:180'], - 'external_auth_id' => ['string'], + 'external_auth_id' => ['string', 'max:180'], 'permissions' => ['array'], 'mfa_enforced' => ['string'], ]); @@ -127,7 +128,7 @@ public function update(Request $request, string $id) */ public function showDelete(string $id) { - $this->checkPermission('user-roles-manage'); + $this->checkPermission(Permission::UserRolesManage); $role = $this->permissionsRepo->getRoleById($id); $roles = $this->permissionsRepo->getAllRolesExcept($role); $blankRole = $role->newInstance(['display_name' => trans('settings.role_delete_no_migration')]); @@ -146,7 +147,7 @@ public function showDelete(string $id) */ public function delete(Request $request, string $id) { - $this->checkPermission('user-roles-manage'); + $this->checkPermission(Permission::UserRolesManage); try { $migrateRoleId = intval($request->get('migrate_role_id') ?: "0"); diff --git a/app/Users/Controllers/UserAccountController.php b/app/Users/Controllers/UserAccountController.php index 708a91e9d4a..a8baba5294b 100644 --- a/app/Users/Controllers/UserAccountController.php +++ b/app/Users/Controllers/UserAccountController.php @@ -4,6 +4,7 @@ use BookStack\Access\SocialDriverManager; use BookStack\Http\Controller; +use BookStack\Permissions\Permission; use BookStack\Permissions\PermissionApplicator; use BookStack\Settings\UserNotificationPreferences; use BookStack\Settings\UserShortcutMap; @@ -62,9 +63,9 @@ public function updateProfile(Request $request, ImageRepo $imageRepo) 'profile_image' => array_merge(['nullable'], $this->getImageValidationRules()), ]); - $this->userRepo->update($user, $validated, userCan('users-manage')); + $this->userRepo->update($user, $validated, userCan(Permission::UsersManage)); - // Save profile image if in request + // Save the profile image if in request if ($request->hasFile('profile_image')) { $imageUpload = $request->file('profile_image'); $imageRepo->destroyImage($user->avatar); @@ -73,7 +74,7 @@ public function updateProfile(Request $request, ImageRepo $imageRepo) $user->save(); } - // Delete the profile image if reset option is in request + // Delete the profile image if the reset option is in request if ($request->has('profile_image_reset')) { $imageRepo->destroyImage($user->avatar); $user->image_id = 0; @@ -122,7 +123,7 @@ public function updateShortcuts(Request $request) */ public function showNotifications(PermissionApplicator $permissions) { - $this->checkPermission('receive-notifications'); + $this->checkPermission(Permission::ReceiveNotifications); $preferences = (new UserNotificationPreferences(user())); @@ -145,7 +146,7 @@ public function showNotifications(PermissionApplicator $permissions) public function updateNotifications(Request $request) { $this->preventAccessInDemoMode(); - $this->checkPermission('receive-notifications'); + $this->checkPermission(Permission::ReceiveNotifications); $data = $this->validate($request, [ 'preferences' => ['required', 'array'], 'preferences.*' => ['required', 'string'], @@ -218,7 +219,7 @@ public function destroy(Request $request) $this->preventAccessInDemoMode(); $requestNewOwnerId = intval($request->get('new_owner_id')) ?: null; - $newOwnerId = userCan('users-manage') ? $requestNewOwnerId : null; + $newOwnerId = userCan(Permission::UsersManage) ? $requestNewOwnerId : null; $this->userRepo->destroy(user(), $newOwnerId); diff --git a/app/Users/Controllers/UserApiController.php b/app/Users/Controllers/UserApiController.php index 1ccfecd7335..9134b3cc13b 100644 --- a/app/Users/Controllers/UserApiController.php +++ b/app/Users/Controllers/UserApiController.php @@ -4,9 +4,9 @@ use BookStack\Exceptions\UserUpdateException; use BookStack\Http\ApiController; +use BookStack\Permissions\Permission; use BookStack\Users\Models\User; use BookStack\Users\UserRepo; -use Closure; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; use Illuminate\Validation\Rules\Password; @@ -26,38 +26,39 @@ public function __construct(UserRepo $userRepo) // Checks for all endpoints in this controller $this->middleware(function ($request, $next) { - $this->checkPermission('users-manage'); + $this->checkPermission(Permission::UsersManage); $this->preventAccessInDemoMode(); return $next($request); }); } - protected function rules(int $userId = null): array + protected function rules(?int $userId = null): array { return [ 'create' => [ - 'name' => ['required', 'min:2', 'max:100'], + 'name' => ['required', 'string', 'min:1', 'max:100'], 'email' => [ - 'required', 'min:2', 'email', new Unique('users', 'email'), + 'required', 'string', 'email', 'min:2', new Unique('users', 'email'), ], 'external_auth_id' => ['string'], 'language' => ['string', 'max:15', 'alpha_dash'], - 'password' => [Password::default()], + 'password' => ['string', Password::default()], 'roles' => ['array'], 'roles.*' => ['integer'], 'send_invite' => ['boolean'], ], 'update' => [ - 'name' => ['min:2', 'max:100'], + 'name' => ['string', 'min:1', 'max:100'], 'email' => [ - 'min:2', + 'string', 'email', - (new Unique('users', 'email'))->ignore($userId ?? null), + 'min:2', + (new Unique('users', 'email'))->ignore($userId), ], 'external_auth_id' => ['string'], 'language' => ['string', 'max:15', 'alpha_dash'], - 'password' => [Password::default()], + 'password' => ['string', Password::default()], 'roles' => ['array'], 'roles.*' => ['integer'], ], @@ -80,7 +81,7 @@ public function list() return $this->apiListingResponse($users, [ 'id', 'name', 'slug', 'email', 'external_auth_id', 'created_at', 'updated_at', 'last_activity_at', - ], [Closure::fromCallable([$this, 'listFormatter'])]); + ], [$this->listFormatter(...)]); } /** @@ -124,7 +125,7 @@ public function update(Request $request, string $id) { $data = $this->validate($request, $this->rules($id)['update']); $user = $this->userRepo->getById($id); - $this->userRepo->update($user, $data, userCan('users-manage')); + $this->userRepo->update($user, $data, userCan(Permission::UsersManage)); $this->singleFormatter($user); return response()->json($user); diff --git a/app/Users/Controllers/UserController.php b/app/Users/Controllers/UserController.php index 185d6101c61..494221b143e 100644 --- a/app/Users/Controllers/UserController.php +++ b/app/Users/Controllers/UserController.php @@ -3,9 +3,11 @@ namespace BookStack\Users\Controllers; use BookStack\Access\SocialDriverManager; +use BookStack\Access\UserInviteException; use BookStack\Exceptions\ImageUploadException; use BookStack\Exceptions\UserUpdateException; use BookStack\Http\Controller; +use BookStack\Permissions\Permission; use BookStack\Uploads\ImageRepo; use BookStack\Users\Models\Role; use BookStack\Users\Queries\UsersAllPaginatedAndSorted; @@ -14,6 +16,7 @@ use Exception; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Log; use Illuminate\Validation\Rules\Password; use Illuminate\Validation\ValidationException; @@ -30,7 +33,7 @@ public function __construct( */ public function index(Request $request) { - $this->checkPermission('users-manage'); + $this->checkPermission(Permission::UsersManage); $listOptions = SimpleListOptions::fromRequest($request, 'users')->withSortOptions([ 'name' => trans('common.sort_name'), @@ -56,7 +59,7 @@ public function index(Request $request) */ public function create() { - $this->checkPermission('users-manage'); + $this->checkPermission(Permission::UsersManage); $authMethod = config('auth.method'); $roles = Role::query()->orderBy('display_name', 'asc')->get(); $this->setPageTitle(trans('settings.users_add_new')); @@ -71,7 +74,7 @@ public function create() */ public function store(Request $request) { - $this->checkPermission('users-manage'); + $this->checkPermission(Permission::UsersManage); $authMethod = config('auth.method'); $sendInvite = ($request->get('send_invite', 'false') === 'true'); @@ -91,9 +94,15 @@ public function store(Request $request) $validated = $this->validate($request, array_filter($validationRules)); - DB::transaction(function () use ($validated, $sendInvite) { - $this->userRepo->create($validated, $sendInvite); - }); + try { + DB::transaction(function () use ($validated, $sendInvite) { + $this->userRepo->create($validated, $sendInvite); + }); + } catch (UserInviteException $e) { + Log::error("Failed to send user invite with error: {$e->getMessage()}"); + $this->showErrorNotification(trans('errors.users_could_not_send_invite')); + return redirect('/settings/users/create')->withInput(); + } return redirect('/settings/users'); } @@ -103,7 +112,7 @@ public function store(Request $request) */ public function edit(int $id, SocialDriverManager $socialDriverManager) { - $this->checkPermission('users-manage'); + $this->checkPermission(Permission::UsersManage); $user = $this->userRepo->getById($id); $user->load(['apiTokens', 'mfaValues']); @@ -133,10 +142,10 @@ public function edit(int $id, SocialDriverManager $socialDriverManager) public function update(Request $request, int $id) { $this->preventAccessInDemoMode(); - $this->checkPermission('users-manage'); + $this->checkPermission(Permission::UsersManage); $validated = $this->validate($request, [ - 'name' => ['min:2', 'max:100'], + 'name' => ['min:1', 'max:100'], 'email' => ['min:2', 'email', 'unique:users,email,' . $id], 'password' => ['required_with:password_confirm', Password::default()], 'password-confirm' => ['same:password', 'required_with:password'], @@ -174,7 +183,7 @@ public function update(Request $request, int $id) */ public function delete(int $id) { - $this->checkPermission('users-manage'); + $this->checkPermission(Permission::UsersManage); $user = $this->userRepo->getById($id); $this->setPageTitle(trans('settings.users_delete_named', ['userName' => $user->name])); @@ -190,7 +199,7 @@ public function delete(int $id) public function destroy(Request $request, int $id) { $this->preventAccessInDemoMode(); - $this->checkPermission('users-manage'); + $this->checkPermission(Permission::UsersManage); $user = $this->userRepo->getById($id); $newOwnerId = intval($request->get('new_owner_id')) ?: null; diff --git a/app/Users/Controllers/UserProfileController.php b/app/Users/Controllers/UserProfileController.php index bdf268260a1..963d69a6247 100644 --- a/app/Users/Controllers/UserProfileController.php +++ b/app/Users/Controllers/UserProfileController.php @@ -10,16 +10,25 @@ class UserProfileController extends Controller { + public function __construct( + protected UserRepo $userRepo, + protected ActivityQueries $activityQueries, + protected UserContentCounts $contentCounts, + protected UserRecentlyCreatedContent $recentlyCreatedContent + ) { + } + + /** * Show the user profile page. */ - public function show(UserRepo $repo, ActivityQueries $activities, string $slug) + public function show(string $slug) { - $user = $repo->getBySlug($slug); + $user = $this->userRepo->getBySlug($slug); - $userActivity = $activities->userActivity($user); - $recentlyCreated = (new UserRecentlyCreatedContent())->run($user, 5); - $assetCounts = (new UserContentCounts())->run($user); + $userActivity = $this->activityQueries->userActivity($user); + $recentlyCreated = $this->recentlyCreatedContent->run($user, 5); + $assetCounts = $this->contentCounts->run($user); $this->setPageTitle($user->name); diff --git a/app/Users/Controllers/UserSearchController.php b/app/Users/Controllers/UserSearchController.php index b6f37bce08b..a2543b7eed3 100644 --- a/app/Users/Controllers/UserSearchController.php +++ b/app/Users/Controllers/UserSearchController.php @@ -3,6 +3,7 @@ namespace BookStack\Users\Controllers; use BookStack\Http\Controller; +use BookStack\Permissions\Permission; use BookStack\Users\Models\User; use Illuminate\Http\Request; @@ -15,9 +16,9 @@ class UserSearchController extends Controller public function forSelect(Request $request) { $hasPermission = !user()->isGuest() && ( - userCan('users-manage') - || userCan('restrictions-manage-own') - || userCan('restrictions-manage-all') + userCan(Permission::UsersManage) + || userCan(Permission::RestrictionsManageOwn) + || userCan(Permission::RestrictionsManageAll) ); if (!$hasPermission) { diff --git a/app/Users/Models/HasCreatorAndUpdater.php b/app/Users/Models/HasCreatorAndUpdater.php index 5ff46bc3cb7..bb05de11af7 100644 --- a/app/Users/Models/HasCreatorAndUpdater.php +++ b/app/Users/Models/HasCreatorAndUpdater.php @@ -7,6 +7,8 @@ /** * @property int $created_by * @property int $updated_by + * @property ?User $createdBy + * @property ?User $updatedBy */ trait HasCreatorAndUpdater { @@ -25,4 +27,9 @@ public function updatedBy(): BelongsTo { return $this->belongsTo(User::class, 'updated_by'); } + + public function getOwnerFieldName(): string + { + return 'created_by'; + } } diff --git a/app/Users/Models/HasOwner.php b/app/Users/Models/HasOwner.php deleted file mode 100644 index eb830ef6fda..00000000000 --- a/app/Users/Models/HasOwner.php +++ /dev/null @@ -1,19 +0,0 @@ -belongsTo(User::class, 'owned_by'); - } -} diff --git a/app/Users/Models/OwnableInterface.php b/app/Users/Models/OwnableInterface.php new file mode 100644 index 00000000000..8f738487f2d --- /dev/null +++ b/app/Users/Models/OwnableInterface.php @@ -0,0 +1,8 @@ + */ public function permissions(): BelongsToMany { - return $this->belongsToMany(RolePermission::class, 'permission_role', 'role_id', 'permission_id'); + return $this->belongsToMany(RolePermission::class, 'permission_role', 'role_id', 'permission_id') + ->select(['id', 'name']); } /** diff --git a/app/Users/Models/User.php b/app/Users/Models/User.php index 3797e7cb027..0017653b53d 100644 --- a/app/Users/Models/User.php +++ b/app/Users/Models/User.php @@ -10,8 +10,9 @@ use BookStack\Activity\Models\Watch; use BookStack\Api\ApiToken; use BookStack\App\Model; -use BookStack\App\Sluggable; +use BookStack\App\SluggableInterface; use BookStack\Entities\Tools\SlugGenerator; +use BookStack\Permissions\Permission; use BookStack\Translation\LocaleDefinition; use BookStack\Translation\LocaleManager; use BookStack\Uploads\Image; @@ -45,8 +46,9 @@ * @property string $system_name * @property Collection $roles * @property Collection $mfaValues + * @property ?Image $avatar */ -class User extends Model implements AuthenticatableContract, CanResetPasswordContract, Loggable, Sluggable +class User extends Model implements AuthenticatableContract, CanResetPasswordContract, Loggable, SluggableInterface { use HasFactory; use Authenticatable; @@ -63,7 +65,7 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon /** * The attributes that are mass assignable. * - * @var array + * @var list */ protected $fillable = ['name', 'email']; @@ -72,7 +74,7 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon /** * The attributes excluded from the model's JSON form. * - * @var array + * @var list */ protected $hidden = [ 'password', 'remember_token', 'system_name', 'email_confirmed', 'external_auth_id', 'email', @@ -117,14 +119,10 @@ public function hasAppAccess(): bool /** * The roles that belong to the user. * - * @return BelongsToMany + * @return BelongsToMany */ - public function roles() + public function roles(): BelongsToMany { - if ($this->id === 0) { - return; - } - return $this->belongsToMany(Role::class); } @@ -158,8 +156,9 @@ public function attachDefaultRole(): void /** * Check if the user has a particular permission. */ - public function can(string $permissionName): bool + public function can(string|Permission $permission): bool { + $permissionName = is_string($permission) ? $permission : $permission->value; return $this->permissions()->contains($permissionName); } @@ -183,9 +182,9 @@ protected function permissions(): Collection } /** - * Clear any cached permissions on this instance. + * Clear any cached permissions in this instance. */ - public function clearPermissionCache() + public function clearPermissionCache(): void { $this->permissions = null; } @@ -193,7 +192,7 @@ public function clearPermissionCache() /** * Attach a role to this user. */ - public function attachRole(Role $role) + public function attachRole(Role $role): void { $this->roles()->attach($role->id); $this->unsetRelation('roles'); @@ -209,15 +208,11 @@ public function socialAccounts(): HasMany /** * Check if the user has a social account, - * If a driver is passed it checks for that single account type. - * - * @param bool|string $socialDriver - * - * @return bool + * If a driver is passed, it checks for that single account type. */ - public function hasSocialAccount($socialDriver = false) + public function hasSocialAccount(string $socialDriver = ''): bool { - if ($socialDriver === false) { + if (empty($socialDriver)) { return $this->socialAccounts()->count() > 0; } @@ -371,7 +366,7 @@ public function logDescriptor(): string */ public function refreshSlug(): string { - $this->slug = app()->make(SlugGenerator::class)->generate($this); + $this->slug = app()->make(SlugGenerator::class)->generate($this, $this->name); return $this->slug; } diff --git a/app/Users/Queries/UserContentCounts.php b/app/Users/Queries/UserContentCounts.php index 178d8536b1d..af38bfa7e2a 100644 --- a/app/Users/Queries/UserContentCounts.php +++ b/app/Users/Queries/UserContentCounts.php @@ -2,10 +2,7 @@ namespace BookStack\Users\Queries; -use BookStack\Entities\Models\Book; -use BookStack\Entities\Models\Bookshelf; -use BookStack\Entities\Models\Chapter; -use BookStack\Entities\Models\Page; +use BookStack\Entities\Queries\EntityQueries; use BookStack\Users\Models\User; /** @@ -13,6 +10,12 @@ */ class UserContentCounts { + public function __construct( + protected EntityQueries $queries, + ) { + } + + /** * @return array{pages: int, chapters: int, books: int, shelves: int} */ @@ -21,10 +24,10 @@ public function run(User $user): array $createdBy = ['created_by' => $user->id]; return [ - 'pages' => Page::visible()->where($createdBy)->count(), - 'chapters' => Chapter::visible()->where($createdBy)->count(), - 'books' => Book::visible()->where($createdBy)->count(), - 'shelves' => Bookshelf::visible()->where($createdBy)->count(), + 'pages' => $this->queries->pages->visibleForList()->where($createdBy)->count(), + 'chapters' => $this->queries->chapters->visibleForList()->where($createdBy)->count(), + 'books' => $this->queries->books->visibleForList()->where($createdBy)->count(), + 'shelves' => $this->queries->shelves->visibleForList()->where($createdBy)->count(), ]; } } diff --git a/app/Users/Queries/UserRecentlyCreatedContent.php b/app/Users/Queries/UserRecentlyCreatedContent.php index 23db2c1f1a2..23850e07201 100644 --- a/app/Users/Queries/UserRecentlyCreatedContent.php +++ b/app/Users/Queries/UserRecentlyCreatedContent.php @@ -2,10 +2,7 @@ namespace BookStack\Users\Queries; -use BookStack\Entities\Models\Book; -use BookStack\Entities\Models\Bookshelf; -use BookStack\Entities\Models\Chapter; -use BookStack\Entities\Models\Page; +use BookStack\Entities\Queries\EntityQueries; use BookStack\Users\Models\User; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Collection; @@ -15,6 +12,11 @@ */ class UserRecentlyCreatedContent { + public function __construct( + protected EntityQueries $queries, + ) { + } + /** * @return array{pages: Collection, chapters: Collection, books: Collection, shelves: Collection} */ @@ -28,10 +30,10 @@ public function run(User $user, int $count): array }; return [ - 'pages' => $query(Page::visible()->where('draft', '=', false)), - 'chapters' => $query(Chapter::visible()), - 'books' => $query(Book::visible()), - 'shelves' => $query(Bookshelf::visible()), + 'pages' => $query($this->queries->pages->visibleForList()->where('draft', '=', false)), + 'chapters' => $query($this->queries->chapters->visibleForList()), + 'books' => $query($this->queries->books->visibleForList()), + 'shelves' => $query($this->queries->shelves->visibleForList()), ]; } } diff --git a/app/Users/UserRepo.php b/app/Users/UserRepo.php index 32e23ecdec1..d24f7002e71 100644 --- a/app/Users/UserRepo.php +++ b/app/Users/UserRepo.php @@ -2,6 +2,7 @@ namespace BookStack\Users; +use BookStack\Access\UserInviteException; use BookStack\Access\UserInviteService; use BookStack\Activity\ActivityType; use BookStack\Entities\EntityProvider; @@ -83,6 +84,7 @@ public function createWithoutActivity(array $data, bool $emailConfirmed = false) * As per "createWithoutActivity" but records a "create" activity. * * @param array{name: string, email: string, password: ?string, external_auth_id: ?string, language: ?string, roles: ?array} $data + * @throws UserInviteException */ public function create(array $data, bool $sendInvite = false): User { @@ -98,13 +100,13 @@ public function create(array $data, bool $sendInvite = false): User } /** - * Update the given user with the given data. + * Update the given user with the given data, but do not create an activity. * * @param array{name: ?string, email: ?string, external_auth_id: ?string, password: ?string, roles: ?array, language: ?string} $data * * @throws UserUpdateException */ - public function update(User $user, array $data, bool $manageUsersAllowed): User + public function updateWithoutActivity(User $user, array $data, bool $manageUsersAllowed): User { if (!empty($data['name'])) { $user->name = $data['name']; @@ -132,6 +134,21 @@ public function update(User $user, array $data, bool $manageUsersAllowed): User } $user->save(); + + return $user; + } + + /** + * Update the given user with the given data. + * + * @param array{name: ?string, email: ?string, external_auth_id: ?string, password: ?string, roles: ?array, language: ?string} $data + * + * @throws UserUpdateException + */ + public function update(User $user, array $data, bool $manageUsersAllowed): User + { + $user = $this->updateWithoutActivity($user, $data, $manageUsersAllowed); + Activity::add(ActivityType::USER_UPDATE, $user); return $user; diff --git a/app/Util/CspService.php b/app/Util/CspService.php index 227ec8e0b3d..4262b5c98f8 100644 --- a/app/Util/CspService.php +++ b/app/Util/CspService.php @@ -133,18 +133,30 @@ protected function getAllowedIframeHosts(): array protected function getAllowedIframeSources(): array { - $sources = config('app.iframe_sources', ''); - $hosts = array_filter(explode(' ', $sources)); + $sources = explode(' ', config('app.iframe_sources', '')); + $sources[] = $this->getDrawioHost(); - // Extract drawing service url to allow embedding if active + return array_filter($sources); + } + + /** + * Extract the host name of the configured drawio URL for use in CSP. + * Returns empty string if not in use. + */ + protected function getDrawioHost(): string + { $drawioConfigValue = config('services.drawio'); - if ($drawioConfigValue) { - $drawioSource = is_string($drawioConfigValue) ? $drawioConfigValue : 'https://embed.diagrams.net/'; - $drawioSourceParsed = parse_url($drawioSource); - $drawioHost = $drawioSourceParsed['scheme'] . '://' . $drawioSourceParsed['host']; - $hosts[] = $drawioHost; + if (!$drawioConfigValue) { + return ''; + } + + $drawioSource = is_string($drawioConfigValue) ? $drawioConfigValue : 'https://embed.diagrams.net/'; + $drawioSourceParsed = parse_url($drawioSource); + $drawioHost = $drawioSourceParsed['scheme'] . '://' . $drawioSourceParsed['host']; + if (isset($drawioSourceParsed['port'])) { + $drawioHost .= ':' . $drawioSourceParsed['port']; } - return $hosts; + return $drawioHost; } } diff --git a/app/Util/DatabaseTransaction.php b/app/Util/DatabaseTransaction.php new file mode 100644 index 00000000000..e36bd2ef310 --- /dev/null +++ b/app/Util/DatabaseTransaction.php @@ -0,0 +1,42 @@ +callback); + } +} diff --git a/app/Util/DateFormatter.php b/app/Util/DateFormatter.php new file mode 100644 index 00000000000..c6e60bd5399 --- /dev/null +++ b/app/Util/DateFormatter.php @@ -0,0 +1,26 @@ +clone()->setTimezone($this->displayTimezone); + + return $withDisplayTimezone->format('Y-m-d H:i:s T'); + } + + public function relative(Carbon $date, bool $includeSuffix = true): string + { + return $date->diffForHumans(null, $includeSuffix ? null : CarbonInterface::DIFF_ABSOLUTE); + } +} diff --git a/app/Util/FilePathNormalizer.php b/app/Util/FilePathNormalizer.php new file mode 100644 index 00000000000..d55fb74f879 --- /dev/null +++ b/app/Util/FilePathNormalizer.php @@ -0,0 +1,17 @@ +normalizePath($path); + } +} diff --git a/app/Util/HtmlDescriptionFilter.php b/app/Util/HtmlDescriptionFilter.php index 7287586d16f..d4f7d2c8fa2 100644 --- a/app/Util/HtmlDescriptionFilter.php +++ b/app/Util/HtmlDescriptionFilter.php @@ -4,7 +4,6 @@ use DOMAttr; use DOMElement; -use DOMNamedNodeMap; use DOMNode; /** @@ -20,11 +19,12 @@ class HtmlDescriptionFilter */ protected static array $allowedAttrsByElements = [ 'p' => [], - 'a' => ['href', 'title'], + 'a' => ['href', 'title', 'target'], 'ol' => [], 'ul' => [], 'li' => [], 'strong' => [], + 'span' => [], 'em' => [], 'br' => [], ]; @@ -59,7 +59,6 @@ protected static function filterElement(DOMElement $element): void return; } - /** @var DOMNamedNodeMap $attrs */ $attrs = $element->attributes; for ($i = $attrs->length - 1; $i >= 0; $i--) { /** @var DOMAttr $attr */ @@ -70,7 +69,8 @@ protected static function filterElement(DOMElement $element): void } } - foreach ($element->childNodes as $child) { + $childNodes = [...$element->childNodes]; + foreach ($childNodes as $child) { if ($child instanceof DOMElement) { static::filterElement($child); } diff --git a/app/Util/HtmlDocument.php b/app/Util/HtmlDocument.php index b8c53d43916..7517955b9f6 100644 --- a/app/Util/HtmlDocument.php +++ b/app/Util/HtmlDocument.php @@ -6,6 +6,7 @@ use DOMElement; use DOMNode; use DOMNodeList; +use DOMText; use DOMXPath; /** @@ -81,6 +82,14 @@ public function createElement(string $localName, string $value = ''): DOMElement return $element; } + /** + * Create a new text node within this document. + */ + public function createTextNode(string $text): DOMText + { + return $this->document->createTextNode($text); + } + /** * Get an element within the document of the given ID. */ diff --git a/app/Util/OutOfMemoryHandler.php b/app/Util/OutOfMemoryHandler.php index 88e9581f492..6632d22a5d5 100644 --- a/app/Util/OutOfMemoryHandler.php +++ b/app/Util/OutOfMemoryHandler.php @@ -42,7 +42,7 @@ protected function handle(): mixed } /** - * Forget the handler so no action is taken place on out of memory. + * Forget the handler, so no action is taken place on out of memory. */ public function forget(): void { @@ -53,6 +53,11 @@ public function forget(): void protected function getHandler(): Handler { + /** + * We want to resolve our specific BookStack handling via the set app handler + * singleton, but phpstan will only infer based on the interface. + * @phpstan-ignore return.type + */ return app()->make(ExceptionHandler::class); } } diff --git a/app/Util/SsrUrlValidator.php b/app/Util/SsrUrlValidator.php index 0b3a6a31de9..076a653fc86 100644 --- a/app/Util/SsrUrlValidator.php +++ b/app/Util/SsrUrlValidator.php @@ -4,11 +4,16 @@ use BookStack\Exceptions\HttpFetchException; +/** + * Validate the host we're connecting to when making a server-side-request. + * Will use the given hosts config if given during construction otherwise + * will look to the app configured config. + */ class SsrUrlValidator { protected string $config; - public function __construct(string $config = null) + public function __construct(?string $config = null) { $this->config = $config ?? config('app.ssr_hosts') ?? ''; } diff --git a/app/Util/WebSafeMimeSniffer.php b/app/Util/WebSafeMimeSniffer.php index b182d8ac19b..4a82de85d25 100644 --- a/app/Util/WebSafeMimeSniffer.php +++ b/app/Util/WebSafeMimeSniffer.php @@ -13,7 +13,7 @@ class WebSafeMimeSniffer /** * @var string[] */ - protected $safeMimes = [ + protected array $safeMimes = [ 'application/json', 'application/octet-stream', 'application/pdf', @@ -48,16 +48,28 @@ class WebSafeMimeSniffer 'video/av1', ]; + protected array $textTypesByExtension = [ + 'css' => 'text/css', + 'js' => 'text/javascript', + 'json' => 'application/json', + 'csv' => 'text/csv', + ]; + /** * Sniff the mime-type from the given file content while running the result * through an allow-list to ensure a web-safe result. * Takes the content as a reference since the value may be quite large. + * Accepts an optional $extension which can be used for further guessing. */ - public function sniff(string &$content): string + public function sniff(string &$content, string $extension = ''): string { $fInfo = new finfo(FILEINFO_MIME_TYPE); $mime = $fInfo->buffer($content) ?: 'application/octet-stream'; + if ($mime === 'text/plain' && $extension) { + $mime = $this->textTypesByExtension[$extension] ?? 'text/plain'; + } + if (in_array($mime, $this->safeMimes)) { return $mime; } diff --git a/bookstack-system-cli b/bookstack-system-cli index 0a643e1142e..c55c5a8a784 100755 Binary files a/bookstack-system-cli and b/bookstack-system-cli differ diff --git a/composer.json b/composer.json index 85bb3cbaebd..2285a22cb18 100644 --- a/composer.json +++ b/composer.json @@ -8,7 +8,7 @@ "license": "MIT", "type": "project", "require": { - "php": "^8.0.2", + "php": "^8.2.0", "ext-curl": "*", "ext-dom": "*", "ext-fileinfo": "*", @@ -16,15 +16,15 @@ "ext-json": "*", "ext-mbstring": "*", "ext-xml": "*", - "bacon/bacon-qr-code": "^2.0", - "barryvdh/laravel-dompdf": "^2.0", - "barryvdh/laravel-snappy": "^1.0", - "doctrine/dbal": "^3.5", + "ext-zip": "*", + "bacon/bacon-qr-code": "^3.0", + "dompdf/dompdf": "^3.1", "guzzlehttp/guzzle": "^7.4", - "intervention/image": "^2.7", - "laravel/framework": "^9.0", + "intervention/image": "^3.5", + "knplabs/knp-snappy": "^1.5", + "laravel/framework": "^v12.26.4", "laravel/socialite": "^5.10", - "laravel/tinker": "^2.6", + "laravel/tinker": "^2.8", "league/commonmark": "^2.3", "league/flysystem-aws-s3-v3": "^3.0", "league/html-to-markdown": "^5.0.0", @@ -32,24 +32,23 @@ "onelogin/php-saml": "^4.0", "phpseclib/phpseclib": "^3.0", "pragmarx/google2fa": "^8.0", - "predis/predis": "^2.1", + "predis/predis": "^3.2", "socialiteproviders/discord": "^4.1", "socialiteproviders/gitlab": "^4.1", "socialiteproviders/microsoft-azure": "^5.1", "socialiteproviders/okta": "^4.2", "socialiteproviders/twitch": "^5.3", - "ssddanbrown/htmldiff": "^1.0.2", - "ssddanbrown/symfony-mailer": "6.0.x-dev" + "ssddanbrown/htmldiff": "^2.0.0" }, "require-dev": { "fakerphp/faker": "^1.21", "itsgoingd/clockwork": "^5.1", "mockery/mockery": "^1.5", - "nunomaduro/collision": "^6.4", - "larastan/larastan": "^2.7", - "phpunit/phpunit": "^9.5", + "nunomaduro/collision": "^8.6", + "larastan/larastan": "^v3.0", + "phpunit/phpunit": "^11.5", "squizlabs/php_codesniffer": "^3.7", - "ssddanbrown/asserthtml": "^2.0" + "ssddanbrown/asserthtml": "^3.1" }, "autoload": { "psr-4": { @@ -72,6 +71,10 @@ "lint": "phpcs", "test": "phpunit", "t-reset": "@php artisan test --recreate-databases", + "build-licenses": [ + "@php ./dev/licensing/gen-js-licenses", + "@php ./dev/licensing/gen-php-licenses" + ], "post-autoload-dump": [ "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump", "@php artisan package:discover --ansi" @@ -99,7 +102,7 @@ "preferred-install": "dist", "sort-packages": true, "platform": { - "php": "8.0.2" + "php": "8.2.0" } }, "extra": { diff --git a/composer.lock b/composer.lock index c27012f5e6d..1dbb752034f 100644 --- a/composer.lock +++ b/composer.lock @@ -4,20 +4,20 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "7c64775f79832d552a2ef40e11f79c40", + "content-hash": "506bcb9f80a5fd736b79c8e138efafa0", "packages": [ { "name": "aws/aws-crt-php", - "version": "v1.2.4", + "version": "v1.2.7", "source": { "type": "git", "url": "https://github.com/awslabs/aws-crt-php.git", - "reference": "eb0c6e4e142224a10b08f49ebf87f32611d162b2" + "reference": "d71d9906c7bb63a28295447ba12e74723bd3730e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/awslabs/aws-crt-php/zipball/eb0c6e4e142224a10b08f49ebf87f32611d162b2", - "reference": "eb0c6e4e142224a10b08f49ebf87f32611d162b2", + "url": "https://api.github.com/repos/awslabs/aws-crt-php/zipball/d71d9906c7bb63a28295447ba12e74723bd3730e", + "reference": "d71d9906c7bb63a28295447ba12e74723bd3730e", "shasum": "" }, "require": { @@ -56,22 +56,22 @@ ], "support": { "issues": "https://github.com/awslabs/aws-crt-php/issues", - "source": "https://github.com/awslabs/aws-crt-php/tree/v1.2.4" + "source": "https://github.com/awslabs/aws-crt-php/tree/v1.2.7" }, - "time": "2023-11-08T00:42:13+00:00" + "time": "2024-10-18T22:15:13+00:00" }, { "name": "aws/aws-sdk-php", - "version": "3.300.4", + "version": "3.356.8", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "27d59c22c121ce9c0041c563dc9d7270e180925c" + "reference": "3efa8c62c11fedb17b90f60b2d3a9f815b406e63" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/27d59c22c121ce9c0041c563dc9d7270e180925c", - "reference": "27d59c22c121ce9c0041c563dc9d7270e180925c", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/3efa8c62c11fedb17b90f60b2d3a9f815b406e63", + "reference": "3efa8c62c11fedb17b90f60b2d3a9f815b406e63", "shasum": "" }, "require": { @@ -79,31 +79,30 @@ "ext-json": "*", "ext-pcre": "*", "ext-simplexml": "*", - "guzzlehttp/guzzle": "^6.5.8 || ^7.4.5", - "guzzlehttp/promises": "^1.4.0 || ^2.0", - "guzzlehttp/psr7": "^1.9.1 || ^2.4.5", - "mtdowling/jmespath.php": "^2.6", - "php": ">=7.2.5", - "psr/http-message": "^1.0 || ^2.0" + "guzzlehttp/guzzle": "^7.4.5", + "guzzlehttp/promises": "^2.0", + "guzzlehttp/psr7": "^2.4.5", + "mtdowling/jmespath.php": "^2.8.0", + "php": ">=8.1", + "psr/http-message": "^2.0" }, "require-dev": { "andrewsville/php-token-reflection": "^1.4", "aws/aws-php-sns-message-validator": "~1.0", "behat/behat": "~3.0", - "composer/composer": "^1.10.22", + "composer/composer": "^2.7.8", "dms/phpunit-arraysubset-asserts": "^0.4.0", "doctrine/cache": "~1.4", "ext-dom": "*", "ext-openssl": "*", "ext-pcntl": "*", "ext-sockets": "*", - "nette/neon": "^2.3", - "paragonie/random_compat": ">= 2", "phpunit/phpunit": "^5.6.3 || ^8.5 || ^9.5", - "psr/cache": "^1.0", - "psr/simple-cache": "^1.0", - "sebastian/comparator": "^1.2.3 || ^4.0", - "yoast/phpunit-polyfills": "^1.0" + "psr/cache": "^2.0 || ^3.0", + "psr/simple-cache": "^2.0 || ^3.0", + "sebastian/comparator": "^1.2.3 || ^4.0 || ^5.0", + "symfony/filesystem": "^v6.4.0 || ^v7.1.0", + "yoast/phpunit-polyfills": "^2.0" }, "suggest": { "aws/aws-php-sns-message-validator": "To validate incoming SNS notifications", @@ -124,7 +123,10 @@ ], "psr-4": { "Aws\\": "src/" - } + }, + "exclude-from-classmap": [ + "src/data/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -149,36 +151,36 @@ "sdk" ], "support": { - "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80", + "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.300.4" + "source": "https://github.com/aws/aws-sdk-php/tree/3.356.8" }, - "time": "2024-02-23T19:10:30+00:00" + "time": "2025-08-29T18:06:18+00:00" }, { "name": "bacon/bacon-qr-code", - "version": "2.0.8", + "version": "v3.0.1", "source": { "type": "git", "url": "https://github.com/Bacon/BaconQrCode.git", - "reference": "8674e51bb65af933a5ffaf1c308a660387c35c22" + "reference": "f9cc1f52b5a463062251d666761178dbdb6b544f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Bacon/BaconQrCode/zipball/8674e51bb65af933a5ffaf1c308a660387c35c22", - "reference": "8674e51bb65af933a5ffaf1c308a660387c35c22", + "url": "https://api.github.com/repos/Bacon/BaconQrCode/zipball/f9cc1f52b5a463062251d666761178dbdb6b544f", + "reference": "f9cc1f52b5a463062251d666761178dbdb6b544f", "shasum": "" }, "require": { "dasprid/enum": "^1.0.3", "ext-iconv": "*", - "php": "^7.1 || ^8.0" + "php": "^8.1" }, "require-dev": { - "phly/keep-a-changelog": "^2.1", - "phpunit/phpunit": "^7 | ^8 | ^9", - "spatie/phpunit-snapshot-assertions": "^4.2.9", - "squizlabs/php_codesniffer": "^3.4" + "phly/keep-a-changelog": "^2.12", + "phpunit/phpunit": "^10.5.11 || 11.0.4", + "spatie/phpunit-snapshot-assertions": "^5.1.5", + "squizlabs/php_codesniffer": "^3.9" }, "suggest": { "ext-imagick": "to generate QR code images" @@ -205,186 +207,31 @@ "homepage": "https://github.com/Bacon/BaconQrCode", "support": { "issues": "https://github.com/Bacon/BaconQrCode/issues", - "source": "https://github.com/Bacon/BaconQrCode/tree/2.0.8" - }, - "time": "2022-12-07T17:46:57+00:00" - }, - { - "name": "barryvdh/laravel-dompdf", - "version": "v2.0.1", - "source": { - "type": "git", - "url": "https://github.com/barryvdh/laravel-dompdf.git", - "reference": "9843d2be423670fb434f4c978b3c0f4dd92c87a6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/barryvdh/laravel-dompdf/zipball/9843d2be423670fb434f4c978b3c0f4dd92c87a6", - "reference": "9843d2be423670fb434f4c978b3c0f4dd92c87a6", - "shasum": "" - }, - "require": { - "dompdf/dompdf": "^2.0.1", - "illuminate/support": "^6|^7|^8|^9|^10", - "php": "^7.2 || ^8.0" - }, - "require-dev": { - "nunomaduro/larastan": "^1|^2", - "orchestra/testbench": "^4|^5|^6|^7|^8", - "phpro/grumphp": "^1", - "squizlabs/php_codesniffer": "^3.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - }, - "laravel": { - "providers": [ - "Barryvdh\\DomPDF\\ServiceProvider" - ], - "aliases": { - "Pdf": "Barryvdh\\DomPDF\\Facade\\Pdf", - "PDF": "Barryvdh\\DomPDF\\Facade\\Pdf" - } - } - }, - "autoload": { - "psr-4": { - "Barryvdh\\DomPDF\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Barry vd. Heuvel", - "email": "barryvdh@gmail.com" - } - ], - "description": "A DOMPDF Wrapper for Laravel", - "keywords": [ - "dompdf", - "laravel", - "pdf" - ], - "support": { - "issues": "https://github.com/barryvdh/laravel-dompdf/issues", - "source": "https://github.com/barryvdh/laravel-dompdf/tree/v2.0.1" + "source": "https://github.com/Bacon/BaconQrCode/tree/v3.0.1" }, - "funding": [ - { - "url": "https://fruitcake.nl", - "type": "custom" - }, - { - "url": "https://github.com/barryvdh", - "type": "github" - } - ], - "time": "2023-01-12T15:12:49+00:00" - }, - { - "name": "barryvdh/laravel-snappy", - "version": "v1.0.2", - "source": { - "type": "git", - "url": "https://github.com/barryvdh/laravel-snappy.git", - "reference": "940eec2d99b89cbc9bea2f493cf068382962a485" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/barryvdh/laravel-snappy/zipball/940eec2d99b89cbc9bea2f493cf068382962a485", - "reference": "940eec2d99b89cbc9bea2f493cf068382962a485", - "shasum": "" - }, - "require": { - "illuminate/filesystem": "^9|^10", - "illuminate/support": "^9|^10", - "knplabs/knp-snappy": "^1.4", - "php": ">=7.2" - }, - "require-dev": { - "orchestra/testbench": "^7|^8" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - }, - "laravel": { - "providers": [ - "Barryvdh\\Snappy\\ServiceProvider" - ], - "aliases": { - "PDF": "Barryvdh\\Snappy\\Facades\\SnappyPdf", - "SnappyImage": "Barryvdh\\Snappy\\Facades\\SnappyImage" - } - } - }, - "autoload": { - "psr-4": { - "Barryvdh\\Snappy\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Barry vd. Heuvel", - "email": "barryvdh@gmail.com" - } - ], - "description": "Snappy PDF/Image for Laravel", - "keywords": [ - "image", - "laravel", - "pdf", - "snappy", - "wkhtmltoimage", - "wkhtmltopdf" - ], - "support": { - "issues": "https://github.com/barryvdh/laravel-snappy/issues", - "source": "https://github.com/barryvdh/laravel-snappy/tree/v1.0.2" - }, - "funding": [ - { - "url": "https://fruitcake.nl", - "type": "custom" - }, - { - "url": "https://github.com/barryvdh", - "type": "github" - } - ], - "time": "2023-04-07T10:38:54+00:00" + "time": "2024-10-01T13:55:55+00:00" }, { "name": "brick/math", - "version": "0.11.0", + "version": "0.13.1", "source": { "type": "git", "url": "https://github.com/brick/math.git", - "reference": "0ad82ce168c82ba30d1c01ec86116ab52f589478" + "reference": "fc7ed316430118cc7836bf45faff18d5dfc8de04" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/brick/math/zipball/0ad82ce168c82ba30d1c01ec86116ab52f589478", - "reference": "0ad82ce168c82ba30d1c01ec86116ab52f589478", + "url": "https://api.github.com/repos/brick/math/zipball/fc7ed316430118cc7836bf45faff18d5dfc8de04", + "reference": "fc7ed316430118cc7836bf45faff18d5dfc8de04", "shasum": "" }, "require": { - "php": "^8.0" + "php": "^8.1" }, "require-dev": { "php-coveralls/php-coveralls": "^2.2", - "phpunit/phpunit": "^9.0", - "vimeo/psalm": "5.0.0" + "phpunit/phpunit": "^10.1", + "vimeo/psalm": "6.8.8" }, "type": "library", "autoload": { @@ -404,12 +251,17 @@ "arithmetic", "bigdecimal", "bignum", + "bignumber", "brick", - "math" + "decimal", + "integer", + "math", + "mathematics", + "rational" ], "support": { "issues": "https://github.com/brick/math/issues", - "source": "https://github.com/brick/math/tree/0.11.0" + "source": "https://github.com/brick/math/tree/0.13.1" }, "funding": [ { @@ -417,30 +269,30 @@ "type": "github" } ], - "time": "2023-01-15T23:15:59+00:00" + "time": "2025-03-29T13:50:30+00:00" }, { "name": "carbonphp/carbon-doctrine-types", - "version": "2.1.0", + "version": "3.2.0", "source": { "type": "git", "url": "https://github.com/CarbonPHP/carbon-doctrine-types.git", - "reference": "99f76ffa36cce3b70a4a6abce41dba15ca2e84cb" + "reference": "18ba5ddfec8976260ead6e866180bd5d2f71aa1d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/CarbonPHP/carbon-doctrine-types/zipball/99f76ffa36cce3b70a4a6abce41dba15ca2e84cb", - "reference": "99f76ffa36cce3b70a4a6abce41dba15ca2e84cb", + "url": "https://api.github.com/repos/CarbonPHP/carbon-doctrine-types/zipball/18ba5ddfec8976260ead6e866180bd5d2f71aa1d", + "reference": "18ba5ddfec8976260ead6e866180bd5d2f71aa1d", "shasum": "" }, "require": { - "php": "^7.4 || ^8.0" + "php": "^8.1" }, "conflict": { - "doctrine/dbal": "<3.7.0 || >=4.0.0" + "doctrine/dbal": "<4.0.0 || >=5.0.0" }, "require-dev": { - "doctrine/dbal": "^3.7.0", + "doctrine/dbal": "^4.0.0", "nesbot/carbon": "^2.71.0 || ^3.0.0", "phpunit/phpunit": "^10.3" }, @@ -470,7 +322,7 @@ ], "support": { "issues": "https://github.com/CarbonPHP/carbon-doctrine-types/issues", - "source": "https://github.com/CarbonPHP/carbon-doctrine-types/tree/2.1.0" + "source": "https://github.com/CarbonPHP/carbon-doctrine-types/tree/3.2.0" }, "funding": [ { @@ -486,27 +338,27 @@ "type": "tidelift" } ], - "time": "2023-12-11T17:09:12+00:00" + "time": "2024-02-09T16:56:22+00:00" }, { "name": "dasprid/enum", - "version": "1.0.5", + "version": "1.0.6", "source": { "type": "git", "url": "https://github.com/DASPRiD/Enum.git", - "reference": "6faf451159fb8ba4126b925ed2d78acfce0dc016" + "reference": "8dfd07c6d2cf31c8da90c53b83c026c7696dda90" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/DASPRiD/Enum/zipball/6faf451159fb8ba4126b925ed2d78acfce0dc016", - "reference": "6faf451159fb8ba4126b925ed2d78acfce0dc016", + "url": "https://api.github.com/repos/DASPRiD/Enum/zipball/8dfd07c6d2cf31c8da90c53b83c026c7696dda90", + "reference": "8dfd07c6d2cf31c8da90c53b83c026c7696dda90", "shasum": "" }, "require": { "php": ">=7.1 <9.0" }, "require-dev": { - "phpunit/phpunit": "^7 | ^8 | ^9", + "phpunit/phpunit": "^7 || ^8 || ^9 || ^10 || ^11", "squizlabs/php_codesniffer": "*" }, "type": "library", @@ -534,22 +386,22 @@ ], "support": { "issues": "https://github.com/DASPRiD/Enum/issues", - "source": "https://github.com/DASPRiD/Enum/tree/1.0.5" + "source": "https://github.com/DASPRiD/Enum/tree/1.0.6" }, - "time": "2023-08-25T16:18:39+00:00" + "time": "2024-08-09T14:30:48+00:00" }, { "name": "dflydev/dot-access-data", - "version": "v3.0.2", + "version": "v3.0.3", "source": { "type": "git", "url": "https://github.com/dflydev/dflydev-dot-access-data.git", - "reference": "f41715465d65213d644d3141a6a93081be5d3549" + "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/f41715465d65213d644d3141a6a93081be5d3549", - "reference": "f41715465d65213d644d3141a6a93081be5d3549", + "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/a23a2bf4f31d3518f3ecb38660c95715dfead60f", + "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f", "shasum": "" }, "require": { @@ -609,42 +461,38 @@ ], "support": { "issues": "https://github.com/dflydev/dflydev-dot-access-data/issues", - "source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.2" + "source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.3" }, - "time": "2022-10-27T11:44:00+00:00" + "time": "2024-07-08T12:26:09+00:00" }, { - "name": "doctrine/cache", - "version": "2.2.0", + "name": "doctrine/inflector", + "version": "2.1.0", "source": { "type": "git", - "url": "https://github.com/doctrine/cache.git", - "reference": "1ca8f21980e770095a31456042471a57bc4c68fb" + "url": "https://github.com/doctrine/inflector.git", + "reference": "6d6c96277ea252fc1304627204c3d5e6e15faa3b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/cache/zipball/1ca8f21980e770095a31456042471a57bc4c68fb", - "reference": "1ca8f21980e770095a31456042471a57bc4c68fb", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/6d6c96277ea252fc1304627204c3d5e6e15faa3b", + "reference": "6d6c96277ea252fc1304627204c3d5e6e15faa3b", "shasum": "" }, "require": { - "php": "~7.1 || ^8.0" - }, - "conflict": { - "doctrine/common": ">2.2,<2.4" + "php": "^7.2 || ^8.0" }, "require-dev": { - "cache/integration-tests": "dev-master", - "doctrine/coding-standard": "^9", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "psr/cache": "^1.0 || ^2.0 || ^3.0", - "symfony/cache": "^4.4 || ^5.4 || ^6", - "symfony/var-exporter": "^4.4 || ^5.4 || ^6" + "doctrine/coding-standard": "^12.0 || ^13.0", + "phpstan/phpstan": "^1.12 || ^2.0", + "phpstan/phpstan-phpunit": "^1.4 || ^2.0", + "phpstan/phpstan-strict-rules": "^1.6 || ^2.0", + "phpunit/phpunit": "^8.5 || ^12.2" }, "type": "library", "autoload": { "psr-4": { - "Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache" + "Doctrine\\Inflector\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -673,135 +521,23 @@ "email": "schmittjoh@gmail.com" } ], - "description": "PHP Doctrine Cache library is a popular cache implementation that supports many different drivers such as redis, memcache, apc, mongodb and others.", - "homepage": "https://www.doctrine-project.org/projects/cache.html", + "description": "PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.", + "homepage": "https://www.doctrine-project.org/projects/inflector.html", "keywords": [ - "abstraction", - "apcu", - "cache", - "caching", - "couchdb", - "memcached", + "inflection", + "inflector", + "lowercase", + "manipulation", "php", - "redis", - "xcache" + "plural", + "singular", + "strings", + "uppercase", + "words" ], "support": { - "issues": "https://github.com/doctrine/cache/issues", - "source": "https://github.com/doctrine/cache/tree/2.2.0" - }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fcache", - "type": "tidelift" - } - ], - "time": "2022-05-20T20:07:39+00:00" - }, - { - "name": "doctrine/dbal", - "version": "3.8.2", - "source": { - "type": "git", - "url": "https://github.com/doctrine/dbal.git", - "reference": "a19a1d05ca211f41089dffcc387733a6875196cb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/a19a1d05ca211f41089dffcc387733a6875196cb", - "reference": "a19a1d05ca211f41089dffcc387733a6875196cb", - "shasum": "" - }, - "require": { - "composer-runtime-api": "^2", - "doctrine/cache": "^1.11|^2.0", - "doctrine/deprecations": "^0.5.3|^1", - "doctrine/event-manager": "^1|^2", - "php": "^7.4 || ^8.0", - "psr/cache": "^1|^2|^3", - "psr/log": "^1|^2|^3" - }, - "require-dev": { - "doctrine/coding-standard": "12.0.0", - "fig/log-test": "^1", - "jetbrains/phpstorm-stubs": "2023.1", - "phpstan/phpstan": "1.10.57", - "phpstan/phpstan-strict-rules": "^1.5", - "phpunit/phpunit": "9.6.16", - "psalm/plugin-phpunit": "0.18.4", - "slevomat/coding-standard": "8.13.1", - "squizlabs/php_codesniffer": "3.8.1", - "symfony/cache": "^5.4|^6.0|^7.0", - "symfony/console": "^4.4|^5.4|^6.0|^7.0", - "vimeo/psalm": "4.30.0" - }, - "suggest": { - "symfony/console": "For helpful console commands such as SQL execution and import of files." - }, - "bin": [ - "bin/doctrine-dbal" - ], - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\DBAL\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - } - ], - "description": "Powerful PHP database abstraction layer (DBAL) with many features for database schema introspection and management.", - "homepage": "https://www.doctrine-project.org/projects/dbal.html", - "keywords": [ - "abstraction", - "database", - "db2", - "dbal", - "mariadb", - "mssql", - "mysql", - "oci8", - "oracle", - "pdo", - "pgsql", - "postgresql", - "queryobject", - "sasql", - "sql", - "sqlite", - "sqlserver", - "sqlsrv" - ], - "support": { - "issues": "https://github.com/doctrine/dbal/issues", - "source": "https://github.com/doctrine/dbal/tree/3.8.2" + "issues": "https://github.com/doctrine/inflector/issues", + "source": "https://github.com/doctrine/inflector/tree/2.1.0" }, "funding": [ { @@ -813,90 +549,40 @@ "type": "patreon" }, { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fdbal", + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finflector", "type": "tidelift" } ], - "time": "2024-02-12T18:36:36+00:00" + "time": "2025-08-10T19:31:58+00:00" }, { - "name": "doctrine/deprecations", - "version": "1.1.3", - "source": { - "type": "git", - "url": "https://github.com/doctrine/deprecations.git", - "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", - "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "require-dev": { - "doctrine/coding-standard": "^9", - "phpstan/phpstan": "1.4.10 || 1.10.15", - "phpstan/phpstan-phpunit": "^1.0", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "psalm/plugin-phpunit": "0.18.4", - "psr/log": "^1 || ^2 || ^3", - "vimeo/psalm": "4.30.0 || 5.12.0" - }, - "suggest": { - "psr/log": "Allows logging deprecations via PSR-3 logger implementation" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", - "homepage": "https://www.doctrine-project.org/", - "support": { - "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/1.1.3" - }, - "time": "2024-01-30T19:34:25+00:00" - }, - { - "name": "doctrine/event-manager", - "version": "1.2.0", + "name": "doctrine/lexer", + "version": "3.0.1", "source": { "type": "git", - "url": "https://github.com/doctrine/event-manager.git", - "reference": "95aa4cb529f1e96576f3fda9f5705ada4056a520" + "url": "https://github.com/doctrine/lexer.git", + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/event-manager/zipball/95aa4cb529f1e96576f3fda9f5705ada4056a520", - "reference": "95aa4cb529f1e96576f3fda9f5705ada4056a520", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", "shasum": "" }, "require": { - "doctrine/deprecations": "^0.5.3 || ^1", - "php": "^7.1 || ^8.0" - }, - "conflict": { - "doctrine/common": "<2.9" + "php": "^8.1" }, "require-dev": { - "doctrine/coding-standard": "^9 || ^10", - "phpstan/phpstan": "~1.4.10 || ^1.8.8", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "vimeo/psalm": "^4.24" + "doctrine/coding-standard": "^12", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^10.5", + "psalm/plugin-phpunit": "^0.18.3", + "vimeo/psalm": "^5.21" }, "type": "library", "autoload": { "psr-4": { - "Doctrine\\Common\\": "src" + "Doctrine\\Common\\Lexer\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -912,35 +598,23 @@ "name": "Roman Borschel", "email": "roman@code-factory.org" }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, { "name": "Johannes Schmitt", "email": "schmittjoh@gmail.com" - }, - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com" } ], - "description": "The Doctrine Event Manager is a simple PHP event system that was built to be used with the various Doctrine projects.", - "homepage": "https://www.doctrine-project.org/projects/event-manager.html", + "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "https://www.doctrine-project.org/projects/lexer.html", "keywords": [ - "event", - "event dispatcher", - "event manager", - "event system", - "events" + "annotations", + "docblock", + "lexer", + "parser", + "php" ], "support": { - "issues": "https://github.com/doctrine/event-manager/issues", - "source": "https://github.com/doctrine/event-manager/tree/1.2.0" + "issues": "https://github.com/doctrine/lexer/issues", + "source": "https://github.com/doctrine/lexer/tree/3.0.1" }, "funding": [ { @@ -952,255 +626,179 @@ "type": "patreon" }, { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fevent-manager", + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", "type": "tidelift" } ], - "time": "2022-10-12T20:51:15+00:00" + "time": "2024-02-05T11:56:58+00:00" }, { - "name": "doctrine/inflector", - "version": "2.0.10", + "name": "dompdf/dompdf", + "version": "v3.1.0", "source": { "type": "git", - "url": "https://github.com/doctrine/inflector.git", - "reference": "5817d0659c5b50c9b950feb9af7b9668e2c436bc" + "url": "https://github.com/dompdf/dompdf.git", + "reference": "a51bd7a063a65499446919286fb18b518177155a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/inflector/zipball/5817d0659c5b50c9b950feb9af7b9668e2c436bc", - "reference": "5817d0659c5b50c9b950feb9af7b9668e2c436bc", + "url": "https://api.github.com/repos/dompdf/dompdf/zipball/a51bd7a063a65499446919286fb18b518177155a", + "reference": "a51bd7a063a65499446919286fb18b518177155a", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0" + "dompdf/php-font-lib": "^1.0.0", + "dompdf/php-svg-lib": "^1.0.0", + "ext-dom": "*", + "ext-mbstring": "*", + "masterminds/html5": "^2.0", + "php": "^7.1 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^11.0", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-phpunit": "^1.1", - "phpstan/phpstan-strict-rules": "^1.3", - "phpunit/phpunit": "^8.5 || ^9.5", - "vimeo/psalm": "^4.25 || ^5.4" + "ext-gd": "*", + "ext-json": "*", + "ext-zip": "*", + "mockery/mockery": "^1.3", + "phpunit/phpunit": "^7.5 || ^8 || ^9 || ^10 || ^11", + "squizlabs/php_codesniffer": "^3.5", + "symfony/process": "^4.4 || ^5.4 || ^6.2 || ^7.0" + }, + "suggest": { + "ext-gd": "Needed to process images", + "ext-gmagick": "Improves image processing performance", + "ext-imagick": "Improves image processing performance", + "ext-zlib": "Needed for pdf stream compression" }, "type": "library", "autoload": { "psr-4": { - "Doctrine\\Inflector\\": "lib/Doctrine/Inflector" - } + "Dompdf\\": "src/" + }, + "classmap": [ + "lib/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "LGPL-2.1" ], "authors": [ { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" + "name": "The Dompdf Community", + "homepage": "https://github.com/dompdf/dompdf/blob/master/AUTHORS.md" } ], - "description": "PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.", - "homepage": "https://www.doctrine-project.org/projects/inflector.html", - "keywords": [ - "inflection", - "inflector", - "lowercase", - "manipulation", - "php", - "plural", - "singular", - "strings", - "uppercase", - "words" - ], + "description": "DOMPDF is a CSS 2.1 compliant HTML to PDF converter", + "homepage": "https://github.com/dompdf/dompdf", "support": { - "issues": "https://github.com/doctrine/inflector/issues", - "source": "https://github.com/doctrine/inflector/tree/2.0.10" + "issues": "https://github.com/dompdf/dompdf/issues", + "source": "https://github.com/dompdf/dompdf/tree/v3.1.0" }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finflector", - "type": "tidelift" - } - ], - "time": "2024-02-18T20:23:39+00:00" + "time": "2025-01-15T14:09:04+00:00" }, { - "name": "doctrine/lexer", - "version": "2.1.1", + "name": "dompdf/php-font-lib", + "version": "1.0.1", "source": { "type": "git", - "url": "https://github.com/doctrine/lexer.git", - "reference": "861c870e8b75f7c8f69c146c7f89cc1c0f1b49b6" + "url": "https://github.com/dompdf/php-font-lib.git", + "reference": "6137b7d4232b7f16c882c75e4ca3991dbcf6fe2d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/861c870e8b75f7c8f69c146c7f89cc1c0f1b49b6", - "reference": "861c870e8b75f7c8f69c146c7f89cc1c0f1b49b6", + "url": "https://api.github.com/repos/dompdf/php-font-lib/zipball/6137b7d4232b7f16c882c75e4ca3991dbcf6fe2d", + "reference": "6137b7d4232b7f16c882c75e4ca3991dbcf6fe2d", "shasum": "" }, "require": { - "doctrine/deprecations": "^1.0", + "ext-mbstring": "*", "php": "^7.1 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^9 || ^12", - "phpstan/phpstan": "^1.3", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6", - "psalm/plugin-phpunit": "^0.18.3", - "vimeo/psalm": "^4.11 || ^5.21" + "symfony/phpunit-bridge": "^3 || ^4 || ^5 || ^6" }, "type": "library", "autoload": { - "psr-4": { - "Doctrine\\Common\\Lexer\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", - "homepage": "https://www.doctrine-project.org/projects/lexer.html", - "keywords": [ - "annotations", - "docblock", - "lexer", - "parser", - "php" - ], - "support": { - "issues": "https://github.com/doctrine/lexer/issues", - "source": "https://github.com/doctrine/lexer/tree/2.1.1" + "psr-4": { + "FontLib\\": "src/FontLib" + } }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1-or-later" + ], + "authors": [ { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", - "type": "tidelift" + "name": "The FontLib Community", + "homepage": "https://github.com/dompdf/php-font-lib/blob/master/AUTHORS.md" } ], - "time": "2024-02-05T11:35:39+00:00" + "description": "A library to read, parse, export and make subsets of different types of font files.", + "homepage": "https://github.com/dompdf/php-font-lib", + "support": { + "issues": "https://github.com/dompdf/php-font-lib/issues", + "source": "https://github.com/dompdf/php-font-lib/tree/1.0.1" + }, + "time": "2024-12-02T14:37:59+00:00" }, { - "name": "dompdf/dompdf", - "version": "v2.0.4", + "name": "dompdf/php-svg-lib", + "version": "1.0.0", "source": { "type": "git", - "url": "https://github.com/dompdf/dompdf.git", - "reference": "093f2d9739cec57428e39ddadedfd4f3ae862c0f" + "url": "https://github.com/dompdf/php-svg-lib.git", + "reference": "eb045e518185298eb6ff8d80d0d0c6b17aecd9af" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dompdf/dompdf/zipball/093f2d9739cec57428e39ddadedfd4f3ae862c0f", - "reference": "093f2d9739cec57428e39ddadedfd4f3ae862c0f", + "url": "https://api.github.com/repos/dompdf/php-svg-lib/zipball/eb045e518185298eb6ff8d80d0d0c6b17aecd9af", + "reference": "eb045e518185298eb6ff8d80d0d0c6b17aecd9af", "shasum": "" }, "require": { - "ext-dom": "*", "ext-mbstring": "*", - "masterminds/html5": "^2.0", - "phenx/php-font-lib": ">=0.5.4 <1.0.0", - "phenx/php-svg-lib": ">=0.3.3 <1.0.0", - "php": "^7.1 || ^8.0" + "php": "^7.1 || ^8.0", + "sabberworm/php-css-parser": "^8.4" }, "require-dev": { - "ext-json": "*", - "ext-zip": "*", - "mockery/mockery": "^1.3", - "phpunit/phpunit": "^7.5 || ^8 || ^9", - "squizlabs/php_codesniffer": "^3.5" - }, - "suggest": { - "ext-gd": "Needed to process images", - "ext-gmagick": "Improves image processing performance", - "ext-imagick": "Improves image processing performance", - "ext-zlib": "Needed for pdf stream compression" + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5" }, "type": "library", "autoload": { "psr-4": { - "Dompdf\\": "src/" - }, - "classmap": [ - "lib/" - ] + "Svg\\": "src/Svg" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "LGPL-2.1" + "LGPL-3.0-or-later" ], "authors": [ { - "name": "The Dompdf Community", - "homepage": "https://github.com/dompdf/dompdf/blob/master/AUTHORS.md" + "name": "The SvgLib Community", + "homepage": "https://github.com/dompdf/php-svg-lib/blob/master/AUTHORS.md" } ], - "description": "DOMPDF is a CSS 2.1 compliant HTML to PDF converter", - "homepage": "https://github.com/dompdf/dompdf", + "description": "A library to read, parse and export to PDF SVG files.", + "homepage": "https://github.com/dompdf/php-svg-lib", "support": { - "issues": "https://github.com/dompdf/dompdf/issues", - "source": "https://github.com/dompdf/dompdf/tree/v2.0.4" + "issues": "https://github.com/dompdf/php-svg-lib/issues", + "source": "https://github.com/dompdf/php-svg-lib/tree/1.0.0" }, - "time": "2023-12-12T20:19:39+00:00" + "time": "2024-04-29T13:26:35+00:00" }, { "name": "dragonmantank/cron-expression", - "version": "v3.3.3", + "version": "v3.4.0", "source": { "type": "git", "url": "https://github.com/dragonmantank/cron-expression.git", - "reference": "adfb1f505deb6384dc8b39804c5065dd3c8c8c0a" + "reference": "8c784d071debd117328803d86b2097615b457500" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/adfb1f505deb6384dc8b39804c5065dd3c8c8c0a", - "reference": "adfb1f505deb6384dc8b39804c5065dd3c8c8c0a", + "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/8c784d071debd117328803d86b2097615b457500", + "reference": "8c784d071debd117328803d86b2097615b457500", "shasum": "" }, "require": { @@ -1213,10 +811,14 @@ "require-dev": { "phpstan/extension-installer": "^1.0", "phpstan/phpstan": "^1.0", - "phpstan/phpstan-webmozart-assert": "^1.0", "phpunit/phpunit": "^7.0|^8.0|^9.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, "autoload": { "psr-4": { "Cron\\": "src/Cron/" @@ -1240,7 +842,7 @@ ], "support": { "issues": "https://github.com/dragonmantank/cron-expression/issues", - "source": "https://github.com/dragonmantank/cron-expression/tree/v3.3.3" + "source": "https://github.com/dragonmantank/cron-expression/tree/v3.4.0" }, "funding": [ { @@ -1248,30 +850,30 @@ "type": "github" } ], - "time": "2023-08-10T19:36:49+00:00" + "time": "2024-10-09T13:47:03+00:00" }, { "name": "egulias/email-validator", - "version": "3.2.6", + "version": "4.0.4", "source": { "type": "git", "url": "https://github.com/egulias/EmailValidator.git", - "reference": "e5997fa97e8790cdae03a9cbd5e78e45e3c7bda7" + "reference": "d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/e5997fa97e8790cdae03a9cbd5e78e45e3c7bda7", - "reference": "e5997fa97e8790cdae03a9cbd5e78e45e3c7bda7", + "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa", + "reference": "d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa", "shasum": "" }, "require": { - "doctrine/lexer": "^1.2|^2", - "php": ">=7.2", - "symfony/polyfill-intl-idn": "^1.15" + "doctrine/lexer": "^2.0 || ^3.0", + "php": ">=8.1", + "symfony/polyfill-intl-idn": "^1.26" }, "require-dev": { - "phpunit/phpunit": "^8.5.8|^9.3.3", - "vimeo/psalm": "^4" + "phpunit/phpunit": "^10.2", + "vimeo/psalm": "^5.12" }, "suggest": { "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation" @@ -1279,7 +881,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0.x-dev" + "dev-master": "4.0.x-dev" } }, "autoload": { @@ -1307,7 +909,7 @@ ], "support": { "issues": "https://github.com/egulias/EmailValidator/issues", - "source": "https://github.com/egulias/EmailValidator/tree/3.2.6" + "source": "https://github.com/egulias/EmailValidator/tree/4.0.4" }, "funding": [ { @@ -1315,7 +917,70 @@ "type": "github" } ], - "time": "2023-06-01T07:04:22+00:00" + "time": "2025-03-06T22:45:56+00:00" + }, + { + "name": "firebase/php-jwt", + "version": "v6.11.1", + "source": { + "type": "git", + "url": "https://github.com/firebase/php-jwt.git", + "reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/firebase/php-jwt/zipball/d1e91ecf8c598d073d0995afa8cd5c75c6e19e66", + "reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66", + "shasum": "" + }, + "require": { + "php": "^8.0" + }, + "require-dev": { + "guzzlehttp/guzzle": "^7.4", + "phpspec/prophecy-phpunit": "^2.0", + "phpunit/phpunit": "^9.5", + "psr/cache": "^2.0||^3.0", + "psr/http-client": "^1.0", + "psr/http-factory": "^1.0" + }, + "suggest": { + "ext-sodium": "Support EdDSA (Ed25519) signatures", + "paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present" + }, + "type": "library", + "autoload": { + "psr-4": { + "Firebase\\JWT\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Neuman Vong", + "email": "neuman+pear@twilio.com", + "role": "Developer" + }, + { + "name": "Anant Narayanan", + "email": "anant@php.net", + "role": "Developer" + } + ], + "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.", + "homepage": "https://github.com/firebase/php-jwt", + "keywords": [ + "jwt", + "php" + ], + "support": { + "issues": "https://github.com/firebase/php-jwt/issues", + "source": "https://github.com/firebase/php-jwt/tree/v6.11.1" + }, + "time": "2025-04-09T20:32:01+00:00" }, { "name": "fruitcake/php-cors", @@ -1390,24 +1055,24 @@ }, { "name": "graham-campbell/result-type", - "version": "v1.1.2", + "version": "v1.1.3", "source": { "type": "git", "url": "https://github.com/GrahamCampbell/Result-Type.git", - "reference": "fbd48bce38f73f8a4ec8583362e732e4095e5862" + "reference": "3ba905c11371512af9d9bdd27d99b782216b6945" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/fbd48bce38f73f8a4ec8583362e732e4095e5862", - "reference": "fbd48bce38f73f8a4ec8583362e732e4095e5862", + "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/3ba905c11371512af9d9bdd27d99b782216b6945", + "reference": "3ba905c11371512af9d9bdd27d99b782216b6945", "shasum": "" }, "require": { "php": "^7.2.5 || ^8.0", - "phpoption/phpoption": "^1.9.2" + "phpoption/phpoption": "^1.9.3" }, "require-dev": { - "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2" + "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28" }, "type": "library", "autoload": { @@ -1436,7 +1101,7 @@ ], "support": { "issues": "https://github.com/GrahamCampbell/Result-Type/issues", - "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.2" + "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.3" }, "funding": [ { @@ -1448,26 +1113,26 @@ "type": "tidelift" } ], - "time": "2023-11-12T22:16:48+00:00" + "time": "2024-07-20T21:45:45+00:00" }, { "name": "guzzlehttp/guzzle", - "version": "7.8.1", + "version": "7.10.0", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "41042bc7ab002487b876a0683fc8dce04ddce104" + "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/41042bc7ab002487b876a0683fc8dce04ddce104", - "reference": "41042bc7ab002487b876a0683fc8dce04ddce104", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", + "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", "shasum": "" }, "require": { "ext-json": "*", - "guzzlehttp/promises": "^1.5.3 || ^2.0.1", - "guzzlehttp/psr7": "^1.9.1 || ^2.5.1", + "guzzlehttp/promises": "^2.3", + "guzzlehttp/psr7": "^2.8", "php": "^7.2.5 || ^8.0", "psr/http-client": "^1.0", "symfony/deprecation-contracts": "^2.2 || ^3.0" @@ -1478,9 +1143,9 @@ "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", "ext-curl": "*", - "php-http/client-integration-tests": "dev-master#2c025848417c1135031fdf9c728ee53d0a7ceaee as 3.0.999", + "guzzle/client-integration-tests": "3.0.2", "php-http/message-factory": "^1.1", - "phpunit/phpunit": "^8.5.36 || ^9.6.15", + "phpunit/phpunit": "^8.5.39 || ^9.6.20", "psr/log": "^1.1 || ^2.0 || ^3.0" }, "suggest": { @@ -1558,7 +1223,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.8.1" + "source": "https://github.com/guzzle/guzzle/tree/7.10.0" }, "funding": [ { @@ -1574,20 +1239,20 @@ "type": "tidelift" } ], - "time": "2023-12-03T20:35:24+00:00" + "time": "2025-08-23T22:36:01+00:00" }, { "name": "guzzlehttp/promises", - "version": "2.0.2", + "version": "2.3.0", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "bbff78d96034045e58e13dedd6ad91b5d1253223" + "reference": "481557b130ef3790cf82b713667b43030dc9c957" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/bbff78d96034045e58e13dedd6ad91b5d1253223", - "reference": "bbff78d96034045e58e13dedd6ad91b5d1253223", + "url": "https://api.github.com/repos/guzzle/promises/zipball/481557b130ef3790cf82b713667b43030dc9c957", + "reference": "481557b130ef3790cf82b713667b43030dc9c957", "shasum": "" }, "require": { @@ -1595,7 +1260,7 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.36 || ^9.6.15" + "phpunit/phpunit": "^8.5.44 || ^9.6.25" }, "type": "library", "extra": { @@ -1641,7 +1306,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/2.0.2" + "source": "https://github.com/guzzle/promises/tree/2.3.0" }, "funding": [ { @@ -1657,20 +1322,20 @@ "type": "tidelift" } ], - "time": "2023-12-03T20:19:20+00:00" + "time": "2025-08-22T14:34:08+00:00" }, { "name": "guzzlehttp/psr7", - "version": "2.6.2", + "version": "2.8.0", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "45b30f99ac27b5ca93cb4831afe16285f57b8221" + "reference": "21dc724a0583619cd1652f673303492272778051" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/45b30f99ac27b5ca93cb4831afe16285f57b8221", - "reference": "45b30f99ac27b5ca93cb4831afe16285f57b8221", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/21dc724a0583619cd1652f673303492272778051", + "reference": "21dc724a0583619cd1652f673303492272778051", "shasum": "" }, "require": { @@ -1685,8 +1350,8 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "http-interop/http-factory-tests": "^0.9", - "phpunit/phpunit": "^8.5.36 || ^9.6.15" + "http-interop/http-factory-tests": "0.9.0", + "phpunit/phpunit": "^8.5.44 || ^9.6.25" }, "suggest": { "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" @@ -1757,7 +1422,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.6.2" + "source": "https://github.com/guzzle/psr7/tree/2.8.0" }, "funding": [ { @@ -1773,20 +1438,20 @@ "type": "tidelift" } ], - "time": "2023-12-03T20:05:35+00:00" + "time": "2025-08-23T21:21:41+00:00" }, { "name": "guzzlehttp/uri-template", - "version": "v1.0.3", + "version": "v1.0.5", "source": { "type": "git", "url": "https://github.com/guzzle/uri-template.git", - "reference": "ecea8feef63bd4fef1f037ecb288386999ecc11c" + "reference": "4f4bbd4e7172148801e76e3decc1e559bdee34e1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/uri-template/zipball/ecea8feef63bd4fef1f037ecb288386999ecc11c", - "reference": "ecea8feef63bd4fef1f037ecb288386999ecc11c", + "url": "https://api.github.com/repos/guzzle/uri-template/zipball/4f4bbd4e7172148801e76e3decc1e559bdee34e1", + "reference": "4f4bbd4e7172148801e76e3decc1e559bdee34e1", "shasum": "" }, "require": { @@ -1795,7 +1460,7 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.36 || ^9.6.15", + "phpunit/phpunit": "^8.5.44 || ^9.6.25", "uri-template/tests": "1.0.0" }, "type": "library", @@ -1843,7 +1508,7 @@ ], "support": { "issues": "https://github.com/guzzle/uri-template/issues", - "source": "https://github.com/guzzle/uri-template/tree/v1.0.3" + "source": "https://github.com/guzzle/uri-template/tree/v1.0.5" }, "funding": [ { @@ -1859,53 +1524,109 @@ "type": "tidelift" } ], - "time": "2023-12-03T19:50:20+00:00" + "time": "2025-08-22T14:27:06+00:00" + }, + { + "name": "intervention/gif", + "version": "4.2.2", + "source": { + "type": "git", + "url": "https://github.com/Intervention/gif.git", + "reference": "5999eac6a39aa760fb803bc809e8909ee67b451a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Intervention/gif/zipball/5999eac6a39aa760fb803bc809e8909ee67b451a", + "reference": "5999eac6a39aa760fb803bc809e8909ee67b451a", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "phpstan/phpstan": "^2.1", + "phpunit/phpunit": "^10.0 || ^11.0 || ^12.0", + "slevomat/coding-standard": "~8.0", + "squizlabs/php_codesniffer": "^3.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "Intervention\\Gif\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Oliver Vogel", + "email": "oliver@intervention.io", + "homepage": "https://intervention.io/" + } + ], + "description": "Native PHP GIF Encoder/Decoder", + "homepage": "https://github.com/intervention/gif", + "keywords": [ + "animation", + "gd", + "gif", + "image" + ], + "support": { + "issues": "https://github.com/Intervention/gif/issues", + "source": "https://github.com/Intervention/gif/tree/4.2.2" + }, + "funding": [ + { + "url": "https://paypal.me/interventionio", + "type": "custom" + }, + { + "url": "https://github.com/Intervention", + "type": "github" + }, + { + "url": "https://ko-fi.com/interventionphp", + "type": "ko_fi" + } + ], + "time": "2025-03-29T07:46:21+00:00" }, { "name": "intervention/image", - "version": "2.7.2", + "version": "3.11.4", "source": { "type": "git", "url": "https://github.com/Intervention/image.git", - "reference": "04be355f8d6734c826045d02a1079ad658322dad" + "reference": "8c49eb21a6d2572532d1bc425964264f3e496846" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Intervention/image/zipball/04be355f8d6734c826045d02a1079ad658322dad", - "reference": "04be355f8d6734c826045d02a1079ad658322dad", + "url": "https://api.github.com/repos/Intervention/image/zipball/8c49eb21a6d2572532d1bc425964264f3e496846", + "reference": "8c49eb21a6d2572532d1bc425964264f3e496846", "shasum": "" }, "require": { - "ext-fileinfo": "*", - "guzzlehttp/psr7": "~1.1 || ^2.0", - "php": ">=5.4.0" + "ext-mbstring": "*", + "intervention/gif": "^4.2", + "php": "^8.1" }, "require-dev": { - "mockery/mockery": "~0.9.2", - "phpunit/phpunit": "^4.8 || ^5.7 || ^7.5.15" + "mockery/mockery": "^1.6", + "phpstan/phpstan": "^2.1", + "phpunit/phpunit": "^10.0 || ^11.0 || ^12.0", + "slevomat/coding-standard": "~8.0", + "squizlabs/php_codesniffer": "^3.8" }, "suggest": { - "ext-gd": "to use GD library based image processing.", - "ext-imagick": "to use Imagick based image processing.", - "intervention/imagecache": "Caching extension for the Intervention Image library" + "ext-exif": "Recommended to be able to read EXIF data properly." }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.4-dev" - }, - "laravel": { - "providers": [ - "Intervention\\Image\\ImageServiceProvider" - ], - "aliases": { - "Image": "Intervention\\Image\\Facades\\Image" - } - } - }, "autoload": { "psr-4": { - "Intervention\\Image\\": "src/Intervention/Image" + "Intervention\\Image\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -1919,19 +1640,19 @@ "homepage": "https://intervention.io/" } ], - "description": "Image handling and manipulation library with support for Laravel integration", - "homepage": "http://image.intervention.io/", + "description": "PHP image manipulation", + "homepage": "https://image.intervention.io/", "keywords": [ "gd", "image", "imagick", - "laravel", + "resize", "thumbnail", "watermark" ], "support": { "issues": "https://github.com/Intervention/image/issues", - "source": "https://github.com/Intervention/image/tree/2.7.2" + "source": "https://github.com/Intervention/image/tree/3.11.4" }, "funding": [ { @@ -1941,42 +1662,39 @@ { "url": "https://github.com/Intervention", "type": "github" + }, + { + "url": "https://ko-fi.com/interventionphp", + "type": "ko_fi" } ], - "time": "2022-05-21T17:30:32+00:00" + "time": "2025-07-30T13:13:19+00:00" }, { "name": "knplabs/knp-snappy", - "version": "v1.4.4", + "version": "v1.5.1", "source": { "type": "git", "url": "https://github.com/KnpLabs/snappy.git", - "reference": "3db13fe45d12a7bccb2b83f622e5a90f7e40b111" + "reference": "3dd138e9e47de91cd2e056c5e6e1a0dd72547ee7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/KnpLabs/snappy/zipball/3db13fe45d12a7bccb2b83f622e5a90f7e40b111", - "reference": "3db13fe45d12a7bccb2b83f622e5a90f7e40b111", + "url": "https://api.github.com/repos/KnpLabs/snappy/zipball/3dd138e9e47de91cd2e056c5e6e1a0dd72547ee7", + "reference": "3dd138e9e47de91cd2e056c5e6e1a0dd72547ee7", "shasum": "" }, "require": { - "php": ">=7.1", - "psr/log": "^1.0||^2.0||^3.0", - "symfony/process": "~3.4||~4.3||~5.0||~6.0" + "php": ">=8.1", + "psr/log": "^2.0||^3.0", + "symfony/process": "^5.0||^6.0||^7.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^2.16||^3.0", + "friendsofphp/php-cs-fixer": "^3.0", "pedrotroller/php-cs-custom-fixer": "^2.19", "phpstan/phpstan": "^1.0.0", "phpstan/phpstan-phpunit": "^1.0.0", - "phpunit/phpunit": "~7.4||~8.5" - }, - "suggest": { - "h4cc/wkhtmltoimage-amd64": "Provides wkhtmltoimage-amd64 binary for Linux-compatible machines, use version `~0.12` as dependency", - "h4cc/wkhtmltoimage-i386": "Provides wkhtmltoimage-i386 binary for Linux-compatible machines, use version `~0.12` as dependency", - "h4cc/wkhtmltopdf-amd64": "Provides wkhtmltopdf-amd64 binary for Linux-compatible machines, use version `~0.12` as dependency", - "h4cc/wkhtmltopdf-i386": "Provides wkhtmltopdf-i386 binary for Linux-compatible machines, use version `~0.12` as dependency", - "wemersonjanuario/wkhtmltopdf-windows": "Provides wkhtmltopdf executable for Windows, use version `~0.12` as dependency" + "phpunit/phpunit": "^8.5" }, "type": "library", "extra": { @@ -2015,28 +1733,29 @@ ], "support": { "issues": "https://github.com/KnpLabs/snappy/issues", - "source": "https://github.com/KnpLabs/snappy/tree/v1.4.4" + "source": "https://github.com/KnpLabs/snappy/tree/v1.5.1" }, - "time": "2023-09-13T12:18:19+00:00" + "time": "2025-01-06T16:53:26+00:00" }, { "name": "laravel/framework", - "version": "v9.52.16", + "version": "v12.26.4", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "082345d76fc6a55b649572efe10b11b03e279d24" + "reference": "085a367a32ba86fcfa647bfc796098ae6f795b09" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/082345d76fc6a55b649572efe10b11b03e279d24", - "reference": "082345d76fc6a55b649572efe10b11b03e279d24", + "url": "https://api.github.com/repos/laravel/framework/zipball/085a367a32ba86fcfa647bfc796098ae6f795b09", + "reference": "085a367a32ba86fcfa647bfc796098ae6f795b09", "shasum": "" }, "require": { - "brick/math": "^0.9.3|^0.10.2|^0.11", + "brick/math": "^0.11|^0.12|^0.13", + "composer-runtime-api": "^2.2", "doctrine/inflector": "^2.0.5", - "dragonmantank/cron-expression": "^3.3.2", + "dragonmantank/cron-expression": "^3.4", "egulias/email-validator": "^3.2.1|^4.0", "ext-ctype": "*", "ext-filter": "*", @@ -2045,39 +1764,47 @@ "ext-openssl": "*", "ext-session": "*", "ext-tokenizer": "*", - "fruitcake/php-cors": "^1.2", + "fruitcake/php-cors": "^1.3", + "guzzlehttp/guzzle": "^7.8.2", "guzzlehttp/uri-template": "^1.0", - "laravel/serializable-closure": "^1.2.2", - "league/commonmark": "^2.2.1", - "league/flysystem": "^3.8.0", - "monolog/monolog": "^2.0", - "nesbot/carbon": "^2.62.1", - "nunomaduro/termwind": "^1.13", - "php": "^8.0.2", + "laravel/prompts": "^0.3.0", + "laravel/serializable-closure": "^1.3|^2.0", + "league/commonmark": "^2.7", + "league/flysystem": "^3.25.1", + "league/flysystem-local": "^3.25.1", + "league/uri": "^7.5.1", + "monolog/monolog": "^3.0", + "nesbot/carbon": "^3.8.4", + "nunomaduro/termwind": "^2.0", + "php": "^8.2", "psr/container": "^1.1.1|^2.0.1", "psr/log": "^1.0|^2.0|^3.0", "psr/simple-cache": "^1.0|^2.0|^3.0", "ramsey/uuid": "^4.7", - "symfony/console": "^6.0.9", - "symfony/error-handler": "^6.0", - "symfony/finder": "^6.0", - "symfony/http-foundation": "^6.0", - "symfony/http-kernel": "^6.0", - "symfony/mailer": "^6.0", - "symfony/mime": "^6.0", - "symfony/process": "^6.0", - "symfony/routing": "^6.0", - "symfony/uid": "^6.0", - "symfony/var-dumper": "^6.0", + "symfony/console": "^7.2.0", + "symfony/error-handler": "^7.2.0", + "symfony/finder": "^7.2.0", + "symfony/http-foundation": "^7.2.0", + "symfony/http-kernel": "^7.2.0", + "symfony/mailer": "^7.2.0", + "symfony/mime": "^7.2.0", + "symfony/polyfill-php83": "^1.33", + "symfony/polyfill-php84": "^1.33", + "symfony/polyfill-php85": "^1.33", + "symfony/process": "^7.2.0", + "symfony/routing": "^7.2.0", + "symfony/uid": "^7.2.0", + "symfony/var-dumper": "^7.2.0", "tijsverkoyen/css-to-inline-styles": "^2.2.5", - "vlucas/phpdotenv": "^5.4.1", - "voku/portable-ascii": "^2.0" + "vlucas/phpdotenv": "^5.6.1", + "voku/portable-ascii": "^2.0.2" }, "conflict": { "tightenco/collect": "<5.5.33" }, "provide": { "psr/container-implementation": "1.1|2.0", + "psr/log-implementation": "1.0|2.0|3.0", "psr/simple-cache-implementation": "1.0|2.0|3.0" }, "replace": { @@ -2086,6 +1813,7 @@ "illuminate/bus": "self.version", "illuminate/cache": "self.version", "illuminate/collections": "self.version", + "illuminate/concurrency": "self.version", "illuminate/conditionable": "self.version", "illuminate/config": "self.version", "illuminate/console": "self.version", @@ -2104,6 +1832,7 @@ "illuminate/notifications": "self.version", "illuminate/pagination": "self.version", "illuminate/pipeline": "self.version", + "illuminate/process": "self.version", "illuminate/queue": "self.version", "illuminate/redis": "self.version", "illuminate/routing": "self.version", @@ -2112,35 +1841,39 @@ "illuminate/testing": "self.version", "illuminate/translation": "self.version", "illuminate/validation": "self.version", - "illuminate/view": "self.version" + "illuminate/view": "self.version", + "spatie/once": "*" }, "require-dev": { "ably/ably-php": "^1.0", - "aws/aws-sdk-php": "^3.235.5", - "doctrine/dbal": "^2.13.3|^3.1.4", + "aws/aws-sdk-php": "^3.322.9", "ext-gmp": "*", - "fakerphp/faker": "^1.21", - "guzzlehttp/guzzle": "^7.5", - "league/flysystem-aws-s3-v3": "^3.0", - "league/flysystem-ftp": "^3.0", - "league/flysystem-path-prefixing": "^3.3", - "league/flysystem-read-only": "^3.3", - "league/flysystem-sftp-v3": "^3.0", - "mockery/mockery": "^1.5.1", - "orchestra/testbench-core": "^7.24", - "pda/pheanstalk": "^4.0", - "phpstan/phpdoc-parser": "^1.15", - "phpstan/phpstan": "^1.4.7", - "phpunit/phpunit": "^9.5.8", - "predis/predis": "^1.1.9|^2.0.2", - "symfony/cache": "^6.0", - "symfony/http-client": "^6.0" + "fakerphp/faker": "^1.24", + "guzzlehttp/promises": "^2.0.3", + "guzzlehttp/psr7": "^2.4", + "laravel/pint": "^1.18", + "league/flysystem-aws-s3-v3": "^3.25.1", + "league/flysystem-ftp": "^3.25.1", + "league/flysystem-path-prefixing": "^3.25.1", + "league/flysystem-read-only": "^3.25.1", + "league/flysystem-sftp-v3": "^3.25.1", + "mockery/mockery": "^1.6.10", + "orchestra/testbench-core": "^10.6.3", + "pda/pheanstalk": "^5.0.6|^7.0.0", + "php-http/discovery": "^1.15", + "phpstan/phpstan": "^2.0", + "phpunit/phpunit": "^10.5.35|^11.5.3|^12.0.1", + "predis/predis": "^2.3|^3.0", + "resend/resend-php": "^0.10.0", + "symfony/cache": "^7.2.0", + "symfony/http-client": "^7.2.0", + "symfony/psr-http-message-bridge": "^7.2.0", + "symfony/translation": "^7.2.0" }, "suggest": { "ably/ably-php": "Required to use the Ably broadcast driver (^1.0).", - "aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage, and SES mail driver (^3.235.5).", - "brianium/paratest": "Required to run tests in parallel (^6.0).", - "doctrine/dbal": "Required to rename columns and drop SQLite columns (^2.13.3|^3.1.4).", + "aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage, and SES mail driver (^3.322.9).", + "brianium/paratest": "Required to run tests in parallel (^7.0|^8.0).", "ext-apcu": "Required to use the APC cache driver.", "ext-fileinfo": "Required to use the Filesystem class.", "ext-ftp": "Required to use the Flysystem FTP driver.", @@ -2149,41 +1882,45 @@ "ext-pcntl": "Required to use all features of the queue worker and console signal trapping.", "ext-pdo": "Required to use all database features.", "ext-posix": "Required to use all features of the queue worker.", - "ext-redis": "Required to use the Redis cache and queue drivers (^4.0|^5.0).", + "ext-redis": "Required to use the Redis cache and queue drivers (^4.0|^5.0|^6.0).", "fakerphp/faker": "Required to use the eloquent factory builder (^1.9.1).", "filp/whoops": "Required for friendly error pages in development (^2.14.3).", - "guzzlehttp/guzzle": "Required to use the HTTP Client and the ping methods on schedules (^7.5).", "laravel/tinker": "Required to use the tinker console command (^2.0).", - "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^3.0).", - "league/flysystem-ftp": "Required to use the Flysystem FTP driver (^3.0).", - "league/flysystem-path-prefixing": "Required to use the scoped driver (^3.3).", - "league/flysystem-read-only": "Required to use read-only disks (^3.3)", - "league/flysystem-sftp-v3": "Required to use the Flysystem SFTP driver (^3.0).", - "mockery/mockery": "Required to use mocking (^1.5.1).", - "nyholm/psr7": "Required to use PSR-7 bridging features (^1.2).", - "pda/pheanstalk": "Required to use the beanstalk queue driver (^4.0).", - "phpunit/phpunit": "Required to use assertions and run tests (^9.5.8).", - "predis/predis": "Required to use the predis connector (^1.1.9|^2.0.2).", + "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^3.25.1).", + "league/flysystem-ftp": "Required to use the Flysystem FTP driver (^3.25.1).", + "league/flysystem-path-prefixing": "Required to use the scoped driver (^3.25.1).", + "league/flysystem-read-only": "Required to use read-only disks (^3.25.1)", + "league/flysystem-sftp-v3": "Required to use the Flysystem SFTP driver (^3.25.1).", + "mockery/mockery": "Required to use mocking (^1.6).", + "pda/pheanstalk": "Required to use the beanstalk queue driver (^5.0).", + "php-http/discovery": "Required to use PSR-7 bridging features (^1.15).", + "phpunit/phpunit": "Required to use assertions and run tests (^10.5.35|^11.5.3|^12.0.1).", + "predis/predis": "Required to use the predis connector (^2.3|^3.0).", "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).", "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^6.0|^7.0).", - "symfony/cache": "Required to PSR-6 cache bridge (^6.0).", - "symfony/filesystem": "Required to enable support for relative symbolic links (^6.0).", - "symfony/http-client": "Required to enable support for the Symfony API mail transports (^6.0).", - "symfony/mailgun-mailer": "Required to enable support for the Mailgun mail transport (^6.0).", - "symfony/postmark-mailer": "Required to enable support for the Postmark mail transport (^6.0).", - "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^2.0)." + "resend/resend-php": "Required to enable support for the Resend mail transport (^0.10.0).", + "symfony/cache": "Required to PSR-6 cache bridge (^7.2).", + "symfony/filesystem": "Required to enable support for relative symbolic links (^7.2).", + "symfony/http-client": "Required to enable support for the Symfony API mail transports (^7.2).", + "symfony/mailgun-mailer": "Required to enable support for the Mailgun mail transport (^7.2).", + "symfony/postmark-mailer": "Required to enable support for the Postmark mail transport (^7.2).", + "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^7.2)." }, "type": "library", "extra": { "branch-alias": { - "dev-master": "9.x-dev" + "dev-master": "12.x-dev" } }, "autoload": { "files": [ + "src/Illuminate/Collections/functions.php", "src/Illuminate/Collections/helpers.php", "src/Illuminate/Events/functions.php", + "src/Illuminate/Filesystem/functions.php", "src/Illuminate/Foundation/helpers.php", + "src/Illuminate/Log/functions.php", + "src/Illuminate/Support/functions.php", "src/Illuminate/Support/helpers.php" ], "psr-4": { @@ -2215,35 +1952,95 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2023-10-03T13:02:30+00:00" + "time": "2025-08-29T14:15:53+00:00" + }, + { + "name": "laravel/prompts", + "version": "v0.3.6", + "source": { + "type": "git", + "url": "https://github.com/laravel/prompts.git", + "reference": "86a8b692e8661d0fb308cec64f3d176821323077" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/prompts/zipball/86a8b692e8661d0fb308cec64f3d176821323077", + "reference": "86a8b692e8661d0fb308cec64f3d176821323077", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2.2", + "ext-mbstring": "*", + "php": "^8.1", + "symfony/console": "^6.2|^7.0" + }, + "conflict": { + "illuminate/console": ">=10.17.0 <10.25.0", + "laravel/framework": ">=10.17.0 <10.25.0" + }, + "require-dev": { + "illuminate/collections": "^10.0|^11.0|^12.0", + "mockery/mockery": "^1.5", + "pestphp/pest": "^2.3|^3.4", + "phpstan/phpstan": "^1.11", + "phpstan/phpstan-mockery": "^1.1" + }, + "suggest": { + "ext-pcntl": "Required for the spinner to be animated." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "0.3.x-dev" + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Laravel\\Prompts\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "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.6" + }, + "time": "2025-07-07T14:17:42+00:00" }, { "name": "laravel/serializable-closure", - "version": "v1.3.3", + "version": "v2.0.4", "source": { "type": "git", "url": "https://github.com/laravel/serializable-closure.git", - "reference": "3dbf8a8e914634c48d389c1234552666b3d43754" + "reference": "b352cf0534aa1ae6b4d825d1e762e35d43f8a841" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/3dbf8a8e914634c48d389c1234552666b3d43754", - "reference": "3dbf8a8e914634c48d389c1234552666b3d43754", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/b352cf0534aa1ae6b4d825d1e762e35d43f8a841", + "reference": "b352cf0534aa1ae6b4d825d1e762e35d43f8a841", "shasum": "" }, "require": { - "php": "^7.3|^8.0" + "php": "^8.1" }, "require-dev": { - "nesbot/carbon": "^2.61", - "pestphp/pest": "^1.21.3", - "phpstan/phpstan": "^1.8.2", - "symfony/var-dumper": "^5.4.11" + "illuminate/support": "^10.0|^11.0|^12.0", + "nesbot/carbon": "^2.67|^3.0", + "pestphp/pest": "^2.36|^3.0", + "phpstan/phpstan": "^2.0", + "symfony/var-dumper": "^6.2.0|^7.0.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.x-dev" + "dev-master": "2.x-dev" } }, "autoload": { @@ -2275,49 +2072,51 @@ "issues": "https://github.com/laravel/serializable-closure/issues", "source": "https://github.com/laravel/serializable-closure" }, - "time": "2023-11-08T14:08:06+00:00" + "time": "2025-03-19T13:51:03+00:00" }, { "name": "laravel/socialite", - "version": "v5.12.1", + "version": "v5.23.0", "source": { "type": "git", "url": "https://github.com/laravel/socialite.git", - "reference": "7dae1b072573809f32ab6dcf4aebb57c8b3e8acf" + "reference": "e9e0fc83b9d8d71c8385a5da20e5b95ca6234cf5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/socialite/zipball/7dae1b072573809f32ab6dcf4aebb57c8b3e8acf", - "reference": "7dae1b072573809f32ab6dcf4aebb57c8b3e8acf", + "url": "https://api.github.com/repos/laravel/socialite/zipball/e9e0fc83b9d8d71c8385a5da20e5b95ca6234cf5", + "reference": "e9e0fc83b9d8d71c8385a5da20e5b95ca6234cf5", "shasum": "" }, "require": { "ext-json": "*", + "firebase/php-jwt": "^6.4", "guzzlehttp/guzzle": "^6.0|^7.0", - "illuminate/contracts": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0", - "illuminate/http": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0", - "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0", - "league/oauth1-client": "^1.10.1", - "php": "^7.2|^8.0" + "illuminate/contracts": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/http": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "league/oauth1-client": "^1.11", + "php": "^7.2|^8.0", + "phpseclib/phpseclib": "^3.0" }, "require-dev": { "mockery/mockery": "^1.0", - "orchestra/testbench": "^4.0|^5.0|^6.0|^7.0|^8.0|^9.0", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^8.0|^9.3|^10.4" + "orchestra/testbench": "^4.0|^5.0|^6.0|^7.0|^8.0|^9.0|^10.0", + "phpstan/phpstan": "^1.12.23", + "phpunit/phpunit": "^8.0|^9.3|^10.4|^11.5" }, "type": "library", "extra": { - "branch-alias": { - "dev-master": "5.x-dev" - }, "laravel": { - "providers": [ - "Laravel\\Socialite\\SocialiteServiceProvider" - ], "aliases": { "Socialite": "Laravel\\Socialite\\Facades\\Socialite" - } + }, + "providers": [ + "Laravel\\Socialite\\SocialiteServiceProvider" + ] + }, + "branch-alias": { + "dev-master": "5.x-dev" } }, "autoload": { @@ -2345,26 +2144,26 @@ "issues": "https://github.com/laravel/socialite/issues", "source": "https://github.com/laravel/socialite" }, - "time": "2024-02-16T08:58:20+00:00" + "time": "2025-07-23T14:16:08+00:00" }, { "name": "laravel/tinker", - "version": "v2.9.0", + "version": "v2.10.1", "source": { "type": "git", "url": "https://github.com/laravel/tinker.git", - "reference": "502e0fe3f0415d06d5db1f83a472f0f3b754bafe" + "reference": "22177cc71807d38f2810c6204d8f7183d88a57d3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/tinker/zipball/502e0fe3f0415d06d5db1f83a472f0f3b754bafe", - "reference": "502e0fe3f0415d06d5db1f83a472f0f3b754bafe", + "url": "https://api.github.com/repos/laravel/tinker/zipball/22177cc71807d38f2810c6204d8f7183d88a57d3", + "reference": "22177cc71807d38f2810c6204d8f7183d88a57d3", "shasum": "" }, "require": { - "illuminate/console": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0", - "illuminate/contracts": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0", - "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0", + "illuminate/console": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/contracts": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", "php": "^7.2.5|^8.0", "psy/psysh": "^0.11.1|^0.12.0", "symfony/var-dumper": "^4.3.4|^5.0|^6.0|^7.0" @@ -2372,10 +2171,10 @@ "require-dev": { "mockery/mockery": "~1.3.3|^1.4.2", "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^8.5.8|^9.3.3" + "phpunit/phpunit": "^8.5.8|^9.3.3|^10.0" }, "suggest": { - "illuminate/database": "The Illuminate Database package (^6.0|^7.0|^8.0|^9.0|^10.0|^11.0)." + "illuminate/database": "The Illuminate Database package (^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0)." }, "type": "library", "extra": { @@ -2409,22 +2208,22 @@ ], "support": { "issues": "https://github.com/laravel/tinker/issues", - "source": "https://github.com/laravel/tinker/tree/v2.9.0" + "source": "https://github.com/laravel/tinker/tree/v2.10.1" }, - "time": "2024-01-04T16:10:04+00:00" + "time": "2025-01-27T14:24:01+00:00" }, { "name": "league/commonmark", - "version": "2.4.2", + "version": "2.7.1", "source": { "type": "git", "url": "https://github.com/thephpleague/commonmark.git", - "reference": "91c24291965bd6d7c46c46a12ba7492f83b1cadf" + "reference": "10732241927d3971d28e7ea7b5712721fa2296ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/91c24291965bd6d7c46c46a12ba7492f83b1cadf", - "reference": "91c24291965bd6d7c46c46a12ba7492f83b1cadf", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/10732241927d3971d28e7ea7b5712721fa2296ca", + "reference": "10732241927d3971d28e7ea7b5712721fa2296ca", "shasum": "" }, "require": { @@ -2437,8 +2236,8 @@ }, "require-dev": { "cebe/markdown": "^1.0", - "commonmark/cmark": "0.30.3", - "commonmark/commonmark.js": "0.30.0", + "commonmark/cmark": "0.31.1", + "commonmark/commonmark.js": "0.31.1", "composer/package-versions-deprecated": "^1.8", "embed/embed": "^4.4", "erusev/parsedown": "^1.0", @@ -2449,10 +2248,11 @@ "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/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0 || ^7.0", + "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", "unleashedtech/php-coding-standard": "^3.1.1", - "vimeo/psalm": "^4.24.0 || ^5.0.0" + "vimeo/psalm": "^4.24.0 || ^5.0.0 || ^6.0.0" }, "suggest": { "symfony/yaml": "v2.3+ required if using the Front Matter extension" @@ -2460,7 +2260,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "2.5-dev" + "dev-main": "2.8-dev" } }, "autoload": { @@ -2517,7 +2317,7 @@ "type": "tidelift" } ], - "time": "2024-02-02T11:59:32+00:00" + "time": "2025-07-20T12:47:49+00:00" }, { "name": "league/config", @@ -2603,16 +2403,16 @@ }, { "name": "league/flysystem", - "version": "3.24.0", + "version": "3.30.0", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "b25a361508c407563b34fac6f64a8a17a8819675" + "reference": "2203e3151755d874bb2943649dae1eb8533ac93e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/b25a361508c407563b34fac6f64a8a17a8819675", - "reference": "b25a361508c407563b34fac6f64a8a17a8819675", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/2203e3151755d874bb2943649dae1eb8533ac93e", + "reference": "2203e3151755d874bb2943649dae1eb8533ac93e", "shasum": "" }, "require": { @@ -2636,11 +2436,14 @@ "composer/semver": "^3.0", "ext-fileinfo": "*", "ext-ftp": "*", + "ext-mongodb": "^1.3|^2", "ext-zip": "*", "friendsofphp/php-cs-fixer": "^3.5", "google/cloud-storage": "^1.23", + "guzzlehttp/psr7": "^2.6", "microsoft/azure-storage-blob": "^1.1", - "phpseclib/phpseclib": "^3.0.34", + "mongodb/mongodb": "^1.2|^2", + "phpseclib/phpseclib": "^3.0.36", "phpstan/phpstan": "^1.10", "phpunit/phpunit": "^9.5.11|^10.0", "sabre/dav": "^4.6.0" @@ -2677,32 +2480,22 @@ ], "support": { "issues": "https://github.com/thephpleague/flysystem/issues", - "source": "https://github.com/thephpleague/flysystem/tree/3.24.0" + "source": "https://github.com/thephpleague/flysystem/tree/3.30.0" }, - "funding": [ - { - "url": "https://ecologi.com/frankdejonge", - "type": "custom" - }, - { - "url": "https://github.com/frankdejonge", - "type": "github" - } - ], - "time": "2024-02-04T12:10:17+00:00" + "time": "2025-06-25T13:29:59+00:00" }, { "name": "league/flysystem-aws-s3-v3", - "version": "3.24.0", + "version": "3.29.0", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem-aws-s3-v3.git", - "reference": "809474e37b7fb1d1f8bcc0f8a98bc1cae99aa513" + "reference": "c6ff6d4606e48249b63f269eba7fabdb584e76a9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem-aws-s3-v3/zipball/809474e37b7fb1d1f8bcc0f8a98bc1cae99aa513", - "reference": "809474e37b7fb1d1f8bcc0f8a98bc1cae99aa513", + "url": "https://api.github.com/repos/thephpleague/flysystem-aws-s3-v3/zipball/c6ff6d4606e48249b63f269eba7fabdb584e76a9", + "reference": "c6ff6d4606e48249b63f269eba7fabdb584e76a9", "shasum": "" }, "require": { @@ -2742,32 +2535,22 @@ "storage" ], "support": { - "source": "https://github.com/thephpleague/flysystem-aws-s3-v3/tree/3.24.0" + "source": "https://github.com/thephpleague/flysystem-aws-s3-v3/tree/3.29.0" }, - "funding": [ - { - "url": "https://ecologi.com/frankdejonge", - "type": "custom" - }, - { - "url": "https://github.com/frankdejonge", - "type": "github" - } - ], - "time": "2024-01-26T18:43:21+00:00" + "time": "2024-08-17T13:10:48+00:00" }, { "name": "league/flysystem-local", - "version": "3.23.1", + "version": "3.30.0", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem-local.git", - "reference": "b884d2bf9b53bb4804a56d2df4902bb51e253f00" + "reference": "6691915f77c7fb69adfb87dcd550052dc184ee10" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/b884d2bf9b53bb4804a56d2df4902bb51e253f00", - "reference": "b884d2bf9b53bb4804a56d2df4902bb51e253f00", + "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/6691915f77c7fb69adfb87dcd550052dc184ee10", + "reference": "6691915f77c7fb69adfb87dcd550052dc184ee10", "shasum": "" }, "require": { @@ -2801,20 +2584,9 @@ "local" ], "support": { - "issues": "https://github.com/thephpleague/flysystem-local/issues", - "source": "https://github.com/thephpleague/flysystem-local/tree/3.23.1" - }, - "funding": [ - { - "url": "https://ecologi.com/frankdejonge", - "type": "custom" - }, - { - "url": "https://github.com/frankdejonge", - "type": "github" - } - ], - "time": "2024-01-26T18:25:23+00:00" + "source": "https://github.com/thephpleague/flysystem-local/tree/3.30.0" + }, + "time": "2025-05-21T10:34:19+00:00" }, { "name": "league/html-to-markdown", @@ -2907,16 +2679,16 @@ }, { "name": "league/mime-type-detection", - "version": "1.15.0", + "version": "1.16.0", "source": { "type": "git", "url": "https://github.com/thephpleague/mime-type-detection.git", - "reference": "ce0f4d1e8a6f4eb0ddff33f57c69c50fd09f4301" + "reference": "2d6702ff215bf922936ccc1ad31007edc76451b9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/ce0f4d1e8a6f4eb0ddff33f57c69c50fd09f4301", - "reference": "ce0f4d1e8a6f4eb0ddff33f57c69c50fd09f4301", + "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/2d6702ff215bf922936ccc1ad31007edc76451b9", + "reference": "2d6702ff215bf922936ccc1ad31007edc76451b9", "shasum": "" }, "require": { @@ -2947,7 +2719,7 @@ "description": "Mime-type detection for Flysystem", "support": { "issues": "https://github.com/thephpleague/mime-type-detection/issues", - "source": "https://github.com/thephpleague/mime-type-detection/tree/1.15.0" + "source": "https://github.com/thephpleague/mime-type-detection/tree/1.16.0" }, "funding": [ { @@ -2959,20 +2731,20 @@ "type": "tidelift" } ], - "time": "2024-01-28T23:22:08+00:00" + "time": "2024-09-21T08:32:55+00:00" }, { "name": "league/oauth1-client", - "version": "v1.10.1", + "version": "v1.11.0", "source": { "type": "git", "url": "https://github.com/thephpleague/oauth1-client.git", - "reference": "d6365b901b5c287dd41f143033315e2f777e1167" + "reference": "f9c94b088837eb1aae1ad7c4f23eb65cc6993055" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/oauth1-client/zipball/d6365b901b5c287dd41f143033315e2f777e1167", - "reference": "d6365b901b5c287dd41f143033315e2f777e1167", + "url": "https://api.github.com/repos/thephpleague/oauth1-client/zipball/f9c94b088837eb1aae1ad7c4f23eb65cc6993055", + "reference": "f9c94b088837eb1aae1ad7c4f23eb65cc6993055", "shasum": "" }, "require": { @@ -3033,41 +2805,36 @@ ], "support": { "issues": "https://github.com/thephpleague/oauth1-client/issues", - "source": "https://github.com/thephpleague/oauth1-client/tree/v1.10.1" + "source": "https://github.com/thephpleague/oauth1-client/tree/v1.11.0" }, - "time": "2022-04-15T14:02:14+00:00" + "time": "2024-12-10T19:59:05+00:00" }, { "name": "league/oauth2-client", - "version": "2.7.0", + "version": "2.8.1", "source": { "type": "git", "url": "https://github.com/thephpleague/oauth2-client.git", - "reference": "160d6274b03562ebeb55ed18399281d8118b76c8" + "reference": "9df2924ca644736c835fc60466a3a60390d334f9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/oauth2-client/zipball/160d6274b03562ebeb55ed18399281d8118b76c8", - "reference": "160d6274b03562ebeb55ed18399281d8118b76c8", + "url": "https://api.github.com/repos/thephpleague/oauth2-client/zipball/9df2924ca644736c835fc60466a3a60390d334f9", + "reference": "9df2924ca644736c835fc60466a3a60390d334f9", "shasum": "" }, "require": { - "guzzlehttp/guzzle": "^6.0 || ^7.0", - "paragonie/random_compat": "^1 || ^2 || ^9.99", - "php": "^5.6 || ^7.0 || ^8.0" + "ext-json": "*", + "guzzlehttp/guzzle": "^6.5.8 || ^7.4.5", + "php": "^7.1 || >=8.0.0 <8.5.0" }, "require-dev": { "mockery/mockery": "^1.3.5", - "php-parallel-lint/php-parallel-lint": "^1.3.1", - "phpunit/phpunit": "^5.7 || ^6.0 || ^9.5", - "squizlabs/php_codesniffer": "^2.3 || ^3.0" + "php-parallel-lint/php-parallel-lint": "^1.4", + "phpunit/phpunit": "^7 || ^8 || ^9 || ^10 || ^11", + "squizlabs/php_codesniffer": "^3.11" }, "type": "library", - "extra": { - "branch-alias": { - "dev-2.x": "2.0.x-dev" - } - }, "autoload": { "psr-4": { "League\\OAuth2\\Client\\": "src/" @@ -3103,22 +2870,196 @@ ], "support": { "issues": "https://github.com/thephpleague/oauth2-client/issues", - "source": "https://github.com/thephpleague/oauth2-client/tree/2.7.0" + "source": "https://github.com/thephpleague/oauth2-client/tree/2.8.1" + }, + "time": "2025-02-26T04:37:30+00:00" + }, + { + "name": "league/uri", + "version": "7.5.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/uri.git", + "reference": "81fb5145d2644324614cc532b28efd0215bda430" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/uri/zipball/81fb5145d2644324614cc532b28efd0215bda430", + "reference": "81fb5145d2644324614cc532b28efd0215bda430", + "shasum": "" + }, + "require": { + "league/uri-interfaces": "^7.5", + "php": "^8.1" + }, + "conflict": { + "league/uri-schemes": "^1.0" + }, + "suggest": { + "ext-bcmath": "to improve IPV4 host parsing", + "ext-fileinfo": "to create Data URI from file contennts", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "jeremykendall/php-domain-parser": "to resolve Public Suffix and Top Level Domain", + "league/uri-components": "Needed to easily manipulate URI objects components", + "php-64bit": "to improve IPV4 host parsing", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Uri\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://nyamsprod.com" + } + ], + "description": "URI manipulation library", + "homepage": "https://uri.thephpleague.com", + "keywords": [ + "data-uri", + "file-uri", + "ftp", + "hostname", + "http", + "https", + "middleware", + "parse_str", + "parse_url", + "psr-7", + "query-string", + "querystring", + "rfc3986", + "rfc3987", + "rfc6570", + "uri", + "uri-template", + "url", + "ws" + ], + "support": { + "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.5.1" + }, + "funding": [ + { + "url": "https://github.com/sponsors/nyamsprod", + "type": "github" + } + ], + "time": "2024-12-08T08:40:02+00:00" + }, + { + "name": "league/uri-interfaces", + "version": "7.5.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/uri-interfaces.git", + "reference": "08cfc6c4f3d811584fb09c37e2849e6a7f9b0742" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/08cfc6c4f3d811584fb09c37e2849e6a7f9b0742", + "reference": "08cfc6c4f3d811584fb09c37e2849e6a7f9b0742", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "php": "^8.1", + "psr/http-factory": "^1", + "psr/http-message": "^1.1 || ^2.0" + }, + "suggest": { + "ext-bcmath": "to improve IPV4 host parsing", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "php-64bit": "to improve IPV4 host parsing", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Uri\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://nyamsprod.com" + } + ], + "description": "Common interfaces and classes for URI representation and interaction", + "homepage": "https://uri.thephpleague.com", + "keywords": [ + "data-uri", + "file-uri", + "ftp", + "hostname", + "http", + "https", + "parse_str", + "parse_url", + "psr-7", + "query-string", + "querystring", + "rfc3986", + "rfc3987", + "rfc6570", + "uri", + "url", + "ws" + ], + "support": { + "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.5.0" }, - "time": "2023-04-16T18:19:15+00:00" + "funding": [ + { + "url": "https://github.com/sponsors/nyamsprod", + "type": "github" + } + ], + "time": "2024-12-08T08:18:47+00:00" }, { "name": "masterminds/html5", - "version": "2.8.1", + "version": "2.10.0", "source": { "type": "git", "url": "https://github.com/Masterminds/html5-php.git", - "reference": "f47dcf3c70c584de14f21143c55d9939631bc6cf" + "reference": "fcf91eb64359852f00d921887b219479b4f21251" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/f47dcf3c70c584de14f21143c55d9939631bc6cf", - "reference": "f47dcf3c70c584de14f21143c55d9939631bc6cf", + "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/fcf91eb64359852f00d921887b219479b4f21251", + "reference": "fcf91eb64359852f00d921887b219479b4f21251", "shasum": "" }, "require": { @@ -3126,7 +3067,7 @@ "php": ">=5.3.0" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8" + "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8 || ^9" }, "type": "library", "extra": { @@ -3170,48 +3111,49 @@ ], "support": { "issues": "https://github.com/Masterminds/html5-php/issues", - "source": "https://github.com/Masterminds/html5-php/tree/2.8.1" + "source": "https://github.com/Masterminds/html5-php/tree/2.10.0" }, - "time": "2023-05-10T11:58:31+00:00" + "time": "2025-07-25T09:04:22+00:00" }, { "name": "monolog/monolog", - "version": "2.9.2", + "version": "3.9.0", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "437cb3628f4cf6042cc10ae97fc2b8472e48ca1f" + "reference": "10d85740180ecba7896c87e06a166e0c95a0e3b6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/437cb3628f4cf6042cc10ae97fc2b8472e48ca1f", - "reference": "437cb3628f4cf6042cc10ae97fc2b8472e48ca1f", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/10d85740180ecba7896c87e06a166e0c95a0e3b6", + "reference": "10d85740180ecba7896c87e06a166e0c95a0e3b6", "shasum": "" }, "require": { - "php": ">=7.2", - "psr/log": "^1.0.1 || ^2.0 || ^3.0" + "php": ">=8.1", + "psr/log": "^2.0 || ^3.0" }, "provide": { - "psr/log-implementation": "1.0.0 || 2.0.0 || 3.0.0" + "psr/log-implementation": "3.0.0" }, "require-dev": { - "aws/aws-sdk-php": "^2.4.9 || ^3.0", + "aws/aws-sdk-php": "^3.0", "doctrine/couchdb": "~1.0@dev", "elasticsearch/elasticsearch": "^7 || ^8", "ext-json": "*", - "graylog2/gelf-php": "^1.4.2 || ^2@dev", - "guzzlehttp/guzzle": "^7.4", + "graylog2/gelf-php": "^1.4.2 || ^2.0", + "guzzlehttp/guzzle": "^7.4.5", "guzzlehttp/psr7": "^2.2", "mongodb/mongodb": "^1.8", "php-amqplib/php-amqplib": "~2.4 || ^3", - "phpspec/prophecy": "^1.15", - "phpstan/phpstan": "^0.12.91", - "phpunit/phpunit": "^8.5.14", - "predis/predis": "^1.1 || ^2.0", - "rollbar/rollbar": "^1.3 || ^2 || ^3", - "ruflin/elastica": "^7", - "swiftmailer/swiftmailer": "^5.3|^6.0", + "php-console/php-console": "^3.1.8", + "phpstan/phpstan": "^2", + "phpstan/phpstan-deprecation-rules": "^2", + "phpstan/phpstan-strict-rules": "^2", + "phpunit/phpunit": "^10.5.17 || ^11.0.7", + "predis/predis": "^1.1 || ^2", + "rollbar/rollbar": "^4.0", + "ruflin/elastica": "^7 || ^8", "symfony/mailer": "^5.4 || ^6", "symfony/mime": "^5.4 || ^6" }, @@ -3234,7 +3176,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "2.x-dev" + "dev-main": "3.x-dev" } }, "autoload": { @@ -3262,7 +3204,7 @@ ], "support": { "issues": "https://github.com/Seldaek/monolog/issues", - "source": "https://github.com/Seldaek/monolog/tree/2.9.2" + "source": "https://github.com/Seldaek/monolog/tree/3.9.0" }, "funding": [ { @@ -3274,20 +3216,20 @@ "type": "tidelift" } ], - "time": "2023-10-27T15:25:26+00:00" + "time": "2025-03-24T10:02:05+00:00" }, { "name": "mtdowling/jmespath.php", - "version": "2.7.0", + "version": "2.8.0", "source": { "type": "git", "url": "https://github.com/jmespath/jmespath.php.git", - "reference": "bbb69a935c2cbb0c03d7f481a238027430f6440b" + "reference": "a2a865e05d5f420b50cc2f85bb78d565db12a6bc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/bbb69a935c2cbb0c03d7f481a238027430f6440b", - "reference": "bbb69a935c2cbb0c03d7f481a238027430f6440b", + "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/a2a865e05d5f420b50cc2f85bb78d565db12a6bc", + "reference": "a2a865e05d5f420b50cc2f85bb78d565db12a6bc", "shasum": "" }, "require": { @@ -3304,7 +3246,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } }, "autoload": { @@ -3338,58 +3280,52 @@ ], "support": { "issues": "https://github.com/jmespath/jmespath.php/issues", - "source": "https://github.com/jmespath/jmespath.php/tree/2.7.0" + "source": "https://github.com/jmespath/jmespath.php/tree/2.8.0" }, - "time": "2023-08-25T10:54:48+00:00" + "time": "2024-09-04T18:46:31+00:00" }, { "name": "nesbot/carbon", - "version": "2.72.3", + "version": "3.10.2", "source": { "type": "git", - "url": "https://github.com/briannesbitt/Carbon.git", - "reference": "0c6fd108360c562f6e4fd1dedb8233b423e91c83" + "url": "https://github.com/CarbonPHP/carbon.git", + "reference": "76b5c07b8a9d2025ed1610e14cef1f3fd6ad2c24" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/0c6fd108360c562f6e4fd1dedb8233b423e91c83", - "reference": "0c6fd108360c562f6e4fd1dedb8233b423e91c83", + "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/76b5c07b8a9d2025ed1610e14cef1f3fd6ad2c24", + "reference": "76b5c07b8a9d2025ed1610e14cef1f3fd6ad2c24", "shasum": "" }, "require": { - "carbonphp/carbon-doctrine-types": "*", + "carbonphp/carbon-doctrine-types": "<100.0", "ext-json": "*", - "php": "^7.1.8 || ^8.0", + "php": "^8.1", "psr/clock": "^1.0", + "symfony/clock": "^6.3.12 || ^7.0", "symfony/polyfill-mbstring": "^1.0", - "symfony/polyfill-php80": "^1.16", - "symfony/translation": "^3.4 || ^4.0 || ^5.0 || ^6.0" + "symfony/translation": "^4.4.18 || ^5.2.1 || ^6.0 || ^7.0" }, "provide": { "psr/clock-implementation": "1.0" }, "require-dev": { - "doctrine/dbal": "^2.0 || ^3.1.4 || ^4.0", - "doctrine/orm": "^2.7 || ^3.0", - "friendsofphp/php-cs-fixer": "^3.0", - "kylekatarnls/multi-tester": "^2.0", - "ondrejmirtes/better-reflection": "*", - "phpmd/phpmd": "^2.9", - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^0.12.99 || ^1.7.14", - "phpunit/php-file-iterator": "^2.0.5 || ^3.0.6", - "phpunit/phpunit": "^7.5.20 || ^8.5.26 || ^9.5.20", - "squizlabs/php_codesniffer": "^3.4" + "doctrine/dbal": "^3.6.3 || ^4.0", + "doctrine/orm": "^2.15.2 || ^3.0", + "friendsofphp/php-cs-fixer": "^3.75.0", + "kylekatarnls/multi-tester": "^2.5.3", + "phpmd/phpmd": "^2.15.0", + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^2.1.17", + "phpunit/phpunit": "^10.5.46", + "squizlabs/php_codesniffer": "^3.13.0" }, "bin": [ "bin/carbon" ], "type": "library", "extra": { - "branch-alias": { - "dev-3.x": "3.x-dev", - "dev-master": "2.x-dev" - }, "laravel": { "providers": [ "Carbon\\Laravel\\ServiceProvider" @@ -3399,6 +3335,10 @@ "includes": [ "extension.neon" ] + }, + "branch-alias": { + "dev-2.x": "2.x-dev", + "dev-master": "3.x-dev" } }, "autoload": { @@ -3430,8 +3370,8 @@ ], "support": { "docs": "https://carbon.nesbot.com/docs", - "issues": "https://github.com/briannesbitt/Carbon/issues", - "source": "https://github.com/briannesbitt/Carbon" + "issues": "https://github.com/CarbonPHP/carbon/issues", + "source": "https://github.com/CarbonPHP/carbon" }, "funding": [ { @@ -3447,35 +3387,35 @@ "type": "tidelift" } ], - "time": "2024-01-25T10:35:09+00:00" + "time": "2025-08-02T09:36:06+00:00" }, { "name": "nette/schema", - "version": "v1.2.5", + "version": "v1.3.2", "source": { "type": "git", "url": "https://github.com/nette/schema.git", - "reference": "0462f0166e823aad657c9224d0f849ecac1ba10a" + "reference": "da801d52f0354f70a638673c4a0f04e16529431d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/schema/zipball/0462f0166e823aad657c9224d0f849ecac1ba10a", - "reference": "0462f0166e823aad657c9224d0f849ecac1ba10a", + "url": "https://api.github.com/repos/nette/schema/zipball/da801d52f0354f70a638673c4a0f04e16529431d", + "reference": "da801d52f0354f70a638673c4a0f04e16529431d", "shasum": "" }, "require": { - "nette/utils": "^2.5.7 || ^3.1.5 || ^4.0", - "php": "7.1 - 8.3" + "nette/utils": "^4.0", + "php": "8.1 - 8.4" }, "require-dev": { - "nette/tester": "^2.3 || ^2.4", + "nette/tester": "^2.5.2", "phpstan/phpstan-nette": "^1.0", - "tracy/tracy": "^2.7" + "tracy/tracy": "^2.8" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2-dev" + "dev-master": "1.3-dev" } }, "autoload": { @@ -3507,35 +3447,35 @@ ], "support": { "issues": "https://github.com/nette/schema/issues", - "source": "https://github.com/nette/schema/tree/v1.2.5" + "source": "https://github.com/nette/schema/tree/v1.3.2" }, - "time": "2023-10-05T20:37:59+00:00" + "time": "2024-10-06T23:10:23+00:00" }, { "name": "nette/utils", - "version": "v4.0.4", + "version": "v4.0.8", "source": { "type": "git", "url": "https://github.com/nette/utils.git", - "reference": "d3ad0aa3b9f934602cb3e3902ebccf10be34d218" + "reference": "c930ca4e3cf4f17dcfb03037703679d2396d2ede" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/utils/zipball/d3ad0aa3b9f934602cb3e3902ebccf10be34d218", - "reference": "d3ad0aa3b9f934602cb3e3902ebccf10be34d218", + "url": "https://api.github.com/repos/nette/utils/zipball/c930ca4e3cf4f17dcfb03037703679d2396d2ede", + "reference": "c930ca4e3cf4f17dcfb03037703679d2396d2ede", "shasum": "" }, "require": { - "php": ">=8.0 <8.4" + "php": "8.0 - 8.5" }, "conflict": { "nette/finder": "<3", "nette/schema": "<1.2.2" }, "require-dev": { - "jetbrains/phpstorm-attributes": "dev-master", + "jetbrains/phpstorm-attributes": "^1.2", "nette/tester": "^2.5", - "phpstan/phpstan": "^1.0", + "phpstan/phpstan-nette": "^2.0@stable", "tracy/tracy": "^2.9" }, "suggest": { @@ -3553,6 +3493,9 @@ } }, "autoload": { + "psr-4": { + "Nette\\": "src" + }, "classmap": [ "src/" ] @@ -3593,22 +3536,22 @@ ], "support": { "issues": "https://github.com/nette/utils/issues", - "source": "https://github.com/nette/utils/tree/v4.0.4" + "source": "https://github.com/nette/utils/tree/v4.0.8" }, - "time": "2024-01-17T16:50:36+00:00" + "time": "2025-08-06T21:43:34+00:00" }, { "name": "nikic/php-parser", - "version": "v5.0.1", + "version": "v5.6.1", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "2218c2252c874a4624ab2f613d86ac32d227bc69" + "reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/2218c2252c874a4624ab2f613d86ac32d227bc69", - "reference": "2218c2252c874a4624ab2f613d86ac32d227bc69", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2", + "reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2", "shasum": "" }, "require": { @@ -3619,7 +3562,7 @@ }, "require-dev": { "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" + "phpunit/phpunit": "^9.0" }, "bin": [ "bin/php-parse" @@ -3627,7 +3570,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-master": "5.x-dev" } }, "autoload": { @@ -3651,39 +3594,37 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.0.1" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.1" }, - "time": "2024-02-21T19:24:10+00:00" + "time": "2025-08-13T20:13:15+00:00" }, { "name": "nunomaduro/termwind", - "version": "v1.15.1", + "version": "v2.3.1", "source": { "type": "git", "url": "https://github.com/nunomaduro/termwind.git", - "reference": "8ab0b32c8caa4a2e09700ea32925441385e4a5dc" + "reference": "dfa08f390e509967a15c22493dc0bac5733d9123" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/8ab0b32c8caa4a2e09700ea32925441385e4a5dc", - "reference": "8ab0b32c8caa4a2e09700ea32925441385e4a5dc", + "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/dfa08f390e509967a15c22493dc0bac5733d9123", + "reference": "dfa08f390e509967a15c22493dc0bac5733d9123", "shasum": "" }, "require": { "ext-mbstring": "*", - "php": "^8.0", - "symfony/console": "^5.3.0|^6.0.0" + "php": "^8.2", + "symfony/console": "^7.2.6" }, "require-dev": { - "ergebnis/phpstan-rules": "^1.0.", - "illuminate/console": "^8.0|^9.0", - "illuminate/support": "^8.0|^9.0", - "laravel/pint": "^1.0.0", - "pestphp/pest": "^1.21.0", - "pestphp/pest-plugin-mock": "^1.0", - "phpstan/phpstan": "^1.4.6", - "phpstan/phpstan-strict-rules": "^1.1.0", - "symfony/var-dumper": "^5.2.7|^6.0.0", + "illuminate/console": "^11.44.7", + "laravel/pint": "^1.22.0", + "mockery/mockery": "^1.6.12", + "pestphp/pest": "^2.36.0 || ^3.8.2", + "phpstan/phpstan": "^1.12.25", + "phpstan/phpstan-strict-rules": "^1.6.2", + "symfony/var-dumper": "^7.2.6", "thecodingmachine/phpstan-strict-rules": "^1.0.0" }, "type": "library", @@ -3692,6 +3633,9 @@ "providers": [ "Termwind\\Laravel\\TermwindServiceProvider" ] + }, + "branch-alias": { + "dev-2.x": "2.x-dev" } }, "autoload": { @@ -3723,7 +3667,7 @@ ], "support": { "issues": "https://github.com/nunomaduro/termwind/issues", - "source": "https://github.com/nunomaduro/termwind/tree/v1.15.1" + "source": "https://github.com/nunomaduro/termwind/tree/v2.3.1" }, "funding": [ { @@ -3739,25 +3683,25 @@ "type": "github" } ], - "time": "2023-02-08T01:06:31+00:00" + "time": "2025-05-08T08:14:37+00:00" }, { "name": "onelogin/php-saml", - "version": "4.1.0", + "version": "4.3.0", "source": { "type": "git", - "url": "https://github.com/onelogin/php-saml.git", - "reference": "b22a57ebd13e838b90df5d3346090bc37056409d" + "url": "https://github.com/SAML-Toolkits/php-saml.git", + "reference": "bf5efce9f2df5d489d05e78c27003a0fc8bc50f0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/onelogin/php-saml/zipball/b22a57ebd13e838b90df5d3346090bc37056409d", - "reference": "b22a57ebd13e838b90df5d3346090bc37056409d", + "url": "https://api.github.com/repos/SAML-Toolkits/php-saml/zipball/bf5efce9f2df5d489d05e78c27003a0fc8bc50f0", + "reference": "bf5efce9f2df5d489d05e78c27003a0fc8bc50f0", "shasum": "" }, "require": { "php": ">=7.3", - "robrichards/xmlseclibs": ">=3.1.1" + "robrichards/xmlseclibs": "^3.1" }, "require-dev": { "pdepend/pdepend": "^2.8.0", @@ -3783,40 +3727,48 @@ "license": [ "MIT" ], - "description": "OneLogin PHP SAML Toolkit", - "homepage": "https://developers.onelogin.com/saml/php", + "description": "PHP SAML Toolkit", + "homepage": "https://github.com/SAML-Toolkits/php-saml", "keywords": [ + "Federation", "SAML2", - "onelogin", + "SSO", + "identity", "saml" ], "support": { - "email": "sixto.garcia@onelogin.com", - "issues": "https://github.com/onelogin/php-saml/issues", - "source": "https://github.com/onelogin/php-saml/" + "email": "sixto.martin.garcia@gmail.com", + "issues": "https://github.com/onelogin/SAML-Toolkits/issues", + "source": "https://github.com/onelogin/SAML-Toolkits/" }, - "time": "2022-07-15T20:44:36+00:00" + "funding": [ + { + "url": "https://github.com/SAML-Toolkits", + "type": "github" + } + ], + "time": "2025-05-25T14:28:00+00:00" }, { "name": "paragonie/constant_time_encoding", - "version": "v2.6.3", + "version": "v3.0.0", "source": { "type": "git", "url": "https://github.com/paragonie/constant_time_encoding.git", - "reference": "58c3f47f650c94ec05a151692652a868995d2938" + "reference": "df1e7fde177501eee2037dd159cf04f5f301a512" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/58c3f47f650c94ec05a151692652a868995d2938", - "reference": "58c3f47f650c94ec05a151692652a868995d2938", + "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/df1e7fde177501eee2037dd159cf04f5f301a512", + "reference": "df1e7fde177501eee2037dd159cf04f5f301a512", "shasum": "" }, "require": { - "php": "^7|^8" + "php": "^8" }, "require-dev": { - "phpunit/phpunit": "^6|^7|^8|^9", - "vimeo/psalm": "^1|^2|^3|^4" + "phpunit/phpunit": "^9", + "vimeo/psalm": "^4|^5" }, "type": "library", "autoload": { @@ -3862,7 +3814,7 @@ "issues": "https://github.com/paragonie/constant_time_encoding/issues", "source": "https://github.com/paragonie/constant_time_encoding" }, - "time": "2022-06-14T06:56:20+00:00" + "time": "2024-05-08T12:36:18+00:00" }, { "name": "paragonie/random_compat", @@ -3914,108 +3866,18 @@ }, "time": "2020-10-15T08:29:30+00:00" }, - { - "name": "phenx/php-font-lib", - "version": "0.5.6", - "source": { - "type": "git", - "url": "https://github.com/dompdf/php-font-lib.git", - "reference": "a1681e9793040740a405ac5b189275059e2a9863" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/dompdf/php-font-lib/zipball/a1681e9793040740a405ac5b189275059e2a9863", - "reference": "a1681e9793040740a405ac5b189275059e2a9863", - "shasum": "" - }, - "require": { - "ext-mbstring": "*" - }, - "require-dev": { - "symfony/phpunit-bridge": "^3 || ^4 || ^5 || ^6" - }, - "type": "library", - "autoload": { - "psr-4": { - "FontLib\\": "src/FontLib" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "LGPL-2.1-or-later" - ], - "authors": [ - { - "name": "Fabien Ménager", - "email": "fabien.menager@gmail.com" - } - ], - "description": "A library to read, parse, export and make subsets of different types of font files.", - "homepage": "https://github.com/PhenX/php-font-lib", - "support": { - "issues": "https://github.com/dompdf/php-font-lib/issues", - "source": "https://github.com/dompdf/php-font-lib/tree/0.5.6" - }, - "time": "2024-01-29T14:45:26+00:00" - }, - { - "name": "phenx/php-svg-lib", - "version": "0.5.2", - "source": { - "type": "git", - "url": "https://github.com/dompdf/php-svg-lib.git", - "reference": "732faa9fb4309221e2bd9b2fda5de44f947133aa" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/dompdf/php-svg-lib/zipball/732faa9fb4309221e2bd9b2fda5de44f947133aa", - "reference": "732faa9fb4309221e2bd9b2fda5de44f947133aa", - "shasum": "" - }, - "require": { - "ext-mbstring": "*", - "php": "^7.1 || ^8.0", - "sabberworm/php-css-parser": "^8.4" - }, - "require-dev": { - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "Svg\\": "src/Svg" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "LGPL-3.0" - ], - "authors": [ - { - "name": "Fabien Ménager", - "email": "fabien.menager@gmail.com" - } - ], - "description": "A library to read, parse and export to PDF SVG files.", - "homepage": "https://github.com/PhenX/php-svg-lib", - "support": { - "issues": "https://github.com/dompdf/php-svg-lib/issues", - "source": "https://github.com/dompdf/php-svg-lib/tree/0.5.2" - }, - "time": "2024-02-07T12:49:40+00:00" - }, { "name": "phpoption/phpoption", - "version": "1.9.2", + "version": "1.9.4", "source": { "type": "git", "url": "https://github.com/schmittjoh/php-option.git", - "reference": "80735db690fe4fc5c76dfa7f9b770634285fa820" + "reference": "638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/80735db690fe4fc5c76dfa7f9b770634285fa820", - "reference": "80735db690fe4fc5c76dfa7f9b770634285fa820", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d", + "reference": "638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d", "shasum": "" }, "require": { @@ -4023,13 +3885,13 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2" + "phpunit/phpunit": "^8.5.44 || ^9.6.25 || ^10.5.53 || ^11.5.34" }, "type": "library", "extra": { "bamarni-bin": { "bin-links": true, - "forward-command": true + "forward-command": false }, "branch-alias": { "dev-master": "1.9-dev" @@ -4065,7 +3927,7 @@ ], "support": { "issues": "https://github.com/schmittjoh/php-option/issues", - "source": "https://github.com/schmittjoh/php-option/tree/1.9.2" + "source": "https://github.com/schmittjoh/php-option/tree/1.9.4" }, "funding": [ { @@ -4077,24 +3939,24 @@ "type": "tidelift" } ], - "time": "2023-11-12T21:59:55+00:00" + "time": "2025-08-21T11:53:16+00:00" }, { "name": "phpseclib/phpseclib", - "version": "3.0.36", + "version": "3.0.46", "source": { "type": "git", "url": "https://github.com/phpseclib/phpseclib.git", - "reference": "c2fb5136162d4be18fdd4da9980696f3aee96d7b" + "reference": "56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/c2fb5136162d4be18fdd4da9980696f3aee96d7b", - "reference": "c2fb5136162d4be18fdd4da9980696f3aee96d7b", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6", + "reference": "56483a7de62a6c2a6635e42e93b8a9e25d4f0ec6", "shasum": "" }, "require": { - "paragonie/constant_time_encoding": "^1|^2", + "paragonie/constant_time_encoding": "^1|^2|^3", "paragonie/random_compat": "^1.4|^2.0|^9.99.99", "php": ">=5.6.1" }, @@ -4171,7 +4033,7 @@ ], "support": { "issues": "https://github.com/phpseclib/phpseclib/issues", - "source": "https://github.com/phpseclib/phpseclib/tree/3.0.36" + "source": "https://github.com/phpseclib/phpseclib/tree/3.0.46" }, "funding": [ { @@ -4187,28 +4049,28 @@ "type": "tidelift" } ], - "time": "2024-02-26T05:13:14+00:00" + "time": "2025-06-26T16:29:55+00:00" }, { "name": "pragmarx/google2fa", - "version": "v8.0.1", + "version": "v8.0.3", "source": { "type": "git", "url": "https://github.com/antonioribeiro/google2fa.git", - "reference": "80c3d801b31fe165f8fe99ea085e0a37834e1be3" + "reference": "6f8d87ebd5afbf7790bde1ffc7579c7c705e0fad" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/antonioribeiro/google2fa/zipball/80c3d801b31fe165f8fe99ea085e0a37834e1be3", - "reference": "80c3d801b31fe165f8fe99ea085e0a37834e1be3", + "url": "https://api.github.com/repos/antonioribeiro/google2fa/zipball/6f8d87ebd5afbf7790bde1ffc7579c7c705e0fad", + "reference": "6f8d87ebd5afbf7790bde1ffc7579c7c705e0fad", "shasum": "" }, "require": { - "paragonie/constant_time_encoding": "^1.0|^2.0", + "paragonie/constant_time_encoding": "^1.0|^2.0|^3.0", "php": "^7.1|^8.0" }, "require-dev": { - "phpstan/phpstan": "^0.12.18", + "phpstan/phpstan": "^1.9", "phpunit/phpunit": "^7.5.15|^8.5|^9.0" }, "type": "library", @@ -4237,97 +4099,41 @@ ], "support": { "issues": "https://github.com/antonioribeiro/google2fa/issues", - "source": "https://github.com/antonioribeiro/google2fa/tree/v8.0.1" - }, - "time": "2022-06-13T21:57:56+00:00" - }, - { - "name": "predis/predis", - "version": "v2.2.2", - "source": { - "type": "git", - "url": "https://github.com/predis/predis.git", - "reference": "b1d3255ed9ad4d7254f9f9bba386c99f4bb983d1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/predis/predis/zipball/b1d3255ed9ad4d7254f9f9bba386c99f4bb983d1", - "reference": "b1d3255ed9ad4d7254f9f9bba386c99f4bb983d1", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^3.3", - "phpstan/phpstan": "^1.9", - "phpunit/phpunit": "^8.0 || ~9.4.4" - }, - "suggest": { - "ext-relay": "Faster connection with in-memory caching (>=0.6.2)" + "source": "https://github.com/antonioribeiro/google2fa/tree/v8.0.3" }, - "type": "library", - "autoload": { - "psr-4": { - "Predis\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Till Krüss", - "homepage": "https://till.im", - "role": "Maintainer" - } - ], - "description": "A flexible and feature-complete Redis client for PHP.", - "homepage": "http://github.com/predis/predis", - "keywords": [ - "nosql", - "predis", - "redis" - ], - "support": { - "issues": "https://github.com/predis/predis/issues", - "source": "https://github.com/predis/predis/tree/v2.2.2" - }, - "funding": [ - { - "url": "https://github.com/sponsors/tillkruss", - "type": "github" - } - ], - "time": "2023-09-13T16:42:03+00:00" + "time": "2024-09-05T11:56:40+00:00" }, { - "name": "psr/cache", - "version": "3.0.0", + "name": "predis/predis", + "version": "v3.2.0", "source": { "type": "git", - "url": "https://github.com/php-fig/cache.git", - "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf" + "url": "https://github.com/predis/predis.git", + "reference": "9e9deec4dfd3ebf65d32eb368f498c646ba2ecd8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", - "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "url": "https://api.github.com/repos/predis/predis/zipball/9e9deec4dfd3ebf65d32eb368f498c646ba2ecd8", + "reference": "9e9deec4dfd3ebf65d32eb368f498c646ba2ecd8", "shasum": "" }, "require": { - "php": ">=8.0.0" + "php": "^7.2 || ^8.0", + "psr/http-message": "^1.0|^2.0" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.3", + "phpstan/phpstan": "^1.9", + "phpunit/phpcov": "^6.0 || ^8.0", + "phpunit/phpunit": "^8.0 || ~9.4.4" + }, + "suggest": { + "ext-relay": "Faster connection with in-memory caching (>=0.6.2)" }, + "type": "library", "autoload": { "psr-4": { - "Psr\\Cache\\": "src/" + "Predis\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -4336,20 +4142,29 @@ ], "authors": [ { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" + "name": "Till Krüss", + "homepage": "https://till.im", + "role": "Maintainer" } ], - "description": "Common interface for caching libraries", + "description": "A flexible and feature-complete Redis/Valkey client for PHP.", + "homepage": "http://github.com/predis/predis", "keywords": [ - "cache", - "psr", - "psr-6" + "nosql", + "predis", + "redis" ], "support": { - "source": "https://github.com/php-fig/cache/tree/3.0.0" + "issues": "https://github.com/predis/predis/issues", + "source": "https://github.com/predis/predis/tree/v3.2.0" }, - "time": "2021-02-03T23:26:27+00:00" + "funding": [ + { + "url": "https://github.com/sponsors/tillkruss", + "type": "github" + } + ], + "time": "2025-08-06T06:41:24+00:00" }, { "name": "psr/clock", @@ -4556,20 +4371,20 @@ }, { "name": "psr/http-factory", - "version": "1.0.2", + "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/php-fig/http-factory.git", - "reference": "e616d01114759c4c489f93b099585439f795fe35" + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-factory/zipball/e616d01114759c4c489f93b099585439f795fe35", - "reference": "e616d01114759c4c489f93b099585439f795fe35", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", "shasum": "" }, "require": { - "php": ">=7.0.0", + "php": ">=7.1", "psr/http-message": "^1.0 || ^2.0" }, "type": "library", @@ -4593,7 +4408,7 @@ "homepage": "https://www.php-fig.org/" } ], - "description": "Common interfaces for PSR-7 HTTP message factories", + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", "keywords": [ "factory", "http", @@ -4605,9 +4420,9 @@ "response" ], "support": { - "source": "https://github.com/php-fig/http-factory/tree/1.0.2" + "source": "https://github.com/php-fig/http-factory" }, - "time": "2023-04-10T20:10:41+00:00" + "time": "2024-04-15T12:06:14+00:00" }, { "name": "psr/http-message", @@ -4664,16 +4479,16 @@ }, { "name": "psr/log", - "version": "3.0.0", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", - "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", "shasum": "" }, "require": { @@ -4708,9 +4523,9 @@ "psr-3" ], "support": { - "source": "https://github.com/php-fig/log/tree/3.0.0" + "source": "https://github.com/php-fig/log/tree/3.0.2" }, - "time": "2021-07-14T16:46:02+00:00" + "time": "2024-09-11T13:17:53+00:00" }, { "name": "psr/simple-cache", @@ -4765,16 +4580,16 @@ }, { "name": "psy/psysh", - "version": "v0.12.0", + "version": "v0.12.10", "source": { "type": "git", "url": "https://github.com/bobthecow/psysh.git", - "reference": "750bf031a48fd07c673dbe3f11f72362ea306d0d" + "reference": "6e80abe6f2257121f1eb9a4c55bf29d921025b22" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bobthecow/psysh/zipball/750bf031a48fd07c673dbe3f11f72362ea306d0d", - "reference": "750bf031a48fd07c673dbe3f11f72362ea306d0d", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/6e80abe6f2257121f1eb9a4c55bf29d921025b22", + "reference": "6e80abe6f2257121f1eb9a4c55bf29d921025b22", "shasum": "" }, "require": { @@ -4801,12 +4616,12 @@ ], "type": "library", "extra": { - "branch-alias": { - "dev-main": "0.12.x-dev" - }, "bamarni-bin": { "bin-links": false, "forward-command": false + }, + "branch-alias": { + "dev-main": "0.12.x-dev" } }, "autoload": { @@ -4824,12 +4639,11 @@ "authors": [ { "name": "Justin Hileman", - "email": "justin@justinhileman.info", - "homepage": "http://justinhileman.com" + "email": "justin@justinhileman.info" } ], "description": "An interactive shell for modern PHP.", - "homepage": "http://psysh.org", + "homepage": "https://psysh.org", "keywords": [ "REPL", "console", @@ -4838,9 +4652,9 @@ ], "support": { "issues": "https://github.com/bobthecow/psysh/issues", - "source": "https://github.com/bobthecow/psysh/tree/v0.12.0" + "source": "https://github.com/bobthecow/psysh/tree/v0.12.10" }, - "time": "2023-12-20T15:28:09+00:00" + "time": "2025-08-04T12:39:37+00:00" }, { "name": "ralouphie/getallheaders", @@ -4888,43 +4702,39 @@ }, { "name": "ramsey/collection", - "version": "1.3.0", + "version": "2.1.1", "source": { "type": "git", "url": "https://github.com/ramsey/collection.git", - "reference": "ad7475d1c9e70b190ecffc58f2d989416af339b4" + "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/collection/zipball/ad7475d1c9e70b190ecffc58f2d989416af339b4", - "reference": "ad7475d1c9e70b190ecffc58f2d989416af339b4", + "url": "https://api.github.com/repos/ramsey/collection/zipball/344572933ad0181accbf4ba763e85a0306a8c5e2", + "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2", "shasum": "" }, "require": { - "php": "^7.4 || ^8.0", - "symfony/polyfill-php81": "^1.23" + "php": "^8.1" }, "require-dev": { "captainhook/plugin-composer": "^5.3", - "ergebnis/composer-normalize": "^2.28.3", - "fakerphp/faker": "^1.21", + "ergebnis/composer-normalize": "^2.45", + "fakerphp/faker": "^1.24", "hamcrest/hamcrest-php": "^2.0", - "jangregor/phpstan-prophecy": "^1.0", - "mockery/mockery": "^1.5", + "jangregor/phpstan-prophecy": "^2.1", + "mockery/mockery": "^1.6", "php-parallel-lint/php-console-highlighter": "^1.0", - "php-parallel-lint/php-parallel-lint": "^1.3", - "phpcsstandards/phpcsutils": "^1.0.0-rc1", - "phpspec/prophecy-phpunit": "^2.0", - "phpstan/extension-installer": "^1.2", - "phpstan/phpstan": "^1.9", - "phpstan/phpstan-mockery": "^1.1", - "phpstan/phpstan-phpunit": "^1.3", - "phpunit/phpunit": "^9.5", - "psalm/plugin-mockery": "^1.1", - "psalm/plugin-phpunit": "^0.18.4", - "ramsey/coding-standard": "^2.0.3", - "ramsey/conventional-commits": "^1.3", - "vimeo/psalm": "^5.4" + "php-parallel-lint/php-parallel-lint": "^1.4", + "phpspec/prophecy-phpunit": "^2.3", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-mockery": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^10.5", + "ramsey/coding-standard": "^2.3", + "ramsey/conventional-commits": "^1.6", + "roave/security-advisories": "dev-latest" }, "type": "library", "extra": { @@ -4962,37 +4772,26 @@ ], "support": { "issues": "https://github.com/ramsey/collection/issues", - "source": "https://github.com/ramsey/collection/tree/1.3.0" + "source": "https://github.com/ramsey/collection/tree/2.1.1" }, - "funding": [ - { - "url": "https://github.com/ramsey", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/ramsey/collection", - "type": "tidelift" - } - ], - "time": "2022-12-27T19:12:24+00:00" + "time": "2025-03-22T05:38:12+00:00" }, { "name": "ramsey/uuid", - "version": "4.7.5", + "version": "4.9.0", "source": { "type": "git", "url": "https://github.com/ramsey/uuid.git", - "reference": "5f0df49ae5ad6efb7afa69e6bfab4e5b1e080d8e" + "reference": "4e0e23cc785f0724a0e838279a9eb03f28b092a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/5f0df49ae5ad6efb7afa69e6bfab4e5b1e080d8e", - "reference": "5f0df49ae5ad6efb7afa69e6bfab4e5b1e080d8e", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/4e0e23cc785f0724a0e838279a9eb03f28b092a0", + "reference": "4e0e23cc785f0724a0e838279a9eb03f28b092a0", "shasum": "" }, "require": { - "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11", - "ext-json": "*", + "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13", "php": "^8.0", "ramsey/collection": "^1.2 || ^2.0" }, @@ -5000,26 +4799,23 @@ "rhumsaa/uuid": "self.version" }, "require-dev": { - "captainhook/captainhook": "^5.10", + "captainhook/captainhook": "^5.25", "captainhook/plugin-composer": "^5.3", - "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", - "doctrine/annotations": "^1.8", - "ergebnis/composer-normalize": "^2.15", - "mockery/mockery": "^1.3", + "dealerdirect/phpcodesniffer-composer-installer": "^1.0", + "ergebnis/composer-normalize": "^2.47", + "mockery/mockery": "^1.6", "paragonie/random-lib": "^2", - "php-mock/php-mock": "^2.2", - "php-mock/php-mock-mockery": "^1.3", - "php-parallel-lint/php-parallel-lint": "^1.1", - "phpbench/phpbench": "^1.0", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-mockery": "^1.1", - "phpstan/phpstan-phpunit": "^1.1", - "phpunit/phpunit": "^8.5 || ^9", - "ramsey/composer-repl": "^1.4", - "slevomat/coding-standard": "^8.4", - "squizlabs/php_codesniffer": "^3.5", - "vimeo/psalm": "^4.9" + "php-mock/php-mock": "^2.6", + "php-mock/php-mock-mockery": "^1.5", + "php-parallel-lint/php-parallel-lint": "^1.4.0", + "phpbench/phpbench": "^1.2.14", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-mockery": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^9.6", + "slevomat/coding-standard": "^8.18", + "squizlabs/php_codesniffer": "^3.13" }, "suggest": { "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.", @@ -5054,32 +4850,22 @@ ], "support": { "issues": "https://github.com/ramsey/uuid/issues", - "source": "https://github.com/ramsey/uuid/tree/4.7.5" + "source": "https://github.com/ramsey/uuid/tree/4.9.0" }, - "funding": [ - { - "url": "https://github.com/ramsey", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/ramsey/uuid", - "type": "tidelift" - } - ], - "time": "2023-11-08T05:53:05+00:00" + "time": "2025-06-25T14:20:11+00:00" }, { "name": "robrichards/xmlseclibs", - "version": "3.1.1", + "version": "3.1.3", "source": { "type": "git", "url": "https://github.com/robrichards/xmlseclibs.git", - "reference": "f8f19e58f26cdb42c54b214ff8a820760292f8df" + "reference": "2bdfd742624d739dfadbd415f00181b4a77aaf07" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/robrichards/xmlseclibs/zipball/f8f19e58f26cdb42c54b214ff8a820760292f8df", - "reference": "f8f19e58f26cdb42c54b214ff8a820760292f8df", + "url": "https://api.github.com/repos/robrichards/xmlseclibs/zipball/2bdfd742624d739dfadbd415f00181b4a77aaf07", + "reference": "2bdfd742624d739dfadbd415f00181b4a77aaf07", "shasum": "" }, "require": { @@ -5106,30 +4892,31 @@ ], "support": { "issues": "https://github.com/robrichards/xmlseclibs/issues", - "source": "https://github.com/robrichards/xmlseclibs/tree/3.1.1" + "source": "https://github.com/robrichards/xmlseclibs/tree/3.1.3" }, - "time": "2020-09-05T13:00:25+00:00" + "time": "2024-11-20T21:13:56+00:00" }, { "name": "sabberworm/php-css-parser", - "version": "v8.5.1", + "version": "v8.9.0", "source": { "type": "git", "url": "https://github.com/MyIntervals/PHP-CSS-Parser.git", - "reference": "4a3d572b0f8b28bb6fd016ae8bbfc445facef152" + "reference": "d8e916507b88e389e26d4ab03c904a082aa66bb9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/MyIntervals/PHP-CSS-Parser/zipball/4a3d572b0f8b28bb6fd016ae8bbfc445facef152", - "reference": "4a3d572b0f8b28bb6fd016ae8bbfc445facef152", + "url": "https://api.github.com/repos/MyIntervals/PHP-CSS-Parser/zipball/d8e916507b88e389e26d4ab03c904a082aa66bb9", + "reference": "d8e916507b88e389e26d4ab03c904a082aa66bb9", "shasum": "" }, "require": { "ext-iconv": "*", - "php": ">=5.6.20" + "php": "^5.6.20 || ^7.0.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0" }, "require-dev": { - "phpunit/phpunit": "^5.7.27" + "phpunit/phpunit": "5.7.27 || 6.5.14 || 7.5.20 || 8.5.41", + "rawr/cross-data-providers": "^2.0.0" }, "suggest": { "ext-mbstring": "for parsing UTF-8 CSS" @@ -5171,9 +4958,9 @@ ], "support": { "issues": "https://github.com/MyIntervals/PHP-CSS-Parser/issues", - "source": "https://github.com/MyIntervals/PHP-CSS-Parser/tree/v8.5.1" + "source": "https://github.com/MyIntervals/PHP-CSS-Parser/tree/v8.9.0" }, - "time": "2024-02-15T16:41:13+00:00" + "time": "2025-07-11T13:20:48+00:00" }, { "name": "socialiteproviders/discord", @@ -5268,22 +5055,22 @@ }, { "name": "socialiteproviders/manager", - "version": "v4.5.1", + "version": "v4.8.1", "source": { "type": "git", "url": "https://github.com/SocialiteProviders/Manager.git", - "reference": "a67f194f0f4c4c7616c549afc697b78df9658d44" + "reference": "8180ec14bef230ec2351cff993d5d2d7ca470ef4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/SocialiteProviders/Manager/zipball/a67f194f0f4c4c7616c549afc697b78df9658d44", - "reference": "a67f194f0f4c4c7616c549afc697b78df9658d44", + "url": "https://api.github.com/repos/SocialiteProviders/Manager/zipball/8180ec14bef230ec2351cff993d5d2d7ca470ef4", + "reference": "8180ec14bef230ec2351cff993d5d2d7ca470ef4", "shasum": "" }, "require": { - "illuminate/support": "^8.0 || ^9.0 || ^10.0 || ^11.0", - "laravel/socialite": "^5.2", - "php": "^8.0" + "illuminate/support": "^8.0 || ^9.0 || ^10.0 || ^11.0 || ^12.0", + "laravel/socialite": "^5.5", + "php": "^8.1" }, "require-dev": { "mockery/mockery": "^1.2", @@ -5338,26 +5125,26 @@ "issues": "https://github.com/socialiteproviders/manager/issues", "source": "https://github.com/socialiteproviders/manager" }, - "time": "2024-02-17T08:58:03+00:00" + "time": "2025-02-24T19:33:30+00:00" }, { "name": "socialiteproviders/microsoft-azure", - "version": "5.1.0", + "version": "5.2.0", "source": { "type": "git", "url": "https://github.com/SocialiteProviders/Microsoft-Azure.git", - "reference": "7522b27cd8518706b50e03b40a396fb0a6891feb" + "reference": "453d62c9d7e3b3b76e94c913fb46e68a33347b16" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/SocialiteProviders/Microsoft-Azure/zipball/7522b27cd8518706b50e03b40a396fb0a6891feb", - "reference": "7522b27cd8518706b50e03b40a396fb0a6891feb", + "url": "https://api.github.com/repos/SocialiteProviders/Microsoft-Azure/zipball/453d62c9d7e3b3b76e94c913fb46e68a33347b16", + "reference": "453d62c9d7e3b3b76e94c913fb46e68a33347b16", "shasum": "" }, "require": { "ext-json": "*", - "php": "^7.2 || ^8.0", - "socialiteproviders/manager": "~4.0" + "php": "^8.0", + "socialiteproviders/manager": "^4.4" }, "type": "library", "autoload": { @@ -5389,20 +5176,20 @@ "issues": "https://github.com/socialiteproviders/providers/issues", "source": "https://github.com/socialiteproviders/providers" }, - "time": "2022-03-15T21:17:43+00:00" + "time": "2024-03-15T03:02:10+00:00" }, { "name": "socialiteproviders/okta", - "version": "4.4.0", + "version": "4.5.0", "source": { "type": "git", "url": "https://github.com/SocialiteProviders/Okta.git", - "reference": "5e47cd7b4c19da94ecafbd91fa430e4151c09806" + "reference": "b8b0d5ea72325ead82481524a48576aaf39356ed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/SocialiteProviders/Okta/zipball/5e47cd7b4c19da94ecafbd91fa430e4151c09806", - "reference": "5e47cd7b4c19da94ecafbd91fa430e4151c09806", + "url": "https://api.github.com/repos/SocialiteProviders/Okta/zipball/b8b0d5ea72325ead82481524a48576aaf39356ed", + "reference": "b8b0d5ea72325ead82481524a48576aaf39356ed", "shasum": "" }, "require": { @@ -5439,26 +5226,26 @@ "issues": "https://github.com/socialiteproviders/providers/issues", "source": "https://github.com/socialiteproviders/providers" }, - "time": "2023-12-12T01:59:17+00:00" + "time": "2024-11-07T21:57:40+00:00" }, { "name": "socialiteproviders/twitch", - "version": "5.3.1", + "version": "5.4.0", "source": { "type": "git", "url": "https://github.com/SocialiteProviders/Twitch.git", - "reference": "7accf30ae7a3139b757b4ca8f34989c09a3dbee7" + "reference": "c8791b9d208195b5f02bea432de89d0e612b955d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/SocialiteProviders/Twitch/zipball/7accf30ae7a3139b757b4ca8f34989c09a3dbee7", - "reference": "7accf30ae7a3139b757b4ca8f34989c09a3dbee7", + "url": "https://api.github.com/repos/SocialiteProviders/Twitch/zipball/c8791b9d208195b5f02bea432de89d0e612b955d", + "reference": "c8791b9d208195b5f02bea432de89d0e612b955d", "shasum": "" }, "require": { "ext-json": "*", - "php": "^7.2 || ^8.0", - "socialiteproviders/manager": "~4.0" + "php": "^8.0", + "socialiteproviders/manager": "^4.4" }, "type": "library", "autoload": { @@ -5477,31 +5264,39 @@ } ], "description": "Twitch OAuth2 Provider for Laravel Socialite", + "keywords": [ + "laravel", + "oauth", + "provider", + "socialite", + "twitch" + ], "support": { - "source": "https://github.com/SocialiteProviders/Twitch/tree/5.3.1" + "docs": "https://socialiteproviders.com/twitch", + "issues": "https://github.com/socialiteproviders/providers/issues", + "source": "https://github.com/socialiteproviders/providers" }, - "time": "2020-12-01T23:10:59+00:00" + "time": "2024-04-01T01:15:35+00:00" }, { "name": "ssddanbrown/htmldiff", - "version": "v1.0.2", + "version": "v2.0.0", "source": { "type": "git", - "url": "https://github.com/ssddanbrown/HtmlDiff.git", - "reference": "58f81857c02b50b199273edb4cc339876b5a4038" + "url": "https://codeberg.org/danb/HtmlDiff", + "reference": "b7bc848b01ae6e26a3e4ad4f07cd816e6faa8fe8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ssddanbrown/HtmlDiff/zipball/58f81857c02b50b199273edb4cc339876b5a4038", - "reference": "58f81857c02b50b199273edb4cc339876b5a4038", - "shasum": "" + "url": "https://codeberg.org/api/v1/repos/danb/HtmlDiff/archive/%prettyVersion%.zip", + "reference": "b7bc848b01ae6e26a3e4ad4f07cd816e6faa8fe8" }, "require": { "ext-mbstring": "*", - "php": ">=7.2" + "php": ">=8.0" }, "require-dev": { - "phpunit/phpunit": "^8.5|^9.5.9", + "phpunit/phpunit": "^9.6.22", "vimeo/psalm": "^4.10" }, "type": "library", @@ -5517,62 +5312,53 @@ "authors": [ { "name": "Dan Brown", - "email": "ssddanbrown@googlemail.com", + "homepage": "https://danb.me", "role": "Developer" } ], "description": "HTML Content Diff Generator", - "homepage": "https://github.com/ssddanbrown/htmldiff", - "support": { - "issues": "https://github.com/ssddanbrown/HtmlDiff/issues", - "source": "https://github.com/ssddanbrown/HtmlDiff/tree/v1.0.2" - }, + "homepage": "https://codeberg.org/danb/HtmlDiff", "funding": [ { - "url": "https://github.com/ssddanbrown", + "url": "https://github.com/sponsors/ssddanbrown", "type": "github" + }, + { + "url": "https://ko-fi.com/ssddanbrown", + "type": "kofi" } ], - "time": "2022-01-24T20:12:20+00:00" + "time": "2025-07-07T11:55:59+00:00" }, { - "name": "ssddanbrown/symfony-mailer", - "version": "6.0.x-dev", + "name": "symfony/clock", + "version": "v7.3.0", "source": { "type": "git", - "url": "https://github.com/ssddanbrown/symfony-mailer.git", - "reference": "2219dcdc5f58e4f382ce8f1e6942d16982aa3012" + "url": "https://github.com/symfony/clock.git", + "reference": "b81435fbd6648ea425d1ee96a2d8e68f4ceacd24" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ssddanbrown/symfony-mailer/zipball/2219dcdc5f58e4f382ce8f1e6942d16982aa3012", - "reference": "2219dcdc5f58e4f382ce8f1e6942d16982aa3012", + "url": "https://api.github.com/repos/symfony/clock/zipball/b81435fbd6648ea425d1ee96a2d8e68f4ceacd24", + "reference": "b81435fbd6648ea425d1ee96a2d8e68f4ceacd24", "shasum": "" }, "require": { - "egulias/email-validator": "^2.1.10|^3|^4", - "php": ">=8.0.2", - "psr/event-dispatcher": "^1", - "psr/log": "^1|^2|^3", - "symfony/event-dispatcher": "^5.4|^6.0", - "symfony/mime": "^5.4|^6.0", - "symfony/service-contracts": "^1.1|^2|^3" - }, - "conflict": { - "symfony/http-kernel": "<5.4" - }, - "replace": { - "symfony/mailer": "^6.0" + "php": ">=8.2", + "psr/clock": "^1.0", + "symfony/polyfill-php83": "^1.28" }, - "require-dev": { - "symfony/http-client-contracts": "^1.1|^2|^3", - "symfony/messenger": "^5.4|^6.0" + "provide": { + "psr/clock-implementation": "1.0" }, - "default-branch": true, "type": "library", "autoload": { + "files": [ + "Resources/now.php" + ], "psr-4": { - "Symfony\\Component\\Mailer\\": "" + "Symfony\\Component\\Clock\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -5584,69 +5370,83 @@ ], "authors": [ { - "name": "Dan Brown", - "homepage": "https://danb.me" - }, - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Helps sending emails", + "description": "Decouples applications from the system clock", "homepage": "https://symfony.com", + "keywords": [ + "clock", + "psr20", + "time" + ], "support": { - "source": "https://github.com/ssddanbrown/symfony-mailer/tree/6.0" + "source": "https://github.com/symfony/clock/tree/v7.3.0" }, - "time": "2023-07-04T14:10:33+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:21:43+00:00" }, { "name": "symfony/console", - "version": "v6.0.19", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "c3ebc83d031b71c39da318ca8b7a07ecc67507ed" + "reference": "cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/c3ebc83d031b71c39da318ca8b7a07ecc67507ed", - "reference": "c3ebc83d031b71c39da318ca8b7a07ecc67507ed", + "url": "https://api.github.com/repos/symfony/console/zipball/cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7", + "reference": "cb0102a1c5ac3807cf3fdf8bea96007df7fdbea7", "shasum": "" }, "require": { - "php": ">=8.0.2", + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0", - "symfony/service-contracts": "^1.1|^2|^3", - "symfony/string": "^5.4|^6.0" + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^7.2" }, "conflict": { - "symfony/dependency-injection": "<5.4", - "symfony/dotenv": "<5.4", - "symfony/event-dispatcher": "<5.4", - "symfony/lock": "<5.4", - "symfony/process": "<5.4" + "symfony/dependency-injection": "<6.4", + "symfony/dotenv": "<6.4", + "symfony/event-dispatcher": "<6.4", + "symfony/lock": "<6.4", + "symfony/process": "<6.4" }, "provide": { "psr/log-implementation": "1.0|2.0|3.0" }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/event-dispatcher": "^5.4|^6.0", - "symfony/lock": "^5.4|^6.0", - "symfony/process": "^5.4|^6.0", - "symfony/var-dumper": "^5.4|^6.0" - }, - "suggest": { - "psr/log": "For using the console logger", - "symfony/event-dispatcher": "", - "symfony/lock": "", - "symfony/process": "" + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/lock": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/stopwatch": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -5675,12 +5475,12 @@ "homepage": "https://symfony.com", "keywords": [ "cli", - "command line", + "command-line", "console", "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.0.19" + "source": "https://github.com/symfony/console/tree/v7.3.3" }, "funding": [ { @@ -5691,29 +5491,33 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2023-01-01T08:36:10+00:00" + "time": "2025-08-25T06:35:40+00:00" }, { "name": "symfony/css-selector", - "version": "v6.0.19", + "version": "v7.3.0", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "f1d00bddb83a4cb2138564b2150001cb6ce272b1" + "reference": "601a5ce9aaad7bf10797e3663faefce9e26c24e2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/f1d00bddb83a4cb2138564b2150001cb6ce272b1", - "reference": "f1d00bddb83a4cb2138564b2150001cb6ce272b1", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/601a5ce9aaad7bf10797e3663faefce9e26c24e2", + "reference": "601a5ce9aaad7bf10797e3663faefce9e26c24e2", "shasum": "" }, "require": { - "php": ">=8.0.2" + "php": ">=8.2" }, "type": "library", "autoload": { @@ -5745,7 +5549,7 @@ "description": "Converts CSS selectors to XPath expressions", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/css-selector/tree/v6.0.19" + "source": "https://github.com/symfony/css-selector/tree/v7.3.0" }, "funding": [ { @@ -5761,33 +5565,33 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:36:10+00:00" + "time": "2024-09-25T14:21:43+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.0.2", + "version": "v3.6.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c" + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/26954b3d62a6c5fd0ea8a2a00c0353a14978d05c", - "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", "shasum": "" }, "require": { - "php": ">=8.0.2" + "php": ">=8.1" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "3.0-dev" - }, "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" } }, "autoload": { @@ -5812,7 +5616,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.0.2" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0" }, "funding": [ { @@ -5828,31 +5632,37 @@ "type": "tidelift" } ], - "time": "2022-01-02T09:55:41+00:00" + "time": "2024-09-25T14:21:43+00:00" }, { "name": "symfony/error-handler", - "version": "v6.0.19", + "version": "v7.3.2", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "c7df52182f43a68522756ac31a532dd5b1e6db67" + "reference": "0b31a944fcd8759ae294da4d2808cbc53aebd0c3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/c7df52182f43a68522756ac31a532dd5b1e6db67", - "reference": "c7df52182f43a68522756ac31a532dd5b1e6db67", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/0b31a944fcd8759ae294da4d2808cbc53aebd0c3", + "reference": "0b31a944fcd8759ae294da4d2808cbc53aebd0c3", "shasum": "" }, "require": { - "php": ">=8.0.2", + "php": ">=8.2", "psr/log": "^1|^2|^3", - "symfony/var-dumper": "^5.4|^6.0" + "symfony/var-dumper": "^6.4|^7.0" + }, + "conflict": { + "symfony/deprecation-contracts": "<2.5", + "symfony/http-kernel": "<6.4" }, "require-dev": { - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/http-kernel": "^5.4|^6.0", - "symfony/serializer": "^5.4|^6.0" + "symfony/console": "^6.4|^7.0", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/serializer": "^6.4|^7.0", + "symfony/webpack-encore-bundle": "^1.0|^2.0" }, "bin": [ "Resources/bin/patch-type-declarations" @@ -5883,7 +5693,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v6.0.19" + "source": "https://github.com/symfony/error-handler/tree/v7.3.2" }, "funding": [ { @@ -5894,33 +5704,38 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2023-01-01T08:36:10+00:00" + "time": "2025-07-07T08:17:57+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v6.0.19", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "2eaf8e63bc5b8cefabd4a800157f0d0c094f677a" + "reference": "b7dc69e71de420ac04bc9ab830cf3ffebba48191" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/2eaf8e63bc5b8cefabd4a800157f0d0c094f677a", - "reference": "2eaf8e63bc5b8cefabd4a800157f0d0c094f677a", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/b7dc69e71de420ac04bc9ab830cf3ffebba48191", + "reference": "b7dc69e71de420ac04bc9ab830cf3ffebba48191", "shasum": "" }, "require": { - "php": ">=8.0.2", - "symfony/event-dispatcher-contracts": "^2|^3" + "php": ">=8.2", + "symfony/event-dispatcher-contracts": "^2.5|^3" }, "conflict": { - "symfony/dependency-injection": "<5.4" + "symfony/dependency-injection": "<6.4", + "symfony/service-contracts": "<2.5" }, "provide": { "psr/event-dispatcher-implementation": "1.0", @@ -5928,17 +5743,13 @@ }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/error-handler": "^5.4|^6.0", - "symfony/expression-language": "^5.4|^6.0", - "symfony/http-foundation": "^5.4|^6.0", - "symfony/service-contracts": "^1.1|^2|^3", - "symfony/stopwatch": "^5.4|^6.0" - }, - "suggest": { - "symfony/dependency-injection": "", - "symfony/http-kernel": "" + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/error-handler": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/stopwatch": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -5966,7 +5777,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v6.0.19" + "source": "https://github.com/symfony/event-dispatcher/tree/v7.3.3" }, "funding": [ { @@ -5977,42 +5788,43 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2023-01-01T08:36:10+00:00" + "time": "2025-08-13T11:49:31+00:00" }, { "name": "symfony/event-dispatcher-contracts", - "version": "v3.0.2", + "version": "v3.6.0", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "7bc61cc2db649b4637d331240c5346dcc7708051" + "reference": "59eb412e93815df44f05f342958efa9f46b1e586" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/7bc61cc2db649b4637d331240c5346dcc7708051", - "reference": "7bc61cc2db649b4637d331240c5346dcc7708051", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/59eb412e93815df44f05f342958efa9f46b1e586", + "reference": "59eb412e93815df44f05f342958efa9f46b1e586", "shasum": "" }, "require": { - "php": ">=8.0.2", + "php": ">=8.1", "psr/event-dispatcher": "^1" }, - "suggest": { - "symfony/event-dispatcher-implementation": "" - }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "3.0-dev" - }, "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" } }, "autoload": { @@ -6045,7 +5857,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.0.2" + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.6.0" }, "funding": [ { @@ -6061,24 +5873,27 @@ "type": "tidelift" } ], - "time": "2022-01-02T09:55:41+00:00" + "time": "2024-09-25T14:21:43+00:00" }, { "name": "symfony/finder", - "version": "v6.0.19", + "version": "v7.3.2", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "5cc9cac6586fc0c28cd173780ca696e419fefa11" + "reference": "2a6614966ba1074fa93dae0bc804227422df4dfe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/5cc9cac6586fc0c28cd173780ca696e419fefa11", - "reference": "5cc9cac6586fc0c28cd173780ca696e419fefa11", + "url": "https://api.github.com/repos/symfony/finder/zipball/2a6614966ba1074fa93dae0bc804227422df4dfe", + "reference": "2a6614966ba1074fa93dae0bc804227422df4dfe", "shasum": "" }, "require": { - "php": ">=8.0.2" + "php": ">=8.2" + }, + "require-dev": { + "symfony/filesystem": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -6106,7 +5921,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v6.0.19" + "source": "https://github.com/symfony/finder/tree/v7.3.2" }, "funding": [ { @@ -6117,43 +5932,51 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2023-01-20T17:44:14+00:00" + "time": "2025-07-15T13:41:35+00:00" }, { "name": "symfony/http-foundation", - "version": "v6.0.20", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "e16b2676a4b3b1fa12378a20b29c364feda2a8d6" + "reference": "7475561ec27020196c49bb7c4f178d33d7d3dc00" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/e16b2676a4b3b1fa12378a20b29c364feda2a8d6", - "reference": "e16b2676a4b3b1fa12378a20b29c364feda2a8d6", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/7475561ec27020196c49bb7c4f178d33d7d3dc00", + "reference": "7475561ec27020196c49bb7c4f178d33d7d3dc00", "shasum": "" }, "require": { - "php": ">=8.0.2", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-mbstring": "~1.1" + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3.0", + "symfony/polyfill-mbstring": "~1.1", + "symfony/polyfill-php83": "^1.27" }, - "require-dev": { - "predis/predis": "~1.0", - "symfony/cache": "^5.4|^6.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/expression-language": "^5.4|^6.0", - "symfony/http-kernel": "^5.4.12|^6.0.12|^6.1.4", - "symfony/mime": "^5.4|^6.0", - "symfony/rate-limiter": "^5.2|^6.0" + "conflict": { + "doctrine/dbal": "<3.6", + "symfony/cache": "<6.4.12|>=7.0,<7.1.5" }, - "suggest": { - "symfony/mime": "To use the file extension guesser" + "require-dev": { + "doctrine/dbal": "^3.6|^4", + "predis/predis": "^1.1|^2.0", + "symfony/cache": "^6.4.12|^7.1.5", + "symfony/clock": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/mime": "^6.4|^7.0", + "symfony/rate-limiter": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -6181,7 +6004,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v6.0.20" + "source": "https://github.com/symfony/http-foundation/tree/v7.3.3" }, "funding": [ { @@ -6192,77 +6015,86 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2023-01-30T15:41:07+00:00" + "time": "2025-08-20T08:04:18+00:00" }, { "name": "symfony/http-kernel", - "version": "v6.0.20", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "6dc70833fd0ef5e861e17c7854c12d7d86679349" + "reference": "72c304de37e1a1cec6d5d12b81187ebd4850a17b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/6dc70833fd0ef5e861e17c7854c12d7d86679349", - "reference": "6dc70833fd0ef5e861e17c7854c12d7d86679349", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/72c304de37e1a1cec6d5d12b81187ebd4850a17b", + "reference": "72c304de37e1a1cec6d5d12b81187ebd4850a17b", "shasum": "" }, "require": { - "php": ">=8.0.2", + "php": ">=8.2", "psr/log": "^1|^2|^3", - "symfony/error-handler": "^5.4|^6.0", - "symfony/event-dispatcher": "^5.4|^6.0", - "symfony/http-foundation": "^5.4|^6.0", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/error-handler": "^6.4|^7.0", + "symfony/event-dispatcher": "^7.3", + "symfony/http-foundation": "^7.3", "symfony/polyfill-ctype": "^1.8" }, "conflict": { - "symfony/browser-kit": "<5.4", - "symfony/cache": "<5.4", - "symfony/config": "<5.4", - "symfony/console": "<5.4", - "symfony/dependency-injection": "<5.4", - "symfony/doctrine-bridge": "<5.4", - "symfony/form": "<5.4", - "symfony/http-client": "<5.4", - "symfony/mailer": "<5.4", - "symfony/messenger": "<5.4", - "symfony/translation": "<5.4", - "symfony/twig-bridge": "<5.4", - "symfony/validator": "<5.4", - "twig/twig": "<2.13" + "symfony/browser-kit": "<6.4", + "symfony/cache": "<6.4", + "symfony/config": "<6.4", + "symfony/console": "<6.4", + "symfony/dependency-injection": "<6.4", + "symfony/doctrine-bridge": "<6.4", + "symfony/form": "<6.4", + "symfony/http-client": "<6.4", + "symfony/http-client-contracts": "<2.5", + "symfony/mailer": "<6.4", + "symfony/messenger": "<6.4", + "symfony/translation": "<6.4", + "symfony/translation-contracts": "<2.5", + "symfony/twig-bridge": "<6.4", + "symfony/validator": "<6.4", + "symfony/var-dumper": "<6.4", + "twig/twig": "<3.12" }, "provide": { "psr/log-implementation": "1.0|2.0|3.0" }, "require-dev": { "psr/cache": "^1.0|^2.0|^3.0", - "symfony/browser-kit": "^5.4|^6.0", - "symfony/config": "^5.4|^6.0", - "symfony/console": "^5.4|^6.0", - "symfony/css-selector": "^5.4|^6.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/dom-crawler": "^5.4|^6.0", - "symfony/expression-language": "^5.4|^6.0", - "symfony/finder": "^5.4|^6.0", - "symfony/http-client-contracts": "^1.1|^2|^3", - "symfony/process": "^5.4|^6.0", - "symfony/routing": "^5.4|^6.0", - "symfony/stopwatch": "^5.4|^6.0", - "symfony/translation": "^5.4|^6.0", - "symfony/translation-contracts": "^1.1|^2|^3", - "twig/twig": "^2.13|^3.0.4" - }, - "suggest": { - "symfony/browser-kit": "", - "symfony/config": "", - "symfony/console": "", - "symfony/dependency-injection": "" + "symfony/browser-kit": "^6.4|^7.0", + "symfony/clock": "^6.4|^7.0", + "symfony/config": "^6.4|^7.0", + "symfony/console": "^6.4|^7.0", + "symfony/css-selector": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/dom-crawler": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/finder": "^6.4|^7.0", + "symfony/http-client-contracts": "^2.5|^3", + "symfony/process": "^6.4|^7.0", + "symfony/property-access": "^7.1", + "symfony/routing": "^6.4|^7.0", + "symfony/serializer": "^7.1", + "symfony/stopwatch": "^6.4|^7.0", + "symfony/translation": "^6.4|^7.0", + "symfony/translation-contracts": "^2.5|^3", + "symfony/uid": "^6.4|^7.0", + "symfony/validator": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0", + "symfony/var-exporter": "^6.4|^7.0", + "twig/twig": "^3.12" }, "type": "library", "autoload": { @@ -6290,7 +6122,91 @@ "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/v6.0.20" + "source": "https://github.com/symfony/http-kernel/tree/v7.3.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-08-29T08:23:45+00:00" + }, + { + "name": "symfony/mailer", + "version": "v7.3.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/mailer.git", + "reference": "a32f3f45f1990db8c4341d5122a7d3a381c7e575" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mailer/zipball/a32f3f45f1990db8c4341d5122a7d3a381c7e575", + "reference": "a32f3f45f1990db8c4341d5122a7d3a381c7e575", + "shasum": "" + }, + "require": { + "egulias/email-validator": "^2.1.10|^3|^4", + "php": ">=8.2", + "psr/event-dispatcher": "^1", + "psr/log": "^1|^2|^3", + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/mime": "^7.2", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/http-client-contracts": "<2.5", + "symfony/http-kernel": "<6.4", + "symfony/messenger": "<6.4", + "symfony/mime": "<6.4", + "symfony/twig-bridge": "<6.4" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/twig-bridge": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mailer\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Helps sending emails", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/mailer/tree/v7.3.3" }, "funding": [ { @@ -6301,29 +6217,33 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2023-02-01T08:22:55+00:00" + "time": "2025-08-13T11:49:31+00:00" }, { "name": "symfony/mime", - "version": "v6.0.19", + "version": "v7.3.2", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "d7052547a0070cbeadd474e172b527a00d657301" + "reference": "e0a0f859148daf1edf6c60b398eb40bfc96697d1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/d7052547a0070cbeadd474e172b527a00d657301", - "reference": "d7052547a0070cbeadd474e172b527a00d657301", + "url": "https://api.github.com/repos/symfony/mime/zipball/e0a0f859148daf1edf6c60b398eb40bfc96697d1", + "reference": "e0a0f859148daf1edf6c60b398eb40bfc96697d1", "shasum": "" }, "require": { - "php": ">=8.0.2", + "php": ">=8.2", "symfony/polyfill-intl-idn": "^1.10", "symfony/polyfill-mbstring": "^1.0" }, @@ -6331,16 +6251,18 @@ "egulias/email-validator": "~3.0.0", "phpdocumentor/reflection-docblock": "<3.2.2", "phpdocumentor/type-resolver": "<1.4.0", - "symfony/mailer": "<5.4", - "symfony/serializer": "<5.4.14|>=6.0,<6.0.14|>=6.1,<6.1.6" + "symfony/mailer": "<6.4", + "symfony/serializer": "<6.4.3|>7.0,<7.0.3" }, "require-dev": { "egulias/email-validator": "^2.1.10|^3.1|^4", + "league/html-to-markdown": "^5.0", "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/property-access": "^5.4|^6.0", - "symfony/property-info": "^5.4|^6.0", - "symfony/serializer": "^5.4.14|~6.0.14|^6.1.6" + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/property-access": "^6.4|^7.0", + "symfony/property-info": "^6.4|^7.0", + "symfony/serializer": "^6.4.3|^7.0.3" }, "type": "library", "autoload": { @@ -6372,7 +6294,7 @@ "mime-type" ], "support": { - "source": "https://github.com/symfony/mime/tree/v6.0.19" + "source": "https://github.com/symfony/mime/tree/v7.3.2" }, "funding": [ { @@ -6383,29 +6305,33 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2023-01-11T11:50:03+00:00" + "time": "2025-07-15T13:41:35+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.29.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4" + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ef4d7e442ca910c4764bce785146269b30cb5fc4", - "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-ctype": "*" @@ -6416,8 +6342,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -6451,7 +6377,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0" }, "funding": [ { @@ -6462,29 +6388,33 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.29.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f" + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/32a9da87d7b3245e09ac426c83d334ae9f06f80f", - "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70", + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "suggest": { "ext-intl": "For best performance" @@ -6492,8 +6422,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -6529,7 +6459,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.33.0" }, "funding": [ { @@ -6537,7 +6467,11 @@ "type": "custom" }, { - "url": "https://github.com/fabpot", + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", "type": "github" }, { @@ -6545,26 +6479,25 @@ "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2025-06-27T09:58:17+00:00" }, { "name": "symfony/polyfill-intl-idn", - "version": "v1.29.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "a287ed7475f85bf6f61890146edbc932c0fff919" + "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/a287ed7475f85bf6f61890146edbc932c0fff919", - "reference": "a287ed7475f85bf6f61890146edbc932c0fff919", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/9614ac4d8061dc257ecc64cba1b140873dce8ad3", + "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3", "shasum": "" }, "require": { - "php": ">=7.1", - "symfony/polyfill-intl-normalizer": "^1.10", - "symfony/polyfill-php72": "^1.10" + "php": ">=7.2", + "symfony/polyfill-intl-normalizer": "^1.10" }, "suggest": { "ext-intl": "For best performance" @@ -6572,8 +6505,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -6613,7 +6546,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.33.0" }, "funding": [ { @@ -6624,29 +6557,33 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2024-09-10T14:38:51+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.29.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "bc45c394692b948b4d383a08d7753968bed9a83d" + "reference": "3833d7255cc303546435cb650316bff708a1c75c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/bc45c394692b948b4d383a08d7753968bed9a83d", - "reference": "bc45c394692b948b4d383a08d7753968bed9a83d", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "suggest": { "ext-intl": "For best performance" @@ -6654,8 +6591,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -6694,7 +6631,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0" }, "funding": [ { @@ -6705,29 +6642,34 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.29.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec" + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec", - "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", "shasum": "" }, "require": { - "php": ">=7.1" + "ext-iconv": "*", + "php": ">=7.2" }, "provide": { "ext-mbstring": "*" @@ -6738,8 +6680,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -6774,7 +6716,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" }, "funding": [ { @@ -6785,35 +6727,39 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2024-12-23T08:48:59+00:00" }, { - "name": "symfony/polyfill-php72", - "version": "v1.29.0", + "name": "symfony/polyfill-php80", + "version": "v1.33.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "861391a8da9a04cbad2d232ddd9e4893220d6e25" + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/861391a8da9a04cbad2d232ddd9e4893220d6e25", - "reference": "861391a8da9a04cbad2d232ddd9e4893220d6e25", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608", + "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -6821,14 +6767,21 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Php72\\": "" - } + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, { "name": "Nicolas Grekas", "email": "p@tchwork.com" @@ -6838,7 +6791,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", @@ -6847,7 +6800,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php72/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0" }, "funding": [ { @@ -6858,35 +6811,39 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2025-01-02T08:10:11+00:00" }, { - "name": "symfony/polyfill-php80", - "version": "v1.29.0", + "name": "symfony/polyfill-php83", + "version": "v1.33.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b" + "url": "https://github.com/symfony/polyfill-php83.git", + "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/87b68208d5c1188808dd7839ee1e6c8ec3b02f1b", - "reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/17f6f9a6b1735c0f163024d959f700cfbc5155e5", + "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -6894,7 +6851,7 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Php80\\": "" + "Symfony\\Polyfill\\Php83\\": "" }, "classmap": [ "Resources/stubs" @@ -6906,9 +6863,85 @@ ], "authors": [ { - "name": "Ion Bazan", - "email": "ion.bazan@gmail.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.3+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php83/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-07-08T02:45:35+00:00" + }, + { + "name": "symfony/polyfill-php84", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php84.git", + "reference": "d8ced4d875142b6a7426000426b8abc631d6b191" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/d8ced4d875142b6a7426000426b8abc631d6b191", + "reference": "d8ced4d875142b6a7426000426b8abc631d6b191", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php84\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ { "name": "Nicolas Grekas", "email": "p@tchwork.com" @@ -6918,7 +6951,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "description": "Symfony polyfill backporting some PHP 8.4+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", @@ -6927,7 +6960,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-php84/tree/v1.33.0" }, "funding": [ { @@ -6938,35 +6971,39 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2025-06-24T13:30:11+00:00" }, { - "name": "symfony/polyfill-php81", - "version": "v1.29.0", + "name": "symfony/polyfill-php85", + "version": "v1.33.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php81.git", - "reference": "c565ad1e63f30e7477fc40738343c62b40bc672d" + "url": "https://github.com/symfony/polyfill-php85.git", + "reference": "d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/c565ad1e63f30e7477fc40738343c62b40bc672d", - "reference": "c565ad1e63f30e7477fc40738343c62b40bc672d", + "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91", + "reference": "d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -6974,7 +7011,7 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Php81\\": "" + "Symfony\\Polyfill\\Php85\\": "" }, "classmap": [ "Resources/stubs" @@ -6994,7 +7031,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", + "description": "Symfony polyfill backporting some PHP 8.5+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", @@ -7003,7 +7040,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php81/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-php85/tree/v1.33.0" }, "funding": [ { @@ -7014,29 +7051,33 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2025-06-23T16:12:55+00:00" }, { "name": "symfony/polyfill-uuid", - "version": "v1.29.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-uuid.git", - "reference": "3abdd21b0ceaa3000ee950097bc3cf9efc137853" + "reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/3abdd21b0ceaa3000ee950097bc3cf9efc137853", - "reference": "3abdd21b0ceaa3000ee950097bc3cf9efc137853", + "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/21533be36c24be3f4b1669c4725c7d1d2bab4ae2", + "reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-uuid": "*" @@ -7047,8 +7088,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -7082,7 +7123,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/polyfill-uuid/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-uuid/tree/v1.33.0" }, "funding": [ { @@ -7093,29 +7134,33 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/process", - "version": "v6.0.19", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "2114fd60f26a296cc403a7939ab91478475a33d4" + "reference": "32241012d521e2e8a9d713adb0812bb773b907f1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/2114fd60f26a296cc403a7939ab91478475a33d4", - "reference": "2114fd60f26a296cc403a7939ab91478475a33d4", + "url": "https://api.github.com/repos/symfony/process/zipball/32241012d521e2e8a9d713adb0812bb773b907f1", + "reference": "32241012d521e2e8a9d713adb0812bb773b907f1", "shasum": "" }, "require": { - "php": ">=8.0.2" + "php": ">=8.2" }, "type": "library", "autoload": { @@ -7143,7 +7188,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v6.0.19" + "source": "https://github.com/symfony/process/tree/v7.3.3" }, "funding": [ { @@ -7154,50 +7199,47 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2023-01-01T08:36:10+00:00" + "time": "2025-08-18T09:42:54+00:00" }, { "name": "symfony/routing", - "version": "v6.0.19", + "version": "v7.3.2", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "e56ca9b41c1ec447193474cd86ad7c0b547755ac" + "reference": "7614b8ca5fa89b9cd233e21b627bfc5774f586e4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/e56ca9b41c1ec447193474cd86ad7c0b547755ac", - "reference": "e56ca9b41c1ec447193474cd86ad7c0b547755ac", + "url": "https://api.github.com/repos/symfony/routing/zipball/7614b8ca5fa89b9cd233e21b627bfc5774f586e4", + "reference": "7614b8ca5fa89b9cd233e21b627bfc5774f586e4", "shasum": "" }, "require": { - "php": ">=8.0.2" + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3" }, "conflict": { - "doctrine/annotations": "<1.12", - "symfony/config": "<5.4", - "symfony/dependency-injection": "<5.4", - "symfony/yaml": "<5.4" + "symfony/config": "<6.4", + "symfony/dependency-injection": "<6.4", + "symfony/yaml": "<6.4" }, "require-dev": { - "doctrine/annotations": "^1.12|^2", "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/expression-language": "^5.4|^6.0", - "symfony/http-foundation": "^5.4|^6.0", - "symfony/yaml": "^5.4|^6.0" - }, - "suggest": { - "symfony/config": "For using the all-in-one router or any loader", - "symfony/expression-language": "For using expression matching", - "symfony/http-foundation": "For using a Symfony Request object", - "symfony/yaml": "For using the YAML loader" + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/yaml": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -7231,7 +7273,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v6.0.19" + "source": "https://github.com/symfony/routing/tree/v7.3.2" }, "funding": [ { @@ -7242,51 +7284,56 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2023-01-01T08:36:10+00:00" + "time": "2025-07-15T11:36:08+00:00" }, { "name": "symfony/service-contracts", - "version": "v3.0.2", + "version": "v3.6.0", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "d78d39c1599bd1188b8e26bb341da52c3c6d8a66" + "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/d78d39c1599bd1188b8e26bb341da52c3c6d8a66", - "reference": "d78d39c1599bd1188b8e26bb341da52c3c6d8a66", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f021b05a130d35510bd6b25fe9053c2a8a15d5d4", + "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4", "shasum": "" }, "require": { - "php": ">=8.0.2", - "psr/container": "^2.0" + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" }, "conflict": { "ext-psr": "<1.1|>=2" }, - "suggest": { - "symfony/service-implementation": "" - }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "3.0-dev" - }, "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" } }, "autoload": { "psr-4": { "Symfony\\Contracts\\Service\\": "" - } + }, + "exclude-from-classmap": [ + "/Test/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -7313,7 +7360,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.0.2" + "source": "https://github.com/symfony/service-contracts/tree/v3.6.0" }, "funding": [ { @@ -7329,37 +7376,39 @@ "type": "tidelift" } ], - "time": "2022-05-30T19:17:58+00:00" + "time": "2025-04-25T09:37:31+00:00" }, { "name": "symfony/string", - "version": "v6.0.19", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "d9e72497367c23e08bf94176d2be45b00a9d232a" + "reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/d9e72497367c23e08bf94176d2be45b00a9d232a", - "reference": "d9e72497367c23e08bf94176d2be45b00a9d232a", + "url": "https://api.github.com/repos/symfony/string/zipball/17a426cce5fd1f0901fefa9b2a490d0038fd3c9c", + "reference": "17a426cce5fd1f0901fefa9b2a490d0038fd3c9c", "shasum": "" }, "require": { - "php": ">=8.0.2", + "php": ">=8.2", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-intl-grapheme": "~1.0", "symfony/polyfill-intl-normalizer": "~1.0", "symfony/polyfill-mbstring": "~1.0" }, "conflict": { - "symfony/translation-contracts": "<2.0" + "symfony/translation-contracts": "<2.5" }, "require-dev": { - "symfony/error-handler": "^5.4|^6.0", - "symfony/http-client": "^5.4|^6.0", - "symfony/translation-contracts": "^2.0|^3.0", - "symfony/var-exporter": "^5.4|^6.0" + "symfony/emoji": "^7.1", + "symfony/error-handler": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/intl": "^6.4|^7.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -7398,7 +7447,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.0.19" + "source": "https://github.com/symfony/string/tree/v7.3.3" }, "funding": [ { @@ -7409,60 +7458,65 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2023-01-01T08:36:10+00:00" + "time": "2025-08-25T06:35:40+00:00" }, { "name": "symfony/translation", - "version": "v6.0.19", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "9c24b3fdbbe9fb2ef3a6afd8bbaadfd72dad681f" + "reference": "e0837b4cbcef63c754d89a4806575cada743a38d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/9c24b3fdbbe9fb2ef3a6afd8bbaadfd72dad681f", - "reference": "9c24b3fdbbe9fb2ef3a6afd8bbaadfd72dad681f", + "url": "https://api.github.com/repos/symfony/translation/zipball/e0837b4cbcef63c754d89a4806575cada743a38d", + "reference": "e0837b4cbcef63c754d89a4806575cada743a38d", "shasum": "" }, "require": { - "php": ">=8.0.2", + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0", - "symfony/translation-contracts": "^2.3|^3.0" + "symfony/translation-contracts": "^2.5|^3.0" }, "conflict": { - "symfony/config": "<5.4", - "symfony/console": "<5.4", - "symfony/dependency-injection": "<5.4", - "symfony/http-kernel": "<5.4", - "symfony/twig-bundle": "<5.4", - "symfony/yaml": "<5.4" + "nikic/php-parser": "<5.0", + "symfony/config": "<6.4", + "symfony/console": "<6.4", + "symfony/dependency-injection": "<6.4", + "symfony/http-client-contracts": "<2.5", + "symfony/http-kernel": "<6.4", + "symfony/service-contracts": "<2.5", + "symfony/twig-bundle": "<6.4", + "symfony/yaml": "<6.4" }, "provide": { "symfony/translation-implementation": "2.3|3.0" }, "require-dev": { + "nikic/php-parser": "^5.0", "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0", - "symfony/console": "^5.4|^6.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/finder": "^5.4|^6.0", - "symfony/http-client-contracts": "^1.1|^2.0|^3.0", - "symfony/http-kernel": "^5.4|^6.0", - "symfony/intl": "^5.4|^6.0", + "symfony/config": "^6.4|^7.0", + "symfony/console": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/finder": "^6.4|^7.0", + "symfony/http-client-contracts": "^2.5|^3.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/intl": "^6.4|^7.0", "symfony/polyfill-intl-icu": "^1.21", - "symfony/service-contracts": "^1.1.2|^2|^3", - "symfony/yaml": "^5.4|^6.0" - }, - "suggest": { - "psr/log-implementation": "To use logging capability in translator", - "symfony/config": "", - "symfony/yaml": "" + "symfony/routing": "^6.4|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/yaml": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -7493,7 +7547,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v6.0.19" + "source": "https://github.com/symfony/translation/tree/v7.3.3" }, "funding": [ { @@ -7504,47 +7558,51 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2023-01-01T08:36:10+00:00" + "time": "2025-08-01T21:02:37+00:00" }, { "name": "symfony/translation-contracts", - "version": "v3.0.2", + "version": "v3.6.0", "source": { "type": "git", "url": "https://github.com/symfony/translation-contracts.git", - "reference": "acbfbb274e730e5a0236f619b6168d9dedb3e282" + "reference": "df210c7a2573f1913b2d17cc95f90f53a73d8f7d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/acbfbb274e730e5a0236f619b6168d9dedb3e282", - "reference": "acbfbb274e730e5a0236f619b6168d9dedb3e282", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/df210c7a2573f1913b2d17cc95f90f53a73d8f7d", + "reference": "df210c7a2573f1913b2d17cc95f90f53a73d8f7d", "shasum": "" }, "require": { - "php": ">=8.0.2" - }, - "suggest": { - "symfony/translation-implementation": "" + "php": ">=8.1" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "3.0-dev" - }, "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" } }, "autoload": { "psr-4": { "Symfony\\Contracts\\Translation\\": "" - } + }, + "exclude-from-classmap": [ + "/Test/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -7571,7 +7629,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/translation-contracts/tree/v3.0.2" + "source": "https://github.com/symfony/translation-contracts/tree/v3.6.0" }, "funding": [ { @@ -7587,28 +7645,28 @@ "type": "tidelift" } ], - "time": "2022-06-27T17:10:44+00:00" + "time": "2024-09-27T08:32:26+00:00" }, { "name": "symfony/uid", - "version": "v6.0.19", + "version": "v7.3.1", "source": { "type": "git", "url": "https://github.com/symfony/uid.git", - "reference": "6499e28b0ac9f2aa3151e11845bdb5cd21e6bb9d" + "reference": "a69f69f3159b852651a6bf45a9fdd149520525bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/uid/zipball/6499e28b0ac9f2aa3151e11845bdb5cd21e6bb9d", - "reference": "6499e28b0ac9f2aa3151e11845bdb5cd21e6bb9d", + "url": "https://api.github.com/repos/symfony/uid/zipball/a69f69f3159b852651a6bf45a9fdd149520525bb", + "reference": "a69f69f3159b852651a6bf45a9fdd149520525bb", "shasum": "" }, "require": { - "php": ">=8.0.2", + "php": ">=8.2", "symfony/polyfill-uuid": "^1.15" }, "require-dev": { - "symfony/console": "^5.4|^6.0" + "symfony/console": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -7645,7 +7703,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/uid/tree/v6.0.19" + "source": "https://github.com/symfony/uid/tree/v7.3.1" }, "funding": [ { @@ -7661,41 +7719,36 @@ "type": "tidelift" } ], - "time": "2023-01-01T08:36:10+00:00" + "time": "2025-06-27T19:55:54+00:00" }, { "name": "symfony/var-dumper", - "version": "v6.0.19", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "eb980457fa6899840fe1687e8627a03a7d8a3d52" + "reference": "34d8d4c4b9597347306d1ec8eb4e1319b1e6986f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/eb980457fa6899840fe1687e8627a03a7d8a3d52", - "reference": "eb980457fa6899840fe1687e8627a03a7d8a3d52", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/34d8d4c4b9597347306d1ec8eb4e1319b1e6986f", + "reference": "34d8d4c4b9597347306d1ec8eb4e1319b1e6986f", "shasum": "" }, "require": { - "php": ">=8.0.2", + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0" }, "conflict": { - "phpunit/phpunit": "<5.4.3", - "symfony/console": "<5.4" + "symfony/console": "<6.4" }, "require-dev": { - "ext-iconv": "*", - "symfony/console": "^5.4|^6.0", - "symfony/process": "^5.4|^6.0", - "symfony/uid": "^5.4|^6.0", - "twig/twig": "^2.13|^3.0.4" - }, - "suggest": { - "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).", - "ext-intl": "To show region name in time zone dump", - "symfony/console": "To use the ServerDumpCommand and/or the bin/var-dump-server script" + "symfony/console": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/uid": "^6.4|^7.0", + "twig/twig": "^3.12" }, "bin": [ "Resources/bin/var-dump-server" @@ -7733,7 +7786,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v6.0.19" + "source": "https://github.com/symfony/var-dumper/tree/v7.3.3" }, "funding": [ { @@ -7744,40 +7797,46 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2023-01-20T17:44:14+00:00" + "time": "2025-08-13T11:49:31+00:00" }, { "name": "tijsverkoyen/css-to-inline-styles", - "version": "v2.2.7", + "version": "v2.3.0", "source": { "type": "git", "url": "https://github.com/tijsverkoyen/CssToInlineStyles.git", - "reference": "83ee6f38df0a63106a9e4536e3060458b74ccedb" + "reference": "0d72ac1c00084279c1816675284073c5a337c20d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/83ee6f38df0a63106a9e4536e3060458b74ccedb", - "reference": "83ee6f38df0a63106a9e4536e3060458b74ccedb", + "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/0d72ac1c00084279c1816675284073c5a337c20d", + "reference": "0d72ac1c00084279c1816675284073c5a337c20d", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", - "php": "^5.5 || ^7.0 || ^8.0", - "symfony/css-selector": "^2.7 || ^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0" + "php": "^7.4 || ^8.0", + "symfony/css-selector": "^5.4 || ^6.0 || ^7.0" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0 || ^7.5 || ^8.5.21 || ^9.5.10" + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^8.5.21 || ^9.5.10" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.2.x-dev" + "dev-master": "2.x-dev" } }, "autoload": { @@ -7800,29 +7859,29 @@ "homepage": "https://github.com/tijsverkoyen/CssToInlineStyles", "support": { "issues": "https://github.com/tijsverkoyen/CssToInlineStyles/issues", - "source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/v2.2.7" + "source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/v2.3.0" }, - "time": "2023-12-08T13:03:43+00:00" + "time": "2024-12-21T16:25:41+00:00" }, { "name": "vlucas/phpdotenv", - "version": "v5.6.0", + "version": "v5.6.2", "source": { "type": "git", "url": "https://github.com/vlucas/phpdotenv.git", - "reference": "2cf9fb6054c2bb1d59d1f3817706ecdb9d2934c4" + "reference": "24ac4c74f91ee2c193fa1aaa5c249cb0822809af" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/2cf9fb6054c2bb1d59d1f3817706ecdb9d2934c4", - "reference": "2cf9fb6054c2bb1d59d1f3817706ecdb9d2934c4", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/24ac4c74f91ee2c193fa1aaa5c249cb0822809af", + "reference": "24ac4c74f91ee2c193fa1aaa5c249cb0822809af", "shasum": "" }, "require": { "ext-pcre": "*", - "graham-campbell/result-type": "^1.1.2", + "graham-campbell/result-type": "^1.1.3", "php": "^7.2.5 || ^8.0", - "phpoption/phpoption": "^1.9.2", + "phpoption/phpoption": "^1.9.3", "symfony/polyfill-ctype": "^1.24", "symfony/polyfill-mbstring": "^1.24", "symfony/polyfill-php80": "^1.24" @@ -7839,7 +7898,7 @@ "extra": { "bamarni-bin": { "bin-links": true, - "forward-command": true + "forward-command": false }, "branch-alias": { "dev-master": "5.6-dev" @@ -7874,7 +7933,7 @@ ], "support": { "issues": "https://github.com/vlucas/phpdotenv/issues", - "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.0" + "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.2" }, "funding": [ { @@ -7886,20 +7945,20 @@ "type": "tidelift" } ], - "time": "2023-11-12T22:43:29+00:00" + "time": "2025-04-30T23:37:27+00:00" }, { "name": "voku/portable-ascii", - "version": "2.0.1", + "version": "2.0.3", "source": { "type": "git", "url": "https://github.com/voku/portable-ascii.git", - "reference": "b56450eed252f6801410d810c8e1727224ae0743" + "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/voku/portable-ascii/zipball/b56450eed252f6801410d810c8e1727224ae0743", - "reference": "b56450eed252f6801410d810c8e1727224ae0743", + "url": "https://api.github.com/repos/voku/portable-ascii/zipball/b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", + "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", "shasum": "" }, "require": { @@ -7924,7 +7983,7 @@ "authors": [ { "name": "Lars Moelleken", - "homepage": "http://www.moelleken.org/" + "homepage": "https://www.moelleken.org/" } ], "description": "Portable ASCII library - performance optimized (ascii) string functions for php.", @@ -7936,7 +7995,7 @@ ], "support": { "issues": "https://github.com/voku/portable-ascii/issues", - "source": "https://github.com/voku/portable-ascii/tree/2.0.1" + "source": "https://github.com/voku/portable-ascii/tree/2.0.3" }, "funding": [ { @@ -7960,7 +8019,7 @@ "type": "tidelift" } ], - "time": "2022-03-08T17:03:00+00:00" + "time": "2024-11-21T01:49:47+00:00" }, { "name": "webmozart/assert", @@ -8022,88 +8081,18 @@ } ], "packages-dev": [ - { - "name": "doctrine/instantiator", - "version": "1.5.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", - "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "require-dev": { - "doctrine/coding-standard": "^9 || ^11", - "ext-pdo": "*", - "ext-phar": "*", - "phpbench/phpbench": "^0.16 || ^1", - "phpstan/phpstan": "^1.4", - "phpstan/phpstan-phpunit": "^1", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "vimeo/psalm": "^4.30 || ^5.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "https://ocramius.github.io/" - } - ], - "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://www.doctrine-project.org/projects/instantiator.html", - "keywords": [ - "constructor", - "instantiate" - ], - "support": { - "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/1.5.0" - }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", - "type": "tidelift" - } - ], - "time": "2022-12-30T00:15:36+00:00" - }, { "name": "fakerphp/faker", - "version": "v1.23.1", + "version": "v1.24.1", "source": { "type": "git", "url": "https://github.com/FakerPHP/Faker.git", - "reference": "bfb4fe148adbf78eff521199619b93a52ae3554b" + "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/bfb4fe148adbf78eff521199619b93a52ae3554b", - "reference": "bfb4fe148adbf78eff521199619b93a52ae3554b", + "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5", + "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5", "shasum": "" }, "require": { @@ -8151,32 +8140,32 @@ ], "support": { "issues": "https://github.com/FakerPHP/Faker/issues", - "source": "https://github.com/FakerPHP/Faker/tree/v1.23.1" + "source": "https://github.com/FakerPHP/Faker/tree/v1.24.1" }, - "time": "2024-01-02T13:46:09+00:00" + "time": "2024-11-21T13:46:39+00:00" }, { "name": "filp/whoops", - "version": "2.15.4", + "version": "2.18.4", "source": { "type": "git", "url": "https://github.com/filp/whoops.git", - "reference": "a139776fa3f5985a50b509f2a02ff0f709d2a546" + "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filp/whoops/zipball/a139776fa3f5985a50b509f2a02ff0f709d2a546", - "reference": "a139776fa3f5985a50b509f2a02ff0f709d2a546", + "url": "https://api.github.com/repos/filp/whoops/zipball/d2102955e48b9fd9ab24280a7ad12ed552752c4d", + "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d", "shasum": "" }, "require": { - "php": "^5.5.9 || ^7.0 || ^8.0", + "php": "^7.1 || ^8.0", "psr/log": "^1.0.1 || ^2.0 || ^3.0" }, "require-dev": { - "mockery/mockery": "^0.9 || ^1.0", - "phpunit/phpunit": "^4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.3", - "symfony/var-dumper": "^2.6 || ^3.0 || ^4.0 || ^5.0" + "mockery/mockery": "^1.0", + "phpunit/phpunit": "^7.5.20 || ^8.5.8 || ^9.3.3", + "symfony/var-dumper": "^4.0 || ^5.0" }, "suggest": { "symfony/var-dumper": "Pretty print complex values better with var-dumper available", @@ -8216,7 +8205,7 @@ ], "support": { "issues": "https://github.com/filp/whoops/issues", - "source": "https://github.com/filp/whoops/tree/2.15.4" + "source": "https://github.com/filp/whoops/tree/2.18.4" }, "funding": [ { @@ -8224,24 +8213,24 @@ "type": "github" } ], - "time": "2023-11-03T12:00:00+00:00" + "time": "2025-08-08T12:00:00+00:00" }, { "name": "hamcrest/hamcrest-php", - "version": "v2.0.1", + "version": "v2.1.1", "source": { "type": "git", "url": "https://github.com/hamcrest/hamcrest-php.git", - "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3" + "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", - "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", + "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487", + "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487", "shasum": "" }, "require": { - "php": "^5.3|^7.0|^8.0" + "php": "^7.4|^8.0" }, "replace": { "cordoval/hamcrest-php": "*", @@ -8249,8 +8238,8 @@ "kodova/hamcrest-php": "*" }, "require-dev": { - "phpunit/php-file-iterator": "^1.4 || ^2.0", - "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0" + "phpunit/php-file-iterator": "^1.4 || ^2.0 || ^3.0", + "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0 || ^8.0 || ^9.0" }, "type": "library", "extra": { @@ -8273,44 +8262,86 @@ ], "support": { "issues": "https://github.com/hamcrest/hamcrest-php/issues", - "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.0.1" + "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.1.1" + }, + "time": "2025-04-30T06:54:44+00:00" + }, + { + "name": "iamcal/sql-parser", + "version": "v0.6", + "source": { + "type": "git", + "url": "https://github.com/iamcal/SQLParser.git", + "reference": "947083e2dca211a6f12fb1beb67a01e387de9b62" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/iamcal/SQLParser/zipball/947083e2dca211a6f12fb1beb67a01e387de9b62", + "reference": "947083e2dca211a6f12fb1beb67a01e387de9b62", + "shasum": "" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^1.0", + "phpunit/phpunit": "^5|^6|^7|^8|^9" + }, + "type": "library", + "autoload": { + "psr-4": { + "iamcal\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Cal Henderson", + "email": "cal@iamcal.com" + } + ], + "description": "MySQL schema parser", + "support": { + "issues": "https://github.com/iamcal/SQLParser/issues", + "source": "https://github.com/iamcal/SQLParser/tree/v0.6" }, - "time": "2020-07-09T08:09:16+00:00" + "time": "2025-03-17T16:59:46+00:00" }, { "name": "itsgoingd/clockwork", - "version": "v5.2.0", + "version": "v5.3.4", "source": { "type": "git", "url": "https://github.com/itsgoingd/clockwork.git", - "reference": "df52c7c4d8d60443ea1d14bcf9b182d4eaaeec26" + "reference": "c27ad77a08a9e58bf0049de46969fa4fe3b506e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/itsgoingd/clockwork/zipball/df52c7c4d8d60443ea1d14bcf9b182d4eaaeec26", - "reference": "df52c7c4d8d60443ea1d14bcf9b182d4eaaeec26", + "url": "https://api.github.com/repos/itsgoingd/clockwork/zipball/c27ad77a08a9e58bf0049de46969fa4fe3b506e5", + "reference": "c27ad77a08a9e58bf0049de46969fa4fe3b506e5", "shasum": "" }, "require": { "ext-json": "*", - "php": ">=5.6" + "php": ">=7.1" }, "suggest": { "ext-pdo": "Needed in order to use a SQL database for metadata storage", "ext-pdo_mysql": "Needed in order to use MySQL for metadata storage", "ext-pdo_postgres": "Needed in order to use Postgres for metadata storage", "ext-pdo_sqlite": "Needed in order to use a SQLite for metadata storage", - "ext-redis": "Needed in order to use Redis for metadata storage" + "ext-redis": "Needed in order to use Redis for metadata storage", + "php-http/discovery": "Vanilla integration - required for the middleware zero-configuration setup" }, "type": "library", "extra": { "laravel": { - "providers": [ - "Clockwork\\Support\\Laravel\\ClockworkServiceProvider" - ], "aliases": { "Clockwork": "Clockwork\\Support\\Laravel\\Facade" - } + }, + "providers": [ + "Clockwork\\Support\\Laravel\\ClockworkServiceProvider" + ] } }, "autoload": { @@ -8342,7 +8373,7 @@ ], "support": { "issues": "https://github.com/itsgoingd/clockwork/issues", - "source": "https://github.com/itsgoingd/clockwork/tree/v5.2.0" + "source": "https://github.com/itsgoingd/clockwork/tree/v5.3.4" }, "funding": [ { @@ -8350,53 +8381,57 @@ "type": "github" } ], - "time": "2024-02-20T22:36:44+00:00" + "time": "2025-02-09T15:57:21+00:00" }, { "name": "larastan/larastan", - "version": "v2.9.0", + "version": "v3.6.1", "source": { "type": "git", "url": "https://github.com/larastan/larastan.git", - "reference": "35fa9cbe1895e76215bbe74571a344f2705fbe01" + "reference": "3c223047e374befd1b64959784685d6ecccf66aa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/larastan/larastan/zipball/35fa9cbe1895e76215bbe74571a344f2705fbe01", - "reference": "35fa9cbe1895e76215bbe74571a344f2705fbe01", + "url": "https://api.github.com/repos/larastan/larastan/zipball/3c223047e374befd1b64959784685d6ecccf66aa", + "reference": "3c223047e374befd1b64959784685d6ecccf66aa", "shasum": "" }, "require": { "ext-json": "*", - "illuminate/console": "^9.52.16 || ^10.28.0 || ^11.0", - "illuminate/container": "^9.52.16 || ^10.28.0 || ^11.0", - "illuminate/contracts": "^9.52.16 || ^10.28.0 || ^11.0", - "illuminate/database": "^9.52.16 || ^10.28.0 || ^11.0", - "illuminate/http": "^9.52.16 || ^10.28.0 || ^11.0", - "illuminate/pipeline": "^9.52.16 || ^10.28.0 || ^11.0", - "illuminate/support": "^9.52.16 || ^10.28.0 || ^11.0", - "php": "^8.0.2", - "phpmyadmin/sql-parser": "^5.8.2", - "phpstan/phpstan": "^1.10.50" + "iamcal/sql-parser": "^0.6.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", + "php": "^8.2", + "phpstan/phpstan": "^2.1.11" }, "require-dev": { - "nikic/php-parser": "^4.17.1", - "orchestra/canvas": "^7.11.1 || ^8.11.0 || ^9.0.0", - "orchestra/testbench": "^7.33.0 || ^8.13.0 || ^9.0.0", - "phpunit/phpunit": "^9.6.13 || ^10.5" + "doctrine/coding-standard": "^13", + "laravel/framework": "^11.44.2 || ^12.7.2", + "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", + "phpstan/phpstan-deprecation-rules": "^2.0.1", + "phpunit/phpunit": "^10.5.35 || ^11.5.15" }, "suggest": { "orchestra/testbench": "Using Larastan for analysing a package needs Testbench" }, "type": "phpstan-extension", "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - }, "phpstan": { "includes": [ "extension.neon" ] + }, + "branch-alias": { + "dev-master": "3.0-dev" } }, "autoload": { @@ -8412,13 +8447,9 @@ { "name": "Can Vural", "email": "can9119@gmail.com" - }, - { - "name": "Nuno Maduro", - "email": "enunomaduro@gmail.com" } ], - "description": "Larastan - Discover bugs in your code without running it. A phpstan/phpstan wrapper for Laravel", + "description": "Larastan - Discover bugs in your code without running it. A phpstan/phpstan extension for Laravel", "keywords": [ "PHPStan", "code analyse", @@ -8431,40 +8462,28 @@ ], "support": { "issues": "https://github.com/larastan/larastan/issues", - "source": "https://github.com/larastan/larastan/tree/v2.9.0" + "source": "https://github.com/larastan/larastan/tree/v3.6.1" }, "funding": [ - { - "url": "https://www.paypal.com/paypalme/enunomaduro", - "type": "custom" - }, { "url": "https://github.com/canvural", "type": "github" - }, - { - "url": "https://github.com/nunomaduro", - "type": "github" - }, - { - "url": "https://www.patreon.com/nunomaduro", - "type": "patreon" } ], - "time": "2024-02-13T11:49:22+00:00" + "time": "2025-08-25T07:24:56+00:00" }, { "name": "mockery/mockery", - "version": "1.6.7", + "version": "1.6.12", "source": { "type": "git", "url": "https://github.com/mockery/mockery.git", - "reference": "0cc058854b3195ba21dc6b1f7b1f60f4ef3a9c06" + "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mockery/mockery/zipball/0cc058854b3195ba21dc6b1f7b1f60f4ef3a9c06", - "reference": "0cc058854b3195ba21dc6b1f7b1f60f4ef3a9c06", + "url": "https://api.github.com/repos/mockery/mockery/zipball/1f4efdd7d3beafe9807b08156dfcb176d18f1699", + "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699", "shasum": "" }, "require": { @@ -8476,8 +8495,8 @@ "phpunit/phpunit": "<8.0" }, "require-dev": { - "phpunit/phpunit": "^8.5 || ^9.6.10", - "symplify/easy-coding-standard": "^12.0.8" + "phpunit/phpunit": "^8.5 || ^9.6.17", + "symplify/easy-coding-standard": "^12.1.14" }, "type": "library", "autoload": { @@ -8534,20 +8553,20 @@ "security": "https://github.com/mockery/mockery/security/advisories", "source": "https://github.com/mockery/mockery" }, - "time": "2023-12-10T02:24:34+00:00" + "time": "2024-05-16T03:13:13+00:00" }, { "name": "myclabs/deep-copy", - "version": "1.11.1", + "version": "1.13.4", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", "shasum": "" }, "require": { @@ -8555,11 +8574,12 @@ }, "conflict": { "doctrine/collections": "<1.6.8", - "doctrine/common": "<2.13.3 || >=3,<3.2.2" + "doctrine/common": "<2.13.3 || >=3 <3.2.2" }, "require-dev": { "doctrine/collections": "^1.6.8", "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" }, "type": "library", @@ -8585,7 +8605,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" }, "funding": [ { @@ -8593,49 +8613,59 @@ "type": "tidelift" } ], - "time": "2023-03-08T13:26:56+00:00" + "time": "2025-08-01T08:46:24+00:00" }, { "name": "nunomaduro/collision", - "version": "v6.4.0", + "version": "v8.8.2", "source": { "type": "git", "url": "https://github.com/nunomaduro/collision.git", - "reference": "f05978827b9343cba381ca05b8c7deee346b6015" + "reference": "60207965f9b7b7a4ce15a0f75d57f9dadb105bdb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/collision/zipball/f05978827b9343cba381ca05b8c7deee346b6015", - "reference": "f05978827b9343cba381ca05b8c7deee346b6015", + "url": "https://api.github.com/repos/nunomaduro/collision/zipball/60207965f9b7b7a4ce15a0f75d57f9dadb105bdb", + "reference": "60207965f9b7b7a4ce15a0f75d57f9dadb105bdb", "shasum": "" }, "require": { - "filp/whoops": "^2.14.5", - "php": "^8.0.0", - "symfony/console": "^6.0.2" + "filp/whoops": "^2.18.1", + "nunomaduro/termwind": "^2.3.1", + "php": "^8.2.0", + "symfony/console": "^7.3.0" + }, + "conflict": { + "laravel/framework": "<11.44.2 || >=13.0.0", + "phpunit/phpunit": "<11.5.15 || >=13.0.0" }, "require-dev": { - "brianium/paratest": "^6.4.1", - "laravel/framework": "^9.26.1", - "laravel/pint": "^1.1.1", - "nunomaduro/larastan": "^1.0.3", - "nunomaduro/mock-final-classes": "^1.1.0", - "orchestra/testbench": "^7.7", - "phpunit/phpunit": "^9.5.23", - "spatie/ignition": "^1.4.1" + "brianium/paratest": "^7.8.3", + "larastan/larastan": "^3.4.2", + "laravel/framework": "^11.44.2 || ^12.18", + "laravel/pint": "^1.22.1", + "laravel/sail": "^1.43.1", + "laravel/sanctum": "^4.1.1", + "laravel/tinker": "^2.10.1", + "orchestra/testbench-core": "^9.12.0 || ^10.4", + "pestphp/pest": "^3.8.2", + "sebastian/environment": "^7.2.1 || ^8.0" }, "type": "library", "extra": { - "branch-alias": { - "dev-develop": "6.x-dev" - }, "laravel": { "providers": [ "NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider" ] + }, + "branch-alias": { + "dev-8.x": "8.x-dev" } }, "autoload": { + "files": [ + "./src/Adapters/Phpunit/Autoload.php" + ], "psr-4": { "NunoMaduro\\Collision\\": "src/" } @@ -8656,6 +8686,7 @@ "cli", "command-line", "console", + "dev", "error", "handling", "laravel", @@ -8681,24 +8712,25 @@ "type": "patreon" } ], - "time": "2023-01-03T12:54:54+00:00" + "time": "2025-06-25T02:12:12+00:00" }, { "name": "phar-io/manifest", - "version": "2.0.3", + "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/phar-io/manifest.git", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53" + "reference": "54750ef60c58e43759730615a392c31c80e23176" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", "shasum": "" }, "require": { "ext-dom": "*", + "ext-libxml": "*", "ext-phar": "*", "ext-xmlwriter": "*", "phar-io/version": "^3.0.1", @@ -8739,9 +8771,15 @@ "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", "support": { "issues": "https://github.com/phar-io/manifest/issues", - "source": "https://github.com/phar-io/manifest/tree/2.0.3" + "source": "https://github.com/phar-io/manifest/tree/2.0.4" }, - "time": "2021-07-20T11:28:43+00:00" + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" }, { "name": "phar-io/version", @@ -8794,110 +8832,22 @@ }, "time": "2022-02-21T01:04:05+00:00" }, - { - "name": "phpmyadmin/sql-parser", - "version": "5.9.0", - "source": { - "type": "git", - "url": "https://github.com/phpmyadmin/sql-parser.git", - "reference": "011fa18a4e55591fac6545a821921dd1d61c6984" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpmyadmin/sql-parser/zipball/011fa18a4e55591fac6545a821921dd1d61c6984", - "reference": "011fa18a4e55591fac6545a821921dd1d61c6984", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0", - "symfony/polyfill-mbstring": "^1.3", - "symfony/polyfill-php80": "^1.16" - }, - "conflict": { - "phpmyadmin/motranslator": "<3.0" - }, - "require-dev": { - "phpbench/phpbench": "^1.1", - "phpmyadmin/coding-standard": "^3.0", - "phpmyadmin/motranslator": "^4.0 || ^5.0", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^1.9.12", - "phpstan/phpstan-phpunit": "^1.3.3", - "phpunit/php-code-coverage": "*", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "psalm/plugin-phpunit": "^0.16.1", - "vimeo/psalm": "^4.11", - "zumba/json-serializer": "~3.0.2" - }, - "suggest": { - "ext-mbstring": "For best performance", - "phpmyadmin/motranslator": "Translate messages to your favorite locale" - }, - "bin": [ - "bin/highlight-query", - "bin/lint-query", - "bin/sql-parser", - "bin/tokenize-query" - ], - "type": "library", - "autoload": { - "psr-4": { - "PhpMyAdmin\\SqlParser\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "GPL-2.0-or-later" - ], - "authors": [ - { - "name": "The phpMyAdmin Team", - "email": "developers@phpmyadmin.net", - "homepage": "https://www.phpmyadmin.net/team/" - } - ], - "description": "A validating SQL lexer and parser with a focus on MySQL dialect.", - "homepage": "https://github.com/phpmyadmin/sql-parser", - "keywords": [ - "analysis", - "lexer", - "parser", - "query linter", - "sql", - "sql lexer", - "sql linter", - "sql parser", - "sql syntax highlighter", - "sql tokenizer" - ], - "support": { - "issues": "https://github.com/phpmyadmin/sql-parser/issues", - "source": "https://github.com/phpmyadmin/sql-parser" - }, - "funding": [ - { - "url": "https://www.phpmyadmin.net/donate/", - "type": "other" - } - ], - "time": "2024-01-20T20:34:02+00:00" - }, { "name": "phpstan/phpstan", - "version": "1.10.59", + "version": "2.1.22", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "e607609388d3a6d418a50a49f7940e8086798281" + "reference": "41600c8379eb5aee63e9413fe9e97273e25d57e4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/e607609388d3a6d418a50a49f7940e8086798281", - "reference": "e607609388d3a6d418a50a49f7940e8086798281", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/41600c8379eb5aee63e9413fe9e97273e25d57e4", + "reference": "41600c8379eb5aee63e9413fe9e97273e25d57e4", "shasum": "" }, "require": { - "php": "^7.2|^8.0" + "php": "^7.4|^8.0" }, "conflict": { "phpstan/phpstan-shim": "*" @@ -8936,45 +8886,41 @@ { "url": "https://github.com/phpstan", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan", - "type": "tidelift" } ], - "time": "2024-02-20T13:59:13+00:00" + "time": "2025-08-04T19:17:37+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "9.2.30", + "version": "11.0.11", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "ca2bd87d2f9215904682a9cb9bb37dda98e76089" + "reference": "4f7722aa9a7b76aa775e2d9d4e95d1ea16eeeef4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ca2bd87d2f9215904682a9cb9bb37dda98e76089", - "reference": "ca2bd87d2f9215904682a9cb9bb37dda98e76089", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/4f7722aa9a7b76aa775e2d9d4e95d1ea16eeeef4", + "reference": "4f7722aa9a7b76aa775e2d9d4e95d1ea16eeeef4", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.18 || ^5.0", - "php": ">=7.3", - "phpunit/php-file-iterator": "^3.0.3", - "phpunit/php-text-template": "^2.0.2", - "sebastian/code-unit-reverse-lookup": "^2.0.2", - "sebastian/complexity": "^2.0", - "sebastian/environment": "^5.1.2", - "sebastian/lines-of-code": "^1.0.3", - "sebastian/version": "^3.0.1", - "theseer/tokenizer": "^1.2.0" + "nikic/php-parser": "^5.4.0", + "php": ">=8.2", + "phpunit/php-file-iterator": "^5.1.0", + "phpunit/php-text-template": "^4.0.1", + "sebastian/code-unit-reverse-lookup": "^4.0.1", + "sebastian/complexity": "^4.0.1", + "sebastian/environment": "^7.2.0", + "sebastian/lines-of-code": "^3.0.1", + "sebastian/version": "^5.0.2", + "theseer/tokenizer": "^1.2.3" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^11.5.2" }, "suggest": { "ext-pcov": "PHP extension that provides line coverage", @@ -8983,7 +8929,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "9.2-dev" + "dev-main": "11.0.x-dev" } }, "autoload": { @@ -9012,40 +8958,52 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.30" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/11.0.11" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-code-coverage", + "type": "tidelift" } ], - "time": "2023-12-22T06:47:57+00:00" + "time": "2025-08-27T14:37:49+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "3.0.6", + "version": "5.1.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" + "reference": "118cfaaa8bc5aef3287bf315b6060b1174754af6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/118cfaaa8bc5aef3287bf315b6060b1174754af6", + "reference": "118cfaaa8bc5aef3287bf315b6060b1174754af6", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^11.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -9072,7 +9030,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" + "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/5.1.0" }, "funding": [ { @@ -9080,28 +9039,28 @@ "type": "github" } ], - "time": "2021-12-02T12:48:52+00:00" + "time": "2024-08-27T05:02:59+00:00" }, { "name": "phpunit/php-invoker", - "version": "3.1.1", + "version": "5.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-invoker.git", - "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" + "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", - "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/c1ca3814734c07492b3d4c5f794f4b0995333da2", + "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.2" }, "require-dev": { "ext-pcntl": "*", - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^11.0" }, "suggest": { "ext-pcntl": "*" @@ -9109,7 +9068,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -9135,7 +9094,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-invoker/issues", - "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" + "security": "https://github.com/sebastianbergmann/php-invoker/security/policy", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/5.0.1" }, "funding": [ { @@ -9143,32 +9103,32 @@ "type": "github" } ], - "time": "2020-09-28T05:58:55+00:00" + "time": "2024-07-03T05:07:44+00:00" }, { "name": "phpunit/php-text-template", - "version": "2.0.4", + "version": "4.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" + "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/3e0404dc6b300e6bf56415467ebcb3fe4f33e964", + "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^11.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -9194,7 +9154,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/4.0.1" }, "funding": [ { @@ -9202,32 +9163,32 @@ "type": "github" } ], - "time": "2020-10-26T05:33:50+00:00" + "time": "2024-07-03T05:08:43+00:00" }, { "name": "phpunit/php-timer", - "version": "5.0.3", + "version": "7.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" + "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3b415def83fbcb41f991d9ebf16ae4ad8b7837b3", + "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^11.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -9253,7 +9214,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" + "security": "https://github.com/sebastianbergmann/php-timer/security/policy", + "source": "https://github.com/sebastianbergmann/php-timer/tree/7.0.1" }, "funding": [ { @@ -9261,54 +9223,52 @@ "type": "github" } ], - "time": "2020-10-26T13:16:10+00:00" + "time": "2024-07-03T05:09:35+00:00" }, { "name": "phpunit/phpunit", - "version": "9.6.17", + "version": "11.5.35", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "1a156980d78a6666721b7e8e8502fe210b587fcd" + "reference": "d341ee94ee5007b286fc7907b383aae6b5b3cc91" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/1a156980d78a6666721b7e8e8502fe210b587fcd", - "reference": "1a156980d78a6666721b7e8e8502fe210b587fcd", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/d341ee94ee5007b286fc7907b383aae6b5b3cc91", + "reference": "d341ee94ee5007b286fc7907b383aae6b5b3cc91", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.3.1 || ^2", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.10.1", - "phar-io/manifest": "^2.0.3", - "phar-io/version": "^3.0.2", - "php": ">=7.3", - "phpunit/php-code-coverage": "^9.2.28", - "phpunit/php-file-iterator": "^3.0.5", - "phpunit/php-invoker": "^3.1.1", - "phpunit/php-text-template": "^2.0.3", - "phpunit/php-timer": "^5.0.2", - "sebastian/cli-parser": "^1.0.1", - "sebastian/code-unit": "^1.0.6", - "sebastian/comparator": "^4.0.8", - "sebastian/diff": "^4.0.3", - "sebastian/environment": "^5.1.3", - "sebastian/exporter": "^4.0.5", - "sebastian/global-state": "^5.0.1", - "sebastian/object-enumerator": "^4.0.3", - "sebastian/resource-operations": "^3.0.3", - "sebastian/type": "^3.2", - "sebastian/version": "^3.0.2" + "myclabs/deep-copy": "^1.13.4", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", + "php": ">=8.2", + "phpunit/php-code-coverage": "^11.0.11", + "phpunit/php-file-iterator": "^5.1.0", + "phpunit/php-invoker": "^5.0.1", + "phpunit/php-text-template": "^4.0.1", + "phpunit/php-timer": "^7.0.1", + "sebastian/cli-parser": "^3.0.2", + "sebastian/code-unit": "^3.0.3", + "sebastian/comparator": "^6.3.2", + "sebastian/diff": "^6.0.2", + "sebastian/environment": "^7.2.1", + "sebastian/exporter": "^6.3.0", + "sebastian/global-state": "^7.0.2", + "sebastian/object-enumerator": "^6.0.1", + "sebastian/type": "^5.1.3", + "sebastian/version": "^5.0.2", + "staabm/side-effects-detector": "^1.0.5" }, "suggest": { - "ext-soap": "To be able to generate mocks based on WSDL files", - "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + "ext-soap": "To be able to generate mocks based on WSDL files" }, "bin": [ "phpunit" @@ -9316,7 +9276,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "9.6-dev" + "dev-main": "11.5-dev" } }, "autoload": { @@ -9348,7 +9308,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.17" + "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.35" }, "funding": [ { @@ -9359,37 +9319,45 @@ "url": "https://github.com/sebastianbergmann", "type": "github" }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, { "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", "type": "tidelift" } ], - "time": "2024-02-23T13:14:51+00:00" + "time": "2025-08-28T05:13:54+00:00" }, { "name": "sebastian/cli-parser", - "version": "1.0.1", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" + "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/15c5dd40dc4f38794d383bb95465193f5e0ae180", + "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^11.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-main": "3.0-dev" } }, "autoload": { @@ -9412,7 +9380,8 @@ "homepage": "https://github.com/sebastianbergmann/cli-parser", "support": { "issues": "https://github.com/sebastianbergmann/cli-parser/issues", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" + "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/3.0.2" }, "funding": [ { @@ -9420,32 +9389,32 @@ "type": "github" } ], - "time": "2020-09-28T06:08:49+00:00" + "time": "2024-07-03T04:41:36+00:00" }, { "name": "sebastian/code-unit", - "version": "1.0.8", + "version": "3.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit.git", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" + "reference": "54391c61e4af8078e5b276ab082b6d3c54c9ad64" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/54391c61e4af8078e5b276ab082b6d3c54c9ad64", + "reference": "54391c61e4af8078e5b276ab082b6d3c54c9ad64", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^11.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-main": "3.0-dev" } }, "autoload": { @@ -9468,7 +9437,8 @@ "homepage": "https://github.com/sebastianbergmann/code-unit", "support": { "issues": "https://github.com/sebastianbergmann/code-unit/issues", - "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + "security": "https://github.com/sebastianbergmann/code-unit/security/policy", + "source": "https://github.com/sebastianbergmann/code-unit/tree/3.0.3" }, "funding": [ { @@ -9476,32 +9446,32 @@ "type": "github" } ], - "time": "2020-10-26T13:08:54+00:00" + "time": "2025-03-19T07:56:08+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", - "version": "2.0.3", + "version": "4.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" + "reference": "183a9b2632194febd219bb9246eee421dad8d45e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/183a9b2632194febd219bb9246eee421dad8d45e", + "reference": "183a9b2632194febd219bb9246eee421dad8d45e", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^11.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -9523,7 +9493,8 @@ "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", "support": { "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" + "security": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/security/policy", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/4.0.1" }, "funding": [ { @@ -9531,34 +9502,39 @@ "type": "github" } ], - "time": "2020-09-28T05:30:19+00:00" + "time": "2024-07-03T04:45:54+00:00" }, { "name": "sebastian/comparator", - "version": "4.0.8", + "version": "6.3.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a" + "reference": "85c77556683e6eee4323e4c5468641ca0237e2e8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/85c77556683e6eee4323e4c5468641ca0237e2e8", + "reference": "85c77556683e6eee4323e4c5468641ca0237e2e8", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/diff": "^4.0", - "sebastian/exporter": "^4.0" + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.2", + "sebastian/diff": "^6.0", + "sebastian/exporter": "^6.0" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^11.4" + }, + "suggest": { + "ext-bcmath": "For comparing BcMath\\Number objects" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "6.3-dev" } }, "autoload": { @@ -9597,41 +9573,54 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/6.3.2" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", + "type": "tidelift" } ], - "time": "2022-09-14T12:41:17+00:00" + "time": "2025-08-10T08:07:46+00:00" }, { "name": "sebastian/complexity", - "version": "2.0.3", + "version": "4.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a" + "reference": "ee41d384ab1906c68852636b6de493846e13e5a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/25f207c40d62b8b7aa32f5ab026c53561964053a", - "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/ee41d384ab1906c68852636b6de493846e13e5a0", + "reference": "ee41d384ab1906c68852636b6de493846e13e5a0", "shasum": "" }, "require": { - "nikic/php-parser": "^4.18 || ^5.0", - "php": ">=7.3" + "nikic/php-parser": "^5.0", + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^11.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -9654,7 +9643,8 @@ "homepage": "https://github.com/sebastianbergmann/complexity", "support": { "issues": "https://github.com/sebastianbergmann/complexity/issues", - "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.3" + "security": "https://github.com/sebastianbergmann/complexity/security/policy", + "source": "https://github.com/sebastianbergmann/complexity/tree/4.0.1" }, "funding": [ { @@ -9662,33 +9652,33 @@ "type": "github" } ], - "time": "2023-12-22T06:19:30+00:00" + "time": "2024-07-03T04:49:50+00:00" }, { "name": "sebastian/diff", - "version": "4.0.5", + "version": "6.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131" + "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/74be17022044ebaaecfdf0c5cd504fc9cd5a7131", - "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/b4ccd857127db5d41a5b676f24b51371d76d8544", + "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^9.3", + "phpunit/phpunit": "^11.0", "symfony/process": "^4.2 || ^5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -9720,7 +9710,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/4.0.5" + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/6.0.2" }, "funding": [ { @@ -9728,27 +9719,27 @@ "type": "github" } ], - "time": "2023-05-07T05:35:17+00:00" + "time": "2024-07-03T04:53:05+00:00" }, { "name": "sebastian/environment", - "version": "5.1.5", + "version": "7.2.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" + "reference": "a5c75038693ad2e8d4b6c15ba2403532647830c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", - "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/a5c75038693ad2e8d4b6c15ba2403532647830c4", + "reference": "a5c75038693ad2e8d4b6c15ba2403532647830c4", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^11.3" }, "suggest": { "ext-posix": "*" @@ -9756,7 +9747,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.1-dev" + "dev-main": "7.2-dev" } }, "autoload": { @@ -9775,7 +9766,7 @@ } ], "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "http://www.github.com/sebastianbergmann/environment", + "homepage": "https://github.com/sebastianbergmann/environment", "keywords": [ "Xdebug", "environment", @@ -9783,42 +9774,55 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" + "security": "https://github.com/sebastianbergmann/environment/security/policy", + "source": "https://github.com/sebastianbergmann/environment/tree/7.2.1" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/environment", + "type": "tidelift" } ], - "time": "2023-02-03T06:03:51+00:00" + "time": "2025-05-21T11:55:47+00:00" }, { "name": "sebastian/exporter", - "version": "4.0.5", + "version": "6.3.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d" + "reference": "3473f61172093b2da7de1fb5782e1f24cc036dc3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", - "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/3473f61172093b2da7de1fb5782e1f24cc036dc3", + "reference": "3473f61172093b2da7de1fb5782e1f24cc036dc3", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/recursion-context": "^4.0" + "ext-mbstring": "*", + "php": ">=8.2", + "sebastian/recursion-context": "^6.0" }, "require-dev": { - "ext-mbstring": "*", - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^11.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "6.1-dev" } }, "autoload": { @@ -9860,7 +9864,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5" + "security": "https://github.com/sebastianbergmann/exporter/security/policy", + "source": "https://github.com/sebastianbergmann/exporter/tree/6.3.0" }, "funding": [ { @@ -9868,38 +9873,35 @@ "type": "github" } ], - "time": "2022-09-14T06:03:37+00:00" + "time": "2024-12-05T09:17:50+00:00" }, { "name": "sebastian/global-state", - "version": "5.0.6", + "version": "7.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "bde739e7565280bda77be70044ac1047bc007e34" + "reference": "3be331570a721f9a4b5917f4209773de17f747d7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bde739e7565280bda77be70044ac1047bc007e34", - "reference": "bde739e7565280bda77be70044ac1047bc007e34", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/3be331570a721f9a4b5917f4209773de17f747d7", + "reference": "3be331570a721f9a4b5917f4209773de17f747d7", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/object-reflector": "^2.0", - "sebastian/recursion-context": "^4.0" + "php": ">=8.2", + "sebastian/object-reflector": "^4.0", + "sebastian/recursion-context": "^6.0" }, "require-dev": { "ext-dom": "*", - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-uopz": "*" + "phpunit/phpunit": "^11.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -9918,13 +9920,14 @@ } ], "description": "Snapshotting of global state", - "homepage": "http://www.github.com/sebastianbergmann/global-state", + "homepage": "https://www.github.com/sebastianbergmann/global-state", "keywords": [ "global state" ], "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.6" + "security": "https://github.com/sebastianbergmann/global-state/security/policy", + "source": "https://github.com/sebastianbergmann/global-state/tree/7.0.2" }, "funding": [ { @@ -9932,33 +9935,33 @@ "type": "github" } ], - "time": "2023-08-02T09:26:13+00:00" + "time": "2024-07-03T04:57:36+00:00" }, { "name": "sebastian/lines-of-code", - "version": "1.0.4", + "version": "3.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5" + "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/e1e4a170560925c26d424b6a03aed157e7dcc5c5", - "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/d36ad0d782e5756913e42ad87cb2890f4ffe467a", + "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a", "shasum": "" }, "require": { - "nikic/php-parser": "^4.18 || ^5.0", - "php": ">=7.3" + "nikic/php-parser": "^5.0", + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^11.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-main": "3.0-dev" } }, "autoload": { @@ -9981,7 +9984,8 @@ "homepage": "https://github.com/sebastianbergmann/lines-of-code", "support": { "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.4" + "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/3.0.1" }, "funding": [ { @@ -9989,34 +9993,34 @@ "type": "github" } ], - "time": "2023-12-22T06:20:34+00:00" + "time": "2024-07-03T04:58:38+00:00" }, { "name": "sebastian/object-enumerator", - "version": "4.0.4", + "version": "6.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" + "reference": "f5b498e631a74204185071eb41f33f38d64608aa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/f5b498e631a74204185071eb41f33f38d64608aa", + "reference": "f5b498e631a74204185071eb41f33f38d64608aa", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/object-reflector": "^2.0", - "sebastian/recursion-context": "^4.0" + "php": ">=8.2", + "sebastian/object-reflector": "^4.0", + "sebastian/recursion-context": "^6.0" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^11.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -10038,7 +10042,8 @@ "homepage": "https://github.com/sebastianbergmann/object-enumerator/", "support": { "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" + "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/6.0.1" }, "funding": [ { @@ -10046,32 +10051,32 @@ "type": "github" } ], - "time": "2020-10-26T13:12:34+00:00" + "time": "2024-07-03T05:00:13+00:00" }, { "name": "sebastian/object-reflector", - "version": "2.0.4", + "version": "4.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" + "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/6e1a43b411b2ad34146dee7524cb13a068bb35f9", + "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^11.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -10093,7 +10098,8 @@ "homepage": "https://github.com/sebastianbergmann/object-reflector/", "support": { "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" + "security": "https://github.com/sebastianbergmann/object-reflector/security/policy", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/4.0.1" }, "funding": [ { @@ -10101,32 +10107,32 @@ "type": "github" } ], - "time": "2020-10-26T13:14:26+00:00" + "time": "2024-07-03T05:01:32+00:00" }, { "name": "sebastian/recursion-context", - "version": "4.0.5", + "version": "6.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" + "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/f6458abbf32a6c8174f8f26261475dc133b3d9dc", + "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^11.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -10156,95 +10162,53 @@ "homepage": "https://github.com/sebastianbergmann/recursion-context", "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" + "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/6.0.3" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" - } - ], - "time": "2023-02-03T06:07:39+00:00" - }, - { - "name": "sebastian/resource-operations", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ + }, { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides a list of PHP built-in functions that operate on resources", - "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "support": { - "issues": "https://github.com/sebastianbergmann/resource-operations/issues", - "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" - }, - "funding": [ + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, { - "url": "https://github.com/sebastianbergmann", - "type": "github" + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", + "type": "tidelift" } ], - "time": "2020-09-28T06:45:17+00:00" + "time": "2025-08-13T04:42:22+00:00" }, { "name": "sebastian/type", - "version": "3.2.1", + "version": "5.1.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" + "reference": "f77d2d4e78738c98d9a68d2596fe5e8fa380f449" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", - "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/f77d2d4e78738c98d9a68d2596fe5e8fa380f449", + "reference": "f77d2d4e78738c98d9a68d2596fe5e8fa380f449", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^9.5" + "phpunit/phpunit": "^11.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.2-dev" + "dev-main": "5.1-dev" } }, "autoload": { @@ -10267,37 +10231,50 @@ "homepage": "https://github.com/sebastianbergmann/type", "support": { "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" + "security": "https://github.com/sebastianbergmann/type/security/policy", + "source": "https://github.com/sebastianbergmann/type/tree/5.1.3" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/type", + "type": "tidelift" } ], - "time": "2023-02-03T06:13:03+00:00" + "time": "2025-08-09T06:55:48+00:00" }, { "name": "sebastian/version", - "version": "3.0.2", + "version": "5.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/version.git", - "reference": "c6c1022351a901512170118436c764e473f6de8c" + "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", - "reference": "c6c1022351a901512170118436c764e473f6de8c", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c687e3387b99f5b03b6caa64c74b63e2936ff874", + "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -10320,7 +10297,8 @@ "homepage": "https://github.com/sebastianbergmann/version", "support": { "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" + "security": "https://github.com/sebastianbergmann/version/security/policy", + "source": "https://github.com/sebastianbergmann/version/tree/5.0.2" }, "funding": [ { @@ -10328,20 +10306,20 @@ "type": "github" } ], - "time": "2020-09-28T06:39:44+00:00" + "time": "2024-10-09T05:16:32+00:00" }, { "name": "squizlabs/php_codesniffer", - "version": "3.9.0", + "version": "3.13.2", "source": { "type": "git", "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", - "reference": "d63cee4890a8afaf86a22e51ad4d97c91dd4579b" + "reference": "5b5e3821314f947dd040c70f7992a64eac89025c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/d63cee4890a8afaf86a22e51ad4d97c91dd4579b", - "reference": "d63cee4890a8afaf86a22e51ad4d97c91dd4579b", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/5b5e3821314f947dd040c70f7992a64eac89025c", + "reference": "5b5e3821314f947dd040c70f7992a64eac89025c", "shasum": "" }, "require": { @@ -10406,34 +10384,36 @@ { "url": "https://opencollective.com/php_codesniffer", "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcsstandards", + "type": "thanks_dev" } ], - "time": "2024-02-16T15:06:51+00:00" + "time": "2025-06-17T22:17:01+00:00" }, { "name": "ssddanbrown/asserthtml", - "version": "v2.0.0", + "version": "v3.1.0", "source": { "type": "git", - "url": "https://github.com/ssddanbrown/asserthtml.git", - "reference": "6baf3ef2087f5928ae34f0d41db27aefcdf60414" + "url": "https://codeberg.org/danb/asserthtml", + "reference": "cf8206171d667d43e1bdde17d67191f30e95c8a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ssddanbrown/asserthtml/zipball/6baf3ef2087f5928ae34f0d41db27aefcdf60414", - "reference": "6baf3ef2087f5928ae34f0d41db27aefcdf60414", - "shasum": "" + "url": "https://codeberg.org/api/v1/repos/danb/asserthtml/archive/%prettyVersion%.zip" }, "require": { "ext-dom": "*", "ext-json": "*", - "php": ">=7.4", - "phpunit/phpunit": "^9.0", - "symfony/css-selector": "^5.0|^6.0", - "symfony/dom-crawler": "^5.0|^6.0" + "php": ">=8.1", + "phpunit/phpunit": "^10.0|^11.0", + "symfony/css-selector": "^6.0|^7.0", + "symfony/dom-crawler": "^6.0|^7.0" }, "require-dev": { - "vimeo/psalm": "^4.10" + "phpstan/phpstan": "^2.0" }, "type": "library", "autoload": { @@ -10448,52 +10428,88 @@ "authors": [ { "name": "Dan Brown", - "email": "ssddanbrown@googlemail.com", + "homepage": "https://danb.me", "role": "Developer" } ], "description": "HTML Content Assertions for PHPUnit", - "homepage": "https://github.com/ssddanbrown/asserthtml", + "homepage": "https://codeberg.org/danb/asserthtml", + "time": "2025-01-11T13:35:55+00:00" + }, + { + "name": "staabm/side-effects-detector", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/staabm/side-effects-detector.git", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/staabm/side-effects-detector/zipball/d8334211a140ce329c13726d4a715adbddd0a163", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^1.12.6", + "phpunit/phpunit": "^9.6.21", + "symfony/var-dumper": "^5.4.43", + "tomasvotruba/type-coverage": "1.0.0", + "tomasvotruba/unused-public": "1.0.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "lib/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A static analysis tool to detect side effects in PHP code", + "keywords": [ + "static analysis" + ], "support": { - "issues": "https://github.com/ssddanbrown/asserthtml/issues", - "source": "https://github.com/ssddanbrown/asserthtml/tree/v2.0.0" + "issues": "https://github.com/staabm/side-effects-detector/issues", + "source": "https://github.com/staabm/side-effects-detector/tree/1.0.5" }, "funding": [ { - "url": "https://github.com/ssddanbrown", + "url": "https://github.com/staabm", "type": "github" } ], - "time": "2023-03-01T16:48:08+00:00" + "time": "2024-10-20T05:08:20+00:00" }, { "name": "symfony/dom-crawler", - "version": "v6.0.19", + "version": "v7.3.3", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "622578ff158318b1b49d95068bd6b66c713601e9" + "reference": "efa076ea0eeff504383ff0dcf827ea5ce15690ba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/622578ff158318b1b49d95068bd6b66c713601e9", - "reference": "622578ff158318b1b49d95068bd6b66c713601e9", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/efa076ea0eeff504383ff0dcf827ea5ce15690ba", + "reference": "efa076ea0eeff504383ff0dcf827ea5ce15690ba", "shasum": "" }, "require": { - "php": ">=8.0.2", + "masterminds/html5": "^2.6", + "php": ">=8.2", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.0" }, - "conflict": { - "masterminds/html5": "<2.6" - }, "require-dev": { - "masterminds/html5": "^2.6", - "symfony/css-selector": "^5.4|^6.0" - }, - "suggest": { - "symfony/css-selector": "" + "symfony/css-selector": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -10521,7 +10537,7 @@ "description": "Eases DOM navigation for HTML and XML documents", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dom-crawler/tree/v6.0.19" + "source": "https://github.com/symfony/dom-crawler/tree/v7.3.3" }, "funding": [ { @@ -10532,25 +10548,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2023-01-20T17:44:14+00:00" + "time": "2025-08-06T20:13:54+00:00" }, { "name": "theseer/tokenizer", - "version": "1.2.2", + "version": "1.2.3", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96" + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b2ad5003ca10d4ee50a12da31de12a5774ba6b96", - "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", "shasum": "" }, "require": { @@ -10579,7 +10599,7 @@ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.2" + "source": "https://github.com/theseer/tokenizer/tree/1.2.3" }, "funding": [ { @@ -10587,29 +10607,28 @@ "type": "github" } ], - "time": "2023-11-20T00:12:19+00:00" + "time": "2024-03-03T12:36:25+00:00" } ], "aliases": [], "minimum-stability": "stable", - "stability-flags": { - "ssddanbrown/symfony-mailer": 20 - }, + "stability-flags": {}, "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": "^8.0.2", + "php": "^8.2.0", "ext-curl": "*", "ext-dom": "*", "ext-fileinfo": "*", "ext-gd": "*", "ext-json": "*", "ext-mbstring": "*", - "ext-xml": "*" + "ext-xml": "*", + "ext-zip": "*" }, - "platform-dev": [], + "platform-dev": {}, "platform-overrides": { - "php": "8.0.2" + "php": "8.2.0" }, - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.6.0" } diff --git a/database/factories/Activity/Models/CommentFactory.php b/database/factories/Activity/Models/CommentFactory.php index 4b76cd11dec..844bc399381 100644 --- a/database/factories/Activity/Models/CommentFactory.php +++ b/database/factories/Activity/Models/CommentFactory.php @@ -25,8 +25,10 @@ public function definition() return [ 'html' => $html, - 'text' => $text, 'parent_id' => null, + 'local_id' => 1, + 'content_ref' => '', + 'archived' => false, ]; } } diff --git a/database/factories/Entities/Models/BookFactory.php b/database/factories/Entities/Models/BookFactory.php index 9cb8e971c6e..48d43d7a8b7 100644 --- a/database/factories/Entities/Models/BookFactory.php +++ b/database/factories/Entities/Models/BookFactory.php @@ -26,7 +26,9 @@ public function definition() 'name' => $this->faker->sentence(), 'slug' => Str::random(10), 'description' => $description, - 'description_html' => '

' . e($description) . '

' + 'description_html' => '

' . e($description) . '

', + 'sort_rule_id' => null, + 'default_template_id' => null, ]; } } diff --git a/database/factories/Entities/Models/PageFactory.php b/database/factories/Entities/Models/PageFactory.php index 319d97880ca..8115700950c 100644 --- a/database/factories/Entities/Models/PageFactory.php +++ b/database/factories/Entities/Models/PageFactory.php @@ -2,6 +2,7 @@ namespace Database\Factories\Entities\Models; +use BookStack\Entities\Tools\PageEditorType; use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Support\Str; @@ -29,6 +30,7 @@ public function definition() 'html' => $html, 'text' => strip_tags($html), 'revision_count' => 1, + 'editor' => 'wysiwyg', ]; } } diff --git a/database/factories/Exports/ImportFactory.php b/database/factories/Exports/ImportFactory.php new file mode 100644 index 00000000000..cdb019dd362 --- /dev/null +++ b/database/factories/Exports/ImportFactory.php @@ -0,0 +1,32 @@ + 'uploads/files/imports/' . Str::random(10) . '.zip', + 'name' => $this->faker->words(3, true), + 'type' => 'book', + 'size' => rand(1, 1001), + 'metadata' => '{"name": "My book"}', + 'created_at' => User::factory(), + ]; + } +} diff --git a/database/factories/Sorting/SortRuleFactory.php b/database/factories/Sorting/SortRuleFactory.php new file mode 100644 index 00000000000..dafe8c3fa74 --- /dev/null +++ b/database/factories/Sorting/SortRuleFactory.php @@ -0,0 +1,30 @@ + $op->name . ' Sort', + 'sequence' => $op->value, + ]; + } +} diff --git a/database/migrations/2014_10_12_000000_create_users_table.php b/database/migrations/2014_10_12_000000_create_users_table.php index 61b73b7f29c..638394df609 100644 --- a/database/migrations/2014_10_12_000000_create_users_table.php +++ b/database/migrations/2014_10_12_000000_create_users_table.php @@ -1,16 +1,17 @@ increments('id'); @@ -26,17 +27,15 @@ public function up() 'name' => 'Admin', 'email' => 'admin@admin.com', 'password' => bcrypt('password'), - 'created_at' => \Carbon\Carbon::now()->toDateTimeString(), - 'updated_at' => \Carbon\Carbon::now()->toDateTimeString(), + 'created_at' => Carbon::now()->toDateTimeString(), + 'updated_at' => Carbon::now()->toDateTimeString(), ]); } /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::drop('users'); } diff --git a/database/migrations/2014_10_12_100000_create_password_resets_table.php b/database/migrations/2014_10_12_100000_create_password_resets_table.php index f29aff42b8e..f53370009c6 100644 --- a/database/migrations/2014_10_12_100000_create_password_resets_table.php +++ b/database/migrations/2014_10_12_100000_create_password_resets_table.php @@ -2,15 +2,14 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\Schema; return new class extends Migration { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::create('password_resets', function (Blueprint $table) { $table->string('email')->index(); @@ -21,10 +20,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::drop('password_resets'); } diff --git a/database/migrations/2015_07_12_114933_create_books_table.php b/database/migrations/2015_07_12_114933_create_books_table.php index 95916945871..98d0433650d 100644 --- a/database/migrations/2015_07_12_114933_create_books_table.php +++ b/database/migrations/2015_07_12_114933_create_books_table.php @@ -2,15 +2,14 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\Schema; return new class extends Migration { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::create('books', function (Blueprint $table) { $table->increments('id'); @@ -23,10 +22,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::drop('books'); } diff --git a/database/migrations/2015_07_12_190027_create_pages_table.php b/database/migrations/2015_07_12_190027_create_pages_table.php index d9b266813ba..8ef5fc97120 100644 --- a/database/migrations/2015_07_12_190027_create_pages_table.php +++ b/database/migrations/2015_07_12_190027_create_pages_table.php @@ -2,15 +2,14 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\Schema; return new class extends Migration { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::create('pages', function (Blueprint $table) { $table->increments('id'); @@ -27,10 +26,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::drop('pages'); } diff --git a/database/migrations/2015_07_13_172121_create_images_table.php b/database/migrations/2015_07_13_172121_create_images_table.php index 8814d079e36..ae57fc80da0 100644 --- a/database/migrations/2015_07_13_172121_create_images_table.php +++ b/database/migrations/2015_07_13_172121_create_images_table.php @@ -2,15 +2,14 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\Schema; return new class extends Migration { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::create('images', function (Blueprint $table) { $table->increments('id'); @@ -22,10 +21,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::drop('images'); } diff --git a/database/migrations/2015_07_27_172342_create_chapters_table.php b/database/migrations/2015_07_27_172342_create_chapters_table.php index bfccd6fac5a..fca92429c3d 100644 --- a/database/migrations/2015_07_27_172342_create_chapters_table.php +++ b/database/migrations/2015_07_27_172342_create_chapters_table.php @@ -2,15 +2,14 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\Schema; return new class extends Migration { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::create('chapters', function (Blueprint $table) { $table->increments('id'); @@ -25,10 +24,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::drop('chapters'); } diff --git a/database/migrations/2015_08_08_200447_add_users_to_entities.php b/database/migrations/2015_08_08_200447_add_users_to_entities.php index 8acfd360ae5..51dfafc6aa7 100644 --- a/database/migrations/2015_08_08_200447_add_users_to_entities.php +++ b/database/migrations/2015_08_08_200447_add_users_to_entities.php @@ -2,15 +2,14 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\Schema; return new class extends Migration { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::table('pages', function (Blueprint $table) { $table->integer('created_by'); @@ -32,10 +31,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::table('pages', function (Blueprint $table) { $table->dropColumn('created_by'); diff --git a/database/migrations/2015_08_09_093534_create_page_revisions_table.php b/database/migrations/2015_08_09_093534_create_page_revisions_table.php index b8264fd29e6..af9104edcd3 100644 --- a/database/migrations/2015_08_09_093534_create_page_revisions_table.php +++ b/database/migrations/2015_08_09_093534_create_page_revisions_table.php @@ -2,15 +2,14 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\Schema; return new class extends Migration { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::create('page_revisions', function (Blueprint $table) { $table->increments('id'); @@ -25,10 +24,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::drop('page_revisions'); } diff --git a/database/migrations/2015_08_16_142133_create_activities_table.php b/database/migrations/2015_08_16_142133_create_activities_table.php index 742a4a40dd1..316d52ced9f 100644 --- a/database/migrations/2015_08_16_142133_create_activities_table.php +++ b/database/migrations/2015_08_16_142133_create_activities_table.php @@ -2,15 +2,14 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\Schema; return new class extends Migration { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::create('activities', function (Blueprint $table) { $table->increments('id'); @@ -26,10 +25,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::drop('activities'); } diff --git a/database/migrations/2015_08_29_105422_add_roles_and_permissions.php b/database/migrations/2015_08_29_105422_add_roles_and_permissions.php index 517cfaf439d..a2a8ae9d69f 100644 --- a/database/migrations/2015_08_29_105422_add_roles_and_permissions.php +++ b/database/migrations/2015_08_29_105422_add_roles_and_permissions.php @@ -10,17 +10,18 @@ * @url https://github.com/Zizaco/entrust */ +use Carbon\Carbon; use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Schema; return new class extends Migration { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { // Create table for storing roles Schema::create('roles', function (Blueprint $table) { @@ -71,22 +72,22 @@ public function up() 'name' => 'admin', 'display_name' => 'Admin', 'description' => 'Administrator of the whole application', - 'created_at' => \Carbon\Carbon::now()->toDateTimeString(), - 'updated_at' => \Carbon\Carbon::now()->toDateTimeString(), + 'created_at' => Carbon::now()->toDateTimeString(), + 'updated_at' => Carbon::now()->toDateTimeString(), ]); $editorId = DB::table('roles')->insertGetId([ 'name' => 'editor', 'display_name' => 'Editor', 'description' => 'User can edit Books, Chapters & Pages', - 'created_at' => \Carbon\Carbon::now()->toDateTimeString(), - 'updated_at' => \Carbon\Carbon::now()->toDateTimeString(), + 'created_at' => Carbon::now()->toDateTimeString(), + 'updated_at' => Carbon::now()->toDateTimeString(), ]); $viewerId = DB::table('roles')->insertGetId([ 'name' => 'viewer', 'display_name' => 'Viewer', 'description' => 'User can view books & their content behind authentication', - 'created_at' => \Carbon\Carbon::now()->toDateTimeString(), - 'updated_at' => \Carbon\Carbon::now()->toDateTimeString(), + 'created_at' => Carbon::now()->toDateTimeString(), + 'updated_at' => Carbon::now()->toDateTimeString(), ]); // Create default CRUD permissions and allocate to admins and editors @@ -97,8 +98,8 @@ public function up() $newPermId = DB::table('permissions')->insertGetId([ 'name' => strtolower($entity) . '-' . strtolower($op), 'display_name' => $op . ' ' . $entity . 's', - 'created_at' => \Carbon\Carbon::now()->toDateTimeString(), - 'updated_at' => \Carbon\Carbon::now()->toDateTimeString(), + 'created_at' => Carbon::now()->toDateTimeString(), + 'updated_at' => Carbon::now()->toDateTimeString(), ]); DB::table('permission_role')->insert([ ['permission_id' => $newPermId, 'role_id' => $adminId], @@ -115,8 +116,8 @@ public function up() $newPermId = DB::table('permissions')->insertGetId([ 'name' => strtolower($entity) . '-' . strtolower($op), 'display_name' => $op . ' ' . $entity, - 'created_at' => \Carbon\Carbon::now()->toDateTimeString(), - 'updated_at' => \Carbon\Carbon::now()->toDateTimeString(), + 'created_at' => Carbon::now()->toDateTimeString(), + 'updated_at' => Carbon::now()->toDateTimeString(), ]); DB::table('permission_role')->insert([ 'permission_id' => $newPermId, @@ -138,10 +139,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::drop('permission_role'); Schema::drop('permissions'); diff --git a/database/migrations/2015_08_30_125859_create_settings_table.php b/database/migrations/2015_08_30_125859_create_settings_table.php index 45c9c08b32d..d1273bf099d 100644 --- a/database/migrations/2015_08_30_125859_create_settings_table.php +++ b/database/migrations/2015_08_30_125859_create_settings_table.php @@ -2,15 +2,14 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\Schema; return new class extends Migration { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::create('settings', function (Blueprint $table) { $table->string('setting_key')->primary()->indexed(); @@ -21,10 +20,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::drop('settings'); } diff --git a/database/migrations/2015_08_31_175240_add_search_indexes.php b/database/migrations/2015_08_31_175240_add_search_indexes.php index 6097fd13edd..13141698a9f 100644 --- a/database/migrations/2015_08_31_175240_add_search_indexes.php +++ b/database/migrations/2015_08_31_175240_add_search_indexes.php @@ -2,13 +2,13 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Schema; return new class extends Migration { /** * Run the migrations. - * - * @return void */ public function up() { @@ -23,29 +23,22 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { - $sm = Schema::getConnection()->getDoctrineSchemaManager(); - $pages = $sm->listTableDetails('pages'); - $books = $sm->listTableDetails('books'); - $chapters = $sm->listTableDetails('chapters'); - - if ($pages->hasIndex('search')) { + if (Schema::hasIndex('pages', 'search')) { Schema::table('pages', function (Blueprint $table) { $table->dropIndex('search'); }); } - if ($books->hasIndex('search')) { + if (Schema::hasIndex('books', 'search')) { Schema::table('books', function (Blueprint $table) { $table->dropIndex('search'); }); } - if ($chapters->hasIndex('search')) { + if (Schema::hasIndex('chapters', 'search')) { Schema::table('chapters', function (Blueprint $table) { $table->dropIndex('search'); }); diff --git a/database/migrations/2015_09_04_165821_create_social_accounts_table.php b/database/migrations/2015_09_04_165821_create_social_accounts_table.php index bea90113628..7a9b1a49655 100644 --- a/database/migrations/2015_09_04_165821_create_social_accounts_table.php +++ b/database/migrations/2015_09_04_165821_create_social_accounts_table.php @@ -2,15 +2,14 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\Schema; return new class extends Migration { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::create('social_accounts', function (Blueprint $table) { $table->increments('id'); @@ -24,10 +23,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::drop('social_accounts'); } diff --git a/database/migrations/2015_09_05_164707_add_email_confirmation_table.php b/database/migrations/2015_09_05_164707_add_email_confirmation_table.php index a28a5728edb..9e58493c79c 100644 --- a/database/migrations/2015_09_05_164707_add_email_confirmation_table.php +++ b/database/migrations/2015_09_05_164707_add_email_confirmation_table.php @@ -2,15 +2,14 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\Schema; return new class extends Migration { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::table('users', function (Blueprint $table) { $table->boolean('email_confirmed')->default(true); @@ -26,10 +25,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::table('users', function (Blueprint $table) { $table->dropColumn('email_confirmed'); diff --git a/database/migrations/2015_11_21_145609_create_views_table.php b/database/migrations/2015_11_21_145609_create_views_table.php index bb8c373d889..00ccc7ac317 100644 --- a/database/migrations/2015_11_21_145609_create_views_table.php +++ b/database/migrations/2015_11_21_145609_create_views_table.php @@ -2,15 +2,14 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\Schema; return new class extends Migration { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::create('views', function (Blueprint $table) { $table->increments('id'); @@ -24,10 +23,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::drop('views'); } diff --git a/database/migrations/2015_11_26_221857_add_entity_indexes.php b/database/migrations/2015_11_26_221857_add_entity_indexes.php index 3203eb8a7fd..3f18824477d 100644 --- a/database/migrations/2015_11_26_221857_add_entity_indexes.php +++ b/database/migrations/2015_11_26_221857_add_entity_indexes.php @@ -2,15 +2,14 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\Schema; return new class extends Migration { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::table('books', function (Blueprint $table) { $table->index('slug'); @@ -48,10 +47,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::table('books', function (Blueprint $table) { $table->dropIndex('books_slug_index'); diff --git a/database/migrations/2015_12_05_145049_fulltext_weighting.php b/database/migrations/2015_12_05_145049_fulltext_weighting.php index a18508c4561..3d0d21e7081 100644 --- a/database/migrations/2015_12_05_145049_fulltext_weighting.php +++ b/database/migrations/2015_12_05_145049_fulltext_weighting.php @@ -2,13 +2,13 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Schema; return new class extends Migration { /** * Run the migrations. - * - * @return void */ public function up() { @@ -23,29 +23,22 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { - $sm = Schema::getConnection()->getDoctrineSchemaManager(); - $pages = $sm->listTableDetails('pages'); - $books = $sm->listTableDetails('books'); - $chapters = $sm->listTableDetails('chapters'); - - if ($pages->hasIndex('name_search')) { + if (Schema::hasIndex('pages', 'name_search')) { Schema::table('pages', function (Blueprint $table) { $table->dropIndex('name_search'); }); } - if ($books->hasIndex('name_search')) { + if (Schema::hasIndex('books', 'name_search')) { Schema::table('books', function (Blueprint $table) { $table->dropIndex('name_search'); }); } - if ($chapters->hasIndex('name_search')) { + if (Schema::hasIndex('chapters', 'name_search')) { Schema::table('chapters', function (Blueprint $table) { $table->dropIndex('name_search'); }); diff --git a/database/migrations/2015_12_07_195238_add_image_upload_types.php b/database/migrations/2015_12_07_195238_add_image_upload_types.php index c9f9675c713..ce6e5d6b61a 100644 --- a/database/migrations/2015_12_07_195238_add_image_upload_types.php +++ b/database/migrations/2015_12_07_195238_add_image_upload_types.php @@ -3,15 +3,14 @@ use BookStack\Uploads\Image; use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\Schema; return new class extends Migration { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::table('images', function (Blueprint $table) { $table->string('path', 400); @@ -27,10 +26,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::table('images', function (Blueprint $table) { $table->dropColumn('type'); diff --git a/database/migrations/2015_12_09_195748_add_user_avatars.php b/database/migrations/2015_12_09_195748_add_user_avatars.php index 950526ddc58..dd8bcf0b49e 100644 --- a/database/migrations/2015_12_09_195748_add_user_avatars.php +++ b/database/migrations/2015_12_09_195748_add_user_avatars.php @@ -2,15 +2,14 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\Schema; return new class extends Migration { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::table('users', function (Blueprint $table) { $table->integer('image_id')->default(0); @@ -19,10 +18,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::table('users', function (Blueprint $table) { $table->dropColumn('image_id'); diff --git a/database/migrations/2016_01_11_210908_add_external_auth_to_users.php b/database/migrations/2016_01_11_210908_add_external_auth_to_users.php index ff889d867df..413a8a29d95 100644 --- a/database/migrations/2016_01_11_210908_add_external_auth_to_users.php +++ b/database/migrations/2016_01_11_210908_add_external_auth_to_users.php @@ -2,15 +2,14 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\Schema; return new class extends Migration { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::table('users', function (Blueprint $table) { $table->string('external_auth_id')->index(); @@ -19,10 +18,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::table('users', function (Blueprint $table) { $table->dropColumn('external_auth_id'); diff --git a/database/migrations/2016_02_25_184030_add_slug_to_revisions.php b/database/migrations/2016_02_25_184030_add_slug_to_revisions.php index a083e10b580..86a08f2c8ab 100644 --- a/database/migrations/2016_02_25_184030_add_slug_to_revisions.php +++ b/database/migrations/2016_02_25_184030_add_slug_to_revisions.php @@ -2,15 +2,14 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\Schema; return new class extends Migration { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::table('page_revisions', function (Blueprint $table) { $table->string('slug'); @@ -22,10 +21,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::table('page_revisions', function (Blueprint $table) { $table->dropColumn('slug'); diff --git a/database/migrations/2016_02_27_120329_update_permissions_and_roles.php b/database/migrations/2016_02_27_120329_update_permissions_and_roles.php index dd62301d6f3..c0d86086d92 100644 --- a/database/migrations/2016_02_27_120329_update_permissions_and_roles.php +++ b/database/migrations/2016_02_27_120329_update_permissions_and_roles.php @@ -1,15 +1,15 @@ where('name', '=', 'admin')->first()->id; @@ -30,8 +30,8 @@ public function up() $permissionId = DB::table('permissions')->insertGetId([ 'name' => $name, 'display_name' => $displayName, - 'created_at' => \Carbon\Carbon::now()->toDateTimeString(), - 'updated_at' => \Carbon\Carbon::now()->toDateTimeString(), + 'created_at' => Carbon::now()->toDateTimeString(), + 'updated_at' => Carbon::now()->toDateTimeString(), ]); DB::table('permission_role')->insert([ 'role_id' => $adminRoleId, @@ -47,8 +47,8 @@ public function up() $permissionId = DB::table('permissions')->insertGetId([ 'name' => strtolower($entity) . '-' . strtolower(str_replace(' ', '-', $op)), 'display_name' => $op . ' ' . $entity . 's', - 'created_at' => \Carbon\Carbon::now()->toDateTimeString(), - 'updated_at' => \Carbon\Carbon::now()->toDateTimeString(), + 'created_at' => Carbon::now()->toDateTimeString(), + 'updated_at' => Carbon::now()->toDateTimeString(), ]); DB::table('permission_role')->insert([ 'role_id' => $adminRoleId, @@ -66,10 +66,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { // Get roles with permissions we need to change $adminRoleId = DB::table('roles')->where('name', '=', 'admin')->first()->id; @@ -85,8 +83,8 @@ public function down() $permissionId = DB::table('permissions')->insertGetId([ 'name' => strtolower($entity) . '-' . strtolower($op), 'display_name' => $op . ' ' . $entity . 's', - 'created_at' => \Carbon\Carbon::now()->toDateTimeString(), - 'updated_at' => \Carbon\Carbon::now()->toDateTimeString(), + 'created_at' => Carbon::now()->toDateTimeString(), + 'updated_at' => Carbon::now()->toDateTimeString(), ]); DB::table('permission_role')->insert([ 'role_id' => $adminRoleId, @@ -103,8 +101,8 @@ public function down() $permissionId = DB::table('permissions')->insertGetId([ 'name' => strtolower($entity) . '-' . strtolower($op), 'display_name' => $op . ' ' . $entity, - 'created_at' => \Carbon\Carbon::now()->toDateTimeString(), - 'updated_at' => \Carbon\Carbon::now()->toDateTimeString(), + 'created_at' => Carbon::now()->toDateTimeString(), + 'updated_at' => Carbon::now()->toDateTimeString(), ]); DB::table('permission_role')->insert([ 'role_id' => $adminRoleId, diff --git a/database/migrations/2016_02_28_084200_add_entity_access_controls.php b/database/migrations/2016_02_28_084200_add_entity_access_controls.php index 7a796e728d2..cf8ba5f3027 100644 --- a/database/migrations/2016_02_28_084200_add_entity_access_controls.php +++ b/database/migrations/2016_02_28_084200_add_entity_access_controls.php @@ -2,15 +2,14 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\Schema; return new class extends Migration { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::table('images', function (Blueprint $table) { $table->integer('uploaded_to')->default(0); @@ -46,10 +45,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::table('images', function (Blueprint $table) { $table->dropColumn('uploaded_to'); diff --git a/database/migrations/2016_03_09_203143_add_page_revision_types.php b/database/migrations/2016_03_09_203143_add_page_revision_types.php index 2fc63043724..566f2fdeb1f 100644 --- a/database/migrations/2016_03_09_203143_add_page_revision_types.php +++ b/database/migrations/2016_03_09_203143_add_page_revision_types.php @@ -2,15 +2,14 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\Schema; return new class extends Migration { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::table('page_revisions', function (Blueprint $table) { $table->string('type')->default('version'); @@ -20,10 +19,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::table('page_revisions', function (Blueprint $table) { $table->dropColumn('type'); diff --git a/database/migrations/2016_03_13_082138_add_page_drafts.php b/database/migrations/2016_03_13_082138_add_page_drafts.php index ce597963215..a8690513f67 100644 --- a/database/migrations/2016_03_13_082138_add_page_drafts.php +++ b/database/migrations/2016_03_13_082138_add_page_drafts.php @@ -2,15 +2,14 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\Schema; return new class extends Migration { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::table('pages', function (Blueprint $table) { $table->boolean('draft')->default(false); @@ -20,10 +19,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::table('pages', function (Blueprint $table) { $table->dropColumn('draft'); diff --git a/database/migrations/2016_03_25_123157_add_markdown_support.php b/database/migrations/2016_03_25_123157_add_markdown_support.php index a89fc273d78..f0b42b645e9 100644 --- a/database/migrations/2016_03_25_123157_add_markdown_support.php +++ b/database/migrations/2016_03_25_123157_add_markdown_support.php @@ -2,15 +2,14 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\Schema; return new class extends Migration { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::table('pages', function (Blueprint $table) { $table->longText('markdown')->default(''); @@ -23,10 +22,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::table('pages', function (Blueprint $table) { $table->dropColumn('markdown'); diff --git a/database/migrations/2016_04_09_100730_add_view_permissions_to_roles.php b/database/migrations/2016_04_09_100730_add_view_permissions_to_roles.php index 411c240074c..481ac785f28 100644 --- a/database/migrations/2016_04_09_100730_add_view_permissions_to_roles.php +++ b/database/migrations/2016_04_09_100730_add_view_permissions_to_roles.php @@ -1,15 +1,15 @@ get(); @@ -21,8 +21,8 @@ public function up() $permId = DB::table('permissions')->insertGetId([ 'name' => strtolower($entity) . '-' . strtolower(str_replace(' ', '-', $op)), 'display_name' => $op . ' ' . $entity . 's', - 'created_at' => \Carbon\Carbon::now()->toDateTimeString(), - 'updated_at' => \Carbon\Carbon::now()->toDateTimeString(), + 'created_at' => Carbon::now()->toDateTimeString(), + 'updated_at' => Carbon::now()->toDateTimeString(), ]); // Assign view permission to all current roles foreach ($currentRoles as $role) { @@ -37,10 +37,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { // Delete the new view permission $entities = ['Book', 'Page', 'Chapter']; diff --git a/database/migrations/2016_04_20_192649_create_joint_permissions_table.php b/database/migrations/2016_04_20_192649_create_joint_permissions_table.php index 745e156bc20..ba9f615e47e 100644 --- a/database/migrations/2016_04_20_192649_create_joint_permissions_table.php +++ b/database/migrations/2016_04_20_192649_create_joint_permissions_table.php @@ -1,17 +1,18 @@ increments('id'); @@ -48,8 +49,8 @@ public function up() 'description' => 'The role given to public visitors if allowed', 'system_name' => 'public', 'hidden' => true, - 'created_at' => \Carbon\Carbon::now()->toDateTimeString(), - 'updated_at' => \Carbon\Carbon::now()->toDateTimeString(), + 'created_at' => Carbon::now()->toDateTimeString(), + 'updated_at' => Carbon::now()->toDateTimeString(), ]; // Ensure unique name @@ -79,10 +80,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::drop('joint_permissions'); diff --git a/database/migrations/2016_05_06_185215_create_tags_table.php b/database/migrations/2016_05_06_185215_create_tags_table.php index b065a052f68..1be9a389f1c 100644 --- a/database/migrations/2016_05_06_185215_create_tags_table.php +++ b/database/migrations/2016_05_06_185215_create_tags_table.php @@ -2,15 +2,14 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\Schema; return new class extends Migration { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::create('tags', function (Blueprint $table) { $table->increments('id'); @@ -30,10 +29,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::drop('tags'); } diff --git a/database/migrations/2016_07_07_181521_add_summary_to_page_revisions.php b/database/migrations/2016_07_07_181521_add_summary_to_page_revisions.php index 03942b17a39..562e6415f36 100644 --- a/database/migrations/2016_07_07_181521_add_summary_to_page_revisions.php +++ b/database/migrations/2016_07_07_181521_add_summary_to_page_revisions.php @@ -1,15 +1,14 @@ string('summary')->nullable(); @@ -18,10 +17,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::table('page_revisions', function ($table) { $table->dropColumn('summary'); diff --git a/database/migrations/2016_09_29_101449_remove_hidden_roles.php b/database/migrations/2016_09_29_101449_remove_hidden_roles.php index 2f729673ffa..fa0a5fe9270 100644 --- a/database/migrations/2016_09_29_101449_remove_hidden_roles.php +++ b/database/migrations/2016_09_29_101449_remove_hidden_roles.php @@ -1,17 +1,17 @@ 'Guest', 'system_name' => 'public', 'email_confirmed' => true, - 'created_at' => \Carbon\Carbon::now(), - 'updated_at' => \Carbon\Carbon::now(), + 'created_at' => Carbon::now(), + 'updated_at' => Carbon::now(), ]); // Get the public role @@ -45,10 +45,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::table('roles', function (Blueprint $table) { $table->boolean('hidden')->default(false); diff --git a/database/migrations/2016_10_09_142037_create_attachments_table.php b/database/migrations/2016_10_09_142037_create_attachments_table.php index 41db5723d0a..278051c8be1 100644 --- a/database/migrations/2016_10_09_142037_create_attachments_table.php +++ b/database/migrations/2016_10_09_142037_create_attachments_table.php @@ -1,17 +1,17 @@ increments('id'); @@ -40,8 +40,8 @@ public function up() $permissionId = DB::table('role_permissions')->insertGetId([ 'name' => strtolower($entity) . '-' . strtolower(str_replace(' ', '-', $op)), 'display_name' => $op . ' ' . $entity . 's', - 'created_at' => \Carbon\Carbon::now()->toDateTimeString(), - 'updated_at' => \Carbon\Carbon::now()->toDateTimeString(), + 'created_at' => Carbon::now()->toDateTimeString(), + 'updated_at' => Carbon::now()->toDateTimeString(), ]); DB::table('permission_role')->insert([ 'role_id' => $adminRoleId, @@ -52,10 +52,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::dropIfExists('attachments'); diff --git a/database/migrations/2017_01_21_163556_create_cache_table.php b/database/migrations/2017_01_21_163556_create_cache_table.php index abff912f20a..7c07c0cb8be 100644 --- a/database/migrations/2017_01_21_163556_create_cache_table.php +++ b/database/migrations/2017_01_21_163556_create_cache_table.php @@ -8,10 +8,8 @@ { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::create('cache', function (Blueprint $table) { $table->string('key')->unique(); @@ -22,10 +20,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::dropIfExists('cache'); } diff --git a/database/migrations/2017_01_21_163602_create_sessions_table.php b/database/migrations/2017_01_21_163602_create_sessions_table.php index c32838a8cc9..3f52752c234 100644 --- a/database/migrations/2017_01_21_163602_create_sessions_table.php +++ b/database/migrations/2017_01_21_163602_create_sessions_table.php @@ -8,10 +8,8 @@ { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::create('sessions', function (Blueprint $table) { $table->string('id')->unique(); @@ -25,10 +23,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::dropIfExists('sessions'); } diff --git a/database/migrations/2017_03_19_091553_create_search_index_table.php b/database/migrations/2017_03_19_091553_create_search_index_table.php index 03a63392afd..7f96aca6b33 100644 --- a/database/migrations/2017_03_19_091553_create_search_index_table.php +++ b/database/migrations/2017_03_19_091553_create_search_index_table.php @@ -2,16 +2,15 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Schema; return new class extends Migration { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::create('search_terms', function (Blueprint $table) { $table->increments('id'); @@ -26,26 +25,21 @@ public function up() $table->index('score'); }); - $sm = Schema::getConnection()->getDoctrineSchemaManager(); - $pages = $sm->listTableDetails('pages'); - $books = $sm->listTableDetails('books'); - $chapters = $sm->listTableDetails('chapters'); - - if ($pages->hasIndex('search')) { + if (Schema::hasIndex('pages', 'search')) { Schema::table('pages', function (Blueprint $table) { $table->dropIndex('search'); $table->dropIndex('name_search'); }); } - if ($books->hasIndex('search')) { + if (Schema::hasIndex('books', 'search')) { Schema::table('books', function (Blueprint $table) { $table->dropIndex('search'); $table->dropIndex('name_search'); }); } - if ($chapters->hasIndex('search')) { + if (Schema::hasIndex('chapters', 'search')) { Schema::table('chapters', function (Blueprint $table) { $table->dropIndex('search'); $table->dropIndex('name_search'); @@ -55,10 +49,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { // This was removed for v0.24 since these indexes are removed anyway // and will cause issues for db engines that don't support such indexes. diff --git a/database/migrations/2017_04_20_185112_add_revision_counts.php b/database/migrations/2017_04_20_185112_add_revision_counts.php index 891ff8e0a1d..182d95c7597 100644 --- a/database/migrations/2017_04_20_185112_add_revision_counts.php +++ b/database/migrations/2017_04_20_185112_add_revision_counts.php @@ -2,16 +2,15 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Schema; return new class extends Migration { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::table('pages', function (Blueprint $table) { $table->integer('revision_count'); @@ -29,10 +28,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::table('pages', function (Blueprint $table) { $table->dropColumn('revision_count'); diff --git a/database/migrations/2017_07_02_152834_update_db_encoding_to_ut8mb4.php b/database/migrations/2017_07_02_152834_update_db_encoding_to_ut8mb4.php index 7805be700bd..178cb584f9a 100644 --- a/database/migrations/2017_07_02_152834_update_db_encoding_to_ut8mb4.php +++ b/database/migrations/2017_07_02_152834_update_db_encoding_to_ut8mb4.php @@ -6,8 +6,6 @@ { /** * Run the migrations. - * - * @return void */ public function up() { @@ -18,8 +16,6 @@ public function up() /** * Reverse the migrations. - * - * @return void */ public function down() { diff --git a/database/migrations/2017_08_01_130541_create_comments_table.php b/database/migrations/2017_08_01_130541_create_comments_table.php index f654ebf6596..d1478547616 100644 --- a/database/migrations/2017_08_01_130541_create_comments_table.php +++ b/database/migrations/2017_08_01_130541_create_comments_table.php @@ -1,17 +1,17 @@ increments('id')->unsigned(); @@ -37,8 +37,8 @@ public function up() $permissionId = DB::table('role_permissions')->insertGetId([ 'name' => strtolower($entity) . '-' . strtolower(str_replace(' ', '-', $op)), 'display_name' => $op . ' ' . $entity . 's', - 'created_at' => \Carbon\Carbon::now()->toDateTimeString(), - 'updated_at' => \Carbon\Carbon::now()->toDateTimeString(), + 'created_at' => Carbon::now()->toDateTimeString(), + 'updated_at' => Carbon::now()->toDateTimeString(), ]); DB::table('permission_role')->insert([ 'role_id' => $adminRoleId, @@ -50,10 +50,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::dropIfExists('comments'); // Delete comment role permissions diff --git a/database/migrations/2017_08_29_102650_add_cover_image_display.php b/database/migrations/2017_08_29_102650_add_cover_image_display.php index 5ba3145f2b0..0f7b7d82a9a 100644 --- a/database/migrations/2017_08_29_102650_add_cover_image_display.php +++ b/database/migrations/2017_08_29_102650_add_cover_image_display.php @@ -8,10 +8,8 @@ { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::table('books', function (Blueprint $table) { $table->integer('image_id')->nullable()->default(null); @@ -20,10 +18,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::table('books', function (Blueprint $table) { $table->dropColumn('image_id'); diff --git a/database/migrations/2018_07_15_173514_add_role_external_auth_id.php b/database/migrations/2018_07_15_173514_add_role_external_auth_id.php index eb2bf91fc83..58694765655 100644 --- a/database/migrations/2018_07_15_173514_add_role_external_auth_id.php +++ b/database/migrations/2018_07_15_173514_add_role_external_auth_id.php @@ -8,10 +8,8 @@ { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::table('roles', function (Blueprint $table) { $table->string('external_auth_id', 180)->default(''); @@ -21,10 +19,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::table('roles', function (Blueprint $table) { $table->dropColumn('external_auth_id'); diff --git a/database/migrations/2018_08_04_115700_create_bookshelves_table.php b/database/migrations/2018_08_04_115700_create_bookshelves_table.php index bd3478bc7c8..54ccfb6baea 100644 --- a/database/migrations/2018_08_04_115700_create_bookshelves_table.php +++ b/database/migrations/2018_08_04_115700_create_bookshelves_table.php @@ -1,5 +1,6 @@ insertGetId([ 'name' => 'bookshelf-' . $dbOpName, 'display_name' => $op . ' ' . 'BookShelves', - 'created_at' => \Carbon\Carbon::now()->toDateTimeString(), - 'updated_at' => \Carbon\Carbon::now()->toDateTimeString(), + 'created_at' => Carbon::now()->toDateTimeString(), + 'updated_at' => Carbon::now()->toDateTimeString(), ]); $rowsToInsert = $roleIdsWithBookPermission->filter(function ($roleId) { @@ -103,10 +102,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { // Drop created permissions $ops = ['bookshelf-create-all', 'bookshelf-create-own', 'bookshelf-delete-all', 'bookshelf-delete-own', 'bookshelf-update-all', 'bookshelf-update-own', 'bookshelf-view-all', 'bookshelf-view-own']; diff --git a/database/migrations/2019_07_07_112515_add_template_support.php b/database/migrations/2019_07_07_112515_add_template_support.php index 423e02827f5..cabde549e83 100644 --- a/database/migrations/2019_07_07_112515_add_template_support.php +++ b/database/migrations/2019_07_07_112515_add_template_support.php @@ -3,16 +3,15 @@ use Carbon\Carbon; use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Schema; return new class extends Migration { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::table('pages', function (Blueprint $table) { $table->boolean('template')->default(false); @@ -35,10 +34,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::table('pages', function (Blueprint $table) { $table->dropColumn('template'); diff --git a/database/migrations/2019_08_17_140214_add_user_invites_table.php b/database/migrations/2019_08_17_140214_add_user_invites_table.php index ce88a28949b..1ab4c72f8aa 100644 --- a/database/migrations/2019_08_17_140214_add_user_invites_table.php +++ b/database/migrations/2019_08_17_140214_add_user_invites_table.php @@ -8,10 +8,8 @@ { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::create('user_invites', function (Blueprint $table) { $table->increments('id'); @@ -23,10 +21,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::dropIfExists('user_invites'); } diff --git a/database/migrations/2019_12_29_120917_add_api_auth.php b/database/migrations/2019_12_29_120917_add_api_auth.php index 2cf970a8aee..99ce8d27c49 100644 --- a/database/migrations/2019_12_29_120917_add_api_auth.php +++ b/database/migrations/2019_12_29_120917_add_api_auth.php @@ -3,16 +3,15 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Carbon; +use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Schema; return new class extends Migration { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { // Add API tokens table @@ -42,10 +41,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { // Remove API tokens table Schema::dropIfExists('api_tokens'); diff --git a/database/migrations/2020_08_04_111754_drop_joint_permissions_id.php b/database/migrations/2020_08_04_111754_drop_joint_permissions_id.php index 902d3c227b6..882001cad5a 100644 --- a/database/migrations/2020_08_04_111754_drop_joint_permissions_id.php +++ b/database/migrations/2020_08_04_111754_drop_joint_permissions_id.php @@ -8,10 +8,8 @@ { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::table('joint_permissions', function (Blueprint $table) { $table->dropColumn('id'); @@ -21,10 +19,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::table('joint_permissions', function (Blueprint $table) { $table->dropPrimary(['role_id', 'entity_type', 'entity_id', 'action']); diff --git a/database/migrations/2020_08_04_131052_remove_role_name_field.php b/database/migrations/2020_08_04_131052_remove_role_name_field.php index cd9f64697c7..10800e11fa4 100644 --- a/database/migrations/2020_08_04_131052_remove_role_name_field.php +++ b/database/migrations/2020_08_04_131052_remove_role_name_field.php @@ -9,10 +9,8 @@ { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::table('roles', function (Blueprint $table) { $table->dropColumn('name'); @@ -21,10 +19,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::table('roles', function (Blueprint $table) { $table->string('name')->index(); diff --git a/database/migrations/2020_09_19_094251_add_activity_indexes.php b/database/migrations/2020_09_19_094251_add_activity_indexes.php index f7f35b62b9a..ca1afb1daa9 100644 --- a/database/migrations/2020_09_19_094251_add_activity_indexes.php +++ b/database/migrations/2020_09_19_094251_add_activity_indexes.php @@ -8,10 +8,8 @@ { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::table('activities', function (Blueprint $table) { $table->index('key'); @@ -21,10 +19,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::table('activities', function (Blueprint $table) { $table->dropIndex('activities_key_index'); diff --git a/database/migrations/2020_09_27_210059_add_entity_soft_deletes.php b/database/migrations/2020_09_27_210059_add_entity_soft_deletes.php index 9cc12acdce3..bc6c3f18f59 100644 --- a/database/migrations/2020_09_27_210059_add_entity_soft_deletes.php +++ b/database/migrations/2020_09_27_210059_add_entity_soft_deletes.php @@ -8,10 +8,8 @@ { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::table('bookshelves', function (Blueprint $table) { $table->softDeletes(); @@ -29,10 +27,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::table('bookshelves', function (Blueprint $table) { $table->dropSoftDeletes(); diff --git a/database/migrations/2020_09_27_210528_create_deletions_table.php b/database/migrations/2020_09_27_210528_create_deletions_table.php index 3b1098175e9..96c6c0b0dee 100644 --- a/database/migrations/2020_09_27_210528_create_deletions_table.php +++ b/database/migrations/2020_09_27_210528_create_deletions_table.php @@ -8,10 +8,8 @@ { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::create('deletions', function (Blueprint $table) { $table->increments('id'); @@ -28,10 +26,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::dropIfExists('deletions'); } diff --git a/database/migrations/2020_11_07_232321_simplify_activities_table.php b/database/migrations/2020_11_07_232321_simplify_activities_table.php index 6ebe3fad015..c128a3235f7 100644 --- a/database/migrations/2020_11_07_232321_simplify_activities_table.php +++ b/database/migrations/2020_11_07_232321_simplify_activities_table.php @@ -9,10 +9,8 @@ { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::table('activities', function (Blueprint $table) { $table->renameColumn('key', 'type'); @@ -32,10 +30,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { DB::table('activities') ->whereNull('entity_id') diff --git a/database/migrations/2020_12_30_173528_add_owned_by_field_to_entities.php b/database/migrations/2020_12_30_173528_add_owned_by_field_to_entities.php index d9fab7f02dd..965a061d834 100644 --- a/database/migrations/2020_12_30_173528_add_owned_by_field_to_entities.php +++ b/database/migrations/2020_12_30_173528_add_owned_by_field_to_entities.php @@ -9,10 +9,8 @@ { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { $tables = ['pages', 'books', 'chapters', 'bookshelves']; foreach ($tables as $table) { @@ -30,10 +28,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { $tables = ['pages', 'books', 'chapters', 'bookshelves']; foreach ($tables as $table) { diff --git a/database/migrations/2021_01_30_225441_add_settings_type_column.php b/database/migrations/2021_01_30_225441_add_settings_type_column.php index 5d83d15f19a..367b93a89c8 100644 --- a/database/migrations/2021_01_30_225441_add_settings_type_column.php +++ b/database/migrations/2021_01_30_225441_add_settings_type_column.php @@ -8,10 +8,8 @@ { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::table('settings', function (Blueprint $table) { $table->string('type', 50)->default('string'); @@ -20,10 +18,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::table('settings', function (Blueprint $table) { $table->dropColumn('type'); diff --git a/database/migrations/2021_03_08_215138_add_user_slug.php b/database/migrations/2021_03_08_215138_add_user_slug.php index c0e1313a714..af01d667251 100644 --- a/database/migrations/2021_03_08_215138_add_user_slug.php +++ b/database/migrations/2021_03_08_215138_add_user_slug.php @@ -2,6 +2,7 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Schema; use Illuminate\Support\Str; @@ -9,10 +10,8 @@ { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::table('users', function (Blueprint $table) { $table->string('slug', 180); @@ -38,10 +37,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::table('users', function (Blueprint $table) { $table->dropColumn('slug'); diff --git a/database/migrations/2021_05_15_173110_create_favourites_table.php b/database/migrations/2021_05_15_173110_create_favourites_table.php index cdd7f4f6485..73fa1b24be5 100644 --- a/database/migrations/2021_05_15_173110_create_favourites_table.php +++ b/database/migrations/2021_05_15_173110_create_favourites_table.php @@ -8,10 +8,8 @@ { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::create('favourites', function (Blueprint $table) { $table->increments('id'); @@ -26,10 +24,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::dropIfExists('favourites'); } diff --git a/database/migrations/2021_06_30_173111_create_mfa_values_table.php b/database/migrations/2021_06_30_173111_create_mfa_values_table.php index d145bf4ae9a..ea45b8ce124 100644 --- a/database/migrations/2021_06_30_173111_create_mfa_values_table.php +++ b/database/migrations/2021_06_30_173111_create_mfa_values_table.php @@ -8,10 +8,8 @@ { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::create('mfa_values', function (Blueprint $table) { $table->increments('id'); @@ -24,10 +22,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::dropIfExists('mfa_values'); } diff --git a/database/migrations/2021_07_03_085038_add_mfa_enforced_to_roles_table.php b/database/migrations/2021_07_03_085038_add_mfa_enforced_to_roles_table.php index 8a180f08854..b8f8b1cff21 100644 --- a/database/migrations/2021_07_03_085038_add_mfa_enforced_to_roles_table.php +++ b/database/migrations/2021_07_03_085038_add_mfa_enforced_to_roles_table.php @@ -8,10 +8,8 @@ { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::table('roles', function (Blueprint $table) { $table->boolean('mfa_enforced'); @@ -20,10 +18,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::table('roles', function (Blueprint $table) { $table->dropColumn('mfa_enforced'); diff --git a/database/migrations/2021_08_28_161743_add_export_role_permission.php b/database/migrations/2021_08_28_161743_add_export_role_permission.php index 3bacab20b4e..99416f9fcf6 100644 --- a/database/migrations/2021_08_28_161743_add_export_role_permission.php +++ b/database/migrations/2021_08_28_161743_add_export_role_permission.php @@ -8,13 +8,10 @@ { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { - // Create new templates-manage permission and assign to admin role - $roles = DB::table('roles')->get('id'); + // Create new content-export permission $permissionId = DB::table('role_permissions')->insertGetId([ 'name' => 'content-export', 'display_name' => 'Export Content', @@ -22,6 +19,7 @@ public function up() 'updated_at' => Carbon::now()->toDateTimeString(), ]); + $roles = DB::table('roles')->get('id'); $permissionRoles = $roles->map(function ($role) use ($permissionId) { return [ 'role_id' => $role->id, @@ -29,21 +27,20 @@ public function up() ]; })->values()->toArray(); + // Assign to all existing roles in the system DB::table('permission_role')->insert($permissionRoles); } /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { // Remove content-export permission $contentExportPermission = DB::table('role_permissions') ->where('name', '=', 'content-export')->first(); DB::table('permission_role')->where('permission_id', '=', $contentExportPermission->id)->delete(); - DB::table('role_permissions')->where('id', '=', 'content-export')->delete(); + DB::table('role_permissions')->where('id', '=', $contentExportPermission->id)->delete(); } }; diff --git a/database/migrations/2021_09_26_044614_add_activities_ip_column.php b/database/migrations/2021_09_26_044614_add_activities_ip_column.php index 5f8a9500276..6c21f4b2629 100644 --- a/database/migrations/2021_09_26_044614_add_activities_ip_column.php +++ b/database/migrations/2021_09_26_044614_add_activities_ip_column.php @@ -8,10 +8,8 @@ { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::table('activities', function (Blueprint $table) { $table->string('ip', 45)->after('user_id'); @@ -20,10 +18,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::table('activities', function (Blueprint $table) { $table->dropColumn('ip'); diff --git a/database/migrations/2021_11_26_070438_add_index_for_user_ip.php b/database/migrations/2021_11_26_070438_add_index_for_user_ip.php index 564e68a6de0..9a0171bde11 100644 --- a/database/migrations/2021_11_26_070438_add_index_for_user_ip.php +++ b/database/migrations/2021_11_26_070438_add_index_for_user_ip.php @@ -8,10 +8,8 @@ { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::table('activities', function (Blueprint $table) { $table->index('ip', 'activities_ip_index'); @@ -20,10 +18,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::table('activities', function (Blueprint $table) { $table->dropIndex('activities_ip_index'); diff --git a/database/migrations/2021_12_07_111343_create_webhooks_table.php b/database/migrations/2021_12_07_111343_create_webhooks_table.php index 15d4851d68f..69be2273db0 100644 --- a/database/migrations/2021_12_07_111343_create_webhooks_table.php +++ b/database/migrations/2021_12_07_111343_create_webhooks_table.php @@ -8,10 +8,8 @@ { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::create('webhooks', function (Blueprint $table) { $table->increments('id'); @@ -37,10 +35,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::dropIfExists('webhooks'); Schema::dropIfExists('webhook_tracked_events'); diff --git a/database/migrations/2021_12_13_152024_create_jobs_table.php b/database/migrations/2021_12_13_152024_create_jobs_table.php index a786a891006..6098d9b123f 100644 --- a/database/migrations/2021_12_13_152024_create_jobs_table.php +++ b/database/migrations/2021_12_13_152024_create_jobs_table.php @@ -8,10 +8,8 @@ { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::create('jobs', function (Blueprint $table) { $table->bigIncrements('id'); @@ -26,10 +24,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::dropIfExists('jobs'); } diff --git a/database/migrations/2021_12_13_152120_create_failed_jobs_table.php b/database/migrations/2021_12_13_152120_create_failed_jobs_table.php index 17191986b47..249da8171ac 100644 --- a/database/migrations/2021_12_13_152120_create_failed_jobs_table.php +++ b/database/migrations/2021_12_13_152120_create_failed_jobs_table.php @@ -8,10 +8,8 @@ { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::create('failed_jobs', function (Blueprint $table) { $table->id(); @@ -26,10 +24,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::dropIfExists('failed_jobs'); } diff --git a/database/migrations/2022_01_03_154041_add_webhooks_timeout_error_columns.php b/database/migrations/2022_01_03_154041_add_webhooks_timeout_error_columns.php index cb26fa26aea..968568d3362 100644 --- a/database/migrations/2022_01_03_154041_add_webhooks_timeout_error_columns.php +++ b/database/migrations/2022_01_03_154041_add_webhooks_timeout_error_columns.php @@ -8,10 +8,8 @@ { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::table('webhooks', function (Blueprint $table) { $table->unsignedInteger('timeout')->default(3); @@ -23,10 +21,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::table('webhooks', function (Blueprint $table) { $table->dropColumn('timeout'); diff --git a/database/migrations/2022_04_17_101741_add_editor_change_field_and_permission.php b/database/migrations/2022_04_17_101741_add_editor_change_field_and_permission.php index 17ed0fba064..568e2651264 100644 --- a/database/migrations/2022_04_17_101741_add_editor_change_field_and_permission.php +++ b/database/migrations/2022_04_17_101741_add_editor_change_field_and_permission.php @@ -10,10 +10,8 @@ { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { // Add the new 'editor' column to the pages table Schema::table('pages', function (Blueprint $table) { @@ -46,10 +44,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { // Drop the new column from the pages table Schema::table('pages', function (Blueprint $table) { diff --git a/database/migrations/2022_04_25_140741_update_polymorphic_types.php b/database/migrations/2022_04_25_140741_update_polymorphic_types.php index 2ec45e7d188..f201d41ccc6 100644 --- a/database/migrations/2022_04_25_140741_update_polymorphic_types.php +++ b/database/migrations/2022_04_25_140741_update_polymorphic_types.php @@ -8,7 +8,7 @@ /** * Mapping of old polymorphic types to new simpler values. */ - protected $changeMap = [ + protected array $changeMap = [ 'BookStack\\Bookshelf' => 'bookshelf', 'BookStack\\Book' => 'book', 'BookStack\\Chapter' => 'chapter', @@ -18,7 +18,7 @@ /** * Mapping of tables and columns that contain polymorphic types. */ - protected $columnsByTable = [ + protected array $columnsByTable = [ 'activities' => 'entity_type', 'comments' => 'entity_type', 'deletions' => 'deletable_type', @@ -32,10 +32,8 @@ /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { foreach ($this->columnsByTable as $table => $column) { foreach ($this->changeMap as $oldVal => $newVal) { @@ -48,10 +46,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { foreach ($this->columnsByTable as $table => $column) { foreach ($this->changeMap as $oldVal => $newVal) { diff --git a/database/migrations/2022_07_16_170051_drop_joint_permission_type.php b/database/migrations/2022_07_16_170051_drop_joint_permission_type.php index ac52af2a6d6..a05290e8ae8 100644 --- a/database/migrations/2022_07_16_170051_drop_joint_permission_type.php +++ b/database/migrations/2022_07_16_170051_drop_joint_permission_type.php @@ -9,10 +9,8 @@ { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { DB::table('joint_permissions') ->where('action', '!=', 'view') @@ -27,10 +25,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::table('joint_permissions', function (Blueprint $table) { $table->string('action'); diff --git a/database/migrations/2022_08_17_092941_create_references_table.php b/database/migrations/2022_08_17_092941_create_references_table.php index 4b688c43e88..e2580289d5e 100644 --- a/database/migrations/2022_08_17_092941_create_references_table.php +++ b/database/migrations/2022_08_17_092941_create_references_table.php @@ -8,10 +8,8 @@ { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::create('references', function (Blueprint $table) { $table->id(); @@ -24,10 +22,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::dropIfExists('references'); } diff --git a/database/migrations/2022_09_02_082910_fix_shelf_cover_image_types.php b/database/migrations/2022_09_02_082910_fix_shelf_cover_image_types.php index b8c23671dc1..aecb4bca397 100644 --- a/database/migrations/2022_09_02_082910_fix_shelf_cover_image_types.php +++ b/database/migrations/2022_09_02_082910_fix_shelf_cover_image_types.php @@ -7,10 +7,8 @@ { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { // This updates the 'type' field for images, uploaded as shelf cover images, // to be cover_bookshelf instead of cover_book. @@ -32,10 +30,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { DB::table('images') ->where('type', '=', 'cover_bookshelf') diff --git a/database/migrations/2022_10_07_091406_flatten_entity_permissions_table.php b/database/migrations/2022_10_07_091406_flatten_entity_permissions_table.php index f1072102f87..a086796e9f1 100644 --- a/database/migrations/2022_10_07_091406_flatten_entity_permissions_table.php +++ b/database/migrations/2022_10_07_091406_flatten_entity_permissions_table.php @@ -10,10 +10,8 @@ { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { // Remove entries for non-existing roles (Caused by previous lack of deletion handling) $roleIds = DB::table('roles')->pluck('id'); @@ -63,10 +61,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { // Create old table structure for entity_permissions Schema::create('old_entity_permissions', function (Blueprint $table) { diff --git a/database/migrations/2022_10_08_104202_drop_entity_restricted_field.php b/database/migrations/2022_10_08_104202_drop_entity_restricted_field.php index b2987674d61..8e45d9ea51f 100644 --- a/database/migrations/2022_10_08_104202_drop_entity_restricted_field.php +++ b/database/migrations/2022_10_08_104202_drop_entity_restricted_field.php @@ -11,10 +11,8 @@ { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { // Remove entity-permissions on non-restricted entities $deleteInactiveEntityPermissions = function (string $table, string $morphClass) { @@ -61,10 +59,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { // Create restricted columns $createRestrictedColumn = fn(Blueprint $table) => $table->boolean('restricted')->index()->default(0); diff --git a/database/migrations/2023_01_24_104625_refactor_joint_permissions_storage.php b/database/migrations/2023_01_24_104625_refactor_joint_permissions_storage.php index 53994c52b57..0e25c1d6052 100644 --- a/database/migrations/2023_01_24_104625_refactor_joint_permissions_storage.php +++ b/database/migrations/2023_01_24_104625_refactor_joint_permissions_storage.php @@ -10,10 +10,8 @@ { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { // Truncate before schema changes to avoid performance issues // since we'll need to rebuild anyway. @@ -34,10 +32,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { DB::table('joint_permissions')->truncate(); diff --git a/database/migrations/2023_01_28_141230_copy_color_settings_for_dark_mode.php b/database/migrations/2023_01_28_141230_copy_color_settings_for_dark_mode.php index 5187dabf470..6f137eca705 100644 --- a/database/migrations/2023_01_28_141230_copy_color_settings_for_dark_mode.php +++ b/database/migrations/2023_01_28_141230_copy_color_settings_for_dark_mode.php @@ -7,10 +7,8 @@ { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { $colorSettings = [ 'app-color', @@ -45,10 +43,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { $colorSettings = [ 'app-color-dark', diff --git a/database/migrations/2023_02_20_093655_increase_attachments_path_length.php b/database/migrations/2023_02_20_093655_increase_attachments_path_length.php index f7cb64ce648..b2a81d49e13 100644 --- a/database/migrations/2023_02_20_093655_increase_attachments_path_length.php +++ b/database/migrations/2023_02_20_093655_increase_attachments_path_length.php @@ -8,10 +8,8 @@ { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::table('attachments', function (Blueprint $table) { $table->text('path')->change(); @@ -20,10 +18,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::table('attachments', function (Blueprint $table) { $table->string('path')->change(); diff --git a/database/migrations/2023_02_23_200227_add_updated_at_index_to_pages.php b/database/migrations/2023_02_23_200227_add_updated_at_index_to_pages.php index 115bbb0c011..c3dc3fb0aa7 100644 --- a/database/migrations/2023_02_23_200227_add_updated_at_index_to_pages.php +++ b/database/migrations/2023_02_23_200227_add_updated_at_index_to_pages.php @@ -8,10 +8,8 @@ { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::table('pages', function (Blueprint $table) { $table->index('updated_at', 'pages_updated_at_index'); @@ -20,10 +18,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::table('pages', function (Blueprint $table) { $table->dropIndex('pages_updated_at_index'); diff --git a/database/migrations/2023_06_10_071823_remove_guest_user_secondary_roles.php b/database/migrations/2023_06_10_071823_remove_guest_user_secondary_roles.php index 8d04efdd953..05f6be4a1c9 100644 --- a/database/migrations/2023_06_10_071823_remove_guest_user_secondary_roles.php +++ b/database/migrations/2023_06_10_071823_remove_guest_user_secondary_roles.php @@ -7,10 +7,8 @@ { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { $guestUserId = DB::table('users') ->where('system_name', '=', 'public') @@ -36,8 +34,6 @@ public function up() /** * Reverse the migrations. - * - * @return void */ public function down() { diff --git a/database/migrations/2023_06_25_181952_remove_bookshelf_create_entity_permissions.php b/database/migrations/2023_06_25_181952_remove_bookshelf_create_entity_permissions.php index b5dcaee751c..540491d3b38 100644 --- a/database/migrations/2023_06_25_181952_remove_bookshelf_create_entity_permissions.php +++ b/database/migrations/2023_06_25_181952_remove_bookshelf_create_entity_permissions.php @@ -7,8 +7,6 @@ { /** * Run the migrations. - * - * @return void */ public function up() { @@ -20,8 +18,6 @@ public function up() /** * Reverse the migrations. - * - * @return void */ public function down() { diff --git a/database/migrations/2023_07_25_124945_add_receive_notifications_role_permissions.php b/database/migrations/2023_07_25_124945_add_receive_notifications_role_permissions.php index 4872e421e64..79e45a762fd 100644 --- a/database/migrations/2023_07_25_124945_add_receive_notifications_role_permissions.php +++ b/database/migrations/2023_07_25_124945_add_receive_notifications_role_permissions.php @@ -2,15 +2,14 @@ use Carbon\Carbon; use Illuminate\Database\Migrations\Migration; +use Illuminate\Support\Facades\DB; return new class extends Migration { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { // Create new receive-notifications permission and assign to admin role $permissionId = DB::table('role_permissions')->insertGetId([ @@ -29,10 +28,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { $permission = DB::table('role_permissions') ->where('name', '=', 'receive-notifications') diff --git a/database/migrations/2023_07_31_104430_create_watches_table.php b/database/migrations/2023_07_31_104430_create_watches_table.php index e2a5c20d07d..cf2c7f1051b 100644 --- a/database/migrations/2023_07_31_104430_create_watches_table.php +++ b/database/migrations/2023_07_31_104430_create_watches_table.php @@ -8,10 +8,8 @@ { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::create('watches', function (Blueprint $table) { $table->increments('id'); @@ -27,10 +25,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::dropIfExists('watches'); } diff --git a/database/migrations/2023_08_21_174248_increase_cache_size.php b/database/migrations/2023_08_21_174248_increase_cache_size.php index 865472c2e5d..bca7f9fc3de 100644 --- a/database/migrations/2023_08_21_174248_increase_cache_size.php +++ b/database/migrations/2023_08_21_174248_increase_cache_size.php @@ -8,10 +8,8 @@ { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::table('cache', function (Blueprint $table) { $table->mediumText('value')->change(); @@ -20,10 +18,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::table('cache', function (Blueprint $table) { $table->text('value')->change(); diff --git a/database/migrations/2023_12_02_104541_add_default_template_to_books.php b/database/migrations/2023_12_02_104541_add_default_template_to_books.php index c23bebc2e63..bfc480a2e22 100644 --- a/database/migrations/2023_12_02_104541_add_default_template_to_books.php +++ b/database/migrations/2023_12_02_104541_add_default_template_to_books.php @@ -8,10 +8,8 @@ class AddDefaultTemplateToBooks extends Migration { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { Schema::table('books', function (Blueprint $table) { $table->integer('default_template_id')->nullable()->default(null); @@ -20,10 +18,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { Schema::table('books', function (Blueprint $table) { $table->dropColumn('default_template_id'); diff --git a/database/migrations/2023_12_17_140913_add_description_html_to_entities.php b/database/migrations/2023_12_17_140913_add_description_html_to_entities.php index 68c52e81be5..781197e601d 100644 --- a/database/migrations/2023_12_17_140913_add_description_html_to_entities.php +++ b/database/migrations/2023_12_17_140913_add_description_html_to_entities.php @@ -8,10 +8,8 @@ { /** * Run the migrations. - * - * @return void */ - public function up() + public function up(): void { $addColumn = fn(Blueprint $table) => $table->text('description_html'); @@ -22,10 +20,8 @@ public function up() /** * Reverse the migrations. - * - * @return void */ - public function down() + public function down(): void { $removeColumn = fn(Blueprint $table) => $table->removeColumn('description_html'); diff --git a/database/migrations/2024_01_01_104542_add_default_template_to_chapters.php b/database/migrations/2024_01_01_104542_add_default_template_to_chapters.php new file mode 100644 index 00000000000..77df82272a7 --- /dev/null +++ b/database/migrations/2024_01_01_104542_add_default_template_to_chapters.php @@ -0,0 +1,28 @@ +integer('default_template_id')->nullable()->default(null); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('chapters', function (Blueprint $table) { + $table->dropColumn('default_template_id'); + }); + } +} diff --git a/database/migrations/2024_02_04_141358_add_views_updated_index.php b/database/migrations/2024_02_04_141358_add_views_updated_index.php new file mode 100644 index 00000000000..bc753867873 --- /dev/null +++ b/database/migrations/2024_02_04_141358_add_views_updated_index.php @@ -0,0 +1,28 @@ +index(['updated_at'], 'views_updated_at_index'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('views', function (Blueprint $table) { + $table->dropIndex('views_updated_at_index'); + }); + } +}; diff --git a/database/migrations/2024_05_04_154409_rename_activity_relation_columns.php b/database/migrations/2024_05_04_154409_rename_activity_relation_columns.php new file mode 100644 index 00000000000..ee3358d7321 --- /dev/null +++ b/database/migrations/2024_05_04_154409_rename_activity_relation_columns.php @@ -0,0 +1,30 @@ +renameColumn('entity_id', 'loggable_id'); + $table->renameColumn('entity_type', 'loggable_type'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('activities', function (Blueprint $table) { + $table->renameColumn('loggable_id', 'entity_id'); + $table->renameColumn('loggable_type', 'entity_type'); + }); + } +}; diff --git a/database/migrations/2024_09_29_140340_ensure_editor_value_set.php b/database/migrations/2024_09_29_140340_ensure_editor_value_set.php new file mode 100644 index 00000000000..786322b3c63 --- /dev/null +++ b/database/migrations/2024_09_29_140340_ensure_editor_value_set.php @@ -0,0 +1,48 @@ +where('setting_key', '=', 'app-editor') + ->first() + ->value ?? 'wysiwyg'; + $default = ($default === 'markdown') ? 'markdown' : 'wysiwyg'; + + // We set it to 'markdown' for pages currently with markdown content + DB::table('pages') + ->where('editor', '=', '') + ->where('markdown', '!=', '') + ->update(['editor' => 'markdown']); + + // We set it to 'wysiwyg' where we have HTML but no markdown + DB::table('pages') + ->where('editor', '=', '') + ->where('markdown', '=', '') + ->where('html', '!=', '') + ->update(['editor' => 'wysiwyg']); + + // Otherwise, where still empty, set to the current default + DB::table('pages') + ->where('editor', '=', '') + ->update(['editor' => $default]); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + // Can't reverse due to not knowing what would have been empty before + } +}; diff --git a/database/migrations/2024_10_29_114420_add_import_role_permission.php b/database/migrations/2024_10_29_114420_add_import_role_permission.php new file mode 100644 index 00000000000..17bbe4cff26 --- /dev/null +++ b/database/migrations/2024_10_29_114420_add_import_role_permission.php @@ -0,0 +1,61 @@ +insertGetId([ + 'name' => 'content-import', + 'display_name' => 'Import Content', + 'created_at' => Carbon::now()->toDateTimeString(), + 'updated_at' => Carbon::now()->toDateTimeString(), + ]); + + // Get existing admin-level role ids + $settingManagePermission = DB::table('role_permissions') + ->where('name', '=', 'settings-manage')->first(); + + if (!$settingManagePermission) { + return; + } + + $adminRoleIds = DB::table('permission_role') + ->where('permission_id', '=', $settingManagePermission->id) + ->pluck('role_id')->all(); + + // Assign the new permission to all existing admins + $newPermissionRoles = array_values(array_map(function ($roleId) use ($permissionId) { + return [ + 'role_id' => $roleId, + 'permission_id' => $permissionId, + ]; + }, $adminRoleIds)); + + DB::table('permission_role')->insert($newPermissionRoles); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + // Remove content-import permission + $importPermission = DB::table('role_permissions') + ->where('name', '=', 'content-import')->first(); + + if (!$importPermission) { + return; + } + + DB::table('permission_role')->where('permission_id', '=', $importPermission->id)->delete(); + DB::table('role_permissions')->where('id', '=', $importPermission->id)->delete(); + } +}; diff --git a/database/migrations/2024_11_02_160700_create_imports_table.php b/database/migrations/2024_11_02_160700_create_imports_table.php new file mode 100644 index 00000000000..0784591b8e3 --- /dev/null +++ b/database/migrations/2024_11_02_160700_create_imports_table.php @@ -0,0 +1,33 @@ +increments('id'); + $table->string('name'); + $table->string('path'); + $table->integer('size'); + $table->string('type'); + $table->longText('metadata'); + $table->integer('created_by')->index(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('imports'); + } +}; diff --git a/database/migrations/2024_11_27_171039_add_instance_id_setting.php b/database/migrations/2024_11_27_171039_add_instance_id_setting.php new file mode 100644 index 00000000000..ee1e90d0303 --- /dev/null +++ b/database/migrations/2024_11_27_171039_add_instance_id_setting.php @@ -0,0 +1,30 @@ +insert([ + 'setting_key' => 'instance-id', + 'value' => Str::uuid(), + 'created_at' => Carbon::now(), + 'updated_at' => Carbon::now(), + 'type' => 'string', + ]); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + DB::table('settings')->where('setting_key', '=', 'instance-id')->delete(); + } +}; diff --git a/database/migrations/2025_01_29_180933_create_sort_rules_table.php b/database/migrations/2025_01_29_180933_create_sort_rules_table.php new file mode 100644 index 00000000000..37d20ddf618 --- /dev/null +++ b/database/migrations/2025_01_29_180933_create_sort_rules_table.php @@ -0,0 +1,29 @@ +increments('id'); + $table->string('name'); + $table->text('sequence'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('sort_rules'); + } +}; diff --git a/database/migrations/2025_02_05_150842_add_sort_rule_id_to_books.php b/database/migrations/2025_02_05_150842_add_sort_rule_id_to_books.php new file mode 100644 index 00000000000..106db05caa0 --- /dev/null +++ b/database/migrations/2025_02_05_150842_add_sort_rule_id_to_books.php @@ -0,0 +1,28 @@ +unsignedInteger('sort_rule_id')->nullable()->default(null); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('books', function (Blueprint $table) { + $table->dropColumn('sort_rule_id'); + }); + } +}; diff --git a/database/migrations/2025_04_18_215145_add_content_refs_and_archived_to_comments.php b/database/migrations/2025_04_18_215145_add_content_refs_and_archived_to_comments.php new file mode 100644 index 00000000000..794201dec8a --- /dev/null +++ b/database/migrations/2025_04_18_215145_add_content_refs_and_archived_to_comments.php @@ -0,0 +1,30 @@ +string('content_ref'); + $table->boolean('archived')->index(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('comments', function (Blueprint $table) { + $table->dropColumn('content_ref'); + $table->dropColumn('archived'); + }); + } +}; diff --git a/database/migrations/2025_09_02_111542_remove_unused_columns.php b/database/migrations/2025_09_02_111542_remove_unused_columns.php new file mode 100644 index 00000000000..3a36e5ad603 --- /dev/null +++ b/database/migrations/2025_09_02_111542_remove_unused_columns.php @@ -0,0 +1,38 @@ +dropColumn('text'); + }); + + Schema::table('role_permissions', function (Blueprint $table) { + $table->dropColumn('display_name'); + $table->dropColumn('description'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('comments', function (Blueprint $table) { + $table->longText('text')->nullable(); + }); + + Schema::table('role_permissions', function (Blueprint $table) { + $table->string('display_name')->nullable(); + $table->string('description')->nullable(); + }); + } +}; diff --git a/dev/api/requests/chapters-create.json b/dev/api/requests/chapters-create.json index e9d9033874e..02aee9eeabf 100644 --- a/dev/api/requests/chapters-create.json +++ b/dev/api/requests/chapters-create.json @@ -3,6 +3,7 @@ "name": "My fantastic new chapter", "description_html": "

This is a great new chapter that I've created via the API

", "priority": 15, + "default_template_id": 25, "tags": [ {"name": "Category", "value": "Top Content"}, {"name": "Rating", "value": "Highest"} diff --git a/dev/api/requests/chapters-update.json b/dev/api/requests/chapters-update.json index be675772bc6..cf9c89eacdc 100644 --- a/dev/api/requests/chapters-update.json +++ b/dev/api/requests/chapters-update.json @@ -3,6 +3,7 @@ "name": "My fantastic updated chapter", "description_html": "

This is an updated chapter that I've altered via the API

", "priority": 16, + "default_template_id": 2428, "tags": [ {"name": "Category", "value": "Kinda Good Content"}, {"name": "Rating", "value": "Medium"} diff --git a/dev/api/requests/imports-run.json b/dev/api/requests/imports-run.json new file mode 100644 index 00000000000..836a66f3a94 --- /dev/null +++ b/dev/api/requests/imports-run.json @@ -0,0 +1,4 @@ +{ + "parent_type": "book", + "parent_id": 28 +} \ No newline at end of file diff --git a/dev/api/requests/search-all.http b/dev/api/requests/search-all.http index ee522381641..f9c17fa1696 100644 --- a/dev/api/requests/search-all.http +++ b/dev/api/requests/search-all.http @@ -1 +1 @@ -GET /api/search?query=cats+{created_by:me}&page=1&count=2 \ No newline at end of file +GET /api/search?query=cats+{created_by:me}&page=1&count=2 diff --git a/dev/api/responses/audit-log-list.json b/dev/api/responses/audit-log-list.json new file mode 100644 index 00000000000..15a25e10689 --- /dev/null +++ b/dev/api/responses/audit-log-list.json @@ -0,0 +1,80 @@ +{ + "data": [ + { + "id": 1, + "type": "bookshelf_create", + "detail": "", + "user_id": 1, + "loggable_id": 1, + "loggable_type": "bookshelf", + "ip": "124.4.x.x", + "created_at": "2021-09-29T12:32:02.000000Z", + "user": { + "id": 1, + "name": "Admins", + "slug": "admins" + } + }, + { + "id": 2, + "type": "auth_login", + "detail": "standard; (1) Admin", + "user_id": 1, + "loggable_id": null, + "loggable_type": null, + "ip": "127.0.x.x", + "created_at": "2021-09-29T12:32:04.000000Z", + "user": { + "id": 1, + "name": "Admins", + "slug": "admins" + } + }, + { + "id": 3, + "type": "bookshelf_update", + "detail": "", + "user_id": 1, + "loggable_id": 1, + "loggable_type": "bookshelf", + "ip": "127.0.x.x", + "created_at": "2021-09-29T12:32:07.000000Z", + "user": { + "id": 1, + "name": "Admins", + "slug": "admins" + } + }, + { + "id": 4, + "type": "page_create", + "detail": "", + "user_id": 1, + "loggable_id": 1, + "loggable_type": "page", + "ip": "127.0.x.x", + "created_at": "2021-09-29T12:32:13.000000Z", + "user": { + "id": 1, + "name": "Admins", + "slug": "admins" + } + }, + { + "id": 5, + "type": "page_update", + "detail": "", + "user_id": 1, + "loggable_id": 1, + "loggable_type": "page", + "ip": "127.0.x.x", + "created_at": "2021-09-29T12:37:27.000000Z", + "user": { + "id": 1, + "name": "Admins", + "slug": "admins" + } + } + ], + "total": 6088 +} \ No newline at end of file diff --git a/dev/api/responses/books-list.json b/dev/api/responses/books-list.json index 0f8458feda2..50c8c49e6ef 100644 --- a/dev/api/responses/books-list.json +++ b/dev/api/responses/books-list.json @@ -9,7 +9,8 @@ "updated_at": "2019-12-11T20:57:31.000000Z", "created_by": 1, "updated_by": 1, - "owned_by": 1 + "owned_by": 1, + "cover": null }, { "id": 2, @@ -20,7 +21,12 @@ "updated_at": "2019-12-11T20:57:23.000000Z", "created_by": 4, "updated_by": 3, - "owned_by": 3 + "owned_by": 3, + "cover": { + "id": 11, + "name": "cat_banner.jpg", + "url": "https://example.com/uploads/images/cover_book/2021-10/cat-banner.jpg" + } } ], "total": 14 diff --git a/dev/api/responses/chapters-create.json b/dev/api/responses/chapters-create.json index 2d404440587..3711d1cc9d6 100644 --- a/dev/api/responses/chapters-create.json +++ b/dev/api/responses/chapters-create.json @@ -11,6 +11,7 @@ "updated_by": 1, "owned_by": 1, "description_html": "

This is a great new chapter<\/strong> that I've created via the API<\/p>", + "default_template_id": 25, "book_slug": "example-book", "tags": [ { @@ -24,4 +25,4 @@ "order": 0 } ] -} \ No newline at end of file +} diff --git a/dev/api/responses/chapters-read.json b/dev/api/responses/chapters-read.json index 192ffce7cd2..01a2f4b9f98 100644 --- a/dev/api/responses/chapters-read.json +++ b/dev/api/responses/chapters-read.json @@ -5,6 +5,7 @@ "name": "Content Creation", "description": "How to create documentation on whatever subject you need to write about.", "description_html": "

How to create documentation on whatever subject you need to write about.

", + "default_template_id": 25, "priority": 3, "created_at": "2019-05-05T21:49:56.000000Z", "updated_at": "2019-09-28T11:24:23.000000Z", diff --git a/dev/api/responses/chapters-update.json b/dev/api/responses/chapters-update.json index 3dad6aa0c12..96784dcedf7 100644 --- a/dev/api/responses/chapters-update.json +++ b/dev/api/responses/chapters-update.json @@ -11,6 +11,7 @@ "updated_by": 1, "owned_by": 1, "description_html": "

This is an updated chapter<\/strong> that I've altered via the API<\/p>", + "default_template_id": 2428, "book_slug": "example-book", "tags": [ { @@ -24,4 +25,4 @@ "order": 0 } ] -} \ No newline at end of file +} diff --git a/dev/api/responses/imports-create.json b/dev/api/responses/imports-create.json new file mode 100644 index 00000000000..997758799a4 --- /dev/null +++ b/dev/api/responses/imports-create.json @@ -0,0 +1,10 @@ +{ + "type": "chapter", + "name": "Pension Providers", + "created_by": 1, + "size": 2757, + "path": "uploads\/files\/imports\/ghnxmS3u9QxLWu82.zip", + "updated_at": "2025-07-18T14:50:27.000000Z", + "created_at": "2025-07-18T14:50:27.000000Z", + "id": 31 +} \ No newline at end of file diff --git a/dev/api/responses/imports-list.json b/dev/api/responses/imports-list.json new file mode 100644 index 00000000000..7451e443783 --- /dev/null +++ b/dev/api/responses/imports-list.json @@ -0,0 +1,23 @@ +{ + "data": [ + { + "id": 25, + "name": "IT Department", + "size": 618462, + "type": "book", + "created_by": 1, + "created_at": "2024-12-20T18:40:38.000000Z", + "updated_at": "2024-12-20T18:40:38.000000Z" + }, + { + "id": 27, + "name": "Clients", + "size": 15364, + "type": "chapter", + "created_by": 1, + "created_at": "2025-03-20T12:41:44.000000Z", + "updated_at": "2025-03-20T12:41:44.000000Z" + } + ], + "total": 2 +} \ No newline at end of file diff --git a/dev/api/responses/imports-read.json b/dev/api/responses/imports-read.json new file mode 100644 index 00000000000..e256854d127 --- /dev/null +++ b/dev/api/responses/imports-read.json @@ -0,0 +1,51 @@ +{ + "id": 25, + "name": "IT Department", + "path": "uploads\/files\/imports\/7YOpZ6sGIEbYdRFL.zip", + "size": 618462, + "type": "book", + "created_by": 1, + "created_at": "2024-12-20T18:40:38.000000Z", + "updated_at": "2024-12-20T18:40:38.000000Z", + "details": { + "id": 4, + "name": "IT Department", + "chapters": [ + { + "id": 3, + "name": "Server Systems", + "priority": 1, + "pages": [ + { + "id": 22, + "name": "prod-aws-stonehawk", + "priority": 0, + "attachments": [], + "images": [], + "tags": [] + } + ], + "tags": [] + } + ], + "pages": [ + { + "id": 23, + "name": "Member Onboarding Guide", + "priority": 0, + "attachments": [], + "images": [], + "tags": [] + }, + { + "id": 25, + "name": "IT Holiday Party Event", + "priority": 2, + "attachments": [], + "images": [], + "tags": [] + } + ], + "tags": [] + } +} \ No newline at end of file diff --git a/dev/api/responses/imports-run.json b/dev/api/responses/imports-run.json new file mode 100644 index 00000000000..90b34d6aa09 --- /dev/null +++ b/dev/api/responses/imports-run.json @@ -0,0 +1,14 @@ +{ + "id": 1067, + "book_id": 28, + "slug": "pension-providers", + "name": "Pension Providers", + "description": "Details on the various pension providers that are available", + "priority": 7, + "created_at": "2025-07-18T14:53:35.000000Z", + "updated_at": "2025-07-18T14:53:36.000000Z", + "created_by": 1, + "updated_by": 1, + "owned_by": 1, + "default_template_id": null +} \ No newline at end of file diff --git a/dev/api/responses/search-all.json b/dev/api/responses/search-all.json index 2c7584e3fd0..2ad89641693 100644 --- a/dev/api/responses/search-all.json +++ b/dev/api/responses/search-all.json @@ -8,7 +8,12 @@ "created_at": "2021-11-14T15:57:35.000000Z", "updated_at": "2021-11-14T15:57:35.000000Z", "type": "chapter", - "url": "https://example.com/books/my-book/chapter/a-chapter-for-cats", + "url": "https://example.com/books/cats/chapter/a-chapter-for-cats", + "book": { + "id": 1, + "name": "Cats", + "slug": "cats" + }, "preview_html": { "name": "A chapter for cats", "content": "...once a bunch of cats named tony...behaviour of cats is unsuitable" @@ -26,7 +31,17 @@ "created_at": "2021-05-15T16:28:10.000000Z", "updated_at": "2021-11-14T15:56:49.000000Z", "type": "page", - "url": "https://example.com/books/my-book/page/the-hows-and-whys-of-cats", + "url": "https://example.com/books/cats/page/the-hows-and-whys-of-cats", + "book": { + "id": 1, + "name": "Cats", + "slug": "cats" + }, + "chapter": { + "id": 75, + "name": "A chapter for cats", + "slug": "a-chapter-for-cats" + }, "preview_html": { "name": "The hows and whys of cats", "content": "...people ask why cats? but there are...the reason that cats are fast are due to..." @@ -55,7 +70,17 @@ "created_at": "2020-11-29T21:55:07.000000Z", "updated_at": "2021-11-14T16:02:39.000000Z", "type": "page", - "url": "https://example.com/books/my-book/page/how-advanced-are-cats", + "url": "https://example.com/books/big-cats/page/how-advanced-are-cats", + "book": { + "id": 13, + "name": "Big Cats", + "slug": "big-cats" + }, + "chapter": { + "id": 73, + "name": "A chapter for bigger cats", + "slug": "a-chapter-for-bigger-cats" + }, "preview_html": { "name": "How advanced are cats?", "content": "cats are some of the most advanced animals in the world." @@ -64,4 +89,4 @@ } ], "total": 3 -} \ No newline at end of file +} diff --git a/dev/api/responses/shelves-list.json b/dev/api/responses/shelves-list.json index 4b1a1b43f93..d5debfaef10 100644 --- a/dev/api/responses/shelves-list.json +++ b/dev/api/responses/shelves-list.json @@ -9,7 +9,12 @@ "updated_at": "2020-04-10T13:00:45.000000Z", "created_by": 4, "updated_by": 1, - "owned_by": 1 + "owned_by": 1, + "cover": { + "id": 4, + "name": "shelf.jpg", + "url": "https://example.com/uploads/images/cover_bookshelf/2024-12/shelf.jpg" + } }, { "id": 9, @@ -20,7 +25,8 @@ "updated_at": "2020-04-10T13:00:58.000000Z", "created_by": 4, "updated_by": 1, - "owned_by": 1 + "owned_by": 1, + "cover": null }, { "id": 10, @@ -31,7 +37,8 @@ "updated_at": "2020-04-10T13:00:53.000000Z", "created_by": 4, "updated_by": 1, - "owned_by": 4 + "owned_by": 4, + "cover": null } ], "total": 3 diff --git a/dev/api/responses/system-read.json b/dev/api/responses/system-read.json new file mode 100644 index 00000000000..4687d6ffc09 --- /dev/null +++ b/dev/api/responses/system-read.json @@ -0,0 +1,7 @@ +{ + "version": "v25.02.4", + "instance_id": "1234abcd-cc12-7808-af0a-264cb0cbd611", + "app_name": "My BookStack Instance", + "app_logo": "https://docs.example.com/uploads/images/system/2025-05/cat-icon.png", + "base_url": "https://docs.example.com" +} \ No newline at end of file diff --git a/dev/api/responses/users-list.json b/dev/api/responses/users-list.json index cbc7fb1043e..9cc2517b7ff 100644 --- a/dev/api/responses/users-list.json +++ b/dev/api/responses/users-list.json @@ -8,7 +8,6 @@ "updated_at": "2022-02-03T16:27:55.000000Z", "external_auth_id": "abc123456", "slug": "dan-brown", - "user_id": 1, "last_activity_at": "2022-02-03T16:27:55.000000Z", "profile_url": "https://docs.example.com/user/dan-brown", "edit_url": "https://docs.example.com/settings/users/1", @@ -22,7 +21,6 @@ "updated_at": "2021-11-18T17:10:58.000000Z", "external_auth_id": "", "slug": "benny", - "user_id": 2, "last_activity_at": "2022-01-31T20:39:24.000000Z", "profile_url": "https://docs.example.com/user/benny", "edit_url": "https://docs.example.com/settings/users/2", diff --git a/dev/build/esbuild.js b/dev/build/esbuild.js index c47727c2c4e..63387d612ce 100644 --- a/dev/build/esbuild.js +++ b/dev/build/esbuild.js @@ -10,9 +10,11 @@ const isProd = process.argv[2] === 'production'; // Gather our input files const entryPoints = { - app: path.join(__dirname, '../../resources/js/app.js'), + app: path.join(__dirname, '../../resources/js/app.ts'), code: path.join(__dirname, '../../resources/js/code/index.mjs'), 'legacy-modes': path.join(__dirname, '../../resources/js/code/legacy-modes.mjs'), + markdown: path.join(__dirname, '../../resources/js/markdown/index.mts'), + wysiwyg: path.join(__dirname, '../../resources/js/wysiwyg/index.ts'), }; // Locate our output directory @@ -30,6 +32,19 @@ esbuild.build({ format: 'esm', minify: isProd, logLevel: 'info', + loader: { + '.svg': 'text', + }, + absWorkingDir: path.join(__dirname, '../..'), + alias: { + '@icons': './resources/icons', + lexical: './resources/js/wysiwyg/lexical/core', + '@lexical': './resources/js/wysiwyg/lexical', + }, + banner: { + js: '// See the "/licenses" URI for full package license details', + css: '/* See the "/licenses" URI for full package license details */', + }, }).then(result => { fs.writeFileSync('esbuild-meta.json', JSON.stringify(result.metafile)); }).catch(() => process.exit(1)); diff --git a/dev/build/svg-blank-transform.js b/dev/build/svg-blank-transform.js new file mode 100644 index 00000000000..5183014c880 --- /dev/null +++ b/dev/build/svg-blank-transform.js @@ -0,0 +1,14 @@ +// This is a basic transformer stub to help jest handle SVG files. +// Essentially blanks them since we don't really need to involve them +// in our tests (yet). +module.exports = { + process() { + return { + code: 'module.exports = \'\';', + }; + }, + getCacheKey() { + // The output is always the same. + return 'svgTransform'; + }, +}; diff --git a/dev/checksums/.gitignore b/dev/checksums/.gitignore new file mode 100644 index 00000000000..b722e9e13ef --- /dev/null +++ b/dev/checksums/.gitignore @@ -0,0 +1 @@ +!.gitignore \ No newline at end of file diff --git a/dev/checksums/vendor b/dev/checksums/vendor new file mode 100644 index 00000000000..eb8b2f05237 --- /dev/null +++ b/dev/checksums/vendor @@ -0,0 +1 @@ +22e02ee72d21ff719c1073abbec8302f8e2096ba6d072e133051064ed24b45b1 diff --git a/dev/docker/Dockerfile b/dev/docker/Dockerfile index 349e315d2e3..edab90ca1c5 100644 --- a/dev/docker/Dockerfile +++ b/dev/docker/Dockerfile @@ -1,25 +1,38 @@ -FROM php:8.1-apache +FROM php:8.3-apache -ENV APACHE_DOCUMENT_ROOT /app/public -WORKDIR /app +# Install additional dependencies +RUN apt-get update && \ + apt-get install -y \ + git \ + zip \ + unzip \ + libfreetype-dev \ + libjpeg62-turbo-dev \ + libldap2-dev \ + libpng-dev \ + libzip-dev \ + wait-for-it && \ + rm -rf /var/lib/apt/lists/* -# Install additional dependacnies and configure apache -RUN apt-get update -y \ - && apt-get install -y git zip unzip libpng-dev libldap2-dev libzip-dev wait-for-it \ - && docker-php-ext-configure ldap --with-libdir="lib/$(gcc -dumpmachine)" \ - && docker-php-ext-install pdo_mysql gd ldap zip \ - && pecl install xdebug \ - && docker-php-ext-enable xdebug \ - && a2enmod rewrite \ - && sed -ri -e 's!/var/www/html!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/sites-available/*.conf \ - && sed -ri -e 's!/var/www/!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/apache2.conf /etc/apache2/conf-available/*.conf +# Install PHP extensions +RUN docker-php-ext-configure ldap --with-libdir="lib/$(gcc -dumpmachine)" && \ + docker-php-ext-configure gd --with-freetype --with-jpeg && \ + docker-php-ext-install -j$(nproc) pdo_mysql gd ldap zip && \ + pecl install xdebug && \ + docker-php-ext-enable xdebug # Install composer -RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" \ - && php composer-setup.php \ - && mv composer.phar /usr/bin/composer \ - && php -r "unlink('composer-setup.php');" +RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer + +# Configure apache +RUN a2enmod rewrite && \ + sed -ri -e 's!/var/www/html!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/sites-available/*.conf && \ + sed -ri -e 's!/var/www/!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/apache2.conf /etc/apache2/conf-available/*.conf # Use the default production configuration and update it as required -RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" \ - && sed -i 's/memory_limit = 128M/memory_limit = 512M/g' "$PHP_INI_DIR/php.ini" +RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" && \ + sed -i 's/memory_limit = 128M/memory_limit = 512M/g' "$PHP_INI_DIR/php.ini" + +ENV APACHE_DOCUMENT_ROOT="/app/public" + +WORKDIR /app diff --git a/dev/docker/entrypoint.app.sh b/dev/docker/entrypoint.app.sh index e91d34a7133..b09edda8863 100755 --- a/dev/docker/entrypoint.app.sh +++ b/dev/docker/entrypoint.app.sh @@ -9,7 +9,7 @@ if [[ -n "$1" ]]; then else composer install wait-for-it db:3306 -t 45 - php artisan migrate --database=mysql - chown -R www-data:www-data storage + php artisan migrate --database=mysql --force + chown -R www-data storage public/uploads bootstrap/cache exec apache2-foreground fi diff --git a/dev/docs/development.md b/dev/docs/development.md index 3c7a6e9d299..ea3e692a198 100644 --- a/dev/docs/development.md +++ b/dev/docs/development.md @@ -3,7 +3,7 @@ All development on BookStack is currently done on the `development` branch. When it's time for a release the `development` branch is merged into release with built & minified CSS & JS then tagged at its version. Here are the current development requirements: -* [Node.js](https://nodejs.org/en/) v18.0+ +* [Node.js](https://nodejs.org/en/) v20.0+ ## Building CSS & JavaScript Assets @@ -82,7 +82,7 @@ If all the conditions are met, you can proceed with the following steps: 1. **Copy `.env.example` to `.env`**, change `APP_KEY` to a random 32 char string and set `APP_ENV` to `local`. 2. Make sure **port 8080 is unused** *or else* change `DEV_PORT` to a free port on your host. -3. **Run `chgrp -R docker storage`**. The development container will chown the `storage` directory to the `www-data` user inside the container so BookStack can write to it. You need to change the group to your host's `docker` group here to not lose access to the `storage` directory. +3. **Run `chgrp -R docker storage`**. The development container will chown the `storage`, `public/uploads` and `bootstrap/cache` directories to the `www-data` user inside the container so BookStack can write to it. You need to change the group to your host's `docker` group here to not lose access to the `storage` directory. 4. **Run `docker-compose up`** and wait until the image is built and all database migrations have been done. 5. You can now login with `admin@admin.com` and `password` as password on `localhost:8080` (or another port if specified). diff --git a/dev/docs/javascript-code.md b/dev/docs/javascript-code.md index ba7d7997248..e5f491839f0 100644 --- a/dev/docs/javascript-code.md +++ b/dev/docs/javascript-code.md @@ -137,8 +137,8 @@ window.$events.showValidationErrors(error); // Translator // Take the given plural text and count to decide on what plural option // to use, Similar to laravel's trans_choice function but instead -// takes the direction directly instead of a translation key. -window.trans_plural(translationString, count, replacements); +// takes the translation text directly instead of a translation key. +window.$trans.choice(translationString, count, replacements); // Component System // Parse and initialise any components from the given root el down. diff --git a/dev/docs/logical-theme-system.md b/dev/docs/logical-theme-system.md index 139055b3db2..0063c4e8bac 100644 --- a/dev/docs/logical-theme-system.md +++ b/dev/docs/logical-theme-system.md @@ -2,7 +2,9 @@ BookStack allows logical customization via the theme system which enables you to add, or extend, functionality within the PHP side of the system without needing to alter the core application files. -WARNING: This system is currently in alpha so may incur changes. Once we've gathered some feedback on usage we'll look to removing this warning. This system will be considered semi-stable in the future. The `Theme::` system will be kept maintained but specific customizations or deeper app/framework usage using this system will not be supported nor considered in any way stable. Customizations using this system should be checked after updates. +This is part of the theme system alongside the [visual theme system](./visual-theme-system.md). + +**Note:** This system is considered semi-stable. The `Theme::` system is kept maintained but specific customizations or deeper app/framework usage using this system will not be supported nor considered in any way stable. Customizations using this system should be checked after updates. ## Getting Started @@ -47,6 +49,7 @@ This method allows you to register a custom social authentication driver within - string $driverName - array $config - string $socialiteHandler +- callable|null $configureForRedirect = null **Example** diff --git a/dev/docs/portable-zip-file-format.md b/dev/docs/portable-zip-file-format.md new file mode 100644 index 00000000000..754cb4d3e94 --- /dev/null +++ b/dev/docs/portable-zip-file-format.md @@ -0,0 +1,160 @@ +# Portable ZIP File Format + +BookStack provides exports in a "Portable ZIP" which allows the portable transfer, storage, import & export of BookStack content. +This document details the format used, and is intended for our own internal development use in addition to detailing the format for potential external use-cases (readers, apps, import for other platforms etc...). + +**Note:** This is not a BookStack backup format! This format misses much of the data that would be needed to re-create/restore a BookStack instance. There are existing better alternative options for this use-case. + +## Stability + +Following the goals & ideals of BookStack, stability is very important. We aim for this defined format to be stable and forwards compatible, to prevent breakages in use-case due to changes. Here are the general rules we follow in regard to stability & changes: + +- New features & properties may be added with any release. +- Where reasonably possible, we will attempt to avoid modifications/removals of existing features/properties. +- Where potentially breaking changes do have to be made, these will be noted in BookStack release/update notes. + +The addition of new features/properties alone are not considered as a breaking change to the format. Breaking changes are considered as such where they could impact common/expected use of the existing properties and features we document, they are not considered based upon user assumptions or any possible breakage. +For example if your application, using the format, breaks because we added a new property while you hard-coded your application to use the third property (instead of a property name), then that's on you. + +## Format Outline + +The format is intended to be very simple, readable and based on open standards that could be easily read/handled in most common programming languages. +The below outlines the structure of the format: + +- **ZIP archive container** + - **data.json** - Export data. + - **files/** - Directory containing referenced files. + - *file-a* + - *file-b* + - *...* + +## References + +Some properties in the export data JSON are indicated as `String reference`, and these are direct references to a file name within the `files/` directory of the ZIP. For example, the below book cover is directly referencing a `files/4a5m4a.jpg` within the ZIP which would be expected to exist. + +```json +{ + "book": { + "cover": "4a5m4a.jpg" + } +} +``` + +Within HTML and markdown content, you may require references across to other items within the export content. +This can be done using the following format: + +``` +[[bsexport::]] +``` + +References are to the `id` for data objects. +Here's an example of each type of such reference that could be used: + +``` +[[bsexport:image:22]] +[[bsexport:attachment:55]] +[[bsexport:page:40]] +[[bsexport:chapter:2]] +[[bsexport:book:8]] +``` + +## HTML & Markdown Content + +BookStack commonly stores & utilises content in the HTML format. +Properties that expect or provided HTML will either be named `html` or contain `html` in the property name. +While BookStack supports a range of HTML, not all HTML content will be supported by BookStack and be assured to work as desired across all BookStack features. +The HTML supported by BookStack is not yet formally documented, but you can inspect to what the WYSIWYG editor produces as a basis. +Generally, top-level elements should keep to common block formats (p, blockquote, h1, h2 etc...) with no nesting or custom structure apart from common inline elements. +Some areas of BookStack where HTML is used, like book & chapter descriptions, will strictly limit/filter HTML tag & attributes to an allow-list. + +For markdown content, in BookStack we target [the commonmark spec](https://commonmark.org/) with the addition of tables & task-lists. +HTML within markdown is supported but not all HTML is assured to work as advised above. + +### Content Security + +If you're consuming HTML or markdown within an export please consider that the content is not assured to be safe, even if provided directly by a BookStack instance. It's best to treat such content as potentially unsafe. +By default, BookStack performs some basic filtering to remove scripts among other potentially dangerous elements but this is not foolproof. BookStack itself relies on additional security mechanisms such as [CSP](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) to help prevent a range of exploits. + +## Export Data - `data.json` + +The `data.json` file is a JSON format file which contains all structured data for the export. The properties are as follows: + +- `instance` - [Instance](#instance) Object, optional, details of the export source instance. +- `exported_at` - String, optional, full ISO 8601 datetime of when the export was created. +- `book` - [Book](#book) Object, optional, book export data. +- `chapter` - [Chapter](#chapter) Object, optional, chapter export data. +- `page` - [Page](#page) Object, optional, page export data. + +Either `book`, `chapter` or `page` will exist depending on export type. You'd want to check for each to check what kind of export this is, and if it's an export you can handle. It's possible that other options are added in the future (`books` for a range of books for example) so it'd be wise to specifically check for properties that can be handled, otherwise error to indicate lack of support. + +## Data Objects + +The below details the objects & their properties used in Application Data. + +#### Instance + +These details are informational regarding the exporting BookStack instance from where an export was created from. + +- `id` - String, required, unique identifier for the BookStack instance. +- `version` - String, required, BookStack version of the export source instance. + +#### Book + +- `id` - Number, optional, original ID for the book from exported system. +- `name` - String, required, name/title of the book. +- `description_html` - String, optional, HTML description content. +- `cover` - String reference, optional, reference to book cover image. +- `chapters` - [Chapter](#chapter) array, optional, chapters within this book. +- `pages` - [Page](#page) array, optional, direct child pages for this book. +- `tags` - [Tag](#tag) array, optional, tags assigned to this book. + +The `pages` are not all pages within the book, just those that are direct children (not in a chapter). To build an ordered mixed chapter/page list for the book, as what you'd see in BookStack, you'd need to combine `chapters` and `pages` together and sort by their `priority` value (low to high). + +#### Chapter + +- `id` - Number, optional, original ID for the chapter from exported system. +- `name` - String, required, name/title of the chapter. +- `description_html` - String, optional, HTML description content. +- `priority` - Number, optional, integer order for when shown within a book (shown low to high). +- `pages` - [Page](#page) array, optional, pages within this chapter. +- `tags` - [Tag](#tag) array, optional, tags assigned to this chapter. + +#### Page + +- `id` - Number, optional, original ID for the page from exported system. +- `name` - String, required, name/title of the page. +- `html` - String, optional, page HTML content. +- `markdown` - String, optional, user markdown content for this page. +- `priority` - Number, optional, integer order for when shown within a book (shown low to high). +- `attachments` - [Attachment](#attachment) array, optional, attachments uploaded to this page. +- `images` - [Image](#image) array, optional, images used in this page. +- `tags` - [Tag](#tag) array, optional, tags assigned to this page. + +To define the page content, either `markdown` or `html` should be provided. Ideally these should be limited to the range of markdown and HTML which BookStack supports. See the ["HTML & Markdown Content"](#html--markdown-content) section. + +The page editor type, and edit content will be determined by what content is provided. If non-empty `markdown` is provided, the page will be assumed as a markdown editor page (where permissions allow) and the HTML will be rendered from the markdown content. Otherwise, the provided `html` will be used as editor & display content. + +#### Image + +- `id` - Number, optional, original ID for the page from exported system. +- `name` - String, required, name of image. +- `file` - String reference, required, reference to image file. +- `type` - String, required, must be 'gallery' or 'drawio' + +File must be an image type accepted by BookStack (png, jpg, gif, webp). +Images of type 'drawio' are expected to be png with draw.io drawing data +embedded within it. + +#### Attachment + +- `id` - Number, optional, original ID for the attachment from exported system. +- `name` - String, required, name of attachment. +- `link` - String, semi-optional, URL of attachment. +- `file` - String reference, semi-optional, reference to attachment file. + +Either `link` or `file` must be present, as that will determine the type of attachment. + +#### Tag + +- `name` - String, required, name of the tag. +- `value` - String, optional, value of the tag (can be empty). \ No newline at end of file diff --git a/dev/docs/visual-theme-system.md b/dev/docs/visual-theme-system.md index 47ee57c0e0c..8a76ddb00e0 100644 --- a/dev/docs/visual-theme-system.md +++ b/dev/docs/visual-theme-system.md @@ -2,18 +2,20 @@ BookStack allows visual customization via the theme system which enables you to extensively customize views, translation text & icons. -This theme system itself is maintained and supported but usages of this system, including the files you are able to override, are not considered stable and may change upon any update. You should test any customizations made after updates. +This is part of the theme system alongside the [logical theme system](./logical-theme-system.md). + +**Note:** This theme system itself is maintained and supported but usages of this system, including the files you are able to override, are not considered stable and may change upon any update. You should test any customizations made after updates. ## Getting Started -*[Video Guide](https://www.youtube.com/watch?v=gLy_2GBse48)* +*[Video Guide](https://foss.video/w/ibNY6bGmKFV1tva3Jz4KfA)* This makes use of the theme system. Create a folder for your theme within your BookStack `themes` directory. As an example we'll use `my_theme`, so we'd create a `themes/my_theme` folder. You'll need to tell BookStack to use your theme via the `APP_THEME` option in your `.env` file. For example: `APP_THEME=my_theme`. ## Customizing View Files -Content placed in your `themes//` folder will override the original view files found in the `resources/views` folder. These files are typically [Laravel Blade](https://laravel.com/docs/8.x/blade) files. +Content placed in your `themes//` folder will override the original view files found in the `resources/views` folder. These files are typically [Laravel Blade](https://laravel.com/docs/10.x/blade) files. As an example, I could override the `resources/views/books/parts/list-item.blade.php` file with my own template at the path `themes//books/parts/list-item.blade.php`. ## Customizing Icons @@ -22,7 +24,7 @@ SVG files placed in a `themes//icons` folder will override any icons ## Customizing Text Content -Folders with PHP translation files placed in a `themes//lang` folder will override translations defined within the `resources/lang` folder. Custom translations are merged with the original files so you only need to override the select translations you want to override, you don't need to copy the whole original file. Note that you'll need to include the language folder. +Folders with PHP translation files placed in a `themes//lang` folder will override translations defined within the `lang` folder. Custom translations are merged with the original files, so you only need to override the select translations you want to override, you don't need to copy the whole original file. Note that you'll need to include the language folder. As an example, Say I wanted to change 'Search' to 'Find'; Within a `themes//lang/en/common.php` file I'd set the following: @@ -32,3 +34,24 @@ return [ 'search' => 'find', ]; ``` + +## Publicly Accessible Files + +As part of deeper customizations you may want to expose additional files +(images, scripts, styles, etc...) as part of your theme, in a way so they're +accessible in public web-space to browsers. + +To achieve this, you can put files within a `themes//public` folder. +BookStack will serve any files within this folder from a `/theme/` base path. + +As an example, if I had an image located at `themes/custom/public/cat.jpg`, I could access +that image via the URL path `/theme/custom/cat.jpg`. That's assuming that `custom` is the currently +configured application theme. + +There are some considerations to these publicly served files: + +- Only a predetermined range "web safe" content-types are currently served. + - This limits running into potential insecure scenarios in serving problematic file types. +- A static 1-day cache time it set on files served from this folder. + - You can use alternative cache-breaking techniques (change of query string) upon changes if needed. + - If required, you could likely override caching at the webserver level. diff --git a/dev/licensing/gen-js-licenses b/dev/licensing/gen-js-licenses new file mode 100644 index 00000000000..191a16f302f --- /dev/null +++ b/dev/licensing/gen-js-licenses @@ -0,0 +1,63 @@ +#!/usr/bin/env php +name}"]; + + $license = $package->license ?? ''; + if ($license) { + $output[] = "License: {$license}"; + } else { + warn("Package {$package->name}: No license found"); + } + + $licenseFile = findLicenseFile($package->name, $packagePath); + if ($licenseFile) { + $relLicenseFile = str_replace("{$rootPath}/", '', $licenseFile); + $output[] = "License File: {$relLicenseFile}"; + $copyright = findCopyright($licenseFile); + if ($copyright) { + $output[] = "Copyright: {$copyright}"; + } else { + warn("Package {$package->name}: no copyright found in its license"); + } + } + + $source = $package->repository->url ?? $package->repository ?? ''; + if ($source) { + $output[] = "Source: {$source}"; + } + + $link = $package->homepage ?? $source; + if ($link) { + $output[] = "Link: {$link}"; + } + + return implode("\n", $output); +} diff --git a/dev/licensing/gen-licenses-shared.php b/dev/licensing/gen-licenses-shared.php new file mode 100644 index 00000000000..a8ad350f22d --- /dev/null +++ b/dev/licensing/gen-licenses-shared.php @@ -0,0 +1,66 @@ + 1) { + warn("Package {$packageName}: more than one license file found"); + } + + if (count($foundLicenses) > 0) { + $fileName = basename($foundLicenses[0]); + return "{$packageDir}/{$fileName}"; + } + + warn("Package {$packageName}: no license files found"); + return ''; +} + +function findCopyright(string $licenseFile): string +{ + $fileContents = file_get_contents($licenseFile); + $pattern = '/^.*?copyright (\(c\)|\d{4})[\s\S]*?(\n\n|\.\n)/mi'; + $matches = []; + preg_match($pattern, $fileContents, $matches); + $copyright = trim($matches[0] ?? ''); + + if (str_contains($copyright, 'i.e.')) { + return ''; + } + + $emailPattern = '/([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/i'; + return preg_replace_callback($emailPattern, obfuscateEmail(...), $copyright); +} + +function obfuscateEmail(array $matches): string +{ + return preg_replace('/[^@.]/', '*', $matches[1]); +} + +function warn(string $text): void +{ + global $warnings; + $warnings[] = "WARN:" . $text; +} + +function getWarnings(): array +{ + global $warnings; + return $warnings; +} diff --git a/dev/licensing/gen-php-licenses b/dev/licensing/gen-php-licenses new file mode 100644 index 00000000000..79f4c2ff696 --- /dev/null +++ b/dev/licensing/gen-php-licenses @@ -0,0 +1,55 @@ +#!/usr/bin/env php +packages; +$packageOutput = array_map(packageToOutput(...), $packages); + +$licenseInfo = implode($outputSeparator, $packageOutput) . "\n"; +file_put_contents($outputPath, $licenseInfo); + +echo "License information written to {$outputPath}\n"; +echo implode("\n", getWarnings()) . "\n"; + +function packageToOutput(stdClass $package) : string { + global $rootPath; + $output = ["{$package->name}"]; + + $licenses = is_array($package->license) ? $package->license : [$package->license]; + $output[] = "License: " . implode(' ', $licenses); + + $packagePath = "{$rootPath}/vendor/{$package->name}/package.json"; + $licenseFile = findLicenseFile($package->name, $packagePath); + if ($licenseFile) { + $relLicenseFile = str_replace("{$rootPath}/", '', $licenseFile); + $output[] = "License File: {$relLicenseFile}"; + $copyright = findCopyright($licenseFile); + if ($copyright) { + $output[] = "Copyright: {$copyright}"; + } else { + warn("Package {$package->name}: no copyright found in its license"); + } + } + + $source = $package->source->url; + if ($source) { + $output[] = "Source: {$source}"; + } + + $link = $package->homepage ?? $package->source->url ?? ''; + if ($link) { + $output[] = "Link: {$link}"; + } + + return implode("\n", $output); +} \ No newline at end of file diff --git a/dev/licensing/js-library-licenses.txt b/dev/licensing/js-library-licenses.txt new file mode 100644 index 00000000000..b5fa446a468 --- /dev/null +++ b/dev/licensing/js-library-licenses.txt @@ -0,0 +1,3891 @@ +abab +License: BSD-3-Clause +License File: node_modules/abab/LICENSE.md +Source: git+https://github.com/jsdom/abab.git +Link: https://github.com/jsdom/abab#readme +----------- +acorn-globals +License: MIT +License File: node_modules/acorn-globals/LICENSE +Copyright: Copyright (c) 2014 Forbes Lindesay +Source: https://github.com/ForbesLindesay/acorn-globals.git +Link: https://github.com/ForbesLindesay/acorn-globals.git +----------- +acorn-jsx +License: MIT +License File: node_modules/acorn-jsx/LICENSE +Copyright: Copyright (C) 2012-2017 by Ingvar Stepanyan +Source: https://github.com/acornjs/acorn-jsx +Link: https://github.com/acornjs/acorn-jsx +----------- +acorn-walk +License: MIT +License File: node_modules/acorn-walk/LICENSE +Copyright: Copyright (C) 2012-2020 by various contributors (see AUTHORS) +Source: https://github.com/acornjs/acorn.git +Link: https://github.com/acornjs/acorn +----------- +acorn +License: MIT +License File: node_modules/acorn/LICENSE +Copyright: Copyright (C) 2012-2022 by various contributors (see AUTHORS) +Source: git+https://github.com/acornjs/acorn.git +Link: https://github.com/acornjs/acorn +----------- +agent-base +License: MIT +Source: git://github.com/TooTallNate/node-agent-base.git +Link: git://github.com/TooTallNate/node-agent-base.git +----------- +ajv +License: MIT +License File: node_modules/ajv/LICENSE +Copyright: Copyright (c) 2015-2017 Evgeny Poberezkin +Source: https://github.com/ajv-validator/ajv.git +Link: https://github.com/ajv-validator/ajv +----------- +ansi-escapes +License: MIT +License File: node_modules/ansi-escapes/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (https://sindresorhus.com) +Source: sindresorhus/ansi-escapes +Link: sindresorhus/ansi-escapes +----------- +ansi-regex +License: MIT +License File: node_modules/ansi-regex/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (sindresorhus.com) +Source: chalk/ansi-regex +Link: chalk/ansi-regex +----------- +ansi-styles +License: MIT +License File: node_modules/ansi-styles/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (sindresorhus.com) +Source: chalk/ansi-styles +Link: chalk/ansi-styles +----------- +anymatch +License: ISC +License File: node_modules/anymatch/LICENSE +Copyright: Copyright (c) 2019 Elan Shanker, Paul Miller (https://paulmillr.com) +Source: https://github.com/micromatch/anymatch +Link: https://github.com/micromatch/anymatch +----------- +arg +License: MIT +License File: node_modules/arg/LICENSE.md +Copyright: Copyright (c) 2017-2019 Zeit, Inc. +Source: zeit/arg +Link: zeit/arg +----------- +argparse +License: Python-2.0 +License File: node_modules/argparse/LICENSE +Source: nodeca/argparse +Link: nodeca/argparse +----------- +array-buffer-byte-length +License: MIT +License File: node_modules/array-buffer-byte-length/LICENSE +Copyright: Copyright (c) 2023 Inspect JS +Source: git+https://github.com/inspect-js/array-buffer-byte-length.git +Link: https://github.com/inspect-js/array-buffer-byte-length#readme +----------- +array-includes +License: MIT +License File: node_modules/array-includes/LICENSE +Copyright: Copyright (C) 2015 Jordan Harband +Source: git://github.com/es-shims/array-includes.git +Link: git://github.com/es-shims/array-includes.git +----------- +array.prototype.findlastindex +License: MIT +License File: node_modules/array.prototype.findlastindex/LICENSE +Copyright: Copyright (c) 2021 ECMAScript Shims +Source: git+https://github.com/es-shims/Array.prototype.findLastIndex.git +Link: https://github.com/es-shims/Array.prototype.findLastIndex#readme +----------- +array.prototype.flat +License: MIT +License File: node_modules/array.prototype.flat/LICENSE +Copyright: Copyright (c) 2017 ECMAScript Shims +Source: git://github.com/es-shims/Array.prototype.flat.git +Link: git://github.com/es-shims/Array.prototype.flat.git +----------- +array.prototype.flatmap +License: MIT +License File: node_modules/array.prototype.flatmap/LICENSE +Copyright: Copyright (c) 2017 ECMAScript Shims +Source: git://github.com/es-shims/Array.prototype.flatMap.git +Link: git://github.com/es-shims/Array.prototype.flatMap.git +----------- +arraybuffer.prototype.slice +License: MIT +License File: node_modules/arraybuffer.prototype.slice/LICENSE +Copyright: Copyright (c) 2023 ECMAScript Shims +Source: git+https://github.com/es-shims/ArrayBuffer.prototype.slice.git +Link: https://github.com/es-shims/ArrayBuffer.prototype.slice#readme +----------- +async-function +License: MIT +License File: node_modules/async-function/LICENSE +Copyright: Copyright (c) 2016 EduardoRFS +Source: git+https://github.com/ljharb/async-function.git +Link: https://github.com/ljharb/async-function#readme +----------- +async +License: MIT +License File: node_modules/async/LICENSE +Copyright: Copyright (c) 2010-2018 Caolan McMahon +Source: https://github.com/caolan/async.git +Link: https://caolan.github.io/async/ +----------- +asynckit +License: MIT +License File: node_modules/asynckit/LICENSE +Copyright: Copyright (c) 2016 Alex Indigo +Source: git+https://github.com/alexindigo/asynckit.git +Link: https://github.com/alexindigo/asynckit#readme +----------- +available-typed-arrays +License: MIT +License File: node_modules/available-typed-arrays/LICENSE +Copyright: Copyright (c) 2020 Inspect JS +Source: git+https://github.com/inspect-js/available-typed-arrays.git +Link: https://github.com/inspect-js/available-typed-arrays#readme +----------- +babel-jest +License: MIT +License File: node_modules/babel-jest/LICENSE +Copyright: Copyright (c) Meta Platforms, Inc. and affiliates. +Source: https://github.com/jestjs/jest.git +Link: https://github.com/jestjs/jest.git +----------- +babel-plugin-istanbul +License: BSD-3-Clause +License File: node_modules/babel-plugin-istanbul/LICENSE +Copyright: Copyright (c) 2016, Istanbul Code Coverage +All rights reserved. +Source: git+https://github.com/istanbuljs/babel-plugin-istanbul.git +Link: https://github.com/istanbuljs/babel-plugin-istanbul#readme +----------- +babel-plugin-jest-hoist +License: MIT +License File: node_modules/babel-plugin-jest-hoist/LICENSE +Copyright: Copyright (c) Meta Platforms, Inc. and affiliates. +Source: https://github.com/jestjs/jest.git +Link: https://github.com/jestjs/jest.git +----------- +babel-preset-current-node-syntax +License: MIT +License File: node_modules/babel-preset-current-node-syntax/LICENSE +Copyright: Copyright (c) 2020 Nicolò Ribaudo and other contributors +Source: https://github.com/nicolo-ribaudo/babel-preset-current-node-syntax.git +Link: https://github.com/nicolo-ribaudo/babel-preset-current-node-syntax.git +----------- +babel-preset-jest +License: MIT +License File: node_modules/babel-preset-jest/LICENSE +Copyright: Copyright (c) Meta Platforms, Inc. and affiliates. +Source: https://github.com/jestjs/jest.git +Link: https://github.com/jestjs/jest.git +----------- +balanced-match +License: MIT +License File: node_modules/balanced-match/LICENSE.md +Copyright: Copyright (c) 2013 Julian Gruber <******@************.***> +Source: git://github.com/juliangruber/balanced-match.git +Link: https://github.com/juliangruber/balanced-match +----------- +binary-extensions +License: MIT +License File: node_modules/binary-extensions/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (https://sindresorhus.com) +Copyright (c) Paul Miller (https://paulmillr.com) +Source: sindresorhus/binary-extensions +Link: sindresorhus/binary-extensions +----------- +brace-expansion +License: MIT +License File: node_modules/brace-expansion/LICENSE +Copyright: Copyright (c) 2013 Julian Gruber <******@************.***> +Source: git://github.com/juliangruber/brace-expansion.git +Link: https://github.com/juliangruber/brace-expansion +----------- +braces +License: MIT +License File: node_modules/braces/LICENSE +Copyright: Copyright (c) 2014-present, Jon Schlinkert. +Source: micromatch/braces +Link: https://github.com/micromatch/braces +----------- +browserslist +License: MIT +License File: node_modules/browserslist/LICENSE +Copyright: Copyright 2014 Andrey Sitnik <******@******.**> and other contributors +Source: browserslist/browserslist +Link: browserslist/browserslist +----------- +bs-logger +License: MIT +License File: node_modules/bs-logger/LICENSE +Copyright: Copyright (c) 2018 Huafu Gandon +Source: git+https://github.com/huafu/bs-logger.git +Link: git+https://github.com/huafu/bs-logger.git +----------- +bser +License: Apache-2.0 +Source: https://github.com/facebook/watchman +Link: https://facebook.github.io/watchman/docs/bser.html +----------- +buffer-from +License: MIT +License File: node_modules/buffer-from/LICENSE +Copyright: Copyright (c) 2016, 2018 Linus Unnebäck +Source: LinusU/buffer-from +Link: LinusU/buffer-from +----------- +call-bind-apply-helpers +License: MIT +License File: node_modules/call-bind-apply-helpers/LICENSE +Copyright: Copyright (c) 2024 Jordan Harband +Source: git+https://github.com/ljharb/call-bind-apply-helpers.git +Link: https://github.com/ljharb/call-bind-apply-helpers#readme +----------- +call-bind +License: MIT +License File: node_modules/call-bind/LICENSE +Copyright: Copyright (c) 2020 Jordan Harband +Source: git+https://github.com/ljharb/call-bind.git +Link: https://github.com/ljharb/call-bind#readme +----------- +call-bound +License: MIT +License File: node_modules/call-bound/LICENSE +Copyright: Copyright (c) 2024 Jordan Harband +Source: git+https://github.com/ljharb/call-bound.git +Link: https://github.com/ljharb/call-bound#readme +----------- +callsites +License: MIT +License File: node_modules/callsites/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (sindresorhus.com) +Source: sindresorhus/callsites +Link: sindresorhus/callsites +----------- +camelcase +License: MIT +License File: node_modules/camelcase/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (sindresorhus.com) +Source: sindresorhus/camelcase +Link: sindresorhus/camelcase +----------- +caniuse-lite +License: CC-BY-4.0 +License File: node_modules/caniuse-lite/LICENSE +Source: browserslist/caniuse-lite +Link: browserslist/caniuse-lite +----------- +chalk +License: MIT +License File: node_modules/chalk/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (sindresorhus.com) +Source: chalk/chalk +Link: chalk/chalk +----------- +char-regex +License: MIT +License File: node_modules/char-regex/LICENSE +Source: https://github.com/Richienb/char-regex.git +Link: https://github.com/Richienb/char-regex.git +----------- +chokidar-cli +License: MIT +License File: node_modules/chokidar-cli/LICENSE +Copyright: Copyright (c) 2015 Kimmo Brunfeldt +Source: https://github.com/open-npm-tools/chokidar-cli.git +Link: https://github.com/open-npm-tools/chokidar-cli +----------- +chokidar +License: MIT +License File: node_modules/chokidar/LICENSE +Copyright: Copyright (c) 2012-2019 Paul Miller (https://paulmillr.com), Elan Shanker +Source: git+https://github.com/paulmillr/chokidar.git +Link: https://github.com/paulmillr/chokidar +----------- +ci-info +License: MIT +License File: node_modules/ci-info/LICENSE +Copyright: Copyright (c) 2016 Thomas Watson Steen +Source: https://github.com/watson/ci-info.git +Link: https://github.com/watson/ci-info +----------- +cjs-module-lexer +License: MIT +License File: node_modules/cjs-module-lexer/LICENSE +Copyright: Copyright (C) 2018-2020 Guy Bedford +Source: git+https://github.com/nodejs/cjs-module-lexer.git +Link: https://github.com/nodejs/cjs-module-lexer#readme +----------- +cliui +License: ISC +License File: node_modules/cliui/LICENSE.txt +Copyright: Copyright (c) 2015, Contributors +Source: http://github.com/yargs/cliui.git +Link: http://github.com/yargs/cliui.git +----------- +co +License: MIT +License File: node_modules/co/LICENSE +Copyright: Copyright (c) 2014 TJ Holowaychuk <**@************.**> +Source: tj/co +Link: tj/co +----------- +codemirror +License: MIT +License File: node_modules/codemirror/LICENSE +Copyright: Copyright (C) 2018-2021 by Marijn Haverbeke <******@*********.******> and others +Source: https://github.com/codemirror/basic-setup.git +Link: https://github.com/codemirror/basic-setup.git +----------- +collect-v8-coverage +License: MIT +License File: node_modules/collect-v8-coverage/LICENSE +Copyright: Copyright (c) 2019 Simen Bekkhus +Source: SimenB/collect-v8-coverage +Link: SimenB/collect-v8-coverage +----------- +color-convert +License: MIT +License File: node_modules/color-convert/LICENSE +Copyright: Copyright (c) 2011-2016 Heather Arthur <**********@*****.***> +Source: Qix-/color-convert +Link: Qix-/color-convert +----------- +color-name +License: MIT +License File: node_modules/color-name/LICENSE +Source: git@github.com:colorjs/color-name.git +Link: https://github.com/colorjs/color-name +----------- +combined-stream +License: MIT +License File: node_modules/combined-stream/License +Copyright: Copyright (c) 2011 Debuggable Limited <*****@**********.***> +Source: git://github.com/felixge/node-combined-stream.git +Link: https://github.com/felixge/node-combined-stream +----------- +concat-map +License: MIT +License File: node_modules/concat-map/LICENSE +Source: git://github.com/substack/node-concat-map.git +Link: git://github.com/substack/node-concat-map.git +----------- +convert-source-map +License: MIT +License File: node_modules/convert-source-map/LICENSE +Copyright: Copyright 2013 Thorsten Lorenz. +All rights reserved. +Source: git://github.com/thlorenz/convert-source-map.git +Link: https://github.com/thlorenz/convert-source-map +----------- +create-jest +License: MIT +License File: node_modules/create-jest/LICENSE +Copyright: Copyright (c) Meta Platforms, Inc. and affiliates. +Source: https://github.com/jestjs/jest.git +Link: https://github.com/jestjs/jest.git +----------- +create-require +License: MIT +License File: node_modules/create-require/LICENSE +Copyright: Copyright (c) 2020 +Source: nuxt-contrib/create-require +Link: nuxt-contrib/create-require +----------- +crelt +License: MIT +License File: node_modules/crelt/LICENSE +Copyright: Copyright (C) 2020 by Marijn Haverbeke <******@*********.******> +Source: git+https://github.com/marijnh/crelt.git +Link: https://github.com/marijnh/crelt#readme +----------- +cross-spawn +License: MIT +License File: node_modules/cross-spawn/LICENSE +Copyright: Copyright (c) 2018 Made With MOXY Lda <*****@****.******> +Source: git@github.com:moxystudio/node-cross-spawn.git +Link: https://github.com/moxystudio/node-cross-spawn +----------- +cssom +License: MIT +License File: node_modules/cssom/LICENSE.txt +Copyright: Copyright (c) Nikita Vasilyev +Source: NV/CSSOM +Link: NV/CSSOM +----------- +cssstyle +License: MIT +License File: node_modules/cssstyle/LICENSE +Copyright: Copyright (c) Chad Walker +Source: jsdom/cssstyle +Link: https://github.com/jsdom/cssstyle +----------- +data-urls +License: MIT +License File: node_modules/data-urls/LICENSE.txt +Source: jsdom/data-urls +Link: jsdom/data-urls +----------- +data-view-buffer +License: MIT +License File: node_modules/data-view-buffer/LICENSE +Copyright: Copyright (c) 2023 Jordan Harband +Source: git+https://github.com/inspect-js/data-view-buffer.git +Link: https://github.com/inspect-js/data-view-buffer#readme +----------- +data-view-byte-length +License: MIT +License File: node_modules/data-view-byte-length/LICENSE +Copyright: Copyright (c) 2024 Jordan Harband +Source: git+https://github.com/inspect-js/data-view-byte-length.git +Link: https://github.com/inspect-js/data-view-byte-length#readme +----------- +data-view-byte-offset +License: MIT +License File: node_modules/data-view-byte-offset/LICENSE +Copyright: Copyright (c) 2024 Jordan Harband +Source: git+https://github.com/inspect-js/data-view-byte-offset.git +Link: https://github.com/inspect-js/data-view-byte-offset#readme +----------- +debug +License: MIT +License File: node_modules/debug/LICENSE +Copyright: Copyright (c) 2014-2017 TJ Holowaychuk <**@************.**> +Copyright (c) 2018-2021 Josh Junon +Source: git://github.com/debug-js/debug.git +Link: git://github.com/debug-js/debug.git +----------- +decamelize +License: MIT +License File: node_modules/decamelize/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (sindresorhus.com) +Source: sindresorhus/decamelize +Link: sindresorhus/decamelize +----------- +decimal.js +License: MIT +Source: https://github.com/MikeMcl/decimal.js.git +Link: https://github.com/MikeMcl/decimal.js.git +----------- +dedent +License: MIT +License File: node_modules/dedent/LICENSE.md +Source: https://github.com/dmnd/dedent +Link: https://github.com/dmnd/dedent +----------- +deep-is +License: MIT +License File: node_modules/deep-is/LICENSE +Copyright: Copyright (c) 2012, 2013 Thorsten Lorenz <********@***.**> +Copyright (c) 2012 James Halliday <****@********.***> +Copyright (c) 2009 Thomas Robinson <280north.com> +Source: http://github.com/thlorenz/deep-is.git +Link: http://github.com/thlorenz/deep-is.git +----------- +deepmerge +License: MIT +License File: node_modules/deepmerge/license.txt +Copyright: Copyright (c) 2012 James Halliday, Josh Duff, and other contributors +Source: git://github.com/TehShrike/deepmerge.git +Link: https://github.com/TehShrike/deepmerge +----------- +define-data-property +License: MIT +License File: node_modules/define-data-property/LICENSE +Copyright: Copyright (c) 2023 Jordan Harband +Source: git+https://github.com/ljharb/define-data-property.git +Link: https://github.com/ljharb/define-data-property#readme +----------- +define-properties +License: MIT +License File: node_modules/define-properties/LICENSE +Copyright: Copyright (C) 2015 Jordan Harband +Source: git://github.com/ljharb/define-properties.git +Link: git://github.com/ljharb/define-properties.git +----------- +delayed-stream +License: MIT +License File: node_modules/delayed-stream/License +Copyright: Copyright (c) 2011 Debuggable Limited <*****@**********.***> +Source: git://github.com/felixge/node-delayed-stream.git +Link: https://github.com/felixge/node-delayed-stream +----------- +detect-libc +License: Apache-2.0 +License File: node_modules/detect-libc/LICENSE +Source: git://github.com/lovell/detect-libc +Link: git://github.com/lovell/detect-libc +----------- +detect-newline +License: MIT +License File: node_modules/detect-newline/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (sindresorhus.com) +Source: sindresorhus/detect-newline +Link: sindresorhus/detect-newline +----------- +diff-sequences +License: MIT +License File: node_modules/diff-sequences/LICENSE +Copyright: Copyright (c) Meta Platforms, Inc. and affiliates. +Source: https://github.com/jestjs/jest.git +Link: https://github.com/jestjs/jest.git +----------- +diff +License: BSD-3-Clause +License File: node_modules/diff/LICENSE +Copyright: Copyright (c) 2009-2015, Kevin Decker <********@*****.***> +Source: git://github.com/kpdecker/jsdiff.git +Link: git://github.com/kpdecker/jsdiff.git +----------- +doctrine +License: Apache-2.0 +License File: node_modules/doctrine/LICENSE +Source: eslint/doctrine +Link: https://github.com/eslint/doctrine +----------- +domexception +License: MIT +License File: node_modules/domexception/LICENSE.txt +Source: jsdom/domexception +Link: jsdom/domexception +----------- +dunder-proto +License: MIT +License File: node_modules/dunder-proto/LICENSE +Copyright: Copyright (c) 2024 ECMAScript Shims +Source: git+https://github.com/es-shims/dunder-proto.git +Link: https://github.com/es-shims/dunder-proto#readme +----------- +ejs +License: Apache-2.0 +License File: node_modules/ejs/LICENSE +Source: git://github.com/mde/ejs.git +Link: https://github.com/mde/ejs +----------- +electron-to-chromium +License: ISC +License File: node_modules/electron-to-chromium/LICENSE +Copyright: Copyright 2018 Kilian Valkhof +Source: https://github.com/kilian/electron-to-chromium/ +Link: https://github.com/kilian/electron-to-chromium/ +----------- +emittery +License: MIT +License File: node_modules/emittery/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (https://sindresorhus.com) +Source: sindresorhus/emittery +Link: sindresorhus/emittery +----------- +emoji-regex +License: MIT +License File: node_modules/emoji-regex/LICENSE-MIT.txt +Source: https://github.com/mathiasbynens/emoji-regex.git +Link: https://mths.be/emoji-regex +----------- +entities +License: BSD-2-Clause +License File: node_modules/entities/LICENSE +Copyright: Copyright (c) Felix Böhm +All rights reserved. +Source: git://github.com/fb55/entities.git +Link: git://github.com/fb55/entities.git +----------- +error-ex +License: MIT +License File: node_modules/error-ex/LICENSE +Copyright: Copyright (c) 2015 JD Ballard +Source: qix-/node-error-ex +Link: qix-/node-error-ex +----------- +es-abstract +License: MIT +License File: node_modules/es-abstract/LICENSE +Copyright: Copyright (C) 2015 Jordan Harband +Source: git://github.com/ljharb/es-abstract.git +Link: git://github.com/ljharb/es-abstract.git +----------- +es-define-property +License: MIT +License File: node_modules/es-define-property/LICENSE +Copyright: Copyright (c) 2024 Jordan Harband +Source: git+https://github.com/ljharb/es-define-property.git +Link: https://github.com/ljharb/es-define-property#readme +----------- +es-errors +License: MIT +License File: node_modules/es-errors/LICENSE +Copyright: Copyright (c) 2024 Jordan Harband +Source: git+https://github.com/ljharb/es-errors.git +Link: https://github.com/ljharb/es-errors#readme +----------- +es-object-atoms +License: MIT +License File: node_modules/es-object-atoms/LICENSE +Copyright: Copyright (c) 2024 Jordan Harband +Source: git+https://github.com/ljharb/es-object-atoms.git +Link: https://github.com/ljharb/es-object-atoms#readme +----------- +es-set-tostringtag +License: MIT +License File: node_modules/es-set-tostringtag/LICENSE +Copyright: Copyright (c) 2022 ECMAScript Shims +Source: git+https://github.com/es-shims/es-set-tostringtag.git +Link: https://github.com/es-shims/es-set-tostringtag#readme +----------- +es-shim-unscopables +License: MIT +License File: node_modules/es-shim-unscopables/LICENSE +Copyright: Copyright (c) 2022 Jordan Harband +Source: git+https://github.com/ljharb/es-shim-unscopables.git +Link: https://github.com/ljharb/es-shim-unscopables#readme +----------- +es-to-primitive +License: MIT +License File: node_modules/es-to-primitive/LICENSE +Copyright: Copyright (c) 2015 Jordan Harband +Source: git://github.com/ljharb/es-to-primitive.git +Link: git://github.com/ljharb/es-to-primitive.git +----------- +esbuild +License: MIT +License File: node_modules/esbuild/LICENSE.md +Copyright: Copyright (c) 2020 Evan Wallace +Source: git+https://github.com/evanw/esbuild.git +Link: git+https://github.com/evanw/esbuild.git +----------- +escalade +License: MIT +License File: node_modules/escalade/license +Copyright: Copyright (c) Luke Edwards <****.*********@*****.***> (lukeed.com) +Source: lukeed/escalade +Link: lukeed/escalade +----------- +escape-string-regexp +License: MIT +License File: node_modules/escape-string-regexp/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (https://sindresorhus.com) +Source: sindresorhus/escape-string-regexp +Link: sindresorhus/escape-string-regexp +----------- +escodegen +License: BSD-2-Clause +License File: node_modules/escodegen/LICENSE.BSD +Copyright: Copyright (C) 2012 Yusuke Suzuki (twitter: @Constellation) and other contributors. +Source: http://github.com/estools/escodegen.git +Link: http://github.com/estools/escodegen +----------- +eslint-import-resolver-node +License: MIT +License File: node_modules/eslint-import-resolver-node/LICENSE +Copyright: Copyright (c) 2015 Ben Mosher +Source: https://github.com/import-js/eslint-plugin-import +Link: https://github.com/import-js/eslint-plugin-import +----------- +eslint-module-utils +License: MIT +License File: node_modules/eslint-module-utils/LICENSE +Copyright: Copyright (c) 2015 Ben Mosher +Source: git+https://github.com/import-js/eslint-plugin-import.git +Link: https://github.com/import-js/eslint-plugin-import#readme +----------- +eslint-plugin-import +License: MIT +License File: node_modules/eslint-plugin-import/LICENSE +Copyright: Copyright (c) 2015 Ben Mosher +Source: https://github.com/import-js/eslint-plugin-import +Link: https://github.com/import-js/eslint-plugin-import +----------- +eslint-scope +License: BSD-2-Clause +License File: node_modules/eslint-scope/LICENSE +Copyright: Copyright (C) 2012-2013 Yusuke Suzuki (twitter: @Constellation) and other contributors. +Source: https://github.com/eslint/js.git +Link: https://github.com/eslint/js/blob/main/packages/eslint-scope/README.md +----------- +eslint-visitor-keys +License: Apache-2.0 +License File: node_modules/eslint-visitor-keys/LICENSE +Source: https://github.com/eslint/js.git +Link: https://github.com/eslint/js/blob/main/packages/eslint-visitor-keys/README.md +----------- +eslint +License: MIT +License File: node_modules/eslint/LICENSE +Source: eslint/eslint +Link: https://eslint.org +----------- +espree +License: BSD-2-Clause +License File: node_modules/espree/LICENSE +Copyright: Copyright (c) Open JS Foundation +All rights reserved. +Source: https://github.com/eslint/js.git +Link: https://github.com/eslint/js/blob/main/packages/espree/README.md +----------- +esprima +License: BSD-2-Clause +License File: node_modules/esprima/LICENSE.BSD +Source: https://github.com/jquery/esprima.git +Link: http://esprima.org +----------- +esquery +License: BSD-3-Clause +License File: node_modules/esquery/license.txt +Copyright: Copyright (c) 2013, Joel Feenstra +All rights reserved. +Source: https://github.com/estools/esquery.git +Link: https://github.com/estools/esquery/ +----------- +esrecurse +License: BSD-2-Clause +Source: https://github.com/estools/esrecurse.git +Link: https://github.com/estools/esrecurse +----------- +estraverse +License: BSD-2-Clause +License File: node_modules/estraverse/LICENSE.BSD +Source: http://github.com/estools/estraverse.git +Link: https://github.com/estools/estraverse +----------- +esutils +License: BSD-2-Clause +License File: node_modules/esutils/LICENSE.BSD +Source: http://github.com/estools/esutils.git +Link: https://github.com/estools/esutils +----------- +execa +License: MIT +License File: node_modules/execa/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (https://sindresorhus.com) +Source: sindresorhus/execa +Link: sindresorhus/execa +----------- +exit +Source: git://github.com/cowboy/node-exit.git +Link: https://github.com/cowboy/node-exit +----------- +expect +License: MIT +License File: node_modules/expect/LICENSE +Copyright: Copyright (c) Meta Platforms, Inc. and affiliates. +Source: https://github.com/jestjs/jest.git +Link: https://github.com/jestjs/jest.git +----------- +fast-deep-equal +License: MIT +License File: node_modules/fast-deep-equal/LICENSE +Copyright: Copyright (c) 2017 Evgeny Poberezkin +Source: git+https://github.com/epoberezkin/fast-deep-equal.git +Link: https://github.com/epoberezkin/fast-deep-equal#readme +----------- +fast-json-stable-stringify +License: MIT +License File: node_modules/fast-json-stable-stringify/LICENSE +Copyright: Copyright (c) 2017 Evgeny Poberezkin +Copyright (c) 2013 James Halliday +Source: git://github.com/epoberezkin/fast-json-stable-stringify.git +Link: https://github.com/epoberezkin/fast-json-stable-stringify +----------- +fast-levenshtein +License: MIT +License File: node_modules/fast-levenshtein/LICENSE.md +Copyright: Copyright (c) 2013 [Ramesh Nair](http://www.hiddentao.com/) +Source: https://github.com/hiddentao/fast-levenshtein.git +Link: https://github.com/hiddentao/fast-levenshtein.git +----------- +fb-watchman +License: Apache-2.0 +Source: git@github.com:facebook/watchman.git +Link: https://facebook.github.io/watchman/ +----------- +file-entry-cache +License: MIT +License File: node_modules/file-entry-cache/LICENSE +Copyright: Copyright (c) Roy Riojas & Jared Wray +Source: jaredwray/file-entry-cache +Link: jaredwray/file-entry-cache +----------- +filelist +License: Apache-2.0 +Source: git://github.com/mde/filelist.git +Link: https://github.com/mde/filelist +----------- +fill-range +License: MIT +License File: node_modules/fill-range/LICENSE +Copyright: Copyright (c) 2014-present, Jon Schlinkert. +Source: jonschlinkert/fill-range +Link: https://github.com/jonschlinkert/fill-range +----------- +find-up +License: MIT +License File: node_modules/find-up/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (https://sindresorhus.com) +Source: sindresorhus/find-up +Link: sindresorhus/find-up +----------- +flat-cache +License: MIT +License File: node_modules/flat-cache/LICENSE +Copyright: Copyright (c) Roy Riojas and Jared Wray +Source: jaredwray/flat-cache +Link: jaredwray/flat-cache +----------- +flatted +License: ISC +License File: node_modules/flatted/LICENSE +Copyright: Copyright (c) 2018-2020, Andrea Giammarchi, @WebReflection +Source: git+https://github.com/WebReflection/flatted.git +Link: https://github.com/WebReflection/flatted#readme +----------- +for-each +License: MIT +License File: node_modules/for-each/LICENSE +Copyright: Copyright (c) 2012 Raynos. +Source: https://github.com/Raynos/for-each.git +Link: https://github.com/Raynos/for-each +----------- +form-data +License: MIT +License File: node_modules/form-data/License +Copyright: Copyright (c) 2012 Felix Geisendörfer (*****@**********.***) and contributors +Source: git://github.com/form-data/form-data.git +Link: git://github.com/form-data/form-data.git +----------- +fs.realpath +License: ISC +License File: node_modules/fs.realpath/LICENSE +Copyright: Copyright (c) Isaac Z. Schlueter and Contributors +Source: git+https://github.com/isaacs/fs.realpath.git +Link: git+https://github.com/isaacs/fs.realpath.git +----------- +function-bind +License: MIT +License File: node_modules/function-bind/LICENSE +Copyright: Copyright (c) 2013 Raynos. +Source: https://github.com/Raynos/function-bind.git +Link: https://github.com/Raynos/function-bind +----------- +function.prototype.name +License: MIT +License File: node_modules/function.prototype.name/LICENSE +Copyright: Copyright (c) 2016 Jordan Harband +Source: git://github.com/es-shims/Function.prototype.name.git +Link: git://github.com/es-shims/Function.prototype.name.git +----------- +functions-have-names +License: MIT +License File: node_modules/functions-have-names/LICENSE +Copyright: Copyright (c) 2019 Jordan Harband +Source: git+https://github.com/inspect-js/functions-have-names.git +Link: https://github.com/inspect-js/functions-have-names#readme +----------- +gensync +License: MIT +License File: node_modules/gensync/LICENSE +Copyright: Copyright 2018 Logan Smyth <***********@*****.***> +Source: https://github.com/loganfsmyth/gensync.git +Link: https://github.com/loganfsmyth/gensync +----------- +get-caller-file +License: ISC +License File: node_modules/get-caller-file/LICENSE.md +Copyright: Copyright 2018 Stefan Penner +Source: git+https://github.com/stefanpenner/get-caller-file.git +Link: https://github.com/stefanpenner/get-caller-file#readme +----------- +get-intrinsic +License: MIT +License File: node_modules/get-intrinsic/LICENSE +Copyright: Copyright (c) 2020 Jordan Harband +Source: git+https://github.com/ljharb/get-intrinsic.git +Link: https://github.com/ljharb/get-intrinsic#readme +----------- +get-package-type +License: MIT +License File: node_modules/get-package-type/LICENSE +Copyright: Copyright (c) 2020 CFWare, LLC +Source: git+https://github.com/cfware/get-package-type.git +Link: https://github.com/cfware/get-package-type#readme +----------- +get-proto +License: MIT +License File: node_modules/get-proto/LICENSE +Copyright: Copyright (c) 2025 Jordan Harband +Source: git+https://github.com/ljharb/get-proto.git +Link: https://github.com/ljharb/get-proto#readme +----------- +get-stream +License: MIT +License File: node_modules/get-stream/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (https://sindresorhus.com) +Source: sindresorhus/get-stream +Link: sindresorhus/get-stream +----------- +get-symbol-description +License: MIT +License File: node_modules/get-symbol-description/LICENSE +Copyright: Copyright (c) 2021 Inspect JS +Source: git+https://github.com/inspect-js/get-symbol-description.git +Link: https://github.com/inspect-js/get-symbol-description#readme +----------- +glob-parent +License: ISC +License File: node_modules/glob-parent/LICENSE +Copyright: Copyright (c) 2015, 2019 Elan Shanker +Source: gulpjs/glob-parent +Link: gulpjs/glob-parent +----------- +glob +License: ISC +License File: node_modules/glob/LICENSE +Copyright: Copyright (c) Isaac Z. Schlueter and Contributors +Source: git://github.com/isaacs/node-glob.git +Link: git://github.com/isaacs/node-glob.git +----------- +globals +License: MIT +License File: node_modules/globals/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (https://sindresorhus.com) +Source: sindresorhus/globals +Link: sindresorhus/globals +----------- +globalthis +License: MIT +License File: node_modules/globalthis/LICENSE +Copyright: Copyright (c) 2016 Jordan Harband +Source: git://github.com/ljharb/System.global.git +Link: git://github.com/ljharb/System.global.git +----------- +gopd +License: MIT +License File: node_modules/gopd/LICENSE +Copyright: Copyright (c) 2022 Jordan Harband +Source: git+https://github.com/ljharb/gopd.git +Link: https://github.com/ljharb/gopd#readme +----------- +graceful-fs +License: ISC +License File: node_modules/graceful-fs/LICENSE +Copyright: Copyright (c) 2011-2022 Isaac Z. Schlueter, Ben Noordhuis, and Contributors +Source: https://github.com/isaacs/node-graceful-fs +Link: https://github.com/isaacs/node-graceful-fs +----------- +has-bigints +License: MIT +License File: node_modules/has-bigints/LICENSE +Copyright: Copyright (c) 2019 Jordan Harband +Source: git+https://github.com/ljharb/has-bigints.git +Link: https://github.com/ljharb/has-bigints#readme +----------- +has-flag +License: MIT +License File: node_modules/has-flag/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (sindresorhus.com) +Source: sindresorhus/has-flag +Link: sindresorhus/has-flag +----------- +has-property-descriptors +License: MIT +License File: node_modules/has-property-descriptors/LICENSE +Copyright: Copyright (c) 2022 Inspect JS +Source: git+https://github.com/inspect-js/has-property-descriptors.git +Link: https://github.com/inspect-js/has-property-descriptors#readme +----------- +has-proto +License: MIT +License File: node_modules/has-proto/LICENSE +Copyright: Copyright (c) 2022 Inspect JS +Source: git+https://github.com/inspect-js/has-proto.git +Link: https://github.com/inspect-js/has-proto#readme +----------- +has-symbols +License: MIT +License File: node_modules/has-symbols/LICENSE +Copyright: Copyright (c) 2016 Jordan Harband +Source: git://github.com/inspect-js/has-symbols.git +Link: https://github.com/ljharb/has-symbols#readme +----------- +has-tostringtag +License: MIT +License File: node_modules/has-tostringtag/LICENSE +Copyright: Copyright (c) 2021 Inspect JS +Source: git+https://github.com/inspect-js/has-tostringtag.git +Link: https://github.com/inspect-js/has-tostringtag#readme +----------- +hasown +License: MIT +License File: node_modules/hasown/LICENSE +Copyright: Copyright (c) Jordan Harband and contributors +Source: git+https://github.com/inspect-js/hasOwn.git +Link: https://github.com/inspect-js/hasOwn#readme +----------- +hosted-git-info +License: ISC +License File: node_modules/hosted-git-info/LICENSE +Copyright: Copyright (c) 2015, Rebecca Turner +Source: git+https://github.com/npm/hosted-git-info.git +Link: https://github.com/npm/hosted-git-info +----------- +html-encoding-sniffer +License: MIT +License File: node_modules/html-encoding-sniffer/LICENSE.txt +Source: jsdom/html-encoding-sniffer +Link: jsdom/html-encoding-sniffer +----------- +html-escaper +License: MIT +License File: node_modules/html-escaper/LICENSE.txt +Copyright: Copyright (C) 2017-present by Andrea Giammarchi - @WebReflection +Source: https://github.com/WebReflection/html-escaper.git +Link: https://github.com/WebReflection/html-escaper +----------- +http-proxy-agent +License: MIT +Source: git://github.com/TooTallNate/node-http-proxy-agent.git +Link: git://github.com/TooTallNate/node-http-proxy-agent.git +----------- +https-proxy-agent +License: MIT +Source: git://github.com/TooTallNate/node-https-proxy-agent.git +Link: git://github.com/TooTallNate/node-https-proxy-agent.git +----------- +human-signals +License: Apache-2.0 +License File: node_modules/human-signals/LICENSE +Copyright: Copyright 2019 ehmicky <*******@*****.***> +Source: ehmicky/human-signals +Link: https://git.io/JeluP +----------- +iconv-lite +License: MIT +License File: node_modules/iconv-lite/LICENSE +Copyright: Copyright (c) 2011 Alexander Shtuchkin +Source: git://github.com/ashtuchkin/iconv-lite.git +Link: https://github.com/ashtuchkin/iconv-lite +----------- +idb-keyval +License: Apache-2.0 +Source: git+https://github.com/jakearchibald/idb-keyval.git +Link: https://github.com/jakearchibald/idb-keyval#readme +----------- +ignore +License: MIT +Source: git@github.com:kaelzhang/node-ignore.git +Link: git@github.com:kaelzhang/node-ignore.git +----------- +immutable +License: MIT +License File: node_modules/immutable/LICENSE +Copyright: Copyright (c) 2014-present, Lee Byron and other contributors. +Source: git://github.com/immutable-js/immutable-js.git +Link: https://immutable-js.com +----------- +import-fresh +License: MIT +License File: node_modules/import-fresh/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (https://sindresorhus.com) +Source: sindresorhus/import-fresh +Link: sindresorhus/import-fresh +----------- +import-local +License: MIT +License File: node_modules/import-local/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (https://sindresorhus.com) +Source: sindresorhus/import-local +Link: sindresorhus/import-local +----------- +imurmurhash +License: MIT +Source: https://github.com/jensyt/imurmurhash-js +Link: https://github.com/jensyt/imurmurhash-js +----------- +inflight +License: ISC +License File: node_modules/inflight/LICENSE +Copyright: Copyright (c) Isaac Z. Schlueter +Source: https://github.com/npm/inflight.git +Link: https://github.com/isaacs/inflight +----------- +inherits +License: ISC +License File: node_modules/inherits/LICENSE +Copyright: Copyright (c) Isaac Z. Schlueter +Source: git://github.com/isaacs/inherits +Link: git://github.com/isaacs/inherits +----------- +internal-slot +License: MIT +License File: node_modules/internal-slot/LICENSE +Copyright: Copyright (c) 2019 Jordan Harband +Source: git+https://github.com/ljharb/internal-slot.git +Link: https://github.com/ljharb/internal-slot#readme +----------- +is-array-buffer +License: MIT +License File: node_modules/is-array-buffer/LICENSE +Copyright: Copyright (c) 2015 Chen Gengyuan, Inspect JS +Source: git+https://github.com/inspect-js/is-array-buffer.git +Link: https://github.com/inspect-js/is-array-buffer#readme +----------- +is-arrayish +License: MIT +License File: node_modules/is-arrayish/LICENSE +Copyright: Copyright (c) 2015 JD Ballard +Source: https://github.com/qix-/node-is-arrayish.git +Link: https://github.com/qix-/node-is-arrayish.git +----------- +is-async-function +License: MIT +License File: node_modules/is-async-function/LICENSE +Copyright: Copyright (c) 2021 Jordan Harband +Source: git://github.com/inspect-js/is-async-function.git +Link: git://github.com/inspect-js/is-async-function.git +----------- +is-bigint +License: MIT +License File: node_modules/is-bigint/LICENSE +Copyright: Copyright (c) 2018 Jordan Harband +Source: git+https://github.com/inspect-js/is-bigint.git +Link: https://github.com/inspect-js/is-bigint#readme +----------- +is-binary-path +License: MIT +License File: node_modules/is-binary-path/license +Copyright: Copyright (c) 2019 Sindre Sorhus <************@*****.***> (https://sindresorhus.com), Paul Miller (https://paulmillr.com) +Source: sindresorhus/is-binary-path +Link: sindresorhus/is-binary-path +----------- +is-boolean-object +License: MIT +License File: node_modules/is-boolean-object/LICENSE +Copyright: Copyright (c) 2015 Jordan Harband +Source: git://github.com/inspect-js/is-boolean-object.git +Link: git://github.com/inspect-js/is-boolean-object.git +----------- +is-callable +License: MIT +License File: node_modules/is-callable/LICENSE +Copyright: Copyright (c) 2015 Jordan Harband +Source: git://github.com/inspect-js/is-callable.git +Link: git://github.com/inspect-js/is-callable.git +----------- +is-core-module +License: MIT +License File: node_modules/is-core-module/LICENSE +Copyright: Copyright (c) 2014 Dave Justice +Source: git+https://github.com/inspect-js/is-core-module.git +Link: https://github.com/inspect-js/is-core-module +----------- +is-data-view +License: MIT +License File: node_modules/is-data-view/LICENSE +Copyright: Copyright (c) 2024 Inspect JS +Source: git+https://github.com/inspect-js/is-data-view.git +Link: https://github.com/inspect-js/is-data-view#readme +----------- +is-date-object +License: MIT +License File: node_modules/is-date-object/LICENSE +Copyright: Copyright (c) 2015 Jordan Harband +Source: git://github.com/inspect-js/is-date-object.git +Link: git://github.com/inspect-js/is-date-object.git +----------- +is-extglob +License: MIT +License File: node_modules/is-extglob/LICENSE +Copyright: Copyright (c) 2014-2016, Jon Schlinkert +Source: jonschlinkert/is-extglob +Link: https://github.com/jonschlinkert/is-extglob +----------- +is-finalizationregistry +License: MIT +License File: node_modules/is-finalizationregistry/LICENSE +Copyright: Copyright (c) 2020 Inspect JS +Source: git+https://github.com/inspect-js/is-finalizationregistry.git +Link: https://github.com/inspect-js/is-finalizationregistry#readme +----------- +is-fullwidth-code-point +License: MIT +License File: node_modules/is-fullwidth-code-point/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (sindresorhus.com) +Source: sindresorhus/is-fullwidth-code-point +Link: sindresorhus/is-fullwidth-code-point +----------- +is-generator-fn +License: MIT +License File: node_modules/is-generator-fn/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (sindresorhus.com) +Source: sindresorhus/is-generator-fn +Link: sindresorhus/is-generator-fn +----------- +is-generator-function +License: MIT +License File: node_modules/is-generator-function/LICENSE +Copyright: Copyright (c) 2014 Jordan Harband +Source: git://github.com/inspect-js/is-generator-function.git +Link: git://github.com/inspect-js/is-generator-function.git +----------- +is-glob +License: MIT +License File: node_modules/is-glob/LICENSE +Copyright: Copyright (c) 2014-2017, Jon Schlinkert. +Source: micromatch/is-glob +Link: https://github.com/micromatch/is-glob +----------- +is-map +License: MIT +License File: node_modules/is-map/LICENSE +Copyright: Copyright (c) 2019 Inspect JS +Source: git+https://github.com/inspect-js/is-map.git +Link: https://github.com/inspect-js/is-map#readme +----------- +is-negative-zero +License: MIT +License File: node_modules/is-negative-zero/LICENSE +Copyright: Copyright (c) 2014 Jordan Harband +Source: git://github.com/inspect-js/is-negative-zero.git +Link: https://github.com/inspect-js/is-negative-zero +----------- +is-number-object +License: MIT +License File: node_modules/is-number-object/LICENSE +Copyright: Copyright (c) 2015 Jordan Harband +Source: git://github.com/inspect-js/is-number-object.git +Link: https://github.com/inspect-js/is-number-object#readme +----------- +is-number +License: MIT +License File: node_modules/is-number/LICENSE +Copyright: Copyright (c) 2014-present, Jon Schlinkert. +Source: jonschlinkert/is-number +Link: https://github.com/jonschlinkert/is-number +----------- +is-potential-custom-element-name +License: MIT +License File: node_modules/is-potential-custom-element-name/LICENSE-MIT.txt +Source: https://github.com/mathiasbynens/is-potential-custom-element-name.git +Link: https://github.com/mathiasbynens/is-potential-custom-element-name +----------- +is-regex +License: MIT +License File: node_modules/is-regex/LICENSE +Copyright: Copyright (c) 2014 Jordan Harband +Source: git://github.com/inspect-js/is-regex.git +Link: https://github.com/inspect-js/is-regex +----------- +is-set +License: MIT +License File: node_modules/is-set/LICENSE +Copyright: Copyright (c) 2019 Inspect JS +Source: git+https://github.com/inspect-js/is-set.git +Link: https://github.com/inspect-js/is-set#readme +----------- +is-shared-array-buffer +License: MIT +License File: node_modules/is-shared-array-buffer/LICENSE +Copyright: Copyright (c) 2021 Inspect JS +Source: git+https://github.com/inspect-js/is-shared-array-buffer.git +Link: https://github.com/inspect-js/is-shared-array-buffer#readme +----------- +is-stream +License: MIT +License File: node_modules/is-stream/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (https://sindresorhus.com) +Source: sindresorhus/is-stream +Link: sindresorhus/is-stream +----------- +is-string +License: MIT +License File: node_modules/is-string/LICENSE +Copyright: Copyright (c) 2015 Jordan Harband +Source: git://github.com/inspect-js/is-string.git +Link: git://github.com/inspect-js/is-string.git +----------- +is-symbol +License: MIT +License File: node_modules/is-symbol/LICENSE +Copyright: Copyright (c) 2015 Jordan Harband +Source: git://github.com/inspect-js/is-symbol.git +Link: git://github.com/inspect-js/is-symbol.git +----------- +is-typed-array +License: MIT +License File: node_modules/is-typed-array/LICENSE +Copyright: Copyright (c) 2015 Jordan Harband +Source: git://github.com/inspect-js/is-typed-array.git +Link: git://github.com/inspect-js/is-typed-array.git +----------- +is-weakmap +License: MIT +License File: node_modules/is-weakmap/LICENSE +Copyright: Copyright (c) 2019 Inspect JS +Source: git+https://github.com/inspect-js/is-weakmap.git +Link: https://github.com/inspect-js/is-weakmap#readme +----------- +is-weakref +License: MIT +License File: node_modules/is-weakref/LICENSE +Copyright: Copyright (c) 2020 Inspect JS +Source: git+https://github.com/inspect-js/is-weakref.git +Link: https://github.com/inspect-js/is-weakref#readme +----------- +is-weakset +License: MIT +License File: node_modules/is-weakset/LICENSE +Copyright: Copyright (c) 2019 Inspect JS +Source: git+https://github.com/inspect-js/is-weakset.git +Link: https://github.com/inspect-js/is-weakset#readme +----------- +isarray +License: MIT +License File: node_modules/isarray/LICENSE +Copyright: Copyright (c) 2013 Julian Gruber <******@************.***> +Source: git://github.com/juliangruber/isarray.git +Link: https://github.com/juliangruber/isarray +----------- +isexe +License: ISC +License File: node_modules/isexe/LICENSE +Copyright: Copyright (c) Isaac Z. Schlueter and Contributors +Source: git+https://github.com/isaacs/isexe.git +Link: https://github.com/isaacs/isexe#readme +----------- +istanbul-lib-coverage +License: BSD-3-Clause +License File: node_modules/istanbul-lib-coverage/LICENSE +Copyright: Copyright 2012-2015 Yahoo! Inc. +Source: git+ssh://git@github.com/istanbuljs/istanbuljs.git +Link: https://istanbul.js.org/ +----------- +istanbul-lib-instrument +License: BSD-3-Clause +License File: node_modules/istanbul-lib-instrument/LICENSE +Copyright: Copyright 2012-2015 Yahoo! Inc. +Source: git+ssh://git@github.com/istanbuljs/istanbuljs.git +Link: https://istanbul.js.org/ +----------- +istanbul-lib-report +License: BSD-3-Clause +License File: node_modules/istanbul-lib-report/LICENSE +Copyright: Copyright 2012-2015 Yahoo! Inc. +Source: git+ssh://git@github.com/istanbuljs/istanbuljs.git +Link: https://istanbul.js.org/ +----------- +istanbul-lib-source-maps +License: BSD-3-Clause +License File: node_modules/istanbul-lib-source-maps/LICENSE +Copyright: Copyright 2015 Yahoo! Inc. +Source: git+ssh://git@github.com/istanbuljs/istanbuljs.git +Link: https://istanbul.js.org/ +----------- +istanbul-reports +License: BSD-3-Clause +License File: node_modules/istanbul-reports/LICENSE +Copyright: Copyright 2012-2015 Yahoo! Inc. +Source: git+ssh://git@github.com/istanbuljs/istanbuljs.git +Link: https://istanbul.js.org/ +----------- +jake +License: Apache-2.0 +Source: git://github.com/jakejs/jake.git +Link: git://github.com/jakejs/jake.git +----------- +jest-changed-files +License: MIT +License File: node_modules/jest-changed-files/LICENSE +Copyright: Copyright (c) Meta Platforms, Inc. and affiliates. +Source: https://github.com/jestjs/jest.git +Link: https://github.com/jestjs/jest.git +----------- +jest-circus +License: MIT +License File: node_modules/jest-circus/LICENSE +Copyright: Copyright (c) Meta Platforms, Inc. and affiliates. +Source: https://github.com/jestjs/jest.git +Link: https://github.com/jestjs/jest.git +----------- +jest-cli +License: MIT +License File: node_modules/jest-cli/LICENSE +Copyright: Copyright (c) Meta Platforms, Inc. and affiliates. +Source: https://github.com/jestjs/jest.git +Link: https://jestjs.io/ +----------- +jest-config +License: MIT +License File: node_modules/jest-config/LICENSE +Copyright: Copyright (c) Meta Platforms, Inc. and affiliates. +Source: https://github.com/jestjs/jest.git +Link: https://github.com/jestjs/jest.git +----------- +jest-diff +License: MIT +License File: node_modules/jest-diff/LICENSE +Copyright: Copyright (c) Meta Platforms, Inc. and affiliates. +Source: https://github.com/jestjs/jest.git +Link: https://github.com/jestjs/jest.git +----------- +jest-docblock +License: MIT +License File: node_modules/jest-docblock/LICENSE +Copyright: Copyright (c) Meta Platforms, Inc. and affiliates. +Source: https://github.com/jestjs/jest.git +Link: https://github.com/jestjs/jest.git +----------- +jest-each +License: MIT +License File: node_modules/jest-each/LICENSE +Copyright: Copyright (c) Meta Platforms, Inc. and affiliates. +Source: https://github.com/jestjs/jest.git +Link: https://github.com/jestjs/jest.git +----------- +jest-environment-jsdom +License: MIT +License File: node_modules/jest-environment-jsdom/LICENSE +Copyright: Copyright (c) Meta Platforms, Inc. and affiliates. +Source: https://github.com/jestjs/jest.git +Link: https://github.com/jestjs/jest.git +----------- +jest-environment-node +License: MIT +License File: node_modules/jest-environment-node/LICENSE +Copyright: Copyright (c) Meta Platforms, Inc. and affiliates. +Source: https://github.com/jestjs/jest.git +Link: https://github.com/jestjs/jest.git +----------- +jest-get-type +License: MIT +License File: node_modules/jest-get-type/LICENSE +Copyright: Copyright (c) Meta Platforms, Inc. and affiliates. +Source: https://github.com/jestjs/jest.git +Link: https://github.com/jestjs/jest.git +----------- +jest-haste-map +License: MIT +License File: node_modules/jest-haste-map/LICENSE +Copyright: Copyright (c) Meta Platforms, Inc. and affiliates. +Source: https://github.com/jestjs/jest.git +Link: https://github.com/jestjs/jest.git +----------- +jest-leak-detector +License: MIT +License File: node_modules/jest-leak-detector/LICENSE +Copyright: Copyright (c) Meta Platforms, Inc. and affiliates. +Source: https://github.com/jestjs/jest.git +Link: https://github.com/jestjs/jest.git +----------- +jest-matcher-utils +License: MIT +License File: node_modules/jest-matcher-utils/LICENSE +Copyright: Copyright (c) Meta Platforms, Inc. and affiliates. +Source: https://github.com/jestjs/jest.git +Link: https://github.com/jestjs/jest.git +----------- +jest-message-util +License: MIT +License File: node_modules/jest-message-util/LICENSE +Copyright: Copyright (c) Meta Platforms, Inc. and affiliates. +Source: https://github.com/jestjs/jest.git +Link: https://github.com/jestjs/jest.git +----------- +jest-mock +License: MIT +License File: node_modules/jest-mock/LICENSE +Copyright: Copyright (c) Meta Platforms, Inc. and affiliates. +Source: https://github.com/jestjs/jest.git +Link: https://github.com/jestjs/jest.git +----------- +jest-pnp-resolver +License: MIT +Source: https://github.com/arcanis/jest-pnp-resolver.git +Link: https://github.com/arcanis/jest-pnp-resolver +----------- +jest-regex-util +License: MIT +License File: node_modules/jest-regex-util/LICENSE +Copyright: Copyright (c) Meta Platforms, Inc. and affiliates. +Source: https://github.com/jestjs/jest.git +Link: https://github.com/jestjs/jest.git +----------- +jest-resolve-dependencies +License: MIT +License File: node_modules/jest-resolve-dependencies/LICENSE +Copyright: Copyright (c) Meta Platforms, Inc. and affiliates. +Source: https://github.com/jestjs/jest.git +Link: https://github.com/jestjs/jest.git +----------- +jest-resolve +License: MIT +License File: node_modules/jest-resolve/LICENSE +Copyright: Copyright (c) Meta Platforms, Inc. and affiliates. +Source: https://github.com/jestjs/jest.git +Link: https://github.com/jestjs/jest.git +----------- +jest-runner +License: MIT +License File: node_modules/jest-runner/LICENSE +Copyright: Copyright (c) Meta Platforms, Inc. and affiliates. +Source: https://github.com/jestjs/jest.git +Link: https://github.com/jestjs/jest.git +----------- +jest-runtime +License: MIT +License File: node_modules/jest-runtime/LICENSE +Copyright: Copyright (c) Meta Platforms, Inc. and affiliates. +Source: https://github.com/jestjs/jest.git +Link: https://github.com/jestjs/jest.git +----------- +jest-snapshot +License: MIT +License File: node_modules/jest-snapshot/LICENSE +Copyright: Copyright (c) Meta Platforms, Inc. and affiliates. +Source: https://github.com/jestjs/jest.git +Link: https://github.com/jestjs/jest.git +----------- +jest-util +License: MIT +License File: node_modules/jest-util/LICENSE +Copyright: Copyright (c) Meta Platforms, Inc. and affiliates. +Source: https://github.com/jestjs/jest.git +Link: https://github.com/jestjs/jest.git +----------- +jest-validate +License: MIT +License File: node_modules/jest-validate/LICENSE +Copyright: Copyright (c) Meta Platforms, Inc. and affiliates. +Source: https://github.com/jestjs/jest.git +Link: https://github.com/jestjs/jest.git +----------- +jest-watcher +License: MIT +License File: node_modules/jest-watcher/LICENSE +Copyright: Copyright (c) Meta Platforms, Inc. and affiliates. +Source: https://github.com/jestjs/jest.git +Link: https://jestjs.io/ +----------- +jest-worker +License: MIT +License File: node_modules/jest-worker/LICENSE +Copyright: Copyright (c) Meta Platforms, Inc. and affiliates. +Source: https://github.com/jestjs/jest.git +Link: https://github.com/jestjs/jest.git +----------- +jest +License: MIT +License File: node_modules/jest/LICENSE +Copyright: Copyright (c) Meta Platforms, Inc. and affiliates. +Source: https://github.com/jestjs/jest.git +Link: https://jestjs.io/ +----------- +js-tokens +License: MIT +License File: node_modules/js-tokens/LICENSE +Copyright: Copyright (c) 2014, 2015, 2016, 2017, 2018 Simon Lydell +Source: lydell/js-tokens +Link: lydell/js-tokens +----------- +js-yaml +License: MIT +License File: node_modules/js-yaml/LICENSE +Copyright: Copyright (C) 2011-2015 by Vitaly Puzrin +Source: nodeca/js-yaml +Link: nodeca/js-yaml +----------- +jsdom +License: MIT +License File: node_modules/jsdom/LICENSE.txt +Copyright: Copyright (c) 2010 Elijah Insua +Source: jsdom/jsdom +Link: jsdom/jsdom +----------- +jsesc +License: MIT +License File: node_modules/jsesc/LICENSE-MIT.txt +Source: https://github.com/mathiasbynens/jsesc.git +Link: https://mths.be/jsesc +----------- +json-buffer +License: MIT +License File: node_modules/json-buffer/LICENSE +Copyright: Copyright (c) 2013 Dominic Tarr +Source: git://github.com/dominictarr/json-buffer.git +Link: https://github.com/dominictarr/json-buffer +----------- +json-parse-better-errors +License: MIT +License File: node_modules/json-parse-better-errors/LICENSE.md +Copyright: Copyright 2017 Kat Marchán +Source: https://github.com/zkat/json-parse-better-errors +Link: https://github.com/zkat/json-parse-better-errors +----------- +json-parse-even-better-errors +License: MIT +License File: node_modules/json-parse-even-better-errors/LICENSE.md +Copyright: Copyright 2017 Kat Marchán +Copyright npm, Inc. +Source: https://github.com/npm/json-parse-even-better-errors +Link: https://github.com/npm/json-parse-even-better-errors +----------- +json-schema-traverse +License: MIT +License File: node_modules/json-schema-traverse/LICENSE +Copyright: Copyright (c) 2017 Evgeny Poberezkin +Source: git+https://github.com/epoberezkin/json-schema-traverse.git +Link: https://github.com/epoberezkin/json-schema-traverse#readme +----------- +json-stable-stringify-without-jsonify +License: MIT +License File: node_modules/json-stable-stringify-without-jsonify/LICENSE +Source: git://github.com/samn/json-stable-stringify.git +Link: https://github.com/samn/json-stable-stringify +----------- +json5 +License: MIT +License File: node_modules/json5/LICENSE.md +Copyright: Copyright (c) 2012-2018 Aseem Kishore, and [others]. +Source: git+https://github.com/json5/json5.git +Link: http://json5.org/ +----------- +keyv +License: MIT +Source: git+https://github.com/jaredwray/keyv.git +Link: https://github.com/jaredwray/keyv +----------- +kleur +License: MIT +License File: node_modules/kleur/license +Copyright: Copyright (c) Luke Edwards <****.*********@*****.***> (lukeed.com) +Source: lukeed/kleur +Link: lukeed/kleur +----------- +leven +License: MIT +License File: node_modules/leven/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (sindresorhus.com) +Source: sindresorhus/leven +Link: sindresorhus/leven +----------- +levn +License: MIT +License File: node_modules/levn/LICENSE +Copyright: Copyright (c) George Zahariev +Source: git://github.com/gkz/levn.git +Link: https://github.com/gkz/levn +----------- +lines-and-columns +License: MIT +License File: node_modules/lines-and-columns/LICENSE +Copyright: Copyright (c) 2015 Brian Donovan +Source: https://github.com/eventualbuddha/lines-and-columns.git +Link: https://github.com/eventualbuddha/lines-and-columns#readme +----------- +linkify-it +License: MIT +License File: node_modules/linkify-it/LICENSE +Copyright: Copyright (c) 2015 Vitaly Puzrin. +Source: markdown-it/linkify-it +Link: markdown-it/linkify-it +----------- +livereload-js +License: MIT +License File: node_modules/livereload-js/LICENSE +Copyright: Copyright (c) 2010-2012 Andrey Tarantsov +Source: git://github.com/livereload/livereload-js.git +Link: https://github.com/livereload/livereload-js +----------- +livereload +License: MIT +License File: node_modules/livereload/LICENSE +Copyright: Copyright (c) 2010 Joshua Peek +Source: http://github.com/napcs/node-livereload.git +Link: http://github.com/napcs/node-livereload.git +----------- +load-json-file +License: MIT +License File: node_modules/load-json-file/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (sindresorhus.com) +Source: sindresorhus/load-json-file +Link: sindresorhus/load-json-file +----------- +locate-path +License: MIT +License File: node_modules/locate-path/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (https://sindresorhus.com) +Source: sindresorhus/locate-path +Link: sindresorhus/locate-path +----------- +lodash.debounce +License: MIT +License File: node_modules/lodash.debounce/LICENSE +Source: lodash/lodash +Link: https://lodash.com/ +----------- +lodash.memoize +License: MIT +License File: node_modules/lodash.memoize/LICENSE +Source: lodash/lodash +Link: https://lodash.com/ +----------- +lodash.merge +License: MIT +License File: node_modules/lodash.merge/LICENSE +Source: lodash/lodash +Link: https://lodash.com/ +----------- +lodash.throttle +License: MIT +License File: node_modules/lodash.throttle/LICENSE +Source: lodash/lodash +Link: https://lodash.com/ +----------- +lru-cache +License: ISC +License File: node_modules/lru-cache/LICENSE +Copyright: Copyright (c) Isaac Z. Schlueter and Contributors +Source: git://github.com/isaacs/node-lru-cache.git +Link: git://github.com/isaacs/node-lru-cache.git +----------- +make-dir +License: MIT +License File: node_modules/make-dir/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (https://sindresorhus.com) +Source: sindresorhus/make-dir +Link: sindresorhus/make-dir +----------- +make-error +License: ISC +License File: node_modules/make-error/LICENSE +Copyright: Copyright 2014 Julien Fontanet +Source: git://github.com/JsCommunity/make-error.git +Link: https://github.com/JsCommunity/make-error +----------- +makeerror +License: BSD-3-Clause +License File: node_modules/makeerror/license +Copyright: Copyright (c) 2014, Naitik Shah. All rights reserved. +Source: https://github.com/daaku/nodejs-makeerror +Link: https://github.com/daaku/nodejs-makeerror +----------- +markdown-it-task-lists +License: ISC +License File: node_modules/markdown-it-task-lists/LICENSE +Copyright: Copyright (c) 2016, Revin Guillen +Source: git@github.com:revin/markdown-it-task-lists.git +Link: https://github.com/revin/markdown-it-task-lists#readme +----------- +markdown-it +License: MIT +License File: node_modules/markdown-it/LICENSE +Copyright: Copyright (c) 2014 Vitaly Puzrin, Alex Kocharin. +Source: markdown-it/markdown-it +Link: markdown-it/markdown-it +----------- +math-intrinsics +License: MIT +License File: node_modules/math-intrinsics/LICENSE +Copyright: Copyright (c) 2024 ECMAScript Shims +Source: git+https://github.com/es-shims/math-intrinsics.git +Link: https://github.com/es-shims/math-intrinsics#readme +----------- +mdurl +License: MIT +License File: node_modules/mdurl/LICENSE +Copyright: Copyright (c) 2015 Vitaly Puzrin, Alex Kocharin. +Source: markdown-it/mdurl +Link: markdown-it/mdurl +----------- +memorystream +License File: node_modules/memorystream/LICENSE +Copyright: Copyright (C) 2011 Dmitry Nizovtsev +Source: https://github.com/JSBizon/node-memorystream.git +Link: https://github.com/JSBizon/node-memorystream +----------- +merge-stream +License: MIT +License File: node_modules/merge-stream/LICENSE +Copyright: Copyright (c) Stephen Sugden <**@*************.***> (stephensugden.com) +Source: grncdr/merge-stream +Link: grncdr/merge-stream +----------- +micromatch +License: MIT +License File: node_modules/micromatch/LICENSE +Copyright: Copyright (c) 2014-present, Jon Schlinkert. +Source: micromatch/micromatch +Link: https://github.com/micromatch/micromatch +----------- +mime-db +License: MIT +License File: node_modules/mime-db/LICENSE +Copyright: Copyright (c) 2014 Jonathan Ong <**@***********.***> +Copyright (c) 2015-2022 Douglas Christopher Wilson <****@*************.***> +Source: jshttp/mime-db +Link: jshttp/mime-db +----------- +mime-types +License: MIT +License File: node_modules/mime-types/LICENSE +Copyright: Copyright (c) 2014 Jonathan Ong <**@***********.***> +Copyright (c) 2015 Douglas Christopher Wilson <****@*************.***> +Source: jshttp/mime-types +Link: jshttp/mime-types +----------- +mimic-fn +License: MIT +License File: node_modules/mimic-fn/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (sindresorhus.com) +Source: sindresorhus/mimic-fn +Link: sindresorhus/mimic-fn +----------- +minimatch +License: ISC +License File: node_modules/minimatch/LICENSE +Copyright: Copyright (c) Isaac Z. Schlueter and Contributors +Source: git://github.com/isaacs/minimatch.git +Link: git://github.com/isaacs/minimatch.git +----------- +minimist +License: MIT +License File: node_modules/minimist/LICENSE +Source: git://github.com/minimistjs/minimist.git +Link: https://github.com/minimistjs/minimist +----------- +ms +License: MIT +License File: node_modules/ms/license.md +Copyright: Copyright (c) 2020 Vercel, Inc. +Source: vercel/ms +Link: vercel/ms +----------- +natural-compare +License: MIT +Source: git://github.com/litejs/natural-compare-lite.git +Link: git://github.com/litejs/natural-compare-lite.git +----------- +nice-try +License: MIT +License File: node_modules/nice-try/LICENSE +Copyright: Copyright (c) 2018 Tobias Reich +Source: https://github.com/electerious/nice-try.git +Link: https://github.com/electerious/nice-try +----------- +node-addon-api +License: MIT +License File: node_modules/node-addon-api/LICENSE.md +Copyright: Copyright (c) 2017 [Node.js API collaborators](https://github.com/nodejs/node-addon-api#collaborators) +Source: git://github.com/nodejs/node-addon-api.git +Link: https://github.com/nodejs/node-addon-api +----------- +node-int64 +License: MIT +License File: node_modules/node-int64/LICENSE +Copyright: Copyright (c) 2014 Robert Kieffer +Source: https://github.com/broofa/node-int64 +Link: https://github.com/broofa/node-int64 +----------- +node-releases +License: MIT +License File: node_modules/node-releases/LICENSE +Copyright: Copyright (c) 2017 Sergey Rubanov (https://github.com/chicoxyzzy) +Source: git+https://github.com/chicoxyzzy/node-releases.git +Link: git+https://github.com/chicoxyzzy/node-releases.git +----------- +normalize-package-data +License: BSD-2-Clause +License File: node_modules/normalize-package-data/LICENSE +Copyright: Copyright (c) Meryn Stol ("Author") +All rights reserved. +Source: git://github.com/npm/normalize-package-data.git +Link: git://github.com/npm/normalize-package-data.git +----------- +normalize-path +License: MIT +License File: node_modules/normalize-path/LICENSE +Copyright: Copyright (c) 2014-2018, Jon Schlinkert. +Source: jonschlinkert/normalize-path +Link: https://github.com/jonschlinkert/normalize-path +----------- +npm-run-all +License: MIT +License File: node_modules/npm-run-all/LICENSE +Copyright: Copyright (c) 2015 Toru Nagashima +Source: mysticatea/npm-run-all +Link: https://github.com/mysticatea/npm-run-all +----------- +npm-run-path +License: MIT +License File: node_modules/npm-run-path/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (sindresorhus.com) +Source: sindresorhus/npm-run-path +Link: sindresorhus/npm-run-path +----------- +nwsapi +License: MIT +License File: node_modules/nwsapi/LICENSE +Copyright: Copyright (c) 2007-2025 Diego Perini (http://www.iport.it/) +Source: git://github.com/dperini/nwsapi.git +Link: https://javascript.nwbox.com/nwsapi/ +----------- +object-inspect +License: MIT +License File: node_modules/object-inspect/LICENSE +Copyright: Copyright (c) 2013 James Halliday +Source: git://github.com/inspect-js/object-inspect.git +Link: https://github.com/inspect-js/object-inspect +----------- +object-keys +License: MIT +License File: node_modules/object-keys/LICENSE +Copyright: Copyright (C) 2013 Jordan Harband +Source: git://github.com/ljharb/object-keys.git +Link: git://github.com/ljharb/object-keys.git +----------- +object.assign +License: MIT +License File: node_modules/object.assign/LICENSE +Copyright: Copyright (c) 2014 Jordan Harband +Source: git://github.com/ljharb/object.assign.git +Link: git://github.com/ljharb/object.assign.git +----------- +object.fromentries +License: MIT +License File: node_modules/object.fromentries/LICENSE +Copyright: Copyright (c) 2018 Jordan Harband +Source: git://github.com/es-shims/Object.fromEntries.git +Link: git://github.com/es-shims/Object.fromEntries.git +----------- +object.groupby +License: MIT +License File: node_modules/object.groupby/LICENSE +Copyright: Copyright (c) 2023 ECMAScript Shims +Source: git+https://github.com/es-shims/Object.groupBy.git +Link: https://github.com/es-shims/Object.groupBy#readme +----------- +object.values +License: MIT +License File: node_modules/object.values/LICENSE +Copyright: Copyright (c) 2015 Jordan Harband +Source: git://github.com/es-shims/Object.values.git +Link: git://github.com/es-shims/Object.values.git +----------- +once +License: ISC +License File: node_modules/once/LICENSE +Copyright: Copyright (c) Isaac Z. Schlueter and Contributors +Source: git://github.com/isaacs/once +Link: git://github.com/isaacs/once +----------- +onetime +License: MIT +License File: node_modules/onetime/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (https://sindresorhus.com) +Source: sindresorhus/onetime +Link: sindresorhus/onetime +----------- +optionator +License: MIT +License File: node_modules/optionator/LICENSE +Copyright: Copyright (c) George Zahariev +Source: git://github.com/gkz/optionator.git +Link: https://github.com/gkz/optionator +----------- +opts +License: BSD-2-Clause +License File: node_modules/opts/LICENSE.txt +Copyright: Copyright (c) 2010, Joey Mazzarelli +All rights reserved. +Source: github:khtdr/opts +Link: http://khtdr.com/opts +----------- +own-keys +License: MIT +License File: node_modules/own-keys/LICENSE +Copyright: Copyright (c) 2024 Jordan Harband +Source: git+https://github.com/ljharb/own-keys.git +Link: https://github.com/ljharb/own-keys#readme +----------- +p-limit +License: MIT +License File: node_modules/p-limit/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (https://sindresorhus.com) +Source: sindresorhus/p-limit +Link: sindresorhus/p-limit +----------- +p-locate +License: MIT +License File: node_modules/p-locate/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (https://sindresorhus.com) +Source: sindresorhus/p-locate +Link: sindresorhus/p-locate +----------- +p-try +License: MIT +License File: node_modules/p-try/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (sindresorhus.com) +Source: sindresorhus/p-try +Link: sindresorhus/p-try +----------- +parent-module +License: MIT +License File: node_modules/parent-module/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (sindresorhus.com) +Source: sindresorhus/parent-module +Link: sindresorhus/parent-module +----------- +parse-json +License: MIT +License File: node_modules/parse-json/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (https://sindresorhus.com) +Source: sindresorhus/parse-json +Link: sindresorhus/parse-json +----------- +parse5 +License: MIT +License File: node_modules/parse5/LICENSE +Copyright: Copyright (c) 2013-2019 Ivan Nikulin (******@*****.***, https://github.com/inikulin) +Source: git://github.com/inikulin/parse5.git +Link: https://parse5.js.org +----------- +path-exists +License: MIT +License File: node_modules/path-exists/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (sindresorhus.com) +Source: sindresorhus/path-exists +Link: sindresorhus/path-exists +----------- +path-is-absolute +License: MIT +License File: node_modules/path-is-absolute/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (sindresorhus.com) +Source: sindresorhus/path-is-absolute +Link: sindresorhus/path-is-absolute +----------- +path-key +License: MIT +License File: node_modules/path-key/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (sindresorhus.com) +Source: sindresorhus/path-key +Link: sindresorhus/path-key +----------- +path-parse +License: MIT +License File: node_modules/path-parse/LICENSE +Copyright: Copyright (c) 2015 Javier Blanco +Source: https://github.com/jbgutierrez/path-parse.git +Link: https://github.com/jbgutierrez/path-parse#readme +----------- +path-type +License: MIT +License File: node_modules/path-type/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (sindresorhus.com) +Source: sindresorhus/path-type +Link: sindresorhus/path-type +----------- +picocolors +License: ISC +License File: node_modules/picocolors/LICENSE +Copyright: Copyright (c) 2021-2024 Oleksii Raspopov, Kostiantyn Denysov, Anton Verinov +Source: alexeyraspopov/picocolors +Link: alexeyraspopov/picocolors +----------- +picomatch +License: MIT +License File: node_modules/picomatch/LICENSE +Copyright: Copyright (c) 2017-present, Jon Schlinkert. +Source: micromatch/picomatch +Link: https://github.com/micromatch/picomatch +----------- +pidtree +License: MIT +License File: node_modules/pidtree/license +Copyright: Copyright (c) 2018 Simone Primarosa +Source: github:simonepri/pidtree +Link: http://github.com/simonepri/pidtree#readme +----------- +pify +License: MIT +License File: node_modules/pify/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (sindresorhus.com) +Source: sindresorhus/pify +Link: sindresorhus/pify +----------- +pirates +License: MIT +License File: node_modules/pirates/LICENSE +Copyright: Copyright (c) 2016-2018 Ari Porad +Source: https://github.com/danez/pirates.git +Link: https://github.com/danez/pirates#readme +----------- +pkg-dir +License: MIT +License File: node_modules/pkg-dir/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (sindresorhus.com) +Source: sindresorhus/pkg-dir +Link: sindresorhus/pkg-dir +----------- +possible-typed-array-names +License: MIT +License File: node_modules/possible-typed-array-names/LICENSE +Copyright: Copyright (c) 2024 Jordan Harband +Source: git+https://github.com/ljharb/possible-typed-array-names.git +Link: https://github.com/ljharb/possible-typed-array-names#readme +----------- +prelude-ls +License: MIT +License File: node_modules/prelude-ls/LICENSE +Copyright: Copyright (c) George Zahariev +Source: git://github.com/gkz/prelude-ls.git +Link: http://preludels.com +----------- +pretty-format +License: MIT +License File: node_modules/pretty-format/LICENSE +Copyright: Copyright (c) Meta Platforms, Inc. and affiliates. +Source: https://github.com/jestjs/jest.git +Link: https://github.com/jestjs/jest.git +----------- +prompts +License: MIT +License File: node_modules/prompts/license +Copyright: Copyright (c) 2018 Terkel Gjervig Nielsen +Source: terkelg/prompts +Link: terkelg/prompts +----------- +psl +License: MIT +License File: node_modules/psl/LICENSE +Copyright: Copyright (c) 2017 Lupo Montero ***********@*****.*** +Source: git@github.com:lupomontero/psl.git +Link: git@github.com:lupomontero/psl.git +----------- +punycode.js +License: MIT +License File: node_modules/punycode.js/LICENSE-MIT.txt +Source: https://github.com/mathiasbynens/punycode.js.git +Link: https://mths.be/punycode +----------- +punycode +License: MIT +License File: node_modules/punycode/LICENSE-MIT.txt +Source: https://github.com/mathiasbynens/punycode.js.git +Link: https://mths.be/punycode +----------- +pure-rand +License: MIT +License File: node_modules/pure-rand/LICENSE +Copyright: Copyright (c) 2018 Nicolas DUBIEN +Source: git+https://github.com/dubzzz/pure-rand.git +Link: https://github.com/dubzzz/pure-rand#readme +----------- +querystringify +License: MIT +License File: node_modules/querystringify/LICENSE +Copyright: Copyright (c) 2015 Unshift.io, Arnout Kazemier, the Contributors. +Source: https://github.com/unshiftio/querystringify +Link: https://github.com/unshiftio/querystringify +----------- +react-is +License: MIT +License File: node_modules/react-is/LICENSE +Copyright: Copyright (c) Facebook, Inc. and its affiliates. +Source: https://github.com/facebook/react.git +Link: https://reactjs.org/ +----------- +read-pkg +License: MIT +License File: node_modules/read-pkg/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (sindresorhus.com) +Source: sindresorhus/read-pkg +Link: sindresorhus/read-pkg +----------- +readdirp +License: MIT +License File: node_modules/readdirp/LICENSE +Copyright: Copyright (c) 2012-2019 Thorsten Lorenz, Paul Miller (https://paulmillr.com) +Source: git://github.com/paulmillr/readdirp.git +Link: https://github.com/paulmillr/readdirp +----------- +reflect.getprototypeof +License: MIT +License File: node_modules/reflect.getprototypeof/LICENSE +Copyright: Copyright (c) 2021 ECMAScript Shims +Source: git+https://github.com/es-shims/Reflect.getPrototypeOf.git +Link: https://github.com/es-shims/Reflect.getPrototypeOf +----------- +regexp.prototype.flags +License: MIT +License File: node_modules/regexp.prototype.flags/LICENSE +Copyright: Copyright (C) 2014 Jordan Harband +Source: git://github.com/es-shims/RegExp.prototype.flags.git +Link: git://github.com/es-shims/RegExp.prototype.flags.git +----------- +require-directory +License: MIT +License File: node_modules/require-directory/LICENSE +Copyright: Copyright (c) 2011 Troy Goode <*********@*****.***> +Source: git://github.com/troygoode/node-require-directory.git +Link: https://github.com/troygoode/node-require-directory/ +----------- +require-main-filename +License: ISC +License File: node_modules/require-main-filename/LICENSE.txt +Copyright: Copyright (c) 2016, Contributors +Source: git+ssh://git@github.com/yargs/require-main-filename.git +Link: https://github.com/yargs/require-main-filename#readme +----------- +requires-port +License: MIT +License File: node_modules/requires-port/LICENSE +Copyright: Copyright (c) 2015 Unshift.io, Arnout Kazemier, the Contributors. +Source: https://github.com/unshiftio/requires-port +Link: https://github.com/unshiftio/requires-port +----------- +resolve-cwd +License: MIT +License File: node_modules/resolve-cwd/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (sindresorhus.com) +Source: sindresorhus/resolve-cwd +Link: sindresorhus/resolve-cwd +----------- +resolve-from +License: MIT +License File: node_modules/resolve-from/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (sindresorhus.com) +Source: sindresorhus/resolve-from +Link: sindresorhus/resolve-from +----------- +resolve.exports +License: MIT +License File: node_modules/resolve.exports/license +Copyright: Copyright (c) Luke Edwards <****.*********@*****.***> (lukeed.com) +Source: lukeed/resolve.exports +Link: lukeed/resolve.exports +----------- +resolve +License: MIT +License File: node_modules/resolve/LICENSE +Copyright: Copyright (c) 2012 James Halliday +Source: git://github.com/browserify/resolve.git +Link: git://github.com/browserify/resolve.git +----------- +safe-array-concat +License: MIT +License File: node_modules/safe-array-concat/LICENSE +Copyright: Copyright (c) 2023 Jordan Harband +Source: git+https://github.com/ljharb/safe-array-concat.git +Link: https://github.com/ljharb/safe-array-concat#readme +----------- +safe-push-apply +License: MIT +License File: node_modules/safe-push-apply/LICENSE +Copyright: Copyright (c) 2024 Jordan Harband +Source: git+https://github.com/ljharb/safe-push-apply.git +Link: https://github.com/ljharb/safe-push-apply#readme +----------- +safe-regex-test +License: MIT +License File: node_modules/safe-regex-test/LICENSE +Copyright: Copyright (c) 2022 Jordan Harband +Source: git+https://github.com/ljharb/safe-regex-test.git +Link: https://github.com/ljharb/safe-regex-test#readme +----------- +safer-buffer +License: MIT +License File: node_modules/safer-buffer/LICENSE +Copyright: Copyright (c) 2018 Nikita Skovoroda <********@*****.***> +Source: git+https://github.com/ChALkeR/safer-buffer.git +Link: git+https://github.com/ChALkeR/safer-buffer.git +----------- +sass +License: MIT +License File: node_modules/sass/LICENSE +Copyright: Copyright (c) 2016, Google Inc. +Source: https://github.com/sass/dart-sass +Link: https://github.com/sass/dart-sass +----------- +saxes +License: ISC +Source: https://github.com/lddubeau/saxes.git +Link: https://github.com/lddubeau/saxes.git +----------- +semver +License: ISC +License File: node_modules/semver/LICENSE +Copyright: Copyright (c) Isaac Z. Schlueter and Contributors +Source: https://github.com/npm/node-semver.git +Link: https://github.com/npm/node-semver.git +----------- +set-blocking +License: ISC +License File: node_modules/set-blocking/LICENSE.txt +Copyright: Copyright (c) 2016, Contributors +Source: git+https://github.com/yargs/set-blocking.git +Link: https://github.com/yargs/set-blocking#readme +----------- +set-function-length +License: MIT +License File: node_modules/set-function-length/LICENSE +Copyright: Copyright (c) Jordan Harband and contributors +Source: git+https://github.com/ljharb/set-function-length.git +Link: https://github.com/ljharb/set-function-length#readme +----------- +set-function-name +License: MIT +License File: node_modules/set-function-name/LICENSE +Copyright: Copyright (c) Jordan Harband and contributors +Source: git+https://github.com/ljharb/set-function-name.git +Link: https://github.com/ljharb/set-function-name#readme +----------- +set-proto +License: MIT +License File: node_modules/set-proto/LICENSE +Copyright: Copyright (c) 2024 Jordan Harband +Source: git+https://github.com/ljharb/set-proto.git +Link: https://github.com/ljharb/set-proto#readme +----------- +shebang-command +License: MIT +License File: node_modules/shebang-command/license +Copyright: Copyright (c) Kevin Mårtensson <***************@*****.***> (github.com/kevva) +Source: kevva/shebang-command +Link: kevva/shebang-command +----------- +shebang-regex +License: MIT +License File: node_modules/shebang-regex/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (sindresorhus.com) +Source: sindresorhus/shebang-regex +Link: sindresorhus/shebang-regex +----------- +shell-quote +License: MIT +License File: node_modules/shell-quote/LICENSE +Copyright: Copyright (c) 2013 James Halliday (****@********.***) +Source: http://github.com/ljharb/shell-quote.git +Link: https://github.com/ljharb/shell-quote +----------- +side-channel-list +License: MIT +License File: node_modules/side-channel-list/LICENSE +Copyright: Copyright (c) 2024 Jordan Harband +Source: git+https://github.com/ljharb/side-channel-list.git +Link: https://github.com/ljharb/side-channel-list#readme +----------- +side-channel-map +License: MIT +License File: node_modules/side-channel-map/LICENSE +Copyright: Copyright (c) 2024 Jordan Harband +Source: git+https://github.com/ljharb/side-channel-map.git +Link: https://github.com/ljharb/side-channel-map#readme +----------- +side-channel-weakmap +License: MIT +License File: node_modules/side-channel-weakmap/LICENSE +Copyright: Copyright (c) 2019 Jordan Harband +Source: git+https://github.com/ljharb/side-channel-weakmap.git +Link: https://github.com/ljharb/side-channel-weakmap#readme +----------- +side-channel +License: MIT +License File: node_modules/side-channel/LICENSE +Copyright: Copyright (c) 2019 Jordan Harband +Source: git+https://github.com/ljharb/side-channel.git +Link: https://github.com/ljharb/side-channel#readme +----------- +signal-exit +License: ISC +License File: node_modules/signal-exit/LICENSE.txt +Copyright: Copyright (c) 2015, Contributors +Source: https://github.com/tapjs/signal-exit.git +Link: https://github.com/tapjs/signal-exit +----------- +sisteransi +License: MIT +License File: node_modules/sisteransi/license +Copyright: Copyright (c) 2018 Terkel Gjervig Nielsen +Source: https://github.com/terkelg/sisteransi +Link: https://github.com/terkelg/sisteransi +----------- +slash +License: MIT +License File: node_modules/slash/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (sindresorhus.com) +Source: sindresorhus/slash +Link: sindresorhus/slash +----------- +snabbdom +License: MIT +License File: node_modules/snabbdom/LICENSE +Copyright: Copyright (c) 2015 Simon Friis Vindum +Source: git+https://github.com/snabbdom/snabbdom.git +Link: https://github.com/snabbdom/snabbdom#readme +----------- +sortablejs +License: MIT +License File: node_modules/sortablejs/LICENSE +Source: git://github.com/SortableJS/Sortable.git +Link: git://github.com/SortableJS/Sortable.git +----------- +source-map-js +License: BSD-3-Clause +License File: node_modules/source-map-js/LICENSE +Copyright: Copyright (c) 2009-2011, Mozilla Foundation and contributors +All rights reserved. +Source: 7rulnik/source-map-js +Link: https://github.com/7rulnik/source-map-js +----------- +source-map-support +License: MIT +License File: node_modules/source-map-support/LICENSE.md +Copyright: Copyright (c) 2014 Evan Wallace +Source: https://github.com/evanw/node-source-map-support +Link: https://github.com/evanw/node-source-map-support +----------- +source-map +License: BSD-3-Clause +License File: node_modules/source-map/LICENSE +Copyright: Copyright (c) 2009-2011, Mozilla Foundation and contributors +All rights reserved. +Source: http://github.com/mozilla/source-map.git +Link: https://github.com/mozilla/source-map +----------- +spdx-correct +License: Apache-2.0 +License File: node_modules/spdx-correct/LICENSE +Source: jslicense/spdx-correct.js +Link: jslicense/spdx-correct.js +----------- +spdx-exceptions +License: CC-BY-3.0 +Source: kemitchell/spdx-exceptions.json +Link: kemitchell/spdx-exceptions.json +----------- +spdx-expression-parse +License: MIT +License File: node_modules/spdx-expression-parse/LICENSE +Copyright: Copyright (c) 2015 Kyle E. Mitchell & other authors listed in AUTHORS +Source: jslicense/spdx-expression-parse.js +Link: jslicense/spdx-expression-parse.js +----------- +spdx-license-ids +License: CC0-1.0 +Source: jslicense/spdx-license-ids +Link: jslicense/spdx-license-ids +----------- +sprintf-js +License: BSD-3-Clause +License File: node_modules/sprintf-js/LICENSE +Copyright: Copyright (c) 2007-2014, Alexandru Marasteanu +All rights reserved. +Source: https://github.com/alexei/sprintf.js.git +Link: https://github.com/alexei/sprintf.js.git +----------- +stack-utils +License: MIT +License File: node_modules/stack-utils/LICENSE.md +Copyright: Copyright (c) 2016-2022 Isaac Z. Schlueter <*@***.**>, James Talmage <*****@*******.**> (github.com/jamestalmage), and Contributors +Source: tapjs/stack-utils +Link: tapjs/stack-utils +----------- +stop-iteration-iterator +License: MIT +License File: node_modules/stop-iteration-iterator/LICENSE +Copyright: Copyright (c) 2023 Jordan Harband +Source: git+https://github.com/ljharb/stop-iteration-iterator.git +Link: https://github.com/ljharb/stop-iteration-iterator#readme +----------- +string-length +License: MIT +License File: node_modules/string-length/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (https://sindresorhus.com) +Source: sindresorhus/string-length +Link: sindresorhus/string-length +----------- +string-width +License: MIT +License File: node_modules/string-width/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (sindresorhus.com) +Source: sindresorhus/string-width +Link: sindresorhus/string-width +----------- +string.prototype.padend +License: MIT +License File: node_modules/string.prototype.padend/LICENSE +Copyright: Copyright (c) 2015 EcmaScript Shims +Source: git://github.com/es-shims/String.prototype.padEnd.git +Link: git://github.com/es-shims/String.prototype.padEnd.git +----------- +string.prototype.trim +License: MIT +License File: node_modules/string.prototype.trim/LICENSE +Copyright: Copyright (c) 2015 Jordan Harband +Source: git://github.com/es-shims/String.prototype.trim.git +Link: git://github.com/es-shims/String.prototype.trim.git +----------- +string.prototype.trimend +License: MIT +License File: node_modules/string.prototype.trimend/LICENSE +Copyright: Copyright (c) 2017 Khaled Al-Ansari +Source: git://github.com/es-shims/String.prototype.trimEnd.git +Link: git://github.com/es-shims/String.prototype.trimEnd.git +----------- +string.prototype.trimstart +License: MIT +License File: node_modules/string.prototype.trimstart/LICENSE +Copyright: Copyright (c) 2017 Khaled Al-Ansari +Source: git://github.com/es-shims/String.prototype.trimStart.git +Link: git://github.com/es-shims/String.prototype.trimStart.git +----------- +strip-ansi +License: MIT +License File: node_modules/strip-ansi/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (sindresorhus.com) +Source: chalk/strip-ansi +Link: chalk/strip-ansi +----------- +strip-bom +License: MIT +License File: node_modules/strip-bom/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (sindresorhus.com) +Source: sindresorhus/strip-bom +Link: sindresorhus/strip-bom +----------- +strip-final-newline +License: MIT +License File: node_modules/strip-final-newline/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (sindresorhus.com) +Source: sindresorhus/strip-final-newline +Link: sindresorhus/strip-final-newline +----------- +strip-json-comments +License: MIT +License File: node_modules/strip-json-comments/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (https://sindresorhus.com) +Source: sindresorhus/strip-json-comments +Link: sindresorhus/strip-json-comments +----------- +style-mod +License: MIT +License File: node_modules/style-mod/LICENSE +Copyright: Copyright (C) 2018 by Marijn Haverbeke <******@*********.******> and others +Source: git+https://github.com/marijnh/style-mod.git +Link: git+https://github.com/marijnh/style-mod.git +----------- +supports-color +License: MIT +License File: node_modules/supports-color/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (sindresorhus.com) +Source: chalk/supports-color +Link: chalk/supports-color +----------- +supports-preserve-symlinks-flag +License: MIT +License File: node_modules/supports-preserve-symlinks-flag/LICENSE +Copyright: Copyright (c) 2022 Inspect JS +Source: git+https://github.com/inspect-js/node-supports-preserve-symlinks-flag.git +Link: https://github.com/inspect-js/node-supports-preserve-symlinks-flag#readme +----------- +symbol-tree +License: MIT +License File: node_modules/symbol-tree/LICENSE +Copyright: Copyright (c) 2015 Joris van der Wel +Source: https://github.com/jsdom/js-symbol-tree.git +Link: https://github.com/jsdom/js-symbol-tree#symbol-tree +----------- +test-exclude +License: ISC +License File: node_modules/test-exclude/LICENSE.txt +Copyright: Copyright (c) 2016, Contributors +Source: git+https://github.com/istanbuljs/test-exclude.git +Link: https://istanbul.js.org/ +----------- +tmpl +License: BSD-3-Clause +License File: node_modules/tmpl/license +Copyright: Copyright (c) 2014, Naitik Shah. All rights reserved. +Source: https://github.com/daaku/nodejs-tmpl +Link: https://github.com/daaku/nodejs-tmpl +----------- +to-regex-range +License: MIT +License File: node_modules/to-regex-range/LICENSE +Copyright: Copyright (c) 2015-present, Jon Schlinkert. +Source: micromatch/to-regex-range +Link: https://github.com/micromatch/to-regex-range +----------- +tough-cookie +License: BSD-3-Clause +License File: node_modules/tough-cookie/LICENSE +Copyright: Copyright (c) 2015, Salesforce.com, Inc. +Source: git://github.com/salesforce/tough-cookie.git +Link: https://github.com/salesforce/tough-cookie +----------- +tr46 +License: MIT +License File: node_modules/tr46/LICENSE.md +Copyright: Copyright (c) Sebastian Mayr +Source: https://github.com/jsdom/tr46 +Link: https://github.com/jsdom/tr46 +----------- +ts-jest +License: MIT +License File: node_modules/ts-jest/LICENSE.md +Copyright: Copyright (c) 2016-2025 +Source: git+https://github.com/kulshekhar/ts-jest.git +Link: https://kulshekhar.github.io/ts-jest +----------- +ts-node +License: MIT +License File: node_modules/ts-node/LICENSE +Copyright: Copyright (c) 2014 Blake Embrey (*****@***********.***) +Source: git://github.com/TypeStrong/ts-node.git +Link: https://typestrong.org/ts-node +----------- +tsconfig-paths +License: MIT +License File: node_modules/tsconfig-paths/LICENSE +Copyright: Copyright (c) 2016 Jonas Kello +Source: https://github.com/dividab/tsconfig-paths +Link: https://github.com/dividab/tsconfig-paths +----------- +type-check +License: MIT +License File: node_modules/type-check/LICENSE +Copyright: Copyright (c) George Zahariev +Source: git://github.com/gkz/type-check.git +Link: https://github.com/gkz/type-check +----------- +type-detect +License: MIT +License File: node_modules/type-detect/LICENSE +Copyright: Copyright (c) 2013 Jake Luer <****@***************.***> (http://alogicalparadox.com) +Source: git+ssh://git@github.com/chaijs/type-detect.git +Link: git+ssh://git@github.com/chaijs/type-detect.git +----------- +type-fest +License: (MIT OR CC0-1.0) +License File: node_modules/type-fest/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (https:/sindresorhus.com) +Source: sindresorhus/type-fest +Link: sindresorhus/type-fest +----------- +typed-array-buffer +License: MIT +License File: node_modules/typed-array-buffer/LICENSE +Copyright: Copyright (c) 2023 Jordan Harband +Source: git+https://github.com/inspect-js/typed-array-buffer.git +Link: https://github.com/inspect-js/typed-array-buffer#readme +----------- +typed-array-byte-length +License: MIT +License File: node_modules/typed-array-byte-length/LICENSE +Copyright: Copyright (c) 2020 Inspect JS +Source: git+https://github.com/inspect-js/typed-array-byte-length.git +Link: https://github.com/inspect-js/typed-array-byte-length#readme +----------- +typed-array-byte-offset +License: MIT +License File: node_modules/typed-array-byte-offset/LICENSE +Copyright: Copyright (c) 2020 Inspect JS +Source: git+https://github.com/inspect-js/typed-array-byte-offset.git +Link: https://github.com/inspect-js/typed-array-byte-offset#readme +----------- +typed-array-length +License: MIT +License File: node_modules/typed-array-length/LICENSE +Copyright: Copyright (c) 2020 Inspect JS +Source: git+https://github.com/inspect-js/typed-array-length.git +Link: https://github.com/inspect-js/typed-array-length#readme +----------- +typescript +License: Apache-2.0 +License File: node_modules/typescript/LICENSE.txt +Source: https://github.com/microsoft/TypeScript.git +Link: https://www.typescriptlang.org/ +----------- +uc.micro +License: MIT +License File: node_modules/uc.micro/LICENSE.txt +Source: markdown-it/uc.micro +Link: markdown-it/uc.micro +----------- +unbox-primitive +License: MIT +License File: node_modules/unbox-primitive/LICENSE +Copyright: Copyright (c) 2019 Jordan Harband +Source: git+https://github.com/ljharb/unbox-primitive.git +Link: https://github.com/ljharb/unbox-primitive#readme +----------- +undici-types +License: MIT +License File: node_modules/undici-types/LICENSE +Copyright: Copyright (c) Matteo Collina and Undici contributors +Source: git+https://github.com/nodejs/undici.git +Link: https://undici.nodejs.org +----------- +universalify +License: MIT +License File: node_modules/universalify/LICENSE +Copyright: Copyright (c) 2017, Ryan Zimmerman <*******@*******.***> +Source: git+https://github.com/RyanZim/universalify.git +Link: https://github.com/RyanZim/universalify#readme +----------- +update-browserslist-db +License: MIT +License File: node_modules/update-browserslist-db/LICENSE +Copyright: Copyright 2022 Andrey Sitnik <******@******.**> and other contributors +Source: browserslist/update-db +Link: browserslist/update-db +----------- +uri-js +License: BSD-2-Clause +License File: node_modules/uri-js/LICENSE +Copyright: Copyright 2011 Gary Court. All rights reserved. +Source: http://github.com/garycourt/uri-js +Link: https://github.com/garycourt/uri-js +----------- +url-parse +License: MIT +License File: node_modules/url-parse/LICENSE +Copyright: Copyright (c) 2015 Unshift.io, Arnout Kazemier, the Contributors. +Source: https://github.com/unshiftio/url-parse.git +Link: https://github.com/unshiftio/url-parse.git +----------- +v8-compile-cache-lib +License: MIT +License File: node_modules/v8-compile-cache-lib/LICENSE +Copyright: Copyright (c) 2019 Andres Suarez +Source: https://github.com/cspotcode/v8-compile-cache-lib.git +Link: https://github.com/cspotcode/v8-compile-cache-lib.git +----------- +v8-to-istanbul +License: ISC +License File: node_modules/v8-to-istanbul/LICENSE.txt +Copyright: Copyright (c) 2017, Contributors +Source: istanbuljs/v8-to-istanbul +Link: istanbuljs/v8-to-istanbul +----------- +validate-npm-package-license +License: Apache-2.0 +License File: node_modules/validate-npm-package-license/LICENSE +Source: kemitchell/validate-npm-package-license.js +Link: kemitchell/validate-npm-package-license.js +----------- +w3c-keyname +License: MIT +License File: node_modules/w3c-keyname/LICENSE +Copyright: Copyright (C) 2016 by Marijn Haverbeke <******@*********.******> and others +Source: git+https://github.com/marijnh/w3c-keyname.git +Link: https://github.com/marijnh/w3c-keyname#readme +----------- +w3c-xmlserializer +License: MIT +License File: node_modules/w3c-xmlserializer/LICENSE.md +Source: jsdom/w3c-xmlserializer +Link: jsdom/w3c-xmlserializer +----------- +walker +License: Apache-2.0 +License File: node_modules/walker/LICENSE +Copyright: Copyright 2013 Naitik Shah +Source: https://github.com/daaku/nodejs-walker +Link: https://github.com/daaku/nodejs-walker +----------- +webidl-conversions +License: BSD-2-Clause +License File: node_modules/webidl-conversions/LICENSE.md +Copyright: Copyright (c) 2014, Domenic Denicola +All rights reserved. +Source: jsdom/webidl-conversions +Link: jsdom/webidl-conversions +----------- +whatwg-encoding +License: MIT +License File: node_modules/whatwg-encoding/LICENSE.txt +Source: jsdom/whatwg-encoding +Link: jsdom/whatwg-encoding +----------- +whatwg-mimetype +License: MIT +License File: node_modules/whatwg-mimetype/LICENSE.txt +Source: jsdom/whatwg-mimetype +Link: jsdom/whatwg-mimetype +----------- +whatwg-url +License: MIT +License File: node_modules/whatwg-url/LICENSE.txt +Copyright: Copyright (c) Sebastian Mayr +Source: jsdom/whatwg-url +Link: jsdom/whatwg-url +----------- +which-boxed-primitive +License: MIT +License File: node_modules/which-boxed-primitive/LICENSE +Copyright: Copyright (c) 2019 Jordan Harband +Source: git+https://github.com/inspect-js/which-boxed-primitive.git +Link: https://github.com/inspect-js/which-boxed-primitive#readme +----------- +which-builtin-type +License: MIT +License File: node_modules/which-builtin-type/LICENSE +Copyright: Copyright (c) 2020 ECMAScript Shims +Source: git+https://github.com/inspect-js/which-builtin-type.git +Link: https://github.com/inspect-js/which-builtin-type#readme +----------- +which-collection +License: MIT +License File: node_modules/which-collection/LICENSE +Copyright: Copyright (c) 2019 Inspect JS +Source: git+https://github.com/inspect-js/which-collection.git +Link: https://github.com/inspect-js/which-collection#readme +----------- +which-module +License: ISC +License File: node_modules/which-module/LICENSE +Copyright: Copyright (c) 2016, Contributors +Source: git+https://github.com/nexdrew/which-module.git +Link: https://github.com/nexdrew/which-module#readme +----------- +which-typed-array +License: MIT +License File: node_modules/which-typed-array/LICENSE +Copyright: Copyright (c) 2015 Jordan Harband +Source: git://github.com/inspect-js/which-typed-array.git +Link: git://github.com/inspect-js/which-typed-array.git +----------- +which +License: ISC +License File: node_modules/which/LICENSE +Copyright: Copyright (c) Isaac Z. Schlueter and Contributors +Source: git://github.com/isaacs/node-which.git +Link: git://github.com/isaacs/node-which.git +----------- +word-wrap +License: MIT +License File: node_modules/word-wrap/LICENSE +Copyright: Copyright (c) 2014-2016, Jon Schlinkert +Source: jonschlinkert/word-wrap +Link: https://github.com/jonschlinkert/word-wrap +----------- +wrap-ansi +License: MIT +License File: node_modules/wrap-ansi/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (sindresorhus.com) +Source: chalk/wrap-ansi +Link: chalk/wrap-ansi +----------- +wrappy +License: ISC +License File: node_modules/wrappy/LICENSE +Copyright: Copyright (c) Isaac Z. Schlueter and Contributors +Source: https://github.com/npm/wrappy +Link: https://github.com/npm/wrappy +----------- +write-file-atomic +License: ISC +License File: node_modules/write-file-atomic/LICENSE.md +Copyright: Copyright (c) 2015, Rebecca Turner +Source: https://github.com/npm/write-file-atomic.git +Link: https://github.com/npm/write-file-atomic +----------- +ws +License: MIT +License File: node_modules/ws/LICENSE +Copyright: Copyright (c) 2011 Einar Otto Stangvik <*******@*****.***> +Copyright (c) 2013 Arnout Kazemier and contributors +Copyright (c) 2016 Luigi Pinca and contributors +Source: git+https://github.com/websockets/ws.git +Link: https://github.com/websockets/ws +----------- +xml-name-validator +License: Apache-2.0 +License File: node_modules/xml-name-validator/LICENSE.txt +Source: jsdom/xml-name-validator +Link: jsdom/xml-name-validator +----------- +xmlchars +License: MIT +License File: node_modules/xmlchars/LICENSE +Source: https://github.com/lddubeau/xmlchars.git +Link: https://github.com/lddubeau/xmlchars.git +----------- +y18n +License: ISC +License File: node_modules/y18n/LICENSE +Copyright: Copyright (c) 2015, Contributors +Source: git@github.com:yargs/y18n.git +Link: https://github.com/yargs/y18n +----------- +yallist +License: ISC +License File: node_modules/yallist/LICENSE +Copyright: Copyright (c) Isaac Z. Schlueter and Contributors +Source: git+https://github.com/isaacs/yallist.git +Link: git+https://github.com/isaacs/yallist.git +----------- +yargs-parser +License: ISC +License File: node_modules/yargs-parser/LICENSE.txt +Copyright: Copyright (c) 2016, Contributors +Source: https://github.com/yargs/yargs-parser.git +Link: https://github.com/yargs/yargs-parser.git +----------- +yargs +License: MIT +License File: node_modules/yargs/LICENSE +Copyright: Copyright 2010 James Halliday (****@********.***) +Modified work Copyright 2014 Contributors (***@*****.***) +Source: https://github.com/yargs/yargs.git +Link: https://yargs.js.org/ +----------- +yn +License: MIT +License File: node_modules/yn/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (sindresorhus.com) +Source: sindresorhus/yn +Link: sindresorhus/yn +----------- +yocto-queue +License: MIT +License File: node_modules/yocto-queue/license +Copyright: Copyright (c) Sindre Sorhus <************@*****.***> (https://sindresorhus.com) +Source: sindresorhus/yocto-queue +Link: sindresorhus/yocto-queue +----------- +@ampproject/remapping +License: Apache-2.0 +License File: node_modules/@ampproject/remapping/LICENSE +Source: git+https://github.com/ampproject/remapping.git +Link: git+https://github.com/ampproject/remapping.git +----------- +@babel/code-frame +License: MIT +License File: node_modules/@babel/code-frame/LICENSE +Copyright: Copyright (c) 2014-present Sebastian McKenzie and other contributors +Source: https://github.com/babel/babel.git +Link: https://babel.dev/docs/en/next/babel-code-frame +----------- +@babel/compat-data +License: MIT +License File: node_modules/@babel/compat-data/LICENSE +Copyright: Copyright (c) 2014-present Sebastian McKenzie and other contributors +Source: https://github.com/babel/babel.git +Link: https://github.com/babel/babel.git +----------- +@babel/core +License: MIT +License File: node_modules/@babel/core/LICENSE +Copyright: Copyright (c) 2014-present Sebastian McKenzie and other contributors +Source: https://github.com/babel/babel.git +Link: https://babel.dev/docs/en/next/babel-core +----------- +@babel/generator +License: MIT +License File: node_modules/@babel/generator/LICENSE +Copyright: Copyright (c) 2014-present Sebastian McKenzie and other contributors +Source: https://github.com/babel/babel.git +Link: https://babel.dev/docs/en/next/babel-generator +----------- +@babel/helper-compilation-targets +License: MIT +License File: node_modules/@babel/helper-compilation-targets/LICENSE +Copyright: Copyright (c) 2014-present Sebastian McKenzie and other contributors +Source: https://github.com/babel/babel.git +Link: https://github.com/babel/babel.git +----------- +@babel/helper-globals +License: MIT +License File: node_modules/@babel/helper-globals/LICENSE +Copyright: Copyright (c) 2014-present Sebastian McKenzie and other contributors +Source: https://github.com/babel/babel.git +Link: https://github.com/babel/babel.git +----------- +@babel/helper-module-imports +License: MIT +License File: node_modules/@babel/helper-module-imports/LICENSE +Copyright: Copyright (c) 2014-present Sebastian McKenzie and other contributors +Source: https://github.com/babel/babel.git +Link: https://babel.dev/docs/en/next/babel-helper-module-imports +----------- +@babel/helper-module-transforms +License: MIT +License File: node_modules/@babel/helper-module-transforms/LICENSE +Copyright: Copyright (c) 2014-present Sebastian McKenzie and other contributors +Source: https://github.com/babel/babel.git +Link: https://babel.dev/docs/en/next/babel-helper-module-transforms +----------- +@babel/helper-plugin-utils +License: MIT +License File: node_modules/@babel/helper-plugin-utils/LICENSE +Copyright: Copyright (c) 2014-present Sebastian McKenzie and other contributors +Source: https://github.com/babel/babel.git +Link: https://babel.dev/docs/en/next/babel-helper-plugin-utils +----------- +@babel/helper-string-parser +License: MIT +License File: node_modules/@babel/helper-string-parser/LICENSE +Copyright: Copyright (c) 2014-present Sebastian McKenzie and other contributors +Source: https://github.com/babel/babel.git +Link: https://babel.dev/docs/en/next/babel-helper-string-parser +----------- +@babel/helper-validator-identifier +License: MIT +License File: node_modules/@babel/helper-validator-identifier/LICENSE +Copyright: Copyright (c) 2014-present Sebastian McKenzie and other contributors +Source: https://github.com/babel/babel.git +Link: https://github.com/babel/babel.git +----------- +@babel/helper-validator-option +License: MIT +License File: node_modules/@babel/helper-validator-option/LICENSE +Copyright: Copyright (c) 2014-present Sebastian McKenzie and other contributors +Source: https://github.com/babel/babel.git +Link: https://github.com/babel/babel.git +----------- +@babel/helpers +License: MIT +License File: node_modules/@babel/helpers/LICENSE +Copyright: Copyright (c) 2014-present Sebastian McKenzie and other contributors +Copyright (c) 2014-present, Facebook, Inc. (ONLY ./src/helpers/regenerator* files) +Source: https://github.com/babel/babel.git +Link: https://babel.dev/docs/en/next/babel-helpers +----------- +@babel/parser +License: MIT +License File: node_modules/@babel/parser/LICENSE +Copyright: Copyright (C) 2012-2014 by various contributors (see AUTHORS) +Source: https://github.com/babel/babel.git +Link: https://babel.dev/docs/en/next/babel-parser +----------- +@babel/plugin-syntax-async-generators +License: MIT +License File: node_modules/@babel/plugin-syntax-async-generators/LICENSE +Copyright: Copyright (c) 2014-present Sebastian McKenzie and other contributors +Source: https://github.com/babel/babel/tree/master/packages/babel-plugin-syntax-async-generators +Link: https://github.com/babel/babel/tree/master/packages/babel-plugin-syntax-async-generators +----------- +@babel/plugin-syntax-bigint +License: MIT +License File: node_modules/@babel/plugin-syntax-bigint/LICENSE +Copyright: Copyright (c) 2014-present Sebastian McKenzie and other contributors +Source: https://github.com/babel/babel/tree/master/packages/babel-plugin-syntax-bigint +Link: https://github.com/babel/babel/tree/master/packages/babel-plugin-syntax-bigint +----------- +@babel/plugin-syntax-class-properties +License: MIT +License File: node_modules/@babel/plugin-syntax-class-properties/LICENSE +Copyright: Copyright (c) 2014-present Sebastian McKenzie and other contributors +Source: https://github.com/babel/babel.git +Link: https://babel.dev/docs/en/next/babel-plugin-syntax-class-properties +----------- +@babel/plugin-syntax-class-static-block +License: MIT +License File: node_modules/@babel/plugin-syntax-class-static-block/LICENSE +Copyright: Copyright (c) 2014-present Sebastian McKenzie and other contributors +Source: https://github.com/babel/babel.git +Link: https://babel.dev/docs/en/next/babel-plugin-syntax-class-static-block +----------- +@babel/plugin-syntax-import-attributes +License: MIT +License File: node_modules/@babel/plugin-syntax-import-attributes/LICENSE +Copyright: Copyright (c) 2014-present Sebastian McKenzie and other contributors +Source: https://github.com/babel/babel.git +Link: https://github.com/babel/babel.git +----------- +@babel/plugin-syntax-import-meta +License: MIT +License File: node_modules/@babel/plugin-syntax-import-meta/LICENSE +Copyright: Copyright (c) 2014-present Sebastian McKenzie and other contributors +Source: https://github.com/babel/babel.git +Link: https://github.com/babel/babel.git +----------- +@babel/plugin-syntax-json-strings +License: MIT +License File: node_modules/@babel/plugin-syntax-json-strings/LICENSE +Copyright: Copyright (c) 2014-present Sebastian McKenzie and other contributors +Source: https://github.com/babel/babel/tree/master/packages/babel-plugin-syntax-json-strings +Link: https://github.com/babel/babel/tree/master/packages/babel-plugin-syntax-json-strings +----------- +@babel/plugin-syntax-jsx +License: MIT +License File: node_modules/@babel/plugin-syntax-jsx/LICENSE +Copyright: Copyright (c) 2014-present Sebastian McKenzie and other contributors +Source: https://github.com/babel/babel.git +Link: https://babel.dev/docs/en/next/babel-plugin-syntax-jsx +----------- +@babel/plugin-syntax-logical-assignment-operators +License: MIT +License File: node_modules/@babel/plugin-syntax-logical-assignment-operators/LICENSE +Copyright: Copyright (c) 2014-present Sebastian McKenzie and other contributors +Source: https://github.com/babel/babel.git +Link: https://github.com/babel/babel.git +----------- +@babel/plugin-syntax-nullish-coalescing-operator +License: MIT +License File: node_modules/@babel/plugin-syntax-nullish-coalescing-operator/LICENSE +Copyright: Copyright (c) 2014-present Sebastian McKenzie and other contributors +Source: https://github.com/babel/babel/tree/master/packages/babel-plugin-syntax-nullish-coalescing-operator +Link: https://github.com/babel/babel/tree/master/packages/babel-plugin-syntax-nullish-coalescing-operator +----------- +@babel/plugin-syntax-numeric-separator +License: MIT +License File: node_modules/@babel/plugin-syntax-numeric-separator/LICENSE +Copyright: Copyright (c) 2014-present Sebastian McKenzie and other contributors +Source: https://github.com/babel/babel.git +Link: https://github.com/babel/babel.git +----------- +@babel/plugin-syntax-object-rest-spread +License: MIT +License File: node_modules/@babel/plugin-syntax-object-rest-spread/LICENSE +Copyright: Copyright (c) 2014-present Sebastian McKenzie and other contributors +Source: https://github.com/babel/babel/tree/master/packages/babel-plugin-syntax-object-rest-spread +Link: https://github.com/babel/babel/tree/master/packages/babel-plugin-syntax-object-rest-spread +----------- +@babel/plugin-syntax-optional-catch-binding +License: MIT +License File: node_modules/@babel/plugin-syntax-optional-catch-binding/LICENSE +Copyright: Copyright (c) 2014-present Sebastian McKenzie and other contributors +Source: https://github.com/babel/babel/tree/master/packages/babel-plugin-syntax-optional-catch-binding +Link: https://github.com/babel/babel/tree/master/packages/babel-plugin-syntax-optional-catch-binding +----------- +@babel/plugin-syntax-optional-chaining +License: MIT +License File: node_modules/@babel/plugin-syntax-optional-chaining/LICENSE +Copyright: Copyright (c) 2014-present Sebastian McKenzie and other contributors +Source: https://github.com/babel/babel/tree/master/packages/babel-plugin-syntax-optional-chaining +Link: https://github.com/babel/babel/tree/master/packages/babel-plugin-syntax-optional-chaining +----------- +@babel/plugin-syntax-private-property-in-object +License: MIT +License File: node_modules/@babel/plugin-syntax-private-property-in-object/LICENSE +Copyright: Copyright (c) 2014-present Sebastian McKenzie and other contributors +Source: https://github.com/babel/babel.git +Link: https://babel.dev/docs/en/next/babel-plugin-syntax-private-property-in-object +----------- +@babel/plugin-syntax-top-level-await +License: MIT +License File: node_modules/@babel/plugin-syntax-top-level-await/LICENSE +Copyright: Copyright (c) 2014-present Sebastian McKenzie and other contributors +Source: https://github.com/babel/babel.git +Link: https://babel.dev/docs/en/next/babel-plugin-syntax-top-level-await +----------- +@babel/plugin-syntax-typescript +License: MIT +License File: node_modules/@babel/plugin-syntax-typescript/LICENSE +Copyright: Copyright (c) 2014-present Sebastian McKenzie and other contributors +Source: https://github.com/babel/babel.git +Link: https://babel.dev/docs/en/next/babel-plugin-syntax-typescript +----------- +@babel/template +License: MIT +License File: node_modules/@babel/template/LICENSE +Copyright: Copyright (c) 2014-present Sebastian McKenzie and other contributors +Source: https://github.com/babel/babel.git +Link: https://babel.dev/docs/en/next/babel-template +----------- +@babel/traverse +License: MIT +License File: node_modules/@babel/traverse/LICENSE +Copyright: Copyright (c) 2014-present Sebastian McKenzie and other contributors +Source: https://github.com/babel/babel.git +Link: https://babel.dev/docs/en/next/babel-traverse +----------- +@babel/types +License: MIT +License File: node_modules/@babel/types/LICENSE +Copyright: Copyright (c) 2014-present Sebastian McKenzie and other contributors +Source: https://github.com/babel/babel.git +Link: https://babel.dev/docs/en/next/babel-types +----------- +@bcoe/v8-coverage +License: MIT +License File: node_modules/@bcoe/v8-coverage/LICENSE.md +Source: git://github.com/demurgos/v8-coverage.git +Link: https://demurgos.github.io/v8-coverage +----------- +@codemirror/autocomplete +License: MIT +License File: node_modules/@codemirror/autocomplete/LICENSE +Copyright: Copyright (C) 2018-2021 by Marijn Haverbeke <******@*********.******> and others +Source: https://github.com/codemirror/autocomplete.git +Link: https://github.com/codemirror/autocomplete.git +----------- +@codemirror/commands +License: MIT +License File: node_modules/@codemirror/commands/LICENSE +Copyright: Copyright (C) 2018-2021 by Marijn Haverbeke <******@*********.******> and others +Source: https://github.com/codemirror/commands.git +Link: https://github.com/codemirror/commands.git +----------- +@codemirror/lang-css +License: MIT +License File: node_modules/@codemirror/lang-css/LICENSE +Copyright: Copyright (C) 2018-2021 by Marijn Haverbeke <******@*********.******> and others +Source: https://github.com/codemirror/lang-css.git +Link: https://github.com/codemirror/lang-css.git +----------- +@codemirror/lang-html +License: MIT +License File: node_modules/@codemirror/lang-html/LICENSE +Copyright: Copyright (C) 2018-2021 by Marijn Haverbeke <******@*********.******> and others +Source: https://github.com/codemirror/lang-html.git +Link: https://github.com/codemirror/lang-html.git +----------- +@codemirror/lang-javascript +License: MIT +License File: node_modules/@codemirror/lang-javascript/LICENSE +Copyright: Copyright (C) 2018-2021 by Marijn Haverbeke <******@*********.******> and others +Source: https://github.com/codemirror/lang-javascript.git +Link: https://github.com/codemirror/lang-javascript.git +----------- +@codemirror/lang-json +License: MIT +License File: node_modules/@codemirror/lang-json/LICENSE +Copyright: Copyright (C) 2018-2021 by Marijn Haverbeke <******@*********.******> and others +Source: https://github.com/codemirror/lang-json.git +Link: https://github.com/codemirror/lang-json.git +----------- +@codemirror/lang-markdown +License: MIT +License File: node_modules/@codemirror/lang-markdown/LICENSE +Copyright: Copyright (C) 2018-2021 by Marijn Haverbeke <******@*********.******> and others +Source: https://github.com/codemirror/lang-markdown.git +Link: https://github.com/codemirror/lang-markdown.git +----------- +@codemirror/lang-php +License: MIT +License File: node_modules/@codemirror/lang-php/LICENSE +Copyright: Copyright (C) 2018-2021 by Marijn Haverbeke <******@*********.******> and others +Source: https://github.com/codemirror/lang-php.git +Link: https://github.com/codemirror/lang-php.git +----------- +@codemirror/lang-xml +License: MIT +License File: node_modules/@codemirror/lang-xml/LICENSE +Copyright: Copyright (C) 2018-2021 by Marijn Haverbeke <******@*********.******> and others +Source: https://github.com/codemirror/lang-xml.git +Link: https://github.com/codemirror/lang-xml.git +----------- +@codemirror/language +License: MIT +License File: node_modules/@codemirror/language/LICENSE +Copyright: Copyright (C) 2018-2021 by Marijn Haverbeke <******@*********.******> and others +Source: https://github.com/codemirror/language.git +Link: https://github.com/codemirror/language.git +----------- +@codemirror/legacy-modes +License: MIT +License File: node_modules/@codemirror/legacy-modes/LICENSE +Copyright: Copyright (C) 2018-2021 by Marijn Haverbeke <******@*********.******> and others +Source: https://github.com/codemirror/legacy-modes.git +Link: https://github.com/codemirror/legacy-modes.git +----------- +@codemirror/lint +License: MIT +License File: node_modules/@codemirror/lint/LICENSE +Copyright: Copyright (C) 2018-2021 by Marijn Haverbeke <******@*********.******> and others +Source: https://github.com/codemirror/lint.git +Link: https://github.com/codemirror/lint.git +----------- +@codemirror/search +License: MIT +License File: node_modules/@codemirror/search/LICENSE +Copyright: Copyright (C) 2018-2021 by Marijn Haverbeke <******@*********.******> and others +Source: https://github.com/codemirror/search.git +Link: https://github.com/codemirror/search.git +----------- +@codemirror/state +License: MIT +License File: node_modules/@codemirror/state/LICENSE +Copyright: Copyright (C) 2018-2021 by Marijn Haverbeke <******@*********.******> and others +Source: https://github.com/codemirror/state.git +Link: https://github.com/codemirror/state.git +----------- +@codemirror/theme-one-dark +License: MIT +License File: node_modules/@codemirror/theme-one-dark/LICENSE +Copyright: Copyright (C) 2018-2021 by Marijn Haverbeke <******@*********.******> and others +Source: https://github.com/codemirror/theme-one-dark.git +Link: https://github.com/codemirror/theme-one-dark.git +----------- +@codemirror/view +License: MIT +License File: node_modules/@codemirror/view/LICENSE +Copyright: Copyright (C) 2018-2021 by Marijn Haverbeke <******@*********.******> and others +Source: https://github.com/codemirror/view.git +Link: https://github.com/codemirror/view.git +----------- +@cspotcode/source-map-support +License: MIT +License File: node_modules/@cspotcode/source-map-support/LICENSE.md +Copyright: Copyright (c) 2014 Evan Wallace +Source: https://github.com/cspotcode/node-source-map-support +Link: https://github.com/cspotcode/node-source-map-support +----------- +@esbuild/linux-x64 +License: MIT +Source: git+https://github.com/evanw/esbuild.git +Link: git+https://github.com/evanw/esbuild.git +----------- +@eslint-community/eslint-utils +License: MIT +License File: node_modules/@eslint-community/eslint-utils/LICENSE +Copyright: Copyright (c) 2018 Toru Nagashima +Source: https://github.com/eslint-community/eslint-utils +Link: https://github.com/eslint-community/eslint-utils#readme +----------- +@eslint-community/regexpp +License: MIT +License File: node_modules/@eslint-community/regexpp/LICENSE +Copyright: Copyright (c) 2018 Toru Nagashima +Source: https://github.com/eslint-community/regexpp +Link: https://github.com/eslint-community/regexpp#readme +----------- +@eslint/config-array +License: Apache-2.0 +License File: node_modules/@eslint/config-array/LICENSE +Source: git+https://github.com/eslint/rewrite.git +Link: https://github.com/eslint/rewrite/tree/main/packages/config-array#readme +----------- +@eslint/config-helpers +License: Apache-2.0 +License File: node_modules/@eslint/config-helpers/LICENSE +Source: git+https://github.com/eslint/rewrite.git +Link: https://github.com/eslint/rewrite/tree/main/packages/config-helpers#readme +----------- +@eslint/core +License: Apache-2.0 +License File: node_modules/@eslint/core/LICENSE +Source: git+https://github.com/eslint/rewrite.git +Link: https://github.com/eslint/rewrite/tree/main/packages/core#readme +----------- +@eslint/eslintrc +License: MIT +License File: node_modules/@eslint/eslintrc/LICENSE +Source: eslint/eslintrc +Link: https://github.com/eslint/eslintrc#readme +----------- +@eslint/js +License: MIT +License File: node_modules/@eslint/js/LICENSE +Source: https://github.com/eslint/eslint.git +Link: https://eslint.org +----------- +@eslint/object-schema +License: Apache-2.0 +License File: node_modules/@eslint/object-schema/LICENSE +Source: git+https://github.com/eslint/rewrite.git +Link: https://github.com/eslint/rewrite#readme +----------- +@eslint/plugin-kit +License: Apache-2.0 +License File: node_modules/@eslint/plugin-kit/LICENSE +Source: git+https://github.com/eslint/rewrite.git +Link: https://github.com/eslint/rewrite/tree/main/packages/plugin-kit#readme +----------- +@humanfs/core +License: Apache-2.0 +License File: node_modules/@humanfs/core/LICENSE +Source: git+https://github.com/humanwhocodes/humanfs.git +Link: https://github.com/humanwhocodes/humanfs#readme +----------- +@humanfs/node +License: Apache-2.0 +License File: node_modules/@humanfs/node/LICENSE +Source: git+https://github.com/humanwhocodes/humanfs.git +Link: https://github.com/humanwhocodes/humanfs#readme +----------- +@humanwhocodes/module-importer +License: Apache-2.0 +License File: node_modules/@humanwhocodes/module-importer/LICENSE +Source: git+https://github.com/humanwhocodes/module-importer.git +Link: git+https://github.com/humanwhocodes/module-importer.git +----------- +@humanwhocodes/retry +License: Apache-2.0 +License File: node_modules/@humanwhocodes/retry/LICENSE +Source: git+https://github.com/humanwhocodes/retry.git +Link: git+https://github.com/humanwhocodes/retry.git +----------- +@istanbuljs/load-nyc-config +License: ISC +License File: node_modules/@istanbuljs/load-nyc-config/LICENSE +Copyright: Copyright (c) 2019, Contributors +Source: git+https://github.com/istanbuljs/load-nyc-config.git +Link: https://github.com/istanbuljs/load-nyc-config#readme +----------- +@istanbuljs/schema +License: MIT +License File: node_modules/@istanbuljs/schema/LICENSE +Copyright: Copyright (c) 2019 CFWare, LLC +Source: git+https://github.com/istanbuljs/schema.git +Link: https://github.com/istanbuljs/schema#readme +----------- +@jest/console +License: MIT +License File: node_modules/@jest/console/LICENSE +Copyright: Copyright (c) Meta Platforms, Inc. and affiliates. +Source: https://github.com/jestjs/jest.git +Link: https://github.com/jestjs/jest.git +----------- +@jest/core +License: MIT +License File: node_modules/@jest/core/LICENSE +Copyright: Copyright (c) Meta Platforms, Inc. and affiliates. +Source: https://github.com/jestjs/jest.git +Link: https://jestjs.io/ +----------- +@jest/environment +License: MIT +License File: node_modules/@jest/environment/LICENSE +Copyright: Copyright (c) Meta Platforms, Inc. and affiliates. +Source: https://github.com/jestjs/jest.git +Link: https://github.com/jestjs/jest.git +----------- +@jest/expect-utils +License: MIT +License File: node_modules/@jest/expect-utils/LICENSE +Copyright: Copyright (c) Meta Platforms, Inc. and affiliates. +Source: https://github.com/jestjs/jest.git +Link: https://github.com/jestjs/jest.git +----------- +@jest/expect +License: MIT +License File: node_modules/@jest/expect/LICENSE +Copyright: Copyright (c) Meta Platforms, Inc. and affiliates. +Source: https://github.com/jestjs/jest.git +Link: https://github.com/jestjs/jest.git +----------- +@jest/fake-timers +License: MIT +License File: node_modules/@jest/fake-timers/LICENSE +Copyright: Copyright (c) Meta Platforms, Inc. and affiliates. +Source: https://github.com/jestjs/jest.git +Link: https://github.com/jestjs/jest.git +----------- +@jest/globals +License: MIT +License File: node_modules/@jest/globals/LICENSE +Copyright: Copyright (c) Meta Platforms, Inc. and affiliates. +Source: https://github.com/jestjs/jest.git +Link: https://github.com/jestjs/jest.git +----------- +@jest/reporters +License: MIT +License File: node_modules/@jest/reporters/LICENSE +Copyright: Copyright (c) Meta Platforms, Inc. and affiliates. +Source: https://github.com/jestjs/jest.git +Link: https://jestjs.io/ +----------- +@jest/schemas +License: MIT +License File: node_modules/@jest/schemas/LICENSE +Copyright: Copyright (c) Meta Platforms, Inc. and affiliates. +Source: https://github.com/jestjs/jest.git +Link: https://github.com/jestjs/jest.git +----------- +@jest/source-map +License: MIT +License File: node_modules/@jest/source-map/LICENSE +Copyright: Copyright (c) Meta Platforms, Inc. and affiliates. +Source: https://github.com/jestjs/jest.git +Link: https://github.com/jestjs/jest.git +----------- +@jest/test-result +License: MIT +License File: node_modules/@jest/test-result/LICENSE +Copyright: Copyright (c) Meta Platforms, Inc. and affiliates. +Source: https://github.com/jestjs/jest.git +Link: https://github.com/jestjs/jest.git +----------- +@jest/test-sequencer +License: MIT +License File: node_modules/@jest/test-sequencer/LICENSE +Copyright: Copyright (c) Meta Platforms, Inc. and affiliates. +Source: https://github.com/jestjs/jest.git +Link: https://github.com/jestjs/jest.git +----------- +@jest/transform +License: MIT +License File: node_modules/@jest/transform/LICENSE +Copyright: Copyright (c) Meta Platforms, Inc. and affiliates. +Source: https://github.com/jestjs/jest.git +Link: https://github.com/jestjs/jest.git +----------- +@jest/types +License: MIT +License File: node_modules/@jest/types/LICENSE +Copyright: Copyright (c) Meta Platforms, Inc. and affiliates. +Source: https://github.com/jestjs/jest.git +Link: https://github.com/jestjs/jest.git +----------- +@jridgewell/gen-mapping +License: MIT +License File: node_modules/@jridgewell/gen-mapping/LICENSE +Copyright: Copyright 2024 Justin Ridgewell <******@*********.****> +Source: git+https://github.com/jridgewell/sourcemaps.git +Link: https://github.com/jridgewell/sourcemaps/tree/main/packages/gen-mapping +----------- +@jridgewell/resolve-uri +License: MIT +License File: node_modules/@jridgewell/resolve-uri/LICENSE +Copyright: Copyright 2019 Justin Ridgewell <**********@******.***> +Source: https://github.com/jridgewell/resolve-uri +Link: https://github.com/jridgewell/resolve-uri +----------- +@jridgewell/sourcemap-codec +License: MIT +License File: node_modules/@jridgewell/sourcemap-codec/LICENSE +Copyright: Copyright 2024 Justin Ridgewell <******@*********.****> +Source: git+https://github.com/jridgewell/sourcemaps.git +Link: https://github.com/jridgewell/sourcemaps/tree/main/packages/sourcemap-codec +----------- +@jridgewell/trace-mapping +License: MIT +License File: node_modules/@jridgewell/trace-mapping/LICENSE +Copyright: Copyright 2024 Justin Ridgewell <******@*********.****> +Source: git+https://github.com/jridgewell/sourcemaps.git +Link: https://github.com/jridgewell/sourcemaps/tree/main/packages/trace-mapping +----------- +@lezer/common +License: MIT +License File: node_modules/@lezer/common/LICENSE +Copyright: Copyright (C) 2018 by Marijn Haverbeke <******@*********.******> and others +Source: https://github.com/lezer-parser/common.git +Link: https://github.com/lezer-parser/common.git +----------- +@lezer/css +License: MIT +License File: node_modules/@lezer/css/LICENSE +Copyright: Copyright (C) 2018 by Marijn Haverbeke <******@*********.******> and others +Source: https://github.com/lezer-parser/css.git +Link: https://github.com/lezer-parser/css.git +----------- +@lezer/generator +License: MIT +License File: node_modules/@lezer/generator/LICENSE +Copyright: Copyright (C) 2018 by Marijn Haverbeke <******@*********.******> and others +Source: https://github.com/lezer-parser/generator.git +Link: https://github.com/lezer-parser/generator.git +----------- +@lezer/highlight +License: MIT +License File: node_modules/@lezer/highlight/LICENSE +Copyright: Copyright (C) 2018 by Marijn Haverbeke <******@*********.******> and others +Source: https://github.com/lezer-parser/highlight.git +Link: https://github.com/lezer-parser/highlight.git +----------- +@lezer/html +License: MIT +License File: node_modules/@lezer/html/LICENSE +Copyright: Copyright (C) 2018 by Marijn Haverbeke <******@*********.******> and others +Source: https://github.com/lezer-parser/html.git +Link: https://github.com/lezer-parser/html.git +----------- +@lezer/javascript +License: MIT +License File: node_modules/@lezer/javascript/LICENSE +Copyright: Copyright (C) 2018 by Marijn Haverbeke <******@*********.******> and others +Source: https://github.com/lezer-parser/javascript.git +Link: https://github.com/lezer-parser/javascript.git +----------- +@lezer/json +License: MIT +License File: node_modules/@lezer/json/LICENSE +Copyright: Copyright (C) 2020 by Marijn Haverbeke <******@*********.******>, Arun Srinivasan <*******@*****.***>, and others +Source: https://github.com/lezer-parser/json.git +Link: https://github.com/lezer-parser/json.git +----------- +@lezer/lr +License: MIT +License File: node_modules/@lezer/lr/LICENSE +Copyright: Copyright (C) 2018 by Marijn Haverbeke <******@*********.******> and others +Source: https://github.com/lezer-parser/lr.git +Link: https://github.com/lezer-parser/lr.git +----------- +@lezer/markdown +License: MIT +License File: node_modules/@lezer/markdown/LICENSE +Copyright: Copyright (C) 2020 by Marijn Haverbeke <******@*********.******> and others +Source: https://github.com/lezer-parser/markdown.git +Link: https://github.com/lezer-parser/markdown.git +----------- +@lezer/php +License: MIT +License File: node_modules/@lezer/php/LICENSE +Copyright: Copyright (C) 2018 by Marijn Haverbeke <******@*********.******> and others +Source: https://github.com/lezer-parser/php.git +Link: https://github.com/lezer-parser/php.git +----------- +@lezer/xml +License: MIT +License File: node_modules/@lezer/xml/LICENSE +Copyright: Copyright (C) 2018 by Marijn Haverbeke <******@*********.******> and others +Source: https://github.com/lezer-parser/xml.git +Link: https://github.com/lezer-parser/xml.git +----------- +@marijn/find-cluster-break +License: MIT +License File: node_modules/@marijn/find-cluster-break/LICENSE +Copyright: Copyright (C) 2024 by Marijn Haverbeke <******@*********.******> +Source: git+https://github.com/marijnh/find-cluster-break.git +Link: https://github.com/marijnh/find-cluster-break#readme +----------- +@parcel/watcher-linux-x64-glibc +License: MIT +License File: node_modules/@parcel/watcher-linux-x64-glibc/LICENSE +Copyright: Copyright (c) 2017-present Devon Govett +Source: https://github.com/parcel-bundler/watcher.git +Link: https://github.com/parcel-bundler/watcher.git +----------- +@parcel/watcher-linux-x64-musl +License: MIT +License File: node_modules/@parcel/watcher-linux-x64-musl/LICENSE +Copyright: Copyright (c) 2017-present Devon Govett +Source: https://github.com/parcel-bundler/watcher.git +Link: https://github.com/parcel-bundler/watcher.git +----------- +@parcel/watcher +License: MIT +License File: node_modules/@parcel/watcher/LICENSE +Copyright: Copyright (c) 2017-present Devon Govett +Source: https://github.com/parcel-bundler/watcher.git +Link: https://github.com/parcel-bundler/watcher.git +----------- +@rtsao/scc +License: MIT +License File: node_modules/@rtsao/scc/LICENSE +Copyright: Copyright (c) 2019 Ryan Tsao +Source: rtsao/scc +Link: rtsao/scc +----------- +@sinclair/typebox +License: MIT +License File: node_modules/@sinclair/typebox/license +Source: https://github.com/sinclairzx81/typebox +Link: https://github.com/sinclairzx81/typebox +----------- +@sinonjs/commons +License: BSD-3-Clause +License File: node_modules/@sinonjs/commons/LICENSE +Copyright: Copyright (c) 2018, Sinon.JS +All rights reserved. +Source: git+https://github.com/sinonjs/commons.git +Link: https://github.com/sinonjs/commons#readme +----------- +@sinonjs/fake-timers +License: BSD-3-Clause +License File: node_modules/@sinonjs/fake-timers/LICENSE +Copyright: Copyright (c) 2010-2014, Christian Johansen, *********@*********.**. All rights reserved. +Source: https://github.com/sinonjs/fake-timers.git +Link: https://github.com/sinonjs/fake-timers +----------- +@ssddanbrown/codemirror-lang-smarty +License: MIT +License File: node_modules/@ssddanbrown/codemirror-lang-smarty/LICENSE +Copyright: Copyright (C) 2023 by Dan Brown, Marijn Haverbeke and others +----------- +@ssddanbrown/codemirror-lang-twig +License: MIT +License File: node_modules/@ssddanbrown/codemirror-lang-twig/LICENSE +Copyright: Copyright (C) 2023 by Dan Brown, Marijn Haverbeke and others +----------- +@tootallnate/once +License: MIT +License File: node_modules/@tootallnate/once/LICENSE +Copyright: Copyright (c) 2020 Nathan Rajlich +Source: git://github.com/TooTallNate/once.git +Link: git://github.com/TooTallNate/once.git +----------- +@tsconfig/node10 +License: MIT +License File: node_modules/@tsconfig/node10/LICENSE +Copyright: Copyright (c) Microsoft Corporation. +Source: https://github.com/tsconfig/bases.git +Link: https://github.com/tsconfig/bases.git +----------- +@tsconfig/node12 +License: MIT +License File: node_modules/@tsconfig/node12/LICENSE +Copyright: Copyright (c) Microsoft Corporation. +Source: https://github.com/tsconfig/bases.git +Link: https://github.com/tsconfig/bases.git +----------- +@tsconfig/node14 +License: MIT +License File: node_modules/@tsconfig/node14/LICENSE +Copyright: Copyright (c) Microsoft Corporation. +Source: https://github.com/tsconfig/bases.git +Link: https://github.com/tsconfig/bases.git +----------- +@tsconfig/node16 +License: MIT +License File: node_modules/@tsconfig/node16/LICENSE +Copyright: Copyright (c) Microsoft Corporation. +Source: https://github.com/tsconfig/bases.git +Link: https://github.com/tsconfig/bases.git +----------- +@types/babel__core +License: MIT +License File: node_modules/@types/babel__core/LICENSE +Copyright: Copyright (c) Microsoft Corporation. +Source: https://github.com/DefinitelyTyped/DefinitelyTyped.git +Link: https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/babel__core +----------- +@types/babel__generator +License: MIT +License File: node_modules/@types/babel__generator/LICENSE +Copyright: Copyright (c) Microsoft Corporation. +Source: https://github.com/DefinitelyTyped/DefinitelyTyped.git +Link: https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/babel__generator +----------- +@types/babel__template +License: MIT +License File: node_modules/@types/babel__template/LICENSE +Copyright: Copyright (c) Microsoft Corporation. +Source: https://github.com/DefinitelyTyped/DefinitelyTyped.git +Link: https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/babel__template +----------- +@types/babel__traverse +License: MIT +License File: node_modules/@types/babel__traverse/LICENSE +Copyright: Copyright (c) Microsoft Corporation. +Source: https://github.com/DefinitelyTyped/DefinitelyTyped.git +Link: https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/babel__traverse +----------- +@types/estree +License: MIT +License File: node_modules/@types/estree/LICENSE +Copyright: Copyright (c) Microsoft Corporation. +Source: https://github.com/DefinitelyTyped/DefinitelyTyped.git +Link: https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/estree +----------- +@types/graceful-fs +License: MIT +License File: node_modules/@types/graceful-fs/LICENSE +Copyright: Copyright (c) Microsoft Corporation. +Source: https://github.com/DefinitelyTyped/DefinitelyTyped.git +Link: https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/graceful-fs +----------- +@types/istanbul-lib-coverage +License: MIT +License File: node_modules/@types/istanbul-lib-coverage/LICENSE +Copyright: Copyright (c) Microsoft Corporation. +Source: https://github.com/DefinitelyTyped/DefinitelyTyped.git +Link: https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/istanbul-lib-coverage +----------- +@types/istanbul-lib-report +License: MIT +License File: node_modules/@types/istanbul-lib-report/LICENSE +Copyright: Copyright (c) Microsoft Corporation. +Source: https://github.com/DefinitelyTyped/DefinitelyTyped.git +Link: https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/istanbul-lib-report +----------- +@types/istanbul-reports +License: MIT +License File: node_modules/@types/istanbul-reports/LICENSE +Copyright: Copyright (c) Microsoft Corporation. +Source: https://github.com/DefinitelyTyped/DefinitelyTyped.git +Link: https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/istanbul-reports +----------- +@types/jest +License: MIT +License File: node_modules/@types/jest/LICENSE +Copyright: Copyright (c) Microsoft Corporation. +Source: https://github.com/DefinitelyTyped/DefinitelyTyped.git +Link: https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/jest +----------- +@types/jsdom +License: MIT +License File: node_modules/@types/jsdom/LICENSE +Copyright: Copyright (c) Microsoft Corporation. +Source: https://github.com/DefinitelyTyped/DefinitelyTyped.git +Link: https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/jsdom +----------- +@types/json-schema +License: MIT +License File: node_modules/@types/json-schema/LICENSE +Copyright: Copyright (c) Microsoft Corporation. +Source: https://github.com/DefinitelyTyped/DefinitelyTyped.git +Link: https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/json-schema +----------- +@types/json5 +License: MIT +Source: https://www.github.com/DefinitelyTyped/DefinitelyTyped.git +Link: https://www.github.com/DefinitelyTyped/DefinitelyTyped.git +----------- +@types/linkify-it +License: MIT +License File: node_modules/@types/linkify-it/LICENSE +Copyright: Copyright (c) Microsoft Corporation. +Source: https://github.com/DefinitelyTyped/DefinitelyTyped.git +Link: https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/linkify-it +----------- +@types/markdown-it +License: MIT +License File: node_modules/@types/markdown-it/LICENSE +Copyright: Copyright (c) Microsoft Corporation. +Source: https://github.com/DefinitelyTyped/DefinitelyTyped.git +Link: https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/markdown-it +----------- +@types/mdurl +License: MIT +License File: node_modules/@types/mdurl/LICENSE +Copyright: Copyright (c) Microsoft Corporation. +Source: https://github.com/DefinitelyTyped/DefinitelyTyped.git +Link: https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/mdurl +----------- +@types/node +License: MIT +License File: node_modules/@types/node/LICENSE +Copyright: Copyright (c) Microsoft Corporation. +Source: https://github.com/DefinitelyTyped/DefinitelyTyped.git +Link: https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/node +----------- +@types/sortablejs +License: MIT +License File: node_modules/@types/sortablejs/LICENSE +Copyright: Copyright (c) Microsoft Corporation. +Source: https://github.com/DefinitelyTyped/DefinitelyTyped.git +Link: https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/sortablejs +----------- +@types/stack-utils +License: MIT +License File: node_modules/@types/stack-utils/LICENSE +Copyright: Copyright (c) Microsoft Corporation. +Source: https://github.com/DefinitelyTyped/DefinitelyTyped.git +Link: https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/stack-utils +----------- +@types/tough-cookie +License: MIT +License File: node_modules/@types/tough-cookie/LICENSE +Copyright: Copyright (c) Microsoft Corporation. +Source: https://github.com/DefinitelyTyped/DefinitelyTyped.git +Link: https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/tough-cookie +----------- +@types/yargs-parser +License: MIT +License File: node_modules/@types/yargs-parser/LICENSE +Copyright: Copyright (c) Microsoft Corporation. +Source: https://github.com/DefinitelyTyped/DefinitelyTyped.git +Link: https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/yargs-parser +----------- +@types/yargs +License: MIT +License File: node_modules/@types/yargs/LICENSE +Copyright: Copyright (c) Microsoft Corporation. +Source: https://github.com/DefinitelyTyped/DefinitelyTyped.git +Link: https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/yargs diff --git a/dev/licensing/php-library-licenses.txt b/dev/licensing/php-library-licenses.txt new file mode 100644 index 00000000000..f1ca23c9f56 --- /dev/null +++ b/dev/licensing/php-library-licenses.txt @@ -0,0 +1,768 @@ +aws/aws-crt-php +License: Apache-2.0 +License File: vendor/aws/aws-crt-php/LICENSE +Source: https://github.com/awslabs/aws-crt-php.git +Link: https://github.com/awslabs/aws-crt-php +----------- +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 +----------- +bacon/bacon-qr-code +License: BSD-2-Clause +License File: vendor/bacon/bacon-qr-code/LICENSE +Copyright: Copyright (c) 2017, Ben Scholzen 'DASPRiD' +All rights reserved. +Source: https://github.com/Bacon/BaconQrCode.git +Link: https://github.com/Bacon/BaconQrCode +----------- +brick/math +License: MIT +License File: vendor/brick/math/LICENSE +Copyright: Copyright (c) 2013-present Benjamin Morel +Source: https://github.com/brick/math.git +Link: https://github.com/brick/math.git +----------- +carbonphp/carbon-doctrine-types +License: MIT +License File: vendor/carbonphp/carbon-doctrine-types/LICENSE +Copyright: Copyright (c) 2023 Carbon +Source: https://github.com/CarbonPHP/carbon-doctrine-types.git +Link: https://github.com/CarbonPHP/carbon-doctrine-types.git +----------- +dasprid/enum +License: BSD-2-Clause +License File: vendor/dasprid/enum/LICENSE +Copyright: Copyright (c) 2017, Ben Scholzen 'DASPRiD' +All rights reserved. +Source: https://github.com/DASPRiD/Enum.git +Link: https://github.com/DASPRiD/Enum.git +----------- +dflydev/dot-access-data +License: MIT +License File: vendor/dflydev/dot-access-data/LICENSE +Copyright: Copyright (c) 2012 Dragonfly Development Inc. +Source: https://github.com/dflydev/dflydev-dot-access-data.git +Link: https://github.com/dflydev/dflydev-dot-access-data +----------- +doctrine/inflector +License: MIT +License File: vendor/doctrine/inflector/LICENSE +Copyright: Copyright (c) 2006-2015 Doctrine Project +Source: https://github.com/doctrine/inflector.git +Link: https://www.doctrine-project.org/projects/inflector.html +----------- +doctrine/lexer +License: MIT +License File: vendor/doctrine/lexer/LICENSE +Copyright: Copyright (c) 2006-2018 Doctrine Project +Source: https://github.com/doctrine/lexer.git +Link: https://www.doctrine-project.org/projects/lexer.html +----------- +dompdf/dompdf +License: LGPL-2.1 +License File: vendor/dompdf/dompdf/LICENSE.LGPL +Copyright: Copyright (C) 1991, 1999 Free Software Foundation, Inc. +Source: https://github.com/dompdf/dompdf.git +Link: https://github.com/dompdf/dompdf +----------- +dompdf/php-font-lib +License: LGPL-2.1-or-later +License File: vendor/dompdf/php-font-lib/LICENSE +Copyright: Copyright (C) 1991, 1999 Free Software Foundation, Inc. +Source: https://github.com/dompdf/php-font-lib.git +Link: https://github.com/dompdf/php-font-lib +----------- +dompdf/php-svg-lib +License: LGPL-3.0-or-later +License File: vendor/dompdf/php-svg-lib/LICENSE +Copyright: Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. +Source: https://github.com/dompdf/php-svg-lib.git +Link: https://github.com/dompdf/php-svg-lib +----------- +dragonmantank/cron-expression +License: MIT +License File: vendor/dragonmantank/cron-expression/LICENSE +Copyright: Copyright (c) 2011 Michael Dowling <*********@*****.***>, 2016 Chris Tankersley <*****@***********.***>, and contributors +Source: https://github.com/dragonmantank/cron-expression.git +Link: https://github.com/dragonmantank/cron-expression.git +----------- +egulias/email-validator +License: MIT +License File: vendor/egulias/email-validator/LICENSE +Copyright: Copyright (c) 2013-2023 Eduardo Gulias Davis +Source: https://github.com/egulias/EmailValidator.git +Link: https://github.com/egulias/EmailValidator +----------- +firebase/php-jwt +License: BSD-3-Clause +License File: vendor/firebase/php-jwt/LICENSE +Copyright: Copyright (c) 2011, Neuman Vong +Source: https://github.com/firebase/php-jwt.git +Link: https://github.com/firebase/php-jwt +----------- +fruitcake/php-cors +License: MIT +License File: vendor/fruitcake/php-cors/LICENSE +Copyright: Copyright (c) 2013-2017 Alexander <***.*****@*****.***> +Copyright (c) 2017-2022 Barryvdh <********@*****.***> +Source: https://github.com/fruitcake/php-cors.git +Link: https://github.com/fruitcake/php-cors +----------- +graham-campbell/result-type +License: MIT +License File: vendor/graham-campbell/result-type/LICENSE +Copyright: Copyright (c) 2020-2024 Graham Campbell <*****@**********.**.**> +Source: https://github.com/GrahamCampbell/Result-Type.git +Link: https://github.com/GrahamCampbell/Result-Type.git +----------- +guzzlehttp/guzzle +License: MIT +License File: vendor/guzzlehttp/guzzle/LICENSE +Copyright: Copyright (c) 2011 Michael Dowling <*********@*****.***> +Copyright (c) 2012 Jeremy Lindblom <**********@*****.***> +Copyright (c) 2014 Graham Campbell <*****@**********.**.**> +Copyright (c) 2015 Márk Sági-Kazár <****.*********@*****.***> +Copyright (c) 2015 Tobias Schultze <*********@**********.**> +Copyright (c) 2016 Tobias Nyholm <******.******@*****.***> +Copyright (c) 2016 George Mponos <*******@*****.***> +Source: https://github.com/guzzle/guzzle.git +Link: https://github.com/guzzle/guzzle.git +----------- +guzzlehttp/promises +License: MIT +License File: vendor/guzzlehttp/promises/LICENSE +Copyright: Copyright (c) 2015 Michael Dowling <*********@*****.***> +Copyright (c) 2015 Graham Campbell <*****@**********.**.**> +Copyright (c) 2017 Tobias Schultze <*********@**********.**> +Copyright (c) 2020 Tobias Nyholm <******.******@*****.***> +Source: https://github.com/guzzle/promises.git +Link: https://github.com/guzzle/promises.git +----------- +guzzlehttp/psr7 +License: MIT +License File: vendor/guzzlehttp/psr7/LICENSE +Copyright: Copyright (c) 2015 Michael Dowling <*********@*****.***> +Copyright (c) 2015 Márk Sági-Kazár <****.*********@*****.***> +Copyright (c) 2015 Graham Campbell <*****@**********.**.**> +Copyright (c) 2016 Tobias Schultze <*********@**********.**> +Copyright (c) 2016 George Mponos <*******@*****.***> +Copyright (c) 2018 Tobias Nyholm <******.******@*****.***> +Source: https://github.com/guzzle/psr7.git +Link: https://github.com/guzzle/psr7.git +----------- +guzzlehttp/uri-template +License: MIT +License File: vendor/guzzlehttp/uri-template/LICENSE +Copyright: Copyright (c) 2014 Michael Dowling <*********@*****.***> +Copyright (c) 2020 George Mponos <*******@*****.***> +Copyright (c) 2020 Graham Campbell <*****@**********.**.**> +Source: https://github.com/guzzle/uri-template.git +Link: https://github.com/guzzle/uri-template.git +----------- +intervention/gif +License: MIT +License File: vendor/intervention/gif/LICENSE +Copyright: Copyright (c) 2020-present Oliver Vogel +Source: https://github.com/Intervention/gif.git +Link: https://github.com/intervention/gif +----------- +intervention/image +License: MIT +License File: vendor/intervention/image/LICENSE +Copyright: Copyright (c) 2013-present Oliver Vogel +Source: https://github.com/Intervention/image.git +Link: https://image.intervention.io/ +----------- +knplabs/knp-snappy +License: MIT +License File: vendor/knplabs/knp-snappy/LICENSE +Copyright: Copyright (c) 2010 Matthieu Bontemps +Source: https://github.com/KnpLabs/snappy.git +Link: http://github.com/KnpLabs/snappy +----------- +laravel/framework +License: MIT +License File: vendor/laravel/framework/LICENSE.md +Copyright: Copyright (c) Taylor Otwell +Source: https://github.com/laravel/framework.git +Link: https://laravel.com +----------- +laravel/prompts +License: MIT +License File: vendor/laravel/prompts/LICENSE.md +Copyright: Copyright (c) Taylor Otwell +Source: https://github.com/laravel/prompts.git +Link: https://github.com/laravel/prompts.git +----------- +laravel/serializable-closure +License: MIT +License File: vendor/laravel/serializable-closure/LICENSE.md +Copyright: Copyright (c) Taylor Otwell +Source: https://github.com/laravel/serializable-closure.git +Link: https://github.com/laravel/serializable-closure.git +----------- +laravel/socialite +License: MIT +License File: vendor/laravel/socialite/LICENSE.md +Copyright: Copyright (c) Taylor Otwell +Source: https://github.com/laravel/socialite.git +Link: https://laravel.com +----------- +laravel/tinker +License: MIT +License File: vendor/laravel/tinker/LICENSE.md +Copyright: Copyright (c) Taylor Otwell +Source: https://github.com/laravel/tinker.git +Link: https://github.com/laravel/tinker.git +----------- +league/commonmark +License: BSD-3-Clause +License File: vendor/league/commonmark/LICENSE +Copyright: Copyright (c) 2014-2022, Colin O'Dell. All rights reserved. Some code based on commonmark.js (copyright 2014-2018, John MacFarlane) and commonmark-java (copyright 2015-2016, Atlassian Pty Ltd) +Source: https://github.com/thephpleague/commonmark.git +Link: https://commonmark.thephpleague.com +----------- +league/config +License: BSD-3-Clause +License File: vendor/league/config/LICENSE.md +Copyright: Copyright (c) 2022, Colin O'Dell. All rights reserved. +Source: https://github.com/thephpleague/config.git +Link: https://config.thephpleague.com +----------- +league/flysystem +License: MIT +License File: vendor/league/flysystem/LICENSE +Copyright: Copyright (c) 2013-2024 Frank de Jonge +Source: https://github.com/thephpleague/flysystem.git +Link: https://github.com/thephpleague/flysystem.git +----------- +league/flysystem-aws-s3-v3 +License: MIT +License File: vendor/league/flysystem-aws-s3-v3/LICENSE +Copyright: Copyright (c) 2013-2024 Frank de Jonge +Source: https://github.com/thephpleague/flysystem-aws-s3-v3.git +Link: https://github.com/thephpleague/flysystem-aws-s3-v3.git +----------- +league/flysystem-local +License: MIT +License File: vendor/league/flysystem-local/LICENSE +Copyright: Copyright (c) 2013-2024 Frank de Jonge +Source: https://github.com/thephpleague/flysystem-local.git +Link: https://github.com/thephpleague/flysystem-local.git +----------- +league/html-to-markdown +License: MIT +License File: vendor/league/html-to-markdown/LICENSE +Copyright: Copyright (c) 2015 Colin O'Dell; Originally created by Nick Cernis +Source: https://github.com/thephpleague/html-to-markdown.git +Link: https://github.com/thephpleague/html-to-markdown +----------- +league/mime-type-detection +License: MIT +License File: vendor/league/mime-type-detection/LICENSE +Copyright: Copyright (c) 2013-2023 Frank de Jonge +Source: https://github.com/thephpleague/mime-type-detection.git +Link: https://github.com/thephpleague/mime-type-detection.git +----------- +league/oauth1-client +License: MIT +License File: vendor/league/oauth1-client/LICENSE +Copyright: Copyright (c) 2013 Ben Corlett <**********@**.***> +Source: https://github.com/thephpleague/oauth1-client.git +Link: https://github.com/thephpleague/oauth1-client.git +----------- +league/oauth2-client +License: MIT +License File: vendor/league/oauth2-client/LICENSE +Copyright: Copyright (c) 2013-2023 Alex Bilbie <*****@**********.***> +Source: https://github.com/thephpleague/oauth2-client.git +Link: https://github.com/thephpleague/oauth2-client.git +----------- +league/uri +License: MIT +License File: vendor/league/uri/LICENSE +Copyright: Copyright (c) 2015 ignace nyamagana butera +Source: https://github.com/thephpleague/uri.git +Link: https://uri.thephpleague.com +----------- +league/uri-interfaces +License: MIT +License File: vendor/league/uri-interfaces/LICENSE +Copyright: Copyright (c) 2015 ignace nyamagana butera +Source: https://github.com/thephpleague/uri-interfaces.git +Link: https://uri.thephpleague.com +----------- +masterminds/html5 +License: MIT +License File: vendor/masterminds/html5/LICENSE.txt +Copyright: Copyright (c) 2013 The Authors of HTML5-PHP +Source: https://github.com/Masterminds/html5-php.git +Link: http://masterminds.github.io/html5-php +----------- +monolog/monolog +License: MIT +License File: vendor/monolog/monolog/LICENSE +Copyright: Copyright (c) 2011-2020 Jordi Boggiano +Source: https://github.com/Seldaek/monolog.git +Link: https://github.com/Seldaek/monolog +----------- +mtdowling/jmespath.php +License: MIT +License File: vendor/mtdowling/jmespath.php/LICENSE +Copyright: Copyright (c) 2014 Michael Dowling, https://github.com/mtdowling +Source: https://github.com/jmespath/jmespath.php.git +Link: https://github.com/jmespath/jmespath.php.git +----------- +nesbot/carbon +License: MIT +License File: vendor/nesbot/carbon/LICENSE +Copyright: Copyright (C) Brian Nesbitt +Source: https://github.com/CarbonPHP/carbon.git +Link: https://carbon.nesbot.com +----------- +nette/schema +License: BSD-3-Clause GPL-2.0-only GPL-3.0-only +License File: vendor/nette/schema/license.md +Copyright: Copyright (c) 2004, 2014 David Grudl (https://davidgrudl.com) +All rights reserved. +Source: https://github.com/nette/schema.git +Link: https://nette.org +----------- +nette/utils +License: BSD-3-Clause GPL-2.0-only GPL-3.0-only +License File: vendor/nette/utils/license.md +Copyright: Copyright (c) 2004, 2014 David Grudl (https://davidgrudl.com) +All rights reserved. +Source: https://github.com/nette/utils.git +Link: https://nette.org +----------- +nikic/php-parser +License: BSD-3-Clause +License File: vendor/nikic/php-parser/LICENSE +Copyright: Copyright (c) 2011, Nikita Popov +All rights reserved. +Source: https://github.com/nikic/PHP-Parser.git +Link: https://github.com/nikic/PHP-Parser.git +----------- +nunomaduro/termwind +License: MIT +License File: vendor/nunomaduro/termwind/LICENSE.md +Copyright: Copyright (c) Nuno Maduro <***********@*****.***> +Source: https://github.com/nunomaduro/termwind.git +Link: https://github.com/nunomaduro/termwind.git +----------- +onelogin/php-saml +License: MIT +License File: vendor/onelogin/php-saml/LICENSE +Copyright: Copyright (c) 2010-2022 OneLogin, Inc. +Source: https://github.com/SAML-Toolkits/php-saml.git +Link: https://github.com/SAML-Toolkits/php-saml +----------- +paragonie/constant_time_encoding +License: MIT +License File: vendor/paragonie/constant_time_encoding/LICENSE.txt +Copyright: Copyright (c) 2016 - 2022 Paragon Initiative Enterprises +Source: https://github.com/paragonie/constant_time_encoding.git +Link: https://github.com/paragonie/constant_time_encoding.git +----------- +paragonie/random_compat +License: MIT +License File: vendor/paragonie/random_compat/LICENSE +Copyright: Copyright (c) 2015 Paragon Initiative Enterprises +Source: https://github.com/paragonie/random_compat.git +Link: https://github.com/paragonie/random_compat.git +----------- +phpoption/phpoption +License: Apache-2.0 +License File: vendor/phpoption/phpoption/LICENSE +Source: https://github.com/schmittjoh/php-option.git +Link: https://github.com/schmittjoh/php-option.git +----------- +phpseclib/phpseclib +License: MIT +License File: vendor/phpseclib/phpseclib/LICENSE +Copyright: Copyright (c) 2011-2019 TerraFrost and other contributors +Source: https://github.com/phpseclib/phpseclib.git +Link: http://phpseclib.sourceforge.net +----------- +pragmarx/google2fa +License: MIT +License File: vendor/pragmarx/google2fa/LICENSE.md +Copyright: Copyright 2014-2018 Phil, Antonio Carlos Ribeiro and All Contributors +Source: https://github.com/antonioribeiro/google2fa.git +Link: https://github.com/antonioribeiro/google2fa.git +----------- +predis/predis +License: MIT +License File: vendor/predis/predis/LICENSE +Copyright: Copyright (c) 2009-2020 Daniele Alessandri (original work) +Copyright (c) 2021-2024 Till Krüss (modified work) +Source: https://github.com/predis/predis.git +Link: http://github.com/predis/predis +----------- +psr/clock +License: MIT +License File: vendor/psr/clock/LICENSE +Copyright: Copyright (c) 2017 PHP Framework Interoperability Group +Source: https://github.com/php-fig/clock.git +Link: https://github.com/php-fig/clock +----------- +psr/container +License: MIT +License File: vendor/psr/container/LICENSE +Copyright: Copyright (c) 2013-2016 container-interop +Copyright (c) 2016 PHP Framework Interoperability Group +Source: https://github.com/php-fig/container.git +Link: https://github.com/php-fig/container +----------- +psr/event-dispatcher +License: MIT +License File: vendor/psr/event-dispatcher/LICENSE +Copyright: Copyright (c) 2018 PHP-FIG +Source: https://github.com/php-fig/event-dispatcher.git +Link: https://github.com/php-fig/event-dispatcher.git +----------- +psr/http-client +License: MIT +License File: vendor/psr/http-client/LICENSE +Copyright: Copyright (c) 2017 PHP Framework Interoperability Group +Source: https://github.com/php-fig/http-client.git +Link: https://github.com/php-fig/http-client +----------- +psr/http-factory +License: MIT +License File: vendor/psr/http-factory/LICENSE +Copyright: Copyright (c) 2018 PHP-FIG +Source: https://github.com/php-fig/http-factory.git +Link: https://github.com/php-fig/http-factory.git +----------- +psr/http-message +License: MIT +License File: vendor/psr/http-message/LICENSE +Copyright: Copyright (c) 2014 PHP Framework Interoperability Group +Source: https://github.com/php-fig/http-message.git +Link: https://github.com/php-fig/http-message +----------- +psr/log +License: MIT +License File: vendor/psr/log/LICENSE +Copyright: Copyright (c) 2012 PHP Framework Interoperability Group +Source: https://github.com/php-fig/log.git +Link: https://github.com/php-fig/log +----------- +psr/simple-cache +License: MIT +License File: vendor/psr/simple-cache/LICENSE.md +Copyright: Copyright (c) 2016 PHP Framework Interoperability Group +Source: https://github.com/php-fig/simple-cache.git +Link: https://github.com/php-fig/simple-cache.git +----------- +psy/psysh +License: MIT +License File: vendor/psy/psysh/LICENSE +Copyright: Copyright (c) 2012-2023 Justin Hileman +Source: https://github.com/bobthecow/psysh.git +Link: https://psysh.org +----------- +ralouphie/getallheaders +License: MIT +License File: vendor/ralouphie/getallheaders/LICENSE +Copyright: Copyright (c) 2014 Ralph Khattar +Source: https://github.com/ralouphie/getallheaders.git +Link: https://github.com/ralouphie/getallheaders.git +----------- +ramsey/collection +License: MIT +License File: vendor/ramsey/collection/LICENSE +Copyright: Copyright (c) 2015-2022 Ben Ramsey <***@*********.***> +Source: https://github.com/ramsey/collection.git +Link: https://github.com/ramsey/collection.git +----------- +ramsey/uuid +License: MIT +License File: vendor/ramsey/uuid/LICENSE +Copyright: Copyright (c) 2012-2025 Ben Ramsey <***@*********.***> +Source: https://github.com/ramsey/uuid.git +Link: https://github.com/ramsey/uuid.git +----------- +robrichards/xmlseclibs +License: BSD-3-Clause +License File: vendor/robrichards/xmlseclibs/LICENSE +Copyright: Copyright (c) 2007-2024, Robert Richards <*********@*********.***>. +Source: https://github.com/robrichards/xmlseclibs.git +Link: https://github.com/robrichards/xmlseclibs +----------- +sabberworm/php-css-parser +License: MIT +License File: vendor/sabberworm/php-css-parser/LICENSE +Copyright: Copyright (c) 2011 Raphael Schweikert, https://www.sabberworm.com/ +Source: https://github.com/MyIntervals/PHP-CSS-Parser.git +Link: https://www.sabberworm.com/blog/2010/6/10/php-css-parser +----------- +socialiteproviders/discord +License: MIT +Source: https://github.com/SocialiteProviders/Discord.git +Link: https://github.com/SocialiteProviders/Discord.git +----------- +socialiteproviders/gitlab +License: MIT +Source: https://github.com/SocialiteProviders/GitLab.git +Link: https://github.com/SocialiteProviders/GitLab.git +----------- +socialiteproviders/manager +License: MIT +License File: vendor/socialiteproviders/manager/LICENSE +Copyright: Copyright (c) 2015 Andy Wendt +Source: https://github.com/SocialiteProviders/Manager.git +Link: https://socialiteproviders.com +----------- +socialiteproviders/microsoft-azure +License: MIT +Source: https://github.com/SocialiteProviders/Microsoft-Azure.git +Link: https://github.com/SocialiteProviders/Microsoft-Azure.git +----------- +socialiteproviders/okta +License: MIT +Source: https://github.com/SocialiteProviders/Okta.git +Link: https://github.com/SocialiteProviders/Okta.git +----------- +socialiteproviders/twitch +License: MIT +Source: https://github.com/SocialiteProviders/Twitch.git +Link: https://github.com/SocialiteProviders/Twitch.git +----------- +ssddanbrown/htmldiff +License: MIT +License File: vendor/ssddanbrown/htmldiff/license.md +Copyright: Copyright (c) 2024 Nathan Herald, Rohland de Charmoy, Dan Brown +Source: https://codeberg.org/danb/HtmlDiff +Link: https://codeberg.org/danb/HtmlDiff +----------- +symfony/clock +License: MIT +License File: vendor/symfony/clock/LICENSE +Copyright: Copyright (c) 2022-present Fabien Potencier +Source: https://github.com/symfony/clock.git +Link: https://symfony.com +----------- +symfony/console +License: MIT +License File: vendor/symfony/console/LICENSE +Copyright: Copyright (c) 2004-present Fabien Potencier +Source: https://github.com/symfony/console.git +Link: https://symfony.com +----------- +symfony/css-selector +License: MIT +License File: vendor/symfony/css-selector/LICENSE +Copyright: Copyright (c) 2004-present Fabien Potencier +Source: https://github.com/symfony/css-selector.git +Link: https://symfony.com +----------- +symfony/deprecation-contracts +License: MIT +License File: vendor/symfony/deprecation-contracts/LICENSE +Copyright: Copyright (c) 2020-present Fabien Potencier +Source: https://github.com/symfony/deprecation-contracts.git +Link: https://symfony.com +----------- +symfony/error-handler +License: MIT +License File: vendor/symfony/error-handler/LICENSE +Copyright: Copyright (c) 2019-present Fabien Potencier +Source: https://github.com/symfony/error-handler.git +Link: https://symfony.com +----------- +symfony/event-dispatcher +License: MIT +License File: vendor/symfony/event-dispatcher/LICENSE +Copyright: Copyright (c) 2004-present Fabien Potencier +Source: https://github.com/symfony/event-dispatcher.git +Link: https://symfony.com +----------- +symfony/event-dispatcher-contracts +License: MIT +License File: vendor/symfony/event-dispatcher-contracts/LICENSE +Copyright: Copyright (c) 2018-present Fabien Potencier +Source: https://github.com/symfony/event-dispatcher-contracts.git +Link: https://symfony.com +----------- +symfony/finder +License: MIT +License File: vendor/symfony/finder/LICENSE +Copyright: Copyright (c) 2004-present Fabien Potencier +Source: https://github.com/symfony/finder.git +Link: https://symfony.com +----------- +symfony/http-foundation +License: MIT +License File: vendor/symfony/http-foundation/LICENSE +Copyright: Copyright (c) 2004-present Fabien Potencier +Source: https://github.com/symfony/http-foundation.git +Link: https://symfony.com +----------- +symfony/http-kernel +License: MIT +License File: vendor/symfony/http-kernel/LICENSE +Copyright: Copyright (c) 2004-present Fabien Potencier +Source: https://github.com/symfony/http-kernel.git +Link: https://symfony.com +----------- +symfony/mailer +License: MIT +License File: vendor/symfony/mailer/LICENSE +Copyright: Copyright (c) 2019-present Fabien Potencier +Source: https://github.com/symfony/mailer.git +Link: https://symfony.com +----------- +symfony/mime +License: MIT +License File: vendor/symfony/mime/LICENSE +Copyright: Copyright (c) 2010-present Fabien Potencier +Source: https://github.com/symfony/mime.git +Link: https://symfony.com +----------- +symfony/polyfill-ctype +License: MIT +License File: vendor/symfony/polyfill-ctype/LICENSE +Copyright: Copyright (c) 2018-present Fabien Potencier +Source: https://github.com/symfony/polyfill-ctype.git +Link: https://symfony.com +----------- +symfony/polyfill-intl-grapheme +License: MIT +License File: vendor/symfony/polyfill-intl-grapheme/LICENSE +Copyright: Copyright (c) 2015-present Fabien Potencier +Source: https://github.com/symfony/polyfill-intl-grapheme.git +Link: https://symfony.com +----------- +symfony/polyfill-intl-idn +License: MIT +License File: vendor/symfony/polyfill-intl-idn/LICENSE +Copyright: Copyright (c) 2018-present Fabien Potencier and Trevor Rowbotham <******.*********@**.**> +Source: https://github.com/symfony/polyfill-intl-idn.git +Link: https://symfony.com +----------- +symfony/polyfill-intl-normalizer +License: MIT +License File: vendor/symfony/polyfill-intl-normalizer/LICENSE +Copyright: Copyright (c) 2015-present Fabien Potencier +Source: https://github.com/symfony/polyfill-intl-normalizer.git +Link: https://symfony.com +----------- +symfony/polyfill-mbstring +License: MIT +License File: vendor/symfony/polyfill-mbstring/LICENSE +Copyright: Copyright (c) 2015-present Fabien Potencier +Source: https://github.com/symfony/polyfill-mbstring.git +Link: https://symfony.com +----------- +symfony/polyfill-php80 +License: MIT +License File: vendor/symfony/polyfill-php80/LICENSE +Copyright: Copyright (c) 2020-present Fabien Potencier +Source: https://github.com/symfony/polyfill-php80.git +Link: https://symfony.com +----------- +symfony/polyfill-php83 +License: MIT +License File: vendor/symfony/polyfill-php83/LICENSE +Copyright: Copyright (c) 2022-present Fabien Potencier +Source: https://github.com/symfony/polyfill-php83.git +Link: https://symfony.com +----------- +symfony/polyfill-uuid +License: MIT +License File: vendor/symfony/polyfill-uuid/LICENSE +Copyright: Copyright (c) 2018-present Fabien Potencier +Source: https://github.com/symfony/polyfill-uuid.git +Link: https://symfony.com +----------- +symfony/process +License: MIT +License File: vendor/symfony/process/LICENSE +Copyright: Copyright (c) 2004-present Fabien Potencier +Source: https://github.com/symfony/process.git +Link: https://symfony.com +----------- +symfony/routing +License: MIT +License File: vendor/symfony/routing/LICENSE +Copyright: Copyright (c) 2004-present Fabien Potencier +Source: https://github.com/symfony/routing.git +Link: https://symfony.com +----------- +symfony/service-contracts +License: MIT +License File: vendor/symfony/service-contracts/LICENSE +Copyright: Copyright (c) 2018-present Fabien Potencier +Source: https://github.com/symfony/service-contracts.git +Link: https://symfony.com +----------- +symfony/string +License: MIT +License File: vendor/symfony/string/LICENSE +Copyright: Copyright (c) 2019-present Fabien Potencier +Source: https://github.com/symfony/string.git +Link: https://symfony.com +----------- +symfony/translation +License: MIT +License File: vendor/symfony/translation/LICENSE +Copyright: Copyright (c) 2004-present Fabien Potencier +Source: https://github.com/symfony/translation.git +Link: https://symfony.com +----------- +symfony/translation-contracts +License: MIT +License File: vendor/symfony/translation-contracts/LICENSE +Copyright: Copyright (c) 2018-present Fabien Potencier +Source: https://github.com/symfony/translation-contracts.git +Link: https://symfony.com +----------- +symfony/uid +License: MIT +License File: vendor/symfony/uid/LICENSE +Copyright: Copyright (c) 2020-present Fabien Potencier +Source: https://github.com/symfony/uid.git +Link: https://symfony.com +----------- +symfony/var-dumper +License: MIT +License File: vendor/symfony/var-dumper/LICENSE +Copyright: Copyright (c) 2014-present Fabien Potencier +Source: https://github.com/symfony/var-dumper.git +Link: https://symfony.com +----------- +tijsverkoyen/css-to-inline-styles +License: BSD-3-Clause +License File: vendor/tijsverkoyen/css-to-inline-styles/LICENSE.md +Copyright: Copyright (c) Tijs Verkoyen. All rights reserved. +Source: https://github.com/tijsverkoyen/CssToInlineStyles.git +Link: https://github.com/tijsverkoyen/CssToInlineStyles +----------- +vlucas/phpdotenv +License: BSD-3-Clause +License File: vendor/vlucas/phpdotenv/LICENSE +Copyright: Copyright (c) 2014, Graham Campbell. +Source: https://github.com/vlucas/phpdotenv.git +Link: https://github.com/vlucas/phpdotenv.git +----------- +voku/portable-ascii +License: MIT +License File: vendor/voku/portable-ascii/LICENSE.txt +Copyright: Copyright (C) 2019 Lars Moelleken +Source: https://github.com/voku/portable-ascii.git +Link: https://github.com/voku/portable-ascii +----------- +webmozart/assert +License: MIT +License File: vendor/webmozart/assert/LICENSE +Copyright: Copyright (c) 2014 Bernhard Schussek +Source: https://github.com/webmozarts/assert.git +Link: https://github.com/webmozarts/assert.git diff --git a/docker-compose.yml b/docker-compose.yml index 13648c12176..f4c3a64c664 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,20 +1,17 @@ # This is a Docker Compose configuration # intended for development purposes only -version: '3' - volumes: db: {} services: db: - image: mysql:8 + image: mysql:8.4 environment: MYSQL_DATABASE: bookstack-dev MYSQL_USER: bookstack-test MYSQL_PASSWORD: bookstack-test MYSQL_RANDOM_ROOT_PASSWORD: 'true' - command: --default-authentication-plugin=mysql_native_password volumes: - ./dev/docker/init.db:/docker-entrypoint-initdb.d - db:/var/lib/mysql @@ -43,7 +40,7 @@ services: extra_hosts: - "host.docker.internal:host-gateway" node: - image: node:alpine + image: node:22-alpine working_dir: /app user: node volumes: diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 00000000000..0071e65985a --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,64 @@ +import globals from 'globals'; +import js from '@eslint/js'; + +export default [ + js.configs.recommended, + { + ignores: ['resources/**/*-stub.js', 'resources/**/*.ts'], + }, { + languageOptions: { + globals: { + ...globals.browser, + }, + + ecmaVersion: 'latest', + sourceType: 'module', + }, + + rules: { + indent: ['error', 4], + 'arrow-parens': ['error', 'as-needed'], + + 'padded-blocks': ['error', { + blocks: 'never', + classes: 'always', + }], + + 'object-curly-spacing': ['error', 'never'], + + 'space-before-function-paren': ['error', { + anonymous: 'never', + named: 'never', + asyncArrow: 'always', + }], + + 'import/prefer-default-export': 'off', + + 'no-plusplus': ['error', { + allowForLoopAfterthoughts: true, + }], + + 'arrow-body-style': 'off', + 'no-restricted-syntax': 'off', + 'no-continue': 'off', + 'prefer-destructuring': 'off', + 'class-methods-use-this': 'off', + 'no-param-reassign': 'off', + + 'no-console': ['warn', { + allow: ['error', 'warn'], + }], + + 'no-new': 'off', + + 'max-len': ['error', { + code: 110, + tabWidth: 4, + ignoreUrls: true, + ignoreComments: false, + ignoreRegExpLiterals: true, + ignoreStrings: true, + ignoreTemplateLiterals: true, + }], + }, + }]; diff --git a/jest.config.ts b/jest.config.ts new file mode 100644 index 00000000000..53bfceb053e --- /dev/null +++ b/jest.config.ts @@ -0,0 +1,210 @@ +/** + * For a detailed explanation regarding each configuration property, visit: + * https://jestjs.io/docs/configuration + */ + +import type {Config} from 'jest'; +import {pathsToModuleNameMapper} from "ts-jest"; +import { compilerOptions } from './tsconfig.json'; + +const config: Config = { + // All imported modules in your tests should be mocked automatically + // automock: false, + + // Stop running tests after `n` failures + // bail: 0, + + // The directory where Jest should store its cached dependency information + // cacheDirectory: "/tmp/jest_rs", + + // Automatically clear mock calls, instances, contexts and results before every test + clearMocks: true, + + // Indicates whether the coverage information should be collected while executing the test + collectCoverage: false, + + // An array of glob patterns indicating a set of files for which coverage information should be collected + // collectCoverageFrom: undefined, + + // The directory where Jest should output its coverage files + coverageDirectory: "coverage", + + // An array of regexp pattern strings used to skip coverage collection + // coveragePathIgnorePatterns: [ + // "/node_modules/" + // ], + + // Indicates which provider should be used to instrument code for coverage + coverageProvider: "v8", + + // A list of reporter names that Jest uses when writing coverage reports + // coverageReporters: [ + // "json", + // "text", + // "lcov", + // "clover" + // ], + + // An object that configures minimum threshold enforcement for coverage results + // coverageThreshold: undefined, + + // A path to a custom dependency extractor + // dependencyExtractor: undefined, + + // Make calling deprecated APIs throw helpful error messages + // errorOnDeprecated: false, + + // The default configuration for fake timers + // fakeTimers: { + // "enableGlobally": false + // }, + + // Force coverage collection from ignored files using an array of glob patterns + // forceCoverageMatch: [], + + // A path to a module which exports an async function that is triggered once before all test suites + // globalSetup: undefined, + + // A path to a module which exports an async function that is triggered once after all test suites + // globalTeardown: undefined, + + // A set of global variables that need to be available in all test environments + globals: { + __DEV__: true, + }, + + // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers. + // maxWorkers: "50%", + + // An array of directory names to be searched recursively up from the requiring module's location + // moduleDirectories: [ + // "node_modules" + // ], + + // An array of file extensions your modules use + // moduleFileExtensions: [ + // "js", + // "mjs", + // "cjs", + // "jsx", + // "ts", + // "tsx", + // "json", + // "node" + // ], + + modulePaths: ['./'], + + // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module + moduleNameMapper: { + 'lexical/shared/invariant': 'resources/js/wysiwyg/lexical/core/shared/__mocks__/invariant', + ...pathsToModuleNameMapper(compilerOptions.paths), + }, + + // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader + // modulePathIgnorePatterns: [], + + // Activates notifications for test results + // notify: false, + + // An enum that specifies notification mode. Requires { notify: true } + // notifyMode: "failure-change", + + // A preset that is used as a base for Jest's configuration + // preset: undefined, + + // Run tests from one or more projects + // projects: undefined, + + // Use this configuration option to add custom reporters to Jest + // reporters: undefined, + + // Automatically reset mock state before every test + // resetMocks: false, + + // Reset the module registry before running each individual test + // resetModules: false, + + // A path to a custom resolver + // resolver: undefined, + + // Automatically restore mock state and implementation before every test + // restoreMocks: false, + + // The root directory that Jest should scan for tests and modules within + // rootDir: undefined, + + // A list of paths to directories that Jest should use to search for files in + roots: [ + "./resources/js" + ], + + // Allows you to use a custom runner instead of Jest's default test runner + // runner: "jest-runner", + + // The paths to modules that run some code to configure or set up the testing environment before each test + // setupFiles: [], + + // A list of paths to modules that run some code to configure or set up the testing framework before each test + // setupFilesAfterEnv: [], + + // The number of seconds after which a test is considered as slow and reported as such in the results. + // slowTestThreshold: 5, + + // A list of paths to snapshot serializer modules Jest should use for snapshot testing + // snapshotSerializers: [], + + // The test environment that will be used for testing + testEnvironment: "jsdom", + + // Options that will be passed to the testEnvironment + // testEnvironmentOptions: {}, + + // Adds a location field to test results + // testLocationInResults: false, + + // The glob patterns Jest uses to detect test files + testMatch: [ + "**/__tests__/**/*.test.[jt]s", + ], + + // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped + // testPathIgnorePatterns: [ + // "/node_modules/" + // ], + + // The regexp pattern or array of patterns that Jest uses to detect test files + // testRegex: [], + + // This option allows the use of a custom results processor + // testResultsProcessor: undefined, + + // This option allows use of a custom test runner + // testRunner: "jest-circus/runner", + + // A map from regular expressions to paths to transformers + transform: { + "^.+.tsx?$": ["ts-jest",{}], + "^.+.svg$": ["/dev/build/svg-blank-transform.js",{}], + }, + + // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation + // transformIgnorePatterns: [ + // "/node_modules/", + // "\\.pnp\\.[^\\/]+$" + // ], + + // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them + // unmockedModulePathPatterns: undefined, + + // Indicates whether each individual test should be reported during the run + // verbose: undefined, + + // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode + // watchPathIgnorePatterns: [], + + // Whether to use watchman for file crawling + // watchman: true, +}; + +export default config; diff --git a/lang/ar/activities.php b/lang/ar/activities.php index 51f814d3db3..69da1213846 100644 --- a/lang/ar/activities.php +++ b/lang/ar/activities.php @@ -64,12 +64,12 @@ // Auth 'auth_login' => 'تم تسجيل الدخول', 'auth_register' => 'سجل كمستخدم جديد', - 'auth_password_reset_request' => 'طلب رابط جديد لإعادة تعيين كلمة المرور', + 'auth_password_reset_request' => 'طلب رابط جديد لإعادة تعيين كلمة السر', 'auth_password_reset_update' => 'إعادة تعيين كلمة مرور المستخدم', - 'mfa_setup_method' => 'طريقة MFA المكونة', - 'mfa_setup_method_notification' => 'تم تكوين طريقة متعددة العوامل بنجاح', - 'mfa_remove_method' => 'إزالة طريقة MFA', - 'mfa_remove_method_notification' => 'تمت إزالة طريقة متعددة العوامل بنجاح', + 'mfa_setup_method' => 'طريقة المصادقة متعددة العوامل المُهيأة', + 'mfa_setup_method_notification' => 'تم إعداد المصادقة متعددة العوامل بنجاح', + 'mfa_remove_method' => 'إزالة طريقة المصادقة متعددة العوامل', + 'mfa_remove_method_notification' => 'تمت إزالة المصادقة متعددة العوامل بنجاح', // Settings 'settings_update' => 'تحديث الإعدادات', @@ -77,28 +77,36 @@ 'maintenance_action_run' => 'إجراء الصيانة', // Webhooks - 'webhook_create' => 'تم إنشاء webhook', - 'webhook_create_notification' => 'تم إنشاء Webhook بنجاح', - 'webhook_update' => 'تم تحديث webhook', - 'webhook_update_notification' => 'تم تحديث Webhook بنجاح', - 'webhook_delete' => 'حذف webhook', - 'webhook_delete_notification' => 'تم حذف Webhook بنجاح', + 'webhook_create' => 'تم إنشاء خطاف ويب', + 'webhook_create_notification' => 'تم إنشاء خطاف ويب بنجاح', + 'webhook_update' => 'تم تحديث خطاف الويب', + 'webhook_update_notification' => 'تم تحديث خطاف الويب بنجاح', + 'webhook_delete' => 'حذف خطاف ويب', + 'webhook_delete_notification' => 'تم حذف خطاف الويب بنجاح', + + // Imports + 'import_create' => 'تم إنشاء الاستيراد', + 'import_create_notification' => 'تم رفع الاستيراد بنجاح', + 'import_run' => 'تم تحديث الاستيراد', + 'import_run_notification' => 'تم استيراد المحتوى بنجاح', + 'import_delete' => 'تم حذف الاستيراد', + 'import_delete_notification' => 'تم الاستيراد بنجاح', // Users 'user_create' => 'إنشاء مستخدم', - 'user_create_notification' => 'تم انشاء الحساب', + 'user_create_notification' => 'تم إنشاء الحساب', 'user_update' => 'المستخدم المحدث', 'user_update_notification' => 'تم تحديث المستخدم بنجاح', 'user_delete' => 'المستخدم المحذوف', 'user_delete_notification' => 'تم إزالة المستخدم بنجاح', // API Tokens - 'api_token_create' => 'تم إنشاء رمز api', - 'api_token_create_notification' => 'تم إنشاء رمز الـ API بنجاح', - 'api_token_update' => 'تم تحديث رمز api', - 'api_token_update_notification' => 'تم تحديث رمز الـ API بنجاح', - 'api_token_delete' => 'رمز api المحذوف', - 'api_token_delete_notification' => 'تم حذف رمز الـ API بنجاح', + 'api_token_create' => 'تم إنشاء رمز واجهة برمجة التطبيقات -API-', + 'api_token_create_notification' => 'تم إنشاء واجهة برمجة التطبيقات -API- بنجاح', + 'api_token_update' => 'رمز واجهة برمجة التطبيقات المحدث', + 'api_token_update_notification' => 'تم تحديث رمز واجهة برمجة التطبيقات -API- بنجاح', + 'api_token_delete' => 'رمز واجهة برمجة التطبيقات المحذوف', + 'api_token_delete_notification' => 'تم حذف رمز واجهة برمجة التطبيقات -API- بنجاح', // Roles 'role_create' => 'إنشاء صَلاحِيَة', @@ -119,6 +127,14 @@ 'comment_update' => 'تعليق محدث', 'comment_delete' => 'تعليق محذوف', + // Sort Rules + 'sort_rule_create' => 'تم إنشاء قاعدة الفرز', + 'sort_rule_create_notification' => 'تم إنشاء قاعدة الفرز بنجاح', + 'sort_rule_update' => 'تم تحديث قاعدة الفرز', + 'sort_rule_update_notification' => 'تم تحديث قاعدة الفرز بنجاح', + 'sort_rule_delete' => 'تم حذف قاعدة الفرز', + 'sort_rule_delete_notification' => 'تم حذف قاعدة الفرز بنجاح', + // Other - 'permissions_update' => 'تحديث الأذونات', + 'permissions_update' => 'تحديث الصلاحيات', ]; diff --git a/lang/ar/auth.php b/lang/ar/auth.php index 7d64babc449..fb6f3d1cf72 100644 --- a/lang/ar/auth.php +++ b/lang/ar/auth.php @@ -7,26 +7,26 @@ return [ 'failed' => 'البيانات المعطاة لا توافق سجلاتنا.', - 'throttle' => 'تجاوزت الحد الأقصى من المحاولات. الرجاء المحاولة مرة أخرى بعد :seconds seconds.', + 'throttle' => 'تجاوزت الحد الأقصى من المحاولات. الرجاء المحاولة مرة أخرى بعد :seconds ثانية/ثواني.', // Login & Register 'sign_up' => 'إنشاء حساب', 'log_in' => 'تسجيل الدخول', 'log_in_with' => 'تسجيل الدخول باستخدام :socialDriver', 'sign_up_with' => 'إنشاء حساب باستخدام :socialDriver', - 'logout' => 'تسجيل الخروج', + 'logout' => 'الخروج', 'name' => 'الاسم', 'username' => 'اسم المستخدم', 'email' => 'البريد الإلكتروني', - 'password' => 'كلمة المرور', - 'password_confirm' => 'تأكيد كلمة المرور', - 'password_hint' => 'يجب أن تحتوي كلمة المرور على 8 خانات على الأقل', - 'forgot_password' => 'نسيت كلمة المرور؟', + 'password' => 'كلمة السر', + 'password_confirm' => 'تأكيد كلمة السر', + 'password_hint' => 'يجب أن تحتوي كلمة السر على 8 خانات على الأقل', + 'forgot_password' => 'نسيت كلمة السر؟', 'remember_me' => 'تذكرني', 'ldap_email_hint' => 'الرجاء إدخال عنوان بريد إلكتروني لاستخدامه مع الحساب.', 'create_account' => 'إنشاء حساب', - 'already_have_account' => 'لديك حساب بالفعل؟', + 'already_have_account' => 'لديك حساب مسبقاً؟', 'dont_have_account' => 'ليس لديك حساب؟', 'social_login' => 'تسجيل الدخول باستخدام حسابات التواصل الاجتماعي', 'social_registration' => 'إنشاء حساب باستخدام حسابات التواصل الاجتماعي', @@ -44,14 +44,14 @@ 'auto_init_start_link' => 'المتابعة مع المصادقة', // Password Reset - 'reset_password' => 'استعادة كلمة المرور', - 'reset_password_send_instructions' => 'أدخل بريدك الإلكتروني بالأسفل وسيتم إرسال رسالة برابط لاستعادة كلمة المرور.', + 'reset_password' => 'استعادة كلمة السر', + 'reset_password_send_instructions' => 'أدخل بريدك الإلكتروني بالأسفل وسيتم إرسال رسالة برابط لاستعادة كلمة السر.', 'reset_password_send_button' => 'أرسل رابط الاستعادة', - 'reset_password_sent' => 'سيتم إرسال رابط إعادة تعيين كلمة المرور إلى عنوان البريد الإلكتروني هذا إذا كان موجودًا في النظام.', - 'reset_password_success' => 'تمت استعادة كلمة المرور بنجاح.', - 'email_reset_subject' => 'استعد كلمة المرور الخاصة بتطبيق :appName', - 'email_reset_text' => 'تم إرسال هذه الرسالة بسبب تلقينا لطلب استعادة كلمة المرور الخاصة بحسابكم.', - 'email_reset_not_requested' => 'إذا لم يتم طلب استعادة كلمة المرور من قبلكم، فلا حاجة لاتخاذ أية خطوات.', + 'reset_password_sent' => 'سيتم إرسال رابط إعادة تعيين كلمة السر إلى عنوان البريد الإلكتروني هذا إذا كان موجودًا في النظام.', + 'reset_password_success' => 'تمت استعادة كلمة السر بنجاح.', + 'email_reset_subject' => 'استعد كلمة السر الخاصة بتطبيق :appName', + 'email_reset_text' => 'تم إرسال هذه الرسالة بسبب تلقينا لطلب استعادة كلمة السر الخاصة بحسابكم.', + 'email_reset_not_requested' => 'إذا لم يتم طلب استعادة كلمة السر من قبلكم، فلا حاجة لاتخاذ أية خطوات.', // Email Confirmation 'email_confirm_subject' => 'تأكيد بريدكم الإلكتروني لتطبيق :appName', @@ -60,7 +60,7 @@ 'email_confirm_action' => 'تأكيد البريد الإلكتروني', 'email_confirm_send_error' => 'تأكيد البريد الإلكتروني مطلوب ولكن النظام لم يستطع إرسال الرسالة. تواصل مع مشرف النظام للتأكد من إعدادات البريد.', 'email_confirm_success' => 'تم تأكيد بريدك الإلكتروني! يمكنك الآن تسجيل الدخول باستخدام عنوان البريد الإلكتروني هذا.', - 'email_confirm_resent' => 'تمت إعادة إرسال رسالة التأكيد. الرجاء مراجعة صندوق الوارد', + 'email_confirm_resent' => 'تمت إعادة إرسال رسالة التأكيد، الرجاء مراجعة صندوق الوارد.', 'email_confirm_thanks' => 'شكرا للتأكيد!', 'email_confirm_thanks_desc' => 'الرجاء الانتظار لحظة بينما يتم التعامل مع التأكيد الخاص بك. إذا لم يتم إعادة توجيهك بعد 3 ثوان اضغط على الرابط "المتابعة" أدناه للمتابعة.', @@ -72,46 +72,46 @@ // User Invite 'user_invite_email_subject' => 'تمت دعوتك للانضمام إلى صفحة الحالة الخاصة بـ :app_name!', - 'user_invite_email_greeting' => 'تم إنشاء حساب مستخدم لك على %site%.', - 'user_invite_email_text' => 'انقر على الزر أدناه لتعيين كلمة مرور الحساب والحصول على الوصول:', + 'user_invite_email_greeting' => 'تم إنشاء حساب مستخدم لك على :appName.', + 'user_invite_email_text' => 'انقر على الزر أدناه لتعيين كلمة سر الحساب والحصول على الوصول:', 'user_invite_email_action' => 'كلمة سر المستخدم', 'user_invite_page_welcome' => 'مرحبا بكم في :appName!', - 'user_invite_page_text' => 'لإكمال حسابك والحصول على حق الوصول تحتاج إلى تعيين كلمة مرور سيتم استخدامها لتسجيل الدخول إلى :appName في الزيارات المستقبلية.', - 'user_invite_page_confirm_button' => 'تأكيد كلمة المرور', - 'user_invite_success_login' => 'تم تأكيد كلمة المرور. يمكنك الآن تسجيل الدخول باستخدام كلمة المرور المحددة للوصول إلى :appName!', + 'user_invite_page_text' => 'لإكمال حسابك والحصول على حق الوصول تحتاج إلى تعيين كلمة السر سيتم استخدامها لتسجيل الدخول إلى :appName في الزيارات المستقبلية.', + 'user_invite_page_confirm_button' => 'تأكيد كلمة السر', + 'user_invite_success_login' => 'تم تأكيد كلمة السر. يمكنك الآن تسجيل الدخول باستخدام كلمة السر المحددة للوصول إلى :appName !', // Multi-factor Authentication 'mfa_setup' => 'إعداد المصادقة متعددة العوامل', 'mfa_setup_desc' => 'إعداد المصادقة متعددة العوامل كطبقة إضافية من الأمان لحساب المستخدم الخاص بك.', 'mfa_setup_configured' => 'تم إعداده مسبقاً', 'mfa_setup_reconfigure' => 'إعادة التكوين', - 'mfa_setup_remove_confirmation' => 'هل أنت متأكد من أنك تريد إزالة طريقة المصادقة متعددة العناصر هذه؟', - 'mfa_setup_action' => 'إعداد (تنصيب)', - 'mfa_backup_codes_usage_limit_warning' => 'You have less than 5 backup codes remaining, Please generate and store a new set before you run out of codes to prevent being locked out of your account.', + 'mfa_setup_remove_confirmation' => 'متأكد من أنك تريد إزالة طريقة المصادقة متعددة العوامل هذه؟', + 'mfa_setup_action' => 'إعداد', + 'mfa_backup_codes_usage_limit_warning' => 'لديك أقل من 5 رموز احتياطية متبقية، الرجاء إنشاء وتخزين مجموعة جديدة قبل نفاد الرموز لتجنب إغلاق حسابك.', 'mfa_option_totp_title' => 'تطبيق الجوال', - 'mfa_option_totp_desc' => 'To use multi-factor authentication you\'ll need a mobile application that supports TOTP such as Google Authenticator, Authy or Microsoft Authenticator.', - 'mfa_option_backup_codes_title' => 'Backup Codes', - 'mfa_option_backup_codes_desc' => 'Securely store a set of one-time-use backup codes which you can enter to verify your identity.', - 'mfa_gen_confirm_and_enable' => 'Confirm and Enable', - 'mfa_gen_backup_codes_title' => 'Backup Codes Setup', - 'mfa_gen_backup_codes_desc' => 'Store the below list of codes in a safe place. When accessing the system you\'ll be able to use one of the codes as a second authentication mechanism.', - 'mfa_gen_backup_codes_download' => 'Download Codes', - 'mfa_gen_backup_codes_usage_warning' => 'Each code can only be used once', - 'mfa_gen_totp_title' => 'Mobile App Setup', - 'mfa_gen_totp_desc' => 'To use multi-factor authentication you\'ll need a mobile application that supports TOTP such as Google Authenticator, Authy or Microsoft Authenticator.', - 'mfa_gen_totp_scan' => 'Scan the QR code below using your preferred authentication app to get started.', - 'mfa_gen_totp_verify_setup' => 'Verify Setup', - 'mfa_gen_totp_verify_setup_desc' => 'Verify that all is working by entering a code, generated within your authentication app, in the input box below:', - 'mfa_gen_totp_provide_code_here' => 'Provide your app generated code here', - 'mfa_verify_access' => 'Verify Access', - 'mfa_verify_access_desc' => 'Your user account requires you to confirm your identity via an additional level of verification before you\'re granted access. Verify using one of your configured methods to continue.', - 'mfa_verify_no_methods' => 'No Methods Configured', - 'mfa_verify_no_methods_desc' => 'No multi-factor authentication methods could be found for your account. You\'ll need to set up at least one method before you gain access.', - 'mfa_verify_use_totp' => 'Verify using a mobile app', - 'mfa_verify_use_backup_codes' => 'Verify using a backup code', - 'mfa_verify_backup_code' => 'Backup Code', - 'mfa_verify_backup_code_desc' => 'Enter one of your remaining backup codes below:', - 'mfa_verify_backup_code_enter_here' => 'Enter backup code here', - 'mfa_verify_totp_desc' => 'Enter the code, generated using your mobile app, below:', - 'mfa_setup_login_notification' => 'Multi-factor method configured, Please now login again using the configured method.', + 'mfa_option_totp_desc' => 'لاستخدام المصادقة المتعددة العوامل، ستحتاج إلى تطبيق جوال يدعم كلمة السر المؤقته -TOTP- مثل جوجل أوثنتيكاتور -Google Authenticator- أو أوثي -Authy- أو مايكروسوفت أوثنتيكاتور -Microsoft Authenticator-.', + 'mfa_option_backup_codes_title' => 'رموز النسخ الاحتياطي', + 'mfa_option_backup_codes_desc' => 'إنشاء مجموعة من رموز النسخ الاحتياطية للاستخدام مرة واحدة و التي سَتُدِخلها عند تسجيل الدخول للتحقق من هويتك. احرص أن تخزينها في مكان آمن.', + 'mfa_gen_confirm_and_enable' => 'تأكيد وتمكين', + 'mfa_gen_backup_codes_title' => 'إعداد رموز النسخ الاحتياطي', + 'mfa_gen_backup_codes_desc' => 'خَزِن قائمة الرموز أدناه في مكان آمن. عند الوصول إلى النظام، ستتمكن من استخدام أحد الرموز كآلية مصادقة ثانية.', + 'mfa_gen_backup_codes_download' => 'تنزيل الرموز', + 'mfa_gen_backup_codes_usage_warning' => 'يمكن استخدام كل رمز مرة واحدة فقط', + 'mfa_gen_totp_title' => 'إعداد تطبيق الجوال', + 'mfa_gen_totp_desc' => 'لاستخدام المصادقة المتعددة ، ستحتاج إلى تطبيق جوال كلمة السر المؤقته -TOTP- مثل جوجل أوثنتيكاتور -Google Authenticator- أو أوثي -Authy- أو مايكروسوفت أوثنتيكاتور -Microsoft Authenticator-.', + 'mfa_gen_totp_scan' => 'امسح رمز الاستجابة السريعة -QR- أدناه باستخدام تطبيق المصادقة المفضل لديك للبدء.', + 'mfa_gen_totp_verify_setup' => 'التحقق من الإعداد', + 'mfa_gen_totp_verify_setup_desc' => 'تحقق أن كل شيء يعمل عن طريق إدخال رمز تم إنشاؤه داخل تطبيق المصادقة الخاص بك في مربع الإدخال أدناه:', + 'mfa_gen_totp_provide_code_here' => 'أدخل الرمز الذي تم إنشاؤه للتطبيق الخاص بك هنا', + 'mfa_verify_access' => 'التحقق من الوصول', + 'mfa_verify_access_desc' => 'يتطلب حساب المستخدم الخاص بك تأكيد هويتك عن طريق مستوى إضافي من التحقق قبل منحك حق الوصول. تحقق استخدام إحدى الطرق التي إعدادها للمتابعة.', + 'mfa_verify_no_methods' => 'لا توجد طرق معدة', + 'mfa_verify_no_methods_desc' => 'لم يتم العثور على طرق مصادقة متعددة العوامل لحسابك. ستحتاج إلى إعداد طريقة واحدة على الأقل قبل أن تتمكن من الوصول.', + 'mfa_verify_use_totp' => 'التحقق باستخدام تطبيق الجوال', + 'mfa_verify_use_backup_codes' => 'التحقق باستخدام رمز النسخ الاحتياطي', + 'mfa_verify_backup_code' => 'الرموز الاحتياطية', + 'mfa_verify_backup_code_desc' => 'أدخل أحد الرموز الاحتياطية المتبقية أدناه:', + 'mfa_verify_backup_code_enter_here' => 'أدخل الرمز الاحتياطي هنا', + 'mfa_verify_totp_desc' => 'أدخل الرمز الذي تم إنشاؤه باستخدام تطبيق الجوال الخاص بك، أدناه:', + 'mfa_setup_login_notification' => 'تم إعداد طريقة الدخول متعددة العوامل، يرجى الآن تسجيل الدخول مرة أخرى باستخدام الطريقة التي تم إعدادها.', ]; diff --git a/lang/ar/common.php b/lang/ar/common.php index 5a391cd6d4b..44d057d7eb1 100644 --- a/lang/ar/common.php +++ b/lang/ar/common.php @@ -20,7 +20,7 @@ 'description' => 'الوصف', 'role' => 'الدور', 'cover_image' => 'صورة الغلاف', - 'cover_image_description' => 'الصورة يجب أن تكون مقاربة لحجم 440×250 بكسل.', + 'cover_image_description' => 'يجب أن يكون حجم هذه الصورة تقريبًا 440 في 250 بكسل، مع أنّه سيتم تحجيمها وقصها بشكل مرن لتناسب واجهة المستخدم في سيناريوهات مختلفة حسب الحاجة، لذا فإن الأبعاد الفعلية للعرض ستختلف.', // Actions 'actions' => 'إجراءات', @@ -30,6 +30,8 @@ 'create' => 'إنشاء', 'update' => 'تحديث', 'edit' => 'تعديل', + 'archive' => 'أرشف', + 'unarchive' => 'إلغاء الأرشفة', 'sort' => 'سرد', 'move' => 'نقل', 'copy' => 'نسخ', @@ -48,8 +50,8 @@ 'unfavourite' => 'إزالة من المفضلة', 'next' => 'التالي', 'previous' => 'السابق', - 'filter_active' => 'الفلاتر المفعلة:', - 'filter_clear' => 'مسح الفلاتر', + 'filter_active' => 'التصفية المفعلة:', + 'filter_clear' => 'مسح التصفية', 'download' => 'تنزيل', 'open_in_tab' => 'فتح في علامة تبويب', 'open' => 'فتح', @@ -107,4 +109,7 @@ // Not directly used but available for convenience to users. 'privacy_policy' => 'سياسة الخصوصية', 'terms_of_service' => 'اتفاقية شروط الخدمة', + + // OpenSearch + 'opensearch_description' => 'البحث عن :appName', ]; diff --git a/lang/ar/components.php b/lang/ar/components.php index 211b249eadd..12bc0e58ae9 100644 --- a/lang/ar/components.php +++ b/lang/ar/components.php @@ -6,36 +6,36 @@ // Image Manager 'image_select' => 'تحديد صورة', - 'image_list' => 'Image List', - 'image_details' => 'Image Details', - 'image_upload' => 'Upload Image', - 'image_intro' => 'Here you can select and manage images that have been previously uploaded to the system.', - 'image_intro_upload' => 'Upload a new image by dragging an image file into this window, or by using the "Upload Image" button above.', + 'image_list' => 'قائمة الصور', + 'image_details' => 'تفاصيل الصورة', + 'image_upload' => 'تحميل صورة', + 'image_intro' => 'هنا يمكنك تحديد وإدارة الصور التي تم تحميلها مسبقًا إلى النظام.', + 'image_intro_upload' => 'تحميل صورة جديدة عن طريق سحب الصورة إلى هذه النافذة، أو باستخدام زر "تحميل صورة" أعلاه.', 'image_all' => 'الكل', 'image_all_title' => 'عرض جميع الصور', 'image_book_title' => 'عرض الصور المرفوعة لهذا الكتاب', 'image_page_title' => 'عرض الصور المرفوعة لهذه الصفحة', 'image_search_hint' => 'البحث باستخدام اسم الصورة', 'image_uploaded' => 'وقت الرفع :uploadedDate', - 'image_uploaded_by' => 'Uploaded by :userName', - 'image_uploaded_to' => 'Uploaded to :pageLink', - 'image_updated' => 'Updated :updateDate', + 'image_uploaded_by' => 'تم تحميلها من قبل :userName', + 'image_uploaded_to' => 'تم رفعها إلى :pageLink', + 'image_updated' => 'تم تحديثها :updatedate', 'image_load_more' => 'المزيد', 'image_image_name' => 'اسم الصورة', 'image_delete_used' => 'هذه الصورة مستخدمة بالصفحات أدناه.', 'image_delete_confirm_text' => 'هل أنت متأكد من أنك تريد حذف هذه الصورة؟', 'image_select_image' => 'تحديد الصورة', 'image_dropzone' => 'قم بإسقاط الصورة أو اضغط هنا للرفع', - 'image_dropzone_drop' => 'Drop images here to upload', + 'image_dropzone_drop' => 'إسقاط صورة أو اضغط هنا للرفع', 'images_deleted' => 'تم حذف الصور', 'image_preview' => 'معاينة الصور', 'image_upload_success' => 'تم رفع الصورة بنجاح', 'image_update_success' => 'تم تحديث تفاصيل الصورة بنجاح', 'image_delete_success' => 'تم حذف الصورة بنجاح', - 'image_replace' => 'Replace Image', - 'image_replace_success' => 'Image file successfully updated', - 'image_rebuild_thumbs' => 'Regenerate Size Variations', - 'image_rebuild_thumbs_success' => 'Image size variations successfully rebuilt!', + 'image_replace' => 'استبدال صورة', + 'image_replace_success' => 'تم تحديث الصورة بنجاح', + 'image_rebuild_thumbs' => 'تجديد تغيرات الحجم', + 'image_rebuild_thumbs_success' => 'تم إعادة بناء تغيرات حجم الصورة بنجاح!', // Code Editor 'code_editor' => 'تعديل الشفرة', diff --git a/lang/ar/editor.php b/lang/ar/editor.php index d951f62a125..b3a8fd3c7be 100644 --- a/lang/ar/editor.php +++ b/lang/ar/editor.php @@ -13,6 +13,7 @@ 'cancel' => 'إلغاء', 'save' => 'حفظ', 'close' => 'إغلاق', + 'apply' => 'تطبيق', 'undo' => 'تراجع', 'redo' => 'إعادة التنفيذ', 'left' => 'يسار', @@ -24,7 +25,7 @@ 'width' => 'العرض', 'height' => 'الارتفاع', 'More' => 'المزيد', - 'select' => 'Select...', + 'select' => 'إختار...', // Toolbar 'formats' => 'التنسيقات', @@ -47,76 +48,80 @@ 'superscript' => 'نص مرتفع', 'subscript' => 'نص منخفض', 'text_color' => 'لون النص', + 'highlight_color' => 'لون التمييز', 'custom_color' => 'لون مخصص', 'remove_color' => 'إزالة اللون', 'background_color' => 'لون الخلفية', 'align_left' => 'محاذاة لليسار', 'align_center' => 'محاذاة بالمنتصف', 'align_right' => 'مُحاذاة لليمين', - 'align_justify' => 'Justify', + 'align_justify' => 'المحاذاة', 'list_bullet' => 'قائمة نقاط', 'list_numbered' => 'قائمة مرقمة', - 'list_task' => 'Task list', + 'list_task' => 'قائمة المهام', 'indent_increase' => 'زيادة البادئة', 'indent_decrease' => 'إنقاص البادئة', 'table' => 'جدول', 'insert_image' => 'ادراج صورة', - 'insert_image_title' => 'Insert/Edit Image', - 'insert_link' => 'Insert/edit link', - 'insert_link_title' => 'Insert/Edit Link', - 'insert_horizontal_line' => 'Insert horizontal line', - 'insert_code_block' => 'Insert code block', - 'edit_code_block' => 'Edit code block', - 'insert_drawing' => 'Insert/edit drawing', - 'drawing_manager' => 'Drawing manager', - 'insert_media' => 'Insert/edit media', - 'insert_media_title' => 'Insert/Edit Media', - 'clear_formatting' => 'Clear formatting', - 'source_code' => 'Source code', - 'source_code_title' => 'Source Code', - 'fullscreen' => 'Fullscreen', - 'image_options' => 'Image options', + 'insert_image_title' => 'إضافة/تحرير الصورة', + 'insert_link' => 'إضافة/تعديل الرابط', + 'insert_link_title' => 'إضافة/تحرير الرابط', + 'insert_horizontal_line' => 'إضافة خط أفقي', + 'insert_code_block' => 'إضافة مربع رموز برمجية', + 'edit_code_block' => 'تعديل مربع الرموز البرمجية', + 'insert_drawing' => 'إضافة/تعديل الرسم', + 'drawing_manager' => 'إدارة الرسم', + 'insert_media' => 'إضافة/تحرير الوسائط', + 'insert_media_title' => 'إضافة/تحرير الوسائط', + 'clear_formatting' => 'مسح التنسيق', + 'source_code' => 'الرمز البرمجي', + 'source_code_title' => 'الرمز البرمجي', + 'fullscreen' => 'شاشة كاملة', + 'image_options' => 'خيارات الصورة', // Tables - 'table_properties' => 'Table properties', - 'table_properties_title' => 'Table Properties', - 'delete_table' => 'Delete table', - 'insert_row_before' => 'Insert row before', - 'insert_row_after' => 'Insert row after', - 'delete_row' => 'Delete row', - 'insert_column_before' => 'Insert column before', - 'insert_column_after' => 'Insert column after', - 'delete_column' => 'Delete column', - 'table_cell' => 'Cell', - 'table_row' => 'Row', - 'table_column' => 'Column', - 'cell_properties' => 'Cell properties', - 'cell_properties_title' => 'Cell Properties', - 'cell_type' => 'Cell type', - 'cell_type_cell' => 'Cell', - 'cell_scope' => 'Scope', - 'cell_type_header' => 'Header cell', - 'merge_cells' => 'Merge cells', - 'split_cell' => 'Split cell', - 'table_row_group' => 'Row Group', - 'table_column_group' => 'Column Group', - 'horizontal_align' => 'Horizontal align', - 'vertical_align' => 'Vertical align', - 'border_width' => 'Border width', - 'border_style' => 'Border style', - 'border_color' => 'Border color', - 'row_properties' => 'Row properties', - 'row_properties_title' => 'Row Properties', - 'cut_row' => 'Cut row', - 'copy_row' => 'Copy row', - 'paste_row_before' => 'Paste row before', - 'paste_row_after' => 'Paste row after', - 'row_type' => 'Row type', - 'row_type_header' => 'Header', - 'row_type_body' => 'Body', - 'row_type_footer' => 'Footer', - 'alignment' => 'Alignment', - 'cut_column' => 'Cut column', + 'table_properties' => 'خصائص الجدول', + 'table_properties_title' => 'خصائص الجدول', + 'delete_table' => 'حذف الجدول', + 'table_clear_formatting' => 'مسح تنسيق الجدول', + 'resize_to_contents' => 'تغيير الحجم إلى المحتوى', + 'row_header' => 'رأس الصف', + 'insert_row_before' => 'إضافة صف قبل', + 'insert_row_after' => 'إضافة صف بعد', + 'delete_row' => 'حذف الصف', + 'insert_column_before' => 'إدراج عمود قبل', + 'insert_column_after' => 'إدراج عمود بعد', + 'delete_column' => 'حذف عمود', + 'table_cell' => 'خلية', + 'table_row' => 'صف', + 'table_column' => 'عمود', + 'cell_properties' => 'خصائص الخلية', + 'cell_properties_title' => 'خصائص الخلية', + 'cell_type' => 'نوع الخلية', + 'cell_type_cell' => 'الخلية', + 'cell_scope' => 'النِطَاق', + 'cell_type_header' => 'عنوان الخلية', + 'merge_cells' => 'دمج الخلايا', + 'split_cell' => 'خلية منقسمة', + 'table_row_group' => 'مجموعة الصفوف', + 'table_column_group' => 'مجموعة الأعمدة', + 'horizontal_align' => 'محاذاة أفقية', + 'vertical_align' => 'محاذاة عمودية', + 'border_width' => 'عرض الحدود', + 'border_style' => 'نمط الحدود', + 'border_color' => 'لون الحدود', + 'row_properties' => 'خصائص الصف', + 'row_properties_title' => 'خصائص الصف', + 'cut_row' => 'فص الصف', + 'copy_row' => 'نسخ الصف', + 'paste_row_before' => 'لصق الصف قبل', + 'paste_row_after' => 'لصق الصف بعد', + 'row_type' => 'نوع الصف', + 'row_type_header' => 'العنوان', + 'row_type_body' => 'المحتوى ', + 'row_type_footer' => 'تذييل', + 'alignment' => 'المحاذاة', + 'cut_column' => 'قص العمود', 'copy_column' => 'نسخ العمود', 'paste_column_before' => 'لصق عمود قبل', 'paste_column_after' => 'لصق عمود بعد', @@ -124,51 +129,54 @@ 'cell_spacing' => 'تباعد الخلايا', 'caption' => 'الوصف', 'show_caption' => 'إظهار الوصف', - 'constrain' => 'Constrain proportions', - 'cell_border_solid' => 'Solid', - 'cell_border_dotted' => 'Dotted', - 'cell_border_dashed' => 'Dashed', - 'cell_border_double' => 'Double', - 'cell_border_groove' => 'Groove', - 'cell_border_ridge' => 'Ridge', - 'cell_border_inset' => 'Inset', - 'cell_border_outset' => 'Outset', - 'cell_border_none' => 'None', - 'cell_border_hidden' => 'Hidden', + 'constrain' => 'تقييد النسب', + 'cell_border_solid' => 'لون كامل', + 'cell_border_dotted' => 'مُنَقط', + 'cell_border_dashed' => 'متقطع', + 'cell_border_double' => 'مزدوج', + 'cell_border_groove' => 'أخدود', + 'cell_border_ridge' => 'الحافَة', + 'cell_border_inset' => 'الداخلية', + 'cell_border_outset' => 'الخارجية', + 'cell_border_none' => 'لا شَيْء', + 'cell_border_hidden' => 'مخفي', // Images, links, details/summary & embed - 'source' => 'Source', - 'alt_desc' => 'Alternative description', - 'embed' => 'Embed', - 'paste_embed' => 'Paste your embed code below:', - 'url' => 'URL', - 'text_to_display' => 'Text to display', - 'title' => 'Title', - 'open_link' => 'Open link', - 'open_link_in' => 'Open link in...', - 'open_link_current' => 'Current window', - 'open_link_new' => 'New window', - 'remove_link' => 'Remove link', - 'insert_collapsible' => 'Insert collapsible block', - 'collapsible_unwrap' => 'Unwrap', - 'edit_label' => 'Edit label', - 'toggle_open_closed' => 'Toggle open/closed', - 'collapsible_edit' => 'Edit collapsible block', - 'toggle_label' => 'Toggle label', + 'source' => 'المصدر', + 'alt_desc' => 'وصف بديل', + 'embed' => 'تضمين', + 'paste_embed' => 'قم بلصق الرموز المصدرية المضمنة الخاص بك أدناه:', + 'url' => 'الرابط', + 'text_to_display' => 'النص المراد عرضه', + 'title' => 'العنوان', + 'browse_links' => 'تصفح الروابط', + 'open_link' => 'افتح الرابط', + 'open_link_in' => 'افتح الرابط في...', + 'open_link_current' => 'النافذة الحالية', + 'open_link_new' => 'نافذة جديدة', + 'remove_link' => 'إزالة الرابط', + 'insert_collapsible' => 'أدخل كتلة قابلة للطي', + 'collapsible_unwrap' => 'بسط', + 'edit_label' => 'عدل الوصف', + 'toggle_open_closed' => 'التبديل بين الفتح والإغلاق', + 'collapsible_edit' => 'تحرير الكتلة القابلة للطي', + 'toggle_label' => 'تبديل التسمية', // About view - 'about' => 'About the editor', - 'about_title' => 'About the WYSIWYG Editor', - 'editor_license' => 'Editor License & Copyright', - 'editor_tiny_license' => 'This editor is built using :tinyLink which is provided under the MIT license.', - 'editor_tiny_license_link' => 'The copyright and license details of TinyMCE can be found here.', - 'save_continue' => 'Save Page & Continue', - 'callouts_cycle' => '(Keep pressing to toggle through types)', - 'link_selector' => 'Link to content', - 'shortcuts' => 'Shortcuts', - 'shortcut' => 'Shortcut', - 'shortcuts_intro' => 'The following shortcuts are available in the editor:', - 'windows_linux' => '(Windows/Linux)', - 'mac' => '(Mac)', - 'description' => 'Description', + 'about' => 'عن المحرر', + 'about_title' => 'حول محرر ما تراه هو ما تحصل عليه -WYSIWYG-', + 'editor_license' => 'رخصة المحرر وحقوق التأليف والنشر', + 'editor_lexical_license' => 'تم إنشاء هذا المحرر باعتباره فرعًا لـ :lexicalLink الذي يتم توزيعه بموجب ترخيص معهد ماساتشوستس للتقانة -MIT-.', + 'editor_lexical_license_link' => 'يمكنك العثور على تفاصيل الترخيص الكاملة هنا.', + 'editor_tiny_license' => 'تم إنشاء هذا المحرر باستخدام :tinyLink والذي يتم توفيره بموجب ترخيص معهد ماساتشوستس للتقانة -MIT-.', + 'editor_tiny_license_link' => 'يمكن الاطلاع هنا على تفاصيل حقوق التأليف والنشر والترخيص الخاصة بتاینی‌ام‌سی‌ای -TinyMCE-.', + 'save_continue' => 'حفظ الصفحة ومتابعة', + 'callouts_cycle' => '(استمر في الضغط للتبديل بين الأنواع)', + 'link_selector' => 'رابط للمحتوى', + 'shortcuts' => 'الاختصارات', + 'shortcut' => 'الاختصار', + 'shortcuts_intro' => 'الاختصارات التالية متاحة في المحرر:', + 'windows_linux' => '(ويندوز/لينكس)', + 'mac' => '(ماك)', + 'description' => 'الوصف', ]; diff --git a/lang/ar/entities.php b/lang/ar/entities.php index 30ad7845423..1f1d4e4117a 100644 --- a/lang/ar/entities.php +++ b/lang/ar/entities.php @@ -22,15 +22,15 @@ 'meta_created_name' => 'أنشئ :timeLength بواسطة :user', 'meta_updated' => 'مُحدث :timeLength', 'meta_updated_name' => 'مُحدث :timeLength بواسطة :user', - 'meta_owned_name' => 'Owned by :user', - 'meta_reference_count' => 'Referenced by :count item|Referenced by :count items', + 'meta_owned_name' => 'مملوكة لـ:user', + 'meta_reference_count' => 'مشار إليه :count مرة|مشار إليه :count مرة', 'entity_select' => 'اختيار الكيان', - 'entity_select_lack_permission' => 'You don\'t have the required permissions to select this item', + 'entity_select_lack_permission' => 'ليس لديك الصلاحيات المطلوبة لتحديد هذا العنصر', 'images' => 'صور', 'my_recent_drafts' => 'مسوداتي الحديثة', 'my_recently_viewed' => 'ما عرضته مؤخراً', - 'my_most_viewed_favourites' => 'My Most Viewed Favourites', - 'my_favourites' => 'My Favourites', + 'my_most_viewed_favourites' => 'مفضلاتي الأكثر مشاهدة', + 'my_favourites' => 'مفضلاتي', 'no_pages_viewed' => 'لم تستعرض أي صفحات', 'no_pages_recently_created' => 'لم تنشأ أي صفحات مؤخراً', 'no_pages_recently_updated' => 'لم تُحدّث أي صفحات مؤخراً', @@ -38,19 +38,47 @@ 'export_html' => 'صفحة ويب', 'export_pdf' => 'ملف PDF', 'export_text' => 'ملف نص عادي', - 'export_md' => 'Markdown File', + 'export_md' => 'ملف ماركداون -Markdown-', + 'export_zip' => 'ملف مضغوط -ZIP-', + 'default_template' => 'قالب الصفحة الافتراضية', + 'default_template_explain' => 'قم بتعيين قالب صفحة سيتم استخدامه كمحتوى افتراضي لجميع الصفحات التي تم إنشاؤها ضمن هذا العنصر. ضع في اعتبارك أن هذا لن يتم استخدامه إلا إذا كان لدى منشئ الصفحة حق الوصول إلى صفحة القالب المختارة.', + 'default_template_select' => 'حدد صفحة القالب', + 'import' => 'استيراد', + 'import_validate' => 'التحقق من صحة الاستيراد', + 'import_desc' => 'استيراد الكتب والفصول والصفحات باستخدام تصدير مِلَفّ مضغوط ZIP محمول من نفس النظام أو نظام مختلف. حدد مِلَفّ ZIP للمتابعة. بعد تحميل المِلَفّ والتحقق من صحته، ستتمكن من إعداد وتأكيد الاستيراد في العرض التالي.', + 'import_zip_select' => 'حدد مِلَفّ مضغوط بصيغة ZIP للتحميل', + 'import_zip_validation_errors' => 'تم اكتشاف أخطاء في أثناء التحقق من صحة المِلَفّ المضغوط ZIP المقدم:', + 'import_pending' => 'الاستيرادات المعلقة', + 'import_pending_none' => 'لم يتم البَدْء في أي عملية استيراد.', + 'import_continue' => 'متابعة الاستيراد', + 'import_continue_desc' => 'راجع المحتوى الذي يجب استيراده من المِلَفّ المضغوط ZIP الذي تم تحميله. عندما يكون جاهزًا، تشتغل عملية الاستيراد لإضافة محتوياته إلى هذا النظام. سيتم إزالة مِلَفّ الاستيراد الذي تم تحميله تلقائيًا عند الاستيراد الناجح.', + 'import_details' => 'تفاصيل الاستيراد', + 'import_run' => 'تشغيل الاستيراد', + 'import_size' => 'حجم الاستيراد :size ', + 'import_uploaded_at' => 'تم تحميلة في :relativeTime', + 'import_uploaded_by' => 'رُفِع بواسطة', + 'import_location' => 'موقع الاستيراد', + 'import_location_desc' => 'حدد موقعًا مستهدفًا للمحتوى المستورد. ستحتاج إلى الصلاحيات ذات الصلة لإنشاء المحتوى داخل الموقع الذي تختاره.', + 'import_delete_confirm' => 'متيقِّن من أنك تريد حذف الاستيراد؟', + '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', // Permissions and restrictions 'permissions' => 'الأذونات', - 'permissions_desc' => 'Set permissions here to override the default permissions provided by user roles.', - 'permissions_book_cascade' => 'Permissions set on books will automatically cascade to child chapters and pages, unless they have their own permissions defined.', - 'permissions_chapter_cascade' => 'Permissions set on chapters will automatically cascade to child pages, unless they have their own permissions defined.', + 'permissions_desc' => 'تعيين الصلاحيات هنا لتجاوز الصلاحيات الافتراضية التي توفرها أدوار المستخدم.', + 'permissions_book_cascade' => 'سيتم نقل الصلاحيات التي تم تعيينها للكتب تلقائيًا إلى الفصول والصفحات الفرعية، ما لم تكن لديها صلاحيات خاصة بها محددة.', + 'permissions_chapter_cascade' => 'سيتم نقل الصلاحيات التي تم تعيينها على الفصول تلقائيًا إلى الصفحات الفرعية، ما لم تكن لديها صلاحيات خاصة بها محددة.', 'permissions_save' => 'حفظ الأذونات', - 'permissions_owner' => 'Owner', - 'permissions_role_everyone_else' => 'Everyone Else', - 'permissions_role_everyone_else_desc' => 'Set permissions for all roles not specifically overridden.', - 'permissions_role_override' => 'Override permissions for role', - 'permissions_inherit_defaults' => 'Inherit defaults', + 'permissions_owner' => 'المالك', + 'permissions_role_everyone_else' => 'الآخرين', + 'permissions_role_everyone_else_desc' => 'تعيين الصلاحيات لجميع الأدوار التي لم يتم تجاوزها على وجه التحديد.', + 'permissions_role_override' => 'تجاوز الصلاحيات للدور', + 'permissions_inherit_defaults' => 'وراثة الإعدادات الافتراضية', // Search 'search_results' => 'نتائج البحث', @@ -70,7 +98,7 @@ 'search_permissions_set' => 'حزمة الأذونات', 'search_created_by_me' => 'أنشئت بواسطتي', 'search_updated_by_me' => 'حُدثت بواسطتي', - 'search_owned_by_me' => 'Owned by me', + 'search_owned_by_me' => 'مملوكة لي', 'search_date_options' => 'خيارات التاريخ', 'search_updated_before' => 'حدثت قبل', 'search_updated_after' => 'حدثت بعد', @@ -93,24 +121,24 @@ 'shelves_save' => 'حفظ الرف', 'shelves_books' => 'كتب على هذا الرف', 'shelves_add_books' => 'إضافة كتب لهذا الرف', - 'shelves_drag_books' => 'Drag books below to add them to this shelf', + 'shelves_drag_books' => 'اسحب الكتب الموجودة بالأسفل لإضافتها إلى هذا الرف', 'shelves_empty_contents' => 'لا توجد كتب مخصصة لهذا الرف', 'shelves_edit_and_assign' => 'تحرير الرف لإدراج كتب', - 'shelves_edit_named' => 'Edit Shelf :name', - 'shelves_edit' => 'Edit Shelf', - 'shelves_delete' => 'Delete Shelf', - 'shelves_delete_named' => 'Delete Shelf :name', - 'shelves_delete_explain' => "This will delete the shelf with the name ':name'. Contained books will not be deleted.", - 'shelves_delete_confirmation' => 'Are you sure you want to delete this shelf?', - 'shelves_permissions' => 'Shelf Permissions', - 'shelves_permissions_updated' => 'Shelf Permissions Updated', - 'shelves_permissions_active' => 'Shelf Permissions Active', - 'shelves_permissions_cascade_warning' => 'Permissions on shelves do not automatically cascade to contained books. This is because a book can exist on multiple shelves. Permissions can however be copied down to child books using the option found below.', - 'shelves_permissions_create' => 'Shelf create permissions are only used for copying permissions to child books using the action below. They do not control the ability to create books.', + 'shelves_edit_named' => 'تعديل الرف :name', + 'shelves_edit' => 'تعديل الرف', + 'shelves_delete' => 'حذف الرف', + 'shelves_delete_named' => 'حذف الرف :name', + 'shelves_delete_explain' => "سيؤدي هذا إلى حذف الرف الذي يحمل الاسم ':name'. لن يتم حذف الكتب المضمنة بداخله.", + 'shelves_delete_confirmation' => 'هل أنت متأكد أنك تريد حذف هذا الرف؟', + 'shelves_permissions' => 'صلاحيات الرف', + 'shelves_permissions_updated' => 'تم تحديث صلاحيات الرف', + 'shelves_permissions_active' => 'صلاحيات الرف نشطة', + 'shelves_permissions_cascade_warning' => 'لا يتم نقل الصلاحيات الموجودة على الأرفف تلقائيًا إلى الكتب الموجودة في كل رف. وذلك لأن الكتاب يمكن أن يوجد على أرفف متعددة. ومع ذلك، يمكن نسخ الصلاحيات إلى الكتب الفرعية باستخدام الخِيار الموجود أدناه.', + 'shelves_permissions_create' => 'تُستخدم صلاحيات إنشاء الرفوف فقط لنسخ الصلاحيات إلى الكتب الفرعية باستخدام الإجراء أدناه. ولا تتحكم في القدرة على إنشاء الكتب.', 'shelves_copy_permissions_to_books' => 'نسخ أذونات الوصول إلى الكتب', 'shelves_copy_permissions' => 'نسخ الأذونات', - 'shelves_copy_permissions_explain' => 'This will apply the current permission settings of this shelf to all books contained within. Before activating, ensure any changes to the permissions of this shelf have been saved.', - 'shelves_copy_permission_success' => 'Shelf permissions copied to :count books', + 'shelves_copy_permissions_explain' => 'سيؤدي هذا إلى تطبيق إعدادات الصلاحيات الحالية لهذا الرف على جميع الكتب الموجودة بداخله. قبل التنشيط، تأكد من حفظ أي تغييرات على صلاحيات هذا الرف.', + 'shelves_copy_permission_success' => 'تم نسخ صلاحيات الرف إلى :count كتاب/كتب', // Books 'book' => 'كتاب', @@ -132,9 +160,6 @@ 'books_edit_named' => 'تعديل كتاب :bookName', 'books_form_book_name' => 'اسم الكتاب', 'books_save' => 'حفظ الكتاب', - 'books_default_template' => 'Default Page Template', - 'books_default_template_explain' => 'Assign a page template that will be used as the default content for all new pages in this book. Keep in mind this will only be used if the page creator has view access to those chosen template page.', - 'books_default_template_select' => 'Select a template page', 'books_permissions' => 'أذونات الكتاب', 'books_permissions_updated' => 'تم تحديث أذونات الكتاب', 'books_empty_contents' => 'لم يتم إنشاء أي صفحات أو فصول لهذا الكتاب.', @@ -145,7 +170,9 @@ 'books_search_this' => 'البحث في هذا الكتاب', 'books_navigation' => 'تصفح الكتاب', 'books_sort' => 'فرز محتويات الكتاب', - 'books_sort_desc' => 'Move chapters and pages within a book to reorganise its contents. Other books can be added which allows easy moving of chapters and pages between books.', + 'books_sort_desc' => 'نقل الفصول والصفحات داخل الكتاب لإعادة تنظيم محتوياته. يمكن إضافة كتب أخرى مما يسمح بنقل الفصول والصفحات بسهولة بين الكتب. اختياريًا، يمكن تعيين قاعدة فرز تلقائي لفرز محتويات هذا الكتاب تلقائيًا عند حدوث تغييرات.', + 'books_sort_auto_sort' => 'خِيار الفرز التلقائي', + 'books_sort_auto_sort_active' => 'الفرز التلقائي الشَغَّال: :sortName', 'books_sort_named' => 'فرز كتاب :bookName', 'books_sort_name' => 'ترتيب حسب الإسم', 'books_sort_created' => 'ترتيب حسب تاريخ الإنشاء', @@ -154,19 +181,19 @@ 'books_sort_chapters_last' => 'الفصول الأخيرة', 'books_sort_show_other' => 'عرض كتب أخرى', 'books_sort_save' => 'حفظ الترتيب الجديد', - 'books_sort_show_other_desc' => 'Add other books here to include them in the sort operation, and allow easy cross-book reorganisation.', - 'books_sort_move_up' => 'Move Up', - 'books_sort_move_down' => 'Move Down', - 'books_sort_move_prev_book' => 'Move to Previous Book', - 'books_sort_move_next_book' => 'Move to Next Book', - 'books_sort_move_prev_chapter' => 'Move Into Previous Chapter', - 'books_sort_move_next_chapter' => 'Move Into Next Chapter', - 'books_sort_move_book_start' => 'Move to Start of Book', - 'books_sort_move_book_end' => 'Move to End of Book', - 'books_sort_move_before_chapter' => 'Move to Before Chapter', - 'books_sort_move_after_chapter' => 'Move to After Chapter', - 'books_copy' => 'Copy Book', - 'books_copy_success' => 'Book successfully copied', + 'books_sort_show_other_desc' => 'أضف كتبًا أخرى هنا لتضمينها في عملية الفرز، والسماح بإعادة تنظيم الكتب بسهولة.', + 'books_sort_move_up' => 'حرك للأعلى', + 'books_sort_move_down' => 'حرك للأسفل', + 'books_sort_move_prev_book' => 'نقل للكتاب السابق', + 'books_sort_move_next_book' => 'نقل للكتاب التالي', + 'books_sort_move_prev_chapter' => 'نقل إلى الفصل السابق', + 'books_sort_move_next_chapter' => 'نقل إلى الفصل التالي', + 'books_sort_move_book_start' => 'نقل إلى بداية الكتاب', + 'books_sort_move_book_end' => 'نقل إلى نهاية الكتاب', + 'books_sort_move_before_chapter' => 'نقل إلى الفصل السابق', + 'books_sort_move_after_chapter' => 'نقل إلى الفصل التالي', + 'books_copy' => 'نسخة الكتاب', + 'books_copy_success' => 'تم نسخ الكتاب بنجاح', // Chapters 'chapter' => 'فصل', @@ -177,21 +204,21 @@ 'chapters_create' => 'إنشاء فصل جديد', 'chapters_delete' => 'حذف الفصل', 'chapters_delete_named' => 'حذف فصل :chapterName', - 'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages that exist within this chapter will also be deleted.', + 'chapters_delete_explain' => 'سيؤدي هذا إلى حذف الفصل الذي يحمل الاسم \':chapterName\'. كما سيتم حذف جميع الصفحات الموجودة داخل هذا الفصل.', 'chapters_delete_confirm' => 'تأكيد حذف الفصل؟', 'chapters_edit' => 'تعديل الفصل', 'chapters_edit_named' => 'تعديل فصل :chapterName', 'chapters_save' => 'حفظ الفصل', 'chapters_move' => 'نقل الفصل', 'chapters_move_named' => 'نقل فصل :chapterName', - 'chapters_copy' => 'Copy Chapter', - 'chapters_copy_success' => 'Chapter successfully copied', + 'chapters_copy' => 'نسخ الفصل', + 'chapters_copy_success' => 'تم نسخ الفصل بنجاح', 'chapters_permissions' => 'أذونات الفصل', 'chapters_empty' => 'لا توجد أي صفحات في هذا الفصل حالياً', 'chapters_permissions_active' => 'أذونات الفصل مفعلة', 'chapters_permissions_success' => 'تم تحديث أذونات الفصل', 'chapters_search_this' => 'البحث في هذا الفصل', - 'chapter_sort_book' => 'Sort Book', + 'chapter_sort_book' => 'فرز الكتاب', // Pages 'page' => 'صفحة', @@ -207,7 +234,7 @@ 'pages_delete_draft' => 'حذف المسودة', 'pages_delete_success' => 'تم حذف الصفحة', 'pages_delete_draft_success' => 'تم حذف المسودة', - 'pages_delete_warning_template' => 'This page is in active use as a book default page template. These books will no longer have a default page template assigned after this page is deleted.', + 'pages_delete_warning_template' => 'هذه الصفحة قيد الاستخدام كقالب افتراضي لصفحات الكتب أو الفصول. لن يكون لهذه الكتب أو الفصول قالب افتراضي بعد حذفها.', 'pages_delete_confirm' => 'تأكيد حذف الصفحة؟', 'pages_delete_draft_confirm' => 'تأكيد حذف المسودة؟', 'pages_editing_named' => ':pageName قيد التعديل', @@ -218,21 +245,23 @@ 'pages_editing_page' => 'الصفحة قيد التعديل', 'pages_edit_draft_save_at' => 'تم خفظ المسودة في ', 'pages_edit_delete_draft' => 'حذف المسودة', - 'pages_edit_delete_draft_confirm' => 'Are you sure you want to delete your draft page changes? All of your changes, since the last full save, will be lost and the editor will be updated with the latest page non-draft save state.', + 'pages_edit_delete_draft_confirm' => 'متيقِّن من رغبتك في حذف تغييرات صفحة المُسَوَّدَة؟ ستُفقد جميع تغييراتك، منذ آخر حفظ كامل، وسيتم تحديث المحرر بأحدث حالة حفظ للصفحة (غير مسودة).', 'pages_edit_discard_draft' => 'التخلص من المسودة', - 'pages_edit_switch_to_markdown' => 'Switch to Markdown Editor', - 'pages_edit_switch_to_markdown_clean' => '(Clean Content)', - 'pages_edit_switch_to_markdown_stable' => '(Stable Content)', - 'pages_edit_switch_to_wysiwyg' => 'Switch to WYSIWYG Editor', + 'pages_edit_switch_to_markdown' => 'التبديل إلى محرر ماركداون -Markdown-', + 'pages_edit_switch_to_markdown_clean' => '(محتوى نظيف)', + 'pages_edit_switch_to_markdown_stable' => '(محتوى مستقر)', + 'pages_edit_switch_to_wysiwyg' => 'التبديل إلى محرر ما تراه هو ما تحصل عليه -WYSIWYG-', + 'pages_edit_switch_to_new_wysiwyg' => 'التبديل إلى محرر ما تراه هو ما تحصل عليه الجديد -new WYSIWYG-', + 'pages_edit_switch_to_new_wysiwyg_desc' => '(في الاختبار التجريبي)', 'pages_edit_set_changelog' => 'تثبيت سجل التعديل', 'pages_edit_enter_changelog_desc' => 'ضع وصف مختصر للتعديلات التي تمت', 'pages_edit_enter_changelog' => 'أدخل سجل التعديل', - 'pages_editor_switch_title' => 'Switch Editor', - 'pages_editor_switch_are_you_sure' => 'Are you sure you want to change the editor for this page?', - 'pages_editor_switch_consider_following' => 'Consider the following when changing editors:', - 'pages_editor_switch_consideration_a' => 'Once saved, the new editor option will be used by any future editors, including those that may not be able to change editor type themselves.', - 'pages_editor_switch_consideration_b' => 'This can potentially lead to a loss of detail and syntax in certain circumstances.', - 'pages_editor_switch_consideration_c' => 'Tag or changelog changes, made since last save, won\'t persist across this change.', + 'pages_editor_switch_title' => 'تبديل المحرر', + 'pages_editor_switch_are_you_sure' => 'متيقِّن أنك تريد تغيير المحرر لهذه الصفحة؟', + 'pages_editor_switch_consider_following' => 'عند تغيير المحررين، ضع في اعتبارك ما يلي:', + 'pages_editor_switch_consideration_a' => 'بمجرد الحفظ، سيتم استخدام خِيار المحرر الجديد بواسطة أي محررين مستقبليين، بما في ذلك أولئك الذين قد لا يتمكنون من تغيير نوع المحرر بأنفسهم.', + 'pages_editor_switch_consideration_b' => 'من الممكن أن يؤدي هذا إلى فقدان التفاصيل والنحو في ظروف معينة.', + 'pages_editor_switch_consideration_c' => 'لن تستمر تغييرات العلامة أو سجل التغييرات، التي تم إجراؤها منذ الحفظ الأخير، عبر هذا التغيير.', 'pages_save' => 'حفظ الصفحة', 'pages_title' => 'عنوان الصفحة', 'pages_name' => 'اسم الصفحة', @@ -241,10 +270,11 @@ 'pages_md_insert_image' => 'إدخال صورة', 'pages_md_insert_link' => 'إدراج ارتباط الكيان', 'pages_md_insert_drawing' => 'إدخال رسمة', - 'pages_md_show_preview' => 'Show preview', - 'pages_md_sync_scroll' => 'Sync preview scroll', - 'pages_drawing_unsaved' => 'Unsaved Drawing Found', - 'pages_drawing_unsaved_confirm' => 'Unsaved drawing data was found from a previous failed drawing save attempt. Would you like to restore and continue editing this unsaved drawing?', + 'pages_md_show_preview' => 'عرض المعاينة', + 'pages_md_sync_scroll' => 'مزامنة معاينة التمرير', + 'pages_md_plain_editor' => 'محرر النصوص العادي', + 'pages_drawing_unsaved' => 'تم العثور على رسم غير محفوظ', + 'pages_drawing_unsaved_confirm' => 'تم العثور على بيانات رسم غير محفوظة من محاولة حفظ رسم سابقة فاشلة. هل ترغب في استعادة هذا الرسم غير المحفوظ ومواصلة تحريره؟', 'pages_not_in_chapter' => 'صفحة ليست في فصل', 'pages_move' => 'نقل الصفحة', 'pages_copy' => 'نسخ الصفحة', @@ -254,17 +284,17 @@ 'pages_permissions_success' => 'تم تحديث أذونات الصفحة', 'pages_revision' => 'مراجعة', 'pages_revisions' => 'مراجعات الصفحة', - 'pages_revisions_desc' => 'Listed below are all the past revisions of this page. You can look back upon, compare, and restore old page versions if permissions allow. The full history of the page may not be fully reflected here since, depending on system configuration, old revisions could be auto-deleted.', + 'pages_revisions_desc' => 'تجد أدناه جميع الإصدارات السابقة لهذه الصفحة. يمكنك الاطلاع عليها ومقارنتها واستعادة الإصدارات القديمة إذا سمحت الصلاحيات بذلك. قد لا يظهر تاريخ الصفحة بالكامل هنا، إذ قد تُحذف الإصدارات القديمة تلقائيًا، وذلك حسب إعدادات النظام.', 'pages_revisions_named' => 'مراجعات صفحة :pageName', 'pages_revision_named' => 'مراجعة صفحة :pageName', - 'pages_revision_restored_from' => 'Restored from #:id; :summary', + 'pages_revision_restored_from' => 'تم الاستعادة من #:id; :summary', 'pages_revisions_created_by' => 'أنشئ بواسطة', 'pages_revisions_date' => 'تاريخ المراجعة', 'pages_revisions_number' => '#', - 'pages_revisions_sort_number' => 'Revision Number', + 'pages_revisions_sort_number' => 'رَقْم المراجعة', 'pages_revisions_numbered' => 'مراجعة #:id', 'pages_revisions_numbered_changes' => 'مراجعة #: رقم تعريفي التغييرات', - 'pages_revisions_editor' => 'Editor Type', + 'pages_revisions_editor' => 'نوع المحرر', 'pages_revisions_changelog' => 'سجل التعديل', 'pages_revisions_changes' => 'التعديلات', 'pages_revisions_current' => 'النسخة الحالية', @@ -272,20 +302,20 @@ 'pages_revisions_restore' => 'استرجاع', 'pages_revisions_none' => 'لا توجد مراجعات لهذه الصفحة', 'pages_copy_link' => 'نسخ الرابط', - 'pages_edit_content_link' => 'Jump to section in editor', - 'pages_pointer_enter_mode' => 'Enter section select mode', - 'pages_pointer_label' => 'Page Section Options', - 'pages_pointer_permalink' => 'Page Section Permalink', - 'pages_pointer_include_tag' => 'Page Section Include Tag', - 'pages_pointer_toggle_link' => 'Permalink mode, Press to show include tag', - 'pages_pointer_toggle_include' => 'Include tag mode, Press to show permalink', + 'pages_edit_content_link' => 'انتقل إلى القسم في المحرر', + 'pages_pointer_enter_mode' => 'أدخل وضع اختيار القسم', + 'pages_pointer_label' => 'خيارات قسم الصفحة', + 'pages_pointer_permalink' => 'رابط دائم لقسم الصفحة', + 'pages_pointer_include_tag' => 'قسم الصفحة يتضمن العلامة', + 'pages_pointer_toggle_link' => 'وضع الرابط الدائم، اضغط لإظهار علامة التضمين', + 'pages_pointer_toggle_include' => 'تضمين وضع العلامة، اضغط لإظهار الرابط الدائم', 'pages_permissions_active' => 'أذونات الصفحة مفعلة', 'pages_initial_revision' => 'نشر مبدئي', - 'pages_references_update_revision' => 'System auto-update of internal links', + 'pages_references_update_revision' => 'التحديث التلقائي للنظام للروابط الداخلية', 'pages_initial_name' => 'صفحة جديدة', 'pages_editing_draft_notification' => 'جارٍ تعديل مسودة لم يتم حفظها من :timeDiff.', 'pages_draft_edited_notification' => 'تم تحديث هذه الصفحة منذ ذلك الوقت. من الأفضل التخلص من هذه المسودة.', - 'pages_draft_page_changed_since_creation' => 'This page has been updated since this draft was created. It is recommended that you discard this draft or take care not to overwrite any page changes.', + 'pages_draft_page_changed_since_creation' => 'تم تحديث هذه الصفحة منذ إنشاء هذه المُسَوَّدَة. يُنصح بتجاهل هذه المُسَوَّدَة أو الحرص على عدم استبدال أي تغييرات في الصفحة.', 'pages_draft_edit_active' => [ 'start_a' => ':count من المستخدمين بدأوا بتعديل هذه الصفحة', 'start_b' => ':userName بدأ بتعديل هذه الصفحة', @@ -293,44 +323,44 @@ 'time_b' => 'في آخر :minCount دقيقة/دقائق', 'message' => 'وقت البدء: احرص على عدم الكتابة فوق تحديثات بعضنا البعض!', ], - 'pages_draft_discarded' => 'Draft discarded! The editor has been updated with the current page content', - 'pages_draft_deleted' => 'Draft deleted! The editor has been updated with the current page content', + 'pages_draft_discarded' => 'تم رفض المُسَوَّدَة! تم تحديث المحرر بمحتوى الصفحة الحالي.', + 'pages_draft_deleted' => 'تم حذف المُسَوَّدَة! تم تحديث المحرر بمحتوى الصفحة الحالي.', 'pages_specific' => 'صفحة محددة', 'pages_is_template' => 'قالب الصفحة', // Editor Sidebar - 'toggle_sidebar' => 'Toggle Sidebar', + 'toggle_sidebar' => 'تبديل الشريط الجانبي', 'page_tags' => 'وسوم الصفحة', 'chapter_tags' => 'وسوم الفصل', 'book_tags' => 'وسوم الكتاب', 'shelf_tags' => 'علامات الرف', 'tag' => 'وسم', 'tags' => 'وسوم', - 'tags_index_desc' => 'Tags can be applied to content within the system to apply a flexible form of categorization. Tags can have both a key and value, with the value being optional. Once applied, content can then be queried using the tag name and value.', + 'tags_index_desc' => 'يمكن تطبيق الوسوم على المحتوى داخل النظام لتطبيق تصنيف مرن. يمكن أن تحتوي الوسوم على مفتاح وقيمة، مع العلم أن القيمة اختيارية. بعد تطبيقها، يمكن الاستعلام عن المحتوى باستخدام اسم الوسم وقيمته.', 'tag_name' => 'اسم العلامة', 'tag_value' => 'قيمة الوسم (اختياري)', 'tags_explain' => "إضافة الوسوم تساعد بترتيب وتقسيم المحتوى. \n من الممكن وضع قيمة لكل وسم لترتيب أفضل وأدق.", 'tags_add' => 'إضافة وسم آخر', 'tags_remove' => 'إزالة هذه العلامة', - 'tags_usages' => 'Total tag usages', - 'tags_assigned_pages' => 'Assigned to Pages', - 'tags_assigned_chapters' => 'Assigned to Chapters', - 'tags_assigned_books' => 'Assigned to Books', - 'tags_assigned_shelves' => 'Assigned to Shelves', - 'tags_x_unique_values' => ':count unique values', - 'tags_all_values' => 'All values', - 'tags_view_tags' => 'View Tags', - 'tags_view_existing_tags' => 'View existing tags', - 'tags_list_empty_hint' => 'Tags can be assigned via the page editor sidebar or while editing the details of a book, chapter or shelf.', + 'tags_usages' => 'إجمالي استخدامات العلامة', + 'tags_assigned_pages' => 'مُخصصة للصفحات', + 'tags_assigned_chapters' => 'مُخصصة للفصول', + 'tags_assigned_books' => 'مُخصص للكتب', + 'tags_assigned_shelves' => 'مُخصصة للأرفف', + 'tags_x_unique_values' => 'قيم الفريدة :count', + 'tags_all_values' => 'جميع القيم', + 'tags_view_tags' => 'عرض العلامات', + 'tags_view_existing_tags' => 'عرض العلامات الموجودة', + 'tags_list_empty_hint' => 'يمكن تعيين العلامات بواسطة الشريط الجانبي لمحرر الصفحة أو خلال تحرير تفاصيل الكتاب أو الفصل أو الرف.', 'attachments' => 'المرفقات', 'attachments_explain' => 'ارفع بعض الملفات أو أرفق بعض الروابط لعرضها بصفحتك. ستكون الملفات والروابط معروضة في الشريط الجانبي للصفحة.', 'attachments_explain_instant_save' => 'سيتم حفظ التغييرات هنا آنيا.', 'attachments_upload' => 'رفع ملف', 'attachments_link' => 'إرفاق رابط', - 'attachments_upload_drop' => 'Alternatively you can drag and drop a file here to upload it as an attachment.', + 'attachments_upload_drop' => 'وبدلاً من ذلك، يمكنك سحب المِلَفّ وإفلاته هنا لتحميله كمرفق.', 'attachments_set_link' => 'تحديد الرابط', 'attachments_delete' => 'هل أنت متأكد من أنك تريد حذف هذا المرفق؟', - 'attachments_dropzone' => 'Drop files here to upload', + 'attachments_dropzone' => 'قم بإسقاط الملفات هنا للتحميل', 'attachments_no_files' => 'لم تُرفع أي ملفات', 'attachments_explain_link' => 'بالإمكان إرفاق رابط في حال عدم تفضيل رفع ملف. قد يكون الرابط لصفحة أخرى أو لملف في أحد خدمات التخزين السحابي.', 'attachments_link_name' => 'اسم الرابط', @@ -367,19 +397,28 @@ 'comment' => 'تعليق', 'comments' => 'تعليقات', 'comment_add' => 'إضافة تعليق', + 'comment_none' => 'لا توجد تعليقات لعرضها', 'comment_placeholder' => 'ضع تعليقاً هنا', - 'comment_count' => '{0} لا توجد تعليقات|{1} تعليق واحد|{2} تعليقان[3,*] :count تعليقات', + 'comment_thread_count' => ':count تعليقات| :count تعليقات', + 'comment_archived_count' => ':count مؤرشف', + 'comment_archived_threads' => 'المواضيع المؤرشفة', 'comment_save' => 'حفظ التعليق', 'comment_new' => 'تعليق جديد', 'comment_created' => 'تم التعليق :createDiff', 'comment_updated' => 'تم التحديث :updateDiff بواسطة :username', - 'comment_updated_indicator' => 'Updated', + 'comment_updated_indicator' => 'تم التحديث', 'comment_deleted_success' => 'تم حذف التعليق', 'comment_created_success' => 'تمت إضافة التعليق', 'comment_updated_success' => 'تم تحديث التعليق', + 'comment_archive_success' => 'تم أرشفة التعليق', + 'comment_unarchive_success' => 'تعليق غير مؤرشف', + 'comment_view' => 'عرض التعليق', + 'comment_jump_to_thread' => 'انتقل إلى الموضوع', 'comment_delete_confirm' => 'تأكيد حذف التعليق؟', 'comment_in_reply_to' => 'رداً على :commentId', - 'comment_editor_explain' => 'Here are the comments that have been left on this page. Comments can be added & managed when viewing the saved page.', + 'comment_reference' => 'المرجع', + 'comment_reference_outdated' => '(قديمة)', + 'comment_editor_explain' => 'هذه هي التعليقات المُضافة على هذه الصفحة. يُمكنك إضافة التعليقات وإدارتها عند عرض الصفحة المحفوظة.', // Revision 'revision_delete_confirm' => 'هل أنت متأكد من أنك تريد حذف هذه المراجعة؟', @@ -387,51 +426,51 @@ 'revision_cannot_delete_latest' => 'لايمكن حذف آخر مراجعة.', // Copy view - 'copy_consider' => 'Please consider the below when copying content.', - 'copy_consider_permissions' => 'Custom permission settings will not be copied.', - 'copy_consider_owner' => 'You will become the owner of all copied content.', - 'copy_consider_images' => 'Page image files will not be duplicated & the original images will retain their relation to the page they were originally uploaded to.', - 'copy_consider_attachments' => 'Page attachments will not be copied.', - 'copy_consider_access' => 'A change of location, owner or permissions may result in this content being accessible to those previously without access.', + 'copy_consider' => 'يرجى مراعاة ما يلي عند نسخ المحتوى.', + 'copy_consider_permissions' => 'لن يتم نسخ إعدادات الصلاحيات المخصصة.', + 'copy_consider_owner' => 'سوف تصبح مالكًا لجميع المحتوى المنسوخ.', + 'copy_consider_images' => 'لن يتم تكرار ملفات صور الصفحة وستحتفظ الصور الأصلية بعلاقتها بالصفحة التي تم تحميلها إليها في الأصل.', + 'copy_consider_attachments' => 'لن يتم نسخ مرفقات الصفحة.', + 'copy_consider_access' => 'قد يؤدي تغيير الموقع أو المالك أو الصلاحيات إلى إمكانية وصول الأشخاص الذين لم يتمكنوا من الوصول إلى هذا المحتوى سابقًا.', // Conversions - 'convert_to_shelf' => 'Convert to Shelf', - 'convert_to_shelf_contents_desc' => 'You can convert this book to a new shelf with the same contents. Chapters contained within this book will be converted to new books. If this book contains any pages, that are not in a chapter, this book will be renamed and contain such pages, and this book will become part of the new shelf.', - 'convert_to_shelf_permissions_desc' => 'Any permissions set on this book will be copied to the new shelf and to all new child books that don\'t have their own permissions enforced. Note that permissions on shelves do not auto-cascade to content within, as they do for books.', - 'convert_book' => 'Convert Book', - 'convert_book_confirm' => 'Are you sure you want to convert this book?', - 'convert_undo_warning' => 'This cannot be as easily undone.', - 'convert_to_book' => 'Convert to Book', - 'convert_to_book_desc' => 'You can convert this chapter to a new book with the same contents. Any permissions set on this chapter will be copied to the new book but any inherited permissions, from the parent book, will not be copied which could lead to a change of access control.', - 'convert_chapter' => 'Convert Chapter', - 'convert_chapter_confirm' => 'Are you sure you want to convert this chapter?', + 'convert_to_shelf' => 'تحويل إلى رف', + 'convert_to_shelf_contents_desc' => 'يمكنك تحويل هذا الكتاب إلى رف جديد بنفس المحتويات. سيتم تحويل الفصول الموجودة فيه إلى كتب جديدة. إذا احتوى هذا الكتاب على أي صفحات غير موجودة في أي فصل، فسيتم إعادة تسمية الكتاب وإضافة هذه الصفحات إليه، وسيصبح جزءًا من الرف الجديد.', + 'convert_to_shelf_permissions_desc' => 'سيتم نسخ أي صلاحيات مُحددة لهذا الكتاب إلى الرف الجديد وإلى جميع الكتب الفرعية الجديدة التي لم تُطبّق عليها صلاحيات خاصة بها. يُرجى العلم بأن الصلاحيات على الرفوف لا تنتقل تلقائيًا إلى المحتوى داخلها، كما هو الحال مع الكتب.', + 'convert_book' => 'تحويل الكتاب', + 'convert_book_confirm' => 'هل أنت متيقِّن أنك تريد تحويل هذا الكتاب؟', + 'convert_undo_warning' => 'لا يمكن التراجع عن هذا الأمر بسهولة.', + 'convert_to_book' => 'تحويله إلى كتاب', + 'convert_to_book_desc' => 'يمكنك تحويل هذا الفصل إلى كتاب جديد بنفس المحتوى. سيتم نسخ أي صلاحيات مُعيّنة لهذا الفصل إلى الكتاب الجديد، ولكن لن يتم نسخ أي صلاحيات موروثة من الكتاب الأصلي، مما قد يؤدي إلى تغيير في التحكم في الوصول.', + 'convert_chapter' => 'تحويل الفصل', + 'convert_chapter_confirm' => 'هل أنت متيقِّن أنك تريد تحويل هذا الفصل؟', // References - 'references' => 'References', - 'references_none' => 'There are no tracked references to this item.', - 'references_to_desc' => 'Listed below is all the known content in the system that links to this item.', + 'references' => 'مراجع', + 'references_none' => 'لا توجد مراجع متعقبة لهذا العنصر.', + 'references_to_desc' => 'تجد أدناه كل المحتوى المعروف في النظام المرتبط بهذا العنصر.', // Watch Options - 'watch' => 'Watch', - 'watch_title_default' => 'Default Preferences', - 'watch_desc_default' => 'Revert watching to just your default notification preferences.', - 'watch_title_ignore' => 'Ignore', - 'watch_desc_ignore' => 'Ignore all notifications, including those from user-level preferences.', - 'watch_title_new' => 'New Pages', - 'watch_desc_new' => 'Notify when any new page is created within this item.', - 'watch_title_updates' => 'All Page Updates', - 'watch_desc_updates' => 'Notify upon all new pages and page changes.', - 'watch_desc_updates_page' => 'Notify upon all page changes.', - 'watch_title_comments' => 'All Page Updates & Comments', - 'watch_desc_comments' => 'Notify upon all new pages, page changes and new comments.', - 'watch_desc_comments_page' => 'Notify upon page changes and new comments.', - 'watch_change_default' => 'Change default notification preferences', - 'watch_detail_ignore' => 'Ignoring notifications', - 'watch_detail_new' => 'Watching for new pages', - 'watch_detail_updates' => 'Watching new pages and updates', - 'watch_detail_comments' => 'Watching new pages, updates & comments', - 'watch_detail_parent_book' => 'Watching via parent book', - 'watch_detail_parent_book_ignore' => 'Ignoring via parent book', - 'watch_detail_parent_chapter' => 'Watching via parent chapter', - 'watch_detail_parent_chapter_ignore' => 'Ignoring via parent chapter', + 'watch' => 'شاهد', + 'watch_title_default' => 'التفضيلات الافتراضية', + 'watch_desc_default' => 'استعادة المشاهدة إلى تفضيلات الإشعارات الافتراضية فقط.', + 'watch_title_ignore' => 'تجاهل', + 'watch_desc_ignore' => 'تجاهل كافة الإشعارات، بما في ذلك تلك الواردة من تفضيلات مستوى المستخدم.', + 'watch_title_new' => 'صفحات جديدة', + 'watch_desc_new' => 'إعلام عند إنشاء أي صفحة جديدة ضمن هذا العنصر.', + 'watch_title_updates' => 'جميع تحديثات الصفحة', + 'watch_desc_updates' => 'إشعار بجميع الصفحات الجديدة والتغييرات في الصفحات.', + 'watch_desc_updates_page' => 'إشعار عند حدوث أي تغييرات في الصفحة.', + 'watch_title_comments' => 'جميع تحديثات الصفحة والتعليقات', + 'watch_desc_comments' => 'إشعار بجميع الصفحات الجديدة، وتغييرات الصفحات والتعليقات الجديدة.', + 'watch_desc_comments_page' => 'إشعار عند حدوث تغييرات في الصفحة أو تعليقات جديدة.', + 'watch_change_default' => 'تغيير تفضيلات الإشعارات الافتراضية', + 'watch_detail_ignore' => 'تجاهل الإشعارات', + 'watch_detail_new' => 'ترقب الصفحات الجديدة', + 'watch_detail_updates' => 'مشاهدة الصفحات الجديدة والتحديثات', + 'watch_detail_comments' => 'مشاهدة الصفحات الجديدة والتحديثات والتعليقات', + 'watch_detail_parent_book' => 'المشاهدة عبر الكتاب الرئيس', + 'watch_detail_parent_book_ignore' => 'التجاهل عبر الكتاب الرئيس', + 'watch_detail_parent_chapter' => 'المشاهدة عبر الفصل الرئيس', + 'watch_detail_parent_chapter_ignore' => 'التجاهل عبر الفصل الرئيس', ]; diff --git a/lang/ar/errors.php b/lang/ar/errors.php index 70bcf2137d0..4c6325cb3a1 100644 --- a/lang/ar/errors.php +++ b/lang/ar/errors.php @@ -10,12 +10,13 @@ // Auth 'error_user_exists_different_creds' => 'يوجد مستخدم ببيانات مختلفة مسجل بالنظام للبريد الإلكتروني :email.', + 'auth_pre_register_theme_prevention' => 'لم يتمكن حساب المستخدم من التسجيل للحصول على التفاصيل المقدمة', 'email_already_confirmed' => 'تم تأكيد البريد الإلكتروني من قبل, الرجاء محاولة تسجيل الدخول.', 'email_confirmation_invalid' => 'رابط التأكيد غير صحيح أو قد تم استخدامه من قبل, الرجاء محاولة التسجيل من جديد.', 'email_confirmation_expired' => 'صلاحية رابط التأكيد انتهت, تم إرسال رسالة تأكيد جديدة لعنوان البريد الإلكتروني.', 'email_confirmation_awaiting' => 'عنوان البريد الإلكتروني للحساب قيد الاستخدام يحتاج إلى تأكيد', 'ldap_fail_anonymous' => 'فشل الوصول إلى LDAP باستخدام الربط المجهول', - 'ldap_fail_authed' => 'فشل الوصول إلى LDAP باستخدام dn و password المعطاة', + 'ldap_fail_authed' => 'فشل الوصول إلى LDAP باستخدام dn و كلمة السر المعطاة', 'ldap_extension_not_installed' => 'لم يتم تثبيت إضافة LDAP PHP', 'ldap_cannot_connect' => 'لا يمكن الاتصال بخادم ldap, فشل الاتصال المبدئي', 'saml_already_logged_in' => 'تم تسجيل الدخول بالفعل', @@ -23,7 +24,6 @@ 'saml_invalid_response_id' => 'لم يتم التعرف على الطلب من نظام التوثيق الخارجي من خلال عملية تبدأ بهذا التطبيق. العودة بعد تسجيل الدخول يمكن أن يسبب هذه المشكلة.', 'saml_fail_authed' => 'تسجيل الدخول باستخدام :system فشل، النظام لم يوفر التفويض الناجح', 'oidc_already_logged_in' => 'تم تسجيل الدخول مسبقاً', - 'oidc_user_not_registered' => 'المستخدم :name غير مسجل و لايمكن التسجيل بشكل اتوماتيكي', 'oidc_no_email_address' => 'تعذر العثور على عنوان بريد إلكتروني، لهذا المستخدم، في البيانات المقدمة من نظام المصادقة الخارجي', 'oidc_fail_authed' => 'تسجيل الدخول باستخدام :system فشل، النظام لم يوفر التفويض الناجح', 'social_no_action_defined' => 'لم يتم تعريف أي إجراء', @@ -37,6 +37,7 @@ 'social_driver_not_found' => 'لم يتم العثور على السوشيال درايفر "Social driver"', 'social_driver_not_configured' => 'لم يتم تهيئة إعدادات حسابك الاجتماعي بشكل صحيح.', 'invite_token_expired' => 'انتهت صلاحية رابط هذه الدعوة. يمكنك بدلاً من ذلك محاولة إعادة تعيين كلمة مرور حسابك.', + 'login_user_not_found' => 'لم يتم العثور على مستخدم لهذا الإجراء.', // System 'path_not_writable' => 'لا يمكن الرفع إلى مسار :filePath. الرجاء التأكد من قابلية الكتابة إلى الخادم.', @@ -77,6 +78,7 @@ // Users 'users_cannot_delete_only_admin' => 'لا يمكن حذف المشرف الوحيد', 'users_cannot_delete_guest' => 'لا يمكن حذف المستخدم الضيف', + 'users_could_not_send_invite' => 'لم يتم إنشاء المستخدم بسبب فشل إرسال بريد الدعوة', // Roles 'role_cannot_be_edited' => 'لا يمكن تعديل هذا الدور', @@ -103,6 +105,18 @@ 'app_down' => ':appName لا يعمل حالياً', 'back_soon' => 'سيعود للعمل قريباً.', + // Import + 'import_zip_cant_read' => 'لم أتمكن من قراءة المِلَفّ المضغوط -ZIP-.', + 'import_zip_cant_decode_data' => 'لم نتمكن من العثور على محتوى المِلَفّ المضغوط data.json وفك تشفيره.', + 'import_zip_no_data' => 'لا تتضمن بيانات المِلَفّ المضغوط أي محتوى متوقع للكتاب أو الفصل أو الصفحة.', + 'import_validation_failed' => 'فشل التحقق من صحة استيراد المِلَفّ المضغوط بسبب الأخطاء التالية:', + 'import_zip_failed_notification' => 'فشل استيراد المِلَفّ المضغوط.', + 'import_perms_books' => 'أنت تفتقر إلى الصلاحيات المطلوبة لإنشاء الكتب.', + 'import_perms_chapters' => 'أنت تفتقر إلى الصلاحيات المطلوبة لإنشاء الفصول.', + 'import_perms_pages' => 'أنت تفتقر إلى الصلاحيات المطلوبة لإنشاء الصفحات.', + 'import_perms_images' => 'أنت تفتقر إلى الصلاحيات المطلوبة لإنشاء الصور.', + 'import_perms_attachments' => 'أنت تفتقر إلى الصَّلاحِيَة المطلوب لإنشاء المرفقات.', + // API errors 'api_no_authorization_found' => 'لم يتم العثور على رمز ترخيص مميز في الطلب', 'api_bad_authorization_format' => 'تم العثور على رمز ترخيص مميز في الطلب ولكن يبدو أن التنسيق غير صحيح', diff --git a/lang/ar/notifications.php b/lang/ar/notifications.php index 1afd23f1dc4..30a49a631cf 100644 --- a/lang/ar/notifications.php +++ b/lang/ar/notifications.php @@ -4,24 +4,24 @@ */ return [ - 'new_comment_subject' => 'New comment on page: :pageName', - 'new_comment_intro' => 'A user has commented on a page in :appName:', - 'new_page_subject' => 'New page: :pageName', - 'new_page_intro' => 'A new page has been created in :appName:', - 'updated_page_subject' => 'Updated page: :pageName', - 'updated_page_intro' => 'A page has been updated in :appName:', - 'updated_page_debounce' => 'To prevent a mass of notifications, for a while you won\'t be sent notifications for further edits to this page by the same editor.', + 'new_comment_subject' => 'تعليق جديد على الصفحة: :pageName', + 'new_comment_intro' => 'قام أحد المستخدمين بالتعليق على صفحة في :appName:', + 'new_page_subject' => 'صفحة جديدة: :pageName', + 'new_page_intro' => 'تم إنشاء صفحة جديدة في :appName:', + 'updated_page_subject' => 'تم تحديث الصفحة: :pageName', + 'updated_page_intro' => 'تم تحديث الصفحة في :appName:', + 'updated_page_debounce' => 'لمنع تلقي عدد كبير من الإشعارات، لن يتم إرسال إشعارات إليك لفترة من الوقت لإجراء المزيد من التعديلات على هذه الصفحة بواسطة نفس المحرر.', - 'detail_page_name' => 'Page Name:', - 'detail_page_path' => 'Page Path:', - 'detail_commenter' => 'Commenter:', - 'detail_comment' => 'Comment:', - 'detail_created_by' => 'Created By:', - 'detail_updated_by' => 'Updated By:', + 'detail_page_name' => 'اسم الصفحة:', + 'detail_page_path' => 'مسار الصفحة:', + 'detail_commenter' => 'المُعَلِق:', + 'detail_comment' => 'التعليق:', + 'detail_created_by' => 'أنشئ من طرف:', + 'detail_updated_by' => 'تم التحديث بواسطة:', - 'action_view_comment' => 'View Comment', - 'action_view_page' => 'View Page', + 'action_view_comment' => 'عرض التعليق', + 'action_view_page' => 'عرض الصفحة', - 'footer_reason' => 'This notification was sent to you because :link cover this type of activity for this item.', - 'footer_reason_link' => 'your notification preferences', + 'footer_reason' => 'لقد تم إرسال هذا الإشعار إليك لأن :link يغطي هذا النوع من النشاط لهذا العنصر.', + 'footer_reason_link' => 'إعدادات الإشعارات الخاصة بك', ]; diff --git a/lang/ar/passwords.php b/lang/ar/passwords.php index 4ab447d5d56..1e8a10ccc89 100644 --- a/lang/ar/passwords.php +++ b/lang/ar/passwords.php @@ -6,10 +6,10 @@ */ return [ - 'password' => 'يجب أن تتكون كلمة المرور من ستة أحرف على الأقل وأن تطابق التأكيد.', + 'password' => 'يجب أن تتكون كلمة السر من ستة أحرف على الأقل وأن تطابق التأكيد.', 'user' => "لم يتم العثور على مستخدم بعنوان البريد الإلكتروني المعطى.", - 'token' => 'رمز إعادة تعيين كلمة المرور غير صالح لعنوان هذا البريد الإلكتروني.', - 'sent' => 'تم إرسال رابط تجديد كلمة المرور إلى بريدكم الإلكتروني!', - 'reset' => 'تم تجديد كلمة المرور الخاصة بكم!', + 'token' => 'رمز إعادة تعيين كلمة السر غير صالح لعنوان هذا البريد الإلكتروني.', + 'sent' => 'تم إرسال رابط تجديد كلمة السر إلى بريدكم الإلكتروني!', + 'reset' => 'تم تجديد كلمة السر الخاصة بكم!', ]; diff --git a/lang/ar/preferences.php b/lang/ar/preferences.php index 2b88f9671ff..9158b4b375b 100644 --- a/lang/ar/preferences.php +++ b/lang/ar/preferences.php @@ -5,47 +5,47 @@ */ return [ - 'my_account' => 'My Account', + 'my_account' => 'حسابي', - 'shortcuts' => 'Shortcuts', - 'shortcuts_interface' => 'UI Shortcut Preferences', - 'shortcuts_toggle_desc' => 'Here you can enable or disable keyboard system interface shortcuts, used for navigation and actions.', - 'shortcuts_customize_desc' => 'You can customize each of the shortcuts below. Just press your desired key combination after selecting the input for a shortcut.', - 'shortcuts_toggle_label' => 'Keyboard shortcuts enabled', - 'shortcuts_section_navigation' => 'Navigation', - 'shortcuts_section_actions' => 'Common Actions', - 'shortcuts_save' => 'Save Shortcuts', - 'shortcuts_overlay_desc' => 'Note: When shortcuts are enabled a helper overlay is available via pressing "?" which will highlight the available shortcuts for actions currently visible on the screen.', - 'shortcuts_update_success' => 'Shortcut preferences have been updated!', - 'shortcuts_overview_desc' => 'Manage keyboard shortcuts you can use to navigate the system user interface.', + 'shortcuts' => 'الاختصارات', + 'shortcuts_interface' => 'خيارات اختصار واجهة المستخدم', + 'shortcuts_toggle_desc' => 'هنا يمكنك تمكين أو تعطيل اختصارات واجهة نظام لوحة المفاتيح، المستخدمة للتنقل والإجراءات.', + 'shortcuts_customize_desc' => 'يمكنك تخصيص كل اختصار من الاختصارات أدناه. ما عليك سوى الضغط على تركيبة المفاتيح المطلوبة بعد تحديد مدخل الاختصار.', + 'shortcuts_toggle_label' => 'تم تمكين اختصارات لوحة المفاتيح', + 'shortcuts_section_navigation' => 'التنقل', + 'shortcuts_section_actions' => 'الإجراءات المشتركة', + 'shortcuts_save' => 'حفظ الاختصارات', + 'shortcuts_overlay_desc' => 'ملاحظة: عندما يتم تمكين الاختصارات، تتوفر تراكب المساعد عن طريق الضغط على "؟" الذي سيسلط الضوء على الاختصارات المتاحة للإجراءات المرئية حاليا على الشاشة.', + 'shortcuts_update_success' => 'تم تحديث خيارات الاختصار!', + 'shortcuts_overview_desc' => 'إدارة اختصارات لوحة المفاتيح التي يمكنك استخدامها للتنقل في واجهة مستخدم النظام.', - 'notifications' => 'Notification Preferences', - 'notifications_desc' => 'Control the email notifications you receive when certain activity is performed within the system.', - 'notifications_opt_own_page_changes' => 'Notify upon changes to pages I own', - 'notifications_opt_own_page_comments' => 'Notify upon comments on pages I own', - 'notifications_opt_comment_replies' => 'Notify upon replies to my comments', - 'notifications_save' => 'Save Preferences', - 'notifications_update_success' => 'Notification preferences have been updated!', - 'notifications_watched' => 'Watched & Ignored Items', - 'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.', + 'notifications' => 'إعدادات الإشعارات', + 'notifications_desc' => 'التحكم في إشعارات البريد الإلكتروني الذي تتلقاها عند إجراء نشاط معين داخل النظام.', + 'notifications_opt_own_page_changes' => 'إشعاري عند حدوث تغييرات في الصفحات التي أملكها', + 'notifications_opt_own_page_comments' => 'إشعاري بشأن التعليقات على الصفحات التي أملكها', + 'notifications_opt_comment_replies' => 'إشعاري عند الردود على تعليقاتي', + 'notifications_save' => 'حفظ اﻹعدادات', + 'notifications_update_success' => 'تم تحديث إعدادات الإشعارات!', + 'notifications_watched' => 'العناصر التي تمت مشاهدتها وتجاهلها', + 'notifications_watched_desc' => 'فيما يلي العناصر التي طُبِّقت عليها إعدادات ساعة مخصصة. لتحديث إعداداتك، استعرض العنصر ثم ابحث عن خيارات الساعة في الشريط الجانبي.', - 'auth' => 'Access & Security', - 'auth_change_password' => 'Change Password', - 'auth_change_password_desc' => 'Change the password you use to log-in to the application. This must be at least 8 characters long.', - 'auth_change_password_success' => 'Password has been updated!', + 'auth' => 'الوصول و الأمان', + 'auth_change_password' => 'تغيير كلمة السر', + 'auth_change_password_desc' => 'غيّر كلمة السر التي تستخدمها لتسجيل الدخول إلى التطبيق. يجب ألا تقل عن 8 أحرف.', + 'auth_change_password_success' => 'تم تحديث كلمة السر!', - 'profile' => 'Profile Details', - 'profile_desc' => 'Manage the details of your account which represents you to other users, in addition to details that are used for communication and system personalisation.', - 'profile_view_public' => 'View Public Profile', - 'profile_name_desc' => 'Configure your display name which will be visible to other users in the system through the activity you perform, and content you own.', - 'profile_email_desc' => 'This email will be used for notifications and, depending on active system authentication, system access.', - 'profile_email_no_permission' => 'Unfortunately you don\'t have permission to change your email address. If you want to change this, you\'d need to ask an administrator to change this for you.', - 'profile_avatar_desc' => 'Select an image which will be used to represent yourself to others in the system. Ideally this image should be square and about 256px in width and height.', - 'profile_admin_options' => 'Administrator Options', - 'profile_admin_options_desc' => 'Additional administrator-level options, like those to manage role assignments, can be found for your user account in the "Settings > Users" area of the application.', + 'profile' => 'تفاصيل المِلَفّ الشخصي', + 'profile_desc' => 'إدارة تفاصيل حسابك الذي يمثلك أمام المستخدمين الآخرين، بالإضافة إلى التفاصيل المستخدمة للتواصل وتخصيص النظام.', + 'profile_view_public' => 'عرض المِلَفّ الشخصي العام', + 'profile_name_desc' => 'إعداد اسم العرض الخاص بك الذي سيكون مرئيًا للمستخدمين الآخرين في النظام من خلال النشاط الذي تقوم به والمحتوى الذي تملكه.', + 'profile_email_desc' => 'سيتم استخدام هذا البريد الإلكتروني للإشعارات، وبناءً على مصادقة النظام النشط، سيتم استخدام الوصول إلى النظام.', + 'profile_email_no_permission' => 'للأسف، ليس لديك إذن لتغيير عنوان بريدك الإلكتروني. إذا كنت ترغب في تغييره، فعليك طلب ذلك من أحد المسؤولين.', + 'profile_avatar_desc' => 'اختر صورةً تُمثّلك أمام الآخرين في النظام. يُفضّل أن تكون الصورة مربعةً، وعرضها وارتفاعها حوالي ٢٥٦ بكسل.', + 'profile_admin_options' => 'خيارات المسؤول', + 'profile_admin_options_desc' => 'يمكنك العثور على خيارات إضافية على مستوى المسؤول، مثل تلك الخاصة بإدارة تعيينات الأدوار، لحساب المستخدم الخاص بك في منطقة "الإعدادات > المستخدمون" في التطبيق.', - 'delete_account' => 'Delete Account', - 'delete_my_account' => 'Delete My Account', - 'delete_my_account_desc' => 'This will fully delete your user account from the system. You will not be able to recover this account or revert this action. Content you\'ve created, such as created pages and uploaded images, will remain.', - 'delete_my_account_warning' => 'Are you sure you want to delete your account?', + 'delete_account' => 'حذف الحساب', + 'delete_my_account' => 'حذف حسابي', + 'delete_my_account_desc' => 'سيؤدي هذا إلى حذف حساب المستخدم الخاص بك بالكامل من النظام. لن تتمكن من استعادة هذا الحساب أو التراجع عن هذا الإجراء. سيبقى المحتوى الذي أنشأته، مثل الصفحات التي أنشأتها والصور التي رفعتها، كما هي.', + 'delete_my_account_warning' => 'هل أنت متأكد أنك تريد حذف حسابك؟', ]; diff --git a/lang/ar/settings.php b/lang/ar/settings.php index ca0f2fdb3d0..9006ac2277f 100644 --- a/lang/ar/settings.php +++ b/lang/ar/settings.php @@ -48,12 +48,12 @@ 'app_disable_comments_desc' => 'تعطيل التعليقات على جميع الصفحات داخل التطبيق. التعليقات الموجودة من الأصل لن تكون ظاهرة.', // Color settings - 'color_scheme' => 'Application Color Scheme', - 'color_scheme_desc' => 'Set the colors to use in the application user interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.', - 'ui_colors_desc' => 'Set the application primary color and default link color. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the application interface.', - 'app_color' => 'Primary Color', - 'link_color' => 'Default Link Color', - 'content_colors_desc' => 'Set colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.', + 'color_scheme' => 'مخطط ألوان التطبيق', + 'color_scheme_desc' => 'حدّد الألوان المستخدمة في واجهة مستخدم التطبيق. يمكن ضبط الألوان بشكل منفصل للوضعين الداكن والفاتح لتناسب المظهر بشكل أفضل ولضمان وضوح النص.', + 'ui_colors_desc' => 'عيّن اللون الأساسي للتطبيق ولون الرابط الافتراضي. يُستخدم اللون الأساسي بشكل رئيس في شعار الصفحة الرئيسة والأزرار وزخارف الواجهة. أما اللون الافتراضي للرابط، فيُستخدم للروابط والإجراءات النصية، سواءً داخل المحتوى المكتوب أو في واجهة التطبيق.', + 'app_color' => 'اللون الأساسي', + 'link_color' => 'لون الرابط الافتراضي', + 'content_colors_desc' => 'حدّد ألوان جميع عناصر هيكل تنظيم الصفحة. يُنصح باختيار ألوان بنفس سطوع الألوان الافتراضية لسهولة القراءة.', 'bookshelf_color' => 'لون الرف', 'book_color' => 'لون الكتاب', 'chapter_color' => 'لون الفصل', @@ -74,11 +74,41 @@ 'reg_confirm_restrict_domain_desc' => 'أدخل قائمة مفصولة بفواصل لنطاقات البريد الإلكتروني التي ترغب في تقييد التسجيل إليها. سيتم إرسال بريد إلكتروني للمستخدمين لتأكيد عنوانهم قبل السماح لهم بالتفاعل مع التطبيق.
لاحظ أن المستخدمين سيكونون قادرين على تغيير عناوين البريد الإلكتروني الخاصة بهم بعد التسجيل بنجاح.', 'reg_confirm_restrict_domain_placeholder' => 'لم يتم اختيار أي قيود', + // Sorting Settings + 'sorting' => 'طريقة الترتيب', + 'sorting_book_default' => 'ترتيب الكتاب الافتراضي', + 'sorting_book_default_desc' => 'حدد قاعدة الترتيب الافتراضية لتطبيقها على الكتب الجديدة. لن يؤثر هذا على الكتب الحالية، ويمكن تجاوزه لكل كتاب على حدة.', + 'sorting_rules' => 'قواعد الترتيب', + 'sorting_rules_desc' => 'هذه هي عمليات الترتيب المحددة مسبقًا الذي يمكن تطبيقها على المحتوى الموجود في النظام.', + 'sort_rule_assigned_to_x_books' => 'مُعيَّن إلى :count كتاب|مُعيَّن إلى :count كتاب', + 'sort_rule_create' => 'إنشاء قاعدة الترتيب', + 'sort_rule_edit' => 'تعديل قاعدة الترتيب', + 'sort_rule_delete' => 'حذف قاعدة الترتيب', + 'sort_rule_delete_desc' => 'أزل قاعدة الترتيب هذه من النظام. الكتب التي تستخدم هذا الفرز ستعود إلى الفرز اليدوي.', + 'sort_rule_delete_warn_books' => 'تُستخدم قاعدة الترتيب هذه حاليًا على :count كتاب/كتب. متيقن من رغبتك في حذف هذا؟', + 'sort_rule_delete_warn_default' => 'تُستخدم قاعدة الترتيب هذه حاليًا كإعداد افتراضي للكتب. متيقن من رغبتك في حذفها؟', + 'sort_rule_details' => 'تفاصيل قاعدة الترتيب', + 'sort_rule_details_desc' => 'تعيين اسم لقاعدة الترتيب هذه، التي ستظهر في القوائم عندما يقوم المستخدمون باختيار نوع ما.', + 'sort_rule_operations' => 'عمليات الترتيب', + 'sort_rule_operations_desc' => 'جهّز إجراءات الترتيب المطلوب تنفيذها بنقلها من قائمة العمليات المتاحة. عند الاستخدام، سيتم تطبيق العمليات بالترتيب من الأعلى إلى الأسفل. أي تغييرات تُجرى هنا ستُطبّق على جميع الكتب المُخصّصة عند الحفظ.', + 'sort_rule_available_operations' => 'العمليات المتاحة', + 'sort_rule_available_operations_empty' => 'لا توجد عمليات متبقية', + 'sort_rule_configured_operations' => 'العمليات المُهيأة', + 'sort_rule_configured_operations_empty' => 'اسحب/أضف العمليات من قائمة "العمليات المتاحة"', + 'sort_rule_op_asc' => '(تصاعدي)', + 'sort_rule_op_desc' => '(تنازلي)', + 'sort_rule_op_name' => 'الاسم - أبجديًا', + 'sort_rule_op_name_numeric' => 'الاسم - رقمي', + 'sort_rule_op_created_date' => 'تاريخ الإنشاء', + 'sort_rule_op_updated_date' => 'تاريخ التحديث', + 'sort_rule_op_chapters_first' => 'الفصول الأولى', + 'sort_rule_op_chapters_last' => 'الفصول الأخيرة', + // Maintenance settings 'maint' => 'الصيانة', 'maint_image_cleanup' => 'تنظيف الصور', 'maint_image_cleanup_desc' => 'مسح الصفحة ومراجعة المحتوى للتحقق من أي الصور والرسوم المستخدمة حاليًا وأي الصور زائدة عن الحاجة. تأكد من إنشاء قاعدة بيانات كاملة و نسخة احتياطية للصور قبل تشغيل هذا.', - 'maint_delete_images_only_in_revisions' => 'Also delete images that only exist in old page revisions', + 'maint_delete_images_only_in_revisions' => 'قم أيضًا بحذف الصور الموجودة فقط في مراجعات الصفحة القديمة', 'maint_image_cleanup_run' => 'بدء التنظيف', 'maint_image_cleanup_warning' => 'يوجد عدد :count من الصور المحتمل عدم استخدامها. تأكيد حذف الصور؟', 'maint_image_cleanup_success' => 'تم إيجاد وحذف عدد :count من الصور المحتمل عدم استخدامها!', @@ -92,16 +122,16 @@ 'maint_send_test_email_mail_text' => 'تهانينا! كما تلقيت إشعار هذا البريد الإلكتروني، يبدو أن إعدادات البريد الإلكتروني الخاص بك قد تم تكوينها بشكل صحيح.', 'maint_recycle_bin_desc' => 'تُرسل الأرفف والكتب والفصول والصفحات المحذوفة إلى سلة المحذوفات حتى يمكن استعادتها أو حذفها نهائيًا. قد يتم إزالة العناصر الأقدم في سلة المحذوفات تلقائيًا بعد فترة اعتمادًا على تكوين النظام.', 'maint_recycle_bin_open' => 'افتح سلة المحذوفات', - 'maint_regen_references' => 'Regenerate References', - 'maint_regen_references_desc' => 'This action will rebuild the cross-item reference index within the database. This is usually handled automatically but this action can be useful to index old content or content added via unofficial methods.', - 'maint_regen_references_success' => 'Reference index has been regenerated!', - 'maint_timeout_command_note' => 'Note: This action can take time to run, which can lead to timeout issues in some web environments. As an alternative, this action be performed using a terminal command.', + 'maint_regen_references' => 'إعادة إنشاء المراجع', + 'maint_regen_references_desc' => 'سيعيد هذا الإجراء بناء فِهْرِس المراجع بين العناصر داخل قاعدة البيانات. عادةً ما يتم ذلك تلقائيًا، ولكنه قد يكون مفيدًا لفهرسة المحتوى القديم أو المحتوى المُضاف بطرق غير رسمية.', + 'maint_regen_references_success' => 'لقد تم تجديد فِهْرِس المرجع!', + 'maint_timeout_command_note' => 'ملاحظة: قد يستغرق تنفيذ هذا الإجراء بعض الوقت، مما قد يؤدي إلى مشاكل في مهلة التنفيذ في بعض بيئات الويب. كبديل، يمكن تنفيذ هذا الإجراء باستخدام سطر الأوامر.', // Recycle Bin 'recycle_bin' => 'سلة المحذوفات', 'recycle_bin_desc' => 'هنا يمكنك استعادة العناصر التي تم حذفها أو اختيار إزالتها نهائيا من النظام. هذه القائمة غير مصفاة خلافاً لقوائم الأنشطة المماثلة في النظام حيث يتم تطبيق عوامل تصفية الأذونات.', 'recycle_bin_deleted_item' => 'عنصر محذوف', - 'recycle_bin_deleted_parent' => 'Parent', + 'recycle_bin_deleted_parent' => 'اﻷب', 'recycle_bin_deleted_by' => 'حُذف بواسطة', 'recycle_bin_deleted_at' => 'وقت الحذف', 'recycle_bin_permanently_delete' => 'حُذف نهائيًا', @@ -109,12 +139,12 @@ 'recycle_bin_contents_empty' => 'سلة المحذوفات فارغة حاليًا', 'recycle_bin_empty' => 'إفراغ سلة المحذوفات', 'recycle_bin_empty_confirm' => 'سيؤدي هذا إلى إتلاف جميع العناصر الموجودة في سلة المحذوفات بشكل دائم بما في ذلك المحتوى الموجود داخل كل عنصر. هل أنت متأكد من أنك تريد إفراغ سلة المحذوفات؟', - 'recycle_bin_destroy_confirm' => 'سيؤدي هذا الإجراء إلى حذف هذا العنصر نهائيًا ، إلى جانب أي عناصر فرعية مدرجة أدناه ، من النظام ولن تتمكن من استعادة هذا المحتوى. هل أنت متأكد من أنك تريد حذف هذا العنصر نهائيًا؟', + 'recycle_bin_destroy_confirm' => 'سيؤدي هذا الإجراء إلى حذف هذا العنصر نهائيًا من النظام، بالإضافة إلى أي عناصر فرعية مدرجة أدناه، ولن تتمكن من استعادة هذا المحتوى. هل أنت متيقِّن من رغبتك في حذف هذا العنصر نهائيًا؟', 'recycle_bin_destroy_list' => 'العناصر المراد تدميرها', 'recycle_bin_restore_list' => 'العناصر المراد استرجاعها', 'recycle_bin_restore_confirm' => 'سيعيد هذا الإجراء العنصر المحذوف ، بما في ذلك أي عناصر فرعية ، إلى موقعه الأصلي. إذا تم حذف الموقع الأصلي منذ ذلك الحين ، وهو الآن في سلة المحذوفات ، فسيلزم أيضًا استعادة العنصر الأصلي.', 'recycle_bin_restore_deleted_parent' => 'تم حذف أصل هذا العنصر أيضًا. سيبقى حذفه حتى يتم استعادة ذلك الأصل أيضًا.', - 'recycle_bin_restore_parent' => 'Restore Parent', + 'recycle_bin_restore_parent' => 'استعادة اﻷب', 'recycle_bin_destroy_notification' => 'المحذوف: قُم بعد إجمالي العناصر من سلة المحذوفات.', 'recycle_bin_restore_notification' => 'المرتجع: قُم بعد إجمالي العناصر من سلة المحذوفات.', @@ -128,7 +158,7 @@ 'audit_table_user' => 'المستخدم', 'audit_table_event' => 'الحدث', 'audit_table_related' => 'العنصر أو التفاصيل ذات الصلة', - 'audit_table_ip' => 'IP Address', + 'audit_table_ip' => 'عنوان عُرف اﻹنترنت -IP-', 'audit_table_date' => 'تاريخ النشاط', 'audit_date_from' => 'نطاق التاريخ من', 'audit_date_to' => 'نطاق التاريخ إلى', @@ -136,11 +166,11 @@ // Role Settings 'roles' => 'الأدوار', 'role_user_roles' => 'أدوار المستخدمين', - 'roles_index_desc' => 'Roles are used to group users & provide system permission to their members. When a user is a member of multiple roles the privileges granted will stack and the user will inherit all abilities.', - 'roles_x_users_assigned' => ':count user assigned|:count users assigned', - 'roles_x_permissions_provided' => ':count permission|:count permissions', - 'roles_assigned_users' => 'Assigned Users', - 'roles_permissions_provided' => 'Provided Permissions', + 'roles_index_desc' => 'تُستخدم الأدوار لتجميع المستخدمين ومنح أذونات النظام لأعضائها. عندما يكون المستخدم عضوًا في أدوار متعددة، تتراكم الصلاحيات الممنوحة، ويرث المستخدم جميع القدرات.', + 'roles_x_users_assigned' => ':count مستخدم معين|:count مستخدمين معينين', + 'roles_x_permissions_provided' => ':count إذن |:count إذونات', + 'roles_assigned_users' => 'المستخدمون المعينون', + 'roles_permissions_provided' => 'الصلاحيات المقدمة', 'role_create' => 'إنشاء دور جديد', 'role_delete' => 'حذف الدور', 'role_delete_confirm' => 'سيتم حذف الدور المسمى \':roleName\'.', @@ -151,7 +181,7 @@ 'role_details' => 'تفاصيل الدور', 'role_name' => 'اسم الدور', 'role_desc' => 'وصف مختصر للدور', - 'role_mfa_enforced' => 'Requires Multi-Factor Authentication', + 'role_mfa_enforced' => 'يتطلب مصادقة متعددة العوامل', 'role_external_auth_id' => 'ربط الحساب بمواقع التواصل', 'role_system' => 'أذونات النظام', 'role_manage_users' => 'إدارة المستخدمين', @@ -161,14 +191,15 @@ 'role_manage_page_templates' => 'إدارة قوالب الصفحة', 'role_access_api' => 'الوصول إلى واجهة برمجة تطبيقات النظام API', 'role_manage_settings' => 'إدارة إعدادات التطبيق', - 'role_export_content' => 'Export content', - 'role_editor_change' => 'Change page editor', - 'role_notifications' => 'Receive & manage notifications', + 'role_export_content' => 'تصدير المحتوى', + 'role_import_content' => 'استيراد المحتوى', + 'role_editor_change' => 'تغيير محرر الصفحة', + 'role_notifications' => 'تلقي الإشعارات وإدارتها', 'role_asset' => 'أذونات الأصول', 'roles_system_warning' => 'اعلم أن الوصول إلى أي من الأذونات الثلاثة المذكورة أعلاه يمكن أن يسمح للمستخدم بتغيير امتيازاته الخاصة أو امتيازات الآخرين في النظام. قم بتعيين الأدوار مع هذه الأذونات فقط للمستخدمين الموثوق بهم.', 'role_asset_desc' => 'تتحكم هذه الأذونات في الوصول الافتراضي إلى الأصول داخل النظام. ستتجاوز الأذونات الخاصة بالكتب والفصول والصفحات هذه الأذونات.', 'role_asset_admins' => 'يُمنح المسؤولين حق الوصول تلقائيًا إلى جميع المحتويات ولكن هذه الخيارات قد تعرض خيارات واجهة المستخدم أو تخفيها.', - 'role_asset_image_view_note' => 'This relates to visibility within the image manager. Actual access of uploaded image files will be dependant upon system image storage option.', + 'role_asset_image_view_note' => 'يتعلق هذا بالرؤية داخل مدير الصور. يعتمد الوصول الفعلي لملفات الصور المُحمّلة على خِيار تخزين الصور في النظام.', 'role_all' => 'الكل', 'role_own' => 'ما يخص', 'role_controlled_by_asset' => 'يتحكم فيها الأصول التي يتم رفعها إلى', @@ -178,7 +209,7 @@ // Users 'users' => 'المستخدمون', - 'users_index_desc' => 'Create & manage individual user accounts within the system. User accounts are used for login and attribution of content & activity. Access permissions are primarily role-based but user content ownership, among other factors, may also affect permissions & access.', + 'users_index_desc' => 'إنشاء وإدارة حسابات المستخدمين الفردية داخل النظام. يتم استخدام حسابات المستخدم لتسجيل الدخول وإسناد المحتوى والنشاط. صلاحيات الوصول هي أساسا قائمة على الأدوار ولكن ملكية محتوى المستخدم، من بين عوامل أخرى، قد تؤثر أيضا على صلاحيات والوصول إليها.', 'user_profile' => 'ملف المستخدم', 'users_add_new' => 'إضافة مستخدم جديد', 'users_search' => 'بحث عن مستخدم', @@ -189,20 +220,20 @@ 'users_role' => 'أدوار المستخدمين', 'users_role_desc' => 'حدد الأدوار التي سيتم تعيين هذا المستخدم لها. إذا تم تعيين مستخدم لأدوار متعددة ، فسيتم تكديس الأذونات من هذه الأدوار وسيتلقى كل قدرات الأدوار المعينة.', 'users_password' => 'كلمة مرور المستخدم', - 'users_password_desc' => 'Set a password used to log-in to the application. This must be at least 8 characters long.', - 'users_send_invite_text' => 'يمكنك اختيار إرسال دعوة بالبريد الإلكتروني إلى هذا المستخدم مما يسمح له بتعيين كلمة المرور الخاصة به أو يمكنك تعيين كلمة المرور الخاصة به بنفسك.', + 'users_password_desc' => 'عيّن كلمة سر لتسجيل الدخول إلى التطبيق. يجب ألا تقل عن 8 أحرف.', + 'users_send_invite_text' => 'يمكنك اختيار إرسال دعوة بالبريد الإلكتروني إلى هذا المستخدم مما يسمح له بتعيين كلمة السر الخاصة به أو يمكنك تعيين كلمة المرور الخاصة به بنفسك.', 'users_send_invite_option' => 'أرسل بريدًا إلكترونيًا لدعوة المستخدم', 'users_external_auth_id' => 'ربط الحساب بمواقع التواصل', - 'users_external_auth_id_desc' => 'When an external authentication system is in use (such as SAML2, OIDC or LDAP) this is the ID which links this BookStack user to the authentication system account. You can ignore this field if using the default email-based authentication.', - 'users_password_warning' => 'Only fill the below if you would like to change the password for this user.', + 'users_external_auth_id_desc' => 'عند استخدام نظام مصادقة خارجي (مثل SAML2 أو OIDC أو LDAP)، يكون هذا هو المعرف الذي يربط مستخدم بوكستاك -BookStack- بحساب نظام المصادقة. يمكنك تجاهل هذا الحقل عند استخدام المصادقة الافتراضية عبر البريد الإلكتروني.', + 'users_password_warning' => 'قم بملء الحقل أدناه فقط إذا كنت ترغب في تغيير كلمة السر لهذا المستخدم.', 'users_system_public' => 'هذا المستخدم يمثل أي ضيف يقوم بزيارة شيء يخصك. لا يمكن استخدامه لتسجيل الدخول ولكن يتم تعيينه تلقائياً.', 'users_delete' => 'حذف المستخدم', 'users_delete_named' => 'حذف المستخدم :userName', 'users_delete_warning' => 'سيتم حذف المستخدم \':userName\' بشكل تام من النظام.', 'users_delete_confirm' => 'تأكيد حذف المستخدم؟', - 'users_migrate_ownership' => 'Migrate Ownership', - 'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.', - 'users_none_selected' => 'No user selected', + 'users_migrate_ownership' => 'نقل الملكية', + 'users_migrate_ownership_desc' => 'حدد مستخدم هنا إذا كنت تريد أن يصبح مستخدم آخر مالك جميع العناصر التي يمتلكها هذا المستخدم حاليا.', + 'users_none_selected' => 'لم يتم تحديد مستخدم', 'users_edit' => 'تعديل المستخدم', 'users_edit_profile' => 'تعديل الملف', 'users_avatar' => 'صورة المستخدم', @@ -210,24 +241,24 @@ 'users_preferred_language' => 'اللغة المفضلة', 'users_preferred_language_desc' => 'سيؤدي هذا الخيار إلى تغيير اللغة المستخدمة لواجهة المستخدم الخاصة بالتطبيق. لن يؤثر هذا على أي محتوى قد أنشائه المستخدم.', 'users_social_accounts' => 'الحسابات الاجتماعية', - 'users_social_accounts_desc' => 'View the status of the connected social accounts for this user. Social accounts can be used in addition to the primary authentication system for system access.', + 'users_social_accounts_desc' => 'عرض حالة الحسابات الاجتماعية المرتبطة لهذا المستخدم. ويمكن استخدام الحسابات الاجتماعية بالإضافة إلى نظام التوثيق الرئيس للوصول إلى النظام.', 'users_social_accounts_info' => 'Here you can connect your other accounts for quicker and easier login. Disconnecting an account here does not previously authorized access. Revoke access from your profile settings on the connected social account.', 'users_social_connect' => 'ربط الحساب', 'users_social_disconnect' => 'فصل الحساب', - 'users_social_status_connected' => 'Connected', - 'users_social_status_disconnected' => 'Disconnected', + 'users_social_status_connected' => 'متصل', + 'users_social_status_disconnected' => 'غير متصل', 'users_social_connected' => 'تم ربط حساب :socialAccount بملفك بنجاح.', 'users_social_disconnected' => 'تم فصل حساب :socialAccount من ملفك بنجاح.', 'users_api_tokens' => 'رموز الـ API', - 'users_api_tokens_desc' => 'Create and manage the access tokens used to authenticate with the BookStack REST API. Permissions for the API are managed via the user that the token belongs to.', + 'users_api_tokens_desc' => 'أنشئ وأدر رموز الوصول المستخدمة للمصادقة باستخدام واجهة برمجة تطبيقات بوكستاك رِست -BookStack REST API-. تتم إدارة صلاحيات واجهة برمجة التطبيقات بواسطة المستخدم الذي ينتمي إليه الرمز.', 'users_api_tokens_none' => 'لم يتم إنشاء رموز API لهذا المستخدم', 'users_api_tokens_create' => 'قم بإنشاء رمز مميز', 'users_api_tokens_expires' => 'انتهاء مدة الصلاحية', 'users_api_tokens_docs' => 'وثائق API', - 'users_mfa' => 'Multi-Factor Authentication', - 'users_mfa_desc' => 'Setup multi-factor authentication as an extra layer of security for your user account.', - 'users_mfa_x_methods' => ':count method configured|:count methods configured', - 'users_mfa_configure' => 'Configure Methods', + 'users_mfa' => 'المصادقة متعددة العوامل', + 'users_mfa_desc' => 'إعداد المصادقة متعددة العوامل كطبقة إضافية من الأمان لحساب المستخدم الخاص بك.', + 'users_mfa_x_methods' => ':count طريقة مُهيأة | :count طرق مُهيأة', + 'users_mfa_configure' => 'إعداد الطرق', // API Tokens 'user_api_token_create' => 'قم بإنشاء رمز API', @@ -248,34 +279,42 @@ 'user_api_token_delete_confirm' => 'هل أنت متأكد من أنك تريد حذف رمز API؟', // Webhooks - 'webhooks' => 'Webhooks', - 'webhooks_index_desc' => 'Webhooks are a way to send data to external URLs when certain actions and events occur within the system which allows event-based integration with external platforms such as messaging or notification systems.', - 'webhooks_x_trigger_events' => ':count trigger event|:count trigger events', - 'webhooks_create' => 'Create New Webhook', - 'webhooks_none_created' => 'No webhooks have yet been created.', - 'webhooks_edit' => 'Edit Webhook', - 'webhooks_save' => 'Save Webhook', - 'webhooks_details' => 'Webhook Details', - 'webhooks_details_desc' => 'Provide a user friendly name and a POST endpoint as a location for the webhook data to be sent to.', - 'webhooks_events' => 'Webhook Events', - 'webhooks_events_desc' => 'Select all the events that should trigger this webhook to be called.', - 'webhooks_events_warning' => 'Keep in mind that these events will be triggered for all selected events, even if custom permissions are applied. Ensure that use of this webhook won\'t expose confidential content.', - 'webhooks_events_all' => 'All system events', - 'webhooks_name' => 'Webhook Name', - 'webhooks_timeout' => 'Webhook Request Timeout (Seconds)', - 'webhooks_endpoint' => 'Webhook Endpoint', - 'webhooks_active' => 'Webhook Active', - 'webhook_events_table_header' => 'Events', - 'webhooks_delete' => 'Delete Webhook', - 'webhooks_delete_warning' => 'This will fully delete this webhook, with the name \':webhookName\', from the system.', - 'webhooks_delete_confirm' => 'Are you sure you want to delete this webhook?', - 'webhooks_format_example' => 'Webhook Format Example', - 'webhooks_format_example_desc' => 'Webhook data is sent as a POST request to the configured endpoint as JSON following the format below. The "related_item" and "url" properties are optional and will depend on the type of event triggered.', - 'webhooks_status' => 'Webhook Status', - 'webhooks_last_called' => 'Last Called:', - 'webhooks_last_errored' => 'Last Errored:', - 'webhooks_last_error_message' => 'Last Error Message:', + 'webhooks' => 'خطافات الويب -Webhooks-', + 'webhooks_index_desc' => 'خطافات الويب هي طريقة لإرسال البيانات إلى الروابط الخارجية عندما تحدث بعض الإجراءات والأحداث داخل النظام الذي يسمح بالتكامل القائم على الأحداث مع المنصات الخارجية مثل نظم المراسلة أو الإشعار.', + 'webhooks_x_trigger_events' => ':count حدث تشغيل |:count أحداث تشغيل', + 'webhooks_create' => 'إنشاء خطاف ويب جديد', + 'webhooks_none_created' => 'لم يتم إنشاء أي خطافات ويب حتى الآن.', + 'webhooks_edit' => 'تحرير خطاف ويب', + 'webhooks_save' => 'حفظ خطاف ويب', + 'webhooks_details' => 'تفاصيل خطاف الويب', + 'webhooks_details_desc' => 'قم بتوفير اسم سهل الاستخدام ونقطة نهاية POST كموقع لإرسال بيانات خطافات الويب إليه.', + 'webhooks_events' => 'أحداث خطفات الويب', + 'webhooks_events_desc' => 'حدد جميع الأحداث التي يجب أن تشغل هذا الرابط ليتم استدعاؤها.', + 'webhooks_events_warning' => 'ضع في اعتبارك أن هذه الأحداث سيتم تشغيلها لجميع الأحداث المحددة، حتى إذا تم تطبيق صلاحيات مخصصة. تحقق أن استخدام خطاف الويب هذا لن يكشف عن محتوى سري.', + 'webhooks_events_all' => 'جميع أحداث النظام', + 'webhooks_name' => 'اسم خطاف الويب', + 'webhooks_timeout' => 'مهلة طلب خطاف الويب (بالثواني)', + 'webhooks_endpoint' => 'نقطة نهاية خطاف الويب', + 'webhooks_active' => 'خطاف الويب فعال', + 'webhook_events_table_header' => 'الأحداث', + 'webhooks_delete' => 'حذف خطاف الويب', + 'webhooks_delete_warning' => 'سيؤدي هذا إلى حذف خطاف الويب بالكامل، الذي يحمل اسم \':webhookName\'، من النظام.', + 'webhooks_delete_confirm' => 'هل أنت متيقِّن أنك تريد حذف هذا الخطاف؟', + 'webhooks_format_example' => 'مثال على تنسيق خطاف الويب', + 'webhooks_format_example_desc' => 'يتم إرسال بيانات خطاف الويب كطلب بوست -POST- إلى نقطة النهاية المكونة كجيسون -JSON- باتباع التنسيق أدناه. خصائص "ذات صلة" و "روابط" اختيارية و ستعتمد على نوع الحدث الذي تم تشغيله.', + 'webhooks_status' => 'حالة خطاف الويب', + 'webhooks_last_called' => 'آخر اتصال:', + 'webhooks_last_errored' => 'أخر خطأ:', + 'webhooks_last_error_message' => 'رسالة الخطأ الأخيرة:', + // Licensing + 'licenses' => 'الرخص', + 'licenses_desc' => 'هذه الصفحة تفصل معلومات الرخص لبوكستاك -BookStack- بالإضافة إلى المشاريع والمكتبات المستخدمة في بوكستاك. ولا يمكن استخدام العديد من المشاريع المدرجة إلا في سياق إنمائي.', + 'licenses_bookstack' => 'رخص بوكستاك', + 'licenses_php' => 'رخص مكتبات بي إتش بي -PHP-', + 'licenses_js' => 'رخص مكتبة جافا سكريبت -JavaScript-', + 'licenses_other' => 'رخص أخرى', + 'license_details' => 'تفاصيل الرخصة', //! If editing translations files directly please ignore this in all //! languages apart from en. Content will be auto-copied from en. @@ -287,6 +326,7 @@ 'bs' => 'Bosanski', 'ca' => 'Català', 'cs' => 'Česky', + 'cy' => 'Cymraeg', 'da' => 'Dansk', 'de' => 'Deutsch (Sie)', 'de_informal' => 'Deutsch (Du)', @@ -308,6 +348,7 @@ 'lt' => 'Lietuvių Kalba', 'lv' => 'Latviešu Valoda', 'nb' => 'Norsk (Bokmål)', + 'ne' => 'नेपाली', 'nn' => 'Nynorsk', 'nl' => 'Nederlands', 'pl' => 'Polski', diff --git a/lang/ar/validation.php b/lang/ar/validation.php index a9793c54844..c27770fe3a9 100644 --- a/lang/ar/validation.php +++ b/lang/ar/validation.php @@ -105,10 +105,15 @@ 'url' => 'صيغة :attribute غير صالحة.', 'uploaded' => 'تعذر تحميل الملف. قد لا يقبل الخادم ملفات بهذا الحجم.', + 'zip_file' => ':attribute بحاجة إلى الرجوع إلى مِلَفّ داخل المِلَفّ المضغوط.', + 'zip_file_mime' => ':attribute بحاجة إلى الإشارة إلى مِلَفّ من نوع :validTypes، وجدت :foundType.', + 'zip_model_expected' => 'عنصر البيانات المتوقع ولكن ":type" تم العثور عليه.', + 'zip_unique' => 'يجب أن يكون :attribute فريداً لنوع الكائن داخل المِلَفّ المضغوط.', + // Custom validation lines 'custom' => [ 'password-confirm' => [ - 'required_with' => 'يجب تأكيد كلمة المرور', + 'required_with' => 'يجب تأكيد كلمة السر', ], ], diff --git a/lang/bg/activities.php b/lang/bg/activities.php index 2c58883aa21..f5ed71a4cc6 100644 --- a/lang/bg/activities.php +++ b/lang/bg/activities.php @@ -15,7 +15,7 @@ 'page_restore' => 'възстановена страница', 'page_restore_notification' => 'Страницата е възстановена успешно', 'page_move' => 'преместена страница', - 'page_move_notification' => 'Page successfully moved', + 'page_move_notification' => 'Страницата беше успешно преместена', // Chapters 'chapter_create' => 'създадена глава', @@ -25,13 +25,13 @@ 'chapter_delete' => 'изтрита глава', 'chapter_delete_notification' => 'Успешно изтрита глава', 'chapter_move' => 'преместена глава', - 'chapter_move_notification' => 'Chapter successfully moved', + 'chapter_move_notification' => 'Главата е успешно преместена', // Books 'book_create' => 'създадена книга', 'book_create_notification' => 'Книгата е създадена успешно', 'book_create_from_chapter' => 'превърната глава в книга', - 'book_create_from_chapter_notification' => 'Chapter successfully converted to a book', + 'book_create_from_chapter_notification' => 'Главата е успешно преобразувана в книга', 'book_update' => 'обновена книга', 'book_update_notification' => 'Книгата е обновена успешно', 'book_delete' => 'изтрита книга', @@ -84,6 +84,14 @@ 'webhook_delete' => 'изтрита уебкука', 'webhook_delete_notification' => 'Уебкуката е изтрита успешно', + // Imports + 'import_create' => 'created import', + 'import_create_notification' => 'Import successfully uploaded', + 'import_run' => 'updated import', + 'import_run_notification' => 'Content successfully imported', + 'import_delete' => 'deleted import', + 'import_delete_notification' => 'Import successfully deleted', + // Users 'user_create' => 'created user', 'user_create_notification' => 'User successfully created', @@ -93,11 +101,11 @@ 'user_delete_notification' => 'Потребителят е премахнат успешно', // API Tokens - 'api_token_create' => 'created api token', + 'api_token_create' => 'created API token', 'api_token_create_notification' => 'API token successfully created', - 'api_token_update' => 'updated api token', + 'api_token_update' => 'updated API token', 'api_token_update_notification' => 'API token successfully updated', - 'api_token_delete' => 'deleted api token', + 'api_token_delete' => 'deleted API token', 'api_token_delete_notification' => 'API token successfully deleted', // Roles @@ -119,6 +127,14 @@ 'comment_update' => 'updated comment', 'comment_delete' => 'deleted comment', + // Sort Rules + 'sort_rule_create' => 'created sort rule', + 'sort_rule_create_notification' => 'Sort rule successfully created', + 'sort_rule_update' => 'updated sort rule', + 'sort_rule_update_notification' => 'Sort rule successfully updated', + 'sort_rule_delete' => 'deleted sort rule', + 'sort_rule_delete_notification' => 'Sort rule successfully deleted', + // Other 'permissions_update' => 'обновени права', ]; diff --git a/lang/bg/auth.php b/lang/bg/auth.php index ec3e753513d..71cd2a71295 100644 --- a/lang/bg/auth.php +++ b/lang/bg/auth.php @@ -91,7 +91,7 @@ 'mfa_option_totp_title' => 'Мобилно приложение', 'mfa_option_totp_desc' => 'За да използваш многофакторно удостоверяване, ще ти трябва мобилно приложение, което поддържа временни еднократни пароли (TOTP), като например Google Authenticator, Authy или Microsoft Authenticator.', 'mfa_option_backup_codes_title' => 'Резервни кодове', - 'mfa_option_backup_codes_desc' => 'Запази на сигурно място набор от еднократни резервни кодове, с които можеш да устовериш идентичността си.', + 'mfa_option_backup_codes_desc' => 'Generates a set of one-time-use backup codes which you\'ll enter on login to verify your identity. Make sure to store these in a safe & secure place.', 'mfa_gen_confirm_and_enable' => 'Потвърди и включи', 'mfa_gen_backup_codes_title' => 'Настройка на резервни кодове', 'mfa_gen_backup_codes_desc' => 'Запази този лист с кодове на сигурно място. Когато достъпваш системата, ще можеш да използваш един от тези кодове като вторичен механизъм за удостоверяване.', diff --git a/lang/bg/common.php b/lang/bg/common.php index 5fa91b2e871..ba74ea5b2cb 100644 --- a/lang/bg/common.php +++ b/lang/bg/common.php @@ -20,7 +20,7 @@ 'description' => 'Описание', 'role' => 'Роля', 'cover_image' => 'Образ на корицата', - 'cover_image_description' => 'Образът трябва да е горе-долу 440х250 пиксела.', + 'cover_image_description' => 'This image should be approximately 440x250px although it will be flexibly scaled & cropped to fit the user interface in different scenarios as required, so actual dimensions for display will differ.', // Actions 'actions' => 'Действия', @@ -30,6 +30,8 @@ 'create' => 'Създаване', 'update' => 'Обновяване', 'edit' => 'Редактиране', + 'archive' => 'Archive', + 'unarchive' => 'Un-Archive', 'sort' => 'Сортиране', 'move' => 'Преместване', 'copy' => 'Копиране', @@ -107,4 +109,7 @@ // Not directly used but available for convenience to users. 'privacy_policy' => 'Политика за поверителност', 'terms_of_service' => 'Условия на услугата', + + // OpenSearch + 'opensearch_description' => 'Search :appName', ]; diff --git a/lang/bg/editor.php b/lang/bg/editor.php index f677afa631e..a75128953eb 100644 --- a/lang/bg/editor.php +++ b/lang/bg/editor.php @@ -13,6 +13,7 @@ 'cancel' => 'Отказ', 'save' => 'Запис', 'close' => 'Затваряне', + 'apply' => 'Apply', 'undo' => 'Отмяна', 'redo' => 'Повтаряне', 'left' => 'Вляво', @@ -47,6 +48,7 @@ 'superscript' => 'Горен индекс', 'subscript' => 'Долен индекс', 'text_color' => 'Цвят на текста', + 'highlight_color' => 'Highlight color', 'custom_color' => 'Цвят по избор', 'remove_color' => 'Премахване на цвят', 'background_color' => 'Фонов цвят', @@ -81,6 +83,9 @@ 'table_properties' => 'Свойства на таблицата', 'table_properties_title' => 'Свойства на таблица', 'delete_table' => 'Изтриване на таблица', + 'table_clear_formatting' => 'Clear table formatting', + 'resize_to_contents' => 'Resize to contents', + 'row_header' => 'Row header', 'insert_row_before' => 'Вмъкни реда преди', 'insert_row_after' => 'Вмъкни реда след', 'delete_row' => 'Изтриване на ред', @@ -144,6 +149,7 @@ 'url' => 'URL', 'text_to_display' => 'Текст за показване', 'title' => 'Заглавие', + 'browse_links' => 'Browse links', 'open_link' => 'Open link', 'open_link_in' => 'Open link in...', 'open_link_current' => 'Текущ прозорец', @@ -160,6 +166,8 @@ 'about' => 'За редактора', 'about_title' => 'Относно визуалния редактор', 'editor_license' => 'Лиценз, авторски и сходни права на редактора', + 'editor_lexical_license' => 'This editor is built as a fork of :lexicalLink which is distributed under the MIT license.', + 'editor_lexical_license_link' => 'Full license details can be found here.', 'editor_tiny_license' => 'Този редактор е изграден посредством :tinyLink, което е предоставен под лиценз MIT.', 'editor_tiny_license_link' => 'Авторското и сходните му права, както и лицензът на TinyMCE, могат да бъдат намерени тук.', 'save_continue' => 'Запази страницата и продължи', diff --git a/lang/bg/entities.php b/lang/bg/entities.php index fb57bf1bca0..42cdad801a9 100644 --- a/lang/bg/entities.php +++ b/lang/bg/entities.php @@ -39,6 +39,34 @@ 'export_pdf' => 'PDF файл', 'export_text' => 'Обикновен текстов файл', 'export_md' => 'Markdown файл', + 'export_zip' => 'Portable ZIP', + 'default_template' => 'Default Page Template', + 'default_template_explain' => 'Assign a page template that will be used as the default content for all pages created within this item. Keep in mind this will only be used if the page creator has view access to the chosen template page.', + 'default_template_select' => 'Select a template page', + 'import' => 'Import', + 'import_validate' => 'Validate Import', + 'import_desc' => 'Import books, chapters & pages using a portable zip export from the same, or a different, instance. Select a ZIP file to proceed. After the file has been uploaded and validated you\'ll be able to configure & confirm the import in the next view.', + '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_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_size' => ':size Import ZIP Size', + 'import_uploaded_at' => 'Uploaded :relativeTime', + 'import_uploaded_by' => 'Uploaded by', + 'import_location' => 'Import Location', + '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_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', + 'breadcrumb_siblings_for_book' => 'Navigate siblings for book', + 'breadcrumb_siblings_for_bookshelf' => 'Navigate siblings for shelf', // Permissions and restrictions 'permissions' => 'Права', @@ -132,9 +160,6 @@ 'books_edit_named' => 'Редактирай книга :bookName', 'books_form_book_name' => 'Име на книга', 'books_save' => 'Запази книга', - 'books_default_template' => 'Default Page Template', - 'books_default_template_explain' => 'Assign a page template that will be used as the default content for all new pages in this book. Keep in mind this will only be used if the page creator has view access to those chosen template page.', - 'books_default_template_select' => 'Select a template page', 'books_permissions' => 'Настройки за достъп до книгата', 'books_permissions_updated' => 'Настройките за достъп до книгата бяха обновени', 'books_empty_contents' => 'Няма създадени страници или глави към тази книга.', @@ -145,7 +170,9 @@ 'books_search_this' => 'Търси в книгата', 'books_navigation' => 'Навигация на книгата', 'books_sort' => 'Сортирай съдържанието на книгата', - 'books_sort_desc' => 'Move chapters and pages within a book to reorganise its contents. Other books can be added which allows easy moving of chapters and pages between books.', + 'books_sort_desc' => 'Move chapters and pages within a book to reorganise its contents. Other books can be added which allows easy moving of chapters and pages between books. Optionally an auto sort rule can be set to automatically sort this book\'s contents upon changes.', + 'books_sort_auto_sort' => 'Auto Sort Option', + 'books_sort_auto_sort_active' => 'Auto Sort Active: :sortName', 'books_sort_named' => 'Сортирай книга :bookName', 'books_sort_name' => 'Сортиране по име', 'books_sort_created' => 'Сортирай по дата на създаване', @@ -207,7 +234,7 @@ 'pages_delete_draft' => 'Изтрий чернова', 'pages_delete_success' => 'Страницата е изтрита', 'pages_delete_draft_success' => 'Черновата на страницата бе изтрита', - 'pages_delete_warning_template' => 'This page is in active use as a book default page template. These books will no longer have a default page template assigned after this page is deleted.', + 'pages_delete_warning_template' => 'This page is in active use as a book or chapter default page template. These books or chapters will no longer have a default page template assigned after this page is deleted.', 'pages_delete_confirm' => 'Сигурни ли сте, че искате да изтриете тази страница?', 'pages_delete_draft_confirm' => 'Сигурни ли сте, че искате да изтриете тази чернова?', 'pages_editing_named' => 'Редактиране на страница :pageName', @@ -224,6 +251,8 @@ 'pages_edit_switch_to_markdown_clean' => '(Clean Content)', 'pages_edit_switch_to_markdown_stable' => '(Stable Content)', 'pages_edit_switch_to_wysiwyg' => 'Switch to WYSIWYG Editor', + 'pages_edit_switch_to_new_wysiwyg' => 'Switch to new WYSIWYG', + 'pages_edit_switch_to_new_wysiwyg_desc' => '(In Beta Testing)', 'pages_edit_set_changelog' => 'Задайте регистър на промените', 'pages_edit_enter_changelog_desc' => 'Въведете кратко резюме на промените, които сте създали', 'pages_edit_enter_changelog' => 'Въведи регистър на промените', @@ -243,6 +272,7 @@ 'pages_md_insert_drawing' => 'Вмъкни рисунка', 'pages_md_show_preview' => 'Show preview', 'pages_md_sync_scroll' => 'Sync preview scroll', + 'pages_md_plain_editor' => 'Plaintext editor', 'pages_drawing_unsaved' => 'Unsaved Drawing Found', 'pages_drawing_unsaved_confirm' => 'Unsaved drawing data was found from a previous failed drawing save attempt. Would you like to restore and continue editing this unsaved drawing?', 'pages_not_in_chapter' => 'Страницата не принадлежи в никоя глава', @@ -367,8 +397,11 @@ 'comment' => 'Коментирай', 'comments' => 'Коментари', 'comment_add' => 'Добавяне на коментар', + 'comment_none' => 'No comments to display', 'comment_placeholder' => 'Напишете коментар', - 'comment_count' => '{0} Няма коментари|{1} 1 коментар|[2,*] :count коментара', + 'comment_thread_count' => ':count Comment Thread|:count Comment Threads', + 'comment_archived_count' => ':count Archived', + 'comment_archived_threads' => 'Archived Threads', 'comment_save' => 'Запази коментар', 'comment_new' => 'Нов коментар', 'comment_created' => 'коментирано :createDiff', @@ -377,8 +410,14 @@ '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_delete_confirm' => 'Наистина ли искате да изтриете този коментар?', 'comment_in_reply_to' => 'В отговор на :commentId', + 'comment_reference' => 'Reference', + 'comment_reference_outdated' => '(Outdated)', 'comment_editor_explain' => 'Here are the comments that have been left on this page. Comments can be added & managed when viewing the saved page.', // Revision diff --git a/lang/bg/errors.php b/lang/bg/errors.php index fbc042a055e..dd024518083 100644 --- a/lang/bg/errors.php +++ b/lang/bg/errors.php @@ -10,6 +10,7 @@ // Auth 'error_user_exists_different_creds' => 'Потребител с емайл :email вече съществува но с други данни.', + 'auth_pre_register_theme_prevention' => 'User account could not be registered for the provided details', 'email_already_confirmed' => 'Емейлът вече беше потвърден. Моля опитрайте да влезете.', 'email_confirmation_invalid' => 'Този код за достъп не е валиден или вече е бил използван, Моля опитай да се регистрираш отново.', 'email_confirmation_expired' => 'Кодът за потвърждение изтече, нов емейл за потвърждение беше изпратен.', @@ -23,7 +24,6 @@ 'saml_invalid_response_id' => 'Заявката от външната система не е разпознат от процеса започнат от това приложение. Връщането назад след влизане може да породи този проблем.', 'saml_fail_authed' => 'Влизането чрез :system не беше успешно, системата не успя да удостовери потребителя', 'oidc_already_logged_in' => 'Вече си вписан', - 'oidc_user_not_registered' => 'Потребителят :name не е регистриран, а автоматичната регистрация е изключена', 'oidc_no_email_address' => 'Не можах да намеря имейл адрес за този потребител в данните, предоставени от външната удостоверителна система', 'oidc_fail_authed' => 'Вписването чрез :system не беше успешно, тъй като системата не предостави успешна оторизация', 'social_no_action_defined' => 'Действието не беше дефинирано', @@ -37,6 +37,7 @@ 'social_driver_not_found' => 'Кодът за връзка със социалната мрежа не съществува', 'social_driver_not_configured' => 'Социалните настройки на твоя :socialAccount не са конфигурирани правилно.', 'invite_token_expired' => 'Твоята покана е изтекла. Вместо това може да пробваш да възстановиш паролата на профила си.', + 'login_user_not_found' => 'A user for this action could not be found.', // System 'path_not_writable' => 'Не може да се качи файл в :filePath. Увери се на сървъра, че в пътя може да се записва.', @@ -77,6 +78,7 @@ // Users 'users_cannot_delete_only_admin' => 'Не можеш да изтриеш единствения администратор', 'users_cannot_delete_guest' => 'Не можеш да изтриеш потребителя на госта', + 'users_could_not_send_invite' => 'Could not create user since invite email failed to send', // Roles 'role_cannot_be_edited' => 'Ролята не може да бъде редактирана', @@ -103,6 +105,18 @@ 'app_down' => ':appName не е достъпно в момента', 'back_soon' => 'Ще се върне обратно онлайн скоро.', + // Import + 'import_zip_cant_read' => 'Could not read ZIP file.', + 'import_zip_cant_decode_data' => 'Could not find and decode ZIP data.json content.', + 'import_zip_no_data' => 'ZIP file data has no expected book, chapter or page content.', + 'import_validation_failed' => 'Import ZIP failed to validate with errors:', + 'import_zip_failed_notification' => 'Failed to import ZIP file.', + 'import_perms_books' => 'You are lacking the required permissions to create books.', + 'import_perms_chapters' => 'You are lacking the required permissions to create chapters.', + 'import_perms_pages' => 'You are lacking the required permissions to create pages.', + 'import_perms_images' => 'You are lacking the required permissions to create images.', + 'import_perms_attachments' => 'You are lacking the required permission to create attachments.', + // API errors 'api_no_authorization_found' => 'Но беше намерен код за достъп в заявката', 'api_bad_authorization_format' => 'В заявката имаше код за достъп, но формата изглежда е неправилен', diff --git a/lang/bg/preferences.php b/lang/bg/preferences.php index 963f1f04c7d..f954340e2d2 100644 --- a/lang/bg/preferences.php +++ b/lang/bg/preferences.php @@ -27,7 +27,7 @@ 'notifications_save' => 'Save Preferences', 'notifications_update_success' => 'Notification preferences have been updated!', 'notifications_watched' => 'Watched & Ignored Items', - 'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.', + 'notifications_watched_desc' => 'Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.', 'auth' => 'Access & Security', 'auth_change_password' => 'Change Password', diff --git a/lang/bg/settings.php b/lang/bg/settings.php index f4654135b5a..6dd27d74a57 100644 --- a/lang/bg/settings.php +++ b/lang/bg/settings.php @@ -74,6 +74,36 @@ 'reg_confirm_restrict_domain_desc' => 'Въведи разделен със запетаи списък от имейл домейни, до които да бъде ограничена регистрацията. На потребителите ще им бъде изпратен имейл, за да потвърдят адреса, преди да могат да използват приложението.
Имай предвид, че потребителите ще могат да сменят имейл адресите си след успешна регистрация.', 'reg_confirm_restrict_domain_placeholder' => 'Няма наложени ограничения', + // Sorting Settings + 'sorting' => 'Sorting', + 'sorting_book_default' => 'Default Book Sort', + 'sorting_book_default_desc' => 'Select the default sort rule to apply to new books. This won\'t affect existing books, and can be overridden per-book.', + 'sorting_rules' => 'Sort Rules', + 'sorting_rules_desc' => 'These are predefined sorting operations which can be applied to content in the system.', + 'sort_rule_assigned_to_x_books' => 'Assigned to :count Book|Assigned to :count Books', + 'sort_rule_create' => 'Create Sort Rule', + 'sort_rule_edit' => 'Edit Sort Rule', + 'sort_rule_delete' => 'Delete Sort Rule', + 'sort_rule_delete_desc' => 'Remove this sort rule from the system. Books using this sort will revert to manual sorting.', + 'sort_rule_delete_warn_books' => 'This sort rule is currently used on :count book(s). Are you sure you want to delete this?', + 'sort_rule_delete_warn_default' => 'This sort rule is currently used as the default for books. Are you sure you want to delete this?', + 'sort_rule_details' => 'Sort Rule Details', + 'sort_rule_details_desc' => 'Set a name for this sort rule, which will appear in lists when users are selecting a sort.', + 'sort_rule_operations' => 'Sort Operations', + 'sort_rule_operations_desc' => 'Configure the sort actions to be performed by moving them from the list of available operations. Upon use, the operations will be applied in order, from top to bottom. Any changes made here will be applied to all assigned books upon save.', + 'sort_rule_available_operations' => 'Available Operations', + 'sort_rule_available_operations_empty' => 'No operations remaining', + 'sort_rule_configured_operations' => 'Configured Operations', + 'sort_rule_configured_operations_empty' => 'Drag/add operations from the "Available Operations" list', + 'sort_rule_op_asc' => '(Asc)', + 'sort_rule_op_desc' => '(Desc)', + 'sort_rule_op_name' => 'Name - Alphabetical', + 'sort_rule_op_name_numeric' => '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_last' => 'Chapters Last', + // Maintenance settings 'maint' => 'Поддръжка', 'maint_image_cleanup' => 'Разчисти изображения', @@ -109,7 +139,7 @@ 'recycle_bin_contents_empty' => 'Кошчето е празно', 'recycle_bin_empty' => 'Изпразни кочшето', 'recycle_bin_empty_confirm' => 'Това ще унищожи завинаги всички обекти в кошчето, включително съдържанието във всеки обект. Сигурен/на ли си, че искаш да изпразниш кошчето?', - 'recycle_bin_destroy_confirm' => 'Това действие завинаги ще изтрие от системата този обект, както и всички негови поделементи, и няма да можеш да го възстановиш. Сигурен/на ли си, че искаш да изтриеш този обект завинаги?', + 'recycle_bin_destroy_confirm' => 'This action will permanently delete this item from the system, along with any child elements listed below, and you will not be able to restore this content. Are you sure you want to permanently delete this item?', 'recycle_bin_destroy_list' => 'Обекти за унищожение', 'recycle_bin_restore_list' => 'Обекти за възстановяване', 'recycle_bin_restore_confirm' => 'Това действие ще възстанови изтрития обект, както и всички негови поделементи, в оригиналното им местоположение. Ако оригиналното им местоположение също е изтрито и сега се намира в кошчето, то също ще трябва да бъде възстановено.', @@ -162,6 +192,7 @@ 'role_access_api' => 'Достъп до API на системата', 'role_manage_settings' => 'Управление на настройките на приложението', 'role_export_content' => 'Експортирай съдържанието', + 'role_import_content' => 'Import content', 'role_editor_change' => 'Change page editor', 'role_notifications' => 'Receive & manage notifications', 'role_asset' => 'Настройки за достъп до активи', @@ -276,6 +307,14 @@ 'webhooks_last_errored' => 'Последна грешка на:', 'webhooks_last_error_message' => 'Последно съобщение за грешка:', + // Licensing + 'licenses' => 'Licenses', + 'licenses_desc' => 'This page details license information for BookStack in addition to the projects & libraries that are used within BookStack. Many projects listed may only be used in a development context.', + 'licenses_bookstack' => 'BookStack License', + 'licenses_php' => 'PHP Library Licenses', + 'licenses_js' => 'JavaScript Library Licenses', + 'licenses_other' => 'Other Licenses', + 'license_details' => 'License Details', //! If editing translations files directly please ignore this in all //! languages apart from en. Content will be auto-copied from en. @@ -287,6 +326,7 @@ 'bs' => 'Bosanski', 'ca' => 'Català', 'cs' => 'Česky', + 'cy' => 'Cymraeg', 'da' => 'Dansk', 'de' => 'Deutsch (Sie)', 'de_informal' => 'Deutsch (Du)', @@ -308,6 +348,7 @@ 'lt' => 'Lietuvių Kalba', 'lv' => 'Latviešu Valoda', 'nb' => 'Norsk (Bokmål)', + 'ne' => 'नेपाली', 'nn' => 'Nynorsk', 'nl' => 'Nederlands', 'pl' => 'Polski', diff --git a/lang/bg/validation.php b/lang/bg/validation.php index fb6c4dd4a63..e08eb55de61 100644 --- a/lang/bg/validation.php +++ b/lang/bg/validation.php @@ -105,6 +105,11 @@ 'url' => 'Форматът на :attribute не е валиден.', 'uploaded' => 'Файлът не можа да бъде качен. Сървърът може да не приема файлове с такъв размер.', + 'zip_file' => 'The :attribute needs to reference a file within the ZIP.', + 'zip_file_mime' => 'The :attribute needs to reference a file of type :validTypes, found :foundType.', + 'zip_model_expected' => 'Data object expected but ":type" found.', + 'zip_unique' => 'The :attribute must be unique for the object type within the ZIP.', + // Custom validation lines 'custom' => [ 'password-confirm' => [ diff --git a/lang/bn/activities.php b/lang/bn/activities.php new file mode 100644 index 00000000000..7e4ba8229aa --- /dev/null +++ b/lang/bn/activities.php @@ -0,0 +1,140 @@ + 'নতুন পৃষ্ঠা সৃষ্টি করেছেন', + 'page_create_notification' => 'পৃষ্ঠাটি সার্থকভাবে তৈরী করা হয়েছে', + 'page_update' => 'পৃষ্ঠা হালনাগাদ করেছেন', + 'page_update_notification' => 'পৃষ্ঠাটি সার্থকভাবে হালনাগাদ করা হয়েছে', + 'page_delete' => 'পৃষ্ঠা মুছে ফেলেছেন', + 'page_delete_notification' => 'পৃষ্ঠাটি সার্থকভাবে মুছে ফেলা হয়েছে', + 'page_restore' => 'মুছে ফেলা পৃষ্ঠা পুনরুদ্ধার করেছেন', + 'page_restore_notification' => 'পৃষ্ঠাটি সার্থকভাবে পুনরুদ্ধার করা হয়েছে', + 'page_move' => 'পৃষ্ঠা স্থানান্তর করেছেন', + 'page_move_notification' => 'পৃষ্ঠাটি সার্থকভাবে স্থানান্তর করা হয়েছে', + + // Chapters + 'chapter_create' => 'নতুন অধ্যায় সৃষ্টি করেছেন', + 'chapter_create_notification' => 'অধ্যায়টি সার্থকভাবে তৈরী করা হয়েছে', + 'chapter_update' => 'অধ্যায় হালনাগাদ করেছেন', + 'chapter_update_notification' => 'অধ্যায়টি সার্থকভাবে হালনাগাদ করা হয়েছে', + 'chapter_delete' => 'অধ্যায় মুছে ফেলেছেন', + 'chapter_delete_notification' => 'অধ্যায়টি সার্থকভাবে মুছে ফেলা হয়েছে', + 'chapter_move' => 'অধ্যায় স্থানান্তর করেছেন', + 'chapter_move_notification' => 'অধ্যায়টি সার্থকভাবে স্থানান্তর করা হয়েছে', + + // Books + 'book_create' => 'নতুন বই সৃষ্টি করেছেন', + 'book_create_notification' => 'বইটি সার্থকভাবে তৈরী করা হয়েছে', + 'book_create_from_chapter' => 'অধ্যায়কে বইতে রূপান্তরিত করেছেন', + 'book_create_from_chapter_notification' => 'অধ্যায়কে বইতে রূপান্তর করার প্রক্রিয়া সফলভাবে সম্পন্ন হয়েছে', + 'book_update' => 'বই হালনাগাদ করেছেন', + 'book_update_notification' => 'বইটি সার্থকভাবে হালনাগাদ করা হয়েছে', + 'book_delete' => 'বই মুছে ফেলেছেন', + 'book_delete_notification' => 'বইটি সার্থকভাবে মুছে ফেলা হয়েছে', + 'book_sort' => 'বইটি ক্রমানুযায়ী সাজিয়েছেন', + 'book_sort_notification' => 'বইটি সার্থকভাবে ক্রমানুযায়ী সাজানো হয়েছে', + + // Bookshelves + 'bookshelf_create' => 'নতুন বুকশেলফ তৈরী করেছেন', + 'bookshelf_create_notification' => 'বুকশেলফটি সার্থকভাবে তৈরী করা হয়েছে', + 'bookshelf_create_from_book' => 'বইটিকে বুকশেলফে রূপান্তরিত করার প্রক্রিয়া সফলভাবে সম্পন্ন হয়েছে', + 'bookshelf_create_from_book_notification' => 'বইকে বুকশেলফে রূপান্তর করার প্রক্রিয়া সফলভাবে সম্পন্ন হয়েছে', + 'bookshelf_update' => 'বুকশেলফটি হালনাগাদ করেছেন', + 'bookshelf_update_notification' => 'বুকশেলফটি সার্থকভাবে হালনাগাদ করা হয়েছে', + 'bookshelf_delete' => 'বুকশেলফটি মুছে ফেলেছেন', + 'bookshelf_delete_notification' => 'বুকশেলফটি সার্থকভাবে মুছে ফেলা হয়েছে', + + // Revisions + 'revision_restore' => 'সংশোধনী পুনঃস্থাপন করেছেন', + 'revision_delete' => 'সংশোধনী মুছে ফেলেছেন', + 'revision_delete_notification' => 'সংশোধনী সার্থকভাবে মুছে ফেলা হয়েছে', + + // Favourites + 'favourite_add_notification' => 'আপনার প্রিয় তালিকায় ":name" যোগ করা হয়েছে', + 'favourite_remove_notification' => 'আপনার প্রিয় তালিকা হতে ":name"-কে মুছে ফেলা হয়েছে', + + // Watching + 'watch_update_level_notification' => 'পর্যবেক্ষণনীতি সার্থকভাবে হালনাগাদ করা হয়েছে', + + // Auth + 'auth_login' => 'লগড ইন অবস্থায় আছেন', + 'auth_register' => 'নতুন ব্যবহারকারী হিসাবে নিবন্ধিত', + 'auth_password_reset_request' => 'ব্যবহারকারীর পাসওয়ার্ড রিসেটের আবেদন করেছেন', + 'auth_password_reset_update' => 'ব্যবহারকারী পাসওয়ার্ড রিসেট করুন', + 'mfa_setup_method' => 'মাল্টি ফ্যাক্টর অথেনটিকেশন সক্রিয় করেছেন', + 'mfa_setup_method_notification' => 'মাল্টি ফ্যাক্টর অথেনটিকেশন সার্থকভাবে সক্রিয় করা হয়েছে', + 'mfa_remove_method' => 'মাল্টি ফ্যাক্টর অথেনটিকেশন নিষ্ক্রিয় করেছেন', + 'mfa_remove_method_notification' => 'মাল্টি ফ্যাক্টর অথেনটিকেশন সার্থকভাবে নিষ্ক্রিয় করা হয়েছে', + + // Settings + 'settings_update' => 'সেটিংস হালনাগাদ করেছেন', + 'settings_update_notification' => 'সেটিংস সার্থকভাবে হালনাগাদ করা হয়েছে', + 'maintenance_action_run' => 'রক্ষণাবেক্ষণ কার্যক্রম চালু করেছেন', + + // Webhooks + 'webhook_create' => 'নতুন ওয়েবহুক তৈরী করেছেন', + 'webhook_create_notification' => 'নতুন ওয়েবহুক সার্থকভাবে তৈরী করা হয়েছে', + 'webhook_update' => 'ওয়েবহুকটি হালনাগাদ করেছেন', + 'webhook_update_notification' => 'ওয়েবহুকটি সার্থকভাবে হালনাগাদ করা হয়েছে', + 'webhook_delete' => 'ওয়েবহুকটি মুছে ফেলেছেন', + 'webhook_delete_notification' => 'ওয়েবহুকটি সার্থকভাবে মুছে ফেলা হয়েছে', + + // Imports + 'import_create' => 'ইমপোর্টটি তৈরী করেছেন', + 'import_create_notification' => 'ইমপোর্টটি সার্থকভাবে আপলোড করা হয়েছে', + 'import_run' => 'ইমপোর্টটি হালনাগাদ করেছেন', + 'import_run_notification' => 'কনটেন্ট সার্থকভাবে ইমপোর্ট করা হয়েছে', + 'import_delete' => 'ইমপোর্টটি মুছে ফেলেছেন', + 'import_delete_notification' => 'ইমপোর্টটি সার্থকভাবে মুছে ফেলা হয়েছে', + + // Users + 'user_create' => 'নতুন ব্যবহারকারী তৈরী করেছেন', + 'user_create_notification' => 'নতুন ব্যবহারকারী সার্থকভাবে তৈরী করা হয়েছে', + 'user_update' => 'ব্যবহারকারীটি হালনাগাদ করেছেন', + 'user_update_notification' => 'ব্যবহারকারীটি সার্থকভাবে হালনাগাদ করা হয়েছে', + 'user_delete' => 'ব্যবহারকারীটি মুছে ফেলেছেন', + 'user_delete_notification' => 'ব্যবহারকারীটি সার্থকভাবে মুছে ফেলা হয়েছে', + + // API Tokens + 'api_token_create' => 'এপিআই টোকেনটি তৈরী করেছেন', + 'api_token_create_notification' => 'এপিআই টোকেনটি সার্থকভাবে তৈরী করা হয়েছে', + 'api_token_update' => 'এপিআই টোকেনটি হালনাগাদ করেছেন', + 'api_token_update_notification' => 'এপিআই টোকেনটি হালনাগাদ করা হয়েছে', + 'api_token_delete' => 'এপিআই টোকেনটি মুছে ফেলেছেন', + 'api_token_delete_notification' => 'এপিআই টোকেনটি সার্থকভাবে মুছে ফেলা হয়েছে', + + // Roles + 'role_create' => 'রোলটি তৈরী করেছেন', + 'role_create_notification' => 'রোলটি সার্থকভাবে তৈরী করা হয়েছে', + 'role_update' => 'রোলটি হালনাগাদ করেছেন', + 'role_update_notification' => 'রোলটি সার্থকভাবে হালনাগাদ করা হয়েছে', + 'role_delete' => 'রোলটি মুছে ফেলেছেন', + 'role_delete_notification' => 'রোলটি সার্থকভাবে মুছে ফেলা হয়েছে', + + // Recycle Bin + 'recycle_bin_empty' => 'রিসাইকেল বিন খালি করে ফেলেছেন', + 'recycle_bin_restore' => 'রিসাইকেল বিন হতে প্রত্যাবর্তন করা হয়েছে', + 'recycle_bin_destroy' => 'রিসাইকেল বিন হতে অপসারণ করা হয়েছে', + + // Comments + 'commented_on' => 'মন্তব্য প্রদান করেছেন', + 'comment_create' => 'মন্তব্য যোগ করেছেন', + 'comment_update' => 'মন্তব্য হালনাগাদ করেছেন', + 'comment_delete' => 'মন্তব্য মুছে ফেলেছেন', + + // Sort Rules + 'sort_rule_create' => 'created sort rule', + 'sort_rule_create_notification' => 'Sort rule successfully created', + 'sort_rule_update' => 'updated sort rule', + 'sort_rule_update_notification' => 'Sort rule successfully updated', + 'sort_rule_delete' => 'deleted sort rule', + 'sort_rule_delete_notification' => 'Sort rule successfully deleted', + + // Other + 'permissions_update' => 'অনুমতিক্রম হালনাগাদ করেছেন', +]; diff --git a/lang/bn/auth.php b/lang/bn/auth.php new file mode 100644 index 00000000000..06ed376a71a --- /dev/null +++ b/lang/bn/auth.php @@ -0,0 +1,117 @@ + 'প্রদত্ত তথ্যনিরূপিত কোন রেকর্ড পাওয়া যায়নি।', + 'throttle' => 'লগইন প্রচেষ্টার সীমা অতিক্রান্ত। দয়া করে :seconds সেকেন্ড পর আবার চেষ্টা করুন।', + + // Login & Register + 'sign_up' => 'নিবন্ধিত হোন', + 'log_in' => 'লগ ইন করুন', + 'log_in_with' => ':socialDriver দ্বারা লগইন করুন', + 'sign_up_with' => ':socialDriver দ্বারা নিবন্ধিত হোন', + 'logout' => 'লগআউট', + + 'name' => 'নাম', + 'username' => 'ব্যবহারকারী', + 'email' => 'ই-মেইল', + 'password' => 'পাসওয়ার্ড', + 'password_confirm' => 'পাসওয়ার্ডের পুনরাবৃত্তি', + 'password_hint' => 'ন্যূনতম ৮ অক্ষরের হতে হবে', + 'forgot_password' => 'পাসওয়ার্ড ভুলে গেছেন?', + 'remember_me' => 'লগইন স্থায়িত্ব ধরে রাখুন', + 'ldap_email_hint' => 'Please enter an email to use for this account.', + 'create_account' => 'Create Account', + 'already_have_account' => 'Already have an account?', + 'dont_have_account' => 'Don\'t have an account?', + 'social_login' => 'Social Login', + 'social_registration' => 'Social Registration', + 'social_registration_text' => 'Register and sign in using another service.', + + 'register_thanks' => 'Thanks for registering!', + 'register_confirm' => 'Please check your email and click the confirmation button to access :appName.', + 'registrations_disabled' => 'Registrations are currently disabled', + 'registration_email_domain_invalid' => 'That email domain does not have access to this application', + 'register_success' => 'Thanks for signing up! You are now registered and signed in.', + + // Login auto-initiation + 'auto_init_starting' => 'Attempting Login', + 'auto_init_starting_desc' => 'We\'re contacting your authentication system to start the login process. If there\'s no progress after 5 seconds you can try clicking the link below.', + 'auto_init_start_link' => 'Proceed with authentication', + + // Password Reset + 'reset_password' => 'Reset Password', + 'reset_password_send_instructions' => 'Enter your email below and you will be sent an email with a password reset link.', + 'reset_password_send_button' => 'Send Reset Link', + 'reset_password_sent' => 'A password reset link will be sent to :email if that email address is found in the system.', + 'reset_password_success' => 'Your password has been successfully reset.', + 'email_reset_subject' => 'Reset your :appName password', + 'email_reset_text' => 'You are receiving this email because we received a password reset request for your account.', + 'email_reset_not_requested' => 'If you did not request a password reset, no further action is required.', + + // Email Confirmation + 'email_confirm_subject' => 'Confirm your email on :appName', + 'email_confirm_greeting' => 'Thanks for joining :appName!', + 'email_confirm_text' => 'Please confirm your email address by clicking the button below:', + 'email_confirm_action' => 'Confirm Email', + 'email_confirm_send_error' => 'Email confirmation required but the system could not send the email. Contact the admin to ensure email is set up correctly.', + 'email_confirm_success' => 'Your email has been confirmed! You should now be able to login using this email address.', + 'email_confirm_resent' => 'Confirmation email resent, Please check your inbox.', + 'email_confirm_thanks' => 'Thanks for confirming!', + 'email_confirm_thanks_desc' => 'Please wait a moment while your confirmation is handled. If you are not redirected after 3 seconds press the "Continue" link below to proceed.', + + 'email_not_confirmed' => 'Email Address Not Confirmed', + 'email_not_confirmed_text' => 'Your email address has not yet been confirmed.', + 'email_not_confirmed_click_link' => 'Please click the link in the email that was sent shortly after you registered.', + 'email_not_confirmed_resend' => 'If you cannot find the email you can re-send the confirmation email by submitting the form below.', + 'email_not_confirmed_resend_button' => 'Resend Confirmation Email', + + // User Invite + 'user_invite_email_subject' => 'You have been invited to join :appName!', + 'user_invite_email_greeting' => 'An account has been created for you on :appName.', + 'user_invite_email_text' => 'Click the button below to set an account password and gain access:', + 'user_invite_email_action' => 'Set Account Password', + 'user_invite_page_welcome' => 'Welcome to :appName!', + 'user_invite_page_text' => 'To finalise your account and gain access you need to set a password which will be used to log-in to :appName on future visits.', + 'user_invite_page_confirm_button' => 'Confirm Password', + 'user_invite_success_login' => 'Password set, you should now be able to login using your set password to access :appName!', + + // Multi-factor Authentication + 'mfa_setup' => 'Setup Multi-Factor Authentication', + 'mfa_setup_desc' => 'Setup multi-factor authentication as an extra layer of security for your user account.', + 'mfa_setup_configured' => 'Already configured', + 'mfa_setup_reconfigure' => 'Reconfigure', + 'mfa_setup_remove_confirmation' => 'Are you sure you want to remove this multi-factor authentication method?', + 'mfa_setup_action' => 'Setup', + 'mfa_backup_codes_usage_limit_warning' => 'You have less than 5 backup codes remaining, Please generate and store a new set before you run out of codes to prevent being locked out of your account.', + 'mfa_option_totp_title' => 'Mobile App', + 'mfa_option_totp_desc' => 'To use multi-factor authentication you\'ll need a mobile application that supports TOTP such as Google Authenticator, Authy or Microsoft Authenticator.', + 'mfa_option_backup_codes_title' => 'Backup Codes', + 'mfa_option_backup_codes_desc' => 'Generates a set of one-time-use backup codes which you\'ll enter on login to verify your identity. Make sure to store these in a safe & secure place.', + 'mfa_gen_confirm_and_enable' => 'Confirm and Enable', + 'mfa_gen_backup_codes_title' => 'Backup Codes Setup', + 'mfa_gen_backup_codes_desc' => 'Store the below list of codes in a safe place. When accessing the system you\'ll be able to use one of the codes as a second authentication mechanism.', + 'mfa_gen_backup_codes_download' => 'Download Codes', + 'mfa_gen_backup_codes_usage_warning' => 'Each code can only be used once', + 'mfa_gen_totp_title' => 'Mobile App Setup', + 'mfa_gen_totp_desc' => 'To use multi-factor authentication you\'ll need a mobile application that supports TOTP such as Google Authenticator, Authy or Microsoft Authenticator.', + 'mfa_gen_totp_scan' => 'Scan the QR code below using your preferred authentication app to get started.', + 'mfa_gen_totp_verify_setup' => 'Verify Setup', + 'mfa_gen_totp_verify_setup_desc' => 'Verify that all is working by entering a code, generated within your authentication app, in the input box below:', + 'mfa_gen_totp_provide_code_here' => 'Provide your app generated code here', + 'mfa_verify_access' => 'Verify Access', + 'mfa_verify_access_desc' => 'Your user account requires you to confirm your identity via an additional level of verification before you\'re granted access. Verify using one of your configured methods to continue.', + 'mfa_verify_no_methods' => 'No Methods Configured', + 'mfa_verify_no_methods_desc' => 'No multi-factor authentication methods could be found for your account. You\'ll need to set up at least one method before you gain access.', + 'mfa_verify_use_totp' => 'Verify using a mobile app', + 'mfa_verify_use_backup_codes' => 'Verify using a backup code', + 'mfa_verify_backup_code' => 'Backup Code', + 'mfa_verify_backup_code_desc' => 'Enter one of your remaining backup codes below:', + 'mfa_verify_backup_code_enter_here' => 'Enter backup code here', + 'mfa_verify_totp_desc' => 'Enter the code, generated using your mobile app, below:', + 'mfa_setup_login_notification' => 'Multi-factor method configured, Please now login again using the configured method.', +]; diff --git a/lang/bn/common.php b/lang/bn/common.php new file mode 100644 index 00000000000..baf20426f01 --- /dev/null +++ b/lang/bn/common.php @@ -0,0 +1,115 @@ + 'প্রত্যাহার করুন', + 'close' => 'বন্ধ করুন', + 'confirm' => 'নিশ্চিত করুন', + 'back' => 'প্রত্যাবর্তন করুন', + 'save' => 'সংরক্ষণ করুন', + 'continue' => 'অগ্রসর হউন', + 'select' => 'নির্বাচন করুন', + 'toggle_all' => 'সবগুলোকে টগল করুন', + 'more' => 'বিস্তারিত', + + // Form Labels + 'name' => 'নাম', + 'description' => 'বিবরণ', + 'role' => 'রোল', + 'cover_image' => 'প্রচ্ছদ ছবি', + 'cover_image_description' => 'এই চিত্রটি আনুমানিক 440x250px হওয়া বাঞ্চনীয়। ক্ষেত্রবিশেষে ও ব্যবহারকারীর ইন্টারফেসের সাথে মানানসই করে উপস্থাপন করার জন্যে প্রয়োজনে এর আকার পরিবর্তন করে প্রদর্শন করা হবে, যা প্রকৃত মাত্রা হতে ভিন্ন হবে৷', + + // Actions + 'actions' => 'কার্যক্রম', + 'view' => 'দেখুন', + 'view_all' => 'সব দেখুন', + 'new' => 'নতুন', + 'create' => 'তৈরী করুন', + 'update' => 'হালনাগাদ করুন', + 'edit' => 'সম্পাদন করুন', + 'archive' => 'Archive', + 'unarchive' => 'Un-Archive', + 'sort' => 'ক্রমান্বয় করুন', + 'move' => 'স্থানান্তর করুন', + 'copy' => 'অনুলিপি করুন', + 'reply' => 'প্রত্যুত্তর করুন', + 'delete' => 'মুছে ফেলুন', + 'delete_confirm' => 'মুছে ফেলা নিশ্চিত করুন', + 'search' => 'অনুসন্ধান করুন', + 'search_clear' => 'অনুসন্ধান পুনঃসূচনা করুন', + 'reset' => 'পুনঃসূচনা করুন', + 'remove' => 'অপসারণ করুন', + 'add' => 'যোগ করুন', + 'configure' => 'সংস্থাপন করুন', + 'manage' => 'ব্যবস্থাপনা করুন', + 'fullscreen' => 'ফুলস্ক্রিন', + 'favourite' => 'প্রিয় তালিকায় যুক্ত করুন', + 'unfavourite' => 'প্রিয় তালিকা হতে অপসারণ করুন', + 'next' => 'পরবর্তী', + 'previous' => 'পূর্ববর্তী', + 'filter_active' => 'Active Filter:', + 'filter_clear' => 'Clear Filter', + 'download' => 'Download', + 'open_in_tab' => 'Open in Tab', + 'open' => 'Open', + + // Sort Options + 'sort_options' => 'Sort Options', + 'sort_direction_toggle' => 'Sort Direction Toggle', + 'sort_ascending' => 'Sort Ascending', + 'sort_descending' => 'Sort Descending', + 'sort_name' => 'Name', + 'sort_default' => 'Default', + 'sort_created_at' => 'Created Date', + 'sort_updated_at' => 'Updated Date', + + // Misc + 'deleted_user' => 'Deleted User', + 'no_activity' => 'No activity to show', + 'no_items' => 'No items available', + 'back_to_top' => 'Back to top', + 'skip_to_main_content' => 'Skip to main content', + 'toggle_details' => 'Toggle Details', + 'toggle_thumbnails' => 'Toggle Thumbnails', + 'details' => 'Details', + 'grid_view' => 'Grid View', + 'list_view' => 'List View', + 'default' => 'Default', + 'breadcrumb' => 'Breadcrumb', + 'status' => 'অবস্থা', + 'status_active' => 'Active', + 'status_inactive' => 'নিষ্ক্রিয়', + 'never' => 'অভূতপূর্ব', + 'none' => 'None', + + // Header + 'homepage' => 'নীড়পাতা', + 'header_menu_expand' => 'হেডার মেন্যু প্রসারিত করুন', + 'profile_menu' => 'প্রোফাইল মেন্যু', + 'view_profile' => 'প্রোফাইল দেখুন', + 'edit_profile' => 'প্রোফাইল সম্পাদনা করুন', + 'dark_mode' => 'নৈশরূপ', + 'light_mode' => 'দিবারূপ', + 'global_search' => 'সকল স্থানে অনুসন্ধান', + + // Layout tabs + 'tab_info' => 'তথ্য', + 'tab_info_label' => 'ট্যাব: গৌণ তথ্য', + 'tab_content' => 'কনটেন্ট', + 'tab_content_label' => 'ট্যাব: মূখ্য তথ্য', + + // Email Content + 'email_action_help' => 'আপনার যদি ":actionText"-এ ক্লিক করতে সমস্যা হয়, তবে নিচের লিংকটি কপি করে আপনার ওয়েব ব্রাউজারে পেস্ট করুন:', + 'email_rights' => 'সর্বস্বত্ব সংরক্ষিত', + + // Footer Link Options + // Not directly used but available for convenience to users. + 'privacy_policy' => 'গোপনীয়তা নীতি', + 'terms_of_service' => 'পরিষেবার শর্তাবলী', + + // OpenSearch + 'opensearch_description' => 'অনুসন্ধান :appName', +]; diff --git a/lang/bn/components.php b/lang/bn/components.php new file mode 100644 index 00000000000..c33b1d0b791 --- /dev/null +++ b/lang/bn/components.php @@ -0,0 +1,46 @@ + 'Image Select', + 'image_list' => 'Image List', + 'image_details' => 'Image Details', + 'image_upload' => 'Upload Image', + 'image_intro' => 'Here you can select and manage images that have been previously uploaded to the system.', + 'image_intro_upload' => 'Upload a new image by dragging an image file into this window, or by using the "Upload Image" button above.', + 'image_all' => 'All', + 'image_all_title' => 'View all images', + 'image_book_title' => 'View images uploaded to this book', + 'image_page_title' => 'View images uploaded to this page', + 'image_search_hint' => 'Search by image name', + 'image_uploaded' => 'Uploaded :uploadedDate', + 'image_uploaded_by' => 'Uploaded by :userName', + 'image_uploaded_to' => 'Uploaded to :pageLink', + 'image_updated' => 'Updated :updateDate', + 'image_load_more' => 'Load More', + 'image_image_name' => 'Image Name', + 'image_delete_used' => 'This image is used in the pages below.', + 'image_delete_confirm_text' => 'Are you sure you want to delete this image?', + 'image_select_image' => 'Select Image', + 'image_dropzone' => 'Drop images or click here to upload', + 'image_dropzone_drop' => 'Drop images here to upload', + 'images_deleted' => 'Images Deleted', + 'image_preview' => 'Image Preview', + 'image_upload_success' => 'Image uploaded successfully', + 'image_update_success' => 'Image details successfully updated', + 'image_delete_success' => 'Image successfully deleted', + 'image_replace' => 'Replace Image', + 'image_replace_success' => 'Image file successfully updated', + 'image_rebuild_thumbs' => 'Regenerate Size Variations', + 'image_rebuild_thumbs_success' => 'Image size variations successfully rebuilt!', + + // Code Editor + 'code_editor' => 'Edit Code', + 'code_language' => 'Code Language', + 'code_content' => 'Code Content', + 'code_session_history' => 'Session History', + 'code_save' => 'Save Code', +]; diff --git a/lang/bn/editor.php b/lang/bn/editor.php new file mode 100644 index 00000000000..d8476eeaed2 --- /dev/null +++ b/lang/bn/editor.php @@ -0,0 +1,182 @@ + 'সাধারণ', + 'advanced' => 'উন্নত', + 'none' => 'অপ্রযোজ্য', + 'cancel' => 'প্রত্যাহার করুন', + 'save' => 'সংরক্ষণ করুন', + 'close' => 'বন্ধ করুন', + 'apply' => 'Apply', + 'undo' => 'প্রত্যাহার করুন', + 'redo' => 'পুনর্বহাল রাখুন', + 'left' => 'বাম', + 'center' => 'মধ্য', + 'right' => 'ডান', + 'top' => 'উপর', + 'middle' => 'মধ্য', + 'bottom' => 'নিচে', + 'width' => 'প্রস্থ', + 'height' => 'উচ্চতা', + 'More' => 'বিস্তারিত', + 'select' => 'নির্বাচন করুন...', + + // Toolbar + 'formats' => 'প্রকরণ', + 'header_large' => 'বড় হেডার', + 'header_medium' => 'মাঝারি হেডার', + 'header_small' => 'ছোট হেডার', + 'header_tiny' => 'ক্ষুদ্র হেডার', + 'paragraph' => 'প্যারাগ্রাফ', + 'blockquote' => 'ব্লককোট', + 'inline_code' => 'ইনলাইন কোড', + 'callouts' => 'কলআউট', + 'callout_information' => 'তথ্যমূলক', + 'callout_success' => 'সফলজনক', + 'callout_warning' => 'সতর্কতামূলক', + 'callout_danger' => 'বিপদজনক', + 'bold' => 'বোল্ড', + 'italic' => 'ইটালিক', + 'underline' => 'আন্ডারলাইন', + 'strikethrough' => 'স্ট্রাইকথ্রু', + 'superscript' => 'Superscript', + 'subscript' => 'Subscript', + 'text_color' => 'Text color', + 'highlight_color' => 'Highlight color', + 'custom_color' => 'Custom color', + 'remove_color' => 'Remove color', + 'background_color' => 'Background color', + 'align_left' => 'Align left', + 'align_center' => 'Align center', + 'align_right' => 'Align right', + 'align_justify' => 'Justify', + 'list_bullet' => 'Bullet list', + 'list_numbered' => 'Numbered list', + 'list_task' => 'Task list', + 'indent_increase' => 'Increase indent', + 'indent_decrease' => 'Decrease indent', + 'table' => 'Table', + 'insert_image' => 'Insert image', + 'insert_image_title' => 'Insert/Edit Image', + 'insert_link' => 'Insert/edit link', + 'insert_link_title' => 'Insert/Edit Link', + 'insert_horizontal_line' => 'Insert horizontal line', + 'insert_code_block' => 'Insert code block', + 'edit_code_block' => 'Edit code block', + 'insert_drawing' => 'Insert/edit drawing', + 'drawing_manager' => 'Drawing manager', + 'insert_media' => 'Insert/edit media', + 'insert_media_title' => 'Insert/Edit Media', + 'clear_formatting' => 'Clear formatting', + 'source_code' => 'Source code', + 'source_code_title' => 'Source Code', + 'fullscreen' => 'Fullscreen', + 'image_options' => 'Image options', + + // Tables + 'table_properties' => 'Table properties', + 'table_properties_title' => 'Table Properties', + 'delete_table' => 'Delete table', + 'table_clear_formatting' => 'Clear table formatting', + 'resize_to_contents' => 'Resize to contents', + 'row_header' => 'Row header', + 'insert_row_before' => 'Insert row before', + 'insert_row_after' => 'Insert row after', + 'delete_row' => 'Delete row', + 'insert_column_before' => 'Insert column before', + 'insert_column_after' => 'Insert column after', + 'delete_column' => 'Delete column', + 'table_cell' => 'Cell', + 'table_row' => 'Row', + 'table_column' => 'Column', + 'cell_properties' => 'Cell properties', + 'cell_properties_title' => 'Cell Properties', + 'cell_type' => 'Cell type', + 'cell_type_cell' => 'Cell', + 'cell_scope' => 'Scope', + 'cell_type_header' => 'Header cell', + 'merge_cells' => 'Merge cells', + 'split_cell' => 'Split cell', + 'table_row_group' => 'Row Group', + 'table_column_group' => 'Column Group', + 'horizontal_align' => 'Horizontal align', + 'vertical_align' => 'Vertical align', + 'border_width' => 'Border width', + 'border_style' => 'Border style', + 'border_color' => 'Border color', + 'row_properties' => 'Row properties', + 'row_properties_title' => 'Row Properties', + 'cut_row' => 'Cut row', + 'copy_row' => 'Copy row', + 'paste_row_before' => 'Paste row before', + 'paste_row_after' => 'Paste row after', + 'row_type' => 'Row type', + 'row_type_header' => 'Header', + 'row_type_body' => 'Body', + 'row_type_footer' => 'Footer', + 'alignment' => 'Alignment', + 'cut_column' => 'Cut column', + 'copy_column' => 'Copy column', + 'paste_column_before' => 'Paste column before', + 'paste_column_after' => 'Paste column after', + 'cell_padding' => 'Cell padding', + 'cell_spacing' => 'Cell spacing', + 'caption' => 'Caption', + 'show_caption' => 'Show caption', + 'constrain' => 'Constrain proportions', + 'cell_border_solid' => 'Solid', + 'cell_border_dotted' => 'Dotted', + 'cell_border_dashed' => 'Dashed', + 'cell_border_double' => 'Double', + 'cell_border_groove' => 'Groove', + 'cell_border_ridge' => 'Ridge', + 'cell_border_inset' => 'Inset', + 'cell_border_outset' => 'Outset', + 'cell_border_none' => 'None', + 'cell_border_hidden' => 'Hidden', + + // Images, links, details/summary & embed + 'source' => 'Source', + 'alt_desc' => 'Alternative description', + 'embed' => 'Embed', + 'paste_embed' => 'Paste your embed code below:', + 'url' => 'URL', + 'text_to_display' => 'Text to display', + 'title' => 'Title', + 'browse_links' => 'Browse links', + 'open_link' => 'Open link', + 'open_link_in' => 'Open link in...', + 'open_link_current' => 'Current window', + 'open_link_new' => 'New window', + 'remove_link' => 'Remove link', + 'insert_collapsible' => 'Insert collapsible block', + 'collapsible_unwrap' => 'Unwrap', + 'edit_label' => 'Edit label', + 'toggle_open_closed' => 'Toggle open/closed', + 'collapsible_edit' => 'Edit collapsible block', + 'toggle_label' => 'Toggle label', + + // About view + 'about' => 'About the editor', + 'about_title' => 'About the WYSIWYG Editor', + 'editor_license' => 'Editor License & Copyright', + 'editor_lexical_license' => 'This editor is built as a fork of :lexicalLink which is distributed under the MIT license.', + 'editor_lexical_license_link' => 'Full license details can be found here.', + 'editor_tiny_license' => 'This editor is built using :tinyLink which is provided under the MIT license.', + 'editor_tiny_license_link' => 'The copyright and license details of TinyMCE can be found here.', + 'save_continue' => 'Save Page & Continue', + 'callouts_cycle' => '(Keep pressing to toggle through types)', + 'link_selector' => 'Link to content', + 'shortcuts' => 'Shortcuts', + 'shortcut' => 'Shortcut', + 'shortcuts_intro' => 'The following shortcuts are available in the editor:', + 'windows_linux' => '(Windows/Linux)', + 'mac' => '(Mac)', + 'description' => 'Description', +]; diff --git a/lang/bn/entities.php b/lang/bn/entities.php new file mode 100644 index 00000000000..74c50be3b2f --- /dev/null +++ b/lang/bn/entities.php @@ -0,0 +1,476 @@ + 'Recently Created', + 'recently_created_pages' => 'Recently Created Pages', + 'recently_updated_pages' => 'Recently Updated Pages', + 'recently_created_chapters' => 'Recently Created Chapters', + 'recently_created_books' => 'Recently Created Books', + 'recently_created_shelves' => 'Recently Created Shelves', + 'recently_update' => 'Recently Updated', + 'recently_viewed' => 'Recently Viewed', + 'recent_activity' => 'Recent Activity', + 'create_now' => 'Create one now', + 'revisions' => 'Revisions', + 'meta_revision' => 'Revision #:revisionCount', + 'meta_created' => 'Created :timeLength', + 'meta_created_name' => 'Created :timeLength by :user', + 'meta_updated' => 'Updated :timeLength', + 'meta_updated_name' => 'Updated :timeLength by :user', + 'meta_owned_name' => 'Owned by :user', + 'meta_reference_count' => 'Referenced by :count item|Referenced by :count items', + 'entity_select' => 'Entity Select', + 'entity_select_lack_permission' => 'You don\'t have the required permissions to select this item', + 'images' => 'Images', + 'my_recent_drafts' => 'My Recent Drafts', + 'my_recently_viewed' => 'My Recently Viewed', + 'my_most_viewed_favourites' => 'My Most Viewed Favourites', + 'my_favourites' => 'My Favourites', + 'no_pages_viewed' => 'You have not viewed any pages', + 'no_pages_recently_created' => 'No pages have been recently created', + 'no_pages_recently_updated' => 'No pages have been recently updated', + 'export' => 'Export', + 'export_html' => 'Contained Web File', + 'export_pdf' => 'PDF File', + 'export_text' => 'Plain Text File', + 'export_md' => 'Markdown File', + 'export_zip' => 'Portable ZIP', + 'default_template' => 'Default Page Template', + 'default_template_explain' => 'Assign a page template that will be used as the default content for all pages created within this item. Keep in mind this will only be used if the page creator has view access to the chosen template page.', + 'default_template_select' => 'Select a template page', + 'import' => 'Import', + 'import_validate' => 'Validate Import', + 'import_desc' => 'Import books, chapters & pages using a portable zip export from the same, or a different, instance. Select a ZIP file to proceed. After the file has been uploaded and validated you\'ll be able to configure & confirm the import in the next view.', + '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_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_size' => ':size Import ZIP Size', + 'import_uploaded_at' => 'Uploaded :relativeTime', + 'import_uploaded_by' => 'Uploaded by', + 'import_location' => 'Import Location', + '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_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', + 'breadcrumb_siblings_for_book' => 'Navigate siblings for book', + 'breadcrumb_siblings_for_bookshelf' => 'Navigate siblings for shelf', + + // Permissions and restrictions + 'permissions' => 'Permissions', + 'permissions_desc' => 'Set permissions here to override the default permissions provided by user roles.', + 'permissions_book_cascade' => 'Permissions set on books will automatically cascade to child chapters and pages, unless they have their own permissions defined.', + 'permissions_chapter_cascade' => 'Permissions set on chapters will automatically cascade to child pages, unless they have their own permissions defined.', + 'permissions_save' => 'Save Permissions', + 'permissions_owner' => 'Owner', + 'permissions_role_everyone_else' => 'Everyone Else', + 'permissions_role_everyone_else_desc' => 'Set permissions for all roles not specifically overridden.', + 'permissions_role_override' => 'Override permissions for role', + 'permissions_inherit_defaults' => 'Inherit defaults', + + // Search + 'search_results' => 'Search Results', + 'search_total_results_found' => ':count result found|:count total results found', + 'search_clear' => 'Clear Search', + 'search_no_pages' => 'No pages matched this search', + 'search_for_term' => 'Search for :term', + 'search_more' => 'More Results', + 'search_advanced' => 'Advanced Search', + 'search_terms' => 'Search Terms', + 'search_content_type' => 'Content Type', + 'search_exact_matches' => 'Exact Matches', + 'search_tags' => 'Tag Searches', + 'search_options' => 'Options', + 'search_viewed_by_me' => 'Viewed by me', + 'search_not_viewed_by_me' => 'Not viewed by me', + 'search_permissions_set' => 'Permissions set', + 'search_created_by_me' => 'Created by me', + 'search_updated_by_me' => 'Updated by me', + 'search_owned_by_me' => 'Owned by me', + 'search_date_options' => 'Date Options', + 'search_updated_before' => 'Updated before', + 'search_updated_after' => 'Updated after', + 'search_created_before' => 'Created before', + 'search_created_after' => 'Created after', + 'search_set_date' => 'Set Date', + 'search_update' => 'Update Search', + + // Shelves + 'shelf' => 'Shelf', + 'shelves' => 'Shelves', + 'x_shelves' => ':count Shelf|:count Shelves', + 'shelves_empty' => 'No shelves have been created', + 'shelves_create' => 'Create New Shelf', + 'shelves_popular' => 'Popular Shelves', + 'shelves_new' => 'New Shelves', + 'shelves_new_action' => 'New Shelf', + 'shelves_popular_empty' => 'The most popular shelves will appear here.', + 'shelves_new_empty' => 'The most recently created shelves will appear here.', + 'shelves_save' => 'Save Shelf', + 'shelves_books' => 'Books on this shelf', + 'shelves_add_books' => 'Add books to this shelf', + 'shelves_drag_books' => 'Drag books below to add them to this shelf', + 'shelves_empty_contents' => 'This shelf has no books assigned to it', + 'shelves_edit_and_assign' => 'Edit shelf to assign books', + 'shelves_edit_named' => 'Edit Shelf :name', + 'shelves_edit' => 'Edit Shelf', + 'shelves_delete' => 'Delete Shelf', + 'shelves_delete_named' => 'Delete Shelf :name', + 'shelves_delete_explain' => "This will delete the shelf with the name ':name'. Contained books will not be deleted.", + 'shelves_delete_confirmation' => 'Are you sure you want to delete this shelf?', + 'shelves_permissions' => 'Shelf Permissions', + 'shelves_permissions_updated' => 'Shelf Permissions Updated', + 'shelves_permissions_active' => 'Shelf Permissions Active', + 'shelves_permissions_cascade_warning' => 'Permissions on shelves do not automatically cascade to contained books. This is because a book can exist on multiple shelves. Permissions can however be copied down to child books using the option found below.', + 'shelves_permissions_create' => 'Shelf create permissions are only used for copying permissions to child books using the action below. They do not control the ability to create books.', + 'shelves_copy_permissions_to_books' => 'Copy Permissions to Books', + 'shelves_copy_permissions' => 'Copy Permissions', + 'shelves_copy_permissions_explain' => 'This will apply the current permission settings of this shelf to all books contained within. Before activating, ensure any changes to the permissions of this shelf have been saved.', + 'shelves_copy_permission_success' => 'Shelf permissions copied to :count books', + + // Books + 'book' => 'Book', + 'books' => 'Books', + 'x_books' => ':count Book|:count Books', + 'books_empty' => 'No books have been created', + 'books_popular' => 'Popular Books', + 'books_recent' => 'Recent Books', + 'books_new' => 'New Books', + 'books_new_action' => 'New Book', + 'books_popular_empty' => 'The most popular books will appear here.', + 'books_new_empty' => 'The most recently created books will appear here.', + 'books_create' => 'Create New Book', + 'books_delete' => 'Delete Book', + 'books_delete_named' => 'Delete Book :bookName', + 'books_delete_explain' => 'This will delete the book with the name \':bookName\'. All pages and chapters will be removed.', + 'books_delete_confirmation' => 'Are you sure you want to delete this book?', + 'books_edit' => 'Edit Book', + 'books_edit_named' => 'Edit Book :bookName', + 'books_form_book_name' => 'Book Name', + 'books_save' => 'Save Book', + 'books_permissions' => 'Book Permissions', + 'books_permissions_updated' => 'Book Permissions Updated', + 'books_empty_contents' => 'No pages or chapters have been created for this book.', + 'books_empty_create_page' => 'Create a new page', + 'books_empty_sort_current_book' => 'Sort the current book', + 'books_empty_add_chapter' => 'Add a chapter', + 'books_permissions_active' => 'Book Permissions Active', + 'books_search_this' => 'Search this book', + 'books_navigation' => 'Book Navigation', + 'books_sort' => 'Sort Book Contents', + 'books_sort_desc' => 'Move chapters and pages within a book to reorganise its contents. Other books can be added which allows easy moving of chapters and pages between books. Optionally an auto sort rule can be set to automatically sort this book\'s contents upon changes.', + 'books_sort_auto_sort' => 'Auto Sort Option', + 'books_sort_auto_sort_active' => 'Auto Sort Active: :sortName', + 'books_sort_named' => 'Sort Book :bookName', + 'books_sort_name' => 'Sort by Name', + 'books_sort_created' => 'Sort by Created Date', + 'books_sort_updated' => 'Sort by Updated Date', + 'books_sort_chapters_first' => 'Chapters First', + 'books_sort_chapters_last' => 'Chapters Last', + 'books_sort_show_other' => 'Show Other Books', + 'books_sort_save' => 'Save New Order', + 'books_sort_show_other_desc' => 'Add other books here to include them in the sort operation, and allow easy cross-book reorganisation.', + 'books_sort_move_up' => 'Move Up', + 'books_sort_move_down' => 'Move Down', + 'books_sort_move_prev_book' => 'Move to Previous Book', + 'books_sort_move_next_book' => 'Move to Next Book', + 'books_sort_move_prev_chapter' => 'Move Into Previous Chapter', + 'books_sort_move_next_chapter' => 'Move Into Next Chapter', + 'books_sort_move_book_start' => 'Move to Start of Book', + 'books_sort_move_book_end' => 'Move to End of Book', + 'books_sort_move_before_chapter' => 'Move to Before Chapter', + 'books_sort_move_after_chapter' => 'Move to After Chapter', + 'books_copy' => 'Copy Book', + 'books_copy_success' => 'Book successfully copied', + + // Chapters + 'chapter' => 'Chapter', + 'chapters' => 'Chapters', + 'x_chapters' => ':count Chapter|:count Chapters', + 'chapters_popular' => 'Popular Chapters', + 'chapters_new' => 'New Chapter', + 'chapters_create' => 'Create New Chapter', + 'chapters_delete' => 'Delete Chapter', + 'chapters_delete_named' => 'Delete Chapter :chapterName', + 'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages that exist within this chapter will also be deleted.', + 'chapters_delete_confirm' => 'Are you sure you want to delete this chapter?', + 'chapters_edit' => 'Edit Chapter', + 'chapters_edit_named' => 'Edit Chapter :chapterName', + 'chapters_save' => 'Save Chapter', + 'chapters_move' => 'Move Chapter', + 'chapters_move_named' => 'Move Chapter :chapterName', + 'chapters_copy' => 'Copy Chapter', + 'chapters_copy_success' => 'Chapter successfully copied', + 'chapters_permissions' => 'Chapter Permissions', + 'chapters_empty' => 'No pages are currently in this chapter.', + 'chapters_permissions_active' => 'Chapter Permissions Active', + 'chapters_permissions_success' => 'Chapter Permissions Updated', + 'chapters_search_this' => 'Search this chapter', + 'chapter_sort_book' => 'Sort Book', + + // Pages + 'page' => 'Page', + 'pages' => 'Pages', + 'x_pages' => ':count Page|:count Pages', + 'pages_popular' => 'Popular Pages', + 'pages_new' => 'New Page', + 'pages_attachments' => 'Attachments', + 'pages_navigation' => 'Page Navigation', + 'pages_delete' => 'Delete Page', + 'pages_delete_named' => 'Delete Page :pageName', + 'pages_delete_draft_named' => 'Delete Draft Page :pageName', + 'pages_delete_draft' => 'Delete Draft Page', + 'pages_delete_success' => 'Page deleted', + 'pages_delete_draft_success' => 'Draft page deleted', + 'pages_delete_warning_template' => 'This page is in active use as a book or chapter default page template. These books or chapters will no longer have a default page template assigned after this page is deleted.', + 'pages_delete_confirm' => 'Are you sure you want to delete this page?', + 'pages_delete_draft_confirm' => 'Are you sure you want to delete this draft page?', + 'pages_editing_named' => 'Editing Page :pageName', + 'pages_edit_draft_options' => 'Draft Options', + 'pages_edit_save_draft' => 'Save Draft', + 'pages_edit_draft' => 'Edit Page Draft', + 'pages_editing_draft' => 'Editing Draft', + 'pages_editing_page' => 'Editing Page', + 'pages_edit_draft_save_at' => 'Draft saved at ', + 'pages_edit_delete_draft' => 'Delete Draft', + 'pages_edit_delete_draft_confirm' => 'Are you sure you want to delete your draft page changes? All of your changes, since the last full save, will be lost and the editor will be updated with the latest page non-draft save state.', + 'pages_edit_discard_draft' => 'Discard Draft', + 'pages_edit_switch_to_markdown' => 'Switch to Markdown Editor', + 'pages_edit_switch_to_markdown_clean' => '(Clean Content)', + 'pages_edit_switch_to_markdown_stable' => '(Stable Content)', + 'pages_edit_switch_to_wysiwyg' => 'Switch to WYSIWYG Editor', + 'pages_edit_switch_to_new_wysiwyg' => 'Switch to new WYSIWYG', + 'pages_edit_switch_to_new_wysiwyg_desc' => '(In Beta Testing)', + 'pages_edit_set_changelog' => 'Set Changelog', + 'pages_edit_enter_changelog_desc' => 'Enter a brief description of the changes you\'ve made', + 'pages_edit_enter_changelog' => 'Enter Changelog', + 'pages_editor_switch_title' => 'Switch Editor', + 'pages_editor_switch_are_you_sure' => 'Are you sure you want to change the editor for this page?', + 'pages_editor_switch_consider_following' => 'Consider the following when changing editors:', + 'pages_editor_switch_consideration_a' => 'Once saved, the new editor option will be used by any future editors, including those that may not be able to change editor type themselves.', + 'pages_editor_switch_consideration_b' => 'This can potentially lead to a loss of detail and syntax in certain circumstances.', + 'pages_editor_switch_consideration_c' => 'Tag or changelog changes, made since last save, won\'t persist across this change.', + 'pages_save' => 'Save Page', + 'pages_title' => 'Page Title', + 'pages_name' => 'Page Name', + 'pages_md_editor' => 'Editor', + 'pages_md_preview' => 'Preview', + 'pages_md_insert_image' => 'Insert Image', + 'pages_md_insert_link' => 'Insert Entity Link', + 'pages_md_insert_drawing' => 'Insert Drawing', + 'pages_md_show_preview' => 'Show preview', + 'pages_md_sync_scroll' => 'Sync preview scroll', + 'pages_md_plain_editor' => 'Plaintext editor', + 'pages_drawing_unsaved' => 'Unsaved Drawing Found', + 'pages_drawing_unsaved_confirm' => 'Unsaved drawing data was found from a previous failed drawing save attempt. Would you like to restore and continue editing this unsaved drawing?', + 'pages_not_in_chapter' => 'Page is not in a chapter', + 'pages_move' => 'Move Page', + 'pages_copy' => 'Copy Page', + 'pages_copy_desination' => 'Copy Destination', + 'pages_copy_success' => 'Page successfully copied', + 'pages_permissions' => 'Page Permissions', + 'pages_permissions_success' => 'Page permissions updated', + 'pages_revision' => 'Revision', + 'pages_revisions' => 'Page Revisions', + 'pages_revisions_desc' => 'Listed below are all the past revisions of this page. You can look back upon, compare, and restore old page versions if permissions allow. The full history of the page may not be fully reflected here since, depending on system configuration, old revisions could be auto-deleted.', + 'pages_revisions_named' => 'Page Revisions for :pageName', + 'pages_revision_named' => 'Page Revision for :pageName', + 'pages_revision_restored_from' => 'Restored from #:id; :summary', + 'pages_revisions_created_by' => 'Created By', + 'pages_revisions_date' => 'Revision Date', + 'pages_revisions_number' => '#', + 'pages_revisions_sort_number' => 'Revision Number', + 'pages_revisions_numbered' => 'Revision #:id', + 'pages_revisions_numbered_changes' => 'Revision #:id Changes', + 'pages_revisions_editor' => 'Editor Type', + 'pages_revisions_changelog' => 'Changelog', + 'pages_revisions_changes' => 'Changes', + 'pages_revisions_current' => 'Current Version', + 'pages_revisions_preview' => 'Preview', + 'pages_revisions_restore' => 'Restore', + 'pages_revisions_none' => 'This page has no revisions', + 'pages_copy_link' => 'Copy Link', + 'pages_edit_content_link' => 'Jump to section in editor', + 'pages_pointer_enter_mode' => 'Enter section select mode', + 'pages_pointer_label' => 'Page Section Options', + 'pages_pointer_permalink' => 'Page Section Permalink', + 'pages_pointer_include_tag' => 'Page Section Include Tag', + 'pages_pointer_toggle_link' => 'Permalink mode, Press to show include tag', + 'pages_pointer_toggle_include' => 'Include tag mode, Press to show permalink', + 'pages_permissions_active' => 'Page Permissions Active', + 'pages_initial_revision' => 'Initial publish', + 'pages_references_update_revision' => 'System auto-update of internal links', + 'pages_initial_name' => 'New Page', + 'pages_editing_draft_notification' => 'You are currently editing a draft that was last saved :timeDiff.', + 'pages_draft_edited_notification' => 'This page has been updated by since that time. It is recommended that you discard this draft.', + 'pages_draft_page_changed_since_creation' => 'This page has been updated since this draft was created. It is recommended that you discard this draft or take care not to overwrite any page changes.', + 'pages_draft_edit_active' => [ + 'start_a' => ':count users have started editing this page', + 'start_b' => ':userName has started editing this page', + 'time_a' => 'since the page was last updated', + 'time_b' => 'in the last :minCount minutes', + 'message' => ':start :time. Take care not to overwrite each other\'s updates!', + ], + 'pages_draft_discarded' => 'Draft discarded! The editor has been updated with the current page content', + 'pages_draft_deleted' => 'Draft deleted! The editor has been updated with the current page content', + 'pages_specific' => 'Specific Page', + 'pages_is_template' => 'Page Template', + + // Editor Sidebar + 'toggle_sidebar' => 'Toggle Sidebar', + 'page_tags' => 'Page Tags', + 'chapter_tags' => 'Chapter Tags', + 'book_tags' => 'Book Tags', + 'shelf_tags' => 'Shelf Tags', + 'tag' => 'Tag', + 'tags' => 'Tags', + 'tags_index_desc' => 'Tags can be applied to content within the system to apply a flexible form of categorization. Tags can have both a key and value, with the value being optional. Once applied, content can then be queried using the tag name and value.', + 'tag_name' => 'Tag Name', + 'tag_value' => 'Tag Value (Optional)', + 'tags_explain' => "Add some tags to better categorise your content. \n You can assign a value to a tag for more in-depth organisation.", + 'tags_add' => 'Add another tag', + 'tags_remove' => 'Remove this tag', + 'tags_usages' => 'Total tag usages', + 'tags_assigned_pages' => 'Assigned to Pages', + 'tags_assigned_chapters' => 'Assigned to Chapters', + 'tags_assigned_books' => 'Assigned to Books', + 'tags_assigned_shelves' => 'Assigned to Shelves', + 'tags_x_unique_values' => ':count unique values', + 'tags_all_values' => 'All values', + 'tags_view_tags' => 'View Tags', + 'tags_view_existing_tags' => 'View existing tags', + 'tags_list_empty_hint' => 'Tags can be assigned via the page editor sidebar or while editing the details of a book, chapter or shelf.', + 'attachments' => 'Attachments', + 'attachments_explain' => 'Upload some files or attach some links to display on your page. These are visible in the page sidebar.', + 'attachments_explain_instant_save' => 'Changes here are saved instantly.', + 'attachments_upload' => 'Upload File', + 'attachments_link' => 'Attach Link', + 'attachments_upload_drop' => 'Alternatively you can drag and drop a file here to upload it as an attachment.', + 'attachments_set_link' => 'Set Link', + 'attachments_delete' => 'Are you sure you want to delete this attachment?', + 'attachments_dropzone' => 'Drop files here to upload', + 'attachments_no_files' => 'No files have been uploaded', + 'attachments_explain_link' => 'You can attach a link if you\'d prefer not to upload a file. This can be a link to another page or a link to a file in the cloud.', + 'attachments_link_name' => 'Link Name', + 'attachment_link' => 'Attachment link', + 'attachments_link_url' => 'Link to file', + 'attachments_link_url_hint' => 'Url of site or file', + 'attach' => 'Attach', + 'attachments_insert_link' => 'Add Attachment Link to Page', + 'attachments_edit_file' => 'Edit File', + 'attachments_edit_file_name' => 'File Name', + 'attachments_edit_drop_upload' => 'Drop files or click here to upload and overwrite', + 'attachments_order_updated' => 'Attachment order updated', + 'attachments_updated_success' => 'Attachment details updated', + 'attachments_deleted' => 'Attachment deleted', + 'attachments_file_uploaded' => 'File successfully uploaded', + 'attachments_file_updated' => 'File successfully updated', + 'attachments_link_attached' => 'Link successfully attached to page', + 'templates' => 'Templates', + 'templates_set_as_template' => 'Page is a template', + 'templates_explain_set_as_template' => 'You can set this page as a template so its contents be utilized when creating other pages. Other users will be able to use this template if they have view permissions for this page.', + 'templates_replace_content' => 'Replace page content', + 'templates_append_content' => 'Append to page content', + 'templates_prepend_content' => 'Prepend to page content', + + // Profile View + 'profile_user_for_x' => 'User for :time', + 'profile_created_content' => 'Created Content', + 'profile_not_created_pages' => ':userName has not created any pages', + 'profile_not_created_chapters' => ':userName has not created any chapters', + 'profile_not_created_books' => ':userName has not created any books', + 'profile_not_created_shelves' => ':userName has not created any shelves', + + // Comments + 'comment' => 'Comment', + 'comments' => 'Comments', + 'comment_add' => 'Add Comment', + 'comment_none' => 'No comments to display', + 'comment_placeholder' => 'Leave a comment here', + 'comment_thread_count' => ':count Comment Thread|:count Comment Threads', + 'comment_archived_count' => ':count Archived', + 'comment_archived_threads' => 'Archived Threads', + 'comment_save' => 'Save Comment', + 'comment_new' => 'New Comment', + 'comment_created' => 'commented :createDiff', + 'comment_updated' => 'Updated :updateDiff by :username', + 'comment_updated_indicator' => 'Updated', + 'comment_deleted_success' => 'Comment deleted', + 'comment_created_success' => 'Comment added', + 'comment_updated_success' => 'Comment updated', + 'comment_archive_success' => 'Comment archived', + 'comment_unarchive_success' => 'Comment un-archived', + 'comment_view' => 'View comment', + 'comment_jump_to_thread' => 'Jump to thread', + 'comment_delete_confirm' => 'Are you sure you want to delete this comment?', + 'comment_in_reply_to' => 'In reply to :commentId', + 'comment_reference' => 'Reference', + 'comment_reference_outdated' => '(Outdated)', + 'comment_editor_explain' => 'Here are the comments that have been left on this page. Comments can be added & managed when viewing the saved page.', + + // Revision + 'revision_delete_confirm' => 'Are you sure you want to delete this revision?', + 'revision_restore_confirm' => 'Are you sure you want to restore this revision? The current page contents will be replaced.', + 'revision_cannot_delete_latest' => 'Cannot delete the latest revision.', + + // Copy view + 'copy_consider' => 'Please consider the below when copying content.', + 'copy_consider_permissions' => 'Custom permission settings will not be copied.', + 'copy_consider_owner' => 'You will become the owner of all copied content.', + 'copy_consider_images' => 'Page image files will not be duplicated & the original images will retain their relation to the page they were originally uploaded to.', + 'copy_consider_attachments' => 'Page attachments will not be copied.', + 'copy_consider_access' => 'A change of location, owner or permissions may result in this content being accessible to those previously without access.', + + // Conversions + 'convert_to_shelf' => 'Convert to Shelf', + 'convert_to_shelf_contents_desc' => 'You can convert this book to a new shelf with the same contents. Chapters contained within this book will be converted to new books. If this book contains any pages, that are not in a chapter, this book will be renamed and contain such pages, and this book will become part of the new shelf.', + 'convert_to_shelf_permissions_desc' => 'Any permissions set on this book will be copied to the new shelf and to all new child books that don\'t have their own permissions enforced. Note that permissions on shelves do not auto-cascade to content within, as they do for books.', + 'convert_book' => 'Convert Book', + 'convert_book_confirm' => 'Are you sure you want to convert this book?', + 'convert_undo_warning' => 'This cannot be as easily undone.', + 'convert_to_book' => 'Convert to Book', + 'convert_to_book_desc' => 'You can convert this chapter to a new book with the same contents. Any permissions set on this chapter will be copied to the new book but any inherited permissions, from the parent book, will not be copied which could lead to a change of access control.', + 'convert_chapter' => 'Convert Chapter', + 'convert_chapter_confirm' => 'Are you sure you want to convert this chapter?', + + // References + 'references' => 'References', + 'references_none' => 'There are no tracked references to this item.', + 'references_to_desc' => 'Listed below is all the known content in the system that links to this item.', + + // Watch Options + 'watch' => 'Watch', + 'watch_title_default' => 'Default Preferences', + 'watch_desc_default' => 'Revert watching to just your default notification preferences.', + 'watch_title_ignore' => 'Ignore', + 'watch_desc_ignore' => 'Ignore all notifications, including those from user-level preferences.', + 'watch_title_new' => 'New Pages', + 'watch_desc_new' => 'Notify when any new page is created within this item.', + 'watch_title_updates' => 'All Page Updates', + 'watch_desc_updates' => 'Notify upon all new pages and page changes.', + 'watch_desc_updates_page' => 'Notify upon all page changes.', + 'watch_title_comments' => 'All Page Updates & Comments', + 'watch_desc_comments' => 'Notify upon all new pages, page changes and new comments.', + 'watch_desc_comments_page' => 'Notify upon page changes and new comments.', + 'watch_change_default' => 'Change default notification preferences', + 'watch_detail_ignore' => 'Ignoring notifications', + 'watch_detail_new' => 'Watching for new pages', + 'watch_detail_updates' => 'Watching new pages and updates', + 'watch_detail_comments' => 'Watching new pages, updates & comments', + 'watch_detail_parent_book' => 'Watching via parent book', + 'watch_detail_parent_book_ignore' => 'Ignoring via parent book', + 'watch_detail_parent_chapter' => 'Watching via parent chapter', + 'watch_detail_parent_chapter_ignore' => 'Ignoring via parent chapter', +]; diff --git a/lang/bn/errors.php b/lang/bn/errors.php new file mode 100644 index 00000000000..ee2fbfa2100 --- /dev/null +++ b/lang/bn/errors.php @@ -0,0 +1,133 @@ + 'অনুরোধকৃত পৃষ্ঠাটিতে আপনার ব্যবহারাধিকারের অনুমতি নেই।', + 'permissionJson' => 'You do not have permission to perform the requested action.', + + // Auth + 'error_user_exists_different_creds' => 'A user with the email :email already exists but with different credentials.', + 'auth_pre_register_theme_prevention' => 'User account could not be registered for the provided details', + 'email_already_confirmed' => 'Email has already been confirmed, Try logging in.', + 'email_confirmation_invalid' => 'This confirmation token is not valid or has already been used, Please try registering again.', + 'email_confirmation_expired' => 'The confirmation token has expired, A new confirmation email has been sent.', + 'email_confirmation_awaiting' => 'The email address for the account in use needs to be confirmed', + 'ldap_fail_anonymous' => 'LDAP access failed using anonymous bind', + 'ldap_fail_authed' => 'LDAP access failed using given dn & password details', + 'ldap_extension_not_installed' => 'LDAP PHP extension not installed', + 'ldap_cannot_connect' => 'Cannot connect to ldap server, Initial connection failed', + 'saml_already_logged_in' => 'Already logged in', + 'saml_no_email_address' => 'Could not find an email address, for this user, in the data provided by the external authentication system', + 'saml_invalid_response_id' => 'The request from the external authentication system is not recognised by a process started by this application. Navigating back after a login could cause this issue.', + 'saml_fail_authed' => 'Login using :system failed, system did not provide successful authorization', + 'oidc_already_logged_in' => 'Already logged in', + 'oidc_no_email_address' => 'Could not find an email address, for this user, in the data provided by the external authentication system', + 'oidc_fail_authed' => 'Login using :system failed, system did not provide successful authorization', + 'social_no_action_defined' => 'No action defined', + 'social_login_bad_response' => "Error received during :socialAccount login: \n:error", + 'social_account_in_use' => 'This :socialAccount account is already in use, Try logging in via the :socialAccount option.', + 'social_account_email_in_use' => 'The email :email is already in use. If you already have an account you can connect your :socialAccount account from your profile settings.', + 'social_account_existing' => 'This :socialAccount is already attached to your profile.', + 'social_account_already_used_existing' => 'This :socialAccount account is already used by another user.', + 'social_account_not_used' => 'This :socialAccount account is not linked to any users. Please attach it in your profile settings. ', + 'social_account_register_instructions' => 'If you do not yet have an account, You can register an account using the :socialAccount option.', + 'social_driver_not_found' => 'Social driver not found', + 'social_driver_not_configured' => 'Your :socialAccount social settings are not configured correctly.', + 'invite_token_expired' => 'This invitation link has expired. You can instead try to reset your account password.', + 'login_user_not_found' => 'A user for this action could not be found.', + + // System + 'path_not_writable' => 'File path :filePath could not be uploaded to. Ensure it is writable to the server.', + 'cannot_get_image_from_url' => 'Cannot get image from :url', + 'cannot_create_thumbs' => 'The server cannot create thumbnails. Please check you have the GD PHP extension installed.', + 'server_upload_limit' => 'The server does not allow uploads of this size. Please try a smaller file size.', + 'server_post_limit' => 'The server cannot receive the provided amount of data. Try again with less data or a smaller file.', + 'uploaded' => 'The server does not allow uploads of this size. Please try a smaller file size.', + + // Drawing & Images + 'image_upload_error' => 'An error occurred uploading the image', + 'image_upload_type_error' => 'The image type being uploaded is invalid', + 'image_upload_replace_type' => 'Image file replacements must be of the same type', + 'image_upload_memory_limit' => 'Failed to handle image upload and/or create thumbnails due to system resource limits.', + 'image_thumbnail_memory_limit' => 'Failed to create image size variations due to system resource limits.', + 'image_gallery_thumbnail_memory_limit' => 'Failed to create gallery thumbnails due to system resource limits.', + 'drawing_data_not_found' => 'Drawing data could not be loaded. The drawing file might no longer exist or you may not have permission to access it.', + + // Attachments + 'attachment_not_found' => 'Attachment not found', + 'attachment_upload_error' => 'An error occurred uploading the attachment file', + + // Pages + 'page_draft_autosave_fail' => 'Failed to save draft. Ensure you have internet connection before saving this page', + 'page_draft_delete_fail' => 'Failed to delete page draft and fetch current page saved content', + 'page_custom_home_deletion' => 'Cannot delete a page while it is set as a homepage', + + // Entities + 'entity_not_found' => 'Entity not found', + 'bookshelf_not_found' => 'Shelf not found', + 'book_not_found' => 'Book not found', + 'page_not_found' => 'Page not found', + 'chapter_not_found' => 'Chapter not found', + 'selected_book_not_found' => 'The selected book was not found', + 'selected_book_chapter_not_found' => 'The selected Book or Chapter was not found', + 'guests_cannot_save_drafts' => 'Guests cannot save drafts', + + // Users + 'users_cannot_delete_only_admin' => 'You cannot delete the only admin', + 'users_cannot_delete_guest' => 'You cannot delete the guest user', + 'users_could_not_send_invite' => 'Could not create user since invite email failed to send', + + // Roles + 'role_cannot_be_edited' => 'This role cannot be edited', + 'role_system_cannot_be_deleted' => 'This role is a system role and cannot be deleted', + 'role_registration_default_cannot_delete' => 'This role cannot be deleted while set as the default registration role', + 'role_cannot_remove_only_admin' => 'This user is the only user assigned to the administrator role. Assign the administrator role to another user before attempting to remove it here.', + + // Comments + 'comment_list' => 'An error occurred while fetching the comments.', + 'cannot_add_comment_to_draft' => 'You cannot add comments to a draft.', + 'comment_add' => 'An error occurred while adding / updating the comment.', + 'comment_delete' => 'An error occurred while deleting the comment.', + 'empty_comment' => 'Cannot add an empty comment.', + + // Error pages + '404_page_not_found' => 'Page Not Found', + 'sorry_page_not_found' => 'Sorry, The page you were looking for could not be found.', + 'sorry_page_not_found_permission_warning' => 'If you expected this page to exist, you might not have permission to view it.', + 'image_not_found' => 'Image Not Found', + 'image_not_found_subtitle' => 'Sorry, The image file you were looking for could not be found.', + 'image_not_found_details' => 'If you expected this image to exist it might have been deleted.', + 'return_home' => 'Return to home', + 'error_occurred' => 'An Error Occurred', + 'app_down' => ':appName is down right now', + 'back_soon' => 'It will be back up soon.', + + // Import + 'import_zip_cant_read' => 'Could not read ZIP file.', + 'import_zip_cant_decode_data' => 'Could not find and decode ZIP data.json content.', + 'import_zip_no_data' => 'ZIP file data has no expected book, chapter or page content.', + 'import_validation_failed' => 'Import ZIP failed to validate with errors:', + 'import_zip_failed_notification' => 'Failed to import ZIP file.', + 'import_perms_books' => 'You are lacking the required permissions to create books.', + 'import_perms_chapters' => 'You are lacking the required permissions to create chapters.', + 'import_perms_pages' => 'You are lacking the required permissions to create pages.', + 'import_perms_images' => 'You are lacking the required permissions to create images.', + 'import_perms_attachments' => 'You are lacking the required permission to create attachments.', + + // API errors + 'api_no_authorization_found' => 'No authorization token found on the request', + 'api_bad_authorization_format' => 'An authorization token was found on the request but the format appeared incorrect', + 'api_user_token_not_found' => 'No matching API token was found for the provided authorization token', + 'api_incorrect_token_secret' => 'The secret provided for the given used API token is incorrect', + 'api_user_no_api_permission' => 'The owner of the used API token does not have permission to make API calls', + 'api_user_token_expired' => 'The authorization token used has expired', + + // Settings & Maintenance + 'maintenance_test_email_failure' => 'Error thrown when sending a test email:', + + // HTTP errors + 'http_ssr_url_no_match' => 'The URL does not match the configured allowed SSR hosts', +]; diff --git a/lang/bn/notifications.php b/lang/bn/notifications.php new file mode 100644 index 00000000000..1afd23f1dc4 --- /dev/null +++ b/lang/bn/notifications.php @@ -0,0 +1,27 @@ + 'New comment on page: :pageName', + 'new_comment_intro' => 'A user has commented on a page in :appName:', + 'new_page_subject' => 'New page: :pageName', + 'new_page_intro' => 'A new page has been created in :appName:', + 'updated_page_subject' => 'Updated page: :pageName', + 'updated_page_intro' => 'A page has been updated in :appName:', + 'updated_page_debounce' => 'To prevent a mass of notifications, for a while you won\'t be sent notifications for further edits to this page by the same editor.', + + 'detail_page_name' => 'Page Name:', + 'detail_page_path' => 'Page Path:', + 'detail_commenter' => 'Commenter:', + 'detail_comment' => 'Comment:', + 'detail_created_by' => 'Created By:', + 'detail_updated_by' => 'Updated By:', + + 'action_view_comment' => 'View Comment', + 'action_view_page' => 'View Page', + + 'footer_reason' => 'This notification was sent to you because :link cover this type of activity for this item.', + 'footer_reason_link' => 'your notification preferences', +]; diff --git a/lang/bn/pagination.php b/lang/bn/pagination.php new file mode 100644 index 00000000000..2cf360a0fb6 --- /dev/null +++ b/lang/bn/pagination.php @@ -0,0 +1,12 @@ + '« পূর্ববর্তী', + 'next' => 'পরবর্তী »', + +]; diff --git a/lang/bn/passwords.php b/lang/bn/passwords.php new file mode 100644 index 00000000000..da318b1acc0 --- /dev/null +++ b/lang/bn/passwords.php @@ -0,0 +1,15 @@ + 'পাসওয়ার্ড কমপক্ষে আট অক্ষরের হতে হবে এবং পাসওয়ার্ড নিশ্চিতকরণের ঘরে প্রদত্ত পাসওয়ার্ডের সাথে মিলতে হবে।', + 'user' => "প্রদত্ত ইমেইল ঠিকানার স্বাপেক্ষে কোন ব্যবহারকারী খুঁজে পাওয়া যায়নি।", + 'token' => 'প্রদত্ত পাসওয়ার্ড রিসেট টোকেন অত্র ইমেল ঠিকানার জন্য বৈধ নয়৷', + 'sent' => 'আপনার পাসওয়ার্ড রিসেট লিঙ্কটি ই-মেইলের মাধ্যমে প্রেরণ করা হয়েছে!', + 'reset' => 'আপনার পাসওয়ার্ডটি রিসেট করা হয়েছে!', + +]; diff --git a/lang/bn/preferences.php b/lang/bn/preferences.php new file mode 100644 index 00000000000..2e47604e471 --- /dev/null +++ b/lang/bn/preferences.php @@ -0,0 +1,51 @@ + 'আমার অ্যাকাউন্ট', + + 'shortcuts' => 'Shortcuts', + 'shortcuts_interface' => 'UI Shortcut Preferences', + 'shortcuts_toggle_desc' => 'Here you can enable or disable keyboard system interface shortcuts, used for navigation and actions.', + 'shortcuts_customize_desc' => 'You can customize each of the shortcuts below. Just press your desired key combination after selecting the input for a shortcut.', + 'shortcuts_toggle_label' => 'Keyboard shortcuts enabled', + 'shortcuts_section_navigation' => 'Navigation', + 'shortcuts_section_actions' => 'Common Actions', + 'shortcuts_save' => 'Save Shortcuts', + 'shortcuts_overlay_desc' => 'Note: When shortcuts are enabled a helper overlay is available via pressing "?" which will highlight the available shortcuts for actions currently visible on the screen.', + 'shortcuts_update_success' => 'Shortcut preferences have been updated!', + 'shortcuts_overview_desc' => 'Manage keyboard shortcuts you can use to navigate the system user interface.', + + 'notifications' => 'Notification Preferences', + 'notifications_desc' => 'Control the email notifications you receive when certain activity is performed within the system.', + 'notifications_opt_own_page_changes' => 'Notify upon changes to pages I own', + 'notifications_opt_own_page_comments' => 'Notify upon comments on pages I own', + 'notifications_opt_comment_replies' => 'Notify upon replies to my comments', + 'notifications_save' => 'Save Preferences', + 'notifications_update_success' => 'Notification preferences have been updated!', + 'notifications_watched' => 'Watched & Ignored Items', + 'notifications_watched_desc' => 'Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.', + + 'auth' => 'Access & Security', + 'auth_change_password' => 'Change Password', + 'auth_change_password_desc' => 'Change the password you use to log-in to the application. This must be at least 8 characters long.', + 'auth_change_password_success' => 'Password has been updated!', + + 'profile' => 'Profile Details', + 'profile_desc' => 'Manage the details of your account which represents you to other users, in addition to details that are used for communication and system personalisation.', + 'profile_view_public' => 'View Public Profile', + 'profile_name_desc' => 'Configure your display name which will be visible to other users in the system through the activity you perform, and content you own.', + 'profile_email_desc' => 'This email will be used for notifications and, depending on active system authentication, system access.', + 'profile_email_no_permission' => 'Unfortunately you don\'t have permission to change your email address. If you want to change this, you\'d need to ask an administrator to change this for you.', + 'profile_avatar_desc' => 'Select an image which will be used to represent yourself to others in the system. Ideally this image should be square and about 256px in width and height.', + 'profile_admin_options' => 'Administrator Options', + 'profile_admin_options_desc' => 'Additional administrator-level options, like those to manage role assignments, can be found for your user account in the "Settings > Users" area of the application.', + + 'delete_account' => 'Delete Account', + 'delete_my_account' => 'Delete My Account', + 'delete_my_account_desc' => 'This will fully delete your user account from the system. You will not be able to recover this account or revert this action. Content you\'ve created, such as created pages and uploaded images, will remain.', + 'delete_my_account_warning' => 'Are you sure you want to delete your account?', +]; diff --git a/lang/bn/settings.php b/lang/bn/settings.php new file mode 100644 index 00000000000..04aca4f2e0f --- /dev/null +++ b/lang/bn/settings.php @@ -0,0 +1,370 @@ + 'সেটিংস', + 'settings_save' => 'সেটিংস সংরক্ষণ করুন', + 'system_version' => 'সিস্টেম ভার্সন', + 'categories' => 'শ্রেণীবিভাগ সমূহ', + + // App Settings + 'app_customization' => 'নিজস্বীকরণ', + 'app_features_security' => 'ফিচারসমূহ ও নিরাপত্তা', + 'app_name' => 'এপ্লিকেশনের নাম', + 'app_name_desc' => 'এই নামটি হেডারে এবং যেকোন সিস্টেম-প্রেরিত ইমেলে দেখানো হয়।', + 'app_name_header' => 'হেডারে নাম দেখান', + 'app_public_access' => 'পাবলিক এক্সেস', + 'app_public_access_desc' => 'উক্ত অপশনটি সক্রিয় করলে আপনার বুকস্ট্যাক ওয়েবসাইটের সকল তথ্য, যে কেউ লগ ইন করা ছাড়াই, দেখতে বা পড়তে অথবা এক্সেস করতে পারবেন।', + 'app_public_access_desc_guest' => '"Guest" ব্যবহারকারীর মাধ্যমে ওয়েবসাইট ভিসিটরদের পঠনধিকার নিয়ন্ত্রণ করা যেতে পারে।', + 'app_public_access_toggle' => 'পাবলিক অ্যাক্সেসের অনুমতি দিন', + 'app_public_viewing' => 'সকলের জন্যে উন্মুক্ত করতে চান?', + 'app_secure_images' => 'Higher Security Image Uploads', + 'app_secure_images_toggle' => 'Enable higher security image uploads', + 'app_secure_images_desc' => 'For performance reasons, all images are public. This option adds a random, hard-to-guess string in front of image urls. Ensure directory indexes are not enabled to prevent easy access.', + 'app_default_editor' => 'Default Page Editor', + 'app_default_editor_desc' => 'Select which editor will be used by default when editing new pages. This can be overridden at a page level where permissions allow.', + 'app_custom_html' => 'Custom HTML Head Content', + 'app_custom_html_desc' => 'Any content added here will be inserted into the bottom of the section of every page. This is handy for overriding styles or adding analytics code.', + 'app_custom_html_disabled_notice' => 'Custom HTML head content is disabled on this settings page to ensure any breaking changes can be reverted.', + 'app_logo' => 'অ্যাপ্লিকেশনের লোগো', + 'app_logo_desc' => 'এটি অ্যাপ্লিকেশনের হেডার বার-এ দেখানো হবে। উক্ত ছবিটির উচ্চতা সর্বোচ্চ ৮৬ পিক্সেলের হতে হবে। অধিকতর উচ্চতার ছবিকে স্কেল ডাউন করা হবে। ', + 'app_icon' => 'অ্যাপ্লিকেশনের আইকন', + 'app_icon_desc' => 'This icon is used for browser tabs and shortcut icons. This should be a 256px square PNG image.', + 'app_homepage' => 'অ্যাপ্লিকেশনের নীড়পাতা', + 'app_homepage_desc' => 'Select a view to show on the homepage instead of the default view. Page permissions are ignored for selected pages.', + 'app_homepage_select' => 'একটি পৃষ্ঠা নির্বাচন করুন', + 'app_footer_links' => 'ফুটার লিঙ্কসমূহ', + 'app_footer_links_desc' => 'Add links to show within the site footer. These will be displayed at the bottom of most pages, including those that do not require login. You can use a label of "trans::" to use system-defined translations. For example: Using "trans::common.privacy_policy" will provide the translated text "Privacy Policy" and "trans::common.terms_of_service" will provide the translated text "Terms of Service".', + 'app_footer_links_label' => 'Link Label', + 'app_footer_links_url' => 'Link URL', + 'app_footer_links_add' => 'Add Footer Link', + 'app_disable_comments' => 'Disable Comments', + 'app_disable_comments_toggle' => 'Disable comments', + 'app_disable_comments_desc' => 'Disables comments across all pages in the application.
Existing comments are not shown.', + + // Color settings + 'color_scheme' => 'Application Color Scheme', + 'color_scheme_desc' => 'Set the colors to use in the application user interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.', + 'ui_colors_desc' => 'Set the application primary color and default link color. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the application interface.', + 'app_color' => 'Primary Color', + 'link_color' => 'Default Link Color', + 'content_colors_desc' => 'Set colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.', + 'bookshelf_color' => 'Shelf Color', + 'book_color' => 'Book Color', + 'chapter_color' => 'Chapter Color', + 'page_color' => 'Page Color', + 'page_draft_color' => 'Page Draft Color', + + // Registration Settings + 'reg_settings' => 'Registration', + 'reg_enable' => 'Enable Registration', + 'reg_enable_toggle' => 'Enable registration', + 'reg_enable_desc' => 'When registration is enabled user will be able to sign themselves up as an application user. Upon registration they are given a single, default user role.', + 'reg_default_role' => 'Default user role after registration', + 'reg_enable_external_warning' => 'The option above is ignored while external LDAP or SAML authentication is active. User accounts for non-existing members will be auto-created if authentication, against the external system in use, is successful.', + 'reg_email_confirmation' => 'Email Confirmation', + 'reg_email_confirmation_toggle' => 'Require email confirmation', + 'reg_confirm_email_desc' => 'If domain restriction is used then email confirmation will be required and this option will be ignored.', + 'reg_confirm_restrict_domain' => 'Domain Restriction', + 'reg_confirm_restrict_domain_desc' => 'Enter a comma separated list of email domains you would like to restrict registration to. Users will be sent an email to confirm their address before being allowed to interact with the application.
Note that users will be able to change their email addresses after successful registration.', + 'reg_confirm_restrict_domain_placeholder' => 'No restriction set', + + // Sorting Settings + 'sorting' => 'Sorting', + 'sorting_book_default' => 'Default Book Sort', + 'sorting_book_default_desc' => 'Select the default sort rule to apply to new books. This won\'t affect existing books, and can be overridden per-book.', + 'sorting_rules' => 'Sort Rules', + 'sorting_rules_desc' => 'These are predefined sorting operations which can be applied to content in the system.', + 'sort_rule_assigned_to_x_books' => 'Assigned to :count Book|Assigned to :count Books', + 'sort_rule_create' => 'Create Sort Rule', + 'sort_rule_edit' => 'Edit Sort Rule', + 'sort_rule_delete' => 'Delete Sort Rule', + 'sort_rule_delete_desc' => 'Remove this sort rule from the system. Books using this sort will revert to manual sorting.', + 'sort_rule_delete_warn_books' => 'This sort rule is currently used on :count book(s). Are you sure you want to delete this?', + 'sort_rule_delete_warn_default' => 'This sort rule is currently used as the default for books. Are you sure you want to delete this?', + 'sort_rule_details' => 'Sort Rule Details', + 'sort_rule_details_desc' => 'Set a name for this sort rule, which will appear in lists when users are selecting a sort.', + 'sort_rule_operations' => 'Sort Operations', + 'sort_rule_operations_desc' => 'Configure the sort actions to be performed by moving them from the list of available operations. Upon use, the operations will be applied in order, from top to bottom. Any changes made here will be applied to all assigned books upon save.', + 'sort_rule_available_operations' => 'Available Operations', + 'sort_rule_available_operations_empty' => 'No operations remaining', + 'sort_rule_configured_operations' => 'Configured Operations', + 'sort_rule_configured_operations_empty' => 'Drag/add operations from the "Available Operations" list', + 'sort_rule_op_asc' => '(Asc)', + 'sort_rule_op_desc' => '(Desc)', + 'sort_rule_op_name' => 'Name - Alphabetical', + 'sort_rule_op_name_numeric' => '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_last' => 'Chapters Last', + + // Maintenance settings + 'maint' => 'Maintenance', + 'maint_image_cleanup' => 'Cleanup Images', + 'maint_image_cleanup_desc' => 'Scans page & revision content to check which images and drawings are currently in use and which images are redundant. Ensure you create a full database and image backup before running this.', + 'maint_delete_images_only_in_revisions' => 'Also delete images that only exist in old page revisions', + 'maint_image_cleanup_run' => 'Run Cleanup', + 'maint_image_cleanup_warning' => ':count potentially unused images were found. Are you sure you want to delete these images?', + 'maint_image_cleanup_success' => ':count potentially unused images found and deleted!', + 'maint_image_cleanup_nothing_found' => 'No unused images found, Nothing deleted!', + 'maint_send_test_email' => 'Send a Test Email', + 'maint_send_test_email_desc' => 'This sends a test email to your email address specified in your profile.', + 'maint_send_test_email_run' => 'Send test email', + 'maint_send_test_email_success' => 'Email sent to :address', + 'maint_send_test_email_mail_subject' => 'Test Email', + 'maint_send_test_email_mail_greeting' => 'Email delivery seems to work!', + 'maint_send_test_email_mail_text' => 'Congratulations! As you received this email notification, your email settings seem to be configured properly.', + 'maint_recycle_bin_desc' => 'Deleted shelves, books, chapters & pages are sent to the recycle bin so they can be restored or permanently deleted. Older items in the recycle bin may be automatically removed after a while depending on system configuration.', + 'maint_recycle_bin_open' => 'Open Recycle Bin', + 'maint_regen_references' => 'Regenerate References', + 'maint_regen_references_desc' => 'This action will rebuild the cross-item reference index within the database. This is usually handled automatically but this action can be useful to index old content or content added via unofficial methods.', + 'maint_regen_references_success' => 'Reference index has been regenerated!', + 'maint_timeout_command_note' => 'Note: This action can take time to run, which can lead to timeout issues in some web environments. As an alternative, this action be performed using a terminal command.', + + // Recycle Bin + 'recycle_bin' => 'Recycle Bin', + 'recycle_bin_desc' => 'Here you can restore items that have been deleted or choose to permanently remove them from the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.', + 'recycle_bin_deleted_item' => 'Deleted Item', + 'recycle_bin_deleted_parent' => 'Parent', + 'recycle_bin_deleted_by' => 'Deleted By', + 'recycle_bin_deleted_at' => 'Deletion Time', + 'recycle_bin_permanently_delete' => 'Permanently Delete', + 'recycle_bin_restore' => 'Restore', + 'recycle_bin_contents_empty' => 'The recycle bin is currently empty', + 'recycle_bin_empty' => 'Empty Recycle Bin', + 'recycle_bin_empty_confirm' => 'This will permanently destroy all items in the recycle bin including content contained within each item. Are you sure you want to empty the recycle bin?', + 'recycle_bin_destroy_confirm' => 'This action will permanently delete this item from the system, along with any child elements listed below, and you will not be able to restore this content. Are you sure you want to permanently delete this item?', + 'recycle_bin_destroy_list' => 'Items to be Destroyed', + 'recycle_bin_restore_list' => 'Items to be Restored', + 'recycle_bin_restore_confirm' => 'This action will restore the deleted item, including any child elements, to their original location. If the original location has since been deleted, and is now in the recycle bin, the parent item will also need to be restored.', + 'recycle_bin_restore_deleted_parent' => 'The parent of this item has also been deleted. These will remain deleted until that parent is also restored.', + 'recycle_bin_restore_parent' => 'Restore Parent', + 'recycle_bin_destroy_notification' => 'Deleted :count total items from the recycle bin.', + 'recycle_bin_restore_notification' => 'Restored :count total items from the recycle bin.', + + // Audit Log + 'audit' => 'Audit Log', + 'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.', + 'audit_event_filter' => 'Event Filter', + 'audit_event_filter_no_filter' => 'No Filter', + 'audit_deleted_item' => 'Deleted Item', + 'audit_deleted_item_name' => 'Name: :name', + 'audit_table_user' => 'User', + 'audit_table_event' => 'Event', + 'audit_table_related' => 'Related Item or Detail', + 'audit_table_ip' => 'IP Address', + 'audit_table_date' => 'Activity Date', + 'audit_date_from' => 'Date Range From', + 'audit_date_to' => 'Date Range To', + + // Role Settings + 'roles' => 'Roles', + 'role_user_roles' => 'User Roles', + 'roles_index_desc' => 'Roles are used to group users & provide system permission to their members. When a user is a member of multiple roles the privileges granted will stack and the user will inherit all abilities.', + 'roles_x_users_assigned' => ':count user assigned|:count users assigned', + 'roles_x_permissions_provided' => ':count permission|:count permissions', + 'roles_assigned_users' => 'Assigned Users', + 'roles_permissions_provided' => 'Provided Permissions', + 'role_create' => 'Create New Role', + 'role_delete' => 'Delete Role', + 'role_delete_confirm' => 'This will delete the role with the name \':roleName\'.', + 'role_delete_users_assigned' => 'This role has :userCount users assigned to it. If you would like to migrate the users from this role select a new role below.', + 'role_delete_no_migration' => "Don't migrate users", + 'role_delete_sure' => 'Are you sure you want to delete this role?', + 'role_edit' => 'Edit Role', + 'role_details' => 'Role Details', + 'role_name' => 'Role Name', + 'role_desc' => 'Short Description of Role', + 'role_mfa_enforced' => 'Requires Multi-Factor Authentication', + 'role_external_auth_id' => 'External Authentication IDs', + 'role_system' => 'System Permissions', + 'role_manage_users' => 'Manage users', + 'role_manage_roles' => 'Manage roles & role permissions', + 'role_manage_entity_permissions' => 'Manage all book, chapter & page permissions', + 'role_manage_own_entity_permissions' => 'Manage permissions on own book, chapter & pages', + 'role_manage_page_templates' => 'Manage page templates', + 'role_access_api' => 'Access system API', + 'role_manage_settings' => 'Manage app settings', + 'role_export_content' => 'Export content', + 'role_import_content' => 'Import content', + 'role_editor_change' => 'Change page editor', + 'role_notifications' => 'Receive & manage notifications', + 'role_asset' => 'Asset Permissions', + 'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.', + 'role_asset_desc' => 'These permissions control default access to the assets within the system. Permissions on Books, Chapters and Pages will override these permissions.', + 'role_asset_admins' => 'Admins are automatically given access to all content but these options may show or hide UI options.', + 'role_asset_image_view_note' => 'This relates to visibility within the image manager. Actual access of uploaded image files will be dependant upon system image storage option.', + 'role_all' => 'All', + 'role_own' => 'Own', + 'role_controlled_by_asset' => 'Controlled by the asset they are uploaded to', + 'role_save' => 'Save Role', + 'role_users' => 'Users in this role', + 'role_users_none' => 'No users are currently assigned to this role', + + // Users + 'users' => 'Users', + 'users_index_desc' => 'Create & manage individual user accounts within the system. User accounts are used for login and attribution of content & activity. Access permissions are primarily role-based but user content ownership, among other factors, may also affect permissions & access.', + 'user_profile' => 'User Profile', + 'users_add_new' => 'Add New User', + 'users_search' => 'Search Users', + 'users_latest_activity' => 'Latest Activity', + 'users_details' => 'User Details', + 'users_details_desc' => 'Set a display name and an email address for this user. The email address will be used for logging into the application.', + 'users_details_desc_no_email' => 'Set a display name for this user so others can recognise them.', + 'users_role' => 'User Roles', + 'users_role_desc' => 'Select which roles this user will be assigned to. If a user is assigned to multiple roles the permissions from those roles will stack and they will receive all abilities of the assigned roles.', + 'users_password' => 'User Password', + 'users_password_desc' => 'Set a password used to log-in to the application. This must be at least 8 characters long.', + 'users_send_invite_text' => 'You can choose to send this user an invitation email which allows them to set their own password otherwise you can set their password yourself.', + 'users_send_invite_option' => 'Send user invite email', + 'users_external_auth_id' => 'External Authentication ID', + 'users_external_auth_id_desc' => 'When an external authentication system is in use (such as SAML2, OIDC or LDAP) this is the ID which links this BookStack user to the authentication system account. You can ignore this field if using the default email-based authentication.', + 'users_password_warning' => 'Only fill the below if you would like to change the password for this user.', + 'users_system_public' => 'This user represents any guest users that visit your instance. It cannot be used to log in but is assigned automatically.', + 'users_delete' => 'Delete User', + 'users_delete_named' => 'Delete user :userName', + 'users_delete_warning' => 'This will fully delete this user with the name \':userName\' from the system.', + 'users_delete_confirm' => 'Are you sure you want to delete this user?', + 'users_migrate_ownership' => 'Migrate Ownership', + 'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.', + 'users_none_selected' => 'No user selected', + 'users_edit' => 'Edit User', + 'users_edit_profile' => 'Edit Profile', + 'users_avatar' => 'User Avatar', + 'users_avatar_desc' => 'Select an image to represent this user. This should be approx 256px square.', + 'users_preferred_language' => 'Preferred Language', + 'users_preferred_language_desc' => 'This option will change the language used for the user-interface of the application. This will not affect any user-created content.', + 'users_social_accounts' => 'Social Accounts', + 'users_social_accounts_desc' => 'View the status of the connected social accounts for this user. Social accounts can be used in addition to the primary authentication system for system access.', + 'users_social_accounts_info' => 'Here you can connect your other accounts for quicker and easier login. Disconnecting an account here does not revoke previously authorized access. Revoke access from your profile settings on the connected social account.', + 'users_social_connect' => 'Connect Account', + 'users_social_disconnect' => 'Disconnect Account', + 'users_social_status_connected' => 'Connected', + 'users_social_status_disconnected' => 'Disconnected', + 'users_social_connected' => ':socialAccount account was successfully attached to your profile.', + 'users_social_disconnected' => ':socialAccount account was successfully disconnected from your profile.', + 'users_api_tokens' => 'API Tokens', + 'users_api_tokens_desc' => 'Create and manage the access tokens used to authenticate with the BookStack REST API. Permissions for the API are managed via the user that the token belongs to.', + 'users_api_tokens_none' => 'No API tokens have been created for this user', + 'users_api_tokens_create' => 'Create Token', + 'users_api_tokens_expires' => 'Expires', + 'users_api_tokens_docs' => 'API Documentation', + 'users_mfa' => 'Multi-Factor Authentication', + 'users_mfa_desc' => 'Setup multi-factor authentication as an extra layer of security for your user account.', + 'users_mfa_x_methods' => ':count method configured|:count methods configured', + 'users_mfa_configure' => 'Configure Methods', + + // API Tokens + 'user_api_token_create' => 'Create API Token', + 'user_api_token_name' => 'Name', + 'user_api_token_name_desc' => 'Give your token a readable name as a future reminder of its intended purpose.', + 'user_api_token_expiry' => 'Expiry Date', + 'user_api_token_expiry_desc' => 'Set a date at which this token expires. After this date, requests made using this token will no longer work. Leaving this field blank will set an expiry 100 years into the future.', + 'user_api_token_create_secret_message' => 'Immediately after creating this token a "Token ID" & "Token Secret" will be generated and displayed. The secret will only be shown a single time so be sure to copy the value to somewhere safe and secure before proceeding.', + 'user_api_token' => 'API Token', + 'user_api_token_id' => 'Token ID', + 'user_api_token_id_desc' => 'This is a non-editable system generated identifier for this token which will need to be provided in API requests.', + 'user_api_token_secret' => 'Token Secret', + 'user_api_token_secret_desc' => 'This is a system generated secret for this token which will need to be provided in API requests. This will only be displayed this one time so copy this value to somewhere safe and secure.', + 'user_api_token_created' => 'Token created :timeAgo', + 'user_api_token_updated' => 'Token updated :timeAgo', + 'user_api_token_delete' => 'Delete Token', + 'user_api_token_delete_warning' => 'This will fully delete this API token with the name \':tokenName\' from the system.', + 'user_api_token_delete_confirm' => 'Are you sure you want to delete this API token?', + + // Webhooks + 'webhooks' => 'Webhooks', + 'webhooks_index_desc' => 'Webhooks are a way to send data to external URLs when certain actions and events occur within the system which allows event-based integration with external platforms such as messaging or notification systems.', + 'webhooks_x_trigger_events' => ':count trigger event|:count trigger events', + 'webhooks_create' => 'Create New Webhook', + 'webhooks_none_created' => 'No webhooks have yet been created.', + 'webhooks_edit' => 'Edit Webhook', + 'webhooks_save' => 'Save Webhook', + 'webhooks_details' => 'Webhook Details', + 'webhooks_details_desc' => 'Provide a user friendly name and a POST endpoint as a location for the webhook data to be sent to.', + 'webhooks_events' => 'Webhook Events', + 'webhooks_events_desc' => 'Select all the events that should trigger this webhook to be called.', + 'webhooks_events_warning' => 'Keep in mind that these events will be triggered for all selected events, even if custom permissions are applied. Ensure that use of this webhook won\'t expose confidential content.', + 'webhooks_events_all' => 'All system events', + 'webhooks_name' => 'Webhook Name', + 'webhooks_timeout' => 'Webhook Request Timeout (Seconds)', + 'webhooks_endpoint' => 'Webhook Endpoint', + 'webhooks_active' => 'Webhook Active', + 'webhook_events_table_header' => 'Events', + 'webhooks_delete' => 'Delete Webhook', + 'webhooks_delete_warning' => 'This will fully delete this webhook, with the name \':webhookName\', from the system.', + 'webhooks_delete_confirm' => 'Are you sure you want to delete this webhook?', + 'webhooks_format_example' => 'Webhook Format Example', + 'webhooks_format_example_desc' => 'Webhook data is sent as a POST request to the configured endpoint as JSON following the format below. The "related_item" and "url" properties are optional and will depend on the type of event triggered.', + 'webhooks_status' => 'Webhook Status', + 'webhooks_last_called' => 'Last Called:', + 'webhooks_last_errored' => 'Last Errored:', + 'webhooks_last_error_message' => 'Last Error Message:', + + // Licensing + 'licenses' => 'Licenses', + 'licenses_desc' => 'This page details license information for BookStack in addition to the projects & libraries that are used within BookStack. Many projects listed may only be used in a development context.', + 'licenses_bookstack' => 'BookStack License', + 'licenses_php' => 'PHP Library Licenses', + 'licenses_js' => 'JavaScript Library Licenses', + 'licenses_other' => 'Other Licenses', + 'license_details' => 'License Details', + + //! If editing translations files directly please ignore this in all + //! languages apart from en. Content will be auto-copied from en. + //!//////////////////////////////// + 'language_select' => [ + 'en' => 'English', + 'ar' => 'العربية', + 'bg' => 'Bǎlgarski', + 'bs' => 'Bosanski', + 'ca' => 'Català', + 'cs' => 'Česky', + 'cy' => 'Cymraeg', + 'da' => 'Dansk', + 'de' => 'Deutsch (Sie)', + 'de_informal' => 'Deutsch (Du)', + 'el' => 'ελληνικά', + 'es' => 'Español', + 'es_AR' => 'Español Argentina', + 'et' => 'Eesti keel', + 'eu' => 'Euskara', + 'fa' => 'فارسی', + 'fi' => 'Suomi', + 'fr' => 'Français', + 'he' => 'עברית', + 'hr' => 'Hrvatski', + 'hu' => 'Magyar', + 'id' => 'Bahasa Indonesia', + 'it' => 'Italian', + 'ja' => '日本語', + 'ko' => '한국어', + 'lt' => 'Lietuvių Kalba', + 'lv' => 'Latviešu Valoda', + 'nb' => 'Norsk (Bokmål)', + 'ne' => 'नेपाली', + 'nn' => 'Nynorsk', + 'nl' => 'Nederlands', + 'pl' => 'Polski', + 'pt' => 'Português', + 'pt_BR' => 'Português do Brasil', + 'ro' => 'Română', + 'ru' => 'Русский', + 'sk' => 'Slovensky', + 'sl' => 'Slovenščina', + 'sv' => 'Svenska', + 'tr' => 'Türkçe', + 'uk' => 'Українська', + 'uz' => 'O‘zbekcha', + 'vi' => 'Tiếng Việt', + 'zh_CN' => '简体中文', + 'zh_TW' => '繁體中文', + ], + //!//////////////////////////////// +]; diff --git a/lang/bn/validation.php b/lang/bn/validation.php new file mode 100644 index 00000000000..d9b982d1e23 --- /dev/null +++ b/lang/bn/validation.php @@ -0,0 +1,122 @@ + 'The :attribute must be accepted.', + 'active_url' => 'The :attribute is not a valid URL.', + 'after' => 'The :attribute must be a date after :date.', + 'alpha' => 'The :attribute may only contain letters.', + 'alpha_dash' => 'The :attribute may only contain letters, numbers, dashes and underscores.', + 'alpha_num' => 'The :attribute may only contain letters and numbers.', + 'array' => 'The :attribute must be an array.', + 'backup_codes' => 'The provided code is not valid or has already been used.', + 'before' => 'The :attribute must be a date before :date.', + 'between' => [ + 'numeric' => 'The :attribute must be between :min and :max.', + 'file' => 'The :attribute must be between :min and :max kilobytes.', + 'string' => 'The :attribute must be between :min and :max characters.', + 'array' => 'The :attribute must have between :min and :max items.', + ], + 'boolean' => 'The :attribute field must be true or false.', + 'confirmed' => 'The :attribute confirmation does not match.', + 'date' => 'The :attribute is not a valid date.', + 'date_format' => 'The :attribute does not match the format :format.', + 'different' => 'The :attribute and :other must be different.', + 'digits' => 'The :attribute must be :digits digits.', + 'digits_between' => 'The :attribute must be between :min and :max digits.', + 'email' => 'The :attribute must be a valid email address.', + 'ends_with' => 'The :attribute must end with one of the following: :values', + 'file' => 'The :attribute must be provided as a valid file.', + 'filled' => 'The :attribute field is required.', + 'gt' => [ + 'numeric' => 'The :attribute must be greater than :value.', + 'file' => 'The :attribute must be greater than :value kilobytes.', + 'string' => 'The :attribute must be greater than :value characters.', + 'array' => 'The :attribute must have more than :value items.', + ], + 'gte' => [ + 'numeric' => 'The :attribute must be greater than or equal :value.', + 'file' => 'The :attribute must be greater than or equal :value kilobytes.', + 'string' => 'The :attribute must be greater than or equal :value characters.', + 'array' => 'The :attribute must have :value items or more.', + ], + 'exists' => 'The selected :attribute is invalid.', + 'image' => 'The :attribute must be an image.', + 'image_extension' => 'The :attribute must have a valid & supported image extension.', + 'in' => 'The selected :attribute is invalid.', + 'integer' => 'The :attribute must be an integer.', + 'ip' => 'The :attribute must be a valid IP address.', + 'ipv4' => 'The :attribute must be a valid IPv4 address.', + 'ipv6' => 'The :attribute must be a valid IPv6 address.', + 'json' => 'The :attribute must be a valid JSON string.', + 'lt' => [ + 'numeric' => 'The :attribute must be less than :value.', + 'file' => 'The :attribute must be less than :value kilobytes.', + 'string' => 'The :attribute must be less than :value characters.', + 'array' => 'The :attribute must have less than :value items.', + ], + 'lte' => [ + 'numeric' => 'The :attribute must be less than or equal :value.', + 'file' => 'The :attribute must be less than or equal :value kilobytes.', + 'string' => 'The :attribute must be less than or equal :value characters.', + 'array' => 'The :attribute must not have more than :value items.', + ], + 'max' => [ + 'numeric' => 'The :attribute may not be greater than :max.', + 'file' => 'The :attribute may not be greater than :max kilobytes.', + 'string' => 'The :attribute may not be greater than :max characters.', + 'array' => 'The :attribute may not have more than :max items.', + ], + 'mimes' => 'The :attribute must be a file of type: :values.', + 'min' => [ + 'numeric' => 'The :attribute must be at least :min.', + 'file' => 'The :attribute must be at least :min kilobytes.', + 'string' => 'The :attribute must be at least :min characters.', + 'array' => 'The :attribute must have at least :min items.', + ], + 'not_in' => 'The selected :attribute is invalid.', + 'not_regex' => 'The :attribute format is invalid.', + 'numeric' => 'The :attribute must be a number.', + 'regex' => 'The :attribute format is invalid.', + 'required' => 'The :attribute field is required.', + 'required_if' => 'The :attribute field is required when :other is :value.', + 'required_with' => 'The :attribute field is required when :values is present.', + 'required_with_all' => 'The :attribute field is required when :values is present.', + 'required_without' => 'The :attribute field is required when :values is not present.', + 'required_without_all' => 'The :attribute field is required when none of :values are present.', + 'same' => 'The :attribute and :other must match.', + 'safe_url' => 'The provided link may not be safe.', + 'size' => [ + 'numeric' => 'The :attribute must be :size.', + 'file' => 'The :attribute must be :size kilobytes.', + 'string' => 'The :attribute must be :size characters.', + 'array' => 'The :attribute must contain :size items.', + ], + 'string' => 'The :attribute must be a string.', + 'timezone' => 'The :attribute must be a valid zone.', + 'totp' => 'The provided code is not valid or has expired.', + 'unique' => 'The :attribute has already been taken.', + 'url' => 'The :attribute format is invalid.', + 'uploaded' => 'The file could not be uploaded. The server may not accept files of this size.', + + 'zip_file' => 'The :attribute needs to reference a file within the ZIP.', + 'zip_file_mime' => 'The :attribute needs to reference a file of type :validTypes, found :foundType.', + 'zip_model_expected' => 'Data object expected but ":type" found.', + 'zip_unique' => 'The :attribute must be unique for the object type within the ZIP.', + + // Custom validation lines + 'custom' => [ + 'password-confirm' => [ + 'required_with' => 'Password confirmation required', + ], + ], + + // Custom validation attributes + 'attributes' => [], +]; diff --git a/lang/bs/activities.php b/lang/bs/activities.php index bd157743612..e7308770a29 100644 --- a/lang/bs/activities.php +++ b/lang/bs/activities.php @@ -84,6 +84,14 @@ 'webhook_delete' => 'deleted webhook', 'webhook_delete_notification' => 'Webhook successfully deleted', + // Imports + 'import_create' => 'created import', + 'import_create_notification' => 'Import successfully uploaded', + 'import_run' => 'updated import', + 'import_run_notification' => 'Content successfully imported', + 'import_delete' => 'deleted import', + 'import_delete_notification' => 'Import successfully deleted', + // Users 'user_create' => 'created user', 'user_create_notification' => 'User successfully created', @@ -93,11 +101,11 @@ 'user_delete_notification' => 'User successfully removed', // API Tokens - 'api_token_create' => 'created api token', + 'api_token_create' => 'created API token', 'api_token_create_notification' => 'API token successfully created', - 'api_token_update' => 'updated api token', + 'api_token_update' => 'updated API token', 'api_token_update_notification' => 'API token successfully updated', - 'api_token_delete' => 'deleted api token', + 'api_token_delete' => 'deleted API token', 'api_token_delete_notification' => 'API token successfully deleted', // Roles @@ -119,6 +127,14 @@ 'comment_update' => 'updated comment', 'comment_delete' => 'deleted comment', + // Sort Rules + 'sort_rule_create' => 'created sort rule', + 'sort_rule_create_notification' => 'Sort rule successfully created', + 'sort_rule_update' => 'updated sort rule', + 'sort_rule_update_notification' => 'Sort rule successfully updated', + 'sort_rule_delete' => 'deleted sort rule', + 'sort_rule_delete_notification' => 'Sort rule successfully deleted', + // Other 'permissions_update' => 'je ažurirao/la dozvole', ]; diff --git a/lang/bs/auth.php b/lang/bs/auth.php index 96ab0efea19..a8d0da78eb9 100644 --- a/lang/bs/auth.php +++ b/lang/bs/auth.php @@ -91,7 +91,7 @@ 'mfa_option_totp_title' => 'Mobile App', 'mfa_option_totp_desc' => 'To use multi-factor authentication you\'ll need a mobile application that supports TOTP such as Google Authenticator, Authy or Microsoft Authenticator.', 'mfa_option_backup_codes_title' => 'Backup Codes', - 'mfa_option_backup_codes_desc' => 'Securely store a set of one-time-use backup codes which you can enter to verify your identity.', + 'mfa_option_backup_codes_desc' => 'Generates a set of one-time-use backup codes which you\'ll enter on login to verify your identity. Make sure to store these in a safe & secure place.', 'mfa_gen_confirm_and_enable' => 'Confirm and Enable', 'mfa_gen_backup_codes_title' => 'Backup Codes Setup', 'mfa_gen_backup_codes_desc' => 'Store the below list of codes in a safe place. When accessing the system you\'ll be able to use one of the codes as a second authentication mechanism.', diff --git a/lang/bs/common.php b/lang/bs/common.php index dcf6248a0ce..c2f1fda0d1a 100644 --- a/lang/bs/common.php +++ b/lang/bs/common.php @@ -20,7 +20,7 @@ 'description' => 'Opis', 'role' => 'Uloga', 'cover_image' => 'Naslovna slika', - 'cover_image_description' => 'Ova slika treba biti približno 440x250px.', + 'cover_image_description' => 'This image should be approximately 440x250px although it will be flexibly scaled & cropped to fit the user interface in different scenarios as required, so actual dimensions for display will differ.', // Actions 'actions' => 'Akcije', @@ -30,6 +30,8 @@ 'create' => 'Kreiraj', 'update' => 'Ažuriraj', 'edit' => 'Uredi', + 'archive' => 'Archive', + 'unarchive' => 'Un-Archive', 'sort' => 'Sortiraj', 'move' => 'Pomjeri', 'copy' => 'Kopiraj', @@ -107,4 +109,7 @@ // Not directly used but available for convenience to users. 'privacy_policy' => 'Pravila o privatnosti', 'terms_of_service' => 'Uslovi korištenja', + + // OpenSearch + 'opensearch_description' => 'Search :appName', ]; diff --git a/lang/bs/editor.php b/lang/bs/editor.php index 670c1c5e1ac..0d250e9a7bd 100644 --- a/lang/bs/editor.php +++ b/lang/bs/editor.php @@ -13,6 +13,7 @@ 'cancel' => 'Cancel', 'save' => 'Save', 'close' => 'Close', + 'apply' => 'Apply', 'undo' => 'Undo', 'redo' => 'Redo', 'left' => 'Left', @@ -47,6 +48,7 @@ 'superscript' => 'Superscript', 'subscript' => 'Subscript', 'text_color' => 'Text color', + 'highlight_color' => 'Highlight color', 'custom_color' => 'Custom color', 'remove_color' => 'Remove color', 'background_color' => 'Background color', @@ -81,6 +83,9 @@ 'table_properties' => 'Table properties', 'table_properties_title' => 'Table Properties', 'delete_table' => 'Delete table', + 'table_clear_formatting' => 'Clear table formatting', + 'resize_to_contents' => 'Resize to contents', + 'row_header' => 'Row header', 'insert_row_before' => 'Insert row before', 'insert_row_after' => 'Insert row after', 'delete_row' => 'Delete row', @@ -144,6 +149,7 @@ 'url' => 'URL', 'text_to_display' => 'Text to display', 'title' => 'Title', + 'browse_links' => 'Browse links', 'open_link' => 'Open link', 'open_link_in' => 'Open link in...', 'open_link_current' => 'Current window', @@ -160,6 +166,8 @@ 'about' => 'About the editor', 'about_title' => 'About the WYSIWYG Editor', 'editor_license' => 'Editor License & Copyright', + 'editor_lexical_license' => 'This editor is built as a fork of :lexicalLink which is distributed under the MIT license.', + 'editor_lexical_license_link' => 'Full license details can be found here.', 'editor_tiny_license' => 'This editor is built using :tinyLink which is provided under the MIT license.', 'editor_tiny_license_link' => 'The copyright and license details of TinyMCE can be found here.', 'save_continue' => 'Save Page & Continue', diff --git a/lang/bs/entities.php b/lang/bs/entities.php index a8a49fe1c56..f671e1b4527 100644 --- a/lang/bs/entities.php +++ b/lang/bs/entities.php @@ -39,6 +39,34 @@ 'export_pdf' => 'PDF fajl', 'export_text' => 'Plain Text fajl', 'export_md' => 'Markdown File', + 'export_zip' => 'Portable ZIP', + 'default_template' => 'Default Page Template', + 'default_template_explain' => 'Assign a page template that will be used as the default content for all pages created within this item. Keep in mind this will only be used if the page creator has view access to the chosen template page.', + 'default_template_select' => 'Select a template page', + 'import' => 'Import', + 'import_validate' => 'Validate Import', + 'import_desc' => 'Import books, chapters & pages using a portable zip export from the same, or a different, instance. Select a ZIP file to proceed. After the file has been uploaded and validated you\'ll be able to configure & confirm the import in the next view.', + '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_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_size' => ':size Import ZIP Size', + 'import_uploaded_at' => 'Uploaded :relativeTime', + 'import_uploaded_by' => 'Uploaded by', + 'import_location' => 'Import Location', + '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_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', + 'breadcrumb_siblings_for_book' => 'Navigate siblings for book', + 'breadcrumb_siblings_for_bookshelf' => 'Navigate siblings for shelf', // Permissions and restrictions 'permissions' => 'Dozvole', @@ -132,9 +160,6 @@ 'books_edit_named' => 'Uredi knjigu :bookName', 'books_form_book_name' => 'Naziv knjige', 'books_save' => 'Spremi knjigu', - 'books_default_template' => 'Default Page Template', - 'books_default_template_explain' => 'Assign a page template that will be used as the default content for all new pages in this book. Keep in mind this will only be used if the page creator has view access to those chosen template page.', - 'books_default_template_select' => 'Select a template page', 'books_permissions' => 'Dozvole knjige', 'books_permissions_updated' => 'Dozvole knjige su ažurirane', 'books_empty_contents' => 'Za ovu knjigu nisu napravljene ni stranice ni poglavlja.', @@ -145,7 +170,9 @@ 'books_search_this' => 'Pretraži ovu knjigu', 'books_navigation' => 'Navigacija knjige', 'books_sort' => 'Sortiraj sadržaj knjige', - 'books_sort_desc' => 'Move chapters and pages within a book to reorganise its contents. Other books can be added which allows easy moving of chapters and pages between books.', + 'books_sort_desc' => 'Move chapters and pages within a book to reorganise its contents. Other books can be added which allows easy moving of chapters and pages between books. Optionally an auto sort rule can be set to automatically sort this book\'s contents upon changes.', + 'books_sort_auto_sort' => 'Auto Sort Option', + 'books_sort_auto_sort_active' => 'Auto Sort Active: :sortName', 'books_sort_named' => 'Sortiraj knjigu :bookName', 'books_sort_name' => 'Sortiraj po imenu', 'books_sort_created' => 'Sortiraj po datumu kreiranja', @@ -207,7 +234,7 @@ 'pages_delete_draft' => 'Delete Draft Page', 'pages_delete_success' => 'Page deleted', 'pages_delete_draft_success' => 'Draft page deleted', - 'pages_delete_warning_template' => 'This page is in active use as a book default page template. These books will no longer have a default page template assigned after this page is deleted.', + 'pages_delete_warning_template' => 'This page is in active use as a book or chapter default page template. These books or chapters will no longer have a default page template assigned after this page is deleted.', 'pages_delete_confirm' => 'Are you sure you want to delete this page?', 'pages_delete_draft_confirm' => 'Are you sure you want to delete this draft page?', 'pages_editing_named' => 'Editing Page :pageName', @@ -224,6 +251,8 @@ 'pages_edit_switch_to_markdown_clean' => '(Clean Content)', 'pages_edit_switch_to_markdown_stable' => '(Stable Content)', 'pages_edit_switch_to_wysiwyg' => 'Switch to WYSIWYG Editor', + 'pages_edit_switch_to_new_wysiwyg' => 'Switch to new WYSIWYG', + 'pages_edit_switch_to_new_wysiwyg_desc' => '(In Beta Testing)', 'pages_edit_set_changelog' => 'Set Changelog', 'pages_edit_enter_changelog_desc' => 'Enter a brief description of the changes you\'ve made', 'pages_edit_enter_changelog' => 'Enter Changelog', @@ -243,6 +272,7 @@ 'pages_md_insert_drawing' => 'Insert Drawing', 'pages_md_show_preview' => 'Show preview', 'pages_md_sync_scroll' => 'Sync preview scroll', + 'pages_md_plain_editor' => 'Plaintext editor', 'pages_drawing_unsaved' => 'Unsaved Drawing Found', 'pages_drawing_unsaved_confirm' => 'Unsaved drawing data was found from a previous failed drawing save attempt. Would you like to restore and continue editing this unsaved drawing?', 'pages_not_in_chapter' => 'Page is not in a chapter', @@ -367,8 +397,11 @@ 'comment' => 'Comment', 'comments' => 'Comments', 'comment_add' => 'Add Comment', + 'comment_none' => 'No comments to display', 'comment_placeholder' => 'Leave a comment here', - 'comment_count' => '{0} No Comments|{1} 1 Comment|[2,*] :count Comments', + 'comment_thread_count' => ':count Comment Thread|:count Comment Threads', + 'comment_archived_count' => ':count Archived', + 'comment_archived_threads' => 'Archived Threads', 'comment_save' => 'Save Comment', 'comment_new' => 'New Comment', 'comment_created' => 'commented :createDiff', @@ -377,8 +410,14 @@ 'comment_deleted_success' => 'Comment deleted', 'comment_created_success' => 'Comment added', 'comment_updated_success' => 'Comment updated', + 'comment_archive_success' => 'Comment archived', + 'comment_unarchive_success' => 'Comment un-archived', + 'comment_view' => 'View comment', + 'comment_jump_to_thread' => 'Jump to thread', 'comment_delete_confirm' => 'Are you sure you want to delete this comment?', 'comment_in_reply_to' => 'In reply to :commentId', + 'comment_reference' => 'Reference', + 'comment_reference_outdated' => '(Outdated)', 'comment_editor_explain' => 'Here are the comments that have been left on this page. Comments can be added & managed when viewing the saved page.', // Revision diff --git a/lang/bs/errors.php b/lang/bs/errors.php index 4f3d61066ed..f60f92f0782 100644 --- a/lang/bs/errors.php +++ b/lang/bs/errors.php @@ -10,6 +10,7 @@ // Auth 'error_user_exists_different_creds' => 'Korisnik sa e-mailom :email već postoji ali sa različitim podacima.', + 'auth_pre_register_theme_prevention' => 'User account could not be registered for the provided details', 'email_already_confirmed' => 'E-mail je već potvrđen, pokušajte se prijaviti.', 'email_confirmation_invalid' => 'Ovaj token za potvrdu nije ispravan ili je već iskorišten, molimo vas pokušajte se registrovati ponovno.', 'email_confirmation_expired' => 'Ovaj token za potvrdu je istekao, novi e-mail za potvrdu je poslan.', @@ -23,7 +24,6 @@ 'saml_invalid_response_id' => 'Proces, koji je pokrenula ova aplikacija, nije prepoznao zahtjev od eksternog sistema za autentifikaciju. Navigacija nazad nakon prijave može uzrokovati ovaj problem.', 'saml_fail_authed' => 'Prijava koristeći :system nije uspjela, sistem nije obezbijedio uspješnu autorizaciju', 'oidc_already_logged_in' => 'Already logged in', - 'oidc_user_not_registered' => 'The user :name is not registered and automatic registration is disabled', 'oidc_no_email_address' => 'Could not find an email address, for this user, in the data provided by the external authentication system', 'oidc_fail_authed' => 'Login using :system failed, system did not provide successful authorization', 'social_no_action_defined' => 'Nema definisane akcije', @@ -37,6 +37,7 @@ 'social_driver_not_found' => 'Driver društvene mreže nije pronađen', 'social_driver_not_configured' => 'Vaše :socialAccount postavke nisu konfigurisane ispravno.', 'invite_token_expired' => 'Pozivni link je istekao. Možete umjesto toga pokušati da resetujete lozinku.', + 'login_user_not_found' => 'A user for this action could not be found.', // System 'path_not_writable' => 'Na putanju fajla :filePath se ne može učitati. Potvrdite da je omogućeno pisanje na server.', @@ -77,6 +78,7 @@ // Users 'users_cannot_delete_only_admin' => 'Ne možete izbrisati jedinog administratora', 'users_cannot_delete_guest' => 'Ne možete izbrisati gost korisnika', + 'users_could_not_send_invite' => 'Could not create user since invite email failed to send', // Roles 'role_cannot_be_edited' => 'Ova uloga ne može biti mijenjana', @@ -103,6 +105,18 @@ 'app_down' => ':appName trenutno nije u funkciji', 'back_soon' => 'Biti će uskoro u funkciji.', + // Import + 'import_zip_cant_read' => 'Could not read ZIP file.', + 'import_zip_cant_decode_data' => 'Could not find and decode ZIP data.json content.', + 'import_zip_no_data' => 'ZIP file data has no expected book, chapter or page content.', + 'import_validation_failed' => 'Import ZIP failed to validate with errors:', + 'import_zip_failed_notification' => 'Failed to import ZIP file.', + 'import_perms_books' => 'You are lacking the required permissions to create books.', + 'import_perms_chapters' => 'You are lacking the required permissions to create chapters.', + 'import_perms_pages' => 'You are lacking the required permissions to create pages.', + 'import_perms_images' => 'You are lacking the required permissions to create images.', + 'import_perms_attachments' => 'You are lacking the required permission to create attachments.', + // API errors 'api_no_authorization_found' => 'Na zahtjevu nije pronađen token za autorizaciju', 'api_bad_authorization_format' => 'Token za autorizaciju je pronađen u zahtjevu ali je format neispravan', diff --git a/lang/bs/preferences.php b/lang/bs/preferences.php index 2b88f9671ff..2872f5f3c65 100644 --- a/lang/bs/preferences.php +++ b/lang/bs/preferences.php @@ -27,7 +27,7 @@ 'notifications_save' => 'Save Preferences', 'notifications_update_success' => 'Notification preferences have been updated!', 'notifications_watched' => 'Watched & Ignored Items', - 'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.', + 'notifications_watched_desc' => 'Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.', 'auth' => 'Access & Security', 'auth_change_password' => 'Change Password', diff --git a/lang/bs/settings.php b/lang/bs/settings.php index 03e9bf462e2..81c2c0a93c3 100644 --- a/lang/bs/settings.php +++ b/lang/bs/settings.php @@ -74,6 +74,36 @@ 'reg_confirm_restrict_domain_desc' => 'Enter a comma separated list of email domains you would like to restrict registration to. Users will be sent an email to confirm their address before being allowed to interact with the application.
Note that users will be able to change their email addresses after successful registration.', 'reg_confirm_restrict_domain_placeholder' => 'No restriction set', + // Sorting Settings + 'sorting' => 'Sorting', + 'sorting_book_default' => 'Default Book Sort', + 'sorting_book_default_desc' => 'Select the default sort rule to apply to new books. This won\'t affect existing books, and can be overridden per-book.', + 'sorting_rules' => 'Sort Rules', + 'sorting_rules_desc' => 'These are predefined sorting operations which can be applied to content in the system.', + 'sort_rule_assigned_to_x_books' => 'Assigned to :count Book|Assigned to :count Books', + 'sort_rule_create' => 'Create Sort Rule', + 'sort_rule_edit' => 'Edit Sort Rule', + 'sort_rule_delete' => 'Delete Sort Rule', + 'sort_rule_delete_desc' => 'Remove this sort rule from the system. Books using this sort will revert to manual sorting.', + 'sort_rule_delete_warn_books' => 'This sort rule is currently used on :count book(s). Are you sure you want to delete this?', + 'sort_rule_delete_warn_default' => 'This sort rule is currently used as the default for books. Are you sure you want to delete this?', + 'sort_rule_details' => 'Sort Rule Details', + 'sort_rule_details_desc' => 'Set a name for this sort rule, which will appear in lists when users are selecting a sort.', + 'sort_rule_operations' => 'Sort Operations', + 'sort_rule_operations_desc' => 'Configure the sort actions to be performed by moving them from the list of available operations. Upon use, the operations will be applied in order, from top to bottom. Any changes made here will be applied to all assigned books upon save.', + 'sort_rule_available_operations' => 'Available Operations', + 'sort_rule_available_operations_empty' => 'No operations remaining', + 'sort_rule_configured_operations' => 'Configured Operations', + 'sort_rule_configured_operations_empty' => 'Drag/add operations from the "Available Operations" list', + 'sort_rule_op_asc' => '(Asc)', + 'sort_rule_op_desc' => '(Desc)', + 'sort_rule_op_name' => 'Name - Alphabetical', + 'sort_rule_op_name_numeric' => '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_last' => 'Chapters Last', + // Maintenance settings 'maint' => 'Maintenance', 'maint_image_cleanup' => 'Cleanup Images', @@ -109,7 +139,7 @@ 'recycle_bin_contents_empty' => 'The recycle bin is currently empty', 'recycle_bin_empty' => 'Empty Recycle Bin', 'recycle_bin_empty_confirm' => 'This will permanently destroy all items in the recycle bin including content contained within each item. Are you sure you want to empty the recycle bin?', - 'recycle_bin_destroy_confirm' => 'This action will permanently delete this item, along with any child elements listed below, from the system and you will not be able to restore this content. Are you sure you want to permanently delete this item?', + 'recycle_bin_destroy_confirm' => 'This action will permanently delete this item from the system, along with any child elements listed below, and you will not be able to restore this content. Are you sure you want to permanently delete this item?', 'recycle_bin_destroy_list' => 'Items to be Destroyed', 'recycle_bin_restore_list' => 'Items to be Restored', 'recycle_bin_restore_confirm' => 'This action will restore the deleted item, including any child elements, to their original location. If the original location has since been deleted, and is now in the recycle bin, the parent item will also need to be restored.', @@ -162,6 +192,7 @@ 'role_access_api' => 'Access system API', 'role_manage_settings' => 'Manage app settings', 'role_export_content' => 'Export content', + 'role_import_content' => 'Import content', 'role_editor_change' => 'Change page editor', 'role_notifications' => 'Receive & manage notifications', 'role_asset' => 'Asset Permissions', @@ -276,6 +307,14 @@ 'webhooks_last_errored' => 'Last Errored:', 'webhooks_last_error_message' => 'Last Error Message:', + // Licensing + 'licenses' => 'Licenses', + 'licenses_desc' => 'This page details license information for BookStack in addition to the projects & libraries that are used within BookStack. Many projects listed may only be used in a development context.', + 'licenses_bookstack' => 'BookStack License', + 'licenses_php' => 'PHP Library Licenses', + 'licenses_js' => 'JavaScript Library Licenses', + 'licenses_other' => 'Other Licenses', + 'license_details' => 'License Details', //! If editing translations files directly please ignore this in all //! languages apart from en. Content will be auto-copied from en. @@ -287,6 +326,7 @@ 'bs' => 'Bosanski', 'ca' => 'Català', 'cs' => 'Česky', + 'cy' => 'Cymraeg', 'da' => 'Dansk', 'de' => 'Deutsch (Sie)', 'de_informal' => 'Deutsch (Du)', @@ -308,6 +348,7 @@ 'lt' => 'Lietuvių Kalba', 'lv' => 'Latviešu Valoda', 'nb' => 'Norsk (Bokmål)', + 'ne' => 'नेपाली', 'nn' => 'Nynorsk', 'nl' => 'Nederlands', 'pl' => 'Polski', diff --git a/lang/bs/validation.php b/lang/bs/validation.php index b07df9178fc..4b026afd2dc 100644 --- a/lang/bs/validation.php +++ b/lang/bs/validation.php @@ -105,6 +105,11 @@ 'url' => 'Format :attribute je neispravan.', 'uploaded' => 'Fajl nije učitan. Server ne prihvata fajlove ove veličine.', + 'zip_file' => 'The :attribute needs to reference a file within the ZIP.', + 'zip_file_mime' => 'The :attribute needs to reference a file of type :validTypes, found :foundType.', + 'zip_model_expected' => 'Data object expected but ":type" found.', + 'zip_unique' => 'The :attribute must be unique for the object type within the ZIP.', + // Custom validation lines 'custom' => [ 'password-confirm' => [ diff --git a/lang/ca/activities.php b/lang/ca/activities.php index fb9071174bd..4894c279aad 100644 --- a/lang/ca/activities.php +++ b/lang/ca/activities.php @@ -7,118 +7,134 @@ // Pages 'page_create' => 'ha creat la pàgina', - 'page_create_notification' => 'Pàgina creada correctament', + 'page_create_notification' => 'S’ha creat la pàgina', 'page_update' => 'ha actualitzat la pàgina', - 'page_update_notification' => 'Pàgina actualitzada correctament', - 'page_delete' => 'ha suprimit una pàgina', - 'page_delete_notification' => 'Pàgina suprimida correctament', + 'page_update_notification' => 'S’ha actualitzat la pàgina', + 'page_delete' => 'ha suprimit la pàgina', + 'page_delete_notification' => 'S’ha suprimit la pàgina', 'page_restore' => 'ha restaurat la pàgina', - 'page_restore_notification' => 'Pàgina restaurada correctament', + 'page_restore_notification' => 'S’ha restaurat la pàgina', 'page_move' => 'ha mogut la pàgina', - 'page_move_notification' => 'Pàgina moguda correctament', + 'page_move_notification' => 'S’ha mogut la pàgina', // Chapters - 'chapter_create' => 'ha creat el capítol', - 'chapter_create_notification' => 'Capítol creat correctament', + 'chapter_create' => 'S\'ha creat el capítol', + 'chapter_create_notification' => 'S’ha creat el capítol', 'chapter_update' => 'ha actualitzat el capítol', - 'chapter_update_notification' => 'Capítol actualitzat correctament', - 'chapter_delete' => 'ha suprimit un capítol', - 'chapter_delete_notification' => 'Capítol suprimit correctament', - 'chapter_move' => 'ha mogut el capítol', - 'chapter_move_notification' => 'Capítol mogut correctament', + 'chapter_update_notification' => 'S’ha actualitzat el capítol', + 'chapter_delete' => 'ha suprimit el capítol', + 'chapter_delete_notification' => 'S’ha suprimit el capítol', + 'chapter_move' => 's\'ha mogut el capítol', + 'chapter_move_notification' => 'S’ha mogut el capítol', // Books - 'book_create' => 'ha creat el llibre', - 'book_create_notification' => 'Llibre creat correctament', - 'book_create_from_chapter' => 'ha convertit un capítol en el llibre', - 'book_create_from_chapter_notification' => 'Capítol convertit en llibre correctament', - 'book_update' => 'ha actualitzat el llibre', - 'book_update_notification' => 'Llibre actualitzat correctament', - 'book_delete' => 'ha suprimit un llibre', - 'book_delete_notification' => 'Llibre suprimit correctament', - 'book_sort' => 'ha ordenat el llibre', - 'book_sort_notification' => 'Llibre reordenat correctament', + 'book_create' => 'llibre creat', + 'book_create_notification' => 'S’ha creat el llibre', + 'book_create_from_chapter' => 'ha convertit el capítol a llibre', + 'book_create_from_chapter_notification' => 'S’ha convertit el capítol a llibre', + 'book_update' => 'llibre actualitzat', + 'book_update_notification' => 'S’ha actualitzat el llibre', + 'book_delete' => 'llibre suprimit', + 'book_delete_notification' => 'S’ha suprimit el llibre', + 'book_sort' => 'llibre ordenat', + 'book_sort_notification' => 'S’ha tornat a ordenar el llibre', // Bookshelves 'bookshelf_create' => 'ha creat el prestatge', - 'bookshelf_create_notification' => 'Prestatge creat correctament', - 'bookshelf_create_from_book' => 'ha convertit un llibre en el prestatge', - 'bookshelf_create_from_book_notification' => 'Llibre convertit en prestatge correctament', - 'bookshelf_update' => 'ha actualitzat el prestatge', - 'bookshelf_update_notification' => 'Prestatge actualitzat correctament', - 'bookshelf_delete' => 'ha suprimit un prestatge', - 'bookshelf_delete_notification' => 'Prestatge suprimit correctament', + 'bookshelf_create_notification' => 'S’ha creat el prestatge', + 'bookshelf_create_from_book' => 'llibre convertit a prestatge', + 'bookshelf_create_from_book_notification' => 'S’ha convertit el llibre a prestatge', + 'bookshelf_update' => 'prestatge actualitzat', + 'bookshelf_update_notification' => 'S’ha actualitzat el prestatge', + 'bookshelf_delete' => 'prestatge suprimit', + 'bookshelf_delete_notification' => 'S’ha suprimit el prestatge', // Revisions 'revision_restore' => 'ha restaurat la revisió', - 'revision_delete' => 'ha suprimit una revisió', - 'revision_delete_notification' => 'Revisió suprimida correctament', + 'revision_delete' => 'ha suprimit la revisió', + 'revision_delete_notification' => 'S’ha suprimit la revisió', // Favourites - 'favourite_add_notification' => 'S’ha afegit «:name» als vostres preferits', - 'favourite_remove_notification' => 'S’ha suprimit «:name» dels vostres preferits', + 'favourite_add_notification' => 'S’ha afegit «:name» als favorits.', + 'favourite_remove_notification' => 'S’ha eliminat «:name» dels favorits.', // Watching - 'watch_update_level_notification' => 'Preferències de seguiment actualitzades correctament', + 'watch_update_level_notification' => 'S’han actualitzat les preferències de seguiment', // Auth - 'auth_login' => 'ha iniciat la sessió', + 'auth_login' => 'ha iniciat sessió', 'auth_register' => 's’ha registrat com a usuari nou', - 'auth_password_reset_request' => 'ha sol·licitat un restabliment de la contrasenya', - 'auth_password_reset_update' => 'ha restablert la contrasenya de l’usuari', - 'mfa_setup_method' => 'ha configurat un mètode d’autenticació de múltiple factor', - 'mfa_setup_method_notification' => 'Mètode d’autenticació de múltiple factor configurat correctament', - 'mfa_remove_method' => 'ha suprimit un mètode d’autenticació de múltiple factor', - 'mfa_remove_method_notification' => 'Mètode d’autenticació de múltiple factor suprimit correctament', + 'auth_password_reset_request' => 'ha sol·licitat la reinicialització de la contrasenya', + 'auth_password_reset_update' => 'ha reinicialitzat la contrasenya', + 'mfa_setup_method' => 'ha configurat un mètode d’autenticació multifactorial', + 'mfa_setup_method_notification' => 'S’ha configurat un mètode d’autenticació multifactorial', + 'mfa_remove_method' => 'ha eliminat un mètode d’autenticació multifactorial', + 'mfa_remove_method_notification' => 'S’ha eliminat un mètode d’autenticació multifactorial', // Settings 'settings_update' => 'ha actualitzat la configuració', - 'settings_update_notification' => 'Configuració actualitzada correctament', + 'settings_update_notification' => 'S’ha actualitzat la configuració', 'maintenance_action_run' => 'ha executat una acció de manteniment', // Webhooks 'webhook_create' => 'ha creat un webhook', - 'webhook_create_notification' => 'Webhook creat correctament', - 'webhook_update' => 'ha actualitzat un webhook', - 'webhook_update_notification' => 'Webhook actualitzat correctament', - 'webhook_delete' => 'ha suprimit un webhook', - 'webhook_delete_notification' => 'Webhook suprimit correctament', + 'webhook_create_notification' => 'S’ha creat el webhook', + 'webhook_update' => 'ha actualitzat el webhook', + 'webhook_update_notification' => 'S’ha actualitzat el webhook', + 'webhook_delete' => 'ha suprimit el webhook', + 'webhook_delete_notification' => 'S’ha suprimit el webhook', + + // Imports + 'import_create' => 'importació creada', + 'import_create_notification' => 'L\'importació s\'ha carregat correctament', + 'import_run' => 'importació actualitzada', + 'import_run_notification' => 'Contingut importat correctament', + 'import_delete' => 'importació eliminada', + 'import_delete_notification' => 'Importació eliminada correctament', // Users 'user_create' => 'ha creat l’usuari', - 'user_create_notification' => 'Usuari creat correctament', + 'user_create_notification' => 'S’ha creat l’usuari', 'user_update' => 'ha actualitzat l’usuari', - 'user_update_notification' => 'Usuari actualitzat correctament', - 'user_delete' => 'ha suprimit un usuari', - 'user_delete_notification' => 'Usuari suprimit correctament', + 'user_update_notification' => 'S’ha actualitzat l’usuari', + 'user_delete' => 'ha suprimit l’usuari', + 'user_delete_notification' => 'S’ha suprimit l’usuari', // API Tokens - 'api_token_create' => 'ha creat un testimoni d’API', - 'api_token_create_notification' => 'Testimoni d’API creat correctament', - 'api_token_update' => 'ha actualitzat un testimoni d’API', - 'api_token_update_notification' => 'Testimoni d’API actualitzat correctament', - 'api_token_delete' => 'ha suprimit un testumoni d’API', - 'api_token_delete_notification' => 'Testimoni d’API suprimit correctament', + 'api_token_create' => 'ha creat el testimoni API', + 'api_token_create_notification' => 'S’ha creat el testimoni API', + 'api_token_update' => 'ha actualitzat el testimoni API', + 'api_token_update_notification' => 'S’ha actualitzat el testimoni API', + 'api_token_delete' => 'ha suprimit el testimoni API', + 'api_token_delete_notification' => 'S’ha suprimit el testimoni API', // Roles 'role_create' => 'ha creat el rol', - 'role_create_notification' => 'Rol creat correctament', + 'role_create_notification' => 'S’ha creat el rol', 'role_update' => 'ha actualitzat el rol', - 'role_update_notification' => 'Rol actualitzat correctament', - 'role_delete' => 'ha suprimit un rol', - 'role_delete_notification' => 'Rol suprimit correctament', + 'role_update_notification' => 'S’ha actualitzat el rol', + 'role_delete' => 'ha suprimit el rol', + 'role_delete_notification' => 'S’ha suprimit el rol', // Recycle Bin - 'recycle_bin_empty' => 'ha buidat la paperera de reciclatge', - 'recycle_bin_restore' => 'ha restaurat de la paperera de reciclatge', - 'recycle_bin_destroy' => 'ha suprimit de la paperera de reciclatge', + 'recycle_bin_empty' => 'ha buidat la paperera', + 'recycle_bin_restore' => 'ha restaurat de la paperera', + 'recycle_bin_destroy' => 'ha eliminat de la paperera', // Comments 'commented_on' => 'ha comentat a', - 'comment_create' => 'ha afegit el comentari', - 'comment_update' => 'ha actualitzat el comentari', + 'comment_create' => 'ha afegit un comentari', + 'comment_update' => 'ha actualitzat un comentari', 'comment_delete' => 'ha suprimit un comentari', + // Sort Rules + 'sort_rule_create' => 'crear regla d\'ordenació', + 'sort_rule_create_notification' => 'Regla d\'ordenació creada correctament', + 'sort_rule_update' => 'regla d\'ordenació actualitzada', + 'sort_rule_update_notification' => 'Regla d\'ordenació actualitzada correctament', + 'sort_rule_delete' => 'regla d\'ordenació eliminada', + 'sort_rule_delete_notification' => 'Regla d\'ordenació eliminada correctament', + // Other 'permissions_update' => 'ha actualitzat els permisos', ]; diff --git a/lang/ca/auth.php b/lang/ca/auth.php index 714c095b6c6..ba6ede42f0c 100644 --- a/lang/ca/auth.php +++ b/lang/ca/auth.php @@ -6,112 +6,112 @@ */ return [ - 'failed' => 'Les credencials no coincideixen amb les que hi ha emmagatzemades.', - 'throttle' => 'Massa intents d’inici de sessió. Torneu-ho a provar d’aquí a :seconds segons.', + 'failed' => 'Aquestes credencials no existeixen al nostre registre.', + 'throttle' => 'Massa intents d’inici de sessió. Torneu-ho a provar d’aquí :seconds segons.', // Login & Register - 'sign_up' => 'Registra-m’hi', - 'log_in' => 'Inicia la sessió', - 'log_in_with' => 'Inicia la sessió amb :socialDriver', - 'sign_up_with' => 'Registra-m’hi amb :socialDriver', + 'sign_up' => 'Registreu-vos', + 'log_in' => 'Inicia sessió', + 'log_in_with' => 'Inicia sessió amb :socialDriver', + 'sign_up_with' => 'Registreu-vos amb :socialDriver', 'logout' => 'Tanca la sessió', 'name' => 'Nom', 'username' => 'Nom d’usuari', - 'email' => 'Adreça electrònica', + 'email' => 'Correu electrònic', 'password' => 'Contrasenya', 'password_confirm' => 'Confirmeu la contrasenya', - 'password_hint' => 'Cal que tingui un mínim de 8 caràcters', + 'password_hint' => 'Ha de tenir almenys 8 caràcters', 'forgot_password' => 'Heu oblidat la contrasenya?', 'remember_me' => 'Recorda’m', - 'ldap_email_hint' => 'Introduïu una adreça electrònica per a aquest compte.', - 'create_account' => 'Crea el compte', + 'ldap_email_hint' => 'Introduïu un correu electrònic per al compte.', + 'create_account' => 'Creeu un compte', 'already_have_account' => 'Ja teniu un compte?', 'dont_have_account' => 'No teniu cap compte?', - 'social_login' => 'Inici de sessió amb xarxes socials', - 'social_registration' => 'Registre amb xarxes socials', - 'social_registration_text' => 'Registreu-vos i inicieu la sessió fent servir un altre servei.', + 'social_login' => 'Inici de sessió amb una xarxa social', + 'social_registration' => 'Registre amb una xarxa social', + 'social_registration_text' => 'Registreu-vos i inicieu sessió amb un altre servei.', - 'register_thanks' => 'Gràcies per registrar-vos!', - 'register_confirm' => 'Reviseu el vostre correu electrònic i feu clic al botó de confirmació per a accedir a :appName.', - 'registrations_disabled' => 'Actualment, els registres estan desactivats', - 'registration_email_domain_invalid' => 'Aquest domini de correu electrònic no té accés a aquesta aplicació', - 'register_success' => 'Gràcies per registrar-vos! Ja us hi heu registrat i heu iniciat la sessió.', + 'register_thanks' => 'Gràcies per registrar-vos.', + 'register_confirm' => 'Comproveu el correu electrònic i cliqueu el botó de confirmació per a accedir a :appName.', + 'registrations_disabled' => 'Els registres estan desactivats', + 'registration_email_domain_invalid' => 'Aquest domini de correu electrònic no té accés a l’aplicació.', + 'register_success' => 'Gràcies per registrar-vos Us heu registrat i heu iniciat sessió.', // Login auto-initiation - 'auto_init_starting' => 'S’està provant d’iniciar la sessió', - 'auto_init_starting_desc' => 'Estem contactant amb el vostre sistema d’autenticació per a començar el procés d’inici de sessió. Si no hi ha cap progrés d’aquí a 5 segons, proveu de fer clic a l’enllaç de sota.', - 'auto_init_start_link' => 'Continua amb l’autenticació', + 'auto_init_starting' => 'S’està provant d’iniciar sessió', + 'auto_init_starting_desc' => 'Estem contactant amb el vostre sistema d’autenticació per a començar el procés d’inici sessió. Si d’aquí 5 segons no hi ha hagut cap progrés proveu de clicar l’enllaç.', + 'auto_init_start_link' => 'Continua amb la l’autenticació', // Password Reset - 'reset_password' => 'Restableix la contrasenya', - 'reset_password_send_instructions' => 'Introduïu la vostra adreça electrònica a continuació i us enviarem un correu electrònic amb un enllaç per a restablir la contrasenya.', - 'reset_password_send_button' => 'Envia l’enllaç de restabliment', - 'reset_password_sent' => 'S’enviarà un enllaç per a restablir la contrasenya a :email, si es troba aquesta adreça al sistema.', - 'reset_password_success' => 'La contrasenya s’ha restablert correctament.', - 'email_reset_subject' => 'Restabliu la contrasenya a :appName', - 'email_reset_text' => 'Rebeu aquest correu electrònic perquè s’ha fet una petició de restabliment de contrasenya per al vostre compte.', - 'email_reset_not_requested' => 'Si no heu demanat restablir la contrasenya, no cal que emprengueu cap acció.', + 'reset_password' => 'Reinicialitza la contrasenya', + 'reset_password_send_instructions' => 'Introduïu la vostra adreça electrònica i us enviarem un correu electrònic amb un enllaç de reinicialització de la contrasenya.', + 'reset_password_send_button' => 'Envia’m un enllaç de reinicialització', + 'reset_password_sent' => 'Si l’adreça electrònica :email existeix al sistema, us hi enviarem un enllaç de reinicialització de contrasenya.', + 'reset_password_success' => 'S’ha reinicialitzat la contrasenya.', + 'email_reset_subject' => 'Reinicialització de la contrasenya de :appName', + 'email_reset_text' => 'Heu rebut aquest correu electrònic perquè hem rebut una sol·licitud de reinicialització de contrasenya per al vostre compte.', + 'email_reset_not_requested' => 'Si no heu sol·licitat la reinicialització de la contrasenya, no cal que feu res més.', // Email Confirmation - 'email_confirm_subject' => 'Confirmeu la vostra adreça electrònica a :appName', - 'email_confirm_greeting' => 'Gràcies per unir-vos a :appName!', - 'email_confirm_text' => 'Confirmeu la vostra adreça electrònica fent clic al botó a continuació:', - 'email_confirm_action' => 'Confirma el correu', - 'email_confirm_send_error' => 'Cal confirmar l’adreça electrònica, però el sistema no ha pogut enviar el correu electrònic. Poseu-vos en contacte amb l’administrador perquè s’asseguri que el correu electrònic està ben configurat.', - 'email_confirm_success' => 'Heu confirmat el vostre correu electrònic! Ara hauríeu de poder iniciar la sessió fent servir aquesta adreça electrònica.', - 'email_confirm_resent' => 'S’ha tornat a enviar el correu electrònic de confirmació. Reviseu la vostra safata d’entrada.', - 'email_confirm_thanks' => 'Gràcies per la confirmació!', - 'email_confirm_thanks_desc' => 'Espereu uns instants mentre gestionem la confirmació. Si no se us redirigeix d’aquí a 3 segons, premeu l’enllaç «Continua» de sota per a continuar.', + 'email_confirm_subject' => 'Confirmeu l’adreça electrònica de :appName', + 'email_confirm_greeting' => 'Gràcies per registrar-vos a :appName!', + 'email_confirm_text' => 'Cliqueu el botó per a confirmar l’adreça electrònica:', + 'email_confirm_action' => 'Confirmeu d’adreça electrònica', + 'email_confirm_send_error' => 'S’ha de confirmar de l’adreça electrònica però no s’ha pogut enviar el correu de confirmació. Contacteu l’administrador per a assegurar-vos que el correu està ben configurat.', + 'email_confirm_success' => 'S’ha confirmat el correu electrònic. Ara hauríeu de poder iniciar sessió amb aquesta adreça electrònica.', + 'email_confirm_resent' => 'S’ha enviat el correu de confirmació. Comproveu la safata d’entrada.', + 'email_confirm_thanks' => 'Gràcies per la confirmació.', + 'email_confirm_thanks_desc' => 'Espereu-vos un moment mentre es gestiona la confirmació. Si d’aquí 3 segons no se us ha redirigit, cliqueu l’enllaç «Continua» per a continuar.', - 'email_not_confirmed' => 'Adreça electrònica no confirmada', - 'email_not_confirmed_text' => 'La vostra adreça electrònica encara no està confirmada.', - 'email_not_confirmed_click_link' => 'Feu clic a l’enllaç del correu electrònic que us vam enviar poc després que us registréssiu.', - 'email_not_confirmed_resend' => 'Si no el trobeu, podeu tornar a enviar el correu electrònic de confirmació enviant el formulari a continuació.', + 'email_not_confirmed' => 'No s’ha confirmat l’adreça de correu electrònic', + 'email_not_confirmed_text' => 'Encara no heu confirmat l’adreça electrònica.', + 'email_not_confirmed_click_link' => 'Cliqueu l’enllaç que hi ha al correu electrònic que se us va enviar en registrar-vos.', + 'email_not_confirmed_resend' => 'Si no trobeu el correu electrònic ompliu el formulari a continuació i us n’enviarem un altre.', 'email_not_confirmed_resend_button' => 'Torna a enviar el correu de confirmació', // User Invite - 'user_invite_email_subject' => 'Us han convidat a unir-vos a :appName!', - 'user_invite_email_greeting' => 'S’ha creat un compte en el vostre nom a :appName.', - 'user_invite_email_text' => 'Feu clic al botó a continuació per a definir una contrasenya per al compte i obtenir-hi accés:', - 'user_invite_email_action' => 'Defineix una contrasenya per al compte', + 'user_invite_email_subject' => 'Us han convidat a utilitzar :appName!', + 'user_invite_email_greeting' => 'Us han creat un compte a :appName.', + 'user_invite_email_text' => 'Cliqueu el botó per a configurar una contrasenya pel compte i poder-hi accedir:', + 'user_invite_email_action' => 'Configura la contrasenya del compte', 'user_invite_page_welcome' => 'Us donem la benvinguda a :appName!', - 'user_invite_page_text' => 'Per a enllestir el vostre compte i obtenir-hi accés, cal que definiu una contrasenya, que es farà servir per a iniciar la sessió a :appName en futures visites.', + 'user_invite_page_text' => 'Per a poder accedir al compte heu de configurar una contrasenya que s’utilitzarà per a iniciar sessió a :appName d’ara endavant.', 'user_invite_page_confirm_button' => 'Confirma la contrasenya', - 'user_invite_success_login' => 'S’ha definit la contrasenya. Ara hauríeu de poder iniciar la sessió fent servir la contrasenya que heu definit per a accedir a :appName!', + 'user_invite_success_login' => 'S’ha configurat la contrasenya. Ara hauríeu de poder iniciar sessió a :appName amb la contrasenya configurada.', // Multi-factor Authentication - 'mfa_setup' => 'Configura l’autenticació de múltiple factor', - 'mfa_setup_desc' => 'Configureu l’autenticació de múltiple factor com a capa extra de seguretat en el vostre compte d’usuari.', - 'mfa_setup_configured' => 'Ja està configurada', + 'mfa_setup' => 'Configuració de l’autenticació multifactorial', + 'mfa_setup_desc' => 'Configureu l’autenticació multifactorial per a afegir una capa de seguretat extra al vostre compte d’usuari.', + 'mfa_setup_configured' => 'Ja està configurat', 'mfa_setup_reconfigure' => 'Torna-la a configurar', - 'mfa_setup_remove_confirmation' => 'Segur que voleu suprimir aquest mètode d’autenticació de múltiple factor?', + 'mfa_setup_remove_confirmation' => 'Esteu segur que voleu eliminar aquest mètode d’autenticació multifactorial?', 'mfa_setup_action' => 'Configura', - 'mfa_backup_codes_usage_limit_warning' => 'Teniu menys de 5 codis de seguretat restants. Genereu-ne i emmagatzemeu-ne un nou conjunt abans que se us acabin perquè no perdeu l’accés al vostre compte.', + 'mfa_backup_codes_usage_limit_warning' => 'Us queden menys de 5 codis de suport. Genereu-ne de nous i deseu-los abans de quedar-vos-en sense per a evitar perdre l’accés al compte.', 'mfa_option_totp_title' => 'Aplicació mòbil', - 'mfa_option_totp_desc' => 'Per a fer servir l’autenticació de múltiple factor us caldrà una aplicació mòbil que suporti TOTP, com ara Google Authenticador, Authy o Microsoft Authenticator.', - 'mfa_option_backup_codes_title' => 'Codis de seguretat', - 'mfa_option_backup_codes_desc' => 'Deseu de manera segura codis de seguretat d’un sol ús que podeu introduir per a verificar la vostra identitat.', + 'mfa_option_totp_desc' => 'Per a utilitzar l’autenticació multifactorial heu de tenir una aplicació que sigui compatible amb TOPT (contrasenyes d’un sol ús basades en el temps) com ara Google Authenticator, Authy, o Microsoft Authenticator.', + 'mfa_option_backup_codes_title' => 'Codis de suport', + 'mfa_option_backup_codes_desc' => 'Genereu un joc de codis de suport d’un sol ús que haureu d’introduir per a verificar la vostra identitat. Assegureu-vos de desar-los en un lloc segur.', 'mfa_gen_confirm_and_enable' => 'Confirma i activa', - 'mfa_gen_backup_codes_title' => 'Configuració de codis de seguretat', - 'mfa_gen_backup_codes_desc' => 'Deseu la següent llista de codis en un lloc segur. Quan accediu al sistema, podeu fer servir un dels codis com a segon mètode d’autenticació.', + 'mfa_gen_backup_codes_title' => 'Configuració dels codis de suport', + 'mfa_gen_backup_codes_desc' => 'Deseu els codis en un lloc segur. Podreu utilitzar un dels codis com a un altre mètode d’autenticació per a iniciar sessió.', 'mfa_gen_backup_codes_download' => 'Baixa els codis', - 'mfa_gen_backup_codes_usage_warning' => 'Cada codi es pot fer servir només una vegada', + 'mfa_gen_backup_codes_usage_warning' => 'Només podeu utilitzar cada codi un sol cop.', 'mfa_gen_totp_title' => 'Configuració de l’aplicació mòbil', - 'mfa_gen_totp_desc' => 'Per a fer servir l’autenticació de múltiple factor us caldrà una aplicació mòbil que suporti TOTP, com ara Google Authenticador, Authy o Microsoft Authenticator.', - 'mfa_gen_totp_scan' => 'Per a començar, escanegeu el codi QR següent fent servir la vostre aplicació d’autenticació preferida.', - 'mfa_gen_totp_verify_setup' => 'Verifica la configuració', - 'mfa_gen_totp_verify_setup_desc' => 'Verifiqueu que tot funciona introduint un codi, generat amb l’aplicació d’autenticació, a la capsa de text següent:', - 'mfa_gen_totp_provide_code_here' => 'Proporcioneu aquí el codi generat per l’aplicació', + 'mfa_gen_totp_desc' => 'Per a utilitzar l’autenticació multifactorial heu de tenir una aplicació que sigui compatible amb TOPT (contrasenyes d’un sol ús basades en el temps) com ara Google Authenticator, Authy, o Microsoft Authenticator.', + 'mfa_gen_totp_scan' => 'Escanegeu el codi QR amb l’aplicació d’autenticació que preferiu per a començar.', + 'mfa_gen_totp_verify_setup' => 'Verificació de la configuració', + 'mfa_gen_totp_verify_setup_desc' => 'Introduïu un dels codis generats per l’aplicació d’autenticació per a verificar que tot funciona:', + 'mfa_gen_totp_provide_code_here' => 'Codi', 'mfa_verify_access' => 'Verifica l’accés', - 'mfa_verify_access_desc' => 'El vostre compte d’usuari requereix que confirmeu la vostra identitat amb un nivell addicional de verificació abans que pugueu obtenir-hi accés. Verifiqueu-la fent servir un dels vostres mètodes configurats per a continuar.', + 'mfa_verify_access_desc' => 'Heu de verificar la vostra identitat amb un nivell de verificació addicional per a iniciar sessió. Verifiqueu-la amb un dels mètodes que heu configurat per a continuar.', 'mfa_verify_no_methods' => 'No hi ha cap mètode configurat', - 'mfa_verify_no_methods_desc' => 'No s’ha trobat cap mètode d’autenticació de múltiple factor al vostre compte. Cal que configureu almenys un mètode abans d’obtenir-hi accés.', - 'mfa_verify_use_totp' => 'Verifica fent servir una aplicació mòbil', - 'mfa_verify_use_backup_codes' => 'Verifica fent servir un codi de seguretat', - 'mfa_verify_backup_code' => 'Codi de seguretat', - 'mfa_verify_backup_code_desc' => 'Introduïu un dels vostres codis de seguretat restants a continuació:', - 'mfa_verify_backup_code_enter_here' => 'Introduïu aquí el codi de seguretat', - 'mfa_verify_totp_desc' => 'Introduïu el codi generat amb la vostra aplicació mòbil a continuació:', - 'mfa_setup_login_notification' => 'S’ha configurat el mètode d’autenticació de múltiple factor. Torneu a iniciar la sessió fent servir el mètode que heu configurat.', + 'mfa_verify_no_methods_desc' => 'No hi ha cap mètode d’autenticació multifactorial configurat al vostre compte. Heu de configurar com a mínim un mètode per a iniciar sessió.', + 'mfa_verify_use_totp' => 'Verificació amb una aplicació mòbil', + 'mfa_verify_use_backup_codes' => 'Verificació amb un codi de suport', + 'mfa_verify_backup_code' => 'Codi de suport', + 'mfa_verify_backup_code_desc' => 'Introduïu un dels codis de suport que us quedin:', + 'mfa_verify_backup_code_enter_here' => 'Codi:', + 'mfa_verify_totp_desc' => 'Introduïu el codi generat per l’aplicació mòbil:', + 'mfa_setup_login_notification' => 'S’ha configurat el mètode multifactorial. Torneu a iniciar sessió amb el mètode configurat.', ]; diff --git a/lang/ca/common.php b/lang/ca/common.php index 1798c82b716..8976edaa127 100644 --- a/lang/ca/common.php +++ b/lang/ca/common.php @@ -7,8 +7,8 @@ // Buttons 'cancel' => 'Cancel·la', 'close' => 'Tanca', - 'confirm' => 'D’acord', - 'back' => 'Enrere', + 'confirm' => 'Confirma', + 'back' => 'Torna', 'save' => 'Desa', 'continue' => 'Continua', 'select' => 'Selecciona', @@ -18,18 +18,20 @@ // Form Labels 'name' => 'Nom', 'description' => 'Descripció', - 'role' => 'Rol', - 'cover_image' => 'Imatge de portada', - 'cover_image_description' => 'Aquesta imatge hauria de fer aproximadament 440x250 px.', + 'role' => 'Funció', + 'cover_image' => 'Imatge de coberta', + 'cover_image_description' => 'La imatge ha de ser d’uns 440×250px, però quan calgui se n’ajustarà la mida o s’escapçarà per a adaptar-la a la interfície d’usuari i les mides amb què es mostrarà canviaran.', // Actions 'actions' => 'Accions', - 'view' => 'Visualitza', - 'view_all' => 'Visualitza-ho tot', + 'view' => 'Mostra', + 'view_all' => 'Mostra-ho tot', 'new' => 'Nou', 'create' => 'Crea', 'update' => 'Actualitza', 'edit' => 'Edita', + 'archive' => 'Arxivar', + 'unarchive' => 'Desarxivar', 'sort' => 'Ordena', 'move' => 'Mou', 'copy' => 'Copia', @@ -44,10 +46,10 @@ 'configure' => 'Configura', 'manage' => 'Gestiona', 'fullscreen' => 'Pantalla completa', - 'favourite' => 'Afegeix a preferits', - 'unfavourite' => 'Suprimeix de preferits', - 'next' => 'Següent', - 'previous' => 'Anterior', + 'favourite' => 'Afegeix als favorits', + 'unfavourite' => 'Suprimeix dels favorits', + 'next' => 'Endavant', + 'previous' => 'Endarrere', 'filter_active' => 'Filtre actiu:', 'filter_clear' => 'Esborra el filtre', 'download' => 'Baixa', @@ -56,26 +58,26 @@ // Sort Options 'sort_options' => 'Opcions d’ordenació', - 'sort_direction_toggle' => 'Commuta la direcció de l’ordenació', - 'sort_ascending' => 'Ordre ascendent', - 'sort_descending' => 'Ordre descendent', + 'sort_direction_toggle' => 'Inverteix l’ordre', + 'sort_ascending' => 'Ordenació ascendent', + 'sort_descending' => 'Ordenació descendent', 'sort_name' => 'Nom', - 'sort_default' => 'Per defecte', + 'sort_default' => 'Predeterminat', 'sort_created_at' => 'Data de creació', 'sort_updated_at' => 'Data d’actualització', // Misc 'deleted_user' => 'Usuari suprimit', - 'no_activity' => 'No hi ha activitat', - 'no_items' => 'No hi ha cap element', - 'back_to_top' => 'Torna a dalt', - 'skip_to_main_content' => 'Vés al contingut', + 'no_activity' => 'No hi ha cap activitat per mostrar', + 'no_items' => 'No hi ha cap element disponible', + 'back_to_top' => 'Amunt', + 'skip_to_main_content' => 'Ves al contingut principal', 'toggle_details' => 'Commuta els detalls', 'toggle_thumbnails' => 'Commuta les miniatures', 'details' => 'Detalls', 'grid_view' => 'Visualització en graella', 'list_view' => 'Visualització en llista', - 'default' => 'Per defecte', + 'default' => 'Predeterminat', 'breadcrumb' => 'Ruta de navegació', 'status' => 'Estat', 'status_active' => 'Actiu', @@ -84,8 +86,8 @@ 'none' => 'Cap', // Header - 'homepage' => 'Pàgina principal', - 'header_menu_expand' => 'Expandeix el menú de la capçalera', + 'homepage' => 'Pàgina d’inici', + 'header_menu_expand' => 'Desplega el menú de la capçalera', 'profile_menu' => 'Menú del perfil', 'view_profile' => 'Mostra el perfil', 'edit_profile' => 'Edita el perfil', @@ -100,11 +102,14 @@ 'tab_content_label' => 'Pestanya: Mostra el contingut principal', // Email Content - 'email_action_help' => 'Si teniu problemes per a fer clic al botó «:actionText», copieu i enganxeu l’URL següent al vostre navegador web:', + 'email_action_help' => 'Si el botó «:actionText» no funciona, copieu i enganxeu l’URL següent al vostre navegador:', 'email_rights' => 'Tots els drets reservats', // Footer Link Options // Not directly used but available for convenience to users. 'privacy_policy' => 'Política de privadesa', 'terms_of_service' => 'Condicions del servei', + + // OpenSearch + 'opensearch_description' => 'Buscar :appName', ]; diff --git a/lang/ca/components.php b/lang/ca/components.php index 9c34850213d..c1494ec53a9 100644 --- a/lang/ca/components.php +++ b/lang/ca/components.php @@ -9,37 +9,37 @@ 'image_list' => 'Llista d’imatges', 'image_details' => 'Detalls de la imatge', 'image_upload' => 'Puja una imatge', - 'image_intro' => 'Aquí podeu seleccionar i gestionar imatges que s’hagin pujat anteriorment al sistema.', - 'image_intro_upload' => 'Pugeu una imatge nova arrossegant un fitxer d’imatge a aquesta finestra o fent servir el botó de «Puja una imatge».', + 'image_intro' => 'Seleccioneu i gestioneu les imatges que s’han pujat al sistema amb anterioritat.', + 'image_intro_upload' => 'Pugeu una imatge arrossegant-la i deixant-la anar en aquesta finestra o amb el botó «Puja una imatge».', 'image_all' => 'Totes', 'image_all_title' => 'Mostra totes les imatges', - 'image_book_title' => 'Mostra les imatges pujades a aquest llibre', - 'image_page_title' => 'Mostra les imatges pujades a aquesta pàgina', - 'image_search_hint' => 'Cerca per nom d’imatge', - 'image_uploaded' => 'Pujada :uploadedDate', + 'image_book_title' => 'Mostra les imatges pujades en aquest llibre', + 'image_page_title' => 'Mostra les imatges pujades en aquesta pàgina', + 'image_search_hint' => 'Cerca pel nom de la imatge', + 'image_uploaded' => 'Pujada el :uploadedDate', 'image_uploaded_by' => 'Pujada per :userName', 'image_uploaded_to' => 'Pujada a :pageLink', - 'image_updated' => 'Actualitzada :updateDate', + 'image_updated' => 'Actualitzada el :updateDate', 'image_load_more' => 'Carrega’n més', 'image_image_name' => 'Nom de la imatge', - 'image_delete_used' => 'Aquesta imatge s’utilitza a les pàgines següents.', - 'image_delete_confirm_text' => 'Segur que voleu suprimir aquesta imatge?', - 'image_select_image' => 'Selecciona una imatge', - 'image_dropzone' => 'Arrossegueu imatges o feu clic aquí per a pujar-les', - 'image_dropzone_drop' => 'Arrossegueu imatges aquí per a pujar-les', + 'image_delete_used' => 'Aquesta imatge s’utilitza en les pàgines següents.', + 'image_delete_confirm_text' => 'Esteu segur que voleu suprimir aquesta imatge?', + 'image_select_image' => 'Selecció d’imatges', + 'image_dropzone' => 'Deixeu-hi anar les imatges o cliqueu aquí per a pujar-ne', + 'image_dropzone_drop' => 'Deixeu-hi anar les imatges per a pujar-les', 'images_deleted' => 'Imatges suprimides', 'image_preview' => 'Previsualització de la imatge', - 'image_upload_success' => 'Imatge pujada correctament', - 'image_update_success' => 'Detalls de la imatge actualitzats correctament', - 'image_delete_success' => 'Imatge suprimida correctament', + 'image_upload_success' => 'S’ha pujat la imatge', + 'image_update_success' => 'S’han actualitzat els detalls de la imatge', + 'image_delete_success' => 'S’ha suprimit la imatge', 'image_replace' => 'Substitueix la imatge', - 'image_replace_success' => 'Fitxer de la imatge actualitzat correctament', + 'image_replace_success' => 'S’ha substituït la imatge', 'image_rebuild_thumbs' => 'Regenera les variacions de mida', - 'image_rebuild_thumbs_success' => 'Variacions de mida de la imatge regenerades correctament!', + 'image_rebuild_thumbs_success' => 'S’han regenerat les variacions de mida', // Code Editor 'code_editor' => 'Edita el codi', - 'code_language' => 'Llenguatge del codi', + 'code_language' => 'Llenguatge de codificació', 'code_content' => 'Contingut del codi', 'code_session_history' => 'Historial de la sessió', 'code_save' => 'Desa el codi', diff --git a/lang/ca/editor.php b/lang/ca/editor.php index eaef6402fb4..e19d82f8f3c 100644 --- a/lang/ca/editor.php +++ b/lang/ca/editor.php @@ -13,32 +13,33 @@ 'cancel' => 'Cancel·la', 'save' => 'Desa', 'close' => 'Tanca', + 'apply' => 'Aplicar', 'undo' => 'Desfés', 'redo' => 'Refés', 'left' => 'Esquerra', 'center' => 'Centre', 'right' => 'Dreta', - 'top' => 'Superior', - 'middle' => 'Centre', - 'bottom' => 'Inferior', + 'top' => 'Dalt', + 'middle' => 'Mig', + 'bottom' => 'Baix', 'width' => 'Amplada', - 'height' => 'Alçada', + 'height' => 'Altura', 'More' => 'Més', - 'select' => 'Selecciona...', + 'select' => 'Selecciona …', // Toolbar 'formats' => 'Formats', - 'header_large' => 'Capçalera grossa', - 'header_medium' => 'Capçalera mitjana', - 'header_small' => 'Capçalera petita', - 'header_tiny' => 'Capçalera minúscula', + 'header_large' => 'Títol gros', + 'header_medium' => 'Títol mitjà', + 'header_small' => 'Títol petit', + 'header_tiny' => 'Títol minúscul', 'paragraph' => 'Paràgraf', - 'blockquote' => 'Citació', - 'inline_code' => 'Codi en línia', - 'callouts' => 'Llegendes', + 'blockquote' => 'Bloc de cita', + 'inline_code' => 'Línia de codi', + 'callouts' => 'Crides', 'callout_information' => 'Informació', 'callout_success' => 'Èxit', - 'callout_warning' => 'Advertència', + 'callout_warning' => 'Advertiment', 'callout_danger' => 'Perill', 'bold' => 'Negreta', 'italic' => 'Cursiva', @@ -47,31 +48,32 @@ 'superscript' => 'Superíndex', 'subscript' => 'Subíndex', 'text_color' => 'Color del text', + 'highlight_color' => 'Color ressaltat', 'custom_color' => 'Color personalitzat', - 'remove_color' => 'Suprimeix el color', + 'remove_color' => 'Elimina el color', 'background_color' => 'Color de fons', 'align_left' => 'Alinea a l’esquerra', 'align_center' => 'Alinea al centre', 'align_right' => 'Alinea a la dreta', 'align_justify' => 'Justifica', - 'list_bullet' => 'Llista amb pics', + 'list_bullet' => 'Llista de pics', 'list_numbered' => 'Llista numerada', - 'list_task' => 'Llista de tasques', - 'indent_increase' => 'Augmenta el sagnat', - 'indent_decrease' => 'Redueix el sagnat', + 'list_task' => 'Llista de caselles', + 'indent_increase' => 'Augmenta la sagnia', + 'indent_decrease' => 'Redueix la sagnia', 'table' => 'Taula', 'insert_image' => 'Insereix una imatge', - 'insert_image_title' => 'Insereix/edita una imatge', - 'insert_link' => 'Insereix/edita un enllaç', - 'insert_link_title' => 'Insereix/edita un enllaç', + 'insert_image_title' => 'Insereix o edita una imatge', + 'insert_link' => 'Insereix un enllaç', + 'insert_link_title' => 'Insereix o edita un enllaç', 'insert_horizontal_line' => 'Insereix una línia horitzontal', 'insert_code_block' => 'Insereix un bloc de codi', - 'edit_code_block' => 'Edita un bloc de codi', - 'insert_drawing' => 'Insereix/edita un dibuix', - 'drawing_manager' => 'Gestor de dibuixos', - 'insert_media' => 'Insereix/edita un mitjà', - 'insert_media_title' => 'Insereix/edita un mitjà', - 'clear_formatting' => 'Neteja el format', + 'edit_code_block' => 'Edita el bloc de codi', + 'insert_drawing' => 'Insereix o edita un diagrama', + 'drawing_manager' => 'Gestor de diagrames', + 'insert_media' => 'Insereix o edita un contingut multimèdia', + 'insert_media_title' => 'Insereix o edita un contingut multimèdia', + 'clear_formatting' => 'Lleva el format', 'source_code' => 'Codi font', 'source_code_title' => 'Codi font', 'fullscreen' => 'Pantalla completa', @@ -81,6 +83,9 @@ 'table_properties' => 'Propietats de la taula', 'table_properties_title' => 'Propietats de la taula', 'delete_table' => 'Suprimeix la taula', + 'table_clear_formatting' => 'Lleva el format de la taula', + 'resize_to_contents' => 'Ajusta al contingut', + 'row_header' => 'Capçalera de la fila', 'insert_row_before' => 'Insereix una fila abans', 'insert_row_after' => 'Insereix una fila després', 'delete_row' => 'Suprimeix la fila', @@ -94,10 +99,10 @@ 'cell_properties_title' => 'Propietats de la cel·la', 'cell_type' => 'Tipus de cel·la', 'cell_type_cell' => 'Cel·la', - 'cell_scope' => 'Àmbit', + 'cell_scope' => 'Àmbit de la cel·la', 'cell_type_header' => 'Cel·la de capçalera', - 'merge_cells' => 'Fusiona les cel·les', - 'split_cell' => 'Divideix les cel·les', + 'merge_cells' => 'Combina les cel·les', + 'split_cell' => 'Divideix la cel·la', 'table_row_group' => 'Grup de files', 'table_column_group' => 'Grup de columnes', 'horizontal_align' => 'Alineació horitzontal', @@ -114,61 +119,64 @@ 'row_type' => 'Tipus de fila', 'row_type_header' => 'Capçalera', 'row_type_body' => 'Cos', - 'row_type_footer' => 'Peu de pàgina', + 'row_type_footer' => 'Peu', 'alignment' => 'Alineació', 'cut_column' => 'Retalla la columna', 'copy_column' => 'Copia la columna', 'paste_column_before' => 'Enganxa la columna abans', 'paste_column_after' => 'Enganxa la columna després', - 'cell_padding' => 'Separació de la cel·la', - 'cell_spacing' => 'Espaiat de la cel·la', - 'caption' => 'Títol', - 'show_caption' => 'Mostra el títol', + 'cell_padding' => 'Separació de cel·la', + 'cell_spacing' => 'Espaiat de cel·la', + 'caption' => 'Llegenda', + 'show_caption' => 'Mostra la llegenda', 'constrain' => 'Mantén les proporcions', 'cell_border_solid' => 'Sòlida', - 'cell_border_dotted' => 'Puntejada', - 'cell_border_dashed' => 'Discontínua', + 'cell_border_dotted' => 'De punts', + 'cell_border_dashed' => 'De guions', 'cell_border_double' => 'Doble', - 'cell_border_groove' => 'Bisellada', - 'cell_border_ridge' => 'Emmarcada', - 'cell_border_inset' => 'Enfonsada', - 'cell_border_outset' => 'Ressaltada', - 'cell_border_none' => 'Cap', - 'cell_border_hidden' => 'Oculta', + 'cell_border_groove' => 'Vora enfonsada', + 'cell_border_ridge' => 'Vora ressaltada', + 'cell_border_inset' => 'Fons enfonsat', + 'cell_border_outset' => 'Fons ressaltat', + 'cell_border_none' => 'Sense vora', + 'cell_border_hidden' => 'Amagada', // Images, links, details/summary & embed - 'source' => 'Origen', + 'source' => 'Font', 'alt_desc' => 'Descripció alternativa', - 'embed' => 'Incrustació', - 'paste_embed' => 'Enganxeu el codi d’incrustació a continuació:', + 'embed' => 'Incrusta', + 'paste_embed' => 'Enganxa el codi per a incrustar a sota:', 'url' => 'URL', - 'text_to_display' => 'Text a mostrar', + 'text_to_display' => 'Text per a mostrar', 'title' => 'Títol', + 'browse_links' => 'Explorar enllaços', 'open_link' => 'Obre l’enllaç', - 'open_link_in' => 'Obre l’enllaç a...', - 'open_link_current' => 'Finestra actual', - 'open_link_new' => 'Finestra nova', - 'remove_link' => 'Suprimeix l’enllaç', - 'insert_collapsible' => 'Insereix un bloc contraïble', - 'collapsible_unwrap' => 'Deixa de contraure', + 'open_link_in' => 'Obre l’enllaç…', + 'open_link_current' => 'A la finestra actual', + 'open_link_new' => 'En una finestra nova', + 'remove_link' => 'Lleva l’enllaç', + 'insert_collapsible' => 'Insereix un bloc desplegable', + 'collapsible_unwrap' => 'Lleva el bloc desplegable', 'edit_label' => 'Edita l’etiqueta', - 'toggle_open_closed' => 'Obre/tanca', - 'collapsible_edit' => 'Edita el bloc contraïble', + 'toggle_open_closed' => 'Obre o tanca', + 'collapsible_edit' => 'Edita el bloc desplegable', 'toggle_label' => 'Commuta l’etiqueta', // About view - 'about' => 'Quant a l’editor', - 'about_title' => 'Quant a l’editor WYSIWYG', - 'editor_license' => 'Llicència i copyright de l’editor', - 'editor_tiny_license' => 'Aquest editor s’ha construït amb :tinyLink, que es proporciona sota la llicència MIT.', - 'editor_tiny_license_link' => 'Podeu trobar els detalls de la llicència i el copyright del TinyMCE aquí.', + 'about' => 'Quant a l’Editor', + 'about_title' => 'Quant a l’Editor WYSIWYG', + 'editor_license' => 'Llicència i copyright de l’Editor', + 'editor_lexical_license' => 'Aquest editor està construït com una bifurcació de :lexicalLink i es distribueix sota la llicència MIT.', + 'editor_lexical_license_link' => 'Tots els detalls complets de la llicència es poden trobar aquí.', + 'editor_tiny_license' => 'Aquest editor s’ha creat amb :tinyLink que es proporciona amb la llicència MIT.', + 'editor_tiny_license_link' => 'Detalls de la llicència i el copyright de TinyMCE.', 'save_continue' => 'Desa la pàgina i continua', - 'callouts_cycle' => '(Continueu prement per a commutar els diferents tipus)', - 'link_selector' => 'Enllaç al contingut', - 'shortcuts' => 'Dreceres', + 'callouts_cycle' => '(Continueu prement per a canviar entre tipus)', + 'link_selector' => 'Enllaç a contingut', + 'shortcuts' => 'Dreceres de teclat', 'shortcut' => 'Drecera', - 'shortcuts_intro' => 'Hi ha les següents dreceres disponibles a l’editor:', - 'windows_linux' => '(Windows/Linux)', + 'shortcuts_intro' => 'A l’editor, hi ha disponibles les dreceres de teclat següents:', + 'windows_linux' => '(Windows o Linux)', 'mac' => '(Mac)', 'description' => 'Descripció', ]; diff --git a/lang/ca/entities.php b/lang/ca/entities.php index 53b766b4259..108a38c2c50 100644 --- a/lang/ca/entities.php +++ b/lang/ca/entities.php @@ -6,76 +6,104 @@ return [ // Shared - 'recently_created' => 'Creat fa poc', - 'recently_created_pages' => 'Pàgines creades fa poc', - 'recently_updated_pages' => 'Pàgines actualitzades fa poc', - 'recently_created_chapters' => 'Capítols creats fa poc', - 'recently_created_books' => 'Llibres creats fa poc', - 'recently_created_shelves' => 'Prestatges creats fa poc', - 'recently_update' => 'Actualitzat fa poc', - 'recently_viewed' => 'Vist fa poc', + 'recently_created' => 'Elements creats recentment', + 'recently_created_pages' => 'Pàgines creades recentment', + 'recently_updated_pages' => 'Pàgines actualitzades recentment', + 'recently_created_chapters' => 'Capítols creats recentment', + 'recently_created_books' => 'Llibres creats recentment', + 'recently_created_shelves' => 'Prestatges creats recentment', + 'recently_update' => 'Elements actualitzats recentment', + 'recently_viewed' => 'Elements vistos recentment', 'recent_activity' => 'Activitat recent', - 'create_now' => 'Crea’n ara', + 'create_now' => 'Crea un element', 'revisions' => 'Revisions', 'meta_revision' => 'Revisió núm. :revisionCount', - 'meta_created' => 'Creat :timeLength', - 'meta_created_name' => 'Creat :timeLength per :user', - 'meta_updated' => 'Actualitzat :timeLength', - 'meta_updated_name' => 'Actualitzat :timeLength per :user', + 'meta_created' => 'S’ha creat :timeLength', + 'meta_created_name' => ':user l’ha creat :timeLength', + 'meta_updated' => 'S’ha actualitzat :timeLength', + 'meta_updated_name' => ':user l’ha actualitzat :timeLength', 'meta_owned_name' => 'Propietat de :user', - 'meta_reference_count' => 'Hi fa referència :count element|Hi fan referència :count elements', - 'entity_select' => 'Selecciona una entitat', + 'meta_reference_count' => 'S’hi fa referència en :count element|S’hi fa referència en :count elements', + 'entity_select' => 'Selecció de l’entitat', 'entity_select_lack_permission' => 'No teniu els permisos necessaris per a seleccionar aquest element', 'images' => 'Imatges', - 'my_recent_drafts' => 'Els vostres esborranys recents', - 'my_recently_viewed' => 'Les vostres visualitzacions recents', - 'my_most_viewed_favourites' => 'Els vostres preferits més vistos', - 'my_favourites' => 'Els vostres preferits', + 'my_recent_drafts' => 'Els meus esborranys recents', + 'my_recently_viewed' => 'Els meus vistos recentment', + 'my_most_viewed_favourites' => 'Els meus favorits més vistos', + 'my_favourites' => 'Els meus favorits', 'no_pages_viewed' => 'No heu vist cap pàgina', - 'no_pages_recently_created' => 'No s’ha creat cap pàgina fa poc', - 'no_pages_recently_updated' => 'No s’ha actualitzat cap pàgina fa poc', + 'no_pages_recently_created' => 'No s’ha creat cap pàgina recentment', + 'no_pages_recently_updated' => 'No s’ha actualitzat cap pàgina recentment', 'export' => 'Exporta', 'export_html' => 'Fitxer web independent', 'export_pdf' => 'Fitxer PDF', 'export_text' => 'Fitxer de text sense format', 'export_md' => 'Fitxer Markdown', + 'export_zip' => 'ZIP portable', + 'default_template' => 'Plantilla de pàgina per defecte', + 'default_template_explain' => 'Assigna una plantilla de pàgina que s\'utilitzarà com a contingut predeterminat per a totes les pàgines creades en aquest element. Tingues en compte que això només s\'utilitzarà si el creador de pàgines té accés a la plantilla de pàgina triada.', + 'default_template_select' => 'Seleccioneu una plantilla de pàgina', + 'import' => 'Importar', + 'import_validate' => 'Validar importació', + 'import_desc' => 'Importar llibres, capítols i pàgines utilitzant una exportació ZIP portable de la mateixa o una altra instància. Selecciona un arxiu ZIP per continuar. Després que l\'arxiu s\'hagi penjat i validat, podrà configurar i confirmar l\'importació en la següent vista.', + 'import_zip_select' => 'Seleccioneu un fitxer ZIP per pujar', + 'import_zip_validation_errors' => 'S\'han detectat errors al validar l\'arxiu ZIP proporcionat:', + 'import_pending' => 'Importació pendent', + 'import_pending_none' => 'No s\'han iniciat les importacions.', + 'import_continue' => 'Continuar importació', + 'import_continue_desc' => 'Revisa el contingut que s\'ha d\'importar de l\'arxiu ZIP penjat. Quan estigui llest, executa l\'importació per afegir el seu contingut al sistema. L\'arxiu d\'importació ZIP penjat s\'eliminarà automàticament al finalitzar l\'importació correctament.', + 'import_details' => 'Detalls d\'importació', + 'import_run' => 'Executar importació', + 'import_size' => ':size Mida de l\'arxiu ZIP', + 'import_uploaded_at' => 'Penjat :relativeTime', + 'import_uploaded_by' => 'Actualitzat per', + 'import_location' => 'Importar ubicació', + 'import_location_desc' => 'Selecciona una ubicació de destí pel contingut importat. Necessitarà els permisos pertinents per crear-lo dins de la ubicació triada.', + 'import_delete_confirm' => 'Esteu segur que voleu suplir aquesta importació?', + 'import_delete_desc' => 'Això eliminarà l\'arxiu ZIP d\'importació penjat i no es pot desfer.', + 'import_errors' => 'Importar errors', + 'import_errors_desc' => 'S\'han produït els següents errors durant l\'intent d\'importació:', + 'breadcrumb_siblings_for_page' => 'Navegar entre pàgines del mateix nivell', + 'breadcrumb_siblings_for_chapter' => 'Navegar entre capítols del mateix nivell', + 'breadcrumb_siblings_for_book' => 'Navegar entre llibres del mateix nivell', + 'breadcrumb_siblings_for_bookshelf' => 'Navegar entre llibreries del mateix nivell', // Permissions and restrictions 'permissions' => 'Permisos', - 'permissions_desc' => 'Definiu aquí permisos que tindran preferència sobre les permisos per defecte proporcionats pels rols d’usuari.', - 'permissions_book_cascade' => 'Els permisos definits en llibres s’hereten automàticament en pàgines i capítols inferiors, llevat que tinguin permisos propis definits.', - 'permissions_chapter_cascade' => 'Els permisos definits en capítols s’hereten automàticament en pàgines inferiors, llevat que tinguin permisos propis definits.', + 'permissions_desc' => 'Configureu aquí els permisos per a sobreescriure els permisos per defecte proporcionats pels rols d’usuari.', + 'permissions_book_cascade' => 'Els permisos dels llibres s’aplicaran també als capítols i les pàgines que continguin llevat que tinguin els seus propis permisos.', + 'permissions_chapter_cascade' => 'Els permisos dels capítols s’aplicaran també a les pàgines que continguin llevat que tinguin els seus propis permisos.', 'permissions_save' => 'Desa els permisos', 'permissions_owner' => 'Propietari', - 'permissions_role_everyone_else' => 'Tota la resta', - 'permissions_role_everyone_else_desc' => 'Definiu permisos per a tots els rols no definits específicament.', - 'permissions_role_override' => 'Força els permisos per al rol', - 'permissions_inherit_defaults' => 'Hereta els permisos per defecte', + 'permissions_role_everyone_else' => 'La resta dels usuaris', + 'permissions_role_everyone_else_desc' => 'Configureu permisos per a tots els rols que no s’hagin sobreescrit específicament.', + 'permissions_role_override' => 'Sobreescriu els permisos per al rol', + 'permissions_inherit_defaults' => 'Hereta la configuració per defecte', // Search 'search_results' => 'Resultats de la cerca', - 'search_total_results_found' => 'S’ha trobat :count resultat|S’han trobat :count resultats en total', + 'search_total_results_found' => 'S’ha trobat :count coincidència|S’han trobat :count coincidències', 'search_clear' => 'Esborra la cerca', - 'search_no_pages' => 'La cerca no coincideix amb cap pàgina', + 'search_no_pages' => 'No s’ha trobat cap pàgina que coincideixi amb la cerca', 'search_for_term' => 'Cerca :term', 'search_more' => 'Més resultats', 'search_advanced' => 'Cerca avançada', - 'search_terms' => 'Termes de la cerca', + 'search_terms' => 'Termes de cerca', 'search_content_type' => 'Tipus de contingut', 'search_exact_matches' => 'Coincidències exactes', 'search_tags' => 'Cerca d’etiquetes', 'search_options' => 'Opcions', - 'search_viewed_by_me' => 'Visualitzat per mi', - 'search_not_viewed_by_me' => 'No visualitzat per mi', - 'search_permissions_set' => 'Amb permisos definits', - 'search_created_by_me' => 'Creat per mi', - 'search_updated_by_me' => 'Actualitzat per mi', - 'search_owned_by_me' => 'En sóc propietari', - 'search_date_options' => 'Opcions de dates', - 'search_updated_before' => 'Actualitzat abans de', - 'search_updated_after' => 'Actualitzat després de', - 'search_created_before' => 'Creat abans de', - 'search_created_after' => 'Creat després de', + 'search_viewed_by_me' => 'Vistos per mi', + 'search_not_viewed_by_me' => 'No vistos per mi', + 'search_permissions_set' => 'Amb permisos', + 'search_created_by_me' => 'Creats per mi', + 'search_updated_by_me' => 'Actualitzats per mi', + 'search_owned_by_me' => 'En soc propietari', + 'search_date_options' => 'Opcions de data', + 'search_updated_before' => 'Actualitzats abans de', + 'search_updated_after' => 'Actualitzats després de', + 'search_created_before' => 'Creats abans de', + 'search_created_after' => 'Creats després de', 'search_set_date' => 'Defineix una data', 'search_update' => 'Actualitza la cerca', @@ -83,90 +111,89 @@ 'shelf' => 'Prestatge', 'shelves' => 'Prestatges', 'x_shelves' => ':count prestatge|:count prestatges', - 'shelves_empty' => 'No hi ha cap prestatge creat', - 'shelves_create' => 'Crea un prestatge nou', + 'shelves_empty' => 'No hi ha cap prestatge', + 'shelves_create' => 'Crea un prestatge', 'shelves_popular' => 'Prestatges populars', 'shelves_new' => 'Prestatges nous', 'shelves_new_action' => 'Prestatge nou', 'shelves_popular_empty' => 'Aquí apareixeran els prestatges més populars.', - 'shelves_new_empty' => 'Aquí apareixeran els prestatges creats fa poc.', + 'shelves_new_empty' => 'Aquí apareixeran els prestatges més recents.', 'shelves_save' => 'Desa el prestatge', 'shelves_books' => 'Llibres en aquest prestatge', - 'shelves_add_books' => 'Afegeix llibres a aquest prestatge', - 'shelves_drag_books' => 'Arrossegueu llibres a sota per a afegir-los a aquest prestatge', - 'shelves_empty_contents' => 'Aquest prestatge no té cap llibre assignat', - 'shelves_edit_and_assign' => 'Editeu el prestatge per a assignar-hi llibres', - 'shelves_edit_named' => 'Edita el prestatge :name', + 'shelves_add_books' => 'Afegiu llibres en aquest prestatge', + 'shelves_drag_books' => 'Arrossegueu i deixeu anar llibres a sota per a afegir-los en aquest prestatge', + 'shelves_empty_contents' => 'Aquest prestatge no té cap llibre', + 'shelves_edit_and_assign' => 'Editeu el prestatge per a afegir-hi llibres', + 'shelves_edit_named' => 'Edita el prestatge «:name»', 'shelves_edit' => 'Edita el prestatge', 'shelves_delete' => 'Suprimeix el prestatge', - 'shelves_delete_named' => 'Suprimeix el prestatge :name', - 'shelves_delete_explain' => "Se suprimirà el prestatge amb el nom «:name». Els llibres que contingui no se suprimiran.", - 'shelves_delete_confirmation' => 'Segur que voleu suprimir aquest prestatge?', + 'shelves_delete_named' => 'Suprimeix del prestatge «:name»', + 'shelves_delete_explain' => "Se suprimirà el prestatge «:name». No se suprimiran els llibres que contingui.", + 'shelves_delete_confirmation' => 'Esteu segur que voleu suprimir aquest prestatge?', 'shelves_permissions' => 'Permisos del prestatge', 'shelves_permissions_updated' => 'S’han actualitzat els permisos del prestatge', - 'shelves_permissions_active' => 'Permisos del prestatge actius', - 'shelves_permissions_cascade_warning' => 'Els permisos dels prestatges no s’hereten automàticament en els llibres que contenen. És així perquè un llibre pot estar en múltiples prestatges. Es poden copiar, però, els permisos als llibres continguts fent servir l’opció a continuació.', - 'shelves_permissions_create' => 'Els permisos de crear prestatges només es fan servir per a copiar permisos a llibres continguts fent servir l’acció següent. No controlen la capacitat de crear llibres.', + 'shelves_permissions_active' => 'El prestatge té permisos', + 'shelves_permissions_cascade_warning' => 'Els permisos dels prestatges no s’aplicaran als llibres que contingui. Això és perquè els llibres poden estar en més d’un prestatge a la vegada. Això no obstant, els permisos es poden copiar als llibres que contingui amb l’opció de sota.', + 'shelves_permissions_create' => 'Els permisos per a crear prestatges només s’utilitzen per a copiar permisos als llibres que contenen amb l’acció de sota. No controlen els permisos per a crear llibres.', 'shelves_copy_permissions_to_books' => 'Copia els permisos als llibres', 'shelves_copy_permissions' => 'Copia els permisos', - 'shelves_copy_permissions_explain' => 'S’aplicarà la configuració de permisos actual a tots els llibres continguts. Abans d’activar-la, assegureu-vos que hàgiu desat qualsevol canvi en els permisos d’aquest prestatge.', + 'shelves_copy_permissions_explain' => 'S’aplicaran els permisos del prestatge a tots els llibres que contingui. Abans de continuar assegureu-vos que heu desat els canvis que hàgiu fet als permisos del prestatge.', 'shelves_copy_permission_success' => 'S’han copiat els permisos del prestatge a :count llibres', // Books 'book' => 'Llibre', 'books' => 'Llibres', 'x_books' => ':count llibre|:count llibres', - 'books_empty' => 'No hi ha cap llibre creat', + 'books_empty' => 'No hi ha cap llibre', 'books_popular' => 'Llibres populars', 'books_recent' => 'Llibres recents', 'books_new' => 'Llibres nous', 'books_new_action' => 'Llibre nou', 'books_popular_empty' => 'Aquí apareixeran els llibres més populars.', - 'books_new_empty' => 'Aquí apareixeran els llibres creats fa poc.', - 'books_create' => 'Crea un llibre nou', + 'books_new_empty' => 'Aquí apareixeran els llibres més recents.', + 'books_create' => 'Crea un llibre', 'books_delete' => 'Suprimeix el llibre', - 'books_delete_named' => 'Suprimeix el llibre :bookName', - 'books_delete_explain' => 'Se suprimirà el llibre amb el nom «:bookName». Se’n suprimiran totes les pàgines i tots els capítols.', - 'books_delete_confirmation' => 'Segur que voleu suprimir aquest llibre?', + 'books_delete_named' => 'Suprimeix el llibre «:bookName»', + 'books_delete_explain' => 'Se suprimirà el llibre «:bookName». Se suprimiran totes les pàgines i capítols.', + 'books_delete_confirmation' => 'Esteu segur que voleu suprimir aquest llibre?', 'books_edit' => 'Edita el llibre', - 'books_edit_named' => 'Edita el llibre :bookName', - 'books_form_book_name' => 'Nom del llibre', + 'books_edit_named' => 'Edita el llibre «:bookName»', + 'books_form_book_name' => 'Títol del llibre', 'books_save' => 'Desa el llibre', - 'books_default_template' => 'Plantilla de pàgina per defecte', - 'books_default_template_explain' => 'Assigneu una plantilla de pàgina que s’utilitzarà com a contingut per defecte de totes les pàgines noves d’aquest llibre. Tingueu en compte que només es farà servir si qui crea la pàgina té accés de visualització a la plantilla de pàgina elegida.', - 'books_default_template_select' => 'Seleccioneu una plantilla de pàgina', 'books_permissions' => 'Permisos del llibre', - 'books_permissions_updated' => 'S’han actualitzat els permisos del llibre', - 'books_empty_contents' => 'No hi ha cap pàgina ni cap capítol creat en aquest llibre.', - 'books_empty_create_page' => 'Crea una pàgina nova', - 'books_empty_sort_current_book' => 'Ordena el llibre actual', + 'books_permissions_updated' => 'S’han actualitzat els permisos', + 'books_empty_contents' => 'No hi ha cap capítol ni cap pàgina en aquest llibre.', + 'books_empty_create_page' => 'Crea una pàgina', + 'books_empty_sort_current_book' => 'Ordena el llibre', 'books_empty_add_chapter' => 'Afegeix un capítol', - 'books_permissions_active' => 'S’han activat els permisos del llibre', + 'books_permissions_active' => 'El llibre té permisos', 'books_search_this' => 'Cerca en aquest llibre', - 'books_navigation' => 'Navegació pel llibre', - 'books_sort' => 'Ordena el contingut del llibre', - 'books_sort_desc' => 'Moveu capítols i pàgines dins d’un llibre per a reorganitzar-ne el contingut. Podeu afegir altres llibres, la qual cosa permet moure fàcilment capítols i pàgines entre llibres.', - 'books_sort_named' => 'Ordena el llibre :bookName', - 'books_sort_name' => 'Ordena per nom', - 'books_sort_created' => 'Ordena per data de creació', - 'books_sort_updated' => 'Ordena per data d’actualització', - 'books_sort_chapters_first' => 'Els capítols al principi', - 'books_sort_chapters_last' => 'Els capítols al final', + 'books_navigation' => 'Navegació del llibre', + 'books_sort' => 'Ordena el contingut d’un llibre', + 'books_sort_desc' => 'Mou capítols i pàgines dins d\'un llibre per reorganitzar el seu contingut. Es poden afegir altres llibres que permetin moure fàcilment capítols i pàgines entre llibres. De manera opcional, es poden establir regles d\'ordenació automàtica per ordenar automàticament el contingut d\'aquest llibre quan hi hagi canvis.', + 'books_sort_auto_sort' => 'Opció d\'ordenació automàtica', + 'books_sort_auto_sort_active' => 'Opció d\'ordenació activa :sortName', + 'books_sort_named' => 'Ordena el llibre «:bookName»', + 'books_sort_name' => 'Ordena pel nom', + 'books_sort_created' => 'Ordena per la data de creació', + 'books_sort_updated' => 'Ordena per la data d’actualització', + 'books_sort_chapters_first' => 'Capítols al principi', + 'books_sort_chapters_last' => 'Capítols al final', 'books_sort_show_other' => 'Mostra altres llibres', 'books_sort_save' => 'Desa l’ordre nou', - 'books_sort_show_other_desc' => 'Afegiu aquí altres llibres per a incloure’ls a l’operació d’ordenació i permetre la reorganització entre llibres de manera fàcil.', - 'books_sort_move_up' => 'Mou amunt', - 'books_sort_move_down' => 'Mou avall', - 'books_sort_move_prev_book' => 'Mou al llibre anterior', - 'books_sort_move_next_book' => 'Mou al llibre següent', - 'books_sort_move_prev_chapter' => 'Mou al capítol anterior', - 'books_sort_move_next_chapter' => 'Mou al capítol següent', - 'books_sort_move_book_start' => 'Mou a l’inici del llibre', - 'books_sort_move_book_end' => 'Mou al final del llibre', - 'books_sort_move_before_chapter' => 'Mou al capítol anterior', - 'books_sort_move_after_chapter' => 'Mou al capítol següent', + 'books_sort_show_other_desc' => 'Afegiu llibres per a incloure’ls en l’ordenació i permetre una reorganització entre llibres més senzilla.', + 'books_sort_move_up' => 'Mou-ho cap amunt', + 'books_sort_move_down' => 'Mou-ho cap avall', + 'books_sort_move_prev_book' => 'Mou-ho al llibre anterior', + 'books_sort_move_next_book' => 'Mou-ho al llibre següent', + 'books_sort_move_prev_chapter' => 'Mou-ho al capítol anterior', + 'books_sort_move_next_chapter' => 'Mou-ho al capítol següent', + 'books_sort_move_book_start' => 'Mou-ho al principi del llibre', + 'books_sort_move_book_end' => 'Mou-ho al final del llibre', + 'books_sort_move_before_chapter' => 'Mou-ho a abans del capítol', + 'books_sort_move_after_chapter' => 'Mou-ho a després del capítol', 'books_copy' => 'Copia el llibre', - 'books_copy_success' => 'Llibre copiat correctament', + 'books_copy_success' => 'S’ha copiat el llibre', // Chapters 'chapter' => 'Capítol', @@ -174,21 +201,21 @@ 'x_chapters' => ':count capítol|:count capítols', 'chapters_popular' => 'Capítols populars', 'chapters_new' => 'Capítol nou', - 'chapters_create' => 'Crea un capítol nou', - 'chapters_delete' => 'Suprimeix el capítol', - 'chapters_delete_named' => 'Suprimeix el capítol :chapterName', - 'chapters_delete_explain' => 'Se suprimirà el capítol amb el nom «:chapterName». Totes les pàgines que contingui també se suprimiran.', - 'chapters_delete_confirm' => 'Segur que voleu suprimir aquest capítol?', + 'chapters_create' => 'Crea un capítol', + 'chapters_delete' => 'Suprimeix un capítol', + 'chapters_delete_named' => 'Suprimeix el capítol «:chapterName»', + 'chapters_delete_explain' => 'Se suprimirà el capítol «:chapterName». Se suprimiran totes les pàgines que hi ha al capítol.', + 'chapters_delete_confirm' => 'Esteu segur que voleu suprimir aquest capítol?', 'chapters_edit' => 'Edita el capítol', - 'chapters_edit_named' => 'Edita el capítol :chapterName', + 'chapters_edit_named' => 'Edita el capítol «:chapterName»', 'chapters_save' => 'Desa el capítol', 'chapters_move' => 'Mou el capítol', - 'chapters_move_named' => 'Mou el capítol :chapterName', + 'chapters_move_named' => 'Mou el capítol «:chapterName»', 'chapters_copy' => 'Copia el capítol', - 'chapters_copy_success' => 'Capítol copiat correctament', + 'chapters_copy_success' => 'S’ha copiat el capítol', 'chapters_permissions' => 'Permisos del capítol', - 'chapters_empty' => 'De moment, aquest capítol no conté cap pàgina.', - 'chapters_permissions_active' => 'S’han activat els permisos del capítol', + 'chapters_empty' => 'No hi ha cap pàgina en aquest capítol.', + 'chapters_permissions_active' => 'El capítol té permisos', 'chapters_permissions_success' => 'S’han actualitzat els permisos del capítol', 'chapters_search_this' => 'Cerca en aquest capítol', 'chapter_sort_book' => 'Ordena el llibre', @@ -198,105 +225,108 @@ 'pages' => 'Pàgines', 'x_pages' => ':count pàgina|:count pàgines', 'pages_popular' => 'Pàgines populars', - 'pages_new' => 'Pàgina nova', - 'pages_attachments' => 'Adjuncions', - 'pages_navigation' => 'Navegació per la pàgina', + 'pages_new' => 'Crea una pàgina', + 'pages_attachments' => 'Fitxers adjunts', + 'pages_navigation' => 'Navegació de la pàgina', 'pages_delete' => 'Suprimeix la pàgina', - 'pages_delete_named' => 'Suprimeix la pàgina :pageName', - 'pages_delete_draft_named' => 'Suprimeix l’esborrany de pàgina :pageName', + 'pages_delete_named' => 'Suprimeix la pàgina «:pageName»', + 'pages_delete_draft_named' => 'Suprimeix l’esborrany de pàgina «:pageName»', 'pages_delete_draft' => 'Suprimeix l’esborrany de pàgina', 'pages_delete_success' => 'S’ha suprimit la pàgina', 'pages_delete_draft_success' => 'S’ha suprimit l’esborrany de pàgina', - 'pages_delete_warning_template' => 'Aquesta pàgina es fa servir com a plantilla de pàgina per defecte en algun llibre. Quan l’hàgiu suprimida, aquests llibres ja no tindran assignada cap plantilla de pàgina per defecte.', - 'pages_delete_confirm' => 'Segur que voleu suprimir aquesta pàgina?', - 'pages_delete_draft_confirm' => 'Segur que voleu suprimir aquest esborrany de pàgina?', - 'pages_editing_named' => 'Esteu editant :pageName', + 'pages_delete_warning_template' => 'Aquesta pàgina està en ús com a plantilla de pàgina predeterminada de llibre o capítol. Aquests llibres o capítols ja no tindran una plantilla de pàgina predeterminada assignada després d\'eliminar aquesta pàgina.', + 'pages_delete_confirm' => 'Esteu segur que voleu suprimir aquesta pàgina?', + 'pages_delete_draft_confirm' => 'Esteu segur que voleu suprimir aquest esborrany de pàgina?', + 'pages_editing_named' => 'Edició de la pàgina «:pageName»', 'pages_edit_draft_options' => 'Opcions d’esborrany', 'pages_edit_save_draft' => 'Desa l’esborrany', 'pages_edit_draft' => 'Edita l’esborrany de pàgina', - 'pages_editing_draft' => 'Esteu editant l’esborrany', - 'pages_editing_page' => 'Esteu editant la pàgina', - 'pages_edit_draft_save_at' => 'Esborrany desat ', + 'pages_editing_draft' => 'Estàs editant l’esborrany', + 'pages_editing_page' => 'Edita la pàgina', + 'pages_edit_draft_save_at' => 'S’ha desat l’esborrany a les ', 'pages_edit_delete_draft' => 'Suprimeix l’esborrany', - 'pages_edit_delete_draft_confirm' => 'Segur que voleu suprimir els canvis a l’esborrany de pàgina? Es perdran tots els vostres canvis d’ençà de la darrera vegada que heu desat i s’actualitzarà l’editor amb l’estat de la darrera pàgina desada sense ser un esborrany.', + 'pages_edit_delete_draft_confirm' => 'Esteu segur que voleu suprimir els canvis de l’esborrany de pàgina? Es perdran tots els canvis que hàgiu fet després de la darrera vegada que vau desar la pàgina i l’editor s’actualitzarà amb l’estat de la darrera vegada que es va desar la pàgina que no era esborrany.', 'pages_edit_discard_draft' => 'Descarta l’esborrany', - 'pages_edit_switch_to_markdown' => 'Canvia a l’editor Markdown', + 'pages_edit_switch_to_markdown' => 'Canvia a l’editor de Markdown', 'pages_edit_switch_to_markdown_clean' => '(Contingut net)', 'pages_edit_switch_to_markdown_stable' => '(Contingut estable)', 'pages_edit_switch_to_wysiwyg' => 'Canvia a l’editor WYSIWYG', - 'pages_edit_set_changelog' => 'Defineix el registre de canvis', - 'pages_edit_enter_changelog_desc' => 'Introduïu una breu descripció dels canvis que heu fet', - 'pages_edit_enter_changelog' => 'Introduïu un registre de canvis', - 'pages_editor_switch_title' => 'Canvia d’editor', - 'pages_editor_switch_are_you_sure' => 'Segur que voleu canviar l’editor d’aquesta pàgina?', - 'pages_editor_switch_consider_following' => 'Considereu el següent en canviar d’editor:', - 'pages_editor_switch_consideration_a' => 'Quan hàgiu desat, l’opció del nou editor serà la que utilitzaran tots els futurs editors, incloent-hi els que no poden canviar de tipus d’editor amb el seu usuari.', - 'pages_editor_switch_consideration_b' => 'En algunes circumstàncies, això pot comportar una pèrdua de detalls i de sintaxi.', - 'pages_editor_switch_consideration_c' => 'Els canvis al registre de canvis o a les etiquetes fets d’ençà de la darrera vegada que s’ha desat no es mantindran en aquest canvi.', + 'pages_edit_switch_to_new_wysiwyg' => 'Canviar al nou editor WYSIWYG', + 'pages_edit_switch_to_new_wysiwyg_desc' => '(En Beta Test)', + 'pages_edit_set_changelog' => 'Registre de canvis', + 'pages_edit_enter_changelog_desc' => 'Introduïu una descripció breu dels canvis que heu fet', + 'pages_edit_enter_changelog' => 'Registra un canvi', + 'pages_editor_switch_title' => 'Canvia l’editor', + 'pages_editor_switch_are_you_sure' => 'Esteu segur que voleu canviar l’editor d’aquesta pàgina?', + 'pages_editor_switch_consider_following' => 'Quan canvieu l’editor tingueu en compte que:', + 'pages_editor_switch_consideration_a' => 'Un cop s’hagi desat, l’editor nou serà el que hagin d’utilitzar tots els editor futurs, incloent-hi aquells que no puguin canviar el tipus d’editor.', + 'pages_editor_switch_consideration_b' => 'És possible que hi hagi pèrdues en el detall i la sintaxi.', + 'pages_editor_switch_consideration_c' => 'Els canvis en les etiquetes i el registre de canvis que s’hagin fet després de la darrera vegada que es va desar no es conservaran.', 'pages_save' => 'Desa la pàgina', 'pages_title' => 'Títol de la pàgina', 'pages_name' => 'Nom de la pàgina', 'pages_md_editor' => 'Editor', - 'pages_md_preview' => 'Previsualització', + 'pages_md_preview' => 'Visualització prèvia', 'pages_md_insert_image' => 'Insereix una imatge', - 'pages_md_insert_link' => 'Insereix un enllaç a una entitat', - 'pages_md_insert_drawing' => 'Insereix un diagrama', - 'pages_md_show_preview' => 'Mostra la previsualització', - 'pages_md_sync_scroll' => 'Sincronitza el desplaçament de la previsualització', - 'pages_drawing_unsaved' => 'S’ha trobat un diagrama no desat', - 'pages_drawing_unsaved_confirm' => 'S’han trobat dades d’un diagrama d’un intent de desat fallit anterior. Voleu restaurar-les i continuar editant aquest diagrama no desat?', - 'pages_not_in_chapter' => 'La pàgina no pertany a cap capítol', + 'pages_md_insert_link' => 'Insereix un enllaç d’entitat', + 'pages_md_insert_drawing' => 'Insereix un dibuix', + 'pages_md_show_preview' => 'Mostra la visualització prèvia', + 'pages_md_sync_scroll' => 'Sincronitza el desplaçament de la visualització prèvia', + 'pages_md_plain_editor' => 'Editor de text pla', + 'pages_drawing_unsaved' => 'S’ha trobat un dibuix sense desar', + 'pages_drawing_unsaved_confirm' => 'S’han trobat dades d’un dibuix d’un intent anterior de desar un dibuix. Voleu restaurar aquest dibuix no desat per a reprendre’n l’edició?', + 'pages_not_in_chapter' => 'La pàgina no és un capítol', 'pages_move' => 'Mou la pàgina', 'pages_copy' => 'Copia la pàgina', - 'pages_copy_desination' => 'Destinació de la còpia', - 'pages_copy_success' => 'Pàgina copiada correctament', + 'pages_copy_desination' => 'Copia la destinació', + 'pages_copy_success' => 'S’ha copiat la pàgina', 'pages_permissions' => 'Permisos de la pàgina', 'pages_permissions_success' => 'S’han actualitzat els permisos de la pàgina', 'pages_revision' => 'Revisió', 'pages_revisions' => 'Revisions de la pàgina', - 'pages_revisions_desc' => 'A continuació hi ha les revisions anteriors de la pàgina. Podeu mirar-les, comparar-les i restaurar-ne versions antigues si us ho permeten els permisos. És possible que l’historial complet de la pàgina no s’hi vegi reflectit perquè, depenent de la configuració del sistema, es poden haver esborrat automàticament revisions antigues.', - 'pages_revisions_named' => 'Revisions de la pàgina :pageName', - 'pages_revision_named' => 'Revisió de la pàgina :pageName', - 'pages_revision_restored_from' => 'Restaurada de núm. :id; :summary', - 'pages_revisions_created_by' => 'Creada per', + 'pages_revisions_desc' => 'A continuació hi ha la llista de totes les revisions d’aquesta pàgina. Si hi teniu permís, podeu mirar, comparar i restaurar versions antigues de la pàgina. És possible que no hi aparegui tot l’historial de la pàgina ja que, segons la configuració del sistema, és possible que s’hagin suprimit les revisions antigues automàticament.', + 'pages_revisions_named' => 'Revisions de la pàgina «:pageName»', + 'pages_revision_named' => 'Revisió de la pàgina «:pageName»', + 'pages_revision_restored_from' => 'S’ha restaurat de núm. :id: :summary', + 'pages_revisions_created_by' => 'Revisor:', 'pages_revisions_date' => 'Data de la revisió', - 'pages_revisions_number' => 'Núm. ', - 'pages_revisions_sort_number' => 'Número de revisió', + 'pages_revisions_number' => '#', + 'pages_revisions_sort_number' => 'Número de la revisió', 'pages_revisions_numbered' => 'Revisió núm. :id', 'pages_revisions_numbered_changes' => 'Canvis de la revisió núm. :id', 'pages_revisions_editor' => 'Tipus d’editor', 'pages_revisions_changelog' => 'Registre de canvis', 'pages_revisions_changes' => 'Canvis', 'pages_revisions_current' => 'Versió actual', - 'pages_revisions_preview' => 'Previsualitza', + 'pages_revisions_preview' => 'Visualització prèvia', 'pages_revisions_restore' => 'Restaura', 'pages_revisions_none' => 'Aquesta pàgina no té cap revisió', 'pages_copy_link' => 'Copia l’enllaç', - 'pages_edit_content_link' => 'Vés a la secció a l’editor', - 'pages_pointer_enter_mode' => 'Activa el mode de selecció de secció', - 'pages_pointer_label' => 'Opcions de la secció de la pàgina', - 'pages_pointer_permalink' => 'Enllaç permanent de la secció de la pàgina', - 'pages_pointer_include_tag' => 'Etiqueta d’inclusió de la secció de la pàgina', - 'pages_pointer_toggle_link' => 'Mode d’enllaç permanent, premeu per a mostrar l’etiqueta d‘inclusió', - 'pages_pointer_toggle_include' => 'Mode d’etiqueta d’inclusió, premeu per a mostrar l’enllaç permanent', - 'pages_permissions_active' => 'S’han activat els permisos de la pàgina', + 'pages_edit_content_link' => 'Ves a la secció a l’editor', + 'pages_pointer_enter_mode' => 'Entra al mode de selecció de secció', + 'pages_pointer_label' => 'Opcions de la secció de pàgina', + 'pages_pointer_permalink' => 'Enllaç permanent de la secció de pàgina', + 'pages_pointer_include_tag' => 'Etiqueta inclou de la secció de pàgina', + 'pages_pointer_toggle_link' => 'Enllaç permanent. Cliqueu per a mostrar l’etiqueta d’inclusió', + 'pages_pointer_toggle_include' => 'Etiqueta d’inclusió. Cliqueu per a mostrar l’enllaç permanent', + 'pages_permissions_active' => 'La pàgina té permisos', 'pages_initial_revision' => 'Publicació inicial', - 'pages_references_update_revision' => 'Actualització automàtica dels enllaços interns del sistema', + 'pages_references_update_revision' => 'Actualització automàtica dels enllaços interns', 'pages_initial_name' => 'Pàgina nova', - 'pages_editing_draft_notification' => 'Esteu editant un esborrany que es va desar per darrer cop :timeDiff.', - 'pages_draft_edited_notification' => 'Aquesta pàgina s’ha actualitzat d’ençà d’aleshores. Us recomanem que descarteu aquest esborrany.', - 'pages_draft_page_changed_since_creation' => 'Aquesta pàgina s’ha actualitzat d‘ençà que es va crear l’esborrany. Us recomanem que descarteu aquest esborrany i que aneu amb compte de no sobreescriure els canvis de la pàgina.', + 'pages_editing_draft_notification' => 'Esteu editant un esborrany que es va desar :timeDiff.', + 'pages_draft_edited_notification' => 'Des de llavors, s’ha actualitzat la pàgina. Us recomanem que descarteu aquest esborrany.', + 'pages_draft_page_changed_since_creation' => 'Des que es va crear l’esborrany, la pàgina s’ha actualitzat. Us recomanem que descarteu aquest esborrany o que aneu amb compte de no sobreescriure cap canvi.', 'pages_draft_edit_active' => [ 'start_a' => ':count usuaris han començat a editar aquesta pàgina', 'start_b' => ':userName ha començat a editar aquesta pàgina', - 'time_a' => 'd’ençà que la pàgina es va actualitzar per darrer cop', + 'time_a' => 'des que es va actualitzar aquesta pàgina', 'time_b' => 'en els darrers :minCount minuts', - 'message' => ':start :time. Aneu amb compte de no trepitjar-vos les actualitzacions entre vosaltres!', + 'message' => ':start :time. Aneu amb compte de no sobreescriure-us els canvis.', ], - 'pages_draft_discarded' => 'S’ha descartat l’esborrany! S’ha actualitzat l’editor amb el contingut actual de la pàgina', - 'pages_draft_deleted' => 'S’ha suprimit l’esborrany! S’ha actualitzat l’editor amb el contingut actual de la pàgina', - 'pages_specific' => 'Una pàgina específica', - 'pages_is_template' => 'Plantilla de pàgina', + 'pages_draft_discarded' => 'S’ha descartat l’esborrany. L’editor s’ha actualitzat amb el contingut actual de la pàgina', + 'pages_draft_deleted' => 'S’ha suprimit l’esborrany! L’editor s’ha actualitzat amb el contingut actual de la pàgina', + 'pages_specific' => 'Pàgina específica', + 'pages_is_template' => 'Plantilla de la pàgina', // Editor Sidebar 'toggle_sidebar' => 'Commuta la barra lateral', @@ -306,57 +336,57 @@ 'shelf_tags' => 'Etiquetes del prestatge', 'tag' => 'Etiqueta', 'tags' => 'Etiquetes', - 'tags_index_desc' => 'Es poden aplicar etiquetes al contingut dins del sistema per a aplicar un mode de categorització flexible. Les etiquetes poden tenir una clau i un valor, però el valor és opcional. Quan s’han aplican, el contingut es pot cercar fent servir el nom o el valor de l’etiqueta.', + 'tags_index_desc' => 'Es poden posar etiquetes al contingut per a aconseguir una categorització flexible. Les etiquetes han de tenir una clau i poden o no tenir un valor. Un cop s’hi han posat, es pot cercar el contingut utilitzant el nom o el valor de les etiquetes.', 'tag_name' => 'Nom de l’etiqueta', 'tag_value' => 'Valor de l’etiqueta (opcional)', - 'tags_explain' => "Afegiu etiquetes per a categoritzar millor el contingut. \n Podeu assignar un valor a cada etiqueta per a una organització més detallada.", - 'tags_add' => 'Afegeix una altra etiqueta', - 'tags_remove' => 'Elimina aquesta etiqueta', - 'tags_usages' => 'Usos totals de l’etiqueta', - 'tags_assigned_pages' => 'Assignada a pàgines', - 'tags_assigned_chapters' => 'Assignada a capítols', - 'tags_assigned_books' => 'Assignada a llibres', - 'tags_assigned_shelves' => 'Assignada a prestatges', + 'tags_explain' => "Poseu alguna etiqueta per a categoritzar millor el contingut. \n Podeu assignar un valor a l’etiqueta per a aconseguir una organització més detallada.", + 'tags_add' => 'Posa-hi una altra etiqueta', + 'tags_remove' => 'Lleva aquesta etiqueta', + 'tags_usages' => 'Ús total de les etiquetes', + 'tags_assigned_pages' => 'Assignades a pàgines', + 'tags_assigned_chapters' => 'Assignades a capítols', + 'tags_assigned_books' => 'Assignades a llibres', + 'tags_assigned_shelves' => 'Assignades a prestatges', 'tags_x_unique_values' => ':count valors únics', 'tags_all_values' => 'Tots els valors', 'tags_view_tags' => 'Mostra les etiquetes', 'tags_view_existing_tags' => 'Mostra les etiquetes existents', - 'tags_list_empty_hint' => 'Es poden assignar etiquetes mitjançant la barra lateral de l’editor de la pàgina o quan s’editen els detalls d’un llibre, capítol o prestatge.', - 'attachments' => 'Adjuncions', - 'attachments_explain' => 'Pugeu fitxers o adjunteu enllaços per a mostrar-los a la pàgina. Són visibles a la barra lateral de la pàgina.', - 'attachments_explain_instant_save' => 'Els canvis fets aquí es desen instantàniament.', + 'tags_list_empty_hint' => 'Podeu posar etiquetes a la barra lateral de l’editor o quan canvieu la informació d’un capítol o d’un prestatge.', + 'attachments' => 'Fitxers adjunts', + 'attachments_explain' => 'Pugeu fitxers o afegiu enllaços per a mostrar a la pàgina. Els podreu veure a la barra lateral de la pàgina.', + 'attachments_explain_instant_save' => 'Els canvis dels fitxers adjunts es desen a l’instant.', 'attachments_upload' => 'Puja un fitxer', - 'attachments_link' => 'Adjunta un enllaç', - 'attachments_upload_drop' => 'De manera alternativa, podeu arrossegar i deixar anar un fitxer aquí per a pujar-lo com a adjunció.', - 'attachments_set_link' => 'Defineix l’enllaç', - 'attachments_delete' => 'Segur que voleu suprimir aquesta adjunció?', - 'attachments_dropzone' => 'Deixeu anar fitxers aquí per a pujar-los', + 'attachments_link' => 'Afegeix un enllaç', + 'attachments_upload_drop' => 'També podeu arrossegar i deixar anar un fitxer aquí per a adjuntar-lo.', + 'attachments_set_link' => 'Configura l’enllaç', + 'attachments_delete' => 'Esteu segur que voleu suprimir aquest fitxer adjunt?', + 'attachments_dropzone' => 'Deixeu-hi anar fitxers per a pujar-los', 'attachments_no_files' => 'No s’ha pujat cap fitxer', - 'attachments_explain_link' => 'Podeu adjuntar un enllaç si preferiu no pujar un fitxer. Pot ser un enllaç a una altra pàgina o un enllaç a un fitxer al núvol.', + 'attachments_explain_link' => 'Si us estimeu més no pujar un fitxer, podeu afegir un enllaç. Pot ser un enllaç en una altra pàgina o a un fitxer al núvol.', 'attachments_link_name' => 'Nom de l’enllaç', - 'attachment_link' => 'Enllaç de l’adjunció', + 'attachment_link' => 'Enllaç del fitxer adjunt', 'attachments_link_url' => 'Enllaç al fitxer', - 'attachments_link_url_hint' => 'URL del lloc o fitxer', + 'attachments_link_url_hint' => 'URL del lloc o del fitxer.', 'attach' => 'Adjunta', - 'attachments_insert_link' => 'Afegeix un enllaç de l’adjunció a la pàgina', + 'attachments_insert_link' => 'Afegeix un enllaç de fitxer adjunt a la pàgina', 'attachments_edit_file' => 'Edita el fitxer', 'attachments_edit_file_name' => 'Nom del fitxer', - 'attachments_edit_drop_upload' => 'Arrossegueu fitxers o feu clic aquí per a pujar-los i sobreescriure’ls', - 'attachments_order_updated' => 'S’ha actualitzat l’ordre de les adjuncions', - 'attachments_updated_success' => 'S’han actualitzat els detalls de les adjuncions', - 'attachments_deleted' => 'S’ha suprimit l’adjunció', - 'attachments_file_uploaded' => 'Fitxer pujat correctament', - 'attachments_file_updated' => 'Fitxer actualitzat correctament', - 'attachments_link_attached' => 'Enllaç adjuntat a la pàgina correctament', + 'attachments_edit_drop_upload' => 'Deixeu-hi anar fitxers o cliqueu aquí per a pujar-ne i sobreescriure’ls.', + 'attachments_order_updated' => 'S’ha actualitzat l’ordre del fitxer adjunt', + 'attachments_updated_success' => 'S’han actualitzat els detalls dels fitxer adjunt', + 'attachments_deleted' => 'S’ha suprimit el fitxer adjunt', + 'attachments_file_uploaded' => 'S’ha pujat el fitxer', + 'attachments_file_updated' => 'S’ha actualitzat el fitxer', + 'attachments_link_attached' => 'S’ha adjuntat l’enllaç a la pàgina', 'templates' => 'Plantilles', 'templates_set_as_template' => 'La pàgina és una plantilla', - 'templates_explain_set_as_template' => 'Podeu definir aquesta pàgina com a plantilla perquè el seu contingut es pugui fer servir en crear altres pàgines. Els altres usuaris podran fer servir la plantilla si tenen permís per a veure aquesta pàgina.', + 'templates_explain_set_as_template' => 'Podeu configurar aquesta pàgina com a plantilla perquè s’utilitzi en la creació d’altres pàgines. Els altres usuaris podran utilitzar aquesta plantilla si tenen permisos de visualització d’aquesta pàgina.', 'templates_replace_content' => 'Substitueix el contingut de la pàgina', - 'templates_append_content' => 'Afegeix al final del contingut de la pàgina', - 'templates_prepend_content' => 'Afegeix al principi del contingut de la pàgina', + 'templates_append_content' => 'Afegeix-ho després del contingut la pàgina', + 'templates_prepend_content' => 'Afegeix-ho abans del contingut la pàgina', // Profile View - 'profile_user_for_x' => 'Usuari fa :time', + 'profile_user_for_x' => 'Usuari des de fa :time', 'profile_created_content' => 'Contingut creat', 'profile_not_created_pages' => ':userName no ha creat cap pàgina', 'profile_not_created_chapters' => ':userName no ha creat cap capítol', @@ -367,71 +397,80 @@ 'comment' => 'Comentari', 'comments' => 'Comentaris', 'comment_add' => 'Afegeix un comentari', - 'comment_placeholder' => 'Deixeu un comentari aquí', - 'comment_count' => '{0} Sense comentaris|{1} 1 comentari|[2,*] :count comentaris', + 'comment_none' => 'No hi ha comentaris per mostrar', + 'comment_placeholder' => 'Deixa un comentari aquí', + 'comment_thread_count' => ':count fil de comentaris|:count fils de comentaris', + 'comment_archived_count' => ':count Arxivats', + 'comment_archived_threads' => 'Fils arxivats', 'comment_save' => 'Desa el comentari', - 'comment_new' => 'Comentari nou', + 'comment_new' => 'Crea un comentari', 'comment_created' => 'ha comentat :createDiff', - 'comment_updated' => 'Actualitzat :updateDiff per :username', + 'comment_updated' => ':username l’ha actualitzat :updateDiff', 'comment_updated_indicator' => 'Actualitzat', - 'comment_deleted_success' => 'Comentari suprimit', - 'comment_created_success' => 'Comentari afegit', - 'comment_updated_success' => 'Comentari actualitzat', - 'comment_delete_confirm' => 'Segur que voleu suprimir aquest comentari?', + 'comment_deleted_success' => 'S’ha suprimit el comentari', + 'comment_created_success' => 'S’ha afegit un comentari', + 'comment_updated_success' => 'S’ha actualitzat un comentari', + 'comment_archive_success' => 'Comentari arxivat', + 'comment_unarchive_success' => 'Comentari desarxivat', + 'comment_view' => 'Mostra el comentari', + 'comment_jump_to_thread' => 'Anar al fil', + 'comment_delete_confirm' => 'Esteu segur que voleu suprimir aquest comentari?', 'comment_in_reply_to' => 'En resposta a :commentId', - 'comment_editor_explain' => 'Aquí hi ha els comentaris que s’han deixat en aquesta pàgina. Els comentaris es poden afegir i gestionar en veure una pàgina desada.', + 'comment_reference' => 'Referència', + 'comment_reference_outdated' => '(Obsolet)', + 'comment_editor_explain' => 'Vet aquí els comentaris que s’han fet en aquesta pàgina. Els comentaris es poden fer i gestionar quan es visualitza la pàgina desada.', // Revision - 'revision_delete_confirm' => 'Segur que voleu suprimir aquesta revisió?', - 'revision_restore_confirm' => 'Segur que voleu restaurar aquesta revisió? Se substituirà el contingut de la pàgina actual.', + 'revision_delete_confirm' => 'Esteu segur que voleu suprimir aquesta revisió?', + 'revision_restore_confirm' => 'Esteu segur que voleu restaurar aquesta revisió? Se substituirà el contingut actual de la pàgina.', 'revision_cannot_delete_latest' => 'No es pot suprimir la darrera revisió.', // Copy view - 'copy_consider' => 'Tingueu en compte el següent quan copieu contingut.', + 'copy_consider' => 'Quan copieu contingut, tingueu en compte el següent:', 'copy_consider_permissions' => 'No es copiarà la configuració personalitzada de permisos.', - 'copy_consider_owner' => 'Esdevindreu el nou propietari de qualsevol contingut copiat.', - 'copy_consider_images' => 'Els fitxers d’imatge de les pàgines no es duplicaran i les imatges originals mantindran la relació amb la pàgina a la qual es van pujar originalment.', - 'copy_consider_attachments' => 'No es copiaran les adjuncions de la pàgina.', - 'copy_consider_access' => 'Un canvi d’ubicació, propietari o permisos pot provocar que aquest contingut esdevingui accessible a públic que abans no n’hi tenia.', + 'copy_consider_owner' => 'Sereu el propietari de tot el contingut copiat.', + 'copy_consider_images' => 'Els fitxers d’imatge de la pàgina no es duplicaran i les imatges originals conservaran la relació amb la pàgina a què es van pujar originalment.', + 'copy_consider_attachments' => 'No es copiaran els fitxers adjunts de la pàgina.', + 'copy_consider_access' => 'És possible que, en canviar la ubicació, el propietari o els permisos, el contingut esdevingui accessible a usuaris que no hi tenien accés.', // Conversions 'convert_to_shelf' => 'Converteix en prestatge', - 'convert_to_shelf_contents_desc' => 'Podeu convertir aquest llibre en un prestatge nou amb el mateix contingut. Els capítols continguts en aquest llibre es convertiran en nous lliures. Si aquest llibre conté pàgines que no pertanyin a cap capítol, aquest llibre canviarà de nom i les contindrà, i aquest llibre esdevindrà part del nou prestatge.', - 'convert_to_shelf_permissions_desc' => 'Qualsevol permís definit en aquest llibre es copiarà al nou prestatge i a tots els nous llibres fills que no tinguin permisos explícits. Tingueu en compte que els permisos dels prestatges no s’hereten automàticament al contingut que continguin de la mateixa manera que passa amb els llibres.', + 'convert_to_shelf_contents_desc' => 'Pots convertir aquest llibre en un prestatge nou amb els mateixos continguts. Els capítols d’aquest llibre es convertiran en llibres. Si aquest llibre conté cap pàgina que no sigui en un capítol, es canviarà el nom del llibre que contindrà aquestes pàgines i el llibre formarà part del prestatge nou.', + 'convert_to_shelf_permissions_desc' => 'Es copiaran els permisos d’aquest llibre al prestatge nou i a tots els llibres que contingui que no tinguin els seus propis permisos. Tingueu en compte que els permisos dels prestatges no es propaguen als seus continguts com sí que ho fan els dels llibres.', 'convert_book' => 'Converteix el llibre', - 'convert_book_confirm' => 'Segur que voleu convertir aquest llibre?', - 'convert_undo_warning' => 'No es podrà desfer de manera fàcil.', + 'convert_book_confirm' => 'Esteu segur que voleu convertir aquest llibre?', + 'convert_undo_warning' => 'Això no es pot desfer fàcilment.', 'convert_to_book' => 'Converteix en llibre', - 'convert_to_book_desc' => 'Podeu convertir aquest capítol en un llibre nou amb el mateix contingut. Qualsevol permís definit en aquest capítol es copiarà al nou llibre, però qualsevol permís heretat del llibre pare no es copiarà, la qual cosa podria implicar canvis en el control d’accés.', + 'convert_to_book_desc' => 'Podeu convertir aquest capítol en un llibre nou amb els mateixos continguts. Es copiaran els permisos d’aquest capítol al llibre nou, però no es copiaran els permisos heretats del llibre a què pertany cosa que podria canviar-ne el control d’accés.', 'convert_chapter' => 'Converteix el capítol', - 'convert_chapter_confirm' => 'Segur que voleu convertir aquest capítol?', + 'convert_chapter_confirm' => 'Esteu segur que voleu convertir aquest capítol?', // References 'references' => 'Referències', - 'references_none' => 'Ni hi ha cap referència detectada a aquest element.', - 'references_to_desc' => 'A continuació es llisten tots els continguts coneguts del sistema que enllacen a aquest element.', + 'references_none' => 'No hi ha cap referència cap a aquest element.', + 'references_to_desc' => 'A la llista que hi ha a continuació, hi trobareu tot el contingut que enllaça cap aquest element.', // Watch Options 'watch' => 'Segueix', - 'watch_title_default' => 'Preferències per defecte', - 'watch_desc_default' => 'Restableix el seguiment a només les preferències de notificació per defecte.', + 'watch_title_default' => 'Preferències predeterminades', + 'watch_desc_default' => 'Reverteix el seguiment a les preferències de notificació per defecte.', 'watch_title_ignore' => 'Ignora', - 'watch_desc_ignore' => 'Ignora totes les notificacions, incloent-hi les preferències a nivell d’usuari.', + 'watch_desc_ignore' => 'Ignora totes les notificacions, incloent-hi les de les preferències de nivell usuari.', 'watch_title_new' => 'Pàgines noves', - 'watch_desc_new' => 'Notifica’m quan es creï qualsevol pàgina nova dins d’aquest element.', - 'watch_title_updates' => 'Totes les actualitzacions de pàgines', - 'watch_desc_updates' => 'Notifica’m totes les pàgines noves i qualsevol canvi de pàgina.', - 'watch_desc_updates_page' => 'Notifica’m tots els canvis de pàgina.', - 'watch_title_comments' => 'Totes les actualitzacions de pàgines i comentaris', - 'watch_desc_comments' => 'Notifica’m totes les pàgines noves, qualsevol canvi de pàgina i comentaris nous.', - 'watch_desc_comments_page' => 'Notifica’m qualsevol canvi de pàgina i comentaris nous.', - 'watch_change_default' => 'Canvieu les preferències de notificació per deecte', - 'watch_detail_ignore' => 'S’ignoren les notificacions', - 'watch_detail_new' => 'Se segueixen pàgines noves', - 'watch_detail_updates' => 'Se segueixen pàgines noves i actualitzacions', - 'watch_detail_comments' => 'Se segueixen pàgines noves, actualitzacions i comentaris', - 'watch_detail_parent_book' => 'Se segueix mitjançant el llibre pare', - 'watch_detail_parent_book_ignore' => 'S’ignora mitjançant el llibre pare', - 'watch_detail_parent_chapter' => 'Se segueix mitjançant el capítol pare', - 'watch_detail_parent_chapter_ignore' => 'S’ignora mitjançant el capítol pare', + 'watch_desc_new' => 'Notifica’m la creació d’una pàgina nova dins d’aquest element.', + 'watch_title_updates' => 'Actualitzacions de la pàgina', + 'watch_desc_updates' => 'Notifica’m totes les pàgines noves i totes les actualitzacions de les pàgines.', + 'watch_desc_updates_page' => 'Notifica’m totes les actualitzacions de les pàgines.', + 'watch_title_comments' => 'Actualitzacions i comentaris de la pàgina', + 'watch_desc_comments' => 'Notifica’m totes les pàgines noves, totes les actualitzacions de les pàgines i tots els comentaris nous.', + 'watch_desc_comments_page' => 'Notifica’m tots les actualitzacions de pàgines i tots els comentaris nous.', + 'watch_change_default' => 'Canvia les preferències de notificació per defecte', + 'watch_detail_ignore' => 'S’estan ignorant les notificacions', + 'watch_detail_new' => 'S’està fent el seguiment de pàgines noves', + 'watch_detail_updates' => 'S’està fent el seguiment de pàgines noves i les actualitzacions de les pàgines', + 'watch_detail_comments' => 'S’està fent el seguiment de pàgines noves i les actualitzacions de les pàgines i els comentaris', + 'watch_detail_parent_book' => 'S’està fent el seguiment a través del llibre de què és part', + 'watch_detail_parent_book_ignore' => 'S’està ignorant a través del llibre de què és part', + 'watch_detail_parent_chapter' => 'S’està fent el seguiment a través del capítol de què és part', + 'watch_detail_parent_chapter_ignore' => 'S’està ignorant a través del capítol de què és part', ]; diff --git a/lang/ca/errors.php b/lang/ca/errors.php index 22ecb2b0eba..6f3784c2cff 100644 --- a/lang/ca/errors.php +++ b/lang/ca/errors.php @@ -9,111 +9,125 @@ 'permissionJson' => 'No teniu permís per a executar l’acció sol·licitada.', // Auth - 'error_user_exists_different_creds' => 'Ja hi ha un usuari amb l’adreça electrònica :email però amb credencials diferents.', + 'error_user_exists_different_creds' => 'Ja existeix un usuari amb el correu electrònic :email però amb unes credencials diferents.', + 'auth_pre_register_theme_prevention' => 'El compte d\'usuari no s\'ha pogut registrar amb els detalls proporcionats', 'email_already_confirmed' => 'L’adreça electrònica ja està confirmada. Proveu d’iniciar la sessió.', 'email_confirmation_invalid' => 'Aquest testimoni de confirmació no és vàlid o ja s’ha utilitzat. Proveu de tornar-vos a registrar.', - 'email_confirmation_expired' => 'El testimoni de confirmació ha caducat. S’ha enviat un nou correu electrònic de confirmació.', + 'email_confirmation_expired' => 'Aquest testimoni de confirmació ha caducat. S’ha enviat un altre correu electrònic de confirmació.', 'email_confirmation_awaiting' => 'Cal confirmar l’adreça electrònica del compte que utilitzeu', - 'ldap_fail_anonymous' => 'L’accés a l’LDAP ha fallat fent servir un lligam anònim', - 'ldap_fail_authed' => 'L’accés a l’LDAP ha fallat fent servir els detalls de DN i contrasenya proporcionats', - 'ldap_extension_not_installed' => 'L’extensió de l’LDAP del PHP no està instal·lada', - 'ldap_cannot_connect' => 'No s’ha pogut connectar amb el servidor de l’LDAP, la connexió inicial ha fallat', - 'saml_already_logged_in' => 'Ja heu iniciat la sessió', - 'saml_no_email_address' => 'No s’ha pogut trobar cap adreça electrònica per a aquest usuari en les dades proporcionades pel sistema d’autenticació extern', - 'saml_invalid_response_id' => 'La petició del sistema d’autenticació extern no és reconeguda per un procés iniciat per aquesta aplicació. Aquest problema podria ser causat per navegar endarrere després d’iniciar la sessió.', - 'saml_fail_authed' => 'L’inici de sessió fent servir :system ha fallat, el sistema no ha proporcionat una autorització satisfactòria', - 'oidc_already_logged_in' => 'Ja teniu una sessió iniciada', - 'oidc_user_not_registered' => 'L’usuari :name no està registrat i els registres automàtics estan desactivats', - 'oidc_no_email_address' => 'No s’ha pogut trobar cap adreça electrònica per a aquest usuari en les dades proporcionades pel sistema d’autenticació extern', - 'oidc_fail_authed' => 'L’inici de sessió fent servir :system ha fallat, el sistema no ha proporcionat una autorització satisfactòria', - 'social_no_action_defined' => 'No hi ha cap acció definida', - 'social_login_bad_response' => "S’ha rebut un error mentre s’iniciava la sessió amb :socialAccount: \n:error", - 'social_account_in_use' => 'Aquest compte de :socialAccount ja està en ús, proveu d’iniciar la sessió mitjançant l’opció de :socialAccount.', + 'ldap_fail_anonymous' => 'L’accés LDAP anònim ha fallat', + 'ldap_fail_authed' => 'L’accés LDAP amb el nom distintiu i la contrasenya proporcionats ha fallat', + 'ldap_extension_not_installed' => 'L’extensió PHP de l’LDAP no està instal·lada', + 'ldap_cannot_connect' => 'No s’ha pogut connectar amb el servidor LDAP perquè la connexió inicial ha fallat', + 'saml_already_logged_in' => 'Ja heu iniciat sessió', + 'saml_no_email_address' => 'No s’ha pogut trobar una adreça electrònica per a aquest usuari a les dades proporcionades pel sistema d’autenticació extern', + 'saml_invalid_response_id' => 'Un procés iniciat per aquesta aplicació no reconeix la sol·licitud del sistema d’autenticació extern. Haver navegat enrere després d’iniciar sessió en podria ser la causa.', + 'saml_fail_authed' => 'No s’ha pogut iniciar sessió amb :system perquè el sistema no ha proporcionat una autorització satisfactòria', + 'oidc_already_logged_in' => 'Ja heu iniciat sessió', + 'oidc_no_email_address' => 'No s’ha pogut trobar una adreça electrònica per a aquest usuari a les dades proporcionades pel sistema d’autenticació extern', + 'oidc_fail_authed' => 'No s’ha pogut iniciar sessió amb :system perquè el sistema no ha proporcionat una autorització satisfactòria', + 'social_no_action_defined' => 'No s’ha definit cap acció', + 'social_login_bad_response' => "S’ha produït un error en l’inici de sessió amb :socialAccount: \n:error", + 'social_account_in_use' => 'Aquest compte de :socialAccount ja s’està utilitzant. Proveu d’iniciar sessió amb :socialAccount.', 'social_account_email_in_use' => 'L’adreça electrònica :email ja està en ús. Si ja teniu un compte, podeu connectar-hi el vostre compte de :socialAccount a la configuració del vostre perfil.', 'social_account_existing' => 'Aquest compte de :socialAccount ja està associat al vostre perfil.', - 'social_account_already_used_existing' => 'Aquest compte de :socialAccount ja el fa servir un altre usuari.', + 'social_account_already_used_existing' => 'Aquest compte de :socialAccount ja està associat a un altre usuari.', 'social_account_not_used' => 'Aquest compte de :socialAccount no està associat a cap usuari. Associeu-lo a la configuració del vostre perfil. ', - 'social_account_register_instructions' => 'Si encara no teniu cap compte, podeu registrar-vos fent servir l’opció de :socialAccount.', + 'social_account_register_instructions' => 'Si encara no teniu un compte, podeu registrar-vos amb l’opció :socialAccount.', 'social_driver_not_found' => 'No s’ha trobat el controlador social', - 'social_driver_not_configured' => 'La configuració social de :socialAccount no és correcta.', + 'social_driver_not_configured' => 'La configuració de :socialAccount no és correcta.', 'invite_token_expired' => 'Aquest enllaç d’invitació ha caducat. Podeu provar de restablir la contrasenya del vostre compte.', + 'login_user_not_found' => 'No s\'ha pogut trobar un usuari per aquesta acció.', // System - 'path_not_writable' => 'No s’ha pogut pujar al camí del fitxer :filePath. Assegureu-vos que el servidor hi té permisos d’escriptura.', - 'cannot_get_image_from_url' => 'No s’ha pogut obtenir la imatge de :url', - 'cannot_create_thumbs' => 'El servidor no pot crear miniatures. Reviseu que tingueu instal·lada l’extensió GD del PHP.', - 'server_upload_limit' => 'El servidor no permet pujades d’aquesta mida. Proveu-ho amb una mida de fitxer més petita.', - 'server_post_limit' => 'El servidor no pot rebre la quantitat de dades que heu proporcionat. Torneu-ho a provar amb menys dades o un fitxer més petit.', - 'uploaded' => 'El servidor no permet pujades d’aquesta mida. Proveu-ho amb una mida de fitxer més petita.', + 'path_not_writable' => 'No s’ha pogut pujar a :filePath. Assegureu-vos que teniu permisos d’escriptura al servidor.', + 'cannot_get_image_from_url' => 'No s’ha pogut obtenir la imatge des de :url', + 'cannot_create_thumbs' => 'El servidor no pot crear miniatures. Assegureu-vos que s’ha instal·lat l’extensió de GD PHP.', + 'server_upload_limit' => 'El servidor no permet pujades d’aquesta mida. Proveu-ho amb una mida més petita.', + 'server_post_limit' => 'El servidor no pot rebre la quantitat de dades proporcionades. Proveu-ho amb menys dades o amb un fitxer més petit.', + 'uploaded' => 'El servidor no permet pujades d’aquesta mida. Proveu-ho amb un fitxer més petit.', // Drawing & Images - 'image_upload_error' => 'S’ha produït un error en pujar la imatge', - 'image_upload_type_error' => 'El tipus d’imatge que heu pujat no és vàlid', - 'image_upload_replace_type' => 'Les substitucions de fitxers d’imatge han de ser el mateix tipus', - 'image_upload_memory_limit' => 'No s’ha pogut gestionar la pujada de la imatge i/o crear-ne miniatures a causa dels límits dels recursos del sistema.', - 'image_thumbnail_memory_limit' => 'No s’ha pogut crear les variacions de mida de la imatge a causa dels límits dels recursos del sistema.', + 'image_upload_error' => 'S’ha produït un error en pujar la imatge.', + 'image_upload_type_error' => 'El tipus d’imatge no és vàlid.', + 'image_upload_replace_type' => 'Els fitxers d’imatge s’han de substituir per un del mateix tipus', + 'image_upload_memory_limit' => 'No s’ha pogut pujar la imatge o crear-ne les miniatures a causa dels límits dels recursos del sistema.', + 'image_thumbnail_memory_limit' => 'No s’han pogut crear mides d’imatge diferents a causa dels límits dels recursos del sistema.', 'image_gallery_thumbnail_memory_limit' => 'No s’han pogut crear les miniatures de la galeria a causa dels límits dels recursos del sistema.', - 'drawing_data_not_found' => 'No s’han pogut carregar les dades de dibuix. És possible que el fitxer de dibuix ja no existeixi o que no tingueu permisos per a accedir-hi.', + 'drawing_data_not_found' => 'No s’han pogut pujar les dades del dibuix. És possible que el fitxer del dibuix ja no existeixi o que no tingueu permís per a accedir-hi.', // Attachments - 'attachment_not_found' => 'No s’ha trobat l’adjunció', - 'attachment_upload_error' => 'S’ha produït un error en pujar el fitxer de l’adjunció', + 'attachment_not_found' => 'No s’ha trobat el fitxer adjunt', + 'attachment_upload_error' => 'S’ha produït un error en pujar el fitxer adjunt', // Pages - 'page_draft_autosave_fail' => 'No s’ha pogut desar l’esborrany. Assegureu-vos que teniu connexió a Internet abans de desar la pàgina', - 'page_draft_delete_fail' => 'No s’ha pogut suprimir l’esborrany de la pàgina i obtenir el contingut desat actual de la pàgina', - 'page_custom_home_deletion' => 'No es pot suprimir una pàgina mentre estigui definida com a pàgina d’inici', + 'page_draft_autosave_fail' => 'No s’ha pogut desar l’esborrany. Assegureu-vos que esteu connectat a internet per a desar la pàgina.', + 'page_draft_delete_fail' => 'No s’ha pogut suprimir l’esborrany de la pàgina i obtenir el contingut de la pàgina desada actual.', + 'page_custom_home_deletion' => 'No es pot suprimir una pàgina que està definida com a pàgina d’inici.', // Entities - 'entity_not_found' => 'No s’ha trobat l’entitat', - 'bookshelf_not_found' => 'No s’ha trobat el prestatge', - 'book_not_found' => 'No s’ha trobat el llibre', - 'page_not_found' => 'No s’ha trobat la pàgina', - 'chapter_not_found' => 'No s’ha trobat el capítol', - 'selected_book_not_found' => 'No s’ha trobat el llibre seleccionat', - 'selected_book_chapter_not_found' => 'No s’ha trobat el llibre o el capítol seleccionat', - 'guests_cannot_save_drafts' => 'Els convidats no poden desar esborranys', + 'entity_not_found' => 'No s’ha trobat l’entitat.', + 'bookshelf_not_found' => 'No s’ha trobat el prestatge.', + 'book_not_found' => 'No s’ha trobat el llibre.', + 'page_not_found' => 'No s’ha trobat la pàgina.', + 'chapter_not_found' => 'No s’ha trobat el capítol.', + 'selected_book_not_found' => 'No s’ha trobat el llibre seleccionat.', + 'selected_book_chapter_not_found' => 'No s’ha trobat el llibre o el capítol seleccionat.', + 'guests_cannot_save_drafts' => 'Els convidats no poden desar esborranys.', // Users - 'users_cannot_delete_only_admin' => 'No podeu suprimir l’únic administrador', - 'users_cannot_delete_guest' => 'No podeu suprimir l’usuari convidat', + 'users_cannot_delete_only_admin' => 'No podeu suprimir l’administrador únic.', + 'users_cannot_delete_guest' => 'No podeu suprimir l’usuari convidat.', + 'users_could_not_send_invite' => 'No s\'ha pogut crear l\'usuari, ja que no s\'ha pogut enviar el correu d\'invitació', // Roles - 'role_cannot_be_edited' => 'Aquest rol no es pot editar', - 'role_system_cannot_be_deleted' => 'Aquest rol és un rol del sistema i no es pot suprimir', - 'role_registration_default_cannot_delete' => 'No es pot suprimir aquest rol mentre estigui definit com a rol per defecte dels registres', - 'role_cannot_remove_only_admin' => 'Aquest usuari és l’únic usuari assignat al rol d’administrador. Assigneu el rol d’administrador a un altre usuari abans de provar de suprimir aquest.', + 'role_cannot_be_edited' => 'No es pot editar aquest rol.', + 'role_system_cannot_be_deleted' => 'No es pot suprimir aquest rol perquè és un rol del sistema.', + 'role_registration_default_cannot_delete' => 'No es pot suprimir aquest rol mentre estigui configurat com a rol de registre per defecte.', + 'role_cannot_remove_only_admin' => 'Aquest és l’únic usuari assignat al rol d’administrador. Assigneu el rol d’administrador a un altre usuari abans de provar de suprimir-lo.', // Comments 'comment_list' => 'S’ha produït un error en obtenir els comentaris.', - 'cannot_add_comment_to_draft' => 'No podeu afegir comentaris a un esborrany.', + 'cannot_add_comment_to_draft' => 'No es poden afegir comentaris en un esborrany.', 'comment_add' => 'S’ha produït un error en afegir o actualitzar el comentari.', 'comment_delete' => 'S’ha produït un error en suprimir el comentari.', - 'empty_comment' => 'No podeu afegir un comentari buit.', + 'empty_comment' => 'No es pot afegir un comentari buit.', // Error pages '404_page_not_found' => 'No s’ha trobat la pàgina', - 'sorry_page_not_found' => 'No hem pogut trobar la pàgina que cerqueu.', - 'sorry_page_not_found_permission_warning' => 'Si esperàveu que existís, és possible que no tingueu permisos per a veure-la.', + 'sorry_page_not_found' => 'No s’ha trobat la pàgina que heu cercat.', + 'sorry_page_not_found_permission_warning' => 'Si la pàgina existeix, és possible que no tingueu permís per a accedir-hi.', 'image_not_found' => 'No s’ha trobat la imatge', - 'image_not_found_subtitle' => 'No ha estat possible trobar el fitxer de la imatge que cerqueu.', - 'image_not_found_details' => 'Si esperàveu que existís, és possible que s’hagi suprimit.', - 'return_home' => 'Torna a l’inici', - 'error_occurred' => 'S’ha produït un error', - 'app_down' => ':appName està fora de servei en aquests moments', - 'back_soon' => 'Tornarà a estar disponible aviat.', + 'image_not_found_subtitle' => 'No s’ha trobat la imatge que heu cercat.', + 'image_not_found_details' => 'És possible que s’hagi suprimit.', + 'return_home' => 'Torna a la pàgina d’inici', + 'error_occurred' => 'S’ha produït un error.', + 'app_down' => ':appName està fora de servei.', + 'back_soon' => 'Aviat ho arreglarem.', + + // Import + 'import_zip_cant_read' => 'No es pot llegir el fitxer ZIP.', + 'import_zip_cant_decode_data' => 'No s\'ha pogut trobar i descodificar el fitxer data.json en el fitxer ZIP.', + 'import_zip_no_data' => 'Les dades del fitxer ZIP no contenen cap llibre, capítol o contingut de pàgina.', + 'import_validation_failed' => 'Error en validar la importació del ZIP amb els errors:', + 'import_zip_failed_notification' => 'Error en importar l\'arxiu ZIP.', + 'import_perms_books' => 'Li falten els permisos necessaris per crear llibres.', + 'import_perms_chapters' => 'Li falten els permisos necessaris per crear capítols.', + 'import_perms_pages' => 'Li falten els permisos necessaris per crear pàgines.', + 'import_perms_images' => 'Li falten els permisos necessaris per crear imatges.', + 'import_perms_attachments' => 'Li falten els permisos necessaris per crear adjunts.', // API errors - 'api_no_authorization_found' => 'No s’ha trobat cap testimoni d’autorització a la petició', - 'api_bad_authorization_format' => 'S’ha trobat un testimoni d’autorització a la petició, però el format sembla erroni', - 'api_user_token_not_found' => 'No s’ha trobat cap testimoni de l’API per al testimoni d’autorització proporcionat', - 'api_incorrect_token_secret' => 'El secret proporcionat per al testimoni de l’API proporcionat és incorrecte', - 'api_user_no_api_permission' => 'El propietari del testimoni de l’API utilitzat no té permís per a fer crides a l’API', - 'api_user_token_expired' => 'El testimoni d’autorització utilitzat ha caducat', + 'api_no_authorization_found' => 'No s’ha trobat cap testimoni d’autorització en aquesta sol·licitud.', + 'api_bad_authorization_format' => 'S’ha trobat un testimoni d’autorització en aquesta sol·licitud però no tenia el format correcte.', + 'api_user_token_not_found' => 'No s’ha trobat cap testimoni d’API per al testimoni d’autorització proporcionat.', + 'api_incorrect_token_secret' => 'El secret proporcionat per al testimoni d’API utilitzat no és correcte.', + 'api_user_no_api_permission' => 'El propietari del testimoni API utilitzat no té permís per a fer crides a l’API.', + 'api_user_token_expired' => 'El testimoni d’autorització utilitzat ha caducat.', // Settings & Maintenance - 'maintenance_test_email_failure' => 'S’ha produït un error en enviar un correu electrònic de prova:', + 'maintenance_test_email_failure' => 'S’ha produït un error en enviar el correu electrònic de prova:', // HTTP errors - 'http_ssr_url_no_match' => 'L’URL no coincideix amb els amfitrions SSR permesos segons la configuració', + 'http_ssr_url_no_match' => 'L’URL no coincideix amb els amfitrions SSR configurats permesos.', ]; diff --git a/lang/ca/notifications.php b/lang/ca/notifications.php index 8f86b85ad52..49b986ad063 100644 --- a/lang/ca/notifications.php +++ b/lang/ca/notifications.php @@ -4,24 +4,24 @@ */ return [ - 'new_comment_subject' => 'Comentari nou a la pàgina :pageName', - 'new_comment_intro' => 'Un usuari ha comentat en una pàgina de :appName:', - 'new_page_subject' => 'Pàgina nova: :pageName', - 'new_page_intro' => 'S’ha creat una pàgina nova a :appName:', - 'updated_page_subject' => 'Pàgina actualitzada: :pageName', - 'updated_page_intro' => 'S’ha actualitzat una pàgina a :appName:', - 'updated_page_debounce' => 'Per a evitar les notificacions massives, no us enviarem notificacions si hi ha més edicions en aquesta pàgina fetes pel mateix editor.', + 'new_comment_subject' => 'S’ha fet un comentari a la pàgina :pageName.', + 'new_comment_intro' => 'S’ha fet un comentari nou en una pàgina a :appName.', + 'new_page_subject' => 'S’ha creat la pàgina :pageName', + 'new_page_intro' => 'S’ha creat una pàgina nova a :appName.', + 'updated_page_subject' => 'S’ha actualitzat la pàgina :pageName', + 'updated_page_intro' => 'S’ha actualitzat una pàgina a :appName.', + 'updated_page_debounce' => 'Per a evitar que s’acumulin les notificacions, durant un temps no se us notificarà cap canvi fet en aquesta pàgina pel mateix usuari.', 'detail_page_name' => 'Nom de la pàgina:', - 'detail_page_path' => 'Camí de la pàgina:', + 'detail_page_path' => 'Ruta de la pàgina:', 'detail_commenter' => 'Autor del comentari:', 'detail_comment' => 'Comentari:', - 'detail_created_by' => 'Creat per:', - 'detail_updated_by' => 'Actualitzat per:', + 'detail_created_by' => 'Creada per:', + 'detail_updated_by' => 'Actualitzada per:', 'action_view_comment' => 'Mostra el comentari', 'action_view_page' => 'Mostra la pàgina', - 'footer_reason' => 'Rebeu aquesta notificació perquè :link cobreixen aquest tipus d’activitat en aquest element.', - 'footer_reason_link' => 'les vostres preferències de notificacions', + 'footer_reason' => 'Heu rebut aquesta notificació perquè :link inclouen aquest tipus d’activitat per a aquest element.', + 'footer_reason_link' => 'les vostres preferències de notificació', ]; diff --git a/lang/ca/passwords.php b/lang/ca/passwords.php index 60789c8a248..aa727ad4b61 100644 --- a/lang/ca/passwords.php +++ b/lang/ca/passwords.php @@ -6,10 +6,10 @@ */ return [ - 'password' => 'Les contrasenyes han de tenir almenys vuit caràcters i coincidir amb la confirmació.', - 'user' => "No s’ha trobat cap usuari amb aquesta adreça electrònica.", + 'password' => 'Les contrasenyes han de tenir almenys 8 caràcters i han de coincidir amb la confirmació de contrasenya.', + 'user' => "No es pot trobar un usuari amb aquesta adreça electrònica.", 'token' => 'El testimoni de restabliment de la contrasenya no és vàlid per a aquesta adreça electrònica.', 'sent' => 'Us hem enviat un enllaç per a restablir la contrasenya!', - 'reset' => 'S’ha restablert la contrasenya!', + 'reset' => 'S’ha reinicialitzat la contrasenya.', ]; diff --git a/lang/ca/preferences.php b/lang/ca/preferences.php index b56b8b3bb09..25206726ecd 100644 --- a/lang/ca/preferences.php +++ b/lang/ca/preferences.php @@ -7,45 +7,45 @@ return [ 'my_account' => 'El meu compte', - 'shortcuts' => 'Dreceres', - 'shortcuts_interface' => 'Preferències de les dreceres de la interfície d’usuari', - 'shortcuts_toggle_desc' => 'Aquí podeu activar o desactivar les dreceres de teclat de la interfície del sistema, que s’utilitzen per a la navegació i les accions.', - 'shortcuts_customize_desc' => 'Podeu personalitzar cadascuna de les dreceres següents. Premeu la combinació de tecles desitjada després de seleccionar la casella d’una drecera.', - 'shortcuts_toggle_label' => 'Dreceres de teclat activades', + 'shortcuts' => 'Dreceres de teclat', + 'shortcuts_interface' => 'Preferències de dreceres de teclat de la interfície d’usuari', + 'shortcuts_toggle_desc' => 'Configureu les dreceres de teclat de la interfície d’usuari del sistema utilitzades per a la navegació i les accions.', + 'shortcuts_customize_desc' => 'Podeu personalitzar cadascuna de les dreceres a continuació. seleccionant el camp d’entrada de cada drecera i prement la combinació de tecles que vulgueu.', + 'shortcuts_toggle_label' => 'Activa les dreceres de teclat', 'shortcuts_section_navigation' => 'Navegació', 'shortcuts_section_actions' => 'Accions habituals', 'shortcuts_save' => 'Desa les dreceres', - 'shortcuts_overlay_desc' => 'Nota: Quan s’activen les dreceres, hi ha una interfície d’ajuda disponible en prémer «?» que destaca les dreceres disponibles per a accions visibles actualment a la pantalla.', - 'shortcuts_update_success' => 'S’han actualitzat les preferències de les dreceres!', - 'shortcuts_overview_desc' => 'Gestioneu les dreceres de teclat que podeu utilitzar per a navegar per la interfície d’usuari del sistema.', + 'shortcuts_overlay_desc' => 'Nota: Quan les dreces estan activades hi haurà disponible una ajuda que es pot obtenir prement «?» que ressaltarà les dreceres disponibles a la pantalla que s’estigui visualitzant.', + 'shortcuts_update_success' => 'S’han actualitzat les preferències de drecera.', + 'shortcuts_overview_desc' => 'Gestioneu les dreceres que s’utilitzen per a navegar per la interfície d’usuari.', - 'notifications' => 'Preferències de notificació', - 'notifications_desc' => 'Controleu les notificacions per correu electrònic que rebeu quan es fan certes activitats dins del sistema.', - 'notifications_opt_own_page_changes' => 'Notifica’m quan hi hagi canvis a pàgines de les quals sóc propietari', - 'notifications_opt_own_page_comments' => 'Notifica’m quan hi hagi comentaris a pàgines de les quals sóc propietari', - 'notifications_opt_comment_replies' => 'Notifica’m quan hi hagi respostes als meus comentaris', + 'notifications' => 'Preferències de les notificacions', + 'notifications_desc' => 'Gestioneu les notificacions de correu electrònic que rebreu quan es facin certes activitats.', + 'notifications_opt_own_page_changes' => 'Notifica’m els canvis a les meves pàgines.', + 'notifications_opt_own_page_comments' => 'Notifica’m la creació de comentaris a les meves pàgines.', + 'notifications_opt_comment_replies' => 'Notifica’m les respostes als meus comentaris.', 'notifications_save' => 'Desa les preferències', - 'notifications_update_success' => 'S’han actualitzat les preferències de notificacions!', + 'notifications_update_success' => 'S’han actualitzat les preferències de notificació', 'notifications_watched' => 'Elements seguits i ignorats', - 'notifications_watched_desc' => ' A continuació hi ha els elements que tenen aplicades preferències de seguiment personalitzades. Per a actualitzar-ne les preferències, consulteu l’element i seleccioneu les opcions de seguiment a la barra lateral.', + 'notifications_watched_desc' => 'A continuació hi ha els elements que tenen preferències de seguiment personalitzades. Per a actualitzar-les, accediu a l’element i configureu-les a la barra lateral.', 'auth' => 'Accés i seguretat', 'auth_change_password' => 'Canvia la contrasenya', - 'auth_change_password_desc' => 'Canvieu la contrasenya que feu servir per a iniciar la sessió a l’aplicació. Cal que tingui un mínim de 8 caràcters.', - 'auth_change_password_success' => 'S’ha actualitzat la contrasenya!', + 'auth_change_password_desc' => 'Canvieu la contrasenya que s’utilitzarà per a iniciar sessió a l’aplicació. Ha de tenir com a mínim 8 caràcters.', + 'auth_change_password_success' => 'S’ha actualitzat la contrasenya.', - 'profile' => 'Detalls del perfil', - 'profile_desc' => 'Gestioneu els detalls del vostre compte, que us representa davant d’altres usuaris, i també els detalls que s’utilitzen per a la comunicació i la personalització del sistema.', - 'profile_view_public' => 'Mostra el perfil públic', - 'profile_name_desc' => 'Configureu el vostre nom públic, que és visible als altres usuaris del sistema a través de les activitats que realitzeu i del contingut del qual sou propietari.', - 'profile_email_desc' => 'Aquest correu s’utilitza per a notificacions i, depenent de l’autenticació activa del sistema, per a l’accés al sistema.', - 'profile_email_no_permission' => 'Malauradament, no teniu permisos per a canviar l’adreça electrònica. Si voleu canviar-la, caldrà que demaneu a un administrador que us faci el canvi.', - 'profile_avatar_desc' => 'Seleccioneu una imatge que us representarà davant d’altres usuaris del sistema. Idealment, aquesta imatge hauria de ser un quadrat de 256 px d’alçada i d’amplada.', - 'profile_admin_options' => 'Opcions d’administració', - 'profile_admin_options_desc' => 'Podeu trobar opcions de nivell d’administració addicionals del vostre compte, com ara les que permeten gestionar les assignacions de rols, a l’àrea de l’aplicació «Configuració > Usuaris».', + 'profile' => 'Informació del perfil', + 'profile_desc' => 'Gestioneu la informació del vostre compte que representa com us veuran els altres usuaris a més de la informació que s’utilitza per a la comunicació i la personalització del sistema.', + 'profile_view_public' => 'Mostra’m el perfil públic', + 'profile_name_desc' => 'Configureu el nom públic que veuran els altres usuaris a les activitats que feu i al vostre contingut.', + 'profile_email_desc' => 'Aquesta serà l’adreça electrònica on s’enviaran les notificacions i que, segons l’autenticació del sistema activada, s’utilitzarà per a iniciar sessió.', + 'profile_email_no_permission' => 'No teniu permís per a canviar la vostra adreça electrònica. Si voleu canviar-la, demaneu-ho a un administrador.', + 'profile_avatar_desc' => 'Seleccioneu una imatge que us representi davant dels altres usuaris. És millor que sigui una imatge quadrada de 256px de costat.', + 'profile_admin_options' => 'Preferències d’administrador', + 'profile_admin_options_desc' => 'Podeu trobar preferències addicionals d’administrador pel vostre compte, com ara les que gestionen els rols d’usuari, a «Preferències › Usuaris».', 'delete_account' => 'Suprimeix el compte', 'delete_my_account' => 'Suprimeix el meu compte', - 'delete_my_account_desc' => 'Se suprimirà completament el vostre compte d’usuari del sistema. No podreu recuperar aquest compte ni revertir aquesta acció. El contingut que hàgiu creat, com ara les pàgines creades o les imatges pujades, es mantindrà.', - 'delete_my_account_warning' => 'Segur que voleu suprimir el vostre compte?', + 'delete_my_account_desc' => 'Se suprimirà completament del sistema el vostre compte d’usuari. No podreu recuperar el compte ni revertir-ne la supressió. Es conservarà el contingut que hàgiu creat, com ara les pàgines o les imatges que hàgiu pujat.', + 'delete_my_account_warning' => 'Esteu segur que voleu suprimir el vostre compte?', ]; diff --git a/lang/ca/settings.php b/lang/ca/settings.php index 0305a5c232d..63352147e0b 100644 --- a/lang/ca/settings.php +++ b/lang/ca/settings.php @@ -8,119 +8,149 @@ // Common Messages 'settings' => 'Configuració', - 'settings_save' => 'Desa la configuració', - 'system_version' => 'Versió del sistema', + 'settings_save' => 'Guardar configuració', + 'system_version' => 'Versió de sistema', 'categories' => 'Categories', // App Settings 'app_customization' => 'Personalització', - 'app_features_security' => 'Funcionalitats i seguretat', + 'app_features_security' => 'Funcions i seguretat', 'app_name' => 'Nom de l’aplicació', - 'app_name_desc' => 'Aquest nom es mostra a la capçalera i en tots els correus electrònics enviats pel sistema.', + 'app_name_desc' => 'El nom es mostra a la capçalera i als correus electrònics enviats pel sistema.', 'app_name_header' => 'Mostra el nom a la capçalera', 'app_public_access' => 'Accés públic', - 'app_public_access_desc' => 'Si activeu aquesta opció, es permetrà que els visitants que no hagin iniciat la sessió accedeixin al contingut de la vostra instància del BookStack.', - 'app_public_access_desc_guest' => 'Podeu controlar l’accés dels visitants públics amb l’usuari «Convidat».', + 'app_public_access_desc' => 'Si activeu aquesta opció permetrà als visitants, accedir a la vostra instància del BookStack sense iniciar sessió.', + 'app_public_access_desc_guest' => 'L’accés per als visitants públics es pot gestionar amb l’usuari Convidat.', 'app_public_access_toggle' => 'Permet l’accés públic', - 'app_public_viewing' => 'Voleu permetre la visualització pública?', - 'app_secure_images' => 'Pujades d’imatges amb més seguretat', - 'app_secure_images_toggle' => 'Activa les pujades d’imatges amb més seguretat', - 'app_secure_images_desc' => 'Per motius de rendiment, totes les imatges són públiques. Aquesta opció afegeix una cadena aleatòria i difícil d’endevinar al davant dels URL d’imatges. Assegureu-vos que els índexs de directoris no estiguin activats per a evitar-hi l’accés de manera fàcil.', + 'app_public_viewing' => 'Esteu segur que voleu permetre l’accés públic?', + 'app_secure_images' => 'Pujada d’imatges amb més seguretat', + 'app_secure_images_toggle' => 'Habilita la pujada d’imatges amb més seguretat', + 'app_secure_images_desc' => 'Per raons de rendiment, totes les imatges són públiques. Aquesta opció afegeix una cadena aleatòria que és difícil d’endevinar al davant de l’URL de les imatges. Assegureu-vos que els índex de carpetes estan desactivats perquè no s’hi pugui accedir fàcilment.', 'app_default_editor' => 'Editor de pàgines per defecte', - 'app_default_editor_desc' => 'Seleccioneu quin editor es farà servir per defecte en editar pàgines noves. Es pot canviar a nivell de pàgina si els permisos ho permeten.', + 'app_default_editor_desc' => 'Seleccioneu quin editor s’utilitzarà per defecte quan s’editin pàgines noves. Això es pot sobreescriure a nivell de pàgina si els permisos ho permeten.', 'app_custom_html' => 'Contingut personalitzat a la capçalera HTML', - 'app_custom_html_desc' => 'Aquí podeu afegir contingut que s’inserirà a la part final de la secció de cada pàgina. És útil per a sobreescriure estils o afegir-hi codi d’analítiques.', - 'app_custom_html_disabled_notice' => 'El contingut personalitzat a la capçalera HTML es desactiva en aquesta pàgina de la configuració per a assegurar que qualsevol canvi que trenqui el web es pugui desfer.', - 'app_logo' => 'Logo de l’aplicació', - 'app_logo_desc' => 'Es fa servir a la barra de la capçalera de l’aplicació, a banda d’altres zones. Aquesta imatge ha de fer 86 px d’alçada. Les imatges massa grosses es reduiran.', + 'app_custom_html_desc' => 'El contingut que s’afegeixi aquí s’inserirà al final de la secció de cada pàgina. Això permet sobreescriure els estils o afegir codi d’analítiques web.', + 'app_custom_html_disabled_notice' => 'El contingut personalitzat a la capçalera HTML està desactivat en aquesta pàgina perquè es puguin revertir els canvis que trenquin el lloc web.', + 'app_logo' => 'Logotip de l’aplicació', + 'app_logo_desc' => 'El logotip s’utilitzarà a la barra de la capçalera de l’aplicació, entre d’altres llocs. La imatge ha de tenir 86px d’alçada. Les imatges més grans es reduiran.', 'app_icon' => 'Icona de l’aplicació', - 'app_icon_desc' => 'Aquesta icona es fa servir a la pestanya del navegador i a les icones de les dreceres. Hauria de ser una imatge PNG quadrada de 256 px.', + 'app_icon_desc' => 'La icona s’utilitzarà a les pestanyes del navegador i a les icones de les adreces. La imatge ha de ser un PNG quadrat de 256px de costat.', 'app_homepage' => 'Pàgina d’inici de l’aplicació', - 'app_homepage_desc' => 'Seleccioneu la visualització que es mostrarà a la pàgina d’inici en lloc de la visualització per defecte. Els permisos de pàgines s’ignoraran per a les pàgines seleccionades.', - 'app_homepage_select' => 'Selecciona una pàgina', - 'app_footer_links' => 'Enllaços al peu de pàgina', - 'app_footer_links_desc' => 'Afegiu enllaços que es mostraran al peu de pàgina del lloc. Es mostraran a la part inferior de la majoria de pàgines, incloent-hi les que no requereixen iniciar la sessió. Podeu utilitzar l’etiqueta «trans::» per a fer servir traduccions definides pel sistema. Per exemple, si feu servir «trans::common.privacy_policy», es mostrarà el text traduït «Política de privadesa», i amb «trans::common.terms_of_service» es mostrarà el text traduït «Condicions del servei».', - 'app_footer_links_label' => 'Etiqueta de l’enllaç', + 'app_homepage_desc' => 'Seleccioneu una vista per a mostrar a la pàgina d’inici en comptes de la vista per defecte. Els permisos de pàgina s’ignoren a les pàgines seleccionades.', + 'app_homepage_select' => 'Seleccioneu una pàgina', + 'app_footer_links' => 'Enllaços del peu de pàgina', + 'app_footer_links_desc' => 'Afegiu enllaços per a mostrar al peu de pàgina. Els enllaços es mostraran al final de la majoria de les pàgines, incloent-hi les pàgines per a les que no es requereix iniciar sessió. Podeu utilitzar una etiqueta «trans::» per a utilitzar traduccions definides pel sistema. Per exemple: Si utilitzeu «trans::common.privacy_policy» es mostrarà la traducció de «Privacy Policy» i si utilitzeu «trans::common.terms_of_service» es mostrarà la traducció de «Terms of Service».', + 'app_footer_links_label' => 'Text de l’enllaç', 'app_footer_links_url' => 'URL de l’enllaç', - 'app_footer_links_add' => 'Afegeix un enllaç al peu de pàgina', + 'app_footer_links_add' => 'Afegeix l’enllaç en el peu de pàgina', 'app_disable_comments' => 'Desactiva els comentaris', 'app_disable_comments_toggle' => 'Desactiva els comentaris', - 'app_disable_comments_desc' => 'Desactiva els comentaris a totes les pàgines de l’aplicació.
Els comentaris existents no es mostraran.', + 'app_disable_comments_desc' => 'Desactivarà els comentaris a totes les pàgines de l’aplicació.
Els comentaris existents no es mostraran.', // Color settings 'color_scheme' => 'Esquema de colors de l’aplicació', - 'color_scheme_desc' => 'Definiu els colors que s’utilitzaran a la interfície d’usuari de l’aplicació. Els colors es poden configurar de manera separada per als modes fosc i clar perquè encaixin millor amb el tema i n’assegurin la llegibilitat.', - 'ui_colors_desc' => 'Definiu el color primari de l’aplicació i el color per defecte dels enllaços. El color primari es fa servir sobretot per a la capçalera, els botons i la decoració de la interfície. El color per defecte dels enllaços s’utilitza per als enllaços de text i les accions, tant al contingut escrit com a la interfície de l’aplicació.', - 'app_color' => 'Color primari', + 'color_scheme_desc' => 'Configureu els colors que s’utilitzaran a la interfície d’usuari de l’aplicació. Podeu configurar els colors pel mode clar o el mode fosc per separat perquè escaigui millor al tema i assegurar la llegibilitat.', + 'ui_colors_desc' => 'Configureu el color principal de l’aplicació i el color per defecte dels enllaços. El color principal s’utilitza al bàner de la capçalera, els botons i les decoracions de la interfície. El color per defecte dels enllaços s’utilitza a les accions i els enllaços de text, tant al contingut escrit com a la interfície de l’aplicació.', + 'app_color' => 'Color principal', 'link_color' => 'Color per defecte dels enllaços', - 'content_colors_desc' => 'Definiu els colors per a tots els elements de la jerarquia d’organització de pàgines. És recomanable triar colors amb una brillantor similar a la dels colors per defecte per a millorar la llegibilitat.', + 'content_colors_desc' => 'Configureu els colors per a tots els elements en la jerarquia de l’organització de la pàgina. És recomanable que trieu uns colors amb una brillantor similar a la dels colors per defecte per assegurar la llegibilitat.', 'bookshelf_color' => 'Color dels prestatges', 'book_color' => 'Color dels llibres', 'chapter_color' => 'Color dels capítols', 'page_color' => 'Color de les pàgines', - 'page_draft_color' => 'Color dels esborranys de pàgines', + 'page_draft_color' => 'Color de les pàgines d’esborrany', // Registration Settings 'reg_settings' => 'Registre', 'reg_enable' => 'Activa el registre d’usuaris', 'reg_enable_toggle' => 'Activa el registre d’usuaris', - 'reg_enable_desc' => 'Si els registres estan activats, els usuaris podran registrar-se ells mateixos com a usuaris de l’aplicació. Un cop registrats, se’ls assigna un únic rol d’usuari per defecte.', - 'reg_default_role' => 'Rol d’usuari per defecte en registrar-se', - 'reg_enable_external_warning' => 'L’opció anterior s’ignora quan hi ha activada l’autenticació SAML o LDAP externa. Els comptes d’usuari de membres inexistents es crearan automàticament si l’autenticació contra el sistema extern és satisfactòria.', - 'reg_email_confirmation' => 'Confirmació de correu electrònic', - 'reg_email_confirmation_toggle' => 'Requereix la confirmació per correu electrònic', - 'reg_confirm_email_desc' => 'Si s’utilitza la restricció de dominis, serà obligatòria la confirmació per correu electrònic, i s’ignorarà aquesta opció.', + 'reg_enable_desc' => 'Quan el registre està activat, els usuaris es podran registrar com a usuari de l’aplicació. Un cop registrat, se’ls assignarà un rol d’usuari per defecte únic.', + 'reg_default_role' => 'Rol d’usuari per defecte després del registre.', + 'reg_enable_external_warning' => 'S’ignorarà l’opció de sobre quan l’autenticació LDAP or SAML estigui activada. Es crearan automàticament comptes d’usuari per als membres que encara no ho siguin si no és possible l’autenticació amb els sistema d’autenticació extern.', + 'reg_email_confirmation' => 'Correu electrònic de confirmació', + 'reg_email_confirmation_toggle' => 'Requereix un correu electrònic de confirmació', + 'reg_confirm_email_desc' => 'Si s’utilitza la restricció de dominis es requerirà un correu electrònic de confirmació i s’ignorarà aquesta opció.', 'reg_confirm_restrict_domain' => 'Restricció de dominis', - 'reg_confirm_restrict_domain_desc' => 'Introduïu una llista separada per comes de dominis de correu electrònic als quals voleu restringir els registres. S’enviarà un correu electrònic als usuaris perquè confirmin la seva adreça abans de permetre’ls interactuar amb l’aplicació.
Tingueu en compte que els usuaris podran canviar les seves adreces electròniques després de registrar-se correctament.', + 'reg_confirm_restrict_domain_desc' => 'Introduïu una llista separada per comes dels dominis a què voleu restringir el registre. S’enviarà un correu electrònic als usuaris perquè confirmin la seva adreça electrònica abans que puguin interactuar amb l’aplicació.
Tingueu en compte que els usuaris podran canviar l’adreça electrònica un cop s’hagin registrat.', 'reg_confirm_restrict_domain_placeholder' => 'No hi ha cap restricció', + // Sorting Settings + 'sorting' => 'Ordenar', + 'sorting_book_default' => 'Ordre predeterminat del llibre', + 'sorting_book_default_desc' => 'Selecciona la regla d\'ordenació predeterminada per aplicar a nous llibres. Això no afectarà els llibres existents, i pot ser anul·lat per llibre.', + 'sorting_rules' => 'Regles d\'ordenació', + 'sorting_rules_desc' => 'Són operacions d\'ordenació predefinides que es poden aplicar al contingut en el sistema.', + 'sort_rule_assigned_to_x_books' => 'Assignat a :count llibre | Assignat a :count llibres', + 'sort_rule_create' => 'Crear regla d\'ordenació', + 'sort_rule_edit' => 'Editar regla d\'ordenació', + 'sort_rule_delete' => 'Eliminar regla d\'ordenació', + 'sort_rule_delete_desc' => 'Eliminar aquesta regla d\'ordenació del sistema. Els llibres que utilitzin aquest tipus, es revertiran a l\'ordenació manual.', + 'sort_rule_delete_warn_books' => 'Aquesta regla d\'ordenació s\'utilitza actualment en :count llibre(s). Està segur que vol eliminar-la?', + 'sort_rule_delete_warn_default' => 'Aquesta regla d\'ordenació s\'utilitza actualment com a predeterminada per als llibres. Està segur que vol eliminar-la?', + 'sort_rule_details' => 'Detalls de la regla d\'ordenació', + 'sort_rule_details_desc' => 'Defineix un nom per aquesta regla d\'ordenació, que apareixerà a les llistes quan els usuaris estiguin seleccionant un ordre.', + 'sort_rule_operations' => 'Operacions d\'ordenació', + 'sort_rule_operations_desc' => 'Configura les accions d\'ordenació a realitzar movent-les de la llista d\'operacions disponibles. En utilitzar-se, les operacions s\'aplicaran en ordre, de dalt cap a baix. Qualsevol canvi realitzat aquí, s\'aplicarà a tots els llibres assignats en guardar.', + 'sort_rule_available_operations' => 'Operacions disponibles', + 'sort_rule_available_operations_empty' => 'No hi ha operacions pendents', + 'sort_rule_configured_operations' => 'Operacions configurades', + 'sort_rule_configured_operations_empty' => 'Arrossegar/afegir operacions des de la llista d\'Operacions Disponibles', + 'sort_rule_op_asc' => '(Asc)', + 'sort_rule_op_desc' => '(Desc)', + 'sort_rule_op_name' => 'Nom - Alfabètic', + 'sort_rule_op_name_numeric' => 'Nom - Numèric', + 'sort_rule_op_created_date' => 'Data de creació', + 'sort_rule_op_updated_date' => 'Data d\'actualització', + 'sort_rule_op_chapters_first' => 'Capítols a l\'inici', + 'sort_rule_op_chapters_last' => 'Capítols al final', + // Maintenance settings 'maint' => 'Manteniment', - 'maint_image_cleanup' => 'Neteja les imatges', - 'maint_image_cleanup_desc' => 'Escaneja el contingut de les pàgines i les revisions per a comprovar quines imatges i diagrames estan en ús actualment i quines imatges són redundants. Assegureu-vos de crear una còpia de seguretat completa de la base de dades i de les imatges abans d’executar això.', - 'maint_delete_images_only_in_revisions' => 'Suprimeix també les imatges que només existeixin en revisions antigues de pàgines', + 'maint_image_cleanup' => 'Neteja d’imatges', + 'maint_image_cleanup_desc' => 'Escanegeu les pàgines i les revisions per a comprovar quines imatges o dibuixos s’utilitzen i quins no. Assegureu-vos de crear una còpia de seguretat completa de la base de dades i de les imatges abans d’executar-la.', + 'maint_delete_images_only_in_revisions' => 'Suprimiu també les imatges que només existeixen en revisions de pàgina antigues.', 'maint_image_cleanup_run' => 'Executa la neteja', - 'maint_image_cleanup_warning' => 'S’han trobat :count imatges potencialment no utilitzades. Segur que voleu suprimir aquestes imatges?', - 'maint_image_cleanup_success' => 'S’han trobat i suprimit :count imatges potencialment no utilitzades!', - 'maint_image_cleanup_nothing_found' => 'No s’ha trobat cap imatge no utilitzada, així que no s’ha suprimit res!', - 'maint_send_test_email' => 'Envia un correu electrònic de prova', - 'maint_send_test_email_desc' => 'Envia un correu electrònic de prova a l’adreça electrònica que hàgiu especificat al perfil.', + 'maint_image_cleanup_warning' => 'Imatges que potencialment no s’utilitzen: :count. Esteu segur que voleu suprimir aquestes imatges?', + 'maint_image_cleanup_success' => 'Imatges que potencialment no s’utilitzen que s’han suprimit: :count.', + 'maint_image_cleanup_nothing_found' => 'No s’ha trobat cap imatge que no s’utilitzi. No s’ha suprimit res.', + 'maint_send_test_email' => 'Envia un correu electrònic de prova.', + 'maint_send_test_email_desc' => 'S’enviarà un correu electrònic de prova a l’adreça electrònica que figura al vostre perfil.', 'maint_send_test_email_run' => 'Envia el correu electrònic de prova', - 'maint_send_test_email_success' => 'S’ha enviat el correu electrònic a :address', + 'maint_send_test_email_success' => 'S’ha enviat un correu electrònic a :address', 'maint_send_test_email_mail_subject' => 'Correu electrònic de prova', - 'maint_send_test_email_mail_greeting' => 'El lliurament de correus electrònics sembla que funciona!', - 'maint_send_test_email_mail_text' => 'Enhorabona! Com que heu rebut aquesta notificació per correu electrònic, la vostra configuració del correu electrònic sembla que està ben configurada.', - 'maint_recycle_bin_desc' => 'Els prestatges, llibres, capítols i pàgines suprimits s’envien a la paperera de reciclatge perquè es puguin restaurar o suprimir de manera permanent. Pot ser que els elements més antics de la paperera de reciclatge se suprimeixin automàticament després d’un temps, depenent de la configuració del sistema.', - 'maint_recycle_bin_open' => 'Obre la paperera de reciclatge', + 'maint_send_test_email_mail_greeting' => 'Sembla que l’enviament de correus electrònics funciona.', + 'maint_send_test_email_mail_text' => 'Enhorabona! Que hagis rebut aquesta notificació de correu electrònic vol dir que la vostra configuració de correu electrònic és correcta.', + 'maint_recycle_bin_desc' => 'Els prestatges, els llibres, els capítols i les pàgines suprimides s’envien a la paperera perquè es puguin restaurar o suprimir permanentment. És possible que els elements que fa més temps que són a la paperera s’eliminin automàticament al cap d’un temps segons la configuració del sistema.', + 'maint_recycle_bin_open' => 'Obre la paperera', 'maint_regen_references' => 'Regenera les referències', - 'maint_regen_references_desc' => 'Aquesta acció reconstruirà l’índex de referències entre elements de la base de dades. Normalment es gestiona automàticament, però aquesta acció pot ser útil per a indexar contingut antic o contingut afegit mitjançant mètodes no oficials.', - 'maint_regen_references_success' => 'L’índex de referències s’ha regenerat!', - 'maint_timeout_command_note' => 'Nota: Aquesta acció pot trigar estona a executar-se, la qual cosa pot provocar errors de temps d’expera excedits en alguns entorns web. Com a alternativa, podeu executar aquesta acció en una ordre al terminal.', + 'maint_regen_references_desc' => 'Aquesta acció reconstruirà l’índex de referències creuades entre elements a la base de dades. Normalment es fa automàticament però aquesta acció és útil per a indexar contingut antic o contingut afegit a través de mètodes no oficials.', + 'maint_regen_references_success' => 'S’ha regenerat l’índex de referències.', + 'maint_timeout_command_note' => 'Nota: És possible que aquesta acció trigui a executar-se cosa que pot provocar que s’excedeixi el temps d’espera en alguns entorns web. Com a alternativa, podeu executar aquesta acció amb una ordre del terminal.', // Recycle Bin - 'recycle_bin' => 'Paperera de reciclatge', - 'recycle_bin_desc' => 'Aquí podeu restaurar els elements que hàgiu suprimit o triar suprimir-los del sistema de manera permanent. Aquesta llista no té cap filtre, al contrari que altres llistes d’activitat similars en què es tenen en compte els filtres de permisos.', + 'recycle_bin' => 'Paperera', + 'recycle_bin_desc' => 'Aquí podeu restaurar els elements que s’han suprimit o suprimir-los permanentment del sistema. Aquesta llista no està filtrada, a diferència de llistes d’activitat similars on s’apliquen filtres de permisos.', 'recycle_bin_deleted_item' => 'Element suprimit', 'recycle_bin_deleted_parent' => 'Pare', 'recycle_bin_deleted_by' => 'Suprimit per', - 'recycle_bin_deleted_at' => 'Moment de la supressió', - 'recycle_bin_permanently_delete' => 'Suprimeix permanentment', + 'recycle_bin_deleted_at' => 'Hora de supressió', + 'recycle_bin_permanently_delete' => 'Suprimit permanentment', 'recycle_bin_restore' => 'Restaura', - 'recycle_bin_contents_empty' => 'La paperera de reciclatge és buida', - 'recycle_bin_empty' => 'Buida la paperera de reciclatge', - 'recycle_bin_empty_confirm' => 'Se suprimiran de manera permanent tots els elements de la paperera de reciclatge, incloent-hi el contingut dins de cada element. Segur que voleu buidar la paperera de reciclatge?', - 'recycle_bin_destroy_confirm' => 'Aquesta acció suprimirà del sistema de manera permanent aquest element, juntament amb tots els elements fills que es llisten a sota, i no podreu restaurar aquest contingut. Segur que voleu suprimir de manera permanent aquest element?', - 'recycle_bin_destroy_list' => 'Elements que es destruiran', - 'recycle_bin_restore_list' => 'Elements que es restauraran', - 'recycle_bin_restore_confirm' => 'Aquesta acció restaurarà l’element suprimit, incloent-hi tots els elements fills, a la seva ubicació original. Si la ubicació original ha estat suprimida, i ara és a la paperera de reciclatge, caldrà que també en restaureu l’element pare.', - 'recycle_bin_restore_deleted_parent' => 'El pare d’aquest element també ha estat suprimit. L’element es mantindrà suprimit fins que el pare també es restauri.', - 'recycle_bin_restore_parent' => 'Restaura’n el pare', - 'recycle_bin_destroy_notification' => 'S’han suprimit :count elements en total de la paperera de reciclatge.', - 'recycle_bin_restore_notification' => 'S’han restaurat :count elements en total de la paperera de reciclatge.', + 'recycle_bin_contents_empty' => 'La paperera és buida', + 'recycle_bin_empty' => 'Buida la paperera', + 'recycle_bin_empty_confirm' => 'Se suprimiran permanentment tots els elements que hi ha a la paperera incloent-hi el contingut que hi hagi a cada element. Esteu segur que voleu buidar la paperera?', + 'recycle_bin_destroy_confirm' => 'Aquesta acció suprimirà del sistema de manera permanent aquest element, juntament amb tots els fills que es llisten a sota, i no podreu restaurar aquest contingut. Segur que voleu suprimir de manera permanent aquest element?', + 'recycle_bin_destroy_list' => 'Elements per destruir', + 'recycle_bin_restore_list' => 'Elements per restaurar', + 'recycle_bin_restore_confirm' => 'Aquesta acció restaurarà l’element suprimit, incloent-hi els elements fills, a la seva ubicació original. Si la ubicació original s’ha suprimit i és a la paperera, l’element pare també s’haurà de restaurar.', + 'recycle_bin_restore_deleted_parent' => 'El pare d’aquest element també s’ha suprimit. L’element continuarà suprimit fins que no se’n restauri també el pare.', + 'recycle_bin_restore_parent' => 'Restaura el pare', + 'recycle_bin_destroy_notification' => 'Elements suprimits de la paperera: :count.', + 'recycle_bin_restore_notification' => 'Elements restaurats de la paperera: :count.', // Audit Log 'audit' => 'Registre d’auditoria', - 'audit_desc' => 'Aquest registre d’auditoria mostra una llista d’activitats registrades al sistema. Aquesta llista no té cap filtre, al contrari que altres llistes d’activitat similars en què es tenen en compte els filtres de permisos.', + 'audit_desc' => 'El registre d\'auditoria mostra una llista de les activitats de què es fa un seguiment. Aquesta llista no està filtrada, a diferència de llistes d’activitat similars on s’apliquen filtres de permisos.', 'audit_event_filter' => 'Filtre d’esdeveniments', 'audit_event_filter_no_filter' => 'Sense filtre', 'audit_deleted_item' => 'Element suprimit', @@ -130,152 +160,161 @@ 'audit_table_related' => 'Element relacionat o detall', 'audit_table_ip' => 'Adreça IP', 'audit_table_date' => 'Data de l’activitat', - 'audit_date_from' => 'Rang de dates a partir de', - 'audit_date_to' => 'Rang de rates fins a', + 'audit_date_from' => 'Des de', + 'audit_date_to' => 'Fins a', // Role Settings 'roles' => 'Rols', 'role_user_roles' => 'Rols d’usuari', - 'roles_index_desc' => 'Els rols s’utilitzen per a agrupar usuaris i proporcionar permisos del sistema a llurs membres. Quan un usuari és membre de múltiples rols, els privilegis que li concedeixin s’acumularan i l’usuari heretarà totes les habilitats.', + 'roles_index_desc' => 'Els rols d’usuari s’utilitzen per a agrupar usuaris i donar-los permisos conjuntament. Un usuari que tingui més d’un rol acumularà els privilegis que s’atorguin a tots els rols i n’heretarà els permisos.', 'roles_x_users_assigned' => ':count usuari assignat|:count usuaris assignats', 'roles_x_permissions_provided' => ':count permís|:count permisos', 'roles_assigned_users' => 'Usuaris assignats', - 'roles_permissions_provided' => 'Permisos proporcionats', - 'role_create' => 'Crea un rol nou', - 'role_delete' => 'Suprimeix el rol', - 'role_delete_confirm' => 'Se suprimirà el rol amb el nom «:roleName».', - 'role_delete_users_assigned' => 'Aquest rol té :userCount usuaris assignats. Si voleu migrar els usuaris d’aquest rol, seleccioneu un rol nou a continuació.', + 'roles_permissions_provided' => 'Permisos atorgats', + 'role_create' => 'Crea un rol', + 'role_delete' => 'Suprimeix un rol', + 'role_delete_confirm' => 'Se suprimirà el rol «:roleName».', + 'role_delete_users_assigned' => 'Usuaris assignats en aquest rol: :userCount. Si voleu migrar aquests usuaris a un altre rol, seleccioneu-ne un dels de sota.', 'role_delete_no_migration' => "No migris els usuaris", - 'role_delete_sure' => 'Segur que voleu suprimir aquest rol?', + 'role_delete_sure' => 'Esteu segur que voleu suprimir aquest rol?', 'role_edit' => 'Edita el rol', 'role_details' => 'Detalls del rol', 'role_name' => 'Nom del rol', - 'role_desc' => 'Descripció curta del rol', - 'role_mfa_enforced' => 'Requereix autenticació de múltiple factor', + 'role_desc' => 'Descripció del rol', + 'role_mfa_enforced' => 'Autenticació multifactorial requerida', 'role_external_auth_id' => 'Identificadors d’autenticació externa', 'role_system' => 'Permisos del sistema', - 'role_manage_users' => 'Gestiona usuaris', - 'role_manage_roles' => 'Gestiona rols i permisos de rols', - 'role_manage_entity_permissions' => 'Gestiona els permisos de tots els llibres, capítols i pàgines', - 'role_manage_own_entity_permissions' => 'Gestiona els permisos dels llibres, capítols i pàgines propis', - 'role_manage_page_templates' => 'Gestiona les plantilles de pàgines', - 'role_access_api' => 'Accedeix a l’API del sistema', - 'role_manage_settings' => 'Gestiona la configuració de l’aplicació', - 'role_export_content' => 'Exporta el contingut', - 'role_editor_change' => 'Canvia l’editor de pàgines', - 'role_notifications' => 'Rep i gestiona les notificacions', + 'role_manage_users' => 'Gestió dels usuaris', + 'role_manage_roles' => 'Gestió dels rols i els seus permisos', + 'role_manage_entity_permissions' => 'Gestió de tots els permisos de llibres, capítols i pàgines', + 'role_manage_own_entity_permissions' => 'Gestió dels permisos als seus llibres, capítols i pàgines', + 'role_manage_page_templates' => 'Gestió de les plantilles de pàgina', + 'role_access_api' => 'Accés a l’API del sistema', + 'role_manage_settings' => 'Gestió de la configuració de l’aplicació', + 'role_export_content' => 'Exportació de contingut', + 'role_import_content' => 'Importar contingut', + 'role_editor_change' => 'Canvi de l’editor de pàgina', + 'role_notifications' => 'Recepció i gestió de notificacions', 'role_asset' => 'Permisos de recursos', - 'roles_system_warning' => 'Tingueu en compte que l’accés a qualsevol dels tres permisos de dalt pot permetre que un usuari alteri els seus propis permisos o els privilegis d’altres usuaris del sistema. Assigneu rols amb aquests permisos només a usuaris de confiança.', - 'role_asset_desc' => 'Aquests permisos controlen l’accés per defecte als recursos del sistema. Els permisos de llibres, capítols i pàgines tindran més importància que aquests permisos.', - 'role_asset_admins' => 'Els administradors tenen accés automàticament a tot el contingut, però aquestes opcions poden mostrar o amagar opcions de la interfície d’usuari.', - 'role_asset_image_view_note' => 'Això és relatiu a la visibilitat dins del gestor d’imatges. L’accés real als fitxers d’imatge pujats dependrà de l’opció d’emmagatzematge d’imatges del sistema.', + 'roles_system_warning' => 'Tingueu en compte que l’accés a qualsevol dels tres permisos de dalt permeten que l’usuari canviï els seus privilegis i els privilegis d’altres usuaris. Assigneu rols d’usuari amb aquests permisos només a usuaris de confiança.', + 'role_asset_desc' => 'Aquests permisos controlen l’accés per defecte als recursos del sistema. El permisos dels llibres, capítols i pàgines sobreescriuran aquests permisos.', + 'role_asset_admins' => 'Als administradors se’ls dona accés automàticament a tot el contingut però aquestes opcions mostren o amaguen opcions de la interfície d’usuari.', + 'role_asset_image_view_note' => 'Això té relació amb la visibilitat al gestor d’imatges. L’accés a les imatges pujades dependrà de l’opció d’emmagatzematge d’imatges dels sistema.', 'role_all' => 'Tot', 'role_own' => 'Propi', - 'role_controlled_by_asset' => 'Controlat pel recurs en què es pugen', + 'role_controlled_by_asset' => 'Controlat pel recurs a què estan pujats', 'role_save' => 'Desa el rol', - 'role_users' => 'Usuaris amb aquest rol', - 'role_users_none' => 'Ara mateix no hi ha cap usuari assignat a aquest rol', + 'role_users' => 'Usuaris assignats en aquest rol', + 'role_users_none' => 'No hi ha cap usuari assignat en aquest rol', // Users 'users' => 'Usuaris', - 'users_index_desc' => 'Creeu i gestioneu comptes d’usuari individuals dins del sistema. Els comptes d’usuari s’utilitzen per a iniciar la sessió i atribuir el contingut i les activitats. Els permisos d’accés són principalment basats en rols, però si un usuari és propietari o no d’un contingut, entre altres factors, també pot afectar-ne els permisos i l’accés.', - 'user_profile' => 'Perfil de l’usuari', + 'users_index_desc' => 'Creeu i gestioneu comptes d’usuari individuals. Els comptes d’usuari s’utilitzen per als inicis de sessió i les atribucions de contingut i activitat. Els permisos d’accés es basen fonamentalment en el rol d’usuari però la propietat del contingut, entre d’altres, també afecta els permisos i l’accés.', + 'user_profile' => 'Perfil d’usuari', 'users_add_new' => 'Afegeix un usuari nou', 'users_search' => 'Cerca usuaris', - 'users_latest_activity' => 'Darrera activitat', + 'users_latest_activity' => 'Activitat més recent', 'users_details' => 'Detalls de l’usuari', - 'users_details_desc' => 'Definiu un nom públic i una adreça electrònica per a aquest usuari. L’adreça electrònica es farà servir per a iniciar la sessió a l’aplicació.', - 'users_details_desc_no_email' => 'Definiu un nom públic per a aquest usuari perquè els altres el puguin reconèixer.', - 'users_role' => 'Rols de l’usuari', - 'users_role_desc' => 'Seleccioneu a quins rols s’assignarà l’usuari. Si un usuari s’assigna a múltiples rols, els permisos dels rols s’acumularan i l’usuari rebrà totes les habilitats dels rols assignats.', - 'users_password' => 'Contrasenya de l’usuari', - 'users_password_desc' => 'Definiu una contrasenya que s’utilitzarà per a iniciar la sessió a l’aplicació. Cal que tingui un mínim de 8 caràcters.', - 'users_send_invite_text' => 'Podeu elegir enviar un correu d’invitació a aquest usuari, la qual cosa li permetrà definir la seva contrasenya, o podeu definir-li una contrasenya vós.', - 'users_send_invite_option' => 'Envia un correu d’invitació a l’usuari', + 'users_details_desc' => 'Configureu un nom i una adreça electrònica per a aquest usuari. L’adreça electrònica s’usarà per a iniciar sessió a l’aplicació.', + 'users_details_desc_no_email' => 'Configureu un nom per a aquest usuari perquè se’l pugui reconèixer.', + 'users_role' => 'Rols d’usuari', + 'users_role_desc' => 'Seleccioneu quins rols s’assignaran en aquest usuari. Si a un usuari se li assignen més d’un rol acumularà els privilegis que s’atorguin a tots els rols i n’heretarà les els permisos.', + 'users_password' => 'Contrasenya d’usuari', + 'users_password_desc' => 'Configureu la contrasenya que s’utilitzarà per a iniciar sessió a l’aplicació. Ha de tenir com a mínim 8 caràcters.', + 'users_send_invite_text' => 'Podeu configurar la contrasenya o enviar un correu electrònic que convidi a l’usuari a configurar-la.', + 'users_send_invite_option' => 'Envia el correu electrònic', 'users_external_auth_id' => 'Identificador d’autenticació extern', - 'users_external_auth_id_desc' => 'Quan es fa servir un sistema d’autenticació extern (com ara SAML2, OIDC o LDAP), aquest és l’identificador que enllaça aquest usuari del BookStack amb el compte del sistema d’autenticació. Podeu ignorar aquest camp si feu servir l’autenticació basada en correu electrònic per defecte.', - 'users_password_warning' => 'Ompliu-ho només si voleu canviar la contrasenya d’aquest usuari.', - 'users_system_public' => 'Aquest usuari representa qualsevol usuari convidat que visita la vostra instància. No es pot fer servir per a iniciar la sessió, però s’assigna automàticament.', + 'users_external_auth_id_desc' => 'Quan s’utilitza un sistema d’autenticació extern (com ara SAML2, OIDC or LDAP) l’identificador enllaça l’usuari amb el compte dels sistema d’autenticació. Podeu ignorar aquest camp si utilitzeu l’autenticació amb correu electrònic per defecte.', + 'users_password_warning' => 'Ompliu els camps de sota només si voleu canviar la contrasenya d’aquest usuari.', + 'users_system_public' => 'Aquest usuari representa qualsevol usuari convidat que visiti la instància. No es pot utilitzar per a iniciar sessió sinó que s’assigna automàticament.', 'users_delete' => 'Suprimeix l’usuari', 'users_delete_named' => 'Suprimeix l’usuari :userName', - 'users_delete_warning' => 'Se suprimirà completament del sistema l’usuari amb el nom «:userName».', - 'users_delete_confirm' => 'Segur que voleu suprimir aquest usuari?', - 'users_migrate_ownership' => 'Migra l’autoria', - 'users_migrate_ownership_desc' => 'Seleccioneu un usuari si voleu que un altre usuari esdevingui el propietari de tots els elements que ara són propietat d’aquest usuari.', - 'users_none_selected' => 'No hi ha cap usuari seleccionat', + 'users_delete_warning' => 'Se suprimirà l’usuari «:userName» del sistema.', + 'users_delete_confirm' => 'Esteu segur que voleu suprimir aquest usuari?', + 'users_migrate_ownership' => 'Migració de la propietat', + 'users_migrate_ownership_desc' => 'Si voleu que un altre usuari esdevingui el propietari de tots els elements d’aquest usuari, seleccioneu-ne un.', + 'users_none_selected' => 'No s’ha seleccionat cap usuari', 'users_edit' => 'Edita l’usuari', 'users_edit_profile' => 'Edita el perfil', - 'users_avatar' => 'Avatar de l’usuari', - 'users_avatar_desc' => 'Seleccioneu una imatge que representi aquest usuari. Hauria de ser un quadrat d’aproximadament 256 px.', - 'users_preferred_language' => 'Llengua preferida', - 'users_preferred_language_desc' => 'Aquesta opció canviarà la llengua utilitzada a la interfície d’usuari de l’aplicació. No afectarà el contingut creat pels usuaris.', - 'users_social_accounts' => 'Comptes socials', - 'users_social_accounts_desc' => 'Vegeu l’estat dels comptes socials connectats d’aquest usuari. Els comptes socials es poden fer servir per a accedir al sistema de manera addicional al sistema d’autenticació principal.', - 'users_social_accounts_info' => 'Aquí podeu connectar altres comptes per a un inici de sessió més ràpid i còmode. Si desconnecteu un compte aquí, no en revoqueu l’accés d’autorització donat amb anterioritat. Revoqueu-hi l’accés a la configuració del perfil del compte social que hàgiu connectat.', - 'users_social_connect' => 'Connecta un compte', + 'users_avatar' => 'Avatar', + 'users_avatar_desc' => 'Seleccioneu una imatge per a representar aquest usuari. Ha de ser una imatge quadrada de 256px de costat, aproximadament.', + 'users_preferred_language' => 'Llengua', + 'users_preferred_language_desc' => 'Canvia la llengua en què es mostra la interfície d’usuari de l’aplicació. No afectarà el contingut creat pels usuaris.', + 'users_social_accounts' => 'Comptes a les xarxes socials', + 'users_social_accounts_desc' => 'Mireu l’estat dels comptes socials connectats d’aquest usuari. També es poden utilitzar els comptes socials per a iniciar sessió a banda del sistema d’autenticació principal.', + 'users_social_accounts_info' => 'Connecteu els vostres comptes socials per a iniciar sessió més ràpidament. La desconnexió d’un compte no revoca l’autorització d’accés atorgada prèviament. Revoqueu l’accés des de la configuració de perfil del compte social connectat.', + 'users_social_connect' => 'Connecta el compte', 'users_social_disconnect' => 'Desconnecta el compte', 'users_social_status_connected' => 'Connectat', 'users_social_status_disconnected' => 'Desconnectat', - 'users_social_connected' => 'El compte de :socialAccount s’ha associat correctament al vostre perfil.', - 'users_social_disconnected' => 'El compte de :socialAccount ’ha desassociat correctament del vostre perfil.', - 'users_api_tokens' => 'Testimonis de l’API', - 'users_api_tokens_desc' => 'Creeu i gestioneu els testimonis d’accés que s’utilitzen per a autenticar-vos a l’API REST del BookStack. Els permisos de l’API es gestionen fent servir l’usuari al qual pertany el testimoni.', - 'users_api_tokens_none' => 'No s’ha creat cap testimoni de l’API per a aquest usuari', + 'users_social_connected' => 'S’ha connectat el compte :socialAccount al vostre perfil.', + 'users_social_disconnected' => 'S’ha desconnectat el compte :socialAccount del vostre perfil.', + 'users_api_tokens' => 'Testimonis API', + 'users_api_tokens_desc' => 'Creeu i gestioneu els testimonis d’accés que s’utilitzen per autenticar els usuaris amb la l’API REST del BookStack. Els permisos de l’API es gestionen a través de l’usuari a qui pertany el testimoni.', + 'users_api_tokens_none' => 'No s’ha creat cap testimoni API per a aquest usuari', 'users_api_tokens_create' => 'Crea un testimoni', 'users_api_tokens_expires' => 'Caducitat', 'users_api_tokens_docs' => 'Documentació de l’API', - 'users_mfa' => 'Autenticació de múltiple factor', - 'users_mfa_desc' => 'Configureu l’autenticació de múltiple factor com a capa extra de seguretat en el vostre compte d’usuari.', - 'users_mfa_x_methods' => ':count mètode configurat|:count mètodes configurats', - 'users_mfa_configure' => 'Configura els mètodes', + 'users_mfa' => 'Autenticació multifactorial', + 'users_mfa_desc' => 'Configureu l’autenticació multifactorial per a afegir una capa de seguretat extra al vostre compte d’usuari.', + 'users_mfa_x_methods' => 'Hi ha :count mètode configurat|Hi ha :count mètodes configurats', + 'users_mfa_configure' => 'Configura un mètode', // API Tokens - 'user_api_token_create' => 'Crea un testimoni de l’API', + 'user_api_token_create' => 'Crea un testimoni API', 'user_api_token_name' => 'Nom', - 'user_api_token_name_desc' => 'Poseu un nom llegible al vostre testimoni com a recordatori futur del propòsit al qual el voleu destinar.', + 'user_api_token_name_desc' => 'Anomeneu el testimoni amb un nom entenedor que permeti saber-ne el propòsit.', 'user_api_token_expiry' => 'Data de caducitat', - 'user_api_token_expiry_desc' => 'Definiu una data en què aquest testimoni caducarà. Després d’aquesta data, les peticions fetes amb aquest testimoni deixaran de funcionar. Si deixeu aquest camp en blanc, es definirà una caducitat d’aquí a 100 anys.', - 'user_api_token_create_secret_message' => 'Just després de crear aquest testimoni, es generaran i es mostraran un «Identificador del testimoni» i un «Secret del testimoni». El secret només es mostrarà una única vegada: assegureu-vos de copiar-lo a un lloc segur abans de continuar.', - 'user_api_token' => 'Testimoni de l’API', - 'user_api_token_id' => 'Identificador del testimoni', - 'user_api_token_id_desc' => 'Aquest identificador és generat pel sistema per a aquest testimoni i no és editable i caldrà que el proporcioneu a les peticions a l’API.', - 'user_api_token_secret' => 'Secret del testimoni', - 'user_api_token_secret_desc' => 'Aquest secret és generat pel sistema per a aquest testimoni i caldrà que el proporcioneu a les peticions a l’API. Només es mostrarà aquesta única vegada, assegureu-vos de copiar-lo a un lloc segur.', + 'user_api_token_expiry_desc' => 'Configureu la data de caducitat del testimoni. Un cop passada aquesta data, les sol·licituds fetes amb aquest testimoni no funcionaran. Si deixeu aquest camp en blanc, el testimoni caducarà d’aquí 100 anys.', + 'user_api_token_create_secret_message' => 'Es crearà i es mostrarà un «identificador de testimoni» i un «secret de testimoni» immediatament després de crear aquest testimoni. El secret es mostrarà només un sol cop. Assegureu-vos d’anotar-lo i de desar-lo en un lloc segur abans de continuar.', + 'user_api_token' => 'Testimoni API', + 'user_api_token_id' => 'Identificador de testimoni', + 'user_api_token_id_desc' => 'És un identificador no editable generat pel sistema per a aquest testimoni i que s’haurà de proporcionar en les sol·licituds API.', + 'user_api_token_secret' => 'Secret de testimoni', + 'user_api_token_secret_desc' => 'És un secret generat pel sistema per a aquest testimoni i que s’haurà de proporcionar en les sol·licituds API. Es mostrarà només un sol cop. Assegureu-vos d’anotar-lo i de desar-lo en un lloc segur abans de continuar.', 'user_api_token_created' => 'Testimoni creat :timeAgo', 'user_api_token_updated' => 'Testimoni actualitzat :timeAgo', 'user_api_token_delete' => 'Suprimeix el testimoni', - 'user_api_token_delete_warning' => 'Se suprimirà completament del sistema aquest testimoni de l’API amb el nom «:tokenName».', - 'user_api_token_delete_confirm' => 'Segur que voleu suprimir aquest testimoni de l’API?', + 'user_api_token_delete_warning' => 'Se suprimirà el testimoni API «:tokenName» del sistema.', + 'user_api_token_delete_confirm' => 'Esteu segur que voleu suprimir aquest testimoni API?', // Webhooks 'webhooks' => 'Webhooks', - 'webhooks_index_desc' => 'Els webhooks són una manera d’enviar dades a URL externs quan es produeixen certes accions i esdeveniments al sistema, la qual cosa permet una integració basada en esdeveniments amb plataformes externs, com ara sistemes de missatgeria o de notificacions.', - 'webhooks_x_trigger_events' => ':count esdeveniment disparador|:count elements disparadors', - 'webhooks_create' => 'Crea un webhook nou', - 'webhooks_none_created' => 'Encara no s’ha creat cap webhook.', + 'webhooks_index_desc' => 'Els webhooks permeten enviar dades a URLs externs quan ocorren unes accions o esdeveniments determinats. Això permet la integració basada en esdeveniments amb plataformes externes com ara sistemes de missatgeria o notificació.', + 'webhooks_x_trigger_events' => 'Hi ha :count esdeveniment disparador|Hi ha :count esdeveniments disparadors', + 'webhooks_create' => 'Crea un webhook', + 'webhooks_none_created' => 'No hi ha cap webhook', 'webhooks_edit' => 'Edita el webhook', 'webhooks_save' => 'Desa el webhook', 'webhooks_details' => 'Detalls del webhook', - 'webhooks_details_desc' => 'Proporcioneu un nom amigable i un endpoint POST com a ubicació on s’enviaran les dades del webhook.', - 'webhooks_events' => 'Esdeveniments del webhook', - 'webhooks_events_desc' => 'Seleccioneu tots els esdeveniments que haurien de fer que es cridés aquest webhook.', - 'webhooks_events_warning' => 'Tingueu en compte que aquests esdeveniments es produiran per a tots els esdeveniments seleccionats, fins i tot si s’hi apliquen permisos personalitzats. Assegureu-vos que aquest webhook no exposarà contingut confidencial.', + 'webhooks_details_desc' => 'Anomeneu el webhook amb un nom entenedor i proporcioneu un extrem POST com a ubicació on enviar les dades per al webhook.', + 'webhooks_events' => 'Esdeveniments webhook', + 'webhooks_events_desc' => 'Seleccioneu els esdeveniment que voleu que disparin la crida d’aquest webhook.', + 'webhooks_events_warning' => 'Tingueu en compte que aquestes crides es dispararan a tots els esdeveniments seleccionats, inclús si s’apliquen permisos personalitzats. Assegureu-vos que l’ús d’aquest webhook no exposarà contingut confidencial.', 'webhooks_events_all' => 'Tots els esdeveniments del sistema', 'webhooks_name' => 'Nom del webhook', - 'webhooks_timeout' => 'Temps d’espera de les peticions al webhook (en segons)', - 'webhooks_endpoint' => 'Endpoint del webhook', + 'webhooks_timeout' => 'Temps d’espera del webhook (en segons)', + 'webhooks_endpoint' => 'Extrem del webhook', 'webhooks_active' => 'Webhook actiu', 'webhook_events_table_header' => 'Esdeveniments', 'webhooks_delete' => 'Suprimeix el webhook', - 'webhooks_delete_warning' => 'Se suprimirà completament del sistema el webhook amb el nom «:webhookName».', - 'webhooks_delete_confirm' => 'Segur que voleu suprimir aquest webhook?', - 'webhooks_format_example' => 'Exemple del format del webhook', - 'webhooks_format_example_desc' => 'Les dades del webhook s’envien com una petició POST a l’endpoint configurat amb un JSON que segueix el següent format. Les propietats «related_item» i «url» són opcionals i dependran del tipus d’esdeveniment produït.', + 'webhooks_delete_warning' => 'Se suprimirà el webhook «:webhookName» del sistema.', + 'webhooks_delete_confirm' => 'Esteu segur que voleu suprimir aquest webhook?', + 'webhooks_format_example' => 'Exemple de format de webhook', + 'webhooks_format_example_desc' => 'Les dades d’un webhook s’envien com una sol·licitud POST a l’extrem configurat com a JSON amb el format següent. Les propietats «related_item» i «url» són opcionals i dependran del tipus d’esdeveniment que es dispari.', 'webhooks_status' => 'Estat del webhook', - 'webhooks_last_called' => 'Cridat per darrera vegada:', - 'webhooks_last_errored' => 'Error per darrera vegada:', + 'webhooks_last_called' => 'Darrera crida:', + 'webhooks_last_errored' => 'Darrer error:', 'webhooks_last_error_message' => 'Darrer missatge d’error:', + // Licensing + 'licenses' => 'Llicències', + 'licenses_desc' => 'Aquesta pàgina detalla informació sobre la llicència de BookStack a més dels projectes i biblioteques que s\'utilitzen en BookStack. Molts projectes enumerats aquí poden ser utilitzats només en un context de desenvolupament.', + 'licenses_bookstack' => 'Llicència de BookStack', + 'licenses_php' => 'Llicències de Biblioteques de PHP', + 'licenses_js' => 'Llicències de Biblioteques de JavaScript', + 'licenses_other' => 'Altres llicències', + 'license_details' => 'Detalls de la llicència', //! If editing translations files directly please ignore this in all //! languages apart from en. Content will be auto-copied from en. @@ -287,6 +326,7 @@ 'bs' => 'Bosanski', 'ca' => 'Català', 'cs' => 'Česky', + 'cy' => 'Cymraeg', 'da' => 'Dansk', 'de' => 'Deutsch (Sie)', 'de_informal' => 'Deutsch (Du)', @@ -308,6 +348,7 @@ 'lt' => 'Lietuvių Kalba', 'lv' => 'Latviešu Valoda', 'nb' => 'Norsk (Bokmål)', + 'ne' => 'नेपाली', 'nn' => 'Nynorsk', 'nl' => 'Nederlands', 'pl' => 'Polski', diff --git a/lang/ca/validation.php b/lang/ca/validation.php index b73a66c5c6c..bc821749a25 100644 --- a/lang/ca/validation.php +++ b/lang/ca/validation.php @@ -12,13 +12,13 @@ 'active_url' => 'El camp :attribute no és un URL vàlid.', 'after' => 'El camp :attribute ha de ser una data posterior a :date.', 'alpha' => 'El camp :attribute només pot contenir lletres.', - 'alpha_dash' => 'El camp :attribute només pot contenir lletres, números, guions i guions baixos.', - 'alpha_num' => 'El camp :attribute només pot contenir lletres i números.', - 'array' => 'El camp :attribute ha de ser un vector.', - 'backup_codes' => 'El codi que heu proporcionat no és vàlid o ja s’ha fet servir.', - 'before' => 'El camp :attribute ha de ser una data anterior a :date.', + 'alpha_dash' => 'El camp :attribute només pot contenir lletres, xifres, guionets i guions baixos.', + 'alpha_num' => 'El camp :attribute només pot contenir lletres, xifres.', + 'array' => 'El camp :attribute ha de ser una matriu.', + 'backup_codes' => 'El codi que heu proporcionat no és vàlid o ja s’ha utilitzat.', + 'before' => 'El camp :attribute ha de ser una data posterior a :date.', 'between' => [ - 'numeric' => 'El camp :attribute ha d’estar entre :min i :max.', + 'numeric' => 'El camp :attribute ha de ser un nombre entre :min i :max.', 'file' => 'El camp :attribute ha de tenir entre :min i :max kilobytes.', 'string' => 'El camp :attribute ha de tenir entre :min i :max caràcters.', 'array' => 'El camp :attribute ha de tenir entre :min i :max elements.', @@ -27,11 +27,11 @@ 'confirmed' => 'La confirmació del camp :attribute no coincideix.', 'date' => 'El camp :attribute no és una data vàlida.', 'date_format' => 'El camp :attribute no coincideix amb el format :format.', - 'different' => 'Els camps :attribute i :other han de ser diferents.', - 'digits' => 'El camp :attribute ha de tenir :digits dígits.', - 'digits_between' => 'El camp :attribute ha de tenir entre :min i :max dígits.', - 'email' => 'El camp :attribute ha de ser una adreça electrònica vàlida.', - 'ends_with' => 'El camp :attribute ha d’acabar amb un dels següents valors: :values', + 'different' => 'El camp :attribute i :other han de ser diferents.', + 'digits' => 'El camp :attribute ha de tenir :digits xifres.', + 'digits_between' => 'El camp :attribute ha de tenir entre :min i :max xifres.', + 'email' => 'El camp :attribute ha de ser un adreça electrònica vàlida.', + 'ends_with' => 'El camp :attribute ha d’acabar amb un dels signes següents: :values', 'file' => 'El camp :attribute ha de ser un fitxer vàlid.', 'filled' => 'El camp :attribute és obligatori.', 'gt' => [ @@ -41,74 +41,79 @@ 'array' => 'El camp :attribute ha de tenir més de :value elements.', ], 'gte' => [ - 'numeric' => 'El camp :attribute ha de ser més gran o igual que :value.', - 'file' => 'El camp :attribute ha de tenir :value kilobytes o més.', - 'string' => 'El camp :attribute ha de tenir :value caràcters o més.', - 'array' => 'El camp :attribute ha de tenir :value elements o més.', + 'numeric' => 'El camp :attribute ha de ser com a mínim :value.', + 'file' => 'El camp :attribute ha de tenir com a mínim :value kilobytes.', + 'string' => 'El camp :attribute ha de tenir com a mínim :value caràcters.', + 'array' => 'El camp :attribute ha de tenir com a mínim :value elements.', ], 'exists' => 'El camp :attribute seleccionat no és vàlid.', 'image' => 'El camp :attribute ha de ser una imatge.', - 'image_extension' => 'El camp :attribute ha de tenir una extensió d’imatge vàlida i suportada.', - 'in' => 'El camp :attribute seleccionat no és vàlid.', - 'integer' => 'El camp :attribute ha de ser un enter.', - 'ip' => 'El camp :attribute ha de ser una adreça IP vàlida.', - 'ipv4' => 'El camp :attribute ha de ser una adreça IPv4 vàlida.', - 'ipv6' => 'El camp :attribute ha de ser una adreça IPv6 vàlida.', + 'image_extension' => 'El camp :attribute ha de tenir una extensió d’imatge compatible.', + 'in' => 'El camp :attribute no és vàlid.', + 'integer' => 'El camp :attribute ha de ser un nombre enter.', + 'ip' => 'El camp :attribute ha de ser un adreça IP vàlida.', + 'ipv4' => 'El camp :attribute ha de ser un adreça IPv4 vàlida.', + 'ipv6' => 'El camp :attribute ha de ser un adreça IPv6 vàlida.', 'json' => 'El camp :attribute ha de ser una cadena JSON vàlida.', 'lt' => [ - 'numeric' => 'El camp :attribute ha de ser menor que :value.', + 'numeric' => 'El camp :attribute ha de ser més petit que :value.', 'file' => 'El camp :attribute ha de tenir menys de :value kilobytes.', 'string' => 'El camp :attribute ha de tenir menys de :value caràcters.', 'array' => 'El camp :attribute ha de tenir menys de :value elements.', ], 'lte' => [ - 'numeric' => 'El camp :attribute ha de ser més petit o igual que :value.', - 'file' => 'El camp :attribute ha de tenir :value kilobytes o menys.', - 'string' => 'El camp :attribute ha de tenir :value caràcters o menys.', - 'array' => 'El camp :attribute ha de tenir :value elements o menys.', + 'numeric' => 'El camp :attribute ha de ser com a màxim :value.', + 'file' => 'El camp :attribute ha de tenir com a màxim :value kilobytes.', + 'string' => 'El camp :attribute ha de tenir com a màxim :value caràcters.', + 'array' => 'El camp :attribute ha de tenir com a màxim :value elements.', ], 'max' => [ - 'numeric' => 'El camp :attribute no pot ser més gran que :max.', - 'file' => 'El camp :attribute no pot tenir més de :max kilobytes.', - 'string' => 'El camp :attribute no pot tenir més de :max caràcters.', - 'array' => 'El camp :attribute no pot tenir més de :max elements.', + 'numeric' => 'El camp :attribute ha de ser com a màxim :max.', + 'file' => 'El camp :attribute ha de tenir com a màxim :max kilobytes.', + 'string' => 'El camp :attribute ha de tenir com a màxim :max caràcters.', + 'array' => 'El camp :attribute ha de tenir com a màxim :max elements.', ], 'mimes' => 'El camp :attribute ha de ser un fitxer del tipus: :values.', 'min' => [ - 'numeric' => 'El camp :attribute no pot ser més petit que :min.', - 'file' => 'El camp :attribute no pot tenir menys de :min kilobytes.', - 'string' => 'El camp :attribute no pot tenir menys de :min caràcters.', - 'array' => 'El camp :attribute no pot tenir menys de :min elements.', + 'numeric' => 'El camp :attribute ha de ser com a mínim :min.', + 'file' => 'El camp :attribute ha de tenir com a mínim :min kilobytes.', + 'string' => 'El camp :attribute ha de tenir com a mínim :min caràcters.', + 'array' => 'El camp :attribute ha de tenir com a mínim :min elements.', ], - 'not_in' => 'El camp :attribute seleccionat no és vàlid.', - 'not_regex' => 'El format del camp :attribute no és vàlid.', - 'numeric' => 'El camp :attribute ha de ser un número.', - 'regex' => 'El format del camp :attribute no és vàlid.', + 'not_in' => 'El camp :attribute no és vàlid.', + 'not_regex' => 'El format :attribute no és vàlid.', + 'numeric' => 'El camp :attribute ha de ser un nombre.', + 'regex' => 'El format :attribute no és vàlid.', 'required' => 'El camp :attribute és obligatori.', 'required_if' => 'El camp :attribute és obligatori quan :other és :value.', - 'required_with' => 'El camp :attribute és obligatori quan hi ha aquest valor: :values.', - 'required_with_all' => 'El camp :attribute és obligatori quan hi ha algun d’aquests valors: :values.', - 'required_without' => 'El camp :attribute és obligatori quan no hi ha aquest valor: :values.', + 'required_with' => 'El camp :attribute és obligatori quan hi ha :values.', + 'required_with_all' => 'El camp :attribute és obligatori quan hi ha tots aquests valors: :values.', + 'required_without' => 'El camp :attribute és obligatori quan no hi ha :values.', 'required_without_all' => 'El camp :attribute és obligatori quan no hi ha cap d’aquests valors: :values.', - 'same' => 'Els camps :attribute i :other han de coincidir.', - 'safe_url' => 'L’enllaç proporcionat podria no ser segur.', + 'same' => 'El camp :attribute i :other han de coincidir.', + 'safe_url' => 'És possible que l’enllaç proporcionat no sigui segur.', 'size' => [ 'numeric' => 'El camp :attribute ha de ser :size.', 'file' => 'El camp :attribute ha de tenir :size kilobytes.', - 'string' => 'El camp :attribute ha de tenir :size caràcters.', - 'array' => 'El camp :attribute ha de contenir :size elements.', + 'string' => 'El camp :attribute ha de tenir :size caràcters', + 'array' => 'El camp :attribute ha de tenir :size elements.', ], - 'string' => 'El camp :attribute ha de ser una cadena.', - 'timezone' => 'El camp :attribute ha de ser una zona vàlida.', - 'totp' => 'El codi que heu proporcionat no és vàlid o ha caducat.', - 'unique' => 'El camp :attribute ja es fa servir.', - 'url' => 'El format del camp :attribute no és vàlid.', - 'uploaded' => 'No s’ha pogut pujar el fitxer. És possible que el servidor no accepti fitxers d’aquesta mida.', + 'string' => 'El camp :attribute ha de ser una cadena de text.', + 'timezone' => 'El camp :attribute ha de ser un fus horari vàlid.', + 'totp' => 'El codi proporcionat no és vàlid o ha caducat.', + 'unique' => 'El camp :attribute ja s’ha utilitzat.', + 'url' => 'El format :attribute no és vàlid.', + 'uploaded' => 'No s’ha pogut pujar el fitxer. És possible que el servidor no admeti fitxers d’aquesta mida.', + + 'zip_file' => 'El :attribute necessita fer referència a un arxiu dins del ZIP.', + 'zip_file_mime' => 'El :attribute necessita fer referència a un arxiu de tipus :validTyes, trobat :foundType.', + 'zip_model_expected' => 'S\'esperava un objecte de dades, però s\'ha trobat ":type".', + 'zip_unique' => 'El :attribute ha de ser únic pel tipus d\'objecte dins del ZIP.', // Custom validation lines 'custom' => [ 'password-confirm' => [ - 'required_with' => 'Cal la confirmació de la contrasenya', + 'required_with' => 'Heu de confirmar la contrasenya.', ], ], diff --git a/lang/cs/activities.php b/lang/cs/activities.php index 18d537f6463..f7e9337d3fa 100644 --- a/lang/cs/activities.php +++ b/lang/cs/activities.php @@ -40,14 +40,14 @@ 'book_sort_notification' => 'Kniha byla úspěšně seřazena', // Bookshelves - 'bookshelf_create' => 'vytvořit knihovnu', - 'bookshelf_create_notification' => 'Knihovna byla úspěšně vytvořena', - 'bookshelf_create_from_book' => 'převést knihu na knihovnu', - 'bookshelf_create_from_book_notification' => 'Kniha byla úspěšně převedena na knihovnu', - 'bookshelf_update' => 'aktualizovat knihovnu', - 'bookshelf_update_notification' => 'Knihovna byla úspěšně aktualizována', - 'bookshelf_delete' => 'odstranit knihovnu', - 'bookshelf_delete_notification' => 'Knihovna byla úspěšně smazána', + 'bookshelf_create' => 'vytvořil polici', + 'bookshelf_create_notification' => 'Police byla úspěšně vytvořena', + 'bookshelf_create_from_book' => 'převést knihu na polici', + 'bookshelf_create_from_book_notification' => 'Kniha byla úspěšně převedena na polici', + 'bookshelf_update' => 'aktualizovat polici', + 'bookshelf_update_notification' => 'Police byla úspěšně aktualizována', + 'bookshelf_delete' => 'odstranil polici', + 'bookshelf_delete_notification' => 'Police byla úspěšně odstraněna', // Revisions 'revision_restore' => 'obnovil revizi', @@ -84,6 +84,14 @@ 'webhook_delete' => 'odstranil/a webhook', 'webhook_delete_notification' => 'Webhook byl úspěšně odstraněn', + // Imports + 'import_create' => 'vytvořil/a import', + 'import_create_notification' => 'Import byl úspěšně nahrán', + 'import_run' => 'aktualizoval/a import', + 'import_run_notification' => 'Obsah byl úspěšně importován', + 'import_delete' => 'odstranil/a import', + 'import_delete_notification' => 'Import byl úspěšně odstraněn', + // Users 'user_create' => 'vytvořil uživatele', 'user_create_notification' => 'Uživatel byl úspěšně vytvořen', @@ -93,11 +101,11 @@ 'user_delete_notification' => 'Uživatel byl úspěšně odstraněn', // API Tokens - 'api_token_create' => 'vytvořil api token', + 'api_token_create' => 'API token byl vytvořen', 'api_token_create_notification' => 'API token úspěšně vytvořen', - 'api_token_update' => 'aktualizoval api token', + 'api_token_update' => 'API token byl aktualizován', 'api_token_update_notification' => 'API token úspěšně aktualizován', - 'api_token_delete' => 'odstranil api token', + 'api_token_delete' => 'API token byl odstraněn', 'api_token_delete_notification' => 'API token úspěšně odstraněn', // Roles @@ -119,6 +127,14 @@ 'comment_update' => 'aktualizoval komentář', 'comment_delete' => 'odstranil komentář', + // Sort Rules + 'sort_rule_create' => 'vytvořil/a pravidlo řazení', + 'sort_rule_create_notification' => 'Pravidlo řazení bylo úspěšně vytvořeno', + 'sort_rule_update' => 'aktualizoval/a pravidlo řazení', + 'sort_rule_update_notification' => 'Pravidlo řazení bylo úspěšně aktualizováno', + 'sort_rule_delete' => 'odstranil/a pravidlo řazení', + 'sort_rule_delete_notification' => 'Pravidlo řazení bylo úspěšně odstraněno', + // Other 'permissions_update' => 'oprávnění upravena', ]; diff --git a/lang/cs/auth.php b/lang/cs/auth.php index 585315ee000..ad225a2fee2 100644 --- a/lang/cs/auth.php +++ b/lang/cs/auth.php @@ -91,7 +91,7 @@ 'mfa_option_totp_title' => 'Mobilní aplikace', 'mfa_option_totp_desc' => 'Pro použití vícefaktorového ověření budete potřebovat mobilní aplikaci, která podporuje TOTP jako např. Google Authenticator, Authy nebo Microsoft Authenticator.', 'mfa_option_backup_codes_title' => 'Záložní kódy', - 'mfa_option_backup_codes_desc' => 'Bezpečně si uložte sadu jednorázových záložních kódů, které můžete použít pro ověření vaší identity.', + 'mfa_option_backup_codes_desc' => 'Vygeneruje sadu jednorázových záložních kódů, které zadáte při přihlášení k ověření své identity. Ujistěte se, že jsou uloženy na bezpečném místě.', 'mfa_gen_confirm_and_enable' => 'Potvrdit a povolit', 'mfa_gen_backup_codes_title' => 'Nastavení záložních kódů', 'mfa_gen_backup_codes_desc' => 'Uložte níže uvedený seznam kódů na bezpečné místo. Při přístupu k systému budete moci použít jeden z kódů jako druhou metodu ověření.', diff --git a/lang/cs/common.php b/lang/cs/common.php index 506f5328130..25c32b0397c 100644 --- a/lang/cs/common.php +++ b/lang/cs/common.php @@ -20,7 +20,7 @@ 'description' => 'Popis', 'role' => 'Role', 'cover_image' => 'Obrázek obálky', - 'cover_image_description' => 'Obrázek by měl být přibližně 440×250px.', + 'cover_image_description' => 'Tento obrázek by měl mít rozměry přibližně 440x250px, ačkoli bude podle potřeby zmenšen a oříznut, aby se vešel do uživatelského rozhraní, takže se skutečné rozměry budou lišit.', // Actions 'actions' => 'Akce', @@ -30,6 +30,8 @@ 'create' => 'Vytvořit', 'update' => 'Aktualizovat', 'edit' => 'Upravit', + 'archive' => 'Archivovat', + 'unarchive' => 'Od-Archivovat', 'sort' => 'Seřadit', 'move' => 'Přesunout', 'copy' => 'Kopírovat', @@ -68,13 +70,13 @@ 'deleted_user' => 'Odstraněný uživatel', 'no_activity' => 'Žádná aktivita k zobrazení', 'no_items' => 'Žádné položky k dispozici', - 'back_to_top' => 'Zpět na začátek', + 'back_to_top' => 'Zpět nahoru', 'skip_to_main_content' => 'Přeskočit na hlavní obsah', 'toggle_details' => 'Přepnout podrobnosti', 'toggle_thumbnails' => 'Přepnout náhledy', 'details' => 'Podrobnosti', - 'grid_view' => 'Zobrazení mřížky', - 'list_view' => 'Zobrazení seznamu', + 'grid_view' => 'Zobrazit mřížku', + 'list_view' => 'Zobrazit seznam', 'default' => 'Výchozí', 'breadcrumb' => 'Drobečková navigace', 'status' => 'Stav', @@ -107,4 +109,7 @@ // Not directly used but available for convenience to users. 'privacy_policy' => 'Zásady ochrany osobních údajů', 'terms_of_service' => 'Podmínky služby', + + // OpenSearch + 'opensearch_description' => 'Vyhledat :appName', ]; diff --git a/lang/cs/editor.php b/lang/cs/editor.php index 5e16ad947c0..2a6b344d549 100644 --- a/lang/cs/editor.php +++ b/lang/cs/editor.php @@ -13,6 +13,7 @@ 'cancel' => 'Zrušit', 'save' => 'Uložit', 'close' => 'Zavřít‏', + 'apply' => 'Použít', 'undo' => 'Zpět', 'redo' => 'Znovu', 'left' => 'Vlevo', @@ -46,7 +47,8 @@ 'strikethrough' => 'Proškrtnuté', 'superscript' => 'horní index', 'subscript' => 'Dolní index', - 'text_color' => 'Barva textu:', + 'text_color' => 'Barva textu', + 'highlight_color' => 'Barva zvýraznění', 'custom_color' => 'Vlastní barva', 'remove_color' => 'Odstranit barvu', 'background_color' => 'Barva pozadí', @@ -81,6 +83,9 @@ 'table_properties' => 'Vlastnosti tabulky', 'table_properties_title' => 'Vlastnosti tabulky', 'delete_table' => 'Smazat tabulku', + 'table_clear_formatting' => 'Vymazat formátování tabulky', + 'resize_to_contents' => 'Změnit velikost podle obsahu', + 'row_header' => 'Záhlaví řádku', 'insert_row_before' => 'Vložit řádek před', 'insert_row_after' => 'Vložit řádek za', 'delete_row' => 'Smazat řádek', @@ -144,6 +149,7 @@ 'url' => 'Adresa URL', 'text_to_display' => 'Text k zobrazení', 'title' => 'Titulek', + 'browse_links' => 'Procházet odkazy', 'open_link' => 'Otevřít odkaz', 'open_link_in' => 'Otevřít odkaz v...', 'open_link_current' => 'Aktuální okno', @@ -160,6 +166,8 @@ 'about' => 'O editoru', 'about_title' => 'O WYSIWYG editoru', 'editor_license' => 'Editor licence a autorská práva', + 'editor_lexical_license' => 'Tento editor je vytvořen jako fork :lexicalLink, který je distribuován pod licencí MIT.', + 'editor_lexical_license_link' => 'Kompletní údaje o licenci naleznete zde.', 'editor_tiny_license' => 'Tento editor je vytvořen pomocí :tinyLink, který je poskytován pod licencí MIT.', 'editor_tiny_license_link' => 'Podrobnosti o autorských právech a licenci TinyMCE naleznete zde.', 'save_continue' => 'Uložit stránku a pokračovat', diff --git a/lang/cs/entities.php b/lang/cs/entities.php index 4471c4b3787..cda6b6c82f5 100644 --- a/lang/cs/entities.php +++ b/lang/cs/entities.php @@ -39,6 +39,34 @@ 'export_pdf' => 'PDF dokument', 'export_text' => 'Textový soubor', 'export_md' => 'Markdown', + 'export_zip' => 'Přenosný archiv ZIP', + 'default_template' => 'Výchozí šablona stránky', + 'default_template_explain' => 'Přiřadit šablonu stránky, která bude použita jako výchozí obsah pro všechny nové stránky v této knize. Mějte na paměti, že šablona bude použita pouze v případě, že tvůrce stránek bude mít přístup k těmto vybraným stránkám šablony.', + 'default_template_select' => 'Vyberte šablonu stránky', + 'import' => 'Importovat', + 'import_validate' => 'Ověřit import', + 'import_desc' => 'Import knih, kapitol a stránek pomocí přenosného archivu ZIP z tohoto nebo jiného webu. Pro pokračování vyberte ZIP soubor. Po nahrání a ověření souboru budete moci v dalším kroku nastavit a potvrdit import.', + 'import_zip_select' => 'Vybrat ZIP soubor k nahrání', + 'import_zip_validation_errors' => 'Při ověřování vybraného souboru ZIP byly zjištěny chyby:', + 'import_pending' => 'Čekající importy', + 'import_pending_none' => 'Žádné importy nebyly spuštěny.', + 'import_continue' => 'Pokračovat v importu', + 'import_continue_desc' => 'Zkontrolujte obsah, který má být importován z nahraného souboru. Pokud je vše v pořádku, spusťte import pro přidání obsahu ZIP archivu na tento web. Nahraný soubor ZIP bude automaticky odstraněn po úspěšném importu.', + 'import_details' => 'Obsah importu', + 'import_run' => 'Spustit import', + 'import_size' => 'Velikost archivu ZIP: :size', + 'import_uploaded_at' => 'Nahráno :relativeTime', + 'import_uploaded_by' => 'Nahrál/a', + 'import_location' => 'Cílové umístění', + 'import_location_desc' => 'Vyberte cílové umístění pro importovaný obsah. K vytvoření potřebujete příslušná oprávnění.', + 'import_delete_confirm' => 'Opravdu chcete tento import odstranit?', + 'import_delete_desc' => 'Potvrzením odstraníte nahraný ZIP soubor. Tento krok nelze vrátit zpět.', + 'import_errors' => 'Chyby importu', + 'import_errors_desc' => 'Při pokusu o import došlo k následujícím chybám:', + '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', // Permissions and restrictions 'permissions' => 'Oprávnění', @@ -80,37 +108,37 @@ 'search_update' => 'Aktualizovat hledání', // Shelves - 'shelf' => 'Knihovna', - 'shelves' => 'Knihovny', - 'x_shelves' => '{0}:count knihoven|{1}:count knihovna|[2,4]:count knihovny|[5,*]:count knihoven', - 'shelves_empty' => 'Nebyly vytvořeny žádné knihovny', - 'shelves_create' => 'Vytvořit novou knihovnu', - 'shelves_popular' => 'Populární knihovny', - 'shelves_new' => 'Nové knihovny', - 'shelves_new_action' => 'Nová Knihovna', - 'shelves_popular_empty' => 'Nejpopulárnější knihovny se objeví zde.', - 'shelves_new_empty' => 'Zde se zobrazí nejnověji vytvořené knihovny.', - 'shelves_save' => 'Uložit knihovnu', - 'shelves_books' => 'Knihy v této knihovně', - 'shelves_add_books' => 'Přidat knihy do knihovny', - 'shelves_drag_books' => 'Přetáhněte knihy níže a přidejte je do této knihovny', - 'shelves_empty_contents' => 'Tato knihovna neobsahuje žádné knihy', - 'shelves_edit_and_assign' => 'Upravit knihovnu a přiřadit knihy', - 'shelves_edit_named' => 'Upravit knihovnu :name', - 'shelves_edit' => 'Upravit knihovnu', - 'shelves_delete' => 'Odstranit knihovnu', - 'shelves_delete_named' => 'Odstranit knihovnu :name', - 'shelves_delete_explain' => "Chystáte se smazat knihovnu ':name'. Knihy v ní obsažené zůstanou zachovány.", - 'shelves_delete_confirmation' => 'Opravdu chcete odstranit tuto knihovnu?', - 'shelves_permissions' => 'Oprávnění knihovny', - 'shelves_permissions_updated' => 'Oprávnění knihovny byla aktualizována', - 'shelves_permissions_active' => 'Oprávnění knihovny byla aktivována', - 'shelves_permissions_cascade_warning' => 'Oprávnění v Knihovnách nejsou automaticky kaskádována do obsažených knih. To proto, že kniha může existovat ve více Knihovnách. Oprávnění však lze zkopírovat do podřízených knih pomocí níže uvedené možnosti.', + 'shelf' => 'Police', + 'shelves' => 'Police', + 'x_shelves' => '{0}:count polic|{1}:count police|[2,4]:count police|[5,*]:count polic', + 'shelves_empty' => 'Nebyly vytvořeny žádné police', + 'shelves_create' => 'Vytvořit novou polici', + 'shelves_popular' => 'Populární police', + 'shelves_new' => 'Nové police', + 'shelves_new_action' => 'Nová Police', + 'shelves_popular_empty' => 'Nejpopulárnější police se objeví zde.', + 'shelves_new_empty' => 'Zde se zobrazí nejnovější police.', + 'shelves_save' => 'Uložit polici', + 'shelves_books' => 'Knihy na této polici', + 'shelves_add_books' => 'Přidat knihy do této police', + 'shelves_drag_books' => 'Přetáhněte knihy níže a přidejte je do této police', + 'shelves_empty_contents' => 'Tato police neobsahuje žádné knihy', + 'shelves_edit_and_assign' => 'Upravit polici a přiřadit knihy', + 'shelves_edit_named' => 'Upravit polici :name', + 'shelves_edit' => 'Upravit polici', + 'shelves_delete' => 'Odstranit polici', + 'shelves_delete_named' => 'Odstranit polici :name', + 'shelves_delete_explain' => "Chystáte se smazat polici ':name'. Knihy v ní obsažené zůstanou zachovány.", + 'shelves_delete_confirmation' => 'Opravdu chcete odstranit tuto polici?', + 'shelves_permissions' => 'Oprávnění police', + 'shelves_permissions_updated' => 'Oprávnění police byla aktualizována', + 'shelves_permissions_active' => 'Oprávnění police byla aktivována', + 'shelves_permissions_cascade_warning' => 'Oprávnění v policiích nejsou automaticky kaskádována do obsažených knih. To proto, že kniha může existovat ve více policích. Oprávnění však lze zkopírovat do podřízených knih pomocí níže uvedené možnosti.', 'shelves_permissions_create' => 'Oprávnění k vytváření Shelf se používají pouze ke kopírování oprávnění do dětských knih pomocí níže uvedené akce. Nekontrolují schopnost vytvářet knihy.', 'shelves_copy_permissions_to_books' => 'Kopírovat oprávnění na knihy', 'shelves_copy_permissions' => 'Kopírovat oprávnění', - 'shelves_copy_permissions_explain' => 'Toto použije aktuální nastavení oprávnění knihovny na všechny knihy v ní obsažené. Před aktivací se ujistěte, že byly uloženy všechny změny oprávnění této knihovny.', - 'shelves_copy_permission_success' => 'Oprávnění knihovny byla zkopírována na :count knih', + 'shelves_copy_permissions_explain' => 'Tímto se použije aktuální nastavení oprávnění police na všechny knihy v ní obsažené. Před aktivací se ujistěte, že byly uloženy všechny změny oprávnění této police.', + 'shelves_copy_permission_success' => '{1}Oprávnění police byla zkopírována na :count knihu|[2,4]Oprávnění police byla zkopírována na :count knihy|[5,*]Oprávnění police byla zkopírována na :count knih', // Books 'book' => 'Kniha', @@ -132,9 +160,6 @@ 'books_edit_named' => 'Upravit knihu :bookName', 'books_form_book_name' => 'Název knihy', 'books_save' => 'Uložit knihu', - 'books_default_template' => 'Výchozí šablona stránky', - 'books_default_template_explain' => 'Přiřadit šablonu stránky, která bude použita jako výchozí obsah pro všechny nové stránky v této knize. Mějte na paměti, že šablona bude použita pouze v případě, že tvůrce stránek bude mít přístup k těmto vybraným stránkám šablony.', - 'books_default_template_select' => 'Vyberte šablonu stránky', 'books_permissions' => 'Oprávnění knihy', 'books_permissions_updated' => 'Oprávnění knihy byla aktualizována', 'books_empty_contents' => 'Pro tuto knihu nebyly vytvořeny žádné stránky ani kapitoly.', @@ -145,7 +170,9 @@ 'books_search_this' => 'Prohledat tuto knihu', 'books_navigation' => 'Navigace knihy', 'books_sort' => 'Seřadit obsah knihy', - 'books_sort_desc' => 'Přesunout kapitoly a stránky v knize pro přeuspořádání obsahu. Mohou být přidány další knihy, které umožňují snadný přesun kapitol a stránek mezi knihami.', + 'books_sort_desc' => 'Pro přeuspořádání obsahu přesuňte kapitoly a stránky v knize. Mohou být přidány další knihy, které umožní snadný přesun kapitol a stránek mezi knihami. Volitelně lze nastavit pravidlo automatického řazení, aby se při změnách automaticky seřadil obsah této knihy.', + 'books_sort_auto_sort' => 'Možnost automatického řazení', + 'books_sort_auto_sort_active' => 'Aktivní automatické řazení: :sortName', 'books_sort_named' => 'Seřadit knihu :bookName', 'books_sort_name' => 'Seřadit podle názvu', 'books_sort_created' => 'Seřadit podle data vytvoření', @@ -171,7 +198,7 @@ // Chapters 'chapter' => 'Kapitola', 'chapters' => 'Kapitoly', - 'x_chapters' => '{0}:count Kapitol|{1}:count Kapitola|[2,4]:count Kapitoly|[5,*]:count Kapitol', + 'x_chapters' => '{0}:count kapitol|{1}:count kapitola|[2,4]:count kapitoly|[5,*]:count kapitol', 'chapters_popular' => 'Populární kapitoly', 'chapters_new' => 'Nová kapitola', 'chapters_create' => 'Vytvořit novou kapitolu', @@ -196,7 +223,7 @@ // Pages 'page' => 'Stránka', 'pages' => 'Stránky', - 'x_pages' => '{0}:count Stran|{1}:count Strana|[2,4]:count Strany|[5,*]:count Stran', + 'x_pages' => '{0}:count stran|{1}:count strana|[2,4]:count strany|[5,*]:count stran', 'pages_popular' => 'Populární stránky', 'pages_new' => 'Nová stránka', 'pages_attachments' => 'Přílohy', @@ -207,7 +234,7 @@ 'pages_delete_draft' => 'Odstranit koncept stránky', 'pages_delete_success' => 'Stránka odstraněna', 'pages_delete_draft_success' => 'Koncept stránky odstraněn', - 'pages_delete_warning_template' => 'Tato stránka je aktivní výchozí šablona. Tyto knihy již nebudou mít výchozí šablonu stránky přiřazenou po odstranění této stránky.', + 'pages_delete_warning_template' => 'Tato stránka je aktivní výchozí šablona pro nějakou knihu či kapitolu. Tyto knihy nebo kapitoly již nebudou mít výchozí šablonu stránky přiřazenou po odstranění této stránky.', 'pages_delete_confirm' => 'Opravdu chcete odstranit tuto stránku?', 'pages_delete_draft_confirm' => 'Opravdu chcete odstranit tento koncept stránky?', 'pages_editing_named' => 'Úpravy stránky :pageName', @@ -221,9 +248,11 @@ 'pages_edit_delete_draft_confirm' => 'Jste si jisti, že chcete odstranit změny vašich konceptů? Všechny vaše změny, od posledního úplného uložení, budou ztraceny a editor bude aktualizován s nejnovějším stavem nekonceptu stránky.', 'pages_edit_discard_draft' => 'Zahodit koncept', 'pages_edit_switch_to_markdown' => 'Přepnout na Markdown Editor', - 'pages_edit_switch_to_markdown_clean' => '(Vytvořený obsah)', - 'pages_edit_switch_to_markdown_stable' => '(Stabilní obsah)', + 'pages_edit_switch_to_markdown_clean' => '(Prostý zápis)', + 'pages_edit_switch_to_markdown_stable' => '(Formátovaný zápis)', 'pages_edit_switch_to_wysiwyg' => 'Přepnout na WYSIWYG Editor', + 'pages_edit_switch_to_new_wysiwyg' => 'Přepnout na nový WYSIWYG', + 'pages_edit_switch_to_new_wysiwyg_desc' => '(V beta testování)', 'pages_edit_set_changelog' => 'Nastavit protokol změn', 'pages_edit_enter_changelog_desc' => 'Zadejte stručný popis změn, které jste provedli', 'pages_edit_enter_changelog' => 'Zadejte protokol změn', @@ -243,6 +272,7 @@ 'pages_md_insert_drawing' => 'Vložit kresbu', 'pages_md_show_preview' => 'Zobrazit náhled', 'pages_md_sync_scroll' => 'Synchronizovat náhled', + 'pages_md_plain_editor' => 'Upravovat prostý text', 'pages_drawing_unsaved' => 'Nalezen neuložený výkres', 'pages_drawing_unsaved_confirm' => 'Byly nalezeny neuložené kresby z předchozí neúspěšné pokusu o uložení kresby. Chcete je obnovit a pokračovat v úpravě této neuložené kresby?', 'pages_not_in_chapter' => 'Stránka není v kapitole', @@ -287,7 +317,7 @@ 'pages_draft_edited_notification' => 'Tato stránka se od té doby změnila. Je doporučeno aktuální koncept zahodit.', 'pages_draft_page_changed_since_creation' => 'Tato stránka byla aktualizována od vytvoření tohoto konceptu. Doporučuje se zrušit tento koncept nebo se postarat o to, abyste si nepřepsali žádné již zadané změny.', 'pages_draft_edit_active' => [ - 'start_a' => 'Uživatelé začali upravovat tuto stránku (celkem :count)', + 'start_a' => '{1}:count uživatel začal upravovat tuto stránku|[2,4]:count uživatelé začali upravovat tuto stránku|[5,*]:count uživatelů začalo upravovat tuto stránku', 'start_b' => ':userName začal/a upravovat tuto stránku', 'time_a' => 'od doby, kdy byla tato stránky naposledy aktualizována', 'time_b' => 'v posledních minutách (:minCount min.)', @@ -303,7 +333,7 @@ 'page_tags' => 'Štítky stránky', 'chapter_tags' => 'Štítky kapitoly', 'book_tags' => 'Štítky knihy', - 'shelf_tags' => 'Štítky knihovny', + 'shelf_tags' => 'Štítky police', 'tag' => 'Štítek', 'tags' => 'Štítky', 'tags_index_desc' => 'Tagy mohou být použity pro obsah v rámci systému pro pružnou formu kategorizace. Tagy mohou mít klíč i hodnotu, přičemž hodnota je nepovinná. Po aplikaci může být obsah dotazován pomocí názvu a hodnoty štítku.', @@ -316,12 +346,12 @@ 'tags_assigned_pages' => 'Přiřazeno ke stránkám', 'tags_assigned_chapters' => 'Přiřazeno ke kapitolám', 'tags_assigned_books' => 'Přiřazeno ke knihám', - 'tags_assigned_shelves' => 'Přiřazeno ke knihovnám', - 'tags_x_unique_values' => ':count jedinečných hodnot', + 'tags_assigned_shelves' => 'Přiřazeno k policím', + 'tags_x_unique_values' => '{1}:count jedinečná hodnota|[2,4]:count jedinečné hodnoty|[5,*]:count jedinečných hodnot', 'tags_all_values' => 'Všechny hodnoty', 'tags_view_tags' => 'Zobrazit štítky', 'tags_view_existing_tags' => 'Zobrazit existující štítky', - 'tags_list_empty_hint' => 'Štítky mohou být přiřazeny pomocí postranního panelu editoru stránky nebo při úpravách podrobností knihy, kapitoly nebo knihovny.', + 'tags_list_empty_hint' => 'Štítky mohou být přiřazeny pomocí postranního panelu editoru stránky nebo při úpravách podrobností knihy, kapitoly nebo police.', 'attachments' => 'Přílohy', 'attachments_explain' => 'Nahrajte soubory nebo připojte odkazy, které se zobrazí na stránce. Budou k nalezení v postranní liště.', 'attachments_explain_instant_save' => 'Změny zde provedené se okamžitě ukládají.', @@ -367,8 +397,11 @@ 'comment' => 'Komentář', 'comments' => 'Komentáře', 'comment_add' => 'Přidat komentář', + 'comment_none' => 'Žádné komentáře k zobrazení', 'comment_placeholder' => 'Zde zadejte komentář', - 'comment_count' => '{0} Bez komentářů|{1} 1 komentář|[2,4] :count komentáře|[5,*] :count komentářů', + 'comment_thread_count' => '{0}:count vláken komentářů|{1}:count vlákno komentářů|[2,4]:count vlákna komentářů|[5,*]:count vláken komentářů', + 'comment_archived_count' => '[0,1]:count archivováno|[2,4]:count archivována|[5,*]:count archivováno', + 'comment_archived_threads' => 'Archivovaná vlákna', 'comment_save' => 'Uložit komentář', 'comment_new' => 'Nový komentář', 'comment_created' => 'komentováno :createDiff', @@ -377,8 +410,14 @@ 'comment_deleted_success' => 'Komentář odstraněn', 'comment_created_success' => 'Komentář přidán', 'comment_updated_success' => 'Komentář aktualizován', + 'comment_archive_success' => 'Komentář archivován', + 'comment_unarchive_success' => 'Komentář od-archivován', + 'comment_view' => 'Zobrazit komentář', + 'comment_jump_to_thread' => 'Přejít na vlákno', 'comment_delete_confirm' => 'Opravdu chcete odstranit tento komentář?', 'comment_in_reply_to' => 'Odpověď na :commentId', + 'comment_reference' => 'Odkaz', + 'comment_reference_outdated' => '(Zastaralý)', 'comment_editor_explain' => 'Zde jsou komentáře, které zůstaly na této stránce. Komentáře lze přidat a spravovat při prohlížení uložené stránky.', // Revision @@ -395,9 +434,9 @@ 'copy_consider_access' => 'Po změně umístění, vlastníka nebo oprávnění může dojít k tomu, že obsah může být přístupný těm, kteří přístup dříve něměli.', // Conversions - 'convert_to_shelf' => 'Převést na knihovnu', - 'convert_to_shelf_contents_desc' => 'Tuto knihu můžete převést na novou knihovnu se stejným obsahem. Kapitoly obsažené v této knize budou převedeny na nové knihy. Pokud tato kniha obsahuje jakékoli stránky, které nejsou uvedeny v kapitole, Tato kniha bude přejmenována a bude obsahovat tyto stránky a tato kniha se stane součástí nové knihovny.', - 'convert_to_shelf_permissions_desc' => 'Veškerá oprávnění nastavená v této knize budou zkopírována do nové knihovny a do všech nových podřazených knih, které nemají vlastní oprávnění. Všimněte si, že oprávnění na regálech neobsahují automatickou kaskádu na obsah, jak to dělají pro knihy.', + 'convert_to_shelf' => 'Převést na polici', + 'convert_to_shelf_contents_desc' => 'Tuto knihu můžete převést na novou polici se stejným obsahem. Kapitoly obsažené v této knize budou převedeny na nové knihy. Pokud tato kniha obsahuje jakékoli stránky, které nejsou uvedeny v kapitole, bude tato kniha přejmenována a bude obsahovat tyto stránky a stane se součástí nové police.', + 'convert_to_shelf_permissions_desc' => 'Veškerá oprávnění nastavená v této knize budou zkopírována do nové police a do všech nových podřazených knih, které nemají vlastní oprávnění. Všimněte si, že oprávnění na policích neobsahují automatickou kaskádu na obsah, jako je tomu u knih.', 'convert_book' => 'Převést knihu', 'convert_book_confirm' => 'Opravdu chcete převést tuto knihu?', 'convert_undo_warning' => 'To nelze tak snadno vrátit zpět.', diff --git a/lang/cs/errors.php b/lang/cs/errors.php index 5d2cab6908a..b1eeb54e015 100644 --- a/lang/cs/errors.php +++ b/lang/cs/errors.php @@ -9,7 +9,8 @@ 'permissionJson' => 'Nemáte povolení k provedení požadované akce.', // Auth - 'error_user_exists_different_creds' => 'Uživatel s emailem :email již existuje ale s jinými přihlašovacími údaji.', + 'error_user_exists_different_creds' => 'Uživatel s emailem :email již existuje, ale s jinými přihlašovacími údaji.', + 'auth_pre_register_theme_prevention' => 'Zadané údaje nedovolují zaregistrovat uživatelský účet', 'email_already_confirmed' => 'Emailová adresa již byla potvrzena. Zkuste se přihlásit.', 'email_confirmation_invalid' => 'Tento potvrzovací odkaz již neplatí nebo už byl použit. Zkuste prosím registraci znovu.', 'email_confirmation_expired' => 'Tento potvrzovací odkaz již neplatí, byl Vám odeslán nový potvrzovací e-mail.', @@ -23,7 +24,6 @@ 'saml_invalid_response_id' => 'Požadavek z externího ověřovacího systému nebyl rozpoznám procesem, který tato aplikace spustila. Tento problém může způsobit stisknutí tlačítka Zpět po přihlášení.', 'saml_fail_authed' => 'Přihlášení pomocí :system selhalo, systém neposkytl úspěšnou autorizaci', 'oidc_already_logged_in' => 'Již jste přihlášeni', - 'oidc_user_not_registered' => 'Uživatel :name není registrován a automatická registrace je zakázána', 'oidc_no_email_address' => 'Nelze najít e-mailovou adresu pro tohoto uživatele v datech poskytnutých externím přihlašovacím systémem', 'oidc_fail_authed' => 'Přihlášení pomocí :system selhalo, systém neposkytl úspěšnou autorizaci', 'social_no_action_defined' => 'Nebyla zvolena žádá akce', @@ -37,6 +37,7 @@ 'social_driver_not_found' => 'Doplněk pro tohoto správce identity nebyl nalezen.', 'social_driver_not_configured' => 'Nastavení vašeho účtu na :socialAccount není správné. :socialAccount musí mít vaše svolení pro naší aplikaci vás přihlásit.', 'invite_token_expired' => 'Odkaz v pozvánce již bohužel vypršel. Namísto toho ale můžete zkusit resetovat heslo do Vašeho účtu.', + 'login_user_not_found' => 'Uživatele pro tuto akci se nepodařilo najít.', // System 'path_not_writable' => 'Nelze zapisovat na cestu k souboru :filePath. Zajistěte aby se dalo nahrávat na server.', @@ -66,7 +67,7 @@ // Entities 'entity_not_found' => 'Prvek nenalezen', - 'bookshelf_not_found' => 'Knihovna nenalezena', + 'bookshelf_not_found' => 'Police nenalezena', 'book_not_found' => 'Kniha nenalezena', 'page_not_found' => 'Stránka nenalezena', 'chapter_not_found' => 'Kapitola nenalezena', @@ -77,6 +78,7 @@ // Users 'users_cannot_delete_only_admin' => 'Nemůžete odstranit posledního administrátora', 'users_cannot_delete_guest' => 'Uživatele Host není možno odstranit', + 'users_could_not_send_invite' => 'Nebylo možné vytvořit uživatele, protože se nepodařilo odeslat email s pozvánkou', // Roles 'role_cannot_be_edited' => 'Tuto roli nelze editovat', @@ -103,6 +105,18 @@ 'app_down' => ':appName je momentálně vypnutá', 'back_soon' => 'Brzy bude opět v provozu.', + // Import + 'import_zip_cant_read' => 'Nelze načíst ZIP soubor.', + 'import_zip_cant_decode_data' => 'Nelze najít a dekódovat data.json v archivu ZIP.', + 'import_zip_no_data' => 'ZIP archiv neobsahuje knihy, kapitoly nebo stránky.', + 'import_validation_failed' => 'Importování ZIP selhalo s chybami:', + 'import_zip_failed_notification' => 'Nepodařilo se naimportovat ZIP soubor.', + 'import_perms_books' => 'Chybí vám požadovaná oprávnění k vytvoření knih.', + 'import_perms_chapters' => 'Chybí vám požadovaná oprávnění k vytvoření kapitol.', + 'import_perms_pages' => 'Chybí vám požadovaná oprávnění k vytvoření stránek.', + 'import_perms_images' => 'Chybí vám požadovaná oprávnění k vytvoření obrázků.', + 'import_perms_attachments' => 'Chybí vám požadovaná oprávnění k vytvoření příloh.', + // API errors 'api_no_authorization_found' => 'V požadavku nebyl nalezen žádný autorizační token', 'api_bad_authorization_format' => 'V požadavku byl nalezen autorizační token, ale jeho formát se zdá být chybný', diff --git a/lang/cs/notifications.php b/lang/cs/notifications.php index b16eb34291e..caf75f17951 100644 --- a/lang/cs/notifications.php +++ b/lang/cs/notifications.php @@ -5,23 +5,23 @@ return [ 'new_comment_subject' => 'Nový komentář na stránce: :pageName', - 'new_comment_intro' => 'Uživatel okomentoval stránku v :appName:', - 'new_page_subject' => 'New page: :pageName', - 'new_page_intro' => 'Nová stránka byla vytvořena v :appName:', + 'new_comment_intro' => 'Uživatel/ka okomentoval/a stránku v :appName:', + 'new_page_subject' => 'Nová stránka: :pageName', + 'new_page_intro' => 'V :appName byla vytvořena nová stránka:', 'updated_page_subject' => 'Aktualizovaná stránka: :pageName', - 'updated_page_intro' => 'Stránka byla aktualizována v :appName:', - 'updated_page_debounce' => 'Aby se zabránilo hromadnému zasílání upozornění, na nějakou dobu nebudete posílat oznámení o dalších úpravách této stránky stejným editorem.', + 'updated_page_intro' => 'V :appName byla aktualizována stránka:', + 'updated_page_debounce' => 'Po nějakou dobu neobdržíte další oznámení o aktualizaci této stránky stejným editorem, aby se omezil počet stejných zpráv.', 'detail_page_name' => 'Název stránky:', - 'detail_page_path' => 'Cesta ke stránce:', - 'detail_commenter' => 'Komentoval:', + 'detail_page_path' => 'Umístění:', + 'detail_commenter' => 'Komentoval/a:', 'detail_comment' => 'Komentář:', - 'detail_created_by' => 'Vytvořil:', - 'detail_updated_by' => 'Aktualizoval(a):', + 'detail_created_by' => 'Vytvořil/a:', + 'detail_updated_by' => 'Aktualizoval/a:', 'action_view_comment' => 'Zobrazit komentář', 'action_view_page' => 'Zobrazit stránku', - 'footer_reason' => 'Toto oznámení vám bylo zasláno, protože :link pokrývá tento typ aktivity pro tuto položku.', + 'footer_reason' => 'Tato zpráva Vám byla doručena na základě Vašeho :link.', 'footer_reason_link' => 'nastavení upozornění', ]; diff --git a/lang/cs/preferences.php b/lang/cs/preferences.php index 295fb5ca1a6..ec7afaf276b 100644 --- a/lang/cs/preferences.php +++ b/lang/cs/preferences.php @@ -20,14 +20,14 @@ 'shortcuts_overview_desc' => 'Správa klávesových zkratek, které můžete použít k navigaci systémového uživatelského rozhraní.', 'notifications' => 'Nastavení upozornění', - 'notifications_desc' => 'Ovládat e-mailová oznámení, která dostáváte při provádění určité aktivity v systému.', + 'notifications_desc' => 'Nastavte si e-mailová oznámení, která dostanete při provedení určitých akcí v systému.', 'notifications_opt_own_page_changes' => 'Upozornit na změny stránek u kterých jsem vlastníkem', 'notifications_opt_own_page_comments' => 'Upozornit na komentáře na stránkách, které vlastním', 'notifications_opt_comment_replies' => 'Upozornit na odpovědi na mé komentáře', - 'notifications_save' => 'Uložit preference', + 'notifications_save' => 'Uložit nastavení', 'notifications_update_success' => 'Nastavení oznámení byla aktualizována!', 'notifications_watched' => 'Sledované a ignorované položky', - 'notifications_watched_desc' => ' Níže jsou položky, které mají vlastní nastavení hodinek. Chcete-li aktualizovat vaše předvolby, podívejte se na položku a pak najděte možnosti hodinek v postranním panelu.', + 'notifications_watched_desc' => 'Níže jsou položky, které mají vlastní nastavení sledování. Chcete-li aktualizovat vaše předvolby, zobrazte položku a pak upravte možnosti sledování v postranním panelu.', 'auth' => 'Přístup a zabezpečení', 'auth_change_password' => 'Změnit heslo', diff --git a/lang/cs/settings.php b/lang/cs/settings.php index 9542debe79a..e72d4b403ad 100644 --- a/lang/cs/settings.php +++ b/lang/cs/settings.php @@ -54,7 +54,7 @@ 'app_color' => 'Hlavní barva', 'link_color' => 'Výchozí barva odkazu', 'content_colors_desc' => 'Nastaví barvy pro všechny prvky v organizační struktuře stránky. Pro lepší čitelnost doporučujeme zvolit barvy s podobným jasem, jakou mají výchozí barvy.', - 'bookshelf_color' => 'Barva knihovny', + 'bookshelf_color' => 'Barva police', 'book_color' => 'Barva knihy', 'chapter_color' => 'Barva kapitoly', 'page_color' => 'Barva stránky', @@ -74,6 +74,36 @@ 'reg_confirm_restrict_domain_desc' => 'Zadejte seznam e-mailových domén oddělených čárkami, na které chcete registraci omezit. Registrujícímu se uživateli bude zaslán e-mail, aby ověřil svoji e-mailovou adresu před tím, než mu bude přístup do aplikace povolen.
Upozorňujeme, že po úspěšné registraci může uživatel svoji e-mailovou adresu změnit.', 'reg_confirm_restrict_domain_placeholder' => 'Žádná omezení nebyla nastavena', + // Sorting Settings + 'sorting' => 'Řazení', + 'sorting_book_default' => 'Výchozí řazení knih', + 'sorting_book_default_desc' => 'Vybere výchozí pravidlo řazení pro nové knihy. Řazení neovlivní existující knihy a může být upraveno u konkrétní knihy.', + 'sorting_rules' => 'Pravidla řazení', + 'sorting_rules_desc' => 'Toto jsou předem definovaná pravidla řazení, která mohou být použita na webu.', + 'sort_rule_assigned_to_x_books' => 'Přiřazeno k :count knize|Přiřazeno :count knihám', + 'sort_rule_create' => 'Vytvořit pravidlo řazení', + 'sort_rule_edit' => 'Upravit pravidlo řazení', + 'sort_rule_delete' => 'Odstranit pravidlo řazení', + 'sort_rule_delete_desc' => 'Odstraní toto pravidlo řazení z webu. Knihy, které jej používají, se vrátí k ručnímu řazení.', + 'sort_rule_delete_warn_books' => 'Toto pravidlo řazení se v současné době používá na :count knihách. Opravdu ho chcete smazat?', + 'sort_rule_delete_warn_default' => 'Toto pravidlo řazení je v současné době používáno jako výchozí. Opravdu ho chcete odstranit?', + 'sort_rule_details' => 'Podrobnosti pravidla pro řazení', + 'sort_rule_details_desc' => 'Nastavte název pro toto pravidlo, který se zobrazí v seznamu při výběru řazení.', + 'sort_rule_operations' => 'Možnosti řazení', + 'sort_rule_operations_desc' => 'Přesunem ze seznamu dostupných operací nastavte akce řazení, které mají být provedeny. Operace se použijí v pořadí od shora dolů. Veškeré změny zde provedené budou při uložení aplikovány na všechny přiřazené knihy.', + 'sort_rule_available_operations' => 'Dostupné operace', + 'sort_rule_available_operations_empty' => 'Žádné zbývající operace', + 'sort_rule_configured_operations' => 'Konfigurované operace', + 'sort_rule_configured_operations_empty' => 'Přetáhněte/přidejte operace ze seznamu "Dostupné operace"', + 'sort_rule_op_asc' => '(Vzest)', + 'sort_rule_op_desc' => '(Sest)', + 'sort_rule_op_name' => 'Název - abecední', + 'sort_rule_op_name_numeric' => 'Název - číselné', + 'sort_rule_op_created_date' => 'Datum vytvoření', + 'sort_rule_op_updated_date' => 'Datum aktualizace', + 'sort_rule_op_chapters_first' => 'Kapitoly jako první', + 'sort_rule_op_chapters_last' => 'Kapitoly jako poslední', + // Maintenance settings 'maint' => 'Údržba', 'maint_image_cleanup' => 'Pročistění obrázků', @@ -90,7 +120,7 @@ 'maint_send_test_email_mail_subject' => 'Testovací e-mail', 'maint_send_test_email_mail_greeting' => 'Zdá se, že posílání e-mailů funguje!', 'maint_send_test_email_mail_text' => 'Gratulujeme! Protože jste dostali tento e-mail, zdá se, že nastavení e-mailů je v pořádku.', - 'maint_recycle_bin_desc' => 'Odstraněné knihovny, knihy, kapitoly a stránky se přesouvají do Koše, aby je bylo možné obnovit nebo trvale smazat. Starší položky v koši mohou být po čase automaticky odstraněny v závislosti na konfiguraci systému.', + 'maint_recycle_bin_desc' => 'Odstraněné police, knihy, kapitoly a stránky se přesouvají do Koše, aby je bylo možné obnovit nebo trvale smazat. Starší položky v koši mohou být po čase automaticky odstraněny v závislosti na konfiguraci systému.', 'maint_recycle_bin_open' => 'Otevřít Koš', 'maint_regen_references' => 'Přegenerovat odkazy', 'maint_regen_references_desc' => 'Tato akce obnoví referenční index křížových položek v rámci databáze. Toto je obvykle zpracováno automaticky, ale tato akce může být užitečná pro indexování starého obsahu nebo obsahu přidaného neoficiálními metodami.', @@ -109,7 +139,7 @@ 'recycle_bin_contents_empty' => 'Koš je nyní prázdný', 'recycle_bin_empty' => 'Vysypat Koš', 'recycle_bin_empty_confirm' => 'Toto trvale odstraní všechny položky v Koši včetně obsahu vloženého v každé položce. Opravdu chcete vysypat Koš?', - 'recycle_bin_destroy_confirm' => 'Tato akce trvale odstraní ze systému tuto položku spolu s veškerým vloženým obsahem a tento obsah nebudete moci obnovit. Opravdu chcete tuto položku trvale odstranit?', + 'recycle_bin_destroy_confirm' => 'Tato akce trvale odstraní tuto položku ze systému spolu se všemi položkami uvedenými níže a nebudete je moci obnovit. Opravdu chcete tuto položku trvale odstranit?', 'recycle_bin_destroy_list' => 'Položky k trvalému odstranění', 'recycle_bin_restore_list' => 'Položky k obnovení', 'recycle_bin_restore_confirm' => 'Tato akce obnoví odstraněnou položku včetně veškerého vloženého obsahu do původního umístění. Pokud bylo původní umístění od té doby odstraněno a nyní je v Koši, bude také nutné obnovit nadřazenou položku.', @@ -162,6 +192,7 @@ 'role_access_api' => 'Přístup k systémovému API', 'role_manage_settings' => 'Správa nastavení aplikace', 'role_export_content' => 'Exportovat obsah', + 'role_import_content' => 'Importovat obsah', 'role_editor_change' => 'Změnit editor stránek', 'role_notifications' => 'Přijímat a spravovat oznámení', 'role_asset' => 'Obsahová oprávnění', @@ -276,6 +307,14 @@ 'webhooks_last_errored' => 'Poslední chyba:', 'webhooks_last_error_message' => 'Poslední chybová zpráva', + // Licensing + 'licenses' => 'Licence', + 'licenses_desc' => 'Na této stránce naleznete kromě informací o projektech a knihovnách, které se v rámci BookStacku používají, také informace o licencích pro BookStack. Mnoho uvedených projektů lze používat pouze ve vývojovém kontextu.', + 'licenses_bookstack' => 'BookStack licence', + 'licenses_php' => 'Licence PHP knihoven', + 'licenses_js' => 'Licence JavaScript knihoven', + 'licenses_other' => 'Ostatní licence', + 'license_details' => 'Podrobnosti o licenci', //! If editing translations files directly please ignore this in all //! languages apart from en. Content will be auto-copied from en. @@ -287,6 +326,7 @@ 'bs' => 'Bosanski', 'ca' => 'Català', 'cs' => 'Česky', + 'cy' => 'Cymraeg', 'da' => 'Dansk', 'de' => 'Deutsch (Sie)', 'de_informal' => 'Deutsch (Du)', @@ -308,6 +348,7 @@ 'lt' => 'Lietuvių Kalba', 'lv' => 'Latviešu Valoda', 'nb' => 'Norsk (Bokmål)', + 'ne' => 'नेपाली', 'nn' => 'Nynorsk', 'nl' => 'Nederlands', 'pl' => 'Polski', diff --git a/lang/cs/validation.php b/lang/cs/validation.php index 83929cc887b..b05d94625af 100644 --- a/lang/cs/validation.php +++ b/lang/cs/validation.php @@ -105,6 +105,11 @@ 'url' => 'Formát :attribute je neplatný.', 'uploaded' => 'Nahrávání :attribute se nezdařilo.', + 'zip_file' => ':attribute musí odkazovat na soubor v archivu ZIP.', + 'zip_file_mime' => ':attribute musí odkazovat na soubor typu :validTypes, nalezen :foundType.', + 'zip_model_expected' => 'Očekáván datový objekt, ale nalezen „:type“.', + 'zip_unique' => ':attribute musí být jedinečný pro typ objektu v archivu ZIP.', + // Custom validation lines 'custom' => [ 'password-confirm' => [ diff --git a/lang/cy/activities.php b/lang/cy/activities.php index b3301d757b8..2459f2357f5 100644 --- a/lang/cy/activities.php +++ b/lang/cy/activities.php @@ -30,8 +30,8 @@ // Books 'book_create' => 'llyfr wedi creu', 'book_create_notification' => 'Llyfr wedi\'i creu\'n llwyddiannus', - 'book_create_from_chapter' => 'converted chapter to book', - 'book_create_from_chapter_notification' => 'Chapter successfully converted to a book', + 'book_create_from_chapter' => 'trosodd bennod i lyfr', + 'book_create_from_chapter_notification' => 'Pennod wedi\'i trosi\'n llwyddiannus i lyfr', 'book_update' => 'llyfr wedi diweddaru', 'book_update_notification' => 'Llyfr wedi\'i diweddaru\'n llwyddiannus', 'book_delete' => 'llyfr wedi\'i dileu', @@ -42,39 +42,39 @@ // Bookshelves 'bookshelf_create' => 'creodd silff', 'bookshelf_create_notification' => 'Silff wedi\'i chreu\'n llwyddiannus', - 'bookshelf_create_from_book' => 'converted book to shelf', - 'bookshelf_create_from_book_notification' => 'Book successfully converted to a shelf', + 'bookshelf_create_from_book' => 'trosodd lyfr i silff', + 'bookshelf_create_from_book_notification' => 'Llyfr wedi\'i trosi\'n llwyddiannus i silff', 'bookshelf_update' => 'diweddarodd silff', 'bookshelf_update_notification' => 'Silff wedi\'i diweddaru\'n llwyddiannus', 'bookshelf_delete' => 'dileodd silff', 'bookshelf_delete_notification' => 'Silff wedi\'i dileu\'n llwyddiannus', // Revisions - 'revision_restore' => 'restored revision', - 'revision_delete' => 'deleted revision', - 'revision_delete_notification' => 'Revision successfully deleted', + 'revision_restore' => 'adferodd y diwygiad', + 'revision_delete' => 'dileuodd y diwygiad', + 'revision_delete_notification' => 'Diwygiad wedi\'i dileu\'n llwyddiannus', // Favourites 'favourite_add_notification' => 'Mae ":name" wedi\'i ychwanegu at eich ffefrynnau', 'favourite_remove_notification' => 'Mae ":name" wedi\'i tynnu o\'ch ffefrynnau', // Watching - 'watch_update_level_notification' => 'Watch preferences successfully updated', + 'watch_update_level_notification' => 'Dewisiadau gwylio wedi’u diweddaru\'n llwyddiannus', // Auth 'auth_login' => 'wedi\'u mewngofnodi', - 'auth_register' => 'registered as new user', - 'auth_password_reset_request' => 'requested user password reset', - 'auth_password_reset_update' => 'reset user password', - 'mfa_setup_method' => 'configured MFA method', + 'auth_register' => 'wedi\'i cofrestru\'n ddefnyddiwr newydd', + 'auth_password_reset_request' => 'wedi ceisio ailosod gair pass defnyddiwr', + 'auth_password_reset_update' => 'ailosododd air pass defnyddiwr', + 'mfa_setup_method' => 'Dull Dilysu Aml-ffactor wedi’i ffurfweddu', 'mfa_setup_method_notification' => 'Dull aml-ffactor wedi\'i ffurfweddu\'n llwyddiannus', - 'mfa_remove_method' => 'removed MFA method', + 'mfa_remove_method' => 'Dull Dilysu Aml-ffactor wedi\'i ddileu', 'mfa_remove_method_notification' => 'Llwyddwyd i ddileu dull aml-ffactor', // Settings - 'settings_update' => 'updated settings', - 'settings_update_notification' => 'Settings successfully updated', - 'maintenance_action_run' => 'ran maintenance action', + 'settings_update' => 'diweddarodd osodiadau', + 'settings_update_notification' => 'Gosodiadau wedi\'i diweddaru\'n llwyddiannus', + 'maintenance_action_run' => 'rhedeg gweithred cynnal a chadw', // Webhooks 'webhook_create' => 'webhook wedi creu', @@ -84,6 +84,14 @@ 'webhook_delete' => 'webhook wedi\'i dileu', 'webhook_delete_notification' => 'Webhook wedi\'i dileu\'n llwyddiannus', + // Imports + 'import_create' => 'creodd fewnforyn', + 'import_create_notification' => 'Mewnforyn wedi\'i lwytho i fyny\'n llwyddiannus', + 'import_run' => 'diweddarodd fewnforyn', + 'import_run_notification' => 'Cynnwys wedi\'i fewnforio\'n llwyddiannus', + 'import_delete' => 'dileodd fewnforyn', + 'import_delete_notification' => 'Mewnforyn wedi\'i ddileu\'n llwyddiannus', + // Users 'user_create' => 'creodd ddefnyddiwr', 'user_create_notification' => 'Defnyddiwr wedi\'i greu\'n llwyddiannus', @@ -93,25 +101,25 @@ 'user_delete_notification' => 'Tynnwyd y defnyddiwr yn llwyddiannus', // API Tokens - 'api_token_create' => 'creodd tocyn api', + 'api_token_create' => 'creodd docyn API', 'api_token_create_notification' => 'Tocyn API wedi\'i greu\'n llwyddiannus', - 'api_token_update' => 'diweddarodd docyn api', + 'api_token_update' => 'diweddarodd docyn API', 'api_token_update_notification' => 'Tocyn API wedi\'i ddiweddaru\'n llwyddiannus', - 'api_token_delete' => 'dileodd docyn api', + 'api_token_delete' => 'dileodd docyn API', 'api_token_delete_notification' => 'Tocyn API wedi\'i ddileu\'n llwyddiannus', // Roles - 'role_create' => 'created role', - 'role_create_notification' => 'Role successfully created', - 'role_update' => 'updated role', - 'role_update_notification' => 'Role successfully updated', - 'role_delete' => 'deleted role', - 'role_delete_notification' => 'Role successfully deleted', + 'role_create' => 'creodd rôl', + 'role_create_notification' => 'Rôl wedi\'i creu\'n llwyddiannus', + 'role_update' => 'diweddarodd rôl', + 'role_update_notification' => 'Rôl wedi\'i diweddaru\'n llwyddiannus', + 'role_delete' => 'dileodd rôl', + 'role_delete_notification' => 'Rôl wedi\'i dileu\'n llwyddiannus', // Recycle Bin 'recycle_bin_empty' => 'gwagodd fin ailgylchu', - 'recycle_bin_restore' => 'restored from recycle bin', - 'recycle_bin_destroy' => 'removed from recycle bin', + 'recycle_bin_restore' => 'wedi\'i adfer o\'r bin ailgylchu', + 'recycle_bin_destroy' => 'symudwyd o’r bin ailgylchu', // Comments 'commented_on' => 'gwnaeth sylwadau ar', @@ -119,6 +127,14 @@ 'comment_update' => 'diweddarodd sylw', 'comment_delete' => 'dileodd sylw', + // Sort Rules + 'sort_rule_create' => 'created sort rule', + 'sort_rule_create_notification' => 'Sort rule successfully created', + 'sort_rule_update' => 'updated sort rule', + 'sort_rule_update_notification' => 'Sort rule successfully updated', + 'sort_rule_delete' => 'deleted sort rule', + 'sort_rule_delete_notification' => 'Sort rule successfully deleted', + // Other 'permissions_update' => 'caniatadau wedi\'u diweddaru', ]; diff --git a/lang/cy/auth.php b/lang/cy/auth.php index 94fc0b6b54b..5c4034fcdd4 100644 --- a/lang/cy/auth.php +++ b/lang/cy/auth.php @@ -39,79 +39,79 @@ 'register_success' => 'Diolch am arwyddo! Rydych bellach wedi cofrestru ac wedi mewngofnodi.', // Login auto-initiation - 'auto_init_starting' => 'Attempting Login', - 'auto_init_starting_desc' => 'We\'re contacting your authentication system to start the login process. If there\'s no progress after 5 seconds you can try clicking the link below.', - 'auto_init_start_link' => 'Proceed with authentication', + 'auto_init_starting' => 'Wrthi\'n ceisio mewngofnodi', + 'auto_init_starting_desc' => 'Rydym yn cysylltu â\'ch system ddilysu i ddechrau\'r broses fewngofnodi. Os nad oes cynnydd ar ôl 5 eiliad, gallwch geisio clicio ar y ddolen isod.', + 'auto_init_start_link' => 'Parhau gyda dilysu', // Password Reset 'reset_password' => 'Ailosod cyfrinair', 'reset_password_send_instructions' => 'Rhowch eich e-bost isod ac anfonir e-bost atoch gyda dolen ailosod cyfrinair.', 'reset_password_send_button' => 'Anfon Dolen Ailosod', - 'reset_password_sent' => 'A password reset link will be sent to :email if that email address is found in the system.', - 'reset_password_success' => 'Your password has been successfully reset.', - 'email_reset_subject' => 'Reset your :appName password', - 'email_reset_text' => 'You are receiving this email because we received a password reset request for your account.', - 'email_reset_not_requested' => 'If you did not request a password reset, no further action is required.', + 'reset_password_sent' => 'Bydd dolen ailosod cyfrinair yn cael ei hanfon at :email os ceir hyd i’r cyfeiriad e-bost hwn yn y system.', + 'reset_password_success' => 'Mae eich cyfrinair wedi\'i ailosod yn llwyddiannus.', + 'email_reset_subject' => 'Ailosod eich gair pass :appName', + 'email_reset_text' => 'Anfonwyd yr e-bost hwn atoch oherwydd ein bod wedi cael cais ailosod cyfrinair ar gyfer eich cyfrif.', + 'email_reset_not_requested' => 'Os nad oeddwch chi\'n ceisio ailosod eich gair pass, does dim byd arall i wneud.', // Email Confirmation 'email_confirm_subject' => 'Cadarnhewch eich e-bost chi a :appName', 'email_confirm_greeting' => 'Diolch am ymuno â :appName!', 'email_confirm_text' => 'Os gwelwch yn dda cadarnhewch eich e-bost chi gan clicio ar y botwm isod:', 'email_confirm_action' => 'Cadarnhau E-bost', - 'email_confirm_send_error' => 'Email confirmation required but the system could not send the email. Contact the admin to ensure email is set up correctly.', - 'email_confirm_success' => 'Your email has been confirmed! You should now be able to login using this email address.', - 'email_confirm_resent' => 'Confirmation email resent, Please check your inbox.', + 'email_confirm_send_error' => 'Mae angen cadarnhad e-bost ond ni allai\'r system anfon yr e-bost. Cysylltwch â\'r gweinyddwr i sicrhau bod yr e-bost wedi\'i osod yn gywir.', + 'email_confirm_success' => 'Mae eich e-bost wedi\'i gadarnhau! Dylech nawr allu mewngofnodi gan ddefnyddio\'r cyfeiriad e-bost hwn.', + 'email_confirm_resent' => 'Ail-anfonwyd cadarnhad e-bost, gwiriwch eich mewnflwch.', 'email_confirm_thanks' => 'Diolch am gadarnhau!', - 'email_confirm_thanks_desc' => 'Please wait a moment while your confirmation is handled. If you are not redirected after 3 seconds press the "Continue" link below to proceed.', + 'email_confirm_thanks_desc' => 'Arhoswch eiliad wrth i’ch cadarnhad gael ei drin. Os na chewch eich ailgyfeirio ar ôl 3 eiliad, pwyswch y ddolen "Parhau" isod i symud ymlaen.', 'email_not_confirmed' => 'Cyfeiriad E-bost heb ei Gadarnhau', 'email_not_confirmed_text' => 'Dyw eich cyfeiriad e-bost chi ddim wedi cael ei gadarnhau eto.', - 'email_not_confirmed_click_link' => 'Please click the link in the email that was sent shortly after you registered.', - 'email_not_confirmed_resend' => 'If you cannot find the email you can re-send the confirmation email by submitting the form below.', - 'email_not_confirmed_resend_button' => 'Resend Confirmation Email', + 'email_not_confirmed_click_link' => 'Cliciwch ar y ddolen yn yr e-bost a anfonwyd yn fuan ar ôl i chi gofrestru.', + 'email_not_confirmed_resend' => 'Os na allwch ddod o hyd i\'r e-bost, gallwch ail-anfon yr e-bost cadarnhad trwy gyflwyno\'r ffurflen isod.', + 'email_not_confirmed_resend_button' => 'Ail-anfon E-bost Cadarnhad', // User Invite - 'user_invite_email_subject' => 'You have been invited to join :appName!', + 'user_invite_email_subject' => 'Rydych chi wedi cael gwahoddiad i ymuno :appName!', 'user_invite_email_greeting' => 'Mae cyfrif wedi cae ei greu i chi ar :appName.', - 'user_invite_email_text' => 'Click the button below to set an account password and gain access:', - 'user_invite_email_action' => 'Set Account Password', + 'user_invite_email_text' => 'Cliciwch ar y botwm isod i osod cyfrinair cyfrif a chael mynediad:', + 'user_invite_email_action' => 'Gosod Cyfrinair Cyfrif', 'user_invite_page_welcome' => 'Croeso i :appName!', - 'user_invite_page_text' => 'To finalise your account and gain access you need to set a password which will be used to log-in to :appName on future visits.', + 'user_invite_page_text' => 'I gwblhau eich cyfrif a chael mynediad mae angen i chi osod cyfrinair a fydd yn cael ei ddefnyddio i fewngofnodi i :appName ar ymweliadau yn y dyfodol.', 'user_invite_page_confirm_button' => 'Cadarnhau cyfrinair', - 'user_invite_success_login' => 'Password set, you should now be able to login using your set password to access :appName!', + 'user_invite_success_login' => 'Cyfrinair wedi’i osod, dylech nawr allu mewngofnodi gan ddefnyddio\'r cyfrinair a osodwyd i gael mynediad i :appName!', // Multi-factor Authentication - 'mfa_setup' => 'Setup Multi-Factor Authentication', - 'mfa_setup_desc' => 'Setup multi-factor authentication as an extra layer of security for your user account.', - 'mfa_setup_configured' => 'Already configured', - 'mfa_setup_reconfigure' => 'Reconfigure', - 'mfa_setup_remove_confirmation' => 'Are you sure you want to remove this multi-factor authentication method?', - 'mfa_setup_action' => 'Setup', - 'mfa_backup_codes_usage_limit_warning' => 'You have less than 5 backup codes remaining, Please generate and store a new set before you run out of codes to prevent being locked out of your account.', + 'mfa_setup' => 'Gosod Dilysu Aml-Ffactor', + 'mfa_setup_desc' => 'Gosod dilysu aml-ffactor fel haen ychwanegol o ddiogelwch ar gyfer eich cyfrif defnyddiwr.', + 'mfa_setup_configured' => 'Wedi\'i ail-ffurfweddu\'n barod', + 'mfa_setup_reconfigure' => 'Ail-ffurfweddu', + 'mfa_setup_remove_confirmation' => 'Ydych chi\'n siŵr eich bod am gael gwared ar y dull dilysu aml-ffactor hwn?', + 'mfa_setup_action' => 'Gosodiad', + 'mfa_backup_codes_usage_limit_warning' => 'Mae gennych lai na 5 cod wrth gefn yn weddill, crëwch a storio cyfres newydd cyn i chi redeg allan o godau i osgoi cael eich cloi allan o\'ch cyfrif.', 'mfa_option_totp_title' => 'Ap Ffôn Symudol', - 'mfa_option_totp_desc' => 'To use multi-factor authentication you\'ll need a mobile application that supports TOTP such as Google Authenticator, Authy or Microsoft Authenticator.', - 'mfa_option_backup_codes_title' => 'Backup Codes', - 'mfa_option_backup_codes_desc' => 'Securely store a set of one-time-use backup codes which you can enter to verify your identity.', - 'mfa_gen_confirm_and_enable' => 'Confirm and Enable', - 'mfa_gen_backup_codes_title' => 'Backup Codes Setup', - 'mfa_gen_backup_codes_desc' => 'Store the below list of codes in a safe place. When accessing the system you\'ll be able to use one of the codes as a second authentication mechanism.', - 'mfa_gen_backup_codes_download' => 'Download Codes', - 'mfa_gen_backup_codes_usage_warning' => 'Each code can only be used once', - 'mfa_gen_totp_title' => 'Mobile App Setup', - 'mfa_gen_totp_desc' => 'To use multi-factor authentication you\'ll need a mobile application that supports TOTP such as Google Authenticator, Authy or Microsoft Authenticator.', - 'mfa_gen_totp_scan' => 'Scan the QR code below using your preferred authentication app to get started.', - 'mfa_gen_totp_verify_setup' => 'Verify Setup', - 'mfa_gen_totp_verify_setup_desc' => 'Verify that all is working by entering a code, generated within your authentication app, in the input box below:', - 'mfa_gen_totp_provide_code_here' => 'Provide your app generated code here', - 'mfa_verify_access' => 'Verify Access', - 'mfa_verify_access_desc' => 'Your user account requires you to confirm your identity via an additional level of verification before you\'re granted access. Verify using one of your configured methods to continue.', - 'mfa_verify_no_methods' => 'No Methods Configured', - 'mfa_verify_no_methods_desc' => 'No multi-factor authentication methods could be found for your account. You\'ll need to set up at least one method before you gain access.', - 'mfa_verify_use_totp' => 'Verify using a mobile app', - 'mfa_verify_use_backup_codes' => 'Verify using a backup code', - 'mfa_verify_backup_code' => 'Backup Code', - 'mfa_verify_backup_code_desc' => 'Enter one of your remaining backup codes below:', - 'mfa_verify_backup_code_enter_here' => 'Enter backup code here', - 'mfa_verify_totp_desc' => 'Enter the code, generated using your mobile app, below:', - 'mfa_setup_login_notification' => 'Multi-factor method configured, Please now login again using the configured method.', + 'mfa_option_totp_desc' => 'I ddefnyddio dilysu aml-ffactor bydd angen dyfais symudol arnoch sy\'n cefnogi TOTP megis Google Authenticator, Authy neu Microsoft Authenticator.', + 'mfa_option_backup_codes_title' => 'Codau wrth Gefn', + 'mfa_option_backup_codes_desc' => 'Mae’n cynhyrchu cyfres o godau wrth gefn un-amser y byddwch chi\'n eu defnyddio i fewngofnodi i wirio pwy ydych chi. Gwnewch yn siŵr eich bod yn storio\'r rhain mewn lle saff a diogel.', + 'mfa_gen_confirm_and_enable' => 'Cadarnhau a Galluogi', + 'mfa_gen_backup_codes_title' => 'Gosodiad Codau wrth Gefn', + 'mfa_gen_backup_codes_desc' => 'Storiwch y rhestr isod o godau mewn lle diogel. Wrth ddefnyddio’r system bydd modd i chi ddefnyddio un o\'r codau fel ail fecanwaith dilysu.', + 'mfa_gen_backup_codes_download' => 'Llwytho Codau i Lawr', + 'mfa_gen_backup_codes_usage_warning' => 'Gellir defnyddio pob cod unwaith yn unig', + 'mfa_gen_totp_title' => 'Gosod Ap Symudol', + 'mfa_gen_totp_desc' => 'I ddefnyddio dilysu aml-ffactor bydd angen dyfais symudol arnoch sy\'n cefnogi TOTP megis Google Authenticator, Authy neu Microsoft Authenticator.', + 'mfa_gen_totp_scan' => 'Sganiwch y cod QR isod gan ddefnyddio\'ch ap dilysu dewisol i ddechrau.', + 'mfa_gen_totp_verify_setup' => 'Gwirio Gosodiad', + 'mfa_gen_totp_verify_setup_desc' => 'Gwiriwch fod popeth yn gweithio trwy roi cod, a gynhyrchwyd gan eich ap dilysu, yn y blwch mewnbwn isod:', + 'mfa_gen_totp_provide_code_here' => 'Rhowch y cod a gynhyrchwyd gan eich ap yma', + 'mfa_verify_access' => 'Gwirio Mynediad', + 'mfa_verify_access_desc' => 'Mae eich cyfrif defnyddiwr yn gofyn i chi gadarnhau pwy ydych chi trwy lefel ychwanegol o ddilysu cyn i chi gael mynediad. Gwiriwch gan ddefnyddio un o\'ch dulliau ffurfweddu i barhau.', + 'mfa_verify_no_methods' => 'Dim Dulliau wedi\'u Ffurfweddu', + 'mfa_verify_no_methods_desc' => 'Ni ellid dod o hyd i unrhyw ddulliau dilysu aml-ffactor ar gyfer eich cyfrif. Bydd angen i chi osod o leiaf un dull cyn i chi gael mynediad.', + 'mfa_verify_use_totp' => 'Gwirio gan ap ffôn', + 'mfa_verify_use_backup_codes' => 'Gwirio gan god wrth gefn', + 'mfa_verify_backup_code' => 'Cod wrth Gefn', + 'mfa_verify_backup_code_desc' => 'Rhowch un o\'ch codau wrth gefn sy\'n weddill isod:', + 'mfa_verify_backup_code_enter_here' => 'Cofnodi cod wrth gefn yma', + 'mfa_verify_totp_desc' => 'Rhowch y cod, a gynhyrchwyd gan ddefnyddio\'ch ap symudol, isod:', + 'mfa_setup_login_notification' => 'Dull aml-ffactor wedi\'i ffurfweddu, nawr mewngofnodwch eto gan ddefnyddio\'r dull wedi\'i ffurfweddu.', ]; diff --git a/lang/cy/common.php b/lang/cy/common.php index e359e8ba361..b42e0d44b19 100644 --- a/lang/cy/common.php +++ b/lang/cy/common.php @@ -12,15 +12,15 @@ 'save' => 'Cadw', 'continue' => 'Parhau', 'select' => 'Dewis', - 'toggle_all' => 'Toggle All', + 'toggle_all' => 'Toglo Popeth', 'more' => 'Mwy', // Form Labels 'name' => 'Enw', 'description' => 'Disgrifiad', - 'role' => 'Role', - 'cover_image' => 'Cover image', - 'cover_image_description' => 'This image should be approx 440x250px.', + 'role' => 'Rôl', + 'cover_image' => 'Delwedd y clawr', + 'cover_image_description' => 'Dylai\'r ddelwedd hon fod oddeutu 440x250px er y bydd wedi\'i graddio a’i thocio i ffitio rhyngwyneb y defnyddiwr mewn gwahanol senarios yn ôl yr angen, felly bydd y dimensiynau arddangos gwirioneddol yn amrywio.', // Actions 'actions' => 'Gweithredoedd', @@ -30,6 +30,8 @@ 'create' => 'Creu', 'update' => 'Diweddaru', 'edit' => 'Golygu', + 'archive' => 'Archive', + 'unarchive' => 'Un-Archive', 'sort' => 'Trefnu', 'move' => 'Symud', 'copy' => 'Copïo', @@ -43,9 +45,9 @@ 'add' => 'Ychwanegu', 'configure' => 'Ffurfweddu', 'manage' => 'Rheoli', - 'fullscreen' => 'Fullscreen', - 'favourite' => 'Favourite', - 'unfavourite' => 'Unfavourite', + 'fullscreen' => 'Sgrin Llawn', + 'favourite' => 'Gwneud Ffefryn', + 'unfavourite' => 'Dadwneud Ffefryn', 'next' => 'Nesa', 'previous' => 'Cynt', 'filter_active' => 'Hidl weithredol:', @@ -56,55 +58,58 @@ // Sort Options 'sort_options' => 'Trefnu\'r opsiynau', - 'sort_direction_toggle' => 'Sort Direction Toggle', + 'sort_direction_toggle' => 'Trefnu Cyfeiriad Togl', 'sort_ascending' => 'Trefnu\'n esgynnol', 'sort_descending' => 'Trefnu\'n ddisgynnol', 'sort_name' => 'Enw', - 'sort_default' => 'Default', - 'sort_created_at' => 'Created Date', - 'sort_updated_at' => 'Updated Date', + 'sort_default' => 'Diofyn', + 'sort_created_at' => 'Dyddiad Creu', + 'sort_updated_at' => 'Dyddiad Diweddaru', // Misc - 'deleted_user' => 'Deleted User', - 'no_activity' => 'No activity to show', + 'deleted_user' => 'Defnyddiwr wedi\'i Dileu', + 'no_activity' => 'Dim actifedd i arddangos', 'no_items' => 'Dim eitemau ar gael', 'back_to_top' => 'Yn ôl i\'r brig', - 'skip_to_main_content' => 'Skip to main content', - 'toggle_details' => 'Toggle Details', - 'toggle_thumbnails' => 'Toggle Thumbnails', - 'details' => 'Details', - 'grid_view' => 'Grid View', - 'list_view' => 'List View', - 'default' => 'Default', - 'breadcrumb' => 'Breadcrumb', - 'status' => 'Status', + 'skip_to_main_content' => 'Neidio i\'r prif gynnwys', + 'toggle_details' => 'Toglo Manylion', + 'toggle_thumbnails' => 'Toglo Mân-Luniau', + 'details' => 'Manylion', + 'grid_view' => 'Golwg Grid', + 'list_view' => 'Golwg Rhestr', + 'default' => 'Diofyn', + 'breadcrumb' => 'Briwsion bara', + 'status' => 'Statws', 'status_active' => 'Gweithredol', 'status_inactive' => 'Anweithredol', 'never' => 'Byth', 'none' => 'Dim un', // Header - 'homepage' => 'Homepage', - 'header_menu_expand' => 'Expand Header Menu', - 'profile_menu' => 'Profile Menu', - 'view_profile' => 'View Profile', - 'edit_profile' => 'Edit Profile', - 'dark_mode' => 'Dark Mode', - 'light_mode' => 'Light Mode', - 'global_search' => 'Global Search', + 'homepage' => 'Tudalen cartref', + 'header_menu_expand' => 'Ehangu Dewislen Pennawd', + 'profile_menu' => 'Dewislen Proffil', + 'view_profile' => 'Gweld proffil', + 'edit_profile' => 'Golygu Proffil', + 'dark_mode' => 'Modd Tywyll', + 'light_mode' => 'Modd Golau', + 'global_search' => 'Chwilio Byd-eang', // Layout tabs 'tab_info' => 'Gwybodaeth', - 'tab_info_label' => 'Tab: Show Secondary Information', + 'tab_info_label' => 'Tab: Dangos Gwybodaeth Eilaidd', 'tab_content' => 'Cynnwys', - 'tab_content_label' => 'Tab: Show Primary Content', + 'tab_content_label' => 'Tab: Dangos Prif Gynnwys', // Email Content - 'email_action_help' => 'If you’re having trouble clicking the ":actionText" button, copy and paste the URL below into your web browser:', - 'email_rights' => 'All rights reserved', + 'email_action_help' => 'Os ydych chi\'n cael trafferth clicio ar y botwm ":actionText", copïwch a gludwch yr URL isod i\'ch porwr gwe:', + 'email_rights' => 'Cedwir pob hawl', // Footer Link Options // Not directly used but available for convenience to users. - 'privacy_policy' => 'Privacy Policy', - 'terms_of_service' => 'Terms of Service', + 'privacy_policy' => 'Polisi Preifatrwydd', + 'terms_of_service' => 'Telerau Gwasanaeth', + + // OpenSearch + 'opensearch_description' => 'Chwilio :appName', ]; diff --git a/lang/cy/components.php b/lang/cy/components.php index 9286c456346..84ddbb5e643 100644 --- a/lang/cy/components.php +++ b/lang/cy/components.php @@ -5,42 +5,42 @@ return [ // Image Manager - 'image_select' => 'Image Select', + 'image_select' => 'Dewis Llun', 'image_list' => 'Rhestr o Ddelweddau', 'image_details' => 'Manylion Delwedd', - 'image_upload' => 'Upload Image', - 'image_intro' => 'Here you can select and manage images that have been previously uploaded to the system.', - 'image_intro_upload' => 'Upload a new image by dragging an image file into this window, or by using the "Upload Image" button above.', + 'image_upload' => 'Uwchlwytho Llun', + 'image_intro' => 'Yma gallwch ddewis a rheoli lluniau sydd wedi\'u huwchlwytho i’r system o’r blaen.', + 'image_intro_upload' => 'Uwchlwythwch lun newydd drwy lusgo ffeil llun i\'r ffenestr hon, neu drwy ddefnyddio\'r botwm "Uwchlwytho Llun" uchod.', 'image_all' => 'Popeth', 'image_all_title' => 'Gweld holl ddelweddau', - 'image_book_title' => 'View images uploaded to this book', - 'image_page_title' => 'View images uploaded to this page', + 'image_book_title' => 'Gweld lluniau a uwchlwythwyd i’r llyfr hwn', + 'image_page_title' => 'Gweld lluniau a uwchlwythwyd i’r dudalen hon', 'image_search_hint' => 'Cwilio gan enw delwedd', - 'image_uploaded' => 'Uploaded :uploadedDate', - 'image_uploaded_by' => 'Uploaded by :userName', - 'image_uploaded_to' => 'Uploaded to :pageLink', - 'image_updated' => 'Updated :updateDate', - 'image_load_more' => 'Load More', + 'image_uploaded' => 'Uwchlwythwyd am :uploadedDate', + 'image_uploaded_by' => 'Uwchlwythwyd gan :userName', + 'image_uploaded_to' => 'Uwchlwythwyd i :pageLink', + 'image_updated' => 'Diweddarwyd am :updateDate', + 'image_load_more' => 'Llwytho Mwy', 'image_image_name' => 'Enw Delwedd', - 'image_delete_used' => 'This image is used in the pages below.', + 'image_delete_used' => 'Mae\'r llun hwn yn cael ei ddefnyddio ar y tudalennau isod.', 'image_delete_confirm_text' => 'Wyt ti\'n bendant eisiau dileu\'r ddelwedd hwn?', - 'image_select_image' => 'Select Image', - 'image_dropzone' => 'Drop images or click here to upload', - 'image_dropzone_drop' => 'Drop images here to upload', + 'image_select_image' => 'Dewis Llun', + 'image_dropzone' => 'Gollyngwch luniau neu cliciwch yma i uwchlwytho', + 'image_dropzone_drop' => 'Gollyngwch luniau yma i uwchlwytho', 'images_deleted' => 'Delweddau wedi\'u Dileu', - 'image_preview' => 'Image Preview', - 'image_upload_success' => 'Image uploaded successfully', - 'image_update_success' => 'Image details successfully updated', - 'image_delete_success' => 'Image successfully deleted', - 'image_replace' => 'Replace Image', - 'image_replace_success' => 'Image file successfully updated', - 'image_rebuild_thumbs' => 'Regenerate Size Variations', - 'image_rebuild_thumbs_success' => 'Image size variations successfully rebuilt!', + 'image_preview' => 'Rhagolwg o’r Llun', + 'image_upload_success' => 'Uwchlwythwyd y llun yn llwyddiannus', + 'image_update_success' => 'Diweddarwyd manylion y llun yn llwyddiannus', + 'image_delete_success' => 'Dilëwyd y llun yn llwyddiannus', + 'image_replace' => 'Newid y llun', + 'image_replace_success' => 'Ffeil llun wedi\'i diweddaru’n llwyddiannus', + 'image_rebuild_thumbs' => 'Atgynhyrchu Amrywiadau Maint', + 'image_rebuild_thumbs_success' => 'Ailadeiladwyd amrywiadau maint y llun yn llwyddiannus!', // Code Editor - 'code_editor' => 'Edit Code', - 'code_language' => 'Code Language', - 'code_content' => 'Code Content', - 'code_session_history' => 'Session History', - 'code_save' => 'Save Code', + 'code_editor' => 'Golygu Cod', + 'code_language' => 'Iaith y Cod', + 'code_content' => 'Cynnwys y Cod', + 'code_session_history' => 'Hanes y Sesiwn', + 'code_save' => 'Cadw Cod', ]; diff --git a/lang/cy/editor.php b/lang/cy/editor.php index 5ad569a8ed4..980099ffd7a 100644 --- a/lang/cy/editor.php +++ b/lang/cy/editor.php @@ -7,12 +7,13 @@ */ return [ // General editor terms - 'general' => 'General', - 'advanced' => 'Advanced', + 'general' => 'Cyffredin', + 'advanced' => 'Datblygedig', 'none' => 'Dim un', 'cancel' => 'Canslo', 'save' => 'Cadw', 'close' => 'Cau', + 'apply' => 'Apply', 'undo' => 'Dadwneud', 'redo' => 'Ail-wneud', 'left' => 'Chwith', @@ -33,141 +34,148 @@ 'header_small' => 'Pennyn Bach', 'header_tiny' => 'Pennyn Mân', 'paragraph' => 'Paragraff', - 'blockquote' => 'Blockquote', - 'inline_code' => 'Inline code', - 'callouts' => 'Callouts', + 'blockquote' => 'Dyfyniad bloc', + 'inline_code' => 'Cod mewnol', + 'callouts' => 'Galwadau', 'callout_information' => 'Gwybodaeth', 'callout_success' => 'Llwyddiant', 'callout_warning' => 'Rhybudd', 'callout_danger' => 'Perygl', 'bold' => 'Trwm', 'italic' => 'Italig', - 'underline' => 'Underline', - 'strikethrough' => 'Strikethrough', - 'superscript' => 'Superscript', - 'subscript' => 'Subscript', + 'underline' => 'Tanlinellu', + 'strikethrough' => 'Llinell Drwodd', + 'superscript' => 'Uwchysgrif', + 'subscript' => 'Isysgrif', 'text_color' => 'Lliw testun', + 'highlight_color' => 'Highlight color', 'custom_color' => 'Lliw addasu', - 'remove_color' => 'Remove color', + 'remove_color' => 'Dileu lliw', 'background_color' => 'Lliw cefnder', - 'align_left' => 'Align left', - 'align_center' => 'Align center', - 'align_right' => 'Align right', - 'align_justify' => 'Justify', - 'list_bullet' => 'Bullet list', - 'list_numbered' => 'Numbered list', - 'list_task' => 'Task list', - 'indent_increase' => 'Increase indent', - 'indent_decrease' => 'Decrease indent', - 'table' => 'Table', - 'insert_image' => 'Insert image', - 'insert_image_title' => 'Insert/Edit Image', - 'insert_link' => 'Insert/edit link', - 'insert_link_title' => 'Insert/Edit Link', - 'insert_horizontal_line' => 'Insert horizontal line', - 'insert_code_block' => 'Insert code block', - 'edit_code_block' => 'Edit code block', - 'insert_drawing' => 'Insert/edit drawing', - 'drawing_manager' => 'Drawing manager', - 'insert_media' => 'Insert/edit media', - 'insert_media_title' => 'Insert/Edit Media', - 'clear_formatting' => 'Clear formatting', - 'source_code' => 'Source code', - 'source_code_title' => 'Source Code', - 'fullscreen' => 'Fullscreen', - 'image_options' => 'Image options', + 'align_left' => 'Alinio i\'r chwith', + 'align_center' => 'Alinio i’r canol', + 'align_right' => 'Alinio i\'r dde', + 'align_justify' => 'Unioni', + 'list_bullet' => 'Rhestr o Bwyntiau Bwled', + 'list_numbered' => 'Rhestr wedi\'i rhifo', + 'list_task' => 'Rhestr dasgau', + 'indent_increase' => 'Cynyddu mewnoliad', + 'indent_decrease' => 'Lleihau mewnoliad', + 'table' => 'Bwrdd', + 'insert_image' => 'Mewnosod llun', + 'insert_image_title' => 'Mewnosod/Golygu Llun', + 'insert_link' => 'Mewnosod/golygu dolen', + 'insert_link_title' => 'Mewnosod/Golygu Dolen', + 'insert_horizontal_line' => 'Mewnosod llinell lorweddol', + 'insert_code_block' => 'Mewnosod bloc cod', + 'edit_code_block' => 'Golygu bloc cod', + 'insert_drawing' => 'Mewnosod/golygu dyluniad', + 'drawing_manager' => 'Rheolwr dylunio', + 'insert_media' => 'Mewnosod/golygu cyfryngau', + 'insert_media_title' => 'Mewnosod/Golygu Cyfryngau', + 'clear_formatting' => 'Clirio fformatio', + 'source_code' => 'Cod ffynhonnell', + 'source_code_title' => 'Cod Ffynhonnell', + 'fullscreen' => 'Sgrin lawn', + 'image_options' => 'Opsiynau llun', // Tables - 'table_properties' => 'Table properties', - 'table_properties_title' => 'Table Properties', - 'delete_table' => 'Delete table', - 'insert_row_before' => 'Insert row before', - 'insert_row_after' => 'Insert row after', - 'delete_row' => 'Delete row', - 'insert_column_before' => 'Insert column before', - 'insert_column_after' => 'Insert column after', - 'delete_column' => 'Delete column', + 'table_properties' => 'Priodweddau tabl', + 'table_properties_title' => 'Priodweddau Tabl', + 'delete_table' => 'Dileu tabl', + 'table_clear_formatting' => 'Clirio fformatio tabl', + 'resize_to_contents' => 'Newid maint i\'r cynnwys', + 'row_header' => 'Pennyn rhes', + 'insert_row_before' => 'Mewnosod rhes cyn', + 'insert_row_after' => 'Mewnosod rhes ar ôl', + 'delete_row' => 'Dileu rhes', + 'insert_column_before' => 'Mewnosod colofn cyn', + 'insert_column_after' => 'Mewnosod colofn ar ôl', + 'delete_column' => 'Dileu colofn', 'table_cell' => 'Cell', - 'table_row' => 'Row', - 'table_column' => 'Column', - 'cell_properties' => 'Cell properties', - 'cell_properties_title' => 'Cell Properties', - 'cell_type' => 'Cell type', + 'table_row' => 'Rhes', + 'table_column' => 'Colofn', + 'cell_properties' => 'Priodweddau cell', + 'cell_properties_title' => 'Priodweddau Cell', + 'cell_type' => 'Math o gell', 'cell_type_cell' => 'Cell', - 'cell_scope' => 'Scope', - 'cell_type_header' => 'Header cell', - 'merge_cells' => 'Merge cells', - 'split_cell' => 'Split cell', - 'table_row_group' => 'Row Group', - 'table_column_group' => 'Column Group', - 'horizontal_align' => 'Horizontal align', - 'vertical_align' => 'Vertical align', - 'border_width' => 'Border width', - 'border_style' => 'Border style', - 'border_color' => 'Border color', - 'row_properties' => 'Row properties', - 'row_properties_title' => 'Row Properties', - 'cut_row' => 'Cut row', - 'copy_row' => 'Copy row', - 'paste_row_before' => 'Paste row before', - 'paste_row_after' => 'Paste row after', - 'row_type' => 'Row type', + 'cell_scope' => 'Cwmpas', + 'cell_type_header' => 'Pennyn cell', + 'merge_cells' => 'Uno celloedd', + 'split_cell' => 'Hollti cell', + 'table_row_group' => 'Grŵp Rhes', + 'table_column_group' => 'Grŵp Colofn', + 'horizontal_align' => 'Alinio llorweddol', + 'vertical_align' => 'Alinio fertigol', + 'border_width' => 'Lled y border', + 'border_style' => 'Arddull y border', + 'border_color' => 'Lliw y border', + 'row_properties' => 'Priodweddau’r rhes', + 'row_properties_title' => 'Priodweddau’r Rhes', + 'cut_row' => 'Torri rhes', + 'copy_row' => 'Copïo rhes', + 'paste_row_before' => 'Gludo rhes cyn', + 'paste_row_after' => 'Gludo rhes ar ôl', + 'row_type' => 'Math o res', 'row_type_header' => 'Pennyn', - 'row_type_body' => 'Body', + 'row_type_body' => 'Corff', 'row_type_footer' => 'Troedyn', 'alignment' => 'Aliniad', - 'cut_column' => 'Cut column', - 'copy_column' => 'Copy column', - 'paste_column_before' => 'Paste column before', - 'paste_column_after' => 'Paste column after', - 'cell_padding' => 'Cell padding', - 'cell_spacing' => 'Cell spacing', + 'cut_column' => 'Torri colofn', + 'copy_column' => 'Copïo colofn', + 'paste_column_before' => 'Gludo colofn cyn', + 'paste_column_after' => 'Gludo colofn ar ôl', + 'cell_padding' => 'Padio cell', + 'cell_spacing' => 'Bylchau rhwng celloedd', 'caption' => 'Pennawd', 'show_caption' => 'Dangos pennawd', - 'constrain' => 'Constrain proportions', + 'constrain' => 'Cyfyngu cyfrannau', 'cell_border_solid' => 'Solid', - 'cell_border_dotted' => 'Dotted', - 'cell_border_dashed' => 'Dashed', - 'cell_border_double' => 'Double', - 'cell_border_groove' => 'Groove', - 'cell_border_ridge' => 'Ridge', - 'cell_border_inset' => 'Inset', - 'cell_border_outset' => 'Outset', - 'cell_border_none' => 'None', - 'cell_border_hidden' => 'Hidden', + 'cell_border_dotted' => 'Dotiog', + 'cell_border_dashed' => 'Llinell Doredig', + 'cell_border_double' => 'Dwbl', + 'cell_border_groove' => 'Rhigol', + 'cell_border_ridge' => 'Crib', + 'cell_border_inset' => 'Mewnosodiad', + 'cell_border_outset' => 'Cychwyniad', + 'cell_border_none' => 'Dim un', + 'cell_border_hidden' => 'Cuddiedig', // Images, links, details/summary & embed - 'source' => 'Source', - 'alt_desc' => 'Alternative description', - 'embed' => 'Embed', - 'paste_embed' => 'Paste your embed code below:', + 'source' => 'Ffynhonnell', + 'alt_desc' => 'Disgrifiad amgen', + 'embed' => 'Ymgorffori', + 'paste_embed' => 'Gludwch eich cod ymgorffori isod:', 'url' => 'URL', - 'text_to_display' => 'Text to display', + 'text_to_display' => 'Testun i\'w arddangos', 'title' => 'Teitl', - 'open_link' => 'Open link', - 'open_link_in' => 'Open link in...', - 'open_link_current' => 'Current window', - 'open_link_new' => 'New window', - 'remove_link' => 'Remove link', - 'insert_collapsible' => 'Insert collapsible block', - 'collapsible_unwrap' => 'Unwrap', - 'edit_label' => 'Edit label', - 'toggle_open_closed' => 'Toggle open/closed', - 'collapsible_edit' => 'Edit collapsible block', - 'toggle_label' => 'Toggle label', + 'browse_links' => 'Browse links', + 'open_link' => 'Agor dolen', + 'open_link_in' => 'Agor dolen yn...', + 'open_link_current' => 'Ffenestr bresennol', + 'open_link_new' => 'Ffenestr newydd', + 'remove_link' => 'Dileu dolen', + 'insert_collapsible' => 'Mewnosod bloc cwympadwy', + 'collapsible_unwrap' => 'Datod', + 'edit_label' => 'Golygu label', + 'toggle_open_closed' => 'Agor/cau Togl', + 'collapsible_edit' => 'Golygu bloc cwympadwy', + 'toggle_label' => 'Toglo label', // About view - 'about' => 'About the editor', - 'about_title' => 'About the WYSIWYG Editor', - 'editor_license' => 'Editor License & Copyright', - 'editor_tiny_license' => 'This editor is built using :tinyLink which is provided under the MIT license.', - 'editor_tiny_license_link' => 'The copyright and license details of TinyMCE can be found here.', - 'save_continue' => 'Save Page & Continue', - 'callouts_cycle' => '(Keep pressing to toggle through types)', - 'link_selector' => 'Link to content', - 'shortcuts' => 'Shortcuts', - 'shortcut' => 'Shortcut', - 'shortcuts_intro' => 'The following shortcuts are available in the editor:', + 'about' => 'Ynglŷn â\'r golygydd', + 'about_title' => 'Ynglŷn â\'r Golygydd WYSIWYG', + 'editor_license' => 'Trwydded a Hawlfraint Golygydd', + 'editor_lexical_license' => 'Mae\'r golygydd hwn wedi\'i adeiladu fel fforc o :lexicalLink sy\'n cael ei ddosbarthu o dan y drwydded MIT.', + 'editor_lexical_license_link' => 'Gellir gweld manylion llawn y drwydded yma.', + 'editor_tiny_license' => 'Mae\'r golygydd hwn wedi\'i adeiladu gan ddefnyddio :tinyLink sy\'n cael ei ddarparu o dan y drwydded MIT.', + 'editor_tiny_license_link' => 'Gellir dod o hyd i fanylion hawlfraint a thrwydded TinyMCE yma.', + 'save_continue' => 'Cadw Tudalen a Pharhau', + 'callouts_cycle' => '(Pwyswch i doglo drwy’r mathau)', + 'link_selector' => 'Dolen i\'r cynnwys', + 'shortcuts' => 'Llwybrau Byr', + 'shortcut' => 'Llwybr Byr', + 'shortcuts_intro' => 'Mae\'r llwybrau byr canlynol ar gael yn y golygydd:', 'windows_linux' => '(Windows/Linux)', 'mac' => '(Mac)', 'description' => 'Disgrifiad', diff --git a/lang/cy/entities.php b/lang/cy/entities.php index f1f915544d1..e6df4317b1d 100644 --- a/lang/cy/entities.php +++ b/lang/cy/entities.php @@ -6,432 +6,471 @@ return [ // Shared - 'recently_created' => 'Recently Created', - 'recently_created_pages' => 'Recently Created Pages', - 'recently_updated_pages' => 'Recently Updated Pages', - 'recently_created_chapters' => 'Recently Created Chapters', - 'recently_created_books' => 'Recently Created Books', - 'recently_created_shelves' => 'Recently Created Shelves', - 'recently_update' => 'Recently Updated', - 'recently_viewed' => 'Recently Viewed', - 'recent_activity' => 'Recent Activity', - 'create_now' => 'Create one now', - 'revisions' => 'Revisions', - 'meta_revision' => 'Revision #:revisionCount', - 'meta_created' => 'Created :timeLength', - 'meta_created_name' => 'Created :timeLength by :user', - 'meta_updated' => 'Updated :timeLength', - 'meta_updated_name' => 'Updated :timeLength by :user', - 'meta_owned_name' => 'Owned by :user', - 'meta_reference_count' => 'Referenced by :count item|Referenced by :count items', - 'entity_select' => 'Entity Select', - 'entity_select_lack_permission' => 'You don\'t have the required permissions to select this item', - 'images' => 'Images', - 'my_recent_drafts' => 'My Recent Drafts', - 'my_recently_viewed' => 'My Recently Viewed', - 'my_most_viewed_favourites' => 'My Most Viewed Favourites', - 'my_favourites' => 'My Favourites', - 'no_pages_viewed' => 'You have not viewed any pages', - 'no_pages_recently_created' => 'No pages have been recently created', - 'no_pages_recently_updated' => 'No pages have been recently updated', - 'export' => 'Export', - 'export_html' => 'Contained Web File', - 'export_pdf' => 'PDF File', - 'export_text' => 'Plain Text File', - 'export_md' => 'Markdown File', + 'recently_created' => 'Crëwyd yn Ddiweddar', + 'recently_created_pages' => 'Tudalennau a Grëwyd yn Ddiweddar', + 'recently_updated_pages' => 'Tudalennau a Ddiweddarwyd yn Ddiweddar', + 'recently_created_chapters' => 'Penodau a Grëwyd yn Ddiweddar', + 'recently_created_books' => 'Llyfrau a Grëwyd yn Ddiweddar', + 'recently_created_shelves' => 'Silffoedd a Grëwyd yn Ddiweddar', + 'recently_update' => 'Diweddarwyd yn Ddiweddar', + 'recently_viewed' => 'Gwelwyd yn Ddiweddar', + 'recent_activity' => 'Gweithgaredd Diweddar', + 'create_now' => 'Creu un nawr', + 'revisions' => 'Diwygiadau', + 'meta_revision' => 'Diwygiad #:revisionCount', + 'meta_created' => 'Crëwyd', + 'meta_created_name' => 'Crëwyd :timeLength gan :user', + 'meta_updated' => 'Diweddarwyd :timeLength', + 'meta_updated_name' => 'Diweddarwyd :timeLength gan :user', + 'meta_owned_name' => 'Mae\'n eiddo i :user', + 'meta_reference_count' => 'Cyfeirir ato gan :count eitem|Cyfeirir ato gan :count o eitemau', + 'entity_select' => 'Dewis Endid', + 'entity_select_lack_permission' => 'Nid oes gennych y caniatâd angenrheidiol i ddewis yr eitem hon', + 'images' => 'Delweddau', + 'my_recent_drafts' => 'Fy Nrafftiau Diweddar', + 'my_recently_viewed' => 'Edrych yn Ddiweddar', + 'my_most_viewed_favourites' => 'Fy Ffefrynnau Mwyaf Poblogaidd', + 'my_favourites' => 'Fy Ffefrynnau', + 'no_pages_viewed' => 'Nid ydych wedi edrych ar unrhyw dudalennau', + 'no_pages_recently_created' => 'Nid oes unrhyw dudalennau wedi\'u creu\'n ddiweddar', + 'no_pages_recently_updated' => 'Nid oes unrhyw dudalennau wedi\'u diweddaru\'n ddiweddar', + 'export' => 'Allforio', + 'export_html' => 'Ffeil Gwe wedi\'i Chynnwys', + 'export_pdf' => 'Ffeil PDF', + 'export_text' => 'Ffeil Testun Plaen', + 'export_md' => 'Ffeil Markdown', + 'export_zip' => 'ZIP cludadwy', + 'default_template' => 'Templed Tudalen Diofyn', + 'default_template_explain' => 'Clustnodwch dempled tudalen a fydd yn cael ei ddefnyddio fel y cynnwys diofyn ar gyfer pob tudalen a grëwyd yn yr eitem hon. Cofiwch y bydd hwn ond yn cael ei ddefnyddio os yw’r sawl a grëodd y dudalen â mynediad gweld i’r dudalen dempled a ddewiswyd.', + 'default_template_select' => 'Dewiswch dudalen templed', + 'import' => 'Mewnforio', + 'import_validate' => 'Dilysu\'r Mewnforyn', + 'import_desc' => 'Mewnforio llyfrau, pennodau & tudalennau gan allforyn zip cludadwy o\'r un enghraifft, neu enghraifft wahanol. Dewis ffeil ZIP i barhau. Ar ôl i\'r ffeil wedi cael ei llwytho i fyny a dilysu, gallwch chi ffurfweddu & cadarnhau\'r mewnforyn yn yr olygfa nesa.', + 'import_zip_select' => 'Dewis ffeil ZIP i lwytho i fyny', + 'import_zip_validation_errors' => 'Wedi canfod gwallau pan ddilysu\'r ffeil ZIP wedi\'i roi:', + 'import_pending' => 'Mewnforion i Ddod', + 'import_pending_none' => 'Nid wedi dechrau unrhyw fewnforion.', + 'import_continue' => 'Parhau Mewnforyn', + 'import_continue_desc' => 'Adolygu\'r cynnwys sy\'n ddod i gael ei fewnforio o\'r ffeil ZIP wedi\'i lwytho i fyny. Pan barod, Rhedeg y mewnforyn i ychwanegu ei gynnwys â\'r system yma. Bydd y ffeil ZIP yn cael ei symud yn awtomatig ar ôl mewnforyn llwyddiannus.', + 'import_details' => 'Manylion Mewnforyn', + 'import_run' => 'Rhedeg y Mewnforyn', + 'import_size' => ':size Mewnforio Maint ZIP', + 'import_uploaded_at' => 'Wedi\'i lwytho i fyny :relativeTime', + 'import_uploaded_by' => 'Llwythwyd gan', + 'import_location' => 'Lleoliad Mewnforyn', + 'import_location_desc' => 'Dewis lleoliad targedi\'r cynnwys wedi\'i mewnforio. Bydd angen yr hawliau perthnasol i greu yn y lleoliad yno.', + 'import_delete_confirm' => 'Ydych chi\'n siŵr eich bod eisiau dileu\'r mewnforyn yma?', + 'import_delete_desc' => 'Bydd hwn yn dileu\'r mewnforyn ffeil ZIP sy wedi\'i lwytho i fyny, a fydd e ddim gallu cael ei ddadwneud.', + 'import_errors' => 'Gwallau Mewnforyn', + 'import_errors_desc' => 'Digwyddodd y gwallau canlynol yn ystod cynnig y mewnforyn:', + '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', // Permissions and restrictions - 'permissions' => 'Permissions', - 'permissions_desc' => 'Set permissions here to override the default permissions provided by user roles.', - 'permissions_book_cascade' => 'Permissions set on books will automatically cascade to child chapters and pages, unless they have their own permissions defined.', - 'permissions_chapter_cascade' => 'Permissions set on chapters will automatically cascade to child pages, unless they have their own permissions defined.', - 'permissions_save' => 'Save Permissions', - 'permissions_owner' => 'Owner', - 'permissions_role_everyone_else' => 'Everyone Else', - 'permissions_role_everyone_else_desc' => 'Set permissions for all roles not specifically overridden.', - 'permissions_role_override' => 'Override permissions for role', - 'permissions_inherit_defaults' => 'Inherit defaults', + 'permissions' => 'Caniatâd', + 'permissions_desc' => 'Gosodwch ganiatâd yma i ddiystyru\'r caniatâd diofyn a ddarperir gan rolau defnyddwyr.', + 'permissions_book_cascade' => 'Bydd caniatâd a osodir ar lyfrau yn rhaeadru’n awtomatig i benodau a thudalennau plant, oni bai bod ganddynt eu caniatâd diffiniedig eu hunain.', + 'permissions_chapter_cascade' => 'Bydd caniatâd a osodir ar benodau yn rhaeadru’n awtomatig i dudalennau plant, oni bai bod ganddynt eu caniatâd diffiniedig eu hunain.', + 'permissions_save' => 'Cadw Caniatâd', + 'permissions_owner' => 'Perchennog', + 'permissions_role_everyone_else' => 'Pawb arall', + 'permissions_role_everyone_else_desc' => 'Gosod caniatâd ar gyfer pob rôl nad ydynt yn cael eu diystyru\'n benodol.', + 'permissions_role_override' => 'Diystyru caniatâd ar gyfer rôl', + 'permissions_inherit_defaults' => 'Etifeddu rhagosodiadau', // Search - 'search_results' => 'Search Results', - 'search_total_results_found' => ':count result found|:count total results found', - 'search_clear' => 'Clear Search', - 'search_no_pages' => 'No pages matched this search', - 'search_for_term' => 'Search for :term', - 'search_more' => 'More Results', - 'search_advanced' => 'Advanced Search', - 'search_terms' => 'Search Terms', - 'search_content_type' => 'Content Type', - 'search_exact_matches' => 'Exact Matches', - 'search_tags' => 'Tag Searches', - 'search_options' => 'Options', - 'search_viewed_by_me' => 'Viewed by me', - 'search_not_viewed_by_me' => 'Not viewed by me', - 'search_permissions_set' => 'Permissions set', - 'search_created_by_me' => 'Created by me', - 'search_updated_by_me' => 'Updated by me', - 'search_owned_by_me' => 'Owned by me', - 'search_date_options' => 'Date Options', - 'search_updated_before' => 'Updated before', - 'search_updated_after' => 'Updated after', - 'search_created_before' => 'Created before', - 'search_created_after' => 'Created after', - 'search_set_date' => 'Set Date', - 'search_update' => 'Update Search', + 'search_results' => 'Canlyniadau Chwilio', + 'search_total_results_found' => 'Cafwyd :count canlyniad|Cafwyd cyfanswm o :count canlyniad', + 'search_clear' => 'Clirio\'r Chwiliad', + 'search_no_pages' => 'Nid oedd unrhyw dudalennau yn cyfateb â\'r chwiliad hwn', + 'search_for_term' => 'Chwilio am :term', + 'search_more' => 'Mwy o Ganlyniadau', + 'search_advanced' => 'Math o Gynnwys', + 'search_terms' => 'Termau Chwilio', + 'search_content_type' => 'Math o Gynnwys', + 'search_exact_matches' => 'Union Gyfatebiaethau', + 'search_tags' => 'Tagio Chwiliadau', + 'search_options' => 'Opsiynau', + 'search_viewed_by_me' => 'Gwelwyd gennyf fi', + 'search_not_viewed_by_me' => 'Nas gwelwyd gennyf fi', + 'search_permissions_set' => 'Gosod Caniatâd', + 'search_created_by_me' => 'Crëwyd gennyf fi', + 'search_updated_by_me' => 'Diweddarwyd gennyf fi', + 'search_owned_by_me' => 'Yn eiddo i mi', + 'search_date_options' => 'Opsiynau Dyddiad', + 'search_updated_before' => 'Diweddarwyd cyn', + 'search_updated_after' => 'Diweddarwyd ar ôl', + 'search_created_before' => 'Crëwyd cyn', + 'search_created_after' => 'Crëwyd ar ôl', + 'search_set_date' => 'Gosod Dyddiad', + 'search_update' => 'Diweddaru Chwiliad', // Shelves - 'shelf' => 'Shelf', - 'shelves' => 'Shelves', - 'x_shelves' => ':count Shelf|:count Shelves', - 'shelves_empty' => 'No shelves have been created', - 'shelves_create' => 'Create New Shelf', - 'shelves_popular' => 'Popular Shelves', - 'shelves_new' => 'New Shelves', - 'shelves_new_action' => 'New Shelf', - 'shelves_popular_empty' => 'The most popular shelves will appear here.', - 'shelves_new_empty' => 'The most recently created shelves will appear here.', - 'shelves_save' => 'Save Shelf', - 'shelves_books' => 'Books on this shelf', - 'shelves_add_books' => 'Add books to this shelf', - 'shelves_drag_books' => 'Drag books below to add them to this shelf', - 'shelves_empty_contents' => 'This shelf has no books assigned to it', - 'shelves_edit_and_assign' => 'Edit shelf to assign books', - 'shelves_edit_named' => 'Edit Shelf :name', - 'shelves_edit' => 'Edit Shelf', - 'shelves_delete' => 'Delete Shelf', - 'shelves_delete_named' => 'Delete Shelf :name', - 'shelves_delete_explain' => "This will delete the shelf with the name ':name'. Contained books will not be deleted.", - 'shelves_delete_confirmation' => 'Are you sure you want to delete this shelf?', - 'shelves_permissions' => 'Shelf Permissions', - 'shelves_permissions_updated' => 'Shelf Permissions Updated', - 'shelves_permissions_active' => 'Shelf Permissions Active', - 'shelves_permissions_cascade_warning' => 'Permissions on shelves do not automatically cascade to contained books. This is because a book can exist on multiple shelves. Permissions can however be copied down to child books using the option found below.', - 'shelves_permissions_create' => 'Shelf create permissions are only used for copying permissions to child books using the action below. They do not control the ability to create books.', - 'shelves_copy_permissions_to_books' => 'Copy Permissions to Books', - 'shelves_copy_permissions' => 'Copy Permissions', - 'shelves_copy_permissions_explain' => 'This will apply the current permission settings of this shelf to all books contained within. Before activating, ensure any changes to the permissions of this shelf have been saved.', - 'shelves_copy_permission_success' => 'Shelf permissions copied to :count books', + 'shelf' => 'Silff', + 'shelves' => 'Silffau', + 'x_shelves' => ':count Silff|:count Shelves', + 'shelves_empty' => 'Ni chrëwyd unrhyw silffoedd', + 'shelves_create' => 'Creu Silff Newydd', + 'shelves_popular' => 'Silffoedd Poblogaidd', + 'shelves_new' => 'Silffau Newydd', + 'shelves_new_action' => 'Silff Newydd', + 'shelves_popular_empty' => 'Bydd y silffoedd mwyaf poblogaidd yn ymddangos yma.', + 'shelves_new_empty' => 'Bydd y silffoedd a grëwyd fwyaf diweddar yn ymddangos yma.', + 'shelves_save' => 'Cadw Silff', + 'shelves_books' => 'Llyfrau ar y silff hon', + 'shelves_add_books' => 'Ychwanegu llyfrau i\'r silff hon', + 'shelves_drag_books' => 'Llusgwch lyfrau isod i\'w hychwanegu at y silff hon', + 'shelves_empty_contents' => 'Nid oes gan y silff hon unrhyw lyfrau wedi’u clustnodi iddi', + 'shelves_edit_and_assign' => 'Golygu silff i glustnodi llyfrau', + 'shelves_edit_named' => 'Golygu Silff :name', + 'shelves_edit' => 'Golygu Silff', + 'shelves_delete' => 'Dileu Silff', + 'shelves_delete_named' => 'Dileu Silff :name', + 'shelves_delete_explain' => "Bydd hyn yn dileu'r silff gyda'r enw ':name'. Ni fydd llyfrau wedi'u cynnwys yn cael eu dileu.", + 'shelves_delete_confirmation' => 'Ydych chi\'n siŵr eich bod chi eisiau dileu\'r silff hon?', + 'shelves_permissions' => 'Caniatâd Silffoedd', + 'shelves_permissions_updated' => 'Diweddarwyd Caniatâd Silffoedd', + 'shelves_permissions_active' => 'Caniatâd Silffoedd yn Weithredol', + 'shelves_permissions_cascade_warning' => 'Nid yw caniatâd ar silffoedd yn rhaeadru’n awtomatig i lyfrau sydd wedi\'u cynnwys. Mae hyn oherwydd y gall llyfr fodoli ar silffoedd lluosog. Fodd bynnag, gellir copïo caniatâd i lawr i lyfrau plant gan ddefnyddio\'r opsiwn a geir isod.', + 'shelves_permissions_create' => 'Dim ond ar gyfer copïo caniatâd i lyfrau plant y defnyddir caniatâd creu silff gan ddefnyddio\'r camau isod. Nid ydynt yn rheoli\'r gallu i greu llyfrau.', + 'shelves_copy_permissions_to_books' => 'Copïo Caniatâd i Lyfrau', + 'shelves_copy_permissions' => 'Copïo Caniatâd', + 'shelves_copy_permissions_explain' => 'Bydd hyn yn cymhwyso gosodiadau caniatâd presennol y silff hon i bob llyfr sydd wedi\'u cynnwys ynddi. Cyn ysgogi, gwnewch yn siŵr bod unrhyw newidiadau i ganiatâd y silff hon wedi\'u cadw.', + 'shelves_copy_permission_success' => 'Caniatâd silff wedi\'i gopïo i :count o lyfrau', // Books - 'book' => 'Book', - 'books' => 'Books', - 'x_books' => ':count Book|:count Books', - 'books_empty' => 'No books have been created', - 'books_popular' => 'Popular Books', - 'books_recent' => 'Recent Books', - 'books_new' => 'New Books', - 'books_new_action' => 'New Book', - 'books_popular_empty' => 'The most popular books will appear here.', - 'books_new_empty' => 'The most recently created books will appear here.', - 'books_create' => 'Create New Book', - 'books_delete' => 'Delete Book', - 'books_delete_named' => 'Delete Book :bookName', - 'books_delete_explain' => 'This will delete the book with the name \':bookName\'. All pages and chapters will be removed.', - 'books_delete_confirmation' => 'Are you sure you want to delete this book?', - 'books_edit' => 'Edit Book', - 'books_edit_named' => 'Edit Book :bookName', - 'books_form_book_name' => 'Book Name', - 'books_save' => 'Save Book', - 'books_default_template' => 'Default Page Template', - 'books_default_template_explain' => 'Assign a page template that will be used as the default content for all new pages in this book. Keep in mind this will only be used if the page creator has view access to those chosen template page.', - 'books_default_template_select' => 'Select a template page', - 'books_permissions' => 'Book Permissions', - 'books_permissions_updated' => 'Book Permissions Updated', - 'books_empty_contents' => 'No pages or chapters have been created for this book.', - 'books_empty_create_page' => 'Create a new page', - 'books_empty_sort_current_book' => 'Sort the current book', - 'books_empty_add_chapter' => 'Add a chapter', - 'books_permissions_active' => 'Book Permissions Active', - 'books_search_this' => 'Search this book', - 'books_navigation' => 'Book Navigation', - 'books_sort' => 'Sort Book Contents', - 'books_sort_desc' => 'Move chapters and pages within a book to reorganise its contents. Other books can be added which allows easy moving of chapters and pages between books.', - 'books_sort_named' => 'Sort Book :bookName', - 'books_sort_name' => 'Sort by Name', - 'books_sort_created' => 'Sort by Created Date', - 'books_sort_updated' => 'Sort by Updated Date', - 'books_sort_chapters_first' => 'Chapters First', - 'books_sort_chapters_last' => 'Chapters Last', - 'books_sort_show_other' => 'Show Other Books', - 'books_sort_save' => 'Save New Order', - 'books_sort_show_other_desc' => 'Add other books here to include them in the sort operation, and allow easy cross-book reorganisation.', - 'books_sort_move_up' => 'Move Up', - 'books_sort_move_down' => 'Move Down', - 'books_sort_move_prev_book' => 'Move to Previous Book', - 'books_sort_move_next_book' => 'Move to Next Book', - 'books_sort_move_prev_chapter' => 'Move Into Previous Chapter', - 'books_sort_move_next_chapter' => 'Move Into Next Chapter', - 'books_sort_move_book_start' => 'Move to Start of Book', - 'books_sort_move_book_end' => 'Move to End of Book', - 'books_sort_move_before_chapter' => 'Move to Before Chapter', - 'books_sort_move_after_chapter' => 'Move to After Chapter', - 'books_copy' => 'Copy Book', - 'books_copy_success' => 'Book successfully copied', + 'book' => 'Llyfr', + 'books' => 'Llyfrau', + 'x_books' => ':count Llyfr|:count o Lyfrau', + 'books_empty' => 'Ni chrëwyd unrhyw llyfrau', + 'books_popular' => 'Llyfrau Poblogaidd', + 'books_recent' => 'Llyfrau Diweddar', + 'books_new' => 'Llyfrau Newydd', + 'books_new_action' => 'Llyfr Newydd', + 'books_popular_empty' => 'Bydd y llyfrau mwyaf poblogaidd yn ymddangos yma.', + 'books_new_empty' => 'Bydd y llyfrau a grëwyd fwyaf diweddar yn ymddangos yma.', + 'books_create' => 'Creu Llyfr Newydd', + 'books_delete' => 'Dileu Llyfr', + 'books_delete_named' => 'Dileu :bookName Llyfr', + 'books_delete_explain' => 'Bydd hyn yn dileu\'r llyfr gyda\'r enw ‘:bookName’. Bydd yr holl dudalennau a phenodau yn cael eu dileu.', + 'books_delete_confirmation' => 'Ydych chi\'n siŵr eich bod eisiau dileu\'r llyfr hwn?', + 'books_edit' => 'Golygu\'r Llyfr', + 'books_edit_named' => 'Golygu :bookName Llyfr', + 'books_form_book_name' => 'Enw\'r Llyfr', + 'books_save' => 'Cadw Llyfr', + 'books_permissions' => 'Caniatâd Llyfr', + 'books_permissions_updated' => 'Diweddarwyd Caniatâd Llyfr', + 'books_empty_contents' => 'Ni chrëwyd unrhyw dudalennau neu benodau ar gyfer y llyfr hwn.', + 'books_empty_create_page' => 'Creu tudalen newydd', + 'books_empty_sort_current_book' => 'Trefnu’r llyfr presennol', + 'books_empty_add_chapter' => 'Ychwanegu pennod', + 'books_permissions_active' => 'Caniatâd Llyfr yn Weithredol', + 'books_search_this' => 'Chwilio\'r llyfr hwn', + 'books_navigation' => 'Llywio Llyfr', + 'books_sort' => 'Trefnu Cynnwys Llyfr', + 'books_sort_desc' => 'Move chapters and pages within a book to reorganise its contents. Other books can be added which allows easy moving of chapters and pages between books. Optionally an auto sort rule can be set to automatically sort this book\'s contents upon changes.', + 'books_sort_auto_sort' => 'Auto Sort Option', + 'books_sort_auto_sort_active' => 'Auto Sort Active: :sortName', + 'books_sort_named' => 'Trefnu Llyfr :bookName', + 'books_sort_name' => 'Trefnu yn ôl Enw', + 'books_sort_created' => 'Trefnu yn ôl Dyddiad Creu', + 'books_sort_updated' => 'Trefnu yn ôl Dyddiad Diweddaru', + 'books_sort_chapters_first' => 'Penodau yn Gyntaf', + 'books_sort_chapters_last' => 'Penodau yn Olaf', + 'books_sort_show_other' => 'Dangos Llyfrau Eraill', + 'books_sort_save' => 'Cadw’r Drefn Newydd', + 'books_sort_show_other_desc' => 'Ychwanegwch lyfrau eraill yma i\'w cynnwys yn y gwaith didoli, a chaniatáu ad-drefnu hawdd rhwng llyfrau.', + 'books_sort_move_up' => 'Symud i Fyny', + 'books_sort_move_down' => 'Symud i Lawr', + 'books_sort_move_prev_book' => 'Symud i\'r Llyfr Blaenorol', + 'books_sort_move_next_book' => 'Symud i\'r Llyfr Nesaf', + 'books_sort_move_prev_chapter' => 'Symud i\'r Bennod Flaenorol', + 'books_sort_move_next_chapter' => 'Symud i\'r Bennod Nesaf', + 'books_sort_move_book_start' => 'Symud i Ddechrau\'r Llyfr', + 'books_sort_move_book_end' => 'Symud i Ddiwedd y Llyfr', + 'books_sort_move_before_chapter' => 'Symud i’r Bennod Cynt', + 'books_sort_move_after_chapter' => 'Symud i’r Bennod Ddilynol', + 'books_copy' => 'Copio Llyfr', + 'books_copy_success' => 'Llyfr wedi\'i copio\'n llwyddiannus', // Chapters - 'chapter' => 'Chapter', - 'chapters' => 'Chapters', - 'x_chapters' => ':count Chapter|:count Chapters', - 'chapters_popular' => 'Popular Chapters', - 'chapters_new' => 'New Chapter', - 'chapters_create' => 'Create New Chapter', - 'chapters_delete' => 'Delete Chapter', - 'chapters_delete_named' => 'Delete Chapter :chapterName', - 'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages that exist within this chapter will also be deleted.', - 'chapters_delete_confirm' => 'Are you sure you want to delete this chapter?', - 'chapters_edit' => 'Edit Chapter', - 'chapters_edit_named' => 'Edit Chapter :chapterName', - 'chapters_save' => 'Save Chapter', - 'chapters_move' => 'Move Chapter', - 'chapters_move_named' => 'Move Chapter :chapterName', - 'chapters_copy' => 'Copy Chapter', - 'chapters_copy_success' => 'Chapter successfully copied', - 'chapters_permissions' => 'Chapter Permissions', - 'chapters_empty' => 'No pages are currently in this chapter.', - 'chapters_permissions_active' => 'Chapter Permissions Active', - 'chapters_permissions_success' => 'Chapter Permissions Updated', - 'chapters_search_this' => 'Search this chapter', - 'chapter_sort_book' => 'Sort Book', + 'chapter' => 'Pennod', + 'chapters' => 'Penodau', + 'x_chapters' => ':count Pennod|:count Penodau', + 'chapters_popular' => 'Penodau Poblogaidd', + 'chapters_new' => 'Pennod Newydd', + 'chapters_create' => 'Creu Pennod Newydd', + 'chapters_delete' => 'Dileu Pennod', + 'chapters_delete_named' => 'Dileu :chapterName Pennod', + 'chapters_delete_explain' => 'Bydd hyn yn dileu\'r bennod gyda\'r enw \':chapterName\'. Bydd yr holl dudalennau sy\'n bodoli yn y bennod hon hefyd yn cael eu dileu.', + 'chapters_delete_confirm' => 'Ydych chi\'n siŵr eich bod eisiau dileu\'r bennod hon?', + 'chapters_edit' => 'Ychwanegu Pennod', + 'chapters_edit_named' => 'Ychwanegu Pennod :chapterName', + 'chapters_save' => 'Cadw Pennod', + 'chapters_move' => 'Symud Pennod', + 'chapters_move_named' => 'Symud Pennod :chapterName', + 'chapters_copy' => 'Copïo Pennod', + 'chapters_copy_success' => 'Pennod wedi\'i chopïo\'n llwyddiannus', + 'chapters_permissions' => 'Pennau Taith Pennod', + 'chapters_empty' => 'Does dim tudalennau yn y bennod hon ar hyn o bryd.', + 'chapters_permissions_active' => 'Caniatâd Pennod yn Weithredol', + 'chapters_permissions_success' => 'Diweddarwyd Caniatâd Pennod', + 'chapters_search_this' => 'Chwilio yn y bennod hon', + 'chapter_sort_book' => 'Trefnu Llyfr', // Pages - 'page' => 'Page', - 'pages' => 'Pages', - 'x_pages' => ':count Page|:count Pages', - 'pages_popular' => 'Popular Pages', - 'pages_new' => 'New Page', - 'pages_attachments' => 'Attachments', - 'pages_navigation' => 'Page Navigation', - 'pages_delete' => 'Delete Page', - 'pages_delete_named' => 'Delete Page :pageName', - 'pages_delete_draft_named' => 'Delete Draft Page :pageName', - 'pages_delete_draft' => 'Delete Draft Page', - 'pages_delete_success' => 'Page deleted', - 'pages_delete_draft_success' => 'Draft page deleted', - 'pages_delete_warning_template' => 'This page is in active use as a book default page template. These books will no longer have a default page template assigned after this page is deleted.', - 'pages_delete_confirm' => 'Are you sure you want to delete this page?', - 'pages_delete_draft_confirm' => 'Are you sure you want to delete this draft page?', - 'pages_editing_named' => 'Editing Page :pageName', - 'pages_edit_draft_options' => 'Draft Options', - 'pages_edit_save_draft' => 'Save Draft', - 'pages_edit_draft' => 'Edit Page Draft', - 'pages_editing_draft' => 'Editing Draft', - 'pages_editing_page' => 'Editing Page', - 'pages_edit_draft_save_at' => 'Draft saved at ', - 'pages_edit_delete_draft' => 'Delete Draft', - 'pages_edit_delete_draft_confirm' => 'Are you sure you want to delete your draft page changes? All of your changes, since the last full save, will be lost and the editor will be updated with the latest page non-draft save state.', - 'pages_edit_discard_draft' => 'Discard Draft', - 'pages_edit_switch_to_markdown' => 'Switch to Markdown Editor', - 'pages_edit_switch_to_markdown_clean' => '(Clean Content)', - 'pages_edit_switch_to_markdown_stable' => '(Stable Content)', - 'pages_edit_switch_to_wysiwyg' => 'Switch to WYSIWYG Editor', - 'pages_edit_set_changelog' => 'Set Changelog', - 'pages_edit_enter_changelog_desc' => 'Enter a brief description of the changes you\'ve made', - 'pages_edit_enter_changelog' => 'Enter Changelog', - 'pages_editor_switch_title' => 'Switch Editor', - 'pages_editor_switch_are_you_sure' => 'Are you sure you want to change the editor for this page?', - 'pages_editor_switch_consider_following' => 'Consider the following when changing editors:', - 'pages_editor_switch_consideration_a' => 'Once saved, the new editor option will be used by any future editors, including those that may not be able to change editor type themselves.', - 'pages_editor_switch_consideration_b' => 'This can potentially lead to a loss of detail and syntax in certain circumstances.', - 'pages_editor_switch_consideration_c' => 'Tag or changelog changes, made since last save, won\'t persist across this change.', - 'pages_save' => 'Save Page', - 'pages_title' => 'Page Title', - 'pages_name' => 'Page Name', - 'pages_md_editor' => 'Editor', - 'pages_md_preview' => 'Preview', - 'pages_md_insert_image' => 'Insert Image', - 'pages_md_insert_link' => 'Insert Entity Link', - 'pages_md_insert_drawing' => 'Insert Drawing', - 'pages_md_show_preview' => 'Show preview', - 'pages_md_sync_scroll' => 'Sync preview scroll', - 'pages_drawing_unsaved' => 'Unsaved Drawing Found', - 'pages_drawing_unsaved_confirm' => 'Unsaved drawing data was found from a previous failed drawing save attempt. Would you like to restore and continue editing this unsaved drawing?', - 'pages_not_in_chapter' => 'Page is not in a chapter', - 'pages_move' => 'Move Page', - 'pages_copy' => 'Copy Page', - 'pages_copy_desination' => 'Copy Destination', - 'pages_copy_success' => 'Page successfully copied', - 'pages_permissions' => 'Page Permissions', - 'pages_permissions_success' => 'Page permissions updated', - 'pages_revision' => 'Revision', - 'pages_revisions' => 'Page Revisions', - 'pages_revisions_desc' => 'Listed below are all the past revisions of this page. You can look back upon, compare, and restore old page versions if permissions allow. The full history of the page may not be fully reflected here since, depending on system configuration, old revisions could be auto-deleted.', - 'pages_revisions_named' => 'Page Revisions for :pageName', - 'pages_revision_named' => 'Page Revision for :pageName', - 'pages_revision_restored_from' => 'Restored from #:id; :summary', - 'pages_revisions_created_by' => 'Created By', - 'pages_revisions_date' => 'Revision Date', + 'page' => 'Tudalen', + 'pages' => 'Tudalennau', + 'x_pages' => ':count Tudalen|:count Tudalennau', + 'pages_popular' => 'Tudalennau Poblogaidd', + 'pages_new' => 'Tudalen Newydd', + 'pages_attachments' => 'Atodiadau', + 'pages_navigation' => 'Llywio Tudalen', + 'pages_delete' => 'Dileu Tudalen', + 'pages_delete_named' => 'Dileu :pageName Tudalen', + 'pages_delete_draft_named' => 'Dileu Tudalen Ddrafft :pageName', + 'pages_delete_draft' => 'Dileu Tudalen Ddrafft', + 'pages_delete_success' => 'Tudalen wedi\'i dileu', + 'pages_delete_draft_success' => 'Tudalen ddrafft wedi’i dileu', + 'pages_delete_warning_template' => 'Mae\'r dudalen hon yn cael ei defnyddio\'n weithredol fel templed tudalen diofyn llyfr neu bennod. Ni fydd gan y llyfrau neu\'r penodau hyn dempled tudalen diofyn wedi\'i glustnodi ar ôl dileu\'r dudalen hon.', + 'pages_delete_confirm' => 'Ydych chi\'n siŵr eich bod eisiau dileu\'r dudalen hon?', + 'pages_delete_draft_confirm' => 'Ydych chi\'n siŵr eich bod eisiau dileu\'r dudalen ddrafft hon?', + 'pages_editing_named' => 'Golygu Tudalen :pageName', + 'pages_edit_draft_options' => 'Opsiynau Drafft', + 'pages_edit_save_draft' => 'Cadw Drafft', + 'pages_edit_draft' => 'Golygu Tudalen Ddrafft', + 'pages_editing_draft' => 'Golygu Drafft', + 'pages_editing_page' => 'Golygu Tudalen', + 'pages_edit_draft_save_at' => 'Cadwyd drafft ar ', + 'pages_edit_delete_draft' => 'Dileu Drafft', + 'pages_edit_delete_draft_confirm' => 'Ydych chi\'n siŵr eich bod am ddileu eich newidiadau i’r dudalen ddrafft? Bydd eich holl newidiadau, ers eu cadw ddiwethaf, yn cael eu colli a bydd y golygydd yn cael ei ddiweddaru gyda\'r dudalen ddiweddaraf nad yw\'n ddrafft.', + 'pages_edit_discard_draft' => 'Gwaredu Drafft', + 'pages_edit_switch_to_markdown' => 'Newid i’r Golygydd Markdown', + 'pages_edit_switch_to_markdown_clean' => '(Cynnwys Glân)', + 'pages_edit_switch_to_markdown_stable' => '(Cynnwys Glân)', + 'pages_edit_switch_to_wysiwyg' => 'Newid i Olygydd WYSIWYG', + 'pages_edit_switch_to_new_wysiwyg' => 'Newid i WYSIWYG newydd', + 'pages_edit_switch_to_new_wysiwyg_desc' => '(In Beta Testing)', + 'pages_edit_set_changelog' => 'Gosod Changelog', + 'pages_edit_enter_changelog_desc' => 'Rhowch ddisgrifiad byr o\'r newidiadau rydych wedi\'u gwneud', + 'pages_edit_enter_changelog' => 'Cofnodwch Changelog', + 'pages_editor_switch_title' => 'Newid Golygydd', + 'pages_editor_switch_are_you_sure' => 'Ydych chi\'n siŵr eich bod eisiau newid y golygydd ar gyfer y dudalen hon?', + 'pages_editor_switch_consider_following' => 'Ystyriwch y canlynol wrth newid golygyddion:', + 'pages_editor_switch_consideration_a' => 'Ar ôl ei gadw, bydd yr opsiwn golygydd newydd yn cael ei ddefnyddio gan unrhyw olygydd yn y dyfodol, gan gynnwys y rhai na fyddant efallai\'n gallu newid y math o olygydd eu hunain.', + 'pages_editor_switch_consideration_b' => 'Gall hyn arwain at golli manylion a Syntax mewn rhai amgylchiadau.', + 'pages_editor_switch_consideration_c' => 'Ni fydd newidiadau tag neu changelog, a wnaed ers eu cadw ddiwethaf, yn parhau ar draws y newid hwn.', + 'pages_save' => 'Cadw Tudalen', + 'pages_title' => 'Teitl y Dudalen', + 'pages_name' => 'Enw\'r Dudalen', + 'pages_md_editor' => 'Golygydd', + 'pages_md_preview' => 'Rhagolwg', + 'pages_md_insert_image' => 'Mewnosod Delwedd', + 'pages_md_insert_link' => 'Mewnosod Dolen Endid', + 'pages_md_insert_drawing' => 'Mewnosod Llun', + 'pages_md_show_preview' => 'Dangos rhagolwg', + 'pages_md_sync_scroll' => 'Cydamseru sgrôl ragolwg', + 'pages_md_plain_editor' => 'Plaintext editor', + 'pages_drawing_unsaved' => 'Canfuwyd Llun heb ei Gadw', + 'pages_drawing_unsaved_confirm' => 'Canfuwyd data llun heb ei gadw o ymgais aflwyddiannus blaenorol i gadw llun. Hoffech chi adfer a pharhau i olygu\'r llun heb ei gadw?', + 'pages_not_in_chapter' => 'Nid yw\'r dudalen mewn pennod', + 'pages_move' => 'Symud Tudalen', + 'pages_copy' => 'Copïo Tudalen', + 'pages_copy_desination' => 'Copïo Cyrchfan', + 'pages_copy_success' => 'Tudalen wedi\'i chreu\'n llwyddiannus', + 'pages_permissions' => 'Pennau Taith Tudalen', + 'pages_permissions_success' => 'Pennau taith tudalen wedi\'u diweddaru', + 'pages_revision' => 'Diwygiad', + 'pages_revisions' => 'Diwygiadau\'r Dudalen', + 'pages_revisions_desc' => 'Isod ceir holl ddiwygiadau blaenorol y dudalen hon. Gallwch edrych yn ôl ar, cymharu, ac adfer hen fersiynau o’r dudalen os oes gennych y caniatâd priodol. Efallai na fydd hanes llawn y dudalen yn cael ei adlewyrchu\'n llawn yma oherwydd, gan ddibynnu ar ffurfweddiad y system, gallai hen fersiynau fod wedi’u dileu’n awtomatig.', + 'pages_revisions_named' => 'Diwygiadau Tudalen ar gyfer :pageName', + 'pages_revision_named' => 'Diwygiad Tudalen ar gyfer :pageName', + 'pages_revision_restored_from' => 'Adferwyd o #:id; :summary', + 'pages_revisions_created_by' => 'Crëwyd gan', + 'pages_revisions_date' => 'Dyddiad Adolygu', 'pages_revisions_number' => '#', - 'pages_revisions_sort_number' => 'Revision Number', - 'pages_revisions_numbered' => 'Revision #:id', - 'pages_revisions_numbered_changes' => 'Revision #:id Changes', - 'pages_revisions_editor' => 'Editor Type', + 'pages_revisions_sort_number' => 'Rhif Diwygiad', + 'pages_revisions_numbered' => 'Diwygiad #:id', + 'pages_revisions_numbered_changes' => 'Diwygiad #:id Newidiadau', + 'pages_revisions_editor' => 'Math o Olygydd', 'pages_revisions_changelog' => 'Changelog', - 'pages_revisions_changes' => 'Changes', - 'pages_revisions_current' => 'Current Version', - 'pages_revisions_preview' => 'Preview', - 'pages_revisions_restore' => 'Restore', - 'pages_revisions_none' => 'This page has no revisions', - 'pages_copy_link' => 'Copy Link', - 'pages_edit_content_link' => 'Jump to section in editor', - 'pages_pointer_enter_mode' => 'Enter section select mode', - 'pages_pointer_label' => 'Page Section Options', - 'pages_pointer_permalink' => 'Page Section Permalink', - 'pages_pointer_include_tag' => 'Page Section Include Tag', - 'pages_pointer_toggle_link' => 'Permalink mode, Press to show include tag', - 'pages_pointer_toggle_include' => 'Include tag mode, Press to show permalink', - 'pages_permissions_active' => 'Page Permissions Active', - 'pages_initial_revision' => 'Initial publish', - 'pages_references_update_revision' => 'System auto-update of internal links', - 'pages_initial_name' => 'New Page', - 'pages_editing_draft_notification' => 'You are currently editing a draft that was last saved :timeDiff.', - 'pages_draft_edited_notification' => 'This page has been updated by since that time. It is recommended that you discard this draft.', - 'pages_draft_page_changed_since_creation' => 'This page has been updated since this draft was created. It is recommended that you discard this draft or take care not to overwrite any page changes.', + 'pages_revisions_changes' => 'Newidiadau', + 'pages_revisions_current' => 'Fersiwn Bresennol', + 'pages_revisions_preview' => 'Rhagolwg', + 'pages_revisions_restore' => 'Adfer', + 'pages_revisions_none' => 'Nid oes gan y dudalen hon unrhyw ddiwygiadau', + 'pages_copy_link' => 'Copïo Dolen', + 'pages_edit_content_link' => 'Neidio i\'r adran yn y golygydd', + 'pages_pointer_enter_mode' => 'Rhowch y modd dethol adran', + 'pages_pointer_label' => 'Dewisiadau Adran Tudalen', + 'pages_pointer_permalink' => 'Dolen Barhaol Adran Tudalen', + 'pages_pointer_include_tag' => 'Adran Tudalen Cynnwys Tag', + 'pages_pointer_toggle_link' => 'Modd dolen barhaol, Pwyswch i ddangos cynnwys tag', + 'pages_pointer_toggle_include' => 'Modd cynnwys tag, Pwyswch i ddangos dolen barhaol', + 'pages_permissions_active' => 'Caniatâd Tudalen yn Weithredol', + 'pages_initial_revision' => 'Cyhoeddi cychwynnol', + 'pages_references_update_revision' => 'Diweddariad awtomatig y system o ddolenni mewnol', + 'pages_initial_name' => 'Tudalen Newydd', + 'pages_editing_draft_notification' => 'Rydych chi wrthi’n golygu drafft a gafodd ei gadw ddiwethaf ar :timeDiff.', + 'pages_draft_edited_notification' => 'Mae\'r dudalen hon wedi\'i diweddaru ers hynny. Argymhellir eich bod yn dileu\'r drafft hwn.', + 'pages_draft_page_changed_since_creation' => 'Mae\'r dudalen hon wedi\'i diweddaru ers i\'r drafft hwn gael ei greu. Argymhellir eich bod yn dileu\'r drafft hwn neu\'n sicrhau nad ydych yn ysgrifennu unrhyw newidiadau i’r dudalen.', 'pages_draft_edit_active' => [ - 'start_a' => ':count users have started editing this page', - 'start_b' => ':userName has started editing this page', - 'time_a' => 'since the page was last updated', - 'time_b' => 'in the last :minCount minutes', - 'message' => ':start :time. Take care not to overwrite each other\'s updates!', + 'start_a' => 'Mae :count defnyddiwr wedi dechrau golygu\'r dudalen hon', + 'start_b' => 'Mae :userName wedi dechrau golygu\'r dudalen hon', + 'time_a' => 'ers i\'r dudalen gael ei diweddaru ddiwethaf', + 'time_b' => 'yn y :minCount munud diwethaf', + 'message' => ':start :time. Gofalwch beidio ag ysgrifennu dros ddiweddariadau eich gilydd!', ], - 'pages_draft_discarded' => 'Draft discarded! The editor has been updated with the current page content', - 'pages_draft_deleted' => 'Draft deleted! The editor has been updated with the current page content', - 'pages_specific' => 'Specific Page', - 'pages_is_template' => 'Page Template', + 'pages_draft_discarded' => 'Drafft wedi\'i waredu! Mae\'r golygydd wedi\'i ddiweddaru gyda chynnwys presennol y dudalen', + 'pages_draft_deleted' => 'Drafft wedi\'i ddileu! Mae\'r golygydd wedi\'i ddiweddaru gyda chynnwys presennol y dudalen', + 'pages_specific' => 'Tudalen Benodol', + 'pages_is_template' => 'Templed Tudalen', // Editor Sidebar - 'toggle_sidebar' => 'Toggle Sidebar', - 'page_tags' => 'Page Tags', - 'chapter_tags' => 'Chapter Tags', - 'book_tags' => 'Book Tags', - 'shelf_tags' => 'Shelf Tags', + 'toggle_sidebar' => 'Toglo Bar ochr', + 'page_tags' => 'Tagiau Tudalennau', + 'chapter_tags' => 'Tagiau Penodau', + 'book_tags' => 'Tagiau Llyfrau', + 'shelf_tags' => 'Tagiau Silffoedd', 'tag' => 'Tag', - 'tags' => 'Tags', - 'tags_index_desc' => 'Tags can be applied to content within the system to apply a flexible form of categorization. Tags can have both a key and value, with the value being optional. Once applied, content can then be queried using the tag name and value.', - 'tag_name' => 'Tag Name', - 'tag_value' => 'Tag Value (Optional)', - 'tags_explain' => "Add some tags to better categorise your content. \n You can assign a value to a tag for more in-depth organisation.", - 'tags_add' => 'Add another tag', - 'tags_remove' => 'Remove this tag', - 'tags_usages' => 'Total tag usages', - 'tags_assigned_pages' => 'Assigned to Pages', - 'tags_assigned_chapters' => 'Assigned to Chapters', - 'tags_assigned_books' => 'Assigned to Books', - 'tags_assigned_shelves' => 'Assigned to Shelves', - 'tags_x_unique_values' => ':count unique values', - 'tags_all_values' => 'All values', - 'tags_view_tags' => 'View Tags', - 'tags_view_existing_tags' => 'View existing tags', - 'tags_list_empty_hint' => 'Tags can be assigned via the page editor sidebar or while editing the details of a book, chapter or shelf.', - 'attachments' => 'Attachments', - 'attachments_explain' => 'Upload some files or attach some links to display on your page. These are visible in the page sidebar.', - 'attachments_explain_instant_save' => 'Changes here are saved instantly.', - 'attachments_upload' => 'Upload File', - 'attachments_link' => 'Attach Link', - 'attachments_upload_drop' => 'Alternatively you can drag and drop a file here to upload it as an attachment.', - 'attachments_set_link' => 'Set Link', - 'attachments_delete' => 'Are you sure you want to delete this attachment?', - 'attachments_dropzone' => 'Drop files here to upload', - 'attachments_no_files' => 'No files have been uploaded', - 'attachments_explain_link' => 'You can attach a link if you\'d prefer not to upload a file. This can be a link to another page or a link to a file in the cloud.', - 'attachments_link_name' => 'Link Name', - 'attachment_link' => 'Attachment link', - 'attachments_link_url' => 'Link to file', - 'attachments_link_url_hint' => 'Url of site or file', - 'attach' => 'Attach', - 'attachments_insert_link' => 'Add Attachment Link to Page', - 'attachments_edit_file' => 'Edit File', - 'attachments_edit_file_name' => 'File Name', - 'attachments_edit_drop_upload' => 'Drop files or click here to upload and overwrite', - 'attachments_order_updated' => 'Attachment order updated', - 'attachments_updated_success' => 'Attachment details updated', - 'attachments_deleted' => 'Attachment deleted', - 'attachments_file_uploaded' => 'File successfully uploaded', - 'attachments_file_updated' => 'File successfully updated', - 'attachments_link_attached' => 'Link successfully attached to page', - 'templates' => 'Templates', - 'templates_set_as_template' => 'Page is a template', - 'templates_explain_set_as_template' => 'You can set this page as a template so its contents be utilized when creating other pages. Other users will be able to use this template if they have view permissions for this page.', - 'templates_replace_content' => 'Replace page content', - 'templates_append_content' => 'Append to page content', - 'templates_prepend_content' => 'Prepend to page content', + 'tags' => 'Tagiau', + 'tags_index_desc' => 'Gellir cymhwyso tagiau i gynnwys o fewn y system i sicrhau categoreiddio hyblyg. Gall tagiau fod ag allwedd a gwerth, gyda\'r gwerth yn ddewisol. Ar ôl ei gymhwyso, gellir cwestiynu’r cynnwys gan ddefnyddio enw a gwerth y tag.', + 'tag_name' => 'Enw’r Tag', + 'tag_value' => 'Gwerth y Tag (Dewisol)', + 'tags_explain' => "Ychwanegwch rai tagiau i gategoreiddio'ch cynnwys yn well. Gallwch glustnodi gwerth i dag i gael trefn fanylach.", + 'tags_add' => 'Ychwanegu tag arall', + 'tags_remove' => 'Tynnu’r tag hwn', + 'tags_usages' => 'Cyfanswm y defnydd o’r tag', + 'tags_assigned_pages' => 'Clustnodwyd i Dudalennau', + 'tags_assigned_chapters' => 'Clustnodwyd i Benodau', + 'tags_assigned_books' => 'Clustnodwyd i Lyfrau', + 'tags_assigned_shelves' => 'Clustnodwyd i Silffoedd', + 'tags_x_unique_values' => ':count gwerthoedd unigryw', + 'tags_all_values' => 'Pob gwerth', + 'tags_view_tags' => 'Gweld Tagiau', + 'tags_view_existing_tags' => 'Gweld tagiau presennol', + 'tags_list_empty_hint' => 'Gellir clustnodi tagiau trwy far ochr golygydd y dudalen neu wrth olygu manylion llyfr, pennod neu silff.', + 'attachments' => 'Atodiadau', + 'attachments_explain' => 'Uwchlwytho rhai ffeiliau neu atodi rhai dolenni i\'w harddangos ar eich tudalen. Mae\'r rhain i\'w gweld ym mar ochr y dudalen.', + 'attachments_explain_instant_save' => 'Caiff y newidiadau yma eu cadw ar unwaith.', + 'attachments_upload' => 'Uwchlwytho Ffeil', + 'attachments_link' => 'Atodi Dolen', + 'attachments_upload_drop' => 'Neu gallwch lusgo a gollwng ffeil yma i\'w huwchlwytho fel atodiad.', + 'attachments_set_link' => 'Gosod Dolen', + 'attachments_delete' => 'Ydych chi\'n siŵr eich bod eisiau dileu\'r atodiad hwn?', + 'attachments_dropzone' => 'Gollyngwch ffeiliau yma i\'w huwchlwytho', + 'attachments_no_files' => 'Nid oes unrhyw ffeiliau wedi\'u huwchlwytho', + 'attachments_explain_link' => 'Gallwch atodi dolen pe bai’n well gennych beidio ag uwchlwytho ffeil. Gall hyn fod yn ddolen i dudalen arall neu\'n ddolen i ffeil yn y cwmwl.', + 'attachments_link_name' => 'Enw’r Ddolen', + 'attachment_link' => 'Dolen atodiad', + 'attachments_link_url' => 'Dolen i ffeil', + 'attachments_link_url_hint' => 'Url y safle neu ffeil', + 'attach' => 'Atodi', + 'attachments_insert_link' => 'Ychwanegu Dolen Atodiad i Dudalen', + 'attachments_edit_file' => 'Golygu Ffeil', + 'attachments_edit_file_name' => 'Enw\'r Ffeil', + 'attachments_edit_drop_upload' => 'Gollwng ffeiliau neu glicio yma i uwchlwytho ac arysgrifennu', + 'attachments_order_updated' => 'Trefn atodiad wedi’i diweddaru', + 'attachments_updated_success' => 'Manylion yr atodiad wedi\'u diweddaru', + 'attachments_deleted' => 'Atodiad wedi’i ddileu', + 'attachments_file_uploaded' => 'Ffeil wedi\'i huwchwytho\'n llwyddiannus', + 'attachments_file_updated' => 'Ffeil wedi\'i diweddaru’n llwyddiannus', + 'attachments_link_attached' => 'Dolen wedi\'i atodi’n llwyddiannus i\'r dudalen', + 'templates' => 'Templedi', + 'templates_set_as_template' => 'Mae\'r dudalen yn dempled', + 'templates_explain_set_as_template' => 'Gallwch osod y dudalen hon fel templed er mwyn gallu defnyddio ei chynnwys wrth greu tudalennau eraill. Bydd modd i ddefnyddwyr eraill ddefnyddio\'r templed hwn os oes ganddynt ganiatâd gweld ar gyfer y dudalen hon.', + 'templates_replace_content' => 'Disodli cynnwys tudalen', + 'templates_append_content' => 'Atodi i gynnwys tudalen', + 'templates_prepend_content' => 'Rhagarweiniad i gynnwys tudalen', // Profile View - 'profile_user_for_x' => 'User for :time', - 'profile_created_content' => 'Created Content', - 'profile_not_created_pages' => ':userName has not created any pages', - 'profile_not_created_chapters' => ':userName has not created any chapters', - 'profile_not_created_books' => ':userName has not created any books', - 'profile_not_created_shelves' => ':userName has not created any shelves', + 'profile_user_for_x' => 'Defnyddiwr am :time', + 'profile_created_content' => 'Cynnwys a Grëwyd', + 'profile_not_created_pages' => 'Nid yw :userName wedi creu unrhyw dudalennau', + 'profile_not_created_chapters' => 'Nid yw :userName wedi creu unrhyw benodau', + 'profile_not_created_books' => 'Nid yw :userName wedi creu unrhyw lyfrau', + 'profile_not_created_shelves' => 'Nid yw :userName wedi creu unrhyw silffoedd', // Comments - 'comment' => 'Comment', - 'comments' => 'Comments', - 'comment_add' => 'Add Comment', - 'comment_placeholder' => 'Leave a comment here', - 'comment_count' => '{0} No Comments|{1} 1 Comment|[2,*] :count Comments', - 'comment_save' => 'Save Comment', - 'comment_new' => 'New Comment', - 'comment_created' => 'commented :createDiff', - 'comment_updated' => 'Updated :updateDiff by :username', - 'comment_updated_indicator' => 'Updated', - 'comment_deleted_success' => 'Comment deleted', - 'comment_created_success' => 'Comment added', - 'comment_updated_success' => 'Comment updated', - 'comment_delete_confirm' => 'Are you sure you want to delete this comment?', - 'comment_in_reply_to' => 'In reply to :commentId', - 'comment_editor_explain' => 'Here are the comments that have been left on this page. Comments can be added & managed when viewing the saved page.', + 'comment' => 'Sylw', + 'comments' => 'Sylwadau', + 'comment_add' => 'Ychwanegu Sylw', + 'comment_none' => 'No comments to display', + 'comment_placeholder' => 'Gadewch sylw yma', + 'comment_thread_count' => ':count Comment Thread|:count Comment Threads', + 'comment_archived_count' => ':count Archived', + 'comment_archived_threads' => 'Archived Threads', + 'comment_save' => 'Cadw Sylw', + 'comment_new' => 'Sylw Newydd', + 'comment_created' => 'sylwodd :createDiff', + 'comment_updated' => 'Diweddarwyd :update gan :username', + 'comment_updated_indicator' => 'Diweddarwyd', + 'comment_deleted_success' => 'Dilëwyd sylw', + 'comment_created_success' => 'Ychwanegwyd sylw', + 'comment_updated_success' => 'Diweddarwyd sylw', + 'comment_archive_success' => 'Comment archived', + 'comment_unarchive_success' => 'Comment un-archived', + 'comment_view' => 'View comment', + 'comment_jump_to_thread' => 'Jump to thread', + 'comment_delete_confirm' => 'Ydych chi\'n siwr eich bod eisiau dileu\'r sylw hwn?', + 'comment_in_reply_to' => 'Mewn ymateb i :commentId', + 'comment_reference' => 'Reference', + 'comment_reference_outdated' => '(Outdated)', + 'comment_editor_explain' => 'Dyma\'r sylwadau sydd wedi eu gadael ar y dudalen hon. Gellir ychwanegu a rheoli sylwadau wrth edrych ar y dudalen a gadwyd.', // Revision - 'revision_delete_confirm' => 'Are you sure you want to delete this revision?', - 'revision_restore_confirm' => 'Are you sure you want to restore this revision? The current page contents will be replaced.', - 'revision_cannot_delete_latest' => 'Cannot delete the latest revision.', + 'revision_delete_confirm' => 'Ydych chi\'n siŵr eich bod eisiau dileu\'r adolygiad hwn?', + 'revision_restore_confirm' => 'Ydych chi\'n siŵr eich bod eisiau adfer yr adolygiad hwn? Bydd cynnwys presennol y dudalen yn cael ei newid.', + 'revision_cannot_delete_latest' => 'Ni ellir dileu\'r adolygiad diweddaraf.', // Copy view - 'copy_consider' => 'Please consider the below when copying content.', - 'copy_consider_permissions' => 'Custom permission settings will not be copied.', - 'copy_consider_owner' => 'You will become the owner of all copied content.', - 'copy_consider_images' => 'Page image files will not be duplicated & the original images will retain their relation to the page they were originally uploaded to.', - 'copy_consider_attachments' => 'Page attachments will not be copied.', - 'copy_consider_access' => 'A change of location, owner or permissions may result in this content being accessible to those previously without access.', + 'copy_consider' => 'Ystyriwch yr isod wrth gopïo cynnwys.', + 'copy_consider_permissions' => 'Ni fydd gosodiadau caniatâd personol yn cael eu copïo.', + 'copy_consider_owner' => 'Byddwch yn dod yn berchennog yr holl gynnwys sydd wedi’i gopïo.', + 'copy_consider_images' => 'Ni fydd ffeiliau delwedd tudalen yn cael eu dyblygu a bydd y delweddau gwreiddiol yn cadw eu perthynas â\'r dudalen y cawsant eu huwchlwytho yn wreiddiol iddi.', + 'copy_consider_attachments' => 'Ni fydd atodiadau tudalen yn cael eu copïo.', + 'copy_consider_access' => 'Gall newid lleoliad, perchennog neu ganiatâd olygu bod y cynnwys hwn yn hygyrch i\'r rhai nad oedd ganddynt fynediad o\'r blaen.', // Conversions - 'convert_to_shelf' => 'Convert to Shelf', - 'convert_to_shelf_contents_desc' => 'You can convert this book to a new shelf with the same contents. Chapters contained within this book will be converted to new books. If this book contains any pages, that are not in a chapter, this book will be renamed and contain such pages, and this book will become part of the new shelf.', - 'convert_to_shelf_permissions_desc' => 'Any permissions set on this book will be copied to the new shelf and to all new child books that don\'t have their own permissions enforced. Note that permissions on shelves do not auto-cascade to content within, as they do for books.', - 'convert_book' => 'Convert Book', - 'convert_book_confirm' => 'Are you sure you want to convert this book?', - 'convert_undo_warning' => 'This cannot be as easily undone.', - 'convert_to_book' => 'Convert to Book', - 'convert_to_book_desc' => 'You can convert this chapter to a new book with the same contents. Any permissions set on this chapter will be copied to the new book but any inherited permissions, from the parent book, will not be copied which could lead to a change of access control.', - 'convert_chapter' => 'Convert Chapter', - 'convert_chapter_confirm' => 'Are you sure you want to convert this chapter?', + 'convert_to_shelf' => 'Trosi i Silff', + 'convert_to_shelf_contents_desc' => 'Gallwch drosi\'r llyfr hwn i silff newydd gyda\'r un cynnwys. Bydd penodau yn y llyfr hwn yn cael eu trosi i lyfrau newydd. Os yw\'r llyfr hwn yn cynnwys unrhyw dudalennau, nad ydynt mewn pennod, bydd y llyfr hwn yn cael ei ailenwi ac yn cynnwys tudalennau o\'r fath, a bydd y llyfr hwn yn dod yn rhan o\'r silff newydd.', + 'convert_to_shelf_permissions_desc' => 'Bydd unrhyw ganiatâd a osodir ar y llyfr hwn yn cael ei gopïo i\'r silff newydd ac i bob llyfr plentyn newydd nad oes ganddynt eu caniatâd eu hunain. Noder nad yw caniatâd ar silffoedd yn rhaeadru’n awtomatig i’r cynnwys oddi mewn, fel y maent ar gyfer llyfrau.', + 'convert_book' => 'Trosi Llyfr', + 'convert_book_confirm' => 'Ydych chi\'n siwr eich bod eisiau trosi’r llyfr hwn?', + 'convert_undo_warning' => 'Ni ellir dad-wneud hyn mor hawdd.', + 'convert_to_book' => 'Trosi i Lyfr', + 'convert_to_book_desc' => 'Gallwch drosi\'r bennod hon i lyfr newydd gyda\'r un cynnwys. Bydd unrhyw ganiatâd a osodir ar y bennod hon yn cael ei gopïo i\'r llyfr newydd ond ni fydd unrhyw ganiatâd a etifeddir o\'r llyfr rhiant yn cael ei gopïo, a allai arwain at newid rheolaeth mynediad.', + 'convert_chapter' => 'Trosi Pennod', + 'convert_chapter_confirm' => 'Ydych chi\'n siŵr eich bod eisiau trosi’r bennod hon?', // References - 'references' => 'References', - 'references_none' => 'There are no tracked references to this item.', - 'references_to_desc' => 'Listed below is all the known content in the system that links to this item.', + 'references' => 'Cyfeirnodau', + 'references_none' => 'Nid oes unrhyw gyfeirnodau wedi\'u holrhain ar gyfer yr eitem hon.', + 'references_to_desc' => 'Isod ceir yr holl gynnwys hysbys yn y system sy\'n cysylltu â\'r eitem hon.', // Watch Options - 'watch' => 'Watch', - 'watch_title_default' => 'Default Preferences', - 'watch_desc_default' => 'Revert watching to just your default notification preferences.', - 'watch_title_ignore' => 'Ignore', - 'watch_desc_ignore' => 'Ignore all notifications, including those from user-level preferences.', - 'watch_title_new' => 'New Pages', - 'watch_desc_new' => 'Notify when any new page is created within this item.', - 'watch_title_updates' => 'All Page Updates', - 'watch_desc_updates' => 'Notify upon all new pages and page changes.', - 'watch_desc_updates_page' => 'Notify upon all page changes.', - 'watch_title_comments' => 'All Page Updates & Comments', - 'watch_desc_comments' => 'Notify upon all new pages, page changes and new comments.', - 'watch_desc_comments_page' => 'Notify upon page changes and new comments.', - 'watch_change_default' => 'Change default notification preferences', - 'watch_detail_ignore' => 'Ignoring notifications', - 'watch_detail_new' => 'Watching for new pages', - 'watch_detail_updates' => 'Watching new pages and updates', - 'watch_detail_comments' => 'Watching new pages, updates & comments', - 'watch_detail_parent_book' => 'Watching via parent book', - 'watch_detail_parent_book_ignore' => 'Ignoring via parent book', - 'watch_detail_parent_chapter' => 'Watching via parent chapter', - 'watch_detail_parent_chapter_ignore' => 'Ignoring via parent chapter', + 'watch' => 'Gwylio', + 'watch_title_default' => 'Dewisiadau Diofyn', + 'watch_desc_default' => 'Newid i weld eich dewisiadau hysbysu diofyn yn unig.', + 'watch_title_ignore' => 'Anwybyddu', + 'watch_desc_ignore' => 'Anwybyddu pob hysbysiad, gan gynnwys y rhai o ddewisiadau lefel defnyddiwr.', + 'watch_title_new' => 'Tudalennau Newydd', + 'watch_desc_new' => 'Rhoi gwybod pan fydd unrhyw dudalen newydd yn cael ei chreu yn yr eitem hon.', + 'watch_title_updates' => 'Diweddariadau Pob Tudalen', + 'watch_desc_updates' => 'Hysbysu am bob tudalen newydd a newid i dudalennau.', + 'watch_desc_updates_page' => 'Hysbysu am bob newid i dudalennau.', + 'watch_title_comments' => 'Pob Diweddariad i Dualennau a Sylwadau', + 'watch_desc_comments' => 'Hysbysu am bob tudalen newydd, newidiadau i dudalennau a sylwadau newydd.', + 'watch_desc_comments_page' => 'Hysbysu am newidiadau i dudalennau a sylwadau newydd.', + 'watch_change_default' => 'Newid dewisiadau hysbysu diofyn', + 'watch_detail_ignore' => 'Anwybyddu hysbysiadau', + 'watch_detail_new' => 'Gwylio am dudalennau newydd', + 'watch_detail_updates' => 'Gwylio tudalennau a diweddariadau newydd', + 'watch_detail_comments' => 'Gwylio tudalennau newydd, diweddariadau a sylwadau', + 'watch_detail_parent_book' => 'Gwylio trwy lyfr rhiant', + 'watch_detail_parent_book_ignore' => 'Anwybyddu trwy lyfr rhiant', + 'watch_detail_parent_chapter' => 'Gwylio trwy bennod rhiant', + 'watch_detail_parent_chapter_ignore' => 'Anwybyddu trwy bennod rhiant', ]; diff --git a/lang/cy/errors.php b/lang/cy/errors.php index a85f74fd569..db3a468b60a 100644 --- a/lang/cy/errors.php +++ b/lang/cy/errors.php @@ -10,6 +10,7 @@ // Auth 'error_user_exists_different_creds' => 'Mae defnyddiwr gyda\'r e-bost :email eisoes yn bodoli ond gyda nodweddion gwahanol.', + 'auth_pre_register_theme_prevention' => 'Nid oedd modd cofrestru cyfrif defnyddiwr ar gyfer y manylion a ddarparwyd', 'email_already_confirmed' => 'E-bost eisoes wedi\'i gadarnhau, Ceisiwch fewngofnodi.', 'email_confirmation_invalid' => 'Nid yw\'r tocyn cadarnhau hwn yn ddilys neu mae eisoes wedi\'i ddefnyddio. Ceisiwch gofrestru eto.', 'email_confirmation_expired' => 'Mae\'r tocyn cadarnhad wedi dod i ben, Mae e-bost cadarnhau newydd wedi\'i anfon.', @@ -23,7 +24,6 @@ 'saml_invalid_response_id' => 'Nid yw\'r cais o\'r system ddilysu allanol yn cael ei gydnabod gan broses a ddechreuwyd gan y cais hwn. Gallai llywio yn ôl ar ôl mewngofnodi achosi\'r broblem hon.', 'saml_fail_authed' => 'Wedi methu mewngofnodi gan ddefnyddio :system, ni roddodd y system awdurdodiad llwyddiannus', 'oidc_already_logged_in' => 'Wedi mewngofnodi yn barod', - 'oidc_user_not_registered' => 'Nid yw\'r defnyddiwr :name wedi\'i gofrestru ac mae cofrestriad awtomatig wedi\'i analluogi', 'oidc_no_email_address' => 'Methu dod o hyd i gyfeiriad e-bost, ar gyfer y defnyddiwr hwn, yn y data a ddarparwyd gan y system ddilysu allanol', 'oidc_fail_authed' => 'Wedi methu mewngofnodi gan ddefnyddio :system, ni roddodd y system awdurdodiad llwyddiannus', 'social_no_action_defined' => 'Dim gweithred wedi\'i diffinio', @@ -37,36 +37,37 @@ 'social_driver_not_found' => 'Gyrrwr cymdeithasol heb ei ganfod', 'social_driver_not_configured' => 'Nid yw eich gosodiadau cymdeithasol :socialAccount wedi\'u ffurfweddu\'n gywir.', 'invite_token_expired' => 'Mae\'r ddolen wahoddiad hon wedi dod i ben. Yn lle hynny, gallwch chi geisio ailosod cyfrinair eich cyfrif.', + 'login_user_not_found' => 'Nid oedd modd dod o hyd i ddefnyddiwr ar gyfer y weithred hon.', // System 'path_not_writable' => 'Nid oedd modd uwchlwytho llwybr ffeil :filePath. Sicrhewch ei fod yn ysgrifenadwy i\'r gweinydd.', 'cannot_get_image_from_url' => 'Methu cael delwedd o :url', 'cannot_create_thumbs' => 'Ni all y gweinydd greu mân-luniau. Gwiriwch fod gennych yr estyniad GD PHP wedi\'i osod.', 'server_upload_limit' => 'Nid yw\'r gweinydd yn caniatáu uwchlwythiadau o\'r maint hwn. Rhowch gynnig ar faint ffeil llai.', - 'server_post_limit' => 'The server cannot receive the provided amount of data. Try again with less data or a smaller file.', + 'server_post_limit' => 'Ni all y gweinydd dderbyn y swm o ddata a ddarparwyd. Rhowch gynnig arall arni eto gyda llai o ddata neu ffeil lai.', 'uploaded' => 'Nid yw\'r gweinydd yn caniatáu uwchlwythiadau o\'r maint hwn. Rhowch gynnig ar faint ffeil llai.', // Drawing & Images 'image_upload_error' => 'Bu gwall wrth uwchlwytho\'r ddelwedd', 'image_upload_type_error' => 'Mae\'r math o ddelwedd sy\'n cael ei huwchlwytho yn annilys', - 'image_upload_replace_type' => 'Image file replacements must be of the same type', - 'image_upload_memory_limit' => 'Failed to handle image upload and/or create thumbnails due to system resource limits.', - 'image_thumbnail_memory_limit' => 'Failed to create image size variations due to system resource limits.', - 'image_gallery_thumbnail_memory_limit' => 'Failed to create gallery thumbnails due to system resource limits.', - 'drawing_data_not_found' => 'Drawing data could not be loaded. The drawing file might no longer exist or you may not have permission to access it.', + 'image_upload_replace_type' => 'Rhaid i ffeiliau delwedd a newidir fod o\'r un math', + 'image_upload_memory_limit' => 'Methwyd â thrin y llun a uwchlwythwyd a/neu greu mân-luniau oherwydd cyfyngiadau i adnoddau’r system.', + 'image_thumbnail_memory_limit' => 'Methwyd â chreu amrywiadau i faint y llun oherwydd cyfyngiadau i adnoddau’r system.', + 'image_gallery_thumbnail_memory_limit' => 'Methwyd â chreu oriel o fân-luniau oherwydd cyfyngiadau i adnoddau’r system.', + 'drawing_data_not_found' => 'Nid oedd modd llwytho\'r data dylunio. Efallai nad yw’r ffeil ddylunio yn bodoli mwyach neu efallai nad oes gennych ganiatâd i\'w defnyddio.', // Attachments 'attachment_not_found' => 'Ni chanfuwyd yr atodiad', - 'attachment_upload_error' => 'An error occurred uploading the attachment file', + 'attachment_upload_error' => 'Digwyddodd gwall wrth uwchlwytho’r ffeil atodiad', // Pages 'page_draft_autosave_fail' => 'Wedi methu cadw\'r drafft. Sicrhewch fod gennych gysylltiad rhyngrwyd cyn cadw\'r dudalen hon', - 'page_draft_delete_fail' => 'Failed to delete page draft and fetch current page saved content', + 'page_draft_delete_fail' => 'Methwyd â dileu’r dudalen ddrafft a chyrchu cynnwys y dudalen gyfredol', 'page_custom_home_deletion' => 'Methu dileu tudalen tra ei bod wedi\'i gosod fel hafan', // Entities 'entity_not_found' => 'Endid heb ei ganfod', - 'bookshelf_not_found' => 'Shelf not found', + 'bookshelf_not_found' => 'Ni chanfuwyd y silff', 'book_not_found' => 'Ni chanfuwyd y llyfr', 'page_not_found' => 'Heb ganfod y dudalen', 'chapter_not_found' => 'Pennod heb ei chanfod', @@ -77,6 +78,7 @@ // Users 'users_cannot_delete_only_admin' => 'Ni allwch ddileu\'r unig weinyddwr', 'users_cannot_delete_guest' => 'Ni allwch ddileu\'r defnyddiwr gwadd', + 'users_could_not_send_invite' => 'Methu creu defnyddiwr oherwydd ni fu modd anfon e-bost gwahodd', // Roles 'role_cannot_be_edited' => 'Nid oes modd golygu\'r rôl hon', @@ -103,6 +105,18 @@ 'app_down' => 'Mae :appName i lawr ar hyn o bryd', 'back_soon' => 'Bydd yn ôl i fyny yn fuan.', + // Import + 'import_zip_cant_read' => 'Wedi methu darllen ffeil ZIP.', + 'import_zip_cant_decode_data' => 'Wedi methu ffeindio a dadgodio cynnwys ZIP data.json.', + 'import_zip_no_data' => 'Nid oes cynnwys llyfr, pennod neu dudalen disgwyliedig yn nata ffeil ZIP.', + 'import_validation_failed' => 'ZIP mewnforyn wedi\'i methu dilysu gyda gwallau:', + 'import_zip_failed_notification' => 'Wedi methu mewnforio ffeil ZIP.', + 'import_perms_books' => 'Dych chi\'n methu\'r caniatâd gofynnol i greu llyfrau.', + 'import_perms_chapters' => 'Dych chi\'n methu\'r caniatâd gofynnol i greu pennodau.', + 'import_perms_pages' => 'Dych chi\'n methu\'r caniatâd gofynnol i greu tudalennau.', + 'import_perms_images' => 'Dych chi\'n methu\'r caniatâd gofynnol i greu delwau.', + 'import_perms_attachments' => 'Dych chi\'n methu\'r caniatâd gofynnol i greu atodiadau.', + // API errors 'api_no_authorization_found' => 'Ni chanfuwyd tocyn awdurdodi ar y cais', 'api_bad_authorization_format' => 'Canfuwyd tocyn awdurdodi ar y cais ond roedd yn ymddangos bod y fformat yn anghywir', @@ -115,5 +129,5 @@ 'maintenance_test_email_failure' => 'Gwall a daflwyd wrth anfon e-bost prawf:', // HTTP errors - 'http_ssr_url_no_match' => 'The URL does not match the configured allowed SSR hosts', + 'http_ssr_url_no_match' => 'Nid yw\'r URL yn cyd-fynd â\'r gwesteion SSR ffurfweddu a ganiateir', ]; diff --git a/lang/cy/notifications.php b/lang/cy/notifications.php index b9f58ac2576..26f65f1447d 100644 --- a/lang/cy/notifications.php +++ b/lang/cy/notifications.php @@ -10,18 +10,18 @@ 'new_page_intro' => 'Mae tudalen newydd wedi cael ei chreu yn :appName:', 'updated_page_subject' => 'Tudalen wedi\'i diweddaru :pageName', 'updated_page_intro' => 'Mae tudalen newydd wedi cael ei diweddaru yn :appName:', - 'updated_page_debounce' => 'To prevent a mass of notifications, for a while you won\'t be sent notifications for further edits to this page by the same editor.', + 'updated_page_debounce' => 'Er mwyn atal llu o hysbysiadau, am gyfnod ni fyddwch yn cael hysbysiadau am ragor o olygiadau i\'r dudalen hon gan yr un golygydd.', 'detail_page_name' => 'Enw\'r dudalen:', - 'detail_page_path' => 'Page Path:', - 'detail_commenter' => 'Commenter:', + 'detail_page_path' => 'Llwybr Tudalen:', + 'detail_commenter' => 'Sylwebydd:', 'detail_comment' => 'Sylw:', - 'detail_created_by' => 'Created By:', - 'detail_updated_by' => 'Updated By:', + 'detail_created_by' => 'Crëwyd gan:', + 'detail_updated_by' => 'Diweddarwyd Gan:', 'action_view_comment' => 'Gweld y sylw', 'action_view_page' => 'Gweld y dudalen', - 'footer_reason' => 'This notification was sent to you because :link cover this type of activity for this item.', - 'footer_reason_link' => 'your notification preferences', + 'footer_reason' => 'Anfonwyd yr hysbysiad hwn atoch oherwydd bod y :ddolen yn ymdrin â’r math hwn o weithgaredd ar gyfer yr eitem hon.', + 'footer_reason_link' => 'eich dewisiadau hysbysu', ]; diff --git a/lang/cy/passwords.php b/lang/cy/passwords.php index b408f3c2fda..992c1d09321 100644 --- a/lang/cy/passwords.php +++ b/lang/cy/passwords.php @@ -6,10 +6,10 @@ */ return [ - 'password' => 'Passwords must be at least eight characters and match the confirmation.', - 'user' => "We can't find a user with that e-mail address.", - 'token' => 'The password reset token is invalid for this email address.', - 'sent' => 'We have e-mailed your password reset link!', - 'reset' => 'Your password has been reset!', + 'password' => 'Rhaid i gyfrineiriau fod yn 8 nod o leiaf ac yn cyd-fynd â\'r cadarnhad.', + 'user' => "Ni allwn ddod o hyd i ddefnyddiwr gyda'r cyfeiriad e-bost hwn.", + 'token' => 'Mae\'r tocyn ailosod cyfrinair yn annilys ar gyfer y cyfeiriad e-bost hwn.', + 'sent' => 'Rydym wedi e-bostio eich dolen ailosod cyfrinair!', + 'reset' => 'Mae eich cyfrinair wedi\'i ailosod!', ]; diff --git a/lang/cy/preferences.php b/lang/cy/preferences.php index 2b88f9671ff..6e00adfe8ee 100644 --- a/lang/cy/preferences.php +++ b/lang/cy/preferences.php @@ -5,47 +5,47 @@ */ return [ - 'my_account' => 'My Account', + 'my_account' => 'Fy Nghyfrif', - 'shortcuts' => 'Shortcuts', - 'shortcuts_interface' => 'UI Shortcut Preferences', - 'shortcuts_toggle_desc' => 'Here you can enable or disable keyboard system interface shortcuts, used for navigation and actions.', - 'shortcuts_customize_desc' => 'You can customize each of the shortcuts below. Just press your desired key combination after selecting the input for a shortcut.', - 'shortcuts_toggle_label' => 'Keyboard shortcuts enabled', - 'shortcuts_section_navigation' => 'Navigation', - 'shortcuts_section_actions' => 'Common Actions', - 'shortcuts_save' => 'Save Shortcuts', - 'shortcuts_overlay_desc' => 'Note: When shortcuts are enabled a helper overlay is available via pressing "?" which will highlight the available shortcuts for actions currently visible on the screen.', - 'shortcuts_update_success' => 'Shortcut preferences have been updated!', - 'shortcuts_overview_desc' => 'Manage keyboard shortcuts you can use to navigate the system user interface.', + 'shortcuts' => 'Llwybrau Byr', + 'shortcuts_interface' => 'Dewisiadau Llwybr Byr UI', + 'shortcuts_toggle_desc' => 'Yma gallwch alluogi neu analluogi llwybrau byr rhyngwyneb system ar y bysellfwrdd, a ddefnyddir ar gyfer llywio a gweithredoedd.', + 'shortcuts_customize_desc' => 'Gallwch addasu pob un o\'r llwybrau byr isod. Pwyswch eich cyfuniad o fysellau ar ôl dewis y mewnbwn ar gyfer llwybr byr.', + 'shortcuts_toggle_label' => 'Llwybrau byr bysellfwrdd wedi\'u galluogi', + 'shortcuts_section_navigation' => 'Llywio', + 'shortcuts_section_actions' => 'Gweithredoedd Cyffredin', + 'shortcuts_save' => 'Cadw Llwybrau Byr', + 'shortcuts_overlay_desc' => 'Noder: Pan fydd llwybrau byr yn cael eu galluogi, mae troshaen helpwr ar gael trwy bwyso "?" a fydd yn tynnu sylw at y llwybrau byr sydd ar gael ar gyfer camau gweithredu sydd i\'w gweld ar y sgrin ar hyn o bryd.', + 'shortcuts_update_success' => 'Mae’r dewisiadau llwybr byr wedi\'u diweddaru!', + 'shortcuts_overview_desc' => 'Rheoli llwybrau byr bysellfwrdd a gellir eu defnyddio i lywio trwy ryngwyneb defnyddiwr y system.', - 'notifications' => 'Notification Preferences', - 'notifications_desc' => 'Control the email notifications you receive when certain activity is performed within the system.', - 'notifications_opt_own_page_changes' => 'Notify upon changes to pages I own', - 'notifications_opt_own_page_comments' => 'Notify upon comments on pages I own', - 'notifications_opt_comment_replies' => 'Notify upon replies to my comments', - 'notifications_save' => 'Save Preferences', - 'notifications_update_success' => 'Notification preferences have been updated!', - 'notifications_watched' => 'Watched & Ignored Items', - 'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.', + 'notifications' => 'Dewisiadau Hysbysu', + 'notifications_desc' => 'Rheoli’r hysbysiadau e-bost a gewch pan fydd gweithgaredd penodol yn cael ei gyflawni o fewn y system.', + 'notifications_opt_own_page_changes' => 'Hysbysu am newidiadau i dudalennau yr wyf yn berchen arnynt', + 'notifications_opt_own_page_comments' => 'Hysbysu am sylwadau ar dudalennau yr wyf yn berchen arnynt', + 'notifications_opt_comment_replies' => 'Hysbysu am atebion i\'m sylwadau', + 'notifications_save' => 'Dewisiadau Cadw', + 'notifications_update_success' => 'Mae’r dewisiadau hysbysu wedi\'u diweddaru!', + 'notifications_watched' => 'Eitemau Gwylio ac Anwybyddu', + 'notifications_watched_desc' => 'Isod ceir yr eitemau sydd â dewisiadau gwylio penodol wedi\'u cymhwyso. I ddiweddaru eich dewisiadau ar gyfer y rhain, edrychwch ar yr eitem yna dewch o hyd i\'r opsiynau gwylio yn y bar ochr.', - 'auth' => 'Access & Security', - 'auth_change_password' => 'Change Password', - 'auth_change_password_desc' => 'Change the password you use to log-in to the application. This must be at least 8 characters long.', - 'auth_change_password_success' => 'Password has been updated!', + 'auth' => 'Mynediad a Diogelwch', + 'auth_change_password' => 'Newid Cyfrinair', + 'auth_change_password_desc' => 'Newidiwch y cyfrinair rydych chi’n ei ddefnyddio i fewngofnodi i’r rhaglen. Rhaid i gyfrineiriau fod yn 8 nod o leiaf.', + 'auth_change_password_success' => 'Mae\'r cyfrinair wedi\'i ddiweddaru!', - 'profile' => 'Profile Details', - 'profile_desc' => 'Manage the details of your account which represents you to other users, in addition to details that are used for communication and system personalisation.', - 'profile_view_public' => 'View Public Profile', - 'profile_name_desc' => 'Configure your display name which will be visible to other users in the system through the activity you perform, and content you own.', - 'profile_email_desc' => 'This email will be used for notifications and, depending on active system authentication, system access.', - 'profile_email_no_permission' => 'Unfortunately you don\'t have permission to change your email address. If you want to change this, you\'d need to ask an administrator to change this for you.', - 'profile_avatar_desc' => 'Select an image which will be used to represent yourself to others in the system. Ideally this image should be square and about 256px in width and height.', - 'profile_admin_options' => 'Administrator Options', - 'profile_admin_options_desc' => 'Additional administrator-level options, like those to manage role assignments, can be found for your user account in the "Settings > Users" area of the application.', + 'profile' => 'Manylion Proffil', + 'profile_desc' => 'Rheoli manylion eich cyfrif sy\'n eich cynrychioli chi i ddefnyddwyr eraill, yn ogystal â manylion a ddefnyddir ar gyfer cyfathrebu a phersonoli systemau.', + 'profile_view_public' => 'Gweld Proffil Cyhoeddus', + 'profile_name_desc' => 'Ffurfweddwch eich enw arddangos a fydd yn weladwy i ddefnyddwyr eraill yn y system trwy\'r hyn yr ydych yn ei wneud, a\'r cynnwys rydych chi\'n berchen arno.', + 'profile_email_desc' => 'Bydd yr e-bost hwn yn cael ei ddefnyddio ar gyfer hysbysiadau a, gan ddibynnu ar ddilysiad system gweithredol, mynediad i’r system.', + 'profile_email_no_permission' => 'Yn anffodus, nid oes gennych ganiatâd i newid eich cyfeiriad e-bost. Os hoffech newid hwn, byddai angen i chi ofyn i weinyddwr newid hyn ar eich rhan.', + 'profile_avatar_desc' => 'Dewiswch lun a fydd yn cael ei ddefnyddio i’ch cynrychioli chi i eraill yn y system. Yn ddelfrydol, dylai\'r llun hwn fod yn sgwâr a thua 256px o led ac uchder.', + 'profile_admin_options' => 'Dewisiadau Gweinyddwr', + 'profile_admin_options_desc' => 'Gellir dod o hyd i ddewisiadau lefel gweinyddwyr ychwanegol, fel y rhai i reoli aseiniadau rôl, ar gyfer eich cyfrif defnyddiwr yn yr ardal "Gosodiadau > Defnyddiwr” o’r rhaglen.', - 'delete_account' => 'Delete Account', - 'delete_my_account' => 'Delete My Account', - 'delete_my_account_desc' => 'This will fully delete your user account from the system. You will not be able to recover this account or revert this action. Content you\'ve created, such as created pages and uploaded images, will remain.', - 'delete_my_account_warning' => 'Are you sure you want to delete your account?', + 'delete_account' => 'Dileu Cyfrif', + 'delete_my_account' => 'Dileu fy Nghyfrif', + 'delete_my_account_desc' => 'Bydd hyn yn dileu eich cyfrif defnyddiwr o\'r system yn llwyr. Ni fydd modd i chi adfer y cyfrif hwn na gwrthdroi\'r weithred hon. Bydd cynnwys rydych chi wedi\'i greu, megis tudalennau wedi\'u creu a delweddau wedi\'u huwchlwytho, yn parhau.', + 'delete_my_account_warning' => 'Ydych chi\'n siŵr eich bod eisiau dileu eich cyfrif?', ]; diff --git a/lang/cy/settings.php b/lang/cy/settings.php index 03e9bf462e2..cb08e8c9068 100644 --- a/lang/cy/settings.php +++ b/lang/cy/settings.php @@ -7,275 +7,314 @@ return [ // Common Messages - 'settings' => 'Settings', - 'settings_save' => 'Save Settings', - 'system_version' => 'System Version', - 'categories' => 'Categories', + 'settings' => 'Gosodiadau', + 'settings_save' => 'Cadw Gosodiadau', + 'system_version' => 'Fersiwn System', + 'categories' => 'Categorïau', // App Settings - 'app_customization' => 'Customization', - 'app_features_security' => 'Features & Security', - 'app_name' => 'Application Name', - 'app_name_desc' => 'This name is shown in the header and in any system-sent emails.', - 'app_name_header' => 'Show name in header', - 'app_public_access' => 'Public Access', - 'app_public_access_desc' => 'Enabling this option will allow visitors, that are not logged-in, to access content in your BookStack instance.', - 'app_public_access_desc_guest' => 'Access for public visitors can be controlled through the "Guest" user.', - 'app_public_access_toggle' => 'Allow public access', - 'app_public_viewing' => 'Allow public viewing?', - 'app_secure_images' => 'Higher Security Image Uploads', - 'app_secure_images_toggle' => 'Enable higher security image uploads', - 'app_secure_images_desc' => 'For performance reasons, all images are public. This option adds a random, hard-to-guess string in front of image urls. Ensure directory indexes are not enabled to prevent easy access.', - 'app_default_editor' => 'Default Page Editor', - 'app_default_editor_desc' => 'Select which editor will be used by default when editing new pages. This can be overridden at a page level where permissions allow.', - 'app_custom_html' => 'Custom HTML Head Content', - 'app_custom_html_desc' => 'Any content added here will be inserted into the bottom of the section of every page. This is handy for overriding styles or adding analytics code.', - 'app_custom_html_disabled_notice' => 'Custom HTML head content is disabled on this settings page to ensure any breaking changes can be reverted.', - 'app_logo' => 'Application Logo', - 'app_logo_desc' => 'This is used in the application header bar, among other areas. This image should be 86px in height. Large images will be scaled down.', - 'app_icon' => 'Application Icon', - 'app_icon_desc' => 'This icon is used for browser tabs and shortcut icons. This should be a 256px square PNG image.', - 'app_homepage' => 'Application Homepage', - 'app_homepage_desc' => 'Select a view to show on the homepage instead of the default view. Page permissions are ignored for selected pages.', - 'app_homepage_select' => 'Select a page', - 'app_footer_links' => 'Footer Links', - 'app_footer_links_desc' => 'Add links to show within the site footer. These will be displayed at the bottom of most pages, including those that do not require login. You can use a label of "trans::" to use system-defined translations. For example: Using "trans::common.privacy_policy" will provide the translated text "Privacy Policy" and "trans::common.terms_of_service" will provide the translated text "Terms of Service".', - 'app_footer_links_label' => 'Link Label', - 'app_footer_links_url' => 'Link URL', - 'app_footer_links_add' => 'Add Footer Link', - 'app_disable_comments' => 'Disable Comments', - 'app_disable_comments_toggle' => 'Disable comments', - 'app_disable_comments_desc' => 'Disables comments across all pages in the application.
Existing comments are not shown.', + 'app_customization' => 'Addasiad', + 'app_features_security' => 'Nodweddion & Diogelwch', + 'app_name' => 'Enw\'r Rhaglen', + 'app_name_desc' => 'Dangosir yr enw hwn yn y pennawd ac mewn unrhyw negeseuon e-bost a anfonir gan y system.', + 'app_name_header' => 'Dangos enw yn y pennyn', + 'app_public_access' => 'Mynediad Cyhoeddus', + 'app_public_access_desc' => 'Bydd galluogi\'r opsiwn hwn yn caniatáu i ymwelwyr, nad ydynt wedi mewngofnodi, gael mynediad at gynnwys yn achos BookStack.', + 'app_public_access_desc_guest' => 'Gellir rheoli mynediad i ymwelwyr cyhoeddus trwy\'r defnyddiwr "Gwestai".', + 'app_public_access_toggle' => 'Caniatáu mynediad i\'r cyhoedd', + 'app_public_viewing' => 'Caniatáu i\'r cyhoedd weld?', + 'app_secure_images' => 'Uwchlwytho Delwedd Diogelwch Uwch', + 'app_secure_images_toggle' => 'Galluogi uwchlwytho delwedd diogelwch uwch', + 'app_secure_images_desc' => 'Am resymau perfformiad, mae\'r holl ddelweddau yn gyhoeddus. Mae\'r opsiwn hwn yn ychwanegu llinyn ar hap, anodd ei ddyfalu o flaen urls delwedd. Sicrhau nad yw mynegeion cyfeiriadur yn cael eu galluogi i atal mynediad hawdd.', + 'app_default_editor' => 'Golygydd Tudalen Diofyn', + 'app_default_editor_desc' => 'Dewiswch pa olygydd fydd yn cael ei ddefnyddio yn ddiofyn wrth olygu tudalennau newydd. Gellir diystyru hyn ar lefel tudalen os yw’r caniatâd yn galluogi hyn.', + 'app_custom_html' => 'Cynnwys Pen HTML wedi’i deilwra', + 'app_custom_html_desc' => 'Bydd unrhyw gynnwys a ychwanegir yma yn cael ei fewnosod i waelod yr adran ar bob tudalen. Mae hyn yn ddefnyddiol ar gyfer diystyru arddulliau neu ychwanegu cod dadansoddeg.', + 'app_custom_html_disabled_notice' => 'Mae cynnwys pen HTML wedi\'i deilwra wedi’i analluogi ar y dudalen gosodiadau hon i sicrhau y gellir troi unrhyw newidiadau toriadol yn ôl.', + 'app_logo' => 'Logo’r Rhaglen', + 'app_logo_desc' => 'Defnyddir hwn ym mar pennawd y rhaglen, ymhlith ardaloedd eraill. Dylai\'r ddelwedd hon fod yn 86px o uchder. Bydd delweddau mawr yn cael eu graddio i lawr.', + 'app_icon' => 'Eicon yr Ap', + 'app_icon_desc' => 'Defnyddir yr eicon hwn ar gyfer tabiau porwr ac eiconau llwybr byr. Dylai hwn fod yn ddelwedd PNG sgwâr 256px.', + 'app_homepage' => 'Hafan y Rhaglen', + 'app_homepage_desc' => 'Dewiswch wedd i\'w dangos ar yr hafan yn hytrach na\'r wedd ddiofyn. Anwybyddir caniatâd tudalen ar gyfer tudalennau dethol.', + 'app_homepage_select' => 'Dewiswch dudalen', + 'app_footer_links' => 'Dolenni Troedynnau', + 'app_footer_links_desc' => 'Ychwanegwch ddolenni i\'w dangos o fewn troedyn y safle. Bydd y rhain yn cael eu harddangos ar waelod y rhan fwyaf o dudalennau, gan gynnwys y rhai nad oes angen mewngofnodi. Gallwch ddefnyddio label "trans:: i ddefnyddio cyfieithiadau wedi\'u diffinio gan y system. Er enghraifft: Bydd defnyddio "trans::common.privacy_policy" yn darparu\'r testun wedi’i gyfieithu "Polisi Preifatrwydd" a "trans::common.terms_of_service" yn darparu\'r testun wedi’i gyfieithu "Telerau Gwasanaeth".', + 'app_footer_links_label' => 'Label Dolen', + 'app_footer_links_url' => 'URL Dolen', + 'app_footer_links_add' => 'Ychwanegu Dolen Troedyn', + 'app_disable_comments' => 'Analluogi Sylwadau', + 'app_disable_comments_toggle' => 'Analluogi sylwadau', + 'app_disable_comments_desc' => 'Analluogi sylwadau ar draws pob tudalen yn y rhaglen.
Nid yw\'r sylwadau presennol yn cael eu dangos.', // Color settings - 'color_scheme' => 'Application Color Scheme', - 'color_scheme_desc' => 'Set the colors to use in the application user interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.', - 'ui_colors_desc' => 'Set the application primary color and default link color. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the application interface.', - 'app_color' => 'Primary Color', - 'link_color' => 'Default Link Color', - 'content_colors_desc' => 'Set colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.', - 'bookshelf_color' => 'Shelf Color', - 'book_color' => 'Book Color', - 'chapter_color' => 'Chapter Color', - 'page_color' => 'Page Color', - 'page_draft_color' => 'Page Draft Color', + 'color_scheme' => 'Cynllun Lliw’r Rhaglen', + 'color_scheme_desc' => 'Gosodwch y lliwiau i\'w defnyddio yn rhyngwyneb defnyddiwr y rhaglen. Gellir ffurfweddu lliwiau ar wahân ar gyfer moddau tywyll a golau i gyd-fynd â\'r thema orau a sicrhau ei fod yn ddarllenadwy.', + 'ui_colors_desc' => 'Gosodwch liw sylfaenol y rhaglen a’r lliw diofyn y ddolen. Defnyddir y lliw sylfaenol yn bennaf ar gyfer baner y pennyn, y botymau ac addurniadau rhyngwyneb. Defnyddir lliw diofyn y ddolen ar gyfer dolenni a chamau gweithredu testun, o fewn cynnwys ysgrifenedig ac yn rhyngwyneb y rhaglen.', + 'app_color' => 'Prif Liw', + 'link_color' => 'Lliw Diofyn y Ddolen', + 'content_colors_desc' => 'Gosodwch liwiau ar gyfer pob elfen yn hierarchaeth trefn y dudalen. Argymhellir dewis lliwiau â disgleirdeb tebyg i\'r lliwiau diofyn ar gyfer darllenadwyedd.', + 'bookshelf_color' => 'Lliw Silff', + 'book_color' => 'Lliw Llyfr', + 'chapter_color' => 'Lliw Pennod', + 'page_color' => 'Lliw Tudalen', + 'page_draft_color' => 'Lliw Tudalen Ddrafft', // Registration Settings - 'reg_settings' => 'Registration', - 'reg_enable' => 'Enable Registration', - 'reg_enable_toggle' => 'Enable registration', - 'reg_enable_desc' => 'When registration is enabled user will be able to sign themselves up as an application user. Upon registration they are given a single, default user role.', - 'reg_default_role' => 'Default user role after registration', - 'reg_enable_external_warning' => 'The option above is ignored while external LDAP or SAML authentication is active. User accounts for non-existing members will be auto-created if authentication, against the external system in use, is successful.', - 'reg_email_confirmation' => 'Email Confirmation', - 'reg_email_confirmation_toggle' => 'Require email confirmation', - 'reg_confirm_email_desc' => 'If domain restriction is used then email confirmation will be required and this option will be ignored.', - 'reg_confirm_restrict_domain' => 'Domain Restriction', - 'reg_confirm_restrict_domain_desc' => 'Enter a comma separated list of email domains you would like to restrict registration to. Users will be sent an email to confirm their address before being allowed to interact with the application.
Note that users will be able to change their email addresses after successful registration.', - 'reg_confirm_restrict_domain_placeholder' => 'No restriction set', + 'reg_settings' => 'Cofrestriad', + 'reg_enable' => 'Galluogi Cofrestru', + 'reg_enable_toggle' => 'Galluogi cofrestru', + 'reg_enable_desc' => 'Pan fydd cofrestru wedi\'i alluogi bydd modd i ddefnyddwyr gofrestru fel defnyddwyr y rhaglen. Wrth gofrestru maent yn cael un rôl defnyddiwr diofyn.', + 'reg_default_role' => 'Rôl defnyddiwr diofyn ar ôl cofrestru', + 'reg_enable_external_warning' => 'Anwybyddir y dewis uchod tra bod dilysu LDAP neu SAML allanol yn weithredol. Bydd cyfrifon defnyddwyr ar gyfer aelodau nad ydynt yn bodoli yn cael eu creu\'n awtomatig os bydd dilysiad, yn erbyn y system allanol sy\'n cael ei defnyddio, yn llwyddiannus.', + 'reg_email_confirmation' => 'Cadarnhad E-bost', + 'reg_email_confirmation_toggle' => 'Angen cadarnhad e-bost', + 'reg_confirm_email_desc' => 'Os defnyddir cyfyngiad parth, bydd angen cadarnhad e-bost a bydd yr opsiwn hwn yn cael ei anwybyddu.', + 'reg_confirm_restrict_domain' => 'Cyfyngiad Parth', + 'reg_confirm_restrict_domain_desc' => 'Rhowch restr wedi\'i gwahanu gan goma o barthau e-bost yr hoffech gyfyngu cofrestriad iddynt. Bydd defnyddwyr yn cael e-bost i gadarnhau eu cyfeiriad cyn cael caniatâd i ryngweithio â\'r rhaglen.
Noder y bydd modd i ddefnyddwyr newid eu cyfeiriadau e-bost ar ôl cofrestru\'n llwyddiannus.', + 'reg_confirm_restrict_domain_placeholder' => 'Ni osodwyd cyfyngiad', + + // Sorting Settings + 'sorting' => 'Sorting', + 'sorting_book_default' => 'Default Book Sort', + 'sorting_book_default_desc' => 'Select the default sort rule to apply to new books. This won\'t affect existing books, and can be overridden per-book.', + 'sorting_rules' => 'Sort Rules', + 'sorting_rules_desc' => 'These are predefined sorting operations which can be applied to content in the system.', + 'sort_rule_assigned_to_x_books' => 'Assigned to :count Book|Assigned to :count Books', + 'sort_rule_create' => 'Create Sort Rule', + 'sort_rule_edit' => 'Edit Sort Rule', + 'sort_rule_delete' => 'Delete Sort Rule', + 'sort_rule_delete_desc' => 'Remove this sort rule from the system. Books using this sort will revert to manual sorting.', + 'sort_rule_delete_warn_books' => 'This sort rule is currently used on :count book(s). Are you sure you want to delete this?', + 'sort_rule_delete_warn_default' => 'This sort rule is currently used as the default for books. Are you sure you want to delete this?', + 'sort_rule_details' => 'Sort Rule Details', + 'sort_rule_details_desc' => 'Set a name for this sort rule, which will appear in lists when users are selecting a sort.', + 'sort_rule_operations' => 'Sort Operations', + 'sort_rule_operations_desc' => 'Configure the sort actions to be performed by moving them from the list of available operations. Upon use, the operations will be applied in order, from top to bottom. Any changes made here will be applied to all assigned books upon save.', + 'sort_rule_available_operations' => 'Available Operations', + 'sort_rule_available_operations_empty' => 'No operations remaining', + 'sort_rule_configured_operations' => 'Configured Operations', + 'sort_rule_configured_operations_empty' => 'Drag/add operations from the "Available Operations" list', + 'sort_rule_op_asc' => '(Asc)', + 'sort_rule_op_desc' => '(Desc)', + 'sort_rule_op_name' => 'Name - Alphabetical', + 'sort_rule_op_name_numeric' => '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_last' => 'Chapters Last', // Maintenance settings - 'maint' => 'Maintenance', - 'maint_image_cleanup' => 'Cleanup Images', - 'maint_image_cleanup_desc' => 'Scans page & revision content to check which images and drawings are currently in use and which images are redundant. Ensure you create a full database and image backup before running this.', - 'maint_delete_images_only_in_revisions' => 'Also delete images that only exist in old page revisions', - 'maint_image_cleanup_run' => 'Run Cleanup', - 'maint_image_cleanup_warning' => ':count potentially unused images were found. Are you sure you want to delete these images?', - 'maint_image_cleanup_success' => ':count potentially unused images found and deleted!', - 'maint_image_cleanup_nothing_found' => 'No unused images found, Nothing deleted!', - 'maint_send_test_email' => 'Send a Test Email', - 'maint_send_test_email_desc' => 'This sends a test email to your email address specified in your profile.', - 'maint_send_test_email_run' => 'Send test email', - 'maint_send_test_email_success' => 'Email sent to :address', - 'maint_send_test_email_mail_subject' => 'Test Email', - 'maint_send_test_email_mail_greeting' => 'Email delivery seems to work!', - 'maint_send_test_email_mail_text' => 'Congratulations! As you received this email notification, your email settings seem to be configured properly.', - 'maint_recycle_bin_desc' => 'Deleted shelves, books, chapters & pages are sent to the recycle bin so they can be restored or permanently deleted. Older items in the recycle bin may be automatically removed after a while depending on system configuration.', - 'maint_recycle_bin_open' => 'Open Recycle Bin', - 'maint_regen_references' => 'Regenerate References', - 'maint_regen_references_desc' => 'This action will rebuild the cross-item reference index within the database. This is usually handled automatically but this action can be useful to index old content or content added via unofficial methods.', - 'maint_regen_references_success' => 'Reference index has been regenerated!', - 'maint_timeout_command_note' => 'Note: This action can take time to run, which can lead to timeout issues in some web environments. As an alternative, this action be performed using a terminal command.', + 'maint' => 'Cynnal', + 'maint_image_cleanup' => 'Glanhau Delweddau', + 'maint_image_cleanup_desc' => 'Sganio tudalennau a chynnwys adolygu i wirio pa ddelweddau a darluniau sy\'n cael eu defnyddio ar hyn o bryd a pha ddelweddau sy\'n cael eu diddymu. Gwnewch yn siŵr eich bod yn creu cronfa ddata lawn a chopi wrth gefn o’r ddelwedd cyn rhedeg hwn.', + 'maint_delete_images_only_in_revisions' => 'Hefyd dilëwch ddelweddau sydd ond yn bodoli mewn dhen dudalennau', + 'maint_image_cleanup_run' => 'Rhedeg Glanhau', + 'maint_image_cleanup_warning' => 'Daethpwyd o hyd i :count o ddelweddau nas defnyddiwyd o bosibl. Ydych chi\'n siŵr eich bod eisiau dileu’r delweddau hyn?', + 'maint_image_cleanup_success' => 'daethpwyd o hyd i :count o ddelweddau nas defnyddiwyd o bosibl a chawsant eu dileu!', + 'maint_image_cleanup_nothing_found' => 'Daethpwyd o hyd i ddim ddelweddau, Dim byd wedi\'i dileu!', + 'maint_send_test_email' => 'Anfon E-bost Prawf', + 'maint_send_test_email_desc' => 'Mae hyn yn anfon e-bost prawf i\'ch cyfeiriad e-bost a nodir yn eich proffil.', + 'maint_send_test_email_run' => 'Anfon e-bost prawf', + 'maint_send_test_email_success' => 'Anfonwyd e-bost at :address', + 'maint_send_test_email_mail_subject' => 'E-bost Prawf', + 'maint_send_test_email_mail_greeting' => 'Mae\'n ymddangos bod anfon e-bost yn gweithio!', + 'maint_send_test_email_mail_text' => 'Llongyfarchiadau! Gan eich bod wedi derbyn yr hysbysiad e-bost hwn, mae\'n ymddangos bod eich gosodiadau e-bost wedi\'u ffurfweddu\'n iawn.', + 'maint_recycle_bin_desc' => 'Caiff silffoedd, llyfrau, penodau a thudalennau wedi\'u dileu eu hanfon i’r bin ailgylchu fel y gellir eu hadfer neu eu dileu\'n barhaol. Bydd eitemau hŷn yn y bin ailgylchu yn cael eu dileu’n awtomatig ar ôl ychydig o bosibl, gan ddibynnu ar ffurfweddiad y system.', + 'maint_recycle_bin_open' => 'Agor y Bin Ailgylchu', + 'maint_regen_references' => 'Atgynhyrchu Cyfeiriadau', + 'maint_regen_references_desc' => 'Bydd y weithred hon yn ailadeiladu\'r mynegai cyfeirio traws-eitem yn y gronfa ddata. Fel arfer, caiff hyn ei drin yn awtomatig ond gall y weithred hon fod yn ddefnyddiol i fynegeio hen gynnwys neu gynnwys a ychwanegwyd trwy ddulliau answyddogol.', + 'maint_regen_references_success' => 'Mae’r mynegai cyfeirio wedi cael ei atgynhyrchu!', + 'maint_timeout_command_note' => 'Noder: Gall y weithred hon gymryd amser i redeg, a all arwain at faterion amseru mewn rhai amgylcheddau gwe. Fel dewis arall, cyflawnir y weithred hon gan ddefnyddio gorchymyn terfynell.', // Recycle Bin - 'recycle_bin' => 'Recycle Bin', - 'recycle_bin_desc' => 'Here you can restore items that have been deleted or choose to permanently remove them from the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.', - 'recycle_bin_deleted_item' => 'Deleted Item', - 'recycle_bin_deleted_parent' => 'Parent', - 'recycle_bin_deleted_by' => 'Deleted By', - 'recycle_bin_deleted_at' => 'Deletion Time', - 'recycle_bin_permanently_delete' => 'Permanently Delete', - 'recycle_bin_restore' => 'Restore', - 'recycle_bin_contents_empty' => 'The recycle bin is currently empty', - 'recycle_bin_empty' => 'Empty Recycle Bin', - 'recycle_bin_empty_confirm' => 'This will permanently destroy all items in the recycle bin including content contained within each item. Are you sure you want to empty the recycle bin?', - 'recycle_bin_destroy_confirm' => 'This action will permanently delete this item, along with any child elements listed below, from the system and you will not be able to restore this content. Are you sure you want to permanently delete this item?', - 'recycle_bin_destroy_list' => 'Items to be Destroyed', - 'recycle_bin_restore_list' => 'Items to be Restored', - 'recycle_bin_restore_confirm' => 'This action will restore the deleted item, including any child elements, to their original location. If the original location has since been deleted, and is now in the recycle bin, the parent item will also need to be restored.', - 'recycle_bin_restore_deleted_parent' => 'The parent of this item has also been deleted. These will remain deleted until that parent is also restored.', - 'recycle_bin_restore_parent' => 'Restore Parent', - 'recycle_bin_destroy_notification' => 'Deleted :count total items from the recycle bin.', - 'recycle_bin_restore_notification' => 'Restored :count total items from the recycle bin.', + 'recycle_bin' => 'Bin Ailgylchu', + 'recycle_bin_desc' => 'Yma gallwch adfer eitemau sydd wedi\'u dileu neu ddewis eu dileu yn barhaol oddi ar y system. Nid yw’r rhestr hon wedi’i hidlo yn wahanol i restrau gweithgaredd tebyg yn y system lle mae hidlwyr caniatâd yn cael eu cymhwyso.', + 'recycle_bin_deleted_item' => 'Eitem wedi\'i Dileu', + 'recycle_bin_deleted_parent' => 'Rhiant', + 'recycle_bin_deleted_by' => 'Dilëwyd gan', + 'recycle_bin_deleted_at' => 'Amser Dileu', + 'recycle_bin_permanently_delete' => 'Dileu yn Barhaol', + 'recycle_bin_restore' => 'Adfer', + 'recycle_bin_contents_empty' => 'Mae\'r bin ailgylchu yn wag ar hyn o bryd', + 'recycle_bin_empty' => 'Gwagio’r Bin Ailgylchu', + 'recycle_bin_empty_confirm' => 'Bydd hyn yn dileu pob eitem yn y bin ailgylchu yn barhaol gan gynnwys popeth sydd wedi\'i gynnwys ym mhob eitem. Ydych chi\'n siŵr eich bod am wagio’r bin ailgylchu?', + 'recycle_bin_destroy_confirm' => 'Bydd y weithred hon yn dileu\'r eitem hon o\'r system yn barhaol, ynghyd ag unrhyw elfennau plentyn a restrir isod, ac ni fyddwch yn gallu adfer y cynnwys hwn. Ydych chi\'n siŵr eich bod eisiau dileu\'r eitem hon yn barhaol?', + 'recycle_bin_destroy_list' => 'Eitemau i\'w Dinistrio', + 'recycle_bin_restore_list' => 'Eitemau i\'w Hadfer', + 'recycle_bin_restore_confirm' => 'Bydd y weithred hon yn adfer yr eitem a ddilëwyd, gan gynnwys unrhyw elfennau plentyn, i\'w lleoliad gwreiddiol. Os yw\'r lleoliad gwreiddiol wedi\'i ddileu ers hynny, a’i fod bellach yn y bin ailgylchu, bydd angen adfer yr eitem riant hefyd.', + 'recycle_bin_restore_deleted_parent' => 'Mae rhiant yr eitem hon hefyd wedi\'i dileu. Bydd y rhain yn parhau i fod wedi’u dileu nes bod y rhiant hwnnw hefyd yn cael ei adfer.', + 'recycle_bin_restore_parent' => 'Adfer Rhiant', + 'recycle_bin_destroy_notification' => 'Dilëwyd cyfanswm o :count o eitemau o\'r bin ailgylchu.', + 'recycle_bin_restore_notification' => 'Adferwyd cyfanswm o :count o eitemau o\'r bin ailgylchu.', // Audit Log - 'audit' => 'Audit Log', - 'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.', - 'audit_event_filter' => 'Event Filter', - 'audit_event_filter_no_filter' => 'No Filter', - 'audit_deleted_item' => 'Deleted Item', - 'audit_deleted_item_name' => 'Name: :name', - 'audit_table_user' => 'User', - 'audit_table_event' => 'Event', - 'audit_table_related' => 'Related Item or Detail', - 'audit_table_ip' => 'IP Address', - 'audit_table_date' => 'Activity Date', - 'audit_date_from' => 'Date Range From', - 'audit_date_to' => 'Date Range To', + 'audit' => 'Log Awdit', + 'audit_desc' => 'Mae\'r cofnod archwilio hwn yn dangos rhestr o weithgareddau sydd wedi\'u holrhain yn y system. Nid yw’r rhestr hon wedi’i hidlo yn wahanol i restrau gweithgaredd tebyg yn y system lle mae hidlwyr caniatâd yn cael eu cymhwyso.', + 'audit_event_filter' => 'Hidlydd Digwyddiad', + 'audit_event_filter_no_filter' => 'Dim Fidlydd', + 'audit_deleted_item' => 'Eitem wedi\'i Dileu', + 'audit_deleted_item_name' => 'Enw: :name', + 'audit_table_user' => 'Defnyddiwr', + 'audit_table_event' => 'Digwyddiad', + 'audit_table_related' => 'Eitem neu Fanylion Cysylltiedig', + 'audit_table_ip' => 'Cyfeiriad IP', + 'audit_table_date' => 'Dyddiad Gweithgaredd', + 'audit_date_from' => 'Amrediad Dyddiad O', + 'audit_date_to' => 'Amrediad Dyddiad Tan', // Role Settings - 'roles' => 'Roles', - 'role_user_roles' => 'User Roles', - 'roles_index_desc' => 'Roles are used to group users & provide system permission to their members. When a user is a member of multiple roles the privileges granted will stack and the user will inherit all abilities.', - 'roles_x_users_assigned' => ':count user assigned|:count users assigned', - 'roles_x_permissions_provided' => ':count permission|:count permissions', - 'roles_assigned_users' => 'Assigned Users', - 'roles_permissions_provided' => 'Provided Permissions', - 'role_create' => 'Create New Role', - 'role_delete' => 'Delete Role', - 'role_delete_confirm' => 'This will delete the role with the name \':roleName\'.', - 'role_delete_users_assigned' => 'This role has :userCount users assigned to it. If you would like to migrate the users from this role select a new role below.', - 'role_delete_no_migration' => "Don't migrate users", - 'role_delete_sure' => 'Are you sure you want to delete this role?', - 'role_edit' => 'Edit Role', - 'role_details' => 'Role Details', - 'role_name' => 'Role Name', - 'role_desc' => 'Short Description of Role', - 'role_mfa_enforced' => 'Requires Multi-Factor Authentication', - 'role_external_auth_id' => 'External Authentication IDs', - 'role_system' => 'System Permissions', - 'role_manage_users' => 'Manage users', - 'role_manage_roles' => 'Manage roles & role permissions', - 'role_manage_entity_permissions' => 'Manage all book, chapter & page permissions', - 'role_manage_own_entity_permissions' => 'Manage permissions on own book, chapter & pages', - 'role_manage_page_templates' => 'Manage page templates', - 'role_access_api' => 'Access system API', - 'role_manage_settings' => 'Manage app settings', - 'role_export_content' => 'Export content', - 'role_editor_change' => 'Change page editor', - 'role_notifications' => 'Receive & manage notifications', - 'role_asset' => 'Asset Permissions', - 'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.', - 'role_asset_desc' => 'These permissions control default access to the assets within the system. Permissions on Books, Chapters and Pages will override these permissions.', - 'role_asset_admins' => 'Admins are automatically given access to all content but these options may show or hide UI options.', - 'role_asset_image_view_note' => 'This relates to visibility within the image manager. Actual access of uploaded image files will be dependant upon system image storage option.', - 'role_all' => 'All', - 'role_own' => 'Own', - 'role_controlled_by_asset' => 'Controlled by the asset they are uploaded to', - 'role_save' => 'Save Role', - 'role_users' => 'Users in this role', - 'role_users_none' => 'No users are currently assigned to this role', + 'roles' => 'Rolau', + 'role_user_roles' => 'Rolau Defnyddiwr', + 'roles_index_desc' => 'Defnyddir rolau i grwpio defnyddwyr a rhoi caniatâd system i\'w haelodau. Pan fydd defnyddiwr yn aelod o sawl rôl, bydd y breintiau a roddir yn pentyrru a bydd y defnyddiwr yn etifeddu pob gallu.', + 'roles_x_users_assigned' => ':count defnyddiwr wedi\'u clustnodi|:count o ddefnyddwyr wedi\'u clustnodi', + 'roles_x_permissions_provided' => ':count caniatâd|:count caniatâd', + 'roles_assigned_users' => 'Clustnodi Defnyddwyr', + 'roles_permissions_provided' => 'Darparu Caniatâd', + 'role_create' => 'Creu Rôl Newydd', + 'role_delete' => 'Dileu Rôl', + 'role_delete_confirm' => 'Bydd hyn yn dileu\'r rôl gyda\'r enw \':roleName\'.', + 'role_delete_users_assigned' => 'Mae gan y rôl hon :userCount o ddefnyddwyr wedi’u clustnodi iddi. Os hoffech chi fudo\'r defnyddwyr o\'r rôl hon dewiswch rôl newydd isod.', + 'role_delete_no_migration' => "Peidiwch â mudo defnyddwyr", + 'role_delete_sure' => 'Wyt ti\'n bendant eisiau dileu\'r rôl hwn?', + 'role_edit' => 'Golygu Rôl', + 'role_details' => 'Manylion Rôl', + 'role_name' => 'Enw Rôl', + 'role_desc' => 'Disgrifiad Byr o’r Rôl', + 'role_mfa_enforced' => 'Angen Dilysu Aml-Ffactor', + 'role_external_auth_id' => 'ID Dilysu Allanol', + 'role_system' => 'Caniatâd System', + 'role_manage_users' => 'Rheoli defnyddwyr', + 'role_manage_roles' => 'Rheoli rolau & chaniatâd rolau', + 'role_manage_entity_permissions' => 'Rheoli caniatâd pob llyfr, pennod a thudalen', + 'role_manage_own_entity_permissions' => 'Rheoli caniatâd eich llyfr, pennod a thudalennau eich hun', + 'role_manage_page_templates' => 'Rheoli templedi tudalen', + 'role_access_api' => 'Mynediad i Ryngwyneb Rhaglennu Cymwysiadau (API) system', + 'role_manage_settings' => 'Rheoli gosodiadau apiau', + 'role_export_content' => 'Cynnwys allforio', + 'role_import_content' => 'Mewnforio Cynnwys', + 'role_editor_change' => 'Newid golygydd tudalen', + 'role_notifications' => 'Derbyn a rheoli hysbysiadau', + 'role_asset' => 'Caniatâd Asedau', + 'roles_system_warning' => 'Byddwch yn ymwybodol y gall mynediad i unrhyw un o\'r tri chaniatâd uchod ganiatáu i ddefnyddiwr newid eu breintiau eu hunain neu freintiau eraill yn y system. Neilltuo rolau gyda\'r caniatâd hyn i ddefnyddwyr dibynadwy yn unig.', + 'role_asset_desc' => 'Mae\'r caniatâd hwn yn rheoli mynediad diofyn i\'r asedau o fewn y system. Bydd caniatâd ar Lyfrau, Penodau a Thudalennau yn diystyru\'r caniatâd hwn.', + 'role_asset_admins' => 'Mae gweinyddwyr yn cael mynediad awtomatig i\'r holl gynnwys ond gall yr opsiynau hyn ddangos neu guddio opsiynau UI.', + 'role_asset_image_view_note' => 'Mae hyn yn ymwneud â gwelededd o fewn y rheolwr delweddau. Bydd mynediad gwirioneddol i ffeiliau delwedd wedi\'u huwchlwytho yn dibynnu ar opsiwn storio delwedd y system.', + 'role_all' => 'Popeth', + 'role_own' => 'Meddu', + 'role_controlled_by_asset' => 'Wedi\'u rheoli gan yr ased y maent yn cael eu huwchlwytho iddo', + 'role_save' => 'Cadw Rôl', + 'role_users' => 'Defnyddwyr yn y rôl hon', + 'role_users_none' => 'Nid oes unrhyw ddefnyddwyr wedi’u neilltuo i\'r rôl hon ar hyn o bryd', // Users - 'users' => 'Users', - 'users_index_desc' => 'Create & manage individual user accounts within the system. User accounts are used for login and attribution of content & activity. Access permissions are primarily role-based but user content ownership, among other factors, may also affect permissions & access.', - 'user_profile' => 'User Profile', - 'users_add_new' => 'Add New User', - 'users_search' => 'Search Users', - 'users_latest_activity' => 'Latest Activity', - 'users_details' => 'User Details', - 'users_details_desc' => 'Set a display name and an email address for this user. The email address will be used for logging into the application.', - 'users_details_desc_no_email' => 'Set a display name for this user so others can recognise them.', - 'users_role' => 'User Roles', - 'users_role_desc' => 'Select which roles this user will be assigned to. If a user is assigned to multiple roles the permissions from those roles will stack and they will receive all abilities of the assigned roles.', - 'users_password' => 'User Password', - 'users_password_desc' => 'Set a password used to log-in to the application. This must be at least 8 characters long.', - 'users_send_invite_text' => 'You can choose to send this user an invitation email which allows them to set their own password otherwise you can set their password yourself.', - 'users_send_invite_option' => 'Send user invite email', + 'users' => 'Defnyddwyr', + 'users_index_desc' => 'Creu a rheoli cyfrifon defnyddwyr unigol o fewn y system. Defnyddir cyfrifon defnyddwyr ar gyfer mewngofnodi a phriodoli cynnwys a gweithgaredd. Mae caniatâd mynediad yn seiliedig ar rôl yn bennaf ond gall perchenogaeth cynnwys defnyddwyr, ymhlith ffactorau eraill, hefyd effeithio ar ganiatâd a mynediad.', + 'user_profile' => 'Proffil Defnyddiwr', + 'users_add_new' => 'Ychwanegu Defnyddiwr Newydd', + 'users_search' => 'Chwilio Defnyddwyr', + 'users_latest_activity' => 'Gweithgaredd Diweddaraf', + 'users_details' => 'Manylion Defnyddiwr', + 'users_details_desc' => 'Gosodwch enw arddangos a chyfeiriad e-bost ar gyfer y defnyddiwr hwn. Bydd y cyfeiriad e-bost yn cael ei ddefnyddio ar gyfer mewngofnodi i’r cais.', + 'users_details_desc_no_email' => 'Gosodwch enw arddangos ar gyfer y defnyddiwr hwn fel y gall eraill eu hadnabod.', + 'users_role' => 'Rolau Defnyddiwr', + 'users_role_desc' => 'Dewiswch ba rolau a neilltuir i’r defnyddiwr hwn. Os yw defnyddiwr yn cael ei neilltuo i rolau lluosog, bydd y caniatâd o\'r rolau hynny yn pentyrru a byddant yn derbyn pob gallu o\'r rolau a neilltuwyd.', + 'users_password' => 'Cyfrinair Defnyddiwr', + 'users_password_desc' => 'Gosodwch gyfrinair a ddefnyddir i fewngofnodi i\'r rhaglen. Rhaid i gyfrineiriau fod yn 8 nod o leiaf.', + 'users_send_invite_text' => 'Gallwch ddewis anfon e-bost gwahoddiad i\'r defnyddiwr hwn sy\'n caniatáu iddynt osod eu cyfrinair eu hunain neu gallwch osod eu cyfrinair eich hun.', + 'users_send_invite_option' => 'Anfon e-bost gwahodd i’r defnyddiwr', 'users_external_auth_id' => 'External Authentication ID', - 'users_external_auth_id_desc' => 'When an external authentication system is in use (such as SAML2, OIDC or LDAP) this is the ID which links this BookStack user to the authentication system account. You can ignore this field if using the default email-based authentication.', - 'users_password_warning' => 'Only fill the below if you would like to change the password for this user.', - 'users_system_public' => 'This user represents any guest users that visit your instance. It cannot be used to log in but is assigned automatically.', - 'users_delete' => 'Delete User', - 'users_delete_named' => 'Delete user :userName', - 'users_delete_warning' => 'This will fully delete this user with the name \':userName\' from the system.', - 'users_delete_confirm' => 'Are you sure you want to delete this user?', - 'users_migrate_ownership' => 'Migrate Ownership', - 'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.', - 'users_none_selected' => 'No user selected', - 'users_edit' => 'Edit User', - 'users_edit_profile' => 'Edit Profile', - 'users_avatar' => 'User Avatar', - 'users_avatar_desc' => 'Select an image to represent this user. This should be approx 256px square.', - 'users_preferred_language' => 'Preferred Language', - 'users_preferred_language_desc' => 'This option will change the language used for the user-interface of the application. This will not affect any user-created content.', - 'users_social_accounts' => 'Social Accounts', - 'users_social_accounts_desc' => 'View the status of the connected social accounts for this user. Social accounts can be used in addition to the primary authentication system for system access.', - 'users_social_accounts_info' => 'Here you can connect your other accounts for quicker and easier login. Disconnecting an account here does not revoke previously authorized access. Revoke access from your profile settings on the connected social account.', - 'users_social_connect' => 'Connect Account', - 'users_social_disconnect' => 'Disconnect Account', - 'users_social_status_connected' => 'Connected', - 'users_social_status_disconnected' => 'Disconnected', - 'users_social_connected' => ':socialAccount account was successfully attached to your profile.', - 'users_social_disconnected' => ':socialAccount account was successfully disconnected from your profile.', - 'users_api_tokens' => 'API Tokens', - 'users_api_tokens_desc' => 'Create and manage the access tokens used to authenticate with the BookStack REST API. Permissions for the API are managed via the user that the token belongs to.', - 'users_api_tokens_none' => 'No API tokens have been created for this user', - 'users_api_tokens_create' => 'Create Token', - 'users_api_tokens_expires' => 'Expires', - 'users_api_tokens_docs' => 'API Documentation', - 'users_mfa' => 'Multi-Factor Authentication', - 'users_mfa_desc' => 'Setup multi-factor authentication as an extra layer of security for your user account.', - 'users_mfa_x_methods' => ':count method configured|:count methods configured', - 'users_mfa_configure' => 'Configure Methods', + 'users_external_auth_id_desc' => 'Pan fydd system ddilysu allanol yn cael ei defnyddio (megis SAML2, OIDC neu LDAP) dyma\'r ID sy\'n cysylltu\'r defnyddiwr BookStack hwn â\'r cyfrif system ddilysu. Gallwch anwybyddu\'r maes hwn os ydych chi\'n defnyddio\'r dilysiad diofyn e-bost.', + 'users_password_warning' => 'Llenwch y canlynol os hoffech newid y cyfrinair ar gyfer y defnyddiwr hwn yn unig.', + 'users_system_public' => 'Mae\'r defnyddiwr hwn yn cynrychioli unrhyw westeion sy\'n ymweld â\'ch enghraifft. Ni ellir ei ddefnyddio i fewngofnodi ond mae\'n cael ei aseinio\'n awtomatig.', + 'users_delete' => 'Dileu Defnyddiwr', + 'users_delete_named' => 'Dileu defnyddiwr :userName', + 'users_delete_warning' => 'Bydd hyn yn dileu\'r defnyddiwr hwn yn llawn gyda\'r enw \':userName\' o\'r system.', + 'users_delete_confirm' => 'Ydych chi\'n siŵr eich bod eisiau dileu’r defnyddiwr hwn?', + 'users_migrate_ownership' => 'Mudo Perchnogaeth', + 'users_migrate_ownership_desc' => 'Dewiswch ddefnyddiwr yma os ydych chi am i ddefnyddiwr arall ddod yn berchennog ar yr holl eitemau sy\'n eiddo i\'r defnyddiwr hwn ar hyn o bryd.', + 'users_none_selected' => 'Ni ddewiswyd defnyddiwr', + 'users_edit' => 'Golygu Defnyddiwr', + 'users_edit_profile' => 'Golygu Proffil', + 'users_avatar' => 'Afatar Defnyddiwr', + 'users_avatar_desc' => 'Dewiswch ddelwedd i gynrychioli\'r defnyddiwr hwn. Dylai hwn fod yn ddelwedd sgwâr tua 256px.', + 'users_preferred_language' => 'Dewis Iaith', + 'users_preferred_language_desc' => 'Bydd yr opsiwn hwn yn newid yr iaith a ddefnyddir ar gyfer rhyngwyneb defnyddiwr y rhaglen. Ni fydd hyn yn effeithio ar unrhyw gynnwys a grëwyd gan y defnyddiwr.', + 'users_social_accounts' => 'Cyfrifon Cymdeithasol', + 'users_social_accounts_desc' => 'Gweld statws y cyfrifon cymdeithasol cysylltiedig ar gyfer y defnyddiwr hwn. Gellir defnyddio cyfrifon cymdeithasol yn ychwanegol at y system ddilysu sylfaenol ar gyfer mynediad system.', + 'users_social_accounts_info' => 'Yma gallwch gysylltu eich cyfrifon eraill ar gyfer mewngofnodi cyflymach a haws. Nid yw datgysylltu cyfrif yma yn diddymu mynediad awdurdodedig blaenorol. Diddymu mynediad o\'ch gosodiadau proffil ar y cyfrif cymdeithasol cysylltiedig.', + 'users_social_connect' => 'Cysylltu Cyfrif', + 'users_social_disconnect' => 'Datgysylltu Cyfrif', + 'users_social_status_connected' => 'Wedi Cysylltu', + 'users_social_status_disconnected' => 'Wedi Datgysylltu', + 'users_social_connected' => 'Mae eich cyfrif :socialAccount wedi\'i gysylltu\'n llwyddiannus â\'ch proffil.', + 'users_social_disconnected' => 'Mae eich cyfrif :socialAccount wedi\'i ddatgysylltu\'n llwyddiannus o\'ch proffil.', + 'users_api_tokens' => 'Tocynnau API', + 'users_api_tokens_desc' => 'Creu a rheoli\'r tocynnau mynediad a ddefnyddir i ddilysu gyda\'r API BookStack REST. Rheolir caniatâd ar gyfer yr API trwy\'r defnyddiwr y mae\'r tocyn yn perthyn iddo.', + 'users_api_tokens_none' => 'Nid oes tocynnau API wedi\'u creu ar gyfer y defnyddiwr hwn', + 'users_api_tokens_create' => 'Creu Tocyn', + 'users_api_tokens_expires' => 'Yn dod i ben', + 'users_api_tokens_docs' => 'Dogfennaeth API', + 'users_mfa' => 'Dilysu Aml-Ffactor', + 'users_mfa_desc' => 'Gosod dilysu aml-ffactor fel haen ychwanegol o ddiogelwch ar gyfer eich cyfrif defnyddiwr.', + 'users_mfa_x_methods' => ':count dull wedi\'i ffurfweddu|:count dull wedi\'u ffurfweddu', + 'users_mfa_configure' => 'Ffurfweddu Dulliau', // API Tokens - 'user_api_token_create' => 'Create API Token', - 'user_api_token_name' => 'Name', - 'user_api_token_name_desc' => 'Give your token a readable name as a future reminder of its intended purpose.', - 'user_api_token_expiry' => 'Expiry Date', - 'user_api_token_expiry_desc' => 'Set a date at which this token expires. After this date, requests made using this token will no longer work. Leaving this field blank will set an expiry 100 years into the future.', - 'user_api_token_create_secret_message' => 'Immediately after creating this token a "Token ID" & "Token Secret" will be generated and displayed. The secret will only be shown a single time so be sure to copy the value to somewhere safe and secure before proceeding.', - 'user_api_token' => 'API Token', - 'user_api_token_id' => 'Token ID', - 'user_api_token_id_desc' => 'This is a non-editable system generated identifier for this token which will need to be provided in API requests.', - 'user_api_token_secret' => 'Token Secret', - 'user_api_token_secret_desc' => 'This is a system generated secret for this token which will need to be provided in API requests. This will only be displayed this one time so copy this value to somewhere safe and secure.', - 'user_api_token_created' => 'Token created :timeAgo', - 'user_api_token_updated' => 'Token updated :timeAgo', - 'user_api_token_delete' => 'Delete Token', - 'user_api_token_delete_warning' => 'This will fully delete this API token with the name \':tokenName\' from the system.', - 'user_api_token_delete_confirm' => 'Are you sure you want to delete this API token?', + 'user_api_token_create' => 'Creu Tocyn API', + 'user_api_token_name' => 'Enw', + 'user_api_token_name_desc' => 'Rhowch enw darllenadwy i\'ch tocyn i\'ch atgoffa o\'i bwrpas arfaethedig yn y dyfodol.', + 'user_api_token_expiry' => 'Dyddiad Dod i Ben', + 'user_api_token_expiry_desc' => 'Gosodwch ddyddiad pan fydd y tocyn hwn yn dod i ben. Ar ôl y dyddiad hwn, ni fydd ceisiadau a wneir gan ddefnyddio\'r tocyn hwn yn gweithio mwyach. Bydd gadael y maes hwn yn wag yn gosod dyddiad dod i ben 100 mlynedd i\'r dyfodol.', + 'user_api_token_create_secret_message' => 'Yn syth ar ôl creu\'r tocyn hwn bydd "ID tocyn" a "Chyfrinach Tocyn" yn cael eu cynhyrchu a\'u harddangos. Dim ond unwaith y bydd y gyfrinach yn cael ei dangos, felly gwnewch yn siŵr eich bod yn copïo\'r gwerth i rywle diogel cyn bwrw ymlaen.', + 'user_api_token' => 'Tocyn API', + 'user_api_token_id' => 'ID Tocyn', + 'user_api_token_id_desc' => 'Mae hwn yn ddynodwr a gynhyrchir gan y system na ellir ei olygu ar gyfer y tocyn hwn y bydd angen ei ddarparu mewn ceisiadau API.', + 'user_api_token_secret' => 'Cyfrinach Tocyn', + 'user_api_token_secret_desc' => 'Mae hwn yn gyfrinach a gynhyrchir gan y system ar gyfer y tocyn hwn y bydd angen ei ddarparu mewn ceisiadau API. Dim ond unwaith y bydd hyn yn cael ei arddangos, felly copïwch y gwerth hwn i rywle diogel.', + 'user_api_token_created' => 'Crëwyd tocyn :timeAgo', + 'user_api_token_updated' => 'Diweddarwyd tocyn :timeAgo', + 'user_api_token_delete' => 'Dileu Tocyn', + 'user_api_token_delete_warning' => 'Bydd hyn yn dileu\'r tocyn API hwn yn llawn gyda\'r enw \':tokenName\' o\'r system.', + 'user_api_token_delete_confirm' => 'Ydych chi\'n siŵr eich bod eisiau dileu’r tocyn API hwn?', // Webhooks - 'webhooks' => 'Webhooks', - 'webhooks_index_desc' => 'Webhooks are a way to send data to external URLs when certain actions and events occur within the system which allows event-based integration with external platforms such as messaging or notification systems.', - 'webhooks_x_trigger_events' => ':count trigger event|:count trigger events', - 'webhooks_create' => 'Create New Webhook', - 'webhooks_none_created' => 'No webhooks have yet been created.', - 'webhooks_edit' => 'Edit Webhook', - 'webhooks_save' => 'Save Webhook', - 'webhooks_details' => 'Webhook Details', - 'webhooks_details_desc' => 'Provide a user friendly name and a POST endpoint as a location for the webhook data to be sent to.', - 'webhooks_events' => 'Webhook Events', - 'webhooks_events_desc' => 'Select all the events that should trigger this webhook to be called.', - 'webhooks_events_warning' => 'Keep in mind that these events will be triggered for all selected events, even if custom permissions are applied. Ensure that use of this webhook won\'t expose confidential content.', - 'webhooks_events_all' => 'All system events', - 'webhooks_name' => 'Webhook Name', - 'webhooks_timeout' => 'Webhook Request Timeout (Seconds)', - 'webhooks_endpoint' => 'Webhook Endpoint', - 'webhooks_active' => 'Webhook Active', - 'webhook_events_table_header' => 'Events', - 'webhooks_delete' => 'Delete Webhook', - 'webhooks_delete_warning' => 'This will fully delete this webhook, with the name \':webhookName\', from the system.', - 'webhooks_delete_confirm' => 'Are you sure you want to delete this webhook?', - 'webhooks_format_example' => 'Webhook Format Example', - 'webhooks_format_example_desc' => 'Webhook data is sent as a POST request to the configured endpoint as JSON following the format below. The "related_item" and "url" properties are optional and will depend on the type of event triggered.', - 'webhooks_status' => 'Webhook Status', - 'webhooks_last_called' => 'Last Called:', - 'webhooks_last_errored' => 'Last Errored:', - 'webhooks_last_error_message' => 'Last Error Message:', + 'webhooks' => 'Gwe-fachau', + 'webhooks_index_desc' => 'Mae gwe-fachau’n ffordd o anfon data at URL allanol pan fydd rhai gweithredoedd a digwyddiadau yn digwydd o fewn y system sy\'n caniatáu integreiddio ar sail digwyddiadau gyda llwyfannau allanol megis systemau negeseuon neu hysbysu.', + 'webhooks_x_trigger_events' => ':count sbarduno digwyddiad|:count sbarduno digwyddiadau', + 'webhooks_create' => 'Creu Gwe-fachyn Newydd', + 'webhooks_none_created' => 'Nid oes unrhyw we-fachau wedi\'u creu eto.', + 'webhooks_edit' => 'Golygu Gwe-fachyn', + 'webhooks_save' => 'Cadw Gwe-fachyn', + 'webhooks_details' => 'Manylion Gwe-fachyn', + 'webhooks_details_desc' => 'Darparu enw defnyddiwr cyfeillgar a chyfeiriad POST fel lleoliad ar gyfer anfon y data gwe-fachyn iddo.', + 'webhooks_events' => 'Digwyddiadau Gwe-fachyn', + 'webhooks_events_desc' => 'Dewiswch yr holl ddigwyddiadau a ddylai sbarduno\'r gwe-fachyn hwn i gael ei alw.', + 'webhooks_events_warning' => 'Cadwch mewn cof y bydd y digwyddiadau hyn yn cael eu sbarduno ar gyfer yr holl ddigwyddiadau a ddewiswyd, hyd yn oed os rhoddir caniatâd arferol ar waith. Gwnewch yn siŵr na fydd defnyddio\'r gwe-fachyn hwn yn datgelu cynnwys cyfrinachol.', + 'webhooks_events_all' => 'Holl ddigwyddiadau\'r system', + 'webhooks_name' => 'Enw Gwe-fachyn', + 'webhooks_timeout' => 'Terfyn Amser Gwneud Cais Gwe-fachyn (Eiliadau)', + 'webhooks_endpoint' => 'Pwynt Terfyn Gwe-fachyn', + 'webhooks_active' => 'Gwe-fachyn Byw', + 'webhook_events_table_header' => 'Digwyddiadau', + 'webhooks_delete' => 'Dileu Gwe-fachyn', + 'webhooks_delete_warning' => 'Bydd hyn yn dileu\'r gwe-fachyn hwn yn llawn gyda\'r enw \':webhookName\', o\'r system.', + 'webhooks_delete_confirm' => 'Ydych chi\'n siŵr eich bod eisiau dileu’r gwe-fachyn hwn?', + 'webhooks_format_example' => 'Enghraifft Fformat Gwe-fachyn', + 'webhooks_format_example_desc' => 'Anfonir data Gwe-fachyn fel cais POST i\'r pwynt terfyn wedi\'i ffurfweddu fel JSON yn dilyn y fformat isod. Mae\'r nodweddion "related_item" ac "url" yn ddewisol a byddant yn dibynnu ar y math o ddigwyddiad a sbardunir.', + 'webhooks_status' => 'Statws Gwe-fachyn', + 'webhooks_last_called' => 'Galwyd Ddiwethaf:', + 'webhooks_last_errored' => 'Cafwyd Gwall Ddiwethaf:', + 'webhooks_last_error_message' => 'Neges Wall Ddiwethaf:', + // Licensing + 'licenses' => 'Trwyddedau', + 'licenses_desc' => 'Mae\'r dudalen hon yn manylu ar wybodaeth am drwydded ar gyfer BookStack yn ychwanegol at y prosiectau a’r llyfrgelloedd a ddefnyddir o fewn BookStack. Dim ond mewn cyd-destun datblygu y gellir defnyddio llawer o’r prosiectau a restrir.', + 'licenses_bookstack' => 'Trwydded BookStack', + 'licenses_php' => 'Trwyddedau Llyfrgell PHP', + 'licenses_js' => 'Trwyddedau Llyfrgell JavaScript', + 'licenses_other' => 'Trwyddedau Eraill', + 'license_details' => 'Manylion y Drwydded', //! If editing translations files directly please ignore this in all //! languages apart from en. Content will be auto-copied from en. @@ -287,6 +326,7 @@ 'bs' => 'Bosanski', 'ca' => 'Català', 'cs' => 'Česky', + 'cy' => 'Cymraeg', 'da' => 'Dansk', 'de' => 'Deutsch (Sie)', 'de_informal' => 'Deutsch (Du)', @@ -308,6 +348,7 @@ 'lt' => 'Lietuvių Kalba', 'lv' => 'Latviešu Valoda', 'nb' => 'Norsk (Bokmål)', + 'ne' => 'नेपाली', 'nn' => 'Nynorsk', 'nl' => 'Nederlands', 'pl' => 'Polski', diff --git a/lang/cy/validation.php b/lang/cy/validation.php index 87efe285093..5259f647244 100644 --- a/lang/cy/validation.php +++ b/lang/cy/validation.php @@ -41,74 +41,79 @@ 'array' => 'Rhaid i\'r :attribute fod yn fwy na :value eitemau.', ], 'gte' => [ - 'numeric' => 'The :attribute must be greater than or equal :value.', - 'file' => 'The :attribute must be greater than or equal :value kilobytes.', - 'string' => 'The :attribute must be greater than or equal :value characters.', - 'array' => 'The :attribute must have :value items or more.', + 'numeric' => 'Rhaid i’r :attribute fod yn fwy na, neu’n gyfartal â :value.', + 'file' => 'Rhaid i’r :attribute fod yn fwy na, neu’n gyfartal â :value cilobeit.', + 'string' => 'Rhaid i’r :attribute fod yn fwy na, neu’n gyfartal â :value nod.', + 'array' => 'Rhaid i’r :attribute fod â :value o eitemau neu fwy.', ], - 'exists' => 'The selected :attribute is invalid.', - 'image' => 'The :attribute must be an image.', - 'image_extension' => 'The :attribute must have a valid & supported image extension.', - 'in' => 'The selected :attribute is invalid.', - 'integer' => 'The :attribute must be an integer.', - 'ip' => 'The :attribute must be a valid IP address.', - 'ipv4' => 'The :attribute must be a valid IPv4 address.', - 'ipv6' => 'The :attribute must be a valid IPv6 address.', - 'json' => 'The :attribute must be a valid JSON string.', + 'exists' => 'Mae\'r dewis :attribute yn annilys.', + 'image' => 'Rhaid i’r :attribute fod yn ddelwedd.', + 'image_extension' => 'Rhaid i’r :attribute fod ag estyniad delwedd dilys & gefnogir.', + 'in' => 'Mae\'r dewis :attribute yn annilys.', + 'integer' => 'Rhaid i’r :attribute fod yn gyfanrif.', + 'ip' => 'Rhaid i’r :attribute fod yn gyfeiriad IP dilys.', + 'ipv4' => 'Rhaid i’r :attribute fod yn gyfeiriad IPv4 dilys.', + 'ipv6' => 'Rhaid i’r :attribute fod yn gyfeiriad IPv6 dilys.', + 'json' => 'Rhaid i’r :attribute fod yn llinyn JSON dilys.', 'lt' => [ - 'numeric' => 'The :attribute must be less than :value.', - 'file' => 'The :attribute must be less than :value kilobytes.', - 'string' => 'The :attribute must be less than :value characters.', - 'array' => 'The :attribute must have less than :value items.', + 'numeric' => 'Rhaid i’r :attribute fod yn llai na :value.', + 'file' => 'Rhaid i’r :attribute fod yn llai na :value cilobeit.', + 'string' => 'Rhaid i’r :attribute fod yn llai na :value nod.', + 'array' => 'Rhaid i’r :attribute fod â llai na :value o eitemau.', ], 'lte' => [ - 'numeric' => 'The :attribute must be less than or equal :value.', - 'file' => 'The :attribute must be less than or equal :value kilobytes.', - 'string' => 'The :attribute must be less than or equal :value characters.', - 'array' => 'The :attribute must not have more than :value items.', + 'numeric' => 'Rhaid i’r :attribute fod yn llai na, neu’n gyfartal â :value.', + 'file' => 'Rhaid i’r :attribute fod yn llai na, neu’n gyfartal â :value cilobeit.', + 'string' => 'Rhaid i’r :attribute fod yn llai na, neu’n gyfartal â :value nod.', + 'array' => 'Ni ddylai’r :attribute fod â mwy na :value o eitemau.', ], 'max' => [ - 'numeric' => 'The :attribute may not be greater than :max.', - 'file' => 'The :attribute may not be greater than :max kilobytes.', - 'string' => 'The :attribute may not be greater than :max characters.', - 'array' => 'The :attribute may not have more than :max items.', + 'numeric' => 'Ni ddylai’r :attribute fod yn fwy na :max.', + 'file' => 'Ni ddylai’r :attribute fod yn fwy na :max cilobeit.', + 'string' => 'Ni ddylai’r :attribute fod yn fwy na :max nod.', + 'array' => 'Ni ddylai’r :attribute fod â mwy na :max o eitemau.', ], - 'mimes' => 'The :attribute must be a file of type: :values.', + 'mimes' => 'Rhaid i’r :attribute fod yn ffeil o fath: :values.', 'min' => [ - 'numeric' => 'The :attribute must be at least :min.', - 'file' => 'The :attribute must be at least :min kilobytes.', - 'string' => 'The :attribute must be at least :min characters.', - 'array' => 'The :attribute must have at least :min items.', + 'numeric' => 'Rhaid i’r :attribute fod yn o leiaf :min.', + 'file' => 'Rhaid i’r :attribute fod yn o leiaf :min cilobeit.', + 'string' => 'Rhaid i’r :attribute fod yn o leiaf :min nod.', + 'array' => 'Rhaid i’r :attribute fod â llai na :min o eitemau.', ], - 'not_in' => 'The selected :attribute is invalid.', - 'not_regex' => 'The :attribute format is invalid.', - 'numeric' => 'The :attribute must be a number.', - 'regex' => 'The :attribute format is invalid.', - 'required' => 'The :attribute field is required.', - 'required_if' => 'The :attribute field is required when :other is :value.', - 'required_with' => 'The :attribute field is required when :values is present.', - 'required_with_all' => 'The :attribute field is required when :values is present.', - 'required_without' => 'The :attribute field is required when :values is not present.', - 'required_without_all' => 'The :attribute field is required when none of :values are present.', - 'same' => 'The :attribute and :other must match.', - 'safe_url' => 'The provided link may not be safe.', + 'not_in' => 'Mae\'r dewis :attribute yn annilys.', + 'not_regex' => 'Mae’r fformat :attribute yn annilys.', + 'numeric' => 'Rhaid i’r :attribute fod yn rhif.', + 'regex' => 'Mae’r fformat :attribute yn annilys.', + 'required' => 'Mae :attribute yn faes gofynnol.', + 'required_if' => 'Mae :attribute yn faes gofynnol pan fo :other yn :value.', + 'required_with' => 'Mae :attribute yn faes gofynnol pan fo :values yn bresennol.', + 'required_with_all' => 'Mae :attribute yn faes gofynnol pan fo :values yn bresennol.', + 'required_without' => 'Mae :attitude yn faes gofynnol pan nad yw :values yn bresennol.', + 'required_without_all' => 'Mae angen y maes :attribute os dydi\'r un o :values yn bresennol.', + 'same' => 'Mae’n rhaid i’r :attribute a :other gyd-fynd.', + 'safe_url' => 'Efallai na fydd y ddolen a ddarperir yn ddiogel.', 'size' => [ - 'numeric' => 'The :attribute must be :size.', - 'file' => 'The :attribute must be :size kilobytes.', - 'string' => 'The :attribute must be :size characters.', - 'array' => 'The :attribute must contain :size items.', + 'numeric' => 'Rhaid i’r :attribute fod yn :size.', + 'file' => 'Rhaid i’r :attribute fod yn :size cilobeit.', + 'string' => 'Rhaid i’r :attribute fod yn :size nod.', + 'array' => 'Rhaid i’r :attribute gynnwys eitemau :size.', ], - 'string' => 'The :attribute must be a string.', - 'timezone' => 'The :attribute must be a valid zone.', - 'totp' => 'The provided code is not valid or has expired.', - 'unique' => 'The :attribute has already been taken.', - 'url' => 'The :attribute format is invalid.', - 'uploaded' => 'The file could not be uploaded. The server may not accept files of this size.', + 'string' => 'Rhaid i’r :attribute fod yn llinyn.', + 'timezone' => 'Rhaid i’r :attribute fod yn barth dilys.', + 'totp' => 'Nid yw\'r cod a ddarperir yn ddilys neu mae wedi dod i ben.', + 'unique' => 'Mae’r :attribute eisoes wedi ei gymryd.', + 'url' => 'Mae’r fformat :attribute yn annilys.', + 'uploaded' => 'Nid oedd modd uwchlwytho’r ffeil. Efallai na fydd y gweinydd yn derbyn ffeiliau o\'r maint hwn.', + + 'zip_file' => 'Mae\'r :attribute angen cyfeirio at ffeil yn y ZIP.', + 'zip_file_mime' => 'Mae\'r :attribute angen cyfeirio at ffeil o fath :valid Types, sydd wedi\'i ffeindio :foundType.', + 'zip_model_expected' => 'Dyswgyl am wrthrych data ond wedi ffeindio ":type".', + 'zip_unique' => 'Mae rhaid y :attribute fod yn unigol i\'r fath o wrthrych yn y ZIP.', // Custom validation lines 'custom' => [ 'password-confirm' => [ - 'required_with' => 'Password confirmation required', + 'required_with' => 'Rhaid cadarnhau cyfrinair', ], ], diff --git a/lang/da/activities.php b/lang/da/activities.php index 48d159e8e60..fa11e8e3afb 100644 --- a/lang/da/activities.php +++ b/lang/da/activities.php @@ -15,7 +15,7 @@ 'page_restore' => 'gendannede side', 'page_restore_notification' => 'Siden blev gendannet', 'page_move' => 'flyttede side', - 'page_move_notification' => 'Page successfully moved', + 'page_move_notification' => 'Siden blev flyttet', // Chapters 'chapter_create' => 'oprettede kapitel', @@ -25,7 +25,7 @@ 'chapter_delete' => 'slettede kapitel', 'chapter_delete_notification' => 'Kapitel blev slettet', 'chapter_move' => 'flyttede kapitel', - 'chapter_move_notification' => 'Chapter successfully moved', + 'chapter_move_notification' => 'Kapitlet blev flyttet', // Books 'book_create' => 'oprettede bog', @@ -40,41 +40,41 @@ 'book_sort_notification' => 'Bogen blev re-sorteret', // Bookshelves - 'bookshelf_create' => 'created shelf', - 'bookshelf_create_notification' => 'Shelf successfully created', - 'bookshelf_create_from_book' => 'converted book to shelf', + 'bookshelf_create' => 'oprettede reol', + 'bookshelf_create_notification' => 'Reolen blev oprettet', + 'bookshelf_create_from_book' => 'omdannede bog til reol', 'bookshelf_create_from_book_notification' => 'Bogen blev omdannet til en bogreal', - 'bookshelf_update' => 'updated shelf', - 'bookshelf_update_notification' => 'Shelf successfully updated', - 'bookshelf_delete' => 'deleted shelf', - 'bookshelf_delete_notification' => 'Shelf successfully deleted', + 'bookshelf_update' => 'opdaterede reolen', + 'bookshelf_update_notification' => 'Reolen blev opdateret', + 'bookshelf_delete' => 'slettede reol', + 'bookshelf_delete_notification' => 'Reolen blev slettet', // Revisions - 'revision_restore' => 'restored revision', - 'revision_delete' => 'deleted revision', - 'revision_delete_notification' => 'Revision successfully deleted', + 'revision_restore' => 'gendannede version', + 'revision_delete' => 'slettede version', + 'revision_delete_notification' => 'Versionen blev slettet', // Favourites 'favourite_add_notification' => '":name" er blevet tilføjet til dine favoritter', 'favourite_remove_notification' => '":name" er blevet fjernet fra dine favoritter', // Watching - 'watch_update_level_notification' => 'Watch preferences successfully updated', + 'watch_update_level_notification' => 'Opdatering af urets præferencer lykkedes', // Auth - 'auth_login' => 'logged in', - 'auth_register' => 'registered as new user', - 'auth_password_reset_request' => 'requested user password reset', - 'auth_password_reset_update' => 'reset user password', - 'mfa_setup_method' => 'configured MFA method', + 'auth_login' => 'loggede ind', + 'auth_register' => 'registreret som ny bruger', + 'auth_password_reset_request' => 'anmodet om nulstilling af brugeradgangskode', + 'auth_password_reset_update' => 'nulstil adgangskode', + 'mfa_setup_method' => 'konfigureret MFA metode', 'mfa_setup_method_notification' => 'Multi-faktor metode konfigureret', - 'mfa_remove_method' => 'removed MFA method', + 'mfa_remove_method' => 'fjernet MFA metode', 'mfa_remove_method_notification' => 'Multi-faktor metode fjernet', // Settings - 'settings_update' => 'updated settings', - 'settings_update_notification' => 'Settings successfully updated', - 'maintenance_action_run' => 'ran maintenance action', + 'settings_update' => 'opdaterede indstillinger', + 'settings_update_notification' => 'Indstillinger opdateret', + 'maintenance_action_run' => 'kørte vedligeholdelsesaktion', // Webhooks 'webhook_create' => 'oprettede webhook', @@ -84,41 +84,57 @@ 'webhook_delete' => 'slettede webhooken', 'webhook_delete_notification' => 'Webhooken blev slettet', + // Imports + 'import_create' => 'oprettet import', + 'import_create_notification' => 'Importen er uploadet med succes', + 'import_run' => 'opdateret import', + 'import_run_notification' => 'Indhold importeret med succes', + 'import_delete' => 'slettet import', + 'import_delete_notification' => 'Import slettet med succes', + // Users - 'user_create' => 'created user', - 'user_create_notification' => 'User successfully created', - 'user_update' => 'updated user', + 'user_create' => 'opret bruger', + 'user_create_notification' => 'Bruger oprettet korrekt', + 'user_update' => 'opdateret bruger', 'user_update_notification' => 'Brugeren blev opdateret', - 'user_delete' => 'deleted user', + 'user_delete' => 'slettet bruger', 'user_delete_notification' => 'Brugeren blev fjernet', // API Tokens - 'api_token_create' => 'created api token', - 'api_token_create_notification' => 'API token successfully created', - 'api_token_update' => 'updated api token', - 'api_token_update_notification' => 'API token successfully updated', - 'api_token_delete' => 'deleted api token', - 'api_token_delete_notification' => 'API token successfully deleted', + 'api_token_create' => 'oprettet API token', + 'api_token_create_notification' => 'API-token oprettet med succes', + 'api_token_update' => 'opdateret API-token', + 'api_token_update_notification' => 'API-token opdateret med succes', + 'api_token_delete' => 'slettet API-token', + 'api_token_delete_notification' => 'API-token slettet med succes', // Roles - 'role_create' => 'created role', - 'role_create_notification' => 'Role successfully created', - 'role_update' => 'updated role', - 'role_update_notification' => 'Role successfully updated', - 'role_delete' => 'deleted role', - 'role_delete_notification' => 'Role successfully deleted', + 'role_create' => 'oprettet rolle', + 'role_create_notification' => 'Rolle oprettet', + 'role_update' => 'opdateret rolle', + 'role_update_notification' => 'Rolle opdateret', + 'role_delete' => 'slettet rolle', + 'role_delete_notification' => 'Rollen blev slettet', // Recycle Bin - 'recycle_bin_empty' => 'emptied recycle bin', - 'recycle_bin_restore' => 'restored from recycle bin', - 'recycle_bin_destroy' => 'removed from recycle bin', + 'recycle_bin_empty' => 'tømt papirkurven', + 'recycle_bin_restore' => 'gendannet fra papirkurven', + 'recycle_bin_destroy' => 'fjernet fra papirkurven', // Comments 'commented_on' => 'kommenterede til', - 'comment_create' => 'added comment', - 'comment_update' => 'updated comment', - 'comment_delete' => 'deleted comment', + 'comment_create' => 'tilføjet kommentar', + 'comment_update' => 'opdateret kommentar', + 'comment_delete' => 'slettet kommentar', + + // Sort Rules + 'sort_rule_create' => 'oprettet sorteringsregel', + 'sort_rule_create_notification' => 'Sorteringsregel oprettet med succes', + 'sort_rule_update' => 'opdateret sorteringsregel', + 'sort_rule_update_notification' => 'Sorteringsregel opdateret med succes', + 'sort_rule_delete' => 'slettet sorteringsregel', + 'sort_rule_delete_notification' => 'Sorteringsregel slettet med succes', // Other - 'permissions_update' => 'Tilladelser opdateret', + 'permissions_update' => 'opdaterede tilladelser', ]; diff --git a/lang/da/auth.php b/lang/da/auth.php index 2d2b7bebb88..dcc531125d5 100644 --- a/lang/da/auth.php +++ b/lang/da/auth.php @@ -6,11 +6,11 @@ */ return [ - 'failed' => 'Dee indtastede brugeroplysninger stemmer ikke overens med vores registreringer.', + 'failed' => 'De indtastede brugeroplysninger stemmer ikke overens med vores registreringer.', 'throttle' => 'For mange mislykkede loginforsøg. Prøv igen om :seconds sekunder.', // Login & Register - 'sign_up' => 'Registrér', + 'sign_up' => 'Registrer', 'log_in' => 'Log ind', 'log_in_with' => 'Log ind med :socialDriver', 'sign_up_with' => 'Registrér med :socialDriver', @@ -39,34 +39,34 @@ 'register_success' => 'Tak for din registrering. Du er nu registeret og logget ind.', // Login auto-initiation - 'auto_init_starting' => 'Attempting Login', - 'auto_init_starting_desc' => 'We\'re contacting your authentication system to start the login process. If there\'s no progress after 5 seconds you can try clicking the link below.', - 'auto_init_start_link' => 'Proceed with authentication', + 'auto_init_starting' => 'Forsøger Login', + 'auto_init_starting_desc' => 'Vi kontakter dit godkendelsessystem for at starte loginprocessen. Hvis der ikke er nogen fremskridt efter 5 sekunder, kan du prøve at klikke på linket nedenfor.', + 'auto_init_start_link' => 'Fortsæt med godkendelse', // Password Reset 'reset_password' => 'Nulstil adgangskode', - 'reset_password_send_instructions' => 'Indtast din E-Mail herunder og du vil blive sendt en E-Mail med et link til at nulstille din adgangskode.', + 'reset_password_send_instructions' => 'Indtast din e-mail herunder og du vil blive sendt en e-mail med et link til at nulstille din adgangskode.', 'reset_password_send_button' => 'Send link til nulstilling', 'reset_password_sent' => 'Et link til nulstilling af adgangskode sendes til :email, hvis den e-mail-adresse findes i systemet.', 'reset_password_success' => 'Din adgangskode er blevet nulstillet.', 'email_reset_subject' => 'Nulstil din :appName adgangskode', - 'email_reset_text' => 'Du modtager denne E-Mail fordi vi har modtaget en anmodning om at nulstille din adgangskode.', + 'email_reset_text' => 'Du modtager denne e-mail fordi vi har modtaget en anmodning om at nulstille din adgangskode.', 'email_reset_not_requested' => 'Hvis du ikke har anmodet om at få din adgangskode nulstillet, behøver du ikke at foretage dig noget.', // Email Confirmation 'email_confirm_subject' => 'Bekræft din E-Mail på :appName', 'email_confirm_greeting' => 'Tak for at oprette dig på :appName!', - 'email_confirm_text' => 'Bekræft venligst din E-Mail adresse ved at klikke på linket nedenfor:', - 'email_confirm_action' => 'Bekræft E-Mail', - 'email_confirm_send_error' => 'E-Mail-bekræftelse kræves, men systemet kunne ikke sende E-Mailen. Kontakt administratoren for at sikre, at E-Mail er konfigureret korrekt.', - 'email_confirm_success' => 'Din email er blevet bekræftet! Du bør nu kune logge ind med denne emailadresse.', + 'email_confirm_text' => 'Bekræft venligst din e-mail adresse ved at klikke på linket nedenfor:', + 'email_confirm_action' => 'Bekræft e-mail', + 'email_confirm_send_error' => 'E-mailbekræftelse kræves, men systemet kunne ikke sende e-mailen. Kontakt administratoren for at sikre, at e-mail er konfigureret korrekt.', + 'email_confirm_success' => 'Din e-mail er blevet bekræftet! Du bør nu kunne logge ind med denne e-mailadresse.', 'email_confirm_resent' => 'Bekræftelsesmail sendt, tjek venligst din indboks.', - 'email_confirm_thanks' => 'Thanks for confirming!', - 'email_confirm_thanks_desc' => 'Please wait a moment while your confirmation is handled. If you are not redirected after 3 seconds press the "Continue" link below to proceed.', + 'email_confirm_thanks' => 'Tak for bekræftelsen!', + 'email_confirm_thanks_desc' => 'Vent venligst et øjeblik, mens din bekræftelse behandles. Hvis du ikke bliver omdirigeret efter 3 sekunder, skal du trykke på linket "Fortsæt" nedenfor for at fortsætte.', - 'email_not_confirmed' => 'E-Mail adresse ikke bekræftet', - 'email_not_confirmed_text' => 'Din E-Mail adresse er endnu ikke blevet bekræftet.', - 'email_not_confirmed_click_link' => 'Klik venligst på linket i E-Mailen der blev sendt kort efter du registrerede dig.', + 'email_not_confirmed' => 'E-mailadresse ikke bekræftet', + 'email_not_confirmed_text' => 'Din e-mailadresse er endnu ikke blevet bekræftet.', + 'email_not_confirmed_click_link' => 'Klik venligst på linket i e-mailen der blev sendt kort efter du registrerede dig.', 'email_not_confirmed_resend' => 'Hvis du ikke kan finde E-Mailen, kan du du få gensendt bekræftelsesemailen ved at trykke herunder.', 'email_not_confirmed_resend_button' => 'Gensend bekræftelsesemail', @@ -91,27 +91,27 @@ 'mfa_option_totp_title' => 'Mobil app', 'mfa_option_totp_desc' => 'For at bruge multi-faktor godkendelse, skal du bruge en mobil app, der understøtter TOTP såsom Google Authenticator, Authy eller Microsoft Authenticator.', 'mfa_option_backup_codes_title' => 'Backup koder', - 'mfa_option_backup_codes_desc' => 'Gem sikkert et sæt af engangs backup koder, som du kan indtaste for at bekræfte din identitet.', + 'mfa_option_backup_codes_desc' => 'Genererer et sæt backup-koder til engangsbrug, som du skal indtaste ved login for at bekræfte din identitet. Sørg for at opbevare dem et sikkert sted.', 'mfa_gen_confirm_and_enable' => 'Bekræft og aktivér', 'mfa_gen_backup_codes_title' => 'Backup koder opsætning', - 'mfa_gen_backup_codes_desc' => 'Store the below list of codes in a safe place. When accessing the system you\'ll be able to use one of the codes as a second authentication mechanism.', + 'mfa_gen_backup_codes_desc' => 'Opbevar nedenstående liste med koder et sikkert sted. Når du får adgang til systemet, kan du bruge en af koderne som en ekstra godkendelsesmekanisme.', 'mfa_gen_backup_codes_download' => 'Download koder', 'mfa_gen_backup_codes_usage_warning' => 'Hver kode kan kun bruges en gang', 'mfa_gen_totp_title' => 'Mobil App Setup', - 'mfa_gen_totp_desc' => 'To use multi-factor authentication you\'ll need a mobile application that supports TOTP such as Google Authenticator, Authy or Microsoft Authenticator.', - 'mfa_gen_totp_scan' => 'Scan the QR code below using your preferred authentication app to get started.', + 'mfa_gen_totp_desc' => 'For at bruge multifaktorgodkendelse skal du bruge en mobilapplikation, der understøtter TOTP, f.eks. Google Authenticator, Authy eller Microsoft Authenticator.', + 'mfa_gen_totp_scan' => 'Scan QR-koden nedenfor med din foretrukne godkendelsesapp for at komme i gang.', 'mfa_gen_totp_verify_setup' => 'Verificer Opsætning', - 'mfa_gen_totp_verify_setup_desc' => 'Verify that all is working by entering a code, generated within your authentication app, in the input box below:', - 'mfa_gen_totp_provide_code_here' => 'Provide your app generated code here', - 'mfa_verify_access' => 'Verify Access', - 'mfa_verify_access_desc' => 'Your user account requires you to confirm your identity via an additional level of verification before you\'re granted access. Verify using one of your configured methods to continue.', + 'mfa_gen_totp_verify_setup_desc' => 'Kontrollér, at alt fungerer, ved at indtaste en kode, der er genereret i din godkendelsesapp, i indtastningsfeltet nedenfor:', + 'mfa_gen_totp_provide_code_here' => 'Angiv din app-genererede kode her', + 'mfa_verify_access' => 'Bekræft Adgang', + 'mfa_verify_access_desc' => 'Din brugerkonto kræver, at du bekræfter din identitet via et ekstra verifikationsniveau, før du får adgang. Bekræft ved hjælp af en af dine konfigurerede metoder for at fortsætte.', 'mfa_verify_no_methods' => 'Ingen Metoder Konfigureret', - 'mfa_verify_no_methods_desc' => 'No multi-factor authentication methods could be found for your account. You\'ll need to set up at least one method before you gain access.', + 'mfa_verify_no_methods_desc' => 'Der blev ikke fundet nogen metoder til multifaktorgodkendelse til din konto. Du skal konfigurere mindst én metode, før du får adgang.', 'mfa_verify_use_totp' => 'Bekræft ved brug af en mobil app', 'mfa_verify_use_backup_codes' => 'Bekræft ved hjælp af en backup kode', 'mfa_verify_backup_code' => 'Backup Kode', 'mfa_verify_backup_code_desc' => 'Indtast en af dine resterende backup koder nedenfor:', 'mfa_verify_backup_code_enter_here' => 'Indtast backup kode her', - 'mfa_verify_totp_desc' => 'Enter the code, generated using your mobile app, below:', - 'mfa_setup_login_notification' => 'Multi-factor method configured, Please now login again using the configured method.', + 'mfa_verify_totp_desc' => 'Indtast koden, der er genereret med din mobilapp, nedenfor:', + 'mfa_setup_login_notification' => 'Multifaktormetode konfigureret, log venligst ind igen med den konfigurerede metode.', ]; diff --git a/lang/da/common.php b/lang/da/common.php index 8c3aa24a79f..db75772c857 100644 --- a/lang/da/common.php +++ b/lang/da/common.php @@ -6,7 +6,7 @@ // Buttons 'cancel' => 'Annuller', - 'close' => 'Close', + 'close' => 'Luk', 'confirm' => 'Bekræft', 'back' => 'Tilbage', 'save' => 'Gem', @@ -20,16 +20,18 @@ 'description' => 'Beskrivelse', 'role' => 'Rolle', 'cover_image' => 'Coverbillede', - 'cover_image_description' => 'Dette billede skal være omtrent 440x250px.', + 'cover_image_description' => 'Dette billede skal være omkring 440x250px selvom det vil være fleksibelt skaleret & beskåret for at passe til brugergrænsefladen i forskellige scenarier efter behov. Så de faktiske dimensioner for visning vil være forskellige.', // Actions 'actions' => 'Handlinger', 'view' => 'Vis', 'view_all' => 'Vis alle', - 'new' => 'New', + 'new' => 'Ny', 'create' => 'Opret', 'update' => 'Opdater', 'edit' => 'Rediger', + 'archive' => 'Arkiver', + 'unarchive' => 'Tilbagekald', 'sort' => 'Sorter', 'move' => 'Flyt', 'copy' => 'Kopier', @@ -42,17 +44,17 @@ 'remove' => 'Fjern', 'add' => 'Tilføj', 'configure' => 'Konfigurer', - 'manage' => 'Manage', + 'manage' => 'Administrer', 'fullscreen' => 'Fuld skærm', 'favourite' => 'Foretrukken', 'unfavourite' => 'Fjern som foretrukken', 'next' => 'Næste', 'previous' => 'Forrige', - 'filter_active' => 'Aktivt Filter:', - 'filter_clear' => 'Nulstil Filter', + 'filter_active' => 'Aktivt filter:', + 'filter_clear' => 'Nulstil filter', 'download' => 'Hent', 'open_in_tab' => 'Åben i ny fane', - 'open' => 'Open', + 'open' => 'Åbn', // Sort Options 'sort_options' => 'Sorteringsindstillinger', @@ -84,14 +86,14 @@ 'none' => 'Ingen', // Header - 'homepage' => 'Homepage', + 'homepage' => 'Forside', 'header_menu_expand' => 'Udvid header menu', 'profile_menu' => 'Profilmenu', 'view_profile' => 'Vis profil', 'edit_profile' => 'Redigér Profil', 'dark_mode' => 'Mørk tilstand', 'light_mode' => 'Lys tilstand', - 'global_search' => 'Global Search', + 'global_search' => 'Global søgning', // Layout tabs 'tab_info' => 'Info', @@ -107,4 +109,7 @@ // Not directly used but available for convenience to users. 'privacy_policy' => 'Privatlivspolitik', 'terms_of_service' => 'Tjenestevilkår', + + // OpenSearch + 'opensearch_description' => 'Søg :appName', ]; diff --git a/lang/da/components.php b/lang/da/components.php index bcbf1167285..3deeadf485d 100644 --- a/lang/da/components.php +++ b/lang/da/components.php @@ -6,36 +6,36 @@ // Image Manager 'image_select' => 'Billedselektion', - 'image_list' => 'Image List', - 'image_details' => 'Image Details', - 'image_upload' => 'Upload Image', - 'image_intro' => 'Here you can select and manage images that have been previously uploaded to the system.', - 'image_intro_upload' => 'Upload a new image by dragging an image file into this window, or by using the "Upload Image" button above.', + 'image_list' => 'Billedliste', + 'image_details' => 'Billeddetaljer', + 'image_upload' => 'Upload billede', + 'image_intro' => 'Her kan du vælge og administrere billeder, der tidligere er blevet uploadet til systemet.', + 'image_intro_upload' => 'Upload et nyt billede ved at trække en billedfil ind i dette vindue, eller ved at bruge knappen "Upload billede" ovenfor.', 'image_all' => 'Alt', 'image_all_title' => 'Se alle billeder', 'image_book_title' => 'Vis billeder uploadet til denne bog', 'image_page_title' => 'Vis billeder uploadet til denne side', 'image_search_hint' => 'Søg efter billednavn', 'image_uploaded' => 'Uploadet :uploadedDate', - 'image_uploaded_by' => 'Uploaded by :userName', - 'image_uploaded_to' => 'Uploaded to :pageLink', - 'image_updated' => 'Updated :updateDate', + 'image_uploaded_by' => 'Uploadet af :userName', + 'image_uploaded_to' => 'Uploadet til :pageLink', + 'image_updated' => 'Opdateret :updateDate', 'image_load_more' => 'Indlæse mere', 'image_image_name' => 'Billednavn', 'image_delete_used' => 'Dette billede er brugt på siderne nedenfor.', 'image_delete_confirm_text' => 'Er du sikker på at du vil slette dette billede?', 'image_select_image' => 'Vælg billede', 'image_dropzone' => 'Træk-og-slip billede eller klik her for at uploade', - 'image_dropzone_drop' => 'Drop images here to upload', + 'image_dropzone_drop' => 'Slip billeder her for at uploade', 'images_deleted' => 'Billede slettet', 'image_preview' => 'Billedeksempel', 'image_upload_success' => 'Foto uploadet', 'image_update_success' => 'Billeddetaljer succesfuldt opdateret', 'image_delete_success' => 'Billede slettet', - 'image_replace' => 'Replace Image', - 'image_replace_success' => 'Image file successfully updated', - 'image_rebuild_thumbs' => 'Regenerate Size Variations', - 'image_rebuild_thumbs_success' => 'Image size variations successfully rebuilt!', + 'image_replace' => 'Erstat billede', + 'image_replace_success' => 'Billedfil blev opdateret', + 'image_rebuild_thumbs' => 'Regenerer størrelsesvariationer', + 'image_rebuild_thumbs_success' => 'Variationer i billedstørrelse blev genopbygget!', // Code Editor 'code_editor' => 'Rediger kode', diff --git a/lang/da/editor.php b/lang/da/editor.php index 38638ba86b5..4c8aa0f7212 100644 --- a/lang/da/editor.php +++ b/lang/da/editor.php @@ -13,8 +13,9 @@ 'cancel' => 'Annuller', 'save' => 'Gem', 'close' => 'Luk', + 'apply' => 'Anvend', 'undo' => 'Fortryd', - 'redo' => 'Redo', + 'redo' => 'Gendan', 'left' => 'Venstre', 'center' => 'Midten', 'right' => 'Højre', @@ -31,7 +32,7 @@ 'header_large' => 'Stor Overskrift', 'header_medium' => 'Mellemstor Overskrift', 'header_small' => 'Lille Overskrift', - 'header_tiny' => 'Tiny Header', + 'header_tiny' => 'Lille Header', 'paragraph' => 'Paragraf', 'blockquote' => 'Citat', 'inline_code' => 'Inline kode', @@ -47,6 +48,7 @@ 'superscript' => 'Hævet', 'subscript' => 'Sænket', 'text_color' => 'Tekstfarve', + 'highlight_color' => 'Highlight color', 'custom_color' => 'Tilpasset farve', 'remove_color' => 'Fjern farve', 'background_color' => 'Baggrundsfarve', @@ -60,27 +62,30 @@ 'indent_increase' => 'Forøg indrykning', 'indent_decrease' => 'Formindsk indrykning', 'table' => 'Tabel', - 'insert_image' => 'Insert image', - 'insert_image_title' => 'Insert/Edit Image', + 'insert_image' => 'Indsæt billede', + 'insert_image_title' => 'Indsæt/rediger billede', 'insert_link' => 'Indsæt/rediger link', 'insert_link_title' => 'Indsæt/Rediger Link', 'insert_horizontal_line' => 'Indsæt vandret linje', 'insert_code_block' => 'Indsæt kodeblok', - 'edit_code_block' => 'Edit code block', + 'edit_code_block' => 'Rediger kodeblok', 'insert_drawing' => 'Indsæt/rediger tegning', - 'drawing_manager' => 'Drawing manager', + 'drawing_manager' => 'Tegningsansvarlig', 'insert_media' => 'Indsæt/rediger medie', 'insert_media_title' => 'Indsæt/Rediger Medie', 'clear_formatting' => 'Ryd formatering', 'source_code' => 'Kildekode', 'source_code_title' => 'Kildekode', 'fullscreen' => 'Fuld skærm', - 'image_options' => 'Image options', + 'image_options' => 'Billedindstillinger', // Tables 'table_properties' => 'Tabelegenskaber', 'table_properties_title' => 'Tabelegenskaber', 'delete_table' => 'Slet tabel', + 'table_clear_formatting' => 'Ryd tabelformatering', + 'resize_to_contents' => 'Ændre størrelse til indhold', + 'row_header' => 'Rækkeoverskrift', 'insert_row_before' => 'Indsæt række før', 'insert_row_after' => 'Indsæt række efter', 'delete_row' => 'Slet række', @@ -94,8 +99,8 @@ 'cell_properties_title' => 'Celle Egenskaber', 'cell_type' => 'Celle type', 'cell_type_cell' => 'Celle', - 'cell_scope' => 'Scope', - 'cell_type_header' => 'Header cell', + 'cell_scope' => 'Omfang', + 'cell_type_header' => 'Overskriftscelle', 'merge_cells' => 'Flet celler', 'split_cell' => 'Opdel celle', 'table_row_group' => 'Række Gruppe', @@ -120,18 +125,18 @@ 'copy_column' => 'Kopier kolonne', 'paste_column_before' => 'Indsæt kolonne før', 'paste_column_after' => 'Indsæt kolonne efter', - 'cell_padding' => 'Cell padding', - 'cell_spacing' => 'Cell spacing', - 'caption' => 'Caption', - 'show_caption' => 'Show caption', - 'constrain' => 'Constrain proportions', + 'cell_padding' => 'Celle margen', + 'cell_spacing' => 'Celle afstand', + 'caption' => 'Citat', + 'show_caption' => 'Vis citat', + 'constrain' => 'Begræns proportioner', 'cell_border_solid' => 'Solid', 'cell_border_dotted' => 'Prikket', 'cell_border_dashed' => 'Stiplet', 'cell_border_double' => 'Dobbelt', - 'cell_border_groove' => 'Groove', - 'cell_border_ridge' => 'Ridge', - 'cell_border_inset' => 'Inset', + 'cell_border_groove' => 'Udfræsning', + 'cell_border_ridge' => 'Kant', + 'cell_border_inset' => 'Indsat', 'cell_border_outset' => 'Outset', 'cell_border_none' => 'Ingen', 'cell_border_hidden' => 'Gemt', @@ -139,32 +144,35 @@ // Images, links, details/summary & embed 'source' => 'Kilde', 'alt_desc' => 'Alternativ beskrivelse', - 'embed' => 'Embed', - 'paste_embed' => 'Paste your embed code below:', + 'embed' => 'Indlejre', + 'paste_embed' => 'Indsæt din indlejringskode nedenfor:', 'url' => 'URL', 'text_to_display' => 'Tekst til visning', 'title' => 'Titel', - 'open_link' => 'Open link', - 'open_link_in' => 'Open link in...', + 'browse_links' => 'Gennemse links', + 'open_link' => 'Åbn link', + 'open_link_in' => 'Åbn link i...', 'open_link_current' => 'Nuværende vindue', 'open_link_new' => 'Nyt vindue', - 'remove_link' => 'Remove link', - 'insert_collapsible' => 'Insert collapsible block', - 'collapsible_unwrap' => 'Unwrap', - 'edit_label' => 'Edit label', - 'toggle_open_closed' => 'Toggle open/closed', - 'collapsible_edit' => 'Edit collapsible block', - 'toggle_label' => 'Toggle label', + 'remove_link' => 'Fjern link', + 'insert_collapsible' => 'Indsæt sammenklappelig blok', + 'collapsible_unwrap' => 'Pak ud', + 'edit_label' => 'Rediger mærkat', + 'toggle_open_closed' => 'Skift mellem åben og lukket', + 'collapsible_edit' => 'Rediger sammenklappelig blok', + 'toggle_label' => 'Skift etiket', // About view - 'about' => 'About the editor', - 'about_title' => 'About the WYSIWYG Editor', - 'editor_license' => 'Editor License & Copyright', - 'editor_tiny_license' => 'This editor is built using :tinyLink which is provided under the MIT license.', - 'editor_tiny_license_link' => 'The copyright and license details of TinyMCE can be found here.', + 'about' => 'Om redaktøren', + 'about_title' => 'Om WYSIWYG-editoren', + 'editor_license' => 'Editor-licens og copyright', + 'editor_lexical_license' => 'Denne editor er bygget som en forgrening af :lexicalLink, som distribueres under MIT-licensen.', + 'editor_lexical_license_link' => 'Du kan finde alle licensoplysninger her.', + 'editor_tiny_license' => 'Denne editor er bygget ved hjælp af :tinyLink, som leveres under MIT-licensen.', + 'editor_tiny_license_link' => 'Oplysninger om copyright og licens for TinyMCE kan findes her.', 'save_continue' => 'Gem og fortsæt', - 'callouts_cycle' => '(Keep pressing to toggle through types)', - 'link_selector' => 'Link to content', + 'callouts_cycle' => '(Bliv ved med at trykke for at skifte mellem typerne)', + 'link_selector' => 'Link til indhold', 'shortcuts' => 'Genveje', 'shortcut' => 'Genvej', 'shortcuts_intro' => 'Følgende genveje er tilgængelige i editoren:', diff --git a/lang/da/entities.php b/lang/da/entities.php index 88945019c98..66e3fd2063b 100644 --- a/lang/da/entities.php +++ b/lang/da/entities.php @@ -23,9 +23,9 @@ 'meta_updated' => 'Opdateret :timeLength', 'meta_updated_name' => 'Opdateret :timeLength af :user', 'meta_owned_name' => 'Ejet af :user', - 'meta_reference_count' => 'Referenced by :count item|Referenced by :count items', + 'meta_reference_count' => 'Refereret af :count item|Refereret af :count items', 'entity_select' => 'Vælg emne', - 'entity_select_lack_permission' => 'You don\'t have the required permissions to select this item', + 'entity_select_lack_permission' => 'Du har ikke de nødvendige tilladelser til at vælge dette element', 'images' => 'Billeder', 'my_recent_drafts' => 'Mine seneste kladder', 'my_recently_viewed' => 'Mine senest viste', @@ -34,23 +34,51 @@ 'no_pages_viewed' => 'Du har ikke besøgt nogle sider', 'no_pages_recently_created' => 'Ingen sider er blevet oprettet for nyligt', 'no_pages_recently_updated' => 'Ingen sider er blevet opdateret for nyligt', - 'export' => 'Exporter', + 'export' => 'Eksporter', 'export_html' => 'Indeholdt webfil', 'export_pdf' => 'PDF-fil', 'export_text' => 'Almindelig tekstfil', 'export_md' => 'Markdown Fil', + 'export_zip' => 'Bærbar ZIP', + 'default_template' => 'Standard sideskabelon', + 'default_template_explain' => 'Tildel en sideskabelon, der vil blive brugt som standardindhold for alle sider, der oprettes i dette element. Husk, at dette kun vil blive brugt, hvis sideskaberen har adgang til den valgte skabelonside.', + 'default_template_select' => 'Vælg en skabelonside', + 'import' => 'Importer', + 'import_validate' => 'Valider import', + 'import_desc' => 'Importer bøger, kapitler og sider ved hjælp af en bærbar zip-eksport fra den samme eller en anden instans. Vælg en ZIP-fil for at fortsætte. Når filen er blevet uploadet og valideret, kan du konfigurere og bekræfte importen i den næste visning.', + 'import_zip_select' => 'Vælg den ZIP-fil, der skal uploades', + 'import_zip_validation_errors' => 'Der blev opdaget fejl under validering af den medfølgende ZIP-fil:', + 'import_pending' => 'Afventende import', + 'import_pending_none' => 'Der er ikke startet nogen import.', + 'import_continue' => 'Fortsæt med at importere', + 'import_continue_desc' => 'Gennemgå det indhold, der skal importeres fra den uploadede ZIP-fil. Når du er klar, skal du køre importen for at tilføje dens indhold til dette system. Den uploadede ZIP-importfil fjernes automatisk, når importen er vellykket.', + 'import_details' => 'Import detaljer', + 'import_run' => 'Kør import', + 'import_size' => ':size Importer ZIP-størrelse', + 'import_uploaded_at' => 'Uploadet :relativeTime', + 'import_uploaded_by' => 'Uploadet af', + 'import_location' => 'Importplacering', + 'import_location_desc' => 'Vælg en målplacering for dit importerede indhold. Du skal have de relevante tilladelser til at oprette på den valgte placering.', + 'import_delete_confirm' => 'Er du sikker på, at du vil slette denne import?', + 'import_delete_desc' => 'Dette vil slette den uploadede ZIP-fil og kan ikke fortrydes.', + 'import_errors' => 'Importfejl', + 'import_errors_desc' => 'Følgende fejl opstod under importforsøget:', + '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', // Permissions and restrictions 'permissions' => 'Rettigheder', - 'permissions_desc' => 'Set permissions here to override the default permissions provided by user roles.', - 'permissions_book_cascade' => 'Permissions set on books will automatically cascade to child chapters and pages, unless they have their own permissions defined.', - 'permissions_chapter_cascade' => 'Permissions set on chapters will automatically cascade to child pages, unless they have their own permissions defined.', + 'permissions_desc' => 'Angiv tilladelser her for at tilsidesætte de standardtilladelser, der gives af brugerroller.', + 'permissions_book_cascade' => 'Tilladelser, der er indstillet på bøger, vil automatisk blive overført til underordnede kapitler og sider, medmindre de har deres egne tilladelser defineret.', + 'permissions_chapter_cascade' => 'Tilladelser, der er indstillet på kapitler, overføres automatisk til underordnede sider, medmindre de har deres egne tilladelser defineret.', 'permissions_save' => 'Gem tilladelser', 'permissions_owner' => 'Ejer', - 'permissions_role_everyone_else' => 'Everyone Else', - 'permissions_role_everyone_else_desc' => 'Set permissions for all roles not specifically overridden.', - 'permissions_role_override' => 'Override permissions for role', - 'permissions_inherit_defaults' => 'Inherit defaults', + 'permissions_role_everyone_else' => 'Alle andre', + 'permissions_role_everyone_else_desc' => 'Indstil tilladelser for alle roller, der ikke specifikt er tilsidesat.', + 'permissions_role_override' => 'Tilsidesæt tilladelser for rolle', + 'permissions_inherit_defaults' => 'Arv standardindstillinger', // Search 'search_results' => 'Søgeresultater', @@ -93,24 +121,24 @@ 'shelves_save' => 'Gem reol', 'shelves_books' => 'Bøger på denne reol', 'shelves_add_books' => 'Tilføj bøger til denne reol', - 'shelves_drag_books' => 'Drag books below to add them to this shelf', + 'shelves_drag_books' => 'Træk bøger nedenfor for at tilføje dem til denne hylde', 'shelves_empty_contents' => 'Denne reol har ingen bøger tilknyttet til den', 'shelves_edit_and_assign' => 'Rediger reol for at tilføje bøger', - 'shelves_edit_named' => 'Edit Shelf :name', - 'shelves_edit' => 'Edit Shelf', - 'shelves_delete' => 'Delete Shelf', - 'shelves_delete_named' => 'Delete Shelf :name', - 'shelves_delete_explain' => "This will delete the shelf with the name ':name'. Contained books will not be deleted.", - 'shelves_delete_confirmation' => 'Are you sure you want to delete this shelf?', - 'shelves_permissions' => 'Shelf Permissions', - 'shelves_permissions_updated' => 'Shelf Permissions Updated', - 'shelves_permissions_active' => 'Shelf Permissions Active', - 'shelves_permissions_cascade_warning' => 'Permissions on shelves do not automatically cascade to contained books. This is because a book can exist on multiple shelves. Permissions can however be copied down to child books using the option found below.', - 'shelves_permissions_create' => 'Shelf create permissions are only used for copying permissions to child books using the action below. They do not control the ability to create books.', + 'shelves_edit_named' => 'Rediger hylde :navn', + 'shelves_edit' => 'Rediger Reol', + 'shelves_delete' => 'Slettede reoler', + 'shelves_delete_named' => 'Slet hylde :navn', + 'shelves_delete_explain' => "Dette vil slette hylden med navnet ':navn'. Indeholdte bøger vil ikke blive slettet.", + 'shelves_delete_confirmation' => 'Er du sikker på, at du vil slette denne hylde?', + 'shelves_permissions' => 'Tilladelser til hylder', + 'shelves_permissions_updated' => 'Tilladelser til hylder opdateret', + 'shelves_permissions_active' => 'Hyldetilladelser aktive', + 'shelves_permissions_cascade_warning' => 'Tilladelser på hylder overføres ikke automatisk til indeholdte bøger. Det skyldes, at en bog kan findes på flere hylder. Tilladelser kan dog kopieres ned til underordnede bøger ved hjælp af nedenstående mulighed.', + 'shelves_permissions_create' => 'Hyldeoprettelsestilladelser bruges kun til at kopiere tilladelser til underordnede bøger ved hjælp af nedenstående handling. De kontrollerer ikke muligheden for at oprette bøger.', 'shelves_copy_permissions_to_books' => 'Kopier tilladelser til bøger', 'shelves_copy_permissions' => 'Kopier tilladelser', - 'shelves_copy_permissions_explain' => 'This will apply the current permission settings of this shelf to all books contained within. Before activating, ensure any changes to the permissions of this shelf have been saved.', - 'shelves_copy_permission_success' => 'Shelf permissions copied to :count books', + 'shelves_copy_permissions_explain' => 'Dette vil anvende de aktuelle tilladelsesindstillinger for denne hylde på alle bøger, der er indeholdt i den. Før du aktiverer, skal du sikre dig, at eventuelle ændringer af tilladelserne for denne hylde er blevet gemt.', + 'shelves_copy_permission_success' => 'Hyldetilladelser kopieret til :count books', // Books 'book' => 'Bog', @@ -132,9 +160,6 @@ 'books_edit_named' => 'Rediger bog :bookName', 'books_form_book_name' => 'Bognavn', 'books_save' => 'Gem bog', - 'books_default_template' => 'Default Page Template', - 'books_default_template_explain' => 'Assign a page template that will be used as the default content for all new pages in this book. Keep in mind this will only be used if the page creator has view access to those chosen template page.', - 'books_default_template_select' => 'Select a template page', 'books_permissions' => 'Bogtilladelser', 'books_permissions_updated' => 'Bogtilladelser opdateret', 'books_empty_contents' => 'Ingen sider eller kapitler er blevet oprettet i denne bog.', @@ -145,7 +170,9 @@ 'books_search_this' => 'Søg i denne bog', 'books_navigation' => 'Bognavigation', 'books_sort' => 'Sorter bogindhold', - 'books_sort_desc' => 'Move chapters and pages within a book to reorganise its contents. Other books can be added which allows easy moving of chapters and pages between books.', + 'books_sort_desc' => 'Flyt kapitler og sider i en bog for at omorganisere dens indhold. Der kan tilføjes andre bøger, som gør det nemt at flytte kapitler og sider mellem bøgerne. Man kan indstille en automatisk sorteringsregel, så bogens indhold automatisk sorteres efter ændringer.', + 'books_sort_auto_sort' => 'Mulighed for automatisk sortering', + 'books_sort_auto_sort_active' => 'Automatisk sortering Aktiv: :sortName', 'books_sort_named' => 'Sorter bog :bookName', 'books_sort_name' => 'Sortér efter navn', 'books_sort_created' => 'Sortér efter oprettelsesdato', @@ -154,17 +181,17 @@ 'books_sort_chapters_last' => 'Kapitler sidst', 'books_sort_show_other' => 'Vis andre bøger', 'books_sort_save' => 'Gem ny ordre', - 'books_sort_show_other_desc' => 'Add other books here to include them in the sort operation, and allow easy cross-book reorganisation.', - 'books_sort_move_up' => 'Move Up', - 'books_sort_move_down' => 'Move Down', - 'books_sort_move_prev_book' => 'Move to Previous Book', - 'books_sort_move_next_book' => 'Move to Next Book', - 'books_sort_move_prev_chapter' => 'Move Into Previous Chapter', - 'books_sort_move_next_chapter' => 'Move Into Next Chapter', - 'books_sort_move_book_start' => 'Move to Start of Book', - 'books_sort_move_book_end' => 'Move to End of Book', - 'books_sort_move_before_chapter' => 'Move to Before Chapter', - 'books_sort_move_after_chapter' => 'Move to After Chapter', + 'books_sort_show_other_desc' => 'Tilføj andre bøger her for at inkludere dem i sorteringen og gøre det nemt at omorganisere på tværs af bøger.', + 'books_sort_move_up' => 'Flyt op', + 'books_sort_move_down' => 'Flyt ned', + 'books_sort_move_prev_book' => 'Flyt til forrige bog', + 'books_sort_move_next_book' => 'Flyt til næste bog', + 'books_sort_move_prev_chapter' => 'Flyt Til Foregående Kapitel', + 'books_sort_move_next_chapter' => 'Flyt Til Næste Kapitel', + 'books_sort_move_book_start' => 'Flyt til Start af bog', + 'books_sort_move_book_end' => 'Flyt til slutningen af bogen', + 'books_sort_move_before_chapter' => 'Gå til kapitlet Før', + 'books_sort_move_after_chapter' => 'Flyt til Efter kapitel', 'books_copy' => 'Kopier Bog', 'books_copy_success' => 'Bogen blev kopieret', @@ -191,7 +218,7 @@ 'chapters_permissions_active' => 'Aktive kapiteltilladelser', 'chapters_permissions_success' => 'Kapiteltilladelser opdateret', 'chapters_search_this' => 'Søg i dette kapitel', - 'chapter_sort_book' => 'Sort Book', + 'chapter_sort_book' => 'Sorter bog', // Pages 'page' => 'Side', @@ -207,7 +234,7 @@ 'pages_delete_draft' => 'Slet kladdeside', 'pages_delete_success' => 'Side slettet', 'pages_delete_draft_success' => 'Kladdeside slettet', - 'pages_delete_warning_template' => 'This page is in active use as a book default page template. These books will no longer have a default page template assigned after this page is deleted.', + 'pages_delete_warning_template' => 'Denne side er i aktiv brug som standardsideskabelon for en bog eller et kapitel. Disse bøger eller kapitler vil ikke længere have en standardsideskabelon tildelt, når denne side er slettet.', 'pages_delete_confirm' => 'Er du sikker på, du vil slette denne side?', 'pages_delete_draft_confirm' => 'Er du sikker på at du vil slette denne kladdesidde?', 'pages_editing_named' => 'Redigerer :pageName', @@ -218,21 +245,23 @@ 'pages_editing_page' => 'Redigerer side', 'pages_edit_draft_save_at' => 'Kladde gemt ved ', 'pages_edit_delete_draft' => 'Slet kladde', - 'pages_edit_delete_draft_confirm' => 'Are you sure you want to delete your draft page changes? All of your changes, since the last full save, will be lost and the editor will be updated with the latest page non-draft save state.', + 'pages_edit_delete_draft_confirm' => 'Er du sikker på, at du vil slette dine ændringer på kladdesiden? Alle dine ændringer siden den sidste fulde lagring vil gå tabt, og editoren vil blive opdateret med den seneste lagring af siden uden udkast.', 'pages_edit_discard_draft' => 'Kassér kladde', 'pages_edit_switch_to_markdown' => 'Skift til Markdown redigering', - 'pages_edit_switch_to_markdown_clean' => '(Clean Content)', - 'pages_edit_switch_to_markdown_stable' => '(Stable Content)', + 'pages_edit_switch_to_markdown_clean' => '(Rent indhold)', + 'pages_edit_switch_to_markdown_stable' => '(Stabilt indhold)', 'pages_edit_switch_to_wysiwyg' => 'Skift til WYSIWYG redigering', + 'pages_edit_switch_to_new_wysiwyg' => 'Skift til ny WYSIWYG (Hvad man ser, er hvad man får)', + 'pages_edit_switch_to_new_wysiwyg_desc' => '(In Beta Testing)', 'pages_edit_set_changelog' => 'Sæt ændringsoversigt', 'pages_edit_enter_changelog_desc' => 'Indtast en kort beskrivelse af ændringer du har lavet', 'pages_edit_enter_changelog' => 'Indtast ændringsoversigt', 'pages_editor_switch_title' => 'Skift Editor', - 'pages_editor_switch_are_you_sure' => 'Are you sure you want to change the editor for this page?', - 'pages_editor_switch_consider_following' => 'Consider the following when changing editors:', - 'pages_editor_switch_consideration_a' => 'Once saved, the new editor option will be used by any future editors, including those that may not be able to change editor type themselves.', - 'pages_editor_switch_consideration_b' => 'This can potentially lead to a loss of detail and syntax in certain circumstances.', - 'pages_editor_switch_consideration_c' => 'Tag or changelog changes, made since last save, won\'t persist across this change.', + 'pages_editor_switch_are_you_sure' => 'Er du sikker på, at du vil ændre editoren for denne side?', + 'pages_editor_switch_consider_following' => 'Overvej følgende ved skift af redaktører:', + 'pages_editor_switch_consideration_a' => 'Når den nye redaktørindstilling er gemt, vil den blive brugt af alle fremtidige redaktører, også dem, der måske ikke selv kan ændre redaktørtype.', + 'pages_editor_switch_consideration_b' => 'Dette kan potentielt føre til tab af detaljer og syntaks under visse omstændigheder.', + 'pages_editor_switch_consideration_c' => 'Ændringer i tag eller changelog, der er foretaget siden sidste lagring, fortsætter ikke på tværs af denne ændring.', 'pages_save' => 'Gem siden', 'pages_title' => 'Overskrift', 'pages_name' => 'Sidenavn', @@ -241,10 +270,11 @@ 'pages_md_insert_image' => 'Indsæt billede', 'pages_md_insert_link' => 'Indsæt emnelink', 'pages_md_insert_drawing' => 'Indsæt tegning', - 'pages_md_show_preview' => 'Show preview', - 'pages_md_sync_scroll' => 'Sync preview scroll', - 'pages_drawing_unsaved' => 'Unsaved Drawing Found', - 'pages_drawing_unsaved_confirm' => 'Unsaved drawing data was found from a previous failed drawing save attempt. Would you like to restore and continue editing this unsaved drawing?', + 'pages_md_show_preview' => 'Vis forhåndsvisning', + 'pages_md_sync_scroll' => 'Rulning af forhåndsvisning af synkronisering', + 'pages_md_plain_editor' => 'Klartekst editor', + 'pages_drawing_unsaved' => 'Ikke gemt tegning fundet', + 'pages_drawing_unsaved_confirm' => 'Der blev fundet ikke-gemte tegningsdata fra et tidligere mislykket forsøg på at gemme en tegning. Vil du gendanne og fortsætte med at redigere denne ikke-gemte tegning?', 'pages_not_in_chapter' => 'Side er ikke i et kapitel', 'pages_move' => 'Flyt side', 'pages_copy' => 'Kopier side', @@ -254,14 +284,14 @@ 'pages_permissions_success' => 'Sidetilladelser opdateret', 'pages_revision' => 'Revision', 'pages_revisions' => 'Sidserevisioner', - 'pages_revisions_desc' => 'Listed below are all the past revisions of this page. You can look back upon, compare, and restore old page versions if permissions allow. The full history of the page may not be fully reflected here since, depending on system configuration, old revisions could be auto-deleted.', + 'pages_revisions_desc' => 'Nedenfor findes alle tidligere revisioner af denne side. Du kan se tilbage på, sammenligne og gendanne gamle sideversioner, hvis tilladelserne tillader det. Sidens fulde historik afspejles muligvis ikke fuldt ud her, da gamle revisioner kan blive slettet automatisk, afhængigt af systemkonfigurationen.', 'pages_revisions_named' => 'Siderevisioner for :pageName', 'pages_revision_named' => 'Siderevision for :pageName', 'pages_revision_restored_from' => 'Genoprettet fra #:id; :summary', 'pages_revisions_created_by' => 'Oprettet af', 'pages_revisions_date' => 'Revisionsdato', 'pages_revisions_number' => '#', - 'pages_revisions_sort_number' => 'Revision Number', + 'pages_revisions_sort_number' => 'Revisionsnummer', 'pages_revisions_numbered' => 'Revision #:id', 'pages_revisions_numbered_changes' => 'Revision #:id ændringer', 'pages_revisions_editor' => 'Editor Type', @@ -272,16 +302,16 @@ 'pages_revisions_restore' => 'Gendan', 'pages_revisions_none' => 'Denne side har ingen revisioner', 'pages_copy_link' => 'Kopier link', - 'pages_edit_content_link' => 'Jump to section in editor', - 'pages_pointer_enter_mode' => 'Enter section select mode', - 'pages_pointer_label' => 'Page Section Options', - 'pages_pointer_permalink' => 'Page Section Permalink', - 'pages_pointer_include_tag' => 'Page Section Include Tag', - 'pages_pointer_toggle_link' => 'Permalink mode, Press to show include tag', - 'pages_pointer_toggle_include' => 'Include tag mode, Press to show permalink', + 'pages_edit_content_link' => 'Spring til sektionen i editoren', + 'pages_pointer_enter_mode' => 'Gå ind i sektionsvalgtilstand', + 'pages_pointer_label' => 'Indstillinger for sideafsnit', + 'pages_pointer_permalink' => 'Side Afsnit Permalink', + 'pages_pointer_include_tag' => 'Sideafsnit inkluderer tag', + 'pages_pointer_toggle_link' => 'Permalink-tilstand, tryk for at vise include-tag', + 'pages_pointer_toggle_include' => 'Inkluder tag-tilstand, tryk for at vise permalink', 'pages_permissions_active' => 'Aktive sidetilladelser', 'pages_initial_revision' => 'Første udgivelse', - 'pages_references_update_revision' => 'System auto-update of internal links', + 'pages_references_update_revision' => 'Automatisk systemopdatering af interne links', 'pages_initial_name' => 'Ny side', 'pages_editing_draft_notification' => 'Du redigerer en kladde der sidst var gemt :timeDiff.', 'pages_draft_edited_notification' => 'Siden har været opdateret siden da. Det er anbefalet at du kasserer denne kladde.', @@ -293,26 +323,26 @@ 'time_b' => 'i de sidste :minCount minutter', 'message' => ':start : time. Pas på ikke at overskrive hinandens opdateringer!', ], - 'pages_draft_discarded' => 'Draft discarded! The editor has been updated with the current page content', - 'pages_draft_deleted' => 'Draft deleted! The editor has been updated with the current page content', + 'pages_draft_discarded' => 'Udkast kasseret! Editoren er blevet opdateret med det aktuelle sideindhold', + 'pages_draft_deleted' => 'Udkast slettet! Editoren er blevet opdateret med det aktuelle sideindhold', 'pages_specific' => 'Specifik side', 'pages_is_template' => 'Sideskabelon', // Editor Sidebar - 'toggle_sidebar' => 'Toggle Sidebar', + 'toggle_sidebar' => 'Sidebjælke til/fra', 'page_tags' => 'Sidetags', 'chapter_tags' => 'Kapiteltags', 'book_tags' => 'Bogtags', 'shelf_tags' => 'Reoltags', 'tag' => 'Tag', 'tags' => 'Tags', - 'tags_index_desc' => 'Tags can be applied to content within the system to apply a flexible form of categorization. Tags can have both a key and value, with the value being optional. Once applied, content can then be queried using the tag name and value.', + 'tags_index_desc' => 'Tags kan anvendes på indhold i systemet for at anvende en fleksibel form for kategorisering. Tags kan have både en nøgle og en værdi, hvor værdien er valgfri. Når de er anvendt, kan der søges efter indhold ved hjælp af tagnavn og -værdi.', 'tag_name' => 'Tagnavn', 'tag_value' => 'Tagværdi (valgfri)', 'tags_explain' => "Tilføj nogle tags for bedre at kategorisere dit indhold. \n Du kan tildele en værdi til et tag for mere dybdegående organisering.", 'tags_add' => 'Tilføj endnu et tag', 'tags_remove' => 'Fjern dette tag', - 'tags_usages' => 'Total tag usages', + 'tags_usages' => 'Samlet brug af tags', 'tags_assigned_pages' => 'Tildelt til sider', 'tags_assigned_chapters' => 'Tildelt til Kapitler', 'tags_assigned_books' => 'Tildelt til Bøger', @@ -321,16 +351,16 @@ 'tags_all_values' => 'Alle værdier', 'tags_view_tags' => 'Vis Tags', 'tags_view_existing_tags' => 'Vis eksisterende tags', - 'tags_list_empty_hint' => 'Tags can be assigned via the page editor sidebar or while editing the details of a book, chapter or shelf.', + 'tags_list_empty_hint' => 'Tags kan tildeles via sideeditorens sidepanel eller under redigering af detaljerne i en bog, et kapitel eller en hylde.', 'attachments' => 'Vedhæftninger', 'attachments_explain' => 'Upload nogle filer, eller vedhæft nogle links, der skal vises på siden. Disse er synlige i sidepanelet.', 'attachments_explain_instant_save' => 'Ændringer her gemmes med det samme.', 'attachments_upload' => 'Upload fil', 'attachments_link' => 'Vedhæft link', - 'attachments_upload_drop' => 'Alternatively you can drag and drop a file here to upload it as an attachment.', + 'attachments_upload_drop' => 'Alternativt kan du trække og slippe en fil her for at uploade den som en vedhæftet fil.', 'attachments_set_link' => 'Sæt link', 'attachments_delete' => 'Er du sikker på at du vil slette denne vedhæftning?', - 'attachments_dropzone' => 'Drop files here to upload', + 'attachments_dropzone' => 'Slip filer her for at uploade', 'attachments_no_files' => 'Ingen filer er blevet overført', 'attachments_explain_link' => 'Du kan vedhæfte et link, hvis du foretrækker ikke at uploade en fil. Dette kan være et link til en anden side eller et link til en fil i skyen.', 'attachments_link_name' => 'Linknavn', @@ -367,19 +397,28 @@ 'comment' => 'Kommentar', 'comments' => 'Kommentarer', 'comment_add' => 'Tilføj kommentar', + 'comment_none' => 'No comments to display', 'comment_placeholder' => 'Skriv en kommentar her', - 'comment_count' => '{0} Ingen kommentarer|{1} 1 Kommentar|[2,*] :count kommentarer', + 'comment_thread_count' => ':count Comment Thread|:count Comment Threads', + 'comment_archived_count' => ':count Arkiveret', + 'comment_archived_threads' => 'Archived Threads', 'comment_save' => 'Gem kommentar', 'comment_new' => 'Ny kommentar', 'comment_created' => 'kommenteret :createDiff', 'comment_updated' => 'Opdateret :updateDiff af :username', - 'comment_updated_indicator' => 'Updated', + 'comment_updated_indicator' => 'Opdateret', 'comment_deleted_success' => 'Kommentar slettet', 'comment_created_success' => 'Kommentaren er tilføjet', 'comment_updated_success' => 'Kommentaren er opdateret', + 'comment_archive_success' => 'Comment archived', + 'comment_unarchive_success' => 'Comment un-archived', + 'comment_view' => 'Se kommentar', + 'comment_jump_to_thread' => 'Hop til tråd', 'comment_delete_confirm' => 'Er du sikker på, at du vil slette denne kommentar?', 'comment_in_reply_to' => 'Som svar til :commentId', - 'comment_editor_explain' => 'Here are the comments that have been left on this page. Comments can be added & managed when viewing the saved page.', + 'comment_reference' => 'Reference', + 'comment_reference_outdated' => '(Forældet)', + 'comment_editor_explain' => 'Her er de kommentarer, der er blevet efterladt på denne side. Kommentarer kan tilføjes og administreres, når du ser den gemte side.', // Revision 'revision_delete_confirm' => 'Er du sikker på at du vil slette denne revision?', @@ -387,51 +426,51 @@ 'revision_cannot_delete_latest' => 'Kan ikke slette seneste revision.', // Copy view - 'copy_consider' => 'Please consider the below when copying content.', - 'copy_consider_permissions' => 'Custom permission settings will not be copied.', - 'copy_consider_owner' => 'You will become the owner of all copied content.', - 'copy_consider_images' => 'Page image files will not be duplicated & the original images will retain their relation to the page they were originally uploaded to.', - 'copy_consider_attachments' => 'Page attachments will not be copied.', - 'copy_consider_access' => 'A change of location, owner or permissions may result in this content being accessible to those previously without access.', + 'copy_consider' => 'Vær opmærksom på nedenstående, når du kopierer indhold.', + 'copy_consider_permissions' => 'Brugerdefinerede tilladelsesindstillinger kopieres ikke.', + 'copy_consider_owner' => 'Du bliver ejer af alt kopieret indhold.', + 'copy_consider_images' => 'Sidens billedfiler vil ikke blive duplikeret, og de originale billeder vil bevare deres relation til den side, de oprindeligt blev uploadet til.', + 'copy_consider_attachments' => 'Vedhæftede sider bliver ikke kopieret.', + 'copy_consider_access' => 'En ændring af placering, ejer eller tilladelser kan resultere i, at dette indhold er tilgængeligt for dem, der tidligere ikke havde adgang.', // Conversions - 'convert_to_shelf' => 'Convert to Shelf', - 'convert_to_shelf_contents_desc' => 'You can convert this book to a new shelf with the same contents. Chapters contained within this book will be converted to new books. If this book contains any pages, that are not in a chapter, this book will be renamed and contain such pages, and this book will become part of the new shelf.', - 'convert_to_shelf_permissions_desc' => 'Any permissions set on this book will be copied to the new shelf and to all new child books that don\'t have their own permissions enforced. Note that permissions on shelves do not auto-cascade to content within, as they do for books.', + 'convert_to_shelf' => 'Konverter til hylde', + 'convert_to_shelf_contents_desc' => 'Du kan konvertere denne bog til en ny hylde med samme indhold. Kapitler i denne bog vil blive konverteret til nye bøger. Hvis denne bog indeholder sider, som ikke er i et kapitel, vil bogen blive omdøbt og indeholde sådanne sider, og bogen vil blive en del af den nye hylde.', + 'convert_to_shelf_permissions_desc' => 'Alle tilladelser, der er indstillet på denne bog, vil blive kopieret til den nye hylde og til alle nye underordnede bøger, der ikke har deres egne tilladelser håndhævet. Bemærk, at tilladelser på hylder ikke automatisk overføres til indholdet på hylden, som de gør for bøger.', 'convert_book' => 'Omdan Bog', - 'convert_book_confirm' => 'Are you sure you want to convert this book?', - 'convert_undo_warning' => 'This cannot be as easily undone.', + 'convert_book_confirm' => 'Er du sikker på, at du vil konvertere denne bog?', + 'convert_undo_warning' => 'Det kan ikke gøres om så let.', 'convert_to_book' => 'Omdan til Bog', - 'convert_to_book_desc' => 'You can convert this chapter to a new book with the same contents. Any permissions set on this chapter will be copied to the new book but any inherited permissions, from the parent book, will not be copied which could lead to a change of access control.', + 'convert_to_book_desc' => 'Du kan konvertere dette kapitel til en ny bog med samme indhold. Alle tilladelser, der er indstillet på dette kapitel, vil blive kopieret til den nye bog, men eventuelle nedarvede tilladelser fra den overordnede bog vil ikke blive kopieret, hvilket kan føre til en ændring af adgangskontrollen.', 'convert_chapter' => 'Omdan Kapitel', 'convert_chapter_confirm' => 'Er du sikker på at du vil omdanne dette kapitel?', // References - 'references' => 'References', - 'references_none' => 'There are no tracked references to this item.', - 'references_to_desc' => 'Listed below is all the known content in the system that links to this item.', + 'references' => 'Kilder', + 'references_none' => 'Der er ingen sporede referencer til dette emne.', + 'references_to_desc' => 'Nedenfor vises alt det kendte indhold i systemet, som linker til dette element.', // Watch Options - 'watch' => 'Watch', - 'watch_title_default' => 'Default Preferences', - 'watch_desc_default' => 'Revert watching to just your default notification preferences.', - 'watch_title_ignore' => 'Ignore', - 'watch_desc_ignore' => 'Ignore all notifications, including those from user-level preferences.', - 'watch_title_new' => 'New Pages', - 'watch_desc_new' => 'Notify when any new page is created within this item.', - 'watch_title_updates' => 'All Page Updates', - 'watch_desc_updates' => 'Notify upon all new pages and page changes.', - 'watch_desc_updates_page' => 'Notify upon all page changes.', - 'watch_title_comments' => 'All Page Updates & Comments', - 'watch_desc_comments' => 'Notify upon all new pages, page changes and new comments.', - 'watch_desc_comments_page' => 'Notify upon page changes and new comments.', - 'watch_change_default' => 'Change default notification preferences', - 'watch_detail_ignore' => 'Ignoring notifications', - 'watch_detail_new' => 'Watching for new pages', - 'watch_detail_updates' => 'Watching new pages and updates', - 'watch_detail_comments' => 'Watching new pages, updates & comments', - 'watch_detail_parent_book' => 'Watching via parent book', - 'watch_detail_parent_book_ignore' => 'Ignoring via parent book', - 'watch_detail_parent_chapter' => 'Watching via parent chapter', - 'watch_detail_parent_chapter_ignore' => 'Ignoring via parent chapter', + 'watch' => 'Overvåg', + 'watch_title_default' => 'Standardindstillinger', + 'watch_desc_default' => 'Gå tilbage til kun at se dine standardindstillinger for notifikationer.', + 'watch_title_ignore' => 'Ignorér', + 'watch_desc_ignore' => 'Ignorer alle meddelelser, også dem fra indstillinger på brugerniveau.', + 'watch_title_new' => 'Nye sider', + 'watch_desc_new' => 'Giv besked, når der oprettes en ny side i dette element.', + 'watch_title_updates' => 'Alle sideopdateringer', + 'watch_desc_updates' => 'Giv besked om alle nye sider og sideændringer.', + 'watch_desc_updates_page' => 'Giv besked om alle sideændringer.', + 'watch_title_comments' => 'Alle sideopdateringer og kommentarer', + 'watch_desc_comments' => 'Giv besked om alle nye sider, sideændringer og nye kommentarer.', + 'watch_desc_comments_page' => 'Giv besked ved sideændringer og nye kommentarer.', + 'watch_change_default' => 'Skift indstillinger for standardmeddelelser', + 'watch_detail_ignore' => 'Ignorerer notifikationer', + 'watch_detail_new' => 'Holder øje med nye sider', + 'watch_detail_updates' => 'Holder øje med nye sider og opdateringer', + 'watch_detail_comments' => 'Holder øje med nye sider, opdateringer og kommentarer', + 'watch_detail_parent_book' => 'Se med via forældrebogen', + 'watch_detail_parent_book_ignore' => 'Ignorering via forældrebog', + 'watch_detail_parent_chapter' => 'Se med via forældrekapitel', + 'watch_detail_parent_chapter_ignore' => 'Ignorering via forældrekapitel', ]; diff --git a/lang/da/errors.php b/lang/da/errors.php index 162a7476343..864dec270dc 100644 --- a/lang/da/errors.php +++ b/lang/da/errors.php @@ -10,6 +10,7 @@ // Auth 'error_user_exists_different_creds' => 'En bruger med email :email eksistere allerede, men med andre legitimationsoplysninger.', + 'auth_pre_register_theme_prevention' => 'Brugerkonto kunne ikke registreres for de angivne detaljer', 'email_already_confirmed' => 'Email er allerede bekræftet. Prøv at logge ind.', 'email_confirmation_invalid' => 'Denne bekræftelsestoken er ikke gyldig eller er allerede blevet brugt. Prøv at registrere dig igen.', 'email_confirmation_expired' => 'Bekræftelsestoken er udløbet. En ny bekræftelsesmail er blevet sendt.', @@ -23,7 +24,6 @@ 'saml_invalid_response_id' => 'Anmodningen fra det eksterne godkendelsessystem genkendes ikke af en proces, der er startet af denne applikation. Navigering tilbage efter et login kan forårsage dette problem.', 'saml_fail_authed' => 'Login ved hjælp af :system failed, systemet har ikke givet tilladelse', 'oidc_already_logged_in' => 'Allerede logget ind', - 'oidc_user_not_registered' => 'Brugeren :name er ikke registreret, og automatisk registrering er slået fra', 'oidc_no_email_address' => 'Kunne ikke finde en e-mailadresse for denne bruger i de data, der leveres af det eksterne godkendelsessystem', 'oidc_fail_authed' => 'Login ved hjælp af :system fejlede, systemet har ikke givet tilladelse', 'social_no_action_defined' => 'Ingen handling er defineret', @@ -37,36 +37,37 @@ 'social_driver_not_found' => 'Socialdriver ikke fundet', 'social_driver_not_configured' => 'Dine :socialAccount indstillinger er ikke konfigureret korret.', 'invite_token_expired' => 'Dette invitationslink er udløbet. I stedet kan du prøve at nulstille din kontos kodeord.', + 'login_user_not_found' => 'Der kunne ikke findes en bruger til denne handling.', // System 'path_not_writable' => 'Filsti :filePath kunne ikke uploades til. Sørg for at den kan skrives til af webserveren.', 'cannot_get_image_from_url' => 'Kan ikke finde billede på :url', 'cannot_create_thumbs' => 'Serveren kan ikke oprette miniaturer. Kontroller, at GD PHP-udvidelsen er installeret.', 'server_upload_limit' => 'Serveren tillader ikke uploads af denne størrelse. Prøv en mindre filstørrelse.', - 'server_post_limit' => 'The server cannot receive the provided amount of data. Try again with less data or a smaller file.', + 'server_post_limit' => 'Serveren kan ikke modtage den angivne mængde data. Prøv igen med færre data eller en mindre fil.', 'uploaded' => 'Serveren tillader ikke uploads af denne størrelse. Prøv en mindre filstørrelse.', // Drawing & Images 'image_upload_error' => 'Der opstod en fejl ved upload af billedet', 'image_upload_type_error' => 'Billedtypen, der uploades, er ugyldig', - 'image_upload_replace_type' => 'Image file replacements must be of the same type', - 'image_upload_memory_limit' => 'Failed to handle image upload and/or create thumbnails due to system resource limits.', - 'image_thumbnail_memory_limit' => 'Failed to create image size variations due to system resource limits.', - 'image_gallery_thumbnail_memory_limit' => 'Failed to create gallery thumbnails due to system resource limits.', - 'drawing_data_not_found' => 'Drawing data could not be loaded. The drawing file might no longer exist or you may not have permission to access it.', + 'image_upload_replace_type' => 'Udskiftninger af billedfiler skal være af samme type', + 'image_upload_memory_limit' => 'Kunne ikke håndtere billedupload og/eller oprette thumbnails på grund af begrænsninger i systemets ressourcer.', + 'image_thumbnail_memory_limit' => 'Det lykkedes ikke at skabe variationer i billedstørrelsen på grund af begrænsninger i systemets ressourcer.', + 'image_gallery_thumbnail_memory_limit' => 'Det lykkedes ikke at oprette galleriminiaturer på grund af begrænsede systemressourcer.', + 'drawing_data_not_found' => 'Tegningsdata kunne ikke indlæses. Tegningsfilen findes måske ikke længere, eller du har måske ikke tilladelse til at få adgang til den.', // Attachments 'attachment_not_found' => 'Vedhæftning ikke fundet', - 'attachment_upload_error' => 'An error occurred uploading the attachment file', + 'attachment_upload_error' => 'Der opstod en fejl ved upload af den vedhæftede fil', // Pages 'page_draft_autosave_fail' => 'Kunne ikke gemme kladde. Tjek at du har internetforbindelse før du gemmer siden', - 'page_draft_delete_fail' => 'Failed to delete page draft and fetch current page saved content', + 'page_draft_delete_fail' => 'Kunne ikke slette sideudkast og hente den aktuelle sides gemte indhold', 'page_custom_home_deletion' => 'Kan ikke slette en side der er sat som forside', // Entities 'entity_not_found' => 'Emne ikke fundet', - 'bookshelf_not_found' => 'Shelf not found', + 'bookshelf_not_found' => 'Hylde ikke fundet', 'book_not_found' => 'Bog ikke fundet', 'page_not_found' => 'Side ikke fundet', 'chapter_not_found' => 'Kapitel ikke fundet', @@ -77,6 +78,7 @@ // Users 'users_cannot_delete_only_admin' => 'Du kan ikke slette den eneste admin', 'users_cannot_delete_guest' => 'Du kan ikke slette gæstebrugeren', + 'users_could_not_send_invite' => 'Kunne ikke oprette en bruger, da invitationsmailen ikke blev sendt', // Roles 'role_cannot_be_edited' => 'Denne rolle kan ikke redigeres', @@ -103,6 +105,18 @@ 'app_down' => ':appName er nede lige nu', 'back_soon' => 'Den er oppe igen snart.', + // Import + 'import_zip_cant_read' => 'Kunne ikke læse ZIP-filen.', + 'import_zip_cant_decode_data' => 'Kunne ikke finde og afkode ZIP data.json-indhold.', + 'import_zip_no_data' => 'ZIP-filens data har ikke noget forventet bog-, kapitel- eller sideindhold.', + 'import_validation_failed' => 'Import ZIP kunne ikke valideres med fejl:', + 'import_zip_failed_notification' => 'Kunne ikke importere ZIP-fil.', + 'import_perms_books' => 'Du mangler de nødvendige tilladelser til at oprette bøger.', + 'import_perms_chapters' => 'Du mangler de nødvendige tilladelser til at oprette kapitler.', + 'import_perms_pages' => 'Du mangler de nødvendige tilladelser til at oprette sider.', + 'import_perms_images' => 'Du mangler de nødvendige tilladelser til at oprette billeder.', + 'import_perms_attachments' => 'Du mangler den nødvendige tilladelse til at oprette vedhæftede filer.', + // API errors 'api_no_authorization_found' => 'Der blev ikke fundet nogen autorisationstoken på anmodningen', 'api_bad_authorization_format' => 'En autorisationstoken blev fundet på anmodningen, men formatet var forkert', @@ -115,5 +129,5 @@ 'maintenance_test_email_failure' => 'Følgende fejl opstod under afsendelse af testemail:', // HTTP errors - 'http_ssr_url_no_match' => 'The URL does not match the configured allowed SSR hosts', + 'http_ssr_url_no_match' => 'URL\'en stemmer ikke overens med de konfigurerede tilladte SSR-værter', ]; diff --git a/lang/da/notifications.php b/lang/da/notifications.php index 61a44f1f094..8074c03c4d9 100644 --- a/lang/da/notifications.php +++ b/lang/da/notifications.php @@ -12,16 +12,16 @@ 'updated_page_intro' => 'En side er blevet opdateret i :appName:', 'updated_page_debounce' => 'For at forhindre en masse af notifikationer, i et stykke tid vil du ikke blive sendt notifikationer for yderligere redigeringer til denne side af den samme editor.', - 'detail_page_name' => 'Page Name:', - 'detail_page_path' => 'Page Path:', - 'detail_commenter' => 'Commenter:', - 'detail_comment' => 'Comment:', - 'detail_created_by' => 'Created By:', - 'detail_updated_by' => 'Updated By:', + 'detail_page_name' => 'Sidens navn:', + 'detail_page_path' => 'Sidesti:', + 'detail_commenter' => 'Kommentator:', + 'detail_comment' => 'Bemærkninger:', + 'detail_created_by' => 'Oprettet af:', + 'detail_updated_by' => 'Opdateret af:', - 'action_view_comment' => 'View Comment', - 'action_view_page' => 'View Page', + 'action_view_comment' => 'Se kommentar', + 'action_view_page' => 'Vis Side', - 'footer_reason' => 'This notification was sent to you because :link cover this type of activity for this item.', - 'footer_reason_link' => 'your notification preferences', + 'footer_reason' => 'Denne meddelelse blev sendt til dig, fordi :link dækker denne type aktivitet for denne vare.', + 'footer_reason_link' => 'dine indstillinger for notifikationer', ]; diff --git a/lang/da/passwords.php b/lang/da/passwords.php index 343fa2b8524..b58a4c94b78 100644 --- a/lang/da/passwords.php +++ b/lang/da/passwords.php @@ -7,7 +7,7 @@ return [ 'password' => 'Adgangskoder skal være mindst otte tegn og svare til bekræftelsen.', - 'user' => "Vi kan ikke finde en bruger med den e-mail adresse.", + 'user' => "Vi kan ikke finde en bruger med den e-mailadresse.", 'token' => 'Linket til nulstilling af adgangskode er ugyldigt for denne e-mail-adresse.', 'sent' => 'Vi har sendt dig en e-mail med et link til at nulstille adgangskoden!', 'reset' => 'Dit kodeord er blevet nulstillet!', diff --git a/lang/da/preferences.php b/lang/da/preferences.php index 2b88f9671ff..143405a163c 100644 --- a/lang/da/preferences.php +++ b/lang/da/preferences.php @@ -5,47 +5,47 @@ */ return [ - 'my_account' => 'My Account', + 'my_account' => 'Min konto', - 'shortcuts' => 'Shortcuts', - 'shortcuts_interface' => 'UI Shortcut Preferences', - 'shortcuts_toggle_desc' => 'Here you can enable or disable keyboard system interface shortcuts, used for navigation and actions.', - 'shortcuts_customize_desc' => 'You can customize each of the shortcuts below. Just press your desired key combination after selecting the input for a shortcut.', - 'shortcuts_toggle_label' => 'Keyboard shortcuts enabled', + 'shortcuts' => 'Genveje', + 'shortcuts_interface' => 'Genveje', + 'shortcuts_toggle_desc' => 'Her kan du aktivere eller deaktivere genveje, der bruges til navigation og handlinger.', + 'shortcuts_customize_desc' => 'Du kan tilpasse hver af genvejene nedenfor. Tryk på din ønskede tastekombination efter at have valgt feltet for genvejen.', + 'shortcuts_toggle_label' => 'Tastaturgenveje aktiveret', 'shortcuts_section_navigation' => 'Navigation', - 'shortcuts_section_actions' => 'Common Actions', - 'shortcuts_save' => 'Save Shortcuts', - 'shortcuts_overlay_desc' => 'Note: When shortcuts are enabled a helper overlay is available via pressing "?" which will highlight the available shortcuts for actions currently visible on the screen.', - 'shortcuts_update_success' => 'Shortcut preferences have been updated!', - 'shortcuts_overview_desc' => 'Manage keyboard shortcuts you can use to navigate the system user interface.', + 'shortcuts_section_actions' => 'Almindelige handlinger', + 'shortcuts_save' => 'Gem Genveje', + 'shortcuts_overlay_desc' => 'Bemærk: Når genveje er aktiveret kan du altid se de tilgængelige genveje ved at trykke på "?" på dit tastatur.', + 'shortcuts_update_success' => 'Genvejspræferencer er blevet opdateret!', + 'shortcuts_overview_desc' => 'Håndtér tastaturgenveje, du kan bruge til at navigere i systemets brugergrænseflade.', - 'notifications' => 'Notification Preferences', - 'notifications_desc' => 'Control the email notifications you receive when certain activity is performed within the system.', - 'notifications_opt_own_page_changes' => 'Notify upon changes to pages I own', - 'notifications_opt_own_page_comments' => 'Notify upon comments on pages I own', - 'notifications_opt_comment_replies' => 'Notify upon replies to my comments', - 'notifications_save' => 'Save Preferences', - 'notifications_update_success' => 'Notification preferences have been updated!', - 'notifications_watched' => 'Watched & Ignored Items', - 'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.', + 'notifications' => 'Notifikationer', + 'notifications_desc' => 'Administrer de e-mail-notifikationer, du modtager, når visse aktiviteter udføres i systemet.', + 'notifications_opt_own_page_changes' => 'Adviser ved ændringer af sider, jeg ejer', + 'notifications_opt_own_page_comments' => 'Adviser ved kommentarer på sider, jeg ejer', + 'notifications_opt_comment_replies' => 'Adviser ved svar på mine kommentarer', + 'notifications_save' => 'Gem indstillinger', + 'notifications_update_success' => 'Indstillinger for notifikationer er blevet opdateret!', + 'notifications_watched' => 'Overvågede og ignorerede', + 'notifications_watched_desc' => 'Nedenfor er de elementer, der har brugerdefinerede overvågning aktivt. For at opdatere dine præferencer for disse, gå til elementet og find derefter overvågning i sidepanelet.', - 'auth' => 'Access & Security', - 'auth_change_password' => 'Change Password', - 'auth_change_password_desc' => 'Change the password you use to log-in to the application. This must be at least 8 characters long.', - 'auth_change_password_success' => 'Password has been updated!', + 'auth' => 'Adgang og sikkerhed', + 'auth_change_password' => 'Skift adgangskode', + 'auth_change_password_desc' => 'Skift den adgangskode, du bruger til at logge ind med. Den skal være mindst 8 tegn lang.', + 'auth_change_password_success' => 'Adgangskoden er blevet opdateret!', - 'profile' => 'Profile Details', - 'profile_desc' => 'Manage the details of your account which represents you to other users, in addition to details that are used for communication and system personalisation.', - 'profile_view_public' => 'View Public Profile', - 'profile_name_desc' => 'Configure your display name which will be visible to other users in the system through the activity you perform, and content you own.', - 'profile_email_desc' => 'This email will be used for notifications and, depending on active system authentication, system access.', - 'profile_email_no_permission' => 'Unfortunately you don\'t have permission to change your email address. If you want to change this, you\'d need to ask an administrator to change this for you.', - 'profile_avatar_desc' => 'Select an image which will be used to represent yourself to others in the system. Ideally this image should be square and about 256px in width and height.', - 'profile_admin_options' => 'Administrator Options', - 'profile_admin_options_desc' => 'Additional administrator-level options, like those to manage role assignments, can be found for your user account in the "Settings > Users" area of the application.', + 'profile' => 'Profil', + 'profile_desc' => 'Administrer detaljerne på din konto, som repræsenterer dig over for andre brugere.', + 'profile_view_public' => 'Vis offentlig profil', + 'profile_name_desc' => 'Konfigurer dit visningsnavn, som vil være synligt for andre brugere i systemet gennem den aktivitet, du udfører, og indhold du ejer.', + 'profile_email_desc' => 'Denne e-mail vil blive brugt til notifikationer og, afhængigt af aktiv systemgodkendelse, systemadgang.', + 'profile_email_no_permission' => 'Desværre har du ikke tilladelse til at ændre din e-mailadresse. Hvis du ønsker at ændre dette, skal du bede en administrator om at ændre dette for dig.', + 'profile_avatar_desc' => 'Vælg et billede som vil blive brugt til at repræsentere dig selv over for andre i systemet. Ideelt set bør dette billede være kvadrat og omkring 256px i bredde og højde.', + 'profile_admin_options' => 'Administratorindstillinger', + 'profile_admin_options_desc' => 'Yderligere indstillinger på administratorniveau, såsom dem der håndterer rolleopgaver, kan findes for din brugerkonto i området "Indstillinger > Brugere".', - 'delete_account' => 'Delete Account', - 'delete_my_account' => 'Delete My Account', - 'delete_my_account_desc' => 'This will fully delete your user account from the system. You will not be able to recover this account or revert this action. Content you\'ve created, such as created pages and uploaded images, will remain.', - 'delete_my_account_warning' => 'Are you sure you want to delete your account?', + 'delete_account' => 'Slet konto', + 'delete_my_account' => 'Slet min konto', + 'delete_my_account_desc' => 'Dette vil fuldt ud slette din brugerkonto fra systemet. Du vil ikke være i stand til at gendanne denne konto eller fortryde denne handling. Indhold, du har oprettet, såsom oprettede sider og uploadede billeder, vil ikke blive slettet.', + 'delete_my_account_warning' => 'Er du sikker at du vil slette din konto?', ]; diff --git a/lang/da/settings.php b/lang/da/settings.php index 13ef813c6e4..34d3975bec0 100644 --- a/lang/da/settings.php +++ b/lang/da/settings.php @@ -26,15 +26,15 @@ 'app_secure_images' => 'Højere sikkerhed for billeduploads', 'app_secure_images_toggle' => 'Aktiver højere sikkerhed for billeduploads', 'app_secure_images_desc' => 'Af performanceårsager er alle billeder offentlige. Denne funktion tilføjer en tilfældig, vanskelig at gætte streng foran billed-url\'er. Sørg for, at mappeindeksering ikke er aktiveret for at forhindre nem adgang.', - 'app_default_editor' => 'Standard Side Editor', + 'app_default_editor' => 'Standard sideeditor', 'app_default_editor_desc' => 'Vælg hvilken editor der som standard skal bruges ved redigering af nye sider. Dette kan tilsidesættes på side niveau, hvor tilladelser tillader det.', 'app_custom_html' => 'Tilpasset HTML head indhold', 'app_custom_html_desc' => 'Alt indhold tilføjet her, vil blive indsat i bunden af sektionen på alle sider. Dette er brugbart til overskrivning af styles og tilføjelse af analytics kode.', 'app_custom_html_disabled_notice' => 'Brugerdefineret HTML head indhold er deaktiveret på denne indstillingsside for at, at ændringer kan rulles tilbage.', 'app_logo' => 'Applikationslogo', - 'app_logo_desc' => 'This is used in the application header bar, among other areas. This image should be 86px in height. Large images will be scaled down.', - 'app_icon' => 'Application Icon', - 'app_icon_desc' => 'This icon is used for browser tabs and shortcut icons. This should be a 256px square PNG image.', + 'app_logo_desc' => 'Det bruges blandt andet i applikationens headerbar. Dette billede skal være 86px i højden. Store billeder vil blive skaleret ned.', + 'app_icon' => 'Programikon', + 'app_icon_desc' => 'Dette ikon bruges til browserfaner og genvejsikoner. Det skal være et 256px kvadratisk PNG-billede.', 'app_homepage' => 'Applikationsforside', 'app_homepage_desc' => 'Vælg en visning, der skal vises på forsiden i stedet for standardvisningen. Sidetilladelser ignoreres for de valgte sider.', 'app_homepage_select' => 'Vælg en side', @@ -48,12 +48,12 @@ 'app_disable_comments_desc' => 'Deaktiverer kommentarer på tværs af alle sider i applikationen.
Eksisterende kommentarer vises ikke.', // Color settings - 'color_scheme' => 'Application Color Scheme', - 'color_scheme_desc' => 'Set the colors to use in the application user interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.', - 'ui_colors_desc' => 'Set the application primary color and default link color. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the application interface.', - 'app_color' => 'Primary Color', - 'link_color' => 'Default Link Color', - 'content_colors_desc' => 'Set colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.', + 'color_scheme' => 'Applikationens farveskema', + 'color_scheme_desc' => 'Indstil de farver, der skal bruges i applikationens brugergrænseflade. Farver kan konfigureres separat for mørke og lyse tilstande for at passe bedst til temaet og sikre læsbarhed.', + 'ui_colors_desc' => 'Indstil applikationens primære farve og standardlinkfarve. Den primære farve bruges hovedsageligt til headerbanneret, knapper og interfacedekorationer. Standardlinkfarven bruges til tekstbaserede links og handlinger, både i det skrevne indhold og i programmets brugerflade.', + 'app_color' => 'Primær farve', + 'link_color' => 'Standard Link Farve', + 'content_colors_desc' => 'Indstil farver for alle elementer i sideorganisationshierarkiet. Det anbefales at vælge farver med samme lysstyrke som standardfarverne af hensyn til læsbarheden.', 'bookshelf_color' => 'Bogreolfarve', 'book_color' => 'Bogfarve', 'chapter_color' => 'Kapitelfarve', @@ -74,6 +74,36 @@ 'reg_confirm_restrict_domain_desc' => 'Indtast en kommasepareret liste over e-mail-domæner, som du vil begrænse registreringen til. Brugere får en E-Mail for at bekræfte deres adresse, før de får tilladelse til at interagere med applikationen.
Bemærk, at brugere vil kunne ændre deres e-mail-adresser efter vellykket registrering.', 'reg_confirm_restrict_domain_placeholder' => 'Ingen restriktion opsat', + // Sorting Settings + 'sorting' => 'Sortering', + 'sorting_book_default' => 'Standard bog-sortering', + 'sorting_book_default_desc' => 'Vælg den standardsorteringsregel, der skal gælde for nye bøger. Dette påvirker ikke eksisterende bøger og kan tilsidesættes for hver enkelt bog.', + 'sorting_rules' => 'Regler for sortering', + 'sorting_rules_desc' => 'Det er foruddefinerede sorteringsoperationer, som kan anvendes på indhold i systemet.', + 'sort_rule_assigned_to_x_books' => 'Tildelt til :count Book|Tildelt til :count Books', + 'sort_rule_create' => 'Opret sorteringsregel', + 'sort_rule_edit' => 'Rediger sorteringsregel', + 'sort_rule_delete' => 'Slet sorteringsregel', + 'sort_rule_delete_desc' => 'Fjern denne sorteringsregel fra systemet. Bøger, der bruger denne sortering, vil vende tilbage til manuel sortering.', + 'sort_rule_delete_warn_books' => 'Denne sorteringsregel bruges i øjeblikket på :count book(s). Er du sikker på, at du vil slette den?', + 'sort_rule_delete_warn_default' => 'Denne sorteringsregel bruges i øjeblikket som standard for bøger. Er du sikker på, at du vil slette den?', + 'sort_rule_details' => 'Detaljer om sorteringsregler', + 'sort_rule_details_desc' => 'Angiv et navn for denne sorteringsregel, som vises i lister, når brugerne vælger en sortering.', + 'sort_rule_operations' => 'Sorteringsoperationer', + 'sort_rule_operations_desc' => 'Konfigurer de sorteringshandlinger, der skal udføres, ved at flytte dem fra listen over tilgængelige handlinger. Ved brug vil handlingerne blive anvendt i rækkefølge, fra top til bund. Alle ændringer, der foretages her, vil blive anvendt på alle tildelte bøger, når de gemmes.', + 'sort_rule_available_operations' => 'Tilgængelige operationer', + 'sort_rule_available_operations_empty' => 'Ingen operationer tilbage', + 'sort_rule_configured_operations' => 'Konfigurerede operationer', + 'sort_rule_configured_operations_empty' => 'Træk/tilføj operationer fra listen "Tilgængelige operationer"', + 'sort_rule_op_asc' => '(Asc)', + 'sort_rule_op_desc' => '(Desc)', + 'sort_rule_op_name' => 'Navn - alfabetisk', + 'sort_rule_op_name_numeric' => 'Navn - numerisk', + 'sort_rule_op_created_date' => 'Oprettet den', + 'sort_rule_op_updated_date' => 'Opdateret dato', + 'sort_rule_op_chapters_first' => 'Kapitler først', + 'sort_rule_op_chapters_last' => 'De sidste kapitler', + // Maintenance settings 'maint' => 'Vedligeholdelse', 'maint_image_cleanup' => 'Ryd op i billeder', @@ -92,10 +122,10 @@ 'maint_send_test_email_mail_text' => 'Tillykke! Da du har modtaget denne mailnotifikation, ser det ud som om, at dine mailindstillinger er opsat korrekt.', 'maint_recycle_bin_desc' => 'Slettede hylder, bøger, kapitler og sider overføres til papirkurven, så de kan gendannes eller slettes permanent. Ældre elementer i papirkurven fjernes automatisk efter et stykke tid afhængigt af systemets konfiguration.', 'maint_recycle_bin_open' => 'Åbn papirkurven', - 'maint_regen_references' => 'Regenerate References', - 'maint_regen_references_desc' => 'This action will rebuild the cross-item reference index within the database. This is usually handled automatically but this action can be useful to index old content or content added via unofficial methods.', - 'maint_regen_references_success' => 'Reference index has been regenerated!', - 'maint_timeout_command_note' => 'Note: This action can take time to run, which can lead to timeout issues in some web environments. As an alternative, this action be performed using a terminal command.', + 'maint_regen_references' => 'Regenerer Referencer', + 'maint_regen_references_desc' => 'Denne handling vil genopbygge referenceindekset på tværs af elementer i databasen. Dette håndteres normalt automatisk, men denne handling kan være nyttig til at indeksere gammelt indhold eller indhold, der er tilføjet via uofficielle metoder.', + 'maint_regen_references_success' => 'Referenceindekset er blevet genskabt!', + 'maint_timeout_command_note' => 'Bemærk: Denne handling kan tage tid at udføre, hvilket kan føre til timeout-problemer i nogle webmiljøer. Som et alternativ kan denne handling udføres med en terminalkommando.', // Recycle Bin 'recycle_bin' => 'Papirkurv', @@ -109,7 +139,7 @@ 'recycle_bin_contents_empty' => 'Papirkurven er tom', 'recycle_bin_empty' => 'Tøm papirkurv', 'recycle_bin_empty_confirm' => 'Dette vil permanent slette alle elementer i papirkurven, inkluderet hvert elements indhold. Er du sikker på, at du vil tømme papirkurven?', - 'recycle_bin_destroy_confirm' => 'Denne handling sletter dette element permanent, sammen med elementerne anført nedenfor, fra systemet. Du vil ikke være i stand til at gendanne dette indhold. Er du sikker på, at du vil slette dette element permanent?', + 'recycle_bin_destroy_confirm' => 'Denne handling vil permanent slette dette element fra systemet sammen med alle underordnede elementer, der er anført nedenfor, og du vil ikke kunne gendanne dette indhold. Er du sikker på, at du vil slette dette element permanent?', 'recycle_bin_destroy_list' => 'Elementer der skal slettes', 'recycle_bin_restore_list' => 'Elementer der skal gendannes', 'recycle_bin_restore_confirm' => 'Denne handling vil gendanne det slettede element, herunder alle underelementer, til deres oprindelige placering. Hvis den oprindelige placering siden er blevet slettet, og nu er i papirkurven, vil det overordnede element også skulle gendannes.', @@ -136,11 +166,11 @@ // Role Settings 'roles' => 'Roller', 'role_user_roles' => 'Brugerroller', - 'roles_index_desc' => 'Roles are used to group users & provide system permission to their members. When a user is a member of multiple roles the privileges granted will stack and the user will inherit all abilities.', - 'roles_x_users_assigned' => ':count user assigned|:count users assigned', - 'roles_x_permissions_provided' => ':count permission|:count permissions', - 'roles_assigned_users' => 'Assigned Users', - 'roles_permissions_provided' => 'Provided Permissions', + 'roles_index_desc' => 'Roller bruges til at gruppere brugere og give systemtilladelser til deres medlemmer. Når en bruger er medlem af flere roller, stables de tildelte rettigheder, og brugeren arver alle evner.', + 'roles_x_users_assigned' => ':count bruger tildelt:count brugere tildelt', + 'roles_x_permissions_provided' => ':count tilladelser: count tilladelser', + 'roles_assigned_users' => 'Tildelte brugere', + 'roles_permissions_provided' => 'Givne tilladelser', 'role_create' => 'Opret en ny rolle', 'role_delete' => 'Slet rolle', 'role_delete_confirm' => 'Dette vil slette rollen med navnet \':roleName\'.', @@ -162,13 +192,14 @@ 'role_access_api' => 'Tilgå system-API', 'role_manage_settings' => 'Administrer app-indstillinger', 'role_export_content' => 'Eksporter indhold', + 'role_import_content' => 'Importer indhold', 'role_editor_change' => 'Skift side editor', - 'role_notifications' => 'Receive & manage notifications', + 'role_notifications' => 'Modtag og administrer notifikationer', 'role_asset' => 'Tilladelser for medier og "assets"', 'roles_system_warning' => 'Vær opmærksom på, at adgang til alle af de ovennævnte tre tilladelser, kan give en bruger mulighed for at ændre deres egne brugerrettigheder eller brugerrettigheder for andre i systemet. Tildel kun roller med disse tilladelser til betroede brugere.', 'role_asset_desc' => 'Disse tilladelser kontrollerer standardadgang til medier og "assets" i systemet. Tilladelser til bøger, kapitler og sider tilsidesætter disse tilladelser.', 'role_asset_admins' => 'Administratorer får automatisk adgang til alt indhold, men disse indstillinger kan vise eller skjule UI-indstillinger.', - 'role_asset_image_view_note' => 'This relates to visibility within the image manager. Actual access of uploaded image files will be dependant upon system image storage option.', + 'role_asset_image_view_note' => 'Dette vedrører synlighed i billedhåndteringen. Den faktiske adgang til uploadede billedfiler vil afhænge af systemets billedlagringsindstilling.', 'role_all' => 'Alle', 'role_own' => 'Eget', 'role_controlled_by_asset' => 'Styres af det medie/"asset", de uploades til', @@ -178,7 +209,7 @@ // Users 'users' => 'Brugere', - 'users_index_desc' => 'Create & manage individual user accounts within the system. User accounts are used for login and attribution of content & activity. Access permissions are primarily role-based but user content ownership, among other factors, may also affect permissions & access.', + 'users_index_desc' => 'Opret og administrer individuelle brugerkonti i systemet. Brugerkonti bruges til login og tilskrivning af indhold og aktivitet. Adgangstilladelser er primært rollebaserede, men ejerskab af brugerindhold, blandt andre faktorer, kan også påvirke tilladelser og adgang.', 'user_profile' => 'Brugerprofil', 'users_add_new' => 'Tilføj ny bruger', 'users_search' => 'Søg efter brugere', @@ -193,8 +224,8 @@ 'users_send_invite_text' => 'Du kan vælge at sende denne bruger en invitation på E-Mail, som giver dem mulighed for at indstille deres egen adgangskode, ellers kan du indstille deres adgangskode selv.', 'users_send_invite_option' => 'Send bruger en invitationsmail', 'users_external_auth_id' => 'Ekstern godkendelses ID', - 'users_external_auth_id_desc' => 'When an external authentication system is in use (such as SAML2, OIDC or LDAP) this is the ID which links this BookStack user to the authentication system account. You can ignore this field if using the default email-based authentication.', - 'users_password_warning' => 'Only fill the below if you would like to change the password for this user.', + 'users_external_auth_id_desc' => 'Når et eksternt godkendelsessystem er i brug (f.eks. SAML2, OIDC eller LDAP), er dette det ID, som forbinder denne BookStack-bruger med godkendelsessystemets konto. Du kan ignorere dette felt, hvis du bruger den e-mailbaserede standardgodkendelse.', + 'users_password_warning' => 'Udfyld kun nedenstående, hvis du ønsker at ændre adgangskoden for denne bruger.', 'users_system_public' => 'Denne bruger repræsenterer alle gæstebrugere, der besøger din instans. Den kan ikke bruges til at logge på, men tildeles automatisk.', 'users_delete' => 'Slet bruger', 'users_delete_named' => 'Slet bruger :userName', @@ -210,16 +241,16 @@ 'users_preferred_language' => 'Foretrukket sprog', 'users_preferred_language_desc' => 'Denne indstilling ændrer det sprog, der bruges til applikationens brugergrænseflade. Dette påvirker ikke noget brugeroprettet indhold.', 'users_social_accounts' => 'Sociale konti', - 'users_social_accounts_desc' => 'View the status of the connected social accounts for this user. Social accounts can be used in addition to the primary authentication system for system access.', + 'users_social_accounts_desc' => 'Se status for de tilsluttede sociale konti for denne bruger. Sociale konti kan bruges som supplement til det primære godkendelsessystem til systemadgang.', 'users_social_accounts_info' => 'Her kan du forbinde dine andre konti for hurtigere og lettere login. Afbrydelse af en konto her tilbagekalder ikke tidligere autoriseret adgang. Tilbagekald adgang fra dine profilindstillinger på den tilsluttede sociale konto.', 'users_social_connect' => 'Forbind konto', 'users_social_disconnect' => 'Frakobl konto', - 'users_social_status_connected' => 'Connected', - 'users_social_status_disconnected' => 'Disconnected', + 'users_social_status_connected' => 'Tilsuttet', + 'users_social_status_disconnected' => 'Afbrudt', 'users_social_connected' => ':socialAccount kontoen blev knyttet til din profil.', 'users_social_disconnected' => ':socialAccount kontoen blev afbrudt fra din profil.', 'users_api_tokens' => 'API Tokens', - 'users_api_tokens_desc' => 'Create and manage the access tokens used to authenticate with the BookStack REST API. Permissions for the API are managed via the user that the token belongs to.', + 'users_api_tokens_desc' => 'Opret og administrer de adgangstokens, der bruges til at godkende med BookStack REST API. Tilladelser til API\'en administreres via den bruger, som tokenet tilhører.', 'users_api_tokens_none' => 'Ingen API tokens er blevet oprettet for denne bruger', 'users_api_tokens_create' => 'Opret Token', 'users_api_tokens_expires' => 'Udløber', @@ -249,8 +280,8 @@ // Webhooks 'webhooks' => 'Webhooks', - 'webhooks_index_desc' => 'Webhooks are a way to send data to external URLs when certain actions and events occur within the system which allows event-based integration with external platforms such as messaging or notification systems.', - 'webhooks_x_trigger_events' => ':count trigger event|:count trigger events', + 'webhooks_index_desc' => 'Webhooks er en måde at sende data til eksterne URL\'er på, når bestemte handlinger og hændelser sker i systemet, hvilket giver mulighed for hændelsesbaseret integration med eksterne platforme som f.eks. besked- eller notifikationssystemer.', + 'webhooks_x_trigger_events' => ':count trigger begivenhed:count trigger events', 'webhooks_create' => 'Opret ny Webhook', 'webhooks_none_created' => 'Ingen webhooks er blevet oprettet endnu.', 'webhooks_edit' => 'Rediger Webhook', @@ -276,6 +307,14 @@ 'webhooks_last_errored' => 'Sidst Fejlet:', 'webhooks_last_error_message' => 'Sidste fejlmeddelelse:', + // Licensing + 'licenses' => 'Licenser', + 'licenses_desc' => 'Denne side indeholder licensoplysninger for BookStack ud over de projekter og biblioteker, der bruges i BookStack. Mange af de nævnte projekter må kun bruges i udviklingssammenhæng.', + 'licenses_bookstack' => 'BookStack-licens', + 'licenses_php' => 'Licenser til PHP-biblioteker', + 'licenses_js' => 'Licenser til JavaScript-biblioteker', + 'licenses_other' => 'Andre licenser', + 'license_details' => 'Licensoplysninger', //! If editing translations files directly please ignore this in all //! languages apart from en. Content will be auto-copied from en. @@ -287,6 +326,7 @@ 'bs' => 'Bosanski', 'ca' => 'Catalansk', 'cs' => 'Česky', + 'cy' => 'Cymraeg', 'da' => 'Dansk', 'de' => 'Deutsch (Sie)', 'de_informal' => 'Deutsch (Du)', @@ -308,6 +348,7 @@ 'lt' => 'Lietuvių Kalba', 'lv' => 'Latviešu Valoda', 'nb' => 'Norsk (Bokmål)', + 'ne' => 'नेपाली', 'nn' => 'Nynorsk', 'nl' => 'Nederlands', 'pl' => 'Polski', diff --git a/lang/da/validation.php b/lang/da/validation.php index 022839dc693..3caf1417a45 100644 --- a/lang/da/validation.php +++ b/lang/da/validation.php @@ -105,6 +105,11 @@ 'url' => ':attribute-formatet er ugyldigt.', 'uploaded' => 'Filen kunne ikke oploades. Serveren accepterer muligvis ikke filer af denne størrelse.', + 'zip_file' => 'Attributten skal henvise til en fil i ZIP.', + 'zip_file_mime' => 'Attributten skal henvise til en fil af typen: validTypes, fundet:foundType.', + 'zip_model_expected' => 'Data objekt forventet men ":type" fundet.', + 'zip_unique' => 'Attributten skal være unik for objekttypen i ZIP.', + // Custom validation lines 'custom' => [ 'password-confirm' => [ diff --git a/lang/de/activities.php b/lang/de/activities.php index b2fb996f114..025cbaeffab 100644 --- a/lang/de/activities.php +++ b/lang/de/activities.php @@ -10,11 +10,11 @@ 'page_create_notification' => 'Seite erfolgreich erstellt', 'page_update' => 'aktualisierte Seite', 'page_update_notification' => 'Seite erfolgreich aktualisiert', - 'page_delete' => 'hat die Seite gelöscht', + 'page_delete' => 'löschte Seite', 'page_delete_notification' => 'Seite erfolgreich gelöscht', - 'page_restore' => 'hat die Seite wiederhergestellt', + 'page_restore' => 'wiederherstellte Seite', 'page_restore_notification' => 'Seite erfolgreich wiederhergestellt', - 'page_move' => 'Seite verschoben', + 'page_move' => 'verschob Seite', 'page_move_notification' => 'Seite erfolgreich verschoben', // Chapters @@ -37,13 +37,13 @@ 'book_delete' => 'löschte Buch', 'book_delete_notification' => 'Buch erfolgreich gelöscht', 'book_sort' => 'sortierte Buch', - 'book_sort_notification' => 'Das Buch wurde erfolgreich umsortiert', + 'book_sort_notification' => 'Buch wurde erfolgreich umsortiert', // Bookshelves 'bookshelf_create' => 'erstellte Regal', 'bookshelf_create_notification' => 'Regal erfolgreich erstellt', 'bookshelf_create_from_book' => 'konvertierte Buch zu Regal', - 'bookshelf_create_from_book_notification' => 'Buch erfolgreich in ein Regal konvertiert', + 'bookshelf_create_from_book_notification' => 'Buch erfolgreich in Regal konvertiert', 'bookshelf_update' => 'aktualisierte Regal', 'bookshelf_update_notification' => 'Regal erfolgreich aktualisiert', 'bookshelf_delete' => 'löschte Regal', @@ -62,28 +62,36 @@ 'watch_update_level_notification' => 'Beobachtungseinstellungen erfolgreich aktualisiert', // Auth - 'auth_login' => 'hat sich eingeloggt', - 'auth_register' => 'hat sich als neuer Benutzer registriert', - 'auth_password_reset_request' => 'hat eine Rücksetzung des Benutzerpassworts beantragt', - 'auth_password_reset_update' => 'hat Benutzerpasswort zurückgesetzt', - 'mfa_setup_method' => 'hat MFA-Methode konfiguriert', + 'auth_login' => 'loggte sich ein', + 'auth_register' => 'registrierte sich als neuer User', + 'auth_password_reset_request' => 'forderte Rücksetzung des Benutzerpassworts an', + 'auth_password_reset_update' => 'setzte Benutzerpasswort zurück', + 'mfa_setup_method' => 'konfigurierte MFA-Methode', 'mfa_setup_method_notification' => 'Multi-Faktor-Methode erfolgreich konfiguriert', - 'mfa_remove_method' => 'hat MFA-Methode entfernt', + 'mfa_remove_method' => 'entfernte MFA-Methode', 'mfa_remove_method_notification' => 'Multi-Faktor-Methode erfolgreich entfernt', // Settings - 'settings_update' => 'hat Einstellungen aktualisiert', + 'settings_update' => 'aktualisierte Einstellungen', 'settings_update_notification' => 'Einstellungen erfolgreich aktualisiert', - 'maintenance_action_run' => 'hat Wartungsarbeiten ausgeführt', + 'maintenance_action_run' => 'führte Wartungsaktion aus', // Webhooks 'webhook_create' => 'erstellte Webhook', - 'webhook_create_notification' => 'Webhook wurde erfolgreich eingerichtet', + 'webhook_create_notification' => 'Webhook erfolgreich erstellt', 'webhook_update' => 'aktualisierte Webhook', - 'webhook_update_notification' => 'Webhook wurde erfolgreich aktualisiert', + 'webhook_update_notification' => 'Webhook erfolgreich aktualisiert', 'webhook_delete' => 'löschte Webhook', 'webhook_delete_notification' => 'Webhook erfolgreich gelöscht', + // Imports + 'import_create' => 'Import erstellt', + 'import_create_notification' => 'Import erfolgreich hochgeladen', + 'import_run' => 'Import aktualisiert', + 'import_run_notification' => 'Inhalt erfolgreich importiert', + 'import_delete' => 'Import gelöscht', + 'import_delete_notification' => 'Import erfolgreich gelöscht', + // Users 'user_create' => 'hat Benutzer erzeugt:', 'user_create_notification' => 'Benutzer erfolgreich erstellt', @@ -93,11 +101,11 @@ 'user_delete_notification' => 'Benutzer erfolgreich entfernt', // API Tokens - 'api_token_create' => 'hat API-Token erzeugt:', + 'api_token_create' => 'API-Token erstellt', 'api_token_create_notification' => 'API-Token erfolgreich erstellt', - 'api_token_update' => 'hat API-Token aktualisiert:', + 'api_token_update' => 'API-Token aktualisiert', 'api_token_update_notification' => 'API-Token erfolgreich aktualisiert', - 'api_token_delete' => 'hat API-Token gelöscht:', + 'api_token_delete' => 'API-Token gelöscht', 'api_token_delete_notification' => 'API-Token erfolgreich gelöscht', // Roles @@ -119,6 +127,14 @@ 'comment_update' => 'Kommentar aktualisiert', 'comment_delete' => 'Kommentar gelöscht', + // Sort Rules + 'sort_rule_create' => 'hat eine Sortierregel erstellt', + 'sort_rule_create_notification' => 'Sortierregel erfolgreich angelegt', + 'sort_rule_update' => 'hat eine Sortierregel aktualisiert', + 'sort_rule_update_notification' => 'Sortierregel erfolgreich aktualisiert', + 'sort_rule_delete' => 'hat eine Sortierregel gelöscht', + 'sort_rule_delete_notification' => 'Sortierregel erfolgreich gelöscht', + // Other 'permissions_update' => 'hat die Berechtigungen aktualisiert', ]; diff --git a/lang/de/auth.php b/lang/de/auth.php index 505ae0dd5d9..92789d18216 100644 --- a/lang/de/auth.php +++ b/lang/de/auth.php @@ -40,7 +40,7 @@ // Login auto-initiation 'auto_init_starting' => 'Anmeldeversuche', - 'auto_init_starting_desc' => 'Wir verbinden und mit Ihrem Authentifizierungssystem um den Anmeldeprozess zu starten. Sollte es nach 5 Sekunden nicht weitergehen, klicken Sie bitte auf den unten stehenden Link.', + 'auto_init_starting_desc' => 'Wir verbinden uns mit Ihrem Authentifizierungssystem, um den Anmeldeprozess zu starten. Sollte es nach 5 Sekunden nicht weitergehen, klicken Sie bitte auf den unten stehenden Link.', 'auto_init_start_link' => 'Mit Authentifizierung fortfahren', // Password Reset @@ -91,7 +91,7 @@ 'mfa_option_totp_title' => 'Handy-App', 'mfa_option_totp_desc' => 'Um Mehrfach-Faktor-Authentifizierung nutzen zu können, benötigen Sie eine Handy-Anwendung, die TOTP unterstützt, wie Google Authenticator, Authy oder Microsoft Authenticator.', 'mfa_option_backup_codes_title' => 'Backup-Codes', - 'mfa_option_backup_codes_desc' => 'Speichern Sie sicher eine Reihe von einmaligen Backup-Codes, die Sie eingeben können, um Ihre Identität zu überprüfen.', + 'mfa_option_backup_codes_desc' => 'Erzeugt eine Reihe von einmalig nutzbaren Backup-Codes, welche Sie bei der Anmeldung eingeben, um Ihre Identität zu bestätigen. Achten Sie darauf diese an einem sicheren Ort aufzubewahren.', 'mfa_gen_confirm_and_enable' => 'Bestätigen und aktivieren', 'mfa_gen_backup_codes_title' => 'Backup-Codes einrichten', 'mfa_gen_backup_codes_desc' => 'Speichern Sie die folgende Liste von Codes an einem sicheren Ort. Wenn Sie auf das System zugreifen, können Sie einen der Codes als zweiten Authentifizierungsmechanismus verwenden.', diff --git a/lang/de/common.php b/lang/de/common.php index 5934e1e7194..4f2ae54d272 100644 --- a/lang/de/common.php +++ b/lang/de/common.php @@ -20,7 +20,7 @@ 'description' => 'Beschreibung', 'role' => 'Rolle', 'cover_image' => 'Titelbild', - 'cover_image_description' => 'Das Bild sollte eine Auflösung von ca. 440x250px haben.', + 'cover_image_description' => 'Dieses Bild sollte ungefähr 440x250px groß sein, obwohl es flexibel skaliert und beschnitten wird, um in verschiedenen Szenarien in die Benutzeroberfläche zu passen, sodass die tatsächlichen Abmessungen für die Anzeige abweichen können.', // Actions 'actions' => 'Aktionen', @@ -30,6 +30,8 @@ 'create' => 'Erstellen', 'update' => 'Aktualisieren', 'edit' => 'Bearbeiten', + 'archive' => 'Archivieren', + 'unarchive' => 'Nicht mehr archivieren', 'sort' => 'Sortieren', 'move' => 'Verschieben', 'copy' => 'Kopieren', @@ -107,4 +109,7 @@ // Not directly used but available for convenience to users. 'privacy_policy' => 'Datenschutzbestimmungen', 'terms_of_service' => 'Allgemeine Geschäftsbedingungen', + + // OpenSearch + 'opensearch_description' => 'Search :appName', ]; diff --git a/lang/de/editor.php b/lang/de/editor.php index 230dc29f6df..1be31551987 100644 --- a/lang/de/editor.php +++ b/lang/de/editor.php @@ -13,6 +13,7 @@ 'cancel' => 'Abbrechen', 'save' => 'Speichern', 'close' => 'Schließen', + 'apply' => 'Übernehmen', 'undo' => 'Rückgängig', 'redo' => 'Wiederholen', 'left' => 'Links', @@ -47,6 +48,7 @@ 'superscript' => 'Hochgestellt', 'subscript' => 'Tiefgestellt', 'text_color' => 'Schriftfarbe', + 'highlight_color' => 'Markierungsfarbe', 'custom_color' => 'Benutzerdefinierte Farbe', 'remove_color' => 'Farbe entfernen', 'background_color' => 'Hintergrundfarbe', @@ -81,6 +83,9 @@ 'table_properties' => 'Tabelleneigenschaften', 'table_properties_title' => 'Tabelleneigenschaften', 'delete_table' => 'Tabelle löschen', + 'table_clear_formatting' => 'Tabellenformatierung löschen', + 'resize_to_contents' => 'Größe an Inhalt anpassen', + 'row_header' => 'Zeilenkopf', 'insert_row_before' => 'Zeile davor einfügen', 'insert_row_after' => 'Zeile danach einfügen', 'delete_row' => 'Zeile löschen', @@ -144,6 +149,7 @@ 'url' => 'URL', 'text_to_display' => 'Anzuzeigender Text', 'title' => 'Titel', + 'browse_links' => 'Links durchsuchen', 'open_link' => 'Link öffnen', 'open_link_in' => 'Link öffnen in...', 'open_link_current' => 'Aktuelles Fenster', @@ -160,6 +166,8 @@ 'about' => 'Über den Editor', 'about_title' => 'Über den WYSIWYG-Editor', 'editor_license' => 'Editorlizenz & Copyright', + 'editor_lexical_license' => 'Dieser Editor wurde mithilfe von :lexicalLink erstellt, der unter der MIT-Lizenz bereitgestellt wird.', + 'editor_lexical_license_link' => 'Vollständige Lizenzdetails finden Sie hier.', 'editor_tiny_license' => 'Dieser Editor wurde mithilfe von :tinyLink erstellt, der unter der MIT-Lizenz bereitgestellt wird.', 'editor_tiny_license_link' => 'Die Copyright- und Lizenzdetails von TinyMCE finden Sie hier.', 'save_continue' => 'Speichern & Fortfahren', diff --git a/lang/de/entities.php b/lang/de/entities.php index 0b46b41f398..ac70c2a9f61 100644 --- a/lang/de/entities.php +++ b/lang/de/entities.php @@ -39,6 +39,34 @@ 'export_pdf' => 'PDF-Datei', 'export_text' => 'Textdatei', 'export_md' => 'Markdown-Datei', + 'export_zip' => 'Portable ZIP', + 'default_template' => 'Standard-Seitenvorlage', + 'default_template_explain' => 'Bestimmen Sie eine Seitenvorlage, die als Standardinhalt für alle Seiten verwendet wird, die innerhalb dieses Elements erstellt werden. Beachten Sie, dass dies nur dann verwendet wird, wenn der Ersteller der Seite Lesezugriff auf die ausgewählte Vorlagen-Seite hat.', + 'default_template_select' => 'Wählen Sie eine Seitenvorlage', + 'import' => 'Import', + 'import_validate' => 'Import validieren', + 'import_desc' => 'Importieren Sie Bücher, Kapitel & Seiten mit einem "Portable Zip-Export" von der gleichen oder einer anderen Instanz. Wählen Sie eine ZIP-Datei, um fortzufahren. Nachdem die Datei hochgeladen und bestätigt wurde, können Sie den Import in der nächsten Ansicht konfigurieren und bestätigen.', + 'import_zip_select' => 'ZIP-Datei zum Hochladen auswählen', + 'import_zip_validation_errors' => 'Fehler bei der Validierung der angegebenen ZIP-Datei:', + 'import_pending' => 'Ausstehende Importe', + 'import_pending_none' => 'Es wurden keine Importe gestartet.', + 'import_continue' => 'Import fortsetzen', + 'import_continue_desc' => 'Überprüfen Sie den Inhalt, der aus der hochgeladenen ZIP-Datei importiert werden soll. Führen Sie den Import aus, um dessen Inhalt zu diesem System hinzuzufügen. Die hochgeladene ZIP-Importdatei wird bei erfolgreichem Import automatisch entfernt.', + 'import_details' => 'Einzelheiten zum Import', + 'import_run' => 'Import starten', + 'import_size' => ':size Import ZIP Größe', + 'import_uploaded_at' => 'Hochgeladen :relativeTime', + 'import_uploaded_by' => 'Hochgeladen von', + 'import_location' => 'Import Ort', + 'import_location_desc' => 'Wählen Sie einen Zielort für Ihren importierten Inhalt. Sie benötigen die entsprechenden Berechtigungen, um innerhalb des gewünschten Standortes zu erstellen.', + 'import_delete_confirm' => 'Sind Sie sicher, dass Sie diesen Import löschen möchten?', + 'import_delete_desc' => 'Dies löscht die hochgeladene ZIP-Datei und kann nicht rückgängig gemacht werden.', + 'import_errors' => 'Importfehler', + 'import_errors_desc' => 'Die folgenden Fehler sind während des Importversuchs aufgetreten:', + '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', // Permissions and restrictions 'permissions' => 'Berechtigungen', @@ -63,14 +91,14 @@ 'search_terms' => 'Suchbegriffe', 'search_content_type' => 'Inhaltstyp', 'search_exact_matches' => 'Exakte Treffer', - 'search_tags' => 'Nach Schlagwort suchen', + 'search_tags' => 'Schlagwort-Suchen', 'search_options' => 'Optionen', 'search_viewed_by_me' => 'Schon von mir angesehen', 'search_not_viewed_by_me' => 'Noch nicht von mir angesehen', 'search_permissions_set' => 'Berechtigungen gesetzt', 'search_created_by_me' => 'Von mir erstellt', 'search_updated_by_me' => 'Von mir aktualisiert', - 'search_owned_by_me' => 'Besitzt von mir', + 'search_owned_by_me' => 'In meinem Besitz', 'search_date_options' => 'Datums Optionen', 'search_updated_before' => 'Aktualisiert vor', 'search_updated_after' => 'Aktualisiert nach', @@ -132,9 +160,6 @@ 'books_edit_named' => 'Buch ":bookName" bearbeiten', 'books_form_book_name' => 'Name des Buches', 'books_save' => 'Buch speichern', - 'books_default_template' => 'Standard Seitentemplate', - 'books_default_template_explain' => 'Zuweisen einer Seitenvorlage, die als Standardinhalt für alle neuen Seiten in diesem Buch verwendet wird. Beachten Sie, dass dies nur dann verwendet wird, wenn der Ersteller einer Seite Zugriff auf die ausgewählte Template-Seite hat.', - 'books_default_template_select' => 'Template-Seite auswählen', 'books_permissions' => 'Buch-Berechtigungen', 'books_permissions_updated' => 'Buch-Berechtigungen aktualisiert', 'books_empty_contents' => 'Es sind noch keine Seiten oder Kapitel zu diesem Buch hinzugefügt worden.', @@ -145,7 +170,9 @@ 'books_search_this' => 'Dieses Buch durchsuchen', 'books_navigation' => 'Buchnavigation', 'books_sort' => 'Buchinhalte sortieren', - 'books_sort_desc' => 'Kapitel und Seiten innerhalb eines Buches verschieben, um dessen Inhalt zu reorganisieren. Andere Bücher können hinzugefügt werden, was das Verschieben von Kapiteln und Seiten zwischen Büchern erleichtert.', + 'books_sort_desc' => 'Kapitel und Seiten innerhalb eines Buches verschieben, um dessen Inhalt zu reorganisieren. Andere Bücher können hinzugefügt werden, was das Verschieben von Kapiteln und Seiten zwischen Büchern erleichtert. Optional kann eine automatische Sortierregel erstellt werden, um den Inhalt dieses Buches nach Änderungen automatisch zu sortieren.', + 'books_sort_auto_sort' => 'Auto-Sortieroption', + 'books_sort_auto_sort_active' => 'Automatische Sortierung aktiv: :sortName', 'books_sort_named' => 'Buch ":bookName" sortieren', 'books_sort_name' => 'Sortieren nach Namen', 'books_sort_created' => 'Sortieren nach Erstellungsdatum', @@ -207,7 +234,7 @@ 'pages_delete_draft' => 'Seitenentwurf löschen', 'pages_delete_success' => 'Seite gelöscht', 'pages_delete_draft_success' => 'Seitenentwurf gelöscht', - 'pages_delete_warning_template' => 'Diese Seite wird aktiv als Standardvorlage für Seiten in diesem Buch verwendet. Diese Bücher haben nach dem Löschen dieser Seite keine Standardvorlage mehr zugewiesen.', + 'pages_delete_warning_template' => 'Diese Seite wird aktiv als Standardvorlage für Bücher oder Kapitel verwendet. In diesen Büchern oder Kapiteln wird nach dem Löschen dieser Seite keine Standardvorlage mehr zugewiesen sein.', 'pages_delete_confirm' => 'Sind Sie sicher, dass Sie diese Seite löschen möchen?', 'pages_delete_draft_confirm' => 'Sind Sie sicher, dass Sie diesen Seitenentwurf löschen möchten?', 'pages_editing_named' => 'Seite ":pageName" bearbeiten', @@ -220,10 +247,12 @@ 'pages_edit_delete_draft' => 'Entwurf löschen', 'pages_edit_delete_draft_confirm' => 'Sind Sie sicher, dass Sie Ihren Entwurf löschen möchten? Alle Ihre Änderungen seit dem letzten vollständigen Speichern gehen verloren und der Editor wird mit dem letzten Speicherzustand aktualisiert, der kein Entwurf ist.', 'pages_edit_discard_draft' => 'Entwurf verwerfen', - 'pages_edit_switch_to_markdown' => 'Zum Markdown Editor wechseln', - 'pages_edit_switch_to_markdown_clean' => '(gesäuberter Output)', - 'pages_edit_switch_to_markdown_stable' => '(html beibehalten)', - 'pages_edit_switch_to_wysiwyg' => 'Wechseln Sie zum WYSIWYG-Editor', + 'pages_edit_switch_to_markdown' => 'Zum Markdown-Editor wechseln', + 'pages_edit_switch_to_markdown_clean' => '(Gesäuberter Inhalt)', + 'pages_edit_switch_to_markdown_stable' => '(Stabiler Inhalt)', + 'pages_edit_switch_to_wysiwyg' => 'Zum WYSIWYG-Editor wechseln', + 'pages_edit_switch_to_new_wysiwyg' => 'Zum neuen WYSIWYG wechseln', + 'pages_edit_switch_to_new_wysiwyg_desc' => '(Im Beta-Test)', 'pages_edit_set_changelog' => 'Änderungsprotokoll hinzufügen', 'pages_edit_enter_changelog_desc' => 'Bitte geben Sie eine kurze Zusammenfassung Ihrer Änderungen ein', 'pages_edit_enter_changelog' => 'Änderungsprotokoll eingeben', @@ -243,6 +272,7 @@ 'pages_md_insert_drawing' => 'Zeichnung einfügen', 'pages_md_show_preview' => 'Vorschau anzeigen', 'pages_md_sync_scroll' => 'Vorschau synchronisieren', + 'pages_md_plain_editor' => 'Plaintext editor', 'pages_drawing_unsaved' => 'Ungespeicherte Zeichnung gefunden', 'pages_drawing_unsaved_confirm' => 'Es wurden ungespeicherte Zeichnungsdaten von einem früheren, fehlgeschlagenen Versuch, die Zeichnung zu speichern, gefunden. Möchten Sie diese ungespeicherte Zeichnung wiederherstellen und weiter bearbeiten?', 'pages_not_in_chapter' => 'Seite ist in keinem Kapitel', @@ -307,7 +337,7 @@ 'tag' => 'Schlagwort', 'tags' => 'Schlagwörter', 'tags_index_desc' => 'Tags können auf Inhalte im System angewendet werden, um eine flexible Form der Kategorisierung anzuwenden. Tags können sowohl einen Schlüssel als auch einen Wert haben, wobei der Wert optional ist. Einmal angewendet, können Inhalte unter Verwendung des Tag-Namens und Wertes abgefragt werden.', - 'tag_name' => 'Schlagwort Name', + 'tag_name' => 'Schlagwortname', 'tag_value' => 'Inhalt (Optional)', 'tags_explain' => "Fügen Sie Schlagwörter hinzu, um Ihren Inhalt zu kategorisieren.\nSie können einen erklärenden Inhalt hinzufügen, um eine genauere Unterteilung vorzunehmen.", 'tags_add' => 'Weiteres Schlagwort hinzufügen', @@ -367,8 +397,11 @@ 'comment' => 'Kommentar', 'comments' => 'Kommentare', 'comment_add' => 'Kommentieren', - 'comment_placeholder' => 'Geben Sie hier Ihre Kommentare ein (Markdown unterstützt)', - 'comment_count' => '{0} Keine Kommentare|{1} 1 Kommentar|[2,*] :count Kommentare', + 'comment_none' => 'Keine Kommentare vorhanden', + 'comment_placeholder' => 'Geben Sie hier Ihre Kommentare ein', + 'comment_thread_count' => ':count Thema|:count Themen', + 'comment_archived_count' => ':count archiviert', + 'comment_archived_threads' => 'Archivierte Themen', 'comment_save' => 'Kommentar speichern', 'comment_new' => 'Neuer Kommentar', 'comment_created' => ':createDiff kommentiert', @@ -377,8 +410,14 @@ 'comment_deleted_success' => 'Kommentar gelöscht', 'comment_created_success' => 'Kommentar hinzugefügt', 'comment_updated_success' => 'Kommentar aktualisiert', + 'comment_archive_success' => 'Kommentar archiviert', + 'comment_unarchive_success' => 'Kommentar nicht mehr archiviert', + 'comment_view' => 'Kommentar ansehen', + 'comment_jump_to_thread' => 'Zum Thema springen', 'comment_delete_confirm' => 'Möchten Sie diesen Kommentar wirklich löschen?', 'comment_in_reply_to' => 'Antwort auf :commentId', + 'comment_reference' => 'Referenz', + 'comment_reference_outdated' => '(Veraltet)', 'comment_editor_explain' => 'Hier sind die Kommentare, die auf dieser Seite hinterlassen wurden. Kommentare können hinzugefügt und verwaltet werden, wenn die gespeicherte Seite angezeigt wird.', // Revision diff --git a/lang/de/errors.php b/lang/de/errors.php index 2fd34c00d21..c1f65d94006 100644 --- a/lang/de/errors.php +++ b/lang/de/errors.php @@ -10,6 +10,7 @@ // Auth 'error_user_exists_different_creds' => 'Ein Benutzer mit der E-Mail-Adresse :email ist bereits mit anderen Anmeldedaten registriert.', + 'auth_pre_register_theme_prevention' => 'Benutzerkonto konnte für die angegebenen Details nicht registriert werden', 'email_already_confirmed' => 'Die E-Mail-Adresse ist bereits bestätigt. Bitte melden Sie sich an.', 'email_confirmation_invalid' => 'Der Bestätigungslink ist nicht gültig oder wurde bereits verwendet. Bitte registrieren Sie sich erneut.', 'email_confirmation_expired' => 'Der Bestätigungslink ist abgelaufen. Es wurde eine neue Bestätigungs-E-Mail gesendet.', @@ -23,7 +24,6 @@ 'saml_invalid_response_id' => 'Die Anfrage vom externen Authentifizierungssystem wird von einem von dieser Anwendung gestarteten Prozess nicht erkannt. Das Zurückgehen nach einer Anmeldung könnte dieses Problem verursachen.', 'saml_fail_authed' => 'Anmeldung mit :system fehlgeschlagen, System konnte keine erfolgreiche Autorisierung bereitstellen', 'oidc_already_logged_in' => 'Bereits angemeldet', - 'oidc_user_not_registered' => 'Der Benutzer :name ist nicht registriert und die automatische Registrierung ist deaktiviert', 'oidc_no_email_address' => 'Es konnte keine E-Mail-Adresse für diesen Benutzer in den vom externen Authentifizierungssystem zur Verfügung gestellten Daten gefunden werden', 'oidc_fail_authed' => 'Anmeldung mit :system fehlgeschlagen, System konnte keine erfolgreiche Autorisierung bereitstellen', 'social_no_action_defined' => 'Es ist keine Aktion definiert', @@ -37,6 +37,7 @@ 'social_driver_not_found' => 'Treiber für Social-Media-Konten nicht gefunden', 'social_driver_not_configured' => 'Ihr :socialAccount-Konto ist nicht korrekt konfiguriert.', 'invite_token_expired' => 'Dieser Einladungslink ist abgelaufen. Sie können stattdessen versuchen Ihr Passwort zurückzusetzen.', + 'login_user_not_found' => 'Ein Benutzer für diese Aktion konnte nicht gefunden werden.', // System 'path_not_writable' => 'Die Datei kann nicht in den angegebenen Pfad :filePath hochgeladen werden. Stellen Sie sicher, dass dieser Ordner auf dem Server beschreibbar ist.', @@ -77,6 +78,7 @@ // Users 'users_cannot_delete_only_admin' => 'Sie können den einzigen Administrator nicht löschen', 'users_cannot_delete_guest' => 'Sie können den Gast-Benutzer nicht löschen', + 'users_could_not_send_invite' => 'Benutzer konnte nicht erstellt werden, da die Einladungs-E-Mail nicht gesendet wurde', // Roles 'role_cannot_be_edited' => 'Diese Rolle kann nicht bearbeitet werden', @@ -103,6 +105,18 @@ 'app_down' => ':appName befindet sich aktuell im Wartungsmodus', 'back_soon' => 'Wir werden so schnell wie möglich wieder online sein.', + // Import + '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_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.', + 'import_perms_chapters' => 'Ihnen fehlt die erforderliche Berechtigung, um Bücher zu erstellen.', + 'import_perms_pages' => 'Ihnen fehlt die erforderliche Berechtigung, um Seiten zu erstellen.', + 'import_perms_images' => 'Ihnen fehlt die erforderliche Berechtigung, um Bilder zu erstellen.', + 'import_perms_attachments' => 'Ihnen fehlt die erforderliche Berechtigung, um Anhänge zu erstellen.', + // API errors 'api_no_authorization_found' => 'Kein Autorisierungstoken für die Anfrage gefunden', 'api_bad_authorization_format' => 'Ein Autorisierungstoken wurde auf die Anfrage gefunden, aber das Format schien falsch zu sein', diff --git a/lang/de/passwords.php b/lang/de/passwords.php index 5896297e08b..c93ccb4dd84 100644 --- a/lang/de/passwords.php +++ b/lang/de/passwords.php @@ -8,7 +8,7 @@ 'password' => 'Passwörter müssen aus mindestens acht Zeichen bestehen und mit der eingegebenen Wiederholung übereinstimmen.', 'user' => "Es wurde kein Benutzer mit dieser E-Mail-Adresse gefunden.", - 'token' => 'Der Link zum Zurücksetzen Ihres Passworts ist entweder ungültig oder abgelaufen.', + 'token' => 'Der Token zum Zurücksetzen des Passworts ist für diese E-Mail-Adresse ungültig.', 'sent' => 'Der Link zum Zurücksetzen Ihres Passwortes wurde Ihnen per E-Mail zugesendet.', 'reset' => 'Ihr Passwort wurde zurückgesetzt!', diff --git a/lang/de/preferences.php b/lang/de/preferences.php index 66c7f6f12e4..26f0b05dacc 100644 --- a/lang/de/preferences.php +++ b/lang/de/preferences.php @@ -27,7 +27,7 @@ 'notifications_save' => 'Einstellungen speichern', 'notifications_update_success' => 'Benachrichtigungseinstellungen wurden aktualisiert!', 'notifications_watched' => 'Beobachtete und ignorierte Elemente', - 'notifications_watched_desc' => ' Nachfolgend finden Sie die Elemente, für die benutzerdefinierten Überwachungspräferenzen gelten. Um Ihre Einstellungen für diese Elemente zu aktualisieren, sehen Sie sich das Element an und suchen dann die Überwachungsoptionen in der Seitenleiste.', + 'notifications_watched_desc' => 'Nachfolgend finden Sie die Elemente, für die benutzerdefinierten Überwachungspräferenzen gelten. Um Ihre Einstellungen für diese Elemente zu aktualisieren, sehen Sie sich das Element an und suchen dann die Überwachungsoptionen in der Seitenleiste.', 'auth' => 'Zugang & Sicherheit', 'auth_change_password' => 'Passwort ändern', @@ -35,12 +35,12 @@ 'auth_change_password_success' => 'Das Passwort wurde aktualisiert!', 'profile' => 'Profildetails', - 'profile_desc' => 'Verwalten Sie die Details Ihres Kontos welche Sie gegenüber anderen Benutzern repräsentiert, zusätzlich zu den Details die für die Kommunikation und Personalisierung des Systems genutzt werden.', - 'profile_view_public' => 'Öffentliches Profil zeigen', - 'profile_name_desc' => 'Konfigurieren Sie Ihren Anzeigenamen, der durch die Aktivität, die Sie ausführen, für andere Benutzer und Ihre eigenen Inhalte im System sichtbar ist.', + 'profile_desc' => 'Verwalten Sie die Details Ihres Kontos, welches Sie gegenüber anderen Benutzern repräsentiert, zusätzlich zu den Details, die für die Kommunikation und Personalisierung des Systems genutzt werden.', + 'profile_view_public' => 'Öffentliches Profil anzeigen', + 'profile_name_desc' => 'Konfigurieren Sie Ihren Anzeigenamen, der durch die Aktivität, die Sie ausführen, und die Ihnen gehörenden Inhalte für andere Benutzer sichtbar ist.', 'profile_email_desc' => 'Diese E-Mail wird für Benachrichtigungen und, je nach aktiver Systemauthentifizierung, den Systemzugriff verwendet.', 'profile_email_no_permission' => 'Leider haben Sie nicht die Berechtigung, Ihre E-Mail-Adresse zu ändern. Wenn Sie diese ändern möchten, wenden Sie sich bitte an Ihren Administrator.', - 'profile_avatar_desc' => 'Wählen Sie ein Bild, dass anderen im System angezeigt wird, um Sie zu repräsentieren. Idealerweise sollte dieses Bild quadratisch und etwa 256px breit und hoch sein.', + 'profile_avatar_desc' => 'Wählen Sie ein Bild aus, das anderen im System angezeigt wird, um Sie zu repräsentieren. Idealerweise sollte dieses Bild quadratisch und etwa 256px breit und hoch sein.', 'profile_admin_options' => 'Administratoroptionen', 'profile_admin_options_desc' => 'Weitere Administrator-Optionen wie zum Beispiel die Verwaltung von Rollenzuweisungen für Ihr Benutzerkonto finden Sie im Bereich "Einstellungen > Benutzer" der Anwendung.', diff --git a/lang/de/settings.php b/lang/de/settings.php index 8769a825344..250668e8a78 100644 --- a/lang/de/settings.php +++ b/lang/de/settings.php @@ -19,17 +19,17 @@ 'app_name_desc' => 'Dieser Name wird im Header und in E-Mails angezeigt.', 'app_name_header' => 'Anwendungsname im Header anzeigen?', 'app_public_access' => 'Öffentlicher Zugriff', - 'app_public_access_desc' => 'Wenn Sie diese Option aktivieren können Besucher, die nicht angemeldet sind, auf Inhalte in Ihrer BookStack-Instanz zugreifen.', + 'app_public_access_desc' => 'Wenn Sie diese Option aktivieren, können Besucher, die nicht angemeldet sind, auf Inhalte in Ihrer BookStack-Instanz zugreifen.', 'app_public_access_desc_guest' => 'Der Zugang für öffentliche Besucher kann über den Benutzer "Guest" gesteuert werden.', 'app_public_access_toggle' => 'Öffentlichen Zugriff erlauben', 'app_public_viewing' => 'Öffentliche Ansicht erlauben?', 'app_secure_images' => 'Erhöhte Sicherheit für hochgeladene Bilder aktivieren?', - 'app_secure_images_toggle' => 'Aktiviere höhere Sicherheit für Bild-Uploads', + 'app_secure_images_toggle' => 'Höhere Sicherheit für Bild-Uploads aktivieren', 'app_secure_images_desc' => 'Aus Leistungsgründen sind alle Bilder öffentlich sichtbar. Diese Option fügt zufällige, schwer zu erratende, Zeichenketten zu Bild-URLs hinzu. Stellen Sie sicher, dass Verzeichnisindizes deaktiviert sind, um einen einfachen Zugriff zu verhindern.', 'app_default_editor' => 'Standard-Seiten-Editor', 'app_default_editor_desc' => 'Wählen Sie aus, welcher Editor standardmäßig beim Bearbeiten neuer Seiten verwendet wird. Dies kann auf einer Seitenebene überschrieben werden, wenn es die Berechtigungen erlauben.', - 'app_custom_html' => 'Benutzerdefinierter HTML Inhalt', - 'app_custom_html_desc' => 'Jeder Inhalt, der hier hinzugefügt wird, wird am Ende der Sektion jeder Seite eingefügt. Diese kann praktisch sein, um CSS Styles anzupassen oder Analytics-Code hinzuzufügen.', + 'app_custom_html' => 'Benutzerdefinierter HTML-Head-Inhalt', + 'app_custom_html_desc' => 'Jeder Inhalt, der hier hinzugefügt wird, wird am Ende der -Sektion jeder Seite eingefügt. Diese kann praktisch sein, um CSS-Styles anzupassen oder Analytics-Code hinzuzufügen.', 'app_custom_html_disabled_notice' => 'Benutzerdefinierte HTML-Kopfzeileninhalte sind auf dieser Einstellungsseite deaktiviert, um sicherzustellen, dass alle Änderungen rückgängig gemacht werden können.', 'app_logo' => 'Anwendungslogo', 'app_logo_desc' => 'Dies wird unter anderem in der Kopfzeile der Anwendung verwendet. Dieses Bild sollte 86px hoch sein. Große Bilder werden herunterskaliert.', @@ -75,6 +75,36 @@ Hinweis: Benutzer können ihre E-Mail-Adresse nach erfolgreicher Registrierung ändern.', 'reg_confirm_restrict_domain_placeholder' => 'Keine Einschränkung gesetzt', + // Sorting Settings + 'sorting' => 'Sortierung', + 'sorting_book_default' => 'Standard-Buchsortierung', + 'sorting_book_default_desc' => 'Wählen Sie die Standard-Sortierregel aus, die auf neue Bücher angewendet werden soll. Dies wirkt sich nicht auf bestehende Bücher aus und kann pro Buch überschrieben werden.', + 'sorting_rules' => 'Sortierregeln', + 'sorting_rules_desc' => 'Dies sind vordefinierte Sortieraktionen, die auf Inhalte im System angewendet werden können.', + 'sort_rule_assigned_to_x_books' => ':count Buch zugewiesen|:count Büchern zugewiesen', + 'sort_rule_create' => 'Sortierregel erstellen', + 'sort_rule_edit' => 'Sortierregel bearbeiten', + 'sort_rule_delete' => 'Sortierregel löschen', + 'sort_rule_delete_desc' => 'Diese Sortierregel aus dem System entfernen. Bücher mit dieser Sortierung werden auf manuelle Sortierung zurückgesetzt.', + 'sort_rule_delete_warn_books' => 'Diese Sortierregel wird derzeit in :count Bücher(n) verwendet. Sind Sie sicher, dass Sie dies löschen möchten?', + 'sort_rule_delete_warn_default' => 'Diese Sortierregel wird derzeit als Standard für Bücher verwendet. Sind Sie sicher, dass Sie dies löschen möchten?', + 'sort_rule_details' => 'Sortierregel-Details', + 'sort_rule_details_desc' => 'Legen Sie einen Namen für diese Sortierregel fest, der in Listen erscheint, wenn Benutzer eine Sortierung auswählen.', + 'sort_rule_operations' => 'Sortierungs-Aktionen', + 'sort_rule_operations_desc' => 'Konfigurieren Sie die durchzuführenden Sortieraktionen durch Verschieben von der Liste der verfügbaren Aktionen. Bei der Verwendung werden die Aktionen von oben nach unten angewendet. Alle hier vorgenommenen Änderungen werden beim Speichern auf alle zugewiesenen Bücher angewendet.', + 'sort_rule_available_operations' => 'Verfügbare Aktionen', + 'sort_rule_available_operations_empty' => 'Keine verbleibenden Aktionen', + 'sort_rule_configured_operations' => 'Konfigurierte Aktionen', + 'sort_rule_configured_operations_empty' => 'Aktionen aus der Liste "Verfügbare Operationen" ziehen/hinzufügen', + 'sort_rule_op_asc' => '(Aufst.)', + 'sort_rule_op_desc' => '(Abst.)', + 'sort_rule_op_name' => 'Name - Alphabetisch', + 'sort_rule_op_name_numeric' => 'Name - Numerisch', + 'sort_rule_op_created_date' => 'Erstellungsdatum', + 'sort_rule_op_updated_date' => 'Aktualisierungsdatum', + 'sort_rule_op_chapters_first' => 'Kapitel zuerst', + 'sort_rule_op_chapters_last' => 'Kapitel zuletzt', + // Maintenance settings 'maint' => 'Wartung', 'maint_image_cleanup' => 'Bilder bereinigen', @@ -84,11 +114,11 @@ 'maint_image_cleanup_warning' => ':count eventuell unbenutze Bilder wurden gefunden. Möchten Sie diese Bilder löschen?', 'maint_image_cleanup_success' => ':count eventuell unbenutze Bilder wurden gefunden und gelöscht.', 'maint_image_cleanup_nothing_found' => 'Keine unbenutzen Bilder gefunden. Nichts zu löschen!', - 'maint_send_test_email' => 'Test Email versenden', - 'maint_send_test_email_desc' => 'Dies sendet eine Test E-Mail an Ihre in Ihrem Profil angegebene E-Mail-Adresse.', - 'maint_send_test_email_run' => 'Sende eine Test E-Mail', + 'maint_send_test_email' => 'Eine Test-E-Mail versenden', + 'maint_send_test_email_desc' => 'Dies sendet eine Test-E-Mail an Ihre in Ihrem Profil angegebene E-Mail-Adresse.', + 'maint_send_test_email_run' => 'Test-E-Mail senden', 'maint_send_test_email_success' => 'E-Mail wurde an :address gesendet', - 'maint_send_test_email_mail_subject' => 'Test E-Mail', + 'maint_send_test_email_mail_subject' => 'Test-E-Mail', 'maint_send_test_email_mail_greeting' => 'E-Mail-Versand scheint zu funktionieren!', 'maint_send_test_email_mail_text' => 'Glückwunsch! Da Sie diese E-Mail Benachrichtigung erhalten haben, scheinen Ihre E-Mail-Einstellungen korrekt konfiguriert zu sein.', 'maint_recycle_bin_desc' => 'Gelöschte Regale, Bücher, Kapitel & Seiten werden in den Papierkorb verschoben, so dass sie wiederhergestellt oder dauerhaft gelöscht werden können. Ältere Gegenstände im Papierkorb können, in Abhängigkeit von der Systemkonfiguration, nach einer Weile automatisch entfernt werden.', @@ -110,7 +140,7 @@ 'recycle_bin_contents_empty' => 'Der Papierkorb ist derzeit leer', 'recycle_bin_empty' => 'Papierkorb leeren', 'recycle_bin_empty_confirm' => 'Dies wird alle Gegenstände im Papierkorb dauerhaft entfernen, einschließlich der Inhalte, die darin enthalten sind. Sind Sie sicher, dass Sie den Papierkorb leeren möchten?', - 'recycle_bin_destroy_confirm' => 'Diese Aktion wird dieses Element zusammen mit allen unten aufgeführten Unterelementen dauerhaft aus dem System löschen und Sie werden nicht in der Lage sein, diesen Inhalt wiederherzustellen. Sind Sie sicher, dass Sie dieses Element endgültig löschen möchten?', + 'recycle_bin_destroy_confirm' => 'Diese Aktion löscht dieses Element dauerhaft aus dem System, zusammen mit allen unten aufgeführten untergeordneten Elementen, und es ist nicht möglich, diesen Inhalt wiederherzustellen. Sind Sie sicher, dass Sie dieses Element dauerhaft löschen möchten?', 'recycle_bin_destroy_list' => 'Zu löschende Elemente', 'recycle_bin_restore_list' => 'Zu wiederherzustellende Elemente', 'recycle_bin_restore_confirm' => 'Mit dieser Aktion wird das gelöschte Element einschließlich aller untergeordneten Elemente an seinen ursprünglichen Ort wiederherstellen. Wenn der ursprüngliche Ort gelöscht wurde und sich nun im Papierkorb befindet, muss auch das übergeordnete Element wiederhergestellt werden.', @@ -129,7 +159,7 @@ 'audit_table_user' => 'Benutzer', 'audit_table_event' => 'Ereignis', 'audit_table_related' => 'Verknüpftes Element oder Detail', - 'audit_table_ip' => 'IP Adresse', + 'audit_table_ip' => 'IP-Adresse', 'audit_table_date' => 'Aktivitätsdatum', 'audit_date_from' => 'Zeitraum von', 'audit_date_to' => 'Zeitraum bis', @@ -163,6 +193,7 @@ 'role_access_api' => 'Systemzugriffs-API', 'role_manage_settings' => 'Globaleinstellungen verwalten', 'role_export_content' => 'Inhalt exportieren', + 'role_import_content' => 'Inhalt importieren', 'role_editor_change' => 'Seiten-Editor ändern', 'role_notifications' => 'Empfangen und Verwalten von Benachrichtigungen', 'role_asset' => 'Berechtigungen', @@ -277,6 +308,14 @@ 'webhooks_last_errored' => 'Letzter Fehler:', 'webhooks_last_error_message' => 'Letzte Fehlermeldung:', + // Licensing + 'licenses' => 'Lizenzen', + 'licenses_desc' => 'Diese Seite beschreibt Lizenzinformationen für BookStack zusätzlich zu den Projekten und Bibliotheken, die in BookStack verwendet werden. Viele aufgelistete Projekte dürfen nur in einem Entwicklungskontext verwendet werden.', + 'licenses_bookstack' => 'BookStack-Lizenz', + 'licenses_php' => 'PHP-Bibliothekslizenzen', + 'licenses_js' => 'JavaScript-Bibliothekslizenzen', + 'licenses_other' => 'Andere Lizenzen', + 'license_details' => 'Lizenzdetails', //! If editing translations files directly please ignore this in all //! languages apart from en. Content will be auto-copied from en. @@ -288,6 +327,7 @@ 'bs' => 'Bosnisch', 'ca' => 'Katalanisch', 'cs' => 'Tschechisch', + 'cy' => 'Cymraeg', 'da' => 'Dänisch', 'de' => 'Deutsch (Sie)', 'de_informal' => 'Deutsch (Du)', @@ -309,6 +349,7 @@ 'lt' => 'Litauisch', 'lv' => 'Lettisch', 'nb' => 'Norwegisch (Bokmål)', + 'ne' => 'नेपाली', 'nn' => 'Nynorsk', 'nl' => 'Niederländisch', 'pl' => 'Polnisch', diff --git a/lang/de/validation.php b/lang/de/validation.php index 1ca1f458e18..2ffad05298e 100644 --- a/lang/de/validation.php +++ b/lang/de/validation.php @@ -9,7 +9,7 @@ // Standard laravel validation lines 'accepted' => ':attribute muss akzeptiert werden.', - 'active_url' => ':attribute ist keine valide URL.', + 'active_url' => ':attribute ist keine gültige URL.', 'after' => ':attribute muss ein Datum nach :date sein.', 'alpha' => ':attribute kann nur Buchstaben enthalten.', 'alpha_dash' => ':attribute kann nur Buchstaben, Zahlen und Bindestriche enthalten.', @@ -25,12 +25,12 @@ ], 'boolean' => ':attribute Feld muss wahr oder falsch sein.', 'confirmed' => ':attribute stimmt nicht überein.', - 'date' => ':attribute ist kein valides Datum.', + 'date' => ':attribute ist kein gültiges Datum.', 'date_format' => ':attribute entspricht nicht dem Format :format.', 'different' => ':attribute und :other müssen unterschiedlich sein.', 'digits' => ':attribute muss :digits Stellen haben.', 'digits_between' => ':attribute muss zwischen :min und :max Stellen haben.', - 'email' => ':attribute muss eine valide E-Mail-Adresse sein.', + 'email' => ':attribute muss eine gültige E-Mail-Adresse sein.', 'ends_with' => ':attribute muss mit einem der folgenden Werte: :values enden', 'file' => ':attribute muss als gültige Datei angegeben werden.', 'filled' => ':attribute ist erforderlich.', @@ -51,8 +51,8 @@ 'image_extension' => ':attribute muss eine gültige und unterstützte Bild-Dateiendung haben.', 'in' => ':attribute ist ungültig.', 'integer' => ':attribute muss eine Zahl sein.', - 'ip' => ':attribute muss eine valide IP-Adresse sein.', - 'ipv4' => ':attribute muss eine gültige IPv4 Adresse sein.', + 'ip' => ':attribute muss eine gültige IP-Adresse sein.', + 'ipv4' => ':attribute muss eine gültige IPv4-Adresse sein.', 'ipv6' => ':attribute muss eine gültige IPv6-Adresse sein.', 'json' => 'Das Attribut muss eine gültige JSON-Zeichenfolge sein.', 'lt' => [ @@ -80,8 +80,8 @@ 'string' => ':attribute muss mindestens :min Zeichen lang sein.', 'array' => ':attribute muss mindesten :min Elemente enthalten.', ], - 'not_in' => ':attribute ist ungültig.', - 'not_regex' => ':attribute ist kein valides Format.', + 'not_in' => 'Das ausgewählte :attribute ist ungültig.', + 'not_regex' => ':attribute ist kein gültiges Format.', 'numeric' => ':attribute muss eine Zahl sein.', 'regex' => ':attribute ist in einem ungültigen Format.', 'required' => ':attribute ist erforderlich.', @@ -99,12 +99,17 @@ 'array' => ':attribute muss :size Elemente enthalten.', ], 'string' => ':attribute muss eine Zeichenkette sein.', - 'timezone' => ':attribute muss eine valide zeitzone sein.', + 'timezone' => ':attribute muss eine gültige Zeitzone sein.', 'totp' => 'Der angegebene Code ist ungültig oder abgelaufen.', 'unique' => ':attribute wird bereits verwendet.', - 'url' => ':attribute ist kein valides Format.', + 'url' => ':attribute ist kein gültiges Format.', 'uploaded' => 'Die Datei konnte nicht hochgeladen werden. Der Server akzeptiert möglicherweise keine Dateien dieser Größe.', + 'zip_file' => ':attribute muss eine Datei innerhalb des ZIP referenzieren.', + 'zip_file_mime' => ':attribute muss eine Datei des Typs :validType referenzieren, gefunden :foundType.', + 'zip_model_expected' => 'Datenobjekt erwartet, aber ":type" gefunden.', + 'zip_unique' => ':attribute muss für den Objekttyp innerhalb des ZIP eindeutig sein.', + // Custom validation lines 'custom' => [ 'password-confirm' => [ diff --git a/lang/de_informal/activities.php b/lang/de_informal/activities.php index a176a75f75d..990bfa0bc56 100644 --- a/lang/de_informal/activities.php +++ b/lang/de_informal/activities.php @@ -6,51 +6,51 @@ return [ // Pages - 'page_create' => 'erstellt Seite', + 'page_create' => 'erstellte Seite', 'page_create_notification' => 'Seite erfolgreich erstellt', - 'page_update' => 'Seite aktualisiert', + 'page_update' => 'aktualisierte Seite', 'page_update_notification' => 'Seite erfolgreich aktualisiert', - 'page_delete' => 'Seite gelöscht', + 'page_delete' => 'löschte Seite', 'page_delete_notification' => 'Seite erfolgreich gelöscht', - 'page_restore' => 'Seite wiederhergestellt', + 'page_restore' => 'stellte Seite wieder her', 'page_restore_notification' => 'Seite erfolgreich wiederhergestellt', - 'page_move' => 'Seite verschoben', + 'page_move' => 'verschob Seite', 'page_move_notification' => 'Seite erfolgreich verschoben', // Chapters - 'chapter_create' => 'Kapitel erstellt', + 'chapter_create' => 'erstellte Kapitel', 'chapter_create_notification' => 'Kapitel erfolgreich erstellt', - 'chapter_update' => 'Kapitel aktualisiert', + 'chapter_update' => 'aktualisierte Kapitel', 'chapter_update_notification' => 'Kapitel erfolgreich aktualisiert', - 'chapter_delete' => 'Kapitel gelöscht', + 'chapter_delete' => 'löschte Kapitel', 'chapter_delete_notification' => 'Kapitel erfolgreich gelöscht', - 'chapter_move' => 'Kapitel verschoben', + 'chapter_move' => 'verschob Kapitel', 'chapter_move_notification' => 'Kapitel erfolgreich verschoben', // Books - 'book_create' => 'Buch erstellt', + 'book_create' => 'erstellte Buch', 'book_create_notification' => 'Buch erfolgreich erstellt', - 'book_create_from_chapter' => 'Kapitel zu Buch umgewandelt', + 'book_create_from_chapter' => 'wandelte Kapitel zu Buch um', 'book_create_from_chapter_notification' => 'Kapitel erfolgreich in ein Buch umgewandelt', - 'book_update' => 'Buch aktualisiert', + 'book_update' => 'aktualisierte Buch', 'book_update_notification' => 'Buch erfolgreich aktualisiert', - 'book_delete' => 'Buch gelöscht', + 'book_delete' => 'löschte Buch', 'book_delete_notification' => 'Buch erfolgreich gelöscht', - 'book_sort' => 'Buch sortiert', + 'book_sort' => 'sortierte Buch', 'book_sort_notification' => 'Buch erfolgreich umsortiert', // Bookshelves - 'bookshelf_create' => 'Regal erstellt', + 'bookshelf_create' => 'erstellte Regal', 'bookshelf_create_notification' => 'Regal erfolgreich erstellt', - 'bookshelf_create_from_book' => 'Buch zu Regal umgewandelt', + 'bookshelf_create_from_book' => 'wandelte Buch zu Regal um', 'bookshelf_create_from_book_notification' => 'Buch erfolgreich zu einem Regal umgewandelt', - 'bookshelf_update' => 'Regal aktualisiert', + 'bookshelf_update' => 'aktualisierte Regal', 'bookshelf_update_notification' => 'Regal erfolgreich aktualisiert', - 'bookshelf_delete' => 'Regal gelöscht', + 'bookshelf_delete' => 'löschte Regal', 'bookshelf_delete_notification' => 'Regal erfolgreich gelöscht', // Revisions - 'revision_restore' => 'Revision wiederhergestellt', + 'revision_restore' => 'stellte Revision wieder her', 'revision_delete' => 'Revision gelöscht', 'revision_delete_notification' => 'Revision erfolgreich gelöscht', @@ -84,6 +84,14 @@ 'webhook_delete' => 'gelöschter Webhook', 'webhook_delete_notification' => 'Webhook erfolgreich gelöscht', + // Imports + 'import_create' => 'erstellter Import', + 'import_create_notification' => 'Import erfolgreich hochgeladen', + 'import_run' => 'aktualisierter Import', + 'import_run_notification' => 'Inhalt erfolgreich importiert', + 'import_delete' => 'gelöschter Import', + 'import_delete_notification' => 'Import erfolgreich gelöscht', + // Users 'user_create' => 'hat Benutzer erzeugt:', 'user_create_notification' => 'Benutzer erfolgreich erstellt', @@ -93,11 +101,11 @@ 'user_delete_notification' => 'Benutzer erfolgreich entfernt', // API Tokens - 'api_token_create' => 'hat API-Token erzeugt:', + 'api_token_create' => 'API Token wurde erstellt', 'api_token_create_notification' => 'API-Token erfolgreich erstellt', - 'api_token_update' => 'hat API-Token aktualisiert:', + 'api_token_update' => 'API Token wurde aktualisiert', 'api_token_update_notification' => 'API-Token erfolgreich aktualisiert', - 'api_token_delete' => 'hat API-Token gelöscht:', + 'api_token_delete' => 'API Token gelöscht', 'api_token_delete_notification' => 'API-Token erfolgreich gelöscht', // Roles @@ -119,6 +127,14 @@ 'comment_update' => 'Kommentar aktualisiert', 'comment_delete' => 'Kommentar gelöscht', + // Sort Rules + 'sort_rule_create' => 'hat eine Sortierregel erstellt', + 'sort_rule_create_notification' => 'Sortierregel erfolgreich angelegt', + 'sort_rule_update' => 'hat eine Sortierregel aktualisiert', + 'sort_rule_update_notification' => 'Sortierregel erfolgreich aktualisiert', + 'sort_rule_delete' => 'hat eine Sortierregel gelöscht', + 'sort_rule_delete_notification' => 'Sortierregel erfolgreich gelöscht', + // Other 'permissions_update' => 'aktualisierte Berechtigungen', ]; diff --git a/lang/de_informal/auth.php b/lang/de_informal/auth.php index b133df49e3d..17dcdbcb26f 100644 --- a/lang/de_informal/auth.php +++ b/lang/de_informal/auth.php @@ -33,7 +33,7 @@ 'social_registration_text' => 'Mit einem dieser Dienste registrieren oder anmelden', 'register_thanks' => 'Vielen Dank für deine Registrierung!', - 'register_confirm' => 'Bitte prüfe deinen Posteingang und bestätige die Registrierung.', + 'register_confirm' => 'Bitte prüfe deinen Posteingang und bestätige die Registrierung, um :appName nutzen zu können.', 'registrations_disabled' => 'Eine Registrierung ist momentan nicht möglich', 'registration_email_domain_invalid' => 'Du kannst dich mit dieser E-Mail nicht registrieren.', 'register_success' => 'Vielen Dank für deine Registrierung! Du bist jetzt registriert und eingeloggt.', @@ -82,7 +82,7 @@ // Multi-factor Authentication 'mfa_setup' => 'Multi-Faktor-Authentifizierung einrichten', - 'mfa_setup_desc' => 'Richte eine Multi-Faktor-Authentifizierung als zusätzliche Sicherheitsstufe für dein Benutzerkonto ein.', + 'mfa_setup_desc' => 'Richte Multi-Faktor-Authentifizierung als zusätzliche Sicherheitsstufe für dein Benutzerkonto ein.', 'mfa_setup_configured' => 'Bereits konfiguriert', 'mfa_setup_reconfigure' => 'Umkonfigurieren', 'mfa_setup_remove_confirmation' => 'Bist du sicher, dass du diese Multi-Faktor-Authentifizierungsmethode entfernen möchtest?', @@ -91,7 +91,7 @@ 'mfa_option_totp_title' => 'Mobile App', 'mfa_option_totp_desc' => 'Um Mehrfach-Faktor-Authentifizierung nutzen zu können, benötigst du eine mobile Anwendung, die TOTP unterstützt, wie Google Authenticator, Authy oder Microsoft Authenticator.', 'mfa_option_backup_codes_title' => 'Backup Code', - 'mfa_option_backup_codes_desc' => 'Speichere eine Reihe von einmaligen Backup-Codes an einem sicheren Ort. Du kannst damit deine identität bestätigen.', + 'mfa_option_backup_codes_desc' => 'Erzeugt eine Reihe von einmalig nutzbaren Backup-Codes, welche Du bei der Anmeldung eingibst, um deine Identität zu bestätigen. Achte darauf diese an einem sicheren Ort aufzubewahren.', 'mfa_gen_confirm_and_enable' => 'Bestätigen und aktivieren', 'mfa_gen_backup_codes_title' => 'Backup-Codes einrichten', 'mfa_gen_backup_codes_desc' => 'Speichere die folgende Liste der Codes an einem sicheren Ort. Wenn du auf das System zugreifst, kannst du einen der Codes als zweiten Authentifizierungsmechanismus verwenden.', diff --git a/lang/de_informal/common.php b/lang/de_informal/common.php index 9014f4b2f3e..d07fa056fd5 100644 --- a/lang/de_informal/common.php +++ b/lang/de_informal/common.php @@ -20,7 +20,7 @@ 'description' => 'Beschreibung', 'role' => 'Rolle', 'cover_image' => 'Titelbild', - 'cover_image_description' => 'Das Bild sollte eine Auflösung von 440x250px haben.', + 'cover_image_description' => 'Dieses Bild sollte ungefähr 440 x 250 Pixel groß sein, kann jedoch je nach Bedarf flexibel skaliert und zugeschnitten werden, um es an die Benutzeroberfläche in verschiedenen Szenarien anzupassen, sodass die tatsächlichen Abmessungen für die Anzeige abweichen können.', // Actions 'actions' => 'Aktionen', @@ -30,6 +30,8 @@ 'create' => 'Anlegen', 'update' => 'Aktualisieren', 'edit' => 'Bearbeiten', + 'archive' => 'Archivieren', + 'unarchive' => 'Nicht mehr archivieren', 'sort' => 'Sortieren', 'move' => 'Verschieben', 'copy' => 'Kopieren', @@ -107,4 +109,7 @@ // Not directly used but available for convenience to users. 'privacy_policy' => 'Datenschutzerklärung', 'terms_of_service' => 'Allgemeine Geschäftsbedingungen', + + // OpenSearch + 'opensearch_description' => 'Suche :appName', ]; diff --git a/lang/de_informal/editor.php b/lang/de_informal/editor.php index 314961088ed..30c38271cfa 100644 --- a/lang/de_informal/editor.php +++ b/lang/de_informal/editor.php @@ -13,6 +13,7 @@ 'cancel' => 'Abbrechen', 'save' => 'Speichern', 'close' => 'Schließen', + 'apply' => 'Übernehmen', 'undo' => 'Rückgängig machen', 'redo' => 'Wiederholen', 'left' => 'Links', @@ -47,6 +48,7 @@ 'superscript' => 'Hochgestellt', 'subscript' => 'Tiefgestellt', 'text_color' => 'Textfarbe', + 'highlight_color' => 'Markierungsfarbe', 'custom_color' => 'Benutzerdefinierte Farbe', 'remove_color' => 'Farbe entfernen', 'background_color' => 'Hintergrundfarbe', @@ -81,6 +83,9 @@ 'table_properties' => 'Tabelleneigenschaften', 'table_properties_title' => 'Tabelleneigenschaften', 'delete_table' => 'Tabelle löschen', + 'table_clear_formatting' => 'Tabellenformatierung entfernen', + 'resize_to_contents' => 'Größe an Inhalt anpassen', + 'row_header' => 'Zeilenüberschrift', 'insert_row_before' => 'Zeile oberhalb einfügen', 'insert_row_after' => 'Zeile unterhalb einfügen', 'delete_row' => 'Zeile löschen', @@ -144,6 +149,7 @@ 'url' => 'URL', 'text_to_display' => 'Anzuzeigender Text', 'title' => 'Titel', + 'browse_links' => 'Links durchsuchen', 'open_link' => 'Link öffnen', 'open_link_in' => 'Link öffnen in...', 'open_link_current' => 'Aktuellem Fenster', @@ -158,8 +164,10 @@ // About view 'about' => 'Über den Editor', - 'about_title' => 'Über den WYSIWYG Editor', + 'about_title' => 'Über den WYSIWYG-Editor', 'editor_license' => 'Editorlizenz & Copyright', + 'editor_lexical_license' => 'Dieser Editor wurde mithilfe von :lexicalLink erstellt, der unter der MIT-Lizenz bereitgestellt wird.', + 'editor_lexical_license_link' => 'Vollständige Lizenzdetails findest du hier.', 'editor_tiny_license' => 'Dieser Editor wurde mit :tinyLink erstellt, das unter der MIT-Lizenz zur Verfügung gestellt wird.', 'editor_tiny_license_link' => 'Die Copyright- und Lizenzdetails von TinyMCE findest du hier.', 'save_continue' => 'Seite speichern & fortfahren', diff --git a/lang/de_informal/entities.php b/lang/de_informal/entities.php index 43842a55d16..346cd891ead 100644 --- a/lang/de_informal/entities.php +++ b/lang/de_informal/entities.php @@ -39,6 +39,34 @@ 'export_pdf' => 'PDF-Datei', 'export_text' => 'Textdatei', 'export_md' => 'Markdown-Datei', + 'export_zip' => 'Portable ZIP', + 'default_template' => 'Standard-Seitenvorlage', + 'default_template_explain' => 'Bestimme eine Seitenvorlage, die als Standardinhalt für alle Seiten verwendet wird, die innerhalb dieses Elements erstellt werden. Beachte, dass dies nur dann verwendet wird, wenn der Ersteller der Seite Lesezugriff auf die ausgewählte Vorlagen-Seite hat.', + 'default_template_select' => 'Wähle eine Seitenvorlage', + 'import' => 'Importieren', + 'import_validate' => 'Import validieren', + 'import_desc' => 'Importiere Bücher, Kapitel & Seiten mit einem ZIP-Export von der gleichen oder einer anderen Instanz. Wähle eine ZIP-Datei, um fortzufahren. Nachdem die Datei hochgeladen und bestätigt wurde, kannst Du den Import in der nächsten Ansicht konfigurieren und bestätigen.', + 'import_zip_select' => 'ZIP-Datei zum Hochladen auswählen', + 'import_zip_validation_errors' => 'Fehler bei der Validierung der angegebenen ZIP-Datei:', + 'import_pending' => 'Ausstehende Importe', + 'import_pending_none' => 'Es wurden keine Importe gestartet.', + 'import_continue' => 'Import fortsetzen', + 'import_continue_desc' => 'Überprüfe den Inhalt, der aus der hochgeladenen ZIP-Datei importiert werden soll. Führe den Import aus, um dessen Inhalt zu diesem System hinzuzufügen. Die hochgeladene ZIP-Importdatei wird bei erfolgreichem Import automatisch gelöscht.', + 'import_details' => 'Importdetails', + 'import_run' => 'Import ausführen', + 'import_size' => 'Größe des importierten ZIP: :size', + 'import_uploaded_at' => 'Hochgeladen :relativeTime', + 'import_uploaded_by' => 'Hochgeladen von', + 'import_location' => 'Import Zielort', + 'import_location_desc' => 'Wähle einen Zielort für deinen importierten Inhalt. Du benötigst die entsprechenden Berechtigungen, um innerhalb des gewünschten Zielortes Inhalte zu erstellen.', + 'import_delete_confirm' => 'Bist Du sicher, dass Du diesen Import löschen möchtest?', + 'import_delete_desc' => 'Dies löscht die hochgeladene ZIP-Datei und kann nicht rückgängig gemacht werden.', + 'import_errors' => 'Importfehler', + 'import_errors_desc' => 'Die folgenden Fehler sind während des Importversuchs aufgetreten:', + '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', // Permissions and restrictions 'permissions' => 'Berechtigungen', @@ -63,7 +91,7 @@ 'search_terms' => 'Suchbegriffe', 'search_content_type' => 'Inhaltstyp', 'search_exact_matches' => 'Exakte Treffer', - 'search_tags' => 'Nach Schlagwort suchen', + 'search_tags' => 'Schlagwort-Suchen', 'search_options' => 'Optionen', 'search_viewed_by_me' => 'Schon von mir angesehen', 'search_not_viewed_by_me' => 'Noch nicht von mir angesehen', @@ -132,9 +160,6 @@ 'books_edit_named' => 'Buch ":bookName" bearbeiten', 'books_form_book_name' => 'Name des Buches', 'books_save' => 'Buch speichern', - 'books_default_template' => 'Standard Seitentemplate', - 'books_default_template_explain' => 'Zuweisen einer Seitenvorlage, die als Standardinhalt für alle neuen Seiten in diesem Buch verwendet wird. Beachte, dass dies nur dann verwendet wird, wenn der Ersteller einer Seite Zugriff auf die ausgewählte Template-Seite hat.', - 'books_default_template_select' => 'Template-Seite auswählen', 'books_permissions' => 'Buch-Berechtigungen', 'books_permissions_updated' => 'Buch-Berechtigungen aktualisiert', 'books_empty_contents' => 'Es sind noch keine Seiten oder Kapitel zu diesem Buch hinzugefügt worden.', @@ -145,7 +170,9 @@ 'books_search_this' => 'Dieses Buch durchsuchen', 'books_navigation' => 'Buchnavigation', 'books_sort' => 'Buchinhalte sortieren', - 'books_sort_desc' => 'Kapitel und Seiten innerhalb eines Buches verschieben, um dessen Inhalt neu zu ordnen. Andere Bücher können hinzugefügt werden, was das Verschieben von Kapiteln und Seiten zwischen Büchern erleichtert.', + 'books_sort_desc' => 'Kapitel und Seiten innerhalb eines Buches verschieben, um dessen Inhalt zu reorganisieren. Andere Bücher können hinzugefügt werden, was das Verschieben von Kapiteln und Seiten zwischen Büchern erleichtert. Optional kann eine automatische Sortierregel erstellt werden, um den Inhalt dieses Buches nach Änderungen automatisch zu sortieren.', + 'books_sort_auto_sort' => 'Auto-Sortieroption', + 'books_sort_auto_sort_active' => 'Automatische Sortierung aktiv: :sortName', 'books_sort_named' => 'Buch ":bookName" sortieren', 'books_sort_name' => 'Sortieren nach Namen', 'books_sort_created' => 'Sortieren nach Erstellungsdatum', @@ -207,7 +234,7 @@ 'pages_delete_draft' => 'Seitenentwurf löschen', 'pages_delete_success' => 'Seite gelöscht', 'pages_delete_draft_success' => 'Seitenentwurf gelöscht', - 'pages_delete_warning_template' => 'Diese Seite wird aktiv als Standardvorlage für Seiten in diesem Buch verwendet. Diese Bücher haben nach dem Löschen dieser Seite keine Standardvorlage mehr zugewiesen.', + 'pages_delete_warning_template' => 'Diese Seite wird aktiv als Standardvorlage für Bücher oder Kapitel verwendet. In diesen Büchern oder Kapiteln wird nach dem Löschen dieser Seite keine Standardvorlage mehr zugewiesen sein.', 'pages_delete_confirm' => 'Bist du sicher, dass du diese Seite löschen möchtest?', 'pages_delete_draft_confirm' => 'Bist du sicher, dass du diesen Seitenentwurf löschen möchtest?', 'pages_editing_named' => 'Seite ":pageName" bearbeiten', @@ -220,10 +247,12 @@ 'pages_edit_delete_draft' => 'Entwurf löschen', 'pages_edit_delete_draft_confirm' => 'Bist du sicher, dass du deinen Entwurf löschen möchtest? Alle deine Änderungen seit dem letzten vollständigen Speichern gehen verloren und der Editor wird mit dem letzten Speicherzustand aktualisiert, der kein Entwurf ist.', 'pages_edit_discard_draft' => 'Entwurf verwerfen', - 'pages_edit_switch_to_markdown' => 'Zum Markdown Editor wechseln', - 'pages_edit_switch_to_markdown_clean' => '(Sauberer Inhalt)', + 'pages_edit_switch_to_markdown' => 'Zum Markdown-Editor wechseln', + 'pages_edit_switch_to_markdown_clean' => '(Gesäuberter Inhalt)', 'pages_edit_switch_to_markdown_stable' => '(Stabiler Inhalt)', - 'pages_edit_switch_to_wysiwyg' => 'Zum WYSIWYG Editor wechseln', + 'pages_edit_switch_to_wysiwyg' => 'Zum WYSIWYG-Editor wechseln', + 'pages_edit_switch_to_new_wysiwyg' => 'Zum neuen WYSIWYG wechseln', + 'pages_edit_switch_to_new_wysiwyg_desc' => '(Im Beta-Test)', 'pages_edit_set_changelog' => 'Änderungsprotokoll hinzufügen', 'pages_edit_enter_changelog_desc' => 'Bitte gib eine kurze Zusammenfassung deiner Änderungen ein', 'pages_edit_enter_changelog' => 'Änderungsprotokoll eingeben', @@ -243,6 +272,7 @@ 'pages_md_insert_drawing' => 'Zeichnung einfügen', 'pages_md_show_preview' => 'Vorschau anzeigen', 'pages_md_sync_scroll' => 'Vorschau synchronisieren', + 'pages_md_plain_editor' => 'Plaintext editor', 'pages_drawing_unsaved' => 'Ungespeicherte Zeichnung gefunden', 'pages_drawing_unsaved_confirm' => 'Es wurden ungespeicherte Zeichnungsdaten von einem früheren, fehlgeschlagenen Versuch, die Zeichnung zu speichern, gefunden. Möchtest du diese ungespeicherte Zeichnung wiederherstellen und weiter bearbeiten?', 'pages_not_in_chapter' => 'Seite ist in keinem Kapitel', @@ -307,7 +337,7 @@ 'tag' => 'Schlagwort', 'tags' => 'Schlagwörter', 'tags_index_desc' => 'Tags können auf Inhalte im System angewendet werden, um eine flexible Form der Kategorisierung anzuwenden. Tags können sowohl einen Schlüssel als auch einen Wert haben, wobei der Wert optional ist. Einmal angewendet, können Inhalte unter Verwendung des Tag-Namens und Wertes abgefragt werden.', - 'tag_name' => 'Schlagwort Name', + 'tag_name' => 'Schlagwortname', 'tag_value' => 'Inhalt (Optional)', 'tags_explain' => "Füge Schlagwörter hinzu, um ihren Inhalt zu kategorisieren.\nDu kannst einen erklärenden Inhalt hinzufügen, um eine genauere Unterteilung vorzunehmen.", 'tags_add' => 'Weiteres Schlagwort hinzufügen', @@ -367,8 +397,11 @@ 'comment' => 'Kommentar', 'comments' => 'Kommentare', 'comment_add' => 'Kommentieren', - 'comment_placeholder' => 'Gib hier deine Kommentare ein (Markdown unterstützt)', - 'comment_count' => '{0} Keine Kommentare|{1} 1 Kommentar|[2,*] :count Kommentare', + 'comment_none' => 'Keine Kommentare vorhanden', + 'comment_placeholder' => 'Gib hier deine Kommentare ein', + 'comment_thread_count' => ':count Thema|:count Themen', + 'comment_archived_count' => ':count archiviert', + 'comment_archived_threads' => 'Archivierte Themen', 'comment_save' => 'Kommentar speichern', 'comment_new' => 'Neuer Kommentar', 'comment_created' => ':createDiff kommentiert', @@ -377,8 +410,14 @@ 'comment_deleted_success' => 'Kommentar gelöscht', 'comment_created_success' => 'Kommentar hinzugefügt', 'comment_updated_success' => 'Kommentar aktualisiert', + 'comment_archive_success' => 'Kommentar archiviert', + 'comment_unarchive_success' => 'Kommentar nicht mehr archiviert', + 'comment_view' => 'Kommentar ansehen', + 'comment_jump_to_thread' => 'Zum Thema springen', 'comment_delete_confirm' => 'Möchtst du diesen Kommentar wirklich löschen?', 'comment_in_reply_to' => 'Antwort auf :commentId', + 'comment_reference' => 'Referenz', + 'comment_reference_outdated' => '(Veraltet)', 'comment_editor_explain' => 'Hier sind die Kommentare, die auf dieser Seite hinterlassen wurden. Kommentare können hinzugefügt und verwaltet werden, wenn die gespeicherte Seite angezeigt wird.', // Revision @@ -398,7 +437,7 @@ 'convert_to_shelf' => 'In Regal umwandeln', 'convert_to_shelf_contents_desc' => 'Du kannst dieses Buch in ein neues Regal mit demselben Inhalt umwandeln. Die in diesem Buch enthaltenen Kapitel werden in neue Bücher umgewandelt. Wenn dieses Buch Seiten enthält, die nicht in einem Kapitel enthalten sind, wird das Buch umbenannt und enthält diese Seiten, und das Buch wird Teil des neuen Regals.', 'convert_to_shelf_permissions_desc' => 'Alle Berechtigungen, die für dieses Buch festgelegt wurden, werden in das neue Regal und in alle neuen untergeordneten Bücher kopiert, für die keine eigenen Berechtigungen festgelegt wurden. Beachte, dass Berechtigungen für Regale nicht automatisch auf den Inhalt übertragen werden, wie es bei Büchern der Fall ist.', - 'convert_book' => 'In Buch umwandeln', + 'convert_book' => 'Buch umwandeln', 'convert_book_confirm' => 'Bist du dir sicher, dass du dieses Buch umwandelt möchtest?', 'convert_undo_warning' => 'Das kann nicht so einfach rückgängig gemacht werden.', 'convert_to_book' => 'In Buch umwandeln', diff --git a/lang/de_informal/errors.php b/lang/de_informal/errors.php index ad7a36d23d9..856f02b48c7 100644 --- a/lang/de_informal/errors.php +++ b/lang/de_informal/errors.php @@ -10,6 +10,7 @@ // Auth 'error_user_exists_different_creds' => 'Ein Benutzer mit der E-Mail-Adresse :email ist bereits mit anderen Anmeldedaten registriert.', + 'auth_pre_register_theme_prevention' => 'Das Benutzerkonto kann mit den bereitgestellten Informationen nicht erstellen werden', 'email_already_confirmed' => 'Die E-Mail-Adresse ist bereits bestätigt. Bitte melde dich an.', 'email_confirmation_invalid' => 'Der Bestätigungslink ist nicht gültig oder wurde bereits verwendet. Bitte registriere dich erneut.', 'email_confirmation_expired' => 'Der Bestätigungslink ist abgelaufen. Es wurde eine neue Bestätigungs-E-Mail gesendet.', @@ -23,7 +24,6 @@ 'saml_invalid_response_id' => 'Die Anfrage vom externen Authentifizierungssystem wird von einem von dieser Anwendung gestarteten Prozess nicht erkannt. Das Zurückblättern nach einem Login könnte dieses Problem verursachen.', 'saml_fail_authed' => 'Anmeldung mit :system fehlgeschlagen, System konnte keine erfolgreiche Autorisierung bereitstellen', 'oidc_already_logged_in' => 'Bereits angemeldet', - 'oidc_user_not_registered' => 'Der Benutzer :name ist nicht registriert und die automatische Registrierung ist deaktiviert', 'oidc_no_email_address' => 'Es konnte keine E-Mail-Adresse für diesen Benutzer in den vom externen Authentifizierungssystem bereitgestellten Daten gefunden werden', 'oidc_fail_authed' => 'Anmeldung mit :system fehlgeschlagen, System hat keine erfolgreiche Autorisierung geliefert', 'social_no_action_defined' => 'Es ist keine Aktion definiert', @@ -37,6 +37,7 @@ 'social_driver_not_found' => 'Treiber für Social-Media-Konten nicht gefunden', 'social_driver_not_configured' => 'Dein :socialAccount-Konto ist nicht korrekt konfiguriert.', 'invite_token_expired' => 'Dieser Einladungslink ist abgelaufen. Du kannst stattdessen versuchen, dein Passwort zurückzusetzen.', + 'login_user_not_found' => 'Ein Benutzer für diese Aktion konnte nicht gefunden werden.', // System 'path_not_writable' => 'Die Datei kann nicht in den angegebenen Pfad :filePath hochgeladen werden. Stelle sicher, dass dieser Ordner auf dem Server beschreibbar ist.', @@ -77,6 +78,7 @@ // Users 'users_cannot_delete_only_admin' => 'Du kannst den einzigen Administrator nicht löschen.', 'users_cannot_delete_guest' => 'Du kannst den Gast-Benutzer nicht löschen', + 'users_could_not_send_invite' => 'Benutzer konnte nicht erstellt werden, da die Einladungs-E-Mail nicht gesendet werden konnte', // Roles 'role_cannot_be_edited' => 'Diese Rolle kann nicht bearbeitet werden.', @@ -103,6 +105,18 @@ 'app_down' => ':appName befindet sich aktuell im Wartungsmodus.', 'back_soon' => 'Wir werden so schnell wie möglich wieder online sein.', + // Import + '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_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.', + 'import_perms_chapters' => 'Dir fehlt die erforderliche Berechtigung, um Kapitel zu erstellen.', + 'import_perms_pages' => 'Dir fehlt die erforderliche Berechtigung, um Seiten zu erstellen.', + 'import_perms_images' => 'Dir fehlt die erforderliche Berechtigung, um Bilder zu erstellen.', + 'import_perms_attachments' => 'Dir fehlt die erforderliche Berechtigung, um Anhänge zu erstellen.', + // API errors 'api_no_authorization_found' => 'Kein Autorisierungs-Token für die Anfrage gefunden', 'api_bad_authorization_format' => 'Ein Autorisierungs-Token wurde auf die Anfrage gefunden, aber das Format schien falsch zu sein', diff --git a/lang/de_informal/passwords.php b/lang/de_informal/passwords.php index e3606760bee..862382379fd 100644 --- a/lang/de_informal/passwords.php +++ b/lang/de_informal/passwords.php @@ -8,7 +8,7 @@ 'password' => 'Passwörter müssen aus mindestens acht Zeichen bestehen und mit der eingegebenen Wiederholung übereinstimmen.', 'user' => "Es wurde kein Benutzer mit dieser E-Mail-Adresse gefunden.", - 'token' => 'Der Token zum Zurücksetzen des Passworts für diese E-Mail-Adresse ist ungültig.', + 'token' => 'Der Token zum Zurücksetzen des Passworts ist für diese E-Mail-Adresse ungültig.', 'sent' => 'Wir haben dir einen Link zum Zurücksetzen des Passwortes per E-Mail geschickt!', 'reset' => 'Dein Passwort wurde zurückgesetzt!', diff --git a/lang/de_informal/preferences.php b/lang/de_informal/preferences.php index a2fc60a14ff..07b83a8da3a 100644 --- a/lang/de_informal/preferences.php +++ b/lang/de_informal/preferences.php @@ -27,7 +27,7 @@ 'notifications_save' => 'Einstellungen speichern', 'notifications_update_success' => 'Benachrichtigungseinstellungen wurden aktualisiert!', 'notifications_watched' => 'Beobachtete und ignorierte Elemente', - 'notifications_watched_desc' => ' Nachfolgend finden Sie die Elemente, für die benutzerdefinierten Überwachungspräferenzen gelten. Um deine Einstellungen für diese Elemente zu aktualisieren, sieh dir das Element an und suche dann die Überwachungsoptionen in der Seitenleiste.', + 'notifications_watched_desc' => 'Nachfolgend finden Sie die Elemente, für die benutzerdefinierten Überwachungspräferenzen gelten. Um deine Einstellungen für diese Elemente zu aktualisieren, sieh dir das Element an und suche dann die Überwachungsoptionen in der Seitenleiste.', 'auth' => 'Zugang & Sicherheit', 'auth_change_password' => 'Passwort ändern', @@ -35,12 +35,12 @@ 'auth_change_password_success' => 'Das Passwort wurde aktualisiert!', 'profile' => 'Profildetails', - 'profile_desc' => 'Verwalte die Details für dein Konto, welche dich gegenüber anderen Benutzern repräsentieren, zusätzlich zu den Details, die für die Kommunikation und die Personalisierung des Systems verwendet werden.', + 'profile_desc' => 'Verwalte die Details deines Kontos, welches dich gegenüber anderen Benutzern repräsentiert, zusätzlich zu den Details, die für die Kommunikation und die Personalisierung des Systems genutzt werden.', 'profile_view_public' => 'Öffentliches Profil anzeigen', - 'profile_name_desc' => 'Konfiguriere deinen Anzeigenamen, der durch die Aktivität, die du durchführst, für andere Benutzer und deine eigenen Inhalte im System sichtbar ist.', + 'profile_name_desc' => 'Konfiguriere deinen Anzeigenamen, der durch die Aktivität, die du ausführst, und die dir gehörenden Inhalte für andere Benutzer sichtbar ist.', 'profile_email_desc' => 'Diese E-Mail wird für Benachrichtigungen und, je nach aktiver Systemauthentifizierung, den Systemzugriff verwendet.', 'profile_email_no_permission' => 'Leider hast du nicht die Berechtigung, deine E-Mail-Adresse zu ändern. Wenn du diese ändern möchtest, wende dich bitte an deinen Administrator.', - 'profile_avatar_desc' => 'Wähle ein Bild aus, dass anderen im System angezeigt wird, um dich zu repräsentieren. Idealerweise sollte dieses Bild quadratisch und etwa 256px breit und hoch sein.', + 'profile_avatar_desc' => 'Wähle ein Bild aus, das anderen im System angezeigt wird, um dich zu repräsentieren. Idealerweise sollte dieses Bild quadratisch und etwa 256px breit und hoch sein.', 'profile_admin_options' => 'Administratoroptionen', 'profile_admin_options_desc' => 'Weitere Administrator-Optionen, wie zum Beispiel die Verwaltung von Rollenzuweisungen, findest du in deinem Benutzerkonto im Bereich "Einstellungen > Benutzer" der Anwendung.', diff --git a/lang/de_informal/settings.php b/lang/de_informal/settings.php index 5cd8982404d..50003c58ee7 100644 --- a/lang/de_informal/settings.php +++ b/lang/de_informal/settings.php @@ -24,12 +24,12 @@ 'app_public_access_toggle' => 'Öffentlichen Zugriff erlauben', 'app_public_viewing' => 'Öffentliche Ansicht erlauben?', 'app_secure_images' => 'Erhöhte Sicherheit für hochgeladene Bilder aktivieren?', - 'app_secure_images_toggle' => 'Aktiviere Bild-Upload mit höherer Sicherheit', - 'app_secure_images_desc' => 'Aus Leistungsgründen sind alle Bilder öffentlich sichtbar. Diese Option fügt zufällige, schwer zu erratene, Zeichenketten zu Bild-URLs hinzu. Stelle sicher, dass Verzeichnisindizes deaktiviert sind, um einen einfachen Zugriff zu verhindern.', + 'app_secure_images_toggle' => 'Höhere Sicherheit für Bild-Uploads aktivieren', + 'app_secure_images_desc' => 'Aus Leistungsgründen sind alle Bilder öffentlich sichtbar. Diese Option fügt zufällige, schwer zu erratende, Zeichenketten zu Bild-URLs hinzu. Stelle sicher, dass Verzeichnisindizes deaktiviert sind, um einen einfachen Zugriff zu verhindern.', 'app_default_editor' => 'Standard Seiteneditor', 'app_default_editor_desc' => 'Wähle aus, welcher Editor bei der Bearbeitung neuer Seiten standardmäßig verwendet werden soll. Dies kann auf Seitenebene außer Kraft gesetzt werden, sofern die Berechtigungen dies zulassen.', - 'app_custom_html' => 'Benutzerdefinierter HTML Inhalt', - 'app_custom_html_desc' => 'Jeder Inhalt, der hier hinzugefügt wird, wird am Ende der Sektion jeder Seite eingefügt. Diese kann praktisch sein, um CSS Styles anzupassen oder Analytics-Code hinzuzufügen.', + 'app_custom_html' => 'Benutzerdefinierter HTML-Head-Inhalt', + 'app_custom_html_desc' => 'Jeder Inhalt, der hier hinzugefügt wird, wird am Ende der -Sektion jeder Seite eingefügt. Diese kann praktisch sein, um CSS-Styles anzupassen oder Analytics-Code hinzuzufügen.', 'app_custom_html_disabled_notice' => 'Benutzerdefinierte HTML-Kopfzeileninhalte sind auf dieser Einstellungsseite deaktiviert, um sicherzustellen, dass alle Änderungen rückgängig gemacht werden können.', 'app_logo' => 'Anwendungslogo', 'app_logo_desc' => 'Dies wird unter anderem in der Kopfzeile der Anwendung verwendet. Dieses Bild sollte 86px hoch sein. Große Bilder werden herunterskaliert.', @@ -75,6 +75,36 @@ Hinweis: Benutzer können ihre E-Mail Adresse nach erfolgreicher Registrierung ändern.', 'reg_confirm_restrict_domain_placeholder' => 'Keine Einschränkung gesetzt', + // Sorting Settings + 'sorting' => 'Sortierung', + 'sorting_book_default' => 'Standard-Buchsortierung', + 'sorting_book_default_desc' => 'Wähle die Standard-Sortierregel aus, die auf neue Bücher angewendet werden soll. Dies wirkt sich nicht auf bestehende Bücher aus und kann pro Buch überschrieben werden.', + 'sorting_rules' => 'Sortierregeln', + 'sorting_rules_desc' => 'Dies sind vordefinierte Sortieraktionen, die auf Inhalte im System angewendet werden können.', + 'sort_rule_assigned_to_x_books' => ':count Buch zugewiesen|:count Büchern zugewiesen', + 'sort_rule_create' => 'Sortierregel erstellen', + 'sort_rule_edit' => 'Sortierregel bearbeiten', + 'sort_rule_delete' => 'Sortierregel löschen', + 'sort_rule_delete_desc' => 'Diese Sortierregel aus dem System entfernen. Bücher mit dieser Sortierung werden auf manuelle Sortierung zurückgesetzt.', + 'sort_rule_delete_warn_books' => 'Diese Sortierregel wird derzeit in :count Bücher(n) verwendet. Bist du sicher, dass du dies löschen möchtest?', + 'sort_rule_delete_warn_default' => 'Diese Sortierregel wird derzeit als Standard für Bücher verwendet. Bist du sicher, dass du dies löschen möchtest?', + 'sort_rule_details' => 'Sortierregel-Details', + 'sort_rule_details_desc' => 'Lege einen Namen für diese Sortierregel fest, der in Listen erscheint, wenn Benutzer eine Sortierung auswählen.', + 'sort_rule_operations' => 'Sortierungs-Aktionen', + 'sort_rule_operations_desc' => 'Konfiguriere die durchzuführenden Sortieraktionen durch Verschieben von der Liste der verfügbaren Aktionen. Bei der Verwendung werden die Aktionen von oben nach unten angewendet. Alle hier vorgenommenen Änderungen werden beim Speichern auf alle zugewiesenen Bücher angewendet.', + 'sort_rule_available_operations' => 'Verfügbare Aktionen', + 'sort_rule_available_operations_empty' => 'Keine verbleibenden Aktionen', + 'sort_rule_configured_operations' => 'Konfigurierte Aktionen', + 'sort_rule_configured_operations_empty' => 'Aktionen aus der Liste "Verfügbare Operationen" ziehen/hinzufügen', + 'sort_rule_op_asc' => '(Aufst.)', + 'sort_rule_op_desc' => '(Abst.)', + 'sort_rule_op_name' => 'Name - Alphabetisch', + 'sort_rule_op_name_numeric' => 'Name - Numerisch', + 'sort_rule_op_created_date' => 'Erstellungsdatum', + 'sort_rule_op_updated_date' => 'Aktualisierungsdatum', + 'sort_rule_op_chapters_first' => 'Kapitel zuerst', + 'sort_rule_op_chapters_last' => 'Kapitel zuletzt', + // Maintenance settings 'maint' => 'Wartung', 'maint_image_cleanup' => 'Bilder bereinigen', @@ -84,11 +114,11 @@ 'maint_image_cleanup_warning' => ':count eventuell unbenutze Bilder wurden gefunden. Möchtest du diese Bilder löschen?', 'maint_image_cleanup_success' => ':count eventuell unbenutze Bilder wurden gefunden und gelöscht.', 'maint_image_cleanup_nothing_found' => 'Keine unbenutzen Bilder gefunden. Nichts zu löschen!', - 'maint_send_test_email' => 'Test E-Mail versenden', - 'maint_send_test_email_desc' => 'Dies sendet eine Test E-Mail an die in deinem Profil angegebene E-Mail-Adresse.', - 'maint_send_test_email_run' => 'Sende eine Test E-Mail', + 'maint_send_test_email' => 'Eine Test-E-Mail versenden', + 'maint_send_test_email_desc' => 'Dies sendet eine Test-E-Mail an die in deinem Profil angegebene E-Mail-Adresse.', + 'maint_send_test_email_run' => 'Test-E-Mail senden', 'maint_send_test_email_success' => 'E-Mail wurde an :address gesendet', - 'maint_send_test_email_mail_subject' => 'Test E-Mail', + 'maint_send_test_email_mail_subject' => 'Test-E-Mail', 'maint_send_test_email_mail_greeting' => 'E-Mail-Versand scheint zu funktionieren!', 'maint_send_test_email_mail_text' => 'Glückwunsch! Da du diese E-Mail Benachrichtigung erhalten hast, scheinen deine E-Mail-Einstellungen korrekt konfiguriert zu sein.', 'maint_recycle_bin_desc' => 'Gelöschte Regale, Bücher, Kapitel & Seiten werden in den Papierkorb verschoben, so dass sie wiederhergestellt oder dauerhaft gelöscht werden können. Ältere Einträge im Papierkorb können, in Abhängigkeit von der Systemkonfiguration, nach einer Weile automatisch entfernt werden.', @@ -110,7 +140,7 @@ 'recycle_bin_contents_empty' => 'Der Papierkorb ist derzeit leer', 'recycle_bin_empty' => 'Papierkorb leeren', 'recycle_bin_empty_confirm' => 'Dies wird alle Einträge im Papierkorb dauerhaft entfernen, einschließlich der Inhalte, die darin enthalten sind. Bist du sicher, dass du den Papierkorb leeren möchtest?', - 'recycle_bin_destroy_confirm' => 'Diese Aktion wird diesen Eintrag zusammen mit allen unten aufgeführten Untereinträgen dauerhaft aus dem System löschen und du wirst nicht in der Lage sein, diesen Inhalt wiederherzustellen. Bist du sicher, dass du diesen Eintrag endgültig löschen möchtest?', + 'recycle_bin_destroy_confirm' => 'Dieser Schritt löscht permanent das Element, gemeinsam mit allen untergeordneten Elementen, aus dem System. Dieser Schritt kann nicht rückgängig gemacht werden. Sind Sie sicher, dass Sie eine dauerhafte Löschung durchführen wollen?', 'recycle_bin_destroy_list' => 'Zu löschende Einträge', 'recycle_bin_restore_list' => 'Wiederherzustellende Einträge', 'recycle_bin_restore_confirm' => 'Mit dieser Aktion wird der gelöschte Eintrag einschließlich aller untergeordneten Einträge an seinem ursprünglichen Ort wiederhergestellt. Wenn der ursprüngliche Ort gelöscht wurde und sich nun im Papierkorb befindet, muss auch der übergeordnete Eintrag wiederhergestellt werden.', @@ -129,7 +159,7 @@ 'audit_table_user' => 'Benutzer', 'audit_table_event' => 'Ereignis', 'audit_table_related' => 'Verknüpfter Eintrag oder Detail', - 'audit_table_ip' => 'IP Adresse', + 'audit_table_ip' => 'IP-Adresse', 'audit_table_date' => 'Aktivitätsdatum', 'audit_date_from' => 'Zeitraum von', 'audit_date_to' => 'Zeitraum bis', @@ -163,6 +193,7 @@ 'role_access_api' => 'Systemzugriffs-API', 'role_manage_settings' => 'Globaleinstellungen verwalten', 'role_export_content' => 'Inhalt exportieren', + 'role_import_content' => 'Inhalt importieren', 'role_editor_change' => 'Seiteneditor ändern', 'role_notifications' => 'Empfangen und Verwalten von Benachrichtigungen', 'role_asset' => 'Berechtigungen', @@ -226,7 +257,7 @@ 'users_api_tokens_expires' => 'Endet', 'users_api_tokens_docs' => 'API Dokumentation', 'users_mfa' => 'Multi-Faktor-Authentifizierung', - 'users_mfa_desc' => 'Richte die Multi-Faktor-Authentifizierung als zusätzliche Sicherheitsstufe für dein Benutzerkonto ein.', + 'users_mfa_desc' => 'Richte Multi-Faktor-Authentifizierung als zusätzliche Sicherheitsstufe für dein Benutzerkonto ein.', 'users_mfa_x_methods' => ':count Methode konfiguriert|:count Methoden konfiguriert', 'users_mfa_configure' => 'Methoden konfigurieren', @@ -277,6 +308,14 @@ 'webhooks_last_errored' => 'Letzter Fehler:', 'webhooks_last_error_message' => 'Letzte Fehlermeldung:', + // Licensing + 'licenses' => 'Lizenzen', + 'licenses_desc' => 'Diese Seite beschreibt Lizenzinformationen für BookStack zusätzlich zu den Projekten und Bibliotheken, die in BookStack verwendet werden. Viele aufgelistete Projekte werden nur in einem Entwicklungskontext verwendet.', + 'licenses_bookstack' => 'BookStack-Lizenz', + 'licenses_php' => 'PHP-Bibliothekslizenzen', + 'licenses_js' => 'JavaScript-Bibliothekslizenzen', + 'licenses_other' => 'Andere Lizenzen', + 'license_details' => 'Lizenzdetails', //! If editing translations files directly please ignore this in all //! languages apart from en. Content will be auto-copied from en. @@ -288,6 +327,7 @@ 'bs' => 'Bosnisch', 'ca' => 'Katalanisch', 'cs' => 'Tschechisch', + 'cy' => 'Cymraeg', 'da' => 'Dänisch', 'de' => 'Deutsch (Sie)', 'de_informal' => 'Deutsch (Du)', @@ -309,6 +349,7 @@ 'lt' => 'Litauisch', 'lv' => 'Lettisch', 'nb' => 'Norwegisch (Bokmål)', + 'ne' => 'नेपाली', 'nn' => 'Nynorsk', 'nl' => 'Niederländisch', 'pl' => 'Polnisch', diff --git a/lang/de_informal/validation.php b/lang/de_informal/validation.php index 61cd7409b4e..f7be5a016c3 100644 --- a/lang/de_informal/validation.php +++ b/lang/de_informal/validation.php @@ -9,7 +9,7 @@ // Standard laravel validation lines 'accepted' => ':attribute muss akzeptiert werden.', - 'active_url' => ':attribute ist keine valide URL.', + 'active_url' => ':attribute ist keine gültige URL.', 'after' => ':attribute muss ein Datum nach :date sein.', 'alpha' => ':attribute kann nur Buchstaben enthalten.', 'alpha_dash' => ':attribute kann nur Buchstaben, Zahlen und Bindestriche enthalten.', @@ -25,12 +25,12 @@ ], 'boolean' => ':attribute Feld muss wahr oder falsch sein.', 'confirmed' => ':attribute stimmt nicht überein.', - 'date' => ':attribute ist kein valides Datum.', + 'date' => ':attribute ist kein gültiges Datum.', 'date_format' => ':attribute entspricht nicht dem Format :format.', 'different' => ':attribute und :other müssen unterschiedlich sein.', 'digits' => ':attribute muss :digits Stellen haben.', 'digits_between' => ':attribute muss zwischen :min und :max Stellen haben.', - 'email' => ':attribute muss eine valide E-Mail-Adresse sein.', + 'email' => ':attribute muss eine gültige E-Mail-Adresse sein.', 'ends_with' => ':attribute muss mit einem der folgenden Werte: :values enden', 'file' => ':attribute muss als gültige Datei angegeben werden.', 'filled' => ':attribute ist erforderlich.', @@ -51,8 +51,8 @@ 'image_extension' => ':attribute muss eine gültige und unterstützte Bild-Dateiendung haben.', 'in' => ':attribute ist ungültig.', 'integer' => ':attribute muss eine Zahl sein.', - 'ip' => ':attribute muss eine valide IP-Adresse sein.', - 'ipv4' => ':attribute muss eine gültige IPv4 Adresse sein.', + 'ip' => ':attribute muss eine gültige IP-Adresse sein.', + 'ipv4' => ':attribute muss eine gültige IPv4-Adresse sein.', 'ipv6' => ':attribute muss eine gültige IPv6-Adresse sein.', 'json' => ':attribute muss ein gültiger JSON-String sein.', 'lt' => [ @@ -75,12 +75,12 @@ ], 'mimes' => ':attribute muss eine Datei vom Typ: :values sein.', 'min' => [ - 'numeric' => ':attribute muss mindestens :min sein', + 'numeric' => ':attribute muss mindestens :min sein.', 'file' => ':attribute muss mindestens :min Kilobyte groß sein.', 'string' => ':attribute muss mindestens :min Zeichen lang sein.', 'array' => ':attribute muss mindesten :min Elemente enthalten.', ], - 'not_in' => ':attribute ist ungültig.', + 'not_in' => 'Das ausgewählte :attribute ist ungültig.', 'not_regex' => ':attribute ist kein gültiges Format.', 'numeric' => ':attribute muss eine Zahl sein.', 'regex' => ':attribute ist in einem ungültigen Format.', @@ -99,12 +99,17 @@ 'array' => ':attribute muss :size Elemente enthalten.', ], 'string' => ':attribute muss eine Zeichenkette sein.', - 'timezone' => ':attribute muss eine valide zeitzone sein.', + 'timezone' => ':attribute muss eine gültige Zeitzone sein.', 'totp' => 'Der angegebene Code ist ungültig oder abgelaufen.', 'unique' => ':attribute wird bereits verwendet.', 'url' => ':attribute ist kein valides Format.', 'uploaded' => 'Die Datei konnte nicht hochgeladen werden. Der Server akzeptiert möglicherweise keine Dateien dieser Größe.', + 'zip_file' => ':attribute muss auf eine Datei innerhalb des ZIP verweisen.', + 'zip_file_mime' => ':attribute muss eine Datei des Typs :validType referenzieren, gefunden :foundType.', + 'zip_model_expected' => 'Datenobjekt erwartet, aber ":type" gefunden.', + 'zip_unique' => ':attribute muss für den Objekttyp innerhalb des ZIP eindeutig sein.', + // Custom validation lines 'custom' => [ 'password-confirm' => [ diff --git a/lang/el/activities.php b/lang/el/activities.php index 3bb66da8b1e..226ef254074 100644 --- a/lang/el/activities.php +++ b/lang/el/activities.php @@ -14,7 +14,7 @@ 'page_delete_notification' => 'Η σελίδα διαγράφηκε επιτυχώς', 'page_restore' => 'αποκατεστημένη σελίδα', 'page_restore_notification' => 'Η σελίδα αποκαταστάθηκε με επιτυχία', - 'page_move' => 'Η σελίδα μετακινήθηκε', + 'page_move' => 'σελίδα που μετακινήθηκε', 'page_move_notification' => 'Η σελίδα μετακινήθηκε με επιτυχία', // Chapters @@ -59,7 +59,7 @@ 'favourite_remove_notification' => '":name" προστέθηκε στα αγαπημένα σας', // Watching - 'watch_update_level_notification' => 'Watch preferences successfully updated', + 'watch_update_level_notification' => 'Οι προτιμήσεις παρακολούθησης ενημερώθηκαν επιτυχώς', // Auth 'auth_login' => 'συνδεδεμένος', @@ -84,6 +84,14 @@ 'webhook_delete' => 'διαγραμμένο webhook', 'webhook_delete_notification' => 'Το Webhook διαγράφηκε επιτυχώς', + // Imports + 'import_create' => 'δημιουργημένη εισαγωγή', + 'import_create_notification' => 'Το εισαγόμενο αρχείο μεταφορτώθηκε επιτυχώς', + 'import_run' => 'ενημερωμένη εισαγωγή', + 'import_run_notification' => 'Το περιεχόμενο εισήχθη επιτυχώς', + 'import_delete' => 'διαγραμμένη εισαγωγή', + 'import_delete_notification' => 'Η εισαγωγή διαγράφηκε επιτυχώς', + // Users 'user_create' => 'δημιουργημένος χρήστης', 'user_create_notification' => 'Ο χρήστης δημιουργήθηκε με επιτυχία', @@ -93,11 +101,11 @@ 'user_delete_notification' => 'Ο Χρήστης αφαιρέθηκε επιτυχώς', // API Tokens - 'api_token_create' => 'δημιουργήθηκε κωδικός API', + 'api_token_create' => 'created API token', 'api_token_create_notification' => 'O κωδικός API δημιουργήθηκε με επιτυχία', - 'api_token_update' => 'κωδικός API ενημερώθηκε', + 'api_token_update' => 'updated API token', 'api_token_update_notification' => 'κωδικός API ενημερώθηκε με επιτυχία', - 'api_token_delete' => 'διαγραμμένο api token', + 'api_token_delete' => 'deleted API token', 'api_token_delete_notification' => 'Το διακριτικό API διαγράφηκε με επιτυχία', // Roles @@ -105,19 +113,27 @@ 'role_create_notification' => 'Ο Ρόλος δημιουργήθηκε με επιτυχία', 'role_update' => 'Ενημέρωση ρόλου', 'role_update_notification' => 'Ο Ρόλος ενημερώθηκε με επιτυχία', - 'role_delete' => 'deleted role', + 'role_delete' => 'διαγραμμένος ρόλος', 'role_delete_notification' => 'Ο Ρόλος διαγράφηκε επιτυχώς', // Recycle Bin - 'recycle_bin_empty' => 'emptied recycle bin', - 'recycle_bin_restore' => 'restored from recycle bin', - 'recycle_bin_destroy' => 'removed from recycle bin', + 'recycle_bin_empty' => 'αδειασμένος κάδος ανακύκλωσης', + 'recycle_bin_restore' => 'αποκαταστάθηκε από τον κάδο ανακύκλωσης', + 'recycle_bin_destroy' => 'αφαιρέθηκε από τον κάδο ανακύκλωσης', // Comments 'commented_on' => 'σχολίασε', - 'comment_create' => 'added comment', - 'comment_update' => 'updated comment', - 'comment_delete' => 'deleted comment', + 'comment_create' => 'προστέθηκε σχόλιο', + 'comment_update' => 'ενημερώθηκε σχόλιο', + 'comment_delete' => 'διαγράφηκε σχόλιο', + + // Sort Rules + 'sort_rule_create' => 'created sort rule', + 'sort_rule_create_notification' => 'Sort rule successfully created', + 'sort_rule_update' => 'updated sort rule', + 'sort_rule_update_notification' => 'Sort rule successfully updated', + 'sort_rule_delete' => 'deleted sort rule', + 'sort_rule_delete_notification' => 'Sort rule successfully deleted', // Other 'permissions_update' => 'ενημερωμένα δικαιώματα', diff --git a/lang/el/auth.php b/lang/el/auth.php index b6a237dab1e..0b94ef859dd 100644 --- a/lang/el/auth.php +++ b/lang/el/auth.php @@ -91,7 +91,7 @@ 'mfa_option_totp_title' => 'Εφαρμογή για κινητό', 'mfa_option_totp_desc' => 'Για να χρησιμοποιήσετε τον έλεγχο ταυτότητας πολλαπλών παραγόντων, θα χρειαστείτε μια εφαρμογή για κινητά που υποστηρίζει TOTP, όπως το Google Authenticator, το Authy ή το Microsoft Authenticator.', 'mfa_option_backup_codes_title' => 'Εφεδρικοί κωδικοί', - 'mfa_option_backup_codes_desc' => 'Αποθηκεύστε με ασφάλεια ένα σύνολο εφεδρικών κωδικών μίας χρήσης τους οποίους μπορείτε να εισαγάγετε για να επαληθεύσετε την ταυτότητά σας.', + 'mfa_option_backup_codes_desc' => 'Δημιουργεί ένα σύνολο εφεδρικών κωδικών μίας χρήσης τους οποίους θα εισάγετε κατά τη σύνδεση σας, για να πιστοποιήσετε την ταυτότητά σας. Φροντίστε να τους αποθηκεύσετε σε ασφαλές σημείο.', 'mfa_gen_confirm_and_enable' => 'Επιβεβαίωση και ενεργοποίηση', 'mfa_gen_backup_codes_title' => 'Ρύθμιση εφεδρικών κωδικών', 'mfa_gen_backup_codes_desc' => 'Αποθηκεύστε την παρακάτω λίστα κωδικών σε ασφαλές μέρος. Κατά την πρόσβαση στο σύστημα, θα μπορείτε να χρησιμοποιήσετε έναν από τους κωδικούς ως δεύτερο μηχανισμό ελέγχου ταυτότητας.', diff --git a/lang/el/common.php b/lang/el/common.php index c9d38d5fd14..39c5aad18fd 100644 --- a/lang/el/common.php +++ b/lang/el/common.php @@ -6,7 +6,7 @@ // Buttons 'cancel' => 'Ακύρωση', - 'close' => 'Close', + 'close' => 'Κλείσιμο', 'confirm' => 'Οκ', 'back' => 'Πίσω', 'save' => 'Αποθήκευση', @@ -20,7 +20,7 @@ 'description' => 'Περιγραφή', 'role' => 'Ρόλος', 'cover_image' => 'Εικόνα εξώφυλλου', - 'cover_image_description' => 'Αυτή η εικόνα πρέπει να είναι περίπου 440x250px.', + 'cover_image_description' => 'Αυτή η εικόνα θα πρέπει να είναι περίπου 440x250px, αν και θα κλιμακωθεί και θα περικοπεί με ευελιξία ώστε να ταιριάζει στη διεπαφή χρήστη σε διαφορετικά σενάρια όπως απαιτείται, επομένως οι πραγματικές διαστάσεις για την εμφάνιση θα διαφέρουν.', // Actions 'actions' => 'Ενέργειες', @@ -30,6 +30,8 @@ 'create' => 'Δημιουργία', 'update' => 'Ενημέρωση', 'edit' => 'Επεξεργασία', + 'archive' => 'Archive', + 'unarchive' => 'Un-Archive', 'sort' => 'Ταξινόμηση', 'move' => 'Μετακίνηση', 'copy' => 'Αντιγραφή', @@ -42,7 +44,7 @@ 'remove' => 'Αφαίρεση', 'add' => 'Προσθήκη', 'configure' => 'Διαμόρφωση', - 'manage' => 'Manage', + 'manage' => 'Διαχείριση', 'fullscreen' => 'Πλήρης οθόνη', 'favourite' => 'Αγαπημένα', 'unfavourite' => 'Αφαίρεση από Αγαπημένα', @@ -52,7 +54,7 @@ 'filter_clear' => 'Διαγραφή φίλτρου', 'download' => 'Λήψη', 'open_in_tab' => 'Άνοιγμα σε Καρτέλα', - 'open' => 'Open', + 'open' => 'Άνοιγμα', // Sort Options 'sort_options' => 'Επιλογές ταξινόμησης', @@ -107,4 +109,7 @@ // Not directly used but available for convenience to users. 'privacy_policy' => 'Πολιτική Απορρήτου', 'terms_of_service' => 'Όροι χρήσης', + + // OpenSearch + 'opensearch_description' => 'Αναζήτηση :appName', ]; diff --git a/lang/el/components.php b/lang/el/components.php index b54a48601e3..9c9d3e8f839 100644 --- a/lang/el/components.php +++ b/lang/el/components.php @@ -6,36 +6,36 @@ // Image Manager 'image_select' => 'Επιλογή εικόνας', - 'image_list' => 'Image List', - 'image_details' => 'Image Details', - 'image_upload' => 'Upload Image', - 'image_intro' => 'Here you can select and manage images that have been previously uploaded to the system.', - 'image_intro_upload' => 'Upload a new image by dragging an image file into this window, or by using the "Upload Image" button above.', + 'image_list' => 'Κατάλογος εικόνων', + 'image_details' => 'Λεπτομέρειες εικόνας', + 'image_upload' => 'Μεταφόρτωση εικόνας', + 'image_intro' => 'Εδώ μπορείτε να επιλέξετε και να διαχειριστείτε εικόνες που έχουν προηγουμένως μεταφορτωθεί στο σύστημα.', + 'image_intro_upload' => 'Μεταφορτώστε μια νέα εικόνα σύροντας ένα αρχείο εικόνας σε αυτό το παράθυρο ή χρησιμοποιώντας το κουμπί "Μεταφόρτωση εικόνας" παραπάνω.', 'image_all' => 'Όλες', 'image_all_title' => 'Δείτε όλες τις εικόνες που υπάρχουν στο Server', 'image_book_title' => 'Προβολή εικόνων που έχουν μεταφορτωθεί σε αυτό το βιβλίο', 'image_page_title' => 'Προβολή εικόνων που έχουν δημοσιευτεί σε αυτήν τη σελίδα', 'image_search_hint' => 'Αναζήτηση με όνομα εικόνας', 'image_uploaded' => 'Μεταφορτώθηκε :uploadedDate', - 'image_uploaded_by' => 'Uploaded by :userName', - 'image_uploaded_to' => 'Uploaded to :pageLink', - 'image_updated' => 'Updated :updateDate', + 'image_uploaded_by' => 'Μεταφορτώθηκε από :userName', + 'image_uploaded_to' => 'Μεταφορτώθηκε στο :pageLink', + 'image_updated' => 'Ενημερώθηκε :updateDate', 'image_load_more' => 'Φόρτωσε περισσότερα', 'image_image_name' => 'Όνομα εικόνας', 'image_delete_used' => 'Αυτή η εικόνα χρησιμοποιείται στις παρακάτω σελίδες.', 'image_delete_confirm_text' => 'Είστε σίγουροι ότι θέλετε να διαγράψετε αυτήν την εικόνα;', 'image_select_image' => 'Επιλέξτε Εικόνα', 'image_dropzone' => 'Σύρτε ή κάντε κλικ εδώ για μεταφόρτωση εικόνων', - 'image_dropzone_drop' => 'Drop images here to upload', + 'image_dropzone_drop' => 'Απόθεση εικόνων εδώ για μεταφόρτωση', 'images_deleted' => 'Οι εικόνες διαγράφηκαν', 'image_preview' => 'Προεπισκόπηση εικόνας', 'image_upload_success' => 'Η εικόνα μεταφορτώθηκε με επιτυχία', 'image_update_success' => 'Τα στοιχεία της εικόνας ενημερώθηκαν με επιτυχία', 'image_delete_success' => 'Η εικόνα διαγράφηκε επιτυχώς', - 'image_replace' => 'Replace Image', - 'image_replace_success' => 'Image file successfully updated', - 'image_rebuild_thumbs' => 'Regenerate Size Variations', - 'image_rebuild_thumbs_success' => 'Image size variations successfully rebuilt!', + 'image_replace' => 'Αντικατάσταση εικόνας', + 'image_replace_success' => 'Το αρχείο εικόνας ενημερώθηκε επιτυχώς', + 'image_rebuild_thumbs' => 'Επαναδημιουργία παραλλαγών μεγέθους', + 'image_rebuild_thumbs_success' => 'Οι παραλλαγές μεγέθους εικόνας ανακατασκευάστηκαν επιτυχώς!', // Code Editor 'code_editor' => 'Επεξεργασία κώδικα', diff --git a/lang/el/editor.php b/lang/el/editor.php index c011a5ac8fb..9952e7fdff4 100644 --- a/lang/el/editor.php +++ b/lang/el/editor.php @@ -13,6 +13,7 @@ 'cancel' => 'Ακύρωση', 'save' => 'Αποθήκευση', 'close' => 'Κλείσιμο', + 'apply' => 'Apply', 'undo' => 'Αναίρεση', 'redo' => 'Επανάληψη', 'left' => 'Αριστερά', @@ -33,7 +34,7 @@ 'header_small' => 'Μικρή κεφαλίδα', 'header_tiny' => 'Μικροσκοπική κεφαλίδα', 'paragraph' => 'Παράγραφος', - 'blockquote' => 'Blockquote', + 'blockquote' => 'Μπλοκ κειμένου παράθεσης', 'inline_code' => 'Ενσωματωμένος κωδικός', 'callouts' => 'Επεξηγήσεις', 'callout_information' => 'Πληροφορίες', @@ -47,6 +48,7 @@ 'superscript' => 'Εκθέτης', 'subscript' => 'Δείκτης', 'text_color' => 'Χρώμα κειμένου', + 'highlight_color' => 'Highlight color', 'custom_color' => 'Προσαρμογή χρώματος', 'remove_color' => 'Αφαίρεση χρώματος', 'background_color' => 'Χρώμα φόντου', @@ -57,7 +59,7 @@ 'list_bullet' => 'Λίστα με κουκκίδες', 'list_numbered' => 'Λίστα με αρίθμηση', 'list_task' => 'Λίστα εργασιών', - 'indent_increase' => 'Αύξηση εσοχήςt', + 'indent_increase' => 'Αύξηση Εσοχής', 'indent_decrease' => 'Μείωση εσοχής', 'table' => 'Πίνακας', 'insert_image' => 'Εισαγωγή εικόνας', @@ -81,6 +83,9 @@ 'table_properties' => 'Ιδιότητες πίνακα', 'table_properties_title' => 'Ιδιότητες πίνακα', 'delete_table' => 'Διαγραφή πίνακα', + 'table_clear_formatting' => 'Εκκαθάριση μορφοποίησης πίνακα', + 'resize_to_contents' => 'Αλλαγή μεγέθους σε περιεχόμενο', + 'row_header' => 'Κεφαλίδα γραμμής', 'insert_row_before' => 'Εισαγωγή γραμμής πάνω', 'insert_row_after' => 'Εισαγωγή γραμμής κάτω', 'delete_row' => 'Διαγραφή γραμμής', @@ -144,6 +149,7 @@ 'url' => 'URL', 'text_to_display' => 'Κείμενο εμφάνισης', 'title' => 'Τίτλος', + 'browse_links' => 'Browse links', 'open_link' => 'Άνοιγμα συνδέσμου', 'open_link_in' => 'Άνοιγμα συνδέσμου σε...', 'open_link_current' => 'Τρέχον παράθυρο', @@ -160,6 +166,8 @@ 'about' => 'Σχετικά', 'about_title' => 'Σχετικά με τον επεξεργαστή WYSIWYG', 'editor_license' => 'Άδεια εκδότη και πνευματικά δικαιώματα', + 'editor_lexical_license' => 'This editor is built as a fork of :lexicalLink which is distributed under the MIT license.', + 'editor_lexical_license_link' => 'Full license details can be found here.', 'editor_tiny_license' => 'Αυτός ο επεξεργαστής έχει δημιουργηθεί χρησιμοποιώντας :tinyLink που παρέχεται με την άδεια MIT.', 'editor_tiny_license_link' => 'Τα πνευματικά δικαιώματα και τα στοιχεία άδειας χρήσης του TinyMCE μπορείτε να τα βρείτε εδώ.', 'save_continue' => 'Αποθήκευση σελίδας & Συνέχεια', diff --git a/lang/el/entities.php b/lang/el/entities.php index 8aabdda0aa1..3779cd18a61 100644 --- a/lang/el/entities.php +++ b/lang/el/entities.php @@ -39,6 +39,34 @@ 'export_pdf' => 'Αρχείο PDF', 'export_text' => 'Αρχείο Απλού κειμένου', 'export_md' => 'Αρχείο Markdown', + 'export_zip' => 'Portable ZIP', + 'default_template' => 'Default Page Template', + 'default_template_explain' => 'Assign a page template that will be used as the default content for all pages created within this item. Keep in mind this will only be used if the page creator has view access to the chosen template page.', + 'default_template_select' => 'Select a template page', + 'import' => 'Import', + 'import_validate' => 'Validate Import', + 'import_desc' => 'Import books, chapters & pages using a portable zip export from the same, or a different, instance. Select a ZIP file to proceed. After the file has been uploaded and validated you\'ll be able to configure & confirm the import in the next view.', + '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_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_size' => ':size Import ZIP Size', + 'import_uploaded_at' => 'Uploaded :relativeTime', + 'import_uploaded_by' => 'Uploaded by', + 'import_location' => 'Import Location', + '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_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', + 'breadcrumb_siblings_for_book' => 'Navigate siblings for book', + 'breadcrumb_siblings_for_bookshelf' => 'Navigate siblings for shelf', // Permissions and restrictions 'permissions' => 'Δικαιώματα', @@ -132,9 +160,6 @@ 'books_edit_named' => 'Επεξεργασία Βιβλίου :bookname', 'books_form_book_name' => 'Όνομα Βιβλίου', 'books_save' => 'Αποθήκευση Βιβλίου', - 'books_default_template' => 'Default Page Template', - 'books_default_template_explain' => 'Assign a page template that will be used as the default content for all new pages in this book. Keep in mind this will only be used if the page creator has view access to those chosen template page.', - 'books_default_template_select' => 'Select a template page', 'books_permissions' => 'Άδειες Βιβλίου', 'books_permissions_updated' => 'Τα Δικαιώματα Βιβλίου Ενημερώθηκαν', 'books_empty_contents' => 'Δεν έχουν δημιουργηθεί σελίδες ή κεφάλαια για αυτό το βιβλίο.', @@ -145,7 +170,9 @@ 'books_search_this' => 'Αναζήτηση σε αυτό το βιβλίο', 'books_navigation' => 'Πλοήγηση Βιβλίου', 'books_sort' => 'Ταξινόμηση Περιεχομένων Βιβλίου', - 'books_sort_desc' => 'Μετακινήστε κεφάλαια και σελίδες μέσα σε ένα βιβλίο για να αναδιοργανώσετε τα περιεχόμενά του. Μπορούν να προστεθούν και άλλα βιβλία, και επιτρέπουν την εύκολη μετακίνηση κεφαλαίων και σελίδων μεταξύ βιβλίων.', + 'books_sort_desc' => 'Move chapters and pages within a book to reorganise its contents. Other books can be added which allows easy moving of chapters and pages between books. Optionally an auto sort rule can be set to automatically sort this book\'s contents upon changes.', + 'books_sort_auto_sort' => 'Auto Sort Option', + 'books_sort_auto_sort_active' => 'Auto Sort Active: :sortName', 'books_sort_named' => 'Ταξινόμηση Βιβλίου :bookname', 'books_sort_name' => 'Ταξινόμηση κατά όνομα', 'books_sort_created' => 'Ταξινόμηση κατά ημερομηνία δημιουργίας', @@ -207,7 +234,7 @@ 'pages_delete_draft' => 'Διαγραφή Προσχέδιας Σελίδας', 'pages_delete_success' => 'Η σελίδα διαγράφηκε', 'pages_delete_draft_success' => 'Η προσχέδια (πρόχειρη) σελίδα διαγράφηκε', - 'pages_delete_warning_template' => 'This page is in active use as a book default page template. These books will no longer have a default page template assigned after this page is deleted.', + 'pages_delete_warning_template' => 'This page is in active use as a book or chapter default page template. These books or chapters will no longer have a default page template assigned after this page is deleted.', 'pages_delete_confirm' => 'Είστε βέβαιοι ότι θέλετε να διαγράψετε αυτή τη σελίδα;', 'pages_delete_draft_confirm' => 'Θέλετε σίγουρα να διαγράψετε την προσχέδια σελίδα;', 'pages_editing_named' => 'Επεξεργασία Σελίδας :pageName', @@ -224,6 +251,8 @@ 'pages_edit_switch_to_markdown_clean' => '(Καθαρισμός Περιεχομένου)', 'pages_edit_switch_to_markdown_stable' => '(Σταθερό Περιεχόμενο)', '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_set_changelog' => 'Ορισμός καταγραφής αλλαγών', 'pages_edit_enter_changelog_desc' => 'Εισάγετε μια σύντομη περιγραφή των αλλαγών που κάνατε', 'pages_edit_enter_changelog' => 'Εισαγωγή Αρχείου Καταγραφής Αλλαγών', @@ -243,6 +272,7 @@ 'pages_md_insert_drawing' => 'Εισαγωγή Σχεδίου', 'pages_md_show_preview' => 'Εμφάνιση προεπισκόπησης', 'pages_md_sync_scroll' => 'Συγχρονισμός προεπισκόπησης', + 'pages_md_plain_editor' => 'Plaintext editor', 'pages_drawing_unsaved' => 'Unsaved Drawing Found', 'pages_drawing_unsaved_confirm' => 'Unsaved drawing data was found from a previous failed drawing save attempt. Would you like to restore and continue editing this unsaved drawing?', 'pages_not_in_chapter' => 'Η σελίδα δεν είναι σε κεφάλαιο', @@ -367,8 +397,11 @@ 'comment' => 'Σχόλιο', 'comments' => 'Σχόλια', 'comment_add' => 'Προσθήκη Σχολίου', + 'comment_none' => 'No comments to display', 'comment_placeholder' => 'Αφήστε ένα σχόλιο εδώ', - 'comment_count' => '{0} Κανένα Σχόλιο |{1} 1 Σχόλιο |[2,*] :count Σχόλια', + 'comment_thread_count' => ':count Comment Thread|:count Comment Threads', + 'comment_archived_count' => ':count Archived', + 'comment_archived_threads' => 'Archived Threads', 'comment_save' => 'Αποθήκευση Σχολίου', 'comment_new' => 'Νέο Σχόλιο', 'comment_created' => 'σχολίασε :createDiff', @@ -377,8 +410,14 @@ '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_delete_confirm' => 'Είστε βέβαιοι ότι θέλετε να διαγράψετε αυτό το σχόλιο;', 'comment_in_reply_to' => 'Σε απάντηση στο :commentId', + 'comment_reference' => 'Reference', + 'comment_reference_outdated' => '(Outdated)', 'comment_editor_explain' => 'Here are the comments that have been left on this page. Comments can be added & managed when viewing the saved page.', // Revision diff --git a/lang/el/errors.php b/lang/el/errors.php index efb20b1b660..cdfa2155f13 100644 --- a/lang/el/errors.php +++ b/lang/el/errors.php @@ -10,6 +10,7 @@ // Auth 'error_user_exists_different_creds' => 'Ένας χρήστης με email :email υπάρχει ήδη αλλά με διαφορετικά διαπιστευτήρια.', + 'auth_pre_register_theme_prevention' => 'User account could not be registered for the provided details', 'email_already_confirmed' => 'Το email έχει ήδη επιβεβαιωθεί, Δοκιμάστε να συνδεθείτε.', 'email_confirmation_invalid' => 'Αυτό το διακριτικό επιβεβαίωσης δεν είναι έγκυρο ή έχει ήδη χρησιμοποιηθεί, Παρακαλώ δοκιμάστε να εγγραφείτε ξανά.', 'email_confirmation_expired' => 'Το διακριτικό επιβεβαίωσης έχει λήξει, έχει σταλεί ένα νέο email επιβεβαίωσης.', @@ -23,7 +24,6 @@ 'saml_invalid_response_id' => 'Το αίτημα από το εξωτερικό σύστημα ελέγχου ταυτότητας δεν αναγνωρίζεται από μια διαδικασία που ξεκίνησε από αυτή την εφαρμογή. Η πλοήγηση πίσω μετά από μια σύνδεση θα μπορούσε να προκαλέσει αυτό το ζήτημα.', 'saml_fail_authed' => 'Η σύνδεση με τη χρήση :system απέτυχε, το σύστημα δεν παρείχε επιτυχή εξουσιοδότηση', 'oidc_already_logged_in' => 'Ήδη συνδεδεμένος', - 'oidc_user_not_registered' => 'Ο χρήστης :name δεν είναι εγγεγραμμένος και η αυτόματη εγγραφή είναι απενεργοποιημένη', 'oidc_no_email_address' => 'Δεν ήταν δυνατή η εύρεση μιας διεύθυνσης ηλεκτρονικού ταχυδρομείου, για αυτόν τον χρήστη, στα δεδομένα που παρέχονται από το εξωτερικό σύστημα ελέγχου ταυτότητας', 'oidc_fail_authed' => 'Η σύνδεση με τη χρήση :system απέτυχε, το σύστημα δεν παρείχε επιτυχή εξουσιοδότηση', 'social_no_action_defined' => 'Καμία ενέργεια δεν ορίστηκε', @@ -37,6 +37,7 @@ 'social_driver_not_found' => 'Δεν βρέθηκε κοινωνικός οδηγός', 'social_driver_not_configured' => 'Οι κοινωνικές ρυθμίσεις του :socialAccount δεν έχουν ρυθμιστεί σωστά.', 'invite_token_expired' => 'Αυτός ο σύνδεσμος πρόσκλησης έχει λήξει. Αντ\' αυτού μπορείτε να προσπαθήσετε να επαναφέρετε τον κωδικό πρόσβασής σας.', + 'login_user_not_found' => 'A user for this action could not be found.', // System 'path_not_writable' => 'Η διαδρομή αρχείου :filePath δεν μπόρεσε να μεταφορτωθεί. Βεβαιωθείτε ότι είναι εγγράψιμη στο διακομιστή.', @@ -77,6 +78,7 @@ // Users 'users_cannot_delete_only_admin' => 'Δεν μπορείτε να διαγράψετε τον μοναδικό διαχειριστή', 'users_cannot_delete_guest' => 'Δεν μπορείτε να διαγράψετε τον επισκέπτη', + 'users_could_not_send_invite' => 'Could not create user since invite email failed to send', // Roles 'role_cannot_be_edited' => 'Αυτός ο ρόλος δεν μπορεί να επεξεργαστεί', @@ -103,6 +105,18 @@ 'app_down' => ':appName είναι προσωρινά μη διαθέσιμη', 'back_soon' => 'Θα υπάρξει σύντομα υποστήριξη.', + // Import + 'import_zip_cant_read' => 'Could not read ZIP file.', + 'import_zip_cant_decode_data' => 'Could not find and decode ZIP data.json content.', + 'import_zip_no_data' => 'ZIP file data has no expected book, chapter or page content.', + 'import_validation_failed' => 'Import ZIP failed to validate with errors:', + 'import_zip_failed_notification' => 'Failed to import ZIP file.', + 'import_perms_books' => 'You are lacking the required permissions to create books.', + 'import_perms_chapters' => 'You are lacking the required permissions to create chapters.', + 'import_perms_pages' => 'You are lacking the required permissions to create pages.', + 'import_perms_images' => 'You are lacking the required permissions to create images.', + 'import_perms_attachments' => 'You are lacking the required permission to create attachments.', + // API errors 'api_no_authorization_found' => 'Δεν βρέθηκε διακριτικό εξουσιοδότησης κατόπιν αιτήματος', 'api_bad_authorization_format' => 'Ένα διακριτικό εξουσιοδότησης βρέθηκε κατόπιν αιτήματος, αλλά η μορφή εμφανίστηκε εσφαλμένη', diff --git a/lang/el/notifications.php b/lang/el/notifications.php index 1afd23f1dc4..3924cab3b5c 100644 --- a/lang/el/notifications.php +++ b/lang/el/notifications.php @@ -4,24 +4,24 @@ */ return [ - 'new_comment_subject' => 'New comment on page: :pageName', - 'new_comment_intro' => 'A user has commented on a page in :appName:', - 'new_page_subject' => 'New page: :pageName', - 'new_page_intro' => 'A new page has been created in :appName:', - 'updated_page_subject' => 'Updated page: :pageName', - 'updated_page_intro' => 'A page has been updated in :appName:', - 'updated_page_debounce' => 'To prevent a mass of notifications, for a while you won\'t be sent notifications for further edits to this page by the same editor.', + 'new_comment_subject' => 'Νέο σχόλιο στη σελίδα: :pageName', + 'new_comment_intro' => 'Ένας χρήστης έχει σχολιάσει σε μια σελίδα στο :appName:', + 'new_page_subject' => 'Νέα σελίδα: :pageName', + 'new_page_intro' => 'Μια νέα σελίδα έχει δημιουργηθεί στο :appName:', + 'updated_page_subject' => 'Ενημερωμένη σελίδα: :pageName', + 'updated_page_intro' => 'Μια σελίδα έχει ενημερωθεί στο :appName:', + 'updated_page_debounce' => 'Για να αποτρέψετε μαζικές ειδοποιήσεις, για κάποιο διάστημα δε θα σας αποστέλλονται ειδοποιήσεις για περαιτέρω αλλαγές σε αυτήν τη σελίδα από τον ίδιο συντάκτη.', - 'detail_page_name' => 'Page Name:', - 'detail_page_path' => 'Page Path:', - 'detail_commenter' => 'Commenter:', - 'detail_comment' => 'Comment:', - 'detail_created_by' => 'Created By:', - 'detail_updated_by' => 'Updated By:', + 'detail_page_name' => 'Όνομα σελίδας:', + 'detail_page_path' => 'Διαδρομή σελίδας:', + 'detail_commenter' => 'Σχολιαστής:', + 'detail_comment' => 'Σχόλιο:', + 'detail_created_by' => 'Δημιουργήθηκε από:', + 'detail_updated_by' => 'Ενημερώθηκε από:', - 'action_view_comment' => 'View Comment', - 'action_view_page' => 'View Page', + 'action_view_comment' => 'Προβολή σχολίου', + 'action_view_page' => 'Προβολή σελίδας', - 'footer_reason' => 'This notification was sent to you because :link cover this type of activity for this item.', - 'footer_reason_link' => 'your notification preferences', + 'footer_reason' => 'Αυτή η ειδοποίηση εστάλη σε εσάς επειδή το :link καλύπτει αυτόν τον τύπο δραστηριότητας για αυτό το αντικείμενο.', + 'footer_reason_link' => 'προτιμήσεις σας για ειδοποιήσεις', ]; diff --git a/lang/el/preferences.php b/lang/el/preferences.php index a06aff54718..4f7e97d5257 100644 --- a/lang/el/preferences.php +++ b/lang/el/preferences.php @@ -27,7 +27,7 @@ 'notifications_save' => 'Save Preferences', 'notifications_update_success' => 'Notification preferences have been updated!', 'notifications_watched' => 'Watched & Ignored Items', - 'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.', + 'notifications_watched_desc' => 'Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.', 'auth' => 'Access & Security', 'auth_change_password' => 'Change Password', diff --git a/lang/el/settings.php b/lang/el/settings.php index 0c3497e64ef..172038fd014 100644 --- a/lang/el/settings.php +++ b/lang/el/settings.php @@ -74,6 +74,36 @@ 'reg_confirm_restrict_domain_desc' => 'Εισαγάγετε μια λίστα διαχωρισμένων με κόμματα τομέων email στους οποίους θέλετε να περιορίσετε την εγγραφή. Θα σταλεί στους χρήστες ένα email για να επιβεβαιώσουν τη διεύθυνσή τους πριν τους επιτραπεί να αλληλεπιδράσουν με την εφαρμογή.
Σημειώστε ότι οι χρήστες θα μπορούν να αλλάξουν τις διευθύνσεις email τους μετά την επιτυχή εγγραφή.', 'reg_confirm_restrict_domain_placeholder' => 'Δε έχουν ρυθμιστεί περιορισμοί ακόμα', + // Sorting Settings + 'sorting' => 'Sorting', + 'sorting_book_default' => 'Default Book Sort', + 'sorting_book_default_desc' => 'Select the default sort rule to apply to new books. This won\'t affect existing books, and can be overridden per-book.', + 'sorting_rules' => 'Sort Rules', + 'sorting_rules_desc' => 'These are predefined sorting operations which can be applied to content in the system.', + 'sort_rule_assigned_to_x_books' => 'Assigned to :count Book|Assigned to :count Books', + 'sort_rule_create' => 'Create Sort Rule', + 'sort_rule_edit' => 'Edit Sort Rule', + 'sort_rule_delete' => 'Delete Sort Rule', + 'sort_rule_delete_desc' => 'Remove this sort rule from the system. Books using this sort will revert to manual sorting.', + 'sort_rule_delete_warn_books' => 'This sort rule is currently used on :count book(s). Are you sure you want to delete this?', + 'sort_rule_delete_warn_default' => 'This sort rule is currently used as the default for books. Are you sure you want to delete this?', + 'sort_rule_details' => 'Sort Rule Details', + 'sort_rule_details_desc' => 'Set a name for this sort rule, which will appear in lists when users are selecting a sort.', + 'sort_rule_operations' => 'Sort Operations', + 'sort_rule_operations_desc' => 'Configure the sort actions to be performed by moving them from the list of available operations. Upon use, the operations will be applied in order, from top to bottom. Any changes made here will be applied to all assigned books upon save.', + 'sort_rule_available_operations' => 'Available Operations', + 'sort_rule_available_operations_empty' => 'No operations remaining', + 'sort_rule_configured_operations' => 'Configured Operations', + 'sort_rule_configured_operations_empty' => 'Drag/add operations from the "Available Operations" list', + 'sort_rule_op_asc' => '(Asc)', + 'sort_rule_op_desc' => '(Desc)', + 'sort_rule_op_name' => 'Name - Alphabetical', + 'sort_rule_op_name_numeric' => '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_last' => 'Chapters Last', + // Maintenance settings 'maint' => 'Συντήρηση', 'maint_image_cleanup' => 'Εκκαθάριση Εικόνων', @@ -109,7 +139,7 @@ 'recycle_bin_contents_empty' => 'Ο κάδος ανακύκλωσης είναι επί του παρόντος άδειος', 'recycle_bin_empty' => 'Αδειάστε τον Κάδο Ανακύκλωσης', 'recycle_bin_empty_confirm' => 'Αυτό θα καταστρέψει οριστικά όλα τα αντικείμενα στον κάδο ανακύκλωσης, συμπεριλαμβανομένου του περιεχομένου που περιέχεται σε κάθε αντικείμενο. Είστε βέβαιοι ότι θέλετε να αδειάσετε τον κάδο ανακύκλωσης;', - 'recycle_bin_destroy_confirm' => 'Αυτή η ενέργεια θα διαγράψει οριστικά από το σύστημα αυτό το στοιχείο μαζί με τυχόν θυγατρικά, που αναφέρονται παρακάτω. Μετά την επιβεβαίωση της διαγραφής δεν θα μπορείτε να επαναφέρετε αυτό το περιεχόμενο. Είστε βέβαιοι ότι θέλετε να διαγράψετε οριστικά αυτό το στοιχείο;', + 'recycle_bin_destroy_confirm' => 'Αυτή η ενέργεια θα διαγράψει οριστικά από το σύστημα αυτό το στοιχείο μαζί με τυχόν θυγατρικά, που αναφέρονται παρακάτω. Μετά την επιβεβαίωση της διαγραφής δε θα μπορείτε να επαναφέρετε αυτό το περιεχόμενο. Είστε βέβαιοι ότι θέλετε να διαγράψετε οριστικά αυτό το στοιχείο;', 'recycle_bin_destroy_list' => 'Αντικείμενα για καταστροφή', 'recycle_bin_restore_list' => 'Αντικείμενα για επαναφορά', 'recycle_bin_restore_confirm' => 'Αυτή η ενέργεια θα επαναφέρει το διαγραμμένο στοιχείο, συμπεριλαμβανομένων τυχόν θυγατρικών στοιχείων, στην αρχική τους θέση. Εάν η αρχική τοποθεσία έχει από τότε διαγραφεί και βρίσκεται τώρα στον κάδο ανακύκλωσης, θα πρέπει επίσης να αποκατασταθεί και το γονικό στοιχείο.', @@ -162,8 +192,9 @@ 'role_access_api' => 'Πρόσβαση στο API του συστήματος', 'role_manage_settings' => 'Διαχειριστείτε τις ρυθμίσεις του ΑΡΙ', 'role_export_content' => 'Εξαγωγή περιεχομένου', + 'role_import_content' => 'Εισαγωγή περιεχομένου', 'role_editor_change' => 'Αλλαγή προγράμματος επεξεργασίας σελίδας', - 'role_notifications' => 'Receive & manage notifications', + 'role_notifications' => 'Λήψη & διαχείριση ειδοποιήσεων', 'role_asset' => 'Δικαιώματα Συστήματος', 'roles_system_warning' => 'Λάβετε υπόψη ότι η πρόσβαση σε οποιοδήποτε από τις τρεις παραπάνω άδειες (δικαιώματα) μπορεί να επιτρέψει σε έναν χρήστη να αλλάξει τα δικά του προνόμια ή τα προνόμια άλλων στο σύστημα. Εκχωρήστε ρόλους με αυτά τα δικαιώματα μόνο σε αξιόπιστους χρήστες.', 'role_asset_desc' => 'Αυτά τα δικαιώματα ελέγχουν την προεπιλεγμένη πρόσβαση στα στοιχεία (άδειες) εντός του συστήματος. Τα δικαιώματα σε Βιβλία, Κεφάλαια και Σελίδες θα παρακάμψουν αυτές τις άδειες.', @@ -193,8 +224,8 @@ 'users_send_invite_text' => 'Μπορείτε να επιλέξετε να στείλετε σε αυτόν τον χρήστη ένα email πρόσκλησης που του επιτρέπει να ορίσει τον δικό του κωδικό πρόσβασης. Σε διαφορετική περίπτωση μπορείτε να ορίσετε τον κωδικό πρόσβασής του εσείς.', 'users_send_invite_option' => 'Αποστολή email πρόσκλησης σε χρήστη', 'users_external_auth_id' => 'Εξωτερικός έλεγχος ταυτότητας', - 'users_external_auth_id_desc' => 'When an external authentication system is in use (such as SAML2, OIDC or LDAP) this is the ID which links this BookStack user to the authentication system account. You can ignore this field if using the default email-based authentication.', - 'users_password_warning' => 'Only fill the below if you would like to change the password for this user.', + 'users_external_auth_id_desc' => 'Όταν χρησιμοποιείται ένα εξωτερικό σύστημα ελέγχου ταυτότητας (όπως SAML2, OIDC ή LDAP) αυτό είναι το αναγνωριστικό που συνδέει αυτόν τον χρήστη BookStack με τον λογαριασμό συστήματος ελέγχου ταυτότητας. Μπορείτε να αγνοήσετε αυτό το πεδίο αν χρησιμοποιείτε τον προεπιλεγμένο έλεγχο ταυτότητας μέσω email.', + 'users_password_warning' => 'Συμπληρώστε τα παρακάτω μόνο αν θέλετε να αλλάξετε τον κωδικό πρόσβασης για αυτόν το χρήστη.', 'users_system_public' => 'Αυτός ο χρήστης αντιπροσωπεύει οποιονδήποτε επισκέπτη που επισκέπτεται τη Βιβλιοθήκη σας. Δεν μπορεί να χρησιμοποιηθεί για τη σύνδεση αλλά εκχωρείται αυτόματα.', 'users_delete' => 'Διαγραφή Χρήστη', 'users_delete_named' => 'Διαγραφή χρήστη :userName', @@ -210,16 +241,16 @@ 'users_preferred_language' => 'Προτιμώμενη γλώσσα', 'users_preferred_language_desc' => 'Αυτή η επιλογή θα αλλάξει τη γλώσσα που χρησιμοποιείται για τη διεπαφή χρήστη της εφαρμογής. Αυτό δεν θα επηρεάσει οποιοδήποτε περιεχόμενο που δημιουργήθηκε από χρήστες.', 'users_social_accounts' => 'Λογαριασμοί Κοινωνικής δικτύωσης ', - 'users_social_accounts_desc' => 'View the status of the connected social accounts for this user. Social accounts can be used in addition to the primary authentication system for system access.', + 'users_social_accounts_desc' => 'Δείτε την κατάσταση των συνδεδεμένων λογαριασμών κοινωνικών δικτύων για αυτόν το χρήστη. Οι λογαριασμοί κοινωνικών δικτύων, μπορούν να χρησιμοποιηθούν επιπλέον του κύριου συστήματος ελέγχου ταυτότητας, για πρόσβαση στο σύστημα.', 'users_social_accounts_info' => 'Εδώ μπορείτε να συνδέσετε τους άλλους λογαριασμούς σας για ταχύτερη και ευκολότερη σύνδεση. Η αποσύνδεση ενός λογαριασμού εδώ δεν ανακαλεί προηγουμένως εξουσιοδοτημένη πρόσβαση. Ανάκληση πρόσβασης από τις ρυθμίσεις προφίλ σας στον συνδεδεμένο κοινωνικό λογαριασμό.', 'users_social_connect' => 'Σύνδεση λογαριασμού', 'users_social_disconnect' => 'Αποσύνδεση λογαριασμού', - 'users_social_status_connected' => 'Connected', - 'users_social_status_disconnected' => 'Disconnected', + 'users_social_status_connected' => 'Συνδεδεμένο', + 'users_social_status_disconnected' => 'Αποσυνδεδεμένο', 'users_social_connected' => ':socialΛογαριασμός λογαριασμού συνδέθηκε με επιτυχία στο προφίλ σας.', 'users_social_disconnected' => ':socialΛογαριασμός αποσυνδέθηκε επιτυχώς από το προφίλ σας.', 'users_api_tokens' => 'API Tokens', - 'users_api_tokens_desc' => 'Create and manage the access tokens used to authenticate with the BookStack REST API. Permissions for the API are managed via the user that the token belongs to.', + 'users_api_tokens_desc' => 'Δημιουργία και διαχείριση των διακριτικών πρόσβασης (token) που χρησιμοποιούνται για τον έλεγχο ταυτότητας με το REST API του BookStack. Τα δικαιώματα για το API τα διαχειρίζεται ο χρήστης στον οποίο ανήκει το διακριτικό (token).', 'users_api_tokens_none' => 'Δεν έχουν δημιουργηθεί διακριτικά API για αυτόν το χρήστη', 'users_api_tokens_create' => 'Δημιουργία διακριτικού Api Token', 'users_api_tokens_expires' => 'Λήγει', @@ -276,6 +307,14 @@ 'webhooks_last_errored' => 'Τελευταίο σφάλμα:', 'webhooks_last_error_message' => 'Τελευταίο μήνυμα λάθους:', + // Licensing + 'licenses' => 'Άδειες', + 'licenses_desc' => 'Αυτή η σελίδα αναφέρει λεπτομερώς τις πληροφορίες άδειας χρήσης για το BookStack επιπρόσθετα στα έργα και τις βιβλιοθήκες που χρησιμοποιούνται εντός του BookStack. Πολλά έργα που αναφέρονται μπορούν να χρησιμοποιηθούν μόνο σε ένα πλαίσιο ανάπτυξης.', + 'licenses_bookstack' => 'Άδεια BookStack', + 'licenses_php' => 'Άδειες Βιβλιοθήκης PHP', + 'licenses_js' => 'Άδειες Βιβλιοθήκης JavaScript', + 'licenses_other' => 'Άλλες άδειες', + 'license_details' => 'Λεπτομέρειες άδειας', //! If editing translations files directly please ignore this in all //! languages apart from en. Content will be auto-copied from en. @@ -287,6 +326,7 @@ 'bs' => 'Bosanski', 'ca' => 'Català', 'cs' => 'Česky', + 'cy' => 'Cymraeg', 'da' => 'Dansk', 'de' => 'Deutsch (Sie)', 'de_informal' => 'Deutsch (Du)', @@ -308,6 +348,7 @@ 'lt' => 'Lietuvių Kalba', 'lv' => 'Latviešu Valoda', 'nb' => 'Norsk (Bokmål)', + 'ne' => 'नेपाली', 'nn' => 'Nynorsk', 'nl' => 'Nederlands', 'pl' => 'Polski', diff --git a/lang/el/validation.php b/lang/el/validation.php index be96cd0caf6..12d4919cab2 100644 --- a/lang/el/validation.php +++ b/lang/el/validation.php @@ -105,6 +105,11 @@ 'url' => 'Η μορφή του :attribute δεν είναι έγκυρη.', 'uploaded' => 'Δεν ήταν δυνατή η αποστολή του αρχείου. Ο διακομιστής ενδέχεται να μην δέχεται αρχεία αυτού του μεγέθους.', + 'zip_file' => 'Το :attribute πρέπει να παραπέμπει σε ένα αρχείο εντός του ZIP.', + 'zip_file_mime' => 'Το :attribute πρέπει να αναφέρεται σε αρχείο τύπου :validTypes, βρέθηκε :foundType.', + 'zip_model_expected' => 'Αναμενόταν αντικείμενο δεδομένων, αλλά ":type" βρέθηκε.', + 'zip_unique' => 'Το :attribute πρέπει να είναι μοναδικό για τον τύπο αντικειμένου εντός του ZIP.', + // Custom validation lines 'custom' => [ 'password-confirm' => [ diff --git a/lang/en/activities.php b/lang/en/activities.php index d5b55c03dcb..4362fc02958 100644 --- a/lang/en/activities.php +++ b/lang/en/activities.php @@ -84,6 +84,14 @@ 'webhook_delete' => 'deleted webhook', 'webhook_delete_notification' => 'Webhook successfully deleted', + // Imports + 'import_create' => 'created import', + 'import_create_notification' => 'Import successfully uploaded', + 'import_run' => 'updated import', + 'import_run_notification' => 'Content successfully imported', + 'import_delete' => 'deleted import', + 'import_delete_notification' => 'Import successfully deleted', + // Users 'user_create' => 'created user', 'user_create_notification' => 'User successfully created', @@ -93,11 +101,11 @@ 'user_delete_notification' => 'User successfully removed', // API Tokens - 'api_token_create' => 'created api token', + 'api_token_create' => 'created API token', 'api_token_create_notification' => 'API token successfully created', - 'api_token_update' => 'updated api token', + 'api_token_update' => 'updated API token', 'api_token_update_notification' => 'API token successfully updated', - 'api_token_delete' => 'deleted api token', + 'api_token_delete' => 'deleted API token', 'api_token_delete_notification' => 'API token successfully deleted', // Roles @@ -119,6 +127,14 @@ 'comment_update' => 'updated comment', 'comment_delete' => 'deleted comment', + // Sort Rules + 'sort_rule_create' => 'created sort rule', + 'sort_rule_create_notification' => 'Sort rule successfully created', + 'sort_rule_update' => 'updated sort rule', + 'sort_rule_update_notification' => 'Sort rule successfully updated', + 'sort_rule_delete' => 'deleted sort rule', + 'sort_rule_delete_notification' => 'Sort rule successfully deleted', + // Other 'permissions_update' => 'updated permissions', ]; diff --git a/lang/en/auth.php b/lang/en/auth.php index dc4b242a09e..57f0cb5c632 100644 --- a/lang/en/auth.php +++ b/lang/en/auth.php @@ -91,7 +91,7 @@ 'mfa_option_totp_title' => 'Mobile App', 'mfa_option_totp_desc' => 'To use multi-factor authentication you\'ll need a mobile application that supports TOTP such as Google Authenticator, Authy or Microsoft Authenticator.', 'mfa_option_backup_codes_title' => 'Backup Codes', - 'mfa_option_backup_codes_desc' => 'Securely store a set of one-time-use backup codes which you can enter to verify your identity.', + 'mfa_option_backup_codes_desc' => 'Generates a set of one-time-use backup codes which you\'ll enter on login to verify your identity. Make sure to store these in a safe & secure place.', 'mfa_gen_confirm_and_enable' => 'Confirm and Enable', 'mfa_gen_backup_codes_title' => 'Backup Codes Setup', 'mfa_gen_backup_codes_desc' => 'Store the below list of codes in a safe place. When accessing the system you\'ll be able to use one of the codes as a second authentication mechanism.', diff --git a/lang/en/common.php b/lang/en/common.php index 27037babec0..06a9e855ce3 100644 --- a/lang/en/common.php +++ b/lang/en/common.php @@ -20,7 +20,7 @@ 'description' => 'Description', 'role' => 'Role', 'cover_image' => 'Cover image', - 'cover_image_description' => 'This image should be approx 440x250px.', + 'cover_image_description' => 'This image should be approximately 440x250px although it will be flexibly scaled & cropped to fit the user interface in different scenarios as required, so actual dimensions for display will differ.', // Actions 'actions' => 'Actions', @@ -30,6 +30,8 @@ 'create' => 'Create', 'update' => 'Update', 'edit' => 'Edit', + 'archive' => 'Archive', + 'unarchive' => 'Un-Archive', 'sort' => 'Sort', 'move' => 'Move', 'copy' => 'Copy', @@ -107,4 +109,7 @@ // Not directly used but available for convenience to users. 'privacy_policy' => 'Privacy Policy', 'terms_of_service' => 'Terms of Service', + + // OpenSearch + 'opensearch_description' => 'Search :appName', ]; diff --git a/lang/en/editor.php b/lang/en/editor.php index 670c1c5e1ac..0d250e9a7bd 100644 --- a/lang/en/editor.php +++ b/lang/en/editor.php @@ -13,6 +13,7 @@ 'cancel' => 'Cancel', 'save' => 'Save', 'close' => 'Close', + 'apply' => 'Apply', 'undo' => 'Undo', 'redo' => 'Redo', 'left' => 'Left', @@ -47,6 +48,7 @@ 'superscript' => 'Superscript', 'subscript' => 'Subscript', 'text_color' => 'Text color', + 'highlight_color' => 'Highlight color', 'custom_color' => 'Custom color', 'remove_color' => 'Remove color', 'background_color' => 'Background color', @@ -81,6 +83,9 @@ 'table_properties' => 'Table properties', 'table_properties_title' => 'Table Properties', 'delete_table' => 'Delete table', + 'table_clear_formatting' => 'Clear table formatting', + 'resize_to_contents' => 'Resize to contents', + 'row_header' => 'Row header', 'insert_row_before' => 'Insert row before', 'insert_row_after' => 'Insert row after', 'delete_row' => 'Delete row', @@ -144,6 +149,7 @@ 'url' => 'URL', 'text_to_display' => 'Text to display', 'title' => 'Title', + 'browse_links' => 'Browse links', 'open_link' => 'Open link', 'open_link_in' => 'Open link in...', 'open_link_current' => 'Current window', @@ -160,6 +166,8 @@ 'about' => 'About the editor', 'about_title' => 'About the WYSIWYG Editor', 'editor_license' => 'Editor License & Copyright', + 'editor_lexical_license' => 'This editor is built as a fork of :lexicalLink which is distributed under the MIT license.', + 'editor_lexical_license_link' => 'Full license details can be found here.', 'editor_tiny_license' => 'This editor is built using :tinyLink which is provided under the MIT license.', 'editor_tiny_license_link' => 'The copyright and license details of TinyMCE can be found here.', 'save_continue' => 'Save Page & Continue', diff --git a/lang/en/entities.php b/lang/en/entities.php index f1f915544d1..74c50be3b2f 100644 --- a/lang/en/entities.php +++ b/lang/en/entities.php @@ -39,6 +39,34 @@ 'export_pdf' => 'PDF File', 'export_text' => 'Plain Text File', 'export_md' => 'Markdown File', + 'export_zip' => 'Portable ZIP', + 'default_template' => 'Default Page Template', + 'default_template_explain' => 'Assign a page template that will be used as the default content for all pages created within this item. Keep in mind this will only be used if the page creator has view access to the chosen template page.', + 'default_template_select' => 'Select a template page', + 'import' => 'Import', + 'import_validate' => 'Validate Import', + 'import_desc' => 'Import books, chapters & pages using a portable zip export from the same, or a different, instance. Select a ZIP file to proceed. After the file has been uploaded and validated you\'ll be able to configure & confirm the import in the next view.', + '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_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_size' => ':size Import ZIP Size', + 'import_uploaded_at' => 'Uploaded :relativeTime', + 'import_uploaded_by' => 'Uploaded by', + 'import_location' => 'Import Location', + '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_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', + 'breadcrumb_siblings_for_book' => 'Navigate siblings for book', + 'breadcrumb_siblings_for_bookshelf' => 'Navigate siblings for shelf', // Permissions and restrictions 'permissions' => 'Permissions', @@ -132,9 +160,6 @@ 'books_edit_named' => 'Edit Book :bookName', 'books_form_book_name' => 'Book Name', 'books_save' => 'Save Book', - 'books_default_template' => 'Default Page Template', - 'books_default_template_explain' => 'Assign a page template that will be used as the default content for all new pages in this book. Keep in mind this will only be used if the page creator has view access to those chosen template page.', - 'books_default_template_select' => 'Select a template page', 'books_permissions' => 'Book Permissions', 'books_permissions_updated' => 'Book Permissions Updated', 'books_empty_contents' => 'No pages or chapters have been created for this book.', @@ -145,7 +170,9 @@ 'books_search_this' => 'Search this book', 'books_navigation' => 'Book Navigation', 'books_sort' => 'Sort Book Contents', - 'books_sort_desc' => 'Move chapters and pages within a book to reorganise its contents. Other books can be added which allows easy moving of chapters and pages between books.', + 'books_sort_desc' => 'Move chapters and pages within a book to reorganise its contents. Other books can be added which allows easy moving of chapters and pages between books. Optionally an auto sort rule can be set to automatically sort this book\'s contents upon changes.', + 'books_sort_auto_sort' => 'Auto Sort Option', + 'books_sort_auto_sort_active' => 'Auto Sort Active: :sortName', 'books_sort_named' => 'Sort Book :bookName', 'books_sort_name' => 'Sort by Name', 'books_sort_created' => 'Sort by Created Date', @@ -207,7 +234,7 @@ 'pages_delete_draft' => 'Delete Draft Page', 'pages_delete_success' => 'Page deleted', 'pages_delete_draft_success' => 'Draft page deleted', - 'pages_delete_warning_template' => 'This page is in active use as a book default page template. These books will no longer have a default page template assigned after this page is deleted.', + 'pages_delete_warning_template' => 'This page is in active use as a book or chapter default page template. These books or chapters will no longer have a default page template assigned after this page is deleted.', 'pages_delete_confirm' => 'Are you sure you want to delete this page?', 'pages_delete_draft_confirm' => 'Are you sure you want to delete this draft page?', 'pages_editing_named' => 'Editing Page :pageName', @@ -224,6 +251,8 @@ 'pages_edit_switch_to_markdown_clean' => '(Clean Content)', 'pages_edit_switch_to_markdown_stable' => '(Stable Content)', 'pages_edit_switch_to_wysiwyg' => 'Switch to WYSIWYG Editor', + 'pages_edit_switch_to_new_wysiwyg' => 'Switch to new WYSIWYG', + 'pages_edit_switch_to_new_wysiwyg_desc' => '(In Beta Testing)', 'pages_edit_set_changelog' => 'Set Changelog', 'pages_edit_enter_changelog_desc' => 'Enter a brief description of the changes you\'ve made', 'pages_edit_enter_changelog' => 'Enter Changelog', @@ -243,6 +272,7 @@ 'pages_md_insert_drawing' => 'Insert Drawing', 'pages_md_show_preview' => 'Show preview', 'pages_md_sync_scroll' => 'Sync preview scroll', + 'pages_md_plain_editor' => 'Plaintext editor', 'pages_drawing_unsaved' => 'Unsaved Drawing Found', 'pages_drawing_unsaved_confirm' => 'Unsaved drawing data was found from a previous failed drawing save attempt. Would you like to restore and continue editing this unsaved drawing?', 'pages_not_in_chapter' => 'Page is not in a chapter', @@ -367,8 +397,11 @@ 'comment' => 'Comment', 'comments' => 'Comments', 'comment_add' => 'Add Comment', + 'comment_none' => 'No comments to display', 'comment_placeholder' => 'Leave a comment here', - 'comment_count' => '{0} No Comments|{1} 1 Comment|[2,*] :count Comments', + 'comment_thread_count' => ':count Comment Thread|:count Comment Threads', + 'comment_archived_count' => ':count Archived', + 'comment_archived_threads' => 'Archived Threads', 'comment_save' => 'Save Comment', 'comment_new' => 'New Comment', 'comment_created' => 'commented :createDiff', @@ -377,8 +410,14 @@ 'comment_deleted_success' => 'Comment deleted', 'comment_created_success' => 'Comment added', 'comment_updated_success' => 'Comment updated', + 'comment_archive_success' => 'Comment archived', + 'comment_unarchive_success' => 'Comment un-archived', + 'comment_view' => 'View comment', + 'comment_jump_to_thread' => 'Jump to thread', 'comment_delete_confirm' => 'Are you sure you want to delete this comment?', 'comment_in_reply_to' => 'In reply to :commentId', + 'comment_reference' => 'Reference', + 'comment_reference_outdated' => '(Outdated)', 'comment_editor_explain' => 'Here are the comments that have been left on this page. Comments can be added & managed when viewing the saved page.', // Revision diff --git a/lang/en/errors.php b/lang/en/errors.php index 607b6ea833a..9d738379648 100644 --- a/lang/en/errors.php +++ b/lang/en/errors.php @@ -10,6 +10,7 @@ // Auth 'error_user_exists_different_creds' => 'A user with the email :email already exists but with different credentials.', + 'auth_pre_register_theme_prevention' => 'User account could not be registered for the provided details', 'email_already_confirmed' => 'Email has already been confirmed, Try logging in.', 'email_confirmation_invalid' => 'This confirmation token is not valid or has already been used, Please try registering again.', 'email_confirmation_expired' => 'The confirmation token has expired, A new confirmation email has been sent.', @@ -23,7 +24,6 @@ 'saml_invalid_response_id' => 'The request from the external authentication system is not recognised by a process started by this application. Navigating back after a login could cause this issue.', 'saml_fail_authed' => 'Login using :system failed, system did not provide successful authorization', 'oidc_already_logged_in' => 'Already logged in', - 'oidc_user_not_registered' => 'The user :name is not registered and automatic registration is disabled', 'oidc_no_email_address' => 'Could not find an email address, for this user, in the data provided by the external authentication system', 'oidc_fail_authed' => 'Login using :system failed, system did not provide successful authorization', 'social_no_action_defined' => 'No action defined', @@ -37,6 +37,7 @@ 'social_driver_not_found' => 'Social driver not found', 'social_driver_not_configured' => 'Your :socialAccount social settings are not configured correctly.', 'invite_token_expired' => 'This invitation link has expired. You can instead try to reset your account password.', + 'login_user_not_found' => 'A user for this action could not be found.', // System 'path_not_writable' => 'File path :filePath could not be uploaded to. Ensure it is writable to the server.', @@ -77,6 +78,7 @@ // Users 'users_cannot_delete_only_admin' => 'You cannot delete the only admin', 'users_cannot_delete_guest' => 'You cannot delete the guest user', + 'users_could_not_send_invite' => 'Could not create user since invite email failed to send', // Roles 'role_cannot_be_edited' => 'This role cannot be edited', @@ -103,6 +105,18 @@ 'app_down' => ':appName is down right now', 'back_soon' => 'It will be back up soon.', + // Import + 'import_zip_cant_read' => 'Could not read ZIP file.', + 'import_zip_cant_decode_data' => 'Could not find and decode ZIP data.json content.', + 'import_zip_no_data' => 'ZIP file data has no expected book, chapter or page content.', + 'import_validation_failed' => 'Import ZIP failed to validate with errors:', + 'import_zip_failed_notification' => 'Failed to import ZIP file.', + 'import_perms_books' => 'You are lacking the required permissions to create books.', + 'import_perms_chapters' => 'You are lacking the required permissions to create chapters.', + 'import_perms_pages' => 'You are lacking the required permissions to create pages.', + 'import_perms_images' => 'You are lacking the required permissions to create images.', + 'import_perms_attachments' => 'You are lacking the required permission to create attachments.', + // API errors 'api_no_authorization_found' => 'No authorization token found on the request', 'api_bad_authorization_format' => 'An authorization token was found on the request but the format appeared incorrect', diff --git a/lang/en/preferences.php b/lang/en/preferences.php index 2b88f9671ff..2872f5f3c65 100644 --- a/lang/en/preferences.php +++ b/lang/en/preferences.php @@ -27,7 +27,7 @@ 'notifications_save' => 'Save Preferences', 'notifications_update_success' => 'Notification preferences have been updated!', 'notifications_watched' => 'Watched & Ignored Items', - 'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.', + 'notifications_watched_desc' => 'Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.', 'auth' => 'Access & Security', 'auth_change_password' => 'Change Password', diff --git a/lang/en/settings.php b/lang/en/settings.php index 1a64dffcb67..34b098bbf2f 100644 --- a/lang/en/settings.php +++ b/lang/en/settings.php @@ -80,6 +80,36 @@ 'reg_confirm_restrict_domain_desc' => 'Enter a comma separated list of email domains you would like to restrict registration to. Users will be sent an email to confirm their address before being allowed to interact with the application.
Note that users will be able to change their email addresses after successful registration.', 'reg_confirm_restrict_domain_placeholder' => 'No restriction set', + // Sorting Settings + 'sorting' => 'Sorting', + 'sorting_book_default' => 'Default Book Sort', + 'sorting_book_default_desc' => 'Select the default sort rule to apply to new books. This won\'t affect existing books, and can be overridden per-book.', + 'sorting_rules' => 'Sort Rules', + 'sorting_rules_desc' => 'These are predefined sorting operations which can be applied to content in the system.', + 'sort_rule_assigned_to_x_books' => 'Assigned to :count Book|Assigned to :count Books', + 'sort_rule_create' => 'Create Sort Rule', + 'sort_rule_edit' => 'Edit Sort Rule', + 'sort_rule_delete' => 'Delete Sort Rule', + 'sort_rule_delete_desc' => 'Remove this sort rule from the system. Books using this sort will revert to manual sorting.', + 'sort_rule_delete_warn_books' => 'This sort rule is currently used on :count book(s). Are you sure you want to delete this?', + 'sort_rule_delete_warn_default' => 'This sort rule is currently used as the default for books. Are you sure you want to delete this?', + 'sort_rule_details' => 'Sort Rule Details', + 'sort_rule_details_desc' => 'Set a name for this sort rule, which will appear in lists when users are selecting a sort.', + 'sort_rule_operations' => 'Sort Operations', + 'sort_rule_operations_desc' => 'Configure the sort actions to be performed by moving them from the list of available operations. Upon use, the operations will be applied in order, from top to bottom. Any changes made here will be applied to all assigned books upon save.', + 'sort_rule_available_operations' => 'Available Operations', + 'sort_rule_available_operations_empty' => 'No operations remaining', + 'sort_rule_configured_operations' => 'Configured Operations', + 'sort_rule_configured_operations_empty' => 'Drag/add operations from the "Available Operations" list', + 'sort_rule_op_asc' => '(Asc)', + 'sort_rule_op_desc' => '(Desc)', + 'sort_rule_op_name' => 'Name - Alphabetical', + 'sort_rule_op_name_numeric' => '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_last' => 'Chapters Last', + // Maintenance settings 'maint' => 'Maintenance', 'maint_image_cleanup' => 'Cleanup Images', @@ -115,7 +145,7 @@ 'recycle_bin_contents_empty' => 'The recycle bin is currently empty', 'recycle_bin_empty' => 'Empty Recycle Bin', 'recycle_bin_empty_confirm' => 'This will permanently destroy all items in the recycle bin including content contained within each item. Are you sure you want to empty the recycle bin?', - 'recycle_bin_destroy_confirm' => 'This action will permanently delete this item, along with any child elements listed below, from the system and you will not be able to restore this content. Are you sure you want to permanently delete this item?', + 'recycle_bin_destroy_confirm' => 'This action will permanently delete this item from the system, along with any child elements listed below, and you will not be able to restore this content. Are you sure you want to permanently delete this item?', 'recycle_bin_destroy_list' => 'Items to be Destroyed', 'recycle_bin_restore_list' => 'Items to be Restored', 'recycle_bin_restore_confirm' => 'This action will restore the deleted item, including any child elements, to their original location. If the original location has since been deleted, and is now in the recycle bin, the parent item will also need to be restored.', @@ -168,6 +198,7 @@ 'role_access_api' => 'Access system API', 'role_manage_settings' => 'Manage app settings', 'role_export_content' => 'Export content', + 'role_import_content' => 'Import content', 'role_editor_change' => 'Change page editor', 'role_notifications' => 'Receive & manage notifications', 'role_asset' => 'Asset Permissions', @@ -282,6 +313,14 @@ 'webhooks_last_errored' => 'Last Errored:', 'webhooks_last_error_message' => 'Last Error Message:', + // Licensing + 'licenses' => 'Licenses', + 'licenses_desc' => 'This page details license information for BookStack in addition to the projects & libraries that are used within BookStack. Many projects listed may only be used in a development context.', + 'licenses_bookstack' => 'BookStack License', + 'licenses_php' => 'PHP Library Licenses', + 'licenses_js' => 'JavaScript Library Licenses', + 'licenses_other' => 'Other Licenses', + 'license_details' => 'License Details', //! If editing translations files directly please ignore this in all //! languages apart from en. Content will be auto-copied from en. @@ -293,6 +332,7 @@ 'bs' => 'Bosanski', 'ca' => 'Català', 'cs' => 'Česky', + 'cy' => 'Cymraeg', 'da' => 'Dansk', 'de' => 'Deutsch (Sie)', 'de_informal' => 'Deutsch (Du)', @@ -314,6 +354,7 @@ 'lt' => 'Lietuvių Kalba', 'lv' => 'Latviešu Valoda', 'nb' => 'Norsk (Bokmål)', + 'ne' => 'नेपाली', 'nn' => 'Nynorsk', 'nl' => 'Nederlands', 'pl' => 'Polski', diff --git a/lang/en/validation.php b/lang/en/validation.php index 2a676c7c4cc..d9b982d1e23 100644 --- a/lang/en/validation.php +++ b/lang/en/validation.php @@ -105,6 +105,11 @@ 'url' => 'The :attribute format is invalid.', 'uploaded' => 'The file could not be uploaded. The server may not accept files of this size.', + 'zip_file' => 'The :attribute needs to reference a file within the ZIP.', + 'zip_file_mime' => 'The :attribute needs to reference a file of type :validTypes, found :foundType.', + 'zip_model_expected' => 'Data object expected but ":type" found.', + 'zip_unique' => 'The :attribute must be unique for the object type within the ZIP.', + // Custom validation lines 'custom' => [ 'password-confirm' => [ diff --git a/lang/es/activities.php b/lang/es/activities.php index a8325821227..89a11c63a2e 100644 --- a/lang/es/activities.php +++ b/lang/es/activities.php @@ -6,7 +6,7 @@ return [ // Pages - 'page_create' => 'página creada', + 'page_create' => 'Página creada', 'page_create_notification' => 'Página creada correctamente', 'page_update' => 'página actualizada', 'page_update_notification' => 'Página actualizada correctamente', @@ -72,7 +72,7 @@ 'mfa_remove_method_notification' => 'Método de Autenticación en Dos Pasos eliminado correctamente', // Settings - 'settings_update' => 'ajustes actualizados', + 'settings_update' => 'configuración actualizada', 'settings_update_notification' => 'Configuración actualizada correctamente', 'maintenance_action_run' => 'ejecutada acción de mantenimiento', @@ -84,6 +84,14 @@ 'webhook_delete' => 'webhook eliminado', 'webhook_delete_notification' => 'Webhook eliminado correctamente', + // Imports + 'import_create' => 'importación creada', + 'import_create_notification' => 'Importación cargada correctamente', + 'import_run' => 'importación actualizada', + 'import_run_notification' => 'Contenido importado correctamente', + 'import_delete' => 'importación borrada', + 'import_delete_notification' => 'Importación borrada correctamente', + // Users 'user_create' => 'usuario creado', 'user_create_notification' => 'Usuario creado correctamente', @@ -93,11 +101,11 @@ 'user_delete_notification' => 'Usuario eliminado correctamente', // API Tokens - 'api_token_create' => 'token de api creado', + 'api_token_create' => 'token de API creado', 'api_token_create_notification' => 'Token API creado correctamente', - 'api_token_update' => 'token de api actualizado', + 'api_token_update' => 'token de API actualizado', 'api_token_update_notification' => 'Token API actualizado correctamente', - 'api_token_delete' => 'token de api borrado', + 'api_token_delete' => 'token de API borrado', 'api_token_delete_notification' => 'Token API borrado correctamente', // Roles @@ -119,6 +127,14 @@ 'comment_update' => 'comentario actualizado', 'comment_delete' => 'comentario borrado', + // Sort Rules + 'sort_rule_create' => 'regla de ordenación creada', + 'sort_rule_create_notification' => 'Rol de ordenación creada con éxito', + 'sort_rule_update' => 'regla de ordenación actualizada', + 'sort_rule_update_notification' => 'Regla de ordenación actualizada correctamente', + 'sort_rule_delete' => 'regla de ordenación eliminada', + 'sort_rule_delete_notification' => 'Rol de ordenación borrada con éxito', + // Other 'permissions_update' => 'permisos actualizados', ]; diff --git a/lang/es/auth.php b/lang/es/auth.php index 13566e81131..45d3eb32916 100644 --- a/lang/es/auth.php +++ b/lang/es/auth.php @@ -91,7 +91,7 @@ 'mfa_option_totp_title' => 'Aplicación para móviles', 'mfa_option_totp_desc' => 'Para utilizar la autenticación en dos pasos necesitarás una aplicación móvil que soporte TOTP como Google Authenticator, Authy o Microsoft Authenticator.', 'mfa_option_backup_codes_title' => 'Códigos de Respaldo', - 'mfa_option_backup_codes_desc' => 'Almacena de forma segura un conjunto de códigos de respaldo de un solo uso que puedes introducir para verificar tu identidad.', + 'mfa_option_backup_codes_desc' => 'Genera un conjunto de códigos de seguridad de un solo uso que ingresará al iniciar sesión para verificar su identidad. Asegúrese de guardarlos en un lugar seguro.', 'mfa_gen_confirm_and_enable' => 'Confirmar y Activar', 'mfa_gen_backup_codes_title' => 'Configuración de Códigos de Respaldo', 'mfa_gen_backup_codes_desc' => 'Guarda la siguiente lista de códigos en un lugar seguro. Al acceder al sistema podrás usar uno de los códigos como un segundo mecanismo de autenticación.', diff --git a/lang/es/common.php b/lang/es/common.php index 6295c598713..399d1d862aa 100644 --- a/lang/es/common.php +++ b/lang/es/common.php @@ -20,7 +20,7 @@ 'description' => 'Descripción', 'role' => 'Rol', 'cover_image' => 'Imagen de portada', - 'cover_image_description' => 'Esta imagen debe ser aproximadamente de 440x250px.', + 'cover_image_description' => 'Esta imagen debe ser de aproximadamente 440x250px aunque será escalada y recortada para adaptarse a la interfaz de usuario en diferentes escenarios según sea necesario, por lo que las dimensiones en pantalla diferirán.', // Actions 'actions' => 'Acciones', @@ -30,6 +30,8 @@ 'create' => 'Crear', 'update' => 'Actualizar', 'edit' => 'Editar', + 'archive' => 'Archivar', + 'unarchive' => 'Desarchivar', 'sort' => 'Ordenar', 'move' => 'Mover', 'copy' => 'Copiar', @@ -107,4 +109,7 @@ // Not directly used but available for convenience to users. 'privacy_policy' => 'Política de privacidad', 'terms_of_service' => 'Términos de Servicio', + + // OpenSearch + 'opensearch_description' => 'Buscar :appName', ]; diff --git a/lang/es/editor.php b/lang/es/editor.php index 1284cad6187..2df05dede86 100644 --- a/lang/es/editor.php +++ b/lang/es/editor.php @@ -13,6 +13,7 @@ 'cancel' => 'Cancelar', 'save' => 'Guardar', 'close' => 'Cerrar', + 'apply' => 'Aplicar', 'undo' => 'Deshacer', 'redo' => 'Rehacer', 'left' => 'Izquierda', @@ -47,6 +48,7 @@ 'superscript' => 'Superíndice', 'subscript' => 'Subíndice', 'text_color' => 'Color de texto', + 'highlight_color' => 'Color de resaltado', 'custom_color' => 'Color personalizado', 'remove_color' => 'Eliminar color', 'background_color' => 'Color de fondo', @@ -81,6 +83,9 @@ 'table_properties' => 'Propiedades de tabla', 'table_properties_title' => 'Propiedades de Tabla', 'delete_table' => 'Eliminar tabla', + 'table_clear_formatting' => 'Limpiar formato de tabla', + 'resize_to_contents' => 'Redimensionar al contenido', + 'row_header' => 'Fila de cabecera', 'insert_row_before' => 'Insertar fila arriba', 'insert_row_after' => 'Insertar fila abajo', 'delete_row' => 'Eliminar fila', @@ -144,6 +149,7 @@ 'url' => 'URL', 'text_to_display' => 'Texto para mostrar', 'title' => 'Titulo', + 'browse_links' => 'Ver enlaces', 'open_link' => 'Abrir enlace', 'open_link_in' => 'Abrir enlace en...', 'open_link_current' => 'Ventana actual', @@ -160,6 +166,8 @@ 'about' => 'Acerca del editor', 'about_title' => 'Acerca del editor WYSIWYG', 'editor_license' => 'Licencia del editor y derechos de autor', + 'editor_lexical_license' => 'Este editor está construido como una bifurcación de :lexicalLink que se distribuye bajo la licencia MIT.', + 'editor_lexical_license_link' => 'Los detalles completos de la licencia se pueden encontrar aquí.', 'editor_tiny_license' => 'Este editor se construye usando :tinyLink que se proporciona bajo la licencia MIT.', 'editor_tiny_license_link' => 'Aquí encontrará los detalles de los derechos de autor y la licencia de TinyMCE.', 'save_continue' => 'Guardar Página y Continuar', diff --git a/lang/es/entities.php b/lang/es/entities.php index 82cf3e69217..23ec5f442de 100644 --- a/lang/es/entities.php +++ b/lang/es/entities.php @@ -39,6 +39,34 @@ 'export_pdf' => 'Archivo PDF', 'export_text' => 'Archivo de texto', 'export_md' => 'Archivo Markdown', + 'export_zip' => 'ZIP portable', + 'default_template' => 'Plantilla de página por defecto', + 'default_template_explain' => 'Asigne una plantilla de página que se utilizará como contenido predeterminado para todas las páginas creadas en este elemento. Tenga en cuenta que esto sólo se utilizará si el creador de páginas tiene acceso a la plantilla de página elegida.', + 'default_template_select' => 'Seleccione una página de plantilla', + 'import' => 'Importar', + 'import_validate' => 'Validar importación', + 'import_desc' => 'Importar libros, capítulos y páginas usando una exportación zip portable de la misma o distinta instancia. Seleccione un archivo ZIP para continuar. Después de que el archivo haya sido subido y validado, podrá configurar y confirmar la importación en la siguiente vista.', + 'import_zip_select' => 'Seleccione archivo ZIP a subir', + 'import_zip_validation_errors' => 'Se detectaron errores al validar el archivo ZIP proporcionado:', + 'import_pending' => 'Importaciones pendientes', + 'import_pending_none' => 'No se han iniciado importaciones.', + 'import_continue' => 'Continuar importación', + 'import_continue_desc' => 'Revise el contenido que debe importarse del archivo ZIP subido. Cuando esté listo, ejecute la importación para añadir su contenido a este sistema. El archivo de importación ZIP subido se eliminará automáticamente al terminar la importación correctamente.', + 'import_details' => 'Detalles de la Importación', + 'import_run' => 'Ejecutar Importación', + 'import_size' => ':size tamaño archivo ZIP', + 'import_uploaded_at' => 'Subido :relativeTime', + 'import_uploaded_by' => 'Subido por', + 'import_location' => 'Ubicación de Importación', + 'import_location_desc' => 'Seleccione una ubicación de destino para el contenido importado. Necesitará los permisos pertinentes para crearlo dentro de la ubicación que elija.', + 'import_delete_confirm' => '¿Está seguro de que desea eliminar esta importación?', + 'import_delete_desc' => 'Esto eliminará el archivo ZIP de importación subido y no se puede deshacer.', + 'import_errors' => 'Errores de Importación', + 'import_errors_desc' => 'Se han producido los siguientes errores durante el intento de importación:', + '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', // Permissions and restrictions 'permissions' => 'Permisos', @@ -132,9 +160,6 @@ 'books_edit_named' => 'Editar Libro :bookName', 'books_form_book_name' => 'Nombre de libro', 'books_save' => 'Guardar libro', - 'books_default_template' => 'Plantilla de página por defecto', - 'books_default_template_explain' => 'Asigne una plantilla de página que se utilizará como contenido predeterminado para todas las páginas nuevas de este libro. Tenga en cuenta que esto sólo se utilizará si el creador de páginas tiene acceso a la página elegida.', - 'books_default_template_select' => 'Seleccione una página de plantilla', 'books_permissions' => 'Permisos del libro', 'books_permissions_updated' => 'Permisos del libro actualizados', 'books_empty_contents' => 'Ninguna página o capítulo ha sido creada para este libro.', @@ -145,7 +170,9 @@ 'books_search_this' => 'Buscar en este libro', 'books_navigation' => 'Navegación de libro', 'books_sort' => 'Organizar contenido de libro', - 'books_sort_desc' => 'Mueve capítulos y páginas dentro de un libro para reorganizar su contenido. Se pueden añadir otros libros lo que permite mover fácilmente capítulos y páginas entre libros.', + 'books_sort_desc' => 'Mueve capítulos y páginas dentro de un libro para reorganizar su contenido. Se pueden añadir otros libros que permiten mover fácilmente capítulos y páginas entre libros. Opcionalmente, se puede establecer una regla de ordenación automática para ordenar automáticamente el contenido de este libro cuando haya cambios.', + 'books_sort_auto_sort' => 'Opción de ordenación automática', + 'books_sort_auto_sort_active' => 'Opción de ordenación activa: sortName', 'books_sort_named' => 'Organizar libro :bookName', 'books_sort_name' => 'Organizar por Nombre', 'books_sort_created' => 'Organizar por Fecha de creación', @@ -207,7 +234,7 @@ 'pages_delete_draft' => 'Borrar borrador de página', 'pages_delete_success' => 'Página borrada', 'pages_delete_draft_success' => 'Borrador de página borrado', - 'pages_delete_warning_template' => 'Esta página está en uso como plantilla de página predeterminada del libro. Estos libros ya no tendrán una plantilla de página predeterminada asignada después de eliminar esta página.', + 'pages_delete_warning_template' => 'Esta página está en uso como plantilla de página predeterminada de libro o capítulo. Estos libros o capítulos ya no tendrán una plantilla de página predeterminada asignada después de eliminar esta página.', 'pages_delete_confirm' => '¿Está seguro de borrar esta página?', 'pages_delete_draft_confirm' => '¿Está seguro de que desea borrar este borrador de página?', 'pages_editing_named' => 'Editando página :pageName', @@ -224,6 +251,8 @@ 'pages_edit_switch_to_markdown_clean' => '(Limpiar Contenido)', 'pages_edit_switch_to_markdown_stable' => '(Contenido Estable)', 'pages_edit_switch_to_wysiwyg' => 'Cambiar a Editor WYSIWYG', + 'pages_edit_switch_to_new_wysiwyg' => 'Cambiar a nuevo editor WYSIWYG', + 'pages_edit_switch_to_new_wysiwyg_desc' => '(En prueba beta)', 'pages_edit_set_changelog' => 'Ajustar Log de cambios', 'pages_edit_enter_changelog_desc' => 'Introduzca una breve descripción de los cambios que ha realizado', 'pages_edit_enter_changelog' => 'Entrar al Log de cambios', @@ -243,6 +272,7 @@ 'pages_md_insert_drawing' => 'Insertar Dibujo', 'pages_md_show_preview' => 'Mostrar vista previa', 'pages_md_sync_scroll' => 'Sincronizar desplazamiento de vista previa', + 'pages_md_plain_editor' => 'Editor de texto plano', 'pages_drawing_unsaved' => 'Encontrado dibujo sin guardar', 'pages_drawing_unsaved_confirm' => 'Se encontraron datos no guardados del dibujo de un intento de guardado fallido. ¿Desea restaurar y continuar editando el dibujo no guardado?', 'pages_not_in_chapter' => 'La página no está en un capítulo', @@ -367,8 +397,11 @@ 'comment' => 'Comentario', 'comments' => 'Comentarios', 'comment_add' => 'Añadir Comentario', + 'comment_none' => 'No hay comentarios para mostrar', 'comment_placeholder' => 'Introduzca su comentario aquí', - 'comment_count' => '{0} Sin Comentarios|{1} 1 Comentario|[2,*] :count Comentarios', + 'comment_thread_count' => ':count hilo de comentarios|:count hilos de comentarios', + 'comment_archived_count' => ':count Archivados', + 'comment_archived_threads' => 'Hilos archivados', 'comment_save' => 'Guardar comentario', 'comment_new' => 'Nuevo Comentario', 'comment_created' => 'comentado :createDiff', @@ -377,8 +410,14 @@ 'comment_deleted_success' => 'Comentario borrado', 'comment_created_success' => 'Comentario añadido', 'comment_updated_success' => 'Comentario actualizado', + 'comment_archive_success' => 'Comentario archivado', + 'comment_unarchive_success' => 'Comentario desarchivado', + 'comment_view' => 'Ver comentario', + 'comment_jump_to_thread' => 'Ir al hilo', 'comment_delete_confirm' => '¿Está seguro de que quiere borrar este comentario?', 'comment_in_reply_to' => 'En respuesta a :commentId', + 'comment_reference' => 'Referencia', + 'comment_reference_outdated' => '(obsoleto)', 'comment_editor_explain' => 'Estos son los comentarios que se han escrito en esta página. Los comentarios se pueden añadir y administrar cuando se ve la página guardada.', // Revision diff --git a/lang/es/errors.php b/lang/es/errors.php index 9fb7afba469..98aa2acd593 100644 --- a/lang/es/errors.php +++ b/lang/es/errors.php @@ -10,6 +10,7 @@ // Auth 'error_user_exists_different_creds' => 'Un usuario con el correo electrónico :email ya existe pero con credenciales diferentes.', + 'auth_pre_register_theme_prevention' => 'La cuenta de usuario no pudo ser registrada con los detalles proporcionados', 'email_already_confirmed' => 'El correo electrónico ya ha sido confirmado, intente acceder a la aplicación.', 'email_confirmation_invalid' => 'Este token de confirmación no es válido o ya ha sido usado, intente registrar uno nuevamente.', 'email_confirmation_expired' => 'El token de confirmación ha expirado, un nuevo email de confirmacón ha sido enviado.', @@ -23,7 +24,6 @@ 'saml_invalid_response_id' => 'La solicitud del sistema de autenticación externo no está reconocida por un proceso iniciado por esta aplicación. Navegar hacia atrás después de un inicio de sesión podría causar este problema.', 'saml_fail_authed' => 'El inicio de sesión con :system falló, el sistema no proporcionó una autorización correcta', 'oidc_already_logged_in' => 'Ya tenías la sesión iniciada', - 'oidc_user_not_registered' => 'El usuario :name no está registrado y el registro automático está deshabilitado', 'oidc_no_email_address' => 'No se pudo encontrar una dirección de correo electrónico, para este usuario, en los datos proporcionados por el sistema de autenticación externo', 'oidc_fail_authed' => 'El inicio de sesión con :system falló, el sistema no proporcionó una autorización correcta', 'social_no_action_defined' => 'Acción no definida', @@ -37,6 +37,7 @@ 'social_driver_not_found' => 'Driver social no encontrado', 'social_driver_not_configured' => 'Su configuración :socialAccount no es correcta.', 'invite_token_expired' => 'Este enlace de invitación ha expirado. Puede resetear la contraseña de su cuenta como alternativa.', + 'login_user_not_found' => 'No se pudo encontrar un usuario para esta acción.', // System 'path_not_writable' => 'El fichero no pudo ser subido a la ruta :filePath . Asegúrese de que es escribible por el servidor.', @@ -77,6 +78,7 @@ // Users 'users_cannot_delete_only_admin' => 'No se puede borrar el único administrador', 'users_cannot_delete_guest' => 'No se puede borrar el usuario invitado', + 'users_could_not_send_invite' => 'No se pudo crear el usuario porque no se pudo enviar el correo de invitación', // Roles 'role_cannot_be_edited' => 'Este rol no puede ser editado', @@ -103,6 +105,18 @@ 'app_down' => 'La aplicación :appName se encuentra caída en este momento', 'back_soon' => 'Volverá a estar operativa pronto.', + // Import + 'import_zip_cant_read' => 'No se pudo leer el archivo ZIP.', + 'import_zip_cant_decode_data' => 'No se pudo encontrar y decodificar el archivo data.json. en el archivo ZIP.', + 'import_zip_no_data' => 'Los datos del archivo ZIP no contienen ningún libro, capítulo o contenido de página.', + 'import_validation_failed' => 'Error al validar la importación del ZIP con errores:', + 'import_zip_failed_notification' => 'Error al importar archivo ZIP.', + 'import_perms_books' => 'Le faltan los permisos necesarios para crear libros.', + 'import_perms_chapters' => 'Le faltan los permisos necesarios para crear capítulos.', + 'import_perms_pages' => 'Le faltan los permisos necesarios para crear páginas.', + 'import_perms_images' => 'Le faltan los permisos necesarios para crear imágenes.', + 'import_perms_attachments' => 'Le faltan los permisos necesarios para crear adjuntos.', + // API errors 'api_no_authorization_found' => 'No se encontró ningún token de autorización en la solicitud', 'api_bad_authorization_format' => 'Se ha encontrado un token de autorización en la solicitud pero el formato era incorrecto', diff --git a/lang/es/preferences.php b/lang/es/preferences.php index 3974683f7f7..13af93d2978 100644 --- a/lang/es/preferences.php +++ b/lang/es/preferences.php @@ -27,7 +27,7 @@ 'notifications_save' => 'Guardar preferencias', 'notifications_update_success' => '¡Se han actualizado las preferencias de notificaciones!', 'notifications_watched' => 'Elementos vistos e ignorados', - 'notifications_watched_desc' => ' A continuación se muestran los elementos que tienen preferencias personalizadas de monitorización. Para actualizar sus preferencias, vea el artículo y las opciones se mostrarán en la barra lateral.', + 'notifications_watched_desc' => 'A continuación se muestran los elementos que tienen preferencias personalizadas de monitorización. Para actualizar sus preferencias, vea el elemento y las opciones se mostrarán en la barra lateral.', 'auth' => 'Acceso y seguridad', 'auth_change_password' => 'Cambiar contraseña', diff --git a/lang/es/settings.php b/lang/es/settings.php index 0d0cf221ee6..55eb498d630 100644 --- a/lang/es/settings.php +++ b/lang/es/settings.php @@ -74,6 +74,36 @@ 'reg_confirm_restrict_domain_desc' => 'Introduzca una lista separada por comas de los dominios para cuentas de correo a los que se les permitirá el registro de usuarios. A los usuarios les será enviado un correo electrónico para confirmar la dirección antes de que se le permita interactuar con la aplicación.
Tenga en cuenta que los usuarios podrán cambiar sus direcciones de correo electrónico después de registrarse exitosamente.', 'reg_confirm_restrict_domain_placeholder' => 'Ninguna restricción establecida', + // Sorting Settings + 'sorting' => 'Ordenación', + 'sorting_book_default' => 'Orden por defecto del libro', + 'sorting_book_default_desc' => 'Seleccione la regla de ordenación predeterminada para aplicar a nuevos libros. Esto no afectará a los libros existentes, y puede ser anulado por libro.', + 'sorting_rules' => 'Reglas de ordenación', + 'sorting_rules_desc' => 'Son operaciones de ordenación predefinidas que se pueden aplicar al contenido en el sistema.', + 'sort_rule_assigned_to_x_books' => 'Asignado a :count libro | Asignado a :count libros', + 'sort_rule_create' => 'Crear regla de ordenación', + 'sort_rule_edit' => 'Editar regla de ordenación', + 'sort_rule_delete' => 'Eliminar regla de ordenación', + 'sort_rule_delete_desc' => 'Eliminar esta regla de ordenación del sistema. Los Libros que utilicen este tipo se revertirán a la ordenación manual.', + 'sort_rule_delete_warn_books' => 'Esta regla de ordenación se utiliza actualmente en :count libro(s). ¿Está seguro que desea eliminarla?', + 'sort_rule_delete_warn_default' => 'Esta regla de ordenación se utiliza actualmente como predeterminado para los libros. ¿Está seguro de que desea eliminarla?', + 'sort_rule_details' => 'Detalles de la regla de ordenación', + 'sort_rule_details_desc' => 'Establezca un nombre para esta regla de ordenación, que aparecerá en las listas cuando los usuarios estén seleccionando un orden.', + 'sort_rule_operations' => 'Operaciones de ordenación', + 'sort_rule_operations_desc' => 'Configure las acciones de ordenación a realizar moviéndolas de la lista de operaciones disponibles. Al usarse, las operaciones se aplicarán en orden, de arriba a abajo. Cualquier cambio realizado aquí se aplicará a todos los libros asignados al guardar.', + 'sort_rule_available_operations' => 'Operaciones disponibles', + 'sort_rule_available_operations_empty' => 'No hay operaciones restantes', + 'sort_rule_configured_operations' => 'Operaciones configuradas', + 'sort_rule_configured_operations_empty' => 'Arrastrar/añadir operaciones desde la lista de "Operaciones disponibles"', + 'sort_rule_op_asc' => '(Asc)', + 'sort_rule_op_desc' => '(Desc)', + 'sort_rule_op_name' => 'Nombre - Alfabético', + 'sort_rule_op_name_numeric' => 'Nombre - Numérico', + 'sort_rule_op_created_date' => 'Fecha de creación', + 'sort_rule_op_updated_date' => 'Fecha de actualización', + 'sort_rule_op_chapters_first' => 'Capítulos al inicio', + 'sort_rule_op_chapters_last' => 'Capítulos al final', + // Maintenance settings 'maint' => 'Mantenimiento', 'maint_image_cleanup' => 'Limpiar imágenes', @@ -162,6 +192,7 @@ 'role_access_api' => 'API de sistema de acceso', 'role_manage_settings' => 'Gestionar ajustes de la aplicación', 'role_export_content' => 'Exportar contenido', + 'role_import_content' => 'Importar contenido', 'role_editor_change' => 'Cambiar editor de página', 'role_notifications' => 'Recibir y gestionar notificaciones', 'role_asset' => 'Permisos de contenido', @@ -276,6 +307,14 @@ 'webhooks_last_errored' => 'Último error:', 'webhooks_last_error_message' => 'Último mensaje de error:', + // Licensing + 'licenses' => 'Licencias', + 'licenses_desc' => 'Esta página detalla información sobre la licencia de BookStack además de los proyectos y bibliotecas que se utilizan en BookStack. Muchos proyectos enumerados aquí pueden ser utilizados solo en un contexto de desarrollo.', + 'licenses_bookstack' => 'Licencia BookStack', + 'licenses_php' => 'Licencias de la biblioteca PHP', + 'licenses_js' => 'Licencias de la biblioteca JavaScript', + 'licenses_other' => 'Otras Licencias', + 'license_details' => 'Datos de la licencia', //! If editing translations files directly please ignore this in all //! languages apart from en. Content will be auto-copied from en. @@ -287,6 +326,7 @@ 'bs' => 'Bosnio', 'ca' => 'Català', 'cs' => 'Checo', + 'cy' => 'Cymraeg', 'da' => 'Danés', 'de' => 'Alemán (informal)', 'de_informal' => 'Alemán (formal)', @@ -308,6 +348,7 @@ 'lt' => 'Lituano', 'lv' => 'Letón', 'nb' => 'Norsk (Bokmål)', + 'ne' => 'नेपाली', 'nn' => 'Nynorsk', 'nl' => 'Holanda', 'pl' => 'Polaco', diff --git a/lang/es/validation.php b/lang/es/validation.php index bfcb050bea4..d5f4f8495c8 100644 --- a/lang/es/validation.php +++ b/lang/es/validation.php @@ -105,6 +105,11 @@ 'url' => 'El atributo :attribute tiene un formato inválido.', 'uploaded' => 'El archivo no ha podido subirse. Es posible que el servidor no acepte archivos de este tamaño.', + 'zip_file' => 'El :attribute necesita hacer referencia a un archivo dentro del ZIP.', + 'zip_file_mime' => 'El :attribute necesita hacer referencia a un archivo de tipo :validTypes, encontrado :foundType.', + 'zip_model_expected' => 'Se esperaba un objeto de datos, pero se encontró ":type".', + 'zip_unique' => 'El :attribute debe ser único para el tipo de objeto dentro del ZIP.', + // Custom validation lines 'custom' => [ 'password-confirm' => [ diff --git a/lang/es_AR/activities.php b/lang/es_AR/activities.php index 760652e48da..e01fe4e643e 100644 --- a/lang/es_AR/activities.php +++ b/lang/es_AR/activities.php @@ -10,7 +10,7 @@ 'page_create_notification' => 'Página creada correctamente', 'page_update' => 'página actualizada', 'page_update_notification' => 'Página actualizada correctamente', - 'page_delete' => 'página borrada', + 'page_delete' => 'borró la página', 'page_delete_notification' => 'Página eliminada correctamente', 'page_restore' => 'página restaurada', 'page_restore_notification' => 'Página restaurada correctamente', @@ -59,7 +59,7 @@ 'favourite_remove_notification' => '".name" se eliminó de sus favoritos', // Watching - 'watch_update_level_notification' => 'Preferencias de suscripciones actualizadas correctamente', + 'watch_update_level_notification' => 'Preferencias de visualización actualizadas con éxito', // Auth 'auth_login' => 'sesión iniciada', @@ -84,11 +84,19 @@ 'webhook_delete' => 'webhook eliminado', 'webhook_delete_notification' => 'Webhook eliminado correctamente', + // Imports + 'import_create' => 'importación creada', + 'import_create_notification' => 'Importación cargada correctamente', + 'import_run' => 'importación actualizada', + 'import_run_notification' => 'Contenido importado correctamente', + 'import_delete' => 'importación borrada', + 'import_delete_notification' => 'Importación borrada correctamente', + // Users 'user_create' => 'usuario creado', 'user_create_notification' => 'Usuario creado correctamente', 'user_update' => 'usuario actualizado', - 'user_update_notification' => 'Usuario actualizado correctamente', + 'user_update_notification' => 'Usuario actualizado con éxito', 'user_delete' => 'usuario eliminado', 'user_delete_notification' => 'El usuario fue eliminado correctamente', @@ -97,7 +105,7 @@ 'api_token_create_notification' => 'Token de API creado correctamente', 'api_token_update' => 'token de API actualizado', 'api_token_update_notification' => 'Token de API actualizado correctamente', - 'api_token_delete' => 'token de API eliminado', + 'api_token_delete' => 'API token eliminado', 'api_token_delete_notification' => 'Token de API eliminado correctamente', // Roles @@ -115,9 +123,17 @@ // Comments 'commented_on' => 'comentado', - 'comment_create' => 'comentario añadido', + 'comment_create' => 'comentario agregado', 'comment_update' => 'comentario actualizado', - 'comment_delete' => 'comentario borrado', + 'comment_delete' => 'comentario eliminado', + + // Sort Rules + 'sort_rule_create' => 'regla de ordenación creada', + 'sort_rule_create_notification' => 'Regla de ordenación creada correctamente', + 'sort_rule_update' => 'regla de ordenación actualizada', + 'sort_rule_update_notification' => 'Regla de ordenación actualizada correctamente', + 'sort_rule_delete' => 'regla de ordenación eliminada', + 'sort_rule_delete_notification' => 'Regla de ordenación eliminada correctamente', // Other 'permissions_update' => 'permisos actualizados', diff --git a/lang/es_AR/auth.php b/lang/es_AR/auth.php index 19b86f67961..7687f709e92 100644 --- a/lang/es_AR/auth.php +++ b/lang/es_AR/auth.php @@ -6,12 +6,12 @@ */ return [ - 'failed' => 'Las credenciales no concuerdan con nuestros registros.', - 'throttle' => 'Demasiados intentos fallidos de conexión. Por favor intente nuevamente en :seconds segundos.', + 'failed' => 'Estas credenciales no concuerdan con nuestros registros.', + 'throttle' => 'Demasiados intentos fallidos de inicio de sesión. Por favor intente nuevamente en :seconds segundos.', // Login & Register 'sign_up' => 'Registrarse', - 'log_in' => 'Acceder', + 'log_in' => 'Iniciar sesión', 'log_in_with' => 'Acceder con :socialDriver', 'sign_up_with' => 'Registrarse con :socialDriver', 'logout' => 'Salir', @@ -91,7 +91,7 @@ 'mfa_option_totp_title' => 'Aplicación móvil', 'mfa_option_totp_desc' => 'Para utilizar la autenticación en dos pasos necesitará una aplicación móvil que soporte TOTP como Google Authenticator, Authy o Microsoft Authenticator.', 'mfa_option_backup_codes_title' => 'Códigos de Respaldo', - 'mfa_option_backup_codes_desc' => 'Almacene de forma segura un conjunto de códigos de respaldo de un solo uso que pueda introducir para verificar su identidad.', + 'mfa_option_backup_codes_desc' => 'Genera un conjunto de códigos de respaldo de un único uso que ingresará al iniciar sesión para verificar su identidad. Asegúrese de guardarlos en un lugar seguro.', 'mfa_gen_confirm_and_enable' => 'Confirmar y Activar', 'mfa_gen_backup_codes_title' => 'Configuración de Códigos de Respaldo', 'mfa_gen_backup_codes_desc' => 'Guarde la siguiente lista de códigos en un lugar seguro. Al acceder al sistema podrá usar uno de los códigos como un segundo mecanismo de autenticación.', diff --git a/lang/es_AR/common.php b/lang/es_AR/common.php index bc95a4fcfcb..e30adbf69be 100644 --- a/lang/es_AR/common.php +++ b/lang/es_AR/common.php @@ -12,7 +12,7 @@ 'save' => 'Guardar', 'continue' => 'Continuar', 'select' => 'Seleccionar', - 'toggle_all' => 'Alternar todo', + 'toggle_all' => 'Mostrar/Ocultar Todo', 'more' => 'Más', // Form Labels @@ -20,7 +20,7 @@ 'description' => 'Descripción', 'role' => 'Rol', 'cover_image' => 'Imagen de cubierta', - 'cover_image_description' => 'Esta imagen debe ser de 440x250px aproximadamente.', + 'cover_image_description' => 'Esta imagen debe ser de 440x250px aproximadamente, aunque será escalada y recortada para adaptarse a la interfaz de usuario en los diferentes escenarios según sea necesario, por lo que las dimensiones en pantalla serán diferentes.', // Actions 'actions' => 'Acciones', @@ -30,6 +30,8 @@ 'create' => 'Crear', 'update' => 'Actualizar', 'edit' => 'Editar', + 'archive' => 'Archivar', + 'unarchive' => 'Desarchivar', 'sort' => 'Ordenar', 'move' => 'Mover', 'copy' => 'Copiar', @@ -42,7 +44,7 @@ 'remove' => 'Remover', 'add' => 'Agregar', 'configure' => 'Configurar', - 'manage' => 'Gestionar', + 'manage' => 'Administra', 'fullscreen' => 'Pantalla completa', 'favourite' => 'Favoritos', 'unfavourite' => 'Eliminar de favoritos', @@ -70,8 +72,8 @@ 'no_items' => 'No hay elementos disponibles', 'back_to_top' => 'Volver arriba', 'skip_to_main_content' => 'Ir al contenido principal', - 'toggle_details' => 'Alternar detalles', - 'toggle_thumbnails' => 'Alternar miniaturas', + 'toggle_details' => 'Mostrar/Ocultar Detalles', + 'toggle_thumbnails' => 'Mostrar/Ocultar Miniaturas', 'details' => 'Detalles', 'grid_view' => 'Vista de grilla', 'list_view' => 'Vista de lista', @@ -107,4 +109,7 @@ // Not directly used but available for convenience to users. 'privacy_policy' => 'Política de privacidad', 'terms_of_service' => 'Términos de Servicio', + + // OpenSearch + 'opensearch_description' => 'Buscar :appName', ]; diff --git a/lang/es_AR/components.php b/lang/es_AR/components.php index d701c857a6a..0e2c4c88bae 100644 --- a/lang/es_AR/components.php +++ b/lang/es_AR/components.php @@ -34,7 +34,7 @@ 'image_delete_success' => 'Imagen borrada exitosamente', 'image_replace' => 'Reemplazar imagen', 'image_replace_success' => 'Imagen actualizada correctamente', - 'image_rebuild_thumbs' => 'Regenerar distintos tamaños', + 'image_rebuild_thumbs' => 'Regenerar Variaciones de Tamaño', 'image_rebuild_thumbs_success' => '¡Imágenes de distinto tamaño regeneradas correctamente!', // Code Editor diff --git a/lang/es_AR/editor.php b/lang/es_AR/editor.php index df453f2f771..94fd55b0998 100644 --- a/lang/es_AR/editor.php +++ b/lang/es_AR/editor.php @@ -13,6 +13,7 @@ 'cancel' => 'Cancelar', 'save' => 'Guardar', 'close' => 'Cerrar', + 'apply' => 'Aplicar', 'undo' => 'Deshacer', 'redo' => 'Rehacer', 'left' => 'Izquierda', @@ -47,6 +48,7 @@ 'superscript' => 'Superíndice', 'subscript' => 'Subíndice', 'text_color' => 'Color del texto', + 'highlight_color' => 'Color de resaltado', 'custom_color' => 'Color personalizado', 'remove_color' => 'Eliminar color', 'background_color' => 'Color de fondo', @@ -81,6 +83,9 @@ 'table_properties' => 'Propiedades de tabla', 'table_properties_title' => 'Propiedades de Tabla', 'delete_table' => 'Eliminar tabla', + 'table_clear_formatting' => 'Limpiar el formato de tabla', + 'resize_to_contents' => 'Redimensionar al contenido', + 'row_header' => 'Cabecera de fila', 'insert_row_before' => 'Insertar fila arriba', 'insert_row_after' => 'Insertar fila abajo', 'delete_row' => 'Eliminar fila', @@ -144,6 +149,7 @@ 'url' => 'URL', 'text_to_display' => 'Texto a mostrar', 'title' => 'Título', + 'browse_links' => 'Explorar enlaces', 'open_link' => 'Abrir enlace', 'open_link_in' => 'Abrir enlace en...', 'open_link_current' => 'Ventana actual', @@ -160,6 +166,8 @@ 'about' => 'Acerca del editor', 'about_title' => 'Acerca del editor WYSIWYG', 'editor_license' => 'Licencia del editor y derechos de autor', + 'editor_lexical_license' => 'Este editor está construido como una bifurcación de :lexicalLink que se distribuye bajo la licencia MIT.', + 'editor_lexical_license_link' => 'Los detalles completos de la licencia se pueden encontrar aquí.', 'editor_tiny_license' => 'Este editor se construye usando :tinyLink provisto bajo la licencia MIT.', 'editor_tiny_license_link' => 'Aquí se muestran los detalles de los derechos de autor y la licencia de TinyMCE.', 'save_continue' => 'Guardar Página y Continuar', diff --git a/lang/es_AR/entities.php b/lang/es_AR/entities.php index 3afb54bcda0..6c54a000413 100644 --- a/lang/es_AR/entities.php +++ b/lang/es_AR/entities.php @@ -23,7 +23,7 @@ 'meta_updated' => 'Actualizado el :timeLength', 'meta_updated_name' => 'Actualizado el :timeLength por :user', 'meta_owned_name' => 'Propiedad de :user', - 'meta_reference_count' => 'Referido en :count página | Referido en :count paginas', + 'meta_reference_count' => 'Referido en :count página | Referido en :count páginas', 'entity_select' => 'Seleccione entidad', 'entity_select_lack_permission' => 'No tiene los permisos necesarios para seleccionar este elemento', 'images' => 'Imágenes', @@ -39,10 +39,38 @@ 'export_pdf' => 'Archivo PDF', 'export_text' => 'Archivo de texto plano', 'export_md' => 'Archivo Markdown', + 'export_zip' => 'ZIP portable', + 'default_template' => 'Plantilla de página predeterminada', + 'default_template_explain' => 'Asigne una plantilla de página que se utilizará como contenido predeterminado para todas las páginas creadas en este elemento. Tenga en cuenta que esto sólo se utilizará si el creador de las páginas tiene acceso a la plantilla de página elegida.', + 'default_template_select' => 'Seleccione una página de plantilla', + 'import' => 'Importar', + 'import_validate' => 'Validar importación', + 'import_desc' => 'Importar libros, capítulos y páginas usando una exportación zip portable de la misma instancia u otra distinta. Seleccione un archivo ZIP para continuar. Después de que el archivo haya sido subido y validado, podrá configurar y confirmar la importación en la siguiente vista.', + 'import_zip_select' => 'Seleccione archivo ZIP a subir', + 'import_zip_validation_errors' => 'Se detectaron errores al validar el archivo ZIP proporcionado:', + 'import_pending' => 'Importaciones pendientes', + 'import_pending_none' => 'No se iniciaron importaciones.', + 'import_continue' => 'Continuar la importación', + 'import_continue_desc' => 'Revise el contenido que debe importarse del archivo ZIP subido. Cuando esté listo, ejecute la importación para añadir su contenido a este sistema. El archivo de importación ZIP subido se eliminará automáticamente al terminar la importación correctamente.', + 'import_details' => 'Detalles de la Importación', + 'import_run' => 'Ejecutar la importación', + 'import_size' => ':size tamaño del archivo ZIP', + 'import_uploaded_at' => 'Subido :relativeTime', + 'import_uploaded_by' => 'Subido por', + 'import_location' => 'Ubicación de Importación', + 'import_location_desc' => 'Seleccione una ubicación de destino para el contenido importado. Necesitará los permisos pertinentes para crearlo dentro de la ubicación que elija.', + 'import_delete_confirm' => '¿Está seguro de que desea eliminar esta importación?', + 'import_delete_desc' => 'Esto eliminará el archivo ZIP de importación subido y no se puede deshacer.', + 'import_errors' => 'Errores de Importación', + 'import_errors_desc' => 'Se produjeron los siguientes errores durante el intento de importación:', + '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', // Permissions and restrictions 'permissions' => 'Permisos', - 'permissions_desc' => 'Establezca los permisos aquí para reemplazar los permisos por defecto proporcionados por los roles de usuario.', + 'permissions_desc' => 'Establezca los permisos aquí para reemplazar los permisos predeterminados proporcionados por los roles de usuario.', 'permissions_book_cascade' => 'Los permisos establecidos en los libros se aplicarán a los capítulos contenidos y las páginas contenidas, a menos que tengan sus propios permisos definidos.', 'permissions_chapter_cascade' => 'Los permisos establecidos en los capítulos se aplicarán a las páginas contenidas, a menos que tengan sus propios permisos definidos.', 'permissions_save' => 'Guardar permisos', @@ -50,7 +78,7 @@ 'permissions_role_everyone_else' => 'Todos los demás', 'permissions_role_everyone_else_desc' => 'Establecer permisos para todos los roles no específicamente reemplazados.', 'permissions_role_override' => 'Reemplazar permisos para el rol', - 'permissions_inherit_defaults' => 'Heredar valores por defecto', + 'permissions_inherit_defaults' => 'Heredar valores predeterminados', // Search 'search_results' => 'Buscar resultados', @@ -132,9 +160,6 @@ 'books_edit_named' => 'Editar Libro :bookName', 'books_form_book_name' => 'Nombre de libro', 'books_save' => 'Guardar libro', - 'books_default_template' => 'Plantilla de página por defecto', - 'books_default_template_explain' => 'Asigne una plantilla de página que se utilizará como contenido predeterminado para todas las páginas nuevas de este libro. Tenga en cuenta que esto sólo se utilizará si el creador de páginas tiene acceso a la página elegida.', - 'books_default_template_select' => 'Seleccione una página de plantilla', 'books_permissions' => 'permisos de libro', 'books_permissions_updated' => 'Permisos de libro actualizados', 'books_empty_contents' => 'Ninguna página o capítulo ha sido creada para este libro.', @@ -145,7 +170,9 @@ 'books_search_this' => 'Buscar en este libro', 'books_navigation' => 'Navegación de libro', 'books_sort' => 'Organizar contenido de libro', - 'books_sort_desc' => 'Mueva capítulos y páginas dentro de un libro para reorganizar su contenido. Se pueden añadir otros libros, lo que permite mover fácilmente capítulos y páginas entre libros.', + 'books_sort_desc' => 'Mueve capítulos y páginas dentro de un libro para reorganizar su contenido. Se pueden añadir otros libros que permiten mover fácilmente capítulos y páginas entre libros. Opcionalmente, se puede establecer una regla de ordenación automática para el contenido de este libro cuando haya cambios.', + 'books_sort_auto_sort' => 'Opción de ordenación automática', + 'books_sort_auto_sort_active' => 'Opción de ordenación activa: sortName', 'books_sort_named' => 'Organizar libro :bookName', 'books_sort_name' => 'Organizar por nombre', 'books_sort_created' => 'Organizar por fecha de creación', @@ -207,7 +234,7 @@ 'pages_delete_draft' => 'Borrar borrador de página', 'pages_delete_success' => 'Página borrada', 'pages_delete_draft_success' => 'Borrador de página borrado', - 'pages_delete_warning_template' => 'Esta página está en uso como plantilla de página predeterminada del libro. Estos libros ya no tendrán una plantilla de página predeterminada asignada después de eliminar esta página.', + 'pages_delete_warning_template' => 'Esta página está en uso como plantilla de página predeterminada de libro o capítulo. Estos libros o capítulos ya no tendrán una plantilla de página predeterminada asignada después de eliminar esta página.', 'pages_delete_confirm' => '¿Está seguro de borrar esta página?', 'pages_delete_draft_confirm' => 'Está seguro de que desea borrar este borrador de página?', 'pages_editing_named' => 'Editando página :pageName', @@ -224,6 +251,8 @@ 'pages_edit_switch_to_markdown_clean' => '(Limpiar Contenido)', 'pages_edit_switch_to_markdown_stable' => '(Contenido Estable)', 'pages_edit_switch_to_wysiwyg' => 'Cambiar a Editor WYSIWYG', + 'pages_edit_switch_to_new_wysiwyg' => 'Cambiar a nuevo editor WYSIWYG', + 'pages_edit_switch_to_new_wysiwyg_desc' => '(En prueba beta)', 'pages_edit_set_changelog' => 'Establecer cambios de registro', 'pages_edit_enter_changelog_desc' => 'Introduzca una breve descripción de los cambios que ha realizado', 'pages_edit_enter_changelog' => 'Entrar en cambio de registro', @@ -243,8 +272,9 @@ 'pages_md_insert_drawing' => 'Insertar Dibujo', 'pages_md_show_preview' => 'Mostrar vista previa', 'pages_md_sync_scroll' => 'Sincronizar desplazamiento de vista previa', + 'pages_md_plain_editor' => 'Editor de texto plano', 'pages_drawing_unsaved' => 'Encontrado dibujo sin guardar', - 'pages_drawing_unsaved_confirm' => 'Se encontraron datos no guardados del dibujo de un intento de guardado fallido. ¿Desea restaurar y continuar editando el dibujo no guardado?', + 'pages_drawing_unsaved_confirm' => 'Se encontraron datos del dibujo no guardados durante un intento de guardado fallido anterior. ¿Desea restaurar y continuar editando el dibujo no guardado?', 'pages_not_in_chapter' => 'La página no esá en el capítulo', 'pages_move' => 'Mover página', 'pages_copy' => 'Copiar página', @@ -367,8 +397,11 @@ 'comment' => 'Comentario', 'comments' => 'Comentarios', 'comment_add' => 'Agregar comentario', + 'comment_none' => 'No hay comentarios para mostrar', 'comment_placeholder' => 'DEjar un comentario aquí', - 'comment_count' => '{0} Sin Comentarios|{1} 1 Comentario|[2,*] :count Comentarios', + 'comment_thread_count' => ':count Hilo de Comentarios|:count Hilos de Comentarios', + 'comment_archived_count' => ':count Archivados', + 'comment_archived_threads' => 'Hilos Archivados', 'comment_save' => 'Guardar comentario', 'comment_new' => 'Nuevo comentario', 'comment_created' => 'comentado :createDiff', @@ -377,8 +410,14 @@ 'comment_deleted_success' => 'Comentario borrado', 'comment_created_success' => 'Comentario agregado', 'comment_updated_success' => 'Comentario actualizado', + 'comment_archive_success' => 'Comentario archivado', + 'comment_unarchive_success' => 'Comentario no archivado', + 'comment_view' => 'Ver comentario', + 'comment_jump_to_thread' => 'Ir al hilo', 'comment_delete_confirm' => '¿Está seguro que quiere borrar este comentario?', 'comment_in_reply_to' => 'En respuesta a :commentId', + 'comment_reference' => 'Referencia', + 'comment_reference_outdated' => '(Obsoleto)', 'comment_editor_explain' => 'Estos son los comentarios que se escribieron en esta página. Los comentarios se pueden añadir y administrar cuando se visualiza la página guardada.', // Revision @@ -409,29 +448,29 @@ // References 'references' => 'Referencias', 'references_none' => 'No hay referencias a este elemento.', - 'references_to_desc' => 'A continuación se muestran todas las páginas en el sistema que enlazan a este elemento.', + 'references_to_desc' => 'A continuación se muestra todo el contenido conocido en el sistema que enlaza a este elemento.', // Watch Options 'watch' => 'Suscribirme', - 'watch_title_default' => 'Preferencias por defecto', - 'watch_desc_default' => 'Revertir suscripciones a tus preferencias de notificación por defecto.', + 'watch_title_default' => 'Preferencias Predeterminadas', + 'watch_desc_default' => 'Revertir las suscripciones a solo tus preferencias de notificación predeterminadas.', 'watch_title_ignore' => 'Ignorar', - 'watch_desc_ignore' => 'Ignorar todas las notificaciones, incluyendo las de las preferencias a nivel de usuario.', - 'watch_title_new' => 'Nuevas páginas', + 'watch_desc_ignore' => 'Ignorar todas las notificaciones, incluyendo las de preferencias a nivel de usuario.', + 'watch_title_new' => 'Páginas Nuevas', 'watch_desc_new' => 'Notificar cuando se crea una nueva página dentro de este elemento.', - 'watch_title_updates' => 'Todas las actualizaciones de páginas', - 'watch_desc_updates' => 'Notificar todos los cambios de páginas y páginas nuevas.', - 'watch_desc_updates_page' => 'Notificar todos los cambios en la página.', - 'watch_title_comments' => 'Todas las actualizaciones de páginas y comentarios', - 'watch_desc_comments' => 'Notificar sobre todas las páginas nuevas, cambios de página y nuevos comentarios.', + 'watch_title_updates' => 'Todas las actualizaciones de página', + 'watch_desc_updates' => 'Notificar todas las páginas nuevas y todos los cambios de página.', + 'watch_desc_updates_page' => 'Notificar todos los cambios de página.', + 'watch_title_comments' => 'Todas las actualizaciones y comentarios de página', + 'watch_desc_comments' => 'Notificar todas las páginas nuevas, todos los cambios de página y todos los nuevos comentarios.', 'watch_desc_comments_page' => 'Notificar los cambios en las páginas y los nuevos comentarios.', - 'watch_change_default' => 'Cambiar preferencias de notificación por defecto', - 'watch_detail_ignore' => 'Ignorar notificaciones', + 'watch_change_default' => 'Cambiar preferencias de notificación predeterminadas', + 'watch_detail_ignore' => 'Ignorando notificaciones', 'watch_detail_new' => 'Suscripciones de nuevas páginas', 'watch_detail_updates' => 'Suscripciones de nuevas páginas y actualizaciones de páginas', 'watch_detail_comments' => 'Suscripciones de nuevas páginas, actualizaciones de páginas y comentarios', 'watch_detail_parent_book' => 'Subscripciones por libro contenedor', 'watch_detail_parent_book_ignore' => 'Ignorando a través del libro contenedor', - 'watch_detail_parent_chapter' => 'Subscripciones por capítulo superior', - 'watch_detail_parent_chapter_ignore' => 'Ignorar por capítulo superior', + 'watch_detail_parent_chapter' => 'Subscripciones por capítulo contenedor', + 'watch_detail_parent_chapter_ignore' => 'Ignorar por capítulo contenedor', ]; diff --git a/lang/es_AR/errors.php b/lang/es_AR/errors.php index e784b5092b2..16a5b5467e3 100644 --- a/lang/es_AR/errors.php +++ b/lang/es_AR/errors.php @@ -10,6 +10,7 @@ // Auth 'error_user_exists_different_creds' => 'Un usuario con el email :email ya existe pero con credenciales diferentes.', + 'auth_pre_register_theme_prevention' => 'La cuenta de usuario no pudo ser registrada con los detalles proporcionados', 'email_already_confirmed' => 'El email ya ha sido confirmado, Intente loguearse en la aplicación.', 'email_confirmation_invalid' => 'Este token de confirmación no e válido o ya ha sido usado,Intente registrar uno nuevamente.', 'email_confirmation_expired' => 'El token de confirmación ha expirado, Un nuevo email de confirmacón ha sido enviado.', @@ -23,7 +24,6 @@ 'saml_invalid_response_id' => 'La solicitud del sistema de autenticación externo no está reconocida por un proceso iniciado por esta aplicación. Navegar hacia atrás después de un inicio de sesión podría causar este problema.', 'saml_fail_authed' => 'El inicio de sesión con :system falló, el sistema no proporcionó una autorización correcta', 'oidc_already_logged_in' => 'Ya está conectado', - 'oidc_user_not_registered' => 'El usuario :name no está registrado y el registro automático está deshabilitado', 'oidc_no_email_address' => 'No se pudo encontrar una dirección de correo electrónico para este usuario en los datos proporcionados por el sistema de autenticación externo', 'oidc_fail_authed' => 'El inicio de sesión con :system falló, el sistema no proporcionó una autorización correcta', 'social_no_action_defined' => 'Acción no definida', @@ -37,6 +37,7 @@ 'social_driver_not_found' => 'Driver social no encontrado', 'social_driver_not_configured' => 'Su configuración :socialAccount no es correcta.', 'invite_token_expired' => 'El enace de la esta invitación expiró. Puede intentar restablecer la contraseña de su cuenta', + 'login_user_not_found' => 'No se pudo encontrar un usuario para esta acción.', // System 'path_not_writable' => 'La ruta :filePath no pudo ser cargada. Asegurese de que es escribible por el servidor.', @@ -77,6 +78,7 @@ // Users 'users_cannot_delete_only_admin' => 'No se puede borrar el único administrador', 'users_cannot_delete_guest' => 'No se puede borrar el usuario invitado', + 'users_could_not_send_invite' => 'No se creó el usuario porque no se pudo enviar el correo de invitación', // Roles 'role_cannot_be_edited' => 'Este rol no puede ser editado', @@ -103,6 +105,18 @@ 'app_down' => 'La aplicación :appName se encuentra caída en este momento', 'back_soon' => 'Volverá a estar operativa en corto tiempo.', + // Import + 'import_zip_cant_read' => 'No se pudo leer el archivo ZIP.', + 'import_zip_cant_decode_data' => 'No se pudo encontrar ni decodificar el contenido del archivo ZIP data.json.', + 'import_zip_no_data' => 'Los datos del archivo ZIP no tienen un libro, un capítulo o contenido de página en su contenido.', + 'import_validation_failed' => 'Error al validar la importación del ZIP con los errores:', + 'import_zip_failed_notification' => 'Error al importar archivo ZIP.', + 'import_perms_books' => 'Le faltan los permisos necesarios para crear libros.', + 'import_perms_chapters' => 'Le faltan los permisos necesarios para crear capítulos.', + 'import_perms_pages' => 'Le faltan los permisos necesarios para crear páginas.', + 'import_perms_images' => 'Le faltan los permisos necesarios para crear imágenes.', + 'import_perms_attachments' => 'Le faltan los permisos necesarios para crear adjuntos.', + // API errors 'api_no_authorization_found' => 'No se encontró ningún token de autorización en la solicitud', 'api_bad_authorization_format' => 'Se ha encontrado un token de autorización en la solicitud pero el formato era incorrecto', @@ -115,5 +129,5 @@ 'maintenance_test_email_failure' => 'Error al enviar un email de prueba:', // HTTP errors - 'http_ssr_url_no_match' => 'La URL no coincide con los hosts SSR permitidos', + 'http_ssr_url_no_match' => 'La URL no coincide con los hosts SSR configurados como permitidos', ]; diff --git a/lang/es_AR/notifications.php b/lang/es_AR/notifications.php index 5ebc42129d9..fc89b2c25ab 100644 --- a/lang/es_AR/notifications.php +++ b/lang/es_AR/notifications.php @@ -5,16 +5,16 @@ return [ 'new_comment_subject' => 'Nuevo comentario en la página: :pageName', - 'new_comment_intro' => 'Un usuario ha comentado en una página de :appName:', - 'new_page_subject' => 'Nueva página: :pageName', - 'new_page_intro' => 'Una nueva página ha sido creada en :appName:', + 'new_comment_intro' => 'Un usuario comentó en una página de :appName:', + 'new_page_subject' => 'Página nueva: :pageName', + 'new_page_intro' => 'Se creó una nueva página en :appName:', 'updated_page_subject' => 'Página actualizada: :pageName', - 'updated_page_intro' => 'Una página ha sido actualizada en :appName:', - 'updated_page_debounce' => 'Para prevenir notificaciones en masa, durante un tiempo no se enviarán notificaciones para futuras ediciones de esta página por el mismo editor.', + 'updated_page_intro' => 'Se actualizó una página en :appName:', + 'updated_page_debounce' => 'Para evitar una avalancha de notificaciones, durante un tiempo no se enviarán notificaciones sobre más ediciones de esta página por el mismo editor.', - 'detail_page_name' => 'Nombre de página:', + 'detail_page_name' => 'Nombre de la página:', 'detail_page_path' => 'Ruta de la página:', - 'detail_commenter' => 'Autor del comentario:', + 'detail_commenter' => 'Comentarista:', 'detail_comment' => 'Comentario:', 'detail_created_by' => 'Creado por:', 'detail_updated_by' => 'Actualizado por:', @@ -22,6 +22,6 @@ 'action_view_comment' => 'Ver comentario', 'action_view_page' => 'Ver página', - 'footer_reason' => 'Esta notificación fue enviada porque :link cubre este tipo de actividad para este artículo.', - 'footer_reason_link' => 'sus preferencias de notificación', + 'footer_reason' => 'Esta notificación le fue enviada porque :link cubre este tipo de actividad para este elemento.', + 'footer_reason_link' => 'nuestras preferencias de notificación', ]; diff --git a/lang/es_AR/preferences.php b/lang/es_AR/preferences.php index f037d0d92a6..11c872021c0 100644 --- a/lang/es_AR/preferences.php +++ b/lang/es_AR/preferences.php @@ -8,7 +8,7 @@ 'my_account' => 'Mi cuenta', 'shortcuts' => 'Atajos', - 'shortcuts_interface' => 'Preferencias de acceso directo en la interfaz', + 'shortcuts_interface' => 'Preferencias de atajos en la interfaz', 'shortcuts_toggle_desc' => 'Aquí puede activar o desactivar los accesos rápidos de la interfaz, utilizados para la navegación y las acciones.', 'shortcuts_customize_desc' => 'Puede personalizar cada uno de los atajos a continuación. Simplemente pulse la combinación de teclas deseada después de seleccionar la entrada para un atajo.', 'shortcuts_toggle_label' => 'Atajos de teclado habilitados', @@ -21,31 +21,31 @@ 'notifications' => 'Preferencias de notificaciones', 'notifications_desc' => 'Controle las notificaciones por correo electrónico que recibe cuando se realiza cierta actividad dentro del sistema.', - 'notifications_opt_own_page_changes' => 'Notificar sobre los cambios en las páginas en las que soy propietario', - 'notifications_opt_own_page_comments' => 'Notificar sobre comentarios en las páginas en las que soy propietario', + 'notifications_opt_own_page_changes' => 'Notificar sobre los cambios en las páginas de las que soy propietario', + 'notifications_opt_own_page_comments' => 'Notificar sobre comentarios en las páginas de las que soy propietario', 'notifications_opt_comment_replies' => 'Notificar sobre respuestas a mis comentarios', 'notifications_save' => 'Guardar preferencias', - 'notifications_update_success' => '¡Se han actualizado las preferencias de notificaciones!', - 'notifications_watched' => 'Elementos vistos e ignorados', - 'notifications_watched_desc' => ' A continuación se muestran los elementos que tienen preferencias personalizadas de monitorización. Para actualizar sus preferencias, vea el artículo y las opciones se mostrarán en la barra lateral.', + 'notifications_update_success' => '¡Se actualizaron las preferencias de notificaciones!', + 'notifications_watched' => 'Elementos vigilados e ignorados', + 'notifications_watched_desc' => 'A continuación se muestran los elementos que tienen preferencias personalizadas de monitorización. Para actualizar sus preferencias, vea el artículo y las opciones se mostrarán en la barra lateral.', 'auth' => 'Acceso y seguridad', 'auth_change_password' => 'Cambiar contraseña', 'auth_change_password_desc' => 'Cambie la contraseña que utiliza para iniciar sesión en la aplicación. Debe tener al menos 8 caracteres.', - 'auth_change_password_success' => '¡La contraseña ha sido actualizada!', + 'auth_change_password_success' => '¡Se actualizó la contraseña!', 'profile' => 'Detalles del perfil', - 'profile_desc' => 'Administre los detalles de su cuenta que le representa a otros usuarios, además de los detalles que se utilizan para la comunicación y la personalización del sistema.', - 'profile_view_public' => 'Ver perfil público', + 'profile_desc' => 'Administre los detalles de su cuenta que lo representan ante otros usuarios, además de los detalles que se utilizan para la comunicación y la personalización del sistema.', + 'profile_view_public' => 'Ver Perfil Público', 'profile_name_desc' => 'Configure el nombre que será visible para otros usuarios del sistema a través de la actividad que realiza, y el contenido que posee.', - 'profile_email_desc' => 'Este correo electrónico se utilizará para las notificaciones y, dependiendo de la autenticación activa del sistema, el acceso del sistema.', + 'profile_email_desc' => 'Este correo electrónico se utilizará para las notificaciones y, dependiendo de la autenticación activa del sistema, para el acceso al sistema.', 'profile_email_no_permission' => 'Lamentablemente no tiene permiso para cambiar su dirección de correo electrónico. Si desea cambiar esto, necesitará pedir a un administrador que lo cambie por usted.', - 'profile_avatar_desc' => 'Seleccione una imagen pública que verán los demás en el sistema. Idealmente esta imagen debe ser cuadrada y alrededor de 256px de anchura y altura.', - 'profile_admin_options' => 'Opciones de administrador', - 'profile_admin_options_desc' => 'Opciones adicionales de administrador, como por ejemplo administrar asignaciones de rol, se pueden encontrar para su cuenta de usuario en el área de "Ajustes > Usuarios" de la aplicación.', + 'profile_avatar_desc' => 'Seleccione una imagen que se utilizará para representarlo ante los demás en el sistema. Idealmente esta imagen debe ser cuadrada y alrededor de 256px de ancho y alto.', + 'profile_admin_options' => 'Opciones de Administrador', + 'profile_admin_options_desc' => 'Opciones adicionales de nivel de administrador, como aquellas para administrar asignaciones de rol, se pueden encontrar para su cuenta de usuario en el área de "Ajustes > Usuarios" de la aplicación.', - 'delete_account' => 'Eliminar cuenta', - 'delete_my_account' => 'Eliminar mi cuenta', + 'delete_account' => 'Eliminar Cuenta', + 'delete_my_account' => 'Eliminar Mi Cuenta', 'delete_my_account_desc' => 'Esto eliminará completamente su cuenta de usuario del sistema. No podrá recuperar esta cuenta o revertir esta acción. El contenido que ha creado, como páginas creadas e imágenes subidas, permanecerá.', 'delete_my_account_warning' => '¿Está seguro de que desea eliminar su cuenta?', ]; diff --git a/lang/es_AR/settings.php b/lang/es_AR/settings.php index 7c1cba813d4..5cbcff86eae 100644 --- a/lang/es_AR/settings.php +++ b/lang/es_AR/settings.php @@ -74,6 +74,36 @@ 'reg_confirm_restrict_domain_desc' => 'Introduzca una lista separada por comas de los correos electrónicos del dominio a los que les gustaría restringir el registro por dominio. A los usuarios les será enviado un correo elctrónico para confirmar la dirección antes de que se le permita interactuar con la aplicación.
Note que a los usuarios se les permitirá cambiar sus direcciones de correo electrónico luego de un registro éxioso.', 'reg_confirm_restrict_domain_placeholder' => 'Ninguna restricción establecida', + // Sorting Settings + 'sorting' => 'Ordenando', + 'sorting_book_default' => 'Orden predeterminado del libro', + 'sorting_book_default_desc' => 'Seleccione la regla de ordenación predeterminada para aplicar a nuevos libros. Esto no afectará a los libros existentes, y puede ser anulado por libro.', + 'sorting_rules' => 'Reglas de Ordenación', + 'sorting_rules_desc' => 'Son operaciones de ordenación predefinidas que se pueden aplicar al contenido en el sistema.', + 'sort_rule_assigned_to_x_books' => 'Asignado a :count libro | Asignado a :count libros', + 'sort_rule_create' => 'Crear regla de ordenación', + 'sort_rule_edit' => 'Editar regla de ordenación', + 'sort_rule_delete' => 'Eliminar regla de ordenación', + 'sort_rule_delete_desc' => 'Eliminar esta regla de ordenación del sistema. Los Libros que utilicen este tipo se revertirán a la ordenación manual.', + 'sort_rule_delete_warn_books' => 'Esta regla de ordenación se utiliza actualmente en :count libro(s). ¿Está seguro que desea eliminarla?', + 'sort_rule_delete_warn_default' => 'Esta regla de ordenación se utiliza actualmente como predeterminada para los libros. ¿Está seguro de que desea eliminarla?', + 'sort_rule_details' => 'Detalles de la regla de ordenación', + 'sort_rule_details_desc' => 'Establezca un nombre para esta regla de ordenación, que aparecerá en las listas cuando los usuarios estén seleccionando un orden.', + 'sort_rule_operations' => 'Operaciones de ordenación', + 'sort_rule_operations_desc' => 'Configure las acciones de ordenación a realizar moviéndolas de la lista de operaciones disponibles. Al usarse, las operaciones se aplicarán en orden, de arriba a abajo. Cualquier cambio realizado aquí se aplicará a todos los libros asignados al guardar.', + 'sort_rule_available_operations' => 'Operaciones disponibles', + 'sort_rule_available_operations_empty' => 'No hay operaciones pendientes', + 'sort_rule_configured_operations' => 'Operaciones configuradas', + 'sort_rule_configured_operations_empty' => 'Arrastrar/añadir operaciones desde la lista de "Operaciones disponibles"', + 'sort_rule_op_asc' => '(Asc)', + 'sort_rule_op_desc' => '(Desc)', + 'sort_rule_op_name' => 'Nombre - Alfabético', + 'sort_rule_op_name_numeric' => 'Nombre - Numérico', + 'sort_rule_op_created_date' => 'Fecha de creación', + 'sort_rule_op_updated_date' => 'Fecha de actualización', + 'sort_rule_op_chapters_first' => 'Capítulos al inicio', + 'sort_rule_op_chapters_last' => 'Capítulos al final', + // Maintenance settings 'maint' => 'Mantenimiento', 'maint_image_cleanup' => 'Limpiar imágenes', @@ -109,7 +139,7 @@ 'recycle_bin_contents_empty' => 'La papelera de reciclaje está vacía', 'recycle_bin_empty' => 'Vaciar Papelera de reciclaje', 'recycle_bin_empty_confirm' => 'Esto destruirá permanentemente todos los elementos de la papelera de reciclaje, incluyendo el contenido existente en cada elemento. ¿Está seguro de que desea vaciar la papelera de reciclaje?', - 'recycle_bin_destroy_confirm' => 'Esta acción eliminará permanentemente este elemento del sistema, junto con los elementos secundarios listados a continuación, y no podrá restaurar este contenido. ¿Está seguro de que desea eliminar permanentemente este elemento?', + 'recycle_bin_destroy_confirm' => 'Esta acción eliminará permanentemente este elemento del sistema, junto con los elementos secundarios listados a continuación, y no podrá restaurar este contenido de nuevo. ¿Está seguro de que desea eliminar permanentemente este elemento?', 'recycle_bin_destroy_list' => 'Elementos a destruir', 'recycle_bin_restore_list' => 'Elementos a restaurar', 'recycle_bin_restore_confirm' => 'Esta acción restaurará el elemento eliminado, incluyendo cualquier elemento secundario, a su ubicación original. Si la ubicación original ha sido eliminada, y ahora está en la papelera de reciclaje, el elemento padre también tendrá que ser restaurado.', @@ -163,6 +193,7 @@ 'role_access_api' => 'API de sistema de acceso', 'role_manage_settings' => 'Gestionar ajustes de activos', 'role_export_content' => 'Exportar contenido', + 'role_import_content' => 'Importar contenido', 'role_editor_change' => 'Cambiar editor de página', 'role_notifications' => 'Recibir y gestionar notificaciones', 'role_asset' => 'Permisos de activos', @@ -194,8 +225,8 @@ 'users_send_invite_text' => 'Puede optar por enviar a este usuario un correo electrónico de invitación que les permita establecer su propia contraseña; de lo contrario, puede establecerla contraseña usted mismo.', 'users_send_invite_option' => 'Enviar correo electrónico de invitación al usuario.', 'users_external_auth_id' => 'ID externo de autenticación', - 'users_external_auth_id_desc' => 'Cuando un sistema de autenticación externa está en uso (como SAML2, OIDC o LDAP) este es el ID que vincula este usuario de BookStack a la cuenta del sistema de autenticación. Puede ignorar este campo si utiliza la autenticación por defecto basada en correo electrónico.', - 'users_password_warning' => 'Solo debe rellenar este campo si desea cambiar la contraseña pora este usuario.', + 'users_external_auth_id_desc' => 'Cuando se usa un sistema de autenticación externa (como SAML2, OIDC o LDAP) este es el ID que vincula este usuario de BookStack a la cuenta del sistema de autenticación. Puede ignorar este campo si utiliza la autenticación por defecto basada en correo electrónico.', + 'users_password_warning' => 'Solo complete lo siguiente si desea cambiar la contraseña para este usuario.', 'users_system_public' => 'Este usuario representa cualquier usuario invitado que visita la aplicación. No puede utilizarse para hacer login sino que es asignado automáticamente.', 'users_delete' => 'Borrar usuario', 'users_delete_named' => 'Borrar usuario :userName', @@ -211,7 +242,7 @@ 'users_preferred_language' => 'Lenguaje preferido', 'users_preferred_language_desc' => 'Esta opción cambiará el idioma de la interfaz de usuario en la aplicación. No afectará al contenido creado por los usuarios.', 'users_social_accounts' => 'Cuentas sociales', - 'users_social_accounts_desc' => 'Ver el estado de las cuentas sociales conectadas para este usuario. Las cuentas sociales se pueden utilizar adicionalmente al sistema de autenticación primaria para el acceso al sistema.', + 'users_social_accounts_desc' => 'Vea el estado de las cuentas sociales conectadas para este usuario. Las cuentas sociales se pueden usar además del sistema de autenticación principal para el acceso al sistema.', 'users_social_accounts_info' => 'Aquí puede conectar sus otras cuentas para un acceso rápido y más fácil. Desconectando una cuenta aquí no revoca accesos ya autorizados. Revoque el acceso desde los ajustes de perfil en la cuenta social conectada.', 'users_social_connect' => 'Conectar cuenta', 'users_social_disconnect' => 'Desconectar cuenta', @@ -220,7 +251,7 @@ 'users_social_connected' => 'La cuenta :socialAccount ha sido exitosamente añadida a su perfil.', 'users_social_disconnected' => 'La cuenta :socialAccount ha sido desconectada exitosamente de su perfil.', 'users_api_tokens' => 'Tokens API', - 'users_api_tokens_desc' => 'Crear y administrar los tokens de acceso utilizados para autenticar con la REST API de BookStack. Los permisos para el API se administran a través del usuario al que pertenece el token.', + 'users_api_tokens_desc' => 'Cree y administre los tokens de acceso utilizados para autenticarse con la API REST de BookStack. Los permisos para la API se gestionan a través del usuario al que pertenece el token.', 'users_api_tokens_none' => 'No se han creado tokens API para este usuario', 'users_api_tokens_create' => 'Crear token', 'users_api_tokens_expires' => 'Expira', @@ -277,6 +308,14 @@ 'webhooks_last_errored' => 'Último error:', 'webhooks_last_error_message' => 'Último mensaje de error:', + // Licensing + 'licenses' => 'Licencias', + 'licenses_desc' => 'Esta página detalla información sobre la licencia de BookStack además de los proyectos y bibliotecas que se utilizan en BookStack. Muchos proyectos enumerados aquí pueden ser utilizados solo en un contexto de desarrollo.', + 'licenses_bookstack' => 'Licencia BookStack', + 'licenses_php' => 'Licencias de Bibliotecas PHP', + 'licenses_js' => 'Licencias de Bibliotecas JavaScript', + 'licenses_other' => 'Otras Licencias', + 'license_details' => 'Detalles de la licencia', //! If editing translations files directly please ignore this in all //! languages apart from en. Content will be auto-copied from en. @@ -288,6 +327,7 @@ 'bs' => 'Bosnio', 'ca' => 'Català', 'cs' => 'Checo', + 'cy' => 'Cymraeg', 'da' => 'Danés', 'de' => 'Alemán (informal)', 'de_informal' => 'Alemán (formal)', @@ -309,6 +349,7 @@ 'lt' => 'Lituano', 'lv' => 'Letón', 'nb' => 'Norsk (Bokmål)', + 'ne' => 'नेपाली', 'nn' => 'Nynorsk', 'nl' => 'Holanda', 'pl' => 'Polaco', diff --git a/lang/es_AR/validation.php b/lang/es_AR/validation.php index 9b2b1b4134a..db16845e030 100644 --- a/lang/es_AR/validation.php +++ b/lang/es_AR/validation.php @@ -105,6 +105,11 @@ 'url' => 'El atributo :attribute tiene un formato inválido.', 'uploaded' => 'El archivo no se pudo subir. Puede ser que el servidor no acepte archivos de este tamaño.', + 'zip_file' => 'El :attribute necesita hacer referencia a un archivo dentro del ZIP.', + 'zip_file_mime' => 'El :attribute necesita hacer referencia a un archivo de tipo :validTypes, encontrado :foundType.', + 'zip_model_expected' => 'Se esperaba un objeto de datos, pero se encontró ":type".', + 'zip_unique' => 'El :attribute debe ser único para el tipo de objeto dentro del ZIP.', + // Custom validation lines 'custom' => [ 'password-confirm' => [ diff --git a/lang/et/activities.php b/lang/et/activities.php index 46812551023..3a86dcd8623 100644 --- a/lang/et/activities.php +++ b/lang/et/activities.php @@ -84,6 +84,14 @@ 'webhook_delete' => 'kustutas veebihaagi', 'webhook_delete_notification' => 'Veebihaak on kustutatud', + // Imports + 'import_create' => 'lisas impordi', + 'import_create_notification' => 'Import on üles laaditud', + 'import_run' => 'muutis importi', + 'import_run_notification' => 'Sisu on imporditud', + 'import_delete' => 'kustutas impordi', + 'import_delete_notification' => 'Import on kustutatud', + // Users 'user_create' => 'lisas kasutaja', 'user_create_notification' => 'Kasutaja on lisatud', @@ -119,6 +127,14 @@ 'comment_update' => 'muutis kommentaari', 'comment_delete' => 'kustutas kommentaari', + // Sort Rules + 'sort_rule_create' => 'lisas sorteerimisreegli', + 'sort_rule_create_notification' => 'Sorteerimisreegel on lisatud', + 'sort_rule_update' => 'muutis sorteerimisreeglit', + 'sort_rule_update_notification' => 'Sorteerimisreegel on muudetud', + 'sort_rule_delete' => 'kustutas sorteerimisreegli', + 'sort_rule_delete_notification' => 'Sorteerimisreegel on kustutatud', + // Other 'permissions_update' => 'muutis õiguseid', ]; diff --git a/lang/et/auth.php b/lang/et/auth.php index 74e19a230ad..014a9f858eb 100644 --- a/lang/et/auth.php +++ b/lang/et/auth.php @@ -91,7 +91,7 @@ 'mfa_option_totp_title' => 'Mobiilirakendus', 'mfa_option_totp_desc' => 'Mitmeastmelise autentimise kasutamiseks on sul vaja TOTP-toega mobiilirakendust, nagu Google Authenticator, Authy või Microsoft Authenticator.', 'mfa_option_backup_codes_title' => 'Varukoodid', - 'mfa_option_backup_codes_desc' => 'Hoiusta kindlas kohas komplekt ühekordseid varukoode, millega saad oma isikut tuvastada.', + 'mfa_option_backup_codes_desc' => 'Genereerib komplekti ühekordseid tagavarakoode, mille abil saad sisselogimisel oma isikut tuvastada. Hoia neid kindlas ja turvalises kohas.', 'mfa_gen_confirm_and_enable' => 'Kinnita ja lülita sisse', 'mfa_gen_backup_codes_title' => 'Varukoodide seadistamine', 'mfa_gen_backup_codes_desc' => 'Hoiusta allolevad koodid turvalises kohas. Saad neid kasutada sisselogimisel sekundaarse autentimismeetodina.', diff --git a/lang/et/common.php b/lang/et/common.php index 465a1a40508..580cdf133c4 100644 --- a/lang/et/common.php +++ b/lang/et/common.php @@ -20,7 +20,7 @@ 'description' => 'Kirjeldus', 'role' => 'Roll', 'cover_image' => 'Kaanepilt', - 'cover_image_description' => 'See pilt peaks olema umbes 440x250 pikslit.', + 'cover_image_description' => 'See pildifail peaks olema umbes 440x250px, ehkki seda skaleeritakse ja lõigatakse vastavalt vajadusele, et see mahuks erinevatesse kasutajaliidestesse, seega tegelikud kuvamõõdud võivad erineda.', // Actions 'actions' => 'Tegevused', @@ -30,6 +30,8 @@ 'create' => 'Lisa', 'update' => 'Uuenda', 'edit' => 'Muuda', + 'archive' => 'Arhiveeri', + 'unarchive' => 'Taasta arhiivist', 'sort' => 'Sorteeri', 'move' => 'Liiguta', 'copy' => 'Kopeeri', @@ -107,4 +109,7 @@ // Not directly used but available for convenience to users. 'privacy_policy' => 'Privaatsus', 'terms_of_service' => 'Kasutustingimused', + + // OpenSearch + 'opensearch_description' => 'Otsi :appName', ]; diff --git a/lang/et/editor.php b/lang/et/editor.php index fecff9af9fe..66561c3c858 100644 --- a/lang/et/editor.php +++ b/lang/et/editor.php @@ -13,6 +13,7 @@ 'cancel' => 'Tühista', 'save' => 'Salvesta', 'close' => 'Sulge', + 'apply' => 'Rakenda', 'undo' => 'Võta tagasi', 'redo' => 'Korda', 'left' => 'Vasakul', @@ -47,6 +48,7 @@ 'superscript' => 'Ülaindeks', 'subscript' => 'Alaindeks', 'text_color' => 'Teksti värv', + 'highlight_color' => 'Esiletõstuvärv', 'custom_color' => 'Kohandatud värv', 'remove_color' => 'Eemalda värv', 'background_color' => 'Taustavärv', @@ -81,6 +83,9 @@ 'table_properties' => 'Tabeli omadused', 'table_properties_title' => 'Tabeli omadused', 'delete_table' => 'Kustuta tabel', + 'table_clear_formatting' => 'Eemalda tabeli vormindus', + 'resize_to_contents' => 'Muuda suurus sisule vastavaks', + 'row_header' => 'Päiserida', 'insert_row_before' => 'Sisesta rida enne', 'insert_row_after' => 'Sisesta rida pärast', 'delete_row' => 'Kustuta rida', @@ -144,6 +149,7 @@ 'url' => 'URL', 'text_to_display' => 'Kuvatav tekst', 'title' => 'Pealkiri', + 'browse_links' => 'Sirve linke', 'open_link' => 'Ava link', 'open_link_in' => 'Ava link...', 'open_link_current' => 'Samas aknas', @@ -160,6 +166,8 @@ 'about' => 'Redaktori info', 'about_title' => 'Info WYSIWYG redaktori kohta', 'editor_license' => 'Redaktori litsents ja autoriõigused', + 'editor_lexical_license' => 'See redaktor on loodud :lexicalLink põhjal, mis on saadaval MIT litsentsi alusel.', + 'editor_lexical_license_link' => 'Täielikud litsentsitingimused on leitavad siin.', 'editor_tiny_license' => 'See redaktor on loodud :tinyLink abil, mis on saadaval MIT litsentsi alusel.', 'editor_tiny_license_link' => 'TinyMCE autoriõigused ja litsents on saadaval siin.', 'save_continue' => 'Salvesta leht ja jätka', diff --git a/lang/et/entities.php b/lang/et/entities.php index cd9606c636a..323985b2d4a 100644 --- a/lang/et/entities.php +++ b/lang/et/entities.php @@ -23,7 +23,7 @@ 'meta_updated' => 'Muudetud :timeLength', 'meta_updated_name' => 'Muudetud :timeLength kasutaja :user poolt', 'meta_owned_name' => 'Kuulub kasutajale :user', - 'meta_reference_count' => 'Referenced by :count item|Referenced by :count items', + 'meta_reference_count' => 'Viidatud :count objekti poolt|Viidatud :count objekti poolt', 'entity_select' => 'Objekti valik', 'entity_select_lack_permission' => 'Sul pole õiguseid selle objekti valimiseks', 'images' => 'Pildid', @@ -39,6 +39,34 @@ 'export_pdf' => 'PDF fail', 'export_text' => 'Tekstifail', 'export_md' => 'Markdown fail', + 'export_zip' => 'Portatiivne ZIP', + 'default_template' => 'Vaikimisi lehe mall', + 'default_template_explain' => 'Vali lehe mall, mida kasutatakse kõigi selle objekti sees loodud lehtede vaikimisi sisuna. Pea meeles, et seda kasutatakse ainult siis, kui lehe loojal on valitud malli vaatamise õigus.', + 'default_template_select' => 'Vali mall', + 'import' => 'Import', + 'import_validate' => 'Valideeri import', + 'import_desc' => 'Impordi raamatud, peatükid ja lehed portatiivse ZIP-formaadi abil samast või erinevast instantsist. Jätkamiseks vali ZIP-fail. Pärast faili üleslaadimist ja valideerimist saad importimise seadistada ja kinnitada.', + 'import_zip_select' => 'Vali ZIP-fail üleslaadimiseks', + 'import_zip_validation_errors' => 'Valitud ZIP-faili valideerimisel tekkisid vead:', + 'import_pending' => 'Ootel impordid', + 'import_pending_none' => 'Ühtegi importi pole alustatud.', + 'import_continue' => 'Jätka importimist', + 'import_continue_desc' => 'Vaata üle sisu, mida üleslaaditud ZIP-failist importida. Kui oled valmis, käivita import, et sisu süsteemi lisada. Eduka impordi korral kustutatakse üleslaaditud ZIP-fail automaatselt.', + 'import_details' => 'Importimise andmed', + 'import_run' => 'Käivita import', + 'import_size' => ':size Imporditud ZIP-faili maht', + 'import_uploaded_at' => 'Üleslaaditud :relativeTime', + 'import_uploaded_by' => 'Üles laadis', + 'import_location' => 'Importimise asukoht', + 'import_location_desc' => 'Vali imporditud sisu jaoks sihtkoht. Sul on vaja valitud asukohas sisu lisamiseks vajalikke õiguseid.', + 'import_delete_confirm' => 'Kas oled kindel, et soovid selle impordi kustutada?', + 'import_delete_desc' => 'See kustutab üleslaaditud ZIP-faili, ja seda ei saa tagasi võtta.', + 'import_errors' => 'Importimise vead', + 'import_errors_desc' => 'Importimisel esinesid järgmised vead:', + 'breadcrumb_siblings_for_page' => 'Sirvi teisi lehti', + 'breadcrumb_siblings_for_chapter' => 'Sirvi teisi peatükke', + 'breadcrumb_siblings_for_book' => 'Sirvi teisi raamatuid', + 'breadcrumb_siblings_for_bookshelf' => 'Sirvi teisi riiuleid', // Permissions and restrictions 'permissions' => 'Õigused', @@ -132,9 +160,6 @@ 'books_edit_named' => 'Muuda raamatut :bookName', 'books_form_book_name' => 'Raamatu pealkiri', 'books_save' => 'Salvesta raamat', - 'books_default_template' => 'Default Page Template', - 'books_default_template_explain' => 'Assign a page template that will be used as the default content for all new pages in this book. Keep in mind this will only be used if the page creator has view access to those chosen template page.', - 'books_default_template_select' => 'Select a template page', 'books_permissions' => 'Raamatu õigused', 'books_permissions_updated' => 'Raamatu õigused muudetud', 'books_empty_contents' => 'Ühtegi lehte ega peatükki pole lisatud.', @@ -145,7 +170,9 @@ 'books_search_this' => 'Otsi sellest raamatust', 'books_navigation' => 'Raamatu sisukord', 'books_sort' => 'Sorteeri raamatu sisu', - 'books_sort_desc' => 'Liiguta peatükke ja lehti raamatu sisu ümber organiseerimiseks. Teisi raamatuid lisades saad peatükke ja lehti lihtsasti raamatute vahel liigutada.', + 'books_sort_desc' => 'Liiguta raamatu sees peatükke ja lehti, et selle sisu ümber organiseerida. Saad lisada teisi raamatuid, mis võimaldab peatükke ja lehti lihtsasti raamatute vahel liigutada. Lisaks saad määrata automaatse sorteerimise reegli, et selle raamatu sisu muudatuste puhul automaatselt järjestada.', + 'books_sort_auto_sort' => 'Automaatne sorteerimine', + 'books_sort_auto_sort_active' => 'Automaatne sorteerimine aktiivne: :sortName', 'books_sort_named' => 'Sorteeri raamat :bookName', 'books_sort_name' => 'Sorteeri nime järgi', 'books_sort_created' => 'Sorteeri loomisaja järgi', @@ -207,7 +234,7 @@ 'pages_delete_draft' => 'Kustuta mustand', 'pages_delete_success' => 'Leht kustutatud', 'pages_delete_draft_success' => 'Mustand kustutatud', - 'pages_delete_warning_template' => 'This page is in active use as a book default page template. These books will no longer have a default page template assigned after this page is deleted.', + 'pages_delete_warning_template' => 'See leht on valitud raamatu või peatüki lehe malliks. Pärast selle lehe kustutamist ei ole vastavatel raamatutel või peatükkidel enam vaikimisi lehe malli.', 'pages_delete_confirm' => 'Kas oled kindel, et soovid selle lehe kustutada?', 'pages_delete_draft_confirm' => 'Kas oled kindel, et soovid selle mustandi kustutada?', 'pages_editing_named' => 'Lehe :pageName muutmine', @@ -224,6 +251,8 @@ 'pages_edit_switch_to_markdown_clean' => '(Puhas sisu)', 'pages_edit_switch_to_markdown_stable' => '(Stabiilne sisu)', 'pages_edit_switch_to_wysiwyg' => 'Kasuta WYSIWYG redaktorit', + 'pages_edit_switch_to_new_wysiwyg' => 'Kasuta uut tekstiredaktorit', + 'pages_edit_switch_to_new_wysiwyg_desc' => '(beetatestimisel)', 'pages_edit_set_changelog' => 'Muudatuste logi', 'pages_edit_enter_changelog_desc' => 'Sisesta tehtud muudatuste lühikirjeldus', 'pages_edit_enter_changelog' => 'Salvesta muudatuste logi', @@ -243,6 +272,7 @@ 'pages_md_insert_drawing' => 'Lisa joonis', 'pages_md_show_preview' => 'Näita eelvaadet', 'pages_md_sync_scroll' => 'Sünkrooni eelvaate kerimine', + 'pages_md_plain_editor' => 'Lihttekstiredaktor', 'pages_drawing_unsaved' => 'Leiti salvestamata joonis', 'pages_drawing_unsaved_confirm' => 'Varasemast ebaõnnestunud salvestuskatsest leiti salvestamata joonis. Kas soovid salvestamata joonise taastada ja selle muutmist jätkata?', 'pages_not_in_chapter' => 'Leht ei kuulu peatüki alla', @@ -367,8 +397,11 @@ 'comment' => 'Kommentaar', 'comments' => 'Kommentaarid', 'comment_add' => 'Lisa kommentaar', + 'comment_none' => 'Pole kommentaare, mida kuvada', 'comment_placeholder' => 'Jäta siia kommentaar', - 'comment_count' => '{0} Kommentaare pole|{1} 1 kommentaar|[2,*] :count kommentaari', + 'comment_thread_count' => ':count kommentaarilõim|:count kommentaarilõime', + 'comment_archived_count' => ':count arhiveeritud', + 'comment_archived_threads' => 'Arhiveeritud lõimed', 'comment_save' => 'Salvesta kommentaar', 'comment_new' => 'Uus kommentaar', 'comment_created' => 'kommenteeris :createDiff', @@ -377,8 +410,14 @@ 'comment_deleted_success' => 'Kommentaar kustutatud', 'comment_created_success' => 'Kommentaar lisatud', 'comment_updated_success' => 'Kommentaar muudetud', + 'comment_archive_success' => 'Kommentaar arhiveeritud', + 'comment_unarchive_success' => 'Kommentaar arhiivist taastatud', + 'comment_view' => 'Vaata kommentaari', + 'comment_jump_to_thread' => 'Hüppa lõime juurde', 'comment_delete_confirm' => 'Kas oled kindel, et soovid selle kommentaari kustutada?', 'comment_in_reply_to' => 'Vastus kommentaarile :commentId', + 'comment_reference' => 'Viide', + 'comment_reference_outdated' => '(Aegunud)', 'comment_editor_explain' => 'Siin on lehele jäetud kommentaarid. Kommentaare saab lisada ja hallata salvestatud lehte vaadates.', // Revision @@ -409,7 +448,7 @@ // References 'references' => 'Viited', 'references_none' => 'Sellele objektile ei ole viiteid.', - 'references_to_desc' => 'Listed below is all the known content in the system that links to this item.', + 'references_to_desc' => 'Allpool on kogu teadaolev sisu süsteemis, mis sellele objektile viitab.', // Watch Options 'watch' => 'Jälgi', diff --git a/lang/et/errors.php b/lang/et/errors.php index 56e8c9cd6de..49e755fccf1 100644 --- a/lang/et/errors.php +++ b/lang/et/errors.php @@ -10,6 +10,7 @@ // Auth 'error_user_exists_different_creds' => 'See e-posti aadress on juba seotud teise kasutajaga.', + 'auth_pre_register_theme_prevention' => 'Etteantud detailidega kasutajakontot ei õnnestunud registreerida', 'email_already_confirmed' => 'E-posti aadress on juba kinnitatud. Proovi sisse logida.', 'email_confirmation_invalid' => 'Kinnituslink ei ole kehtiv või on seda juba kasutatud. Proovi uuesti registreeruda.', 'email_confirmation_expired' => 'Kinnituslink on aegunud. Sulle saadeti aadressi kinnitamiseks uus e-kiri.', @@ -23,7 +24,6 @@ 'saml_invalid_response_id' => 'Välisest autentimissüsteemist tulnud päringut ei algatatud sellest rakendusest. Seda viga võib põhjustada pärast sisselogimist tagasi liikumine.', 'saml_fail_authed' => 'Sisenemine :system kaudu ebaõnnestus, süsteem ei andnud volitust', 'oidc_already_logged_in' => 'Juba sisse logitud', - 'oidc_user_not_registered' => 'Kasutaja :name ei ole registreeritud ning automaatne registreerimine on keelatud', 'oidc_no_email_address' => 'Selle kasutaja e-posti aadressi ei õnnestunud välisest autentimissüsteemist leida', 'oidc_fail_authed' => 'Sisenemine :system kaudu ebaõnnestus, süsteem ei andnud volitust', 'social_no_action_defined' => 'Tegevus on defineerimata', @@ -37,6 +37,7 @@ 'social_driver_not_found' => 'Sotsiaalmeedia kontode draiverit ei leitud', 'social_driver_not_configured' => 'Sinu :socialAccount konto seaded ei ole korrektsed.', 'invite_token_expired' => 'Link on aegunud. Võid selle asemel proovida oma konto parooli lähtestada.', + 'login_user_not_found' => 'Selle tegevuse jaoks ei leitud kasutajat.', // System 'path_not_writable' => 'Faili asukohaga :filePath ei õnnestunud üles laadida. Veendu, et serveril on kirjutusõigused.', @@ -77,6 +78,7 @@ // Users 'users_cannot_delete_only_admin' => 'Ainsat administraatorit ei saa kustutada', 'users_cannot_delete_guest' => 'Külaliskasutajat ei saa kustutada', + 'users_could_not_send_invite' => 'Kasutajat ei õnnestunud luua, kuna kutse e-kirja saatmine ebaõnnestus', // Roles 'role_cannot_be_edited' => 'Seda rolli ei saa muuta', @@ -103,6 +105,18 @@ 'app_down' => ':appName on hetkel maas', 'back_soon' => 'See on varsti tagasi.', + // Import + 'import_zip_cant_read' => 'ZIP-faili lugemine ebaõnnestus.', + 'import_zip_cant_decode_data' => 'ZIP-failist ei leitud data.json sisu.', + 'import_zip_no_data' => 'ZIP-failist ei leitud raamatute, peatükkide või lehtede sisu.', + 'import_validation_failed' => 'Imporditud ZIP-faili valideerimine ebaõnnestus vigadega:', + 'import_zip_failed_notification' => 'ZIP-faili importimine ebaõnnestus.', + 'import_perms_books' => 'Sul puuduvad õigused raamatute lisamiseks.', + 'import_perms_chapters' => 'Sul puuduvad õigused peatükkide lisamiseks.', + 'import_perms_pages' => 'Sul puuduvad õigused lehtede lisamiseks.', + 'import_perms_images' => 'Sul puuduvad õigused piltide lisamiseks.', + 'import_perms_attachments' => 'Sul puuduvad õigused manuste lisamiseks.', + // API errors 'api_no_authorization_found' => 'Päringust ei leitud volitustunnust', 'api_bad_authorization_format' => 'Päringust leiti volitustunnus, aga see ei olnud korrektses formaadis', diff --git a/lang/et/preferences.php b/lang/et/preferences.php index 9ac77a965b6..f4ba5e6aed7 100644 --- a/lang/et/preferences.php +++ b/lang/et/preferences.php @@ -27,7 +27,7 @@ 'notifications_save' => 'Salvesta eelistused', 'notifications_update_success' => 'Teavituste eelistused on salvestatud!', 'notifications_watched' => 'Jälgitud ja ignoreeritud objektid', - 'notifications_watched_desc' => ' Allpool on objektid, millele on määratud kohaldatud jälgimise eelistused. Eelistuste muutmiseks ava vastav objekt ning leia jälgimise valikud külgmenüüs.', + 'notifications_watched_desc' => 'Allpool on objektid, millele on määratud kohaldatud jälgimise eelistused. Eelistuste muutmiseks ava vastav objekt ning leia jälgimise valikud külgmenüüs.', 'auth' => 'Ligipääs ja turvalisus', 'auth_change_password' => 'Muuda parool', diff --git a/lang/et/settings.php b/lang/et/settings.php index 01822ed81d9..003238cfebe 100644 --- a/lang/et/settings.php +++ b/lang/et/settings.php @@ -74,6 +74,36 @@ 'reg_confirm_restrict_domain_desc' => 'Sisesta komaga eraldatud nimekiri e-posti domeenidest, millega soovid registreerumist piirata. Kasutajale saadetakse aadressi kinnitamiseks e-kiri, enne kui neil lubatakse rakendust kasutada.
Pane tähele, et kasutajad saavad pärast edukat registreerumist oma e-posti aadressi muuta.', 'reg_confirm_restrict_domain_placeholder' => 'Piirangut ei ole', + // Sorting Settings + 'sorting' => 'Sorteerimine', + 'sorting_book_default' => 'Vaikimisi raamatu sorteerimine', + 'sorting_book_default_desc' => 'Vali vaikimisi uutele raamatutele rakenduv sorteerimisreegel. See ei mõjuta olemasolevaid raamatuid ning seda saab raamatupõhiselt muuta.', + 'sorting_rules' => 'Sorteerimisreeglid', + 'sorting_rules_desc' => 'Need on eeldefineeritud sorteerimistoimingud, mida saab süsteemis olevale sisule rakendada.', + 'sort_rule_assigned_to_x_books' => 'Määratud :count raamatule|Määratud :count raamatule', + 'sort_rule_create' => 'Lisa sorteerimisreegel', + 'sort_rule_edit' => 'Muuda sorteerimisreeglit', + 'sort_rule_delete' => 'Kustuta sorteerimisreegel', + 'sort_rule_delete_desc' => 'Eemalda sorteerimisreegel süsteemist. Seda reeglit kasutavad raamatud jäävad käsitsi sorteerituks.', + 'sort_rule_delete_warn_books' => 'Seda reeglit kasutab hetkel :count raamat(ut). Kas oled kindel, et soovid selle kustutada?', + 'sort_rule_delete_warn_default' => 'See reegel on hetkel raamatute vaikimisi sorteerimisreegel. Kas oled kindel, et soovid selle kustutada?', + 'sort_rule_details' => 'Sorteerimisreegli andmed', + 'sort_rule_details_desc' => 'Määra sorteerimisreegli nimi, mida kasutatakse reegli valimise loendites.', + 'sort_rule_operations' => 'Sorteerimistoimingud', + 'sort_rule_operations_desc' => 'Seadista sorteerimistoimingud, liigutades neid saadaval toimingute loendist. Toimingud rakendatakse järjest, ülevalt alla. Siin tehtud muudatused rakenduvad salvestamisel kõigile määratud raamatutele.', + 'sort_rule_available_operations' => 'Saadaval toimingud', + 'sort_rule_available_operations_empty' => 'Rohkem toiminguid pole', + 'sort_rule_configured_operations' => 'Seadistatud toimingud', + 'sort_rule_configured_operations_empty' => 'Lisa toiminguid "Saadaval toimingud" loendist', + 'sort_rule_op_asc' => '(kasvavalt)', + 'sort_rule_op_desc' => '(kahanevalt)', + 'sort_rule_op_name' => 'Nimi - tähestikuline', + 'sort_rule_op_name_numeric' => 'Nimi - numbriline', + 'sort_rule_op_created_date' => 'Loomise aeg', + 'sort_rule_op_updated_date' => 'Muutmise aeg', + 'sort_rule_op_chapters_first' => 'Peatükid eespool', + 'sort_rule_op_chapters_last' => 'Peatükid tagapool', + // Maintenance settings 'maint' => 'Hooldus', 'maint_image_cleanup' => 'Pildifailide koristus', @@ -109,7 +139,7 @@ 'recycle_bin_contents_empty' => 'Prügikast on hetkel tühi', 'recycle_bin_empty' => 'Tühjenda prügikast', 'recycle_bin_empty_confirm' => 'See kustutab lõplikult kõik objektid prügikastis, kaasa arvatud nende sisu. Kas oled kindel, et soovid prügikasti tühjendada?', - 'recycle_bin_destroy_confirm' => 'See kustutab lõplikult valitud objekti koos loetletud alamobjektidega, ja seda sisu ei ole enam võimalik taastada. Kas oled kindel, et soovid selle objekti kustutada?', + 'recycle_bin_destroy_confirm' => 'See kustutab lõplikult süsteemist valitud objekti koos loetletud alamobjektidega, ja seda sisu ei ole enam võimalik taastada. Kas oled kindel, et soovid selle objekti kustutada?', 'recycle_bin_destroy_list' => 'Kustutatavad objektid', 'recycle_bin_restore_list' => 'Taastatavad objektid', 'recycle_bin_restore_confirm' => 'See taastab valitud objekti koos kõigi alamobjektidega nende algsesse asukohta. Kui see asukoht on ka vahepeal kustutatud ja on nüüd prügikastis, tuleb ka see taastada.', @@ -162,6 +192,7 @@ 'role_access_api' => 'Süsteemi API ligipääs', 'role_manage_settings' => 'Rakenduse seadete haldamine', 'role_export_content' => 'Sisu eksport', + 'role_import_content' => 'Imporditud sisu', 'role_editor_change' => 'Lehe redaktori muutmine', 'role_notifications' => 'Võta vastu ja halda teavitusi', 'role_asset' => 'Sisu õigused', @@ -276,6 +307,14 @@ 'webhooks_last_errored' => 'Viimati ebaõnnestunud:', 'webhooks_last_error_message' => 'Viimane veateade:', + // Licensing + 'licenses' => 'Litsentsid', + 'licenses_desc' => 'See leht koondab litsentsiinfot BookStack\'i ja selles kasutatud projektide ja teekide kohta. Paljusid loetletud teekidest võidakse kasutada ainult arenduse kontekstis.', + 'licenses_bookstack' => 'BookStack\'i litsents', + 'licenses_php' => 'PHP teekide litsentsid', + 'licenses_js' => 'JavaScript teekide litsentsid', + 'licenses_other' => 'Muud litsentsid', + 'license_details' => 'Litsentsi detailid', //! If editing translations files directly please ignore this in all //! languages apart from en. Content will be auto-copied from en. @@ -287,6 +326,7 @@ 'bs' => 'Bosanski (bosnia keel)', 'ca' => 'Català (katalaani keel)', 'cs' => 'Česky (tšehhi keel)', + 'cy' => 'Cymraeg', 'da' => 'Dansk (taani keel)', 'de' => 'Deutsch (saksa keel)', 'de_informal' => 'Deutsch (Du) (mitteformaalne saksa keel)', @@ -308,6 +348,7 @@ 'lt' => 'Lietuvių Kalba (leedu keel)', 'lv' => 'Latviešu Valoda (läti keel)', 'nb' => 'Norsk (Bokmål) (norra keel)', + 'ne' => 'नेपाली', 'nn' => 'Nynorsk', 'nl' => 'Nederlands (hollandi keel)', 'pl' => 'Polski (poola keel)', diff --git a/lang/et/validation.php b/lang/et/validation.php index 5f7f6f3aa39..1354d1ed489 100644 --- a/lang/et/validation.php +++ b/lang/et/validation.php @@ -105,6 +105,11 @@ 'url' => ':attribute on vigases formaadis.', 'uploaded' => 'Faili üleslaadimine ebaõnnestus. Server ei pruugi sellise suurusega faile vastu võtta.', + 'zip_file' => ':attribute peab viitama failile ZIP-arhiivi sees.', + 'zip_file_mime' => ':attribute peab viitama :validTypes tüüpi failile, leiti :foundType.', + 'zip_model_expected' => 'Oodatud andmete asemel leiti ":type".', + 'zip_unique' => ':attribute peab olema ZIP-arhiivi piires objekti tüübile unikaalne.', + // Custom validation lines 'custom' => [ 'password-confirm' => [ diff --git a/lang/eu/activities.php b/lang/eu/activities.php index 2e9f586a838..847014efe74 100644 --- a/lang/eu/activities.php +++ b/lang/eu/activities.php @@ -84,6 +84,14 @@ 'webhook_delete' => 'webhook ezabatua', 'webhook_delete_notification' => 'Webhook egoki ezabatua', + // Imports + 'import_create' => 'created import', + 'import_create_notification' => 'Import successfully uploaded', + 'import_run' => 'updated import', + 'import_run_notification' => 'Content successfully imported', + 'import_delete' => 'deleted import', + 'import_delete_notification' => 'Import successfully deleted', + // Users 'user_create' => 'created user', 'user_create_notification' => 'User successfully created', @@ -93,11 +101,11 @@ 'user_delete_notification' => 'Erabiltzailea egoki ezabatua', // API Tokens - 'api_token_create' => 'created api token', + 'api_token_create' => 'created API token', 'api_token_create_notification' => 'API token successfully created', - 'api_token_update' => 'updated api token', + 'api_token_update' => 'updated API token', 'api_token_update_notification' => 'API token successfully updated', - 'api_token_delete' => 'deleted api token', + 'api_token_delete' => 'deleted API token', 'api_token_delete_notification' => 'API token successfully deleted', // Roles @@ -119,6 +127,14 @@ 'comment_update' => 'updated comment', 'comment_delete' => 'deleted comment', + // Sort Rules + 'sort_rule_create' => 'created sort rule', + 'sort_rule_create_notification' => 'Sort rule successfully created', + 'sort_rule_update' => 'updated sort rule', + 'sort_rule_update_notification' => 'Sort rule successfully updated', + 'sort_rule_delete' => 'deleted sort rule', + 'sort_rule_delete_notification' => 'Sort rule successfully deleted', + // Other 'permissions_update' => 'eguneratu baimenak', ]; diff --git a/lang/eu/auth.php b/lang/eu/auth.php index d1f37804b6d..35cbafd721b 100644 --- a/lang/eu/auth.php +++ b/lang/eu/auth.php @@ -91,7 +91,7 @@ 'mfa_option_totp_title' => 'Aplikazio mugikorra', 'mfa_option_totp_desc' => 'To use multi-factor authentication you\'ll need a mobile application that supports TOTP such as Google Authenticator, Authy or Microsoft Authenticator.', 'mfa_option_backup_codes_title' => 'Backup Codes', - 'mfa_option_backup_codes_desc' => 'Securely store a set of one-time-use backup codes which you can enter to verify your identity.', + 'mfa_option_backup_codes_desc' => 'Generates a set of one-time-use backup codes which you\'ll enter on login to verify your identity. Make sure to store these in a safe & secure place.', 'mfa_gen_confirm_and_enable' => 'Baieztatu eta gaitu', 'mfa_gen_backup_codes_title' => 'Backup Codes Setup', 'mfa_gen_backup_codes_desc' => 'Store the below list of codes in a safe place. When accessing the system you\'ll be able to use one of the codes as a second authentication mechanism.', diff --git a/lang/eu/common.php b/lang/eu/common.php index e2de6ae9f81..f3696825e57 100644 --- a/lang/eu/common.php +++ b/lang/eu/common.php @@ -20,7 +20,7 @@ 'description' => 'Deskribapena', 'role' => 'Rola', 'cover_image' => 'Azaleko irudia', - 'cover_image_description' => 'Irudiaren tamaina 440x250px antzekoa izan beharko litzake.', + 'cover_image_description' => 'This image should be approximately 440x250px although it will be flexibly scaled & cropped to fit the user interface in different scenarios as required, so actual dimensions for display will differ.', // Actions 'actions' => 'Ekintzak', @@ -30,6 +30,8 @@ 'create' => 'Sortu', 'update' => 'Eguneratu', 'edit' => 'Editatu', + 'archive' => 'Archive', + 'unarchive' => 'Un-Archive', 'sort' => 'Ordenatu', 'move' => 'Mugitu', 'copy' => 'Kopiatu', @@ -107,4 +109,7 @@ // Not directly used but available for convenience to users. 'privacy_policy' => 'Pribatutasun politika', 'terms_of_service' => 'Zerbitzu-baldintzak', + + // OpenSearch + 'opensearch_description' => 'Search :appName', ]; diff --git a/lang/eu/editor.php b/lang/eu/editor.php index 6972b991eb2..3ec398cb7d5 100644 --- a/lang/eu/editor.php +++ b/lang/eu/editor.php @@ -13,6 +13,7 @@ 'cancel' => 'Ezeztatu', 'save' => 'Gorde', 'close' => 'Itxi', + 'apply' => 'Apply', 'undo' => 'Desegin', 'redo' => 'Berregin', 'left' => 'Ezkerra', @@ -47,6 +48,7 @@ 'superscript' => 'Gain-eskripta', 'subscript' => 'Azpi-script', 'text_color' => 'Testuaren kolorea', + 'highlight_color' => 'Highlight color', 'custom_color' => 'Kolore pertsonalizatua', 'remove_color' => 'Kolorea ezabatu', 'background_color' => 'Atzeko planoaren kolorea', @@ -81,6 +83,9 @@ 'table_properties' => 'Taularen propietateak', 'table_properties_title' => 'Taularen propietateak', 'delete_table' => 'Ezabatu taula', + 'table_clear_formatting' => 'Clear table formatting', + 'resize_to_contents' => 'Resize to contents', + 'row_header' => 'Row header', 'insert_row_before' => 'Insert row before', 'insert_row_after' => 'Insert row after', 'delete_row' => 'Delete row', @@ -144,6 +149,7 @@ 'url' => 'URL', 'text_to_display' => 'Text to display', 'title' => 'Title', + 'browse_links' => 'Browse links', 'open_link' => 'Open link', 'open_link_in' => 'Open link in...', 'open_link_current' => 'Current window', @@ -160,6 +166,8 @@ 'about' => 'About the editor', 'about_title' => 'About the WYSIWYG Editor', 'editor_license' => 'Editor License & Copyright', + 'editor_lexical_license' => 'This editor is built as a fork of :lexicalLink which is distributed under the MIT license.', + 'editor_lexical_license_link' => 'Full license details can be found here.', 'editor_tiny_license' => 'This editor is built using :tinyLink which is provided under the MIT license.', 'editor_tiny_license_link' => 'The copyright and license details of TinyMCE can be found here.', 'save_continue' => 'Save Page & Continue', diff --git a/lang/eu/entities.php b/lang/eu/entities.php index d3582283f4f..cc51012d50f 100644 --- a/lang/eu/entities.php +++ b/lang/eu/entities.php @@ -39,6 +39,34 @@ 'export_pdf' => 'PDF fitxategia', 'export_text' => 'Testu lauko fitxategiak', 'export_md' => 'Markdown fitxategia', + 'export_zip' => 'Portable ZIP', + 'default_template' => 'Default Page Template', + 'default_template_explain' => 'Assign a page template that will be used as the default content for all pages created within this item. Keep in mind this will only be used if the page creator has view access to the chosen template page.', + 'default_template_select' => 'Select a template page', + 'import' => 'Import', + 'import_validate' => 'Validate Import', + 'import_desc' => 'Import books, chapters & pages using a portable zip export from the same, or a different, instance. Select a ZIP file to proceed. After the file has been uploaded and validated you\'ll be able to configure & confirm the import in the next view.', + '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_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_size' => ':size Import ZIP Size', + 'import_uploaded_at' => 'Uploaded :relativeTime', + 'import_uploaded_by' => 'Uploaded by', + 'import_location' => 'Import Location', + '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_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', + 'breadcrumb_siblings_for_book' => 'Navigate siblings for book', + 'breadcrumb_siblings_for_bookshelf' => 'Navigate siblings for shelf', // Permissions and restrictions 'permissions' => 'Baimenak', @@ -132,9 +160,6 @@ 'books_edit_named' => 'Editatu :bookName liburua', 'books_form_book_name' => 'Liburu izena', 'books_save' => 'Gorde Liburua', - 'books_default_template' => 'Default Page Template', - 'books_default_template_explain' => 'Assign a page template that will be used as the default content for all new pages in this book. Keep in mind this will only be used if the page creator has view access to those chosen template page.', - 'books_default_template_select' => 'Select a template page', 'books_permissions' => 'Liburu baimenak', 'books_permissions_updated' => 'Liburu baimenak eguneratuta', 'books_empty_contents' => 'Ez da orri edo kapitulurik sortu liburu honentzat.', @@ -145,7 +170,9 @@ 'books_search_this' => 'Bilatu liburu hau', 'books_navigation' => 'Liburu nabigazioa', 'books_sort' => 'Ordenatu liburu edukiak', - 'books_sort_desc' => 'Move chapters and pages within a book to reorganise its contents. Other books can be added which allows easy moving of chapters and pages between books.', + 'books_sort_desc' => 'Move chapters and pages within a book to reorganise its contents. Other books can be added which allows easy moving of chapters and pages between books. Optionally an auto sort rule can be set to automatically sort this book\'s contents upon changes.', + 'books_sort_auto_sort' => 'Auto Sort Option', + 'books_sort_auto_sort_active' => 'Auto Sort Active: :sortName', 'books_sort_named' => 'Ordenatu :bookName liburua', 'books_sort_name' => 'Ordenatu izenaren arabera', 'books_sort_created' => 'Ordenatu argitaratze-dataren arabera', @@ -207,7 +234,7 @@ 'pages_delete_draft' => 'Delete Draft Page', 'pages_delete_success' => 'Orria ezabatua', 'pages_delete_draft_success' => 'Draft page deleted', - 'pages_delete_warning_template' => 'This page is in active use as a book default page template. These books will no longer have a default page template assigned after this page is deleted.', + 'pages_delete_warning_template' => 'This page is in active use as a book or chapter default page template. These books or chapters will no longer have a default page template assigned after this page is deleted.', 'pages_delete_confirm' => 'Ziur al zaude orri hau ezabatu nahi duzula?', 'pages_delete_draft_confirm' => 'Are you sure you want to delete this draft page?', 'pages_editing_named' => 'Editing Page :pageName', @@ -224,6 +251,8 @@ 'pages_edit_switch_to_markdown_clean' => '(Clean Content)', 'pages_edit_switch_to_markdown_stable' => '(Stable Content)', 'pages_edit_switch_to_wysiwyg' => 'Switch to WYSIWYG Editor', + 'pages_edit_switch_to_new_wysiwyg' => 'Switch to new WYSIWYG', + 'pages_edit_switch_to_new_wysiwyg_desc' => '(In Beta Testing)', 'pages_edit_set_changelog' => 'Set Changelog', 'pages_edit_enter_changelog_desc' => 'Enter a brief description of the changes you\'ve made', 'pages_edit_enter_changelog' => 'Enter Changelog', @@ -243,6 +272,7 @@ 'pages_md_insert_drawing' => 'Txertatu marrazki berria', 'pages_md_show_preview' => 'Show preview', 'pages_md_sync_scroll' => 'Sync preview scroll', + 'pages_md_plain_editor' => 'Plaintext editor', 'pages_drawing_unsaved' => 'Unsaved Drawing Found', 'pages_drawing_unsaved_confirm' => 'Unsaved drawing data was found from a previous failed drawing save attempt. Would you like to restore and continue editing this unsaved drawing?', 'pages_not_in_chapter' => 'Page is not in a chapter', @@ -367,8 +397,11 @@ 'comment' => 'Iruzkina', 'comments' => 'Iruzkinak', 'comment_add' => 'Iruzkina gehitu', + 'comment_none' => 'No comments to display', 'comment_placeholder' => 'Utzi iruzkin bat hemen', - 'comment_count' => '{0} No Comments|{1} 1 Comment|[2,*] :count Comments', + 'comment_thread_count' => ':count Comment Thread|:count Comment Threads', + 'comment_archived_count' => ':count Archived', + 'comment_archived_threads' => 'Archived Threads', 'comment_save' => 'Iruzkina gorde', 'comment_new' => 'Iruzkin berria', 'comment_created' => 'commented :createDiff', @@ -377,8 +410,14 @@ 'comment_deleted_success' => 'Comment deleted', 'comment_created_success' => 'Iruzkina gehituta', 'comment_updated_success' => 'Iruzkina gehituta', + 'comment_archive_success' => 'Comment archived', + 'comment_unarchive_success' => 'Comment un-archived', + 'comment_view' => 'View comment', + 'comment_jump_to_thread' => 'Jump to thread', 'comment_delete_confirm' => 'Ziur zaude iruzkin hau ezabatu nahi duzula?', 'comment_in_reply_to' => 'In reply to :commentId', + 'comment_reference' => 'Reference', + 'comment_reference_outdated' => '(Outdated)', 'comment_editor_explain' => 'Here are the comments that have been left on this page. Comments can be added & managed when viewing the saved page.', // Revision diff --git a/lang/eu/errors.php b/lang/eu/errors.php index 2244e0142cd..2a747e5f8bb 100644 --- a/lang/eu/errors.php +++ b/lang/eu/errors.php @@ -10,6 +10,7 @@ // Auth 'error_user_exists_different_creds' => ':email kontuakin erabiltzaile bat badago, baina kredentzial ezberdinekin.', + 'auth_pre_register_theme_prevention' => 'User account could not be registered for the provided details', 'email_already_confirmed' => 'Email kontua berretsita dago, saiatu saioa hasten.', 'email_confirmation_invalid' => 'Berrezpen token hau ez da baliozkoa eta iada erabiltzen da, mesedez, saiatu berriz erregistroa burutzen.', 'email_confirmation_expired' => 'Berrezpen tokena iraungi da, berrezpen email berri bnat bidali da.', @@ -23,7 +24,6 @@ 'saml_invalid_response_id' => 'Kanpoko egiazkotasun-sistemaren eskaria ez du onartzen aplikazio honek abiarazitako prozesu batek. Loginean atzera egitea izan daiteke arrazoia.', 'saml_fail_authed' => 'Login using :system failed, system did not provide successful authorization', 'oidc_already_logged_in' => 'Dagoeneko saioa hasita', - 'oidc_user_not_registered' => 'The user :name is not registered and automatic registration is disabled', 'oidc_no_email_address' => 'Could not find an email address, for this user, in the data provided by the external authentication system', 'oidc_fail_authed' => 'Login using :system failed, system did not provide successful authorization', 'social_no_action_defined' => 'No action defined', @@ -37,6 +37,7 @@ 'social_driver_not_found' => 'Social driver not found', 'social_driver_not_configured' => 'Your :socialAccount social settings are not configured correctly.', 'invite_token_expired' => 'This invitation link has expired. You can instead try to reset your account password.', + 'login_user_not_found' => 'A user for this action could not be found.', // System 'path_not_writable' => 'File path :filePath could not be uploaded to. Ensure it is writable to the server.', @@ -77,6 +78,7 @@ // Users 'users_cannot_delete_only_admin' => 'You cannot delete the only admin', 'users_cannot_delete_guest' => 'You cannot delete the guest user', + 'users_could_not_send_invite' => 'Could not create user since invite email failed to send', // Roles 'role_cannot_be_edited' => 'This role cannot be edited', @@ -103,6 +105,18 @@ 'app_down' => ':appName is down right now', 'back_soon' => 'It will be back up soon.', + // Import + 'import_zip_cant_read' => 'Could not read ZIP file.', + 'import_zip_cant_decode_data' => 'Could not find and decode ZIP data.json content.', + 'import_zip_no_data' => 'ZIP file data has no expected book, chapter or page content.', + 'import_validation_failed' => 'Import ZIP failed to validate with errors:', + 'import_zip_failed_notification' => 'Failed to import ZIP file.', + 'import_perms_books' => 'You are lacking the required permissions to create books.', + 'import_perms_chapters' => 'You are lacking the required permissions to create chapters.', + 'import_perms_pages' => 'You are lacking the required permissions to create pages.', + 'import_perms_images' => 'You are lacking the required permissions to create images.', + 'import_perms_attachments' => 'You are lacking the required permission to create attachments.', + // API errors 'api_no_authorization_found' => 'No authorization token found on the request', 'api_bad_authorization_format' => 'An authorization token was found on the request but the format appeared incorrect', diff --git a/lang/eu/preferences.php b/lang/eu/preferences.php index 3e83a9784d6..da7638593b5 100644 --- a/lang/eu/preferences.php +++ b/lang/eu/preferences.php @@ -27,7 +27,7 @@ 'notifications_save' => 'Save Preferences', 'notifications_update_success' => 'Notification preferences have been updated!', 'notifications_watched' => 'Watched & Ignored Items', - 'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.', + 'notifications_watched_desc' => 'Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.', 'auth' => 'Access & Security', 'auth_change_password' => 'Change Password', diff --git a/lang/eu/settings.php b/lang/eu/settings.php index 0e6fa244419..e93f3c96469 100644 --- a/lang/eu/settings.php +++ b/lang/eu/settings.php @@ -74,6 +74,36 @@ 'reg_confirm_restrict_domain_desc' => 'Enter a comma separated list of email domains you would like to restrict registration to. Users will be sent an email to confirm their address before being allowed to interact with the application.
Note that users will be able to change their email addresses after successful registration.', 'reg_confirm_restrict_domain_placeholder' => 'Mugarik gabe', + // Sorting Settings + 'sorting' => 'Sorting', + 'sorting_book_default' => 'Default Book Sort', + 'sorting_book_default_desc' => 'Select the default sort rule to apply to new books. This won\'t affect existing books, and can be overridden per-book.', + 'sorting_rules' => 'Sort Rules', + 'sorting_rules_desc' => 'These are predefined sorting operations which can be applied to content in the system.', + 'sort_rule_assigned_to_x_books' => 'Assigned to :count Book|Assigned to :count Books', + 'sort_rule_create' => 'Create Sort Rule', + 'sort_rule_edit' => 'Edit Sort Rule', + 'sort_rule_delete' => 'Delete Sort Rule', + 'sort_rule_delete_desc' => 'Remove this sort rule from the system. Books using this sort will revert to manual sorting.', + 'sort_rule_delete_warn_books' => 'This sort rule is currently used on :count book(s). Are you sure you want to delete this?', + 'sort_rule_delete_warn_default' => 'This sort rule is currently used as the default for books. Are you sure you want to delete this?', + 'sort_rule_details' => 'Sort Rule Details', + 'sort_rule_details_desc' => 'Set a name for this sort rule, which will appear in lists when users are selecting a sort.', + 'sort_rule_operations' => 'Sort Operations', + 'sort_rule_operations_desc' => 'Configure the sort actions to be performed by moving them from the list of available operations. Upon use, the operations will be applied in order, from top to bottom. Any changes made here will be applied to all assigned books upon save.', + 'sort_rule_available_operations' => 'Available Operations', + 'sort_rule_available_operations_empty' => 'No operations remaining', + 'sort_rule_configured_operations' => 'Configured Operations', + 'sort_rule_configured_operations_empty' => 'Drag/add operations from the "Available Operations" list', + 'sort_rule_op_asc' => '(Asc)', + 'sort_rule_op_desc' => '(Desc)', + 'sort_rule_op_name' => 'Name - Alphabetical', + 'sort_rule_op_name_numeric' => '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_last' => 'Chapters Last', + // Maintenance settings 'maint' => 'Mantentze-lanak', 'maint_image_cleanup' => 'Garbitu irudiak', @@ -109,7 +139,7 @@ 'recycle_bin_contents_empty' => 'Zakarrontzia hutsik dago', 'recycle_bin_empty' => 'Hustu Zakarrontzia', 'recycle_bin_empty_confirm' => 'This will permanently destroy all items in the recycle bin including content contained within each item. Are you sure you want to empty the recycle bin?', - 'recycle_bin_destroy_confirm' => 'This action will permanently delete this item, along with any child elements listed below, from the system and you will not be able to restore this content. Are you sure you want to permanently delete this item?', + 'recycle_bin_destroy_confirm' => 'This action will permanently delete this item from the system, along with any child elements listed below, and you will not be able to restore this content. Are you sure you want to permanently delete this item?', 'recycle_bin_destroy_list' => 'Ezabatuko diren elementuak', 'recycle_bin_restore_list' => 'Berrezarriko diren elementuak', 'recycle_bin_restore_confirm' => 'This action will restore the deleted item, including any child elements, to their original location. If the original location has since been deleted, and is now in the recycle bin, the parent item will also need to be restored.', @@ -162,6 +192,7 @@ 'role_access_api' => 'Sistemako APIra sarrera', 'role_manage_settings' => 'Kudeatu aplikazio ezarpenak', 'role_export_content' => 'Exportatu edukia', + 'role_import_content' => 'Import content', 'role_editor_change' => 'Change page editor', 'role_notifications' => 'Receive & manage notifications', 'role_asset' => 'Fitxategi baimenak', @@ -276,6 +307,14 @@ 'webhooks_last_errored' => 'Last Errored:', 'webhooks_last_error_message' => 'Last Error Message:', + // Licensing + 'licenses' => 'Licenses', + 'licenses_desc' => 'This page details license information for BookStack in addition to the projects & libraries that are used within BookStack. Many projects listed may only be used in a development context.', + 'licenses_bookstack' => 'BookStack License', + 'licenses_php' => 'PHP Library Licenses', + 'licenses_js' => 'JavaScript Library Licenses', + 'licenses_other' => 'Other Licenses', + 'license_details' => 'License Details', //! If editing translations files directly please ignore this in all //! languages apart from en. Content will be auto-copied from en. @@ -287,6 +326,7 @@ 'bs' => 'Bosanski', 'ca' => 'Català', 'cs' => 'Česky', + 'cy' => 'Cymraeg', 'da' => 'Dansk', 'de' => 'Deutsch (Sie)', 'de_informal' => 'Deutsch (Du)', @@ -308,6 +348,7 @@ 'lt' => 'Lietuvių Kalba', 'lv' => 'Latviešu Valoda', 'nb' => 'Norsk (Bokmål)', + 'ne' => 'नेपाली', 'nn' => 'Nynorsk', 'nl' => 'Nederlands', 'pl' => 'Polski', diff --git a/lang/eu/validation.php b/lang/eu/validation.php index cd7ba387dfa..f79dc852f3e 100644 --- a/lang/eu/validation.php +++ b/lang/eu/validation.php @@ -105,6 +105,11 @@ 'url' => 'The :attribute format is invalid.', 'uploaded' => 'The file could not be uploaded. The server may not accept files of this size.', + 'zip_file' => 'The :attribute needs to reference a file within the ZIP.', + 'zip_file_mime' => 'The :attribute needs to reference a file of type :validTypes, found :foundType.', + 'zip_model_expected' => 'Data object expected but ":type" found.', + 'zip_unique' => 'The :attribute must be unique for the object type within the ZIP.', + // Custom validation lines 'custom' => [ 'password-confirm' => [ diff --git a/lang/fa/activities.php b/lang/fa/activities.php index 7b536bf2dc5..07eb78f3709 100644 --- a/lang/fa/activities.php +++ b/lang/fa/activities.php @@ -6,7 +6,7 @@ return [ // Pages - 'page_create' => 'تاریخ ایجاد', + 'page_create' => 'صفحه ایجاد شد', 'page_create_notification' => 'صفحه با موفقیت ایجاد شد', 'page_update' => 'به روزرسانی صفحه', 'page_update_notification' => 'صفحه با موفقیت به روزرسانی شد', @@ -84,6 +84,14 @@ 'webhook_delete' => 'حذف وب هوک', 'webhook_delete_notification' => 'وب هوک با موفقیت حذف شد', + // Imports + 'import_create' => 'ورودی ایجاد شد', + 'import_create_notification' => 'فایل با موفقیت آپلود شد', + 'import_run' => 'آیتم واردشده بروزرسانی شد', + 'import_run_notification' => 'محتوا با موفقیت انتقال یافت', + 'import_delete' => 'آیتم ورودی حدف شده', + 'import_delete_notification' => 'آیتم واردشده با موفقیت حذف شد', + // Users 'user_create' => 'کاربر ایجاد شده', 'user_create_notification' => 'کاربر با موفقیت به ایجاد شد', @@ -93,7 +101,7 @@ 'user_delete_notification' => 'کاربر با موفقیت حذف شد', // API Tokens - 'api_token_create' => 'توکن api ایجاد شده', + 'api_token_create' => 'ایجاد توکن API', 'api_token_create_notification' => 'توکن api با موفقیت ایجاد شد', 'api_token_update' => 'توکن api بروز شده', 'api_token_update_notification' => 'توکن API با موفقیت بروزرسانی شد', @@ -119,6 +127,14 @@ 'comment_update' => 'نظر به روز شده', 'comment_delete' => 'نظر حذف شده', + // Sort Rules + 'sort_rule_create' => 'قانون مرتب‌سازی ایجاد شد', + 'sort_rule_create_notification' => 'قانون مرتب‌سازی با موفقیت ایجاد شد', + 'sort_rule_update' => 'قانون مرتب‌سازی به‌روزرسانی شد', + 'sort_rule_update_notification' => 'قانون مرتب‌سازی با موفقیت به‌روزرسانی شد', + 'sort_rule_delete' => 'قانون مرتب‌سازی حذف شد', + 'sort_rule_delete_notification' => 'قانون مرتب‌سازی با موفقیت حذف شد', + // Other 'permissions_update' => 'به روزرسانی مجوزها', ]; diff --git a/lang/fa/auth.php b/lang/fa/auth.php index f4b30d43892..c92f5f22b8a 100644 --- a/lang/fa/auth.php +++ b/lang/fa/auth.php @@ -91,7 +91,7 @@ 'mfa_option_totp_title' => 'برنامه ی موبایل', 'mfa_option_totp_desc' => 'برای استفاده از احراز هویت چند عاملی به یک برنامه موبایلی نیاز دارید که از TOTP پشتیبانی کند، مانند Google Authenticator، Authy یا Microsoft Authenticator.', 'mfa_option_backup_codes_title' => 'کدهای پشتیبان', - 'mfa_option_backup_codes_desc' => 'مجموعه ای از کدهای پشتیبان یکبار مصرف را ایمن ذخیره کنید که می توانید برای تأیید هویت خود وارد کنید.', + 'mfa_option_backup_codes_desc' => 'این فرایند مجموعه‌ای از کدهای پشتیبان یک‌بار مصرف تولید می‌کند که هنگام ورود به سامانه جهت تأیید هویت باید از آن‌ها استفاده کنید. توصیه می‌شود این کدها را در محلّی امن و محفوظ نگهداری نمایید.', 'mfa_gen_confirm_and_enable' => 'تایید و فعال کنید', 'mfa_gen_backup_codes_title' => 'راه اندازی کدهای پشتیبان', 'mfa_gen_backup_codes_desc' => 'لیست کدهای زیر را در مکانی امن ذخیره کنید. هنگام دسترسی به سیستم، می توانید از یکی از کدها به عنوان مکانیزم احراز هویت دوم استفاده کنید.', diff --git a/lang/fa/common.php b/lang/fa/common.php index 1f1fa688393..49667a47c42 100644 --- a/lang/fa/common.php +++ b/lang/fa/common.php @@ -20,7 +20,7 @@ 'description' => 'توضیحات', 'role' => 'نقش', 'cover_image' => 'تصویر روی جلد', - 'cover_image_description' => 'سایز تصویر باید 440x250 باشد.', + 'cover_image_description' => 'تصویر باید حدودا 250*440 پیکسل باشد اما از آنجا که متناسب با رابط کاربری کوچک و بزرگ شده و بریده میشود، پس ابعاد نمایش داده شده متفاوت خواهند بود.', // Actions 'actions' => 'عملیات', @@ -30,6 +30,8 @@ 'create' => 'ایجاد', 'update' => 'به‌روز رسانی', 'edit' => 'ويرايش', + 'archive' => 'انتقال به بایگانی', + 'unarchive' => 'فعّال‌سازی دوباره (خروج از بایگانی)', 'sort' => 'مرتب سازی', 'move' => 'جابجایی', 'copy' => 'کپی', @@ -107,4 +109,7 @@ // Not directly used but available for convenience to users. 'privacy_policy' => 'سیاست حفظ حریم خصوصی', 'terms_of_service' => 'شرایط خدمات', + + // OpenSearch + 'opensearch_description' => 'جست‌وجو در :appName', ]; diff --git a/lang/fa/components.php b/lang/fa/components.php index 90681d3b66e..18a01dc5d52 100644 --- a/lang/fa/components.php +++ b/lang/fa/components.php @@ -18,7 +18,7 @@ 'image_search_hint' => 'جستجو بر اساس نام تصویر', 'image_uploaded' => 'بارگذاری شده :uploadedDate', 'image_uploaded_by' => 'بارگذاری شده توسط:userName', - 'image_uploaded_to' => 'بارگذاری شده در صفحه:pageLink', + 'image_uploaded_to' => 'بارگذاری شده در صفحه :pageLink', 'image_updated' => 'به‌روزرسانی شده در:updateDate', 'image_load_more' => 'بارگذاری بیشتر', 'image_image_name' => 'نام تصویر', @@ -34,8 +34,8 @@ 'image_delete_success' => 'تصویر با موفقیت حذف شد', 'image_replace' => 'جایگزینی تصویر', 'image_replace_success' => 'تصویر با موفقیت به روز شد', - 'image_rebuild_thumbs' => 'Regenerate Size Variations', - 'image_rebuild_thumbs_success' => 'Image size variations successfully rebuilt!', + 'image_rebuild_thumbs' => 'بازتولید اندازه‌های گوناگونی از تصویر', + 'image_rebuild_thumbs_success' => 'اندازه‌های گوناگونی از تصویر با موفقیت بازتولید شدند.', // Code Editor 'code_editor' => 'ویرایش کد', diff --git a/lang/fa/editor.php b/lang/fa/editor.php index 3e0be53b85f..a8b3d211a94 100644 --- a/lang/fa/editor.php +++ b/lang/fa/editor.php @@ -13,6 +13,7 @@ 'cancel' => 'لغو', 'save' => 'ذخیره', 'close' => 'بستن', + 'apply' => 'اعمال', 'undo' => 'برگشت', 'redo' => 'از نو', 'left' => 'چپ', @@ -47,6 +48,7 @@ 'superscript' => 'بالانویسی', 'subscript' => 'پایین نویسی', 'text_color' => 'رنگ متن', + 'highlight_color' => 'Highlight color', 'custom_color' => 'رنگ دلخواه', 'remove_color' => 'حذف رنگ', 'background_color' => 'رنگ زمینه', @@ -81,6 +83,9 @@ 'table_properties' => 'تنظیمات جدول', 'table_properties_title' => 'تنظیمات جدول', 'delete_table' => 'حذف جدول', + 'table_clear_formatting' => 'حذف قالب‌بندی جدول', + 'resize_to_contents' => 'تغییر اندازه بر اساس محتوا', + 'row_header' => 'عنوان سطر', 'insert_row_before' => 'افزودن سطر به قبل', 'insert_row_after' => 'افزودن سطر به بعد', 'delete_row' => 'حذف سطر', @@ -144,6 +149,7 @@ 'url' => 'آدرس', 'text_to_display' => 'متن جهت نمایش', 'title' => 'عنوان', + 'browse_links' => 'مرور پیوندها', 'open_link' => 'بازکردن لینک', 'open_link_in' => 'باز کردن لینک در ...', 'open_link_current' => 'پنجره کنونی', @@ -160,6 +166,8 @@ 'about' => 'درباره ویرایشگر', 'about_title' => 'درباره ویرایشگر WYSIWYG', 'editor_license' => 'مجوز و حق کپی رایت ویرایشگر', + 'editor_lexical_license' => 'این ویرایشگر بر پایه‌ی نسخه‌ای مشتق‌شده از «:lexicalLink» ساخته شده است که تحت مجوز MIT منتشر می‌شود.', + 'editor_lexical_license_link' => 'جزئیات کامل مجوز را می‌توانید این‌جا مشاهده کنید.', 'editor_tiny_license' => 'این ویرایشگر توسط :tinyLink و تحت مجوز MIT ساخته شده است.', 'editor_tiny_license_link' => 'جزئیات کپی رایت و مجوز TinyMCE را می توانید در اینجا پیدا کنید.', 'save_continue' => 'ذخیره صفحه و ادامه', diff --git a/lang/fa/entities.php b/lang/fa/entities.php index bbdbe5b6afa..34a12689afc 100644 --- a/lang/fa/entities.php +++ b/lang/fa/entities.php @@ -23,7 +23,7 @@ 'meta_updated' => 'به روزرسانی شده :timeLength', 'meta_updated_name' => 'به روزرسانی شده :timeLength توسط :user', 'meta_owned_name' => 'متعلق به :user', - 'meta_reference_count' => 'Referenced by :count item|Referenced by :count items', + 'meta_reference_count' => 'در 1 صفحه به آن ارجاع داده شده|در :count صفحه به آن ارجاع داده شده', 'entity_select' => 'انتخاب موجودیت', 'entity_select_lack_permission' => 'شما مجوزهای لازم برای انتخاب این مورد را ندارید', 'images' => 'عکس‌ها', @@ -39,6 +39,34 @@ 'export_pdf' => 'فایل PDF', 'export_text' => 'پرونده متنی ساده', 'export_md' => 'راهنما مارک‌دون', + 'export_zip' => 'فایل فشرده‌ی قابل‌حمل (ZIP)', + 'default_template' => 'قالب پیش‌فرض صفحه', + 'default_template_explain' => 'قالبی برای صفحه تعیین کنید که به‌عنوان محتوای پیش‌فرض در تمام صفحاتی که در این مورد ایجاد می‌شوند، به‌کار رود. توجه داشته باشید این قالب تنها در صورتی اعمال می‌شود که سازندهٔ صفحه به صفحهٔ قالب انتخاب‌شده دسترسی نمایشی داشته باشد.', + 'default_template_select' => 'انتخاب صفحهٔ قالب', + 'import' => 'وارد کردن', + 'import_validate' => 'اعتبارسنجی آیتم‌های واردشده', + 'import_desc' => 'می‌توانید کتاب‌ها، فصل‌ها و صفحات را با استفاده از یک فایل فشرده (ZIP) که از همین سامانه یا نمونه‌ای دیگر استخراج شده، وارد کنید. برای ادامه، یک فایل ZIP انتخاب نمایید. پس از بارگذاری و اعتبارسنجی فایل، در مرحله بعد می‌توانید تنظیمات انتقال را انجام داده و انتقال را تأیید کنید.', + 'import_zip_select' => 'انتخاب فایل ZIP برای بارگذاری', + 'import_zip_validation_errors' => 'هنگام اعتبارسنجی فایل ZIP ارائه‌شده، خطاهایی شناسایی شد:', + 'import_pending' => 'ورودی‌های در انتظار انتقال', + 'import_pending_none' => 'هیچ انتقال ورودی آغاز نشده است.', + 'import_continue' => 'ادامه فرآیند انتقال ورودی', + 'import_continue_desc' => 'محتوای فایل ZIP بارگذاری‌شده را که قرار است وارد سامانه شود، مرور کنید. پس از اطمینان از صحت آن، انتقال را آغاز نمایید تا محتوا به این سامانه افزوده شود. توجه داشته باشید که پس از انتقال موفق، فایل ZIP بارگذاری‌شده به‌صورت خودکار حذف خواهد شد.', + 'import_details' => 'جزئیات انتقال ورودی', + 'import_run' => 'شروع فرایند انتقال ورودی', + 'import_size' => 'حجم فایل ZIP واردشده: :size', + 'import_uploaded_at' => 'زمان بارگذاری: :relativeTime', + 'import_uploaded_by' => 'بارگذاری شده توسط:', + 'import_location' => 'مکان انتقال', + 'import_location_desc' => 'برای محتوای واردشده، مقصدی انتخاب کنید. برای ایجاد محتوا در آن مقصد، داشتن مجوزهای لازم ضروری است.', + 'import_delete_confirm' => 'مطمئن هستید که می‌خواهید آیتم واردشده را حدف کنید؟', + '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', // Permissions and restrictions 'permissions' => 'مجوزها', @@ -132,9 +160,6 @@ 'books_edit_named' => 'ویرایش کتاب:bookName', 'books_form_book_name' => 'نام کتاب', 'books_save' => 'ذخیره کتاب', - 'books_default_template' => 'Default Page Template', - 'books_default_template_explain' => 'Assign a page template that will be used as the default content for all new pages in this book. Keep in mind this will only be used if the page creator has view access to those chosen template page.', - 'books_default_template_select' => 'Select a template page', 'books_permissions' => 'مجوزهای کتاب', 'books_permissions_updated' => 'مجوزهای کتاب به روز شد', 'books_empty_contents' => 'هیچ صفحه یا فصلی برای این کتاب ایجاد نشده است.', @@ -145,7 +170,9 @@ 'books_search_this' => 'این کتاب را جستجو کنید', 'books_navigation' => 'ناوبری کتاب', 'books_sort' => 'مرتب سازی مطالب کتاب', - 'books_sort_desc' => 'فصل‌ها و صفحات را در یک کتاب جابه‌جا کنید تا محتوای آن را مجددا سازماندهی کنید. کتاب‌های دیگری را می‌توان اضافه کرد که امکان جابه‌جایی آسان فصل‌ها و صفحات را بین کتاب‌ها فراهم می‌کند.', + 'books_sort_desc' => 'برای سامان‌دهی محتوای یک کتاب، می‌توانید فصل‌ها و صفحات آن را جابه‌جا کنید. همچنین می‌توانید کتاب‌های دیگری بیفزایید تا جابه‌جایی فصل‌ها و صفحات میان کتاب‌ها آسان شود. در صورت تمایل، می‌توانید قاعده‌ای برای مرتب‌سازی خودکار تعیین کنید تا محتوای کتاب در صورت ایجاد تغییرات، به طور خودکار مرتب شود.', + 'books_sort_auto_sort' => 'گزینه مرتب‌سازی خودکار', + 'books_sort_auto_sort_active' => 'مرتب‌سازی خودکار با قاعده: :sortName فعال است', 'books_sort_named' => 'مرتب‌سازی کتاب:bookName', 'books_sort_name' => 'مرتب سازی بر اساس نام', 'books_sort_created' => 'مرتب سازی بر اساس تاریخ ایجاد', @@ -207,7 +234,9 @@ 'pages_delete_draft' => 'حذف صفحه پیش نویس', 'pages_delete_success' => 'صفحه حذف شد', 'pages_delete_draft_success' => 'صفحه پیش نویس حذف شد', - 'pages_delete_warning_template' => 'This page is in active use as a book default page template. These books will no longer have a default page template assigned after this page is deleted.', + 'pages_delete_warning_template' => 'این صفحه هم‌اکنون به‌عنوان قالب پیش‌فرض صفحه برای یک کتاب یا فصل در حال استفاده است. پس از حذف این صفحه، کتاب‌ها یا فصل‌های مربوطه دیگر قالب پیش‌فرض صفحه نخواهند داشت. + +', 'pages_delete_confirm' => 'آیا مطمئن هستید که می خواهید این صفحه را حذف کنید؟', 'pages_delete_draft_confirm' => 'آیا مطمئن هستید که می خواهید این صفحه پیش نویس را حذف کنید؟', 'pages_editing_named' => 'ویرایش صفحه :pageName', @@ -224,6 +253,9 @@ 'pages_edit_switch_to_markdown_clean' => '(مطالب تمیز)', 'pages_edit_switch_to_markdown_stable' => '(محتوای پایدار)', 'pages_edit_switch_to_wysiwyg' => 'به ویرایشگر WYSIWYG بروید', + 'pages_edit_switch_to_new_wysiwyg' => 'تغییر به ویرایشگر جدید WYSIWYG +(ویرایشگر WYSIWYG: «آنچه می‌بینید همان است که به‌دست می‌آورید»)', + 'pages_edit_switch_to_new_wysiwyg_desc' => '(در مرحله آزمایش بتا)', 'pages_edit_set_changelog' => 'تنظیم تغییرات', 'pages_edit_enter_changelog_desc' => 'توضیح مختصری از تغییراتی که ایجاد کرده اید وارد کنید', 'pages_edit_enter_changelog' => 'وارد کردن تغییرات', @@ -243,6 +275,7 @@ 'pages_md_insert_drawing' => 'درج طرح', 'pages_md_show_preview' => 'دیدن پیش نمایش', 'pages_md_sync_scroll' => 'هماهنگ سازی اسکرول پیش نمایش', + 'pages_md_plain_editor' => 'Plaintext editor', 'pages_drawing_unsaved' => 'نقاشی ذخیره نشده پیدا شد', 'pages_drawing_unsaved_confirm' => 'نسخه‌ای ذخیره‌نشده از طراحی‌های قبلی پیدا شد. آیا می‌خواهید این طراحی ذخیره‌نشده را بازیابی کنید و به ویرایش آن ادامه دهید؟', 'pages_not_in_chapter' => 'صفحه در یک فصل نیست', @@ -276,9 +309,9 @@ 'pages_pointer_enter_mode' => 'ورود به حالت انتخاب قسمت', 'pages_pointer_label' => 'گزینه‌های قسمت صفحه', 'pages_pointer_permalink' => 'لینک ثابت قسمت صفحه', - 'pages_pointer_include_tag' => 'Page Section Include Tag', - 'pages_pointer_toggle_link' => 'Permalink mode, Press to show include tag', - 'pages_pointer_toggle_include' => 'Include tag mode, Press to show permalink', + 'pages_pointer_include_tag' => 'افزودن برچسب برای این بخش از صفحه', + 'pages_pointer_toggle_link' => 'حالت پیوند دائمی؛ برای مشاهده برچسب افزوده شده، کلیک کنید', + 'pages_pointer_toggle_include' => 'حالت افزودن برچسب؛ برای نمایش پیوند دائمی کلیک کنید', 'pages_permissions_active' => 'مجوزهای صفحه فعال است', 'pages_initial_revision' => 'انتشار اولیه', 'pages_references_update_revision' => 'به‌روز‌رسانی خودکار لینک‌های داخلی سیستم', @@ -299,7 +332,7 @@ 'pages_is_template' => 'الگوی صفحه', // Editor Sidebar - 'toggle_sidebar' => 'Toggle Sidebar', + 'toggle_sidebar' => 'نمایش/پنهان‌سازی نوار کناری', 'page_tags' => 'برچسب‌های صفحه', 'chapter_tags' => 'برچسب‌های فصل', 'book_tags' => 'برچسب های کتاب', @@ -367,8 +400,11 @@ 'comment' => 'اظهار نظر', 'comments' => 'نظرات', 'comment_add' => 'افزودن توضیح', + 'comment_none' => 'نظری برای نمایش وجود ندارد', 'comment_placeholder' => 'اینجا نظر بدهید', - 'comment_count' => '{0} بدون نظر|{1} 1 نظر|[2,*] :count نظرات', + 'comment_thread_count' => ':count رشته گفت‌وگو', + 'comment_archived_count' => ':count مورد بایگانی‌شده', + 'comment_archived_threads' => 'رشته‌ گفت‌وگوهای بایگانی‌شده', 'comment_save' => 'ذخیره نظر', 'comment_new' => 'نظر جدید', 'comment_created' => ':createDiff نظر داد', @@ -377,8 +413,14 @@ 'comment_deleted_success' => 'نظر حذف شد', 'comment_created_success' => 'نظر اضافه شد', 'comment_updated_success' => 'نظر به روز شد', + 'comment_archive_success' => 'نظر بایگانی شد', + 'comment_unarchive_success' => 'نظر از بایگانی خارج شد', + 'comment_view' => 'دیدن نظر', + 'comment_jump_to_thread' => 'رفتن به رشته گفت‌وگو', 'comment_delete_confirm' => 'آیا مطمئن هستید که می خواهید این نظر را حذف کنید؟', 'comment_in_reply_to' => 'در پاسخ به :commentId', + 'comment_reference' => 'مرجع', + 'comment_reference_outdated' => '(نسخه قدیمی)', 'comment_editor_explain' => 'در اینجا نظراتی که در این صفحه گذاشته شده است، مشاهده می‌شود. نظرات را می‌توان در هنگام مشاهده صفحه ذخیره شده، اضافه و مدیریت کرد.', // Revision @@ -409,7 +451,7 @@ // References 'references' => 'مراجع', 'references_none' => 'هیچ رفرنسی برای این قلم یافت نشد.', - 'references_to_desc' => 'Listed below is all the known content in the system that links to this item.', + 'references_to_desc' => 'در زیر تمام صفحات شناخته شده در سیستم که به این مورد پیوند دارند، نشان داده شده است.', // Watch Options 'watch' => 'نظارت', diff --git a/lang/fa/errors.php b/lang/fa/errors.php index 6632246dab0..9d5257fcf79 100644 --- a/lang/fa/errors.php +++ b/lang/fa/errors.php @@ -10,6 +10,7 @@ // Auth 'error_user_exists_different_creds' => 'کاربری با ایمیل :email از قبل وجود دارد اما دارای اطلاعات متفاوتی می باشد.', + 'auth_pre_register_theme_prevention' => 'بر اساس اطلاعات ارائه شده، حساب کاربری نمی‌نواند ایجاد بشود', 'email_already_confirmed' => 'ایمیل قبلا تایید شده است، وارد سیستم شوید.', 'email_confirmation_invalid' => 'این کلمه عبور معتبر نمی باشد و یا قبلا استفاده شده است، لطفا دوباره ثبت نام نمایید.', 'email_confirmation_expired' => 'کلمه عبور منقضی شده است، یک ایمیل تایید جدید ارسال شد.', @@ -17,13 +18,12 @@ 'ldap_fail_anonymous' => 'دسترسی LDAP با استفاده از صحافی ناشناس انجام نشد', 'ldap_fail_authed' => 'دسترسی به LDAP با استفاده از جزئیات داده شده و رمز عبور انجام نشد', 'ldap_extension_not_installed' => 'افزونه PHP LDAP نصب نشده است', - 'ldap_cannot_connect' => 'اتصال به سرور LDAP امکان پذیر نیست، اتصال اولیه برقرار نشد', + 'ldap_cannot_connect' => 'اتصال به سرور LDAP امکان‌پذیر نیست، اتصال اولیه برقرار نشد', 'saml_already_logged_in' => 'قبلا وارد سیستم شده اید', 'saml_no_email_address' => 'آدرس داده ای برای این کاربر در داده های ارائه شده توسط سیستم احراز هویت خارجی یافت نشد', - 'saml_invalid_response_id' => 'درخواست از سیستم احراز هویت خارجی توسط فرایندی که توسط این نرم افزار آغاز شده است شناخته نمی شود. بازگشت به سیستم پس از ورود به سیستم می تواند باعث این مسئله شود.', + 'saml_invalid_response_id' => 'درخواست ارسال‌شده از سامانه احراز هویت خارجی، توسط فرآیند آغازشده از سوی این نرم‌افزار شناسایی نشد. ممکن است بازگشت به صفحه قبل پس از ورود، موجب ایجاد این مشکل شده باشد.', 'saml_fail_authed' => 'ورود به سیستم :system انجام نشد، سیستم مجوز موفقیت آمیز ارائه نکرد', 'oidc_already_logged_in' => 'قبلا وارد شده اید', - 'oidc_user_not_registered' => 'کاربر :name ثبت نشده و ثبت نام خودکار غیرفعال است', 'oidc_no_email_address' => 'آدرس ایمیلی برای این کاربر در داده های ارائه شده توسط سیستم احراز هویت خارجی یافت نشد', 'oidc_fail_authed' => 'ورود به سیستم با استفاده از :system انجام نشد، سیستم مجوز موفقیت آمیز ارائه نکرد', 'social_no_action_defined' => 'عملی تعریف نشده است', @@ -37,22 +37,23 @@ 'social_driver_not_found' => 'درایور شبکه اجتماعی یافت نشد', 'social_driver_not_configured' => 'تنظیمات شبکه اجتماعی :socialAccount به درستی پیکربندی نشده است.', 'invite_token_expired' => 'این پیوند دعوت منقضی شده است. در عوض می توانید سعی کنید رمز عبور حساب خود را بازنشانی کنید.', + 'login_user_not_found' => 'کاربری برای این کار پیدا نمی‌شود.', // System 'path_not_writable' => 'مسیر فایل :filePath را نمی توان در آن آپلود کرد. مطمئن شوید که روی سرور قابل نوشتن است.', 'cannot_get_image_from_url' => 'نمی توان تصویر را از :url دریافت کرد', 'cannot_create_thumbs' => 'سرور نمی تواند تصاویر کوچک ایجاد کند. لطفاً بررسی کنید که پسوند GD PHP را نصب کرده اید.', - 'server_upload_limit' => 'سرور اجازه آپلود در این اندازه را نمی دهد. لطفا اندازه فایل کوچکتر را امتحان کنید.', - 'server_post_limit' => 'The server cannot receive the provided amount of data. Try again with less data or a smaller file.', + 'server_upload_limit' => 'سرور اجازه آپلود با این حجم را نمی دهد. لطفا فایلی با حجم کم‌تر را امتحان کنید.', + 'server_post_limit' => 'سرور نمی‌تواند داده مقادیر ارائه شده داده را دریافت کند. با مقدار کمتر و فایل کوچکتر دوباره امتحان کنید.', 'uploaded' => 'سرور اجازه آپلود در این اندازه را نمی دهد. لطفا اندازه فایل کوچکتر را امتحان کنید.', // Drawing & Images 'image_upload_error' => 'هنگام آپلود تصویر خطایی روی داد', 'image_upload_type_error' => 'نوع تصویر در حال آپلود نامعتبر است', 'image_upload_replace_type' => 'جایگزینی فایل تصویری باید از یک نوع باشد', - 'image_upload_memory_limit' => 'Failed to handle image upload and/or create thumbnails due to system resource limits.', - 'image_thumbnail_memory_limit' => 'Failed to create image size variations due to system resource limits.', - 'image_gallery_thumbnail_memory_limit' => 'Failed to create gallery thumbnails due to system resource limits.', + 'image_upload_memory_limit' => 'به دلیل محدودیت منابع سامانه، بارگذاری فایل و/یا ایجاد تصاویر کاور (thumbnails) ناموفق بود.', + 'image_thumbnail_memory_limit' => 'به دلیل محدودیت منابع سیستم، تصاویر با اندازه گوناگون ایجاد نشدند.', + 'image_gallery_thumbnail_memory_limit' => 'به دلیل محدودیت منابع سیستم، تصاویر کوچک گالری ایجاد نشد.', 'drawing_data_not_found' => 'داده های طرح قابل بارگذاری نیستند. ممکن است فایل طرح دیگر وجود نداشته باشد یا شما به آن دسترسی نداشته باشید.', // Attachments @@ -77,6 +78,7 @@ // Users 'users_cannot_delete_only_admin' => 'شما نمی توانید تنها ادمین را حذف کنید', 'users_cannot_delete_guest' => 'شما نمی توانید کاربر مهمان را حذف کنید', + 'users_could_not_send_invite' => 'امکان ایجاد کاربر وجود ندارد؛ زیرا ارسال ایمیل دعوت با خطا مواجه شد.', // Roles 'role_cannot_be_edited' => 'این نقش قابل ویرایش نیست', @@ -103,6 +105,18 @@ 'app_down' => ':appName در حال حاضر قطع است', 'back_soon' => 'به زودی پشتیبان خواهد شد.', + // Import + 'import_zip_cant_read' => 'امکان ایجاد کاربر وجود ندارد؛ زیرا ارسال ایمیل دعوت با خطا مواجه شد.', + 'import_zip_cant_decode_data' => 'محتوای data.json در فایل ZIP پیدا یا رمزگشایی نشد.', + 'import_zip_no_data' => 'داده‌های فایل ZIP فاقد محتوای کتاب، فصل یا صفحه مورد انتظار است.', + 'import_validation_failed' => 'اعتبارسنجی فایل ZIP واردشده با خطا مواجه شد:', + 'import_zip_failed_notification' => ' فایل ZIP وارد نشد.', + 'import_perms_books' => 'شما مجوز لازم برای ایجاد کتاب را ندارید.', + 'import_perms_chapters' => 'شما مجوز لازم برای ایجاد فصل را ندارید.', + 'import_perms_pages' => 'شما مجوز لازم برای ایجاد صفحه را ندارید.', + 'import_perms_images' => 'شما مجوز لازم برای ایجاد تصویر را ندارید.', + 'import_perms_attachments' => 'شما مجوز لازم برای ایجاد پیوست را ندارید.', + // API errors 'api_no_authorization_found' => 'هیچ نشانه مجوزی در درخواست یافت نشد', 'api_bad_authorization_format' => 'یک نشانه مجوز در این درخواست یافت شد اما قالب نادرست به نظر می‌رسید', diff --git a/lang/fa/notifications.php b/lang/fa/notifications.php index 12f4230a13e..4595fd8252a 100644 --- a/lang/fa/notifications.php +++ b/lang/fa/notifications.php @@ -22,6 +22,6 @@ 'action_view_comment' => 'مشاهده نظر', 'action_view_page' => 'مشاهده صفحه', - 'footer_reason' => 'This notification was sent to you because :link cover this type of activity for this item.', + 'footer_reason' => 'این اعلان برای شما ارسال شده است، زیرا پیوند (:link) فعالیتی از این نوع را برای این مورد پوشش می‌دهد.', 'footer_reason_link' => 'تنظیمات اطلاع‌رسانی شما', ]; diff --git a/lang/fa/settings.php b/lang/fa/settings.php index c48f4d8e92b..e4cb9018ebf 100644 --- a/lang/fa/settings.php +++ b/lang/fa/settings.php @@ -15,7 +15,7 @@ // App Settings 'app_customization' => 'سفارشی‌سازی', 'app_features_security' => 'ویژگی‌ها و امنیت', - 'app_name' => 'نام نرم افزار', + 'app_name' => 'نام نرم‌افزار', 'app_name_desc' => 'این نام در هدر و در هر ایمیل ارسال شده توسط سیستم نشان داده شده است.', 'app_name_header' => 'نمایش نام در هدر', 'app_public_access' => 'دسترسی عمومی', @@ -74,6 +74,36 @@ 'reg_confirm_restrict_domain_desc' => 'فهرستی از دامنه‌های ایمیل جدا شده با کاما را وارد کنید که می‌خواهید ثبت نام را محدود کنید. قبل از اینکه به کاربران اجازه تعامل با برنامه داده شود، ایمیلی برای تأیید آدرس آنها ارسال می شود.
توجه داشته باشید که کاربران پس از ثبت نام موفق می توانند آدرس ایمیل خود را تغییر دهند.', 'reg_confirm_restrict_domain_placeholder' => 'بدون محدودیت', + // Sorting Settings + 'sorting' => 'مرتب‌سازی', + 'sorting_book_default' => 'مرتب‌سازی پیش‌فرض کتاب', + 'sorting_book_default_desc' => 'قانون پیش‌فرض مرتب‌سازی را برای کتاب‌های جدید انتخاب کنید. تغییر قانون بر ترتیب کتاب‌های موجود تأثیری ندارد و می‌تواند برای هر کتاب به‌صورت جداگانه تغییر یابد.', + 'sorting_rules' => 'قوانین مرتب‌سازی', + 'sorting_rules_desc' => 'این‌ها عملیات مرتب‌سازی از پیش تعریف‌شده‌ای هستند که می‌توانید آن‌ها را بر محتوای سیستم اعمال کنید.', + 'sort_rule_assigned_to_x_books' => 'اختصاص داده شده به :count کتاب', + 'sort_rule_create' => 'ایجاد قانون مرتب‌سازی', + 'sort_rule_edit' => 'ویرایش قانون مرتب‌سازی', + 'sort_rule_delete' => 'حذف قانون مرتب‌سازی', + 'sort_rule_delete_desc' => 'این قانون مرتب‌سازی را از سیستم حذف کنید. کتاب‌هایی که از این شیوه مرتب‌سازی استفاده می‌کنند، به حالت مرتب‌سازی دستی بازخواهند گشت.', + 'sort_rule_delete_warn_books' => 'در حال حاضر این قانون مرتب‌سازی برای :count کتاب به‌کار می‌رود. آیا مطمئن هستید که می‌خواهید آن را حذف کنید؟', + 'sort_rule_delete_warn_default' => 'این قانون مرتب‌سازی هم‌اکنون به عنوان پیش‌فرض کتاب‌ها تعیین شده است. آیا مطمئن هستید که می‌خواهید آن را حذف کنید؟', + 'sort_rule_details' => 'جزئیات قانون مرتب‌سازی', + 'sort_rule_details_desc' => 'برای این قانون مرتب‌سازی یک نام انتخاب کنید. این نام هنگام انتخاب شیوه مرتب‌سازی در فهرست‌ها نمایش داده خواهد شد.', + 'sort_rule_operations' => 'عملیات مرتب‌سازی', + 'sort_rule_operations_desc' => 'عملیات مرتب‌سازی را پیکربندی کنید. برای این منظور، آن‌ها را در فهرست عملیاتِ در دسترس جابه‌جا کنید تا ترتیب اجرای آن‌ها مشخص شود. هنگام استفاده، این عملیات به‌ترتیب از بالا به پایین اعمال خواهند شد. هر تغییری که در این بخش ایجاد کنید، پس از ذخیره، برای همه کتاب‌های اختصاص‌یافته اجرا می‌شود.', + 'sort_rule_available_operations' => 'عملیات موجود', + 'sort_rule_available_operations_empty' => 'عملیاتی باقی نمانده است', + 'sort_rule_configured_operations' => 'عملیات پیکربندی‌شده', + 'sort_rule_configured_operations_empty' => 'عملیات را از فهرست «عملیات موجود» حذف یا اضافه کنید', + 'sort_rule_op_asc' => '(صعودی)', + 'sort_rule_op_desc' => '(نزولی)', + 'sort_rule_op_name' => 'نام - الفبایی', + 'sort_rule_op_name_numeric' => 'نام - عددی', + 'sort_rule_op_created_date' => 'تاریخ ایجاد', + 'sort_rule_op_updated_date' => 'تاریخ به‌روزرسانی', + 'sort_rule_op_chapters_first' => 'ابتدا فصل‌ها', + 'sort_rule_op_chapters_last' => 'فصل‌ها در آخر', + // Maintenance settings 'maint' => 'نگهداری', 'maint_image_cleanup' => 'پاکسازی تصاویر', @@ -109,7 +139,7 @@ 'recycle_bin_contents_empty' => 'سطل بازیافت در حال حاضر خالی است', 'recycle_bin_empty' => 'سطل آشغال خالی', 'recycle_bin_empty_confirm' => 'این کار همه اقلام موجود در سطل بازیافت از جمله محتوای موجود در هر مورد را برای همیشه از بین می برد. آیا مطمئن هستید که می خواهید سطل بازیافت را خالی کنید؟', - 'recycle_bin_destroy_confirm' => 'این اقدام این مورد را به همراه هر عنصر فرعی فهرست شده در زیر برای همیشه از سیستم حذف می کند و شما نمی توانید این محتوا را بازیابی کنید. آیا مطمئن هستید که می خواهید این مورد را برای همیشه حذف کنید؟', + 'recycle_bin_destroy_confirm' => 'این کار تمامی عناصر زیر مجموعه آیتم که در پایین فهرست شده را به همراه خودش برای همیشه از سامانه پاک میکند و شما امکان بازیابی آن را نخواهید داشت. آیا مطمئن هستید که میخواهید برای همیشه این آیتم را پاک کنید؟', 'recycle_bin_destroy_list' => 'مواردی که باید نابود شوند', 'recycle_bin_restore_list' => 'مواردی که باید بازیابی شوند', 'recycle_bin_restore_confirm' => 'این اقدام، مورد حذف شده، از جمله هر عنصر فرزند، را به مکان اصلی خود باز می گرداند. اگر مکان اصلی از آن زمان حذف شده باشد، و اکنون در سطل بازیافت است، مورد اصلی نیز باید بازیابی شود.', @@ -162,6 +192,7 @@ 'role_access_api' => 'دسترسی به API سیستم', 'role_manage_settings' => 'تنظیمات برنامه را مدیریت کنید', 'role_export_content' => 'صادرات محتوا', + 'role_import_content' => 'وارد کردن محتوا', 'role_editor_change' => 'تغییر ویرایشگر صفحه', 'role_notifications' => 'دریافت و مدیریت اعلان‌ها', 'role_asset' => 'مجوزهای دارایی', @@ -193,7 +224,7 @@ 'users_send_invite_text' => 'می توانید انتخاب کنید که برای این کاربر یک ایمیل دعوت نامه ارسال شود که به آنها امکان می دهد رمز عبور خود را تعیین کنند در غیر این صورت می توانید رمز عبور خود را تعیین کنید.', 'users_send_invite_option' => 'ارسال ایمیل دعوت کاربر', 'users_external_auth_id' => 'شناسه احراز هویت خارجی', - 'users_external_auth_id_desc' => 'When an external authentication system is in use (such as SAML2, OIDC or LDAP) this is the ID which links this BookStack user to the authentication system account. You can ignore this field if using the default email-based authentication.', + 'users_external_auth_id_desc' => 'هنگامی که یک سامانه احراز هویت خارجی مورد استفاده است (مانند SAML2، OIDC یا LDAP)، شناسه ای که کاربر BookStack را به حساب سامانه احراز هویت مرتبط میکند بدین‌گونه می‌باشد. اگر از احراز هویت پیش فرض ایمیلی استفاده می‌کنید، میتوانید این را در نظر نگیرید.', 'users_password_warning' => 'فقط در صورتی که مایل به تغییر رمز عبور این کاربر هستید، موارد زیر را پر کنید.', 'users_system_public' => 'این کاربر نماینده هر کاربر مهمانی است که از نمونه شما بازدید می کند. نمی توان از آن برای ورود استفاده کرد اما به طور خودکار اختصاص داده می شود.', 'users_delete' => 'حذف کاربر', @@ -214,12 +245,12 @@ 'users_social_accounts_info' => 'در اینجا می‌توانید حساب‌های دیگر خود را برای ورود سریع‌تر و آسان‌تر متصل کنید. قطع ارتباط حساب در اینجا، دسترسی مجاز قبلی را لغو نمی کند. دسترسی را از تنظیمات نمایه خود در حساب اجتماعی متصل لغو کنید.', 'users_social_connect' => 'اتصال حساب کاربری', 'users_social_disconnect' => 'قطع حساب', - 'users_social_status_connected' => 'Connected', + 'users_social_status_connected' => 'ارتباط برقرار شد', 'users_social_status_disconnected' => 'قطع اتصال', 'users_social_connected' => 'حساب :socialAccount با موفقیت به نمایه شما پیوست شد.', 'users_social_disconnected' => 'حساب :socialAccount با موفقیت از نمایه شما قطع شد.', 'users_api_tokens' => 'توکن‌های API', - 'users_api_tokens_desc' => 'Create and manage the access tokens used to authenticate with the BookStack REST API. Permissions for the API are managed via the user that the token belongs to.', + 'users_api_tokens_desc' => 'توکن های دسترسی مورد استفاده در BookStack REST API را بسازید و مدیریت کنید. اجازه‌های مربوط به API توسط کاربری که صاحب آنها است مدیریت می‌شوند.', 'users_api_tokens_none' => 'هیچ نشانه API برای این کاربر ایجاد نشده است', 'users_api_tokens_create' => 'ایجاد توکن', 'users_api_tokens_expires' => 'منقضی شده ها', @@ -276,6 +307,14 @@ 'webhooks_last_errored' => 'آخرین خطا:', 'webhooks_last_error_message' => 'آخرین پیغام خطا:', + // Licensing + 'licenses' => 'مجوز‌ها', + 'licenses_desc' => 'این صفحه اطلاعات مجوز‌های بوک استک به همراه پروژه‌ها و کتابخانه‌های مورد استفاده با آن را به نمایش می‌گذارد. بسیاری از پروژه‌های فهرست شده ممکن است فقط در بحث توسعه به کار گرفته شده باشند.', + 'licenses_bookstack' => 'مجوز‌های بوک استک', + 'licenses_php' => 'مجوز‌های کتابخانه PHP', + 'licenses_js' => 'مجوز‌های کتابخانه جاوا اسکریپت', + 'licenses_other' => 'سایر مجوز‌ها', + 'license_details' => 'جزئیات مجوز', //! If editing translations files directly please ignore this in all //! languages apart from en. Content will be auto-copied from en. @@ -287,6 +326,7 @@ 'bs' => 'Bosanski', 'ca' => 'Català', 'cs' => 'Česky', + 'cy' => 'Cymraeg', 'da' => 'Dansk', 'de' => 'Deutsch (Sie)', 'de_informal' => 'Deutsch (Du)', @@ -308,6 +348,7 @@ 'lt' => 'Lietuvių Kalba', 'lv' => 'Latviešu Valoda', 'nb' => 'Norsk (Bokmål)', + 'ne' => 'नेपाली', 'nn' => 'Nynorsk', 'nl' => 'Nederlands', 'pl' => 'Polski', diff --git a/lang/fa/validation.php b/lang/fa/validation.php index 467c68702dd..e1c69ece9a1 100644 --- a/lang/fa/validation.php +++ b/lang/fa/validation.php @@ -105,6 +105,11 @@ 'url' => ':attribute معتبر نمی‌باشد.', 'uploaded' => 'بارگذاری فایل :attribute موفقیت آمیز نبود.', + 'zip_file' => 'ویژگی :attribute باید به یک فایل درون پرونده فشرده شده اشاره کند.', + 'zip_file_mime' => 'ویژگی :attribute باید به فایلی با نوع :validTypes اشاره کند، اما نوع یافت‌شده :foundType است.', + 'zip_model_expected' => 'سیستم در این بخش انتظار دریافت یک شیء داده‌ای را داشت، اما «:type» دریافت گردید', + 'zip_unique' => 'برای هر نوع شیء در فایل ZIP، مقدار ویژگی :attribute باید یکتا و بدون تکرار باشد.', + // Custom validation lines 'custom' => [ 'password-confirm' => [ diff --git a/lang/fi/activities.php b/lang/fi/activities.php index 6bb267c707c..a5ee14906ef 100644 --- a/lang/fi/activities.php +++ b/lang/fi/activities.php @@ -84,6 +84,14 @@ 'webhook_delete' => 'poisti toimintokutsun', 'webhook_delete_notification' => 'Toimintokutsu poistettiin onnistuneesti', + // Imports + 'import_create' => 'luotu tuonti', + 'import_create_notification' => 'Tuontitiedosto ladattiin onnistuneesti', + 'import_run' => 'päivitetty tuonti', + 'import_run_notification' => 'Sisällön tuonti onnistui', + 'import_delete' => 'poistettu tuonti', + 'import_delete_notification' => 'Tuontitiedosto poistettiin onnistuneesti', + // Users 'user_create' => 'loi käyttäjän', 'user_create_notification' => 'Käyttäjä luotiin onnistuneesti', @@ -119,6 +127,14 @@ 'comment_update' => 'päivitti kommentin', 'comment_delete' => 'poisti kommentin', + // Sort Rules + 'sort_rule_create' => 'luotu lajittelusääntö', + 'sort_rule_create_notification' => 'Lajittelusääntö luotiin onnistuneesti', + 'sort_rule_update' => 'päivitetty lajittelusääntö', + 'sort_rule_update_notification' => 'Sort rule successfully updated', + 'sort_rule_delete' => 'poistettu lajittelusääntö', + 'sort_rule_delete_notification' => 'Lajittelusääntö poistettiin onnistuneesti', + // Other 'permissions_update' => 'päivitti käyttöoikeudet', ]; diff --git a/lang/fi/auth.php b/lang/fi/auth.php index 40aca069565..580c8679726 100644 --- a/lang/fi/auth.php +++ b/lang/fi/auth.php @@ -91,7 +91,7 @@ 'mfa_option_totp_title' => 'Mobiilisovellus', 'mfa_option_totp_desc' => 'Jos haluat käyttää monivaiheista tunnistautumista, tarvitset mobiilisovelluksen, joka tukee TOTP:tä, kuten Google Authenticator, Authy tai Microsoft Authenticator.', 'mfa_option_backup_codes_title' => 'Varmistuskoodit', - 'mfa_option_backup_codes_desc' => 'Tallenna turvallisesti joukko kertakäyttöisiä varmistuskoodeja, jotka voit syöttää henkilöllisyytesi varmentamiseksi.', + 'mfa_option_backup_codes_desc' => 'Generates a set of one-time-use backup codes which you\'ll enter on login to verify your identity. Make sure to store these in a safe & secure place.', 'mfa_gen_confirm_and_enable' => 'Vahvista ja ota käyttöön', 'mfa_gen_backup_codes_title' => 'Varmistuskoodien asetukset', 'mfa_gen_backup_codes_desc' => 'Säilytä alla oleva luettelo koodeista turvallisessa paikassa. Kun käytät järjestelmää, voit käyttää yhtä koodeista toisena tunnistautumistapana.', diff --git a/lang/fi/common.php b/lang/fi/common.php index 76c5cc41f21..e05f40f44e6 100644 --- a/lang/fi/common.php +++ b/lang/fi/common.php @@ -20,7 +20,7 @@ 'description' => 'Kuvaus', 'role' => 'Rooli', 'cover_image' => 'Kansikuva', - 'cover_image_description' => 'Kuvan tulee olla noin 440x250px.', + 'cover_image_description' => 'Kuvan tulee olla noin 440x250 pikselin kokoinen. Kuvan koko ja rajaus voi vaihdella käyttötilanteesta riippuen.', // Actions 'actions' => 'Toiminnot', @@ -30,6 +30,8 @@ 'create' => 'Luo', 'update' => 'Päivitä', 'edit' => 'Muokkaa', + 'archive' => 'Archive', + 'unarchive' => 'Un-Archive', 'sort' => 'Järjestä', 'move' => 'Siirrä', 'copy' => 'Kopioi', @@ -107,4 +109,7 @@ // Not directly used but available for convenience to users. 'privacy_policy' => 'Tietosuojaseloste', 'terms_of_service' => 'Palvelun käyttöehdot', + + // OpenSearch + 'opensearch_description' => 'Search :appName', ]; diff --git a/lang/fi/editor.php b/lang/fi/editor.php index 7954aa6035f..8b72b4e5e3e 100644 --- a/lang/fi/editor.php +++ b/lang/fi/editor.php @@ -13,6 +13,7 @@ 'cancel' => 'Peruuta', 'save' => 'Tallenna', 'close' => 'Sulje', + 'apply' => 'Apply', 'undo' => 'Kumoa', 'redo' => 'Tee uudelleen', 'left' => 'Vasen', @@ -47,6 +48,7 @@ 'superscript' => 'Yläindeksi', 'subscript' => 'Alaindeksi', 'text_color' => 'Tekstin väri', + 'highlight_color' => 'Highlight color', 'custom_color' => 'Mukautettu väri', 'remove_color' => 'Poista väri', 'background_color' => 'Taustaväri', @@ -81,6 +83,9 @@ 'table_properties' => 'Taulukon ominaisuudet', 'table_properties_title' => 'Taulukon ominaisuudet', 'delete_table' => 'Poista taulukko', + 'table_clear_formatting' => 'Poista taulukon muotoilut', + 'resize_to_contents' => 'Sovita koko sisältöön', + 'row_header' => 'Rivin otsikko', 'insert_row_before' => 'Lisää rivi ennen', 'insert_row_after' => 'Lisää rivi jälkeen', 'delete_row' => 'Poista rivi', @@ -144,6 +149,7 @@ 'url' => 'URL-osoite', 'text_to_display' => 'Näytettävä teksti', 'title' => 'Otsikko', + 'browse_links' => 'Browse links', 'open_link' => 'Avaa linkki', 'open_link_in' => 'Avaa linkki...', 'open_link_current' => 'Nykyinen ikkuna', @@ -160,6 +166,8 @@ 'about' => 'Tietoja editorista', 'about_title' => 'Tietoja WYSIWYG-editorista', 'editor_license' => 'Editorin lisenssi ja tekijänoikeus', + 'editor_lexical_license' => 'This editor is built as a fork of :lexicalLink which is distributed under the MIT license.', + 'editor_lexical_license_link' => 'Full license details can be found here.', 'editor_tiny_license' => 'Tämä editori on rakennettu käyttäen sovellusta :tinyLink, joka on MIT-lisenssin alainen.', 'editor_tiny_license_link' => 'TinyMCE-editorin tekijänoikeus- ja lisenssitiedot löytyvät täältä.', 'save_continue' => 'Tallenna sivu ja jatka', diff --git a/lang/fi/entities.php b/lang/fi/entities.php index dc43388a325..c64621f0d12 100644 --- a/lang/fi/entities.php +++ b/lang/fi/entities.php @@ -39,6 +39,34 @@ 'export_pdf' => 'PDF-tiedosto', 'export_text' => 'Tekstitiedosto', 'export_md' => 'Markdown-tiedosto', + 'export_zip' => 'Portable ZIP', + 'default_template' => 'Default Page Template', + 'default_template_explain' => 'Assign a page template that will be used as the default content for all pages created within this item. Keep in mind this will only be used if the page creator has view access to the chosen template page.', + 'default_template_select' => 'Select a template page', + 'import' => 'Import', + 'import_validate' => 'Validate Import', + 'import_desc' => 'Import books, chapters & pages using a portable zip export from the same, or a different, instance. Select a ZIP file to proceed. After the file has been uploaded and validated you\'ll be able to configure & confirm the import in the next view.', + '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_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_size' => ':size Import ZIP Size', + 'import_uploaded_at' => 'Uploaded :relativeTime', + 'import_uploaded_by' => 'Uploaded by', + 'import_location' => 'Import Location', + '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_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', + 'breadcrumb_siblings_for_book' => 'Navigate siblings for book', + 'breadcrumb_siblings_for_bookshelf' => 'Navigate siblings for shelf', // Permissions and restrictions 'permissions' => 'Käyttöoikeudet', @@ -132,9 +160,6 @@ 'books_edit_named' => 'Muokkaa kirjaa :bookName', 'books_form_book_name' => 'Kirjan nimi', 'books_save' => 'Tallenna kirja', - 'books_default_template' => 'Oletusmallipohja', - 'books_default_template_explain' => 'Määritä mallipohja, jota käytetään oletuksena kaikille tämän kirjan uusille sivuille. Muista, että mallipohjaa käytetään vain, jos sivun luojalla on katseluoikeudet valittuun mallipohjaan.', - 'books_default_template_select' => 'Valitse mallipohja', 'books_permissions' => 'Kirjan käyttöoikeudet', 'books_permissions_updated' => 'Kirjan käyttöoikeudet päivitetty', 'books_empty_contents' => 'Tähän kirjaan ei ole luotu sivuja tai lukuja.', @@ -145,7 +170,9 @@ 'books_search_this' => 'Hae tästä kirjasta', 'books_navigation' => 'Kirjan navigaatio', 'books_sort' => 'Järjestä kirjan sisältö', - 'books_sort_desc' => 'Siirrä lukuja ja sivuja kirjan sisällä järjestelläksesi sen sisältöä uudelleen. Voit lisätä muita kirjoja, jolloin lukujen ja sivujen siirtäminen kirjojen välillä on helppoa.', + 'books_sort_desc' => 'Move chapters and pages within a book to reorganise its contents. Other books can be added which allows easy moving of chapters and pages between books. Optionally an auto sort rule can be set to automatically sort this book\'s contents upon changes.', + 'books_sort_auto_sort' => 'Auto Sort Option', + 'books_sort_auto_sort_active' => 'Auto Sort Active: :sortName', 'books_sort_named' => 'Järjestä kirja :bookName', 'books_sort_name' => 'Järjestä nimen mukaan', 'books_sort_created' => 'Järjestä luontipäiväyksen mukaan', @@ -207,7 +234,7 @@ 'pages_delete_draft' => 'Poista luonnos', 'pages_delete_success' => 'Sivu poistettu', 'pages_delete_draft_success' => 'Luonnos poistettu', - 'pages_delete_warning_template' => 'Tätä sivua käytetään oletusmallipohjana. Näillä kirjoilla ei ole enää oletusmallipohjaa, jos tämä sivu poistetaan.', + 'pages_delete_warning_template' => 'This page is in active use as a book or chapter default page template. These books or chapters will no longer have a default page template assigned after this page is deleted.', 'pages_delete_confirm' => 'Oletko varma, että haluat poistaa tämän sivun?', 'pages_delete_draft_confirm' => 'Haluatko varmasti poistaa tämän luonnoksen?', 'pages_editing_named' => 'Muokataan sivua :pageName', @@ -224,6 +251,8 @@ 'pages_edit_switch_to_markdown_clean' => '(Puhdas sisältö)', 'pages_edit_switch_to_markdown_stable' => '(Vakaa sisältö)', 'pages_edit_switch_to_wysiwyg' => 'Vaihda WYSIWYG-editoriin', + 'pages_edit_switch_to_new_wysiwyg' => 'Switch to new WYSIWYG', + 'pages_edit_switch_to_new_wysiwyg_desc' => '(In Beta Testing)', 'pages_edit_set_changelog' => 'Aseta muutosloki', 'pages_edit_enter_changelog_desc' => 'Kirjoita lyhyt kuvaus tekemistäsi muutoksista', 'pages_edit_enter_changelog' => 'Syötä muutosloki', @@ -243,6 +272,7 @@ 'pages_md_insert_drawing' => 'Lisää piirustus', 'pages_md_show_preview' => 'Näytä esikatselu', 'pages_md_sync_scroll' => 'Vieritä esikatselua koodin vierityksen mukaan', + 'pages_md_plain_editor' => 'Plaintext editor', 'pages_drawing_unsaved' => 'Tallentamaton piirustus löytyi', 'pages_drawing_unsaved_confirm' => 'Järjestelmä löysi tallentamattoman piirustuksen. Haluatko palauttaa piirustuksen ja jatkaa sen muokkaamista?', 'pages_not_in_chapter' => 'Sivu ei kuulu mihinkään lukuun', @@ -367,8 +397,11 @@ 'comment' => 'Kommentti', 'comments' => 'Kommentit', 'comment_add' => 'Lisää kommentti', + 'comment_none' => 'No comments to display', 'comment_placeholder' => 'Jätä kommentti tähän', - 'comment_count' => '{0} Ei kommentteja|{1} 1 kommentti|[2,*] :count kommenttia', + 'comment_thread_count' => ':count Comment Thread|:count Comment Threads', + 'comment_archived_count' => ':count Archived', + 'comment_archived_threads' => 'Archived Threads', 'comment_save' => 'Tallenna kommentti', 'comment_new' => 'Uusi kommentti', 'comment_created' => 'kommentoi :createDiff', @@ -377,8 +410,14 @@ 'comment_deleted_success' => 'Kommentti poistettu', 'comment_created_success' => 'Kommentti lisätty', 'comment_updated_success' => 'Kommentti päivitetty', + 'comment_archive_success' => 'Comment archived', + 'comment_unarchive_success' => 'Comment un-archived', + 'comment_view' => 'View comment', + 'comment_jump_to_thread' => 'Jump to thread', 'comment_delete_confirm' => 'Haluatko varmasti poistaa tämän kommentin?', 'comment_in_reply_to' => 'Vastaus kommenttiin :commentId', + 'comment_reference' => 'Reference', + 'comment_reference_outdated' => '(Outdated)', 'comment_editor_explain' => 'Tässä ovat sivulle jätetyt komentit. Kommentteja voi lisätä ja hallita, kun tarkastelet tallennettua sivua.', // Revision diff --git a/lang/fi/errors.php b/lang/fi/errors.php index 082cac2427d..9af7490d521 100644 --- a/lang/fi/errors.php +++ b/lang/fi/errors.php @@ -10,6 +10,7 @@ // Auth 'error_user_exists_different_creds' => 'Sähköpostiosoite :email on jo käytössä toisessa käyttäjätunnuksessa.', + 'auth_pre_register_theme_prevention' => 'Käyttäjätiliä ei voitu rekisteröidä annetuille tiedoille', 'email_already_confirmed' => 'Sähköposti on jo vahvistettu, yritä kirjautua sisään.', 'email_confirmation_invalid' => 'Tämä vahvistuslinkki ei ole voimassa tai sitä on jo käytetty, yritä rekisteröityä uudelleen.', 'email_confirmation_expired' => 'Vahvistuslinkki on vanhentunut, uusi vahvistussähköposti on lähetetty.', @@ -24,7 +25,6 @@ Sovellus ei tunnista ulkoisen todennuspalvelun pyyntöä. Ongelman voi aiheuttaa siirtyminen selaimessa takaisin edelliseen näkymään kirjautumisen jälkeen.', 'saml_fail_authed' => 'Sisäänkirjautuminen :system käyttäen epäonnistui, järjestelmä ei antanut valtuutusta', 'oidc_already_logged_in' => 'Olet jo kirjautunut sisään', - 'oidc_user_not_registered' => 'Käyttäjää :nimi ei ole rekisteröity ja automaattinen rekisteröinti on pois käytöstä', 'oidc_no_email_address' => 'Ulkoisen todennuspalvelun antamista tiedoista ei löytynyt tämän käyttäjän sähköpostiosoitetta', 'oidc_fail_authed' => 'Sisäänkirjautuminen :system käyttäen epäonnistui, järjestelmä ei antanut valtuutusta', 'social_no_action_defined' => 'Ei määriteltyä toimenpidettä', @@ -38,6 +38,7 @@ 'social_driver_not_found' => 'Sosiaalisen median tilin ajuria ei löytynyt', 'social_driver_not_configured' => ':socialAccount-tilin asetuksia ei ole määritetty oikein.', 'invite_token_expired' => 'Tämä kutsulinkki on vanhentunut. Voit sen sijaan yrittää palauttaa tilisi salasanan.', + 'login_user_not_found' => 'Käyttäjää tälle toiminnolle ei löytynyt.', // System 'path_not_writable' => 'Tiedostopolkuun :filePath ei voitu ladata tiedostoa. Tarkista polun kirjoitusoikeudet.', @@ -78,6 +79,7 @@ // Users 'users_cannot_delete_only_admin' => 'Ainoaa ylläpitäjää ei voi poistaa', 'users_cannot_delete_guest' => 'Vieraskäyttäjää ei voi poistaa', + 'users_could_not_send_invite' => 'Käyttäjää ei voitu luoda kutsun lähettämisen jälkeen', // Roles 'role_cannot_be_edited' => 'Tätä roolia ei voi muokata', @@ -104,6 +106,18 @@ 'app_down' => ':appName on kaatunut', 'back_soon' => 'Se palautetaan pian.', + // Import + 'import_zip_cant_read' => 'ZIP-tiedostoa ei voitu lukea.', + 'import_zip_cant_decode_data' => 'ZIP-tiedoston data.json sisältöä ei löydy eikä sitä voitu purkaa.', + 'import_zip_no_data' => 'ZIP-tiedostoilla ei ole odotettua kirjaa, lukua tai sivun sisältöä.', + 'import_validation_failed' => 'Tuonti ZIP epäonnistui virheiden kanssa:', + 'import_zip_failed_notification' => 'ZIP-tiedoston tuominen epäonnistui.', + 'import_perms_books' => 'Sinulla ei ole tarvittavia oikeuksia luoda kirjoja.', + 'import_perms_chapters' => 'Sinulla ei ole tarvittavia oikeuksia luoda kappaleita.', + 'import_perms_pages' => 'Sinulla ei ole tarvittavia oikeuksia luoda sivuja.', + 'import_perms_images' => 'Sinulla ei ole tarvittavia oikeuksia luoda kuvia.', + 'import_perms_attachments' => 'Sinulla ei ole tarvittavaa lupaa luoda liitteitä.', + // API errors 'api_no_authorization_found' => 'Pyynnöstä ei löytynyt valtuutuskoodia', 'api_bad_authorization_format' => 'Pyynnöstä löytyi valtuutuskoodi, mutta sen muoto oli virheellinen', diff --git a/lang/fi/settings.php b/lang/fi/settings.php index 03517b6e5bc..d9e91d4fa88 100644 --- a/lang/fi/settings.php +++ b/lang/fi/settings.php @@ -74,6 +74,36 @@ 'reg_confirm_restrict_domain_desc' => 'Kirjoita pilkulla erotettu luettelo sähköpostien domain-nimistä, joihin haluat rajoittaa rekisteröitymisen. Käyttäjille lähetetään sähköpostiviesti osoitteen vahvistamiseksi, ennen kuin he pääsevät käyttämään sivustoa.
Huomaa, että käyttäjät voivat muuttaa sähköpostiosoitteensa onnistuneen rekisteröinnin jälkeen.', 'reg_confirm_restrict_domain_placeholder' => 'Ei rajoituksia', + // Sorting Settings + 'sorting' => 'Sorting', + 'sorting_book_default' => 'Default Book Sort', + 'sorting_book_default_desc' => 'Select the default sort rule to apply to new books. This won\'t affect existing books, and can be overridden per-book.', + 'sorting_rules' => 'Sort Rules', + 'sorting_rules_desc' => 'These are predefined sorting operations which can be applied to content in the system.', + 'sort_rule_assigned_to_x_books' => 'Assigned to :count Book|Assigned to :count Books', + 'sort_rule_create' => 'Create Sort Rule', + 'sort_rule_edit' => 'Edit Sort Rule', + 'sort_rule_delete' => 'Delete Sort Rule', + 'sort_rule_delete_desc' => 'Remove this sort rule from the system. Books using this sort will revert to manual sorting.', + 'sort_rule_delete_warn_books' => 'This sort rule is currently used on :count book(s). Are you sure you want to delete this?', + 'sort_rule_delete_warn_default' => 'This sort rule is currently used as the default for books. Are you sure you want to delete this?', + 'sort_rule_details' => 'Sort Rule Details', + 'sort_rule_details_desc' => 'Set a name for this sort rule, which will appear in lists when users are selecting a sort.', + 'sort_rule_operations' => 'Sort Operations', + 'sort_rule_operations_desc' => 'Configure the sort actions to be performed by moving them from the list of available operations. Upon use, the operations will be applied in order, from top to bottom. Any changes made here will be applied to all assigned books upon save.', + 'sort_rule_available_operations' => 'Available Operations', + 'sort_rule_available_operations_empty' => 'No operations remaining', + 'sort_rule_configured_operations' => 'Configured Operations', + 'sort_rule_configured_operations_empty' => 'Drag/add operations from the "Available Operations" list', + 'sort_rule_op_asc' => '(Asc)', + 'sort_rule_op_desc' => '(Desc)', + 'sort_rule_op_name' => 'Name - Alphabetical', + 'sort_rule_op_name_numeric' => '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_last' => 'Chapters Last', + // Maintenance settings 'maint' => 'Huolto', 'maint_image_cleanup' => 'Siivoa kuvat', @@ -109,7 +139,7 @@ 'recycle_bin_contents_empty' => 'Roskakori on tällä hetkellä tyhjä', 'recycle_bin_empty' => 'Tyhjennä roskakori', 'recycle_bin_empty_confirm' => 'Tämä tuhoaa pysyvästi kaikki kohteet roskakorissa, mukaan lukien kunkin kohteen sisältämän sisällön. Haluatko varmasti tyhjentää roskakorin?', - 'recycle_bin_destroy_confirm' => 'Tämä toiminto poistaa tämän kohteen ja kaikki alla luetellut sisältyvät kohteet pysyvästi järjestelmästä, etkä voi enää palauttaa tätä sisältöä. Haluatko varmasti poistaa tämän kohteen pysyvästi?', + 'recycle_bin_destroy_confirm' => 'This action will permanently delete this item from the system, along with any child elements listed below, and you will not be able to restore this content. Are you sure you want to permanently delete this item?', 'recycle_bin_destroy_list' => 'Poistettavat kohteet', 'recycle_bin_restore_list' => 'Palautettavat kohteet', 'recycle_bin_restore_confirm' => 'Tämä toiminto palauttaa poistetun kohteen, mukaan lukien kaikki siihen sisältyvät kohteet, alkuperäiseen sijaintiinsa. Jos alkuperäinen sijainti on sittemmin poistettu ja on nyt roskakorissa, myös sitä koskeva kohde on palautettava.', @@ -162,6 +192,7 @@ 'role_access_api' => 'Pääsy järjestelmän ohjelmointirajapintaan', 'role_manage_settings' => 'Hallinnoi sivuston asetuksia', 'role_export_content' => 'Vie sisältöjä', + 'role_import_content' => 'Import content', 'role_editor_change' => 'Vaihda sivun editoria', 'role_notifications' => 'Vastaanota ja hallinnoi ilmoituksia', 'role_asset' => 'Sisältöjen oikeudet', @@ -276,6 +307,14 @@ 'webhooks_last_errored' => 'Viimeisin virhe:', 'webhooks_last_error_message' => 'Viimeisin virheviesti:', + // Licensing + 'licenses' => 'Licenses', + 'licenses_desc' => 'This page details license information for BookStack in addition to the projects & libraries that are used within BookStack. Many projects listed may only be used in a development context.', + 'licenses_bookstack' => 'BookStack License', + 'licenses_php' => 'PHP Library Licenses', + 'licenses_js' => 'JavaScript Library Licenses', + 'licenses_other' => 'Other Licenses', + 'license_details' => 'License Details', //! If editing translations files directly please ignore this in all //! languages apart from en. Content will be auto-copied from en. @@ -287,6 +326,7 @@ 'bs' => 'Bosanski', 'ca' => 'Català', 'cs' => 'Česky', + 'cy' => 'Cymraeg', 'da' => 'Dansk', 'de' => 'Deutsch (Sie)', 'de_informal' => 'Deutsch (Du)', @@ -308,6 +348,7 @@ 'lt' => 'Lietuvių Kalba', 'lv' => 'Latviešu Valoda', 'nb' => 'Norsk (Bokmål)', + 'ne' => 'नेपाली', 'nn' => 'Nynorsk', 'nl' => 'Nederlands', 'pl' => 'Polski', diff --git a/lang/fi/validation.php b/lang/fi/validation.php index 35c5f1924b2..8adc934a177 100644 --- a/lang/fi/validation.php +++ b/lang/fi/validation.php @@ -105,6 +105,11 @@ 'url' => ':attribute muoto ei ole kelvollinen.', 'uploaded' => 'Tiedostoa ei voitu ladata. Palvelin ei ehkä hyväksy tämän kokoisia tiedostoja.', + 'zip_file' => 'Attribuutin :attribute on viitattava tiedostoon ZIP-tiedoston sisällä.', + 'zip_file_mime' => 'The :attribute needs to reference a file of type :validTypes, found :foundType.', + 'zip_model_expected' => 'Data object expected but ":type" found.', + 'zip_unique' => 'The :attribute must be unique for the object type within the ZIP.', + // Custom validation lines 'custom' => [ 'password-confirm' => [ diff --git a/lang/fr/activities.php b/lang/fr/activities.php index 8edbab4b89c..b65134a2409 100644 --- a/lang/fr/activities.php +++ b/lang/fr/activities.php @@ -84,6 +84,14 @@ 'webhook_delete' => 'supprimer un Webhook', 'webhook_delete_notification' => 'Webhook supprimé avec succès', + // Imports + 'import_create' => 'import créé', + 'import_create_notification' => 'Importation envoyée avec succès', + 'import_run' => 'importation mise à jour', + 'import_run_notification' => 'Contenu importé avec succès', + 'import_delete' => 'import supprimé', + 'import_delete_notification' => 'Importation supprimée avec succès', + // Users 'user_create' => 'utilisateur créé', 'user_create_notification' => 'Utilisateur créé avec succès', @@ -93,11 +101,11 @@ 'user_delete_notification' => 'Utilisateur supprimé avec succès', // API Tokens - 'api_token_create' => 'jeton d\'api créé', + 'api_token_create' => 'a créé un jeton API', 'api_token_create_notification' => 'Jeton d\'API créé avec succès', - 'api_token_update' => 'jeton d\'api mis à jour', + 'api_token_update' => 'a mis à jour un jeton API', 'api_token_update_notification' => 'Jeton d\'API mis à jour avec succès', - 'api_token_delete' => 'jeton d\'api supprimé', + 'api_token_delete' => 'a supprimé un jeton API', 'api_token_delete_notification' => 'Jeton d\'API supprimé avec succès', // Roles @@ -119,6 +127,14 @@ 'comment_update' => 'Commentaire mis à jour', 'comment_delete' => 'Commentaire supprimé', + // Sort Rules + 'sort_rule_create' => 'règle de tri crée', + 'sort_rule_create_notification' => 'Règle de tri crée avec succès', + 'sort_rule_update' => 'règle de tri mise à jour', + 'sort_rule_update_notification' => 'Règle de tri mise à jour avec succès', + 'sort_rule_delete' => 'règle de tri supprimée', + 'sort_rule_delete_notification' => 'La règle de tri a été supprimée avec succès', + // Other 'permissions_update' => 'a mis à jour les autorisations sur', ]; diff --git a/lang/fr/auth.php b/lang/fr/auth.php index 32b8c27ec1c..fab11a83bfc 100644 --- a/lang/fr/auth.php +++ b/lang/fr/auth.php @@ -91,7 +91,7 @@ 'mfa_option_totp_title' => 'Application mobile', 'mfa_option_totp_desc' => 'Pour utiliser l\'authentification multi-facteurs, vous aurez besoin d\'une application mobile qui supporte TOTP comme Google Authenticator, Authy ou Microsoft Authenticator.', 'mfa_option_backup_codes_title' => 'Codes de secours', - 'mfa_option_backup_codes_desc' => 'Stockez en toute sécurité un jeu de codes de secours que vous pourrez utiliser pour vérifier votre identité.', + 'mfa_option_backup_codes_desc' => 'Génère un ensemble de codes de sauvegarde à usage unique que vous saisirez lors de la connexion pour vérifier votre identité. Veillez à les conserver dans un endroit sûr.', 'mfa_gen_confirm_and_enable' => 'Confirmer et activer', 'mfa_gen_backup_codes_title' => 'Configuration des codes de secours', 'mfa_gen_backup_codes_desc' => 'Stockez la liste des codes ci-dessous dans un endroit sûr. Lorsque vous accédez au système, vous pourrez utiliser l\'un des codes comme un deuxième mécanisme d\'authentification.', diff --git a/lang/fr/common.php b/lang/fr/common.php index 2f54ed7fe15..8f8a31135e0 100644 --- a/lang/fr/common.php +++ b/lang/fr/common.php @@ -20,7 +20,7 @@ 'description' => 'Description', 'role' => 'Rôle', 'cover_image' => 'Image de couverture', - 'cover_image_description' => 'Cette image doit faire environ 440x250 px.', + 'cover_image_description' => 'Cette image doit faire environ 440x250px. Elle sera mise à l\'échelle et recadrée automatiquement pour s\'adapter à l\'interface utilisateur, si nécessaire, à différents emplacements.', // Actions 'actions' => 'Actions', @@ -30,6 +30,8 @@ 'create' => 'Créer', 'update' => 'Modifier', 'edit' => 'Éditer', + 'archive' => 'Archiver', + 'unarchive' => 'Désarchiver', 'sort' => 'Trier', 'move' => 'Déplacer', 'copy' => 'Copier', @@ -107,4 +109,7 @@ // Not directly used but available for convenience to users. 'privacy_policy' => 'Politique de confidentialité', 'terms_of_service' => 'Conditions d\'utilisation', + + // OpenSearch + 'opensearch_description' => 'Recherche :appName', ]; diff --git a/lang/fr/editor.php b/lang/fr/editor.php index 78c4157e817..73bc6ad21db 100644 --- a/lang/fr/editor.php +++ b/lang/fr/editor.php @@ -13,6 +13,7 @@ 'cancel' => 'Annuler', 'save' => 'Sauvegarder', 'close' => 'Fermer', + 'apply' => 'Appliquer', 'undo' => 'Annuler', 'redo' => 'Rétablir', 'left' => 'Gauche', @@ -47,6 +48,7 @@ 'superscript' => 'Exposant', 'subscript' => 'Indice', 'text_color' => 'Couleur Texte', + 'highlight_color' => 'Couleur de surlignage', 'custom_color' => 'Couleur personnalisée', 'remove_color' => 'Supprimer la couleur', 'background_color' => 'Couleur d\'arrière-plan', @@ -81,6 +83,9 @@ 'table_properties' => 'Propriétés du tableau', 'table_properties_title' => 'Propriétés du tableau', 'delete_table' => 'Supprimer le tableau', + 'table_clear_formatting' => 'Effacer toute la mise en forme', + 'resize_to_contents' => 'Redimensionner au contenu', + 'row_header' => 'En-tête de ligne', 'insert_row_before' => 'Insérer une ligne avant', 'insert_row_after' => 'Insérer une ligne après', 'delete_row' => 'Supprimer ligne', @@ -144,6 +149,7 @@ 'url' => 'URL', 'text_to_display' => 'Texte à afficher', 'title' => 'Titre', + 'browse_links' => 'Parcourir les liens', 'open_link' => 'Ouvrir le lien', 'open_link_in' => 'Ouvrir le lien dans...', 'open_link_current' => 'Fenêtre actuelle', @@ -160,6 +166,8 @@ 'about' => 'À propos de l\'éditeur', 'about_title' => 'À propos de l\'éditeur WYSIWYG', 'editor_license' => 'Licence d\'éditeur et droit d\'auteur', + 'editor_lexical_license' => 'Cet éditeur est construit comme un fork de :lexicalLink qui est distribué sous licence MIT.', + 'editor_lexical_license_link' => 'Vous trouverez ici tous les détails de la licence.', 'editor_tiny_license' => 'Cet éditeur est construit en utilisant :tinyLink qui est fourni sous la licence MIT.', 'editor_tiny_license_link' => 'Vous trouverez ici les détails sur les droits d\'auteur et les licences de TinyMCE.', 'save_continue' => 'Enregistrer et continuer', diff --git a/lang/fr/entities.php b/lang/fr/entities.php index 32351548b0c..827e88a1b6d 100644 --- a/lang/fr/entities.php +++ b/lang/fr/entities.php @@ -11,7 +11,7 @@ 'recently_updated_pages' => 'Pages mises à jour récemment', 'recently_created_chapters' => 'Chapitres créés récemment', 'recently_created_books' => 'Livres créés récemment', - 'recently_created_shelves' => 'Étagères créés récemment', + 'recently_created_shelves' => 'Étagères créées récemment', 'recently_update' => 'Mis à jour récemment', 'recently_viewed' => 'Vus récemment', 'recent_activity' => 'Activité récente', @@ -39,6 +39,34 @@ 'export_pdf' => 'Fichier PDF', 'export_text' => 'Document texte', 'export_md' => 'Fichiers Markdown', + 'export_zip' => 'Export ZIP', + 'default_template' => 'Modèle de page par défaut', + 'default_template_explain' => 'Sélectionnez un modèle de page qui sera utilisé comme contenu par défaut pour les nouvelles pages créées dans cet élément. Gardez à l\'esprit que le modèle ne sera utilisé que si le créateur de la page a accès au modèle sélectionné.', + 'default_template_select' => 'Sélectionnez un modèle de page', + 'import' => 'Importation', + 'import_validate' => 'Valider l\'import', + 'import_desc' => 'Importez des livres, des chapitres et des pages à l\'aide d\'un export zip à partir de la même instance ou d\'une instance différente. Sélectionnez un fichier ZIP pour continuer. Une fois le fichier téléchargé et validé, vous pourrez configurer et confirmer l\'importation dans la vue suivante.', + 'import_zip_select' => 'Sélectionnez le fichier ZIP à télécharger', + 'import_zip_validation_errors' => 'Des erreurs ont été détectées lors de la validation du fichier ZIP fourni:', + 'import_pending' => 'Importations en attente', + 'import_pending_none' => 'Aucune importation n\'a été commencée.', + 'import_continue' => 'Continuer l\'importation', + 'import_continue_desc' => 'Examinez le contenu à importer à partir du fichier ZIP téléchargé. Lorsque vous êtes prêt, lancez l\'importation pour ajouter son contenu à ce système. Le fichier d\'importation ZIP téléchargé sera automatiquement supprimé si l\'importation est réussie.', + 'import_details' => 'Détails de l\'importation', + 'import_run' => 'Exécuter Importation', + 'import_size' => ':size taille du ZIP d\'import', + 'import_uploaded_at' => ':relativeTime téléchargé', + 'import_uploaded_by' => 'Téléchargé par', + 'import_location' => 'Emplacement de l\'importation', + 'import_location_desc' => 'Sélectionnez un emplacement cible pour votre contenu importé. Vous aurez besoin des autorisations appropriées pour créer dans l\'emplacement que vous choisissez.', + 'import_delete_confirm' => 'Êtes-vous sûr de vouloir supprimer cette importation ?', + 'import_delete_desc' => 'Ceci supprimera le fichier ZIP importé et ne pourra pas être annulé.', + 'import_errors' => 'Erreurs d\'importation', + 'import_errors_desc' => 'Les erreurs suivantes se sont produites lors de la tentative d\'importation :', + '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', // Permissions and restrictions 'permissions' => 'Autorisations', @@ -60,10 +88,10 @@ 'search_for_term' => 'recherche pour :term', 'search_more' => 'Plus de résultats', 'search_advanced' => 'Recherche avancée', - 'search_terms' => 'Mot-clé', + 'search_terms' => 'Termes de recherche', 'search_content_type' => 'Type de contenu', 'search_exact_matches' => 'Correspondances exactes', - 'search_tags' => 'Recherche par mots-clés', + 'search_tags' => 'Recherche par tags', 'search_options' => 'Options', 'search_viewed_by_me' => 'Vu par moi', 'search_not_viewed_by_me' => 'Non vu par moi', @@ -132,20 +160,19 @@ 'books_edit_named' => 'Modifier le livre :bookName', 'books_form_book_name' => 'Nom du livre', 'books_save' => 'Enregistrer le livre', - 'books_default_template' => 'Modèle de page par défaut', - 'books_default_template_explain' => 'Sélectionnez un modèle de page qui sera utilisé comme contenu par défaut pour les nouvelles pages créées dans ce livre. Gardez à l\'esprit que le modèle ne sera utilisé que si le créateur de la page a accès au modèle sélectionné.', - 'books_default_template_select' => 'Sélectionnez un modèle de page', 'books_permissions' => 'Permissions du livre', 'books_permissions_updated' => 'Permissions du livre mises à jour', 'books_empty_contents' => 'Aucune page ou chapitre n\'a été ajouté à ce livre.', 'books_empty_create_page' => 'Créer une nouvelle page', 'books_empty_sort_current_book' => 'Trier les pages du livre', 'books_empty_add_chapter' => 'Ajouter un chapitre', - 'books_permissions_active' => 'Permissions personnalisées activées', + 'books_permissions_active' => 'Permissions de livre actives', 'books_search_this' => 'Rechercher dans ce livre', 'books_navigation' => 'Navigation dans le livre', 'books_sort' => 'Trier les contenus du livre', - 'books_sort_desc' => 'Déplacez les chapitres et les pages d\'un livre pour réorganiser son contenu. D\'autres livres peuvent être ajoutés qui permettent de déplacer facilement les chapitres et les pages entre les livres.', + 'books_sort_desc' => 'Déplacer les pages et chapitres au sein d’un livre pour en réorganiser le contenu. D’autres livres peuvent être ajoutés pour faciliter le déplacement des pages et chapitres entre les livres. Facultativement, une règle de tri automatique peut être mise en place afin de trier le livre lorsqu’il est édité.', + 'books_sort_auto_sort' => 'Option de tri automatique', + 'books_sort_auto_sort_active' => 'Tri automatique actif : :sortName', 'books_sort_named' => 'Trier le livre :bookName', 'books_sort_name' => 'Trier par le nom', 'books_sort_created' => 'Trier par la date de création', @@ -207,7 +234,7 @@ 'pages_delete_draft' => 'Supprimer le brouillon', 'pages_delete_success' => 'Page supprimée', 'pages_delete_draft_success' => 'Brouillon supprimé', - 'pages_delete_warning_template' => 'Cette page est utilisée comme modèle de page par défaut dans un livre. Ces livres n\'utiliseront plus ce modèle après la suppression de cette page.', + 'pages_delete_warning_template' => 'Cette page actuellement utilisée comme modèle de page par défaut de livre ou de chapitre. Ces livres ou chapitres n\'auront plus de modèle de page par défaut assigné après la suppression de cette page.', 'pages_delete_confirm' => 'Êtes-vous sûr(e) de vouloir supprimer cette page ?', 'pages_delete_draft_confirm' => 'Êtes-vous sûr(e) de vouloir supprimer ce brouillon ?', 'pages_editing_named' => 'Modification de la page :pageName', @@ -224,6 +251,8 @@ 'pages_edit_switch_to_markdown_clean' => '(Contenu nettoyé)', 'pages_edit_switch_to_markdown_stable' => '(Contenu stable)', 'pages_edit_switch_to_wysiwyg' => 'Basculer vers l\'éditeur WYSIWYG', + 'pages_edit_switch_to_new_wysiwyg' => 'Basculer vers le nouveau WYSIWYG', + 'pages_edit_switch_to_new_wysiwyg_desc' => '(En bêta-test)', 'pages_edit_set_changelog' => 'Remplir le journal des changements', 'pages_edit_enter_changelog_desc' => 'Entrez une brève description des changements effectués', 'pages_edit_enter_changelog' => 'Ouvrir le journal des changements', @@ -243,6 +272,7 @@ 'pages_md_insert_drawing' => 'Insérer un dessin', 'pages_md_show_preview' => 'Prévisualisation', 'pages_md_sync_scroll' => 'Défilement prévisualisation', + 'pages_md_plain_editor' => 'Plaintext editor', 'pages_drawing_unsaved' => 'Dessin non enregistré trouvé', 'pages_drawing_unsaved_confirm' => 'Des données de dessin non enregistrées ont été trouvées à partir d\'une tentative de sauvegarde de dessin échouée. Voulez-vous restaurer et continuer à modifier ce dessin non sauvegardé ?', 'pages_not_in_chapter' => 'La page n\'est pas dans un chapitre', @@ -307,7 +337,7 @@ 'tag' => 'Mot-clé', 'tags' => 'Mots-clés', 'tags_index_desc' => 'Les tags peuvent être appliqués au contenu du système pour appliquer une forme flexible de catégorisation. Les tags peuvent avoir à la fois une clé et une valeur, la valeur étant facultative. Une fois appliqué, le contenu peut ensuite être interrogé à l’aide du nom et de la valeur du tag.', - 'tag_name' => 'Nom du mot-clé', + 'tag_name' => 'Nom de l’étiquette', 'tag_value' => 'Valeur du mot-clé (optionnel)', 'tags_explain' => "Ajouter des mots-clés pour catégoriser votre contenu.", 'tags_add' => 'Ajouter un autre mot-clé', @@ -367,8 +397,11 @@ 'comment' => 'Commentaire', 'comments' => 'Commentaires', 'comment_add' => 'Ajouter un commentaire', + 'comment_none' => 'Aucun commentaire à afficher', 'comment_placeholder' => 'Entrez vos commentaires ici', - 'comment_count' => '{0} Pas de commentaires|{1} Un commentaire|[2,*] :count commentaires', + 'comment_thread_count' => ':count Fil de commentaires|:count Fils de commentaires', + 'comment_archived_count' => ':count Archivé', + 'comment_archived_threads' => 'Fils archivés', 'comment_save' => 'Enregistrer le commentaire', 'comment_new' => 'Nouveau commentaire', 'comment_created' => 'commenté :createDiff', @@ -377,8 +410,14 @@ 'comment_deleted_success' => 'Commentaire supprimé', 'comment_created_success' => 'Commentaire ajouté', 'comment_updated_success' => 'Commentaire mis à jour', + 'comment_archive_success' => 'Commentaire archivé', + 'comment_unarchive_success' => 'Commentaire désarchiver', + 'comment_view' => 'Voir le commentaire', + 'comment_jump_to_thread' => 'Aller au fil', 'comment_delete_confirm' => 'Êtes-vous sûr de vouloir supprimer ce commentaire ?', 'comment_in_reply_to' => 'En réponse à :commentId', + 'comment_reference' => 'Référence', + 'comment_reference_outdated' => '(Obsolète)', 'comment_editor_explain' => 'Voici les commentaires qui ont été laissés sur cette page. Les commentaires peuvent être ajoutés et gérés en visualisant la page enregistrée.', // Revision diff --git a/lang/fr/errors.php b/lang/fr/errors.php index 2e4cfa21626..94d21e1dd14 100644 --- a/lang/fr/errors.php +++ b/lang/fr/errors.php @@ -10,6 +10,7 @@ // Auth 'error_user_exists_different_creds' => 'Un utilisateur avec l\'adresse :email existe déjà.', + '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é.', @@ -23,7 +24,6 @@ 'saml_invalid_response_id' => 'La requête du système d\'authentification externe n\'est pas reconnue par un processus démarré par cette application. Naviguer après une connexion peut causer ce problème.', 'saml_fail_authed' => 'Connexion avec :system échouée, le système n\'a pas fourni l\'autorisation réussie', 'oidc_already_logged_in' => 'Déjà connecté', - 'oidc_user_not_registered' => 'L\'utilisateur :name n\'est pas enregistré et l\'enregistrement automatique est désactivé', 'oidc_no_email_address' => 'Impossible de trouver une adresse e-mail pour cet utilisateur, dans les données fournies par le système d\'authentification externe', 'oidc_fail_authed' => 'La connexion en utilisant :system a échoué, le système n\'a pas fourni d\'autorisation avec succès', 'social_no_action_defined' => 'Pas d\'action définie', @@ -37,6 +37,7 @@ 'social_driver_not_found' => 'Pilote de compte de réseaux sociaux absent', 'social_driver_not_configured' => 'Vos préférences pour le compte :socialAccount sont incorrectes.', 'invite_token_expired' => 'Le lien de cette invitation a expiré. Vous pouvez essayer de réinitialiser votre mot de passe.', + 'login_user_not_found' => 'Impossible de trouver un utilisateur pour cette action.', // System 'path_not_writable' => 'Impossible d\'écrire dans :filePath. Assurez-vous d\'avoir les droits d\'écriture sur le serveur', @@ -77,6 +78,7 @@ // Users 'users_cannot_delete_only_admin' => 'Vous ne pouvez pas supprimer le dernier administrateur', 'users_cannot_delete_guest' => 'Vous ne pouvez pas supprimer l\'utilisateur invité', + 'users_could_not_send_invite' => 'Impossible de créer l\'utilisateur à cause d\'une erreur d\'envoi de l\'email d\'invitation', // Roles 'role_cannot_be_edited' => 'Ce rôle ne peut pas être modifié', @@ -103,6 +105,18 @@ 'app_down' => ':appName n\'est pas en service pour le moment', 'back_soon' => 'Nous serons bientôt de retour.', + // Import + 'import_zip_cant_read' => 'Impossible de lire le fichier ZIP.', + 'import_zip_cant_decode_data' => 'Impossible de trouver et de décoder le contenu ZIP data.json.', + 'import_zip_no_data' => 'Les données du fichier ZIP n\'ont pas de livre, de chapitre ou de page attendus.', + 'import_validation_failed' => 'L\'importation du ZIP n\'a pas été validée avec les erreurs :', + 'import_zip_failed_notification' => 'Impossible d\'importer le fichier ZIP.', + 'import_perms_books' => 'Vous n\'avez pas les permissions requises pour créer des livres.', + 'import_perms_chapters' => 'Vous n\'avez pas les permissions requises pour créer des chapitres.', + 'import_perms_pages' => 'Vous n\'avez pas les permissions requises pour créer des pages.', + 'import_perms_images' => 'Vous n\'avez pas les permissions requises pour créer des images.', + 'import_perms_attachments' => 'Vous n\'avez pas les permissions requises pour créer des pièces jointes.', + // API errors 'api_no_authorization_found' => 'Aucun jeton d\'autorisation trouvé pour la demande', 'api_bad_authorization_format' => 'Un jeton d\'autorisation a été trouvé pour la requête, mais le format semble incorrect', diff --git a/lang/fr/passwords.php b/lang/fr/passwords.php index e209c217823..17dd15d01bd 100644 --- a/lang/fr/passwords.php +++ b/lang/fr/passwords.php @@ -7,8 +7,8 @@ return [ 'password' => 'Les mots de passe doivent faire au moins 8 caractères et correspondre à la confirmation.', - 'user' => "Nous n'avons pas trouvé d'utilisateur avec cette adresse.", - 'token' => 'Le mot de passe reset du token n\'est pas valide pour cette adresse e-mail.', + 'user' => "Nous n'avons pas trouvé d'utilisateur avec cette adresse e-mail.", + 'token' => 'Le jeton de réinitialisation du mot de passe n\'est pas valide pour cette adresse e-mail.', 'sent' => 'Nous vous avons envoyé un lien de réinitialisation de mot de passe par e-mail !', 'reset' => 'Votre mot de passe a été réinitialisé !', diff --git a/lang/fr/preferences.php b/lang/fr/preferences.php index f26972d7c36..2180ed70c46 100644 --- a/lang/fr/preferences.php +++ b/lang/fr/preferences.php @@ -8,7 +8,7 @@ 'my_account' => 'Mon compte', 'shortcuts' => 'Raccourcis', - 'shortcuts_interface' => 'Préférences de raccourci de l\'interface utilisateur', + 'shortcuts_interface' => 'Préférences de raccourcis de l\'interface utilisateur', 'shortcuts_toggle_desc' => 'Ici vous pouvez activer ou désactiver les raccourcis clavier, utilisés pour la navigation et les actions.', 'shortcuts_customize_desc' => 'Vous pouvez personnaliser chaque raccourci ci-dessous. Il vous suffit d\'appuyer sur la combinaison de touche choisie après avoir sélectionné l\'entrée pour un raccourci.', 'shortcuts_toggle_label' => 'Raccourcis clavier activés', @@ -27,7 +27,7 @@ 'notifications_save' => 'Enregistrer les préférences', 'notifications_update_success' => 'Les préférences de notification ont été mises à jour !', 'notifications_watched' => 'Éléments surveillés et ignorés', - 'notifications_watched_desc' => ' Voici les éléments qui ont des préférences de surveillance personnalisées appliquées. Pour mettre à jour vos préférences pour celles-ci, consultez l\'élément puis trouvez les options de surveillance dans la barre latérale.', + 'notifications_watched_desc' => 'Voici les éléments qui ont des préférences de surveillance personnalisées appliquées. Pour mettre à jour vos préférences pour celles-ci, consultez l\'élément puis trouvez les options de surveillance dans la barre latérale.', 'auth' => 'Accès et sécurité', 'auth_change_password' => 'Changer le mot de passe', diff --git a/lang/fr/settings.php b/lang/fr/settings.php index 928fed4b351..e73d8a4b19d 100644 --- a/lang/fr/settings.php +++ b/lang/fr/settings.php @@ -74,6 +74,36 @@ 'reg_confirm_restrict_domain_desc' => 'Entrez une liste de domaines acceptés lors de l\'inscription, séparés par une virgule. Les utilisateurs recevront un e-mail de confirmation à cette adresse.
Les utilisateurs pourront changer leur adresse après inscription s\'ils le souhaitent.', 'reg_confirm_restrict_domain_placeholder' => 'Aucune restriction en place', + // Sorting Settings + 'sorting' => 'Tri', + 'sorting_book_default' => 'Tri des livres par défaut', + 'sorting_book_default_desc' => 'Sélectionnez le tri par défaut à mettre en place sur les nouveaux livres. Cela n’affectera pas les livres existants, et peut être redéfini dans les livres.', + 'sorting_rules' => 'Règles de tri', + 'sorting_rules_desc' => 'Ce sont les opérations de tri qui peuvent être appliquées au contenu du système.', + 'sort_rule_assigned_to_x_books' => 'Assignée à :count livre|Assignée à :count livres', + 'sort_rule_create' => 'Créer une règle de tri', + 'sort_rule_edit' => 'Modifier une règle de tri', + 'sort_rule_delete' => 'Supprimer une règle de tri', + 'sort_rule_delete_desc' => 'Supprimer cette règle du système. Les livres l’utilisant précédemment reviendront au tri manuel.', + 'sort_rule_delete_warn_books' => 'Cette règle est actuellement utilisée par :count livre(s). Êtes-vous sûr·e de vouloir la supprimer ?', + 'sort_rule_delete_warn_default' => 'Cette règle est actuellement utilisée par défaut pour les livres. Êtes-vous sûr·e de vouloir la supprimer ?', + 'sort_rule_details' => 'Détails de la Règle de Tri', + 'sort_rule_details_desc' => 'Assignez un nom à cette règle, qui apparaîtra dans les listes servant à sélectionner une règle de tri.', + 'sort_rule_operations' => 'Opérations de tri', + 'sort_rule_operations_desc' => 'Configurez les actions de tri à appliquer en les déplaçant depuis la liste d’opérations. Lors de l’usage de cette règle, les opérations seront appliquées dans l’ordre, de haut en bas. Les changements effectués ici seront appliqués à tous les livres utilisant cette règle lors de la sauvegarde.', + 'sort_rule_available_operations' => 'Opérations disponibles', + 'sort_rule_available_operations_empty' => 'Aucune opération restante', + 'sort_rule_configured_operations' => 'Opérations sélectionnées', + 'sort_rule_configured_operations_empty' => 'Déplacez/ajoutez des opérations depuis la liste "Opérations disponibles"', + 'sort_rule_op_asc' => '(Asc)', + 'sort_rule_op_desc' => '(Desc)', + 'sort_rule_op_name' => 'Nom - Alphabétique', + 'sort_rule_op_name_numeric' => 'Nom - Numérique', + 'sort_rule_op_created_date' => 'Date de création', + 'sort_rule_op_updated_date' => 'Date de mise à jour', + 'sort_rule_op_chapters_first' => 'Chapitres en premier', + 'sort_rule_op_chapters_last' => 'Chapitres en dernier', + // Maintenance settings 'maint' => 'Maintenance', 'maint_image_cleanup' => 'Nettoyer les images', @@ -162,6 +192,7 @@ 'role_access_api' => 'Accès à l\'API du système', 'role_manage_settings' => 'Gérer les préférences de l\'application', 'role_export_content' => 'Exporter le contenu', + 'role_import_content' => 'Importer le contenu', 'role_editor_change' => 'Changer l\'éditeur de page', 'role_notifications' => 'Recevoir et gérer les notifications', 'role_asset' => 'Permissions des ressources', @@ -222,7 +253,7 @@ 'users_api_tokens_desc' => 'Créer et gérer les jetons d\'accès utilisés pour s\'authentifier avec l\'API REST de BookStack. Les permissions pour l\'API sont gérées par l\'utilisateur auquel le jeton appartient.', 'users_api_tokens_none' => 'Aucun jeton API n\'a été créé pour cet utilisateur', 'users_api_tokens_create' => 'Créer un jeton', - 'users_api_tokens_expires' => 'Expiré', + 'users_api_tokens_expires' => 'Expire', 'users_api_tokens_docs' => 'Documentation de l\'API', 'users_mfa' => 'Authentification multi-facteurs', 'users_mfa_desc' => 'Configurer l\'authentification multi-facteurs ajoute une couche supplémentaire de sécurité à votre compte utilisateur.', @@ -276,6 +307,14 @@ 'webhooks_last_errored' => 'Dernier en erreur :', 'webhooks_last_error_message' => 'Dernier message d\'erreur : ', + // Licensing + 'licenses' => 'Licences', + 'licenses_desc' => 'Cette page détaille les informations de licence pour BookStack ainsi que les projets et librairies utilisées dans BookStack. Nombre des projets listés peuvent n\'être utilisés que dans un contexte de développement.', + 'licenses_bookstack' => 'Licences BookStack', + 'licenses_php' => 'Licences de librairies PHP', + 'licenses_js' => 'Licences de librairies JavaScript', + 'licenses_other' => 'Autres Licences', + 'license_details' => 'Détails de la licence', //! If editing translations files directly please ignore this in all //! languages apart from en. Content will be auto-copied from en. @@ -287,6 +326,7 @@ 'bs' => 'Bosniaque', 'ca' => 'Catalan', 'cs' => 'Tchèque', + 'cy' => 'Cymraeg', 'da' => 'Danois', 'de' => 'Allemand', 'de_informal' => 'Allemand (informel)', @@ -308,6 +348,7 @@ 'lt' => 'Lituanien', 'lv' => 'Letton', 'nb' => 'Norvegien', + 'ne' => 'नेपाली', 'nn' => 'Nynorsk', 'nl' => 'Néerlandais', 'pl' => 'Polonais', diff --git a/lang/fr/validation.php b/lang/fr/validation.php index 702c025854a..7db0493a165 100644 --- a/lang/fr/validation.php +++ b/lang/fr/validation.php @@ -105,6 +105,11 @@ 'url' => ':attribute a un format invalide.', 'uploaded' => 'Le fichier n\'a pas pu être envoyé. Le serveur peut ne pas accepter des fichiers de cette taille.', + 'zip_file' => 'L\'attribut :attribute doit référencer un fichier dans le ZIP.', + 'zip_file_mime' => ':attribute doit référencer un fichier de type :validTypes, trouvé :foundType.', + 'zip_model_expected' => 'Objet de données attendu, mais ":type" trouvé.', + 'zip_unique' => 'L\'attribut :attribute doit être unique pour le type d\'objet dans le ZIP.', + // Custom validation lines 'custom' => [ 'password-confirm' => [ diff --git a/lang/he/activities.php b/lang/he/activities.php index 1a21e4b0475..8dd244b7247 100644 --- a/lang/he/activities.php +++ b/lang/he/activities.php @@ -6,16 +6,16 @@ return [ // Pages - 'page_create' => 'דף נוצר', + 'page_create' => 'הדף נוצר', 'page_create_notification' => 'הדף נוצר בהצלחה', - 'page_update' => 'דף עודכן', + 'page_update' => 'הדף עודכן', 'page_update_notification' => 'הדף עודכן בהצלחה', - 'page_delete' => 'דף נמחק', + 'page_delete' => 'הדף נמחק', 'page_delete_notification' => 'הדף הוסר בהצלחה', - 'page_restore' => 'דף שוחזר', + 'page_restore' => 'הדף שוחזר', 'page_restore_notification' => 'הדף שוחזר בהצלחה', 'page_move' => 'דף הועבר', - 'page_move_notification' => 'Page successfully moved', + 'page_move_notification' => 'הדף הוזז בהצלחה', // Chapters 'chapter_create' => 'פרק נוצר', @@ -25,7 +25,7 @@ 'chapter_delete' => 'פרק נמחק', 'chapter_delete_notification' => 'הפרק נמחק בהצלחה', 'chapter_move' => 'פרק הועבר', - 'chapter_move_notification' => 'Chapter successfully moved', + 'chapter_move_notification' => 'פרק הוזז בהצלחה', // Books 'book_create' => 'ספר נוצר', @@ -50,74 +50,90 @@ 'bookshelf_delete_notification' => 'מדף נמחק בהצלחה', // Revisions - 'revision_restore' => 'restored revision', - 'revision_delete' => 'deleted revision', - 'revision_delete_notification' => 'Revision successfully deleted', + 'revision_restore' => 'גרסא שוחזרה', + 'revision_delete' => 'גרסא הוסרה', + 'revision_delete_notification' => 'גרסא הוסרה בהצלחה', // Favourites - 'favourite_add_notification' => '":name" has been added to your favourites', - 'favourite_remove_notification' => '":name" has been removed from your favourites', + 'favourite_add_notification' => '":name" הוסף למועדפים', + 'favourite_remove_notification' => '":name" הוסר מהמועדפים', // Watching - 'watch_update_level_notification' => 'Watch preferences successfully updated', + 'watch_update_level_notification' => 'העדפות צפייה עודכנו בהצלחה', // Auth - 'auth_login' => 'logged in', - 'auth_register' => 'registered as new user', - 'auth_password_reset_request' => 'requested user password reset', - 'auth_password_reset_update' => 'reset user password', - 'mfa_setup_method' => 'configured MFA method', - 'mfa_setup_method_notification' => 'Multi-factor method successfully configured', - 'mfa_remove_method' => 'removed MFA method', - 'mfa_remove_method_notification' => 'Multi-factor method successfully removed', + 'auth_login' => 'מחובר', + 'auth_register' => 'נרשם כמשתמש חדש', + 'auth_password_reset_request' => 'בקשת איפוס סיסמה למשתמש בוצעה בהצלחה', + 'auth_password_reset_update' => 'איפוס סיסמה למשתמש', + 'mfa_setup_method' => 'הגדרת אימות דו-שלבי פעיל', + 'mfa_setup_method_notification' => 'הגדרת אימות דו-שלבי בוצע בהצלחה', + 'mfa_remove_method' => 'הגדרת אימות דו-שלבי הוסר', + 'mfa_remove_method_notification' => 'אפשרות אימות דו-שלבי הוסר בהצלחה', // Settings - 'settings_update' => 'updated settings', - 'settings_update_notification' => 'Settings successfully updated', - 'maintenance_action_run' => 'ran maintenance action', + 'settings_update' => 'הגדרות עודכנו בהצלחה', + 'settings_update_notification' => 'ההגדרות עודכנו בהצלחה', + 'maintenance_action_run' => 'פעולות תחזוקה שהופעלו', // Webhooks - 'webhook_create' => 'created webhook', - 'webhook_create_notification' => 'Webhook נוצר בהצלחה', - 'webhook_update' => 'updated webhook', - 'webhook_update_notification' => 'Webhook successfully updated', - 'webhook_delete' => 'deleted webhook', - 'webhook_delete_notification' => 'Webhook successfully deleted', + 'webhook_create' => 'webook נוצר', + 'webhook_create_notification' => 'יצירת Webhook בוצעה בהצלחה', + 'webhook_update' => 'webhook עודכן', + 'webhook_update_notification' => 'webook עודכן בהצלחה', + 'webhook_delete' => 'Webhook נמחק', + 'webhook_delete_notification' => 'Webook נמחק בהצלחה', + + // Imports + 'import_create' => 'יבוא נוצר', + 'import_create_notification' => 'יבוא עודכן בהצלחה', + 'import_run' => 'יבוא עודכן', + 'import_run_notification' => 'תוכן יובא בהצלחה', + 'import_delete' => 'יבוא נמחק', + 'import_delete_notification' => 'יבוא נמחק בהצלחה', // Users - 'user_create' => 'created user', - 'user_create_notification' => 'User successfully created', - 'user_update' => 'updated user', + 'user_create' => 'משתמש חדש נוצר', + 'user_create_notification' => 'משתמש נוצר בהצלחה', + 'user_update' => 'משתמש עודכן', 'user_update_notification' => 'משתמש עודכן בהצלחה', - 'user_delete' => 'deleted user', + 'user_delete' => 'משתמש נמחק', 'user_delete_notification' => 'משתמש הוסר בהצלחה', // API Tokens - 'api_token_create' => 'created api token', - 'api_token_create_notification' => 'API token successfully created', - 'api_token_update' => 'updated api token', - 'api_token_update_notification' => 'API token successfully updated', - 'api_token_delete' => 'deleted api token', - 'api_token_delete_notification' => 'API token successfully deleted', + 'api_token_create' => 'API Token נוצר', + 'api_token_create_notification' => 'API Token נוצר בהצלחה', + 'api_token_update' => 'API Token עודכן', + 'api_token_update_notification' => 'API Token עודכן בהצלחה', + 'api_token_delete' => 'API Token נמחק', + 'api_token_delete_notification' => 'API Token נמחק בהצלחה', // Roles - 'role_create' => 'created role', + 'role_create' => 'תפקיד נוצר', 'role_create_notification' => 'תפקיד נוצר בהצלחה', - 'role_update' => 'updated role', + 'role_update' => 'תפקיד עודכן', 'role_update_notification' => 'תפקיד עודכן בהצלחה', - 'role_delete' => 'deleted role', + 'role_delete' => 'תפקיד נמחק', 'role_delete_notification' => 'תפקיד נמחק בהצלחה', // Recycle Bin - 'recycle_bin_empty' => 'emptied recycle bin', - 'recycle_bin_restore' => 'restored from recycle bin', - 'recycle_bin_destroy' => 'removed from recycle bin', + 'recycle_bin_empty' => 'סל המחזור רוקן', + 'recycle_bin_restore' => 'שוחזר מסל המחזור', + 'recycle_bin_destroy' => 'נמחק מסל המחזור', // Comments 'commented_on' => 'הגיב/ה על', - 'comment_create' => 'added comment', - 'comment_update' => 'updated comment', - 'comment_delete' => 'deleted comment', + 'comment_create' => 'הערה הוספה', + 'comment_update' => 'תגובה הוספה', + 'comment_delete' => 'תגובה נמחקה', + + // Sort Rules + 'sort_rule_create' => 'created sort rule', + 'sort_rule_create_notification' => 'Sort rule successfully created', + 'sort_rule_update' => 'updated sort rule', + 'sort_rule_update_notification' => 'Sort rule successfully updated', + 'sort_rule_delete' => 'deleted sort rule', + 'sort_rule_delete_notification' => 'Sort rule successfully deleted', // Other 'permissions_update' => 'הרשאות עודכנו', diff --git a/lang/he/auth.php b/lang/he/auth.php index 4e303a2ebbb..2948d5b2078 100644 --- a/lang/he/auth.php +++ b/lang/he/auth.php @@ -6,12 +6,12 @@ */ return [ - 'failed' => 'פרטי ההתחברות אינם תואמים את הנתונים שלנו', - 'throttle' => 'נסיונות התחברות רבים מדי, יש להמתין :seconds שניות ולנסות שנית', + 'failed' => 'פרטי ההתחברות אינם תואמים את הנתונים שלנו.', + 'throttle' => 'נסיונות התחברות מהירים מדי, יש להמתין :seconds שניות ולנסות שנית.', // Login & Register - 'sign_up' => 'הרשמה', - 'log_in' => 'התחבר', + 'sign_up' => 'הרשמה למערכת', + 'log_in' => 'התחבר למערכת', 'log_in_with' => 'התחבר באמצעות :socialDriver', 'sign_up_with' => 'הרשם באמצעות :socialDriver', 'logout' => 'התנתק', @@ -72,46 +72,46 @@ // User Invite 'user_invite_email_subject' => 'הוזמנת להצטרף ל:appName!', - 'user_invite_email_greeting' => 'An account has been created for you on :appName.', + 'user_invite_email_greeting' => 'חשבון נוצר עבורך ב :appName.', 'user_invite_email_text' => 'לחץ על הכפתור מטה בכדי להגדיר סיסמת משתמש ולקבל גישה:', 'user_invite_email_action' => 'הגדר סיסמה לחשבון', - 'user_invite_page_welcome' => 'Welcome to :appName!', - 'user_invite_page_text' => 'To finalise your account and gain access you need to set a password which will be used to log-in to :appName on future visits.', + 'user_invite_page_welcome' => 'ברוכים הבאים ל :appName!', + 'user_invite_page_text' => 'על מנת לסיים את ההרשמה ולקבל גישה עלייך להגדיר סיסמה אשר תהיה בשימוש בהתחברות ל :appName בביקורים עתידיים.', 'user_invite_page_confirm_button' => 'אימות סיסמא', - 'user_invite_success_login' => 'Password set, you should now be able to login using your set password to access :appName!', + 'user_invite_success_login' => 'הסיסמה הוגדרה בהצלחה, כעת תוכלו לקבל גישה ל :appName!', // Multi-factor Authentication 'mfa_setup' => 'הגדר אימות רב-שלבי', 'mfa_setup_desc' => 'הגדר אימות רב-שלבי כשכבת אבטחה נוספת עבור החשבון שלך.', 'mfa_setup_configured' => 'כבר הוגדר', 'mfa_setup_reconfigure' => 'הגדר מחדש', - 'mfa_setup_remove_confirmation' => 'Are you sure you want to remove this multi-factor authentication method?', - 'mfa_setup_action' => 'Setup', + 'mfa_setup_remove_confirmation' => 'האם להסיר את אפשרות האימות הדו-שלבי הזאת?', + 'mfa_setup_action' => 'הגדרה', 'mfa_backup_codes_usage_limit_warning' => 'נשאר לך פחות מ 5 קודי גיבוי, בבקשה חולל ואחסן סט חדש לפני שיגמרו לך הקודים בכדי למנוע נעילה מחוץ לחשבון שלך.', 'mfa_option_totp_title' => 'אפליקציה לנייד', 'mfa_option_totp_desc' => 'בכדי להשתמש באימות רב-שלבי תצטרך אפליקציית מובייל תומכת TOTP כמו Google Authenticator, Authy או Microsoft Authenticator.', 'mfa_option_backup_codes_title' => 'קודי גיבוי', - 'mfa_option_backup_codes_desc' => 'Securely store a set of one-time-use backup codes which you can enter to verify your identity.', - 'mfa_gen_confirm_and_enable' => 'Confirm and Enable', - 'mfa_gen_backup_codes_title' => 'Backup Codes Setup', - 'mfa_gen_backup_codes_desc' => 'Store the below list of codes in a safe place. When accessing the system you\'ll be able to use one of the codes as a second authentication mechanism.', - 'mfa_gen_backup_codes_download' => 'Download Codes', - 'mfa_gen_backup_codes_usage_warning' => 'Each code can only be used once', - 'mfa_gen_totp_title' => 'Mobile App Setup', - 'mfa_gen_totp_desc' => 'To use multi-factor authentication you\'ll need a mobile application that supports TOTP such as Google Authenticator, Authy or Microsoft Authenticator.', - 'mfa_gen_totp_scan' => 'Scan the QR code below using your preferred authentication app to get started.', - 'mfa_gen_totp_verify_setup' => 'Verify Setup', - 'mfa_gen_totp_verify_setup_desc' => 'Verify that all is working by entering a code, generated within your authentication app, in the input box below:', - 'mfa_gen_totp_provide_code_here' => 'Provide your app generated code here', - 'mfa_verify_access' => 'Verify Access', - 'mfa_verify_access_desc' => 'Your user account requires you to confirm your identity via an additional level of verification before you\'re granted access. Verify using one of your configured methods to continue.', - 'mfa_verify_no_methods' => 'No Methods Configured', + 'mfa_option_backup_codes_desc' => 'יצירת מערך סיסמאות חד-פעמיות כגיבוי אשר תתקבשו להזין על מנת לאמת את הזהות שלכם, אנא וודאו כי הקודים האלו שמורים במקום בטוח.', + 'mfa_gen_confirm_and_enable' => 'אישור והפעלה', + 'mfa_gen_backup_codes_title' => 'הגדרת קודי גיבוי', + 'mfa_gen_backup_codes_desc' => 'אנא שמור את רשימת הקודים הרשומים מטה במקום בטוח. בגישה למערכת תהיה אפשרות להשתמש באחד הקודים הללו כאמצעי זיהוי נוסף.', + 'mfa_gen_backup_codes_download' => 'הורדת קודים', + 'mfa_gen_backup_codes_usage_warning' => 'ניתן להשתמש בכל קוד פעם אחת בלבד', + 'mfa_gen_totp_title' => 'הגדרת אפליקציה לזיהוי', + 'mfa_gen_totp_desc' => 'על מנת להגדיר זיהוי דו-שלבי במכשיר נייד עלייך להשתמש באפליקציה התומכת ב TOTP כגון Google Authenticator, Authy או Microsoft Authenticator.', + 'mfa_gen_totp_scan' => 'סרוק את קוד ה QR באמצעות האפליקציה שבה מתבצע הזיהוי על מנת להתחיל.', + 'mfa_gen_totp_verify_setup' => 'אשר את ההגדרה', + 'mfa_gen_totp_verify_setup_desc' => 'על מנת לוודא כי הזיהוי הדו-שלבי עובד יש להכניס את הקוד, הוא מופיע לך על מסך האפליקציה, בשדה מטה:', + 'mfa_gen_totp_provide_code_here' => 'אנא הכנס את הקוד כאן', + 'mfa_verify_access' => 'אשר גישה', + 'mfa_verify_access_desc' => 'חשבון המשתמש שלך דורש ממך לאת את הזהות שלך בשכבת הגנה נוספת על מנת לאפשר לך גישה. יש לאשר גישה דרך אחד האמצעים הקיימים על מנת להמשיך.', + 'mfa_verify_no_methods' => 'אין אפשרויות אימות דו-שלבי מוגדרות', 'mfa_verify_no_methods_desc' => 'No multi-factor authentication methods could be found for your account. You\'ll need to set up at least one method before you gain access.', - 'mfa_verify_use_totp' => 'Verify using a mobile app', - 'mfa_verify_use_backup_codes' => 'Verify using a backup code', - 'mfa_verify_backup_code' => 'Backup Code', - 'mfa_verify_backup_code_desc' => 'Enter one of your remaining backup codes below:', - 'mfa_verify_backup_code_enter_here' => 'Enter backup code here', - 'mfa_verify_totp_desc' => 'Enter the code, generated using your mobile app, below:', + 'mfa_verify_use_totp' => 'אמת באמצעות אפליקציה', + 'mfa_verify_use_backup_codes' => 'אמת באמצעות קוד גיבוי', + 'mfa_verify_backup_code' => 'קוד גיבוי', + 'mfa_verify_backup_code_desc' => 'הזן מטה אחד מקודי הגיבוי הנותרים לך:', + 'mfa_verify_backup_code_enter_here' => 'הזן קוד גיבוי כאן', + 'mfa_verify_totp_desc' => 'הזן את הקוד, שהונפק דרך האפליקציה שלך, מטה:', 'mfa_setup_login_notification' => 'Multi-factor method configured, Please now login again using the configured method.', ]; diff --git a/lang/he/common.php b/lang/he/common.php index dafafe6ed18..2e387f67d78 100644 --- a/lang/he/common.php +++ b/lang/he/common.php @@ -20,7 +20,7 @@ 'description' => 'תיאור', 'role' => 'תפקיד', 'cover_image' => 'תמונת נושא', - 'cover_image_description' => 'התמונה צריכה להיות בסביבות 440x250px', + 'cover_image_description' => 'התמונה צריכה להיות לפחות בגודל של 440 על 250 למרות שהיא תוצג בצורה דינמית ותותאם בהתאם להתאים לממשק המשתמש במצבים שונים על פי הדרישה, ולכן המימדים יהיו שונים.', // Actions 'actions' => 'פעולות', @@ -30,6 +30,8 @@ 'create' => 'צור', 'update' => 'עדכן', 'edit' => 'ערוך', + 'archive' => 'Archive', + 'unarchive' => 'Un-Archive', 'sort' => 'מיין', 'move' => 'הזז', 'copy' => 'העתק', @@ -52,7 +54,7 @@ 'filter_clear' => 'נקה מסננים', 'download' => 'הורדה', 'open_in_tab' => 'פתח בכרטיסייה חדשה', - 'open' => 'Open', + 'open' => 'פתח', // Sort Options 'sort_options' => 'אפשרויות מיון', @@ -76,7 +78,7 @@ 'grid_view' => 'תצוגת רשת', 'list_view' => 'תצוגת רשימה', 'default' => 'ברירת מחדל', - 'breadcrumb' => 'Breadcrumb', + 'breadcrumb' => 'סימון מסלול', 'status' => 'סטטוס', 'status_active' => 'פעיל', 'status_inactive' => 'לא פעיל', @@ -107,4 +109,7 @@ // Not directly used but available for convenience to users. 'privacy_policy' => 'מדיניות הפרטיות', 'terms_of_service' => 'תנאי שימוש', + + // OpenSearch + 'opensearch_description' => 'חפש :appName', ]; diff --git a/lang/he/components.php b/lang/he/components.php index 9e075d89da9..30b9ab01a09 100644 --- a/lang/he/components.php +++ b/lang/he/components.php @@ -6,36 +6,36 @@ // Image Manager 'image_select' => 'בחירת תמונה', - 'image_list' => 'Image List', - 'image_details' => 'Image Details', - 'image_upload' => 'Upload Image', - 'image_intro' => 'Here you can select and manage images that have been previously uploaded to the system.', - 'image_intro_upload' => 'Upload a new image by dragging an image file into this window, or by using the "Upload Image" button above.', + 'image_list' => 'רשימת תמונות', + 'image_details' => 'פרטי תמונה', + 'image_upload' => 'העלאת תמונה', + 'image_intro' => 'כאן ניתן לבחור ולנהל תמונות אשר הועלו למערכת.', + 'image_intro_upload' => 'ניתן לגרור תמונות לחלון זה, או על ידי לחיצה על כפתור "העלאת תמונות" למעלה.', 'image_all' => 'הכל', 'image_all_title' => 'הצג את כל התמונות', 'image_book_title' => 'הצג תמונות שהועלו לספר זה', 'image_page_title' => 'הצג תמונות שהועלו לדף זה', 'image_search_hint' => 'חפש תמונה לפי שם', 'image_uploaded' => 'הועלה :uploadedDate', - 'image_uploaded_by' => 'Uploaded by :userName', - 'image_uploaded_to' => 'Uploaded to :pageLink', - 'image_updated' => 'Updated :updateDate', + 'image_uploaded_by' => 'הועלה על ידי :userName', + 'image_uploaded_to' => 'הועלה ל :pageLink', + 'image_updated' => 'עודכן :updateDate', 'image_load_more' => 'טען עוד', 'image_image_name' => 'שם התמונה', 'image_delete_used' => 'תמונה זו בשימוש בדפים שמתחת', 'image_delete_confirm_text' => 'האם את/ה בטוח/ה שברצונך למחוק את התמונה הזו?', 'image_select_image' => 'בחר תמונה', 'image_dropzone' => 'גרור תמונות או לחץ כאן להעלאה', - 'image_dropzone_drop' => 'Drop images here to upload', + 'image_dropzone_drop' => 'גרור תמונות לכאן על מנת להעלאות אותם', 'images_deleted' => 'התמונות נמחקו', 'image_preview' => 'תצוגה מקדימה', 'image_upload_success' => 'התמונה עלתה בהצלחה', 'image_update_success' => 'פרטי התמונה עודכנו בהצלחה', 'image_delete_success' => 'התמונה נמחקה בהצלחה', - 'image_replace' => 'Replace Image', - 'image_replace_success' => 'Image file successfully updated', - 'image_rebuild_thumbs' => 'Regenerate Size Variations', - 'image_rebuild_thumbs_success' => 'Image size variations successfully rebuilt!', + 'image_replace' => 'החלפת תמונה', + 'image_replace_success' => 'תמונה עודכנה בהצלחה', + 'image_rebuild_thumbs' => 'יצר ווריאציות גודל מחדש', + 'image_rebuild_thumbs_success' => 'ווריאציות גודל תמונה יוצרו מחדש בהצלחה!', // Code Editor 'code_editor' => 'ערוך קוד', diff --git a/lang/he/editor.php b/lang/he/editor.php index 86594f6b5be..6390389b803 100644 --- a/lang/he/editor.php +++ b/lang/he/editor.php @@ -13,6 +13,7 @@ 'cancel' => 'ביטול', 'save' => 'שמור', 'close' => 'סגור', + 'apply' => 'Apply', 'undo' => 'אחזר', 'redo' => 'בצע שוב', 'left' => 'שמאל', @@ -47,6 +48,7 @@ 'superscript' => 'כתב עילי', 'subscript' => 'כתב תחתי', 'text_color' => 'צבע טקסט', + 'highlight_color' => 'Highlight color', 'custom_color' => 'צבע מותאם', 'remove_color' => 'הסר צבע', 'background_color' => 'צבע רקע', @@ -81,6 +83,9 @@ 'table_properties' => 'Table properties', 'table_properties_title' => 'Table Properties', 'delete_table' => 'Delete table', + 'table_clear_formatting' => 'Clear table formatting', + 'resize_to_contents' => 'Resize to contents', + 'row_header' => 'Row header', 'insert_row_before' => 'Insert row before', 'insert_row_after' => 'Insert row after', 'delete_row' => 'Delete row', @@ -144,6 +149,7 @@ 'url' => 'קישור', 'text_to_display' => 'טקסט שיוצג', 'title' => 'כותרת', + 'browse_links' => 'Browse links', 'open_link' => 'Open link', 'open_link_in' => 'Open link in...', 'open_link_current' => 'החלון הנוכחי', @@ -160,6 +166,8 @@ 'about' => 'About the editor', 'about_title' => 'אודות עורך הטקסט הויזואלי', 'editor_license' => 'רשיון וזכויות העורך', + 'editor_lexical_license' => 'This editor is built as a fork of :lexicalLink which is distributed under the MIT license.', + 'editor_lexical_license_link' => 'Full license details can be found here.', 'editor_tiny_license' => 'This editor is built using :tinyLink which is provided under the MIT license.', 'editor_tiny_license_link' => 'The copyright and license details of TinyMCE can be found here.', 'save_continue' => 'שמור עמוד והמשך', diff --git a/lang/he/entities.php b/lang/he/entities.php index 113af659b9b..0cf2bb7fd32 100644 --- a/lang/he/entities.php +++ b/lang/he/entities.php @@ -25,11 +25,11 @@ 'meta_owned_name' => 'Owned by :user', 'meta_reference_count' => 'Referenced by :count item|Referenced by :count items', 'entity_select' => 'בחר יישות', - 'entity_select_lack_permission' => 'You don\'t have the required permissions to select this item', + 'entity_select_lack_permission' => 'אין לך אישורים דרושים לבחירת פריט זה', 'images' => 'תמונות', 'my_recent_drafts' => 'הטיוטות האחרונות שלי', 'my_recently_viewed' => 'הנצפים לאחרונה שלי', - 'my_most_viewed_favourites' => 'My Most Viewed Favourites', + 'my_most_viewed_favourites' => 'האהובים הנצפים ביותר שלי', 'my_favourites' => 'המועדפים שלי', 'no_pages_viewed' => 'לא צפית בדפים כלשהם', 'no_pages_recently_created' => 'לא נוצרו דפים לאחרונה', @@ -39,6 +39,34 @@ 'export_pdf' => 'קובץ PDF', 'export_text' => 'טקסט רגיל', 'export_md' => 'קובץ Markdown', + 'export_zip' => 'Portable ZIP', + 'default_template' => 'תבנית דף ברירת מחדל', + 'default_template_explain' => 'Assign a page template that will be used as the default content for all pages created within this item. Keep in mind this will only be used if the page creator has view access to the chosen template page.', + 'default_template_select' => 'בחר דף תבנית', + 'import' => 'יבוא', + 'import_validate' => 'תקף יבוא', + 'import_desc' => 'Import books, chapters & pages using a portable zip export from the same, or a different, instance. Select a ZIP file to proceed. After the file has been uploaded and validated you\'ll be able to configure & confirm the import in the next view.', + 'import_zip_select' => 'בחר קובץ ZIP להעלאה', + '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' => 'המשך יבוא', + '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_run' => 'להריץ יבוא', + 'import_size' => ':size Import ZIP Size', + 'import_uploaded_at' => 'Uploaded :relativeTime', + 'import_uploaded_by' => 'הועלה על ידי', + 'import_location' => 'מיקום יבוא', + '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_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', + 'breadcrumb_siblings_for_book' => 'Navigate siblings for book', + 'breadcrumb_siblings_for_bookshelf' => 'Navigate siblings for shelf', // Permissions and restrictions 'permissions' => 'הרשאות', @@ -132,9 +160,6 @@ 'books_edit_named' => 'עריכת ספר :bookName', 'books_form_book_name' => 'שם הספר', 'books_save' => 'שמור ספר', - 'books_default_template' => 'Default Page Template', - 'books_default_template_explain' => 'Assign a page template that will be used as the default content for all new pages in this book. Keep in mind this will only be used if the page creator has view access to those chosen template page.', - 'books_default_template_select' => 'Select a template page', 'books_permissions' => 'הרשאות ספר', 'books_permissions_updated' => 'הרשאות הספר עודכנו', 'books_empty_contents' => 'לא נוצרו פרקים או דפים עבור ספר זה', @@ -145,7 +170,9 @@ 'books_search_this' => 'חפש בספר זה', 'books_navigation' => 'ניווט בספר', 'books_sort' => 'מיין את תוכן הספר', - 'books_sort_desc' => 'Move chapters and pages within a book to reorganise its contents. Other books can be added which allows easy moving of chapters and pages between books.', + 'books_sort_desc' => 'Move chapters and pages within a book to reorganise its contents. Other books can be added which allows easy moving of chapters and pages between books. Optionally an auto sort rule can be set to automatically sort this book\'s contents upon changes.', + 'books_sort_auto_sort' => 'Auto Sort Option', + 'books_sort_auto_sort_active' => 'Auto Sort Active: :sortName', 'books_sort_named' => 'מיין את הספר :bookName', 'books_sort_name' => 'מיין לפי שם', 'books_sort_created' => 'מיין לפי תאריך יצירה', @@ -207,7 +234,7 @@ 'pages_delete_draft' => 'מחק טיוטת דף', 'pages_delete_success' => 'הדף נמחק', 'pages_delete_draft_success' => 'טיוטת דף נמחקה', - 'pages_delete_warning_template' => 'This page is in active use as a book default page template. These books will no longer have a default page template assigned after this page is deleted.', + 'pages_delete_warning_template' => 'This page is in active use as a book or chapter default page template. These books or chapters will no longer have a default page template assigned after this page is deleted.', 'pages_delete_confirm' => 'האם ברצונך למחוק דף זה?', 'pages_delete_draft_confirm' => 'האם ברצונך למחוק את טיוטת הדף הזה?', 'pages_editing_named' => 'עריכת דף :pageName', @@ -224,6 +251,8 @@ 'pages_edit_switch_to_markdown_clean' => '(Clean Content)', 'pages_edit_switch_to_markdown_stable' => '(Stable Content)', 'pages_edit_switch_to_wysiwyg' => 'Switch to WYSIWYG Editor', + 'pages_edit_switch_to_new_wysiwyg' => 'Switch to new WYSIWYG', + 'pages_edit_switch_to_new_wysiwyg_desc' => '(In Beta Testing)', 'pages_edit_set_changelog' => 'הגדר יומן שינויים', 'pages_edit_enter_changelog_desc' => 'ציין תיאור קצר אודות השינויים שביצעת', 'pages_edit_enter_changelog' => 'הכנס יומן שינויים', @@ -243,6 +272,7 @@ 'pages_md_insert_drawing' => 'הכנס סרטוט', 'pages_md_show_preview' => 'Show preview', 'pages_md_sync_scroll' => 'Sync preview scroll', + 'pages_md_plain_editor' => 'Plaintext editor', 'pages_drawing_unsaved' => 'Unsaved Drawing Found', 'pages_drawing_unsaved_confirm' => 'Unsaved drawing data was found from a previous failed drawing save attempt. Would you like to restore and continue editing this unsaved drawing?', 'pages_not_in_chapter' => 'דף אינו חלק מפרק', @@ -367,8 +397,11 @@ 'comment' => 'תגובה', 'comments' => 'תגובות', 'comment_add' => 'הוסף תגובה', + 'comment_none' => 'No comments to display', 'comment_placeholder' => 'השאר תגובה כאן', - 'comment_count' => '{0} אין תגובות|{1} 1 תגובה|[2,*] :count תגובות', + 'comment_thread_count' => ':count Comment Thread|:count Comment Threads', + 'comment_archived_count' => ':count Archived', + 'comment_archived_threads' => 'Archived Threads', 'comment_save' => 'שמור תגובה', 'comment_new' => 'תגובה חדשה', 'comment_created' => 'הוגב :createDiff', @@ -377,8 +410,14 @@ '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_delete_confirm' => 'האם ברצונך למחוק תגובה זו?', 'comment_in_reply_to' => 'בתגובה ל :commentId', + 'comment_reference' => 'Reference', + 'comment_reference_outdated' => '(Outdated)', 'comment_editor_explain' => 'Here are the comments that have been left on this page. Comments can be added & managed when viewing the saved page.', // Revision diff --git a/lang/he/errors.php b/lang/he/errors.php index 4de4d88c51e..75f7949ea76 100644 --- a/lang/he/errors.php +++ b/lang/he/errors.php @@ -10,6 +10,7 @@ // Auth 'error_user_exists_different_creds' => 'משתמש עם המייל :email כבר קיים אך עם פרטי הזדהות שונים', + 'auth_pre_register_theme_prevention' => 'User account could not be registered for the provided details', 'email_already_confirmed' => 'המייל כבר אומת, אנא נסה להתחבר', 'email_confirmation_invalid' => 'מפתח האימות אינו תקין או שכבר נעשה בו שימוש, אנא נסה להרשם שנית', 'email_confirmation_expired' => 'מפתח האימות פג-תוקף, מייל אימות חדש נשלח שוב.', @@ -23,7 +24,6 @@ 'saml_invalid_response_id' => 'The request from the external authentication system is not recognised by a process started by this application. Navigating back after a login could cause this issue.', 'saml_fail_authed' => 'Login using :system failed, system did not provide successful authorization', 'oidc_already_logged_in' => 'כבר מחובר', - 'oidc_user_not_registered' => 'The user :name is not registered and automatic registration is disabled', 'oidc_no_email_address' => 'Could not find an email address, for this user, in the data provided by the external authentication system', 'oidc_fail_authed' => 'Login using :system failed, system did not provide successful authorization', 'social_no_action_defined' => 'לא הוגדרה פעולה', @@ -37,6 +37,7 @@ 'social_driver_not_found' => 'Social driver not found', 'social_driver_not_configured' => 'הגדרות ה :socialAccount אינן מוגדרות כראוי', 'invite_token_expired' => 'לינק ההזמנה פג. אתה יכול לנסות לאפס את סיסמת החשבון שלך במקום.', + 'login_user_not_found' => 'A user for this action could not be found.', // System 'path_not_writable' => 'לא ניתן להעלות את :filePath אנא ודא שניתן לכתוב למיקום זה', @@ -77,6 +78,7 @@ // Users 'users_cannot_delete_only_admin' => 'אינך יכול למחוק את המנהל היחיד', 'users_cannot_delete_guest' => 'אינך יכול למחוק את משתמש האורח', + 'users_could_not_send_invite' => 'Could not create user since invite email failed to send', // Roles 'role_cannot_be_edited' => 'לא ניתן לערוך תפקיד זה', @@ -103,6 +105,18 @@ 'app_down' => ':appName כרגע אינו זמין', 'back_soon' => 'מקווים שיחזור במהרה', + // Import + 'import_zip_cant_read' => 'Could not read ZIP file.', + 'import_zip_cant_decode_data' => 'Could not find and decode ZIP data.json content.', + 'import_zip_no_data' => 'ZIP file data has no expected book, chapter or page content.', + 'import_validation_failed' => 'Import ZIP failed to validate with errors:', + 'import_zip_failed_notification' => 'Failed to import ZIP file.', + 'import_perms_books' => 'You are lacking the required permissions to create books.', + 'import_perms_chapters' => 'You are lacking the required permissions to create chapters.', + 'import_perms_pages' => 'You are lacking the required permissions to create pages.', + 'import_perms_images' => 'You are lacking the required permissions to create images.', + 'import_perms_attachments' => 'You are lacking the required permission to create attachments.', + // API errors 'api_no_authorization_found' => 'No authorization token found on the request', 'api_bad_authorization_format' => 'An authorization token was found on the request but the format appeared incorrect', diff --git a/lang/he/passwords.php b/lang/he/passwords.php index a94d7c30b36..3909680e3d4 100644 --- a/lang/he/passwords.php +++ b/lang/he/passwords.php @@ -6,10 +6,10 @@ */ return [ - 'password' => 'הסיסמה חייבת להיות בעלת 8 תווים לפחות ולהתאים לאימות.', - 'user' => "לא נמצא משתמש עם כתובת דוא\"ל זו.", - 'token' => 'אסימון איפוס הסיסמה לא תקף עבור כתובת דוא"ל זו.', - 'sent' => 'שלחנו לך דוא"ל עם קישור לאיפוס הסיסמה!', + 'password' => 'הסיסמא חייבת להיות בעלת 8 תווים לפחות ולהתאים לאימות.', + 'user' => "לא נמצא משתמש קיים עם כתובת דוא\"ל זו.", + 'token' => 'הקישור לאיפוס הסיסמה לא תקף עבור כתובת דוא"ל זו.', + 'sent' => 'שלחנו לך דוא"ל עם קישור לאיפוס הסיסמא!', 'reset' => 'איפוס הסיסמה הושלם בהצלחה!', ]; diff --git a/lang/he/preferences.php b/lang/he/preferences.php index 28c8b127c11..9e05eefb3ab 100644 --- a/lang/he/preferences.php +++ b/lang/he/preferences.php @@ -5,10 +5,10 @@ */ return [ - 'my_account' => 'My Account', + 'my_account' => 'החשבון שלי', 'shortcuts' => 'קיצורי דרך', - 'shortcuts_interface' => 'UI Shortcut Preferences', + 'shortcuts_interface' => 'העדפות קיצורי ממשק משתמש', 'shortcuts_toggle_desc' => 'כאן תוכל להפעיל או לבטל קיצורי דרך לממשק מערכת המקלדת, המשמשים לניווט ולפעולות.', 'shortcuts_customize_desc' => 'אתה יכול להתאים אישית כל אחד מקיצורי הדרך למטה. פשוט לחץ על צירוף המקשים הרצוי לאחר בחירת הקלט לקיצור דרך.', 'shortcuts_toggle_label' => 'קיצורי מקשים מופעלים', @@ -17,35 +17,35 @@ 'shortcuts_save' => 'שמור קיצורי דרך', 'shortcuts_overlay_desc' => 'הערה: כאשר קיצורי דרך מופעלים, שכבת-על מסייעת זמינה באמצעות לחיצה על "?" אשר ידגיש את קיצורי הדרך הזמינים לפעולות הנראות כעת על המסך.', 'shortcuts_update_success' => 'העדפותיך נשמרו!', - 'shortcuts_overview_desc' => 'Manage keyboard shortcuts you can use to navigate the system user interface.', + 'shortcuts_overview_desc' => 'ניהול קיצורי מקלדת שבשימוש לניווט מהיר בממשק המשתמש.', - 'notifications' => 'Notification Preferences', - 'notifications_desc' => 'Control the email notifications you receive when certain activity is performed within the system.', - 'notifications_opt_own_page_changes' => 'Notify upon changes to pages I own', - 'notifications_opt_own_page_comments' => 'Notify upon comments on pages I own', - 'notifications_opt_comment_replies' => 'Notify upon replies to my comments', - 'notifications_save' => 'Save Preferences', - 'notifications_update_success' => 'Notification preferences have been updated!', - 'notifications_watched' => 'Watched & Ignored Items', - 'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.', + 'notifications' => 'הגדרת התראות', + 'notifications_desc' => 'העדפות קבלת מייל והתראות כאשר מבוצעת פעולה מסויימת במערכת.', + 'notifications_opt_own_page_changes' => 'עדכן אותי כאשר מתבצעים שינויים לדפים שבבעלותי', + 'notifications_opt_own_page_comments' => 'עדכן אותי כאשר נוספות הערות לדפים שבבעלותי', + 'notifications_opt_comment_replies' => 'עדכן אותי כאשר מתקבלות תגובות להערות שלי', + 'notifications_save' => 'שמור העדפות', + 'notifications_update_success' => 'הגדרת התראות עודכנו!', + 'notifications_watched' => 'פריטים נצפים וברשימת התעלמות', + 'notifications_watched_desc' => 'להן רשימת הפריטים אשר קיימת עבורם הגדרות מותאמות אישית. על מנת לעדכן רשימה זו, צפה בפריט ומצא את אפשרות הצפייה / מעקב בפאנל הצידי.', - 'auth' => 'Access & Security', - 'auth_change_password' => 'Change Password', - 'auth_change_password_desc' => 'Change the password you use to log-in to the application. This must be at least 8 characters long.', - 'auth_change_password_success' => 'Password has been updated!', + 'auth' => 'גישה ואבטחה', + 'auth_change_password' => 'שינוי סיסמה', + 'auth_change_password_desc' => 'שינוי הסיסמה לכניסה למערכת. הסיסמה חייבת להיות לפחות 8 תווים.', + 'auth_change_password_success' => 'הסיסמה עודכנה בהצלחה!', - 'profile' => 'Profile Details', - 'profile_desc' => 'Manage the details of your account which represents you to other users, in addition to details that are used for communication and system personalisation.', - 'profile_view_public' => 'View Public Profile', - 'profile_name_desc' => 'Configure your display name which will be visible to other users in the system through the activity you perform, and content you own.', - 'profile_email_desc' => 'This email will be used for notifications and, depending on active system authentication, system access.', - 'profile_email_no_permission' => 'Unfortunately you don\'t have permission to change your email address. If you want to change this, you\'d need to ask an administrator to change this for you.', - 'profile_avatar_desc' => 'Select an image which will be used to represent yourself to others in the system. Ideally this image should be square and about 256px in width and height.', - 'profile_admin_options' => 'Administrator Options', - 'profile_admin_options_desc' => 'Additional administrator-level options, like those to manage role assignments, can be found for your user account in the "Settings > Users" area of the application.', + 'profile' => 'פרטי הפרופיל', + 'profile_desc' => 'נהל את פרטי החשבון שלך אשר יוצגו לאחרים במערכת, בנוסף לפרטים אשר נועדו להתאמה אישית במערכת ויצירת קשר.', + 'profile_view_public' => 'צפה בפרופיל הציבורי', + 'profile_name_desc' => 'הגדר את שם התצוגה שלך אשר יהיה זמין לצפייה לשאר משתמשי המערכת ובכל פעולה אשר תבצע במערכת, ובתוכן אשר שייך לך.', + 'profile_email_desc' => 'כתובת הדוא"ל תשמש להתראות, ובהתאם למערכות ההזדהות הפעילות לגישה למערכת.', + 'profile_email_no_permission' => 'לצערנו אין לך גישה לביצוע שינוי כתובת הדוא"ל שלך, אם ברצונך לבצע שינוי בכתובת אנא פנה למנהל המערכת על מנת שיבצע שינוי זה עבורך.', + 'profile_avatar_desc' => 'אנא בחר תמונה שתייצג אותך כלפי אחרים במערכת, תמונה אידיאלית צריכה להיות בגובה ואורך של 256 פיקסלים.', + 'profile_admin_options' => 'אפשרויות מנהל', + 'profile_admin_options_desc' => 'אפשרויות ברמת מנהל נוספות, כגון הוספת תפקידים והרשאות למשתמשים, אפשר למצוא עבור המשתמש שלך באיזור "הגדרות > משתמשים" במערכת.', - 'delete_account' => 'Delete Account', - 'delete_my_account' => 'Delete My Account', - 'delete_my_account_desc' => 'This will fully delete your user account from the system. You will not be able to recover this account or revert this action. Content you\'ve created, such as created pages and uploaded images, will remain.', - 'delete_my_account_warning' => 'Are you sure you want to delete your account?', + 'delete_account' => 'הסר חשבון', + 'delete_my_account' => 'הסר את החשבון שלי', + 'delete_my_account_desc' => 'פעולה זו תמחק את החשבון שלך כולל כל ההגדרות עבור החשבון שלך מהמערכת. אין דרך לשחזר את החשבון לאחר המחיקה. כל תוכן שיצרת כולל קבצים שהעלת לאתר ישארו פעילים.', + 'delete_my_account_warning' => 'הפעולה אינה ניתנת לביטול, האם למחוק את החשבון?', ]; diff --git a/lang/he/settings.php b/lang/he/settings.php index ead74732ced..5b405072f86 100644 --- a/lang/he/settings.php +++ b/lang/he/settings.php @@ -9,8 +9,8 @@ // Common Messages 'settings' => 'הגדרות', 'settings_save' => 'שמור הגדרות', - 'system_version' => 'System Version', - 'categories' => 'Categories', + 'system_version' => 'גירסת מערכת', + 'categories' => 'קטגוריות', // App Settings 'app_customization' => 'התאמה אישית', @@ -26,7 +26,7 @@ 'app_secure_images' => 'העלאת תמונות מאובטחת', 'app_secure_images_toggle' => 'אפשר העלאת תמונות מאובטחת', 'app_secure_images_desc' => 'משיקולי ביצועים, כל התמונות הינן ציבוריות. אפשרות זו מוסיפה מחרוזת אקראית שקשה לנחש לכל כתובת של תמונה. אנא ודא שאפשרות הצגת תוכן התיקייה מבוטל.', - 'app_default_editor' => 'Default Page Editor', + 'app_default_editor' => 'עורך דפים ברירת מחדל', 'app_default_editor_desc' => 'Select which editor will be used by default when editing new pages. This can be overridden at a page level where permissions allow.', 'app_custom_html' => 'HTML מותאם אישית לחלק העליון', 'app_custom_html_desc' => 'כל קוד שיתווסף כאן, יופיע בתחתית תגית ה head של כל דף. חלק זה שימושי על מנת להגדיר עיצובי CSS והתקנת קוד Analytics', @@ -74,6 +74,36 @@ 'reg_confirm_restrict_domain_desc' => 'הכנס דומיינים מופרדים בפסיק אשר עבורם תוגבל ההרשמה. משתמשים יקבלו אי-מייל על מנת לאמת את כתובת המייל שלהם.
לתשומת לבך: משתמש יוכל לשנות את כתובת המייל שלו לאחר ההרשמה', 'reg_confirm_restrict_domain_placeholder' => 'אין הגבלה לדומיין', + // Sorting Settings + 'sorting' => 'Sorting', + 'sorting_book_default' => 'Default Book Sort', + 'sorting_book_default_desc' => 'Select the default sort rule to apply to new books. This won\'t affect existing books, and can be overridden per-book.', + 'sorting_rules' => 'Sort Rules', + 'sorting_rules_desc' => 'These are predefined sorting operations which can be applied to content in the system.', + 'sort_rule_assigned_to_x_books' => 'Assigned to :count Book|Assigned to :count Books', + 'sort_rule_create' => 'Create Sort Rule', + 'sort_rule_edit' => 'Edit Sort Rule', + 'sort_rule_delete' => 'Delete Sort Rule', + 'sort_rule_delete_desc' => 'Remove this sort rule from the system. Books using this sort will revert to manual sorting.', + 'sort_rule_delete_warn_books' => 'This sort rule is currently used on :count book(s). Are you sure you want to delete this?', + 'sort_rule_delete_warn_default' => 'This sort rule is currently used as the default for books. Are you sure you want to delete this?', + 'sort_rule_details' => 'Sort Rule Details', + 'sort_rule_details_desc' => 'Set a name for this sort rule, which will appear in lists when users are selecting a sort.', + 'sort_rule_operations' => 'Sort Operations', + 'sort_rule_operations_desc' => 'Configure the sort actions to be performed by moving them from the list of available operations. Upon use, the operations will be applied in order, from top to bottom. Any changes made here will be applied to all assigned books upon save.', + 'sort_rule_available_operations' => 'Available Operations', + 'sort_rule_available_operations_empty' => 'No operations remaining', + 'sort_rule_configured_operations' => 'Configured Operations', + 'sort_rule_configured_operations_empty' => 'Drag/add operations from the "Available Operations" list', + 'sort_rule_op_asc' => '(Asc)', + 'sort_rule_op_desc' => '(Desc)', + 'sort_rule_op_name' => 'Name - Alphabetical', + 'sort_rule_op_name_numeric' => '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_last' => 'Chapters Last', + // Maintenance settings 'maint' => 'תחזוקה', 'maint_image_cleanup' => 'ניקוי תמונות', @@ -109,7 +139,7 @@ 'recycle_bin_contents_empty' => 'סל המיחזור כרגע ריק', 'recycle_bin_empty' => 'רוקן את סל המיחזור', 'recycle_bin_empty_confirm' => 'פעולה זו תשמיד לצמיתות את כל הפריטים בסל המיחזור, לרבות התוכן בכל פריט. אתם בטוחים שאתם מעוניינים לרוקן את סל המיחזור?', - 'recycle_bin_destroy_confirm' => 'פעולה זו תמחק מהמערכת לצמיתות פריט זה, יחד עם כל פריטי-הבן ברשימה להלן, ולא תוכלו לאחזר תוכל זה. אתם בטוחים שברצונכם למחוק לצמיתות פריט זה?', + 'recycle_bin_destroy_confirm' => 'This action will permanently delete this item from the system, along with any child elements listed below, and you will not be able to restore this content. Are you sure you want to permanently delete this item?', 'recycle_bin_destroy_list' => 'פריטים שיושמדו', 'recycle_bin_restore_list' => 'פריטים שיאוחזרו', 'recycle_bin_restore_confirm' => 'פעולה זו תאחזר את הפריט שנמחק, לרבות רכיבי-הבן שלו, למיקומו המקורי. אם המיקום המקורי נמחק מאז, וכעת נמצא בסל המיחזור, יש לאחזר גם את פריט-האב.', @@ -140,7 +170,7 @@ 'roles_x_users_assigned' => ':count user assigned|:count users assigned', 'roles_x_permissions_provided' => ':count permission|:count permissions', 'roles_assigned_users' => 'Assigned Users', - 'roles_permissions_provided' => 'Provided Permissions', + 'roles_permissions_provided' => 'הרשאות שהוקצו', 'role_create' => 'צור תפקיד משתמש חדש', 'role_delete' => 'מחק תפקיד', 'role_delete_confirm' => 'פעולה זו תמחק את התפקיד: :roleName', @@ -162,8 +192,9 @@ 'role_access_api' => 'גש ל-API המערכת', 'role_manage_settings' => 'ניהול הגדרות יישום', 'role_export_content' => 'Export content', - 'role_editor_change' => 'Change page editor', - 'role_notifications' => 'Receive & manage notifications', + 'role_import_content' => 'Import content', + 'role_editor_change' => 'שנה עורך עמודים', + 'role_notifications' => 'ניהול התראות', 'role_asset' => 'הרשאות משאבים', 'roles_system_warning' => 'שימו לב לכך שגישה לכל אחת משלושת ההרשאות הנ"ל יכולה לאפשר למשתמש לשנות את הפריווילגיות שלהם או של אחרים במערכת. הגדירו תפקידים להרשאות אלה למשתמשים בהם אתם בוטחים בלבד.', 'role_asset_desc' => 'הרשאות אלו שולטות בגישת ברירת המחדל למשאבים בתוך המערכת. הרשאות של ספרים, פרקים ודפים יגברו על הרשאות אלו.', @@ -276,6 +307,14 @@ 'webhooks_last_errored' => 'Last Errored:', 'webhooks_last_error_message' => 'Last Error Message:', + // Licensing + 'licenses' => 'Licenses', + 'licenses_desc' => 'This page details license information for BookStack in addition to the projects & libraries that are used within BookStack. Many projects listed may only be used in a development context.', + 'licenses_bookstack' => 'BookStack License', + 'licenses_php' => 'PHP Library Licenses', + 'licenses_js' => 'JavaScript Library Licenses', + 'licenses_other' => 'Other Licenses', + 'license_details' => 'License Details', //! If editing translations files directly please ignore this in all //! languages apart from en. Content will be auto-copied from en. @@ -287,6 +326,7 @@ 'bs' => 'Bosanski', 'ca' => 'Català', 'cs' => 'Česky', + 'cy' => 'Cymraeg', 'da' => 'Dansk', 'de' => 'Deutsch (Sie)', 'de_informal' => 'Deutsch (Du)', @@ -308,6 +348,7 @@ 'lt' => 'Lietuvių Kalba', 'lv' => 'Latviešu Valoda', 'nb' => 'Norsk (Bokmål)', + 'ne' => 'नेपाली', 'nn' => 'Nynorsk', 'nl' => 'Nederlands', 'pl' => 'Polski', diff --git a/lang/he/validation.php b/lang/he/validation.php index 85f6c698eb7..2d4f8b30598 100644 --- a/lang/he/validation.php +++ b/lang/he/validation.php @@ -15,7 +15,7 @@ 'alpha_dash' => 'שדה :attribute יכול להכיל אותיות, מספרים ומקפים בלבד.', 'alpha_num' => 'שדה :attribute יכול להכיל אותיות ומספרים בלבד.', 'array' => 'שדה :attribute חייב להיות מערך.', - 'backup_codes' => 'The provided code is not valid or has already been used.', + 'backup_codes' => 'קוד שהוזן לא תקין או שכבר השתמשו בו.', 'before' => 'שדה :attribute חייב להיות תאריך לפני :date.', 'between' => [ 'numeric' => 'שדה :attribute חייב להיות בין :min ל-:max.', @@ -105,6 +105,11 @@ 'url' => 'שדה :attribute בעל פורמט שאינו תקין.', 'uploaded' => 'שדה :attribute ארעה שגיאה בעת ההעלאה.', + 'zip_file' => 'The :attribute needs to reference a file within the ZIP.', + 'zip_file_mime' => 'The :attribute needs to reference a file of type :validTypes, found :foundType.', + 'zip_model_expected' => 'Data object expected but ":type" found.', + 'zip_unique' => 'The :attribute must be unique for the object type within the ZIP.', + // Custom validation lines 'custom' => [ 'password-confirm' => [ diff --git a/lang/hr/activities.php b/lang/hr/activities.php index 541f67646b6..bf79f9ab14a 100644 --- a/lang/hr/activities.php +++ b/lang/hr/activities.php @@ -84,6 +84,14 @@ 'webhook_delete' => 'web- dojavnik izbrisan', 'webhook_delete_notification' => 'Web-dojavnik je uspješno izbrisan', + // Imports + 'import_create' => 'created import', + 'import_create_notification' => 'Import successfully uploaded', + 'import_run' => 'updated import', + 'import_run_notification' => 'Content successfully imported', + 'import_delete' => 'deleted import', + 'import_delete_notification' => 'Import successfully deleted', + // Users 'user_create' => 'kreirani korisnik', 'user_create_notification' => 'Korisnik je uspješno kreiran', @@ -93,11 +101,11 @@ 'user_delete_notification' => 'Korisnik je uspješno uklonjen', // API Tokens - 'api_token_create' => 'kreirani API token', + 'api_token_create' => 'created API token', 'api_token_create_notification' => 'API token uspješno kreiran', - 'api_token_update' => 'ažurirani API token', + 'api_token_update' => 'updated API token', 'api_token_update_notification' => 'API token uspješno ažuriran', - 'api_token_delete' => 'obrisan API token', + 'api_token_delete' => 'deleted API token', 'api_token_delete_notification' => 'API token uspješno izbrisan', // Roles @@ -119,6 +127,14 @@ 'comment_update' => 'ažurirani komentar', 'comment_delete' => 'obrisani komentar', + // Sort Rules + 'sort_rule_create' => 'created sort rule', + 'sort_rule_create_notification' => 'Sort rule successfully created', + 'sort_rule_update' => 'updated sort rule', + 'sort_rule_update_notification' => 'Sort rule successfully updated', + 'sort_rule_delete' => 'deleted sort rule', + 'sort_rule_delete_notification' => 'Sort rule successfully deleted', + // Other 'permissions_update' => 'ažurirana dopuštenja', ]; diff --git a/lang/hr/auth.php b/lang/hr/auth.php index f77853c430d..bef07edcdaf 100644 --- a/lang/hr/auth.php +++ b/lang/hr/auth.php @@ -91,7 +91,7 @@ 'mfa_option_totp_title' => 'Mobilna Aplikacija', 'mfa_option_totp_desc' => 'Da biste koristili višestruku provjeru autentičnosti, trebat će vam mobilna aplikacija koja podržava TOTP (Time-Based One-Time Password) kao što su Google Authenticator, Authy ili Microsoft Authenticator.', 'mfa_option_backup_codes_title' => 'Rezervni Kodovi', - 'mfa_option_backup_codes_desc' => 'Sigurno pohranite set jednokratnih rezervnih kodova koje možete unijeti kako biste potvrdili svoj identitet.', + 'mfa_option_backup_codes_desc' => 'Generates a set of one-time-use backup codes which you\'ll enter on login to verify your identity. Make sure to store these in a safe & secure place.', 'mfa_gen_confirm_and_enable' => 'Potvrdi i Omogući', 'mfa_gen_backup_codes_title' => 'Postavke Rezervnih Kodova', 'mfa_gen_backup_codes_desc' => 'Spremite sljedeći popis kodova na sigurno mjesto. Prilikom pristupa sustavu, moći ćete koristiti jedan od ovih kodova kao drugi mehanizam autentifikacije.', diff --git a/lang/hr/common.php b/lang/hr/common.php index e9ee02c86f8..7cd779593bf 100644 --- a/lang/hr/common.php +++ b/lang/hr/common.php @@ -20,7 +20,7 @@ 'description' => 'Opis', 'role' => 'Uloga', 'cover_image' => 'Naslovna slika', - 'cover_image_description' => 'Slika treba biti približno 440x250px.', + 'cover_image_description' => 'This image should be approximately 440x250px although it will be flexibly scaled & cropped to fit the user interface in different scenarios as required, so actual dimensions for display will differ.', // Actions 'actions' => 'Aktivnost', @@ -30,6 +30,8 @@ 'create' => 'Stvori', 'update' => 'Ažuriraj', 'edit' => 'Uredi', + 'archive' => 'Archive', + 'unarchive' => 'Un-Archive', 'sort' => 'Razvrstaj', 'move' => 'Makni', 'copy' => 'Kopiraj', @@ -107,4 +109,7 @@ // Not directly used but available for convenience to users. 'privacy_policy' => 'Politika privatnosti', 'terms_of_service' => 'Uvjeti korištenja', + + // OpenSearch + 'opensearch_description' => 'Search :appName', ]; diff --git a/lang/hr/editor.php b/lang/hr/editor.php index a5372c87d3d..4ac9083188f 100644 --- a/lang/hr/editor.php +++ b/lang/hr/editor.php @@ -13,6 +13,7 @@ 'cancel' => 'Otkaži', 'save' => 'Spremi', 'close' => 'Zatvori', + 'apply' => 'Apply', 'undo' => 'Poništi', 'redo' => 'Ponovi', 'left' => 'Lijevo', @@ -47,6 +48,7 @@ 'superscript' => 'Gornji indeks', 'subscript' => 'Donji indeks', 'text_color' => 'Boja teksta', + 'highlight_color' => 'Highlight color', 'custom_color' => 'Prilagođena boja', 'remove_color' => 'Ukloni boju', 'background_color' => 'Boja pozadine', @@ -81,6 +83,9 @@ 'table_properties' => 'Svojstva tablice', 'table_properties_title' => 'Svojstva Tablice', 'delete_table' => 'Obriši tablicu', + 'table_clear_formatting' => 'Clear table formatting', + 'resize_to_contents' => 'Resize to contents', + 'row_header' => 'Row header', 'insert_row_before' => 'Umetni redak prije', 'insert_row_after' => 'Umetni redak poslije', 'delete_row' => 'Izbriši red', @@ -144,6 +149,7 @@ 'url' => 'URL', 'text_to_display' => 'Tekst za prikaz', 'title' => 'Naslov', + 'browse_links' => 'Browse links', 'open_link' => 'Otvori poveznicu', 'open_link_in' => 'Otvori poveznicu u...', 'open_link_current' => 'Trenutni prozor', @@ -160,6 +166,8 @@ 'about' => 'O Editoru', 'about_title' => 'O WYSIWYG Editoru', 'editor_license' => 'Licenca i autorsko pravo uređivača', + 'editor_lexical_license' => 'This editor is built as a fork of :lexicalLink which is distributed under the MIT license.', + 'editor_lexical_license_link' => 'Full license details can be found here.', 'editor_tiny_license' => 'Ovaj uređivač je izrađen pomoću: tinyLink koji je dostupan pod MIT licencom.', 'editor_tiny_license_link' => 'Detalji o autorskim pravima i licenci za TinyMCE mogu se pronaći ovdje.', 'save_continue' => 'Spremi Stranicu i Nastavi', diff --git a/lang/hr/entities.php b/lang/hr/entities.php index 6a69a0d92b8..99df50ea33c 100644 --- a/lang/hr/entities.php +++ b/lang/hr/entities.php @@ -39,6 +39,34 @@ 'export_pdf' => 'PDF Datoteka', 'export_text' => 'Text File', 'export_md' => 'Markdown Datoteka', + 'export_zip' => 'Portable ZIP', + 'default_template' => 'Default Page Template', + 'default_template_explain' => 'Assign a page template that will be used as the default content for all pages created within this item. Keep in mind this will only be used if the page creator has view access to the chosen template page.', + 'default_template_select' => 'Select a template page', + 'import' => 'Import', + 'import_validate' => 'Validate Import', + 'import_desc' => 'Import books, chapters & pages using a portable zip export from the same, or a different, instance. Select a ZIP file to proceed. After the file has been uploaded and validated you\'ll be able to configure & confirm the import in the next view.', + '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_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_size' => ':size Import ZIP Size', + 'import_uploaded_at' => 'Uploaded :relativeTime', + 'import_uploaded_by' => 'Uploaded by', + 'import_location' => 'Import Location', + '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_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', + 'breadcrumb_siblings_for_book' => 'Navigate siblings for book', + 'breadcrumb_siblings_for_bookshelf' => 'Navigate siblings for shelf', // Permissions and restrictions 'permissions' => 'Dopuštenja', @@ -132,9 +160,6 @@ 'books_edit_named' => 'Uredi knjigu :bookName', 'books_form_book_name' => 'Ime knjige', 'books_save' => 'Spremi knjigu', - 'books_default_template' => 'Default Page Template', - 'books_default_template_explain' => 'Assign a page template that will be used as the default content for all new pages in this book. Keep in mind this will only be used if the page creator has view access to those chosen template page.', - 'books_default_template_select' => 'Select a template page', 'books_permissions' => 'Dopuštenja za knjigu', 'books_permissions_updated' => 'Ažurirana dopuštenja za knjigu', 'books_empty_contents' => 'U ovoj knjizi još nema stranica ni poglavlja.', @@ -145,7 +170,9 @@ 'books_search_this' => 'Traži knjigu', 'books_navigation' => 'Navigacija knjige', 'books_sort' => 'Razvrstaj sadržaj knjige', - 'books_sort_desc' => 'Pomaknite poglavlja i stranice unutar knjige kako biste reorganizirali njen sadržaj. Možete dodati i druge knjige što omogućuje jednostavno premještanje poglavlja i stranica između knjiga.', + 'books_sort_desc' => 'Move chapters and pages within a book to reorganise its contents. Other books can be added which allows easy moving of chapters and pages between books. Optionally an auto sort rule can be set to automatically sort this book\'s contents upon changes.', + 'books_sort_auto_sort' => 'Auto Sort Option', + 'books_sort_auto_sort_active' => 'Auto Sort Active: :sortName', 'books_sort_named' => 'Razvrstaj knjigu :bookName', 'books_sort_name' => 'Razvrstaj po imenu', 'books_sort_created' => 'Razvrstaj po datumu nastanka', @@ -207,7 +234,7 @@ 'pages_delete_draft' => 'Izbriši nacrt stranice', 'pages_delete_success' => 'Izbrisana stranica', 'pages_delete_draft_success' => 'Izbrisan nacrt stranice', - 'pages_delete_warning_template' => 'This page is in active use as a book default page template. These books will no longer have a default page template assigned after this page is deleted.', + 'pages_delete_warning_template' => 'This page is in active use as a book or chapter default page template. These books or chapters will no longer have a default page template assigned after this page is deleted.', 'pages_delete_confirm' => 'Jeste li sigurni da želite izbrisati stranicu?', 'pages_delete_draft_confirm' => 'Jeste li sigurni da želite izbrisati nacrt stranice?', 'pages_editing_named' => 'Uređivanje stranice :pageName', @@ -224,6 +251,8 @@ 'pages_edit_switch_to_markdown_clean' => '(Čisti Sadržaj)', 'pages_edit_switch_to_markdown_stable' => '(Stabilan Sadržaj)', 'pages_edit_switch_to_wysiwyg' => 'Prebaci se na WYSIWYG uređivač', + 'pages_edit_switch_to_new_wysiwyg' => 'Switch to new WYSIWYG', + 'pages_edit_switch_to_new_wysiwyg_desc' => '(In Beta Testing)', 'pages_edit_set_changelog' => 'Postavi dnevnik promjena', 'pages_edit_enter_changelog_desc' => 'Ukratko opišite promjene koje ste napravili', 'pages_edit_enter_changelog' => 'Unesi dnevnik promjena', @@ -243,6 +272,7 @@ 'pages_md_insert_drawing' => 'Umetni crtež', 'pages_md_show_preview' => 'Prikaži pregled', 'pages_md_sync_scroll' => 'Sinkroniziraj pomicanje pregleda', + 'pages_md_plain_editor' => 'Plaintext editor', 'pages_drawing_unsaved' => 'Pronađen je Nespremljen Crtež', 'pages_drawing_unsaved_confirm' => 'Pronađeni su nespremljeni podaci crteža iz prethodnog neuspjelog pokušaja spremanja crteža. Želite li obnoviti i nastaviti uređivati ovaj nespremljeni crtež?', 'pages_not_in_chapter' => 'Stranica nije u poglavlju', @@ -367,8 +397,11 @@ 'comment' => 'Komentar', 'comments' => 'Komentari', 'comment_add' => 'Dodaj komentar', + 'comment_none' => 'No comments to display', 'comment_placeholder' => 'Komentar ostavi ovdje', - 'comment_count' => '{0} Nema komentara|{1} 1 Komentar|[2,*] :count Komentara', + 'comment_thread_count' => ':count Comment Thread|:count Comment Threads', + 'comment_archived_count' => ':count Archived', + 'comment_archived_threads' => 'Archived Threads', 'comment_save' => 'Spremi komentar', 'comment_new' => 'Novi komentar', 'comment_created' => 'komentirano :createDiff', @@ -377,8 +410,14 @@ 'comment_deleted_success' => 'Izbrisani komentar', 'comment_created_success' => 'Dodani komentar', 'comment_updated_success' => 'Ažurirani komentar', + 'comment_archive_success' => 'Comment archived', + 'comment_unarchive_success' => 'Comment un-archived', + 'comment_view' => 'View comment', + 'comment_jump_to_thread' => 'Jump to thread', 'comment_delete_confirm' => 'Jeste li sigurni da želite izbrisati ovaj komentar?', 'comment_in_reply_to' => 'Odgovor na :commentId', + 'comment_reference' => 'Reference', + 'comment_reference_outdated' => '(Outdated)', 'comment_editor_explain' => 'Evo komentara koji su ostavljeni na ovoj stranici. Komentari se mogu dodavati i upravljati prilikom pregleda spremljene stranice.', // Revision diff --git a/lang/hr/errors.php b/lang/hr/errors.php index 608c05e293e..92b9de7d24e 100644 --- a/lang/hr/errors.php +++ b/lang/hr/errors.php @@ -10,6 +10,7 @@ // Auth 'error_user_exists_different_creds' => 'Korisnik s mailom :email već postoji, ali s drugom vjerodajnicom.', + 'auth_pre_register_theme_prevention' => 'User account could not be registered for the provided details', 'email_already_confirmed' => 'Email je već potvrđen, pokušajte se logirati.', 'email_confirmation_invalid' => 'Ova vjerodajnica nije valjana ili je već bila korištena. Pokušajte se ponovno registrirati.', 'email_confirmation_expired' => 'Ova vjerodajnica je istekla. Poslan je novi email za pristup.', @@ -23,7 +24,6 @@ 'saml_invalid_response_id' => 'Sustav za autentifikaciju nije prepoznat. Ovaj problem možda je nastao zbog vraćanja nakon prijave.', 'saml_fail_authed' => 'Prijava pomoću :system nije uspjela zbog neuspješne autorizacije', 'oidc_already_logged_in' => 'Već ste prijavljeni', - 'oidc_user_not_registered' => 'Korisnik :name nije registriran i automatska registracija je onemogućena', 'oidc_no_email_address' => 'Nije moguće pronaći adresu e-pošte za ovog korisnika u podacima koje pruža vanjski sustav za autentifikaciju', 'oidc_fail_authed' => 'Prijavljivanje putem :system nije uspjelo. Sustav nije uspješno odobrio autorizaciju', 'social_no_action_defined' => 'Nije definirana nijedna radnja', @@ -37,6 +37,7 @@ 'social_driver_not_found' => 'Nije pronađeno', 'social_driver_not_configured' => 'Postavke vašeg :socialAccount računa nisu ispravno postavljene.', 'invite_token_expired' => 'Vaša pozivnica je istekla. Pokušajte ponovno postaviti lozinku.', + 'login_user_not_found' => 'A user for this action could not be found.', // System 'path_not_writable' => 'Datoteka :filePath ne može se prenijeti. Učinite je lakše prepoznatljivom vašem serveru.', @@ -77,6 +78,7 @@ // Users 'users_cannot_delete_only_admin' => 'Ne možete izbrisati', 'users_cannot_delete_guest' => 'Ne možete izbrisati', + 'users_could_not_send_invite' => 'Could not create user since invite email failed to send', // Roles 'role_cannot_be_edited' => 'Ne može se urediti', @@ -103,6 +105,18 @@ 'app_down' => ':appName trenutno nije dostupna', 'back_soon' => 'Uskoro će se vratiti.', + // Import + 'import_zip_cant_read' => 'Could not read ZIP file.', + 'import_zip_cant_decode_data' => 'Could not find and decode ZIP data.json content.', + 'import_zip_no_data' => 'ZIP file data has no expected book, chapter or page content.', + 'import_validation_failed' => 'Import ZIP failed to validate with errors:', + 'import_zip_failed_notification' => 'Failed to import ZIP file.', + 'import_perms_books' => 'You are lacking the required permissions to create books.', + 'import_perms_chapters' => 'You are lacking the required permissions to create chapters.', + 'import_perms_pages' => 'You are lacking the required permissions to create pages.', + 'import_perms_images' => 'You are lacking the required permissions to create images.', + 'import_perms_attachments' => 'You are lacking the required permission to create attachments.', + // API errors 'api_no_authorization_found' => 'Nije pronađena autorizacija', 'api_bad_authorization_format' => 'Pogreška prilikom autorizacije', diff --git a/lang/hr/preferences.php b/lang/hr/preferences.php index 520d9da73ee..2f409f8d3e4 100644 --- a/lang/hr/preferences.php +++ b/lang/hr/preferences.php @@ -29,7 +29,7 @@ 'notifications_save' => 'Spremi Postavke', 'notifications_update_success' => 'Postavke obavijesti su ažurirane!', 'notifications_watched' => 'Praćene i ignorirane stavke', - 'notifications_watched_desc' => ' Ispod su stavke na koje su primijenjene prilagođene postavke praćenja. Da biste ažurirali svoje postavke za ove stavke, pregledajte stavku, a zatim pronađite opcije praćenja u bočnoj traci.', + 'notifications_watched_desc' => 'Ispod su stavke na koje su primijenjene prilagođene postavke praćenja. Da biste ažurirali svoje postavke za ove stavke, pregledajte stavku, a zatim pronađite opcije praćenja u bočnoj traci.', 'auth' => 'Access & Security', 'auth_change_password' => 'Change Password', diff --git a/lang/hr/settings.php b/lang/hr/settings.php index 6040489ac39..5f000742631 100644 --- a/lang/hr/settings.php +++ b/lang/hr/settings.php @@ -74,6 +74,36 @@ 'reg_confirm_restrict_domain_desc' => 'Unesite popis email domena kojima želite ograničiti registraciju i odvojite ih zarezom. Korisnicima će se slati email prije interakcije s aplikacijom.
Uzmite u obzir da će korisnici moći koristiti druge e mail adrese nakon uspješne registracije.', 'reg_confirm_restrict_domain_placeholder' => 'Bez ograničenja', + // Sorting Settings + 'sorting' => 'Sorting', + 'sorting_book_default' => 'Default Book Sort', + 'sorting_book_default_desc' => 'Select the default sort rule to apply to new books. This won\'t affect existing books, and can be overridden per-book.', + 'sorting_rules' => 'Sort Rules', + 'sorting_rules_desc' => 'These are predefined sorting operations which can be applied to content in the system.', + 'sort_rule_assigned_to_x_books' => 'Assigned to :count Book|Assigned to :count Books', + 'sort_rule_create' => 'Create Sort Rule', + 'sort_rule_edit' => 'Edit Sort Rule', + 'sort_rule_delete' => 'Delete Sort Rule', + 'sort_rule_delete_desc' => 'Remove this sort rule from the system. Books using this sort will revert to manual sorting.', + 'sort_rule_delete_warn_books' => 'This sort rule is currently used on :count book(s). Are you sure you want to delete this?', + 'sort_rule_delete_warn_default' => 'This sort rule is currently used as the default for books. Are you sure you want to delete this?', + 'sort_rule_details' => 'Sort Rule Details', + 'sort_rule_details_desc' => 'Set a name for this sort rule, which will appear in lists when users are selecting a sort.', + 'sort_rule_operations' => 'Sort Operations', + 'sort_rule_operations_desc' => 'Configure the sort actions to be performed by moving them from the list of available operations. Upon use, the operations will be applied in order, from top to bottom. Any changes made here will be applied to all assigned books upon save.', + 'sort_rule_available_operations' => 'Available Operations', + 'sort_rule_available_operations_empty' => 'No operations remaining', + 'sort_rule_configured_operations' => 'Configured Operations', + 'sort_rule_configured_operations_empty' => 'Drag/add operations from the "Available Operations" list', + 'sort_rule_op_asc' => '(Asc)', + 'sort_rule_op_desc' => '(Desc)', + 'sort_rule_op_name' => 'Name - Alphabetical', + 'sort_rule_op_name_numeric' => '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_last' => 'Chapters Last', + // Maintenance settings 'maint' => 'Održavanje', 'maint_image_cleanup' => 'Čišćenje slika', @@ -109,7 +139,7 @@ 'recycle_bin_contents_empty' => 'Recycle Bin je prazan', 'recycle_bin_empty' => 'Isprazni Recycle Bin', 'recycle_bin_empty_confirm' => 'Ovo će trajno obrisati sve stavke u Recycle Bin i sadržaje povezane s njima. Jeste li sigurni da želite isprazniti Recycle Bin?', - 'recycle_bin_destroy_confirm' => 'Ovom radnjom ćete trajno izbrisati ovu stavku i nećete je više moći vratiti. Želite li je trajno izbrisati?', + 'recycle_bin_destroy_confirm' => 'This action will permanently delete this item from the system, along with any child elements listed below, and you will not be able to restore this content. Are you sure you want to permanently delete this item?', 'recycle_bin_destroy_list' => 'Stavke koje treba izbrisati', 'recycle_bin_restore_list' => 'Stavke koje treba vratiti', 'recycle_bin_restore_confirm' => 'Ova radnja vraća izbrisane stavke i njene podređene elemente na prvobitnu lokaciju. Ako je nadređena stavka izbrisana i nju treba vratiti.', @@ -162,6 +192,7 @@ 'role_access_api' => 'API pristup', 'role_manage_settings' => 'Upravljanje postavkama aplikacija', 'role_export_content' => 'Izvoz sadržaja', + 'role_import_content' => 'Import content', 'role_editor_change' => 'Promijeni uređivač stranica', 'role_notifications' => 'Primanje i upravljanje obavijestima', 'role_asset' => 'Upravljanje vlasništvom', @@ -276,6 +307,14 @@ 'webhooks_last_errored' => 'Zadnja pogreška:', 'webhooks_last_error_message' => 'Posljednja poruka o pogrešci:', + // Licensing + 'licenses' => 'Licenses', + 'licenses_desc' => 'This page details license information for BookStack in addition to the projects & libraries that are used within BookStack. Many projects listed may only be used in a development context.', + 'licenses_bookstack' => 'BookStack License', + 'licenses_php' => 'PHP Library Licenses', + 'licenses_js' => 'JavaScript Library Licenses', + 'licenses_other' => 'Other Licenses', + 'license_details' => 'License Details', //! If editing translations files directly please ignore this in all //! languages apart from en. Content will be auto-copied from en. @@ -287,6 +326,7 @@ 'bs' => 'Bosanski', 'ca' => 'Català', 'cs' => 'Česky', + 'cy' => 'Cymraeg', 'da' => 'Dansk', 'de' => 'Deutsch (Sie)', 'de_informal' => 'Deutsch (Du)', @@ -308,6 +348,7 @@ 'lt' => 'Lietuvių Kalba', 'lv' => 'Latviešu Valoda', 'nb' => 'Norsk (Bokmål)', + 'ne' => 'नेपाली', 'nn' => 'Nynorsk', 'nl' => 'Nederlands', 'pl' => 'Polski', diff --git a/lang/hr/validation.php b/lang/hr/validation.php index 95263ff07bd..32b11a9bd80 100644 --- a/lang/hr/validation.php +++ b/lang/hr/validation.php @@ -105,6 +105,11 @@ 'url' => 'Format :attribute nije valjan.', 'uploaded' => 'Datoteka se ne može prenijeti. Server možda ne prihvaća datoteke te veličine.', + 'zip_file' => 'The :attribute needs to reference a file within the ZIP.', + 'zip_file_mime' => 'The :attribute needs to reference a file of type :validTypes, found :foundType.', + 'zip_model_expected' => 'Data object expected but ":type" found.', + 'zip_unique' => 'The :attribute must be unique for the object type within the ZIP.', + // Custom validation lines 'custom' => [ 'password-confirm' => [ diff --git a/lang/hu/activities.php b/lang/hu/activities.php index a6db776415c..43683930bea 100644 --- a/lang/hu/activities.php +++ b/lang/hu/activities.php @@ -6,119 +6,135 @@ return [ // Pages - 'page_create' => 'létrehozta az oldalt:', + 'page_create' => 'létrehozta az oldalt', 'page_create_notification' => 'Oldal sikeresen létrehozva', - 'page_update' => 'frissítette az oldalt:', + 'page_update' => 'frissítette az oldalt', 'page_update_notification' => 'Oldal sikeresen frissítve', - 'page_delete' => 'törölte az oldalt:', + 'page_delete' => 'törölte az oldalt', 'page_delete_notification' => 'Oldal sikeresen törölve', - 'page_restore' => 'visszaállította az oldalt:', + 'page_restore' => 'visszaállította az oldalt', 'page_restore_notification' => 'Oldal sikeresen visszaállítva', - 'page_move' => 'áthelyezte az oldalt:', - 'page_move_notification' => 'Page successfully moved', + 'page_move' => 'áthelyezte az oldalt', + 'page_move_notification' => 'Oldal sikeresen áthelyezve', // Chapters - 'chapter_create' => 'létrehozta a fejezetet:', + 'chapter_create' => 'létrehozta a fejezetet', 'chapter_create_notification' => 'Fejezet sikeresen létrehozva', - 'chapter_update' => 'frissítette a fejezetet:', + 'chapter_update' => 'frissítette a fejezetet', 'chapter_update_notification' => 'Fejezet sikeresen frissítve', - 'chapter_delete' => 'törölte a fejezetet:', + 'chapter_delete' => 'törölte a fejezetet', 'chapter_delete_notification' => 'Fejezet sikeresen törölve', - 'chapter_move' => 'áthelyezte a fejezetet:', - 'chapter_move_notification' => 'Chapter successfully moved', + 'chapter_move' => 'áthelyezte a fejezetet', + 'chapter_move_notification' => 'Fejezet sikeresen áthelyezve', // Books - 'book_create' => 'létrehozott egy könyvet:', + 'book_create' => 'létrehozott egy könyvet', 'book_create_notification' => 'Könyv sikeresen létrehozva', - 'book_create_from_chapter' => 'converted chapter to book', - 'book_create_from_chapter_notification' => 'Chapter successfully converted to a book', - 'book_update' => 'frissítette a könyvet:', + 'book_create_from_chapter' => 'könyvvé alakította a fejezetet', + 'book_create_from_chapter_notification' => 'Fejezet sikeresen könyvvé lett alakítva', + 'book_update' => 'frissítette a könyvet', 'book_update_notification' => 'Könyv sikeresen frissítve', - 'book_delete' => 'törölte a könyvet:', + 'book_delete' => 'törölte a könyvet', 'book_delete_notification' => 'Könyv sikeresen törölve', - 'book_sort' => 'átrendezte a könyvet:', + 'book_sort' => 'átrendezte a könyvet', 'book_sort_notification' => 'Könyv sikeresen újrarendezve', // Bookshelves - 'bookshelf_create' => 'created shelf', + 'bookshelf_create' => 'létrehozta a polcot', 'bookshelf_create_notification' => 'Könyvespolc sikeresen létrehozva', - 'bookshelf_create_from_book' => 'converted book to shelf', - 'bookshelf_create_from_book_notification' => 'Book successfully converted to a shelf', - 'bookshelf_update' => 'updated shelf', - 'bookshelf_update_notification' => 'Shelf successfully updated', - 'bookshelf_delete' => 'deleted shelf', - 'bookshelf_delete_notification' => 'Shelf successfully deleted', + 'bookshelf_create_from_book' => 'könyvet polccá alakított', + 'bookshelf_create_from_book_notification' => 'Könyv sikeresen polccá lett alakítva', + 'bookshelf_update' => 'polcot frissített', + 'bookshelf_update_notification' => 'Polc sikeresen frissítve', + 'bookshelf_delete' => 'polcot törölt', + 'bookshelf_delete_notification' => 'Polc sikeresen törölve', // Revisions - 'revision_restore' => 'restored revision', - 'revision_delete' => 'deleted revision', - 'revision_delete_notification' => 'Revision successfully deleted', + 'revision_restore' => 'visszaállította a változatot', + 'revision_delete' => 'törölte a változatot', + 'revision_delete_notification' => 'Változat sikeresen törölve', // Favourites - 'favourite_add_notification' => '":name" has been added to your favourites', - 'favourite_remove_notification' => '":name" has been removed from your favourites', + 'favourite_add_notification' => '":name" hozzáadva a kedvencekhez', + 'favourite_remove_notification' => '":name" törölve a kedvencek közül', // Watching - 'watch_update_level_notification' => 'Watch preferences successfully updated', + 'watch_update_level_notification' => 'A megfigyelési beállítások sikeresen frissültek', // Auth - 'auth_login' => 'logged in', - 'auth_register' => 'registered as new user', - 'auth_password_reset_request' => 'requested user password reset', - 'auth_password_reset_update' => 'reset user password', - 'mfa_setup_method' => 'configured MFA method', - 'mfa_setup_method_notification' => 'Multi-factor method successfully configured', - 'mfa_remove_method' => 'removed MFA method', - 'mfa_remove_method_notification' => 'Multi-factor method successfully removed', + 'auth_login' => 'bejelentkezve', + 'auth_register' => 'új felhasználóként regisztrált', + 'auth_password_reset_request' => 'jelszó visszaállítást kért', + 'auth_password_reset_update' => 'felhasználói jelszó visszaállítás', + 'mfa_setup_method' => 'MFA módszert állított be', + 'mfa_setup_method_notification' => 'Többfaktoros azonosítás sikeresen beállítva', + 'mfa_remove_method' => 'MFA módszert törölt', + 'mfa_remove_method_notification' => 'Többfaktoros azonosítás sikeresen törölve', // Settings - 'settings_update' => 'updated settings', - 'settings_update_notification' => 'Settings successfully updated', - 'maintenance_action_run' => 'ran maintenance action', + 'settings_update' => 'frissítette a beállításokat', + 'settings_update_notification' => 'Beállítások sikeresen frissítve', + 'maintenance_action_run' => 'karbantartási műveletet futtatott', // Webhooks - 'webhook_create' => 'created webhook', + 'webhook_create' => 'webhookot hozott létre', 'webhook_create_notification' => 'Webhook sikeresen létrehozva', - 'webhook_update' => 'updated webhook', + 'webhook_update' => 'webhookot frissített', 'webhook_update_notification' => 'Webhook sikeresen frissítve', - 'webhook_delete' => 'deleted webhook', - 'webhook_delete_notification' => 'Webhook successfully deleted', + 'webhook_delete' => 'webhookot törölt', + 'webhook_delete_notification' => 'Webhook sikeresen törölve', + + // Imports + 'import_create' => 'created import', + 'import_create_notification' => 'Import successfully uploaded', + 'import_run' => 'updated import', + 'import_run_notification' => 'Content successfully imported', + 'import_delete' => 'deleted import', + 'import_delete_notification' => 'Import successfully deleted', // Users - 'user_create' => 'felhasználó létrehozása', + 'user_create' => 'létrehozta a felhasználót', 'user_create_notification' => 'Felhasználó sikeresen létrehozva', - 'user_update' => 'felhasználó módosítása', + 'user_update' => 'frissítette a felhasználót', 'user_update_notification' => 'Felhasználó sikeresen frissítve', - 'user_delete' => 'deleted user', + 'user_delete' => 'felhasználót törölt', 'user_delete_notification' => 'Felhasználó sikeresen eltávolítva', // API Tokens - 'api_token_create' => 'created api token', - 'api_token_create_notification' => 'API token successfully created', - 'api_token_update' => 'updated api token', - 'api_token_update_notification' => 'API token successfully updated', - 'api_token_delete' => 'deleted api token', - 'api_token_delete_notification' => 'API token successfully deleted', + 'api_token_create' => 'létrehozta az API tokent', + 'api_token_create_notification' => 'API token sikeresen létrehozva', + 'api_token_update' => 'frissítette az API tokent', + 'api_token_update_notification' => 'API token sikeresen frissítve', + 'api_token_delete' => 'törölte az API tokent', + 'api_token_delete_notification' => 'API token sikeresen törölve', // Roles - 'role_create' => 'created role', - 'role_create_notification' => 'Role successfully created', - 'role_update' => 'updated role', - 'role_update_notification' => 'Role successfully updated', - 'role_delete' => 'deleted role', - 'role_delete_notification' => 'Role successfully deleted', + 'role_create' => 'szerepkört hozott létre', + 'role_create_notification' => 'Szerepkör sikeresen létrehozva', + 'role_update' => 'frissítette a szerepkört', + 'role_update_notification' => 'Szerepkör sikeresen frissítve', + 'role_delete' => 'törölte a szerepkört', + 'role_delete_notification' => 'Szerepkör sikeresen törölve', // Recycle Bin - 'recycle_bin_empty' => 'emptied recycle bin', - 'recycle_bin_restore' => 'restored from recycle bin', - 'recycle_bin_destroy' => 'removed from recycle bin', + 'recycle_bin_empty' => 'kiürítette a lomtárat', + 'recycle_bin_restore' => 'lomtárból visszaállítva', + 'recycle_bin_destroy' => 'lomtárból törölve', // Comments 'commented_on' => 'megjegyzést fűzött hozzá:', - 'comment_create' => 'added comment', - 'comment_update' => 'updated comment', + 'comment_create' => 'hozzáadott hozzászólás', + 'comment_update' => 'frissített hozzászólás', 'comment_delete' => 'megjegyzés törlése', + // Sort Rules + 'sort_rule_create' => 'létrehozta a rendezési szabályt', + 'sort_rule_create_notification' => 'Rendezési szabály sikeresen létrehozva', + 'sort_rule_update' => 'frissítette a rendezési szabályt', + 'sort_rule_update_notification' => 'Rendezési szabály sikeresen frissítve', + 'sort_rule_delete' => 'törölte a rendezési szabályt', + 'sort_rule_delete_notification' => 'Rendezési szabály sikeresen törölve', + // Other 'permissions_update' => 'engedélyek frissítve', ]; diff --git a/lang/hu/auth.php b/lang/hu/auth.php index f7c30551fd9..90669e2fba2 100644 --- a/lang/hu/auth.php +++ b/lang/hu/auth.php @@ -26,47 +26,47 @@ 'remember_me' => 'Emlékezzen rám', 'ldap_email_hint' => 'A fiókhoz használt email cím megadása.', 'create_account' => 'Fiók létrehozása', - 'already_have_account' => 'Korábban volt beállítva fiók?', - 'dont_have_account' => 'Még nincs beállítva fiók?', + 'already_have_account' => 'Rendelkezik már felhasználói fiókkal?', + 'dont_have_account' => 'Még nincs felhasználói fiókja?', 'social_login' => 'Közösségi bejelentkezés', 'social_registration' => 'Közösségi regisztráció', 'social_registration_text' => 'Regisztráció és bejelentkezés másik szolgáltatással.', 'register_thanks' => 'Köszönjük a regisztrációt!', - 'register_confirm' => 'Ellenőrizni kell a megadott email címet és a megerősítő gombra kell kattintani :appName eléréséhez.', + 'register_confirm' => 'Ellenőrizze a megadott e-mail címet, és kattintson a megerősítő gombra :appName eléréséhez.', 'registrations_disabled' => 'A regisztráció jelenleg le van tiltva', 'registration_email_domain_invalid' => 'Ebből az email tartományról nem lehet hozzáférni ehhez az alkalmazáshoz', 'register_success' => 'Köszönjük a regisztrációt! A regisztráció és a bejelentkezés megtörtént.', // Login auto-initiation - 'auto_init_starting' => 'Attempting Login', - 'auto_init_starting_desc' => 'We\'re contacting your authentication system to start the login process. If there\'s no progress after 5 seconds you can try clicking the link below.', - 'auto_init_start_link' => 'Proceed with authentication', + 'auto_init_starting' => 'Bejelentkezési kísérlet', + 'auto_init_starting_desc' => 'Kapcsolatba lépünk az azonosítási rendszereddel, hogy elkezdjük a bejelentkezési folyamatot. Ha 5 másodperc után sem történik előrelépés, próbálkozhatsz az alábbi linkre kattintva.', + 'auto_init_start_link' => 'Folytatás azonosítással', // Password Reset 'reset_password' => 'Jelszó visszaállítása', - 'reset_password_send_instructions' => 'Meg kell adni az email címet amire egy jelszó visszaállító hivatkozás lesz elküldve.', + 'reset_password_send_instructions' => 'Adja meg az e-mail címet, amire a jelszó-visszaállító linket küldjük.', 'reset_password_send_button' => 'Visszaállító hivatkozás elküldése', - 'reset_password_sent' => 'A password reset link will be sent to :email if that email address is found in the system.', + 'reset_password_sent' => 'A jelszó-visszaállító linket e-mailben fogjuk elküldeni a(z) :email címre, ha beállításra került a rendszerben.', 'reset_password_success' => 'A jelszó sikeresen visszaállítva.', 'email_reset_subject' => ':appName jelszó visszaállítása', - 'email_reset_text' => 'Ezt az emailt azért küldtük mert egy jelszó visszaállításra vonatkozó kérést kaptunk ebből a fiókból.', - 'email_reset_not_requested' => 'Ha nem történt jelszó visszaállításra vonatkozó kérés, akkor nincs szükség további intézkedésre.', + 'email_reset_text' => 'Ezt az e-mailt azért küldtük, mert egy jelszó-visszaállításra vonatkozó kérést kaptunk ebből a fiókból.', + 'email_reset_not_requested' => 'Ha nem Ön kérte a jelszó visszaállítását, akkor nincs szükség további intézkedésre.', // Email Confirmation 'email_confirm_subject' => ':appName alkalmazásban beállított email címet meg kell erősíteni', 'email_confirm_greeting' => ':appName köszöni a csatlakozást!', 'email_confirm_text' => 'Az email címet a lenti gombra kattintva lehet megerősíteni:', 'email_confirm_action' => 'Email megerősítése', - 'email_confirm_send_error' => 'Az email megerősítés kötelező, de a rendszer nem tudta elküldeni az emailt. Fel kell venni a kapcsolatot az adminisztrátorral és meg kell győződni róla, hogy az email beállítások megfelelőek.', - 'email_confirm_success' => 'Your email has been confirmed! You should now be able to login using this email address.', - 'email_confirm_resent' => 'Megerősítő email újraküldve. Ellenőrizni kell a bejövő üzeneteket.', - 'email_confirm_thanks' => 'Thanks for confirming!', - 'email_confirm_thanks_desc' => 'Please wait a moment while your confirmation is handled. If you are not redirected after 3 seconds press the "Continue" link below to proceed.', + 'email_confirm_send_error' => 'Az e-mail megerősítés kötelező, de a rendszer nem tudta elküldeni az e-mailt. Keresse fel az adminisztrátort, és gondoskodjon róla, hogy az e-mail helyesen van beállítva.', + 'email_confirm_success' => 'Az Ön e-mail címe sikeresen meg lett erősítve, most már be tud jelentkezni az e-mail címe használatával.', + 'email_confirm_resent' => 'Megerősítő e-mail újraküldve. Ellenőrizze a bejövő üzeneteit!', + 'email_confirm_thanks' => 'Köszönjük a megerősítést!', + 'email_confirm_thanks_desc' => 'Kérjük, várjon egy pillanatot, amíg a megerősítést kezeljük. Ha nem kerül átirányításra 3 másodperc után, kattintson a lenti "Folytatás" linkre a továbbhaladáshoz.', 'email_not_confirmed' => 'Az email cím nincs megerősítve', 'email_not_confirmed_text' => 'Az email cím még nincs megerősítve.', - 'email_not_confirmed_click_link' => 'Rá kell kattintani a regisztráció után nem sokkal elküldött emailben található hivatkozásra.', + 'email_not_confirmed_click_link' => 'Kattintson a regisztráció után nem sokkal elküldött e-mailben található hivatkozásra.', 'email_not_confirmed_resend' => 'Ha nem érkezik meg a megerősítő email, a lenti űrlap beküldésével újra lehet küldeni.', 'email_not_confirmed_resend_button' => 'Megerősítő email újraküldése', @@ -78,40 +78,40 @@ 'user_invite_page_welcome' => ':appName üdvözöl!', 'user_invite_page_text' => 'A fiók véglegesítéséhez és a hozzáféréshez be kell állítani egy jelszót ami :appName weboldalon lesz használva a bejelentkezéshez.', 'user_invite_page_confirm_button' => 'Jelszó megerősítése', - 'user_invite_success_login' => 'Password set, you should now be able to login using your set password to access :appName!', + 'user_invite_success_login' => 'Jelszó beállítva. Most már be tudsz jelentkezni a beállított jelszóval a következő rendszerbe: :appName!', // Multi-factor Authentication - 'mfa_setup' => 'Setup Multi-Factor Authentication', - 'mfa_setup_desc' => 'Setup multi-factor authentication as an extra layer of security for your user account.', - 'mfa_setup_configured' => 'Already configured', - 'mfa_setup_reconfigure' => 'Reconfigure', - 'mfa_setup_remove_confirmation' => 'Are you sure you want to remove this multi-factor authentication method?', + 'mfa_setup' => 'Többlépcsős azonosítás beállítása', + 'mfa_setup_desc' => 'Állítsa be a többlépcsős azonosítást egy extra biztonsági rétegként a felhasználói fiókjához.', + 'mfa_setup_configured' => 'Már beállítva', + 'mfa_setup_reconfigure' => 'Újrakonfigurálás', + 'mfa_setup_remove_confirmation' => 'Biztosan ki szeretné kapcsolni a többlépcsős azonosítást?', 'mfa_setup_action' => 'Beállítások', 'mfa_backup_codes_usage_limit_warning' => 'Kevesebb, mint 5 visszaállítási kódja maradt. Kérem, hogy generáljon új kódokat, hogy csökkentse a rendszerből való kizárásának esélyét.', 'mfa_option_totp_title' => 'Mobilalkalmazás', - 'mfa_option_totp_desc' => 'To use multi-factor authentication you\'ll need a mobile application that supports TOTP such as Google Authenticator, Authy or Microsoft Authenticator.', + 'mfa_option_totp_desc' => 'A többlépcsős azonosításhoz olyan mobilalkalmazásra lesz szükséged, amely támogatja a TOTP-t, például a Google Authenticator, az Authy vagy a Microsoft Authenticator.', 'mfa_option_backup_codes_title' => 'Visszaállítási kulcsok', - 'mfa_option_backup_codes_desc' => 'Biztonságosan tárolja el az egyszer használatos visszaállítási kódjait, amiket a későbbiekben fel tud majd használni bejelentkezése során.', + 'mfa_option_backup_codes_desc' => 'Egyszer használatos biztonsági kódokat hoz létre, amelyeket bejelentkezéskor kell megadnia személyazonosságának igazolására. Ügyeljen arra, hogy ezeket biztonságos helyen tárolja.', 'mfa_gen_confirm_and_enable' => 'Jóváhagyás és engedélyezés', 'mfa_gen_backup_codes_title' => 'Visszaállítási kódok beállítása', 'mfa_gen_backup_codes_desc' => 'Tárolja el egy biztonságos helyen az alábbi kódokat. Bejelentkezés során fel tudja használni őket másodlagos bejelentkezési kódként.', 'mfa_gen_backup_codes_download' => 'Kódok letöltése', 'mfa_gen_backup_codes_usage_warning' => 'A kódok egyszerhasználatosak', 'mfa_gen_totp_title' => 'Mobilalkalmazás beállítása', - 'mfa_gen_totp_desc' => 'To use multi-factor authentication you\'ll need a mobile application that supports TOTP such as Google Authenticator, Authy or Microsoft Authenticator.', - 'mfa_gen_totp_scan' => 'Scan the QR code below using your preferred authentication app to get started.', - 'mfa_gen_totp_verify_setup' => 'Verify Setup', - 'mfa_gen_totp_verify_setup_desc' => 'Verify that all is working by entering a code, generated within your authentication app, in the input box below:', - 'mfa_gen_totp_provide_code_here' => 'Provide your app generated code here', - 'mfa_verify_access' => 'Verify Access', - 'mfa_verify_access_desc' => 'Your user account requires you to confirm your identity via an additional level of verification before you\'re granted access. Verify using one of your configured methods to continue.', - 'mfa_verify_no_methods' => 'No Methods Configured', - 'mfa_verify_no_methods_desc' => 'No multi-factor authentication methods could be found for your account. You\'ll need to set up at least one method before you gain access.', - 'mfa_verify_use_totp' => 'Verify using a mobile app', + 'mfa_gen_totp_desc' => 'A többlépcsős azonosításhoz olyan mobilalkalmazásra lesz szükséged, amely támogatja a TOTP-t, például a Google Authenticator, az Authy vagy a Microsoft Authenticator.', + 'mfa_gen_totp_scan' => 'Szkenneld be az alábbi QR-kódot az általad használt azonosító alkalmazásoddal, hogy használhasd az alkalmazást.', + 'mfa_gen_totp_verify_setup' => 'Beállítások ellenőrzése', + 'mfa_gen_totp_verify_setup_desc' => 'Ellenőrizd, hogy minden működik, azzal hogy beírod a kapott kódot amit az authentikátor alkalmazás generált az alábbi beviteli mezőbe:', + 'mfa_gen_totp_provide_code_here' => 'Add meg az alkalmazás által generált kódot ide', + 'mfa_verify_access' => 'Hozzáférés ellenőrzése', + 'mfa_verify_access_desc' => 'Felhasználói fiókja megköveteli, hogy erősítse meg személyazonosságát egy további ellenőrzési szinttel, mielőtt hozzáférést kapna. A folytatáshoz használja az egyik konfigurált módszert.', + 'mfa_verify_no_methods' => 'Nincs konfigurálva MFA', + 'mfa_verify_no_methods_desc' => 'Nem található többlépcsős hitelesítési módszer a fiókjához. A hozzáféréshez legalább egy módszert be kell állítania.', + 'mfa_verify_use_totp' => 'Ellenőrzés mobil alkalmazás használatával', 'mfa_verify_use_backup_codes' => 'Ellenőrzés visszaállítási kóddal', 'mfa_verify_backup_code' => 'Visszaállítási kód', 'mfa_verify_backup_code_desc' => 'Adjon meg egy még fel nem használt visszaállítási kódot:', 'mfa_verify_backup_code_enter_here' => 'Írd be a tartalék kódot', - 'mfa_verify_totp_desc' => 'Enter the code, generated using your mobile app, below:', - 'mfa_setup_login_notification' => 'Multi-factor method configured, Please now login again using the configured method.', + 'mfa_verify_totp_desc' => 'Írja be alább a mobilalkalmazásával generált kódot:', + 'mfa_setup_login_notification' => 'Többfaktoros hitelesítés konfigurálva. Kérjük, most jelentkezzen be újra a konfigurált módszerrel.', ]; diff --git a/lang/hu/common.php b/lang/hu/common.php index 295fa959b45..e65e6890921 100644 --- a/lang/hu/common.php +++ b/lang/hu/common.php @@ -20,7 +20,7 @@ 'description' => 'Leírás', 'role' => 'Szerepkör', 'cover_image' => 'Borítókép', - 'cover_image_description' => 'A kép méretének kb. 440x250px-nek kell lennie.', + 'cover_image_description' => 'Ennek a képnek körülbelül 440 x 250 képpont méretűnek kell lennie, bár rugalmasan méretezhető és levágható, hogy a felhasználói felülethez illeszkedjen a különböző lehetőségek esetén, így a megjelenítés tényleges méretei eltérőek lesznek.', // Actions 'actions' => 'Műveletek', @@ -30,6 +30,8 @@ 'create' => 'Létrehozás', 'update' => 'Frissítés', 'edit' => 'Szerkesztés', + 'archive' => 'Archive', + 'unarchive' => 'Un-Archive', 'sort' => 'Rendezés', 'move' => 'Áthelyezés', 'copy' => 'Másolás', @@ -42,7 +44,7 @@ 'remove' => 'Eltávolítás', 'add' => 'Hozzáadás', 'configure' => 'Beállítás', - 'manage' => 'Manage', + 'manage' => 'Kezelés', 'fullscreen' => 'Teljes képernyő', 'favourite' => 'Kedvencekhez ad', 'unfavourite' => 'Kedvencekből eltávolít', @@ -85,7 +87,7 @@ // Header 'homepage' => 'Kezdőlap', - 'header_menu_expand' => 'Expand Header Menu', + 'header_menu_expand' => 'Menü megnyitása', 'profile_menu' => 'Profil menü', 'view_profile' => 'Profil megtekintése', 'edit_profile' => 'Profil szerkesztése', @@ -95,9 +97,9 @@ // Layout tabs 'tab_info' => 'Információ', - 'tab_info_label' => 'Tab: Show Secondary Information', + 'tab_info_label' => 'Tab: Másodlagos információk megjelenítése', 'tab_content' => 'Tartalom', - 'tab_content_label' => 'Tab: Show Primary Content', + 'tab_content_label' => 'Tab: Elsődleges információk megjelenítése', // Email Content 'email_action_help' => 'Probléma esetén a lenti ":actionText" gombra kell kattintani, majd ki kell másolni a lenti webcímet és be kell illeszteni egy böngészőbe:', @@ -107,4 +109,7 @@ // Not directly used but available for convenience to users. 'privacy_policy' => 'Adatvédelmi irányelvek', 'terms_of_service' => 'Felhasználási feltételek', + + // OpenSearch + 'opensearch_description' => 'Search :appName', ]; diff --git a/lang/hu/components.php b/lang/hu/components.php index 26cd89010db..6a54dd1b034 100644 --- a/lang/hu/components.php +++ b/lang/hu/components.php @@ -6,36 +6,36 @@ // Image Manager 'image_select' => 'Kép kiválasztása', - 'image_list' => 'Image List', - 'image_details' => 'Image Details', - 'image_upload' => 'Upload Image', - 'image_intro' => 'Here you can select and manage images that have been previously uploaded to the system.', - 'image_intro_upload' => 'Upload a new image by dragging an image file into this window, or by using the "Upload Image" button above.', + 'image_list' => 'Képek listája', + 'image_details' => 'A kép részletei', + 'image_upload' => 'Kép feltöltése', + 'image_intro' => 'Itt választhatsz ki és kezelhetsz olyan képeket, amelyeket korábban feltöltöttek a rendszerbe.', + 'image_intro_upload' => 'Húzz ide egy új képfájlt az új kép feltöltéséhez, vagy használd a fenti "Kép feltöltése" gombot.', 'image_all' => 'Összes', 'image_all_title' => 'Összes kép megtekintése', - 'image_book_title' => 'A könyv feltöltött képek megtekintése', + 'image_book_title' => 'A könyvhöz feltöltött képek megtekintése', 'image_page_title' => 'Az oldalra feltöltött képek megtekintése', 'image_search_hint' => 'Keresés kép neve alapján', 'image_uploaded' => 'Feltöltve ekkor: :uploadedDate', - 'image_uploaded_by' => 'Uploaded by :userName', - 'image_uploaded_to' => 'Uploaded to :pageLink', - 'image_updated' => 'Updated :updateDate', + 'image_uploaded_by' => 'Feltöltötte :userName', + 'image_uploaded_to' => 'Feltöltve ide: :pageLink', + 'image_updated' => 'Frissítve ekkor: :updateDate', 'image_load_more' => 'Több betöltése', 'image_image_name' => 'Kép neve', 'image_delete_used' => 'Ez a kép a lenti oldalakon van használatban.', 'image_delete_confirm_text' => 'Biztosan törölhető ez a kép?', 'image_select_image' => 'Kép kiválasztása', 'image_dropzone' => 'Képek feltöltése ejtéssel vagy kattintással', - 'image_dropzone_drop' => 'Drop images here to upload', + 'image_dropzone_drop' => 'Húzza a képeket ide a feltöltéshez', 'images_deleted' => 'Képek törölve', 'image_preview' => 'Kép előnézete', 'image_upload_success' => 'Kép sikeresen feltöltve', 'image_update_success' => 'Kép részletei sikeresen frissítve', 'image_delete_success' => 'Kép sikeresen törölve', - 'image_replace' => 'Replace Image', - 'image_replace_success' => 'Image file successfully updated', - 'image_rebuild_thumbs' => 'Regenerate Size Variations', - 'image_rebuild_thumbs_success' => 'Image size variations successfully rebuilt!', + 'image_replace' => 'Kép cseréje', + 'image_replace_success' => 'Képfájl sikeresen frissítve', + 'image_rebuild_thumbs' => 'Méret variációk újragenerálása', + 'image_rebuild_thumbs_success' => 'Kép méret változatok sikeresen újra lettek generálva!', // Code Editor 'code_editor' => 'Kód szerkesztése', diff --git a/lang/hu/editor.php b/lang/hu/editor.php index 7c0da37bf7f..705bc6d54c0 100644 --- a/lang/hu/editor.php +++ b/lang/hu/editor.php @@ -13,6 +13,7 @@ 'cancel' => 'Mégsem', 'save' => 'Mentés', 'close' => 'Bezárás', + 'apply' => 'Apply', 'undo' => 'Visszavonás', 'redo' => 'Újra', 'left' => 'Balra', @@ -35,10 +36,10 @@ 'paragraph' => 'Bekezdés', 'blockquote' => 'Idézet', 'inline_code' => 'Forráskód', - 'callouts' => 'Ábrafelirat', + 'callouts' => 'Szövegdobozok', 'callout_information' => 'Információ', - 'callout_success' => 'Sikeres', - 'callout_warning' => 'Figyelem', + 'callout_success' => 'Siker', + 'callout_warning' => 'Figyelmeztetés', 'callout_danger' => 'Veszély', 'bold' => 'Félkövér', 'italic' => 'Dőlt', @@ -47,16 +48,17 @@ 'superscript' => 'Felső index', 'subscript' => 'Alsó index', 'text_color' => 'Szöveg szín', + 'highlight_color' => 'Highlight color', 'custom_color' => 'Egyéni szín', 'remove_color' => 'Szín eltávolítása', 'background_color' => 'Háttérszín', 'align_left' => 'Balra igazítás', 'align_center' => 'Középre igazítás', 'align_right' => 'Jobbra igazítás', - 'align_justify' => 'Justify', + 'align_justify' => 'Sorkizárt', 'list_bullet' => 'Felsorolásjeles lista', 'list_numbered' => 'Számozott lista', - 'list_task' => 'Task list', + 'list_task' => 'Feladatlista', 'indent_increase' => 'Behúzás növelése', 'indent_decrease' => 'Behúzás csökkentése', 'table' => 'Táblázat', @@ -81,6 +83,9 @@ 'table_properties' => 'Táblázat tulajdonságai', 'table_properties_title' => 'Táblázat Tulajdonságai', 'delete_table' => 'Táblázat törlése', + 'table_clear_formatting' => 'Tábla formázás törlése', + 'resize_to_contents' => 'Átméretezés a tartalomhoz', + 'row_header' => 'Sorfejléc', 'insert_row_before' => 'Sor beszúrása elé', 'insert_row_after' => 'Sor beszúrása mögé', 'delete_row' => 'Sor törlése', @@ -94,10 +99,10 @@ 'cell_properties_title' => 'Cella Tulajdonságai', 'cell_type' => 'Cella típusa', 'cell_type_cell' => 'Cella', - 'cell_scope' => 'Scope', + 'cell_scope' => 'Hatáskör', 'cell_type_header' => 'Címsor cella', - 'merge_cells' => 'Merge cells', - 'split_cell' => 'Split cell', + 'merge_cells' => 'Cellák egyesítése', + 'split_cell' => 'Cellák szétválasztása', 'table_row_group' => 'Sorcsoport', 'table_column_group' => 'Oszlopcsoport', 'horizontal_align' => 'Vízszintes elrendezés', @@ -120,54 +125,57 @@ 'copy_column' => 'Oszlop másolása', 'paste_column_before' => 'Oszlop beszúrása elé', 'paste_column_after' => 'Oszlop beszúrása utána', - 'cell_padding' => 'Cell padding', - 'cell_spacing' => 'Cell spacing', + 'cell_padding' => 'Cellatávolság', + 'cell_spacing' => 'Cellatávolság', 'caption' => 'Felirat', 'show_caption' => 'Képaláírás mutatása', 'constrain' => 'Arányok megőrzése', - 'cell_border_solid' => 'Solid', - 'cell_border_dotted' => 'Dotted', - 'cell_border_dashed' => 'Dashed', - 'cell_border_double' => 'Double', - 'cell_border_groove' => 'Groove', - 'cell_border_ridge' => 'Ridge', - 'cell_border_inset' => 'Inset', - 'cell_border_outset' => 'Outset', - 'cell_border_none' => 'None', - 'cell_border_hidden' => 'Hidden', + 'cell_border_solid' => 'Folyamatos', + 'cell_border_dotted' => 'Pontozott', + 'cell_border_dashed' => 'Szaggatott', + 'cell_border_double' => 'Dupla', + 'cell_border_groove' => 'Horony', + 'cell_border_ridge' => 'Domború', + 'cell_border_inset' => 'Behúzott', + 'cell_border_outset' => 'Kiemelés', + 'cell_border_none' => 'Egyik sem', + 'cell_border_hidden' => 'Rejtett', // Images, links, details/summary & embed 'source' => 'Forrás', 'alt_desc' => 'Alternatív leírás', 'embed' => 'Beágyazás', - 'paste_embed' => 'Paste your embed code below:', + 'paste_embed' => 'Illeszd be a beágyazási kódot ide:', 'url' => 'URL', 'text_to_display' => 'Megjelenő szöveg', 'title' => 'Cím', - 'open_link' => 'Open link', - 'open_link_in' => 'Open link in...', + 'browse_links' => 'Browse links', + 'open_link' => 'Hivatkozás megnyitása', + 'open_link_in' => 'Hivatkozás megnyitása...', 'open_link_current' => 'Aktuális ablak', 'open_link_new' => 'Új ablak', - 'remove_link' => 'Remove link', - 'insert_collapsible' => 'Insert collapsible block', - 'collapsible_unwrap' => 'Unwrap', + 'remove_link' => 'Hivatkozás eltávolítása', + 'insert_collapsible' => 'Illeszd be az összecsukható blokkot', + 'collapsible_unwrap' => 'Kicsomagol', 'edit_label' => 'Címke szerkesztése', 'toggle_open_closed' => 'Nyitott/zárt váltása', - 'collapsible_edit' => 'Edit collapsible block', - 'toggle_label' => 'Toggle label', + 'collapsible_edit' => 'Összecsukható blokk szerkesztése', + 'toggle_label' => 'Címke ki-be kapcsolása', // About view - 'about' => 'About the editor', - 'about_title' => 'About the WYSIWYG Editor', - 'editor_license' => 'Editor License & Copyright', - 'editor_tiny_license' => 'This editor is built using :tinyLink which is provided under the MIT license.', - 'editor_tiny_license_link' => 'The copyright and license details of TinyMCE can be found here.', + 'about' => 'A szerkesztőről', + 'about_title' => 'A WYSIWYG szerkesztőről', + 'editor_license' => 'Szerkesztő Licensz és Copyright információi', + 'editor_lexical_license' => 'This editor is built as a fork of :lexicalLink which is distributed under the MIT license.', + 'editor_lexical_license_link' => 'Full license details can be found here.', + 'editor_tiny_license' => 'Ez a szerkesztő az MIT licenc alatt szolgáltatott :tinyLink segítségével készült.', + 'editor_tiny_license_link' => 'A TinyMCE szerzői jogi és licencinformációi itt találhatók.', 'save_continue' => 'Mentés és Folytatás', - 'callouts_cycle' => '(Keep pressing to toggle through types)', - 'link_selector' => 'Link to content', + 'callouts_cycle' => '(Folyamatos lenyomva tartással válassza ki a típusok közötti váltást)', + 'link_selector' => 'Tartalom hivatkozása', 'shortcuts' => 'Gyorsbillentyűk', 'shortcut' => 'Gyorsbillentyű', - 'shortcuts_intro' => 'The following shortcuts are available in the editor:', + 'shortcuts_intro' => 'Az alábbi gyorsbillentyűk érhetők el a szerkesztőben:', 'windows_linux' => '(Windows/Linux)', 'mac' => '(Mac)', 'description' => 'Leírás', diff --git a/lang/hu/entities.php b/lang/hu/entities.php index 339a8d1000e..570120dfa99 100644 --- a/lang/hu/entities.php +++ b/lang/hu/entities.php @@ -23,9 +23,9 @@ 'meta_updated' => 'Frissítve :timeLength', 'meta_updated_name' => ':user frissítette :timeLength', 'meta_owned_name' => ':user tulajdona', - 'meta_reference_count' => 'Referenced by :count item|Referenced by :count items', + 'meta_reference_count' => 'Hivatkozva a következő által: :count |Hivatkozva a következő által: :count', 'entity_select' => 'Entitás kiválasztása', - 'entity_select_lack_permission' => 'You don\'t have the required permissions to select this item', + 'entity_select_lack_permission' => 'Nincs meg a szükséges jogosultságod ennek az elemnek a kiválasztásához', 'images' => 'Képek', 'my_recent_drafts' => 'Legutóbbi vázlataim', 'my_recently_viewed' => 'Általam legutóbb megtekintett', @@ -35,22 +35,50 @@ 'no_pages_recently_created' => 'Nincsenek legutóbb létrehozott oldalak', 'no_pages_recently_updated' => 'Nincsenek legutóbb frissített oldalak', 'export' => 'Exportálás', - 'export_html' => 'Webfájlt tartalmaz', + 'export_html' => 'Önálló weblap', 'export_pdf' => 'PDF fájl', 'export_text' => 'Egyszerű szövegfájl', 'export_md' => 'Markdown jegyzetek', + 'export_zip' => 'Hordozható ZIP', + 'default_template' => 'Alapértelmezett oldalsablon', + 'default_template_explain' => 'Rendeljen hozzá egy oldalsablont, amely alapértelmezett tartalomként lesz használva az ezen az elemen belül létrehozott összes oldalon. Ne feledje, hogy ezt csak akkor használja, ha az oldal készítője megtekintési hozzáféréssel rendelkezik a kiválasztott sablonoldalhoz.', + 'default_template_select' => 'Válasszon ki egy oldalsablont', + 'import' => 'Import', + 'import_validate' => 'Validate Import', + 'import_desc' => 'Import books, chapters & pages using a portable zip export from the same, or a different, instance. Select a ZIP file to proceed. After the file has been uploaded and validated you\'ll be able to configure & confirm the import in the next view.', + '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_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_size' => ':size Import ZIP Size', + 'import_uploaded_at' => 'Uploaded :relativeTime', + 'import_uploaded_by' => 'Uploaded by', + 'import_location' => 'Import Location', + '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_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', + 'breadcrumb_siblings_for_book' => 'Navigate siblings for book', + 'breadcrumb_siblings_for_bookshelf' => 'Navigate siblings for shelf', // Permissions and restrictions 'permissions' => 'Jogosultságok', - 'permissions_desc' => 'Set permissions here to override the default permissions provided by user roles.', - 'permissions_book_cascade' => 'Permissions set on books will automatically cascade to child chapters and pages, unless they have their own permissions defined.', - 'permissions_chapter_cascade' => 'Permissions set on chapters will automatically cascade to child pages, unless they have their own permissions defined.', + 'permissions_desc' => 'Itt állítsa be az engedélyeket a felhasználói szerepkörök által biztosított alapértelmezett engedélyek felülbírálásához.', + 'permissions_book_cascade' => 'A könyvekre beállított engedélyek automatikusan az alárendelt fejezetekhez és oldalakhoz kapcsolódnak, kivéve, ha saját engedélyekkel rendelkeznek.', + 'permissions_chapter_cascade' => 'A fejezetekre beállított engedélyek automatikusan az alárendelt oldalakra lépnek át, hacsak nem rendelkeznek saját engedélyekkel.', 'permissions_save' => 'Jogosultságok mentése', 'permissions_owner' => 'Tulajdonos', - 'permissions_role_everyone_else' => 'Everyone Else', - 'permissions_role_everyone_else_desc' => 'Set permissions for all roles not specifically overridden.', - 'permissions_role_override' => 'Override permissions for role', - 'permissions_inherit_defaults' => 'Inherit defaults', + 'permissions_role_everyone_else' => 'Mindenki más', + 'permissions_role_everyone_else_desc' => 'Állítson be engedélyeket az összes, kifejezetten nem felülírt szerepkörhöz.', + 'permissions_role_override' => 'A szerepkör engedélyeinek felülbírálása', + 'permissions_inherit_defaults' => 'Alapértelmezett értékek öröklése', // Search 'search_results' => 'Keresési eredmények', @@ -93,24 +121,24 @@ 'shelves_save' => 'Polc mentése', 'shelves_books' => 'Könyvek ezen a polcon', 'shelves_add_books' => 'Könyvek hozzáadása ehhez a polchoz', - 'shelves_drag_books' => 'Drag books below to add them to this shelf', + 'shelves_drag_books' => 'Könyveket áthúzással lehet elhelyezni ezen a polcon', 'shelves_empty_contents' => 'Ehhez a polchoz nincsenek könyvek rendelve', 'shelves_edit_and_assign' => 'Polc szerkesztése könyvek hozzárendeléséhez', - 'shelves_edit_named' => 'Edit Shelf :name', - 'shelves_edit' => 'Edit Shelf', - 'shelves_delete' => 'Delete Shelf', - 'shelves_delete_named' => 'Delete Shelf :name', - 'shelves_delete_explain' => "This will delete the shelf with the name ':name'. Contained books will not be deleted.", - 'shelves_delete_confirmation' => 'Are you sure you want to delete this shelf?', - 'shelves_permissions' => 'Shelf Permissions', - 'shelves_permissions_updated' => 'Shelf Permissions Updated', - 'shelves_permissions_active' => 'Shelf Permissions Active', - 'shelves_permissions_cascade_warning' => 'Permissions on shelves do not automatically cascade to contained books. This is because a book can exist on multiple shelves. Permissions can however be copied down to child books using the option found below.', - 'shelves_permissions_create' => 'Shelf create permissions are only used for copying permissions to child books using the action below. They do not control the ability to create books.', + 'shelves_edit_named' => ':name polc szerkesztése', + 'shelves_edit' => 'Polc szerkesztése', + 'shelves_delete' => 'Polc törlése', + 'shelves_delete_named' => ':name polc törlése', + 'shelves_delete_explain' => "':name'. nevű polc ezzel le lesz törölve. A benne található könyvek nem lesznek törölve.", + 'shelves_delete_confirmation' => 'Biztosan törölhető ez a polc?', + 'shelves_permissions' => 'Polc jogosultság', + 'shelves_permissions_updated' => 'Polc jogosultságok frissítve', + 'shelves_permissions_active' => 'Polc jogosultságok aktívak', + 'shelves_permissions_cascade_warning' => 'A polcokhoz kapcsolódó jogosultságok nem kapcsolódnak automatikusan a tárolt könyvekhez. Ennek az az oka, hogy egy könyv több polcon is létezhet. Az engedélyek azonban lemásolhatók a gyermekkönyvekbe az alábbi lehetőség segítségével.', + 'shelves_permissions_create' => 'A polclétrehozási jogosultságok csak az alárendelt könyvekbe való másoláshoz használhatók az alábbi művelettel. Nem szabályozzák a könyvek létrehozásának lehetőségét.', 'shelves_copy_permissions_to_books' => 'Jogosultság másolása könyvekre', 'shelves_copy_permissions' => 'Jogosultság másolása', - 'shelves_copy_permissions_explain' => 'This will apply the current permission settings of this shelf to all books contained within. Before activating, ensure any changes to the permissions of this shelf have been saved.', - 'shelves_copy_permission_success' => 'Shelf permissions copied to :count books', + 'shelves_copy_permissions_explain' => 'Ezzel a polc jelenlegi engedélybeállításait alkalmazza a benne található összes könyvre. Az aktiválás előtt győződjön meg arról, hogy a polc engedélyeinek módosításait elmentette.', + 'shelves_copy_permission_success' => 'Könyvespolc jogosultságok átmásolva :count könyvre', // Books 'book' => 'Könyv', @@ -132,9 +160,6 @@ 'books_edit_named' => ':bookName könyv szerkesztése', 'books_form_book_name' => 'Könyv neve', 'books_save' => 'Könyv mentése', - 'books_default_template' => 'Default Page Template', - 'books_default_template_explain' => 'Assign a page template that will be used as the default content for all new pages in this book. Keep in mind this will only be used if the page creator has view access to those chosen template page.', - 'books_default_template_select' => 'Select a template page', 'books_permissions' => 'Könyv jogosultságok', 'books_permissions_updated' => 'Könyv jogosultságok frissítve', 'books_empty_contents' => 'Ehhez a könyvhöz nincsenek oldalak vagy fejezetek létrehozva.', @@ -145,7 +170,9 @@ 'books_search_this' => 'Keresés ebben a könyvben', 'books_navigation' => 'Könyv navigáció', 'books_sort' => 'Könyv tartalmak rendezése', - 'books_sort_desc' => 'Move chapters and pages within a book to reorganise its contents. Other books can be added which allows easy moving of chapters and pages between books.', + 'books_sort_desc' => 'Move chapters and pages within a book to reorganise its contents. Other books can be added which allows easy moving of chapters and pages between books. Optionally an auto sort rule can be set to automatically sort this book\'s contents upon changes.', + 'books_sort_auto_sort' => 'Auto Sort Option', + 'books_sort_auto_sort_active' => 'Auto Sort Active: :sortName', 'books_sort_named' => ':bookName könyv rendezése', 'books_sort_name' => 'Rendezés név szerint', 'books_sort_created' => 'Rendezés létrehozás dátuma szerint', @@ -154,17 +181,17 @@ 'books_sort_chapters_last' => 'Fejezetek hátul', 'books_sort_show_other' => 'Egyéb könyvek mutatása', 'books_sort_save' => 'Új elrendezés mentése', - 'books_sort_show_other_desc' => 'Add other books here to include them in the sort operation, and allow easy cross-book reorganisation.', - 'books_sort_move_up' => 'Move Up', - 'books_sort_move_down' => 'Move Down', - 'books_sort_move_prev_book' => 'Move to Previous Book', - 'books_sort_move_next_book' => 'Move to Next Book', - 'books_sort_move_prev_chapter' => 'Move Into Previous Chapter', - 'books_sort_move_next_chapter' => 'Move Into Next Chapter', - 'books_sort_move_book_start' => 'Move to Start of Book', - 'books_sort_move_book_end' => 'Move to End of Book', - 'books_sort_move_before_chapter' => 'Move to Before Chapter', - 'books_sort_move_after_chapter' => 'Move to After Chapter', + 'books_sort_show_other_desc' => 'Adjon hozzá más könyveket, hogy bevonja őket a rendezési műveletbe, és lehetővé tegye a könyvek közötti egyszerű átszervezést.', + 'books_sort_move_up' => 'Mozgatás fel', + 'books_sort_move_down' => 'Mozgatás le', + 'books_sort_move_prev_book' => 'Mozgatás az előző könyvbe', + 'books_sort_move_next_book' => 'Mozgatás a következő könyvbe', + 'books_sort_move_prev_chapter' => 'Mozgatás az előző fejezetbe', + 'books_sort_move_next_chapter' => 'Mozgatás a következő fejezetbe', + 'books_sort_move_book_start' => 'Mozgatás a könyv elejére', + 'books_sort_move_book_end' => 'Mozgatás a könyv végére', + 'books_sort_move_before_chapter' => 'Morgazás a fejezet elé', + 'books_sort_move_after_chapter' => 'Mozgatás a fejezet után', 'books_copy' => 'Könyv másolása', 'books_copy_success' => 'Könyv sikeresen lemásolva', @@ -191,7 +218,7 @@ 'chapters_permissions_active' => 'Fejezet jogosultságok aktívak', 'chapters_permissions_success' => 'Fejezet jogosultságok frissítve', 'chapters_search_this' => 'Keresés ebben a fejezetben', - 'chapter_sort_book' => 'Sort Book', + 'chapter_sort_book' => 'Könyv rendezése', // Pages 'page' => 'Oldal', @@ -207,7 +234,7 @@ 'pages_delete_draft' => 'Vázlat oldal törlése', 'pages_delete_success' => 'Oldal törölve', 'pages_delete_draft_success' => 'Vázlat oldal törölve', - 'pages_delete_warning_template' => 'This page is in active use as a book default page template. These books will no longer have a default page template assigned after this page is deleted.', + 'pages_delete_warning_template' => 'Ez az oldal aktívan használatban van könyv vagy fejezet alapértelmezett oldalsablonjaként. Ezekhez a könyvekhez vagy fejezetekhez a továbbiakban nem lesz alapértelmezett oldalsablon hozzárendelve az oldal törlése után.', 'pages_delete_confirm' => 'Biztosan törölhető ez az oldal?', 'pages_delete_draft_confirm' => 'Biztosan törölhető ez a vázlatoldal?', 'pages_editing_named' => ':pageName oldal szerkesztése', @@ -218,21 +245,23 @@ 'pages_editing_page' => 'Oldal szerkesztése', 'pages_edit_draft_save_at' => 'Vázlat elmentve:', 'pages_edit_delete_draft' => 'Vázlat törlése', - 'pages_edit_delete_draft_confirm' => 'Are you sure you want to delete your draft page changes? All of your changes, since the last full save, will be lost and the editor will be updated with the latest page non-draft save state.', + 'pages_edit_delete_draft_confirm' => 'Biztos benne, hogy törölni kívánja az oldalmódosítások piszkozatát? Az utolsó teljes mentés óta végrehajtott összes módosítása elvész, és a szerkesztő frissül a legfrissebb, nem vázlatos mentési állapottal.', 'pages_edit_discard_draft' => 'Vázlat elvetése', - 'pages_edit_switch_to_markdown' => 'Switch to Markdown Editor', - 'pages_edit_switch_to_markdown_clean' => '(Clean Content)', - 'pages_edit_switch_to_markdown_stable' => '(Stable Content)', - 'pages_edit_switch_to_wysiwyg' => 'Switch to WYSIWYG Editor', + 'pages_edit_switch_to_markdown' => 'Váltás Markdown szerkesztőre', + 'pages_edit_switch_to_markdown_clean' => '(Tisztított tartalom)', + 'pages_edit_switch_to_markdown_stable' => '(Stabil tartalom)', + 'pages_edit_switch_to_wysiwyg' => 'Váltás a WYSIWYG szerkesztőre', + 'pages_edit_switch_to_new_wysiwyg' => 'Switch to new WYSIWYG', + 'pages_edit_switch_to_new_wysiwyg_desc' => '(In Beta Testing)', 'pages_edit_set_changelog' => 'Változásnapló beállítása', 'pages_edit_enter_changelog_desc' => 'A végrehajtott módosítások rövid leírása', 'pages_edit_enter_changelog' => 'Változásnapló megadása', - 'pages_editor_switch_title' => 'Switch Editor', - 'pages_editor_switch_are_you_sure' => 'Are you sure you want to change the editor for this page?', - 'pages_editor_switch_consider_following' => 'Consider the following when changing editors:', - 'pages_editor_switch_consideration_a' => 'Once saved, the new editor option will be used by any future editors, including those that may not be able to change editor type themselves.', - 'pages_editor_switch_consideration_b' => 'This can potentially lead to a loss of detail and syntax in certain circumstances.', - 'pages_editor_switch_consideration_c' => 'Tag or changelog changes, made since last save, won\'t persist across this change.', + 'pages_editor_switch_title' => 'Szerkesztőváltás', + 'pages_editor_switch_are_you_sure' => 'Biztosan módosítani szeretné ennek az oldalnak a szerkesztőjét?', + 'pages_editor_switch_consider_following' => 'A szerkesztők módosításakor vegye figyelembe a következőket:', + 'pages_editor_switch_consideration_a' => 'Mentés után az új szerkesztő opciót minden jövőbeli szerkesztő használni fogja, beleértve azokat is, amelyek esetleg nem tudják maguk módosítani a szerkesztő típusát.', + 'pages_editor_switch_consideration_b' => 'Ez bizonyos körülmények között a részletek és a szintaxis elvesztéséhez vezethet.', + 'pages_editor_switch_consideration_c' => 'A legutóbbi mentés óta végrehajtott címke- vagy változásnapló-módosítások nem maradnak fenn a módosítás során.', 'pages_save' => 'Oldal mentése', 'pages_title' => 'Oldal címe', 'pages_name' => 'Oldal neve', @@ -241,10 +270,11 @@ 'pages_md_insert_image' => 'Kép beillesztése', 'pages_md_insert_link' => 'Entitás hivatkozás beillesztése', 'pages_md_insert_drawing' => 'Rajz beillesztése', - 'pages_md_show_preview' => 'Show preview', - 'pages_md_sync_scroll' => 'Sync preview scroll', - 'pages_drawing_unsaved' => 'Unsaved Drawing Found', - 'pages_drawing_unsaved_confirm' => 'Unsaved drawing data was found from a previous failed drawing save attempt. Would you like to restore and continue editing this unsaved drawing?', + 'pages_md_show_preview' => 'Előnézet megjelenítése', + 'pages_md_sync_scroll' => 'Előnézet pozíció szinkronizálása', + 'pages_md_plain_editor' => 'Plaintext editor', + 'pages_drawing_unsaved' => 'Nem mentett rajz található', + 'pages_drawing_unsaved_confirm' => 'A rendszer nem mentett rajzadatokat talált egy korábbi sikertelen rajzmentési kísérletből. Szeretné visszaállítani és folytatni a nem mentett rajz szerkesztését?', 'pages_not_in_chapter' => 'Az oldal nincs fejezetben', 'pages_move' => 'Oldal áthelyezése', 'pages_copy' => 'Oldal másolása', @@ -254,17 +284,17 @@ 'pages_permissions_success' => 'Oldal jogosultságok frissítve', 'pages_revision' => 'Változat', 'pages_revisions' => 'Oldal változatai', - 'pages_revisions_desc' => 'Listed below are all the past revisions of this page. You can look back upon, compare, and restore old page versions if permissions allow. The full history of the page may not be fully reflected here since, depending on system configuration, old revisions could be auto-deleted.', + 'pages_revisions_desc' => 'Az alábbiakban az oldal összes korábbi verziója látható. Visszatekinthet, összehasonlíthatja és visszaállíthatja a régi oldalverziókat, ha az engedélyek lehetővé teszik. Előfordulhat, hogy az oldal teljes előzménye itt nem jelenik meg teljes mértékben, mivel a rendszerkonfigurációtól függően a régi változatok automatikusan törlődnek.', 'pages_revisions_named' => ':pageName oldal változatai', 'pages_revision_named' => ':pageName oldal változata', - 'pages_revision_restored_from' => 'Restored from #:id; :summary', + 'pages_revision_restored_from' => 'Visszaállítva innen: #:id; :summary', 'pages_revisions_created_by' => 'Létrehozta:', 'pages_revisions_date' => 'Változat dátuma', 'pages_revisions_number' => '#', - 'pages_revisions_sort_number' => 'Revision Number', + 'pages_revisions_sort_number' => 'Változat száma', 'pages_revisions_numbered' => 'Változat #:id', 'pages_revisions_numbered_changes' => '#:id változat módosításai', - 'pages_revisions_editor' => 'Editor Type', + 'pages_revisions_editor' => 'Szerkesztő típusa', 'pages_revisions_changelog' => 'Változásnapló', 'pages_revisions_changes' => 'Módosítások', 'pages_revisions_current' => 'Aktuális verzió', @@ -272,20 +302,20 @@ 'pages_revisions_restore' => 'Visszaállítás', 'pages_revisions_none' => 'Ennek az oldalnak nincsenek változatai', 'pages_copy_link' => 'Hivatkozás másolása', - 'pages_edit_content_link' => 'Jump to section in editor', - 'pages_pointer_enter_mode' => 'Enter section select mode', - 'pages_pointer_label' => 'Page Section Options', - 'pages_pointer_permalink' => 'Page Section Permalink', - 'pages_pointer_include_tag' => 'Page Section Include Tag', - 'pages_pointer_toggle_link' => 'Permalink mode, Press to show include tag', - 'pages_pointer_toggle_include' => 'Include tag mode, Press to show permalink', + 'pages_edit_content_link' => 'Ugrás a szakaszhoz a szerkesztőben', + 'pages_pointer_enter_mode' => 'Lépjen be a szakaszválasztó módba', + 'pages_pointer_label' => 'Oldalszakasz beállításai', + 'pages_pointer_permalink' => 'Oldalszakasz állandó hivatkozás', + 'pages_pointer_include_tag' => 'Oldalszakasz tartalmazza a címkét', + 'pages_pointer_toggle_link' => 'Permalink mód, Nyomja meg az include tag megjelenítéséhez', + 'pages_pointer_toggle_include' => 'Include tag mód, Nyomja meg az permalink megjelenítéséhez', 'pages_permissions_active' => 'Oldal jogosultságok aktívak', 'pages_initial_revision' => 'Kezdeti közzététel', - 'pages_references_update_revision' => 'System auto-update of internal links', + 'pages_references_update_revision' => 'A belső hivatkozások automatikus frissítése', 'pages_initial_name' => 'Új oldal', 'pages_editing_draft_notification' => 'A jelenleg szerkesztett vázlat legutóbb ekkor volt elmentve: :timeDiff.', 'pages_draft_edited_notification' => 'Ezt az oldalt azóta már frissítették. Javasolt ennek a vázlatnak az elvetése.', - 'pages_draft_page_changed_since_creation' => 'This page has been updated since this draft was created. It is recommended that you discard this draft or take care not to overwrite any page changes.', + 'pages_draft_page_changed_since_creation' => 'Ez az oldal a vázlat létrehozása óta frissült. Javasoljuk, hogy dobja el ezt a piszkozatot, vagy ügyeljen arra, hogy ne írja felül az oldal módosításait.', 'pages_draft_edit_active' => [ 'start_a' => ':count felhasználók kezdte el szerkeszteni ezt az oldalt', 'start_b' => ':userName elkezdte szerkeszteni ezt az oldalt', @@ -293,44 +323,44 @@ 'time_b' => 'az utolsó :minCount percben', 'message' => ':start :time. Ügyeljen arra, hogy ne írjuk felül egymás frissítéseit!', ], - 'pages_draft_discarded' => 'Draft discarded! The editor has been updated with the current page content', - 'pages_draft_deleted' => 'Draft deleted! The editor has been updated with the current page content', + 'pages_draft_discarded' => 'Vázlat elvetve! A szerkesztő frissítve lesz az oldal aktuális tartalmával', + 'pages_draft_deleted' => 'Vázlat elvetve! A szerkesztő frissítve lesz az oldal aktuális tartalmával', 'pages_specific' => 'Egy bizonyos oldal', 'pages_is_template' => 'Oldalsablon', // Editor Sidebar - 'toggle_sidebar' => 'Toggle Sidebar', + 'toggle_sidebar' => 'Oldalsáv ki/be', 'page_tags' => 'Oldal címkék', 'chapter_tags' => 'Fejezet címkék', 'book_tags' => 'Könyv címkék', 'shelf_tags' => 'Polc címkék', 'tag' => 'Címke', 'tags' => 'Címkék', - 'tags_index_desc' => 'Tags can be applied to content within the system to apply a flexible form of categorization. Tags can have both a key and value, with the value being optional. Once applied, content can then be queried using the tag name and value.', + 'tags_index_desc' => 'A címkék a rendszeren belüli tartalomra alkalmazhatók a kategorizálás rugalmas formája alkalmazása érdekében. A címkéknek kulcsuk és értékük is lehetnek, de az érték nem kötelező. Alkalmazása után a tartalom lekérdezhető a címkenév és érték használatával.', 'tag_name' => 'Címkenév', 'tag_value' => 'Címke érték (nem kötelező)', 'tags_explain' => "Címkék hozzáadása a tartalom jobb kategorizálásához.\nA mélyebb szervezettség megvalósításához hozzá lehet rendelni egy értéket a címkéhez.", 'tags_add' => 'Másik címke hozzáadása', 'tags_remove' => 'Címke eltávolítása', - 'tags_usages' => 'Total tag usages', + 'tags_usages' => 'Összes címkehasználat', 'tags_assigned_pages' => 'Oldalakhoz Rendelt', 'tags_assigned_chapters' => 'Fejezetekhez rendelt', 'tags_assigned_books' => 'Könyvekhez Rendelt', 'tags_assigned_shelves' => 'Polcokhoz Rendelt', 'tags_x_unique_values' => ':count egyedi érték', 'tags_all_values' => 'Összes érték', - 'tags_view_tags' => 'Címke megtekintése', - 'tags_view_existing_tags' => 'Címkék megtekintése', - 'tags_list_empty_hint' => 'Tags can be assigned via the page editor sidebar or while editing the details of a book, chapter or shelf.', + 'tags_view_tags' => 'Címkék megtekintése', + 'tags_view_existing_tags' => 'Létező címkék megtekintése', + 'tags_list_empty_hint' => 'A címkék hozzárendelhetők az oldalszerkesztő oldalsávján keresztül, vagy egy könyv, fejezet vagy polc adatainak szerkesztése közben.', 'attachments' => 'Csatolmányok', 'attachments_explain' => 'Az oldalon megjelenő fájlok feltöltése vagy hivatkozások csatolása. Az oldal oldalsávjában fognak megjelenni.', 'attachments_explain_instant_save' => 'Az itt történt módosítások azonnal el lesznek mentve.', 'attachments_upload' => 'Fájlfeltöltés', 'attachments_link' => 'Hivatkozás csatolása', - 'attachments_upload_drop' => 'Alternatively you can drag and drop a file here to upload it as an attachment.', + 'attachments_upload_drop' => 'Alternatív megoldásként a fájlt ide húzva is fel lehet tölteni mellékletként.', 'attachments_set_link' => 'Hivatkozás beállítása', 'attachments_delete' => 'Biztosan törölhető ez a melléklet?', - 'attachments_dropzone' => 'Drop files here to upload', + 'attachments_dropzone' => 'Húzza a file(oka)t ide a feltöltéshez', 'attachments_no_files' => 'Nincsenek fájlok feltöltve', 'attachments_explain_link' => 'Fájl feltöltése helyett hozzá lehet kapcsolni egy hivatkozást. Ez egy hivatkozás lesz egy másik oldalra vagy egy fájlra a felhőben.', 'attachments_link_name' => 'Hivatkozás neve', @@ -367,19 +397,28 @@ 'comment' => 'Megjegyzés', 'comments' => 'Megjegyzések', 'comment_add' => 'Megjegyzés hozzáadása', + 'comment_none' => 'No comments to display', 'comment_placeholder' => 'Megjegyzés írása', - 'comment_count' => '{0} Nincs megjegyzés|{1} 1 megjegyzés|[2,*] :count megjegyzés', + 'comment_thread_count' => ':count Comment Thread|:count Comment Threads', + 'comment_archived_count' => ':count Archived', + 'comment_archived_threads' => 'Archived Threads', 'comment_save' => 'Megjegyzés mentése', 'comment_new' => 'Új megjegyzés', 'comment_created' => 'megjegyzést fűzött hozzá :createDiff', 'comment_updated' => 'Frissítve :updateDiff :username által', - 'comment_updated_indicator' => 'Updated', + 'comment_updated_indicator' => 'Frissített', 'comment_deleted_success' => 'Megjegyzés törölve', 'comment_created_success' => 'Megjegyzés hozzáadva', 'comment_updated_success' => 'Megjegyzés frissítve', + 'comment_archive_success' => 'Comment archived', + 'comment_unarchive_success' => 'Comment un-archived', + 'comment_view' => 'View comment', + 'comment_jump_to_thread' => 'Jump to thread', 'comment_delete_confirm' => 'Biztosan törölhető ez a megjegyzés?', 'comment_in_reply_to' => 'Válasz erre: :commentId', - 'comment_editor_explain' => 'Here are the comments that have been left on this page. Comments can be added & managed when viewing the saved page.', + 'comment_reference' => 'Reference', + 'comment_reference_outdated' => '(Outdated)', + 'comment_editor_explain' => 'Itt vannak az ezen az oldalon lévő megjegyzések. Megjegyzések hozzáadhatók és kezelhetők a mentett oldal megtekintésekor.', // Revision 'revision_delete_confirm' => 'Biztosan törölhető ez a változat?', @@ -388,50 +427,50 @@ // Copy view 'copy_consider' => 'Kérem, fontolja meg az alábbiakat, amikor tartalmat kíván másolni.', - 'copy_consider_permissions' => 'Custom permission settings will not be copied.', + 'copy_consider_permissions' => 'Az egyéni engedélybeállítások nem kerülnek másolásra.', 'copy_consider_owner' => 'Minden lemásolt tartalomnak Ön lesz a tulajdonosa.', - 'copy_consider_images' => 'Page image files will not be duplicated & the original images will retain their relation to the page they were originally uploaded to.', - 'copy_consider_attachments' => 'Page attachments will not be copied.', + 'copy_consider_images' => 'Az oldalképfájlok nem duplikálódnak, és az eredeti képek megőrzik kapcsolatukat az eredetileg feltöltött oldallal.', + 'copy_consider_attachments' => 'Az oldal mellékletei nem kerülnek másolásra.', 'copy_consider_access' => 'A change of location, owner or permissions may result in this content being accessible to those previously without access.', // Conversions - 'convert_to_shelf' => 'Convert to Shelf', - 'convert_to_shelf_contents_desc' => 'You can convert this book to a new shelf with the same contents. Chapters contained within this book will be converted to new books. If this book contains any pages, that are not in a chapter, this book will be renamed and contain such pages, and this book will become part of the new shelf.', - 'convert_to_shelf_permissions_desc' => 'Any permissions set on this book will be copied to the new shelf and to all new child books that don\'t have their own permissions enforced. Note that permissions on shelves do not auto-cascade to content within, as they do for books.', - 'convert_book' => 'Convert Book', - 'convert_book_confirm' => 'Are you sure you want to convert this book?', - 'convert_undo_warning' => 'This cannot be as easily undone.', - 'convert_to_book' => 'Convert to Book', - 'convert_to_book_desc' => 'You can convert this chapter to a new book with the same contents. Any permissions set on this chapter will be copied to the new book but any inherited permissions, from the parent book, will not be copied which could lead to a change of access control.', - 'convert_chapter' => 'Convert Chapter', - 'convert_chapter_confirm' => 'Are you sure you want to convert this chapter?', + 'convert_to_shelf' => 'Átalakítás polccá', + 'convert_to_shelf_contents_desc' => 'Ezt a könyvet új polccá alakíthatja, azonos tartalommal. A könyvben található fejezetek új könyvekké lesznek átalakítva. Ha ez a könyv tartalmaz olyan oldalakat, amelyek nem szerepelnek egy fejezetben, akkor a könyv átnevezzük és tartalmaz ilyen oldalakat, és ez a könyv az új polc részévé válik.', + 'convert_to_shelf_permissions_desc' => 'A könyvhöz beállított engedélyek át lesznek másolva az új polcra és az összes olyan új alárendelt könyvre, amelyek nem rendelkeznek saját engedélyekkel. Vegye figyelembe, hogy a polcokon lévő engedélyek nem kapcsolódnak automatikusan a tartalomhoz, ahogy a könyvek esetében.', + 'convert_book' => 'Könyv átalakítása', + 'convert_book_confirm' => 'Biztosan konvertálni szeretné ezt a könyvet?', + 'convert_undo_warning' => 'Ezt nem lehet olyan könnyen visszavonni.', + 'convert_to_book' => 'Átalakítás könyvvé', + 'convert_to_book_desc' => 'Ezt a fejezetet új, azonos tartalmú könyvvé alakíthatja. Az ebben a fejezetben beállított engedélyek átmásolódnak az új könyvbe, de a szülőkönyvből származó örökölt engedélyek nem kerülnek másolásra, ami a hozzáférés-szabályozás megváltozásához vezethet.', + 'convert_chapter' => 'Fejezet átalakítása', + 'convert_chapter_confirm' => 'Biztosan át szeretnéd alakítani ezt a fejezetet?', // References - 'references' => 'References', - 'references_none' => 'There are no tracked references to this item.', - 'references_to_desc' => 'Listed below is all the known content in the system that links to this item.', + 'references' => 'Értékelések', + 'references_none' => 'Nincsenek nyomon követett hivatkozások erre az elemre.', + 'references_to_desc' => 'Az alábbiakban felsoroljuk az összes ismert tartalmat a rendszerben, amely erre az elemre hivatkozik.', // Watch Options - 'watch' => 'Watch', - 'watch_title_default' => 'Default Preferences', - 'watch_desc_default' => 'Revert watching to just your default notification preferences.', - 'watch_title_ignore' => 'Ignore', - 'watch_desc_ignore' => 'Ignore all notifications, including those from user-level preferences.', - 'watch_title_new' => 'New Pages', - 'watch_desc_new' => 'Notify when any new page is created within this item.', - 'watch_title_updates' => 'All Page Updates', - 'watch_desc_updates' => 'Notify upon all new pages and page changes.', - 'watch_desc_updates_page' => 'Notify upon all page changes.', - 'watch_title_comments' => 'All Page Updates & Comments', - 'watch_desc_comments' => 'Notify upon all new pages, page changes and new comments.', - 'watch_desc_comments_page' => 'Notify upon page changes and new comments.', - 'watch_change_default' => 'Change default notification preferences', - 'watch_detail_ignore' => 'Ignoring notifications', - 'watch_detail_new' => 'Watching for new pages', - 'watch_detail_updates' => 'Watching new pages and updates', - 'watch_detail_comments' => 'Watching new pages, updates & comments', - 'watch_detail_parent_book' => 'Watching via parent book', - 'watch_detail_parent_book_ignore' => 'Ignoring via parent book', - 'watch_detail_parent_chapter' => 'Watching via parent chapter', - 'watch_detail_parent_chapter_ignore' => 'Ignoring via parent chapter', + 'watch' => 'Megfigyelés', + 'watch_title_default' => 'Alapértelmezett beállítások', + 'watch_desc_default' => 'Állítsa vissza a megfigyelést az alapértelmezett értesítési beállításokra.', + 'watch_title_ignore' => 'Mellőzés', + 'watch_desc_ignore' => 'Figyelmen kívül hagyja az összes értesítést, beleértve a felhasználói szintű beállításokból származó értesítéseket is.', + 'watch_title_new' => 'Új oldalak', + 'watch_desc_new' => 'Értesítés, ha új oldal jön létre ezen az elemen belül.', + 'watch_title_updates' => 'Minden oldal frissítése', + 'watch_desc_updates' => 'Értesítés minden új oldalról és oldalváltozásról.', + 'watch_desc_updates_page' => 'Értesítsen minden oldalváltozásról.', + 'watch_title_comments' => 'Az oldal összes frissítése és megjegyzése', + 'watch_desc_comments' => 'Értesítés minden új oldalról, oldalváltozásról és új megjegyzésről.', + 'watch_desc_comments_page' => 'Értesítés az oldal változásairól és az új megjegyzésekről.', + 'watch_change_default' => 'Az alapértelmezett értesítési beállítások módosítása', + 'watch_detail_ignore' => 'Az értesítések figyelmen kívül hagyása', + 'watch_detail_new' => 'Új oldalak figyelése', + 'watch_detail_updates' => 'Új oldalak és frissítések figyelése', + 'watch_detail_comments' => 'Új oldalak, frissítések és megjegyzések figyelése', + 'watch_detail_parent_book' => 'Megfigyelés szülőkönyvből', + 'watch_detail_parent_book_ignore' => 'Figyelmen kívül hagyás a szülőkönyvön keresztül', + 'watch_detail_parent_chapter' => 'Megfigyelés szülő fejezetből', + 'watch_detail_parent_chapter_ignore' => 'Figyelmen kívül hagyás a szülő fejezeten keresztül', ]; diff --git a/lang/hu/errors.php b/lang/hu/errors.php index e51ef633f43..8ee055e29f7 100644 --- a/lang/hu/errors.php +++ b/lang/hu/errors.php @@ -10,6 +10,7 @@ // Auth 'error_user_exists_different_creds' => ':email címmel már létezik felhasználó, de más hitelesítő adatokkal.', + 'auth_pre_register_theme_prevention' => 'A felhasználói fiók nem regisztrálható a megadott adatokkal', 'email_already_confirmed' => 'Az email cím már meg van erősítve, meg lehet próbálni a bejelentkezést.', 'email_confirmation_invalid' => 'A megerősítő vezérjel nem érvényes vagy használva volt. Meg kell próbálni újraregisztrálni.', 'email_confirmation_expired' => 'A megerősítő vezérjel lejárt. Egy új megerősítő email lett elküldve.', @@ -23,7 +24,6 @@ 'saml_invalid_response_id' => 'A külső hitelesítő rendszerből érkező kérést nem ismerte fel az alkalmazás által indított folyamat. Bejelentkezés után az előző oldalra történő visszalépés okozhatja ezt a hibát.', 'saml_fail_authed' => 'Bejelentkezés :system használatával sikertelen, a rendszer nem biztosított sikeres hitelesítést', 'oidc_already_logged_in' => 'Már bejelentkezett', - 'oidc_user_not_registered' => ':name felhasználó nincs regisztrálva és az automatikus regisztráció le van tiltva', 'oidc_no_email_address' => 'Ehhez a felhasználóhoz nem található email cím a külső hitelesítő rendszer által átadott adatokban', 'oidc_fail_authed' => 'Bejelentkezés :system használatával sikertelen, a rendszer nem biztosított sikeres hitelesítést', 'social_no_action_defined' => 'Nincs művelet meghatározva', @@ -37,36 +37,37 @@ 'social_driver_not_found' => 'Közösségi meghajtó nem található', 'social_driver_not_configured' => ':socialAccount közösségi beállítások nem megfelelőek.', 'invite_token_expired' => 'Ez a meghívó hivatkozás lejárt. Helyette meg lehet próbálni új jelszót megadni a fiókhoz.', + 'login_user_not_found' => 'A művelethez nem található felhasználó.', // System 'path_not_writable' => ':filePath elérési út nem tölthető fel. Ellenőrizni kell, hogy az útvonal a kiszolgáló számára írható.', 'cannot_get_image_from_url' => 'Nem lehet lekérni a képet innen: :url', 'cannot_create_thumbs' => 'A kiszolgáló nem tud létrehozni bélyegképeket. Ellenőrizni kell, hogy telepítve van-a a GD PHP kiterjesztés.', 'server_upload_limit' => 'A kiszolgáló nem engedélyez ilyen méretű feltöltéseket. Kisebb fájlmérettel kell próbálkozni.', - 'server_post_limit' => 'The server cannot receive the provided amount of data. Try again with less data or a smaller file.', + 'server_post_limit' => 'A szerver nem tudja fogadni a megadott adatmennyiséget. Próbálkozz újra kevesebb adattal vagy egy kisebb fájllal.', 'uploaded' => 'A kiszolgáló nem engedélyez ilyen méretű feltöltéseket. Kisebb fájlmérettel kell próbálkozni.', // Drawing & Images 'image_upload_error' => 'Hiba történt a kép feltöltése közben', 'image_upload_type_error' => 'A feltöltött kép típusa érvénytelen', - 'image_upload_replace_type' => 'Image file replacements must be of the same type', - 'image_upload_memory_limit' => 'Failed to handle image upload and/or create thumbnails due to system resource limits.', - 'image_thumbnail_memory_limit' => 'Failed to create image size variations due to system resource limits.', - 'image_gallery_thumbnail_memory_limit' => 'Failed to create gallery thumbnails due to system resource limits.', - 'drawing_data_not_found' => 'Drawing data could not be loaded. The drawing file might no longer exist or you may not have permission to access it.', + 'image_upload_replace_type' => 'A cserélt képnek azonos típusúnak kell lennie', + 'image_upload_memory_limit' => 'A rendszererőforrás-korlátok miatt nem sikerült kezelni a képfeltöltést és/vagy az indexképek létrehozását.', + 'image_thumbnail_memory_limit' => 'A rendszererőforrás-korlátok miatt nem sikerült létrehozni a képméret-változatokat.', + 'image_gallery_thumbnail_memory_limit' => 'A rendszererőforrás-korlátok miatt nem sikerült létrehozni a galéria bélyegképét.', + 'drawing_data_not_found' => 'A rajzadatokat nem sikerült betölteni. Előfordulhat, hogy a rajzfájl már nem létezik, vagy nem rendelkezik hozzáférési engedéllyel.', // Attachments 'attachment_not_found' => 'Csatolmány nem található', - 'attachment_upload_error' => 'An error occurred uploading the attachment file', + 'attachment_upload_error' => 'Hiba történt a melléklet feltöltésekor', // Pages - 'page_draft_autosave_fail' => 'Nem sikerült a vázlat mentése. Mentés előtt meg kell róla győződni, hogy van internetkapcsolat', - 'page_draft_delete_fail' => 'Failed to delete page draft and fetch current page saved content', + 'page_draft_autosave_fail' => 'Nem sikerült a vázlat mentése. Mentés előtt állítsd helyre az internetkapcsolatot', + 'page_draft_delete_fail' => 'Nem sikerült törölni az oldalvázlatot és lekérni az aktuális oldal mentett tartalmat', 'page_custom_home_deletion' => 'Nem lehet oldalt törölni ha kezdőlapnak van beállítva', // Entities 'entity_not_found' => 'Entitás nem található', - 'bookshelf_not_found' => 'Shelf not found', + 'bookshelf_not_found' => 'Polc nem található', 'book_not_found' => 'Könyv nem található', 'page_not_found' => 'Oldal nem található', 'chapter_not_found' => 'Fejezet nem található', @@ -77,6 +78,7 @@ // Users 'users_cannot_delete_only_admin' => 'Nem lehet törölni az egyetlen adminisztrátort', 'users_cannot_delete_guest' => 'A vendég felhasználót nem lehet törölni', + 'users_could_not_send_invite' => 'Could not create user since invite email failed to send', // Roles 'role_cannot_be_edited' => 'Ezt a szerepkört nem lehet szerkeszteni', @@ -94,20 +96,32 @@ // Error pages '404_page_not_found' => 'Oldal nem található', 'sorry_page_not_found' => 'Sajnáljuk, a keresett oldal nem található.', - 'sorry_page_not_found_permission_warning' => 'If you expected this page to exist, you might not have permission to view it.', + 'sorry_page_not_found_permission_warning' => 'Ha arra számított, hogy ez az oldal létezik, előfordulhat, hogy nincs engedélye a megtekintésére.', 'image_not_found' => 'A kép nem található', 'image_not_found_subtitle' => 'Sajnáljuk, a keresett kép nem található.', - 'image_not_found_details' => 'If you expected this image to exist it might have been deleted.', + 'image_not_found_details' => 'Ha arra számított, hogy ez a kép létezik, akkor előfordulhat, hogy törölték.', 'return_home' => 'Vissza a kezdőlapra', - 'error_occurred' => 'Hiba örtént', + 'error_occurred' => 'Hiba történt', 'app_down' => ':appName jelenleg nem üzemel', 'back_soon' => 'Hamarosan újra elérhető lesz.', + // Import + 'import_zip_cant_read' => 'Could not read ZIP file.', + 'import_zip_cant_decode_data' => 'Could not find and decode ZIP data.json content.', + 'import_zip_no_data' => 'ZIP file data has no expected book, chapter or page content.', + 'import_validation_failed' => 'Import ZIP failed to validate with errors:', + 'import_zip_failed_notification' => 'Failed to import ZIP file.', + 'import_perms_books' => 'You are lacking the required permissions to create books.', + 'import_perms_chapters' => 'You are lacking the required permissions to create chapters.', + 'import_perms_pages' => 'You are lacking the required permissions to create pages.', + 'import_perms_images' => 'You are lacking the required permissions to create images.', + 'import_perms_attachments' => 'You are lacking the required permission to create attachments.', + // API errors 'api_no_authorization_found' => 'A kérésben nem található hitelesítési vezérjel', 'api_bad_authorization_format' => 'A kérésben hitelesítési vezérjel található de a formátuma érvénytelennek tűnik', 'api_user_token_not_found' => 'A megadott hitelesítési vezérjelhez nem található egyező API vezérjel', - 'api_incorrect_token_secret' => 'The secret provided for the given used API token is incorrect', + 'api_incorrect_token_secret' => 'Az API tokenhez használt secret helytelen', 'api_user_no_api_permission' => 'A használt API vezérjel tulajdonosának nincs jogosultsága API hívások végrehajtásához', 'api_user_token_expired' => 'A használt hitelesítési vezérjel lejárt', @@ -115,5 +129,5 @@ 'maintenance_test_email_failure' => 'Hiba történt egy teszt email küldésekor:', // HTTP errors - 'http_ssr_url_no_match' => 'The URL does not match the configured allowed SSR hosts', + 'http_ssr_url_no_match' => 'Az URL nem egyezik a konfigurált és engedélyezett SSR-állomásokkal', ]; diff --git a/lang/hu/notifications.php b/lang/hu/notifications.php index 1afd23f1dc4..c8cae9143c3 100644 --- a/lang/hu/notifications.php +++ b/lang/hu/notifications.php @@ -4,24 +4,24 @@ */ return [ - 'new_comment_subject' => 'New comment on page: :pageName', - 'new_comment_intro' => 'A user has commented on a page in :appName:', - 'new_page_subject' => 'New page: :pageName', - 'new_page_intro' => 'A new page has been created in :appName:', - 'updated_page_subject' => 'Updated page: :pageName', - 'updated_page_intro' => 'A page has been updated in :appName:', - 'updated_page_debounce' => 'To prevent a mass of notifications, for a while you won\'t be sent notifications for further edits to this page by the same editor.', + 'new_comment_subject' => 'Új megjegyzés ezen az oldalon: :pageName', + 'new_comment_intro' => 'Egy felhasználó hozzászólt egy oldalon itt: :appName:', + 'new_page_subject' => 'Új oldal: :pageName', + 'new_page_intro' => 'Az új oldal létrehozása sikeres volt itt: :appName:', + 'updated_page_subject' => 'Frissített oldal: :pageName', + 'updated_page_intro' => 'Az oldal frissítése sikeres volt itt: :appName:', + 'updated_page_debounce' => 'Az értesítések tömegének elkerülése érdekében egy ideig nem kap értesítést az oldal további szerkesztéseiről ugyanaz a szerkesztő.', - 'detail_page_name' => 'Page Name:', - 'detail_page_path' => 'Page Path:', - 'detail_commenter' => 'Commenter:', - 'detail_comment' => 'Comment:', - 'detail_created_by' => 'Created By:', - 'detail_updated_by' => 'Updated By:', + 'detail_page_name' => 'Oldal neve:', + 'detail_page_path' => 'Oldal helye:', + 'detail_commenter' => 'Hozzászóló:', + 'detail_comment' => 'Megjegyzés:', + 'detail_created_by' => 'Készítette:', + 'detail_updated_by' => 'Frissítette:', - 'action_view_comment' => 'View Comment', - 'action_view_page' => 'View Page', + 'action_view_comment' => 'Hozzászólás megtekintése', + 'action_view_page' => 'Oldal megtekintése', - 'footer_reason' => 'This notification was sent to you because :link cover this type of activity for this item.', - 'footer_reason_link' => 'your notification preferences', + 'footer_reason' => 'Ezt az értesítést azért küldtük, mert a :link lefedi ezt a tevékenységtípust ehhez az elemhez.', + 'footer_reason_link' => 'értesítési beállításait', ]; diff --git a/lang/hu/preferences.php b/lang/hu/preferences.php index 2b88f9671ff..4251a28fbc7 100644 --- a/lang/hu/preferences.php +++ b/lang/hu/preferences.php @@ -5,47 +5,47 @@ */ return [ - 'my_account' => 'My Account', + 'my_account' => 'Fiókom', - 'shortcuts' => 'Shortcuts', - 'shortcuts_interface' => 'UI Shortcut Preferences', - 'shortcuts_toggle_desc' => 'Here you can enable or disable keyboard system interface shortcuts, used for navigation and actions.', - 'shortcuts_customize_desc' => 'You can customize each of the shortcuts below. Just press your desired key combination after selecting the input for a shortcut.', - 'shortcuts_toggle_label' => 'Keyboard shortcuts enabled', - 'shortcuts_section_navigation' => 'Navigation', - 'shortcuts_section_actions' => 'Common Actions', - 'shortcuts_save' => 'Save Shortcuts', - 'shortcuts_overlay_desc' => 'Note: When shortcuts are enabled a helper overlay is available via pressing "?" which will highlight the available shortcuts for actions currently visible on the screen.', - 'shortcuts_update_success' => 'Shortcut preferences have been updated!', - 'shortcuts_overview_desc' => 'Manage keyboard shortcuts you can use to navigate the system user interface.', + 'shortcuts' => 'Billentyűparancsok', + 'shortcuts_interface' => 'UI billentyűparancsok beállításai', + 'shortcuts_toggle_desc' => 'Itt engedélyezheti vagy letilthatja a navigációhoz és műveletekhez használt billentyűparancsokat.', + 'shortcuts_customize_desc' => 'Az alábbi billentyűparancsok testre szabhatók. Csak nyomja meg a kívánt billentyűkombinációt, miután kiválasztotta a billentyűparancshoz tartozó mezőt.', + 'shortcuts_toggle_label' => 'A billentyűparancsok engedélyezve', + 'shortcuts_section_navigation' => 'Navigáció', + 'shortcuts_section_actions' => 'Gyakori műveletek', + 'shortcuts_save' => 'Billentyűparancsok mentése', + 'shortcuts_overlay_desc' => 'Megjegyzés: Amikor a gyorsbillentyűk engedélyezve vannak, egy segítő átfedés érhető el azzal, hogy a "?" billentyűt megnyomva kiemeli az aktuálisan látható képernyőn elérhető gyorsbillentyűket a műveletekhez.', + 'shortcuts_update_success' => 'A gyorsbillentyű-beállítások frissítve lettek!', + 'shortcuts_overview_desc' => 'A rendszerfelhasználói felületen történő navigálásához használható billentyűparancsok kezelése.', - 'notifications' => 'Notification Preferences', - 'notifications_desc' => 'Control the email notifications you receive when certain activity is performed within the system.', - 'notifications_opt_own_page_changes' => 'Notify upon changes to pages I own', - 'notifications_opt_own_page_comments' => 'Notify upon comments on pages I own', - 'notifications_opt_comment_replies' => 'Notify upon replies to my comments', - 'notifications_save' => 'Save Preferences', - 'notifications_update_success' => 'Notification preferences have been updated!', - 'notifications_watched' => 'Watched & Ignored Items', - 'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.', + 'notifications' => 'Értesítési beállítások', + 'notifications_desc' => 'Állítsd be az e-mail értesítéseket, amelyeket akkor kapsz, ha bizonyos tevékenység történik a rendszeren belül.', + 'notifications_opt_own_page_changes' => 'Értesítsen változásokról az általam tulajdonolt oldalakon', + 'notifications_opt_own_page_comments' => 'Értesítés a hozzászólásokról az általam tulajdonolt oldalakon', + 'notifications_opt_comment_replies' => 'Értesítsen válaszokról a hozzászólásaimra', + 'notifications_save' => 'Beállítások mentése', + 'notifications_update_success' => 'Az értesítési beállítások frissítve lettek!', + 'notifications_watched' => 'Megfigyelt és figyelmen kívül hagyott elemek', + 'notifications_watched_desc' => 'Az alábbi elemekre egyedi figyelési beállítások vannak alkalmazva. A beállítások frissítéséhez tekintsd meg az elemet, majd keresd a figyelési lehetőségeket az oldalsávban.', - 'auth' => 'Access & Security', - 'auth_change_password' => 'Change Password', - 'auth_change_password_desc' => 'Change the password you use to log-in to the application. This must be at least 8 characters long.', - 'auth_change_password_success' => 'Password has been updated!', + 'auth' => 'Hozzáférés és Biztonság', + 'auth_change_password' => 'Jelszó módosítása', + 'auth_change_password_desc' => 'Változtasd meg az alkalmazásba történő bejelentkezéshez használt jelszavadat. Ennek legalább 8 karakter hosszúnak kell lennie.', + 'auth_change_password_success' => 'A jelszó frissítve lett!', - 'profile' => 'Profile Details', - 'profile_desc' => 'Manage the details of your account which represents you to other users, in addition to details that are used for communication and system personalisation.', - 'profile_view_public' => 'View Public Profile', - 'profile_name_desc' => 'Configure your display name which will be visible to other users in the system through the activity you perform, and content you own.', - 'profile_email_desc' => 'This email will be used for notifications and, depending on active system authentication, system access.', - 'profile_email_no_permission' => 'Unfortunately you don\'t have permission to change your email address. If you want to change this, you\'d need to ask an administrator to change this for you.', - 'profile_avatar_desc' => 'Select an image which will be used to represent yourself to others in the system. Ideally this image should be square and about 256px in width and height.', - 'profile_admin_options' => 'Administrator Options', - 'profile_admin_options_desc' => 'Additional administrator-level options, like those to manage role assignments, can be found for your user account in the "Settings > Users" area of the application.', + 'profile' => 'Felhasználó részletei', + 'profile_desc' => 'A kommunikációhoz és a rendszer személyre szabásához használt adatokon kívül kezelheti fiókja adatait, amelyek más felhasználók számára jelennek meg.', + 'profile_view_public' => 'Nyilvános profil megtekintése', + 'profile_name_desc' => 'Állítsd be a megjelenített nevedet, amely látható lesz a rendszer többi felhasználója számára az általad végzett tevékenység és a saját tartalom révén.', + 'profile_email_desc' => 'Ezt az e-mail címet értesítésekre fogjuk használni, valamint az érvényben lévő beállítások függvényében hitelesítéshez is.', + 'profile_email_no_permission' => 'Sajnos nincs jogosultságod az e-mail cím megváltoztatására. Ha szeretnéd ezt megváltoztatni, kérj meg egy adminisztrátort, hogy ezt megtegye helyetted.', + 'profile_avatar_desc' => 'Válassz egy képet, amelyet a rendszerben használnál a neved mellett. A kép lehetőleg négyzet alakú és körülbelül 256px szélességű és magasságú legyen.', + 'profile_admin_options' => 'Adminisztrátori beállítások', + 'profile_admin_options_desc' => 'További adminisztrátori szintű lehetőségek, például a szerepkörök hozzárendelésének kezelése, megtalálhatóak a felhasználói fiókod beállításai között az "Beállítások > Felhasználók" területen az alkalmazásban.', - 'delete_account' => 'Delete Account', - 'delete_my_account' => 'Delete My Account', - 'delete_my_account_desc' => 'This will fully delete your user account from the system. You will not be able to recover this account or revert this action. Content you\'ve created, such as created pages and uploaded images, will remain.', - 'delete_my_account_warning' => 'Are you sure you want to delete your account?', + 'delete_account' => 'Felhasználói fiók törlése', + 'delete_my_account' => 'Törlöm a felhasználói fiókomat', + 'delete_my_account_desc' => 'Ez véglegesen törölni fogja a felhasználói fiókodat a rendszerből. Nem lesz lehetőséged visszaállítani ezt a fiókot, vagy visszavonni ezt a műveletet. A létrehozott tartalmak, például az oldalak és feltöltött képek megmaradnak.', + 'delete_my_account_warning' => 'Biztosan törölni szeretnéd a fiókodat?', ]; diff --git a/lang/hu/settings.php b/lang/hu/settings.php index 333b82574f1..813fee7b054 100644 --- a/lang/hu/settings.php +++ b/lang/hu/settings.php @@ -27,19 +27,19 @@ 'app_secure_images_toggle' => 'Magasabb biztonságú képfeltöltés engedélyezése', 'app_secure_images_desc' => 'Teljesítmény optimalizálási okokból minden kép nyilvános. Ez a beállítás egy véletlenszerű, nehezen kitalálható karakterláncot illeszt a képek útvonalának elejére. Meg kell győződni róla, hogy a könnyű hozzáférés megakadályozása érdekében a könyvtár indexek nincsenek engedélyezve.', 'app_default_editor' => 'Alapértelmezett oldal szerkesztő', - 'app_default_editor_desc' => 'Select which editor will be used by default when editing new pages. This can be overridden at a page level where permissions allow.', + 'app_default_editor_desc' => 'Válassza ki, hogy alapértelmezés szerint melyik szerkesztőt szeretné használni az új oldalak szerkesztésekor. Ezt felülírhatja oldalszintű szinten, amennyiben az engedélyek lehetővé teszik.', 'app_custom_html' => 'Egyéni HTML fejléc tartalom', 'app_custom_html_desc' => 'Az itt hozzáadott bármilyen tartalom be lesz illesztve minden oldal szekciójának aljára. Ez hasznos a stílusok felülírásához van analitikai kódok hozzáadásához.', 'app_custom_html_disabled_notice' => 'Az egyéni HTML fejléc tartalom le van tiltva ezen a beállítási oldalon, hogy az esetleg hibásan megadott módosításokat vissza lehessen állítani.', 'app_logo' => 'Alkalmazás logó', - 'app_logo_desc' => 'This is used in the application header bar, among other areas. This image should be 86px in height. Large images will be scaled down.', + 'app_logo_desc' => 'Ez az alkalmazás fejléc sávjában van használva többek között. Ennek a képnek 86 képpont magasnak kell lennie. A nagy képek át lesznek méretezve.', 'app_icon' => 'Alkalmazás ikon', - 'app_icon_desc' => 'This icon is used for browser tabs and shortcut icons. This should be a 256px square PNG image.', + 'app_icon_desc' => 'Ez az ikon a böngésző fülekhez és a gyorsikonokhoz használatos. Ez egy 256 képpont négyzet alakú PNG képnek kell lennie.', 'app_homepage' => 'Alkalmazás kezdőlapja', 'app_homepage_desc' => 'A kezdőlapon az alapértelmezés szerinti nézet helyett megjelenő nézet kiválasztása. A kiválasztott oldalakon figyelmen kívül lesznek hagyva az oldal engedélyek.', 'app_homepage_select' => 'Egy oldal kiválasztása', 'app_footer_links' => 'Lábléc linkek', - 'app_footer_links_desc' => 'Add links to show within the site footer. These will be displayed at the bottom of most pages, including those that do not require login. You can use a label of "trans::" to use system-defined translations. For example: Using "trans::common.privacy_policy" will provide the translated text "Privacy Policy" and "trans::common.terms_of_service" will provide the translated text "Terms of Service".', + 'app_footer_links_desc' => 'Adj hozzá linkeket a weboldal láblécéhez. Ezek a legtöbb oldalon megjelennek, beleértve azokat is, amelyekhez nincs szükség bejelentkezésre. Használhatsz egy "trans::" címkét a rendszer által definiált fordítások használatához. Például: A "trans::common.privacy_policy" használata a lefordított szöveget ("Adatvédelmi Irányelvek") adja vissza, és a "trans::common.terms_of_service" a "Szolgáltatási feltételek" lefordított szöveget adja eredményül.', 'app_footer_links_label' => 'Link címke', 'app_footer_links_url' => 'Link URL', 'app_footer_links_add' => 'Lábléc hivatkozás hozzáadása', @@ -49,11 +49,11 @@ // Color settings 'color_scheme' => 'Alkalmazás színséma', - 'color_scheme_desc' => 'Set the colors to use in the application user interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.', - 'ui_colors_desc' => 'Set the application primary color and default link color. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the application interface.', + 'color_scheme_desc' => 'Állítsd be a színeket az alkalmazás felhasználói felületén. A színeket külön-külön lehet konfigurálni a sötét és a világos módokhoz, hogy a legjobban illeszkedjenek a témához, és biztosítsák az olvashatóságot.', + 'ui_colors_desc' => 'Állítsa be az alkalmazás elsődleges színét és alapértelmezett hivatkozási színét. Az elsődleges színt főként a fejléc szalaghirdetéséhez, a gombokhoz és a felület díszítéséhez használják. Az alapértelmezett hivatkozásszín a szöveges hivatkozásokhoz és műveletekhez használatos, mind az írott tartalomban, mind az alkalmazás felületén.', 'app_color' => 'Elsődleges szín', 'link_color' => 'Alapértelmezett link szín', - 'content_colors_desc' => 'Set colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.', + 'content_colors_desc' => 'Beállítja az elemek színét az oldalszervezési hierarchiában. Az olvashatóság szempontjából javasolt az alapértelmezés szerinti színhez hasonló fényerősséget választani.', 'bookshelf_color' => 'Polc színe', 'book_color' => 'Könyv színe', 'chapter_color' => 'Fejezet színe', @@ -74,11 +74,41 @@ 'reg_confirm_restrict_domain_desc' => 'Azoknak az email tartományoknak a vesszővel elválasztott listája, melyekre a regisztráció korlátozva lesz. A felhasználók egy emailt fognak kapni, hogy megerősítsék az email címüket mielőtt használni kezdhetnék az alkalmazást.
Fontos tudni, hogy a felhasználók a sikeres regisztráció után megváltoztathatják az email címüket.', 'reg_confirm_restrict_domain_placeholder' => 'Nincs beállítva korlátozás', + // Sorting Settings + 'sorting' => 'Sorting', + 'sorting_book_default' => 'Default Book Sort', + 'sorting_book_default_desc' => 'Select the default sort rule to apply to new books. This won\'t affect existing books, and can be overridden per-book.', + 'sorting_rules' => 'Sort Rules', + 'sorting_rules_desc' => 'These are predefined sorting operations which can be applied to content in the system.', + 'sort_rule_assigned_to_x_books' => 'Assigned to :count Book|Assigned to :count Books', + 'sort_rule_create' => 'Create Sort Rule', + 'sort_rule_edit' => 'Edit Sort Rule', + 'sort_rule_delete' => 'Delete Sort Rule', + 'sort_rule_delete_desc' => 'Remove this sort rule from the system. Books using this sort will revert to manual sorting.', + 'sort_rule_delete_warn_books' => 'This sort rule is currently used on :count book(s). Are you sure you want to delete this?', + 'sort_rule_delete_warn_default' => 'This sort rule is currently used as the default for books. Are you sure you want to delete this?', + 'sort_rule_details' => 'Sort Rule Details', + 'sort_rule_details_desc' => 'Set a name for this sort rule, which will appear in lists when users are selecting a sort.', + 'sort_rule_operations' => 'Sort Operations', + 'sort_rule_operations_desc' => 'Configure the sort actions to be performed by moving them from the list of available operations. Upon use, the operations will be applied in order, from top to bottom. Any changes made here will be applied to all assigned books upon save.', + 'sort_rule_available_operations' => 'Available Operations', + 'sort_rule_available_operations_empty' => 'No operations remaining', + 'sort_rule_configured_operations' => 'Configured Operations', + 'sort_rule_configured_operations_empty' => 'Drag/add operations from the "Available Operations" list', + 'sort_rule_op_asc' => '(Asc)', + 'sort_rule_op_desc' => '(Desc)', + 'sort_rule_op_name' => 'Name - Alphabetical', + 'sort_rule_op_name_numeric' => '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_last' => 'Chapters Last', + // Maintenance settings 'maint' => 'Karbantartás', 'maint_image_cleanup' => 'Képek tisztítása', 'maint_image_cleanup_desc' => 'Végigolvassa az oldalakat és a tartalmak változatait, hogy leellenőrizze jelenleg mely képek és rajzok vannak használatban, és mely képek szerepelnek többször. A futtatása előtt feltétlen készíteni kell egy teljes adatbázis és lemezkép mentést.', - 'maint_delete_images_only_in_revisions' => 'Also delete images that only exist in old page revisions', + 'maint_delete_images_only_in_revisions' => 'Törölje azokat a képeket is, amelyek csak a régi oldalverziókban léteznek', 'maint_image_cleanup_run' => 'Tisztítás futtatása', 'maint_image_cleanup_warning' => ':count potenciálisan nem használt képet találtam. Biztosan törölhetőek ezek a képek?', 'maint_image_cleanup_success' => ':count potenciálisan nem használt kép megtalálva és törölve!', @@ -90,16 +120,16 @@ 'maint_send_test_email_mail_subject' => 'Teszt e-mail', 'maint_send_test_email_mail_greeting' => 'Az email kézbesítés működőképesnek tűnik!', 'maint_send_test_email_mail_text' => 'Gratulálunk! Mivel ez az email figyelmeztetés megérkezett az email beállítások megfelelőek.', - 'maint_recycle_bin_desc' => 'Deleted shelves, books, chapters & pages are sent to the recycle bin so they can be restored or permanently deleted. Older items in the recycle bin may be automatically removed after a while depending on system configuration.', + 'maint_recycle_bin_desc' => 'A törölt polcok, könyvek, fejezetek és oldalak a lomtárba kerülnek, így visszaállíthatók vagy véglegesen törölhetők. A rendszer konfigurációtól függően egy idő után a lomtárban lévő régebbi elemek automatikusan eltávolíthatók.', 'maint_recycle_bin_open' => 'Lomtár megnyitása', - 'maint_regen_references' => 'Regenerate References', - 'maint_regen_references_desc' => 'This action will rebuild the cross-item reference index within the database. This is usually handled automatically but this action can be useful to index old content or content added via unofficial methods.', - 'maint_regen_references_success' => 'Reference index has been regenerated!', - 'maint_timeout_command_note' => 'Note: This action can take time to run, which can lead to timeout issues in some web environments. As an alternative, this action be performed using a terminal command.', + 'maint_regen_references' => 'Referenciák újragenerálása', + 'maint_regen_references_desc' => 'Ez a művelet újraépíti az adatbázison belüli elemek közötti hivatkozási indexet. Ez általában automatikusan történik, de ez a művelet hasznos lehet régi vagy nem hivatalos módszerekkel hozzáadott tartalom indexeléséhez.', + 'maint_regen_references_success' => 'A referenciaindex újragenerálásra került!', + 'maint_timeout_command_note' => 'Megjegyzés: Ennek a műveletnek a futtatása időbe telhet, ami bizonyos webes környezetekben időtúllépési problémákhoz vezethet. Alternatív megoldásként ezt a műveletet terminálparancs segítségével is végrehajthatja.', // Recycle Bin 'recycle_bin' => 'Lomtár', - 'recycle_bin_desc' => 'Here you can restore items that have been deleted or choose to permanently remove them from the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.', + 'recycle_bin_desc' => 'Itt visszaállíthatja a törölt elemeket, vagy dönthet úgy, hogy véglegesen eltávolítja őket a rendszerből. Ez a lista nem szűrhető, ellentétben a rendszer hasonló tevékenységlistáival, ahol engedélyszűrőket alkalmaznak.', 'recycle_bin_deleted_item' => 'Törölt elem', 'recycle_bin_deleted_parent' => 'Szülő', 'recycle_bin_deleted_by' => 'Törölte', @@ -108,26 +138,26 @@ 'recycle_bin_restore' => 'Visszaállítás', 'recycle_bin_contents_empty' => 'A lomtár jelenleg üres', 'recycle_bin_empty' => 'Lomtár kiürítése', - 'recycle_bin_empty_confirm' => 'This will permanently destroy all items in the recycle bin including content contained within each item. Are you sure you want to empty the recycle bin?', - 'recycle_bin_destroy_confirm' => 'This action will permanently delete this item, along with any child elements listed below, from the system and you will not be able to restore this content. Are you sure you want to permanently delete this item?', + 'recycle_bin_empty_confirm' => 'Ezzel véglegesen megsemmisíti a lomtárban lévő összes elemet, beleértve az egyes tételekben található tartalmat is. Biztos benne, hogy ki akarja üríteni a lomtárat?', + 'recycle_bin_destroy_confirm' => 'Ez a művelet véglegesen törli ezt az elemet a rendszerből az alább felsorolt összes alárendelt elemmel együtt, és nem fogja tudni visszaállítani ezt a tartalmat. Biztosan véglegesen törli ezt az elemet?', 'recycle_bin_destroy_list' => 'Megsemmisítendő elemek', 'recycle_bin_restore_list' => 'Visszaállítandó elemek', - 'recycle_bin_restore_confirm' => 'This action will restore the deleted item, including any child elements, to their original location. If the original location has since been deleted, and is now in the recycle bin, the parent item will also need to be restored.', - 'recycle_bin_restore_deleted_parent' => 'The parent of this item has also been deleted. These will remain deleted until that parent is also restored.', + 'recycle_bin_restore_confirm' => 'Ez a művelet visszaállítja a törölt elemet, beleértve az utódelemeket is, az eredeti helyükre. Ha az eredeti helyet azóta törölték, és most a lomtárban van, akkor a szülőelemet is vissza kell állítani.', + 'recycle_bin_restore_deleted_parent' => 'Ennek az elemnek a szülője is törölve lett. Ezek mindaddig törölve maradnak, amíg az adott szülőt is vissza nem állítják.', 'recycle_bin_restore_parent' => 'Szűlő visszaállítása', - 'recycle_bin_destroy_notification' => 'Deleted :count total items from the recycle bin.', - 'recycle_bin_restore_notification' => 'Restored :count total items from the recycle bin.', + 'recycle_bin_destroy_notification' => 'Összesen :count elemet törölt a lomtárból.', + 'recycle_bin_restore_notification' => 'Összesen :count elemet helyreállítottak a lomtárból.', // Audit Log 'audit' => 'Audit napló', - 'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.', + 'audit_desc' => 'Ez a napló a rendszerben nyomon követett tevékenységek listáját jeleníti meg. Ez a lista nem szűrhető, ellentétben a rendszer hasonló tevékenységlistáival, ahol engedélyszűrőket alkalmaznak.', 'audit_event_filter' => 'Eseményszűrő', 'audit_event_filter_no_filter' => 'Nincs szűrő', 'audit_deleted_item' => 'Törölt elem', 'audit_deleted_item_name' => 'Név: :name', 'audit_table_user' => 'Felhasználó', 'audit_table_event' => 'Esemény', - 'audit_table_related' => 'Related Item or Detail', + 'audit_table_related' => 'Kapcsolódó elem vagy részlet', 'audit_table_ip' => 'IP Cím', 'audit_table_date' => 'Tevékenység időpontja', 'audit_date_from' => 'Kezdő dátum', @@ -136,11 +166,11 @@ // Role Settings 'roles' => 'Szerepkörök', 'role_user_roles' => 'Felhasználói szerepkörök', - 'roles_index_desc' => 'Roles are used to group users & provide system permission to their members. When a user is a member of multiple roles the privileges granted will stack and the user will inherit all abilities.', - 'roles_x_users_assigned' => ':count user assigned|:count users assigned', - 'roles_x_permissions_provided' => ':count permission|:count permissions', + 'roles_index_desc' => 'A szerepkörök a felhasználók csoportosítására és rendszerengedélyek biztosítására szolgálnak tagjaiknak. Ha egy felhasználó több szerepkör tagja, a megadott jogosultságok halmozódnak, és a felhasználó örökli az összes képességet.', + 'roles_x_users_assigned' => ':count hozzárendelt felhasználó|:count hozzárendelt felhasználó', + 'roles_x_permissions_provided' => ':count jogosultság|:count jogosultság', 'roles_assigned_users' => 'Hozzárendelt felhasználók', - 'roles_permissions_provided' => 'Provided Permissions', + 'roles_permissions_provided' => 'Megadott jogosultságok', 'role_create' => 'Új szerepkör létrehozása', 'role_delete' => 'Szerepkör törlése', 'role_delete_confirm' => 'Ez törölni fogja \':roleName\' szerepkört.', @@ -162,13 +192,14 @@ 'role_access_api' => 'Hozzáférés a rendszer API-hoz', 'role_manage_settings' => 'Alkalmazás beállításainak kezelése', 'role_export_content' => 'Tartalom exportálása', - 'role_editor_change' => 'Change page editor', - 'role_notifications' => 'Receive & manage notifications', + 'role_import_content' => 'Import content', + 'role_editor_change' => 'Oldalszerkesztő módosítása', + 'role_notifications' => 'Értesítések fogadása és kezelése', 'role_asset' => 'Eszköz jogosultságok', - 'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.', + 'roles_system_warning' => 'Ne feledje, hogy a fenti három engedély bármelyikéhez való hozzáférés lehetővé teszi a felhasználó számára, hogy módosítsa saját vagy a rendszerben mások jogosultságait. Csak megbízható felhasználókhoz rendeljen szerepeket ezekkel az engedélyekkel.', 'role_asset_desc' => 'Ezek a jogosultságok vezérlik az alapértelmezés szerinti hozzáférést a rendszerben található eszközökhöz. A könyvek, fejezetek és oldalak jogosultságai felülírják ezeket a jogosultságokat.', 'role_asset_admins' => 'Az adminisztrátorok automatikusan hozzáférést kapnak minden tartalomhoz, de ezek a beállítások megjeleníthetnek vagy elrejthetnek felhasználói felület beállításokat.', - 'role_asset_image_view_note' => 'This relates to visibility within the image manager. Actual access of uploaded image files will be dependant upon system image storage option.', + 'role_asset_image_view_note' => 'Ez a képkezelőn belüli láthatóságra vonatkozik. A feltöltött képfájlok tényleges elérése a rendszerkép tárolási beállításától függ.', 'role_all' => 'Összes', 'role_own' => 'Saját', 'role_controlled_by_asset' => 'Az általuk feltöltött eszköz által ellenőrzött', @@ -178,7 +209,7 @@ // Users 'users' => 'Felhasználók', - 'users_index_desc' => 'Create & manage individual user accounts within the system. User accounts are used for login and attribution of content & activity. Access permissions are primarily role-based but user content ownership, among other factors, may also affect permissions & access.', + 'users_index_desc' => 'Egyéni felhasználói fiókok létrehozása és kezelése a rendszeren belül. A felhasználói fiókok a bejelentkezéshez, valamint a tartalom és tevékenység hozzárendeléséhez használatosak. A hozzáférési engedélyek elsősorban szerepalapúak, de a felhasználói tartalmak tulajdonlása – többek között – befolyásolhatja az engedélyeket és a hozzáférést.', 'user_profile' => 'Felhasználói profil', 'users_add_new' => 'Új felhasználó hozzáadása', 'users_search' => 'Felhasználók keresése', @@ -189,19 +220,19 @@ 'users_role' => 'Felhasználói szerepkörök', 'users_role_desc' => 'A felhasználó melyik szerepkörhöz lesz rendelve. Ha a felhasználó több szerepkörhöz van rendelve, akkor ezeknek a szerepköröknek a jogosultságai összeadódnak, és a a felhasználó a hozzárendelt szerepkörök minden képességét megkapja.', 'users_password' => 'Felhasználó jelszava', - 'users_password_desc' => 'Set a password used to log-in to the application. This must be at least 8 characters long.', + 'users_password_desc' => 'Az alkalmazásba bejelentkezéshez használható jelszó beállítása. Legalább 8 karakter hosszúnak kell lennie.', 'users_send_invite_text' => 'Lehetséges egy meghívó emailt küldeni ennek a felhasználónak ami lehetővé teszi, hogy beállíthassa a saját jelszavát. Máskülönben a jelszót az erre jogosult felhasználónak kell beállítania.', 'users_send_invite_option' => 'Felhasználó meghívó levél küldése', 'users_external_auth_id' => 'Külső hitelesítés azonosítója', - 'users_external_auth_id_desc' => 'When an external authentication system is in use (such as SAML2, OIDC or LDAP) this is the ID which links this BookStack user to the authentication system account. You can ignore this field if using the default email-based authentication.', - 'users_password_warning' => 'Only fill the below if you would like to change the password for this user.', + 'users_external_auth_id_desc' => 'Ha külső hitelesítési rendszer van használatban (például SAML2, OIDC vagy LDAP), ez az az azonosító, amely a BookStack felhasználót a hitelesítési rendszerfiókhoz kapcsolja. Ha az alapértelmezett e-mail alapú hitelesítést használja, figyelmen kívül hagyhatja ezt a mezőt.', + 'users_password_warning' => 'Csak akkor töltse ki az alábbi mezőt, ha módosítani szeretné ennek a felhasználónak a jelszavát.', 'users_system_public' => 'Ez a felhasználó bármelyik, a példányt megtekintő felhasználót képviseli. Nem lehet vele bejelentkezni de automatikusan hozzá lesz rendelve.', 'users_delete' => 'Felhasználó törlése', 'users_delete_named' => ':userName felhasználó törlése', 'users_delete_warning' => '\':userName\' felhasználó teljesen törölve lesz a rendszerből.', 'users_delete_confirm' => 'Biztosan törölhető ez a felhasználó?', 'users_migrate_ownership' => 'Tulajdonjog átruházása', - 'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.', + 'users_migrate_ownership_desc' => 'Válasszon itt egy felhasználót, ha azt szeretné, hogy egy másik felhasználó legyen a tulajdonosa az összes, jelenleg a felhasználó tulajdonában lévő elemnek.', 'users_none_selected' => 'Nincs felhasználó kiválasztva', 'users_edit' => 'Felhasználó szerkesztése', 'users_edit_profile' => 'Profil szerkesztése', @@ -210,37 +241,37 @@ 'users_preferred_language' => 'Előnyben részesített nyelv', 'users_preferred_language_desc' => 'Ez a beállítás megváltoztatja az alkalmazás felhasználói felületén használt nyelvet. Nincs hatása a felhasználók által létrehozott tartalomra.', 'users_social_accounts' => 'Közösségi fiókok', - 'users_social_accounts_desc' => 'View the status of the connected social accounts for this user. Social accounts can be used in addition to the primary authentication system for system access.', + 'users_social_accounts_desc' => 'Tekintse meg a felhasználó csatlakoztatott közösségi fiókjainak állapotát. A közösségi fiókok az elsődleges hitelesítési rendszer mellett használhatók a rendszerhez való hozzáféréshez.', 'users_social_accounts_info' => 'Itt lehet egyéb fiókokat hozzákapcsolni a gyorsabb és könnyebb bejelentkezés érdekében. Itt olyan fiókot lehet lecsatlakoztatni, melynek korábban nem volt engedélyezett hozzáférése. Visszavonja a hozzáférést a csatlakoztatott szociális fiók profilbeállításaiból.', 'users_social_connect' => 'Fiók csatlakoztatása', 'users_social_disconnect' => 'Fiók lecsatlakoztatása', - 'users_social_status_connected' => 'Connected', - 'users_social_status_disconnected' => 'Disconnected', + 'users_social_status_connected' => 'Csatlakozva', + 'users_social_status_disconnected' => 'Lecsatlakozva', 'users_social_connected' => ':socialAccount fiók sikeresen csatlakoztatva a profilhoz.', 'users_social_disconnected' => ':socialAccount fiók sikeresen lecsatlakoztatva a profilról.', 'users_api_tokens' => 'API vezérjelek', - 'users_api_tokens_desc' => 'Create and manage the access tokens used to authenticate with the BookStack REST API. Permissions for the API are managed via the user that the token belongs to.', + 'users_api_tokens_desc' => 'A BookStack REST API-val történő hitelesítéshez használt hozzáférési token létrehozása és kezelése. Az API engedélyeit azon a felhasználón keresztül kezelik, akihez a token tartozik.', 'users_api_tokens_none' => 'Ehhez a felhasználóhoz nincsenek létrehozva API vezérjelek', 'users_api_tokens_create' => 'Vezérjel létrehozása', 'users_api_tokens_expires' => 'Lejárat', 'users_api_tokens_docs' => 'API dokumentáció', 'users_mfa' => 'Többfaktoros hitelesítés', - 'users_mfa_desc' => 'Setup multi-factor authentication as an extra layer of security for your user account.', - 'users_mfa_x_methods' => ':count method configured|:count methods configured', - 'users_mfa_configure' => 'Configure Methods', + 'users_mfa_desc' => 'Állítsa be a többlépcsős azonosítást egy extra biztonsági rétegként a felhasználói fiókjához.', + 'users_mfa_x_methods' => ':count metódus konfigurálva|:count metódus konfigurálva', + 'users_mfa_configure' => 'Módszer beállítása', // API Tokens 'user_api_token_create' => 'API vezérjel létrehozása', 'user_api_token_name' => 'Név', - 'user_api_token_name_desc' => 'Give your token a readable name as a future reminder of its intended purpose.', + 'user_api_token_name_desc' => 'Adjon a tokennek egy olvasható nevet, hogy a jövőben emlékeztessen a tervezett céljára.', 'user_api_token_expiry' => 'Lejárati dátum', 'user_api_token_expiry_desc' => 'Dátum megadása ameddig a vezérjel érvényes. Ez után a dátum után az ezzel a vezérjellel történő kérések nem fognak működni. Üresen hagyva a lejárati idő 100 évre lesz beállítva.', - 'user_api_token_create_secret_message' => 'Immediately after creating this token a "Token ID" & "Token Secret" will be generated and displayed. The secret will only be shown a single time so be sure to copy the value to somewhere safe and secure before proceeding.', + 'user_api_token_create_secret_message' => 'Közvetlenül a token létrehozása után egy „Token ID” és „Token Secret” generálódik és jelenik meg. A Secret csak egyszer jelenik meg, ezért a folytatás előtt másolja át az értéket egy biztonságos helyre.', 'user_api_token' => 'API vezérjel', 'user_api_token_id' => 'Vezérjel azonosító', 'user_api_token_id_desc' => 'Ez egy nem szerkeszthető, a rendszer által létrehozott azonosító ehhez a vezérjelhez amire API kérésekben lehet szükség.', 'user_api_token_secret' => 'Vezérjel titkos kódja', - 'user_api_token_secret_desc' => 'This is a system generated secret for this token which will need to be provided in API requests. This will only be displayed this one time so copy this value to somewhere safe and secure.', + 'user_api_token_secret_desc' => 'Ez egy rendszer által generált "secret" ehhez a tokenhez, amelyet meg kell adni az API-kérésekben. Ez csak most jelenik meg, ezért másolja ezt az értéket egy biztonságos helyre.', 'user_api_token_created' => 'Vezérjel létrehozva :timeAgo', 'user_api_token_updated' => 'Vezérjel frissítve :timeAgo', 'user_api_token_delete' => 'Vezérjel törlése', @@ -249,33 +280,41 @@ // Webhooks 'webhooks' => 'Webhook-ok', - 'webhooks_index_desc' => 'Webhooks are a way to send data to external URLs when certain actions and events occur within the system which allows event-based integration with external platforms such as messaging or notification systems.', - 'webhooks_x_trigger_events' => ':count trigger event|:count trigger events', - 'webhooks_create' => 'Create New Webhook', - 'webhooks_none_created' => 'No webhooks have yet been created.', - 'webhooks_edit' => 'Edit Webhook', - 'webhooks_save' => 'Save Webhook', - 'webhooks_details' => 'Webhook Details', - 'webhooks_details_desc' => 'Provide a user friendly name and a POST endpoint as a location for the webhook data to be sent to.', - 'webhooks_events' => 'Webhook Events', - 'webhooks_events_desc' => 'Select all the events that should trigger this webhook to be called.', - 'webhooks_events_warning' => 'Keep in mind that these events will be triggered for all selected events, even if custom permissions are applied. Ensure that use of this webhook won\'t expose confidential content.', - 'webhooks_events_all' => 'All system events', - 'webhooks_name' => 'Webhook Name', - 'webhooks_timeout' => 'Webhook Request Timeout (Seconds)', - 'webhooks_endpoint' => 'Webhook Endpoint', - 'webhooks_active' => 'Webhook Active', - 'webhook_events_table_header' => 'Events', - 'webhooks_delete' => 'Delete Webhook', - 'webhooks_delete_warning' => 'This will fully delete this webhook, with the name \':webhookName\', from the system.', - 'webhooks_delete_confirm' => 'Are you sure you want to delete this webhook?', - 'webhooks_format_example' => 'Webhook Format Example', - 'webhooks_format_example_desc' => 'Webhook data is sent as a POST request to the configured endpoint as JSON following the format below. The "related_item" and "url" properties are optional and will depend on the type of event triggered.', - 'webhooks_status' => 'Webhook Status', - 'webhooks_last_called' => 'Last Called:', - 'webhooks_last_errored' => 'Last Errored:', - 'webhooks_last_error_message' => 'Last Error Message:', + 'webhooks_index_desc' => 'A webhookok segítségével adatokat küldhetünk külső URL-ekre, amikor bizonyos műveletek és események történnek a rendszeren belül, ami lehetővé teszi az eseményalapú integrációt külső platformokkal, például üzenetküldő vagy értesítési rendszerekkel.', + 'webhooks_x_trigger_events' => ':count kiváltó esemény|:count kiváltó esemény', + 'webhooks_create' => 'Új webhook létrehozása', + 'webhooks_none_created' => 'Még nincs létrehozva egy webhook sem.', + 'webhooks_edit' => 'Webhook szerkesztése', + 'webhooks_save' => 'Webhook mentése', + 'webhooks_details' => 'Webhook részletei', + 'webhooks_details_desc' => 'Adjon meg egy felhasználóbarát nevet és egy POST-végpontot a webhook-adatok elküldésének helyeként.', + 'webhooks_events' => 'Webhook események', + 'webhooks_events_desc' => 'Jelölje ki az összes eseményt, amely kiváltja a webhook meghívását.', + 'webhooks_events_warning' => 'Ne feledje, hogy ezek az események az összes kiválasztott eseménynél aktiválódnak, még akkor is, ha egyéni engedélyeket alkalmaznak. Győződjön meg arról, hogy a webhook használata nem tesz közzé bizalmas tartalmat.', + 'webhooks_events_all' => 'Minden rendszeresemény', + 'webhooks_name' => 'Webhook neve', + 'webhooks_timeout' => 'Webhook kérés időtúllépése (másodperc)', + 'webhooks_endpoint' => 'Webhook végpont', + 'webhooks_active' => 'Webhook aktív', + 'webhook_events_table_header' => 'Események', + 'webhooks_delete' => 'Webhook törlése', + 'webhooks_delete_warning' => 'Ezzel a \':webhookName\' nevű webhookot teljesen törli a rendszerből.', + 'webhooks_delete_confirm' => 'Biztosan törli ezt a webhookot?', + 'webhooks_format_example' => 'Webhook formátum példa', + 'webhooks_format_example_desc' => 'A Webhook-adatok POST-kérésként kerülnek elküldésre a konfigurált végponthoz JSON-ként az alábbi formátumban. A "related_item" és az "url" tulajdonság nem kötelező, és az aktivált esemény típusától függ.', + 'webhooks_status' => 'Webhook állapota', + 'webhooks_last_called' => 'Utolsó hívás:', + 'webhooks_last_errored' => 'Utolsó hiba:', + 'webhooks_last_error_message' => 'Utolsó hibaüzenet:', + // Licensing + 'licenses' => 'Licenszek', + 'licenses_desc' => 'Ez az oldal a BookStack licencinformációit részletezi, a BookStackben használt projekteken és könyvtárakon kívül. Sok felsorolt projekt csak fejlesztési környezetben használható.', + 'licenses_bookstack' => 'BookStack Licensz', + 'licenses_php' => 'PHP könyvtár licencek', + 'licenses_js' => 'JavaScript könyvtár licencek', + 'licenses_other' => 'Egyéb licencek', + 'license_details' => 'Licenc részletek', //! If editing translations files directly please ignore this in all //! languages apart from en. Content will be auto-copied from en. @@ -287,6 +326,7 @@ 'bs' => 'Bosanski', 'ca' => 'Català', 'cs' => 'Česky', + 'cy' => 'Cymraeg', 'da' => 'Dansk', 'de' => 'Deutsch (Sie)', 'de_informal' => 'Deutsch (Du)', @@ -308,6 +348,7 @@ 'lt' => 'Lietuvių Kalba', 'lv' => 'Latviešu Valoda', 'nb' => 'Norsk (Bokmål)', + 'ne' => 'नेपाली', 'nn' => 'Nynorsk', 'nl' => 'Nederlands', 'pl' => 'Polski', diff --git a/lang/hu/validation.php b/lang/hu/validation.php index db6d24f8a2c..a215416ca5f 100644 --- a/lang/hu/validation.php +++ b/lang/hu/validation.php @@ -15,7 +15,7 @@ 'alpha_dash' => ':attribute csak betűket, számokat és kötőjeleket tartalmazhat.', 'alpha_num' => ':attribute csak betűket és számokat tartalmazhat.', 'array' => ':attribute tömb kell legyen.', - 'backup_codes' => 'The provided code is not valid or has already been used.', + 'backup_codes' => 'A megadott kód érvénytelen, vagy már felhasználták.', 'before' => ':attribute dátumnak :date előttinek kell lennie.', 'between' => [ 'numeric' => ':attribute értékének :min és :max között kell lennie.', @@ -32,7 +32,7 @@ 'digits_between' => ':attribute hosszának :min és :max számjegy között kell lennie.', 'email' => ':attribute érvényes email cím kell legyen.', 'ends_with' => ':attribute attribútumnak a következők egyikével kell végződnie: :values', - 'file' => 'The :attribute must be provided as a valid file.', + 'file' => 'A(z) :attribute érvényes fájlnak kell lennie.', 'filled' => ':attribute mező kötelező.', 'gt' => [ 'numeric' => ':attribute nagyobb kell, hogy legyen, mint :value.', @@ -42,9 +42,9 @@ ], 'gte' => [ 'numeric' => ':attribute attribútumnak :value értéknél nagyobbnak vagy vele egyenlőnek kell lennie.', - 'file' => 'The :attribute must be greater than or equal :value kilobytes.', - 'string' => 'The :attribute must be greater than or equal :value characters.', - 'array' => 'The :attribute must have :value items or more.', + 'file' => 'A(z) :attribute mérete nem lehet kevesebb, mint :value kilobájt.', + 'string' => 'A(z) :attribute nagyobbnak, vagy egyenlőnek kell lennie, mint a :value karakter.', + 'array' => 'A(z) :attribute rendelkezzen :value vagy több elemmel.', ], 'exists' => 'A kiválasztott :attribute érvénytelen.', 'image' => ':attribute kép kell legyen.', @@ -52,20 +52,20 @@ 'in' => 'A kiválasztott :attribute érvénytelen.', 'integer' => ':attribute egész szám kell legyen.', 'ip' => ':attribute érvényes IP cím kell legyen.', - 'ipv4' => 'The :attribute must be a valid IPv4 address.', - 'ipv6' => 'The :attribute must be a valid IPv6 address.', - 'json' => 'The :attribute must be a valid JSON string.', + 'ipv4' => 'A(z) :attribute érvényes IPv4 címnek kell lennie.', + 'ipv6' => 'A(z) :attribute érvényes IPv6 címnek kell lennie.', + 'json' => 'A(z) :attribute érvényes JSON stringnek kell lennie.', 'lt' => [ - 'numeric' => 'The :attribute must be less than :value.', - 'file' => 'The :attribute must be less than :value kilobytes.', - 'string' => 'The :attribute must be less than :value characters.', - 'array' => 'The :attribute must have less than :value items.', + 'numeric' => 'A(z) :attribute kisebb kell, hogy legyen, mint :value.', + 'file' => 'A(z) :attribute kevesebbnek kell lennie, mint :value kilobájt.', + 'string' => 'A(z) :attribute rövidebb kell, hogy legyen, mint :value karakter.', + 'array' => 'A(z) :attribute kevesebb, mint :value elemet kell, hogy tartalmazzon.', ], 'lte' => [ - 'numeric' => 'The :attribute must be less than or equal :value.', - 'file' => 'The :attribute must be less than or equal :value kilobytes.', - 'string' => 'The :attribute must be less than or equal :value characters.', - 'array' => 'The :attribute must not have more than :value items.', + 'numeric' => 'A(z) :attribute kisebb vagy egyenlő kell, hogy legyen, mint :value.', + 'file' => 'A(z) :attribute mérete nem lehet több, mint :value kilobájt.', + 'string' => 'A(z) :attribute hossza nem lehet több, mint :value karakter.', + 'array' => 'A(z) :attribute legfeljebb :value elemet kell, hogy tartalmazzon.', ], 'max' => [ 'numeric' => ':attribute nem lehet nagyobb mint :max.', @@ -91,7 +91,7 @@ 'required_without' => ':attribute mező kötelező ha :values nincs beállítva.', 'required_without_all' => ':attribute mező kötelező ha egyik :values sincs beállítva.', 'same' => ':attribute és :other értékének egyeznie kell.', - 'safe_url' => 'The provided link may not be safe.', + 'safe_url' => 'Előfordulhat, hogy a megadott link nem biztonságos.', 'size' => [ 'numeric' => ':attribute :size méretű kell legyen.', 'file' => ':attribute :size kilobájt méretű kell legyen.', @@ -100,11 +100,16 @@ ], 'string' => ':attribute karaktersorozatnak kell legyen.', 'timezone' => ':attribute érvényes zóna kell legyen.', - 'totp' => 'The provided code is not valid or has expired.', + 'totp' => 'A megadott kód érvénytelen vagy lejárt.', 'unique' => ':attribute már elkészült.', 'url' => ':attribute formátuma érvénytelen.', 'uploaded' => 'A fájlt nem lehet feltölteni. A kiszolgáló nem fogad el ilyen méretű fájlokat.', + 'zip_file' => 'The :attribute needs to reference a file within the ZIP.', + 'zip_file_mime' => 'The :attribute needs to reference a file of type :validTypes, found :foundType.', + 'zip_model_expected' => 'Data object expected but ":type" found.', + 'zip_unique' => 'The :attribute must be unique for the object type within the ZIP.', + // Custom validation lines 'custom' => [ 'password-confirm' => [ diff --git a/lang/id/activities.php b/lang/id/activities.php index 3337fcdd85c..6e1583a94e7 100644 --- a/lang/id/activities.php +++ b/lang/id/activities.php @@ -15,7 +15,7 @@ 'page_restore' => 'halaman telah dipulihkan', 'page_restore_notification' => 'Halaman berhasil dipulihkan', 'page_move' => 'halaman dipindahkan', - 'page_move_notification' => 'Page successfully moved', + 'page_move_notification' => 'Halaman berhasil dipindahkan', // Chapters 'chapter_create' => 'membuat bab', @@ -25,7 +25,7 @@ 'chapter_delete' => 'hapus bab', 'chapter_delete_notification' => 'Bab berhasil dihapus', 'chapter_move' => 'bab dipindahkan', - 'chapter_move_notification' => 'Chapter successfully moved', + 'chapter_move_notification' => 'Bab berhasil dipindahkan', // Books 'book_create' => 'membuat buku', @@ -50,31 +50,31 @@ 'bookshelf_delete_notification' => 'Rak berhasil dihapus', // Revisions - 'revision_restore' => 'restored revision', - 'revision_delete' => 'deleted revision', - 'revision_delete_notification' => 'Revision successfully deleted', + 'revision_restore' => 'revisi yang dipulihkan', + 'revision_delete' => 'revisi yang dihapus', + 'revision_delete_notification' => 'Revisi berhasil dihapus', // Favourites 'favourite_add_notification' => '":name" telah ditambahkan ke favorit Anda', 'favourite_remove_notification' => '":name" telah dihapus dari favorit Anda', // Watching - 'watch_update_level_notification' => 'Watch preferences successfully updated', + 'watch_update_level_notification' => 'Preferensi pantauan berhasil diperbarui', // Auth - 'auth_login' => 'logged in', - 'auth_register' => 'registered as new user', - 'auth_password_reset_request' => 'requested user password reset', - 'auth_password_reset_update' => 'reset user password', - 'mfa_setup_method' => 'configured MFA method', + 'auth_login' => 'telah masuk', + 'auth_register' => 'daftar sebagai user baru', + 'auth_password_reset_request' => 'permintaan pengguna mengatur ulang kata sandi', + 'auth_password_reset_update' => 'atur ulang kata sandi pengguna', + 'mfa_setup_method' => 'metode MFA yang dikonfigurasi', 'mfa_setup_method_notification' => 'Metode multi-faktor sukses dikonfigurasi', - 'mfa_remove_method' => 'removed MFA method', + 'mfa_remove_method' => 'menghapus metode MFA', 'mfa_remove_method_notification' => 'Metode multi-faktor sukses dihapus', // Settings - 'settings_update' => 'updated settings', - 'settings_update_notification' => 'Settings successfully updated', - 'maintenance_action_run' => 'ran maintenance action', + 'settings_update' => 'memperbarui setelan', + 'settings_update_notification' => 'Pengaturan berhasil diperbarui', + 'maintenance_action_run' => 'menjalankan tindakan pemeliharaan', // Webhooks 'webhook_create' => 'membuat webhook', @@ -84,40 +84,56 @@ 'webhook_delete' => 'menghapus webhook', 'webhook_delete_notification' => 'Webhook berhasil dihapus', + // Imports + 'import_create' => 'telat membuat impor', + 'import_create_notification' => 'Impor berhasil diunggah', + 'import_run' => 'telah memperbarui impor', + 'import_run_notification' => 'Konten berhasil diimpor', + 'import_delete' => 'telah menghapus impor', + 'import_delete_notification' => 'Impor berhasil dihapus', + // Users - 'user_create' => 'created user', - 'user_create_notification' => 'User successfully created', - 'user_update' => 'updated user', + 'user_create' => 'pengguna yang dibuat', + 'user_create_notification' => 'Pengguna berhasil dibuat', + 'user_update' => 'perbarui Pengguna', 'user_update_notification' => 'Pengguna berhasil diperbarui', - 'user_delete' => 'deleted user', + 'user_delete' => 'pengguna yang dihapus', 'user_delete_notification' => 'Pengguna berhasil dihapus', // API Tokens - 'api_token_create' => 'created api token', - 'api_token_create_notification' => 'API token successfully created', - 'api_token_update' => 'updated api token', - 'api_token_update_notification' => 'API token successfully updated', - 'api_token_delete' => 'deleted api token', - 'api_token_delete_notification' => 'API token successfully deleted', + 'api_token_create' => 'API token yang dibuat', + 'api_token_create_notification' => 'Token API berhasil dibuat', + 'api_token_update' => 'token API yang diperbarui', + 'api_token_update_notification' => 'token API berhasil dirubah', + 'api_token_delete' => 'token API yang dihapus', + 'api_token_delete_notification' => 'token API berhasil dihapus ', // Roles - 'role_create' => 'created role', + 'role_create' => 'telah membuat peran', 'role_create_notification' => 'Peran berhasil dibuat', - 'role_update' => 'updated role', + 'role_update' => 'telah memperbarui peran', 'role_update_notification' => 'Peran berhasil diperbarui', - 'role_delete' => 'deleted role', + 'role_delete' => 'telah menghapus peran', 'role_delete_notification' => 'Peran berhasil dihapus', // Recycle Bin - 'recycle_bin_empty' => 'emptied recycle bin', - 'recycle_bin_restore' => 'restored from recycle bin', - 'recycle_bin_destroy' => 'removed from recycle bin', + 'recycle_bin_empty' => 'telah mengosongkan tempat sampah', + 'recycle_bin_restore' => 'telah mengembalikan dari tempat sampah', + 'recycle_bin_destroy' => 'telah menghapus dari tempat sampah', // Comments 'commented_on' => 'berkomentar pada', - 'comment_create' => 'added comment', - 'comment_update' => 'updated comment', - 'comment_delete' => 'deleted comment', + 'comment_create' => 'telah menambah komentar', + 'comment_update' => 'telah memperbarui komentar', + 'comment_delete' => 'telah menghapus komentar', + + // Sort Rules + 'sort_rule_create' => 'telah membuat aturan penyortiran', + 'sort_rule_create_notification' => 'Aturan penyortiran berhasil dibuat', + 'sort_rule_update' => 'telah mengubah aturan penyortiran', + 'sort_rule_update_notification' => 'Aturan penyortiran berhasil diubah', + 'sort_rule_delete' => 'telah menghapus aturan penyortiran', + 'sort_rule_delete_notification' => 'Aturan penyortiran berhasil dihapus', // Other 'permissions_update' => 'izin diperbarui', diff --git a/lang/id/auth.php b/lang/id/auth.php index 5d174f6acb4..6af60ca80b1 100644 --- a/lang/id/auth.php +++ b/lang/id/auth.php @@ -86,32 +86,32 @@ 'mfa_setup_configured' => 'Sudah dikonfigurasi', 'mfa_setup_reconfigure' => 'Konfigurasi ulang', 'mfa_setup_remove_confirmation' => 'Apakah Anda yakin ingin menghapus metode autentikasi multi-faktor ini?', - 'mfa_setup_action' => 'Setup', - 'mfa_backup_codes_usage_limit_warning' => 'You have less than 5 backup codes remaining, Please generate and store a new set before you run out of codes to prevent being locked out of your account.', + 'mfa_setup_action' => 'Atur', + 'mfa_backup_codes_usage_limit_warning' => 'Anda memiliki kurang dari 5 kode cadangan yang tersisa. Harap buat dan simpan set baru sebelum Anda kehabisan kode untuk mencegah akun Anda terkunci.', 'mfa_option_totp_title' => 'Aplikasi Seluler', - 'mfa_option_totp_desc' => 'To use multi-factor authentication you\'ll need a mobile application that supports TOTP such as Google Authenticator, Authy or Microsoft Authenticator.', + 'mfa_option_totp_desc' => 'Untuk menggunakan autentikasi multi-faktor, Anda memerlukan aplikasi seluler yang mendukung TOTP seperti Google Authenticator, Authy, atau Microsoft Authenticator.', 'mfa_option_backup_codes_title' => 'Kode Cadangan', - 'mfa_option_backup_codes_desc' => 'Securely store a set of one-time-use backup codes which you can enter to verify your identity.', - 'mfa_gen_confirm_and_enable' => 'Confirm and Enable', - 'mfa_gen_backup_codes_title' => 'Backup Codes Setup', - 'mfa_gen_backup_codes_desc' => 'Store the below list of codes in a safe place. When accessing the system you\'ll be able to use one of the codes as a second authentication mechanism.', - 'mfa_gen_backup_codes_download' => 'Download Codes', - 'mfa_gen_backup_codes_usage_warning' => 'Each code can only be used once', - 'mfa_gen_totp_title' => 'Mobile App Setup', - 'mfa_gen_totp_desc' => 'To use multi-factor authentication you\'ll need a mobile application that supports TOTP such as Google Authenticator, Authy or Microsoft Authenticator.', - 'mfa_gen_totp_scan' => 'Scan the QR code below using your preferred authentication app to get started.', - 'mfa_gen_totp_verify_setup' => 'Verify Setup', - 'mfa_gen_totp_verify_setup_desc' => 'Verify that all is working by entering a code, generated within your authentication app, in the input box below:', - 'mfa_gen_totp_provide_code_here' => 'Provide your app generated code here', - 'mfa_verify_access' => 'Verify Access', - 'mfa_verify_access_desc' => 'Your user account requires you to confirm your identity via an additional level of verification before you\'re granted access. Verify using one of your configured methods to continue.', - 'mfa_verify_no_methods' => 'No Methods Configured', - 'mfa_verify_no_methods_desc' => 'No multi-factor authentication methods could be found for your account. You\'ll need to set up at least one method before you gain access.', + 'mfa_option_backup_codes_desc' => 'Menghasilkan serangkaian kode cadangan sekali pakai yang akan Anda masukkan saat masuk untuk memverifikasi identitas Anda. Pastikan untuk menyimpannya di tempat yang aman.', + 'mfa_gen_confirm_and_enable' => 'Konfirmasi dan Aktifkan', + 'mfa_gen_backup_codes_title' => 'Pengaturan Kode Cadangan', + 'mfa_gen_backup_codes_desc' => 'Simpan daftar kode di bawah ini di tempat yang aman. Saat mengakses sistem, Anda dapat menggunakan salah satu kode sebagai mekanisme autentikasi kedua.', + 'mfa_gen_backup_codes_download' => 'Unduh Kode', + 'mfa_gen_backup_codes_usage_warning' => 'Setiap kode hanya dapat digunakan satu kali', + 'mfa_gen_totp_title' => 'Pengaturan Aplikasi Seluler', + 'mfa_gen_totp_desc' => 'Untuk menggunakan autentikasi multi-faktor, Anda memerlukan aplikasi seluler yang mendukung TOTP seperti Google Authenticator, Authy, atau Microsoft Authenticator.', + 'mfa_gen_totp_scan' => 'Pindai kode QR di bawah ini menggunakan aplikasi autentikasi pilihan Anda untuk memulai.', + 'mfa_gen_totp_verify_setup' => 'Verifikasi Pengaturan', + 'mfa_gen_totp_verify_setup_desc' => 'Verifikasi bahwa semuanya berfungsi dengan memasukkan kode yang dibuat dalam aplikasi autentikasi Anda pada kolom input di bawah ini:', + 'mfa_gen_totp_provide_code_here' => 'Berikan kode yang dihasilkan aplikasi Anda di sini', + 'mfa_verify_access' => 'Verifikasi Akses', + 'mfa_verify_access_desc' => 'Akun pengguna Anda mengharuskan Anda mengonfirmasi identitas Anda melalui tingkat verifikasi tambahan sebelum Anda diberikan akses. Verifikasi menggunakan salah satu metode yang telah Anda konfigurasikan untuk melanjutkan.', + 'mfa_verify_no_methods' => 'Tidak Ada Metode yang Dikonfigurasi', + 'mfa_verify_no_methods_desc' => 'Tidak ada metode autentikasi multi-faktor yang ditemukan untuk akun Anda. Anda perlu menyiapkan setidaknya satu metode sebelum mendapatkan akses.', 'mfa_verify_use_totp' => 'Verifikasi menggunakan aplikasi seluler', 'mfa_verify_use_backup_codes' => 'Verifikasi menggunakan kode cadangan', 'mfa_verify_backup_code' => 'Kode Cadangan', - 'mfa_verify_backup_code_desc' => 'Enter one of your remaining backup codes below:', - 'mfa_verify_backup_code_enter_here' => 'Enter backup code here', - 'mfa_verify_totp_desc' => 'Enter the code, generated using your mobile app, below:', - 'mfa_setup_login_notification' => 'Multi-factor method configured, Please now login again using the configured method.', + 'mfa_verify_backup_code_desc' => 'Masukkan salah satu kode cadangan Anda yang tersisa di bawah ini:', + 'mfa_verify_backup_code_enter_here' => 'Masukkan kode cadangan di sini', + 'mfa_verify_totp_desc' => 'Masukkan kode yang dibuat menggunakan aplikasi seluler Anda di bawah ini:', + 'mfa_setup_login_notification' => 'Metode multi-faktor dikonfigurasi. Silakan masuk lagi menggunakan metode yang dikonfigurasi.', ]; diff --git a/lang/id/common.php b/lang/id/common.php index efb72dc4dd8..cee2d327ff0 100644 --- a/lang/id/common.php +++ b/lang/id/common.php @@ -6,7 +6,7 @@ // Buttons 'cancel' => 'Batal', - 'close' => 'Close', + 'close' => 'Tutup', 'confirm' => 'Konfirmasi', 'back' => 'Kembali', 'save' => 'Simpan', @@ -20,16 +20,18 @@ 'description' => 'Deskripsi', 'role' => 'Peran', 'cover_image' => 'Sampul gambar', - 'cover_image_description' => 'Gambar ini harus berukuran kira-kira 440x250 piksel.', + 'cover_image_description' => 'Gambar ini harus berukuran sekitar 440x250px walaupun nanti akan disesuaikan & terpotong secara fleksibel agar sesuai dengan tampilan pengguna, sehingga dimensi sebenarnya untuk tampilan akan berbeda.', // Actions 'actions' => 'Tindakan', 'view' => 'Lihat', 'view_all' => 'Lihat Semua', - 'new' => 'New', + 'new' => 'Buat Baru', 'create' => 'Buat', 'update' => 'Perbarui', 'edit' => 'Sunting', + 'archive' => 'Buat Arsip', + 'unarchive' => 'Batalkan Arsip', 'sort' => 'Sortir', 'move' => 'Pindahkan', 'copy' => 'Salin', @@ -41,18 +43,18 @@ 'reset' => 'Atur ulang', 'remove' => 'Hapus', 'add' => 'Tambah', - 'configure' => 'Configure', - 'manage' => 'Manage', + 'configure' => 'Atur', + 'manage' => 'Kelola', 'fullscreen' => 'Layar Penuh', 'favourite' => 'Favorit', 'unfavourite' => 'Batal favorit', 'next' => 'Selanjutnya', 'previous' => 'Sebelumnya', - 'filter_active' => 'Active Filter:', - 'filter_clear' => 'Clear Filter', - 'download' => 'Download', - 'open_in_tab' => 'Open in Tab', - 'open' => 'Open', + 'filter_active' => 'Filter Aktif:', + 'filter_clear' => 'Hapus Filter', + 'download' => 'Unduh', + 'open_in_tab' => 'Buka di tab baru', + 'open' => 'Buka', // Sort Options 'sort_options' => 'Opsi Sortir', @@ -78,20 +80,20 @@ 'default' => 'Bawaan', 'breadcrumb' => 'Breadcrumb', 'status' => 'Status', - 'status_active' => 'Active', - 'status_inactive' => 'Inactive', - 'never' => 'Never', - 'none' => 'None', + 'status_active' => 'Aktif', + 'status_inactive' => 'Tidak Aktif', + 'never' => 'Jangan Pernah', + 'none' => 'Tidak Satupun', // Header - 'homepage' => 'Homepage', + 'homepage' => 'Beranda', 'header_menu_expand' => 'Perluas Menu Tajuk', 'profile_menu' => 'Menu Profil', 'view_profile' => 'Tampilkan Profil', 'edit_profile' => 'Sunting Profil', 'dark_mode' => 'Mode Gelap', 'light_mode' => 'Mode Terang', - 'global_search' => 'Global Search', + 'global_search' => 'Pencarian Global', // Layout tabs 'tab_info' => 'Informasi', @@ -107,4 +109,7 @@ // Not directly used but available for convenience to users. 'privacy_policy' => 'Kebijakan Privasi', 'terms_of_service' => 'Ketentuan Layanan', + + // OpenSearch + 'opensearch_description' => 'Cari :appName', ]; diff --git a/lang/id/components.php b/lang/id/components.php index 373d6ace516..45aa72fba22 100644 --- a/lang/id/components.php +++ b/lang/id/components.php @@ -6,36 +6,36 @@ // Image Manager 'image_select' => 'Pilih Gambar', - 'image_list' => 'Image List', - 'image_details' => 'Image Details', - 'image_upload' => 'Upload Image', - 'image_intro' => 'Here you can select and manage images that have been previously uploaded to the system.', - 'image_intro_upload' => 'Upload a new image by dragging an image file into this window, or by using the "Upload Image" button above.', + 'image_list' => 'Daftar Gambar', + 'image_details' => 'Detail Gambar', + 'image_upload' => 'Unggah Gambar', + 'image_intro' => 'Di sini Anda dapat memilih dan mengelola gambar yang sebelumnya diunggah ke sistem.', + 'image_intro_upload' => 'Unggah gambar baru dengan menyeret berkas gambar ke jendela ini, atau dengan menggunakan tombol "Unggah Gambar" di atas.', 'image_all' => 'Semua', 'image_all_title' => 'Lihat semua gambar', 'image_book_title' => 'Lihat gambar yang diunggah ke buku ini', 'image_page_title' => 'Lihat gambar yang diunggah ke halaman ini', 'image_search_hint' => 'Cari berdasarkan nama gambar', 'image_uploaded' => 'Diunggah :uploadedDate', - 'image_uploaded_by' => 'Uploaded by :userName', - 'image_uploaded_to' => 'Uploaded to :pageLink', - 'image_updated' => 'Updated :updateDate', + 'image_uploaded_by' => 'Diunggah oleh :userName', + 'image_uploaded_to' => 'Diunggah ke :pageLink', + 'image_updated' => 'Diperbarui :updateDate', 'image_load_more' => 'Muat lebih banyak', 'image_image_name' => 'Muat lebih banyak', 'image_delete_used' => 'Gambar ini digunakan untuk halaman dibawah ini.', 'image_delete_confirm_text' => 'Anda yakin ingin menghapus gambar ini?', 'image_select_image' => 'Pilih gambar', 'image_dropzone' => 'Lepaskan gambar atau klik di sini untuk mengunggah', - 'image_dropzone_drop' => 'Drop images here to upload', + 'image_dropzone_drop' => 'Letakkan gambar di sini untuk diunggah', 'images_deleted' => 'Gambar Dihapus', 'image_preview' => 'Pratinjau Gambar', 'image_upload_success' => 'Gambar berhasil diunggah', 'image_update_success' => 'Detail gambar berhasil diperbarui', 'image_delete_success' => 'Gambar berhasil dihapus', - 'image_replace' => 'Replace Image', - 'image_replace_success' => 'Image file successfully updated', - 'image_rebuild_thumbs' => 'Regenerate Size Variations', - 'image_rebuild_thumbs_success' => 'Image size variations successfully rebuilt!', + 'image_replace' => 'Ganti Gambar', + 'image_replace_success' => 'Berkas gambar berhasil diperbarui', + 'image_rebuild_thumbs' => 'Buat Ulang Variasi Ukuran', + 'image_rebuild_thumbs_success' => 'Variasi ukuran gambar berhasil dibuat ulang!', // Code Editor 'code_editor' => 'Edit Kode', diff --git a/lang/id/editor.php b/lang/id/editor.php index 18d85733fa7..04999368930 100644 --- a/lang/id/editor.php +++ b/lang/id/editor.php @@ -13,6 +13,7 @@ 'cancel' => 'Batal', 'save' => 'Simpan', 'close' => 'Tutup', + 'apply' => 'Terapkan', 'undo' => 'Undo', 'redo' => 'Ulangi', 'left' => 'Kiri', @@ -28,66 +29,70 @@ // Toolbar 'formats' => 'Format', - 'header_large' => 'Large Header', - 'header_medium' => 'Medium Header', - 'header_small' => 'Small Header', - 'header_tiny' => 'Tiny Header', - 'paragraph' => 'Paragraph', - 'blockquote' => 'Blockquote', - 'inline_code' => 'Inline code', - 'callouts' => 'Callouts', - 'callout_information' => 'Information', + 'header_large' => 'Header Besar', + 'header_medium' => 'Header Sedang', + 'header_small' => 'Header Kecil', + 'header_tiny' => 'Header Sangat Kecil', + 'paragraph' => 'Paragraf', + 'blockquote' => 'Kutipan blok', + 'inline_code' => 'Kode sebaris', + 'callouts' => 'Anotasi', + 'callout_information' => 'Informasi', 'callout_success' => 'Sukses', 'callout_warning' => 'Peringatan', 'callout_danger' => 'Bahaya', 'bold' => 'Berani', 'italic' => 'Italic', 'underline' => 'Garis Bawah', - 'strikethrough' => 'Strikethrough', - 'superscript' => 'Superscript', + 'strikethrough' => 'Coret', + 'superscript' => 'Superskrip', 'subscript' => 'Berlangganan', 'text_color' => 'Warna teks', + 'highlight_color' => 'Highlight color', 'custom_color' => 'Warna khusus', 'remove_color' => 'Hapus Warna', 'background_color' => 'Warna latar', 'align_left' => 'Rata Kiri', - 'align_center' => 'Align center', - 'align_right' => 'Align right', - 'align_justify' => 'Justify', - 'list_bullet' => 'Bullet list', - 'list_numbered' => 'Numbered list', - 'list_task' => 'Task list', - 'indent_increase' => 'Increase indent', - 'indent_decrease' => 'Decrease indent', - 'table' => 'Table', - 'insert_image' => 'Insert image', - 'insert_image_title' => 'Insert/Edit Image', - 'insert_link' => 'Insert/edit link', - 'insert_link_title' => 'Insert/Edit Link', - 'insert_horizontal_line' => 'Insert horizontal line', - 'insert_code_block' => 'Insert code block', - 'edit_code_block' => 'Edit code block', - 'insert_drawing' => 'Insert/edit drawing', - 'drawing_manager' => 'Drawing manager', - 'insert_media' => 'Insert/edit media', - 'insert_media_title' => 'Insert/Edit Media', - 'clear_formatting' => 'Clear formatting', - 'source_code' => 'Source code', - 'source_code_title' => 'Source Code', - 'fullscreen' => 'Fullscreen', - 'image_options' => 'Image options', + 'align_center' => 'Rata tengah', + 'align_right' => 'Rata kanan', + 'align_justify' => 'Rata kanan kiri', + 'list_bullet' => 'Daftar poin', + 'list_numbered' => 'Daftar bernomor', + 'list_task' => 'Daftar tugas', + 'indent_increase' => 'Tambah indentasi', + 'indent_decrease' => 'Kurangi indentasi', + 'table' => 'Tabel', + 'insert_image' => 'Sisipkan gambar', + 'insert_image_title' => 'Sisipkan/Ubah Gambar', + 'insert_link' => 'Sisipkan/ubah tautan', + 'insert_link_title' => 'Sisipkan/Ubah Tautan', + 'insert_horizontal_line' => 'Sisipkan garis horizontal', + 'insert_code_block' => 'Sisipkan blok kode', + 'edit_code_block' => 'Ubah blok kode', + 'insert_drawing' => 'Sisipkan/ubah gambaran', + 'drawing_manager' => 'Manajer Gambaran', + 'insert_media' => 'Sisipkan/ubah media', + 'insert_media_title' => 'Sisipkan/Ubah Media', + 'clear_formatting' => 'Bersihkan format', + 'source_code' => 'Kode sumber', + 'source_code_title' => 'Kode Sumber', + 'fullscreen' => 'Layar penuh', + 'image_options' => 'Opsi gambar', // Tables - 'table_properties' => 'Table properties', - 'table_properties_title' => 'Table Properties', - 'delete_table' => 'Delete table', - 'insert_row_before' => 'Insert row before', - 'insert_row_after' => 'Insert row after', - 'delete_row' => 'Delete row', - 'insert_column_before' => 'Insert column before', - 'insert_column_after' => 'Insert column after', - 'delete_column' => 'Delete column', - 'table_cell' => 'Cell', + 'table_properties' => 'Properti tabel', + 'table_properties_title' => 'Properti Tabel', + 'delete_table' => 'Hapus tabel', + 'table_clear_formatting' => 'Bersihkan format tabel', + 'resize_to_contents' => 'Sesuaikan dengan ukuran konten', + 'row_header' => 'Judul baris', + 'insert_row_before' => 'Sisipkan baris sebelum', + 'insert_row_after' => 'Sisipkan baris setelah', + 'delete_row' => 'Hapus baris', + 'insert_column_before' => 'Sisipkan kolom sebelum', + 'insert_column_after' => 'Sisipkan kolom sesudah', + 'delete_column' => 'Hapus kolom', + 'table_cell' => 'Sel', 'table_row' => 'Row', 'table_column' => 'Column', 'cell_properties' => 'Cell properties', @@ -144,6 +149,7 @@ 'url' => 'URL', 'text_to_display' => 'Text to display', 'title' => 'Title', + 'browse_links' => 'Browse links', 'open_link' => 'Open link', 'open_link_in' => 'Open link in...', 'open_link_current' => 'Current window', @@ -160,6 +166,8 @@ 'about' => 'About the editor', 'about_title' => 'About the WYSIWYG Editor', 'editor_license' => 'Editor License & Copyright', + 'editor_lexical_license' => 'This editor is built as a fork of :lexicalLink which is distributed under the MIT license.', + 'editor_lexical_license_link' => 'Full license details can be found here.', 'editor_tiny_license' => 'This editor is built using :tinyLink which is provided under the MIT license.', 'editor_tiny_license_link' => 'The copyright and license details of TinyMCE can be found here.', 'save_continue' => 'Save Page & Continue', diff --git a/lang/id/entities.php b/lang/id/entities.php index 0c7c7a9ef44..2f932992571 100644 --- a/lang/id/entities.php +++ b/lang/id/entities.php @@ -39,6 +39,34 @@ 'export_pdf' => 'Dokumen PDF', 'export_text' => 'Dokumen Teks Biasa', 'export_md' => 'File Markdown', + 'export_zip' => 'Portable ZIP', + 'default_template' => 'Default Page Template', + 'default_template_explain' => 'Assign a page template that will be used as the default content for all pages created within this item. Keep in mind this will only be used if the page creator has view access to the chosen template page.', + 'default_template_select' => 'Select a template page', + 'import' => 'Import', + 'import_validate' => 'Validate Import', + 'import_desc' => 'Import books, chapters & pages using a portable zip export from the same, or a different, instance. Select a ZIP file to proceed. After the file has been uploaded and validated you\'ll be able to configure & confirm the import in the next view.', + '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_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_size' => ':size Import ZIP Size', + 'import_uploaded_at' => 'Uploaded :relativeTime', + 'import_uploaded_by' => 'Uploaded by', + 'import_location' => 'Import Location', + '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_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', + 'breadcrumb_siblings_for_book' => 'Navigate siblings for book', + 'breadcrumb_siblings_for_bookshelf' => 'Navigate siblings for shelf', // Permissions and restrictions 'permissions' => 'Izin', @@ -132,9 +160,6 @@ 'books_edit_named' => 'Sunting Buku :bookName', 'books_form_book_name' => 'Nama Buku', 'books_save' => 'Simpan Buku', - 'books_default_template' => 'Default Page Template', - 'books_default_template_explain' => 'Assign a page template that will be used as the default content for all new pages in this book. Keep in mind this will only be used if the page creator has view access to those chosen template page.', - 'books_default_template_select' => 'Select a template page', 'books_permissions' => 'Izin Buku', 'books_permissions_updated' => 'Izin Buku Diperbarui', 'books_empty_contents' => 'Tidak ada halaman atau bab yang telah dibuat untuk buku ini.', @@ -145,7 +170,9 @@ 'books_search_this' => 'Cari buku ini', 'books_navigation' => 'Navigasi Buku', 'books_sort' => 'Sortir Isi Buku', - 'books_sort_desc' => 'Move chapters and pages within a book to reorganise its contents. Other books can be added which allows easy moving of chapters and pages between books.', + 'books_sort_desc' => 'Move chapters and pages within a book to reorganise its contents. Other books can be added which allows easy moving of chapters and pages between books. Optionally an auto sort rule can be set to automatically sort this book\'s contents upon changes.', + 'books_sort_auto_sort' => 'Auto Sort Option', + 'books_sort_auto_sort_active' => 'Auto Sort Active: :sortName', 'books_sort_named' => 'Sortir Buku :bookName', 'books_sort_name' => 'Diurutkan berdasarkan nama', 'books_sort_created' => 'Urutkan berdasarkan Tanggal Dibuat', @@ -207,7 +234,7 @@ 'pages_delete_draft' => 'Hapus Halaman Draf', 'pages_delete_success' => 'Halaman dihapus', 'pages_delete_draft_success' => 'Halaman draf dihapus', - 'pages_delete_warning_template' => 'This page is in active use as a book default page template. These books will no longer have a default page template assigned after this page is deleted.', + 'pages_delete_warning_template' => 'This page is in active use as a book or chapter default page template. These books or chapters will no longer have a default page template assigned after this page is deleted.', 'pages_delete_confirm' => 'Anda yakin ingin menghapus halaman ini?', 'pages_delete_draft_confirm' => 'Anda yakin ingin menghapus halaman draf ini?', 'pages_editing_named' => 'Menyunting Halaman :pageName', @@ -224,6 +251,8 @@ 'pages_edit_switch_to_markdown_clean' => '(Clean Content)', 'pages_edit_switch_to_markdown_stable' => '(Stable Content)', 'pages_edit_switch_to_wysiwyg' => 'Switch to WYSIWYG Editor', + 'pages_edit_switch_to_new_wysiwyg' => 'Switch to new WYSIWYG', + 'pages_edit_switch_to_new_wysiwyg_desc' => '(In Beta Testing)', 'pages_edit_set_changelog' => 'Atur Changelog', 'pages_edit_enter_changelog_desc' => 'Masukkan deskripsi singkat tentang perubahan yang Anda buat', 'pages_edit_enter_changelog' => 'Masuk ke Changelog', @@ -243,6 +272,7 @@ 'pages_md_insert_drawing' => 'Sisipkan Gambar', 'pages_md_show_preview' => 'Show preview', 'pages_md_sync_scroll' => 'Sync preview scroll', + 'pages_md_plain_editor' => 'Plaintext editor', 'pages_drawing_unsaved' => 'Unsaved Drawing Found', 'pages_drawing_unsaved_confirm' => 'Unsaved drawing data was found from a previous failed drawing save attempt. Would you like to restore and continue editing this unsaved drawing?', 'pages_not_in_chapter' => 'Halaman tidak dalam satu bab', @@ -367,8 +397,11 @@ 'comment' => 'Komentar', 'comments' => 'Komentar', 'comment_add' => 'Tambah Komentar', + 'comment_none' => 'No comments to display', 'comment_placeholder' => 'Tinggalkan komentar disini', - 'comment_count' => '{0} Tidak ada komentar |{1} 1 Komentar |[2,*] :count Komentar', + 'comment_thread_count' => ':count Comment Thread|:count Comment Threads', + 'comment_archived_count' => ':count Archived', + 'comment_archived_threads' => 'Archived Threads', 'comment_save' => 'Simpan Komentar', 'comment_new' => 'Komentar Baru', 'comment_created' => 'dikomentari oleh :createDiff', @@ -377,8 +410,14 @@ 'comment_deleted_success' => 'Komentar telah dihapus', 'comment_created_success' => 'Komentar telah di tambahkan', 'comment_updated_success' => 'Komentar Telah diperbaharui', + 'comment_archive_success' => 'Comment archived', + 'comment_unarchive_success' => 'Comment un-archived', + 'comment_view' => 'View comment', + 'comment_jump_to_thread' => 'Jump to thread', 'comment_delete_confirm' => 'Anda yakin ingin menghapus komentar ini?', 'comment_in_reply_to' => 'Sebagai balasan untuk :commentId', + 'comment_reference' => 'Reference', + 'comment_reference_outdated' => '(Outdated)', 'comment_editor_explain' => 'Here are the comments that have been left on this page. Comments can be added & managed when viewing the saved page.', // Revision diff --git a/lang/id/errors.php b/lang/id/errors.php index 7ed7f2e1196..6f766173874 100644 --- a/lang/id/errors.php +++ b/lang/id/errors.php @@ -10,6 +10,7 @@ // Auth 'error_user_exists_different_creds' => 'Pengguna dengan email :email sudah ada tetapi dengan kredensial berbeda.', + 'auth_pre_register_theme_prevention' => 'Akun pengguna tidak dapat didaftarkan untuk rincian yang diberikan', 'email_already_confirmed' => 'Email telah dikonfirmasi, Coba masuk.', 'email_confirmation_invalid' => 'Token konfirmasi ini tidak valid atau telah digunakan, Silakan coba mendaftar lagi.', 'email_confirmation_expired' => 'Token konfirmasi telah kedaluwarsa, Email konfirmasi baru telah dikirim.', @@ -22,10 +23,9 @@ 'saml_no_email_address' => 'Tidak dapat menemukan sebuah alamat email untuk pengguna ini, dalam data yang diberikan oleh sistem autentikasi eksternal', 'saml_invalid_response_id' => 'Permintaan dari sistem otentikasi eksternal tidak dikenali oleh sebuah proses yang dimulai oleh aplikasi ini. Menavigasi kembali setelah masuk dapat menyebabkan masalah ini.', 'saml_fail_authed' => 'Masuk menggunakan :system gagal, sistem tidak memberikan otorisasi yang berhasil', - 'oidc_already_logged_in' => 'Already logged in', - 'oidc_user_not_registered' => 'The user :name is not registered and automatic registration is disabled', - 'oidc_no_email_address' => 'Could not find an email address, for this user, in the data provided by the external authentication system', - 'oidc_fail_authed' => 'Login using :system failed, system did not provide successful authorization', + 'oidc_already_logged_in' => 'Sudah masuk', + 'oidc_no_email_address' => 'Tidak dapat menemukan alamat email untuk pengguna ini dalam data yang diberikan oleh sistem autentikasi eksternal', + 'oidc_fail_authed' => 'Masuk menggunakan :system gagal, sistem tidak memberikan otorisasi yang berhasil', 'social_no_action_defined' => 'Tidak ada tindakan yang ditentukan', 'social_login_bad_response' => "Kesalahan yang diterima selama masuk menggunakan :socialAccount : \n:error", 'social_account_in_use' => 'Akun :socialAccount ini sudah digunakan, Coba masuk melalui opsi :socialAccount.', @@ -37,36 +37,37 @@ 'social_driver_not_found' => 'Pengemudi sosial tidak ditemukan', 'social_driver_not_configured' => 'Pengaturan sosial :socialAccount Anda tidak dikonfigurasi dengan benar.', 'invite_token_expired' => 'Tautan undangan ini telah kedaluwarsa. Sebagai gantinya, Anda dapat mencoba mengatur ulang kata sandi akun Anda.', + 'login_user_not_found' => 'Pengguna untuk tindakan ini tidak dapat ditemukan.', // System 'path_not_writable' => 'Jalur berkas :filePath tidak dapat diunggah. Pastikan berkas tersebut dapat ditulis ke server.', 'cannot_get_image_from_url' => 'Tidak dapat mengambil gambar dari :url', 'cannot_create_thumbs' => 'Server tidak dapat membuat thumbnail. Harap periksa apakah Anda telah memasang ekstensi GD PHP.', 'server_upload_limit' => 'Server tidak mengizinkan unggahan dengan ukuran ini. Harap coba ukuran berkas yang lebih kecil.', - 'server_post_limit' => 'The server cannot receive the provided amount of data. Try again with less data or a smaller file.', + 'server_post_limit' => 'Server tidak dapat menerima jumlah data yang diberikan. Coba lagi dengan data yang lebih sedikit atau berkas yang lebih kecil.', 'uploaded' => 'Server tidak mengizinkan unggahan dengan ukuran ini. Harap coba ukuran berkas yang lebih kecil.', // Drawing & Images 'image_upload_error' => 'Terjadi kesalahan saat mengunggah gambar', 'image_upload_type_error' => 'Jenis gambar yang diunggah tidak valid', - 'image_upload_replace_type' => 'Image file replacements must be of the same type', - 'image_upload_memory_limit' => 'Failed to handle image upload and/or create thumbnails due to system resource limits.', - 'image_thumbnail_memory_limit' => 'Failed to create image size variations due to system resource limits.', - 'image_gallery_thumbnail_memory_limit' => 'Failed to create gallery thumbnails due to system resource limits.', - 'drawing_data_not_found' => 'Drawing data could not be loaded. The drawing file might no longer exist or you may not have permission to access it.', + 'image_upload_replace_type' => 'Penggantian file gambar harus berjenis sama', + 'image_upload_memory_limit' => 'Gagal menangani pengunggahan gambar dan/atau membuat thumbnail karena keterbatasan sumber daya sistem.', + 'image_thumbnail_memory_limit' => 'Gagal membuat variasi ukuran gambar karena keterbatasan sumber daya sistem.', + 'image_gallery_thumbnail_memory_limit' => 'Gagal membuat thumbnail galeri karena keterbatasan sumber daya sistem.', + 'drawing_data_not_found' => 'Data gambar tidak dapat dimuat. Berkas gambar mungkin sudah tidak ada atau Anda mungkin tidak memiliki izin untuk mengaksesnya.', // Attachments 'attachment_not_found' => 'Lampiran tidak ditemukan', - 'attachment_upload_error' => 'An error occurred uploading the attachment file', + 'attachment_upload_error' => 'Terjadi kesalahan saat mengunggah berkas', // Pages 'page_draft_autosave_fail' => 'Gagal menyimpan draf. Pastikan Anda memiliki koneksi internet sebelum menyimpan halaman ini', - 'page_draft_delete_fail' => 'Failed to delete page draft and fetch current page saved content', + 'page_draft_delete_fail' => 'Gagal menghapus draf halaman dan mengambil konten tersimpan halaman saat ini', 'page_custom_home_deletion' => 'Tidak dapat menghapus sebuah halaman saat diatur sebagai sebuah halaman beranda', // Entities 'entity_not_found' => 'Entitas tidak ditemukan', - 'bookshelf_not_found' => 'Shelf not found', + 'bookshelf_not_found' => 'Rak tidak ditemukan', 'book_not_found' => 'Buku tidak ditemukan', 'page_not_found' => 'Halaman tidak ditemukan', 'chapter_not_found' => 'Bab tidak ditemukan', @@ -77,6 +78,7 @@ // Users 'users_cannot_delete_only_admin' => 'Anda tidak dapat menghapus satu-satunya admin', 'users_cannot_delete_guest' => 'Anda tidak dapat menghapus pengguna tamu', + 'users_could_not_send_invite' => 'Tidak dapat membuat pengguna karena email undangan gagal dikirim', // Roles 'role_cannot_be_edited' => 'Peran ini tidak dapat disunting', @@ -103,6 +105,18 @@ 'app_down' => ':appName sedang down sekarang', 'back_soon' => 'Ini akan segera kembali.', + // Import + 'import_zip_cant_read' => 'Tidak dapat membaca berkas ZIP.', + 'import_zip_cant_decode_data' => 'Tidak dapat menemukan dan mendekode konten ZIP data.json.', + 'import_zip_no_data' => 'Data berkas ZIP tidak berisi konten buku, bab, atau halaman yang diharapkan.', + 'import_validation_failed' => 'Impor ZIP gagal divalidasi dengan kesalahan:', + 'import_zip_failed_notification' => 'Gagal mengimpor berkas ZIP.', + 'import_perms_books' => 'Anda tidak memiliki izin yang diperlukan untuk membuat buku.', + 'import_perms_chapters' => 'Anda tidak memiliki izin yang diperlukan untuk membuat bab.', + 'import_perms_pages' => 'Anda tidak memiliki izin yang diperlukan untuk membuat halaman.', + 'import_perms_images' => 'Anda tidak memiliki izin yang diperlukan untuk membuat gambar.', + 'import_perms_attachments' => 'Anda tidak memiliki izin yang diperlukan untuk membuat lampiran.', + // API errors 'api_no_authorization_found' => 'Tidak ada token otorisasi yang ditemukan pada permintaan tersebut', 'api_bad_authorization_format' => 'Token otorisasi ditemukan pada permintaan tetapi formatnya salah', @@ -115,5 +129,5 @@ 'maintenance_test_email_failure' => 'Kesalahan dilempar saat mengirim email uji:', // HTTP errors - 'http_ssr_url_no_match' => 'The URL does not match the configured allowed SSR hosts', + 'http_ssr_url_no_match' => 'URL tidak cocok dengan host SSR yang diizinkan yang dikonfigurasi', ]; diff --git a/lang/id/notifications.php b/lang/id/notifications.php index 1afd23f1dc4..b22af1f730e 100644 --- a/lang/id/notifications.php +++ b/lang/id/notifications.php @@ -4,24 +4,24 @@ */ return [ - 'new_comment_subject' => 'New comment on page: :pageName', - 'new_comment_intro' => 'A user has commented on a page in :appName:', - 'new_page_subject' => 'New page: :pageName', - 'new_page_intro' => 'A new page has been created in :appName:', - 'updated_page_subject' => 'Updated page: :pageName', - 'updated_page_intro' => 'A page has been updated in :appName:', - 'updated_page_debounce' => 'To prevent a mass of notifications, for a while you won\'t be sent notifications for further edits to this page by the same editor.', + 'new_comment_subject' => 'Komentar baru di halaman: :pageName', + 'new_comment_intro' => 'Seorang pengguna telah mengomentari halaman di :appName:', + 'new_page_subject' => 'Halaman baru: :pageName', + 'new_page_intro' => 'Halaman baru telah dibuat di :appName:', + 'updated_page_subject' => 'Halaman yang diperbarui: :pageName', + 'updated_page_intro' => 'Halaman telah diperbarui di :appName:', + 'updated_page_debounce' => 'Untuk mencegah banyaknya pemberitahuan, untuk sementara Anda tidak akan dikirimi pemberitahuan untuk pengeditan lebih lanjut pada halaman ini oleh editor yang sama.', - 'detail_page_name' => 'Page Name:', - 'detail_page_path' => 'Page Path:', - 'detail_commenter' => 'Commenter:', - 'detail_comment' => 'Comment:', - 'detail_created_by' => 'Created By:', - 'detail_updated_by' => 'Updated By:', + 'detail_page_name' => 'Nama Halaman:', + 'detail_page_path' => 'Jalur Halaman:', + 'detail_commenter' => 'Komentator:', + 'detail_comment' => 'Komentar:', + 'detail_created_by' => 'Dibuat Oleh:', + 'detail_updated_by' => 'Diperbarui Oleh:', - 'action_view_comment' => 'View Comment', - 'action_view_page' => 'View Page', + 'action_view_comment' => 'Lihat Komentar', + 'action_view_page' => 'Lihat Halaman', - 'footer_reason' => 'This notification was sent to you because :link cover this type of activity for this item.', - 'footer_reason_link' => 'your notification preferences', + 'footer_reason' => 'Pemberitahuan ini dikirimkan kepada Anda karena :link mencakup jenis aktivitas untuk item ini.', + 'footer_reason_link' => 'preferensi notifikasi Anda', ]; diff --git a/lang/id/preferences.php b/lang/id/preferences.php index 2b88f9671ff..2872f5f3c65 100644 --- a/lang/id/preferences.php +++ b/lang/id/preferences.php @@ -27,7 +27,7 @@ 'notifications_save' => 'Save Preferences', 'notifications_update_success' => 'Notification preferences have been updated!', 'notifications_watched' => 'Watched & Ignored Items', - 'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.', + 'notifications_watched_desc' => 'Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.', 'auth' => 'Access & Security', 'auth_change_password' => 'Change Password', diff --git a/lang/id/settings.php b/lang/id/settings.php index 515d4bd9520..51c0613cbae 100644 --- a/lang/id/settings.php +++ b/lang/id/settings.php @@ -9,8 +9,8 @@ // Common Messages 'settings' => 'Pengaturan', 'settings_save' => 'Simpan Pengaturan', - 'system_version' => 'System Version', - 'categories' => 'Categories', + 'system_version' => 'Versi Sistem', + 'categories' => 'Kategori', // App Settings 'app_customization' => 'Kustomisasi', @@ -26,15 +26,15 @@ 'app_secure_images' => 'Unggahan Gambar Keamanan Lebih Tinggi', 'app_secure_images_toggle' => 'Aktifkan unggahan gambar dengan keamanan lebih tinggi', 'app_secure_images_desc' => 'Untuk alasan performa, semua gambar bersifat publik. Opsi ini menambahkan string acak yang sulit ditebak di depan url gambar. Pastikan indeks direktori tidak diaktifkan untuk mencegah akses mudah.', - 'app_default_editor' => 'Default Page Editor', - 'app_default_editor_desc' => 'Select which editor will be used by default when editing new pages. This can be overridden at a page level where permissions allow.', + 'app_default_editor' => 'Editor Halaman Default', + 'app_default_editor_desc' => 'Pilih editor yang akan digunakan secara default saat mengedit halaman baru. Pengaturan ini dapat diganti di tingkat halaman di mana itu diizinkan.', 'app_custom_html' => 'Kustom Konten HTML Head', 'app_custom_html_desc' => 'Konten apa pun yang ditambahkan di sini akan dimasukkan ke bagian bawah bagian dari setiap halaman. Ini berguna untuk mengganti gaya atau menambahkan kode analitik.', 'app_custom_html_disabled_notice' => 'Kustom konten HTML Head dinonaktifkan pada halaman pengaturan ini untuk memastikan setiap perubahan yang mengganggu dapat dikembalikan.', 'app_logo' => 'Logo Aplikasi', - 'app_logo_desc' => 'This is used in the application header bar, among other areas. This image should be 86px in height. Large images will be scaled down.', - 'app_icon' => 'Application Icon', - 'app_icon_desc' => 'This icon is used for browser tabs and shortcut icons. This should be a 256px square PNG image.', + 'app_logo_desc' => 'Ini digunakan di bilah tajuk aplikasi, di antara area lainnya. Gambar ini harus memiliki tinggi 86px. Gambar besar akan diperkecil.', + 'app_icon' => 'Ikon Aplikasi', + 'app_icon_desc' => 'Ikon digunakan pada tab browser dan ikon-ikon pintasan. Berkas harus berupa gambar IMG persegi dengan ukuran 256px.', 'app_homepage' => 'Beranda Aplikasi', 'app_homepage_desc' => 'Pilih tampilan untuk ditampilkan di beranda alih-alih tampilan default. Izin halaman diabaikan untuk halaman yang dipilih.', 'app_homepage_select' => 'Pilih halaman', @@ -48,12 +48,12 @@ 'app_disable_comments_desc' => 'Menonaktifkan komentar di semua halaman dalam aplikasi.
Komentar yang ada tidak ditampilkan.', // Color settings - 'color_scheme' => 'Application Color Scheme', - 'color_scheme_desc' => 'Set the colors to use in the application user interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.', - 'ui_colors_desc' => 'Set the application primary color and default link color. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the application interface.', - 'app_color' => 'Primary Color', - 'link_color' => 'Default Link Color', - 'content_colors_desc' => 'Set colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.', + 'color_scheme' => 'Skema Warna Aplikasi', + 'color_scheme_desc' => 'Atur warna-warna untuk digunakan dalam antar muka aplikasi. Warna-warna dapat diatur secara terpisah masing-masing untuk mode gelap dan terang agar sesuai dengan tema dan memastikan keterbacaan.', + 'ui_colors_desc' => 'Atur warna primer aplikasi dan warna tautan default. Warna primer terutama digunakan untuk bilah tajuk, tombol, dan dekorasi antarmuka. Warna tautan default digunakan untuk tautan dan tindakan berbasis teks, baik dalam konten tertulis maupun dalam antarmuka aplikasi.', + 'app_color' => 'Warna Utama', + 'link_color' => 'Warna Tautan Default', + 'content_colors_desc' => 'Tetapkan warna untuk semua elemen dalam hierarki organisasi halaman. Sebaiknya pilih warna dengan tingkat kecerahan yang mirip dengan warna default agar mudah dibaca.', 'bookshelf_color' => 'Warna Rak', 'book_color' => 'Warna Buku', 'chapter_color' => 'Warna Bab', @@ -74,6 +74,36 @@ 'reg_confirm_restrict_domain_desc' => 'Masukkan daftar domain email yang dipisahkan dengan koma yang ingin Anda batasi pendaftarannya. Pengguna akan dikirimi email untuk mengonfirmasi alamat mereka sebelum diizinkan untuk berinteraksi dengan aplikasi.
Perhatikan bahwa pengguna akan dapat mengubah alamat email mereka setelah pendaftaran berhasil.', 'reg_confirm_restrict_domain_placeholder' => 'Tidak ada batasan yang ditetapkan', + // Sorting Settings + 'sorting' => 'Menyortir', + 'sorting_book_default' => 'Penyortiran Buku Default', + 'sorting_book_default_desc' => 'Pilih aturan sortir default yang akan diterapkan pada buku baru. Aturan ini tidak akan memengaruhi buku yang sudah ada, dan dapat diganti per buku.', + 'sorting_rules' => 'Aturan Penyortiran', + 'sorting_rules_desc' => 'Ini adalah operasi penyortiran yang telah ditetapkan sebelumnya yang dapat diterapkan pada konten dalam sistem.', + 'sort_rule_assigned_to_x_books' => 'Dikaitkan ke :count Buku|Dikaitkan ke :count Buku', + 'sort_rule_create' => 'Buat Aturan Penyortiran', + 'sort_rule_edit' => 'Mengedit Aturan Penyortiran', + 'sort_rule_delete' => 'Hapus Aturan Penyortiran', + 'sort_rule_delete_desc' => 'Hapus aturan sortir ini dari sistem. Buku yang menggunakan sortir ini akan kembali ke sortir manual.', + 'sort_rule_delete_warn_books' => 'Aturan sortir ini saat ini digunakan pada :count buku (banyak buku). Apakah Anda yakin ingin menghapus ini?', + 'sort_rule_delete_warn_default' => 'Aturan sortir ini saat ini digunakan sebagai aturan baku untuk buku. Apakah Anda yakin ingin menghapusnya?', + 'sort_rule_details' => 'Perincian Aturan Penyortiran', + 'sort_rule_details_desc' => 'Tetapkan nama untuk aturan pengurutan ini, yang akan muncul dalam daftar saat pengguna memilih pengurutan.', + 'sort_rule_operations' => 'Operasi Penyortiran', + 'sort_rule_operations_desc' => 'Konfigurasikan tindakan sortir yang akan dilakukan dengan memindahkannya dari daftar operasi yang tersedia. Setelah digunakan, operasi akan diterapkan secara berurutan, dari atas ke bawah. Setiap perubahan yang dibuat di sini akan diterapkan ke semua buku yang ditetapkan setelah disimpan.', + 'sort_rule_available_operations' => 'Operasi yang Tersedia', + 'sort_rule_available_operations_empty' => 'Tidak ada operasi yang tersisa', + 'sort_rule_configured_operations' => 'Operasi yang Sudah Dikonfigurasi', + 'sort_rule_configured_operations_empty' => 'Seret/tambahkan operasi dari daftar "Operasi yang Tersedia"', + 'sort_rule_op_asc' => '(Naik)', + 'sort_rule_op_desc' => '(Turun)', + 'sort_rule_op_name' => 'Nama - Alfabetis', + 'sort_rule_op_name_numeric' => 'Nama - Numerik', + 'sort_rule_op_created_date' => 'Tanggal Dibuat', + 'sort_rule_op_updated_date' => 'Tanggal Pembaruan', + 'sort_rule_op_chapters_first' => 'Bab di Urutan Pertama', + 'sort_rule_op_chapters_last' => 'Bab di Urutan Terakhir', + // Maintenance settings 'maint' => 'Pemeliharaan', 'maint_image_cleanup' => 'Gambar Bersihkan', @@ -92,10 +122,10 @@ 'maint_send_test_email_mail_text' => 'Selamat! Saat Anda menerima pemberitahuan email ini, pengaturan email Anda tampaknya telah dikonfigurasi dengan benar.', 'maint_recycle_bin_desc' => 'Rak, buku, bab & halaman yang dihapus dikirim ke recycle bin sehingga dapat dipulihkan atau dihapus secara permanen. Item lama di recycle bin dapat dihapus secara otomatis setelah beberapa saat tergantung pada konfigurasi sistem.', 'maint_recycle_bin_open' => 'Buka Tempat Sampah', - 'maint_regen_references' => 'Regenerate References', - 'maint_regen_references_desc' => 'This action will rebuild the cross-item reference index within the database. This is usually handled automatically but this action can be useful to index old content or content added via unofficial methods.', - 'maint_regen_references_success' => 'Reference index has been regenerated!', - 'maint_timeout_command_note' => 'Note: This action can take time to run, which can lead to timeout issues in some web environments. As an alternative, this action be performed using a terminal command.', + 'maint_regen_references' => 'Perbarui Referensi', + 'maint_regen_references_desc' => 'Tindakan ini akan membangun kembali indeks referensi lintas item dalam basis data. Hal ini biasanya ditangani secara otomatis, tetapi tindakan ini dapat berguna untuk mengindeks konten lama atau konten yang ditambahkan melalui metode tidak resmi.', + 'maint_regen_references_success' => 'Indeks referensi telah diperbarui!', + 'maint_timeout_command_note' => 'Catatan: Tindakan ini memerlukan waktu untuk dijalankan, yang dapat menyebabkan masalah batas waktu di beberapa lingkungan web. Sebagai alternatif, tindakan ini dapat dilakukan menggunakan perintah terminal.', // Recycle Bin 'recycle_bin' => 'Tempat Sampah', @@ -109,7 +139,7 @@ 'recycle_bin_contents_empty' => 'Hapus Secara Permanen', 'recycle_bin_empty' => 'Kosongkan Tempat Sampah', 'recycle_bin_empty_confirm' => 'Ini akan menghancurkan secara permanen semua item di tempat sampah termasuk konten yang ada di dalam setiap item. Anda yakin ingin mengosongkan tempat sampah?', - 'recycle_bin_destroy_confirm' => 'Tindakan ini akan menghapus item ini secara permanen, bersama dengan elemen turunan apa pun yang tercantum di bawah, dari sistem dan Anda tidak akan dapat memulihkan konten ini. Anda yakin ingin menghapus item ini secara permanen?', + 'recycle_bin_destroy_confirm' => 'Tindakan ini akan menghapus item ini secara permanen dari sistem, beserta semua elemen turunan yang tercantum di bawah ini, dan Anda tidak akan dapat memulihkan konten ini. Apakah Anda yakin ingin menghapus item ini secara permanen?', 'recycle_bin_destroy_list' => 'Item yang akan Dihancurkan', 'recycle_bin_restore_list' => 'Item yang akan Dipulihkan', 'recycle_bin_restore_confirm' => 'Tindakan ini akan memulihkan item yang dihapus, termasuk semua elemen anak, ke lokasi aslinya. Jika lokasi asli telah dihapus, dan sekarang berada di keranjang sampah, item induk juga perlu dipulihkan.', @@ -128,7 +158,7 @@ 'audit_table_user' => 'Pengguna', 'audit_table_event' => 'Peristiwa', 'audit_table_related' => 'Item atau Detail Terkait', - 'audit_table_ip' => 'IP Address', + 'audit_table_ip' => 'Alamat IP', 'audit_table_date' => 'Tanggal Kegiatan', 'audit_date_from' => 'Rentang Tanggal Dari', 'audit_date_to' => 'Rentang Tanggal Sampai', @@ -136,11 +166,11 @@ // Role Settings 'roles' => 'Peran', 'role_user_roles' => 'Peran Pengguna', - 'roles_index_desc' => 'Roles are used to group users & provide system permission to their members. When a user is a member of multiple roles the privileges granted will stack and the user will inherit all abilities.', - 'roles_x_users_assigned' => ':count user assigned|:count users assigned', - 'roles_x_permissions_provided' => ':count permission|:count permissions', - 'roles_assigned_users' => 'Assigned Users', - 'roles_permissions_provided' => 'Provided Permissions', + 'roles_index_desc' => 'Peran digunakan untuk mengelompokkan pengguna & memberikan izin sistem kepada anggotanya. Jika pengguna menjadi anggota beberapa peran, hak istimewa yang diberikan akan bertumpuk dan pengguna akan mewarisi semua kemampuan.', + 'roles_x_users_assigned' => ':count pengguna yang ditetapkan|:count pengguna-pengguna yang ditetapkan', + 'roles_x_permissions_provided' => ':count izin|:count izin-izin', + 'roles_assigned_users' => 'Pengguna yang Ditetapkan', + 'roles_permissions_provided' => 'Izin yang diberikan', 'role_create' => 'Buat Peran Baru', 'role_delete' => 'Hapus Peran', 'role_delete_confirm' => 'Ini akan menghapus peran dengan nama \':roleName\'.', @@ -151,7 +181,7 @@ 'role_details' => 'Detail Peran', 'role_name' => 'Nama peran', 'role_desc' => 'Deskripsi Singkat Peran', - 'role_mfa_enforced' => 'Requires Multi-Factor Authentication', + 'role_mfa_enforced' => 'Membutuhkan Otentikasi Multi Faktor', 'role_external_auth_id' => 'Otentikasi Eksternal IDs', 'role_system' => 'Izin Sistem', 'role_manage_users' => 'Kelola pengguna', @@ -161,9 +191,10 @@ 'role_manage_page_templates' => 'Kelola template halaman', 'role_access_api' => 'Akses Sistem API', 'role_manage_settings' => 'Kelola setelan aplikasi', - 'role_export_content' => 'Export content', - 'role_editor_change' => 'Change page editor', - 'role_notifications' => 'Receive & manage notifications', + 'role_export_content' => 'Ekspor konten', + 'role_import_content' => 'Impor Konten', + 'role_editor_change' => 'Ubah editor halaman', + 'role_notifications' => 'Terima dan kelola notifikasi', 'role_asset' => 'Izin Aset', 'roles_system_warning' => 'Ketahuilah bahwa akses ke salah satu dari tiga izin di atas dapat memungkinkan pengguna untuk mengubah hak mereka sendiri atau orang lain dalam sistem. Hanya tetapkan peran dengan izin ini untuk pengguna tepercaya.', 'role_asset_desc' => 'Izin ini mengontrol akses default ke aset dalam sistem. Izin pada Buku, Bab, dan Halaman akan menggantikan izin ini.', @@ -214,8 +245,8 @@ 'users_social_accounts_info' => 'Di sini Anda dapat menghubungkan akun Anda yang lain untuk login yang lebih cepat dan mudah. Memutuskan akun di sini tidak mencabut akses resmi sebelumnya. Cabut akses dari pengaturan profil Anda pada akun sosial yang terhubung.', 'users_social_connect' => 'Hubungkan Akun', 'users_social_disconnect' => 'Putuskan Sambungan Akun', - 'users_social_status_connected' => 'Connected', - 'users_social_status_disconnected' => 'Disconnected', + 'users_social_status_connected' => 'Terhubung', + 'users_social_status_disconnected' => 'Terputus', 'users_social_connected' => ':socialAccount akun berhasil dilampirkan ke profil Anda.', 'users_social_disconnected' => ':socialAccount akun berhasil diputuskan dari profil Anda.', 'users_api_tokens' => 'Token API', @@ -224,7 +255,7 @@ 'users_api_tokens_create' => 'Buat Token', 'users_api_tokens_expires' => 'Kedaluwarsa', 'users_api_tokens_docs' => 'Dokumentasi API', - 'users_mfa' => 'Multi-Factor Authentication', + 'users_mfa' => 'Otentikasi Multi Faktor', 'users_mfa_desc' => 'Setup multi-factor authentication as an extra layer of security for your user account.', 'users_mfa_x_methods' => ':count method configured|:count methods configured', 'users_mfa_configure' => 'Configure Methods', @@ -276,6 +307,14 @@ 'webhooks_last_errored' => 'Last Errored:', 'webhooks_last_error_message' => 'Last Error Message:', + // Licensing + 'licenses' => 'Licenses', + 'licenses_desc' => 'This page details license information for BookStack in addition to the projects & libraries that are used within BookStack. Many projects listed may only be used in a development context.', + 'licenses_bookstack' => 'Lisensi BookStack', + 'licenses_php' => 'Lisensi Pustaka PHP', + 'licenses_js' => 'Lisensi Pustaka JavaScript', + 'licenses_other' => 'Lisensi Lainnya', + 'license_details' => 'Perincian Lisensi', //! If editing translations files directly please ignore this in all //! languages apart from en. Content will be auto-copied from en. @@ -287,6 +326,7 @@ 'bs' => 'Bosanski', 'ca' => 'Katalan', 'cs' => 'Česky', + 'cy' => 'Cymraeg', 'da' => 'Dansk', 'de' => 'Deutsch (Sie)', 'de_informal' => 'Deutsch (Du)', @@ -308,6 +348,7 @@ 'lt' => 'Lietuvių Kalba', 'lv' => 'Latviešu Valoda', 'nb' => 'Norsk (Bokmål)', + 'ne' => 'नेपाली', 'nn' => 'Nynorsk', 'nl' => 'Nederlands', 'pl' => 'Polski', diff --git a/lang/id/validation.php b/lang/id/validation.php index e1fcbc7246c..d5ccb27093c 100644 --- a/lang/id/validation.php +++ b/lang/id/validation.php @@ -105,6 +105,11 @@ 'url' => ':attribute format tidak valid.', 'uploaded' => 'Berkas tidak dapat diunggah. Server mungkin tidak menerima berkas dengan ukuran ini.', + 'zip_file' => ':attribute perlu merujuk ke sebuah file yang terdapat di dalam arsip ZIP.', + 'zip_file_mime' => ':attribute seharusnya berupa file dengan tipe :validTypes, tapi yang Anda unggah bertipe :foundType.', + 'zip_model_expected' => 'Diharapkan sebuah objek data, namun yang ditemukan adalah \':type\'.', + 'zip_unique' => ':attribute harus bersifat unik untuk setiap jenis objek dalam file ZIP.', + // Custom validation lines 'custom' => [ 'password-confirm' => [ diff --git a/lang/is/activities.php b/lang/is/activities.php new file mode 100644 index 00000000000..e70aa2567f0 --- /dev/null +++ b/lang/is/activities.php @@ -0,0 +1,140 @@ + 'stofna síðu', + 'page_create_notification' => 'Síða stofnuð', + 'page_update' => 'nafnlaus síða', + 'page_update_notification' => 'Síða uppfærð', + 'page_delete' => 'síðu eytt', + 'page_delete_notification' => 'Tókst að eyða síðu', + 'page_restore' => 'endurvirkja síðu', + 'page_restore_notification' => 'Síða endurvirkjuð', + 'page_move' => 'síða færð', + 'page_move_notification' => 'Tókst að færa síðu', + + // Chapters + 'chapter_create' => 'kafli búinn til', + 'chapter_create_notification' => 'Tókst að búa til kafla', + 'chapter_update' => 'kafli uppfærður', + 'chapter_update_notification' => 'Tókst að uppfæra kafla', + 'chapter_delete' => 'eyddur kafli', + 'chapter_delete_notification' => 'Tókst að eyða kafla', + 'chapter_move' => 'færður kafli', + 'chapter_move_notification' => 'Tókst að færa kafla', + + // Books + 'book_create' => 'stofnuð bók', + 'book_create_notification' => 'Tókst að stofna bók', + 'book_create_from_chapter' => 'kafla breytt í bók', + 'book_create_from_chapter_notification' => 'Tókst að breyta kafla í bók', + 'book_update' => 'uppfærð bók', + 'book_update_notification' => 'Tókst að uppfæra bók', + 'book_delete' => 'eydd bók', + 'book_delete_notification' => 'Tókst að eyða bók', + 'book_sort' => 'flokkuð bók', + 'book_sort_notification' => 'Tókst að endurflokka bók', + + // Bookshelves + 'bookshelf_create' => 'stofna hillu', + 'bookshelf_create_notification' => 'Tókst að stofna hillu', + 'bookshelf_create_from_book' => 'bók breytt i hillu', + 'bookshelf_create_from_book_notification' => 'Tókst að breyta bók í hillu', + 'bookshelf_update' => 'uppærð hilla', + 'bookshelf_update_notification' => 'Tókst að uppfæra hillu', + 'bookshelf_delete' => 'eydd hilla', + 'bookshelf_delete_notification' => 'Tókst að eyða hillu', + + // Revisions + 'revision_restore' => 'útgáfa bakfærð', + 'revision_delete' => 'útgáfu eytt', + 'revision_delete_notification' => 'Tókst að eyða útgáfu', + + // Favourites + 'favourite_add_notification' => 'hefur verið bætt í eftirlæti', + 'favourite_remove_notification' => 'hefur verið eytt úr eftirlæti', + + // Watching + 'watch_update_level_notification' => 'Fylgjast með hefur verið uppfært', + + // Auth + 'auth_login' => 'skráður inn', + 'auth_register' => 'skráður sem nýr notandi', + 'auth_password_reset_request' => 'bað um nýtt lykilorð', + 'auth_password_reset_update' => 'endurstilla lykilorð', + 'mfa_setup_method' => 'valin MFA aðferð', + 'mfa_setup_method_notification' => 'Fjölauðkenningar aðferð stillt', + 'mfa_remove_method' => 'fjarlægja MFA aðferð', + 'mfa_remove_method_notification' => 'Fjölauðkenningar aðferð fjarlægð', + + // Settings + 'settings_update' => 'uppfæra stillingar', + 'settings_update_notification' => 'Tókst að uppfæra stillingar', + 'maintenance_action_run' => 'keyrði uppfærslu', + + // Webhooks + 'webhook_create' => 'webhook búin til', + 'webhook_create_notification' => 'Tókst að búa til Webhook', + 'webhook_update' => 'webhook uppfærður', + 'webhook_update_notification' => 'Tókst að uppfæra Webhook', + 'webhook_delete' => 'eyða Webhook', + 'webhook_delete_notification' => 'Tókst að eyða Webhook', + + // Imports + 'import_create' => 'búa til innlestur', + 'import_create_notification' => 'Innlestur tókst', + 'import_run' => 'uppfæra innlestur', + 'import_run_notification' => 'Tókst að lesa inn', + 'import_delete' => 'innlestri eytt', + 'import_delete_notification' => 'Tókst að eyða innlestri', + + // Users + 'user_create' => 'stofnaður notandi', + 'user_create_notification' => 'Tókst að stofna notanda', + 'user_update' => 'uppfærður notandi', + 'user_update_notification' => 'Tókst að uppfæra notanda', + 'user_delete' => 'eyddur notandi', + 'user_delete_notification' => 'Tókst að eyða notanda', + + // API Tokens + 'api_token_create' => 'API token búið til', + 'api_token_create_notification' => 'Tókst að búa til API tóka', + 'api_token_update' => 'API tóki uppfærður', + 'api_token_update_notification' => 'Tókst að uppfæra API tóka', + 'api_token_delete' => 'eyddur API tóki', + 'api_token_delete_notification' => 'Tókst að eyða API tóka', + + // Roles + 'role_create' => 'stofnað hlutverk', + 'role_create_notification' => 'Tókst að stofna hlutverk', + 'role_update' => 'hlutverk uppfært', + 'role_update_notification' => 'Tókst að uppfæra hlutverk', + 'role_delete' => 'eytt hlutverk', + 'role_delete_notification' => 'Tókst að eyða hlutverki', + + // Recycle Bin + 'recycle_bin_empty' => 'tæmd ruslatunna', + 'recycle_bin_restore' => 'endurheimt úr ruslatunnu', + 'recycle_bin_destroy' => 'fjarlægt úr ruslatunnu', + + // Comments + 'commented_on' => 'athugasemd á', + 'comment_create' => 'athugasemd bætt við', + 'comment_update' => 'athugasemd uppfærð', + 'comment_delete' => 'athugasemd eytt', + + // Sort Rules + 'sort_rule_create' => 'created sort rule', + 'sort_rule_create_notification' => 'Sort rule successfully created', + 'sort_rule_update' => 'updated sort rule', + 'sort_rule_update_notification' => 'Sort rule successfully updated', + 'sort_rule_delete' => 'deleted sort rule', + 'sort_rule_delete_notification' => 'Sort rule successfully deleted', + + // Other + 'permissions_update' => 'uppfærðar heimildir', +]; diff --git a/lang/is/auth.php b/lang/is/auth.php new file mode 100644 index 00000000000..c5e7ce4f1d7 --- /dev/null +++ b/lang/is/auth.php @@ -0,0 +1,117 @@ + 'Þeesi auðkenning er ekki á skrá.', + 'throttle' => 'Of margar tilraunir til innskráningar. Reyndu aftur eftir :seconds sekúndur.', + + // Login & Register + 'sign_up' => 'Nýskrá', + 'log_in' => 'Innskrá', + 'log_in_with' => 'Innskrá með :socialDriver', + 'sign_up_with' => 'Búa til aðgang með :socialDriver', + 'logout' => 'Skrá út', + + 'name' => 'Nafn', + 'username' => 'Notandanafn', + 'email' => 'Netfang', + 'password' => 'Lykilorð', + 'password_confirm' => 'Staðfestu lykilorð', + 'password_hint' => 'Verður að vera minnst 8 stafir', + 'forgot_password' => 'Gleymt lykilorð?', + 'remember_me' => 'Geyma innskráningarupplýsingar', + 'ldap_email_hint' => 'Settu inn netfang til að nota þennan aðgang.', + 'create_account' => 'Stofna aðgang', + 'already_have_account' => 'Þegar með notandaaðgang?', + 'dont_have_account' => 'Ekki með aðgang?', + 'social_login' => 'Innskráning með samfélagsmiðli', + 'social_registration' => 'Skráning samfélagsmiðils', + 'social_registration_text' => 'Skráðu þig og innskrá með annari þjónustu.', + + 'register_thanks' => 'Takk fyrir að skrá þig!', + 'register_confirm' => 'Skoðaðu tölvupóstinn þinn og smelltu á staðfestingarhlekkinn :appName.', + 'registrations_disabled' => 'Skráningar eru óvirkar í augnablikinu', + 'registration_email_domain_invalid' => 'Þetta lén hefur ekki aðgang að þessu forriti', + 'register_success' => 'Takk fyrir að skrá þig, nú ertu innskráðursem notandi.', + + // Login auto-initiation + 'auto_init_starting' => 'Reyni innskráningu', + 'auto_init_starting_desc' => 'Reyni að tengjast auðkenningarþjónustu, ef ekkert gerist innan 5 sekúndna getur þú smellt á hlekkinn hér að neðan.', + 'auto_init_start_link' => 'Halda áfram með auðkenningu', + + // Password Reset + 'reset_password' => 'Endurstilla lykilorð', + 'reset_password_send_instructions' => 'Settu netfangið þitt hér að neðan og þú færð tölvupóst með endurstillingar hlekk.', + 'reset_password_send_button' => 'Senda hlekk', + 'reset_password_sent' => 'Endurstillingar hlekkur hefur verið sendur í tölvupósti :email ef netfangið er á skrá.', + 'reset_password_success' => 'Lykilorðið þitt hefur verið endurstillt.', + 'email_reset_subject' => 'Endurstilla :appName lykilorðið þitt', + 'email_reset_text' => 'Þú fékkst þennan tölvupóst því að beðið var um endurstillingu lykilorðs á þínum aðgangi.', + 'email_reset_not_requested' => 'Ef þú baðst ekki um endurstillingu lykilorðs þarftu ekki að gera neitt.', + + // Email Confirmation + 'email_confirm_subject' => 'Staðfestu netfangið þitt á :appName', + 'email_confirm_greeting' => 'Takk fyrir að skrá þig á :appName!', + 'email_confirm_text' => 'Vinsamlegast staðfestu netfangið þitt með því að smella á hnappin hér fyrir neðan:', + 'email_confirm_action' => 'Staðfesta netfang', + 'email_confirm_send_error' => 'Staðfesting netfangs er nauðsynleg en kerfið gat ekki sent póst, vinsamlegast hafið samband við kerfisstjóra.', + 'email_confirm_success' => 'Netfang þitt hefur verið staðfest, þú ættir nú að geta skráð þig inn með þessu netfangi.', + 'email_confirm_resent' => 'Staðfestingar tölvupóstur hefur verið sendur, kíktu í póshólfið þitt.', + 'email_confirm_thanks' => 'Takk fyrir að staðfesta!', + 'email_confirm_thanks_desc' => 'Hinkraðu smá á meðan staðfestingin þín er í vinnslu, ef ekkert gerist eftir 3 sekúndur, smelltu á "Halda áfram" hlekkinn hér fyrir neðan.', + + 'email_not_confirmed' => 'Netfang hefur ekki verið staðfest', + 'email_not_confirmed_text' => 'Netfangið þitt hefur ekki enn verið staðfest.', + 'email_not_confirmed_click_link' => 'Vinsamlegast smelltu á hlekkinn sem barst þér í tölvupósti eftir skráningu.', + 'email_not_confirmed_resend' => 'Ef þú finnur ekki tölvupóstinn sem var sendur á þig, getur þú fengið hann endursendann með því að fylla út formið hér að neðan.', + 'email_not_confirmed_resend_button' => 'Endursenda staðfestingarpóst', + + // User Invite + 'user_invite_email_subject' => 'Þér hefur verið boðið að tengjast :appName!', + 'user_invite_email_greeting' => 'Það hefur verið stofnaður aðgangur fyrir ig á :appName.', + 'user_invite_email_text' => 'Smelltu á hnappinn fyrir neðan til að setja upp lykilorð og fá aðgang:', + 'user_invite_email_action' => 'Settu inn lykilorð', + 'user_invite_page_welcome' => 'Velkominn á :appName!', + 'user_invite_page_text' => 'Til að ljúka við uppsetningu og fá aðgang að :appName verður þú að velja þér lykilorð.', + 'user_invite_page_confirm_button' => 'Staðfestu lykilorð', + 'user_invite_success_login' => 'Lykilorð klárt, nú ættir þú að geta skráð þig inn á :appName!', + + // Multi-factor Authentication + 'mfa_setup' => 'Setja upp tvöfalda auðkenningu', + 'mfa_setup_desc' => 'Tvöföld euðkenning er viðbótar vörn til að tryggja aðganginn þinn.', + 'mfa_setup_configured' => 'Þegar uppsett', + 'mfa_setup_reconfigure' => 'Endurstilla', + 'mfa_setup_remove_confirmation' => 'Ertu viss um að þú viljið fjarlæga þessa auðkenningarleið?', + 'mfa_setup_action' => 'Uppsetning', + 'mfa_backup_codes_usage_limit_warning' => 'Þú átt færri en 5 tilraunir eftir. Búðu til og geymdu hjá þér fleiri tilraunir svo þú læsist ekki úti.', + 'mfa_option_totp_title' => 'App', + 'mfa_option_totp_desc' => 'Til að virkja tvöfalda auðkenningu verður þú að hafa app í símanum sem styður TOPT, til dæmis Google Authenticator, Authy eða Microsoft Authenticator.', + 'mfa_option_backup_codes_title' => 'Varakóðar', + 'mfa_option_backup_codes_desc' => 'Býr til sett af einskiptis kóðum sem þú getur notað til að auðkenna þig með. Geymdu þessa kóða á öruggum stað.', + 'mfa_gen_confirm_and_enable' => 'Staðfesta og virkja', + 'mfa_gen_backup_codes_title' => 'Stillingar varakóða', + 'mfa_gen_backup_codes_desc' => 'Geymdu listann af kóðum á öruggum stað. Þú getur notað þessa kóða sem auka auðkenningu.', + 'mfa_gen_backup_codes_download' => 'Hala niður kóðum', + 'mfa_gen_backup_codes_usage_warning' => 'Hver kóði getur bara verið notaður einu sinni', + 'mfa_gen_totp_title' => 'Uppsetning Apps', + 'mfa_gen_totp_desc' => 'Til að virkja tvöfalda auðkenningu verður þú að hafa app í símanum sem styður TOPT, til dæmis Google Authenticator, Authy eða Microsoft Authenticator.', + 'mfa_gen_totp_scan' => 'Skannaðu QR kóðann með appinu sem þú notar fyrir tvöfalda auðkenningu.', + 'mfa_gen_totp_verify_setup' => 'Staðfesta uppsetningu', + 'mfa_gen_totp_verify_setup_desc' => 'Staðfestu að allt virki með því að setja inn kóða úr síma appinu þínu hér fyrir neðan:', + 'mfa_gen_totp_provide_code_here' => 'Sláðu inn kóða úr auðkennningar appi', + 'mfa_verify_access' => 'Staðfesta aðgang', + 'mfa_verify_access_desc' => 'Aðgangurinn þinn þarf viðbótar auðkenningu, veldu auðkenningarleið.', + 'mfa_verify_no_methods' => 'Engar aðferðir stilltar', + 'mfa_verify_no_methods_desc' => 'Engin aukaauðkenningar aðferð fannst. Þú verður að setja upp minnst eina viðbótarauðkenningu til að halda áfram.', + 'mfa_verify_use_totp' => 'Staðfestu með farsíma appi', + 'mfa_verify_use_backup_codes' => 'Staðfesta með varakóða', + 'mfa_verify_backup_code' => 'Varakóði', + 'mfa_verify_backup_code_desc' => 'Settu inn einn af varakóðunum þínum hér að neðan:', + 'mfa_verify_backup_code_enter_here' => 'Sláðu inn varakóða hér', + 'mfa_verify_totp_desc' => 'Sláðu inn kóðann úr auðkenningar appinu úr símanum þínum:', + 'mfa_setup_login_notification' => 'Tvöföld auðkenning stillt. Skráðu þig nú inn með euðkenningarleiðinni.', +]; diff --git a/lang/is/common.php b/lang/is/common.php new file mode 100644 index 00000000000..84651be1736 --- /dev/null +++ b/lang/is/common.php @@ -0,0 +1,115 @@ + 'Hætta við', + 'close' => 'Loka', + 'confirm' => 'Staðfesta', + 'back' => 'Til baka', + 'save' => 'Vista', + 'continue' => 'Halda áfram', + 'select' => 'Velja', + 'toggle_all' => 'Velja allt', + 'more' => 'Meira', + + // Form Labels + 'name' => 'Nafn', + 'description' => 'Lýsing', + 'role' => 'Hlutverk', + 'cover_image' => 'Forsíðumynd', + 'cover_image_description' => 'Myndin ætti að fera u. þ. b 440x250px þótt hún verði sköluð og kroppuð eftir þörfum, þannig að endanleg stærð mun endurspegla það.', + + // Actions + 'actions' => 'Aðgerðir', + 'view' => 'Skoða', + 'view_all' => 'Skoða allt', + 'new' => 'Ný', + 'create' => 'Búa til', + 'update' => 'Uppfæra', + 'edit' => 'Breyta', + 'archive' => 'Archive', + 'unarchive' => 'Un-Archive', + 'sort' => 'Flokka', + 'move' => 'Færa', + 'copy' => 'Afrita', + 'reply' => 'Svara', + 'delete' => 'Eyða', + 'delete_confirm' => 'Staðfesta eyðingu', + 'search' => 'Leita', + 'search_clear' => 'Hreinsa leit', + 'reset' => 'Endurstilla', + 'remove' => 'Fjarlægja', + 'add' => 'Bæta við', + 'configure' => 'Stilla', + 'manage' => 'Stýra', + 'fullscreen' => 'Fylla skjá', + 'favourite' => 'Eftirlæti', + 'unfavourite' => 'Fjarlægja úr eftirlæti', + 'next' => 'Næst', + 'previous' => 'Fyrri', + 'filter_active' => 'Virk sía:', + 'filter_clear' => 'Hreinsa síu', + 'download' => 'Hlaða niður', + 'open_in_tab' => 'Opna í flipa', + 'open' => 'Opna', + + // Sort Options + 'sort_options' => 'Valkostir röðunar', + 'sort_direction_toggle' => 'Flokkunarátt', + 'sort_ascending' => 'Raða vaxandi', + 'sort_descending' => 'Raða minnkandi', + 'sort_name' => 'Nafn', + 'sort_default' => 'Sjálfgefið', + 'sort_created_at' => 'Stofnað þann', + 'sort_updated_at' => 'Uppfært þann', + + // Misc + 'deleted_user' => 'Eyddur notandi', + 'no_activity' => 'Engin virkni til að sýna', + 'no_items' => 'Engir hlutir tiltækir', + 'back_to_top' => 'Fara efst', + 'skip_to_main_content' => 'Fara í aðalefni', + 'toggle_details' => 'Virkja nánari sýn', + 'toggle_thumbnails' => 'Sýna smámynd', + 'details' => 'Nánari upplýsingar', + 'grid_view' => 'Grid View', + 'list_view' => 'Lista sýn', + 'default' => 'Sjálfgefið', + 'breadcrumb' => 'Brauðmolar', + 'status' => 'Staða', + 'status_active' => 'Virkt', + 'status_inactive' => 'Óvirkt', + 'never' => 'Aldrei', + 'none' => 'Engin', + + // Header + 'homepage' => 'Forsíða', + 'header_menu_expand' => 'Leiðarstjórn', + 'profile_menu' => 'Prófíll', + 'view_profile' => 'Skoða prófíl', + 'edit_profile' => 'Breyta prófíl', + 'dark_mode' => 'Dimmsnið', + 'light_mode' => 'Ljóssnið', + 'global_search' => 'Heildarleit', + + // Layout tabs + 'tab_info' => 'Upplýsingar', + 'tab_info_label' => 'Tab: Sýna fleiri upplýsingar', + 'tab_content' => 'Innihald', + 'tab_content_label' => 'Tab: Sýna aðalinnihald', + + // Email Content + 'email_action_help' => 'Ef þú átt í vandræðum með að smella á ":actionText" hnappinn, afritaðu og límdu slóðina í vefskoðarann þinn:', + 'email_rights' => 'Höfundaréttur varinn', + + // Footer Link Options + // Not directly used but available for convenience to users. + 'privacy_policy' => 'Persónuverndarstefna', + 'terms_of_service' => 'Skilmálar þjónustu', + + // OpenSearch + 'opensearch_description' => 'Leita :appName', +]; diff --git a/lang/is/components.php b/lang/is/components.php new file mode 100644 index 00000000000..6e0527eeede --- /dev/null +++ b/lang/is/components.php @@ -0,0 +1,46 @@ + 'Myndaval', + 'image_list' => 'Myndalisti', + 'image_details' => 'Uplýsingar myndar', + 'image_upload' => 'Hlaða upp mynd', + 'image_intro' => 'Hér getur þú valið og stjórnað þeim myndum sem þegar hefur verið upphlaðið.', + 'image_intro_upload' => 'Hladdu upp nýrri mynd með því að draga hana inn í þennan glugga eða nota "Hlaða upp" hnappinn hér fyrir ofan.', + 'image_all' => 'Allar', + 'image_all_title' => 'Skoða allar myndir', + 'image_book_title' => 'Skoða þær myndir sem þegar hefur verið hlaðið upp í þessa bók', + 'image_page_title' => 'Skoða þær myndir sem þegar hefur verið hlaðið upp á þessa síðu', + 'image_search_hint' => 'Leita af myndum eftir nafni', + 'image_uploaded' => 'Hlaðið upp þann :uploadedDate', + 'image_uploaded_by' => 'Hlaðið upp af :userName', + 'image_uploaded_to' => 'Hlaðið upp á :pageLink', + 'image_updated' => 'Uppfært þann :updateDate', + 'image_load_more' => 'Hlaða fleirum', + 'image_image_name' => 'Nafn myndar', + 'image_delete_used' => 'Þessi mynd er notuð á eftirfarandi síðum.', + 'image_delete_confirm_text' => 'Ertu viss um að þú viljir eyða þessari mynd?', + 'image_select_image' => 'Velja mynd', + 'image_dropzone' => 'Dragðu myndir eða smelltu hér til að hlaða upp', + 'image_dropzone_drop' => 'Dragðu myndir hingað til að hlaða upp', + 'images_deleted' => 'Myndum eytt', + 'image_preview' => 'Forskoðun mynda', + 'image_upload_success' => 'Upphal myndar tókst', + 'image_update_success' => 'Upplýsingar um mynd uppfærðar', + 'image_delete_success' => 'Tókst að eyða mynd', + 'image_replace' => 'Skipta um mynd', + 'image_replace_success' => 'Tókst að skipta um skrá', + 'image_rebuild_thumbs' => 'Endurgera stærðastillingar', + 'image_rebuild_thumbs_success' => 'Tókst að endurgera stærðarstillingar!', + + // Code Editor + 'code_editor' => 'Breyta kóða', + 'code_language' => 'Tungumál kóða', + 'code_content' => 'Innihald kóða', + 'code_session_history' => 'Saga lotu', + 'code_save' => 'Vista kóða', +]; diff --git a/lang/is/editor.php b/lang/is/editor.php new file mode 100644 index 00000000000..2c4a24616ad --- /dev/null +++ b/lang/is/editor.php @@ -0,0 +1,182 @@ + 'Almennt', + 'advanced' => 'Ítarlegt', + 'none' => 'Engin', + 'cancel' => 'Hætta við', + 'save' => 'Vista', + 'close' => 'Loka', + 'apply' => 'Apply', + 'undo' => 'Afturkalla', + 'redo' => 'Endurgera', + 'left' => 'Vinstri', + 'center' => 'Miðja', + 'right' => 'Hægri', + 'top' => 'Efst', + 'middle' => 'Miðja', + 'bottom' => 'Neðst', + 'width' => 'Breidd', + 'height' => 'Hæð', + 'More' => 'Meira', + 'select' => 'Velja...', + + // Toolbar + 'formats' => 'Snið', + 'header_large' => 'Stór fyrirsögn', + 'header_medium' => 'Miðlungs fyrirsögn', + 'header_small' => 'Lítil fyrirsögn', + 'header_tiny' => 'Örsmá fyrirsögn', + 'paragraph' => 'Málsgrein', + 'blockquote' => 'Gæsalappir', + 'inline_code' => 'Inline code', + 'callouts' => 'Vitna í', + 'callout_information' => 'Upplýsingar', + 'callout_success' => 'Árangur', + 'callout_warning' => 'Aðvörun', + 'callout_danger' => 'Hætta', + 'bold' => 'Feitletrað', + 'italic' => 'Skáletrað', + 'underline' => 'Undirstrikað', + 'strikethrough' => 'Yfirstrikað', + 'superscript' => 'Háletur', + 'subscript' => 'Lágletur', + 'text_color' => 'Litur texta', + 'highlight_color' => 'Highlight color', + 'custom_color' => 'Sérsniðinn litur', + 'remove_color' => 'Fjarlægja lit', + 'background_color' => 'Bakgrunnslitur', + 'align_left' => 'Jafna til vinstri', + 'align_center' => 'Miðju jafna', + 'align_right' => 'Hægrijafna', + 'align_justify' => 'Jafna', + 'list_bullet' => 'Punkta listi', + 'list_numbered' => 'Númeraður listi', + 'list_task' => 'Aðgerðar listi', + 'indent_increase' => 'Auka inndrátt', + 'indent_decrease' => 'Minnka inndrátt', + 'table' => 'Tafla', + 'insert_image' => 'Setja inn mynd', + 'insert_image_title' => 'Setja inn eða breyta mynd', + 'insert_link' => 'Setja inn eða breyta hlekk', + 'insert_link_title' => 'Setja inn eða breyta hlekk', + 'insert_horizontal_line' => 'Setja inn lárétta línu', + 'insert_code_block' => 'Setja inn kóðastubb', + 'edit_code_block' => 'Breyta kóðastubb', + 'insert_drawing' => 'Setja inn eða breyta teikningu', + 'drawing_manager' => 'Teikningastjóri', + 'insert_media' => 'Setja inn eða breyta miðlum', + 'insert_media_title' => 'Setja inn eða breyta miðlum', + 'clear_formatting' => 'Hreinsa forsnið', + 'source_code' => 'Frumkóði', + 'source_code_title' => 'Frumkóði', + 'fullscreen' => 'Fullann skjá', + 'image_options' => 'Myndastillingar', + + // Tables + 'table_properties' => 'Eiginleikar töflu', + 'table_properties_title' => 'Eiginleikar töflu', + 'delete_table' => 'Eyða töflu', + 'table_clear_formatting' => 'Hreinsa forsnið töflu', + 'resize_to_contents' => 'Endurstilla stærð innihalds', + 'row_header' => 'Titill raðar', + 'insert_row_before' => 'Líma röð á undan', + 'insert_row_after' => 'Líma röð á eftir', + 'delete_row' => 'Eyða röð', + 'insert_column_before' => 'Líma dálk á undan', + 'insert_column_after' => 'Líma dálk á eftir', + 'delete_column' => 'Eyða dálki', + 'table_cell' => 'Reitur', + 'table_row' => 'Röð', + 'table_column' => 'Dálkur', + 'cell_properties' => 'Eigindi reitar', + 'cell_properties_title' => 'Eigindi reitar', + 'cell_type' => 'Gerð reitar', + 'cell_type_cell' => 'Reitur', + 'cell_scope' => 'Svið', + 'cell_type_header' => 'For reitur', + 'merge_cells' => 'Sameina reiti', + 'split_cell' => 'Kljúfa reiti', + 'table_row_group' => 'Hópur raðar', + 'table_column_group' => 'Hópur dálks', + 'horizontal_align' => 'Jafna lárétt', + 'vertical_align' => 'Jafna lóðrétt', + 'border_width' => 'Border width', + 'border_style' => 'Útlit jaðars', + 'border_color' => 'Litur jaðars', + 'row_properties' => 'Eigindi raðar', + 'row_properties_title' => 'Eigindi raðar', + 'cut_row' => 'Klippa röð', + 'copy_row' => 'Afrita röð', + 'paste_row_before' => 'Líma röð á undan', + 'paste_row_after' => 'Líma röð á eftir', + 'row_type' => 'Gerð raðar', + 'row_type_header' => 'Síðuhaus', + 'row_type_body' => 'Meginmál', + 'row_type_footer' => 'Neðanmál', + 'alignment' => 'Jöfnun', + 'cut_column' => 'Klippa dálk', + 'copy_column' => 'Afrita dálk', + 'paste_column_before' => 'Líma dálk á undan', + 'paste_column_after' => 'Líma dálk á eftir', + 'cell_padding' => 'Cell padding', + 'cell_spacing' => 'Cell spacing', + 'caption' => 'Fyrirsögn', + 'show_caption' => 'Sýna fyrirsögn', + 'constrain' => 'Constrain proportions', + 'cell_border_solid' => 'Fyllt', + 'cell_border_dotted' => 'Punkta', + 'cell_border_dashed' => 'Strikað', + 'cell_border_double' => 'Tvöfalt', + 'cell_border_groove' => 'Groove', + 'cell_border_ridge' => 'Ridge', + 'cell_border_inset' => 'Inset', + 'cell_border_outset' => 'Outset', + 'cell_border_none' => 'Engin', + 'cell_border_hidden' => 'Falin', + + // Images, links, details/summary & embed + 'source' => 'Uppruni', + 'alt_desc' => 'Auka lýsing', + 'embed' => 'Innfellt', + 'paste_embed' => 'Límdu innfellda kóðann þinn að neðan:', + 'url' => 'Vistfang', + 'text_to_display' => 'Teksti til að sýna', + 'title' => 'Titill', + 'browse_links' => 'Browse links', + 'open_link' => 'Opna hlekk', + 'open_link_in' => 'Opna hlekk í...', + 'open_link_current' => 'Virkum glugga', + 'open_link_new' => 'Nýjum glugga', + 'remove_link' => 'Fjarlægja hlekk', + 'insert_collapsible' => 'Insert collapsible block', + 'collapsible_unwrap' => 'Unwrap', + 'edit_label' => 'Breyta miða', + 'toggle_open_closed' => 'Velja opið/lokað', + 'collapsible_edit' => 'Edit collapsible block', + 'toggle_label' => 'Sýna miða', + + // About view + 'about' => 'Um ritilinn', + 'about_title' => 'Um WYSIWYG ritilinn', + 'editor_license' => 'Leyfi og höfundaréttur ritilsins', + 'editor_lexical_license' => 'This editor is built as a fork of :lexicalLink which is distributed under the MIT license.', + 'editor_lexical_license_link' => 'Full license details can be found here.', + 'editor_tiny_license' => 'Þessi ritill er smíðaður með :tinyLink sem er undir MIT leyfi.', + 'editor_tiny_license_link' => 'Höfundarétt og leyfi TinyMCE má finna hér.', + 'save_continue' => 'Vista síðu og halda áfram', + 'callouts_cycle' => '(Keep pressing to toggle through types)', + 'link_selector' => 'Hlekkur á innihald', + 'shortcuts' => 'Flýtileiðir', + 'shortcut' => 'Flýtileið', + 'shortcuts_intro' => 'Eftirtaldar flýtileiðir eru aðgengilegar í ritlinum:', + 'windows_linux' => '(Windows/Linux)', + 'mac' => '(Mac)', + 'description' => 'Lýsing', +]; diff --git a/lang/is/entities.php b/lang/is/entities.php new file mode 100644 index 00000000000..4308835c7d8 --- /dev/null +++ b/lang/is/entities.php @@ -0,0 +1,476 @@ + 'Nýlega búið til', + 'recently_created_pages' => 'Nýlega stofnaðar síður', + 'recently_updated_pages' => 'Nýlega uppfærðar síður', + 'recently_created_chapters' => 'Nýlega stofnaðir kaflar', + 'recently_created_books' => 'Nýlega stofnaðar bækur', + 'recently_created_shelves' => 'Nýlega stofnaðar hillur', + 'recently_update' => 'Nýlega uppfært', + 'recently_viewed' => 'Nýlega skoðað', + 'recent_activity' => 'Nýleg virkni', + 'create_now' => 'Búðu til eina núna', + 'revisions' => 'Útgáfur', + 'meta_revision' => 'Úgáfa #:revisionCount', + 'meta_created' => 'Búið til :timeLength', + 'meta_created_name' => 'Búið til :timeLength af :user', + 'meta_updated' => 'Uppfært :timeLength', + 'meta_updated_name' => 'Uppfært :timeLength af :user', + 'meta_owned_name' => 'Eigandi :user', + 'meta_reference_count' => 'Vitnað í af :count item|Vitnað í af :count items', + 'entity_select' => 'Entity Val', + 'entity_select_lack_permission' => 'Þú hefur ekki nauðsynlegar aðgangsheimildir til að velja þetta', + 'images' => 'Myndir', + 'my_recent_drafts' => 'Nýlegur drögin mín', + 'my_recently_viewed' => 'Síðast skoða af mér', + 'my_most_viewed_favourites' => 'Mest skoðuðu eftirlætin', + 'my_favourites' => 'Eftirlætin mín', + 'no_pages_viewed' => 'Þú hefur ekki skoðað neinar síður', + 'no_pages_recently_created' => 'Engar síður hafa verið búnar til nýlega', + 'no_pages_recently_updated' => 'Engar síður hafa verið uppfærðar nýlega', + 'export' => 'Flytja út', + 'export_html' => 'Innifalin vefskrá', + 'export_pdf' => 'PDF skrá', + 'export_text' => 'Venjuleg textaskrá', + 'export_md' => 'Markdown skrá', + 'export_zip' => 'ZIP skrá', + 'default_template' => 'Sjálfgefið síðusnið', + 'default_template_explain' => 'Veldu síðusnið sem verður sjálgefið snið fyrir allar stofnaðar síður innan þessa hluta. Hafðu í huga að þetta verður aðeins notað ef sá sem stofnar síður er með heimild á þetta snið.', + 'default_template_select' => 'Veldu sniðsíðu', + 'import' => 'Flytja inn', + 'import_validate' => 'Staðfesta innflutning', + 'import_desc' => 'Import books, chapters & pages using a portable zip export from the same, or a different, instance. Select a ZIP file to proceed. After the file has been uploaded and validated you\'ll be able to configure & confirm the import in the next view.', + 'import_zip_select' => 'Veldu ZIP skrá til að hlaða upp', + 'import_zip_validation_errors' => 'Greindar voru villur við að staðreyna uppgefina ZIP skrá:', + 'import_pending' => 'Innflutningur í bið', + 'import_pending_none' => 'Ekkert hefur verið flutt inn.', + 'import_continue' => 'Halda áfram að flytja inn', + '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' => 'Nánari lýsing á innflutningi', + 'import_run' => 'Keyra innflutning', + 'import_size' => ':size Stærð ZIP skrár', + 'import_uploaded_at' => 'Uploaded :relativeTime', + 'import_uploaded_by' => 'Upphlaðið af', + 'import_location' => 'Staðsetning innflutnings', + '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' => 'Ertu viss um að þú viljir eyða þessum innflutningi?', + 'import_delete_desc' => 'Þetta mun eyða innsendri ZIP skrá, þessa aðgerð er ekki hægt að afturkalla.', + 'import_errors' => 'Villur í innflutningi', + 'import_errors_desc' => 'Eftirfarandi villur komu upp við innflutning:', + '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', + + // Permissions and restrictions + 'permissions' => 'Réttindi', + 'permissions_desc' => 'Set permissions here to override the default permissions provided by user roles.', + 'permissions_book_cascade' => 'Permissions set on books will automatically cascade to child chapters and pages, unless they have their own permissions defined.', + 'permissions_chapter_cascade' => 'Permissions set on chapters will automatically cascade to child pages, unless they have their own permissions defined.', + 'permissions_save' => 'Vista réttindi', + 'permissions_owner' => 'Eigandi', + 'permissions_role_everyone_else' => 'Allir aðrir', + 'permissions_role_everyone_else_desc' => 'Set permissions for all roles not specifically overridden.', + 'permissions_role_override' => 'Yfirskrifa réttindi fyrir hlutverk', + 'permissions_inherit_defaults' => 'Erfa sjálfgefið', + + // Search + 'search_results' => 'Leitarniðurstöður', + 'search_total_results_found' => ':count result found|:count total results found', + 'search_clear' => 'Hreinsa leit', + 'search_no_pages' => 'Engar síður passa við þessa leit', + 'search_for_term' => 'Leita að :term', + 'search_more' => 'Fleiri niðurstöður', + 'search_advanced' => 'Ítarleg leit', + 'search_terms' => 'Leitarorð', + 'search_content_type' => 'Efnistegund', + 'search_exact_matches' => 'Nákvæm samsvörun', + 'search_tags' => 'Leita í örmerkjum', + 'search_options' => 'Valkostir', + 'search_viewed_by_me' => 'Skoðað af mér', + 'search_not_viewed_by_me' => 'Ekki skoðað af mér', + 'search_permissions_set' => 'Réttindi stillt', + 'search_created_by_me' => 'Búið til af mér', + 'search_updated_by_me' => 'Uppfært af mér', + 'search_owned_by_me' => 'Í minni eigu', + 'search_date_options' => 'Dagsetningarval', + 'search_updated_before' => 'Uppfært fyrir', + 'search_updated_after' => 'Uppfært eftir', + 'search_created_before' => 'Búið til fyrir', + 'search_created_after' => 'Búið til eftir', + 'search_set_date' => 'Dagsetning', + 'search_update' => 'Uppfæra leit', + + // Shelves + 'shelf' => 'Hilla', + 'shelves' => 'Hillur', + 'x_shelves' => ':count Hilla|:count Hillur', + 'shelves_empty' => 'Engar hillur hafa verið búnar til', + 'shelves_create' => 'Búa til hillu', + 'shelves_popular' => 'Vinsælar hillur', + 'shelves_new' => 'Nýjar hillur', + 'shelves_new_action' => 'Ný hilla', + 'shelves_popular_empty' => 'Vinsælustu hillurnar munu birtast hér.', + 'shelves_new_empty' => 'Nýjustu hillurnar munu birtast hér.', + 'shelves_save' => 'Vista hillu', + 'shelves_books' => 'Bækur í þessari hillu', + 'shelves_add_books' => 'Bæta við bókum í þessa hillu', + 'shelves_drag_books' => 'Dragðu bækur hér undir til að bæta þeim í þessa hillu', + 'shelves_empty_contents' => 'Þessi hilla hefur engar bækur', + 'shelves_edit_and_assign' => 'Breyttu hillu til að setja inn bækur', + 'shelves_edit_named' => 'Breyta hillu :name', + 'shelves_edit' => 'Breyta hillu', + 'shelves_delete' => 'Eyða hillu', + 'shelves_delete_named' => 'Eyða hillu :name', + 'shelves_delete_explain' => "Þetta mun eyða hillunni ':name'. Bókum í þessari hillu verður ekki eytt.", + 'shelves_delete_confirmation' => 'Ertu viss um að þú viljir eyða hillunni?', + 'shelves_permissions' => 'Stillingar á réttindum á hillu', + 'shelves_permissions_updated' => 'Réttindi á hillu uppfærð', + 'shelves_permissions_active' => 'Réttindi á hillu virk', + 'shelves_permissions_cascade_warning' => 'Réttindi á hillum yfirfærast ekki á bækurnar sem í hillunni eru. Þetta er vegna þess að ein bók getur verið í mörgum hillum. Réttindi geta hinsvegar verið afrituð niður á bækur með því að nota valmöguleikann hér fyrir neðan.', + 'shelves_permissions_create' => 'Réttindi til að búa til hillu eru aðeins notuð til að afrita réttindi á undirliggjandi bækur með því að nota aðgerðina hér fyrir neðan. Þau stjórna ekki hvort hægt sé að búa til bækur.', + 'shelves_copy_permissions_to_books' => 'Afrita réttindi á bækur', + 'shelves_copy_permissions' => 'Afrita réttindi', + 'shelves_copy_permissions_explain' => 'This will apply the current permission settings of this shelf to all books contained within. Before activating, ensure any changes to the permissions of this shelf have been saved.', + 'shelves_copy_permission_success' => 'Shelf permissions copied to :count books', + + // Books + 'book' => 'Bók', + 'books' => 'Bækur', + 'x_books' => ':count Bók:count Bækur', + 'books_empty' => 'Engar bækur hafa verið búnar til', + 'books_popular' => 'Vinsælar bækur', + 'books_recent' => 'Nýlegar bækur', + 'books_new' => 'Nýjar bækur', + 'books_new_action' => 'Ný bók', + 'books_popular_empty' => 'Vinsælustu bækurnar munu birtast hér.', + 'books_new_empty' => 'The most recently created books will appear here.', + 'books_create' => 'Búa til nýja bók', + 'books_delete' => 'Eyða bók', + 'books_delete_named' => 'Eyða bók :bookName', + 'books_delete_explain' => 'Þetta mun eyða bók með nafninu \':bookName\'. Allar síður og allir kaflar verða fjarlægðir og eytt.', + 'books_delete_confirmation' => 'Ertu viss um að þú viljir eyða þessari bók?', + 'books_edit' => 'Breyta bók', + 'books_edit_named' => 'Breyta bók :bookName', + 'books_form_book_name' => 'Nafn bókar', + 'books_save' => 'Vista bók', + 'books_permissions' => 'Réttindastillingar bókar', + 'books_permissions_updated' => 'Réttindastillingar bókar uppfærðar', + 'books_empty_contents' => 'Engar síður eða kaflar hafa verið búin til fyrir þessa bók.', + 'books_empty_create_page' => 'Búa til nýja síðu', + 'books_empty_sort_current_book' => 'Raða núverandi bók', + 'books_empty_add_chapter' => 'Bæta við kafla', + 'books_permissions_active' => 'Réttindi bókar virk', + 'books_search_this' => 'Leita í þessari bók', + 'books_navigation' => 'Leiðartré bókar', + 'books_sort' => 'Raða innihaldi bókar', + 'books_sort_desc' => 'Move chapters and pages within a book to reorganise its contents. Other books can be added which allows easy moving of chapters and pages between books. Optionally an auto sort rule can be set to automatically sort this book\'s contents upon changes.', + 'books_sort_auto_sort' => 'Auto Sort Option', + 'books_sort_auto_sort_active' => 'Auto Sort Active: :sortName', + 'books_sort_named' => 'Raða bók :bookName', + 'books_sort_name' => 'Raða eftir nafni', + 'books_sort_created' => 'Raða eftir skráningar dagsetningu', + 'books_sort_updated' => 'Raða eftir upphleðslu dagsetningu', + 'books_sort_chapters_first' => 'Kaflar fyrst', + 'books_sort_chapters_last' => 'Kaflar síðast', + 'books_sort_show_other' => 'Sýna aðrar bækur', + 'books_sort_save' => 'Vista nýja röð', + 'books_sort_show_other_desc' => 'Bæta við öðrum bókum til að bæta þeim við röðunina.', + 'books_sort_move_up' => 'Færa upp', + 'books_sort_move_down' => 'Færa niður', + 'books_sort_move_prev_book' => 'Færa í fyrri bók', + 'books_sort_move_next_book' => 'Færa í næstu bók', + 'books_sort_move_prev_chapter' => 'Færa inn í fyrri kafla', + 'books_sort_move_next_chapter' => 'Færa inn í næsta kafla', + 'books_sort_move_book_start' => 'Færa til byrjunar bókar', + 'books_sort_move_book_end' => 'Færa í enda bókar', + 'books_sort_move_before_chapter' => 'Færa í byrjun kafla', + 'books_sort_move_after_chapter' => 'Færa í lok kafla', + 'books_copy' => 'Afrita bók', + 'books_copy_success' => 'Tókst að afrita bók', + + // Chapters + 'chapter' => 'Kafli', + 'chapters' => 'Kaflar', + 'x_chapters' => ':count Kafli|:count Kaflar', + 'chapters_popular' => 'Vinsælir kaflar', + 'chapters_new' => 'Nýr kafli', + 'chapters_create' => 'Búa til nýjan kafla', + 'chapters_delete' => 'Eyða kafla', + 'chapters_delete_named' => 'Eyða kafla :chapterName', + 'chapters_delete_explain' => 'Þetta mun eyða kafla með nafninu \':chapterName\'. Öllum blaðsíðum í þessum kafla verður einnig eytt.', + 'chapters_delete_confirm' => 'Ertu viss um að þú viljir eyða þessum kafla?', + 'chapters_edit' => 'Breyta kafla', + 'chapters_edit_named' => 'Breyta kafla chapterName', + 'chapters_save' => 'Vista kafla', + 'chapters_move' => 'Færa kafla', + 'chapters_move_named' => 'Færa kalfa :chapterName', + 'chapters_copy' => 'Afrita kafla', + 'chapters_copy_success' => 'Tókst að afrita kafla', + 'chapters_permissions' => 'Réttindi á kafla', + 'chapters_empty' => 'Engar síður eru eins og er í þessum kafla.', + 'chapters_permissions_active' => 'Réttindi á kafla eru virk', + 'chapters_permissions_success' => 'Réttindi á kafla hafa verið uppfærð', + 'chapters_search_this' => 'Leita í þessum kafla', + 'chapter_sort_book' => 'Raða bók', + + // Pages + 'page' => 'Síða', + 'pages' => 'Síður', + 'x_pages' => ':count Síða|:count Síður', + 'pages_popular' => 'Vinsælar síður', + 'pages_new' => 'Ný síða', + 'pages_attachments' => 'Viðhengi', + 'pages_navigation' => 'Síðuráp', + 'pages_delete' => 'Eyða síðu', + 'pages_delete_named' => 'Eyða síðu :pageName', + 'pages_delete_draft_named' => 'Eyða drögum :pageName', + 'pages_delete_draft' => 'Eyða uppkasti', + 'pages_delete_success' => 'Síðu eytt', + 'pages_delete_draft_success' => 'Uppkasti að síðu eytt', + 'pages_delete_warning_template' => 'This page is in active use as a book or chapter default page template. These books or chapters will no longer have a default page template assigned after this page is deleted.', + 'pages_delete_confirm' => 'Ertu viss um að þú viljir eyða þessari síðu?', + 'pages_delete_draft_confirm' => 'Ertu viss um að þú viljir eyða þessu uppkasti að síðu?', + 'pages_editing_named' => 'Breyta síðu :pageName', + 'pages_edit_draft_options' => 'Valkostir uppkasts', + 'pages_edit_save_draft' => 'Vista uppkast', + 'pages_edit_draft' => 'Breyta drögum', + 'pages_editing_draft' => 'Breyta uppkasti', + 'pages_editing_page' => 'Breyta síðu', + 'pages_edit_draft_save_at' => 'Vista uppkast ', + 'pages_edit_delete_draft' => 'Eyða uppkasti', + 'pages_edit_delete_draft_confirm' => 'Ertu viss um að þú viljir eyða uppkasti síðu? Allar breytingar sem gerðar hafa verið frá síðustu vistun á síðunni munu tapast.', + 'pages_edit_discard_draft' => 'Henda uppkasti', + 'pages_edit_switch_to_markdown' => 'Færa þig yfir í Markdown ritil', + 'pages_edit_switch_to_markdown_clean' => '(Hreinsa innihald)', + 'pages_edit_switch_to_markdown_stable' => '(Stable Content)', + 'pages_edit_switch_to_wysiwyg' => 'Skipta yfir í WYSIWYG ritil', + 'pages_edit_switch_to_new_wysiwyg' => 'Skipta yfir í nýja WYSIWYG ritilinn', + 'pages_edit_switch_to_new_wysiwyg_desc' => '(In Beta Testing)', + 'pages_edit_set_changelog' => 'Stilla breytingarskrá', + 'pages_edit_enter_changelog_desc' => 'Skrifaðu stutta lýsingu á breytingunum sem þú gerðir', + 'pages_edit_enter_changelog' => 'Færa í breytingaskrá', + 'pages_editor_switch_title' => 'Skipta um ritil', + 'pages_editor_switch_are_you_sure' => 'Are you sure you want to change the editor for this page?', + 'pages_editor_switch_consider_following' => 'Consider the following when changing editors:', + 'pages_editor_switch_consideration_a' => 'Once saved, the new editor option will be used by any future editors, including those that may not be able to change editor type themselves.', + 'pages_editor_switch_consideration_b' => 'This can potentially lead to a loss of detail and syntax in certain circumstances.', + 'pages_editor_switch_consideration_c' => 'Tag or changelog changes, made since last save, won\'t persist across this change.', + 'pages_save' => 'Vista síðu', + 'pages_title' => 'Titill síðu', + 'pages_name' => 'Nafn síðu', + 'pages_md_editor' => 'Ritill', + 'pages_md_preview' => 'Forskoðun', + 'pages_md_insert_image' => 'Setja inn mynd', + 'pages_md_insert_link' => 'Insert Entity Link', + 'pages_md_insert_drawing' => 'Setja inn teikningu', + 'pages_md_show_preview' => 'Sýna forskoðun', + 'pages_md_sync_scroll' => 'Sync preview scroll', + 'pages_md_plain_editor' => 'Plaintext editor', + 'pages_drawing_unsaved' => 'Unsaved Drawing Found', + 'pages_drawing_unsaved_confirm' => 'Unsaved drawing data was found from a previous failed drawing save attempt. Would you like to restore and continue editing this unsaved drawing?', + 'pages_not_in_chapter' => 'Síðan tilheyrir engum kafla', + 'pages_move' => 'Færa síðu', + 'pages_copy' => 'Afrita síðu', + 'pages_copy_desination' => 'Áfangastaður afritunar', + 'pages_copy_success' => 'Tókst að afrita síðu', + 'pages_permissions' => 'Réttindi síðu', + 'pages_permissions_success' => 'Réttindi síðu uppfærð', + 'pages_revision' => 'Útgáfa', + 'pages_revisions' => 'Útgáfur síðu', + 'pages_revisions_desc' => 'Listed below are all the past revisions of this page. You can look back upon, compare, and restore old page versions if permissions allow. The full history of the page may not be fully reflected here since, depending on system configuration, old revisions could be auto-deleted.', + 'pages_revisions_named' => 'Page Revisions for :pageName', + 'pages_revision_named' => 'Page Revision for :pageName', + 'pages_revision_restored_from' => 'Restored from #:id; :summary', + 'pages_revisions_created_by' => 'Búið til af', + 'pages_revisions_date' => 'Útgáfu dagsetning', + 'pages_revisions_number' => '#', + 'pages_revisions_sort_number' => 'Útgáfunúmer', + 'pages_revisions_numbered' => 'Útgáfu #:id', + 'pages_revisions_numbered_changes' => 'Útgáfu #:id breytingar', + 'pages_revisions_editor' => 'Gerð ritils', + 'pages_revisions_changelog' => 'Breytingaskrá', + 'pages_revisions_changes' => 'Breytingar', + 'pages_revisions_current' => 'Núverandi útgáfa', + 'pages_revisions_preview' => 'Forskoðun', + 'pages_revisions_restore' => 'Endurheimta', + 'pages_revisions_none' => 'Þessi síða hefur engar útgáfur', + 'pages_copy_link' => 'Afrita hlekk', + 'pages_edit_content_link' => 'Hoppa í staðsetningu í ritli', + 'pages_pointer_enter_mode' => 'Enter section select mode', + 'pages_pointer_label' => 'Page Section Options', + 'pages_pointer_permalink' => 'Page Section Permalink', + 'pages_pointer_include_tag' => 'Page Section Include Tag', + 'pages_pointer_toggle_link' => 'Permalink mode, Press to show include tag', + 'pages_pointer_toggle_include' => 'Include tag mode, Press to show permalink', + 'pages_permissions_active' => 'Page Permissions Active', + 'pages_initial_revision' => 'Fyrsta birting', + 'pages_references_update_revision' => 'System auto-update of internal links', + 'pages_initial_name' => 'Ný síða', + 'pages_editing_draft_notification' => 'You are currently editing a draft that was last saved :timeDiff.', + 'pages_draft_edited_notification' => 'This page has been updated by since that time. It is recommended that you discard this draft.', + 'pages_draft_page_changed_since_creation' => 'This page has been updated since this draft was created. It is recommended that you discard this draft or take care not to overwrite any page changes.', + 'pages_draft_edit_active' => [ + 'start_a' => ':count users have started editing this page', + 'start_b' => ':userName has started editing this page', + 'time_a' => 'since the page was last updated', + 'time_b' => 'in the last :minCount minutes', + 'message' => ':start :time. Take care not to overwrite each other\'s updates!', + ], + 'pages_draft_discarded' => 'Draft discarded! The editor has been updated with the current page content', + 'pages_draft_deleted' => 'Draft deleted! The editor has been updated with the current page content', + 'pages_specific' => 'Tilgreind síða', + 'pages_is_template' => 'Forsnið síðu', + + // Editor Sidebar + 'toggle_sidebar' => 'Toggle Sidebar', + 'page_tags' => 'Page Tags', + 'chapter_tags' => 'Chapter Tags', + 'book_tags' => 'Book Tags', + 'shelf_tags' => 'Shelf Tags', + 'tag' => 'Tag', + 'tags' => 'Tags', + 'tags_index_desc' => 'Tags can be applied to content within the system to apply a flexible form of categorization. Tags can have both a key and value, with the value being optional. Once applied, content can then be queried using the tag name and value.', + 'tag_name' => 'Tag Name', + 'tag_value' => 'Tag Value (Optional)', + 'tags_explain' => "Add some tags to better categorise your content. \n You can assign a value to a tag for more in-depth organisation.", + 'tags_add' => 'Add another tag', + 'tags_remove' => 'Remove this tag', + 'tags_usages' => 'Total tag usages', + 'tags_assigned_pages' => 'Assigned to Pages', + 'tags_assigned_chapters' => 'Assigned to Chapters', + 'tags_assigned_books' => 'Assigned to Books', + 'tags_assigned_shelves' => 'Assigned to Shelves', + 'tags_x_unique_values' => ':count unique values', + 'tags_all_values' => 'All values', + 'tags_view_tags' => 'View Tags', + 'tags_view_existing_tags' => 'View existing tags', + 'tags_list_empty_hint' => 'Tags can be assigned via the page editor sidebar or while editing the details of a book, chapter or shelf.', + 'attachments' => 'Attachments', + 'attachments_explain' => 'Upload some files or attach some links to display on your page. These are visible in the page sidebar.', + 'attachments_explain_instant_save' => 'Changes here are saved instantly.', + 'attachments_upload' => 'Upload File', + 'attachments_link' => 'Attach Link', + 'attachments_upload_drop' => 'Alternatively you can drag and drop a file here to upload it as an attachment.', + 'attachments_set_link' => 'Set Link', + 'attachments_delete' => 'Are you sure you want to delete this attachment?', + 'attachments_dropzone' => 'Drop files here to upload', + 'attachments_no_files' => 'No files have been uploaded', + 'attachments_explain_link' => 'You can attach a link if you\'d prefer not to upload a file. This can be a link to another page or a link to a file in the cloud.', + 'attachments_link_name' => 'Link Name', + 'attachment_link' => 'Attachment link', + 'attachments_link_url' => 'Link to file', + 'attachments_link_url_hint' => 'Url of site or file', + 'attach' => 'Attach', + 'attachments_insert_link' => 'Add Attachment Link to Page', + 'attachments_edit_file' => 'Edit File', + 'attachments_edit_file_name' => 'File Name', + 'attachments_edit_drop_upload' => 'Drop files or click here to upload and overwrite', + 'attachments_order_updated' => 'Attachment order updated', + 'attachments_updated_success' => 'Attachment details updated', + 'attachments_deleted' => 'Attachment deleted', + 'attachments_file_uploaded' => 'File successfully uploaded', + 'attachments_file_updated' => 'File successfully updated', + 'attachments_link_attached' => 'Link successfully attached to page', + 'templates' => 'Templates', + 'templates_set_as_template' => 'Page is a template', + 'templates_explain_set_as_template' => 'You can set this page as a template so its contents be utilized when creating other pages. Other users will be able to use this template if they have view permissions for this page.', + 'templates_replace_content' => 'Replace page content', + 'templates_append_content' => 'Append to page content', + 'templates_prepend_content' => 'Prepend to page content', + + // Profile View + 'profile_user_for_x' => 'User for :time', + 'profile_created_content' => 'Created Content', + 'profile_not_created_pages' => ':userName has not created any pages', + 'profile_not_created_chapters' => ':userName has not created any chapters', + 'profile_not_created_books' => ':userName has not created any books', + 'profile_not_created_shelves' => ':userName has not created any shelves', + + // Comments + 'comment' => 'Comment', + 'comments' => 'Comments', + 'comment_add' => 'Add Comment', + 'comment_none' => 'No comments to display', + 'comment_placeholder' => 'Leave a comment here', + 'comment_thread_count' => ':count Comment Thread|:count Comment Threads', + 'comment_archived_count' => ':count Archived', + 'comment_archived_threads' => 'Archived Threads', + 'comment_save' => 'Save Comment', + 'comment_new' => 'New Comment', + 'comment_created' => 'commented :createDiff', + 'comment_updated' => 'Updated :updateDiff by :username', + 'comment_updated_indicator' => 'Updated', + 'comment_deleted_success' => 'Comment deleted', + 'comment_created_success' => 'Comment added', + 'comment_updated_success' => 'Comment updated', + 'comment_archive_success' => 'Comment archived', + 'comment_unarchive_success' => 'Comment un-archived', + 'comment_view' => 'View comment', + 'comment_jump_to_thread' => 'Jump to thread', + 'comment_delete_confirm' => 'Are you sure you want to delete this comment?', + 'comment_in_reply_to' => 'In reply to :commentId', + 'comment_reference' => 'Reference', + 'comment_reference_outdated' => '(Outdated)', + 'comment_editor_explain' => 'Here are the comments that have been left on this page. Comments can be added & managed when viewing the saved page.', + + // Revision + 'revision_delete_confirm' => 'Are you sure you want to delete this revision?', + 'revision_restore_confirm' => 'Are you sure you want to restore this revision? The current page contents will be replaced.', + 'revision_cannot_delete_latest' => 'Cannot delete the latest revision.', + + // Copy view + 'copy_consider' => 'Please consider the below when copying content.', + 'copy_consider_permissions' => 'Custom permission settings will not be copied.', + 'copy_consider_owner' => 'You will become the owner of all copied content.', + 'copy_consider_images' => 'Page image files will not be duplicated & the original images will retain their relation to the page they were originally uploaded to.', + 'copy_consider_attachments' => 'Page attachments will not be copied.', + 'copy_consider_access' => 'A change of location, owner or permissions may result in this content being accessible to those previously without access.', + + // Conversions + 'convert_to_shelf' => 'Convert to Shelf', + 'convert_to_shelf_contents_desc' => 'You can convert this book to a new shelf with the same contents. Chapters contained within this book will be converted to new books. If this book contains any pages, that are not in a chapter, this book will be renamed and contain such pages, and this book will become part of the new shelf.', + 'convert_to_shelf_permissions_desc' => 'Any permissions set on this book will be copied to the new shelf and to all new child books that don\'t have their own permissions enforced. Note that permissions on shelves do not auto-cascade to content within, as they do for books.', + 'convert_book' => 'Convert Book', + 'convert_book_confirm' => 'Are you sure you want to convert this book?', + 'convert_undo_warning' => 'This cannot be as easily undone.', + 'convert_to_book' => 'Convert to Book', + 'convert_to_book_desc' => 'You can convert this chapter to a new book with the same contents. Any permissions set on this chapter will be copied to the new book but any inherited permissions, from the parent book, will not be copied which could lead to a change of access control.', + 'convert_chapter' => 'Convert Chapter', + 'convert_chapter_confirm' => 'Are you sure you want to convert this chapter?', + + // References + 'references' => 'References', + 'references_none' => 'There are no tracked references to this item.', + 'references_to_desc' => 'Listed below is all the known content in the system that links to this item.', + + // Watch Options + 'watch' => 'Watch', + 'watch_title_default' => 'Default Preferences', + 'watch_desc_default' => 'Revert watching to just your default notification preferences.', + 'watch_title_ignore' => 'Ignore', + 'watch_desc_ignore' => 'Ignore all notifications, including those from user-level preferences.', + 'watch_title_new' => 'New Pages', + 'watch_desc_new' => 'Notify when any new page is created within this item.', + 'watch_title_updates' => 'All Page Updates', + 'watch_desc_updates' => 'Notify upon all new pages and page changes.', + 'watch_desc_updates_page' => 'Notify upon all page changes.', + 'watch_title_comments' => 'All Page Updates & Comments', + 'watch_desc_comments' => 'Notify upon all new pages, page changes and new comments.', + 'watch_desc_comments_page' => 'Notify upon page changes and new comments.', + 'watch_change_default' => 'Change default notification preferences', + 'watch_detail_ignore' => 'Ignoring notifications', + 'watch_detail_new' => 'Watching for new pages', + 'watch_detail_updates' => 'Watching new pages and updates', + 'watch_detail_comments' => 'Watching new pages, updates & comments', + 'watch_detail_parent_book' => 'Watching via parent book', + 'watch_detail_parent_book_ignore' => 'Ignoring via parent book', + 'watch_detail_parent_chapter' => 'Watching via parent chapter', + 'watch_detail_parent_chapter_ignore' => 'Ignoring via parent chapter', +]; diff --git a/lang/is/errors.php b/lang/is/errors.php new file mode 100644 index 00000000000..50e30a8c57a --- /dev/null +++ b/lang/is/errors.php @@ -0,0 +1,133 @@ + 'Þú hefur ekki heimild til að skoða þessa síðu.', + 'permissionJson' => 'Þú hefur ekki heimild til að framkvæma þessa aðgerð.', + + // Auth + 'error_user_exists_different_creds' => 'Notandi með netfangið :email er nú þegar til.', + 'auth_pre_register_theme_prevention' => 'Ekki var hægt að búa til aðgang með þessum upplýsingum', + 'email_already_confirmed' => 'Netfang hefur þegar verið staðfest. Prófaðu að skrá þig inn.', + 'email_confirmation_invalid' => 'Þessi staðfestingar tóki er ekki gildur eða hefur þegar verið notaður. Reyndu að skrá þig aftur.', + 'email_confirmation_expired' => 'Staðfestingar tóki hefur runnið út. Nýr staðfestinga tölvupóstur hefur verið sendur.', + 'email_confirmation_awaiting' => 'Eftir á að staðfest þetta netfang', + 'ldap_fail_anonymous' => 'LDAP auðkenning virkaði ekki', + 'ldap_fail_authed' => 'LDAP auðkenning virkaði ekki með að nota uppgefið dn & password', + 'ldap_extension_not_installed' => 'LDAP PHP viðbót ekki uppsett', + 'ldap_cannot_connect' => 'Næ ekki að tengjast Ldap þjóni. Fyrsta tenging mistókst', + 'saml_already_logged_in' => 'Þegar innskráður', + 'saml_no_email_address' => 'Fann ekki netfang fyrir þennan notanda í auðkenningar þjónustu', + 'saml_invalid_response_id' => 'Beiðnin frá ytri auðkenningaraðila er óþekkt af kerfinu. Að fara tilbaka eftir innskráningu gæti valdið þessu vandamáli.', + 'saml_fail_authed' => 'Innskráning sem notaði :system tókst ekki. Kerfið gaf ekki út gilda auðkenningu', + 'oidc_already_logged_in' => 'Þegar skráður inn', + 'oidc_no_email_address' => 'Fann ekki netfang fyrir þennan notanda í ytri auðkenningar þjónustu', + 'oidc_fail_authed' => 'Innskráning sem notaði :system tókst ekki. Kerfið gaf ekki út gilda auðkenningu', + 'social_no_action_defined' => 'Engin aðgerð skilgreind', + 'social_login_bad_response' => "Villa kom upp við auðkenninga á :socialAccount login", + 'social_account_in_use' => 'Þessi :socialAccount er þegar í notkun. Reyndu að skrá þig inn með :socialAccount.', + 'social_account_email_in_use' => 'Netfangið :email er þegar í notkun. Ef þú ert nú þegar með aðgang getur þú tengt :socialAccount við hann í prófíl stillingum.', + 'social_account_existing' => 'Þessi :socialAccount er nú þegar tengdur við prófílinn þinn.', + 'social_account_already_used_existing' => 'Þessi :socialAccount reikningur er nú þegar í notkun hjá öðrum notanda.', + 'social_account_not_used' => 'Þessi :socialAccount er ekki tengdur neinum notanda. Þú getur tengt hann við þig í prófíl stillingar. ', + 'social_account_register_instructions' => 'Ef þú ert ekki nú þegar með aðgang, getur þú skrá þig með :socialAccount', + 'social_driver_not_found' => 'Samfélagsviðbót fannst ekki', + 'social_driver_not_configured' => 'Þínar :socialAccount er ekki rétt stilltar.', + 'invite_token_expired' => 'Þess boðshlekkur er útrunninn, Prófa að endurstilla lykilorðið þitt.', + 'login_user_not_found' => 'Enginn notandi fannst fyrir þessa aðgerð.', + + // System + 'path_not_writable' => 'Ekki var hægt að hlaða upp á slóðinni :filePath. Vertu viss um að slóðin sé skrifanleg.', + 'cannot_get_image_from_url' => 'Get ekki sótt mynd frá :url', + 'cannot_create_thumbs' => 'Netþjónninn getur ekki búið til smámyndir. Vertu viss um að þú hafir GD PHP viðbótina uppsetta.', + 'server_upload_limit' => 'Þessi netþjónn leyfir ekki uphal af þessari stærð. Prófaðu minni skrá.', + 'server_post_limit' => 'Netþjóninn getur ekki tekið á móti þessu magni gagna. Reyndu aftur með færri eða smærri gögnum.', + 'uploaded' => 'Þessi netþjónn leyfir ekki uphal af þessari stærð. Prófaðu minni skrá.', + + // Drawing & Images + 'image_upload_error' => 'Villa kom upp við að hlaða upp mynd', + 'image_upload_type_error' => 'Gerð myndar er ógild', + 'image_upload_replace_type' => 'Myndin sem á að nota við útskipti þarf að vera sömu gerðar', + 'image_upload_memory_limit' => 'Ekki var hægt að taka við upphali og eða búa til smámyndir þar sem ekki eru auðlindir til staðar.', + 'image_thumbnail_memory_limit' => 'Ekki var hægt að búa til nokkrar stærðir myndarinnar vegna skorts á auðlindum.', + 'image_gallery_thumbnail_memory_limit' => 'Ekki var hægt að búa til smámyndayfirlit vegna skorts á auðlindum.', + 'drawing_data_not_found' => 'Ekki tóks að hlaða inn teikningagögnum. Það gæti vantað skránna eða að þú hafir ekki réttindi að henni.', + + // Attachments + 'attachment_not_found' => 'Viðhengi fannst ekki', + 'attachment_upload_error' => 'Það kom upp villa við að hlaða upp viðhenginu', + + // Pages + 'page_draft_autosave_fail' => 'Gat ekki vistað uppkast. Gættu að þú hafir tengingu við internetið áður en þú vistar þessa síðu', + 'page_draft_delete_fail' => 'Ekki var hægt að eyða uppkasti og sækja fyrra innihald síðunar', + 'page_custom_home_deletion' => 'Ekki er hægt að eyða síðu á meðan hún er valin sem sjálfgefin upphafssíða', + + // Entities + 'entity_not_found' => 'Entity fannst ekki', + 'bookshelf_not_found' => 'Hilla fannst ekki', + 'book_not_found' => 'Bók fannst ekki', + 'page_not_found' => 'Síða fannst ekki', + 'chapter_not_found' => 'Kafli fannst ekki', + 'selected_book_not_found' => 'Valin bók fannst ekki', + 'selected_book_chapter_not_found' => 'Valin bók eða kafli fannst ekki', + 'guests_cannot_save_drafts' => 'Gestir geta ekki vistað drög', + + // Users + 'users_cannot_delete_only_admin' => 'Þú getur ekki eytt, bara kerfisstjóri', + 'users_cannot_delete_guest' => 'Þú getur ekki eytt gesta notanda', + 'users_could_not_send_invite' => 'Gat ekki stofnað notanda þar sem ekki tókst að senda staðfestingar tölvupóst', + + // Roles + 'role_cannot_be_edited' => 'Ekki er hægt að breyta þessu hlutverki', + 'role_system_cannot_be_deleted' => 'Þetta er kerfis hlutverk og því ekki hægt að eyða því', + 'role_registration_default_cannot_delete' => 'Ekki er hægt að eyða þessu hlutverki þar sem það er sjálfgefið kerfishlutverk við skráningu', + 'role_cannot_remove_only_admin' => 'Þessi notandi er sá eini sem er með kerfisstjóra hlutverk. Bættu hlutverkinu við annann notanda áður en þú reynir að fjarlægja það héðan.', + + // Comments + 'comment_list' => 'Villa kom upp við að sækja athugasemdir.', + 'cannot_add_comment_to_draft' => 'Þú getur ekki sett athugasemdir við drög.', + 'comment_add' => 'Villa kom upp við að bæta við eða breyta athugasemdinni.', + 'comment_delete' => 'Villa kom upp við að eyða athugasemdinni.', + 'empty_comment' => 'Get ekki bætt við tómri athugasemd.', + + // Error pages + '404_page_not_found' => 'Síða fannst ekki', + 'sorry_page_not_found' => 'Síðan sem þú varst að leita að fannst því miður ekki.', + 'sorry_page_not_found_permission_warning' => 'Ef þú átt von á að þessi síða sé til gæti verið að þú hafir ekki aðgang að henni.', + 'image_not_found' => 'Fann ekki mynd', + 'image_not_found_subtitle' => 'Myndin sem þú varst að leita að fannst því miður ekki.', + 'image_not_found_details' => 'Ef þú heldur að þessi mynda hafi verið til, þá gæti henni hafa verið eytt.', + 'return_home' => 'Fara á forsíðu', + 'error_occurred' => 'Það kom upp villa', + 'app_down' => ':appName er niðri í augnablikinu', + 'back_soon' => 'Verð komin upp aftur fljótlega.', + + // Import + 'import_zip_cant_read' => 'Gat ekki lesið ZIP skrá.', + 'import_zip_cant_decode_data' => 'Fann ekki ZIP data.json innihald.', + 'import_zip_no_data' => 'ZIP skráin inniheldur ekkert efni.', + 'import_validation_failed' => 'ZIP skráin stóðst ekki staðfestingu og skilaði villu:', + 'import_zip_failed_notification' => 'Gat ekki lesið inn ZIP skrá.', + 'import_perms_books' => 'Þú hefur ekki heimild til að búa til bækur.', + 'import_perms_chapters' => 'Þú hefur ekki heimild til að búa til kafla.', + 'import_perms_pages' => 'Þú hefur ekki heimild til að búa til síður.', + 'import_perms_images' => 'Þú hefur ekki heimild til að búa til myndir.', + 'import_perms_attachments' => 'Þú hefur ekki heimild til að búa til viðhengi.', + + // API errors + 'api_no_authorization_found' => 'Engin auðkenningar tóki fannst í aðgerðinni', + 'api_bad_authorization_format' => 'Auðkenningar tóki fannst með aðgerðinni en snið hans er rangt', + 'api_user_token_not_found' => 'Engin API tóki fannst á móti þessum auðkenningar tóka', + 'api_incorrect_token_secret' => 'Leyndarmálið sem gefið var upp fyrir API tókann er rangt', + 'api_user_no_api_permission' => 'Eigandi API tókans hefur ekki heimild til að gera API köll', + 'api_user_token_expired' => 'Auðkenningar tókin er útrunninn', + + // Settings & Maintenance + 'maintenance_test_email_failure' => 'Villa kom upp viðað reyna senda prufu tölvupóst:', + + // HTTP errors + 'http_ssr_url_no_match' => 'Þetta vistfang stemmir ekki við leyfða SSR biðlara', +]; diff --git a/lang/is/notifications.php b/lang/is/notifications.php new file mode 100644 index 00000000000..b4fe01ad748 --- /dev/null +++ b/lang/is/notifications.php @@ -0,0 +1,27 @@ + 'Ný athugasemd á síðu :pageName', + 'new_comment_intro' => 'Notandi hefur sett inn athugasemd á síðu á :appName:', + 'new_page_subject' => 'Ný síða á: :pageName', + 'new_page_intro' => 'Ný síða hefur verið búin til á :appName:', + 'updated_page_subject' => 'Uppfærð síða á: :pageName', + 'updated_page_intro' => 'Síða hefur verið uppfærð á :appName:', + 'updated_page_debounce' => 'Til að fyrirbyggja fjöldatilkynningar verður þér ekki sendar tilkynningar í smá stund um uppfærslu á þessari síðu frá sama höfundi.', + + 'detail_page_name' => 'Síðunafn:', + 'detail_page_path' => 'Síðuslóð:', + 'detail_commenter' => 'Notandi:', + 'detail_comment' => 'Athugasemd:', + 'detail_created_by' => 'Búið til af:', + 'detail_updated_by' => 'Uppfært af:', + + 'action_view_comment' => 'Skoða athugasemd', + 'action_view_page' => 'Skoða síðu', + + 'footer_reason' => 'Þessi tilkynning var send til þín vegna :link nær yfir þessa virkni á þessum hlut.', + 'footer_reason_link' => 'stillingar á tilkynningum til þín', +]; diff --git a/lang/is/pagination.php b/lang/is/pagination.php new file mode 100644 index 00000000000..3eca2f26e4a --- /dev/null +++ b/lang/is/pagination.php @@ -0,0 +1,12 @@ + '« Fyrri', + 'next' => 'Næsta»', + +]; diff --git a/lang/is/passwords.php b/lang/is/passwords.php new file mode 100644 index 00000000000..b8c093b0388 --- /dev/null +++ b/lang/is/passwords.php @@ -0,0 +1,15 @@ + 'Lykilorð verður að vera að lágmarki 8 stafir og stemma saman.', + 'user' => "Enginn notandi finnst með þetta netfang.", + 'token' => 'Tókinn er ógildur fyrir þetta netfang.', + 'sent' => 'Þér hefur verið sendur hlekkur í tölvupósti!', + 'reset' => 'Lykilorðinu hefur verið breytt!', + +]; diff --git a/lang/is/preferences.php b/lang/is/preferences.php new file mode 100644 index 00000000000..b7ebc2df00a --- /dev/null +++ b/lang/is/preferences.php @@ -0,0 +1,51 @@ + 'Minn aðgangur', + + 'shortcuts' => 'Flýtileiðir', + 'shortcuts_interface' => 'UI, stillingar flýtileiða', + 'shortcuts_toggle_desc' => 'Hér getur þú virkjað eða óvirkjað flýtilykla, notað fyrir leiðarstýringu og aðgerðir.', + 'shortcuts_customize_desc' => 'Þú getur stillt alla flýtilyklana hér að neðan. Þú ýtir þá þann flýtilykil sem þú vilt nota eftir að þú hefur valið innsláttarleið.', + 'shortcuts_toggle_label' => 'Flýtilyklar virkjaðir', + 'shortcuts_section_navigation' => 'Leiðarstýring', + 'shortcuts_section_actions' => 'Algengar aðgerðir', + 'shortcuts_save' => 'Vista flýtilykla', + 'shortcuts_overlay_desc' => 'Ath: þegar flýtilyklar eru virkjaðir er hægt að fá aðstoð með því að ýta á "?" sem mun yfirstrika þær flýtileiðir sem í boði eru.', + 'shortcuts_update_success' => 'Stillingar flýtilykla hafa verið uppfærðar!', + 'shortcuts_overview_desc' => 'Stjórna þeim flýtilyklum sem í boði eru.', + + 'notifications' => 'Stillingar tilkynninga', + 'notifications_desc' => 'Stýrðu þeim tölvupóst tilkynningum sem þú færð þegar ákveðnar aðgerðir eru gerðar af kerfinu.', + 'notifications_opt_own_page_changes' => 'Láta vita þegar gerðar eru breytingar á síðum sem ég á', + 'notifications_opt_own_page_comments' => 'Láta vita þegar gerðar eru athugasmedir við síður sem ég á', + 'notifications_opt_comment_replies' => 'Láta vita þegar athugasemdum mínum er svarað', + 'notifications_save' => 'Vista stillingar', + 'notifications_update_success' => 'Stillingar á tilkynningum hafa verið uppfærðar!', + 'notifications_watched' => 'Watched & Ignored Items', + 'notifications_watched_desc' => 'Fyrir neðan eru hlutir sem hafa sérsniðna eftirfylgni stillta. Til að uppfæra stillingarnar fyrir þessa hluti skaltu skoða hann og og finna skoðastillinguna í hliðarstikunni.', + + 'auth' => 'Aðgangur og öryggi', + 'auth_change_password' => 'Breyta lykilorði', + 'auth_change_password_desc' => 'Breyta lykilorðinu sem þú notar til að skrá þig inn í hugbúnaðinn. Lykilorðið verður að vera a. m. k 8 stafa langt.', + 'auth_change_password_success' => 'Lykilorði hefur verið breytt!', + + 'profile' => 'Upplýsingar um prófíl', + 'profile_desc' => 'Stýra þeim upplýsingum um þig sem aðrir notendur sjá.', + 'profile_view_public' => 'Skoða almennan prófíl', + 'profile_name_desc' => 'Stilla þitt notendanafn sem er sýnlegt öðrum notendum.', + 'profile_email_desc' => 'Þetta netfang verður notað fyrir tilkynningar og aðgang að kerfinu hafir þú valið svo.', + 'profile_email_no_permission' => 'Þú hefur ekki heimild til að breyta netfanginu þinu. Ef þú vilt láta breyta því verður þú að hafa samband við kerfisstjóra.', + 'profile_avatar_desc' => 'Veldu mynd til að sýna öðrum notendum. Helst þarf þessi mynd að vera ferköntuð og um það bil 256px bæði á breidd og hæð.', + 'profile_admin_options' => 'Stillingar kerfisstjóra', + 'profile_admin_options_desc' => 'Viðbótar kerfistjóra stillingar, til dæmis stjórnun á hlutverkum sem finna á í "Stillingar > Notendur svæði hugbúnaðarins.', + + 'delete_account' => 'Eyða aðgangi', + 'delete_my_account' => 'Eyða reikningi mínum', + 'delete_my_account_desc' => 'Þetta mun eyða þínum aðgangi að hugbúnaðinum. Þú munt ekki geta enduheimt aðganginn. Efni sem þú hefur búið til eins og síður og þær myndir sem þú hefur sent inn munu halda sér.', + 'delete_my_account_warning' => 'Ertu viss um að þú viljir eyða aðganginum þínum?', +]; diff --git a/lang/is/settings.php b/lang/is/settings.php new file mode 100644 index 00000000000..3765db061cc --- /dev/null +++ b/lang/is/settings.php @@ -0,0 +1,370 @@ + 'Stillingar', + 'settings_save' => 'Vista stillingar', + 'system_version' => 'Kerfisútgáfa', + 'categories' => 'Flokkar', + + // App Settings + 'app_customization' => 'Sérsníða', + 'app_features_security' => 'Eigindi og öryggi', + 'app_name' => 'Nafn kerfis', + 'app_name_desc' => 'Þetta nafn er sýnd í titli og í öllum tölvupóstum sem sendir eru.', + 'app_name_header' => 'Sýna nafn í titli', + 'app_public_access' => 'Almennur aðgangur', + 'app_public_access_desc' => 'Með því að virkja þennan valmöguleika munu notendur sem eru ekki skráðir inn geta skoðað innihald vefsins.', + 'app_public_access_desc_guest' => 'Hægt er að stýra almennum aðgangi í gegnum "Guest" notandann.', + 'app_public_access_toggle' => 'Leyfa almennann aðgang', + 'app_public_viewing' => 'Leyfa almenna skoðun?', + 'app_secure_images' => 'Aukið öryggi á mynda upphal', + 'app_secure_images_toggle' => 'Virkja aukið öryggi á mynda upphal', + 'app_secure_images_desc' => 'For performance reasons, all images are public. This option adds a random, hard-to-guess string in front of image urls. Ensure directory indexes are not enabled to prevent easy access.', + 'app_default_editor' => 'Sjálfgefin ritill', + 'app_default_editor_desc' => 'Veldu hvaða ritil á að nota sjálfgefið þegar unnið er með nýjar síður. Hægt er að yfirskrifa þessa stillingu á hverri síðu ef viðkomandi er með réttindi.', + 'app_custom_html' => 'Custom HTML Head Content', + 'app_custom_html_desc' => 'Any content added here will be inserted into the bottom of the section of every page. This is handy for overriding styles or adding analytics code.', + 'app_custom_html_disabled_notice' => 'Custom HTML head content is disabled on this settings page to ensure any breaking changes can be reverted.', + 'app_logo' => 'Lógó síðu', + 'app_logo_desc' => 'This is used in the application header bar, among other areas. This image should be 86px in height. Large images will be scaled down.', + 'app_icon' => 'Íkon síðu', + 'app_icon_desc' => 'Þetta íkon er notað í tabs í vöfrum og sem flýtivísir á síðu. Þetta ætti að vera 256px PNG mynd.', + 'app_homepage' => 'Heimasíða', + 'app_homepage_desc' => 'Select a view to show on the homepage instead of the default view. Page permissions are ignored for selected pages.', + 'app_homepage_select' => 'Veldu síðu', + 'app_footer_links' => 'Neðangreins hlekkir', + 'app_footer_links_desc' => 'Add links to show within the site footer. These will be displayed at the bottom of most pages, including those that do not require login. You can use a label of "trans::" to use system-defined translations. For example: Using "trans::common.privacy_policy" will provide the translated text "Privacy Policy" and "trans::common.terms_of_service" will provide the translated text "Terms of Service".', + 'app_footer_links_label' => 'Miði hlekks', + 'app_footer_links_url' => 'Vistfang hlekks', + 'app_footer_links_add' => 'Bæta við neðangreinshlekk', + 'app_disable_comments' => 'Óvirkja athugasemdir', + 'app_disable_comments_toggle' => 'Óvirkja athugasemdir', + 'app_disable_comments_desc' => 'Disables comments across all pages in the application.
Existing comments are not shown.', + + // Color settings + 'color_scheme' => 'Litaþema hugbúnaðar', + 'color_scheme_desc' => 'Set the colors to use in the application user interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.', + 'ui_colors_desc' => 'Set the application primary color and default link color. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the application interface.', + 'app_color' => 'Aðal litur', + 'link_color' => 'Aðal litur hlekkja', + 'content_colors_desc' => 'Set colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.', + 'bookshelf_color' => 'Litur hillu', + 'book_color' => 'Litur Bóka', + 'chapter_color' => 'Litur kalfa', + 'page_color' => 'Litur síðu', + 'page_draft_color' => 'Litur draga', + + // Registration Settings + 'reg_settings' => 'Skráning', + 'reg_enable' => 'Virkja skráningar', + 'reg_enable_toggle' => 'Virkja skráningar', + 'reg_enable_desc' => 'When registration is enabled user will be able to sign themselves up as an application user. Upon registration they are given a single, default user role.', + 'reg_default_role' => 'Sjálfgefið hlutverk notanda eftir skráningu', + 'reg_enable_external_warning' => 'The option above is ignored while external LDAP or SAML authentication is active. User accounts for non-existing members will be auto-created if authentication, against the external system in use, is successful.', + 'reg_email_confirmation' => 'Tölvupóst staðfesting', + 'reg_email_confirmation_toggle' => 'Krefast staðfestingar í tölvupósti', + 'reg_confirm_email_desc' => 'If domain restriction is used then email confirmation will be required and this option will be ignored.', + 'reg_confirm_restrict_domain' => 'Læsingar á lén', + 'reg_confirm_restrict_domain_desc' => 'Enter a comma separated list of email domains you would like to restrict registration to. Users will be sent an email to confirm their address before being allowed to interact with the application.
Note that users will be able to change their email addresses after successful registration.', + 'reg_confirm_restrict_domain_placeholder' => 'Engin skilyrði sett', + + // Sorting Settings + 'sorting' => 'Sorting', + 'sorting_book_default' => 'Default Book Sort', + 'sorting_book_default_desc' => 'Select the default sort rule to apply to new books. This won\'t affect existing books, and can be overridden per-book.', + 'sorting_rules' => 'Sort Rules', + 'sorting_rules_desc' => 'These are predefined sorting operations which can be applied to content in the system.', + 'sort_rule_assigned_to_x_books' => 'Assigned to :count Book|Assigned to :count Books', + 'sort_rule_create' => 'Create Sort Rule', + 'sort_rule_edit' => 'Edit Sort Rule', + 'sort_rule_delete' => 'Delete Sort Rule', + 'sort_rule_delete_desc' => 'Remove this sort rule from the system. Books using this sort will revert to manual sorting.', + 'sort_rule_delete_warn_books' => 'This sort rule is currently used on :count book(s). Are you sure you want to delete this?', + 'sort_rule_delete_warn_default' => 'This sort rule is currently used as the default for books. Are you sure you want to delete this?', + 'sort_rule_details' => 'Sort Rule Details', + 'sort_rule_details_desc' => 'Set a name for this sort rule, which will appear in lists when users are selecting a sort.', + 'sort_rule_operations' => 'Sort Operations', + 'sort_rule_operations_desc' => 'Configure the sort actions to be performed by moving them from the list of available operations. Upon use, the operations will be applied in order, from top to bottom. Any changes made here will be applied to all assigned books upon save.', + 'sort_rule_available_operations' => 'Available Operations', + 'sort_rule_available_operations_empty' => 'No operations remaining', + 'sort_rule_configured_operations' => 'Configured Operations', + 'sort_rule_configured_operations_empty' => 'Drag/add operations from the "Available Operations" list', + 'sort_rule_op_asc' => '(Asc)', + 'sort_rule_op_desc' => '(Desc)', + 'sort_rule_op_name' => 'Name - Alphabetical', + 'sort_rule_op_name_numeric' => '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_last' => 'Chapters Last', + + // Maintenance settings + 'maint' => 'Viðhald', + 'maint_image_cleanup' => 'Taka til í myndum', + 'maint_image_cleanup_desc' => 'Scans page & revision content to check which images and drawings are currently in use and which images are redundant. Ensure you create a full database and image backup before running this.', + 'maint_delete_images_only_in_revisions' => 'Also delete images that only exist in old page revisions', + 'maint_image_cleanup_run' => 'Keyra hreinsun', + 'maint_image_cleanup_warning' => ':count potentially unused images were found. Are you sure you want to delete these images?', + 'maint_image_cleanup_success' => ':count potentially unused images found and deleted!', + 'maint_image_cleanup_nothing_found' => 'Engar ónotaðar myndir fundust, engu eytt!', + 'maint_send_test_email' => 'Senda prufu tölvupóst', + 'maint_send_test_email_desc' => 'Þessi aðgerð sendir prufu tölvupóst á netfangið sem stillt er á í prófílnum þínum.', + 'maint_send_test_email_run' => 'Senda prufu tölvupóst', + 'maint_send_test_email_success' => 'Tölvupóstur sendur á :address', + 'maint_send_test_email_mail_subject' => 'Prufupóstur', + 'maint_send_test_email_mail_greeting' => 'Það virðist virka að senda tölvupóst!', + 'maint_send_test_email_mail_text' => 'Congratulations! As you received this email notification, your email settings seem to be configured properly.', + 'maint_recycle_bin_desc' => 'Deleted shelves, books, chapters & pages are sent to the recycle bin so they can be restored or permanently deleted. Older items in the recycle bin may be automatically removed after a while depending on system configuration.', + 'maint_recycle_bin_open' => 'Opna ruslatunnu', + 'maint_regen_references' => 'Regenerate References', + 'maint_regen_references_desc' => 'This action will rebuild the cross-item reference index within the database. This is usually handled automatically but this action can be useful to index old content or content added via unofficial methods.', + 'maint_regen_references_success' => 'Reference index has been regenerated!', + 'maint_timeout_command_note' => 'Note: This action can take time to run, which can lead to timeout issues in some web environments. As an alternative, this action be performed using a terminal command.', + + // Recycle Bin + 'recycle_bin' => 'Ruslatunna', + 'recycle_bin_desc' => 'Here you can restore items that have been deleted or choose to permanently remove them from the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.', + 'recycle_bin_deleted_item' => 'Eyddur hlutur', + 'recycle_bin_deleted_parent' => 'Foreldri', + 'recycle_bin_deleted_by' => 'Eytt af', + 'recycle_bin_deleted_at' => 'Eytt þann', + 'recycle_bin_permanently_delete' => 'Eyða varanlega', + 'recycle_bin_restore' => 'Endurheimta', + 'recycle_bin_contents_empty' => 'Ruslatunnan er tóm', + 'recycle_bin_empty' => 'Tæma ruslatunnu', + 'recycle_bin_empty_confirm' => 'This will permanently destroy all items in the recycle bin including content contained within each item. Are you sure you want to empty the recycle bin?', + 'recycle_bin_destroy_confirm' => 'This action will permanently delete this item from the system, along with any child elements listed below, and you will not be able to restore this content. Are you sure you want to permanently delete this item?', + 'recycle_bin_destroy_list' => 'Hlutir til eyðingar', + 'recycle_bin_restore_list' => 'Hlutir til endurheimtar', + 'recycle_bin_restore_confirm' => 'This action will restore the deleted item, including any child elements, to their original location. If the original location has since been deleted, and is now in the recycle bin, the parent item will also need to be restored.', + 'recycle_bin_restore_deleted_parent' => 'The parent of this item has also been deleted. These will remain deleted until that parent is also restored.', + 'recycle_bin_restore_parent' => 'Endurheimta foreldri', + 'recycle_bin_destroy_notification' => 'Deleted :count total items from the recycle bin.', + 'recycle_bin_restore_notification' => 'Restored :count total items from the recycle bin.', + + // Audit Log + 'audit' => 'Ferilskrá', + 'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.', + 'audit_event_filter' => 'Atburðasía', + 'audit_event_filter_no_filter' => 'Engin sía', + 'audit_deleted_item' => 'Eyddur hlutur', + 'audit_deleted_item_name' => 'Nafn :name', + 'audit_table_user' => 'Notandi', + 'audit_table_event' => 'Atburður', + 'audit_table_related' => 'Tengdur hlutur eða lýsing', + 'audit_table_ip' => 'IP tala', + 'audit_table_date' => 'Dagsetning virkni', + 'audit_date_from' => 'Dagsetning frá', + 'audit_date_to' => 'Dagsetning til', + + // Role Settings + 'roles' => 'Hlutverk', + 'role_user_roles' => 'Notanda hlutverk', + 'roles_index_desc' => 'Roles are used to group users & provide system permission to their members. When a user is a member of multiple roles the privileges granted will stack and the user will inherit all abilities.', + 'roles_x_users_assigned' => ':count user assigned|:count users assigned', + 'roles_x_permissions_provided' => ':count permission|:count permissions', + 'roles_assigned_users' => 'Skilgreindir notendur', + 'roles_permissions_provided' => 'Uppgefnar heimildir', + 'role_create' => 'Búa til nýtt hlutverk', + 'role_delete' => 'Eyða hlutverki', + 'role_delete_confirm' => 'This will delete the role with the name \':roleName\'.', + 'role_delete_users_assigned' => 'This role has :userCount users assigned to it. If you would like to migrate the users from this role select a new role below.', + 'role_delete_no_migration' => "Don't migrate users", + 'role_delete_sure' => 'Are you sure you want to delete this role?', + 'role_edit' => 'Breyta hlutverki', + 'role_details' => 'Lýsing á hlutverki', + 'role_name' => 'Nafn hlutverks', + 'role_desc' => 'Stutt lýsing á hlutverki', + 'role_mfa_enforced' => 'Krefst tvöfaldrar auðkenningar', + 'role_external_auth_id' => 'Ytri auðkenningarnúmer', + 'role_system' => 'Réttindastillingar kerfis', + 'role_manage_users' => 'Sýsla með notendur', + 'role_manage_roles' => 'Stýra hlutverkum og réttindum hlutverka', + 'role_manage_entity_permissions' => 'Stýra öllum bóka, kafla og síðu réttindum', + 'role_manage_own_entity_permissions' => 'Stýra réttindum á eigin bókum, köflum og síðum', + 'role_manage_page_templates' => 'Stýra síðu sníðmátum', + 'role_access_api' => 'Access system API', + 'role_manage_settings' => 'Manage app settings', + 'role_export_content' => 'Flytja út efni', + 'role_import_content' => 'Flytja inn efni', + 'role_editor_change' => 'Skipta um ritil síðu', + 'role_notifications' => 'Receive & manage notifications', + 'role_asset' => 'Asset Permissions', + 'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.', + 'role_asset_desc' => 'These permissions control default access to the assets within the system. Permissions on Books, Chapters and Pages will override these permissions.', + 'role_asset_admins' => 'Admins are automatically given access to all content but these options may show or hide UI options.', + 'role_asset_image_view_note' => 'This relates to visibility within the image manager. Actual access of uploaded image files will be dependant upon system image storage option.', + 'role_all' => 'Allt', + 'role_own' => 'Eigin', + 'role_controlled_by_asset' => 'Controlled by the asset they are uploaded to', + 'role_save' => 'Vista hlutverk', + 'role_users' => 'Notendur í þessu hlutverki', + 'role_users_none' => 'Engir notendur eru eins og er í þessu hlutverki', + + // Users + 'users' => 'Notendur', + 'users_index_desc' => 'Create & manage individual user accounts within the system. User accounts are used for login and attribution of content & activity. Access permissions are primarily role-based but user content ownership, among other factors, may also affect permissions & access.', + 'user_profile' => 'Prófíll notanda', + 'users_add_new' => 'Bæta við nýjum notanda', + 'users_search' => 'Leita að notendum', + 'users_latest_activity' => 'Síðasta virkni', + 'users_details' => 'Notendaupplýsingar', + 'users_details_desc' => 'Set a display name and an email address for this user. The email address will be used for logging into the application.', + 'users_details_desc_no_email' => 'Set a display name for this user so others can recognise them.', + 'users_role' => 'Hlutverk notenda', + 'users_role_desc' => 'Select which roles this user will be assigned to. If a user is assigned to multiple roles the permissions from those roles will stack and they will receive all abilities of the assigned roles.', + 'users_password' => 'Lykilorð notanda', + 'users_password_desc' => 'Setja lykilorð sem þú notar til að skrá þig inn í hugbúnaðinn. Lykilorðið verður að vera a. m. k 8 stafa langt.', + 'users_send_invite_text' => 'You can choose to send this user an invitation email which allows them to set their own password otherwise you can set their password yourself.', + 'users_send_invite_option' => 'Senda boð á notanda með tölvupósti', + 'users_external_auth_id' => 'Ytra auðkenningar númer', + 'users_external_auth_id_desc' => 'When an external authentication system is in use (such as SAML2, OIDC or LDAP) this is the ID which links this BookStack user to the authentication system account. You can ignore this field if using the default email-based authentication.', + 'users_password_warning' => 'Only fill the below if you would like to change the password for this user.', + 'users_system_public' => 'This user represents any guest users that visit your instance. It cannot be used to log in but is assigned automatically.', + 'users_delete' => 'Eyða notanda', + 'users_delete_named' => 'Eyða notanda :userName', + 'users_delete_warning' => 'This will fully delete this user with the name \':userName\' from the system.', + 'users_delete_confirm' => 'Ertu viss um að þú viljir eyða þessum notanda?', + 'users_migrate_ownership' => 'Færa eignarhald', + 'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.', + 'users_none_selected' => 'Engin notandi valin', + 'users_edit' => 'Breyta notanda', + 'users_edit_profile' => 'Breyta prófíl', + 'users_avatar' => 'Avatar notanda', + 'users_avatar_desc' => 'Select an image to represent this user. This should be approx 256px square.', + 'users_preferred_language' => 'Valið tungumál', + 'users_preferred_language_desc' => 'This option will change the language used for the user-interface of the application. This will not affect any user-created content.', + 'users_social_accounts' => 'Samfélagsmiðla reikningar', + 'users_social_accounts_desc' => 'View the status of the connected social accounts for this user. Social accounts can be used in addition to the primary authentication system for system access.', + 'users_social_accounts_info' => 'Here you can connect your other accounts for quicker and easier login. Disconnecting an account here does not revoke previously authorized access. Revoke access from your profile settings on the connected social account.', + 'users_social_connect' => 'Tengja aðgang', + 'users_social_disconnect' => 'Aftengja aðgang', + 'users_social_status_connected' => 'Tengt', + 'users_social_status_disconnected' => 'Aftengt', + 'users_social_connected' => ':socialAccount account was successfully attached to your profile.', + 'users_social_disconnected' => ':socialAccount account was successfully disconnected from your profile.', + 'users_api_tokens' => 'API tókar', + 'users_api_tokens_desc' => 'Create and manage the access tokens used to authenticate with the BookStack REST API. Permissions for the API are managed via the user that the token belongs to.', + 'users_api_tokens_none' => 'No API tokens have been created for this user', + 'users_api_tokens_create' => 'Búa til tóka', + 'users_api_tokens_expires' => 'Rennur út', + 'users_api_tokens_docs' => 'API Documentation', + 'users_mfa' => 'Multi-Factor Authentication', + 'users_mfa_desc' => 'Setup multi-factor authentication as an extra layer of security for your user account.', + 'users_mfa_x_methods' => ':count method configured|:count methods configured', + 'users_mfa_configure' => 'Configure Methods', + + // API Tokens + 'user_api_token_create' => 'Búa til API tóka', + 'user_api_token_name' => 'Nafn', + 'user_api_token_name_desc' => 'Give your token a readable name as a future reminder of its intended purpose.', + 'user_api_token_expiry' => 'Rennur út þann', + 'user_api_token_expiry_desc' => 'Set a date at which this token expires. After this date, requests made using this token will no longer work. Leaving this field blank will set an expiry 100 years into the future.', + 'user_api_token_create_secret_message' => 'Immediately after creating this token a "Token ID" & "Token Secret" will be generated and displayed. The secret will only be shown a single time so be sure to copy the value to somewhere safe and secure before proceeding.', + 'user_api_token' => 'API tóki', + 'user_api_token_id' => 'Númer tóka', + 'user_api_token_id_desc' => 'This is a non-editable system generated identifier for this token which will need to be provided in API requests.', + 'user_api_token_secret' => 'Tóka leyndarmál', + 'user_api_token_secret_desc' => 'This is a system generated secret for this token which will need to be provided in API requests. This will only be displayed this one time so copy this value to somewhere safe and secure.', + 'user_api_token_created' => 'Tóki búinn til :timeAgo', + 'user_api_token_updated' => 'Tóki uppfærður :timeAgo', + 'user_api_token_delete' => 'Eyða tóka', + 'user_api_token_delete_warning' => 'This will fully delete this API token with the name \':tokenName\' from the system.', + 'user_api_token_delete_confirm' => 'Are you sure you want to delete this API token?', + + // Webhooks + 'webhooks' => 'Webhooks', + 'webhooks_index_desc' => 'Webhooks are a way to send data to external URLs when certain actions and events occur within the system which allows event-based integration with external platforms such as messaging or notification systems.', + 'webhooks_x_trigger_events' => ':count trigger event|:count trigger events', + 'webhooks_create' => 'Búa til nýjann Webhook', + 'webhooks_none_created' => 'Engir Webhooks hafa verið búnir til.', + 'webhooks_edit' => 'Breyta Webhook', + 'webhooks_save' => 'Vista Webhook', + 'webhooks_details' => 'Upplýsingar um Webhook', + 'webhooks_details_desc' => 'Provide a user friendly name and a POST endpoint as a location for the webhook data to be sent to.', + 'webhooks_events' => 'Webhook atburðir', + 'webhooks_events_desc' => 'Select all the events that should trigger this webhook to be called.', + 'webhooks_events_warning' => 'Keep in mind that these events will be triggered for all selected events, even if custom permissions are applied. Ensure that use of this webhook won\'t expose confidential content.', + 'webhooks_events_all' => 'Allir kerfis atburðir', + 'webhooks_name' => 'Webhook nafn', + 'webhooks_timeout' => 'Webhook tímamörk (Sekúndur)', + 'webhooks_endpoint' => 'Webhook endapunktur', + 'webhooks_active' => 'Webhook virkur', + 'webhook_events_table_header' => 'Atburðir', + 'webhooks_delete' => 'Eyða Webhook', + 'webhooks_delete_warning' => 'Þessi aðgerð mun eyða þessum Webhook að fullu \':webhookName\',.', + 'webhooks_delete_confirm' => 'Ertu viss um að þú viljir eyða þessum Webhook?', + 'webhooks_format_example' => 'Dæmi um snið á Webhook', + 'webhooks_format_example_desc' => 'Webhook data is sent as a POST request to the configured endpoint as JSON following the format below. The "related_item" and "url" properties are optional and will depend on the type of event triggered.', + 'webhooks_status' => 'Staða á Webhook', + 'webhooks_last_called' => 'Síðast kallað:', + 'webhooks_last_errored' => 'Síðasta villa kom upp:', + 'webhooks_last_error_message' => 'Síðustu villuskilaboð:', + + // Licensing + 'licenses' => 'Leyfi', + 'licenses_desc' => 'This page details license information for BookStack in addition to the projects & libraries that are used within BookStack. Many projects listed may only be used in a development context.', + 'licenses_bookstack' => 'Bookstack leyfi', + 'licenses_php' => 'PHP kóðasafnsleyfi', + 'licenses_js' => 'Javascript kóðasafnsleyfi', + 'licenses_other' => 'Önnur leyfi', + 'license_details' => 'Upplýsingar um leyfi', + + //! If editing translations files directly please ignore this in all + //! languages apart from en. Content will be auto-copied from en. + //!//////////////////////////////// + 'language_select' => [ + 'en' => 'English', + 'ar' => 'العربية', + 'bg' => 'Bǎlgarski', + 'bs' => 'Bosanski', + 'ca' => 'Català', + 'cs' => 'Česky', + 'cy' => 'Cymraeg', + 'da' => 'Dansk', + 'de' => 'Deutsch (Sie)', + 'de_informal' => 'Deutsch (Du)', + 'el' => 'ελληνικά', + 'es' => 'Español', + 'es_AR' => 'Español Argentina', + 'et' => 'Eesti keel', + 'eu' => 'Euskara', + 'fa' => 'فارسی', + 'fi' => 'Suomi', + 'fr' => 'Français', + 'he' => 'עברית', + 'hr' => 'Hrvatski', + 'hu' => 'Magyar', + 'id' => 'Bahasa Indonesia', + 'it' => 'Italian', + 'ja' => '日本語', + 'ko' => '한국어', + 'lt' => 'Lietuvių Kalba', + 'lv' => 'Latviešu Valoda', + 'nb' => 'Norsk (Bokmål)', + 'ne' => 'नेपाली', + 'nn' => 'Nynorsk', + 'nl' => 'Nederlands', + 'pl' => 'Polski', + 'pt' => 'Português', + 'pt_BR' => 'Português do Brasil', + 'ro' => 'Română', + 'ru' => 'Русский', + 'sk' => 'Slovensky', + 'sl' => 'Slovenščina', + 'sv' => 'Svenska', + 'tr' => 'Türkçe', + 'uk' => 'Українська', + 'uz' => 'O‘zbekcha', + 'vi' => 'Tiếng Việt', + 'zh_CN' => '简体中文', + 'zh_TW' => '繁體中文', + ], + //!//////////////////////////////// +]; diff --git a/lang/is/validation.php b/lang/is/validation.php new file mode 100644 index 00000000000..9183d27cf53 --- /dev/null +++ b/lang/is/validation.php @@ -0,0 +1,122 @@ + 'Það verður að samþykkja :attribute.', + 'active_url' => ':attribute Er ekki gilt vistfang.', + 'after' => ':attribute verður að vera dagsetning eftir :date.', + 'alpha' => ':attribute má eingöngu innihalda stafi.', + 'alpha_dash' => ':attribute má eingöngu innihalda stafi, tölustafi, strik og undirstrik.', + 'alpha_num' => ':attribute má eingöngu innihalda stafi og tölustafi.', + 'array' => 'The :attribute must be an array.', + 'backup_codes' => 'Kóðinn sem þú gafst upp er ekki gildur eða hefur þegar verið notaður.', + 'before' => ':attribute verður að vera dagsetning á undan :date.', + 'between' => [ + 'numeric' => ':attribute verður að vera á milli :min og :max.', + 'file' => ':attribute verður að vera á milli :min og :max kílóbæti.', + 'string' => ':attribute verður að vera á milli :min og :max stafir.', + 'array' => ':attribute verður að vera á milli :min og :max fjöldi.', + ], + 'boolean' => ':attribute gildið verður að vera true og false.', + 'confirmed' => 'The :attribute confirmation does not match.', + 'date' => 'The :attribute is not a valid date.', + 'date_format' => 'The :attribute does not match the format :format.', + 'different' => 'The :attribute and :other must be different.', + 'digits' => 'The :attribute must be :digits digits.', + 'digits_between' => 'The :attribute must be between :min and :max digits.', + 'email' => 'The :attribute must be a valid email address.', + 'ends_with' => 'The :attribute must end with one of the following: :values', + 'file' => 'The :attribute must be provided as a valid file.', + 'filled' => 'The :attribute field is required.', + 'gt' => [ + 'numeric' => 'The :attribute must be greater than :value.', + 'file' => 'The :attribute must be greater than :value kilobytes.', + 'string' => 'The :attribute must be greater than :value characters.', + 'array' => 'The :attribute must have more than :value items.', + ], + 'gte' => [ + 'numeric' => 'The :attribute must be greater than or equal :value.', + 'file' => 'The :attribute must be greater than or equal :value kilobytes.', + 'string' => 'The :attribute must be greater than or equal :value characters.', + 'array' => 'The :attribute must have :value items or more.', + ], + 'exists' => 'The selected :attribute is invalid.', + 'image' => 'The :attribute must be an image.', + 'image_extension' => 'The :attribute must have a valid & supported image extension.', + 'in' => 'The selected :attribute is invalid.', + 'integer' => 'The :attribute must be an integer.', + 'ip' => 'The :attribute must be a valid IP address.', + 'ipv4' => 'The :attribute must be a valid IPv4 address.', + 'ipv6' => 'The :attribute must be a valid IPv6 address.', + 'json' => 'The :attribute must be a valid JSON string.', + 'lt' => [ + 'numeric' => 'The :attribute must be less than :value.', + 'file' => 'The :attribute must be less than :value kilobytes.', + 'string' => 'The :attribute must be less than :value characters.', + 'array' => 'The :attribute must have less than :value items.', + ], + 'lte' => [ + 'numeric' => 'The :attribute must be less than or equal :value.', + 'file' => 'The :attribute must be less than or equal :value kilobytes.', + 'string' => 'The :attribute must be less than or equal :value characters.', + 'array' => 'The :attribute must not have more than :value items.', + ], + 'max' => [ + 'numeric' => 'The :attribute may not be greater than :max.', + 'file' => 'The :attribute may not be greater than :max kilobytes.', + 'string' => 'The :attribute may not be greater than :max characters.', + 'array' => 'The :attribute may not have more than :max items.', + ], + 'mimes' => 'The :attribute must be a file of type: :values.', + 'min' => [ + 'numeric' => 'The :attribute must be at least :min.', + 'file' => 'The :attribute must be at least :min kilobytes.', + 'string' => 'The :attribute must be at least :min characters.', + 'array' => 'The :attribute must have at least :min items.', + ], + 'not_in' => 'The selected :attribute is invalid.', + 'not_regex' => 'The :attribute format is invalid.', + 'numeric' => 'The :attribute must be a number.', + 'regex' => 'The :attribute format is invalid.', + 'required' => 'The :attribute field is required.', + 'required_if' => 'The :attribute field is required when :other is :value.', + 'required_with' => 'The :attribute field is required when :values is present.', + 'required_with_all' => 'The :attribute field is required when :values is present.', + 'required_without' => 'The :attribute field is required when :values is not present.', + 'required_without_all' => 'The :attribute field is required when none of :values are present.', + 'same' => 'The :attribute and :other must match.', + 'safe_url' => 'The provided link may not be safe.', + 'size' => [ + 'numeric' => 'The :attribute must be :size.', + 'file' => 'The :attribute must be :size kilobytes.', + 'string' => 'The :attribute must be :size characters.', + 'array' => 'The :attribute must contain :size items.', + ], + 'string' => 'The :attribute must be a string.', + 'timezone' => 'The :attribute must be a valid zone.', + 'totp' => 'The provided code is not valid or has expired.', + 'unique' => 'The :attribute has already been taken.', + 'url' => 'The :attribute format is invalid.', + 'uploaded' => 'The file could not be uploaded. The server may not accept files of this size.', + + 'zip_file' => 'The :attribute needs to reference a file within the ZIP.', + 'zip_file_mime' => 'The :attribute needs to reference a file of type :validTypes, found :foundType.', + 'zip_model_expected' => 'Data object expected but ":type" found.', + 'zip_unique' => 'The :attribute must be unique for the object type within the ZIP.', + + // Custom validation lines + 'custom' => [ + 'password-confirm' => [ + 'required_with' => 'Staðfestingu á lykilorði er krafist', + ], + ], + + // Custom validation attributes + 'attributes' => [], +]; diff --git a/lang/it/activities.php b/lang/it/activities.php index ba07709c082..d682ccca6e0 100644 --- a/lang/it/activities.php +++ b/lang/it/activities.php @@ -6,7 +6,7 @@ return [ // Pages - 'page_create' => 'pagina creata', + 'page_create' => 'ha creato la pagina', 'page_create_notification' => 'Pagina creata con successo', 'page_update' => 'ha aggiornato la pagina', 'page_update_notification' => 'Pagina aggiornata con successo', @@ -14,7 +14,7 @@ 'page_delete_notification' => 'Pagina eliminata con successo', 'page_restore' => 'ha ripristinato la pagina', 'page_restore_notification' => 'Pagina ripristinata con successo', - 'page_move' => 'ha mosso la pagina', + 'page_move' => 'ha spostato la pagina', 'page_move_notification' => 'Pagina spostata con successo', // Chapters @@ -30,28 +30,28 @@ // Books 'book_create' => 'ha creato il libro', 'book_create_notification' => 'Libro creato con successo', - 'book_create_from_chapter' => 'convertito da capitolo a libro', - 'book_create_from_chapter_notification' => 'Capitolo convertito con successo in libro', + 'book_create_from_chapter' => 'ha convertito da capitolo a libro', + 'book_create_from_chapter_notification' => 'Capitolo convertito in libro con successo', 'book_update' => 'ha aggiornato il libro', 'book_update_notification' => 'Libro aggiornato con successo', 'book_delete' => 'ha eliminato il libro', 'book_delete_notification' => 'Libro eliminato con successo', 'book_sort' => 'ha ordinato il libro', - 'book_sort_notification' => 'Libro reindicizzato con successo', + 'book_sort_notification' => 'Libro riordinato con successo', // Bookshelves - 'bookshelf_create' => 'libreria creata', + 'bookshelf_create' => 'ha creato la libreria', 'bookshelf_create_notification' => 'Libreria creata con successo', - 'bookshelf_create_from_book' => 'libro convertito in libreria', - 'bookshelf_create_from_book_notification' => 'Libro convertito con successo in libreria', - 'bookshelf_update' => 'libreria aggiornata', + 'bookshelf_create_from_book' => 'ha convertito il libro in libreria', + 'bookshelf_create_from_book_notification' => 'Libro convertito in libreria con successo', + 'bookshelf_update' => 'ha aggiornato la libreria', 'bookshelf_update_notification' => 'Libreria aggiornata con successo', - 'bookshelf_delete' => 'Iibreria eliminata', + 'bookshelf_delete' => 'ha eliminato la libreria', 'bookshelf_delete_notification' => 'Libreria eliminata con successo', // Revisions - 'revision_restore' => 'revisione ripristinata', - 'revision_delete' => 'revisione eliminata', + 'revision_restore' => 'ha ripristinato la revisione', + 'revision_delete' => 'ha eliminato la revisione', 'revision_delete_notification' => 'Revisione eliminata con successo', // Favourites @@ -64,61 +64,77 @@ // Auth 'auth_login' => 'connesso', 'auth_register' => 'registrato come nuovo utente', - 'auth_password_reset_request' => 'richiesta di reimpostazione della password utente', - 'auth_password_reset_update' => 'reimposta password utente', - 'mfa_setup_method' => 'metodo MFA configurato', + 'auth_password_reset_request' => 'ha richiesto di reimpostare la password utente', + 'auth_password_reset_update' => 'ha reimpostato la password utente', + 'mfa_setup_method' => 'ha configurato un metodo multi-fattore', 'mfa_setup_method_notification' => 'Metodo multi-fattore impostato con successo', - 'mfa_remove_method' => 'metodo MFA rimosso', + 'mfa_remove_method' => 'ha rimosso un metodo multi-fattore', 'mfa_remove_method_notification' => 'Metodo multi-fattore rimosso con successo', // Settings - 'settings_update' => 'impostazioni aggiornate', + 'settings_update' => 'ha aggiornato le impostazioni', 'settings_update_notification' => 'Impostazioni aggiornate con successo', - 'maintenance_action_run' => 'eseguita azione di manutenzione', + 'maintenance_action_run' => 'ha eseguito un\'azione di manutenzione', // Webhooks - 'webhook_create' => 'webhook creato', + 'webhook_create' => 'ha creato un webhook', 'webhook_create_notification' => 'Webhook creato con successo', - 'webhook_update' => 'webhook aggiornato', + 'webhook_update' => 'ha aggiornato un webhook', 'webhook_update_notification' => 'Webhook aggiornato con successo', - 'webhook_delete' => 'webhook eliminato', + 'webhook_delete' => 'ha eliminato un webhook', 'webhook_delete_notification' => 'Webhook eliminato con successo', + // Imports + 'import_create' => 'importazione creata', + 'import_create_notification' => 'Importazione caricata con successo', + 'import_run' => 'importazione aggiornata', + 'import_run_notification' => 'Contenuto importato con successo', + 'import_delete' => 'importazione cancellata', + 'import_delete_notification' => 'Importazione eliminata con successo', + // Users - 'user_create' => 'utente creato', + 'user_create' => 'ha creato un utente', 'user_create_notification' => 'Utente creato con successo', - 'user_update' => 'utente aggiornato', + 'user_update' => 'ha aggiornato un utente', 'user_update_notification' => 'Utente aggiornato con successo', - 'user_delete' => 'utente eliminato', + 'user_delete' => 'ha eliminato un utente', 'user_delete_notification' => 'Utente rimosso con successo', // API Tokens - 'api_token_create' => 'token api creato', + 'api_token_create' => 'ha creato un token API', 'api_token_create_notification' => 'Token API creato con successo', - 'api_token_update' => 'token api aggiornato', + 'api_token_update' => 'ha aggiornato un token API', 'api_token_update_notification' => 'Token API aggiornato correttamente', - 'api_token_delete' => 'token api eliminato', + 'api_token_delete' => 'ha eliminato token API', 'api_token_delete_notification' => 'Token API eliminato con successo', // Roles - 'role_create' => 'creato ruolo', + 'role_create' => 'ha creato un ruolo', 'role_create_notification' => 'Ruolo creato con successo', - 'role_update' => 'aggiornato ruolo', + 'role_update' => 'ha aggiornato un ruolo', 'role_update_notification' => 'Ruolo aggiornato con successo', - 'role_delete' => 'eliminato ruolo', + 'role_delete' => 'ha eliminato un ruolo', 'role_delete_notification' => 'Ruolo eliminato con successo', // Recycle Bin - 'recycle_bin_empty' => 'cestino svuotato', - 'recycle_bin_restore' => 'ripristinato dal cestino', - 'recycle_bin_destroy' => 'rimosso dal cestino', + 'recycle_bin_empty' => 'ha svuotato il cestino', + 'recycle_bin_restore' => 'ha ripristinato dal cestino', + 'recycle_bin_destroy' => 'ha rimosso dal cestino', // Comments 'commented_on' => 'ha commentato in', - 'comment_create' => 'commento aggiunto', - 'comment_update' => 'commento aggiornato', - 'comment_delete' => 'commento rimosso', + 'comment_create' => 'ha aggiunto un commento', + 'comment_update' => 'ha aggiornato un commento', + 'comment_delete' => 'ha rimosso un commento', + + // Sort Rules + 'sort_rule_create' => 'regola di ordinamento creata', + 'sort_rule_create_notification' => 'Ordina regola creata con successo', + 'sort_rule_update' => 'regola di ordinamento aggiornata', + 'sort_rule_update_notification' => 'Regola di ordinamento aggiornata con successo', + 'sort_rule_delete' => 'regola di ordinamento eliminata', + 'sort_rule_delete_notification' => 'Regola di ordinamento eliminata con successo', // Other - 'permissions_update' => 'autorizzazioni aggiornate', + 'permissions_update' => 'ha aggiornate le autorizzazioni', ]; diff --git a/lang/it/auth.php b/lang/it/auth.php index b9bc685aac4..9191ecba794 100644 --- a/lang/it/auth.php +++ b/lang/it/auth.php @@ -20,68 +20,68 @@ 'username' => 'Username', 'email' => 'Email', 'password' => 'Password', - 'password_confirm' => 'Conferma Password', + 'password_confirm' => 'Conferma password', 'password_hint' => 'Deve essere lunga almeno 8 caratteri', 'forgot_password' => 'Password dimenticata?', 'remember_me' => 'Ricordami', - 'ldap_email_hint' => 'Inserisci un email per usare quest\'account.', - 'create_account' => 'Crea Account', + 'ldap_email_hint' => 'Inserisci un\'email per usare quest\'account.', + 'create_account' => 'Crea un account', 'already_have_account' => 'Hai già un account?', 'dont_have_account' => 'Non hai un account?', 'social_login' => 'Login Social', 'social_registration' => 'Registrazione Social', - 'social_registration_text' => 'Registrati usando un altro servizio.', + 'social_registration_text' => 'Registrati e accedi utilizzando un altro servizio.', 'register_thanks' => 'Grazie per esserti registrato!', - 'register_confirm' => 'Controlla la tua mail e clicca il bottone di conferma per accedere a :appName.', - 'registrations_disabled' => 'La registrazione è disabilitata', - 'registration_email_domain_invalid' => 'Questo dominio della mail non ha accesso a questa applicazione', - 'register_success' => 'Grazie per la registrazione! Sei registrato e loggato.', + 'register_confirm' => 'Controlla la tua mail e clicca il pulsante di conferma per accedere a :appName.', + 'registrations_disabled' => 'La registrazione è disabilitata al momento', + 'registration_email_domain_invalid' => 'Questo dominio email non ha accesso a questa applicazione', + 'register_success' => 'Grazie per la registrazione! Sei registrato e connesso.', // Login auto-initiation 'auto_init_starting' => 'Tentativo di accesso', - 'auto_init_starting_desc' => 'Stiamo contattando il vostro sistema di autenticazione per avviare il processo di login. Se dopo 5 secondi non si verifica alcun progresso, si può provare a fare clic sul link sottostante.', + 'auto_init_starting_desc' => 'Stiamo contattando il tuo sistema di autenticazione per avviare il processo di login. Se non ci sono progressi dopo 5 secondi, puoi provare a cliccare sul link qui sotto.', 'auto_init_start_link' => 'Procedi con l\'autenticazione', // Password Reset - 'reset_password' => 'Reimposta Password', - 'reset_password_send_instructions' => 'Inserisci il tuo indirizzo sotto e ti verrà inviata una mail contenente un link per resettare la tua password.', - 'reset_password_send_button' => 'Invia Link Reset', - 'reset_password_sent' => 'Un link di reset della password verrà inviato a :email se la mail verrà trovata nel sistema.', - 'reset_password_success' => 'La tua password è stata resettata correttamente.', + 'reset_password' => 'Reimposta la password', + 'reset_password_send_instructions' => 'Inserisci il tuo indirizzo e ti verrà inviata una mail contenente un link per reimpostare la tua password.', + 'reset_password_send_button' => 'Invia link di ripristino', + 'reset_password_sent' => 'Se la mail verrà trovata nel sistema, verrà inviato a :email un link di ripristino della password.', + 'reset_password_success' => 'La tua password è stata ripristinata correttamente.', 'email_reset_subject' => 'Reimposta la password di :appName', - 'email_reset_text' => 'Stai ricevendo questa mail perché abbiamo ricevuto una richiesta di reset della password per il tuo account.', - 'email_reset_not_requested' => 'Se non hai richiesto un reset della password, ignora questa mail.', + 'email_reset_text' => 'Stai ricevendo questa mail perché abbiamo ricevuto una richiesta di ripristino della password per il tuo account.', + 'email_reset_not_requested' => 'Se non hai richiesto un ripristino della password, ignora questa mail.', // Email Confirmation 'email_confirm_subject' => 'Conferma email per :appName', 'email_confirm_greeting' => 'Grazie per esserti registrato a :appName!', 'email_confirm_text' => 'Conferma il tuo indirizzo email cliccando il pulsante sotto:', - 'email_confirm_action' => 'Conferma Email', + 'email_confirm_action' => 'Conferma email', 'email_confirm_send_error' => 'La conferma della mail è richiesta ma non è stato possibile mandare la mail. Contatta l\'amministratore.', 'email_confirm_success' => 'La tua email è stata confermata! Ora dovresti essere in grado di effettuare il login utilizzando questo indirizzo email.', 'email_confirm_resent' => 'Mail di conferma reinviata, controlla la tua posta.', 'email_confirm_thanks' => 'Grazie per la conferma!', - 'email_confirm_thanks_desc' => 'Attendere un momento mentre la conferma viene gestita. Se non si è reindirizzati dopo 3 secondi, premere il link "Continua" qui sotto per procedere.', + 'email_confirm_thanks_desc' => 'Attendi un momento mentre la conferma viene gestita. Se non vieni reindirizzato dopo 3 secondi, clicca sul link "Continua" qui sotto per procedere.', - 'email_not_confirmed' => 'Indirizzo Email Non Confermato', + 'email_not_confirmed' => 'Indirizzo email non confermato', 'email_not_confirmed_text' => 'Il tuo indirizzo email non è ancora stato confermato.', 'email_not_confirmed_click_link' => 'Clicca il link nella mail mandata subito dopo la tua registrazione.', 'email_not_confirmed_resend' => 'Se non riesci a trovare la mail puoi rimandarla cliccando il pulsante sotto.', - 'email_not_confirmed_resend_button' => 'Reinvia Conferma', + 'email_not_confirmed_resend_button' => 'Reinvia conferma', // User Invite 'user_invite_email_subject' => 'Sei stato invitato a unirti a :appName!', 'user_invite_email_greeting' => 'Un account è stato creato per te su :appName.', 'user_invite_email_text' => 'Clicca sul pulsante qui sotto per impostare una password e ottenere l\'accesso:', - 'user_invite_email_action' => 'Imposta Password', + 'user_invite_email_action' => 'Imposta password', 'user_invite_page_welcome' => 'Benvenuto in :appName!', - 'user_invite_page_text' => 'Per completare il tuo account e ottenere l\'accesso devi impostare una password che verrà utilizzata per accedere a :appName in futuro.', - 'user_invite_page_confirm_button' => 'Conferma Password', + 'user_invite_page_text' => 'Per completare il tuo account e ottenere l\'accesso, devi impostare una password che verrà utilizzata per accedere a :appName in futuro.', + 'user_invite_page_confirm_button' => 'Conferma password', 'user_invite_success_login' => 'Password impostata, ora dovresti essere in grado di effettuare il login utilizzando la password impostata per accedere a :appName!', // Multi-factor Authentication - 'mfa_setup' => 'Imposta Autenticazione Multi-Fattore', + 'mfa_setup' => 'Imposta autenticazione multi-fattore', 'mfa_setup_desc' => 'Imposta l\'autenticazione multi-fattore come misura di sicurezza aggiuntiva per il tuo account.', 'mfa_setup_configured' => 'Già configurata', 'mfa_setup_reconfigure' => 'Riconfigura', @@ -91,13 +91,13 @@ 'mfa_option_totp_title' => 'App mobile', 'mfa_option_totp_desc' => 'Per utilizzare l\'autenticazione multi-fattore avrai bisogno di un\'applicazione mobile che supporti TOTP come Google Authenticator, Authy o Microsoft Authenticator.', 'mfa_option_backup_codes_title' => 'Codici di backup', - 'mfa_option_backup_codes_desc' => 'Salva in modo sicuro una serie di codici di backup monouso che puoi inserire per verificare la tua identità.', + 'mfa_option_backup_codes_desc' => 'Genera un insieme di codici di backup monouso che inserirai al login per verificare la tua identità. Assicurati di conservarli in un luogo sicuro e sicuro.', 'mfa_gen_confirm_and_enable' => 'Conferma e abilita', 'mfa_gen_backup_codes_title' => 'Configurazione codici di backup', 'mfa_gen_backup_codes_desc' => 'Conserva l\'elenco di codici qui sotto in un luogo sicuro. Quando accedi al sistema potrai utilizzare uno dei codici come meccanismo di autenticazione secondario.', 'mfa_gen_backup_codes_download' => 'Scarica codici', 'mfa_gen_backup_codes_usage_warning' => 'Ogni codice può essere utilizzato solo una volta', - 'mfa_gen_totp_title' => 'Impostazione App Mobile', + 'mfa_gen_totp_title' => 'Impostazione app mobile', 'mfa_gen_totp_desc' => 'Per utilizzare l\'autenticazione multi-fattore avrai bisogno di un\'applicazione mobile che supporti TOTP come Google Authenticator, Authy o Microsoft Authenticator.', 'mfa_gen_totp_scan' => 'Scansiona il codice QR qui sotto utilizzando la tua app di autenticazione preferita per iniziare.', 'mfa_gen_totp_verify_setup' => 'Verifica configurazione', diff --git a/lang/it/common.php b/lang/it/common.php index 4501c606cbb..9eba738a4e5 100644 --- a/lang/it/common.php +++ b/lang/it/common.php @@ -20,7 +20,7 @@ 'description' => 'Descrizione', 'role' => 'Ruolo', 'cover_image' => 'Immagine di copertina', - 'cover_image_description' => 'Questa immagine dovrebbe essere approssimativamente 440x250px.', + 'cover_image_description' => 'L\'immagine dovrebbe essere di circa 440x250px, anche se verrà ridimensionata e ritagliata in modo flessibile per adattarsi all\'interfaccia utente in diversi scenari, quindi le dimensioni effettive per la visualizzazione saranno diverse.', // Actions 'actions' => 'Azioni', @@ -30,51 +30,53 @@ 'create' => 'Crea', 'update' => 'Aggiorna', 'edit' => 'Modifica', + 'archive' => 'Archivia', + 'unarchive' => 'Ripristina', 'sort' => 'Ordina', - 'move' => 'Muovi', + 'move' => 'Sposta', 'copy' => 'Copia', 'reply' => 'Rispondi', 'delete' => 'Elimina', - 'delete_confirm' => 'Conferma Eliminazione', + 'delete_confirm' => 'Conferma eliminazione', 'search' => 'Cerca', - 'search_clear' => 'Pulisci Ricerca', + 'search_clear' => 'Pulisci ricerca', 'reset' => 'Azzera', 'remove' => 'Rimuovi', 'add' => 'Aggiungi', 'configure' => 'Configura', 'manage' => 'Gestisci', 'fullscreen' => 'Schermo intero', - 'favourite' => 'Aggiungi ai Preferiti', + 'favourite' => 'Aggiungi ai preferiti', 'unfavourite' => 'Rimuovi dai preferiti', 'next' => 'Successivo', 'previous' => 'Precedente', 'filter_active' => 'Filtro attivo:', 'filter_clear' => 'Pulisci filtro', - 'download' => 'Download', + 'download' => 'Scarica', 'open_in_tab' => 'Apri nella scheda', 'open' => 'Apri', // Sort Options - 'sort_options' => 'Opzioni Ordinamento', - 'sort_direction_toggle' => 'Inverti Direzione Ordinamento', - 'sort_ascending' => 'Ordine Ascendente', - 'sort_descending' => 'Ordine Discendente', + 'sort_options' => 'Opzioni ordinamento', + 'sort_direction_toggle' => 'Inverti direzione ordinamento', + 'sort_ascending' => 'Ordine crescente', + 'sort_descending' => 'Ordine decrescente', 'sort_name' => 'Nome', 'sort_default' => 'Predefinito', - 'sort_created_at' => 'Data Creazione', - 'sort_updated_at' => 'Data Aggiornamento', + 'sort_created_at' => 'Data creazione', + 'sort_updated_at' => 'Data aggiornamento', // Misc - 'deleted_user' => 'Utente Eliminato', + 'deleted_user' => 'Utente eliminato', 'no_activity' => 'Nessuna attività da mostrare', 'no_items' => 'Nessun elemento disponibile', 'back_to_top' => 'Torna in alto', 'skip_to_main_content' => 'Passa al contenuto principale', - 'toggle_details' => 'Mostra Dettagli', - 'toggle_thumbnails' => 'Mostra Miniature', + 'toggle_details' => 'Mostra dettagli', + 'toggle_thumbnails' => 'Mostra miniature', 'details' => 'Dettagli', - 'grid_view' => 'Visualizzazione Griglia', - 'list_view' => 'Visualizzazione Lista', + 'grid_view' => 'Visualizzazione griglia', + 'list_view' => 'Visualizzazione lista', 'default' => 'Predefinito', 'breadcrumb' => 'Navigazione', 'status' => 'Stato', @@ -85,26 +87,29 @@ // Header 'homepage' => 'Homepage', - 'header_menu_expand' => 'Espandi Menù Intestazione', + 'header_menu_expand' => 'Espandi menù intestazione', 'profile_menu' => 'Menù del profilo', - 'view_profile' => 'Visualizza Profilo', - 'edit_profile' => 'Modifica Profilo', - 'dark_mode' => 'Modalità Scura', - 'light_mode' => 'Modalità Chiara', - 'global_search' => 'Ricerca Globale', + 'view_profile' => 'Visualizza profilo', + 'edit_profile' => 'Modifica profilo', + 'dark_mode' => 'Modalità scura', + 'light_mode' => 'Modalità chiara', + 'global_search' => 'Ricerca globale', // Layout tabs - 'tab_info' => 'Info', - 'tab_info_label' => 'Tab: Mostra Informazioni Secondarie', + 'tab_info' => 'Informazioni', + 'tab_info_label' => 'Scheda: Mostra informazioni secondarie', 'tab_content' => 'Contenuto', - 'tab_content_label' => 'Tab: Mostra Contenuto Principale', + 'tab_content_label' => 'Scheda: Mostra contenuto principale', // Email Content - 'email_action_help' => 'Se hai problemi nel cliccare il pulsante ":actionText", copia e incolla lo URL sotto nel tuo browser:', + 'email_action_help' => 'Se hai problemi nel cliccare il pulsante ":actionText", copia e incolla l\'URL qui sotto nel tuo browser:', 'email_rights' => 'Tutti i diritti riservati', // Footer Link Options // Not directly used but available for convenience to users. 'privacy_policy' => 'Norme sulla privacy', - 'terms_of_service' => 'Condizioni del Servizio', + 'terms_of_service' => 'Condizioni del servizio', + + // OpenSearch + 'opensearch_description' => 'Cerca :appName', ]; diff --git a/lang/it/components.php b/lang/it/components.php index b336893b5d2..e067db7033a 100644 --- a/lang/it/components.php +++ b/lang/it/components.php @@ -5,10 +5,10 @@ return [ // Image Manager - 'image_select' => 'Selezione Immagine', - 'image_list' => 'Elenco Immagini', - 'image_details' => 'Dettagli Immagine', - 'image_upload' => 'Carica Immagine', + 'image_select' => 'Seleziona un\'immagine', + 'image_list' => 'Elenco immagini', + 'image_details' => 'Dettagli immagine', + 'image_upload' => 'Carica immagine', 'image_intro' => 'Qui è possibile selezionare e gestire le immagini che sono state precedentemente caricate nel sistema.', 'image_intro_upload' => 'Carica una nuova immagine trascinando un file immagine in questa finestra oppure utilizzando il pulsante "Carica immagine" in alto.', 'image_all' => 'Tutte', @@ -20,27 +20,27 @@ 'image_uploaded_by' => 'Caricato da :userName', 'image_uploaded_to' => 'Caricato su :pageLink', 'image_updated' => 'Aggiornato il :updateDate', - 'image_load_more' => 'Carica Altre', - 'image_image_name' => 'Nome Immagine', + 'image_load_more' => 'Carica altre', + 'image_image_name' => 'Nome immagine', 'image_delete_used' => 'Questa immagine è usata nelle pagine elencate.', 'image_delete_confirm_text' => 'Sei sicuro di voler eliminare questa immagine?', - 'image_select_image' => 'Seleziona Immagine', + 'image_select_image' => 'Seleziona immagine', 'image_dropzone' => 'Rilascia immagini o clicca qui per caricarle', 'image_dropzone_drop' => 'Trascina qui le immagini da caricare', - 'images_deleted' => 'Immagini Eliminate', - 'image_preview' => 'Anteprima Immagine', + 'images_deleted' => 'Immagini eliminate', + 'image_preview' => 'Anteprima immagine', 'image_upload_success' => 'Immagine caricata correttamente', 'image_update_success' => 'Dettagli immagine aggiornati correttamente', 'image_delete_success' => 'Immagine eliminata correttamente', - 'image_replace' => 'Sostituisci Immagine', + 'image_replace' => 'Sostituisci immagine', 'image_replace_success' => 'File immagine aggiornato con successo', - 'image_rebuild_thumbs' => 'Rigenera Variazioni Dimensione', + 'image_rebuild_thumbs' => 'Rigenera variazioni dimensione', 'image_rebuild_thumbs_success' => 'Variazioni di dimensione immagine ricostruite con successo!', // Code Editor - 'code_editor' => 'Modifica Codice', - 'code_language' => 'Linguaggio Codice', - 'code_content' => 'Contenuto Codice', - 'code_session_history' => 'Cronologia Sessione', - 'code_save' => 'Salva Codice', + 'code_editor' => 'Modifica codice', + 'code_language' => 'Linguaggio codice', + 'code_content' => 'Contenuto codice', + 'code_session_history' => 'Cronologia sessione', + 'code_save' => 'Salva codice', ]; diff --git a/lang/it/editor.php b/lang/it/editor.php index 6d3af834347..8e614770836 100644 --- a/lang/it/editor.php +++ b/lang/it/editor.php @@ -13,6 +13,7 @@ 'cancel' => 'Annulla', 'save' => 'Salva', 'close' => 'Chiudi', + 'apply' => 'Applica', 'undo' => 'Annulla', 'redo' => 'Ripeti', 'left' => 'Sinistra', @@ -28,25 +29,26 @@ // Toolbar 'formats' => 'Formati', - 'header_large' => 'Intestazione Grande', - 'header_medium' => 'Intestazione Media', - 'header_small' => 'Intestazione Piccola', - 'header_tiny' => 'Intestazione Minuscola', + 'header_large' => 'Intestazione grande', + 'header_medium' => 'Intestazione media', + 'header_small' => 'Intestazione piccola', + 'header_tiny' => 'Intestazione minuscola', 'paragraph' => 'Paragrafo', 'blockquote' => 'Virgolettato', 'inline_code' => 'Codice in linea', 'callouts' => 'Didascalie', - 'callout_information' => 'Informazioni', + 'callout_information' => 'Informazione', 'callout_success' => 'Fatto', - 'callout_warning' => 'Attenzione', + 'callout_warning' => 'Avviso', 'callout_danger' => 'Pericolo', 'bold' => 'Grassetto', 'italic' => 'Corsivo', 'underline' => 'Sottolineato', - 'strikethrough' => 'Testo barrato', + 'strikethrough' => 'Barrato', 'superscript' => 'Apice', 'subscript' => 'Pedice', 'text_color' => 'Colore del testo', + 'highlight_color' => 'Colore evidenziazione', 'custom_color' => 'Colore personalizzato', 'remove_color' => 'Rimuovi colore', 'background_color' => 'Colore di sfondo', @@ -61,26 +63,29 @@ 'indent_decrease' => 'Riduci rientro', 'table' => 'Tabella', 'insert_image' => 'Inserisci immagine', - 'insert_image_title' => 'Inserisci/Modifica Immagine', - 'insert_link' => 'Inserisci/Modifica Collegamento', - 'insert_link_title' => 'Inserisci/Modifica Collegamento', - 'insert_horizontal_line' => 'Inserisci Riga Orizzontale', + 'insert_image_title' => 'Inserisci/modifica immagine', + 'insert_link' => 'Inserisci/modifica collegamento', + 'insert_link_title' => 'Inserisci/modifica collegamento', + 'insert_horizontal_line' => 'Inserisci riga orizzontale', 'insert_code_block' => 'Inserisci blocco di codice', 'edit_code_block' => 'Modifica blocco di codice', - 'insert_drawing' => 'Inserisci/Modifica Disegno', + 'insert_drawing' => 'Inserisci/modifica disegno', 'drawing_manager' => 'Gestore disegni', 'insert_media' => 'Inserisci/modifica media', - 'insert_media_title' => 'Inserisci/Modifica Media', + 'insert_media_title' => 'Inserisci/modifica media', 'clear_formatting' => 'Cancella formattazione', 'source_code' => 'Codice sorgente', - 'source_code_title' => 'Codice Sorgente', - 'fullscreen' => 'Schermo Intero', + 'source_code_title' => 'Codice sorgente', + 'fullscreen' => 'Schermo intero', 'image_options' => 'Opzioni immagine', // Tables 'table_properties' => 'Proprietà della tabella', 'table_properties_title' => 'Proprietà della tabella', 'delete_table' => 'Elimina tabella', + 'table_clear_formatting' => 'Cancella formattazione tabella', + 'resize_to_contents' => 'Ridimensiona al contenuto', + 'row_header' => 'Intestazione riga', 'insert_row_before' => 'Inserisci riga sopra', 'insert_row_after' => 'Inserisci riga sotto', 'delete_row' => 'Elimina riga', @@ -89,39 +94,39 @@ 'delete_column' => 'Elimina colonna', 'table_cell' => 'Cella', 'table_row' => 'Riga', - 'table_column' => 'Column', + 'table_column' => 'Colonna', 'cell_properties' => 'Proprietà cella', - 'cell_properties_title' => 'Proprietà Cella', + 'cell_properties_title' => 'Proprietà cella', 'cell_type' => 'Tipo di cella', 'cell_type_cell' => 'Cella', 'cell_scope' => 'Ambito', 'cell_type_header' => 'Cella intestazione', 'merge_cells' => 'Unisci celle', 'split_cell' => 'Dividi cella', - 'table_row_group' => 'Gruppo Riga', - 'table_column_group' => 'Gruppo Colonna', + 'table_row_group' => 'Gruppo riga', + 'table_column_group' => 'Gruppo colonna', 'horizontal_align' => 'Allineamento orizzontale', 'vertical_align' => 'Allineamento verticale', 'border_width' => 'Spessore bordo', - 'border_style' => 'Stile del Bordo', - 'border_color' => 'Colore del bordo', + 'border_style' => 'Stile bordo', + 'border_color' => 'Colore bordo', 'row_properties' => 'Proprietà riga', - 'row_properties_title' => 'Proprietà Riga', + 'row_properties_title' => 'Proprietà riga', 'cut_row' => 'Taglia riga', 'copy_row' => 'Copia riga', - 'paste_row_before' => 'Inserisci la riga prima', + 'paste_row_before' => 'Incolla riga prima', 'paste_row_after' => 'Incolla riga dopo', - 'row_type' => 'Tipo di riga', + 'row_type' => 'Tipo riga', 'row_type_header' => 'Intestazione', 'row_type_body' => 'Corpo', - 'row_type_footer' => 'Piè di Pagina', + 'row_type_footer' => 'Piè di pagina', 'alignment' => 'Allineamento', 'cut_column' => 'Taglia colonna', 'copy_column' => 'Copia colonna', 'paste_column_before' => 'Incolla colonna prima', - 'paste_column_after' => 'Inserisci colonna dopo', - 'cell_padding' => 'Spaziatura cella', - 'cell_spacing' => 'Spaziatura celle', + 'paste_column_after' => 'Incolla colonna dopo', + 'cell_padding' => 'Padding cella', + 'cell_spacing' => 'Spaziatura cella', 'caption' => 'Didascalia', 'show_caption' => 'Mostra didascalia', 'constrain' => 'Mantieni proporzioni', @@ -129,10 +134,10 @@ 'cell_border_dotted' => 'Punteggiato', 'cell_border_dashed' => 'Tratteggiato', 'cell_border_double' => 'Doppio', - 'cell_border_groove' => 'Bordo Incassato', - 'cell_border_ridge' => 'Bordo In Rilievo', + 'cell_border_groove' => 'Bordo incassato', + 'cell_border_ridge' => 'In rilievo', 'cell_border_inset' => 'Sfondo Incassato', - 'cell_border_outset' => 'Sfondo In rilievo', + 'cell_border_outset' => 'Sfondo in rilievo', 'cell_border_none' => 'Nessuno', 'cell_border_hidden' => 'Nascosto', @@ -144,6 +149,7 @@ 'url' => 'URL', 'text_to_display' => 'Testo da visualizzare', 'title' => 'Titolo', + 'browse_links' => 'Sfoglia link', 'open_link' => 'Apri collegamento', 'open_link_in' => 'Apri collegamento in...', 'open_link_current' => 'Finestra corrente', @@ -152,17 +158,19 @@ 'insert_collapsible' => 'Inserisci blocco collassabile', 'collapsible_unwrap' => 'Espandi', 'edit_label' => 'Modifica etichetta', - 'toggle_open_closed' => 'Espandi/Comprimi', + 'toggle_open_closed' => 'Espandi/comprimi', 'collapsible_edit' => 'Modifica blocco collassabile', - 'toggle_label' => 'Attiva/Disattiva etichetta', + 'toggle_label' => 'Attiva/disattiva etichetta', // About view 'about' => 'Informazioni sull\'editor', 'about_title' => 'Informazioni sull\'editor di WYSIWYG', - 'editor_license' => 'Licenza & Copyright Dell\'Editor', + 'editor_license' => 'Licenza e copyright dell\'editor', + 'editor_lexical_license' => 'Questo editor è stato creato come fork di :lexicalLink ed è distribuito sotto la licenza MIT.', + 'editor_lexical_license_link' => 'I dettagli della licenza sono disponibili qui.', 'editor_tiny_license' => 'Questo editor è realizzato usando :tinyLink che è fornito sotto la licenza MIT.', 'editor_tiny_license_link' => 'I dettagli del copyright e della licenza di TinyMCE sono disponibili qui.', - 'save_continue' => 'Salva Pagina E Continua', + 'save_continue' => 'Salva pagina e continua', 'callouts_cycle' => '(Continua a premere per passare da un tipo all\'altro)', 'link_selector' => 'Link al contenuto', 'shortcuts' => 'Scorciatoie', diff --git a/lang/it/entities.php b/lang/it/entities.php index 383cb4e7c2d..eb9868973e5 100644 --- a/lang/it/entities.php +++ b/lang/it/entities.php @@ -11,131 +11,156 @@ 'recently_updated_pages' => 'Pagine aggiornate di recente', 'recently_created_chapters' => 'Capitoli creati di recente', 'recently_created_books' => 'Libri creati di recente', - 'recently_created_shelves' => 'Librerie Create Di Recente', + 'recently_created_shelves' => 'Librerie create di recente', 'recently_update' => 'Aggiornati di recente', 'recently_viewed' => 'Visti di recente', - 'recent_activity' => 'Attività Recente', + 'recent_activity' => 'Attività recente', 'create_now' => 'Creane uno ora', - 'revisions' => 'Versioni', - 'meta_revision' => 'Versione #:revisionCount', + 'revisions' => 'Revisioni', + 'meta_revision' => 'Revisione #:revisionCount', 'meta_created' => 'Creato :timeLength', 'meta_created_name' => 'Creato :timeLength da :user', 'meta_updated' => 'Aggiornato :timeLength', 'meta_updated_name' => 'Aggiornato :timeLength da :user', 'meta_owned_name' => 'Creati da :user', 'meta_reference_count' => 'Referenziato da :count item|Referenziato da :count items', - 'entity_select' => 'Selezione Entità', + 'entity_select' => 'Seleziona l\'entità', 'entity_select_lack_permission' => 'Non hai i permessi necessari per selezionare questo elemento', 'images' => 'Immagini', - 'my_recent_drafts' => 'Bozze Recenti', + 'my_recent_drafts' => 'Bozze recenti', 'my_recently_viewed' => 'Visti di recente', - 'my_most_viewed_favourites' => 'I Miei Preferiti Più Visti', - 'my_favourites' => 'I miei Preferiti', + 'my_most_viewed_favourites' => 'I miei preferiti più visti', + 'my_favourites' => 'I miei preferiti', 'no_pages_viewed' => 'Non hai visto nessuna pagina', - 'no_pages_recently_created' => 'Nessuna pagina è stata creata di recente', - 'no_pages_recently_updated' => 'Nessuna pagina è stata aggiornata di recente', + 'no_pages_recently_created' => 'Nessuna pagina creata di recente', + 'no_pages_recently_updated' => 'Nessuna pagina aggiornata di recente', 'export' => 'Esporta', - 'export_html' => 'File Contenuto Web', + 'export_html' => 'File contenuto web', 'export_pdf' => 'File PDF', 'export_text' => 'File di testo', 'export_md' => 'File Markdown', + 'export_zip' => 'ZIP Portatile', + 'default_template' => 'Modello di pagina predefinito', + 'default_template_explain' => 'Assegna un modello di pagina che sarà usato come contenuto predefinito per tutte le pagine create in questo elemento. Tieni presente che potrà essere utilizzato solo se il creatore della pagina ha accesso alla pagina del modello scelto.', + 'default_template_select' => 'Seleziona una pagina modello', + 'import' => 'Importa', + 'import_validate' => 'Convalida Importazione', + 'import_desc' => 'Importa libri, capitoli e pagine utilizzando un\'esportazione zip portatile dalla stessa istanza o da un\'istanza diversa. Selezionare un file ZIP per procedere. Dopo che il file è stato caricato e convalidato, sarà possibile configurare e confermare l\'importazione nella vista successiva.', + 'import_zip_select' => 'Seleziona il file ZIP da caricare', + 'import_zip_validation_errors' => 'Sono stati rilevati errori durante la convalida del file ZIP fornito:', + 'import_pending' => 'Importazioni In Attesa', + 'import_pending_none' => 'Non sono state avviate importazioni.', + 'import_continue' => 'Continua l\'importazione', + 'import_continue_desc' => 'Esaminare il contenuto da importare dal file ZIP caricato. Quando è pronto, eseguire l\'importazione per aggiungere i contenuti al sistema. Il file di importazione ZIP caricato verrà automaticamente rimosso quando l\'importazione sarà riuscita.', + 'import_details' => 'Dettagli dell\'importazione', + 'import_run' => 'Esegui Importazione', + 'import_size' => 'Dimensione dello ZIP importato :size', + 'import_uploaded_at' => 'Caricato il :relativeTime', + 'import_uploaded_by' => 'Caricata da', + 'import_location' => 'Posizione Di Importazione', + 'import_location_desc' => 'Selezionare una posizione di destinazione per i contenuti importati. È necessario disporre delle autorizzazioni adeguate per creare all\'interno della posizione scelta.', + 'import_delete_confirm' => 'Sei sicuro di voler eliminare questa importazione?', + 'import_delete_desc' => 'Questa operazione cancella il file ZIP di importazione caricato e non può essere annullata.', + 'import_errors' => 'Errori di importazione', + 'import_errors_desc' => 'Gli seguenti errori si sono verificati durante il tentativo di importazione:', + 'breadcrumb_siblings_for_page' => 'Naviga tra le pagine correlate', + 'breadcrumb_siblings_for_chapter' => 'Naviga tra i capitoli correlati', + 'breadcrumb_siblings_for_book' => 'Naviga tra i libri correlati', + 'breadcrumb_siblings_for_bookshelf' => 'Naviga tra le librerie correlate', // Permissions and restrictions 'permissions' => 'Permessi', 'permissions_desc' => 'Imposta qui i permessi per sovrascrivere i permessi predefiniti forniti dai ruoli utente.', 'permissions_book_cascade' => 'I permessi impostati sui libri si trasmettono automaticamente a cascata ai capitoli e alle pagine figli, a meno che non siano stati definiti permessi propri.', 'permissions_chapter_cascade' => 'I permessi impostati sui capitoli si trasmettono automaticamente a cascata alle pagine figlie, a meno che non siano stati definiti permessi propri.', - 'permissions_save' => 'Salva Permessi', + 'permissions_save' => 'Salva permessi', 'permissions_owner' => 'Proprietario', - 'permissions_role_everyone_else' => 'Tutti Gli Altri', + 'permissions_role_everyone_else' => 'Tutti gli altri', 'permissions_role_everyone_else_desc' => 'Imposta i permessi per tutti i ruoli non specificamente sovrascritti.', 'permissions_role_override' => 'Sovrascrivere i permessi per il ruolo', 'permissions_inherit_defaults' => 'Eredita predefinite', // Search - 'search_results' => 'Risultati Ricerca', + 'search_results' => 'Risultati della ricerca', 'search_total_results_found' => ':count risultato trovato|:count risultati trovati', - 'search_clear' => 'Pulisci Ricerca', + 'search_clear' => 'Pulisci ricerca', 'search_no_pages' => 'Nessuna pagina corrisponde alla ricerca', 'search_for_term' => 'Ricerca per :term', - 'search_more' => 'Più Risultati', - 'search_advanced' => 'Ricerca Avanzata', - 'search_terms' => 'Termini Ricerca', - 'search_content_type' => 'Tipo di Contenuto', - 'search_exact_matches' => 'Corrispondenza Esatta', - 'search_tags' => 'Ricerche Tag', + 'search_more' => 'Altri risultati', + 'search_advanced' => 'Ricerca avanzata', + 'search_terms' => 'Termini di ricerca', + 'search_content_type' => 'Tipo di contenuto', + 'search_exact_matches' => 'Corrispondenza esatta', + 'search_tags' => 'Ricerche per tag', 'search_options' => 'Opzioni', - 'search_viewed_by_me' => 'Visti', - 'search_not_viewed_by_me' => 'Non visti', + 'search_viewed_by_me' => 'Visti da me', + 'search_not_viewed_by_me' => 'Non visti da me', 'search_permissions_set' => 'Permessi impostati', 'search_created_by_me' => 'Creati da me', 'search_updated_by_me' => 'Aggiornati da me', 'search_owned_by_me' => 'Creati da me', - 'search_date_options' => 'Opzioni Data', + 'search_date_options' => 'Opzioni data', 'search_updated_before' => 'Aggiornati prima del', 'search_updated_after' => 'Aggiornati dopo il', 'search_created_before' => 'Creati prima del', 'search_created_after' => 'Creati dopo il', - 'search_set_date' => 'Imposta Data', - 'search_update' => 'Aggiorna Ricerca', + 'search_set_date' => 'Imposta data', + 'search_update' => 'Aggiorna ricerca', // Shelves 'shelf' => 'Libreria', 'shelves' => 'Librerie', - 'x_shelves' => ':count Libreria|:count Librerie', - 'shelves_empty' => 'Nessuna libreria è stata creata', - 'shelves_create' => 'Crea Nuova Libreria', - 'shelves_popular' => 'Librerie Popolari', - 'shelves_new' => 'Nuove Librerie', - 'shelves_new_action' => 'Nuova Libreria', + 'x_shelves' => ':count libreria|:count librerie', + 'shelves_empty' => 'Nessuna libreria creata', + 'shelves_create' => 'Crea nuova libreria', + 'shelves_popular' => 'Librerie popolari', + 'shelves_new' => 'Nuove librerie', + 'shelves_new_action' => 'Nuova libreria', 'shelves_popular_empty' => 'Le librerie più popolari appariranno qui.', 'shelves_new_empty' => 'Le librerie create più di recente appariranno qui.', - 'shelves_save' => 'Salva Libreria', + 'shelves_save' => 'Salva libreria', 'shelves_books' => 'Libri in questa libreria', 'shelves_add_books' => 'Aggiungi libri a questa libreria', 'shelves_drag_books' => 'Trascina i libri qui sotto per aggiungerli a questa libreria', 'shelves_empty_contents' => 'Questa libreria non ha libri assegnati', 'shelves_edit_and_assign' => 'Modifica la libreria per assegnare i libri', - 'shelves_edit_named' => 'Modifica Libreria :name', - 'shelves_edit' => 'Modifica Libreria', - 'shelves_delete' => 'Elimina Libreria', - 'shelves_delete_named' => 'Elimina Libreria :name', + 'shelves_edit_named' => 'Modifica libreria :name', + 'shelves_edit' => 'Modifica libreria', + 'shelves_delete' => 'Elimina libreria', + 'shelves_delete_named' => 'Elimina libreria :name', 'shelves_delete_explain' => "La libreria ':name' verrà eliminata. I libri al suo interno non verranno eliminati.", 'shelves_delete_confirmation' => 'Sei sicuro di voler eliminare questa libreria?', - 'shelves_permissions' => 'Permessi Libreria', - 'shelves_permissions_updated' => 'Permessi Libreria Aggiornati', - 'shelves_permissions_active' => 'Permessi Libreria Attivi', + 'shelves_permissions' => 'Permessi libreria', + 'shelves_permissions_updated' => 'Permessi libreria aggiornati', + 'shelves_permissions_active' => 'Permessi libreria attivi', 'shelves_permissions_cascade_warning' => 'I permessi delle librerie non si estendono automaticamente ai libri contenuti. Questo perché un libro può essere presente su più scaffali. I permessi possono comunque essere copiati ai libri al suo interno usando l\'opzione sottostante.', 'shelves_permissions_create' => 'Le autorizzazioni per la creazione di librerie sono utilizzate solo per copiare le autorizzazioni ai libri figli utilizzando l\'azione sottostante. Non controllano la capacità di creare libri.', - 'shelves_copy_permissions_to_books' => 'Copia Permessi ai Libri', - 'shelves_copy_permissions' => 'Copia Permessi', - 'shelves_copy_permissions_explain' => 'Verranno applicati tutti i permessi della libreria ai libri al suo interno. Prima dell\'attivazione, assicurati che ogni permesso di questa libreria sia salvato.', + 'shelves_copy_permissions_to_books' => 'Copia permessi ai libri', + 'shelves_copy_permissions' => 'Copia permessi', + 'shelves_copy_permissions_explain' => 'Verranno applicati tutti i permessi della libreria ai libri al suo interno. Prima dell\'attivazione, assicurati di aver salvato le modifiche ai permessi di questa libreria.', 'shelves_copy_permission_success' => 'Permessi della libreria copiati in :count libri', // Books 'book' => 'Libro', 'books' => 'Libri', - 'x_books' => ':count Libro|:count Libri', - 'books_empty' => 'Nessun libro è stato creato', - 'books_popular' => 'Libri Popolari', - 'books_recent' => 'Libri Recenti', - 'books_new' => 'Nuovi Libri', - 'books_new_action' => 'Nuovo Libro', + 'x_books' => ':count libro|:count libri', + 'books_empty' => 'Nessun libro creato', + 'books_popular' => 'Libri popolari', + 'books_recent' => 'Libri recenti', + 'books_new' => 'Nuovi libri', + 'books_new_action' => 'Nuovo libro', 'books_popular_empty' => 'I libri più popolari appariranno qui.', - 'books_new_empty' => 'I libri creati più di recente appariranno qui.', - 'books_create' => 'Crea Nuovo Libro', - 'books_delete' => 'Elimina Libro', + 'books_new_empty' => 'I libri creati di recente appariranno qui.', + 'books_create' => 'Crea nuovo libro', + 'books_delete' => 'Elimina libro', 'books_delete_named' => 'Elimina il libro :bookName', 'books_delete_explain' => 'Questo eliminerà il libro di nome \':bookName\'. Tutte le pagine e i capitoli saranno rimossi.', 'books_delete_confirmation' => 'Sei sicuro di voler eliminare questo libro?', - 'books_edit' => 'Modifica Libro', + 'books_edit' => 'Modifica libro', 'books_edit_named' => 'Modifica il libro :bookName', - 'books_form_book_name' => 'Nome Libro', - 'books_save' => 'Salva Libro', - 'books_default_template' => 'Pagina template predefinita', - 'books_default_template_explain' => 'Assegnare una pagina template che verrà utilizzata come contenuto predefinito per tutte le nuove pagine di questo libro. Tenete presente che questa sarà usata solo se il creatore della pagina ha accesso di visualizzazione alla pagina template scelta.', - 'books_default_template_select' => 'Selezionare una pagina template', - 'books_permissions' => 'Permessi Libro', + 'books_form_book_name' => 'Nome libro', + 'books_save' => 'Salva libro', + 'books_permissions' => 'Permessi libro', 'books_permissions_updated' => 'Permessi del libro aggiornati', 'books_empty_contents' => 'Non ci sono pagine o capitoli per questo libro.', 'books_empty_create_page' => 'Crea una nuova pagina', @@ -143,242 +168,256 @@ 'books_empty_add_chapter' => 'Aggiungi un capitolo', 'books_permissions_active' => 'Permessi libro attivi', 'books_search_this' => 'Cerca in questo libro', - 'books_navigation' => 'Navigazione Libro', + 'books_navigation' => 'Navigazione libro', 'books_sort' => 'Ordina il contenuto del libro', - 'books_sort_desc' => 'Spostare capitoli e pagine all\'interno di un libro per riorganizzarne il contenuto. È possibile aggiungere altri libri, per spostare facilmente capitoli e pagine da un libro all\'altro.', + 'books_sort_desc' => 'Spostare i capitoli e le pagine di un libro per riorganizzarne il contenuto. Possono essere aggiunti altri libri che permettono di spostare facilmente capitoli e pagine tra i libri. Opzionalmente una regola di ordinamento automatico può essere impostata per ordinare automaticamente i contenuti di questo libro in caso di modifiche.', + 'books_sort_auto_sort' => 'Opzione Ordinamento Automatico', + 'books_sort_auto_sort_active' => 'Ordinamento Automatico Attivo: :sortName', 'books_sort_named' => 'Ordina il libro :bookName', 'books_sort_name' => 'Ordina per Nome', - 'books_sort_created' => 'Ordina per Data di Creazione', - 'books_sort_updated' => 'Ordina per Data di Aggiornamento', - 'books_sort_chapters_first' => 'Capitoli Per Primi', - 'books_sort_chapters_last' => 'Capitoli Per Ultimi', - 'books_sort_show_other' => 'Mostra Altri Libri', + 'books_sort_created' => 'Ordina per Data di creazione', + 'books_sort_updated' => 'Ordina per Data di aggiornamento', + 'books_sort_chapters_first' => 'Capitoli per primi', + 'books_sort_chapters_last' => 'Capitoli per ultimi', + 'books_sort_show_other' => 'Mostra altri libri', 'books_sort_save' => 'Salva il nuovo ordine', - 'books_sort_show_other_desc' => 'Aggiungere qui altri libri per includerli nell\'operazione di ordinamento e consentire una facile riorganizzazione incrociata dei libri.', + 'books_sort_show_other_desc' => 'Aggiungi qui altri libri per includerli nell\'operazione di ordinamento e consentire una facile riorganizzazione incrociata dei libri.', 'books_sort_move_up' => 'Muovi su', 'books_sort_move_down' => 'Muovi giù', - 'books_sort_move_prev_book' => 'Passare al libro precedente', - 'books_sort_move_next_book' => 'Passare al libro successivo', - 'books_sort_move_prev_chapter' => 'Passare al capitolo precedente', - 'books_sort_move_next_chapter' => 'Passare al capitolo successivo', - 'books_sort_move_book_start' => 'Spostarsi all\'inizio del libro', - 'books_sort_move_book_end' => 'Spostarsi alla fine del libro', - 'books_sort_move_before_chapter' => 'Passare al capitolo precedente', - 'books_sort_move_after_chapter' => 'Passare al capitolo successivo', - 'books_copy' => 'Copia Libro', + 'books_sort_move_prev_book' => 'Passa al libro precedente', + 'books_sort_move_next_book' => 'Passa al libro successivo', + 'books_sort_move_prev_chapter' => 'Passa al capitolo precedente', + 'books_sort_move_next_chapter' => 'Passa al capitolo successivo', + 'books_sort_move_book_start' => 'Passa all\'inizio del libro', + 'books_sort_move_book_end' => 'Passa alla fine del libro', + 'books_sort_move_before_chapter' => 'Passa al capitolo precedente', + 'books_sort_move_after_chapter' => 'Passa al capitolo successivo', + 'books_copy' => 'Copia libro', 'books_copy_success' => 'Libro copiato con successo', // Chapters 'chapter' => 'Capitolo', 'chapters' => 'Capitoli', - 'x_chapters' => ':count Capitolo|:count Capitoli', - 'chapters_popular' => 'Capitoli Popolari', - 'chapters_new' => 'Nuovo Capitolo', + 'x_chapters' => ':count capitolo|:count capitoli', + 'chapters_popular' => 'Capitoli popolari', + 'chapters_new' => 'Nuovo capitolo', 'chapters_create' => 'Crea un nuovo capitolo', - 'chapters_delete' => 'Elimina Capitolo', + 'chapters_delete' => 'Elimina capitolo', 'chapters_delete_named' => 'Elimina il capitolo :chapterName', - 'chapters_delete_explain' => 'Procedendo si eliminerà il capitolo denominato \':chapterName\'. Anche le pagine in esso contenute saranno eliminate.', + 'chapters_delete_explain' => 'Verrà eliminato il capitolo denominato \':chapterName\'. Saranno eliminate anche le pagine all\'interno.', 'chapters_delete_confirm' => 'Sei sicuro di voler eliminare questo capitolo?', - 'chapters_edit' => 'Elimina Capitolo', + 'chapters_edit' => 'Elimina capitolo', 'chapters_edit_named' => 'Modifica il capitolo :chapterName', - 'chapters_save' => 'Salva Capitolo', - 'chapters_move' => 'Muovi Capitolo', - 'chapters_move_named' => 'Muovi il capitolo :chapterName', - 'chapters_copy' => 'Copia Capitolo', + 'chapters_save' => 'Salva capitolo', + 'chapters_move' => 'Sposta capitolo', + 'chapters_move_named' => 'Sposta il capitolo :chapterName', + 'chapters_copy' => 'Copia capitolo', 'chapters_copy_success' => 'Capitolo copiato con successo', - 'chapters_permissions' => 'Permessi Capitolo', + 'chapters_permissions' => 'Permessi capitolo', 'chapters_empty' => 'Non ci sono pagine in questo capitolo.', - 'chapters_permissions_active' => 'Permessi Capitolo Attivi', - 'chapters_permissions_success' => 'Permessi Capitolo Aggiornati', + 'chapters_permissions_active' => 'Permessi capitolo attivi', + 'chapters_permissions_success' => 'Permessi capitolo aggiornati', 'chapters_search_this' => 'Cerca in questo capitolo', - 'chapter_sort_book' => 'Ordina Libro', + 'chapter_sort_book' => 'Ordina libro', // Pages 'page' => 'Pagina', 'pages' => 'Pagine', - 'x_pages' => ':count Pagina|:count Pagine', - 'pages_popular' => 'Pagine Popolari', - 'pages_new' => 'Nuova Pagina', + 'x_pages' => ':count pagina|:count pagine', + 'pages_popular' => 'Pagine popolari', + 'pages_new' => 'Nuova pagina', 'pages_attachments' => 'Allegati', - 'pages_navigation' => 'Navigazione Pagine', - 'pages_delete' => 'Elimina Pagina', + 'pages_navigation' => 'Navigazione pagine', + 'pages_delete' => 'Elimina pagina', 'pages_delete_named' => 'Elimina la pagina :pageName', 'pages_delete_draft_named' => 'Elimina bozza della pagina :pageName', - 'pages_delete_draft' => 'Elimina Bozza Pagina', + 'pages_delete_draft' => 'Elimina bozza pagina', 'pages_delete_success' => 'Pagina eliminata', 'pages_delete_draft_success' => 'Bozza di una pagina eliminata', - 'pages_delete_warning_template' => 'Questa pagina è in uso come pagina template predefinita di un libro. Dopo l\'eliminazione di questa pagina, a questi libri non verrà più assegnato una pagina template predefinita.', + 'pages_delete_warning_template' => 'Questa pagina è in uso come modello di pagina predefinito del libro o del capitolo. Questi libri o capitoli non avranno più un modello di pagina predefinito assegnato dopo che questa pagina sarà eliminata.', 'pages_delete_confirm' => 'Sei sicuro di voler eliminare questa pagina?', 'pages_delete_draft_confirm' => 'Sei sicuro di voler eliminare la bozza di questa pagina?', 'pages_editing_named' => 'Modifica :pageName', - 'pages_edit_draft_options' => 'Opzioni Bozza', - 'pages_edit_save_draft' => 'Salva Bozza', - 'pages_edit_draft' => 'Modifica Bozza della pagina', - 'pages_editing_draft' => 'Modifica Bozza', - 'pages_editing_page' => 'Modifica Pagina', + 'pages_edit_draft_options' => 'Opzioni bozza', + 'pages_edit_save_draft' => 'Salva bozza', + 'pages_edit_draft' => 'Modifica bozza della pagina', + 'pages_editing_draft' => 'Modifica bozza', + 'pages_editing_page' => 'Modifica pagina', 'pages_edit_draft_save_at' => 'Bozza salvata alle ', - 'pages_edit_delete_draft' => 'Elimina Bozza', + 'pages_edit_delete_draft' => 'Elimina bozza', 'pages_edit_delete_draft_confirm' => 'Si è sicuri di voler eliminare le modifiche alla pagina bozza? Tutte le modifiche apportate dall\'ultimo salvataggio completo andranno perse e l\'editor verrà aggiornato con l\'ultimo stato di salvataggio della pagina non in bozza.', - 'pages_edit_discard_draft' => 'Scarta Bozza', + 'pages_edit_discard_draft' => 'Scarta bozza', 'pages_edit_switch_to_markdown' => 'Passa all\'editor Markdown', 'pages_edit_switch_to_markdown_clean' => '(Contenuto Chiaro)', - 'pages_edit_switch_to_markdown_stable' => '(Contenuto Stabile)', + 'pages_edit_switch_to_markdown_stable' => '(Contenuto stabile)', 'pages_edit_switch_to_wysiwyg' => 'Passa all\'editor WYSIWYG', - 'pages_edit_set_changelog' => 'Imposta Changelog', + 'pages_edit_switch_to_new_wysiwyg' => 'Passa al nuovo WYSIWYG', + 'pages_edit_switch_to_new_wysiwyg_desc' => '(In Beta Test)', + 'pages_edit_set_changelog' => 'Imposta changelog', 'pages_edit_enter_changelog_desc' => 'Inserisci una breve descrizione dei cambiamenti che hai apportato', - 'pages_edit_enter_changelog' => 'Inserisci Changelog', - 'pages_editor_switch_title' => 'Cambia Editor', + 'pages_edit_enter_changelog' => 'Inserisci changelog', + 'pages_editor_switch_title' => 'Cambia editor', 'pages_editor_switch_are_you_sure' => 'Sei sicuro di voler cambiare l\'editor di questa pagina?', - 'pages_editor_switch_consider_following' => 'Considerare quanto segue quando si cambia editor:', - 'pages_editor_switch_consideration_a' => 'Una volta salvata, la nuova opzione di editor sarà utilizzata da qualsiasi editor futuro, inclusi quelli che potrebbero non essere in grado di cambiare il tipo di editor da solo.', + 'pages_editor_switch_consider_following' => 'Considera quanto segue quando si cambia editor:', + 'pages_editor_switch_consideration_a' => 'Una volta salvata, la nuova opzione di editor sarà utilizzata da chi modificherà in futuro, inclusi quelli che potrebbero non essere in grado di cambiare il tipo di editor da soli.', 'pages_editor_switch_consideration_b' => 'Ciò può potenzialmente portare a una perdita di dettagli e sintassi in determinate circostanze.', - 'pages_editor_switch_consideration_c' => 'Le modifiche al tag o al changelog, fatte dall\'ultimo salvataggio, non persisteranno in questa modifica.', - 'pages_save' => 'Salva Pagina', - 'pages_title' => 'Titolo Pagina', - 'pages_name' => 'Nome Pagina', + 'pages_editor_switch_consideration_c' => 'Le modifiche ai tag o al changelog, fatte dall\'ultimo salvataggio, non persisteranno in questa modifica.', + 'pages_save' => 'Salva pagina', + 'pages_title' => 'Titolo pagina', + 'pages_name' => 'Nome pagina', 'pages_md_editor' => 'Editor', 'pages_md_preview' => 'Anteprima', - 'pages_md_insert_image' => 'Inserisci Immagina', - 'pages_md_insert_link' => 'Inserisci Link Entità', - 'pages_md_insert_drawing' => 'Inserisci Disegno', + 'pages_md_insert_image' => 'Inserisci immagine', + 'pages_md_insert_link' => 'Inserisci collegamento entità', + 'pages_md_insert_drawing' => 'Inserisci disegno', 'pages_md_show_preview' => 'Visualizza anteprima', 'pages_md_sync_scroll' => 'Sincronizza scorrimento anteprima', - 'pages_drawing_unsaved' => 'Trovato Disegno Non Salvato', - 'pages_drawing_unsaved_confirm' => 'Sono stati trovati i dati di un disegno non salvati da un precedente tentativo di salvataggio di disegno non riuscito. Ripristinare e continuare a modificare questo disegno non salvato?', + 'pages_md_plain_editor' => 'Editor di testo semplice', + 'pages_drawing_unsaved' => 'Trovato disegno non salvato', + 'pages_drawing_unsaved_confirm' => 'Sono stati trovati i dati di un disegno non salvato da un precedente tentativo di salvataggio di disegno non riuscito. Ripristinare e continuare a modificare questo disegno non salvato?', 'pages_not_in_chapter' => 'La pagina non è in un capitolo', - 'pages_move' => 'Muovi Pagina', - 'pages_copy' => 'Copia Pagina', - 'pages_copy_desination' => 'Copia Destinazione', + 'pages_move' => 'Sposta pagina', + 'pages_copy' => 'Copia pagina', + 'pages_copy_desination' => 'Copia destinazione', 'pages_copy_success' => 'Pagina copiata correttamente', - 'pages_permissions' => 'Permessi Pagina', + 'pages_permissions' => 'Permessi pagina', 'pages_permissions_success' => 'Permessi pagina aggiornati', 'pages_revision' => 'Versione', - 'pages_revisions' => 'Versioni Pagina', - 'pages_revisions_desc' => 'Di seguito sono elencate tutte le revisioni precedenti di questa pagina. È possibile consultare, confrontare e ripristinare le vecchie versioni della pagina, se le autorizzazioni lo consentono. La cronologia completa della pagina potrebbe non essere riportata qui, poiché, a seconda della configurazione del sistema, le vecchie revisioni potrebbero essere cancellate automaticamente.', - 'pages_revisions_named' => 'Versioni della pagina :pageName', - 'pages_revision_named' => 'Versione della pagina :pageName', + 'pages_revisions' => 'Revisioni pagina', + 'pages_revisions_desc' => 'Di seguito sono elencate tutte le revisioni precedenti di questa pagina. È possibile consultare, confrontare e ripristinare le vecchie versioni della pagina, se hai i permessi. La cronologia completa della pagina potrebbe non essere riportata qui, poiché a seconda della configurazione del sistema le vecchie revisioni potrebbero essere cancellate automaticamente.', + 'pages_revisions_named' => 'Revisioni della pagina :pageName', + 'pages_revision_named' => 'Revisione della pagina :pageName', 'pages_revision_restored_from' => 'Ripristinato da #:id; :summary', - 'pages_revisions_created_by' => 'Creata Da', - 'pages_revisions_date' => 'Data Versione', + 'pages_revisions_created_by' => 'Creata da', + 'pages_revisions_date' => 'Data versione', 'pages_revisions_number' => '#', - 'pages_revisions_sort_number' => 'Numero Revisione', + 'pages_revisions_sort_number' => 'Numero revisione', 'pages_revisions_numbered' => 'Revisione #:id', - 'pages_revisions_numbered_changes' => 'Modifiche Revisione #:id', - 'pages_revisions_editor' => 'Tipo Di Editor', - 'pages_revisions_changelog' => 'Cambiamenti', + 'pages_revisions_numbered_changes' => 'Modifiche revisione #:id', + 'pages_revisions_editor' => 'Tipo di editor', + 'pages_revisions_changelog' => 'Changelog', 'pages_revisions_changes' => 'Cambiamenti', - 'pages_revisions_current' => 'Versione Corrente', + 'pages_revisions_current' => 'Versione corrente', 'pages_revisions_preview' => 'Anteprima', 'pages_revisions_restore' => 'Ripristina', - 'pages_revisions_none' => 'Questa pagina non ha versioni', - 'pages_copy_link' => 'Copia Link', + 'pages_revisions_none' => 'Questa pagina non ha revisioni', + 'pages_copy_link' => 'Copia collegamento', 'pages_edit_content_link' => 'Vai alla sezione nell\'editor', - 'pages_pointer_enter_mode' => 'Accedi alla modalità di selezione della sezione', - 'pages_pointer_label' => 'Opzioni Sezione Pagina', - 'pages_pointer_permalink' => 'Permalink Sezione Pagina', - 'pages_pointer_include_tag' => 'Sezione Pagina Includi Tag', - 'pages_pointer_toggle_link' => 'Modalità Permalink, Premi per mostrare il tag includi', - 'pages_pointer_toggle_include' => 'Modo includi tag, premi per mostrare permalink', - 'pages_permissions_active' => 'Permessi Pagina Attivi', + 'pages_pointer_enter_mode' => 'Vai alla modalità di selezione della sezione', + 'pages_pointer_label' => 'Opzioni sezione pagina', + 'pages_pointer_permalink' => 'Sezione pagina Permalink', + 'pages_pointer_include_tag' => 'Sezione pagina includi tag', + 'pages_pointer_toggle_link' => 'Modalità Permalink, premi per mostrare includi tag', + 'pages_pointer_toggle_include' => 'Modalità includi tag, premi per mostrare permalink', + 'pages_permissions_active' => 'Permessi pagina attivi', 'pages_initial_revision' => 'Pubblicazione iniziale', 'pages_references_update_revision' => 'Aggiornamento automatico di sistema dei collegamenti interni', - 'pages_initial_name' => 'Nuova Pagina', + 'pages_initial_name' => 'Nuova pagina', 'pages_editing_draft_notification' => 'Stai modificando una bozza che è stata salvata il :timeDiff.', - 'pages_draft_edited_notification' => 'Questa pagina è stata aggiornata. È consigliabile scartare questa bozza.', - 'pages_draft_page_changed_since_creation' => 'Questa pagina è stata aggiornata da quando è stata creata questa bozza. Si consiglia di scartare questa bozza o fare attenzione a non sovrascrivere alcun cambiamento di pagina.', + 'pages_draft_edited_notification' => 'Questa pagina è stata aggiornata. Si consiglia di scartare questa bozza.', + 'pages_draft_page_changed_since_creation' => 'Questa pagina è stata aggiornata da quando è stata creata questa bozza. Si consiglia di scartare questa bozza o di fare attenzione a non sovrascrivere alcun cambiamento alla pagina.', 'pages_draft_edit_active' => [ - 'start_a' => ':count hanno iniziato a modificare questa pagina', + 'start_a' => ':count utenti hanno iniziato a modificare questa pagina', 'start_b' => ':userName ha iniziato a modificare questa pagina', - 'time_a' => 'da quando le pagine sono state aggiornate', + 'time_a' => 'dall\'ultimo aggiornamento della pagina', 'time_b' => 'negli ultimi :minCount minuti', 'message' => ':start :time. Assicurati di non sovrascrivere le modifiche degli altri!', ], 'pages_draft_discarded' => 'Bozza scartata! L\'editor è stato aggiornato con il contenuto della pagina corrente', 'pages_draft_deleted' => 'Bozza eliminata! L\'editor è stato aggiornato con il contenuto della pagina corrente', - 'pages_specific' => 'Pagina Specifica', - 'pages_is_template' => 'Template Pagina', + 'pages_specific' => 'Pagina specifica', + 'pages_is_template' => 'Modello pagina', // Editor Sidebar - 'toggle_sidebar' => 'Attiva/Disattiva Barra Laterale', - 'page_tags' => 'Tag Pagina', - 'chapter_tags' => 'Tag Capitolo', - 'book_tags' => 'Tag Libro', - 'shelf_tags' => 'Tag Libreria', + 'toggle_sidebar' => 'Attiva/disattiva barra laterale', + 'page_tags' => 'Tag pagina', + 'chapter_tags' => 'Tag capitolo', + 'book_tags' => 'Tag libro', + 'shelf_tags' => 'Tag libreria', 'tag' => 'Tag', 'tags' => 'Tag', - 'tags_index_desc' => 'I tag possono essere applicati ai contenuti del sistema per applicare una forma flessibile di categorizzazione. I tag possono avere contemporaneamente una chiave e un valore, mentre il valore è opzionale. Una volta applicati, i contenuti possono essere interrogati utilizzando il nome e il valore del tag.', - 'tag_name' => 'Nome Tag', - 'tag_value' => 'Valore (Opzionale)', + 'tags_index_desc' => 'I tag possono essere applicati ai contenuti del sistema per applicare una forma flessibile di categorizzazione. I tag possono avere una chiave e un valore, il valore è opzionale. Una volta applicati, i contenuti possono essere cercati utilizzando il nome e il valore del tag.', + 'tag_name' => 'Nome del tag', + 'tag_value' => 'Valore tag (opzionale)', 'tags_explain' => "Aggiungi tag per categorizzare meglio il contenuto. \n Puoi assegnare un valore ai tag per una migliore organizzazione.", 'tags_add' => 'Aggiungi un altro tag', 'tags_remove' => 'Rimuovi questo tag', - 'tags_usages' => 'Utilizzo totale dei tag', - 'tags_assigned_pages' => 'Assegnato alle Pagine', + 'tags_usages' => 'Utilizzi totali dei tag', + 'tags_assigned_pages' => 'Assegnato alle pagine', 'tags_assigned_chapters' => 'Assegnato ai capitoli', - 'tags_assigned_books' => 'Assegnato a Libri', - 'tags_assigned_shelves' => 'Assegnato alle Librerie', + 'tags_assigned_books' => 'Assegnato ai libri', + 'tags_assigned_shelves' => 'Assegnato alle librerie', 'tags_x_unique_values' => ':count valori univoci', 'tags_all_values' => 'Tutti i valori', 'tags_view_tags' => 'Visualizza tag', 'tags_view_existing_tags' => 'Usa i tag esistenti', - 'tags_list_empty_hint' => 'I tag possono essere assegnati tramite la barra laterale dell\'editor di pagina o durante la modifica dei dettagli di un libro, capitolo o libreria.', + 'tags_list_empty_hint' => 'I tag possono essere assegnati tramite la barra laterale dell\'editor di pagina o durante la modifica dei dettagli di un libro, di un capitolo o di una libreria.', 'attachments' => 'Allegati', - 'attachments_explain' => 'Carica alcuni file o allega link per visualizzarli nella pagina. Questi sono visibili nella sidebar della pagina.', + 'attachments_explain' => 'Carica alcuni file o allega dei collegamenti per visualizzarli nella pagina. Questi sono visibili nella barra laterale della pagina.', 'attachments_explain_instant_save' => 'I cambiamenti qui sono salvati istantaneamente.', - 'attachments_upload' => 'Carica File', - 'attachments_link' => 'Allega Link', + 'attachments_upload' => 'Carica file', + 'attachments_link' => 'Allega collegamento', 'attachments_upload_drop' => 'In alternativa puoi trascinare un file qui per caricarlo come allegato.', - 'attachments_set_link' => 'Imposta Link', - 'attachments_delete' => 'Sei sicuro di voler eliminare questo allegato?', + 'attachments_set_link' => 'Imposta collegamento', + 'attachments_delete' => 'Vuoi davvero eliminare questo allegato?', 'attachments_dropzone' => 'Trascina qui i file da caricare', 'attachments_no_files' => 'Nessun file è stato caricato', - 'attachments_explain_link' => 'Puoi allegare un link se preferisci non caricare un file. Questo può essere un link a un\'altra pagina o a un file nel cloud.', - 'attachments_link_name' => 'Nome Link', - 'attachment_link' => 'Link allegato', - 'attachments_link_url' => 'Link al file', + 'attachments_explain_link' => 'Puoi allegare un collegamento se preferisci non caricare un file. Questo può essere un collegamento a un\'altra pagina o a un file nel cloud.', + 'attachments_link_name' => 'Nome collegamento', + 'attachment_link' => 'Collegamento allegato', + 'attachments_link_url' => 'Collegamento al file', 'attachments_link_url_hint' => 'Url del sito o del file', 'attach' => 'Allega', - 'attachments_insert_link' => 'Aggiungi Link Allegato alla Pagina', - 'attachments_edit_file' => 'Modifica File', - 'attachments_edit_file_name' => 'Nome File', - 'attachments_edit_drop_upload' => 'Rilascia file o clicca qui per caricare e sovrascrivere', + 'attachments_insert_link' => 'Aggiungi allegato collegamento alla pagina', + 'attachments_edit_file' => 'Modifica file', + 'attachments_edit_file_name' => 'Nome file', + 'attachments_edit_drop_upload' => 'Trascina file qui o clicca per caricare e sovrascrivere', 'attachments_order_updated' => 'Ordine allegato aggiornato', 'attachments_updated_success' => 'Dettagli allegato aggiornati', 'attachments_deleted' => 'Allegato eliminato', 'attachments_file_uploaded' => 'File caricato correttamente', 'attachments_file_updated' => 'File aggiornato correttamente', - 'attachments_link_attached' => 'Link allegato correttamente alla pagina', - 'templates' => 'Template', - 'templates_set_as_template' => 'La pagina è un template', - 'templates_explain_set_as_template' => 'Puoi impostare questa pagina come template in modo che il suo contenuto sia utilizzato quando si creano altre pagine. Gli altri utenti potranno utilizzare questo template se avranno i permessi di visualizzazione per questa pagina.', + 'attachments_link_attached' => 'Collegamento allegato correttamente alla pagina', + 'templates' => 'Modello', + 'templates_set_as_template' => 'La pagina è un modello', + 'templates_explain_set_as_template' => 'Puoi impostare questa pagina come modello in modo da utilizzare il suo contenuto quando si creano altre pagine. Gli altri utenti potranno utilizzare questo modello se avranno i permessi di visualizzazione per questa pagina.', 'templates_replace_content' => 'Rimpiazza contenuto della pagina', 'templates_append_content' => 'Appendi al contenuto della pagina', - 'templates_prepend_content' => 'Prependi al contenuto della pagina', + 'templates_prepend_content' => 'Anteponi al contenuto della pagina', // Profile View 'profile_user_for_x' => 'Utente da :time', - 'profile_created_content' => 'Contenuti Creati', + 'profile_created_content' => 'Contenuti creati', 'profile_not_created_pages' => ':userName non ha creato pagine', 'profile_not_created_chapters' => ':userName non ha creato capitoli', 'profile_not_created_books' => ':userName non ha creato libri', - 'profile_not_created_shelves' => ':userName non ha creato alcuna libreria', + 'profile_not_created_shelves' => ':userName non ha creato librerie', // Comments 'comment' => 'Commento', 'comments' => 'Commenti', - 'comment_add' => 'Aggiungi Commento', + 'comment_add' => 'Aggiungi commento', + 'comment_none' => 'Nessun commento da visualizzare', 'comment_placeholder' => 'Scrivi un commento', - 'comment_count' => '{0} Nessun Commento|{1} 1 Commento|[2,*] :count Commenti', - 'comment_save' => 'Salva Commento', - 'comment_new' => 'Nuovo Commento', + 'comment_thread_count' => ':count Commento Thread| :count Commenti Threads', + 'comment_archived_count' => ':count Archiviato', + 'comment_archived_threads' => 'Discussioni Archiviate', + 'comment_save' => 'Salva commento', + 'comment_new' => 'Nuovo commento', 'comment_created' => 'ha commentato :createDiff', 'comment_updated' => 'Aggiornato :updateDiff da :username', 'comment_updated_indicator' => 'Aggiornato', 'comment_deleted_success' => 'Commento eliminato', 'comment_created_success' => 'Commento aggiunto', 'comment_updated_success' => 'Commento aggiornato', - 'comment_delete_confirm' => 'Sei sicuro di voler elminare questo commento?', + 'comment_archive_success' => 'Commento archiviato', + 'comment_unarchive_success' => 'Commento ripristinato', + 'comment_view' => 'Visualizza commento', + 'comment_jump_to_thread' => 'Vai al thread', + 'comment_delete_confirm' => 'Sei sicuro di voler eliminare questo commento?', 'comment_in_reply_to' => 'In risposta a :commentId', + 'comment_reference' => 'Riferimento', + 'comment_reference_outdated' => '(Obsoleto)', 'comment_editor_explain' => 'Ecco i commenti che sono stati lasciati in questa pagina. I commenti possono essere aggiunti e gestiti quando si visualizza la pagina salvata.', // Revision @@ -387,23 +426,23 @@ 'revision_cannot_delete_latest' => 'Impossibile eliminare l\'ultima revisione.', // Copy view - 'copy_consider' => 'Per favore, considerate quanto segue quando copiate il contenuto.', + 'copy_consider' => 'Considera quanto segue quando copi il contenuto.', 'copy_consider_permissions' => 'Le impostazioni dei permessi personalizzati non saranno copiate.', 'copy_consider_owner' => 'Diventerai il proprietario di tutti i contenuti copiati.', 'copy_consider_images' => 'I file delle immagini delle pagine non saranno duplicati e le immagini originali manterranno la loro relazione con la pagina su cui sono state originariamente caricate.', 'copy_consider_attachments' => 'Gli allegati della pagina non saranno copiati.', - 'copy_consider_access' => 'Un cambiamento di luogo, di proprietario o di autorizzazioni può far sì che questo contenuto sia accessibile a chi prima non aveva accesso.', + 'copy_consider_access' => 'Un cambiamento di posizione, di proprietario o di autorizzazioni potrebbe rendere questo contenuto accessibile a chi prima non aveva accesso.', // Conversions - 'convert_to_shelf' => 'Converti in Libreria', - 'convert_to_shelf_contents_desc' => 'È possibile convertire questo libro in una nuova libreria con gli stessi contenuti. I capitoli contenuti in questo libro saranno convertiti in nuovi libri. Se il libro contiene pagine che non fanno parte di un capitolo, questo libro verrà rinominato e conterrà tali pagine e diventerà parte della nuova libreria.', - 'convert_to_shelf_permissions_desc' => 'Tutti i permessi impostati su questo libro saranno copiati sulla nuova libreria e su tutti i nuovi libri figli che non hanno i loro permessi applicati. Si noti che i permessi delle librerie non si trasmettono automaticamente ai contenuti al loro interno, come avviene per i libri.', - 'convert_book' => 'Converti Libro', + 'convert_to_shelf' => 'Converti in libreria', + 'convert_to_shelf_contents_desc' => 'Puoi convertire questo libro in una nuova libreria con gli stessi contenuti. I capitoli contenuti in questo libro saranno convertiti in nuovi libri. Se il libro contiene pagine che non fanno parte di un capitolo, questo libro verrà rinominato e conterrà tali pagine e diventerà parte della nuova libreria.', + 'convert_to_shelf_permissions_desc' => 'Tutti i permessi impostati su questo libro saranno copiati sulla nuova libreria e su tutti i nuovi libri figli che non hanno i loro permessi applicati. Nota che i permessi delle librerie non si trasmettono automaticamente ai contenuti al loro interno, come avviene per i libri.', + 'convert_book' => 'Converti libro', 'convert_book_confirm' => 'Sei sicuro di voler convertire questo libro?', 'convert_undo_warning' => 'Questo non può essere annullato con la stessa facilità.', 'convert_to_book' => 'Converti in libro', 'convert_to_book_desc' => 'È possibile convertire questo capitolo in un nuovo libro con gli stessi contenuti. Tutti i permessi impostati su questo capitolo saranno copiati nel nuovo libro, ma i permessi ereditati dal libro principale non saranno copiati, il che potrebbe portare a una modifica del controllo degli accessi.', - 'convert_chapter' => 'Converti Capitolo', + 'convert_chapter' => 'Converti capitolo', 'convert_chapter_confirm' => 'Sei sicuro di voler convertire questo capitolo?', // References @@ -413,25 +452,25 @@ // Watch Options 'watch' => 'Osserva', - 'watch_title_default' => 'Preferenze Predefinite', - 'watch_desc_default' => 'Ripristina la visualizzazione delle tue preferenze di notifica predefinite.', + 'watch_title_default' => 'Preferenze predefinite', + 'watch_desc_default' => 'Ripristina l\'osservazione alle tue preferenze di notifica predefinite.', 'watch_title_ignore' => 'Ignora', - 'watch_desc_ignore' => 'Ignora tutte le notifiche, comprese quelle dalle preferenze di livello utente.', - 'watch_title_new' => 'Nuove Pagine', + 'watch_desc_ignore' => 'Ignora tutte le notifiche, incluse quelle dalle preferenze a livello utente.', + 'watch_title_new' => 'Nuove pagine', 'watch_desc_new' => 'Notifica quando viene creata una nuova pagina all\'interno di questo elemento.', - 'watch_title_updates' => 'Tutti Gli Aggiornamenti Della Pagina', - 'watch_desc_updates' => 'Notificare su tutte le nuove pagine e modifiche di pagina.', - 'watch_desc_updates_page' => 'Notifica su tutte le modifiche alla pagina.', - 'watch_title_comments' => 'Tutti Gli Aggiornamenti Della Pagina E Commenti', - 'watch_desc_comments' => 'Notificare su tutte le nuove pagine, modifiche di pagina e nuovi commenti.', - 'watch_desc_comments_page' => 'Notificare le modifiche alla pagina e i nuovi commenti.', + 'watch_title_updates' => 'Tutti gli aggiornamenti della pagina', + 'watch_desc_updates' => 'Notifica tutte le nuove pagine e le modifiche alle pagine.', + 'watch_desc_updates_page' => 'Notifica tutte le modifiche alla pagine.', + 'watch_title_comments' => 'Tutti gli aggiornamenti delle pagine e i commenti', + 'watch_desc_comments' => 'Notifica tutte le nuove pagine, le modifiche alle pagine e i nuovi commenti.', + 'watch_desc_comments_page' => 'Notifica le modifiche alla pagina e i nuovi commenti.', 'watch_change_default' => 'Modifica le preferenze di notifica predefinite', 'watch_detail_ignore' => 'Ignorare le notifiche', - 'watch_detail_new' => 'In attesa di nuove pagine', + 'watch_detail_new' => 'Osservare le nuove pagine', 'watch_detail_updates' => 'Osservare le nuove pagine e gli aggiornamenti', 'watch_detail_comments' => 'Osservare le nuove pagine, aggiornamenti e commenti', - 'watch_detail_parent_book' => 'Osservare tramite il libro madre', - 'watch_detail_parent_book_ignore' => 'Ignorato tramite il libro madre', - 'watch_detail_parent_chapter' => 'Osservare tramite il capitolo madre', - 'watch_detail_parent_chapter_ignore' => 'Ignorato tramite il capitolo madre', + 'watch_detail_parent_book' => 'Osservare tramite il libro che lo contiene', + 'watch_detail_parent_book_ignore' => 'Ignorare tramite il libro che lo contiene', + 'watch_detail_parent_chapter' => 'Osservare tramite il capitolo che lo contiene', + 'watch_detail_parent_chapter_ignore' => 'Ignorato tramite il capitolo che lo contiene', ]; diff --git a/lang/it/errors.php b/lang/it/errors.php index 6ec9c36a011..b3763cce252 100644 --- a/lang/it/errors.php +++ b/lang/it/errors.php @@ -10,6 +10,7 @@ // Auth 'error_user_exists_different_creds' => 'Un utente con la mail :email esiste già ma con credenziali differenti.', + 'auth_pre_register_theme_prevention' => 'Non è stato possibile registrare l\'account utente coi dettagli forniti', 'email_already_confirmed' => 'La mail è già stata confermata, esegui il login.', 'email_confirmation_invalid' => 'Questo token di conferma non è valido o già stato utilizzato, registrati nuovamente.', 'email_confirmation_expired' => 'Il token di conferma è scaduto, è stata inviata una nuova mail di conferma.', @@ -23,11 +24,10 @@ 'saml_invalid_response_id' => 'La richiesta dal sistema di autenticazione esterno non è riconosciuta da un processo iniziato da questa applicazione. Tornare indietro dopo un login potrebbe causare questo problema.', 'saml_fail_authed' => 'Accesso con :system non riuscito, il sistema non ha fornito l\'autorizzazione corretta', 'oidc_already_logged_in' => 'Hai già effettuato il login', - 'oidc_user_not_registered' => 'L\'utente :name non è registrato e la registrazione automatica è disabilitata', 'oidc_no_email_address' => 'Impossibile trovare un indirizzo email, per questo utente, nei dati forniti dal sistema di autenticazione esterno', 'oidc_fail_authed' => 'Accesso con :system non riuscito, il sistema non ha fornito l\'autorizzazione', 'social_no_action_defined' => 'Nessuna azione definita', - 'social_login_bad_response' => "Ricevuto error durante il login con :socialAccount : \n:error", + 'social_login_bad_response' => "Ricevuto errore durante il login con :socialAccount : \n:error", 'social_account_in_use' => 'Questo account :socialAccount è già utilizzato, prova a loggarti usando l\'opzione :socialAccount.', 'social_account_email_in_use' => 'La mail :email è già in uso. Se hai già un account puoi connettere il tuo account :socialAccount dalle impostazioni del tuo profilo.', 'social_account_existing' => 'Questo account :socialAccount è già connesso al tuo profilo.', @@ -37,17 +37,18 @@ 'social_driver_not_found' => 'Driver social non trovato', 'social_driver_not_configured' => 'Le impostazioni di :socialAccount non sono configurate correttamente.', 'invite_token_expired' => 'Il link di invito è scaduto. Puoi provare a resettare la password del tuo account.', + 'login_user_not_found' => 'Impossibile trovare un utente per questa azione.', // System 'path_not_writable' => 'Il percorso :filePath non è scrivibile. Controlla che abbia i permessi corretti.', 'cannot_get_image_from_url' => 'Impossibile scaricare immagine da :url', - 'cannot_create_thumbs' => 'Il server non può creare thumbnail. Controlla che l\'estensione GD sia installata.', + 'cannot_create_thumbs' => 'Il server non può creare miniature. Controlla che l\'estensione GD sia installata.', 'server_upload_limit' => 'Il server non permette un upload di questa grandezza. Prova con un file più piccolo.', 'server_post_limit' => 'Il server non può ricevere la quantità di dati fornita. Riprovare con meno dati o con un file più piccolo.', 'uploaded' => 'Il server non consente upload di questa grandezza. Prova un file più piccolo.', // Drawing & Images - 'image_upload_error' => 'C\'è stato un errore caricando l\'immagine', + 'image_upload_error' => 'Si è verificato un errore nel caricamento dell\'immagine', 'image_upload_type_error' => 'Il tipo di immagine caricata non è valido', 'image_upload_replace_type' => 'Le sostituzioni di file immagine devono essere dello stesso tipo', 'image_upload_memory_limit' => 'Impossibile gestire il caricamento d\'immagini e/o creare miniature a causa dei limiti delle risorse di sistema.', @@ -71,37 +72,50 @@ 'page_not_found' => 'Pagina non trovata', 'chapter_not_found' => 'Capitolo non trovato', 'selected_book_not_found' => 'Il libro selezionato non è stato trovato', - 'selected_book_chapter_not_found' => 'Il libro selezionato o il capitolo non sono stati trovati', + 'selected_book_chapter_not_found' => 'Il libro o il capitolo selezionati non sono stati trovati', 'guests_cannot_save_drafts' => 'Gli ospiti non possono salvare bozze', // Users 'users_cannot_delete_only_admin' => 'Non puoi eliminare l\'unico admin', 'users_cannot_delete_guest' => 'Non puoi eliminare l\'utente ospite', + 'users_could_not_send_invite' => 'Impossibile creare l\'utente poiché l\'invio dell\'email di invito non è riuscito', // Roles 'role_cannot_be_edited' => 'Questo ruolo non può essere modificato', 'role_system_cannot_be_deleted' => 'Questo ruolo è di sistema e non può essere eliminato', - 'role_registration_default_cannot_delete' => 'Questo ruolo non può essere eliminato finchè è impostato come default alla registrazione', - 'role_cannot_remove_only_admin' => 'Questo utente è l\'unico con assegnato il ruolo di amministratore. Assegna il ruolo di amministratore ad un altro utente prima di rimuoverlo qui.', + 'role_registration_default_cannot_delete' => 'Questo ruolo non può essere eliminato finchè è impostato come ruolo di registrazione predefinito', + 'role_cannot_remove_only_admin' => 'Questo utente è l\'unico con assegnato il ruolo di amministratore. Assegna il ruolo di amministratore ad un altro utente prima di rimuoverlo.', // Comments - 'comment_list' => 'C\'è stato un errore scaricando i commenti.', + 'comment_list' => 'Si è verificato un errore durante il recupero dei commenti.', 'cannot_add_comment_to_draft' => 'Non puoi aggiungere commenti a una bozza.', - 'comment_add' => 'C\'è stato un errore aggiungendo / aggiornando il commento.', - 'comment_delete' => 'C\'è stato un errore eliminando il commento.', + 'comment_add' => 'Si è verificato un errore durante l\'aggiunta / l\'aggiornamento del commento.', + 'comment_delete' => 'Si è verificato un errore durante l’eliminazione del commento.', 'empty_comment' => 'Impossibile aggiungere un commento vuoto.', // Error pages - '404_page_not_found' => 'Pagina Non Trovata', - 'sorry_page_not_found' => 'La pagina che stavi cercando non è stata trovata.', + '404_page_not_found' => 'Pagina non trovata', + 'sorry_page_not_found' => 'Siamo spiacenti, la pagina che stavi cercando non è stata trovata.', 'sorry_page_not_found_permission_warning' => 'Se pensi che questa pagina possa esistere, potresti non avere i permessi per visualizzarla.', 'image_not_found' => 'Immagine non trovata', 'image_not_found_subtitle' => 'Spiacente, l\'immagine che stai cercando non è stata trovata.', - 'image_not_found_details' => 'Se ti aspettavi che questa immagine esistesse, potrebbe essere stata cancellata.', + 'image_not_found_details' => 'Se pensi che questa immagine possa esistere, potrebbe essere stata cancellata.', 'return_home' => 'Ritorna alla home', - 'error_occurred' => 'C\'è Stato un errore', - 'app_down' => ':appName è offline', - 'back_soon' => 'Ritornerà presto.', + 'error_occurred' => 'Si è verificato un errore', + 'app_down' => ':appName è offline al momento', + 'back_soon' => 'Tornerà presto online.', + + // Import + 'import_zip_cant_read' => 'Impossibile leggere il file ZIP.', + 'import_zip_cant_decode_data' => 'Impossibile trovare e decodificare il contenuto ZIP data.json.', + 'import_zip_no_data' => 'I dati del file ZIP non hanno il contenuto previsto di libri, capitoli o pagine.', + 'import_validation_failed' => 'L\'importazione ZIP non è stata convalidata con errori:', + 'import_zip_failed_notification' => 'Impossibile importare il file ZIP.', + 'import_perms_books' => 'Non hai i permessi necessari per creare libri.', + 'import_perms_chapters' => 'Non hai i permessi necessari per creare capitoli.', + 'import_perms_pages' => 'Non hai i permessi necessari per creare pagine.', + 'import_perms_images' => 'Non hai i permessi necessari per creare immagini.', + 'import_perms_attachments' => 'Non hai il permesso necessario per creare allegati.', // API errors 'api_no_authorization_found' => 'Nessun token di autorizzazione trovato nella richiesta', diff --git a/lang/it/notifications.php b/lang/it/notifications.php index 3b902600f37..6b8932ebee0 100644 --- a/lang/it/notifications.php +++ b/lang/it/notifications.php @@ -4,23 +4,23 @@ */ return [ - 'new_comment_subject' => 'Nuovo commento alla pagina: :pageName', + 'new_comment_subject' => 'Nuovo commento sulla pagina: :pageName', 'new_comment_intro' => 'Un utente ha commentato una pagina in :appName:', 'new_page_subject' => 'Nuova pagina: :pageName', 'new_page_intro' => 'Una nuova pagina è stata creata in :appName:', 'updated_page_subject' => 'Pagina aggiornata: :pageName', 'updated_page_intro' => 'Una pagina è stata aggiornata in :appName:', - 'updated_page_debounce' => 'Per evitare una massa di notifiche, per un po \'non verranno inviate notifiche per ulteriori modifiche a questa pagina dallo stesso editor.', + 'updated_page_debounce' => 'Per evitare una massa di notifiche, per un po\' non ti verranno inviate notifiche per ulteriori modifiche a questa pagina dallo stesso editor.', - 'detail_page_name' => 'Nome Della Pagina:', + 'detail_page_name' => 'Nome della pagina:', 'detail_page_path' => 'Percorso della pagina:', 'detail_commenter' => 'Commentatore:', 'detail_comment' => 'Commento:', - 'detail_created_by' => 'Creato Da:', - 'detail_updated_by' => 'Aggiornato Da:', + 'detail_created_by' => 'Creato da:', + 'detail_updated_by' => 'Aggiornato da:', - 'action_view_comment' => 'Visualizza Commento', - 'action_view_page' => 'Visualizza Pagina', + 'action_view_comment' => 'Vedi commento', + 'action_view_page' => 'Vedi pagina', 'footer_reason' => 'Questa notifica è stata inviata perché :link copre questo tipo di attività per questo elemento.', 'footer_reason_link' => 'le tue preferenze di notifica', diff --git a/lang/it/passwords.php b/lang/it/passwords.php index 7099d54f37d..ac46ca06f15 100644 --- a/lang/it/passwords.php +++ b/lang/it/passwords.php @@ -6,8 +6,8 @@ */ return [ - 'password' => 'La password deve avere almeno sei caratteri e corrispondere alla conferma.', - 'user' => "Non possiamo trovare un utente per quella mail.", + 'password' => 'La password deve avere almeno otto caratteri e corrispondere alla conferma.', + 'user' => "Nessun utente trovato per quella mail.", 'token' => 'Il token per reimpostare la password non è valido per questo indirizzo email.', 'sent' => 'Ti abbiamo inviato via mail il link per reimpostare la password!', 'reset' => 'La tua password è stata reimpostata!', diff --git a/lang/it/preferences.php b/lang/it/preferences.php index c06374e6926..f251384011b 100644 --- a/lang/it/preferences.php +++ b/lang/it/preferences.php @@ -5,47 +5,47 @@ */ return [ - 'my_account' => 'Il Mio Account', + 'my_account' => 'Il mio account', 'shortcuts' => 'Scorciatoie', 'shortcuts_interface' => 'Preferenze di scelta rapida dell\'Interfaccia Utente', 'shortcuts_toggle_desc' => 'Qui puoi abilitare o disabilitare le scorciatoie dell\'interfaccia di sistema da tastiera, utilizzate per la navigazione e le azioni.', - 'shortcuts_customize_desc' => 'È possibile personalizzare ciascuna delle scorciatoie riportate di seguito. È sufficiente premere la combinazione di tasti desiderata dopo aver selezionato l\'input per una scelta rapida.', + 'shortcuts_customize_desc' => 'È possibile personalizzare ciascuna delle scorciatoie riportate di seguito. È sufficiente premere la combinazione di tasti desiderata dopo aver selezionato l\'input per una scorciatoia.', 'shortcuts_toggle_label' => 'Scorciatoie da tastiera attivate', 'shortcuts_section_navigation' => 'Navigazione', - 'shortcuts_section_actions' => 'Azioni Comuni', - 'shortcuts_save' => 'Salva Scorciatoie', + 'shortcuts_section_actions' => 'Azioni comuni', + 'shortcuts_save' => 'Salva scorciatoie', 'shortcuts_overlay_desc' => 'Nota: quando le scorciatoie sono abilitate, premendo "?" è possibile visualizzare le scorciatoie disponibili per le azioni attualmente visibili sullo schermo.', 'shortcuts_update_success' => 'Le preferenze delle scorciatoie sono state aggiornate!', 'shortcuts_overview_desc' => 'Gestisci le scorciatoie da tastiera che puoi usare per navigare nell\'interfaccia utente di sistema.', - 'notifications' => 'Preferenze Di Notifica', + 'notifications' => 'Preferenze notifiche', 'notifications_desc' => 'Controlla le notifiche email che ricevi quando viene eseguita una determinata attività all\'interno del sistema.', 'notifications_opt_own_page_changes' => 'Notifica in caso di modifiche alle pagine che possiedo', 'notifications_opt_own_page_comments' => 'Notifica i commenti sulle pagine che possiedo', 'notifications_opt_comment_replies' => 'Notificare le risposte ai miei commenti', - 'notifications_save' => 'Salva Preferenze', + 'notifications_save' => 'Salva preferenze', 'notifications_update_success' => 'Le preferenze di notifica sono state aggiornate!', - 'notifications_watched' => 'Oggetti Osservati E Ignorati', - 'notifications_watched_desc' => 'Di seguito sono riportati gli articoli a cui sono state applicate le preferenze di monitoraggio personalizzate. Per aggiornare le preferenze, visualizzare l\'articolo e trovare le opzioni di monitoraggio nella barra laterale.', + 'notifications_watched' => 'Oggetti osservati e ignorati', + 'notifications_watched_desc' => 'Di seguito sono riportati gli elementi a cui sono state applicate le preferenze di monitoraggio personalizzate. Per aggiornare le preferenze, visualizza l\'elemento e usa le opzioni di monitoraggio nella barra laterale.', - 'auth' => 'Accesso E Sicurezza', + 'auth' => 'Accesso e sicurezza', 'auth_change_password' => 'Modifica password', 'auth_change_password_desc' => 'Modifica la password che usi per accedere all\'applicazione. Deve essere lunga almeno 8 caratteri.', 'auth_change_password_success' => 'La password è stata aggiornata!', 'profile' => 'Dettagli del profilo', 'profile_desc' => 'Gestisci i dettagli dell\'account che ti rappresenta agli altri utenti, oltre ai dettagli utilizzati per la comunicazione e la personalizzazione del sistema.', - 'profile_view_public' => 'Visualizza Profilo Pubblico', + 'profile_view_public' => 'Visualizza profilo pubblico', 'profile_name_desc' => 'Configura il tuo nome visualizzato che sarà visibile ad altri utenti del sistema attraverso l\'attività che esegui e il contenuto che possiedi.', 'profile_email_desc' => 'Questa email verrà utilizzata per le notifiche e, a seconda dell\'autenticazione al sistema attiva, per l\'accesso al sistema.', - 'profile_email_no_permission' => 'Purtroppo non hai il permesso di modificare il tuo indirizzo email. Se si desidera modificarlo, è necessario chiedere ad un amministratore di farlo per te.', + 'profile_email_no_permission' => 'Purtroppo non hai il permesso di modificare il tuo indirizzo email. Se vuoi modificarlo, devi chiedere a un amministratore di farlo per te.', 'profile_avatar_desc' => 'Seleziona un\'immagine che verrà usata per rappresentarti agli altri utenti del sistema. Idealmente questa immagine dovrebbe essere quadrata e di circa 256px in larghezza e altezza.', - 'profile_admin_options' => 'Opzioni Amministratore', + 'profile_admin_options' => 'Opzioni amministratore', 'profile_admin_options_desc' => 'Ulteriori opzioni di livello amministrativo, come quelle per gestire le assegnazioni dei ruoli, possono essere trovate per il tuo account utente nell\'area "Impostazioni > Utenti".', - 'delete_account' => 'Elimina Account', - 'delete_my_account' => 'Elimina Il Mio Account', + 'delete_account' => 'Elimina account', + 'delete_my_account' => 'Elimina il mio account', 'delete_my_account_desc' => 'Questa azione eliminerà completamente il tuo account utente dal sistema. Non sarai in grado di recuperare l\'account né di annullare questa azione. Il contenuto che hai creato, come le pagine create e le immagini caricate, rimarrà nel sistema.', 'delete_my_account_warning' => 'Sei sicuro di voler eliminare il tuo account?', ]; diff --git a/lang/it/settings.php b/lang/it/settings.php index 9ddde05b494..8bda7f8ffe9 100644 --- a/lang/it/settings.php +++ b/lang/it/settings.php @@ -8,63 +8,63 @@ // Common Messages 'settings' => 'Impostazioni', - 'settings_save' => 'Salva Impostazioni', - 'system_version' => 'Versione Del Sistema', + 'settings_save' => 'Salva impostazioni', + 'system_version' => 'Versione del sistema', 'categories' => 'Categorie', // App Settings 'app_customization' => 'Personalizzazione', - 'app_features_security' => 'Funzioni & Sicurezza', + 'app_features_security' => 'Funzioni e sicurezza', 'app_name' => 'Nome applicazione', - 'app_name_desc' => 'Questo nome è mostrato nell\'header e in tutte le mail.', - 'app_name_header' => 'Mostrare il nome nell\'header', - 'app_public_access' => 'Accesso Pubblico', + 'app_name_desc' => 'Questo nome è mostrato nell\'intestazione e in tutte le email inviate dal sistema.', + 'app_name_header' => 'Mostra il nome nell\'header', + 'app_public_access' => 'Accesso pubblico', 'app_public_access_desc' => 'Abilitando questa opzione, i visitatori, che non sono loggati, potranno accedere ai contenuti nella tua istanza BookStack.', 'app_public_access_desc_guest' => 'L\'accesso ai visitatori pubblici può essere controllato attraverso l\'utente "Guest".', 'app_public_access_toggle' => 'Permetti accesso pubblico', 'app_public_viewing' => 'Consentire la visione pubblica?', - 'app_secure_images' => 'Abilitare una sicurezza maggiore per le immagini caricate?', - 'app_secure_images_toggle' => 'Abilita sicurezza aggiuntiva negli upload delle immagini', - 'app_secure_images_desc' => 'Per una ragione di prestazioni, tutte le immagini sono pubbliche. Questa opzione aaggiunge una stringa, difficile da indovinare, random negli url delle immagini. Assicurati che il listing delle cartelle non sia abilitato per prevenire un accesso semplice.', - 'app_default_editor' => 'Editor Di Pagina Predefinito', - 'app_default_editor_desc' => 'Seleziona quale editor sarà usato per impostazione predefinita quando modifichi nuove pagine. Questa impostazione potrà essere sovrascritta a livello di pagina dove i permessi lo permettano.', - 'app_custom_html' => 'Contenuto Head HTML Custom', + 'app_secure_images' => 'Sicurezza aggiuntiva per le immagini caricate', + 'app_secure_images_toggle' => 'Abilita sicurezza aggiuntiva per le immagini caricate', + 'app_secure_images_desc' => 'Per ragioni di prestazioni, tutte le immagini sono pubbliche. Questa opzione aggiunge una stringa casuale, difficile da indovinare, davanti agli url delle immagini. Assicurati che l\'indicizzazione delle cartelle non sia abilitato per prevenire un accesso semplice.', + 'app_default_editor' => 'Editor di pagina predefinito', + 'app_default_editor_desc' => 'Seleziona quale editor sarà usato per impostazione predefinita quando modifichi nuove pagine. Questa impostazione potrà essere sovrascritta a livello di pagina dove i permessi lo consentono.', + 'app_custom_html' => 'Contenuto Head HTML personalizzato', 'app_custom_html_desc' => 'Qualsiasi contenuto aggiunto qui verrà inserito alla fine della sezione di tutte le pagine. Questo è utile per sovrascrivere lo stile o aggiungere il codice per gli analytics.', - 'app_custom_html_disabled_notice' => 'Il contenuto HTML personalizzato è disabilitato su questa pagina impostazioni per garantire che eventuali modifiche possano essere ripristinate.', + 'app_custom_html_disabled_notice' => 'Il contenuto Head HTML personalizzato è disabilitato su questa pagina delle impostazioni per garantire che eventuali modifiche distruttive possano essere annullate.', 'app_logo' => 'Logo applicazione', 'app_logo_desc' => 'Viene utilizzata nella barra di intestazione dell\'applicazione, tra le altre aree. L\'immagine deve avere un\'altezza di 86px. Le immagini più grandi saranno ridimensionate.', - 'app_icon' => 'Icona Applicazione', + 'app_icon' => 'Icona applicazione', 'app_icon_desc' => 'Questa icona viene utilizzata per le schede del browser e per le icone di collegamento. Deve essere un\'immagine PNG quadrata di 256px.', - 'app_homepage' => 'Homepage Applicazione', - 'app_homepage_desc' => 'Seleziona una pagina da mostrare nella home anzichè quella di default. I permessi della pagina sono ignorati per quella selezionata.', + 'app_homepage' => 'Homepage applicazione', + 'app_homepage_desc' => 'Seleziona una pagina da mostrare nella home anzichè quella di default. I permessi della pagina sono ignorati per le pagine selezionate.', 'app_homepage_select' => 'Seleziona una pagina', - 'app_footer_links' => 'Link in basso', + 'app_footer_links' => 'Link a piè di pagina', 'app_footer_links_desc' => 'Aggiungi link da mostrare in basso nel sito. Questi saranno visibili in fondo alla maggior parte delle pagine, incluse quelle che non richiedono un autenticazione. Puoi usare l\'etichetta "trans::" per utilizzare le traduzioni implementate nella piattaforma. Esempio: usando "trans::common.privacy_policy" mostrerà il testo tradotto "Norme sulla privacy" e "trans::common.terms_of_service" mostrerà il testo tradotto "Condizioni del Servizio".', - 'app_footer_links_label' => 'Etichetta del Link', - 'app_footer_links_url' => 'URL del Link', - 'app_footer_links_add' => 'Aggiungi Link in basso', - 'app_disable_comments' => 'Disattiva commenti', - 'app_disable_comments_toggle' => 'Disabilita commenti', - 'app_disable_comments_desc' => 'Disabilita i commenti su tutte le pagine nell\'applicazione. I commenti esistenti non sono mostrati. ', + 'app_footer_links_label' => 'Etichetta del link', + 'app_footer_links_url' => 'URL del link', + 'app_footer_links_add' => 'Aggiungi link in basso', + 'app_disable_comments' => 'Disabilita commenti', + 'app_disable_comments_toggle' => 'Disabilita i commenti', + 'app_disable_comments_desc' => 'Disabilita i commenti su tutte le pagine dell\'applicazione.
I commenti esistenti non sono mostrati.', // Color settings 'color_scheme' => 'Schema di colore dell\'applicazione', - 'color_scheme_desc' => 'Impostare i colori da utilizzare nell\'interfaccia utente dell\'applicazione. I colori possono essere configurati separatamente per le modalità scura e chiara, per adattarsi al meglio al tema e garantire la leggibilità.', + 'color_scheme_desc' => 'Imposta i colori da utilizzare nell\'interfaccia utente dell\'applicazione. I colori possono essere configurati separatamente per le modalità scura e chiara, per adattarsi al meglio al tema e garantire la leggibilità.', 'ui_colors_desc' => 'Imposta il colore primario dell\'applicazione e il colore predefinito dei collegamenti. Il colore primario è utilizzato principalmente per il banner dell\'intestazione, i pulsanti e le decorazioni dell\'interfaccia. Il colore predefinito dei collegamenti viene utilizzato per i collegamenti e le azioni basate sul testo, sia all\'interno dei contenuti scritti che nell\'interfaccia dell\'applicazione.', - 'app_color' => 'Colore Principale', + 'app_color' => 'Colore principale', 'link_color' => 'Colore preferito del link', 'content_colors_desc' => 'Impostare i colori per tutti gli elementi nella gerarchia dell\'organizzazione della pagina. Si consiglia di scegliere colori con una luminosità simile a quella dei colori predefiniti per garantire la leggibilità.', 'bookshelf_color' => 'Colore della libreria', 'book_color' => 'Colore del libro', 'chapter_color' => 'Colore del capitolo', - 'page_color' => 'Colore della Pagina', + 'page_color' => 'Colore della pagina', 'page_draft_color' => 'Colore della bozza', // Registration Settings - 'reg_settings' => 'Impostazioni Registrazione', - 'reg_enable' => 'Abilita Registrazione', + 'reg_settings' => 'Impostazioni registrazione', + 'reg_enable' => 'Abilita registrazione', 'reg_enable_toggle' => 'Abilita registrazione', - 'reg_enable_desc' => 'Quando la registrazione è abilitata, l\utente sarà in grado di registrarsi all\'applicazione. Al momento della registrazione gli verrà associato un ruolo utente predefinito.', + 'reg_enable_desc' => 'Quando la registrazione è abilitata, l\utente sarà in grado di registrarsi autonomamente all\'applicazione. Al momento della registrazione gli verrà associato un ruolo utente predefinito.', 'reg_default_role' => 'Ruolo predefinito dopo la registrazione', 'reg_enable_external_warning' => 'L\'opzione precedente viene ignorata se l\'autenticazione esterna tramite LDAP o SAML è attiva. Se l\'autenticazione (effettuata sul sistema esterno) sarà valida, gli account di eventuali membri non registrati saranno creati in automatico.', 'reg_email_confirmation' => 'Conferma Email', @@ -74,44 +74,74 @@ 'reg_confirm_restrict_domain_desc' => 'Inserisci una lista separata da virgola di domini di email a cui vorresti restringere la registrazione. Agli utenti verrà inviata una mail per confermare il loro indirizzo prima che possano interagire con l\'applicazione.
Nota che gli utenti saranno in grado di cambiare il loro indirizzo dopo aver completato la registrazione.', 'reg_confirm_restrict_domain_placeholder' => 'Nessuna restrizione impostata', + // Sorting Settings + 'sorting' => 'Ordinamento', + 'sorting_book_default' => 'Ordinamento libri predefinito', + 'sorting_book_default_desc' => 'Selezionare la regola di ordinamento predefinita da applicare ai nuovi libri. Questa regola non influisce sui libri esistenti e può essere modificata per ogni libro.', + 'sorting_rules' => 'Regole di ordinamento', + 'sorting_rules_desc' => 'Si tratta di operazioni di ordinamento predefinite applicabili ai contenuti del sistema.', + 'sort_rule_assigned_to_x_books' => 'Assegnato a :count Book|Assegnato a :count Books', + 'sort_rule_create' => 'Crea Regola Di Ordinamento', + 'sort_rule_edit' => 'Modifica Regola di Ordinamento', + 'sort_rule_delete' => 'Elimina Regola di Ordinamento', + 'sort_rule_delete_desc' => 'Rimuove questa regola di ordinamento dal sistema. I libri che usano questo ordinamento torneranno all\'ordinamento manuale.', + 'sort_rule_delete_warn_books' => 'Questa regola di ordinamento è attualmente utilizzata su :count book(s). Sei sicuro di volerla eliminare?', + 'sort_rule_delete_warn_default' => 'Questa regola di ordinamento è attualmente utilizzata come predefinita per i libri. Sei sicuro di volerla eliminare?', + 'sort_rule_details' => 'Dettagli della regola di ordinamento', + 'sort_rule_details_desc' => 'Imposta un nome per questa regola di ordinamento, apparirà nelle liste quando gli utenti selezionano un ordinamento.', + 'sort_rule_operations' => 'Operazioni di ordinamento', + 'sort_rule_operations_desc' => 'Configurare le azioni di ordinamento da eseguire spostandole dall\'elenco delle operazioni disponibili. Al momento dell\'utilizzo, le operazioni saranno applicate in ordine, dall\'alto verso il basso. Le modifiche apportate qui saranno applicate a tutti i libri assegnati al momento del salvataggio.', + 'sort_rule_available_operations' => 'Operazioni Disponibili', + 'sort_rule_available_operations_empty' => 'Nessuna operazione rimanente', + 'sort_rule_configured_operations' => 'Operazioni Configurate', + 'sort_rule_configured_operations_empty' => 'Trascinare/aggiungere le operazioni dall\'elenco “Operazioni disponibili”', + 'sort_rule_op_asc' => '(Ascendente)', + 'sort_rule_op_desc' => '(Discendente)', + 'sort_rule_op_name' => 'Nome - Alfabetico', + 'sort_rule_op_name_numeric' => 'Name - Numerico', + 'sort_rule_op_created_date' => 'Data di creazione', + 'sort_rule_op_updated_date' => 'Data di aggiornamento', + 'sort_rule_op_chapters_first' => 'Capitoli Prima', + 'sort_rule_op_chapters_last' => 'Capitoli dopo', + // Maintenance settings 'maint' => 'Manutenzione', - 'maint_image_cleanup' => 'Pulizia Immagini', - 'maint_image_cleanup_desc' => 'Esegue la scansione del contenuto delle pagine e delle revisioni per verificare quali immagini e disegni sono attualmente in uso e quali immagini sono ridondanti. Assicurati di creare backup completo del database e delle immagini prima di eseguire la pulizia.', + 'maint_image_cleanup' => 'Pulizia immagini', + 'maint_image_cleanup_desc' => 'Esegue la scansione del contenuto delle pagine e delle revisioni per verificare quali immagini e disegni sono attualmente in uso e quali immagini sono ridondanti. Assicurati di creare un backup completo del database e delle immagini prima di eseguire la pulizia.', 'maint_delete_images_only_in_revisions' => 'Elimina anche le immagini che esistono solo nelle vecchie revisioni della pagina', - 'maint_image_cleanup_run' => 'Esegui Pulizia', - 'maint_image_cleanup_warning' => ':count immagini potenzialmente inutilizzate sono state trovate. Sei sicuro di voler eliminare queste immagini?', + 'maint_image_cleanup_run' => 'Esegui pulizia', + 'maint_image_cleanup_warning' => 'Sono state trovate :count immagini potenzialmente inutilizzate. Sei sicuro di voler eliminare queste immagini?', 'maint_image_cleanup_success' => ':count immagini potenzialmente inutilizzate trovate e eliminate!', - 'maint_image_cleanup_nothing_found' => 'Nessuna immagine non utilizzata trovata, Nulla è stato cancellato!', - 'maint_send_test_email' => 'Invia un Email di Test', - 'maint_send_test_email_desc' => 'Questo invia un\'email di prova al tuo indirizzo email specificato nel tuo profilo.', + 'maint_image_cleanup_nothing_found' => 'Nessuna immagine non utilizzata trovata, non è stato cancellato nulla!', + 'maint_send_test_email' => 'Invia un\'email di test', + 'maint_send_test_email_desc' => 'Questo comando invia un\'email di prova al tuo indirizzo email specificato nel tuo profilo.', 'maint_send_test_email_run' => 'Invia email di test', 'maint_send_test_email_success' => 'Email inviata a :address', - 'maint_send_test_email_mail_subject' => 'Email di Test', + 'maint_send_test_email_mail_subject' => 'Email di test', 'maint_send_test_email_mail_greeting' => 'L\'invio delle email sembra funzionare!', 'maint_send_test_email_mail_text' => 'Congratulazioni! Siccome hai ricevuto questa notifica email, le tue impostazioni sembrano essere configurate correttamente.', 'maint_recycle_bin_desc' => 'Le librerie, i libri, i capitoli e le pagine cancellati vengono inviati al cestino in modo che possano essere ripristinati o eliminati definitivamente. Gli elementi più vecchi nel cestino possono essere automaticamente rimossi dopo un certo periodo, a seconda della configurazione del sistema.', 'maint_recycle_bin_open' => 'Apri il Cestino', - 'maint_regen_references' => 'Rigenera Riferimenti', + 'maint_regen_references' => 'Rigenera riferimenti', 'maint_regen_references_desc' => 'Questa azione ricostruirà l\'indice dei riferimenti incrociati all\'interno del database. Di solito questa operazione è gestita automaticamente, ma può essere utile per indicizzare contenuti vecchi o aggiunti con metodi non ufficiali.', 'maint_regen_references_success' => 'L\'indice di riferimento è stato rigenerato!', - 'maint_timeout_command_note' => 'Nota: Questa azione può richiedere del tempo per essere eseguita e può causare problemi di timeout in alcuni ambienti Web. In alternativa, questa azione può essere eseguita usando un comando da terminale.', + 'maint_timeout_command_note' => 'Nota: Questa azione può richiedere del tempo per essere eseguita e può causare problemi di timeout in alcuni ambienti web. In alternativa, questa azione può essere eseguita usando un comando da terminale.', // Recycle Bin 'recycle_bin' => 'Cestino', 'recycle_bin_desc' => 'Qui è possibile ripristinare gli elementi che sono stati eliminati o scegliere di rimuoverli definitivamente dal sistema. Questo elenco non è filtrato a differenza di elenchi di attività simili nel sistema in cui vengono applicati i filtri autorizzazioni.', - 'recycle_bin_deleted_item' => 'Elimina Elemento', + 'recycle_bin_deleted_item' => 'Elemento eliminato', 'recycle_bin_deleted_parent' => 'Superiore', 'recycle_bin_deleted_by' => 'Cancellato da', 'recycle_bin_deleted_at' => 'Orario Cancellazione', - 'recycle_bin_permanently_delete' => 'Elimina Definitivamente', + 'recycle_bin_permanently_delete' => 'Elimina definitivamente', 'recycle_bin_restore' => 'Ripristina', 'recycle_bin_contents_empty' => 'Al momento il cestino è vuoto', 'recycle_bin_empty' => 'Svuota Cestino', 'recycle_bin_empty_confirm' => 'Questa operazione cancellerà definitivamente tutti gli elementi presenti nel cestino, inclusi i contenuti relativi a ciascun elemento. Sei sicuro di voler svuotare il cestino?', - 'recycle_bin_destroy_confirm' => 'Questa operazione eliminerà permanentemente questo elemento (insieme a tutti i relativi elementi elencati qui sotto) dal sistema e non sarà più possibile recuperarlo. Sei sicuro di voler eliminare permanentemente questo elemento?', - 'recycle_bin_destroy_list' => 'Elementi da Eliminare definitivamente', - 'recycle_bin_restore_list' => 'Elementi da Ripristinare', + 'recycle_bin_destroy_confirm' => 'Questa azione eliminerà definitivamente questo elemento dal sistema, insieme a qualsiasi elemento figlio elencato di seguito, e non sarai in grado di ripristinare questo contenuto. Sei sicuro di voler eliminare definitivamente questo elemento?', + 'recycle_bin_destroy_list' => 'Elementi da eliminare definitivamente', + 'recycle_bin_restore_list' => 'Elementi da ripristinare', 'recycle_bin_restore_confirm' => 'Questa azione ripristinerà l\'elemento eliminato, compresi gli elementi figli, nella loro posizione originale. Se la posizione originale è stata eliminata, ed è ora nel cestino, anche l\'elemento padre dovrà essere ripristinato.', 'recycle_bin_restore_deleted_parent' => 'L\'elemento padre di questo elemento è stato eliminato. Questo elemento rimarrà eliminato fino a che l\'elemento padre non sarà ripristinato.', 'recycle_bin_restore_parent' => 'Ripristina Superiore', @@ -121,13 +151,13 @@ // Audit Log 'audit' => 'Registro di Controllo', 'audit_desc' => 'Questo registro di controllo mostra la lista delle attività registrate dal sistema. Questa lista, a differenza di altre liste del sistema a cui vengono applicate dei filtri, è integrale.', - 'audit_event_filter' => 'Filtra Eventi', - 'audit_event_filter_no_filter' => 'Nessun Filtro', - 'audit_deleted_item' => 'Elimina Elemento', + 'audit_event_filter' => 'Filtro eventi', + 'audit_event_filter_no_filter' => 'Nessun filtro', + 'audit_deleted_item' => 'Elemento eliminato', 'audit_deleted_item_name' => 'Nome: :name', 'audit_table_user' => 'Utente', 'audit_table_event' => 'Evento', - 'audit_table_related' => 'Elemento o Dettaglio correlato', + 'audit_table_related' => 'Elemento o dettaglio correlato', 'audit_table_ip' => 'Indirizzo IP', 'audit_table_date' => 'Data attività', 'audit_date_from' => 'Dalla data', @@ -139,89 +169,90 @@ 'roles_index_desc' => 'I ruoli sono utilizzati per raggruppare gli utenti e fornire ai loro membri i permessi di sistema. Quando un utente è membro di più ruoli, i privilegi concessi si sovrappongono e l\'utente eredita tutte le abilità.', 'roles_x_users_assigned' => ':count utente assegnato|:count utenti assegnati', 'roles_x_permissions_provided' => ':count permesso|:count permessi', - 'roles_assigned_users' => 'Utenti Assegnati', + 'roles_assigned_users' => 'Utenti assegnati', 'roles_permissions_provided' => 'Autorizzazioni fornite', - 'role_create' => 'Crea Nuovo Ruolo', - 'role_delete' => 'Elimina Ruolo', + 'role_create' => 'Crea nuovo ruolo', + 'role_delete' => 'Elimina ruolo', 'role_delete_confirm' => 'Questo eliminerà il ruolo con il nome \':roleName\'.', 'role_delete_users_assigned' => 'Questo ruolo ha :userCount utenti assegnati. Se vuoi migrare gli utenti da questo ruolo selezionane uno nuovo sotto.', 'role_delete_no_migration' => "Non migrare gli utenti", 'role_delete_sure' => 'Sei sicuro di voler eliminare questo ruolo?', - 'role_edit' => 'Modifica Ruolo', - 'role_details' => 'Dettagli Ruolo', - 'role_name' => 'Nome Ruolo', - 'role_desc' => 'Breve Descrizione del Ruolo', + 'role_edit' => 'Modifica ruolo', + 'role_details' => 'Dettagli ruolo', + 'role_name' => 'Nome ruolo', + 'role_desc' => 'Breve descrizione del ruolo', 'role_mfa_enforced' => 'Richiesta autenticazione multi-fattore', - 'role_external_auth_id' => 'ID Autenticazione Esterna', - 'role_system' => 'Permessi di Sistema', + 'role_external_auth_id' => 'ID autenticazione esterna', + 'role_system' => 'Permessi di sistema', 'role_manage_users' => 'Gestire gli utenti', - 'role_manage_roles' => 'Gestire ruoli e permessi di essi', + 'role_manage_roles' => 'Gestire ruoli e permessi di ruoli', 'role_manage_entity_permissions' => 'Gestire tutti i permessi di libri, capitoli e pagine', 'role_manage_own_entity_permissions' => 'Gestire i permessi sui propri libri, capitoli e pagine', - 'role_manage_page_templates' => 'Gestisci template pagine', - 'role_access_api' => 'API sistema d\'accesso', + 'role_manage_page_templates' => 'Gestire modelli di pagina', + 'role_access_api' => 'Accedere alle API di sistema', 'role_manage_settings' => 'Gestire impostazioni app', - 'role_export_content' => 'Esporta contenuto', - 'role_editor_change' => 'Cambia editor di pagina', - 'role_notifications' => 'Ricevi & gestisci le notifiche', - 'role_asset' => 'Permessi Entità', - 'roles_system_warning' => 'Siate consapevoli che l\'accesso a uno dei tre permessi qui sopra, può consentire a un utente di modificare i propri privilegi o i privilegi di altri nel sistema. Assegna ruoli con questi permessi solo ad utenti fidati.', - 'role_asset_desc' => 'Questi permessi controllano l\'accesso di default alle entità. I permessi nei Libri, Capitoli e Pagine sovrascriveranno questi.', + 'role_export_content' => 'Esportare contenuto', + 'role_import_content' => 'Importa contenuto', + 'role_editor_change' => 'Cambiare editor di pagina', + 'role_notifications' => 'Ricevere e gestire le notifiche', + 'role_asset' => 'Permessi entità', + 'roles_system_warning' => 'Siate consapevoli che l\'accesso a uno dei tre permessi qui sopra può consentire a un utente di modificare i propri privilegi o i privilegi di altri nel sistema. Assegna ruoli con questi permessi solo ad utenti fidati.', + 'role_asset_desc' => 'Questi permessi controllano l\'accesso predefinito alle entità. I permessi in libri, capitoli e pagine sovrascriveranno questi.', 'role_asset_admins' => 'Gli amministratori hanno automaticamente accesso a tutti i contenuti ma queste opzioni possono mostrare o nascondere le opzioni della UI.', - 'role_asset_image_view_note' => 'Questo si riferisce alla visibilità all\'interno del gestore di immagini. L\'accesso effettivo ai file di immagine caricati dipenderà dall\'opzione di archiviazione delle immagini di sistema.', + 'role_asset_image_view_note' => 'Questo si riferisce alla visibilità all\'interno del gestore delle immagini. L\'accesso effettivo ai file di immagine caricati dipenderà dall\'opzione di archiviazione delle immagini di sistema.', 'role_all' => 'Tutti', 'role_own' => 'Propri', 'role_controlled_by_asset' => 'Controllato dall\'entità in cui sono caricati', - 'role_save' => 'Salva Ruolo', + 'role_save' => 'Salva ruolo', 'role_users' => 'Utenti in questo ruolo', 'role_users_none' => 'Nessun utente assegnato a questo ruolo', // Users 'users' => 'Utenti', 'users_index_desc' => 'Crea e gestisci account utente individuali all\'interno del sistema. Gli account utente sono utilizzati per il login e l\'attribuzione di contenuti e attività. Le autorizzazioni di accesso sono principalmente basate sui ruoli, ma la proprietà dei contenuti dell\'utente, insieme ad altri fattori, può influenzare le autorizzazioni e l\'accesso.', - 'user_profile' => 'Profilo Utente', + 'user_profile' => 'Profilo utente', 'users_add_new' => 'Aggiungi Nuovo Utente', - 'users_search' => 'Cerca Utenti', - 'users_latest_activity' => 'Ultima Attività', - 'users_details' => 'Dettagli Utente', + 'users_search' => 'Cerca utenti', + 'users_latest_activity' => 'Ultima attività', + 'users_details' => 'Dettagli utente', 'users_details_desc' => 'Imposta un nome e un indirizzo email per questo utente. L\'indirizzo email verrà utilizzato per accedere all\'applicazione.', 'users_details_desc_no_email' => 'Imposta un nome per questo utente così gli altri possono riconoscerlo.', - 'users_role' => 'Ruoli Utente', + 'users_role' => 'Ruoli utente', 'users_role_desc' => 'Seleziona a quali ruoli verrà assegnato questo utente. Se un utente è assegnato a più ruoli riceverà tutte le abilità dei ruoli assegnati.', - 'users_password' => 'Password Utente', + 'users_password' => 'Password utente', 'users_password_desc' => 'Imposta una password usata per accedere all\'applicazione. Deve essere lunga almeno 8 caratteri.', 'users_send_invite_text' => 'Puoi scegliere di inviare a questo utente un\'email di invito che permette loro di impostare la propria password altrimenti puoi impostare la password tu stesso.', 'users_send_invite_option' => 'Invia email di invito', - 'users_external_auth_id' => 'ID Autenticazioni Esterna', + 'users_external_auth_id' => 'ID autenticazione esterna', 'users_external_auth_id_desc' => 'Quando è in uso un sistema di autenticazione esterno (come SAML2, OIDC o LDAP) questo è l\'ID che collega questo utente BookStack all\'account del sistema di autenticazione. È possibile ignorare questo campo se si utilizza l\'autenticazione predefinita basata su email.', 'users_password_warning' => 'Compila la parte sottostante solo se desideri cambiare la password per questo utente.', 'users_system_public' => 'Questo utente rappresente qualsiasi ospite che visita il sito. Non può essere usato per effettuare il login ma è assegnato automaticamente.', - 'users_delete' => 'Elimina Utente', + 'users_delete' => 'Elimina utente', 'users_delete_named' => 'Elimina l\'utente :userName', 'users_delete_warning' => 'Questo eliminerà completamente l\'utente \':userName\' dal sistema.', 'users_delete_confirm' => 'Sei sicuro di voler eliminare questo utente?', - 'users_migrate_ownership' => 'Cambia Proprietario', + 'users_migrate_ownership' => 'Cambia proprietario', 'users_migrate_ownership_desc' => 'Seleziona qui un utente se vuoi che un altro utente diventi il proprietario di tutti gli elementi attualmente di proprietà di questo utente.', 'users_none_selected' => 'Nessun utente selezionato', - 'users_edit' => 'Modifica Utente', - 'users_edit_profile' => 'Modifica Profilo', - 'users_avatar' => 'Avatar Utente', - 'users_avatar_desc' => 'Quest\'immagine dovrebbe essere approssimativamente 256px quadrata.', - 'users_preferred_language' => 'Lingua Preferita', + 'users_edit' => 'Modifica utente', + 'users_edit_profile' => 'Modifica profilo', + 'users_avatar' => 'Avatar utente', + 'users_avatar_desc' => 'Quest\'immagine dovrebbe essere quadrata e alta circa 256px.', + 'users_preferred_language' => 'Lingua preferita', 'users_preferred_language_desc' => 'Questa opzione cambierà la lingua utilizzata per l\'interfaccia utente dell\'applicazione. Questo non influirà su alcun contenuto creato dall\'utente.', - 'users_social_accounts' => 'Account Social', + 'users_social_accounts' => 'Account social', 'users_social_accounts_desc' => 'Visualizza lo stato degli account social connessi per questo utente. Gli account social possono essere utilizzati in aggiunta al sistema di autenticazione primaria per l\'accesso al sistema.', 'users_social_accounts_info' => 'Qui puoi connettere gli altri account per un accesso più veloce e semplice. Disconnettere un account qui non rimuoverà le altre sessioni. Revoca l\'accesso dal tuo profilo negli account social connessi.', - 'users_social_connect' => 'Connetti Account', - 'users_social_disconnect' => 'Disconnetti Account', + 'users_social_connect' => 'Connetti account', + 'users_social_disconnect' => 'Disconnetti account', 'users_social_status_connected' => 'Connesso', 'users_social_status_disconnected' => 'Disconnesso', 'users_social_connected' => 'L\'account :socialAccount è stato connesso correttamente al tuo profilo.', 'users_social_disconnected' => 'L\'account :socialAccount è stato disconnesso correttamente dal tuo profilo.', 'users_api_tokens' => 'Token API', - 'users_api_tokens_desc' => 'Creare e gestire i token di accesso utilizzati per autenticarsi con l\'API REST di BookStack. I permessi per l\'API sono gestiti tramite l\'utente a cui appartiene il token.', + 'users_api_tokens_desc' => 'Crea e gestisci i token di accesso utilizzati per autenticarsi con l\'API REST di BookStack. I permessi per l\'API sono gestiti tramite l\'utente a cui appartiene il token.', 'users_api_tokens_none' => 'Nessun token API è stato creato per questo utente', - 'users_api_tokens_create' => 'Crea Token', + 'users_api_tokens_create' => 'Crea token', 'users_api_tokens_expires' => 'Scade', 'users_api_tokens_docs' => 'Documentazione API', 'users_mfa' => 'Autenticazione multi-fattore', @@ -230,52 +261,60 @@ 'users_mfa_configure' => 'Configura metodi', // API Tokens - 'user_api_token_create' => 'Crea Token API', + 'user_api_token_create' => 'Crea token API', 'user_api_token_name' => 'Nome', 'user_api_token_name_desc' => 'Assegna al tuo token un nome leggibile per ricordarne la funzionalità in futuro.', 'user_api_token_expiry' => 'Data di scadenza', 'user_api_token_expiry_desc' => 'Imposta una data di scadenza per questo token. Dopo questa data, le richieste che utilizzeranno questo token non funzioneranno più. Lasciando questo campo vuoto si imposterà la scadenza tra 100 anni.', - 'user_api_token_create_secret_message' => 'Immediatamente dopo aver creato questo token, un "Token ID" e un "Segreto Token" saranno generati e mostrati. Il segreto verrà mostrato unicamente questa volta, assicurati, quindi, di copiare il valore in un posto sicuro prima di procedere.', + 'user_api_token_create_secret_message' => 'Immediatamente dopo aver creato questo token, un "ID del Token" e una "Chiave segreta Token" saranno generati e mostrati. La chiave segreta verrà mostrata unicamente questa volta, assicurati, quindi, di copiare il valore in un posto sicuro prima di procedere.', 'user_api_token' => 'Token API', - 'user_api_token_id' => 'Token ID', + 'user_api_token_id' => 'ID del Token', 'user_api_token_id_desc' => 'Questo è un identificativo non modificabile generato dal sistema per questo token e che sarà necessario fornire per le richieste tramite API.', - 'user_api_token_secret' => 'Token Segreto', - 'user_api_token_secret_desc' => 'Questo è un segreto generato dal sistema per questo token che sarà necessario fornire per le richieste via API. Questo valore sarà visibile unicamente in questo momento pertanto copialo in un posto sicuro.', + 'user_api_token_secret' => 'Chiave segreta token', + 'user_api_token_secret_desc' => 'Questo è una chiave segreta generata dal sistema per questo token che sarà necessario fornire per le richieste via API. Questo valore sarà visibile unicamente in questo momento pertanto copialo in un posto sicuro.', 'user_api_token_created' => 'Token Aggiornato :timeAgo', - 'user_api_token_updated' => 'Token Aggiornato :timeAgo', - 'user_api_token_delete' => 'Elimina Token', + 'user_api_token_updated' => 'Token aggiornato :timeAgo', + 'user_api_token_delete' => 'Elimina token', 'user_api_token_delete_warning' => 'Questa operazione eliminerà irreversibilmente dal sistema il token API denominato \':tokenName\'.', 'user_api_token_delete_confirm' => 'Sei sicuri di voler eliminare questo token API?', // Webhooks - 'webhooks' => 'Webhooks', - 'webhooks_index_desc' => 'I webhook sono un modo per inviare dati a URL esterne quando si verificano determinate azioni ed eventi all\'interno del sistema, consentendo l\'integrazione basata sugli eventi con piattaforme esterne, come sistemi di messaggistica o di notifica.', + 'webhooks' => 'Webhook', + 'webhooks_index_desc' => 'I webhook sono un modo per inviare dati a URL esterni quando si verificano determinate azioni ed eventi all\'interno del sistema, consentendo l\'integrazione basata sugli eventi con piattaforme esterne, come sistemi di messaggistica o di notifica.', 'webhooks_x_trigger_events' => ':count evento trigger|:count eventi trigger', - 'webhooks_create' => 'Crea Nuovo Webhook', + 'webhooks_create' => 'Crea nuovo webhook', 'webhooks_none_created' => 'Nessun webhook è stato creato.', - 'webhooks_edit' => 'Modifica Webhook', - 'webhooks_save' => 'Salva Webhook', - 'webhooks_details' => 'Dettagli Webhook', - 'webhooks_details_desc' => 'Fornire un nome di facile utilizzo e un endpoint POST come posizione per i dati del webhook da inviare.', - 'webhooks_events' => 'Eventi Webhook', + 'webhooks_edit' => 'Modifica webhook', + 'webhooks_save' => 'Salva webhook', + 'webhooks_details' => 'Dettagli webhook', + 'webhooks_details_desc' => 'Fornisci un nome di facile utilizzo e un endpoint POST come posizione per i dati del webhook da inviare.', + 'webhooks_events' => 'Eventi webhook', 'webhooks_events_desc' => 'Seleziona tutti gli eventi che dovrebbero attivare questo webhook da chiamare.', - 'webhooks_events_warning' => 'Tieni presente che questi eventi saranno attivati per tutti gli eventi selezionati, anche se vengono applicati permessi personalizzati. Assicurarsi che l\'uso di questo webhook non esporrà contenuti riservati.', + 'webhooks_events_warning' => 'Tieni presente che questi eventi saranno attivati per tutti gli eventi selezionati, anche se vengono applicati permessi personalizzati. Assicurati che l\'uso di questo webhook non esporrà contenuti riservati.', 'webhooks_events_all' => 'Tutti gli eventi di sistema', - 'webhooks_name' => 'Nome Webhook', - 'webhooks_timeout' => 'Timeout Richiesta Webhook (Secondi)', - 'webhooks_endpoint' => 'Endpoint Webhook', - 'webhooks_active' => 'Webhook Attivo', + 'webhooks_name' => 'Nome webhook', + 'webhooks_timeout' => 'Timeout richiesta webhook (secondi)', + 'webhooks_endpoint' => 'Endpoint webhook', + 'webhooks_active' => 'Webhook attivo', 'webhook_events_table_header' => 'Eventi', - 'webhooks_delete' => 'Elimina Webhook', - 'webhooks_delete_warning' => 'Questo eliminerà completamente questo webhook, con il nome \':webhookName\', dal sistema.', + 'webhooks_delete' => 'Elimina webhook', + 'webhooks_delete_warning' => 'Questo eliminerà completamente questo webhook chiamato \':webhookName\' dal sistema.', 'webhooks_delete_confirm' => 'Sei sicuro di voler eliminare questo webhook?', - 'webhooks_format_example' => 'Esempio Di Formato Webhook', - 'webhooks_format_example_desc' => 'I dati Webhook vengono inviati come richiesta POST all\'endpoint configurato come JSON seguendo il formato sottostante. Le proprietà "related_item" e "url" sono opzionali e dipenderanno dal tipo di evento attivato.', - 'webhooks_status' => 'Stato Webhook', - 'webhooks_last_called' => 'Ultima Chiamata:', - 'webhooks_last_errored' => 'Ultimo Errore:', - 'webhooks_last_error_message' => 'Ultimo Messaggio Di Errore:', + 'webhooks_format_example' => 'Esempio di formato webhook', + 'webhooks_format_example_desc' => 'I dati del webhook vengono inviati come richiesta POST all\'endpoint configurato come JSON seguendo il formato sottostante. Le proprietà "related_item" e "url" sono opzionali e dipenderanno dal tipo di evento attivato.', + 'webhooks_status' => 'Stato webhook', + 'webhooks_last_called' => 'Ultima chiamata:', + 'webhooks_last_errored' => 'Ultimo errore:', + 'webhooks_last_error_message' => 'Ultimo messaggio di errore:', + // Licensing + 'licenses' => 'Licenze', + 'licenses_desc' => 'Questa pagina contiene informazioni dettagliate sulle licenze di BookStack, oltre ai progetti e alle librerie utilizzate all\'interno di BookStack. Molti dei progetti elencati possono essere utilizzati solo in un contesto di sviluppo.', + 'licenses_bookstack' => 'Licenza BookStack', + 'licenses_php' => 'Licenze Librerie PHP', + 'licenses_js' => 'Licenze Librerie JavaScript', + 'licenses_other' => 'Altre Licenze', + 'license_details' => 'Dettagli Licenza', //! If editing translations files directly please ignore this in all //! languages apart from en. Content will be auto-copied from en. @@ -287,6 +326,7 @@ 'bs' => 'Bosniaco', 'ca' => 'Catalano', 'cs' => 'Ceco', + 'cy' => 'Cymraeg', 'da' => 'Danese', 'de' => 'Deutsch (Sie)', 'de_informal' => 'Deutsch (Du)', @@ -308,6 +348,7 @@ 'lt' => 'Lituano', 'lv' => 'Lettone', 'nb' => 'Norvegese (Bokmål)', + 'ne' => 'नेपाली', 'nn' => 'Nynorsk', 'nl' => 'Olandese', 'pl' => 'Polacco', diff --git a/lang/it/validation.php b/lang/it/validation.php index a782986bf2c..c945ff7d426 100644 --- a/lang/it/validation.php +++ b/lang/it/validation.php @@ -12,18 +12,18 @@ 'active_url' => ':attribute non è uno URL valido.', 'after' => ':attribute deve essere una data dopo il :date.', 'alpha' => ':attribute deve contenere solo lettere.', - 'alpha_dash' => ':attribute deve contenere solo lettere, numeri e meno.', + 'alpha_dash' => ':attribute deve contenere solo lettere, numeri, trattini e trattini bassi.', 'alpha_num' => ':attribute deve contenere solo lettere e numeri.', 'array' => ':attribute deve essere un array.', 'backup_codes' => 'Il codice fornito non è valido o è già stato utilizzato.', 'before' => ':attribute deve essere una data prima del :date.', 'between' => [ 'numeric' => 'Il campo :attribute deve essere tra :min e :max.', - 'file' => 'Il campo :attribute deve essere tra :min e :max kilobytes.', + 'file' => 'Il campo :attribute deve essere tra :min e :max kilobyte.', 'string' => 'Il campo :attribute deve essere tra :min e :max caratteri.', - 'array' => 'Il campo :attribute deve essere tra :min e :max oggetti.', + 'array' => 'Il campo :attribute deve avere tra :min e :max oggetti.', ], - 'boolean' => ':attribute deve contenere vero o falso.', + 'boolean' => ':attribute deve essere vero o falso.', 'confirmed' => 'La conferma di :attribute non corrisponde.', 'date' => ':attribute non è una data valida.', 'date_format' => 'Il campo :attribute non corrisponde al formato :format.', @@ -36,17 +36,17 @@ 'filled' => 'Il campo :attribute field is required.', 'gt' => [ 'numeric' => ':attribute deve essere maggiore di :value.', - 'file' => ':attribute deve essere maggiore di :value kilobytes.', + 'file' => ':attribute deve essere maggiore di :value kilobyte.', 'string' => ':attribute deve essere maggiore di :value caratteri.', 'array' => ':attribute deve avere più di :value elementi.', ], 'gte' => [ 'numeric' => ':attribute deve essere maggiore o uguale a :value.', - 'file' => ':attribute deve essere maggiore o uguale a :value kilobytes.', + 'file' => ':attribute deve essere maggiore o uguale a :value kilobyte.', 'string' => ':attribute deve essere maggiore o uguale a :value caratteri.', - 'array' => ':attribute deve avere :value elementi o più.', + 'array' => ':attribute deve avere :value o più elementi.', ], - 'exists' => 'Il campo :attribute non è valido.', + 'exists' => 'Il campo :attribute selezionato non è valido.', 'image' => 'Il campo :attribute deve essere un\'immagine.', 'image_extension' => ':attribute deve avere un\'estensione immagine valida e supportata.', 'in' => 'Il campo :attribute selezionato non è valido.', @@ -57,26 +57,26 @@ 'json' => ':attribute deve essere una stringa JSON valida.', 'lt' => [ 'numeric' => ':attribute deve essere inferiore a :value.', - 'file' => ':attribute deve essere inferiore a :value kilobytes.', + 'file' => ':attribute deve essere inferiore a :value kilobyte.', 'string' => ':attribute deve essere inferiore a :value caratteri.', 'array' => ':attribute deve avere meno di :value elementi.', ], 'lte' => [ 'numeric' => ':attribute deve essere minore o uguale :value.', - 'file' => ':attribute deve essere minore o uguale a :value kilobytes.', + 'file' => ':attribute deve essere minore o uguale a :value kilobyte.', 'string' => ':attribute deve essere minore o uguale a :value caratteri.', 'array' => ':attribute non deve avere più di :value elementi.', ], 'max' => [ 'numeric' => 'Il campo :attribute non deve essere maggiore di :max.', - 'file' => 'Il campo :attribute non deve essere maggiore di :max kilobytes.', + 'file' => 'Il campo :attribute non deve essere maggiore di :max kilobyte.', 'string' => 'Il campo :attribute non deve essere maggiore di :max caratteri.', 'array' => 'Il campo :attribute non deve avere più di :max oggetti.', ], 'mimes' => 'Il campo :attribute deve essere: :values.', 'min' => [ 'numeric' => 'Il campo :attribute deve essere almeno :min.', - 'file' => 'Il campo :attribute deve essere almeno :min kilobytes.', + 'file' => 'Il campo :attribute deve essere almeno :min kilobyte.', 'string' => 'Il campo :attribute deve essere almeno :min caratteri.', 'array' => 'Il campo :attribute deve contenere almeno :min elementi.', ], @@ -94,17 +94,22 @@ 'safe_url' => 'Il link inserito potrebbe non essere sicuro.', 'size' => [ 'numeric' => 'Il campo :attribute deve essere :size.', - 'file' => 'Il campo :attribute deve essere :size kilobytes.', + 'file' => 'Il campo :attribute deve essere :size kilobyte.', 'string' => 'Il campo :attribute deve essere di :size caratteri.', 'array' => 'Il campo :attribute deve contenere :size elementi.', ], 'string' => ':attribute deve essere una stringa.', 'timezone' => ':attribute deve essere una zona valida.', 'totp' => 'Il codice fornito non è valido o è scaduto.', - 'unique' => ':attribute è già preso.', + 'unique' => ':attribute è già usato.', 'url' => 'Il formato :attribute non è valido.', 'uploaded' => 'Il file non può essere caricato. Il server potrebbe non accettare file di questa dimensione.', + 'zip_file' => 'L\'attributo :attribute deve fare riferimento a un file all\'interno dello ZIP.', + 'zip_file_mime' => 'Il campo :attribute deve fare riferimento a un file di tipo :validTypes, trovato :foundType.', + 'zip_model_expected' => 'Oggetto dati atteso ma ":type" trovato.', + 'zip_unique' => 'L\'attributo :attribute deve essere univoco per il tipo di oggetto all\'interno dello ZIP.', + // Custom validation lines 'custom' => [ 'password-confirm' => [ diff --git a/lang/ja/activities.php b/lang/ja/activities.php index 0e6e8d3ce83..6f55098bbd9 100644 --- a/lang/ja/activities.php +++ b/lang/ja/activities.php @@ -84,6 +84,14 @@ 'webhook_delete' => 'がWebhookを削除', 'webhook_delete_notification' => 'Webhookを削除しました', + // Imports + 'import_create' => 'がインポートを作成', + 'import_create_notification' => 'インポートファイルが正常にアップロードされました', + 'import_run' => 'がインポートを更新', + 'import_run_notification' => 'コンテンツが正常にインポートされました', + 'import_delete' => 'がインポートを削除', + 'import_delete_notification' => 'インポートファイルが正常に削除されました', + // Users 'user_create' => 'がユーザを作成', 'user_create_notification' => 'ユーザーを作成しました', @@ -119,6 +127,14 @@ 'comment_update' => 'がコメントを更新', 'comment_delete' => 'がコメントを削除', + // Sort Rules + 'sort_rule_create' => 'がソートルールを作成', + 'sort_rule_create_notification' => 'ソートルールを作成しました', + 'sort_rule_update' => 'がソートルールを更新', + 'sort_rule_update_notification' => 'ソートルールを更新しました', + 'sort_rule_delete' => 'がソートルールを削除', + 'sort_rule_delete_notification' => 'ソートルールを削除しました', + // Other 'permissions_update' => 'が権限を更新:', ]; diff --git a/lang/ja/auth.php b/lang/ja/auth.php index c0c501b729c..077c31ac85c 100644 --- a/lang/ja/auth.php +++ b/lang/ja/auth.php @@ -87,13 +87,13 @@ 'mfa_setup_reconfigure' => '再設定', 'mfa_setup_remove_confirmation' => 'この多要素認証方法を削除してもよろしいですか?', 'mfa_setup_action' => '設定', - 'mfa_backup_codes_usage_limit_warning' => 'バックアップコードは残り5つ以下です。アカウントのロックアウトを防ぐため、コードがなくなる前に新しいセットを生成して保存してください。', + 'mfa_backup_codes_usage_limit_warning' => '有効な確認コードは残り5つ以下です。アカウントのロックアウトを防ぐため、コードがなくなる前に新しいセットを生成して保存してください。', 'mfa_option_totp_title' => 'モバイルアプリ', 'mfa_option_totp_desc' => '多要素認証を使用するには、Google Authenticator、Authy、Microsoft AuthenticatorなどのTOTPをサポートするモバイルアプリケーションが必要です。', - 'mfa_option_backup_codes_title' => 'バックアップコード', - 'mfa_option_backup_codes_desc' => '本人確認のために入力する、一度しか使えないバックアップコードを安全に保存します。', + 'mfa_option_backup_codes_title' => '確認コード', + 'mfa_option_backup_codes_desc' => 'ログイン時に本人確認のために追加入力する1回限りの確認コードセットを生成します。 これは安全な場所に保管してください。', 'mfa_gen_confirm_and_enable' => '確認して有効化', - 'mfa_gen_backup_codes_title' => 'バックアップコードの設定', + 'mfa_gen_backup_codes_title' => '確認コードの構成', 'mfa_gen_backup_codes_desc' => '以下のコードのリストを安全な場所に保管してください。システムにアクセスする際、コードのいずれかを第二の認証手段として使用できます。', 'mfa_gen_backup_codes_download' => 'コードをダウンロード', 'mfa_gen_backup_codes_usage_warning' => '各コードは一度だけ使用できます', @@ -108,10 +108,10 @@ 'mfa_verify_no_methods' => '手段が設定されていません', 'mfa_verify_no_methods_desc' => 'アカウントの多要素認証手段が見つかりませんでした。アクセスする前に、少なくとも1つの手段を設定する必要があります。', 'mfa_verify_use_totp' => 'モバイルアプリを利用して確認', - 'mfa_verify_use_backup_codes' => 'バックアップコードを利用して確認', - 'mfa_verify_backup_code' => 'バックアップコード', - 'mfa_verify_backup_code_desc' => '残りのバックアップコードのいずれかを入力してください:', - 'mfa_verify_backup_code_enter_here' => 'バックアップコードを入力', + 'mfa_verify_use_backup_codes' => '確認コードによる検証', + 'mfa_verify_backup_code' => '確認コード', + 'mfa_verify_backup_code_desc' => '残りの確認コードのいずれかを入力してください:', + 'mfa_verify_backup_code_enter_here' => '確認コードを入力', 'mfa_verify_totp_desc' => 'モバイルアプリを利用して生成されたコードを入力してください:', 'mfa_setup_login_notification' => '多要素認証が構成されました。設定された手段を利用して再度ログインしてください。', ]; diff --git a/lang/ja/common.php b/lang/ja/common.php index 5810d306d55..e888c43f651 100644 --- a/lang/ja/common.php +++ b/lang/ja/common.php @@ -20,7 +20,7 @@ 'description' => '概要', 'role' => '権限', 'cover_image' => 'カバー画像', - 'cover_image_description' => 'この画像はおよそ440x250pxの大きさが必要です。', + 'cover_image_description' => 'この画像はおよそ440x250pxであるべきですが、必要に応じてさまざまなシナリオでユーザー・インターフェースに合うように柔軟に拡大・縮小されるため、実際の表示寸法は異なります。', // Actions 'actions' => '実行', @@ -30,6 +30,8 @@ 'create' => '作成', 'update' => '更新', 'edit' => '編集', + 'archive' => 'アーカイブ', + 'unarchive' => 'アーカイブ解除', 'sort' => '並び順', 'move' => '移動', 'copy' => 'コピー', @@ -107,4 +109,7 @@ // Not directly used but available for convenience to users. 'privacy_policy' => 'プライバシーポリシー', 'terms_of_service' => '利用規約', + + // OpenSearch + 'opensearch_description' => ':appName を検索', ]; diff --git a/lang/ja/editor.php b/lang/ja/editor.php index b47d638b4a9..40307dd5281 100644 --- a/lang/ja/editor.php +++ b/lang/ja/editor.php @@ -13,6 +13,7 @@ 'cancel' => '取消', 'save' => '保存', 'close' => '閉じる', + 'apply' => '適用', 'undo' => '元に戻す', 'redo' => 'やり直し', 'left' => '左寄せ', @@ -46,7 +47,8 @@ 'strikethrough' => '取消線', 'superscript' => '上付き', 'subscript' => '下付き', - 'text_color' => 'テキストの色', + 'text_color' => 'テキスト色', + 'highlight_color' => 'テキスト背景色', 'custom_color' => 'カスタムカラー', 'remove_color' => '色設定を解除', 'background_color' => '背景色', @@ -81,6 +83,9 @@ 'table_properties' => '表の詳細設定', 'table_properties_title' => '表の詳細設定', 'delete_table' => '表の削除', + 'table_clear_formatting' => '表の書式をクリア', + 'resize_to_contents' => '内容に合わせてリサイズ', + 'row_header' => 'ヘッダー行', 'insert_row_before' => '上側に行を挿入', 'insert_row_after' => '下側に行を挿入', 'delete_row' => '行の削除', @@ -144,6 +149,7 @@ 'url' => 'リンク先URL', 'text_to_display' => 'リンク元テキスト', 'title' => 'タイトル', + 'browse_links' => 'エンティティを参照', 'open_link' => 'リンクを開く', 'open_link_in' => 'リンク先の表示場所', 'open_link_current' => '同じウィンドウ', @@ -160,6 +166,8 @@ 'about' => 'エディタについて', 'about_title' => 'WYSIWYGエディタについて', 'editor_license' => 'エディタのライセンスと著作権', + 'editor_lexical_license' => 'このエディタはMITライセンスの下で配布されている :lexicalLink のフォークとして構築されています。', + 'editor_lexical_license_link' => '完全なライセンスの詳細はこちらをご覧ください。', 'editor_tiny_license' => 'このエディタはMITライセンスの下で提供される:tinyLinkを利用して構築されています。', 'editor_tiny_license_link' => 'TinyMCEの著作権およびライセンスの詳細は、こちらをご覧ください。', 'save_continue' => 'ページを保存して続行', diff --git a/lang/ja/entities.php b/lang/ja/entities.php index 0b39c2c27e8..551a2e49672 100644 --- a/lang/ja/entities.php +++ b/lang/ja/entities.php @@ -39,6 +39,34 @@ 'export_pdf' => 'PDF', 'export_text' => 'テキストファイル', 'export_md' => 'Markdown', + 'export_zip' => 'ポータブルZIP', + 'default_template' => 'デフォルトページテンプレート', + 'default_template_explain' => 'このアイテム内に新しいページを作成する際にデフォルトコンテンツとして使用されるページテンプレートを割り当てます。これはページ作成者が選択したテンプレートページへのアクセス権を持つ場合にのみ使用されることに注意してください。', + 'default_template_select' => 'テンプレートページを選択', + 'import' => 'インポート', + 'import_validate' => 'インポートの検証', + 'import_desc' => 'ポータブルzipエクスポートファイルを使用して、同一または別のインスタンスからブック、チャプタ、ページをインポートします。続行するにはZIPファイルを選択します。 ファイルをアップロードすると検証が行われ、次のビューでインポートの設定と確認ができます。', + 'import_zip_select' => 'アップロードするZIPファイルの選択', + 'import_zip_validation_errors' => '指定された ZIP ファイルの検証中にエラーが検出されました:', + 'import_pending' => '保留中のインポート', + 'import_pending_none' => 'インポートは行われていません。', + 'import_continue' => 'インポートを続行', + 'import_continue_desc' => 'アップロードされたZIPファイルからインポートされるコンテンツを確認してください。準備ができたらインポートを実行してこのシステムにコンテンツを追加します。 アップロードされたZIPファイルは、インポートが成功すると自動的に削除されます。', + 'import_details' => 'インポートの詳細', + 'import_run' => 'インポートを実行', + 'import_size' => 'インポートZIPサイズ: :size', + 'import_uploaded_at' => 'アップロード: :relativeTime', + 'import_uploaded_by' => 'アップロードユーザ:', + 'import_location' => 'インポートの場所', + 'import_location_desc' => 'コンテンツをインポートする場所を選択します。選択した場所に作成するための権限を持つ必要があります。', + 'import_delete_confirm' => 'このインポートを削除してもよろしいですか?', + 'import_delete_desc' => 'アップロードされたインポートZIPファイルは削除され、元に戻すことはできません。', + 'import_errors' => 'インポートエラー', + 'import_errors_desc' => 'インポート中に次のエラーが発生しました:', + 'breadcrumb_siblings_for_page' => '階層内のページ', + 'breadcrumb_siblings_for_chapter' => '階層内のチャプタ', + 'breadcrumb_siblings_for_book' => '階層内のブック', + 'breadcrumb_siblings_for_bookshelf' => '階層内の棚', // Permissions and restrictions 'permissions' => '権限', @@ -132,9 +160,6 @@ 'books_edit_named' => 'ブック「:bookName」を編集', 'books_form_book_name' => 'ブック名', 'books_save' => 'ブックを保存', - 'books_default_template' => 'デフォルトページテンプレート', - 'books_default_template_explain' => 'このブックに新しいページを作成する際にデフォルトコンテンツとして使用されるページテンプレートを割り当てます。 これはページ作成者が選択したテンプレートページへのアクセス権を持つ場合にのみ使用されることに注意してください。', - 'books_default_template_select' => 'テンプレートページを選択', 'books_permissions' => 'ブックの権限', 'books_permissions_updated' => 'ブックの権限を更新しました', 'books_empty_contents' => 'まだページまたはチャプターが作成されていません。', @@ -145,7 +170,9 @@ 'books_search_this' => 'このブックから検索', 'books_navigation' => '目次', 'books_sort' => '並び順を変更', - 'books_sort_desc' => 'ブック内のチャプターとページを移動して、その内容を再編成します。 他のブックを追加することで、チャプターやページをブック間で簡単に移動することができます。', + 'books_sort_desc' => 'ブック内のチャプタおよびページを移動して内容を再編成できます。他のブックを並べて、ブック間でチャプタやページを簡単に移動することもできます。オプションで自動ソートルールを設定すると、変更時にブックの内容を自動的にソートすることができます。', + 'books_sort_auto_sort' => '自動ソートオプション', + 'books_sort_auto_sort_active' => '自動ソート有効: :sortName', 'books_sort_named' => 'ブック「:bookName」を並べ替え', 'books_sort_name' => '名前で並べ替え', 'books_sort_created' => '作成日で並べ替え', @@ -207,7 +234,7 @@ 'pages_delete_draft' => 'ページの下書きを削除', 'pages_delete_success' => 'ページを削除しました', 'pages_delete_draft_success' => 'ページの下書きを削除しました', - 'pages_delete_warning_template' => 'このページは現在ブックのデフォルトページテンプレートとして使用されています。 このページが削除されると、それらのブックでデフォルトのページテンプレートが割り当てられなくなります。', + 'pages_delete_warning_template' => 'このページは現在、ブックまたはチャプタのデフォルトページテンプレートとして使用されています。このページが削除されると、それらのアイテムでデフォルトのページテンプレートが割り当てられなくなります。', 'pages_delete_confirm' => 'このページを削除してもよろしいですか?', 'pages_delete_draft_confirm' => 'このページの下書きを削除してもよろしいですか?', 'pages_editing_named' => 'ページ :pageName を編集', @@ -224,6 +251,8 @@ 'pages_edit_switch_to_markdown_clean' => '(クリーンなコンテンツ)', 'pages_edit_switch_to_markdown_stable' => '(安定したコンテンツ)', 'pages_edit_switch_to_wysiwyg' => 'WYSIWYGエディタに切り替え', + 'pages_edit_switch_to_new_wysiwyg' => '新しいWYSIWYGエディタに切り替える', + 'pages_edit_switch_to_new_wysiwyg_desc' => '(ベータテスト版)', 'pages_edit_set_changelog' => '編集内容についての説明', 'pages_edit_enter_changelog_desc' => 'どのような変更を行ったのかを記録してください', 'pages_edit_enter_changelog' => '編集内容を入力', @@ -236,13 +265,14 @@ 'pages_save' => 'ページを保存', 'pages_title' => 'ページタイトル', 'pages_name' => 'ページ名', - 'pages_md_editor' => 'エディター', + 'pages_md_editor' => 'エディタ', 'pages_md_preview' => 'プレビュー', 'pages_md_insert_image' => '画像を挿入', 'pages_md_insert_link' => 'エンティティへのリンクを挿入', 'pages_md_insert_drawing' => '図を追加', 'pages_md_show_preview' => 'プレビューを表示', 'pages_md_sync_scroll' => 'プレビューとスクロールを同期', + 'pages_md_plain_editor' => 'プレーンテキスト エディタ', 'pages_drawing_unsaved' => '未保存の図が見つかりました', 'pages_drawing_unsaved_confirm' => '以前に保存操作が失敗した、未保存の図が見つかりました。 未保存の図面を復元して編集を続けますか?', @@ -368,8 +398,11 @@ 'comment' => 'コメント', 'comments' => 'コメント', 'comment_add' => 'コメント追加', + 'comment_none' => '表示するコメントがありません', 'comment_placeholder' => 'コメントを記入してください', - 'comment_count' => '{0} コメントはありません|[1,*] :count 件のコメント', + 'comment_thread_count' => ':count 個のコメントスレッド|:count 個のコメントスレッド', + 'comment_archived_count' => ':count 個のアーカイブ', + 'comment_archived_threads' => 'アーカイブされたスレッド', 'comment_save' => 'コメントを保存', 'comment_new' => '新規コメント作成', 'comment_created' => 'コメントを作成しました :createDiff', @@ -378,8 +411,14 @@ 'comment_deleted_success' => 'コメントを削除しました', 'comment_created_success' => 'コメントを追加しました', 'comment_updated_success' => 'コメントを更新しました', + 'comment_archive_success' => 'コメントをアーカイブしました', + 'comment_unarchive_success' => 'コメントのアーカイブを解除しました', + 'comment_view' => 'コメントを表示', + 'comment_jump_to_thread' => 'スレッドにジャンプ', 'comment_delete_confirm' => '本当にこのコメントを削除しますか?', 'comment_in_reply_to' => ':commentIdへ返信', + 'comment_reference' => '参照箇所', + 'comment_reference_outdated' => '(以前の記述)', 'comment_editor_explain' => 'ここにはページに付けられたコメントを表示します。 コメントの追加と管理は保存されたページの表示時に行うことができます。', // Revision diff --git a/lang/ja/errors.php b/lang/ja/errors.php index 26de050e4d3..8161c08b860 100644 --- a/lang/ja/errors.php +++ b/lang/ja/errors.php @@ -10,6 +10,7 @@ // Auth 'error_user_exists_different_creds' => ':emailを持つユーザは既に存在しますが、資格情報が異なります。', + 'auth_pre_register_theme_prevention' => '指定された内容によるユーザーアカウントの登録はできません。', 'email_already_confirmed' => 'Eメールは既に確認済みです。ログインしてください。', 'email_confirmation_invalid' => 'この確認トークンは無効か、または既に使用済みです。登録を再試行してください。', 'email_confirmation_expired' => '確認トークンは有効期限切れです。確認メールを再送しました。', @@ -23,7 +24,6 @@ 'saml_invalid_response_id' => '外部認証システムからの要求がアプリケーションによって開始されたプロセスによって認識されません。ログイン後に戻るとこの問題が発生する可能性があります。', 'saml_fail_authed' => ':systemを利用したログインに失敗しました。システムは正常な認証を提供しませんでした。', 'oidc_already_logged_in' => '既にログインしています', - 'oidc_user_not_registered' => 'ユーザー :name は登録されておらず、自動登録は無効になっています', 'oidc_no_email_address' => '外部認証システムから提供されたデータに、このユーザーのメールアドレスが見つかりませんでした', 'oidc_fail_authed' => ':systemを利用したログインに失敗しました。システムは正常な認証を提供しませんでした。', 'social_no_action_defined' => 'アクションが定義されていません', @@ -37,6 +37,7 @@ 'social_driver_not_found' => 'Social driverが見つかりません', 'social_driver_not_configured' => 'あなたの:socialAccount設定は正しく構成されていません。', 'invite_token_expired' => 'この招待リンクの有効期限が切れています。 代わりにアカウントのパスワードをリセットしてみてください。', + 'login_user_not_found' => 'このアクションのユーザーが見つかりません。', // System 'path_not_writable' => 'ファイルパス :filePath へアップロードできませんでした。サーバ上での書き込みが許可されているか確認してください。', @@ -77,6 +78,7 @@ // Users 'users_cannot_delete_only_admin' => '唯一の管理者を削除することはできません', 'users_cannot_delete_guest' => 'ゲストユーザを削除することはできません', + 'users_could_not_send_invite' => '招待メールの送信に失敗したため、ユーザーを作成できませんでした。', // Roles 'role_cannot_be_edited' => 'この役割は編集できません', @@ -103,6 +105,18 @@ 'app_down' => ':appNameは現在停止しています', 'back_soon' => '回復までしばらくお待ちください。', + // Import + 'import_zip_cant_read' => 'ZIPファイルを読み込めません。', + 'import_zip_cant_decode_data' => 'ZIPファイル内に data.json が見つからないかデコードできませんでした。', + 'import_zip_no_data' => 'ZIPファイルのデータにブック、チャプター、またはページコンテンツがありません。', + 'import_validation_failed' => 'エラーによりインポートZIPの検証に失敗しました:', + 'import_zip_failed_notification' => 'ZIP ファイルのインポートに失敗しました。', + 'import_perms_books' => 'ブックを作成するために必要な権限がありません。', + 'import_perms_chapters' => 'チャプタを作成するために必要な権限がありません。', + 'import_perms_pages' => 'ページを作成するために必要な権限がありません。', + 'import_perms_images' => '画像を作成するために必要な権限がありません。', + 'import_perms_attachments' => '添付ファイルを作成するために必要な権限がありません。', + // API errors 'api_no_authorization_found' => 'リクエストに認証トークンが見つかりません', 'api_bad_authorization_format' => 'リクエストに認証トークンが見つかりましたが、形式が正しくないようです', diff --git a/lang/ja/preferences.php b/lang/ja/preferences.php index 0e56852602c..0207596a9ec 100644 --- a/lang/ja/preferences.php +++ b/lang/ja/preferences.php @@ -27,7 +27,7 @@ 'notifications_save' => '設定を保存', 'notifications_update_success' => '通知設定を更新しました。', 'notifications_watched' => 'ウォッチ/通知無効 項目', - 'notifications_watched_desc' => ' 以下はカスタムウォッチの設定が適用されている項目です。 これらの設定を更新するには、項目を表示してサイドバーのウォッチオプションを参照してください。', + 'notifications_watched_desc' => '以下はカスタムウォッチの設定が適用されている項目です。 これらの設定を更新するには、項目を表示してサイドバーのウォッチオプションを参照してください。', 'auth' => 'アクセス & セキュリティ', 'auth_change_password' => 'パスワードの変更', diff --git a/lang/ja/settings.php b/lang/ja/settings.php index 1ef109d7909..db14f3ccc5a 100644 --- a/lang/ja/settings.php +++ b/lang/ja/settings.php @@ -74,6 +74,36 @@ 'reg_confirm_restrict_domain_desc' => '特定のドメインのみ登録できるようにする場合、以下にカンマ区切りで入力します。設定された場合、Eメール認証が必須になります。
登録後、ユーザは自由にEメールアドレスを変更できます。', 'reg_confirm_restrict_domain_placeholder' => '制限しない', + // Sorting Settings + 'sorting' => 'ソート', + 'sorting_book_default' => 'ブックのデフォルトソート', + 'sorting_book_default_desc' => '新しいブックに適用するデフォルトのソートルールを選択します。これは既存のブックには影響しません。ルールはブックごとに上書きすることができます。', + 'sorting_rules' => 'ソートルール', + 'sorting_rules_desc' => 'これらはシステム内のコンテンツに適用できる事前定義のソート操作です。', + 'sort_rule_assigned_to_x_books' => ':count 個のブックに割当|:count 個のブックに割当', + 'sort_rule_create' => 'ソートルールを作成', + 'sort_rule_edit' => 'ソートルールの編集', + 'sort_rule_delete' => 'ソートルールを削除', + 'sort_rule_delete_desc' => 'このソートルールをシステムから削除します。これを使用しているブックは手動ソートに戻ります。', + 'sort_rule_delete_warn_books' => 'このソートルールは現在 :count 個のブックで使用されています。削除してもよろしいですか?', + 'sort_rule_delete_warn_default' => 'このソートルールは現在ブックのデフォルトソートに使用されています。削除してもよろしいですか?', + 'sort_rule_details' => 'ソートルールの詳細', + 'sort_rule_details_desc' => 'ユーザがソートを選択する際にリストに表示される、ソートルールの名前を設定します。', + 'sort_rule_operations' => 'ソート操作', + 'sort_rule_operations_desc' => '利用可能な操作のリストから、実行するソート操作を追加してソートルールを設定します。操作は上から下へ順番に適用されます。ここで変更を行うと、保存時に割り当てられたすべてのブックに適用されます。', + 'sort_rule_available_operations' => '利用可能な操作', + 'sort_rule_available_operations_empty' => '残った操作はありません', + 'sort_rule_configured_operations' => '設定された操作', + 'sort_rule_configured_operations_empty' => '「利用可能な操作」リストから操作をドラッグ/追加してください', + 'sort_rule_op_asc' => '(昇順)', + 'sort_rule_op_desc' => '(降順)', + 'sort_rule_op_name' => '名前 - キャラクタ順', + 'sort_rule_op_name_numeric' => '名前 - 数値順', + 'sort_rule_op_created_date' => '作成日時', + 'sort_rule_op_updated_date' => '更新日時', + 'sort_rule_op_chapters_first' => 'チャプタを最初に', + 'sort_rule_op_chapters_last' => 'チャプタを最後に', + // Maintenance settings 'maint' => 'メンテナンス', 'maint_image_cleanup' => '画像のクリーンアップ', @@ -109,7 +139,7 @@ 'recycle_bin_contents_empty' => 'ごみ箱は現在空です', 'recycle_bin_empty' => 'ごみ箱を空にする', 'recycle_bin_empty_confirm' => 'ごみ箱のすべてのアイテムが、各アイテムに含まれるコンテンツも含めて完全に削除されます。本当にごみ箱を空にしますか?', - 'recycle_bin_destroy_confirm' => 'この操作により、このアイテムと以下にリストされている子要素がシステムから完全に削除され、このコンテンツを復元できなくなります。このアイテムを完全に削除してもよろしいですか?', + 'recycle_bin_destroy_confirm' => 'この操作を行うと、子要素を含めた以下のリストに示すアイテムがシステムから完全に削除され、このコンテンツを復元できなくなります。このアイテムを完全に削除してもよろしいですか?', 'recycle_bin_destroy_list' => '削除されるアイテム', 'recycle_bin_restore_list' => '復元されるアイテム', 'recycle_bin_restore_confirm' => 'この操作により、すべての子要素を含む削除されたアイテムが元の場所に復元されます。元の場所が削除されてごみ箱に入っている場合は、親アイテムも復元する必要があります。', @@ -162,6 +192,7 @@ 'role_access_api' => 'システムのAPIへのアクセス', 'role_manage_settings' => 'アプリケーション設定の管理', 'role_export_content' => 'コンテンツのエクスポート', + 'role_import_content' => 'コンテンツのインポート', 'role_editor_change' => 'ページエディタの変更', 'role_notifications' => '通知の受信と管理', 'role_asset' => 'アセット権限', @@ -276,6 +307,14 @@ 'webhooks_last_errored' => '最後のエラー:', 'webhooks_last_error_message' => '最後のエラーのメッセージ:', + // Licensing + 'licenses' => 'ライセンス', + 'licenses_desc' => 'このページではBookStackとBookStackで使用されるプロジェクトやライブラリのライセンス情報を詳しく説明します。開発環境でのみ使用するものも多数含まれています。', + 'licenses_bookstack' => 'BookStack ライセンス', + 'licenses_php' => 'PHPライブラリライセンス', + 'licenses_js' => 'JavaScriptライブラリライセンス', + 'licenses_other' => 'その他のライセンス', + 'license_details' => 'ライセンス詳細', //! If editing translations files directly please ignore this in all //! languages apart from en. Content will be auto-copied from en. @@ -287,6 +326,7 @@ 'bs' => 'Bosanski', 'ca' => 'Català', 'cs' => 'Česky', + 'cy' => 'Cymraeg', 'da' => 'Dansk', 'de' => 'Deutsch (Sie)', 'de_informal' => 'Deutsch (Du)', @@ -308,6 +348,7 @@ 'lt' => 'Lietuvių Kalba', 'lv' => 'Latviešu Valoda', 'nb' => 'Norsk (Bokmål)', + 'ne' => 'नेपाली', 'nn' => 'Nynorsk', 'nl' => 'Nederlands', 'pl' => 'Polski', diff --git a/lang/ja/validation.php b/lang/ja/validation.php index 96ae7dfff24..7d18c85be72 100644 --- a/lang/ja/validation.php +++ b/lang/ja/validation.php @@ -105,6 +105,11 @@ 'url' => ':attributeのフォーマットは不正です。', 'uploaded' => 'ファイルをアップロードできませんでした。サーバーがこのサイズのファイルを受け付けていない可能性があります。', + 'zip_file' => ':attribute はZIP 内のファイルを参照する必要があります。', + 'zip_file_mime' => ':attribute は種別 :validType のファイルを参照する必要がありますが、種別 :foundType となっています。', + 'zip_model_expected' => 'データオブジェクトが期待されますが、":type" が見つかりました。', + 'zip_unique' => 'ZIP内のオブジェクトタイプに :attribute が一意である必要があります。', + // Custom validation lines 'custom' => [ 'password-confirm' => [ diff --git a/lang/ka/activities.php b/lang/ka/activities.php index d5b55c03dcb..35730fd77a0 100644 --- a/lang/ka/activities.php +++ b/lang/ka/activities.php @@ -69,7 +69,7 @@ 'mfa_setup_method' => 'configured MFA method', 'mfa_setup_method_notification' => 'Multi-factor method successfully configured', 'mfa_remove_method' => 'removed MFA method', - 'mfa_remove_method_notification' => 'Multi-factor method successfully removed', + 'mfa_remove_method_notification' => 'მულტი-ფაქტორული მეთოდი წარმატებით მოიხსნა', // Settings 'settings_update' => 'updated settings', @@ -84,6 +84,14 @@ 'webhook_delete' => 'deleted webhook', 'webhook_delete_notification' => 'Webhook successfully deleted', + // Imports + 'import_create' => 'created import', + 'import_create_notification' => 'Import successfully uploaded', + 'import_run' => 'updated import', + 'import_run_notification' => 'Content successfully imported', + 'import_delete' => 'deleted import', + 'import_delete_notification' => 'Import successfully deleted', + // Users 'user_create' => 'created user', 'user_create_notification' => 'User successfully created', @@ -93,11 +101,11 @@ 'user_delete_notification' => 'User successfully removed', // API Tokens - 'api_token_create' => 'created api token', + 'api_token_create' => 'created API token', 'api_token_create_notification' => 'API token successfully created', - 'api_token_update' => 'updated api token', + 'api_token_update' => 'updated API token', 'api_token_update_notification' => 'API token successfully updated', - 'api_token_delete' => 'deleted api token', + 'api_token_delete' => 'deleted API token', 'api_token_delete_notification' => 'API token successfully deleted', // Roles @@ -119,6 +127,14 @@ 'comment_update' => 'updated comment', 'comment_delete' => 'deleted comment', + // Sort Rules + 'sort_rule_create' => 'created sort rule', + 'sort_rule_create_notification' => 'Sort rule successfully created', + 'sort_rule_update' => 'updated sort rule', + 'sort_rule_update_notification' => 'Sort rule successfully updated', + 'sort_rule_delete' => 'deleted sort rule', + 'sort_rule_delete_notification' => 'Sort rule successfully deleted', + // Other 'permissions_update' => 'updated permissions', ]; diff --git a/lang/ka/auth.php b/lang/ka/auth.php index dc4b242a09e..57f0cb5c632 100644 --- a/lang/ka/auth.php +++ b/lang/ka/auth.php @@ -91,7 +91,7 @@ 'mfa_option_totp_title' => 'Mobile App', 'mfa_option_totp_desc' => 'To use multi-factor authentication you\'ll need a mobile application that supports TOTP such as Google Authenticator, Authy or Microsoft Authenticator.', 'mfa_option_backup_codes_title' => 'Backup Codes', - 'mfa_option_backup_codes_desc' => 'Securely store a set of one-time-use backup codes which you can enter to verify your identity.', + 'mfa_option_backup_codes_desc' => 'Generates a set of one-time-use backup codes which you\'ll enter on login to verify your identity. Make sure to store these in a safe & secure place.', 'mfa_gen_confirm_and_enable' => 'Confirm and Enable', 'mfa_gen_backup_codes_title' => 'Backup Codes Setup', 'mfa_gen_backup_codes_desc' => 'Store the below list of codes in a safe place. When accessing the system you\'ll be able to use one of the codes as a second authentication mechanism.', diff --git a/lang/ka/common.php b/lang/ka/common.php index 27037babec0..06a9e855ce3 100644 --- a/lang/ka/common.php +++ b/lang/ka/common.php @@ -20,7 +20,7 @@ 'description' => 'Description', 'role' => 'Role', 'cover_image' => 'Cover image', - 'cover_image_description' => 'This image should be approx 440x250px.', + 'cover_image_description' => 'This image should be approximately 440x250px although it will be flexibly scaled & cropped to fit the user interface in different scenarios as required, so actual dimensions for display will differ.', // Actions 'actions' => 'Actions', @@ -30,6 +30,8 @@ 'create' => 'Create', 'update' => 'Update', 'edit' => 'Edit', + 'archive' => 'Archive', + 'unarchive' => 'Un-Archive', 'sort' => 'Sort', 'move' => 'Move', 'copy' => 'Copy', @@ -107,4 +109,7 @@ // Not directly used but available for convenience to users. 'privacy_policy' => 'Privacy Policy', 'terms_of_service' => 'Terms of Service', + + // OpenSearch + 'opensearch_description' => 'Search :appName', ]; diff --git a/lang/ka/editor.php b/lang/ka/editor.php index 670c1c5e1ac..0d250e9a7bd 100644 --- a/lang/ka/editor.php +++ b/lang/ka/editor.php @@ -13,6 +13,7 @@ 'cancel' => 'Cancel', 'save' => 'Save', 'close' => 'Close', + 'apply' => 'Apply', 'undo' => 'Undo', 'redo' => 'Redo', 'left' => 'Left', @@ -47,6 +48,7 @@ 'superscript' => 'Superscript', 'subscript' => 'Subscript', 'text_color' => 'Text color', + 'highlight_color' => 'Highlight color', 'custom_color' => 'Custom color', 'remove_color' => 'Remove color', 'background_color' => 'Background color', @@ -81,6 +83,9 @@ 'table_properties' => 'Table properties', 'table_properties_title' => 'Table Properties', 'delete_table' => 'Delete table', + 'table_clear_formatting' => 'Clear table formatting', + 'resize_to_contents' => 'Resize to contents', + 'row_header' => 'Row header', 'insert_row_before' => 'Insert row before', 'insert_row_after' => 'Insert row after', 'delete_row' => 'Delete row', @@ -144,6 +149,7 @@ 'url' => 'URL', 'text_to_display' => 'Text to display', 'title' => 'Title', + 'browse_links' => 'Browse links', 'open_link' => 'Open link', 'open_link_in' => 'Open link in...', 'open_link_current' => 'Current window', @@ -160,6 +166,8 @@ 'about' => 'About the editor', 'about_title' => 'About the WYSIWYG Editor', 'editor_license' => 'Editor License & Copyright', + 'editor_lexical_license' => 'This editor is built as a fork of :lexicalLink which is distributed under the MIT license.', + 'editor_lexical_license_link' => 'Full license details can be found here.', 'editor_tiny_license' => 'This editor is built using :tinyLink which is provided under the MIT license.', 'editor_tiny_license_link' => 'The copyright and license details of TinyMCE can be found here.', 'save_continue' => 'Save Page & Continue', diff --git a/lang/ka/entities.php b/lang/ka/entities.php index f1f915544d1..74c50be3b2f 100644 --- a/lang/ka/entities.php +++ b/lang/ka/entities.php @@ -39,6 +39,34 @@ 'export_pdf' => 'PDF File', 'export_text' => 'Plain Text File', 'export_md' => 'Markdown File', + 'export_zip' => 'Portable ZIP', + 'default_template' => 'Default Page Template', + 'default_template_explain' => 'Assign a page template that will be used as the default content for all pages created within this item. Keep in mind this will only be used if the page creator has view access to the chosen template page.', + 'default_template_select' => 'Select a template page', + 'import' => 'Import', + 'import_validate' => 'Validate Import', + 'import_desc' => 'Import books, chapters & pages using a portable zip export from the same, or a different, instance. Select a ZIP file to proceed. After the file has been uploaded and validated you\'ll be able to configure & confirm the import in the next view.', + '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_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_size' => ':size Import ZIP Size', + 'import_uploaded_at' => 'Uploaded :relativeTime', + 'import_uploaded_by' => 'Uploaded by', + 'import_location' => 'Import Location', + '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_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', + 'breadcrumb_siblings_for_book' => 'Navigate siblings for book', + 'breadcrumb_siblings_for_bookshelf' => 'Navigate siblings for shelf', // Permissions and restrictions 'permissions' => 'Permissions', @@ -132,9 +160,6 @@ 'books_edit_named' => 'Edit Book :bookName', 'books_form_book_name' => 'Book Name', 'books_save' => 'Save Book', - 'books_default_template' => 'Default Page Template', - 'books_default_template_explain' => 'Assign a page template that will be used as the default content for all new pages in this book. Keep in mind this will only be used if the page creator has view access to those chosen template page.', - 'books_default_template_select' => 'Select a template page', 'books_permissions' => 'Book Permissions', 'books_permissions_updated' => 'Book Permissions Updated', 'books_empty_contents' => 'No pages or chapters have been created for this book.', @@ -145,7 +170,9 @@ 'books_search_this' => 'Search this book', 'books_navigation' => 'Book Navigation', 'books_sort' => 'Sort Book Contents', - 'books_sort_desc' => 'Move chapters and pages within a book to reorganise its contents. Other books can be added which allows easy moving of chapters and pages between books.', + 'books_sort_desc' => 'Move chapters and pages within a book to reorganise its contents. Other books can be added which allows easy moving of chapters and pages between books. Optionally an auto sort rule can be set to automatically sort this book\'s contents upon changes.', + 'books_sort_auto_sort' => 'Auto Sort Option', + 'books_sort_auto_sort_active' => 'Auto Sort Active: :sortName', 'books_sort_named' => 'Sort Book :bookName', 'books_sort_name' => 'Sort by Name', 'books_sort_created' => 'Sort by Created Date', @@ -207,7 +234,7 @@ 'pages_delete_draft' => 'Delete Draft Page', 'pages_delete_success' => 'Page deleted', 'pages_delete_draft_success' => 'Draft page deleted', - 'pages_delete_warning_template' => 'This page is in active use as a book default page template. These books will no longer have a default page template assigned after this page is deleted.', + 'pages_delete_warning_template' => 'This page is in active use as a book or chapter default page template. These books or chapters will no longer have a default page template assigned after this page is deleted.', 'pages_delete_confirm' => 'Are you sure you want to delete this page?', 'pages_delete_draft_confirm' => 'Are you sure you want to delete this draft page?', 'pages_editing_named' => 'Editing Page :pageName', @@ -224,6 +251,8 @@ 'pages_edit_switch_to_markdown_clean' => '(Clean Content)', 'pages_edit_switch_to_markdown_stable' => '(Stable Content)', 'pages_edit_switch_to_wysiwyg' => 'Switch to WYSIWYG Editor', + 'pages_edit_switch_to_new_wysiwyg' => 'Switch to new WYSIWYG', + 'pages_edit_switch_to_new_wysiwyg_desc' => '(In Beta Testing)', 'pages_edit_set_changelog' => 'Set Changelog', 'pages_edit_enter_changelog_desc' => 'Enter a brief description of the changes you\'ve made', 'pages_edit_enter_changelog' => 'Enter Changelog', @@ -243,6 +272,7 @@ 'pages_md_insert_drawing' => 'Insert Drawing', 'pages_md_show_preview' => 'Show preview', 'pages_md_sync_scroll' => 'Sync preview scroll', + 'pages_md_plain_editor' => 'Plaintext editor', 'pages_drawing_unsaved' => 'Unsaved Drawing Found', 'pages_drawing_unsaved_confirm' => 'Unsaved drawing data was found from a previous failed drawing save attempt. Would you like to restore and continue editing this unsaved drawing?', 'pages_not_in_chapter' => 'Page is not in a chapter', @@ -367,8 +397,11 @@ 'comment' => 'Comment', 'comments' => 'Comments', 'comment_add' => 'Add Comment', + 'comment_none' => 'No comments to display', 'comment_placeholder' => 'Leave a comment here', - 'comment_count' => '{0} No Comments|{1} 1 Comment|[2,*] :count Comments', + 'comment_thread_count' => ':count Comment Thread|:count Comment Threads', + 'comment_archived_count' => ':count Archived', + 'comment_archived_threads' => 'Archived Threads', 'comment_save' => 'Save Comment', 'comment_new' => 'New Comment', 'comment_created' => 'commented :createDiff', @@ -377,8 +410,14 @@ 'comment_deleted_success' => 'Comment deleted', 'comment_created_success' => 'Comment added', 'comment_updated_success' => 'Comment updated', + 'comment_archive_success' => 'Comment archived', + 'comment_unarchive_success' => 'Comment un-archived', + 'comment_view' => 'View comment', + 'comment_jump_to_thread' => 'Jump to thread', 'comment_delete_confirm' => 'Are you sure you want to delete this comment?', 'comment_in_reply_to' => 'In reply to :commentId', + 'comment_reference' => 'Reference', + 'comment_reference_outdated' => '(Outdated)', 'comment_editor_explain' => 'Here are the comments that have been left on this page. Comments can be added & managed when viewing the saved page.', // Revision diff --git a/lang/ka/errors.php b/lang/ka/errors.php index 607b6ea833a..9d738379648 100644 --- a/lang/ka/errors.php +++ b/lang/ka/errors.php @@ -10,6 +10,7 @@ // Auth 'error_user_exists_different_creds' => 'A user with the email :email already exists but with different credentials.', + 'auth_pre_register_theme_prevention' => 'User account could not be registered for the provided details', 'email_already_confirmed' => 'Email has already been confirmed, Try logging in.', 'email_confirmation_invalid' => 'This confirmation token is not valid or has already been used, Please try registering again.', 'email_confirmation_expired' => 'The confirmation token has expired, A new confirmation email has been sent.', @@ -23,7 +24,6 @@ 'saml_invalid_response_id' => 'The request from the external authentication system is not recognised by a process started by this application. Navigating back after a login could cause this issue.', 'saml_fail_authed' => 'Login using :system failed, system did not provide successful authorization', 'oidc_already_logged_in' => 'Already logged in', - 'oidc_user_not_registered' => 'The user :name is not registered and automatic registration is disabled', 'oidc_no_email_address' => 'Could not find an email address, for this user, in the data provided by the external authentication system', 'oidc_fail_authed' => 'Login using :system failed, system did not provide successful authorization', 'social_no_action_defined' => 'No action defined', @@ -37,6 +37,7 @@ 'social_driver_not_found' => 'Social driver not found', 'social_driver_not_configured' => 'Your :socialAccount social settings are not configured correctly.', 'invite_token_expired' => 'This invitation link has expired. You can instead try to reset your account password.', + 'login_user_not_found' => 'A user for this action could not be found.', // System 'path_not_writable' => 'File path :filePath could not be uploaded to. Ensure it is writable to the server.', @@ -77,6 +78,7 @@ // Users 'users_cannot_delete_only_admin' => 'You cannot delete the only admin', 'users_cannot_delete_guest' => 'You cannot delete the guest user', + 'users_could_not_send_invite' => 'Could not create user since invite email failed to send', // Roles 'role_cannot_be_edited' => 'This role cannot be edited', @@ -103,6 +105,18 @@ 'app_down' => ':appName is down right now', 'back_soon' => 'It will be back up soon.', + // Import + 'import_zip_cant_read' => 'Could not read ZIP file.', + 'import_zip_cant_decode_data' => 'Could not find and decode ZIP data.json content.', + 'import_zip_no_data' => 'ZIP file data has no expected book, chapter or page content.', + 'import_validation_failed' => 'Import ZIP failed to validate with errors:', + 'import_zip_failed_notification' => 'Failed to import ZIP file.', + 'import_perms_books' => 'You are lacking the required permissions to create books.', + 'import_perms_chapters' => 'You are lacking the required permissions to create chapters.', + 'import_perms_pages' => 'You are lacking the required permissions to create pages.', + 'import_perms_images' => 'You are lacking the required permissions to create images.', + 'import_perms_attachments' => 'You are lacking the required permission to create attachments.', + // API errors 'api_no_authorization_found' => 'No authorization token found on the request', 'api_bad_authorization_format' => 'An authorization token was found on the request but the format appeared incorrect', diff --git a/lang/ka/preferences.php b/lang/ka/preferences.php index 2b88f9671ff..2872f5f3c65 100644 --- a/lang/ka/preferences.php +++ b/lang/ka/preferences.php @@ -27,7 +27,7 @@ 'notifications_save' => 'Save Preferences', 'notifications_update_success' => 'Notification preferences have been updated!', 'notifications_watched' => 'Watched & Ignored Items', - 'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.', + 'notifications_watched_desc' => 'Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.', 'auth' => 'Access & Security', 'auth_change_password' => 'Change Password', diff --git a/lang/ka/settings.php b/lang/ka/settings.php index 03e9bf462e2..81c2c0a93c3 100644 --- a/lang/ka/settings.php +++ b/lang/ka/settings.php @@ -74,6 +74,36 @@ 'reg_confirm_restrict_domain_desc' => 'Enter a comma separated list of email domains you would like to restrict registration to. Users will be sent an email to confirm their address before being allowed to interact with the application.
Note that users will be able to change their email addresses after successful registration.', 'reg_confirm_restrict_domain_placeholder' => 'No restriction set', + // Sorting Settings + 'sorting' => 'Sorting', + 'sorting_book_default' => 'Default Book Sort', + 'sorting_book_default_desc' => 'Select the default sort rule to apply to new books. This won\'t affect existing books, and can be overridden per-book.', + 'sorting_rules' => 'Sort Rules', + 'sorting_rules_desc' => 'These are predefined sorting operations which can be applied to content in the system.', + 'sort_rule_assigned_to_x_books' => 'Assigned to :count Book|Assigned to :count Books', + 'sort_rule_create' => 'Create Sort Rule', + 'sort_rule_edit' => 'Edit Sort Rule', + 'sort_rule_delete' => 'Delete Sort Rule', + 'sort_rule_delete_desc' => 'Remove this sort rule from the system. Books using this sort will revert to manual sorting.', + 'sort_rule_delete_warn_books' => 'This sort rule is currently used on :count book(s). Are you sure you want to delete this?', + 'sort_rule_delete_warn_default' => 'This sort rule is currently used as the default for books. Are you sure you want to delete this?', + 'sort_rule_details' => 'Sort Rule Details', + 'sort_rule_details_desc' => 'Set a name for this sort rule, which will appear in lists when users are selecting a sort.', + 'sort_rule_operations' => 'Sort Operations', + 'sort_rule_operations_desc' => 'Configure the sort actions to be performed by moving them from the list of available operations. Upon use, the operations will be applied in order, from top to bottom. Any changes made here will be applied to all assigned books upon save.', + 'sort_rule_available_operations' => 'Available Operations', + 'sort_rule_available_operations_empty' => 'No operations remaining', + 'sort_rule_configured_operations' => 'Configured Operations', + 'sort_rule_configured_operations_empty' => 'Drag/add operations from the "Available Operations" list', + 'sort_rule_op_asc' => '(Asc)', + 'sort_rule_op_desc' => '(Desc)', + 'sort_rule_op_name' => 'Name - Alphabetical', + 'sort_rule_op_name_numeric' => '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_last' => 'Chapters Last', + // Maintenance settings 'maint' => 'Maintenance', 'maint_image_cleanup' => 'Cleanup Images', @@ -109,7 +139,7 @@ 'recycle_bin_contents_empty' => 'The recycle bin is currently empty', 'recycle_bin_empty' => 'Empty Recycle Bin', 'recycle_bin_empty_confirm' => 'This will permanently destroy all items in the recycle bin including content contained within each item. Are you sure you want to empty the recycle bin?', - 'recycle_bin_destroy_confirm' => 'This action will permanently delete this item, along with any child elements listed below, from the system and you will not be able to restore this content. Are you sure you want to permanently delete this item?', + 'recycle_bin_destroy_confirm' => 'This action will permanently delete this item from the system, along with any child elements listed below, and you will not be able to restore this content. Are you sure you want to permanently delete this item?', 'recycle_bin_destroy_list' => 'Items to be Destroyed', 'recycle_bin_restore_list' => 'Items to be Restored', 'recycle_bin_restore_confirm' => 'This action will restore the deleted item, including any child elements, to their original location. If the original location has since been deleted, and is now in the recycle bin, the parent item will also need to be restored.', @@ -162,6 +192,7 @@ 'role_access_api' => 'Access system API', 'role_manage_settings' => 'Manage app settings', 'role_export_content' => 'Export content', + 'role_import_content' => 'Import content', 'role_editor_change' => 'Change page editor', 'role_notifications' => 'Receive & manage notifications', 'role_asset' => 'Asset Permissions', @@ -276,6 +307,14 @@ 'webhooks_last_errored' => 'Last Errored:', 'webhooks_last_error_message' => 'Last Error Message:', + // Licensing + 'licenses' => 'Licenses', + 'licenses_desc' => 'This page details license information for BookStack in addition to the projects & libraries that are used within BookStack. Many projects listed may only be used in a development context.', + 'licenses_bookstack' => 'BookStack License', + 'licenses_php' => 'PHP Library Licenses', + 'licenses_js' => 'JavaScript Library Licenses', + 'licenses_other' => 'Other Licenses', + 'license_details' => 'License Details', //! If editing translations files directly please ignore this in all //! languages apart from en. Content will be auto-copied from en. @@ -287,6 +326,7 @@ 'bs' => 'Bosanski', 'ca' => 'Català', 'cs' => 'Česky', + 'cy' => 'Cymraeg', 'da' => 'Dansk', 'de' => 'Deutsch (Sie)', 'de_informal' => 'Deutsch (Du)', @@ -308,6 +348,7 @@ 'lt' => 'Lietuvių Kalba', 'lv' => 'Latviešu Valoda', 'nb' => 'Norsk (Bokmål)', + 'ne' => 'नेपाली', 'nn' => 'Nynorsk', 'nl' => 'Nederlands', 'pl' => 'Polski', diff --git a/lang/ka/validation.php b/lang/ka/validation.php index 2a676c7c4cc..d9b982d1e23 100644 --- a/lang/ka/validation.php +++ b/lang/ka/validation.php @@ -105,6 +105,11 @@ 'url' => 'The :attribute format is invalid.', 'uploaded' => 'The file could not be uploaded. The server may not accept files of this size.', + 'zip_file' => 'The :attribute needs to reference a file within the ZIP.', + 'zip_file_mime' => 'The :attribute needs to reference a file of type :validTypes, found :foundType.', + 'zip_model_expected' => 'Data object expected but ":type" found.', + 'zip_unique' => 'The :attribute must be unique for the object type within the ZIP.', + // Custom validation lines 'custom' => [ 'password-confirm' => [ diff --git a/lang/ko/activities.php b/lang/ko/activities.php index 4cb03f90cc9..c1dbb93fd7b 100644 --- a/lang/ko/activities.php +++ b/lang/ko/activities.php @@ -6,29 +6,29 @@ return [ // Pages - 'page_create' => '생성된 페이지', - 'page_create_notification' => '페이지가 성공적으로 생성되었습니다.', - 'page_update' => '페이지 업데이트됨', - 'page_update_notification' => '페이지가 성공적으로 업데이트되었습니다.', - 'page_delete' => '삭제된 페이지', - 'page_delete_notification' => '페이지가 성공적으로 삭제되었습니다.', - 'page_restore' => '복구된 페이지', - 'page_restore_notification' => '페이지가 성공적으로 복원되었습니다.', - 'page_move' => '이동된 페이지', - 'page_move_notification' => '페이지가 성공적으로 이동되었습니다.', + 'page_create' => '페이지 생성', + 'page_create_notification' => '페이지가 성공적으로 생성되었습니다', + 'page_update' => '페이지 업데이트', + 'page_update_notification' => '페이지가 성공적으로 업데이트 되었습니다', + 'page_delete' => '페이지 삭제', + 'page_delete_notification' => '페이지가 성공적으로 삭제되었습니다', + 'page_restore' => '페이지 복원', + 'page_restore_notification' => '페이지가 성공적으로 복원되었습니다', + 'page_move' => '페이지 이동', + 'page_move_notification' => '페이지가 성공적으로 이동되었습니다', // Chapters - 'chapter_create' => '챕터 만들기', + 'chapter_create' => '챕터 생성', 'chapter_create_notification' => '챕터가 성공적으로 생성되었습니다.', - 'chapter_update' => '업데이트된 챕터', - 'chapter_update_notification' => '챕터가 성공적으로 업데이트되었습니다.', - 'chapter_delete' => '삭제된 챕터', + 'chapter_update' => '챕터 수정', + 'chapter_update_notification' => '챕터가 성공적으로 수정되었습니다.', + 'chapter_delete' => '챕터 삭제', 'chapter_delete_notification' => '챕터가 성공적으로 삭제되었습니다.', - 'chapter_move' => '이동된 챕터', - 'chapter_move_notification' => '챕터를 성공적으로 이동했습니다.', + 'chapter_move' => '챕터 이동', + 'chapter_move_notification' => '페이지가 성공적으로 이동되었습니다.', // Books - 'book_create' => '생성된 책', + 'book_create' => '책 생성', 'book_create_notification' => '책이 성공적으로 생성되었습니다.', 'book_create_from_chapter' => '챕터를 책으로 변환', 'book_create_from_chapter_notification' => '챕터가 책으로 성공적으로 변환되었습니다.', @@ -36,7 +36,7 @@ 'book_update_notification' => '책이 성공적으로 업데이트되었습니다.', 'book_delete' => '삭제된 책', 'book_delete_notification' => '책이 성공적으로 삭제되었습니다.', - 'book_sort' => '정렬된 책', + 'book_sort' => '책 정렬', 'book_sort_notification' => '책이 성공적으로 재정렬되었습니다.', // Bookshelves @@ -44,15 +44,15 @@ 'bookshelf_create_notification' => '책장을 성공적으로 생성했습니다.', 'bookshelf_create_from_book' => '책을 책장으로 변환함', 'bookshelf_create_from_book_notification' => '책을 성공적으로 책장으로 변환하였습니다.', - 'bookshelf_update' => '책장 업데이트됨', + 'bookshelf_update' => '책장 업데이트', 'bookshelf_update_notification' => '책장이 성공적으로 업데이트 되었습니다.', - 'bookshelf_delete' => '삭제된 책장', + 'bookshelf_delete' => '책장 삭제', 'bookshelf_delete_notification' => '책장이 성공적으로 삭제되었습니다.', // Revisions - 'revision_restore' => '복구된 리비전', - 'revision_delete' => '삭제된 리비전', - 'revision_delete_notification' => '리비전을 성공적으로 삭제하였습니다.', + 'revision_restore' => '버전 복구', + 'revision_delete' => '버전 삭제', + 'revision_delete_notification' => '버전 삭제 성공', // Favourites 'favourite_add_notification' => '":name" 을 북마크에 추가하였습니다.', @@ -62,28 +62,36 @@ 'watch_update_level_notification' => '주시 환경설정이 성공적으로 업데이트되었습니다.', // Auth - 'auth_login' => '로그인 됨', + 'auth_login' => '로그인 완료', 'auth_register' => '신규 사용자 등록', 'auth_password_reset_request' => '사용자 비밀번호 초기화 요청', 'auth_password_reset_update' => '사용자 비밀번호 초기화', - 'mfa_setup_method' => '구성된 MFA 방법', + 'mfa_setup_method' => '다중인증(MFA)이 구성되었습니다.', 'mfa_setup_method_notification' => '다중 인증 설정함', 'mfa_remove_method' => 'MFA 메서드 제거', - 'mfa_remove_method_notification' => '다중 인증 해제함', + 'mfa_remove_method_notification' => '다중인증(MFA)이 성공적으로 제거되었습니다.', // Settings 'settings_update' => '설정 변경', 'settings_update_notification' => '설졍 변경 성공', - 'maintenance_action_run' => '유지 관리 작업 실행', + 'maintenance_action_run' => '유지관리 작업 실행', // Webhooks - 'webhook_create' => '웹 훅 만들기', + 'webhook_create' => '웹 훅 생성', 'webhook_create_notification' => '웹 훅 생성함', - 'webhook_update' => '웹 훅 수정하기', - 'webhook_update_notification' => '웹 훅 수정함', + 'webhook_update' => '웹 훅 수정', + 'webhook_update_notification' => '웹 훅 설정이 수정되었습니다.', 'webhook_delete' => '웹 훅 지우기', 'webhook_delete_notification' => '웹 훅 삭제함', + // Imports + 'import_create' => '컨텐츠 ZIP 파일이 생성되었습니다.', + 'import_create_notification' => '컨텐츠 ZIP 파일이 업로드 되었습니다.', + 'import_run' => '컨텐츠 ZIP 파일을 업데이트하였습니다.', + 'import_run_notification' => '내용을 가져왔습니다.', + 'import_delete' => '임포트 파일 삭제', + 'import_delete_notification' => '임포트 파일을 삭제하였습니다.', + // Users 'user_create' => '사용자 생성', 'user_create_notification' => '사용자 생성 성공', @@ -93,9 +101,9 @@ 'user_delete_notification' => '사용자가 삭제되었습니다', // API Tokens - 'api_token_create' => 'aPI 토큰 생성', + 'api_token_create' => '생성된 API 토큰', 'api_token_create_notification' => 'API 토큰이 성공적으로 생성되었습니다.', - 'api_token_update' => '업데이트된 API 토큰', + 'api_token_update' => '갱신된 API 토큰', 'api_token_update_notification' => 'API 토큰이 성공적으로 업데이트되었습니다.', 'api_token_delete' => '삭제된 API 토큰', 'api_token_delete_notification' => 'API 토큰이 성공적으로 삭제되었습니다.', @@ -119,6 +127,14 @@ 'comment_update' => '댓글 변경', 'comment_delete' => '댓글 삭제', + // Sort Rules + 'sort_rule_create' => '정렬 규칙 생성', + 'sort_rule_create_notification' => '정렬 규칙이 성공적으로 생성되었습니다', + 'sort_rule_update' => '정렬 규칙 업데이트', + 'sort_rule_update_notification' => '정렬 규칙이 성공적으로 업데이트 되었습니다', + 'sort_rule_delete' => '정렬 규칙 삭제', + 'sort_rule_delete_notification' => '정렬 규칙이 성공적으로 삭제되었습니다', + // Other 'permissions_update' => '권한 수정함', ]; diff --git a/lang/ko/auth.php b/lang/ko/auth.php index a29f9b9ad96..6b60225cbd4 100644 --- a/lang/ko/auth.php +++ b/lang/ko/auth.php @@ -68,7 +68,7 @@ 'email_not_confirmed_text' => '전자우편 확인이 아직 완료되지 않았습니다.', 'email_not_confirmed_click_link' => '등록한 직후에 발송된 전자우편에 있는 확인 링크를 클릭하세요.', 'email_not_confirmed_resend' => '전자우편 확인을 위해 발송된 전자우편을 찾을 수 없다면 아래의 폼을 다시 발행하여 전자우편 확인을 재발송할 수 있습니다.', - 'email_not_confirmed_resend_button' => '전자우편 확인 재전송', + 'email_not_confirmed_resend_button' => '확인용 이메일 재전송', // User Invite 'user_invite_email_subject' => ':appName 애플리케이션에서 초대를 받았습니다!', @@ -84,18 +84,18 @@ 'mfa_setup' => '다중 인증 설정', 'mfa_setup_desc' => '추가 보안 계층으로 다중 인증을 설정합니다.', 'mfa_setup_configured' => '이미 설정되었습니다', - 'mfa_setup_reconfigure' => '다시 설정', + 'mfa_setup_reconfigure' => '재설정', 'mfa_setup_remove_confirmation' => '다중 인증을 해제할까요?', 'mfa_setup_action' => '설정', 'mfa_backup_codes_usage_limit_warning' => '남은 백업 코드가 다섯 개 미만입니다. 새 백업 코드 세트를 생성하지 않아 코드가 소진되면 계정이 잠길 수 있습니다.', 'mfa_option_totp_title' => '모바일 앱', 'mfa_option_totp_desc' => '다중 인증에는 Google Authenticator, Authy나 Microsoft Authenticator와 같은 TOTP 지원 모바일 앱이 필요합니다.', 'mfa_option_backup_codes_title' => '백업 코드', - 'mfa_option_backup_codes_desc' => '일회성 백업 코드를 안전한 장소에 보관하세요.', + 'mfa_option_backup_codes_desc' => '로그인 시 인증에 사용되는 일회용 백업 코드를 만듭니다. 이 백업 코드는 안전한 곳에 보관하세요.', 'mfa_gen_confirm_and_enable' => '확인 및 활성화', 'mfa_gen_backup_codes_title' => '백업 코드 설정', 'mfa_gen_backup_codes_desc' => '코드 목록을 안전한 장소에 보관하세요. 코드 중 하나를 2FA에 쓸 수 있습니다.', - 'mfa_gen_backup_codes_download' => '코드 내려받기', + 'mfa_gen_backup_codes_download' => '코드 설치', 'mfa_gen_backup_codes_usage_warning' => '각 코드는 한 번씩만 유효합니다.', 'mfa_gen_totp_title' => '모바일 앱 설정', 'mfa_gen_totp_desc' => '다중 인증에는 Google Authenticator, Authy나 Microsoft Authenticator와 같은 TOTP 지원 모바일 앱이 필요합니다.', @@ -104,7 +104,7 @@ 'mfa_gen_totp_verify_setup_desc' => '인증 앱에서 생성한 코드를 입력하세요:', 'mfa_gen_totp_provide_code_here' => '백업 코드를 입력하세요.', 'mfa_verify_access' => '접근 확인', - 'mfa_verify_access_desc' => '추가 인증으로 신원을 확인합니다. 설정한 방법 중 하나를 고르세요.', + 'mfa_verify_access_desc' => '사용자 계정에서는 액세스 권한을 부여받기 전에 추가 검증 수준을 통해 신원을 확인해야 합니다. 계속하려면 구성된 방법 중 하나를 사용하여 확인하세요.', 'mfa_verify_no_methods' => '설정한 방법이 없습니다.', 'mfa_verify_no_methods_desc' => '다중 인증을 설정하지 않았습니다. 접근 권한을 얻기 전에 하나 이상의 다중 인증을 설정해야 합니다.', 'mfa_verify_use_totp' => '모바일 앱으로 인증하기', diff --git a/lang/ko/common.php b/lang/ko/common.php index 794fdc6cd3c..ff35f097179 100644 --- a/lang/ko/common.php +++ b/lang/ko/common.php @@ -18,26 +18,28 @@ // Form Labels 'name' => '이름', 'description' => '설명', - 'role' => '권한', + 'role' => '역할', 'cover_image' => '대표 이미지', - 'cover_image_description' => '이미지 크기는 440x250px 내외입니다.', + 'cover_image_description' => '이 이미지는 대략 440x250px가 되어야 하지만, 필요에 따라 다양한 시나리오에서 사용자 인터페이스에 맞게 유연하게 크기 조절 및 자르기가 가능하므로 실제로 표시되는 크기는 다를 수 있습니다.', // Actions - 'actions' => '활동', + 'actions' => '동작', 'view' => '보기', 'view_all' => '모두 보기', 'new' => '신규', 'create' => '만들기', 'update' => '바꾸기', 'edit' => '수정', + 'archive' => 'Archive', + 'unarchive' => 'Un-Archive', 'sort' => '정렬', 'move' => '이동', 'copy' => '복사', 'reply' => '답글', 'delete' => '삭제', - 'delete_confirm' => '삭제 요청 확인', + 'delete_confirm' => '삭제', 'search' => '검색', - 'search_clear' => '검색 지우기', + 'search_clear' => '검색창 비우기', 'reset' => '리셋', 'remove' => '제거', 'add' => '추가', @@ -71,10 +73,10 @@ 'back_to_top' => '맨 위로', 'skip_to_main_content' => '메인 항목으로', 'toggle_details' => '내용 보기', - 'toggle_thumbnails' => '섬네일 보기', + 'toggle_thumbnails' => '썸네일 보기', 'details' => '정보', - 'grid_view' => '격자 보기', - 'list_view' => '목록 보기', + 'grid_view' => '격자 형식으로 보기', + 'list_view' => '리스트 형식으로 보기', 'default' => '기본 설정', 'breadcrumb' => '탐색 경로', 'status' => '상태', @@ -101,10 +103,13 @@ // Email Content 'email_action_help' => ':actionText를 클릭할 수 없을 때는 웹 브라우저에서 다음 링크로 접속할 수 있습니다.', - 'email_rights' => 'All rights reserved.', + 'email_rights' => '모든 권리는 보호됩니다.', // Footer Link Options // Not directly used but available for convenience to users. 'privacy_policy' => '개인 정보 처리 방침', 'terms_of_service' => '서비스 이용 약관', + + // OpenSearch + 'opensearch_description' => '검색 :appName', ]; diff --git a/lang/ko/components.php b/lang/ko/components.php index c81497e0308..0c0bc2e65ed 100644 --- a/lang/ko/components.php +++ b/lang/ko/components.php @@ -12,7 +12,7 @@ 'image_intro' => '여기에서 이전에 시스템에 업로드한 이미지를 선택하고 관리할 수 있습니다.', 'image_intro_upload' => '이미지 파일을 이 창으로 끌어다 놓거나 위의 \'이미지 업로드\' 버튼을 사용하여 새 이미지를 업로드합니다.', 'image_all' => '모든 이미지', - 'image_all_title' => '모든 이미지', + 'image_all_title' => '모든 이미지 보기', 'image_book_title' => '이 책에서 쓰고 있는 이미지', 'image_page_title' => '이 문서에서 쓰고 있는 이미지', 'image_search_hint' => '이미지 이름 검색', @@ -23,17 +23,17 @@ 'image_load_more' => '더 보기', 'image_image_name' => '이미지 이름', 'image_delete_used' => '이 이미지는 다음 문서들이 쓰고 있습니다.', - 'image_delete_confirm_text' => '이 이미지를 지울 건가요?', + 'image_delete_confirm_text' => '이 이미지를 정말 삭제하시겠습니까?', 'image_select_image' => '이미지 선택', 'image_dropzone' => '여기에 이미지를 드롭하거나 여기를 클릭하세요. 이미지를 올릴 수 있습니다.', 'image_dropzone_drop' => '업로드 할 이미지 파일을 여기에 놓으세요.', 'images_deleted' => '이미지 삭제함', 'image_preview' => '이미지 미리 보기', 'image_upload_success' => '이미지 올림', - 'image_update_success' => '이미지 정보 수정함', - 'image_delete_success' => '이미지 삭제함', + 'image_update_success' => '이미지 정보가 수정되었습니다.', + 'image_delete_success' => '이미지가 삭제되었습니다.', 'image_replace' => '이미지 교체', - 'image_replace_success' => '이미지 파일 업데이트 성공', + 'image_replace_success' => '이미지가 수정되었습니다.', 'image_rebuild_thumbs' => '사이즈 변경 재생성하기', 'image_rebuild_thumbs_success' => '이미지 크기 변경이 성공적으로 완료되었습니다!', diff --git a/lang/ko/editor.php b/lang/ko/editor.php index 7959bc81dc0..b5c4ae8070c 100644 --- a/lang/ko/editor.php +++ b/lang/ko/editor.php @@ -13,6 +13,7 @@ 'cancel' => '취소', 'save' => '저장', 'close' => '닫기', + 'apply' => '적용', 'undo' => '되돌리기', 'redo' => '다시 실행', 'left' => '왼쪽', @@ -47,6 +48,7 @@ 'superscript' => '윗첨자', 'subscript' => '아랫첨자', 'text_color' => '글자 색상', + 'highlight_color' => 'Highlight color', 'custom_color' => '사용자 지정 색상', 'remove_color' => '색상 제거', 'background_color' => '배경 색상', @@ -81,6 +83,9 @@ 'table_properties' => '테이블 속성', 'table_properties_title' => '테이블 속성', 'delete_table' => '테이블 삭제', + 'table_clear_formatting' => '테이블 형식 지우기', + 'resize_to_contents' => '내용 크기 조정', + 'row_header' => '행 머릿글', 'insert_row_before' => '앞에 행 추가', 'insert_row_after' => '뒤에 행 추가', 'delete_row' => '행 삭제', @@ -144,6 +149,7 @@ 'url' => 'URL', 'text_to_display' => '표시할 텍스트', 'title' => '제목', + 'browse_links' => '링크 찾기', 'open_link' => '링크 열기', 'open_link_in' => '다음에서 링크 열기...', 'open_link_current' => '현재 창', @@ -160,6 +166,8 @@ 'about' => '이 편집기에 대하여', 'about_title' => 'WYSIWYG 편집기에 대하여', 'editor_license' => '편집기 라이선스 & 저작권', + 'editor_lexical_license' => '이 편집기는 MIT 라이선스에 따라 배포되는 :lexicalLink의 포크로 만들어졌습니다.', + 'editor_lexical_license_link' => '전체 라이센스 세부 사항은 여기에서 확인할 수 있습니다.', 'editor_tiny_license' => '이 편집기는 MIT 라이선스에 따라 제공되는 :tinyLink를 사용하여 제작되었습니다.', 'editor_tiny_license_link' => 'TinyMCE의 저작권 및 라이선스 세부 정보는 여기에서 확인할 수 있습니다.', 'save_continue' => '저장하고 계속하기', diff --git a/lang/ko/entities.php b/lang/ko/entities.php index 934d7201e2b..b5624f2213b 100644 --- a/lang/ko/entities.php +++ b/lang/ko/entities.php @@ -12,17 +12,17 @@ 'recently_created_chapters' => '최근에 만든 챕터', 'recently_created_books' => '최근에 만든 책', 'recently_created_shelves' => '최근에 만든 책장', - 'recently_update' => '최근에 수정함', - 'recently_viewed' => '최근에 읽음', - 'recent_activity' => '최근에 활동함', + 'recently_update' => '최신 수정 목록', + 'recently_viewed' => '최근에 본 목록', + 'recent_activity' => '최근 활동 기록', 'create_now' => '바로 만들기', - 'revisions' => '수정본', - 'meta_revision' => '판본 #:revisionCount', - 'meta_created' => '만듦 :timeLength', - 'meta_created_name' => '만듦 :timeLength, :user', - 'meta_updated' => '수정함 :timeLength', - 'meta_updated_name' => '수정함 :timeLength, :user', - 'meta_owned_name' => '소유함 :user', + 'revisions' => '버전', + 'meta_revision' => '버전 #:revisionCount', + 'meta_created' => '생성 :timeLength', + 'meta_created_name' => '생성 :timeLength, :user', + 'meta_updated' => '수정 :timeLength', + 'meta_updated_name' => '수정 :timeLength, :user', + 'meta_owned_name' => ':user 소유', 'meta_reference_count' => '참조 대상 :count item|참조 대상 :count items', 'entity_select' => '항목 선택', 'entity_select_lack_permission' => '이 항목을 선택하기 위해 필요한 권한이 없습니다', @@ -39,6 +39,34 @@ 'export_pdf' => 'PDF 파일', 'export_text' => '일반 텍스트 파일', 'export_md' => '마크다운 파일', + 'export_zip' => '컨텐츠 ZIP 파일', + 'default_template' => '기본 페이지 템플릿', + 'default_template_explain' => '이 항목 내에서 생성되는 모든 페이지의 기본 콘텐츠로 사용할 페이지 템플릿을 지정합니다. 페이지 작성자가 선택한 템플릿 페이지를 볼 수 있는 권한이 있는 경우에만 이 항목이 사용된다는 점을 유의하세요.', + 'default_template_select' => '템플릿 페이지 선택', + 'import' => '컨텐츠 ZIP 파일 가져오기', + 'import_validate' => '컨텐츠 ZIP 파일 검증하기', + 'import_desc' => '같은 인스턴스나 다른 인스턴스에서 휴대용 zip 내보내기를 사용하여 책, 장 및 페이지를 가져옵니다. 진행하려면 ZIP 파일을 선택합니다. 파일을 업로드하고 검증한 후 다음 보기에서 가져오기를 구성하고 확인할 수 있습니다.', + 'import_zip_select' => '업로드할 휴대용 압축 파일 선택', + 'import_zip_validation_errors' => '컨텐츠 ZIP 파일을 검증하는 동안 오류가 감지되었습니다.', + 'import_pending' => '컨텐츠 ZIP 파일 가져오기가 일시정지 되었습니다.', + 'import_pending_none' => 'Portable ZIP 파일 가져오기가 시작되지 않았습니다.', + 'import_continue' => '컨텐츠 ZIP 파일 가져오기가 시작되지 않았습니다.', + 'import_continue_desc' => '업로드된 컨텐츠 ZIP 파일에서 가져올 내용을 검토합니다. 준비가 되면 가져오기를 실행하여 이 시스템에 내용을 추가합니다. 업로드된 ZIP 가져오기 파일은 가져오기가 성공하면 자동으로 제거됩니다.', + 'import_details' => '컨텐츠 ZIP 파일 상세', + 'import_run' => '가져오기 실행', + 'import_size' => '컨텐츠 ZIP 파일 사이즈', + 'import_uploaded_at' => '업로드되었습니다. :relativeTime', + 'import_uploaded_by' => '업로드되었습니다.', + 'import_location' => '가져올 경로', + 'import_location_desc' => '가져온 콘텐츠에 대한 대상 위치를 선택하세요. 선택한 위치 내에서 만들려면 관련 권한이 필요합니다.', + 'import_delete_confirm' => '정말 삭제하시겠습니까?', + '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', // Permissions and restrictions 'permissions' => '권한', @@ -63,7 +91,7 @@ 'search_terms' => '용어 검색', 'search_content_type' => '형식', 'search_exact_matches' => '정확히 일치', - 'search_tags' => '꼬리표 일치', + 'search_tags' => '태그 검색', 'search_options' => '선택', 'search_viewed_by_me' => '내가 읽음', 'search_not_viewed_by_me' => '내가 읽지 않음', @@ -127,25 +155,24 @@ 'books_delete' => '책 삭제하기', 'books_delete_named' => ':bookName(을)를 지웁니다.', 'books_delete_explain' => ':bookName에 있는 모든 챕터와 문서도 지웁니다.', - 'books_delete_confirmation' => '이 책을 지울 건가요?', + 'books_delete_confirmation' => '이 책을 지우시겠습니까?', 'books_edit' => '책 바꾸기', 'books_edit_named' => ':bookName(을)를 바꿉니다.', 'books_form_book_name' => '책 이름', 'books_save' => '저장', - 'books_default_template' => '기본 페이지 템플릿', - 'books_default_template_explain' => '이 책에 있는 모든 새 페이지의 기본 콘텐츠로 사용할 페이지 템플릿을 지정합니다. 페이지 작성자에게 선택한 템플릿 페이지에 대한 보기 액세스 권한이 있는 경우에만 이 템플릿이 사용된다는 점에 유의하세요.', - 'books_default_template_select' => '템플릿 페이지 선택', 'books_permissions' => '책 권한', - 'books_permissions_updated' => '권한 저장함', + 'books_permissions_updated' => '책의 권한이 수정되었습니다.', 'books_empty_contents' => '이 책에 챕터나 문서가 없습니다.', 'books_empty_create_page' => '문서 만들기', - 'books_empty_sort_current_book' => '읽고 있는 책 정렬', + 'books_empty_sort_current_book' => '현재 책 정렬', 'books_empty_add_chapter' => '챕터 만들기', - 'books_permissions_active' => '책 권한 허용함', + 'books_permissions_active' => '책 권한 적용됨', 'books_search_this' => '이 책에서 검색', 'books_navigation' => '목차', - 'books_sort' => '다른 책들', - 'books_sort_desc' => '책 내에서 챕터와 페이지를 이동하여 내용을 재구성할 수 있습니다. 다른 책을 추가하여 책 간에 챕터와 페이지를 쉽게 이동할 수 있습니다.', + 'books_sort' => '책 내용 정렬', + 'books_sort_desc' => '책 내의 챕터와 페이지를 이동하여 콘텐츠를 재구성할 수 있습니다. 다른 책들을 추가하여 책 간의 챕터와 페이지를 쉽게 이동할 수 있습니다. 선택적으로 자동 정렬 규칙을 설정하여 변경 시 이 책의 콘텐츠를 자동으로 정렬할 수 있습니다.', + 'books_sort_auto_sort' => '자동 정렬 옵션', + 'books_sort_auto_sort_active' => '현재 설정된 자동 정렬: :sortName', 'books_sort_named' => ':bookName 정렬', 'books_sort_name' => '제목', 'books_sort_created' => '만든 날짜', @@ -166,7 +193,7 @@ 'books_sort_move_before_chapter' => '이전 챕터로 이동', 'books_sort_move_after_chapter' => '챕터 뒤로 이동', 'books_copy' => '책 복사하기', - 'books_copy_success' => '책 복사함', + 'books_copy_success' => '책을 복사하였습니다.', // Chapters 'chapter' => '챕터', @@ -179,17 +206,17 @@ 'chapters_delete_named' => ':chapterName(을)를 지웁니다.', 'chapters_delete_explain' => '\':ChapterName\'에 있는 모든 페이지도 지웁니다.', 'chapters_delete_confirm' => '이 챕터를 지울 건가요?', - 'chapters_edit' => '챕터 바꾸기', + 'chapters_edit' => '챕터 수정하기', 'chapters_edit_named' => ':chapterName 바꾸기', 'chapters_save' => '저장', 'chapters_move' => '챕터 이동하기', 'chapters_move_named' => ':chapterName 이동하기', 'chapters_copy' => '챕터 복사하기', - 'chapters_copy_success' => '챕터 복사함', + 'chapters_copy_success' => '챕터를 복사하였습니다.', 'chapters_permissions' => '챕터 권한', 'chapters_empty' => '이 챕터에 문서가 없습니다.', 'chapters_permissions_active' => '문서 권한 허용함', - 'chapters_permissions_success' => '권한 저장함', + 'chapters_permissions_success' => '챕터의 권한을 수정하였습니다.', 'chapters_search_this' => '이 챕터에서 검색', 'chapter_sort_book' => '책 정렬하기', @@ -207,7 +234,7 @@ 'pages_delete_draft' => '초안 문서 삭제하기', 'pages_delete_success' => '문서 지움', 'pages_delete_draft_success' => '초안 문서 지움', - 'pages_delete_warning_template' => '이 페이지는 책의 기본 페이지 템플릿으로 사용 중입니다. 이 페이지가 삭제된 후에는 이러한 책에 더 이상 기본 페이지 템플릿이 할당되지 않습니다.', + 'pages_delete_warning_template' => '이 페이지는 책의 기본 페이지 템플릿으로 사용 중입니다. 이 페이지가 삭제되면 해당하는 책에 더 이상 기본 페이지 템플릿이 적용되지 않습니다.', 'pages_delete_confirm' => '이 문서를 지울 건가요?', 'pages_delete_draft_confirm' => '이 초안을 지울 건가요?', 'pages_editing_named' => ':pageName 수정', @@ -219,19 +246,21 @@ 'pages_edit_draft_save_at' => '보관함: ', 'pages_edit_delete_draft' => '초안 삭제', 'pages_edit_delete_draft_confirm' => '초안 페이지 변경 내용을 삭제하시겠습니까? 마지막 전체 저장 이후의 모든 변경 내용이 손실되고 편집기가 최신 페이지의 초안 저장 상태가 아닌 상태로 업데이트됩니다.', - 'pages_edit_discard_draft' => '폐기', + 'pages_edit_discard_draft' => '변경된 내용 삭제', 'pages_edit_switch_to_markdown' => '마크다운 편집기로 전환', 'pages_edit_switch_to_markdown_clean' => '(Clean Content)', 'pages_edit_switch_to_markdown_stable' => '(Stable Content)', 'pages_edit_switch_to_wysiwyg' => 'WYSIWYG 편집기로 전환', + 'pages_edit_switch_to_new_wysiwyg' => '새 위지윅 편집기로 변경', + 'pages_edit_switch_to_new_wysiwyg_desc' => '(In Beta Testing)', 'pages_edit_set_changelog' => '수정본 설명', 'pages_edit_enter_changelog_desc' => '수정본 설명', - 'pages_edit_enter_changelog' => '설명', + 'pages_edit_enter_changelog' => '변경 로그 입력란', 'pages_editor_switch_title' => '편집기 전환', 'pages_editor_switch_are_you_sure' => '이 페이지의 편집기를 변경하시겠어요?', 'pages_editor_switch_consider_following' => '편집기를 전환할 때에 다음 사항들을 고려하세요:', 'pages_editor_switch_consideration_a' => '저장된 새 편집기 옵션은 편집기 유형을 직접 변경할 수 없는 편집자를 포함하여 향후 모든 편집자가 사용할 수 있습니다.', - 'pages_editor_switch_consideration_b' => '이로 인해 특정 상황에서는 디테일과 구문이 손실될 수 있습니다.', + 'pages_editor_switch_consideration_b' => '이로 인해 특정 상황에서는 상세 내용과 구문이 손실될 수 있습니다.', 'pages_editor_switch_consideration_c' => '마지막 저장 이후 변경된 태그 또는 변경 로그 변경 사항은 이 변경 사항에서 유지되지 않습니다.', 'pages_save' => '저장', 'pages_title' => '문서 제목', @@ -243,6 +272,7 @@ 'pages_md_insert_drawing' => '드로잉 추가', 'pages_md_show_preview' => '미리보기 표시', 'pages_md_sync_scroll' => '미리보기 스크롤 동기화', + 'pages_md_plain_editor' => 'Plaintext editor', 'pages_drawing_unsaved' => '저장되지 않은 드로잉 발견', 'pages_drawing_unsaved_confirm' => '이전에 실패한 드로잉 저장 시도에서 저장되지 않은 드로잉 데이터가 발견되었습니다. 이 저장되지 않은 드로잉을 복원하고 계속 편집하시겠습니까?', 'pages_not_in_chapter' => '챕터에 있는 문서가 아닙니다.', @@ -285,7 +315,7 @@ 'pages_initial_name' => '제목 없음', 'pages_editing_draft_notification' => ':timeDiff에 초안 문서입니다.', 'pages_draft_edited_notification' => '최근에 수정한 문서이기 때문에 초안 문서를 폐기하는 편이 좋습니다.', - 'pages_draft_page_changed_since_creation' => '최근에 수정한 문서이기 때문에 초안 문서를 폐기하는 편이 좋습니다.', + 'pages_draft_page_changed_since_creation' => '최근에 수정한 문서이기 때문에 임시 저장문서를 폐기하는 편이 좋습니다.', 'pages_draft_edit_active' => [ 'start_a' => ':count명이 이 문서를 수정하고 있습니다.', 'start_b' => ':userName이 이 문서를 수정하고 있습니다.', @@ -300,7 +330,7 @@ // Editor Sidebar 'toggle_sidebar' => '사이드바 토글', - 'page_tags' => '문서 꼬리표', + 'page_tags' => '문서 태그', 'chapter_tags' => '챕터 꼬리표', 'book_tags' => '책 꼬리표', 'shelf_tags' => '책꽂이 꼬리표', @@ -309,19 +339,19 @@ 'tags_index_desc' => '태그를 시스템 내의 콘텐츠에 적용하여 유연한 형태의 분류를 적용할 수 있습니다. 태그는 키와 값을 모두 가질 수 있으며 값은 선택 사항입니다. 태그가 적용되면 태그 이름과 값을 사용하여 콘텐츠를 쿼리할 수 있습니다.', 'tag_name' => '꼬리표 이름', 'tag_value' => '리스트 값 (선택 사항)', - 'tags_explain' => "꼬리표로 문서를 분류하세요.", + 'tags_explain' => "문서를 더 잘 분류하려면 태그를 추가하세요.\n태그에 값을 할당하여 더욱 체계적으로 구성할 수 있습니다.", 'tags_add' => '꼬리표 추가', 'tags_remove' => '꼬리표 삭제', 'tags_usages' => '모든 꼬리표', 'tags_assigned_pages' => '문서에 꼬리표 지정함', 'tags_assigned_chapters' => '챕터에 꼬리표 지정함', - 'tags_assigned_books' => '책에 꼬리표 지정함', + 'tags_assigned_books' => '책에 태그 지정함', 'tags_assigned_shelves' => '책꽂이에 꼬리표 지정함', 'tags_x_unique_values' => ':count 중복 없는 값', 'tags_all_values' => '모든 값', 'tags_view_tags' => '꼬리표 보기', 'tags_view_existing_tags' => '사용 중인 꼬리표 보기', - 'tags_list_empty_hint' => '꼬리표는 에디터 사이드바나 책, 챕터 또는 책꽂이 정보 편집에서 지정할 수 있습니다.', + 'tags_list_empty_hint' => '태그는 에디터 사이드바나 책, 챕터 또는 책꽂이 정보 편집에서 지정할 수 있습니다.', 'attachments' => '첨부 파일', 'attachments_explain' => '파일이나 링크를 첨부하세요. 정보 탭에 나타납니다.', 'attachments_explain_instant_save' => '여기에서 바꾼 내용은 바로 적용합니다.', @@ -349,7 +379,7 @@ 'attachments_file_updated' => '파일 바꿈', 'attachments_link_attached' => '링크 첨부함', 'templates' => '템플릿', - 'templates_set_as_template' => '템플릿', + 'templates_set_as_template' => '현재 페이지는 템플릿용 페이지 입니다.', 'templates_explain_set_as_template' => '템플릿은 보기 권한만 있어도 문서에 쓸 수 있습니다.', 'templates_replace_content' => '문서 대체', 'templates_append_content' => '문서 앞에 추가', @@ -367,8 +397,11 @@ 'comment' => '댓글', 'comments' => '댓글', 'comment_add' => '댓글 쓰기', + 'comment_none' => 'No comments to display', 'comment_placeholder' => '이곳에 댓글을 쓰세요...', - 'comment_count' => '{0} 댓글 없음|{1} 댓글 1개|[2,*] 댓글 :count개', + 'comment_thread_count' => ':count Comment Thread|:count Comment Threads', + 'comment_archived_count' => ':count Archived', + 'comment_archived_threads' => 'Archived Threads', 'comment_save' => '등록', 'comment_new' => '새로운 댓글', 'comment_created' => '댓글 등록함 :createDiff', @@ -377,14 +410,20 @@ '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_delete_confirm' => '이 댓글을 지울 건가요?', 'comment_in_reply_to' => ':commentId(을)를 향한 답글', + 'comment_reference' => 'Reference', + 'comment_reference_outdated' => '(Outdated)', 'comment_editor_explain' => '이 페이지에 남겨진 댓글은 다음과 같습니다. 저장된 페이지를 볼 때 댓글을 추가하고 관리할 수 있습니다.', // Revision 'revision_delete_confirm' => '이 수정본을 지울 건가요?', - 'revision_restore_confirm' => '이 수정본을 되돌릴 건가요? 현재 판본을 바꿉니다.', - 'revision_cannot_delete_latest' => '현재 판본은 지울 수 없습니다.', + 'revision_restore_confirm' => '이 버전을 되돌릴 건가요? 현재 페이지는 대체됩니다.', + 'revision_cannot_delete_latest' => '현재 버전본은 지울 수 없습니다.', // Copy view 'copy_consider' => '항목을 복사할 때 다음을 고려하세요.', @@ -412,10 +451,10 @@ 'references_to_desc' => '이 항목으로 연결되는 시스템에서 알려진 모든 콘텐츠가 아래에 나열되어 있습니다.', // Watch Options - 'watch' => '주시', + 'watch' => '변경 알림 설정', 'watch_title_default' => '기본 설정', - 'watch_desc_default' => '보기를 기본 알림 환경설정으로 되돌릴 수 있습니다.', - 'watch_title_ignore' => '무시', + 'watch_desc_default' => '알림 설정을 기본값으로 되돌리기', + 'watch_title_ignore' => '알림 설정 끄기', 'watch_desc_ignore' => '사용자 수준 환경설정의 알림을 포함한 모든 알림을 무시합니다.', 'watch_title_new' => '새로운 페이지', 'watch_desc_new' => '이 항목에 새 페이지가 생성되면 알림을 받습니다.', @@ -431,7 +470,7 @@ 'watch_detail_updates' => '새 페이지 및 업데이트 보기', 'watch_detail_comments' => '새 페이지, 업데이트 및 댓글 보기', 'watch_detail_parent_book' => '상위 책을 통해 보기', - 'watch_detail_parent_book_ignore' => '페어런트 북을 통한 무시하기', + 'watch_detail_parent_book_ignore' => '상위 책을 통한 무시하기', 'watch_detail_parent_chapter' => '상위 챕터를 통해 보기', 'watch_detail_parent_chapter_ignore' => '상위 챕터를 통해 무시하기', ]; diff --git a/lang/ko/errors.php b/lang/ko/errors.php index 0ab6789cc0c..9639a503613 100644 --- a/lang/ko/errors.php +++ b/lang/ko/errors.php @@ -10,6 +10,7 @@ // Auth 'error_user_exists_different_creds' => '이메일 :email 이 이미 존재하지만 다른 자격 증명을 가진 사용자입니다.', + 'auth_pre_register_theme_prevention' => '제공된 세부 정보로 사용자 계정을 등록할 수 없습니다', 'email_already_confirmed' => '이메일이 이미 확인되었으니 로그인해 보세요.', 'email_confirmation_invalid' => '이 확인 토큰이 유효하지 않거나 이미 사용되었습니다. 다시 등록해 주세요.', 'email_confirmation_expired' => '확인 토큰이 만료되었습니다. 새 확인 이메일이 전송되었습니다.', @@ -23,7 +24,6 @@ 'saml_invalid_response_id' => '이 애플리케이션에서 시작한 프로세스에서 외부 인증 시스템의 요청을 인식하지 못합니다. 로그인 후 다시 이동하면 이 문제가 발생할 수 있습니다.', 'saml_fail_authed' => ':system 을 사용하여 로그인, 시스템이 성공적인 인증을 제공하지 않음', 'oidc_already_logged_in' => '이미 로그인했습니다.', - 'oidc_user_not_registered' => '사용자 :name 이 등록되지 않았으며 자동 등록이 비활성화되었습니다.', 'oidc_no_email_address' => '외부 인증 시스템에서 제공한 데이터에서 이 사용자의 이메일 주소를 찾을 수 없습니다.', 'oidc_fail_authed' => ':system 을 사용하여 로그인, 시스템이 성공적인 인증을 제공하지 않음', 'social_no_action_defined' => '정의된 동작 없음', @@ -37,6 +37,7 @@ 'social_driver_not_found' => '소셜 드라이버를 찾을 수 없습니다.', 'social_driver_not_configured' => '소셜 계정 :socialAccount 가(이) 올바르게 구성되지 않았습니다.', 'invite_token_expired' => '이 초대 링크가 만료되었습니다. 대신 계정 비밀번호 재설정을 시도해 보세요.', + 'login_user_not_found' => '이 동작의 사용자를 찾을 수 없습니다.', // System 'path_not_writable' => '파일 경로 :filePath 에 업로드할 수 없습니다. 서버에 저장이 가능한지 확인하세요.', @@ -77,6 +78,7 @@ // Users 'users_cannot_delete_only_admin' => 'Admin을 삭제할 수 없습니다.', 'users_cannot_delete_guest' => 'Guest를 삭제할 수 없습니다.', + 'users_could_not_send_invite' => '초대 이메일을 보내는 데 실패하여 사용자를 생성할 수 없습니다.', // Roles 'role_cannot_be_edited' => '권한을 수정할 수 없습니다.', @@ -92,7 +94,7 @@ 'empty_comment' => '빈 댓글은 등록할 수 없습니다.', // Error pages - '404_page_not_found' => '404 Not Found', + '404_page_not_found' => '페이지를 찾을 수 없습니다.', 'sorry_page_not_found' => '문서를 못 찾았습니다.', 'sorry_page_not_found_permission_warning' => '문서를 볼 권한이 없습니다.', 'image_not_found' => '이미지를 찾을 수 없습니다', @@ -103,6 +105,18 @@ 'app_down' => ':appName에 문제가 생겼습니다.', 'back_soon' => '곧 돌아갑니다.', + // Import + 'import_zip_cant_read' => 'ZIP 파일을 읽을 수 없습니다.', + 'import_zip_cant_decode_data' => 'ZIP data.json 콘텐츠를 찾아서 디코딩할 수 없습니다.', + 'import_zip_no_data' => '컨텐츠 ZIP 파일 데이터에 데이터가 비어있습니다.', + 'import_validation_failed' => '컨텐츠 ZIP 파일을 가져오려다 실패했습니다. 이유:', + 'import_zip_failed_notification' => '컨텐츠 ZIP 파일을 가져오지 못했습니다.', + 'import_perms_books' => '책을 만드는 데 필요한 권한이 없습니다.', + 'import_perms_chapters' => '챕터를 만드는 데 필요한 권한이 없습니다.', + 'import_perms_pages' => '페이지를 만드는 데 필요한 권한이 없습니다.', + 'import_perms_images' => '이미지를 만드는 데 필요한 권한이 없습니다.', + 'import_perms_attachments' => '첨부 파일을 만드는 데 필요한 권한이 없습니다.', + // API errors 'api_no_authorization_found' => '요청에서 인증 토큰을 찾을 수 없습니다.', 'api_bad_authorization_format' => '요청에서 인증 토큰을 찾았으나 형식에 문제가 있습니다.', diff --git a/lang/ko/passwords.php b/lang/ko/passwords.php index 064f4d92576..97061631a85 100644 --- a/lang/ko/passwords.php +++ b/lang/ko/passwords.php @@ -6,10 +6,10 @@ */ return [ - 'password' => '여덟 글자를 넘어야 합니다.', + 'password' => '최소 8글자 이상이어야 합니다.', 'user' => "메일 주소를 가진 사용자가 없습니다.", 'token' => '유효하지 않거나 만료된 토큰입니다.', 'sent' => '메일을 보냈습니다.', - 'reset' => '패스워드를 바꿨습니다.', + 'reset' => '패스워드가 초기화되었습니다.', ]; diff --git a/lang/ko/preferences.php b/lang/ko/preferences.php index ccc09e7a33f..79692f564c3 100644 --- a/lang/ko/preferences.php +++ b/lang/ko/preferences.php @@ -26,8 +26,8 @@ 'notifications_opt_comment_replies' => '내 댓글에 대한 답글 알림 받기', 'notifications_save' => '환경설정 저장', 'notifications_update_success' => '알림 환경설정이 업데이트되었습니다!', - 'notifications_watched' => '주시 및 무시한 항목', - 'notifications_watched_desc' => ' 아래는 사용자 지정 시계 환경설정이 적용된 항목입니다. 이러한 항목에 대한 환경설정을 업데이트하려면 해당 항목을 본 다음 사이드바에서 시계 옵션을 찾습니다.', + 'notifications_watched' => '알람 설정 및 알람 무시한 항목', + 'notifications_watched_desc' => '아래는 사용자 지정 알람 환경설정이 적용된 항목입니다. 이러한 항목에 대한 환경설정을 업데이트하려면 해당 항목을 본 다음 사이드바에서 알림 옵션을 찾습니다.', 'auth' => '액세스 및 보안', 'auth_change_password' => '패스워드 변경', diff --git a/lang/ko/settings.php b/lang/ko/settings.php index fe6dd4c7e25..797c12043a1 100644 --- a/lang/ko/settings.php +++ b/lang/ko/settings.php @@ -13,13 +13,13 @@ 'categories' => '카테고리', // App Settings - 'app_customization' => '맞춤', + 'app_customization' => '어플리케이션 설정', 'app_features_security' => '기능 및 보안', 'app_name' => '애플리케이션 이름 (사이트 제목)', 'app_name_desc' => '이 이름은 헤더와 시스템에서 보낸 모든 이메일에 표시됩니다.', 'app_name_header' => '헤더에 이름 표시', 'app_public_access' => '사이트 공개', - 'app_public_access_desc' => '이 옵션을 활성화하면 로그인하지 않은 방문자도 북스택 인스턴스의 콘텐츠에 액세스할 수 있습니다.', + 'app_public_access_desc' => '이 옵션을 활성화하면 로그인하지 않은 방문자도 이 서버의 콘텐츠에 액세스할 수 있습니다.', 'app_public_access_desc_guest' => '일반 방문자의 액세스는 "Guest" 사용자를 통해 제어할 수 있습니다.', 'app_public_access_toggle' => '공개 액세스 허용', 'app_public_viewing' => '공개 열람을 허용할까요?', @@ -63,30 +63,60 @@ // Registration Settings 'reg_settings' => '가입', 'reg_enable' => '가입 활성화', - 'reg_enable_toggle' => '가입 받기', + 'reg_enable_toggle' => '가입 허용', 'reg_enable_desc' => '가입한 사용자는 한 가지 권한을 가집니다.', 'reg_default_role' => '기본 권한', 'reg_enable_external_warning' => '외부 시스템이 LDAP나 SAML 인증이 활성화되어 있다면 설정과 관계없이 인증을 성공할 때 없는 계정을 만듭니다.', 'reg_email_confirmation' => '메일 주소 확인', 'reg_email_confirmation_toggle' => '메일 주소 확인', - 'reg_confirm_email_desc' => '도메인 차단을 활성화하면 설정과 관계없이 메일 주소 확인이 필요합니다.', - 'reg_confirm_restrict_domain' => '도메인 차단', + 'reg_confirm_email_desc' => '도메인 제한을 활성화하면 설정과 관계없이 메일 주소 확인이 필요합니다.', + 'reg_confirm_restrict_domain' => '도메인 제한', 'reg_confirm_restrict_domain_desc' => '가입을 차단할 도메인을 쉼표로 구분하여 입력하세요. 사용자가 메일 주소 확인에 성공하면 메일 주소를 바꿀 수 있습니다.', 'reg_confirm_restrict_domain_placeholder' => '차단한 도메인 없음', + // Sorting Settings + 'sorting' => '정렬', + 'sorting_book_default' => '기본 책 정렬', + 'sorting_book_default_desc' => '새로운 책에 적용할 기본 정렬 규칙을 선택하세요. 이 선택은 기존 책에는 영향을 주지 않고, 기존 책의 설정은 책마다 변경할 수 있습니다.', + 'sorting_rules' => '정렬 규칙', + 'sorting_rules_desc' => '현재 시스템에 미리 정의된 정렬 규칙의 목록입니다.', + 'sort_rule_assigned_to_x_books' => ':count 책에 정렬 규칙 적용', + 'sort_rule_create' => '정렬 규칙 생성하기', + 'sort_rule_edit' => '정렬 규칙 수정하기', + 'sort_rule_delete' => '정렬 규칙 삭제하기', + 'sort_rule_delete_desc' => '시스템에서 이 정렬 규칙을 삭제합니다. 이 정렬 규칙을 사용하는 책들은 수동 정렬 사용으로 변경됩니다.', + 'sort_rule_delete_warn_books' => '이 정렬 규칙은 현재 :count 권의 책에 적용되고 있습니다. 이 정렬 규칙을 정말 삭제하시겠습니까?', + 'sort_rule_delete_warn_default' => '이 정렬 규칙은 현재 책들에 기본으로 적용되고 있습니다. 정말 삭제하시겠습니까?', + 'sort_rule_details' => '정렬 규칙 세부사항', + 'sort_rule_details_desc' => '이 정렬 규칙의 이름을 지어주세요. 이 이름은 사용자가 정렬할 때 나타납니다.', + 'sort_rule_operations' => '정렬 규칙', + 'sort_rule_operations_desc' => '사용 가능한 작업 목록에서 이동하여 수행할 정렬 작업을 구성합니다. 사용 시 위에서 아래로 순서대로 작업이 적용됩니다. 여기에서 변경한 내용은 저장 시 할당된 모든 책에 적용됩니다.', + 'sort_rule_available_operations' => '사용 가능한 정렬 규칙', + 'sort_rule_available_operations_empty' => '사용 가능한 정렬 규칙 없음', + 'sort_rule_configured_operations' => '정렬 규칙 설정', + 'sort_rule_configured_operations_empty' => '"사용 가능한 정렬 규칙"에서 추가하기', + 'sort_rule_op_asc' => '(오름차순)', + 'sort_rule_op_desc' => '(내림차순)', + 'sort_rule_op_name' => '이름순 - 가나다', + 'sort_rule_op_name_numeric' => '이름순 - 숫자', + 'sort_rule_op_created_date' => '생성일', + 'sort_rule_op_updated_date' => '수정일', + 'sort_rule_op_chapters_first' => '챕터 우선 정렬', + 'sort_rule_op_chapters_last' => '챕터 나중 정렬', + // Maintenance settings - 'maint' => '데이터', + 'maint' => '유지관리', 'maint_image_cleanup' => '이미지 정리', - 'maint_image_cleanup_desc' => '중복한 이미지를 찾습니다. 실행하기 전에 이미지를 백업하세요.', + 'maint_image_cleanup_desc' => '중복인 이미지를 찾습니다. 실행하기 전에 이미지를 백업하세요.', 'maint_delete_images_only_in_revisions' => '지난 버전에만 있는 이미지 지우기', 'maint_image_cleanup_run' => '실행', - 'maint_image_cleanup_warning' => '이미지 :count개를 지울 건가요?', + 'maint_image_cleanup_warning' => '이미지 :count개를 지우시겠습니까?', 'maint_image_cleanup_success' => '이미지 :count개 삭제함', 'maint_image_cleanup_nothing_found' => '삭제한 것 없음', 'maint_send_test_email' => '테스트 메일 보내기', 'maint_send_test_email_desc' => '메일 주소로 테스트 메일을 전송합니다.', 'maint_send_test_email_run' => '테스트 메일 보내기', - 'maint_send_test_email_success' => ':address로 보냈습니다.', + 'maint_send_test_email_success' => ':address 계정으로 이메일을 보냈습니다.', 'maint_send_test_email_mail_subject' => '테스트 메일', 'maint_send_test_email_mail_greeting' => '메일을 수신했습니다.', 'maint_send_test_email_mail_text' => '메일을 정상적으로 수신했습니다.', @@ -108,8 +138,8 @@ 'recycle_bin_restore' => '복원', 'recycle_bin_contents_empty' => '휴지통이 비었습니다.', 'recycle_bin_empty' => '비우기', - 'recycle_bin_empty_confirm' => '휴지통을 비울 건가요?', - 'recycle_bin_destroy_confirm' => '아래 자식 항목들이 함께 영구적으로 삭제됩니다. 영구 삭제할 건가요?', + 'recycle_bin_empty_confirm' => '이렇게 하면 각 항목에 포함된 콘텐츠를 포함하여 휴지통에 있는 모든 항목이 영구적으로 삭제됩니다. 휴지통을 비우시겠습니까?', + 'recycle_bin_destroy_confirm' => '이 작업을 수행하면 이 항목이 아래에 나열된 모든 하위 요소와 함께 시스템에서 영구적으로 삭제되며, 복원할 수 없습니다. 이 항목을 영구 삭제하시겠어요?', 'recycle_bin_destroy_list' => '영구 삭제함', 'recycle_bin_restore_list' => '복원함', 'recycle_bin_restore_confirm' => '원래 위치로 복원합니다. 원래 위치의 부모 항목이 지워졌을 경우 부모 항목도 복원해야 합니다.', @@ -119,8 +149,8 @@ 'recycle_bin_restore_notification' => ':count항목 복원함', // Audit Log - 'audit' => '추적 기록', - 'audit_desc' => '시스템에서 추적한 작업입니다. 권한 필터가 작동하지 않습니다.', + 'audit' => '활동 기록', + 'audit_desc' => '이 활동 로그는 시스템에서 추적된 활동 목록을 표시합니다. 이 목록은 권한 필터가 적용되는 시스템의 유사한 활동 목록과 달리 필터링되지 않습니다.', 'audit_event_filter' => '이벤트 필터', 'audit_event_filter_no_filter' => '필터 없음', 'audit_deleted_item' => '삭제한 항목', @@ -134,8 +164,8 @@ 'audit_date_to' => 'To', // Role Settings - 'roles' => '권한', - 'role_user_roles' => '사용자 권한', + 'roles' => '역할', + 'role_user_roles' => '사용자 역할', 'roles_index_desc' => '역할은 사용자를 그룹화하고 구성원에게 시스템 권한을 제공하기 위해 사용됩니다. 사용자가 여러 역할의 구성원인 경우 부여된 권한이 중첩되며 모든 권한을 상속받게 됩니다.', 'roles_x_users_assigned' => ':count 명의 사용자가 할당됨|:count 명의 사용자가 할당됨', 'roles_x_permissions_provided' => ':count 개의 권한|:count 개의 권한', @@ -147,10 +177,10 @@ 'role_delete_users_assigned' => '이 권한을 가진 사용자 :userCount명에 할당할 권한을 고르세요.', 'role_delete_no_migration' => "할당하지 않음", 'role_delete_sure' => '이 권한을 지울 건가요?', - 'role_edit' => '권한 수정', - 'role_details' => '권한 정보', - 'role_name' => '권한 이름', - 'role_desc' => '설명', + 'role_edit' => '역할 수정', + 'role_details' => '역할 정보', + 'role_name' => '역할 이름', + 'role_desc' => '역할 설명', 'role_mfa_enforced' => '다중 인증 필요', 'role_external_auth_id' => '외부 인증 계정', 'role_system' => '시스템 권한', @@ -162,19 +192,20 @@ 'role_access_api' => '시스템 접근 API', 'role_manage_settings' => '사이트 설정 관리', 'role_export_content' => '항목 내보내기', + 'role_import_content' => '내용 가져오기', 'role_editor_change' => '페이지 편집기 변경', 'role_notifications' => '알림 수신 및 관리', 'role_asset' => '권한 항목', 'roles_system_warning' => '위 세 권한은 자신의 권한이나 다른 유저의 권한을 바꿀 수 있습니다.', 'role_asset_desc' => '책, 챕터, 문서별 권한은 이 설정에 우선합니다.', - 'role_asset_admins' => 'Admin 권한은 어디든 접근할 수 있지만 이 설정은 사용자 인터페이스에서 해당 활동을 표시할지 결정합니다.', + 'role_asset_admins' => '관리자 권한은 어디든 접근할 수 있지만 이 설정은 사용자 인터페이스에서 해당 활동을 표시할지 결정합니다.', 'role_asset_image_view_note' => '이는 이미지 관리자 내 가시성과 관련이 있습니다. 업로드된 이미지 파일의 실제 접근은 시스템의 이미지 저장 설정에 따라 달라집니다.', 'role_all' => '모든 항목', 'role_own' => '직접 만든 항목', 'role_controlled_by_asset' => '저마다 다름', 'role_save' => '저장', - 'role_users' => '이 권한을 가진 사용자들', - 'role_users_none' => '그런 사용자가 없습니다.', + 'role_users' => '이 역할을 가진 사용자들', + 'role_users_none' => '역할이 부여된 사용자가 없습니다.', // Users 'users' => '사용자', @@ -187,9 +218,9 @@ 'users_details_desc' => '메일 주소로 로그인합니다.', 'users_details_desc_no_email' => '사용자 이름을 바꿉니다.', 'users_role' => '사용자 권한', - 'users_role_desc' => '고른 권한 모두를 적용합니다.', + 'users_role_desc' => '이 사용자에게 할당될 역할을 선택합니다. 사용자가 여러 역할에 할당된 경우 해당 역할의 권한이 겹쳐서 적용됩니다. 할당된 역할의 모든 기능을 받게 됩니다.', 'users_password' => '사용자 패스워드', - 'users_password_desc' => '패스워드는 여덟 글자를 넘어야 합니다.', + 'users_password_desc' => '패스워드는 8 글자를 넘어야 합니다.', 'users_send_invite_text' => '패스워드 설정을 권유하는 메일을 보내거나 내가 정할 수 있습니다.', 'users_send_invite_option' => '메일 보내기', 'users_external_auth_id' => '외부 인증 계정', @@ -199,7 +230,7 @@ 'users_delete' => '사용자 삭제', 'users_delete_named' => ':userName 삭제', 'users_delete_warning' => ':userName에 관한 데이터를 지웁니다.', - 'users_delete_confirm' => '이 사용자를 지울 건가요?', + 'users_delete_confirm' => '이 사용자를 삭제하시겠습니까?', 'users_migrate_ownership' => '소유자 바꾸기', 'users_migrate_ownership_desc' => '선택한 사용자가 소유하고 있는 모든 항목을 다른 유저가 소유하게 합니다.', 'users_none_selected' => '선택한 유저 없음', @@ -214,8 +245,8 @@ 'users_social_accounts_info' => '다른 계정으로 간단하게 로그인하세요. 여기에서 계정 연결을 끊는 것과 소셜 계정에서 접근 권한을 취소하는 것은 다릅니다.', 'users_social_connect' => '계정 연결', 'users_social_disconnect' => '계정 연결 끊기', - 'users_social_status_connected' => '연결됨', - 'users_social_status_disconnected' => '연결 해제됨', + 'users_social_status_connected' => '연결되었습니다.', + 'users_social_status_disconnected' => '연결 해제되었습니다.', 'users_social_connected' => ':socialAccount(와)과 연결했습니다.', 'users_social_disconnected' => ':socialAccount(와)과의 연결을 끊었습니다.', 'users_api_tokens' => 'API 토큰', @@ -245,7 +276,7 @@ 'user_api_token_updated' => ':timeAgo 전에 토큰 갱신함', 'user_api_token_delete' => '토큰 삭제', 'user_api_token_delete_warning' => '\':tokenName\'을 시스템에서 삭제합니다.', - 'user_api_token_delete_confirm' => '이 API 토큰을 지울 건가요?', + 'user_api_token_delete_confirm' => '이 API 토큰을 삭제하시겠습니까?', // Webhooks 'webhooks' => '웹 훅', @@ -276,6 +307,14 @@ 'webhooks_last_errored' => '마지막 에러:', 'webhooks_last_error_message' => '마지막 에러 메시지:', + // Licensing + 'licenses' => '라이선스', + 'licenses_desc' => '이 페이지에서는 BookStack 내에서 사용되는 프로젝트 및 라이브러리 외에 BookStack 라이선스 정보를 자세히 설명합니다. 나열된 프로젝트는 개발 용도로만 사용할 수 있습니다.', + 'licenses_bookstack' => 'BookStack 라이선스', + 'licenses_php' => 'PHP 라이브러리 라이선스', + 'licenses_js' => 'JavaScript 라이브러리 라이선스', + 'licenses_other' => '기타 라이선스', + 'license_details' => '라이선스 세부 사항', //! If editing translations files directly please ignore this in all //! languages apart from en. Content will be auto-copied from en. @@ -287,6 +326,7 @@ 'bs' => 'Bosanski', 'ca' => 'Català', 'cs' => 'Česky', + 'cy' => 'Cymraeg', 'da' => 'Dansk', 'de' => 'Deutsch (Sie)', 'de_informal' => 'Deutsch (Du)', @@ -308,6 +348,7 @@ 'lt' => 'Lietuvių Kalba', 'lv' => 'Latviešu Valoda', 'nb' => 'Norsk (Bokmål)', + 'ne' => 'नेपाली', 'nn' => 'Nynorsk', 'nl' => 'Nederlands', 'pl' => 'Polski', diff --git a/lang/ko/validation.php b/lang/ko/validation.php index e533429ee1a..ef7361ff9ac 100644 --- a/lang/ko/validation.php +++ b/lang/ko/validation.php @@ -105,6 +105,11 @@ 'url' => ':attribute(은)는 유효하지 않은 형식입니다.', 'uploaded' => '파일 크기가 서버에서 허용하는 수치를 넘습니다.', + 'zip_file' => ':attribute은(는) 컨텐츠 ZIP 파일 내의 객체 유형에 대해 고유해야 합니다.', + 'zip_file_mime' => ':attribute은(는) :validTypes, found :foundType 유형의 파일을 참조해야 합니다.', + 'zip_model_expected' => '데이터 객체가 필요하지만 ":type" 타입이 발견되었습니다.', + 'zip_unique' => ':attribute은(는) 컨텐츠 ZIP 파일 내의 객체 유형에 대해 고유해야 합니다.', + // Custom validation lines 'custom' => [ 'password-confirm' => [ diff --git a/lang/ku/activities.php b/lang/ku/activities.php new file mode 100644 index 00000000000..4362fc02958 --- /dev/null +++ b/lang/ku/activities.php @@ -0,0 +1,140 @@ + 'created page', + 'page_create_notification' => 'Page successfully created', + 'page_update' => 'updated page', + 'page_update_notification' => 'Page successfully updated', + 'page_delete' => 'deleted page', + 'page_delete_notification' => 'Page successfully deleted', + 'page_restore' => 'restored page', + 'page_restore_notification' => 'Page successfully restored', + 'page_move' => 'moved page', + 'page_move_notification' => 'Page successfully moved', + + // Chapters + 'chapter_create' => 'created chapter', + 'chapter_create_notification' => 'Chapter successfully created', + 'chapter_update' => 'updated chapter', + 'chapter_update_notification' => 'Chapter successfully updated', + 'chapter_delete' => 'deleted chapter', + 'chapter_delete_notification' => 'Chapter successfully deleted', + 'chapter_move' => 'moved chapter', + 'chapter_move_notification' => 'Chapter successfully moved', + + // Books + 'book_create' => 'created book', + 'book_create_notification' => 'Book successfully created', + 'book_create_from_chapter' => 'converted chapter to book', + 'book_create_from_chapter_notification' => 'Chapter successfully converted to a book', + 'book_update' => 'updated book', + 'book_update_notification' => 'Book successfully updated', + 'book_delete' => 'deleted book', + 'book_delete_notification' => 'Book successfully deleted', + 'book_sort' => 'sorted book', + 'book_sort_notification' => 'Book successfully re-sorted', + + // Bookshelves + 'bookshelf_create' => 'created shelf', + 'bookshelf_create_notification' => 'Shelf successfully created', + 'bookshelf_create_from_book' => 'converted book to shelf', + 'bookshelf_create_from_book_notification' => 'Book successfully converted to a shelf', + 'bookshelf_update' => 'updated shelf', + 'bookshelf_update_notification' => 'Shelf successfully updated', + 'bookshelf_delete' => 'deleted shelf', + 'bookshelf_delete_notification' => 'Shelf successfully deleted', + + // Revisions + 'revision_restore' => 'restored revision', + 'revision_delete' => 'deleted revision', + 'revision_delete_notification' => 'Revision successfully deleted', + + // Favourites + 'favourite_add_notification' => '":name" has been added to your favourites', + 'favourite_remove_notification' => '":name" has been removed from your favourites', + + // Watching + 'watch_update_level_notification' => 'Watch preferences successfully updated', + + // Auth + 'auth_login' => 'logged in', + 'auth_register' => 'registered as new user', + 'auth_password_reset_request' => 'requested user password reset', + 'auth_password_reset_update' => 'reset user password', + 'mfa_setup_method' => 'configured MFA method', + 'mfa_setup_method_notification' => 'Multi-factor method successfully configured', + 'mfa_remove_method' => 'removed MFA method', + 'mfa_remove_method_notification' => 'Multi-factor method successfully removed', + + // Settings + 'settings_update' => 'updated settings', + 'settings_update_notification' => 'Settings successfully updated', + 'maintenance_action_run' => 'ran maintenance action', + + // Webhooks + 'webhook_create' => 'created webhook', + 'webhook_create_notification' => 'Webhook successfully created', + 'webhook_update' => 'updated webhook', + 'webhook_update_notification' => 'Webhook successfully updated', + 'webhook_delete' => 'deleted webhook', + 'webhook_delete_notification' => 'Webhook successfully deleted', + + // Imports + 'import_create' => 'created import', + 'import_create_notification' => 'Import successfully uploaded', + 'import_run' => 'updated import', + 'import_run_notification' => 'Content successfully imported', + 'import_delete' => 'deleted import', + 'import_delete_notification' => 'Import successfully deleted', + + // Users + 'user_create' => 'created user', + 'user_create_notification' => 'User successfully created', + 'user_update' => 'updated user', + 'user_update_notification' => 'User successfully updated', + 'user_delete' => 'deleted user', + 'user_delete_notification' => 'User successfully removed', + + // API Tokens + 'api_token_create' => 'created API token', + 'api_token_create_notification' => 'API token successfully created', + 'api_token_update' => 'updated API token', + 'api_token_update_notification' => 'API token successfully updated', + 'api_token_delete' => 'deleted API token', + 'api_token_delete_notification' => 'API token successfully deleted', + + // Roles + 'role_create' => 'created role', + 'role_create_notification' => 'Role successfully created', + 'role_update' => 'updated role', + 'role_update_notification' => 'Role successfully updated', + 'role_delete' => 'deleted role', + 'role_delete_notification' => 'Role successfully deleted', + + // Recycle Bin + 'recycle_bin_empty' => 'emptied recycle bin', + 'recycle_bin_restore' => 'restored from recycle bin', + 'recycle_bin_destroy' => 'removed from recycle bin', + + // Comments + 'commented_on' => 'commented on', + 'comment_create' => 'added comment', + 'comment_update' => 'updated comment', + 'comment_delete' => 'deleted comment', + + // Sort Rules + 'sort_rule_create' => 'created sort rule', + 'sort_rule_create_notification' => 'Sort rule successfully created', + 'sort_rule_update' => 'updated sort rule', + 'sort_rule_update_notification' => 'Sort rule successfully updated', + 'sort_rule_delete' => 'deleted sort rule', + 'sort_rule_delete_notification' => 'Sort rule successfully deleted', + + // Other + 'permissions_update' => 'updated permissions', +]; diff --git a/lang/ku/auth.php b/lang/ku/auth.php new file mode 100644 index 00000000000..369e06f93ed --- /dev/null +++ b/lang/ku/auth.php @@ -0,0 +1,117 @@ + 'ئەم بەکارهێنەرە نەدۆزرایەوە.', + 'throttle' => 'Too many login attempts. Please try again in :seconds seconds.', + + // Login & Register + 'sign_up' => 'Sign up', + 'log_in' => 'Log in', + 'log_in_with' => 'Login with :socialDriver', + 'sign_up_with' => 'Sign up with :socialDriver', + 'logout' => 'Logout', + + 'name' => 'Name', + 'username' => 'Username', + 'email' => 'Email', + 'password' => 'Password', + 'password_confirm' => 'Confirm Password', + 'password_hint' => 'Must be at least 8 characters', + 'forgot_password' => 'Forgot Password?', + 'remember_me' => 'Remember Me', + 'ldap_email_hint' => 'Please enter an email to use for this account.', + 'create_account' => 'Create Account', + 'already_have_account' => 'Already have an account?', + 'dont_have_account' => 'Don\'t have an account?', + 'social_login' => 'Social Login', + 'social_registration' => 'Social Registration', + 'social_registration_text' => 'Register and sign in using another service.', + + 'register_thanks' => 'Thanks for registering!', + 'register_confirm' => 'Please check your email and click the confirmation button to access :appName.', + 'registrations_disabled' => 'Registrations are currently disabled', + 'registration_email_domain_invalid' => 'That email domain does not have access to this application', + 'register_success' => 'Thanks for signing up! You are now registered and signed in.', + + // Login auto-initiation + 'auto_init_starting' => 'Attempting Login', + 'auto_init_starting_desc' => 'We\'re contacting your authentication system to start the login process. If there\'s no progress after 5 seconds you can try clicking the link below.', + 'auto_init_start_link' => 'Proceed with authentication', + + // Password Reset + 'reset_password' => 'Reset Password', + 'reset_password_send_instructions' => 'Enter your email below and you will be sent an email with a password reset link.', + 'reset_password_send_button' => 'Send Reset Link', + 'reset_password_sent' => 'A password reset link will be sent to :email if that email address is found in the system.', + 'reset_password_success' => 'Your password has been successfully reset.', + 'email_reset_subject' => 'Reset your :appName password', + 'email_reset_text' => 'You are receiving this email because we received a password reset request for your account.', + 'email_reset_not_requested' => 'If you did not request a password reset, no further action is required.', + + // Email Confirmation + 'email_confirm_subject' => 'Confirm your email on :appName', + 'email_confirm_greeting' => 'Thanks for joining :appName!', + 'email_confirm_text' => 'Please confirm your email address by clicking the button below:', + 'email_confirm_action' => 'ئیمێڵەکەت دووبارە بنووسەوە', + 'email_confirm_send_error' => 'Email confirmation required but the system could not send the email. Contact the admin to ensure email is set up correctly.', + 'email_confirm_success' => 'Your email has been confirmed! You should now be able to login using this email address.', + 'email_confirm_resent' => 'Confirmation email resent, Please check your inbox.', + 'email_confirm_thanks' => 'Thanks for confirming!', + 'email_confirm_thanks_desc' => 'Please wait a moment while your confirmation is handled. If you are not redirected after 3 seconds press the "Continue" link below to proceed.', + + 'email_not_confirmed' => 'Email Address Not Confirmed', + 'email_not_confirmed_text' => 'Your email address has not yet been confirmed.', + 'email_not_confirmed_click_link' => 'Please click the link in the email that was sent shortly after you registered.', + 'email_not_confirmed_resend' => 'If you cannot find the email you can re-send the confirmation email by submitting the form below.', + 'email_not_confirmed_resend_button' => 'Resend Confirmation Email', + + // User Invite + 'user_invite_email_subject' => 'You have been invited to join :appName!', + 'user_invite_email_greeting' => 'An account has been created for you on :appName.', + 'user_invite_email_text' => 'Click the button below to set an account password and gain access:', + 'user_invite_email_action' => 'Set Account Password', + 'user_invite_page_welcome' => 'Welcome to :appName!', + 'user_invite_page_text' => 'To finalise your account and gain access you need to set a password which will be used to log-in to :appName on future visits.', + 'user_invite_page_confirm_button' => 'Confirm Password', + 'user_invite_success_login' => 'Password set, you should now be able to login using your set password to access :appName!', + + // Multi-factor Authentication + 'mfa_setup' => 'Setup Multi-Factor Authentication', + 'mfa_setup_desc' => 'Setup multi-factor authentication as an extra layer of security for your user account.', + 'mfa_setup_configured' => 'Already configured', + 'mfa_setup_reconfigure' => 'Reconfigure', + 'mfa_setup_remove_confirmation' => 'Are you sure you want to remove this multi-factor authentication method?', + 'mfa_setup_action' => 'Setup', + 'mfa_backup_codes_usage_limit_warning' => 'You have less than 5 backup codes remaining, Please generate and store a new set before you run out of codes to prevent being locked out of your account.', + 'mfa_option_totp_title' => 'Mobile App', + 'mfa_option_totp_desc' => 'To use multi-factor authentication you\'ll need a mobile application that supports TOTP such as Google Authenticator, Authy or Microsoft Authenticator.', + 'mfa_option_backup_codes_title' => 'Backup Codes', + 'mfa_option_backup_codes_desc' => 'Generates a set of one-time-use backup codes which you\'ll enter on login to verify your identity. Make sure to store these in a safe & secure place.', + 'mfa_gen_confirm_and_enable' => 'Confirm and Enable', + 'mfa_gen_backup_codes_title' => 'Backup Codes Setup', + 'mfa_gen_backup_codes_desc' => 'Store the below list of codes in a safe place. When accessing the system you\'ll be able to use one of the codes as a second authentication mechanism.', + 'mfa_gen_backup_codes_download' => 'Download Codes', + 'mfa_gen_backup_codes_usage_warning' => 'Each code can only be used once', + 'mfa_gen_totp_title' => 'Mobile App Setup', + 'mfa_gen_totp_desc' => 'To use multi-factor authentication you\'ll need a mobile application that supports TOTP such as Google Authenticator, Authy or Microsoft Authenticator.', + 'mfa_gen_totp_scan' => 'Scan the QR code below using your preferred authentication app to get started.', + 'mfa_gen_totp_verify_setup' => 'Verify Setup', + 'mfa_gen_totp_verify_setup_desc' => 'Verify that all is working by entering a code, generated within your authentication app, in the input box below:', + 'mfa_gen_totp_provide_code_here' => 'Provide your app generated code here', + 'mfa_verify_access' => 'Verify Access', + 'mfa_verify_access_desc' => 'Your user account requires you to confirm your identity via an additional level of verification before you\'re granted access. Verify using one of your configured methods to continue.', + 'mfa_verify_no_methods' => 'No Methods Configured', + 'mfa_verify_no_methods_desc' => 'No multi-factor authentication methods could be found for your account. You\'ll need to set up at least one method before you gain access.', + 'mfa_verify_use_totp' => 'Verify using a mobile app', + 'mfa_verify_use_backup_codes' => 'Verify using a backup code', + 'mfa_verify_backup_code' => 'Backup Code', + 'mfa_verify_backup_code_desc' => 'Enter one of your remaining backup codes below:', + 'mfa_verify_backup_code_enter_here' => 'Enter backup code here', + 'mfa_verify_totp_desc' => 'Enter the code, generated using your mobile app, below:', + 'mfa_setup_login_notification' => 'Multi-factor method configured, Please now login again using the configured method.', +]; diff --git a/lang/ku/common.php b/lang/ku/common.php new file mode 100644 index 00000000000..8d799d028bc --- /dev/null +++ b/lang/ku/common.php @@ -0,0 +1,115 @@ + 'لابردن', + 'close' => 'داخستن', + 'confirm' => 'دڵنیام', + 'back' => 'گەڕانەوە', + 'save' => 'پاشەکەوتکردن', + 'continue' => 'بەردەوامبە', + 'select' => 'هەڵبژێرە', + 'toggle_all' => 'Toggle All', + 'more' => 'زیاتر', + + // Form Labels + 'name' => 'ناو', + 'description' => 'وردەکاری', + 'role' => 'توانا', + 'cover_image' => 'وێنەی کەڤەر', + 'cover_image_description' => 'This image should be approximately 440x250px although it will be flexibly scaled & cropped to fit the user interface in different scenarios as required, so actual dimensions for display will differ.', + + // Actions + 'actions' => 'کردارەکان', + 'view' => 'بینین', + 'view_all' => 'بینینی هەموو', + 'new' => 'نوێ', + 'create' => 'دروستکردن', + 'update' => 'نوێکردنەوە', + 'edit' => 'دەسکاریکردن', + 'archive' => 'Archive', + 'unarchive' => 'Un-Archive', + 'sort' => 'ڕیزکردن', + 'move' => 'جوڵاندن', + 'copy' => 'لەبەرگرتنەوە', + 'reply' => 'وەڵامدانەوە', + 'delete' => 'سڕینەوە', + 'delete_confirm' => 'دڵنیام لە سڕینەوە', + 'search' => 'گەڕان', + 'search_clear' => 'پاککردنه‌وه‌ی گه‌ڕان', + 'reset' => 'ڕێکخستنەوە', + 'remove' => 'لابردن', + 'add' => 'زیادکردن', + 'configure' => 'شێوەپێدان', + 'manage' => 'بەڕیوەبردن', + 'fullscreen' => 'پڕ شاشە', + 'favourite' => 'ئارەزووەکان', + 'unfavourite' => 'Unfavourite', + 'next' => 'دواتر', + 'previous' => 'پێشتر', + 'filter_active' => 'Active Filter:', + 'filter_clear' => 'Clear Filter', + 'download' => 'دابەزاندن', + 'open_in_tab' => 'Open in Tab', + 'open' => 'کردنه‌وه‌', + + // Sort Options + 'sort_options' => 'Sort Options', + 'sort_direction_toggle' => 'Sort Direction Toggle', + 'sort_ascending' => 'Sort Ascending', + 'sort_descending' => 'Sort Descending', + 'sort_name' => 'ناو', + 'sort_default' => 'Default', + 'sort_created_at' => 'Created Date', + 'sort_updated_at' => 'Updated Date', + + // Misc + 'deleted_user' => 'Deleted User', + 'no_activity' => 'No activity to show', + 'no_items' => 'No items available', + 'back_to_top' => 'گەڕانەوە بۆ سەرەوە', + 'skip_to_main_content' => 'Skip to main content', + 'toggle_details' => 'Toggle Details', + 'toggle_thumbnails' => 'Toggle Thumbnails', + 'details' => 'وردەکاریەکان', + 'grid_view' => 'Grid View', + 'list_view' => 'List View', + 'default' => 'Default', + 'breadcrumb' => 'Breadcrumb', + 'status' => 'Status', + 'status_active' => 'Active', + 'status_inactive' => 'Inactive', + 'never' => 'Never', + 'none' => 'هیچ', + + // Header + 'homepage' => 'پەرەی سەرەكی', + 'header_menu_expand' => 'Expand Header Menu', + 'profile_menu' => 'Profile Menu', + 'view_profile' => 'View Profile', + 'edit_profile' => 'ڕێکخستنی دۆسیەی تایبەت', + 'dark_mode' => 'جۆری ڕەش', + 'light_mode' => 'دۆخی ڕووناک', + 'global_search' => 'Global Search', + + // Layout tabs + 'tab_info' => 'Info', + 'tab_info_label' => 'Tab: Show Secondary Information', + 'tab_content' => 'ناوەڕۆک', + 'tab_content_label' => 'Tab: Show Primary Content', + + // Email Content + 'email_action_help' => 'If you’re having trouble clicking the ":actionText" button, copy and paste the URL below into your web browser:', + 'email_rights' => 'All rights reserved', + + // Footer Link Options + // Not directly used but available for convenience to users. + 'privacy_policy' => 'ڕێبازی مافی کەسیی', + 'terms_of_service' => 'مەرجەکانی خزمەت', + + // OpenSearch + 'opensearch_description' => 'Search :appName', +]; diff --git a/lang/ku/components.php b/lang/ku/components.php new file mode 100644 index 00000000000..c33b1d0b791 --- /dev/null +++ b/lang/ku/components.php @@ -0,0 +1,46 @@ + 'Image Select', + 'image_list' => 'Image List', + 'image_details' => 'Image Details', + 'image_upload' => 'Upload Image', + 'image_intro' => 'Here you can select and manage images that have been previously uploaded to the system.', + 'image_intro_upload' => 'Upload a new image by dragging an image file into this window, or by using the "Upload Image" button above.', + 'image_all' => 'All', + 'image_all_title' => 'View all images', + 'image_book_title' => 'View images uploaded to this book', + 'image_page_title' => 'View images uploaded to this page', + 'image_search_hint' => 'Search by image name', + 'image_uploaded' => 'Uploaded :uploadedDate', + 'image_uploaded_by' => 'Uploaded by :userName', + 'image_uploaded_to' => 'Uploaded to :pageLink', + 'image_updated' => 'Updated :updateDate', + 'image_load_more' => 'Load More', + 'image_image_name' => 'Image Name', + 'image_delete_used' => 'This image is used in the pages below.', + 'image_delete_confirm_text' => 'Are you sure you want to delete this image?', + 'image_select_image' => 'Select Image', + 'image_dropzone' => 'Drop images or click here to upload', + 'image_dropzone_drop' => 'Drop images here to upload', + 'images_deleted' => 'Images Deleted', + 'image_preview' => 'Image Preview', + 'image_upload_success' => 'Image uploaded successfully', + 'image_update_success' => 'Image details successfully updated', + 'image_delete_success' => 'Image successfully deleted', + 'image_replace' => 'Replace Image', + 'image_replace_success' => 'Image file successfully updated', + 'image_rebuild_thumbs' => 'Regenerate Size Variations', + 'image_rebuild_thumbs_success' => 'Image size variations successfully rebuilt!', + + // Code Editor + 'code_editor' => 'Edit Code', + 'code_language' => 'Code Language', + 'code_content' => 'Code Content', + 'code_session_history' => 'Session History', + 'code_save' => 'Save Code', +]; diff --git a/lang/ku/editor.php b/lang/ku/editor.php new file mode 100644 index 00000000000..0d250e9a7bd --- /dev/null +++ b/lang/ku/editor.php @@ -0,0 +1,182 @@ + 'General', + 'advanced' => 'Advanced', + 'none' => 'None', + 'cancel' => 'Cancel', + 'save' => 'Save', + 'close' => 'Close', + 'apply' => 'Apply', + 'undo' => 'Undo', + 'redo' => 'Redo', + 'left' => 'Left', + 'center' => 'Center', + 'right' => 'Right', + 'top' => 'Top', + 'middle' => 'Middle', + 'bottom' => 'Bottom', + 'width' => 'Width', + 'height' => 'Height', + 'More' => 'More', + 'select' => 'Select...', + + // Toolbar + 'formats' => 'Formats', + 'header_large' => 'Large Header', + 'header_medium' => 'Medium Header', + 'header_small' => 'Small Header', + 'header_tiny' => 'Tiny Header', + 'paragraph' => 'Paragraph', + 'blockquote' => 'Blockquote', + 'inline_code' => 'Inline code', + 'callouts' => 'Callouts', + 'callout_information' => 'Information', + 'callout_success' => 'Success', + 'callout_warning' => 'Warning', + 'callout_danger' => 'Danger', + 'bold' => 'Bold', + 'italic' => 'Italic', + 'underline' => 'Underline', + 'strikethrough' => 'Strikethrough', + 'superscript' => 'Superscript', + 'subscript' => 'Subscript', + 'text_color' => 'Text color', + 'highlight_color' => 'Highlight color', + 'custom_color' => 'Custom color', + 'remove_color' => 'Remove color', + 'background_color' => 'Background color', + 'align_left' => 'Align left', + 'align_center' => 'Align center', + 'align_right' => 'Align right', + 'align_justify' => 'Justify', + 'list_bullet' => 'Bullet list', + 'list_numbered' => 'Numbered list', + 'list_task' => 'Task list', + 'indent_increase' => 'Increase indent', + 'indent_decrease' => 'Decrease indent', + 'table' => 'Table', + 'insert_image' => 'Insert image', + 'insert_image_title' => 'Insert/Edit Image', + 'insert_link' => 'Insert/edit link', + 'insert_link_title' => 'Insert/Edit Link', + 'insert_horizontal_line' => 'Insert horizontal line', + 'insert_code_block' => 'Insert code block', + 'edit_code_block' => 'Edit code block', + 'insert_drawing' => 'Insert/edit drawing', + 'drawing_manager' => 'Drawing manager', + 'insert_media' => 'Insert/edit media', + 'insert_media_title' => 'Insert/Edit Media', + 'clear_formatting' => 'Clear formatting', + 'source_code' => 'Source code', + 'source_code_title' => 'Source Code', + 'fullscreen' => 'Fullscreen', + 'image_options' => 'Image options', + + // Tables + 'table_properties' => 'Table properties', + 'table_properties_title' => 'Table Properties', + 'delete_table' => 'Delete table', + 'table_clear_formatting' => 'Clear table formatting', + 'resize_to_contents' => 'Resize to contents', + 'row_header' => 'Row header', + 'insert_row_before' => 'Insert row before', + 'insert_row_after' => 'Insert row after', + 'delete_row' => 'Delete row', + 'insert_column_before' => 'Insert column before', + 'insert_column_after' => 'Insert column after', + 'delete_column' => 'Delete column', + 'table_cell' => 'Cell', + 'table_row' => 'Row', + 'table_column' => 'Column', + 'cell_properties' => 'Cell properties', + 'cell_properties_title' => 'Cell Properties', + 'cell_type' => 'Cell type', + 'cell_type_cell' => 'Cell', + 'cell_scope' => 'Scope', + 'cell_type_header' => 'Header cell', + 'merge_cells' => 'Merge cells', + 'split_cell' => 'Split cell', + 'table_row_group' => 'Row Group', + 'table_column_group' => 'Column Group', + 'horizontal_align' => 'Horizontal align', + 'vertical_align' => 'Vertical align', + 'border_width' => 'Border width', + 'border_style' => 'Border style', + 'border_color' => 'Border color', + 'row_properties' => 'Row properties', + 'row_properties_title' => 'Row Properties', + 'cut_row' => 'Cut row', + 'copy_row' => 'Copy row', + 'paste_row_before' => 'Paste row before', + 'paste_row_after' => 'Paste row after', + 'row_type' => 'Row type', + 'row_type_header' => 'Header', + 'row_type_body' => 'Body', + 'row_type_footer' => 'Footer', + 'alignment' => 'Alignment', + 'cut_column' => 'Cut column', + 'copy_column' => 'Copy column', + 'paste_column_before' => 'Paste column before', + 'paste_column_after' => 'Paste column after', + 'cell_padding' => 'Cell padding', + 'cell_spacing' => 'Cell spacing', + 'caption' => 'Caption', + 'show_caption' => 'Show caption', + 'constrain' => 'Constrain proportions', + 'cell_border_solid' => 'Solid', + 'cell_border_dotted' => 'Dotted', + 'cell_border_dashed' => 'Dashed', + 'cell_border_double' => 'Double', + 'cell_border_groove' => 'Groove', + 'cell_border_ridge' => 'Ridge', + 'cell_border_inset' => 'Inset', + 'cell_border_outset' => 'Outset', + 'cell_border_none' => 'None', + 'cell_border_hidden' => 'Hidden', + + // Images, links, details/summary & embed + 'source' => 'Source', + 'alt_desc' => 'Alternative description', + 'embed' => 'Embed', + 'paste_embed' => 'Paste your embed code below:', + 'url' => 'URL', + 'text_to_display' => 'Text to display', + 'title' => 'Title', + 'browse_links' => 'Browse links', + 'open_link' => 'Open link', + 'open_link_in' => 'Open link in...', + 'open_link_current' => 'Current window', + 'open_link_new' => 'New window', + 'remove_link' => 'Remove link', + 'insert_collapsible' => 'Insert collapsible block', + 'collapsible_unwrap' => 'Unwrap', + 'edit_label' => 'Edit label', + 'toggle_open_closed' => 'Toggle open/closed', + 'collapsible_edit' => 'Edit collapsible block', + 'toggle_label' => 'Toggle label', + + // About view + 'about' => 'About the editor', + 'about_title' => 'About the WYSIWYG Editor', + 'editor_license' => 'Editor License & Copyright', + 'editor_lexical_license' => 'This editor is built as a fork of :lexicalLink which is distributed under the MIT license.', + 'editor_lexical_license_link' => 'Full license details can be found here.', + 'editor_tiny_license' => 'This editor is built using :tinyLink which is provided under the MIT license.', + 'editor_tiny_license_link' => 'The copyright and license details of TinyMCE can be found here.', + 'save_continue' => 'Save Page & Continue', + 'callouts_cycle' => '(Keep pressing to toggle through types)', + 'link_selector' => 'Link to content', + 'shortcuts' => 'Shortcuts', + 'shortcut' => 'Shortcut', + 'shortcuts_intro' => 'The following shortcuts are available in the editor:', + 'windows_linux' => '(Windows/Linux)', + 'mac' => '(Mac)', + 'description' => 'Description', +]; diff --git a/lang/ku/entities.php b/lang/ku/entities.php new file mode 100644 index 00000000000..74c50be3b2f --- /dev/null +++ b/lang/ku/entities.php @@ -0,0 +1,476 @@ + 'Recently Created', + 'recently_created_pages' => 'Recently Created Pages', + 'recently_updated_pages' => 'Recently Updated Pages', + 'recently_created_chapters' => 'Recently Created Chapters', + 'recently_created_books' => 'Recently Created Books', + 'recently_created_shelves' => 'Recently Created Shelves', + 'recently_update' => 'Recently Updated', + 'recently_viewed' => 'Recently Viewed', + 'recent_activity' => 'Recent Activity', + 'create_now' => 'Create one now', + 'revisions' => 'Revisions', + 'meta_revision' => 'Revision #:revisionCount', + 'meta_created' => 'Created :timeLength', + 'meta_created_name' => 'Created :timeLength by :user', + 'meta_updated' => 'Updated :timeLength', + 'meta_updated_name' => 'Updated :timeLength by :user', + 'meta_owned_name' => 'Owned by :user', + 'meta_reference_count' => 'Referenced by :count item|Referenced by :count items', + 'entity_select' => 'Entity Select', + 'entity_select_lack_permission' => 'You don\'t have the required permissions to select this item', + 'images' => 'Images', + 'my_recent_drafts' => 'My Recent Drafts', + 'my_recently_viewed' => 'My Recently Viewed', + 'my_most_viewed_favourites' => 'My Most Viewed Favourites', + 'my_favourites' => 'My Favourites', + 'no_pages_viewed' => 'You have not viewed any pages', + 'no_pages_recently_created' => 'No pages have been recently created', + 'no_pages_recently_updated' => 'No pages have been recently updated', + 'export' => 'Export', + 'export_html' => 'Contained Web File', + 'export_pdf' => 'PDF File', + 'export_text' => 'Plain Text File', + 'export_md' => 'Markdown File', + 'export_zip' => 'Portable ZIP', + 'default_template' => 'Default Page Template', + 'default_template_explain' => 'Assign a page template that will be used as the default content for all pages created within this item. Keep in mind this will only be used if the page creator has view access to the chosen template page.', + 'default_template_select' => 'Select a template page', + 'import' => 'Import', + 'import_validate' => 'Validate Import', + 'import_desc' => 'Import books, chapters & pages using a portable zip export from the same, or a different, instance. Select a ZIP file to proceed. After the file has been uploaded and validated you\'ll be able to configure & confirm the import in the next view.', + '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_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_size' => ':size Import ZIP Size', + 'import_uploaded_at' => 'Uploaded :relativeTime', + 'import_uploaded_by' => 'Uploaded by', + 'import_location' => 'Import Location', + '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_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', + 'breadcrumb_siblings_for_book' => 'Navigate siblings for book', + 'breadcrumb_siblings_for_bookshelf' => 'Navigate siblings for shelf', + + // Permissions and restrictions + 'permissions' => 'Permissions', + 'permissions_desc' => 'Set permissions here to override the default permissions provided by user roles.', + 'permissions_book_cascade' => 'Permissions set on books will automatically cascade to child chapters and pages, unless they have their own permissions defined.', + 'permissions_chapter_cascade' => 'Permissions set on chapters will automatically cascade to child pages, unless they have their own permissions defined.', + 'permissions_save' => 'Save Permissions', + 'permissions_owner' => 'Owner', + 'permissions_role_everyone_else' => 'Everyone Else', + 'permissions_role_everyone_else_desc' => 'Set permissions for all roles not specifically overridden.', + 'permissions_role_override' => 'Override permissions for role', + 'permissions_inherit_defaults' => 'Inherit defaults', + + // Search + 'search_results' => 'Search Results', + 'search_total_results_found' => ':count result found|:count total results found', + 'search_clear' => 'Clear Search', + 'search_no_pages' => 'No pages matched this search', + 'search_for_term' => 'Search for :term', + 'search_more' => 'More Results', + 'search_advanced' => 'Advanced Search', + 'search_terms' => 'Search Terms', + 'search_content_type' => 'Content Type', + 'search_exact_matches' => 'Exact Matches', + 'search_tags' => 'Tag Searches', + 'search_options' => 'Options', + 'search_viewed_by_me' => 'Viewed by me', + 'search_not_viewed_by_me' => 'Not viewed by me', + 'search_permissions_set' => 'Permissions set', + 'search_created_by_me' => 'Created by me', + 'search_updated_by_me' => 'Updated by me', + 'search_owned_by_me' => 'Owned by me', + 'search_date_options' => 'Date Options', + 'search_updated_before' => 'Updated before', + 'search_updated_after' => 'Updated after', + 'search_created_before' => 'Created before', + 'search_created_after' => 'Created after', + 'search_set_date' => 'Set Date', + 'search_update' => 'Update Search', + + // Shelves + 'shelf' => 'Shelf', + 'shelves' => 'Shelves', + 'x_shelves' => ':count Shelf|:count Shelves', + 'shelves_empty' => 'No shelves have been created', + 'shelves_create' => 'Create New Shelf', + 'shelves_popular' => 'Popular Shelves', + 'shelves_new' => 'New Shelves', + 'shelves_new_action' => 'New Shelf', + 'shelves_popular_empty' => 'The most popular shelves will appear here.', + 'shelves_new_empty' => 'The most recently created shelves will appear here.', + 'shelves_save' => 'Save Shelf', + 'shelves_books' => 'Books on this shelf', + 'shelves_add_books' => 'Add books to this shelf', + 'shelves_drag_books' => 'Drag books below to add them to this shelf', + 'shelves_empty_contents' => 'This shelf has no books assigned to it', + 'shelves_edit_and_assign' => 'Edit shelf to assign books', + 'shelves_edit_named' => 'Edit Shelf :name', + 'shelves_edit' => 'Edit Shelf', + 'shelves_delete' => 'Delete Shelf', + 'shelves_delete_named' => 'Delete Shelf :name', + 'shelves_delete_explain' => "This will delete the shelf with the name ':name'. Contained books will not be deleted.", + 'shelves_delete_confirmation' => 'Are you sure you want to delete this shelf?', + 'shelves_permissions' => 'Shelf Permissions', + 'shelves_permissions_updated' => 'Shelf Permissions Updated', + 'shelves_permissions_active' => 'Shelf Permissions Active', + 'shelves_permissions_cascade_warning' => 'Permissions on shelves do not automatically cascade to contained books. This is because a book can exist on multiple shelves. Permissions can however be copied down to child books using the option found below.', + 'shelves_permissions_create' => 'Shelf create permissions are only used for copying permissions to child books using the action below. They do not control the ability to create books.', + 'shelves_copy_permissions_to_books' => 'Copy Permissions to Books', + 'shelves_copy_permissions' => 'Copy Permissions', + 'shelves_copy_permissions_explain' => 'This will apply the current permission settings of this shelf to all books contained within. Before activating, ensure any changes to the permissions of this shelf have been saved.', + 'shelves_copy_permission_success' => 'Shelf permissions copied to :count books', + + // Books + 'book' => 'Book', + 'books' => 'Books', + 'x_books' => ':count Book|:count Books', + 'books_empty' => 'No books have been created', + 'books_popular' => 'Popular Books', + 'books_recent' => 'Recent Books', + 'books_new' => 'New Books', + 'books_new_action' => 'New Book', + 'books_popular_empty' => 'The most popular books will appear here.', + 'books_new_empty' => 'The most recently created books will appear here.', + 'books_create' => 'Create New Book', + 'books_delete' => 'Delete Book', + 'books_delete_named' => 'Delete Book :bookName', + 'books_delete_explain' => 'This will delete the book with the name \':bookName\'. All pages and chapters will be removed.', + 'books_delete_confirmation' => 'Are you sure you want to delete this book?', + 'books_edit' => 'Edit Book', + 'books_edit_named' => 'Edit Book :bookName', + 'books_form_book_name' => 'Book Name', + 'books_save' => 'Save Book', + 'books_permissions' => 'Book Permissions', + 'books_permissions_updated' => 'Book Permissions Updated', + 'books_empty_contents' => 'No pages or chapters have been created for this book.', + 'books_empty_create_page' => 'Create a new page', + 'books_empty_sort_current_book' => 'Sort the current book', + 'books_empty_add_chapter' => 'Add a chapter', + 'books_permissions_active' => 'Book Permissions Active', + 'books_search_this' => 'Search this book', + 'books_navigation' => 'Book Navigation', + 'books_sort' => 'Sort Book Contents', + 'books_sort_desc' => 'Move chapters and pages within a book to reorganise its contents. Other books can be added which allows easy moving of chapters and pages between books. Optionally an auto sort rule can be set to automatically sort this book\'s contents upon changes.', + 'books_sort_auto_sort' => 'Auto Sort Option', + 'books_sort_auto_sort_active' => 'Auto Sort Active: :sortName', + 'books_sort_named' => 'Sort Book :bookName', + 'books_sort_name' => 'Sort by Name', + 'books_sort_created' => 'Sort by Created Date', + 'books_sort_updated' => 'Sort by Updated Date', + 'books_sort_chapters_first' => 'Chapters First', + 'books_sort_chapters_last' => 'Chapters Last', + 'books_sort_show_other' => 'Show Other Books', + 'books_sort_save' => 'Save New Order', + 'books_sort_show_other_desc' => 'Add other books here to include them in the sort operation, and allow easy cross-book reorganisation.', + 'books_sort_move_up' => 'Move Up', + 'books_sort_move_down' => 'Move Down', + 'books_sort_move_prev_book' => 'Move to Previous Book', + 'books_sort_move_next_book' => 'Move to Next Book', + 'books_sort_move_prev_chapter' => 'Move Into Previous Chapter', + 'books_sort_move_next_chapter' => 'Move Into Next Chapter', + 'books_sort_move_book_start' => 'Move to Start of Book', + 'books_sort_move_book_end' => 'Move to End of Book', + 'books_sort_move_before_chapter' => 'Move to Before Chapter', + 'books_sort_move_after_chapter' => 'Move to After Chapter', + 'books_copy' => 'Copy Book', + 'books_copy_success' => 'Book successfully copied', + + // Chapters + 'chapter' => 'Chapter', + 'chapters' => 'Chapters', + 'x_chapters' => ':count Chapter|:count Chapters', + 'chapters_popular' => 'Popular Chapters', + 'chapters_new' => 'New Chapter', + 'chapters_create' => 'Create New Chapter', + 'chapters_delete' => 'Delete Chapter', + 'chapters_delete_named' => 'Delete Chapter :chapterName', + 'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages that exist within this chapter will also be deleted.', + 'chapters_delete_confirm' => 'Are you sure you want to delete this chapter?', + 'chapters_edit' => 'Edit Chapter', + 'chapters_edit_named' => 'Edit Chapter :chapterName', + 'chapters_save' => 'Save Chapter', + 'chapters_move' => 'Move Chapter', + 'chapters_move_named' => 'Move Chapter :chapterName', + 'chapters_copy' => 'Copy Chapter', + 'chapters_copy_success' => 'Chapter successfully copied', + 'chapters_permissions' => 'Chapter Permissions', + 'chapters_empty' => 'No pages are currently in this chapter.', + 'chapters_permissions_active' => 'Chapter Permissions Active', + 'chapters_permissions_success' => 'Chapter Permissions Updated', + 'chapters_search_this' => 'Search this chapter', + 'chapter_sort_book' => 'Sort Book', + + // Pages + 'page' => 'Page', + 'pages' => 'Pages', + 'x_pages' => ':count Page|:count Pages', + 'pages_popular' => 'Popular Pages', + 'pages_new' => 'New Page', + 'pages_attachments' => 'Attachments', + 'pages_navigation' => 'Page Navigation', + 'pages_delete' => 'Delete Page', + 'pages_delete_named' => 'Delete Page :pageName', + 'pages_delete_draft_named' => 'Delete Draft Page :pageName', + 'pages_delete_draft' => 'Delete Draft Page', + 'pages_delete_success' => 'Page deleted', + 'pages_delete_draft_success' => 'Draft page deleted', + 'pages_delete_warning_template' => 'This page is in active use as a book or chapter default page template. These books or chapters will no longer have a default page template assigned after this page is deleted.', + 'pages_delete_confirm' => 'Are you sure you want to delete this page?', + 'pages_delete_draft_confirm' => 'Are you sure you want to delete this draft page?', + 'pages_editing_named' => 'Editing Page :pageName', + 'pages_edit_draft_options' => 'Draft Options', + 'pages_edit_save_draft' => 'Save Draft', + 'pages_edit_draft' => 'Edit Page Draft', + 'pages_editing_draft' => 'Editing Draft', + 'pages_editing_page' => 'Editing Page', + 'pages_edit_draft_save_at' => 'Draft saved at ', + 'pages_edit_delete_draft' => 'Delete Draft', + 'pages_edit_delete_draft_confirm' => 'Are you sure you want to delete your draft page changes? All of your changes, since the last full save, will be lost and the editor will be updated with the latest page non-draft save state.', + 'pages_edit_discard_draft' => 'Discard Draft', + 'pages_edit_switch_to_markdown' => 'Switch to Markdown Editor', + 'pages_edit_switch_to_markdown_clean' => '(Clean Content)', + 'pages_edit_switch_to_markdown_stable' => '(Stable Content)', + 'pages_edit_switch_to_wysiwyg' => 'Switch to WYSIWYG Editor', + 'pages_edit_switch_to_new_wysiwyg' => 'Switch to new WYSIWYG', + 'pages_edit_switch_to_new_wysiwyg_desc' => '(In Beta Testing)', + 'pages_edit_set_changelog' => 'Set Changelog', + 'pages_edit_enter_changelog_desc' => 'Enter a brief description of the changes you\'ve made', + 'pages_edit_enter_changelog' => 'Enter Changelog', + 'pages_editor_switch_title' => 'Switch Editor', + 'pages_editor_switch_are_you_sure' => 'Are you sure you want to change the editor for this page?', + 'pages_editor_switch_consider_following' => 'Consider the following when changing editors:', + 'pages_editor_switch_consideration_a' => 'Once saved, the new editor option will be used by any future editors, including those that may not be able to change editor type themselves.', + 'pages_editor_switch_consideration_b' => 'This can potentially lead to a loss of detail and syntax in certain circumstances.', + 'pages_editor_switch_consideration_c' => 'Tag or changelog changes, made since last save, won\'t persist across this change.', + 'pages_save' => 'Save Page', + 'pages_title' => 'Page Title', + 'pages_name' => 'Page Name', + 'pages_md_editor' => 'Editor', + 'pages_md_preview' => 'Preview', + 'pages_md_insert_image' => 'Insert Image', + 'pages_md_insert_link' => 'Insert Entity Link', + 'pages_md_insert_drawing' => 'Insert Drawing', + 'pages_md_show_preview' => 'Show preview', + 'pages_md_sync_scroll' => 'Sync preview scroll', + 'pages_md_plain_editor' => 'Plaintext editor', + 'pages_drawing_unsaved' => 'Unsaved Drawing Found', + 'pages_drawing_unsaved_confirm' => 'Unsaved drawing data was found from a previous failed drawing save attempt. Would you like to restore and continue editing this unsaved drawing?', + 'pages_not_in_chapter' => 'Page is not in a chapter', + 'pages_move' => 'Move Page', + 'pages_copy' => 'Copy Page', + 'pages_copy_desination' => 'Copy Destination', + 'pages_copy_success' => 'Page successfully copied', + 'pages_permissions' => 'Page Permissions', + 'pages_permissions_success' => 'Page permissions updated', + 'pages_revision' => 'Revision', + 'pages_revisions' => 'Page Revisions', + 'pages_revisions_desc' => 'Listed below are all the past revisions of this page. You can look back upon, compare, and restore old page versions if permissions allow. The full history of the page may not be fully reflected here since, depending on system configuration, old revisions could be auto-deleted.', + 'pages_revisions_named' => 'Page Revisions for :pageName', + 'pages_revision_named' => 'Page Revision for :pageName', + 'pages_revision_restored_from' => 'Restored from #:id; :summary', + 'pages_revisions_created_by' => 'Created By', + 'pages_revisions_date' => 'Revision Date', + 'pages_revisions_number' => '#', + 'pages_revisions_sort_number' => 'Revision Number', + 'pages_revisions_numbered' => 'Revision #:id', + 'pages_revisions_numbered_changes' => 'Revision #:id Changes', + 'pages_revisions_editor' => 'Editor Type', + 'pages_revisions_changelog' => 'Changelog', + 'pages_revisions_changes' => 'Changes', + 'pages_revisions_current' => 'Current Version', + 'pages_revisions_preview' => 'Preview', + 'pages_revisions_restore' => 'Restore', + 'pages_revisions_none' => 'This page has no revisions', + 'pages_copy_link' => 'Copy Link', + 'pages_edit_content_link' => 'Jump to section in editor', + 'pages_pointer_enter_mode' => 'Enter section select mode', + 'pages_pointer_label' => 'Page Section Options', + 'pages_pointer_permalink' => 'Page Section Permalink', + 'pages_pointer_include_tag' => 'Page Section Include Tag', + 'pages_pointer_toggle_link' => 'Permalink mode, Press to show include tag', + 'pages_pointer_toggle_include' => 'Include tag mode, Press to show permalink', + 'pages_permissions_active' => 'Page Permissions Active', + 'pages_initial_revision' => 'Initial publish', + 'pages_references_update_revision' => 'System auto-update of internal links', + 'pages_initial_name' => 'New Page', + 'pages_editing_draft_notification' => 'You are currently editing a draft that was last saved :timeDiff.', + 'pages_draft_edited_notification' => 'This page has been updated by since that time. It is recommended that you discard this draft.', + 'pages_draft_page_changed_since_creation' => 'This page has been updated since this draft was created. It is recommended that you discard this draft or take care not to overwrite any page changes.', + 'pages_draft_edit_active' => [ + 'start_a' => ':count users have started editing this page', + 'start_b' => ':userName has started editing this page', + 'time_a' => 'since the page was last updated', + 'time_b' => 'in the last :minCount minutes', + 'message' => ':start :time. Take care not to overwrite each other\'s updates!', + ], + 'pages_draft_discarded' => 'Draft discarded! The editor has been updated with the current page content', + 'pages_draft_deleted' => 'Draft deleted! The editor has been updated with the current page content', + 'pages_specific' => 'Specific Page', + 'pages_is_template' => 'Page Template', + + // Editor Sidebar + 'toggle_sidebar' => 'Toggle Sidebar', + 'page_tags' => 'Page Tags', + 'chapter_tags' => 'Chapter Tags', + 'book_tags' => 'Book Tags', + 'shelf_tags' => 'Shelf Tags', + 'tag' => 'Tag', + 'tags' => 'Tags', + 'tags_index_desc' => 'Tags can be applied to content within the system to apply a flexible form of categorization. Tags can have both a key and value, with the value being optional. Once applied, content can then be queried using the tag name and value.', + 'tag_name' => 'Tag Name', + 'tag_value' => 'Tag Value (Optional)', + 'tags_explain' => "Add some tags to better categorise your content. \n You can assign a value to a tag for more in-depth organisation.", + 'tags_add' => 'Add another tag', + 'tags_remove' => 'Remove this tag', + 'tags_usages' => 'Total tag usages', + 'tags_assigned_pages' => 'Assigned to Pages', + 'tags_assigned_chapters' => 'Assigned to Chapters', + 'tags_assigned_books' => 'Assigned to Books', + 'tags_assigned_shelves' => 'Assigned to Shelves', + 'tags_x_unique_values' => ':count unique values', + 'tags_all_values' => 'All values', + 'tags_view_tags' => 'View Tags', + 'tags_view_existing_tags' => 'View existing tags', + 'tags_list_empty_hint' => 'Tags can be assigned via the page editor sidebar or while editing the details of a book, chapter or shelf.', + 'attachments' => 'Attachments', + 'attachments_explain' => 'Upload some files or attach some links to display on your page. These are visible in the page sidebar.', + 'attachments_explain_instant_save' => 'Changes here are saved instantly.', + 'attachments_upload' => 'Upload File', + 'attachments_link' => 'Attach Link', + 'attachments_upload_drop' => 'Alternatively you can drag and drop a file here to upload it as an attachment.', + 'attachments_set_link' => 'Set Link', + 'attachments_delete' => 'Are you sure you want to delete this attachment?', + 'attachments_dropzone' => 'Drop files here to upload', + 'attachments_no_files' => 'No files have been uploaded', + 'attachments_explain_link' => 'You can attach a link if you\'d prefer not to upload a file. This can be a link to another page or a link to a file in the cloud.', + 'attachments_link_name' => 'Link Name', + 'attachment_link' => 'Attachment link', + 'attachments_link_url' => 'Link to file', + 'attachments_link_url_hint' => 'Url of site or file', + 'attach' => 'Attach', + 'attachments_insert_link' => 'Add Attachment Link to Page', + 'attachments_edit_file' => 'Edit File', + 'attachments_edit_file_name' => 'File Name', + 'attachments_edit_drop_upload' => 'Drop files or click here to upload and overwrite', + 'attachments_order_updated' => 'Attachment order updated', + 'attachments_updated_success' => 'Attachment details updated', + 'attachments_deleted' => 'Attachment deleted', + 'attachments_file_uploaded' => 'File successfully uploaded', + 'attachments_file_updated' => 'File successfully updated', + 'attachments_link_attached' => 'Link successfully attached to page', + 'templates' => 'Templates', + 'templates_set_as_template' => 'Page is a template', + 'templates_explain_set_as_template' => 'You can set this page as a template so its contents be utilized when creating other pages. Other users will be able to use this template if they have view permissions for this page.', + 'templates_replace_content' => 'Replace page content', + 'templates_append_content' => 'Append to page content', + 'templates_prepend_content' => 'Prepend to page content', + + // Profile View + 'profile_user_for_x' => 'User for :time', + 'profile_created_content' => 'Created Content', + 'profile_not_created_pages' => ':userName has not created any pages', + 'profile_not_created_chapters' => ':userName has not created any chapters', + 'profile_not_created_books' => ':userName has not created any books', + 'profile_not_created_shelves' => ':userName has not created any shelves', + + // Comments + 'comment' => 'Comment', + 'comments' => 'Comments', + 'comment_add' => 'Add Comment', + 'comment_none' => 'No comments to display', + 'comment_placeholder' => 'Leave a comment here', + 'comment_thread_count' => ':count Comment Thread|:count Comment Threads', + 'comment_archived_count' => ':count Archived', + 'comment_archived_threads' => 'Archived Threads', + 'comment_save' => 'Save Comment', + 'comment_new' => 'New Comment', + 'comment_created' => 'commented :createDiff', + 'comment_updated' => 'Updated :updateDiff by :username', + 'comment_updated_indicator' => 'Updated', + 'comment_deleted_success' => 'Comment deleted', + 'comment_created_success' => 'Comment added', + 'comment_updated_success' => 'Comment updated', + 'comment_archive_success' => 'Comment archived', + 'comment_unarchive_success' => 'Comment un-archived', + 'comment_view' => 'View comment', + 'comment_jump_to_thread' => 'Jump to thread', + 'comment_delete_confirm' => 'Are you sure you want to delete this comment?', + 'comment_in_reply_to' => 'In reply to :commentId', + 'comment_reference' => 'Reference', + 'comment_reference_outdated' => '(Outdated)', + 'comment_editor_explain' => 'Here are the comments that have been left on this page. Comments can be added & managed when viewing the saved page.', + + // Revision + 'revision_delete_confirm' => 'Are you sure you want to delete this revision?', + 'revision_restore_confirm' => 'Are you sure you want to restore this revision? The current page contents will be replaced.', + 'revision_cannot_delete_latest' => 'Cannot delete the latest revision.', + + // Copy view + 'copy_consider' => 'Please consider the below when copying content.', + 'copy_consider_permissions' => 'Custom permission settings will not be copied.', + 'copy_consider_owner' => 'You will become the owner of all copied content.', + 'copy_consider_images' => 'Page image files will not be duplicated & the original images will retain their relation to the page they were originally uploaded to.', + 'copy_consider_attachments' => 'Page attachments will not be copied.', + 'copy_consider_access' => 'A change of location, owner or permissions may result in this content being accessible to those previously without access.', + + // Conversions + 'convert_to_shelf' => 'Convert to Shelf', + 'convert_to_shelf_contents_desc' => 'You can convert this book to a new shelf with the same contents. Chapters contained within this book will be converted to new books. If this book contains any pages, that are not in a chapter, this book will be renamed and contain such pages, and this book will become part of the new shelf.', + 'convert_to_shelf_permissions_desc' => 'Any permissions set on this book will be copied to the new shelf and to all new child books that don\'t have their own permissions enforced. Note that permissions on shelves do not auto-cascade to content within, as they do for books.', + 'convert_book' => 'Convert Book', + 'convert_book_confirm' => 'Are you sure you want to convert this book?', + 'convert_undo_warning' => 'This cannot be as easily undone.', + 'convert_to_book' => 'Convert to Book', + 'convert_to_book_desc' => 'You can convert this chapter to a new book with the same contents. Any permissions set on this chapter will be copied to the new book but any inherited permissions, from the parent book, will not be copied which could lead to a change of access control.', + 'convert_chapter' => 'Convert Chapter', + 'convert_chapter_confirm' => 'Are you sure you want to convert this chapter?', + + // References + 'references' => 'References', + 'references_none' => 'There are no tracked references to this item.', + 'references_to_desc' => 'Listed below is all the known content in the system that links to this item.', + + // Watch Options + 'watch' => 'Watch', + 'watch_title_default' => 'Default Preferences', + 'watch_desc_default' => 'Revert watching to just your default notification preferences.', + 'watch_title_ignore' => 'Ignore', + 'watch_desc_ignore' => 'Ignore all notifications, including those from user-level preferences.', + 'watch_title_new' => 'New Pages', + 'watch_desc_new' => 'Notify when any new page is created within this item.', + 'watch_title_updates' => 'All Page Updates', + 'watch_desc_updates' => 'Notify upon all new pages and page changes.', + 'watch_desc_updates_page' => 'Notify upon all page changes.', + 'watch_title_comments' => 'All Page Updates & Comments', + 'watch_desc_comments' => 'Notify upon all new pages, page changes and new comments.', + 'watch_desc_comments_page' => 'Notify upon page changes and new comments.', + 'watch_change_default' => 'Change default notification preferences', + 'watch_detail_ignore' => 'Ignoring notifications', + 'watch_detail_new' => 'Watching for new pages', + 'watch_detail_updates' => 'Watching new pages and updates', + 'watch_detail_comments' => 'Watching new pages, updates & comments', + 'watch_detail_parent_book' => 'Watching via parent book', + 'watch_detail_parent_book_ignore' => 'Ignoring via parent book', + 'watch_detail_parent_chapter' => 'Watching via parent chapter', + 'watch_detail_parent_chapter_ignore' => 'Ignoring via parent chapter', +]; diff --git a/lang/ku/errors.php b/lang/ku/errors.php new file mode 100644 index 00000000000..9d738379648 --- /dev/null +++ b/lang/ku/errors.php @@ -0,0 +1,133 @@ + 'You do not have permission to access the requested page.', + 'permissionJson' => 'You do not have permission to perform the requested action.', + + // Auth + 'error_user_exists_different_creds' => 'A user with the email :email already exists but with different credentials.', + 'auth_pre_register_theme_prevention' => 'User account could not be registered for the provided details', + 'email_already_confirmed' => 'Email has already been confirmed, Try logging in.', + 'email_confirmation_invalid' => 'This confirmation token is not valid or has already been used, Please try registering again.', + 'email_confirmation_expired' => 'The confirmation token has expired, A new confirmation email has been sent.', + 'email_confirmation_awaiting' => 'The email address for the account in use needs to be confirmed', + 'ldap_fail_anonymous' => 'LDAP access failed using anonymous bind', + 'ldap_fail_authed' => 'LDAP access failed using given dn & password details', + 'ldap_extension_not_installed' => 'LDAP PHP extension not installed', + 'ldap_cannot_connect' => 'Cannot connect to ldap server, Initial connection failed', + 'saml_already_logged_in' => 'Already logged in', + 'saml_no_email_address' => 'Could not find an email address, for this user, in the data provided by the external authentication system', + 'saml_invalid_response_id' => 'The request from the external authentication system is not recognised by a process started by this application. Navigating back after a login could cause this issue.', + 'saml_fail_authed' => 'Login using :system failed, system did not provide successful authorization', + 'oidc_already_logged_in' => 'Already logged in', + 'oidc_no_email_address' => 'Could not find an email address, for this user, in the data provided by the external authentication system', + 'oidc_fail_authed' => 'Login using :system failed, system did not provide successful authorization', + 'social_no_action_defined' => 'No action defined', + 'social_login_bad_response' => "Error received during :socialAccount login: \n:error", + 'social_account_in_use' => 'This :socialAccount account is already in use, Try logging in via the :socialAccount option.', + 'social_account_email_in_use' => 'The email :email is already in use. If you already have an account you can connect your :socialAccount account from your profile settings.', + 'social_account_existing' => 'This :socialAccount is already attached to your profile.', + 'social_account_already_used_existing' => 'This :socialAccount account is already used by another user.', + 'social_account_not_used' => 'This :socialAccount account is not linked to any users. Please attach it in your profile settings. ', + 'social_account_register_instructions' => 'If you do not yet have an account, You can register an account using the :socialAccount option.', + 'social_driver_not_found' => 'Social driver not found', + 'social_driver_not_configured' => 'Your :socialAccount social settings are not configured correctly.', + 'invite_token_expired' => 'This invitation link has expired. You can instead try to reset your account password.', + 'login_user_not_found' => 'A user for this action could not be found.', + + // System + 'path_not_writable' => 'File path :filePath could not be uploaded to. Ensure it is writable to the server.', + 'cannot_get_image_from_url' => 'Cannot get image from :url', + 'cannot_create_thumbs' => 'The server cannot create thumbnails. Please check you have the GD PHP extension installed.', + 'server_upload_limit' => 'The server does not allow uploads of this size. Please try a smaller file size.', + 'server_post_limit' => 'The server cannot receive the provided amount of data. Try again with less data or a smaller file.', + 'uploaded' => 'The server does not allow uploads of this size. Please try a smaller file size.', + + // Drawing & Images + 'image_upload_error' => 'An error occurred uploading the image', + 'image_upload_type_error' => 'The image type being uploaded is invalid', + 'image_upload_replace_type' => 'Image file replacements must be of the same type', + 'image_upload_memory_limit' => 'Failed to handle image upload and/or create thumbnails due to system resource limits.', + 'image_thumbnail_memory_limit' => 'Failed to create image size variations due to system resource limits.', + 'image_gallery_thumbnail_memory_limit' => 'Failed to create gallery thumbnails due to system resource limits.', + 'drawing_data_not_found' => 'Drawing data could not be loaded. The drawing file might no longer exist or you may not have permission to access it.', + + // Attachments + 'attachment_not_found' => 'Attachment not found', + 'attachment_upload_error' => 'An error occurred uploading the attachment file', + + // Pages + 'page_draft_autosave_fail' => 'Failed to save draft. Ensure you have internet connection before saving this page', + 'page_draft_delete_fail' => 'Failed to delete page draft and fetch current page saved content', + 'page_custom_home_deletion' => 'Cannot delete a page while it is set as a homepage', + + // Entities + 'entity_not_found' => 'Entity not found', + 'bookshelf_not_found' => 'Shelf not found', + 'book_not_found' => 'Book not found', + 'page_not_found' => 'Page not found', + 'chapter_not_found' => 'Chapter not found', + 'selected_book_not_found' => 'The selected book was not found', + 'selected_book_chapter_not_found' => 'The selected Book or Chapter was not found', + 'guests_cannot_save_drafts' => 'Guests cannot save drafts', + + // Users + 'users_cannot_delete_only_admin' => 'You cannot delete the only admin', + 'users_cannot_delete_guest' => 'You cannot delete the guest user', + 'users_could_not_send_invite' => 'Could not create user since invite email failed to send', + + // Roles + 'role_cannot_be_edited' => 'This role cannot be edited', + 'role_system_cannot_be_deleted' => 'This role is a system role and cannot be deleted', + 'role_registration_default_cannot_delete' => 'This role cannot be deleted while set as the default registration role', + 'role_cannot_remove_only_admin' => 'This user is the only user assigned to the administrator role. Assign the administrator role to another user before attempting to remove it here.', + + // Comments + 'comment_list' => 'An error occurred while fetching the comments.', + 'cannot_add_comment_to_draft' => 'You cannot add comments to a draft.', + 'comment_add' => 'An error occurred while adding / updating the comment.', + 'comment_delete' => 'An error occurred while deleting the comment.', + 'empty_comment' => 'Cannot add an empty comment.', + + // Error pages + '404_page_not_found' => 'Page Not Found', + 'sorry_page_not_found' => 'Sorry, The page you were looking for could not be found.', + 'sorry_page_not_found_permission_warning' => 'If you expected this page to exist, you might not have permission to view it.', + 'image_not_found' => 'Image Not Found', + 'image_not_found_subtitle' => 'Sorry, The image file you were looking for could not be found.', + 'image_not_found_details' => 'If you expected this image to exist it might have been deleted.', + 'return_home' => 'Return to home', + 'error_occurred' => 'An Error Occurred', + 'app_down' => ':appName is down right now', + 'back_soon' => 'It will be back up soon.', + + // Import + 'import_zip_cant_read' => 'Could not read ZIP file.', + 'import_zip_cant_decode_data' => 'Could not find and decode ZIP data.json content.', + 'import_zip_no_data' => 'ZIP file data has no expected book, chapter or page content.', + 'import_validation_failed' => 'Import ZIP failed to validate with errors:', + 'import_zip_failed_notification' => 'Failed to import ZIP file.', + 'import_perms_books' => 'You are lacking the required permissions to create books.', + 'import_perms_chapters' => 'You are lacking the required permissions to create chapters.', + 'import_perms_pages' => 'You are lacking the required permissions to create pages.', + 'import_perms_images' => 'You are lacking the required permissions to create images.', + 'import_perms_attachments' => 'You are lacking the required permission to create attachments.', + + // API errors + 'api_no_authorization_found' => 'No authorization token found on the request', + 'api_bad_authorization_format' => 'An authorization token was found on the request but the format appeared incorrect', + 'api_user_token_not_found' => 'No matching API token was found for the provided authorization token', + 'api_incorrect_token_secret' => 'The secret provided for the given used API token is incorrect', + 'api_user_no_api_permission' => 'The owner of the used API token does not have permission to make API calls', + 'api_user_token_expired' => 'The authorization token used has expired', + + // Settings & Maintenance + 'maintenance_test_email_failure' => 'Error thrown when sending a test email:', + + // HTTP errors + 'http_ssr_url_no_match' => 'The URL does not match the configured allowed SSR hosts', +]; diff --git a/lang/ku/notifications.php b/lang/ku/notifications.php new file mode 100644 index 00000000000..1afd23f1dc4 --- /dev/null +++ b/lang/ku/notifications.php @@ -0,0 +1,27 @@ + 'New comment on page: :pageName', + 'new_comment_intro' => 'A user has commented on a page in :appName:', + 'new_page_subject' => 'New page: :pageName', + 'new_page_intro' => 'A new page has been created in :appName:', + 'updated_page_subject' => 'Updated page: :pageName', + 'updated_page_intro' => 'A page has been updated in :appName:', + 'updated_page_debounce' => 'To prevent a mass of notifications, for a while you won\'t be sent notifications for further edits to this page by the same editor.', + + 'detail_page_name' => 'Page Name:', + 'detail_page_path' => 'Page Path:', + 'detail_commenter' => 'Commenter:', + 'detail_comment' => 'Comment:', + 'detail_created_by' => 'Created By:', + 'detail_updated_by' => 'Updated By:', + + 'action_view_comment' => 'View Comment', + 'action_view_page' => 'View Page', + + 'footer_reason' => 'This notification was sent to you because :link cover this type of activity for this item.', + 'footer_reason_link' => 'your notification preferences', +]; diff --git a/lang/ku/pagination.php b/lang/ku/pagination.php new file mode 100644 index 00000000000..85bd12fc319 --- /dev/null +++ b/lang/ku/pagination.php @@ -0,0 +1,12 @@ + '« Previous', + 'next' => 'Next »', + +]; diff --git a/lang/ku/passwords.php b/lang/ku/passwords.php new file mode 100644 index 00000000000..b408f3c2fda --- /dev/null +++ b/lang/ku/passwords.php @@ -0,0 +1,15 @@ + 'Passwords must be at least eight characters and match the confirmation.', + 'user' => "We can't find a user with that e-mail address.", + 'token' => 'The password reset token is invalid for this email address.', + 'sent' => 'We have e-mailed your password reset link!', + 'reset' => 'Your password has been reset!', + +]; diff --git a/lang/ku/preferences.php b/lang/ku/preferences.php new file mode 100644 index 00000000000..2872f5f3c65 --- /dev/null +++ b/lang/ku/preferences.php @@ -0,0 +1,51 @@ + 'My Account', + + 'shortcuts' => 'Shortcuts', + 'shortcuts_interface' => 'UI Shortcut Preferences', + 'shortcuts_toggle_desc' => 'Here you can enable or disable keyboard system interface shortcuts, used for navigation and actions.', + 'shortcuts_customize_desc' => 'You can customize each of the shortcuts below. Just press your desired key combination after selecting the input for a shortcut.', + 'shortcuts_toggle_label' => 'Keyboard shortcuts enabled', + 'shortcuts_section_navigation' => 'Navigation', + 'shortcuts_section_actions' => 'Common Actions', + 'shortcuts_save' => 'Save Shortcuts', + 'shortcuts_overlay_desc' => 'Note: When shortcuts are enabled a helper overlay is available via pressing "?" which will highlight the available shortcuts for actions currently visible on the screen.', + 'shortcuts_update_success' => 'Shortcut preferences have been updated!', + 'shortcuts_overview_desc' => 'Manage keyboard shortcuts you can use to navigate the system user interface.', + + 'notifications' => 'Notification Preferences', + 'notifications_desc' => 'Control the email notifications you receive when certain activity is performed within the system.', + 'notifications_opt_own_page_changes' => 'Notify upon changes to pages I own', + 'notifications_opt_own_page_comments' => 'Notify upon comments on pages I own', + 'notifications_opt_comment_replies' => 'Notify upon replies to my comments', + 'notifications_save' => 'Save Preferences', + 'notifications_update_success' => 'Notification preferences have been updated!', + 'notifications_watched' => 'Watched & Ignored Items', + 'notifications_watched_desc' => 'Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.', + + 'auth' => 'Access & Security', + 'auth_change_password' => 'Change Password', + 'auth_change_password_desc' => 'Change the password you use to log-in to the application. This must be at least 8 characters long.', + 'auth_change_password_success' => 'Password has been updated!', + + 'profile' => 'Profile Details', + 'profile_desc' => 'Manage the details of your account which represents you to other users, in addition to details that are used for communication and system personalisation.', + 'profile_view_public' => 'View Public Profile', + 'profile_name_desc' => 'Configure your display name which will be visible to other users in the system through the activity you perform, and content you own.', + 'profile_email_desc' => 'This email will be used for notifications and, depending on active system authentication, system access.', + 'profile_email_no_permission' => 'Unfortunately you don\'t have permission to change your email address. If you want to change this, you\'d need to ask an administrator to change this for you.', + 'profile_avatar_desc' => 'Select an image which will be used to represent yourself to others in the system. Ideally this image should be square and about 256px in width and height.', + 'profile_admin_options' => 'Administrator Options', + 'profile_admin_options_desc' => 'Additional administrator-level options, like those to manage role assignments, can be found for your user account in the "Settings > Users" area of the application.', + + 'delete_account' => 'Delete Account', + 'delete_my_account' => 'Delete My Account', + 'delete_my_account_desc' => 'This will fully delete your user account from the system. You will not be able to recover this account or revert this action. Content you\'ve created, such as created pages and uploaded images, will remain.', + 'delete_my_account_warning' => 'Are you sure you want to delete your account?', +]; diff --git a/lang/ku/settings.php b/lang/ku/settings.php new file mode 100644 index 00000000000..81c2c0a93c3 --- /dev/null +++ b/lang/ku/settings.php @@ -0,0 +1,370 @@ + 'Settings', + 'settings_save' => 'Save Settings', + 'system_version' => 'System Version', + 'categories' => 'Categories', + + // App Settings + 'app_customization' => 'Customization', + 'app_features_security' => 'Features & Security', + 'app_name' => 'Application Name', + 'app_name_desc' => 'This name is shown in the header and in any system-sent emails.', + 'app_name_header' => 'Show name in header', + 'app_public_access' => 'Public Access', + 'app_public_access_desc' => 'Enabling this option will allow visitors, that are not logged-in, to access content in your BookStack instance.', + 'app_public_access_desc_guest' => 'Access for public visitors can be controlled through the "Guest" user.', + 'app_public_access_toggle' => 'Allow public access', + 'app_public_viewing' => 'Allow public viewing?', + 'app_secure_images' => 'Higher Security Image Uploads', + 'app_secure_images_toggle' => 'Enable higher security image uploads', + 'app_secure_images_desc' => 'For performance reasons, all images are public. This option adds a random, hard-to-guess string in front of image urls. Ensure directory indexes are not enabled to prevent easy access.', + 'app_default_editor' => 'Default Page Editor', + 'app_default_editor_desc' => 'Select which editor will be used by default when editing new pages. This can be overridden at a page level where permissions allow.', + 'app_custom_html' => 'Custom HTML Head Content', + 'app_custom_html_desc' => 'Any content added here will be inserted into the bottom of the section of every page. This is handy for overriding styles or adding analytics code.', + 'app_custom_html_disabled_notice' => 'Custom HTML head content is disabled on this settings page to ensure any breaking changes can be reverted.', + 'app_logo' => 'Application Logo', + 'app_logo_desc' => 'This is used in the application header bar, among other areas. This image should be 86px in height. Large images will be scaled down.', + 'app_icon' => 'Application Icon', + 'app_icon_desc' => 'This icon is used for browser tabs and shortcut icons. This should be a 256px square PNG image.', + 'app_homepage' => 'Application Homepage', + 'app_homepage_desc' => 'Select a view to show on the homepage instead of the default view. Page permissions are ignored for selected pages.', + 'app_homepage_select' => 'Select a page', + 'app_footer_links' => 'Footer Links', + 'app_footer_links_desc' => 'Add links to show within the site footer. These will be displayed at the bottom of most pages, including those that do not require login. You can use a label of "trans::" to use system-defined translations. For example: Using "trans::common.privacy_policy" will provide the translated text "Privacy Policy" and "trans::common.terms_of_service" will provide the translated text "Terms of Service".', + 'app_footer_links_label' => 'Link Label', + 'app_footer_links_url' => 'Link URL', + 'app_footer_links_add' => 'Add Footer Link', + 'app_disable_comments' => 'Disable Comments', + 'app_disable_comments_toggle' => 'Disable comments', + 'app_disable_comments_desc' => 'Disables comments across all pages in the application.
Existing comments are not shown.', + + // Color settings + 'color_scheme' => 'Application Color Scheme', + 'color_scheme_desc' => 'Set the colors to use in the application user interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.', + 'ui_colors_desc' => 'Set the application primary color and default link color. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the application interface.', + 'app_color' => 'Primary Color', + 'link_color' => 'Default Link Color', + 'content_colors_desc' => 'Set colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.', + 'bookshelf_color' => 'Shelf Color', + 'book_color' => 'Book Color', + 'chapter_color' => 'Chapter Color', + 'page_color' => 'Page Color', + 'page_draft_color' => 'Page Draft Color', + + // Registration Settings + 'reg_settings' => 'Registration', + 'reg_enable' => 'Enable Registration', + 'reg_enable_toggle' => 'Enable registration', + 'reg_enable_desc' => 'When registration is enabled user will be able to sign themselves up as an application user. Upon registration they are given a single, default user role.', + 'reg_default_role' => 'Default user role after registration', + 'reg_enable_external_warning' => 'The option above is ignored while external LDAP or SAML authentication is active. User accounts for non-existing members will be auto-created if authentication, against the external system in use, is successful.', + 'reg_email_confirmation' => 'Email Confirmation', + 'reg_email_confirmation_toggle' => 'Require email confirmation', + 'reg_confirm_email_desc' => 'If domain restriction is used then email confirmation will be required and this option will be ignored.', + 'reg_confirm_restrict_domain' => 'Domain Restriction', + 'reg_confirm_restrict_domain_desc' => 'Enter a comma separated list of email domains you would like to restrict registration to. Users will be sent an email to confirm their address before being allowed to interact with the application.
Note that users will be able to change their email addresses after successful registration.', + 'reg_confirm_restrict_domain_placeholder' => 'No restriction set', + + // Sorting Settings + 'sorting' => 'Sorting', + 'sorting_book_default' => 'Default Book Sort', + 'sorting_book_default_desc' => 'Select the default sort rule to apply to new books. This won\'t affect existing books, and can be overridden per-book.', + 'sorting_rules' => 'Sort Rules', + 'sorting_rules_desc' => 'These are predefined sorting operations which can be applied to content in the system.', + 'sort_rule_assigned_to_x_books' => 'Assigned to :count Book|Assigned to :count Books', + 'sort_rule_create' => 'Create Sort Rule', + 'sort_rule_edit' => 'Edit Sort Rule', + 'sort_rule_delete' => 'Delete Sort Rule', + 'sort_rule_delete_desc' => 'Remove this sort rule from the system. Books using this sort will revert to manual sorting.', + 'sort_rule_delete_warn_books' => 'This sort rule is currently used on :count book(s). Are you sure you want to delete this?', + 'sort_rule_delete_warn_default' => 'This sort rule is currently used as the default for books. Are you sure you want to delete this?', + 'sort_rule_details' => 'Sort Rule Details', + 'sort_rule_details_desc' => 'Set a name for this sort rule, which will appear in lists when users are selecting a sort.', + 'sort_rule_operations' => 'Sort Operations', + 'sort_rule_operations_desc' => 'Configure the sort actions to be performed by moving them from the list of available operations. Upon use, the operations will be applied in order, from top to bottom. Any changes made here will be applied to all assigned books upon save.', + 'sort_rule_available_operations' => 'Available Operations', + 'sort_rule_available_operations_empty' => 'No operations remaining', + 'sort_rule_configured_operations' => 'Configured Operations', + 'sort_rule_configured_operations_empty' => 'Drag/add operations from the "Available Operations" list', + 'sort_rule_op_asc' => '(Asc)', + 'sort_rule_op_desc' => '(Desc)', + 'sort_rule_op_name' => 'Name - Alphabetical', + 'sort_rule_op_name_numeric' => '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_last' => 'Chapters Last', + + // Maintenance settings + 'maint' => 'Maintenance', + 'maint_image_cleanup' => 'Cleanup Images', + 'maint_image_cleanup_desc' => 'Scans page & revision content to check which images and drawings are currently in use and which images are redundant. Ensure you create a full database and image backup before running this.', + 'maint_delete_images_only_in_revisions' => 'Also delete images that only exist in old page revisions', + 'maint_image_cleanup_run' => 'Run Cleanup', + 'maint_image_cleanup_warning' => ':count potentially unused images were found. Are you sure you want to delete these images?', + 'maint_image_cleanup_success' => ':count potentially unused images found and deleted!', + 'maint_image_cleanup_nothing_found' => 'No unused images found, Nothing deleted!', + 'maint_send_test_email' => 'Send a Test Email', + 'maint_send_test_email_desc' => 'This sends a test email to your email address specified in your profile.', + 'maint_send_test_email_run' => 'Send test email', + 'maint_send_test_email_success' => 'Email sent to :address', + 'maint_send_test_email_mail_subject' => 'Test Email', + 'maint_send_test_email_mail_greeting' => 'Email delivery seems to work!', + 'maint_send_test_email_mail_text' => 'Congratulations! As you received this email notification, your email settings seem to be configured properly.', + 'maint_recycle_bin_desc' => 'Deleted shelves, books, chapters & pages are sent to the recycle bin so they can be restored or permanently deleted. Older items in the recycle bin may be automatically removed after a while depending on system configuration.', + 'maint_recycle_bin_open' => 'Open Recycle Bin', + 'maint_regen_references' => 'Regenerate References', + 'maint_regen_references_desc' => 'This action will rebuild the cross-item reference index within the database. This is usually handled automatically but this action can be useful to index old content or content added via unofficial methods.', + 'maint_regen_references_success' => 'Reference index has been regenerated!', + 'maint_timeout_command_note' => 'Note: This action can take time to run, which can lead to timeout issues in some web environments. As an alternative, this action be performed using a terminal command.', + + // Recycle Bin + 'recycle_bin' => 'Recycle Bin', + 'recycle_bin_desc' => 'Here you can restore items that have been deleted or choose to permanently remove them from the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.', + 'recycle_bin_deleted_item' => 'Deleted Item', + 'recycle_bin_deleted_parent' => 'Parent', + 'recycle_bin_deleted_by' => 'Deleted By', + 'recycle_bin_deleted_at' => 'Deletion Time', + 'recycle_bin_permanently_delete' => 'Permanently Delete', + 'recycle_bin_restore' => 'Restore', + 'recycle_bin_contents_empty' => 'The recycle bin is currently empty', + 'recycle_bin_empty' => 'Empty Recycle Bin', + 'recycle_bin_empty_confirm' => 'This will permanently destroy all items in the recycle bin including content contained within each item. Are you sure you want to empty the recycle bin?', + 'recycle_bin_destroy_confirm' => 'This action will permanently delete this item from the system, along with any child elements listed below, and you will not be able to restore this content. Are you sure you want to permanently delete this item?', + 'recycle_bin_destroy_list' => 'Items to be Destroyed', + 'recycle_bin_restore_list' => 'Items to be Restored', + 'recycle_bin_restore_confirm' => 'This action will restore the deleted item, including any child elements, to their original location. If the original location has since been deleted, and is now in the recycle bin, the parent item will also need to be restored.', + 'recycle_bin_restore_deleted_parent' => 'The parent of this item has also been deleted. These will remain deleted until that parent is also restored.', + 'recycle_bin_restore_parent' => 'Restore Parent', + 'recycle_bin_destroy_notification' => 'Deleted :count total items from the recycle bin.', + 'recycle_bin_restore_notification' => 'Restored :count total items from the recycle bin.', + + // Audit Log + 'audit' => 'Audit Log', + 'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.', + 'audit_event_filter' => 'Event Filter', + 'audit_event_filter_no_filter' => 'No Filter', + 'audit_deleted_item' => 'Deleted Item', + 'audit_deleted_item_name' => 'Name: :name', + 'audit_table_user' => 'User', + 'audit_table_event' => 'Event', + 'audit_table_related' => 'Related Item or Detail', + 'audit_table_ip' => 'IP Address', + 'audit_table_date' => 'Activity Date', + 'audit_date_from' => 'Date Range From', + 'audit_date_to' => 'Date Range To', + + // Role Settings + 'roles' => 'Roles', + 'role_user_roles' => 'User Roles', + 'roles_index_desc' => 'Roles are used to group users & provide system permission to their members. When a user is a member of multiple roles the privileges granted will stack and the user will inherit all abilities.', + 'roles_x_users_assigned' => ':count user assigned|:count users assigned', + 'roles_x_permissions_provided' => ':count permission|:count permissions', + 'roles_assigned_users' => 'Assigned Users', + 'roles_permissions_provided' => 'Provided Permissions', + 'role_create' => 'Create New Role', + 'role_delete' => 'Delete Role', + 'role_delete_confirm' => 'This will delete the role with the name \':roleName\'.', + 'role_delete_users_assigned' => 'This role has :userCount users assigned to it. If you would like to migrate the users from this role select a new role below.', + 'role_delete_no_migration' => "Don't migrate users", + 'role_delete_sure' => 'Are you sure you want to delete this role?', + 'role_edit' => 'Edit Role', + 'role_details' => 'Role Details', + 'role_name' => 'Role Name', + 'role_desc' => 'Short Description of Role', + 'role_mfa_enforced' => 'Requires Multi-Factor Authentication', + 'role_external_auth_id' => 'External Authentication IDs', + 'role_system' => 'System Permissions', + 'role_manage_users' => 'Manage users', + 'role_manage_roles' => 'Manage roles & role permissions', + 'role_manage_entity_permissions' => 'Manage all book, chapter & page permissions', + 'role_manage_own_entity_permissions' => 'Manage permissions on own book, chapter & pages', + 'role_manage_page_templates' => 'Manage page templates', + 'role_access_api' => 'Access system API', + 'role_manage_settings' => 'Manage app settings', + 'role_export_content' => 'Export content', + 'role_import_content' => 'Import content', + 'role_editor_change' => 'Change page editor', + 'role_notifications' => 'Receive & manage notifications', + 'role_asset' => 'Asset Permissions', + 'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.', + 'role_asset_desc' => 'These permissions control default access to the assets within the system. Permissions on Books, Chapters and Pages will override these permissions.', + 'role_asset_admins' => 'Admins are automatically given access to all content but these options may show or hide UI options.', + 'role_asset_image_view_note' => 'This relates to visibility within the image manager. Actual access of uploaded image files will be dependant upon system image storage option.', + 'role_all' => 'All', + 'role_own' => 'Own', + 'role_controlled_by_asset' => 'Controlled by the asset they are uploaded to', + 'role_save' => 'Save Role', + 'role_users' => 'Users in this role', + 'role_users_none' => 'No users are currently assigned to this role', + + // Users + 'users' => 'Users', + 'users_index_desc' => 'Create & manage individual user accounts within the system. User accounts are used for login and attribution of content & activity. Access permissions are primarily role-based but user content ownership, among other factors, may also affect permissions & access.', + 'user_profile' => 'User Profile', + 'users_add_new' => 'Add New User', + 'users_search' => 'Search Users', + 'users_latest_activity' => 'Latest Activity', + 'users_details' => 'User Details', + 'users_details_desc' => 'Set a display name and an email address for this user. The email address will be used for logging into the application.', + 'users_details_desc_no_email' => 'Set a display name for this user so others can recognise them.', + 'users_role' => 'User Roles', + 'users_role_desc' => 'Select which roles this user will be assigned to. If a user is assigned to multiple roles the permissions from those roles will stack and they will receive all abilities of the assigned roles.', + 'users_password' => 'User Password', + 'users_password_desc' => 'Set a password used to log-in to the application. This must be at least 8 characters long.', + 'users_send_invite_text' => 'You can choose to send this user an invitation email which allows them to set their own password otherwise you can set their password yourself.', + 'users_send_invite_option' => 'Send user invite email', + 'users_external_auth_id' => 'External Authentication ID', + 'users_external_auth_id_desc' => 'When an external authentication system is in use (such as SAML2, OIDC or LDAP) this is the ID which links this BookStack user to the authentication system account. You can ignore this field if using the default email-based authentication.', + 'users_password_warning' => 'Only fill the below if you would like to change the password for this user.', + 'users_system_public' => 'This user represents any guest users that visit your instance. It cannot be used to log in but is assigned automatically.', + 'users_delete' => 'Delete User', + 'users_delete_named' => 'Delete user :userName', + 'users_delete_warning' => 'This will fully delete this user with the name \':userName\' from the system.', + 'users_delete_confirm' => 'Are you sure you want to delete this user?', + 'users_migrate_ownership' => 'Migrate Ownership', + 'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.', + 'users_none_selected' => 'No user selected', + 'users_edit' => 'Edit User', + 'users_edit_profile' => 'Edit Profile', + 'users_avatar' => 'User Avatar', + 'users_avatar_desc' => 'Select an image to represent this user. This should be approx 256px square.', + 'users_preferred_language' => 'Preferred Language', + 'users_preferred_language_desc' => 'This option will change the language used for the user-interface of the application. This will not affect any user-created content.', + 'users_social_accounts' => 'Social Accounts', + 'users_social_accounts_desc' => 'View the status of the connected social accounts for this user. Social accounts can be used in addition to the primary authentication system for system access.', + 'users_social_accounts_info' => 'Here you can connect your other accounts for quicker and easier login. Disconnecting an account here does not revoke previously authorized access. Revoke access from your profile settings on the connected social account.', + 'users_social_connect' => 'Connect Account', + 'users_social_disconnect' => 'Disconnect Account', + 'users_social_status_connected' => 'Connected', + 'users_social_status_disconnected' => 'Disconnected', + 'users_social_connected' => ':socialAccount account was successfully attached to your profile.', + 'users_social_disconnected' => ':socialAccount account was successfully disconnected from your profile.', + 'users_api_tokens' => 'API Tokens', + 'users_api_tokens_desc' => 'Create and manage the access tokens used to authenticate with the BookStack REST API. Permissions for the API are managed via the user that the token belongs to.', + 'users_api_tokens_none' => 'No API tokens have been created for this user', + 'users_api_tokens_create' => 'Create Token', + 'users_api_tokens_expires' => 'Expires', + 'users_api_tokens_docs' => 'API Documentation', + 'users_mfa' => 'Multi-Factor Authentication', + 'users_mfa_desc' => 'Setup multi-factor authentication as an extra layer of security for your user account.', + 'users_mfa_x_methods' => ':count method configured|:count methods configured', + 'users_mfa_configure' => 'Configure Methods', + + // API Tokens + 'user_api_token_create' => 'Create API Token', + 'user_api_token_name' => 'Name', + 'user_api_token_name_desc' => 'Give your token a readable name as a future reminder of its intended purpose.', + 'user_api_token_expiry' => 'Expiry Date', + 'user_api_token_expiry_desc' => 'Set a date at which this token expires. After this date, requests made using this token will no longer work. Leaving this field blank will set an expiry 100 years into the future.', + 'user_api_token_create_secret_message' => 'Immediately after creating this token a "Token ID" & "Token Secret" will be generated and displayed. The secret will only be shown a single time so be sure to copy the value to somewhere safe and secure before proceeding.', + 'user_api_token' => 'API Token', + 'user_api_token_id' => 'Token ID', + 'user_api_token_id_desc' => 'This is a non-editable system generated identifier for this token which will need to be provided in API requests.', + 'user_api_token_secret' => 'Token Secret', + 'user_api_token_secret_desc' => 'This is a system generated secret for this token which will need to be provided in API requests. This will only be displayed this one time so copy this value to somewhere safe and secure.', + 'user_api_token_created' => 'Token created :timeAgo', + 'user_api_token_updated' => 'Token updated :timeAgo', + 'user_api_token_delete' => 'Delete Token', + 'user_api_token_delete_warning' => 'This will fully delete this API token with the name \':tokenName\' from the system.', + 'user_api_token_delete_confirm' => 'Are you sure you want to delete this API token?', + + // Webhooks + 'webhooks' => 'Webhooks', + 'webhooks_index_desc' => 'Webhooks are a way to send data to external URLs when certain actions and events occur within the system which allows event-based integration with external platforms such as messaging or notification systems.', + 'webhooks_x_trigger_events' => ':count trigger event|:count trigger events', + 'webhooks_create' => 'Create New Webhook', + 'webhooks_none_created' => 'No webhooks have yet been created.', + 'webhooks_edit' => 'Edit Webhook', + 'webhooks_save' => 'Save Webhook', + 'webhooks_details' => 'Webhook Details', + 'webhooks_details_desc' => 'Provide a user friendly name and a POST endpoint as a location for the webhook data to be sent to.', + 'webhooks_events' => 'Webhook Events', + 'webhooks_events_desc' => 'Select all the events that should trigger this webhook to be called.', + 'webhooks_events_warning' => 'Keep in mind that these events will be triggered for all selected events, even if custom permissions are applied. Ensure that use of this webhook won\'t expose confidential content.', + 'webhooks_events_all' => 'All system events', + 'webhooks_name' => 'Webhook Name', + 'webhooks_timeout' => 'Webhook Request Timeout (Seconds)', + 'webhooks_endpoint' => 'Webhook Endpoint', + 'webhooks_active' => 'Webhook Active', + 'webhook_events_table_header' => 'Events', + 'webhooks_delete' => 'Delete Webhook', + 'webhooks_delete_warning' => 'This will fully delete this webhook, with the name \':webhookName\', from the system.', + 'webhooks_delete_confirm' => 'Are you sure you want to delete this webhook?', + 'webhooks_format_example' => 'Webhook Format Example', + 'webhooks_format_example_desc' => 'Webhook data is sent as a POST request to the configured endpoint as JSON following the format below. The "related_item" and "url" properties are optional and will depend on the type of event triggered.', + 'webhooks_status' => 'Webhook Status', + 'webhooks_last_called' => 'Last Called:', + 'webhooks_last_errored' => 'Last Errored:', + 'webhooks_last_error_message' => 'Last Error Message:', + + // Licensing + 'licenses' => 'Licenses', + 'licenses_desc' => 'This page details license information for BookStack in addition to the projects & libraries that are used within BookStack. Many projects listed may only be used in a development context.', + 'licenses_bookstack' => 'BookStack License', + 'licenses_php' => 'PHP Library Licenses', + 'licenses_js' => 'JavaScript Library Licenses', + 'licenses_other' => 'Other Licenses', + 'license_details' => 'License Details', + + //! If editing translations files directly please ignore this in all + //! languages apart from en. Content will be auto-copied from en. + //!//////////////////////////////// + 'language_select' => [ + 'en' => 'English', + 'ar' => 'العربية', + 'bg' => 'Bǎlgarski', + 'bs' => 'Bosanski', + 'ca' => 'Català', + 'cs' => 'Česky', + 'cy' => 'Cymraeg', + 'da' => 'Dansk', + 'de' => 'Deutsch (Sie)', + 'de_informal' => 'Deutsch (Du)', + 'el' => 'ελληνικά', + 'es' => 'Español', + 'es_AR' => 'Español Argentina', + 'et' => 'Eesti keel', + 'eu' => 'Euskara', + 'fa' => 'فارسی', + 'fi' => 'Suomi', + 'fr' => 'Français', + 'he' => 'עברית', + 'hr' => 'Hrvatski', + 'hu' => 'Magyar', + 'id' => 'Bahasa Indonesia', + 'it' => 'Italian', + 'ja' => '日本語', + 'ko' => '한국어', + 'lt' => 'Lietuvių Kalba', + 'lv' => 'Latviešu Valoda', + 'nb' => 'Norsk (Bokmål)', + 'ne' => 'नेपाली', + 'nn' => 'Nynorsk', + 'nl' => 'Nederlands', + 'pl' => 'Polski', + 'pt' => 'Português', + 'pt_BR' => 'Português do Brasil', + 'ro' => 'Română', + 'ru' => 'Русский', + 'sk' => 'Slovensky', + 'sl' => 'Slovenščina', + 'sv' => 'Svenska', + 'tr' => 'Türkçe', + 'uk' => 'Українська', + 'uz' => 'O‘zbekcha', + 'vi' => 'Tiếng Việt', + 'zh_CN' => '简体中文', + 'zh_TW' => '繁體中文', + ], + //!//////////////////////////////// +]; diff --git a/lang/ku/validation.php b/lang/ku/validation.php new file mode 100644 index 00000000000..d9b982d1e23 --- /dev/null +++ b/lang/ku/validation.php @@ -0,0 +1,122 @@ + 'The :attribute must be accepted.', + 'active_url' => 'The :attribute is not a valid URL.', + 'after' => 'The :attribute must be a date after :date.', + 'alpha' => 'The :attribute may only contain letters.', + 'alpha_dash' => 'The :attribute may only contain letters, numbers, dashes and underscores.', + 'alpha_num' => 'The :attribute may only contain letters and numbers.', + 'array' => 'The :attribute must be an array.', + 'backup_codes' => 'The provided code is not valid or has already been used.', + 'before' => 'The :attribute must be a date before :date.', + 'between' => [ + 'numeric' => 'The :attribute must be between :min and :max.', + 'file' => 'The :attribute must be between :min and :max kilobytes.', + 'string' => 'The :attribute must be between :min and :max characters.', + 'array' => 'The :attribute must have between :min and :max items.', + ], + 'boolean' => 'The :attribute field must be true or false.', + 'confirmed' => 'The :attribute confirmation does not match.', + 'date' => 'The :attribute is not a valid date.', + 'date_format' => 'The :attribute does not match the format :format.', + 'different' => 'The :attribute and :other must be different.', + 'digits' => 'The :attribute must be :digits digits.', + 'digits_between' => 'The :attribute must be between :min and :max digits.', + 'email' => 'The :attribute must be a valid email address.', + 'ends_with' => 'The :attribute must end with one of the following: :values', + 'file' => 'The :attribute must be provided as a valid file.', + 'filled' => 'The :attribute field is required.', + 'gt' => [ + 'numeric' => 'The :attribute must be greater than :value.', + 'file' => 'The :attribute must be greater than :value kilobytes.', + 'string' => 'The :attribute must be greater than :value characters.', + 'array' => 'The :attribute must have more than :value items.', + ], + 'gte' => [ + 'numeric' => 'The :attribute must be greater than or equal :value.', + 'file' => 'The :attribute must be greater than or equal :value kilobytes.', + 'string' => 'The :attribute must be greater than or equal :value characters.', + 'array' => 'The :attribute must have :value items or more.', + ], + 'exists' => 'The selected :attribute is invalid.', + 'image' => 'The :attribute must be an image.', + 'image_extension' => 'The :attribute must have a valid & supported image extension.', + 'in' => 'The selected :attribute is invalid.', + 'integer' => 'The :attribute must be an integer.', + 'ip' => 'The :attribute must be a valid IP address.', + 'ipv4' => 'The :attribute must be a valid IPv4 address.', + 'ipv6' => 'The :attribute must be a valid IPv6 address.', + 'json' => 'The :attribute must be a valid JSON string.', + 'lt' => [ + 'numeric' => 'The :attribute must be less than :value.', + 'file' => 'The :attribute must be less than :value kilobytes.', + 'string' => 'The :attribute must be less than :value characters.', + 'array' => 'The :attribute must have less than :value items.', + ], + 'lte' => [ + 'numeric' => 'The :attribute must be less than or equal :value.', + 'file' => 'The :attribute must be less than or equal :value kilobytes.', + 'string' => 'The :attribute must be less than or equal :value characters.', + 'array' => 'The :attribute must not have more than :value items.', + ], + 'max' => [ + 'numeric' => 'The :attribute may not be greater than :max.', + 'file' => 'The :attribute may not be greater than :max kilobytes.', + 'string' => 'The :attribute may not be greater than :max characters.', + 'array' => 'The :attribute may not have more than :max items.', + ], + 'mimes' => 'The :attribute must be a file of type: :values.', + 'min' => [ + 'numeric' => 'The :attribute must be at least :min.', + 'file' => 'The :attribute must be at least :min kilobytes.', + 'string' => 'The :attribute must be at least :min characters.', + 'array' => 'The :attribute must have at least :min items.', + ], + 'not_in' => 'The selected :attribute is invalid.', + 'not_regex' => 'The :attribute format is invalid.', + 'numeric' => 'The :attribute must be a number.', + 'regex' => 'The :attribute format is invalid.', + 'required' => 'The :attribute field is required.', + 'required_if' => 'The :attribute field is required when :other is :value.', + 'required_with' => 'The :attribute field is required when :values is present.', + 'required_with_all' => 'The :attribute field is required when :values is present.', + 'required_without' => 'The :attribute field is required when :values is not present.', + 'required_without_all' => 'The :attribute field is required when none of :values are present.', + 'same' => 'The :attribute and :other must match.', + 'safe_url' => 'The provided link may not be safe.', + 'size' => [ + 'numeric' => 'The :attribute must be :size.', + 'file' => 'The :attribute must be :size kilobytes.', + 'string' => 'The :attribute must be :size characters.', + 'array' => 'The :attribute must contain :size items.', + ], + 'string' => 'The :attribute must be a string.', + 'timezone' => 'The :attribute must be a valid zone.', + 'totp' => 'The provided code is not valid or has expired.', + 'unique' => 'The :attribute has already been taken.', + 'url' => 'The :attribute format is invalid.', + 'uploaded' => 'The file could not be uploaded. The server may not accept files of this size.', + + 'zip_file' => 'The :attribute needs to reference a file within the ZIP.', + 'zip_file_mime' => 'The :attribute needs to reference a file of type :validTypes, found :foundType.', + 'zip_model_expected' => 'Data object expected but ":type" found.', + 'zip_unique' => 'The :attribute must be unique for the object type within the ZIP.', + + // Custom validation lines + 'custom' => [ + 'password-confirm' => [ + 'required_with' => 'Password confirmation required', + ], + ], + + // Custom validation attributes + 'attributes' => [], +]; diff --git a/lang/lt/activities.php b/lang/lt/activities.php index 42322ffbc85..a808171bb6d 100644 --- a/lang/lt/activities.php +++ b/lang/lt/activities.php @@ -84,6 +84,14 @@ 'webhook_delete' => 'deleted webhook', 'webhook_delete_notification' => 'Webhook successfully deleted', + // Imports + 'import_create' => 'created import', + 'import_create_notification' => 'Import successfully uploaded', + 'import_run' => 'updated import', + 'import_run_notification' => 'Content successfully imported', + 'import_delete' => 'deleted import', + 'import_delete_notification' => 'Import successfully deleted', + // Users 'user_create' => 'created user', 'user_create_notification' => 'User successfully created', @@ -93,11 +101,11 @@ 'user_delete_notification' => 'User successfully removed', // API Tokens - 'api_token_create' => 'created api token', + 'api_token_create' => 'created API token', 'api_token_create_notification' => 'API token successfully created', - 'api_token_update' => 'updated api token', + 'api_token_update' => 'updated API token', 'api_token_update_notification' => 'API token successfully updated', - 'api_token_delete' => 'deleted api token', + 'api_token_delete' => 'deleted API token', 'api_token_delete_notification' => 'API token successfully deleted', // Roles @@ -119,6 +127,14 @@ 'comment_update' => 'updated comment', 'comment_delete' => 'deleted comment', + // Sort Rules + 'sort_rule_create' => 'created sort rule', + 'sort_rule_create_notification' => 'Sort rule successfully created', + 'sort_rule_update' => 'updated sort rule', + 'sort_rule_update_notification' => 'Sort rule successfully updated', + 'sort_rule_delete' => 'deleted sort rule', + 'sort_rule_delete_notification' => 'Sort rule successfully deleted', + // Other 'permissions_update' => 'atnaujinti leidimai', ]; diff --git a/lang/lt/auth.php b/lang/lt/auth.php index 02e64e25035..a2f7f466846 100644 --- a/lang/lt/auth.php +++ b/lang/lt/auth.php @@ -91,7 +91,7 @@ 'mfa_option_totp_title' => 'Mobile App', 'mfa_option_totp_desc' => 'To use multi-factor authentication you\'ll need a mobile application that supports TOTP such as Google Authenticator, Authy or Microsoft Authenticator.', 'mfa_option_backup_codes_title' => 'Backup Codes', - 'mfa_option_backup_codes_desc' => 'Securely store a set of one-time-use backup codes which you can enter to verify your identity.', + 'mfa_option_backup_codes_desc' => 'Generates a set of one-time-use backup codes which you\'ll enter on login to verify your identity. Make sure to store these in a safe & secure place.', 'mfa_gen_confirm_and_enable' => 'Confirm and Enable', 'mfa_gen_backup_codes_title' => 'Backup Codes Setup', 'mfa_gen_backup_codes_desc' => 'Store the below list of codes in a safe place. When accessing the system you\'ll be able to use one of the codes as a second authentication mechanism.', diff --git a/lang/lt/common.php b/lang/lt/common.php index 8ef5b5d8abd..bbafc347c59 100644 --- a/lang/lt/common.php +++ b/lang/lt/common.php @@ -6,7 +6,7 @@ // Buttons 'cancel' => 'Atšaukti', - 'close' => 'Close', + 'close' => 'Uždaryti', 'confirm' => 'Patvirtinti', 'back' => 'Grįžti', 'save' => 'Išsaugoti', @@ -20,7 +20,7 @@ 'description' => 'Apibūdinimas', 'role' => 'Vaidmuo', 'cover_image' => 'Viršelio nuotrauka', - 'cover_image_description' => 'Ši nuotrauka turi būti maždaug 440x250px.', + 'cover_image_description' => 'This image should be approximately 440x250px although it will be flexibly scaled & cropped to fit the user interface in different scenarios as required, so actual dimensions for display will differ.', // Actions 'actions' => 'Veiksmai', @@ -30,6 +30,8 @@ 'create' => 'Sukurti', 'update' => 'Atnaujinti', 'edit' => 'Redaguoti', + 'archive' => 'Archive', + 'unarchive' => 'Un-Archive', 'sort' => 'Rūšiuoti', 'move' => 'Perkelti', 'copy' => 'Kopijuoti', @@ -107,4 +109,7 @@ // Not directly used but available for convenience to users. 'privacy_policy' => 'Privatumo politika', 'terms_of_service' => 'Paslaugų teikimo paslaugos', + + // OpenSearch + 'opensearch_description' => 'Search :appName', ]; diff --git a/lang/lt/editor.php b/lang/lt/editor.php index 670c1c5e1ac..0d250e9a7bd 100644 --- a/lang/lt/editor.php +++ b/lang/lt/editor.php @@ -13,6 +13,7 @@ 'cancel' => 'Cancel', 'save' => 'Save', 'close' => 'Close', + 'apply' => 'Apply', 'undo' => 'Undo', 'redo' => 'Redo', 'left' => 'Left', @@ -47,6 +48,7 @@ 'superscript' => 'Superscript', 'subscript' => 'Subscript', 'text_color' => 'Text color', + 'highlight_color' => 'Highlight color', 'custom_color' => 'Custom color', 'remove_color' => 'Remove color', 'background_color' => 'Background color', @@ -81,6 +83,9 @@ 'table_properties' => 'Table properties', 'table_properties_title' => 'Table Properties', 'delete_table' => 'Delete table', + 'table_clear_formatting' => 'Clear table formatting', + 'resize_to_contents' => 'Resize to contents', + 'row_header' => 'Row header', 'insert_row_before' => 'Insert row before', 'insert_row_after' => 'Insert row after', 'delete_row' => 'Delete row', @@ -144,6 +149,7 @@ 'url' => 'URL', 'text_to_display' => 'Text to display', 'title' => 'Title', + 'browse_links' => 'Browse links', 'open_link' => 'Open link', 'open_link_in' => 'Open link in...', 'open_link_current' => 'Current window', @@ -160,6 +166,8 @@ 'about' => 'About the editor', 'about_title' => 'About the WYSIWYG Editor', 'editor_license' => 'Editor License & Copyright', + 'editor_lexical_license' => 'This editor is built as a fork of :lexicalLink which is distributed under the MIT license.', + 'editor_lexical_license_link' => 'Full license details can be found here.', 'editor_tiny_license' => 'This editor is built using :tinyLink which is provided under the MIT license.', 'editor_tiny_license_link' => 'The copyright and license details of TinyMCE can be found here.', 'save_continue' => 'Save Page & Continue', diff --git a/lang/lt/entities.php b/lang/lt/entities.php index 9880ebb7999..5624567811f 100644 --- a/lang/lt/entities.php +++ b/lang/lt/entities.php @@ -39,6 +39,34 @@ 'export_pdf' => 'PDF failas', 'export_text' => 'Paprastas failo tekstas', 'export_md' => 'Markdown File', + 'export_zip' => 'Portable ZIP', + 'default_template' => 'Default Page Template', + 'default_template_explain' => 'Assign a page template that will be used as the default content for all pages created within this item. Keep in mind this will only be used if the page creator has view access to the chosen template page.', + 'default_template_select' => 'Select a template page', + 'import' => 'Import', + 'import_validate' => 'Validate Import', + 'import_desc' => 'Import books, chapters & pages using a portable zip export from the same, or a different, instance. Select a ZIP file to proceed. After the file has been uploaded and validated you\'ll be able to configure & confirm the import in the next view.', + '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_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_size' => ':size Import ZIP Size', + 'import_uploaded_at' => 'Uploaded :relativeTime', + 'import_uploaded_by' => 'Uploaded by', + 'import_location' => 'Import Location', + '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_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', + 'breadcrumb_siblings_for_book' => 'Navigate siblings for book', + 'breadcrumb_siblings_for_bookshelf' => 'Navigate siblings for shelf', // Permissions and restrictions 'permissions' => 'Leidimai', @@ -132,9 +160,6 @@ 'books_edit_named' => 'Redaguoti knygą :bookName', 'books_form_book_name' => 'Knygos pavadinimas', 'books_save' => 'Išsaugoti knygą', - 'books_default_template' => 'Default Page Template', - 'books_default_template_explain' => 'Assign a page template that will be used as the default content for all new pages in this book. Keep in mind this will only be used if the page creator has view access to those chosen template page.', - 'books_default_template_select' => 'Select a template page', 'books_permissions' => 'Knygos leidimas', 'books_permissions_updated' => 'Knygos leidimas atnaujintas', 'books_empty_contents' => 'Jokių puslapių ar skyrių nebuvo skurta šiai knygai', @@ -145,7 +170,9 @@ 'books_search_this' => 'Ieškoti šioje knygoje', 'books_navigation' => 'Knygos naršymas', 'books_sort' => 'Rūšiuoti pagal knygos turinį', - 'books_sort_desc' => 'Move chapters and pages within a book to reorganise its contents. Other books can be added which allows easy moving of chapters and pages between books.', + 'books_sort_desc' => 'Move chapters and pages within a book to reorganise its contents. Other books can be added which allows easy moving of chapters and pages between books. Optionally an auto sort rule can be set to automatically sort this book\'s contents upon changes.', + 'books_sort_auto_sort' => 'Auto Sort Option', + 'books_sort_auto_sort_active' => 'Auto Sort Active: :sortName', 'books_sort_named' => 'Rūšiuoti knygą :bookName', 'books_sort_name' => 'Rūšiuoti pagal vardą', 'books_sort_created' => 'Rūšiuoti pagal sukūrimo datą', @@ -207,7 +234,7 @@ 'pages_delete_draft' => 'Ištrinti juodraščio puslapį', 'pages_delete_success' => 'Puslapis ištrintas', 'pages_delete_draft_success' => 'Juodraščio puslapis ištrintas', - 'pages_delete_warning_template' => 'This page is in active use as a book default page template. These books will no longer have a default page template assigned after this page is deleted.', + 'pages_delete_warning_template' => 'This page is in active use as a book or chapter default page template. These books or chapters will no longer have a default page template assigned after this page is deleted.', 'pages_delete_confirm' => 'Ar esate tikri, kad norite ištrinti šį puslapį?', 'pages_delete_draft_confirm' => 'Ar esate tikri, kad norite ištrinti šį juodraščio puslapį?', 'pages_editing_named' => 'Redaguojamas puslapis :pageName', @@ -224,6 +251,8 @@ 'pages_edit_switch_to_markdown_clean' => '(Clean Content)', 'pages_edit_switch_to_markdown_stable' => '(Stable Content)', 'pages_edit_switch_to_wysiwyg' => 'Switch to WYSIWYG Editor', + 'pages_edit_switch_to_new_wysiwyg' => 'Switch to new WYSIWYG', + 'pages_edit_switch_to_new_wysiwyg_desc' => '(In Beta Testing)', 'pages_edit_set_changelog' => 'Nustatyti keitimo žurnalą', 'pages_edit_enter_changelog_desc' => 'Įveskite trumpus, jūsų atliktus, pokyčių aprašymus', 'pages_edit_enter_changelog' => 'Įeiti į keitimo žurnalą', @@ -243,6 +272,7 @@ 'pages_md_insert_drawing' => 'Įterpti piešinį', 'pages_md_show_preview' => 'Show preview', 'pages_md_sync_scroll' => 'Sync preview scroll', + 'pages_md_plain_editor' => 'Plaintext editor', 'pages_drawing_unsaved' => 'Unsaved Drawing Found', 'pages_drawing_unsaved_confirm' => 'Unsaved drawing data was found from a previous failed drawing save attempt. Would you like to restore and continue editing this unsaved drawing?', 'pages_not_in_chapter' => 'Puslapio nėra skyriuje', @@ -367,8 +397,11 @@ 'comment' => 'Komentaras', 'comments' => 'Komentarai', 'comment_add' => 'Pridėti komentarą', + 'comment_none' => 'No comments to display', 'comment_placeholder' => 'Palikite komentarą čia', - 'comment_count' => '{0} Nėra komentarų|{1} 1 komentaras|[2,*] :count komentarai', + 'comment_thread_count' => ':count Comment Thread|:count Comment Threads', + 'comment_archived_count' => ':count Archived', + 'comment_archived_threads' => 'Archived Threads', 'comment_save' => 'Išsaugoti komentarą', 'comment_new' => 'Naujas komentaras', 'comment_created' => 'Pakomentuota :createDiff', @@ -377,8 +410,14 @@ 'comment_deleted_success' => 'Komentaras ištrintas', 'comment_created_success' => 'Komentaras pridėtas', 'comment_updated_success' => 'Komentaras atnaujintas', + 'comment_archive_success' => 'Comment archived', + 'comment_unarchive_success' => 'Comment un-archived', + 'comment_view' => 'View comment', + 'comment_jump_to_thread' => 'Jump to thread', 'comment_delete_confirm' => 'Esate tikri, kad norite ištrinti šį komentarą?', 'comment_in_reply_to' => 'Atsakydamas į :commentId', + 'comment_reference' => 'Reference', + 'comment_reference_outdated' => '(Outdated)', 'comment_editor_explain' => 'Here are the comments that have been left on this page. Comments can be added & managed when viewing the saved page.', // Revision diff --git a/lang/lt/errors.php b/lang/lt/errors.php index 0e14b6517de..392e99a5192 100644 --- a/lang/lt/errors.php +++ b/lang/lt/errors.php @@ -10,6 +10,7 @@ // Auth 'error_user_exists_different_creds' => 'Naudotojo elektroninis paštas :email jau egzistuoja, bet su kitokiais įgaliojimais.', + 'auth_pre_register_theme_prevention' => 'User account could not be registered for the provided details', 'email_already_confirmed' => 'Elektroninis paštas jau buvo patvirtintas, pabandykite prisijungti.', 'email_confirmation_invalid' => 'Šis patvirtinimo prieigos raktas negalioja arba jau buvo panaudotas, prašome bandykite vėl registruotis.', 'email_confirmation_expired' => 'Šis patvirtinimo prieigos raktas baigė galioti, naujas patvirtinimo laiškas jau išsiųstas elektroniniu paštu.', @@ -23,7 +24,6 @@ 'saml_invalid_response_id' => 'Prašymas iš išorinės autentifikavimo sistemos nėra atpažintas proceso, kurį pradėjo ši programa. Naršymas po prisijungimo gali sukelti šią problemą.', 'saml_fail_authed' => 'Prisijungimas, naudojant :system nepavyko, sistema nepateikė sėkmingo leidimo.', 'oidc_already_logged_in' => 'Already logged in', - 'oidc_user_not_registered' => 'The user :name is not registered and automatic registration is disabled', 'oidc_no_email_address' => 'Could not find an email address, for this user, in the data provided by the external authentication system', 'oidc_fail_authed' => 'Login using :system failed, system did not provide successful authorization', 'social_no_action_defined' => 'Neapibrėžtas joks veiksmas', @@ -37,6 +37,7 @@ 'social_driver_not_found' => 'Socialinis diskas nerastas', 'social_driver_not_configured' => 'Jūsų :socialAccount socaliniai nustatymai sukonfigūruoti neteisingai.', 'invite_token_expired' => 'Ši kvietimo nuoroda baigė galioti. Vietoj to, jūs galite bandyti iš naujo nustatyti savo paskyros slaptažodį.', + 'login_user_not_found' => 'A user for this action could not be found.', // System 'path_not_writable' => 'Į failo kelią :filePath negalima įkelti. Įsitikinkite, kad jis yra įrašomas į serverį.', @@ -77,6 +78,7 @@ // Users 'users_cannot_delete_only_admin' => 'Negalite ištrinti vienintelio administratoriaus', 'users_cannot_delete_guest' => 'Negalite ištrinti svečio naudotojo', + 'users_could_not_send_invite' => 'Could not create user since invite email failed to send', // Roles 'role_cannot_be_edited' => 'Šio vaidmens negalima redaguoti', @@ -103,6 +105,18 @@ 'app_down' => ':appName dabar yra apačioje', 'back_soon' => 'Tai sugrįž greitai', + // Import + 'import_zip_cant_read' => 'Could not read ZIP file.', + 'import_zip_cant_decode_data' => 'Could not find and decode ZIP data.json content.', + 'import_zip_no_data' => 'ZIP file data has no expected book, chapter or page content.', + 'import_validation_failed' => 'Import ZIP failed to validate with errors:', + 'import_zip_failed_notification' => 'Failed to import ZIP file.', + 'import_perms_books' => 'You are lacking the required permissions to create books.', + 'import_perms_chapters' => 'You are lacking the required permissions to create chapters.', + 'import_perms_pages' => 'You are lacking the required permissions to create pages.', + 'import_perms_images' => 'You are lacking the required permissions to create images.', + 'import_perms_attachments' => 'You are lacking the required permission to create attachments.', + // API errors 'api_no_authorization_found' => 'Užklausoje nerastas įgaliojimo prieigos raktas', 'api_bad_authorization_format' => 'Užklausoje rastas prieigos raktas, tačiau formatas yra neteisingas', diff --git a/lang/lt/preferences.php b/lang/lt/preferences.php index 2b88f9671ff..5c38d191c0b 100644 --- a/lang/lt/preferences.php +++ b/lang/lt/preferences.php @@ -5,14 +5,14 @@ */ return [ - 'my_account' => 'My Account', + 'my_account' => 'Mano paskyra', 'shortcuts' => 'Shortcuts', 'shortcuts_interface' => 'UI Shortcut Preferences', 'shortcuts_toggle_desc' => 'Here you can enable or disable keyboard system interface shortcuts, used for navigation and actions.', 'shortcuts_customize_desc' => 'You can customize each of the shortcuts below. Just press your desired key combination after selecting the input for a shortcut.', 'shortcuts_toggle_label' => 'Keyboard shortcuts enabled', - 'shortcuts_section_navigation' => 'Navigation', + 'shortcuts_section_navigation' => 'Navigacija', 'shortcuts_section_actions' => 'Common Actions', 'shortcuts_save' => 'Save Shortcuts', 'shortcuts_overlay_desc' => 'Note: When shortcuts are enabled a helper overlay is available via pressing "?" which will highlight the available shortcuts for actions currently visible on the screen.', @@ -27,16 +27,16 @@ 'notifications_save' => 'Save Preferences', 'notifications_update_success' => 'Notification preferences have been updated!', 'notifications_watched' => 'Watched & Ignored Items', - 'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.', + 'notifications_watched_desc' => 'Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.', - 'auth' => 'Access & Security', - 'auth_change_password' => 'Change Password', - 'auth_change_password_desc' => 'Change the password you use to log-in to the application. This must be at least 8 characters long.', - 'auth_change_password_success' => 'Password has been updated!', + 'auth' => 'Prieiga ir saugumas', + 'auth_change_password' => 'Pasikeisti slaptažodį', + 'auth_change_password_desc' => 'Pakeistas slaptažodis bus naudojamas prisijungti prie aplikacijos. Slaptažodis turi būti bent 8 simbolių ilgio.', + 'auth_change_password_success' => 'Slaptažodis atnaujintas!', - 'profile' => 'Profile Details', + 'profile' => 'Profilio informacija', 'profile_desc' => 'Manage the details of your account which represents you to other users, in addition to details that are used for communication and system personalisation.', - 'profile_view_public' => 'View Public Profile', + 'profile_view_public' => 'Rodyti viešąjį profilį', 'profile_name_desc' => 'Configure your display name which will be visible to other users in the system through the activity you perform, and content you own.', 'profile_email_desc' => 'This email will be used for notifications and, depending on active system authentication, system access.', 'profile_email_no_permission' => 'Unfortunately you don\'t have permission to change your email address. If you want to change this, you\'d need to ask an administrator to change this for you.', @@ -44,8 +44,8 @@ 'profile_admin_options' => 'Administrator Options', 'profile_admin_options_desc' => 'Additional administrator-level options, like those to manage role assignments, can be found for your user account in the "Settings > Users" area of the application.', - 'delete_account' => 'Delete Account', - 'delete_my_account' => 'Delete My Account', + 'delete_account' => 'Ištrinti paskyrą', + 'delete_my_account' => 'Ištrinti mano paskyrą', 'delete_my_account_desc' => 'This will fully delete your user account from the system. You will not be able to recover this account or revert this action. Content you\'ve created, such as created pages and uploaded images, will remain.', 'delete_my_account_warning' => 'Are you sure you want to delete your account?', ]; diff --git a/lang/lt/settings.php b/lang/lt/settings.php index 000e018c6ae..563038bf267 100644 --- a/lang/lt/settings.php +++ b/lang/lt/settings.php @@ -74,6 +74,36 @@ 'reg_confirm_restrict_domain_desc' => 'Įveskite kableliais atskirtą elektroninio pašto domenų, kurių registravimą norite apriboti, sąrašą. Vartotojai išsiųs elektorinį laišką, kad patvirtintumėte jų adresą prieš leidžiant naudotis programa.
Prisiminkite, kad vartotojai galės pakeisti savo elektroninius paštus po sėkmingos registracijos.', 'reg_confirm_restrict_domain_placeholder' => 'Nėra jokių apribojimų', + // Sorting Settings + 'sorting' => 'Sorting', + 'sorting_book_default' => 'Default Book Sort', + 'sorting_book_default_desc' => 'Select the default sort rule to apply to new books. This won\'t affect existing books, and can be overridden per-book.', + 'sorting_rules' => 'Sort Rules', + 'sorting_rules_desc' => 'These are predefined sorting operations which can be applied to content in the system.', + 'sort_rule_assigned_to_x_books' => 'Assigned to :count Book|Assigned to :count Books', + 'sort_rule_create' => 'Create Sort Rule', + 'sort_rule_edit' => 'Edit Sort Rule', + 'sort_rule_delete' => 'Delete Sort Rule', + 'sort_rule_delete_desc' => 'Remove this sort rule from the system. Books using this sort will revert to manual sorting.', + 'sort_rule_delete_warn_books' => 'This sort rule is currently used on :count book(s). Are you sure you want to delete this?', + 'sort_rule_delete_warn_default' => 'This sort rule is currently used as the default for books. Are you sure you want to delete this?', + 'sort_rule_details' => 'Sort Rule Details', + 'sort_rule_details_desc' => 'Set a name for this sort rule, which will appear in lists when users are selecting a sort.', + 'sort_rule_operations' => 'Sort Operations', + 'sort_rule_operations_desc' => 'Configure the sort actions to be performed by moving them from the list of available operations. Upon use, the operations will be applied in order, from top to bottom. Any changes made here will be applied to all assigned books upon save.', + 'sort_rule_available_operations' => 'Available Operations', + 'sort_rule_available_operations_empty' => 'No operations remaining', + 'sort_rule_configured_operations' => 'Configured Operations', + 'sort_rule_configured_operations_empty' => 'Drag/add operations from the "Available Operations" list', + 'sort_rule_op_asc' => '(Asc)', + 'sort_rule_op_desc' => '(Desc)', + 'sort_rule_op_name' => 'Name - Alphabetical', + 'sort_rule_op_name_numeric' => '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_last' => 'Chapters Last', + // Maintenance settings 'maint' => 'Priežiūra', 'maint_image_cleanup' => 'Išvalykite vaizdus', @@ -109,7 +139,7 @@ 'recycle_bin_contents_empty' => 'Šiukšliadėžė šiuo metu yra tuščia', 'recycle_bin_empty' => 'Ištuštinti šiukšliadėžę', 'recycle_bin_empty_confirm' => 'Tai visam laikui sunaikins visus elementus, esančius šiukšliadėžėje, įskaitant kiekvieno elemento turinį. Ar esate tikri, jog norite ištuštinti šiukšliadėžę?', - 'recycle_bin_destroy_confirm' => 'Šis veiksmas visam laikui ištrins šį elementą iš sistemos kartu su bet kuriais elementais įvardintais žemiau ir jūs nebegalėsite atkurti jo bei jo turinio. Ar esate tikri, jog norite visam laikui ištrinti šį elementą?', + 'recycle_bin_destroy_confirm' => 'This action will permanently delete this item from the system, along with any child elements listed below, and you will not be able to restore this content. Are you sure you want to permanently delete this item?', 'recycle_bin_destroy_list' => 'Elementai panaikinimui', 'recycle_bin_restore_list' => 'Elementai atkūrimui', 'recycle_bin_restore_confirm' => 'Šis veiksmas atkurs ištrintą elementą ir perkels jį atgal į jo originalią vietą. Jei originali vieta buvo ištrinta ir šiuo metu yra šiukšliadėžėje, ji taip pat turės būti atkurta.', @@ -162,6 +192,7 @@ 'role_access_api' => 'Gauti prieigą prie sistemos API', 'role_manage_settings' => 'Tvarkyti programos nustatymus', 'role_export_content' => 'Export content', + 'role_import_content' => 'Import content', 'role_editor_change' => 'Change page editor', 'role_notifications' => 'Receive & manage notifications', 'role_asset' => 'Nuosavybės leidimai', @@ -276,6 +307,14 @@ 'webhooks_last_errored' => 'Last Errored:', 'webhooks_last_error_message' => 'Last Error Message:', + // Licensing + 'licenses' => 'Licenses', + 'licenses_desc' => 'This page details license information for BookStack in addition to the projects & libraries that are used within BookStack. Many projects listed may only be used in a development context.', + 'licenses_bookstack' => 'BookStack License', + 'licenses_php' => 'PHP Library Licenses', + 'licenses_js' => 'JavaScript Library Licenses', + 'licenses_other' => 'Other Licenses', + 'license_details' => 'License Details', //! If editing translations files directly please ignore this in all //! languages apart from en. Content will be auto-copied from en. @@ -287,6 +326,7 @@ 'bs' => 'Bosanski', 'ca' => 'Català', 'cs' => 'Česky', + 'cy' => 'Cymraeg', 'da' => 'Dansk', 'de' => 'Deutsch (Sie)', 'de_informal' => 'Deutsch (Du)', @@ -308,6 +348,7 @@ 'lt' => 'Lietuvių Kalba', 'lv' => 'Latviešu Valoda', 'nb' => 'Norsk (Bokmål)', + 'ne' => 'नेपाली', 'nn' => 'Nynorsk', 'nl' => 'Nederlands', 'pl' => 'Polski', diff --git a/lang/lt/validation.php b/lang/lt/validation.php index 08470cf2291..92de23004aa 100644 --- a/lang/lt/validation.php +++ b/lang/lt/validation.php @@ -105,6 +105,11 @@ 'url' => ':attribute formatas yra klaidingas.', 'uploaded' => 'Šis failas negali būti įkeltas. Serveris gali nepriimti tokio dydžio failų.', + 'zip_file' => 'The :attribute needs to reference a file within the ZIP.', + 'zip_file_mime' => 'The :attribute needs to reference a file of type :validTypes, found :foundType.', + 'zip_model_expected' => 'Data object expected but ":type" found.', + 'zip_unique' => 'The :attribute must be unique for the object type within the ZIP.', + // Custom validation lines 'custom' => [ 'password-confirm' => [ diff --git a/lang/lv/activities.php b/lang/lv/activities.php index 742288c98dc..38d9ae21766 100644 --- a/lang/lv/activities.php +++ b/lang/lv/activities.php @@ -59,11 +59,11 @@ 'favourite_remove_notification' => '":name" ir izņemts no jūsu favorītiem', // Watching - 'watch_update_level_notification' => 'Watch preferences successfully updated', + 'watch_update_level_notification' => 'Skatīšanas uzstādījumi veiksmīgi atjaunināti', // Auth - 'auth_login' => 'logged in', - 'auth_register' => 'registered as new user', + 'auth_login' => 'pieteicies', + 'auth_register' => 'reģistrējies kā jauns lietotājs', 'auth_password_reset_request' => 'pieprasīja lietotāja paroles atiestatīšanu', 'auth_password_reset_update' => 'atiestatīja lietotāja paroli', 'mfa_setup_method' => 'uzstādīja MFA metodi', @@ -84,6 +84,14 @@ 'webhook_delete' => 'izdzēsa webhook', 'webhook_delete_notification' => 'Webhook veiksmīgi izdzēsts', + // Imports + 'import_create' => 'izveidoja importu', + 'import_create_notification' => 'Imports veiksmīgi augšupielādēts', + 'import_run' => 'atjaunoja importu', + 'import_run_notification' => 'Saturs veiksmīgi importēts', + 'import_delete' => 'izdzēsa importu', + 'import_delete_notification' => 'Imports veiksmīgi dzēsts', + // Users 'user_create' => 'izveidoja lietotāju', 'user_create_notification' => 'Lietotājs veiksmīgi izveidots', @@ -97,7 +105,7 @@ 'api_token_create_notification' => 'API žetons veiksmīgi izveidots', 'api_token_update' => 'atjaunoja API žetonu', 'api_token_update_notification' => 'API žetons veiksmīgi atjaunināts', - 'api_token_delete' => 'dzēsa API žetonu', + 'api_token_delete' => 'izdzēsa API žetonu', 'api_token_delete_notification' => 'API žetons veiksmīgi dzēsts', // Roles @@ -119,6 +127,14 @@ 'comment_update' => 'atjaunoja komentārju', 'comment_delete' => 'dzēsa komentāru', + // Sort Rules + 'sort_rule_create' => 'izveidoja kārtošanas nosacījumu', + 'sort_rule_create_notification' => 'Kārtošanas nosacījums veiksmīgi izveidots', + 'sort_rule_update' => 'atjaunoja kārtošanas nosacījumu', + 'sort_rule_update_notification' => 'Kārtošanas nosacījums veiksmīgi atjaunots', + 'sort_rule_delete' => 'izdzēsa kārtošanas nosacījumu', + 'sort_rule_delete_notification' => 'Kārtošanas nosacījums veiksmīgi dzēsts', + // Other 'permissions_update' => 'atjaunoja atļaujas', ]; diff --git a/lang/lv/auth.php b/lang/lv/auth.php index d13d93012eb..71b92ac0109 100644 --- a/lang/lv/auth.php +++ b/lang/lv/auth.php @@ -91,7 +91,7 @@ 'mfa_option_totp_title' => 'Mobilā aplikācija', 'mfa_option_totp_desc' => 'Lai lietotu vairākfaktoru autentifikāciju, jums būs nepieciešama mobilā aplikācija, kas atbalsta TOTP, piemēram, Google Authenticator, Authy vai Microsoft Authenticator.', 'mfa_option_backup_codes_title' => 'Rezerves kodi', - 'mfa_option_backup_codes_desc' => 'Droši noglabājiet vienreizlietojamu rezerves kodu komplektu, ko varēsiet izmantot, lai verificētu savu identitāti.', + 'mfa_option_backup_codes_desc' => 'Izveido vienreizējas lietošanas rezerves kodus, ko var izmantot pierakstoties, lai apstiprinātu savu identitāti. Pārliecinieties, ka tie ir noglabāti drošā vietā.', 'mfa_gen_confirm_and_enable' => 'Apstiprināt un ieslēgt', 'mfa_gen_backup_codes_title' => 'Rezerves kodu iestatījumi', 'mfa_gen_backup_codes_desc' => 'Noglabājiet zemāk esošo kodu sarakstu drošā vietā. Kad piekļūsiet sistēmai, jūs varēsiet izmantot vienu no kodiem kā papildus autentifikācijas mehānismu.', diff --git a/lang/lv/common.php b/lang/lv/common.php index 01abea4f388..4917ad3d717 100644 --- a/lang/lv/common.php +++ b/lang/lv/common.php @@ -20,7 +20,7 @@ 'description' => 'Apraksts', 'role' => 'Loma', 'cover_image' => 'Vāka attēls', - 'cover_image_description' => 'Šim attēlam būtu jābūt aptuveni 440x250px.', + 'cover_image_description' => 'Šim attēlam jābūt apmēram 440x250px izmērā, taču tas tiks pielāgots lietotāja saskarnei dažādos scenārijos pēc nepieciešamības, un attēla izmēri tad var atšķirties.', // Actions 'actions' => 'Darbības', @@ -30,6 +30,8 @@ 'create' => 'Izveidot', 'update' => 'Atjaunināt', 'edit' => 'Rediģēt', + 'archive' => 'Archive', + 'unarchive' => 'Un-Archive', 'sort' => 'Kārtot', 'move' => 'Pārvietot', 'copy' => 'Kopēt', @@ -107,4 +109,7 @@ // Not directly used but available for convenience to users. 'privacy_policy' => 'Privātuma politika', 'terms_of_service' => 'Pakalpojuma noteikumi', + + // OpenSearch + 'opensearch_description' => 'Meklēt :appName', ]; diff --git a/lang/lv/editor.php b/lang/lv/editor.php index 9a9026b228d..8b2907ddd2a 100644 --- a/lang/lv/editor.php +++ b/lang/lv/editor.php @@ -13,6 +13,7 @@ 'cancel' => 'Atcelt', 'save' => 'Saglabāt', 'close' => 'Aizvērt', + 'apply' => 'Pielietot', 'undo' => 'Atsaukt', 'redo' => 'Atcelt atsaukšanu', 'left' => 'Pa kreisi', @@ -47,6 +48,7 @@ 'superscript' => 'Augšraksts', 'subscript' => 'Apakšraksts', 'text_color' => 'Teksta krāsa', + 'highlight_color' => 'Highlight color', 'custom_color' => 'Pielāgot krāsu', 'remove_color' => 'Noņemt krāsu', 'background_color' => 'Fona krāsa', @@ -81,6 +83,9 @@ 'table_properties' => 'Tabulas īpašības', 'table_properties_title' => 'Tabulas īpašības', 'delete_table' => 'Dzēst tabulu', + 'table_clear_formatting' => 'Notīrīt tabulas formatējumu', + 'resize_to_contents' => 'Pielāgot izmēru saturam', + 'row_header' => 'Rindas galvene', 'insert_row_before' => 'Ievietot rindu augstāk', 'insert_row_after' => 'Ievietot rindu zemāk', 'delete_row' => 'Dzēst rindu', @@ -144,6 +149,7 @@ 'url' => 'URL', 'text_to_display' => 'Attēlojamais teksts', 'title' => 'Nosaukums', + 'browse_links' => 'Pārlūkot saites', 'open_link' => 'Atvērt saiti', 'open_link_in' => 'Atvērt saiti...', 'open_link_current' => 'Šis logs', @@ -160,6 +166,8 @@ 'about' => 'Par redaktoru', 'about_title' => 'Par WYSIWYG redaktoru', 'editor_license' => 'Redaktora licence un autortiesības', + 'editor_lexical_license' => 'Šis redaktors ir izveidots, izmantojot :tinyLink, kas ir publicēts ar MIT licenci.', + 'editor_lexical_license_link' => 'Pilnu licences informāciju var atrast šeit.', 'editor_tiny_license' => 'Šis redaktors ir izveidots, izmantojot :tinyLink, kas ir publicēts ar MIT licenci.', 'editor_tiny_license_link' => 'TinyMCE autortiesības un licences detaļas var atrast šeit.', 'save_continue' => 'Saglabāt lapu un turpināt', diff --git a/lang/lv/entities.php b/lang/lv/entities.php index 96d6b706a15..5f921048e52 100644 --- a/lang/lv/entities.php +++ b/lang/lv/entities.php @@ -23,7 +23,7 @@ 'meta_updated' => 'Atjaunināts :timeLength', 'meta_updated_name' => ':user atjauninājis pirms :timeLength', 'meta_owned_name' => 'Īpašnieks :user', - 'meta_reference_count' => 'Referenced by :count item|Referenced by :count items', + 'meta_reference_count' => 'Atsauce :count vienumā|Atsauce :count vienumos', 'entity_select' => 'Izvēlēties vienumu', 'entity_select_lack_permission' => 'Jums nav nepieciešamās piekļuves tiesības, lai izvēlētu šo vienumu', 'images' => 'Attēli', @@ -39,17 +39,45 @@ 'export_pdf' => 'PDF fails', 'export_text' => 'Vienkāršs teksta fails', 'export_md' => 'Markdown fails', + 'export_zip' => 'Pārceļams ZIP arhīvs', + 'default_template' => 'Noklusētā lapas sagatave', + 'default_template_explain' => 'Norādīt lapas sagatavi, kas tiks izmantota kā noklusētais saturs visām jaunājām lapām šajā grāmatā. Ņemiet vērā, ka tā tiks izmantota tikai tad, ja lapas veidotājam ir skatīšanas tiesības izvēlētajai sagatavei.', + 'default_template_select' => 'Izvēlēt sagataves lapu', + 'import' => 'Importēt', + 'import_validate' => 'Pārbaudīt importu', + 'import_desc' => 'Importēt grāmatas, nodaļas un lapas izmantojot pārceļamu ZIP arhīvu no šīs vai citas sistēmas instances. Izvēlietites ZIP failu, lai turpinātu. Kad fails ir augšupielādēts un pārbaudīts, jūs varēsiet veikt importa uzstādījumus un to apstiprināt nākamajā skatā.', + 'import_zip_select' => 'Izvēlieties ZIP failu, ko augšupielādēt', + 'import_zip_validation_errors' => 'Pārbaudot ZIP failu atrastas šādas kļūdas:', + 'import_pending' => 'Gaidošie importi', + 'import_pending_none' => 'Neviens imports nav uzsākts.', + 'import_continue' => 'Turpināt importu', + 'import_continue_desc' => 'Pārlūkot saturu, kas tiktu importēts no augšupielādētā ZIP faila. Kad esat gatavs, palaidiet importu, lai pievienotu tā saturu šai sistēmai. Augšupielādētais ZIP fails tiks automātiski izvākts pēc veiksmīga importa.', + 'import_details' => 'Importa detaļas', + 'import_run' => 'Palaist importu', + 'import_size' => ':size importa ZIP izmērs', + 'import_uploaded_at' => 'Augšupielādes laiks :relativeTime', + 'import_uploaded_by' => 'Augšupielādēja', + 'import_location' => 'Importa vieta', + 'import_location_desc' => 'Izvēlieties mērķa vietu jūsu importētajam saturam. Jums būs nepieciešamas attiecīgās piekļuves tiesības, lai izveidotu saturu izvēlētajā vietā.', + 'import_delete_confirm' => 'Vai tiešām vēlaties dzēst šo importu?', + 'import_delete_desc' => 'Šis izdzēsīs augšupielādēto importa ZIP failu, un šo darbību nevarēs atcelt.', + 'import_errors' => 'Importa kļūdas', + 'import_errors_desc' => 'Importa mēģinājumā atgadījās šīs kļūdas:', + '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', // Permissions and restrictions 'permissions' => 'Atļaujas', - 'permissions_desc' => 'Set permissions here to override the default permissions provided by user roles.', - 'permissions_book_cascade' => 'Permissions set on books will automatically cascade to child chapters and pages, unless they have their own permissions defined.', + 'permissions_desc' => 'Uzstādīt tiesības šeit, lai aizvietotu noklusētās tiesības no lietotāju lomām.', + 'permissions_book_cascade' => 'Piekļuves tiesības, kas uzstādītas grāmatām, automātiski tiks piešķirtas pakārtotajām nodaļām un lapām, ja vien tām nav atsevišķi norādītas savas piekļuves tiesības.', 'permissions_chapter_cascade' => 'Piekļuves tiesības, kas uzstādītas nodaļām, automātiski tiks piešķirtas pakārtotajām lapām, ja vien tām nav atsevišķi norādītas savas piekļuves tiesības.', 'permissions_save' => 'Saglabāt atļaujas', 'permissions_owner' => 'Īpašnieks', 'permissions_role_everyone_else' => 'Visi pārējie', 'permissions_role_everyone_else_desc' => 'Set permissions for all roles not specifically overridden.', - 'permissions_role_override' => 'Override permissions for role', + 'permissions_role_override' => 'Aizvietot lomas tiesības', 'permissions_inherit_defaults' => 'Mantot noklusētās vērtības', // Search @@ -106,7 +134,7 @@ 'shelves_permissions_updated' => 'Plaukta atļaujas atjauninātas', 'shelves_permissions_active' => 'Plaukta atļaujas ir aktīvas', 'shelves_permissions_cascade_warning' => 'Plauktu piekļuves tiesības netiek automātiski piešķirtas tajā esošajām grāmatām. Tas ir tāpēc, ka grāmata var vienlaicīgi atrasties vairākos plauktos. Tomēr piekļuves tiesības var nokopēt uz plauktam pievienotajām grāmatām, izmantojot zemāk atrodamo opciju.', - 'shelves_permissions_create' => 'Shelf create permissions are only used for copying permissions to child books using the action below. They do not control the ability to create books.', + 'shelves_permissions_create' => 'Plaukta veidošanas tiesības tiek izmantotas tikai, lai kopētu tiesības uz pakārtotajām grāmatām, izmantojot zemāk esošo darbību. Tās nekontrolē iespēju izveidot jaunas grāmatas.', 'shelves_copy_permissions_to_books' => 'Kopēt grāmatplaukta atļaujas uz grāmatām', 'shelves_copy_permissions' => 'Kopēt atļaujas', 'shelves_copy_permissions_explain' => 'Pašreizējās plaukta piekļuves tiesības tiks piemērotas visām tajā esošajām grāmatām. Pirms ieslēgšanas pārliecinieties, ka visas izmaiņas plaukta piekļuves tiesības ir saglabātas.', @@ -132,9 +160,6 @@ 'books_edit_named' => 'Labot grāmatu :bookName', 'books_form_book_name' => 'Grāmatas nosaukums', 'books_save' => 'Saglabāt grāmatu', - 'books_default_template' => 'Noklusētā lapas sagatave', - 'books_default_template_explain' => 'Assign a page template that will be used as the default content for all new pages in this book. Keep in mind this will only be used if the page creator has view access to those chosen template page.', - 'books_default_template_select' => 'Select a template page', 'books_permissions' => 'Grāmatas atļaujas', 'books_permissions_updated' => 'Grāmatas atļaujas atjauninātas', 'books_empty_contents' => 'Lapas vai nodaļas vēl nav izveidotas šai grāmatai.', @@ -145,7 +170,9 @@ 'books_search_this' => 'Meklēt šajā grāmatā', 'books_navigation' => 'Grāmatas navigācija', 'books_sort' => 'Kārtot grāmatas saturu', - 'books_sort_desc' => 'Pārvietojiet nodaļas un lapas grāmatas ietvaros, lai organizētu tās saturu. Var pievienot vēl citas grāmatas, lai vieglāk pārcelt nodaļas un lapas starp grāmatām.', + 'books_sort_desc' => 'Move chapters and pages within a book to reorganise its contents. Other books can be added which allows easy moving of chapters and pages between books. Optionally an auto sort rule can be set to automatically sort this book\'s contents upon changes.', + 'books_sort_auto_sort' => 'Auto Sort Option', + 'books_sort_auto_sort_active' => 'Auto Sort Active: :sortName', 'books_sort_named' => 'Kārtot grāmatu :bookName', 'books_sort_name' => 'Kārtot pēc nosaukuma', 'books_sort_created' => 'Kārtot pēc izveidošanas datuma', @@ -207,7 +234,7 @@ 'pages_delete_draft' => 'Dzēst melnrakstu', 'pages_delete_success' => 'Lapa ir dzēsta', 'pages_delete_draft_success' => 'Melnraksts ir dzēsts', - 'pages_delete_warning_template' => 'This page is in active use as a book default page template. These books will no longer have a default page template assigned after this page is deleted.', + 'pages_delete_warning_template' => 'This page is in active use as a book or chapter default page template. These books or chapters will no longer have a default page template assigned after this page is deleted.', 'pages_delete_confirm' => 'Vai esat pārliecināts, ka vēlaties dzēst šo lapu?', 'pages_delete_draft_confirm' => 'Vai esat pārliecināts, ka vēlaties dzēst šo melnrakstu?', 'pages_editing_named' => 'Rediģē lapu :pageName', @@ -218,12 +245,14 @@ 'pages_editing_page' => 'Labo lapu', 'pages_edit_draft_save_at' => 'Melnraksts saglabāts ', 'pages_edit_delete_draft' => 'Dzēst melnrakstu', - 'pages_edit_delete_draft_confirm' => 'Are you sure you want to delete your draft page changes? All of your changes, since the last full save, will be lost and the editor will be updated with the latest page non-draft save state.', + 'pages_edit_delete_draft_confirm' => 'Vai tiešām vēlaties dzēst savas uzmetuma izmaiņas? Visas jūsu izmaiņas kopš pēdējās saglabāšanas pazudīs un redaktors tiks atjaunos ar pēdējo saglabātu lapas saturu.', 'pages_edit_discard_draft' => 'Atmest malnrakstu', 'pages_edit_switch_to_markdown' => 'Pārslēgties uz Markdown redaktoru', 'pages_edit_switch_to_markdown_clean' => '(Iztīrītais saturs)', 'pages_edit_switch_to_markdown_stable' => '(Stabilais saturs)', 'pages_edit_switch_to_wysiwyg' => 'Pārslēgties uz WYSIWYG redaktoru', + 'pages_edit_switch_to_new_wysiwyg' => 'Pārslēgties uz jauno WYSIWYG redaktoru', + 'pages_edit_switch_to_new_wysiwyg_desc' => '(In Beta Testing)', 'pages_edit_set_changelog' => 'Pievienot izmaiņu aprakstu', 'pages_edit_enter_changelog_desc' => 'Ievadi nelielu aprakstu par vaiktajām izmaiņām', 'pages_edit_enter_changelog' => 'Izmaiņu apraksts', @@ -243,7 +272,8 @@ 'pages_md_insert_drawing' => 'Ievietot zīmējumu', 'pages_md_show_preview' => 'Rādīt priekšskatu', 'pages_md_sync_scroll' => 'Sync preview scroll', - 'pages_drawing_unsaved' => 'Unsaved Drawing Found', + 'pages_md_plain_editor' => 'Plaintext editor', + 'pages_drawing_unsaved' => 'Atrasts nesaglabāts attēls', 'pages_drawing_unsaved_confirm' => 'Unsaved drawing data was found from a previous failed drawing save attempt. Would you like to restore and continue editing this unsaved drawing?', 'pages_not_in_chapter' => 'Lapa nav nodaļā', 'pages_move' => 'Pārvietot lapu', @@ -299,7 +329,7 @@ 'pages_is_template' => 'Lapas šablons', // Editor Sidebar - 'toggle_sidebar' => 'Toggle Sidebar', + 'toggle_sidebar' => 'Pārslēgt sānjoslu', 'page_tags' => 'Lapas birkas', 'chapter_tags' => 'Nodaļas birkas', 'book_tags' => 'Grāmatas birkas', @@ -330,7 +360,7 @@ 'attachments_upload_drop' => 'Alternatively you can drag and drop a file here to upload it as an attachment.', 'attachments_set_link' => 'Uzstādīt saiti', 'attachments_delete' => 'Vai tiešām vēlaties dzēst šo pielikumu?', - 'attachments_dropzone' => 'Drop files here to upload', + 'attachments_dropzone' => 'Ievelciet failus šeit, lai augšupielādētu', 'attachments_no_files' => 'Neviens fails nav augšupielādēts', 'attachments_explain_link' => 'Ja nevēlaties augšupielādēt failu, varat pievienot saiti. Tā var būt saite uz citu lapu vai saite uz failu mākonī.', 'attachments_link_name' => 'Saites nosaukums', @@ -367,8 +397,11 @@ 'comment' => 'Komentārs', 'comments' => 'Komentāri', 'comment_add' => 'Pievienot komentāru', + 'comment_none' => 'No comments to display', 'comment_placeholder' => 'Pievieno komentāru', - 'comment_count' => '{0} Nav komentāru |{1} 1 Komentārs|[2,*] :count Komentāri', + 'comment_thread_count' => ':count Comment Thread|:count Comment Threads', + 'comment_archived_count' => ':count Archived', + 'comment_archived_threads' => 'Archived Threads', 'comment_save' => 'Saglabāt komentāru', 'comment_new' => 'Jauns komentārs', 'comment_created' => 'komentējis :createDiff', @@ -377,8 +410,14 @@ 'comment_deleted_success' => 'Komentārs ir dzēsts', 'comment_created_success' => 'Komentārs ir pievienots', 'comment_updated_success' => 'Komentārs ir atjaunināts', + 'comment_archive_success' => 'Comment archived', + 'comment_unarchive_success' => 'Comment un-archived', + 'comment_view' => 'View comment', + 'comment_jump_to_thread' => 'Jump to thread', 'comment_delete_confirm' => 'Vai esat pārliecināts, ka vēlaties dzēst šo komentāru?', 'comment_in_reply_to' => 'Atbildēt uz :commentId', + 'comment_reference' => 'Reference', + 'comment_reference_outdated' => '(Outdated)', 'comment_editor_explain' => 'Here are the comments that have been left on this page. Comments can be added & managed when viewing the saved page.', // Revision @@ -413,23 +452,23 @@ // Watch Options 'watch' => 'Vērot', - 'watch_title_default' => 'Default Preferences', + 'watch_title_default' => 'Noklusētie uzstādījumi', 'watch_desc_default' => 'Revert watching to just your default notification preferences.', 'watch_title_ignore' => 'Ignorēt', - 'watch_desc_ignore' => 'Ignore all notifications, including those from user-level preferences.', + 'watch_desc_ignore' => 'Ignorēt visus paziņojumus, tai skaitā arī tādus, kas uzstādīti lietotāja uzstādījumos.', 'watch_title_new' => 'Jaunas lapas', 'watch_desc_new' => 'Notify when any new page is created within this item.', 'watch_title_updates' => 'Visi lapu atjauninājumi', - 'watch_desc_updates' => 'Notify upon all new pages and page changes.', - 'watch_desc_updates_page' => 'Notify upon all page changes.', + 'watch_desc_updates' => 'Paziņot par visām jaunām lapām un lapu izmaiņām.', + 'watch_desc_updates_page' => 'Paziņot par visām lapu izmaiņām.', 'watch_title_comments' => 'Visi lapu atjauninājumi un komentāri', - 'watch_desc_comments' => 'Notify upon all new pages, page changes and new comments.', - 'watch_desc_comments_page' => 'Notify upon page changes and new comments.', - 'watch_change_default' => 'Change default notification preferences', + 'watch_desc_comments' => 'Paziņot par visām jaunām lapām, lapu izmaiņām un jauniem komentāriem.', + 'watch_desc_comments_page' => 'Paziņot par lapu izmaiņām un jauniem komentāriem.', + 'watch_change_default' => 'Izmainīt noklusētos paziņojumu uzstādījumus', 'watch_detail_ignore' => 'Ignorēt paziņojumus', - 'watch_detail_new' => 'Watching for new pages', - 'watch_detail_updates' => 'Watching new pages and updates', - 'watch_detail_comments' => 'Watching new pages, updates & comments', + 'watch_detail_new' => 'Vērot jaunas lapas', + 'watch_detail_updates' => 'Vērot jaunas lapas un atjauninājumus', + 'watch_detail_comments' => 'Vērot jaunas lapas, atjauninājumus un komentārus', 'watch_detail_parent_book' => 'Watching via parent book', 'watch_detail_parent_book_ignore' => 'Ignoring via parent book', 'watch_detail_parent_chapter' => 'Watching via parent chapter', diff --git a/lang/lv/errors.php b/lang/lv/errors.php index 2a5777a0a7f..28cc0d892c1 100644 --- a/lang/lv/errors.php +++ b/lang/lv/errors.php @@ -10,6 +10,7 @@ // Auth 'error_user_exists_different_creds' => 'Lietotājs ar epastu :email bet ar citiem piekļuves datiem jau eksistē.', + 'auth_pre_register_theme_prevention' => 'Lietotāja kontu nevar reģistrēt ar norādītajām detaļām', 'email_already_confirmed' => 'Epasts jau ir apstiprināts, mēģini ielogoties.', 'email_confirmation_invalid' => 'Šis apstiprinājuma žetons nav derīgs vai jau ir izmantots. Lūdzu, mēģiniet reģistrēties vēlreiz.', 'email_confirmation_expired' => 'Apstiprinājuma žetona derīguma termiņš ir beidzies. Ir nosūtīts jauns apstiprinājuma e-pasts.', @@ -23,7 +24,6 @@ 'saml_invalid_response_id' => 'Ārējās autentifikācijas sistēmas pieprasījums neatpazīst procesu, kuru sākusi šī lietojumprogramma. Pārvietojoties atpakaļ pēc pieteikšanās var rasties šāda problēma.', 'saml_fail_authed' => 'Piekļuve ar :system neizdevās, sistēma nepieļāva veiksmīgu autorizāciju', 'oidc_already_logged_in' => 'Jau esat ielogojies', - 'oidc_user_not_registered' => 'Lietotājs :name nav reģistrēts un automātiska reģistrācija ir izslēgta', 'oidc_no_email_address' => 'Ārējās autentifikācijas sistēmas sniegtajos datos nevarēja atrast šī lietotāja e-pasta adresi', 'oidc_fail_authed' => 'Piekļuve ar :system neizdevās, sistēma nepieļāva veiksmīgu autorizāciju', 'social_no_action_defined' => 'Darbības nav definētas', @@ -37,6 +37,7 @@ 'social_driver_not_found' => 'Sociālā tīkla savienojums nav atrasts', 'social_driver_not_configured' => 'Jūsu :socialAccount sociālie iestatījumi nav uzstādīti pareizi.', 'invite_token_expired' => 'Šī uzaicinājuma saite ir novecojusi. Tā vietā jūs varat mēģināt atiestatīt sava konta paroli.', + 'login_user_not_found' => 'Šai darbībai netika atrasts lietotājs.', // System 'path_not_writable' => 'Faila ceļā :filePath nav iespējams ielādēt failus. Lūdzu pārliecinieties, ka serverim tur ir rakstīšanas tiesības.', @@ -77,6 +78,7 @@ // Users 'users_cannot_delete_only_admin' => 'Jūs nevarat dzēst vienīgo administratoru', 'users_cannot_delete_guest' => 'Jūs nevarat dzēst lietotāju "viesis"', + 'users_could_not_send_invite' => 'Neizdevās izveidot lietotāju, jo neizdevās nosūtīt ielūguma epastu', // Roles 'role_cannot_be_edited' => 'Šo lomu nevar rediģēt', @@ -103,6 +105,18 @@ 'app_down' => ':appName pagaidām nav pieejams', 'back_soon' => 'Drīz būs atkal pieejams.', + // Import + 'import_zip_cant_read' => 'Nevarēja nolasīt ZIP failu.', + 'import_zip_cant_decode_data' => 'Nevarēja atrast un nolasīt data.json saturu ZIP failā.', + 'import_zip_no_data' => 'ZIP faila datos nav atrasts grāmatu, nodaļu vai lapu saturs.', + 'import_validation_failed' => 'ZIP faila imports ir neveiksmīgs ar šādām kļūdām:', + 'import_zip_failed_notification' => 'ZIP faila imports ir neveiksmīgs.', + 'import_perms_books' => 'Jums nav nepieciešamo tiesību izveidot grāmatas.', + 'import_perms_chapters' => 'Jums nav nepieciešamo tiesību izveidot nodaļas.', + 'import_perms_pages' => 'Jums nav nepieciešamo tiesību izveidot lapas.', + 'import_perms_images' => 'Jums nav nepieciešamo tiesību izviedot attēlus.', + 'import_perms_attachments' => 'Jums nav nepieciešamo tiesību izveidot pielikumus.', + // API errors 'api_no_authorization_found' => 'Pieprasījumā nav atrasts autorizācijas žetons', 'api_bad_authorization_format' => 'Pieprasījumā atrasts autorizācijas žetons, taču tā formāts nav pareizs', diff --git a/lang/lv/notifications.php b/lang/lv/notifications.php index d0fee395021..9fd60222a72 100644 --- a/lang/lv/notifications.php +++ b/lang/lv/notifications.php @@ -10,10 +10,10 @@ 'new_page_intro' => 'Jauna lapa izveidota :appName:', 'updated_page_subject' => 'Atjaunināta lapa: :pageName', 'updated_page_intro' => 'Lapa atjaunināta :appName:', - 'updated_page_debounce' => 'To prevent a mass of notifications, for a while you won\'t be sent notifications for further edits to this page by the same editor.', + 'updated_page_debounce' => 'Lai novērstu pārliecīgu paziņojumu sūtīšanu, uz laiku jums tiks pārtraukti paziņojumi par turpmākiem šī lietotāja labojumiem šai lapai.', 'detail_page_name' => 'Lapas nosaukums:', - 'detail_page_path' => 'Page Path:', + 'detail_page_path' => 'Ceļš uz lapu:', 'detail_commenter' => 'Komentētājs:', 'detail_comment' => 'Komentārs:', 'detail_created_by' => 'Izveidoja:', @@ -22,6 +22,6 @@ 'action_view_comment' => 'Skatīt komentāru', 'action_view_page' => 'Skatīt lapu', - 'footer_reason' => 'This notification was sent to you because :link cover this type of activity for this item.', + 'footer_reason' => 'Šis paziņojums nosūtīts tāpēc, ka :link paredz šādu aktivitāti šai vienībai.', 'footer_reason_link' => 'paziņojumu vēlamie iestatījumi', ]; diff --git a/lang/lv/preferences.php b/lang/lv/preferences.php index b45a063343b..b91cc2e0833 100644 --- a/lang/lv/preferences.php +++ b/lang/lv/preferences.php @@ -9,25 +9,25 @@ 'shortcuts' => 'Saīsnes', 'shortcuts_interface' => 'Saskarnes īsceļu iestatījumi', - 'shortcuts_toggle_desc' => 'Here you can enable or disable keyboard system interface shortcuts, used for navigation and actions.', - 'shortcuts_customize_desc' => 'You can customize each of the shortcuts below. Just press your desired key combination after selecting the input for a shortcut.', + 'shortcuts_toggle_desc' => 'Šeit jūs varat ieslēgt vai izslēgt sistēmas saskarnes klaviatūras īsceļus, kas tiek izmantoti navigācijai un darbībām.', + 'shortcuts_customize_desc' => 'Jūs varat pielāgot katru no zemāk esošajiem īsceļiem. Vienkārši nospiediet nepieciešamo pogu kombināciju pēc tam, kad izvēlēts īsceļa ievadlauks.', 'shortcuts_toggle_label' => 'Klaviatūras saīsnes ieslēgtas', 'shortcuts_section_navigation' => 'Navigācija', 'shortcuts_section_actions' => 'Biežākās darbības', 'shortcuts_save' => 'Saglabāt saīsnes', - 'shortcuts_overlay_desc' => 'Note: When shortcuts are enabled a helper overlay is available via pressing "?" which will highlight the available shortcuts for actions currently visible on the screen.', + 'shortcuts_overlay_desc' => 'Piezīme: kad īsceļi ir ieslēgti, ir pieejams palīdzības lodziņš, kas parādās, nospiežot "?". Tas attēlos pieejamos īsceļus tajā brīdī pieejamajām darbībām.', 'shortcuts_update_success' => 'Saīsņu uzstādījumi ir saglabāt!', - 'shortcuts_overview_desc' => 'Manage keyboard shortcuts you can use to navigate the system user interface.', + 'shortcuts_overview_desc' => 'Pārvaldīt klaviatūras īsceļus, ko var izmantot sistēmas saskarnes navigācijai.', 'notifications' => 'Paziņojumu iestatījumi', - 'notifications_desc' => 'Control the email notifications you receive when certain activity is performed within the system.', + 'notifications_desc' => 'Pārvaldiet epasta paziņojumus, ko saņemsiet, kad sistēmā tiek veiktas noteiktas darbības.', 'notifications_opt_own_page_changes' => 'Paziņot par izmaiņām manās lapās', 'notifications_opt_own_page_comments' => 'Paziņot par komentāriem manās lapās', 'notifications_opt_comment_replies' => 'Paziņot par atbildēm uz maniem komentāriem', 'notifications_save' => 'Saglabāt iestatījumus', 'notifications_update_success' => 'Paziņojumu iestatījumi ir atjaunoti!', 'notifications_watched' => 'Vērotie un ignorētie vienumi', - 'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.', + 'notifications_watched_desc' => 'Zemāk ir vienumi, kam piemēroti īpaši vērošanas nosacījumi. Lai atjaunintātu savus uzstādījums šiem, apskatiet vienumu un tad sameklējiet vērošanas uzstādījumus sānu kolonnā.', 'auth' => 'Piekļuve un drošība', 'auth_change_password' => 'Mainīt paroli', @@ -35,17 +35,17 @@ 'auth_change_password_success' => 'Parole ir nomainīta!', 'profile' => 'Profila informācija', - 'profile_desc' => 'Manage the details of your account which represents you to other users, in addition to details that are used for communication and system personalisation.', + 'profile_desc' => 'Pārvaldiet sava konta detaļas, kas jūs attēlo citiem lietotājiem, kā arī detaļas, kas tiek izmantotas saziņai un sistēmas pielāgošanai.', 'profile_view_public' => 'Skatīt publisko profilu', - 'profile_name_desc' => 'Configure your display name which will be visible to other users in the system through the activity you perform, and content you own.', + 'profile_name_desc' => 'Uzstādiet savu vārdu, kas tiks parādīts citiem lietotājiem sistēmā pie jūsu darbībām un jums piederošā satura.', 'profile_email_desc' => 'Šis epasts tiks izmantots paziņojumiem un sistēmas piekļuvei, atkarībā no sistēmā uzstādītās autentifikācijas metodes.', 'profile_email_no_permission' => 'Diemžēl jums nav tiesību mainīt savu epasta adresi. Ja vēlaties to mainīt, jums jāsazinās ar administratoru, lai tas nomaina šo adresi.', - 'profile_avatar_desc' => 'Select an image which will be used to represent yourself to others in the system. Ideally this image should be square and about 256px in width and height.', - 'profile_admin_options' => 'Administrator Options', - 'profile_admin_options_desc' => 'Additional administrator-level options, like those to manage role assignments, can be found for your user account in the "Settings > Users" area of the application.', + 'profile_avatar_desc' => 'Izvēlieties attēlu, kas tiks izmantots, lai jūs attēlotu citiem sistēmas lietotājiem. Ideālā gadījumā šim attēlam jābūt kvadrātaveida, apmēram 256px platumā un augstumā.', + 'profile_admin_options' => 'Administratora iestatījumi', + 'profile_admin_options_desc' => 'Papildus administratora iestatījumus, kā piemēram, lomu piešķiršanu, var atrast jūsu lietotāja kontā ejot uz "Uzstādījumu > Lietotāji".', 'delete_account' => 'Dzēst kontu', 'delete_my_account' => 'Izdzēst manu kontu', - 'delete_my_account_desc' => 'This will fully delete your user account from the system. You will not be able to recover this account or revert this action. Content you\'ve created, such as created pages and uploaded images, will remain.', + 'delete_my_account_desc' => 'Šī darbība pilnībā izdzēsīs jūsu lietotāja kontu no sistēmas. Jūs vairs nevarēsiet piekļūt kontam, atcelt šo darbību vai atjaunot kontu. Jūsu izveidotais saturs, piemēram, izveidotās lapas un augšupielādētie attēli, tiks saglabāts.', 'delete_my_account_warning' => 'Vai tiešām vēlaties dzēst savu kontu?', ]; diff --git a/lang/lv/settings.php b/lang/lv/settings.php index 5c07cfc1d18..9901f781190 100644 --- a/lang/lv/settings.php +++ b/lang/lv/settings.php @@ -74,6 +74,36 @@ 'reg_confirm_restrict_domain_desc' => 'Ievadiet ar komatiem atdalītu sarakstu ar epasta domēniem, kam jūs gribētu atļaut reģistrāciju. Lietotājiem tiks nosūtīts epasts, lai apstiprinātu tā adresi pirms tiks ļauts darboties ar aplikāciju.
Ņemiet vērā, ka lietotāji varēs nomainīt savu epasta adresi pēc veiksmīgas reģistrācijas.', 'reg_confirm_restrict_domain_placeholder' => 'Nav ierobežojumu', + // Sorting Settings + 'sorting' => 'Kārtošana', + 'sorting_book_default' => 'Noklusētā grāmatu kārtošana', + 'sorting_book_default_desc' => 'Izvēlieties noklusēto kārtošanas nosacījumu, ko pielietot jaunām grāmatām. Šis neskars jau esošas grāmatas, un to var izmainīt grāmatas iestatījumos.', + 'sorting_rules' => 'Kārtošanas noteikumi', + 'sorting_rules_desc' => 'Šīs ir iepriekš noteiktas kārtošanas darbības, ko var pielietot saturam šajā sistēmā.', + 'sort_rule_assigned_to_x_books' => 'Pielietots :count grāmatai|Pielietots :count grāmatām', + 'sort_rule_create' => 'Izveidot kārtošanas nosacījumu', + 'sort_rule_edit' => 'Rediģēt kārtošanas nosacījumu', + 'sort_rule_delete' => 'Dzēst kārtošanas nosacījumu', + 'sort_rule_delete_desc' => 'Izņemt šo kārtošanas nosacījumu no sistēmas. Grāmatām tiks atjaunota manuāla kārtošana.', + 'sort_rule_delete_warn_books' => 'Šis kārtošanas nosacījums pašlaik tiek izmantots :count grāmatām. Vai tiešām vēlaties to dzēst?', + 'sort_rule_delete_warn_default' => 'Šis kārtošanas nosacījums pašlaik ir norādīts kā noklusētais. Vai tiešām vēlaties to dzēst?', + 'sort_rule_details' => 'Kārtošanas nosacījuma detaļas', + 'sort_rule_details_desc' => 'Uzstādiet nosaukumu šim kārtošanas nosacījumam, kas parādīsies sarakstā, kad lietotājs izvēlēsies kārtošanas veidu.', + 'sort_rule_operations' => 'Kārtošanas darbības', + 'sort_rule_operations_desc' => 'Konfigurējiet kārtošanas darbības pārvelkot tās no pieejamo darb\'biu saraksta. Lietošanas procesā darbības tiks piemērotas saraksta kārtībā no augšas uz apakšu. Jekbādas izmaiņas šeit tiks piemērotas grāmatās, kur šis piemērots, pie saglabāšanas.', + 'sort_rule_available_operations' => 'Pieejamās darbības', + 'sort_rule_available_operations_empty' => 'Nav atlikušas darbības', + 'sort_rule_configured_operations' => 'Uzstādītās darbības', + 'sort_rule_configured_operations_empty' => 'Ievelciet/pievienojiet darbības no "Pieejamo darbību" saraksta', + 'sort_rule_op_asc' => '(Pieaug.)', + 'sort_rule_op_desc' => '(Dilst.)', + 'sort_rule_op_name' => 'Nosaukums - alfabētiski', + 'sort_rule_op_name_numeric' => 'Nosaukums - numuriski', + 'sort_rule_op_created_date' => 'Izveidošanas datums', + 'sort_rule_op_updated_date' => 'Atjaunināšanas datums', + 'sort_rule_op_chapters_first' => 'Nodaļas pirmās', + 'sort_rule_op_chapters_last' => 'Nodaļas pēdējās', + // Maintenance settings 'maint' => 'Apkope', 'maint_image_cleanup' => 'Tīrīt neizmantotās bildes', @@ -109,7 +139,7 @@ 'recycle_bin_contents_empty' => 'Miskaste ir tukša', 'recycle_bin_empty' => 'Iztīrīt miskasti', 'recycle_bin_empty_confirm' => 'Šī darbība pilnībā dzēsīs visas vienības miskastē, ieskaitot saturu, kas ievietots katrā no šīm vienībām. Vai tiešām vēlaties dzēst visu miskastes saturu?', - 'recycle_bin_destroy_confirm' => 'Šī darbība pilnībā izdzēsis šo vienību kopā ar tai pakārtotajiem elementiem no sistēmas, un jūs nevarēsiet šo saturu atjaunot. Vai tiešām vēlaties pilnībā izdzēst šo vienību?', + 'recycle_bin_destroy_confirm' => 'Šī darbība pilnībā no sistēmas izdzēsis šo vienību kopā ar tai pakārtotajiem elementiem, un jūs nevarēsiet šo saturu atjaunot. Vai tiešām vēlaties pilnībā izdzēst šo vienību?', 'recycle_bin_destroy_list' => 'Dzēšamās vienības', 'recycle_bin_restore_list' => 'Atjaunojamās vienības', 'recycle_bin_restore_confirm' => 'Šī darbība atjaunos dzēsto vienību, tai skaitā visus tai pakārtotos elementus, uz tās sākotnējo atrašanās vietu. Ja sākotnējā atrašanās vieta ir izdzēsta un atrodas miskastē, būs nepieciešams atjaunot arī to.', @@ -136,11 +166,11 @@ // Role Settings 'roles' => 'Grupas', 'role_user_roles' => 'Lietotāju grupas', - 'roles_index_desc' => 'Roles are used to group users & provide system permission to their members. When a user is a member of multiple roles the privileges granted will stack and the user will inherit all abilities.', - 'roles_x_users_assigned' => ':count user assigned|:count users assigned', - 'roles_x_permissions_provided' => ':count permission|:count permissions', - 'roles_assigned_users' => 'Assigned Users', - 'roles_permissions_provided' => 'Provided Permissions', + 'roles_index_desc' => 'Lomas tiek izmantotas, lai sagrupētu lietotājus un piešķirtu piekļuves tiesības to dalībniekiem. Kad lietotājs ir vairāku lomu dalībnieks, piešķirtās tiesības tiks summētas un lietotājam būs visas tiesības.', + 'roles_x_users_assigned' => ':count lietotājam piešķirts|:count lietotājiem piešķirts', + 'roles_x_permissions_provided' => ':count tiesības|:count tiesības', + 'roles_assigned_users' => 'Pievienotie lietotāji', + 'roles_permissions_provided' => 'Piešķirtās tiesības', 'role_create' => 'Izveidot jaunu grupu', 'role_delete' => 'Dzēst grupu', 'role_delete_confirm' => 'Loma \':roleName\' tiks dzēsta.', @@ -162,13 +192,14 @@ 'role_access_api' => 'Piekļūt sistēmas API', 'role_manage_settings' => 'Pārvaldīt iestatījumus', 'role_export_content' => 'Eksportēt saturu', + 'role_import_content' => 'Importēt saturu', 'role_editor_change' => 'Mainīt lapu redaktoru', - 'role_notifications' => 'Receive & manage notifications', + 'role_notifications' => 'Saņemt un pārvaldīt paziņojumus', 'role_asset' => 'Resursa piekļuves tiesības', 'roles_system_warning' => 'Jebkuras no trīs augstāk redzamajām atļaujām dod iespēju lietotājam mainīt savas un citu lietotāju sistēmas atļaujas. Pievieno šīs grupu atļaujas tikai tiem lietotājiem, kuriem uzticies.', 'role_asset_desc' => 'Šīs piekļuves tiesības kontrolē noklusēto piekļuvi sistēmas resursiem. Grāmatām, nodaļām un lapām norādītās tiesības būs pārākas par šīm.', 'role_asset_admins' => 'Administratoriem automātiski ir piekļuve visam saturam, bet šie uzstādījumi var noslēpt vai parādīt lietotāja saskarnes iespējas.', - 'role_asset_image_view_note' => 'This relates to visibility within the image manager. Actual access of uploaded image files will be dependant upon system image storage option.', + 'role_asset_image_view_note' => 'Šis ir saistīts ar redzamību attēlu pārvaldniekā. Faktiskā piekļuve augšupielādēto attēlu failiem būs atkarīga no sistēmas attēlu glabātuves uzstādījuma.', 'role_all' => 'Visi', 'role_own' => 'Savi', 'role_controlled_by_asset' => 'Kontrolē resurss, uz ko tie ir augšupielādēti', @@ -178,7 +209,7 @@ // Users 'users' => 'Lietotāji', - 'users_index_desc' => 'Create & manage individual user accounts within the system. User accounts are used for login and attribution of content & activity. Access permissions are primarily role-based but user content ownership, among other factors, may also affect permissions & access.', + 'users_index_desc' => 'Izveidot un pārvaldīt atsevišķus lietotāju kontus sistēmā. Lietotāju konti tiek izmantoti piekļuvei un satura un aktivitāšu piesaistei. Piekļuves tiesības ir pamatā balstītas uz lomām, bet lietotāju veidotā satura piederība, cita starpā, arī var ietekmēt piekļuvi un tiesības.', 'user_profile' => 'Lietotāja profils', 'users_add_new' => 'Pievienot jaunu lietotāju', 'users_search' => 'Meklēt lietotājus', @@ -193,8 +224,8 @@ 'users_send_invite_text' => 'Jūs varat izvēlētes vai nosūtīt šim lietotājam uzaicinājuma epastu, kas ļauj tam uzstādīt savu paroli pašam, vai arī varat uzstādīt paroli tagad.', 'users_send_invite_option' => 'Nosūtīt lietotāja uzaicinājuma epastu', 'users_external_auth_id' => 'Ārējais autentifikācijas ID', - 'users_external_auth_id_desc' => 'When an external authentication system is in use (such as SAML2, OIDC or LDAP) this is the ID which links this BookStack user to the authentication system account. You can ignore this field if using the default email-based authentication.', - 'users_password_warning' => 'Only fill the below if you would like to change the password for this user.', + 'users_external_auth_id_desc' => 'Kad tiek izmantota ārēja autentifikācijas sistēma (piemēram, SAML2, OIDC vai LDAP), šis ir identifikators (ID), kas sasaista šo BookStack lietotāja kontu ar autentifikācijas sistēmas kontu. Jūs varat ignorēt šo lauku, ja izmantojat noklusēto autentifikāciju ar epasta adresi.', + 'users_password_warning' => 'Aizpildiet tikai tad, ja vēlaties mainīt paroli šim lietotājam.', 'users_system_public' => 'Šis lietotājs apzīmē visus viesus, kas apmeklēs jūsu lapu. To nevar izmantot lapas piekļuvei un tas tiek norādīts automātiski.', 'users_delete' => 'Dzēst lietotāju', 'users_delete_named' => 'Dzēst lietotāju :userName', @@ -210,7 +241,7 @@ 'users_preferred_language' => 'Vēlamā valoda', 'users_preferred_language_desc' => 'Šis uzstādījums nomainīs valodu, kas izmantota aplikācijas lietotāja saskarnē. Tas neietekmēs neko no lietotāju radītā satura.', 'users_social_accounts' => 'Sociālie konti', - 'users_social_accounts_desc' => 'View the status of the connected social accounts for this user. Social accounts can be used in addition to the primary authentication system for system access.', + 'users_social_accounts_desc' => 'Skatīt piesaistīto sociālo kontu statusu šim lietotājam. Sociālos kontus var izmantot piekļuvei papildus primārajai autentifikācijas sistēmai.', 'users_social_accounts_info' => 'Te jūs varat pieslēgt citus kontus ātrākai un ērtākai piekļuvei. Konta atvienošana no šejienes neatceļ šai aplikācijai dotās tiesības šī konta piekļuvei. Atvienojtiet piekļuvi arī no jūsu profila uzstādījumiem pievienotajā sociālajā kontā.', 'users_social_connect' => 'Pievienot kontu', 'users_social_disconnect' => 'Atvienot kontu', @@ -219,7 +250,7 @@ 'users_social_connected' => ':socialAccount konts veiksmīgi pieslēgts jūsu profilam.', 'users_social_disconnected' => ':socialAccount konts veiksmīgi atslēgts no jūsu profila.', 'users_api_tokens' => 'API žetoni', - 'users_api_tokens_desc' => 'Create and manage the access tokens used to authenticate with the BookStack REST API. Permissions for the API are managed via the user that the token belongs to.', + 'users_api_tokens_desc' => 'Izveidot un pārvaldīt piekļuves žetonus, lai autentificētos ar BookStack REST API. API tiesības ir tās pašas, kas lietotāja kontam, kam pieder šis žetons.', 'users_api_tokens_none' => 'Šim lietotājam nav izveidotu API žetonu', 'users_api_tokens_create' => 'Izveidot žetonu', 'users_api_tokens_expires' => 'Derīguma termiņš', @@ -249,8 +280,8 @@ // Webhooks 'webhooks' => 'Webhook', - 'webhooks_index_desc' => 'Webhooks are a way to send data to external URLs when certain actions and events occur within the system which allows event-based integration with external platforms such as messaging or notification systems.', - 'webhooks_x_trigger_events' => ':count trigger event|:count trigger events', + 'webhooks_index_desc' => 'Webhook ir veids, kā nosūtīt datus ārējām adresēm (URL) pie noteiktām darbībām vai notikumiem sistēmā. Tas ļauj īstenot uz notikumiem balstītu integrāciju ar ārējām platformām, kā piemēram, apziņošanas sistēmām.', + 'webhooks_x_trigger_events' => ':count notikums|:count notikumi', 'webhooks_create' => 'Izveidot jaunu webhook', 'webhooks_none_created' => 'Nav izveidots neviens webhook.', 'webhooks_edit' => 'Labot webhook', @@ -276,6 +307,14 @@ 'webhooks_last_errored' => 'Pedējoreiz kļūda:', 'webhooks_last_error_message' => 'Pēdējais kļūdas paziņojums:', + // Licensing + 'licenses' => 'Licences', + 'licenses_desc' => 'Šī lapa attēlo BookStack licences informāciju, kā arī licences citiem projektiem un bibliotēkām, kas tiek izmantoti BookStack. Daļa no minētajiem projektiem var būt izmantoti tikai izstrādē.', + 'licenses_bookstack' => 'BookStack licence', + 'licenses_php' => 'PHP bibliotēku licences', + 'licenses_js' => 'JavaScript bibliotēku licences', + 'licenses_other' => 'Citas licences', + 'license_details' => 'Licences informācija', //! If editing translations files directly please ignore this in all //! languages apart from en. Content will be auto-copied from en. @@ -287,6 +326,7 @@ 'bs' => 'Bosanski', 'ca' => 'Katalāņu', 'cs' => 'Česky', + 'cy' => 'Cymraeg', 'da' => 'Dansk', 'de' => 'Deutsch (Sie)', 'de_informal' => 'Deutsch (Du)', @@ -308,6 +348,7 @@ 'lt' => 'Lietuvių Kalba', 'lv' => 'Latviešu Valoda', 'nb' => 'Norsk (Bokmål)', + 'ne' => 'नेपाली', 'nn' => 'Nynorsk', 'nl' => 'Nederlands', 'pl' => 'Polski', diff --git a/lang/lv/validation.php b/lang/lv/validation.php index 64a0dece367..dd318119a58 100644 --- a/lang/lv/validation.php +++ b/lang/lv/validation.php @@ -105,6 +105,11 @@ 'url' => ':attribute formāts nav derīgs.', 'uploaded' => 'Fails netika ielādēts. Serveris nevar pieņemt šāda izmēra failus.', + 'zip_file' => ':attribute ir jāatsaucas uz failu ZIP arhīvā.', + 'zip_file_mime' => ':attribute ir jāatsaucas uz failu ar tipu :validTypes, bet atrasts :foundType.', + 'zip_model_expected' => 'Sagaidīts datu objekts, bet atrasts ":type".', + 'zip_unique' => ':attribute jābūt unikālam šim objekta tipam ZIP arhīvā.', + // Custom validation lines 'custom' => [ 'password-confirm' => [ diff --git a/lang/nb/activities.php b/lang/nb/activities.php index c49a09c26dc..fd4de53216e 100644 --- a/lang/nb/activities.php +++ b/lang/nb/activities.php @@ -85,6 +85,14 @@ 'webhook_delete' => 'slettet webhook', 'webhook_delete_notification' => 'Webhook ble slettet', + // Imports + 'import_create' => 'import opprettet', + 'import_create_notification' => 'Importen ble opplastet', + 'import_run' => 'oppdatert import', + 'import_run_notification' => 'Innhold importert', + 'import_delete' => 'import slettet', + 'import_delete_notification' => 'Importering ble slettet', + // Users 'user_create' => 'opprettet bruker', 'user_create_notification' => 'Bruker ble opprettet', @@ -94,11 +102,11 @@ 'user_delete_notification' => 'Brukeren ble fjernet', // API Tokens - 'api_token_create' => 'opprettet api token', + 'api_token_create' => 'opprettet API-nøkkel', 'api_token_create_notification' => 'API-token er opprettet', - 'api_token_update' => 'oppdatert api token', + 'api_token_update' => 'oppdaterte API-nøkkel', 'api_token_update_notification' => 'API-token oppdatert', - 'api_token_delete' => 'slettet api token', + 'api_token_delete' => 'slettet API-nøkkel', 'api_token_delete_notification' => 'API-token ble slettet', // Roles @@ -120,6 +128,14 @@ 'comment_update' => 'oppdatert kommentar', 'comment_delete' => 'slettet kommentar', + // Sort Rules + 'sort_rule_create' => 'opprettet sorteringsregel', + 'sort_rule_create_notification' => 'Sorteringsregel opprettet med suksess', + 'sort_rule_update' => 'oppdatert sorteringsregel', + 'sort_rule_update_notification' => 'Sorteringsregel oppdatert med suksess', + 'sort_rule_delete' => 'slettet sorteringsregel', + 'sort_rule_delete_notification' => 'Sorteringsregel slettet med suksess', + // Other 'permissions_update' => 'oppdaterte tilganger', ]; diff --git a/lang/nb/auth.php b/lang/nb/auth.php index 1db179b5f37..125e6daf5d4 100644 --- a/lang/nb/auth.php +++ b/lang/nb/auth.php @@ -91,7 +91,7 @@ 'mfa_option_totp_title' => 'Mobilapplikasjon', 'mfa_option_totp_desc' => 'For å bruke flerfaktorautentisering trenger du en mobilapplikasjon som støtter TOTP-teknologien, slik som Google Authenticator, Authy eller Microsoft Authenticator.', 'mfa_option_backup_codes_title' => 'Sikkerhetskoder', - 'mfa_option_backup_codes_desc' => 'Lagre sikkerhetskoder til engangsbruk på et trygt sted, disse kan du bruke for å verifisere identiteten din.', + 'mfa_option_backup_codes_desc' => 'Genererer et sett med engangskoder som du kan bruke ved innlogging for å bekrefte identiteten din. Sørg for å lagre disse på et sikkert sted.', 'mfa_gen_confirm_and_enable' => 'Bekreft og aktiver', 'mfa_gen_backup_codes_title' => 'Konfigurasjon av sikkerhetskoder', 'mfa_gen_backup_codes_desc' => 'Lagre nedeforstående liste med koder på et trygt sted. Når du skal ha tilgang til systemet kan du bruke en av disse som en faktor under innlogging.', diff --git a/lang/nb/common.php b/lang/nb/common.php index 92950250f4c..38104ba0396 100644 --- a/lang/nb/common.php +++ b/lang/nb/common.php @@ -20,7 +20,7 @@ 'description' => 'Beskrivelse', 'role' => 'Rolle', 'cover_image' => 'Forside', - 'cover_image_description' => 'Bildet bør være ca. 440x250px.', + 'cover_image_description' => 'Dette bildet bør være omtrent 440x250px selv om det vil bli fleksibelt skalert og beskjært slik at brukergrensesnittet passer til forskjellige scenarier ved behov. Dette betyr at de faktiske dimensjonene for visning varierer.', // Actions 'actions' => 'Handlinger', @@ -30,6 +30,8 @@ 'create' => 'Opprett', 'update' => 'Oppdater', 'edit' => 'Rediger', + 'archive' => 'Archive', + 'unarchive' => 'Un-Archive', 'sort' => 'Sortér', 'move' => 'Flytt', 'copy' => 'Kopier', @@ -107,4 +109,7 @@ // Not directly used but available for convenience to users. 'privacy_policy' => 'Personvernregler', 'terms_of_service' => 'Bruksvilkår', + + // OpenSearch + 'opensearch_description' => 'Søk :appName', ]; diff --git a/lang/nb/editor.php b/lang/nb/editor.php index 2f3b22d8bd2..77b01f17ba9 100644 --- a/lang/nb/editor.php +++ b/lang/nb/editor.php @@ -13,6 +13,7 @@ 'cancel' => 'Avbryt', 'save' => 'Lagre', 'close' => 'Lukk', + 'apply' => 'Bruk', 'undo' => 'Angre', 'redo' => 'Gjør om', 'left' => 'Venstre', @@ -47,6 +48,7 @@ 'superscript' => 'Hevet skrift', 'subscript' => 'Senket skrift', 'text_color' => 'Tekstfarge', + 'highlight_color' => 'Highlight color', 'custom_color' => 'Egenvalgt farge', 'remove_color' => 'Fjern farge', 'background_color' => 'Bakgrunnsfarge', @@ -81,6 +83,9 @@ 'table_properties' => 'Tabellegenskaper', 'table_properties_title' => 'Tabellegenskaper', 'delete_table' => 'Slett tabell', + 'table_clear_formatting' => 'Fjern tabellformatering', + 'resize_to_contents' => 'Endre størrelsen til innhold', + 'row_header' => 'Radoverskrift', 'insert_row_before' => 'Sett inn rad før', 'insert_row_after' => 'Sett inn rad etter', 'delete_row' => 'Slett rad', @@ -144,6 +149,7 @@ 'url' => 'Nettlenke', 'text_to_display' => 'Synlig tekst', 'title' => 'Tittel', + 'browse_links' => 'Bla gjennom linker', 'open_link' => 'Åpne lenke', 'open_link_in' => 'Åpne i ...', 'open_link_current' => 'Samme vindu', @@ -160,6 +166,8 @@ 'about' => 'Om tekstredigeringsprogrammet', 'about_title' => 'Om HDSEHDF-tekstredigeringsprogrammet', 'editor_license' => 'Tekstbehandlerlisens og opphavsrett', + 'editor_lexical_license' => 'Denne redaktoren er bygget som en fork av :lexicalLink, som er distribuert under MIT-lisensen.', + 'editor_lexical_license_link' => 'Full lisensinformasjon finner du her.', 'editor_tiny_license' => 'Denne tekstredigereren er laget med :tinyLink som er lisensiert under MIT.', 'editor_tiny_license_link' => 'Informasjon om opphavsrett og lisens for TinyMCE finnes her.', 'save_continue' => 'Lagre side og fortsett', diff --git a/lang/nb/entities.php b/lang/nb/entities.php index b843fcc0d1b..50f3847d867 100644 --- a/lang/nb/entities.php +++ b/lang/nb/entities.php @@ -23,7 +23,7 @@ 'meta_updated' => 'Oppdatert :timeLength', 'meta_updated_name' => 'Oppdatert :timeLength av :user', 'meta_owned_name' => 'Eies av :user', - 'meta_reference_count' => 'Referenced by :count item|Referenced by :count items', + 'meta_reference_count' => 'Sitert på :count side|Sitert på :count sider', 'entity_select' => 'Velg entitet', 'entity_select_lack_permission' => 'Du har ikke tilgang til å velge dette elementet', 'images' => 'Bilder', @@ -39,6 +39,34 @@ 'export_pdf' => 'PDF Fil', 'export_text' => 'Tekstfil', 'export_md' => 'Markdownfil', + 'export_zip' => 'Flyttbar ZIP', + 'default_template' => 'Standard sidemal', + 'default_template_explain' => 'Tildel en sidemal som vil bli brukt som standardinnhold for alle nye sider i denne boken. Husk dette vil kun bli brukt hvis sideskaperen har tilgang til den valgte malsiden.', + 'default_template_select' => 'Velg en malside', + 'import' => 'Import', + 'import_validate' => 'Valider Import', + 'import_desc' => 'Importer bøker, kapitler & sider ved å bruke en flyttbar zip-eksport fra samme eller en annen forekomst. Velg en ZIP-fil for å fortsette. Når filen har blitt lastet opp og validert vil du kunne konfigurere & bekrefte importen i neste visning.', + 'import_zip_select' => 'Velg ZIP-filen som skal lastes opp', + 'import_zip_validation_errors' => 'Feil ble funnet under validering av den angitte ZIP-filen:', + 'import_pending' => 'Venter på import', + 'import_pending_none' => 'Ingen importer er startet.', + 'import_continue' => 'Fortsett import', + 'import_continue_desc' => 'Gjennomgå innholdet på grunn av at det importeres fra den opplastede ZIP-filen. Når klar, kjøre importen for å legge til innholdet i dette systemet. Den opplastede ZIP-importfilen vil automatisk bli fjernet ved vellykket import.', + 'import_details' => 'Importer detaljer', + 'import_run' => 'Kjør Import', + 'import_size' => ':size Import ZIP størrelse', + 'import_uploaded_at' => 'Opplastet :relativeTime', + 'import_uploaded_by' => 'Lastet opp av', + 'import_location' => 'Import posisjon', + 'import_location_desc' => 'Velg en mållokasjon for ditt importerte innhold. Du vil trenge relevante tillatelser for å opprette innenfor den posisjonen du velger.', + 'import_delete_confirm' => 'Er du sikker på at du vil slette denne importen?', + 'import_delete_desc' => 'Dette vil slette den opplastede importen av ZIP-filen og kan ikke angres.', + 'import_errors' => 'Import feil', + 'import_errors_desc' => 'Feil oppstod under importforsøket:', + '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', // Permissions and restrictions 'permissions' => 'Tilganger', @@ -132,9 +160,6 @@ 'books_edit_named' => 'Endre boken :bookName', 'books_form_book_name' => 'Boktittel', 'books_save' => 'Lagre bok', - 'books_default_template' => 'Default Page Template', - 'books_default_template_explain' => 'Assign a page template that will be used as the default content for all new pages in this book. Keep in mind this will only be used if the page creator has view access to those chosen template page.', - 'books_default_template_select' => 'Select a template page', 'books_permissions' => 'Boktilganger', 'books_permissions_updated' => 'Boktilganger oppdatert', 'books_empty_contents' => 'Ingen sider eller kapitler finnes i denne boken.', @@ -145,7 +170,9 @@ 'books_search_this' => 'Søk i boken', 'books_navigation' => 'Boknavigasjon', 'books_sort' => 'Sorter bokinnhold', - 'books_sort_desc' => 'Flytt kapitler og sider inni en bok for å omorganisere dem. Andre bøker kan bli lagt til slik at det er enklere å flytte frem og tilbake mellom dem.', + 'books_sort_desc' => 'Flytt kapitler og sider innen en bok for å reorganisere innholdet. Andre bøker kan legges til, noe som gjør det enkelt å flytte kapitler og sider mellom bøkene. Valgfritt kan en automatisk sorteringsregel settes for å automatisk sortere innholdet i denne boken ved endringer.', + 'books_sort_auto_sort' => 'Automatisk sorteringsalternativ', + 'books_sort_auto_sort_active' => 'Automatisk sortering aktiv: :sortName', 'books_sort_named' => 'Omorganisér :bookName (bok)', 'books_sort_name' => 'Sorter på navn', 'books_sort_created' => 'Sorter på opprettet dato', @@ -207,7 +234,7 @@ 'pages_delete_draft' => 'Slett utkastet', 'pages_delete_success' => 'Siden er slettet', 'pages_delete_draft_success' => 'Sideutkastet ble slettet', - 'pages_delete_warning_template' => 'This page is in active use as a book default page template. These books will no longer have a default page template assigned after this page is deleted.', + 'pages_delete_warning_template' => 'Denne siden er i aktiv bruk som en bok eller kapittelstandard sidemal. Disse bøkene eller kapitlene vil ikke lenger ha en standardmal som er tilordnet etter at denne siden er slettet.', 'pages_delete_confirm' => 'Er du sikker på at du vil slette siden?', 'pages_delete_draft_confirm' => 'Er du sikker på at du vil slette utkastet?', 'pages_editing_named' => 'Redigerer :pageName (side)', @@ -224,6 +251,8 @@ 'pages_edit_switch_to_markdown_clean' => '(Renset innhold)', 'pages_edit_switch_to_markdown_stable' => '(Urørt innhold)', 'pages_edit_switch_to_wysiwyg' => 'Bytt til WYSIWYG tekstredigering', + 'pages_edit_switch_to_new_wysiwyg' => 'Bytt til ny WYSIWYG', + 'pages_edit_switch_to_new_wysiwyg_desc' => '(In Beta Testing)', 'pages_edit_set_changelog' => 'Angi endringslogg', 'pages_edit_enter_changelog_desc' => 'Gi en kort beskrivelse av endringene dine', 'pages_edit_enter_changelog' => 'Se endringslogg', @@ -243,6 +272,7 @@ 'pages_md_insert_drawing' => 'Sett inn tegning', 'pages_md_show_preview' => 'Forhåndsvisning', 'pages_md_sync_scroll' => 'Synkroniser forhåndsvisningsrulle', + 'pages_md_plain_editor' => 'Plaintext editor', 'pages_drawing_unsaved' => 'Ulagret tegning funnet', 'pages_drawing_unsaved_confirm' => 'Ulagret tegningsdata ble funnet fra en tidligere mislykket lagring. Vil du gjenopprette og fortsette å redigere denne ulagrede tegningen?', 'pages_not_in_chapter' => 'Siden tilhører ingen kapittel', @@ -367,8 +397,11 @@ 'comment' => 'Kommentar', 'comments' => 'Kommentarer', 'comment_add' => 'Skriv kommentar', + 'comment_none' => 'No comments to display', 'comment_placeholder' => 'Skriv en kommentar her', - 'comment_count' => '{0} Ingen kommentarer|{1} 1 kommentar|[2,*] :count kommentarer', + 'comment_thread_count' => ':count Comment Thread|:count Comment Threads', + 'comment_archived_count' => ':count Archived', + 'comment_archived_threads' => 'Archived Threads', 'comment_save' => 'Publiser kommentar', 'comment_new' => 'Ny kommentar', 'comment_created' => 'kommenterte :createDiff', @@ -377,8 +410,14 @@ 'comment_deleted_success' => 'Kommentar fjernet', 'comment_created_success' => 'Kommentar skrevet', 'comment_updated_success' => 'Kommentar endret', + 'comment_archive_success' => 'Comment archived', + 'comment_unarchive_success' => 'Comment un-archived', + 'comment_view' => 'View comment', + 'comment_jump_to_thread' => 'Jump to thread', 'comment_delete_confirm' => 'Er du sikker på at du vil fjerne kommentaren?', 'comment_in_reply_to' => 'Som svar til :commentId', + 'comment_reference' => 'Reference', + 'comment_reference_outdated' => '(Outdated)', 'comment_editor_explain' => 'Her er kommentarene som er på denne siden. Kommentarer kan legges til og administreres når du ser på den lagrede siden.', // Revision @@ -409,7 +448,7 @@ // References 'references' => 'Referanser', 'references_none' => 'Det er ingen sporede referanser til dette elementet.', - 'references_to_desc' => 'Listed below is all the known content in the system that links to this item.', + 'references_to_desc' => 'Nedenfor vises alle de kjente sidene i systemet som lenker til denne oppføringen.', // Watch Options 'watch' => 'Overvåk', diff --git a/lang/nb/errors.php b/lang/nb/errors.php index c167fd49a83..400681b10d1 100644 --- a/lang/nb/errors.php +++ b/lang/nb/errors.php @@ -10,6 +10,7 @@ // Auth 'error_user_exists_different_creds' => 'En konto med :email finnes allerede, men har andre detaljer.', + 'auth_pre_register_theme_prevention' => 'Brukerkonto kunne ikke bli registrert for de angitte opplysningene', 'email_already_confirmed' => 'E-posten er allerede bekreftet, du kan forsøke å logge inn.', 'email_confirmation_invalid' => 'Denne bekreftelseskoden er allerede benyttet eller utgått. Prøv å registrere på nytt.', 'email_confirmation_expired' => 'Bekreftelseskoden er allerede utgått, en ny e-post er sendt.', @@ -23,7 +24,6 @@ 'saml_invalid_response_id' => 'Forespørselen fra det eksterne autentiseringssystemet gjenkjennes ikke av en prosess som startes av dette programmet. Å navigere tilbake etter pålogging kan forårsake dette problemet.', 'saml_fail_authed' => 'Innlogging gjennom :system feilet. Fikk ikke kontakt med autentiseringstjeneren.', 'oidc_already_logged_in' => 'Allerede logget inn', - 'oidc_user_not_registered' => 'Brukeren :name er ikke registrert og automatisk registrering er deaktivert', 'oidc_no_email_address' => 'Finner ikke en e-postadresse, for denne brukeren, i dataene som leveres av det eksterne autentiseringssystemet', 'oidc_fail_authed' => 'Innlogging ved hjelp av :system feilet, systemet ga ikke vellykket godkjenning', 'social_no_action_defined' => 'Ingen handlinger er definert', @@ -37,6 +37,7 @@ 'social_driver_not_found' => 'Autentiseringstjeneste fra sosiale medier er ikke installert', 'social_driver_not_configured' => 'Dine :socialAccount innstilliner er ikke angitt.', 'invite_token_expired' => 'Invitasjonslenken har utgått, du kan forsøke å be om nytt passord istede.', + 'login_user_not_found' => 'En bruker for denne handlingen ble ikke funnet.', // System 'path_not_writable' => 'Filstien :filePath aksepterer ikke filer, du må sjekke filstitilganger i systemet.', @@ -75,8 +76,9 @@ 'guests_cannot_save_drafts' => 'Gjester kan ikke lagre utkast', // Users - 'users_cannot_delete_only_admin' => 'Du kan ikke kaste ut den eneste administratoren', + 'users_cannot_delete_only_admin' => 'Du kan ikke slette den eneste administratoren', 'users_cannot_delete_guest' => 'Du kan ikke slette gjestebrukeren (Du kan deaktivere offentlig visning istede)', + 'users_could_not_send_invite' => 'Kunne ikke opprette bruker fordi invitasjons e-post ikke kunne sendes', // Roles 'role_cannot_be_edited' => 'Denne rollen kan ikke endres', @@ -103,6 +105,18 @@ 'app_down' => ':appName er nede for øyeblikket', 'back_soon' => 'Den vil snart komme tilbake.', + // Import + 'import_zip_cant_read' => 'Kunne ikke lese ZIP-filen.', + 'import_zip_cant_decode_data' => 'Kunne ikke finne og dekode ZIP data.json innhold.', + 'import_zip_no_data' => 'ZIP-fildata har ingen forventet bok, kapittel eller sideinnhold.', + 'import_validation_failed' => 'Import av ZIP feilet i å validere med feil:', + 'import_zip_failed_notification' => 'Kunne ikke importere ZIP-fil.', + 'import_perms_books' => 'Du mangler nødvendige tillatelser for å lage bøker.', + 'import_perms_chapters' => 'Du mangler de nødvendige tillatelsene for å opprette kapittel.', + 'import_perms_pages' => 'Du mangler nødvendige tillatelser for å opprette sider.', + 'import_perms_images' => 'Du mangler de nødvendige tillatelsene for å opprette bilder.', + 'import_perms_attachments' => 'Du mangler nødvendig tillatelse for å opprette vedlegg.', + // API errors 'api_no_authorization_found' => 'Ingen autorisasjonstoken ble funnet på forespørselen', 'api_bad_authorization_format' => 'Det ble funnet et autorisasjonstoken på forespørselen, men formatet virket feil', diff --git a/lang/nb/notifications.php b/lang/nb/notifications.php index 0d4bc266f11..bb46e09dbe8 100644 --- a/lang/nb/notifications.php +++ b/lang/nb/notifications.php @@ -13,7 +13,7 @@ 'updated_page_debounce' => 'For å forhindre mange varslinger, vil du ikke få nye varslinger for endringer på denne siden fra samme forfatter.', 'detail_page_name' => 'Sidenavn:', - 'detail_page_path' => 'Page Path:', + 'detail_page_path' => 'Side bane:', 'detail_commenter' => 'Kommentar fra:', 'detail_comment' => 'Kommentar:', 'detail_created_by' => 'Opprettet av:', diff --git a/lang/nb/preferences.php b/lang/nb/preferences.php index 502a343428c..245c9c954f1 100644 --- a/lang/nb/preferences.php +++ b/lang/nb/preferences.php @@ -27,7 +27,7 @@ 'notifications_save' => 'Lagre innstillinger', 'notifications_update_success' => 'Varslingsinnstillingene er oppdatert!', 'notifications_watched' => 'Overvåka & ignorerte elementer', - 'notifications_watched_desc' => ' Nedenfor er elementene som har egendefinerte varslingsinnstillinger i bruk. For å oppdatere innstillingene for disse, se elementet, finn varslingsalternativene i sidepanelet.', + 'notifications_watched_desc' => 'Nedenfor er elementene som har egendefinerte varslingsinnstillinger i bruk. For å oppdatere innstillingene for disse, se elementet, finn varslingsalternativene i sidepanelet.', 'auth' => 'Tilgang og sikkerhet', 'auth_change_password' => 'Endre passord', diff --git a/lang/nb/settings.php b/lang/nb/settings.php index 5d038c4f78d..68bab6f3038 100644 --- a/lang/nb/settings.php +++ b/lang/nb/settings.php @@ -74,6 +74,36 @@ 'reg_confirm_restrict_domain_desc' => 'Skriv inn en kommaseparert liste over e-postdomener du vil begrense registreringen til. Brukerne vil bli sendt en e-post for å bekrefte adressen deres før de får lov til å kommunisere med applikasjonen.
Vær oppmerksom på at brukere vil kunne endre e-postadressene sine etter vellykket registrering.', 'reg_confirm_restrict_domain_placeholder' => 'Ingen begrensninger er satt', + // Sorting Settings + 'sorting' => 'Sortering', + 'sorting_book_default' => 'Standard boksortering', + 'sorting_book_default_desc' => 'Velg standard sorteringsregelen som skal brukes for nye bøker. Dette vil ikke påvirke eksisterende bøker, og kan overstyres per bok.', + 'sorting_rules' => 'Sorteringsregler', + 'sorting_rules_desc' => 'Dette er forhåndsdefinerte sorteringsoperasjoner som kan brukes på innhold i systemet.', + 'sort_rule_assigned_to_x_books' => 'Tildelt til :count bok|Tildelt til :count bøker', + 'sort_rule_create' => 'Opprett sorteringsregel', + 'sort_rule_edit' => 'Rediger sorteringsregel', + 'sort_rule_delete' => 'Slett sorteringsregel', + 'sort_rule_delete_desc' => 'Fjern denne sorteringsregelen fra systemet. Bøker som bruker denne sorteringsregelen vil gå tilbake til manuell sortering.', + 'sort_rule_delete_warn_books' => 'Denne sorteringsregelen brukes for øyeblikket på :count bok/bøker. Er du sikker på at du vil slette denne?', + 'sort_rule_delete_warn_default' => 'Denne sorteringsregelen brukes for øyeblikket som standard for bøker. Er du sikker på at du vil slette denne?', + 'sort_rule_details' => 'Detaljer om sorteringsregel', + 'sort_rule_details_desc' => 'Angi et navn for denne sorteringsregelen, som vil vises i lister når brukerne velger en sorteringsmetode.', + 'sort_rule_operations' => 'Sorteringsoperasjoner', + 'sort_rule_operations_desc' => 'Konfigurer sorteringshandlinger ved å flytte dem fra listen over tilgjengelige operasjoner. Ved bruk vil operasjonene bli brukt i rekkefølge, fra topp til bunn. Eventuelle endringer gjort her vil bli brukt for alle tildelte bøker når du lagrer.', + 'sort_rule_available_operations' => 'Available Operations', + 'sort_rule_available_operations_empty' => 'No operations remaining', + 'sort_rule_configured_operations' => 'Configured Operations', + 'sort_rule_configured_operations_empty' => 'Dra/legg til operasjoner fra listen "Tilgjengelige operasjoner"', + 'sort_rule_op_asc' => '(Asc)', + 'sort_rule_op_desc' => '(Desc)', + 'sort_rule_op_name' => 'Navn - Alfabetisk', + 'sort_rule_op_name_numeric' => 'Name - Numeric', + 'sort_rule_op_created_date' => 'Created Date', + 'sort_rule_op_updated_date' => 'Updated Date', + 'sort_rule_op_chapters_first' => 'Kapitler først', + 'sort_rule_op_chapters_last' => 'Kapitler sist', + // Maintenance settings 'maint' => 'Vedlikehold', 'maint_image_cleanup' => 'Bildeopprydding', @@ -109,7 +139,7 @@ 'recycle_bin_contents_empty' => 'Papirkurven er for øyeblikket tom', 'recycle_bin_empty' => 'Tøm papirkurven', 'recycle_bin_empty_confirm' => 'Dette vil slette alle elementene i papirkurven permanent. Dette inkluderer innhold i hvert element. Er du sikker på at du vil tømme papirkurven?', - 'recycle_bin_destroy_confirm' => 'Denne handlingen vil permanent slette dette elementet og alle dets underelementer fra systemet, som beskrevet nedenfor. Du vil ikke kunne gjenopprette dette innholdet med mindre du har en tidligere sikkerhetskopi av databasen. Er du sikker på at du vil fortsette?', + 'recycle_bin_destroy_confirm' => 'Denne handlingen vil slette dette elementet permanent fra systemet, sammen med alle underelementer listet nedenfor, og du vil ikke kunne gjenopprette dette innholdet. Er du sikker på at du vil slette dette permanent?', 'recycle_bin_destroy_list' => 'Elementer som skal slettes', 'recycle_bin_restore_list' => 'Elementer som skal gjenopprettes', 'recycle_bin_restore_confirm' => 'Denne handlingen vil hente opp elementet fra papirkurven, inkludert underliggende innhold, til sin opprinnelige sted. Om den opprinnelige plassen har blitt slettet i mellomtiden og nå befinner seg i papirkurven, vil også dette bli hentet opp igjen.', @@ -162,6 +192,7 @@ 'role_access_api' => 'Systemtilgang API', 'role_manage_settings' => 'Behandle applikasjonsinnstillinger', 'role_export_content' => 'Eksporter innhold', + 'role_import_content' => 'Import innhold', 'role_editor_change' => 'Endre sideredigering', 'role_notifications' => 'Motta og administrere varslinger', 'role_asset' => 'Eiendomstillatelser', @@ -276,6 +307,14 @@ 'webhooks_last_errored' => 'Siste feil:', 'webhooks_last_error_message' => 'Siste feilmelding:', + // Licensing + 'licenses' => 'Lisenser', + 'licenses_desc' => 'Denne siden detaljerer lisensinformasjonen for BookStack, i tillegg til prosjektene & bibliotekene som brukes i BookStack. Mange av de oppførte prosjektene kan bare brukes i utviklingssammenheng.', + 'licenses_bookstack' => 'BookStack lisens', + 'licenses_php' => 'PHP Bibliotek lisenser', + 'licenses_js' => 'JavaScript bibliotek-lisenser', + 'licenses_other' => 'Andre lisenser', + 'license_details' => 'Lisens detaljer', //! If editing translations files directly please ignore this in all //! languages apart from en. Content will be auto-copied from en. @@ -287,6 +326,7 @@ 'bs' => 'Bosanski', 'ca' => 'Català', 'cs' => 'Česky', + 'cy' => 'Cymraeg', 'da' => 'Dansk', 'de' => 'Deutsch (Sie)', 'de_informal' => 'Deutsch (Du)', @@ -308,6 +348,7 @@ 'lt' => 'Lietuvių Kalba', 'lv' => 'Latviešu Valoda', 'nb' => 'Norsk (Bokmål)', + 'ne' => 'नेपाली', 'nn' => 'Nynorsk', 'nl' => 'Nederlands', 'pl' => 'Polski', diff --git a/lang/nb/validation.php b/lang/nb/validation.php index 7e3784e30c7..a156da8f705 100644 --- a/lang/nb/validation.php +++ b/lang/nb/validation.php @@ -105,6 +105,11 @@ 'url' => ':attribute format er ugyldig.', 'uploaded' => 'kunne ikke lastes opp, tjeneren støtter ikke filer av denne størrelsen.', + 'zip_file' => 'Attributtet :attribute må henvises til en fil i ZIP.', + 'zip_file_mime' => 'Attributtet :attribute må referere en fil av typen :validTypes, som ble funnet :foundType.', + 'zip_model_expected' => 'Data objekt forventet, men ":type" funnet.', + 'zip_unique' => 'Attributtet :attribute må være unikt for objekttypen i ZIP.', + // Custom validation lines 'custom' => [ 'password-confirm' => [ diff --git a/lang/ne/activities.php b/lang/ne/activities.php new file mode 100644 index 00000000000..4b4edf7be3a --- /dev/null +++ b/lang/ne/activities.php @@ -0,0 +1,140 @@ + 'पाना सिर्जना गरियो', + 'page_create_notification' => 'पाना सफलतापूर्वक सिर्जना गरियो', + 'page_update' => 'पाना अद्यावधिक गरियो', + 'page_update_notification' => 'पाना सफलतापूर्वक अद्यावधिक गरियो', + 'page_delete' => 'पाना हटाइयो', + 'page_delete_notification' => 'पाना सफलतापूर्वक हटाइयो', + 'page_restore' => 'पाना पुनर्स्थापित गरियो', + 'page_restore_notification' => 'पाना सफलतापूर्वक पुनर्स्थापित गरियो', + 'page_move' => 'पाना सारियो', + 'page_move_notification' => 'पाना सफलतापूर्वक सारियो', + + // Chapters + 'chapter_create' => 'अध्याय सिर्जना गरियो', + 'chapter_create_notification' => 'अध्याय सफलतापूर्वक सिर्जना गरियो', + 'chapter_update' => 'अध्याय अद्यावधिक गरियो', + 'chapter_update_notification' => 'अध्याय सफलतापूर्वक अद्यावधिक गरियो', + 'chapter_delete' => 'अध्याय हटाइयो', + 'chapter_delete_notification' => 'अध्याय सफलतापूर्वक हटाइयो', + 'chapter_move' => 'अध्याय सारियो', + 'chapter_move_notification' => 'अध्याय सफलतापूर्वक सारियो', + + // Books + 'book_create' => 'पुस्तक सिर्जना गरियो', + 'book_create_notification' => 'पुस्तक सफलतापूर्वक सिर्जना गरियो', + 'book_create_from_chapter' => 'अध्यायलाई पुस्तकमा परिणत गरियो', + 'book_create_from_chapter_notification' => 'अध्यायलाई पुस्तकमा सफलतापूर्वक परिणत गरियो', + 'book_update' => 'पुस्तक अद्यावधिक गरियो', + 'book_update_notification' => 'पुस्तक सफलतापूर्वक अद्यावधिक गरियो', + 'book_delete' => 'पुस्तक हटाइयो', + 'book_delete_notification' => 'पुस्तक सफलतापूर्वक हटाइयो', + 'book_sort' => 'पुस्तक क्रमबद्ध गरियो', + 'book_sort_notification' => 'पुस्तक सफलतापूर्वक क्रमबद्ध गरियो', + + // Bookshelves + 'bookshelf_create' => 'दराज बनाइयो', + 'bookshelf_create_notification' => 'दराज सफलतापूर्वक बनाइयो', + 'bookshelf_create_from_book' => 'पुस्तकलाई दराजमा परिणत गरियो', + 'bookshelf_create_from_book_notification' => 'पुस्तकलाई दराजमा सफलतापूर्वक परिणत गरियो', + 'bookshelf_update' => 'दराज अद्यावधिक गरियो', + 'bookshelf_update_notification' => 'दराज सफलतापूर्वक अद्यावधिक गरियो', + 'bookshelf_delete' => 'दराज हटाइयो', + 'bookshelf_delete_notification' => 'दराज सफलतापूर्वक हटाइयो', + + // Revisions + 'revision_restore' => 'संशोधन पुनर्स्थापित गरियो', + 'revision_delete' => 'संशोधन हटाइयो', + 'revision_delete_notification' => 'संशोधन सफलतापूर्वक हटाइयो', + + // Favourites + 'favourite_add_notification' => '":name" तपाईंको मनपर्नेमा थपिएको छ', + 'favourite_remove_notification' => '":name" तपाईंको मनपर्नेबाट हटाइएको छ', + + // Watching + 'watch_update_level_notification' => 'हेर्ने अभिरुचि सफलतापूर्वक अद्यावधिक गरियो', + + // Auth + 'auth_login' => 'लग इन भयो', + 'auth_register' => 'नयाँ प्रयोगकर्ता रूपमा दर्ता गरियो', + 'auth_password_reset_request' => 'प्रयोगकर्ताको पासवर्ड रिसेटको अनुरोध गरियो', + 'auth_password_reset_update' => 'प्रयोगकर्ता पासवर्ड रिसेट गर्नुहोस्', + 'mfa_setup_method' => 'MFA विधि सेटअप गरियो', + 'mfa_setup_method_notification' => 'बहु-कारक प्रमाणीकरण विधि सफलतापूर्वक सेटअप गरियो', + 'mfa_remove_method' => 'MFA हटाइयो', + 'mfa_remove_method_notification' => 'बहु-कारक प्रमाणीकरण विधि सफलतापूर्वक हटाइयो', + + // Settings + 'settings_update' => 'सेटिङहरू अद्यावधिक गरियो', + 'settings_update_notification' => 'सेटिङहरू सफलतापूर्वक अद्यावधिक गरियो', + 'maintenance_action_run' => 'मर्मत कार्य सञ्चालन गरियो', + + // Webhooks + 'webhook_create' => 'वेबहुक सिर्जना गरियो', + 'webhook_create_notification' => 'वेबहुक सफलतापूर्वक सिर्जना गरियो', + 'webhook_update' => 'वेबहुकअद्यावधिक गरियो', + 'webhook_update_notification' => 'वेबहुक सफलतापूर्वक अद्यावधिक गरियो', + 'webhook_delete' => 'वेबहुक हटाइयो', + 'webhook_delete_notification' => 'वेबहुक सफलतापूर्वक हटाइयो', + + // Imports + 'import_create' => 'आयात सिर्जना गरियो', + 'import_create_notification' => 'आयात सफलतापूर्वक अपलोड गरियो', + 'import_run' => 'आयात अद्यावधिक गरियो', + 'import_run_notification' => 'सामग्री सफलतापूर्वक आयात गरियो', + 'import_delete' => 'आयात हटाइयो', + 'import_delete_notification' => 'आयात सफलतापूर्वक हटाइयो', + + // Users + 'user_create' => 'प्रयोगकर्ता सिर्जना गरियो', + 'user_create_notification' => 'प्रयोगकर्ता सफलतापूर्वक सिर्जना गरियो', + 'user_update' => 'प्रयोगकर्ता अद्यावधिक गरियो', + 'user_update_notification' => 'प्रयोगकर्ता सफलतापूर्वक अद्यावधिक गरियो', + 'user_delete' => 'प्रयोगकर्ता हटाइयो', + 'user_delete_notification' => 'प्रयोगकर्ता सफलतापूर्वक हटाइयो', + + // API Tokens + 'api_token_create' => 'API टोकन सिर्जना गरियो', + 'api_token_create_notification' => 'API टोकन सफलतापूर्वक सिर्जना गरियो', + 'api_token_update' => 'API टोकन अद्यावधिक गरियो', + 'api_token_update_notification' => 'API टोकन सफलतापूर्वक अद्यावधिक गरियो', + 'api_token_delete' => 'API टोकन हटाइयो', + 'api_token_delete_notification' => 'API टोकन सफलतापूर्वक हटाइयो', + + // Roles + 'role_create' => 'भूमिका सिर्जना गरियो', + 'role_create_notification' => 'भूमिका सफलतापूर्वक सिर्जना गरियो', + 'role_update' => 'भूमिका अद्यावधिक गरियो', + 'role_update_notification' => 'भूमिका सफलतापूर्वक अद्यावधिक गरियो', + 'role_delete' => 'भूमिका हटाइयो', + 'role_delete_notification' => 'भूमिका सफलतापूर्वक हटाइयो', + + // Recycle Bin + 'recycle_bin_empty' => 'पुनः प्रयोगको डब्बा खाली गरियो', + 'recycle_bin_restore' => 'पुनः प्रयोगको डब्बाबाट पुनर्स्थापित गरियो', + 'recycle_bin_destroy' => 'पुनः प्रयोगको डब्बाबाट हटाइयो', + + // Comments + 'commented_on' => 'मा टिप्पणी गरियो', + 'comment_create' => 'टिप्पणी थपियो', + 'comment_update' => 'टिप्पणी अद्यावधिक गरियो', + 'comment_delete' => 'टिप्पणी मेटाइयो', + + // Sort Rules + 'sort_rule_create' => 'क्रम नियम सिर्जना गरियो', + 'sort_rule_create_notification' => 'क्रम नियम सफलतापूर्वक सिर्जना गरियो', + 'sort_rule_update' => 'क्रम नियम अद्यावधिक गरियो', + 'sort_rule_update_notification' => 'क्रम नियम सफलतापूर्वक अद्यावधिक गरियो', + 'sort_rule_delete' => 'क्रम नियम हटाइयो', + 'sort_rule_delete_notification' => 'क्रम नियम सफलतापूर्वक हटाइयो', + + // Other + 'permissions_update' => 'अनुमतिहरू अद्यावधिक गरियो', +]; diff --git a/lang/ne/auth.php b/lang/ne/auth.php new file mode 100644 index 00000000000..cd875a226cb --- /dev/null +++ b/lang/ne/auth.php @@ -0,0 +1,117 @@ + 'यी प्रमाणिकरण जानकारी हाम्रो अभिलेखसँग मेल खाँदैन।', + 'throttle' => 'लगइन प्रयासहरूको संख्या धेरै भएको छ। कृपया :seconds सेकेन्ड पछि पुनः प्रयास गर्नुहोस्।', + + // Login & Register + 'sign_up' => 'साइन अप गर्नुहोस्', + 'log_in' => 'लग इन गर्नुहोस्', + 'log_in_with' => ':socialDriver मार्फत लगइन गर्नुहोस्', + 'sign_up_with' => ':socialDriver प्रयोग गरेर साइन अप गर्नुहोस्', + 'logout' => 'लगआउट', + + 'name' => 'नाम', + 'username' => 'प्रयोगकर्ता नाम', + 'email' => 'ईमेल', + 'password' => 'पासवर्ड', + 'password_confirm' => 'पासवर्ड पक्का गर्नुहोस्', + 'password_hint' => 'कम्तिमा 8 अङ्कको हुनुपर्छ', + 'forgot_password' => 'पासवर्ड भुल्नुभयो?', + 'remember_me' => 'मलाई सम्झनुहोस्', + 'ldap_email_hint' => 'कृपया यस खाताको लागि प्रयोग गर्नको लागि इमेल प्रविष्ट गर्नुहोस्।', + 'create_account' => 'खाता बनाउनुहोस्', + 'already_have_account' => 'तपाईंको पहिले नै खाता छ?', + 'dont_have_account' => 'के तपाईंको खाता छैन?', + 'social_login' => 'सामाजिक लगइन', + 'social_registration' => 'सामाजिक दर्ता', + 'social_registration_text' => 'अर्को सेवाबाट दर्ता गर्नुहोस् र लगइन गर्नुहोस्।', + + 'register_thanks' => 'दर्ता गर्नुभएकोमा धन्यवाद!', + 'register_confirm' => 'कृपया तपाईंको इमेल जाँच गर्नुहोस् र :appName मा पहुँच पाउनको लागि पुष्टिकरण बटनमा क्लिक गर्नुहोस्।', + 'registrations_disabled' => 'दर्ता हाल बन्द गरिएको छ', + 'registration_email_domain_invalid' => 'त्यो इमेल डोमेनलाई यस आवेदनमा पहुँच छैन', + 'register_success' => 'साइन अप गर्नुभएकोमा धन्यवाद! तपाईं अब दर्ता र लगइन भइसकेका हुनुहुन्छ।', + + // Login auto-initiation + 'auto_init_starting' => 'लगइन प्रयास गर्दै', + 'auto_init_starting_desc' => 'हामी तपाईंको प्रमाणीकरण प्रणालीलाई लगइन प्रक्रिया सुरु गर्नका लागि सम्पर्क गर्दैछौं। यदि 5 सेकेन्डपछि प्रगति हुँदैन भने तलको लिङ्कमा क्लिक गर्न प्रयास गर्नुहोस्।', + 'auto_init_start_link' => 'प्रमाणीकरणसँग अगाडि बढ्नुहोस्', + + // Password Reset + 'reset_password' => 'पासवर्ड रिसेट गर्नुहोस्', + 'reset_password_send_instructions' => 'तपाईंको इमेल तल प्रविष्ट गर्नुहोस् र तपाईंलाई पासवर्ड रिसेट लिङ्क सहितको इमेल पठाइनेछ।', + 'reset_password_send_button' => 'रिसेट लिङ्क पठाउनुहोस्', + 'reset_password_sent' => ':email इमेलमा पासवर्ड रिसेट लिङ्क पठाइनेछ यदि त्यो इमेल ठेगाना प्रणालीमा फेला पारिन्छ भने।', + 'reset_password_success' => 'तपाईंको पासवर्ड सफलतापूर्वक रिसेट गरिएको छ।', + 'email_reset_subject' => ':appName पासवर्ड रिसेट गर्नुहोस्', + 'email_reset_text' => 'तपाईं यो इमेल प्राप्त गर्दै हुनुहुन्छ किनकि हामीले तपाईंको खाताको लागि पासवर्ड रिसेट अनुरोध प्राप्त गर्यौं।', + 'email_reset_not_requested' => 'यदि तपाईंले पासवर्ड रिसेट अनुरोध गर्नुभएको छैन भने, अगाडि कुनै कार्य आवश्यक पर्दैन।', + + // Email Confirmation + 'email_confirm_subject' => ':appName मा तपाईंको इमेल पुष्टि गर्नुहोस्', + 'email_confirm_greeting' => ':appName मा सामेल हुनुभएकोमा धन्यवाद!', + 'email_confirm_text' => 'कृपया तलको बटनमा क्लिक गरेर तपाईंको इमेल ठेगाना पुष्टि गर्नुहोस्:', + 'email_confirm_action' => 'इमेल पुष्टि गर्नुहोस्', + 'email_confirm_send_error' => 'इमेल पुष्टिकरण आवश्यक छ तर प्रणालीले इमेल पठाउन सकेन। इमेल सही तरिकाले सेटअप गरिएको छ भनी सुनिश्चित गर्न प्रशासकलाई सम्पर्क गर्नुहोस्।', + 'email_confirm_success' => 'तपाईंको इमेल पुष्टि भएको छ! अब तपाईं यो इमेल ठेगाना प्रयोग गरेर लगइन गर्न सक्षम हुनुहुनेछ।', + 'email_confirm_resent' => 'पुष्टिकरण इमेल पुनः पठाइएको छ, कृपया तपाईंको इनबक्स जाँच गर्नुहोस्।', + 'email_confirm_thanks' => 'पुष्टिकरणको लागि धन्यवाद!', + 'email_confirm_thanks_desc' => 'कृपया केही समय कुर्नुहोस् जबकि तपाईंको पुष्टिकरण प्रक्रिया गरिन्छ। यदि तपाईंलाई 3 सेकेन्ड पछि पुनः रिडिरेक्ट गरिएको छैन भने, तलको "अगाडि बढ्नुहोस्" लिङ्कमा क्लिक गर्नुहोस्।', + + 'email_not_confirmed' => 'इमेल ठेगाना पुष्टि गरिएको छैन', + 'email_not_confirmed_text' => 'तपाईंको इमेल ठेगाना अझै पुष्टि भएको छैन।', + 'email_not_confirmed_click_link' => 'कृपया तपाईंले दर्ता गर्दा पठाइएको इमेलमा रहेको लिङ्कमा क्लिक गर्नुहोस्।', + 'email_not_confirmed_resend' => 'यदि तपाईंलाई इमेल भेट्न गाह्रो भइरहेको छ भने, तपाईं तलको फारम द्वारा पुष्टिकरण इमेल पुनः पठाउन सक्नुहुन्छ।', + 'email_not_confirmed_resend_button' => 'पुष्टिकरण इमेल पुनः पठाउनुहोस्', + + // User Invite + 'user_invite_email_subject' => ':appName मा सामेल हुनका लागि तपाईंलाई आमन्त्रित गरिएको छ!', + 'user_invite_email_greeting' => ':appName मा तपाईंको खाता सिर्जना गरिएको छ।', + 'user_invite_email_text' => 'खाता पासवर्ड सेट गर्न र पहुँच प्राप्त गर्न तलको बटनमा क्लिक गर्नुहोस्:', + 'user_invite_email_action' => 'खाता पासवर्ड सेट गर्नुहोस्', + 'user_invite_page_welcome' => ':appName मा स्वागत छ!', + 'user_invite_page_text' => 'तपाईंको खाता अन्तिम रूप दिन र पहुँच प्राप्त गर्न तपाईंलाई पासवर्ड सेट गर्न आवश्यक छ जुन भविष्यका भ्रमणमा :appName मा लगइन गर्न प्रयोग हुनेछ।', + 'user_invite_page_confirm_button' => 'पासवर्ड पक्का गर्नुहोस्', + 'user_invite_success_login' => 'पासवर्ड सेट गरिएको छ, तपाईं अब तपाईंको सेट गरिएको पासवर्ड प्रयोग गरेर :appName मा लगइन गर्न सक्षम हुनुहुनेछ!', + + // Multi-factor Authentication + 'mfa_setup' => 'मल्टि-फ्याक्टर प्रमाणीकरण सेटअप गर्नुहोस्', + 'mfa_setup_desc' => 'तपाईंको प्रयोगकर्ता खाता सुरक्षा थप गर्न मल्टि-फ्याक्टर प्रमाणीकरण सेटअप गर्नुहोस्।', + 'mfa_setup_configured' => 'पहिले नै कन्फिगर गरिएको', + 'mfa_setup_reconfigure' => 'पुनः कन्फिगर गर्नुहोस्', + 'mfa_setup_remove_confirmation' => 'के तपाईं यो मल्टि-फ्याक्टर प्रमाणीकरण विधि हटाउन चाहानुहुन्छ?', + 'mfa_setup_action' => 'सेटअप', + 'mfa_backup_codes_usage_limit_warning' => 'तपाईंको ५ भन्दा कम ब्याकअप कोड बाँकी छन्, कृपया नयाँ सेट उत्पन्न गर्नुहोस् र सुरक्षित स्थानमा राख्नुहोस् ताकि तपाईंको खाता लकडाउन नहोस्।', + 'mfa_option_totp_title' => 'मोबाइल एप', + 'mfa_option_totp_desc' => 'मल्टि-फ्याक्टर प्रमाणीकरण प्रयोग गर्नको लागि तपाईंलाई Google Authenticator, Authy वा Microsoft Authenticator जस्ता TOTP समर्थित मोबाइल एपको आवश्यकता पर्छ।', + 'mfa_option_backup_codes_title' => 'ब्याकअप कोड', + 'mfa_option_backup_codes_desc' => 'एक सेट ब्याकअप कोड उत्पन्न गर्दछ जसलाई तपाईंले लगइन गर्दा आफ्नो पहिचान प्रमाणित गर्न प्रयोग गर्नुहुनेछ। यी सुरक्षित स्थानमा राख्नुहोस्।', + 'mfa_gen_confirm_and_enable' => 'पुष्टिकरण र सक्षम गर्नुहोस्', + 'mfa_gen_backup_codes_title' => 'ब्याकअप कोड सेटअप', + 'mfa_gen_backup_codes_desc' => 'तलको कोडहरूको सूची सुरक्षित स्थानमा राख्नुहोस्। प्रणालीमा पहुँच गर्दा तपाईंले एक कोडलाई दोस्रो प्रमाणीकरण विधिका रूपमा प्रयोग गर्नुहुनेछ।', + 'mfa_gen_backup_codes_download' => 'कोडहरू डाउनलोड गर्नुहोस्', + 'mfa_gen_backup_codes_usage_warning' => 'प्रत्येक कोड एक पटक मात्र प्रयोग गर्न सकिन्छ।', + 'mfa_gen_totp_title' => 'मोबाइल एप सेटअप', + 'mfa_gen_totp_desc' => 'मल्टि-फ्याक्टर प्रमाणीकरण प्रयोग गर्नको लागि तपाईंलाई Google Authenticator, Authy वा Microsoft Authenticator जस्ता TOTP समर्थित मोबाइल एपको आवश्यकता पर्छ।', + 'mfa_gen_totp_scan' => 'सुरु गर्नको लागि तलको QR कोड स्क्यान गर्नुहोस्।', + 'mfa_gen_totp_verify_setup' => 'सेटअप प्रमाणित गर्नुहोस्', + 'mfa_gen_totp_verify_setup_desc' => 'सुनिश्चित गर्नका लागि एक कोड प्रविष्ट गर्नुहोस्, तपाईंको प्रमाणीकरण एपबाट उत्पन्न गरिएको, तलको इनपुट बक्समा:', + 'mfa_gen_totp_provide_code_here' => 'तपाईंको एपबाट उत्पन्न गरिएको कोड यहाँ प्रदान गर्नुहोस्।', + 'mfa_verify_access' => 'पहुँच प्रमाणित गर्नुहोस्', + 'mfa_verify_access_desc' => 'तपाईंको प्रयोगकर्ता खाता थप प्रमाणीकरणको माध्यमबाट आफ्नो पहिचान प्रमाणित गर्नको लागि आवश्यक छ। कृपया अगाडि बढ्नको लागि तपाईंको कन्फिगर गरिएको विधि प्रयोग गर्नुहोस्।', + 'mfa_verify_no_methods' => 'कुनै तरिका कन्फिगर गरिएको छैन', + 'mfa_verify_no_methods_desc' => 'तपाईंको खातामा मल्टि-फ्याक्टर प्रमाणीकरण विधिहरू फेला परेका छैनन्। तपाईंलाई पहुँच प्राप्त गर्न कम्तिमा एक विधि सेटअप गर्न आवश्यक छ।', + 'mfa_verify_use_totp' => 'मोबाइल एप प्रयोग गरेर प्रमाणित गर्नुहोस्', + 'mfa_verify_use_backup_codes' => 'ब्याकअप कोड प्रयोग गरेर प्रमाणित गर्नुहोस्', + 'mfa_verify_backup_code' => 'ब्याकअप कोड', + 'mfa_verify_backup_code_desc' => 'तल तपाईंको बाँकी रहेका ब्याकअप कोडहरू मध्ये एउटा प्रविष्ट गर्नुहोस्:', + 'mfa_verify_backup_code_enter_here' => 'यहाँ ब्याकअप कोड प्रविष्ट गर्नुहोस्', + 'mfa_verify_totp_desc' => 'तपाईंको मोबाइल एपबाट उत्पन्न गरिएको कोड तल प्रविष्ट गर्नुहोस्:', + 'mfa_setup_login_notification' => 'मल्टि-फ्याक्टर विधि कन्फिगर गरिएको छ, कृपया अब कन्फिगर गरिएको विधि प्रयोग गरेर फेरि लगइन गर्नुहोस्।', +]; diff --git a/lang/ne/common.php b/lang/ne/common.php new file mode 100644 index 00000000000..696ddc6b4dc --- /dev/null +++ b/lang/ne/common.php @@ -0,0 +1,115 @@ + 'रद्द गर्नुहोस्', + 'close' => 'बन्द गर्नुहोस्', + 'confirm' => 'पुष्टि गर्नुहोस्', + 'back' => 'फिर्ता', + 'save' => 'सेभ गर्नुहोस्', + 'continue' => 'जारी राख्नुहोस्', + 'select' => 'छान्नुहोस्', + 'toggle_all' => 'सबै टगल गर्नुहोस्', + 'more' => 'थप', + + // Form Labels + 'name' => 'नाम', + 'description' => 'विवरण', + 'role' => 'भूमिका', + 'cover_image' => 'आवरण चित्र', + 'cover_image_description' => 'यो चित्र करिब 440x250px हुनुपर्छ, यद्यपि यो प्रयोगकर्ता इन्टरफेसमा आवश्यकताअनुसार लचिलो रूपमा स्केल र काटिने भएकाले देखिने वास्तविक आकार फरक हुन सक्छ।', + + // Actions + 'actions' => 'कार्यहरू', + 'view' => 'हेर्नुहोस्', + 'view_all' => 'सबै हेर्नुहोस्', + 'new' => 'नयाँ', + 'create' => 'सिर्जना गर्नुहोस्', + 'update' => 'अद्यावधिक गर्नुहोस्', + 'edit' => 'सम्पादन गर्नुहोस्', + 'archive' => 'अभिलेख राख्नुहोस्', + 'unarchive' => 'अभिलेख हटाउनुहोस्', + 'sort' => 'क्रमबद्ध गर्नुहोस्', + 'move' => 'सार्नुहोस्', + 'copy' => 'प्रतिलिपि गर्नुहोस्', + 'reply' => 'जवाफ दिनुहोस्', + 'delete' => 'हटाउनुहोस्', + 'delete_confirm' => 'हटाउने पुष्टि गर्नुहोस्', + 'search' => 'खोज्नुहोस्', + 'search_clear' => 'खोज हटाउनुहोस्', + 'reset' => 'रीसेट गर्नुहोस्', + 'remove' => 'हटाउनुहोस्', + 'add' => 'थप्नुहोस्', + 'configure' => 'कन्फिगर गर्नुहोस्', + 'manage' => 'व्यवस्थापन गर्नुहोस्', + 'fullscreen' => 'पूर्ण स्क्रिन', + 'favourite' => 'मनपर्ने', + 'unfavourite' => 'मनपर्नेबाट हटाउनुहोस्', + 'next' => 'अर्को', + 'previous' => 'अघिल्लो', + 'filter_active' => 'सक्रिय फिल्टर:', + 'filter_clear' => 'फिल्टर हटाउनुहोस्', + 'download' => 'डाउनलोड गर्नुहोस्', + 'open_in_tab' => 'ट्याबमा खोल्नुहोस्', + 'open' => 'खोल्नुहोस्', + + // Sort Options + 'sort_options' => 'क्रमबद्ध विकल्पहरू', + 'sort_direction_toggle' => 'क्रमबद्ध दिशा टगल', + 'sort_ascending' => 'बढ्दो क्रममा क्रमबद्ध गर्नुहोस्', + 'sort_descending' => 'घट्दो क्रममा क्रमबद्ध गर्नुहोस्', + 'sort_name' => 'नाम', + 'sort_default' => 'पूर्वनिर्धारित', + 'sort_created_at' => 'सिर्जना मिति', + 'sort_updated_at' => 'अद्यावधिक मिति', + + // Misc + 'deleted_user' => 'हटाइएको प्रयोगकर्ता', + 'no_activity' => 'देखाउनका लागि कुनै गतिविधि छैन', + 'no_items' => 'कुनै वस्तुहरू उपलब्ध छैनन्', + 'back_to_top' => 'शीर्षमा फर्कनुहोस्', + 'skip_to_main_content' => 'मुख्य सामग्रीमा जानुहोस्', + 'toggle_details' => 'विवरण टगल गर्नुहोस्', + 'toggle_thumbnails' => 'थम्बनेल टगल गर्नुहोस्', + 'details' => 'विवरण', + 'grid_view' => 'ग्रिड दृश्य', + 'list_view' => 'सूची दृश्य', + 'default' => 'पूर्वनिर्धारित', + 'breadcrumb' => 'ब्रेडक्रम्ब', + 'status' => 'स्थिति', + 'status_active' => 'सक्रिय', + 'status_inactive' => 'निष्क्रिय', + 'never' => 'कहिल्यै होइन', + 'none' => 'कुनै पनि होइन', + + // Header + 'homepage' => 'गृहपृष्ठ', + 'header_menu_expand' => 'हेडर मेनु विस्तार गर्नुहोस्', + 'profile_menu' => 'प्रोफाइल मेनु', + 'view_profile' => 'प्रोफाइल हेर्नुहोस्', + 'edit_profile' => 'प्रोफाइल सम्पादन गर्नुहोस्', + 'dark_mode' => 'गाढा मोड', + 'light_mode' => 'हल्का मोड', + 'global_search' => 'विश्वव्यापी खोज', + + // Layout tabs + 'tab_info' => 'जानकारी', + 'tab_info_label' => 'ट्याब: द्वितीय जानकारी देखाउनुहोस्', + 'tab_content' => 'सामग्री', + 'tab_content_label' => 'ट्याब: प्राथमिक सामग्री देखाउनुहोस्', + + // Email Content + 'email_action_help' => 'यदि तपाईं ":actionText" बटनमा क्लिक गर्न समस्या भइरहेको छ भने, तलको URL आफ्नो वेब ब्राउजरमा कपी गरेर पेस्ट गर्नुहोस्:', + 'email_rights' => 'सर्वाधिकार सुरक्षित', + + // Footer Link Options + // Not directly used but available for convenience to users. + 'privacy_policy' => 'गोपनीयता नीति', + 'terms_of_service' => 'सेवा सर्तहरू', + + // OpenSearch + 'opensearch_description' => ':appName खोज्नुहोस्', +]; diff --git a/lang/ne/components.php b/lang/ne/components.php new file mode 100644 index 00000000000..d488842e481 --- /dev/null +++ b/lang/ne/components.php @@ -0,0 +1,46 @@ + 'चित्र चयन गर्नुहोस्', + 'image_list' => 'चित्र सूची', + 'image_details' => 'चित्र विवरण', + 'image_upload' => 'चित्र अपलोड गर्नुहोस्', + 'image_intro' => 'यहाँ तपाईंले पहिले अपलोड गरिएका चित्रहरू चयन र व्यवस्थापन गर्न सक्नुहुन्छ।', + 'image_intro_upload' => '"चित्र अपलोड गर्नुहोस्" बटन प्रयोग गरेर वा चित्र फाइललाई यो विन्डोमा तानेर नयाँ चित्र अपलोड गर्नुहोस्।', + 'image_all' => 'सबै', + 'image_all_title' => 'सबै चित्रहरू हेर्नुहोस्', + 'image_book_title' => 'यस पुस्तकमा अपलोड गरिएका चित्रहरू हेर्नुहोस्', + 'image_page_title' => 'यस पृष्ठमा अपलोड गरिएका चित्रहरू हेर्नुहोस्', + 'image_search_hint' => 'चित्र नामद्वारा खोज्नुहोस्', + 'image_uploaded' => 'अपलोड गरिएको :uploadedDate', + 'image_uploaded_by' => ':userName द्वारा अपलोड गरिएको', + 'image_uploaded_to' => ':pageLink मा अपलोड गरिएको', + 'image_updated' => 'अद्यावधिक गरिएको :updateDate', + 'image_load_more' => 'थप लोड गर्नुहोस्', + 'image_image_name' => 'चित्र नाम', + 'image_delete_used' => 'यो चित्र तलका पृष्ठहरूमा प्रयोग भइरहेको छ।', + 'image_delete_confirm_text' => 'के तपाईं यो चित्र मेटाउन निश्चित हुनुहुन्छ?', + 'image_select_image' => 'चित्र चयन गर्नुहोस्', + 'image_dropzone' => 'चित्र ड्रप गर्नुहोस् वा अपलोड गर्न यहाँ क्लिक गर्नुहोस्', + 'image_dropzone_drop' => 'अपलोड गर्न यहाँ चित्र ड्रप गर्नुहोस्', + 'images_deleted' => 'चित्रहरू मेटाइयो', + 'image_preview' => 'चित्र पूर्वावलोकन', + 'image_upload_success' => 'चित्र सफलतापूर्वक अपलोड गरियो', + 'image_update_success' => 'चित्र विवरण सफलतापूर्वक अद्यावधिक गरियो', + 'image_delete_success' => 'चित्र सफलतापूर्वक मेटाइयो', + 'image_replace' => 'चित्र प्रतिस्थापन गर्नुहोस्', + 'image_replace_success' => 'चित्र फाइल सफलतापूर्वक अद्यावधिक गरियो', + 'image_rebuild_thumbs' => 'आकारका भेरिएसनहरू पुनर्निर्माण गर्नुहोस्', + 'image_rebuild_thumbs_success' => 'चित्र आकार भेरिएसनहरू सफलतापूर्वक पुनर्निर्माण गरियो!', + + // Code Editor + 'code_editor' => 'कोड सम्पादन गर्नुहोस्', + 'code_language' => 'कोड भाषा', + 'code_content' => 'कोड सामग्री', + 'code_session_history' => 'सेसन इतिहास', + 'code_save' => 'कोड सेभ गर्नुहोस्', +]; diff --git a/lang/ne/editor.php b/lang/ne/editor.php new file mode 100644 index 00000000000..f3b760ba8ea --- /dev/null +++ b/lang/ne/editor.php @@ -0,0 +1,182 @@ + 'सामान्य', + 'advanced' => 'उन्नत', + 'none' => 'कुनै पनि छैन', + 'cancel' => 'रद्द गर्नुहोस्', + 'save' => 'सेभ गर्नुहोस्', + 'close' => 'बन्द गर्नुहोस्', + 'apply' => 'लागु गर्नुहोस्', + 'undo' => 'पूर्ववत् गर्नुहोस्', + 'redo' => 'पुन: गर्नुहोस्', + 'left' => 'बायाँ', + 'center' => 'केन्द्र', + 'right' => 'दायाँ', + 'top' => 'माथि', + 'middle' => 'बीच', + 'bottom' => 'तल', + 'width' => 'चौडाइ', + 'height' => 'उचाइ', + 'More' => 'थप', + 'select' => 'छान्नुहोस्...', + + // Toolbar + 'formats' => 'ढाँचा', + 'header_large' => 'ठुलो शीर्षक', + 'header_medium' => 'मध्यम शीर्षक', + 'header_small' => 'सानो शीर्षक', + 'header_tiny' => 'अत्यन्त सानो शीर्षक', + 'paragraph' => 'प्याराग्राफ', + 'blockquote' => 'ब्लकउद्धरण', + 'inline_code' => 'इनलाइन कोड', + 'callouts' => 'कौलआउटहरू', + 'callout_information' => 'सूचना', + 'callout_success' => 'सफलता', + 'callout_warning' => 'चेतावनी', + 'callout_danger' => 'खतरा', + 'bold' => 'मोठो अक्षर', + 'italic' => 'तेरियो', + 'underline' => 'रेखाङ्कन', + 'strikethrough' => 'रेखाले काटिएको', + 'superscript' => 'सुपरस्क्रिप्ट', + 'subscript' => 'सबस्क्रिप्ट', + 'text_color' => 'पाठको रंग', + 'highlight_color' => 'Highlight color', + 'custom_color' => 'अनुकूलित रंग', + 'remove_color' => 'रंग हटाउनुहोस्', + 'background_color' => 'पृष्ठभूमि रंग', + 'align_left' => 'बायाँ संरेखण', + 'align_center' => 'केन्द्र संरेखण', + 'align_right' => 'दायाँ संरेखण', + 'align_justify' => 'समायोजन', + 'list_bullet' => 'बुलेट सूची', + 'list_numbered' => 'संख्याबद्ध सूची', + 'list_task' => 'कार्य सूची', + 'indent_increase' => 'इनडेन्ट बढाउनुहोस्', + 'indent_decrease' => 'इनडेन्ट घटाउनुहोस्', + 'table' => 'टेबल', + 'insert_image' => 'चित्र राख्नुहोस्', + 'insert_image_title' => 'चित्र राख्नुहोस्/सम्पादन गर्नुहोस्', + 'insert_link' => 'लिंक राख्नुहोस्/सम्पादन गर्नुहोस्', + 'insert_link_title' => 'लिंक राख्नुहोस्/सम्पादन गर्नुहोस्', + 'insert_horizontal_line' => 'क्षैतिज रेखा राख्नुहोस्', + 'insert_code_block' => 'कोड ब्लक राख्नुहोस्', + 'edit_code_block' => 'कोड ब्लक सम्पादन गर्नुहोस्', + 'insert_drawing' => 'ड्रइङ राख्नुहोस्/सम्पादन गर्नुहोस्', + 'drawing_manager' => 'ड्रइङ व्यवस्थापक', + 'insert_media' => 'मिडिया राख्नुहोस्/सम्पादन गर्नुहोस्', + 'insert_media_title' => 'मिडिया राख्नुहोस्/सम्पादन गर्नुहोस्', + 'clear_formatting' => 'ढाँचा सफा गर्नुहोस्', + 'source_code' => 'स्रोत कोड', + 'source_code_title' => 'स्रोत कोड', + 'fullscreen' => 'पूर्ण स्क्रीन', + 'image_options' => 'चित्र विकल्प', + + // Tables + 'table_properties' => 'टेबल गुणहरू', + 'table_properties_title' => 'टेबल गुणहरू', + 'delete_table' => 'टेबल मेटाउनुहोस्', + 'table_clear_formatting' => 'टेबलको ढाँचा सफा गर्नुहोस्', + 'resize_to_contents' => 'सामग्री अनुसार आकार मिलाउनुहोस्', + 'row_header' => 'पङ्क्ति शीर्षक', + 'insert_row_before' => 'अगाडिको पङ्क्ति राख्नुहोस्', + 'insert_row_after' => 'पछिको पङ्क्ति राख्नुहोस्', + 'delete_row' => 'पङ्क्ति मेटाउनुहोस्', + 'insert_column_before' => 'अगाडिको स्तम्भ राख्नुहोस्', + 'insert_column_after' => 'पछिको स्तम्भ राख्नुहोस्', + 'delete_column' => 'स्तम्भ मेटाउनुहोस्', + 'table_cell' => 'सेल', + 'table_row' => 'पङ्क्ति', + 'table_column' => 'स्तम्भ', + 'cell_properties' => 'सेल गुणहरू', + 'cell_properties_title' => 'सेल गुणहरू', + 'cell_type' => 'सेल प्रकार', + 'cell_type_cell' => 'सेल', + 'cell_scope' => 'स्कोप', + 'cell_type_header' => 'शीर्षक सेल', + 'merge_cells' => 'सेल मर्ज गर्नुहोस्', + 'split_cell' => 'सेल विभाजन गर्नुहोस्', + 'table_row_group' => 'पङ्क्ति समूह', + 'table_column_group' => 'स्तम्भ समूह', + 'horizontal_align' => 'क्षैतिज संरेखण', + 'vertical_align' => 'उर्ध्वाधर संरेखण', + 'border_width' => 'बोर्डर चौडाइ', + 'border_style' => 'बोर्डर शैली', + 'border_color' => 'बोर्डर रंग', + 'row_properties' => 'पङ्क्ति गुणहरू', + 'row_properties_title' => 'पङ्क्ति गुणहरू', + 'cut_row' => 'पङ्क्ति काट्नुहोस्', + 'copy_row' => 'पङ्क्ति कपी गर्नुहोस्', + 'paste_row_before' => 'अगाडिको पङ्क्तिमा पेस्ट गर्नुहोस्', + 'paste_row_after' => 'पछिको पङ्क्तिमा पेस्ट गर्नुहोस्', + 'row_type' => 'पङ्क्ति प्रकार', + 'row_type_header' => 'शीर्षक', + 'row_type_body' => 'शरीर', + 'row_type_footer' => 'तल', + 'alignment' => 'संरेखण', + 'cut_column' => 'स्तम्भ काट्नुहोस्', + 'copy_column' => 'स्तम्भ कपी गर्नुहोस्', + 'paste_column_before' => 'अगाडिको स्तम्भमा पेस्ट गर्नुहोस्', + 'paste_column_after' => 'पछिको स्तम्भमा पेस्ट गर्नुहोस्', + 'cell_padding' => 'सेल प्याडिङ', + 'cell_spacing' => 'सेल स्पेसिङ', + 'caption' => 'क्याप्सन', + 'show_caption' => 'क्याप्सन देखाउनुहोस्', + 'constrain' => 'अनुपात सीमित गर्नुहोस्', + 'cell_border_solid' => 'ठोस', + 'cell_border_dotted' => 'डटेड', + 'cell_border_dashed' => 'ड्यास्ड', + 'cell_border_double' => 'डबल', + 'cell_border_groove' => 'ग्रूभ', + 'cell_border_ridge' => 'रिज', + 'cell_border_inset' => 'इनसेट', + 'cell_border_outset' => 'आउटसेट', + 'cell_border_none' => 'कुनै पनि छैन', + 'cell_border_hidden' => 'लुकेको', + + // Images, links, details/summary & embed + 'source' => 'स्रोत', + 'alt_desc' => 'वैकल्पिक विवरण', + 'embed' => 'एम्बेड', + 'paste_embed' => 'तपाईंको एम्बेड कोड तल टाँस्नुहोस्:', + 'url' => 'URL', + 'text_to_display' => 'देखाउने पाठ', + 'title' => 'शीर्षक', + 'browse_links' => 'लिंकहरू ब्राउज गर्नुहोस्', + 'open_link' => 'लिंक खोल्नुहोस्', + 'open_link_in' => 'लिंक खोल्नुहोस् ...', + 'open_link_current' => 'हालको विन्डो', + 'open_link_new' => 'नयाँ विन्डो', + 'remove_link' => 'लिंक हटाउनुहोस्', + 'insert_collapsible' => 'टुंगो लाग्ने ब्लक राख्नुहोस्', + 'collapsible_unwrap' => 'अनर्याप गर्नुहोस्', + 'edit_label' => 'लेबल सम्पादन गर्नुहोस्', + 'toggle_open_closed' => 'खोल्ने/बन्द गर्ने टगल गर्नुहोस्', + 'collapsible_edit' => 'टुंगो लाग्ने ब्लक सम्पादन गर्नुहोस्', + 'toggle_label' => 'लेबल टगल गर्नुहोस्', + + // About view + 'about' => 'संपादकको बारेमा', + 'about_title' => 'WYSIWYG संपादकको बारेमा', + 'editor_license' => 'संपादक अनुमति र कपीराइट', + 'editor_lexical_license' => 'यो संपादक :lexicalLink को फोर्कको रूपमा निर्माण गरिएको हो जुन MIT लाइसेन्स अन्तर्गत वितरण गरिएको छ।', + 'editor_lexical_license_link' => 'पूरा लाइसेन्स विवरण यहाँ भेट्न सकिन्छ।', + 'editor_tiny_license' => 'यो संपादक :tinyLink प्रयोग गरेर निर्माण गरिएको हो जुन MIT लाइसेन्स अन्तर्गत उपलब्ध छ।', + 'editor_tiny_license_link' => 'TinyMCE को कपीराइट र लाइसेन्स विवरण यहाँ भेट्न सकिन्छ।', + 'save_continue' => 'पृष्ठ सेभ गरी जारी राख्नुहोस्', + 'callouts_cycle' => '(प्रकारहरू टगल गर्न थिचिरहनुहोस्)', + 'link_selector' => 'सामग्रीमा लिंक', + 'shortcuts' => 'सर्टकटहरू', + 'shortcut' => 'सर्टकट', + 'shortcuts_intro' => 'संपादकमा निम्न सर्टकटहरू उपलब्ध छन्:', + 'windows_linux' => '(विन्डोज/लिनक्स)', + 'mac' => '(म्याक)', + 'description' => 'विवरण', +]; diff --git a/lang/ne/entities.php b/lang/ne/entities.php new file mode 100644 index 00000000000..88b202deed3 --- /dev/null +++ b/lang/ne/entities.php @@ -0,0 +1,476 @@ + 'हालैमा सिर्जना गरिएको', + 'recently_created_pages' => 'हालैमा सिर्जना गरिएका पाना', + 'recently_updated_pages' => 'हालैमा अद्यावधिक गरिएका पाना', + 'recently_created_chapters' => 'हालैमा सिर्जना गरिएका अध्यायहरू', + 'recently_created_books' => 'हालैमा सिर्जना गरिएका पुस्तकहरू', + 'recently_created_shelves' => 'हालैमा सिर्जना गरिएका दराजहरू', + 'recently_update' => 'हालैमा अद्यावधिक गरिएको', + 'recently_viewed' => 'हालैमा हेरिएको', + 'recent_activity' => 'हालको गतिविधि', + 'create_now' => 'अहिले सिर्जना गर्नुहोस्', + 'revisions' => 'संशोधनहरू', + 'meta_revision' => 'संशोधन #:revisionCount', + 'meta_created' => 'सिर्जना गरिएको :timeLength', + 'meta_created_name' => ':user द्वारा सिर्जना गरिएको :timeLength', + 'meta_updated' => 'अद्यावधिक गरिएको :timeLength', + 'meta_updated_name' => ':user द्वारा अद्यावधिक गरिएको :timeLength', + 'meta_owned_name' => ':user द्वारा स्वामित्व गरिएको', + 'meta_reference_count' => ':count वस्तु द्वारा सन्दर्भित|:count वस्तुहरू द्वारा सन्दर्भित', + 'entity_select' => 'इकाई चयन', + 'entity_select_lack_permission' => 'तपाईंलाई यो वस्तु चयन गर्नको लागि आवश्यक अनुमति छैन', + 'images' => 'छविहरू', + 'my_recent_drafts' => 'मेरो हालका मस्यौदाहरू', + 'my_recently_viewed' => 'मेरो हालै हेरिएका पाना', + 'my_most_viewed_favourites' => 'मेरो सबैभन्दा हेरिएका मनपर्ने', + 'my_favourites' => 'मेरो मनपर्ने', + 'no_pages_viewed' => 'तपाईंले कुनै पाना हेरिसकेको छैन', + 'no_pages_recently_created' => 'हालै कुनै पाना सिर्जना गरिएको छैन', + 'no_pages_recently_updated' => 'हालै कुनै पाना अद्यावधिक गरिएको छैन', + 'export' => 'निर्यात', + 'export_html' => 'समाविष्ट वेब फाइल', + 'export_pdf' => 'PDF फाइल', + 'export_text' => 'साधारण टेक्स्ट फाइल', + 'export_md' => 'Markdown फाइल', + 'export_zip' => 'पोर्टेबल ZIP', + 'default_template' => 'पूर्वनिर्धारित पृष्ठ ढाँचा', + 'default_template_explain' => 'यस वस्तु भित्र सिर्जना गरिएका सबै पानाहरूको लागि पूर्वनिर्धारित सामग्रीको रूपमा प्रयोग हुने पृष्ठ ढाँचाको चयन गर्नुहोस्। ध्यान दिनुहोस् कि यो केवल प्रयोगकर्ता चयन गरेको ढाँचाको पृष्ठलाई हेर्ने अनुमति पाउँदा मात्र लागू हुनेछ।', + 'default_template_select' => 'पृष्ठ ढाँचा चयन गर्नुहोस्', + 'import' => 'आयात', + 'import_validate' => 'आयात प्रमाणित गर्नुहोस्', + 'import_desc' => 'पुस्तकहरू, अध्यायहरू र पृष्ठहरूलाई पोर्टेबल ZIP निर्यातको माध्यमबाट आयात गर्नुहोस्, चाहे त्यसैको, वा अन्य कुनै उदाहरणको। ZIP फाइल चयन गर्न जारी राख्नुहोस्। फाइल अपलोड र प्रमाणित भएपछि, तपाईंलाई आयातको कन्फिगर र पुष्टि गर्ने विकल्प हुनेछ।', + 'import_zip_select' => 'अपलोड गर्न ZIP फाइल चयन गर्नुहोस्', + 'import_zip_validation_errors' => 'प्रदान गरिएको ZIP फाइल प्रमाणित गर्दा निम्न त्रुटिहरू भेटिएका छन्:', + 'import_pending' => 'पर्खाइमा रहेका आयातहरू', + 'import_pending_none' => 'कुनै आयात सुरू गरिएको छैन।', + 'import_continue' => 'आयात जारी राख्नुहोस्', + 'import_continue_desc' => 'अपलोड गरिएको ZIP फाइलबाट आयात गरिने सामग्रीको समीक्षा गर्नुहोस्। तयार भएपछि, आयात सञ्चालन गर्नुहोस् र यसको सामग्री यस प्रणालीमा थप्नुहोस्। सफल आयातपछि, अपलोड गरिएको ZIP आयात फाइल स्वचालित रूपमा मेटाइनेछ।', + 'import_details' => 'आयात विवरण', + 'import_run' => 'आयात सञ्चालन गर्नुहोस्', + 'import_size' => ':size आयात ZIP साइज', + 'import_uploaded_at' => 'अपलोड गरिएको :relativeTime', + 'import_uploaded_by' => 'द्वारा अपलोड गरिएको', + 'import_location' => 'आयात स्थान', + 'import_location_desc' => 'आयात गरिएको सामग्रीको लागि लक्ष्य स्थान चयन गर्नुहोस्। तपाईंले चयन गरेको स्थानमा सिर्जना गर्नको लागि तपाईंलाई सम्बन्धित अनुमतिहरू आवश्यक पर्नेछन्।', + 'import_delete_confirm' => 'के तपाईं पक्का हुनुहुन्छ कि तपाईं यो आयात मेट्न चाहनुहुन्छ?', + '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', + + // Permissions and restrictions + 'permissions' => 'अनुमतिहरू', + 'permissions_desc' => 'यहाँ अनुमतिहरू सेट गर्नुहोस् जसले प्रयोगकर्ता भूमिकाद्वारा प्रदान गरिएका डिफल्ट अनुमतिहरूलाई ओभरराइड गर्दछ।', + 'permissions_book_cascade' => 'पुस्तकहरूमा सेट गरिएका अनुमतिहरू स्वचालित रूपमा सन्तान अध्यायहरू र पृष्ठहरूमा लागू हुनेछन्, जबसम्म तिनीहरूले आफ्नै अनुमतिहरू परिभाषित नगरेका हुँदैन।', + 'permissions_chapter_cascade' => 'अध्याीयमा सेट गरिएका अनुमतिहरू स्वचालित रूपमा सन्तान पृष्ठहरूमा लागू हुनेछन्, जबसम्म तिनीहरूले आफ्नै अनुमतिहरू परिभाषित नगरेका हुँदैन।', + 'permissions_save' => 'अनुमतिहरू बचत गर्नुहोस्', + 'permissions_owner' => 'स्वामी', + 'permissions_role_everyone_else' => 'अरु सबै', + 'permissions_role_everyone_else_desc' => 'प्रयोगकर्ता भूमिकामा विशेष रूपमा ओभरराइड नगरेको सबैका लागि अनुमतिहरू सेट गर्नुहोस्।', + 'permissions_role_override' => 'भूमिकाका लागि अनुमतिहरू ओभरराइड गर्नुहोस्', + 'permissions_inherit_defaults' => 'डिफल्टहरू मर्नुहोस्', + + // Search + 'search_results' => 'खोज परिणामहरू', + 'search_total_results_found' => ':count परिणाम फेला परे|:count कुल परिणामहरू फेला परे', + 'search_clear' => 'खोज मेटाउनुहोस्', + 'search_no_pages' => 'कुनै पाना यस खोजसँग मेल खाएका छैनन्', + 'search_for_term' => ':term को लागि खोजी गर्नुहोस्', + 'search_more' => 'थप परिणामहरू', + 'search_advanced' => 'उन्नत खोजी', + 'search_terms' => 'खोजी शब्दहरू', + 'search_content_type' => 'सामग्री प्रकार', + 'search_exact_matches' => 'सटीक मेलहरू', + 'search_tags' => 'ट्याग खोजी', + 'search_options' => 'विकल्पहरू', + 'search_viewed_by_me' => 'मैले हेरेको', + 'search_not_viewed_by_me' => 'मैले नहेरेको', + 'search_permissions_set' => 'अनुमतिहरू सेट गरिएका', + 'search_created_by_me' => 'मैले सिर्जना गरेको', + 'search_updated_by_me' => 'मैले अद्यावधिक गरेको', + 'search_owned_by_me' => 'मैले स्वामित्व गरेको', + 'search_date_options' => 'मिति विकल्पहरू', + 'search_updated_before' => 'अद्यावधिक गरिएको अघि', + 'search_updated_after' => 'अद्यावधिक गरिएको पछि', + 'search_created_before' => 'सिर्जना गरिएको अघि', + 'search_created_after' => 'सिर्जना गरिएको पछि', + 'search_set_date' => 'मिति सेट गर्नुहोस्', + 'search_update' => 'खोज अपडेट गर्नुहोस्', + + // Shelves + 'shelf' => 'दराज', + 'shelves' => 'दराजहरू', + 'x_shelves' => ':count दराज|:count दराजहरू', + 'shelves_empty' => 'कुनै दराज सिर्जना गरिएको छैन', + 'shelves_create' => 'नयाँ दराज सिर्जना गर्नुहोस्', + 'shelves_popular' => 'लोकप्रिय दराजहरू', + 'shelves_new' => 'नयाँ दराजहरू', + 'shelves_new_action' => 'नयाँ दराज', + 'shelves_popular_empty' => 'यहाँ सबैभन्दा लोकप्रिय दराजहरू देखिनेछन्।', + 'shelves_new_empty' => 'यहाँ सबैभन्दा नयाँ सिर्जना गरिएका दराजहरू देखिनेछन्।', + 'shelves_save' => 'दराज बचत गर्नुहोस्', + 'shelves_books' => 'यस दराजमा पुस्तकहरू', + 'shelves_add_books' => 'यस दराजमा पुस्तकहरू थप्नुहोस्', + 'shelves_drag_books' => 'पुस्तकहरू तल तान्नुहोस् यस दराजमा थप्नका लागि', + 'shelves_empty_contents' => 'यस दराजमा कुनै पुस्तकहरू असाइन गरिएको छैन', + 'shelves_edit_and_assign' => 'दराज सम्पादन गरेर पुस्तकहरू असाइन गर्नुहोस्', + 'shelves_edit_named' => 'दराज सम्पादन गर्नुहोस् :name', + 'shelves_edit' => 'दराज सम्पादन गर्नुहोस्', + 'shelves_delete' => 'दराज मेट्नुहोस्', + 'shelves_delete_named' => 'दराज मेट्नुहोस् :name', + 'shelves_delete_explain' => "यसले ':name' नामक दराज मेट्नेछ। समावेश गरिएका पुस्तकहरू मेटिने छैनन्।", + 'shelves_delete_confirmation' => 'के तपाईं यकिन हुनुहुन्छ कि तपाईं यस दराजलाई मेट्न चाहनुहुन्छ?', + 'shelves_permissions' => 'दराज अनुमतिहरू', + 'shelves_permissions_updated' => 'दराज अनुमतिहरू अद्यावधिक गरिएका', + 'shelves_permissions_active' => 'दराज अनुमतिहरू सक्रिय', + 'shelves_permissions_cascade_warning' => 'दराजमा सेट गरिएका अनुमतिहरू स्वचालित रूपमा समाविष्ट पुस्तकहरूमा क्यास्केड हुँदैनन्। यो कारणले कि पुस्तक एक भन्दा धेरै दराजमा अस्तित्वमा हुन सक्छ।', + 'shelves_permissions_create' => 'दराज सिर्जना अनुमतिहरू केवल बालक पुस्तकहरूमा अनुमतिहरूको कपी गर्नको लागि प्रयोग गरिन्छ। यी अनुमतिहरू पुस्तक सिर्जना गर्नको लागि छैन।', + 'shelves_copy_permissions_to_books' => 'पुस्तकहरूमा अनुमतिहरूको कपी गर्नुहोस्', + 'shelves_copy_permissions' => 'अनुमतिहरूको कपी गर्नुहोस्', + 'shelves_copy_permissions_explain' => 'यसले यस दराजको वर्तमान अनुमतिहरूलाई यसमा समावेश गरिएका सबै पुस्तकहरूमा लागू गर्नेछ। कृपया सुनिश्चित गर्नुहोस् कि कुनै पनि परिवर्तनहरू सेभ भएका छन्।', + 'shelves_copy_permission_success' => ':count पुस्तकहरूमा दराज अनुमतिहरू कपी गरिएका', + + // Books + 'book' => 'पुस्तक', + 'books' => 'पुस्तकहरू', + 'x_books' => ':count पुस्तक|:count पुस्तकहरू', + 'books_empty' => 'कुनै पुस्तकहरू सिर्जना गरिएका छैनन्', + 'books_popular' => 'लोकप्रिय पुस्तकहरू', + 'books_recent' => 'हालका पुस्तकहरू', + 'books_new' => 'नयाँ पुस्तकहरू', + 'books_new_action' => 'नयाँ पुस्तक', + 'books_popular_empty' => 'यहाँ सबैभन्दा लोकप्रिय पुस्तकहरू देखा पर्नेछन्।', + 'books_new_empty' => 'यहाँ सबैभन्दा हालसालै सिर्जना गरिएका पुस्तकहरू देखा पर्नेछन्।', + 'books_create' => 'नयाँ पुस्तक सिर्जना गर्नुहोस्', + 'books_delete' => 'पुस्तक मेट्नुहोस्', + 'books_delete_named' => 'पुस्तक मेट्नुहोस् :bookName', + 'books_delete_explain' => 'यो पुस्तकलाई नाम \':bookName\' मेट्नेछ। सबै पृष्ठहरू र अध्यायहरू हटाइनेछन्।', + 'books_delete_confirmation' => 'के तपाईं पक्का हुनुहुन्छ कि तपाईं यस पुस्तकलाई मेट्न चाहनुहुन्छ?', + 'books_edit' => 'पुस्तक सम्पादन गर्नुहोस्', + 'books_edit_named' => 'पुस्तक सम्पादन गर्नुहोस् :bookName', + 'books_form_book_name' => 'पुस्तकको नाम', + 'books_save' => 'पुस्तक बचत गर्नुहोस्', + 'books_permissions' => 'पुस्तक अनुमतिहरू', + 'books_permissions_updated' => 'पुस्तक अनुमतिहरू अद्यावधिक गरियो', + 'books_empty_contents' => 'यस पुस्तकको लागि कुनै पृष्ठहरू वा अध्यायहरू सिर्जना गरिएका छैनन्।', + 'books_empty_create_page' => 'नयाँ पृष्ठ सिर्जना गर्नुहोस्', + 'books_empty_sort_current_book' => 'हालको पुस्तकलाई वर्गीकृत गर्नुहोस्', + 'books_empty_add_chapter' => 'अध्याय थप्नुहोस्', + 'books_permissions_active' => 'पुस्तक अनुमतिहरू सक्रिय छन्', + 'books_search_this' => 'यस पुस्तकमा खोजी गर्नुहोस्', + 'books_navigation' => 'पुस्तक नेभिगेशन', + 'books_sort' => 'पुस्तक सामग्रीहरू वर्गीकृत गर्नुहोस्', + 'books_sort_desc' => 'पुस्तकमा अध्यायहरू र पृष्ठहरूलाई पुनः व्यवस्थित गर्नका लागि सार्नुहोस्। अन्य पुस्तकहरू थप्न सकिन्छ जसले अध्याय र पृष्ठहरूलाई पुस्तकहरू बीच सजिलै सर्न मद्दत गर्दछ। वैकल्पिक रूपमा एक स्वचालित वर्गीकरण नियम सेट गर्न सकिन्छ जसले पुस्तकको सामग्रीहरू परिवर्तन भएपछि स्वत: वर्गीकृत गर्छ।', + 'books_sort_auto_sort' => 'स्वचालित वर्गीकरण विकल्प', + 'books_sort_auto_sort_active' => 'स्वचालित वर्गीकरण सक्रिय: :sortName', + 'books_sort_named' => 'पुस्तक :bookName को वर्गीकरण गर्नुहोस्', + 'books_sort_name' => 'नाम अनुसार वर्गीकृत गर्नुहोस्', + 'books_sort_created' => 'सिर्जना मितिअनुसार वर्गीकृत गर्नुहोस्', + 'books_sort_updated' => 'अद्यावधिक मितिअनुसार वर्गीकृत गर्नुहोस्', + 'books_sort_chapters_first' => 'पहिले अध्यायहरू', + 'books_sort_chapters_last' => 'अन्तिममा अध्यायहरू', + 'books_sort_show_other' => 'अन्य पुस्तकहरू देखाउनुहोस्', + 'books_sort_save' => 'नयाँ क्रम बचत गर्नुहोस्', + 'books_sort_show_other_desc' => 'यहाँ अन्य पुस्तकहरू थप्नुहोस् जसले वर्गीकरण प्रक्रिया समावेश गर्न र पुस्तकहरू बीच सामग्री सजिलै पुनः व्यवस्थित गर्न मद्दत पुर्याउँछ।', + 'books_sort_move_up' => 'माथि सार्नुहोस्', + 'books_sort_move_down' => 'तल सार्नुहोस्', + 'books_sort_move_prev_book' => 'अघिल्लो पुस्तकमा सार्नुहोस्', + 'books_sort_move_next_book' => 'अर्को पुस्तकमा सार्नुहोस्', + 'books_sort_move_prev_chapter' => 'अघिल्लो अध्यायमा सार्नुहोस्', + 'books_sort_move_next_chapter' => 'अर्को अध्यायमा सार्नुहोस्', + 'books_sort_move_book_start' => 'पुस्तकको सुरुवातमा सार्नुहोस्', + 'books_sort_move_book_end' => 'पुस्तकको अन्त्यमा सार्नुहोस्', + 'books_sort_move_before_chapter' => 'अध्यानको अघि सार्नुहोस्', + 'books_sort_move_after_chapter' => 'अध्यानको पछि सार्नुहोस्', + 'books_copy' => 'पुस्तक प्रतिलिपि गर्नुहोस्', + 'books_copy_success' => 'पुस्तक सफलतापूर्वक प्रतिलिपि गरियो', + + // Chapters + 'chapter' => 'अध्याय', + 'chapters' => 'अध्यायहरू', + 'x_chapters' => ':count अध्याय|:count अध्यायहरू', + 'chapters_popular' => 'लोकप्रिय अध्यायहरू', + 'chapters_new' => 'नयाँ अध्याय', + 'chapters_create' => 'नयाँ अध्याय सिर्जना गर्नुहोस्', + 'chapters_delete' => 'अध्याय मेट्नुहोस्', + 'chapters_delete_named' => 'अध्याय मेट्नुहोस् :chapterName', + 'chapters_delete_explain' => 'यसले \':chapterName\' नामक अध्याय मेट्नेछ। यस अध्यायमा रहेका सबै पृष्ठहरू पनि मेटिनेछन्।', + 'chapters_delete_confirm' => 'के तपाईं यस अध्यायलाई मेट्न चाहनुहुन्छ?', + 'chapters_edit' => 'अध्याय सम्पादन गर्नुहोस्', + 'chapters_edit_named' => 'अध्याय सम्पादन गर्नुहोस् :chapterName', + 'chapters_save' => 'अध्याय बचत गर्नुहोस्', + 'chapters_move' => 'अध्याय सार्नुहोस्', + 'chapters_move_named' => 'अध्याय सार्नुहोस् :chapterName', + 'chapters_copy' => 'अध्याय प्रतिलिपि गर्नुहोस्', + 'chapters_copy_success' => 'अध्याय सफलतापूर्वक प्रतिलिपि गरिएको', + 'chapters_permissions' => 'अध्याय अनुमतिहरू', + 'chapters_empty' => 'हाल यस अध्यायमा कुनै पृष्ठहरू छैनन्।', + 'chapters_permissions_active' => 'अध्याय अनुमतिहरू सक्रिय छन्', + 'chapters_permissions_success' => 'अध्याय अनुमतिहरू अद्यावधिक गरिएका', + 'chapters_search_this' => 'यस अध्यायको खोजी गर्नुहोस्', + 'chapter_sort_book' => 'पुस्तक सॉर्ट गर्नुहोस्', + + // Pages + 'page' => 'पाना', + 'pages' => 'पानाहरू', + 'x_pages' => ':count पाना|:count पानाहरू', + 'pages_popular' => 'लोकप्रिय पानाहरू', + 'pages_new' => 'नयाँ पाना', + 'pages_attachments' => 'जोडिएका फाइलहरू', + 'pages_navigation' => 'पाना नेविगेसन', + 'pages_delete' => 'पाना मेट्नुहोस्', + 'pages_delete_named' => 'पाना मेट्नुहोस् :pageName', + 'pages_delete_draft_named' => 'मस्यौदा पाना मेट्नुहोस् :pageName', + 'pages_delete_draft' => 'मस्यौदा पाना मेट्नुहोस्', + 'pages_delete_success' => 'पाना मेटियो', + 'pages_delete_draft_success' => 'मस्यौदा पाना मेटियो', + 'pages_delete_warning_template' => 'यो पाना पुस्तक वा अध्यायको डिफल्ट पृष्ठ ढाँचाको रूपमा सक्रिय छ। यो पाना मेटिएपछि, ती पुस्तक वा अध्यायहरूमा डिफल्ट पृष्ठ ढाँचाको असाइनमेन्ट हट्नेछ।', + 'pages_delete_confirm' => 'के तपाईं यस पानालाई मेट्न चाहनुहुन्छ?', + 'pages_delete_draft_confirm' => 'के तपाईं मस्यौदा पानालाई मेट्न चाहनुहुन्छ?', + 'pages_editing_named' => 'पाना सम्पादन गर्दै :pageName', + 'pages_edit_draft_options' => 'मस्यौदा विकल्पहरू', + 'pages_edit_save_draft' => 'मस्यौदा बचत गर्नुहोस्', + 'pages_edit_draft' => 'पाना मस्यौदा सम्पादन गर्नुहोस्', + 'pages_editing_draft' => 'मस्यौदा सम्पादन गर्दै', + 'pages_editing_page' => 'पाना सम्पादन गर्दै', + 'pages_edit_draft_save_at' => 'मस्यौदा :time मा बचत गरिएको', + 'pages_edit_delete_draft' => 'मस्यौदा मेट्नुहोस्', + 'pages_edit_delete_draft_confirm' => 'के तपाईं आफ्नो मस्यौदा परिवर्तनहरू मेट्न चाहनुहुन्छ? सबै परिवर्तनहरू, अन्तिम पूर्ण बचतको पछि, हराउनेछन्।', + 'pages_edit_discard_draft' => 'मस्यौदा त्याग्नुहोस्', + 'pages_edit_switch_to_markdown' => 'Markdown सम्पादकमा स्विच गर्नुहोस्', + 'pages_edit_switch_to_markdown_clean' => '(साफ सामग्री)', + 'pages_edit_switch_to_markdown_stable' => '(स्थिर सामग्री)', + 'pages_edit_switch_to_wysiwyg' => 'WYSIWYG सम्पादकमा स्विच गर्नुहोस्', + 'pages_edit_switch_to_new_wysiwyg' => 'नयाँ WYSIWYG मा स्विच गर्नुहोस्', + 'pages_edit_switch_to_new_wysiwyg_desc' => '(बीटा परीक्षणमा)', + 'pages_edit_set_changelog' => 'चेंजलग सेट गर्नुहोस्', + 'pages_edit_enter_changelog_desc' => 'तपाईंले गरेका परिवर्तनहरूको छोटो विवरण लेख्नुहोस्', + 'pages_edit_enter_changelog' => 'चेंजलग लेख्नुहोस्', + 'pages_editor_switch_title' => 'संपादक स्विच गर्नुहोस्', + 'pages_editor_switch_are_you_sure' => 'के तपाईं पक्का हुनुहुन्छ कि तपाईं यस पानाको सम्पादक परिवर्तन गर्न चाहनुहुन्छ?', + 'pages_editor_switch_consider_following' => 'सम्पादक परिवर्तन गर्दा निम्न कुरा ध्यानमा राख्नुहोस्:', + 'pages_editor_switch_consideration_a' => 'एकपटक बचत भएपछि, नयाँ सम्पादक विकल्प भविष्यका सम्पादकहरूमा प्रयोग हुनेछ, जसमा त्यस्ता सम्पादकहरू पनि समावेश छन् जुन आफूले सम्पादकको प्रकार परिवर्तन गर्न सक्षम छैनन्।', + 'pages_editor_switch_consideration_b' => 'यसले केही परिस्थितिहरूमा विवरण र सिन्ट्याक्सको हानि हुन सक्छ।', + 'pages_editor_switch_consideration_c' => 'ट्याग वा चेंजलग परिवर्तनहरू, अन्तिम बचत पछि, यो परिवर्तनमा कायम रहनेछैन।', + 'pages_save' => 'पाना बचत गर्नुहोस्', + 'pages_title' => 'पाना शीर्षक', + 'pages_name' => 'पाना नाम', + 'pages_md_editor' => 'संपादक', + 'pages_md_preview' => 'पूर्वावलोकन', + 'pages_md_insert_image' => 'छवि समावेश गर्नुहोस्', + 'pages_md_insert_link' => 'संगठन लिंक समावेश गर्नुहोस्', + 'pages_md_insert_drawing' => 'चित्र समावेश गर्नुहोस्', + 'pages_md_show_preview' => 'पूर्वावलोकन देखाउनुहोस्', + 'pages_md_sync_scroll' => 'पूर्वावलोकन स्क्रोल सिंक गर्नुहोस्', + 'pages_md_plain_editor' => 'Plaintext editor', + 'pages_drawing_unsaved' => 'असुरक्षित चित्र भेटियो', + 'pages_drawing_unsaved_confirm' => 'अघिल्लो असफल चित्र बचत प्रयासबाट असुरक्षित चित्र डेटा भेटिएको छ। के तपाईं यस असुरक्षित चित्रलाई पुनर्स्थापना गरेर सम्पादन गर्न चाहनुहुन्छ?', + 'pages_not_in_chapter' => 'पाना कुनै अध्यायमा छैन', + 'pages_move' => 'पाना सार्नुहोस्', + 'pages_copy' => 'पाना प्रतिलिपि गर्नुहोस्', + 'pages_copy_desination' => 'प्रतिलिपि गन्तव्य', + 'pages_copy_success' => 'पाना सफलतापूर्वक प्रतिलिपि गरियो', + 'pages_permissions' => 'पाना अनुमतिहरू', + 'pages_permissions_success' => 'पाना अनुमतिहरू अद्यावधिक गरिएका', + 'pages_revision' => 'संशोधन', + 'pages_revisions' => 'पाना संशोधनहरू', + 'pages_revisions_desc' => 'तल सूचीबद्ध गरिएको छ यो पानाका सबै पुराना संशोधनहरू। तपाईं पुराना पृष्ठ संस्करणहरू फर्केर हेर्न, तुलना गर्न र पुनर्स्थापना गर्न सक्नुहुन्छ, यदि अनुमतिहरूले अनुमति दिएको छ भने। प्रणाली कन्फिगरेसनको आधारमा पुराना संशोधनहरू स्वचालित रूपमा मेटिने हुन सक्छ।', + 'pages_revisions_named' => ':pageName का पाना संशोधनहरू', + 'pages_revision_named' => ':pageName का पाना संशोधन', + 'pages_revision_restored_from' => 'पुनर्स्थापित गरिएको #:id; :summary', + 'pages_revisions_created_by' => 'द्वारा सिर्जना गरिएको', + 'pages_revisions_date' => 'संशोधन मिति', + 'pages_revisions_number' => '#', + 'pages_revisions_sort_number' => 'संशोधन संख्या', + 'pages_revisions_numbered' => 'संशोधन #:id', + 'pages_revisions_numbered_changes' => 'संशोधन #:id परिवर्तनहरू', + 'pages_revisions_editor' => 'संपादक प्रकार', + 'pages_revisions_changelog' => 'चेंजलग', + 'pages_revisions_changes' => 'परिवर्तनहरू', + 'pages_revisions_current' => 'हालको संस्करण:', + 'pages_revisions_preview' => 'पूर्वावलोकन गर्नुहोस्', + 'pages_revisions_restore' => 'पुनर्स्थापित गर्नुहोस्', + 'pages_revisions_none' => 'यस पानामा कुनै संशोधन छैन', + 'pages_copy_link' => 'लिंक प्रतिलिपि गर्नुहोस्', + 'pages_edit_content_link' => 'संपादकमा खण्डमा जानुहोस्', + 'pages_pointer_enter_mode' => 'खण्ड चयन मोडमा जानुहोस्', + 'pages_pointer_label' => 'पाना खण्ड विकल्पहरू', + 'pages_pointer_permalink' => 'पाना खण्ड स्थायी लिंक', + 'pages_pointer_include_tag' => 'पाना खण्ड समावेश ट्याग', + 'pages_pointer_toggle_link' => 'स्थायी लिंक मोड, समावेश ट्याग देखाउनका लागि थिच्नुहोस्', + 'pages_pointer_toggle_include' => 'समावेश ट्याग मोड, स्थायी लिंक देखाउनका लागि थिच्नुहोस्', + 'pages_permissions_active' => 'पाना अनुमतिहरू सक्रिय छन्', + 'pages_initial_revision' => 'प्रारम्भिक प्रकाशन', + 'pages_references_update_revision' => 'आन्तरिक लिंकहरूको प्रणाली स्वचालित अद्यावधिक', + 'pages_initial_name' => 'नयाँ पाना', + 'pages_editing_draft_notification' => 'तपाईं हाल एक मस्यौदा सम्पादन गर्दै हुनुहुन्छ जुन अन्तिम पटक :timeDiff मा बचत गरिएको थियो।', + 'pages_draft_edited_notification' => 'यो पाना त्यस समय पछि अद्यावधिक गरिएको छ। यस मस्यौदालाई त्याग्नु उचित हुनेछ।', + 'pages_draft_page_changed_since_creation' => 'यो पाना मस्यौदा सिर्जना भएपछि अद्यावधिक गरिएको छ। यस मस्यौदालाई त्याग्नुपर्छ वा कुनै पाना परिवर्तनहरू मेटिन नदिनुहोस्।', + 'pages_draft_edit_active' => [ + 'start_a' => ':count प्रयोगकर्ताले यस पानालाई सम्पादन सुरु गरेका छन्', + 'start_b' => ':userName ले यस पानालाई सम्पादन सुरु गरेका छन्', + 'time_a' => 'पृष्ठ अन्तिम पटक अद्यावधिक भएको समयदेखि', + 'time_b' => ':minCount मिनेटहरूको भित्र', + 'message' => ':start :time। कृपया एकअर्काका अपडेटहरू मेट्नुहोस्!', + ], + 'pages_draft_discarded' => 'मस्यौदा त्यागियो! सम्पादक वर्तमान पाना सामग्रीसँग अद्यावधिक गरिएको छ', + 'pages_draft_deleted' => 'मस्यौदा मेटियो! सम्पादक वर्तमान पाना सामग्रीसँग अद्यावधिक गरिएको छ', + 'pages_specific' => 'विशिष्ट पाना', + 'pages_is_template' => 'पाना ढांचा', + + // Editor Sidebar + 'toggle_sidebar' => 'साइडबार टगल गर्नुहोस्', + 'page_tags' => 'पाना ट्यागहरू', + 'chapter_tags' => 'अध्याय ट्यागहरू', + 'book_tags' => 'पुस्तक ट्यागहरू', + 'shelf_tags' => 'शेल्फ ट्यागहरू', + 'tag' => 'ट्याग', + 'tags' => 'ट्यागहरू', + 'tags_index_desc' => 'ट्यागहरू सिस्टममा सामग्रीसँग लचिलो प्रकारको वर्गीकरण लागू गर्न प्रयोग गर्न सकिन्छ। ट्यागहरूमा किम्बो मान र मान हो सक्छ, जसले वैकल्पिक हुन्छ। एकपटक लागू भए पछि, सामग्रीलाई ट्यागको नाम र मान प्रयोग गरेर खोजी गर्न सकिन्छ।', + 'tag_name' => 'ट्याग नाम', + 'tag_value' => 'ट्याग मान (वैकल्पिक)', + 'tags_explain' => "आफ्नो सामग्रीलाई राम्रोसँग वर्गीकरण गर्न केही ट्यागहरू थप्नुहोस्। \n तपाई थप संगठनको लागि ट्यागको मान पनि असाइन गर्न सक्नुहुन्छ।", + 'tags_add' => 'अर्को ट्याग थप्नुहोस्', + 'tags_remove' => 'यो ट्याग हटाउनुहोस्', + 'tags_usages' => 'कुल ट्याग प्रयोगहरू', + 'tags_assigned_pages' => 'पानामा असाइन गरिएको', + 'tags_assigned_chapters' => 'अध्यायहरूमा असाइन गरिएको', + 'tags_assigned_books' => 'पुस्तकहरूमा असाइन गरिएको', + 'tags_assigned_shelves' => 'शेल्फहरूमा असाइन गरिएको', + 'tags_x_unique_values' => ':count अनन्य मानहरू', + 'tags_all_values' => 'सबै मानहरू', + 'tags_view_tags' => 'ट्यागहरू हेर्नुहोस्', + 'tags_view_existing_tags' => 'अस्तित्वमा रहेका ट्यागहरू हेर्नुहोस्', + 'tags_list_empty_hint' => 'ट्यागहरू पृष्ठ सम्पादक साइडबार वा पुस्तक, अध्याय वा शेल्फको विवरण सम्पादन गर्दा असाइन गर्न सकिन्छ।', + 'attachments' => 'जोडिएका फाइलहरू', + 'attachments_explain' => 'केही फाइलहरू अपलोड गर्नुहोस् वा केही लिङ्कहरू जोड्नुहोस् जसलाई तपाईंको पानामा देखाउन चाहनुहुन्छ। यी पृष्ठ साइडबारमा देखिनेछन्।', + 'attachments_explain_instant_save' => 'यहाँका परिवर्तनहरू तुरुन्तै बचत हुन्छन्।', + 'attachments_upload' => 'फाइल अपलोड गर्नुहोस्', + 'attachments_link' => 'लिङ्क जोड्नुहोस्', + 'attachments_upload_drop' => 'वैकल्पिक रूपमा तपाईं यहाँ एक फाइल ड्र्याग र ड्रप गरेर अपलोड गर्न सक्नुहुन्छ।', + 'attachments_set_link' => 'लिङ्क सेट गर्नुहोस्', + 'attachments_delete' => 'के तपाईं यो जोडिएको फाइल मेट्न चाहनुहुन्छ?', + 'attachments_dropzone' => 'यहाँ फाइलहरू ड्र्याग गर्न र अपलोड गर्नको लागि ड्रप गर्नुहोस्', + 'attachments_no_files' => 'कुनै फाइलहरू अपलोड गरिएको छैन', + 'attachments_explain_link' => 'यदि तपाईंले कुनै फाइल अपलोड नगरीकन लिङ्क जोड्न चाहनुहुन्छ भने, तपाईं यसलाई थप्न सक्नुहुन्छ। यो अर्को पृष्ठको लिङ्क वा क्लाउडमा राखिएको फाइलको लिङ्क हुन सक्छ।', + 'attachments_link_name' => 'लिङ्कको नाम', + 'attachment_link' => 'जोडिएको फाइल लिङ्क', + 'attachments_link_url' => 'फाइलको लिङ्क', + 'attachments_link_url_hint' => 'साइट वा फाइलको URL', + 'attach' => 'जोड्नुहोस्', + 'attachments_insert_link' => 'पृष्ठमा जोडिएको फाइलको लिङ्क थप्नुहोस्', + 'attachments_edit_file' => 'फाइल सम्पादन गर्नुहोस्', + 'attachments_edit_file_name' => 'फाइलको नाम', + 'attachments_edit_drop_upload' => 'फाइलहरू ड्र्याग गर्नुहोस् वा यहाँ क्लिक गरेर अपलोड र ओभरराइट गर्नुहोस्', + 'attachments_order_updated' => 'जोडिएको फाइलहरूको क्रम अद्यावधिक गरियो', + 'attachments_updated_success' => 'जोडिएको फाइलको विवरण अद्यावधिक गरियो', + 'attachments_deleted' => 'जोडिएको फाइल मेटियो', + 'attachments_file_uploaded' => 'फाइल सफलतापूर्वक अपलोड गरिएको', + 'attachments_file_updated' => 'फाइल सफलतापूर्वक अद्यावधिक गरिएको', + 'attachments_link_attached' => 'लिङ्क सफलतापूर्वक पृष्ठसँग जोडिएको', + 'templates' => 'ढाँचाहरू', + 'templates_set_as_template' => 'पाना ढाँचाको रूपमा सेट गर्नुहोस्', + 'templates_explain_set_as_template' => 'तपाईं यस पानालाई ढाँचाको रूपमा सेट गर्न सक्नुहुन्छ ताकि यसको सामग्रीलाई अन्य पानाहरू सिर्जना गर्दा प्रयोग गर्न सकिन्छ। अन्य प्रयोगकर्ताहरू यस ढाँचालाई तब मात्र प्रयोग गर्न सक्छन् जब उनीहरूलाई यस पानाको भ्यू अनुमति छ।', + 'templates_replace_content' => 'पाना सामग्री प्रतिस्थापन गर्नुहोस्', + 'templates_append_content' => 'पाना सामग्रीमा थप्नुहोस्', + 'templates_prepend_content' => 'पाना सामग्री अगाडि थप्नुहोस्', + + // Profile View + 'profile_user_for_x' => ':time का लागि प्रयोगकर्ता', + 'profile_created_content' => 'सिर्जना गरिएको सामग्री', + 'profile_not_created_pages' => ':userName ले कुनै पानाहरू सिर्जना गरेका छैनन्', + 'profile_not_created_chapters' => ':userName ले कुनै अध्यायहरू सिर्जना गरेका छैनन्', + 'profile_not_created_books' => ':userName ले कुनै पुस्तकहरू सिर्जना गरेका छैनन्', + 'profile_not_created_shelves' => ':userName ले कुनै शेल्फहरू सिर्जना गरेका छैनन्', + + // Comments + 'comment' => 'टिप्पणी', + 'comments' => 'टिप्पणीहरू', + 'comment_add' => 'टिप्पणी थप्नुहोस्', + 'comment_none' => 'प्रदर्शन गर्न कुनै टिप्पणी छैन', + 'comment_placeholder' => 'यहाँ टिप्पणी छोड्नुहोस्', + 'comment_thread_count' => 'टिप्पणीहरू', + 'comment_archived_count' => ':count पुरानो', + 'comment_archived_threads' => 'पुरानो थ्रेडहरू', + 'comment_save' => 'टिप्पणी सेभ गर्नुहोस्', + 'comment_new' => 'नयाँ टिप्पणी', + 'comment_created' => ':createDiff मा टिप्पणी गरियो', + 'comment_updated' => ':updateDiff मा :username द्वारा अद्यावधिक गरिएको', + 'comment_updated_indicator' => 'अद्यावधिक गरिएको', + 'comment_deleted_success' => 'टिप्पणी मेटियो', + 'comment_created_success' => 'टिप्पणी थपियो', + 'comment_updated_success' => 'टिप्पणी अद्यावधिक गरियो', + 'comment_archive_success' => 'टिप्पणी पुरानो गरियो', + 'comment_unarchive_success' => 'टिप्पणी पुनः सक्रिय गरियो', + 'comment_view' => 'टिप्पणी हेर्नुहोस्', + 'comment_jump_to_thread' => 'थ्रेडमा जानुहोस्', + 'comment_delete_confirm' => 'के तपाईं यस टिप्पणीलाई मेट्न चाहनुहुन्छ?', + 'comment_in_reply_to' => ':commentId को जवाफमा', + 'comment_reference' => 'सन्दर्भ', + 'comment_reference_outdated' => '(अप्रचलित)', + 'comment_editor_explain' => 'यहाँ पृष्ठमा छोडिएका टिप्पणीहरू छन्। बचत गरिएको पृष्ठ हेरिरहँदा टिप्पणीहरू थप्न र व्यवस्थापन गर्न सकिन्छ।', + + // Revision + 'revision_delete_confirm' => 'के तपाईं यस संशोधनलाई मेट्न चाहनुहुन्छ?', + 'revision_restore_confirm' => 'के तपाईं यस संशोधनलाई पुनर्स्थापित गर्न चाहनुहुन्छ? हालको पृष्ठ सामग्री प्रतिस्थापित हुनेछ।', + 'revision_cannot_delete_latest' => 'अन्तिम संशोधन मेट्न सकिदैन।', + + // Copy view + 'copy_consider' => 'कृपया सामग्री प्रतिलिपि गर्दा तलका कुराहरू विचार गर्नुहोस्।', + 'copy_consider_permissions' => 'कस्टम अनुमति सेटिङहरू प्रतिलिपि गरिने छैन।', + 'copy_consider_owner' => 'तपाईं सबै प्रतिलिपि गरिएका सामग्रीका मालिक बन्नुहुनेछ।', + 'copy_consider_images' => 'पृष्ठ चित्र फाइलहरू नक्कल गरिने छैनन् र मौलिक चित्रहरूले ती पृष्ठसँगको सम्बन्ध कायम राख्नेछन् जहाँ तिनीहरू पहिले अपलोड गरिएको थिए।', + 'copy_consider_attachments' => 'पृष्ठ जडानहरू प्रतिलिपि गरिने छैनन्।', + 'copy_consider_access' => 'स्थान, मालिक वा अनुमतिहरूको परिवर्तनले यस सामग्रीलाई पहिले पहुँच नभएका प्रयोगकर्ताहरूलाई उपलब्ध गराउन सक्छ।', + + // Conversions + 'convert_to_shelf' => 'शेल्फमा रूपान्तरण गर्नुहोस्', + 'convert_to_shelf_contents_desc' => 'तपाईं यस पुस्तकलाई समान सामग्रीसँग नयाँ शेल्फमा रूपान्तरण गर्न सक्नुहुन्छ। यस पुस्तकमा भएका अध्यायहरू नयाँ पुस्तकहरूमा रूपान्तरण गरिनेछन्। यदि यस पुस्तकमा कुनै पृष्ठहरू छन् जुन कुनै अध्यायमा छैनन् भने, यस पुस्तकको नाम परिवर्तन गरिनेछ र ती पृष्ठहरू समावेश गरिनेछन्, र यस पुस्तकलाई नयाँ शेल्फको हिस्सा बनाइनेछ।', + 'convert_to_shelf_permissions_desc' => 'यस पुस्तकमा सेट गरिएका कुनै पनि अनुमतिहरू नयाँ शेल्फ र सबै नयाँ बाल पुस्तकहरूमा प्रतिलिपि गरिनेछन् जुन आफ्ना अनुमतिहरू लागू गरेका छैनन्। ध्यान दिनुहोस् कि शेल्फहरूमा अनुमतिहरू स्वत: सामग्रीमा लागू हुँदैनन्, जस्तै पुस्तकहरूमा।', + 'convert_book' => 'पुस्तक रूपान्तरण गर्नुहोस्', + 'convert_book_confirm' => 'के तपाईं पक्का हुनुहुन्छ कि तपाईं यस पुस्तकलाई रूपान्तरण गर्न चाहनुहुन्छ?', + 'convert_undo_warning' => 'यो सजिलै उल्टाउन सकिँदैन।', + 'convert_to_book' => 'पुस्तकमा रूपान्तरण गर्नुहोस्', + 'convert_to_book_desc' => 'तपाईं यस अध्यायलाई समान सामग्रीसँग नयाँ पुस्तकमा रूपान्तरण गर्न सक्नुहुन्छ। यस अध्यायमा सेट गरिएका कुनै पनि अनुमतिहरू नयाँ पुस्तकमा प्रतिलिपि गरिनेछन्, तर कुनै पनि पितृ पुस्तकबाट परिग्रहीत अनुमतिहरू प्रतिलिपि गरिने छैनन्, जसले पहुँच नियन्त्रणमा परिवर्तन ल्याउन सक्छ।', + 'convert_chapter' => 'अध्याय रूपान्तरण गर्नुहोस्', + 'convert_chapter_confirm' => 'के तपाईं पक्का हुनुहुन्छ कि तपाईं यस अध्यायलाई रूपान्तरण गर्न चाहनुहुन्छ?', + + // References + 'references' => 'सन्दर्भहरू', + 'references_none' => 'यस वस्तुमा कुनै ट्र्याक गरिएको सन्दर्भहरू छैनन्।', + 'references_to_desc' => 'तल सूचीबद्ध गरिएको छ सबै जानिएको सामग्री प्रणालीमा जुन यस वस्तुसँग लिंक गरिएको छ।', + + // Watch Options + 'watch' => 'हेर्नुहोस्', + 'watch_title_default' => 'पूर्वनिर्धारित प्राथमिकताहरू', + 'watch_desc_default' => 'हेर्नुहोस् केवल तपाईंका पूर्वनिर्धारित सूचनाको प्राथमिकताहरूमा फर्कनुहोस्।', + 'watch_title_ignore' => 'बेवास्ता गर्नुहोस्', + 'watch_desc_ignore' => 'सभी सूचनाहरू बेवास्ता गर्नुहोस्, प्रयोगकर्ता-स्तरका प्राथमिकताहरू सहित।', + 'watch_title_new' => 'नयाँ पृष्ठहरू', + 'watch_desc_new' => 'जब यस वस्तुमा कुनै नयाँ पृष्ठ सिर्जना गरिन्छ भने सूचित गर्नुहोस्।', + 'watch_title_updates' => 'सभी पृष्ठ अपडेटहरू', + 'watch_desc_updates' => 'सभी नयाँ पृष्ठ र पृष्ठ परिवर्तनहरूमा सूचित गर्नुहोस्।', + 'watch_desc_updates_page' => 'सभी पृष्ठ परिवर्तनहरूमा सूचित गर्नुहोस्।', + 'watch_title_comments' => 'सभी पृष्ठ अपडेटहरू र टिप्पणियाँ', + 'watch_desc_comments' => 'सभी नयाँ पृष्ठहरू, पृष्ठ परिवर्तनहरू र नयाँ टिप्पणीहरूमा सूचित गर्नुहोस्।', + 'watch_desc_comments_page' => 'पृष्ठ परिवर्तनहरू र नयाँ टिप्पणीहरूमा सूचित गर्नुहोस्।', + 'watch_change_default' => 'पूर्वनिर्धारित सूचनाका प्राथमिकताहरू परिवर्तन गर्नुहोस्', + 'watch_detail_ignore' => 'सूचनाहरू बेवास्ता गर्दै', + 'watch_detail_new' => 'नयाँ पृष्ठहरू हेर्नुहोस्', + 'watch_detail_updates' => 'नयाँ पृष्ठहरू र अपडेटहरू हेर्नुहोस्', + 'watch_detail_comments' => 'नयाँ पृष्ठहरू, अपडेटहरू र टिप्पणीहरू हेर्नुहोस्', + 'watch_detail_parent_book' => 'पितृ पुस्तक मार्फत हेर्नुहोस्', + 'watch_detail_parent_book_ignore' => 'पितृ पुस्तक मार्फत बेवास्ता गर्दै', + 'watch_detail_parent_chapter' => 'पितृ अध्याय मार्फत हेर्नुहोस्', + 'watch_detail_parent_chapter_ignore' => 'पितृ अध्याय मार्फत बेवास्ता गर्दै', +]; diff --git a/lang/ne/errors.php b/lang/ne/errors.php new file mode 100644 index 00000000000..79ee27c78d1 --- /dev/null +++ b/lang/ne/errors.php @@ -0,0 +1,133 @@ + 'तपाईंले अनुरोध गरिएको पाना पहुँच गर्नको लागि अनुमति प्राप्त गर्नुभएको छैन।', + 'permissionJson' => 'तपाईंले अनुरोध गरिएको क्रियाकलाप गर्नको लागि अनुमति प्राप्त गर्नुभएको छैन।', + + // Auth + 'error_user_exists_different_creds' => 'इमेल :email संग पहिले नै प्रयोगकर्ता अस्तित्वमा छ तर फरक प्रमाणपत्रहरूका साथ।', + 'auth_pre_register_theme_prevention' => 'प्रदत्त विवरणका लागि प्रयोगकर्ता खाता दर्ता गर्न सकिएन।', + 'email_already_confirmed' => 'इमेल पहिले नै प्रमाणित भइसकेको छ, कृपया लगइन प्रयास गर्नुहोस्।', + 'email_confirmation_invalid' => 'यो पुष्टि टोकन अवैध छ वा पहिले नै प्रयोग भइसकेको छ, कृपया पुनः दर्ता प्रयास गर्नुहोस्।', + 'email_confirmation_expired' => 'पुष्टि टोकन समाप्त भइसकेको छ, नयाँ पुष्टि इमेल पठाइएको छ।', + 'email_confirmation_awaiting' => 'यो खाताको इमेल ठेगाना प्रमाणित गर्न बाँकी छ।', + 'ldap_fail_anonymous' => 'LDAP पहुँच अज्ञात बाइन्ड प्रयोग गरेर असफल भएको छ।', + 'ldap_fail_authed' => 'LDAP पहुँच निर्दिष्ट dn र पासवर्ड विवरण प्रयोग गरेर असफल भएको छ।', + 'ldap_extension_not_installed' => 'LDAP PHP एक्स्टेन्सन इन्स्टल गरिएको छैन।', + 'ldap_cannot_connect' => 'LDAP सर्भरमा जडान गर्न सकिएन, आरम्भिक जडान असफल भएको छ।', + 'saml_already_logged_in' => 'पहिले नै लगइन हुनुहुन्छ।', + 'saml_no_email_address' => 'बाह्य प्रमाणीकरण प्रणालीले प्रदान गरेको डाटामा यस प्रयोगकर्ताको इमेल ठेगाना भेट्न सकिएन।', + 'saml_invalid_response_id' => 'बाह्य प्रमाणीकरण प्रणालीबाट आएको अनुरोध यस एप्लिकेशनद्वारा सुरु गरिएको प्रक्रिया द्वारा मान्यता प्राप्त छैन। लगइन पछि फर्किने प्रयास गर्दा यो समस्या उत्पन्न हुन सक्छ।', + 'saml_fail_authed' => ':system प्रयोग गरेर लगइन असफल भएको छ, प्रणालीले सफल प्रमाणिकरण प्रदान गरेको छैन।', + 'oidc_already_logged_in' => 'पहिले नै लगइन हुनुहुन्छ।', + 'oidc_no_email_address' => 'बाह्य प्रमाणीकरण प्रणालीले प्रदान गरेको डाटामा यस प्रयोगकर्ताको इमेल ठेगाना भेट्न सकिएन।', + 'oidc_fail_authed' => ':system प्रयोग गरेर लगइन असफल भएको छ, प्रणालीले सफल प्रमाणिकरण प्रदान गरेको छैन।', + 'social_no_action_defined' => 'कोई क्रियाकलाप परिभाषित गरिएको छैन।', + 'social_login_bad_response' => ":socialAccount लगइनको समयमा त्रुटि प्राप्त: \n:error", + 'social_account_in_use' => 'यो :socialAccount खाता पहिले नै प्रयोगमा छ, कृपया :socialAccount विकल्प मार्फत लगइन प्रयास गर्नुहोस्।', + 'social_account_email_in_use' => 'इमेल :email पहिले नै प्रयोगमा छ। यदि तपाईंको खाता छ भने, तपाईं आफ्नो प्रोफाइल सेटिङमा :socialAccount खाता जडान गर्न सक्नुहुन्छ।', + 'social_account_existing' => 'यो :socialAccount तपाईंको प्रोफाइलसँग पहिले नै जडान गरिएको छ।', + 'social_account_already_used_existing' => 'यो :socialAccount खाता पहिले नै अर्को प्रयोगकर्ताद्वारा प्रयोगमा छ।', + 'social_account_not_used' => 'यो :socialAccount खाता कुनै प्रयोगकर्तासँग जडान गरिएको छैन। कृपया यसलाई आफ्नो प्रोफाइल सेटिङमा जडान गर्नुहोस्।', + 'social_account_register_instructions' => 'यदि तपाईंको खाता छैन भने, तपाईं :socialAccount विकल्प प्रयोग गरेर खाता दर्ता गर्न सक्नुहुन्छ।', + 'social_driver_not_found' => 'सामाजिक ड्राइभर फेला पारिएको छैन।', + 'social_driver_not_configured' => 'तपाईंको :socialAccount सामाजिक सेटिङ सही तरिकाले कन्फिगर गरिएको छैन।', + 'invite_token_expired' => 'यो निमन्त्रणा लिंक समाप्त भइसकेको छ। तपाईं सट्टा आफ्नो खाता पासवर्ड रिसेट गर्न प्रयास गर्न सक्नुहुन्छ।', + 'login_user_not_found' => 'यो क्रियाकलापका लागि प्रयोगकर्ता फेला पारिएको छैन।', + + // System + 'path_not_writable' => 'फाइल पथ :filePath मा अपलोड गर्न सकिएन। कृपया यो पथ सर्भरमा लेख्न योग्य बनाउन सुनिश्चित गर्नुहोस्।', + 'cannot_get_image_from_url' => ':url बाट चित्र प्राप्त गर्न सकिएन।', + 'cannot_create_thumbs' => 'सर्भरले थम्बनेल बनाउन सक्दैन। कृपया तपाईंको सिस्टममा GD PHP एक्स्टेन्सन इन्स्टल गरिएको छ भनेर जाँच गर्नुहोस्।', + 'server_upload_limit' => 'सर्भरले यस आकारको अपलोड अनुमति दिंदैन। कृपया सानो फाइल आकारको प्रयास गर्नुहोस्।', + 'server_post_limit' => 'सर्भरले दिएको डेटा आकार प्राप्त गर्न सक्दैन। कृपया कम डेटा वा सानो फाइलको प्रयास गर्नुहोस्।', + 'uploaded' => 'सर्भरले यस आकारको अपलोड अनुमति दिंदैन। कृपया सानो फाइल आकारको प्रयास गर्नुहोस्।', + + // Drawing & Images + 'image_upload_error' => 'चित्र अपलोड गर्दा त्रुटि भयो।', + 'image_upload_type_error' => 'अपलोड गरिएको चित्र प्रकार अवैध छ।', + 'image_upload_replace_type' => 'चित्र फाइल प्रतिस्थापनहरू समान प्रकारका हुनुपर्छ।', + 'image_upload_memory_limit' => 'चित्र अपलोड गर्न र/वा थम्बनेल बनाउन असफल भएको छ, यो प्रणाली संसाधन सीमाहरूको कारणले हो।', + 'image_thumbnail_memory_limit' => 'चित्रको आकार भिन्नताहरू बनाउन असफल भएको छ, यो प्रणाली संसाधन सीमाहरूको कारणले हो।', + 'image_gallery_thumbnail_memory_limit' => 'ग्यालरी थम्बनेल बनाउन असफल भएको छ, यो प्रणाली संसाधन सीमाहरूको कारणले हो।', + 'drawing_data_not_found' => 'चित्रको डाटा लोड गर्न सकिएन। चित्र फाइल अब अस्तित्वमा नभएको हुन सक्छ वा तपाईंलाई यसमा पहुँचको अनुमति नहुन सक्छ।', + + // Attachments + 'attachment_not_found' => 'जोडिएको फाइल फेला परेन।', + 'attachment_upload_error' => 'जोडिएको फाइल अपलोड गर्दा त्रुटि भयो।', + + // Pages + 'page_draft_autosave_fail' => 'ड्राफ्ट बचत गर्न असफल भयो। यो पाना बचत गर्नु अघि कृपया इन्टरनेट जडान सुनिश्चित गर्नुहोस्।', + 'page_draft_delete_fail' => 'पाना ड्राफ्ट मेटाउन र वर्तमान पाना सामग्री ल्याउन असफल भयो।', + 'page_custom_home_deletion' => 'एक पाना लाई होमपाना को रूपमा सेट गर्दा मेटाउन सकिँदैन।', + + // Entities + 'entity_not_found' => 'इकाई फेला परेन।', + 'bookshelf_not_found' => 'शेल्फ फेला परेन।', + 'book_not_found' => 'पुस्तक फेला परेन।', + 'page_not_found' => 'पाना फेला परेन।', + 'chapter_not_found' => 'अध्याय फेला परेन।', + 'selected_book_not_found' => 'चयन गरिएको पुस्तक फेला परेन।', + 'selected_book_chapter_not_found' => 'चयन गरिएको पुस्तक वा अध्याय फेला परेन।', + 'guests_cannot_save_drafts' => 'अतिथिहरू ड्राफ्टहरू बचत गर्न सक्दैनन्।', + + // Users + 'users_cannot_delete_only_admin' => 'तपाईं केवल व्यवस्थापक भएको प्रयोगकर्तालाई मेटाउन सक्दैनौं।', + 'users_cannot_delete_guest' => 'तपाईं अतिथि प्रयोगकर्तालाई मेटाउन सक्दैनौं।', + 'users_could_not_send_invite' => 'प्रयोगकर्ता सिर्जना गर्न सकिएन, निमन्त्रणा इमेल पठाउन असफल भयो।', + + // Roles + 'role_cannot_be_edited' => 'यो भूमिका सम्पादन गर्न सकिँदैन।', + 'role_system_cannot_be_deleted' => 'यो भूमिका एक प्रणाली भूमिका हो र मेटाउन सकिँदैन।', + 'role_registration_default_cannot_delete' => 'यो भूमिका दर्ता गरेको डिफल्ट भूमिका भएकोले मेटाउन सकिँदैन।', + 'role_cannot_remove_only_admin' => 'यो प्रयोगकर्ता व्यवस्थापक भूमिकामा मात्र एकमात्र प्रयोगकर्ता हो। यसलाई हटाउन प्रयास गर्नु अघि अर्को प्रयोगकर्तालाई व्यवस्थापक भूमिका दिनुहोस्।', + + // Comments + 'comment_list' => 'टिप्पणीहरू प्राप्त गर्दा त्रुटि भयो।', + 'cannot_add_comment_to_draft' => 'तपाईं ड्राफ्टमा टिप्पणी थप्न सक्नुहुन्न।', + 'comment_add' => 'टिप्पणी थप्दा / अद्यावधिक गर्दा त्रुटि भयो।', + 'comment_delete' => 'टिप्पणी मेट्दा त्रुटि भयो।', + 'empty_comment' => 'खाली टिप्पणी थप्न सकिँदैन।', + + // Error pages + '404_page_not_found' => 'पाना फेला परेन।', + 'sorry_page_not_found' => 'माफ गर्नुहोस्, तपाईंले खोज्नुभएको पाना फेला परेन।', + 'sorry_page_not_found_permission_warning' => 'यदि तपाईंलाई यो पाना अस्तित्वमा हुनु पर्ने आशा थियो भने, तपाईंलाई यसलाई हेर्न अनुमति नहुन सक्छ।', + 'image_not_found' => 'चित्र फेला परेन।', + 'image_not_found_subtitle' => 'माफ गर्नुहोस्, तपाईंले खोज्नुभएको चित्र फाइल फेला परेन।', + 'image_not_found_details' => 'यदि तपाईंले यो चित्र फेला पार्नु पर्ने आशा राख्नु भएको थियो भने, यो मेटिएको हुन सक्छ।', + 'return_home' => 'गृहपृष्ठमा फर्कनुहोस्', + 'error_occurred' => 'एउटा त्रुटि भयो।', + 'app_down' => ':appName अहिले डाउन छ।', + 'back_soon' => 'यो चाँडै पुनः सक्रिय हुनेछ।', + + // Import + 'import_zip_cant_read' => 'ZIP फाइल पढ्न सकिएन।', + 'import_zip_cant_decode_data' => 'ZIP डाटा.json सामग्री पत्ता लाग्न र डिकोड गर्न सकिएन।', + 'import_zip_no_data' => 'ZIP फाइल डाटामा अपेक्षित पुस्तक, अध्याय वा पाना सामग्री छैन।', + 'import_validation_failed' => 'आयात ZIP प्रमाणीकरण असफल भयो। त्रुटिहरू छन्:', + 'import_zip_failed_notification' => 'ZIP फाइल आयात गर्न असफल भयो।', + 'import_perms_books' => 'तपाईंलाई पुस्तकहरू सिर्जना गर्न आवश्यक अनुमति छैन।', + 'import_perms_chapters' => 'तपाईंलाई अध्यायहरू सिर्जना गर्न आवश्यक अनुमति छैन।', + 'import_perms_pages' => 'तपाईंलाई पाना सिर्जना गर्न आवश्यक अनुमति छैन।', + 'import_perms_images' => 'तपाईंलाई चित्रहरू सिर्जना गर्न आवश्यक अनुमति छैन।', + 'import_perms_attachments' => 'तपाईंलाई अनुलग्नकहरू सिर्जना गर्न आवश्यक अनुमति छैन।', + + // API errors + 'api_no_authorization_found' => 'अनुरोधमा कुनै प्रमाणीकरण टोकन फेला परेन।', + 'api_bad_authorization_format' => 'अनुरोधमा प्रमाणीकरण टोकन फेला परे तापनि यसको ढाँचा गलत देखिन्छ।', + 'api_user_token_not_found' => 'दिएको प्रमाणीकरण टोकनको लागि मिल्दो API टोकन फेला परेन।', + 'api_incorrect_token_secret' => 'दिइएको API टोकनको लागि प्रदान गरिएको गोप्य सही छैन।', + 'api_user_no_api_permission' => 'API टोकनको मालिकसँग API कल गर्ने अनुमति छैन।', + 'api_user_token_expired' => 'प्रमाणीकरण टोकन समाप्त भइसकेको छ।', + + // Settings & Maintenance + 'maintenance_test_email_failure' => 'टेस्ट इमेल पठाउँदा त्रुटि:', + + // HTTP errors + 'http_ssr_url_no_match' => 'URL कन्फिगर गरिएका अनुमत SSR होस्टसँग मेल खाँदैन।', +]; diff --git a/lang/ne/notifications.php b/lang/ne/notifications.php new file mode 100644 index 00000000000..1e9c572215a --- /dev/null +++ b/lang/ne/notifications.php @@ -0,0 +1,27 @@ + 'नयाँ टिप्पणी: :pageName पानामा', + 'new_comment_intro' => 'एक प्रयोगकर्ताले :appName मा रहेको पानामा टिप्पणी गरेका छन्:', + 'new_page_subject' => 'नयाँ पाना: :pageName', + 'new_page_intro' => ':appName मा नयाँ पाना बनाइएको छ', + 'updated_page_subject' => 'पाना अपडेट भयो: :pageName', + 'updated_page_intro' => ':appName मा पाना अपडेट गरिएको छ', + 'updated_page_debounce' => 'धेरै सूचना नपरोस् भनेर, केही समयको लागि एउटै सम्पादकबाट हुने थप सम्पादनहरूका सूचना तपाईंलाई पठाइने छैन।', + + 'detail_page_name' => 'पानाको नाम:', + 'detail_page_path' => 'पानाको स्थान:', + 'detail_commenter' => 'टिप्पणी गर्ने:', + 'detail_comment' => 'टिप्पणी:', + 'detail_created_by' => 'बनाउने व्यक्ति:', + 'detail_updated_by' => 'अपडेट गर्ने व्यक्ति:', + + 'action_view_comment' => 'टिप्पणी हेर्नुहोस्', + 'action_view_page' => 'पाना हेर्नुहोस्', + + 'footer_reason' => 'तपाईंलाई यो सूचना :link अनुसार पठाइएको हो, जुन यस प्रकारको गतिविधिमा लागु हुन्छ।', + 'footer_reason_link' => 'तपाईंको सूचना प्राथमिकता', +]; diff --git a/lang/ne/pagination.php b/lang/ne/pagination.php new file mode 100644 index 00000000000..3da50cb08d8 --- /dev/null +++ b/lang/ne/pagination.php @@ -0,0 +1,12 @@ + '« अघिल्लो', + 'next' => 'अर्को »', + +]; diff --git a/lang/ne/passwords.php b/lang/ne/passwords.php new file mode 100644 index 00000000000..684201a8e56 --- /dev/null +++ b/lang/ne/passwords.php @@ -0,0 +1,15 @@ + 'पासवर्ड कम्तिमा ८ वर्णको हुनु पर्छ र दाेहाेर्याइएकाे पासवर्ड संग मेल खानु पर्छ।', + 'user' => "हामीले त्यो इमेल ठेगाना भएको प्रयोगकर्ता फेला पार्न सकेनौं।", + 'token' => 'यस इमेल ठेगानाको लागि पासवर्ड रिसेट टोकन अमान्य छ।', + 'sent' => 'हामीले तपाईंको पासवर्ड रिसेट लिङ्क इमेल गरेका छौं!', + 'reset' => 'पासवर्ड रिसेट भयो!', + +]; diff --git a/lang/ne/preferences.php b/lang/ne/preferences.php new file mode 100644 index 00000000000..70388e8baef --- /dev/null +++ b/lang/ne/preferences.php @@ -0,0 +1,51 @@ + 'मेरो खाता', + + 'shortcuts' => 'सर्टकटहरू', + 'shortcuts_interface' => 'UI सर्टकट प्राथमिकताहरू', + 'shortcuts_toggle_desc' => 'यहाँ तपाईं किबोर्ड प्रणाली इन्टरफेस सर्टकटहरू सक्षम वा असक्षम गर्न सक्नुहुन्छ, जुन नेभिगेशन र क्रियाकलापहरूको लागि प्रयोग गरिन्छ।', + 'shortcuts_customize_desc' => 'तपाईं तलका प्रत्येक सर्टकटलाई अनुकूलित गर्न सक्नुहुन्छ। केवल सर्टकटको इनपुट चयन गरेपछि आफ्नो इच्छित कीबोर्ड संयोजन थिच्नुहोस्।', + 'shortcuts_toggle_label' => 'किबोर्ड सर्टकट सक्षम छ', + 'shortcuts_section_navigation' => 'नेभिगेशन', + 'shortcuts_section_actions' => 'साधारण क्रियाकलापहरू', + 'shortcuts_save' => 'सर्टकटहरू बचत गर्नुहोस्', + 'shortcuts_overlay_desc' => 'नोट: जब सर्टकटहरू सक्षम हुन्छन्, तब "?" थिचेर एक सहायक ओभरले देखाइन्छ जसले स्क्रीनमा हाल देखिएका क्रियाकलापहरूको लागि उपलब्ध सर्टकटहरू हाइलाइट गर्दछ।', + 'shortcuts_update_success' => 'सर्टकट प्राथमिकताहरू अपडेट गरिएका छन्!', + 'shortcuts_overview_desc' => 'प्रणाली प्रयोगकर्ता इन्टरफेसमा नेभिगेट गर्न तपाईंले प्रयोग गर्न सक्ने किबोर्ड सर्टकटहरू व्यवस्थापन गर्नुहोस्।', + + 'notifications' => 'सूचना प्राथमिकताहरू', + 'notifications_desc' => 'प्रणालीमा केही क्रियाकलापहरू गर्दा तपाईंलाई प्राप्त हुने इमेल सूचनाहरू नियन्त्रण गर्नुहोस्।', + 'notifications_opt_own_page_changes' => 'मैले स्वामित्व राख्ने पृष्ठहरूमा परिवर्तन हुँदा सूचित गर्नुहोस्', + 'notifications_opt_own_page_comments' => 'मैले स्वामित्व राख्ने पृष्ठहरूमा टिप्पणी हुँदा सूचित गर्नुहोस्', + 'notifications_opt_comment_replies' => 'मेरो टिप्पणीहरूमा उत्तर आएको बेला सूचित गर्नुहोस्', + 'notifications_save' => 'प्राथमिकताहरू बचत गर्नुहोस्', + 'notifications_update_success' => 'सूचना प्राथमिकताहरू अपडेट गरिएका छन्!', + 'notifications_watched' => 'हेर्ने र बेवास्ता गरिएका सामग्रीहरू', + 'notifications_watched_desc' => 'तल ती सामग्रीहरू छन् जसमा कस्टम वाच प्राथमिकताहरू लागू गरिएका छन्। यीलाई अपडेट गर्नको लागि सामग्री हेरेर, साइडबारमा वाच विकल्पहरू फेला पार्नुहोस्।', + + 'auth' => 'प्रवेश र सुरक्षा', + 'auth_change_password' => 'पासवर्ड परिवर्तन गर्नुहोस्', + 'auth_change_password_desc' => 'तपाईंको एप्लिकेसनमा लगइन गर्न प्रयोग गरिने पासवर्ड परिवर्तन गर्नुहोस्। यो कम्तिमा ८ अक्षर लामो हुनुपर्छ।', + 'auth_change_password_success' => 'पासवर्ड अपडेट गरियो!', + + 'profile' => 'प्रोफाइल विवरण', + 'profile_desc' => 'तपाईंको खाता विवरण व्यवस्थापन गर्नुहोस् जसले तपाईंलाई अन्य प्रयोगकर्ताहरूको लागि प्रतिनिधित्व गर्दछ, साथै सम्पर्क र प्रणाली अनुकूलनका लागि प्रयोग गरिने विवरणहरू।', + 'profile_view_public' => 'सार्वजनिक प्रोफाइल हेर्नुहोस्', + 'profile_name_desc' => 'तपाईंको प्रदर्शन नाम कन्फिगर गर्नुहोस् जुन प्रणालीमा अन्य प्रयोगकर्ताहरूलाई तपाईंको क्रियाकलाप र स्वामित्व भएको सामग्रीमार्फत देखिनेछ।', + 'profile_email_desc' => 'यो इमेल सूचनाहरूको लागि प्रयोग हुनेछ र, सक्रिय प्रणाली प्रमाणिकरणमा निर्भर गर्दै, प्रणाली प्रवेशको लागि पनि प्रयोग हुनेछ।', + 'profile_email_no_permission' => 'दुर्भाग्यवश तपाईंलाई तपाईंको इमेल ठेगाना परिवर्तन गर्ने अनुमति छैन। यदि तपाईं यसलाई परिवर्तन गर्न चाहनुहुन्छ भने, तपाईंलाई एक व्यवस्थापकलाई अनुरोध गर्नु पर्नेछ।', + 'profile_avatar_desc' => 'तपाईंको प्रतिनिधित्व गर्नको लागि एक छवि चयन गर्नुहोस् जुन प्रणालीमा अन्य प्रयोगकर्ताहरूलाई तपाईंको रूपमा देखाउनेछ। यस छविको आकार वर्गाकार र लगभग २५६px चौडाइ र उचाइ भएको हुनु पर्छ।', + 'profile_admin_options' => 'व्यवस्थापक विकल्पहरू', + 'profile_admin_options_desc' => 'अधिकार व्यवस्थापन जस्ता अतिरिक्त व्यवस्थापक-स्तरका विकल्पहरू तपाईंको प्रयोगकर्ता खाता "सेटिंग्स > प्रयोगकर्ताहरू" क्षेत्रमा फेला पार्न सकिन्छ।', + + 'delete_account' => 'खाता मेटाउनुहोस्', + 'delete_my_account' => 'मेरो खाता मेटाउनुहोस्', + 'delete_my_account_desc' => 'यसले तपाईंको प्रयोगकर्ता खाता प्रणालीबाट पूर्ण रूपमा मेटाउनेछ। तपाईं यो खाता पुन: प्राप्त गर्न वा यो क्रियाकलापलाई फर्काउन सक्नुहुन्न। तपाईंले सिर्जना गरेको सामग्री, जस्तै सिर्जना गरिएका पृष्ठहरू र अपलोड गरिएका चित्रहरू, बाँकी रहनेछन्।', + 'delete_my_account_warning' => 'के तपाईं यो खाता मेटाउन निश्चित हुनुहुन्छ?', +]; diff --git a/lang/ne/settings.php b/lang/ne/settings.php new file mode 100644 index 00000000000..eb99f26bdf7 --- /dev/null +++ b/lang/ne/settings.php @@ -0,0 +1,370 @@ + 'सेटिङ्ग', + 'settings_save' => 'सेटिङ्ग सुरक्षित गर्नुहोस्', + 'system_version' => 'सिस्टम संस्करण', + 'categories' => 'क्याटोगोरीहरु', + + // App Settings + 'app_customization' => 'अनुकूलन', + 'app_features_security' => 'फिचरहरू र सुरक्षा', + 'app_name' => 'एप्लिकेसन नाम', + 'app_name_desc' => 'यो नाम हेडरमा र कुनै पनि प्रणालीले पठाएको इमेलमा देखाइनेछ।', + 'app_name_header' => 'हेडरमा नाम देखाउनुहोस्', + 'app_public_access' => 'सार्वजनिक पहुँच', + 'app_public_access_desc' => 'यो विकल्प सक्षम गर्दा, लगइन नगरेका आगन्तुकहरूले तपाईंको BookStack मा सामग्री पहुँच गर्न सक्नेछन्।', + 'app_public_access_desc_guest' => 'सार्वजनिक आगन्तुकहरूको पहुँच "Guest" प्रयोगकर्ताबाट नियन्त्रण गर्न सकिन्छ।', + 'app_public_access_toggle' => 'सार्वजनिक पहुँच अनुमति दिनुहोस्', + 'app_public_viewing' => 'सार्वजनिक हेर्न अनुमति दिनुहोस्?', + 'app_secure_images' => 'उच्च सुरक्षा छवि अपलोडहरू', + 'app_secure_images_toggle' => 'उच्च सुरक्षा छवि अपलोडहरू सक्षम गर्नुहोस्', + 'app_secure_images_desc' => 'प्रदर्शन कारणहरूका लागि, सबै छविहरू सार्वजनिक हुन्छन्। यो विकल्पले छवि URL अगाडि एउटा अनियमित, अनुमान गर्न गाह्रो स्ट्रिङ थप्छ। सजिलो पहुँच रोक्न निर्देशिका सूचीकरण निष्क्रिय गर्नुहोस्।', + 'app_default_editor' => 'डिफल्ट पृष्ठ सम्पादक', + 'app_default_editor_desc' => 'नयाँ पृष्ठ सम्पादन गर्दा डिफल्ट रूपमा प्रयोग हुने सम्पादक चयन गर्नुहोस्। अनुमति अनुसार पृष्ठ स्तरमा यो परिवर्तन गर्न सकिन्छ।', + 'app_custom_html' => 'कस्टम HTML हेड सामग्री', + 'app_custom_html_desc' => 'यहाँ थपिएको कुनै पनि सामग्री प्रत्येक पृष्ठको सेक्सनको तल्लो भागमा समावेश हुनेछ। स्टाइल ओभरराइड वा एनालिटिक्स कोड थप्न उपयोगी।', + 'app_custom_html_disabled_notice' => 'कस्टम HTML हेड सामग्री यस सेटिङ पृष्ठमा असक्षम गरिएको छ ताकि कुनै समस्या भएमा फर्काउन सकियोस्।', + 'app_logo' => 'एप्लिकेसन लोगो', + 'app_logo_desc' => 'यो एप्लिकेसन हेडर बार लगायत अन्य ठाउँहरूमा प्रयोग हुन्छ। यो छवि ८६px उचाइको हुनु पर्नेछ। ठूलो छविहरू सानो गरिनेछ।', + 'app_icon' => 'एप्लिकेसन आइकन', + 'app_icon_desc' => 'यो आइकन ब्राउजर ट्याब र छोटोमार्ग आइकनहरूका लागि प्रयोग हुन्छ। PNG २५६px वर्गाकार छवि हुनुपर्छ।', + 'app_homepage' => 'एप्लिकेसन होमपेज', + 'app_homepage_desc' => 'डिफल्ट दृश्यको सट्टा होमपेजमा देखाउनको लागि कुनै दृश्य चयन गर्नुहोस्। चयन गरिएका पृष्ठहरूको अनुमति बेवास्ता गरिनेछ।', + 'app_homepage_select' => 'पृष्ठ चयन गर्नुहोस्', + 'app_footer_links' => 'फुटर लिंकहरू', + 'app_footer_links_desc' => 'साइटको फुटरमा देखाउन लिंकहरू थप्नुहोस्। यी प्रायः पृष्ठहरूको तल्लो भागमा देखिनेछन्, जसमा लगइन आवश्यक नभएका पृष्ठहरू पनि समावेश छन्। "trans::" ले प्रणाली-परिभाषित अनुवाद प्रयोग गर्न सकिन्छ। उदाहरण: "trans::common.privacy_policy" ले "गोपनीयता नीति" र "trans::common.terms_of_service" ले "सेवा सर्तहरू" देखाउनेछ।', + 'app_footer_links_label' => 'लिंक लेबल', + 'app_footer_links_url' => 'लिंक URL', + 'app_footer_links_add' => 'फुटर लिंक थप्नुहोस्', + 'app_disable_comments' => 'टिप्पणीहरू असक्षम पार्नुहोस्', + 'app_disable_comments_toggle' => 'टिप्पणीहरू असक्षम पार्नुहोस्', + 'app_disable_comments_desc' => 'एप्लिकेसनका सबै पृष्ठहरूमा टिप्पणीहरू असक्षम पार्दछ।
अस्तित्वमा रहेका टिप्पणीहरू देखाइने छैनन्।', + + // Color settings + 'color_scheme' => 'एप्लिकेसन रंग योजना', + 'color_scheme_desc' => 'एप्लिकेसनको प्रयोगकर्ता इन्टरफेसमा प्रयोग हुने रंगहरू सेट गर्नुहोस्। रंगहरू डार्क र लाइट मोडका लागि अलग्गै सेट गर्न सकिन्छ जसले विषयवस्तु र पठनीयता सुधार गर्छ।', + 'ui_colors_desc' => 'एप्लिकेसनको मुख्य रंग र डिफल्ट लिंक रंग सेट गर्नुहोस्। मुख्य रंग मुख्य रूपमा हेडर ब्यानर, बटनहरू र इन्टरफेस सजावटमा प्रयोग हुन्छ। डिफल्ट लिंक रंग लेखिएको सामग्री र इन्टरफेस दुवैमा प्रयोग हुन्छ।', + 'app_color' => 'मुख्य रंग', + 'link_color' => 'डिफल्ट लिंक रंग', + 'content_colors_desc' => 'पृष्ठ संगठन संरचनाका सबै तत्वहरूका लागि रंग सेट गर्नुहोस्। पठनीयताको लागि डिफल्ट रंगहरूसँग मिल्दोजुल्दो चमक छनौट गर्न सुझाव दिइन्छ।', + 'bookshelf_color' => 'शेल्फ रंग', + 'book_color' => 'पुस्तक रंग', + 'chapter_color' => 'अध्याय रंग', + 'page_color' => 'पृष्ठ रंग', + 'page_draft_color' => 'पृष्ठ मसौदा रंग', + + // Registration Settings + 'reg_settings' => 'दर्ता', + 'reg_enable' => 'दर्ता सक्षम गर्नुहोस्', + 'reg_enable_toggle' => 'दर्ता सक्षम गर्नुहोस्', + 'reg_enable_desc' => 'दर्ता सक्षम हुँदा प्रयोगकर्ताले आफैंलाई एप्लिकेसन प्रयोगकर्ताको रूपमा दर्ता गर्न सक्नेछन्। दर्ता हुँदा तिनीहरूलाई डिफल्ट प्रयोगकर्ता भूमिका दिइन्छ।', + 'reg_default_role' => 'दर्ता पछि डिफल्ट प्रयोगकर्ता भूमिका', + 'reg_enable_external_warning' => 'बाह्य LDAP वा SAML प्रमाणीकरण सक्रिय हुँदा माथि उल्लेखित विकल्प बेवास्ता गरिनेछ। प्रमाणीकरण सफल भएमा गैर-अस्तित्व प्रयोगकर्ताका खाताहरू स्वचालित सिर्जना हुनेछ।', + 'reg_email_confirmation' => 'इमेल पुष्टि', + 'reg_email_confirmation_toggle' => 'इमेल पुष्टि आवश्यक छ', + 'reg_confirm_email_desc' => 'यदि डोमेन प्रतिबन्ध प्रयोग गरिएको छ भने इमेल पुष्टि आवश्यक हुनेछ र यो विकल्प बेवास्ता गरिनेछ।', + 'reg_confirm_restrict_domain' => 'डोमेन प्रतिबन्ध', + 'reg_confirm_restrict_domain_desc' => 'दर्ता सीमित गर्न चाहनु भएको इमेल डोमेन्सलाई अल्पविरामले छुट्याएर प्रविष्ट गर्नुहोस्। प्रयोगकर्ताहरूलाई ठेगाना पुष्टि गर्न इमेल पठाइनेछ।
दर्ता सफल भएपछि प्रयोगकर्ताले इमेल ठेगाना परिवर्तन गर्न सक्नेछन्।', + 'reg_confirm_restrict_domain_placeholder' => 'कुनै प्रतिबन्ध छैन', + + // Sorting Settings + 'sorting' => 'क्रमबद्धता', + 'sorting_book_default' => 'डिफल्ट पुस्तक क्रम', + 'sorting_book_default_desc' => 'नयाँ पुस्तकहरूमा लागु गर्न डिफल्ट क्रम नियम चयन गर्नुहोस्। यो अस्तित्वमा रहेका पुस्तकहरूमा असर पार्दैन र पुस्तक अनुसार ओभरराइड गर्न सकिन्छ।', + 'sorting_rules' => 'क्रम नियमहरू', + 'sorting_rules_desc' => 'यी पूर्वनिर्धारित क्रम सञ्चालनहरू हुन् जुन प्रणालीमा सामग्रीमा लागू गर्न सकिन्छ।', + 'sort_rule_assigned_to_x_books' => ':count पुस्तकमा लागू गरिएको|:count पुस्तकहरूमा लागू गरिएको', + 'sort_rule_create' => 'क्रम नियम सिर्जना गर्नुहोस्', + 'sort_rule_edit' => 'क्रम नियम सम्पादन गर्नुहोस्', + 'sort_rule_delete' => 'क्रम नियम मेटाउनुहोस्', + 'sort_rule_delete_desc' => 'यस क्रम नियमलाई प्रणालीबाट हटाउनुहोस्। यस नियम प्रयोग गरिएका पुस्तकहरू म्यानुअल क्रमबद्धतामा फर्कनेछन्।', + 'sort_rule_delete_warn_books' => 'यो क्रम नियम हाल :count पुस्तक(हरू) मा प्रयोग भैरहेको छ। के तपाईं पक्का यो मेटाउन चाहनुहुन्छ?', + 'sort_rule_delete_warn_default' => 'यो क्रम नियम हाल पुस्तकहरूको डिफल्ट रूपमा प्रयोग भैरहेको छ। के तपाईं पक्का यो मेटाउन चाहनुहुन्छ?', + 'sort_rule_details' => 'क्रम नियम विवरण', + 'sort_rule_details_desc' => 'यस क्रम नियमको नाम सेट गर्नुहोस्, जुन प्रयोगकर्ताहरूले क्रम छनौट गर्दा सूचिमा देखिनेछ।', + 'sort_rule_operations' => 'क्रम सञ्चालनहरू', + 'sort_rule_operations_desc' => 'उपलब्ध सञ्चालनहरूको सूचीबाट क्रम क्रियाकलापहरू सेट गर्नुहोस्। प्रयोग गर्दा, माथिबाट तल सम्म क्रमसँगै लागू गरिनेछ। यहाँ गरिएको कुनै पनि परिवर्तन सुरक्षित गर्दा सबै लागू पुस्तकहरूमा लागु हुनेछ।', + 'sort_rule_available_operations' => 'उपलब्ध सञ्चालनहरू', + 'sort_rule_available_operations_empty' => 'कोही सञ्चालन बाँकी छैनन्', + 'sort_rule_configured_operations' => 'कन्फिगर गरिएको सञ्चालनहरू', + 'sort_rule_configured_operations_empty' => '"उपलब्ध सञ्चालनहरू" सूचीबाट सञ्चालनहरू तान्नुहोस्/थप्नुहोस्', + 'sort_rule_op_asc' => '(Ascending)', + 'sort_rule_op_desc' => '(Descending)', + 'sort_rule_op_name' => 'नाम - वर्णानुक्रम', + 'sort_rule_op_name_numeric' => 'नाम - सङ्ख्यात्मक', + 'sort_rule_op_created_date' => 'सिर्जना मिति', + 'sort_rule_op_updated_date' => 'अपडेट मिति', + 'sort_rule_op_chapters_first' => 'पहिले अध्यायहरू', + 'sort_rule_op_chapters_last' => 'अन्त्यमा अध्यायहरू', + + // Maintenance settings + 'maint' => 'सम्भार', + 'maint_image_cleanup' => 'छविहरू सफा गर्नुहोस्', + 'maint_image_cleanup_desc' => 'पृष्ठ र संस्करण सामग्री स्क्यान गरी कुन छविहरू र चित्रहरू प्रयोगमा छन् र कुनहरू अनावश्यक छन् जाँच गर्दछ। यो सञ्चालन अघि पूर्ण डाटाबेस र छवि ब्याकअप बनाउनुहोस्।', + 'maint_delete_images_only_in_revisions' => 'पुराना पृष्ठ संस्करणहरूमा मात्र रहेका छविहरू पनि मेटाउनुहोस्', + 'maint_image_cleanup_run' => 'सफा गर्ने प्रक्रिया सुरु गर्नुहोस्', + 'maint_image_cleanup_warning' => ':count सम्भावित अप्रयुक्त छविहरू फेला परे। के तपाईं पक्का यी छविहरू मेटाउन चाहनुहुन्छ?', + 'maint_image_cleanup_success' => ':count सम्भावित अप्रयुक्त छविहरू फेला परे र मेटाइयो!', + 'maint_image_cleanup_nothing_found' => 'कुनै अप्रयुक्त छवि फेला परेन, केही मेटाइएन!', + 'maint_send_test_email' => 'परीक्षण इमेल पठाउनुहोस्', + 'maint_send_test_email_desc' => 'यो तपाईको प्रोफाइलमा दिइएको इमेल ठेगानामा परीक्षण इमेल पठाउँछ।', + 'maint_send_test_email_run' => 'परीक्षण इमेल पठाउनुहोस्', + 'maint_send_test_email_success' => 'इमेल पठाइयो :address', + 'maint_send_test_email_mail_subject' => 'परीक्षण इमेल', + 'maint_send_test_email_mail_greeting' => 'इमेल वितरण सफल देखिन्छ!', + 'maint_send_test_email_mail_text' => 'बधाई छ! तपाईंले यो इमेल प्राप्त गर्नुभएकोले तपाईका इमेल सेटिङहरू ठीकसँग कन्फिगर भएका छन्।', + 'maint_recycle_bin_desc' => 'मेटाइएका शेल्फ, पुस्तक, अध्याय र पृष्ठहरू रीसायकल बिनमा पठाइन्छ जसबाट पुनर्स्थापना वा स्थायी मेटाई गर्न सकिन्छ। पुराना वस्तुहरू प्रणाली कन्फिगरेसन अनुसार स्वचालित रूपमा हटाउन सकिन्छ।', + 'maint_recycle_bin_open' => 'रीसायकल बिन खोल्नुहोस्', + 'maint_regen_references' => 'सन्दर्भहरू पुनः उत्पन्न गर्नुहोस्', + 'maint_regen_references_desc' => 'यो क्रियाले डाटाबेस भित्र वस्तुहरू बीचको सन्दर्भ सूचकांक पुनः बनाउँछ। सामान्यतया यो स्वतः हुन्छ, तर पुराना वा अनअधिकारिक विधिबाट थपिएको सामग्रीलाई सूचीकृत गर्न उपयोगी हुन्छ।', + 'maint_regen_references_success' => 'सन्दर्भ सूचकांक पुनः उत्पन्न गरियो!', + 'maint_timeout_command_note' => 'सूचना: यो क्रियामा समय लाग्न सक्छ जसले केही वेब वातावरणहरूमा टाइमआउट समस्या ल्याउन सक्छ। विकल्पको रूपमा टर्मिनल कमाण्ड प्रयोग गरेर गर्न सकिन्छ।', + + // Recycle Bin + 'recycle_bin' => 'रीसायकल बिन', + 'recycle_bin_desc' => 'यहाँ तपाईंले मेटाइएका वस्तुहरू पुनर्स्थापना गर्न वा प्रणालीबाट स्थायी रूपमा हटाउन सक्नुहुन्छ। यो सूची प्रणालीका अन्य गतिविधि सूचिहरू जस्तो फिल्टर नभएको छ।', + 'recycle_bin_deleted_item' => 'मेटाइएको वस्तु', + 'recycle_bin_deleted_parent' => 'मूल', + 'recycle_bin_deleted_by' => 'मेटाउने व्यक्ति', + 'recycle_bin_deleted_at' => 'मेटाइने समय', + 'recycle_bin_permanently_delete' => 'स्थायी रूपमा मेटाउनुहोस्', + 'recycle_bin_restore' => 'पुन: भण्डारण गर्नुहोस्', + 'recycle_bin_contents_empty' => 'रिसायकल बिन हाल खाली छ', + 'recycle_bin_empty' => 'रिसायकल बिन खाली गर्नुहोस्', + 'recycle_bin_empty_confirm' => 'यसले रिसायकल बिनभित्रका सबै वस्तुहरू र तिनीहरूको सामग्री स्थायी रूपमा मेटाउनेछ। के तपाईं पक्का खाली गर्न चाहनुहुन्छ?', + 'recycle_bin_destroy_confirm' => 'यस क्रियाले यो वस्तु र तल सूचीबद्ध कुनै पनि सन्तान तत्वहरूलाई स्थायी रूपमा प्रणालीबाट मेटाउनेछ र तपाईंले पुनः प्राप्त गर्न सक्नुहुने छैन। के तपाईं पक्का स्थायी रूपमा मेटाउन चाहनुहुन्छ?', + 'recycle_bin_destroy_list' => 'मेटाइने वस्तुहरू', + 'recycle_bin_restore_list' => 'पुनर्स्थापना गरिने वस्तुहरू', + 'recycle_bin_restore_confirm' => 'यो क्रियाले मेटाइएको वस्तु र कुनै पनि सन्तान तत्वहरूलाई मूल स्थानमा पुनर्स्थापना गर्नेछ। यदि मूल स्थान पनि मेटाइएको छ र रिसायकल बिनमा छ भने मूल वस्तुलाई पनि पुनर्स्थापना गर्नुपर्नेछ।', + 'recycle_bin_restore_deleted_parent' => 'यस वस्तुको मूल पनि मेटाइएको छ। मूल वस्तु पुनर्स्थापित नभएसम्म यो वस्तु मेटिएको नै रहनेछ।', + 'recycle_bin_restore_parent' => 'मूल पुनर्स्थापना गर्नुहोस्', + 'recycle_bin_destroy_notification' => 'रिसायकल बिनबाट कुल :count वस्तुहरू मेटाइयो।', + 'recycle_bin_restore_notification' => 'रिसायकल बिनबाट कुल :count वस्तुहरू पुनर्स्थापित गरियो।', + + // Audit Log + 'audit' => 'अडिट लग', + 'audit_desc' => 'यो अडिट लग प्रणालीमा ट्र्याक गरिएका गतिविधिहरूको सूची देखाउँछ। यो सूची प्रणालीका समान गतिविधि सूचीहरू भन्दा फरक फिल्टररहित हुन्छ।', + 'audit_event_filter' => 'घटना फिल्टर', + 'audit_event_filter_no_filter' => 'फिल्टर छैन', + 'audit_deleted_item' => 'मेटाइएको वस्तु', + 'audit_deleted_item_name' => 'नाम: :name', + 'audit_table_user' => 'प्रयोगकर्ता', + 'audit_table_event' => 'घटना', + 'audit_table_related' => 'सम्बन्धित वस्तु वा विवरण', + 'audit_table_ip' => 'IP ठेगाना', + 'audit_table_date' => 'गतिविधि मिति', + 'audit_date_from' => 'मिति दायरा सुरु', + 'audit_date_to' => 'मिति दायरा अन्त्य', + + // Role Settings + 'roles' => 'भूमिकाहरू', + 'role_user_roles' => 'प्रयोगकर्ता भूमिका', + 'roles_index_desc' => 'भूमिकाहरू प्रयोगकर्ताहरूलाई समूहमा राख्न र उनीहरूको सदस्यलाई प्रणाली अनुमति दिन प्रयोग हुन्छ। यदि कुनै प्रयोगकर्ता धेरै भूमिका मा छ भने तिनका अधिकारहरू जोडिनेछन् र सबै क्षमता प्राप्त हुनेछन्।', + 'roles_x_users_assigned' => ':count प्रयोगकर्तालाई भूमिका दिइयो|:count प्रयोगकर्ताहरूलाई भूमिका दिइयो', + 'roles_x_permissions_provided' => ':count अनुमति दिइयो|:count अनुमति दिइयो', + 'roles_assigned_users' => 'दिइएका प्रयोगकर्ताहरू', + 'roles_permissions_provided' => 'दिइएका अनुमति', + 'role_create' => 'नयाँ भूमिका सिर्जना गर्नुहोस्', + 'role_delete' => 'भूमिका मेटाउनुहोस्', + 'role_delete_confirm' => 'यसले \':roleName\' नामको भूमिका मेटाउनेछ।', + 'role_delete_users_assigned' => 'यस भूमिकामा :userCount प्रयोगकर्ता छन्। यदि तपाईंले यी प्रयोगकर्ताहरूलाई अर्को भूमिकामा सार्न चाहनुहुन्छ भने तल नयाँ भूमिका चयन गर्नुहोस्।', + 'role_delete_no_migration' => "प्रयोगकर्ताहरू सार्नु हुँदैन", + 'role_delete_sure' => 'के तपाईं पक्का यो भूमिका मेटाउन चाहनुहुन्छ?', + 'role_edit' => 'भूमिका सम्पादन गर्नुहोस्', + 'role_details' => 'भूमिका विवरण', + 'role_name' => 'भूमिका नाम', + 'role_desc' => 'भूमिकाको संक्षिप्त विवरण', + 'role_mfa_enforced' => 'बहु-फ्याक्टर प्रमाणीकरण आवश्यक', + 'role_external_auth_id' => 'बाह्य प्रमाणीकरण ID हरू', + 'role_system' => 'प्रणाली अनुमति', + 'role_manage_users' => 'प्रयोगकर्ताहरू व्यवस्थापन गर्नुहोस्', + 'role_manage_roles' => 'भूमिका र अनुमति व्यवस्थापन गर्नुहोस्', + 'role_manage_entity_permissions' => 'सबै पुस्तक, अध्याय र पृष्ठ अनुमति व्यवस्थापन गर्नुहोस्', + 'role_manage_own_entity_permissions' => 'आफ्नो पुस्तक, अध्याय र पृष्ठ अनुमति व्यवस्थापन गर्नुहोस्', + 'role_manage_page_templates' => 'पृष्ठ टेम्प्लेट व्यवस्थापन गर्नुहोस्', + 'role_access_api' => 'प्रणाली API पहुँच', + 'role_manage_settings' => 'एप सेटिङ व्यवस्थापन गर्नुहोस्', + 'role_export_content' => 'सामग्री निर्यात गर्नुहोस्', + 'role_import_content' => 'सामग्री आयात गर्नुहोस्', + 'role_editor_change' => 'पृष्ठ सम्पादक परिवर्तन गर्नुहोस्', + 'role_notifications' => 'सूचनाहरू प्राप्त र व्यवस्थापन गर्नुहोस्', + 'role_asset' => 'संपत्ति अनुमति', + 'roles_system_warning' => 'माथिका कुनै पनि तीन अनुमति प्रयोगकर्ताले आफैं वा अरूका अधिकार परिवर्तन गर्न सक्छन्। यी अनुमति भएको भूमिका मात्र भरपर्दो प्रयोगकर्तालाई दिनुहोस्।', + 'role_asset_desc' => 'यी अनुमतिले प्रणालीभित्र सम्पत्तिमा डिफल्ट पहुँच नियन्त्रण गर्छ। पुस्तक, अध्याय र पृष्ठमा अनुमति यी भन्दा प्राथमिक हुन्छ।', + 'role_asset_admins' => 'प्रशासनकर्ताहरूलाई सबै सामग्रीमा स्वतः पहुँच दिइन्छ, यी विकल्पहरूले UI मा देखिने वा लुकेका विकल्पहरू मात्र प्रभाव पार्न सक्छ।', + 'role_asset_image_view_note' => 'यो छवि व्यवस्थापक भित्रको दृश्यता सम्बन्धि हो। अपलोड गरिएको छविमा वास्तविक पहुँच प्रणालीको छवि भण्डारण विकल्प अनुसार हुन्छ।', + 'role_all' => 'सबै', + 'role_own' => 'आफ्नो', + 'role_controlled_by_asset' => 'अपलोड गरिएको सम्पत्तिले नियन्त्रण गरेको', + 'role_save' => 'भूमिका सुरक्षित गर्नुहोस्', + 'role_users' => 'यस भूमिकाका प्रयोगकर्ताहरू', + 'role_users_none' => 'यो भूमिकामा हाल कुनै प्रयोगकर्ता छैन', + + // Users + 'users' => 'प्रयोगकर्ताहरू', + 'users_index_desc' => 'प्रणालीमा व्यक्तिगत प्रयोगकर्ता खाता सिर्जना र व्यवस्थापन गर्नुहोस्। प्रयोगकर्ता खाता लगइन र सामग्री तथा गतिविधि जिम्मेवारीका लागि प्रयोग हुन्छ। पहुँच अनुमतिहरू मुख्यतया भूमिकामा आधारित छन् तर प्रयोगकर्ताको सामग्री स्वामित्वले पनि असर गर्न सक्छ।', + 'user_profile' => 'प्रयोगकर्ता प्रोफाइल', + 'users_add_new' => 'नयाँ प्रयोगकर्ता थप्नुहोस्', + 'users_search' => 'प्रयोगकर्ताहरू खोज्नुहोस्', + 'users_latest_activity' => 'हालैको गतिविधि', + 'users_details' => 'प्रयोगकर्ता विवरण', + 'users_details_desc' => 'यस प्रयोगकर्ताको प्रदर्शन नाम र इमेल ठेगाना सेट गर्नुहोस्। इमेल ठेगाना लगइनका लागि प्रयोग हुनेछ।', + 'users_details_desc_no_email' => 'यो प्रयोगकर्तालाई अरूले चिन्नेगरी प्रदर्शन नाम सेट गर्नुहोस्।', + 'users_role' => 'प्रयोगकर्ता भूमिका', + 'users_role_desc' => 'यो प्रयोगकर्तालाई दिइने भूमिका चयन गर्नुहोस्। प्रयोगकर्ताले धेरै भूमिका पाएमा सबै भूमिका अधिकारहरू जोडिनेछन्।', + 'users_password' => 'प्रयोगकर्ता पासवर्ड', + 'users_password_desc' => 'लगइनका लागि कम्तिमा ८ वर्ण लामो पासवर्ड सेट गर्नुहोस्।', + 'users_send_invite_text' => 'तपाईं यो प्रयोगकर्तालाई निमन्त्रणा इमेल पठाउन सक्नुहुन्छ जसले उनीहरूलाई आफ्नै पासवर्ड सेट गर्न अनुमति दिन्छ, नभए तपाईंले आफैं पासवर्ड सेट गर्न सक्नुहुन्छ।', + 'users_send_invite_option' => 'प्रयोगकर्तालाई निमन्त्रणा इमेल पठाउनुहोस्', + 'users_external_auth_id' => 'बाह्य प्रमाणीकरण ID', + 'users_external_auth_id_desc' => 'जब बाह्य प्रमाणीकरण प्रणाली प्रयोग हुन्छ (जस्तै SAML2, OIDC, LDAP), यो ID ले यो BookStack प्रयोगकर्तालाई सम्बन्धित प्रणाली खातासँग जोड्छ। सामान्य इमेल प्रमाणीकरणमा यो फिल्ड आवश्यक छैन।', + 'users_password_warning' => 'यो प्रयोगकर्ताको पासवर्ड परिवर्तन गर्न मात्र तल भर्नुहोस्।', + 'users_system_public' => 'यो प्रयोगकर्ता कुनै पनि पाहुना प्रयोगकर्तालाई प्रतिनिधित्व गर्दछ। यसले लगइन गर्न सक्दैन तर स्वतः दिइन्छ।', + 'users_delete' => 'प्रयोगकर्ता मेटाउनुहोस्', + 'users_delete_named' => ':userName प्रयोगकर्ता मेटाउनुहोस्', + 'users_delete_warning' => 'यसले \':userName\' नामको प्रयोगकर्तालाई प्रणालीबाट पूर्ण रूपमा मेटाउनेछ।', + 'users_delete_confirm' => 'के तपाईं पक्का यो प्रयोगकर्ता मेटाउन चाहनुहुन्छ?', + 'users_migrate_ownership' => 'स्वामित्व सार्नुहोस्', + 'users_migrate_ownership_desc' => 'यहाँ अर्को प्रयोगकर्ता चयन गर्नुहोस् जसले यस प्रयोगकर्ताका सबै वस्तुहरूको स्वामित्व पाओस्।', + 'users_none_selected' => 'कुनै प्रयोगकर्ता चयन गरिएको छैन', + 'users_edit' => 'प्रयोगकर्ता सम्पादन गर्नुहोस्', + 'users_edit_profile' => 'प्रोफाइल सम्पादन गर्नुहोस्', + 'users_avatar' => 'प्रयोगकर्ता अवतार', + 'users_avatar_desc' => 'यो प्रयोगकर्तालाई प्रतिनिधित्व गर्न एउटा चित्र चयन गर्नुहोस्। करिब २५६px वर्गाकार हुनु पर्छ।', + 'users_preferred_language' => 'रुचाइको भाषा', + 'users_preferred_language_desc' => 'यस विकल्पले एपको यूजर-इन्टरफेसको भाषा परिवर्तन गर्नेछ। प्रयोगकर्ताले सिर्जना गरेको सामग्रीमा असर पार्दैन।', + 'users_social_accounts' => 'सामाजिक खाता', + 'users_social_accounts_desc' => 'यो प्रयोगकर्ताका जडित सामाजिक खाताहरूको स्थिति हेर्नुहोस्। सामाजिक खाताहरू प्रमाणीकरणका लागि प्राथमिक प्रणालीसँगै प्रयोग गर्न सकिन्छ।', + 'users_social_accounts_info' => 'यहाँ तपाईं आफ्नो अन्य खाताहरू छिटो र सजिलो लगइनका लागि जोड्न सक्नुहुन्छ। यहाँबाट खाता डिस्कनेक्ट गर्दा पूर्व अनुमति रद्द हुँदैन। अनुमति रद्द गर्न सामाजिक खाताको सेटिङ प्रयोग गर्नुहोस्।', + 'users_social_connect' => 'खाता जडान गर्नुहोस्', + 'users_social_disconnect' => 'खाता डिस्कनेक्ट गर्नुहोस्', + 'users_social_status_connected' => 'जडान गरिएको', + 'users_social_status_disconnected' => 'डिस्कनेक्ट गरिएको', + 'users_social_connected' => ':socialAccount खाता सफलतापूर्वक प्रोफाइलमा जोडियो।', + 'users_social_disconnected' => ':socialAccount खाता सफलतापूर्वक प्रोफाइलबाट हटाइयो।', + 'users_api_tokens' => 'API टोकनहरू', + 'users_api_tokens_desc' => 'BookStack REST API सँग प्रमाणीकरण गर्न प्रयोग गरिने पहुँच टोकनहरू सिर्जना र व्यवस्थापन गर्नुहोस्। API अनुमतिहरू टोकनधारक प्रयोगकर्ताबाट व्यवस्थापन हुन्छ।', + 'users_api_tokens_none' => 'यस प्रयोगकर्ताका लागि कुनै API टोकन सिर्जना गरिएको छैन', + 'users_api_tokens_create' => 'टोकन सिर्जना गर्नुहोस्', + 'users_api_tokens_expires' => 'म्याद समाप्त', + 'users_api_tokens_docs' => 'API कागजातहरू', + 'users_mfa' => 'बहु-फ्याक्टर प्रमाणीकरण', + 'users_mfa_desc' => 'तपाईंको प्रयोगकर्ता खाताको लागि थप सुरक्षा तहको रूपमा बहु-फ्याक्टर प्रमाणीकरण सेटअप गर्नुहोस्।', + 'users_mfa_x_methods' => ':count विधि सेटअप गरिएको|:count विधिहरू सेटअप गरिएको', + 'users_mfa_configure' => 'विधिहरू सेटअप गर्नुहोस्', + + // API Tokens + 'user_api_token_create' => 'API टोकन सिर्जना गर्नुहोस्', + 'user_api_token_name' => 'नाम', + 'user_api_token_name_desc' => 'यो टोकनको उद्देश्य सम्झनको लागि भविष्यमा सम्झन सकिने नाम दिनुहोस्।', + 'user_api_token_expiry' => 'म्याद समाप्ति मिति', + 'user_api_token_expiry_desc' => 'यो टोकनको म्याद समाप्त हुने मिति सेट गर्नुहोस्। यस मितिपछि, यस टोकनको प्रयोग गरेर गरिएका अनुरोधहरू काम गर्दैनन्। यो फिल्ड खाली छोड्दा भविष्यमा १०० वर्षको म्याद सेट हुनेछ।', + 'user_api_token_create_secret_message' => 'यो टोकन सिर्जना गरेपछि "Token ID" र "Token Secret" जनरेट र प्रदर्शन गरिनेछ। यो गोप्य जानकारी एक पटक मात्र देखाइनेछ, त्यसैले कृपया यसलाई सुरक्षित स्थानमा प्रतिलिपि गर्नुहोस् र त्यसपछि मात्र अगाडि बढ्नुहोस्।', + 'user_api_token' => 'API टोकन', + 'user_api_token_id' => 'टोकन ID', + 'user_api_token_id_desc' => 'यो टोकनको लागि प्रणालीद्वारा उत्पन्न गरिएको अ-सम्पादनयोग्य पहिचान हो, जुन API अनुरोधहरूमा प्रदान गर्न आवश्यक हुनेछ।', + 'user_api_token_secret' => 'टोकन गोप्य जानकारी', + 'user_api_token_secret_desc' => 'यो टोकनको लागि प्रणालीद्वारा उत्पन्न गरिएको गोप्य जानकारी हो, जुन API अनुरोधहरूमा प्रदान गर्न आवश्यक हुनेछ। यसलाई केवल एक पटक मात्र देखाइनेछ, त्यसैले कृपया यसलाई सुरक्षित स्थानमा प्रतिलिपि गर्नुहोस्।', + 'user_api_token_created' => 'टोकन सिर्जना भएको :timeAgo', + 'user_api_token_updated' => 'टोकन अपडेट भएको :timeAgo', + 'user_api_token_delete' => 'टोकन मेटाउनुहोस्', + 'user_api_token_delete_warning' => 'यसले \':tokenName\' नामको API टोकनलाई पूर्ण रूपमा प्रणालीबाट मेटाउनेछ।', + 'user_api_token_delete_confirm' => 'के तपाईं पक्का यो API टोकन मेटाउन चाहनुहुन्छ?', + + // Webhooks + 'webhooks' => 'वेबहुक्स', + 'webhooks_index_desc' => 'वेबहुक्स भनेको प्रणाली भित्रका केही क्रियाकलाप र घटनाहरू हुँदा बाह्य URL हरूमा डेटा पठाउने विधि हो, जसले बाह्य प्लेटफर्महरूसँग जस्तै सन्देश वा सूचनासम्बन्धी सिस्टमहरूसँग घटनामा आधारित एकीकरणलाई अनुमति दिन्छ।', + 'webhooks_x_trigger_events' => ':count ट्रिगर घटना|:count ट्रिगर घटनाहरू', + 'webhooks_create' => 'नयाँ वेबहुक सिर्जना गर्नुहोस्', + 'webhooks_none_created' => 'अझै कुनै वेबहुक सिर्जना गरिएको छैन।', + 'webhooks_edit' => 'वेबहुक सम्पादन गर्नुहोस्', + 'webhooks_save' => 'वेबहुक बचत गर्नुहोस्', + 'webhooks_details' => 'वेबहुक विवरण', + 'webhooks_details_desc' => 'एक प्रयोगकर्ता मैत्री नाम र एक POST इन्डप्वाइंट दिनुहोस् जसलाई वेबहुकको डेटा पठाइने स्थानको रूपमा प्रयोग हुनेछ।', + 'webhooks_events' => 'वेबहुक घटनाहरू', + 'webhooks_events_desc' => 'यी घटनाहरू चयन गर्नुहोस् जसले यो वेबहुकलाई ट्रिगर गर्नुपर्नेछ।', + 'webhooks_events_warning' => 'ध्यान दिनुहोस् कि यी घटनाहरू चयन गरेपछि सबै चयन गरिएका घटनाहरूको लागि वेबहुक ट्रिगर हुनेछ, भले नै कस्टम अनुमतिहरू लागू गरिएका छन्। यो वेबहुक प्रयोग गर्दा गोपनीय सामग्रीको जोखिम नहोस् भन्ने कुरा सुनिश्चित गर्नुहोस्।', + 'webhooks_events_all' => 'सिस्टमका सबै घटनाहरू', + 'webhooks_name' => 'वेबहुक नाम', + 'webhooks_timeout' => 'वेबहुक अनुरोध म्याद समाप्ति (सेकेन्ड)', + 'webhooks_endpoint' => 'वेबहुक इन्डप्वाइंट', + 'webhooks_active' => 'वेबहुक सक्रिय', + 'webhook_events_table_header' => 'घटनाहरू', + 'webhooks_delete' => 'वेबहुक मेटाउनुहोस्', + 'webhooks_delete_warning' => 'यसले \':webhookName\' नामको वेबहुकलाई प्रणालीबाट पूर्ण रूपमा मेटाउनेछ।', + 'webhooks_delete_confirm' => 'के तपाईं पक्का यो वेबहुक मेटाउन चाहनुहुन्छ?', + 'webhooks_format_example' => 'वेबहुक ढाँचाको उदाहरण', + 'webhooks_format_example_desc' => 'वेबहुक डेटा POST अनुरोधको रूपमा JSON ढाँचामा निर्धारित इन्डप्वाइंटमा पठाइन्छ। "related_item" र "url" गुणहरू वैकल्पिक छन् र यो ट्रिगर गरिएको घटनाको प्रकारमा निर्भर गर्नेछ।', + 'webhooks_status' => 'वेबहुक स्थिति', + 'webhooks_last_called' => 'अन्तिम पटक कल गरिएको: ', + 'webhooks_last_errored' => 'अन्तिम पटक एरर भएको: ', + 'webhooks_last_error_message' => 'अन्तिम एरर सन्देश: ', + + // Licensing + 'licenses' => 'लाइसन्स', + 'licenses_desc' => 'यस पृष्ठमा BookStack को लाइसेन्स जानकारी र BookStack भित्र प्रयोग भएका परियोजना र पुस्तकालयहरूको जानकारी दिइएको छ। सूचीबद्ध भएका धेरै परियोजनाहरूले केवल विकासको सन्दर्भमा मात्र प्रयोग गर्न सकिन्छ।', + 'licenses_bookstack' => 'BookStack लाइसेन्स', + 'licenses_php' => 'PHP पुस्तकालय लाइसेन्स', + 'licenses_js' => 'JavaScript पुस्तकालय लाइसेन्स', + 'licenses_other' => 'अन्य लाइसेन्स', + 'license_details' => 'लाइसेन्स विवरण', + + //! If editing translations files directly please ignore this in all + //! languages apart from en. Content will be auto-copied from en. + //!//////////////////////////////// + 'language_select' => [ + 'en' => 'English', + 'ar' => 'العربية', + 'bg' => 'Bǎlgarski', + 'bs' => 'Bosanski', + 'ca' => 'Català', + 'cs' => 'Česky', + 'cy' => 'Cymraeg', + 'da' => 'Dansk', + 'de' => 'Deutsch (Sie)', + 'de_informal' => 'Deutsch (Du)', + 'el' => 'ελληνικά', + 'es' => 'Español', + 'es_AR' => 'Español Argentina', + 'et' => 'Eesti keel', + 'eu' => 'Euskara', + 'fa' => 'فارسی', + 'fi' => 'Suomi', + 'fr' => 'Français', + 'he' => 'עברית', + 'hr' => 'Hrvatski', + 'hu' => 'Magyar', + 'id' => 'Bahasa Indonesia', + 'it' => 'Italian', + 'ja' => '日本語', + 'ko' => '한국어', + 'lt' => 'Lietuvių Kalba', + 'lv' => 'Latviešu Valoda', + 'nb' => 'Norsk (Bokmål)', + 'ne' => 'नेपाली', + 'nn' => 'Nynorsk', + 'nl' => 'Nederlands', + 'pl' => 'Polski', + 'pt' => 'Português', + 'pt_BR' => 'Português do Brasil', + 'ro' => 'Română', + 'ru' => 'Русский', + 'sk' => 'Slovensky', + 'sl' => 'Slovenščina', + 'sv' => 'Svenska', + 'tr' => 'Türkçe', + 'uk' => 'Українська', + 'uz' => 'O‘zbekcha', + 'vi' => 'Tiếng Việt', + 'zh_CN' => '简体中文', + 'zh_TW' => '繁體中文', + ], + //!//////////////////////////////// +]; diff --git a/lang/ne/validation.php b/lang/ne/validation.php new file mode 100644 index 00000000000..f00c0f73153 --- /dev/null +++ b/lang/ne/validation.php @@ -0,0 +1,122 @@ + ':attribute स्वीकार गर्नुपर्छ।', + 'active_url' => ':attribute मान्य URL होइन।', + 'after' => ':attribute मिति :date पछिको हुनुपर्छ।', + 'alpha' => ':attribute मा अक्षर मात्र हुनुपर्छ।', + 'alpha_dash' => ':attribute मा अक्षर, अंक, ड्यास (-) र अन्डरस्कोर (_) मात्र हुनुपर्छ।', + 'alpha_num' => ':attribute मा अक्षर र अंक मात्र हुनुपर्छ।', + 'array' => ':attribute array हुनुपर्छ।', + 'backup_codes' => 'दिइएको कोड गलत छ वा पहिल्यै प्रयोग भइसकेको छ।', + 'before' => ':attribute मिति :date भन्दा पहिला हुनुपर्छ।', + 'between' => [ + 'numeric' => ':attribute :min देखि :max बीचमा हुनुपर्छ।', + 'file' => ':attribute :min देखि :max किलोबाइट बीचमा हुनुपर्छ।', + 'string' => ':attribute :min देखि :max क्यारेक्टरबीच हुनुपर्छ।', + 'array' => ':attribute मा :min देखि :max वस्तुहरू हुनुपर्छ।', + ], + 'boolean' => ':attribute साँचो (true) वा झूटो (false) हुनुपर्छ।', + 'confirmed' => ':attribute पुष्टि मिलेन।', + 'date' => ':attribute मान्य मिति होइन।', + 'date_format' => ':attribute ढाँचा :format सँग मेल खाँदैन।', + 'different' => ':attribute र :other फरक हुनुपर्छ।', + 'digits' => ':attribute मा ठीक :digits अंक हुनुपर्छ।', + 'digits_between' => ':attribute मा :min देखि :max अंक हुनुपर्छ।', + 'email' => ':attribute मान्य ईमेल ठेगाना हुनुपर्छ।', + 'ends_with' => ':attribute यी मध्ये एकले अन्त्य हुनुपर्छ: :values', + 'file' => ':attribute मान्य फाइल हुनुपर्छ।', + 'filled' => ':attribute आवश्यक छ।', + 'gt' => [ + 'numeric' => ':attribute :value भन्दा बढी हुनुपर्छ।', + 'file' => ':attribute :value किलोबाइटभन्दा बढी हुनुपर्छ।', + 'string' => ':attribute :value क्यारेक्टरभन्दा बढी हुनुपर्छ।', + 'array' => ':attribute मा :value भन्दा बढी वस्तुहरू हुनुपर्छ।', + ], + 'gte' => [ + 'numeric' => ':attribute :value भन्दा बढी वा बराबर हुनुपर्छ।', + 'file' => ':attribute :value किलोबाइटभन्दा बढी वा बराबर हुनुपर्छ।', + 'string' => ':attribute :value क्यारेक्टरभन्दा बढी वा बराबर हुनुपर्छ।', + 'array' => ':attribute मा कम्तीमा :value वस्तुहरू हुनुपर्छ।', + ], + 'exists' => 'चयन गरिएको :attribute अमान्य छ।', + 'image' => ':attribute एउटा तस्बिर हुनुपर्छ।', + 'image_extension' => ':attribute मा मान्य र समर्थित तस्बिर विस्तार (extension) हुनुपर्छ।', + 'in' => 'चयन गरिएको :attribute अमान्य छ।', + 'integer' => ':attribute पूर्णांक (integer) हुनुपर्छ।', + 'ip' => ':attribute मान्य IP ठेगाना हुनुपर्छ।', + 'ipv4' => ':attribute मान्य IPv4 ठेगाना हुनुपर्छ।', + 'ipv6' => ':attribute मान्य IPv6 ठेगाना हुनुपर्छ।', + 'json' => ':attribute मान्य JSON स्ट्रिङ हुनुपर्छ।', + 'lt' => [ + 'numeric' => ':attribute :value भन्दा कम हुनुपर्छ।', + 'file' => ':attribute :value किलोबाइटभन्दा कम हुनुपर्छ।', + 'string' => ':attribute :value क्यारेक्टरभन्दा कम हुनुपर्छ।', + 'array' => ':attribute मा :value भन्दा कम वस्तुहरू हुनुपर्छ।', + ], + 'lte' => [ + 'numeric' => ':attribute :value भन्दा कम वा बराबर हुनुपर्छ।', + 'file' => ':attribute :value किलोबाइटभन्दा कम वा बराबर हुनुपर्छ।', + 'string' => ':attribute :value क्यारेक्टरभन्दा कम वा बराबर हुनुपर्छ।', + 'array' => ':attribute मा :value भन्दा बढी वस्तुहरू हुनु हुँदैन।', + ], + 'max' => [ + 'numeric' => ':attribute :max भन्दा बढी हुन सक्दैन।', + 'file' => ':attribute :max किलोबाइटभन्दा बढी हुन सक्दैन।', + 'string' => ':attribute :max क्यारेक्टरभन्दा बढी हुन सक्दैन।', + 'array' => ':attribute मा :max भन्दा बढी वस्तुहरू हुनु हुँदैन।', + ], + 'mimes' => ':attribute फाइलको प्रकार :values हुनुपर्छ।', + 'min' => [ + 'numeric' => ':attribute कम्तीमा :min हुनुपर्छ।', + 'file' => ':attribute कम्तीमा :min किलोबाइट हुनुपर्छ।', + 'string' => ':attribute कम्तीमा :min क्यारेक्टर हुनुपर्छ।', + 'array' => ':attribute मा कम्तीमा :min वस्तुहरू हुनुपर्छ।', + ], + 'not_in' => 'चयन गरिएको :attribute अमान्य छ।', + 'not_regex' => ':attribute को ढाँचा अमान्य छ।', + 'numeric' => ':attribute संख्या हुनुपर्छ।', + 'regex' => ':attribute ढाँचा अमान्य छ।', + 'required' => ':attribute आवश्यक छ।', + 'required_if' => ':other :value हुँदा :attribute आवश्यक हुन्छ।', + 'required_with' => ':values भएमा :attribute आवश्यक छ।', + 'required_with_all' => ':values भएमा :attribute आवश्यक छ।', + 'required_without' => ':values नभएमा :attribute आवश्यक छ।', + 'required_without_all' => ':values मध्ये कुनै पनि नभएमा :attribute आवश्यक छ।', + 'same' => ':attribute र :other मिल्नुपर्छ।', + 'safe_url' => 'दिएको लिङ्क सुरक्षित नहुन सक्छ।', + 'size' => [ + 'numeric' => ':attribute ठीक :size हुनुपर्छ।', + 'file' => ':attribute ठीक :size किलोबाइट हुनुपर्छ।', + 'string' => ':attribute ठीक :size क्यारेक्टर हुनुपर्छ।', + 'array' => ':attribute मा ठीक :size वस्तुहरू हुनुपर्छ।', + ], + 'string' => ':attribute स्ट्रिङ (पाठ) हुनुपर्छ।', + 'timezone' => ':attribute मान्य समय क्षेत्र (timezone) हुनुपर्छ।', + 'totp' => 'दिएको कोड गलत छ वा सकिएको छ।', + 'unique' => ':attribute पहिल्यै प्रयोग भइसकेको छ।', + 'url' => ':attribute को ढाँचा अमान्य छ।', + 'uploaded' => 'फाइल अपलोड हुन सकेन। सर्भरले यस्तो साइज स्वीकार नगर्न सक्छ।', + + 'zip_file' => ':attribute ले ZIP फाइलभित्रको फाइल देखाउनु पर्छ।', + 'zip_file_mime' => ':attribute मा :validTypes प्रकारको फाइल हुनुपर्छ, तर :foundType भेटियो।', + 'zip_model_expected' => 'डेटा वस्तु चाहिएको थियो तर ":type" भेटियो।', + 'zip_unique' => ':attribute ZIP भित्रको वस्तु प्रकारको लागि अद्वितीय हुनुपर्छ।', + + // Custom validation lines + 'custom' => [ + 'password-confirm' => [ + 'required_with' => 'पासवर्ड पुष्टि आवश्यक छ।', + ], + ], + + // Custom validation attributes + 'attributes' => [], +]; diff --git a/lang/nl/activities.php b/lang/nl/activities.php index 261583cc3a9..230c7a58b3a 100644 --- a/lang/nl/activities.php +++ b/lang/nl/activities.php @@ -52,7 +52,7 @@ // Revisions 'revision_restore' => 'herstelde revisie', 'revision_delete' => 'verwijderde revisie', - 'revision_delete_notification' => 'Revisie met succes verwijderd', + 'revision_delete_notification' => 'Revisie succesvol verwijderd', // Favourites 'favourite_add_notification' => '":name" is toegevoegd aan je favorieten', @@ -66,14 +66,14 @@ 'auth_register' => 'registreerde als nieuwe gebruiker', 'auth_password_reset_request' => 'vraagde een nieuw gebruikerswachtwoord aan', 'auth_password_reset_update' => 'stelde gebruikerswachtwoord opnieuw in', - 'mfa_setup_method' => 'stelde MFA-methode in', + 'mfa_setup_method' => 'stelde meervoudige verificatie methode in', 'mfa_setup_method_notification' => 'Meervoudige verificatie methode succesvol geconfigureerd', - 'mfa_remove_method' => 'verwijderde MFA-methode', + 'mfa_remove_method' => 'verwijderde meervoudige verificatie methode', 'mfa_remove_method_notification' => 'Meervoudige verificatie methode is succesvol verwijderd', // Settings 'settings_update' => 'werkte instellingen bij', - 'settings_update_notification' => 'Instellingen met succes bijgewerkt', + 'settings_update_notification' => 'Instellingen succesvol bijgewerkt', 'maintenance_action_run' => 'voerde onderhoudsactie uit', // Webhooks @@ -84,21 +84,29 @@ 'webhook_delete' => 'verwijderde webhook', 'webhook_delete_notification' => 'Webhook succesvol verwijderd', + // Imports + 'import_create' => 'maakte import', + 'import_create_notification' => 'Import succesvol geüpload', + 'import_run' => 'wijzigde import', + 'import_run_notification' => 'Inhoud succesvol geïmporteerd', + 'import_delete' => 'verwijderde import', + 'import_delete_notification' => 'Import succesvol verwijderd', + // Users 'user_create' => 'maakte gebruiker aan', - 'user_create_notification' => 'Gebruiker met succes aangemaakt', + 'user_create_notification' => 'Gebruiker succesvol aangemaakt', 'user_update' => 'werkte gebruiker bij', 'user_update_notification' => 'Gebruiker succesvol bijgewerkt', 'user_delete' => 'verwijderde gebruiker', 'user_delete_notification' => 'Gebruiker succesvol verwijderd', // API Tokens - 'api_token_create' => 'maakte api token aan', - 'api_token_create_notification' => 'API-token met succes aangemaakt', - 'api_token_update' => 'werkte api token bij', - 'api_token_update_notification' => 'API-token met succes bijgewerkt', - 'api_token_delete' => 'verwijderde api token', - 'api_token_delete_notification' => 'API-token met succes verwijderd', + 'api_token_create' => 'API-token aangemaakt', + 'api_token_create_notification' => 'API-token succesvol aangemaakt', + 'api_token_update' => 'wijzigde API-token', + 'api_token_update_notification' => 'API-token succesvol bijgewerkt', + 'api_token_delete' => 'verwijderde API-token', + 'api_token_delete_notification' => 'API-token succesvol verwijderd', // Roles 'role_create' => 'maakte rol aan', @@ -110,15 +118,23 @@ // Recycle Bin 'recycle_bin_empty' => 'leegde prullenbak', - 'recycle_bin_restore' => 'is van prullenbak hersteld', - 'recycle_bin_destroy' => 'is van prullenbak verwijderd', + 'recycle_bin_restore' => 'herstelde van prullenbak', + 'recycle_bin_destroy' => 'verwijderde van prullenbak', // Comments - 'commented_on' => 'reageerde op', + 'commented_on' => 'plaatste opmerking in', 'comment_create' => 'voegde opmerking toe', 'comment_update' => 'paste opmerking aan', 'comment_delete' => 'verwijderde opmerking', + // Sort Rules + 'sort_rule_create' => 'maakte soorteerregel', + 'sort_rule_create_notification' => 'Sorteerregel succesvol aangemaakt', + 'sort_rule_update' => 'wijzigde sorteerregel', + 'sort_rule_update_notification' => 'Sorteerregel succesvol bijgewerkt', + 'sort_rule_delete' => 'verwijderde sorteerregel', + 'sort_rule_delete_notification' => 'Sorteerregel succesvol verwijderd', + // Other 'permissions_update' => 'wijzigde machtigingen', ]; diff --git a/lang/nl/auth.php b/lang/nl/auth.php index 1b974647ade..12c6b6c7bff 100644 --- a/lang/nl/auth.php +++ b/lang/nl/auth.php @@ -36,22 +36,22 @@ 'register_confirm' => 'Controleer je e-mail en klik op de bevestigingsknop om toegang te krijgen tot :appName.', 'registrations_disabled' => 'Registratie is momenteel niet mogelijk', 'registration_email_domain_invalid' => 'Dit e-maildomein wordt niet toegelaten tot deze applicatie', - 'register_success' => 'Bedankt voor het aanmelden! Je bent nu geregistreerd en aangemeld.', + 'register_success' => 'Bedankt voor het aanmelden! Je bent nu geregistreerd en ingelogd.', // Login auto-initiation - 'auto_init_starting' => 'Poging tot inloggen', - 'auto_init_starting_desc' => 'We maken contact met uw authenticatiesysteem om het inlogproces te starten. Als er na 5 seconden geen vooruitgang is, kunt u proberen op de onderstaande link te klikken.', + 'auto_init_starting' => 'Proberen in te loggen', + 'auto_init_starting_desc' => 'We maken contact met je authenticatiesysteem om het inlogproces te starten. Als er na 5 seconden geen vooruitgang is, kun je proberen op de onderstaande link te klikken.', 'auto_init_start_link' => 'Ga verder met authenticatie', // Password Reset - 'reset_password' => 'Wachtwoord herstellen', + 'reset_password' => 'Wachtwoord Herstellen', 'reset_password_send_instructions' => 'Geef je e-mailadres op en er wordt een link gestuurd om je wachtwoord te herstellen.', - 'reset_password_send_button' => 'Link sturen', - 'reset_password_sent' => 'Een link om het wachtwoord te resetten zal verstuurd worden naar :email als dat e-mailadres in het systeem gevonden is.', + 'reset_password_send_button' => 'Stuur Herstel Link', + 'reset_password_sent' => 'Een wachtwoordherstel-link zal worden verstuurd naar :email als dat e-mailadres in het systeem gevonden is.', 'reset_password_success' => 'Je wachtwoord is succesvol hersteld.', 'email_reset_subject' => 'Herstel je wachtwoord van :appName', - 'email_reset_text' => 'Je ontvangt deze e-mail omdat we een wachtwoordresetverzoek voor uw account hebben ontvangen.', - 'email_reset_not_requested' => 'Als je geen wachtwoord herstel hebt aangevraagd, hoef je niets te doen.', + 'email_reset_text' => 'Je ontvangt deze e-mail omdat we een wachtwoordherstelverzoek voor uw account hebben ontvangen.', + 'email_reset_not_requested' => 'Als je geen wachtwoordherstel hebt aangevraagd, hoef je niets te doen.', // Email Confirmation 'email_confirm_subject' => 'Bevestig je e-mailadres op :appName', @@ -59,10 +59,10 @@ 'email_confirm_text' => 'Bevestig je e-mailadres door op onderstaande knop te drukken:', 'email_confirm_action' => 'Bevestig je e-mail', 'email_confirm_send_error' => 'Een e-mailbevestiging is vereist, maar het systeem kon de e-mail niet verzenden. Neem contact op met de beheerder.', - 'email_confirm_success' => 'Uw e-mailadres is bevestigd! U zou nu moeten kunnen inloggen met dit e-mailadres.', + 'email_confirm_success' => 'Je e-mailadres is bevestigd! Je zou nu moeten kunnen inloggen met dit e-mailadres.', 'email_confirm_resent' => 'Bevestigingsmail opnieuw verzonden, controleer je inbox.', 'email_confirm_thanks' => 'Bedankt voor de bevestiging!', - 'email_confirm_thanks_desc' => 'Wacht even terwijl uw bevestiging wordt behandeld. Als u na 3 seconden niet wordt doorverwezen, drukt u op de onderstaande link "Doorgaan" om verder te gaan.', + 'email_confirm_thanks_desc' => 'Wacht even terwijl je bevestiging wordt behandeld. Als je na 3 seconden niet wordt doorverwezen, druk dan op de onderstaande link "Doorgaan" om verder te gaan.', 'email_not_confirmed' => 'E-mailadres nog niet bevestigd', 'email_not_confirmed_text' => 'Je e-mailadres is nog niet bevestigd.', @@ -76,42 +76,42 @@ 'user_invite_email_text' => 'Klik op de onderstaande knop om een account wachtwoord in te stellen en toegang te krijgen:', 'user_invite_email_action' => 'Account wachtwoord instellen', 'user_invite_page_welcome' => 'Welkom bij :appName!', - 'user_invite_page_text' => 'Om je account af te ronden en toegang te krijgen moet je een wachtwoord instellen dat gebruikt wordt om in te loggen op :appName bij toekomstige bezoeken.', - 'user_invite_page_confirm_button' => 'Bevestig wachtwoord', - 'user_invite_success_login' => 'Wachtwoord ingesteld, u zou nu moeten kunnen inloggen met uw ingestelde wachtwoord om toegang te krijgen tot :appName!', + 'user_invite_page_text' => 'Om je registratie af te ronden en toegang te krijgen moet je een wachtwoord instellen dat gebruikt zal worden om in te loggen op :appName bij toekomstige bezoeken.', + 'user_invite_page_confirm_button' => 'Wachtwoord Bevestigen', + 'user_invite_success_login' => 'Wachtwoord ingesteld, je zou nu moeten kunnen inloggen met je ingestelde wachtwoord om toegang te krijgen tot :appName!', // Multi-factor Authentication - 'mfa_setup' => 'Multi-factor authenticatie instellen', - 'mfa_setup_desc' => 'Stel multi-factor authenticatie in als een extra beveiligingslaag voor uw gebruikersaccount.', - 'mfa_setup_configured' => 'Reeds geconfigureerd', + 'mfa_setup' => 'Meervoudige verificatie instellen', + 'mfa_setup_desc' => 'Stel meervoudige verificatie in als een extra beveiligingslaag voor je gebruikersaccount.', + 'mfa_setup_configured' => 'Is al geconfigureerd', 'mfa_setup_reconfigure' => 'Herconfigureren', 'mfa_setup_remove_confirmation' => 'Weet je zeker dat je deze multi-factor authenticatie methode wilt verwijderen?', 'mfa_setup_action' => 'Instellen', - 'mfa_backup_codes_usage_limit_warning' => 'U heeft minder dan 5 back-upcodes over. Genereer en sla een nieuwe set op voordat je geen codes meer hebt om te voorkomen dat je buiten je account wordt gesloten.', + 'mfa_backup_codes_usage_limit_warning' => 'Je hebt minder dan 5 back-upcodes over. Genereer en sla een nieuwe set op voordat je geen codes meer hebt om te voorkomen dat je buiten je account wordt gesloten.', 'mfa_option_totp_title' => 'Mobiele app', - 'mfa_option_totp_desc' => 'Om multi-factor authenticatie te gebruiken heeft u een mobiele applicatie nodig die TOTP ondersteunt, zoals Google Authenticator, Authy of Microsoft Authenticator.', + 'mfa_option_totp_desc' => 'Om meervoudige verificatie te gebruiken heb je een mobiele applicatie nodig die TOTP ondersteunt, zoals Google Authenticator, Authy of Microsoft Authenticator.', 'mfa_option_backup_codes_title' => 'Back-up Codes', - 'mfa_option_backup_codes_desc' => 'Bewaar veilig een set eenmalige back-upcodes die u kunt invoeren om uw identiteit te verifiëren.', + 'mfa_option_backup_codes_desc' => 'Genereert een set met eenmalige back-upcodes die je kan invoeren om je identiteit te bevestigen. Bewaar deze op een veilige plaats.', 'mfa_gen_confirm_and_enable' => 'Bevestigen en inschakelen', - 'mfa_gen_backup_codes_title' => 'Reservekopiecodes instellen', - 'mfa_gen_backup_codes_desc' => 'De onderstaande lijst met codes opslaan op een veilige plaats. Bij de toegang tot het systeem kun je een van de codes gebruiken als tweede verificatiemechanisme.', + 'mfa_gen_backup_codes_title' => 'Back-up codes instellen', + 'mfa_gen_backup_codes_desc' => 'Bewaar de onderstaande lijst met codes op een veilige plaats. Bij toegang tot het systeem kun je een van de codes gebruiken als tweede verificatiemechanisme.', 'mfa_gen_backup_codes_download' => 'Download Codes', 'mfa_gen_backup_codes_usage_warning' => 'Elke code kan slechts eenmaal gebruikt worden', 'mfa_gen_totp_title' => 'Mobiele app installatie', - 'mfa_gen_totp_desc' => 'Om multi-factor authenticatie te gebruiken heeft u een mobiele applicatie nodig die TOTP ondersteunt, zoals Google Authenticator, Authy of Microsoft Authenticator.', - 'mfa_gen_totp_scan' => 'Scan de onderstaande QR-code door gebruik te maken van uw favoriete authenticatie app om aan de slag te gaan.', + 'mfa_gen_totp_desc' => 'Om meervoudige verificatie te gebruiken heb je een mobiele applicatie nodig die TOTP ondersteunt, zoals Google Authenticator, Authy of Microsoft Authenticator.', + 'mfa_gen_totp_scan' => 'Scan de onderstaande QR-code door gebruik te maken van je favoriete authenticatie-app om aan de slag te gaan.', 'mfa_gen_totp_verify_setup' => 'Installatie verifiëren', - 'mfa_gen_totp_verify_setup_desc' => 'Controleer of alles werkt door het invoeren van een code, die wordt gegenereerd binnen uw authenticatie-app, in het onderstaande invoerveld:', - 'mfa_gen_totp_provide_code_here' => 'Geef uw app gegenereerde code hier', + 'mfa_gen_totp_verify_setup_desc' => 'Controleer of alles werkt door het invoeren van een code, die wordt gegenereerd binnen je authenticatie-app, in het onderstaande invoerveld:', + 'mfa_gen_totp_provide_code_here' => 'Vul je app-gegenereerde code hier in', 'mfa_verify_access' => 'Verifieer toegang', - 'mfa_verify_access_desc' => 'Uw gebruikersaccount vereist dat u uw identiteit bevestigt via een extra verificatieniveau voordat u toegang krijgt. Verifieer met een van de door u geconfigureerde methoden om verder te gaan.', - 'mfa_verify_no_methods' => 'Geen methoden geconfigureerd', - 'mfa_verify_no_methods_desc' => 'Er konden geen meervoudige verificatie methoden voor uw account gevonden worden. Je zult minstens één methode moeten instellen voordat u toegang krijgt.', + 'mfa_verify_access_desc' => 'Je moet je identiteit bevestigen via een extra verificatieniveau voordat je toegang krijgt tot je gebruikersaccount. Verifieer met een van de door jou geconfigureerde methoden om verder te gaan.', + 'mfa_verify_no_methods' => 'Geen methode geconfigureerd', + 'mfa_verify_no_methods_desc' => 'Er konden geen meervoudige verificatie methoden voor je account gevonden worden. Je zult minstens één methode moeten instellen voordat je toegang krijgt.', 'mfa_verify_use_totp' => 'Verifieer met een mobiele app', 'mfa_verify_use_backup_codes' => 'Verifieer met een back-up code', 'mfa_verify_backup_code' => 'Back-up code', - 'mfa_verify_backup_code_desc' => 'Voer een van uw resterende back-up codes hieronder in:', + 'mfa_verify_backup_code_desc' => 'Voer één van je resterende back-up codes hieronder in:', 'mfa_verify_backup_code_enter_here' => 'Voer hier de back-up code in', - 'mfa_verify_totp_desc' => 'Voer de code, gegenereerd met uw mobiele app, hieronder in:', + 'mfa_verify_totp_desc' => 'Voer de code, gegenereerd met je mobiele app, hieronder in:', 'mfa_setup_login_notification' => 'Meervoudige verificatie methode geconfigureerd, Gelieve opnieuw in te loggen met de geconfigureerde methode.', ]; diff --git a/lang/nl/common.php b/lang/nl/common.php index 898fd10a556..bcd7fbe0853 100644 --- a/lang/nl/common.php +++ b/lang/nl/common.php @@ -20,7 +20,7 @@ 'description' => 'Beschrijving', 'role' => 'Rol', 'cover_image' => 'Omslagfoto', - 'cover_image_description' => 'Deze afbeelding moet ongeveer 440x250px zijn.', + 'cover_image_description' => 'Deze afbeelding moet ongeveer 440x250 pixels zijn, hoewel deze flexibel zal worden geschaald en bijgesneden naargelang dit nodig is in de verschillende scenario\'s van de gebruikersinterface. De daadwerkelijk gebruikte afmetingen voor weergave zullen dan verschillen.', // Actions 'actions' => 'Acties', @@ -30,6 +30,8 @@ 'create' => 'Aanmaken', 'update' => 'Bijwerken', 'edit' => 'Bewerk', + 'archive' => 'Archiveer', + 'unarchive' => 'Terughalen', 'sort' => 'Sorteer', 'move' => 'Verplaats', 'copy' => 'Kopieer', @@ -38,7 +40,7 @@ 'delete_confirm' => 'Verwijdering bevestigen', 'search' => 'Zoek', 'search_clear' => 'Zoekopdracht wissen', - 'reset' => 'Reset', + 'reset' => 'Wissen', 'remove' => 'Verwijder', 'add' => 'Voeg toe', 'configure' => 'Configureer', @@ -62,7 +64,7 @@ 'sort_name' => 'Naam', 'sort_default' => 'Standaard', 'sort_created_at' => 'Aanmaakdatum', - 'sort_updated_at' => 'Gewijzigd op', + 'sort_updated_at' => 'Bijwerkdatum', // Misc 'deleted_user' => 'Verwijderde gebruiker', @@ -107,4 +109,7 @@ // Not directly used but available for convenience to users. 'privacy_policy' => 'Privacybeleid', 'terms_of_service' => 'Algemene voorwaarden', + + // OpenSearch + 'opensearch_description' => 'Zoek in :appName', ]; diff --git a/lang/nl/components.php b/lang/nl/components.php index 852a7ea85fe..a7fdc3725ec 100644 --- a/lang/nl/components.php +++ b/lang/nl/components.php @@ -23,7 +23,7 @@ 'image_load_more' => 'Laad meer', 'image_image_name' => 'Afbeeldingsnaam', 'image_delete_used' => 'Deze afbeelding is op onderstaande pagina\'s in gebruik.', - 'image_delete_confirm_text' => 'Weet u zeker dat u deze afbeelding wilt verwijderen?', + 'image_delete_confirm_text' => 'Weet je zeker dat je deze afbeelding wilt verwijderen?', 'image_select_image' => 'Kies afbeelding', 'image_dropzone' => 'Sleep afbeeldingen naar hier of klik hier om te uploaden', 'image_dropzone_drop' => 'Sleep hier de afbeeldingen naar toe', diff --git a/lang/nl/editor.php b/lang/nl/editor.php index 6a3852a0764..cc3ad108c03 100644 --- a/lang/nl/editor.php +++ b/lang/nl/editor.php @@ -13,6 +13,7 @@ 'cancel' => 'Annuleer', 'save' => 'Opslaan', 'close' => 'Sluit', + 'apply' => 'Toepassen', 'undo' => 'Maak ongedaan', 'redo' => 'Opnieuw uitvoeren', 'left' => 'Links', @@ -47,6 +48,7 @@ 'superscript' => 'Superscript', 'subscript' => 'Subscript', 'text_color' => 'Tekstkleur', + 'highlight_color' => 'Accentkleur', 'custom_color' => 'Aangepaste kleur', 'remove_color' => 'Verwijder kleur', 'background_color' => 'Tekstmarkeringskleur', @@ -81,6 +83,9 @@ 'table_properties' => 'Tabeleigenschappen', 'table_properties_title' => 'Tabeleigenschappen', 'delete_table' => 'Verwijder tabel', + 'table_clear_formatting' => 'Tabel opmaak wissen', + 'resize_to_contents' => 'Formaat aanpassen naar inhoud', + 'row_header' => 'Koptekst rij', 'insert_row_before' => 'Rij boven invoegen', 'insert_row_after' => 'Rij onder invoegen', 'delete_row' => 'Rij verwijderen', @@ -140,10 +145,11 @@ 'source' => 'Bron', 'alt_desc' => 'Alternatieve beschrijving', 'embed' => 'Insluiten', - 'paste_embed' => 'Plak uw insluitcode hieronder:', + 'paste_embed' => 'Plak je insluitcode hieronder:', 'url' => 'URL', 'text_to_display' => 'Weer te geven tekst', 'title' => 'Titel', + 'browse_links' => 'Blader links', 'open_link' => 'Open koppeling', 'open_link_in' => 'Open koppeling in...', 'open_link_current' => 'Huidig venster', @@ -160,6 +166,8 @@ 'about' => 'Over de bewerker', 'about_title' => 'Over de WYSIWYG Bewerker', 'editor_license' => 'Bewerker Licentie & Copyright', + 'editor_lexical_license' => 'Deze editor is gemaakt als een fork van :lexicalLink welke is verstrekt onder de MIT-licentie.', + 'editor_lexical_license_link' => 'Volledige licentieinformatie kan hier gevonden worden.', 'editor_tiny_license' => 'Deze editor is gemaakt met behulp van :tinyLink welke is verstrekt onder de MIT-licentie.', 'editor_tiny_license_link' => 'De copyright- en licentiegegevens van TinyMCE vindt u hier.', 'save_continue' => 'Pagina opslaan en verdergaan', diff --git a/lang/nl/entities.php b/lang/nl/entities.php index ee13185f697..b9be1f42b3c 100644 --- a/lang/nl/entities.php +++ b/lang/nl/entities.php @@ -6,24 +6,24 @@ return [ // Shared - 'recently_created' => 'Recent gemaakt', - 'recently_created_pages' => 'Recent gemaakte pagina\'s', + 'recently_created' => 'Recent Aangemaakt', + 'recently_created_pages' => 'Recent Aangemaakte Pagina\'s', 'recently_updated_pages' => 'Recent bijgewerkte pagina\'s', - 'recently_created_chapters' => 'Recent gemaakte hoofdstukken', - 'recently_created_books' => 'Recent gemaakte boeken', - 'recently_created_shelves' => 'Recent gemaakte boekenplanken', + 'recently_created_chapters' => 'Recent Aangemaakte Hoofdstukken', + 'recently_created_books' => 'Recent Aangemaakte Boeken', + 'recently_created_shelves' => 'Recent Aangemaakte Boekenplanken', 'recently_update' => 'Recent bijgewerkt', 'recently_viewed' => 'Recent bekeken', 'recent_activity' => 'Recente activiteit', 'create_now' => 'Maak er nu één', 'revisions' => 'Revisies', 'meta_revision' => 'Revisie #:revisionCount', - 'meta_created' => 'Gemaakt op: :timeLength', - 'meta_created_name' => 'Gemaakt op :timeLength door :user', + 'meta_created' => 'Gemaakt: :timeLength', + 'meta_created_name' => 'Gemaakt: :timeLength door :user', 'meta_updated' => 'Bijgewerkt: :timeLength', 'meta_updated_name' => 'Bijgewerkt: :timeLength door :user', 'meta_owned_name' => 'Eigendom van :user', - 'meta_reference_count' => 'Gerefereerd door :count item|Gerefereerd door :count items', + 'meta_reference_count' => 'Verwijzing in :count item|Verwijzing in :count items', 'entity_select' => 'Entiteit selecteren', 'entity_select_lack_permission' => 'Je hebt niet de vereiste machtiging om dit item te selecteren', 'images' => 'Afbeeldingen', @@ -39,12 +39,40 @@ 'export_pdf' => 'PDF bestand', 'export_text' => 'Normaal tekstbestand', 'export_md' => 'Markdown bestand', + 'export_zip' => 'Portable ZIP', + 'default_template' => 'Standaard Paginasjabloon', + 'default_template_explain' => 'Ken een paginasjabloon toe die zal worden gebruikt als de standaardinhoud voor alle pagina\'s die binnen dit item worden aangemaakt. Houd er rekening mee dat dit alleen zal worden gebruikt als de paginamaker leesrechten heeft voor de gekozen sjabloonpagina.', + 'default_template_select' => 'Selecteer een sjabloonpagina', + 'import' => 'Import', + 'import_validate' => 'Valideer Import', + 'import_desc' => 'Importeer boeken, hoofdstukken & pagina\'s met een portable Zip-export van dezelfde, of een andere omgeving. Selecteer een Zip-bestand om door te gaan. Nadat het bestand is geüpload en gecontroleerd kunt u de import configureren en doorvoeren in de volgende weergave.', + 'import_zip_select' => 'Selecteer een Zip-bestand om te uploaden', + 'import_zip_validation_errors' => 'Er zijn fouten gevonden tijdens het controleren van het Zip-bestand:', + 'import_pending' => 'Wachtende Imports', + 'import_pending_none' => 'Er zijn geen imports gestart.', + 'import_continue' => 'Importeren Voortzetten', + 'import_continue_desc' => 'Controleer de inhoud die gaat worden geïmporteerd vanuit get geüploade Zip-bestand. Voer de import door om de inhoud toe te voegen aan dit systeem. Bij een succesvolle import zal het geüploade Zip-bestand automatisch verwijderd worden.', + 'import_details' => 'Import Details', + 'import_run' => 'Import Doorvoeren', + 'import_size' => ':size Import Zip Grootte', + 'import_uploaded_at' => 'Geüpload :relativeTime', + 'import_uploaded_by' => 'Geüpload door', + 'import_location' => 'Importlocatie', + 'import_location_desc' => 'Selecteer een locatie voor de geïmporteerde inhoud. Je hebt de bijbehorende machtigingen nodig op de importlocatie.', + 'import_delete_confirm' => 'Weet je zeker dat je deze import wilt verwijderen?', + 'import_delete_desc' => 'Dit zal het Zip-bestand van de import permanent verwijderen.', + 'import_errors' => 'Importeerfouten', + 'import_errors_desc' => 'De volgende fouten deden zich voor tijdens het importeren:', + 'breadcrumb_siblings_for_page' => 'Navigeer pagina\'s op hetzelfde niveau', + 'breadcrumb_siblings_for_chapter' => 'Navigeer hoofdstukken op hetzelfde niveau', + 'breadcrumb_siblings_for_book' => 'Navigeer boeken op hetzelfde niveau', + 'breadcrumb_siblings_for_bookshelf' => 'Navigeer boekenplanken op hetzelfde niveau', // Permissions and restrictions 'permissions' => 'Machtigingen', 'permissions_desc' => 'Stel hier machtigingen in om de standaardmachtigingen van gebruikersrollen te overschrijven.', - 'permissions_book_cascade' => 'Machtigingen voor boeken worden automatisch doorgegeven aan hoofdstukken en pagina\'s, behalve als deze hun eigen machtigingen hebben.', - 'permissions_chapter_cascade' => 'Machtigingen ingesteld op hoofdstukken zullen automatisch worden doorgegeven aan onderliggende pagina\'s, behalve als deze hun eigen machtigingen hebben.', + 'permissions_book_cascade' => 'Machtigingen voor boeken worden automatisch doorgegeven aan hoofdstukken en pagina\'s, tenzij deze hun eigen machtigingen hebben.', + 'permissions_chapter_cascade' => 'Machtigingen ingesteld op hoofdstukken zullen automatisch worden doorgegeven aan onderliggende pagina\'s, tenzij deze hun eigen machtigingen hebben.', 'permissions_save' => 'Machtigingen opslaan', 'permissions_owner' => 'Eigenaar', 'permissions_role_everyone_else' => 'De rest', @@ -88,8 +116,8 @@ 'shelves_popular' => 'Populaire boekenplanken', 'shelves_new' => 'Nieuwe boekenplanken', 'shelves_new_action' => 'Nieuwe boekenplank', - 'shelves_popular_empty' => 'De meest populaire boekenplanken worden hier weergegeven.', - 'shelves_new_empty' => 'De meest recent gemaakte boekenplanken worden hier weergeven.', + 'shelves_popular_empty' => 'Hier worden de meest populaire boekenplanken weergegeven.', + 'shelves_new_empty' => 'Hier worden de meest recent gemaakte boekenplanken weergegeven.', 'shelves_save' => 'Boekenplank opslaan', 'shelves_books' => 'Boeken op deze plank', 'shelves_add_books' => 'Voeg boeken toe aan deze plank', @@ -101,12 +129,12 @@ 'shelves_delete' => 'Verwijder Boekenplank', 'shelves_delete_named' => 'Verwijder Boekenplank :name', 'shelves_delete_explain' => "Dit zal de boekenplank met de naam ':naam' verwijderen. Boeken die op deze plank staan worden echter niet verwijderd.", - 'shelves_delete_confirmation' => 'Weet u zeker dat u deze boekenplank wilt verwijderen?', + 'shelves_delete_confirmation' => 'Weet je zeker dat je deze boekenplank wilt verwijderen?', 'shelves_permissions' => 'Boekenplank Machtigingen', 'shelves_permissions_updated' => 'Boekenplank Machtigingen Bijgewerkt', 'shelves_permissions_active' => 'Machtigingen op Boekenplank Actief', 'shelves_permissions_cascade_warning' => 'De ingestelde machtigingen op deze boekenplank worden niet automatisch toegepast op de boeken van deze boekenplank. Dit is omdat een boek toegekend kan worden op meerdere boekenplanken. De machtigingen van deze boekenplank kunnen echter wel gekopieerd worden naar de boeken van deze boekenplank via de optie hieronder.', - 'shelves_permissions_create' => '\'Maak boekenplank\' machtigingen worden enkel gebruikt om machtigingen te kopiëren naar boeken binnenin een boekenplank door gebruik te maken van onderstaande actie. Deze machtigingen beheren het maken van boeken niet.', + 'shelves_permissions_create' => '\'Maak boekenplank\' machtigingen worden enkel gebruikt om machtigingen te kopiëren naar boeken binnenin een boekenplank door gebruik te maken van onderstaande actie. Deze machtigingen laten niet toe om een nieuw boek aan te maken.', 'shelves_copy_permissions_to_books' => 'Kopieer Machtigingen naar Boeken', 'shelves_copy_permissions' => 'Kopieer Machtigingen', 'shelves_copy_permissions_explain' => 'Met deze actie worden de machtigingen van deze boekenplank gekopieerd naar alle boeken van deze boekenplank. Voor je deze actie uitvoert, moet je ervoor zorgen dat alle wijzigingen in de machtigingen van deze boekenplank zijn opgeslagen.', @@ -116,13 +144,13 @@ 'book' => 'Boek', 'books' => 'Boeken', 'x_books' => ':count Boek|:count Boeken', - 'books_empty' => 'Geen boeken gemaakt', + 'books_empty' => 'Er zijn geen boeken aangemaakt', 'books_popular' => 'Populaire boeken', 'books_recent' => 'Recente boeken', 'books_new' => 'Nieuwe boeken', 'books_new_action' => 'Nieuw boek', - 'books_popular_empty' => 'De meest populaire boeken worden hier weergegeven.', - 'books_new_empty' => 'De meest recent gemaakte boeken verschijnen hier.', + 'books_popular_empty' => 'Hier worden de meest populaire boeken weergegeven.', + 'books_new_empty' => 'Hier worden de meest recent gemaakte boeken weergegeven.', 'books_create' => 'Nieuw boek maken', 'books_delete' => 'Boek verwijderen', 'books_delete_named' => 'Verwijder boek :bookName', @@ -132,9 +160,6 @@ 'books_edit_named' => 'Bewerk boek :bookName', 'books_form_book_name' => 'Boek naam', 'books_save' => 'Boek opslaan', - 'books_default_template' => 'Standaard Paginasjabloon', - 'books_default_template_explain' => 'Wijs een paginasjabloon toe die zal worden gebruikt als de standaardinhoud voor alle nieuwe pagina\'s in dit boek. Bedenk wel dat dit sjabloon alleen gebruikt wordt als de paginamaker machtiging heeft om die te bekijken.', - 'books_default_template_select' => 'Selecteer een sjabloonpagina', 'books_permissions' => 'Boek machtigingen', 'books_permissions_updated' => 'Boek Machtigingen Bijgewerkt', 'books_empty_contents' => 'Er zijn nog geen hoofdstukken en pagina\'s voor dit boek gemaakt.', @@ -145,7 +170,9 @@ 'books_search_this' => 'Zoeken in dit boek', 'books_navigation' => 'Boek navigatie', 'books_sort' => 'Inhoud van het boek sorteren', - 'books_sort_desc' => 'Verplaats hoofdstukken en pagina\'s binnen een boek om ze te organiseren. Andere boeken kunnen worden toegevoegd, zodat hoofdstukken en pagina\'s gemakkelijk tussen boeken kunnen worden verplaatst.', + 'books_sort_desc' => 'Verplaats hoofdstukken en pagina\'s door het boek om ze te organiseren. Andere boeken kunnen worden toegevoegd zodat hoofdstukken en pagina\'s gemakkelijk tussen boeken kunnen worden verplaatst. Het is mogelijk om een automatische sorteerregel in te stellen die de inhoud zal sorteren bij wijzigingen.', + 'books_sort_auto_sort' => 'Automatisch Sorteren', + 'books_sort_auto_sort_active' => 'Automatisch Sorteren Actief: :sortName', 'books_sort_named' => 'Sorteer boek :bookName', 'books_sort_name' => 'Sorteren op naam', 'books_sort_created' => 'Sorteren op datum van aanmaken', @@ -187,7 +214,7 @@ 'chapters_copy' => 'Kopieer Hoofdstuk', 'chapters_copy_success' => 'Hoofdstuk succesvol gekopieerd', 'chapters_permissions' => 'Hoofdstuk Machtigingen', - 'chapters_empty' => 'Dit hoofdstuk heeft op dit moment geen pagina\'s.', + 'chapters_empty' => 'Er zijn geen pagina\'s in dit hoofdstuk aangemaakt.', 'chapters_permissions_active' => 'Hoofdstuk Machtigingen Actief', 'chapters_permissions_success' => 'Hoofdstuk Machtigingen Bijgewerkt', 'chapters_search_this' => 'Zoek in dit hoofdstuk', @@ -207,7 +234,7 @@ 'pages_delete_draft' => 'Verwijder concept pagina', 'pages_delete_success' => 'Pagina verwijderd', 'pages_delete_draft_success' => 'Concept verwijderd', - 'pages_delete_warning_template' => 'Deze pagina is actief in gebruik als standaard paginasjabloon. Deze boeken zullen geen standaard paginasjabloon meer hebben als deze pagina verwijderd is.', + 'pages_delete_warning_template' => 'Deze pagina wordt actief gebruikt als standaardsjabloon voor een boek of hoofdstuk. Nadat deze pagina is verwijderd, zullen deze boeken of hoofdstukken geen standaardsjabloon meer toegewezen hebben.', 'pages_delete_confirm' => 'Weet je zeker dat je deze pagina wilt verwijderen?', 'pages_delete_draft_confirm' => 'Weet je zeker dat je dit concept wilt verwijderen?', 'pages_editing_named' => 'Pagina :pageName aan het bewerken', @@ -224,15 +251,17 @@ 'pages_edit_switch_to_markdown_clean' => '(Opgeschoonde Inhoud)', 'pages_edit_switch_to_markdown_stable' => '(Stabiele Inhoud)', 'pages_edit_switch_to_wysiwyg' => 'Schakel naar de WYSIWYG Bewerker', + 'pages_edit_switch_to_new_wysiwyg' => 'Schakel naar de nieuwe WYSIWYG', + 'pages_edit_switch_to_new_wysiwyg_desc' => '(In Beta-testfase)', 'pages_edit_set_changelog' => 'Logboek instellen', 'pages_edit_enter_changelog_desc' => 'Geef een korte omschrijving van de wijzigingen die je gemaakt hebt', 'pages_edit_enter_changelog' => 'Voeg toe aan logboek', 'pages_editor_switch_title' => 'Schakel Bewerker', - 'pages_editor_switch_are_you_sure' => 'Weet u zeker dat u de bewerker voor deze pagina wilt wijzigen?', + 'pages_editor_switch_are_you_sure' => 'Weet je zeker dat je de bewerker voor deze pagina wilt wijzigen?', 'pages_editor_switch_consider_following' => 'Houd rekening met het volgende als u van bewerker verandert:', 'pages_editor_switch_consideration_a' => 'Eenmaal opgeslagen, zal de nieuwe bewerker keuze gebruikt worden door alle toekomstige gebruikers, ook diegene die zelf niet van bewerker type kunnen veranderen.', - 'pages_editor_switch_consideration_b' => 'Dit kan mogelijk tot een verlies van detail en syntaxis leiden in bepaalde omstandigheden.', - 'pages_editor_switch_consideration_c' => 'De veranderingen aan Labels of aan het logboek, sinds de laatste keer opslaan, zullen niet behouden blijven met deze wijziging.', + 'pages_editor_switch_consideration_b' => 'Dit kan mogelijks tot een verlies van detail en syntax leiden in bepaalde omstandigheden.', + 'pages_editor_switch_consideration_c' => 'De veranderingen aan Labels of aan het wijzigingslogboek, sinds de laatste keer opslaan, zullen niet behouden blijven met deze wijziging.', 'pages_save' => 'Pagina opslaan', 'pages_title' => 'Pagina titel', 'pages_name' => 'Pagina naam', @@ -243,10 +272,11 @@ 'pages_md_insert_drawing' => 'Tekening invoegen', 'pages_md_show_preview' => 'Toon voorbeeld', 'pages_md_sync_scroll' => 'Synchroniseer scrollen van voorbeeld', + 'pages_md_plain_editor' => 'Normale tekst bewerker', 'pages_drawing_unsaved' => 'Niet-opgeslagen Tekening Gevonden', - 'pages_drawing_unsaved_confirm' => 'Er zijn niet-opgeslagen tekeninggegevens gevonden van een eerdere mislukte poging om de tekening op te slaan. Wilt u deze niet-opgeslagen tekening herstellen en verder bewerken?', + 'pages_drawing_unsaved_confirm' => 'Er zijn niet-opgeslagen tekeninggegevens gevonden van een eerdere mislukte poging om de tekening op te slaan. Wil je deze niet-opgeslagen tekening herstellen en verder bewerken?', 'pages_not_in_chapter' => 'Pagina is niet in een hoofdstuk', - 'pages_move' => 'Pagina verplaatsten', + 'pages_move' => 'Pagina Verplaatsen', 'pages_copy' => 'Pagina kopiëren', 'pages_copy_desination' => 'Kopieër bestemming', 'pages_copy_success' => 'Pagina succesvol gekopieerd', @@ -254,7 +284,7 @@ 'pages_permissions_success' => 'Pagina machtigingen bijgewerkt', 'pages_revision' => 'Revisie', 'pages_revisions' => 'Pagina revisies', - 'pages_revisions_desc' => 'Hieronder staan alle vorige versies van deze pagina. U kunt oude paginaversies terugkijken, vergelijken en herstellen indien de rechten dit toelaten. De volledige geschiedenis van de pagina wordt hier mogelijk niet volledig weergegeven omdat, afhankelijk van de systeemconfiguratie, oude versies al automatisch verwijderd kunnen zijn.', + 'pages_revisions_desc' => 'Hieronder staan alle vorige versies van deze pagina. Je kunt oude paginaversies terugkijken, vergelijken en herstellen indien de rechten dit toelaten. De volledige geschiedenis van de pagina wordt hier mogelijk niet volledig weergegeven omdat, afhankelijk van de systeemconfiguratie, oude versies al automatisch verwijderd kunnen zijn.', 'pages_revisions_named' => 'Pagina revisies voor :pageName', 'pages_revision_named' => 'Pagina revisie voor :pageName', 'pages_revision_restored_from' => 'Hersteld van #:id; :samenvatting', @@ -284,7 +314,7 @@ 'pages_references_update_revision' => 'Automatische systeemupdate van interne links', 'pages_initial_name' => 'Nieuwe pagina', 'pages_editing_draft_notification' => 'U bewerkt momenteel een concept dat voor het laatst is opgeslagen op :timeDiff.', - 'pages_draft_edited_notification' => 'Deze pagina is sindsdien bijgewerkt. Het wordt aanbevolen dat u dit concept verwijderd.', + 'pages_draft_edited_notification' => 'Deze pagina is sindsdien bijgewerkt. Het wordt aanbevolen dat je dit concept verwijderd.', 'pages_draft_page_changed_since_creation' => 'Deze pagina is bijgewerkt sinds het aanmaken van dit concept. Het wordt aanbevolen dat u dit concept verwijdert of ervoor zorgt dat u wijzigingen op de pagina niet overschrijft.', 'pages_draft_edit_active' => [ 'start_a' => ':count gebruikers zijn begonnen deze pagina te bewerken', @@ -309,7 +339,7 @@ 'tags_index_desc' => 'Labels kunnen worden toegepast op inhoud binnen het systeem om een flexibele vorm van categorisering toe te passen. Labels kunnen zowel een sleutel als een waarde hebben, waarbij de waarde optioneel is. Eenmaal toegepast, kan de inhoud worden opgevraagd aan de hand van de naam en de waarde van het label.', 'tag_name' => 'Labelnaam', 'tag_value' => 'Labelwaarde (Optioneel)', - 'tags_explain' => "Voeg enkele labels toe om uw inhoud beter te categoriseren. \nJe kunt een waarde aan een label toekennen voor een meer gedetailleerde organisatie.", + 'tags_explain' => "Voeg enkele labels toe om je inhoud beter te categoriseren.\nJe kunt een waarde aan een label toekennen voor een meer gedetailleerde organisatie.", 'tags_add' => 'Voeg nog een label toe', 'tags_remove' => 'Verwijder deze label', 'tags_usages' => 'Totaal aantal label-toepassingen', @@ -327,9 +357,9 @@ 'attachments_explain_instant_save' => 'Wijzigingen worden meteen opgeslagen.', 'attachments_upload' => 'Bestand uploaden', 'attachments_link' => 'Link toevoegen', - 'attachments_upload_drop' => 'Je kan ook een bestand hiernaartoe slepen om het als bijlage to uploaden.', + 'attachments_upload_drop' => 'Je kan ook een bestand hiernaartoe slepen om het als bijlage te uploaden.', 'attachments_set_link' => 'Zet link', - 'attachments_delete' => 'Weet u zeker dat u deze bijlage wilt verwijderen?', + 'attachments_delete' => 'Weet je zeker dat je deze bijlage wilt verwijderen?', 'attachments_dropzone' => 'Sleep hier de bestanden naar toe', 'attachments_no_files' => 'Er zijn geen bestanden geüpload', 'attachments_explain_link' => 'Je kunt een hyperlink toevoegen als je geen bestanden wilt uploaden. Dit kan een link naar een andere pagina op deze website zijn, maar ook een link naar een andere website.', @@ -350,7 +380,7 @@ 'attachments_link_attached' => 'Hyperlink succesvol gekoppeld aan de pagina', 'templates' => 'Sjablonen', 'templates_set_as_template' => 'Pagina is een sjabloon', - 'templates_explain_set_as_template' => 'Je kan deze pagina als sjabloon instellen zodat de inhoud gebruikt kan worden bij het maken van andere pagina\'s. Andere gebruikers kunnen dit sjabloon gebruiken als ze de machtiging hebben voor deze pagina.', + 'templates_explain_set_as_template' => 'Je kan deze pagina als sjabloon instellen zodat de inhoud gebruikt kan worden bij het maken van andere pagina\'s. Andere gebruikers kunnen dit sjabloon gebruiken als ze de machtiging hebben om deze pagina te zien.', 'templates_replace_content' => 'Pagina-inhoud vervangen', 'templates_append_content' => 'Toevoegen aan pagina-inhoud', 'templates_prepend_content' => 'Voeg vooraan toe aan pagina-inhoud', @@ -364,26 +394,35 @@ 'profile_not_created_shelves' => ':userName heeft geen boekenplanken gemaakt', // Comments - 'comment' => 'Reactie', - 'comments' => 'Reacties', - 'comment_add' => 'Reactie toevoegen', - 'comment_placeholder' => 'Laat hier een reactie achter', - 'comment_count' => '{0} Geen reacties|{1} 1 Reactie|[2,*] :count Reacties', - 'comment_save' => 'Sla reactie op', - 'comment_new' => 'Nieuwe reactie', - 'comment_created' => 'reactie gegeven :createDiff', + 'comment' => 'Opmerking', + 'comments' => 'Opmerkingen', + 'comment_add' => 'Opmerking toevoegen', + 'comment_none' => 'Geen opmerkingen om weer te geven', + 'comment_placeholder' => 'Laat hier een opmerking achter', + 'comment_thread_count' => ':count Opmerking Thread|:count Opmerking Threads', + 'comment_archived_count' => ':count Gearchiveerd', + 'comment_archived_threads' => 'Gearchiveerde Threads', + 'comment_save' => 'Sla opmerking op', + 'comment_new' => 'Nieuwe opmerking', + 'comment_created' => 'opmerking gegeven :createDiff', 'comment_updated' => 'Updatet :updateDiff door :username', 'comment_updated_indicator' => 'Bijgewerkt', - 'comment_deleted_success' => 'Reactie verwijderd', - 'comment_created_success' => 'Reactie toegevoegd', - 'comment_updated_success' => 'Reactie bijgewerkt', - 'comment_delete_confirm' => 'Weet je zeker dat je deze reactie wilt verwijderen?', + 'comment_deleted_success' => 'Opmerking verwijderd', + 'comment_created_success' => 'Opmerking toegevoegd', + 'comment_updated_success' => 'Opmerking bijgewerkt', + 'comment_archive_success' => 'Opmerking gearchiveerd', + 'comment_unarchive_success' => 'Opmerking teruggehaald', + 'comment_view' => 'Opmerking weergeven', + 'comment_jump_to_thread' => 'Ga naar thread', + 'comment_delete_confirm' => 'Weet je zeker dat je deze opmerking wilt verwijderen?', 'comment_in_reply_to' => 'Als antwoord op :commentId', + 'comment_reference' => 'Verwijzing', + 'comment_reference_outdated' => '(Verouderd)', 'comment_editor_explain' => 'Hier zijn de opmerkingen die zijn achtergelaten op deze pagina. Opmerkingen kunnen worden toegevoegd en beheerd wanneer u de opgeslagen pagina bekijkt.', // Revision - 'revision_delete_confirm' => 'Weet u zeker dat u deze revisie wilt verwijderen?', - 'revision_restore_confirm' => 'Weet u zeker dat u deze revisie wilt herstellen? De huidige pagina-inhoud wordt vervangen.', + 'revision_delete_confirm' => 'Weet je zeker dat je deze revisie wilt verwijderen?', + 'revision_restore_confirm' => 'Weet je zeker dat je deze revisie wilt herstellen? De huidige pagina-inhoud wordt vervangen.', 'revision_cannot_delete_latest' => 'Kan de laatste revisie niet verwijderen.', // Copy view @@ -396,7 +435,7 @@ // Conversions 'convert_to_shelf' => 'Converteer naar Boekenplank', - 'convert_to_shelf_contents_desc' => 'U kunt dit boek converteren naar een nieuwe boekenplank met dezelfde inhoud. Hoofdstukken in dit boek zullen worden geconverteerd naar nieuwe boeken. Als dit boek pagina\'s bevat, die niet in een hoofdstuk staan, zal dit boek een nieuwe naam krijgen en deze pagina\'s bevatten, en zal dit boek deel gaan uitmaken van de nieuwe boekenplank.', + 'convert_to_shelf_contents_desc' => 'Je kunt dit boek converteren naar een nieuwe boekenplank met dezelfde inhoud. Hoofdstukken in dit boek zullen worden geconverteerd naar nieuwe boeken. Als dit boek pagina\'s bevat, die niet in een hoofdstuk staan, zal dit boek een nieuwe naam krijgen en deze pagina\'s bevatten, en zal dit boek deel gaan uitmaken van de nieuwe boekenplank.', 'convert_to_shelf_permissions_desc' => 'Elke machtiging ingesteld op dit boek zal gekopieerd worden naar de nieuwe boekenplank en naar alle nieuwe onderliggende boeken die geen eigen machtiging hebben afgedwongen. Merk op dat boekenplank-machtigingen niet automatisch overdragen naar inhoud binnenin de boekenplank, zoals dat wel gebeurd bij boeken.', 'convert_book' => 'Converteer Boek', 'convert_book_confirm' => 'Weet je zeker dat je dit boek wil converteren?', @@ -409,7 +448,7 @@ // References 'references' => 'Verwijzingen', 'references_none' => 'Er zijn geen verwijzingen naar dit item bijgehouden.', - 'references_to_desc' => 'Hieronder is alle inhoud in het systeem dat naar dit item linkt vermeld.', + 'references_to_desc' => 'Hieronder staat alle bekende inhoud in het systeem die gekoppeld is aan dit item.', // Watch Options 'watch' => 'Volg', @@ -427,11 +466,11 @@ 'watch_desc_comments_page' => 'Geef een melding van pagina wijzigingen en nieuwe opmerkingen.', 'watch_change_default' => 'Standaardvoorkeuren voor meldingen wijzigen', 'watch_detail_ignore' => 'Meldingen negeren', - 'watch_detail_new' => 'Op de uitkijk voor nieuwe pagina\'s', - 'watch_detail_updates' => 'Op de uitkijk voor nieuwe pagina\'s en aanpassingen', - 'watch_detail_comments' => 'Op de uitkijk voor nieuwe pagina\'s, aanpassingen en opmerkingen', - 'watch_detail_parent_book' => 'Op de uitkijk via hogerliggend boek', - 'watch_detail_parent_book_ignore' => 'Aan het negeren via hogerliggend boek', - 'watch_detail_parent_chapter' => 'Op de uitkijk via hogerliggend hoofdstuk', - 'watch_detail_parent_chapter_ignore' => 'Aan het negeren via hogerliggend hoofdstuk', + 'watch_detail_new' => 'Nieuwe pagina\'s aan het volgen', + 'watch_detail_updates' => 'Nieuwe pagina\'s en aanpassingen aan het volgen', + 'watch_detail_comments' => 'Nieuwe pagina\'s, aanpassingen en opmerkingen aan het volgen', + 'watch_detail_parent_book' => 'Aan het volgen via bovenliggend boek', + 'watch_detail_parent_book_ignore' => 'Aan het negeren via bovenliggend boek', + 'watch_detail_parent_chapter' => 'Aan het volgen via bovenliggend hoofdstuk', + 'watch_detail_parent_chapter_ignore' => 'Aan het negeren via bovenliggend hoofdstuk', ]; diff --git a/lang/nl/errors.php b/lang/nl/errors.php index 3fa1d94f7e1..9829986a2a7 100644 --- a/lang/nl/errors.php +++ b/lang/nl/errors.php @@ -5,11 +5,12 @@ return [ // Permissions - 'permission' => 'U heeft geen machtiging om de gevraagde pagina te openen.', - 'permissionJson' => 'U heeft geen machtiging om de gevraagde actie uit te voeren.', + 'permission' => 'Je hebt geen machtiging om de gevraagde pagina te openen.', + 'permissionJson' => 'Je hebt geen machtiging om de gevraagde actie uit te voeren.', // Auth 'error_user_exists_different_creds' => 'Er bestaat al een gebruiker met het e-mailadres :email, maar met andere inloggegevens.', + 'auth_pre_register_theme_prevention' => 'Het gebruikersaccount kon niet worden geregistreerd met de opgegeven informatie', 'email_already_confirmed' => 'Het e-mailadres is al bevestigd, probeer in te loggen.', 'email_confirmation_invalid' => 'Deze bevestigingstoken is niet geldig of al gebruikt, probeer opnieuw te registreren.', 'email_confirmation_expired' => 'Het bevestigingstoken is verlopen, Er is een nieuwe bevestigingsmail verzonden.', @@ -23,7 +24,6 @@ 'saml_invalid_response_id' => 'Het verzoek van het externe authenticatiesysteem wordt niet herkend door een proces dat door deze applicatie wordt gestart. Terugkeren na inloggen kan dit probleem veroorzaken.', 'saml_fail_authed' => 'Inloggen met :system mislukt, het systeem gaf geen succesvolle autorisatie', 'oidc_already_logged_in' => 'Reeds ingelogd', - 'oidc_user_not_registered' => 'De gebruiker :name is niet geregistreerd en automatische registratie is uitgeschakeld', 'oidc_no_email_address' => 'In de gegevens van het externe verificatiesysteem kon voor deze gebruiker geen e-mailadres gevonden worden', 'oidc_fail_authed' => 'Inloggen met :system mislukt, systeem heeft geen succesvolle autorisatie gegeven', 'social_no_action_defined' => 'Geen actie gedefinieerd', @@ -36,7 +36,8 @@ 'social_account_register_instructions' => 'Als je nog geen account hebt, kun je je registreren met de :socialAccount optie.', 'social_driver_not_found' => 'Social driver niet gevonden', 'social_driver_not_configured' => 'Je :socialAccount instellingen zijn niet correct geconfigureerd.', - 'invite_token_expired' => 'Deze uitnodigingslink is verlopen. U kunt in plaats daarvan proberen uw wachtwoord opnieuw in te stellen.', + 'invite_token_expired' => 'Deze uitnodigingslink is verlopen. Je kunt in plaats daarvan proberen je wachtwoord te herstellen.', + 'login_user_not_found' => 'Er is geen gebruiker gevonden voor deze actie.', // System 'path_not_writable' => 'Bestandspad :filePath kon niet naar geüpload worden. Zorg dat je schrijfrechten op de server hebt.', @@ -53,7 +54,7 @@ 'image_upload_memory_limit' => 'Het uploaden van afbeeldingen en/of het maken van miniaturen is mislukt vanwege te beperkte systeemmiddelen.', 'image_thumbnail_memory_limit' => 'Het maken van variaties in afbeeldingsgrootte is mislukt vanwege te beperkte systeemmiddelen.', 'image_gallery_thumbnail_memory_limit' => 'Het maken van galerij miniaturen is mislukt vanwege te beperkte systeemmiddelen.', - 'drawing_data_not_found' => 'De gegevens van de tekening konden niet worden geladen. Het tekenbestand bestaat misschien niet meer of u hebt geen machtiging om het te openen.', + 'drawing_data_not_found' => 'De gegevens van de tekening konden niet worden geladen. Het tekenbestand bestaat misschien niet meer of je hebt geen machtiging om het te openen.', // Attachments 'attachment_not_found' => 'Bijlage niet gevonden', @@ -77,6 +78,7 @@ // Users 'users_cannot_delete_only_admin' => 'Je kunt niet het enige admin account verwijderen', 'users_cannot_delete_guest' => 'Je kunt het gastaccount niet verwijderen', + 'users_could_not_send_invite' => 'Kan de gebruiker niet aanmaken, uitnodigingsmail kon niet worden verzonden', // Roles 'role_cannot_be_edited' => 'Deze rol kan niet bewerkt worden', @@ -85,24 +87,36 @@ 'role_cannot_remove_only_admin' => 'Deze gebruiker is de enige gebruiker die is toegewezen aan de beheerdersrol. Wijs de beheerdersrol toe aan een andere gebruiker voordat u probeert deze hier te verwijderen.', // Comments - 'comment_list' => 'Er is een fout opgetreden tijdens het ophalen van de reacties.', - 'cannot_add_comment_to_draft' => 'U kunt geen reacties toevoegen aan een concept.', - 'comment_add' => 'Er is een fout opgetreden tijdens het aanpassen / toevoegen van de reactie.', - 'comment_delete' => 'Er is een fout opgetreden tijdens het verwijderen van de reactie.', - 'empty_comment' => 'Kan geen lege reactie toevoegen.', + 'comment_list' => 'Er is een fout opgetreden tijdens het ophalen van de opmerkingen.', + 'cannot_add_comment_to_draft' => 'Je kunt geen opmerkingen toevoegen aan een concept.', + 'comment_add' => 'Er is een fout opgetreden tijdens het aanpassen / toevoegen van de opmerking.', + 'comment_delete' => 'Er is een fout opgetreden tijdens het verwijderen van de opmerking.', + 'empty_comment' => 'Kan geen lege opmerking toevoegen.', // Error pages '404_page_not_found' => 'Pagina Niet Gevonden', 'sorry_page_not_found' => 'Sorry, de pagina die je zocht kan niet gevonden worden.', - 'sorry_page_not_found_permission_warning' => 'Als u verwachtte dat deze pagina zou bestaan, hebt u misschien geen machtiging om deze te bekijken.', + 'sorry_page_not_found_permission_warning' => 'Als je verwachtte dat deze pagina zou bestaan, heb je misschien geen machtiging om deze te bekijken.', 'image_not_found' => 'Afbeelding niet gevonden', 'image_not_found_subtitle' => 'Sorry, de afbeelding die je zocht is niet beschikbaar.', - 'image_not_found_details' => 'Als u verwachtte dat deze afbeelding zou bestaan, dan is deze misschien verwijderd.', + 'image_not_found_details' => 'Als je verwachtte dat deze afbeelding zou bestaan, dan is deze misschien verwijderd.', 'return_home' => 'Terug naar home', 'error_occurred' => 'Er Ging Iets Fout', 'app_down' => ':appName is nu niet beschikbaar', 'back_soon' => 'Komt snel weer online.', + // Import + 'import_zip_cant_read' => 'Kon het Zip-bestand niet lezen.', + 'import_zip_cant_decode_data' => 'Kon de data.json Zip-inhoud niet vinden of decoderen.', + 'import_zip_no_data' => 'Zip-bestand bevat niet de verwachte boek, hoofdstuk of pagina-inhoud.', + 'import_validation_failed' => 'De validatie van het Zip-bestand is mislukt met de volgende fouten:', + 'import_zip_failed_notification' => 'Importeren van het Zip-bestand is mislukt.', + 'import_perms_books' => 'Je mist de vereiste machtigingen om boeken te maken.', + 'import_perms_chapters' => 'Je mist de vereiste machtigingen om hoofdstukken te maken.', + 'import_perms_pages' => 'Je mist de vereiste machtigingen om pagina\'s te maken.', + 'import_perms_images' => 'Je mist de vereiste machtigingen om afbeeldingen toe te voegen.', + 'import_perms_attachments' => 'Je mist de vereiste machtigingen om bijlagen toe te voegen.', + // API errors 'api_no_authorization_found' => 'Geen autorisatie token gevonden', 'api_bad_authorization_format' => 'Een autorisatie token is gevonden, maar het formaat schijnt onjuist te zijn', diff --git a/lang/nl/notifications.php b/lang/nl/notifications.php index 79c7e7e8b73..1e7035670da 100644 --- a/lang/nl/notifications.php +++ b/lang/nl/notifications.php @@ -5,7 +5,7 @@ return [ 'new_comment_subject' => 'Nieuwe opmerking op pagina: :pageName', - 'new_comment_intro' => 'Een gebruiker heeft gereageerd op een pagina in :appName:', + 'new_comment_intro' => 'Een gebruiker heeft een opmerking geplaatst op een pagina in :appName:', 'new_page_subject' => 'Nieuwe pagina: :pageName', 'new_page_intro' => 'Een nieuwe pagina is gemaakt in :appName:', 'updated_page_subject' => 'Aangepaste pagina: :pageName', @@ -14,7 +14,7 @@ 'detail_page_name' => 'Pagina Naam:', 'detail_page_path' => 'Paginapad:', - 'detail_commenter' => 'Reageerder:', + 'detail_commenter' => 'Commentator:', 'detail_comment' => 'Opmerking:', 'detail_created_by' => 'Gemaakt Door:', 'detail_updated_by' => 'Aangepast Door:', @@ -22,6 +22,6 @@ 'action_view_comment' => 'Bekijk Opmerking', 'action_view_page' => 'Bekijk Pagina', - 'footer_reason' => 'Deze melding is naar u verzonden omdat :link dit type activiteit voor dit artikel dekt.', + 'footer_reason' => 'Deze melding is naar je verzonden omdat :link dit type activiteit voor dit artikel dekt.', 'footer_reason_link' => 'je meldingsvoorkeuren', ]; diff --git a/lang/nl/passwords.php b/lang/nl/passwords.php index 1f1d71925f8..5223bdf39ee 100644 --- a/lang/nl/passwords.php +++ b/lang/nl/passwords.php @@ -6,9 +6,9 @@ */ return [ - 'password' => 'Wachtwoorden moeten uit ten minste acht tekens bestaan en overeenkomen met de bevestiging.', - 'user' => "We kunnen niemand vinden met dat e-mailadres.", - 'token' => 'Het wachtwoord reset token is ongeldig voor dit e-mailadres.', + 'password' => 'Wachtwoorden moeten uit minstens acht tekens bestaan en overeenkomen met de bevestiging.', + 'user' => "We kunnen geen gebruiker vinden met dat e-mailadres.", + 'token' => 'De wachtwoordhersteltoken is ongeldig voor dit e-mailadres.', 'sent' => 'We hebben je een link gestuurd om je wachtwoord te herstellen!', 'reset' => 'Je wachtwoord is hersteld!', diff --git a/lang/nl/preferences.php b/lang/nl/preferences.php index 41ed3c547f7..d0e3623b9cc 100644 --- a/lang/nl/preferences.php +++ b/lang/nl/preferences.php @@ -9,8 +9,8 @@ 'shortcuts' => 'Snelkoppelingen', 'shortcuts_interface' => 'Snelkoppelingen', - 'shortcuts_toggle_desc' => 'Hier kunt u toetscombinaties voor de gebruikersinterface in- of uitschakelen voor navigatie en acties.', - 'shortcuts_customize_desc' => 'U kunt elk van de onderstaande toetsencombinaties aanpassen. Druk simpelweg op de gewenste toetscombinatie na het selecteren van de invoer voor een toetscombinatie.', + 'shortcuts_toggle_desc' => 'Hier kun je toetscombinaties voor de gebruikersinterface in- of uitschakelen voor navigatie en acties.', + 'shortcuts_customize_desc' => 'Je kunt elk van de onderstaande toetsencombinaties aanpassen. Druk simpelweg op de gewenste toetscombinatie na het selecteren van de invoer voor een toetscombinatie.', 'shortcuts_toggle_label' => 'Toetsencombinaties ingeschakeld', 'shortcuts_section_navigation' => 'Navigatie', 'shortcuts_section_actions' => 'Gebruikelijke acties', @@ -27,11 +27,11 @@ 'notifications_save' => 'Voorkeuren opslaan', 'notifications_update_success' => 'Voorkeuren voor meldingen zijn bijgewerkt!', 'notifications_watched' => 'Gevolgde & Genegeerde Items', - 'notifications_watched_desc' => ' Hieronder staan de items waarvoor aangepaste \'Volg\'-voorkeuren zijn toegepast. Om je voorkeuren voor deze items bij te werken, bekijk je het item en zoek je naar de \'Volg\' opties in de zijbalk.', + 'notifications_watched_desc' => 'Hieronder staan de items waarvoor aangepaste volgvoorkeuren zijn toegepast. Om je voorkeuren voor deze items bij te werken, bekijk je het item en zoek je naar de volgopties in de zijbalk.', 'auth' => 'Toegang & Beveiliging', 'auth_change_password' => 'Wachtwoord Wijzigen', - 'auth_change_password_desc' => 'Wijzig hier je wachtwoord om in te loggen op de applicatie. Minstens 8 tekens gebruiken.', + 'auth_change_password_desc' => 'Wijzig hier je wachtwoord die je gebruikt om in te loggen op de applicatie. Gebruik minimaal 8 tekens.', 'auth_change_password_success' => 'Wachtwoord is bijgewerkt!', 'profile' => 'Profielgegevens', diff --git a/lang/nl/settings.php b/lang/nl/settings.php index 014153e536c..3b4da75a985 100644 --- a/lang/nl/settings.php +++ b/lang/nl/settings.php @@ -19,7 +19,7 @@ 'app_name_desc' => 'Deze naam wordt getoond in de header en in alle door het systeem verstuurde e-mails.', 'app_name_header' => 'Toon naam in header', 'app_public_access' => 'Openbare toegang', - 'app_public_access_desc' => 'Door deze optie in te schakelen kunnen bezoekers, die niet ingelogd zijn, toegang krijgen tot de inhoud van uw BookStack omgeving.', + 'app_public_access_desc' => 'Door deze optie in te schakelen kunnen bezoekers die niet ingelogd zijn toegang krijgen tot de inhoud van uw BookStack-omgeving.', 'app_public_access_desc_guest' => 'De toegang voor publieke bezoekers kan worden ingesteld via de "Guest" gebruiker.', 'app_public_access_toggle' => 'Openbare toegang toestaan', 'app_public_viewing' => 'Publieke bezichtigingen toestaan?', @@ -28,24 +28,24 @@ 'app_secure_images_desc' => 'Om prestatieredenen zijn alle afbeeldingen openbaar. Deze optie voegt een willekeurige en moeilijk te raden tekst toe aan de URL\'s van de afbeeldingen. Zorg ervoor dat "directory indexes" niet ingeschakeld zijn om eenvoudige toegang te voorkomen.', 'app_default_editor' => 'Standaard Pagina Bewerker', 'app_default_editor_desc' => 'Selecteer welke bewerker standaard zal worden gebruikt bij het bewerken van nieuwe pagina\'s. Dit kan worden overschreven op paginaniveau als de rechten dat toestaan.', - 'app_custom_html' => 'HTML aan toevoegen', + 'app_custom_html' => 'Aangepaste inhoud voor het HTML head-element', 'app_custom_html_desc' => 'Alle hieronder toegevoegde data wordt aan het einde van de sectie van elke pagina toegevoegd. Gebruik dit om stijlen te overschrijven of analytische code toe te voegen.', 'app_custom_html_disabled_notice' => 'Bovenstaande wordt niet toegevoegd aan deze pagina om ervoor te zorgen dat je foutieve code steeds ongedaan kan maken.', 'app_logo' => 'Applicatielogo', 'app_logo_desc' => 'Dit wordt onder meer gebruikt in de kopbalk van de applicatie. Deze afbeelding dient 86px hoog te zijn. Grote afbeeldingen worden teruggeschaald.', - 'app_icon' => 'Applicatie Pictogram', + 'app_icon' => 'Applicatiepictogram', 'app_icon_desc' => 'Dit pictogram wordt gebruikt voor browsertabbladen en snelkoppelingspictogrammen. Dit dient een 256px vierkante PNG-afbeelding te zijn.', - 'app_homepage' => 'Applicatie Startpagina', + 'app_homepage' => 'Applicatie startpagina', 'app_homepage_desc' => 'Selecteer een weergave om weer te geven op de startpagina in plaats van de standaard weergave. Paginamachtigingen worden genegeerd voor geselecteerde pagina\'s.', 'app_homepage_select' => 'Selecteer een pagina', 'app_footer_links' => 'Voettekst hyperlinks', - 'app_footer_links_desc' => 'Voeg hyperlinks toe aan de voettekst van de applicatie. Deze zullen onderaan de meeste pagina\'s getoond worden, ook aan pagina\'s die geen login vereisen. U kunt een label van "trans::" gebruiken om systeem-gedefinieerde vertalingen te gebruiken. Bijvoorbeeld: Het gebruik van "trans::common.privacy_policy" zal de vertaalde tekst "Privacy Policy" opleveren en "trans::common.terms_of_service" zal de vertaalde tekst "Gebruiksvoorwaarden" opleveren.', + 'app_footer_links_desc' => 'Voeg hyperlinks toe aan de voettekst van de applicatie. Deze zullen onderaan de meeste pagina\'s getoond worden, ook aan pagina\'s die geen login vereisen. Je kunt een label van "trans::" gebruiken om systeem-gedefinieerde vertalingen te gebruiken. Bijvoorbeeld: Het gebruik van "trans::common.privacy_policy" zal de vertaalde tekst "Privacy Policy" opleveren en "trans::common.terms_of_service" zal de vertaalde tekst "Gebruiksvoorwaarden" opleveren.', 'app_footer_links_label' => 'Link label', 'app_footer_links_url' => 'Link URL', 'app_footer_links_add' => 'Voettekst link toevoegen', - 'app_disable_comments' => 'Reacties uitschakelen', - 'app_disable_comments_toggle' => 'Reacties uitschakelen', - 'app_disable_comments_desc' => 'Schakel reacties uit op alle pagina\'s in de applicatie.
Bestaande reacties worden niet getoond.', + 'app_disable_comments' => 'Opmerkingen uitschakelen', + 'app_disable_comments_toggle' => 'Opmerkingen uitschakelen', + 'app_disable_comments_desc' => 'Schakel opmerkingen uit op alle pagina\'s in de applicatie.
Bestaande opmerkingen worden niet getoond.', // Color settings 'color_scheme' => 'Kleurenschema van applicatie', @@ -74,13 +74,43 @@ 'reg_confirm_restrict_domain_desc' => 'Geef een door komma-gescheiden lijst van domeinnamen op die gebruikt mogen worden bij registratie. Gebruikers dienen de ontvangen e-mail te bevestigen voordat ze toegang krijgen tot de applicatie.
Let op: Gebruikers kunnen na registratie hun e-mailadres nog steeds wijzigen.', 'reg_confirm_restrict_domain_placeholder' => 'Geen beperkingen ingesteld', + // Sorting Settings + 'sorting' => 'Sorteren', + 'sorting_book_default' => 'Standaard Sorteerregel Boek', + 'sorting_book_default_desc' => 'Selecteer de standaard sorteerregel om toe te passen op nieuwe boeken. Dit heeft geen invloed op bestaande boeken, en kan per boek worden overschreven.', + 'sorting_rules' => 'Sorteerregels', + 'sorting_rules_desc' => 'Dit zijn vooraf ingestelde sorteeroperaties die kunnen worden toegepast op inhoud in het systeem.', + 'sort_rule_assigned_to_x_books' => 'Toegepast op :count Boek|Toegepast op :count Boeken', + 'sort_rule_create' => 'Maak sorteerregel', + 'sort_rule_edit' => 'Bewerk sorteerregel', + 'sort_rule_delete' => 'Verwijder sorteerregel', + 'sort_rule_delete_desc' => 'Verwijder deze sorteerregel uit het systeem. Boeken die deze regel gebruiken zullen terugvallen op handmatige sortering.', + 'sort_rule_delete_warn_books' => 'Deze sorteerregel wordt momenteel gebruikt door :count boek(en). Weet je zeker dat je deze wilt verwijderen?', + 'sort_rule_delete_warn_default' => 'Deze sorteerregel wordt gebruikt als standaardregel voor boeken. Weet je zeker dat je deze wilt verwijderen?', + 'sort_rule_details' => 'Details sorteerregel', + 'sort_rule_details_desc' => 'Stel een naam in voor deze sorteerregel. Deze wordt weergegeven waar gebruikers een regel kunnen selecteren.', + 'sort_rule_operations' => 'Sorteeroperaties', + 'sort_rule_operations_desc' => 'Configureer de sorteeracties die moeten worden uitgevoerd door ze te verplaatsen van de lijst met beschikbare operaties. Bij gebruik worden de operaties toegepast in volgorde van boven naar beneden. Wijzigingen die hier worden gemaakt worden toegepast op alle toegewezen boeken.', + 'sort_rule_available_operations' => 'Beschikbare Operaties', + 'sort_rule_available_operations_empty' => 'Geen operaties over', + 'sort_rule_configured_operations' => 'Ingestelde Operaties', + 'sort_rule_configured_operations_empty' => 'Voeg operaties toe uit de "Beschikbare Operaties" lijst', + 'sort_rule_op_asc' => '(Oplopend)', + 'sort_rule_op_desc' => '(Aflopend)', + 'sort_rule_op_name' => 'Naam - Alfabetisch', + 'sort_rule_op_name_numeric' => 'Naam - Numeriek', + 'sort_rule_op_created_date' => 'Aanmaakdatum', + 'sort_rule_op_updated_date' => 'Bijwerkdatum', + 'sort_rule_op_chapters_first' => 'Hoofdstukken Eerst', + 'sort_rule_op_chapters_last' => 'Hoofdstukken Laatst', + // Maintenance settings 'maint' => 'Onderhoud', 'maint_image_cleanup' => 'Afbeeldingen opschonen', - 'maint_image_cleanup_desc' => 'Scant pagina- en revisie inhoud om te controleren welke afbeeldingen en tekeningen momenteel worden gebruikt en welke afbeeldingen overbodig zijn. Zorg ervoor dat je een volledige database- en afbeelding back-up maakt voordat je dit uitvoert.', - 'maint_delete_images_only_in_revisions' => 'Ook afbeeldingen verwijderen die alleen in oude pagina revisies bestaan', + 'maint_image_cleanup_desc' => 'Scant pagina- en revisieinhoud om te controleren welke afbeeldingen en tekeningen momenteel worden gebruikt en welke afbeeldingen overbodig zijn. Zorg ervoor dat je een volledige database- en afbeelding back-up maakt voordat je dit uitvoert.', + 'maint_delete_images_only_in_revisions' => 'Ook afbeeldingen verwijderen die alleen in oude paginarevisies bestaan', 'maint_image_cleanup_run' => 'Opschonen uitvoeren', - 'maint_image_cleanup_warning' => ':count potentieel ongebruikte afbeeldingen gevonden. Weet u zeker dat u deze afbeeldingen wilt verwijderen?', + 'maint_image_cleanup_warning' => ':count potentieel ongebruikte afbeeldingen gevonden. Weet je zeker dat je deze afbeeldingen wilt verwijderen?', 'maint_image_cleanup_success' => ':count potentieel ongebruikte afbeeldingen gevonden en verwijderd!', 'maint_image_cleanup_nothing_found' => 'Geen ongebruikte afbeeldingen gevonden, niets verwijderd!', 'maint_send_test_email' => 'Stuur een test e-mail', @@ -94,12 +124,12 @@ 'maint_recycle_bin_open' => 'Prullenbak openen', 'maint_regen_references' => 'Verwijzingen opnieuw genereren', 'maint_regen_references_desc' => 'Deze actie zal de kruisverwijzingen index binnen de database opnieuw opbouwen. Dit wordt doorgaans automatisch gedaan, maar deze actie kan nuttig zijn om oude inhoud of inhoud die via onofficiële methoden is toegevoegd te indexeren.', - 'maint_regen_references_success' => 'Verwijzingen index is opnieuw gegenereerd!', - 'maint_timeout_command_note' => 'Opmerking: Het uitvoeren van deze actie kan enige tijd in beslag nemen, wat in sommige webomgevingen kan leiden tot time-outs. Als alternatief kan deze actie ook worden uitgevoerd met een terminal commando.', + 'maint_regen_references_success' => 'Verwijzingenindex is opnieuw gegenereerd!', + 'maint_timeout_command_note' => 'Let op: Het uitvoeren van deze actie kan enige tijd in beslag nemen, wat in sommige webomgevingen kan leiden tot time-outs. Als alternatief kan deze actie ook worden uitgevoerd met een terminal-commando.', // Recycle Bin 'recycle_bin' => 'Prullenbak', - 'recycle_bin_desc' => 'Hier kunt u items herstellen die zijn verwijderd of ervoor kiezen om ze permanent uit het systeem te verwijderen. Deze lijst is ongefilterd, in tegenstelling tot vergelijkbare activiteitenlijsten in het systeem waar machtigingenfilters worden toegepast.', + 'recycle_bin_desc' => 'Hier kun je items herstellen die zijn verwijderd of ervoor kiezen om ze permanent uit het systeem te verwijderen. Deze lijst is ongefilterd, in tegenstelling tot vergelijkbare activiteitenlijsten in het systeem waar machtigingenfilters worden toegepast.', 'recycle_bin_deleted_item' => 'Verwijderde Item', 'recycle_bin_deleted_parent' => 'Bovenliggende', 'recycle_bin_deleted_by' => 'Verwijderd door', @@ -108,8 +138,8 @@ 'recycle_bin_restore' => 'Herstellen', 'recycle_bin_contents_empty' => 'De prullenbak is momenteel leeg', 'recycle_bin_empty' => 'Prullenbak legen', - 'recycle_bin_empty_confirm' => 'Dit zal permanent alle items in de prullenbak vernietigen, inclusief de inhoud die in elk item zit. Weet u zeker dat u de prullenbak wilt legen?', - 'recycle_bin_destroy_confirm' => 'Deze actie zal dit item permanent uit het systeem verwijderen, samen met alle onderliggende elementen, en u kunt deze inhoud niet herstellen. Weet u zeker dat u dit item permanent wil verwijderen?', + 'recycle_bin_empty_confirm' => 'Dit zal permanent alle items in de prullenbak vernietigen, inclusief de inhoud die in elk item zit. Weet je zeker dat je de prullenbak wil legen?', + 'recycle_bin_destroy_confirm' => 'Deze actie zal dit item permanent verwijderen uit het systeem, samen met onderstaande onderliggende elementen, en u zal deze inhoud niet kunnen herstellen. Bent u zeker dat u dit item permanent wilt verwijderen?', 'recycle_bin_destroy_list' => 'Te vernietigen items', 'recycle_bin_restore_list' => 'Items te herstellen', 'recycle_bin_restore_confirm' => 'Deze actie herstelt het verwijderde item, inclusief alle onderliggende elementen, op hun oorspronkelijke locatie. Als de oorspronkelijke locatie sindsdien is verwijderd en zich nu in de prullenbak bevindt, zal ook het bovenliggende item moeten worden hersteld.', @@ -155,13 +185,14 @@ 'role_external_auth_id' => 'Externe authenticatie ID\'s', 'role_system' => 'Systeem Machtigingen', 'role_manage_users' => 'Gebruikers beheren', - 'role_manage_roles' => 'Beheer rollen & rolmachtigingen', + 'role_manage_roles' => 'Beheer rollen & machtigingen', 'role_manage_entity_permissions' => 'Beheer alle machtigingen voor boeken, hoofdstukken en pagina\'s', 'role_manage_own_entity_permissions' => 'Beheer machtigingen van je eigen boek, hoofdstuk & pagina\'s', 'role_manage_page_templates' => 'Paginasjablonen beheren', 'role_access_api' => 'Ga naar systeem API', 'role_manage_settings' => 'Beheer app instellingen', 'role_export_content' => 'Exporteer inhoud', + 'role_import_content' => 'Importeer inhoud', 'role_editor_change' => 'Wijzig pagina bewerker', 'role_notifications' => 'Meldingen ontvangen & beheren', 'role_asset' => 'Asset Machtigingen', @@ -190,7 +221,7 @@ 'users_role_desc' => 'Selecteer aan welke rollen deze gebruiker zal worden toegewezen. Als een gebruiker aan meerdere rollen wordt toegewezen, worden de machtigingen van die rollen samengevoegd en krijgt hij alle mogelijkheden van de toegewezen rollen.', 'users_password' => 'Wachtwoord gebruiker', 'users_password_desc' => 'Stel een wachtwoord in om op de applicatie in te loggen. Dit moet minstens 8 tekens lang zijn.', - 'users_send_invite_text' => 'U kunt ervoor kiezen om deze gebruiker een uitnodigingsmail te sturen waarmee hij zijn eigen wachtwoord kan instellen, anders kunt u zelf zijn wachtwoord instellen.', + 'users_send_invite_text' => 'Je kunt ervoor kiezen om deze gebruiker een uitnodigingsmail te sturen waarmee hij zijn eigen wachtwoord kan instellen, anders kun je zelf zijn wachtwoord instellen.', 'users_send_invite_option' => 'Stuur gebruiker uitnodigings e-mail', 'users_external_auth_id' => 'Externe authenticatie ID', 'users_external_auth_id_desc' => 'Wanneer een extern authenticatiesysteem wordt gebruikt (zoals SAML2, OIDC of LDAP) is dit de ID die deze BookStack-gebruiker koppelt aan het account van het authenticatiesysteem. Je kunt dit veld negeren als je de standaard op e-mail gebaseerde verificatie gebruikt.', @@ -208,10 +239,10 @@ 'users_avatar' => 'Avatar', 'users_avatar_desc' => 'Selecteer een afbeelding om deze gebruiker voor te stellen. Deze moet ongeveer 256px breed en vierkant zijn.', 'users_preferred_language' => 'Voorkeurstaal', - 'users_preferred_language_desc' => 'Deze optie wijzigt de taal die gebruikt wordt voor de gebruikersinterface. Dit heeft geen invloed op door gebruiker gemaakte inhoud.', + 'users_preferred_language_desc' => 'Deze optie wijzigt de taal die gebruikt wordt voor de gebruikersinterface. Dit heeft geen invloed op door gebruikers gemaakte inhoud.', 'users_social_accounts' => 'Sociale media accounts', 'users_social_accounts_desc' => 'Bekijk de status van de verbonden socialmedia-accounts voor deze gebruiker. socialmedia-accounts kunnen worden gebruikt naast het primaire authenticatiesysteem voor systeemtoegang.', - 'users_social_accounts_info' => 'Hier kunt u uw andere accounts koppelen om sneller en eenvoudiger in te loggen. Als u hier een account loskoppelt, wordt de eerder gemachtigde toegang niet ingetrokken. U kunt de toegang intrekken via uw profielinstellingen op het gekoppelde socialemedia-account zelf.', + 'users_social_accounts_info' => 'Hier kun je je andere accounts koppelen om sneller en eenvoudiger in te loggen. Als je hier een account loskoppelt, wordt de eerder gemachtigde toegang niet ingetrokken. Je kunt de toegang intrekken via je profielinstellingen op het gekoppelde socialemedia-account zelf.', 'users_social_connect' => 'Account Verbinden', 'users_social_disconnect' => 'Account Ontkoppelen', 'users_social_status_connected' => 'Verbonden', @@ -225,7 +256,7 @@ 'users_api_tokens_expires' => 'Verloopt', 'users_api_tokens_docs' => 'API-Documentatie', 'users_mfa' => 'Meervoudige Verificatie', - 'users_mfa_desc' => 'Stel meervoudige verificatie in als extra beveiligingslaag voor uw gebruikersaccount.', + 'users_mfa_desc' => 'Stel meervoudige verificatie in als extra beveiligingslaag voor je gebruikersaccount.', 'users_mfa_x_methods' => ':count methode geconfigureerd|:count methoden geconfigureerd', 'users_mfa_configure' => 'Configureer methoden', @@ -245,7 +276,7 @@ 'user_api_token_updated' => 'Token :timeAgo geleden bijgewerkt', 'user_api_token_delete' => 'Token Verwijderen', 'user_api_token_delete_warning' => 'Dit zal de API-token met de naam \':tokenName\' volledig uit het systeem verwijderen.', - 'user_api_token_delete_confirm' => 'Weet u zeker dat u deze API-token wilt verwijderen?', + 'user_api_token_delete_confirm' => 'Weet je zeker dat je deze API-token wilt verwijderen?', // Webhooks 'webhooks' => 'Webhooks', @@ -268,7 +299,7 @@ 'webhook_events_table_header' => 'Gebeurtenissen', 'webhooks_delete' => 'Verwijder Webhook', 'webhooks_delete_warning' => 'Dit zal de webhook met naam \':webhookName\' volledig verwijderen van het systeem.', - 'webhooks_delete_confirm' => 'Weet u zeker dat u deze webhook wil verwijderen?', + 'webhooks_delete_confirm' => 'Weet je zeker dat je deze webhook wilt verwijderen?', 'webhooks_format_example' => 'Voorbeeld Webhook Formaat', 'webhooks_format_example_desc' => 'Webhook gegevens worden verzonden als een POST verzoek naar het geconfigureerde eindpunt als JSON volgens het onderstaande formaat. De "related_item" en "url" eigenschappen zijn optioneel en hangen af van het type gebeurtenis die geactiveerd wordt.', 'webhooks_status' => 'Webhook Status', @@ -276,6 +307,14 @@ 'webhooks_last_errored' => 'Laatst Gefaald:', 'webhooks_last_error_message' => 'Laatste Foutmelding:', + // Licensing + 'licenses' => 'Licenties', + 'licenses_desc' => 'Deze pagina beschrijft licentie-informatie voor BookStack naast de projecten & bibliotheken die binnen BookStack worden gebruikt. Veel van de vermelde projecten worden alleen in een ontwikkelingscontext gebruikt.', + 'licenses_bookstack' => 'BookStack Licentie', + 'licenses_php' => 'PHP Bibliotheek Licenties', + 'licenses_js' => 'JavaScript Bibliotheek Licenties', + 'licenses_other' => 'Andere Licenties', + 'license_details' => 'Licentie Details', //! If editing translations files directly please ignore this in all //! languages apart from en. Content will be auto-copied from en. @@ -287,6 +326,7 @@ 'bs' => 'Bosanski (Bosnisch)', 'ca' => 'Català (Catalaans)', 'cs' => 'Česky (Tsjechisch)', + 'cy' => 'Cymraeg', 'da' => 'Dansk (Deens)', 'de' => 'Deutsch (Duits)', 'de_informal' => 'Deutsch (Du) (Informeel Duits)', @@ -308,6 +348,7 @@ 'lt' => 'Lietuvių Kalba (Litouws)', 'lv' => 'Latviešu Valoda (Lets)', 'nb' => 'Norsk (Bokmål) (Noors)', + 'ne' => 'नेपाली', 'nn' => 'Nynorsk', 'nl' => 'Nederlands', 'pl' => 'Polski (Pools)', diff --git a/lang/nl/validation.php b/lang/nl/validation.php index 378ec6a9158..7c7e20c8bfc 100644 --- a/lang/nl/validation.php +++ b/lang/nl/validation.php @@ -105,6 +105,11 @@ 'url' => ':attribute formaat is ongeldig.', 'uploaded' => 'Het bestand kon niet worden geüpload. De server accepteert mogelijk geen bestanden van deze grootte.', + 'zip_file' => 'Het \':attribute\' veld moet verwijzen naar een bestand in de ZIP.', + 'zip_file_mime' => 'Het \':attribute\' veld moet verwijzen naar een bestand met het type :validTypes, vond :foundType.', + 'zip_model_expected' => 'Dataobject verwacht maar vond ":type".', + 'zip_unique' => ':attribute moet uniek zijn voor het objecttype binnen de ZIP.', + // Custom validation lines 'custom' => [ 'password-confirm' => [ diff --git a/lang/nn/activities.php b/lang/nn/activities.php index dfb7043c40f..f87aadc6677 100644 --- a/lang/nn/activities.php +++ b/lang/nn/activities.php @@ -84,6 +84,14 @@ 'webhook_delete' => 'sletta webhook', 'webhook_delete_notification' => 'Webhook vart sletta', + // Imports + 'import_create' => 'created import', + 'import_create_notification' => 'Import successfully uploaded', + 'import_run' => 'updated import', + 'import_run_notification' => 'Content successfully imported', + 'import_delete' => 'deleted import', + 'import_delete_notification' => 'Import successfully deleted', + // Users 'user_create' => 'oppretta brukar', 'user_create_notification' => 'Brukar vart oppretta', @@ -93,7 +101,7 @@ 'user_delete_notification' => 'Brukaren vart fjerna', // API Tokens - 'api_token_create' => 'oppretta api token', + 'api_token_create' => 'opprett API-nøkkel', 'api_token_create_notification' => 'API-token er oppretta', 'api_token_update' => 'oppdatert api token', 'api_token_update_notification' => 'API-token oppdatert', @@ -119,6 +127,14 @@ 'comment_update' => 'oppdatert kommentar', 'comment_delete' => 'sletta kommentar', + // Sort Rules + 'sort_rule_create' => 'created sort rule', + 'sort_rule_create_notification' => 'Sort rule successfully created', + 'sort_rule_update' => 'updated sort rule', + 'sort_rule_update_notification' => 'Sort rule successfully updated', + 'sort_rule_delete' => 'deleted sort rule', + 'sort_rule_delete_notification' => 'Sort rule successfully deleted', + // Other 'permissions_update' => 'oppdaterte tilgangar', ]; diff --git a/lang/nn/auth.php b/lang/nn/auth.php index a6ae5917aec..5da686eaa9e 100644 --- a/lang/nn/auth.php +++ b/lang/nn/auth.php @@ -91,7 +91,7 @@ 'mfa_option_totp_title' => 'Mobilapplikasjon', 'mfa_option_totp_desc' => 'For å bruka fleirfaktorautentisering treng du ein mobilapplikasjon som støttar TOTP-teknologien, slik som Google Authenticator, Authy eller Microsoft Authenticator.', 'mfa_option_backup_codes_title' => 'Tryggleikskodar', - 'mfa_option_backup_codes_desc' => 'Lagre tryggleiksskodar til eingongsbruk på ein trygg stad, desse kan du bruka for å verifisera identiteten din.', + 'mfa_option_backup_codes_desc' => 'Genererer et sett med engangs sikkerhetskoder som du skal logge inn for å bekrefte identiteten din. Sørg for å lagre disse på et sikkert og sikkert sted.', 'mfa_gen_confirm_and_enable' => 'Stadfest og aktiver', 'mfa_gen_backup_codes_title' => 'Konfigurasjon av tryggleikskodar', 'mfa_gen_backup_codes_desc' => 'Lagre lista under med kodar på ein trygg stad. Når du skal ha tilgang til systemet kan du bruka ein av desse som ein faktor under innlogging.', diff --git a/lang/nn/common.php b/lang/nn/common.php index 74b39efa2bb..4247563e0fb 100644 --- a/lang/nn/common.php +++ b/lang/nn/common.php @@ -20,7 +20,7 @@ 'description' => 'Skildring', 'role' => 'Rolle', 'cover_image' => 'Framside', - 'cover_image_description' => 'Bilete bør vere ca. 440x250px.', + 'cover_image_description' => 'This image should be approximately 440x250px although it will be flexibly scaled & cropped to fit the user interface in different scenarios as required, so actual dimensions for display will differ.', // Actions 'actions' => 'Handlingar', @@ -30,6 +30,8 @@ 'create' => 'Opprett', 'update' => 'Oppdater', 'edit' => 'Rediger', + 'archive' => 'Archive', + 'unarchive' => 'Un-Archive', 'sort' => 'Sortér', 'move' => 'Flytt', 'copy' => 'Kopier', @@ -107,4 +109,7 @@ // Not directly used but available for convenience to users. 'privacy_policy' => 'Personvernreglar', 'terms_of_service' => 'Bruksvilkår', + + // OpenSearch + 'opensearch_description' => 'Search :appName', ]; diff --git a/lang/nn/editor.php b/lang/nn/editor.php index 4eef9d28aa8..6128223a91d 100644 --- a/lang/nn/editor.php +++ b/lang/nn/editor.php @@ -13,6 +13,7 @@ 'cancel' => 'Avbryt', 'save' => 'Lagre', 'close' => 'Lukk', + 'apply' => 'Apply', 'undo' => 'Angre', 'redo' => 'Gjer om', 'left' => 'Venstre', @@ -47,6 +48,7 @@ 'superscript' => 'Heva skrift', 'subscript' => 'Senka skrift', 'text_color' => 'Tekstfarge', + 'highlight_color' => 'Highlight color', 'custom_color' => 'Eigenvalgt farge', 'remove_color' => 'Fjern farge', 'background_color' => 'Bakgrunnsfarge', @@ -81,6 +83,9 @@ 'table_properties' => 'Tabellegenskaper', 'table_properties_title' => 'Tabellegenskaper', 'delete_table' => 'Slett tabell', + 'table_clear_formatting' => 'Clear table formatting', + 'resize_to_contents' => 'Resize to contents', + 'row_header' => 'Row header', 'insert_row_before' => 'Sett inn rad før', 'insert_row_after' => 'Sett inn rad etter', 'delete_row' => 'Slett rad', @@ -144,6 +149,7 @@ 'url' => 'Nettlenke', 'text_to_display' => 'Synlig tekst', 'title' => 'Tittel', + 'browse_links' => 'Browse links', 'open_link' => 'Åpne lenke', 'open_link_in' => 'Åpne i ...', 'open_link_current' => 'Samme vindu', @@ -160,6 +166,8 @@ 'about' => 'Om tekstredigeringsprogrammet', 'about_title' => 'Om HDSEHDF-tekstredigeringsprogrammet', 'editor_license' => 'Tekstbehandlerlisens og opphavsrett', + 'editor_lexical_license' => 'This editor is built as a fork of :lexicalLink which is distributed under the MIT license.', + 'editor_lexical_license_link' => 'Full license details can be found here.', 'editor_tiny_license' => 'Denne tekstredigereren er laget med :tinyLink som er lisensiert under MIT.', 'editor_tiny_license_link' => 'Informasjon om opphavsrett og lisens for TinyMCE finnes her.', 'save_continue' => 'Lagre side og fortsett', diff --git a/lang/nn/entities.php b/lang/nn/entities.php index 21ab84310b2..4c75d501986 100644 --- a/lang/nn/entities.php +++ b/lang/nn/entities.php @@ -39,6 +39,34 @@ 'export_pdf' => 'PDF-fil', 'export_text' => 'Tekstfil', 'export_md' => 'Markdownfil', + 'export_zip' => 'Portable ZIP', + 'default_template' => 'Default Page Template', + 'default_template_explain' => 'Assign a page template that will be used as the default content for all pages created within this item. Keep in mind this will only be used if the page creator has view access to the chosen template page.', + 'default_template_select' => 'Select a template page', + 'import' => 'Import', + 'import_validate' => 'Validate Import', + 'import_desc' => 'Import books, chapters & pages using a portable zip export from the same, or a different, instance. Select a ZIP file to proceed. After the file has been uploaded and validated you\'ll be able to configure & confirm the import in the next view.', + '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_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_size' => ':size Import ZIP Size', + 'import_uploaded_at' => 'Uploaded :relativeTime', + 'import_uploaded_by' => 'Uploaded by', + 'import_location' => 'Import Location', + '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_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', + 'breadcrumb_siblings_for_book' => 'Navigate siblings for book', + 'breadcrumb_siblings_for_bookshelf' => 'Navigate siblings for shelf', // Permissions and restrictions 'permissions' => 'Tilgongar', @@ -132,9 +160,6 @@ 'books_edit_named' => 'Endre boken :bookName', 'books_form_book_name' => 'Boktittel', 'books_save' => 'Lagre bok', - 'books_default_template' => 'Standard sidemal', - 'books_default_template_explain' => 'Tildel ein sidemal som vil bli brukt som standardinnhold for alle nye sider i denne boka. Hugs at dette berre vil bli brukt om sideskaperen har tilgang til den valgte malsida.', - 'books_default_template_select' => 'Velg ei malside', 'books_permissions' => 'Boktilganger', 'books_permissions_updated' => 'Boktilganger oppdatert', 'books_empty_contents' => 'Ingen sider eller kapittel finst i denne boka.', @@ -145,7 +170,9 @@ 'books_search_this' => 'Søk i boka', 'books_navigation' => 'Boknavigasjon', 'books_sort' => 'Sorter bokinnhald', - 'books_sort_desc' => 'Flytt kapittel og sider i ei bok for å omorganisere dei. Andre bøker kan bli lagt til slik at det er enklere å flytte fram og tilbake mellom dei.', + 'books_sort_desc' => 'Move chapters and pages within a book to reorganise its contents. Other books can be added which allows easy moving of chapters and pages between books. Optionally an auto sort rule can be set to automatically sort this book\'s contents upon changes.', + 'books_sort_auto_sort' => 'Auto Sort Option', + 'books_sort_auto_sort_active' => 'Auto Sort Active: :sortName', 'books_sort_named' => 'Omorganiser :bookName', 'books_sort_name' => 'Sorter på namn', 'books_sort_created' => 'Sorter på oppretta dato', @@ -207,7 +234,7 @@ 'pages_delete_draft' => 'Slett utkastet', 'pages_delete_success' => 'Siden er slettet', 'pages_delete_draft_success' => 'Sideutkastet vart sletta', - 'pages_delete_warning_template' => 'Denne siden er i aktiv bruk som en standard sidemal for bok. Desse bøkene vil ikkje lenger ha ein standard sidemal som er tilordnet etter at denne sida vert sletta.', + 'pages_delete_warning_template' => 'This page is in active use as a book or chapter default page template. These books or chapters will no longer have a default page template assigned after this page is deleted.', 'pages_delete_confirm' => 'Er du sikker på at du vil slette siden?', 'pages_delete_draft_confirm' => 'Er du sikker på at du vil slette utkastet?', 'pages_editing_named' => 'Redigerer :pageName (side)', @@ -224,6 +251,8 @@ 'pages_edit_switch_to_markdown_clean' => '(Renset innhold)', 'pages_edit_switch_to_markdown_stable' => '(Urørt innhold)', 'pages_edit_switch_to_wysiwyg' => 'Bytt til WYSIWYG tekstredigering', + 'pages_edit_switch_to_new_wysiwyg' => 'Switch to new WYSIWYG', + 'pages_edit_switch_to_new_wysiwyg_desc' => '(In Beta Testing)', 'pages_edit_set_changelog' => 'Angi endringslogg', 'pages_edit_enter_changelog_desc' => 'Gi ei kort skildring av endringane dine', 'pages_edit_enter_changelog' => 'Sjå endringslogg', @@ -243,6 +272,7 @@ 'pages_md_insert_drawing' => 'Sett inn tegning', 'pages_md_show_preview' => 'Førhandsvisning', 'pages_md_sync_scroll' => 'Synkroniser førehandsvisingsrulle', + 'pages_md_plain_editor' => 'Plaintext editor', 'pages_drawing_unsaved' => 'Ulagra teikning funne', 'pages_drawing_unsaved_confirm' => 'Ulagra teikninga vart funne frå ei tidligare mislykka lagring. Vil du gjenopprette og fortsette å redigere denne ulagra teikninga?', 'pages_not_in_chapter' => 'Sida tilhøyrer ingen kapittel', @@ -367,8 +397,11 @@ 'comment' => 'Kommentar', 'comments' => 'Kommentarer', 'comment_add' => 'Skriv kommentar', + 'comment_none' => 'No comments to display', 'comment_placeholder' => 'Skriv en kommentar her', - 'comment_count' => '{0} Ingen kommentarer|{1} 1 kommentar|[2,*] :count kommentarer', + 'comment_thread_count' => ':count Comment Thread|:count Comment Threads', + 'comment_archived_count' => ':count Archived', + 'comment_archived_threads' => 'Archived Threads', 'comment_save' => 'Publiser kommentar', 'comment_new' => 'Ny kommentar', 'comment_created' => 'kommenterte :createDiff', @@ -377,8 +410,14 @@ 'comment_deleted_success' => 'Kommentar fjernet', 'comment_created_success' => 'Kommentar skrevet', 'comment_updated_success' => 'Kommentar endret', + 'comment_archive_success' => 'Comment archived', + 'comment_unarchive_success' => 'Comment un-archived', + 'comment_view' => 'View comment', + 'comment_jump_to_thread' => 'Jump to thread', 'comment_delete_confirm' => 'Er du sikker på at du vil fjerne kommentaren?', 'comment_in_reply_to' => 'Som svar til :commentId', + 'comment_reference' => 'Reference', + 'comment_reference_outdated' => '(Outdated)', 'comment_editor_explain' => 'Her er kommentarene som er på denne siden. Kommentarer kan legges til og administreres når du ser på den lagrede siden.', // Revision diff --git a/lang/nn/errors.php b/lang/nn/errors.php index e9087bac407..c56b7d07850 100644 --- a/lang/nn/errors.php +++ b/lang/nn/errors.php @@ -5,11 +5,12 @@ return [ // Permissions - 'permission' => 'Du har ikke tilgang til å se denne siden.', + 'permission' => 'Du har ikkje tilgang til å sjå denne sida.', 'permissionJson' => 'Du har ikke tilgang til å utføre denne handlingen.', // Auth 'error_user_exists_different_creds' => 'En konto med :email finnes allerede, men har andre detaljer.', + 'auth_pre_register_theme_prevention' => 'User account could not be registered for the provided details', 'email_already_confirmed' => 'E-posten er allerede bekreftet, du kan forsøke å logge inn.', 'email_confirmation_invalid' => 'Denne bekreftelseskoden er allerede benyttet eller utgått. Prøv å registrere på nytt.', 'email_confirmation_expired' => 'Bekreftelseskoden er allerede utgått, en ny e-post er sendt.', @@ -23,7 +24,6 @@ 'saml_invalid_response_id' => 'Forespørselen fra det eksterne autentiseringssystemet gjenkjennes ikke av en prosess som startes av dette programmet. Å navigere tilbake etter pålogging kan forårsake dette problemet.', 'saml_fail_authed' => 'Innlogging gjennom :system feilet. Fikk ikke kontakt med autentiseringstjeneren.', 'oidc_already_logged_in' => 'Allerede logget inn', - 'oidc_user_not_registered' => 'Brukeren :name er ikkje registrert og automatisk registrering er deaktivert', 'oidc_no_email_address' => 'Finner ikke en e-postadresse, for denne brukeren, i dataene som leveres av det eksterne autentiseringssystemet', 'oidc_fail_authed' => 'Innlogging ved hjelp av :system feilet, systemet ga ikke vellykket godkjenning', 'social_no_action_defined' => 'Ingen handlinger er definert', @@ -37,6 +37,7 @@ 'social_driver_not_found' => 'Autentiseringstjeneste fra sosiale medier er ikke installert', 'social_driver_not_configured' => 'Dine :socialAccount innstilliner er ikke angitt.', 'invite_token_expired' => 'Invitasjonslenken har utgått, du kan forsøke å be om nytt passord istede.', + 'login_user_not_found' => 'A user for this action could not be found.', // System 'path_not_writable' => 'Filstien :filePath aksepterer ikkje filer, du må sjekke filstitilganger i systemet.', @@ -77,6 +78,7 @@ // Users 'users_cannot_delete_only_admin' => 'Du kan ikke kaste ut den eneste administratoren', 'users_cannot_delete_guest' => 'Du kan ikke slette gjestebrukeren (Du kan deaktivere offentlig visning istede)', + 'users_could_not_send_invite' => 'Could not create user since invite email failed to send', // Roles 'role_cannot_be_edited' => 'Denne rollen kan ikke endres', @@ -103,6 +105,18 @@ 'app_down' => ':appName er nede for øyeblikket', 'back_soon' => 'Den vil snart komme tilbake.', + // Import + 'import_zip_cant_read' => 'Could not read ZIP file.', + 'import_zip_cant_decode_data' => 'Could not find and decode ZIP data.json content.', + 'import_zip_no_data' => 'ZIP file data has no expected book, chapter or page content.', + 'import_validation_failed' => 'Import ZIP failed to validate with errors:', + 'import_zip_failed_notification' => 'Failed to import ZIP file.', + 'import_perms_books' => 'You are lacking the required permissions to create books.', + 'import_perms_chapters' => 'You are lacking the required permissions to create chapters.', + 'import_perms_pages' => 'You are lacking the required permissions to create pages.', + 'import_perms_images' => 'You are lacking the required permissions to create images.', + 'import_perms_attachments' => 'You are lacking the required permission to create attachments.', + // API errors 'api_no_authorization_found' => 'Ingen autorisasjonstoken ble funnet på forespørselen', 'api_bad_authorization_format' => 'Det ble funnet et autorisasjonstoken på forespørselen, men formatet virket feil', diff --git a/lang/nn/preferences.php b/lang/nn/preferences.php index 4e6e6c1ffd1..ac6dc1b77dc 100644 --- a/lang/nn/preferences.php +++ b/lang/nn/preferences.php @@ -27,7 +27,7 @@ 'notifications_save' => 'Lagre innstillinger', 'notifications_update_success' => 'Varslingsinnstillingene er oppdatert!', 'notifications_watched' => 'Overvåka & ignorerte elementer', - 'notifications_watched_desc' => ' Nedenfor er elementene som har egendefinerte varslingsinnstillinger i bruk. For å oppdatere innstillingene for disse, se elementet, finn varslingsalternativene i sidepanelet.', + 'notifications_watched_desc' => 'Nedenfor er elementene som har egendefinerte varslingsinnstillinger i bruk. For å oppdatere innstillingene for disse, se elementet, finn varslingsalternativene i sidepanelet.', 'auth' => 'Tilgang og tryggleik', 'auth_change_password' => 'Endre passord', diff --git a/lang/nn/settings.php b/lang/nn/settings.php index f269c35bcac..c098ac75386 100644 --- a/lang/nn/settings.php +++ b/lang/nn/settings.php @@ -74,6 +74,36 @@ 'reg_confirm_restrict_domain_desc' => 'Skriv inn en kommaseparert liste over e-postdomener du vil begrense registreringen til. Brukerne vil bli sendt en e-post for å bekrefte adressen deres før de får lov til å kommunisere med applikasjonen.
Vær oppmerksom på at brukere vil kunne endre e-postadressene sine etter vellykket registrering.', 'reg_confirm_restrict_domain_placeholder' => 'Ingen begrensninger er satt', + // Sorting Settings + 'sorting' => 'Sorting', + 'sorting_book_default' => 'Default Book Sort', + 'sorting_book_default_desc' => 'Select the default sort rule to apply to new books. This won\'t affect existing books, and can be overridden per-book.', + 'sorting_rules' => 'Sort Rules', + 'sorting_rules_desc' => 'These are predefined sorting operations which can be applied to content in the system.', + 'sort_rule_assigned_to_x_books' => 'Assigned to :count Book|Assigned to :count Books', + 'sort_rule_create' => 'Create Sort Rule', + 'sort_rule_edit' => 'Edit Sort Rule', + 'sort_rule_delete' => 'Delete Sort Rule', + 'sort_rule_delete_desc' => 'Remove this sort rule from the system. Books using this sort will revert to manual sorting.', + 'sort_rule_delete_warn_books' => 'This sort rule is currently used on :count book(s). Are you sure you want to delete this?', + 'sort_rule_delete_warn_default' => 'This sort rule is currently used as the default for books. Are you sure you want to delete this?', + 'sort_rule_details' => 'Sort Rule Details', + 'sort_rule_details_desc' => 'Set a name for this sort rule, which will appear in lists when users are selecting a sort.', + 'sort_rule_operations' => 'Sort Operations', + 'sort_rule_operations_desc' => 'Configure the sort actions to be performed by moving them from the list of available operations. Upon use, the operations will be applied in order, from top to bottom. Any changes made here will be applied to all assigned books upon save.', + 'sort_rule_available_operations' => 'Available Operations', + 'sort_rule_available_operations_empty' => 'No operations remaining', + 'sort_rule_configured_operations' => 'Configured Operations', + 'sort_rule_configured_operations_empty' => 'Drag/add operations from the "Available Operations" list', + 'sort_rule_op_asc' => '(Asc)', + 'sort_rule_op_desc' => '(Desc)', + 'sort_rule_op_name' => 'Name - Alphabetical', + 'sort_rule_op_name_numeric' => '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_last' => 'Chapters Last', + // Maintenance settings 'maint' => 'Vedlikehold', 'maint_image_cleanup' => 'Rydd opp bilete', @@ -109,7 +139,7 @@ 'recycle_bin_contents_empty' => 'Papirkurven er for øyeblikket tom', 'recycle_bin_empty' => 'Tøm papirkurven', 'recycle_bin_empty_confirm' => 'Dette vil slette alle elementene i papirkurven permanent. Dette inkluderer innhold i hvert element. Er du sikker på at du vil tømme papirkurven?', - 'recycle_bin_destroy_confirm' => 'Denne handlinga vil permanent slette dette elementet og alle dets underelementer frå systemet, som skildra nedanfor. Du vil ikkje kunne gjenopprette dette innholdet, med mindre du har ein tidligere tryggleiksskopi av databasen. Er du sikker på at du vil fortsette?', + 'recycle_bin_destroy_confirm' => 'This action will permanently delete this item from the system, along with any child elements listed below, and you will not be able to restore this content. Are you sure you want to permanently delete this item?', 'recycle_bin_destroy_list' => 'Elementer som skal slettes', 'recycle_bin_restore_list' => 'Elementer som skal gjenopprettes', 'recycle_bin_restore_confirm' => 'Denne handlingen vil hente opp elementet fra papirkurven, inkludert underliggende innhold, til sin opprinnelige sted. Om den opprinnelige plassen har blitt slettet i mellomtiden og nå befinner seg i papirkurven, vil også dette bli hentet opp igjen.', @@ -162,6 +192,7 @@ 'role_access_api' => 'Systemtilgang API', 'role_manage_settings' => 'Behandle applikasjonsinnstillinger', 'role_export_content' => 'Eksporter innhold', + 'role_import_content' => 'Import content', 'role_editor_change' => 'Endre sideredigering', 'role_notifications' => 'Motta og administrere varslinger', 'role_asset' => 'Eiendomstillatelser', @@ -276,6 +307,14 @@ 'webhooks_last_errored' => 'Siste feil:', 'webhooks_last_error_message' => 'Siste feilmelding:', + // Licensing + 'licenses' => 'Licenses', + 'licenses_desc' => 'This page details license information for BookStack in addition to the projects & libraries that are used within BookStack. Many projects listed may only be used in a development context.', + 'licenses_bookstack' => 'BookStack License', + 'licenses_php' => 'PHP Library Licenses', + 'licenses_js' => 'JavaScript Library Licenses', + 'licenses_other' => 'Other Licenses', + 'license_details' => 'License Details', //! If editing translations files directly please ignore this in all //! languages apart from en. Content will be auto-copied from en. @@ -287,6 +326,7 @@ 'bs' => 'Bosanski', 'ca' => 'Català', 'cs' => 'Česky', + 'cy' => 'Cymraeg', 'da' => 'Dansk', 'de' => 'Deutsch (Sie)', 'de_informal' => 'Deutsch (Du)', @@ -308,6 +348,7 @@ 'lt' => 'Lietuvių Kalba', 'lv' => 'Latviešu Valoda', 'nb' => 'Norsk (Bokmål)', + 'ne' => 'नेपाली', 'nn' => 'Nynorsk', 'nl' => 'Nederlands', 'pl' => 'Polski', diff --git a/lang/nn/validation.php b/lang/nn/validation.php index 7e3784e30c7..a24ebd17189 100644 --- a/lang/nn/validation.php +++ b/lang/nn/validation.php @@ -105,6 +105,11 @@ 'url' => ':attribute format er ugyldig.', 'uploaded' => 'kunne ikke lastes opp, tjeneren støtter ikke filer av denne størrelsen.', + 'zip_file' => 'The :attribute needs to reference a file within the ZIP.', + 'zip_file_mime' => 'The :attribute needs to reference a file of type :validTypes, found :foundType.', + 'zip_model_expected' => 'Data object expected but ":type" found.', + 'zip_unique' => 'The :attribute must be unique for the object type within the ZIP.', + // Custom validation lines 'custom' => [ 'password-confirm' => [ diff --git a/lang/pl/activities.php b/lang/pl/activities.php index 8b35621defb..dc078ad57ab 100644 --- a/lang/pl/activities.php +++ b/lang/pl/activities.php @@ -84,6 +84,14 @@ 'webhook_delete' => 'usunął webhook', 'webhook_delete_notification' => 'Webhook usunięty pomyślnie', + // Imports + 'import_create' => 'utworzono import', + 'import_create_notification' => 'Import successfully uploaded', + 'import_run' => 'updated import', + 'import_run_notification' => 'Content successfully imported', + 'import_delete' => 'deleted import', + 'import_delete_notification' => 'Import successfully deleted', + // Users 'user_create' => 'utworzył użytkownika', 'user_create_notification' => 'Użytkownik utworzony pomyślnie', @@ -93,11 +101,11 @@ 'user_delete_notification' => 'Użytkownik pomyślnie usunięty', // API Tokens - 'api_token_create' => 'utworzył token api', + 'api_token_create' => 'utworzył token API', 'api_token_create_notification' => 'Token API został poprawnie utworzony', - 'api_token_update' => 'zaktualizował token api', + 'api_token_update' => 'zaktualizował token API', 'api_token_update_notification' => 'Token API został pomyślnie zaktualizowany', - 'api_token_delete' => 'usunął token api', + 'api_token_delete' => 'usunął token API', 'api_token_delete_notification' => 'Token API został pomyślnie usunięty', // Roles @@ -119,6 +127,14 @@ 'comment_update' => 'zaktualizował komentarz', 'comment_delete' => 'usunął komentarz', + // Sort Rules + 'sort_rule_create' => 'created sort rule', + 'sort_rule_create_notification' => 'Sort rule successfully created', + 'sort_rule_update' => 'updated sort rule', + 'sort_rule_update_notification' => 'Sort rule successfully updated', + 'sort_rule_delete' => 'deleted sort rule', + 'sort_rule_delete_notification' => 'Sort rule successfully deleted', + // Other 'permissions_update' => 'zaktualizował uprawnienia', ]; diff --git a/lang/pl/auth.php b/lang/pl/auth.php index 00cce1a2cc5..445f8198af8 100644 --- a/lang/pl/auth.php +++ b/lang/pl/auth.php @@ -91,7 +91,7 @@ 'mfa_option_totp_title' => 'Aplikacja mobilna', 'mfa_option_totp_desc' => 'Aby korzystać z uwierzytelniania wieloskładnikowego, potrzebujesz aplikacji mobilnej, która obsługuje TOTP, takiej jak Google Authenticator, Authy lub Microsoft Authenticator.', 'mfa_option_backup_codes_title' => 'Kody zapasowe', - 'mfa_option_backup_codes_desc' => 'Bezpiecznie przechowuj zestaw jednorazowych kodów zapasowych, które możesz wprowadzić, aby zweryfikować swoją tożsamość.', + 'mfa_option_backup_codes_desc' => 'Generuje zestaw jednorazowych kodów zapasowych, które wprowadzisz przy logowaniu, aby zweryfikować Twoją tożsamość. Upewnij się, że przechowujesz je w bezpiecznym miejscu.', 'mfa_gen_confirm_and_enable' => 'Potwierdź i włącz', 'mfa_gen_backup_codes_title' => 'Ustawienia kopii zapasowych kodów', 'mfa_gen_backup_codes_desc' => 'Przechowuj poniższą listę kodów w bezpiecznym miejscu. Przy dostępie do systemu będziesz mógł użyć jednego z kodów jako drugiego mechanizmu uwierzytelniania.', diff --git a/lang/pl/common.php b/lang/pl/common.php index 13939f0e06d..e21302928b6 100644 --- a/lang/pl/common.php +++ b/lang/pl/common.php @@ -20,7 +20,7 @@ 'description' => 'Opis', 'role' => 'Rola', 'cover_image' => 'Okładka', - 'cover_image_description' => 'Ten obraz powinien posiadać wymiary około 440x250px.', + 'cover_image_description' => 'Ten obraz powinien być o rozmiarze około 440x250px, chociaż zostanie elastycznie przeskalowany i przycięty, aby dopasować interfejs użytkownika do różnych scenariuszy w zależności od potrzeb, więc faktyczne wymiary wyświetlania będą się różnić.', // Actions 'actions' => 'Akcje', @@ -30,6 +30,8 @@ 'create' => 'Utwórz', 'update' => 'Zaktualizuj', 'edit' => 'Edytuj', + 'archive' => 'Archive', + 'unarchive' => 'Un-Archive', 'sort' => 'Sortuj', 'move' => 'Przenieś', 'copy' => 'Skopiuj', @@ -107,4 +109,7 @@ // Not directly used but available for convenience to users. 'privacy_policy' => 'Polityka prywatności', 'terms_of_service' => 'Warunki usługi', + + // OpenSearch + 'opensearch_description' => 'Szukaj :appName', ]; diff --git a/lang/pl/editor.php b/lang/pl/editor.php index 9b19e266768..a93b5dd7880 100644 --- a/lang/pl/editor.php +++ b/lang/pl/editor.php @@ -13,6 +13,7 @@ 'cancel' => 'Anuluj', 'save' => 'Zapisz', 'close' => 'Zamknij', + 'apply' => 'Apply', 'undo' => 'Cofnij', 'redo' => 'Ponów', 'left' => 'Lewa strona', @@ -47,6 +48,7 @@ 'superscript' => 'Indeks górny', 'subscript' => 'Indeks dolny', 'text_color' => 'Kolor tekstu', + 'highlight_color' => 'Highlight color', 'custom_color' => 'Kolor niestandardowy', 'remove_color' => 'Usuń kolor', 'background_color' => 'Kolor tła', @@ -81,6 +83,9 @@ 'table_properties' => 'Właściwości tabeli', 'table_properties_title' => 'Właściwości Tabeli', 'delete_table' => 'Usuń tabelę', + 'table_clear_formatting' => 'Wyczyść formatowanie tabeli', + 'resize_to_contents' => 'Dostosuj rozmiar do zawartości', + 'row_header' => 'Wiersz nagłówka', 'insert_row_before' => 'Wstaw wiersz przed', 'insert_row_after' => 'Wstaw wiersz za', 'delete_row' => 'Usuń wiersz', @@ -144,6 +149,7 @@ 'url' => 'Adres URL', 'text_to_display' => 'Tekst do wyświetlenia', 'title' => 'Tytuł', + 'browse_links' => 'Browse links', 'open_link' => 'Otwórz link', 'open_link_in' => 'Otwórz link w...', 'open_link_current' => 'Bieżące okno', @@ -160,6 +166,8 @@ 'about' => 'O edytorze', 'about_title' => 'O edytorze WYSIWYG', 'editor_license' => 'Licencja edytora i prawa autorskie', + 'editor_lexical_license' => 'This editor is built as a fork of :lexicalLink which is distributed under the MIT license.', + 'editor_lexical_license_link' => 'Full license details can be found here.', 'editor_tiny_license' => 'Ten edytor jest zbudowany przy użyciu :tinyLink, który jest udostępniany na licencji MIT.', 'editor_tiny_license_link' => 'Szczegóły dotyczące praw autorskich i licencji TinyMCE można znaleźć tutaj.', 'save_continue' => 'Zapisz stronę i kontynuuj', diff --git a/lang/pl/entities.php b/lang/pl/entities.php index 6d1bb4348e6..72389500b65 100644 --- a/lang/pl/entities.php +++ b/lang/pl/entities.php @@ -23,7 +23,7 @@ 'meta_updated' => 'Zaktualizowano :timeLength', 'meta_updated_name' => 'Zaktualizowano :timeLength przez :user', 'meta_owned_name' => 'Właściciel: :user', - 'meta_reference_count' => 'Referenced by :count item|Referenced by :count items', + 'meta_reference_count' => 'Odniesienie w :count elemencie|Odniesienia w :count elementach', 'entity_select' => 'Wybór obiektu', 'entity_select_lack_permission' => 'Nie masz wymaganych uprawnień do wybrania tej pozycji', 'images' => 'Obrazki', @@ -39,6 +39,34 @@ 'export_pdf' => 'Plik PDF', 'export_text' => 'Plik tekstowy', 'export_md' => 'Pliki Markdown', + 'export_zip' => 'Portable ZIP', + 'default_template' => 'Domyślny szablon strony', + 'default_template_explain' => 'Przypisz szablon strony, który będzie używany jako domyślna zawartość dla wszystkich stron utworzonych w tym elemencie. Pamiętaj, że będzie to używane tylko wtedy, gdy twórca strony ma dostęp do wybranej strony szablonu.', + 'default_template_select' => 'Wybierz stronę szablonu', + 'import' => 'Import', + 'import_validate' => 'Validate Import', + 'import_desc' => 'Import books, chapters & pages using a portable zip export from the same, or a different, instance. Select a ZIP file to proceed. After the file has been uploaded and validated you\'ll be able to configure & confirm the import in the next view.', + '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_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_size' => ':size Import ZIP Size', + 'import_uploaded_at' => 'Uploaded :relativeTime', + 'import_uploaded_by' => 'Uploaded by', + 'import_location' => 'Import Location', + '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_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', + 'breadcrumb_siblings_for_book' => 'Navigate siblings for book', + 'breadcrumb_siblings_for_bookshelf' => 'Navigate siblings for shelf', // Permissions and restrictions 'permissions' => 'Uprawnienia', @@ -132,9 +160,6 @@ 'books_edit_named' => 'Edytuj książkę :bookName', 'books_form_book_name' => 'Nazwa książki', 'books_save' => 'Zapisz książkę', - 'books_default_template' => 'Default Page Template', - 'books_default_template_explain' => 'Assign a page template that will be used as the default content for all new pages in this book. Keep in mind this will only be used if the page creator has view access to those chosen template page.', - 'books_default_template_select' => 'Select a template page', 'books_permissions' => 'Uprawnienia książki', 'books_permissions_updated' => 'Zaktualizowano uprawnienia książki', 'books_empty_contents' => 'Brak stron lub rozdziałów w tej książce.', @@ -145,7 +170,9 @@ 'books_search_this' => 'Wyszukaj w tej książce', 'books_navigation' => 'Nawigacja po książce', 'books_sort' => 'Sortuj zawartość książki', - 'books_sort_desc' => 'Przenieś rozdziały i strony w książce w celu reorganizacji jej treści. Można dodać inne książki, co umożliwia łatwe przenoszenie rozdziałów i stron między książkami.', + 'books_sort_desc' => 'Move chapters and pages within a book to reorganise its contents. Other books can be added which allows easy moving of chapters and pages between books. Optionally an auto sort rule can be set to automatically sort this book\'s contents upon changes.', + 'books_sort_auto_sort' => 'Auto Sort Option', + 'books_sort_auto_sort_active' => 'Auto Sort Active: :sortName', 'books_sort_named' => 'Sortuj książkę :bookName', 'books_sort_name' => 'Sortuj według nazwy', 'books_sort_created' => 'Sortuj według daty utworzenia', @@ -207,7 +234,7 @@ 'pages_delete_draft' => 'Usuń wersje roboczą', 'pages_delete_success' => 'Strona usunięta pomyślnie', 'pages_delete_draft_success' => 'Werjsa robocza usunięta pomyślnie', - 'pages_delete_warning_template' => 'This page is in active use as a book default page template. These books will no longer have a default page template assigned after this page is deleted.', + 'pages_delete_warning_template' => 'Ta strona jest aktualnie używana jako domyślny szablon strony książki lub rozdziału. Po usunięciu tej strony te książki lub rozdziały nie będą już miały przypisanego domyślnego szablonu strony.', 'pages_delete_confirm' => 'Czy na pewno chcesz usunąć tę stronę?', 'pages_delete_draft_confirm' => 'Czy na pewno chcesz usunąć wersje roboczą strony?', 'pages_editing_named' => 'Edytowanie strony :pageName', @@ -224,6 +251,8 @@ 'pages_edit_switch_to_markdown_clean' => '(Czysta zawartość)', 'pages_edit_switch_to_markdown_stable' => '(Statyczna zawartość)', 'pages_edit_switch_to_wysiwyg' => 'Przełącz na edytor WYSIWYG', + 'pages_edit_switch_to_new_wysiwyg' => 'Przełącz na nowy WYSIWYG', + 'pages_edit_switch_to_new_wysiwyg_desc' => '(In Beta Testing)', 'pages_edit_set_changelog' => 'Ustaw dziennik zmian', 'pages_edit_enter_changelog_desc' => 'Opisz zmiany, które zostały wprowadzone', 'pages_edit_enter_changelog' => 'Wyświetl dziennik zmian', @@ -243,6 +272,7 @@ 'pages_md_insert_drawing' => 'Wstaw rysunek', 'pages_md_show_preview' => 'Pokaż podgląd', 'pages_md_sync_scroll' => 'Synchronizuj przewijanie podglądu', + 'pages_md_plain_editor' => 'Plaintext editor', 'pages_drawing_unsaved' => 'Znaleziono niezapisany rysunek', 'pages_drawing_unsaved_confirm' => 'Znaleziono niezapisane dane rysowania z poprzedniej nieudanej próby zapisu. Czy chcesz przywrócić i kontynuować edycję tego niezapisanego rysunku?', 'pages_not_in_chapter' => 'Strona nie została umieszczona w rozdziale', @@ -367,8 +397,11 @@ 'comment' => 'Komentarz', 'comments' => 'Komentarze', 'comment_add' => 'Dodaj komentarz', + 'comment_none' => 'No comments to display', 'comment_placeholder' => 'Napisz swój komentarz tutaj', - 'comment_count' => '{0} Brak komentarzy |{1} 1 komentarz|[2,*] :count komentarzy', + 'comment_thread_count' => ':count Comment Thread|:count Comment Threads', + 'comment_archived_count' => ':count Archived', + 'comment_archived_threads' => 'Archived Threads', 'comment_save' => 'Zapisz komentarz', 'comment_new' => 'Nowy komentarz', 'comment_created' => 'Skomentowano :createDiff', @@ -377,8 +410,14 @@ 'comment_deleted_success' => 'Komentarz usunięty', 'comment_created_success' => 'Komentarz dodany', 'comment_updated_success' => 'Komentarz zaktualizowany', + 'comment_archive_success' => 'Comment archived', + 'comment_unarchive_success' => 'Comment un-archived', + 'comment_view' => 'View comment', + 'comment_jump_to_thread' => 'Jump to thread', 'comment_delete_confirm' => 'Czy na pewno chcesz usunąc ten komentarz?', 'comment_in_reply_to' => 'W odpowiedzi na :commentId', + 'comment_reference' => 'Reference', + 'comment_reference_outdated' => '(Outdated)', 'comment_editor_explain' => 'Oto komentarze pozostawione na tej stronie. Komentarze mogą być dodawane i zarządzane podczas przeglądania zapisanej strony.', // Revision @@ -409,7 +448,7 @@ // References 'references' => 'Odniesienia', 'references_none' => 'Brak śledzonych odwołań do tego elementu.', - 'references_to_desc' => 'Listed below is all the known content in the system that links to this item.', + 'references_to_desc' => 'Wymienione poniżej są wszystkie znane treści w systemie, które nawiązują do tego elementu.', // Watch Options 'watch' => 'Obserwuj', diff --git a/lang/pl/errors.php b/lang/pl/errors.php index d4ba103e84d..e6ad2093f1f 100644 --- a/lang/pl/errors.php +++ b/lang/pl/errors.php @@ -10,6 +10,7 @@ // Auth 'error_user_exists_different_creds' => 'Użytkownik o adresie :email już istnieje, ale używa innych poświadczeń.', + 'auth_pre_register_theme_prevention' => 'Konto użytkownika nie może być zarejestrowane z podanymi danymi', 'email_already_confirmed' => 'E-mail został potwierdzony, spróbuj się zalogować.', 'email_confirmation_invalid' => 'Ten token jest nieprawidłowy lub został już wykorzystany. Spróbuj zarejestrować się ponownie.', 'email_confirmation_expired' => 'Ten token potwierdzający wygasł. Wysłaliśmy Ci kolejny.', @@ -23,7 +24,6 @@ 'saml_invalid_response_id' => 'Żądanie z zewnętrznego systemu uwierzytelniania nie zostało rozpoznane przez proces rozpoczęty przez tę aplikację. Cofnięcie po zalogowaniu mogło spowodować ten problem.', 'saml_fail_authed' => 'Logowanie przy użyciu :system nie powiodło się, system nie mógł pomyślnie ukończyć uwierzytelniania', 'oidc_already_logged_in' => 'Już zalogowany', - 'oidc_user_not_registered' => 'Użytkownik :name nie jest zarejestrowany, a automatyczna rejestracja jest wyłączona', 'oidc_no_email_address' => 'Nie można odnaleźć adresu email dla tego użytkownika w danych dostarczonych przez zewnętrzny system uwierzytelniania', 'oidc_fail_authed' => 'Logowanie przy użyciu :system nie powiodło się, system nie mógł pomyślnie ukończyć uwierzytelniania', 'social_no_action_defined' => 'Brak zdefiniowanej akcji', @@ -37,6 +37,7 @@ 'social_driver_not_found' => 'Funkcja społecznościowa nie została odnaleziona', 'social_driver_not_configured' => 'Ustawienia konta :socialAccount nie są poprawne.', 'invite_token_expired' => 'Zaproszenie wygasło. Możesz spróować zresetować swoje hasło.', + 'login_user_not_found' => 'Użytkownik dla tej akcji nie został znaleziony.', // System 'path_not_writable' => 'Zapis do ścieżki :filePath jest niemożliwy. Upewnij się że aplikacja ma prawa do zapisu plików na serwerze.', @@ -77,6 +78,7 @@ // Users 'users_cannot_delete_only_admin' => 'Nie możesz usunąć jedynego administratora', 'users_cannot_delete_guest' => 'Nie możesz usunąć użytkownika-gościa', + 'users_could_not_send_invite' => 'Nie można utworzyć użytkownika, ponieważ nie udało się wysłać wiadomości e-mail z zaproszeniem', // Roles 'role_cannot_be_edited' => 'Ta rola nie może być edytowana', @@ -103,6 +105,18 @@ 'app_down' => ':appName jest aktualnie wyłączona', 'back_soon' => 'Niedługo zostanie uruchomiona ponownie.', + // Import + 'import_zip_cant_read' => 'Could not read ZIP file.', + 'import_zip_cant_decode_data' => 'Could not find and decode ZIP data.json content.', + 'import_zip_no_data' => 'ZIP file data has no expected book, chapter or page content.', + 'import_validation_failed' => 'Import ZIP failed to validate with errors:', + 'import_zip_failed_notification' => 'Failed to import ZIP file.', + 'import_perms_books' => 'You are lacking the required permissions to create books.', + 'import_perms_chapters' => 'You are lacking the required permissions to create chapters.', + 'import_perms_pages' => 'You are lacking the required permissions to create pages.', + 'import_perms_images' => 'You are lacking the required permissions to create images.', + 'import_perms_attachments' => 'You are lacking the required permission to create attachments.', + // API errors 'api_no_authorization_found' => 'Nie znaleziono tokenu autoryzacji dla żądania', 'api_bad_authorization_format' => 'Token autoryzacji został znaleziony w żądaniu, ale format okazał się nieprawidłowy', diff --git a/lang/pl/preferences.php b/lang/pl/preferences.php index 6d67dc1b3e6..dd348210279 100644 --- a/lang/pl/preferences.php +++ b/lang/pl/preferences.php @@ -27,7 +27,7 @@ 'notifications_save' => 'Zapisz preferencje', 'notifications_update_success' => 'Preferencje powiadomień zostały zaktualizowane!', 'notifications_watched' => 'Obserwowane i ignorowane elementy', - 'notifications_watched_desc' => ' Poniżej znajdują się elementy, które mają własne preferencje obserwowania. Aby zaktualizować swoje preferencje, zobacz dany element, a następnie znajdź opcje obserwowania na pasku bocznym.', + 'notifications_watched_desc' => 'Poniżej znajdują się elementy, które mają własne preferencje obserwowania. Aby zaktualizować swoje preferencje, zobacz dany element, a następnie znajdź opcje obserwowania na pasku bocznym.', 'auth' => 'Dostęp i bezpieczeństwo', 'auth_change_password' => 'Zmień hasło', diff --git a/lang/pl/settings.php b/lang/pl/settings.php index 4d0bc9f7a2a..7c84ce34b87 100644 --- a/lang/pl/settings.php +++ b/lang/pl/settings.php @@ -74,6 +74,36 @@ 'reg_confirm_restrict_domain_desc' => 'Wprowadź listę domen adresów e-mail, rozdzieloną przecinkami, którym chciałbyś zezwolić na rejestrację. Wymusi to konieczność potwierdzenia adresu e-mail przez użytkownika przed uzyskaniem dostępu do aplikacji.
Pamiętaj, że użytkownicy będą mogli zmienić adres e-mail po rejestracji.', 'reg_confirm_restrict_domain_placeholder' => 'Brak restrykcji', + // Sorting Settings + 'sorting' => 'Sorting', + 'sorting_book_default' => 'Default Book Sort', + 'sorting_book_default_desc' => 'Select the default sort rule to apply to new books. This won\'t affect existing books, and can be overridden per-book.', + 'sorting_rules' => 'Sort Rules', + 'sorting_rules_desc' => 'These are predefined sorting operations which can be applied to content in the system.', + 'sort_rule_assigned_to_x_books' => 'Assigned to :count Book|Assigned to :count Books', + 'sort_rule_create' => 'Create Sort Rule', + 'sort_rule_edit' => 'Edit Sort Rule', + 'sort_rule_delete' => 'Delete Sort Rule', + 'sort_rule_delete_desc' => 'Remove this sort rule from the system. Books using this sort will revert to manual sorting.', + 'sort_rule_delete_warn_books' => 'This sort rule is currently used on :count book(s). Are you sure you want to delete this?', + 'sort_rule_delete_warn_default' => 'This sort rule is currently used as the default for books. Are you sure you want to delete this?', + 'sort_rule_details' => 'Sort Rule Details', + 'sort_rule_details_desc' => 'Set a name for this sort rule, which will appear in lists when users are selecting a sort.', + 'sort_rule_operations' => 'Sort Operations', + 'sort_rule_operations_desc' => 'Configure the sort actions to be performed by moving them from the list of available operations. Upon use, the operations will be applied in order, from top to bottom. Any changes made here will be applied to all assigned books upon save.', + 'sort_rule_available_operations' => 'Available Operations', + 'sort_rule_available_operations_empty' => 'No operations remaining', + 'sort_rule_configured_operations' => 'Configured Operations', + 'sort_rule_configured_operations_empty' => 'Drag/add operations from the "Available Operations" list', + 'sort_rule_op_asc' => '(Asc)', + 'sort_rule_op_desc' => '(Desc)', + 'sort_rule_op_name' => 'Name - Alphabetical', + 'sort_rule_op_name_numeric' => '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_last' => 'Chapters Last', + // Maintenance settings 'maint' => 'Konserwacja', 'maint_image_cleanup' => 'Czyszczenie obrazków', @@ -109,7 +139,7 @@ 'recycle_bin_contents_empty' => 'Kosz jest pusty', 'recycle_bin_empty' => 'Opróżnij kosz', 'recycle_bin_empty_confirm' => 'To na stałe zniszczy wszystkie przedmioty w koszu, w tym zawartość w każdym elemencie. Czy na pewno chcesz opróżnić kosz?', - 'recycle_bin_destroy_confirm' => 'Ta akcja trwale usunie ten element, wraz z elementami podrzędnymi wymienionymi poniżej, z systemu i nie będziesz w stanie przywrócić tej zawartości. Czy na pewno chcesz trwale usunąć ten element?', + 'recycle_bin_destroy_confirm' => 'Ta akcja trwale usunie ten element z systemu, wraz z elementami podrzędnymi wymienionymi poniżej i nie będziesz już mógł przywrócić tej zawartości. Czy na pewno chcesz trwale usunąć ten element?', 'recycle_bin_destroy_list' => 'Elementy do usunięcia', 'recycle_bin_restore_list' => 'Elementy do przywrócenia', 'recycle_bin_restore_confirm' => 'Ta akcja przywróci usunięty element, w tym elementy podrzędne, do ich oryginalnej lokalizacji. Jeśli oryginalna lokalizacja została od tego czasu usunięta, a teraz znajduje się w koszu, element nadrzędny będzie również musiał zostać przywrócony.', @@ -162,6 +192,7 @@ 'role_access_api' => 'Dostęp do systemowego API', 'role_manage_settings' => 'Zarządzanie ustawieniami aplikacji', 'role_export_content' => 'Eksportuj zawartość', + 'role_import_content' => 'Import content', 'role_editor_change' => 'Zmień edytor strony', 'role_notifications' => 'Odbieranie i zarządzanie powiadomieniami', 'role_asset' => 'Zarządzanie zasobami', @@ -276,6 +307,14 @@ 'webhooks_last_errored' => 'Ostatni błąd:', 'webhooks_last_error_message' => 'Ostatni komunikat o błędzie:', + // Licensing + 'licenses' => 'Licencje', + 'licenses_desc' => 'Ta strona podaje szczegóły dotyczące licencji dla BookStack w powiązaniu z projektami i bibliotekami używanymi w BookStack. Wiele wymienionych projektów może zezwalać na wykorzystanie wyłącznie w kontekście rozwoju oprogramowania.', + 'licenses_bookstack' => 'Licencja BookStack', + 'licenses_php' => 'Licencje bibliotek PHP', + 'licenses_js' => 'Licencje bibliotek JavaScript', + 'licenses_other' => 'Inne licencje', + 'license_details' => 'Szczegóły licencji', //! If editing translations files directly please ignore this in all //! languages apart from en. Content will be auto-copied from en. @@ -287,6 +326,7 @@ 'bs' => 'Bosanski', 'ca' => 'Kataloński', 'cs' => 'Česky', + 'cy' => 'Cymraeg', 'da' => 'Dansk', 'de' => 'Deutsch (Sie)', 'de_informal' => 'Deutsch (Du)', @@ -308,6 +348,7 @@ 'lt' => 'Lietuvių Kalba', 'lv' => 'Latviešu Valoda', 'nb' => 'Norsk (Bokmål)', + 'ne' => 'नेपाली', 'nn' => 'Nynorsk', 'nl' => 'Nederlands', 'pl' => 'Polski', diff --git a/lang/pl/validation.php b/lang/pl/validation.php index 935c634df29..f20a66dfb6c 100644 --- a/lang/pl/validation.php +++ b/lang/pl/validation.php @@ -105,6 +105,11 @@ 'url' => 'Format :attribute jest nieprawidłowy.', 'uploaded' => 'Plik nie może zostać wysłany. Serwer nie akceptuje plików o takim rozmiarze.', + 'zip_file' => 'The :attribute needs to reference a file within the ZIP.', + 'zip_file_mime' => 'The :attribute needs to reference a file of type :validTypes, found :foundType.', + 'zip_model_expected' => 'Data object expected but ":type" found.', + 'zip_unique' => 'The :attribute must be unique for the object type within the ZIP.', + // Custom validation lines 'custom' => [ 'password-confirm' => [ diff --git a/lang/pt/activities.php b/lang/pt/activities.php index d12d640cb0b..33d9e2ca686 100644 --- a/lang/pt/activities.php +++ b/lang/pt/activities.php @@ -30,7 +30,7 @@ // Books 'book_create' => 'livro criado', 'book_create_notification' => 'Livro criado com sucesso', - 'book_create_from_chapter' => 'capítulo convertido para lívro', + 'book_create_from_chapter' => 'capítulo convertido para livro', 'book_create_from_chapter_notification' => 'Capítulo convertido em livro com sucesso', 'book_update' => 'livro atualizado', 'book_update_notification' => 'Livro atualizado com sucesso', @@ -46,12 +46,12 @@ 'bookshelf_create_from_book_notification' => 'Livro convertido em prateleira com sucesso', 'bookshelf_update' => 'estante atualizada', 'bookshelf_update_notification' => 'Estante atualizada com sucesso', - 'bookshelf_delete' => 'prateleira excluída', + 'bookshelf_delete' => 'excluiu a estante', 'bookshelf_delete_notification' => 'Estante eliminada com sucesso', // Revisions - 'revision_restore' => 'revisão restaurada', - 'revision_delete' => 'revisão eliminada', + 'revision_restore' => 'restaurou a revisão', + 'revision_delete' => 'eliminou a revisão', 'revision_delete_notification' => 'Revisão eliminada com sucesso', // Favourites @@ -62,7 +62,7 @@ 'watch_update_level_notification' => 'Ver preferências atualizadas com sucesso', // Auth - 'auth_login' => 'sessão iniciada', + 'auth_login' => 'iniciou sessão', 'auth_register' => 'registado como novo utilizador', 'auth_password_reset_request' => 'pedido a redefinição da palavra-passe', 'auth_password_reset_update' => 'redifinir palavra-passe do utilizador', @@ -84,6 +84,14 @@ 'webhook_delete' => 'eliminar webhook', 'webhook_delete_notification' => 'Webhook criado com sucesso', + // Imports + 'import_create' => 'importação criada', + 'import_create_notification' => 'Importação carregada com sucesso', + 'import_run' => 'importação atualizada', + 'import_run_notification' => 'Conteúdo importado com sucesso', + 'import_delete' => 'importação apagada', + 'import_delete_notification' => 'Importação eliminada com sucesso', + // Users 'user_create' => 'utilizador criado', 'user_create_notification' => 'Utilizador criado com sucesso', @@ -93,11 +101,11 @@ 'user_delete_notification' => 'Utilizador removido com sucesso', // API Tokens - 'api_token_create' => 'api token criado', + 'api_token_create' => 'token API criado', 'api_token_create_notification' => 'API token criado com sucesso', - 'api_token_update' => 'api token atualizado', + 'api_token_update' => 'token API atualizado', 'api_token_update_notification' => 'API token atualizado com sucesso', - 'api_token_delete' => 'api token eliminado', + 'api_token_delete' => 'token API apagado', 'api_token_delete_notification' => 'API token atualizado com sucesso', // Roles @@ -119,6 +127,14 @@ 'comment_update' => 'comentário atualizado', 'comment_delete' => 'comentário eliminado', + // Sort Rules + 'sort_rule_create' => 'regra de ordenação criada', + 'sort_rule_create_notification' => 'Regra de ordenação criada com sucesso', + 'sort_rule_update' => 'regra de ordenação atualizada', + 'sort_rule_update_notification' => 'Regra de ordenação atualizada com sucesso', + 'sort_rule_delete' => 'regra de ordenação apagada', + 'sort_rule_delete_notification' => 'Regra de ordenação apagada com sucesso', + // Other 'permissions_update' => 'permissões atualizadas', ]; diff --git a/lang/pt/auth.php b/lang/pt/auth.php index a21eb701b3f..8807e1a87d3 100644 --- a/lang/pt/auth.php +++ b/lang/pt/auth.php @@ -91,7 +91,7 @@ 'mfa_option_totp_title' => 'Aplicação móvel', 'mfa_option_totp_desc' => 'Para usar a autenticação multi-fator, você precisa de uma aplicação móvel que suporte TOTP como o Autenticador do Google, Authy ou o autenticador Microsoft.', 'mfa_option_backup_codes_title' => 'Códigos de Backup', - 'mfa_option_backup_codes_desc' => 'Armazene com segurança um conjunto de códigos de backup únicos que você pode inserir para verificar sua identidade.', + 'mfa_option_backup_codes_desc' => 'Gera um conjunto de códigos de reserva de utilização única, que deverá usar no início de sessão para verificar a sua identidade. Certifique-se de que os guarda num local seguro.', 'mfa_gen_confirm_and_enable' => 'Confirmar e ativar', 'mfa_gen_backup_codes_title' => 'Configuração dos Códigos de Backup', 'mfa_gen_backup_codes_desc' => 'Armazene a lista de códigos abaixo em um lugar seguro. Ao acessar o sistema você poderá usar um dos códigos como um segundo mecanismo de autenticação.', diff --git a/lang/pt/common.php b/lang/pt/common.php index 586ac191150..a0d83ac1e73 100644 --- a/lang/pt/common.php +++ b/lang/pt/common.php @@ -20,7 +20,7 @@ 'description' => 'Descrição', 'role' => 'Cargo', 'cover_image' => 'Imagem de capa', - 'cover_image_description' => 'Esta imagem deve ter aproximadamente 440x250px.', + 'cover_image_description' => 'Esta imagem deve ser de aproximadamente 440x250px, embora seja escalada de forma flexível e cortada para caber na interface do usuário em diferentes cenários conforme necessário, então, dimensões atuais para exibição serão diferentes.', // Actions 'actions' => 'Ações', @@ -30,6 +30,8 @@ 'create' => 'Criar', 'update' => 'Atualizar', 'edit' => 'Editar', + 'archive' => 'Archive', + 'unarchive' => 'Un-Archive', 'sort' => 'Ordenar', 'move' => 'Mover', 'copy' => 'Copiar', @@ -107,4 +109,7 @@ // Not directly used but available for convenience to users. 'privacy_policy' => 'Política de Privacidade', 'terms_of_service' => 'Termos de Utilização', + + // OpenSearch + 'opensearch_description' => 'Procurar :appName', ]; diff --git a/lang/pt/editor.php b/lang/pt/editor.php index f7bc6c894cd..b60f813b51b 100644 --- a/lang/pt/editor.php +++ b/lang/pt/editor.php @@ -13,6 +13,7 @@ 'cancel' => 'Cancelar', 'save' => 'Guardar', 'close' => 'Fechar', + 'apply' => 'Apply', 'undo' => 'Anular', 'redo' => 'Refazer', 'left' => 'Esquerda', @@ -47,6 +48,7 @@ 'superscript' => 'Superior à linha', 'subscript' => 'Inferior à linha', 'text_color' => 'Cor do texto', + 'highlight_color' => 'Highlight color', 'custom_color' => 'Cor personalizada', 'remove_color' => 'Remover cor', 'background_color' => 'Cor de fundo', @@ -81,6 +83,9 @@ 'table_properties' => 'Propriedades da tabela', 'table_properties_title' => 'Propriedades da Tabela', 'delete_table' => 'Eliminar tabela', + 'table_clear_formatting' => 'Clear table formatting', + 'resize_to_contents' => 'Resize to contents', + 'row_header' => 'Row header', 'insert_row_before' => 'Inserir linha antes', 'insert_row_after' => 'Inserir linha depois', 'delete_row' => 'Eliminar linha', @@ -144,6 +149,7 @@ 'url' => 'URL', 'text_to_display' => 'Texto a ser exibido', 'title' => 'Título', + 'browse_links' => 'Browse links', 'open_link' => 'Abrir ligação', 'open_link_in' => 'Abrir ligação em...', 'open_link_current' => 'Janela atual', @@ -160,6 +166,8 @@ 'about' => 'Sobre o editor', 'about_title' => 'Sobre o Editor WYSIWYG', 'editor_license' => 'Editor da licença de direitos autorais', + 'editor_lexical_license' => 'This editor is built as a fork of :lexicalLink which is distributed under the MIT license.', + 'editor_lexical_license_link' => 'Full license details can be found here.', 'editor_tiny_license' => 'Este editor foi criado com :tinyLink que é fornecido sob a licença MIT.', 'editor_tiny_license_link' => 'Os dados relativos aos direitos de autor e à licença do TinyMCE podem ser encontrados aqui.', 'save_continue' => 'Salvar página e continuar', diff --git a/lang/pt/entities.php b/lang/pt/entities.php index b2171ba64fe..91fe6d90cd1 100644 --- a/lang/pt/entities.php +++ b/lang/pt/entities.php @@ -39,6 +39,34 @@ 'export_pdf' => 'Arquivo PDF', 'export_text' => 'Arquivo Texto', 'export_md' => 'Ficheiro Markdown', + 'export_zip' => 'Portable ZIP', + 'default_template' => 'Default Page Template', + 'default_template_explain' => 'Assign a page template that will be used as the default content for all pages created within this item. Keep in mind this will only be used if the page creator has view access to the chosen template page.', + 'default_template_select' => 'Select a template page', + 'import' => 'Import', + 'import_validate' => 'Validate Import', + 'import_desc' => 'Import books, chapters & pages using a portable zip export from the same, or a different, instance. Select a ZIP file to proceed. After the file has been uploaded and validated you\'ll be able to configure & confirm the import in the next view.', + '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_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_size' => ':size Import ZIP Size', + 'import_uploaded_at' => 'Uploaded :relativeTime', + 'import_uploaded_by' => 'Uploaded by', + 'import_location' => 'Import Location', + '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_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', + 'breadcrumb_siblings_for_book' => 'Navigate siblings for book', + 'breadcrumb_siblings_for_bookshelf' => 'Navigate siblings for shelf', // Permissions and restrictions 'permissions' => 'Permissões', @@ -132,9 +160,6 @@ 'books_edit_named' => 'Editar Livro :bookName', 'books_form_book_name' => 'Nome do Livro', 'books_save' => 'Guardar Livro', - 'books_default_template' => 'Default Page Template', - 'books_default_template_explain' => 'Assign a page template that will be used as the default content for all new pages in this book. Keep in mind this will only be used if the page creator has view access to those chosen template page.', - 'books_default_template_select' => 'Select a template page', 'books_permissions' => 'Permissões do Livro', 'books_permissions_updated' => 'Permissões do Livro Atualizadas', 'books_empty_contents' => 'Nenhuma página ou capítulo foram criados para este livro.', @@ -145,7 +170,9 @@ 'books_search_this' => 'Pesquisar neste livro', 'books_navigation' => 'Navegação do Livro', 'books_sort' => 'Ordenar Conteúdos do Livro', - 'books_sort_desc' => 'Mova capítulos e páginas de um livro para reorganizar o seu conteúdo. É possível acrescentar outros livros, o que permite uma movimentação fácil de capítulos e páginas entre livros.', + 'books_sort_desc' => 'Move chapters and pages within a book to reorganise its contents. Other books can be added which allows easy moving of chapters and pages between books. Optionally an auto sort rule can be set to automatically sort this book\'s contents upon changes.', + 'books_sort_auto_sort' => 'Auto Sort Option', + 'books_sort_auto_sort_active' => 'Auto Sort Active: :sortName', 'books_sort_named' => 'Ordenar Livro :bookName', 'books_sort_name' => 'Ordenar por Nome', 'books_sort_created' => 'Ordenar por Data de Criação', @@ -207,7 +234,7 @@ 'pages_delete_draft' => 'Eliminar Rascunho de Página', 'pages_delete_success' => 'Página eliminada', 'pages_delete_draft_success' => 'Rascunho de página eliminado', - 'pages_delete_warning_template' => 'This page is in active use as a book default page template. These books will no longer have a default page template assigned after this page is deleted.', + 'pages_delete_warning_template' => 'This page is in active use as a book or chapter default page template. These books or chapters will no longer have a default page template assigned after this page is deleted.', 'pages_delete_confirm' => 'Tem certeza que deseja eliminar a página?', 'pages_delete_draft_confirm' => 'Tem certeza que deseja eliminar o rascunho de página?', 'pages_editing_named' => 'A Editar a Página :pageName', @@ -224,6 +251,8 @@ 'pages_edit_switch_to_markdown_clean' => '(Conteúdo Limitado)', 'pages_edit_switch_to_markdown_stable' => '(Conteúdo Estável)', 'pages_edit_switch_to_wysiwyg' => 'Alternar para o editor WYSIWYG', + 'pages_edit_switch_to_new_wysiwyg' => 'Switch to new WYSIWYG', + 'pages_edit_switch_to_new_wysiwyg_desc' => '(In Beta Testing)', 'pages_edit_set_changelog' => 'Relatar Alterações', 'pages_edit_enter_changelog_desc' => 'Digite uma breve descrição das alterações efetuadas por si', 'pages_edit_enter_changelog' => 'Inserir Alterações', @@ -243,6 +272,7 @@ 'pages_md_insert_drawing' => 'Inserir Desenho', 'pages_md_show_preview' => 'Mostrar pré-visualização', 'pages_md_sync_scroll' => 'Sincronizar pré-visualização', + 'pages_md_plain_editor' => 'Plaintext editor', 'pages_drawing_unsaved' => 'Encontrado um rascunho não guardado', 'pages_drawing_unsaved_confirm' => 'Dados de um rascunho não guardado foi encontrado de um tentativa anteriormente falhada. Deseja restaurar e continuar a edição desse rascunho?', 'pages_not_in_chapter' => 'A página não está dentro de um capítulo', @@ -367,8 +397,11 @@ 'comment' => 'Comentário', 'comments' => 'Comentários', 'comment_add' => 'Adicionar Comentário', + 'comment_none' => 'No comments to display', 'comment_placeholder' => 'Digite aqui os seus comentários', - 'comment_count' => '{0} Nenhum comentário|{1} 1 Comentário|[2,*] :count Comentários', + 'comment_thread_count' => ':count Comment Thread|:count Comment Threads', + 'comment_archived_count' => ':count Archived', + 'comment_archived_threads' => 'Archived Threads', 'comment_save' => 'Guardar comentário', 'comment_new' => 'Comentário Novo', 'comment_created' => 'comentado :createDiff', @@ -377,8 +410,14 @@ 'comment_deleted_success' => 'Comentário removido', 'comment_created_success' => 'Comentário adicionado', 'comment_updated_success' => 'Comentário editado', + 'comment_archive_success' => 'Comment archived', + 'comment_unarchive_success' => 'Comment un-archived', + 'comment_view' => 'View comment', + 'comment_jump_to_thread' => 'Jump to thread', 'comment_delete_confirm' => 'Tem a certeza de que deseja eliminar este comentário?', 'comment_in_reply_to' => 'Em resposta à :commentId', + 'comment_reference' => 'Reference', + 'comment_reference_outdated' => '(Outdated)', 'comment_editor_explain' => 'Aqui estão os comentários que foram deixados nesta página. Comentários podem ser adicionados e geridos ao visualizar a página guardada.', // Revision diff --git a/lang/pt/errors.php b/lang/pt/errors.php index 34613872ef5..b337005e14f 100644 --- a/lang/pt/errors.php +++ b/lang/pt/errors.php @@ -10,6 +10,7 @@ // Auth 'error_user_exists_different_creds' => 'Um utilizador com o endereço de e-mail :email já existe mas com credenciais diferentes.', + 'auth_pre_register_theme_prevention' => 'User account could not be registered for the provided details', 'email_already_confirmed' => 'E-mail já foi confirmado. Tente iniciar sessão.', 'email_confirmation_invalid' => 'Este token de confirmação não é válido ou já foi utilizado. Por favor, tente registar-se novamente.', 'email_confirmation_expired' => 'O token de confirmação já expirou. Um novo e-mail foi enviado.', @@ -23,7 +24,6 @@ 'saml_invalid_response_id' => 'A requisição do sistema de autenticação externa não foi reconhecia por um processo iniciado por esta aplicação. Navegar para o caminho anterior após o inicio de sessão pode provocar este problema.', 'saml_fail_authed' => 'Inicio de sessão com :system falhou. O sistema não forneceu uma autorização bem sucedida', 'oidc_already_logged_in' => 'Sessão já iniciada', - 'oidc_user_not_registered' => 'O utilizador :name não está registado e o registo automático está desativado', 'oidc_no_email_address' => 'Não foi possível encontrar um endereço de e-mail para este utilizador nos dados providenciados pelo sistema de autenticação externo', 'oidc_fail_authed' => 'Inicio de sessão com :system falhou. O sistema não forneceu uma autorização bem sucedida', 'social_no_action_defined' => 'Nenhuma ação definida', @@ -37,6 +37,7 @@ 'social_driver_not_found' => 'Social driver não encontrado', 'social_driver_not_configured' => 'Os seus parâmetros sociais de :socialAccount não estão corretamente configurados.', 'invite_token_expired' => 'Este link de convite expirou. Alternativamente, pode tentar redefinir a senha da sua conta.', + 'login_user_not_found' => 'A user for this action could not be found.', // System 'path_not_writable' => 'O caminho do arquivo :filePath não pôde ser carregado. Certifique-se de que tem permissões de escrita no servidor.', @@ -77,6 +78,7 @@ // Users 'users_cannot_delete_only_admin' => 'Não pode excluir o único administrador', 'users_cannot_delete_guest' => 'Não pode excluir o usuário convidado', + 'users_could_not_send_invite' => 'Could not create user since invite email failed to send', // Roles 'role_cannot_be_edited' => 'Este cargo não pode ser editado', @@ -103,6 +105,18 @@ 'app_down' => ':appName está fora do ar de momento', 'back_soon' => 'Voltaremos em breve.', + // Import + 'import_zip_cant_read' => 'Could not read ZIP file.', + 'import_zip_cant_decode_data' => 'Could not find and decode ZIP data.json content.', + 'import_zip_no_data' => 'ZIP file data has no expected book, chapter or page content.', + 'import_validation_failed' => 'Import ZIP failed to validate with errors:', + 'import_zip_failed_notification' => 'Failed to import ZIP file.', + 'import_perms_books' => 'You are lacking the required permissions to create books.', + 'import_perms_chapters' => 'You are lacking the required permissions to create chapters.', + 'import_perms_pages' => 'You are lacking the required permissions to create pages.', + 'import_perms_images' => 'You are lacking the required permissions to create images.', + 'import_perms_attachments' => 'You are lacking the required permission to create attachments.', + // API errors 'api_no_authorization_found' => 'Nenhum token de autorização encontrado na requisição', 'api_bad_authorization_format' => 'Um token de autorização foi encontrado na requisição, mas o formato parece incorreto', diff --git a/lang/pt/preferences.php b/lang/pt/preferences.php index ae7e2cc4318..860eec645bc 100644 --- a/lang/pt/preferences.php +++ b/lang/pt/preferences.php @@ -17,17 +17,17 @@ 'shortcuts_save' => 'Salvar Atalhos', 'shortcuts_overlay_desc' => 'Nota: Quando os atalhos estão ativados, um balão de ajuda ficará disponível pressionando "?" destacando os atalhos disponíveis para ações atualmente visíveis na tela.', 'shortcuts_update_success' => 'As suas preferências de atalhos foram guardadas!', - 'shortcuts_overview_desc' => 'Manage keyboard shortcuts you can use to navigate the system user interface.', + 'shortcuts_overview_desc' => 'Gerir atalhos do teclado que podem ser usados para navegar pela interface do utilizador.', 'notifications' => 'Preferências das Notificações', - 'notifications_desc' => 'Control the email notifications you receive when certain activity is performed within the system.', - 'notifications_opt_own_page_changes' => 'Notify upon changes to pages I own', - 'notifications_opt_own_page_comments' => 'Notify upon comments on pages I own', - 'notifications_opt_comment_replies' => 'Notify upon replies to my comments', - 'notifications_save' => 'Save Preferences', - 'notifications_update_success' => 'Notification preferences have been updated!', - 'notifications_watched' => 'Watched & Ignored Items', - 'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.', + 'notifications_desc' => 'Controlar as notificações via correio eletrónico quando certas atividades são executadas pelo sistema.', + 'notifications_opt_own_page_changes' => 'Notificar quando páginas que possuo sofrem alterações', + 'notifications_opt_own_page_comments' => 'Notificar quando comentam páginas que possuo', + 'notifications_opt_comment_replies' => 'Notificar respostas aos meus comentários', + 'notifications_save' => 'Guardar preferências', + 'notifications_update_success' => 'Preferências de notificação foram atualizadas!', + 'notifications_watched' => 'Itens assistidos e ignorados', + 'notifications_watched_desc' => 'Abaixo estão os itens que possuem preferências de seguir personalizadas aplicadas. Para atualizar suas preferências para estes, veja o item e encontre as opções de seguir na barra lateral.', 'auth' => 'Acesso e Segurança', 'auth_change_password' => 'Alterar Palavra-passe', @@ -35,13 +35,13 @@ 'auth_change_password_success' => 'A palavra-passe foi atualizada!', 'profile' => 'Detalhes Do Perfil', - 'profile_desc' => 'Manage the details of your account which represents you to other users, in addition to details that are used for communication and system personalisation.', + 'profile_desc' => 'Gerencie os detalhes de sua conta que o representam para outros usuários, além de detalhes que são usados para personalização do sistema e comunicação.', 'profile_view_public' => 'Visualizar Perfil Público', - 'profile_name_desc' => 'Configure your display name which will be visible to other users in the system through the activity you perform, and content you own.', - 'profile_email_desc' => 'This email will be used for notifications and, depending on active system authentication, system access.', - 'profile_email_no_permission' => 'Unfortunately you don\'t have permission to change your email address. If you want to change this, you\'d need to ask an administrator to change this for you.', - 'profile_avatar_desc' => 'Select an image which will be used to represent yourself to others in the system. Ideally this image should be square and about 256px in width and height.', - 'profile_admin_options' => 'Administrator Options', + 'profile_name_desc' => 'Configure o seu nome de exibição que será visível para outros usuários no sistema através da atividade que você executa e do conteúdo você tem.', + 'profile_email_desc' => 'Este e-mail será usado para notificações e, dependendo da autenticação ativa do sistema, acesso do sistema.', + 'profile_email_no_permission' => 'Infelizmente você não tem permissão para alterar seu correio eletrônico. Se você quiser mudar isso, você precisa pedir a um administrador para alterar por você.', + 'profile_avatar_desc' => 'Selecione uma imagem que será usada para lhe representar aos outros usuários do sistema. Idealmente, esta imagem deve ser quadrada e sobre 256px em largura e altura.', + 'profile_admin_options' => 'Opções de administrador', 'profile_admin_options_desc' => 'Additional administrator-level options, like those to manage role assignments, can be found for your user account in the "Settings > Users" area of the application.', 'delete_account' => 'Excluir Conta', diff --git a/lang/pt/settings.php b/lang/pt/settings.php index 94da707f389..0c765fb9851 100644 --- a/lang/pt/settings.php +++ b/lang/pt/settings.php @@ -74,6 +74,36 @@ 'reg_confirm_restrict_domain_desc' => 'Entre com uma lista separada por vírgulas de domínios de e-mails aos quais você deseja restringir o registo. Um e-mail de confirmação será enviado para o utilizador validar o seu respetivo endereço de e-mail antes de ser permitida a interação com a aplicação.
Note que os utilizadores serão capazes de alterar os seus endereços de e-mail após o sucesso na confirmação do registo.', 'reg_confirm_restrict_domain_placeholder' => 'Nenhuma restrição definida', + // Sorting Settings + 'sorting' => 'Sorting', + 'sorting_book_default' => 'Default Book Sort', + 'sorting_book_default_desc' => 'Select the default sort rule to apply to new books. This won\'t affect existing books, and can be overridden per-book.', + 'sorting_rules' => 'Sort Rules', + 'sorting_rules_desc' => 'These are predefined sorting operations which can be applied to content in the system.', + 'sort_rule_assigned_to_x_books' => 'Assigned to :count Book|Assigned to :count Books', + 'sort_rule_create' => 'Create Sort Rule', + 'sort_rule_edit' => 'Edit Sort Rule', + 'sort_rule_delete' => 'Delete Sort Rule', + 'sort_rule_delete_desc' => 'Remove this sort rule from the system. Books using this sort will revert to manual sorting.', + 'sort_rule_delete_warn_books' => 'This sort rule is currently used on :count book(s). Are you sure you want to delete this?', + 'sort_rule_delete_warn_default' => 'This sort rule is currently used as the default for books. Are you sure you want to delete this?', + 'sort_rule_details' => 'Sort Rule Details', + 'sort_rule_details_desc' => 'Set a name for this sort rule, which will appear in lists when users are selecting a sort.', + 'sort_rule_operations' => 'Sort Operations', + 'sort_rule_operations_desc' => 'Configure the sort actions to be performed by moving them from the list of available operations. Upon use, the operations will be applied in order, from top to bottom. Any changes made here will be applied to all assigned books upon save.', + 'sort_rule_available_operations' => 'Available Operations', + 'sort_rule_available_operations_empty' => 'No operations remaining', + 'sort_rule_configured_operations' => 'Configured Operations', + 'sort_rule_configured_operations_empty' => 'Drag/add operations from the "Available Operations" list', + 'sort_rule_op_asc' => '(Asc)', + 'sort_rule_op_desc' => '(Desc)', + 'sort_rule_op_name' => 'Name - Alphabetical', + 'sort_rule_op_name_numeric' => '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_last' => 'Chapters Last', + // Maintenance settings 'maint' => 'Manutenção', 'maint_image_cleanup' => 'Limpeza de Imagens', @@ -109,7 +139,7 @@ 'recycle_bin_contents_empty' => 'A reciclagem está atualmente vazia', 'recycle_bin_empty' => 'Esvaziar Reciclagem', 'recycle_bin_empty_confirm' => 'Isto irá destruir permanentemente todos os itens na reciclagem inclusive o conteúdo de cada item. Tem certeza de que a deseja esvaziar?', - 'recycle_bin_destroy_confirm' => 'Esta ação irá excluir permanentemente do sistema este item junto com todos os elementos filhos listados abaixo. Não poderá restaurar este conteúdo. Tem certeza de que deseja excluir permanentemente este item?', + 'recycle_bin_destroy_confirm' => 'This action will permanently delete this item from the system, along with any child elements listed below, and you will not be able to restore this content. Are you sure you want to permanently delete this item?', 'recycle_bin_destroy_list' => 'Itens a serem Destruídos', 'recycle_bin_restore_list' => 'Itens a serem Restaurados', 'recycle_bin_restore_confirm' => 'Esta ação irá restaurar o item excluído, inclusive quaisquer elementos filhos, para o seu local original. Se a localização original tiver, entretanto, sido eliminada e estiver agora na reciclagem, o item pai também precisará de ser restaurado.', @@ -162,6 +192,7 @@ 'role_access_api' => 'Aceder à API do sistema', 'role_manage_settings' => 'Gerir as configurações da aplicação', 'role_export_content' => 'Exportar conteúdo', + 'role_import_content' => 'Import content', 'role_editor_change' => 'Alterar editor de página', 'role_notifications' => 'Receive & manage notifications', 'role_asset' => 'Permissões de Ativos', @@ -276,6 +307,14 @@ 'webhooks_last_errored' => 'Último erro:', 'webhooks_last_error_message' => 'Última mensagem de erro:', + // Licensing + 'licenses' => 'Licenses', + 'licenses_desc' => 'This page details license information for BookStack in addition to the projects & libraries that are used within BookStack. Many projects listed may only be used in a development context.', + 'licenses_bookstack' => 'BookStack License', + 'licenses_php' => 'PHP Library Licenses', + 'licenses_js' => 'JavaScript Library Licenses', + 'licenses_other' => 'Other Licenses', + 'license_details' => 'License Details', //! If editing translations files directly please ignore this in all //! languages apart from en. Content will be auto-copied from en. @@ -287,6 +326,7 @@ 'bs' => 'Bosanski', 'ca' => 'Catalão', 'cs' => 'Česky', + 'cy' => 'Cymraeg', 'da' => 'Dansk', 'de' => 'Deutsch (Sie)', 'de_informal' => 'Deutsch (Du)', @@ -308,6 +348,7 @@ 'lt' => 'Lietuvių Kalba', 'lv' => 'Latviešu Valoda', 'nb' => 'Norsk (Bokmål)', + 'ne' => 'नेपाली', 'nn' => 'Nynorsk', 'nl' => 'Nederlands', 'pl' => 'Polski', diff --git a/lang/pt/validation.php b/lang/pt/validation.php index d98f9857eb2..df414c992e2 100644 --- a/lang/pt/validation.php +++ b/lang/pt/validation.php @@ -105,6 +105,11 @@ 'url' => 'O formato da URL :attribute é inválido.', 'uploaded' => 'O arquivo não pôde ser carregado. O servidor pode não aceitar arquivos deste tamanho.', + 'zip_file' => 'The :attribute needs to reference a file within the ZIP.', + 'zip_file_mime' => 'The :attribute needs to reference a file of type :validTypes, found :foundType.', + 'zip_model_expected' => 'Data object expected but ":type" found.', + 'zip_unique' => 'The :attribute must be unique for the object type within the ZIP.', + // Custom validation lines 'custom' => [ 'password-confirm' => [ diff --git a/lang/pt_BR/activities.php b/lang/pt_BR/activities.php index 650782b1db8..2a2d173c790 100644 --- a/lang/pt_BR/activities.php +++ b/lang/pt_BR/activities.php @@ -31,7 +31,7 @@ 'book_create' => 'criou o livro', 'book_create_notification' => 'Livro criado com sucesso', 'book_create_from_chapter' => 'capítulo convertido em livro', - 'book_create_from_chapter_notification' => 'Capítulo convertido com sucesso em um livro', + 'book_create_from_chapter_notification' => 'Capítulo convertido em livro com sucesso', 'book_update' => 'atualizou o livro', 'book_update_notification' => 'Livro atualizado com sucesso', 'book_delete' => 'excluiu o livro', @@ -40,14 +40,14 @@ 'book_sort_notification' => 'Livro reordenado com sucesso', // Bookshelves - 'bookshelf_create' => 'prateleira criada', - 'bookshelf_create_notification' => 'Prateleira criada com sucesso', + 'bookshelf_create' => 'estante criada', + 'bookshelf_create_notification' => 'Estante criada com sucesso', 'bookshelf_create_from_book' => 'livro convertido em estante', - 'bookshelf_create_from_book_notification' => 'Capítulo convertido com sucesso em um livro', - 'bookshelf_update' => 'prateleira atualizada', - 'bookshelf_update_notification' => 'Prateleira atualizada com sucesso', - 'bookshelf_delete' => 'prateleira excluída', - 'bookshelf_delete_notification' => 'Prateleira excluída com sucesso', + 'bookshelf_create_from_book_notification' => 'Livro convertido com sucesso em uma estante', + 'bookshelf_update' => 'estante atualizada', + 'bookshelf_update_notification' => 'Estante atualizada com sucesso', + 'bookshelf_delete' => 'estante excluída', + 'bookshelf_delete_notification' => 'Estante excluída com sucesso', // Revisions 'revision_restore' => 'revisão restaurada', @@ -84,6 +84,14 @@ 'webhook_delete' => 'webhook excluído', 'webhook_delete_notification' => 'Webhook excluido com sucesso', + // Imports + 'import_create' => 'importação criada', + 'import_create_notification' => 'Importação carregada com sucesso', + 'import_run' => 'importação atualizada', + 'import_run_notification' => 'Conteúdo importado com sucesso', + 'import_delete' => 'importação excluída', + 'import_delete_notification' => 'Importação excluída com sucesso', + // Users 'user_create' => 'usuário criado', 'user_create_notification' => 'Usuário criado com sucesso', @@ -93,17 +101,17 @@ 'user_delete_notification' => 'Usuário removido com sucesso', // API Tokens - 'api_token_create' => 'token de api criado', + 'api_token_create' => 'token de API criado', 'api_token_create_notification' => 'Token de API criado com sucesso', 'api_token_update' => 'token de API atualizado', 'api_token_update_notification' => 'Token de API atualizado com sucesso', - 'api_token_delete' => 'token de api excluído', + 'api_token_delete' => 'token de API excluído', 'api_token_delete_notification' => 'Token de API excluído com sucesso', // Roles - 'role_create' => 'função criada', + 'role_create' => 'perfil criado', 'role_create_notification' => 'Perfil criado com sucesso', - 'role_update' => 'função atualizada', + 'role_update' => 'perfil atualizado', 'role_update_notification' => 'Perfil atualizado com sucesso', 'role_delete' => 'Excluir papel', 'role_delete_notification' => 'Perfil excluído com sucesso', @@ -119,6 +127,14 @@ 'comment_update' => 'Atualizar descrição', 'comment_delete' => 'Comentário deletado', + // Sort Rules + 'sort_rule_create' => 'criou regra de ordenação', + 'sort_rule_create_notification' => 'Regra de ordenação criada com sucesso', + 'sort_rule_update' => 'atualizou regra de ordenação', + 'sort_rule_update_notification' => 'Regra de ordenação atualizada com sucesso', + 'sort_rule_delete' => 'excluiu regra de ordenação', + 'sort_rule_delete_notification' => 'Regra de ordenação excluída com sucesso', + // Other 'permissions_update' => 'atualizou permissões', ]; diff --git a/lang/pt_BR/auth.php b/lang/pt_BR/auth.php index 71d8b73a2b3..b5fd974aa20 100644 --- a/lang/pt_BR/auth.php +++ b/lang/pt_BR/auth.php @@ -91,7 +91,7 @@ 'mfa_option_totp_title' => 'Aplicativo Móvel', 'mfa_option_totp_desc' => 'Para usar a autenticação multi-fator, você precisará de um aplicativo móvel que suporte TOTP como o Google Authenticator, Authy ou o Microsoft Authenticator.', 'mfa_option_backup_codes_title' => 'Códigos de backup', - 'mfa_option_backup_codes_desc' => 'Armazene com segurança um conjunto de códigos de backup únicos que você pode inserir para verificar sua identidade.', + 'mfa_option_backup_codes_desc' => 'Gera um conjunto de códigos de backup de uso único que você inserirá no login para verificar sua identidade. Certifique-se de armazená-los em um local seguro e protegido.', 'mfa_gen_confirm_and_enable' => 'Confirmar e habilitar', 'mfa_gen_backup_codes_title' => 'Configuração dos Códigos de Backup', 'mfa_gen_backup_codes_desc' => 'Armazene a lista de códigos abaixo em um lugar seguro. Ao acessar o sistema você poderá usar um dos códigos como segundo mecanismo de autenticação.', diff --git a/lang/pt_BR/common.php b/lang/pt_BR/common.php index 5e3f7611407..b4ff95e99ed 100644 --- a/lang/pt_BR/common.php +++ b/lang/pt_BR/common.php @@ -12,24 +12,31 @@ 'save' => 'Salvar', 'continue' => 'Continuar', 'select' => 'Selecionar', - 'toggle_all' => 'Alternar Todos', - 'more' => 'Mais', + 'toggle_all' => 'Alternar Tudo', + 'more' => 'Mais +', // Form Labels - 'name' => 'Nome', - 'description' => 'Descrição', - 'role' => 'Cargo', - 'cover_image' => 'Imagem de capa', - 'cover_image_description' => 'Esta imagem deve ser aproximadamente 440x250px.', + 'name' => 'Nome +', + 'description' => 'Descrição +', + 'role' => 'Perfil', + 'cover_image' => 'Imagem da capa', + 'cover_image_description' => 'Esta imagem deve ter cerca de 440x250px, embora seja dimensionada e cortada de forma flexível para se ajustar à "interface" do usuário em diferentes cenários, conforme necessário, portanto, as dimensões reais para exibição serão diferentes.', // Actions - 'actions' => 'Ações', + 'actions' => 'Ações +', 'view' => 'Visualizar', - 'view_all' => 'Visualizar Tudo', + 'view_all' => 'Visualizar Tudo +', 'new' => 'Novo', 'create' => 'Criar', 'update' => 'Atualizar', 'edit' => 'Editar', + 'archive' => 'Arquivar', + 'unarchive' => 'Desarquivar', 'sort' => 'Ordenar', 'move' => 'Mover', 'copy' => 'Copiar', @@ -44,13 +51,13 @@ 'configure' => 'Configurar', 'manage' => 'Administrar', 'fullscreen' => 'Tela cheia', - 'favourite' => 'Favoritos', + 'favourite' => 'Favorito', 'unfavourite' => 'Remover dos Favoritos', 'next' => 'Seguinte', 'previous' => 'Anterior', 'filter_active' => 'Filtro Ativo:', 'filter_clear' => 'Limpar Filtro', - 'download' => 'Baixar', + 'download' => 'Baixar ', 'open_in_tab' => 'Abrir na aba', 'open' => 'Abrir', @@ -107,4 +114,7 @@ // Not directly used but available for convenience to users. 'privacy_policy' => 'Políticas de Privacidade', 'terms_of_service' => 'Termos de Serviço', + + // OpenSearch + 'opensearch_description' => 'Procurar: nome do aplicativo', ]; diff --git a/lang/pt_BR/editor.php b/lang/pt_BR/editor.php index 71e284475b6..9a0eb45c9fe 100644 --- a/lang/pt_BR/editor.php +++ b/lang/pt_BR/editor.php @@ -13,6 +13,7 @@ 'cancel' => 'Cancelar', 'save' => 'Salvar', 'close' => 'Fechar', + 'apply' => 'Aplicar', 'undo' => 'Desfazer', 'redo' => 'Refazer', 'left' => 'Esquerda', @@ -31,7 +32,7 @@ 'header_large' => 'Cabeçalho Grande', 'header_medium' => 'Cabeçalho Médio', 'header_small' => 'Cabeçalho Pequeno', - 'header_tiny' => 'Cabeçalho Pequeno', + 'header_tiny' => 'Cabeçalho Minúsculo', 'paragraph' => 'Parágrafo', 'blockquote' => 'Bloco de Citação', 'inline_code' => 'Código embutido', @@ -40,13 +41,14 @@ 'callout_success' => 'Sucesso', 'callout_warning' => 'Atenção', 'callout_danger' => 'Perigo', - 'bold' => 'Bold', + 'bold' => 'Negrito', 'italic' => 'Itálico', - 'underline' => 'Sublinhar', + 'underline' => 'Sublinhado', 'strikethrough' => 'Riscado', - 'superscript' => 'Superinscrição', - 'subscript' => 'Subscrição', + 'superscript' => 'Sobrescrito', + 'subscript' => 'Subscrito', 'text_color' => 'Cor do texto', + 'highlight_color' => 'Cor de destaque', 'custom_color' => 'Cor personalizada', 'remove_color' => 'Remover cor', 'background_color' => 'Cor de fundo', @@ -59,16 +61,16 @@ 'list_task' => 'Lista de tarefas', 'indent_increase' => 'Aumentar recuo', 'indent_decrease' => 'Diminuir recuo', - 'table' => 'Mesa', + 'table' => 'Tabela', 'insert_image' => 'Inserir Imagem', 'insert_image_title' => 'Inserir/Editar imagem', 'insert_link' => 'Inserir/editar link', 'insert_link_title' => 'Inserir/Editar link', 'insert_horizontal_line' => 'Insert horizontal line', - 'insert_code_block' => 'Inserir o bloco de endereço', + 'insert_code_block' => 'Inserir/editar bloco de código', 'edit_code_block' => 'Editar bloco de código', - 'insert_drawing' => 'Inserir/editar desenho', - 'drawing_manager' => 'Gerente de desenho', + 'insert_drawing' => 'Inserir/editar diagrama', + 'drawing_manager' => 'Gerenciador de diagramas', 'insert_media' => 'Inserir/editar mídia', 'insert_media_title' => 'Inserir/Editar Mídia', 'clear_formatting' => 'Limpar formatação', @@ -81,13 +83,16 @@ 'table_properties' => 'Propriedades da tabela', 'table_properties_title' => 'Propriedades da Tabela', 'delete_table' => 'Excluir Tabela', + 'table_clear_formatting' => 'Limpar formatação de tabela', + 'resize_to_contents' => 'Redimensionar para o conteúdo', + 'row_header' => 'Cabeçalho da linha', 'insert_row_before' => 'Inserir linha antes', 'insert_row_after' => 'Inserir linha depois', 'delete_row' => 'Excluir linha', 'insert_column_before' => 'Inserir coluna antes', 'insert_column_after' => 'Inserir coluna depois', 'delete_column' => 'Excluir coluna', - 'table_cell' => 'Celular', + 'table_cell' => 'Célula', 'table_row' => 'Linha', 'table_column' => 'Coluna', 'cell_properties' => 'Propriedades da célula', @@ -144,6 +149,7 @@ 'url' => 'URL', 'text_to_display' => 'Texto de exibição', 'title' => 'Título', + 'browse_links' => 'Procurar links', 'open_link' => 'Link aberto', 'open_link_in' => 'Abrir link em...', 'open_link_current' => 'Janelas atuais', @@ -160,9 +166,11 @@ 'about' => 'Sobre o editor', 'about_title' => 'Sobre o Editor WYSIWYG', 'editor_license' => 'Licença do Editor e Direitos Autorais', + 'editor_lexical_license' => 'Este editor é criado como uma bifurcação de :lexicalLink distribuído sob a licença MIT.', + 'editor_lexical_license_link' => 'Aqui podem ser encontrados detalhes da licença.', 'editor_tiny_license' => 'Este editor é construído usando :tinyLink que é fornecido sob a licença MIT.', 'editor_tiny_license_link' => 'Os dados relativos aos direitos de autor e à licença do TinyMCE podem ser encontrados aqui.', - 'save_continue' => 'Salvar e continuar', + 'save_continue' => 'Salvar Página e Continuar', 'callouts_cycle' => '(Continue pressionando para alternar através de tipos)', 'link_selector' => 'Link para conteúdo', 'shortcuts' => 'Atalhos', diff --git a/lang/pt_BR/entities.php b/lang/pt_BR/entities.php index 6e32f280170..fecd47e8227 100644 --- a/lang/pt_BR/entities.php +++ b/lang/pt_BR/entities.php @@ -6,111 +6,139 @@ return [ // Shared - 'recently_created' => 'Criados Recentemente', - 'recently_created_pages' => 'Páginas Criadas Recentemente', - 'recently_updated_pages' => 'Páginas Atualizadas Recentemente', - 'recently_created_chapters' => 'Capítulos Criados Recentemente', - 'recently_created_books' => 'Livros Criados Recentemente', - 'recently_created_shelves' => 'Prateleiras Criadas Recentemente', - 'recently_update' => 'Atualizados Recentemente', - 'recently_viewed' => 'Visualizados Recentemente', - 'recent_activity' => 'Atividade Recente', - 'create_now' => 'Criar um agora', + 'recently_created' => 'Criado Recentemente', + 'recently_created_pages' => 'Páginas Recentemente Criadas', + 'recently_updated_pages' => 'Páginas Recentemente Atualizadas', + 'recently_created_chapters' => 'Capítulos Recentemente Criados', + 'recently_created_books' => 'Livros Recentemente Criados', + 'recently_created_shelves' => 'Estantes Criadas Recentemente', + 'recently_update' => 'Recentemente Atualizado', + 'recently_viewed' => 'Recentemente Visualizado', + 'recent_activity' => 'Atividades recentes', + 'create_now' => 'Criar agora', 'revisions' => 'Revisões', 'meta_revision' => 'Revisão #:revisionCount', 'meta_created' => 'Criado :timeLength', 'meta_created_name' => 'Criado :timeLength por :user', 'meta_updated' => 'Atualizado :timeLength', - 'meta_updated_name' => 'Atualizado :timeLength por :user', - 'meta_owned_name' => 'De :user', - 'meta_reference_count' => 'Referenced by :count item|Referenced by :count items', - 'entity_select' => 'Seleção de Entidade', - 'entity_select_lack_permission' => 'Você não tem as permissões necessárias para selecionar este item', + 'meta_updated_name' => 'Atualizado: :timeLength por :user', + 'meta_owned_name' => 'Propriedade de :user', + 'meta_reference_count' => 'Referenciado por :count item|Referenciado por :count itens', + 'entity_select' => 'Seleção de entidade', + 'entity_select_lack_permission' => 'Você não tem as permissões necessárias para selecionar este ‘item’', 'images' => 'Imagens', 'my_recent_drafts' => 'Meus Rascunhos Recentes', - 'my_recently_viewed' => 'Visualizados por mim Recentemente', + 'my_recently_viewed' => 'Visualizados Recentemente', 'my_most_viewed_favourites' => 'Meus Favoritos Mais Visualizados', - 'my_favourites' => 'Meus Favoritos', + 'my_favourites' => 'Favoritos', 'no_pages_viewed' => 'Você não visualizou nenhuma página', - 'no_pages_recently_created' => 'Nenhuma página criada recentemente', - 'no_pages_recently_updated' => 'Nenhuma página atualizada recentemente', + 'no_pages_recently_created' => 'Nenhuma página foi criada recentemente', + 'no_pages_recently_updated' => 'Nenhuma página foi atualizada recentemente', 'export' => 'Exportar', - 'export_html' => 'Arquivo Web Contained', + 'export_html' => 'Arquivo Web Contido', 'export_pdf' => 'Arquivo PDF', - 'export_text' => 'Arquivo Texto', - 'export_md' => 'Arquivos para remarcar', + 'export_text' => 'Arquivo de texto simples', + 'export_md' => 'Arquivo de redução', + 'export_zip' => 'ZIP portátil', + 'default_template' => 'Modelo padrão de página', + 'default_template_explain' => 'Atribuir o modelo de página que será usado como padrão para todas as páginas criadas neste livro. Tenha em mente que isto será usado apenas se o criador da página tiver acesso de visualização ao modelo de página escolhido.', + 'default_template_select' => 'Selecione uma página de modelo', + 'import' => 'Importação', + 'import_validate' => 'Validar Importação', + 'import_desc' => 'Importar livros, capítulos e páginas usando uma exportação zip portátil da mesma ou de uma instância diferente. Selecione um arquivo ZIP para prosseguir. Após a carga e validação do arquivo, você será capaz de configurar e confirmar a importação na próxima visualização.', + 'import_zip_select' => 'Selecione o arquivo ZIP para carregar', + 'import_zip_validation_errors' => 'Foram detectados erros ao validar o arquivo ZIP:', + 'import_pending' => 'Importações pendentes', + 'import_pending_none' => 'Nenhuma importação foi iniciada.', + 'import_continue' => 'Continuar a importação', + 'import_continue_desc' => 'Revise o conteúdo que deve ser importado do arquivo ZIP carregado. Quando estiver pronto, execute a importação para adicionar seu conteúdo a este sistema. O arquivo ZIP importado será automaticamente removido quando a importação for bem sucedida.', + 'import_details' => 'Detalhes da importação', + 'import_run' => 'Executar Importação', + 'import_size' => ':size Tamanho do ZIP', + 'import_uploaded_at' => 'Carregado :relativeTime', + 'import_uploaded_by' => 'Enviado por', + 'import_location' => 'Local da importação', + 'import_location_desc' => 'Selecione um local para o conteúdo importado. Você precisa das permissões necessárias para criar no local escolhido.', + 'import_delete_confirm' => 'Tem certeza que deseja excluir esta importação?', + 'import_delete_desc' => 'Isto irá excluir o arquivo ZIP de importação carregado e não poderá ser desfeito.', + 'import_errors' => 'Erros de importação', + 'import_errors_desc' => 'Os seguintes erros ocorreram durante a tentativa de importação:', + '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', // Permissions and restrictions 'permissions' => 'Permissões', - 'permissions_desc' => 'Defina as permissões aqui para substituir as permissões padrão fornecidas pelas funções do usuário.', - 'permissions_book_cascade' => 'As permissões definidas em livros serão automaticamente em cascata para capítulos e páginas filho, a menos que tenham suas próprias permissões definidas.', - 'permissions_chapter_cascade' => 'As permissões definidas nos capítulos serão automaticamente em cascata para as páginas filhas, a menos que tenham suas próprias permissões definidas.', - 'permissions_save' => 'Salvar Permissões', - 'permissions_owner' => 'Proprietário', - 'permissions_role_everyone_else' => 'Todos os outros', - 'permissions_role_everyone_else_desc' => 'Defina permissões para todas as funções não especificamente substituídas.', - 'permissions_role_override' => 'Substituir permissões para função', - 'permissions_inherit_defaults' => 'Herdar padrões', + 'permissions_desc' => 'Defina permissões aqui para substituir as permissões padrão fornecidas pelo perfil do usuário.', + 'permissions_book_cascade' => 'As permissões definidas nos livros serão automaticamente transmitidas aos capítulos e páginas secundários, a menos que eles tenham suas próprias permissões definidas.', + 'permissions_chapter_cascade' => 'As permissões definidas nos capítulos serão automaticamente transmitidas às páginas secundárias, a menos que elas tenham suas próprias permissões definidas.', + 'permissions_save' => 'Salvar permissões', + 'permissions_owner' => 'Dono', + 'permissions_role_everyone_else' => 'Todos os Outros', + 'permissions_role_everyone_else_desc' => 'Defina permissões para todos os perfis não substituídas especificamente.', + 'permissions_role_override' => 'Substituir permissões para o perfil', + 'permissions_inherit_defaults' => 'Herdar Padrões', // Search 'search_results' => 'Resultado(s) da Pesquisa', 'search_total_results_found' => ':count resultado encontrado|:count resultados encontrados', 'search_clear' => 'Limpar Pesquisa', - 'search_no_pages' => 'Nenhuma página corresponde à pesquisa', + 'search_no_pages' => 'Nenhuma página corresponde a esta pesquisa', 'search_for_term' => 'Pesquisar por :term', - 'search_more' => 'Mais Resultados', - 'search_advanced' => 'Pesquisa Avançada', - 'search_terms' => 'Termos da Pesquisa', - 'search_content_type' => 'Tipo de Conteúdo', - 'search_exact_matches' => 'Correspondências Exatas', - 'search_tags' => 'Persquisar Tags', + 'search_more' => 'Mais resultados', + 'search_advanced' => 'Pesquisa avançada', + 'search_terms' => 'Termos da pesquisa', + 'search_content_type' => 'Categoria de conteúdo', + 'search_exact_matches' => 'Correspondências exatas', + 'search_tags' => 'Pesquisar marcadores', 'search_options' => 'Opções', - 'search_viewed_by_me' => 'Visualizado por mim', - 'search_not_viewed_by_me' => 'Não visualizado por mim', - 'search_permissions_set' => 'Permissão definida', - 'search_created_by_me' => 'Criado por mim', - 'search_updated_by_me' => 'Atualizado por mim', - 'search_owned_by_me' => 'Possuído por mim', + 'search_viewed_by_me' => 'Visto por mim', + 'search_not_viewed_by_me' => 'Não visto por mim', + 'search_permissions_set' => 'Permissões definidas', + 'search_created_by_me' => 'Criados por mim', + 'search_updated_by_me' => 'Atualizados por mim', + 'search_owned_by_me' => 'Meus itens', 'search_date_options' => 'Opções de Data', - 'search_updated_before' => 'Atualizado antes de', - 'search_updated_after' => 'Atualizado depois de', - 'search_created_before' => 'Criado antes de', - 'search_created_after' => 'Criado depois de', + 'search_updated_before' => 'Atualizado antes', + 'search_updated_after' => 'Atualizado depois', + 'search_created_before' => 'Criado antes', + 'search_created_after' => 'Criado depois', 'search_set_date' => 'Definir Data', - 'search_update' => 'Refazer Pesquisa', + 'search_update' => 'Atualizar pesquisa', // Shelves - 'shelf' => 'Prateleira', - 'shelves' => 'Prateleiras', - 'x_shelves' => ':count Prateleira|:count Prateleiras', - 'shelves_empty' => 'Nenhuma prateleira foi criada', - 'shelves_create' => 'Criar Nova Prateleira', - 'shelves_popular' => 'Prateleiras Populares', - 'shelves_new' => 'Prateleiras Novas', - 'shelves_new_action' => 'Nova Prateleira', - 'shelves_popular_empty' => 'As prateleiras mais populares aparecerão aqui.', - 'shelves_new_empty' => 'As prateleiras criadas mais recentemente aparecerão aqui.', - 'shelves_save' => 'Salvar Prateleira', - 'shelves_books' => 'Livros nesta prateleira', - 'shelves_add_books' => 'Adicionar livros a esta prateleira', + 'shelf' => 'Estante', + 'shelves' => 'Estantes', + 'x_shelves' => ':count Estante|:count Estantes', + 'shelves_empty' => 'Nenhuma estante foi criada', + 'shelves_create' => 'Criar Estante', + 'shelves_popular' => 'Estantes Populares', + 'shelves_new' => 'Novas Estantes', + 'shelves_new_action' => 'Nova Estante', + 'shelves_popular_empty' => 'As estantes mais populares serão exibidas aqui.', + 'shelves_new_empty' => 'As estantes mais recentes serão exibidas aqui.', + 'shelves_save' => 'Salvar Estante', + 'shelves_books' => 'Livros nesta estante', + 'shelves_add_books' => 'Adicionar livros a esta estante', 'shelves_drag_books' => 'Arraste os livros abaixo para adicioná-los a esta estante', - 'shelves_empty_contents' => 'Esta prateleira não possui livros atribuídos a ela', - 'shelves_edit_and_assign' => 'Editar prateleira para atribuir livros', + 'shelves_empty_contents' => 'Esta estante não possui livros atribuídos a ela', + 'shelves_edit_and_assign' => 'Editar estante para atribuir livros', 'shelves_edit_named' => 'Editar estante :name', - 'shelves_edit' => 'Editar prateleira', - 'shelves_delete' => 'Excluir prateleira', + 'shelves_edit' => 'Editar estante', + 'shelves_delete' => 'Excluir estante', 'shelves_delete_named' => 'Excluir estante :name', 'shelves_delete_explain' => "Isso excluirá a estante com o nome ':name'. Os livros contidos não serão excluídos.", - 'shelves_delete_confirmation' => 'Tem certeza de que deseja excluir esta prateleira?', - 'shelves_permissions' => 'Permissões de prateleira', - 'shelves_permissions_updated' => 'Permissões de prateleira atualizadas', - 'shelves_permissions_active' => 'Permissões de prateleira ativas', - 'shelves_permissions_cascade_warning' => 'As permissões nas prateleiras não são automaticamente em cascata para os livros contidos. Isso ocorre porque um livro pode existir em várias prateleiras. No entanto, as permissões podem ser copiadas para livros filhos usando a opção encontrada abaixo.', - 'shelves_permissions_create' => 'As permissões de criação de prateleira são usadas apenas para copiar livros filhos usando a ação abaixo. Eles não controlam a capacidade de criar livros.', + 'shelves_delete_confirmation' => 'Tem certeza de que deseja excluir esta estante de livros?', + 'shelves_permissions' => 'Permissões da Estante', + 'shelves_permissions_updated' => 'Permissões da estante atualizadas', + 'shelves_permissions_active' => 'Permissões da estante ativada', + 'shelves_permissions_cascade_warning' => 'As permissões nas estantes não são automaticamente em cascata para os livros contidos. Isso ocorre porque um livro pode existir em várias estantes. No entanto, as permissões podem ser copiadas para livros filhos usando a opção encontrada abaixo.', + 'shelves_permissions_create' => 'As permissões de criação de estante são usadas apenas para copiar livros filhos usando a ação abaixo. Eles não controlam a capacidade de criar livros.', 'shelves_copy_permissions_to_books' => 'Copiar Permissões para Livros', - 'shelves_copy_permissions' => 'Copiar Permissões', - 'shelves_copy_permissions_explain' => 'Isso aplicará as configurações de permissão atuais desta estante a todos os livros contidos nela. Antes de ativar, verifique se todas as alterações nas permissões desta prateleira foram salvas.', - 'shelves_copy_permission_success' => 'Permissões de prateleira copiadas para :count books', + 'shelves_copy_permissions' => 'Copiar permissões', + 'shelves_copy_permissions_explain' => 'Isso aplicará as configurações de permissão atuais desta estante a todos os livros contidos nela. Antes de ativar, verifique se todas as alterações nas permissões desta estante foram salvas.', + 'shelves_copy_permission_success' => 'Permissões da estante copiadas para :count books', // Books 'book' => 'Livro', @@ -132,9 +160,6 @@ 'books_edit_named' => 'Editar Livro :bookName', 'books_form_book_name' => 'Nome do Livro', 'books_save' => 'Salvar Livro', - 'books_default_template' => 'Default Page Template', - 'books_default_template_explain' => 'Assign a page template that will be used as the default content for all new pages in this book. Keep in mind this will only be used if the page creator has view access to those chosen template page.', - 'books_default_template_select' => 'Select a template page', 'books_permissions' => 'Permissões do Livro', 'books_permissions_updated' => 'Permissões do Livro Atualizadas', 'books_empty_contents' => 'Nenhuma página ou capítulo foram criados para este livro.', @@ -145,7 +170,9 @@ 'books_search_this' => 'Pesquisar neste livro', 'books_navigation' => 'Navegação do Livro', 'books_sort' => 'Ordenar Conteúdos do Livro', - 'books_sort_desc' => 'Mova capítulos e páginas de um livro para reorganizar seu conteúdo. É possível acrescentar outros livros, o que permite uma movimentação fácil de capítulos e páginas entre livros.', + 'books_sort_desc' => 'Mova capítulos e páginas de um livro para reorganizar seu conteúdo. É possível acrescentar outros livros, o que permite uma movimentação fácil de capítulos e páginas entre livros. Opcionalmente, uma regra de ordenação automática pode ser definida para ordenar automaticamente o conteúdo deste livro após alterações.', + 'books_sort_auto_sort' => 'Opção de ordenação automática', + 'books_sort_auto_sort_active' => 'Ordenação automática ativa: :sortName', 'books_sort_named' => 'Ordenar Livro :bookName', 'books_sort_name' => 'Ordernar por Nome', 'books_sort_created' => 'Ordenar por Data de Criação', @@ -207,7 +234,7 @@ 'pages_delete_draft' => 'Excluir Rascunho de Página', 'pages_delete_success' => 'Página excluída', 'pages_delete_draft_success' => 'Rascunho de página excluído', - 'pages_delete_warning_template' => 'This page is in active use as a book default page template. These books will no longer have a default page template assigned after this page is deleted.', + 'pages_delete_warning_template' => 'Está página atualmente esta atribuída como modelo de página padrão para algum livro ou capítulo. Estes livros ou capítulos não terão mais um modelo de página padrão atribuídos após essa página ser deletada.', 'pages_delete_confirm' => 'Tem certeza que deseja excluir a página?', 'pages_delete_draft_confirm' => 'Tem certeza que deseja excluir o rascunho de página?', 'pages_editing_named' => 'Editando a Página :pageName', @@ -224,6 +251,8 @@ 'pages_edit_switch_to_markdown_clean' => '(Conteúdo Limpo)', 'pages_edit_switch_to_markdown_stable' => '(Conteúdo Estável)', 'pages_edit_switch_to_wysiwyg' => 'Alternar para o Editor WYSIWYG', + 'pages_edit_switch_to_new_wysiwyg' => 'Mudar para o novo WYSIWYG', + 'pages_edit_switch_to_new_wysiwyg_desc' => '(Em teste beta)', 'pages_edit_set_changelog' => 'Relatar Alterações', 'pages_edit_enter_changelog_desc' => 'Digite uma breve descrição das alterações efetuadas por você', 'pages_edit_enter_changelog' => 'Insira Alterações', @@ -232,7 +261,7 @@ 'pages_editor_switch_consider_following' => 'Considere o seguinte ao alterar editores:', 'pages_editor_switch_consideration_a' => 'Uma vez salva, a nova opção do editor será usada por quaisquer editores futuros, incluindo aqueles que podem não ser capazes de mudar o tipo do editor.', 'pages_editor_switch_consideration_b' => 'Isso pode levar a uma perda de detalhes e sintaxe em certas circunstâncias.', - 'pages_editor_switch_consideration_c' => 'Etiqueta ou alterações no log de mudanças, feitas desde o último salvamento, não persistem nesta alteração.', + 'pages_editor_switch_consideration_c' => 'Marcadores ou alterações no log de mudanças, feitas desde o último salvamento, não persistem nesta alteração.', 'pages_save' => 'Salvar Página', 'pages_title' => 'Título da Página', 'pages_name' => 'Nome da Página', @@ -240,11 +269,12 @@ 'pages_md_preview' => 'Pré-Visualização', 'pages_md_insert_image' => 'Inserir Imagem', 'pages_md_insert_link' => 'Inserir Link para Entidade', - 'pages_md_insert_drawing' => 'Inserir Desenho', + 'pages_md_insert_drawing' => 'Inserir Diagrama', 'pages_md_show_preview' => 'Mostrar pré-visualização', 'pages_md_sync_scroll' => 'Sincronizar pré-visualização', - 'pages_drawing_unsaved' => 'Desenho não-salvo encontrado', - 'pages_drawing_unsaved_confirm' => 'Foram encontrados dados não-salvos de uma tentativa anterior de salvar o desenho. Você gostaria de restaurá-los e continuar editando este desenho?', + 'pages_md_plain_editor' => 'Editor de texto simples', + 'pages_drawing_unsaved' => 'Diagrama não-salvo encontrado', + 'pages_drawing_unsaved_confirm' => 'Foram encontrados dados não-salvos de uma tentativa anterior de salvar o diagrama. Você gostaria de restaurá-los e continuar editando este diagrama?', 'pages_not_in_chapter' => 'Página não está dentro de um capítulo', 'pages_move' => 'Mover Página', 'pages_copy' => 'Copiar Página', @@ -276,9 +306,9 @@ 'pages_pointer_enter_mode' => 'Entrar em modo de seleção de seção', 'pages_pointer_label' => 'Opções de Seção de Página', 'pages_pointer_permalink' => 'Seção de Página Permalink', - 'pages_pointer_include_tag' => 'Seção de Página Incluir Tag', - 'pages_pointer_toggle_link' => 'Modo permalink, pressione para mostrar a tag incluída', - 'pages_pointer_toggle_include' => 'Incluir o modo tag, pressione para mostrar permalink', + 'pages_pointer_include_tag' => 'Marcador de inclusão de seção de página', + 'pages_pointer_toggle_link' => 'Modo permalink, pressione para mostrar a marcação incluída', + 'pages_pointer_toggle_include' => 'Incluir o modo de marcação, pressione para mostrar o permalink', 'pages_permissions_active' => 'Permissões de Página Ativas', 'pages_initial_revision' => 'Publicação Inicial', 'pages_references_update_revision' => 'Atualização automática do sistema de links internos', @@ -300,28 +330,28 @@ // Editor Sidebar 'toggle_sidebar' => '', - 'page_tags' => 'Tags de Página', - 'chapter_tags' => 'Tags de Capítulo', - 'book_tags' => 'Tags de Livro', - 'shelf_tags' => 'Tags de Prateleira', - 'tag' => 'Tag', - 'tags' => 'Tags', - 'tags_index_desc' => 'As tags podem ser aplicadas ao conteúdo dentro do sistema para aplicar uma forma flexível de categorização. As tags podem ter uma chave e um valor, sendo o valor opcional. Depois de aplicado, o conteúdo pode ser consultado usando o nome e o valor da tag.', - 'tag_name' => 'Nome da Tag', - 'tag_value' => 'Valor da Tag (Opcional)', - 'tags_explain' => "Adicione algumas tags para melhor categorizar seu conteúdo. \n Você pode atribuir valores às tags para uma organização mais complexa.", - 'tags_add' => 'Adicionar outra tag', - 'tags_remove' => 'Remover essa tag', + 'page_tags' => 'Marcadores de Página', + 'chapter_tags' => 'Marcadores de Capítulo', + 'book_tags' => 'Marcadores de Livro', + 'shelf_tags' => 'Marcadores de Estante', + 'tag' => 'Marcador', + 'tags' => 'Marcadores', + 'tags_index_desc' => 'Os marcadores podem ser aplicadas ao conteúdo dentro do sistema para aplicar uma forma flexível de categorização. Os marcadores podem ter uma chave e um valor, sendo o valor opcional. Depois de aplicado, o conteúdo pode ser consultado usando o nome e o valor do marcador.', + 'tag_name' => 'Nome do marcador', + 'tag_value' => 'Valor do marcador (Opcional)', + 'tags_explain' => "Adicione alguns marcadores para melhor categorizar seu conteúdo. \n Você pode atribuir valores aos marcadores para uma organização mais complexa.", + 'tags_add' => 'Adicionar outro marcador', + 'tags_remove' => 'Remover esse marcador', 'tags_usages' => 'Total de marcadores usados', 'tags_assigned_pages' => 'Atribuído às páginas', 'tags_assigned_chapters' => 'Atribuído aos Capítulos', 'tags_assigned_books' => 'Atribuído a Livros', - 'tags_assigned_shelves' => 'Atribuído a Prateleiras', + 'tags_assigned_shelves' => 'Atribuído a Estantes', 'tags_x_unique_values' => ':count valores únicos', 'tags_all_values' => 'Todos os valores', 'tags_view_tags' => 'Ver Marcadores', - 'tags_view_existing_tags' => 'Ver tags existentes', - 'tags_list_empty_hint' => 'As tags podem ser atribuídas através da barra lateral do editor de página ou ao editar os detalhes de um livro, capítulo ou prateleira.', + 'tags_view_existing_tags' => 'Ver marcadores existentes', + 'tags_list_empty_hint' => 'Os marcadores podem ser atribuídos através da barra lateral do editor de página ou ao editar os detalhes de um livro, capítulo ou estante.', 'attachments' => 'Anexos', 'attachments_explain' => 'Faça o upload de alguns arquivos ou anexe links para serem exibidos na sua página. Eles estarão visíveis na barra lateral à direita.', 'attachments_explain_instant_save' => 'Mudanças são salvas instantaneamente.', @@ -361,14 +391,17 @@ 'profile_not_created_pages' => ':userName não criou páginas', 'profile_not_created_chapters' => ':userName não criou capítulos', 'profile_not_created_books' => ':userName não criou livros', - 'profile_not_created_shelves' => ':userName não criou prateleiras', + 'profile_not_created_shelves' => ':userName não criou estantes', // Comments 'comment' => 'Comentário', 'comments' => 'Comentários', 'comment_add' => 'Adicionar Comentário', + 'comment_none' => 'Nenhum comentário para exibir', 'comment_placeholder' => 'Digite seus comentários aqui', - 'comment_count' => '{0} Nenhum comentário|{1} 1 Comentário|[2,*] :count Comentários', + 'comment_thread_count' => ':count Tópico de Comentário|:count Tópicos de Comentários', + 'comment_archived_count' => ':count Arquivado', + 'comment_archived_threads' => 'Tópicos Arquivados', 'comment_save' => 'Salvar comentário', 'comment_new' => 'Novo Comentário', 'comment_created' => 'comentado :createDiff', @@ -377,8 +410,14 @@ 'comment_deleted_success' => 'Comentário removido', 'comment_created_success' => 'Comentário adicionado', 'comment_updated_success' => 'Comentário editado', + 'comment_archive_success' => 'Comentário arquivado', + 'comment_unarchive_success' => 'Comentário desarquivado', + 'comment_view' => 'Ver comentário', + 'comment_jump_to_thread' => 'Ir para o tópico', 'comment_delete_confirm' => 'Você tem certeza de que deseja excluir este comentário?', 'comment_in_reply_to' => 'Em resposta à :commentId', + 'comment_reference' => 'Referência', + 'comment_reference_outdated' => '(Desatualizado)', 'comment_editor_explain' => 'Aqui estão os comentários que foram deixados nesta página. Comentários podem ser adicionados e gerenciados ao visualizar a página salva.', // Revision @@ -409,7 +448,7 @@ // References 'references' => 'Referências', 'references_none' => 'Não há referências rastreadas para este item.', - 'references_to_desc' => 'Listed below is all the known content in the system that links to this item.', + 'references_to_desc' => 'Abaixo estão todas as páginas conhecidas no sistema que estão vinculadas a este item.', // Watch Options 'watch' => 'Acompanhar', diff --git a/lang/pt_BR/errors.php b/lang/pt_BR/errors.php index 93ed24a5973..8dab893c799 100644 --- a/lang/pt_BR/errors.php +++ b/lang/pt_BR/errors.php @@ -5,68 +5,70 @@ return [ // Permissions - 'permission' => 'Você não tem permissão para acessar a página requerida.', - 'permissionJson' => 'Você não tem permissão para realizar a ação requerida.', + 'permission' => 'Você não tem permissão para acessar a página solicitada.', + 'permissionJson' => 'Você não tem permissão para realizar a ação solicitada.', // Auth - 'error_user_exists_different_creds' => 'Um usuário com o e-mail :email já existe mas com credenciais diferentes.', - 'email_already_confirmed' => 'E-mail já foi confirmado. Tente efetuar o login.', - 'email_confirmation_invalid' => 'Esse token de confirmação não é válido ou já foi utilizado. Por favor, tente cadastrar-se novamente.', - 'email_confirmation_expired' => 'O token de confirmação já expirou. Um novo e-mail foi enviado.', - 'email_confirmation_awaiting' => 'O endereço de e-mail da conta em uso precisa ser confirmado', + 'error_user_exists_different_creds' => 'Um usuário com o endereço eletrônico: endereço eletrônico já existe, mas com credenciais diferentes.', + 'auth_pre_register_theme_prevention' => 'A conta do usuário não pôde ser registrada com os detalhes oferecidos', + 'email_already_confirmed' => 'Endereço eletrônico já foi confirmado. Tente fazer o ‘login’.', + 'email_confirmation_invalid' => 'Esse código de confirmação não é válido ou já foi utilizado. Por favor, tente cadastrar-se novamente.', + 'email_confirmation_expired' => 'O código de confirmação já expirou. Uma nova mensagem eletrônica foi enviada.', + 'email_confirmation_awaiting' => 'O endereço do correio eletrônico da conta em uso precisa ser confirmado', 'ldap_fail_anonymous' => 'O acesso LDAP falhou ao tentar usar o anonymous bind', 'ldap_fail_authed' => 'O acesso LDAP falhou ao tentar os detalhes do dn e senha fornecidos', - 'ldap_extension_not_installed' => 'A extensão LDAP PHP não está instalada', + 'ldap_extension_not_installed' => 'A extensão LDAP PHP não foi instalada', 'ldap_cannot_connect' => 'Não foi possível conectar ao servidor LDAP. Conexão inicial falhou', - 'saml_already_logged_in' => 'Login já efetuado', - 'saml_no_email_address' => 'Não foi possível encontrar um endereço de e-mail para este usuário nos dados providos pelo sistema de autenticação externa', - 'saml_invalid_response_id' => 'A requisição do sistema de autenticação externa não foi reconhecia por um processo iniciado por esta aplicação. Após o login, navegar para o caminho anterior pode causar um problema.', + 'saml_already_logged_in' => '\'Login\' já efetuado', + 'saml_no_email_address' => 'Não foi possível encontrar um endereço de mensagem eletrônica para este usuário nos dados providos pelo sistema de autenticação externa', + 'saml_invalid_response_id' => 'A requisição do sistema de autenticação externa não foi reconhecida por um processo iniciado por esta aplicação. Após o \'login\', navegar para o caminho anterior pode causar um problema.', 'saml_fail_authed' => 'Login utilizando :system falhou. Sistema não forneceu autorização bem sucedida', - 'oidc_already_logged_in' => 'Já está logado', - 'oidc_user_not_registered' => 'O usuário :name não está registrado e o registro automático está desativado', - 'oidc_no_email_address' => 'Não foi possível encontrar um endereço de e-mail para este usuário, nos dados fornecidos pelo sistema de autenticação externa', + 'oidc_already_logged_in' => '\'Login\' já efetuado', + 'oidc_no_email_address' => 'Não foi possível encontrar um endereço de mensagem eletrônica para este usuário, nos dados fornecidos pelo sistema de autenticação externa', 'oidc_fail_authed' => 'Login usando :system falhou, o sistema não forneceu autorização com sucesso', 'social_no_action_defined' => 'Nenhuma ação definida', - 'social_login_bad_response' => "Erro recebido durante o login :socialAccount: \n:error", + 'social_login_bad_response' => "Erro recebido durante o 'login' :socialAccount: \n: error", 'social_account_in_use' => 'Essa conta :socialAccount já está em uso. Por favor, tente entrar utilizando a opção :socialAccount.', 'social_account_email_in_use' => 'O e-mail :email já está em uso. Se você já tem uma conta você poderá se conectar a conta :socialAccount a partir das configurações de seu perfil.', 'social_account_existing' => 'Essa conta :socialAccount já está vinculada a esse perfil.', 'social_account_already_used_existing' => 'Essa conta :socialAccount já está sendo utilizada por outro usuário.', 'social_account_not_used' => 'Essa conta :socialAccount não está vinculada a nenhum usuário. Por favor vincule a conta nas suas configurações de perfil. ', - 'social_account_register_instructions' => 'Se você não tem uma conta, você poderá se cadastrar usando a opção :socialAccount.', + 'social_account_register_instructions' => 'Se você não tem uma conta, você poderá se cadastrar usando a opção: conta social.', 'social_driver_not_found' => 'Social driver não encontrado', - 'social_driver_not_configured' => 'Seus parâmetros socials de :socialAccount não estão configurados corretamente.', - 'invite_token_expired' => 'Esse link de convite expirou. Alternativamente, você pode tentar redefinir a senha da sua conta.', + 'social_driver_not_configured' => 'Seus parâmetros sociais de: conta social não estão configurados corretamente.', + 'invite_token_expired' => 'Este link de convite expirou. Alternativamente, você pode tentar redefinir a senha da sua conta.', + 'login_user_not_found' => 'Não foi possível encontrar um usuário para esta ação.', // System 'path_not_writable' => 'O caminho de destino (:filePath) de upload de arquivo não possui permissão de escrita. Certifique-se que ele possui direitos de escrita no servidor.', 'cannot_get_image_from_url' => 'Não foi possível obter a imagem a partir de :url', 'cannot_create_thumbs' => 'O servidor não pôde criar as miniaturas de imagem. Por favor, verifique se a extensão GD PHP está instalada.', - 'server_upload_limit' => 'O servidor não permite o upload de arquivos com esse tamanho. Por favor, tente fazer o upload de arquivos de menor tamanho.', + 'server_upload_limit' => 'O servidor não permite o ‘upload’ de arquivos com esse tamanho. Por favor, tente um tamanho de arquivo menor.', 'server_post_limit' => 'O servidor não pode receber a quantidade de dados fornecida. Tente novamente com menos dados ou um arquivo menor.', - 'uploaded' => 'O servidor não permite o upload de arquivos com esse tamanho. Por favor, tente fazer o upload de arquivos de menor tamanho.', + 'uploaded' => 'O servidor não permite o ‘upload’ de arquivos com esse tamanho. Por favor, tente fazer o ‘upload’ de arquivos de menor tamanho.', // Drawing & Images - 'image_upload_error' => 'Um erro aconteceu enquanto o servidor tentava efetuar o upload da imagem', - 'image_upload_type_error' => 'O tipo de imagem que está sendo enviada é inválido', - 'image_upload_replace_type' => 'Substituições de arquivos de imagem devem ser do mesmo tipo', - 'image_upload_memory_limit' => 'Falha ao processar o upload de imagem e/ou criar miniaturas devido a limites de recursos do sistema.', - 'image_thumbnail_memory_limit' => 'Falha ao criar variações de tamanho da imagem devido a limites de recursos do sistema.', - 'image_gallery_thumbnail_memory_limit' => 'Falha ao criar miniaturas da galeria devido a limites de recursos do sistema.', - 'drawing_data_not_found' => 'Dados de desenho não puderam ser carregados. Talvez o arquivo de desenho não exista mais ou você não tenha permissão para acessá-lo.', + 'image_upload_error' => 'Um erro ocorreu enquanto o servidor tentava efetuar o ‘upload’ da imagem', + 'image_upload_type_error' => 'A categoria de imagem que está sendo enviada é inválida', + 'image_upload_replace_type' => 'As substituições de arquivos de imagem devem ser do mesmo tipo', + 'image_upload_memory_limit' => 'Falha ao processar o ‘upload’ de imagem e/ou criar miniaturas devido a limites de recursos do sistema.', + 'image_thumbnail_memory_limit' => 'Falha ao criar variações de tamanho de imagem devido a limites de recursos do sistema.', + 'image_gallery_thumbnail_memory_limit' => 'Falha ao criar miniaturas da galeria devido aos limites de recursos do sistema.', + 'drawing_data_not_found' => 'Dados de diagrama não puderam ser carregados. O arquivo do diagrama pode não existir mais ou você não tenha permissão para acessá-lo.', // Attachments - 'attachment_not_found' => 'Anexo não encontrado', - 'attachment_upload_error' => 'Um erro ocorreu ao efetuar o upload do arquivo anexado', + 'attachment_not_found' => 'Documento não encontrado', + 'attachment_upload_error' => 'Um erro ocorreu ao efetuar o ‘upload’ do arquivo anexado', // Pages - 'page_draft_autosave_fail' => 'Falha ao tentar salvar o rascunho. Certifique-se que a conexão de internet está funcional antes de tentar salvar essa página', + 'page_draft_autosave_fail' => 'Falha ao tentar salvar o rascunho. Certifique-se de ter conexão de ‘internet’ antes de tentar salvar essa página', 'page_draft_delete_fail' => 'Falha ao excluir o rascunho da página e buscar conteúdo salvo na página atual', - 'page_custom_home_deletion' => 'Não é possível excluir uma página que está definida como página inicial', + 'page_custom_home_deletion' => 'Não é possível deletar uma página definida como página inicial', // Entities - 'entity_not_found' => 'Entidade não encontrada', - 'bookshelf_not_found' => 'Prateleira não encontrada', + 'entity_not_found' => 'Entidade não encontrada +', + 'bookshelf_not_found' => 'Estante não encontrada', 'book_not_found' => 'Livro não encontrado', 'page_not_found' => 'Página não encontrada', 'chapter_not_found' => 'Capítulo não encontrado', @@ -75,19 +77,20 @@ 'guests_cannot_save_drafts' => 'Convidados não podem salvar rascunhos', // Users - 'users_cannot_delete_only_admin' => 'Você não pode excluir o único admin', + 'users_cannot_delete_only_admin' => 'Você não pode excluir o único administrador', 'users_cannot_delete_guest' => 'Você não pode excluir o usuário convidado', + 'users_could_not_send_invite' => 'Não foi possível criar o usuário porque o endereço eletrônico de convite não foi enviado', // Roles - 'role_cannot_be_edited' => 'Esse cargo não pode ser editado', - 'role_system_cannot_be_deleted' => 'Esse cargo é um cargo do sistema e não pode ser excluído', - 'role_registration_default_cannot_delete' => 'Esse cargo não poderá se excluído enquanto estiver registrado como o cargo padrão', - 'role_cannot_remove_only_admin' => 'Este usuário é o único usuário vinculado ao cargo de administrador. Atribua o cargo de administrador a outro usuário antes de tentar removê-lo aqui.', + 'role_cannot_be_edited' => 'Esse perfil não pode ser editado', + 'role_system_cannot_be_deleted' => 'Este é um perfil do sistema e não pode ser excluído', + 'role_registration_default_cannot_delete' => 'Esse perfil não poderá se excluído enquanto estiver registrado como perfil padrão de registro', + 'role_cannot_remove_only_admin' => 'Este usuário é o único vinculado ao perfil de administrador. Atribua o perfil de administrador a outro usuário antes de tentar removê-lo daqui.', // Comments 'comment_list' => 'Ocorreu um erro ao buscar os comentários.', 'cannot_add_comment_to_draft' => 'Você não pode adicionar comentários a um rascunho.', - 'comment_add' => 'Ocorreu um erro ao adicionar o comentário.', + 'comment_add' => 'Ocorreu um erro ao adicionar / atualizar o comentário.', 'comment_delete' => 'Ocorreu um erro ao excluir o comentário.', 'empty_comment' => 'Não é possível adicionar um comentário vazio.', @@ -101,19 +104,31 @@ 'return_home' => 'Retornar à página inicial', 'error_occurred' => 'Ocorreu um Erro', 'app_down' => ':appName está fora do ar no momento', - 'back_soon' => 'Voltaremos em breve.', + 'back_soon' => 'Vai estar de volta em breve.', + + // Import + '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_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.', + 'import_perms_chapters' => 'Você não tem as permissões necessárias para criar capítulos.', + 'import_perms_pages' => 'Você não tem as permissões necessárias para criar páginas.', + 'import_perms_images' => 'Está não tem permissões necessárias para criar imagens.', + 'import_perms_attachments' => 'Você não tem a permissão necessária para criar anexos.', // API errors - 'api_no_authorization_found' => 'Nenhum token de autorização encontrado na requisição', - 'api_bad_authorization_format' => 'Um token de autorização foi encontrado na requisição, mas o formato parece incorreto', - 'api_user_token_not_found' => 'Nenhum token de API correspondente foi encontrado para o token de autorização fornecido', - 'api_incorrect_token_secret' => 'O segredo fornecido para o token de API usado está incorreto', - 'api_user_no_api_permission' => 'O proprietário do token de API utilizado não tem permissão para fazer requisições de API', - 'api_user_token_expired' => 'O token de autenticação expirou', + 'api_no_authorization_found' => 'Nenhum código de autorização encontrado na requisição', + 'api_bad_authorization_format' => 'Um código de autorização foi encontrado na requisição, mas o formato parece incorreto', + 'api_user_token_not_found' => 'Nenhum código de API correspondente foi encontrado para o código de autorização fornecido', + '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', // Settings & Maintenance - 'maintenance_test_email_failure' => 'Erro encontrado ao enviar um e-mail de teste:', + 'maintenance_test_email_failure' => 'Erro encontrado ao enviar uma mensagem eletrônica de teste:', // HTTP errors - 'http_ssr_url_no_match' => 'A URL não corresponde aos hosts SSR configurados como permitidos', + 'http_ssr_url_no_match' => 'A \'URL\' não corresponde aos anfitriões SSR configurados como permitidos ', ]; diff --git a/lang/pt_BR/notifications.php b/lang/pt_BR/notifications.php index e8fc217adc7..6397b20e5fe 100644 --- a/lang/pt_BR/notifications.php +++ b/lang/pt_BR/notifications.php @@ -13,7 +13,7 @@ '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.', 'detail_page_name' => 'Nome da Página:', - 'detail_page_path' => 'Page Path:', + 'detail_page_path' => 'Caminho da Página:', 'detail_commenter' => 'Comentador:', 'detail_comment' => 'Comentário:', 'detail_created_by' => 'Criado por: ', diff --git a/lang/pt_BR/preferences.php b/lang/pt_BR/preferences.php index aa354a9ad93..e0b79ff0644 100644 --- a/lang/pt_BR/preferences.php +++ b/lang/pt_BR/preferences.php @@ -27,7 +27,7 @@ 'notifications_save' => 'Salvar Preferências', 'notifications_update_success' => 'Preferências de notificação foram atualizadas!', 'notifications_watched' => 'Itens assistidos e ignorados', - 'notifications_watched_desc' => ' Abaixo estão os itens que possuem preferências de relógio personalizadas aplicadas. Para atualizar suas preferências para estes, veja o item e encontre as opções de relógio na barra lateral.', + 'notifications_watched_desc' => 'Abaixo estão os itens que possuem preferências de relógio personalizadas aplicadas. Para atualizar suas preferências para estes, veja o item e encontre as opções de relógio na barra lateral.', 'auth' => 'Acesso & Segurança', 'auth_change_password' => 'Mudar a senha', @@ -42,7 +42,7 @@ 'profile_email_no_permission' => 'Infelizmente você não tem permissão para alterar seu endereço de e-mail. Se quiser alterar isso, você precisará pedir a um administrador para alterar isso para você.', 'profile_avatar_desc' => 'Selecione uma imagem que será usada para representar você mesmo para outras pessoas no sistema. Idealmente, esta imagem deve ser quadrada e ter cerca de 256px de largura e altura.', 'profile_admin_options' => 'Opções do administrador', - 'profile_admin_options_desc' => 'Opções adicionais do nível administrador, como as que visam gerenciar as atribuições de papéis, podem ser encontradas para sua conta de usuário na área "Configurações > Usuários" do aplicativo.', + 'profile_admin_options_desc' => 'Opções adicionais do nível administrador, como as que visam gerenciar as atribuições de perfis, podem ser encontradas para sua conta de usuário na área "Configurações > Usuários" do aplicativo.', 'delete_account' => 'Deletar conta', 'delete_my_account' => 'Deletar minha conta', diff --git a/lang/pt_BR/settings.php b/lang/pt_BR/settings.php index 3502a0e92aa..b940e57d4c0 100644 --- a/lang/pt_BR/settings.php +++ b/lang/pt_BR/settings.php @@ -54,7 +54,7 @@ 'app_color' => 'Cor Primária', 'link_color' => 'Cor Padrão para Links', 'content_colors_desc' => 'Definir cores para todos os elementos na hierarquia da organização da página. Escolher cores com um brilho semelhante às cores padrão é recomendado para legibilidade.', - 'bookshelf_color' => 'Cor da Prateleira', + 'bookshelf_color' => 'Cor da Estante', 'book_color' => 'Cor do Livro', 'chapter_color' => 'Cor do Capítulo', 'page_color' => 'Cor da Página', @@ -64,8 +64,8 @@ 'reg_settings' => 'Cadastro', 'reg_enable' => 'Habilitar Cadastro', 'reg_enable_toggle' => 'Habilitar cadastro', - 'reg_enable_desc' => 'Quando o cadastro é habilitado, visitantes poderão cadastrar-se como usuários do aplicativo. Realizado o cadastro, recebem um único cargo padrão.', - 'reg_default_role' => 'Cargo padrão para usuários após o cadastro', + 'reg_enable_desc' => 'Quando o cadastro é habilitado, visitantes poderão cadastrar-se como usuários do aplicativo. Realizado o cadastro, recebem um único perfil padrão.', + 'reg_default_role' => 'Perfil padrão para usuários após o cadastro', 'reg_enable_external_warning' => 'A opção acima é ignorada enquanto a autenticação externa LDAP ou SAML estiver ativa. Contas de usuários para membros não existentes serão criadas automaticamente se a autenticação pelo sistema externo em uso for bem sucedida.', 'reg_email_confirmation' => 'Confirmação de E-mail', 'reg_email_confirmation_toggle' => 'Requerer confirmação de e-mail', @@ -74,6 +74,36 @@ 'reg_confirm_restrict_domain_desc' => 'Entre com uma lista separada por vírgulas de domínios de e-mails aos quais você deseja restringir o cadastro. Um e-mail de confirmação será enviado para o usuário validar seu endereço de e-mail antes de ser permitido a interagir com a aplicação.
Note que os usuários serão capazes de alterar o seus endereços de e-mail após o sucesso na confirmação do cadastro.', 'reg_confirm_restrict_domain_placeholder' => 'Nenhuma restrição definida', + // Sorting Settings + 'sorting' => 'Ordenação', + 'sorting_book_default' => 'Ordenação padrão de livros', + 'sorting_book_default_desc' => 'Selecione a regra de ordenação padrão a ser aplicada a novos livros. Isso não afetará os livros existentes e pode ser substituído para cada livro individualmente.', + 'sorting_rules' => 'Regras de ordenação', + 'sorting_rules_desc' => 'Estas são operações de ordenação pré-definidas que podem ser aplicadas a conteúdos no sistema.', + 'sort_rule_assigned_to_x_books' => 'Atribuído a :count Livros|Atribuído a :count Livros', + 'sort_rule_create' => 'Criar Regra de Ordenação', + 'sort_rule_edit' => 'Editar Regra de Ordenação', + 'sort_rule_delete' => 'Excluir Regra de Ordenação', + 'sort_rule_delete_desc' => 'Remover esta regra de ordenação do sistema. Os livros usando este tipo serão revertidos para a ordenação manual.', + 'sort_rule_delete_warn_books' => 'Esta regra de ordenação está sendo usada atualmente em :count livro(s). Tem certeza de que deseja excluí-la?', + 'sort_rule_delete_warn_default' => 'Esta regra de ordenação é atualmente usada como padrão para livros. Tem certeza de que deseja excluí-la?', + 'sort_rule_details' => 'Detalhes das Regras de Ordenação', + 'sort_rule_details_desc' => 'Defina um nome para esta regra de ordenação, que aparecerá nas listas quando os usuários selecionarem uma ordenação.', + 'sort_rule_operations' => 'Operações de Ordenação', + 'sort_rule_operations_desc' => 'Configure as ações de ordenação a serem executadas movendo-as da lista de operações disponíveis. Ao usar, as operações serão aplicadas em ordem, de cima para baixo. Quaisquer alterações feitas aqui serão aplicadas a todos os livros atribuídos após salvar.', + 'sort_rule_available_operations' => 'Operações Disponíveis', + 'sort_rule_available_operations_empty' => 'Não há operações restantes', + 'sort_rule_configured_operations' => 'Operações configuradas', + 'sort_rule_configured_operations_empty' => 'Arrastar/adicionar operações da lista "Operações Disponíveis"', + 'sort_rule_op_asc' => '(Asc)', + 'sort_rule_op_desc' => '(Desc)', + 'sort_rule_op_name' => 'Nome - Alfabético', + 'sort_rule_op_name_numeric' => 'Nome - Numérico', + 'sort_rule_op_created_date' => 'Data de Criação', + 'sort_rule_op_updated_date' => 'Data de Atualização', + 'sort_rule_op_chapters_first' => 'Capítulos Primeiro', + 'sort_rule_op_chapters_last' => 'Capítulos por Último', + // Maintenance settings 'maint' => 'Manutenção', 'maint_image_cleanup' => 'Limpeza de Imagens', @@ -90,7 +120,7 @@ 'maint_send_test_email_mail_subject' => 'E-mail de Teste', 'maint_send_test_email_mail_greeting' => 'O envio de e-mails parece funcionar!', 'maint_send_test_email_mail_text' => 'Parabéns! Já que você recebeu esta notificação, suas opções de e-mail parecem estar configuradas corretamente.', - 'maint_recycle_bin_desc' => 'Prateleiras, livros, capítulos e páginas deletados são mandados para a lixeira podendo assim ser restaurados ou excluídos permanentemente. Itens mais antigos da lixeira podem vir a ser automaticamente removidos da lixeira após um tempo dependendo da configuração do sistema.', + 'maint_recycle_bin_desc' => 'Estantes, livros, capítulos e páginas excluídos são mandados para a lixeira podendo assim ser restaurados ou excluídos permanentemente. Itens mais antigos da lixeira podem vir a ser automaticamente removidos da lixeira após um tempo dependendo da configuração do sistema.', 'maint_recycle_bin_open' => 'Abrir Lixeira', 'maint_regen_references' => 'Regenerar referências', 'maint_regen_references_desc' => 'Essa ação reconstruirá o índice de referência entre itens no banco de dados. Isso geralmente é tratado automaticamente, mas essa ação pode ser útil para indexar conteúdo antigo ou adicionado por métodos não oficiais.', @@ -109,7 +139,7 @@ 'recycle_bin_contents_empty' => 'A lixeira está vazia', 'recycle_bin_empty' => 'Esvaziar Lixeira', 'recycle_bin_empty_confirm' => 'Isso irá destruir permanentemente todos os itens na lixeira inclusive o conteúdo de cada item. Tem certeza de que quer esvaziar a lixeira?', - 'recycle_bin_destroy_confirm' => 'Esta ação irá excluir permanentemente do sistema este item junto com todos os elementos filhos listados abaixo. Você não poderá restaurar esse conteúdo. Tem certeza de que deseja excluir permanentemente este item?', + 'recycle_bin_destroy_confirm' => 'Esta ação excluirá permanentemente este item do sistema, juntamente com quaisquer elementos secundários listados abaixo, e você não poderá restaurar este conteúdo. Tem certeza de que deseja excluir permanentemente este item?', 'recycle_bin_destroy_list' => 'Itens a serem Destruídos', 'recycle_bin_restore_list' => 'Itens a serem restaurados', 'recycle_bin_restore_confirm' => 'Esta ação irá restaurar o item excluído, inclusive quaisquer elementos filhos, para seu local original. Se a localização original tiver, entretanto, sido eliminada e estiver agora na lixeira, o item pai também precisará ser restaurado.', @@ -134,51 +164,52 @@ 'audit_date_to' => 'Para', // Role Settings - 'roles' => 'Cargos', - 'role_user_roles' => 'Cargos de Usuário', - 'roles_index_desc' => 'As funções são usadas para agrupar usuários & fornecer permissão de sistema a seus membros. Quando um usuário é membro de várias funções, os privilégios concedidos serão acumulados e o usuário herdará todas as habilidades.', + 'roles' => 'Perfis', + 'role_user_roles' => 'Perfis de Usuários', + 'roles_index_desc' => 'Os perfis são usados para agrupar usuários & fornecer permissão de sistema a seus membros. Quando um usuário possui vários perfis, os privilégios concedidos serão acumulados e o usuário herdará todas as habilidades.', 'roles_x_users_assigned' => ':count usuário atribuído|:count usuários atribuídos', 'roles_x_permissions_provided' => ':count permissão|:count permissões', 'roles_assigned_users' => 'Usuários atribuídos', 'roles_permissions_provided' => 'Permissões fornecidas', - 'role_create' => 'Criar novo Cargo', - 'role_delete' => 'Excluir Cargo', - 'role_delete_confirm' => 'A ação vai excluír o cargo de nome \':roleName\'.', - 'role_delete_users_assigned' => 'Esse cargo tem :userCount usuários vinculados a ele. Se quiser migrar usuários desse cargo para outro, selecione um novo cargo.', + 'role_create' => 'Criar novo Perfil', + 'role_delete' => 'Excluir Perfil', + 'role_delete_confirm' => 'A ação vai excluír o perfil de nome \':roleName\'.', + 'role_delete_users_assigned' => 'Esse perfil tem :userCount usuários vinculados a ele. Se quiser migrar usuários desse perfil para outro, selecione um novo perfil.', 'role_delete_no_migration' => "Não migre os usuários", - 'role_delete_sure' => 'Tem certeza que deseja excluir esse cargo?', - 'role_edit' => 'Editar Cargo', - 'role_details' => 'Detalhes do Cargo', - 'role_name' => 'Nome do Cargo', - 'role_desc' => 'Breve Descrição do Cargo', + 'role_delete_sure' => 'Tem certeza que deseja excluir esse perfil?', + 'role_edit' => 'Editar Perfil', + 'role_details' => 'Detalhes do Perfil', + 'role_name' => 'Nome do Perfil', + 'role_desc' => 'Breve Descrição do Perfil', 'role_mfa_enforced' => 'Requer Autenticação Multi-fator', 'role_external_auth_id' => 'IDs de Autenticação Externa', 'role_system' => 'Permissões do Sistema', 'role_manage_users' => 'Gerenciar usuários', - 'role_manage_roles' => 'Gerenciar cargos e permissões de cargos', + 'role_manage_roles' => 'Gerenciar perfis e permissões de perfis', 'role_manage_entity_permissions' => 'Gerenciar todos os livros, capítulos e permissões de páginas', 'role_manage_own_entity_permissions' => 'Gerenciar permissões de seu próprio livro, capítulo e paginas', 'role_manage_page_templates' => 'Gerenciar modelos de página', 'role_access_api' => 'Acessar API do sistema', 'role_manage_settings' => 'Gerenciar configurações da aplicação', 'role_export_content' => 'Exportar conteúdo', + 'role_import_content' => 'Importar conteúdo', 'role_editor_change' => 'Alterar página de edição', 'role_notifications' => 'Receber e gerenciar notificações', '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 cargos com essas permissões para usuários confiáveis.', + '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_all' => 'Todos', 'role_own' => 'Próprio', 'role_controlled_by_asset' => 'Controlado pelos ativos nos quais o upload foi realizado', - 'role_save' => 'Salvar Cargo', - 'role_users' => 'Usuários com este cargo', - 'role_users_none' => 'Nenhum usuário está atualmente vinculado a este cargo', + 'role_save' => 'Salvar Perfil', + 'role_users' => 'Usuários com este perfil', + 'role_users_none' => 'Nenhum usuário está atualmente vinculado a este perfil', // Users 'users' => 'Usuários', - 'users_index_desc' => 'Crie e gerencie contas de usuários individuais dentro do sistema. As contas de usuário são usadas para login e atribuição de conteúdo e atividade. As permissões de acesso são baseadas principalmente na função, mas a propriedade do conteúdo do usuário, entre outros fatores, também pode afetar as permissões e o acesso.', + 'users_index_desc' => 'Crie e gerencie contas de usuários individuais dentro do sistema. As contas de usuário são usadas para login e atribuição de conteúdo e atividade. As permissões de acesso são baseadas principalmente no perfil, mas a propriedade do conteúdo do usuário, entre outros fatores, também pode afetar as permissões e o acesso.', 'user_profile' => 'Perfil do Usuário', 'users_add_new' => 'Adicionar Novo Usuário', 'users_search' => 'Pesquisar Usuários', @@ -186,8 +217,8 @@ 'users_details' => 'Detalhes do Usuário', 'users_details_desc' => 'Defina um nome de exibição e um endereço de e-mail para este usuário. O endereço de e-mail será usado para fazer login na aplicação.', 'users_details_desc_no_email' => 'Defina um nome de exibição para este usuário para que outros usuários possam reconhecê-lo', - 'users_role' => 'Cargos do Usuário', - 'users_role_desc' => 'Selecione os cargos aos quais este usuário será vinculado. Se um usuário for vinculado a múltiplos cargos, suas permissões serão empilhadas e ele receberá todas as habilidades dos cargos atribuídos.', + 'users_role' => 'Perfis do Usuário', + 'users_role_desc' => 'Selecione os perfis aos quais este usuário será vinculado. Se um usuário for vinculado a múltiplos perfis, suas permissões serão empilhadas e ele receberá todas as habilidades dos perfis atribuídos.', 'users_password' => 'Senha do Usuário', 'users_password_desc' => 'Defina uma senha usada para fazer \'login\' no aplicativo. Deve ter pelo menos 8 caracteres.', 'users_send_invite_text' => 'Você pode escolher enviar a este usuário um convite por e-mail que o possibilitará definir sua própria senha, ou defina você uma senha.', @@ -276,6 +307,14 @@ 'webhooks_last_errored' => 'Último Erro:', 'webhooks_last_error_message' => 'Última mensagem de erro:', + // Licensing + 'licenses' => 'Licenças', + 'licenses_desc' => 'Esta página detalha informações da licença do BookStack, além dos projetos e bibliotecas usadas no BookStack. Muitos projectos listados só podem ser utilizados num contexto de desenvolvimento.', + 'licenses_bookstack' => 'Licença do BookStack', + 'licenses_php' => 'Licenças de Bibliotecas PHP', + 'licenses_js' => 'Licenças de Bibliotecas JavaScript', + 'licenses_other' => 'Outras licenças', + 'license_details' => 'Detalhes da Licença', //! If editing translations files directly please ignore this in all //! languages apart from en. Content will be auto-copied from en. @@ -287,6 +326,7 @@ 'bs' => 'Bosanski', 'ca' => 'Català', 'cs' => 'Česky', + 'cy' => 'Cymraeg', 'da' => 'Dansk', 'de' => 'Deutsch (Sie)', 'de_informal' => 'Deutsch (Du)', @@ -308,6 +348,7 @@ 'lt' => 'Lietuvių Kalba', 'lv' => 'Latviešu Valoda', 'nb' => 'Norsk (Bokmål)', + 'ne' => 'नेपाली', 'nn' => 'Nynorsk', 'nl' => 'Nederlands', 'pl' => 'Polski', diff --git a/lang/pt_BR/validation.php b/lang/pt_BR/validation.php index 68b85029843..9ddfdf2a566 100644 --- a/lang/pt_BR/validation.php +++ b/lang/pt_BR/validation.php @@ -105,6 +105,11 @@ 'url' => 'O formato da URL :attribute é inválido.', '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_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.', + // Custom validation lines 'custom' => [ 'password-confirm' => [ diff --git a/lang/ro/activities.php b/lang/ro/activities.php index 4caafab05bc..eed5cc0832f 100644 --- a/lang/ro/activities.php +++ b/lang/ro/activities.php @@ -84,6 +84,14 @@ 'webhook_delete' => 'a șters webhook', 'webhook_delete_notification' => 'Webhook șters cu succes', + // Imports + 'import_create' => 'created import', + 'import_create_notification' => 'Import successfully uploaded', + 'import_run' => 'updated import', + 'import_run_notification' => 'Content successfully imported', + 'import_delete' => 'deleted import', + 'import_delete_notification' => 'Import successfully deleted', + // Users 'user_create' => 'utilizator creat', 'user_create_notification' => 'Utilizator creat cu succes', @@ -93,11 +101,11 @@ 'user_delete_notification' => 'Utilizator eliminat cu succes', // API Tokens - 'api_token_create' => 'token api creat', + 'api_token_create' => 'created API token', 'api_token_create_notification' => 'Token API creat cu succes', - 'api_token_update' => 'token api actualizat', + 'api_token_update' => 'updated API token', 'api_token_update_notification' => 'Token API actualizat cu succes', - 'api_token_delete' => 'token api șters', + 'api_token_delete' => 'deleted API token', 'api_token_delete_notification' => 'Token API șters cu succes', // Roles @@ -119,6 +127,14 @@ 'comment_update' => 'comentariu actualizat', 'comment_delete' => 'comentariu șters', + // Sort Rules + 'sort_rule_create' => 'created sort rule', + 'sort_rule_create_notification' => 'Sort rule successfully created', + 'sort_rule_update' => 'updated sort rule', + 'sort_rule_update_notification' => 'Sort rule successfully updated', + 'sort_rule_delete' => 'deleted sort rule', + 'sort_rule_delete_notification' => 'Sort rule successfully deleted', + // Other 'permissions_update' => 'a actualizat permisiunile', ]; diff --git a/lang/ro/auth.php b/lang/ro/auth.php index 7f461bc7a8f..e02eeb4c1ba 100644 --- a/lang/ro/auth.php +++ b/lang/ro/auth.php @@ -91,7 +91,7 @@ 'mfa_option_totp_title' => 'Aplicație mobilă', 'mfa_option_totp_desc' => 'Pentru a utiliza autentificarea multifactor, vei avea nevoie de o aplicație mobilă care acceptă TOTP, cum ar fi Google Authenticator, Authy sau Microsoft Authenticator.', 'mfa_option_backup_codes_title' => 'Coduri de rezervă', - 'mfa_option_backup_codes_desc' => 'Stochează în siguranță un set de coduri de rezervă de unică folosință pe care le poți introduce pentru verificarea identității.', + 'mfa_option_backup_codes_desc' => 'Generates a set of one-time-use backup codes which you\'ll enter on login to verify your identity. Make sure to store these in a safe & secure place.', 'mfa_gen_confirm_and_enable' => 'Confirmă și activează', 'mfa_gen_backup_codes_title' => 'Configurarea codurilor de rezervă', 'mfa_gen_backup_codes_desc' => 'Păstrează lista de coduri de mai jos într-un loc sigur. Când accesezi sistemul, vei putea folosi unul dintre coduri ca un al doilea mecanism de autentificare.', diff --git a/lang/ro/common.php b/lang/ro/common.php index 1bc23dc1883..fe4bfe8b9dd 100644 --- a/lang/ro/common.php +++ b/lang/ro/common.php @@ -20,7 +20,7 @@ 'description' => 'Descriere', 'role' => 'Rol', 'cover_image' => 'Imagine copertă', - 'cover_image_description' => 'Această imagine ar trebui să aibă aproximativ 440x250px.', + 'cover_image_description' => 'This image should be approximately 440x250px although it will be flexibly scaled & cropped to fit the user interface in different scenarios as required, so actual dimensions for display will differ.', // Actions 'actions' => 'Acțiuni', @@ -30,6 +30,8 @@ 'create' => 'Crează', 'update' => 'Actualzează', 'edit' => 'Editează', + 'archive' => 'Archive', + 'unarchive' => 'Un-Archive', 'sort' => 'Sortează', 'move' => 'Mută', 'copy' => 'Copiază', @@ -107,4 +109,7 @@ // Not directly used but available for convenience to users. 'privacy_policy' => 'Politică de confidențialitate', 'terms_of_service' => 'Termeni și condiții', + + // OpenSearch + 'opensearch_description' => 'Search :appName', ]; diff --git a/lang/ro/editor.php b/lang/ro/editor.php index e882494eed5..951e027fec1 100644 --- a/lang/ro/editor.php +++ b/lang/ro/editor.php @@ -13,6 +13,7 @@ 'cancel' => 'Anulează', 'save' => 'Salvează', 'close' => 'Închide', + 'apply' => 'Apply', 'undo' => 'Anulează', 'redo' => 'Refă', 'left' => 'Stânga', @@ -47,6 +48,7 @@ 'superscript' => 'Scris sus', 'subscript' => 'Scris jos', 'text_color' => 'Culoare text', + 'highlight_color' => 'Highlight color', 'custom_color' => 'Culoare personalizată', 'remove_color' => 'Elimină culoarea', 'background_color' => 'Culoare fundal', @@ -81,6 +83,9 @@ 'table_properties' => 'Proprietăți tabel', 'table_properties_title' => 'Proprietăți tabel', 'delete_table' => 'Șterge tabel', + 'table_clear_formatting' => 'Clear table formatting', + 'resize_to_contents' => 'Resize to contents', + 'row_header' => 'Row header', 'insert_row_before' => 'Inserează rând după', 'insert_row_after' => 'Inserează rând înainte', 'delete_row' => 'Șterge rând', @@ -144,6 +149,7 @@ 'url' => 'URL', 'text_to_display' => 'Text de afișat', 'title' => 'Titlu', + 'browse_links' => 'Browse links', 'open_link' => 'Deschide link-ul', 'open_link_in' => 'Deschide link-ul în...', 'open_link_current' => 'Fereastra curentă', @@ -160,6 +166,8 @@ 'about' => 'Despre editor', 'about_title' => 'Despre editorul WYSIWYG', 'editor_license' => 'Editor licență și drepturi de autor', + 'editor_lexical_license' => 'This editor is built as a fork of :lexicalLink which is distributed under the MIT license.', + 'editor_lexical_license_link' => 'Full license details can be found here.', 'editor_tiny_license' => 'Acest editor este construit folosind :tinyLink care este furnizat sub licența MIT.', 'editor_tiny_license_link' => 'Detaliile privind drepturile de autor şi licența TinyMCE pot fi consultate aici.', 'save_continue' => 'Salvează pagina și continuă', diff --git a/lang/ro/entities.php b/lang/ro/entities.php index c203df4377b..ac8c9d32609 100644 --- a/lang/ro/entities.php +++ b/lang/ro/entities.php @@ -39,6 +39,34 @@ 'export_pdf' => 'Fișier PDF', 'export_text' => 'Fișier text simplu', 'export_md' => 'Fișier Markdown', + 'export_zip' => 'Portable ZIP', + 'default_template' => 'Default Page Template', + 'default_template_explain' => 'Assign a page template that will be used as the default content for all pages created within this item. Keep in mind this will only be used if the page creator has view access to the chosen template page.', + 'default_template_select' => 'Select a template page', + 'import' => 'Import', + 'import_validate' => 'Validate Import', + 'import_desc' => 'Import books, chapters & pages using a portable zip export from the same, or a different, instance. Select a ZIP file to proceed. After the file has been uploaded and validated you\'ll be able to configure & confirm the import in the next view.', + '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_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_size' => ':size Import ZIP Size', + 'import_uploaded_at' => 'Uploaded :relativeTime', + 'import_uploaded_by' => 'Uploaded by', + 'import_location' => 'Import Location', + '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_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', + 'breadcrumb_siblings_for_book' => 'Navigate siblings for book', + 'breadcrumb_siblings_for_bookshelf' => 'Navigate siblings for shelf', // Permissions and restrictions 'permissions' => 'Permisiuni', @@ -132,9 +160,6 @@ 'books_edit_named' => 'Editează cartea :bookName', 'books_form_book_name' => 'Nume carte', 'books_save' => 'Salvează cartea', - 'books_default_template' => 'Default Page Template', - 'books_default_template_explain' => 'Assign a page template that will be used as the default content for all new pages in this book. Keep in mind this will only be used if the page creator has view access to those chosen template page.', - 'books_default_template_select' => 'Select a template page', 'books_permissions' => 'Permisiuni carte', 'books_permissions_updated' => 'Permisiuni carte actualizate', 'books_empty_contents' => 'Nu au fost create pagini sau capitole pentru această carte.', @@ -145,7 +170,9 @@ 'books_search_this' => 'Caută în această carte', 'books_navigation' => 'Navigare carte', 'books_sort' => 'Sortează conținutul cărții', - 'books_sort_desc' => 'Move chapters and pages within a book to reorganise its contents. Other books can be added which allows easy moving of chapters and pages between books.', + 'books_sort_desc' => 'Move chapters and pages within a book to reorganise its contents. Other books can be added which allows easy moving of chapters and pages between books. Optionally an auto sort rule can be set to automatically sort this book\'s contents upon changes.', + 'books_sort_auto_sort' => 'Auto Sort Option', + 'books_sort_auto_sort_active' => 'Auto Sort Active: :sortName', 'books_sort_named' => 'Sortează cartea :bookName', 'books_sort_name' => 'Sortează după nume', 'books_sort_created' => 'Sortează după data creării', @@ -207,7 +234,7 @@ 'pages_delete_draft' => 'Șterge ciorna', 'pages_delete_success' => 'Pagină ștearsă', 'pages_delete_draft_success' => 'Pagină ciornă ștearsă', - 'pages_delete_warning_template' => 'This page is in active use as a book default page template. These books will no longer have a default page template assigned after this page is deleted.', + 'pages_delete_warning_template' => 'This page is in active use as a book or chapter default page template. These books or chapters will no longer have a default page template assigned after this page is deleted.', 'pages_delete_confirm' => 'Ești sigur că dorești să ștergi acestă pagină?', 'pages_delete_draft_confirm' => 'Ești sigur că vrei să ștergi această pagină schiță?', 'pages_editing_named' => 'Editare pagină :pageNume', @@ -224,6 +251,8 @@ 'pages_edit_switch_to_markdown_clean' => '(Curăță conținut)', 'pages_edit_switch_to_markdown_stable' => '(Conținut stabil)', 'pages_edit_switch_to_wysiwyg' => 'Comută la editorul WYSIWYG', + 'pages_edit_switch_to_new_wysiwyg' => 'Switch to new WYSIWYG', + 'pages_edit_switch_to_new_wysiwyg_desc' => '(In Beta Testing)', 'pages_edit_set_changelog' => 'Setare jurnal modificări', 'pages_edit_enter_changelog_desc' => 'Adaugă o scurtă descriere a modificărilor făcute', 'pages_edit_enter_changelog' => 'Intră în jurnalul de modificări', @@ -243,6 +272,7 @@ 'pages_md_insert_drawing' => 'Inserează desen', 'pages_md_show_preview' => 'Arată previzualizarea', 'pages_md_sync_scroll' => 'Sync preview scroll', + 'pages_md_plain_editor' => 'Plaintext editor', 'pages_drawing_unsaved' => 'Unsaved Drawing Found', 'pages_drawing_unsaved_confirm' => 'Unsaved drawing data was found from a previous failed drawing save attempt. Would you like to restore and continue editing this unsaved drawing?', 'pages_not_in_chapter' => 'Pagina nu este într-un capitol', @@ -367,8 +397,11 @@ 'comment' => 'Comentariu', 'comments' => 'Comentarii', 'comment_add' => 'Adaugă comentariu', + 'comment_none' => 'No comments to display', 'comment_placeholder' => 'Lasă un comentariu aici', - 'comment_count' => '{0} Niciun comentariu|{1} 1 Comentariu [2,*] :count Comentarii', + 'comment_thread_count' => ':count Comment Thread|:count Comment Threads', + 'comment_archived_count' => ':count Archived', + 'comment_archived_threads' => 'Archived Threads', 'comment_save' => 'Salvează comentariul', 'comment_new' => 'Comentariu nou', 'comment_created' => 'comentat :createDiff', @@ -377,8 +410,14 @@ 'comment_deleted_success' => 'Comentariu șters', 'comment_created_success' => 'Comentariu adăugat', 'comment_updated_success' => 'Comentariu actualizat', + 'comment_archive_success' => 'Comment archived', + 'comment_unarchive_success' => 'Comment un-archived', + 'comment_view' => 'View comment', + 'comment_jump_to_thread' => 'Jump to thread', 'comment_delete_confirm' => 'Ești sigur că vrei să ștergi acest comentariu?', 'comment_in_reply_to' => 'Ca răspuns la :commentId', + 'comment_reference' => 'Reference', + 'comment_reference_outdated' => '(Outdated)', 'comment_editor_explain' => 'Here are the comments that have been left on this page. Comments can be added & managed when viewing the saved page.', // Revision diff --git a/lang/ro/errors.php b/lang/ro/errors.php index b1ce69cfd91..07c295b0785 100644 --- a/lang/ro/errors.php +++ b/lang/ro/errors.php @@ -10,6 +10,7 @@ // Auth 'error_user_exists_different_creds' => 'Un utilizator cu adresa de e-mail :email există deja, dar cu acreditări diferite.', + 'auth_pre_register_theme_prevention' => 'User account could not be registered for the provided details', 'email_already_confirmed' => 'E-mailul a fost deja confirmat, încearcă să te conectezi.', 'email_confirmation_invalid' => 'Acest token de confirmare nu este valid sau a fost deja folosit, încercă să te înregistrezi din nou.', 'email_confirmation_expired' => 'Token-ul de confirmare a expirat, a fost trimis un nou e-mail de confirmare.', @@ -23,7 +24,6 @@ 'saml_invalid_response_id' => 'Solicitarea de la sistemul extern de autentificare nu este recunoscută de un proces inițiat de această aplicație. Navigarea înapoi după o autentificare ar putea cauza această problemă.', 'saml_fail_authed' => 'Autentificarea folosind :system a eșuat, sistemul nu a furnizat autorizare cu succes', 'oidc_already_logged_in' => 'Deja conectat', - 'oidc_user_not_registered' => 'Utilizatorul :name nu este înregistrat și înregistrarea automată este dezactivată', 'oidc_no_email_address' => 'Nu s-a putut găsi o adresă de e-mail, pentru acest utilizator, în datele furnizate de sistemul extern de autentificare', 'oidc_fail_authed' => 'Autentificarea folosind :system a eșuat, sistemul nu a furnizat autorizare cu succes', 'social_no_action_defined' => 'Nicio acțiune definită', @@ -37,6 +37,7 @@ 'social_driver_not_found' => 'Driver social negăsit', 'social_driver_not_configured' => 'Setările tale sociale :socialAccount nu sunt configurate corect.', 'invite_token_expired' => 'Acest link de invitație a expirat. Poți încerca să îți resetezi parola contului.', + 'login_user_not_found' => 'A user for this action could not be found.', // System 'path_not_writable' => 'Calea fișierului :filePath nu a putut fi încărcată. Asigurați-vă că poate fi scrisă pe server.', @@ -77,6 +78,7 @@ // Users 'users_cannot_delete_only_admin' => 'Nu poți șterge singurul administrator', 'users_cannot_delete_guest' => 'Nu se poate șterge utilizatorul "Vizitator"', + 'users_could_not_send_invite' => 'Could not create user since invite email failed to send', // Roles 'role_cannot_be_edited' => 'Acest rol nu poate fi editat', @@ -103,6 +105,18 @@ 'app_down' => ':appName nu funcționează acum', 'back_soon' => 'Va reveni în curând.', + // Import + 'import_zip_cant_read' => 'Could not read ZIP file.', + 'import_zip_cant_decode_data' => 'Could not find and decode ZIP data.json content.', + 'import_zip_no_data' => 'ZIP file data has no expected book, chapter or page content.', + 'import_validation_failed' => 'Import ZIP failed to validate with errors:', + 'import_zip_failed_notification' => 'Failed to import ZIP file.', + 'import_perms_books' => 'You are lacking the required permissions to create books.', + 'import_perms_chapters' => 'You are lacking the required permissions to create chapters.', + 'import_perms_pages' => 'You are lacking the required permissions to create pages.', + 'import_perms_images' => 'You are lacking the required permissions to create images.', + 'import_perms_attachments' => 'You are lacking the required permission to create attachments.', + // API errors 'api_no_authorization_found' => 'Nu s-a găsit niciun token de autorizare la cerere', 'api_bad_authorization_format' => 'A fost găsit un token de autorizare, dar formatul este incorect', diff --git a/lang/ro/preferences.php b/lang/ro/preferences.php index 893b44eaf95..f7529305c82 100644 --- a/lang/ro/preferences.php +++ b/lang/ro/preferences.php @@ -27,7 +27,7 @@ 'notifications_save' => 'Salvează Preferințe', 'notifications_update_success' => 'Preferințele de notificare au fost actualizate!', 'notifications_watched' => 'Articole urmărite și ignorate', - 'notifications_watched_desc' => ' Mai jos sunt elementele care au fost aplicate preferințe personalizate. Pentru a actualiza preferințele pentru acestea, vizualizați elementul și apoi găsiți opțiunile de ceas în bara laterală.', + 'notifications_watched_desc' => 'Mai jos sunt elementele care au fost aplicate preferințe personalizate. Pentru a actualiza preferințele pentru acestea, vizualizați elementul și apoi găsiți opțiunile de ceas în bara laterală.', 'auth' => 'Acces & Securitate', 'auth_change_password' => 'Schimbă Parola', diff --git a/lang/ro/settings.php b/lang/ro/settings.php index 062d402bcda..a28dca22c57 100644 --- a/lang/ro/settings.php +++ b/lang/ro/settings.php @@ -74,6 +74,36 @@ 'reg_confirm_restrict_domain_desc' => 'Introduceți o listă de domenii de e-mail separate prin virgulă la care doriți să restricționați înregistrarea. Utilizatorilor le va fi trimis un e-mail pentru a-și confirma adresa înainte de a putea interacționa cu aplicația.
Rețineți că utilizatorii vor putea să își schimbe adresele de e-mail după o înregistrare reușită.', 'reg_confirm_restrict_domain_placeholder' => 'Nicio restricție setată', + // Sorting Settings + 'sorting' => 'Sorting', + 'sorting_book_default' => 'Default Book Sort', + 'sorting_book_default_desc' => 'Select the default sort rule to apply to new books. This won\'t affect existing books, and can be overridden per-book.', + 'sorting_rules' => 'Sort Rules', + 'sorting_rules_desc' => 'These are predefined sorting operations which can be applied to content in the system.', + 'sort_rule_assigned_to_x_books' => 'Assigned to :count Book|Assigned to :count Books', + 'sort_rule_create' => 'Create Sort Rule', + 'sort_rule_edit' => 'Edit Sort Rule', + 'sort_rule_delete' => 'Delete Sort Rule', + 'sort_rule_delete_desc' => 'Remove this sort rule from the system. Books using this sort will revert to manual sorting.', + 'sort_rule_delete_warn_books' => 'This sort rule is currently used on :count book(s). Are you sure you want to delete this?', + 'sort_rule_delete_warn_default' => 'This sort rule is currently used as the default for books. Are you sure you want to delete this?', + 'sort_rule_details' => 'Sort Rule Details', + 'sort_rule_details_desc' => 'Set a name for this sort rule, which will appear in lists when users are selecting a sort.', + 'sort_rule_operations' => 'Sort Operations', + 'sort_rule_operations_desc' => 'Configure the sort actions to be performed by moving them from the list of available operations. Upon use, the operations will be applied in order, from top to bottom. Any changes made here will be applied to all assigned books upon save.', + 'sort_rule_available_operations' => 'Available Operations', + 'sort_rule_available_operations_empty' => 'No operations remaining', + 'sort_rule_configured_operations' => 'Configured Operations', + 'sort_rule_configured_operations_empty' => 'Drag/add operations from the "Available Operations" list', + 'sort_rule_op_asc' => '(Asc)', + 'sort_rule_op_desc' => '(Desc)', + 'sort_rule_op_name' => 'Name - Alphabetical', + 'sort_rule_op_name_numeric' => '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_last' => 'Chapters Last', + // Maintenance settings 'maint' => 'Mentenanţă', 'maint_image_cleanup' => 'Curățare imagini', @@ -109,7 +139,7 @@ 'recycle_bin_contents_empty' => 'Coșul de gunoi este în prezent gol', 'recycle_bin_empty' => 'Golește coșul de gunoi', 'recycle_bin_empty_confirm' => 'Aceasta va distruge definitiv toate elementele din coșul de gunoi, inclusiv conținutul conținut din fiecare element. Ești sigur că vrei să golești coșul de gunoi?', - 'recycle_bin_destroy_confirm' => 'Această acțiune va șterge permanent acest element din sistem, împreună cu orice elemente copil enumerat mai jos, și nu vei putea restaura acest conținut. Ești sigur că vrei să ștergi definitiv acest element?', + 'recycle_bin_destroy_confirm' => 'This action will permanently delete this item from the system, along with any child elements listed below, and you will not be able to restore this content. Are you sure you want to permanently delete this item?', 'recycle_bin_destroy_list' => 'Elemente de distrus', 'recycle_bin_restore_list' => 'Elemente care vor fi restaurate', 'recycle_bin_restore_confirm' => 'Această acțiune va restaura elementul șters, inclusiv orice elemente copii, la locația lor originală. În cazul în care locația originală a fost de atunci ștearsă și acum este în coșul de reciclare, elementul părinte va trebui, de asemenea, să fie restaurat.', @@ -162,6 +192,7 @@ 'role_access_api' => 'Accesează API sistem', 'role_manage_settings' => 'Gestionează setările aplicației', 'role_export_content' => 'Exportă conținut', + 'role_import_content' => 'Import content', 'role_editor_change' => 'Schimbă editorul de pagină', 'role_notifications' => 'Primire și gestionare notificări', 'role_asset' => 'Permisiuni active', @@ -276,6 +307,14 @@ 'webhooks_last_errored' => 'Ultima eroare:', 'webhooks_last_error_message' => 'Ultimul mesaj de eroare:', + // Licensing + 'licenses' => 'Licenses', + 'licenses_desc' => 'This page details license information for BookStack in addition to the projects & libraries that are used within BookStack. Many projects listed may only be used in a development context.', + 'licenses_bookstack' => 'BookStack License', + 'licenses_php' => 'PHP Library Licenses', + 'licenses_js' => 'JavaScript Library Licenses', + 'licenses_other' => 'Other Licenses', + 'license_details' => 'License Details', //! If editing translations files directly please ignore this in all //! languages apart from en. Content will be auto-copied from en. @@ -287,6 +326,7 @@ 'bs' => 'Bosanski', 'ca' => 'Català', 'cs' => 'Česky', + 'cy' => 'Cymraeg', 'da' => 'Dansk', 'de' => 'Deutsch (Sie)', 'de_informal' => 'Deutsch (Du)', @@ -308,6 +348,7 @@ 'lt' => 'Lietuvių Kalba', 'lv' => 'Latviešu Valoda', 'nb' => 'Norsk (Bokmål)', + 'ne' => 'नेपाली', 'nn' => 'Nynorsk', 'nl' => 'Nederlands', 'pl' => 'Polski', diff --git a/lang/ro/validation.php b/lang/ro/validation.php index 52394385098..56a3e2e05d1 100644 --- a/lang/ro/validation.php +++ b/lang/ro/validation.php @@ -105,6 +105,11 @@ 'url' => ':attribute nu este valid.', 'uploaded' => 'Fişierul nu a putut fi încărcat. Serverul nu poate accepta fişiere de această dimensiune.', + 'zip_file' => 'The :attribute needs to reference a file within the ZIP.', + 'zip_file_mime' => 'The :attribute needs to reference a file of type :validTypes, found :foundType.', + 'zip_model_expected' => 'Data object expected but ":type" found.', + 'zip_unique' => 'The :attribute must be unique for the object type within the ZIP.', + // Custom validation lines 'custom' => [ 'password-confirm' => [ diff --git a/lang/ru/activities.php b/lang/ru/activities.php index d1a4926399d..d353b7a7640 100644 --- a/lang/ru/activities.php +++ b/lang/ru/activities.php @@ -66,7 +66,7 @@ 'auth_register' => 'зарегистрировался как новый пользователь', 'auth_password_reset_request' => 'запросил смену пароля пользователя', 'auth_password_reset_update' => 'сбросил пароль пользователя', - 'mfa_setup_method' => 'ностроил метод МФА', + 'mfa_setup_method' => 'настроил метод МФА', 'mfa_setup_method_notification' => 'Многофакторный метод аутентификации успешно настроен', 'mfa_remove_method' => 'удалил метод МФА', 'mfa_remove_method_notification' => 'Многофакторный метод аутентификации успешно удален', @@ -84,6 +84,14 @@ 'webhook_delete' => 'удалил вебхук', 'webhook_delete_notification' => 'Вебхук успешно удален', + // Imports + 'import_create' => 'created import', + 'import_create_notification' => 'Import successfully uploaded', + 'import_run' => 'обновлен импорт', + 'import_run_notification' => 'Content successfully imported', + 'import_delete' => 'deleted import', + 'import_delete_notification' => 'Import successfully deleted', + // Users 'user_create' => 'создал пользователя', 'user_create_notification' => 'Пользователь успешно создан', @@ -93,11 +101,11 @@ 'user_delete_notification' => 'Пользователь успешно удален', // API Tokens - 'api_token_create' => 'создал api token', + 'api_token_create' => 'создан API токен', 'api_token_create_notification' => 'API токен успешно создан', - 'api_token_update' => 'обновил api token', + 'api_token_update' => 'обновлён API токен', 'api_token_update_notification' => 'API токен успешно обновлен', - 'api_token_delete' => 'удалил api token', + 'api_token_delete' => 'обновил API токен', 'api_token_delete_notification' => 'API токен успешно удален', // Roles @@ -119,6 +127,14 @@ 'comment_update' => 'обновил комментарий', 'comment_delete' => 'удалил комментарий', + // Sort Rules + 'sort_rule_create' => 'создал правило сортировки', + 'sort_rule_create_notification' => 'Правило сортировки успешно создано', + 'sort_rule_update' => 'обновил правило сортировки', + 'sort_rule_update_notification' => 'Правило сортировки успешно обновлено', + 'sort_rule_delete' => 'удалил правило сортировки', + 'sort_rule_delete_notification' => 'Правило сортировки успешно удалено', + // Other 'permissions_update' => 'обновил разрешения', ]; diff --git a/lang/ru/auth.php b/lang/ru/auth.php index b50d67a9226..d39da119dea 100644 --- a/lang/ru/auth.php +++ b/lang/ru/auth.php @@ -6,7 +6,7 @@ */ return [ - 'failed' => 'Учетная запись не найдена.', + 'failed' => 'Введенные вами данные не найдены в нашей базе.', 'throttle' => 'Слишком много попыток входа. Пожалуйста, повторите попытку через :seconds секунд.', // Login & Register @@ -91,7 +91,7 @@ 'mfa_option_totp_title' => 'Мобильное приложение', 'mfa_option_totp_desc' => 'Для использования многофакторной аутентификации вам понадобится мобильное приложение, поддерживающее TOTP, например Google Authenticator, Authy или Microsoft Authenticator.', 'mfa_option_backup_codes_title' => 'Резервные коды', - 'mfa_option_backup_codes_desc' => 'Безопасно хранить набор одноразовых резервных кодов, которые вы можете ввести для проверки вашей личности.', + 'mfa_option_backup_codes_desc' => 'Генерирует набор одноразовых резервных кодов, которые вы вводите при входе, чтобы проверить вашу личность. Не забудьте сохранить их в безопасном месте.', 'mfa_gen_confirm_and_enable' => 'Подтвердить и включить', 'mfa_gen_backup_codes_title' => 'Настройка резервных кодов', 'mfa_gen_backup_codes_desc' => 'Сохраните приведенный ниже список кодов в безопасном месте. При доступе к системе вы сможете использовать один из кодов в качестве второго механизма аутентификации.', diff --git a/lang/ru/common.php b/lang/ru/common.php index aaffe250c73..fa9969c32af 100644 --- a/lang/ru/common.php +++ b/lang/ru/common.php @@ -20,7 +20,7 @@ 'description' => 'Описание', 'role' => 'Роль', 'cover_image' => 'Обложка', - 'cover_image_description' => 'Изображение должно быть размером около 440x250px.', + 'cover_image_description' => 'Это изображение должно быть приблизительно 440x250px, хотя оно и будет гибко масштабироваться и обрезаться, чтобы соответствовать пользовательскому интерфейсу в различных необходимых сценариях. Так что фактические размеры дисплея будут отличаться.', // Actions 'actions' => 'Действия', @@ -30,6 +30,8 @@ 'create' => 'Создание', 'update' => 'Обновление', 'edit' => 'Редактировать', + 'archive' => 'Archive', + 'unarchive' => 'Un-Archive', 'sort' => 'Сортировать', 'move' => 'Переместить', 'copy' => 'Скопировать', @@ -52,7 +54,7 @@ 'filter_clear' => 'Сбросить фильтр', 'download' => 'Загрузить', 'open_in_tab' => 'Открыть во вкладке', - 'open' => 'Open', + 'open' => 'Открыть', // Sort Options 'sort_options' => 'Параметры сортировки', @@ -107,4 +109,7 @@ // Not directly used but available for convenience to users. 'privacy_policy' => 'Политика конфиденциальности', 'terms_of_service' => 'Условия использования', + + // OpenSearch + 'opensearch_description' => 'Поиск :appName', ]; diff --git a/lang/ru/editor.php b/lang/ru/editor.php index cc26f75dca0..d26f7edfe08 100644 --- a/lang/ru/editor.php +++ b/lang/ru/editor.php @@ -13,6 +13,7 @@ 'cancel' => 'Отмена', 'save' => 'Сохранить', 'close' => 'Закрыть', + 'apply' => 'Применить', 'undo' => 'Отменить', 'redo' => 'Повторить', 'left' => 'Слева', @@ -28,10 +29,10 @@ // Toolbar 'formats' => 'Форматы', - 'header_large' => 'Большой', - 'header_medium' => 'Средний', - 'header_small' => 'Маленький', - 'header_tiny' => 'Крошечный', + 'header_large' => 'Крупный заголовок', + 'header_medium' => 'Средний заголовок', + 'header_small' => 'Небольшой заголовок', + 'header_tiny' => 'Маленький заголовок', 'paragraph' => 'Обычный текст', 'blockquote' => 'Цитата', 'inline_code' => 'Встроенный код', @@ -47,6 +48,7 @@ 'superscript' => 'Надстрочный', 'subscript' => 'Подстрочный', 'text_color' => 'Цвет текста', + 'highlight_color' => 'Highlight color', 'custom_color' => 'Пользовательский цвет', 'remove_color' => 'Удалить цвет', 'background_color' => 'Цвет фона', @@ -81,6 +83,9 @@ 'table_properties' => 'Свойства таблицы', 'table_properties_title' => 'Свойства таблицы', 'delete_table' => 'Удалить таблицу', + 'table_clear_formatting' => 'Очистить форматирование таблицы', + 'resize_to_contents' => 'Изменить размер содержимого', + 'row_header' => 'Заголовок строки', 'insert_row_before' => 'Вставить строку выше', 'insert_row_after' => 'Вставить строку ниже', 'delete_row' => 'Удалить строку', @@ -144,6 +149,7 @@ 'url' => 'URL-адрес', 'text_to_display' => 'Текст для отображения', 'title' => 'Заголовок', + 'browse_links' => 'Browse links', 'open_link' => 'Открыть ссылку', 'open_link_in' => 'Открыть ссылку в...', 'open_link_current' => 'В текущем окне', @@ -160,6 +166,8 @@ 'about' => 'О редакторе', 'about_title' => 'О редакторе WYSIWYG', 'editor_license' => 'Лицензия редактора и авторские права', + 'editor_lexical_license' => 'This editor is built as a fork of :lexicalLink which is distributed under the MIT license.', + 'editor_lexical_license_link' => 'Full license details can be found here.', 'editor_tiny_license' => 'Этот редактор собран с помощью :tinyLink, который предоставляется под MIT лицензией.', 'editor_tiny_license_link' => 'Авторские права и подробности лицензии TinyMCE вы можете найти здесь.', 'save_continue' => 'Сохранить страницу и продолжить', diff --git a/lang/ru/entities.php b/lang/ru/entities.php index a0891244553..50f061115d9 100644 --- a/lang/ru/entities.php +++ b/lang/ru/entities.php @@ -23,7 +23,7 @@ 'meta_updated' => 'Обновлено :timeLength', 'meta_updated_name' => ':user обновил :timeLength', 'meta_owned_name' => 'Владелец :user', - 'meta_reference_count' => 'Referenced by :count item|Referenced by :count items', + 'meta_reference_count' => 'Ссылается :count элемент|Ссылается :count элементов', 'entity_select' => 'Выбор объекта', 'entity_select_lack_permission' => 'У вас нет разрешения на выбор этого элемента', 'images' => 'Изображения', @@ -39,6 +39,34 @@ 'export_pdf' => 'PDF файл', 'export_text' => 'Текстовый файл', 'export_md' => 'Файл Markdown', + 'export_zip' => 'Portable ZIP', + 'default_template' => 'Шаблон страницы по умолчанию', + 'default_template_explain' => 'Назначить шаблон страницы, который будет использоваться в качестве содержимого по умолчанию для всех страниц, созданных в этом элементе. Имейте в виду, что это будет работать, только если создатель страницы имеет доступ к выбранной странице шаблона.', + 'default_template_select' => 'Выберите страницу шаблона', + 'import' => 'Импорт', + 'import_validate' => 'Validate Import', + '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_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_size' => ':size Import ZIP Size', + 'import_uploaded_at' => 'Uploaded :relativeTime', + 'import_uploaded_by' => 'Uploaded by', + 'import_location' => 'Import Location', + '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_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', + 'breadcrumb_siblings_for_book' => 'Navigate siblings for book', + 'breadcrumb_siblings_for_bookshelf' => 'Navigate siblings for shelf', // Permissions and restrictions 'permissions' => 'Разрешения', @@ -132,9 +160,6 @@ 'books_edit_named' => 'Редактировать книгу :bookName', 'books_form_book_name' => 'Название книги', 'books_save' => 'Сохранить книгу', - 'books_default_template' => 'Default Page Template', - 'books_default_template_explain' => 'Assign a page template that will be used as the default content for all new pages in this book. Keep in mind this will only be used if the page creator has view access to those chosen template page.', - 'books_default_template_select' => 'Select a template page', 'books_permissions' => 'Разрешения на книгу', 'books_permissions_updated' => 'Разрешения на книгу обновлены', 'books_empty_contents' => 'Для этой книги нет страниц или разделов.', @@ -145,7 +170,9 @@ 'books_search_this' => 'Поиск в этой книге', 'books_navigation' => 'Навигация по книге', 'books_sort' => 'Сортировка содержимого книги', - 'books_sort_desc' => 'Переместите разделы и страницы в книге, чтобы изменить содержание. Могут быть добавлены другие книги, что позволяет легко перемещать разделы и страницы между книгами.', + 'books_sort_desc' => 'Move chapters and pages within a book to reorganise its contents. Other books can be added which allows easy moving of chapters and pages between books. Optionally an auto sort rule can be set to automatically sort this book\'s contents upon changes.', + 'books_sort_auto_sort' => 'Auto Sort Option', + 'books_sort_auto_sort_active' => 'Auto Sort Active: :sortName', 'books_sort_named' => 'Сортировка книги :bookName', 'books_sort_name' => 'По имени', 'books_sort_created' => 'По дате создания', @@ -207,7 +234,7 @@ 'pages_delete_draft' => 'Удалить черновик', 'pages_delete_success' => 'Страница удалена', 'pages_delete_draft_success' => 'Черновик удален', - 'pages_delete_warning_template' => 'This page is in active use as a book default page template. These books will no longer have a default page template assigned after this page is deleted.', + 'pages_delete_warning_template' => 'Эта страница активно используется как шаблон страницы по умолчанию для книги или главы. Эти книги или главы больше не будут иметь шаблон страницы по умолчанию, назначенный после удаления этой страницы.', 'pages_delete_confirm' => 'Вы действительно хотите удалить эту страницу?', 'pages_delete_draft_confirm' => 'Вы действительно хотите удалить этот черновик?', 'pages_editing_named' => 'Редактирование страницы :pageName', @@ -224,6 +251,8 @@ 'pages_edit_switch_to_markdown_clean' => 'Только Markdown (с возможными потерями форматирования)', '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_set_changelog' => 'Задать список изменений', 'pages_edit_enter_changelog_desc' => 'Введите краткое описание внесенных изменений', 'pages_edit_enter_changelog' => 'Введите список изменений', @@ -243,8 +272,9 @@ 'pages_md_insert_drawing' => 'Вставить рисунок', 'pages_md_show_preview' => 'Предпросмотр', 'pages_md_sync_scroll' => 'Синхронизировать прокрутку', - 'pages_drawing_unsaved' => 'Unsaved Drawing Found', - 'pages_drawing_unsaved_confirm' => 'Unsaved drawing data was found from a previous failed drawing save attempt. Would you like to restore and continue editing this unsaved drawing?', + 'pages_md_plain_editor' => 'Plaintext editor', + 'pages_drawing_unsaved' => 'Найден несохраненный чертеж', + 'pages_drawing_unsaved_confirm' => 'Несохраненные данные были найдены из предыдущей неудачной попытки сохранения рисунка. Вы хотите восстановить и продолжить редактирование несохраненного рисунка?', 'pages_not_in_chapter' => 'Страница не находится в главе', 'pages_move' => 'Переместить страницу', 'pages_copy' => 'Скопировать страницу', @@ -273,12 +303,12 @@ 'pages_revisions_none' => 'У этой страницы нет других версий', 'pages_copy_link' => 'Копировать ссылку', 'pages_edit_content_link' => 'Перейти к разделу в редакторе', - 'pages_pointer_enter_mode' => 'Enter section select mode', + 'pages_pointer_enter_mode' => 'Войти в режим выбора раздела', 'pages_pointer_label' => 'Настройки раздела страницы', 'pages_pointer_permalink' => 'Постоянная ссылка на раздел страницы', 'pages_pointer_include_tag' => 'Раздел страницы с тегом', - 'pages_pointer_toggle_link' => 'Permalink mode, Press to show include tag', - 'pages_pointer_toggle_include' => 'Include tag mode, Press to show permalink', + 'pages_pointer_toggle_link' => 'Режим постоянной ссылки. Нажмите, чтобы показать включение тега', + 'pages_pointer_toggle_include' => 'Включить режим тега. Нажмите для отображения постоянной ссылки', 'pages_permissions_active' => 'Действующие разрешения на страницу', 'pages_initial_revision' => 'Первоначальное издание', 'pages_references_update_revision' => 'Система автоматически обновила внутренние ссылки', @@ -299,7 +329,7 @@ 'pages_is_template' => 'Шаблон страницы', // Editor Sidebar - 'toggle_sidebar' => 'Toggle Sidebar', + 'toggle_sidebar' => 'Переключить боковую панель', 'page_tags' => 'Теги страницы', 'chapter_tags' => 'Теги главы', 'book_tags' => 'Теги книги', @@ -367,8 +397,11 @@ 'comment' => 'Комментарий', 'comments' => 'Комментарии', 'comment_add' => 'Комментировать', + 'comment_none' => 'No comments to display', 'comment_placeholder' => 'Оставить комментарий здесь', - 'comment_count' => '{0} Нет комментариев|{1} 1 комментарий|[2,*] :count комментария', + 'comment_thread_count' => ':count Comment Thread|:count Comment Threads', + 'comment_archived_count' => ':count Archived', + 'comment_archived_threads' => 'Archived Threads', 'comment_save' => 'Сохранить комментарий', 'comment_new' => 'Новый комментарий', 'comment_created' => 'прокомментировал :createDiff', @@ -377,8 +410,14 @@ '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_delete_confirm' => 'Удалить этот комментарий?', 'comment_in_reply_to' => 'В ответ на :commentId', + 'comment_reference' => 'Reference', + 'comment_reference_outdated' => '(Outdated)', 'comment_editor_explain' => 'Вот комментарии, которые были оставлены на этой странице. Комментарии могут быть добавлены и управляться при просмотре сохраненной страницы.', // Revision @@ -409,25 +448,25 @@ // References 'references' => 'Ссылки', 'references_none' => 'Нет отслеживаемых ссылок на этот элемент.', - 'references_to_desc' => 'Listed below is all the known content in the system that links to this item.', + 'references_to_desc' => 'Ниже перечислены все известные материалы в системе, которые ссылаются на этот элемент.', // Watch Options - 'watch' => 'Watch', + 'watch' => 'Наблюдать', 'watch_title_default' => 'Свойства по умолчанию', - 'watch_desc_default' => 'Revert watching to just your default notification preferences.', + 'watch_desc_default' => 'Вернуть просмотр только ваших настроек уведомлений по умолчанию.', 'watch_title_ignore' => 'Игнорировать', 'watch_desc_ignore' => 'Игнорировать все уведомления, включая уведомления из пользовательского уровня.', 'watch_title_new' => 'Новые страницы', 'watch_desc_new' => 'Уведомлять при создании новой страницы внутри этого элемента.', 'watch_title_updates' => 'Все обновления страницы', - 'watch_desc_updates' => 'Notify upon all new pages and page changes.', - 'watch_desc_updates_page' => 'Notify upon all page changes.', + 'watch_desc_updates' => 'Уведомлять обо всех новых страницах и изменениях страницы.', + 'watch_desc_updates_page' => 'Уведомлять о всех изменениях страницы.', 'watch_title_comments' => 'Все обновления и комментарии страниц', 'watch_desc_comments' => 'Уведомлять обо всех новых страницах, изменениях страниц и новых комментариях.', 'watch_desc_comments_page' => 'Уведомлять об изменениях страниц и новых комментариях.', 'watch_change_default' => 'Изменить настройки уведомлений по умолчанию', 'watch_detail_ignore' => 'Игнорирование уведомлений', - 'watch_detail_new' => 'Watching for new pages', + 'watch_detail_new' => 'Наблюдение за новыми страницами', 'watch_detail_updates' => 'Просмотр новых страниц и обновлений', 'watch_detail_comments' => 'Просмотр новых страниц, обновлений и комментариев', 'watch_detail_parent_book' => 'Просмотр через родительскую книгу', diff --git a/lang/ru/errors.php b/lang/ru/errors.php index 2b18494e7fe..e87c9093629 100644 --- a/lang/ru/errors.php +++ b/lang/ru/errors.php @@ -10,6 +10,7 @@ // Auth 'error_user_exists_different_creds' => 'Пользователь с электронной почтой :email уже существует, но с другими учетными данными.', + 'auth_pre_register_theme_prevention' => 'Пользователь не может быть зарегистрирован по предоставленной информации', 'email_already_confirmed' => 'Адрес электронной почты уже был подтвержден, попробуйте войти в систему.', 'email_confirmation_invalid' => 'Этот токен подтверждения недействителен или уже используется. Повторите попытку регистрации.', 'email_confirmation_expired' => 'Истек срок действия токена. Отправлено новое письмо с подтверждением.', @@ -23,7 +24,6 @@ 'saml_invalid_response_id' => 'Запрос от внешней системы аутентификации не распознается процессом, запущенным этим приложением. Переход назад после входа в систему может вызвать эту проблему.', 'saml_fail_authed' => 'Вход с помощью :system не удался, система не предоставила успешную авторизацию', 'oidc_already_logged_in' => 'Вход в систему уже произведен', - 'oidc_user_not_registered' => 'Пользователь :name не зарегистрирован и автоматическая регистрация отключена', 'oidc_no_email_address' => 'Не удалось найти email этого пользователя в данных, предоставленных внешней системой аутентификации', 'oidc_fail_authed' => 'Вход в систему с помощью :system не удался, система не обеспечила успешную авторизацию', 'social_no_action_defined' => 'Действие не определено', @@ -37,22 +37,23 @@ 'social_driver_not_found' => 'Драйвер для Соцсети не найден', 'social_driver_not_configured' => 'Настройки вашего :socialAccount заданы неправильно.', 'invite_token_expired' => 'Срок действия приглашения истек. Вместо этого вы можете попытаться сбросить пароль своей учетной записи.', + 'login_user_not_found' => 'Пользователь для этого действия не найден.', // System 'path_not_writable' => 'Невозможно загрузить файл по пути :filePath. Убедитесь что сервер доступен для записи.', 'cannot_get_image_from_url' => 'Не удается получить изображение из :url', 'cannot_create_thumbs' => 'Сервер не может создавать эскизы. Убедитесь, что у вас установлено расширение GD PHP.', 'server_upload_limit' => 'Сервер не разрешает загрузку файлов такого размера. Попробуйте уменьшить размер файла.', - 'server_post_limit' => 'The server cannot receive the provided amount of data. Try again with less data or a smaller file.', + 'server_post_limit' => 'Сервер не может получить указанный объем данных. Повторите попытку, используя меньшее количество данных или файл меньшего размера.', 'uploaded' => 'Сервер не позволяет загружать файлы такого размера. Пожалуйста, попробуйте файл меньше.', // Drawing & Images 'image_upload_error' => 'Произошла ошибка при загрузке изображения', 'image_upload_type_error' => 'Неправильный тип загружаемого изображения', 'image_upload_replace_type' => 'Замена файла изображения должна быть того же типа', - 'image_upload_memory_limit' => 'Failed to handle image upload and/or create thumbnails due to system resource limits.', - 'image_thumbnail_memory_limit' => 'Failed to create image size variations due to system resource limits.', - 'image_gallery_thumbnail_memory_limit' => 'Failed to create gallery thumbnails due to system resource limits.', + 'image_upload_memory_limit' => 'Не удалось выполнить загрузку изображений и/или создать эскизы из-за ограничения системных ресурсов.', + 'image_thumbnail_memory_limit' => 'Не удалось создать вариации размера изображения из-за ограничений системных ресурсов.', + 'image_gallery_thumbnail_memory_limit' => 'Не удалось создать эскизы галереи из-за ограниченности системных ресурсов.', 'drawing_data_not_found' => 'Данные чертежа не могут быть загружены. Возможно, файл чертежа больше не существует или у вас нет разрешения на доступ к нему.', // Attachments @@ -77,6 +78,7 @@ // Users 'users_cannot_delete_only_admin' => 'Вы не можете удалить единственного администратора', 'users_cannot_delete_guest' => 'Вы не можете удалить гостевого пользователя', + 'users_could_not_send_invite' => 'Could not create user since invite email failed to send', // Roles 'role_cannot_be_edited' => 'Эта роль не может быть изменена', @@ -103,6 +105,18 @@ 'app_down' => ':appName в данный момент не доступно', 'back_soon' => 'Скоро восстановится.', + // Import + 'import_zip_cant_read' => 'Could not read ZIP file.', + 'import_zip_cant_decode_data' => 'Could not find and decode ZIP data.json content.', + 'import_zip_no_data' => 'ZIP file data has no expected book, chapter or page content.', + 'import_validation_failed' => 'Import ZIP failed to validate with errors:', + 'import_zip_failed_notification' => 'Failed to import ZIP file.', + 'import_perms_books' => 'У вас недостаточно прав для создания книг.', + 'import_perms_chapters' => 'У вас недостаточно прав для создания глав.', + 'import_perms_pages' => 'У вас недостаточно прав для создания страниц.', + 'import_perms_images' => 'У вас недостаточно прав для создания изображений.', + 'import_perms_attachments' => 'У вас недостаточно прав для создания вложений.', + // API errors 'api_no_authorization_found' => 'Отсутствует токен авторизации в запросе', 'api_bad_authorization_format' => 'Токен авторизации найден, но формат запроса неверен', diff --git a/lang/ru/notifications.php b/lang/ru/notifications.php index f37f11986ff..c5e98da80ef 100644 --- a/lang/ru/notifications.php +++ b/lang/ru/notifications.php @@ -10,10 +10,10 @@ 'new_page_intro' => 'Новая страница была создана в :appName:', 'updated_page_subject' => 'Обновлена страница: :pageName', 'updated_page_intro' => 'Страница была обновлена в :appName:', - 'updated_page_debounce' => 'To prevent a mass of notifications, for a while you won\'t be sent notifications for further edits to this page by the same editor.', + 'updated_page_debounce' => 'Чтобы предотвратить массовые уведомления, в течение некоторого времени вы не будете получать уведомления о дальнейших правках этой страницы этим же редактором.', 'detail_page_name' => 'Имя страницы:', - 'detail_page_path' => 'Page Path:', + 'detail_page_path' => 'Путь страницы:', 'detail_commenter' => 'Комментатор:', 'detail_comment' => 'Комментарий:', 'detail_created_by' => 'Создано:', @@ -22,6 +22,6 @@ 'action_view_comment' => 'Просмотреть комментарий', 'action_view_page' => 'Посмотреть страницу', - 'footer_reason' => 'This notification was sent to you because :link cover this type of activity for this item.', + 'footer_reason' => 'Это уведомление было отправлено, потому что :link покрывает этот тип активности для этого элемента.', 'footer_reason_link' => 'ваши настройки уведомлений', ]; diff --git a/lang/ru/preferences.php b/lang/ru/preferences.php index 126aa6f1aed..27217815d13 100644 --- a/lang/ru/preferences.php +++ b/lang/ru/preferences.php @@ -5,10 +5,10 @@ */ return [ - 'my_account' => 'My Account', + 'my_account' => 'Моя учетная запись', 'shortcuts' => 'Горячие клавиши', - 'shortcuts_interface' => 'UI Shortcut Preferences', + 'shortcuts_interface' => 'Настройки горячих клавиш', 'shortcuts_toggle_desc' => 'Здесь вы можете включить или отключить горячие клавиши системного интерфейса, используемые для навигации и действий.', 'shortcuts_customize_desc' => 'Вы можете настроить каждую из горячих клавиш ниже. Просто нажмите комбинацию клавиш после выбора вставки для горячих клавиш.', 'shortcuts_toggle_label' => 'Горячие клавиши включены', @@ -17,35 +17,35 @@ 'shortcuts_save' => 'Сохранить горячие клавиши', 'shortcuts_overlay_desc' => 'Примечание: Когда горячие клавиши включены, вспомогательное наложение доступно через нажатие "?", которая будет подсвечивать доступные горячие клавиши для действий, видимых в настоящее время на экране.', 'shortcuts_update_success' => 'Настройки горячих клавиш были обновлены!', - 'shortcuts_overview_desc' => 'Manage keyboard shortcuts you can use to navigate the system user interface.', + 'shortcuts_overview_desc' => 'Управление клавишами быстрого доступа, которые можно использовать для навигации по системному интерфейсу.', - 'notifications' => 'Notification Preferences', - 'notifications_desc' => 'Control the email notifications you receive when certain activity is performed within the system.', - 'notifications_opt_own_page_changes' => 'Notify upon changes to pages I own', - 'notifications_opt_own_page_comments' => 'Notify upon comments on pages I own', - 'notifications_opt_comment_replies' => 'Notify upon replies to my comments', - 'notifications_save' => 'Save Preferences', - 'notifications_update_success' => 'Notification preferences have been updated!', - 'notifications_watched' => 'Watched & Ignored Items', - 'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.', + 'notifications' => 'Настройки уведомлений', + 'notifications_desc' => 'Управляйте полученными по электронной почте уведомлениями при выполнении определенных действий в системе.', + 'notifications_opt_own_page_changes' => 'Уведомлять об изменениях в собственных страницах', + 'notifications_opt_own_page_comments' => 'Уведомлять о комментариях на собственных страницах', + 'notifications_opt_comment_replies' => 'Уведомлять об ответах на мои комментарии', + 'notifications_save' => 'Сохранить настройки', + 'notifications_update_success' => 'Настройки уведомлений были обновлены!', + 'notifications_watched' => 'Просмотренные и игнорированные элементы', + 'notifications_watched_desc' => 'Ниже приведены элементы, которые имеют пользовательские настройки наблюдения. Чтобы обновить ваши предпочтения, посмотрите этот пункт и найдите варианты наблюдения в боковой панели.', - 'auth' => 'Access & Security', - 'auth_change_password' => 'Change Password', - 'auth_change_password_desc' => 'Change the password you use to log-in to the application. This must be at least 8 characters long.', - 'auth_change_password_success' => 'Password has been updated!', + 'auth' => 'Доступ и безопасность', + 'auth_change_password' => 'Изменить пароль', + 'auth_change_password_desc' => 'Установите пароль для входа в приложение. Длина пароля должна быть не менее 8 символов.', + 'auth_change_password_success' => 'Пароль был обновлен!', - 'profile' => 'Profile Details', - 'profile_desc' => 'Manage the details of your account which represents you to other users, in addition to details that are used for communication and system personalisation.', - 'profile_view_public' => 'View Public Profile', - 'profile_name_desc' => 'Configure your display name which will be visible to other users in the system through the activity you perform, and content you own.', - 'profile_email_desc' => 'This email will be used for notifications and, depending on active system authentication, system access.', - 'profile_email_no_permission' => 'Unfortunately you don\'t have permission to change your email address. If you want to change this, you\'d need to ask an administrator to change this for you.', - 'profile_avatar_desc' => 'Select an image which will be used to represent yourself to others in the system. Ideally this image should be square and about 256px in width and height.', - 'profile_admin_options' => 'Administrator Options', - 'profile_admin_options_desc' => 'Additional administrator-level options, like those to manage role assignments, can be found for your user account in the "Settings > Users" area of the application.', + 'profile' => 'Детали профиля', + 'profile_desc' => 'Управляйте деталями вашей учетной записи, что представляют вас другим пользователям, в дополнение к деталям, используемым для персонализации коммуникации и системы.', + 'profile_view_public' => 'Просмотреть публичный профиль', + 'profile_name_desc' => 'Настройте отображаемое имя, видимое другим пользователям системы через действия, что вы выполняете, и контент, которым вы владеете.', + 'profile_email_desc' => 'Этот адрес электронной почты будет использоваться для уведомлений и, в зависимости от активной системы аутентификации, для доступа к системе.', + 'profile_email_no_permission' => 'К сожалению, у вас нет разрешения на изменение адреса электронной почты. Если вам действительно необходимо его изменить, нужно попросить администратора сделать это.', + 'profile_avatar_desc' => 'Выберите изображение, которое будет использоваться для представления себя другим в системе. По возможности это изображение должно быть квадратным и около 256 пикселей по ширине и высоте.', + 'profile_admin_options' => 'Административные параметры', + 'profile_admin_options_desc' => 'Дополнительные параметры уровня администратора, такие как управление назначением ролей, могут быть найдены для вашей учетной записи в области "Настройки > Пользователи".', - 'delete_account' => 'Delete Account', - 'delete_my_account' => 'Delete My Account', - 'delete_my_account_desc' => 'This will fully delete your user account from the system. You will not be able to recover this account or revert this action. Content you\'ve created, such as created pages and uploaded images, will remain.', - 'delete_my_account_warning' => 'Are you sure you want to delete your account?', + 'delete_account' => 'Удалить аккаунт', + 'delete_my_account' => 'Удалить мой аккаунт', + 'delete_my_account_desc' => 'Это полностью удалит вашу учетную запись из системы. Вы не сможете отменить это действие или восстановить эту учетную запись. Созданный вами контент, такой как созданные страницы и загруженные изображения, останется без изменений.', + 'delete_my_account_warning' => 'Вы уверены, что хотите удалить свой аккаунт?', ]; diff --git a/lang/ru/settings.php b/lang/ru/settings.php index ef11be4bd62..69c13e46dd3 100644 --- a/lang/ru/settings.php +++ b/lang/ru/settings.php @@ -74,6 +74,36 @@ 'reg_confirm_restrict_domain_desc' => 'Введите список доменов почты через запятую, для которых разрешена регистрация. Пользователям будет отправлено письмо для подтверждения адреса перед входом в приложение.
Обратите внимание, что пользователи смогут изменить свой адрес после регистрации.', 'reg_confirm_restrict_domain_placeholder' => 'Без ограничений', + // Sorting Settings + 'sorting' => 'Сортировка', + 'sorting_book_default' => 'Сортировка книг по умолчанию', + 'sorting_book_default_desc' => 'Выберите правило сортировки по умолчанию для новых книг. Это не повлияет на существующие книги, и может быть изменено для каждой книги отдельно.', + 'sorting_rules' => 'Правила сортировки', + 'sorting_rules_desc' => 'Выберите правило сортировки по умолчанию для новых книг. Это не повлияет на существующие книги и может быть изменено для каждой книги отдельно.', + 'sort_rule_assigned_to_x_books' => 'Используется в :count книгах', + 'sort_rule_create' => 'Создать правило сортировки', + 'sort_rule_edit' => 'Edit Sort Rule', + 'sort_rule_delete' => 'Удалить правило сортировки', + 'sort_rule_delete_desc' => 'Удалить это правило сортировки из системы. Книги, использующие эту сортировку, вернутся к ручной сортировке.', + 'sort_rule_delete_warn_books' => 'This sort rule is currently used on :count book(s). Are you sure you want to delete this?', + 'sort_rule_delete_warn_default' => 'Это правило сортировки используется по умолчанию для книг. Вы уверены, что хотите удалить его?', + 'sort_rule_details' => 'Детали правила сортировки', + 'sort_rule_details_desc' => 'Set a name for this sort rule, which will appear in lists when users are selecting a sort.', + 'sort_rule_operations' => 'Sort Operations', + 'sort_rule_operations_desc' => 'Configure the sort actions to be performed by moving them from the list of available operations. Upon use, the operations will be applied in order, from top to bottom. Any changes made here will be applied to all assigned books upon save.', + 'sort_rule_available_operations' => 'Доступные операции', + 'sort_rule_available_operations_empty' => 'No operations remaining', + 'sort_rule_configured_operations' => 'Configured Operations', + 'sort_rule_configured_operations_empty' => 'Перетащите/добавьте операции из списка "Доступные операции"', + 'sort_rule_op_asc' => '(Возрастание)', + 'sort_rule_op_desc' => '(Убывание)', + 'sort_rule_op_name' => 'Name - Alphabetical', + '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_last' => 'Главы в конце', + // Maintenance settings 'maint' => 'Обслуживание', 'maint_image_cleanup' => 'Очистка изображений', @@ -86,7 +116,7 @@ 'maint_send_test_email' => 'Отправить тестовое письмо', 'maint_send_test_email_desc' => 'Отправить тестовое письмо на адрес электронной почты, указанный в профиле.', 'maint_send_test_email_run' => 'Отправить письмо', - 'maint_send_test_email_success' => 'На адрес :address отравлено письмо', + 'maint_send_test_email_success' => 'Письмо отправлено на :address', 'maint_send_test_email_mail_subject' => 'Проверка электронной почты', 'maint_send_test_email_mail_greeting' => 'Доставка электронной почты работает!', 'maint_send_test_email_mail_text' => 'Поздравляем! Поскольку вы получили это письмо, электронная почта настроена правильно.', @@ -109,7 +139,7 @@ 'recycle_bin_contents_empty' => 'На данный момент корзина пуста', 'recycle_bin_empty' => 'Очистить корзину', 'recycle_bin_empty_confirm' => 'Это действие навсегда уничтожит все элементы в корзине, включая содержимое, содержащееся в каждом элементе. Вы уверены, что хотите очистить корзину?', - 'recycle_bin_destroy_confirm' => 'Это действие удалит этот элемент навсегда вместе с любыми дочерними элементами, перечисленными ниже, и вы не сможете восстановить этот контент. Вы уверены, что хотите навсегда удалить этот элемент?', + 'recycle_bin_destroy_confirm' => 'Это действие навсегда удалит этот элемент из системы, вместе с любыми дочерними элементами, перечисленными ниже, и вы не сможете восстановить этот контент. Вы уверены, что хотите навсегда удалить этот элемент?', 'recycle_bin_destroy_list' => 'Элементы для удаления', 'recycle_bin_restore_list' => 'Элементы для восстановления', 'recycle_bin_restore_confirm' => 'Это действие восстановит удаленный элемент, включая дочерние, в исходное место. Если исходное место было удалено и теперь находится в корзине, родительский элемент также необходимо будет восстановить.', @@ -162,6 +192,7 @@ 'role_access_api' => 'Доступ к системному API', 'role_manage_settings' => 'Управление настройками приложения', 'role_export_content' => 'Экспорт контента', + 'role_import_content' => 'Import content', 'role_editor_change' => 'Изменение редактора страниц', 'role_notifications' => 'Получение и управление уведомлениями', 'role_asset' => 'Права доступа к материалам', @@ -193,8 +224,8 @@ 'users_send_invite_text' => 'Вы можете отправить этому пользователю письмо с приглашением, которое позволит ему установить пароль самостоятельно или задайте пароль сами.', 'users_send_invite_option' => 'Отправить пользователю письмо с приглашением', 'users_external_auth_id' => 'Внешний ID аутентификации', - 'users_external_auth_id_desc' => 'When an external authentication system is in use (such as SAML2, OIDC or LDAP) this is the ID which links this BookStack user to the authentication system account. You can ignore this field if using the default email-based authentication.', - 'users_password_warning' => 'Only fill the below if you would like to change the password for this user.', + 'users_external_auth_id_desc' => 'Когда используется внешняя система аутентификации (например, SAML2, OIDC или LDAP), этот идентификатор будет использоваться для связывания пользователя BookStack с учетной записью системы аутентификации. Вы можете игнорировать это поле, если используете стандартную аутентификацию по электронной почте.', + 'users_password_warning' => 'Заполните поля ниже только если вы хотите изменить пароль.', 'users_system_public' => 'Этот пользователь представляет любых гостевых пользователей, которые посещают ваше приложение. Он не может использоваться для входа в систему и назначается автоматически.', 'users_delete' => 'Удалить пользователя', 'users_delete_named' => 'Удалить пользователя :userName', @@ -210,7 +241,7 @@ 'users_preferred_language' => 'Предпочитаемый язык', 'users_preferred_language_desc' => 'Этот параметр изменит язык интерфейса приложения. Это не влияет на созданный пользователем контент.', 'users_social_accounts' => 'Аккаунты социальных сетей', - 'users_social_accounts_desc' => 'View the status of the connected social accounts for this user. Social accounts can be used in addition to the primary authentication system for system access.', + 'users_social_accounts_desc' => 'Просмотр статуса подключенных социальных учетных записей для этого пользователя. Учетные записи социальных сетей могут использоваться в дополнение к системе первичной аутентификации для доступа к системе.', 'users_social_accounts_info' => 'Здесь вы можете подключить другие учетные записи для более быстрого и легкого входа в систему. Отключение учетной записи здесь не возможно. Отмените доступ к настройкам вашего профиля в подключенном социальном аккаунте.', 'users_social_connect' => 'Подключить аккаунт', 'users_social_disconnect' => 'Отключить аккаунт', @@ -276,6 +307,14 @@ 'webhooks_last_errored' => 'Последняя ошибка:', 'webhooks_last_error_message' => 'Последнее сообщение об ошибке:', + // Licensing + 'licenses' => 'Лицензии', + 'licenses_desc' => 'Эта страница содержит сведения о лицензиях для BookStack в дополнение к проектам и библиотекам, которые используются в BookStack. Многие перечисленные проекты могут использоваться только в контексте разработки.', + 'licenses_bookstack' => 'Лицензия BookStack', + 'licenses_php' => 'Лицензии PHP библиотек', + 'licenses_js' => 'Лицензии JavaScript библиотек', + 'licenses_other' => 'Прочие лицензии', + 'license_details' => 'Подробности о лицензии', //! If editing translations files directly please ignore this in all //! languages apart from en. Content will be auto-copied from en. @@ -287,6 +326,7 @@ 'bs' => 'Bosanski', 'ca' => 'Català', 'cs' => 'Česky', + 'cy' => 'Cymraeg', 'da' => 'Dansk', 'de' => 'Deutsch (Sie)', 'de_informal' => 'Deutsch (Du)', @@ -308,6 +348,7 @@ 'lt' => 'Lietuvių Kalba', 'lv' => 'Latviešu Valoda', 'nb' => 'Norsk (Bokmål)', + 'ne' => 'नेपाली', 'nn' => 'Nynorsk', 'nl' => 'Nederlands', 'pl' => 'Polski', diff --git a/lang/ru/validation.php b/lang/ru/validation.php index 88bd9a41f97..156a05fe678 100644 --- a/lang/ru/validation.php +++ b/lang/ru/validation.php @@ -105,6 +105,11 @@ 'url' => 'Формат :attribute некорректен.', 'uploaded' => 'Не удалось загрузить файл. Сервер не может принимать файлы такого размера.', + 'zip_file' => 'The :attribute needs to reference a file within the ZIP.', + 'zip_file_mime' => 'The :attribute needs to reference a file of type :validTypes, found :foundType.', + 'zip_model_expected' => 'Data object expected but ":type" found.', + 'zip_unique' => 'The :attribute must be unique for the object type within the ZIP.', + // Custom validation lines 'custom' => [ 'password-confirm' => [ diff --git a/lang/sk/activities.php b/lang/sk/activities.php index 9750afa1784..65542fbe2a1 100644 --- a/lang/sk/activities.php +++ b/lang/sk/activities.php @@ -15,7 +15,7 @@ 'page_restore' => 'obnovil(a) stránku', 'page_restore_notification' => 'Stránka úspešne obnovená', 'page_move' => 'presunul(a) stránku', - 'page_move_notification' => 'Page successfully moved', + 'page_move_notification' => 'Stránka bola úspešne presunutá', // Chapters 'chapter_create' => 'vytvoril(a) kapitolu', @@ -25,7 +25,7 @@ 'chapter_delete' => 'odstránil(a) kapitolu', 'chapter_delete_notification' => 'Kapitola úspešne odstránená', 'chapter_move' => 'presunul(a) kapitolu', - 'chapter_move_notification' => 'Chapter successfully moved', + 'chapter_move_notification' => 'Kapitola bola úspešne presunutá', // Books 'book_create' => 'vytvoril(a) knihu', @@ -51,8 +51,8 @@ // Revisions 'revision_restore' => 'restored revision', - 'revision_delete' => 'deleted revision', - 'revision_delete_notification' => 'Revision successfully deleted', + 'revision_delete' => 'odstránil(a) revíziu', + 'revision_delete_notification' => 'Revízia úspešne odstránená', // Favourites 'favourite_add_notification' => '":name" bol pridaný medzi obľúbené', @@ -62,7 +62,7 @@ 'watch_update_level_notification' => 'Watch preferences successfully updated', // Auth - 'auth_login' => 'logged in', + 'auth_login' => 'sa prihlásil(a)', 'auth_register' => 'registered as new user', 'auth_password_reset_request' => 'requested user password reset', 'auth_password_reset_update' => 'reset user password', @@ -72,8 +72,8 @@ 'mfa_remove_method_notification' => 'Viacúrovňový spôsob overenia úspešne odstránený', // Settings - 'settings_update' => 'updated settings', - 'settings_update_notification' => 'Settings successfully updated', + 'settings_update' => 'aktualizované nastavenia', + 'settings_update_notification' => 'Nastavenia boli úspešne aktualizované', 'maintenance_action_run' => 'ran maintenance action', // Webhooks @@ -84,20 +84,28 @@ 'webhook_delete' => 'odstránil(a) si webhook', 'webhook_delete_notification' => 'Webhook úspešne odstránený', + // Imports + 'import_create' => 'created import', + 'import_create_notification' => 'Import successfully uploaded', + 'import_run' => 'updated import', + 'import_run_notification' => 'Content successfully imported', + 'import_delete' => 'deleted import', + 'import_delete_notification' => 'Import successfully deleted', + // Users - 'user_create' => 'created user', + 'user_create' => 'užívateľ vytvorený', 'user_create_notification' => 'User successfully created', - 'user_update' => 'updated user', + 'user_update' => 'používateľ aktualizovaný', 'user_update_notification' => 'Používateľ úspešne upravený', - 'user_delete' => 'deleted user', + 'user_delete' => 'odstránený používateľ', 'user_delete_notification' => 'Používateľ úspešne zmazaný', // API Tokens - 'api_token_create' => 'created api token', + 'api_token_create' => 'created API token', 'api_token_create_notification' => 'API token successfully created', - 'api_token_update' => 'updated api token', + 'api_token_update' => 'updated API token', 'api_token_update_notification' => 'API token successfully updated', - 'api_token_delete' => 'deleted api token', + 'api_token_delete' => 'deleted API token', 'api_token_delete_notification' => 'API token successfully deleted', // Roles @@ -105,7 +113,7 @@ 'role_create_notification' => 'Rola úspešne vytvorená', 'role_update' => 'updated role', 'role_update_notification' => 'Rola úspešne aktualizovaná', - 'role_delete' => 'deleted role', + 'role_delete' => 'odstrániť rolu', 'role_delete_notification' => 'Rola úspešne zmazaná', // Recycle Bin @@ -115,9 +123,17 @@ // Comments 'commented_on' => 'komentoval(a)', - 'comment_create' => 'added comment', - 'comment_update' => 'updated comment', - 'comment_delete' => 'deleted comment', + 'comment_create' => 'pridal(a) komentár', + 'comment_update' => 'aktualizoval(a) komentár', + 'comment_delete' => 'odstrániť komentár', + + // Sort Rules + 'sort_rule_create' => 'created sort rule', + 'sort_rule_create_notification' => 'Sort rule successfully created', + 'sort_rule_update' => 'updated sort rule', + 'sort_rule_update_notification' => 'Sort rule successfully updated', + 'sort_rule_delete' => 'deleted sort rule', + 'sort_rule_delete_notification' => 'Sort rule successfully deleted', // Other 'permissions_update' => 'aktualizované oprávnenia', diff --git a/lang/sk/auth.php b/lang/sk/auth.php index 084de678e2a..11ccdc8893f 100644 --- a/lang/sk/auth.php +++ b/lang/sk/auth.php @@ -91,7 +91,7 @@ 'mfa_option_totp_title' => 'Mobilná aplikácia', 'mfa_option_totp_desc' => 'Pre používanie viacúrovňového prihlasovania budete potrebovať mobilnú aplikáciu, ktorá podporuje TOPS ako napríklad Google Authenticator, Authy alebo Microsoft Authenticator.', 'mfa_option_backup_codes_title' => 'Záložné kódy', - 'mfa_option_backup_codes_desc' => 'Bezpečne uložte jednorázové záložné kódy pre overenie vačej identity.', + 'mfa_option_backup_codes_desc' => 'Generates a set of one-time-use backup codes which you\'ll enter on login to verify your identity. Make sure to store these in a safe & secure place.', 'mfa_gen_confirm_and_enable' => 'Potvrdiť a zapnúť', 'mfa_gen_backup_codes_title' => 'Nastavenie záložných kódov', 'mfa_gen_backup_codes_desc' => 'Uložte si tieto kódy na bezpečné miesto. Jeden z kódov budete môcť použiť ako druhý faktor overenia identiy na prihlásenie sa.', diff --git a/lang/sk/common.php b/lang/sk/common.php index 898e08fdb53..6f116006b12 100644 --- a/lang/sk/common.php +++ b/lang/sk/common.php @@ -20,7 +20,7 @@ 'description' => 'Popis', 'role' => 'Rola', 'cover_image' => 'Obal knihy', - 'cover_image_description' => 'Tento obrázok by mal byť približne 300 x 170 pixelov.', + 'cover_image_description' => 'This image should be approximately 440x250px although it will be flexibly scaled & cropped to fit the user interface in different scenarios as required, so actual dimensions for display will differ.', // Actions 'actions' => 'Akcie', @@ -30,6 +30,8 @@ 'create' => 'Vytvoriť', 'update' => 'Aktualizovať', 'edit' => 'Editovať', + 'archive' => 'Archive', + 'unarchive' => 'Un-Archive', 'sort' => 'Zoradiť', 'move' => 'Presunúť', 'copy' => 'Kopírovať', @@ -107,4 +109,7 @@ // Not directly used but available for convenience to users. 'privacy_policy' => 'Zásady ochrany osobných údajov', 'terms_of_service' => 'Podmienky používania', + + // OpenSearch + 'opensearch_description' => 'Search :appName', ]; diff --git a/lang/sk/editor.php b/lang/sk/editor.php index 61ac9ca9e23..7d1781ef457 100644 --- a/lang/sk/editor.php +++ b/lang/sk/editor.php @@ -13,6 +13,7 @@ 'cancel' => 'Zrušiť', 'save' => 'Uložiť', 'close' => 'Zavrieť', + 'apply' => 'Apply', 'undo' => 'Vrátiť späť', 'redo' => 'Obnoviť', 'left' => 'Vľavo', @@ -47,6 +48,7 @@ 'superscript' => 'Horný index', 'subscript' => 'Dolný index', 'text_color' => 'Farba textu', + 'highlight_color' => 'Highlight color', 'custom_color' => 'Vlastná farba', 'remove_color' => 'Odstrániť farbu', 'background_color' => 'Farba pozadia', @@ -81,6 +83,9 @@ 'table_properties' => 'Vlastnosti tabuľky', 'table_properties_title' => 'Vlastnosti tabuľky', 'delete_table' => 'Vymazať tabuľku', + 'table_clear_formatting' => 'Clear table formatting', + 'resize_to_contents' => 'Resize to contents', + 'row_header' => 'Row header', 'insert_row_before' => 'Vložiť riadok pred', 'insert_row_after' => 'Vložiť riadok za', 'delete_row' => 'Vymazať riadok', @@ -144,6 +149,7 @@ 'url' => 'URL', 'text_to_display' => 'Text na zobrazenie', 'title' => 'Názov', + 'browse_links' => 'Browse links', 'open_link' => 'Otvoriť odkaz', 'open_link_in' => 'Otvoriť odkaz v...', 'open_link_current' => 'Iba aktuálne okno', @@ -160,6 +166,8 @@ 'about' => 'O editore', 'about_title' => 'O WYSIWYG Editore', 'editor_license' => 'Licencia editora a autorské práva', + 'editor_lexical_license' => 'This editor is built as a fork of :lexicalLink which is distributed under the MIT license.', + 'editor_lexical_license_link' => 'Full license details can be found here.', 'editor_tiny_license' => 'Tento editor je vytvorený pomocou :tinyLink, ktorý je poskytovaný pod licenciou MIT.', 'editor_tiny_license_link' => 'Podrobnosti o autorských právach a licenciách TinyMCE nájdete tu.', 'save_continue' => 'Uložiť a pokračovať', diff --git a/lang/sk/entities.php b/lang/sk/entities.php index 0d74c691aa8..eb52545357c 100644 --- a/lang/sk/entities.php +++ b/lang/sk/entities.php @@ -39,6 +39,34 @@ 'export_pdf' => 'PDF súbor', 'export_text' => 'Súbor s čistým textom', 'export_md' => 'Súbor Markdown', + 'export_zip' => 'Portable ZIP', + 'default_template' => 'Default Page Template', + 'default_template_explain' => 'Assign a page template that will be used as the default content for all pages created within this item. Keep in mind this will only be used if the page creator has view access to the chosen template page.', + 'default_template_select' => 'Select a template page', + 'import' => 'Import', + 'import_validate' => 'Validate Import', + 'import_desc' => 'Import books, chapters & pages using a portable zip export from the same, or a different, instance. Select a ZIP file to proceed. After the file has been uploaded and validated you\'ll be able to configure & confirm the import in the next view.', + '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_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_size' => ':size Import ZIP Size', + 'import_uploaded_at' => 'Uploaded :relativeTime', + 'import_uploaded_by' => 'Uploaded by', + 'import_location' => 'Import Location', + '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_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', + 'breadcrumb_siblings_for_book' => 'Navigate siblings for book', + 'breadcrumb_siblings_for_bookshelf' => 'Navigate siblings for shelf', // Permissions and restrictions 'permissions' => 'Oprávnenia', @@ -132,9 +160,6 @@ 'books_edit_named' => 'Upraviť knihu :bookName', 'books_form_book_name' => 'Názov knihy', 'books_save' => 'Uložiť knihu', - 'books_default_template' => 'Default Page Template', - 'books_default_template_explain' => 'Assign a page template that will be used as the default content for all new pages in this book. Keep in mind this will only be used if the page creator has view access to those chosen template page.', - 'books_default_template_select' => 'Select a template page', 'books_permissions' => 'Oprávnenia knihy', 'books_permissions_updated' => 'Oprávnenia knihy aktualizované', 'books_empty_contents' => 'Pre túto knihu neboli vytvorené žiadne stránky alebo kapitoly.', @@ -145,7 +170,9 @@ 'books_search_this' => 'Hľadať v tejto knihe', 'books_navigation' => 'Navigácia knihy', 'books_sort' => 'Zoradiť obsah knihy', - 'books_sort_desc' => 'Presúvajte kapitoly a strany v rámci knihy, aby ste reorganizovali jej obsah. Je možné pridať ďalšie knihy, čo umožňuje jednoduché presúvanie kapitol a strán medzi knihami.', + 'books_sort_desc' => 'Move chapters and pages within a book to reorganise its contents. Other books can be added which allows easy moving of chapters and pages between books. Optionally an auto sort rule can be set to automatically sort this book\'s contents upon changes.', + 'books_sort_auto_sort' => 'Auto Sort Option', + 'books_sort_auto_sort_active' => 'Auto Sort Active: :sortName', 'books_sort_named' => 'Zoradiť knihu :bookName', 'books_sort_name' => 'Zoradiť podľa mena', 'books_sort_created' => 'Zoradiť podľa dátumu vytvorenia', @@ -207,7 +234,7 @@ 'pages_delete_draft' => 'Zmazať koncept', 'pages_delete_success' => 'Stránka zmazaná', 'pages_delete_draft_success' => 'Koncept stránky zmazaný', - 'pages_delete_warning_template' => 'This page is in active use as a book default page template. These books will no longer have a default page template assigned after this page is deleted.', + 'pages_delete_warning_template' => 'This page is in active use as a book or chapter default page template. These books or chapters will no longer have a default page template assigned after this page is deleted.', 'pages_delete_confirm' => 'Ste si istý, že chcete zmazať túto stránku?', 'pages_delete_draft_confirm' => 'Ste si istý, že chcete zmazať tento koncept stránky?', 'pages_editing_named' => 'Upraviť stránku :pageName', @@ -224,6 +251,8 @@ 'pages_edit_switch_to_markdown_clean' => '(Vyčistiť obsah)', 'pages_edit_switch_to_markdown_stable' => '(Stabilný obsah)', 'pages_edit_switch_to_wysiwyg' => 'Prepnite na WYSIWYG Editor', + 'pages_edit_switch_to_new_wysiwyg' => 'Switch to new WYSIWYG', + 'pages_edit_switch_to_new_wysiwyg_desc' => '(In Beta Testing)', 'pages_edit_set_changelog' => 'Nastaviť záznam zmien', 'pages_edit_enter_changelog_desc' => 'Zadajte krátky popis zmien, ktoré ste urobili', 'pages_edit_enter_changelog' => 'Zadať záznam zmien', @@ -243,6 +272,7 @@ 'pages_md_insert_drawing' => 'Vložiť kresbu', 'pages_md_show_preview' => 'Zobraziť náhľad', 'pages_md_sync_scroll' => 'Posúvanie ukážky synchronizácie', + 'pages_md_plain_editor' => 'Plaintext editor', 'pages_drawing_unsaved' => 'Unsaved Drawing Found', 'pages_drawing_unsaved_confirm' => 'Unsaved drawing data was found from a previous failed drawing save attempt. Would you like to restore and continue editing this unsaved drawing?', 'pages_not_in_chapter' => 'Stránka nie je v kapitole', @@ -367,8 +397,11 @@ 'comment' => 'Komentár', 'comments' => 'Komentáre', 'comment_add' => 'Pridať komentár', + 'comment_none' => 'No comments to display', 'comment_placeholder' => 'Tu zadajte svoje pripomienky', - 'comment_count' => '{0} Bez komentárov|{1} 1 komentár|[2,3,4] :count komentáre|[5,*] :count komentárov', + 'comment_thread_count' => ':count Comment Thread|:count Comment Threads', + 'comment_archived_count' => ':count Archived', + 'comment_archived_threads' => 'Archived Threads', 'comment_save' => 'Uložiť komentár', 'comment_new' => 'Nový komentár', 'comment_created' => 'komentované :createDiff', @@ -377,8 +410,14 @@ 'comment_deleted_success' => 'Komentár odstránený', 'comment_created_success' => 'Komentár pridaný', 'comment_updated_success' => 'Komentár aktualizovaný', + 'comment_archive_success' => 'Comment archived', + 'comment_unarchive_success' => 'Comment un-archived', + 'comment_view' => 'View comment', + 'comment_jump_to_thread' => 'Jump to thread', 'comment_delete_confirm' => 'Ste si istý, že chcete odstrániť tento komentár?', 'comment_in_reply_to' => 'Odpovedať na :commentId', + 'comment_reference' => 'Reference', + 'comment_reference_outdated' => '(Outdated)', 'comment_editor_explain' => 'Here are the comments that have been left on this page. Comments can be added & managed when viewing the saved page.', // Revision diff --git a/lang/sk/errors.php b/lang/sk/errors.php index 06f396f1788..143259f3ad4 100644 --- a/lang/sk/errors.php +++ b/lang/sk/errors.php @@ -10,6 +10,7 @@ // Auth 'error_user_exists_different_creds' => 'Používateľ s emailom :email už existuje, ale s inými údajmi.', + 'auth_pre_register_theme_prevention' => 'User account could not be registered for the provided details', 'email_already_confirmed' => 'Email bol už overený, skúste sa prihlásiť.', 'email_confirmation_invalid' => 'Tento potvrdzujúci token nie je platný alebo už bol použitý, skúste sa prosím registrovať znova.', 'email_confirmation_expired' => 'Potvrdzujúci token expiroval, bol odoslaný nový potvrdzujúci email.', @@ -23,7 +24,6 @@ 'saml_invalid_response_id' => 'Požiadavka z externého autentifikačného systému nie je rozpoznaná procesom spusteným touto aplikáciou. Tento problém môže spôsobiť navigácia späť po prihlásení.', 'saml_fail_authed' => 'Prihlásenie pomocou :system zlyhalo, systém neposkytol úspešnú autorizáciu', 'oidc_already_logged_in' => 'Používateľ sa už prihlásil', - 'oidc_user_not_registered' => 'Používateľ :name nie je zaregistrovaný a automatická registrácia je zakázaná', 'oidc_no_email_address' => 'V údajoch poskytnutých externým overovacím systémom sa nepodarilo nájsť e-mailovú adresu tohto používateľa', 'oidc_fail_authed' => 'Prihlásenie pomocou :system zlyhalo, systém neposkytol úspešnú autorizáciu', 'social_no_action_defined' => 'Nebola definovaná žiadna akcia', @@ -37,6 +37,7 @@ 'social_driver_not_found' => 'Ovládač socialnych sietí nebol nájdený', 'social_driver_not_configured' => 'Nastavenia Vášho :socialAccount účtu nie sú správne.', 'invite_token_expired' => 'Platnosť tohto odkazu na pozvánku vypršala. Namiesto toho sa môžete pokúsiť obnoviť heslo účtu.', + 'login_user_not_found' => 'A user for this action could not be found.', // System 'path_not_writable' => 'Do cesty :filePath sa nedá nahrávať. Uistite sa, že je zapisovateľná serverom.', @@ -77,6 +78,7 @@ // Users 'users_cannot_delete_only_admin' => 'Nemôžete zmazať posledného správcu', 'users_cannot_delete_guest' => 'Nemôžete zmazať hosťa', + 'users_could_not_send_invite' => 'Could not create user since invite email failed to send', // Roles 'role_cannot_be_edited' => 'Táto rola nemôže byť upravovaná', @@ -103,6 +105,18 @@ 'app_down' => ':appName je momentálne nedostupná', 'back_soon' => 'Čoskoro bude opäť dostupná.', + // Import + 'import_zip_cant_read' => 'Could not read ZIP file.', + 'import_zip_cant_decode_data' => 'Could not find and decode ZIP data.json content.', + 'import_zip_no_data' => 'ZIP file data has no expected book, chapter or page content.', + 'import_validation_failed' => 'Import ZIP failed to validate with errors:', + 'import_zip_failed_notification' => 'Failed to import ZIP file.', + 'import_perms_books' => 'You are lacking the required permissions to create books.', + 'import_perms_chapters' => 'You are lacking the required permissions to create chapters.', + 'import_perms_pages' => 'You are lacking the required permissions to create pages.', + 'import_perms_images' => 'You are lacking the required permissions to create images.', + 'import_perms_attachments' => 'You are lacking the required permission to create attachments.', + // API errors 'api_no_authorization_found' => 'V žiadosti sa nenašiel žiadny autorizačný token', 'api_bad_authorization_format' => 'V žiadosti sa našiel autorizačný token, ale formát sa zdal nesprávny', diff --git a/lang/sk/preferences.php b/lang/sk/preferences.php index b3152848f3c..85da4f331f7 100644 --- a/lang/sk/preferences.php +++ b/lang/sk/preferences.php @@ -27,7 +27,7 @@ 'notifications_save' => 'Save Preferences', 'notifications_update_success' => 'Notification preferences have been updated!', 'notifications_watched' => 'Watched & Ignored Items', - 'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.', + 'notifications_watched_desc' => 'Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.', 'auth' => 'Access & Security', 'auth_change_password' => 'Change Password', diff --git a/lang/sk/settings.php b/lang/sk/settings.php index 0192066defa..2731fd01a2b 100644 --- a/lang/sk/settings.php +++ b/lang/sk/settings.php @@ -74,6 +74,36 @@ 'reg_confirm_restrict_domain_desc' => 'Zadajte zoznam domén, pre ktoré chcete povoliť registráciu oddelených čiarkou. Používatelia dostanú email kvôli overeniu adresy predtým ako im bude dovolené používať aplikáciu.
Používatelia si budú môcť po úspešnej registrácii zmeniť svoju emailovú adresu.', 'reg_confirm_restrict_domain_placeholder' => 'Nie sú nastavené žiadne obmedzenia', + // Sorting Settings + 'sorting' => 'Sorting', + 'sorting_book_default' => 'Default Book Sort', + 'sorting_book_default_desc' => 'Select the default sort rule to apply to new books. This won\'t affect existing books, and can be overridden per-book.', + 'sorting_rules' => 'Sort Rules', + 'sorting_rules_desc' => 'These are predefined sorting operations which can be applied to content in the system.', + 'sort_rule_assigned_to_x_books' => 'Assigned to :count Book|Assigned to :count Books', + 'sort_rule_create' => 'Create Sort Rule', + 'sort_rule_edit' => 'Edit Sort Rule', + 'sort_rule_delete' => 'Delete Sort Rule', + 'sort_rule_delete_desc' => 'Remove this sort rule from the system. Books using this sort will revert to manual sorting.', + 'sort_rule_delete_warn_books' => 'This sort rule is currently used on :count book(s). Are you sure you want to delete this?', + 'sort_rule_delete_warn_default' => 'This sort rule is currently used as the default for books. Are you sure you want to delete this?', + 'sort_rule_details' => 'Sort Rule Details', + 'sort_rule_details_desc' => 'Set a name for this sort rule, which will appear in lists when users are selecting a sort.', + 'sort_rule_operations' => 'Sort Operations', + 'sort_rule_operations_desc' => 'Configure the sort actions to be performed by moving them from the list of available operations. Upon use, the operations will be applied in order, from top to bottom. Any changes made here will be applied to all assigned books upon save.', + 'sort_rule_available_operations' => 'Available Operations', + 'sort_rule_available_operations_empty' => 'No operations remaining', + 'sort_rule_configured_operations' => 'Configured Operations', + 'sort_rule_configured_operations_empty' => 'Drag/add operations from the "Available Operations" list', + 'sort_rule_op_asc' => '(Asc)', + 'sort_rule_op_desc' => '(Desc)', + 'sort_rule_op_name' => 'Name - Alphabetical', + 'sort_rule_op_name_numeric' => '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_last' => 'Chapters Last', + // Maintenance settings 'maint' => 'Údržba', 'maint_image_cleanup' => 'Prečistenie obrázkov', @@ -109,7 +139,7 @@ 'recycle_bin_contents_empty' => 'Kôš je aktuálne prázdny', 'recycle_bin_empty' => 'Vyprázdniť Kôš', 'recycle_bin_empty_confirm' => 'Tým sa natrvalo odstránia všetky položky v koši vrátane obsahu obsiahnutého v každej položke. Naozaj chcete vyprázdniť kôš?', - 'recycle_bin_destroy_confirm' => 'Táto akcia natrvalo odstráni túto položku spolu so všetkými podriadenými prvkami uvedenými nižšie zo systému a tento obsah nebudete môcť obnoviť. Naozaj chcete natrvalo odstrániť túto položku?', + 'recycle_bin_destroy_confirm' => 'This action will permanently delete this item from the system, along with any child elements listed below, and you will not be able to restore this content. Are you sure you want to permanently delete this item?', 'recycle_bin_destroy_list' => 'Položky, ktoré budú odstránené', 'recycle_bin_restore_list' => 'Položky, ktoré budú obnovené', 'recycle_bin_restore_confirm' => 'Táto akcia obnoví odstránenú položku vrátane všetkých podradených prvkov na ich pôvodné miesto. Ak bolo pôvodné umiestnenie medzitým odstránené a teraz je v koši, bude potrebné obnoviť aj nadradenú položku.', @@ -162,6 +192,7 @@ 'role_access_api' => 'API prístupového systému', 'role_manage_settings' => 'Spravovať nastavenia aplikácie', 'role_export_content' => 'Exportovať obsah', + 'role_import_content' => 'Import content', 'role_editor_change' => 'Zmeniť editor stránky', 'role_notifications' => 'Receive & manage notifications', 'role_asset' => 'Oprávnenia majetku', @@ -276,6 +307,14 @@ 'webhooks_last_errored' => 'Posledná chyba:', 'webhooks_last_error_message' => 'Posledná chybová správa:', + // Licensing + 'licenses' => 'Licenses', + 'licenses_desc' => 'This page details license information for BookStack in addition to the projects & libraries that are used within BookStack. Many projects listed may only be used in a development context.', + 'licenses_bookstack' => 'BookStack License', + 'licenses_php' => 'PHP Library Licenses', + 'licenses_js' => 'JavaScript Library Licenses', + 'licenses_other' => 'Other Licenses', + 'license_details' => 'License Details', //! If editing translations files directly please ignore this in all //! languages apart from en. Content will be auto-copied from en. @@ -287,6 +326,7 @@ 'bs' => 'Bosanski', 'ca' => 'Català', 'cs' => 'Česky', + 'cy' => 'Cymraeg', 'da' => 'Dansk', 'de' => 'Deutsch (Sie)', 'de_informal' => 'Deutsch (Du)', @@ -308,6 +348,7 @@ 'lt' => 'Lietuvių Kalba', 'lv' => 'Latviešu Valoda', 'nb' => 'Norsk (Bokmål)', + 'ne' => 'नेपाली', 'nn' => 'Nynorsk', 'nl' => 'Nederlands', 'pl' => 'Polski', diff --git a/lang/sk/validation.php b/lang/sk/validation.php index dfc821b7c9c..415f7cf05b9 100644 --- a/lang/sk/validation.php +++ b/lang/sk/validation.php @@ -105,6 +105,11 @@ 'url' => ':attribute formát je neplatný.', 'uploaded' => 'Súbor sa nepodarilo nahrať. Server nemusí akceptovať súbory tejto veľkosti.', + 'zip_file' => 'The :attribute needs to reference a file within the ZIP.', + 'zip_file_mime' => 'The :attribute needs to reference a file of type :validTypes, found :foundType.', + 'zip_model_expected' => 'Data object expected but ":type" found.', + 'zip_unique' => 'The :attribute must be unique for the object type within the ZIP.', + // Custom validation lines 'custom' => [ 'password-confirm' => [ diff --git a/lang/sl/activities.php b/lang/sl/activities.php index 2db01288820..117c73ca186 100644 --- a/lang/sl/activities.php +++ b/lang/sl/activities.php @@ -7,117 +7,133 @@ // Pages 'page_create' => 'ustvarjena stran', - 'page_create_notification' => 'Page successfully created', + 'page_create_notification' => 'Stran uspešno ustvarjena', 'page_update' => 'posodobljena stran', - 'page_update_notification' => 'Page successfully updated', + 'page_update_notification' => 'Stran uspešno posodobljena', 'page_delete' => 'izbrisana stran', - 'page_delete_notification' => 'Page successfully deleted', + 'page_delete_notification' => 'Stran uspešno izbrisana', 'page_restore' => 'obnovljena stran', - 'page_restore_notification' => 'Page successfully restored', + 'page_restore_notification' => 'Stran uspešno obnovljena', 'page_move' => 'premaknjena stran', - 'page_move_notification' => 'Page successfully moved', + 'page_move_notification' => 'Stran uspešno premaknjena', // Chapters 'chapter_create' => 'ustvarjeno poglavje', - 'chapter_create_notification' => 'Chapter successfully created', + 'chapter_create_notification' => 'Poglavje uspešno ustvarjeno', 'chapter_update' => 'posodobljeno poglavje', - 'chapter_update_notification' => 'Chapter successfully updated', + 'chapter_update_notification' => 'Poglavje uspešno posodobljeno', 'chapter_delete' => 'izbrisano poglavje', - 'chapter_delete_notification' => 'Chapter successfully deleted', + 'chapter_delete_notification' => 'Poglavje uspešno izbrisano', 'chapter_move' => 'premaknjeno poglavje', - 'chapter_move_notification' => 'Chapter successfully moved', + 'chapter_move_notification' => 'Poglavje uspešno premaknjeno', // Books 'book_create' => 'knjiga ustvarjena', - 'book_create_notification' => 'Book successfully created', - 'book_create_from_chapter' => 'converted chapter to book', - 'book_create_from_chapter_notification' => 'Chapter successfully converted to a book', + 'book_create_notification' => 'Knjiga uspešno ustvarjena', + 'book_create_from_chapter' => 'poglavje pretvorjeno v knjigo', + 'book_create_from_chapter_notification' => 'Poglavje uspešno pretvorjeno v knjigo', 'book_update' => 'knjiga posodobljena', - 'book_update_notification' => 'Book successfully updated', + 'book_update_notification' => 'Knjiga uspešno posodobljena', 'book_delete' => 'izbrisana knjiga', - 'book_delete_notification' => 'Book successfully deleted', + 'book_delete_notification' => 'Knjiga uspešno izbrisana', 'book_sort' => 'razvrščena knjiga', - 'book_sort_notification' => 'Book successfully re-sorted', + 'book_sort_notification' => 'Knjiga uspešno razvrščena', // Bookshelves - 'bookshelf_create' => 'created shelf', - 'bookshelf_create_notification' => 'Shelf successfully created', - 'bookshelf_create_from_book' => 'converted book to shelf', - 'bookshelf_create_from_book_notification' => 'Book successfully converted to a shelf', - 'bookshelf_update' => 'updated shelf', - 'bookshelf_update_notification' => 'Shelf successfully updated', - 'bookshelf_delete' => 'deleted shelf', - 'bookshelf_delete_notification' => 'Shelf successfully deleted', + 'bookshelf_create' => 'knjižna polica ustvarjena', + 'bookshelf_create_notification' => 'Knjižna polica uspešno ustvarjena', + 'bookshelf_create_from_book' => 'knjiga pretvorjena v knjižno polico', + 'bookshelf_create_from_book_notification' => 'Knjiga uspešno pretvorjena v knjižno polico', + 'bookshelf_update' => 'knjižna polica posodobljena', + 'bookshelf_update_notification' => 'Knjižna polica uspešno posodobljena', + 'bookshelf_delete' => 'knjižna polica izbrisana', + 'bookshelf_delete_notification' => 'Knjižna polica uspešno izbrisana', // Revisions - 'revision_restore' => 'restored revision', - 'revision_delete' => 'deleted revision', - 'revision_delete_notification' => 'Revision successfully deleted', + 'revision_restore' => 'obnovljena različica', + 'revision_delete' => 'izbrisana različica', + 'revision_delete_notification' => 'Različica uspešno izbrisana', // Favourites - 'favourite_add_notification' => '":name" has been added to your favourites', - 'favourite_remove_notification' => '":name" has been removed from your favourites', + 'favourite_add_notification' => '":name" dodan med priljubljene', + 'favourite_remove_notification' => '":name" odstranjen iz priljubljenih', // Watching - 'watch_update_level_notification' => 'Watch preferences successfully updated', + 'watch_update_level_notification' => 'Nastavitve spremljanja uspešno posodobljene', // Auth - 'auth_login' => 'logged in', - 'auth_register' => 'registered as new user', - 'auth_password_reset_request' => 'requested user password reset', - 'auth_password_reset_update' => 'reset user password', - 'mfa_setup_method' => 'configured MFA method', - 'mfa_setup_method_notification' => 'Multi-factor method successfully configured', - 'mfa_remove_method' => 'removed MFA method', - 'mfa_remove_method_notification' => 'Multi-factor method successfully removed', + 'auth_login' => 'prijavljen', + 'auth_register' => 'registriran kot nov uporabnik', + 'auth_password_reset_request' => 'zahteva ponastavitev uporabniškega gesla', + 'auth_password_reset_update' => 'ponastavitev uporabniškega gesla', + 'mfa_setup_method' => 'nastavljena metoda MFA', + 'mfa_setup_method_notification' => 'Večfaktorska avtentikacija (MFA) uspešno nastavljena', + 'mfa_remove_method' => 'odstranjena metoda MFA', + 'mfa_remove_method_notification' => 'Večfaktorska avtentikacija (MFA) uspešno odstranjena', // Settings - 'settings_update' => 'updated settings', - 'settings_update_notification' => 'Settings successfully updated', - 'maintenance_action_run' => 'ran maintenance action', + 'settings_update' => 'posodobitev nastavitev', + 'settings_update_notification' => 'Nastavitve uspešno posodobljene', + 'maintenance_action_run' => 'zagnano vzdrževanje', // Webhooks - 'webhook_create' => 'created webhook', - 'webhook_create_notification' => 'Webhook successfully created', - 'webhook_update' => 'updated webhook', - 'webhook_update_notification' => 'Webhook successfully updated', - 'webhook_delete' => 'deleted webhook', - 'webhook_delete_notification' => 'Webhook successfully deleted', + 'webhook_create' => 'ustvarjen webhook', + 'webhook_create_notification' => 'Webhook uspešno ustvarjen', + 'webhook_update' => 'posodobljen webhook', + 'webhook_update_notification' => 'Webhook uspešno posodobljen', + 'webhook_delete' => 'izbrisan webhook', + 'webhook_delete_notification' => 'Webhook uspešno izbrisan', + + // Imports + 'import_create' => 'created import', + 'import_create_notification' => 'Import successfully uploaded', + 'import_run' => 'updated import', + 'import_run_notification' => 'Content successfully imported', + 'import_delete' => 'deleted import', + 'import_delete_notification' => 'Import successfully deleted', // Users - 'user_create' => 'created user', - 'user_create_notification' => 'User successfully created', - 'user_update' => 'updated user', - 'user_update_notification' => 'User successfully updated', - 'user_delete' => 'deleted user', - 'user_delete_notification' => 'User successfully removed', + 'user_create' => 'ustvarjen uporabnik', + 'user_create_notification' => 'Uporabnik uspešno ustvarjen', + 'user_update' => 'posodobljen uporabnik', + 'user_update_notification' => 'Uporabnik uspešno posodobljen', + 'user_delete' => 'uporabnik izbrisan', + 'user_delete_notification' => 'Uporabnik uspešno izbrisan', // API Tokens - 'api_token_create' => 'created api token', - 'api_token_create_notification' => 'API token successfully created', - 'api_token_update' => 'updated api token', - 'api_token_update_notification' => 'API token successfully updated', - 'api_token_delete' => 'deleted api token', - 'api_token_delete_notification' => 'API token successfully deleted', + 'api_token_create' => 'ustvarjen žeton API', + 'api_token_create_notification' => 'Žeton API uspešno ustvarjen', + 'api_token_update' => 'posodobljen žeton API', + 'api_token_update_notification' => 'Žeton API uspešno posodobljen', + 'api_token_delete' => 'izbrisan žeton API', + 'api_token_delete_notification' => 'Žeton API uspešno izbrisan', // Roles - 'role_create' => 'created role', - 'role_create_notification' => 'Role successfully created', - 'role_update' => 'updated role', - 'role_update_notification' => 'Role successfully updated', - 'role_delete' => 'deleted role', - 'role_delete_notification' => 'Role successfully deleted', + 'role_create' => 'ustvarjena vloga', + 'role_create_notification' => 'Vloga uspešno ustvarjena', + 'role_update' => 'posodobljena vloga', + 'role_update_notification' => 'Vloga uspešno posodobljena', + 'role_delete' => 'izbrisana vloga', + 'role_delete_notification' => 'Vloga uspešno izbrisana', // Recycle Bin - 'recycle_bin_empty' => 'emptied recycle bin', - 'recycle_bin_restore' => 'restored from recycle bin', - 'recycle_bin_destroy' => 'removed from recycle bin', + 'recycle_bin_empty' => 'izpraznjen koš', + 'recycle_bin_restore' => 'obnovljeno iz koša', + 'recycle_bin_destroy' => 'odstranjeno iz koša', // Comments 'commented_on' => 'komentar na', - 'comment_create' => 'added comment', - 'comment_update' => 'updated comment', - 'comment_delete' => 'deleted comment', + 'comment_create' => 'dodan komentar', + 'comment_update' => 'posodobljen komentar', + 'comment_delete' => 'izbrisan komentar', + + // Sort Rules + 'sort_rule_create' => 'created sort rule', + 'sort_rule_create_notification' => 'Sort rule successfully created', + 'sort_rule_update' => 'updated sort rule', + 'sort_rule_update_notification' => 'Sort rule successfully updated', + 'sort_rule_delete' => 'deleted sort rule', + 'sort_rule_delete_notification' => 'Sort rule successfully deleted', // Other 'permissions_update' => 'pravice so posodobljene', diff --git a/lang/sl/auth.php b/lang/sl/auth.php index ed7b5d4a050..e06d823e7cd 100644 --- a/lang/sl/auth.php +++ b/lang/sl/auth.php @@ -27,10 +27,10 @@ 'ldap_email_hint' => 'Prosimo vpišite e-poštni naslov za ta račun.', 'create_account' => 'Ustvari račun', 'already_have_account' => 'Že imate račun?', - 'dont_have_account' => 'Nimate računa?', + 'dont_have_account' => 'Nimaš računa?', 'social_login' => 'Prijava z računi družbenih omrežij', 'social_registration' => 'Registracija z družbenim omrežjem', - 'social_registration_text' => 'Registrirajte in prijavite se za uporabo drugih možnosti.', + 'social_registration_text' => 'Registriraj in prijavi se z uporabo drugih možnosti.', 'register_thanks' => 'Hvala za registracijo!', 'register_confirm' => 'Prosimo preverite vaš e-poštni predal in kliknite na potrditveni gumb za dostop :appName.', @@ -40,7 +40,7 @@ // Login auto-initiation 'auto_init_starting' => 'Attempting Login', - 'auto_init_starting_desc' => 'We\'re contacting your authentication system to start the login process. If there\'s no progress after 5 seconds you can try clicking the link below.', + 'auto_init_starting_desc' => 'Kontaktiram tvoj sistem za preverjanje pristnosti za pričetek procesa prijave. V primeru, da se nič ne zgodi v naslednjih 5 sekundah, klikni spodnjo povezavo.', 'auto_init_start_link' => 'Proceed with authentication', // Password Reset @@ -91,7 +91,7 @@ 'mfa_option_totp_title' => 'Mobile App', 'mfa_option_totp_desc' => 'To use multi-factor authentication you\'ll need a mobile application that supports TOTP such as Google Authenticator, Authy or Microsoft Authenticator.', 'mfa_option_backup_codes_title' => 'Backup Codes', - 'mfa_option_backup_codes_desc' => 'Securely store a set of one-time-use backup codes which you can enter to verify your identity.', + 'mfa_option_backup_codes_desc' => 'Generates a set of one-time-use backup codes which you\'ll enter on login to verify your identity. Make sure to store these in a safe & secure place.', 'mfa_gen_confirm_and_enable' => 'Confirm and Enable', 'mfa_gen_backup_codes_title' => 'Backup Codes Setup', 'mfa_gen_backup_codes_desc' => 'Store the below list of codes in a safe place. When accessing the system you\'ll be able to use one of the codes as a second authentication mechanism.', diff --git a/lang/sl/common.php b/lang/sl/common.php index 62d8eb033d5..0309237a973 100644 --- a/lang/sl/common.php +++ b/lang/sl/common.php @@ -20,7 +20,7 @@ 'description' => 'Opis', 'role' => 'Vloga', 'cover_image' => 'Naslovna slika', - 'cover_image_description' => 'Slika naj bo velika približno 440x250px.', + 'cover_image_description' => 'This image should be approximately 440x250px although it will be flexibly scaled & cropped to fit the user interface in different scenarios as required, so actual dimensions for display will differ.', // Actions 'actions' => 'Dejanja', @@ -30,6 +30,8 @@ 'create' => 'Ustvari', 'update' => 'Posodobi', 'edit' => 'Uredi', + 'archive' => 'Archive', + 'unarchive' => 'Un-Archive', 'sort' => 'Razvrsti', 'move' => 'Premakni', 'copy' => 'Kopiraj', @@ -44,12 +46,12 @@ 'configure' => 'Configure', 'manage' => 'Manage', 'fullscreen' => 'Celozaslonski način', - 'favourite' => 'Favourite', - 'unfavourite' => 'Unfavourite', - 'next' => 'Next', - 'previous' => 'Previous', + 'favourite' => 'Priljubljeno', + 'unfavourite' => 'Ni priljubljeno', + 'next' => 'Naprej', + 'previous' => 'Nazaj', 'filter_active' => 'Active Filter:', - 'filter_clear' => 'Clear Filter', + 'filter_clear' => 'Počisti filter', 'download' => 'Download', 'open_in_tab' => 'Open in Tab', 'open' => 'Open', @@ -91,7 +93,7 @@ 'edit_profile' => 'Uredi profil', 'dark_mode' => 'Način temnega zaslona', 'light_mode' => 'Način svetlega zaslona', - 'global_search' => 'Global Search', + 'global_search' => 'Globalno iskanje', // Layout tabs 'tab_info' => 'Informacije', @@ -107,4 +109,7 @@ // Not directly used but available for convenience to users. 'privacy_policy' => 'Pravilnik o zasebnosti', 'terms_of_service' => 'Pogoji uporabe', + + // OpenSearch + 'opensearch_description' => 'Search :appName', ]; diff --git a/lang/sl/editor.php b/lang/sl/editor.php index 670c1c5e1ac..3afcc23124e 100644 --- a/lang/sl/editor.php +++ b/lang/sl/editor.php @@ -7,102 +7,107 @@ */ return [ // General editor terms - 'general' => 'General', - 'advanced' => 'Advanced', - 'none' => 'None', - 'cancel' => 'Cancel', - 'save' => 'Save', - 'close' => 'Close', - 'undo' => 'Undo', - 'redo' => 'Redo', - 'left' => 'Left', - 'center' => 'Center', - 'right' => 'Right', - 'top' => 'Top', - 'middle' => 'Middle', - 'bottom' => 'Bottom', - 'width' => 'Width', - 'height' => 'Height', - 'More' => 'More', - 'select' => 'Select...', + 'general' => 'Splošno', + 'advanced' => 'Napredno', + 'none' => 'Brez', + 'cancel' => 'Prekliči', + 'save' => 'Shrani', + 'close' => 'Zapri', + 'apply' => 'Apply', + 'undo' => 'Razveljavi', + 'redo' => 'Ponovi', + 'left' => 'Levo', + 'center' => 'Sredinsko', + 'right' => 'Desno', + 'top' => 'Zgoraj', + 'middle' => 'Sredina', + 'bottom' => 'Spodaj', + 'width' => 'Širina', + 'height' => 'Višina', + 'More' => 'Več', + 'select' => 'Izberi ...', // Toolbar - 'formats' => 'Formats', - 'header_large' => 'Large Header', - 'header_medium' => 'Medium Header', - 'header_small' => 'Small Header', - 'header_tiny' => 'Tiny Header', - 'paragraph' => 'Paragraph', - 'blockquote' => 'Blockquote', - 'inline_code' => 'Inline code', - 'callouts' => 'Callouts', - 'callout_information' => 'Information', - 'callout_success' => 'Success', - 'callout_warning' => 'Warning', - 'callout_danger' => 'Danger', - 'bold' => 'Bold', - 'italic' => 'Italic', - 'underline' => 'Underline', - 'strikethrough' => 'Strikethrough', - 'superscript' => 'Superscript', - 'subscript' => 'Subscript', - 'text_color' => 'Text color', - 'custom_color' => 'Custom color', - 'remove_color' => 'Remove color', - 'background_color' => 'Background color', - 'align_left' => 'Align left', - 'align_center' => 'Align center', - 'align_right' => 'Align right', - 'align_justify' => 'Justify', - 'list_bullet' => 'Bullet list', - 'list_numbered' => 'Numbered list', - 'list_task' => 'Task list', - 'indent_increase' => 'Increase indent', - 'indent_decrease' => 'Decrease indent', - 'table' => 'Table', - 'insert_image' => 'Insert image', - 'insert_image_title' => 'Insert/Edit Image', - 'insert_link' => 'Insert/edit link', - 'insert_link_title' => 'Insert/Edit Link', - 'insert_horizontal_line' => 'Insert horizontal line', + 'formats' => 'Oblike', + 'header_large' => 'Velika glava', + 'header_medium' => 'Srednja glava', + 'header_small' => 'Majhna glava', + 'header_tiny' => 'Drobna glava', + 'paragraph' => 'Odstavek', + 'blockquote' => 'Navedek', + 'inline_code' => 'Vgrajena koda', + 'callouts' => 'Opombe', + 'callout_information' => 'Informacija', + 'callout_success' => 'Uspešno', + 'callout_warning' => 'Opozorilo', + 'callout_danger' => 'Nevarnost', + 'bold' => 'Krepko', + 'italic' => 'Ležeče', + 'underline' => 'Podčrtano', + 'strikethrough' => 'Prečrtano', + 'superscript' => 'Nadpisano', + 'subscript' => 'Podpisano', + 'text_color' => 'Barva besedila', + 'highlight_color' => 'Highlight color', + 'custom_color' => 'Barva po meri', + 'remove_color' => 'Odstrani barvo', + 'background_color' => 'Barva ozadja', + 'align_left' => 'Poravnaj levo', + 'align_center' => 'Poravnaj na sredino', + 'align_right' => 'Poravnaj desno', + 'align_justify' => 'Poravnaj obojestransko', + 'list_bullet' => 'Seznam z oznakami', + 'list_numbered' => 'Oštevilčen seznam', + 'list_task' => 'Seznam opravil', + 'indent_increase' => 'Povečaj zamik', + 'indent_decrease' => 'Zmanjšaj zamik', + 'table' => 'Tabela', + 'insert_image' => 'Vstavi sliko', + 'insert_image_title' => 'Vstavi/Obdelaj sliko', + 'insert_link' => 'Vstavi/Obdelaj povezavo', + 'insert_link_title' => 'Vstavi/obdelaj povezavo', + 'insert_horizontal_line' => 'Vstavi vodoravno črto', 'insert_code_block' => 'Insert code block', 'edit_code_block' => 'Edit code block', - 'insert_drawing' => 'Insert/edit drawing', + 'insert_drawing' => 'Vstavi/Obdelaj risbo', 'drawing_manager' => 'Drawing manager', 'insert_media' => 'Insert/edit media', 'insert_media_title' => 'Insert/Edit Media', - 'clear_formatting' => 'Clear formatting', - 'source_code' => 'Source code', - 'source_code_title' => 'Source Code', - 'fullscreen' => 'Fullscreen', - 'image_options' => 'Image options', + 'clear_formatting' => 'Odstrani oblikovanje', + 'source_code' => 'Izvorna koda', + 'source_code_title' => 'Izvorna koda', + 'fullscreen' => 'Celozaslonski način', + 'image_options' => 'Možnosti za slike', // Tables - 'table_properties' => 'Table properties', - 'table_properties_title' => 'Table Properties', - 'delete_table' => 'Delete table', - 'insert_row_before' => 'Insert row before', - 'insert_row_after' => 'Insert row after', - 'delete_row' => 'Delete row', - 'insert_column_before' => 'Insert column before', - 'insert_column_after' => 'Insert column after', - 'delete_column' => 'Delete column', - 'table_cell' => 'Cell', - 'table_row' => 'Row', - 'table_column' => 'Column', - 'cell_properties' => 'Cell properties', - 'cell_properties_title' => 'Cell Properties', - 'cell_type' => 'Cell type', - 'cell_type_cell' => 'Cell', - 'cell_scope' => 'Scope', - 'cell_type_header' => 'Header cell', - 'merge_cells' => 'Merge cells', - 'split_cell' => 'Split cell', - 'table_row_group' => 'Row Group', - 'table_column_group' => 'Column Group', - 'horizontal_align' => 'Horizontal align', - 'vertical_align' => 'Vertical align', - 'border_width' => 'Border width', + 'table_properties' => 'Lastnosti tabele', + 'table_properties_title' => 'Lastnosti tabele', + 'delete_table' => 'Izbriši tabelo', + 'table_clear_formatting' => 'Odstrani oblikovanje tabele', + 'resize_to_contents' => 'Prilagodi vsebini', + 'row_header' => 'Glava vrstice', + 'insert_row_before' => 'Vstavi vrstico pred', + 'insert_row_after' => 'Vstavi vrstico po', + 'delete_row' => 'Izbriši vrstico', + 'insert_column_before' => 'Vstavi stolpec pred', + 'insert_column_after' => 'Vstavi stolpec po', + 'delete_column' => 'Izbriši stolpec', + 'table_cell' => 'Celica', + 'table_row' => 'Vrstica', + 'table_column' => 'Stolpec', + 'cell_properties' => 'Lastnosti celice', + 'cell_properties_title' => 'Lastnosti celice', + 'cell_type' => 'Vrsta celice', + 'cell_type_cell' => 'Celica', + 'cell_scope' => 'Obseg', + 'cell_type_header' => 'Naslovna celica', + 'merge_cells' => 'Združi celice', + 'split_cell' => 'Razdeli celice', + 'table_row_group' => 'Skupina vrstic', + 'table_column_group' => 'Skupina stolpcev', + 'horizontal_align' => 'Vodoravna poravnava', + 'vertical_align' => 'Navpična poravnava', + 'border_width' => 'Debelina roba', 'border_style' => 'Border style', 'border_color' => 'Border color', 'row_properties' => 'Row properties', @@ -144,31 +149,34 @@ 'url' => 'URL', 'text_to_display' => 'Text to display', 'title' => 'Title', + 'browse_links' => 'Browse links', 'open_link' => 'Open link', 'open_link_in' => 'Open link in...', 'open_link_current' => 'Current window', 'open_link_new' => 'New window', 'remove_link' => 'Remove link', 'insert_collapsible' => 'Insert collapsible block', - 'collapsible_unwrap' => 'Unwrap', - 'edit_label' => 'Edit label', - 'toggle_open_closed' => 'Toggle open/closed', - 'collapsible_edit' => 'Edit collapsible block', - 'toggle_label' => 'Toggle label', + 'collapsible_unwrap' => 'Odpri', + 'edit_label' => 'Uredi oznako', + 'toggle_open_closed' => 'Odpri/Zapri', + 'collapsible_edit' => 'Uredi zložljivi blok', + 'toggle_label' => 'Preklopi oznako', // About view - 'about' => 'About the editor', - 'about_title' => 'About the WYSIWYG Editor', - 'editor_license' => 'Editor License & Copyright', - 'editor_tiny_license' => 'This editor is built using :tinyLink which is provided under the MIT license.', - 'editor_tiny_license_link' => 'The copyright and license details of TinyMCE can be found here.', - 'save_continue' => 'Save Page & Continue', - 'callouts_cycle' => '(Keep pressing to toggle through types)', - 'link_selector' => 'Link to content', - 'shortcuts' => 'Shortcuts', - 'shortcut' => 'Shortcut', - 'shortcuts_intro' => 'The following shortcuts are available in the editor:', + 'about' => 'O urejevalniku', + 'about_title' => 'O Urejevalniku WYSIWYG', + 'editor_license' => 'Licenca in avtorske pravice Urejevalnika', + 'editor_lexical_license' => 'This editor is built as a fork of :lexicalLink which is distributed under the MIT license.', + 'editor_lexical_license_link' => 'Full license details can be found here.', + 'editor_tiny_license' => 'Urejevalnik je ustvarjen z uporabo :tinyLink pod pogoji licence MIT.', + 'editor_tiny_license_link' => 'Podrobnosti o avtorskih pravicah in licenci za TinyMCE lahko preberete tukaj.', + 'save_continue' => 'Shrani stran in Nadaljuj', + 'callouts_cycle' => '(Večkrat pritisnite, da preklopite med vrstami opomb)', + 'link_selector' => 'Povezava do vsebine', + 'shortcuts' => 'Bližnjice', + 'shortcut' => 'Bližnjica', + 'shortcuts_intro' => 'Sledeče bližnjice so na voljo v urejevalniku:', 'windows_linux' => '(Windows/Linux)', 'mac' => '(Mac)', - 'description' => 'Description', + 'description' => 'Opis', ]; diff --git a/lang/sl/entities.php b/lang/sl/entities.php index 591dd009e9b..00ac991811e 100644 --- a/lang/sl/entities.php +++ b/lang/sl/entities.php @@ -29,8 +29,8 @@ 'images' => 'Slike', 'my_recent_drafts' => 'Moji nedavni osnutki', 'my_recently_viewed' => 'Nedavno prikazano', - 'my_most_viewed_favourites' => 'My Most Viewed Favourites', - 'my_favourites' => 'My Favourites', + 'my_most_viewed_favourites' => 'Največkrat gledane priljubljene strani', + 'my_favourites' => 'Priljubljene', 'no_pages_viewed' => 'Niste si ogledali še nobene strani', 'no_pages_recently_created' => 'Nedavno ni bila ustvarjena nobena stran', 'no_pages_recently_updated' => 'Nedavno ni bila posodobljena nobena stran', @@ -39,10 +39,38 @@ 'export_pdf' => 'PDF datoteka (.pdf)', 'export_text' => 'Navadna besedilna datoteka', 'export_md' => 'Markdown File', + 'export_zip' => 'Portable ZIP', + 'default_template' => 'Default Page Template', + 'default_template_explain' => 'Assign a page template that will be used as the default content for all pages created within this item. Keep in mind this will only be used if the page creator has view access to the chosen template page.', + 'default_template_select' => 'Select a template page', + 'import' => 'Import', + 'import_validate' => 'Validate Import', + 'import_desc' => 'Import books, chapters & pages using a portable zip export from the same, or a different, instance. Select a ZIP file to proceed. After the file has been uploaded and validated you\'ll be able to configure & confirm the import in the next view.', + '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_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_size' => ':size Import ZIP Size', + 'import_uploaded_at' => 'Uploaded :relativeTime', + 'import_uploaded_by' => 'Uploaded by', + 'import_location' => 'Import Location', + '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_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', + 'breadcrumb_siblings_for_book' => 'Navigate siblings for book', + 'breadcrumb_siblings_for_bookshelf' => 'Navigate siblings for shelf', // Permissions and restrictions 'permissions' => 'Dovoljenja', - 'permissions_desc' => 'Set permissions here to override the default permissions provided by user roles.', + 'permissions_desc' => 'Nastavi dovoljenja bolj podrobno, kot to določajo uporabniške vloge.', 'permissions_book_cascade' => 'Permissions set on books will automatically cascade to child chapters and pages, unless they have their own permissions defined.', 'permissions_chapter_cascade' => 'Permissions set on chapters will automatically cascade to child pages, unless they have their own permissions defined.', 'permissions_save' => 'Shrani dovoljenja', @@ -54,7 +82,7 @@ // Search 'search_results' => 'Rezultati iskanja', - 'search_total_results_found' => ':count najdenih rezultatov|:count skupaj najdenih rezultatov', + 'search_total_results_found' => ':count najdenih rezultatov|:count skupno najdenih rezultatov', 'search_clear' => 'Počisti iskanje', 'search_no_pages' => 'Nobena stran se ne ujema z vašim iskanjem', 'search_for_term' => 'Išči :term', @@ -77,7 +105,7 @@ 'search_created_before' => 'Ustvarjeno pred', 'search_created_after' => 'Ustvarjeno po', 'search_set_date' => 'Nastavi datum', - 'search_update' => 'Posodobi iskanje', + 'search_update' => 'Išči ponovno', // Shelves 'shelf' => 'Polica', @@ -117,7 +145,7 @@ 'books' => 'Knjige', 'x_books' => ':count Knjiga|:count Knjig', 'books_empty' => 'Ustvarjena ni bila nobena knjiga', - 'books_popular' => 'Priljubjene knjige', + 'books_popular' => 'Priljubljene knjige', 'books_recent' => 'Zadnje knjige', 'books_new' => 'Nove knjige', 'books_new_action' => 'Nova knjiga', @@ -132,9 +160,6 @@ 'books_edit_named' => 'Uredi knjigo :bookName', 'books_form_book_name' => 'Ime knjige', 'books_save' => 'Shrani knjigo', - 'books_default_template' => 'Default Page Template', - 'books_default_template_explain' => 'Assign a page template that will be used as the default content for all new pages in this book. Keep in mind this will only be used if the page creator has view access to those chosen template page.', - 'books_default_template_select' => 'Select a template page', 'books_permissions' => 'Dovoljenja knjige', 'books_permissions_updated' => 'Posodobljena dovoljenja knjige', 'books_empty_contents' => 'V tej knjigi ni bila ustvarjena še nobena stran ali poglavje.', @@ -145,7 +170,9 @@ 'books_search_this' => 'Išči v tej knjigi', 'books_navigation' => 'Navigacija po knjigi', 'books_sort' => 'Razvrsti vsebino knjige', - 'books_sort_desc' => 'Move chapters and pages within a book to reorganise its contents. Other books can be added which allows easy moving of chapters and pages between books.', + 'books_sort_desc' => 'Move chapters and pages within a book to reorganise its contents. Other books can be added which allows easy moving of chapters and pages between books. Optionally an auto sort rule can be set to automatically sort this book\'s contents upon changes.', + 'books_sort_auto_sort' => 'Auto Sort Option', + 'books_sort_auto_sort_active' => 'Auto Sort Active: :sortName', 'books_sort_named' => 'Razvrsti knjigo :bookName', 'books_sort_name' => 'Razvrsti po imenu', 'books_sort_created' => 'Razvrsti po datumu nastanka', @@ -197,7 +224,7 @@ 'page' => 'Stran', 'pages' => 'Strani', 'x_pages' => ':count Stran|:count Strani', - 'pages_popular' => 'Priljubjene strani', + 'pages_popular' => 'Priljubljene knjige', 'pages_new' => 'Nova stran', 'pages_attachments' => 'Priponke', 'pages_navigation' => 'Navigacija po strani', @@ -207,7 +234,7 @@ 'pages_delete_draft' => 'Izbriši osnutek strani', 'pages_delete_success' => 'Stran izbirsana', 'pages_delete_draft_success' => 'Osnutek strani izbrisan', - 'pages_delete_warning_template' => 'This page is in active use as a book default page template. These books will no longer have a default page template assigned after this page is deleted.', + 'pages_delete_warning_template' => 'This page is in active use as a book or chapter default page template. These books or chapters will no longer have a default page template assigned after this page is deleted.', 'pages_delete_confirm' => 'Ste prepričani, da želite izbrisati to stran?', 'pages_delete_draft_confirm' => 'Ali ste prepričani, da želite izbrisati ta osnutek?', 'pages_editing_named' => 'Urejanje strani :pageName', @@ -224,6 +251,8 @@ 'pages_edit_switch_to_markdown_clean' => '(Clean Content)', 'pages_edit_switch_to_markdown_stable' => '(Stable Content)', 'pages_edit_switch_to_wysiwyg' => 'Switch to WYSIWYG Editor', + 'pages_edit_switch_to_new_wysiwyg' => 'Switch to new WYSIWYG', + 'pages_edit_switch_to_new_wysiwyg_desc' => '(In Beta Testing)', 'pages_edit_set_changelog' => 'Opiši spremembe na dokumentu', 'pages_edit_enter_changelog_desc' => 'Vnesite kratek opis sprememb, ki ste jih naredili', 'pages_edit_enter_changelog' => 'Vpišite vsebino sprememb', @@ -243,6 +272,7 @@ 'pages_md_insert_drawing' => 'Vstavi risbo', 'pages_md_show_preview' => 'Show preview', 'pages_md_sync_scroll' => 'Sync preview scroll', + 'pages_md_plain_editor' => 'Plaintext editor', 'pages_drawing_unsaved' => 'Unsaved Drawing Found', 'pages_drawing_unsaved_confirm' => 'Unsaved drawing data was found from a previous failed drawing save attempt. Would you like to restore and continue editing this unsaved drawing?', 'pages_not_in_chapter' => 'Stran ni v poglavju', @@ -367,8 +397,11 @@ 'comment' => 'Komentar', 'comments' => 'Komentarji', 'comment_add' => 'Dodaj komentar', + 'comment_none' => 'No comments to display', 'comment_placeholder' => 'Dodaj komentar', - 'comment_count' => '{0} Ni komentarjev|{1} 1 Komentar|[2,*] :count Komentarji', + 'comment_thread_count' => ':count Comment Thread|:count Comment Threads', + 'comment_archived_count' => ':count Archived', + 'comment_archived_threads' => 'Archived Threads', 'comment_save' => 'Shrani komentar', 'comment_new' => 'Nov kometar', 'comment_created' => 'komentirano :createDiff', @@ -377,8 +410,14 @@ 'comment_deleted_success' => 'Komentar je izbrisan', 'comment_created_success' => 'Komentar dodan', 'comment_updated_success' => 'Komentar posodobljen', + 'comment_archive_success' => 'Comment archived', + 'comment_unarchive_success' => 'Comment un-archived', + 'comment_view' => 'View comment', + 'comment_jump_to_thread' => 'Jump to thread', 'comment_delete_confirm' => 'Ste prepričani, da želite izbrisati ta komentar?', 'comment_in_reply_to' => 'Odgovor na :commentId', + 'comment_reference' => 'Reference', + 'comment_reference_outdated' => '(Outdated)', 'comment_editor_explain' => 'Here are the comments that have been left on this page. Comments can be added & managed when viewing the saved page.', // Revision @@ -417,7 +456,7 @@ 'watch_desc_default' => 'Revert watching to just your default notification preferences.', 'watch_title_ignore' => 'Ignore', 'watch_desc_ignore' => 'Ignore all notifications, including those from user-level preferences.', - 'watch_title_new' => 'New Pages', + 'watch_title_new' => 'Nove strani', 'watch_desc_new' => 'Notify when any new page is created within this item.', 'watch_title_updates' => 'All Page Updates', 'watch_desc_updates' => 'Notify upon all new pages and page changes.', diff --git a/lang/sl/errors.php b/lang/sl/errors.php index 51c34132b8d..da80fd3910e 100644 --- a/lang/sl/errors.php +++ b/lang/sl/errors.php @@ -10,6 +10,7 @@ // Auth 'error_user_exists_different_creds' => 'Uporabnik z e-pošto :email že obstaja, vendar z drugačnimi poverilnicami.', + 'auth_pre_register_theme_prevention' => 'User account could not be registered for the provided details', 'email_already_confirmed' => 'E-naslov je že bil potrjen, poskusite se prijaviti.', 'email_confirmation_invalid' => 'Ta potrditveni žeton ni veljaven ali je že bil uporabljen. Poizkusite znova.', 'email_confirmation_expired' => 'Potrditveni žeton je potekel. Nova potrditvena e-pošta je bila poslana.', @@ -23,7 +24,6 @@ 'saml_invalid_response_id' => 'Zahteva iz zunanjega sistema za preverjanje pristnosti ni prepoznana s strani procesa zagnanega s strani te aplikacije. Pomik nazaj po prijavi je lahko vzrok teh težav.', 'saml_fail_authed' => 'Prijava z uporabo :system ni uspela, sistem ni zagotovil uspešne avtorizacije', 'oidc_already_logged_in' => 'Already logged in', - 'oidc_user_not_registered' => 'The user :name is not registered and automatic registration is disabled', 'oidc_no_email_address' => 'Could not find an email address, for this user, in the data provided by the external authentication system', 'oidc_fail_authed' => 'Login using :system failed, system did not provide successful authorization', 'social_no_action_defined' => 'Akcija ni določena', @@ -37,6 +37,7 @@ 'social_driver_not_found' => 'Socialni vtičnik ni najden', 'social_driver_not_configured' => 'Vaše nastavitve :socialAccount niso pravilo nastavljene.', 'invite_token_expired' => 'Ta povezava je potekla. Namesto tega lahko ponastavite vaše geslo računa.', + 'login_user_not_found' => 'A user for this action could not be found.', // System 'path_not_writable' => 'Poti :filePath ni bilo mogoče naložiti. Prepričajte se, da je zapisljiva na strežnik.', @@ -77,6 +78,7 @@ // Users 'users_cannot_delete_only_admin' => 'Ne morete odstraniti edinega administratorja', 'users_cannot_delete_guest' => 'Ne morete odstraniti uporabnika gost', + 'users_could_not_send_invite' => 'Could not create user since invite email failed to send', // Roles 'role_cannot_be_edited' => 'Te vloge mi možno urejati', @@ -94,7 +96,7 @@ // Error pages '404_page_not_found' => 'Strani ni mogoče najti', 'sorry_page_not_found' => 'Oprostite, strani ki jo iščete, ni mogoče najti.', - 'sorry_page_not_found_permission_warning' => 'Če pričakujete, da ta stran obstaja, mogoče nimate pravic ogleda zanjo.', + 'sorry_page_not_found_permission_warning' => 'Stran ne obstaja ali pa za ogled nimaš ustreznih pravic.', 'image_not_found' => 'Image Not Found', 'image_not_found_subtitle' => 'Sorry, The image file you were looking for could not be found.', 'image_not_found_details' => 'If you expected this image to exist it might have been deleted.', @@ -103,6 +105,18 @@ 'app_down' => ':appName trenutno ni dosegljiva', 'back_soon' => 'Kmalu bo ponovno dosegljiva.', + // Import + 'import_zip_cant_read' => 'Could not read ZIP file.', + 'import_zip_cant_decode_data' => 'Could not find and decode ZIP data.json content.', + 'import_zip_no_data' => 'ZIP file data has no expected book, chapter or page content.', + 'import_validation_failed' => 'Import ZIP failed to validate with errors:', + 'import_zip_failed_notification' => 'Failed to import ZIP file.', + 'import_perms_books' => 'You are lacking the required permissions to create books.', + 'import_perms_chapters' => 'You are lacking the required permissions to create chapters.', + 'import_perms_pages' => 'You are lacking the required permissions to create pages.', + 'import_perms_images' => 'You are lacking the required permissions to create images.', + 'import_perms_attachments' => 'You are lacking the required permission to create attachments.', + // API errors 'api_no_authorization_found' => 'Avtorizacija ni bila najdena', 'api_bad_authorization_format' => 'Avtorizacija je bila najdena, vendar je v napačni obliki', diff --git a/lang/sl/pagination.php b/lang/sl/pagination.php index e04a98d89b5..80a1e1b4743 100644 --- a/lang/sl/pagination.php +++ b/lang/sl/pagination.php @@ -6,7 +6,7 @@ */ return [ - 'previous' => '« Predhodno', - 'next' => 'Naslednje »', + 'previous' => '« Nazaj', + 'next' => 'Naprej »', ]; diff --git a/lang/sl/preferences.php b/lang/sl/preferences.php index 2b88f9671ff..2872f5f3c65 100644 --- a/lang/sl/preferences.php +++ b/lang/sl/preferences.php @@ -27,7 +27,7 @@ 'notifications_save' => 'Save Preferences', 'notifications_update_success' => 'Notification preferences have been updated!', 'notifications_watched' => 'Watched & Ignored Items', - 'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.', + 'notifications_watched_desc' => 'Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.', 'auth' => 'Access & Security', 'auth_change_password' => 'Change Password', diff --git a/lang/sl/settings.php b/lang/sl/settings.php index 9b543040d87..904978f4705 100644 --- a/lang/sl/settings.php +++ b/lang/sl/settings.php @@ -64,9 +64,9 @@ 'reg_settings' => 'registracija', 'reg_enable' => 'onemogočena registracija', 'reg_enable_toggle' => 'omogočena registracija', - 'reg_enable_desc' => 'Ko je registracija omogočena, se bo uporabnik lahko prijavil sam kot uporabnik aplikacije. Po registraciji je uporabniku dodeljena ena prevzeta vloga.', + 'reg_enable_desc' => 'Ko je registracija omogočena, se bo uporabnik lahko prijavil sam kot uporabnik aplikacije. Po registraciji je uporabniku dodeljena privzeta vloga.', 'reg_default_role' => 'prevzeta uporabniška vloga po registraciji', - 'reg_enable_external_warning' => 'Ta možnosti je ignorirana ko zunanja LDAP ali SAML avtentikacija je akitivna. Uporabniški računi za ne obstoječe uporabnike bodo avtomatsko izdelani, če avtentikacija zunanjih uporabljenih sistemov je uspešna.', + 'reg_enable_external_warning' => 'Ta možnosti je prezrta, ko je aktivno zunanja LDAP ali SAML preverjanje pristnosti. Uporabniški računi za neobstoječe uporabnike bodo avtomatično ustvarjeni po uspešnem zunanjem preverjanju pristnosti.', 'reg_email_confirmation' => 'potrditev e-pošte', 'reg_email_confirmation_toggle' => 'potrebna potrditev e-pošte', 'reg_confirm_email_desc' => 'Če uporabite omejitev domene, bo potrebna potrditev e-pošte in ta možnost bo prezrta.', @@ -74,6 +74,36 @@ 'reg_confirm_restrict_domain_desc' => 'Vnesite seznam domen, ločenih z vejico, na katere želite omejiti registracijo. Uporabnik bo prejel e-pošto za potrditev naslova, preden bo omogočena interakcija z aplikacijo.
Upoštevajte, da uporabnik po uspešni registrciji lahko spremeni svoj e-poštni naslov.', 'reg_confirm_restrict_domain_placeholder' => 'Brez omejitev', + // Sorting Settings + 'sorting' => 'Sorting', + 'sorting_book_default' => 'Default Book Sort', + 'sorting_book_default_desc' => 'Select the default sort rule to apply to new books. This won\'t affect existing books, and can be overridden per-book.', + 'sorting_rules' => 'Sort Rules', + 'sorting_rules_desc' => 'These are predefined sorting operations which can be applied to content in the system.', + 'sort_rule_assigned_to_x_books' => 'Assigned to :count Book|Assigned to :count Books', + 'sort_rule_create' => 'Create Sort Rule', + 'sort_rule_edit' => 'Edit Sort Rule', + 'sort_rule_delete' => 'Delete Sort Rule', + 'sort_rule_delete_desc' => 'Remove this sort rule from the system. Books using this sort will revert to manual sorting.', + 'sort_rule_delete_warn_books' => 'This sort rule is currently used on :count book(s). Are you sure you want to delete this?', + 'sort_rule_delete_warn_default' => 'This sort rule is currently used as the default for books. Are you sure you want to delete this?', + 'sort_rule_details' => 'Sort Rule Details', + 'sort_rule_details_desc' => 'Set a name for this sort rule, which will appear in lists when users are selecting a sort.', + 'sort_rule_operations' => 'Sort Operations', + 'sort_rule_operations_desc' => 'Configure the sort actions to be performed by moving them from the list of available operations. Upon use, the operations will be applied in order, from top to bottom. Any changes made here will be applied to all assigned books upon save.', + 'sort_rule_available_operations' => 'Available Operations', + 'sort_rule_available_operations_empty' => 'No operations remaining', + 'sort_rule_configured_operations' => 'Configured Operations', + 'sort_rule_configured_operations_empty' => 'Drag/add operations from the "Available Operations" list', + 'sort_rule_op_asc' => '(Asc)', + 'sort_rule_op_desc' => '(Desc)', + 'sort_rule_op_name' => 'Name - Alphabetical', + 'sort_rule_op_name_numeric' => '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_last' => 'Chapters Last', + // Maintenance settings 'maint' => 'Vzdrževanje', 'maint_image_cleanup' => 'Odstrani /počisti slike', @@ -109,7 +139,7 @@ 'recycle_bin_contents_empty' => 'Koš je prazen', 'recycle_bin_empty' => 'Izprazni koš', 'recycle_bin_empty_confirm' => 'S tem boste trajno uničili vse predmete v košu, vključno z vsebino vsakega predmeta. Ali ste prepričani, da želite izprazniti koš?', - 'recycle_bin_destroy_confirm' => 'S tem dejanjem boste ta element skupaj s spodaj navedenimi podrejenimi elementi trajno izbrisali iz sistema in te vsebine ne boste mogli obnoviti. Ali ste prepričani, da želite trajno izbrisati ta element?', + 'recycle_bin_destroy_confirm' => 'This action will permanently delete this item from the system, along with any child elements listed below, and you will not be able to restore this content. Are you sure you want to permanently delete this item?', 'recycle_bin_destroy_list' => 'Predmeti, ki naj bodo trajno izbrisani', 'recycle_bin_restore_list' => 'Predmeti, ki naj bodo obnovljeni', 'recycle_bin_restore_confirm' => 'S tem dejanjem boste izbrisani element, vključno z vsemi podrejenimi elementi, obnovili na prvotno mesto. Če je bilo prvotno mesto od takrat izbrisano in je zdaj v košu, bo treba obnoviti tudi nadrejeni element.', @@ -137,7 +167,7 @@ 'roles' => 'Vloge', 'role_user_roles' => 'Vloge uporabnika', 'roles_index_desc' => 'Roles are used to group users & provide system permission to their members. When a user is a member of multiple roles the privileges granted will stack and the user will inherit all abilities.', - 'roles_x_users_assigned' => ':count user assigned|:count users assigned', + 'roles_x_users_assigned' => ':count uporabnik v tej vlogi|:count uporabnikov v tej vlogi', 'roles_x_permissions_provided' => ':count permission|:count permissions', 'roles_assigned_users' => 'Assigned Users', 'roles_permissions_provided' => 'Provided Permissions', @@ -162,6 +192,7 @@ 'role_access_api' => 'API za dostop do sistema', 'role_manage_settings' => 'Nastavitve za upravljanje', 'role_export_content' => 'Export content', + 'role_import_content' => 'Import content', 'role_editor_change' => 'Change page editor', 'role_notifications' => 'Receive & manage notifications', 'role_asset' => 'Sistemska dovoljenja', @@ -192,7 +223,7 @@ 'users_password_desc' => 'Set a password used to log-in to the application. This must be at least 8 characters long.', 'users_send_invite_text' => 'Uporabniku lahko pošljete e-poštno sporočilo s povabilom, ki mu omogoča, da nastavi svoje geslo, ali pa ga nastavite kar sami.', 'users_send_invite_option' => 'Pošlji uporabniku e-poštno povabilo', - 'users_external_auth_id' => 'Zunanje dokazilo ID', + 'users_external_auth_id' => 'Zunanji ID za preverjanje pristnosti (Authentication ID)', 'users_external_auth_id_desc' => 'When an external authentication system is in use (such as SAML2, OIDC or LDAP) this is the ID which links this BookStack user to the authentication system account. You can ignore this field if using the default email-based authentication.', 'users_password_warning' => 'Only fill the below if you would like to change the password for this user.', 'users_system_public' => 'Ta uporabnik predstavlja vse gostujoče uporabnike, ki obiščejo vašo wiki stran. Za prijavo je ni mogoče uporabiti, ampak je dodeljena samodejno.', @@ -277,6 +308,14 @@ 'webhooks_last_errored' => 'Last Errored:', 'webhooks_last_error_message' => 'Last Error Message:', + // Licensing + 'licenses' => 'Licenses', + 'licenses_desc' => 'This page details license information for BookStack in addition to the projects & libraries that are used within BookStack. Many projects listed may only be used in a development context.', + 'licenses_bookstack' => 'BookStack License', + 'licenses_php' => 'PHP Library Licenses', + 'licenses_js' => 'JavaScript Library Licenses', + 'licenses_other' => 'Other Licenses', + 'license_details' => 'License Details', //! If editing translations files directly please ignore this in all //! languages apart from en. Content will be auto-copied from en. @@ -288,6 +327,7 @@ 'bs' => 'Bosanski', 'ca' => 'Català', 'cs' => 'Česky', + 'cy' => 'Cymraeg', 'da' => 'danščina', 'de' => 'Deutsch (Sie)', 'de_informal' => 'Deutsch (Du)', @@ -309,6 +349,7 @@ 'lt' => 'Lietuvių Kalba', 'lv' => 'Latviešu Valoda', 'nb' => 'Norsk (Bokmål)', + 'ne' => 'नेपाली', 'nn' => 'Nynorsk', 'nl' => 'Nederlands', 'pl' => 'Polski', diff --git a/lang/sl/validation.php b/lang/sl/validation.php index 92060dd6d43..0d9b56c1019 100644 --- a/lang/sl/validation.php +++ b/lang/sl/validation.php @@ -12,10 +12,10 @@ 'active_url' => ':attribute ni veljaven URL.', 'after' => ':attribute mora biti datum po :date.', 'alpha' => ':attribute lahko vsebuje samo črke.', - 'alpha_dash' => ':attribute lahko vsebuje samo ?rke, ?tevilke in ?rtice.', + 'alpha_dash' => ':attribute lahko vsebuje samo črke, številke, pomišljaje in podčrtaje.', 'alpha_num' => ':attribute lahko vsebuje samo črke in številke.', 'array' => ':attribute mora biti niz.', - 'backup_codes' => 'The provided code is not valid or has already been used.', + 'backup_codes' => 'Podana koda ni veljavna ali je že uporabljena.', 'before' => ':attribute mora biti datum pred :date.', 'between' => [ 'numeric' => ':attribute mora biti med :min in :max.', @@ -32,7 +32,7 @@ 'digits_between' => ':attribute mora biti med :min in :max števkami.', 'email' => ':attribute mora biti veljaven e-naslov.', 'ends_with' => 'The :attribute se mora končati z eno od določenih: :vrednost/values', - 'file' => 'The :attribute must be provided as a valid file.', + 'file' => ':attribute ni veljavna datoteka.', 'filled' => 'Polje ne sme biti prazno.', 'gt' => [ 'numeric' => ':attribute mora biti večji kot :vrednost.', @@ -100,11 +100,16 @@ ], 'string' => ':attribute mora biti niz.', 'timezone' => ':attribute mora biti veljavna cona.', - 'totp' => 'The provided code is not valid or has expired.', + 'totp' => 'Podana koda ni veljavna ali je zapadla.', 'unique' => ':attribute je že zaseden.', 'url' => ':attribute oblika ni veljavna.', 'uploaded' => 'Datoteke ni bilo mogoče naložiti. Strežnik morda ne sprejema datotek te velikosti.', + 'zip_file' => 'The :attribute needs to reference a file within the ZIP.', + 'zip_file_mime' => 'The :attribute needs to reference a file of type :validTypes, found :foundType.', + 'zip_model_expected' => 'Data object expected but ":type" found.', + 'zip_unique' => 'The :attribute must be unique for the object type within the ZIP.', + // Custom validation lines 'custom' => [ 'password-confirm' => [ diff --git a/lang/sq/activities.php b/lang/sq/activities.php index 1c7a10310c1..9296d1759ca 100644 --- a/lang/sq/activities.php +++ b/lang/sq/activities.php @@ -84,6 +84,14 @@ 'webhook_delete' => 'fshiu uebhook', 'webhook_delete_notification' => 'Uebhook-u u fshi me sukses', + // Imports + 'import_create' => 'importi i krijuar', + 'import_create_notification' => 'Importi u ngarkua me sukses', + 'import_run' => 'Importi i përditësuar', + 'import_run_notification' => 'Përmbajtja u importua me sukses', + 'import_delete' => 'Importi i fshirë', + 'import_delete_notification' => 'Importi u fshi me sukses', + // Users 'user_create' => 'krijoi përdorues', 'user_create_notification' => 'Përdoruesi u krijua me sukses', @@ -93,11 +101,11 @@ 'user_delete_notification' => 'Përdoruesi u fshi me sukses', // API Tokens - 'api_token_create' => 'krijoi token api', + 'api_token_create' => 'Krijoi token API', 'api_token_create_notification' => 'Token API u krijua me sukses', - 'api_token_update' => 'përditësoi token api', + 'api_token_update' => 'Token i përditësuar i API-t', 'api_token_update_notification' => 'Token API u përditësua me sukses', - 'api_token_delete' => 'fshiu token api', + 'api_token_delete' => 'Fshiu tokenin API', 'api_token_delete_notification' => 'Token API u fshi me sukses', // Roles @@ -119,6 +127,14 @@ 'comment_update' => 'përditësoi koment', 'comment_delete' => 'fshiu koment', + // Sort Rules + 'sort_rule_create' => 'Rregull i krijuar renditjeje', + 'sort_rule_create_notification' => 'Rregulli i renditjes u krijua me sukses', + 'sort_rule_update' => 'rregulli i renditjes i përditësuar', + 'sort_rule_update_notification' => 'Rregulli i renditjes u përditësua me sukses', + 'sort_rule_delete' => 'rregulli i renditjes është fshirë', + 'sort_rule_delete_notification' => 'Rregulli i renditjes u fshi me sukses', + // Other 'permissions_update' => 'përditësoi lejet', ]; diff --git a/lang/sq/auth.php b/lang/sq/auth.php index 08af155e88f..6fdc4ee4a6d 100644 --- a/lang/sq/auth.php +++ b/lang/sq/auth.php @@ -28,8 +28,8 @@ 'create_account' => 'Krijo një llogari', 'already_have_account' => 'Keni një llogari?', 'dont_have_account' => 'Nuk keni akoma llogari?', - 'social_login' => 'Social Login', - 'social_registration' => 'Social Registration', + 'social_login' => 'Kyçu me rrjete sociale', + 'social_registration' => 'Regjistrohu me rrjete sociale', 'social_registration_text' => 'Regjistrohu dhe logohu duhet përdorur një shërbim tjetër.', 'register_thanks' => 'Faleminderit që u regjistruat!', @@ -39,45 +39,45 @@ 'register_success' => 'Faleminderit që u regjistruar! Ju tani jeni të regjistruar dhe të loguar.', // Login auto-initiation - 'auto_init_starting' => 'Attempting Login', - 'auto_init_starting_desc' => 'We\'re contacting your authentication system to start the login process. If there\'s no progress after 5 seconds you can try clicking the link below.', - 'auto_init_start_link' => 'Proceed with authentication', + 'auto_init_starting' => 'Përpjekje për t\'u kyçur', + 'auto_init_starting_desc' => 'Jemi duke kontaktuar sistemin e verifikimit për të filluar proçesin e kyçjes. Nëse nuk ka progres për 5 sekonda, klikoni linkun më poshtë.', + 'auto_init_start_link' => 'Vazhdoni me verifikimin', // Password Reset - 'reset_password' => 'Reset Password', - 'reset_password_send_instructions' => 'Enter your email below and you will be sent an email with a password reset link.', - 'reset_password_send_button' => 'Send Reset Link', - 'reset_password_sent' => 'A password reset link will be sent to :email if that email address is found in the system.', - 'reset_password_success' => 'Your password has been successfully reset.', - 'email_reset_subject' => 'Reset your :appName password', - 'email_reset_text' => 'You are receiving this email because we received a password reset request for your account.', - 'email_reset_not_requested' => 'If you did not request a password reset, no further action is required.', + 'reset_password' => 'Rivendosni fjalëkalimin', + 'reset_password_send_instructions' => 'Shkruani email-in tuaj më poshtë dhe do të merrni një link në email për të rikthyer fjalëkalimin.', + 'reset_password_send_button' => 'Dërgo linkun e rikthimit të fjalëkalimit', + 'reset_password_sent' => 'Një link për rikthimin e fjalëkalimit do ju dërgohet në :email nëse adresa e email-it ndodhet në sistem.', + 'reset_password_success' => 'Fjalëkalimi juaj u rikthye me sukses.', + 'email_reset_subject' => 'Rikthe fjalëkalimin për :appName', + 'email_reset_text' => 'Ju po e merrni këtë email sepse ne morëm një kërkesë për rivendosjen e fjalëkalimit për llogarinë tuaj.', + 'email_reset_not_requested' => 'Nëse nuk keni kërkuar rivendosjen e fjalëkalimit, nuk kërkohet asnjë veprim i mëtejshëm.', // Email Confirmation - 'email_confirm_subject' => 'Confirm your email on :appName', - 'email_confirm_greeting' => 'Thanks for joining :appName!', - 'email_confirm_text' => 'Please confirm your email address by clicking the button below:', - 'email_confirm_action' => 'Confirm Email', - 'email_confirm_send_error' => 'Email confirmation required but the system could not send the email. Contact the admin to ensure email is set up correctly.', - 'email_confirm_success' => 'Your email has been confirmed! You should now be able to login using this email address.', - 'email_confirm_resent' => 'Confirmation email resent, Please check your inbox.', - 'email_confirm_thanks' => 'Thanks for confirming!', - 'email_confirm_thanks_desc' => 'Please wait a moment while your confirmation is handled. If you are not redirected after 3 seconds press the "Continue" link below to proceed.', + 'email_confirm_subject' => 'Konfirmo email-in tënd në :appName', + 'email_confirm_greeting' => 'Faleminderit që u bashkuat me :appName!', + 'email_confirm_text' => 'Ju lutemi konfirmoni adresën tuaj të email-it duke klikuar butonin më poshtë:', + 'email_confirm_action' => 'Konfirmo email-in', + 'email_confirm_send_error' => 'Kërkohet konfirmimi i email-it, por sistemi nuk mundi ta dërgonte email-in. Kontaktoni administratorin për t\'u siguruar që email-i është konfiguruar saktë.', + 'email_confirm_success' => 'Email-i juaj është konfirmuar! Tani duhet të jeni në gjendje të hyni në sistem duke përdorur këtë adresë email-i.', + 'email_confirm_resent' => 'Emaili i konfirmimit u ridërgua, ju lutem kontrolloni kutinë tuaj postare.', + 'email_confirm_thanks' => 'Faleminderit që konfirmuat!', + 'email_confirm_thanks_desc' => 'Ju lutemi prisni një moment ndërsa konfirmimi juaj përpunohet. Nëse nuk ridrejtoheni pas 3 sekondash, shtypni linkun "Vazhdo" më poshtë për të vazhduar.', - 'email_not_confirmed' => 'Email Address Not Confirmed', - 'email_not_confirmed_text' => 'Your email address has not yet been confirmed.', - 'email_not_confirmed_click_link' => 'Please click the link in the email that was sent shortly after you registered.', - 'email_not_confirmed_resend' => 'If you cannot find the email you can re-send the confirmation email by submitting the form below.', - 'email_not_confirmed_resend_button' => 'Resend Confirmation Email', + 'email_not_confirmed' => 'Adresa e email-it nuk është konfirmuar', + 'email_not_confirmed_text' => 'Adresa juaj e email-it nuk është konfirmuar ende.', + 'email_not_confirmed_click_link' => 'Ju lutemi klikoni linkun në emailin që ju është dërguar menjëherë pasi u regjistruat.', + 'email_not_confirmed_resend' => 'Nëse nuk mund ta gjeni email-in, mund ta ridërgoni email-in e konfirmimit duke plotësuar formularin më poshtë.', + 'email_not_confirmed_resend_button' => 'Ridërgo emailin e konfirmimit', // User Invite - 'user_invite_email_subject' => 'You have been invited to join :appName!', - 'user_invite_email_greeting' => 'An account has been created for you on :appName.', - 'user_invite_email_text' => 'Click the button below to set an account password and gain access:', - 'user_invite_email_action' => 'Set Account Password', - 'user_invite_page_welcome' => 'Welcome to :appName!', - 'user_invite_page_text' => 'To finalise your account and gain access you need to set a password which will be used to log-in to :appName on future visits.', - 'user_invite_page_confirm_button' => 'Confirm Password', + 'user_invite_email_subject' => 'Je ftuar të bashkohesh me :appName!', + 'user_invite_email_greeting' => 'Një llogari është krijuar për ty në :appName.', + 'user_invite_email_text' => 'Klikoni butonin më poshtë për të vendosur një fjalëkalim llogarie dhe për të fituar akses:', + 'user_invite_email_action' => 'Vendos fjalëkalimin e llogarisë', + 'user_invite_page_welcome' => 'Mirë se vini në :appName!', + 'user_invite_page_text' => 'Për të finalizuar llogarinë tuaj dhe për të fituar akses, duhet të vendosni një fjalëkalim i cili do të përdoret për t\'u kyçur në :appName në vizitat e ardhshme.', + 'user_invite_page_confirm_button' => 'Konfirmo fjalëkalimin', 'user_invite_success_login' => 'Password set, you should now be able to login using your set password to access :appName!', // Multi-factor Authentication @@ -91,7 +91,7 @@ 'mfa_option_totp_title' => 'Mobile App', 'mfa_option_totp_desc' => 'To use multi-factor authentication you\'ll need a mobile application that supports TOTP such as Google Authenticator, Authy or Microsoft Authenticator.', 'mfa_option_backup_codes_title' => 'Backup Codes', - 'mfa_option_backup_codes_desc' => 'Securely store a set of one-time-use backup codes which you can enter to verify your identity.', + 'mfa_option_backup_codes_desc' => 'Generates a set of one-time-use backup codes which you\'ll enter on login to verify your identity. Make sure to store these in a safe & secure place.', 'mfa_gen_confirm_and_enable' => 'Confirm and Enable', 'mfa_gen_backup_codes_title' => 'Backup Codes Setup', 'mfa_gen_backup_codes_desc' => 'Store the below list of codes in a safe place. When accessing the system you\'ll be able to use one of the codes as a second authentication mechanism.', diff --git a/lang/sq/common.php b/lang/sq/common.php index 27037babec0..06a9e855ce3 100644 --- a/lang/sq/common.php +++ b/lang/sq/common.php @@ -20,7 +20,7 @@ 'description' => 'Description', 'role' => 'Role', 'cover_image' => 'Cover image', - 'cover_image_description' => 'This image should be approx 440x250px.', + 'cover_image_description' => 'This image should be approximately 440x250px although it will be flexibly scaled & cropped to fit the user interface in different scenarios as required, so actual dimensions for display will differ.', // Actions 'actions' => 'Actions', @@ -30,6 +30,8 @@ 'create' => 'Create', 'update' => 'Update', 'edit' => 'Edit', + 'archive' => 'Archive', + 'unarchive' => 'Un-Archive', 'sort' => 'Sort', 'move' => 'Move', 'copy' => 'Copy', @@ -107,4 +109,7 @@ // Not directly used but available for convenience to users. 'privacy_policy' => 'Privacy Policy', 'terms_of_service' => 'Terms of Service', + + // OpenSearch + 'opensearch_description' => 'Search :appName', ]; diff --git a/lang/sq/editor.php b/lang/sq/editor.php index 670c1c5e1ac..0d250e9a7bd 100644 --- a/lang/sq/editor.php +++ b/lang/sq/editor.php @@ -13,6 +13,7 @@ 'cancel' => 'Cancel', 'save' => 'Save', 'close' => 'Close', + 'apply' => 'Apply', 'undo' => 'Undo', 'redo' => 'Redo', 'left' => 'Left', @@ -47,6 +48,7 @@ 'superscript' => 'Superscript', 'subscript' => 'Subscript', 'text_color' => 'Text color', + 'highlight_color' => 'Highlight color', 'custom_color' => 'Custom color', 'remove_color' => 'Remove color', 'background_color' => 'Background color', @@ -81,6 +83,9 @@ 'table_properties' => 'Table properties', 'table_properties_title' => 'Table Properties', 'delete_table' => 'Delete table', + 'table_clear_formatting' => 'Clear table formatting', + 'resize_to_contents' => 'Resize to contents', + 'row_header' => 'Row header', 'insert_row_before' => 'Insert row before', 'insert_row_after' => 'Insert row after', 'delete_row' => 'Delete row', @@ -144,6 +149,7 @@ 'url' => 'URL', 'text_to_display' => 'Text to display', 'title' => 'Title', + 'browse_links' => 'Browse links', 'open_link' => 'Open link', 'open_link_in' => 'Open link in...', 'open_link_current' => 'Current window', @@ -160,6 +166,8 @@ 'about' => 'About the editor', 'about_title' => 'About the WYSIWYG Editor', 'editor_license' => 'Editor License & Copyright', + 'editor_lexical_license' => 'This editor is built as a fork of :lexicalLink which is distributed under the MIT license.', + 'editor_lexical_license_link' => 'Full license details can be found here.', 'editor_tiny_license' => 'This editor is built using :tinyLink which is provided under the MIT license.', 'editor_tiny_license_link' => 'The copyright and license details of TinyMCE can be found here.', 'save_continue' => 'Save Page & Continue', diff --git a/lang/sq/entities.php b/lang/sq/entities.php index f1f915544d1..74c50be3b2f 100644 --- a/lang/sq/entities.php +++ b/lang/sq/entities.php @@ -39,6 +39,34 @@ 'export_pdf' => 'PDF File', 'export_text' => 'Plain Text File', 'export_md' => 'Markdown File', + 'export_zip' => 'Portable ZIP', + 'default_template' => 'Default Page Template', + 'default_template_explain' => 'Assign a page template that will be used as the default content for all pages created within this item. Keep in mind this will only be used if the page creator has view access to the chosen template page.', + 'default_template_select' => 'Select a template page', + 'import' => 'Import', + 'import_validate' => 'Validate Import', + 'import_desc' => 'Import books, chapters & pages using a portable zip export from the same, or a different, instance. Select a ZIP file to proceed. After the file has been uploaded and validated you\'ll be able to configure & confirm the import in the next view.', + '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_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_size' => ':size Import ZIP Size', + 'import_uploaded_at' => 'Uploaded :relativeTime', + 'import_uploaded_by' => 'Uploaded by', + 'import_location' => 'Import Location', + '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_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', + 'breadcrumb_siblings_for_book' => 'Navigate siblings for book', + 'breadcrumb_siblings_for_bookshelf' => 'Navigate siblings for shelf', // Permissions and restrictions 'permissions' => 'Permissions', @@ -132,9 +160,6 @@ 'books_edit_named' => 'Edit Book :bookName', 'books_form_book_name' => 'Book Name', 'books_save' => 'Save Book', - 'books_default_template' => 'Default Page Template', - 'books_default_template_explain' => 'Assign a page template that will be used as the default content for all new pages in this book. Keep in mind this will only be used if the page creator has view access to those chosen template page.', - 'books_default_template_select' => 'Select a template page', 'books_permissions' => 'Book Permissions', 'books_permissions_updated' => 'Book Permissions Updated', 'books_empty_contents' => 'No pages or chapters have been created for this book.', @@ -145,7 +170,9 @@ 'books_search_this' => 'Search this book', 'books_navigation' => 'Book Navigation', 'books_sort' => 'Sort Book Contents', - 'books_sort_desc' => 'Move chapters and pages within a book to reorganise its contents. Other books can be added which allows easy moving of chapters and pages between books.', + 'books_sort_desc' => 'Move chapters and pages within a book to reorganise its contents. Other books can be added which allows easy moving of chapters and pages between books. Optionally an auto sort rule can be set to automatically sort this book\'s contents upon changes.', + 'books_sort_auto_sort' => 'Auto Sort Option', + 'books_sort_auto_sort_active' => 'Auto Sort Active: :sortName', 'books_sort_named' => 'Sort Book :bookName', 'books_sort_name' => 'Sort by Name', 'books_sort_created' => 'Sort by Created Date', @@ -207,7 +234,7 @@ 'pages_delete_draft' => 'Delete Draft Page', 'pages_delete_success' => 'Page deleted', 'pages_delete_draft_success' => 'Draft page deleted', - 'pages_delete_warning_template' => 'This page is in active use as a book default page template. These books will no longer have a default page template assigned after this page is deleted.', + 'pages_delete_warning_template' => 'This page is in active use as a book or chapter default page template. These books or chapters will no longer have a default page template assigned after this page is deleted.', 'pages_delete_confirm' => 'Are you sure you want to delete this page?', 'pages_delete_draft_confirm' => 'Are you sure you want to delete this draft page?', 'pages_editing_named' => 'Editing Page :pageName', @@ -224,6 +251,8 @@ 'pages_edit_switch_to_markdown_clean' => '(Clean Content)', 'pages_edit_switch_to_markdown_stable' => '(Stable Content)', 'pages_edit_switch_to_wysiwyg' => 'Switch to WYSIWYG Editor', + 'pages_edit_switch_to_new_wysiwyg' => 'Switch to new WYSIWYG', + 'pages_edit_switch_to_new_wysiwyg_desc' => '(In Beta Testing)', 'pages_edit_set_changelog' => 'Set Changelog', 'pages_edit_enter_changelog_desc' => 'Enter a brief description of the changes you\'ve made', 'pages_edit_enter_changelog' => 'Enter Changelog', @@ -243,6 +272,7 @@ 'pages_md_insert_drawing' => 'Insert Drawing', 'pages_md_show_preview' => 'Show preview', 'pages_md_sync_scroll' => 'Sync preview scroll', + 'pages_md_plain_editor' => 'Plaintext editor', 'pages_drawing_unsaved' => 'Unsaved Drawing Found', 'pages_drawing_unsaved_confirm' => 'Unsaved drawing data was found from a previous failed drawing save attempt. Would you like to restore and continue editing this unsaved drawing?', 'pages_not_in_chapter' => 'Page is not in a chapter', @@ -367,8 +397,11 @@ 'comment' => 'Comment', 'comments' => 'Comments', 'comment_add' => 'Add Comment', + 'comment_none' => 'No comments to display', 'comment_placeholder' => 'Leave a comment here', - 'comment_count' => '{0} No Comments|{1} 1 Comment|[2,*] :count Comments', + 'comment_thread_count' => ':count Comment Thread|:count Comment Threads', + 'comment_archived_count' => ':count Archived', + 'comment_archived_threads' => 'Archived Threads', 'comment_save' => 'Save Comment', 'comment_new' => 'New Comment', 'comment_created' => 'commented :createDiff', @@ -377,8 +410,14 @@ 'comment_deleted_success' => 'Comment deleted', 'comment_created_success' => 'Comment added', 'comment_updated_success' => 'Comment updated', + 'comment_archive_success' => 'Comment archived', + 'comment_unarchive_success' => 'Comment un-archived', + 'comment_view' => 'View comment', + 'comment_jump_to_thread' => 'Jump to thread', 'comment_delete_confirm' => 'Are you sure you want to delete this comment?', 'comment_in_reply_to' => 'In reply to :commentId', + 'comment_reference' => 'Reference', + 'comment_reference_outdated' => '(Outdated)', 'comment_editor_explain' => 'Here are the comments that have been left on this page. Comments can be added & managed when viewing the saved page.', // Revision diff --git a/lang/sq/errors.php b/lang/sq/errors.php index 607b6ea833a..9d738379648 100644 --- a/lang/sq/errors.php +++ b/lang/sq/errors.php @@ -10,6 +10,7 @@ // Auth 'error_user_exists_different_creds' => 'A user with the email :email already exists but with different credentials.', + 'auth_pre_register_theme_prevention' => 'User account could not be registered for the provided details', 'email_already_confirmed' => 'Email has already been confirmed, Try logging in.', 'email_confirmation_invalid' => 'This confirmation token is not valid or has already been used, Please try registering again.', 'email_confirmation_expired' => 'The confirmation token has expired, A new confirmation email has been sent.', @@ -23,7 +24,6 @@ 'saml_invalid_response_id' => 'The request from the external authentication system is not recognised by a process started by this application. Navigating back after a login could cause this issue.', 'saml_fail_authed' => 'Login using :system failed, system did not provide successful authorization', 'oidc_already_logged_in' => 'Already logged in', - 'oidc_user_not_registered' => 'The user :name is not registered and automatic registration is disabled', 'oidc_no_email_address' => 'Could not find an email address, for this user, in the data provided by the external authentication system', 'oidc_fail_authed' => 'Login using :system failed, system did not provide successful authorization', 'social_no_action_defined' => 'No action defined', @@ -37,6 +37,7 @@ 'social_driver_not_found' => 'Social driver not found', 'social_driver_not_configured' => 'Your :socialAccount social settings are not configured correctly.', 'invite_token_expired' => 'This invitation link has expired. You can instead try to reset your account password.', + 'login_user_not_found' => 'A user for this action could not be found.', // System 'path_not_writable' => 'File path :filePath could not be uploaded to. Ensure it is writable to the server.', @@ -77,6 +78,7 @@ // Users 'users_cannot_delete_only_admin' => 'You cannot delete the only admin', 'users_cannot_delete_guest' => 'You cannot delete the guest user', + 'users_could_not_send_invite' => 'Could not create user since invite email failed to send', // Roles 'role_cannot_be_edited' => 'This role cannot be edited', @@ -103,6 +105,18 @@ 'app_down' => ':appName is down right now', 'back_soon' => 'It will be back up soon.', + // Import + 'import_zip_cant_read' => 'Could not read ZIP file.', + 'import_zip_cant_decode_data' => 'Could not find and decode ZIP data.json content.', + 'import_zip_no_data' => 'ZIP file data has no expected book, chapter or page content.', + 'import_validation_failed' => 'Import ZIP failed to validate with errors:', + 'import_zip_failed_notification' => 'Failed to import ZIP file.', + 'import_perms_books' => 'You are lacking the required permissions to create books.', + 'import_perms_chapters' => 'You are lacking the required permissions to create chapters.', + 'import_perms_pages' => 'You are lacking the required permissions to create pages.', + 'import_perms_images' => 'You are lacking the required permissions to create images.', + 'import_perms_attachments' => 'You are lacking the required permission to create attachments.', + // API errors 'api_no_authorization_found' => 'No authorization token found on the request', 'api_bad_authorization_format' => 'An authorization token was found on the request but the format appeared incorrect', diff --git a/lang/sq/preferences.php b/lang/sq/preferences.php index 2b88f9671ff..2872f5f3c65 100644 --- a/lang/sq/preferences.php +++ b/lang/sq/preferences.php @@ -27,7 +27,7 @@ 'notifications_save' => 'Save Preferences', 'notifications_update_success' => 'Notification preferences have been updated!', 'notifications_watched' => 'Watched & Ignored Items', - 'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.', + 'notifications_watched_desc' => 'Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.', 'auth' => 'Access & Security', 'auth_change_password' => 'Change Password', diff --git a/lang/sq/settings.php b/lang/sq/settings.php index 03e9bf462e2..81c2c0a93c3 100644 --- a/lang/sq/settings.php +++ b/lang/sq/settings.php @@ -74,6 +74,36 @@ 'reg_confirm_restrict_domain_desc' => 'Enter a comma separated list of email domains you would like to restrict registration to. Users will be sent an email to confirm their address before being allowed to interact with the application.
Note that users will be able to change their email addresses after successful registration.', 'reg_confirm_restrict_domain_placeholder' => 'No restriction set', + // Sorting Settings + 'sorting' => 'Sorting', + 'sorting_book_default' => 'Default Book Sort', + 'sorting_book_default_desc' => 'Select the default sort rule to apply to new books. This won\'t affect existing books, and can be overridden per-book.', + 'sorting_rules' => 'Sort Rules', + 'sorting_rules_desc' => 'These are predefined sorting operations which can be applied to content in the system.', + 'sort_rule_assigned_to_x_books' => 'Assigned to :count Book|Assigned to :count Books', + 'sort_rule_create' => 'Create Sort Rule', + 'sort_rule_edit' => 'Edit Sort Rule', + 'sort_rule_delete' => 'Delete Sort Rule', + 'sort_rule_delete_desc' => 'Remove this sort rule from the system. Books using this sort will revert to manual sorting.', + 'sort_rule_delete_warn_books' => 'This sort rule is currently used on :count book(s). Are you sure you want to delete this?', + 'sort_rule_delete_warn_default' => 'This sort rule is currently used as the default for books. Are you sure you want to delete this?', + 'sort_rule_details' => 'Sort Rule Details', + 'sort_rule_details_desc' => 'Set a name for this sort rule, which will appear in lists when users are selecting a sort.', + 'sort_rule_operations' => 'Sort Operations', + 'sort_rule_operations_desc' => 'Configure the sort actions to be performed by moving them from the list of available operations. Upon use, the operations will be applied in order, from top to bottom. Any changes made here will be applied to all assigned books upon save.', + 'sort_rule_available_operations' => 'Available Operations', + 'sort_rule_available_operations_empty' => 'No operations remaining', + 'sort_rule_configured_operations' => 'Configured Operations', + 'sort_rule_configured_operations_empty' => 'Drag/add operations from the "Available Operations" list', + 'sort_rule_op_asc' => '(Asc)', + 'sort_rule_op_desc' => '(Desc)', + 'sort_rule_op_name' => 'Name - Alphabetical', + 'sort_rule_op_name_numeric' => '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_last' => 'Chapters Last', + // Maintenance settings 'maint' => 'Maintenance', 'maint_image_cleanup' => 'Cleanup Images', @@ -109,7 +139,7 @@ 'recycle_bin_contents_empty' => 'The recycle bin is currently empty', 'recycle_bin_empty' => 'Empty Recycle Bin', 'recycle_bin_empty_confirm' => 'This will permanently destroy all items in the recycle bin including content contained within each item. Are you sure you want to empty the recycle bin?', - 'recycle_bin_destroy_confirm' => 'This action will permanently delete this item, along with any child elements listed below, from the system and you will not be able to restore this content. Are you sure you want to permanently delete this item?', + 'recycle_bin_destroy_confirm' => 'This action will permanently delete this item from the system, along with any child elements listed below, and you will not be able to restore this content. Are you sure you want to permanently delete this item?', 'recycle_bin_destroy_list' => 'Items to be Destroyed', 'recycle_bin_restore_list' => 'Items to be Restored', 'recycle_bin_restore_confirm' => 'This action will restore the deleted item, including any child elements, to their original location. If the original location has since been deleted, and is now in the recycle bin, the parent item will also need to be restored.', @@ -162,6 +192,7 @@ 'role_access_api' => 'Access system API', 'role_manage_settings' => 'Manage app settings', 'role_export_content' => 'Export content', + 'role_import_content' => 'Import content', 'role_editor_change' => 'Change page editor', 'role_notifications' => 'Receive & manage notifications', 'role_asset' => 'Asset Permissions', @@ -276,6 +307,14 @@ 'webhooks_last_errored' => 'Last Errored:', 'webhooks_last_error_message' => 'Last Error Message:', + // Licensing + 'licenses' => 'Licenses', + 'licenses_desc' => 'This page details license information for BookStack in addition to the projects & libraries that are used within BookStack. Many projects listed may only be used in a development context.', + 'licenses_bookstack' => 'BookStack License', + 'licenses_php' => 'PHP Library Licenses', + 'licenses_js' => 'JavaScript Library Licenses', + 'licenses_other' => 'Other Licenses', + 'license_details' => 'License Details', //! If editing translations files directly please ignore this in all //! languages apart from en. Content will be auto-copied from en. @@ -287,6 +326,7 @@ 'bs' => 'Bosanski', 'ca' => 'Català', 'cs' => 'Česky', + 'cy' => 'Cymraeg', 'da' => 'Dansk', 'de' => 'Deutsch (Sie)', 'de_informal' => 'Deutsch (Du)', @@ -308,6 +348,7 @@ 'lt' => 'Lietuvių Kalba', 'lv' => 'Latviešu Valoda', 'nb' => 'Norsk (Bokmål)', + 'ne' => 'नेपाली', 'nn' => 'Nynorsk', 'nl' => 'Nederlands', 'pl' => 'Polski', diff --git a/lang/sq/validation.php b/lang/sq/validation.php index 2a676c7c4cc..d9b982d1e23 100644 --- a/lang/sq/validation.php +++ b/lang/sq/validation.php @@ -105,6 +105,11 @@ 'url' => 'The :attribute format is invalid.', 'uploaded' => 'The file could not be uploaded. The server may not accept files of this size.', + 'zip_file' => 'The :attribute needs to reference a file within the ZIP.', + 'zip_file_mime' => 'The :attribute needs to reference a file of type :validTypes, found :foundType.', + 'zip_model_expected' => 'Data object expected but ":type" found.', + 'zip_unique' => 'The :attribute must be unique for the object type within the ZIP.', + // Custom validation lines 'custom' => [ 'password-confirm' => [ diff --git a/lang/sr/activities.php b/lang/sr/activities.php new file mode 100644 index 00000000000..ad594a2204e --- /dev/null +++ b/lang/sr/activities.php @@ -0,0 +1,140 @@ + 'креирана страница', + 'page_create_notification' => 'Страница је успешно креирана', + 'page_update' => 'ажурирана страница', + 'page_update_notification' => 'Страница је успешно ажурирана', + 'page_delete' => 'обрисана страница', + 'page_delete_notification' => 'Страница је успешно обрисана', + 'page_restore' => 'обновљена страна', + 'page_restore_notification' => 'Страница је успешно обновљена', + 'page_move' => 'премештена страна', + 'page_move_notification' => 'Страница је успешно померена', + + // Chapters + 'chapter_create' => 'креирано поглавље', + 'chapter_create_notification' => 'Поглавље је успешно креирано', + 'chapter_update' => 'ажурирано поглавље', + 'chapter_update_notification' => 'Поглавље је успешно обновљено', + 'chapter_delete' => 'обрисано поглавље', + 'chapter_delete_notification' => 'Поглавље је успешно обрисано', + 'chapter_move' => 'премештено поглавље', + 'chapter_move_notification' => 'Поглавље успешно премештено', + + // Books + 'book_create' => 'креирана књига', + 'book_create_notification' => 'Књига је успешно креирана', + 'book_create_from_chapter' => 'конвертовано поглавље у књигу', + 'book_create_from_chapter_notification' => 'Поглавље успешно конвертовано у књигу', + 'book_update' => 'књига је ажурирана', + 'book_update_notification' => 'Књига је успешно ажурирана', + 'book_delete' => 'књига је обрисана', + 'book_delete_notification' => 'Књига је успешно обрисана', + 'book_sort' => 'сортирана књига', + 'book_sort_notification' => 'Књига је успешно поновно сортирана', + + // Bookshelves + 'bookshelf_create' => 'креирана полица', + 'bookshelf_create_notification' => 'Полица је успешно креирана', + 'bookshelf_create_from_book' => 'конвертована је књига у полицу', + 'bookshelf_create_from_book_notification' => 'Књига је успешно конвертована у полицу', + 'bookshelf_update' => 'ажурирана полица', + 'bookshelf_update_notification' => 'Полица је успешно ажурирана', + 'bookshelf_delete' => 'обрисана је полица', + 'bookshelf_delete_notification' => 'Полица је успешно обрисана', + + // Revisions + 'revision_restore' => 'повраћена ревизија', + 'revision_delete' => 'обрисана ревизија', + 'revision_delete_notification' => 'Ревизија је успешно обрисана', + + // Favourites + 'favourite_add_notification' => '":name" је додато као ваше омиљено', + 'favourite_remove_notification' => '":name" је уклоњено као ваше омиљено', + + // Watching + 'watch_update_level_notification' => 'Подешавање праћених предмета је успешно ажурирано', + + // Auth + 'auth_login' => 'пријављени', + 'auth_register' => 'регистрован као нови корисник', + 'auth_password_reset_request' => 'захтевано ресетовање корисничке лозинке', + 'auth_password_reset_update' => 'ресетуј корисничку лозинку', + 'mfa_setup_method' => 'конфигурисан МФА метод', + 'mfa_setup_method_notification' => 'Вишефакторска метода је успешно конфигурисана', + 'mfa_remove_method' => 'уклоњен МФА метод', + 'mfa_remove_method_notification' => 'Вишефакторска метода је успешно уклоњена', + + // Settings + 'settings_update' => 'ажурирана подешавања', + 'settings_update_notification' => 'Подешавања су успешно ажурирана', + 'maintenance_action_run' => 'покренуо акцију одржавања', + + // Webhooks + 'webhook_create' => 'креиран вебхоок', + 'webhook_create_notification' => 'Вебхоок је успешно креиран', + 'webhook_update' => 'ажуриран вебхоок', + 'webhook_update_notification' => 'Вебхоок је успешно ажуриран', + 'webhook_delete' => 'обрисан вебхоок', + 'webhook_delete_notification' => 'Вебхоок је успешно обрисан', + + // Imports + 'import_create' => 'created import', + 'import_create_notification' => 'Import successfully uploaded', + 'import_run' => 'updated import', + 'import_run_notification' => 'Content successfully imported', + 'import_delete' => 'deleted import', + 'import_delete_notification' => 'Import successfully deleted', + + // Users + 'user_create' => 'креирао корисника', + 'user_create_notification' => 'Корисник је успешно креиран', + 'user_update' => 'ажуриран корисник', + 'user_update_notification' => 'Корисник је успешно ажуриран', + 'user_delete' => 'избрисан корисника', + 'user_delete_notification' => 'Корисник је успешно уклоњен', + + // API Tokens + 'api_token_create' => 'креирао апи токен', + 'api_token_create_notification' => 'АПИ токен је успешно креиран', + 'api_token_update' => 'ажуриран апи токен', + 'api_token_update_notification' => 'АПИ токен је успешно ажуриран', + 'api_token_delete' => 'обрисан апи токен', + 'api_token_delete_notification' => 'АПИ токен је успешно избрисан', + + // Roles + 'role_create' => 'створена улога', + 'role_create_notification' => 'Улога је успешно направљена', + 'role_update' => 'ажурирана улога', + 'role_update_notification' => 'Улога је успешно ажурирана', + 'role_delete' => 'обрисана улога', + 'role_delete_notification' => 'Улога је успешно избрисана', + + // Recycle Bin + 'recycle_bin_empty' => 'испражњена корпа за отпатке', + 'recycle_bin_restore' => 'враћен из корпе за отпатке', + 'recycle_bin_destroy' => 'уклоњен из корпе за отпатке', + + // Comments + 'commented_on' => 'коментарисао', + 'comment_create' => 'додао/ла коментар', + 'comment_update' => 'ажуриран коментар', + 'comment_delete' => 'обрисан коментар', + + // Sort Rules + 'sort_rule_create' => 'created sort rule', + 'sort_rule_create_notification' => 'Sort rule successfully created', + 'sort_rule_update' => 'updated sort rule', + 'sort_rule_update_notification' => 'Sort rule successfully updated', + 'sort_rule_delete' => 'deleted sort rule', + 'sort_rule_delete_notification' => 'Sort rule successfully deleted', + + // Other + 'permissions_update' => 'ажуриране дозволе', +]; diff --git a/lang/sr/auth.php b/lang/sr/auth.php new file mode 100644 index 00000000000..0100e666fa3 --- /dev/null +++ b/lang/sr/auth.php @@ -0,0 +1,117 @@ + 'Ови акредитиви се не поклапају са нашом евиденцијом.', + 'throttle' => 'Превише покушаја пријаве. Покушајте поново за :seconds секунди.', + + // Login & Register + 'sign_up' => 'Региструј се', + 'log_in' => 'Пријави се', + 'log_in_with' => 'Пријавите се са :socialDriver', + 'sign_up_with' => 'Пријавите се са :socialDriver', + 'logout' => 'Одјави се', + + 'name' => 'Име', + 'username' => 'Корисничко име', + 'email' => 'Е-пошта', + 'password' => 'Лозинка', + 'password_confirm' => 'Потврди лозинку', + 'password_hint' => 'Мора да има најмање 8 знакова', + 'forgot_password' => 'Заборавили сте лозинку?', + 'remember_me' => 'Запамти ме', + 'ldap_email_hint' => 'Унесите адресу е-поште коју ћете користити за овај налог.', + 'create_account' => 'Направи налог', + 'already_have_account' => 'Већ имате налог?', + 'dont_have_account' => 'Немате налог?', + 'social_login' => 'Пријава путем друштвених мрежа', + 'social_registration' => 'Регистрација путем друштвених мрежа', + 'social_registration_text' => 'Региструјте се и пријавите користећи другу услугу.', + + 'register_thanks' => 'Хвала на регистрацији!', + 'register_confirm' => 'Проверите своју е-пошту и кликните на дугме за потврду да бисте приступили :appName.', + 'registrations_disabled' => 'Регистрације су тренутно онемогућене', + 'registration_email_domain_invalid' => 'Тај домен е-поште нема приступ овој апликацији', + 'register_success' => 'Хвала што сте се пријавили! Сада сте регистровани и пријављени.', + + // Login auto-initiation + 'auto_init_starting' => 'Покушај пријаве', + 'auto_init_starting_desc' => 'Контактирамо ваш систем за аутентификацију да бисмо започели процес пријављивања. Ако нема напретка након 5 секунди, можете покушати да кликнете на везу испод.', + 'auto_init_start_link' => 'Наставите са аутентификацијом', + + // Password Reset + 'reset_password' => 'Ресетуј лозинку', + 'reset_password_send_instructions' => 'Унесите своју адресу е-поште испод и биће вам послата порука е-поште са везом за ресетовање лозинке.', + 'reset_password_send_button' => 'Пошаљи везу за ресетовање', + 'reset_password_sent' => 'Веза за ресетовање лозинке ће бити послата на :email ако се та адреса е-поште пронађе у систему.', + 'reset_password_success' => 'Ваша лозинка је успешно ресетована.', + 'email_reset_subject' => 'Ресетујте лозинку за :appName', + 'email_reset_text' => 'Примили сте ову е-пошту јер смо примили захтев за ресетовање лозинке за ваш налог.', + 'email_reset_not_requested' => 'Ако нисте захтевали ресетовање лозинке, нису потребне додатне радње.', + + // Email Confirmation + 'email_confirm_subject' => 'Потврдите своју е-пошту на :appName', + 'email_confirm_greeting' => 'Хвала што сте се придружили :appName!', + 'email_confirm_text' => 'Молимо потврдите своју адресу е-поште кликом на дугме испод:', + 'email_confirm_action' => 'Потврдите е-пошту', + 'email_confirm_send_error' => 'Потребна је потврда е-поште, али систем није могао да пошаље е-пошту. Контактирајте администратора да бисте се уверили да је е-пошта исправно подешена.', + 'email_confirm_success' => 'Ваша е-пошта је потврђена! Сада би требало да будете у могућности да се пријавите користећи ову адресу е-поште.', + 'email_confirm_resent' => 'Потврда е-поште је поново послата, проверите пријемно сандуче.', + 'email_confirm_thanks' => 'Хвала на потврди!', + 'email_confirm_thanks_desc' => 'Сачекајте тренутак док се обради ваша потврда. Ако не будете преусмерени након 3 секунде, притисните доњу везу „Настави“ да бисте наставили.', + + 'email_not_confirmed' => 'Адреса е-поште није потврђена', + 'email_not_confirmed_text' => 'Ваша адреса е-поште још није потврђена.', + 'email_not_confirmed_click_link' => 'Кликните на везу у е-поруци која је послата убрзо након што сте се регистровали.', + 'email_not_confirmed_resend' => 'Ако не можете да пронађете е-пошту, можете поново да пошаљете е-поруку за потврду тако што ћете послати образац испод.', + 'email_not_confirmed_resend_button' => 'Пошаљи поново мејл за потврду', + + // User Invite + 'user_invite_email_subject' => 'Позвани сте да се придружите :appName!', + 'user_invite_email_greeting' => 'За вас је креиран налог на :appName.', + 'user_invite_email_text' => 'Кликните на дугме испод да бисте поставили лозинку за налог и добили приступ:', + 'user_invite_email_action' => 'Подесите лозинку за налог', + 'user_invite_page_welcome' => 'Добродошли у :appName!', + 'user_invite_page_text' => 'Да бисте завршили креирање налога и добили приступ, потребно је да поставите лозинку која ће се користити за пријаву на :appName приликом будућих посета.', + 'user_invite_page_confirm_button' => 'Потврди лозинку', + 'user_invite_success_login' => 'Лозинка је постављена, сада би требало да будете у могућности да се пријавите користећи постављену лозинку за приступ :appName!', + + // Multi-factor Authentication + 'mfa_setup' => 'Подешавање вишефакторске аутентификације', + 'mfa_setup_desc' => 'Подесите вишефакторску аутентификацију као додатни ниво безбедности за ваш кориснички налог.', + 'mfa_setup_configured' => 'Већ конфигурисано', + 'mfa_setup_reconfigure' => 'Поново конфигуришите', + 'mfa_setup_remove_confirmation' => 'Да ли сте сигурни да желите да уклоните овај метод вишефакторске аутентификације?', + 'mfa_setup_action' => 'Подешавање', + 'mfa_backup_codes_usage_limit_warning' => 'Преостало вам је мање од 5 резервних кодова. Генеришите и сачувајте нови сет пре него што вам понестане кодова како бисте спречили да останете без налога.', + 'mfa_option_totp_title' => 'Aplikacije za mobilne uređaje', + 'mfa_option_totp_desc' => 'Да бисте користили вишефакторску аутентификацију, биће вам потребна мобилна апликација која подржава ТОТП, као што јеGoogle Authenticator, Authy или Microsoft Authenticator.', + 'mfa_option_backup_codes_title' => 'Резервни кодови', + 'mfa_option_backup_codes_desc' => 'Генерише скуп резервних кодова за једнократну употребу које ћете унети приликом пријављивања да бисте потврдили свој идентитет. Обавезно их чувајте на безбедном и безбедном месту.', + 'mfa_gen_confirm_and_enable' => 'Потврдите и омогућите', + 'mfa_gen_backup_codes_title' => 'Подешавање резервних кодова', + 'mfa_gen_backup_codes_desc' => 'Чувајте доњу листу кодова на безбедном месту. Када приступате систему, моћи ћете да користите један од кодова као други механизам за аутентификацију.', + 'mfa_gen_backup_codes_download' => 'Преузми кодове', + 'mfa_gen_backup_codes_usage_warning' => 'Сваки код се може искористити једном', + 'mfa_gen_totp_title' => 'Подешавање мобилне апликације', + 'mfa_gen_totp_desc' => 'Да бисте користили вишефакторску аутентификацију, биће вам потребна мобилна апликација која подржава ТОТП, као што је Google Authenticator, Authy или Microsoft Authenticator.', + 'mfa_gen_totp_scan' => 'Скенирајте QR код у наставку помоћу жељене апликације за аутентификацију да бисте започели.', + 'mfa_gen_totp_verify_setup' => 'Верификуј подешавања', + 'mfa_gen_totp_verify_setup_desc' => 'Проверите да ли све функционише тако што ћете унети код, генерисан у вашој апликацији за потврду идентитета, у поље за унос испод:', + 'mfa_gen_totp_provide_code_here' => 'Овде унесите код који је генерисао апликација', + 'mfa_verify_access' => 'Верификуј приступ', + 'mfa_verify_access_desc' => 'Ваш кориснички налог захтева да потврдите свој идентитет путем додатног нивоа верификације пре него што вам се одобри приступ. Потврдите коришћењем једног од конфигурисаних метода да бисте наставили.', + 'mfa_verify_no_methods' => 'Методе нису конфигурисане', + 'mfa_verify_no_methods_desc' => 'Нису пронађене методе вишефакторске аутентификације за ваш налог. Мораћете да подесите најмање један метод пре него што добијете приступ.', + 'mfa_verify_use_totp' => 'Верификација путем мобилне апликације', + 'mfa_verify_use_backup_codes' => 'Проверите помоћу резервног кода', + 'mfa_verify_backup_code' => 'Резервни код', + 'mfa_verify_backup_code_desc' => 'Унесите један од преосталих резервних кодова у наставку:', + 'mfa_verify_backup_code_enter_here' => 'Унеси резервни код овде', + 'mfa_verify_totp_desc' => 'Унесите код, генерисан помоћу ваше мобилне апликације, у наставку:', + 'mfa_setup_login_notification' => 'Вишефакторска метода је конфигурисана, сада се поново пријавите користећи конфигурисани метод.', +]; diff --git a/lang/sr/common.php b/lang/sr/common.php new file mode 100644 index 00000000000..e185c99f5bf --- /dev/null +++ b/lang/sr/common.php @@ -0,0 +1,115 @@ + 'Поништи', + 'close' => 'Затвори', + 'confirm' => 'Потврди', + 'back' => 'Назад', + 'save' => 'Сачувај', + 'continue' => 'Настави', + 'select' => 'Изабери', + 'toggle_all' => 'Сакриј/Прикажи све', + 'more' => 'Више', + + // Form Labels + 'name' => 'Назив', + 'description' => 'Опис', + 'role' => 'Улога', + 'cover_image' => 'Насловна слика', + 'cover_image_description' => 'Ова слика би требало да буде приближно 440к250px иако ће бити флексибилно скалирана и исечена како би одговарала корисничком интерфејсу у различитим сценаријима по потреби, тако да ће се стварне димензије приказа разликовати.', + + // Actions + 'actions' => 'Радње', + 'view' => 'Преглед', + 'view_all' => 'Прикажи све', + 'new' => 'Ново', + 'create' => 'Креирај', + 'update' => 'Ажурирање', + 'edit' => 'Уреди', + 'archive' => 'Archive', + 'unarchive' => 'Un-Archive', + 'sort' => 'Разврстај', + 'move' => 'Премести', + 'copy' => 'Умножи', + 'reply' => 'Одговор', + 'delete' => 'Обриши', + 'delete_confirm' => 'Потврди брисање', + 'search' => 'Претражи', + 'search_clear' => 'Обриши претрагу', + 'reset' => 'Ресетуј', + 'remove' => 'Уклони', + 'add' => 'Додај', + 'configure' => 'Конфигуриши', + 'manage' => 'Управљај', + 'fullscreen' => 'Преко целог екрана', + 'favourite' => 'Омиљено', + 'unfavourite' => 'Уклони из "Омиљено"', + 'next' => 'Даље', + 'previous' => 'Претходно', + 'filter_active' => 'Активни филтер:', + 'filter_clear' => 'Уклони филтер', + 'download' => 'Преузимања', + 'open_in_tab' => 'Отвори у картици', + 'open' => 'Отвори', + + // Sort Options + 'sort_options' => 'Опције сортирања', + 'sort_direction_toggle' => 'Пребацивање смера сортирања', + 'sort_ascending' => 'Поређај растуће', + 'sort_descending' => 'Поређај опадајуће', + 'sort_name' => 'Назив', + 'sort_default' => 'Подразумевано', + 'sort_created_at' => 'Датум креирања', + 'sort_updated_at' => 'Датум ажурирања', + + // Misc + 'deleted_user' => 'Избрисан корисник', + 'no_activity' => 'Нема активности за приказ', + 'no_items' => 'Нема доступних ставки', + 'back_to_top' => 'Назад на врх', + 'skip_to_main_content' => 'Пређи на главни садржај', + 'toggle_details' => 'Пребаци приказ детаља', + 'toggle_thumbnails' => 'Пребаци минијатуре', + 'details' => 'Детаљи', + 'grid_view' => 'Приказ мреже', + 'list_view' => 'Приказ листе', + 'default' => 'Подразумевано', + 'breadcrumb' => 'Навигација', + 'status' => 'Стање', + 'status_active' => 'Активан', + 'status_inactive' => 'Неактивно', + 'never' => 'Никад', + 'none' => 'Ништа', + + // Header + 'homepage' => 'Почетна страна', + 'header_menu_expand' => 'Проширите мени заглавља', + 'profile_menu' => 'Мени профила', + 'view_profile' => 'Погледај Профил', + 'edit_profile' => 'Измени профил', + 'dark_mode' => 'Тамни режим', + 'light_mode' => 'Светли режим', + 'global_search' => 'Глобална претрага', + + // Layout tabs + 'tab_info' => 'Информације', + 'tab_info_label' => 'Картица: Прикажи секундарне информације', + 'tab_content' => 'Садржај', + 'tab_content_label' => 'Картица: Прикажи примарни садржај', + + // Email Content + 'email_action_help' => 'Ако имате проблема да кликнете на дугме ":actionText" копирајте и налепите УРЛ доле у свој веб прегледач:', + 'email_rights' => 'Сва права задржана', + + // Footer Link Options + // Not directly used but available for convenience to users. + 'privacy_policy' => 'Правила о приватности', + 'terms_of_service' => 'Услови коришћења', + + // OpenSearch + 'opensearch_description' => 'Search :appName', +]; diff --git a/lang/sr/components.php b/lang/sr/components.php new file mode 100644 index 00000000000..2a78d819bf9 --- /dev/null +++ b/lang/sr/components.php @@ -0,0 +1,46 @@ + 'Изаберите слику', + 'image_list' => 'Листа слика', + 'image_details' => 'Детаљи слике', + 'image_upload' => 'Додај слику', + 'image_intro' => 'Овде можете изабрати и управљати сликама које су претходно отпремљене у систем.', + 'image_intro_upload' => 'Отпремите нову слику тако што ћете превући датотеку слике у овај прозор или помоћу дугмета „Отпреми слику“ изнад.', + 'image_all' => 'Све', + 'image_all_title' => 'Прикажи све слике', + 'image_book_title' => 'Погледајте слике отпремљене уз ову књигу', + 'image_page_title' => 'Погледајте слике отпремљене на ову страницу', + 'image_search_hint' => 'Претражите по имену слике', + 'image_uploaded' => 'Отпремљено :uploadedDate', + 'image_uploaded_by' => 'Поставио :userName', + 'image_uploaded_to' => 'Отпремљено на :pageLink', + 'image_updated' => 'Ажурирано :updateDate', + 'image_load_more' => 'Учитај још', + 'image_image_name' => 'Назив слике', + 'image_delete_used' => 'Ова слика се користи на страницама испод.', + 'image_delete_confirm_text' => 'Да ли си сигуран да желиш да избришеш ову слику?', + 'image_select_image' => 'Изабери слику', + 'image_dropzone' => 'Испустите слике или кликните овде да их отпремите', + 'image_dropzone_drop' => 'Испустите слике да их отпремите', + 'images_deleted' => 'Слике су избрисане', + 'image_preview' => 'Слике су избрисане', + 'image_upload_success' => 'Слика је успешно отпремљена', + 'image_update_success' => 'Детаљи слике су успешно ажурирани', + 'image_delete_success' => 'Слика је успешно избрисана', + 'image_replace' => 'Замени слику', + 'image_replace_success' => 'Датотека слике је успешно ажурирана', + 'image_rebuild_thumbs' => 'Регенеришите варијације величине', + 'image_rebuild_thumbs_success' => 'Варијације величине слике су успешно обновљене!', + + // Code Editor + 'code_editor' => 'Уреди код', + 'code_language' => 'Језик кода', + 'code_content' => 'Садржај кода', + 'code_session_history' => 'Историја сесије', + 'code_save' => 'Сачувај код', +]; diff --git a/lang/sr/editor.php b/lang/sr/editor.php new file mode 100644 index 00000000000..e5052595ceb --- /dev/null +++ b/lang/sr/editor.php @@ -0,0 +1,182 @@ + 'Опште', + 'advanced' => 'Напредно', + 'none' => 'Ништа', + 'cancel' => 'Поништи', + 'save' => 'Сачувај', + 'close' => 'Затвори', + 'apply' => 'Apply', + 'undo' => 'Опозови', + 'redo' => 'Понови', + 'left' => 'Лево', + 'center' => 'Центар', + 'right' => 'Десно', + 'top' => 'Врх', + 'middle' => 'Средина', + 'bottom' => 'Дно', + 'width' => 'Ширина', + 'height' => 'Висина', + 'More' => 'Више', + 'select' => 'Изабери...', + + // Toolbar + 'formats' => 'Формати', + 'header_large' => 'Велико заглавље', + 'header_medium' => 'Средње заглавље', + 'header_small' => 'Мало заглавље', + 'header_tiny' => 'Малено заглавље', + 'paragraph' => 'Параграф', + 'blockquote' => 'Цитат', + 'inline_code' => 'Уграђени код', + 'callouts' => 'Облачићи', + 'callout_information' => 'Информација', + 'callout_success' => 'Успешно', + 'callout_warning' => 'Упозорење', + 'callout_danger' => 'Опасност', + 'bold' => 'Подебљано', + 'italic' => 'Курзив', + 'underline' => 'Подвучено', + 'strikethrough' => 'Прецртано', + 'superscript' => 'Надскрипт', + 'subscript' => 'Субкрипт', + 'text_color' => 'Боја текста', + 'highlight_color' => 'Highlight color', + 'custom_color' => 'Боја текста', + 'remove_color' => 'Уклоните боју', + 'background_color' => 'Боја позадине', + 'align_left' => 'Поравнај лево', + 'align_center' => 'Поравнај по средини', + 'align_right' => 'Поравнај деcно', + 'align_justify' => 'Поравнај', + 'list_bullet' => 'Листа ставки', + 'list_numbered' => 'Нумерисана листа', + 'list_task' => 'Листа задатака', + 'indent_increase' => 'Повећај увлачење', + 'indent_decrease' => 'Умањи увлачење', + 'table' => 'Tabela', + 'insert_image' => 'Уметни слику', + 'insert_image_title' => 'Убаци/уреди слику', + 'insert_link' => 'Убаци/измени везу', + 'insert_link_title' => 'Убаци/измени везу', + 'insert_horizontal_line' => 'Уметни водоравну линију', + 'insert_code_block' => 'Убаците блок кода', + 'edit_code_block' => 'Уредите блок кода', + 'insert_drawing' => 'Уметните/уредите цртеж', + 'drawing_manager' => 'Менаџер цртежа', + 'insert_media' => 'Убаците/уредите медиј', + 'insert_media_title' => 'Уметање/уређивање медија', + 'clear_formatting' => 'Обриши форматирање', + 'source_code' => 'Изворни код', + 'source_code_title' => 'Изворни код', + 'fullscreen' => 'Преко целог екрана', + 'image_options' => 'Подешавање слика', + + // Tables + 'table_properties' => 'Својства табеле', + 'table_properties_title' => 'Својства табеле', + 'delete_table' => 'Обриши табелу', + 'table_clear_formatting' => 'Обриши форматирање табеле', + 'resize_to_contents' => 'Промени величину садржају', + 'row_header' => 'Заглавље реда', + 'insert_row_before' => 'Уметни ред испред', + 'insert_row_after' => 'Уметните ред после', + 'delete_row' => 'Обриши ред', + 'insert_column_before' => 'Уметни колону испред', + 'insert_column_after' => 'Уметни колону после', + 'delete_column' => 'Обриши колону', + 'table_cell' => 'Ћелија', + 'table_row' => 'Ред', + 'table_column' => 'Колона', + 'cell_properties' => 'Својства ћелије', + 'cell_properties_title' => 'Својства ћелије', + 'cell_type' => 'Тип ћелије', + 'cell_type_cell' => 'Ћелија', + 'cell_scope' => 'Обим', + 'cell_type_header' => 'Заглавље ћелије', + 'merge_cells' => 'Обједини ћелије', + 'split_cell' => 'Подели ћелију', + 'table_row_group' => 'Група редова', + 'table_column_group' => 'Група колона', + 'horizontal_align' => 'Хоризонтално поравнање', + 'vertical_align' => 'Вертикално поравнати', + 'border_width' => 'Ширина ивице', + 'border_style' => 'Стил ивице', + 'border_color' => 'Боја ивице', + 'row_properties' => 'Својства реда', + 'row_properties_title' => 'Својства реда', + 'cut_row' => 'Пресеци ред', + 'copy_row' => 'Копирај ред', + 'paste_row_before' => 'Налепите ред пре', + 'paste_row_after' => 'Налепите ред после', + 'row_type' => 'Врста реда', + 'row_type_header' => 'Заглавље', + 'row_type_body' => 'Садржај', + 'row_type_footer' => 'Подножје', + 'alignment' => 'Поравнавање', + 'cut_column' => 'Изрежите колону', + 'copy_column' => 'Копирај колону', + 'paste_column_before' => 'Налепите колону пре', + 'paste_column_after' => 'Налепите колону после', + 'cell_padding' => 'Попуна ћелија', + 'cell_spacing' => 'Размак ћелија', + 'caption' => 'Опис', + 'show_caption' => 'Прикажи титл', + 'constrain' => 'Ограничавају размере', + 'cell_border_solid' => 'Чврсто тело', + 'cell_border_dotted' => 'Тачкасто', + 'cell_border_dashed' => 'Испрекидана', + 'cell_border_double' => 'Двострук', + 'cell_border_groove' => 'Жлеб', + 'cell_border_ridge' => 'Гребен', + 'cell_border_inset' => 'Уметак', + 'cell_border_outset' => 'Почетак', + 'cell_border_none' => 'Без', + 'cell_border_hidden' => 'Сакривено', + + // Images, links, details/summary & embed + 'source' => 'Извор', + 'alt_desc' => 'Алтернативни опис', + 'embed' => 'Угради', + 'paste_embed' => 'Налепите свој код за уградњу испод:', + 'url' => 'УРЛ', + 'text_to_display' => 'Текст за приказ', + 'title' => 'Наслов', + 'browse_links' => 'Browse links', + 'open_link' => 'Отвори везу', + 'open_link_in' => 'Отвори везу у...', + 'open_link_current' => 'Тренутни прозор', + 'open_link_new' => 'Нови Прозор', + 'remove_link' => 'Уклони везу', + 'insert_collapsible' => 'Уредите склопиви блок', + 'collapsible_unwrap' => 'Одмотати', + 'edit_label' => 'Уреди ознаку', + 'toggle_open_closed' => 'Отварање/затварање', + 'collapsible_edit' => 'Уредите склопиви блок', + 'toggle_label' => 'Укључите ознаку', + + // About view + 'about' => 'О уређивачу', + 'about_title' => 'О уређивачу WYSIWYG', + 'editor_license' => 'Уредничка лиценца и ауторска права', + 'editor_lexical_license' => 'This editor is built as a fork of :lexicalLink which is distributed under the MIT license.', + 'editor_lexical_license_link' => 'Full license details can be found here.', + 'editor_tiny_license' => 'Овај уређивач је направљен помоћу :tinyLink који је обезбеђен под МИТ лиценцом.', + 'editor_tiny_license_link' => 'Детаље о ауторским правима и лиценци за ТиниМЦЕ можете пронаћи овде.', + 'save_continue' => 'Сачувај страницу и настави', + 'callouts_cycle' => '(Наставите да притискате да бисте прелазили између типова)', + 'link_selector' => 'Линк до садржаја', + 'shortcuts' => 'Пречице', + 'shortcut' => 'Пречица', + 'shortcuts_intro' => 'Следеће пречице су доступне у уређивачу:', + 'windows_linux' => '(Windows/Linux)', + 'mac' => '(Mac)', + 'description' => 'Опис', +]; diff --git a/lang/sr/entities.php b/lang/sr/entities.php new file mode 100644 index 00000000000..456a76ceb17 --- /dev/null +++ b/lang/sr/entities.php @@ -0,0 +1,476 @@ + 'Недавно додато', + 'recently_created_pages' => 'Недавно креиране странице', + 'recently_updated_pages' => 'Недавно ажуриране странице', + 'recently_created_chapters' => 'Недавно креирана поглавља', + 'recently_created_books' => 'Недавно креиране књиге', + 'recently_created_shelves' => 'Недавно креиране полице', + 'recently_update' => 'Недавно ажурирано', + 'recently_viewed' => 'Недавно прегледано', + 'recent_activity' => 'Скорашња активност', + 'create_now' => 'Направи један сада', + 'revisions' => 'Ревизије', + 'meta_revision' => 'Ревизија #:revisionCount', + 'meta_created' => 'Направљено :timeLength', + 'meta_created_name' => 'Направљено :timeLength од :user', + 'meta_updated' => 'Ажурирано :timeLength', + 'meta_updated_name' => 'Ажурирано :timeLength од :user', + 'meta_owned_name' => 'Власништво :user', + 'meta_reference_count' => 'Референтна од :count item|Референтна од :count items', + 'entity_select' => 'Избор ентитета', + 'entity_select_lack_permission' => 'Немате потребне дозволе да изаберете ову ставку', + 'images' => 'Слике', + 'my_recent_drafts' => 'Моји недавни нацрти', + 'my_recently_viewed' => 'Моје недавно прегледано', + 'my_most_viewed_favourites' => 'Моји најгледанији фаворити', + 'my_favourites' => 'Моји омиљени', + 'no_pages_viewed' => 'Нисте погледали ниједну страницу', + 'no_pages_recently_created' => 'Недавно није направљена ниједна страница', + 'no_pages_recently_updated' => 'Ниједна страница није недавно ажурирана', + 'export' => 'Извоз', + 'export_html' => 'Садржана веб датотека', + 'export_pdf' => 'PDF датотека', + 'export_text' => 'Датотеке чистог текста', + 'export_md' => 'Markdown File', + 'export_zip' => 'Portable ZIP', + 'default_template' => 'Подразумевани шаблон странице', + 'default_template_explain' => 'Доделите шаблон странице који ће се користити као подразумевани садржај за све странице креиране у оквиру ове ставке. Имајте на уму да ће се ово користити само ако креатор странице има приступ за преглед изабране странице шаблона.', + 'default_template_select' => 'Изаберите страницу са шаблоном', + 'import' => 'Import', + 'import_validate' => 'Validate Import', + 'import_desc' => 'Import books, chapters & pages using a portable zip export from the same, or a different, instance. Select a ZIP file to proceed. After the file has been uploaded and validated you\'ll be able to configure & confirm the import in the next view.', + '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_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_size' => ':size Import ZIP Size', + 'import_uploaded_at' => 'Uploaded :relativeTime', + 'import_uploaded_by' => 'Uploaded by', + 'import_location' => 'Import Location', + '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_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', + 'breadcrumb_siblings_for_book' => 'Navigate siblings for book', + 'breadcrumb_siblings_for_bookshelf' => 'Navigate siblings for shelf', + + // Permissions and restrictions + 'permissions' => 'Дозволе', + 'permissions_desc' => 'Подесите дозволе овде да бисте заменили подразумеване дозволе које дају корисничке улоге.', + 'permissions_book_cascade' => 'Дозволе постављене за књиге ће се аутоматски пребацивати на подређена поглавља и странице, осим ако немају дефинисане сопствене дозволе.', + 'permissions_chapter_cascade' => 'Дозволе постављене на поглављима ће се аутоматски каскадно пребацивати на подређене странице, осим ако немају дефинисане сопствене дозволе.', + 'permissions_save' => 'Сачувај дозволе', + 'permissions_owner' => 'Власник', + 'permissions_role_everyone_else' => 'Сви остали', + 'permissions_role_everyone_else_desc' => 'Подесите дозволе за све улоге које нису посебно замењене.', + 'permissions_role_override' => 'Замени дозволе за улогу', + 'permissions_inherit_defaults' => 'Наследи подразумеване вредности', + + // Search + 'search_results' => 'Резултати претраге', + 'search_total_results_found' => ':count пронађених резултата|:count укупно пронађених резултата', + 'search_clear' => 'Обриши претрагу', + 'search_no_pages' => 'No pages matched this search', + 'search_for_term' => 'Search for :term', + 'search_more' => 'More Results', + 'search_advanced' => 'Advanced Search', + 'search_terms' => 'Search Terms', + 'search_content_type' => 'Content Type', + 'search_exact_matches' => 'Exact Matches', + 'search_tags' => 'Tag Searches', + 'search_options' => 'Options', + 'search_viewed_by_me' => 'Viewed by me', + 'search_not_viewed_by_me' => 'Not viewed by me', + 'search_permissions_set' => 'Permissions set', + 'search_created_by_me' => 'Created by me', + 'search_updated_by_me' => 'Updated by me', + 'search_owned_by_me' => 'Owned by me', + 'search_date_options' => 'Date Options', + 'search_updated_before' => 'Updated before', + 'search_updated_after' => 'Updated after', + 'search_created_before' => 'Created before', + 'search_created_after' => 'Created after', + 'search_set_date' => 'Set Date', + 'search_update' => 'Update Search', + + // Shelves + 'shelf' => 'Shelf', + 'shelves' => 'Shelves', + 'x_shelves' => ':count Shelf|:count Shelves', + 'shelves_empty' => 'No shelves have been created', + 'shelves_create' => 'Create New Shelf', + 'shelves_popular' => 'Popular Shelves', + 'shelves_new' => 'New Shelves', + 'shelves_new_action' => 'New Shelf', + 'shelves_popular_empty' => 'The most popular shelves will appear here.', + 'shelves_new_empty' => 'The most recently created shelves will appear here.', + 'shelves_save' => 'Save Shelf', + 'shelves_books' => 'Books on this shelf', + 'shelves_add_books' => 'Add books to this shelf', + 'shelves_drag_books' => 'Drag books below to add them to this shelf', + 'shelves_empty_contents' => 'This shelf has no books assigned to it', + 'shelves_edit_and_assign' => 'Edit shelf to assign books', + 'shelves_edit_named' => 'Edit Shelf :name', + 'shelves_edit' => 'Edit Shelf', + 'shelves_delete' => 'Delete Shelf', + 'shelves_delete_named' => 'Delete Shelf :name', + 'shelves_delete_explain' => "This will delete the shelf with the name ':name'. Contained books will not be deleted.", + 'shelves_delete_confirmation' => 'Are you sure you want to delete this shelf?', + 'shelves_permissions' => 'Shelf Permissions', + 'shelves_permissions_updated' => 'Shelf Permissions Updated', + 'shelves_permissions_active' => 'Shelf Permissions Active', + 'shelves_permissions_cascade_warning' => 'Permissions on shelves do not automatically cascade to contained books. This is because a book can exist on multiple shelves. Permissions can however be copied down to child books using the option found below.', + 'shelves_permissions_create' => 'Shelf create permissions are only used for copying permissions to child books using the action below. They do not control the ability to create books.', + 'shelves_copy_permissions_to_books' => 'Copy Permissions to Books', + 'shelves_copy_permissions' => 'Copy Permissions', + 'shelves_copy_permissions_explain' => 'This will apply the current permission settings of this shelf to all books contained within. Before activating, ensure any changes to the permissions of this shelf have been saved.', + 'shelves_copy_permission_success' => 'Shelf permissions copied to :count books', + + // Books + 'book' => 'Book', + 'books' => 'Books', + 'x_books' => ':count Book|:count Books', + 'books_empty' => 'No books have been created', + 'books_popular' => 'Popular Books', + 'books_recent' => 'Recent Books', + 'books_new' => 'New Books', + 'books_new_action' => 'New Book', + 'books_popular_empty' => 'The most popular books will appear here.', + 'books_new_empty' => 'The most recently created books will appear here.', + 'books_create' => 'Create New Book', + 'books_delete' => 'Delete Book', + 'books_delete_named' => 'Delete Book :bookName', + 'books_delete_explain' => 'This will delete the book with the name \':bookName\'. All pages and chapters will be removed.', + 'books_delete_confirmation' => 'Are you sure you want to delete this book?', + 'books_edit' => 'Edit Book', + 'books_edit_named' => 'Edit Book :bookName', + 'books_form_book_name' => 'Book Name', + 'books_save' => 'Save Book', + 'books_permissions' => 'Book Permissions', + 'books_permissions_updated' => 'Book Permissions Updated', + 'books_empty_contents' => 'No pages or chapters have been created for this book.', + 'books_empty_create_page' => 'Create a new page', + 'books_empty_sort_current_book' => 'Sort the current book', + 'books_empty_add_chapter' => 'Add a chapter', + 'books_permissions_active' => 'Book Permissions Active', + 'books_search_this' => 'Search this book', + 'books_navigation' => 'Book Navigation', + 'books_sort' => 'Sort Book Contents', + 'books_sort_desc' => 'Move chapters and pages within a book to reorganise its contents. Other books can be added which allows easy moving of chapters and pages between books. Optionally an auto sort rule can be set to automatically sort this book\'s contents upon changes.', + 'books_sort_auto_sort' => 'Auto Sort Option', + 'books_sort_auto_sort_active' => 'Auto Sort Active: :sortName', + 'books_sort_named' => 'Sort Book :bookName', + 'books_sort_name' => 'Sort by Name', + 'books_sort_created' => 'Sort by Created Date', + 'books_sort_updated' => 'Sort by Updated Date', + 'books_sort_chapters_first' => 'Chapters First', + 'books_sort_chapters_last' => 'Chapters Last', + 'books_sort_show_other' => 'Show Other Books', + 'books_sort_save' => 'Save New Order', + 'books_sort_show_other_desc' => 'Add other books here to include them in the sort operation, and allow easy cross-book reorganisation.', + 'books_sort_move_up' => 'Move Up', + 'books_sort_move_down' => 'Move Down', + 'books_sort_move_prev_book' => 'Move to Previous Book', + 'books_sort_move_next_book' => 'Move to Next Book', + 'books_sort_move_prev_chapter' => 'Move Into Previous Chapter', + 'books_sort_move_next_chapter' => 'Move Into Next Chapter', + 'books_sort_move_book_start' => 'Move to Start of Book', + 'books_sort_move_book_end' => 'Move to End of Book', + 'books_sort_move_before_chapter' => 'Move to Before Chapter', + 'books_sort_move_after_chapter' => 'Move to After Chapter', + 'books_copy' => 'Copy Book', + 'books_copy_success' => 'Book successfully copied', + + // Chapters + 'chapter' => 'Chapter', + 'chapters' => 'Chapters', + 'x_chapters' => ':count Chapter|:count Chapters', + 'chapters_popular' => 'Popular Chapters', + 'chapters_new' => 'New Chapter', + 'chapters_create' => 'Create New Chapter', + 'chapters_delete' => 'Delete Chapter', + 'chapters_delete_named' => 'Delete Chapter :chapterName', + 'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages that exist within this chapter will also be deleted.', + 'chapters_delete_confirm' => 'Are you sure you want to delete this chapter?', + 'chapters_edit' => 'Edit Chapter', + 'chapters_edit_named' => 'Edit Chapter :chapterName', + 'chapters_save' => 'Save Chapter', + 'chapters_move' => 'Move Chapter', + 'chapters_move_named' => 'Move Chapter :chapterName', + 'chapters_copy' => 'Copy Chapter', + 'chapters_copy_success' => 'Chapter successfully copied', + 'chapters_permissions' => 'Chapter Permissions', + 'chapters_empty' => 'No pages are currently in this chapter.', + 'chapters_permissions_active' => 'Chapter Permissions Active', + 'chapters_permissions_success' => 'Chapter Permissions Updated', + 'chapters_search_this' => 'Search this chapter', + 'chapter_sort_book' => 'Sort Book', + + // Pages + 'page' => 'Page', + 'pages' => 'Pages', + 'x_pages' => ':count Page|:count Pages', + 'pages_popular' => 'Popular Pages', + 'pages_new' => 'New Page', + 'pages_attachments' => 'Attachments', + 'pages_navigation' => 'Page Navigation', + 'pages_delete' => 'Delete Page', + 'pages_delete_named' => 'Delete Page :pageName', + 'pages_delete_draft_named' => 'Delete Draft Page :pageName', + 'pages_delete_draft' => 'Delete Draft Page', + 'pages_delete_success' => 'Page deleted', + 'pages_delete_draft_success' => 'Draft page deleted', + 'pages_delete_warning_template' => 'This page is in active use as a book or chapter default page template. These books or chapters will no longer have a default page template assigned after this page is deleted.', + 'pages_delete_confirm' => 'Are you sure you want to delete this page?', + 'pages_delete_draft_confirm' => 'Are you sure you want to delete this draft page?', + 'pages_editing_named' => 'Editing Page :pageName', + 'pages_edit_draft_options' => 'Draft Options', + 'pages_edit_save_draft' => 'Save Draft', + 'pages_edit_draft' => 'Edit Page Draft', + 'pages_editing_draft' => 'Editing Draft', + 'pages_editing_page' => 'Editing Page', + 'pages_edit_draft_save_at' => 'Draft saved at ', + 'pages_edit_delete_draft' => 'Delete Draft', + 'pages_edit_delete_draft_confirm' => 'Are you sure you want to delete your draft page changes? All of your changes, since the last full save, will be lost and the editor will be updated with the latest page non-draft save state.', + 'pages_edit_discard_draft' => 'Discard Draft', + 'pages_edit_switch_to_markdown' => 'Switch to Markdown Editor', + 'pages_edit_switch_to_markdown_clean' => '(Clean Content)', + 'pages_edit_switch_to_markdown_stable' => '(Stable Content)', + 'pages_edit_switch_to_wysiwyg' => 'Switch to WYSIWYG Editor', + 'pages_edit_switch_to_new_wysiwyg' => 'Switch to new WYSIWYG', + 'pages_edit_switch_to_new_wysiwyg_desc' => '(In Beta Testing)', + 'pages_edit_set_changelog' => 'Set Changelog', + 'pages_edit_enter_changelog_desc' => 'Enter a brief description of the changes you\'ve made', + 'pages_edit_enter_changelog' => 'Enter Changelog', + 'pages_editor_switch_title' => 'Switch Editor', + 'pages_editor_switch_are_you_sure' => 'Are you sure you want to change the editor for this page?', + 'pages_editor_switch_consider_following' => 'Consider the following when changing editors:', + 'pages_editor_switch_consideration_a' => 'Once saved, the new editor option will be used by any future editors, including those that may not be able to change editor type themselves.', + 'pages_editor_switch_consideration_b' => 'This can potentially lead to a loss of detail and syntax in certain circumstances.', + 'pages_editor_switch_consideration_c' => 'Tag or changelog changes, made since last save, won\'t persist across this change.', + 'pages_save' => 'Save Page', + 'pages_title' => 'Page Title', + 'pages_name' => 'Page Name', + 'pages_md_editor' => 'Editor', + 'pages_md_preview' => 'Preview', + 'pages_md_insert_image' => 'Insert Image', + 'pages_md_insert_link' => 'Insert Entity Link', + 'pages_md_insert_drawing' => 'Insert Drawing', + 'pages_md_show_preview' => 'Show preview', + 'pages_md_sync_scroll' => 'Sync preview scroll', + 'pages_md_plain_editor' => 'Plaintext editor', + 'pages_drawing_unsaved' => 'Unsaved Drawing Found', + 'pages_drawing_unsaved_confirm' => 'Unsaved drawing data was found from a previous failed drawing save attempt. Would you like to restore and continue editing this unsaved drawing?', + 'pages_not_in_chapter' => 'Page is not in a chapter', + 'pages_move' => 'Move Page', + 'pages_copy' => 'Copy Page', + 'pages_copy_desination' => 'Copy Destination', + 'pages_copy_success' => 'Page successfully copied', + 'pages_permissions' => 'Page Permissions', + 'pages_permissions_success' => 'Page permissions updated', + 'pages_revision' => 'Revision', + 'pages_revisions' => 'Page Revisions', + 'pages_revisions_desc' => 'Listed below are all the past revisions of this page. You can look back upon, compare, and restore old page versions if permissions allow. The full history of the page may not be fully reflected here since, depending on system configuration, old revisions could be auto-deleted.', + 'pages_revisions_named' => 'Page Revisions for :pageName', + 'pages_revision_named' => 'Page Revision for :pageName', + 'pages_revision_restored_from' => 'Restored from #:id; :summary', + 'pages_revisions_created_by' => 'Created By', + 'pages_revisions_date' => 'Revision Date', + 'pages_revisions_number' => '#', + 'pages_revisions_sort_number' => 'Revision Number', + 'pages_revisions_numbered' => 'Revision #:id', + 'pages_revisions_numbered_changes' => 'Revision #:id Changes', + 'pages_revisions_editor' => 'Editor Type', + 'pages_revisions_changelog' => 'Changelog', + 'pages_revisions_changes' => 'Changes', + 'pages_revisions_current' => 'Current Version', + 'pages_revisions_preview' => 'Preview', + 'pages_revisions_restore' => 'Restore', + 'pages_revisions_none' => 'This page has no revisions', + 'pages_copy_link' => 'Copy Link', + 'pages_edit_content_link' => 'Jump to section in editor', + 'pages_pointer_enter_mode' => 'Enter section select mode', + 'pages_pointer_label' => 'Page Section Options', + 'pages_pointer_permalink' => 'Page Section Permalink', + 'pages_pointer_include_tag' => 'Page Section Include Tag', + 'pages_pointer_toggle_link' => 'Permalink mode, Press to show include tag', + 'pages_pointer_toggle_include' => 'Include tag mode, Press to show permalink', + 'pages_permissions_active' => 'Page Permissions Active', + 'pages_initial_revision' => 'Initial publish', + 'pages_references_update_revision' => 'System auto-update of internal links', + 'pages_initial_name' => 'New Page', + 'pages_editing_draft_notification' => 'You are currently editing a draft that was last saved :timeDiff.', + 'pages_draft_edited_notification' => 'This page has been updated by since that time. It is recommended that you discard this draft.', + 'pages_draft_page_changed_since_creation' => 'This page has been updated since this draft was created. It is recommended that you discard this draft or take care not to overwrite any page changes.', + 'pages_draft_edit_active' => [ + 'start_a' => ':count users have started editing this page', + 'start_b' => ':userName has started editing this page', + 'time_a' => 'since the page was last updated', + 'time_b' => 'in the last :minCount minutes', + 'message' => ':start :time. Take care not to overwrite each other\'s updates!', + ], + 'pages_draft_discarded' => 'Draft discarded! The editor has been updated with the current page content', + 'pages_draft_deleted' => 'Draft deleted! The editor has been updated with the current page content', + 'pages_specific' => 'Specific Page', + 'pages_is_template' => 'Page Template', + + // Editor Sidebar + 'toggle_sidebar' => 'Toggle Sidebar', + 'page_tags' => 'Page Tags', + 'chapter_tags' => 'Chapter Tags', + 'book_tags' => 'Book Tags', + 'shelf_tags' => 'Shelf Tags', + 'tag' => 'Tag', + 'tags' => 'Tags', + 'tags_index_desc' => 'Tags can be applied to content within the system to apply a flexible form of categorization. Tags can have both a key and value, with the value being optional. Once applied, content can then be queried using the tag name and value.', + 'tag_name' => 'Tag Name', + 'tag_value' => 'Tag Value (Optional)', + 'tags_explain' => "Add some tags to better categorise your content. \n You can assign a value to a tag for more in-depth organisation.", + 'tags_add' => 'Add another tag', + 'tags_remove' => 'Remove this tag', + 'tags_usages' => 'Total tag usages', + 'tags_assigned_pages' => 'Assigned to Pages', + 'tags_assigned_chapters' => 'Assigned to Chapters', + 'tags_assigned_books' => 'Assigned to Books', + 'tags_assigned_shelves' => 'Assigned to Shelves', + 'tags_x_unique_values' => ':count unique values', + 'tags_all_values' => 'All values', + 'tags_view_tags' => 'View Tags', + 'tags_view_existing_tags' => 'View existing tags', + 'tags_list_empty_hint' => 'Tags can be assigned via the page editor sidebar or while editing the details of a book, chapter or shelf.', + 'attachments' => 'Attachments', + 'attachments_explain' => 'Upload some files or attach some links to display on your page. These are visible in the page sidebar.', + 'attachments_explain_instant_save' => 'Changes here are saved instantly.', + 'attachments_upload' => 'Upload File', + 'attachments_link' => 'Attach Link', + 'attachments_upload_drop' => 'Alternatively you can drag and drop a file here to upload it as an attachment.', + 'attachments_set_link' => 'Set Link', + 'attachments_delete' => 'Are you sure you want to delete this attachment?', + 'attachments_dropzone' => 'Drop files here to upload', + 'attachments_no_files' => 'No files have been uploaded', + 'attachments_explain_link' => 'You can attach a link if you\'d prefer not to upload a file. This can be a link to another page or a link to a file in the cloud.', + 'attachments_link_name' => 'Link Name', + 'attachment_link' => 'Attachment link', + 'attachments_link_url' => 'Link to file', + 'attachments_link_url_hint' => 'Url of site or file', + 'attach' => 'Attach', + 'attachments_insert_link' => 'Add Attachment Link to Page', + 'attachments_edit_file' => 'Edit File', + 'attachments_edit_file_name' => 'File Name', + 'attachments_edit_drop_upload' => 'Drop files or click here to upload and overwrite', + 'attachments_order_updated' => 'Attachment order updated', + 'attachments_updated_success' => 'Attachment details updated', + 'attachments_deleted' => 'Attachment deleted', + 'attachments_file_uploaded' => 'File successfully uploaded', + 'attachments_file_updated' => 'File successfully updated', + 'attachments_link_attached' => 'Link successfully attached to page', + 'templates' => 'Templates', + 'templates_set_as_template' => 'Page is a template', + 'templates_explain_set_as_template' => 'You can set this page as a template so its contents be utilized when creating other pages. Other users will be able to use this template if they have view permissions for this page.', + 'templates_replace_content' => 'Replace page content', + 'templates_append_content' => 'Append to page content', + 'templates_prepend_content' => 'Prepend to page content', + + // Profile View + 'profile_user_for_x' => 'User for :time', + 'profile_created_content' => 'Created Content', + 'profile_not_created_pages' => ':userName has not created any pages', + 'profile_not_created_chapters' => ':userName has not created any chapters', + 'profile_not_created_books' => ':userName has not created any books', + 'profile_not_created_shelves' => ':userName has not created any shelves', + + // Comments + 'comment' => 'Comment', + 'comments' => 'Comments', + 'comment_add' => 'Add Comment', + 'comment_none' => 'No comments to display', + 'comment_placeholder' => 'Leave a comment here', + 'comment_thread_count' => ':count Comment Thread|:count Comment Threads', + 'comment_archived_count' => ':count Archived', + 'comment_archived_threads' => 'Archived Threads', + 'comment_save' => 'Save Comment', + 'comment_new' => 'New Comment', + 'comment_created' => 'commented :createDiff', + 'comment_updated' => 'Updated :updateDiff by :username', + 'comment_updated_indicator' => 'Updated', + 'comment_deleted_success' => 'Comment deleted', + 'comment_created_success' => 'Comment added', + 'comment_updated_success' => 'Comment updated', + 'comment_archive_success' => 'Comment archived', + 'comment_unarchive_success' => 'Comment un-archived', + 'comment_view' => 'View comment', + 'comment_jump_to_thread' => 'Jump to thread', + 'comment_delete_confirm' => 'Are you sure you want to delete this comment?', + 'comment_in_reply_to' => 'In reply to :commentId', + 'comment_reference' => 'Reference', + 'comment_reference_outdated' => '(Outdated)', + 'comment_editor_explain' => 'Here are the comments that have been left on this page. Comments can be added & managed when viewing the saved page.', + + // Revision + 'revision_delete_confirm' => 'Are you sure you want to delete this revision?', + 'revision_restore_confirm' => 'Are you sure you want to restore this revision? The current page contents will be replaced.', + 'revision_cannot_delete_latest' => 'Cannot delete the latest revision.', + + // Copy view + 'copy_consider' => 'Please consider the below when copying content.', + 'copy_consider_permissions' => 'Custom permission settings will not be copied.', + 'copy_consider_owner' => 'You will become the owner of all copied content.', + 'copy_consider_images' => 'Page image files will not be duplicated & the original images will retain their relation to the page they were originally uploaded to.', + 'copy_consider_attachments' => 'Page attachments will not be copied.', + 'copy_consider_access' => 'A change of location, owner or permissions may result in this content being accessible to those previously without access.', + + // Conversions + 'convert_to_shelf' => 'Convert to Shelf', + 'convert_to_shelf_contents_desc' => 'You can convert this book to a new shelf with the same contents. Chapters contained within this book will be converted to new books. If this book contains any pages, that are not in a chapter, this book will be renamed and contain such pages, and this book will become part of the new shelf.', + 'convert_to_shelf_permissions_desc' => 'Any permissions set on this book will be copied to the new shelf and to all new child books that don\'t have their own permissions enforced. Note that permissions on shelves do not auto-cascade to content within, as they do for books.', + 'convert_book' => 'Convert Book', + 'convert_book_confirm' => 'Are you sure you want to convert this book?', + 'convert_undo_warning' => 'This cannot be as easily undone.', + 'convert_to_book' => 'Convert to Book', + 'convert_to_book_desc' => 'You can convert this chapter to a new book with the same contents. Any permissions set on this chapter will be copied to the new book but any inherited permissions, from the parent book, will not be copied which could lead to a change of access control.', + 'convert_chapter' => 'Convert Chapter', + 'convert_chapter_confirm' => 'Are you sure you want to convert this chapter?', + + // References + 'references' => 'References', + 'references_none' => 'There are no tracked references to this item.', + 'references_to_desc' => 'Listed below is all the known content in the system that links to this item.', + + // Watch Options + 'watch' => 'Watch', + 'watch_title_default' => 'Default Preferences', + 'watch_desc_default' => 'Revert watching to just your default notification preferences.', + 'watch_title_ignore' => 'Ignore', + 'watch_desc_ignore' => 'Ignore all notifications, including those from user-level preferences.', + 'watch_title_new' => 'New Pages', + 'watch_desc_new' => 'Notify when any new page is created within this item.', + 'watch_title_updates' => 'All Page Updates', + 'watch_desc_updates' => 'Notify upon all new pages and page changes.', + 'watch_desc_updates_page' => 'Notify upon all page changes.', + 'watch_title_comments' => 'All Page Updates & Comments', + 'watch_desc_comments' => 'Notify upon all new pages, page changes and new comments.', + 'watch_desc_comments_page' => 'Notify upon page changes and new comments.', + 'watch_change_default' => 'Change default notification preferences', + 'watch_detail_ignore' => 'Ignoring notifications', + 'watch_detail_new' => 'Watching for new pages', + 'watch_detail_updates' => 'Watching new pages and updates', + 'watch_detail_comments' => 'Watching new pages, updates & comments', + 'watch_detail_parent_book' => 'Watching via parent book', + 'watch_detail_parent_book_ignore' => 'Ignoring via parent book', + 'watch_detail_parent_chapter' => 'Watching via parent chapter', + 'watch_detail_parent_chapter_ignore' => 'Ignoring via parent chapter', +]; diff --git a/lang/sr/errors.php b/lang/sr/errors.php new file mode 100644 index 00000000000..ee8443461c3 --- /dev/null +++ b/lang/sr/errors.php @@ -0,0 +1,133 @@ + 'Немате дозволу да приступите овој страни.', + 'permissionJson' => 'Немате овлашћење да извршите ову акцију.', + + // Auth + 'error_user_exists_different_creds' => 'Корисник са е-мејл адресом :email већ постоји са другим приступним подацима.', + 'auth_pre_register_theme_prevention' => 'User account could not be registered for the provided details', + 'email_already_confirmed' => 'Email has already been confirmed, Try logging in.', + 'email_confirmation_invalid' => 'This confirmation token is not valid or has already been used, Please try registering again.', + 'email_confirmation_expired' => 'The confirmation token has expired, A new confirmation email has been sent.', + 'email_confirmation_awaiting' => 'The email address for the account in use needs to be confirmed', + 'ldap_fail_anonymous' => 'LDAP access failed using anonymous bind', + 'ldap_fail_authed' => 'LDAP access failed using given dn & password details', + 'ldap_extension_not_installed' => 'LDAP PHP extension not installed', + 'ldap_cannot_connect' => 'Cannot connect to ldap server, Initial connection failed', + 'saml_already_logged_in' => 'Already logged in', + 'saml_no_email_address' => 'Could not find an email address, for this user, in the data provided by the external authentication system', + 'saml_invalid_response_id' => 'The request from the external authentication system is not recognised by a process started by this application. Navigating back after a login could cause this issue.', + 'saml_fail_authed' => 'Login using :system failed, system did not provide successful authorization', + 'oidc_already_logged_in' => 'Already logged in', + 'oidc_no_email_address' => 'Could not find an email address, for this user, in the data provided by the external authentication system', + 'oidc_fail_authed' => 'Login using :system failed, system did not provide successful authorization', + 'social_no_action_defined' => 'No action defined', + 'social_login_bad_response' => "Error received during :socialAccount login: \n:error", + 'social_account_in_use' => 'This :socialAccount account is already in use, Try logging in via the :socialAccount option.', + 'social_account_email_in_use' => 'The email :email is already in use. If you already have an account you can connect your :socialAccount account from your profile settings.', + 'social_account_existing' => 'This :socialAccount is already attached to your profile.', + 'social_account_already_used_existing' => 'This :socialAccount account is already used by another user.', + 'social_account_not_used' => 'This :socialAccount account is not linked to any users. Please attach it in your profile settings. ', + 'social_account_register_instructions' => 'If you do not yet have an account, You can register an account using the :socialAccount option.', + 'social_driver_not_found' => 'Social driver not found', + 'social_driver_not_configured' => 'Your :socialAccount social settings are not configured correctly.', + 'invite_token_expired' => 'This invitation link has expired. You can instead try to reset your account password.', + 'login_user_not_found' => 'A user for this action could not be found.', + + // System + 'path_not_writable' => 'File path :filePath could not be uploaded to. Ensure it is writable to the server.', + 'cannot_get_image_from_url' => 'Cannot get image from :url', + 'cannot_create_thumbs' => 'The server cannot create thumbnails. Please check you have the GD PHP extension installed.', + 'server_upload_limit' => 'The server does not allow uploads of this size. Please try a smaller file size.', + 'server_post_limit' => 'The server cannot receive the provided amount of data. Try again with less data or a smaller file.', + 'uploaded' => 'The server does not allow uploads of this size. Please try a smaller file size.', + + // Drawing & Images + 'image_upload_error' => 'An error occurred uploading the image', + 'image_upload_type_error' => 'The image type being uploaded is invalid', + 'image_upload_replace_type' => 'Image file replacements must be of the same type', + 'image_upload_memory_limit' => 'Failed to handle image upload and/or create thumbnails due to system resource limits.', + 'image_thumbnail_memory_limit' => 'Failed to create image size variations due to system resource limits.', + 'image_gallery_thumbnail_memory_limit' => 'Failed to create gallery thumbnails due to system resource limits.', + 'drawing_data_not_found' => 'Drawing data could not be loaded. The drawing file might no longer exist or you may not have permission to access it.', + + // Attachments + 'attachment_not_found' => 'Attachment not found', + 'attachment_upload_error' => 'An error occurred uploading the attachment file', + + // Pages + 'page_draft_autosave_fail' => 'Failed to save draft. Ensure you have internet connection before saving this page', + 'page_draft_delete_fail' => 'Failed to delete page draft and fetch current page saved content', + 'page_custom_home_deletion' => 'Cannot delete a page while it is set as a homepage', + + // Entities + 'entity_not_found' => 'Entity not found', + 'bookshelf_not_found' => 'Shelf not found', + 'book_not_found' => 'Књига није пронађена', + 'page_not_found' => 'Страница није пронађена', + 'chapter_not_found' => 'Поглавље није пронађено', + 'selected_book_not_found' => 'Одабрана књига није пронађена', + 'selected_book_chapter_not_found' => 'The selected Book or Chapter was not found', + 'guests_cannot_save_drafts' => 'Гости не могу сачувати нацрте', + + // Users + 'users_cannot_delete_only_admin' => 'Не можете обрисати јединог администратора', + 'users_cannot_delete_guest' => 'Не можете обрисати госта', + 'users_could_not_send_invite' => 'Could not create user since invite email failed to send', + + // Roles + 'role_cannot_be_edited' => 'Ова улога се не може мењати', + 'role_system_cannot_be_deleted' => 'Ово је системска улога и не може се мењати', + 'role_registration_default_cannot_delete' => 'This role cannot be deleted while set as the default registration role', + 'role_cannot_remove_only_admin' => 'This user is the only user assigned to the administrator role. Assign the administrator role to another user before attempting to remove it here.', + + // Comments + 'comment_list' => 'An error occurred while fetching the comments.', + 'cannot_add_comment_to_draft' => 'You cannot add comments to a draft.', + 'comment_add' => 'An error occurred while adding / updating the comment.', + 'comment_delete' => 'An error occurred while deleting the comment.', + 'empty_comment' => 'Cannot add an empty comment.', + + // Error pages + '404_page_not_found' => 'Page Not Found', + 'sorry_page_not_found' => 'Sorry, The page you were looking for could not be found.', + 'sorry_page_not_found_permission_warning' => 'If you expected this page to exist, you might not have permission to view it.', + 'image_not_found' => 'Image Not Found', + 'image_not_found_subtitle' => 'Sorry, The image file you were looking for could not be found.', + 'image_not_found_details' => 'If you expected this image to exist it might have been deleted.', + 'return_home' => 'Return to home', + 'error_occurred' => 'Догодила се грешка', + 'app_down' => ':appName is down right now', + 'back_soon' => 'It will be back up soon.', + + // Import + 'import_zip_cant_read' => 'Could not read ZIP file.', + 'import_zip_cant_decode_data' => 'Could not find and decode ZIP data.json content.', + 'import_zip_no_data' => 'ZIP file data has no expected book, chapter or page content.', + 'import_validation_failed' => 'Import ZIP failed to validate with errors:', + 'import_zip_failed_notification' => 'Failed to import ZIP file.', + 'import_perms_books' => 'You are lacking the required permissions to create books.', + 'import_perms_chapters' => 'You are lacking the required permissions to create chapters.', + 'import_perms_pages' => 'You are lacking the required permissions to create pages.', + 'import_perms_images' => 'You are lacking the required permissions to create images.', + 'import_perms_attachments' => 'You are lacking the required permission to create attachments.', + + // API errors + 'api_no_authorization_found' => 'No authorization token found on the request', + 'api_bad_authorization_format' => 'An authorization token was found on the request but the format appeared incorrect', + 'api_user_token_not_found' => 'No matching API token was found for the provided authorization token', + 'api_incorrect_token_secret' => 'The secret provided for the given used API token is incorrect', + 'api_user_no_api_permission' => 'The owner of the used API token does not have permission to make API calls', + 'api_user_token_expired' => 'The authorization token used has expired', + + // Settings & Maintenance + 'maintenance_test_email_failure' => 'Error thrown when sending a test email:', + + // HTTP errors + 'http_ssr_url_no_match' => 'The URL does not match the configured allowed SSR hosts', +]; diff --git a/lang/sr/notifications.php b/lang/sr/notifications.php new file mode 100644 index 00000000000..6aa3f2abbaa --- /dev/null +++ b/lang/sr/notifications.php @@ -0,0 +1,27 @@ + 'Нови коментар на станици: :pageName', + 'new_comment_intro' => 'Корисник је коментарисао на страници у :appName:', + 'new_page_subject' => 'Нова страница: :pageName', + 'new_page_intro' => 'Нова страница је креирана у :appName:', + 'updated_page_subject' => 'Ажурирана страница: :pageName', + 'updated_page_intro' => 'Страница је ажурирана у :appName:', + 'updated_page_debounce' => 'To prevent a mass of notifications, for a while you won\'t be sent notifications for further edits to this page by the same editor.', + + 'detail_page_name' => 'Назив странице:', + 'detail_page_path' => 'Путања странице:', + 'detail_commenter' => 'Commenter:', + 'detail_comment' => 'Коментар:', + 'detail_created_by' => 'Креирао/ла:', + 'detail_updated_by' => 'Отпремио/ла:', + + 'action_view_comment' => 'Погледај коментар', + 'action_view_page' => 'Погледај страницу', + + 'footer_reason' => 'This notification was sent to you because :link cover this type of activity for this item.', + 'footer_reason_link' => 'your notification preferences', +]; diff --git a/lang/sr/pagination.php b/lang/sr/pagination.php new file mode 100644 index 00000000000..85bd12fc319 --- /dev/null +++ b/lang/sr/pagination.php @@ -0,0 +1,12 @@ + '« Previous', + 'next' => 'Next »', + +]; diff --git a/lang/sr/passwords.php b/lang/sr/passwords.php new file mode 100644 index 00000000000..b408f3c2fda --- /dev/null +++ b/lang/sr/passwords.php @@ -0,0 +1,15 @@ + 'Passwords must be at least eight characters and match the confirmation.', + 'user' => "We can't find a user with that e-mail address.", + 'token' => 'The password reset token is invalid for this email address.', + 'sent' => 'We have e-mailed your password reset link!', + 'reset' => 'Your password has been reset!', + +]; diff --git a/lang/sr/preferences.php b/lang/sr/preferences.php new file mode 100644 index 00000000000..2872f5f3c65 --- /dev/null +++ b/lang/sr/preferences.php @@ -0,0 +1,51 @@ + 'My Account', + + 'shortcuts' => 'Shortcuts', + 'shortcuts_interface' => 'UI Shortcut Preferences', + 'shortcuts_toggle_desc' => 'Here you can enable or disable keyboard system interface shortcuts, used for navigation and actions.', + 'shortcuts_customize_desc' => 'You can customize each of the shortcuts below. Just press your desired key combination after selecting the input for a shortcut.', + 'shortcuts_toggle_label' => 'Keyboard shortcuts enabled', + 'shortcuts_section_navigation' => 'Navigation', + 'shortcuts_section_actions' => 'Common Actions', + 'shortcuts_save' => 'Save Shortcuts', + 'shortcuts_overlay_desc' => 'Note: When shortcuts are enabled a helper overlay is available via pressing "?" which will highlight the available shortcuts for actions currently visible on the screen.', + 'shortcuts_update_success' => 'Shortcut preferences have been updated!', + 'shortcuts_overview_desc' => 'Manage keyboard shortcuts you can use to navigate the system user interface.', + + 'notifications' => 'Notification Preferences', + 'notifications_desc' => 'Control the email notifications you receive when certain activity is performed within the system.', + 'notifications_opt_own_page_changes' => 'Notify upon changes to pages I own', + 'notifications_opt_own_page_comments' => 'Notify upon comments on pages I own', + 'notifications_opt_comment_replies' => 'Notify upon replies to my comments', + 'notifications_save' => 'Save Preferences', + 'notifications_update_success' => 'Notification preferences have been updated!', + 'notifications_watched' => 'Watched & Ignored Items', + 'notifications_watched_desc' => 'Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.', + + 'auth' => 'Access & Security', + 'auth_change_password' => 'Change Password', + 'auth_change_password_desc' => 'Change the password you use to log-in to the application. This must be at least 8 characters long.', + 'auth_change_password_success' => 'Password has been updated!', + + 'profile' => 'Profile Details', + 'profile_desc' => 'Manage the details of your account which represents you to other users, in addition to details that are used for communication and system personalisation.', + 'profile_view_public' => 'View Public Profile', + 'profile_name_desc' => 'Configure your display name which will be visible to other users in the system through the activity you perform, and content you own.', + 'profile_email_desc' => 'This email will be used for notifications and, depending on active system authentication, system access.', + 'profile_email_no_permission' => 'Unfortunately you don\'t have permission to change your email address. If you want to change this, you\'d need to ask an administrator to change this for you.', + 'profile_avatar_desc' => 'Select an image which will be used to represent yourself to others in the system. Ideally this image should be square and about 256px in width and height.', + 'profile_admin_options' => 'Administrator Options', + 'profile_admin_options_desc' => 'Additional administrator-level options, like those to manage role assignments, can be found for your user account in the "Settings > Users" area of the application.', + + 'delete_account' => 'Delete Account', + 'delete_my_account' => 'Delete My Account', + 'delete_my_account_desc' => 'This will fully delete your user account from the system. You will not be able to recover this account or revert this action. Content you\'ve created, such as created pages and uploaded images, will remain.', + 'delete_my_account_warning' => 'Are you sure you want to delete your account?', +]; diff --git a/lang/sr/settings.php b/lang/sr/settings.php new file mode 100644 index 00000000000..b7213e905b0 --- /dev/null +++ b/lang/sr/settings.php @@ -0,0 +1,370 @@ + 'Подешавања', + 'settings_save' => 'Сачувај подешавања', + 'system_version' => 'Верзија система', + 'categories' => 'Категорије', + + // App Settings + 'app_customization' => 'Прилгођавање', + 'app_features_security' => 'Својства и сигурност', + 'app_name' => 'Назив апликације', + 'app_name_desc' => 'Ово име се приказује у заглављу и у свим системским порукама е-поште.', + 'app_name_header' => 'Прикажи назив у заглављу', + 'app_public_access' => 'Javni pristup', + 'app_public_access_desc' => 'Омогућавање ове опције ће омогућити посетиоцима, који нису пријављени, да приступе садржају у вашој Боокстак инстанци.', + 'app_public_access_desc_guest' => 'Приступ за јавне посетиоце може се контролисати преко корисника „Гост“.', + 'app_public_access_toggle' => 'Дозволи јавни приступ', + 'app_public_viewing' => 'Дозволити јавно гледање?', + 'app_secure_images' => 'Веће безбедност отпремања слика', + 'app_secure_images_toggle' => 'Омогућите већу безбедност отпремања слика', + 'app_secure_images_desc' => 'Из разлога перформанси, све слике су јавне. Ова опција додаје насумичан низ који је тешко погодити испред Урл-ова слике. Уверите се да индекси директоријума нису омогућени да бисте спречили лак приступ.', + 'app_default_editor' => 'Подразумевани уређивач страница', + 'app_default_editor_desc' => 'Изаберите који уређивач ће се подразумевано користити приликом уређивања нових страница. Ово се може заменити на нивоу странице где дозволе дозвољавају.', + 'app_custom_html' => 'Прилагођени ХТМЛ садржај заглавља', + 'app_custom_html_desc' => 'Сваки садржај који је додат овде биће уметнут у дно одељка сваке странице. Ово је згодно за надјачавање стилова или додавање кода за анализу.', + 'app_custom_html_disabled_notice' => 'Прилагођени садржај заглавља ХТМЛ-а је онемогућен на овој страници са подешавањима како би се осигурало да се све неоправдане промене могу поништити.', + 'app_logo' => 'Логотип апликације', + 'app_logo_desc' => 'Ово се користи у траци заглавља апликације, између осталих области. Висина ове слике треба да буде 86 пиксела. Велике слике ће бити смањене.', + 'app_icon' => 'Икона апликације', + 'app_icon_desc' => 'Ова икона се користи за картице претраживача и иконе пречица. Ово би требало да буде квадратна ПНГ слика величине 256 пиксела.', + 'app_homepage' => 'Почетна страница апликације', + 'app_homepage_desc' => 'Изаберите приказ који ће се приказати на почетној страници уместо подразумеваног приказа. Дозволе за страницу се занемарују за изабране странице.', + 'app_homepage_select' => 'Изаберите страницу', + 'app_footer_links' => 'Везе у подножју', + 'app_footer_links_desc' => 'Додајте везе које ће се приказати у подножју сајта. Они ће бити приказани на дну већине страница, укључујући и оне које не захтевају пријаву. Можете користити ознаку "trans::" да бисте користили системски дефинисане преводе. На пример: Коришћење "trans::common.privacy_policy"ће обезбедити преведени текст „Политика приватности“, а "trans::common.terms_of_service" ће обезбедити преведени текст „Услови коришћења услуге“.', + 'app_footer_links_label' => 'Ознака линка', + 'app_footer_links_url' => 'УРЛ линка', + 'app_footer_links_add' => 'Додај везу у подножју', + 'app_disable_comments' => 'Онемогући коментаре', + 'app_disable_comments_toggle' => 'Онемогући коментаре', + 'app_disable_comments_desc' => 'Онемогућава коментаре на свим страницама у апликацији.
Постојећи коментари нису приказани.', + + // Color settings + 'color_scheme' => 'Шема боја апликације', + 'color_scheme_desc' => 'Подесите боје које ће се користити у корисничком интерфејсу апликације. Боје се могу засебно конфигурисати за тамне и светле режиме како би најбоље одговарале теми и осигурале читљивост.', + 'ui_colors_desc' => 'Подесите примарну боју апликације и подразумевану боју везе. Примарна боја се углавном користи за банер заглавља, дугмад и декорацију интерфејса. Подразумевана боја везе се користи за везе и радње засноване на тексту, како унутар писаног садржаја, тако и у интерфејсу апликације.', + 'app_color' => 'Примарна боја', + 'link_color' => 'Подразумевана боја везе', + 'content_colors_desc' => 'Подесите боје за све елементе у хијерархији организације страница. За читљивост се препоручује одабир боја сличне осветљености као и подразумеване боје.', + 'bookshelf_color' => 'Боја полице', + 'book_color' => 'Боја књиге', + 'chapter_color' => 'Боја поглавља', + 'page_color' => 'Боја странице', + 'page_draft_color' => 'Боја нацрта странице', + + // Registration Settings + 'reg_settings' => 'Регистрација', + 'reg_enable' => 'Дозволи регистрацију', + 'reg_enable_toggle' => 'Омогући регистрацију', + 'reg_enable_desc' => 'Када је регистрација омогућена, корисник ће моћи да се пријави као корисник апликације. Након регистрације добијају јединствену, подразумевану корисничку улогу.', + 'reg_default_role' => 'Подразумевана корисничка улога након регистрације', + 'reg_enable_external_warning' => 'Горња опција се занемарује док је активна екстерна ЛДАП или САМЛ аутентификација. Кориснички налози за непостојеће чланове биће аутоматски креирани ако је аутентификација, против спољашњег система који се користи, успешна.', + 'reg_email_confirmation' => 'Потврђивање е-поште', + 'reg_email_confirmation_toggle' => 'Захтевајте потврду е-поштом', + 'reg_confirm_email_desc' => 'Ако се користи ограничење домена, биће потребна потврда путем е-поште и ова опција ће бити занемарена.', + 'reg_confirm_restrict_domain' => 'Ограничени домени', + 'reg_confirm_restrict_domain_desc' => 'Унесите листу домена е-поште одвојене зарезима на које желите да ограничите регистрацију. Корисницима ће бити послата е-порука да потврде своју адресу пре него што им буде дозвољено да комуницирају са апликацијом.
Имајте на уму да ће корисници моћи да промене своје адресе е-поште након успешне регистрације.', + 'reg_confirm_restrict_domain_placeholder' => 'Нема постављених ограничења', + + // Sorting Settings + 'sorting' => 'Sorting', + 'sorting_book_default' => 'Default Book Sort', + 'sorting_book_default_desc' => 'Select the default sort rule to apply to new books. This won\'t affect existing books, and can be overridden per-book.', + 'sorting_rules' => 'Sort Rules', + 'sorting_rules_desc' => 'These are predefined sorting operations which can be applied to content in the system.', + 'sort_rule_assigned_to_x_books' => 'Assigned to :count Book|Assigned to :count Books', + 'sort_rule_create' => 'Create Sort Rule', + 'sort_rule_edit' => 'Edit Sort Rule', + 'sort_rule_delete' => 'Delete Sort Rule', + 'sort_rule_delete_desc' => 'Remove this sort rule from the system. Books using this sort will revert to manual sorting.', + 'sort_rule_delete_warn_books' => 'This sort rule is currently used on :count book(s). Are you sure you want to delete this?', + 'sort_rule_delete_warn_default' => 'This sort rule is currently used as the default for books. Are you sure you want to delete this?', + 'sort_rule_details' => 'Sort Rule Details', + 'sort_rule_details_desc' => 'Set a name for this sort rule, which will appear in lists when users are selecting a sort.', + 'sort_rule_operations' => 'Sort Operations', + 'sort_rule_operations_desc' => 'Configure the sort actions to be performed by moving them from the list of available operations. Upon use, the operations will be applied in order, from top to bottom. Any changes made here will be applied to all assigned books upon save.', + 'sort_rule_available_operations' => 'Available Operations', + 'sort_rule_available_operations_empty' => 'No operations remaining', + 'sort_rule_configured_operations' => 'Configured Operations', + 'sort_rule_configured_operations_empty' => 'Drag/add operations from the "Available Operations" list', + 'sort_rule_op_asc' => '(Asc)', + 'sort_rule_op_desc' => '(Desc)', + 'sort_rule_op_name' => 'Name - Alphabetical', + 'sort_rule_op_name_numeric' => '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_last' => 'Chapters Last', + + // Maintenance settings + 'maint' => 'Одржавање', + 'maint_image_cleanup' => 'Чишћење слика', + 'maint_image_cleanup_desc' => 'Скенира садржај странице и ревидира садржај да би проверио које слике и цртежи су тренутно у употреби и које су слике сувишне. Уверите се да сте направили пуну базу података и резервну копију слике пре него што ово покренете.', + 'maint_delete_images_only_in_revisions' => 'Такође избришите слике које постоје само у старим ревизијама странице', + 'maint_image_cleanup_run' => 'Покрени чишћење', + 'maint_image_cleanup_warning' => ':count пронађене су потенцијално неискоришћене слике. Да ли сте сигурни да желите да избришете ове слике?', + 'maint_image_cleanup_success' => ':count potentially unused images found and deleted!', + 'maint_image_cleanup_nothing_found' => 'No unused images found, Nothing deleted!', + 'maint_send_test_email' => 'Send a Test Email', + 'maint_send_test_email_desc' => 'This sends a test email to your email address specified in your profile.', + 'maint_send_test_email_run' => 'Send test email', + 'maint_send_test_email_success' => 'Email sent to :address', + 'maint_send_test_email_mail_subject' => 'Test Email', + 'maint_send_test_email_mail_greeting' => 'Email delivery seems to work!', + 'maint_send_test_email_mail_text' => 'Congratulations! As you received this email notification, your email settings seem to be configured properly.', + 'maint_recycle_bin_desc' => 'Deleted shelves, books, chapters & pages are sent to the recycle bin so they can be restored or permanently deleted. Older items in the recycle bin may be automatically removed after a while depending on system configuration.', + 'maint_recycle_bin_open' => 'Open Recycle Bin', + 'maint_regen_references' => 'Regenerate References', + 'maint_regen_references_desc' => 'This action will rebuild the cross-item reference index within the database. This is usually handled automatically but this action can be useful to index old content or content added via unofficial methods.', + 'maint_regen_references_success' => 'Reference index has been regenerated!', + 'maint_timeout_command_note' => 'Note: This action can take time to run, which can lead to timeout issues in some web environments. As an alternative, this action be performed using a terminal command.', + + // Recycle Bin + 'recycle_bin' => 'Recycle Bin', + 'recycle_bin_desc' => 'Here you can restore items that have been deleted or choose to permanently remove them from the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.', + 'recycle_bin_deleted_item' => 'Deleted Item', + 'recycle_bin_deleted_parent' => 'Parent', + 'recycle_bin_deleted_by' => 'Deleted By', + 'recycle_bin_deleted_at' => 'Deletion Time', + 'recycle_bin_permanently_delete' => 'Permanently Delete', + 'recycle_bin_restore' => 'Restore', + 'recycle_bin_contents_empty' => 'The recycle bin is currently empty', + 'recycle_bin_empty' => 'Empty Recycle Bin', + 'recycle_bin_empty_confirm' => 'This will permanently destroy all items in the recycle bin including content contained within each item. Are you sure you want to empty the recycle bin?', + 'recycle_bin_destroy_confirm' => 'This action will permanently delete this item from the system, along with any child elements listed below, and you will not be able to restore this content. Are you sure you want to permanently delete this item?', + 'recycle_bin_destroy_list' => 'Items to be Destroyed', + 'recycle_bin_restore_list' => 'Items to be Restored', + 'recycle_bin_restore_confirm' => 'This action will restore the deleted item, including any child elements, to their original location. If the original location has since been deleted, and is now in the recycle bin, the parent item will also need to be restored.', + 'recycle_bin_restore_deleted_parent' => 'The parent of this item has also been deleted. These will remain deleted until that parent is also restored.', + 'recycle_bin_restore_parent' => 'Restore Parent', + 'recycle_bin_destroy_notification' => 'Deleted :count total items from the recycle bin.', + 'recycle_bin_restore_notification' => 'Restored :count total items from the recycle bin.', + + // Audit Log + 'audit' => 'Audit Log', + 'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.', + 'audit_event_filter' => 'Event Filter', + 'audit_event_filter_no_filter' => 'No Filter', + 'audit_deleted_item' => 'Избрисана ставка', + 'audit_deleted_item_name' => 'Name: :name', + 'audit_table_user' => 'Корисник', + 'audit_table_event' => 'Догађај', + 'audit_table_related' => 'Related Item or Detail', + 'audit_table_ip' => 'ИП адреса', + 'audit_table_date' => 'Датум активности', + 'audit_date_from' => 'Date Range From', + 'audit_date_to' => 'Date Range To', + + // Role Settings + 'roles' => 'Улоге', + 'role_user_roles' => 'User Roles', + 'roles_index_desc' => 'Roles are used to group users & provide system permission to their members. When a user is a member of multiple roles the privileges granted will stack and the user will inherit all abilities.', + 'roles_x_users_assigned' => ':count user assigned|:count users assigned', + 'roles_x_permissions_provided' => ':count permission|:count permissions', + 'roles_assigned_users' => 'Assigned Users', + 'roles_permissions_provided' => 'Provided Permissions', + 'role_create' => 'Create New Role', + 'role_delete' => 'Delete Role', + 'role_delete_confirm' => 'Ово ће избрисати улогу са именом \':roleName\'.', + 'role_delete_users_assigned' => 'Ова улога има :userCount корисника који су јој додељени. Ако желите да мигрирате кориснике са ове улоге, изаберите нову улогу испод.', + 'role_delete_no_migration' => "Немојте мигрирати кориснике", + 'role_delete_sure' => 'Да ли сте сигурни да желите да избришете ову улогу?', + 'role_edit' => 'Уреди улогу', + 'role_details' => 'Детаљи улоге', + 'role_name' => 'Назив улоге', + 'role_desc' => 'Кратак опис улоге', + 'role_mfa_enforced' => 'Захтева вишефакторску аутентификацију', + 'role_external_auth_id' => 'External Authentication IDs', + 'role_system' => 'System Permissions', + 'role_manage_users' => 'Manage users', + 'role_manage_roles' => 'Manage roles & role permissions', + 'role_manage_entity_permissions' => 'Manage all book, chapter & page permissions', + 'role_manage_own_entity_permissions' => 'Manage permissions on own book, chapter & pages', + 'role_manage_page_templates' => 'Manage page templates', + 'role_access_api' => 'Access system API', + 'role_manage_settings' => 'Manage app settings', + 'role_export_content' => 'Export content', + 'role_import_content' => 'Import content', + 'role_editor_change' => 'Change page editor', + 'role_notifications' => 'Receive & manage notifications', + 'role_asset' => 'Asset Permissions', + 'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.', + 'role_asset_desc' => 'These permissions control default access to the assets within the system. Permissions on Books, Chapters and Pages will override these permissions.', + 'role_asset_admins' => 'Admins are automatically given access to all content but these options may show or hide UI options.', + 'role_asset_image_view_note' => 'This relates to visibility within the image manager. Actual access of uploaded image files will be dependant upon system image storage option.', + 'role_all' => 'All', + 'role_own' => 'Own', + 'role_controlled_by_asset' => 'Controlled by the asset they are uploaded to', + 'role_save' => 'Save Role', + 'role_users' => 'Users in this role', + 'role_users_none' => 'No users are currently assigned to this role', + + // Users + 'users' => 'Users', + 'users_index_desc' => 'Create & manage individual user accounts within the system. User accounts are used for login and attribution of content & activity. Access permissions are primarily role-based but user content ownership, among other factors, may also affect permissions & access.', + 'user_profile' => 'User Profile', + 'users_add_new' => 'Add New User', + 'users_search' => 'Search Users', + 'users_latest_activity' => 'Latest Activity', + 'users_details' => 'User Details', + 'users_details_desc' => 'Set a display name and an email address for this user. The email address will be used for logging into the application.', + 'users_details_desc_no_email' => 'Set a display name for this user so others can recognise them.', + 'users_role' => 'User Roles', + 'users_role_desc' => 'Select which roles this user will be assigned to. If a user is assigned to multiple roles the permissions from those roles will stack and they will receive all abilities of the assigned roles.', + 'users_password' => 'User Password', + 'users_password_desc' => 'Set a password used to log-in to the application. This must be at least 8 characters long.', + 'users_send_invite_text' => 'You can choose to send this user an invitation email which allows them to set their own password otherwise you can set their password yourself.', + 'users_send_invite_option' => 'Send user invite email', + 'users_external_auth_id' => 'External Authentication ID', + 'users_external_auth_id_desc' => 'When an external authentication system is in use (such as SAML2, OIDC or LDAP) this is the ID which links this BookStack user to the authentication system account. You can ignore this field if using the default email-based authentication.', + 'users_password_warning' => 'Only fill the below if you would like to change the password for this user.', + 'users_system_public' => 'This user represents any guest users that visit your instance. It cannot be used to log in but is assigned automatically.', + 'users_delete' => 'Delete User', + 'users_delete_named' => 'Delete user :userName', + 'users_delete_warning' => 'This will fully delete this user with the name \':userName\' from the system.', + 'users_delete_confirm' => 'Are you sure you want to delete this user?', + 'users_migrate_ownership' => 'Migrate Ownership', + 'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.', + 'users_none_selected' => 'No user selected', + 'users_edit' => 'Edit User', + 'users_edit_profile' => 'Edit Profile', + 'users_avatar' => 'User Avatar', + 'users_avatar_desc' => 'Select an image to represent this user. This should be approx 256px square.', + 'users_preferred_language' => 'Preferred Language', + 'users_preferred_language_desc' => 'This option will change the language used for the user-interface of the application. This will not affect any user-created content.', + 'users_social_accounts' => 'Social Accounts', + 'users_social_accounts_desc' => 'View the status of the connected social accounts for this user. Social accounts can be used in addition to the primary authentication system for system access.', + 'users_social_accounts_info' => 'Here you can connect your other accounts for quicker and easier login. Disconnecting an account here does not revoke previously authorized access. Revoke access from your profile settings on the connected social account.', + 'users_social_connect' => 'Connect Account', + 'users_social_disconnect' => 'Disconnect Account', + 'users_social_status_connected' => 'Connected', + 'users_social_status_disconnected' => 'Disconnected', + 'users_social_connected' => ':socialAccount account was successfully attached to your profile.', + 'users_social_disconnected' => ':socialAccount account was successfully disconnected from your profile.', + 'users_api_tokens' => 'API Tokens', + 'users_api_tokens_desc' => 'Create and manage the access tokens used to authenticate with the BookStack REST API. Permissions for the API are managed via the user that the token belongs to.', + 'users_api_tokens_none' => 'No API tokens have been created for this user', + 'users_api_tokens_create' => 'Create Token', + 'users_api_tokens_expires' => 'Expires', + 'users_api_tokens_docs' => 'API Documentation', + 'users_mfa' => 'Multi-Factor Authentication', + 'users_mfa_desc' => 'Setup multi-factor authentication as an extra layer of security for your user account.', + 'users_mfa_x_methods' => ':count method configured|:count methods configured', + 'users_mfa_configure' => 'Configure Methods', + + // API Tokens + 'user_api_token_create' => 'Create API Token', + 'user_api_token_name' => 'Name', + 'user_api_token_name_desc' => 'Give your token a readable name as a future reminder of its intended purpose.', + 'user_api_token_expiry' => 'Expiry Date', + 'user_api_token_expiry_desc' => 'Set a date at which this token expires. After this date, requests made using this token will no longer work. Leaving this field blank will set an expiry 100 years into the future.', + 'user_api_token_create_secret_message' => 'Immediately after creating this token a "Token ID" & "Token Secret" will be generated and displayed. The secret will only be shown a single time so be sure to copy the value to somewhere safe and secure before proceeding.', + 'user_api_token' => 'API Token', + 'user_api_token_id' => 'Token ID', + 'user_api_token_id_desc' => 'This is a non-editable system generated identifier for this token which will need to be provided in API requests.', + 'user_api_token_secret' => 'Token Secret', + 'user_api_token_secret_desc' => 'This is a system generated secret for this token which will need to be provided in API requests. This will only be displayed this one time so copy this value to somewhere safe and secure.', + 'user_api_token_created' => 'Token created :timeAgo', + 'user_api_token_updated' => 'Token updated :timeAgo', + 'user_api_token_delete' => 'Delete Token', + 'user_api_token_delete_warning' => 'This will fully delete this API token with the name \':tokenName\' from the system.', + 'user_api_token_delete_confirm' => 'Are you sure you want to delete this API token?', + + // Webhooks + 'webhooks' => 'Webhooks', + 'webhooks_index_desc' => 'Webhooks are a way to send data to external URLs when certain actions and events occur within the system which allows event-based integration with external platforms such as messaging or notification systems.', + 'webhooks_x_trigger_events' => ':count trigger event|:count trigger events', + 'webhooks_create' => 'Create New Webhook', + 'webhooks_none_created' => 'No webhooks have yet been created.', + 'webhooks_edit' => 'Edit Webhook', + 'webhooks_save' => 'Save Webhook', + 'webhooks_details' => 'Webhook Details', + 'webhooks_details_desc' => 'Provide a user friendly name and a POST endpoint as a location for the webhook data to be sent to.', + 'webhooks_events' => 'Webhook Events', + 'webhooks_events_desc' => 'Select all the events that should trigger this webhook to be called.', + 'webhooks_events_warning' => 'Keep in mind that these events will be triggered for all selected events, even if custom permissions are applied. Ensure that use of this webhook won\'t expose confidential content.', + 'webhooks_events_all' => 'All system events', + 'webhooks_name' => 'Webhook Name', + 'webhooks_timeout' => 'Webhook Request Timeout (Seconds)', + 'webhooks_endpoint' => 'Webhook Endpoint', + 'webhooks_active' => 'Webhook Active', + 'webhook_events_table_header' => 'Events', + 'webhooks_delete' => 'Delete Webhook', + 'webhooks_delete_warning' => 'This will fully delete this webhook, with the name \':webhookName\', from the system.', + 'webhooks_delete_confirm' => 'Are you sure you want to delete this webhook?', + 'webhooks_format_example' => 'Webhook Format Example', + 'webhooks_format_example_desc' => 'Webhook data is sent as a POST request to the configured endpoint as JSON following the format below. The "related_item" and "url" properties are optional and will depend on the type of event triggered.', + 'webhooks_status' => 'Webhook Status', + 'webhooks_last_called' => 'Last Called:', + 'webhooks_last_errored' => 'Last Errored:', + 'webhooks_last_error_message' => 'Last Error Message:', + + // Licensing + 'licenses' => 'Licenses', + 'licenses_desc' => 'This page details license information for BookStack in addition to the projects & libraries that are used within BookStack. Many projects listed may only be used in a development context.', + 'licenses_bookstack' => 'BookStack License', + 'licenses_php' => 'PHP Library Licenses', + 'licenses_js' => 'JavaScript Library Licenses', + 'licenses_other' => 'Other Licenses', + 'license_details' => 'License Details', + + //! If editing translations files directly please ignore this in all + //! languages apart from en. Content will be auto-copied from en. + //!//////////////////////////////// + 'language_select' => [ + 'en' => 'English', + 'ar' => 'العربية', + 'bg' => 'Bǎlgarski', + 'bs' => 'Bosanski', + 'ca' => 'Català', + 'cs' => 'Česky', + 'cy' => 'Cymraeg', + 'da' => 'Dansk', + 'de' => 'Deutsch (Sie)', + 'de_informal' => 'Deutsch (Du)', + 'el' => 'ελληνικά', + 'es' => 'Español', + 'es_AR' => 'Español Argentina', + 'et' => 'Eesti keel', + 'eu' => 'Euskara', + 'fa' => 'فارسی', + 'fi' => 'Suomi', + 'fr' => 'Français', + 'he' => 'עברית', + 'hr' => 'Hrvatski', + 'hu' => 'Magyar', + 'id' => 'Bahasa Indonesia', + 'it' => 'Italian', + 'ja' => '日本語', + 'ko' => '한국어', + 'lt' => 'Lietuvių Kalba', + 'lv' => 'Latviešu Valoda', + 'nb' => 'Norsk (Bokmål)', + 'ne' => 'नेपाली', + 'nn' => 'Nynorsk', + 'nl' => 'Nederlands', + 'pl' => 'Polski', + 'pt' => 'Português', + 'pt_BR' => 'Português do Brasil', + 'ro' => 'Română', + 'ru' => 'Русский', + 'sk' => 'Slovensky', + 'sl' => 'Slovenščina', + 'sv' => 'Svenska', + 'tr' => 'Türkçe', + 'uk' => 'Українська', + 'uz' => 'O‘zbekcha', + 'vi' => 'Tiếng Việt', + 'zh_CN' => '简体中文', + 'zh_TW' => '繁體中文', + ], + //!//////////////////////////////// +]; diff --git a/lang/sr/validation.php b/lang/sr/validation.php new file mode 100644 index 00000000000..d9b982d1e23 --- /dev/null +++ b/lang/sr/validation.php @@ -0,0 +1,122 @@ + 'The :attribute must be accepted.', + 'active_url' => 'The :attribute is not a valid URL.', + 'after' => 'The :attribute must be a date after :date.', + 'alpha' => 'The :attribute may only contain letters.', + 'alpha_dash' => 'The :attribute may only contain letters, numbers, dashes and underscores.', + 'alpha_num' => 'The :attribute may only contain letters and numbers.', + 'array' => 'The :attribute must be an array.', + 'backup_codes' => 'The provided code is not valid or has already been used.', + 'before' => 'The :attribute must be a date before :date.', + 'between' => [ + 'numeric' => 'The :attribute must be between :min and :max.', + 'file' => 'The :attribute must be between :min and :max kilobytes.', + 'string' => 'The :attribute must be between :min and :max characters.', + 'array' => 'The :attribute must have between :min and :max items.', + ], + 'boolean' => 'The :attribute field must be true or false.', + 'confirmed' => 'The :attribute confirmation does not match.', + 'date' => 'The :attribute is not a valid date.', + 'date_format' => 'The :attribute does not match the format :format.', + 'different' => 'The :attribute and :other must be different.', + 'digits' => 'The :attribute must be :digits digits.', + 'digits_between' => 'The :attribute must be between :min and :max digits.', + 'email' => 'The :attribute must be a valid email address.', + 'ends_with' => 'The :attribute must end with one of the following: :values', + 'file' => 'The :attribute must be provided as a valid file.', + 'filled' => 'The :attribute field is required.', + 'gt' => [ + 'numeric' => 'The :attribute must be greater than :value.', + 'file' => 'The :attribute must be greater than :value kilobytes.', + 'string' => 'The :attribute must be greater than :value characters.', + 'array' => 'The :attribute must have more than :value items.', + ], + 'gte' => [ + 'numeric' => 'The :attribute must be greater than or equal :value.', + 'file' => 'The :attribute must be greater than or equal :value kilobytes.', + 'string' => 'The :attribute must be greater than or equal :value characters.', + 'array' => 'The :attribute must have :value items or more.', + ], + 'exists' => 'The selected :attribute is invalid.', + 'image' => 'The :attribute must be an image.', + 'image_extension' => 'The :attribute must have a valid & supported image extension.', + 'in' => 'The selected :attribute is invalid.', + 'integer' => 'The :attribute must be an integer.', + 'ip' => 'The :attribute must be a valid IP address.', + 'ipv4' => 'The :attribute must be a valid IPv4 address.', + 'ipv6' => 'The :attribute must be a valid IPv6 address.', + 'json' => 'The :attribute must be a valid JSON string.', + 'lt' => [ + 'numeric' => 'The :attribute must be less than :value.', + 'file' => 'The :attribute must be less than :value kilobytes.', + 'string' => 'The :attribute must be less than :value characters.', + 'array' => 'The :attribute must have less than :value items.', + ], + 'lte' => [ + 'numeric' => 'The :attribute must be less than or equal :value.', + 'file' => 'The :attribute must be less than or equal :value kilobytes.', + 'string' => 'The :attribute must be less than or equal :value characters.', + 'array' => 'The :attribute must not have more than :value items.', + ], + 'max' => [ + 'numeric' => 'The :attribute may not be greater than :max.', + 'file' => 'The :attribute may not be greater than :max kilobytes.', + 'string' => 'The :attribute may not be greater than :max characters.', + 'array' => 'The :attribute may not have more than :max items.', + ], + 'mimes' => 'The :attribute must be a file of type: :values.', + 'min' => [ + 'numeric' => 'The :attribute must be at least :min.', + 'file' => 'The :attribute must be at least :min kilobytes.', + 'string' => 'The :attribute must be at least :min characters.', + 'array' => 'The :attribute must have at least :min items.', + ], + 'not_in' => 'The selected :attribute is invalid.', + 'not_regex' => 'The :attribute format is invalid.', + 'numeric' => 'The :attribute must be a number.', + 'regex' => 'The :attribute format is invalid.', + 'required' => 'The :attribute field is required.', + 'required_if' => 'The :attribute field is required when :other is :value.', + 'required_with' => 'The :attribute field is required when :values is present.', + 'required_with_all' => 'The :attribute field is required when :values is present.', + 'required_without' => 'The :attribute field is required when :values is not present.', + 'required_without_all' => 'The :attribute field is required when none of :values are present.', + 'same' => 'The :attribute and :other must match.', + 'safe_url' => 'The provided link may not be safe.', + 'size' => [ + 'numeric' => 'The :attribute must be :size.', + 'file' => 'The :attribute must be :size kilobytes.', + 'string' => 'The :attribute must be :size characters.', + 'array' => 'The :attribute must contain :size items.', + ], + 'string' => 'The :attribute must be a string.', + 'timezone' => 'The :attribute must be a valid zone.', + 'totp' => 'The provided code is not valid or has expired.', + 'unique' => 'The :attribute has already been taken.', + 'url' => 'The :attribute format is invalid.', + 'uploaded' => 'The file could not be uploaded. The server may not accept files of this size.', + + 'zip_file' => 'The :attribute needs to reference a file within the ZIP.', + 'zip_file_mime' => 'The :attribute needs to reference a file of type :validTypes, found :foundType.', + 'zip_model_expected' => 'Data object expected but ":type" found.', + 'zip_unique' => 'The :attribute must be unique for the object type within the ZIP.', + + // Custom validation lines + 'custom' => [ + 'password-confirm' => [ + 'required_with' => 'Password confirmation required', + ], + ], + + // Custom validation attributes + 'attributes' => [], +]; diff --git a/lang/sv/activities.php b/lang/sv/activities.php index d90a5f08262..3acf6d4ff8d 100644 --- a/lang/sv/activities.php +++ b/lang/sv/activities.php @@ -64,17 +64,17 @@ // Auth 'auth_login' => 'loggade in', 'auth_register' => 'registrerad som ny användare', - 'auth_password_reset_request' => 'requested user password reset', - 'auth_password_reset_update' => 'reset user password', - 'mfa_setup_method' => 'configured MFA method', + 'auth_password_reset_request' => 'begärd återställning av användarlösenord', + 'auth_password_reset_update' => 'återställa användarens lösenord', + 'mfa_setup_method' => 'konfigurerad MFA metod', 'mfa_setup_method_notification' => 'Multifaktor-metod har konfigurerats', - 'mfa_remove_method' => 'removed MFA method', + 'mfa_remove_method' => 'tog bort MFA metod', 'mfa_remove_method_notification' => 'Multifaktor-metod har tagits bort', // Settings - 'settings_update' => 'updated settings', - 'settings_update_notification' => 'Settings successfully updated', - 'maintenance_action_run' => 'ran maintenance action', + 'settings_update' => 'uppdaterade inställningar', + 'settings_update_notification' => 'Inställningarna har uppdaterats', + 'maintenance_action_run' => 'körde underhållsåtgärder', // Webhooks 'webhook_create' => 'skapade webhook', @@ -84,40 +84,56 @@ 'webhook_delete' => 'raderade webhook', 'webhook_delete_notification' => 'Webhook har tagits bort', + // Imports + 'import_create' => 'import skapades', + 'import_create_notification' => 'Import har laddats upp', + 'import_run' => 'import uppdaterad', + 'import_run_notification' => 'Innehållet har importerats', + 'import_delete' => 'import borttagen', + 'import_delete_notification' => 'Importen har tagits bort', + // Users - 'user_create' => 'created user', - 'user_create_notification' => 'User successfully created', - 'user_update' => 'updated user', + 'user_create' => 'skapade användare', + 'user_create_notification' => 'Användare skapades', + 'user_update' => 'uppdaterad användare', 'user_update_notification' => 'Användaren har uppdaterats', - 'user_delete' => 'deleted user', + 'user_delete' => 'raderad användare', 'user_delete_notification' => 'Användaren har tagits bort', // API Tokens - 'api_token_create' => 'created api token', - 'api_token_create_notification' => 'API token successfully created', - 'api_token_update' => 'updated api token', - 'api_token_update_notification' => 'API token successfully updated', - 'api_token_delete' => 'deleted api token', - 'api_token_delete_notification' => 'API token successfully deleted', + 'api_token_create' => 'skapade API-token', + 'api_token_create_notification' => 'API-token har skapats', + 'api_token_update' => 'uppdaterad API-token', + 'api_token_update_notification' => 'API-token har uppdaterats', + 'api_token_delete' => 'raderad API-token', + 'api_token_delete_notification' => 'API-token har tagits bort', // Roles - 'role_create' => 'created role', - 'role_create_notification' => 'Role successfully created', - 'role_update' => 'updated role', - 'role_update_notification' => 'Role successfully updated', - 'role_delete' => 'deleted role', - 'role_delete_notification' => 'Role successfully deleted', + 'role_create' => 'skapad roll', + 'role_create_notification' => 'Rollen har skapats', + 'role_update' => 'uppdaterad roll', + 'role_update_notification' => 'Rollen har uppdaterats', + 'role_delete' => 'raderad roll', + 'role_delete_notification' => 'Rollen har tagits bort', // Recycle Bin - 'recycle_bin_empty' => 'emptied recycle bin', - 'recycle_bin_restore' => 'restored from recycle bin', - 'recycle_bin_destroy' => 'removed from recycle bin', + 'recycle_bin_empty' => 'tömd papperskorg', + 'recycle_bin_restore' => 'återställd från papperskorgen', + 'recycle_bin_destroy' => 'borttagen från papperskorgen', // Comments 'commented_on' => 'kommenterade', - 'comment_create' => 'added comment', - 'comment_update' => 'updated comment', - 'comment_delete' => 'deleted comment', + 'comment_create' => 'lagt till kommentar', + 'comment_update' => 'uppdaterad kommentar', + 'comment_delete' => 'raderad kommentar', + + // Sort Rules + 'sort_rule_create' => 'sorteringsregel skapad', + 'sort_rule_create_notification' => 'Sorteringsregel har skapats', + 'sort_rule_update' => 'sorteringsregel uppdaterad', + 'sort_rule_update_notification' => 'Sorteringsregel har uppdaterats', + 'sort_rule_delete' => 'sorteringsregel borttagen', + 'sort_rule_delete_notification' => 'Sorteringsregel har tagits bort', // Other 'permissions_update' => 'uppdaterade behörigheter', diff --git a/lang/sv/auth.php b/lang/sv/auth.php index e77f4c6696d..c9feb831218 100644 --- a/lang/sv/auth.php +++ b/lang/sv/auth.php @@ -61,8 +61,8 @@ 'email_confirm_send_error' => 'E-posten behöver bekräftas men systemet kan inte skicka mail. Kontakta adminstratören för att kontrollera att allt är konfigurerat korrekt.', 'email_confirm_success' => 'Din e-postadress har bekräftats! Du bör nu kunna logga in med denna e-postadress.', 'email_confirm_resent' => 'Bekräftelsemailet har skickats på nytt, kolla din mail', - 'email_confirm_thanks' => 'Thanks for confirming!', - 'email_confirm_thanks_desc' => 'Please wait a moment while your confirmation is handled. If you are not redirected after 3 seconds press the "Continue" link below to proceed.', + 'email_confirm_thanks' => 'Tack för att du bekräftade!', + 'email_confirm_thanks_desc' => 'Vänta en stund medan din bekräftelse hanteras. Om du inte omdirigeras efter 3 sekunder tryck på "Fortsätt" länken nedan för att fortsätta.', 'email_not_confirmed' => 'E-posadress ej bekräftad', 'email_not_confirmed_text' => 'Din e-postadress har inte bekräftats ännu.', @@ -91,7 +91,7 @@ 'mfa_option_totp_title' => 'Mobilapp', 'mfa_option_totp_desc' => 'För att använda multifaktorautentisering behöver du en mobil app som stöder TOTP så som Google Authenticator, Authy eller Microsoft Authenticator.', 'mfa_option_backup_codes_title' => 'Reservkoder', - 'mfa_option_backup_codes_desc' => 'Lagra säkert en uppsättning engångsreservkoder som du kan ange för att verifiera din identitet.', + 'mfa_option_backup_codes_desc' => 'Genererar en uppsättning engångsbaserade säkerhetskopieringskoder som du anger vid inloggningen för att verifiera din identitet. Se till att förvara dessa på en säker och säker plats.', 'mfa_gen_confirm_and_enable' => 'Bekräfta och aktivera', 'mfa_gen_backup_codes_title' => 'Konfiguration av reservkoder', 'mfa_gen_backup_codes_desc' => 'Spara nedanstående koder på en säker plats. När du använder systemet kommer du att kunna använda en av koderna som en andra autentiseringsmekanism.', diff --git a/lang/sv/common.php b/lang/sv/common.php index 33437eaa90e..23a98d9e36a 100644 --- a/lang/sv/common.php +++ b/lang/sv/common.php @@ -20,16 +20,18 @@ 'description' => 'Beskrivning', 'role' => 'Roll', 'cover_image' => 'Omslagsbild', - 'cover_image_description' => 'Bilden bör vara cirka 440x250px.', + 'cover_image_description' => 'Denna bild bör vara ungefär 440x250px även om den kommer att vara flexibelt skalad och beskuren för att passa användargränssnittet i olika scenarier där så krävs, kommer faktiska visningsmått att skilja sig.', // Actions 'actions' => 'Åtgärder', 'view' => 'Visa', 'view_all' => 'Visa alla', - 'new' => 'New', + 'new' => 'Ny', 'create' => 'Skapa', 'update' => 'Uppdatera', 'edit' => 'Redigera', + 'archive' => 'Archive', + 'unarchive' => 'Un-Archive', 'sort' => 'Sortera', 'move' => 'Flytta', 'copy' => 'Kopiera', @@ -52,7 +54,7 @@ 'filter_clear' => 'Rensa filter', 'download' => 'Ladda ner', 'open_in_tab' => 'Öppna i flik', - 'open' => 'Open', + 'open' => 'Öppna', // Sort Options 'sort_options' => 'Sorteringsalternativ', @@ -84,14 +86,14 @@ 'none' => 'Inga', // Header - 'homepage' => 'Homepage', + 'homepage' => 'Startsida', 'header_menu_expand' => 'Expandera sidhuvudsmenyn', 'profile_menu' => 'Profilmeny', 'view_profile' => 'Visa profil', 'edit_profile' => 'Redigera profil', 'dark_mode' => 'Mörkt läge', 'light_mode' => 'Ljust läge', - 'global_search' => 'Global Search', + 'global_search' => 'Global sökning', // Layout tabs 'tab_info' => 'Information', @@ -107,4 +109,7 @@ // Not directly used but available for convenience to users. 'privacy_policy' => 'Integritetspolicy', 'terms_of_service' => 'Användarvillkor', + + // OpenSearch + 'opensearch_description' => 'Sök :appName', ]; diff --git a/lang/sv/components.php b/lang/sv/components.php index 5a4296868ae..315e1683c50 100644 --- a/lang/sv/components.php +++ b/lang/sv/components.php @@ -6,36 +6,36 @@ // Image Manager 'image_select' => 'Val av bild', - 'image_list' => 'Image List', - 'image_details' => 'Image Details', - 'image_upload' => 'Upload Image', - 'image_intro' => 'Here you can select and manage images that have been previously uploaded to the system.', - 'image_intro_upload' => 'Upload a new image by dragging an image file into this window, or by using the "Upload Image" button above.', + 'image_list' => 'Bildlista', + 'image_details' => 'Bilddetaljer', + 'image_upload' => 'Ladda upp bild', + 'image_intro' => 'Här kan du välja och hantera bilder som tidigare har laddats upp till systemet.', + 'image_intro_upload' => 'Ladda upp en ny bild genom att dra en bildfil till detta fönster, eller genom att använda knappen "Ladda upp bild" ovan.', 'image_all' => 'Alla', 'image_all_title' => 'Visa alla bilder', 'image_book_title' => 'Visa bilder som laddats upp till den aktuella boken', 'image_page_title' => 'Visa bilder som laddats upp till den aktuella sidan', 'image_search_hint' => 'Sök efter bildens namn', 'image_uploaded' => 'Laddades upp :uploadedDate', - 'image_uploaded_by' => 'Uploaded by :userName', - 'image_uploaded_to' => 'Uploaded to :pageLink', - 'image_updated' => 'Updated :updateDate', + 'image_uploaded_by' => 'Uppladdad av :userName', + 'image_uploaded_to' => 'Uppladdad till :pageLink', + 'image_updated' => 'Uppdaterad :updateDate', 'image_load_more' => 'Ladda fler', 'image_image_name' => 'Bildnamn', 'image_delete_used' => 'Den här bilden används på nedanstående sidor.', 'image_delete_confirm_text' => 'Är du säker på att du vill radera denna bild?', 'image_select_image' => 'Välj bild', 'image_dropzone' => 'Släpp bilder här eller klicka för att ladda upp', - 'image_dropzone_drop' => 'Drop images here to upload', + 'image_dropzone_drop' => 'Släpp filer här för att ladda upp dem', 'images_deleted' => 'Bilder borttagna', 'image_preview' => 'Förhandsgranskning', 'image_upload_success' => 'Bilden har laddats upp', 'image_update_success' => 'Bildens uppgifter har ändrats', 'image_delete_success' => 'Bilden har tagits bort', - 'image_replace' => 'Replace Image', - 'image_replace_success' => 'Image file successfully updated', - 'image_rebuild_thumbs' => 'Regenerate Size Variations', - 'image_rebuild_thumbs_success' => 'Image size variations successfully rebuilt!', + 'image_replace' => 'Ersätt bild', + 'image_replace_success' => 'Bildfilen har uppdaterats', + 'image_rebuild_thumbs' => 'Återskapa variationer av bildstorlekar', + 'image_rebuild_thumbs_success' => 'Variationer av bildstorlekar har återskapats!', // Code Editor 'code_editor' => 'Redigera kod', diff --git a/lang/sv/editor.php b/lang/sv/editor.php index b1ed182dc58..c2485817dee 100644 --- a/lang/sv/editor.php +++ b/lang/sv/editor.php @@ -13,6 +13,7 @@ 'cancel' => 'Avbryt', 'save' => 'Spara', 'close' => 'Stäng', + 'apply' => 'Tillämpa', 'undo' => 'Ångra', 'redo' => 'Gör om', 'left' => 'Vänster', @@ -47,6 +48,7 @@ 'superscript' => 'Upphöjd', 'subscript' => 'Nedsänkt', 'text_color' => 'Textfärg', + 'highlight_color' => 'Highlight color', 'custom_color' => 'Anpassad färg', 'remove_color' => 'Ta bort färg', 'background_color' => 'Bakgrundsfärg', @@ -81,6 +83,9 @@ 'table_properties' => 'Tabellegenskaper', 'table_properties_title' => 'Tabellegenskaper', 'delete_table' => 'Ta bort tabell', + 'table_clear_formatting' => 'Rensa tabell-formatering', + 'resize_to_contents' => 'Ändra storlek till innehåll', + 'row_header' => 'Radrubrik', 'insert_row_before' => 'Infoga rad före', 'insert_row_after' => 'Infoga rad efter', 'delete_row' => 'Ta bort rad', @@ -144,11 +149,12 @@ 'url' => 'URL', 'text_to_display' => 'Text som ska visas', 'title' => 'Titel', - 'open_link' => 'Open link', - 'open_link_in' => 'Open link in...', + 'browse_links' => 'Bläddra bland länkar', + 'open_link' => 'Öppna länk', + 'open_link_in' => 'Öppna länk i...', 'open_link_current' => 'Aktuellt fönster', 'open_link_new' => 'Nytt fönster', - 'remove_link' => 'Remove link', + 'remove_link' => 'Radera länk', 'insert_collapsible' => 'Infoga hopfällbart block', 'collapsible_unwrap' => 'Expandera', 'edit_label' => 'Redigera etikett', @@ -160,9 +166,11 @@ 'about' => 'Om redigeraren', 'about_title' => 'Om WYSIWYG-redigeraren', 'editor_license' => 'Licens och upphovsrätt för redigerare', + 'editor_lexical_license' => 'Denna editor är byggd som en fork av :lexicalLink som distribueras under MIT-licensen.', + 'editor_lexical_license_link' => 'Fullständiga licensdetaljer hittas här.', 'editor_tiny_license' => 'Denna redigerare är byggd med :tinyLink som tillhandahålls under MIT licensen.', 'editor_tiny_license_link' => 'Upphovsrätten och licensuppgifterna för TinyMCE hittar du här.', - 'save_continue' => 'Spara & Fortsätt', + 'save_continue' => 'Spara sida & fortsätt', 'callouts_cycle' => '(Fortsätt trycka för att växla mellan typer)', 'link_selector' => 'Länka till innehåll', 'shortcuts' => 'Genvägar', diff --git a/lang/sv/entities.php b/lang/sv/entities.php index 5ff5cfe203c..a1e826b3080 100644 --- a/lang/sv/entities.php +++ b/lang/sv/entities.php @@ -23,7 +23,7 @@ 'meta_updated' => 'Uppdaterad :timeLength', 'meta_updated_name' => 'Uppdaterad :timeLength av :user', 'meta_owned_name' => 'Ägs av :user', - 'meta_reference_count' => 'Referenced by :count item|Referenced by :count items', + 'meta_reference_count' => 'Refererad till av :count item|Referenced by :count items', 'entity_select' => 'Välj enhet', 'entity_select_lack_permission' => 'Du har inte den behörighet som krävs för att välja det här objektet', 'images' => 'Bilder', @@ -39,6 +39,34 @@ 'export_pdf' => 'PDF-fil', 'export_text' => 'Textfil', 'export_md' => 'Markdown-fil', + 'export_zip' => 'Portabel ZIP', + 'default_template' => 'Förvald sidmall', + 'default_template_explain' => 'Tilldela en sidmall som kommer att användas som standardinnehåll för alla sidor som skapats inom detta objekt. Tänk på att detta endast kommer att användas om skaparen av sidan har tillgång till den valda mallsidan.', + 'default_template_select' => 'Välj en sidmall', + 'import' => 'Importera', + 'import_validate' => 'Validera import', + 'import_desc' => 'Importera böcker, kapitel och sidor med hjälp av en portabel ZIP-export från samma eller en annan instans. Välj en ZIP-fil för att fortsätta. Efter att filen har laddats upp och validerats kan du konfigurera och bekräfta importen i nästa vy.', + 'import_zip_select' => 'Välj ZIP-fil att ladda upp', + 'import_zip_validation_errors' => 'Fel upptäcktes vid validering av den angivna ZIP-filen:', + 'import_pending' => 'Väntande importer', + 'import_pending_none' => 'Ingen import har påbörjats.', + 'import_continue' => 'Fortsätt import', + 'import_continue_desc' => 'Granska innehållet som skall importeras från den uppladdade ZIP-filen. När du är redo, kör importen för att lägga till innehållet. Den uppladdade ZIP-baserade importfilen kommer automatiskt att tas bort vid lyckad import.', + 'import_details' => 'Importdetaljer', + 'import_run' => 'Kör import', + 'import_size' => ':size ZIP-storlek', + 'import_uploaded_at' => 'Uppladdad :relativeTime', + 'import_uploaded_by' => 'Uppladdad av', + 'import_location' => 'Importplats', + 'import_location_desc' => 'Välj en målplats för ditt importerade innehåll. Du behöver relevanta behörigheter för att skapa på den plats du väljer.', + 'import_delete_confirm' => 'Är du säker på att du vill ta bort denna import?', + 'import_delete_desc' => 'Detta kommer att ta bort den uppladdade ZIP-baserade importfilen och kan inte ångras.', + 'import_errors' => 'Importfel', + 'import_errors_desc' => 'Följande fel inträffade under importförsöket:', + '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', // Permissions and restrictions 'permissions' => 'Rättigheter', @@ -50,7 +78,7 @@ 'permissions_role_everyone_else' => 'Alla andra', 'permissions_role_everyone_else_desc' => 'Ställ in rättigheter för alla roller som inte uttryckligen har åsidosatts.', 'permissions_role_override' => 'Åsidosätt rättigheter för roll', - 'permissions_inherit_defaults' => 'Inherit defaults', + 'permissions_inherit_defaults' => 'Ärv standardrättigheter', // Search 'search_results' => 'Sökresultat', @@ -106,7 +134,7 @@ 'shelves_permissions_updated' => 'Rättigheter för hylla uppdaterades', 'shelves_permissions_active' => 'Rättigheter för hylla aktiverade', 'shelves_permissions_cascade_warning' => 'Rättigheter för hyllor ärvs inte automatiskt ner till böckerna i hyllorna. Detta beror på att en bok kan finnas från flera hyllor. Rättigheter kan däremot kopieras ner till en bok i hyllan med hjälp av alternativet nedan.', - 'shelves_permissions_create' => 'Shelf create permissions are only used for copying permissions to child books using the action below. They do not control the ability to create books.', + 'shelves_permissions_create' => 'Behörigheter för att skapa hyllor används endast för att kopiera behörigheter till underliggande böcker via åtgärden nedan. De styr inte möjligheten att skapa böcker.', 'shelves_copy_permissions_to_books' => 'Kopiera rättigheter till böcker', 'shelves_copy_permissions' => 'Kopiera rättigheter', 'shelves_copy_permissions_explain' => 'Detta kommer att tillämpa rättigheterna från den här hyllan på alla böcker den innehåller. Se till att eventuella ändringar sparats innan tillämpningen genomförs.', @@ -132,9 +160,6 @@ 'books_edit_named' => 'Redigera bok :bookName', 'books_form_book_name' => 'Bokens namn', 'books_save' => 'Spara bok', - 'books_default_template' => 'Default Page Template', - 'books_default_template_explain' => 'Assign a page template that will be used as the default content for all new pages in this book. Keep in mind this will only be used if the page creator has view access to those chosen template page.', - 'books_default_template_select' => 'Select a template page', 'books_permissions' => 'Rättigheter för boken', 'books_permissions_updated' => 'Bokens rättigheter har uppdaterats', 'books_empty_contents' => 'Det finns inga sidor eller kapitel i den här boken.', @@ -145,7 +170,9 @@ 'books_search_this' => 'Sök i boken', 'books_navigation' => 'Navigering', 'books_sort' => 'Sortera bokens innehåll', - 'books_sort_desc' => 'Move chapters and pages within a book to reorganise its contents. Other books can be added which allows easy moving of chapters and pages between books.', + 'books_sort_desc' => 'Flytta kapitel och sidor inom en bok för att omorganisera dess innehåll. Andra böcker kan läggas till, vilket gör det enkelt att flytta kapitel och sidor mellan böcker. Du kan även ställa in en regel som automatiskt sorterar bokens innehåll vid ändringar.', + 'books_sort_auto_sort' => 'Automatiskt sorteringsalternativ', + 'books_sort_auto_sort_active' => 'Aktiv automatisk sorteringsregel: :sortName', 'books_sort_named' => 'Sortera boken :bookName', 'books_sort_name' => 'Sortera utifrån namn', 'books_sort_created' => 'Sortera utifrån skapelse', @@ -154,17 +181,17 @@ 'books_sort_chapters_last' => 'Kapitel sist', 'books_sort_show_other' => 'Visa andra böcker', 'books_sort_save' => 'Spara ordning', - 'books_sort_show_other_desc' => 'Add other books here to include them in the sort operation, and allow easy cross-book reorganisation.', - 'books_sort_move_up' => 'Move Up', - 'books_sort_move_down' => 'Move Down', - 'books_sort_move_prev_book' => 'Move to Previous Book', - 'books_sort_move_next_book' => 'Move to Next Book', - 'books_sort_move_prev_chapter' => 'Move Into Previous Chapter', - 'books_sort_move_next_chapter' => 'Move Into Next Chapter', - 'books_sort_move_book_start' => 'Move to Start of Book', - 'books_sort_move_book_end' => 'Move to End of Book', - 'books_sort_move_before_chapter' => 'Move to Before Chapter', - 'books_sort_move_after_chapter' => 'Move to After Chapter', + 'books_sort_show_other_desc' => 'Lägg till andra böcker här för att inkludera dem i sorteringsåtgärden och möjliggöra enkel omorganisering mellan böcker.', + 'books_sort_move_up' => 'Flytta upp', + 'books_sort_move_down' => 'Flytta ned', + 'books_sort_move_prev_book' => 'Gå till förgående bok', + 'books_sort_move_next_book' => 'Gå till nästa bok', + 'books_sort_move_prev_chapter' => 'Gå till förgående kapitel', + 'books_sort_move_next_chapter' => 'Gå till nästa kapitel', + 'books_sort_move_book_start' => 'Gå till början av boken', + 'books_sort_move_book_end' => 'Gå till slutet av boken', + 'books_sort_move_before_chapter' => 'Gå till innan kapitlet', + 'books_sort_move_after_chapter' => 'Gå till efter kapitlet', 'books_copy' => 'Kopiera bok', 'books_copy_success' => 'Boken har kopierats', @@ -207,7 +234,7 @@ 'pages_delete_draft' => 'Ta bort utkast', 'pages_delete_success' => 'Sidan har tagits bort', 'pages_delete_draft_success' => 'Utkastet har tagits bort', - 'pages_delete_warning_template' => 'This page is in active use as a book default page template. These books will no longer have a default page template assigned after this page is deleted.', + 'pages_delete_warning_template' => 'Denna sida används för närvarande som standardsidmall för en bok eller ett kapitel. Dessa böcker eller kapitel kommer inte längre ha någon tilldelad standardsidmall om sidan tas bort.', 'pages_delete_confirm' => 'Är du säker på att du vill ta bort den här sidan?', 'pages_delete_draft_confirm' => 'Är du säker på att du vill ta bort det här utkastet?', 'pages_editing_named' => 'Redigerar sida :pageName', @@ -218,12 +245,14 @@ 'pages_editing_page' => 'Redigerar sida', 'pages_edit_draft_save_at' => 'Utkastet sparades ', 'pages_edit_delete_draft' => 'Ta bort utkast', - 'pages_edit_delete_draft_confirm' => 'Are you sure you want to delete your draft page changes? All of your changes, since the last full save, will be lost and the editor will be updated with the latest page non-draft save state.', + 'pages_edit_delete_draft_confirm' => 'Är du säker på att du vill ta bort dina utkaständringar? Alla ändringar du gjort sedan den senaste fullständiga sparningen kommer att gå förlorade, och redigeraren kommer att uppdateras med det senaste icke-utkastet av sidan.', 'pages_edit_discard_draft' => 'Ta bort utkastet', 'pages_edit_switch_to_markdown' => 'Växla till Markdown-redigerare', 'pages_edit_switch_to_markdown_clean' => '(Rent innehåll)', 'pages_edit_switch_to_markdown_stable' => '(Stabilt innehåll)', 'pages_edit_switch_to_wysiwyg' => 'Växla till WYSIWYG-redigerare', + 'pages_edit_switch_to_new_wysiwyg' => 'Växla till ny WYSIWYG', + 'pages_edit_switch_to_new_wysiwyg_desc' => '(In Beta Testing)', 'pages_edit_set_changelog' => 'Beskriv dina ändringar', 'pages_edit_enter_changelog_desc' => 'Ange en kort beskrivning av de ändringar du har gjort', 'pages_edit_enter_changelog' => 'Ändringslogg', @@ -241,10 +270,11 @@ 'pages_md_insert_image' => 'Infoga bild', 'pages_md_insert_link' => 'Infoga länk', 'pages_md_insert_drawing' => 'Infoga teckning', - 'pages_md_show_preview' => 'Show preview', + 'pages_md_show_preview' => 'Visa förhandsgranskning', 'pages_md_sync_scroll' => 'Sync preview scroll', - 'pages_drawing_unsaved' => 'Unsaved Drawing Found', - 'pages_drawing_unsaved_confirm' => 'Unsaved drawing data was found from a previous failed drawing save attempt. Would you like to restore and continue editing this unsaved drawing?', + 'pages_md_plain_editor' => 'Plaintext editor', + 'pages_drawing_unsaved' => 'Osparad ritning hittades', + 'pages_drawing_unsaved_confirm' => 'Osparade ritningsdata hittades från ett tidigare misslyckat sparförsök. Vill du återställa och fortsätta redigera den osparade ritningen?', 'pages_not_in_chapter' => 'Sidan ligger inte i något kapitel', 'pages_move' => 'Flytta sida', 'pages_copy' => 'Kopiera sida', @@ -254,14 +284,14 @@ 'pages_permissions_success' => 'Rättigheterna för sidan har uppdaterats', 'pages_revision' => 'Revidering', 'pages_revisions' => 'Sidrevisioner', - 'pages_revisions_desc' => 'Listed below are all the past revisions of this page. You can look back upon, compare, and restore old page versions if permissions allow. The full history of the page may not be fully reflected here since, depending on system configuration, old revisions could be auto-deleted.', + 'pages_revisions_desc' => 'Nedan listas alla tidigare versioner av denna sida. Du kan granska, jämföra och återställa äldre versioner om du har behörighet. Den fullständiga versionshistoriken kanske inte visas i sin helhet här, eftersom äldre versioner kan ha raderats automatiskt beroende på systemets konfiguration.', 'pages_revisions_named' => 'Sidrevisioner för :pageName', 'pages_revision_named' => 'Sidrevision för :pageName', 'pages_revision_restored_from' => 'Återställd från #:id; :summary', 'pages_revisions_created_by' => 'Skapad av', 'pages_revisions_date' => 'Revisionsdatum', 'pages_revisions_number' => '#', - 'pages_revisions_sort_number' => 'Revision Number', + 'pages_revisions_sort_number' => 'Revisionsnummer', 'pages_revisions_numbered' => 'Revisions #:id', 'pages_revisions_numbered_changes' => 'Revision #:id ändringar', 'pages_revisions_editor' => 'Typ av redigerare', @@ -272,9 +302,9 @@ 'pages_revisions_restore' => 'Återställ', 'pages_revisions_none' => 'Sidan har inga revisioner', 'pages_copy_link' => 'Kopiera länk', - 'pages_edit_content_link' => 'Jump to section in editor', - 'pages_pointer_enter_mode' => 'Enter section select mode', - 'pages_pointer_label' => 'Page Section Options', + 'pages_edit_content_link' => 'Hoppa till sektionen i redigeraren', + 'pages_pointer_enter_mode' => 'Ange markeringsläge för sektion', + 'pages_pointer_label' => 'Alternativ för sidsektion', 'pages_pointer_permalink' => 'Page Section Permalink', 'pages_pointer_include_tag' => 'Page Section Include Tag', 'pages_pointer_toggle_link' => 'Permalink mode, Press to show include tag', @@ -293,20 +323,20 @@ 'time_b' => 'under de senaste :minCount minuterna', 'message' => ':start :time. Var försiktiga så att ni inte skriver över varandras ändringar!', ], - 'pages_draft_discarded' => 'Draft discarded! The editor has been updated with the current page content', - 'pages_draft_deleted' => 'Draft deleted! The editor has been updated with the current page content', + 'pages_draft_discarded' => 'Utkastet har tagits bort. Redigeringsverktyget har uppdaterats med aktuellt sidinnehåll', + 'pages_draft_deleted' => 'Utkastet har raderats. Redigeringsverktyget har uppdaterats med aktuellt sidinnehåll', 'pages_specific' => 'Specifik sida', 'pages_is_template' => 'Sidmall', // Editor Sidebar - 'toggle_sidebar' => 'Toggle Sidebar', + 'toggle_sidebar' => 'Visa/Dölj sidopanel', 'page_tags' => 'Sidtaggar', 'chapter_tags' => 'Kapiteltaggar', 'book_tags' => 'Boktaggar', 'shelf_tags' => 'Hylltaggar', 'tag' => 'Tagg', 'tags' => 'Taggar', - 'tags_index_desc' => 'Tags can be applied to content within the system to apply a flexible form of categorization. Tags can have both a key and value, with the value being optional. Once applied, content can then be queried using the tag name and value.', + 'tags_index_desc' => 'Taggar kan användas på innehåll inom systemet för att skapa en flexibel form av kategorisering. Taggar kan ha både en nyckel och ett värde, där värdet är valfritt. När en tagg har tilldelats kan innehållet sökas fram med hjälp av taggens namn och värde.', 'tag_name' => 'Etikettnamn', 'tag_value' => 'Taggvärde (Frivilligt)', 'tags_explain' => "Lägg till taggar för att kategorisera ditt innehåll bättre. \n Du kan tilldela ett värde till en tagg för ännu bättre organisering.", @@ -330,7 +360,7 @@ 'attachments_upload_drop' => 'Alternatively you can drag and drop a file here to upload it as an attachment.', 'attachments_set_link' => 'Ange länk', 'attachments_delete' => 'Är du säker på att du vill ta bort bilagan?', - 'attachments_dropzone' => 'Drop files here to upload', + 'attachments_dropzone' => 'Släpp filer här för uppladdning', 'attachments_no_files' => 'Inga filer har laddats upp', 'attachments_explain_link' => 'Du kan bifoga en länk om du inte vill ladda upp en fil. Detta kan vara en länk till en annan sida eller till en fil i molnet.', 'attachments_link_name' => 'Länknamn', @@ -367,19 +397,28 @@ 'comment' => 'Kommentar', 'comments' => 'Kommentarer', 'comment_add' => 'Lägg till kommentar', + 'comment_none' => 'No comments to display', 'comment_placeholder' => 'Lämna en kommentar här', - 'comment_count' => '{0} Inga kommentarer|{1} 1 kommentar|[2,*] :count kommentarer', + 'comment_thread_count' => ':count Comment Thread|:count Comment Threads', + 'comment_archived_count' => ':count Archived', + 'comment_archived_threads' => 'Archived Threads', 'comment_save' => 'Spara kommentar', 'comment_new' => 'Ny kommentar', 'comment_created' => 'kommenterade :createDiff', 'comment_updated' => 'Uppdaterade :updateDiff av :username', - 'comment_updated_indicator' => 'Updated', + 'comment_updated_indicator' => 'Uppdaterad', 'comment_deleted_success' => 'Kommentar borttagen', 'comment_created_success' => 'Kommentaren har sparats', 'comment_updated_success' => 'Kommentaren har uppdaterats', + 'comment_archive_success' => 'Comment archived', + 'comment_unarchive_success' => 'Comment un-archived', + 'comment_view' => 'View comment', + 'comment_jump_to_thread' => 'Jump to thread', 'comment_delete_confirm' => 'Är du säker på att du vill ta bort den här kommentaren?', 'comment_in_reply_to' => 'Som svar på :commentId', - 'comment_editor_explain' => 'Here are the comments that have been left on this page. Comments can be added & managed when viewing the saved page.', + 'comment_reference' => 'Reference', + 'comment_reference_outdated' => '(Outdated)', + 'comment_editor_explain' => 'Här är kommentarer som lämnats på denna sida. Kommentarer kan läggas till och hanteras när den sparade sidan visas.', // Revision 'revision_delete_confirm' => 'Är du säker på att du vill radera den här versionen?', @@ -412,21 +451,21 @@ 'references_to_desc' => 'Listed below is all the known content in the system that links to this item.', // Watch Options - 'watch' => 'Watch', - 'watch_title_default' => 'Default Preferences', + 'watch' => 'Följ', + 'watch_title_default' => 'Standardinställningar', 'watch_desc_default' => 'Revert watching to just your default notification preferences.', 'watch_title_ignore' => 'Ignorera', - 'watch_desc_ignore' => 'Ignore all notifications, including those from user-level preferences.', - 'watch_title_new' => 'New Pages', - 'watch_desc_new' => 'Notify when any new page is created within this item.', - 'watch_title_updates' => 'All Page Updates', - 'watch_desc_updates' => 'Notify upon all new pages and page changes.', - 'watch_desc_updates_page' => 'Notify upon all page changes.', + 'watch_desc_ignore' => 'Ignorera samtliga meddelanden, även sådana som styrs av användarens egna inställningar.', + 'watch_title_new' => 'Nya sidor', + 'watch_desc_new' => 'Meddela när en ny sida skapas inom detta objekt.', + 'watch_title_updates' => 'Alla siduppdateringar', + 'watch_desc_updates' => 'Meddela vid alla nya sidor och siduppdateringar.', + 'watch_desc_updates_page' => 'Meddela alla siduppdateringar.', 'watch_title_comments' => 'All Page Updates & Comments', - 'watch_desc_comments' => 'Notify upon all new pages, page changes and new comments.', - 'watch_desc_comments_page' => 'Notify upon page changes and new comments.', - 'watch_change_default' => 'Change default notification preferences', - 'watch_detail_ignore' => 'Ignoring notifications', + 'watch_desc_comments' => 'Meddela vid alla nya sidor, siduppdateringar och nya kommentarer.', + 'watch_desc_comments_page' => 'Meddela vid siduppdateringar och nya kommentarer.', + 'watch_change_default' => 'Ändra standardinställningar för meddelanden', + 'watch_detail_ignore' => 'Ignorera meddelanden', 'watch_detail_new' => 'Watching for new pages', 'watch_detail_updates' => 'Watching new pages and updates', 'watch_detail_comments' => 'Watching new pages, updates & comments', diff --git a/lang/sv/errors.php b/lang/sv/errors.php index e24e5480733..d65b7d0a013 100644 --- a/lang/sv/errors.php +++ b/lang/sv/errors.php @@ -10,6 +10,7 @@ // Auth 'error_user_exists_different_creds' => 'En användare med adressen :email finns redan.', + 'auth_pre_register_theme_prevention' => 'User account could not be registered for the provided details', 'email_already_confirmed' => 'E-posten har redan bekräftats, prova att logga in.', 'email_confirmation_invalid' => 'Denna bekräftelsekod är inte giltig eller har redan använts. Vänligen prova att registrera dig på nytt.', 'email_confirmation_expired' => 'Denna bekräftelsekod har gått ut. Vi har skickat dig en ny.', @@ -23,7 +24,6 @@ 'saml_invalid_response_id' => 'En begäran från det externa autentiseringssystemet känns inte igen av en process som startats av denna applikation. Att navigera bakåt efter en inloggning kan orsaka detta problem.', 'saml_fail_authed' => 'Inloggning med :system misslyckades, systemet godkände inte auktoriseringen', 'oidc_already_logged_in' => 'Redan inloggad', - 'oidc_user_not_registered' => 'Användaren :name är inte registrerad och automatisk registrering är inaktiverad', 'oidc_no_email_address' => 'Kunde inte hitta en e-postadress för den här användaren i den data som tillhandahölls av det externa autentiseringssystemet', 'oidc_fail_authed' => 'Inloggning med :system misslyckades, systemet presenterade inte en godkänd auktorisering', 'social_no_action_defined' => 'Ingen åtgärd definierad', @@ -37,6 +37,7 @@ 'social_driver_not_found' => 'Drivrutinen för den här tjänsten hittades inte', 'social_driver_not_configured' => 'Dina inställningar för :socialAccount är inte korrekta.', 'invite_token_expired' => 'Denna inbjudningslänk har löpt ut. Du kan istället försöka återställa ditt kontos lösenord.', + 'login_user_not_found' => 'A user for this action could not be found.', // System 'path_not_writable' => 'Kunde inte ladda upp till sökvägen :filePath. Kontrollera att webbservern har skrivåtkomst.', @@ -52,7 +53,7 @@ 'image_upload_replace_type' => 'Image file replacements must be of the same type', 'image_upload_memory_limit' => 'Failed to handle image upload and/or create thumbnails due to system resource limits.', 'image_thumbnail_memory_limit' => 'Failed to create image size variations due to system resource limits.', - 'image_gallery_thumbnail_memory_limit' => 'Failed to create gallery thumbnails due to system resource limits.', + 'image_gallery_thumbnail_memory_limit' => 'Misslyckades att skapa galleriminiatyrer på grund av otillräckliga systemresurser.', 'drawing_data_not_found' => 'Drawing data could not be loaded. The drawing file might no longer exist or you may not have permission to access it.', // Attachments @@ -77,6 +78,7 @@ // Users 'users_cannot_delete_only_admin' => 'Du kan inte ta bort den enda admin-användaren', 'users_cannot_delete_guest' => 'Du kan inte ta bort gästanvändaren', + 'users_could_not_send_invite' => 'Could not create user since invite email failed to send', // Roles 'role_cannot_be_edited' => 'Den här rollen kan inte redigeras', @@ -103,6 +105,18 @@ 'app_down' => ':appName är nere just nu', 'back_soon' => 'Vi är snart tillbaka.', + // Import + 'import_zip_cant_read' => 'Kunde inte läsa ZIP-filen.', + 'import_zip_cant_decode_data' => 'Could not find and decode ZIP data.json content.', + 'import_zip_no_data' => 'ZIP file data has no expected book, chapter or page content.', + 'import_validation_failed' => 'ZIP-filen kunde inte valideras med fel:', + 'import_zip_failed_notification' => 'Det gick inte att importera ZIP-fil.', + 'import_perms_books' => 'Du saknar behörighet att skapa böcker.', + 'import_perms_chapters' => 'Du saknar behörighet att skapa kapitel.', + 'import_perms_pages' => 'Du saknar behörighet att skapa sidor.', + 'import_perms_images' => 'Du saknar behörighet för att skapa bilder.', + 'import_perms_attachments' => 'Du saknar behörighet att skapa bilagor.', + // API errors 'api_no_authorization_found' => 'Ingen auktoriseringstoken hittades på denna begäran', 'api_bad_authorization_format' => 'En auktoriseringstoken hittades på denna begäran men formatet verkade felaktigt', @@ -115,5 +129,5 @@ 'maintenance_test_email_failure' => 'Ett fel uppstod när ett test mail skulle skickas:', // HTTP errors - 'http_ssr_url_no_match' => 'The URL does not match the configured allowed SSR hosts', + 'http_ssr_url_no_match' => 'URL matchar inte de konfigurerade tillåtna SSR-värdarna', ]; diff --git a/lang/sv/notifications.php b/lang/sv/notifications.php index 9d2269ed87d..58418ddb01e 100644 --- a/lang/sv/notifications.php +++ b/lang/sv/notifications.php @@ -4,24 +4,24 @@ */ return [ - 'new_comment_subject' => 'New comment on page: :pageName', - 'new_comment_intro' => 'A user has commented on a page in :appName:', - 'new_page_subject' => 'New page: :pageName', - 'new_page_intro' => 'A new page has been created in :appName:', - 'updated_page_subject' => 'Updated page: :pageName', - 'updated_page_intro' => 'A page has been updated in :appName:', - 'updated_page_debounce' => 'To prevent a mass of notifications, for a while you won\'t be sent notifications for further edits to this page by the same editor.', + 'new_comment_subject' => 'Ny kommentar på sidan: :pageName', + 'new_comment_intro' => 'En användare har kommenterat en sida i :appName:', + 'new_page_subject' => 'Ny sida: :pageName', + 'new_page_intro' => 'En ny sida har blivit skapad i :appName:', + 'updated_page_subject' => 'Uppdaterad sida: :pageName', + 'updated_page_intro' => 'En sida har blivit uppdaterad i :appName:', + 'updated_page_debounce' => 'För att förhindra en massa notiser, så kommer det inte skickas nya notiser på ett tag för ytterligare ändringar till denna sida av samma skribent.', - 'detail_page_name' => 'Page Name:', - 'detail_page_path' => 'Page Path:', - 'detail_commenter' => 'Commenter:', - 'detail_comment' => 'Comment:', - 'detail_created_by' => 'Created By:', - 'detail_updated_by' => 'Updated By:', + 'detail_page_name' => 'Sidonamn:', + 'detail_page_path' => 'Sidosökväg:', + 'detail_commenter' => 'Kommentars-skapare:', + 'detail_comment' => 'Kommentar:', + 'detail_created_by' => 'Skapad av:', + 'detail_updated_by' => 'Uppdaterad av:', 'action_view_comment' => 'Visa kommentar', - 'action_view_page' => 'View Page', + 'action_view_page' => 'Visa sida', - 'footer_reason' => 'This notification was sent to you because :link cover this type of activity for this item.', - 'footer_reason_link' => 'your notification preferences', + 'footer_reason' => 'Detta meddelande skickades till dig eftersom :link täcker denna typ av aktivitet för detta objekt.', + 'footer_reason_link' => 'dina notifikationspreferenser', ]; diff --git a/lang/sv/preferences.php b/lang/sv/preferences.php index 9ca2c40b7cd..945099151f2 100644 --- a/lang/sv/preferences.php +++ b/lang/sv/preferences.php @@ -14,7 +14,7 @@ 'shortcuts_toggle_label' => 'Keyboard shortcuts enabled', 'shortcuts_section_navigation' => 'Navigation', 'shortcuts_section_actions' => 'Common Actions', - 'shortcuts_save' => 'Save Shortcuts', + 'shortcuts_save' => 'Spara genvägar', 'shortcuts_overlay_desc' => 'Note: When shortcuts are enabled a helper overlay is available via pressing "?" which will highlight the available shortcuts for actions currently visible on the screen.', 'shortcuts_update_success' => 'Shortcut preferences have been updated!', 'shortcuts_overview_desc' => 'Manage keyboard shortcuts you can use to navigate the system user interface.', @@ -27,16 +27,16 @@ 'notifications_save' => 'Save Preferences', 'notifications_update_success' => 'Notification preferences have been updated!', 'notifications_watched' => 'Watched & Ignored Items', - 'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.', + 'notifications_watched_desc' => 'Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.', 'auth' => 'Access & Security', 'auth_change_password' => 'Change Password', 'auth_change_password_desc' => 'Change the password you use to log-in to the application. This must be at least 8 characters long.', - 'auth_change_password_success' => 'Password has been updated!', + 'auth_change_password_success' => 'Lösenordet har uppdaterats!', - 'profile' => 'Profile Details', + 'profile' => 'Profildetaljer', 'profile_desc' => 'Manage the details of your account which represents you to other users, in addition to details that are used for communication and system personalisation.', - 'profile_view_public' => 'View Public Profile', + 'profile_view_public' => 'Visa publik profil', 'profile_name_desc' => 'Configure your display name which will be visible to other users in the system through the activity you perform, and content you own.', 'profile_email_desc' => 'This email will be used for notifications and, depending on active system authentication, system access.', 'profile_email_no_permission' => 'Unfortunately you don\'t have permission to change your email address. If you want to change this, you\'d need to ask an administrator to change this for you.', @@ -44,8 +44,8 @@ 'profile_admin_options' => 'Administrator Options', 'profile_admin_options_desc' => 'Additional administrator-level options, like those to manage role assignments, can be found for your user account in the "Settings > Users" area of the application.', - 'delete_account' => 'Delete Account', - 'delete_my_account' => 'Delete My Account', + 'delete_account' => 'Radera konto', + 'delete_my_account' => 'Radera mitt konto', 'delete_my_account_desc' => 'This will fully delete your user account from the system. You will not be able to recover this account or revert this action. Content you\'ve created, such as created pages and uploaded images, will remain.', 'delete_my_account_warning' => 'Are you sure you want to delete your account?', ]; diff --git a/lang/sv/settings.php b/lang/sv/settings.php index 830680b65d8..fdf8ebd0633 100644 --- a/lang/sv/settings.php +++ b/lang/sv/settings.php @@ -32,9 +32,9 @@ 'app_custom_html_desc' => 'Innehåll i det här fältet placeras längst ner i -sektionen på varje sida. Detta kan användas för att skriva över stilmallar eller lägga in spårningskoder.', 'app_custom_html_disabled_notice' => 'Anpassat innehåll i HTML-huvudet är inaktiverat på denna inställningssida för att säkerställa att eventuella ändringar som påverkar funktionaliteten kan återställas.', 'app_logo' => 'Applikationslogotyp', - 'app_logo_desc' => 'This is used in the application header bar, among other areas. This image should be 86px in height. Large images will be scaled down.', - 'app_icon' => 'Application Icon', - 'app_icon_desc' => 'This icon is used for browser tabs and shortcut icons. This should be a 256px square PNG image.', + 'app_logo_desc' => 'Detta används bland annat i applikationshuvudet. Bilden bör vara 86 pixlar i höjd. Stora bilder skalas ned.', + 'app_icon' => 'Applikationsikon', + 'app_icon_desc' => 'Den här ikonen syns i webbläsarens flikar, bokmärken och genvägar. Den skall vara 256x256 pixlar i PNG format.', 'app_homepage' => 'Startsida', 'app_homepage_desc' => 'Välj en vy att använda som startsida istället för standardvyn. Behörigheter för valda sidor kommer att ignoreras.', 'app_homepage_select' => 'Välj en sida', @@ -48,12 +48,12 @@ 'app_disable_comments_desc' => 'Inaktivera kommentarer på alla sidor i applikationen. Befintliga kommentarer visas inte.', // Color settings - 'color_scheme' => 'Application Color Scheme', - 'color_scheme_desc' => 'Set the colors to use in the application user interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.', - 'ui_colors_desc' => 'Set the application primary color and default link color. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the application interface.', - 'app_color' => 'Primary Color', - 'link_color' => 'Default Link Color', - 'content_colors_desc' => 'Set colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.', + 'color_scheme' => 'Programmets färgschema', + 'color_scheme_desc' => 'Ställ in de färger som ska användas i applikationens användargränssnitt. Färger kan konfigureras separat för mörka och ljusa lägen för att bäst passa temat och säkerställa läsbarhet.', + 'ui_colors_desc' => 'Ange applikationens primära färg och standard färg för länkar. Den primära färgen används främst för huvudrubriken, knappar och gränssnitt dekorationer. Standardfärgen på länken används för textbaserade länkar och åtgärder, både inom skriftligt innehåll och i applikationsgränssnittet.', + 'app_color' => 'Primärfärg', + 'link_color' => 'Standardfärg för länkar', + 'content_colors_desc' => 'Ange färger för alla element i sidans organisationshierarki. Det rekommenderas att välja färger med liknande ljusstyrka som standardfärgerna för att bibehålla läsbarheten.', 'bookshelf_color' => 'Hyllfärg', 'book_color' => 'Bokfärg', 'chapter_color' => 'Kapitelfärg', @@ -74,6 +74,36 @@ 'reg_confirm_restrict_domain_desc' => 'Ange en kommaseparerad lista över e-postdomäner som du vill begränsa registrering för. Användare kommer att få ett e-postmeddelande för att bekräfta sin e-postadress innan de tillåts att logga in.
Notera att användare kommer att kunna ändra sin e-postadress efter lyckad registrering.', 'reg_confirm_restrict_domain_placeholder' => 'Ingen begränsning inställd', + // Sorting Settings + 'sorting' => 'Sorterar', + 'sorting_book_default' => 'Standard boksortering', + 'sorting_book_default_desc' => 'Välj standard sorteringsregel som skall tillämpas på nya böcker. Detta påverkar inte befintliga böcker och kan åsidosättas per bok.', + 'sorting_rules' => 'Sorteringsregler', + 'sorting_rules_desc' => 'These are predefined sorting operations which can be applied to content in the system.', + 'sort_rule_assigned_to_x_books' => 'Tilldelad till :count bok|Tilldelad till :count böcker', + 'sort_rule_create' => 'Skapa sorteringsregel', + 'sort_rule_edit' => 'Redigera sorteringsregel', + 'sort_rule_delete' => 'Ta bort sorteringsregel', + 'sort_rule_delete_desc' => 'Remove this sort rule from the system. Books using this sort will revert to manual sorting.', + 'sort_rule_delete_warn_books' => 'This sort rule is currently used on :count book(s). Are you sure you want to delete this?', + 'sort_rule_delete_warn_default' => 'This sort rule is currently used as the default for books. Are you sure you want to delete this?', + 'sort_rule_details' => 'Detaljer för sorteringsregler', + 'sort_rule_details_desc' => 'Set a name for this sort rule, which will appear in lists when users are selecting a sort.', + 'sort_rule_operations' => 'Sort Operations', + 'sort_rule_operations_desc' => 'Configure the sort actions to be performed by moving them from the list of available operations. Upon use, the operations will be applied in order, from top to bottom. Any changes made here will be applied to all assigned books upon save.', + 'sort_rule_available_operations' => 'Available Operations', + 'sort_rule_available_operations_empty' => 'No operations remaining', + 'sort_rule_configured_operations' => 'Configured Operations', + 'sort_rule_configured_operations_empty' => 'Drag/add operations from the "Available Operations" list', + 'sort_rule_op_asc' => '(Asc)', + 'sort_rule_op_desc' => '(Desc)', + 'sort_rule_op_name' => 'Name - Alphabetical', + 'sort_rule_op_name_numeric' => '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_last' => 'Chapters Last', + // Maintenance settings 'maint' => 'Underhåll', 'maint_image_cleanup' => 'Rensa bilder', @@ -109,7 +139,7 @@ 'recycle_bin_contents_empty' => 'Papperskorgen är för närvarande tom', 'recycle_bin_empty' => 'Töm papperskorgen', 'recycle_bin_empty_confirm' => 'Detta kommer permanent att förstöra alla objekt i papperskorgen inklusive innehåll som finns i varje objekt. Är du säker du vill tömma papperskorgen?', - 'recycle_bin_destroy_confirm' => 'Denna åtgärd kommer att permanent ta bort detta objekt, tillsammans med alla underordnade element som anges nedan, från systemet och du kommer inte att kunna återställa detta innehåll. Är du säker på att du vill ta bort objektet permanent?', + 'recycle_bin_destroy_confirm' => 'This action will permanently delete this item from the system, along with any child elements listed below, and you will not be able to restore this content. Are you sure you want to permanently delete this item?', 'recycle_bin_destroy_list' => 'Objekt som ska förstöras', 'recycle_bin_restore_list' => 'Objekt som ska återställas', 'recycle_bin_restore_confirm' => 'Denna åtgärd kommer att återställa det raderade objektet, inklusive alla underordnade element, till deras ursprungliga plats. Om den ursprungliga platsen har tagits bort sedan dess, och är nu i papperskorgen, kommer det överordnade objektet också att behöva återställas.', @@ -162,6 +192,7 @@ 'role_access_api' => 'Åtkomst till systemets API', 'role_manage_settings' => 'Hantera appinställningar', 'role_export_content' => 'Exportera innehåll', + 'role_import_content' => 'Import content', 'role_editor_change' => 'Ändra sidredigerare', 'role_notifications' => 'Receive & manage notifications', 'role_asset' => 'Tillgång till innehåll', @@ -214,8 +245,8 @@ 'users_social_accounts_info' => 'Här kan du ansluta dina andra konton för snabbare och smidigare inloggning. Om du kopplar från en tjänst här kommer de behörigheter som tidigare givits inte att tas bort - ta bort behörigheter genom att logga in på ditt konto på tjänsten i fråga.', 'users_social_connect' => 'Anslut konto', 'users_social_disconnect' => 'Koppla från konto', - 'users_social_status_connected' => 'Connected', - 'users_social_status_disconnected' => 'Disconnected', + 'users_social_status_connected' => 'Ansluten', + 'users_social_status_disconnected' => 'Bortkopplad', 'users_social_connected' => ':socialAccount har kopplats till ditt konto.', 'users_social_disconnected' => ':socialAccount har kopplats bort från ditt konto.', 'users_api_tokens' => 'API-nyckel', @@ -276,6 +307,14 @@ 'webhooks_last_errored' => 'Senast felande:', 'webhooks_last_error_message' => 'Senaste felmeddelande:', + // Licensing + 'licenses' => 'Licenser', + 'licenses_desc' => 'This page details license information for BookStack in addition to the projects & libraries that are used within BookStack. Many projects listed may only be used in a development context.', + 'licenses_bookstack' => 'BookStack licens', + 'licenses_php' => 'Licenser för PHP-bibliotek', + 'licenses_js' => 'Licenser för JavaScript-bibliotek', + 'licenses_other' => 'Andra licenser', + 'license_details' => 'Licensinformation', //! If editing translations files directly please ignore this in all //! languages apart from en. Content will be auto-copied from en. @@ -287,6 +326,7 @@ 'bs' => 'Bosanski', 'ca' => 'Katalanska', 'cs' => 'Česky', + 'cy' => 'Cymraeg', 'da' => 'Danska', 'de' => 'Deutsch (Sie)', 'de_informal' => 'Deutsch (Du)', @@ -308,6 +348,7 @@ 'lt' => 'Lietuvių Kalba', 'lv' => 'Latviešu Valoda', 'nb' => 'Norsk (Bokmål)', + 'ne' => 'नेपाली', 'nn' => 'Nynorsk', 'nl' => 'Nederlands', 'pl' => 'Polski', diff --git a/lang/sv/validation.php b/lang/sv/validation.php index b9c609f486d..4cc98c57598 100644 --- a/lang/sv/validation.php +++ b/lang/sv/validation.php @@ -105,6 +105,11 @@ 'url' => 'Formatet på :attribute är ogiltigt.', 'uploaded' => 'Filen kunde inte laddas upp. Servern kanske inte tillåter filer med denna storlek.', + 'zip_file' => 'The :attribute needs to reference a file within the ZIP.', + 'zip_file_mime' => 'The :attribute needs to reference a file of type :validTypes, found :foundType.', + 'zip_model_expected' => 'Data object expected but ":type" found.', + 'zip_unique' => 'The :attribute must be unique for the object type within the ZIP.', + // Custom validation lines 'custom' => [ 'password-confirm' => [ diff --git a/lang/tk/activities.php b/lang/tk/activities.php new file mode 100644 index 00000000000..4362fc02958 --- /dev/null +++ b/lang/tk/activities.php @@ -0,0 +1,140 @@ + 'created page', + 'page_create_notification' => 'Page successfully created', + 'page_update' => 'updated page', + 'page_update_notification' => 'Page successfully updated', + 'page_delete' => 'deleted page', + 'page_delete_notification' => 'Page successfully deleted', + 'page_restore' => 'restored page', + 'page_restore_notification' => 'Page successfully restored', + 'page_move' => 'moved page', + 'page_move_notification' => 'Page successfully moved', + + // Chapters + 'chapter_create' => 'created chapter', + 'chapter_create_notification' => 'Chapter successfully created', + 'chapter_update' => 'updated chapter', + 'chapter_update_notification' => 'Chapter successfully updated', + 'chapter_delete' => 'deleted chapter', + 'chapter_delete_notification' => 'Chapter successfully deleted', + 'chapter_move' => 'moved chapter', + 'chapter_move_notification' => 'Chapter successfully moved', + + // Books + 'book_create' => 'created book', + 'book_create_notification' => 'Book successfully created', + 'book_create_from_chapter' => 'converted chapter to book', + 'book_create_from_chapter_notification' => 'Chapter successfully converted to a book', + 'book_update' => 'updated book', + 'book_update_notification' => 'Book successfully updated', + 'book_delete' => 'deleted book', + 'book_delete_notification' => 'Book successfully deleted', + 'book_sort' => 'sorted book', + 'book_sort_notification' => 'Book successfully re-sorted', + + // Bookshelves + 'bookshelf_create' => 'created shelf', + 'bookshelf_create_notification' => 'Shelf successfully created', + 'bookshelf_create_from_book' => 'converted book to shelf', + 'bookshelf_create_from_book_notification' => 'Book successfully converted to a shelf', + 'bookshelf_update' => 'updated shelf', + 'bookshelf_update_notification' => 'Shelf successfully updated', + 'bookshelf_delete' => 'deleted shelf', + 'bookshelf_delete_notification' => 'Shelf successfully deleted', + + // Revisions + 'revision_restore' => 'restored revision', + 'revision_delete' => 'deleted revision', + 'revision_delete_notification' => 'Revision successfully deleted', + + // Favourites + 'favourite_add_notification' => '":name" has been added to your favourites', + 'favourite_remove_notification' => '":name" has been removed from your favourites', + + // Watching + 'watch_update_level_notification' => 'Watch preferences successfully updated', + + // Auth + 'auth_login' => 'logged in', + 'auth_register' => 'registered as new user', + 'auth_password_reset_request' => 'requested user password reset', + 'auth_password_reset_update' => 'reset user password', + 'mfa_setup_method' => 'configured MFA method', + 'mfa_setup_method_notification' => 'Multi-factor method successfully configured', + 'mfa_remove_method' => 'removed MFA method', + 'mfa_remove_method_notification' => 'Multi-factor method successfully removed', + + // Settings + 'settings_update' => 'updated settings', + 'settings_update_notification' => 'Settings successfully updated', + 'maintenance_action_run' => 'ran maintenance action', + + // Webhooks + 'webhook_create' => 'created webhook', + 'webhook_create_notification' => 'Webhook successfully created', + 'webhook_update' => 'updated webhook', + 'webhook_update_notification' => 'Webhook successfully updated', + 'webhook_delete' => 'deleted webhook', + 'webhook_delete_notification' => 'Webhook successfully deleted', + + // Imports + 'import_create' => 'created import', + 'import_create_notification' => 'Import successfully uploaded', + 'import_run' => 'updated import', + 'import_run_notification' => 'Content successfully imported', + 'import_delete' => 'deleted import', + 'import_delete_notification' => 'Import successfully deleted', + + // Users + 'user_create' => 'created user', + 'user_create_notification' => 'User successfully created', + 'user_update' => 'updated user', + 'user_update_notification' => 'User successfully updated', + 'user_delete' => 'deleted user', + 'user_delete_notification' => 'User successfully removed', + + // API Tokens + 'api_token_create' => 'created API token', + 'api_token_create_notification' => 'API token successfully created', + 'api_token_update' => 'updated API token', + 'api_token_update_notification' => 'API token successfully updated', + 'api_token_delete' => 'deleted API token', + 'api_token_delete_notification' => 'API token successfully deleted', + + // Roles + 'role_create' => 'created role', + 'role_create_notification' => 'Role successfully created', + 'role_update' => 'updated role', + 'role_update_notification' => 'Role successfully updated', + 'role_delete' => 'deleted role', + 'role_delete_notification' => 'Role successfully deleted', + + // Recycle Bin + 'recycle_bin_empty' => 'emptied recycle bin', + 'recycle_bin_restore' => 'restored from recycle bin', + 'recycle_bin_destroy' => 'removed from recycle bin', + + // Comments + 'commented_on' => 'commented on', + 'comment_create' => 'added comment', + 'comment_update' => 'updated comment', + 'comment_delete' => 'deleted comment', + + // Sort Rules + 'sort_rule_create' => 'created sort rule', + 'sort_rule_create_notification' => 'Sort rule successfully created', + 'sort_rule_update' => 'updated sort rule', + 'sort_rule_update_notification' => 'Sort rule successfully updated', + 'sort_rule_delete' => 'deleted sort rule', + 'sort_rule_delete_notification' => 'Sort rule successfully deleted', + + // Other + 'permissions_update' => 'updated permissions', +]; diff --git a/lang/tk/auth.php b/lang/tk/auth.php new file mode 100644 index 00000000000..57f0cb5c632 --- /dev/null +++ b/lang/tk/auth.php @@ -0,0 +1,117 @@ + 'These credentials do not match our records.', + 'throttle' => 'Too many login attempts. Please try again in :seconds seconds.', + + // Login & Register + 'sign_up' => 'Sign up', + 'log_in' => 'Log in', + 'log_in_with' => 'Login with :socialDriver', + 'sign_up_with' => 'Sign up with :socialDriver', + 'logout' => 'Logout', + + 'name' => 'Name', + 'username' => 'Username', + 'email' => 'Email', + 'password' => 'Password', + 'password_confirm' => 'Confirm Password', + 'password_hint' => 'Must be at least 8 characters', + 'forgot_password' => 'Forgot Password?', + 'remember_me' => 'Remember Me', + 'ldap_email_hint' => 'Please enter an email to use for this account.', + 'create_account' => 'Create Account', + 'already_have_account' => 'Already have an account?', + 'dont_have_account' => 'Don\'t have an account?', + 'social_login' => 'Social Login', + 'social_registration' => 'Social Registration', + 'social_registration_text' => 'Register and sign in using another service.', + + 'register_thanks' => 'Thanks for registering!', + 'register_confirm' => 'Please check your email and click the confirmation button to access :appName.', + 'registrations_disabled' => 'Registrations are currently disabled', + 'registration_email_domain_invalid' => 'That email domain does not have access to this application', + 'register_success' => 'Thanks for signing up! You are now registered and signed in.', + + // Login auto-initiation + 'auto_init_starting' => 'Attempting Login', + 'auto_init_starting_desc' => 'We\'re contacting your authentication system to start the login process. If there\'s no progress after 5 seconds you can try clicking the link below.', + 'auto_init_start_link' => 'Proceed with authentication', + + // Password Reset + 'reset_password' => 'Reset Password', + 'reset_password_send_instructions' => 'Enter your email below and you will be sent an email with a password reset link.', + 'reset_password_send_button' => 'Send Reset Link', + 'reset_password_sent' => 'A password reset link will be sent to :email if that email address is found in the system.', + 'reset_password_success' => 'Your password has been successfully reset.', + 'email_reset_subject' => 'Reset your :appName password', + 'email_reset_text' => 'You are receiving this email because we received a password reset request for your account.', + 'email_reset_not_requested' => 'If you did not request a password reset, no further action is required.', + + // Email Confirmation + 'email_confirm_subject' => 'Confirm your email on :appName', + 'email_confirm_greeting' => 'Thanks for joining :appName!', + 'email_confirm_text' => 'Please confirm your email address by clicking the button below:', + 'email_confirm_action' => 'Confirm Email', + 'email_confirm_send_error' => 'Email confirmation required but the system could not send the email. Contact the admin to ensure email is set up correctly.', + 'email_confirm_success' => 'Your email has been confirmed! You should now be able to login using this email address.', + 'email_confirm_resent' => 'Confirmation email resent, Please check your inbox.', + 'email_confirm_thanks' => 'Thanks for confirming!', + 'email_confirm_thanks_desc' => 'Please wait a moment while your confirmation is handled. If you are not redirected after 3 seconds press the "Continue" link below to proceed.', + + 'email_not_confirmed' => 'Email Address Not Confirmed', + 'email_not_confirmed_text' => 'Your email address has not yet been confirmed.', + 'email_not_confirmed_click_link' => 'Please click the link in the email that was sent shortly after you registered.', + 'email_not_confirmed_resend' => 'If you cannot find the email you can re-send the confirmation email by submitting the form below.', + 'email_not_confirmed_resend_button' => 'Resend Confirmation Email', + + // User Invite + 'user_invite_email_subject' => 'You have been invited to join :appName!', + 'user_invite_email_greeting' => 'An account has been created for you on :appName.', + 'user_invite_email_text' => 'Click the button below to set an account password and gain access:', + 'user_invite_email_action' => 'Set Account Password', + 'user_invite_page_welcome' => 'Welcome to :appName!', + 'user_invite_page_text' => 'To finalise your account and gain access you need to set a password which will be used to log-in to :appName on future visits.', + 'user_invite_page_confirm_button' => 'Confirm Password', + 'user_invite_success_login' => 'Password set, you should now be able to login using your set password to access :appName!', + + // Multi-factor Authentication + 'mfa_setup' => 'Setup Multi-Factor Authentication', + 'mfa_setup_desc' => 'Setup multi-factor authentication as an extra layer of security for your user account.', + 'mfa_setup_configured' => 'Already configured', + 'mfa_setup_reconfigure' => 'Reconfigure', + 'mfa_setup_remove_confirmation' => 'Are you sure you want to remove this multi-factor authentication method?', + 'mfa_setup_action' => 'Setup', + 'mfa_backup_codes_usage_limit_warning' => 'You have less than 5 backup codes remaining, Please generate and store a new set before you run out of codes to prevent being locked out of your account.', + 'mfa_option_totp_title' => 'Mobile App', + 'mfa_option_totp_desc' => 'To use multi-factor authentication you\'ll need a mobile application that supports TOTP such as Google Authenticator, Authy or Microsoft Authenticator.', + 'mfa_option_backup_codes_title' => 'Backup Codes', + 'mfa_option_backup_codes_desc' => 'Generates a set of one-time-use backup codes which you\'ll enter on login to verify your identity. Make sure to store these in a safe & secure place.', + 'mfa_gen_confirm_and_enable' => 'Confirm and Enable', + 'mfa_gen_backup_codes_title' => 'Backup Codes Setup', + 'mfa_gen_backup_codes_desc' => 'Store the below list of codes in a safe place. When accessing the system you\'ll be able to use one of the codes as a second authentication mechanism.', + 'mfa_gen_backup_codes_download' => 'Download Codes', + 'mfa_gen_backup_codes_usage_warning' => 'Each code can only be used once', + 'mfa_gen_totp_title' => 'Mobile App Setup', + 'mfa_gen_totp_desc' => 'To use multi-factor authentication you\'ll need a mobile application that supports TOTP such as Google Authenticator, Authy or Microsoft Authenticator.', + 'mfa_gen_totp_scan' => 'Scan the QR code below using your preferred authentication app to get started.', + 'mfa_gen_totp_verify_setup' => 'Verify Setup', + 'mfa_gen_totp_verify_setup_desc' => 'Verify that all is working by entering a code, generated within your authentication app, in the input box below:', + 'mfa_gen_totp_provide_code_here' => 'Provide your app generated code here', + 'mfa_verify_access' => 'Verify Access', + 'mfa_verify_access_desc' => 'Your user account requires you to confirm your identity via an additional level of verification before you\'re granted access. Verify using one of your configured methods to continue.', + 'mfa_verify_no_methods' => 'No Methods Configured', + 'mfa_verify_no_methods_desc' => 'No multi-factor authentication methods could be found for your account. You\'ll need to set up at least one method before you gain access.', + 'mfa_verify_use_totp' => 'Verify using a mobile app', + 'mfa_verify_use_backup_codes' => 'Verify using a backup code', + 'mfa_verify_backup_code' => 'Backup Code', + 'mfa_verify_backup_code_desc' => 'Enter one of your remaining backup codes below:', + 'mfa_verify_backup_code_enter_here' => 'Enter backup code here', + 'mfa_verify_totp_desc' => 'Enter the code, generated using your mobile app, below:', + 'mfa_setup_login_notification' => 'Multi-factor method configured, Please now login again using the configured method.', +]; diff --git a/lang/tk/common.php b/lang/tk/common.php new file mode 100644 index 00000000000..06a9e855ce3 --- /dev/null +++ b/lang/tk/common.php @@ -0,0 +1,115 @@ + 'Cancel', + 'close' => 'Close', + 'confirm' => 'Confirm', + 'back' => 'Back', + 'save' => 'Save', + 'continue' => 'Continue', + 'select' => 'Select', + 'toggle_all' => 'Toggle All', + 'more' => 'More', + + // Form Labels + 'name' => 'Name', + 'description' => 'Description', + 'role' => 'Role', + 'cover_image' => 'Cover image', + 'cover_image_description' => 'This image should be approximately 440x250px although it will be flexibly scaled & cropped to fit the user interface in different scenarios as required, so actual dimensions for display will differ.', + + // Actions + 'actions' => 'Actions', + 'view' => 'View', + 'view_all' => 'View All', + 'new' => 'New', + 'create' => 'Create', + 'update' => 'Update', + 'edit' => 'Edit', + 'archive' => 'Archive', + 'unarchive' => 'Un-Archive', + 'sort' => 'Sort', + 'move' => 'Move', + 'copy' => 'Copy', + 'reply' => 'Reply', + 'delete' => 'Delete', + 'delete_confirm' => 'Confirm Deletion', + 'search' => 'Search', + 'search_clear' => 'Clear Search', + 'reset' => 'Reset', + 'remove' => 'Remove', + 'add' => 'Add', + 'configure' => 'Configure', + 'manage' => 'Manage', + 'fullscreen' => 'Fullscreen', + 'favourite' => 'Favourite', + 'unfavourite' => 'Unfavourite', + 'next' => 'Next', + 'previous' => 'Previous', + 'filter_active' => 'Active Filter:', + 'filter_clear' => 'Clear Filter', + 'download' => 'Download', + 'open_in_tab' => 'Open in Tab', + 'open' => 'Open', + + // Sort Options + 'sort_options' => 'Sort Options', + 'sort_direction_toggle' => 'Sort Direction Toggle', + 'sort_ascending' => 'Sort Ascending', + 'sort_descending' => 'Sort Descending', + 'sort_name' => 'Name', + 'sort_default' => 'Default', + 'sort_created_at' => 'Created Date', + 'sort_updated_at' => 'Updated Date', + + // Misc + 'deleted_user' => 'Deleted User', + 'no_activity' => 'No activity to show', + 'no_items' => 'No items available', + 'back_to_top' => 'Back to top', + 'skip_to_main_content' => 'Skip to main content', + 'toggle_details' => 'Toggle Details', + 'toggle_thumbnails' => 'Toggle Thumbnails', + 'details' => 'Details', + 'grid_view' => 'Grid View', + 'list_view' => 'List View', + 'default' => 'Default', + 'breadcrumb' => 'Breadcrumb', + 'status' => 'Status', + 'status_active' => 'Active', + 'status_inactive' => 'Inactive', + 'never' => 'Never', + 'none' => 'None', + + // Header + 'homepage' => 'Homepage', + 'header_menu_expand' => 'Expand Header Menu', + 'profile_menu' => 'Profile Menu', + 'view_profile' => 'View Profile', + 'edit_profile' => 'Edit Profile', + 'dark_mode' => 'Dark Mode', + 'light_mode' => 'Light Mode', + 'global_search' => 'Global Search', + + // Layout tabs + 'tab_info' => 'Info', + 'tab_info_label' => 'Tab: Show Secondary Information', + 'tab_content' => 'Content', + 'tab_content_label' => 'Tab: Show Primary Content', + + // Email Content + 'email_action_help' => 'If you’re having trouble clicking the ":actionText" button, copy and paste the URL below into your web browser:', + 'email_rights' => 'All rights reserved', + + // Footer Link Options + // Not directly used but available for convenience to users. + 'privacy_policy' => 'Privacy Policy', + 'terms_of_service' => 'Terms of Service', + + // OpenSearch + 'opensearch_description' => 'Search :appName', +]; diff --git a/lang/tk/components.php b/lang/tk/components.php new file mode 100644 index 00000000000..c33b1d0b791 --- /dev/null +++ b/lang/tk/components.php @@ -0,0 +1,46 @@ + 'Image Select', + 'image_list' => 'Image List', + 'image_details' => 'Image Details', + 'image_upload' => 'Upload Image', + 'image_intro' => 'Here you can select and manage images that have been previously uploaded to the system.', + 'image_intro_upload' => 'Upload a new image by dragging an image file into this window, or by using the "Upload Image" button above.', + 'image_all' => 'All', + 'image_all_title' => 'View all images', + 'image_book_title' => 'View images uploaded to this book', + 'image_page_title' => 'View images uploaded to this page', + 'image_search_hint' => 'Search by image name', + 'image_uploaded' => 'Uploaded :uploadedDate', + 'image_uploaded_by' => 'Uploaded by :userName', + 'image_uploaded_to' => 'Uploaded to :pageLink', + 'image_updated' => 'Updated :updateDate', + 'image_load_more' => 'Load More', + 'image_image_name' => 'Image Name', + 'image_delete_used' => 'This image is used in the pages below.', + 'image_delete_confirm_text' => 'Are you sure you want to delete this image?', + 'image_select_image' => 'Select Image', + 'image_dropzone' => 'Drop images or click here to upload', + 'image_dropzone_drop' => 'Drop images here to upload', + 'images_deleted' => 'Images Deleted', + 'image_preview' => 'Image Preview', + 'image_upload_success' => 'Image uploaded successfully', + 'image_update_success' => 'Image details successfully updated', + 'image_delete_success' => 'Image successfully deleted', + 'image_replace' => 'Replace Image', + 'image_replace_success' => 'Image file successfully updated', + 'image_rebuild_thumbs' => 'Regenerate Size Variations', + 'image_rebuild_thumbs_success' => 'Image size variations successfully rebuilt!', + + // Code Editor + 'code_editor' => 'Edit Code', + 'code_language' => 'Code Language', + 'code_content' => 'Code Content', + 'code_session_history' => 'Session History', + 'code_save' => 'Save Code', +]; diff --git a/lang/tk/editor.php b/lang/tk/editor.php new file mode 100644 index 00000000000..0d250e9a7bd --- /dev/null +++ b/lang/tk/editor.php @@ -0,0 +1,182 @@ + 'General', + 'advanced' => 'Advanced', + 'none' => 'None', + 'cancel' => 'Cancel', + 'save' => 'Save', + 'close' => 'Close', + 'apply' => 'Apply', + 'undo' => 'Undo', + 'redo' => 'Redo', + 'left' => 'Left', + 'center' => 'Center', + 'right' => 'Right', + 'top' => 'Top', + 'middle' => 'Middle', + 'bottom' => 'Bottom', + 'width' => 'Width', + 'height' => 'Height', + 'More' => 'More', + 'select' => 'Select...', + + // Toolbar + 'formats' => 'Formats', + 'header_large' => 'Large Header', + 'header_medium' => 'Medium Header', + 'header_small' => 'Small Header', + 'header_tiny' => 'Tiny Header', + 'paragraph' => 'Paragraph', + 'blockquote' => 'Blockquote', + 'inline_code' => 'Inline code', + 'callouts' => 'Callouts', + 'callout_information' => 'Information', + 'callout_success' => 'Success', + 'callout_warning' => 'Warning', + 'callout_danger' => 'Danger', + 'bold' => 'Bold', + 'italic' => 'Italic', + 'underline' => 'Underline', + 'strikethrough' => 'Strikethrough', + 'superscript' => 'Superscript', + 'subscript' => 'Subscript', + 'text_color' => 'Text color', + 'highlight_color' => 'Highlight color', + 'custom_color' => 'Custom color', + 'remove_color' => 'Remove color', + 'background_color' => 'Background color', + 'align_left' => 'Align left', + 'align_center' => 'Align center', + 'align_right' => 'Align right', + 'align_justify' => 'Justify', + 'list_bullet' => 'Bullet list', + 'list_numbered' => 'Numbered list', + 'list_task' => 'Task list', + 'indent_increase' => 'Increase indent', + 'indent_decrease' => 'Decrease indent', + 'table' => 'Table', + 'insert_image' => 'Insert image', + 'insert_image_title' => 'Insert/Edit Image', + 'insert_link' => 'Insert/edit link', + 'insert_link_title' => 'Insert/Edit Link', + 'insert_horizontal_line' => 'Insert horizontal line', + 'insert_code_block' => 'Insert code block', + 'edit_code_block' => 'Edit code block', + 'insert_drawing' => 'Insert/edit drawing', + 'drawing_manager' => 'Drawing manager', + 'insert_media' => 'Insert/edit media', + 'insert_media_title' => 'Insert/Edit Media', + 'clear_formatting' => 'Clear formatting', + 'source_code' => 'Source code', + 'source_code_title' => 'Source Code', + 'fullscreen' => 'Fullscreen', + 'image_options' => 'Image options', + + // Tables + 'table_properties' => 'Table properties', + 'table_properties_title' => 'Table Properties', + 'delete_table' => 'Delete table', + 'table_clear_formatting' => 'Clear table formatting', + 'resize_to_contents' => 'Resize to contents', + 'row_header' => 'Row header', + 'insert_row_before' => 'Insert row before', + 'insert_row_after' => 'Insert row after', + 'delete_row' => 'Delete row', + 'insert_column_before' => 'Insert column before', + 'insert_column_after' => 'Insert column after', + 'delete_column' => 'Delete column', + 'table_cell' => 'Cell', + 'table_row' => 'Row', + 'table_column' => 'Column', + 'cell_properties' => 'Cell properties', + 'cell_properties_title' => 'Cell Properties', + 'cell_type' => 'Cell type', + 'cell_type_cell' => 'Cell', + 'cell_scope' => 'Scope', + 'cell_type_header' => 'Header cell', + 'merge_cells' => 'Merge cells', + 'split_cell' => 'Split cell', + 'table_row_group' => 'Row Group', + 'table_column_group' => 'Column Group', + 'horizontal_align' => 'Horizontal align', + 'vertical_align' => 'Vertical align', + 'border_width' => 'Border width', + 'border_style' => 'Border style', + 'border_color' => 'Border color', + 'row_properties' => 'Row properties', + 'row_properties_title' => 'Row Properties', + 'cut_row' => 'Cut row', + 'copy_row' => 'Copy row', + 'paste_row_before' => 'Paste row before', + 'paste_row_after' => 'Paste row after', + 'row_type' => 'Row type', + 'row_type_header' => 'Header', + 'row_type_body' => 'Body', + 'row_type_footer' => 'Footer', + 'alignment' => 'Alignment', + 'cut_column' => 'Cut column', + 'copy_column' => 'Copy column', + 'paste_column_before' => 'Paste column before', + 'paste_column_after' => 'Paste column after', + 'cell_padding' => 'Cell padding', + 'cell_spacing' => 'Cell spacing', + 'caption' => 'Caption', + 'show_caption' => 'Show caption', + 'constrain' => 'Constrain proportions', + 'cell_border_solid' => 'Solid', + 'cell_border_dotted' => 'Dotted', + 'cell_border_dashed' => 'Dashed', + 'cell_border_double' => 'Double', + 'cell_border_groove' => 'Groove', + 'cell_border_ridge' => 'Ridge', + 'cell_border_inset' => 'Inset', + 'cell_border_outset' => 'Outset', + 'cell_border_none' => 'None', + 'cell_border_hidden' => 'Hidden', + + // Images, links, details/summary & embed + 'source' => 'Source', + 'alt_desc' => 'Alternative description', + 'embed' => 'Embed', + 'paste_embed' => 'Paste your embed code below:', + 'url' => 'URL', + 'text_to_display' => 'Text to display', + 'title' => 'Title', + 'browse_links' => 'Browse links', + 'open_link' => 'Open link', + 'open_link_in' => 'Open link in...', + 'open_link_current' => 'Current window', + 'open_link_new' => 'New window', + 'remove_link' => 'Remove link', + 'insert_collapsible' => 'Insert collapsible block', + 'collapsible_unwrap' => 'Unwrap', + 'edit_label' => 'Edit label', + 'toggle_open_closed' => 'Toggle open/closed', + 'collapsible_edit' => 'Edit collapsible block', + 'toggle_label' => 'Toggle label', + + // About view + 'about' => 'About the editor', + 'about_title' => 'About the WYSIWYG Editor', + 'editor_license' => 'Editor License & Copyright', + 'editor_lexical_license' => 'This editor is built as a fork of :lexicalLink which is distributed under the MIT license.', + 'editor_lexical_license_link' => 'Full license details can be found here.', + 'editor_tiny_license' => 'This editor is built using :tinyLink which is provided under the MIT license.', + 'editor_tiny_license_link' => 'The copyright and license details of TinyMCE can be found here.', + 'save_continue' => 'Save Page & Continue', + 'callouts_cycle' => '(Keep pressing to toggle through types)', + 'link_selector' => 'Link to content', + 'shortcuts' => 'Shortcuts', + 'shortcut' => 'Shortcut', + 'shortcuts_intro' => 'The following shortcuts are available in the editor:', + 'windows_linux' => '(Windows/Linux)', + 'mac' => '(Mac)', + 'description' => 'Description', +]; diff --git a/lang/tk/entities.php b/lang/tk/entities.php new file mode 100644 index 00000000000..74c50be3b2f --- /dev/null +++ b/lang/tk/entities.php @@ -0,0 +1,476 @@ + 'Recently Created', + 'recently_created_pages' => 'Recently Created Pages', + 'recently_updated_pages' => 'Recently Updated Pages', + 'recently_created_chapters' => 'Recently Created Chapters', + 'recently_created_books' => 'Recently Created Books', + 'recently_created_shelves' => 'Recently Created Shelves', + 'recently_update' => 'Recently Updated', + 'recently_viewed' => 'Recently Viewed', + 'recent_activity' => 'Recent Activity', + 'create_now' => 'Create one now', + 'revisions' => 'Revisions', + 'meta_revision' => 'Revision #:revisionCount', + 'meta_created' => 'Created :timeLength', + 'meta_created_name' => 'Created :timeLength by :user', + 'meta_updated' => 'Updated :timeLength', + 'meta_updated_name' => 'Updated :timeLength by :user', + 'meta_owned_name' => 'Owned by :user', + 'meta_reference_count' => 'Referenced by :count item|Referenced by :count items', + 'entity_select' => 'Entity Select', + 'entity_select_lack_permission' => 'You don\'t have the required permissions to select this item', + 'images' => 'Images', + 'my_recent_drafts' => 'My Recent Drafts', + 'my_recently_viewed' => 'My Recently Viewed', + 'my_most_viewed_favourites' => 'My Most Viewed Favourites', + 'my_favourites' => 'My Favourites', + 'no_pages_viewed' => 'You have not viewed any pages', + 'no_pages_recently_created' => 'No pages have been recently created', + 'no_pages_recently_updated' => 'No pages have been recently updated', + 'export' => 'Export', + 'export_html' => 'Contained Web File', + 'export_pdf' => 'PDF File', + 'export_text' => 'Plain Text File', + 'export_md' => 'Markdown File', + 'export_zip' => 'Portable ZIP', + 'default_template' => 'Default Page Template', + 'default_template_explain' => 'Assign a page template that will be used as the default content for all pages created within this item. Keep in mind this will only be used if the page creator has view access to the chosen template page.', + 'default_template_select' => 'Select a template page', + 'import' => 'Import', + 'import_validate' => 'Validate Import', + 'import_desc' => 'Import books, chapters & pages using a portable zip export from the same, or a different, instance. Select a ZIP file to proceed. After the file has been uploaded and validated you\'ll be able to configure & confirm the import in the next view.', + '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_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_size' => ':size Import ZIP Size', + 'import_uploaded_at' => 'Uploaded :relativeTime', + 'import_uploaded_by' => 'Uploaded by', + 'import_location' => 'Import Location', + '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_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', + 'breadcrumb_siblings_for_book' => 'Navigate siblings for book', + 'breadcrumb_siblings_for_bookshelf' => 'Navigate siblings for shelf', + + // Permissions and restrictions + 'permissions' => 'Permissions', + 'permissions_desc' => 'Set permissions here to override the default permissions provided by user roles.', + 'permissions_book_cascade' => 'Permissions set on books will automatically cascade to child chapters and pages, unless they have their own permissions defined.', + 'permissions_chapter_cascade' => 'Permissions set on chapters will automatically cascade to child pages, unless they have their own permissions defined.', + 'permissions_save' => 'Save Permissions', + 'permissions_owner' => 'Owner', + 'permissions_role_everyone_else' => 'Everyone Else', + 'permissions_role_everyone_else_desc' => 'Set permissions for all roles not specifically overridden.', + 'permissions_role_override' => 'Override permissions for role', + 'permissions_inherit_defaults' => 'Inherit defaults', + + // Search + 'search_results' => 'Search Results', + 'search_total_results_found' => ':count result found|:count total results found', + 'search_clear' => 'Clear Search', + 'search_no_pages' => 'No pages matched this search', + 'search_for_term' => 'Search for :term', + 'search_more' => 'More Results', + 'search_advanced' => 'Advanced Search', + 'search_terms' => 'Search Terms', + 'search_content_type' => 'Content Type', + 'search_exact_matches' => 'Exact Matches', + 'search_tags' => 'Tag Searches', + 'search_options' => 'Options', + 'search_viewed_by_me' => 'Viewed by me', + 'search_not_viewed_by_me' => 'Not viewed by me', + 'search_permissions_set' => 'Permissions set', + 'search_created_by_me' => 'Created by me', + 'search_updated_by_me' => 'Updated by me', + 'search_owned_by_me' => 'Owned by me', + 'search_date_options' => 'Date Options', + 'search_updated_before' => 'Updated before', + 'search_updated_after' => 'Updated after', + 'search_created_before' => 'Created before', + 'search_created_after' => 'Created after', + 'search_set_date' => 'Set Date', + 'search_update' => 'Update Search', + + // Shelves + 'shelf' => 'Shelf', + 'shelves' => 'Shelves', + 'x_shelves' => ':count Shelf|:count Shelves', + 'shelves_empty' => 'No shelves have been created', + 'shelves_create' => 'Create New Shelf', + 'shelves_popular' => 'Popular Shelves', + 'shelves_new' => 'New Shelves', + 'shelves_new_action' => 'New Shelf', + 'shelves_popular_empty' => 'The most popular shelves will appear here.', + 'shelves_new_empty' => 'The most recently created shelves will appear here.', + 'shelves_save' => 'Save Shelf', + 'shelves_books' => 'Books on this shelf', + 'shelves_add_books' => 'Add books to this shelf', + 'shelves_drag_books' => 'Drag books below to add them to this shelf', + 'shelves_empty_contents' => 'This shelf has no books assigned to it', + 'shelves_edit_and_assign' => 'Edit shelf to assign books', + 'shelves_edit_named' => 'Edit Shelf :name', + 'shelves_edit' => 'Edit Shelf', + 'shelves_delete' => 'Delete Shelf', + 'shelves_delete_named' => 'Delete Shelf :name', + 'shelves_delete_explain' => "This will delete the shelf with the name ':name'. Contained books will not be deleted.", + 'shelves_delete_confirmation' => 'Are you sure you want to delete this shelf?', + 'shelves_permissions' => 'Shelf Permissions', + 'shelves_permissions_updated' => 'Shelf Permissions Updated', + 'shelves_permissions_active' => 'Shelf Permissions Active', + 'shelves_permissions_cascade_warning' => 'Permissions on shelves do not automatically cascade to contained books. This is because a book can exist on multiple shelves. Permissions can however be copied down to child books using the option found below.', + 'shelves_permissions_create' => 'Shelf create permissions are only used for copying permissions to child books using the action below. They do not control the ability to create books.', + 'shelves_copy_permissions_to_books' => 'Copy Permissions to Books', + 'shelves_copy_permissions' => 'Copy Permissions', + 'shelves_copy_permissions_explain' => 'This will apply the current permission settings of this shelf to all books contained within. Before activating, ensure any changes to the permissions of this shelf have been saved.', + 'shelves_copy_permission_success' => 'Shelf permissions copied to :count books', + + // Books + 'book' => 'Book', + 'books' => 'Books', + 'x_books' => ':count Book|:count Books', + 'books_empty' => 'No books have been created', + 'books_popular' => 'Popular Books', + 'books_recent' => 'Recent Books', + 'books_new' => 'New Books', + 'books_new_action' => 'New Book', + 'books_popular_empty' => 'The most popular books will appear here.', + 'books_new_empty' => 'The most recently created books will appear here.', + 'books_create' => 'Create New Book', + 'books_delete' => 'Delete Book', + 'books_delete_named' => 'Delete Book :bookName', + 'books_delete_explain' => 'This will delete the book with the name \':bookName\'. All pages and chapters will be removed.', + 'books_delete_confirmation' => 'Are you sure you want to delete this book?', + 'books_edit' => 'Edit Book', + 'books_edit_named' => 'Edit Book :bookName', + 'books_form_book_name' => 'Book Name', + 'books_save' => 'Save Book', + 'books_permissions' => 'Book Permissions', + 'books_permissions_updated' => 'Book Permissions Updated', + 'books_empty_contents' => 'No pages or chapters have been created for this book.', + 'books_empty_create_page' => 'Create a new page', + 'books_empty_sort_current_book' => 'Sort the current book', + 'books_empty_add_chapter' => 'Add a chapter', + 'books_permissions_active' => 'Book Permissions Active', + 'books_search_this' => 'Search this book', + 'books_navigation' => 'Book Navigation', + 'books_sort' => 'Sort Book Contents', + 'books_sort_desc' => 'Move chapters and pages within a book to reorganise its contents. Other books can be added which allows easy moving of chapters and pages between books. Optionally an auto sort rule can be set to automatically sort this book\'s contents upon changes.', + 'books_sort_auto_sort' => 'Auto Sort Option', + 'books_sort_auto_sort_active' => 'Auto Sort Active: :sortName', + 'books_sort_named' => 'Sort Book :bookName', + 'books_sort_name' => 'Sort by Name', + 'books_sort_created' => 'Sort by Created Date', + 'books_sort_updated' => 'Sort by Updated Date', + 'books_sort_chapters_first' => 'Chapters First', + 'books_sort_chapters_last' => 'Chapters Last', + 'books_sort_show_other' => 'Show Other Books', + 'books_sort_save' => 'Save New Order', + 'books_sort_show_other_desc' => 'Add other books here to include them in the sort operation, and allow easy cross-book reorganisation.', + 'books_sort_move_up' => 'Move Up', + 'books_sort_move_down' => 'Move Down', + 'books_sort_move_prev_book' => 'Move to Previous Book', + 'books_sort_move_next_book' => 'Move to Next Book', + 'books_sort_move_prev_chapter' => 'Move Into Previous Chapter', + 'books_sort_move_next_chapter' => 'Move Into Next Chapter', + 'books_sort_move_book_start' => 'Move to Start of Book', + 'books_sort_move_book_end' => 'Move to End of Book', + 'books_sort_move_before_chapter' => 'Move to Before Chapter', + 'books_sort_move_after_chapter' => 'Move to After Chapter', + 'books_copy' => 'Copy Book', + 'books_copy_success' => 'Book successfully copied', + + // Chapters + 'chapter' => 'Chapter', + 'chapters' => 'Chapters', + 'x_chapters' => ':count Chapter|:count Chapters', + 'chapters_popular' => 'Popular Chapters', + 'chapters_new' => 'New Chapter', + 'chapters_create' => 'Create New Chapter', + 'chapters_delete' => 'Delete Chapter', + 'chapters_delete_named' => 'Delete Chapter :chapterName', + 'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages that exist within this chapter will also be deleted.', + 'chapters_delete_confirm' => 'Are you sure you want to delete this chapter?', + 'chapters_edit' => 'Edit Chapter', + 'chapters_edit_named' => 'Edit Chapter :chapterName', + 'chapters_save' => 'Save Chapter', + 'chapters_move' => 'Move Chapter', + 'chapters_move_named' => 'Move Chapter :chapterName', + 'chapters_copy' => 'Copy Chapter', + 'chapters_copy_success' => 'Chapter successfully copied', + 'chapters_permissions' => 'Chapter Permissions', + 'chapters_empty' => 'No pages are currently in this chapter.', + 'chapters_permissions_active' => 'Chapter Permissions Active', + 'chapters_permissions_success' => 'Chapter Permissions Updated', + 'chapters_search_this' => 'Search this chapter', + 'chapter_sort_book' => 'Sort Book', + + // Pages + 'page' => 'Page', + 'pages' => 'Pages', + 'x_pages' => ':count Page|:count Pages', + 'pages_popular' => 'Popular Pages', + 'pages_new' => 'New Page', + 'pages_attachments' => 'Attachments', + 'pages_navigation' => 'Page Navigation', + 'pages_delete' => 'Delete Page', + 'pages_delete_named' => 'Delete Page :pageName', + 'pages_delete_draft_named' => 'Delete Draft Page :pageName', + 'pages_delete_draft' => 'Delete Draft Page', + 'pages_delete_success' => 'Page deleted', + 'pages_delete_draft_success' => 'Draft page deleted', + 'pages_delete_warning_template' => 'This page is in active use as a book or chapter default page template. These books or chapters will no longer have a default page template assigned after this page is deleted.', + 'pages_delete_confirm' => 'Are you sure you want to delete this page?', + 'pages_delete_draft_confirm' => 'Are you sure you want to delete this draft page?', + 'pages_editing_named' => 'Editing Page :pageName', + 'pages_edit_draft_options' => 'Draft Options', + 'pages_edit_save_draft' => 'Save Draft', + 'pages_edit_draft' => 'Edit Page Draft', + 'pages_editing_draft' => 'Editing Draft', + 'pages_editing_page' => 'Editing Page', + 'pages_edit_draft_save_at' => 'Draft saved at ', + 'pages_edit_delete_draft' => 'Delete Draft', + 'pages_edit_delete_draft_confirm' => 'Are you sure you want to delete your draft page changes? All of your changes, since the last full save, will be lost and the editor will be updated with the latest page non-draft save state.', + 'pages_edit_discard_draft' => 'Discard Draft', + 'pages_edit_switch_to_markdown' => 'Switch to Markdown Editor', + 'pages_edit_switch_to_markdown_clean' => '(Clean Content)', + 'pages_edit_switch_to_markdown_stable' => '(Stable Content)', + 'pages_edit_switch_to_wysiwyg' => 'Switch to WYSIWYG Editor', + 'pages_edit_switch_to_new_wysiwyg' => 'Switch to new WYSIWYG', + 'pages_edit_switch_to_new_wysiwyg_desc' => '(In Beta Testing)', + 'pages_edit_set_changelog' => 'Set Changelog', + 'pages_edit_enter_changelog_desc' => 'Enter a brief description of the changes you\'ve made', + 'pages_edit_enter_changelog' => 'Enter Changelog', + 'pages_editor_switch_title' => 'Switch Editor', + 'pages_editor_switch_are_you_sure' => 'Are you sure you want to change the editor for this page?', + 'pages_editor_switch_consider_following' => 'Consider the following when changing editors:', + 'pages_editor_switch_consideration_a' => 'Once saved, the new editor option will be used by any future editors, including those that may not be able to change editor type themselves.', + 'pages_editor_switch_consideration_b' => 'This can potentially lead to a loss of detail and syntax in certain circumstances.', + 'pages_editor_switch_consideration_c' => 'Tag or changelog changes, made since last save, won\'t persist across this change.', + 'pages_save' => 'Save Page', + 'pages_title' => 'Page Title', + 'pages_name' => 'Page Name', + 'pages_md_editor' => 'Editor', + 'pages_md_preview' => 'Preview', + 'pages_md_insert_image' => 'Insert Image', + 'pages_md_insert_link' => 'Insert Entity Link', + 'pages_md_insert_drawing' => 'Insert Drawing', + 'pages_md_show_preview' => 'Show preview', + 'pages_md_sync_scroll' => 'Sync preview scroll', + 'pages_md_plain_editor' => 'Plaintext editor', + 'pages_drawing_unsaved' => 'Unsaved Drawing Found', + 'pages_drawing_unsaved_confirm' => 'Unsaved drawing data was found from a previous failed drawing save attempt. Would you like to restore and continue editing this unsaved drawing?', + 'pages_not_in_chapter' => 'Page is not in a chapter', + 'pages_move' => 'Move Page', + 'pages_copy' => 'Copy Page', + 'pages_copy_desination' => 'Copy Destination', + 'pages_copy_success' => 'Page successfully copied', + 'pages_permissions' => 'Page Permissions', + 'pages_permissions_success' => 'Page permissions updated', + 'pages_revision' => 'Revision', + 'pages_revisions' => 'Page Revisions', + 'pages_revisions_desc' => 'Listed below are all the past revisions of this page. You can look back upon, compare, and restore old page versions if permissions allow. The full history of the page may not be fully reflected here since, depending on system configuration, old revisions could be auto-deleted.', + 'pages_revisions_named' => 'Page Revisions for :pageName', + 'pages_revision_named' => 'Page Revision for :pageName', + 'pages_revision_restored_from' => 'Restored from #:id; :summary', + 'pages_revisions_created_by' => 'Created By', + 'pages_revisions_date' => 'Revision Date', + 'pages_revisions_number' => '#', + 'pages_revisions_sort_number' => 'Revision Number', + 'pages_revisions_numbered' => 'Revision #:id', + 'pages_revisions_numbered_changes' => 'Revision #:id Changes', + 'pages_revisions_editor' => 'Editor Type', + 'pages_revisions_changelog' => 'Changelog', + 'pages_revisions_changes' => 'Changes', + 'pages_revisions_current' => 'Current Version', + 'pages_revisions_preview' => 'Preview', + 'pages_revisions_restore' => 'Restore', + 'pages_revisions_none' => 'This page has no revisions', + 'pages_copy_link' => 'Copy Link', + 'pages_edit_content_link' => 'Jump to section in editor', + 'pages_pointer_enter_mode' => 'Enter section select mode', + 'pages_pointer_label' => 'Page Section Options', + 'pages_pointer_permalink' => 'Page Section Permalink', + 'pages_pointer_include_tag' => 'Page Section Include Tag', + 'pages_pointer_toggle_link' => 'Permalink mode, Press to show include tag', + 'pages_pointer_toggle_include' => 'Include tag mode, Press to show permalink', + 'pages_permissions_active' => 'Page Permissions Active', + 'pages_initial_revision' => 'Initial publish', + 'pages_references_update_revision' => 'System auto-update of internal links', + 'pages_initial_name' => 'New Page', + 'pages_editing_draft_notification' => 'You are currently editing a draft that was last saved :timeDiff.', + 'pages_draft_edited_notification' => 'This page has been updated by since that time. It is recommended that you discard this draft.', + 'pages_draft_page_changed_since_creation' => 'This page has been updated since this draft was created. It is recommended that you discard this draft or take care not to overwrite any page changes.', + 'pages_draft_edit_active' => [ + 'start_a' => ':count users have started editing this page', + 'start_b' => ':userName has started editing this page', + 'time_a' => 'since the page was last updated', + 'time_b' => 'in the last :minCount minutes', + 'message' => ':start :time. Take care not to overwrite each other\'s updates!', + ], + 'pages_draft_discarded' => 'Draft discarded! The editor has been updated with the current page content', + 'pages_draft_deleted' => 'Draft deleted! The editor has been updated with the current page content', + 'pages_specific' => 'Specific Page', + 'pages_is_template' => 'Page Template', + + // Editor Sidebar + 'toggle_sidebar' => 'Toggle Sidebar', + 'page_tags' => 'Page Tags', + 'chapter_tags' => 'Chapter Tags', + 'book_tags' => 'Book Tags', + 'shelf_tags' => 'Shelf Tags', + 'tag' => 'Tag', + 'tags' => 'Tags', + 'tags_index_desc' => 'Tags can be applied to content within the system to apply a flexible form of categorization. Tags can have both a key and value, with the value being optional. Once applied, content can then be queried using the tag name and value.', + 'tag_name' => 'Tag Name', + 'tag_value' => 'Tag Value (Optional)', + 'tags_explain' => "Add some tags to better categorise your content. \n You can assign a value to a tag for more in-depth organisation.", + 'tags_add' => 'Add another tag', + 'tags_remove' => 'Remove this tag', + 'tags_usages' => 'Total tag usages', + 'tags_assigned_pages' => 'Assigned to Pages', + 'tags_assigned_chapters' => 'Assigned to Chapters', + 'tags_assigned_books' => 'Assigned to Books', + 'tags_assigned_shelves' => 'Assigned to Shelves', + 'tags_x_unique_values' => ':count unique values', + 'tags_all_values' => 'All values', + 'tags_view_tags' => 'View Tags', + 'tags_view_existing_tags' => 'View existing tags', + 'tags_list_empty_hint' => 'Tags can be assigned via the page editor sidebar or while editing the details of a book, chapter or shelf.', + 'attachments' => 'Attachments', + 'attachments_explain' => 'Upload some files or attach some links to display on your page. These are visible in the page sidebar.', + 'attachments_explain_instant_save' => 'Changes here are saved instantly.', + 'attachments_upload' => 'Upload File', + 'attachments_link' => 'Attach Link', + 'attachments_upload_drop' => 'Alternatively you can drag and drop a file here to upload it as an attachment.', + 'attachments_set_link' => 'Set Link', + 'attachments_delete' => 'Are you sure you want to delete this attachment?', + 'attachments_dropzone' => 'Drop files here to upload', + 'attachments_no_files' => 'No files have been uploaded', + 'attachments_explain_link' => 'You can attach a link if you\'d prefer not to upload a file. This can be a link to another page or a link to a file in the cloud.', + 'attachments_link_name' => 'Link Name', + 'attachment_link' => 'Attachment link', + 'attachments_link_url' => 'Link to file', + 'attachments_link_url_hint' => 'Url of site or file', + 'attach' => 'Attach', + 'attachments_insert_link' => 'Add Attachment Link to Page', + 'attachments_edit_file' => 'Edit File', + 'attachments_edit_file_name' => 'File Name', + 'attachments_edit_drop_upload' => 'Drop files or click here to upload and overwrite', + 'attachments_order_updated' => 'Attachment order updated', + 'attachments_updated_success' => 'Attachment details updated', + 'attachments_deleted' => 'Attachment deleted', + 'attachments_file_uploaded' => 'File successfully uploaded', + 'attachments_file_updated' => 'File successfully updated', + 'attachments_link_attached' => 'Link successfully attached to page', + 'templates' => 'Templates', + 'templates_set_as_template' => 'Page is a template', + 'templates_explain_set_as_template' => 'You can set this page as a template so its contents be utilized when creating other pages. Other users will be able to use this template if they have view permissions for this page.', + 'templates_replace_content' => 'Replace page content', + 'templates_append_content' => 'Append to page content', + 'templates_prepend_content' => 'Prepend to page content', + + // Profile View + 'profile_user_for_x' => 'User for :time', + 'profile_created_content' => 'Created Content', + 'profile_not_created_pages' => ':userName has not created any pages', + 'profile_not_created_chapters' => ':userName has not created any chapters', + 'profile_not_created_books' => ':userName has not created any books', + 'profile_not_created_shelves' => ':userName has not created any shelves', + + // Comments + 'comment' => 'Comment', + 'comments' => 'Comments', + 'comment_add' => 'Add Comment', + 'comment_none' => 'No comments to display', + 'comment_placeholder' => 'Leave a comment here', + 'comment_thread_count' => ':count Comment Thread|:count Comment Threads', + 'comment_archived_count' => ':count Archived', + 'comment_archived_threads' => 'Archived Threads', + 'comment_save' => 'Save Comment', + 'comment_new' => 'New Comment', + 'comment_created' => 'commented :createDiff', + 'comment_updated' => 'Updated :updateDiff by :username', + 'comment_updated_indicator' => 'Updated', + 'comment_deleted_success' => 'Comment deleted', + 'comment_created_success' => 'Comment added', + 'comment_updated_success' => 'Comment updated', + 'comment_archive_success' => 'Comment archived', + 'comment_unarchive_success' => 'Comment un-archived', + 'comment_view' => 'View comment', + 'comment_jump_to_thread' => 'Jump to thread', + 'comment_delete_confirm' => 'Are you sure you want to delete this comment?', + 'comment_in_reply_to' => 'In reply to :commentId', + 'comment_reference' => 'Reference', + 'comment_reference_outdated' => '(Outdated)', + 'comment_editor_explain' => 'Here are the comments that have been left on this page. Comments can be added & managed when viewing the saved page.', + + // Revision + 'revision_delete_confirm' => 'Are you sure you want to delete this revision?', + 'revision_restore_confirm' => 'Are you sure you want to restore this revision? The current page contents will be replaced.', + 'revision_cannot_delete_latest' => 'Cannot delete the latest revision.', + + // Copy view + 'copy_consider' => 'Please consider the below when copying content.', + 'copy_consider_permissions' => 'Custom permission settings will not be copied.', + 'copy_consider_owner' => 'You will become the owner of all copied content.', + 'copy_consider_images' => 'Page image files will not be duplicated & the original images will retain their relation to the page they were originally uploaded to.', + 'copy_consider_attachments' => 'Page attachments will not be copied.', + 'copy_consider_access' => 'A change of location, owner or permissions may result in this content being accessible to those previously without access.', + + // Conversions + 'convert_to_shelf' => 'Convert to Shelf', + 'convert_to_shelf_contents_desc' => 'You can convert this book to a new shelf with the same contents. Chapters contained within this book will be converted to new books. If this book contains any pages, that are not in a chapter, this book will be renamed and contain such pages, and this book will become part of the new shelf.', + 'convert_to_shelf_permissions_desc' => 'Any permissions set on this book will be copied to the new shelf and to all new child books that don\'t have their own permissions enforced. Note that permissions on shelves do not auto-cascade to content within, as they do for books.', + 'convert_book' => 'Convert Book', + 'convert_book_confirm' => 'Are you sure you want to convert this book?', + 'convert_undo_warning' => 'This cannot be as easily undone.', + 'convert_to_book' => 'Convert to Book', + 'convert_to_book_desc' => 'You can convert this chapter to a new book with the same contents. Any permissions set on this chapter will be copied to the new book but any inherited permissions, from the parent book, will not be copied which could lead to a change of access control.', + 'convert_chapter' => 'Convert Chapter', + 'convert_chapter_confirm' => 'Are you sure you want to convert this chapter?', + + // References + 'references' => 'References', + 'references_none' => 'There are no tracked references to this item.', + 'references_to_desc' => 'Listed below is all the known content in the system that links to this item.', + + // Watch Options + 'watch' => 'Watch', + 'watch_title_default' => 'Default Preferences', + 'watch_desc_default' => 'Revert watching to just your default notification preferences.', + 'watch_title_ignore' => 'Ignore', + 'watch_desc_ignore' => 'Ignore all notifications, including those from user-level preferences.', + 'watch_title_new' => 'New Pages', + 'watch_desc_new' => 'Notify when any new page is created within this item.', + 'watch_title_updates' => 'All Page Updates', + 'watch_desc_updates' => 'Notify upon all new pages and page changes.', + 'watch_desc_updates_page' => 'Notify upon all page changes.', + 'watch_title_comments' => 'All Page Updates & Comments', + 'watch_desc_comments' => 'Notify upon all new pages, page changes and new comments.', + 'watch_desc_comments_page' => 'Notify upon page changes and new comments.', + 'watch_change_default' => 'Change default notification preferences', + 'watch_detail_ignore' => 'Ignoring notifications', + 'watch_detail_new' => 'Watching for new pages', + 'watch_detail_updates' => 'Watching new pages and updates', + 'watch_detail_comments' => 'Watching new pages, updates & comments', + 'watch_detail_parent_book' => 'Watching via parent book', + 'watch_detail_parent_book_ignore' => 'Ignoring via parent book', + 'watch_detail_parent_chapter' => 'Watching via parent chapter', + 'watch_detail_parent_chapter_ignore' => 'Ignoring via parent chapter', +]; diff --git a/lang/tk/errors.php b/lang/tk/errors.php new file mode 100644 index 00000000000..9d738379648 --- /dev/null +++ b/lang/tk/errors.php @@ -0,0 +1,133 @@ + 'You do not have permission to access the requested page.', + 'permissionJson' => 'You do not have permission to perform the requested action.', + + // Auth + 'error_user_exists_different_creds' => 'A user with the email :email already exists but with different credentials.', + 'auth_pre_register_theme_prevention' => 'User account could not be registered for the provided details', + 'email_already_confirmed' => 'Email has already been confirmed, Try logging in.', + 'email_confirmation_invalid' => 'This confirmation token is not valid or has already been used, Please try registering again.', + 'email_confirmation_expired' => 'The confirmation token has expired, A new confirmation email has been sent.', + 'email_confirmation_awaiting' => 'The email address for the account in use needs to be confirmed', + 'ldap_fail_anonymous' => 'LDAP access failed using anonymous bind', + 'ldap_fail_authed' => 'LDAP access failed using given dn & password details', + 'ldap_extension_not_installed' => 'LDAP PHP extension not installed', + 'ldap_cannot_connect' => 'Cannot connect to ldap server, Initial connection failed', + 'saml_already_logged_in' => 'Already logged in', + 'saml_no_email_address' => 'Could not find an email address, for this user, in the data provided by the external authentication system', + 'saml_invalid_response_id' => 'The request from the external authentication system is not recognised by a process started by this application. Navigating back after a login could cause this issue.', + 'saml_fail_authed' => 'Login using :system failed, system did not provide successful authorization', + 'oidc_already_logged_in' => 'Already logged in', + 'oidc_no_email_address' => 'Could not find an email address, for this user, in the data provided by the external authentication system', + 'oidc_fail_authed' => 'Login using :system failed, system did not provide successful authorization', + 'social_no_action_defined' => 'No action defined', + 'social_login_bad_response' => "Error received during :socialAccount login: \n:error", + 'social_account_in_use' => 'This :socialAccount account is already in use, Try logging in via the :socialAccount option.', + 'social_account_email_in_use' => 'The email :email is already in use. If you already have an account you can connect your :socialAccount account from your profile settings.', + 'social_account_existing' => 'This :socialAccount is already attached to your profile.', + 'social_account_already_used_existing' => 'This :socialAccount account is already used by another user.', + 'social_account_not_used' => 'This :socialAccount account is not linked to any users. Please attach it in your profile settings. ', + 'social_account_register_instructions' => 'If you do not yet have an account, You can register an account using the :socialAccount option.', + 'social_driver_not_found' => 'Social driver not found', + 'social_driver_not_configured' => 'Your :socialAccount social settings are not configured correctly.', + 'invite_token_expired' => 'This invitation link has expired. You can instead try to reset your account password.', + 'login_user_not_found' => 'A user for this action could not be found.', + + // System + 'path_not_writable' => 'File path :filePath could not be uploaded to. Ensure it is writable to the server.', + 'cannot_get_image_from_url' => 'Cannot get image from :url', + 'cannot_create_thumbs' => 'The server cannot create thumbnails. Please check you have the GD PHP extension installed.', + 'server_upload_limit' => 'The server does not allow uploads of this size. Please try a smaller file size.', + 'server_post_limit' => 'The server cannot receive the provided amount of data. Try again with less data or a smaller file.', + 'uploaded' => 'The server does not allow uploads of this size. Please try a smaller file size.', + + // Drawing & Images + 'image_upload_error' => 'An error occurred uploading the image', + 'image_upload_type_error' => 'The image type being uploaded is invalid', + 'image_upload_replace_type' => 'Image file replacements must be of the same type', + 'image_upload_memory_limit' => 'Failed to handle image upload and/or create thumbnails due to system resource limits.', + 'image_thumbnail_memory_limit' => 'Failed to create image size variations due to system resource limits.', + 'image_gallery_thumbnail_memory_limit' => 'Failed to create gallery thumbnails due to system resource limits.', + 'drawing_data_not_found' => 'Drawing data could not be loaded. The drawing file might no longer exist or you may not have permission to access it.', + + // Attachments + 'attachment_not_found' => 'Attachment not found', + 'attachment_upload_error' => 'An error occurred uploading the attachment file', + + // Pages + 'page_draft_autosave_fail' => 'Failed to save draft. Ensure you have internet connection before saving this page', + 'page_draft_delete_fail' => 'Failed to delete page draft and fetch current page saved content', + 'page_custom_home_deletion' => 'Cannot delete a page while it is set as a homepage', + + // Entities + 'entity_not_found' => 'Entity not found', + 'bookshelf_not_found' => 'Shelf not found', + 'book_not_found' => 'Book not found', + 'page_not_found' => 'Page not found', + 'chapter_not_found' => 'Chapter not found', + 'selected_book_not_found' => 'The selected book was not found', + 'selected_book_chapter_not_found' => 'The selected Book or Chapter was not found', + 'guests_cannot_save_drafts' => 'Guests cannot save drafts', + + // Users + 'users_cannot_delete_only_admin' => 'You cannot delete the only admin', + 'users_cannot_delete_guest' => 'You cannot delete the guest user', + 'users_could_not_send_invite' => 'Could not create user since invite email failed to send', + + // Roles + 'role_cannot_be_edited' => 'This role cannot be edited', + 'role_system_cannot_be_deleted' => 'This role is a system role and cannot be deleted', + 'role_registration_default_cannot_delete' => 'This role cannot be deleted while set as the default registration role', + 'role_cannot_remove_only_admin' => 'This user is the only user assigned to the administrator role. Assign the administrator role to another user before attempting to remove it here.', + + // Comments + 'comment_list' => 'An error occurred while fetching the comments.', + 'cannot_add_comment_to_draft' => 'You cannot add comments to a draft.', + 'comment_add' => 'An error occurred while adding / updating the comment.', + 'comment_delete' => 'An error occurred while deleting the comment.', + 'empty_comment' => 'Cannot add an empty comment.', + + // Error pages + '404_page_not_found' => 'Page Not Found', + 'sorry_page_not_found' => 'Sorry, The page you were looking for could not be found.', + 'sorry_page_not_found_permission_warning' => 'If you expected this page to exist, you might not have permission to view it.', + 'image_not_found' => 'Image Not Found', + 'image_not_found_subtitle' => 'Sorry, The image file you were looking for could not be found.', + 'image_not_found_details' => 'If you expected this image to exist it might have been deleted.', + 'return_home' => 'Return to home', + 'error_occurred' => 'An Error Occurred', + 'app_down' => ':appName is down right now', + 'back_soon' => 'It will be back up soon.', + + // Import + 'import_zip_cant_read' => 'Could not read ZIP file.', + 'import_zip_cant_decode_data' => 'Could not find and decode ZIP data.json content.', + 'import_zip_no_data' => 'ZIP file data has no expected book, chapter or page content.', + 'import_validation_failed' => 'Import ZIP failed to validate with errors:', + 'import_zip_failed_notification' => 'Failed to import ZIP file.', + 'import_perms_books' => 'You are lacking the required permissions to create books.', + 'import_perms_chapters' => 'You are lacking the required permissions to create chapters.', + 'import_perms_pages' => 'You are lacking the required permissions to create pages.', + 'import_perms_images' => 'You are lacking the required permissions to create images.', + 'import_perms_attachments' => 'You are lacking the required permission to create attachments.', + + // API errors + 'api_no_authorization_found' => 'No authorization token found on the request', + 'api_bad_authorization_format' => 'An authorization token was found on the request but the format appeared incorrect', + 'api_user_token_not_found' => 'No matching API token was found for the provided authorization token', + 'api_incorrect_token_secret' => 'The secret provided for the given used API token is incorrect', + 'api_user_no_api_permission' => 'The owner of the used API token does not have permission to make API calls', + 'api_user_token_expired' => 'The authorization token used has expired', + + // Settings & Maintenance + 'maintenance_test_email_failure' => 'Error thrown when sending a test email:', + + // HTTP errors + 'http_ssr_url_no_match' => 'The URL does not match the configured allowed SSR hosts', +]; diff --git a/lang/tk/notifications.php b/lang/tk/notifications.php new file mode 100644 index 00000000000..1afd23f1dc4 --- /dev/null +++ b/lang/tk/notifications.php @@ -0,0 +1,27 @@ + 'New comment on page: :pageName', + 'new_comment_intro' => 'A user has commented on a page in :appName:', + 'new_page_subject' => 'New page: :pageName', + 'new_page_intro' => 'A new page has been created in :appName:', + 'updated_page_subject' => 'Updated page: :pageName', + 'updated_page_intro' => 'A page has been updated in :appName:', + 'updated_page_debounce' => 'To prevent a mass of notifications, for a while you won\'t be sent notifications for further edits to this page by the same editor.', + + 'detail_page_name' => 'Page Name:', + 'detail_page_path' => 'Page Path:', + 'detail_commenter' => 'Commenter:', + 'detail_comment' => 'Comment:', + 'detail_created_by' => 'Created By:', + 'detail_updated_by' => 'Updated By:', + + 'action_view_comment' => 'View Comment', + 'action_view_page' => 'View Page', + + 'footer_reason' => 'This notification was sent to you because :link cover this type of activity for this item.', + 'footer_reason_link' => 'your notification preferences', +]; diff --git a/lang/tk/pagination.php b/lang/tk/pagination.php new file mode 100644 index 00000000000..85bd12fc319 --- /dev/null +++ b/lang/tk/pagination.php @@ -0,0 +1,12 @@ + '« Previous', + 'next' => 'Next »', + +]; diff --git a/lang/tk/passwords.php b/lang/tk/passwords.php new file mode 100644 index 00000000000..b408f3c2fda --- /dev/null +++ b/lang/tk/passwords.php @@ -0,0 +1,15 @@ + 'Passwords must be at least eight characters and match the confirmation.', + 'user' => "We can't find a user with that e-mail address.", + 'token' => 'The password reset token is invalid for this email address.', + 'sent' => 'We have e-mailed your password reset link!', + 'reset' => 'Your password has been reset!', + +]; diff --git a/lang/tk/preferences.php b/lang/tk/preferences.php new file mode 100644 index 00000000000..2872f5f3c65 --- /dev/null +++ b/lang/tk/preferences.php @@ -0,0 +1,51 @@ + 'My Account', + + 'shortcuts' => 'Shortcuts', + 'shortcuts_interface' => 'UI Shortcut Preferences', + 'shortcuts_toggle_desc' => 'Here you can enable or disable keyboard system interface shortcuts, used for navigation and actions.', + 'shortcuts_customize_desc' => 'You can customize each of the shortcuts below. Just press your desired key combination after selecting the input for a shortcut.', + 'shortcuts_toggle_label' => 'Keyboard shortcuts enabled', + 'shortcuts_section_navigation' => 'Navigation', + 'shortcuts_section_actions' => 'Common Actions', + 'shortcuts_save' => 'Save Shortcuts', + 'shortcuts_overlay_desc' => 'Note: When shortcuts are enabled a helper overlay is available via pressing "?" which will highlight the available shortcuts for actions currently visible on the screen.', + 'shortcuts_update_success' => 'Shortcut preferences have been updated!', + 'shortcuts_overview_desc' => 'Manage keyboard shortcuts you can use to navigate the system user interface.', + + 'notifications' => 'Notification Preferences', + 'notifications_desc' => 'Control the email notifications you receive when certain activity is performed within the system.', + 'notifications_opt_own_page_changes' => 'Notify upon changes to pages I own', + 'notifications_opt_own_page_comments' => 'Notify upon comments on pages I own', + 'notifications_opt_comment_replies' => 'Notify upon replies to my comments', + 'notifications_save' => 'Save Preferences', + 'notifications_update_success' => 'Notification preferences have been updated!', + 'notifications_watched' => 'Watched & Ignored Items', + 'notifications_watched_desc' => 'Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.', + + 'auth' => 'Access & Security', + 'auth_change_password' => 'Change Password', + 'auth_change_password_desc' => 'Change the password you use to log-in to the application. This must be at least 8 characters long.', + 'auth_change_password_success' => 'Password has been updated!', + + 'profile' => 'Profile Details', + 'profile_desc' => 'Manage the details of your account which represents you to other users, in addition to details that are used for communication and system personalisation.', + 'profile_view_public' => 'View Public Profile', + 'profile_name_desc' => 'Configure your display name which will be visible to other users in the system through the activity you perform, and content you own.', + 'profile_email_desc' => 'This email will be used for notifications and, depending on active system authentication, system access.', + 'profile_email_no_permission' => 'Unfortunately you don\'t have permission to change your email address. If you want to change this, you\'d need to ask an administrator to change this for you.', + 'profile_avatar_desc' => 'Select an image which will be used to represent yourself to others in the system. Ideally this image should be square and about 256px in width and height.', + 'profile_admin_options' => 'Administrator Options', + 'profile_admin_options_desc' => 'Additional administrator-level options, like those to manage role assignments, can be found for your user account in the "Settings > Users" area of the application.', + + 'delete_account' => 'Delete Account', + 'delete_my_account' => 'Delete My Account', + 'delete_my_account_desc' => 'This will fully delete your user account from the system. You will not be able to recover this account or revert this action. Content you\'ve created, such as created pages and uploaded images, will remain.', + 'delete_my_account_warning' => 'Are you sure you want to delete your account?', +]; diff --git a/lang/tk/settings.php b/lang/tk/settings.php new file mode 100644 index 00000000000..81c2c0a93c3 --- /dev/null +++ b/lang/tk/settings.php @@ -0,0 +1,370 @@ + 'Settings', + 'settings_save' => 'Save Settings', + 'system_version' => 'System Version', + 'categories' => 'Categories', + + // App Settings + 'app_customization' => 'Customization', + 'app_features_security' => 'Features & Security', + 'app_name' => 'Application Name', + 'app_name_desc' => 'This name is shown in the header and in any system-sent emails.', + 'app_name_header' => 'Show name in header', + 'app_public_access' => 'Public Access', + 'app_public_access_desc' => 'Enabling this option will allow visitors, that are not logged-in, to access content in your BookStack instance.', + 'app_public_access_desc_guest' => 'Access for public visitors can be controlled through the "Guest" user.', + 'app_public_access_toggle' => 'Allow public access', + 'app_public_viewing' => 'Allow public viewing?', + 'app_secure_images' => 'Higher Security Image Uploads', + 'app_secure_images_toggle' => 'Enable higher security image uploads', + 'app_secure_images_desc' => 'For performance reasons, all images are public. This option adds a random, hard-to-guess string in front of image urls. Ensure directory indexes are not enabled to prevent easy access.', + 'app_default_editor' => 'Default Page Editor', + 'app_default_editor_desc' => 'Select which editor will be used by default when editing new pages. This can be overridden at a page level where permissions allow.', + 'app_custom_html' => 'Custom HTML Head Content', + 'app_custom_html_desc' => 'Any content added here will be inserted into the bottom of the section of every page. This is handy for overriding styles or adding analytics code.', + 'app_custom_html_disabled_notice' => 'Custom HTML head content is disabled on this settings page to ensure any breaking changes can be reverted.', + 'app_logo' => 'Application Logo', + 'app_logo_desc' => 'This is used in the application header bar, among other areas. This image should be 86px in height. Large images will be scaled down.', + 'app_icon' => 'Application Icon', + 'app_icon_desc' => 'This icon is used for browser tabs and shortcut icons. This should be a 256px square PNG image.', + 'app_homepage' => 'Application Homepage', + 'app_homepage_desc' => 'Select a view to show on the homepage instead of the default view. Page permissions are ignored for selected pages.', + 'app_homepage_select' => 'Select a page', + 'app_footer_links' => 'Footer Links', + 'app_footer_links_desc' => 'Add links to show within the site footer. These will be displayed at the bottom of most pages, including those that do not require login. You can use a label of "trans::" to use system-defined translations. For example: Using "trans::common.privacy_policy" will provide the translated text "Privacy Policy" and "trans::common.terms_of_service" will provide the translated text "Terms of Service".', + 'app_footer_links_label' => 'Link Label', + 'app_footer_links_url' => 'Link URL', + 'app_footer_links_add' => 'Add Footer Link', + 'app_disable_comments' => 'Disable Comments', + 'app_disable_comments_toggle' => 'Disable comments', + 'app_disable_comments_desc' => 'Disables comments across all pages in the application.
Existing comments are not shown.', + + // Color settings + 'color_scheme' => 'Application Color Scheme', + 'color_scheme_desc' => 'Set the colors to use in the application user interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.', + 'ui_colors_desc' => 'Set the application primary color and default link color. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the application interface.', + 'app_color' => 'Primary Color', + 'link_color' => 'Default Link Color', + 'content_colors_desc' => 'Set colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.', + 'bookshelf_color' => 'Shelf Color', + 'book_color' => 'Book Color', + 'chapter_color' => 'Chapter Color', + 'page_color' => 'Page Color', + 'page_draft_color' => 'Page Draft Color', + + // Registration Settings + 'reg_settings' => 'Registration', + 'reg_enable' => 'Enable Registration', + 'reg_enable_toggle' => 'Enable registration', + 'reg_enable_desc' => 'When registration is enabled user will be able to sign themselves up as an application user. Upon registration they are given a single, default user role.', + 'reg_default_role' => 'Default user role after registration', + 'reg_enable_external_warning' => 'The option above is ignored while external LDAP or SAML authentication is active. User accounts for non-existing members will be auto-created if authentication, against the external system in use, is successful.', + 'reg_email_confirmation' => 'Email Confirmation', + 'reg_email_confirmation_toggle' => 'Require email confirmation', + 'reg_confirm_email_desc' => 'If domain restriction is used then email confirmation will be required and this option will be ignored.', + 'reg_confirm_restrict_domain' => 'Domain Restriction', + 'reg_confirm_restrict_domain_desc' => 'Enter a comma separated list of email domains you would like to restrict registration to. Users will be sent an email to confirm their address before being allowed to interact with the application.
Note that users will be able to change their email addresses after successful registration.', + 'reg_confirm_restrict_domain_placeholder' => 'No restriction set', + + // Sorting Settings + 'sorting' => 'Sorting', + 'sorting_book_default' => 'Default Book Sort', + 'sorting_book_default_desc' => 'Select the default sort rule to apply to new books. This won\'t affect existing books, and can be overridden per-book.', + 'sorting_rules' => 'Sort Rules', + 'sorting_rules_desc' => 'These are predefined sorting operations which can be applied to content in the system.', + 'sort_rule_assigned_to_x_books' => 'Assigned to :count Book|Assigned to :count Books', + 'sort_rule_create' => 'Create Sort Rule', + 'sort_rule_edit' => 'Edit Sort Rule', + 'sort_rule_delete' => 'Delete Sort Rule', + 'sort_rule_delete_desc' => 'Remove this sort rule from the system. Books using this sort will revert to manual sorting.', + 'sort_rule_delete_warn_books' => 'This sort rule is currently used on :count book(s). Are you sure you want to delete this?', + 'sort_rule_delete_warn_default' => 'This sort rule is currently used as the default for books. Are you sure you want to delete this?', + 'sort_rule_details' => 'Sort Rule Details', + 'sort_rule_details_desc' => 'Set a name for this sort rule, which will appear in lists when users are selecting a sort.', + 'sort_rule_operations' => 'Sort Operations', + 'sort_rule_operations_desc' => 'Configure the sort actions to be performed by moving them from the list of available operations. Upon use, the operations will be applied in order, from top to bottom. Any changes made here will be applied to all assigned books upon save.', + 'sort_rule_available_operations' => 'Available Operations', + 'sort_rule_available_operations_empty' => 'No operations remaining', + 'sort_rule_configured_operations' => 'Configured Operations', + 'sort_rule_configured_operations_empty' => 'Drag/add operations from the "Available Operations" list', + 'sort_rule_op_asc' => '(Asc)', + 'sort_rule_op_desc' => '(Desc)', + 'sort_rule_op_name' => 'Name - Alphabetical', + 'sort_rule_op_name_numeric' => '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_last' => 'Chapters Last', + + // Maintenance settings + 'maint' => 'Maintenance', + 'maint_image_cleanup' => 'Cleanup Images', + 'maint_image_cleanup_desc' => 'Scans page & revision content to check which images and drawings are currently in use and which images are redundant. Ensure you create a full database and image backup before running this.', + 'maint_delete_images_only_in_revisions' => 'Also delete images that only exist in old page revisions', + 'maint_image_cleanup_run' => 'Run Cleanup', + 'maint_image_cleanup_warning' => ':count potentially unused images were found. Are you sure you want to delete these images?', + 'maint_image_cleanup_success' => ':count potentially unused images found and deleted!', + 'maint_image_cleanup_nothing_found' => 'No unused images found, Nothing deleted!', + 'maint_send_test_email' => 'Send a Test Email', + 'maint_send_test_email_desc' => 'This sends a test email to your email address specified in your profile.', + 'maint_send_test_email_run' => 'Send test email', + 'maint_send_test_email_success' => 'Email sent to :address', + 'maint_send_test_email_mail_subject' => 'Test Email', + 'maint_send_test_email_mail_greeting' => 'Email delivery seems to work!', + 'maint_send_test_email_mail_text' => 'Congratulations! As you received this email notification, your email settings seem to be configured properly.', + 'maint_recycle_bin_desc' => 'Deleted shelves, books, chapters & pages are sent to the recycle bin so they can be restored or permanently deleted. Older items in the recycle bin may be automatically removed after a while depending on system configuration.', + 'maint_recycle_bin_open' => 'Open Recycle Bin', + 'maint_regen_references' => 'Regenerate References', + 'maint_regen_references_desc' => 'This action will rebuild the cross-item reference index within the database. This is usually handled automatically but this action can be useful to index old content or content added via unofficial methods.', + 'maint_regen_references_success' => 'Reference index has been regenerated!', + 'maint_timeout_command_note' => 'Note: This action can take time to run, which can lead to timeout issues in some web environments. As an alternative, this action be performed using a terminal command.', + + // Recycle Bin + 'recycle_bin' => 'Recycle Bin', + 'recycle_bin_desc' => 'Here you can restore items that have been deleted or choose to permanently remove them from the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.', + 'recycle_bin_deleted_item' => 'Deleted Item', + 'recycle_bin_deleted_parent' => 'Parent', + 'recycle_bin_deleted_by' => 'Deleted By', + 'recycle_bin_deleted_at' => 'Deletion Time', + 'recycle_bin_permanently_delete' => 'Permanently Delete', + 'recycle_bin_restore' => 'Restore', + 'recycle_bin_contents_empty' => 'The recycle bin is currently empty', + 'recycle_bin_empty' => 'Empty Recycle Bin', + 'recycle_bin_empty_confirm' => 'This will permanently destroy all items in the recycle bin including content contained within each item. Are you sure you want to empty the recycle bin?', + 'recycle_bin_destroy_confirm' => 'This action will permanently delete this item from the system, along with any child elements listed below, and you will not be able to restore this content. Are you sure you want to permanently delete this item?', + 'recycle_bin_destroy_list' => 'Items to be Destroyed', + 'recycle_bin_restore_list' => 'Items to be Restored', + 'recycle_bin_restore_confirm' => 'This action will restore the deleted item, including any child elements, to their original location. If the original location has since been deleted, and is now in the recycle bin, the parent item will also need to be restored.', + 'recycle_bin_restore_deleted_parent' => 'The parent of this item has also been deleted. These will remain deleted until that parent is also restored.', + 'recycle_bin_restore_parent' => 'Restore Parent', + 'recycle_bin_destroy_notification' => 'Deleted :count total items from the recycle bin.', + 'recycle_bin_restore_notification' => 'Restored :count total items from the recycle bin.', + + // Audit Log + 'audit' => 'Audit Log', + 'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.', + 'audit_event_filter' => 'Event Filter', + 'audit_event_filter_no_filter' => 'No Filter', + 'audit_deleted_item' => 'Deleted Item', + 'audit_deleted_item_name' => 'Name: :name', + 'audit_table_user' => 'User', + 'audit_table_event' => 'Event', + 'audit_table_related' => 'Related Item or Detail', + 'audit_table_ip' => 'IP Address', + 'audit_table_date' => 'Activity Date', + 'audit_date_from' => 'Date Range From', + 'audit_date_to' => 'Date Range To', + + // Role Settings + 'roles' => 'Roles', + 'role_user_roles' => 'User Roles', + 'roles_index_desc' => 'Roles are used to group users & provide system permission to their members. When a user is a member of multiple roles the privileges granted will stack and the user will inherit all abilities.', + 'roles_x_users_assigned' => ':count user assigned|:count users assigned', + 'roles_x_permissions_provided' => ':count permission|:count permissions', + 'roles_assigned_users' => 'Assigned Users', + 'roles_permissions_provided' => 'Provided Permissions', + 'role_create' => 'Create New Role', + 'role_delete' => 'Delete Role', + 'role_delete_confirm' => 'This will delete the role with the name \':roleName\'.', + 'role_delete_users_assigned' => 'This role has :userCount users assigned to it. If you would like to migrate the users from this role select a new role below.', + 'role_delete_no_migration' => "Don't migrate users", + 'role_delete_sure' => 'Are you sure you want to delete this role?', + 'role_edit' => 'Edit Role', + 'role_details' => 'Role Details', + 'role_name' => 'Role Name', + 'role_desc' => 'Short Description of Role', + 'role_mfa_enforced' => 'Requires Multi-Factor Authentication', + 'role_external_auth_id' => 'External Authentication IDs', + 'role_system' => 'System Permissions', + 'role_manage_users' => 'Manage users', + 'role_manage_roles' => 'Manage roles & role permissions', + 'role_manage_entity_permissions' => 'Manage all book, chapter & page permissions', + 'role_manage_own_entity_permissions' => 'Manage permissions on own book, chapter & pages', + 'role_manage_page_templates' => 'Manage page templates', + 'role_access_api' => 'Access system API', + 'role_manage_settings' => 'Manage app settings', + 'role_export_content' => 'Export content', + 'role_import_content' => 'Import content', + 'role_editor_change' => 'Change page editor', + 'role_notifications' => 'Receive & manage notifications', + 'role_asset' => 'Asset Permissions', + 'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.', + 'role_asset_desc' => 'These permissions control default access to the assets within the system. Permissions on Books, Chapters and Pages will override these permissions.', + 'role_asset_admins' => 'Admins are automatically given access to all content but these options may show or hide UI options.', + 'role_asset_image_view_note' => 'This relates to visibility within the image manager. Actual access of uploaded image files will be dependant upon system image storage option.', + 'role_all' => 'All', + 'role_own' => 'Own', + 'role_controlled_by_asset' => 'Controlled by the asset they are uploaded to', + 'role_save' => 'Save Role', + 'role_users' => 'Users in this role', + 'role_users_none' => 'No users are currently assigned to this role', + + // Users + 'users' => 'Users', + 'users_index_desc' => 'Create & manage individual user accounts within the system. User accounts are used for login and attribution of content & activity. Access permissions are primarily role-based but user content ownership, among other factors, may also affect permissions & access.', + 'user_profile' => 'User Profile', + 'users_add_new' => 'Add New User', + 'users_search' => 'Search Users', + 'users_latest_activity' => 'Latest Activity', + 'users_details' => 'User Details', + 'users_details_desc' => 'Set a display name and an email address for this user. The email address will be used for logging into the application.', + 'users_details_desc_no_email' => 'Set a display name for this user so others can recognise them.', + 'users_role' => 'User Roles', + 'users_role_desc' => 'Select which roles this user will be assigned to. If a user is assigned to multiple roles the permissions from those roles will stack and they will receive all abilities of the assigned roles.', + 'users_password' => 'User Password', + 'users_password_desc' => 'Set a password used to log-in to the application. This must be at least 8 characters long.', + 'users_send_invite_text' => 'You can choose to send this user an invitation email which allows them to set their own password otherwise you can set their password yourself.', + 'users_send_invite_option' => 'Send user invite email', + 'users_external_auth_id' => 'External Authentication ID', + 'users_external_auth_id_desc' => 'When an external authentication system is in use (such as SAML2, OIDC or LDAP) this is the ID which links this BookStack user to the authentication system account. You can ignore this field if using the default email-based authentication.', + 'users_password_warning' => 'Only fill the below if you would like to change the password for this user.', + 'users_system_public' => 'This user represents any guest users that visit your instance. It cannot be used to log in but is assigned automatically.', + 'users_delete' => 'Delete User', + 'users_delete_named' => 'Delete user :userName', + 'users_delete_warning' => 'This will fully delete this user with the name \':userName\' from the system.', + 'users_delete_confirm' => 'Are you sure you want to delete this user?', + 'users_migrate_ownership' => 'Migrate Ownership', + 'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.', + 'users_none_selected' => 'No user selected', + 'users_edit' => 'Edit User', + 'users_edit_profile' => 'Edit Profile', + 'users_avatar' => 'User Avatar', + 'users_avatar_desc' => 'Select an image to represent this user. This should be approx 256px square.', + 'users_preferred_language' => 'Preferred Language', + 'users_preferred_language_desc' => 'This option will change the language used for the user-interface of the application. This will not affect any user-created content.', + 'users_social_accounts' => 'Social Accounts', + 'users_social_accounts_desc' => 'View the status of the connected social accounts for this user. Social accounts can be used in addition to the primary authentication system for system access.', + 'users_social_accounts_info' => 'Here you can connect your other accounts for quicker and easier login. Disconnecting an account here does not revoke previously authorized access. Revoke access from your profile settings on the connected social account.', + 'users_social_connect' => 'Connect Account', + 'users_social_disconnect' => 'Disconnect Account', + 'users_social_status_connected' => 'Connected', + 'users_social_status_disconnected' => 'Disconnected', + 'users_social_connected' => ':socialAccount account was successfully attached to your profile.', + 'users_social_disconnected' => ':socialAccount account was successfully disconnected from your profile.', + 'users_api_tokens' => 'API Tokens', + 'users_api_tokens_desc' => 'Create and manage the access tokens used to authenticate with the BookStack REST API. Permissions for the API are managed via the user that the token belongs to.', + 'users_api_tokens_none' => 'No API tokens have been created for this user', + 'users_api_tokens_create' => 'Create Token', + 'users_api_tokens_expires' => 'Expires', + 'users_api_tokens_docs' => 'API Documentation', + 'users_mfa' => 'Multi-Factor Authentication', + 'users_mfa_desc' => 'Setup multi-factor authentication as an extra layer of security for your user account.', + 'users_mfa_x_methods' => ':count method configured|:count methods configured', + 'users_mfa_configure' => 'Configure Methods', + + // API Tokens + 'user_api_token_create' => 'Create API Token', + 'user_api_token_name' => 'Name', + 'user_api_token_name_desc' => 'Give your token a readable name as a future reminder of its intended purpose.', + 'user_api_token_expiry' => 'Expiry Date', + 'user_api_token_expiry_desc' => 'Set a date at which this token expires. After this date, requests made using this token will no longer work. Leaving this field blank will set an expiry 100 years into the future.', + 'user_api_token_create_secret_message' => 'Immediately after creating this token a "Token ID" & "Token Secret" will be generated and displayed. The secret will only be shown a single time so be sure to copy the value to somewhere safe and secure before proceeding.', + 'user_api_token' => 'API Token', + 'user_api_token_id' => 'Token ID', + 'user_api_token_id_desc' => 'This is a non-editable system generated identifier for this token which will need to be provided in API requests.', + 'user_api_token_secret' => 'Token Secret', + 'user_api_token_secret_desc' => 'This is a system generated secret for this token which will need to be provided in API requests. This will only be displayed this one time so copy this value to somewhere safe and secure.', + 'user_api_token_created' => 'Token created :timeAgo', + 'user_api_token_updated' => 'Token updated :timeAgo', + 'user_api_token_delete' => 'Delete Token', + 'user_api_token_delete_warning' => 'This will fully delete this API token with the name \':tokenName\' from the system.', + 'user_api_token_delete_confirm' => 'Are you sure you want to delete this API token?', + + // Webhooks + 'webhooks' => 'Webhooks', + 'webhooks_index_desc' => 'Webhooks are a way to send data to external URLs when certain actions and events occur within the system which allows event-based integration with external platforms such as messaging or notification systems.', + 'webhooks_x_trigger_events' => ':count trigger event|:count trigger events', + 'webhooks_create' => 'Create New Webhook', + 'webhooks_none_created' => 'No webhooks have yet been created.', + 'webhooks_edit' => 'Edit Webhook', + 'webhooks_save' => 'Save Webhook', + 'webhooks_details' => 'Webhook Details', + 'webhooks_details_desc' => 'Provide a user friendly name and a POST endpoint as a location for the webhook data to be sent to.', + 'webhooks_events' => 'Webhook Events', + 'webhooks_events_desc' => 'Select all the events that should trigger this webhook to be called.', + 'webhooks_events_warning' => 'Keep in mind that these events will be triggered for all selected events, even if custom permissions are applied. Ensure that use of this webhook won\'t expose confidential content.', + 'webhooks_events_all' => 'All system events', + 'webhooks_name' => 'Webhook Name', + 'webhooks_timeout' => 'Webhook Request Timeout (Seconds)', + 'webhooks_endpoint' => 'Webhook Endpoint', + 'webhooks_active' => 'Webhook Active', + 'webhook_events_table_header' => 'Events', + 'webhooks_delete' => 'Delete Webhook', + 'webhooks_delete_warning' => 'This will fully delete this webhook, with the name \':webhookName\', from the system.', + 'webhooks_delete_confirm' => 'Are you sure you want to delete this webhook?', + 'webhooks_format_example' => 'Webhook Format Example', + 'webhooks_format_example_desc' => 'Webhook data is sent as a POST request to the configured endpoint as JSON following the format below. The "related_item" and "url" properties are optional and will depend on the type of event triggered.', + 'webhooks_status' => 'Webhook Status', + 'webhooks_last_called' => 'Last Called:', + 'webhooks_last_errored' => 'Last Errored:', + 'webhooks_last_error_message' => 'Last Error Message:', + + // Licensing + 'licenses' => 'Licenses', + 'licenses_desc' => 'This page details license information for BookStack in addition to the projects & libraries that are used within BookStack. Many projects listed may only be used in a development context.', + 'licenses_bookstack' => 'BookStack License', + 'licenses_php' => 'PHP Library Licenses', + 'licenses_js' => 'JavaScript Library Licenses', + 'licenses_other' => 'Other Licenses', + 'license_details' => 'License Details', + + //! If editing translations files directly please ignore this in all + //! languages apart from en. Content will be auto-copied from en. + //!//////////////////////////////// + 'language_select' => [ + 'en' => 'English', + 'ar' => 'العربية', + 'bg' => 'Bǎlgarski', + 'bs' => 'Bosanski', + 'ca' => 'Català', + 'cs' => 'Česky', + 'cy' => 'Cymraeg', + 'da' => 'Dansk', + 'de' => 'Deutsch (Sie)', + 'de_informal' => 'Deutsch (Du)', + 'el' => 'ελληνικά', + 'es' => 'Español', + 'es_AR' => 'Español Argentina', + 'et' => 'Eesti keel', + 'eu' => 'Euskara', + 'fa' => 'فارسی', + 'fi' => 'Suomi', + 'fr' => 'Français', + 'he' => 'עברית', + 'hr' => 'Hrvatski', + 'hu' => 'Magyar', + 'id' => 'Bahasa Indonesia', + 'it' => 'Italian', + 'ja' => '日本語', + 'ko' => '한국어', + 'lt' => 'Lietuvių Kalba', + 'lv' => 'Latviešu Valoda', + 'nb' => 'Norsk (Bokmål)', + 'ne' => 'नेपाली', + 'nn' => 'Nynorsk', + 'nl' => 'Nederlands', + 'pl' => 'Polski', + 'pt' => 'Português', + 'pt_BR' => 'Português do Brasil', + 'ro' => 'Română', + 'ru' => 'Русский', + 'sk' => 'Slovensky', + 'sl' => 'Slovenščina', + 'sv' => 'Svenska', + 'tr' => 'Türkçe', + 'uk' => 'Українська', + 'uz' => 'O‘zbekcha', + 'vi' => 'Tiếng Việt', + 'zh_CN' => '简体中文', + 'zh_TW' => '繁體中文', + ], + //!//////////////////////////////// +]; diff --git a/lang/tk/validation.php b/lang/tk/validation.php new file mode 100644 index 00000000000..d9b982d1e23 --- /dev/null +++ b/lang/tk/validation.php @@ -0,0 +1,122 @@ + 'The :attribute must be accepted.', + 'active_url' => 'The :attribute is not a valid URL.', + 'after' => 'The :attribute must be a date after :date.', + 'alpha' => 'The :attribute may only contain letters.', + 'alpha_dash' => 'The :attribute may only contain letters, numbers, dashes and underscores.', + 'alpha_num' => 'The :attribute may only contain letters and numbers.', + 'array' => 'The :attribute must be an array.', + 'backup_codes' => 'The provided code is not valid or has already been used.', + 'before' => 'The :attribute must be a date before :date.', + 'between' => [ + 'numeric' => 'The :attribute must be between :min and :max.', + 'file' => 'The :attribute must be between :min and :max kilobytes.', + 'string' => 'The :attribute must be between :min and :max characters.', + 'array' => 'The :attribute must have between :min and :max items.', + ], + 'boolean' => 'The :attribute field must be true or false.', + 'confirmed' => 'The :attribute confirmation does not match.', + 'date' => 'The :attribute is not a valid date.', + 'date_format' => 'The :attribute does not match the format :format.', + 'different' => 'The :attribute and :other must be different.', + 'digits' => 'The :attribute must be :digits digits.', + 'digits_between' => 'The :attribute must be between :min and :max digits.', + 'email' => 'The :attribute must be a valid email address.', + 'ends_with' => 'The :attribute must end with one of the following: :values', + 'file' => 'The :attribute must be provided as a valid file.', + 'filled' => 'The :attribute field is required.', + 'gt' => [ + 'numeric' => 'The :attribute must be greater than :value.', + 'file' => 'The :attribute must be greater than :value kilobytes.', + 'string' => 'The :attribute must be greater than :value characters.', + 'array' => 'The :attribute must have more than :value items.', + ], + 'gte' => [ + 'numeric' => 'The :attribute must be greater than or equal :value.', + 'file' => 'The :attribute must be greater than or equal :value kilobytes.', + 'string' => 'The :attribute must be greater than or equal :value characters.', + 'array' => 'The :attribute must have :value items or more.', + ], + 'exists' => 'The selected :attribute is invalid.', + 'image' => 'The :attribute must be an image.', + 'image_extension' => 'The :attribute must have a valid & supported image extension.', + 'in' => 'The selected :attribute is invalid.', + 'integer' => 'The :attribute must be an integer.', + 'ip' => 'The :attribute must be a valid IP address.', + 'ipv4' => 'The :attribute must be a valid IPv4 address.', + 'ipv6' => 'The :attribute must be a valid IPv6 address.', + 'json' => 'The :attribute must be a valid JSON string.', + 'lt' => [ + 'numeric' => 'The :attribute must be less than :value.', + 'file' => 'The :attribute must be less than :value kilobytes.', + 'string' => 'The :attribute must be less than :value characters.', + 'array' => 'The :attribute must have less than :value items.', + ], + 'lte' => [ + 'numeric' => 'The :attribute must be less than or equal :value.', + 'file' => 'The :attribute must be less than or equal :value kilobytes.', + 'string' => 'The :attribute must be less than or equal :value characters.', + 'array' => 'The :attribute must not have more than :value items.', + ], + 'max' => [ + 'numeric' => 'The :attribute may not be greater than :max.', + 'file' => 'The :attribute may not be greater than :max kilobytes.', + 'string' => 'The :attribute may not be greater than :max characters.', + 'array' => 'The :attribute may not have more than :max items.', + ], + 'mimes' => 'The :attribute must be a file of type: :values.', + 'min' => [ + 'numeric' => 'The :attribute must be at least :min.', + 'file' => 'The :attribute must be at least :min kilobytes.', + 'string' => 'The :attribute must be at least :min characters.', + 'array' => 'The :attribute must have at least :min items.', + ], + 'not_in' => 'The selected :attribute is invalid.', + 'not_regex' => 'The :attribute format is invalid.', + 'numeric' => 'The :attribute must be a number.', + 'regex' => 'The :attribute format is invalid.', + 'required' => 'The :attribute field is required.', + 'required_if' => 'The :attribute field is required when :other is :value.', + 'required_with' => 'The :attribute field is required when :values is present.', + 'required_with_all' => 'The :attribute field is required when :values is present.', + 'required_without' => 'The :attribute field is required when :values is not present.', + 'required_without_all' => 'The :attribute field is required when none of :values are present.', + 'same' => 'The :attribute and :other must match.', + 'safe_url' => 'The provided link may not be safe.', + 'size' => [ + 'numeric' => 'The :attribute must be :size.', + 'file' => 'The :attribute must be :size kilobytes.', + 'string' => 'The :attribute must be :size characters.', + 'array' => 'The :attribute must contain :size items.', + ], + 'string' => 'The :attribute must be a string.', + 'timezone' => 'The :attribute must be a valid zone.', + 'totp' => 'The provided code is not valid or has expired.', + 'unique' => 'The :attribute has already been taken.', + 'url' => 'The :attribute format is invalid.', + 'uploaded' => 'The file could not be uploaded. The server may not accept files of this size.', + + 'zip_file' => 'The :attribute needs to reference a file within the ZIP.', + 'zip_file_mime' => 'The :attribute needs to reference a file of type :validTypes, found :foundType.', + 'zip_model_expected' => 'Data object expected but ":type" found.', + 'zip_unique' => 'The :attribute must be unique for the object type within the ZIP.', + + // Custom validation lines + 'custom' => [ + 'password-confirm' => [ + 'required_with' => 'Password confirmation required', + ], + ], + + // Custom validation attributes + 'attributes' => [], +]; diff --git a/lang/tr/activities.php b/lang/tr/activities.php index 6a0c6ec5da2..5bed9ea303b 100644 --- a/lang/tr/activities.php +++ b/lang/tr/activities.php @@ -50,8 +50,8 @@ 'bookshelf_delete_notification' => 'Kitaplık başarıyla silindi', // Revisions - 'revision_restore' => 'restored revision', - 'revision_delete' => 'deleted revision', + 'revision_restore' => 'geri yüklenen revizyon', + 'revision_delete' => 'silinmiş revizyon', 'revision_delete_notification' => 'Değişiklik başarıyla silindi', // Favourites @@ -64,17 +64,17 @@ // Auth 'auth_login' => 'oturum açıldı', 'auth_register' => 'yeni kullanıcı olarak kayıt yapıldı', - 'auth_password_reset_request' => 'requested user password reset', + 'auth_password_reset_request' => 'talep edilmiş kullanıcı parola sıfırlamaları', 'auth_password_reset_update' => 'Kullanıcı parolasını sıfırla', - 'mfa_setup_method' => 'configured MFA method', + 'mfa_setup_method' => 'uygulanan MFA yöntemi', 'mfa_setup_method_notification' => 'Çok aşamalı kimlik doğrulama yöntemi başarıyla yapılandırıldı', - 'mfa_remove_method' => 'removed MFA method', + 'mfa_remove_method' => 'kaldırılan MFA yöntemi', 'mfa_remove_method_notification' => 'Çok aşamalı kimlik doğrulama yöntemi başarıyla kaldırıldı', // Settings - 'settings_update' => 'updated settings', + 'settings_update' => 'güncellenmiş ayarlar', 'settings_update_notification' => 'Ayarlar başarıyla güncellendi', - 'maintenance_action_run' => 'ran maintenance action', + 'maintenance_action_run' => 'bakım işlemine başla', // Webhooks 'webhook_create' => 'web kancası oluşturuldu', @@ -84,20 +84,28 @@ 'webhook_delete' => 'web kancası silindi', 'webhook_delete_notification' => 'Web kancası başarıyla silindi', + // Imports + 'import_create' => 'created import', + 'import_create_notification' => 'Import successfully uploaded', + 'import_run' => 'updated import', + 'import_run_notification' => 'Content successfully imported', + 'import_delete' => 'deleted import', + 'import_delete_notification' => 'Import successfully deleted', + // Users - 'user_create' => 'created user', + 'user_create' => 'oluşturan kullanıcı', 'user_create_notification' => 'Kullanıcı başarıyla oluşturuldu', 'user_update' => 'updated user', 'user_update_notification' => 'Kullanıcı başarıyla güncellendi', - 'user_delete' => 'deleted user', + 'user_delete' => 'kullanıcı silindi', 'user_delete_notification' => 'Kullanıcı başarıyla silindi', // API Tokens - 'api_token_create' => 'created api token', + 'api_token_create' => 'created API token', 'api_token_create_notification' => 'API anahtarı başarıyla oluşturuldu', - 'api_token_update' => 'updated api token', + 'api_token_update' => 'updated API token', 'api_token_update_notification' => 'API anahtarı başarıyla güncellendi', - 'api_token_delete' => 'silinen sayfa tokenı', + 'api_token_delete' => 'deleted API token', 'api_token_delete_notification' => 'API anahtarı başarıyla silindi', // Roles @@ -110,14 +118,22 @@ // Recycle Bin 'recycle_bin_empty' => 'emptied recycle bin', - 'recycle_bin_restore' => 'restored from recycle bin', - 'recycle_bin_destroy' => 'removed from recycle bin', + 'recycle_bin_restore' => 'çöp kutusundan geri getirilen', + 'recycle_bin_destroy' => 'çöp kutusundan kaldırılan', // Comments 'commented_on' => 'yorum yaptı', - 'comment_create' => 'added comment', - 'comment_update' => 'updated comment', - 'comment_delete' => 'deleted comment', + 'comment_create' => 'eklenen yorum', + 'comment_update' => 'güncellenen yorum', + 'comment_delete' => 'silinen yorum', + + // Sort Rules + 'sort_rule_create' => 'created sort rule', + 'sort_rule_create_notification' => 'Sort rule successfully created', + 'sort_rule_update' => 'updated sort rule', + 'sort_rule_update_notification' => 'Sort rule successfully updated', + 'sort_rule_delete' => 'deleted sort rule', + 'sort_rule_delete_notification' => 'Sort rule successfully deleted', // Other 'permissions_update' => 'güncellenmiş izinler', diff --git a/lang/tr/auth.php b/lang/tr/auth.php index 50f4f60a955..c9663f21e00 100644 --- a/lang/tr/auth.php +++ b/lang/tr/auth.php @@ -91,7 +91,7 @@ 'mfa_option_totp_title' => 'Mobil Uygulama', 'mfa_option_totp_desc' => 'Çok aşamalı kimlik doğrulamayı kullanabilmek için Google Authenticator, Authy veya Microsoft Authenticator gibi TOTP destekleyen bir mobil uygulamaya ihtiyacınız olacaktır.', 'mfa_option_backup_codes_title' => 'Yedekleme Kodları', - 'mfa_option_backup_codes_desc' => 'Kimliğini doğrulamak için kullanabileceğin aşağıdaki tek kullanımlık yedek kodlarını güvenli bir yerde sakla.', + 'mfa_option_backup_codes_desc' => 'Generates a set of one-time-use backup codes which you\'ll enter on login to verify your identity. Make sure to store these in a safe & secure place.', 'mfa_gen_confirm_and_enable' => 'Onayla ve aktive et', 'mfa_gen_backup_codes_title' => 'Yedekleme Kodları Kurulumu', 'mfa_gen_backup_codes_desc' => 'Aşağıdaki kod listesini güvenli bir yerde sakla. Sisteme giriş yaparken kodlardan birini ikinci bir kimlik doğrulama mekanizması olarak kullanabileceksin.', diff --git a/lang/tr/common.php b/lang/tr/common.php index 3c592a3f23a..5b5e753873a 100644 --- a/lang/tr/common.php +++ b/lang/tr/common.php @@ -20,7 +20,7 @@ 'description' => 'Açıklama', 'role' => 'Rol', 'cover_image' => 'Kapak resmi', - 'cover_image_description' => 'Bu görsel yaklaşık 440x250px boyutlarında olmalıdır.', + 'cover_image_description' => 'This image should be approximately 440x250px although it will be flexibly scaled & cropped to fit the user interface in different scenarios as required, so actual dimensions for display will differ.', // Actions 'actions' => 'İşlemler', @@ -30,6 +30,8 @@ 'create' => 'Oluştur', 'update' => 'Güncelle', 'edit' => 'Düzenle', + 'archive' => 'Archive', + 'unarchive' => 'Un-Archive', 'sort' => 'Sırala', 'move' => 'Taşı', 'copy' => 'Kopyala', @@ -107,4 +109,7 @@ // Not directly used but available for convenience to users. 'privacy_policy' => 'Gizlilik Politikası', 'terms_of_service' => 'Hizmet Şartları', + + // OpenSearch + 'opensearch_description' => 'Search :appName', ]; diff --git a/lang/tr/editor.php b/lang/tr/editor.php index 6bdb315d6e8..c020c82fddf 100644 --- a/lang/tr/editor.php +++ b/lang/tr/editor.php @@ -13,6 +13,7 @@ 'cancel' => 'İptal', 'save' => 'Kaydet', 'close' => 'Kapat', + 'apply' => 'Apply', 'undo' => 'Geri al', 'redo' => 'Yeniden yap', 'left' => 'Sol', @@ -47,6 +48,7 @@ 'superscript' => 'Üst simge', 'subscript' => 'Alt simge', 'text_color' => 'Metin rengi', + 'highlight_color' => 'Highlight color', 'custom_color' => 'Özel renk', 'remove_color' => 'Rengi kaldır', 'background_color' => 'Arka plan rengi', @@ -81,6 +83,9 @@ 'table_properties' => 'Tablo özellikleri', 'table_properties_title' => 'Tablo Özellikleri', 'delete_table' => 'Tabloyu sil', + 'table_clear_formatting' => 'Clear table formatting', + 'resize_to_contents' => 'Resize to contents', + 'row_header' => 'Row header', 'insert_row_before' => 'Üste satır ekle', 'insert_row_after' => 'Alta satır ekle', 'delete_row' => 'Satırı sil', @@ -144,6 +149,7 @@ 'url' => 'URL', 'text_to_display' => 'Görüntülenecek metin', 'title' => 'Başlık', + 'browse_links' => 'Browse links', 'open_link' => 'Bağlantıyı aç', 'open_link_in' => 'Bağlantıyı şurada aç...', 'open_link_current' => 'Geçerli pencere', @@ -160,6 +166,8 @@ 'about' => 'Editör hakkında', 'about_title' => 'WYSIWYG editor hakkında', 'editor_license' => 'Editor Lisans ve Telif Hakkı', + 'editor_lexical_license' => 'This editor is built as a fork of :lexicalLink which is distributed under the MIT license.', + 'editor_lexical_license_link' => 'Full license details can be found here.', 'editor_tiny_license' => 'Bu düzenleyici, MIT lisansı altında sağlanan :tinyLink kullanılarak oluşturulmuştur.', 'editor_tiny_license_link' => 'TinyMCE telif ve lisans bilgilerini burada bulabilirsiniz.', 'save_continue' => 'Kaydet & Devam Et', diff --git a/lang/tr/entities.php b/lang/tr/entities.php index 9cc6774eab4..39feb7e3917 100644 --- a/lang/tr/entities.php +++ b/lang/tr/entities.php @@ -39,6 +39,34 @@ 'export_pdf' => 'PDF Dosyası', 'export_text' => 'Düz Metin Dosyası', 'export_md' => 'Markdown Dosyası', + 'export_zip' => 'Portable ZIP', + 'default_template' => 'Default Page Template', + 'default_template_explain' => 'Assign a page template that will be used as the default content for all pages created within this item. Keep in mind this will only be used if the page creator has view access to the chosen template page.', + 'default_template_select' => 'Select a template page', + 'import' => 'Import', + 'import_validate' => 'Validate Import', + 'import_desc' => 'Import books, chapters & pages using a portable zip export from the same, or a different, instance. Select a ZIP file to proceed. After the file has been uploaded and validated you\'ll be able to configure & confirm the import in the next view.', + '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_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_size' => ':size Import ZIP Size', + 'import_uploaded_at' => 'Uploaded :relativeTime', + 'import_uploaded_by' => 'Uploaded by', + 'import_location' => 'Import Location', + '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_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', + 'breadcrumb_siblings_for_book' => 'Navigate siblings for book', + 'breadcrumb_siblings_for_bookshelf' => 'Navigate siblings for shelf', // Permissions and restrictions 'permissions' => 'İzinler', @@ -132,9 +160,6 @@ 'books_edit_named' => ':bookName Kitabını Düzenle', 'books_form_book_name' => 'Kitap Adı', 'books_save' => 'Kitabı Kaydet', - 'books_default_template' => 'Default Page Template', - 'books_default_template_explain' => 'Assign a page template that will be used as the default content for all new pages in this book. Keep in mind this will only be used if the page creator has view access to those chosen template page.', - 'books_default_template_select' => 'Select a template page', 'books_permissions' => 'Kitap İzinleri', 'books_permissions_updated' => 'Kitap İzinleri Güncellendi', 'books_empty_contents' => 'Bu kitaba ait sayfa veya bölüm oluşturulmamış.', @@ -145,7 +170,9 @@ 'books_search_this' => 'Bu kitapta ara', 'books_navigation' => 'Kitap Navigasyonu', 'books_sort' => 'Kitap İçeriklerini Sırala', - 'books_sort_desc' => 'İçeriğini yeniden düzenlemek için bir kitap içindeki bölümleri ve sayfaları taşıyın. Kitaplar arasında bölümlerin ve sayfaların kolayca taşınmasını sağlayan başka kitaplar eklenebilir.', + 'books_sort_desc' => 'Move chapters and pages within a book to reorganise its contents. Other books can be added which allows easy moving of chapters and pages between books. Optionally an auto sort rule can be set to automatically sort this book\'s contents upon changes.', + 'books_sort_auto_sort' => 'Auto Sort Option', + 'books_sort_auto_sort_active' => 'Auto Sort Active: :sortName', 'books_sort_named' => ':bookName Kitabını Sırala', 'books_sort_name' => 'İsme Göre Sırala', 'books_sort_created' => 'Oluşturulma Tarihine Göre Sırala', @@ -207,7 +234,7 @@ 'pages_delete_draft' => 'Sayfa Taslağını Sil', 'pages_delete_success' => 'Sayfa silindi', 'pages_delete_draft_success' => 'Sayfa taslağı silindi', - 'pages_delete_warning_template' => 'This page is in active use as a book default page template. These books will no longer have a default page template assigned after this page is deleted.', + 'pages_delete_warning_template' => 'This page is in active use as a book or chapter default page template. These books or chapters will no longer have a default page template assigned after this page is deleted.', 'pages_delete_confirm' => 'Bu sayfayı silmek istediğinize emin misiniz?', 'pages_delete_draft_confirm' => 'Bu sayfa taslağını silmek istediğinize emin misiniz?', 'pages_editing_named' => ':pageName Sayfası Düzenleniyor', @@ -224,6 +251,8 @@ 'pages_edit_switch_to_markdown_clean' => '(Temiz İçerik)', 'pages_edit_switch_to_markdown_stable' => '(Kararlı İçerik)', 'pages_edit_switch_to_wysiwyg' => 'WYSIWYG düzenleyiciye geç', + 'pages_edit_switch_to_new_wysiwyg' => 'Switch to new WYSIWYG', + 'pages_edit_switch_to_new_wysiwyg_desc' => '(In Beta Testing)', 'pages_edit_set_changelog' => 'Değişim Günlüğünü Ayarla', 'pages_edit_enter_changelog_desc' => 'Yaptığınız değişiklikler hakkında kısa bir açıklama girin', 'pages_edit_enter_changelog' => 'Değişim Günlüğünü Yazın', @@ -243,6 +272,7 @@ 'pages_md_insert_drawing' => 'Çizim Ekle', 'pages_md_show_preview' => 'Önizlemeyi göster', 'pages_md_sync_scroll' => 'Sync preview scroll', + 'pages_md_plain_editor' => 'Plaintext editor', 'pages_drawing_unsaved' => 'Unsaved Drawing Found', 'pages_drawing_unsaved_confirm' => 'Unsaved drawing data was found from a previous failed drawing save attempt. Would you like to restore and continue editing this unsaved drawing?', 'pages_not_in_chapter' => 'Bu sayfa, bir bölüme ait değil', @@ -367,8 +397,11 @@ 'comment' => 'Yorum', 'comments' => 'Yorumlar', 'comment_add' => 'Yorum Ekle', + 'comment_none' => 'No comments to display', 'comment_placeholder' => 'Buraya bir yorum yazın', - 'comment_count' => '{0} Yorum Yapılmamış|{1} 1 Yorum|[2,*] :count Yorum', + 'comment_thread_count' => ':count Comment Thread|:count Comment Threads', + 'comment_archived_count' => ':count Archived', + 'comment_archived_threads' => 'Archived Threads', 'comment_save' => 'Yorumu Gönder', 'comment_new' => 'Yeni Yorum', 'comment_created' => ':createDiff yorum yaptı', @@ -377,8 +410,14 @@ 'comment_deleted_success' => 'Yorum silindi', 'comment_created_success' => 'Yorum gönderildi', 'comment_updated_success' => 'Yorum güncellendi', + 'comment_archive_success' => 'Comment archived', + 'comment_unarchive_success' => 'Comment un-archived', + 'comment_view' => 'View comment', + 'comment_jump_to_thread' => 'Jump to thread', 'comment_delete_confirm' => 'Bu yorumu silmek istediğinize emin misiniz?', 'comment_in_reply_to' => ':commentId yorumuna yanıt olarak', + 'comment_reference' => 'Reference', + 'comment_reference_outdated' => '(Outdated)', 'comment_editor_explain' => 'İşte bu sayfaya bırakılan yorumlar. Kaydedilen sayfayı görüntülerken yorumlar eklenebilir ve yönetilebilir.', // Revision diff --git a/lang/tr/errors.php b/lang/tr/errors.php index 587ba9b4c2e..fd742687127 100644 --- a/lang/tr/errors.php +++ b/lang/tr/errors.php @@ -10,6 +10,7 @@ // Auth 'error_user_exists_different_creds' => ':email e-posta adresine sahip bir kullanıcı zaten var.', + 'auth_pre_register_theme_prevention' => 'User account could not be registered for the provided details', 'email_already_confirmed' => 'E-posta adresi zaten doğrulanmış, giriş yapmayı deneyin.', 'email_confirmation_invalid' => 'Bu doğrulama kodu ya geçersiz ya da daha önce kullanılmış, lütfen tekrar kaydolmayı deneyin.', 'email_confirmation_expired' => 'Doğrulama kodunun süresi doldu, yeni bir doğrulama kodu e-posta adresine gönderildi.', @@ -23,7 +24,6 @@ 'saml_invalid_response_id' => 'Harici doğrulama sistemi tarafından sağlanan bir veri talebi, bu uygulama tarafından başlatılan bir işlem tarafından tanınamadı. Giriş yaptıktan sonra geri dönmek bu soruna yol açmış olabilir.', 'saml_fail_authed' => ':system kullanarak giriş yapma başarısız oldu; sistem, başarılı bir kimlik doğrulama sağlayamadı', 'oidc_already_logged_in' => 'Zaten oturum açılmış', - 'oidc_user_not_registered' => ':name adlı kullanıcı kayıtlı değil ve otomatik kaydolma devre dışı bırakılmış', 'oidc_no_email_address' => 'Harici kimlik doğrulama sisteminden gelen veriler, bu kullanıcının e-posta adresini içermiyor', 'oidc_fail_authed' => ':system kullanarak giriş yapma başarısız oldu; sistem, başarılı bir kimlik doğrulama sağlayamadı', 'social_no_action_defined' => 'Herhangi bir eylem tanımlanmamış', @@ -37,6 +37,7 @@ 'social_driver_not_found' => 'Social driver bulunamadı', 'social_driver_not_configured' => ':socialAccount ayarlarınız doğru bir şekilde ayarlanmadı.', 'invite_token_expired' => 'Davetiye bağlantısının süresi doldu. Bunun yerine parolanızı sıfırlamayı deneyebilirsiniz.', + 'login_user_not_found' => 'A user for this action could not be found.', // System 'path_not_writable' => ':filePath dosya yolu yüklenemedi. Sunucuya yazılabilir olduğundan emin olun.', @@ -77,6 +78,7 @@ // Users 'users_cannot_delete_only_admin' => 'Tek olan yöneticiyi silemezsiniz', 'users_cannot_delete_guest' => 'Misafir kullanıyıcıyı silemezsiniz', + 'users_could_not_send_invite' => 'Could not create user since invite email failed to send', // Roles 'role_cannot_be_edited' => 'Bu rol düzenlenemez', @@ -103,6 +105,18 @@ 'app_down' => ':appName şu anda erişilemez durumda', 'back_soon' => 'En kısa sürede tekrar erişilebilir duruma gelecektir.', + // Import + 'import_zip_cant_read' => 'Could not read ZIP file.', + 'import_zip_cant_decode_data' => 'Could not find and decode ZIP data.json content.', + 'import_zip_no_data' => 'ZIP file data has no expected book, chapter or page content.', + 'import_validation_failed' => 'Import ZIP failed to validate with errors:', + 'import_zip_failed_notification' => 'Failed to import ZIP file.', + 'import_perms_books' => 'You are lacking the required permissions to create books.', + 'import_perms_chapters' => 'You are lacking the required permissions to create chapters.', + 'import_perms_pages' => 'You are lacking the required permissions to create pages.', + 'import_perms_images' => 'You are lacking the required permissions to create images.', + 'import_perms_attachments' => 'You are lacking the required permission to create attachments.', + // API errors 'api_no_authorization_found' => 'Yapılan istekte, yetkilendirme anahtarı bulunamadı', 'api_bad_authorization_format' => 'Yapılan istekte bir yetkilendirme anahtarı bulundu fakat doğru görünmüyor', diff --git a/lang/tr/notifications.php b/lang/tr/notifications.php index 1afd23f1dc4..c26a90b32cb 100644 --- a/lang/tr/notifications.php +++ b/lang/tr/notifications.php @@ -6,7 +6,7 @@ 'new_comment_subject' => 'New comment on page: :pageName', 'new_comment_intro' => 'A user has commented on a page in :appName:', - 'new_page_subject' => 'New page: :pageName', + 'new_page_subject' => 'Yeni sayfa :pageName', 'new_page_intro' => 'A new page has been created in :appName:', 'updated_page_subject' => 'Updated page: :pageName', 'updated_page_intro' => 'A page has been updated in :appName:', diff --git a/lang/tr/preferences.php b/lang/tr/preferences.php index 94a25df739b..1ce7b01fda4 100644 --- a/lang/tr/preferences.php +++ b/lang/tr/preferences.php @@ -27,7 +27,7 @@ 'notifications_save' => 'Save Preferences', 'notifications_update_success' => 'Notification preferences have been updated!', 'notifications_watched' => 'Watched & Ignored Items', - 'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.', + 'notifications_watched_desc' => 'Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.', 'auth' => 'Access & Security', 'auth_change_password' => 'Change Password', diff --git a/lang/tr/settings.php b/lang/tr/settings.php index ce312f1c38b..1eb15d9d4c0 100644 --- a/lang/tr/settings.php +++ b/lang/tr/settings.php @@ -74,6 +74,36 @@ 'reg_confirm_restrict_domain_desc' => 'Kısıtlamak istediğiniz e-posta alan adlarını vigül ile ayırarak yazınız. Kullanıcılara, uygulamaya erişmeden önce adreslerini doğrulamaları için bir e-posta gönderilecektir.
Başarılı bir kayıt işleminden sonra kullanıcıların e-posta adreslerini değiştirebileceklerini unutmayın.', 'reg_confirm_restrict_domain_placeholder' => 'Hiçbir kısıtlama tanımlanmamış', + // Sorting Settings + 'sorting' => 'Sorting', + 'sorting_book_default' => 'Default Book Sort', + 'sorting_book_default_desc' => 'Select the default sort rule to apply to new books. This won\'t affect existing books, and can be overridden per-book.', + 'sorting_rules' => 'Sort Rules', + 'sorting_rules_desc' => 'These are predefined sorting operations which can be applied to content in the system.', + 'sort_rule_assigned_to_x_books' => 'Assigned to :count Book|Assigned to :count Books', + 'sort_rule_create' => 'Create Sort Rule', + 'sort_rule_edit' => 'Edit Sort Rule', + 'sort_rule_delete' => 'Delete Sort Rule', + 'sort_rule_delete_desc' => 'Remove this sort rule from the system. Books using this sort will revert to manual sorting.', + 'sort_rule_delete_warn_books' => 'This sort rule is currently used on :count book(s). Are you sure you want to delete this?', + 'sort_rule_delete_warn_default' => 'This sort rule is currently used as the default for books. Are you sure you want to delete this?', + 'sort_rule_details' => 'Sort Rule Details', + 'sort_rule_details_desc' => 'Set a name for this sort rule, which will appear in lists when users are selecting a sort.', + 'sort_rule_operations' => 'Sort Operations', + 'sort_rule_operations_desc' => 'Configure the sort actions to be performed by moving them from the list of available operations. Upon use, the operations will be applied in order, from top to bottom. Any changes made here will be applied to all assigned books upon save.', + 'sort_rule_available_operations' => 'Available Operations', + 'sort_rule_available_operations_empty' => 'No operations remaining', + 'sort_rule_configured_operations' => 'Configured Operations', + 'sort_rule_configured_operations_empty' => 'Drag/add operations from the "Available Operations" list', + 'sort_rule_op_asc' => '(Asc)', + 'sort_rule_op_desc' => '(Desc)', + 'sort_rule_op_name' => 'Name - Alphabetical', + 'sort_rule_op_name_numeric' => '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_last' => 'Chapters Last', + // Maintenance settings 'maint' => 'Bakım', 'maint_image_cleanup' => 'Görselleri Temizle', @@ -109,7 +139,7 @@ 'recycle_bin_contents_empty' => 'Geri dönüşüm kutusu boş', 'recycle_bin_empty' => 'Geri Dönüşüm Kutusunu Boşalt', 'recycle_bin_empty_confirm' => 'Bu işlem, her bir öğenin içinde bulunan içerik de dahil olmak üzere geri dönüşüm kutusundaki tüm öğeleri kalıcı olarak imha edecektir. Geri dönüşüm kutusunu boşaltmak istediğinizden emin misiniz?', - 'recycle_bin_destroy_confirm' => 'Bu işlem, bu öğeyi kalıcı olarak ve aşağıda listelenen alt öğelerle birlikte sistemden silecek ve bu içeriği geri yükleyemeyeceksiniz. Bu öğeyi kalıcı olarak silmek istediğinizden emin misiniz?', + 'recycle_bin_destroy_confirm' => 'This action will permanently delete this item from the system, along with any child elements listed below, and you will not be able to restore this content. Are you sure you want to permanently delete this item?', 'recycle_bin_destroy_list' => 'Kalıcı Olarak Silinecek Öğeler', 'recycle_bin_restore_list' => 'Geri Yüklenecek Öğeler', 'recycle_bin_restore_confirm' => 'Bu eylem, tüm alt öğeler dahil olmak üzere silinen öğeyi orijinal konumlarına geri yükleyecektir. Orijinal konum o zamandan beri silinmişse ve şimdi geri dönüşüm kutusunda bulunuyorsa, üst öğenin de geri yüklenmesi gerekecektir.', @@ -162,6 +192,7 @@ 'role_access_api' => 'Sistem programlama arayüzüne (API) eriş', 'role_manage_settings' => 'Uygulama ayarlarını yönet', 'role_export_content' => 'İçeriği dışa aktar', + 'role_import_content' => 'Import content', 'role_editor_change' => 'Yazı editörünü değiştir', 'role_notifications' => 'Receive & manage notifications', 'role_asset' => 'Varlık Yetkileri', @@ -276,6 +307,14 @@ 'webhooks_last_errored' => 'Last Errored:', 'webhooks_last_error_message' => 'Son Hata Mesajı:', + // Licensing + 'licenses' => 'Licenses', + 'licenses_desc' => 'This page details license information for BookStack in addition to the projects & libraries that are used within BookStack. Many projects listed may only be used in a development context.', + 'licenses_bookstack' => 'BookStack License', + 'licenses_php' => 'PHP Library Licenses', + 'licenses_js' => 'JavaScript Library Licenses', + 'licenses_other' => 'Other Licenses', + 'license_details' => 'License Details', //! If editing translations files directly please ignore this in all //! languages apart from en. Content will be auto-copied from en. @@ -287,6 +326,7 @@ 'bs' => 'Bosanski', 'ca' => 'Català', 'cs' => 'Česky', + 'cy' => 'Cymraeg', 'da' => 'Danca', 'de' => 'Deutsch (Sie)', 'de_informal' => 'Deutsch (Du)', @@ -308,6 +348,7 @@ 'lt' => 'Lietuvių Kalba', 'lv' => 'Latviešu Valoda', 'nb' => 'Norsk (Bokmål)', + 'ne' => 'नेपाली', 'nn' => 'Nynorsk', 'nl' => 'Nederlands', 'pl' => 'Polski', diff --git a/lang/tr/validation.php b/lang/tr/validation.php index 640fe6131ce..9dbfadd6b9e 100644 --- a/lang/tr/validation.php +++ b/lang/tr/validation.php @@ -105,6 +105,11 @@ 'url' => ':attribute formatı geçersiz.', 'uploaded' => 'Dosya yüklemesi başarısız oldu. Sunucu, bu boyuttaki dosyaları kabul etmiyor olabilir.', + 'zip_file' => 'The :attribute needs to reference a file within the ZIP.', + 'zip_file_mime' => 'The :attribute needs to reference a file of type :validTypes, found :foundType.', + 'zip_model_expected' => 'Data object expected but ":type" found.', + 'zip_unique' => 'The :attribute must be unique for the object type within the ZIP.', + // Custom validation lines 'custom' => [ 'password-confirm' => [ diff --git a/lang/uk/activities.php b/lang/uk/activities.php index 1ab0eca61a8..e8b77e0d430 100644 --- a/lang/uk/activities.php +++ b/lang/uk/activities.php @@ -84,6 +84,14 @@ 'webhook_delete' => 'видалений вебхук', 'webhook_delete_notification' => 'Вебхуки успішно видалено', + // Imports + 'import_create' => 'створений імпорт', + 'import_create_notification' => 'Імпорт успішно завантажений', + 'import_run' => 'оновлений імпорт', + 'import_run_notification' => 'Контент успішно імпортовано', + 'import_delete' => 'видалений імпорт', + 'import_delete_notification' => 'Імпорт успішно видалено', + // Users 'user_create' => 'створений користувач', 'user_create_notification' => 'Користувач успішно створений', @@ -119,6 +127,14 @@ 'comment_update' => 'оновлено коментар', 'comment_delete' => 'видалений коментар', + // Sort Rules + 'sort_rule_create' => 'створене правило сортування', + 'sort_rule_create_notification' => 'Правило сортування успішно створено', + 'sort_rule_update' => 'оновлено правило сортування', + 'sort_rule_update_notification' => 'Правило сортування успішно оновлено', + 'sort_rule_delete' => 'видалено правило сортування', + 'sort_rule_delete_notification' => 'Правило сортування успішно видалено', + // Other 'permissions_update' => 'оновив дозволи', ]; diff --git a/lang/uk/auth.php b/lang/uk/auth.php index 649c6e8df7d..27aae39367e 100644 --- a/lang/uk/auth.php +++ b/lang/uk/auth.php @@ -91,7 +91,7 @@ 'mfa_option_totp_title' => 'Мобільний додаток', 'mfa_option_totp_desc' => 'Для використання багатофакторної автентифікації вам потрібен мобільний додаток, який підтримує TOTP такі як Google Authenticator, Authy або Microsoft Authenticator.', 'mfa_option_backup_codes_title' => 'Резервні коди', - 'mfa_option_backup_codes_desc' => 'Безпечно зберігайте набір резервних кодів з використанням одноразового використання, які можна увійти, щоб підтвердити свою особу.', + 'mfa_option_backup_codes_desc' => 'Генерує набір резервних кодів для використання одноразового використання, які ви вводите в систему, щоб перевірити свою особу. Переконайтеся, що зберігайте їх в безпечному та безпечному місці.', 'mfa_gen_confirm_and_enable' => 'Підтвердити та увімкнути', 'mfa_gen_backup_codes_title' => 'Налаштування резервних кодів', 'mfa_gen_backup_codes_desc' => 'Зберігайте список кодів в безпечному місці. Для доступу до системи ви зможете використовувати один з кодів як другий механізм аутентифікації.', diff --git a/lang/uk/common.php b/lang/uk/common.php index 7d79b53f260..d71f5716200 100644 --- a/lang/uk/common.php +++ b/lang/uk/common.php @@ -20,7 +20,7 @@ 'description' => 'Опис', 'role' => 'Роль', 'cover_image' => 'Обкладинка', - 'cover_image_description' => 'Це зображення має бути приблизно 440x250px.', + 'cover_image_description' => 'Це зображення має бути приблизно 440х250пікс, щоб його було легко масштабувати та обводити відповідно до інтерфейсу користувача у різних сценаріях, як це необхідно, тому реальні розміри для відображення відрізняються.', // Actions 'actions' => 'Дії', @@ -30,6 +30,8 @@ 'create' => 'Створити', 'update' => 'Оновити', 'edit' => 'Редагувати', + 'archive' => 'Архів', + 'unarchive' => 'Розархівувати', 'sort' => 'Сортувати', 'move' => 'Перемістити', 'copy' => 'Копіювати', @@ -107,4 +109,7 @@ // Not directly used but available for convenience to users. 'privacy_policy' => 'Політика приватності', 'terms_of_service' => 'Умови використання', + + // OpenSearch + 'opensearch_description' => 'Шукати :appName', ]; diff --git a/lang/uk/editor.php b/lang/uk/editor.php index 412ff49cfc1..b9024e052b4 100644 --- a/lang/uk/editor.php +++ b/lang/uk/editor.php @@ -13,6 +13,7 @@ 'cancel' => 'Скасувати', 'save' => 'Зберегти', 'close' => 'Закрити', + 'apply' => 'Застосувати', 'undo' => 'Відмінити', 'redo' => 'Відновити', 'left' => 'Ліворуч', @@ -47,6 +48,7 @@ 'superscript' => 'Верхній індекс', 'subscript' => 'Нижній індекс', 'text_color' => 'Колір тексту', + 'highlight_color' => 'Колір підсвічування', 'custom_color' => 'Власний колір', 'remove_color' => 'Видалити колір', 'background_color' => 'Колір фону', @@ -81,6 +83,9 @@ 'table_properties' => 'Властивості таблиці', 'table_properties_title' => 'Властивості таблиці', 'delete_table' => 'Видалити таблицю', + 'table_clear_formatting' => 'Очистити форматування таблиці', + 'resize_to_contents' => 'Змінити розмір до вмісту', + 'row_header' => 'Заголовок рядка', 'insert_row_before' => 'Вставити рядок перед', 'insert_row_after' => 'Вставити рядок після', 'delete_row' => 'Видалити рядок', @@ -144,6 +149,7 @@ 'url' => 'Адреса URL', 'text_to_display' => 'Текст для показу', 'title' => 'Назва', + 'browse_links' => 'Переглянути посилання', 'open_link' => 'Відкрити посилання', 'open_link_in' => 'Відкрити посилання в...', 'open_link_current' => 'Поточне вікно', @@ -160,6 +166,8 @@ 'about' => 'Про редактор', 'about_title' => 'Про WYSIWYG редактор', 'editor_license' => 'Ліцензія редактора і авторські права', + 'editor_lexical_license' => 'Цей редактор побудований як форк :lexicalLink який поширюється під ліцензією MIT.', + 'editor_lexical_license_link' => 'Тут ви можете знайти повну інформацію про ліцензію.', 'editor_tiny_license' => 'Цей редактор побудований за допомогою :tinylink, яке надається за ліцензією MIT.', 'editor_tiny_license_link' => 'Тут можна знайти авторські та умови ліцензії.', 'save_continue' => 'Зберегти і продовжити', diff --git a/lang/uk/entities.php b/lang/uk/entities.php index 3e657cf1f5d..cf79b530cff 100644 --- a/lang/uk/entities.php +++ b/lang/uk/entities.php @@ -39,6 +39,34 @@ 'export_pdf' => 'PDF файл', 'export_text' => 'Текстовий файл', 'export_md' => 'Файл розмітки', + 'export_zip' => 'Портативний ZIP', + 'default_template' => 'Типовий шаблон сторінки', + 'default_template_explain' => 'Призначити шаблон сторінки, який буде використовуватися як типовий вміст для всіх сторінок, створених у цьому елементі. Майте на увазі, що ця сторінка буде використана лише у випадку, якщо вона має доступ до обраної сторінки шаблону.', + 'default_template_select' => 'Виберіть сторінку шаблону', + 'import' => 'Імпорт', + 'import_validate' => 'Перевірка імпорту', + 'import_desc' => 'Імпортувати книги, розділи і сторінки, використовуючи портативний zip-експорт з одного або іншого екземпляру. Виберіть ZIP-файл для продовження. Після завантаження файлу і підтвердження, ви зможете налаштувати та підтвердити імпорт в наступному вікні.', + 'import_zip_select' => 'Виберіть ZIP-файл для завантаження', + 'import_zip_validation_errors' => 'Під час перевірки вказаного ZIP-файлу були виявлені помилки:', + 'import_pending' => 'Іпорт на розгляді', + 'import_pending_none' => 'Імпорти не були розпочаті.', + 'import_continue' => 'Продовжити імпорт', + 'import_continue_desc' => 'Переглянути вміст через імпортування з завантаженого ZIP-файлу. Якщо готово, запустіть імпорт, щоб додати його вміст в цю систему. Завантажений ZIP файл імпорту буде автоматично вилучено при успішному імпорті.', + 'import_details' => 'Імпортувати деталі', + 'import_run' => 'Запустити імпорт', + 'import_size' => ':size Розмір Імпорту ZIP', + 'import_uploaded_at' => 'Завантажено :relativeTime', + 'import_uploaded_by' => 'Завантажено', + 'import_location' => 'Імпортувати місцезнаходження', + 'import_location_desc' => 'Вибір цільового розташування для імпортованого вмісту. Вам потрібні відповідні дозволи для створення локації, яке ви обрали.', + 'import_delete_confirm' => 'Ви впевнені, що бажаєте видалити цей імпорт?', + 'import_delete_desc' => 'Це видалить завантажений імпорт файлу ZIP, і його не можна буде скасувати.', + 'import_errors' => 'Помилки імпорту', + 'import_errors_desc' => 'Під час спроби імпорту відбулися наступні помилки:', + 'breadcrumb_siblings_for_page' => 'Переглянути інші сторінки', + 'breadcrumb_siblings_for_chapter' => 'Переглянути інші розділи', + 'breadcrumb_siblings_for_book' => 'Переглянути інші книги', + 'breadcrumb_siblings_for_bookshelf' => 'Переглянути інші полиці', // Permissions and restrictions 'permissions' => 'Дозволи', @@ -132,9 +160,6 @@ 'books_edit_named' => 'Редагувати книгу :bookName', 'books_form_book_name' => 'Назва книги', 'books_save' => 'Зберегти книгу', - 'books_default_template' => 'Типовий шаблон сторінки', - 'books_default_template_explain' => 'Призначити шаблон сторінки, який буде використовуватися як типовий вміст для всіх нових сторінок цієї книги. Майте на увазі, що це буде використано лише в тому випадку, якщо творець сторінки має доступ до обраної сторінки шаблону.', - 'books_default_template_select' => 'Виберіть сторінку шаблону', 'books_permissions' => 'Дозволи на книгу', 'books_permissions_updated' => 'Дозволи на книгу оновлено', 'books_empty_contents' => 'Для цієї книги не створено жодної сторінки або розділів.', @@ -145,7 +170,9 @@ 'books_search_this' => 'Шукати цю книгу', 'books_navigation' => 'Навігація по книзі', 'books_sort' => 'Сортувати вміст книги', - 'books_sort_desc' => 'Перекладіть розділи та сторінки в межах книги, щоб реорганізувати вміст. Інші книги можна додати, що дозволяє легко переміщати глави та сторінки між книгами.', + 'books_sort_desc' => 'Перекладіть розділи та сторінки в межах книги, щоб реорганізувати вміст. Інші книги можна додати, що дозволяє легко переміщати глави та сторінки між книгами. При необхідності правило автоматичного сортування може бути встановлено для автоматичного сортування вмісту цієї книги при змінах.', + 'books_sort_auto_sort' => 'Опція автоматичного сортування', + 'books_sort_auto_sort_active' => 'Автосортування : :sortName', 'books_sort_named' => 'Сортувати книгу :bookName', 'books_sort_name' => 'Сортувати за назвою', 'books_sort_created' => 'Сортувати за датою створення', @@ -207,7 +234,7 @@ 'pages_delete_draft' => 'Видалити чернетку', 'pages_delete_success' => 'Сторінка видалена', 'pages_delete_draft_success' => 'Чернетка видалена', - 'pages_delete_warning_template' => 'Ця сторінка використовується як шаблон сторінки за промовчанням. У цих книгах немає стандартного шаблону сторінки, призначеного після видалення цієї сторінки.', + 'pages_delete_warning_template' => 'Ця сторінка використовується в якості шаблону сторінки за промовчанням. У цих книгах або розділах більше не буде встановлено шаблон стандартної сторінки, який використовується після того, як ця сторінка буде видалена.', 'pages_delete_confirm' => 'Ви впевнені, що хочете видалити цю сторінку?', 'pages_delete_draft_confirm' => 'Ви впевнені, що хочете видалити цю чернетку?', 'pages_editing_named' => 'Редагування сторінки :pageName', @@ -224,6 +251,8 @@ 'pages_edit_switch_to_markdown_clean' => '(Очистити вміст)', 'pages_edit_switch_to_markdown_stable' => '(Стабілізувати вміст)', 'pages_edit_switch_to_wysiwyg' => 'Змінити редактор на WYSIWYG', + 'pages_edit_switch_to_new_wysiwyg' => 'Перейти на новий WYSIWYG', + 'pages_edit_switch_to_new_wysiwyg_desc' => '(На бета-тестування)', 'pages_edit_set_changelog' => 'Встановити журнал змін', 'pages_edit_enter_changelog_desc' => 'Введіть короткий опис внесених вами змін', 'pages_edit_enter_changelog' => 'Введіть список змін', @@ -243,6 +272,7 @@ 'pages_md_insert_drawing' => 'Вставити малюнок', 'pages_md_show_preview' => 'Показати попередній перегляд', 'pages_md_sync_scroll' => 'Синхронізація прокручування попереднього перегляду', + 'pages_md_plain_editor' => 'Текстовий редактор', 'pages_drawing_unsaved' => 'Знайдено незбережену чернетку', 'pages_drawing_unsaved_confirm' => 'Незбережені чернетки були знайдені з попередньої спроби зберегти звіт. Хочете відновити і продовжити редагування цієї чернетки?', 'pages_not_in_chapter' => 'Сторінка не знаходиться в розділі', @@ -367,8 +397,11 @@ 'comment' => 'Коментар', 'comments' => 'Коментарі', 'comment_add' => 'Додати коментар', + 'comment_none' => 'Немає коментарів для відображення', 'comment_placeholder' => 'Залиште коментар тут', - 'comment_count' => '{0} Без коментарів|{1} 1 коментар|[2,*] :count коментарі(в)', + 'comment_thread_count' => ':count тема коментарів|:count теми коментарів', + 'comment_archived_count' => 'Архівовано :count', + 'comment_archived_threads' => 'Архівовані теми', 'comment_save' => 'Зберегти коментар', 'comment_new' => 'Новий коментар', 'comment_created' => 'прокоментував :createDiff', @@ -377,8 +410,14 @@ 'comment_deleted_success' => 'Коментар видалено', 'comment_created_success' => 'Коментар додано', 'comment_updated_success' => 'Коментар оновлено', + 'comment_archive_success' => 'Коментар архівовано', + 'comment_unarchive_success' => 'Коментар розархівовано', + 'comment_view' => 'Переглянути коментар', + 'comment_jump_to_thread' => 'Перейти до теми', 'comment_delete_confirm' => 'Ви впевнені, що хочете видалити цей коментар?', 'comment_in_reply_to' => 'У відповідь на :commentId', + 'comment_reference' => 'По посиланню', + 'comment_reference_outdated' => '(Застарілий)', 'comment_editor_explain' => 'Ось коментарі, які залишилися на цій сторінці. Коментарі можна додати та керовані при перегляді збереженої сторінки.', // Revision diff --git a/lang/uk/errors.php b/lang/uk/errors.php index 533d3a1e2b5..eed61e8a027 100644 --- a/lang/uk/errors.php +++ b/lang/uk/errors.php @@ -10,6 +10,7 @@ // Auth 'error_user_exists_different_creds' => 'Користувач з електронною адресою: електронна адреса вже існує, але з іншими обліковими даними.', + 'auth_pre_register_theme_prevention' => 'Обліковий запис користувача не може бути зареєстрований за наданими деталями', 'email_already_confirmed' => 'Електронна пошта вже підтверджена, спробуйте увійти.', 'email_confirmation_invalid' => 'Цей токен підтвердження недійсний або вже був використаний, будь ласка, спробуйте знову зареєструватися.', 'email_confirmation_expired' => 'Термін дії токена підтвердження минув, новий електронний лист підтвердження був відправлений.', @@ -23,7 +24,6 @@ 'saml_invalid_response_id' => 'Запит із зовнішньої системи аутентифікації не розпізнається процесом, розпочатим цим додатком. Повернення назад після входу могла спричинити цю проблему.', 'saml_fail_authed' => 'Вхід із використанням «:system» не вдався, система не здійснила успішну авторизацію', 'oidc_already_logged_in' => 'Вже ввійшли в систему', - 'oidc_user_not_registered' => 'Користувач :name не зареєстровано і автоматична реєстрація відключена', 'oidc_no_email_address' => 'Не вдалося знайти адресу електронної пошти для цього користувача у даних, наданих зовнішньою системою автентифікації', 'oidc_fail_authed' => 'Увійти за допомогою :system не вдалося, система не надала успішної авторизації', 'social_no_action_defined' => 'Жодних дій не визначено', @@ -37,6 +37,7 @@ 'social_driver_not_found' => 'Драйвер для СоціальноїМережі не знайдено', 'social_driver_not_configured' => 'Ваші соціальні настройки :socialAccount не правильно налаштовані.', 'invite_token_expired' => 'Термін дії цього запрошення закінчився. Замість цього ви можете спробувати скинути пароль свого облікового запису.', + 'login_user_not_found' => 'Користувач для цієї дії не знайдений.', // System 'path_not_writable' => 'Не вдається завантажити шлях до файлу :filePath. Переконайтеся, що він доступний для запису на сервер.', @@ -77,6 +78,7 @@ // Users 'users_cannot_delete_only_admin' => 'Ви не можете видалити єдиного адміністратора', 'users_cannot_delete_guest' => 'Ви не можете видалити гостьового користувача', + 'users_could_not_send_invite' => 'Не вдалося створити користувача, оскільки не вдалося надіслати електронний лист із запрошенням', // Roles 'role_cannot_be_edited' => 'Цю роль не можна редагувати', @@ -103,6 +105,18 @@ 'app_down' => ':appName зараз недоступний', 'back_soon' => 'Він повернеться найближчим часом.', + // Import + 'import_zip_cant_read' => 'Не вдалося прочитати ZIP-файл.', + 'import_zip_cant_decode_data' => 'Не вдалося знайти і розшифрувати контент ZIP data.json.', + 'import_zip_no_data' => 'ZIP-файл не містить очікуваної книги, глави або вмісту сторінки.', + 'import_validation_failed' => 'Не вдалося виконати перевірку ZIP-адреси із помилками:', + 'import_zip_failed_notification' => 'Не вдалося імпортувати ZIP-файл.', + 'import_perms_books' => 'У Вас не вистачає необхідних прав для створення книг.', + 'import_perms_chapters' => 'Вам не вистачає необхідних дозволів для створення розділів.', + 'import_perms_pages' => 'У Вас немає необхідних прав для створення сторінок.', + 'import_perms_images' => 'У Вас немає необхідних прав для створення зображень.', + 'import_perms_attachments' => 'У Вас немає необхідних прав для створення вкладень.', + // API errors 'api_no_authorization_found' => 'У запиті не знайдено токен авторизації', 'api_bad_authorization_format' => 'У запиті знайдено токен авторизації, але формат недійсний', diff --git a/lang/uk/preferences.php b/lang/uk/preferences.php index 2383c6793f4..14989d2a740 100644 --- a/lang/uk/preferences.php +++ b/lang/uk/preferences.php @@ -27,7 +27,7 @@ 'notifications_save' => 'Зберегти налаштування', 'notifications_update_success' => 'Налаштування сповіщень було оновлено!', 'notifications_watched' => 'Переглянуті та ігноровані елементи', - 'notifications_watched_desc' => ' Нижче наведені предмети, які мають застосовані налаштування перегляду. Щоб оновити ваші налаштування для них, перегляньте елемент, а потім знайдіть параметри перегляду на бічній панелі.', + 'notifications_watched_desc' => 'Нижче наведені предмети, які мають застосовані налаштування перегляду. Щоб оновити ваші налаштування для них, перегляньте елемент, а потім знайдіть параметри перегляду на бічній панелі.', 'auth' => 'Доступ і безпека', 'auth_change_password' => 'Змінити пароль', diff --git a/lang/uk/settings.php b/lang/uk/settings.php index bd1a20ec8ab..3798f1b6078 100644 --- a/lang/uk/settings.php +++ b/lang/uk/settings.php @@ -74,6 +74,36 @@ 'reg_confirm_restrict_domain_desc' => 'Введіть список розділених комами доменів електронної пошти, до яких ви хочете обмежити реєстрацію. Користувачам буде надіслано електронне повідомлення для підтвердження своєї адреси, перш ніж дозволяти взаємодіяти з додатком.
Зауважте, що користувачі зможуть змінювати свої електронні адреси після успішної реєстрації.', 'reg_confirm_restrict_domain_placeholder' => 'Не встановлено обмежень', + // Sorting Settings + 'sorting' => 'Сортування', + 'sorting_book_default' => 'Типовий порядок сортування', + 'sorting_book_default_desc' => 'Виберіть правило сортування за замовчуванням для застосування нових книг. Це не вплине на існуючі книги, і може бути перевизначено для кожної книги.', + 'sorting_rules' => 'Сортувати правила', + 'sorting_rules_desc' => 'Це попередньо визначені операції сортування, які можуть бути застосовані до вмісту в системі.', + 'sort_rule_assigned_to_x_books' => 'Призначено :count книгу|Призначення на :count книг(и)', + 'sort_rule_create' => 'Створити правило сортування', + 'sort_rule_edit' => 'Змінити правило сортування', + 'sort_rule_delete' => 'Видалити правило сортування', + 'sort_rule_delete_desc' => 'Видалення даного правила сортування з системи. Книги за допомогою цього сортування будуть повернутися до ручного сортування.', + 'sort_rule_delete_warn_books' => 'Це правило сортування використовується на :count книг(у,и). Ви впевнені, що хочете видалити це?', + 'sort_rule_delete_warn_default' => 'Правило сортування в даний час використовується як правило за замовчуванням для книг. Ви впевнені, що хочете видалити це?', + 'sort_rule_details' => 'Опис правил сортування', + 'sort_rule_details_desc' => 'Вкажіть ім\'я для цього правила сортування, яке буде відображатися в списках при виборі сортування користувачем.', + 'sort_rule_operations' => 'Операції сортування', + 'sort_rule_operations_desc' => 'Налаштуйте дії, які слід виконати, переміщаючи їх зі списку доступних операцій. Після використання операцій буде застосовано відповідно до самого низу. Будь-які зміни, внесені сюди, будуть застосовані до всіх призначених книг при збереженні.', + 'sort_rule_available_operations' => 'Доступні операції', + 'sort_rule_available_operations_empty' => 'Не залишилось операцій', + 'sort_rule_configured_operations' => 'Налаштовані операції', + 'sort_rule_configured_operations_empty' => 'Перетягніть операції зі списку "Доступні операції"', + 'sort_rule_op_asc' => '(За зростанням)', + 'sort_rule_op_desc' => '(За спаданням)', + 'sort_rule_op_name' => 'Назва - за алфавітом', + 'sort_rule_op_name_numeric' => 'Назва - Числове', + 'sort_rule_op_created_date' => 'Дата створення', + 'sort_rule_op_updated_date' => 'Дата оновлення', + 'sort_rule_op_chapters_first' => 'Спочатку розділи', + 'sort_rule_op_chapters_last' => 'Розділи останні', + // Maintenance settings 'maint' => 'Обслуговування', 'maint_image_cleanup' => 'Очищення зображень', @@ -109,7 +139,7 @@ 'recycle_bin_contents_empty' => 'Зараз кошик порожній', 'recycle_bin_empty' => 'Очистити кошик', 'recycle_bin_empty_confirm' => 'Це назавжди знищить усі елементи в кошику, включаючи вміст кожного елементу. Ви впевнені, що хочете очистити кошик?', - 'recycle_bin_destroy_confirm' => 'Ця дія назавжди видалить цей об\'єкт із системи, а також усі дочірні об\'єкти вказані нижче, і ви не зможете відновити його. Ви впевнені, що хочете назавжди видалити цей об\'єкт?', + 'recycle_bin_destroy_confirm' => 'Ця дія назавжди видалить цей елемент з системи, разом з будь-яким дочірнім елементом, перерахованим нижче, і ви не зможете відновити цей контент. Ви дійсно бажаєте остаточно видалити цей елемент?', 'recycle_bin_destroy_list' => 'Елементи для знищення', 'recycle_bin_restore_list' => 'Елементи для відновлення', 'recycle_bin_restore_confirm' => 'Ця дія відновить видалений елемент у початкове місце, включаючи всі дочірні елементи. Якщо вихідне розташування відтоді було видалено, і знаходиться у кошику, батьківський елемент також потрібно буде відновити.', @@ -162,6 +192,7 @@ 'role_access_api' => 'Доступ до системного API', 'role_manage_settings' => 'Керування налаштуваннями програми', 'role_export_content' => 'Вміст експорту', + 'role_import_content' => 'Імпортувати вміст', 'role_editor_change' => 'Змінити редактор сторінок', 'role_notifications' => 'Отримувати та керувати повідомленнями', 'role_asset' => 'Дозволи', @@ -276,6 +307,14 @@ 'webhooks_last_errored' => 'Остання помилка:', 'webhooks_last_error_message' => 'Останнє повідомлення про помилку:', + // Licensing + 'licenses' => 'Ліцензії', + 'licenses_desc' => 'На цій сторінці детально описано ліцензійну інформацію для BookStack на додаток до проектів і бібліотек, які використовуються в BookStack. Багато проектів із списку можна використовувати лише в контексті розробки.', + 'licenses_bookstack' => 'Ліцензія BookStack', + 'licenses_php' => 'Ліцензії на бібліотеки PHP', + 'licenses_js' => 'Ліцензії бібліотеки JavaScript', + 'licenses_other' => 'Інші ліцензії', + 'license_details' => 'Про ліцензію', //! If editing translations files directly please ignore this in all //! languages apart from en. Content will be auto-copied from en. @@ -287,6 +326,7 @@ 'bs' => 'Bosanski', 'ca' => 'Català', 'cs' => 'Česky', + 'cy' => 'Cymraeg', 'da' => 'Dansk', 'de' => 'Deutsch (Sie)', 'de_informal' => 'Deutsch (Du)', @@ -308,6 +348,7 @@ 'lt' => 'Lietuvių Kalba', 'lv' => 'Latviešu Valoda', 'nb' => 'Norsk (Bokmål)', + 'ne' => 'नेपाली', 'nn' => 'Nynorsk', 'nl' => 'Nederlands', 'pl' => 'Polski', diff --git a/lang/uk/validation.php b/lang/uk/validation.php index 51a59166619..6161aba671a 100644 --- a/lang/uk/validation.php +++ b/lang/uk/validation.php @@ -105,6 +105,11 @@ 'url' => 'Формат поля :attribute неправильний.', 'uploaded' => 'Не вдалося завантажити файл. Сервер може не приймати файли такого розміру.', + 'zip_file' => 'Поле :attribute повинне вказувати файл в ZIP.', + 'zip_file_mime' => 'Поле :attribute повинне посилатись на файл типу :validtypes, знайдений :foundType.', + 'zip_model_expected' => 'Очікувався об’єкт даних, але знайдено ":type".', + 'zip_unique' => 'Поле :attribute має бути унікальним для типу об\'єкта в ZIP.', + // Custom validation lines 'custom' => [ 'password-confirm' => [ diff --git a/lang/uz/activities.php b/lang/uz/activities.php index d90317749a4..84764192d77 100644 --- a/lang/uz/activities.php +++ b/lang/uz/activities.php @@ -84,6 +84,14 @@ 'webhook_delete' => 'o‘chirilgan webhook', 'webhook_delete_notification' => 'Webhook muvaffaqiyatli o‘chirildi', + // Imports + 'import_create' => 'created import', + 'import_create_notification' => 'Import successfully uploaded', + 'import_run' => 'updated import', + 'import_run_notification' => 'Content successfully imported', + 'import_delete' => 'deleted import', + 'import_delete_notification' => 'Import successfully deleted', + // Users 'user_create' => 'created user', 'user_create_notification' => 'User successfully created', @@ -93,11 +101,11 @@ 'user_delete_notification' => 'Foydalanuvchi muvaffaqiyatli olib tashlandi', // API Tokens - 'api_token_create' => 'created api token', + 'api_token_create' => 'created API token', 'api_token_create_notification' => 'API token successfully created', - 'api_token_update' => 'updated api token', + 'api_token_update' => 'updated API token', 'api_token_update_notification' => 'API token successfully updated', - 'api_token_delete' => 'deleted api token', + 'api_token_delete' => 'deleted API token', 'api_token_delete_notification' => 'API token successfully deleted', // Roles @@ -119,6 +127,14 @@ 'comment_update' => 'updated comment', 'comment_delete' => 'deleted comment', + // Sort Rules + 'sort_rule_create' => 'created sort rule', + 'sort_rule_create_notification' => 'Sort rule successfully created', + 'sort_rule_update' => 'updated sort rule', + 'sort_rule_update_notification' => 'Sort rule successfully updated', + 'sort_rule_delete' => 'deleted sort rule', + 'sort_rule_delete_notification' => 'Sort rule successfully deleted', + // Other 'permissions_update' => 'yangilangan huquqlar', ]; diff --git a/lang/uz/auth.php b/lang/uz/auth.php index 18236a8fba6..1a22f0f1d33 100644 --- a/lang/uz/auth.php +++ b/lang/uz/auth.php @@ -91,7 +91,7 @@ 'mfa_option_totp_title' => 'Mobil ilova', 'mfa_option_totp_desc' => 'Ko‘p faktorli autentifikatsiyadan foydalanish uchun sizga Google Authenticator, Authy yoki Microsoft Authenticator kabi OTPni qo‘llab-quvvatlaydigan mobil ilova kerak bo‘ladi.', 'mfa_option_backup_codes_title' => 'Zaxira kodlari', - 'mfa_option_backup_codes_desc' => 'Shaxsingizni tasdiqlash uchun kiritishingiz mumkin bo‘lgan bir martalik zaxira kodlari to‘plamini xavfsiz saqlang.', + 'mfa_option_backup_codes_desc' => 'Generates a set of one-time-use backup codes which you\'ll enter on login to verify your identity. Make sure to store these in a safe & secure place.', 'mfa_gen_confirm_and_enable' => 'Tasdiqlash va yoqish', 'mfa_gen_backup_codes_title' => 'Zaxira kodlarini sozlash', 'mfa_gen_backup_codes_desc' => 'Quyidagi kodlar ro‘yxatini xavfsiz joyda saqlang. Tizimga kirishda siz kodlardan birini ikkinchi autentifikatsiya mexanizmi sifatida ishlatishingiz mumkin.', diff --git a/lang/uz/common.php b/lang/uz/common.php index 5e01378da7f..2c1788bee01 100644 --- a/lang/uz/common.php +++ b/lang/uz/common.php @@ -20,7 +20,7 @@ 'description' => 'Tavsif', 'role' => 'Rol', 'cover_image' => 'Muqova rasmi', - 'cover_image_description' => 'Bu rasm taxminan 440x250px boʻlishi kerak.', + 'cover_image_description' => 'This image should be approximately 440x250px although it will be flexibly scaled & cropped to fit the user interface in different scenarios as required, so actual dimensions for display will differ.', // Actions 'actions' => 'Harakatlar', @@ -30,6 +30,8 @@ 'create' => 'Yaratish', 'update' => 'Yangilash', 'edit' => 'Tahrirlash', + 'archive' => 'Archive', + 'unarchive' => 'Un-Archive', 'sort' => 'Saralash', 'move' => 'Ko‘chirish', 'copy' => 'Nusxalash', @@ -107,4 +109,7 @@ // Not directly used but available for convenience to users. 'privacy_policy' => 'Maxfiylik siyosati', 'terms_of_service' => 'Xizmat ko‘rsatish shartlari', + + // OpenSearch + 'opensearch_description' => 'Search :appName', ]; diff --git a/lang/uz/editor.php b/lang/uz/editor.php index 2ec35045739..a7f91d76916 100644 --- a/lang/uz/editor.php +++ b/lang/uz/editor.php @@ -13,6 +13,7 @@ 'cancel' => 'Bekor qilish', 'save' => 'Saqlash', 'close' => 'Yopish', + 'apply' => 'Apply', 'undo' => 'Bekor qilish', 'redo' => 'Qayta qiling', 'left' => 'Chapga', @@ -47,6 +48,7 @@ 'superscript' => 'Yuqori yozuv', 'subscript' => 'Subscript', 'text_color' => 'Matn rangi', + 'highlight_color' => 'Highlight color', 'custom_color' => 'Maxsus rang', 'remove_color' => 'Rangni olib tashlash', 'background_color' => 'Fon rangi', @@ -81,6 +83,9 @@ 'table_properties' => 'Jadval xususiyatlari', 'table_properties_title' => 'Jadval xususiyatlari', 'delete_table' => 'Jadvalni o‘chirish', + 'table_clear_formatting' => 'Clear table formatting', + 'resize_to_contents' => 'Resize to contents', + 'row_header' => 'Row header', 'insert_row_before' => 'Oldinga qator kiritish', 'insert_row_after' => 'Keyingi qatorni kiritish', 'delete_row' => 'Qatorni o‘chirish', @@ -144,6 +149,7 @@ 'url' => 'URL', 'text_to_display' => 'Ko‘rish uchun matn', 'title' => 'Sarlavha', + 'browse_links' => 'Browse links', 'open_link' => 'Havolani ochish', 'open_link_in' => 'Havolani ochish...', 'open_link_current' => 'Joriy oyna', @@ -160,6 +166,8 @@ 'about' => 'Muharrir haqida', 'about_title' => 'WYSIWYG muharriri haqida', 'editor_license' => 'Muharrir litsenziyasi va mualliflik huquqi', + 'editor_lexical_license' => 'This editor is built as a fork of :lexicalLink which is distributed under the MIT license.', + 'editor_lexical_license_link' => 'Full license details can be found here.', 'editor_tiny_license' => 'Ushbu muharrir MIT litsenziyasi ostida taqdim etilgan :tinyLink yordamida yaratilgan.', 'editor_tiny_license_link' => 'TinyMCE mualliflik huquqi va litsenziya tafsilotlarini bu yerda topishingiz mumkin.', 'save_continue' => 'Sahifani saqlang va Davom eting', diff --git a/lang/uz/entities.php b/lang/uz/entities.php index 0effadf46eb..e35a485b65d 100644 --- a/lang/uz/entities.php +++ b/lang/uz/entities.php @@ -39,6 +39,34 @@ 'export_pdf' => 'PDF holatida', 'export_text' => 'Oddiy matn holatida', 'export_md' => 'Markdown fayli holatida', + 'export_zip' => 'Portable ZIP', + 'default_template' => 'Default Page Template', + 'default_template_explain' => 'Assign a page template that will be used as the default content for all pages created within this item. Keep in mind this will only be used if the page creator has view access to the chosen template page.', + 'default_template_select' => 'Select a template page', + 'import' => 'Import', + 'import_validate' => 'Validate Import', + 'import_desc' => 'Import books, chapters & pages using a portable zip export from the same, or a different, instance. Select a ZIP file to proceed. After the file has been uploaded and validated you\'ll be able to configure & confirm the import in the next view.', + '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_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_size' => ':size Import ZIP Size', + 'import_uploaded_at' => 'Uploaded :relativeTime', + 'import_uploaded_by' => 'Uploaded by', + 'import_location' => 'Import Location', + '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_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', + 'breadcrumb_siblings_for_book' => 'Navigate siblings for book', + 'breadcrumb_siblings_for_bookshelf' => 'Navigate siblings for shelf', // Permissions and restrictions 'permissions' => 'Huquqlar', @@ -132,9 +160,6 @@ 'books_edit_named' => 'Kitobni tahrirlash: kitob nomi', 'books_form_book_name' => 'Kitob nomi', 'books_save' => 'Kitobni saqlash', - 'books_default_template' => 'Default Page Template', - 'books_default_template_explain' => 'Assign a page template that will be used as the default content for all new pages in this book. Keep in mind this will only be used if the page creator has view access to those chosen template page.', - 'books_default_template_select' => 'Select a template page', 'books_permissions' => 'Kitob ruxsatnomalari', 'books_permissions_updated' => 'Kitob ruxsatnomalari yangilandi', 'books_empty_contents' => 'Ushbu kitob uchun hech qanday sahifa yoki bob yaratilmagan.', @@ -145,7 +170,9 @@ 'books_search_this' => 'Ushbu kitobni qidiring', 'books_navigation' => 'Kitob navigatsiya', 'books_sort' => 'Kitob tarkibini saralash', - 'books_sort_desc' => 'Kitob mazmunini qayta tartibga solish uchun bo\'lim va sahifalarni ko\'chiring. Kitoblar o\'rtasida bo\'limlar va sahifalarni osongina ko\'chirish imkonini beruvchi boshqa kitoblar qo\'shilishi mumkin.', + 'books_sort_desc' => 'Move chapters and pages within a book to reorganise its contents. Other books can be added which allows easy moving of chapters and pages between books. Optionally an auto sort rule can be set to automatically sort this book\'s contents upon changes.', + 'books_sort_auto_sort' => 'Auto Sort Option', + 'books_sort_auto_sort_active' => 'Auto Sort Active: :sortName', 'books_sort_named' => 'Kitobni tartiblash: kitob nomi', 'books_sort_name' => 'Nomi bo\'yicha saralash', 'books_sort_created' => 'Yaratilgan sana bo\'yicha saralash', @@ -207,7 +234,7 @@ 'pages_delete_draft' => 'Qoralama sahifani oʻchirish', 'pages_delete_success' => 'Sahifa oʻchirildi', 'pages_delete_draft_success' => 'Qoralama sahifa oʻchirildi', - 'pages_delete_warning_template' => 'This page is in active use as a book default page template. These books will no longer have a default page template assigned after this page is deleted.', + 'pages_delete_warning_template' => 'This page is in active use as a book or chapter default page template. These books or chapters will no longer have a default page template assigned after this page is deleted.', 'pages_delete_confirm' => 'Haqiqatan ham bu sahifani oʻchirib tashlamoqchimisiz?', 'pages_delete_draft_confirm' => 'Haqiqatan ham bu qoralama sahifani oʻchirib tashlamoqchimisiz?', 'pages_editing_named' => 'Sahifani tahrirlash :pageName', @@ -224,6 +251,8 @@ 'pages_edit_switch_to_markdown_clean' => '(Toza tarkib)', 'pages_edit_switch_to_markdown_stable' => '(Barqaror tarkib)', 'pages_edit_switch_to_wysiwyg' => 'WYSIWYG muharririga o\'ting', + 'pages_edit_switch_to_new_wysiwyg' => 'Switch to new WYSIWYG', + 'pages_edit_switch_to_new_wysiwyg_desc' => '(In Beta Testing)', 'pages_edit_set_changelog' => 'O\'zgarishlar jurnalini o\'rnating', 'pages_edit_enter_changelog_desc' => 'Siz kiritgan o\'zgarishlarning qisqacha tavsifini kiriting', 'pages_edit_enter_changelog' => 'O\'zgarishlar jurnaliga kiring', @@ -243,6 +272,7 @@ 'pages_md_insert_drawing' => 'Chizma kiritish', 'pages_md_show_preview' => 'Ko‘rish', 'pages_md_sync_scroll' => 'Sinxronizatsiyani oldindan ko\'rish aylantirish', + 'pages_md_plain_editor' => 'Plaintext editor', 'pages_drawing_unsaved' => 'Saqlanmagan chizma topildi', 'pages_drawing_unsaved_confirm' => 'Saqlanmagan chizma maʼlumotlari avvalgi muvaffaqiyatsiz chizmani saqlash urinishidan topildi. Ushbu saqlanmagan chizmani qayta tiklash va tahrirlashni davom ettirmoqchimisiz?', 'pages_not_in_chapter' => 'Sahifa bir bobda emas', @@ -367,8 +397,11 @@ 'comment' => 'Izoh', 'comments' => 'Izohlar', 'comment_add' => 'Fikr qo\'shish', + 'comment_none' => 'No comments to display', 'comment_placeholder' => 'Bu yerda fikr qoldiring', - 'comment_count' => '{0} Sharhlar yoʻq|{1} 1 ta fikr|[2,*] :sharhlar soni', + 'comment_thread_count' => ':count Comment Thread|:count Comment Threads', + 'comment_archived_count' => ':count Archived', + 'comment_archived_threads' => 'Archived Threads', 'comment_save' => 'Fikrni saqlash', 'comment_new' => 'Yangi izoh', 'comment_created' => 'izoh berdi:createDiff', @@ -377,8 +410,14 @@ 'comment_deleted_success' => 'Fikr o‘chirildi', 'comment_created_success' => 'Fikr qo\'shildi', 'comment_updated_success' => 'Fikr yangilandi', + 'comment_archive_success' => 'Comment archived', + 'comment_unarchive_success' => 'Comment un-archived', + 'comment_view' => 'View comment', + 'comment_jump_to_thread' => 'Jump to thread', 'comment_delete_confirm' => 'Haqiqatan ham bu fikrni oʻchirib tashlamoqchimisiz?', 'comment_in_reply_to' => ':commentId ga javoban', + 'comment_reference' => 'Reference', + 'comment_reference_outdated' => '(Outdated)', 'comment_editor_explain' => 'Mana shu sahifada qolgan izohlar. Saqlangan sahifani ko\'rishda sharhlar qo\'shilishi va boshqarilishi mumkin.', // Revision diff --git a/lang/uz/errors.php b/lang/uz/errors.php index e37ca8bbe25..a0d86c44191 100644 --- a/lang/uz/errors.php +++ b/lang/uz/errors.php @@ -10,6 +10,7 @@ // Auth 'error_user_exists_different_creds' => 'E-pochta manzili boʻlgan foydalanuvchi allaqachon mavjud, ammo hisob ma\'lumotlari boshqacha.', + 'auth_pre_register_theme_prevention' => 'User account could not be registered for the provided details', 'email_already_confirmed' => 'Elektron pochta allaqachon tasdiqlangan, tizimga kiring.', 'email_confirmation_invalid' => 'Bu tasdiqlovchi token yaroqsiz yoki allaqachon ishlatilgan. Iltimos, qayta roʻyxatdan oʻtishga urinib koʻring.', 'email_confirmation_expired' => 'Tasdiqlash belgisi muddati tugadi, yangi tasdiqlovchi elektron pochta xabari yuborildi.', @@ -23,7 +24,6 @@ 'saml_invalid_response_id' => 'Tashqi autentifikatsiya tizimidagi so‘rov ushbu ilova tomonidan boshlangan jarayon tomonidan tan olinmaydi. Kirishdan keyin orqaga qaytish bu muammoga olib kelishi mumkin.', 'saml_fail_authed' => ':tizim yordamida tizimga kirish amalga oshmadi, tizim muvaffaqiyatli avtorizatsiyani taqdim etmadi', 'oidc_already_logged_in' => 'Allaqachon tizimga kirgan', - 'oidc_user_not_registered' => 'Foydalanuvchi :name roʻyxatdan oʻtmagan va avtomatik roʻyxatdan oʻtish oʻchirilgan', 'oidc_no_email_address' => 'Tashqi autentifikatsiya tizimi tomonidan taqdim etilgan maʼlumotlarda ushbu foydalanuvchi uchun elektron pochta manzili topilmadi', 'oidc_fail_authed' => ':tizim yordamida tizimga kirish amalga oshmadi, tizim muvaffaqiyatli avtorizatsiyani taqdim etmadi', 'social_no_action_defined' => 'Hech qanday harakat belgilanmagan', @@ -37,6 +37,7 @@ 'social_driver_not_found' => 'Ijtimoiy haydovchi topilmadi', 'social_driver_not_configured' => 'Sizning :socialAccount ijtimoiy sozlamalaringiz toʻgʻri sozlanmagan.', 'invite_token_expired' => 'Bu taklif havolasi muddati tugagan. Buning oʻrniga hisobingiz parolini tiklashga urinib koʻrishingiz mumkin.', + 'login_user_not_found' => 'A user for this action could not be found.', // System 'path_not_writable' => 'Fayl yoʻli :filePath faylini yuklab boʻlmadi. Uning serverga yozilishi mumkinligiga ishonch hosil qiling.', @@ -77,6 +78,7 @@ // Users 'users_cannot_delete_only_admin' => 'Siz yagona administratorni oʻchira olmaysiz', 'users_cannot_delete_guest' => 'Siz mehmon foydalanuvchini oʻchira olmaysiz', + 'users_could_not_send_invite' => 'Could not create user since invite email failed to send', // Roles 'role_cannot_be_edited' => 'Bu rolni tahrirlab bo‘lmaydi', @@ -103,6 +105,18 @@ 'app_down' => ':appName hozir ishlamayapti', 'back_soon' => 'Tez orada zaxiralanadi.', + // Import + 'import_zip_cant_read' => 'Could not read ZIP file.', + 'import_zip_cant_decode_data' => 'Could not find and decode ZIP data.json content.', + 'import_zip_no_data' => 'ZIP file data has no expected book, chapter or page content.', + 'import_validation_failed' => 'Import ZIP failed to validate with errors:', + 'import_zip_failed_notification' => 'Failed to import ZIP file.', + 'import_perms_books' => 'You are lacking the required permissions to create books.', + 'import_perms_chapters' => 'You are lacking the required permissions to create chapters.', + 'import_perms_pages' => 'You are lacking the required permissions to create pages.', + 'import_perms_images' => 'You are lacking the required permissions to create images.', + 'import_perms_attachments' => 'You are lacking the required permission to create attachments.', + // API errors 'api_no_authorization_found' => 'So‘rovda hech qanday avtorizatsiya belgisi topilmadi', 'api_bad_authorization_format' => 'So‘rovda avtorizatsiya belgisi topildi, lekin format noto‘g‘ri ko‘rindi', diff --git a/lang/uz/preferences.php b/lang/uz/preferences.php index 9dedb0fb545..de36b953e85 100644 --- a/lang/uz/preferences.php +++ b/lang/uz/preferences.php @@ -27,7 +27,7 @@ 'notifications_save' => 'Afzalliklarni saqlash', 'notifications_update_success' => 'Bildirishnoma sozlamalari yangilandi!', 'notifications_watched' => 'Koʻrilgan va e\'tiborga olinmagan narsalar', - 'notifications_watched_desc' => ' Quyida maxsus soat sozlamalari qoʻllaniladigan elementlar mavjud. Bular uchun afzalliklaringizni yangilash uchun elementni koʻring, soʻngra yon paneldagi tomosha parametrlarini toping.', + 'notifications_watched_desc' => 'Quyida maxsus soat sozlamalari qoʻllaniladigan elementlar mavjud. Bular uchun afzalliklaringizni yangilash uchun elementni koʻring, soʻngra yon paneldagi tomosha parametrlarini toping.', 'auth' => 'Access & Security', 'auth_change_password' => 'Change Password', diff --git a/lang/uz/settings.php b/lang/uz/settings.php index f0c23e44525..83fedc569ef 100644 --- a/lang/uz/settings.php +++ b/lang/uz/settings.php @@ -74,6 +74,36 @@ 'reg_confirm_restrict_domain_desc' => 'Roʻyxatdan oʻtishni cheklamoqchi boʻlgan elektron pochta domenlarining vergul bilan ajratilgan roʻyxatini kiriting. Ilova bilan ishlashga ruxsat berishdan oldin foydalanuvchilarga manzillarini tasdiqlash uchun elektron pochta xabari yuboriladi.
E\'tibor bering, foydalanuvchilar muvaffaqiyatli ro\'yxatdan o\'tgandan so\'ng elektron pochta manzillarini o\'zgartirishi mumkin.', 'reg_confirm_restrict_domain_placeholder' => 'Cheklov oʻrnatilmagan', + // Sorting Settings + 'sorting' => 'Sorting', + 'sorting_book_default' => 'Default Book Sort', + 'sorting_book_default_desc' => 'Select the default sort rule to apply to new books. This won\'t affect existing books, and can be overridden per-book.', + 'sorting_rules' => 'Sort Rules', + 'sorting_rules_desc' => 'These are predefined sorting operations which can be applied to content in the system.', + 'sort_rule_assigned_to_x_books' => 'Assigned to :count Book|Assigned to :count Books', + 'sort_rule_create' => 'Create Sort Rule', + 'sort_rule_edit' => 'Edit Sort Rule', + 'sort_rule_delete' => 'Delete Sort Rule', + 'sort_rule_delete_desc' => 'Remove this sort rule from the system. Books using this sort will revert to manual sorting.', + 'sort_rule_delete_warn_books' => 'This sort rule is currently used on :count book(s). Are you sure you want to delete this?', + 'sort_rule_delete_warn_default' => 'This sort rule is currently used as the default for books. Are you sure you want to delete this?', + 'sort_rule_details' => 'Sort Rule Details', + 'sort_rule_details_desc' => 'Set a name for this sort rule, which will appear in lists when users are selecting a sort.', + 'sort_rule_operations' => 'Sort Operations', + 'sort_rule_operations_desc' => 'Configure the sort actions to be performed by moving them from the list of available operations. Upon use, the operations will be applied in order, from top to bottom. Any changes made here will be applied to all assigned books upon save.', + 'sort_rule_available_operations' => 'Available Operations', + 'sort_rule_available_operations_empty' => 'No operations remaining', + 'sort_rule_configured_operations' => 'Configured Operations', + 'sort_rule_configured_operations_empty' => 'Drag/add operations from the "Available Operations" list', + 'sort_rule_op_asc' => '(Asc)', + 'sort_rule_op_desc' => '(Desc)', + 'sort_rule_op_name' => 'Name - Alphabetical', + 'sort_rule_op_name_numeric' => '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_last' => 'Chapters Last', + // Maintenance settings 'maint' => 'Xizmat', 'maint_image_cleanup' => 'Tasvirlarni tozalash', @@ -109,7 +139,7 @@ 'recycle_bin_contents_empty' => 'Qayta ishlash qutisi hozir bo\'sh', 'recycle_bin_empty' => 'Chiqindi qutisini bo\'shatish', 'recycle_bin_empty_confirm' => 'Bu axlat qutisidagi barcha narsalarni, shu jumladan har bir element ichidagi kontentni butunlay yo\'q qiladi. Chiqindi qutisini bo\'shatishga ishonchingiz komilmi?', - 'recycle_bin_destroy_confirm' => 'Bu amal ushbu elementni va quyida sanab o‘tilgan har qanday yordamchi elementlarni tizimdan butunlay o‘chirib tashlaydi va siz ushbu kontentni qayta tiklay olmaysiz. Haqiqatan ham bu elementni butunlay oʻchirib tashlamoqchimisiz?', + 'recycle_bin_destroy_confirm' => 'This action will permanently delete this item from the system, along with any child elements listed below, and you will not be able to restore this content. Are you sure you want to permanently delete this item?', 'recycle_bin_destroy_list' => 'Yo\'q qilinishi kerak bo\'lgan narsalar', 'recycle_bin_restore_list' => 'Qayta tiklanadigan narsalar', 'recycle_bin_restore_confirm' => 'Bu amal oʻchirilgan elementni, shu jumladan har qanday yordamchi elementlarni asl joyiga tiklaydi. Agar asl joy o\'chirilgan bo\'lsa va hozir axlat qutisida bo\'lsa, asosiy element ham tiklanishi kerak bo\'ladi.', @@ -162,6 +192,7 @@ 'role_access_api' => 'Kirish tizimi API', 'role_manage_settings' => 'Ilova sozlamalarini boshqaring', 'role_export_content' => 'Kontentni eksport qilish', + 'role_import_content' => 'Import content', 'role_editor_change' => 'Sahifa muharririni o\'zgartirish', 'role_notifications' => 'Bildirishnomalarni qabul qilish va boshqarish', 'role_asset' => 'Obyektga ruxsatlar', @@ -276,6 +307,14 @@ 'webhooks_last_errored' => 'Oxirgi xato:', 'webhooks_last_error_message' => 'Oxirgi xato xabari:', + // Licensing + 'licenses' => 'Licenses', + 'licenses_desc' => 'This page details license information for BookStack in addition to the projects & libraries that are used within BookStack. Many projects listed may only be used in a development context.', + 'licenses_bookstack' => 'BookStack License', + 'licenses_php' => 'PHP Library Licenses', + 'licenses_js' => 'JavaScript Library Licenses', + 'licenses_other' => 'Other Licenses', + 'license_details' => 'License Details', //! If editing translations files directly please ignore this in all //! languages apart from en. Content will be auto-copied from en. @@ -287,6 +326,7 @@ 'bs' => 'Bosanski', 'ca' => 'Català', 'cs' => 'Česky', + 'cy' => 'Cymraeg', 'da' => 'Dansk', 'de' => 'Deutsch (Sie)', 'de_informal' => 'Deutsch (Du)', @@ -308,6 +348,7 @@ 'lt' => 'Lietuvių Kalba', 'lv' => 'Latviešu Valoda', 'nb' => 'Norsk (Bokmål)', + 'ne' => 'नेपाली', 'nn' => 'Nynorsk', 'nl' => 'Nederlands', 'pl' => 'Polski', diff --git a/lang/uz/validation.php b/lang/uz/validation.php index 4606a012879..d9d0fa0b2af 100644 --- a/lang/uz/validation.php +++ b/lang/uz/validation.php @@ -105,6 +105,11 @@ 'url' => ':attribute URL formatida emas.', 'uploaded' => 'Faylni yuklashda xatolik. Server bunday hajmdagi faylllarni yuklamasligi mumkin.', + 'zip_file' => 'The :attribute needs to reference a file within the ZIP.', + 'zip_file_mime' => 'The :attribute needs to reference a file of type :validTypes, found :foundType.', + 'zip_model_expected' => 'Data object expected but ":type" found.', + 'zip_unique' => 'The :attribute must be unique for the object type within the ZIP.', + // Custom validation lines 'custom' => [ 'password-confirm' => [ diff --git a/lang/vi/activities.php b/lang/vi/activities.php index 17259f1e20d..00ec6ce04d5 100644 --- a/lang/vi/activities.php +++ b/lang/vi/activities.php @@ -59,7 +59,7 @@ 'favourite_remove_notification' => '":name" đã được gỡ khỏi danh sách yêu thích của bạn', // Watching - 'watch_update_level_notification' => 'Watch preferences successfully updated', + 'watch_update_level_notification' => 'Đã cập nhật tùy chọn xem thành công', // Auth 'auth_login' => 'đăng nhập', @@ -84,8 +84,16 @@ 'webhook_delete' => 'đã xóa webhook', 'webhook_delete_notification' => 'Webhook đã được xóa thành công', + // Imports + 'import_create' => 'đã tạo nhập', + 'import_create_notification' => 'Tải lên nhập thành công', + 'import_run' => 'đã nhập cập nhật', + 'import_run_notification' => 'Nội dung đã được nhập thành công', + 'import_delete' => 'Đã xóa nhập', + 'import_delete_notification' => 'Nhập đã được xóa thành công', + // Users - 'user_create' => 'người dùng đã tạo', + 'user_create' => 'đã tạo người dùng', 'user_create_notification' => 'Người dùng được tạo thành công', 'user_update' => 'người dùng được cập nhật', 'user_update_notification' => 'Người dùng được cập nhật thành công', @@ -93,17 +101,17 @@ 'user_delete_notification' => 'Người dùng đã được xóa thành công', // API Tokens - 'api_token_create' => 'created api token', - 'api_token_create_notification' => 'API token successfully created', - 'api_token_update' => 'updated api token', - 'api_token_update_notification' => 'API token successfully updated', - 'api_token_delete' => 'deleted api token', - 'api_token_delete_notification' => 'API token successfully deleted', + 'api_token_create' => 'Đã tạo Token API ', + 'api_token_create_notification' => 'Token API đã tạo thành công', + 'api_token_update' => 'Đã cập nhật token API ', + 'api_token_update_notification' => 'Token API đã cập nhật thành công', + 'api_token_delete' => 'Đã xóa token API', + 'api_token_delete_notification' => 'Đã xóa token API thành công', // Roles - 'role_create' => 'created role', + 'role_create' => 'Đã tạo vai trò', 'role_create_notification' => 'Vai trò mới đã được tạo thành công', - 'role_update' => 'updated role', + 'role_update' => 'Vai trò đã cập nhật', 'role_update_notification' => 'Vai trò đã được cập nhật thành công', 'role_delete' => 'đã xóa vai trò', 'role_delete_notification' => 'Vai trò đã được xóa thành công', @@ -119,6 +127,14 @@ 'comment_update' => 'cập nhật bình luận', 'comment_delete' => 'đã xóa bình luận', + // Sort Rules + 'sort_rule_create' => '', + 'sort_rule_create_notification' => '', + 'sort_rule_update' => 'xóa quy tắc sắp xếp', + 'sort_rule_update_notification' => 'Đã cập nhật quy tắc sắp xếp thành công', + 'sort_rule_delete' => 'xóa quy tắc sắp xếp', + 'sort_rule_delete_notification' => 'Đã xóa quy tắc sắp xếp thành công', + // Other 'permissions_update' => 'các quyền đã được cập nhật', ]; diff --git a/lang/vi/auth.php b/lang/vi/auth.php index 78624f22266..d53d2cf4cb6 100644 --- a/lang/vi/auth.php +++ b/lang/vi/auth.php @@ -66,9 +66,9 @@ 'email_not_confirmed' => 'Địa chỉ email chưa được xác nhận', 'email_not_confirmed_text' => 'Địa chỉ email của bạn hiện vẫn chưa được xác nhận.', - 'email_not_confirmed_click_link' => 'Vui lòng bấm vào liên kết trong mail được gửi trong thời gian ngắn ngay sau khi bạn đăng kí.', - 'email_not_confirmed_resend' => 'Nếu bạn không tìm thấy email bạn có thể yêu cầu gửi lại email xác nhận bằng cách gửi mẫu dưới đây.', - 'email_not_confirmed_resend_button' => 'Gửi lại email xác nhận', + 'email_not_confirmed_click_link' => 'Vui lòng bấm vào liên kết trong email được gửi trong thời gian ngắn ngay sau khi bạn đăng kí.', + 'email_not_confirmed_resend' => 'Nếu bạn không tìm thấy e-mail bạn có thể yêu cầu gửi lại e-mail xác nhận bằng cách gửi mẫu dưới đây.', + 'email_not_confirmed_resend_button' => 'Gửi lại e-mail xác nhận', // User Invite 'user_invite_email_subject' => 'Bạn được mời tham gia :appName!', @@ -91,7 +91,7 @@ 'mfa_option_totp_title' => 'Ứng dụng di động', 'mfa_option_totp_desc' => 'Để sử dụng xác thực đa lớp bạn cần ưng dụng trên điện thoại có hỗ trợ TOTP như Google Authenticator, Authy hoặc Microsoft Authenticator.', 'mfa_option_backup_codes_title' => 'Mã dự phòng', - 'mfa_option_backup_codes_desc' => 'Bảo mật việc lưu trữ mã dự phòng dùng một lần mà bạn có thể sử dụng để xác định danh tính của mình.', + 'mfa_option_backup_codes_desc' => 'Tạo một bộ mã dự phòng sử dụng một lần mà bạn sẽ nhập khi đăng nhập để xác minh danh tính của mình. Hãy đảm bảo cất giữ những thứ này ở nơi an toàn và bảo mật.', 'mfa_gen_confirm_and_enable' => 'Xác nhận và Mở', 'mfa_gen_backup_codes_title' => 'Cài đặt Mã dự phòng', 'mfa_gen_backup_codes_desc' => 'Lưu trữ các mã dưới đây ở một nơi an toàn. Khi truy cập vào hệ thống bạn sẽ có thể sử dụng được một trong các đoạn mã đó như là một phương thức xác thực dự phòng.', diff --git a/lang/vi/common.php b/lang/vi/common.php index 5215e8ab855..5035788f8fd 100644 --- a/lang/vi/common.php +++ b/lang/vi/common.php @@ -20,7 +20,7 @@ 'description' => 'Mô tả', 'role' => 'Vai trò', 'cover_image' => 'Ảnh bìa', - 'cover_image_description' => 'Ảnh nên có kích thước 440x250px.', + 'cover_image_description' => 'Hình ảnh này phải có kích thước khoảng 440x250px mặc dù nó sẽ được thu nhỏ & cắt xén linh hoạt để phù hợp với giao diện người dùng trong các trường hợp khác nhau theo yêu cầu, do đó kích thước thực tế để hiển thị sẽ khác nhau.', // Actions 'actions' => 'Hành động', @@ -30,6 +30,8 @@ 'create' => 'Tạo', 'update' => 'Cập nhật', 'edit' => 'Sửa', + 'archive' => 'Lưu trữ', + 'unarchive' => 'Bỏ lưu trữ', 'sort' => 'Sắp xếp', 'move' => 'Di chuyển', 'copy' => 'Sao chép', @@ -42,7 +44,7 @@ 'remove' => 'Xóa bỏ', 'add' => 'Thêm', 'configure' => 'Cấu hình', - 'manage' => 'Manage', + 'manage' => 'Quản lý', 'fullscreen' => 'Toàn màn hình', 'favourite' => 'Yêu thích', 'unfavourite' => 'Bỏ yêu thích', @@ -52,7 +54,7 @@ 'filter_clear' => 'Xóa bộ lọc', 'download' => 'Tải về', 'open_in_tab' => 'Mở trong thẻ mới', - 'open' => 'Open', + 'open' => 'Mở', // Sort Options 'sort_options' => 'Tùy Chọn Sắp Xếp', @@ -100,11 +102,15 @@ 'tab_content_label' => 'Tab: Hiển thị nội dung chính', // Email Content - 'email_action_help' => 'Nếu bạn đang có vấn đề trong việc bấm nút ":actionText", sao chép và dán địa chỉ URL dưới đây vào trình duyệt web:', + 'email_action_help' => 'Nếu bạn gặp sự cố khi nhấp vào nút ": actionText", hãy sao chép và dán URL bên dưới +vào trình duyệt web của bạn:', 'email_rights' => 'Bản quyền đã được bảo hộ', // Footer Link Options // Not directly used but available for convenience to users. 'privacy_policy' => 'Chính Sách Quyền Riêng Tư', 'terms_of_service' => 'Điều khoản Dịch vụ', + + // OpenSearch + 'opensearch_description' => 'Tìm kiếm :appName', ]; diff --git a/lang/vi/components.php b/lang/vi/components.php index 802d09d0b66..eb17b4acd05 100644 --- a/lang/vi/components.php +++ b/lang/vi/components.php @@ -34,8 +34,8 @@ 'image_delete_success' => 'Ảnh đã được xóa thành công', 'image_replace' => 'Thay thế hình ảnh', 'image_replace_success' => 'Đã cập nhật thành công tệp hình ảnh', - 'image_rebuild_thumbs' => 'Regenerate Size Variations', - 'image_rebuild_thumbs_success' => 'Image size variations successfully rebuilt!', + 'image_rebuild_thumbs' => 'Tái tạo các biến thể kích thước', + 'image_rebuild_thumbs_success' => 'Các biến thể kích thước hình ảnh được xây dựng lại thành công!', // Code Editor 'code_editor' => 'Sửa Mã', diff --git a/lang/vi/editor.php b/lang/vi/editor.php index d1eebd0b3e1..fd3dbdfb12e 100644 --- a/lang/vi/editor.php +++ b/lang/vi/editor.php @@ -10,14 +10,15 @@ 'general' => 'Tổng quát', 'advanced' => 'Nâng cao', 'none' => 'Không', - 'cancel' => 'Huỷ', + 'cancel' => 'Hủy', 'save' => 'Lưu', 'close' => 'Đóng', + 'apply' => 'Áp dụng', 'undo' => 'Hoàn tác', 'redo' => 'Làm lại', 'left' => 'Bên trái', 'center' => 'Chính giữa', - 'right' => 'Bên phải', + 'right' => 'Bên phải', 'top' => 'Trên cùng', 'middle' => 'Giữa', 'bottom' => 'Dưới cùng', @@ -29,7 +30,7 @@ // Toolbar 'formats' => 'Định dạng', 'header_large' => 'Tiêu đề lớn', - 'header_medium' => 'Tiêu đề trung binh', + 'header_medium' => 'Tiêu đề trung bình', 'header_small' => 'Tiêu đề nhỏ', 'header_tiny' => 'Tiêu đề cực nhỏ', 'paragraph' => 'Đoạn văn', @@ -38,17 +39,18 @@ 'callouts' => 'Chú thích', 'callout_information' => 'Thông tin', 'callout_success' => 'Thành công', - 'callout_warning' => 'Cánh báo', + 'callout_warning' => 'Cảnh báo', 'callout_danger' => 'Nguy hiểm', 'bold' => 'In đậm', 'italic' => 'In nghiêng', 'underline' => 'Gạch chân', 'strikethrough' => 'Gạch ngang', 'superscript' => 'Chỉ số trên', - 'subscript' => 'Đăng ký', + 'subscript' => 'Chỉ số dưới', 'text_color' => 'Màu chữ', + 'highlight_color' => 'Màu đánh dấu', 'custom_color' => 'Màu tùy chỉnh', - 'remove_color' => 'Xoá màu', + 'remove_color' => 'Xóa màu', 'background_color' => 'Màu nền', 'align_left' => 'Canh lề trái', 'align_center' => 'Căn giữa', @@ -67,7 +69,7 @@ 'insert_horizontal_line' => 'Chèn đường ngang', 'insert_code_block' => 'Chèn khối mã', 'edit_code_block' => 'Chỉnh sửa khối mã', - 'insert_drawing' => 'Chèn/chỉnh sửa bản vẻ', + 'insert_drawing' => 'Chèn/chỉnh sửa bản vẽ', 'drawing_manager' => 'Quản lý hình vẽ', 'insert_media' => 'Chèn/chỉnh sửa media', 'insert_media_title' => 'Chèn/chỉnh sửa media', @@ -75,12 +77,15 @@ 'source_code' => 'Mã nguồn', 'source_code_title' => 'Mã Nguồn', 'fullscreen' => 'Toàn màn hình', - 'image_options' => 'Tuỳ chọn hình ảnh', + 'image_options' => 'Tùy chọn hình ảnh', // Tables 'table_properties' => 'Thuộc tính bảng', 'table_properties_title' => 'Thuộc tính bảng', 'delete_table' => 'Xóa bảng', + 'table_clear_formatting' => 'Xóa định dạng bảng', + 'resize_to_contents' => 'Thay đổi kích thước theo nội dung', + 'row_header' => 'Tiêu đề hàng', 'insert_row_before' => 'Chèn thêm hàng ở trên', 'insert_row_after' => 'Chèn thêm hàng ở dưới', 'delete_row' => 'Xóa hàng', @@ -107,68 +112,71 @@ 'border_color' => 'Màu viền', 'row_properties' => 'Đặt thuộc tính hàng', 'row_properties_title' => 'Đặt thuộc tính hàng', - 'cut_row' => 'Tách cột', - 'copy_row' => 'Sao chép cột', - 'paste_row_before' => 'Dán cột vào bên trái', - 'paste_row_after' => 'Dán cột vào bên phải', + 'cut_row' => 'Cắt hàng', + 'copy_row' => 'Sao chép hàng', + 'paste_row_before' => 'Dán hàng vào bên trên', + 'paste_row_after' => 'Dán hàng vào bên dưới', 'row_type' => 'Kiểu hàng', 'row_type_header' => 'Tiêu đề', 'row_type_body' => 'Nội dung', - 'row_type_footer' => 'Cuối trang', + 'row_type_footer' => 'Chân trang', 'alignment' => 'Canh lề', 'cut_column' => 'Cắt cột', 'copy_column' => 'Sao chép cột', - 'paste_column_before' => 'Thêm cột phía trước', - 'paste_column_after' => 'Thêm cột phía sau', - 'cell_padding' => 'Cell padding', - 'cell_spacing' => 'Cell spacing', + 'paste_column_before' => 'Dán cột vào bên trái', + 'paste_column_after' => 'Dán cột vào bên phải', + 'cell_padding' => 'Đệm ô', + 'cell_spacing' => 'Khoảng cách ô', 'caption' => 'Chú thích', 'show_caption' => 'Hiện chú thích', 'constrain' => 'Ràng buộc các thuộc tính', - 'cell_border_solid' => 'Khối', - 'cell_border_dotted' => 'Điểm chấm', - 'cell_border_dashed' => 'Dashed', - 'cell_border_double' => 'Double', - 'cell_border_groove' => 'Groove', - 'cell_border_ridge' => 'Ridge', - 'cell_border_inset' => 'Inset', - 'cell_border_outset' => 'Outset', - 'cell_border_none' => 'None', + 'cell_border_solid' => 'Đặc', + 'cell_border_dotted' => 'Chấm chấm', + 'cell_border_dashed' => 'Nét đứt', + 'cell_border_double' => 'Đôi', + 'cell_border_groove' => 'Rãnh', + 'cell_border_ridge' => 'Gờ', + 'cell_border_inset' => 'Lõm', + 'cell_border_outset' => 'Lồi', + 'cell_border_none' => 'Không', 'cell_border_hidden' => 'Ẩn', // Images, links, details/summary & embed 'source' => 'Nguồn', 'alt_desc' => 'Mô tả thay thế', 'embed' => 'Mã nhúng', - 'paste_embed' => 'Dán mã đính kèm của bạn vào bên dưới:', + 'paste_embed' => 'Dán mã nhúng của bạn vào bên dưới:', 'url' => 'Đường dẫn', - 'text_to_display' => 'Các văn bản hiển thị', + 'text_to_display' => 'Văn bản hiển thị', 'title' => 'Tiêu đề', + 'browse_links' => 'Duyệt liên kết', 'open_link' => 'Mở liên kết', 'open_link_in' => 'Mở liên kết trong...', 'open_link_current' => 'Cửa sổ hiện tại', 'open_link_new' => 'Cửa sổ mới', 'remove_link' => 'Loại bỏ liên kết', - 'insert_collapsible' => 'Insert collapsible block', - 'collapsible_unwrap' => 'Unwrap', + 'insert_collapsible' => 'Chèn khối có thể thu gọn', + 'collapsible_unwrap' => 'Tháo bỏ', 'edit_label' => 'Chỉnh sửa nhãn', - 'toggle_open_closed' => 'Toggle open/closed', - 'collapsible_edit' => 'Edit collapsible block', - 'toggle_label' => 'Toggle label', + 'toggle_open_closed' => 'Chuyển đổi mở/đóng', + 'collapsible_edit' => 'Chỉnh sửa khối có thể thu gọn', + 'toggle_label' => 'Chuyển đổi nhãn', // About view 'about' => 'Giới thiệu về trình soạn thảo', 'about_title' => 'Giới thiệu về trình soạn thảo WYSIWYG', - 'editor_license' => 'Giáy phép & Bản quyền của trình soạn thảo', - 'editor_tiny_license' => 'Trình soạn thảo này được xây dựng bằng các sử dụng :tinyLink theo giấy phép MIT.', + 'editor_license' => 'Giấy phép & Bản quyền của trình soạn thảo', + 'editor_lexical_license' => 'Trình soạn thảo này được xây dựng dựa trên :lexicalLink được phân phối theo giấy phép MIT.', + 'editor_lexical_license_link' => 'Chi tiết giấy phép đầy đủ có thể tìm thấy tại đây.', + 'editor_tiny_license' => 'Trình soạn thảo này được xây dựng bằng cách sử dụng :tinyLink theo giấy phép MIT.', 'editor_tiny_license_link' => 'Chi tiết về bản quyền và giấy phép của TinyMCE có thể được tìm thấy tại đây.', 'save_continue' => 'Lưu trang & Tiếp tục', - 'callouts_cycle' => '(Keep pressing to toggle through types)', - 'link_selector' => 'Liên kết đến nội dung', + 'callouts_cycle' => '(Nhấn tiếp để chuyển đổi giữa các loại)', + 'link_selector' => 'Bộ chọn liên kết', 'shortcuts' => 'Phím tắt', 'shortcut' => 'Phím tắt', - 'shortcuts_intro' => 'The following shortcuts are available in the editor:', + 'shortcuts_intro' => 'Các phím tắt sau có sẵn trong trình soạn thảo:', 'windows_linux' => '(Windows/Linux)', 'mac' => '(Mac)', - 'description' => 'Description', + 'description' => 'Mô tả', ]; diff --git a/lang/vi/entities.php b/lang/vi/entities.php index 5740a348351..f538f993ca2 100644 --- a/lang/vi/entities.php +++ b/lang/vi/entities.php @@ -23,22 +23,50 @@ 'meta_updated' => 'Được cập nhật :timeLength', 'meta_updated_name' => 'Được cập nhật :timeLength bởi :user', 'meta_owned_name' => 'Được sở hữu bởi :user', - 'meta_reference_count' => 'Referenced by :count item|Referenced by :count items', + 'meta_reference_count' => 'Được tham chiếu bởi :count mục|Được tham chiếu bởi :count mục', 'entity_select' => 'Chọn thực thể', 'entity_select_lack_permission' => 'Bạn không có quyền để chọn mục này', 'images' => 'Ảnh', 'my_recent_drafts' => 'Bản nháp gần đây của tôi', - 'my_recently_viewed' => 'Xem gần đây', + 'my_recently_viewed' => 'Xem gần đây của tôi', 'my_most_viewed_favourites' => 'Yêu thích được tôi xem nhiều nhất', 'my_favourites' => 'Danh sách yêu thích của tôi', 'no_pages_viewed' => 'Bạn chưa xem bất cứ trang nào', 'no_pages_recently_created' => 'Không có trang nào được tạo gần đây', 'no_pages_recently_updated' => 'Không có trang nào được cập nhật gần đây', - 'export' => 'Kết xuất', + 'export' => 'Xuất', 'export_html' => 'Đang chứa tệp tin Web', 'export_pdf' => 'Tệp PDF', 'export_text' => 'Tệp văn bản thuần túy', - 'export_md' => 'Tệp Markdown', + 'export_md' => 'Tệp Markdown', + 'export_zip' => 'ZIP di động', + 'default_template' => 'Mẫu trang mặc định', + 'default_template_explain' => 'Chỉ định một mẫu trang sẽ được sử dụng làm nội dung mặc định cho tất cả các trang được tạo trong mục này. Hãy chú ý rằng điều này sẽ chỉ được sử dụng nếu người tạo trang có quyền truy cập xem vào trang mẫu đã chọn.', + 'default_template_select' => 'Chọn một trang mẫu', + 'import' => 'Nhập', + 'import_validate' => 'Xác thực nhập', + 'import_desc' => 'Nhập sách, chương & trang bằng cách sử dụng tệp xuất zip di động từ cùng một phiên bản hoặc một phiên bản khác. Chọn tệp ZIP để tiếp tục. Sau khi tệp được tải lên và xác thực, bạn sẽ có thể cấu hình & xác nhận việc nhập trong chế độ xem tiếp theo.', + 'import_zip_select' => 'Chọn tệp ZIP để tải lên', + 'import_zip_validation_errors' => 'Đã phát hiện lỗi khi xác thực tệp ZIP được cung cấp:', + 'import_pending' => 'Nhập đang chờ xử lý', + 'import_pending_none' => 'Chưa có lượt nhập nào được bắt đầu.', + 'import_continue' => 'Tiếp tục nhập', + 'import_continue_desc' => 'Xem lại nội dung sẽ được nhập từ tệp ZIP đã tải lên. Khi sẵn sàng, hãy chạy nhập để thêm nội dung của nó vào hệ thống này. Tệp nhập ZIP đã tải lên sẽ tự động bị xóa khi nhập thành công.', + 'import_details' => 'Chi tiết nhập', + 'import_run' => 'Chạy nhập', + 'import_size' => 'Kích thước tệp ZIP nhập: :size', + 'import_uploaded_at' => 'Đã tải lên :relativeTime', + 'import_uploaded_by' => 'Đã tải lên bởi', + 'import_location' => 'Vị trí nhập', + 'import_location_desc' => 'Chọn vị trí đích cho nội dung đã nhập của bạn. Bạn sẽ cần các quyền liên quan để tạo trong vị trí bạn chọn.', + 'import_delete_confirm' => 'Bạn có chắc chắn muốn xóa lượt nhập này không?', + 'import_delete_desc' => 'Thao tác này sẽ xóa tệp ZIP đã tải lên, và không thể hoàn tác.', + 'import_errors' => 'Lỗi khi nhập dữ liệu', + 'import_errors_desc' => 'Các lỗi sau đã xảy ra trong quá trình nhập:', + 'breadcrumb_siblings_for_page' => 'Điều hướng anh chị em cho trang', + 'breadcrumb_siblings_for_chapter' => 'Điều hướng anh chị em cho chương', + 'breadcrumb_siblings_for_book' => 'Điều hướng anh chị em cho sách', + 'breadcrumb_siblings_for_bookshelf' => 'Điều hướng anh chị em cho kệ sách', // Permissions and restrictions 'permissions' => 'Quyền', @@ -55,16 +83,16 @@ // Search 'search_results' => 'Kết quả Tìm kiếm', 'search_total_results_found' => 'Tìm thấy :count kết quả|:count tổng kết quả', - 'search_clear' => 'Xoá tìm kiếm', + 'search_clear' => 'Xóa tìm kiếm', 'search_no_pages' => 'Không trang nào khớp với tìm kiếm này', 'search_for_term' => 'Tìm kiếm cho :term', 'search_more' => 'Thêm kết quả', 'search_advanced' => 'Tìm kiếm Nâng cao', 'search_terms' => 'Cụm từ Tìm kiếm', - 'search_content_type' => 'Kiểu Nội dung', + 'search_content_type' => 'Kiểu Nội dung', 'search_exact_matches' => 'Hoàn toàn trùng khớp', - 'search_tags' => 'Tìm kiếm Tag', - 'search_options' => 'Tuỳ chọn', + 'search_tags' => 'Tìm kiếm Thẻ', + 'search_options' => 'Tùy chọn', 'search_viewed_by_me' => 'Được xem bởi tôi', 'search_not_viewed_by_me' => 'Không được xem bởi tôi', 'search_permissions_set' => 'Phân quyền', @@ -80,37 +108,37 @@ 'search_update' => 'Cập nhật tìm kiếm', // Shelves - 'shelf' => 'Giá', - 'shelves' => 'Giá', - 'x_shelves' => ':count Giá |:count Giá', - 'shelves_empty' => 'Không có giá nào được tạo', - 'shelves_create' => 'Tạo Giá mới', - 'shelves_popular' => 'Các Giá phổ biến', - 'shelves_new' => 'Các Giá mới', - 'shelves_new_action' => 'Giá mới', - 'shelves_popular_empty' => 'Các giá phổ biến sẽ xuất hiện ở đây.', - 'shelves_new_empty' => 'Các Giá được tạo gần đây sẽ xuất hiện ở đây.', - 'shelves_save' => 'Lưu Giá', - 'shelves_books' => 'Sách trên Giá này', - 'shelves_add_books' => 'Thêm sách vào Giá này', + 'shelf' => 'Giá sách', + 'shelves' => 'Giá sách', + 'x_shelves' => ':count Giá sách|:count Giá sách', + 'shelves_empty' => 'Không có giá sách nào được tạo', + 'shelves_create' => 'Tạo Giá sách mới', + 'shelves_popular' => 'Các Giá sách phổ biến', + 'shelves_new' => 'Các Giá sách mới', + 'shelves_new_action' => 'Giá sách mới', + 'shelves_popular_empty' => 'Các giá sách phổ biến sẽ xuất hiện ở đây.', + 'shelves_new_empty' => 'Các Giá sách được tạo gần đây sẽ xuất hiện ở đây.', + 'shelves_save' => 'Lưu Giá sách', + 'shelves_books' => 'Sách trên Giá sách này', + 'shelves_add_books' => 'Thêm sách vào Giá sách này', 'shelves_drag_books' => 'Kéo sách bên dưới để thêm vào kệ sách này', - 'shelves_empty_contents' => 'Giá này không có sách nào', + 'shelves_empty_contents' => 'Giá sách này không có sách nào', 'shelves_edit_and_assign' => 'Chỉnh sửa kệ để gán sách', 'shelves_edit_named' => 'Chỉnh sửa kệ :name', 'shelves_edit' => 'Chỉnh sửa kệ', 'shelves_delete' => 'Xóa kệ', 'shelves_delete_named' => 'Xóa kệ :name', 'shelves_delete_explain' => "Thao tác này sẽ xóa kệ có tên ':name'. Sách chứa sẽ không bị xóa.", - 'shelves_delete_confirmation' => 'Are you sure you want to delete this shelf?', - 'shelves_permissions' => 'Shelf Permissions', - 'shelves_permissions_updated' => 'Shelf Permissions Updated', - 'shelves_permissions_active' => 'Shelf Permissions Active', - 'shelves_permissions_cascade_warning' => 'Permissions on shelves do not automatically cascade to contained books. This is because a book can exist on multiple shelves. Permissions can however be copied down to child books using the option found below.', - 'shelves_permissions_create' => 'Shelf create permissions are only used for copying permissions to child books using the action below. They do not control the ability to create books.', + 'shelves_delete_confirmation' => 'Bạn có chắc chắn muốn xóa kệ sách này không?', + 'shelves_permissions' => 'Quyền cho kệ sách', + 'shelves_permissions_updated' => 'Quyền cho kệ sách đã được cập nhật', + 'shelves_permissions_active' => 'Quyền của kệ đang hoạt động', + 'shelves_permissions_cascade_warning' => 'Quyền trên kệ sách không tự động xếp theo các sách được chứa. Điều này là do một cuốn sách có thể tồn tại trên nhiều kệ. Tuy nhiên, quyền có thể được sao chép xuống sách con bằng cách sử dụng tùy chọn bên dưới.', + 'shelves_permissions_create' => 'Quyền tạo giá sách chỉ được sử dụng để sao chép quyền vào sách con bằng cách sử dụng tác vụ bên dưới. Chúng không kiểm soát khả năng tạo ra sách.', 'shelves_copy_permissions_to_books' => 'Sao chép các quyền cho sách', 'shelves_copy_permissions' => 'Sao chép các quyền', - 'shelves_copy_permissions_explain' => 'This will apply the current permission settings of this shelf to all books contained within. Before activating, ensure any changes to the permissions of this shelf have been saved.', - 'shelves_copy_permission_success' => 'Shelf permissions copied to :count books', + 'shelves_copy_permissions_explain' => 'Thao tác này sẽ áp dụng cài đặt quyền hiện tại của giá sách này cho tất cả sách có trong đó. Trước khi kích hoạt, hãy đảm bảo mọi thay đổi đối với quyền của giá sách này đã được lưu.', + 'shelves_copy_permission_success' => 'Đã sao chép quyền của kệ vào :count sách', // Books 'book' => 'Sách', @@ -132,20 +160,19 @@ 'books_edit_named' => 'Sửa sách :bookName', 'books_form_book_name' => 'Tên sách', 'books_save' => 'Lưu sách', - 'books_default_template' => 'Default Page Template', - 'books_default_template_explain' => 'Assign a page template that will be used as the default content for all new pages in this book. Keep in mind this will only be used if the page creator has view access to those chosen template page.', - 'books_default_template_select' => 'Select a template page', 'books_permissions' => 'Các quyền của cuốn sách', 'books_permissions_updated' => 'Các quyền của cuốn sách đã được cập nhật', 'books_empty_contents' => 'Không có trang hay chương nào được tạo cho cuốn sách này.', - 'books_empty_create_page' => 'Tao một trang mới', + 'books_empty_create_page' => 'Tạo một trang mới', 'books_empty_sort_current_book' => 'Sắp xếp cuốn sách này', 'books_empty_add_chapter' => 'Thêm một chương mới', 'books_permissions_active' => 'Đang bật các quyền hạn từ Sách', 'books_search_this' => 'Tìm cuốn sách này', 'books_navigation' => 'Điều hướng cuốn sách', 'books_sort' => 'Sắp xếp nội dung cuốn sách', - 'books_sort_desc' => 'Move chapters and pages within a book to reorganise its contents. Other books can be added which allows easy moving of chapters and pages between books.', + 'books_sort_desc' => 'Di chuyển các chương và trang trong một cuốn sách để sắp xếp lại nội dung của nó. Các sách khác có thể được thêm vào để dễ dàng di chuyển các chương và trang giữa các sách. Tùy chọn, một quy tắc sắp xếp tự động có thể được đặt để tự động sắp xếp nội dung cuốn sách này khi có thay đổi.', + 'books_sort_auto_sort' => 'Tùy chọn sắp xếp tự động', + 'books_sort_auto_sort_active' => 'Sắp xếp tự động đang hoạt động: :sortName', 'books_sort_named' => 'Sắp xếp sách :bookName', 'books_sort_name' => 'Sắp xếp theo tên', 'books_sort_created' => 'Sắp xếp theo ngày tạo', @@ -154,19 +181,19 @@ 'books_sort_chapters_last' => 'Các Chương cuối', 'books_sort_show_other' => 'Hiển thị các Sách khác', 'books_sort_save' => 'Lưu thứ tự mới', - 'books_sort_show_other_desc' => 'Add other books here to include them in the sort operation, and allow easy cross-book reorganisation.', - 'books_sort_move_up' => 'Move Up', - 'books_sort_move_down' => 'Move Down', - 'books_sort_move_prev_book' => 'Move to Previous Book', - 'books_sort_move_next_book' => 'Move to Next Book', - 'books_sort_move_prev_chapter' => 'Move Into Previous Chapter', - 'books_sort_move_next_chapter' => 'Move Into Next Chapter', - 'books_sort_move_book_start' => 'Move to Start of Book', - 'books_sort_move_book_end' => 'Move to End of Book', - 'books_sort_move_before_chapter' => 'Move to Before Chapter', - 'books_sort_move_after_chapter' => 'Move to After Chapter', - 'books_copy' => 'Copy Book', - 'books_copy_success' => 'Book successfully copied', + 'books_sort_show_other_desc' => 'Thêm các sách khác vào đây để đưa chúng vào thao tác sắp xếp và cho phép sắp xếp lại nhiều sách dễ dàng.', + 'books_sort_move_up' => 'Đưa lên trên', + 'books_sort_move_down' => 'Đưa xuống dưới', + 'books_sort_move_prev_book' => 'Chuyển tới sách phía trước', + 'books_sort_move_next_book' => 'Chuyển tới sách phía sau', + 'books_sort_move_prev_chapter' => 'Chuyển sang chương trước', + 'books_sort_move_next_chapter' => 'Chuyển sang chương tiếp theo', + 'books_sort_move_book_start' => 'Di chuyển đến đầu sách', + 'books_sort_move_book_end' => 'Di chuyển đến cuối sách', + 'books_sort_move_before_chapter' => 'Chuyển về trước chương', + 'books_sort_move_after_chapter' => 'Chuyển tới chương sau', + 'books_copy' => 'Sao chép sách', + 'books_copy_success' => 'Đã sao chép thành công', // Chapters 'chapter' => 'Chương', @@ -184,14 +211,14 @@ 'chapters_save' => 'Lưu Chương', 'chapters_move' => 'Di chuyển Chương', 'chapters_move_named' => 'Di chuyển Chương :chapterName', - 'chapters_copy' => 'Copy Chapter', - 'chapters_copy_success' => 'Chapter successfully copied', + 'chapters_copy' => 'Sao chép chương', + 'chapters_copy_success' => 'Chương đã được sao chép thành công', 'chapters_permissions' => 'Quyền hạn Chương', 'chapters_empty' => 'Không có trang nào hiện có trong chương này.', 'chapters_permissions_active' => 'Đang bật các quyền hạn từ Chương', 'chapters_permissions_success' => 'Quyền hạn Chương được cập nhật', 'chapters_search_this' => 'Tìm kiếm trong Chương này', - 'chapter_sort_book' => 'Sort Book', + 'chapter_sort_book' => 'Sắp xếp sách', // Pages 'page' => 'Trang', @@ -207,7 +234,7 @@ 'pages_delete_draft' => 'Xóa Trang Nháp', 'pages_delete_success' => 'Đã xóa Trang', 'pages_delete_draft_success' => 'Đã xóa trang Nháp', - 'pages_delete_warning_template' => 'This page is in active use as a book default page template. These books will no longer have a default page template assigned after this page is deleted.', + 'pages_delete_warning_template' => 'Trang này đang được sử dụng làm mẫu trang mặc định của sách hoặc chương. Hãy chú ý: Những cuốn sách hoặc chương này sẽ không còn được chỉ định mẫu trang mặc định sau khi trang này bị xóa.', 'pages_delete_confirm' => 'Bạn có chắc chắn muốn xóa trang này?', 'pages_delete_draft_confirm' => 'Bạn có chắc chắn muốn xóa trang nháp này?', 'pages_editing_named' => 'Đang chỉnh sửa Trang :pageName', @@ -218,21 +245,23 @@ 'pages_editing_page' => 'Đang chỉnh sửa Trang', 'pages_edit_draft_save_at' => 'Bản nháp đã lưu lúc ', 'pages_edit_delete_draft' => 'Xóa Bản nháp', - 'pages_edit_delete_draft_confirm' => 'Are you sure you want to delete your draft page changes? All of your changes, since the last full save, will be lost and the editor will be updated with the latest page non-draft save state.', + 'pages_edit_delete_draft_confirm' => 'Bạn có chắc chắn muốn xóa các thay đổi trên trang nháp của mình không? Tất cả các thay đổi của bạn, kể từ lần lưu đầy đủ gần đây nhất, sẽ bị mất và trình chỉnh sửa sẽ được cập nhật với trạng thái lưu trang không phải bản nháp mới nhất.', 'pages_edit_discard_draft' => 'Hủy bỏ Bản nháp', - 'pages_edit_switch_to_markdown' => 'Switch to Markdown Editor', - 'pages_edit_switch_to_markdown_clean' => '(Clean Content)', - 'pages_edit_switch_to_markdown_stable' => '(Stable Content)', - 'pages_edit_switch_to_wysiwyg' => 'Switch to WYSIWYG Editor', + 'pages_edit_switch_to_markdown' => 'Chuyển sang trình soạn thảo Markdown', + 'pages_edit_switch_to_markdown_clean' => '(Nội dung sạch)', + 'pages_edit_switch_to_markdown_stable' => '(Nội dung ổn định)', + 'pages_edit_switch_to_wysiwyg' => 'Chuyển sang trình soạn thảo WYSIWYG', + 'pages_edit_switch_to_new_wysiwyg' => 'Chuyển sang WYSIWYG mới', + 'pages_edit_switch_to_new_wysiwyg_desc' => '(Đang thử nghiệm Beta)', 'pages_edit_set_changelog' => 'Đặt Changelog', 'pages_edit_enter_changelog_desc' => 'Viết mô tả ngắn gọn cho các thay đổi mà bạn tạo', 'pages_edit_enter_changelog' => 'Viết Changelog', - 'pages_editor_switch_title' => 'Switch Editor', - 'pages_editor_switch_are_you_sure' => 'Are you sure you want to change the editor for this page?', - 'pages_editor_switch_consider_following' => 'Consider the following when changing editors:', - 'pages_editor_switch_consideration_a' => 'Once saved, the new editor option will be used by any future editors, including those that may not be able to change editor type themselves.', - 'pages_editor_switch_consideration_b' => 'This can potentially lead to a loss of detail and syntax in certain circumstances.', - 'pages_editor_switch_consideration_c' => 'Tag or changelog changes, made since last save, won\'t persist across this change.', + 'pages_editor_switch_title' => 'Đổi trình soạn thảo', + 'pages_editor_switch_are_you_sure' => 'Bạn có chắc chắn muốn thay đổi trình soạn thảo cho trang này không?', + 'pages_editor_switch_consider_following' => 'Hãy cân nhắc những điều sau đây khi thay đổi trình soạn thảo:', + 'pages_editor_switch_consideration_a' => 'Sau khi lưu, tùy chọn trình soạn thảo mới sẽ được sử dụng bởi bất kỳ trình chỉnh sửa nào trong tương lai, kể cả những người không thể tự thay đổi loại trình chỉnh sửa.', + 'pages_editor_switch_consideration_b' => 'Điều này có khả năng dẫn đến mất chi tiết và cú pháp trong một số trường hợp nhất định.', + 'pages_editor_switch_consideration_c' => 'Các thay đổi thẻ hoặc nhật ký thay đổi, được thực hiện kể từ lần lưu cuối cùng, sẽ không tồn tại sau thay đổi này.', 'pages_save' => 'Lưu Trang', 'pages_title' => 'Tiêu đề Trang', 'pages_name' => 'Tên Trang', @@ -241,10 +270,11 @@ 'pages_md_insert_image' => 'Chèn hình ảnh', 'pages_md_insert_link' => 'Chèn liên kết thực thể', 'pages_md_insert_drawing' => 'Chèn bản vẽ', - 'pages_md_show_preview' => 'Show preview', - 'pages_md_sync_scroll' => 'Sync preview scroll', - 'pages_drawing_unsaved' => 'Unsaved Drawing Found', - 'pages_drawing_unsaved_confirm' => 'Unsaved drawing data was found from a previous failed drawing save attempt. Would you like to restore and continue editing this unsaved drawing?', + 'pages_md_show_preview' => 'Hiển thị bản xem trước', + 'pages_md_sync_scroll' => 'Đồng bộ hóa cuộn xem trước', + 'pages_md_plain_editor' => 'Trình soạn thảo văn bản thuần túy', + 'pages_drawing_unsaved' => 'Tìm thấy bản vẽ chưa lưu', + 'pages_drawing_unsaved_confirm' => 'Dữ liệu bản vẽ chưa lưu được tìm thấy từ lần lưu bản vẽ không thành công trước đó. Bạn có muốn khôi phục và tiếp tục chỉnh sửa bản vẽ chưa lưu này không?', 'pages_not_in_chapter' => 'Trang không nằm trong một chương', 'pages_move' => 'Di chuyển Trang', 'pages_copy' => 'Sao chép Trang', @@ -254,17 +284,17 @@ 'pages_permissions_success' => 'Quyền hạn Trang được cập nhật', 'pages_revision' => 'Phiên bản', 'pages_revisions' => 'Phiên bản Trang', - 'pages_revisions_desc' => 'Listed below are all the past revisions of this page. You can look back upon, compare, and restore old page versions if permissions allow. The full history of the page may not be fully reflected here since, depending on system configuration, old revisions could be auto-deleted.', + 'pages_revisions_desc' => 'Dưới đây là tất cả các bản sửa đổi trước đây của trang này. Bạn có thể xem lại, so sánh và khôi phục các phiên bản trang cũ nếu được phép. Lịch sử đầy đủ của trang có thể không được phản ánh đầy đủ ở đây vì, tùy thuộc vào cấu hình hệ thống, các bản sửa đổi cũ có thể tự động bị xóa.', 'pages_revisions_named' => 'Phiên bản Trang cho :pageName', 'pages_revision_named' => 'Phiên bản Trang cho :pageName', 'pages_revision_restored_from' => 'Khôi phục từ #:id; :summary', 'pages_revisions_created_by' => 'Tạo bởi', 'pages_revisions_date' => 'Ngày của Phiên bản', 'pages_revisions_number' => '#', - 'pages_revisions_sort_number' => 'Revision Number', + 'pages_revisions_sort_number' => 'Số phiên bản', 'pages_revisions_numbered' => 'Phiên bản #:id', 'pages_revisions_numbered_changes' => 'Các thay đổi của phiên bản #:id', - 'pages_revisions_editor' => 'Editor Type', + 'pages_revisions_editor' => 'Loại trình chỉnh sửa', 'pages_revisions_changelog' => 'Nhật ký thay đổi', 'pages_revisions_changes' => 'Các thay đổi', 'pages_revisions_current' => 'Phiên bản hiện tại', @@ -272,65 +302,65 @@ 'pages_revisions_restore' => 'Khôi phục', 'pages_revisions_none' => 'Trang này không có phiên bản nào', 'pages_copy_link' => 'Sao chép Liên kết', - 'pages_edit_content_link' => 'Jump to section in editor', - 'pages_pointer_enter_mode' => 'Enter section select mode', + 'pages_edit_content_link' => 'Chuyển đến phần trong trình chỉnh sửa', + 'pages_pointer_enter_mode' => 'Vào chế độ chọn phần', 'pages_pointer_label' => 'Tùy chọn phần trang', - 'pages_pointer_permalink' => 'Phần trang Liên kết cố định', + 'pages_pointer_permalink' => 'Liên kết cố định phần trang', 'pages_pointer_include_tag' => 'Phần trang bao gồm thẻ', 'pages_pointer_toggle_link' => 'Chế độ Liên kết cố định, Nhấn để hiển thị thẻ bao gồm', 'pages_pointer_toggle_include' => 'Bao gồm chế độ thẻ, Nhấn để hiển thị liên kết cố định', 'pages_permissions_active' => 'Đang bật các quyền hạn từ Trang', 'pages_initial_revision' => 'Đăng bài mở đầu', - 'pages_references_update_revision' => 'System auto-update of internal links', + 'pages_references_update_revision' => 'Hệ thống tự động cập nhật liên kết nội bộ', 'pages_initial_name' => 'Trang mới', 'pages_editing_draft_notification' => 'Bạn hiện đang chỉnh sửa một bản nháp được lưu cách đây :timeDiff.', 'pages_draft_edited_notification' => 'Trang này đã được cập nhật từ lúc đó. Bạn nên loại bỏ bản nháp này.', - 'pages_draft_page_changed_since_creation' => 'This page has been updated since this draft was created. It is recommended that you discard this draft or take care not to overwrite any page changes.', + 'pages_draft_page_changed_since_creation' => 'Trang này đã được cập nhật kể từ khi bản nháp này được tạo. Bạn nên bỏ bản nháp này hoặc cẩn thận không ghi đè bất kỳ thay đổi nào của trang.', 'pages_draft_edit_active' => [ 'start_a' => ':count người dùng đang bắt đầu chỉnh sửa trang này', 'start_b' => ':userName đang bắt đầu chỉnh sửa trang này', - 'time_a' => 'kể từ khi thang được cập nhật lần cuối', + 'time_a' => 'kể từ khi trang được cập nhật lần cuối', 'time_b' => 'trong :minCount phút cuối', 'message' => ':start :time. Hãy cẩn thận đừng ghi đè vào các bản cập nhật của nhau!', ], - 'pages_draft_discarded' => 'Draft discarded! The editor has been updated with the current page content', - 'pages_draft_deleted' => 'Draft deleted! The editor has been updated with the current page content', + 'pages_draft_discarded' => 'Bản nháp đã bị loại bỏ! Trình chỉnh sửa đã được cập nhật với nội dung trang hiện tại', + 'pages_draft_deleted' => 'Bản nháp đã bị xóa! Trình chỉnh sửa đã được cập nhật với nội dung trang hiện tại', 'pages_specific' => 'Trang cụ thể', 'pages_is_template' => 'Biểu mẫu trang', // Editor Sidebar - 'toggle_sidebar' => 'Toggle Sidebar', + 'toggle_sidebar' => 'Chuyển đổi thanh bên', 'page_tags' => 'Các Thẻ Trang', 'chapter_tags' => 'Các Thẻ Chương', 'book_tags' => 'Các Thẻ Sách', 'shelf_tags' => 'Các Thẻ Kệ', 'tag' => 'Nhãn', 'tags' => 'Các Thẻ', - 'tags_index_desc' => 'Tags can be applied to content within the system to apply a flexible form of categorization. Tags can have both a key and value, with the value being optional. Once applied, content can then be queried using the tag name and value.', + 'tags_index_desc' => 'Thẻ có thể được áp dụng cho nội dung trong hệ thống để áp dụng một hình thức phân loại linh hoạt. Thẻ có thể có cả khóa và giá trị, với giá trị là tùy chọn. Sau khi được áp dụng, nội dung sau đó có thể được truy vấn bằng tên thẻ và giá trị.', 'tag_name' => 'Tên Nhãn', 'tag_value' => 'Giá trị Thẻ (Tùy chọn)', 'tags_explain' => "Thêm vài thẻ để phân loại nội dung của bạn tốt hơn. \n Bạn có thể đặt giá trị cho thẻ để quản lí kĩ càng hơn.", 'tags_add' => 'Thêm thẻ khác', 'tags_remove' => 'Xóa thẻ này', - 'tags_usages' => 'Total tag usages', - 'tags_assigned_pages' => 'Assigned to Pages', - 'tags_assigned_chapters' => 'Assigned to Chapters', - 'tags_assigned_books' => 'Assigned to Books', - 'tags_assigned_shelves' => 'Assigned to Shelves', - 'tags_x_unique_values' => ':count unique values', - 'tags_all_values' => 'All values', - 'tags_view_tags' => 'View Tags', - 'tags_view_existing_tags' => 'View existing tags', - 'tags_list_empty_hint' => 'Tags can be assigned via the page editor sidebar or while editing the details of a book, chapter or shelf.', + 'tags_usages' => 'Tổng số lượt sử dụng thẻ', + 'tags_assigned_pages' => 'Đã gán cho Trang', + 'tags_assigned_chapters' => 'Đã gán cho Chương', + 'tags_assigned_books' => 'Đã gán cho Sách', + 'tags_assigned_shelves' => 'Đã gán cho Kệ', + 'tags_x_unique_values' => ':count giá trị duy nhất', + 'tags_all_values' => 'Tất cả giá trị', + 'tags_view_tags' => 'Xem Thẻ', + 'tags_view_existing_tags' => 'Xem các thẻ hiện có', + 'tags_list_empty_hint' => 'Thẻ có thể được gán thông qua thanh bên của trình chỉnh sửa trang hoặc trong khi chỉnh sửa chi tiết của sách, chương hoặc kệ.', 'attachments' => 'Các Đính kèm', 'attachments_explain' => 'Cập nhật một số tập tin và đính một số liên kết để hiển thị trên trang của bạn. Chúng được hiện trong sidebar của trang.', 'attachments_explain_instant_save' => 'Các thay đổi ở đây sẽ được lưu ngay lập tức.', 'attachments_upload' => 'Tải lên Tập tin', 'attachments_link' => 'Đính kèm Liên kết', - 'attachments_upload_drop' => 'Alternatively you can drag and drop a file here to upload it as an attachment.', + 'attachments_upload_drop' => 'Ngoài ra, bạn có thể kéo và thả tệp vào đây để tải lên làm tệp đính kèm.', 'attachments_set_link' => 'Đặt Liên kết', 'attachments_delete' => 'Bạn có chắc chắn muốn xóa tập tin đính kèm này?', - 'attachments_dropzone' => 'Drop files here to upload', + 'attachments_dropzone' => 'Thả tệp vào đây để tải lên', 'attachments_no_files' => 'Không có tập tin nào được tải lên', 'attachments_explain_link' => 'Bạn có thể đính kèm một liên kết nếu bạn lựa chọn không tải lên tập tin. Liên kết này có thể trỏ đến một trang khác hoặc một tập tin ở trên mạng (đám mây).', 'attachments_link_name' => 'Tên Liên kết', @@ -350,7 +380,7 @@ 'attachments_link_attached' => 'Liên kết được đính kèm đến trang thành công', 'templates' => 'Các Mẫu', 'templates_set_as_template' => 'Trang là một mẫu', - 'templates_explain_set_as_template' => 'Bạn có thể đặt trang này làm mẫu, nội dung của nó sẽ được sử dụng lại khi tạo các trang mới. Người dùng khác có thể sử dụng mẫu này nếu học có quyền hạn xem trang này.', + 'templates_explain_set_as_template' => 'Bạn có thể đặt trang này làm mẫu, nội dung của nó sẽ được sử dụng lại khi tạo các trang mới. Người dùng khác có thể sử dụng mẫu này nếu họ có quyền hạn xem trang này.', 'templates_replace_content' => 'Thay thế nội dung trang', 'templates_append_content' => 'Viết vào nội dung trang', 'templates_prepend_content' => 'Thêm vào đầu nội dung trang', @@ -367,19 +397,28 @@ 'comment' => 'Bình luận', 'comments' => 'Các bình luận', 'comment_add' => 'Thêm bình luận', + 'comment_none' => 'Không có bình luận nào để hiển thị', 'comment_placeholder' => 'Đăng bình luận tại đây', - 'comment_count' => '{0} Không có bình luận|{1} 1 Bình luận|[2,*] :count Bình luận', + 'comment_thread_count' => ':count Chuỗi bình luận|:count Chuỗi bình luận', + 'comment_archived_count' => ':count Đã lưu trữ', + 'comment_archived_threads' => 'Chuỗi đã lưu trữ', 'comment_save' => 'Lưu bình luận', 'comment_new' => 'Bình luận mới', 'comment_created' => 'đã bình luận :createDiff', 'comment_updated' => 'Đã cập nhật :updateDiff bởi :username', - 'comment_updated_indicator' => 'Updated', + 'comment_updated_indicator' => 'Đã cập nhật', 'comment_deleted_success' => 'Bình luận đã bị xóa', 'comment_created_success' => 'Đã thêm bình luận', 'comment_updated_success' => 'Bình luận đã được cập nhật', + 'comment_archive_success' => 'Đã lưu trữ bình luận', + 'comment_unarchive_success' => 'Đã bỏ lưu trữ bình luận', + 'comment_view' => 'Xem bình luận', + 'comment_jump_to_thread' => 'Chuyển đến chuỗi', 'comment_delete_confirm' => 'Bạn có chắc bạn muốn xóa bình luận này?', 'comment_in_reply_to' => 'Trả lời cho :commentId', - 'comment_editor_explain' => 'Here are the comments that have been left on this page. Comments can be added & managed when viewing the saved page.', + 'comment_reference' => 'Tham chiếu', + 'comment_reference_outdated' => '(Đã lỗi thời)', + 'comment_editor_explain' => 'Đây là những bình luận đã được để lại trên trang này. Bình luận có thể được thêm & quản lý khi xem trang đã lưu.', // Revision 'revision_delete_confirm' => 'Bạn có chắc bạn muốn xóa phiên bản này?', @@ -387,51 +426,51 @@ 'revision_cannot_delete_latest' => 'Không thể xóa phiên bản mới nhất.', // Copy view - 'copy_consider' => 'Please consider the below when copying content.', - 'copy_consider_permissions' => 'Custom permission settings will not be copied.', - 'copy_consider_owner' => 'You will become the owner of all copied content.', - 'copy_consider_images' => 'Page image files will not be duplicated & the original images will retain their relation to the page they were originally uploaded to.', - 'copy_consider_attachments' => 'Page attachments will not be copied.', - 'copy_consider_access' => 'A change of location, owner or permissions may result in this content being accessible to those previously without access.', + 'copy_consider' => 'Vui lòng xem xét những điều sau đây khi sao chép nội dung.', + 'copy_consider_permissions' => 'Cài đặt quyền tùy chỉnh sẽ không được sao chép.', + 'copy_consider_owner' => 'Bạn sẽ trở thành chủ sở hữu của tất cả nội dung được sao chép.', + 'copy_consider_images' => 'Các tệp hình ảnh trang sẽ không được nhân đôi & các hình ảnh gốc sẽ giữ lại mối quan hệ của chúng với trang mà chúng được tải lên ban đầu.', + 'copy_consider_attachments' => 'Các tệp đính kèm trang sẽ không được sao chép.', + 'copy_consider_access' => 'Việc thay đổi vị trí, chủ sở hữu hoặc quyền có thể dẫn đến nội dung này có thể truy cập được đối với những người trước đây không có quyền truy cập.', // Conversions - 'convert_to_shelf' => 'Convert to Shelf', - 'convert_to_shelf_contents_desc' => 'You can convert this book to a new shelf with the same contents. Chapters contained within this book will be converted to new books. If this book contains any pages, that are not in a chapter, this book will be renamed and contain such pages, and this book will become part of the new shelf.', - 'convert_to_shelf_permissions_desc' => 'Any permissions set on this book will be copied to the new shelf and to all new child books that don\'t have their own permissions enforced. Note that permissions on shelves do not auto-cascade to content within, as they do for books.', - 'convert_book' => 'Convert Book', - 'convert_book_confirm' => 'Are you sure you want to convert this book?', - 'convert_undo_warning' => 'This cannot be as easily undone.', - 'convert_to_book' => 'Convert to Book', - 'convert_to_book_desc' => 'You can convert this chapter to a new book with the same contents. Any permissions set on this chapter will be copied to the new book but any inherited permissions, from the parent book, will not be copied which could lead to a change of access control.', - 'convert_chapter' => 'Convert Chapter', - 'convert_chapter_confirm' => 'Are you sure you want to convert this chapter?', + 'convert_to_shelf' => 'Chuyển đổi thành Kệ sách', + 'convert_to_shelf_contents_desc' => 'Bạn có thể chuyển đổi cuốn sách này thành một kệ sách mới với nội dung tương tự. Các chương có trong cuốn sách này sẽ được chuyển đổi thành các cuốn sách mới. Nếu cuốn sách này chứa bất kỳ trang nào không nằm trong một chương, cuốn sách này sẽ được đổi tên và chứa các trang đó, và cuốn sách này sẽ trở thành một phần của kệ sách mới.', + 'convert_to_shelf_permissions_desc' => 'Bất kỳ quyền nào được đặt trên cuốn sách này sẽ được sao chép sang kệ sách mới và tất cả các cuốn sách con mới không có quyền riêng của chúng. Lưu ý rằng quyền trên kệ sách không tự động xếp tầng xuống nội dung bên trong, như đối với sách.', + 'convert_book' => 'Chuyển đổi sách', + 'convert_book_confirm' => 'Bạn có chắc chắn muốn chuyển đổi cuốn sách này?', + 'convert_undo_warning' => 'Việc này không thể dễ dàng hoàn tác.', + 'convert_to_book' => 'Chuyển đổi thành Sách', + 'convert_to_book_desc' => 'Bạn có thể chuyển đổi chương này thành một cuốn sách mới với nội dung tương tự. Bất kỳ quyền nào được đặt trên chương này sẽ được sao chép sang cuốn sách mới nhưng bất kỳ quyền thừa kế nào từ cuốn sách mẹ sẽ không được sao chép, điều này có thể dẫn đến thay đổi kiểm soát truy cập.', + 'convert_chapter' => 'Chuyển đổi Chương', + 'convert_chapter_confirm' => 'Bạn có chắc chắn muốn chuyển đổi chương này?', // References - 'references' => 'References', - 'references_none' => 'There are no tracked references to this item.', - 'references_to_desc' => 'Listed below is all the known content in the system that links to this item.', + 'references' => 'Tham chiếu', + 'references_none' => 'Không có tham chiếu nào được theo dõi đến mục này.', + 'references_to_desc' => 'Dưới đây là tất cả nội dung đã biết trong hệ thống liên kết đến mục này.', // Watch Options - 'watch' => 'Watch', - 'watch_title_default' => 'Default Preferences', - 'watch_desc_default' => 'Revert watching to just your default notification preferences.', - 'watch_title_ignore' => 'Ignore', - 'watch_desc_ignore' => 'Ignore all notifications, including those from user-level preferences.', - 'watch_title_new' => 'New Pages', - 'watch_desc_new' => 'Notify when any new page is created within this item.', - 'watch_title_updates' => 'All Page Updates', - 'watch_desc_updates' => 'Notify upon all new pages and page changes.', - 'watch_desc_updates_page' => 'Notify upon all page changes.', - 'watch_title_comments' => 'All Page Updates & Comments', - 'watch_desc_comments' => 'Notify upon all new pages, page changes and new comments.', - 'watch_desc_comments_page' => 'Notify upon page changes and new comments.', - 'watch_change_default' => 'Change default notification preferences', - 'watch_detail_ignore' => 'Ignoring notifications', - 'watch_detail_new' => 'Watching for new pages', - 'watch_detail_updates' => 'Watching new pages and updates', - 'watch_detail_comments' => 'Watching new pages, updates & comments', - 'watch_detail_parent_book' => 'Watching via parent book', - 'watch_detail_parent_book_ignore' => 'Ignoring via parent book', - 'watch_detail_parent_chapter' => 'Watching via parent chapter', - 'watch_detail_parent_chapter_ignore' => 'Ignoring via parent chapter', + 'watch' => 'Theo dõi', + 'watch_title_default' => 'Tùy chọn mặc định', + 'watch_desc_default' => 'Khôi phục việc theo dõi chỉ về các tùy chọn thông báo mặc định của bạn.', + 'watch_title_ignore' => 'Bỏ qua', + 'watch_desc_ignore' => 'Bỏ qua tất cả các thông báo, bao gồm cả những thông báo từ tùy chọn cấp người dùng.', + 'watch_title_new' => 'Trang mới', + 'watch_desc_new' => 'Thông báo khi bất kỳ trang mới nào được tạo trong mục này.', + 'watch_title_updates' => 'Tất cả cập nhật trang', + 'watch_desc_updates' => 'Thông báo khi có tất cả các trang mới và thay đổi trang.', + 'watch_desc_updates_page' => 'Thông báo khi có tất cả các thay đổi trang.', + 'watch_title_comments' => 'Tất cả cập nhật trang & Bình luận', + 'watch_desc_comments' => 'Thông báo khi có tất cả các trang mới, thay đổi trang và bình luận mới.', + 'watch_desc_comments_page' => 'Thông báo khi có thay đổi trang và bình luận mới.', + 'watch_change_default' => 'Thay đổi tùy chọn thông báo mặc định', + 'watch_detail_ignore' => 'Đang bỏ qua thông báo', + 'watch_detail_new' => 'Đang theo dõi các trang mới', + 'watch_detail_updates' => 'Đang theo dõi các trang mới và cập nhật', + 'watch_detail_comments' => 'Đang theo dõi các trang mới, cập nhật & bình luận', + 'watch_detail_parent_book' => 'Đang theo dõi thông qua sách cha', + 'watch_detail_parent_book_ignore' => 'Đang bỏ qua thông qua sách cha', + 'watch_detail_parent_chapter' => 'Đang theo dõi thông qua chương cha', + 'watch_detail_parent_chapter_ignore' => 'Đang bỏ qua thông qua chương cha', ]; diff --git a/lang/vi/errors.php b/lang/vi/errors.php index 1b69b581850..bae87dc38cd 100644 --- a/lang/vi/errors.php +++ b/lang/vi/errors.php @@ -10,6 +10,7 @@ // Auth 'error_user_exists_different_creds' => 'Đã có người sử dụng email :email nhưng với thông tin định danh khác.', + 'auth_pre_register_theme_prevention' => 'Tài khoản người dùng không thể đăng ký với các chi tiết được cung cấp', 'email_already_confirmed' => 'Email đã được xác nhận trước đó, Đang đăng nhập.', 'email_confirmation_invalid' => 'Token xác nhận này không hợp lệ hoặc đã được sử dụng trước đó, Xin hãy thử đăng ký lại.', 'email_confirmation_expired' => 'Token xác nhận đã hết hạn, Một email xác nhận mới đã được gửi.', @@ -23,7 +24,6 @@ 'saml_invalid_response_id' => 'Yêu cầu từ hệ thống xác thực bên ngoài không được nhận diện bởi quy trình chạy cho ứng dụng này. Điều hướng trở lại sau khi đăng nhập có thể đã gây ra vấn đề này.', 'saml_fail_authed' => 'Đăng nhập sử dụng :system thất bại, hệ thống không cung cấp được sự xác thực thành công', 'oidc_already_logged_in' => 'Đã đăng nhập', - 'oidc_user_not_registered' => 'Người dùng :name chưa được đăng ký và tự động đăng ký đang bị tắt', 'oidc_no_email_address' => 'Không tìm thấy địa chỉ email cho người dùng này, trong dữ liệu được cung cấp bới hệ thống xác thực ngoài', 'oidc_fail_authed' => 'Đăng nhập sử dụng :system thất bại, hệ thống không cung cấp được sự xác thực thành công', 'social_no_action_defined' => 'Không có hành động được xác định', @@ -37,36 +37,37 @@ 'social_driver_not_found' => 'Không tìm thấy driver cho MXH', 'social_driver_not_configured' => 'Cài đặt MXH :socialAccount của bạn đang không được cấu hình hợp lệ.', 'invite_token_expired' => 'Liên kết mời này đã hết hạn. Bạn có thể thử đặt lại mật khẩu của tài khoản.', + 'login_user_not_found' => 'Không tìm thấy người dùng cho hành động này.', // System 'path_not_writable' => 'Đường dẫn tệp tin :filePath không thể tải đến được. Đảm bảo rằng đường dẫn này có thể ghi được ở trên máy chủ.', 'cannot_get_image_from_url' => 'Không thể lấy ảnh từ :url', 'cannot_create_thumbs' => 'Máy chủ không thể tạo ảnh nhỏ. Vui lòng kiểm tra bạn đã cài đặt tiện ích mở rộng GD PHP.', 'server_upload_limit' => 'Máy chủ không cho phép tải lên kích thước này. Vui lòng thử lại với tệp tin nhỏ hơn.', - 'server_post_limit' => 'The server cannot receive the provided amount of data. Try again with less data or a smaller file.', + 'server_post_limit' => 'Máy chủ không thể nhận lượng dữ liệu được cung cấp. Hãy thử lại với ít dữ liệu hoặc tệp nhỏ hơn.', 'uploaded' => 'Máy chủ không cho phép tải lên kích thước này. Vui lòng thử lại với tệp tin nhỏ hơn.', // Drawing & Images 'image_upload_error' => 'Đã xảy ra lỗi khi đang tải lên ảnh', 'image_upload_type_error' => 'Ảnh đang được tải lên không hợp lệ', - 'image_upload_replace_type' => 'Image file replacements must be of the same type', - 'image_upload_memory_limit' => 'Failed to handle image upload and/or create thumbnails due to system resource limits.', - 'image_thumbnail_memory_limit' => 'Failed to create image size variations due to system resource limits.', - 'image_gallery_thumbnail_memory_limit' => 'Failed to create gallery thumbnails due to system resource limits.', - 'drawing_data_not_found' => 'Drawing data could not be loaded. The drawing file might no longer exist or you may not have permission to access it.', + 'image_upload_replace_type' => 'Các tệp hình ảnh thay thế phải cùng loại', + 'image_upload_memory_limit' => 'Không xử lý được hình ảnh tải lên và/hoặc tạo hình thu nhỏ do giới hạn tài nguyên hệ thống.', + 'image_thumbnail_memory_limit' => 'Không tạo được các biến thể kích thước hình ảnh do giới hạn tài nguyên hệ thống.', + 'image_gallery_thumbnail_memory_limit' => 'Không tạo được hình thu nhỏ thư viện do giới hạn tài nguyên hệ thống.', + 'drawing_data_not_found' => 'Không thể tải dữ liệu bản vẽ. Tệp bản vẽ có thể không còn tồn tại hoặc bạn không có quyền truy cập vào nó.', // Attachments 'attachment_not_found' => 'Không tìm thấy đính kèm', - 'attachment_upload_error' => 'An error occurred uploading the attachment file', + 'attachment_upload_error' => 'Đã xảy ra lỗi khi tải tệp đính kèm', // Pages 'page_draft_autosave_fail' => 'Lưu bản nháp thất bại. Đảm bảo rằng bạn có kết nối đến internet trước khi lưu trang này', - 'page_draft_delete_fail' => 'Failed to delete page draft and fetch current page saved content', + 'page_draft_delete_fail' => 'Không thể xóa bản nháp trang và lấy nội dung đã lưu của trang hiện tại', 'page_custom_home_deletion' => 'Không thể xóa trang khi nó đang được đặt là trang chủ', // Entities 'entity_not_found' => 'Không tìm thấy thực thể', - 'bookshelf_not_found' => 'Shelf not found', + 'bookshelf_not_found' => 'Không tìm thấy giá sách', 'book_not_found' => 'Không tìm thấy sách', 'page_not_found' => 'Không tìm thấy trang', 'chapter_not_found' => 'Không tìm thấy chương', @@ -77,6 +78,7 @@ // Users 'users_cannot_delete_only_admin' => 'Bạn không thể xóa quản trị viên duy nhất', 'users_cannot_delete_guest' => 'Bạn không thể xóa người dùng khách', + 'users_could_not_send_invite' => 'Không thể tạo người dùng vì email mời không gửi được', // Roles 'role_cannot_be_edited' => 'Không thể chỉnh sửa quyền này', @@ -103,6 +105,18 @@ 'app_down' => ':appName hiện đang ngoại tuyến', 'back_soon' => 'Nó sẽ sớm hoạt động trở lại.', + // Import + 'import_zip_cant_read' => 'Không thể đọc tệp ZIP.', + 'import_zip_cant_decode_data' => 'Không thể tìm và giải mã nội dung ZIP data.json.', + 'import_zip_no_data' => 'Dữ liệu tệp ZIP không có nội dung sách, chương hoặc trang mong đợi.', + 'import_validation_failed' => 'Nhập tệp ZIP không hợp lệ với các lỗi:', + 'import_zip_failed_notification' => 'Không thể nhập tệp ZIP.', + 'import_perms_books' => 'Bạn không có quyền cần thiết để tạo sách.', + 'import_perms_chapters' => 'Bạn không có quyền cần thiết để tạo chương.', + 'import_perms_pages' => 'Bạn không có quyền cần thiết để tạo trang.', + 'import_perms_images' => 'Bạn không có quyền cần thiết để tạo hình ảnh.', + 'import_perms_attachments' => 'Bạn không có quyền cần thiết để tạo tệp đính kèm.', + // API errors 'api_no_authorization_found' => 'Không tìm thấy token ủy quyền trong yêu cầu', 'api_bad_authorization_format' => 'Đã tìm thấy một token ủy quyền trong yêu cầu nhưng định dạng hiển thị không hợp lệ', @@ -115,5 +129,5 @@ 'maintenance_test_email_failure' => 'Lỗi khi gửi email thử:', // HTTP errors - 'http_ssr_url_no_match' => 'The URL does not match the configured allowed SSR hosts', + 'http_ssr_url_no_match' => 'URL không khớp với các máy chủ SSR được cấu hình cho phép', ]; diff --git a/lang/vi/notifications.php b/lang/vi/notifications.php index 1afd23f1dc4..a18695b2347 100644 --- a/lang/vi/notifications.php +++ b/lang/vi/notifications.php @@ -4,24 +4,24 @@ */ return [ - 'new_comment_subject' => 'New comment on page: :pageName', - 'new_comment_intro' => 'A user has commented on a page in :appName:', - 'new_page_subject' => 'New page: :pageName', - 'new_page_intro' => 'A new page has been created in :appName:', - 'updated_page_subject' => 'Updated page: :pageName', - 'updated_page_intro' => 'A page has been updated in :appName:', - 'updated_page_debounce' => 'To prevent a mass of notifications, for a while you won\'t be sent notifications for further edits to this page by the same editor.', + 'new_comment_subject' => 'Bình luận mới trên trang: :pageName', + 'new_comment_intro' => 'Một người dùng đã bình luận trên một trang tại :appName:', + 'new_page_subject' => 'Trang mới: :pageName', + 'new_page_intro' => 'Một trang mới đã được khởi tạo trong :appName:', + 'updated_page_subject' => 'Trang đã cập nhật: :pageName', + 'updated_page_intro' => 'Một trang mới đã được cập nhật trong :appName:', + 'updated_page_debounce' => 'Để tránh việc nhận quá nhiều thông báo, trong một thời gian, bạn sẽ không nhận được thông báo về những chỉnh sửa tiếp theo cho trang này từ cùng một biên tập viên.', - 'detail_page_name' => 'Page Name:', - 'detail_page_path' => 'Page Path:', - 'detail_commenter' => 'Commenter:', - 'detail_comment' => 'Comment:', - 'detail_created_by' => 'Created By:', - 'detail_updated_by' => 'Updated By:', + 'detail_page_name' => 'Tên Trang:', + 'detail_page_path' => 'Đường dẫn trang:', + 'detail_commenter' => 'Người bình luận:', + 'detail_comment' => 'Bình luận:', + 'detail_created_by' => 'Tạo bởi:', + 'detail_updated_by' => 'Cập nhật bởi:', - 'action_view_comment' => 'View Comment', - 'action_view_page' => 'View Page', + 'action_view_comment' => 'Xem bình luận', + 'action_view_page' => 'Xem Trang', - 'footer_reason' => 'This notification was sent to you because :link cover this type of activity for this item.', - 'footer_reason_link' => 'your notification preferences', + 'footer_reason' => 'Đây là thông báo được gửi tới bạn bởi vì :link bao gồm hoạt động cho mục này.', + 'footer_reason_link' => 'ưu tiên thông báo của bạn', ]; diff --git a/lang/vi/preferences.php b/lang/vi/preferences.php index 8c5fc52301a..76bc0be16a2 100644 --- a/lang/vi/preferences.php +++ b/lang/vi/preferences.php @@ -27,7 +27,7 @@ 'notifications_save' => 'Save Preferences', 'notifications_update_success' => 'Notification preferences have been updated!', 'notifications_watched' => 'Watched & Ignored Items', - 'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.', + 'notifications_watched_desc' => 'Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.', 'auth' => 'Access & Security', 'auth_change_password' => 'Change Password', diff --git a/lang/vi/settings.php b/lang/vi/settings.php index 929f1f7b225..1ff252702c0 100644 --- a/lang/vi/settings.php +++ b/lang/vi/settings.php @@ -16,7 +16,7 @@ 'app_customization' => 'Tuỳ biến', 'app_features_security' => 'Chức năng & Bảo mật', 'app_name' => 'Tên Ứng dụng', - 'app_name_desc' => 'Tên này được hiển thị trong header và trong bất kì email hệ thống được gửi.', + 'app_name_desc' => 'Tên này được hiển thị trong header và trong bất kỳ email hệ thống được gửi.', 'app_name_header' => 'Hiển thị tên trong header', 'app_public_access' => 'Quyền truy cập công khai', 'app_public_access_desc' => 'Bật tùy chọn này sẽ cho phép khách, người không cần đăng nhập, truy cập đến nội dung bản BookStack của bạn.', @@ -25,7 +25,7 @@ 'app_public_viewing' => 'Cho phép xem công khai?', 'app_secure_images' => 'Bảo mật tốt hơn cho việc tải lên ảnh', 'app_secure_images_toggle' => 'Bật bảo mật tốt hơn cho các ảnh được tải lên', - 'app_secure_images_desc' => 'Vì lí do hiệu năng, tất cả các ảnh đều được truy cập công khai. Tùy chọn này thêm một chuỗi ngẫu nhiên, khó đoán vào phần liên kết đến ảnh. Đảm bảo rằng tránh việc index thư mục để ngăn chặn việc truy cập đến ảnh một cách dễ dàng.', + 'app_secure_images_desc' => 'Vì lý do hiệu năng, tất cả các ảnh đều được truy cập công khai. Tùy chọn này thêm một chuỗi ngẫu nhiên, khó đoán vào phần liên kết đến ảnh. Đảm bảo rằng tránh việc index thư mục để ngăn chặn việc truy cập đến ảnh một cách dễ dàng.', 'app_default_editor' => 'Trình soạn thảo mặc định', 'app_default_editor_desc' => 'Chọn trình soạn thảo nào sẽ được sử dụng theo mặc định khi chỉnh sửa trang mới. Điều này có thể bị ghi đè ở cấp độ trang nơi quyền cho phép.', 'app_custom_html' => 'Tùy chọn nội dung Head HTML', @@ -33,24 +33,24 @@ 'app_custom_html_disabled_notice' => 'Nội dung tùy biến HTML head bị tắt tại trang cài đặt này để đảm bảo mọi thay đổi làm hỏng hệ thống có để được khôi phục.', 'app_logo' => 'Logo Ứng dụng', 'app_logo_desc' => 'Điều này được sử dụng trong thanh tiêu đề của ứng dụng, trong số những khu vực khác. Hình ảnh này nên có chiều cao 86px. Những hình ảnh lớn sẽ được thu nhỏ lại.', - 'app_icon' => 'Giểu tượng ứng dụng', + 'app_icon' => 'Biểu tượng ứng dụng', 'app_icon_desc' => 'Biểu tượng này được sử dụng cho các tab trình duyệt và các biểu tượng phím tắt. Đây phải là hình ảnh PNG hình vuông 256px.', 'app_homepage' => 'Trang chủ Ứng dụng', 'app_homepage_desc' => 'Chọn hiển thị để hiện tại trang chủ thay cho hiển thị mặc định. Quyền cho trang được bỏ qua cho các trang được chọn.', 'app_homepage_select' => 'Chọn một trang', - 'app_footer_links' => 'Liên kết cuối trang', + 'app_footer_links' => 'Liên kết chân trang', 'app_footer_links_desc' => 'Thêm liên kết để hiển thị trong phần chân trang. Chúng sẽ được hiển thị ở dưới cùng của hầu hết các trang, bao gồm cả những trang không yêu cầu đăng nhập. Bạn có thể sử dụng nhãn "trans::" để dùng bản dịch do hệ thống xác định. Ví dụ: Sử dụng "trans::common.privacy_policy" sẽ cung cấp văn bản được dịch là "Chính sách quyền riêng tư" và "trans::common.terms_of_service" sẽ cung cấp văn bản được dịch là "Điều khoản dịch vụ".', 'app_footer_links_label' => 'Nhãn liên kết', 'app_footer_links_url' => 'Địa chỉ liên kết', - 'app_footer_links_add' => 'Thêm Footer Link', + 'app_footer_links_add' => 'Thêm liên kết chân trang', 'app_disable_comments' => 'Tắt bình luận', 'app_disable_comments_toggle' => 'Tắt bình luận', 'app_disable_comments_desc' => 'Tắt các bình luận trên tất cả các trang của ứng dụng.
Các bình luận đã tồn tại sẽ không được hiển thị.', // Color settings - 'color_scheme' => 'Ứng dụng bảng màu', - 'color_scheme_desc' => 'Set the colors to use in the application user interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.', - 'ui_colors_desc' => 'Set the application primary color and default link color. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the application interface.', + 'color_scheme' => 'Bảng màu ứng dụng', + 'color_scheme_desc' => 'Đặt các màu được sử dụng trong giao diện người dùng của ứng dụng. Màu sắc có thể được cấu hình riêng cho chế độ tối và sáng để phù hợp nhất với chủ đề và đảm bảo khả năng đọc.', + 'ui_colors_desc' => 'Đặt màu chính của ứng dụng và màu liên kết mặc định. Màu chính chủ yếu được sử dụng cho biểu ngữ đầu trang, nút và trang trí giao diện. Màu liên kết mặc định được sử dụng cho các liên kết và hành động dựa trên văn bản, cả trong nội dung đã viết và trong giao diện ứng dụng.', 'app_color' => 'Màu cơ bản', 'link_color' => 'Màu liên kết mặc định', 'content_colors_desc' => 'Đặt màu cho tất cả các thành phần trong phân cấp tổ chức trang. Nên chọn màu có độ sáng tương tự với màu mặc định để có thể đọc được.', @@ -64,8 +64,8 @@ 'reg_settings' => 'Đăng ký', 'reg_enable' => 'Bật Đăng ký', 'reg_enable_toggle' => 'Bật đăng ký', - 'reg_enable_desc' => 'Khi đăng ký được bật người dùng sẽ có thể tự đăng ký để trở thành người dùng của ứng dụng. Khi đăng kí người dùng sẽ được cấp một quyền sử dụng mặc định.', - 'reg_default_role' => 'Quyền người dùng sử dụng mặc định sau khi đăng kí', + 'reg_enable_desc' => 'Khi đăng ký được bật, người dùng sẽ có thể tự đăng ký để trở thành người dùng của ứng dụng. Khi đăng kí, người dùng sẽ được cấp một quyền sử dụng mặc định.', + 'reg_default_role' => 'Quyền người dùng mặc định sau khi đăng kí', 'reg_enable_external_warning' => 'Tùy chọn trên bị bỏ qua khi xác thực từ bên ngoài LDAP hoặc SAML được bật. Tài khoản người dùng chưa phải là thành viên sẽ được tự động tạo nếu xác thực với hệ thống bên ngoài thành công.', 'reg_email_confirmation' => 'Xác nhận Email', 'reg_email_confirmation_toggle' => 'Yêu cầu xác nhận email', @@ -74,15 +74,45 @@ 'reg_confirm_restrict_domain_desc' => 'Điền dấu phẩy ngăn cách danh sách các tên miền email dành cho việc bạn muốn giới hạn đăng nhập. Người dùng sẽ nhận được email xác nhận địa chỉ của họ trước khi được phép tương tác với ứng dụng.
Lưu ý rằng người dùng có thể thay đổi địa chỉ email của họ sau khi đăng ký thành công.', 'reg_confirm_restrict_domain_placeholder' => 'Không có giới hạn nào được thiết lập', + // Sorting Settings + 'sorting' => 'Sắp xếp', + 'sorting_book_default' => 'Sắp xếp sách mặc định', + 'sorting_book_default_desc' => 'Chọn quy tắc sắp xếp mặc định để áp dụng cho sách mới. Điều này sẽ không ảnh hưởng đến các sách hiện có và có thể được ghi đè cho từng sách.', + 'sorting_rules' => 'Quy tắc sắp xếp', + 'sorting_rules_desc' => 'Đây là các thao tác sắp xếp được xác định trước có thể được áp dụng cho nội dung trong hệ thống.', + 'sort_rule_assigned_to_x_books' => 'Được gán cho :count sách|Được gán cho :count sách', + 'sort_rule_create' => 'Tạo quy tắc sắp xếp', + 'sort_rule_edit' => 'Chỉnh sửa quy tắc sắp xếp', + 'sort_rule_delete' => 'Xóa quy tắc sắp xếp', + 'sort_rule_delete_desc' => 'Xóa quy tắc sắp xếp này khỏi hệ thống. Sách sử dụng quy tắc này sẽ trở lại sắp xếp thủ công.', + 'sort_rule_delete_warn_books' => 'Quy tắc sắp xếp này hiện đang được sử dụng trên :count sách. Bạn có chắc chắn muốn xóa quy tắc này không?', + 'sort_rule_delete_warn_default' => 'Quy tắc sắp xếp này hiện đang được sử dụng làm mặc định cho sách. Bạn có chắc chắn muốn xóa quy tắc này không?', + 'sort_rule_details' => 'Chi tiết quy tắc sắp xếp', + 'sort_rule_details_desc' => 'Đặt tên cho quy tắc sắp xếp này, tên này sẽ xuất hiện trong danh sách khi người dùng chọn một sắp xếp.', + 'sort_rule_operations' => 'Thao tác sắp xếp', + 'sort_rule_operations_desc' => 'Cấu hình các hành động sắp xếp sẽ được thực hiện bằng cách di chuyển chúng từ danh sách các thao tác khả dụng. Khi sử dụng, các thao tác sẽ được áp dụng theo thứ tự, từ trên xuống dưới. Bất kỳ thay đổi nào được thực hiện ở đây sẽ được áp dụng cho tất cả các sách được gán khi lưu.', + 'sort_rule_available_operations' => 'Thao tác khả dụng', + 'sort_rule_available_operations_empty' => 'Không còn thao tác nào', + 'sort_rule_configured_operations' => 'Thao tác đã cấu hình', + 'sort_rule_configured_operations_empty' => 'Kéo/thêm thao tác từ danh sách "Thao tác khả dụng"', + 'sort_rule_op_asc' => '(Tăng dần)', + 'sort_rule_op_desc' => '(Giảm dần)', + 'sort_rule_op_name' => 'Tên - Theo bảng chữ cái', + 'sort_rule_op_name_numeric' => 'Tên - Theo số', + 'sort_rule_op_created_date' => 'Ngày tạo', + 'sort_rule_op_updated_date' => 'Ngày cập nhật', + 'sort_rule_op_chapters_first' => 'Chương trước', + 'sort_rule_op_chapters_last' => 'Chương sau', + // Maintenance settings 'maint' => 'Bảo trì', 'maint_image_cleanup' => 'Dọn dẹp ảnh', - 'maint_image_cleanup_desc' => 'Quét nội dung trang và phiên bản để kiểm tra xem các ảnh và hình vẽ nào đang được sử dụng và ảnh nào dư thừa. Đảm bảo rằng bạn đã tạo bản sao lưu toàn dữ liệu và ảnh trước khi chạy chức năng này.', + 'maint_image_cleanup_desc' => 'Quét nội dung trang và phiên bản để kiểm tra xem các ảnh và hình vẽ nào đang được sử dụng và ảnh nào dư thừa. Đảm bảo rằng bạn đã tạo bản sao lưu toàn bộ dữ liệu và ảnh trước khi chạy chức năng này.', 'maint_delete_images_only_in_revisions' => 'Cũng xóa hình ảnh chỉ tồn tại trong các phiên bản trang cũ', 'maint_image_cleanup_run' => 'Chạy Dọn dẹp', 'maint_image_cleanup_warning' => 'Đã tìm thấy :count ảnh có thể không được sử dụng. Bạn muốn chắc rằng muốn xóa các ảnh này?', 'maint_image_cleanup_success' => ':count ảnh có thể không được sử dụng đã được tìm thấy và xóa!', - 'maint_image_cleanup_nothing_found' => 'Không tìm thấy ảnh nào không được xử dụng, Không có gì để xóa!', + 'maint_image_cleanup_nothing_found' => 'Không tìm thấy ảnh nào không được sử dụng, Không có gì để xóa!', 'maint_send_test_email' => 'Gửi một email thử', 'maint_send_test_email_desc' => 'Chức năng này gửi một email thử đến địa chỉ email bạn chỉ định trong hồ sơ của mình.', 'maint_send_test_email_run' => 'Gửi email thử', @@ -109,26 +139,26 @@ 'recycle_bin_contents_empty' => 'Thùng rác hiện đang trống', 'recycle_bin_empty' => 'Dọn dẹp Thùng Rác', 'recycle_bin_empty_confirm' => 'Thao tác này sẽ hủy vĩnh viễn tất cả các mục trong thùng rác bao gồm cả nội dung có trong mỗi mục. Bạn có chắc chắn muốn làm trống thùng rác?', - 'recycle_bin_destroy_confirm' => 'Hành động này sẽ xóa vĩnh viễn mục này, cùng với bất kỳ phần tử con nào được liệt kê bên dưới, khỏi hệ thống và bạn sẽ không thể khôi phục nội dung này. Bạn có chắc chắn muốn xóa vĩnh viễn mặt hàng này không?', - 'recycle_bin_destroy_list' => 'Items to be Destroyed', - 'recycle_bin_restore_list' => 'Items to be Restored', - 'recycle_bin_restore_confirm' => 'This action will restore the deleted item, including any child elements, to their original location. If the original location has since been deleted, and is now in the recycle bin, the parent item will also need to be restored.', - 'recycle_bin_restore_deleted_parent' => 'The parent of this item has also been deleted. These will remain deleted until that parent is also restored.', - 'recycle_bin_restore_parent' => 'Restore Parent', - 'recycle_bin_destroy_notification' => 'Deleted :count total items from the recycle bin.', - 'recycle_bin_restore_notification' => 'Restored :count total items from the recycle bin.', + 'recycle_bin_destroy_confirm' => 'Hành động này sẽ xóa vĩnh viễn mục này khỏi hệ thống, cùng với bất kỳ phần tử con nào được liệt kê bên dưới và bạn sẽ không thể khôi phục nội dung này. Bạn có chắc chắn muốn xóa vĩnh viễn mục này không?', + 'recycle_bin_destroy_list' => 'Các mục sẽ bị hủy', + 'recycle_bin_restore_list' => 'Các mục sẽ được khôi phục', + 'recycle_bin_restore_confirm' => 'Hành động này sẽ khôi phục mục đã xóa, bao gồm mọi phần tử con, về vị trí ban đầu của chúng. Nếu vị trí ban đầu đã bị xóa và hiện đang nằm trong thùng rác, mục cha cũng sẽ cần được khôi phục.', + 'recycle_bin_restore_deleted_parent' => 'Mục cha của mục này cũng đã bị xóa. Chúng sẽ vẫn bị xóa cho đến khi mục cha đó cũng được khôi phục.', + 'recycle_bin_restore_parent' => 'Khôi phục mục cha', + 'recycle_bin_destroy_notification' => 'Đã xóa :count tổng số mục khỏi thùng rác.', + 'recycle_bin_restore_notification' => 'Đã khôi phục :count tổng số mục khỏi thùng rác.', // Audit Log - 'audit' => 'Audit Log', - 'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.', - 'audit_event_filter' => 'Event Filter', + 'audit' => 'Nhật ký kiểm tra', + 'audit_desc' => 'Nhật ký kiểm tra này hiển thị danh sách các hoạt động được theo dõi trong hệ thống. Danh sách này không được lọc không giống như các danh sách hoạt động tương tự trong hệ thống nơi các bộ lọc quyền được áp dụng.', + 'audit_event_filter' => 'Bộ lọc sự kiện', 'audit_event_filter_no_filter' => 'Không Lọc', 'audit_deleted_item' => 'Mục Đã Xóa', 'audit_deleted_item_name' => 'Tên: :name', 'audit_table_user' => 'Người dùng', 'audit_table_event' => 'Sự kiện', - 'audit_table_related' => 'Related Item or Detail', - 'audit_table_ip' => 'IP Address', + 'audit_table_related' => 'Mục hoặc chi tiết liên quan', + 'audit_table_ip' => 'Địa chỉ IP', 'audit_table_date' => 'Ngày hoạt động', 'audit_date_from' => 'Ngày từ khoảng', 'audit_date_to' => 'Ngày đến khoảng', @@ -136,22 +166,22 @@ // Role Settings 'roles' => 'Quyền', 'role_user_roles' => 'Quyền người dùng', - 'roles_index_desc' => 'Roles are used to group users & provide system permission to their members. When a user is a member of multiple roles the privileges granted will stack and the user will inherit all abilities.', - 'roles_x_users_assigned' => ':count user assigned|:count users assigned', - 'roles_x_permissions_provided' => ':count permission|:count permissions', - 'roles_assigned_users' => 'Assigned Users', - 'roles_permissions_provided' => 'Provided Permissions', + 'roles_index_desc' => 'Các quyền được sử dụng để nhóm người dùng và cung cấp quyền hệ thống cho các thành viên của họ. Khi một người dùng là thành viên của nhiều quyền, các đặc quyền được cấp sẽ chồng lên nhau và người dùng sẽ thừa hưởng tất cả các khả năng.', + 'roles_x_users_assigned' => ':count người dùng được gán|:count người dùng được gán', + 'roles_x_permissions_provided' => ':count quyền|:count quyền', + 'roles_assigned_users' => 'Người dùng được gán', + 'roles_permissions_provided' => 'Quyền được cung cấp', 'role_create' => 'Tạo quyền mới', 'role_delete' => 'Xóa quyền', 'role_delete_confirm' => 'Chức năng này sẽ xóa quyền với tên \':roleName\'.', 'role_delete_users_assigned' => 'Quyền này có :userCount người dùng được gán. Nếu bạn muốn di dời các người dùng từ quyền này hãy chọn một quyền mới bên dưới.', - 'role_delete_no_migration' => "Không di dời các người dùng", + 'role_delete_no_migration' => "Không di dời người dùng", 'role_delete_sure' => 'Bạn có chắc rằng muốn xóa quyền này?', 'role_edit' => 'Sửa quyền', 'role_details' => 'Thông tin chi tiết Quyền', 'role_name' => 'Tên quyền', 'role_desc' => 'Thông tin vắn tắt của Quyền', - 'role_mfa_enforced' => 'Requires Multi-Factor Authentication', + 'role_mfa_enforced' => 'Yêu cầu xác thực đa yếu tố', 'role_external_auth_id' => 'Mã của xác thực ngoài', 'role_system' => 'Quyền Hệ thống', 'role_manage_users' => 'Quản lý người dùng', @@ -161,14 +191,15 @@ 'role_manage_page_templates' => 'Quản lý các mẫu trang', 'role_access_api' => 'Truy cập đến API hệ thống', 'role_manage_settings' => 'Quản lý cài đặt của ứng dụng', - 'role_export_content' => 'Export content', - 'role_editor_change' => 'Change page editor', - 'role_notifications' => 'Receive & manage notifications', + 'role_export_content' => 'Xuất nội dung', + 'role_import_content' => 'Nhập nội dung', + 'role_editor_change' => 'Thay đổi trình soạn thảo trang', + 'role_notifications' => 'Nhận & quản lý thông báo', 'role_asset' => 'Quyền tài sản (asset)', - 'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.', - 'role_asset_desc' => 'Các quyền này điều khiển truy cập mặc định tới tài sản (asset) nằm trong hệ thống. Quyền tại Sách, Chường và Trang se ghi đè các quyền này.', + 'roles_system_warning' => 'Cần lưu ý rằng việc truy cập vào bất kỳ ba quyền trên có thể cho phép người dùng thay đổi đặc quyền của chính họ hoặc đặc quyền của những người khác trong hệ thống. Chỉ gán các vai trò có các quyền này cho những người dùng đáng tin cậy.', + 'role_asset_desc' => 'Các quyền này điều khiển truy cập mặc định tới tài sản (asset) nằm trong hệ thống. Quyền tại Sách, Chương và Trang sẽ ghi đè các quyền này.', 'role_asset_admins' => 'Quản trị viên được tự động cấp quyền truy cập đến toàn bộ nội dung, tuy nhiên các tùy chọn đó có thể hiện hoặc ẩn tùy chọn giao diện.', - 'role_asset_image_view_note' => 'This relates to visibility within the image manager. Actual access of uploaded image files will be dependant upon system image storage option.', + 'role_asset_image_view_note' => 'Điều này liên quan đến khả năng hiển thị trong trình quản lý hình ảnh. Quyền truy cập thực tế vào các tệp hình ảnh đã tải lên sẽ phụ thuộc vào tùy chọn lưu trữ hình ảnh của hệ thống.', 'role_all' => 'Tất cả', 'role_own' => 'Sở hữu', 'role_controlled_by_asset' => 'Kiểm soát các tài sản (asset) người dùng tải lên', @@ -178,7 +209,7 @@ // Users 'users' => 'Người dùng', - 'users_index_desc' => 'Create & manage individual user accounts within the system. User accounts are used for login and attribution of content & activity. Access permissions are primarily role-based but user content ownership, among other factors, may also affect permissions & access.', + 'users_index_desc' => 'Tạo và quản lý các tài khoản người dùng riêng lẻ trong hệ thống. Các tài khoản người dùng được sử dụng để đăng nhập và gán nội dung & hoạt động. Quyền truy cập chủ yếu dựa trên vai trò nhưng quyền sở hữu nội dung của người dùng, cùng với các yếu tố khác, cũng có thể ảnh hưởng đến quyền và truy cập.', 'user_profile' => 'Hồ sơ người dùng', 'users_add_new' => 'Thêm người dùng mới', 'users_search' => 'Tìm kiếm người dùng', @@ -189,45 +220,45 @@ 'users_role' => 'Quyền người dùng', 'users_role_desc' => 'Chọn quyền mà người dùng sẽ được gán. Nếu người dùng được gán nhiều quyền, các quyền hạn sẽ ghi đè lên nhau và họ sẽ nhận được tất cả các quyền hạn từ quyền được gán.', 'users_password' => 'Mật khẩu người dùng', - 'users_password_desc' => 'Set a password used to log-in to the application. This must be at least 8 characters long.', + 'users_password_desc' => 'Đặt mật khẩu được sử dụng để đăng nhập vào ứng dụng. Mật khẩu phải dài ít nhất 8 ký tự.', 'users_send_invite_text' => 'Bạn có thể chọn để gửi cho người dùng này một email mời, giúp họ có thể tự đặt mật khẩu cho chính họ. Nếu không bạn có thể đặt mật khẩu cho họ.', 'users_send_invite_option' => 'Gửi email mời người dùng', 'users_external_auth_id' => 'Mã của xác thực ngoài', - 'users_external_auth_id_desc' => 'When an external authentication system is in use (such as SAML2, OIDC or LDAP) this is the ID which links this BookStack user to the authentication system account. You can ignore this field if using the default email-based authentication.', - 'users_password_warning' => 'Only fill the below if you would like to change the password for this user.', + 'users_external_auth_id_desc' => 'Khi một hệ thống xác thực bên ngoài đang được sử dụng (chẳng hạn như SAML2, OIDC hoặc LDAP) đây là ID liên kết người dùng BookStack này với tài khoản hệ thống xác thực. Bạn có thể bỏ qua trường này nếu sử dụng xác thực dựa trên email mặc định.', + 'users_password_warning' => 'Chỉ điền vào phần bên dưới nếu bạn muốn thay đổi mật khẩu cho người dùng này.', 'users_system_public' => 'Người dùng này đại diện cho bất kỳ khách nào thăm trang của bạn. Nó được tự động gán và không thể dùng để đăng nhập.', 'users_delete' => 'Xóa Người dùng', 'users_delete_named' => 'Xóa người dùng :userName', - 'users_delete_warning' => 'Chức năng này sẽ hoàn toàn xóa người dùng với tên \':userName\' từ hệ thống.', + 'users_delete_warning' => 'Chức năng này sẽ hoàn toàn xóa người dùng với tên \':userName\' khỏi hệ thống.', 'users_delete_confirm' => 'Bạn có chắc muốn xóa người dùng này không?', - 'users_migrate_ownership' => 'Migrate Ownership', - 'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.', + 'users_migrate_ownership' => 'Di chuyển quyền sở hữu', + 'users_migrate_ownership_desc' => 'Chọn một người dùng tại đây nếu bạn muốn một người dùng khác trở thành chủ sở hữu của tất cả các mục hiện thuộc sở hữu của người dùng này.', 'users_none_selected' => 'Chưa chọn người dùng', 'users_edit' => 'Sửa người dùng', 'users_edit_profile' => 'Sửa Hồ sơ', 'users_avatar' => 'Ảnh đại diện', - 'users_avatar_desc' => 'Chọn ảnh đê đại hiện cho người dùng này. Ảnh nên có kích cỡ hình vuông 256px.', + 'users_avatar_desc' => 'Chọn ảnh để đại diện cho người dùng này. Ảnh nên có kích cỡ hình vuông 256px.', 'users_preferred_language' => 'Ngôn ngữ ưu tiên', - 'users_preferred_language_desc' => 'Tùy chọn này sẽ thay đổi ngôn ngư sử dụng cho giao diện người dùng của ứng dụng. Nó sẽ không ảnh hưởng đến bất cứ nội dung nào người dùng tạo ra.', + 'users_preferred_language_desc' => 'Tùy chọn này sẽ thay đổi ngôn ngữ sử dụng cho giao diện người dùng của ứng dụng. Nó sẽ không ảnh hưởng đến bất cứ nội dung nào người dùng tạo ra.', 'users_social_accounts' => 'Tài khoản MXH', - 'users_social_accounts_desc' => 'View the status of the connected social accounts for this user. Social accounts can be used in addition to the primary authentication system for system access.', - 'users_social_accounts_info' => 'Bạn có thể kết nối đến các tài khoản khác để đăng nhập nhanh chóng và dễ dàng. Ngắt kết nối đến một tài khoản ở đây không thu hồi việc ủy quyền truy cập trước đó. Thu hồi truy cập của các tài khoản kết nối MXH từ trang cài đặt hồ sở của bạn.', + 'users_social_accounts_desc' => 'Xem trạng thái của các tài khoản xã hội được kết nối cho người dùng này. Các tài khoản xã hội có thể được sử dụng ngoài hệ thống xác thực chính để truy cập hệ thống.', + 'users_social_accounts_info' => 'Bạn có thể kết nối đến các tài khoản khác để đăng nhập nhanh chóng và dễ dàng. Ngắt kết nối đến một tài khoản ở đây không thu hồi việc ủy quyền truy cập trước đó. Thu hồi truy cập của các tài khoản kết nối MXH từ trang cài đặt hồ sơ của bạn.', 'users_social_connect' => 'Kết nối tài khoản', 'users_social_disconnect' => 'Ngắt kết nối tài khoản', - 'users_social_status_connected' => 'Connected', - 'users_social_status_disconnected' => 'Disconnected', + 'users_social_status_connected' => 'Đã kết nối', + 'users_social_status_disconnected' => 'Đã ngắt kết nối', 'users_social_connected' => 'Tài khoản :socialAccount đã được liên kết với hồ sơ của bạn thành công.', 'users_social_disconnected' => 'Tài khoản :socialAccount đã được ngắt kết nối khỏi hồ sơ của bạn thành công.', 'users_api_tokens' => 'Các Token API', - 'users_api_tokens_desc' => 'Create and manage the access tokens used to authenticate with the BookStack REST API. Permissions for the API are managed via the user that the token belongs to.', - 'users_api_tokens_none' => 'Khong có Token API nào được tạo cho người dùng này', + 'users_api_tokens_desc' => 'Tạo và quản lý các mã thông báo truy cập được sử dụng để xác thực với API REST của BookStack. Quyền cho API được quản lý thông qua người dùng mà mã thông báo thuộc về.', + 'users_api_tokens_none' => 'Không có Token API nào được tạo cho người dùng này', 'users_api_tokens_create' => 'Tạo Token', 'users_api_tokens_expires' => 'Hết hạn', 'users_api_tokens_docs' => 'Tài liệu API', - 'users_mfa' => 'Multi-Factor Authentication', - 'users_mfa_desc' => 'Setup multi-factor authentication as an extra layer of security for your user account.', - 'users_mfa_x_methods' => ':count method configured|:count methods configured', - 'users_mfa_configure' => 'Configure Methods', + 'users_mfa' => 'Xác thực đa yếu tố', + 'users_mfa_desc' => 'Thiết lập xác thực đa yếu tố như một lớp bảo mật bổ sung cho tài khoản người dùng của bạn.', + 'users_mfa_x_methods' => ':count phương thức đã cấu hình|:count phương thức đã cấu hình', + 'users_mfa_configure' => 'Cấu hình phương thức', // API Tokens 'user_api_token_create' => 'Tạo Token API', @@ -244,38 +275,46 @@ 'user_api_token_created' => 'Token được tạo :timeAgo', 'user_api_token_updated' => 'Token được cập nhật :timeAgo', 'user_api_token_delete' => 'Xóa Token', - 'user_api_token_delete_warning' => 'Chức năng này sẽ hoàn toàn xóa token API với tên \':tokenName\' từ hệ thống.', + 'user_api_token_delete_warning' => 'Chức năng này sẽ hoàn toàn xóa token API với tên \':tokenName\' khỏi hệ thống.', 'user_api_token_delete_confirm' => 'Bạn có chắc rằng muốn xóa token API này?', // Webhooks 'webhooks' => 'Webhooks', - 'webhooks_index_desc' => 'Webhooks are a way to send data to external URLs when certain actions and events occur within the system which allows event-based integration with external platforms such as messaging or notification systems.', - 'webhooks_x_trigger_events' => ':count trigger event|:count trigger events', + 'webhooks_index_desc' => 'Webhooks là một cách để gửi dữ liệu đến các URL bên ngoài khi một số hành động và sự kiện nhất định xảy ra trong hệ thống, cho phép tích hợp dựa trên sự kiện với các nền tảng bên ngoài như hệ thống nhắn tin hoặc thông báo.', + 'webhooks_x_trigger_events' => ':count sự kiện kích hoạt|:count sự kiện kích hoạt', 'webhooks_create' => 'Tạo Webhook mới', 'webhooks_none_created' => 'Chưa có webhooks nào được tạo.', 'webhooks_edit' => 'Chỉnh sửa Webhook', 'webhooks_save' => 'Lưu Webhook', 'webhooks_details' => 'Chi tiết Webhook', - 'webhooks_details_desc' => 'Provide a user friendly name and a POST endpoint as a location for the webhook data to be sent to.', - 'webhooks_events' => 'Webhook Events', - 'webhooks_events_desc' => 'Select all the events that should trigger this webhook to be called.', - 'webhooks_events_warning' => 'Keep in mind that these events will be triggered for all selected events, even if custom permissions are applied. Ensure that use of this webhook won\'t expose confidential content.', - 'webhooks_events_all' => 'All system events', + 'webhooks_details_desc' => 'Cung cấp một tên thân thiện với người dùng và một điểm cuối POST làm vị trí để dữ liệu webhook được gửi đến.', + 'webhooks_events' => 'Sự kiện Webhook', + 'webhooks_events_desc' => 'Chọn tất cả các sự kiện sẽ kích hoạt webhook này được gọi.', + 'webhooks_events_warning' => 'Hãy nhớ rằng các sự kiện này sẽ được kích hoạt cho tất cả các sự kiện đã chọn, ngay cả khi các quyền tùy chỉnh được áp dụng. Đảm bảo rằng việc sử dụng webhook này sẽ không làm lộ nội dung bí mật.', + 'webhooks_events_all' => 'Tất cả các sự kiện hệ thống', 'webhooks_name' => 'Tên Webhook', - 'webhooks_timeout' => 'Webhook Request Timeout (Seconds)', - 'webhooks_endpoint' => 'Webhook Endpoint', - 'webhooks_active' => 'Webhook Active', - 'webhook_events_table_header' => 'Events', - 'webhooks_delete' => 'Delete Webhook', - 'webhooks_delete_warning' => 'This will fully delete this webhook, with the name \':webhookName\', from the system.', - 'webhooks_delete_confirm' => 'Are you sure you want to delete this webhook?', - 'webhooks_format_example' => 'Webhook Format Example', - 'webhooks_format_example_desc' => 'Webhook data is sent as a POST request to the configured endpoint as JSON following the format below. The "related_item" and "url" properties are optional and will depend on the type of event triggered.', + 'webhooks_timeout' => 'Thời gian chờ yêu cầu Webhook (Giây)', + 'webhooks_endpoint' => 'Điểm cuối Webhook', + 'webhooks_active' => 'Webhook hoạt động', + 'webhook_events_table_header' => 'Sự kiện', + 'webhooks_delete' => 'Xóa Webhook', + 'webhooks_delete_warning' => 'Điều này sẽ xóa hoàn toàn webhook này, với tên \':webhookName\', khỏi hệ thống.', + 'webhooks_delete_confirm' => 'Bạn có chắc chắn muốn xóa webhook này không?', + 'webhooks_format_example' => 'Ví dụ định dạng Webhook', + 'webhooks_format_example_desc' => 'Dữ liệu webhook được gửi dưới dạng yêu cầu POST đến điểm cuối đã cấu hình dưới dạng JSON theo định dạng bên dưới. Các thuộc tính "related_item" và "url" là tùy chọn và sẽ phụ thuộc vào loại sự kiện được kích hoạt.', 'webhooks_status' => 'Trạng thái Webhook', 'webhooks_last_called' => 'Lần cuối được gọi:', 'webhooks_last_errored' => 'Lần cuối xảy ra lỗi:', 'webhooks_last_error_message' => 'Nội dung lỗi gần nhất:', + // Licensing + 'licenses' => 'Giấy phép', + 'licenses_desc' => 'Trang này trình bày chi tiết thông tin giấy phép cho BookStack ngoài các dự án & thư viện được sử dụng trong BookStack. Nhiều dự án được liệt kê có thể chỉ được sử dụng trong ngữ cảnh phát triển.', + 'licenses_bookstack' => 'Giấy phép BookStack', + 'licenses_php' => 'Giấy phép thư viện PHP', + 'licenses_js' => 'Giấy phép thư viện JavaScript', + 'licenses_other' => 'Các giấy phép khác', + 'license_details' => 'Chi tiết giấy phép', //! If editing translations files directly please ignore this in all //! languages apart from en. Content will be auto-copied from en. @@ -287,6 +326,7 @@ 'bs' => 'Bosanski', 'ca' => 'Català', 'cs' => 'Česky', + 'cy' => 'Cymraeg', 'da' => 'Đan Mạch', 'de' => 'Deutsch (Sie)', 'de_informal' => 'Deutsch (Du)', @@ -308,6 +348,7 @@ 'lt' => 'Lietuvių Kalba', 'lv' => 'Latviešu Valoda', 'nb' => 'Norsk (Bokmål)', + 'ne' => 'नेपाली', 'nn' => 'Nynorsk', 'nl' => 'Nederlands', 'pl' => 'Polski', diff --git a/lang/vi/validation.php b/lang/vi/validation.php index e7bcfd3e2a9..bc6fc0e39fd 100644 --- a/lang/vi/validation.php +++ b/lang/vi/validation.php @@ -105,6 +105,11 @@ 'url' => 'Định dạng của :attribute không hợp lệ.', 'uploaded' => 'Tệp tin đã không được tải lên. Máy chủ không chấp nhận các tệp tin với dung lượng lớn như tệp tin trên.', + 'zip_file' => ':attribute cần tham chiếu đến một tệp trong ZIP.', + 'zip_file_mime' => ':attribute cần tham chiếu đến một tệp có kiểu: :validTypes, tìm thấy :foundType.', + 'zip_model_expected' => 'Đối tượng dữ liệu được mong đợi nhưng tìm thấy ":type".', + 'zip_unique' => ':attribute phải là duy nhất cho kiểu đối tượng trong ZIP.', + // Custom validation lines 'custom' => [ 'password-confirm' => [ diff --git a/lang/zh_CN/activities.php b/lang/zh_CN/activities.php index 1d7272eec79..08f831f6102 100644 --- a/lang/zh_CN/activities.php +++ b/lang/zh_CN/activities.php @@ -84,6 +84,14 @@ 'webhook_delete' => 'Webhook 已删除', 'webhook_delete_notification' => 'Webhook 删除成功', + // Imports + 'import_create' => '创建导入', + 'import_create_notification' => '导入上传成功', + 'import_run' => '更新导入', + 'import_run_notification' => '内容成功导入', + 'import_delete' => '删除导入', + 'import_delete_notification' => '导入删除成功', + // Users 'user_create' => '用户已创建', 'user_create_notification' => '用户创建成功', @@ -93,9 +101,9 @@ 'user_delete_notification' => '成功移除用户', // API Tokens - 'api_token_create' => 'API 令牌已创建', + 'api_token_create' => '已创建 API 令牌', 'api_token_create_notification' => '成功创建 API 令牌', - 'api_token_update' => 'API 令牌已更新', + 'api_token_update' => '已更新 API 令牌', 'api_token_update_notification' => 'API 令牌更新成功', 'api_token_delete' => '已删除 API 令牌', 'api_token_delete_notification' => 'API 令牌删除成功', @@ -119,6 +127,14 @@ 'comment_update' => '评论已更新', 'comment_delete' => '评论已删除', + // Sort Rules + 'sort_rule_create' => '创建排序规则', + 'sort_rule_create_notification' => '排序规则创建成功', + 'sort_rule_update' => '更新排序规则', + 'sort_rule_update_notification' => '排序规则更新成功', + 'sort_rule_delete' => '删除排序规则', + 'sort_rule_delete_notification' => '排序规则删除成功', + // Other 'permissions_update' => '权限已更新', ]; diff --git a/lang/zh_CN/auth.php b/lang/zh_CN/auth.php index d6dcd621933..8c4ba63f9e2 100644 --- a/lang/zh_CN/auth.php +++ b/lang/zh_CN/auth.php @@ -36,7 +36,7 @@ 'register_confirm' => '请点击查收您的Email,并点击确认。', 'registrations_disabled' => '注册目前被禁用', 'registration_email_domain_invalid' => '该Email域名无权访问此应用程序', - 'register_success' => '感谢您注册:appName,您现在已经登录。', + 'register_success' => '感谢您注册!您现在已注册并登录。', // Login auto-initiation 'auto_init_starting' => '尝试登录中', @@ -91,7 +91,7 @@ 'mfa_option_totp_title' => '移动设备 App', 'mfa_option_totp_desc' => '要使用多重身份认证功能,您需要一个支持 TOTP(基于时间的一次性密码算法) 的移动设备 App,如谷歌身份验证器(Google Authenticator)、Authy 或微软身份验证器(Microsoft Authenticator)。', 'mfa_option_backup_codes_title' => '备用认证码', - 'mfa_option_backup_codes_desc' => '请安全地保存这些一次性使用的备用认证码,您可以输入这些认证码来验证您的身份。', + 'mfa_option_backup_codes_desc' => '生成一组一次性使用的备份码,您将在登录时输入以验证您的身份。 请确保将它们存储在一个安全可靠的地方。', 'mfa_gen_confirm_and_enable' => '确认并启用', 'mfa_gen_backup_codes_title' => '备用认证码设置', 'mfa_gen_backup_codes_desc' => '将下面的认证码存放在一个安全的地方。访问系统时,您可以使用其中的一个验证码进行二次认证。', diff --git a/lang/zh_CN/common.php b/lang/zh_CN/common.php index 833d3476254..dde4bdf7db0 100644 --- a/lang/zh_CN/common.php +++ b/lang/zh_CN/common.php @@ -20,7 +20,7 @@ 'description' => '概要', 'role' => '角色', 'cover_image' => '封面图片', - 'cover_image_description' => '此图像大小应约为 440x250 像素。', + 'cover_image_description' => '此图像的大小应约为 440 x 250 像素,但会根据需要灵活缩放和裁剪以适应不同场景下的用户界面,因此实际显示尺寸会有所不同。', // Actions 'actions' => '操作', @@ -30,6 +30,8 @@ 'create' => '创建', 'update' => '更新', 'edit' => '编辑', + 'archive' => '存档', + 'unarchive' => '取消存档', 'sort' => '排序', 'move' => '移动', 'copy' => '复制', @@ -107,4 +109,7 @@ // Not directly used but available for convenience to users. 'privacy_policy' => '隐私政策', 'terms_of_service' => '服务条款', + + // OpenSearch + 'opensearch_description' => '搜索 :appName', ]; diff --git a/lang/zh_CN/components.php b/lang/zh_CN/components.php index f04a254ec09..c2ca50d3691 100644 --- a/lang/zh_CN/components.php +++ b/lang/zh_CN/components.php @@ -17,7 +17,7 @@ 'image_page_title' => '查看上传到本页面的图片', 'image_search_hint' => '按图片名称搜索', 'image_uploaded' => '上传于 :uploadedDate', - 'image_uploaded_by' => '由 :username 上传', + 'image_uploaded_by' => '由 :userName 上传', 'image_uploaded_to' => '上传到 :pageLink', 'image_updated' => ':updateDate 更新', 'image_load_more' => '显示更多', diff --git a/lang/zh_CN/editor.php b/lang/zh_CN/editor.php index db65cb216b0..6dbca56e313 100644 --- a/lang/zh_CN/editor.php +++ b/lang/zh_CN/editor.php @@ -13,6 +13,7 @@ 'cancel' => '取消', 'save' => '保存', 'close' => '关闭', + 'apply' => '应用', 'undo' => '撤销', 'redo' => '重做', 'left' => '左对齐', @@ -47,6 +48,7 @@ 'superscript' => '上标', 'subscript' => '下标', 'text_color' => '文本颜色', + 'highlight_color' => 'Highlight color', 'custom_color' => '自定义颜色', 'remove_color' => '移除颜色', 'background_color' => '背景色', @@ -81,6 +83,9 @@ 'table_properties' => '表格属性', 'table_properties_title' => '表格属性', 'delete_table' => '删除表格', + 'table_clear_formatting' => '清除表格格式', + 'resize_to_contents' => '根据内容调整大小', + 'row_header' => '行头', 'insert_row_before' => '在上方插入行', 'insert_row_after' => '在下方插入行', 'delete_row' => '删除行', @@ -144,6 +149,7 @@ 'url' => '网址', 'text_to_display' => '要显示的文本', 'title' => '标题', + 'browse_links' => '浏览链接', 'open_link' => '打开链接', 'open_link_in' => '打开链接于……', 'open_link_current' => '覆盖当前窗口', @@ -160,6 +166,8 @@ 'about' => '关于编辑器', 'about_title' => '关于所见即所得(WYSIWYG)编辑器', 'editor_license' => '编辑器许可证与版权信息', + 'editor_lexical_license' => '该编辑器是 :lexicalLink 的一个分支构建的,并根据 MIT 许可证进行分发.', + 'editor_lexical_license_link' => '许可证详细信息请点击此处', 'editor_tiny_license' => '此编辑器是用 :tinyLink 构建的,基于 MIT 许可证。', 'editor_tiny_license_link' => 'TinyMCE 的版权和许可证详细信息可以在这里找到。', 'save_continue' => '保存页面并继续', diff --git a/lang/zh_CN/entities.php b/lang/zh_CN/entities.php index e12f6051afb..89c23c281c5 100644 --- a/lang/zh_CN/entities.php +++ b/lang/zh_CN/entities.php @@ -39,6 +39,34 @@ 'export_pdf' => 'PDF文件', 'export_text' => '纯文本文件', 'export_md' => 'Markdown 文件', + 'export_zip' => '便携版本 ZIP', + 'default_template' => '默认页面模板', + 'default_template_explain' => '指定一个页面模板,该模板将作为此项目中所有页面的默认内容。请注意,仅当页面创建者具有对所选页面模板的查看访问权限时,此功能才会生效。', + 'default_template_select' => '选择模板页面', + 'import' => '导入', + 'import_validate' => '验证导入', + 'import_desc' => '使用便携式 zip 导出从相同或不同的实例导入书籍、章节和页面。选择一个 ZIP 文件以继续。文件上传并验证后,您就可以在下一个视图中配置和确认导入。', + 'import_zip_select' => '选择要上床的 ZIP 文件', + 'import_zip_validation_errors' => '验证提供的 ZIP 文件时检测到错误:', + 'import_pending' => '等待导入', + 'import_pending_none' => '尚未开始导入。', + 'import_continue' => '继续导入', + 'import_continue_desc' => '查看从上传的 ZIP 文件中导入的内容。准备就绪后,运行导入将其内容添加到本系统中。成功导入后,上传的 ZIP 导入文件将自动删除。', + 'import_details' => '导入详情', + 'import_run' => '执行导入', + 'import_size' => ':size 导入 ZIP 文件大小', + 'import_uploaded_at' => '上传时间 :relativeTime', + 'import_uploaded_by' => '上传者', + 'import_location' => '导入位置', + 'import_location_desc' => '为导入的内容选择目标位置。您需要相关权限才能在所选位置内进行创建', + 'import_delete_confirm' => '您确定要删除此导入吗?', + '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', // Permissions and restrictions 'permissions' => '权限', @@ -132,9 +160,6 @@ 'books_edit_named' => '编辑图书「:bookName」', 'books_form_book_name' => '书名', 'books_save' => '保存图书', - 'books_default_template' => '默认页面模板', - 'books_default_template_explain' => '指定一个页面模板,该模板将用作本书中所有新页面的默认内容。请注意,仅当页面创建者具有对所选页面模板的查看访问权限时,此功能才会生效。', - 'books_default_template_select' => '选择模板页面', 'books_permissions' => '图书权限', 'books_permissions_updated' => '图书权限已更新', 'books_empty_contents' => '本书目前没有页面或章节。', @@ -145,7 +170,9 @@ 'books_search_this' => '搜索这本书', 'books_navigation' => '图书导航', 'books_sort' => '排序图书内容', - 'books_sort_desc' => '移动并重新排序书中的章节和页面。你也可以添加其他图书,这样就可以方便地在图书之间移动章节和页面。', + 'books_sort_desc' => '在书籍内部移动章节与页面以重组内容;支持添加其他书籍,实现跨书籍便捷移动章节与页面;还可设置自动排序规则,在内容发生变更时自动对本书内容进行排序。', + 'books_sort_auto_sort' => '自动排序选项', + 'books_sort_auto_sort_active' => '自动排序已激活:::sortName', 'books_sort_named' => '排序图书「:bookName」', 'books_sort_name' => '按名称排序', 'books_sort_created' => '创建时间排序', @@ -207,7 +234,7 @@ 'pages_delete_draft' => '删除草稿页面', 'pages_delete_success' => '页面已删除', 'pages_delete_draft_success' => '草稿页面已删除', - 'pages_delete_warning_template' => '此页面是当前的书籍默认页面模板。删除此页面后,将不再为这些书籍分配默认页面模板。', + 'pages_delete_warning_template' => '此页面是当前书籍或章节的默认页面模板。删除此页面后,这些书籍或章节的默认页面模板将被取消。', 'pages_delete_confirm' => '您确定要删除此页面吗?', 'pages_delete_draft_confirm' => '您确定要删除此草稿页面吗?', 'pages_editing_named' => '正在编辑页面“:pageName”', @@ -224,6 +251,8 @@ 'pages_edit_switch_to_markdown_clean' => '(整理内容)', 'pages_edit_switch_to_markdown_stable' => '(保留内容)', 'pages_edit_switch_to_wysiwyg' => '切换到所见即所得编辑器', + 'pages_edit_switch_to_new_wysiwyg' => '切换到新的所见即所得', + 'pages_edit_switch_to_new_wysiwyg_desc' => '(正在Beta测试中)', 'pages_edit_set_changelog' => '更新说明', 'pages_edit_enter_changelog_desc' => '输入对您所做更改的简要说明', 'pages_edit_enter_changelog' => '输入更新说明', @@ -243,6 +272,7 @@ 'pages_md_insert_drawing' => '插入图表', 'pages_md_show_preview' => '显示预览', 'pages_md_sync_scroll' => '同步预览滚动', + 'pages_md_plain_editor' => '纯文本编辑器', 'pages_drawing_unsaved' => '找到未保存的绘图', 'pages_drawing_unsaved_confirm' => '从之前保存失败的绘图中发现了可恢复的数据。您想恢复并继续编辑这个未保存的绘图吗?', 'pages_not_in_chapter' => '本页面不在某章节中', @@ -367,8 +397,11 @@ 'comment' => '评论', 'comments' => '评论', 'comment_add' => '添加评论', + 'comment_none' => '没有要显示的评论', 'comment_placeholder' => '在这里评论', - 'comment_count' => '{0} 无评论|{1} 1 条评论|[2,*] :count 条评论', + 'comment_thread_count' => ':count Comment Thread|:count Comment Threads', + 'comment_archived_count' => ':count 条评论已存档', + 'comment_archived_threads' => '已存档的贴子', 'comment_save' => '保存评论', 'comment_new' => '新评论', 'comment_created' => '评论于 :createDiff', @@ -377,8 +410,14 @@ 'comment_deleted_success' => '评论已删除', 'comment_created_success' => '评论已添加', 'comment_updated_success' => '评论已更新', + 'comment_archive_success' => '评论已存档', + 'comment_unarchive_success' => '评论已取消存档', + 'comment_view' => '查看评论', + 'comment_jump_to_thread' => '跳转到贴子', 'comment_delete_confirm' => '您确定要删除这条评论?', 'comment_in_reply_to' => '回复 :commentId', + 'comment_reference' => '参考', + 'comment_reference_outdated' => '(已过时)', 'comment_editor_explain' => '这里是此页面上留下的评论。查看已保存的页面时可以添加和管理评论。', // Revision diff --git a/lang/zh_CN/errors.php b/lang/zh_CN/errors.php index 3dbf3c4904c..37ef86af551 100644 --- a/lang/zh_CN/errors.php +++ b/lang/zh_CN/errors.php @@ -10,6 +10,7 @@ // Auth 'error_user_exists_different_creds' => 'Email为 :email 的用户已经存在,但具有不同的凭据。', + 'auth_pre_register_theme_prevention' => '无法根据所提供的信息注册帐户', 'email_already_confirmed' => 'Email已被确认,请尝试登录。', 'email_confirmation_invalid' => '此确认令牌无效或已被使用,请重新注册。', 'email_confirmation_expired' => '确认令牌已过期,已发送新的确认电子邮件。', @@ -23,7 +24,6 @@ 'saml_invalid_response_id' => '来自外部身份验证系统的请求没有被本应用程序认证,在登录后返回上一页可能会导致此问题。', 'saml_fail_authed' => '使用 :system 登录失败,登录系统未返回成功登录授权信息。', 'oidc_already_logged_in' => '您已经登陆了', - 'oidc_user_not_registered' => '用户 :name 尚未注册,自助注册功能已被禁用', 'oidc_no_email_address' => '无法找到有效的 Email 地址,此用户数据由外部身份验证系统托管', 'oidc_fail_authed' => '使用 :system 登录失败,登录系统未返回成功登录授权信息', 'social_no_action_defined' => '没有定义行为', @@ -37,6 +37,7 @@ 'social_driver_not_found' => '未找到社交驱动程序', 'social_driver_not_configured' => '您的:socialAccount社交设置不正确。', 'invite_token_expired' => '此邀请链接已过期。 您可以尝试重置您的账户密码。', + 'login_user_not_found' => '找不到执行此操作的用户。', // System 'path_not_writable' => '无法上传到文件路径“:filePath”,请确保它可写入服务器。', @@ -77,6 +78,7 @@ // Users 'users_cannot_delete_only_admin' => '您不能删除唯一的管理员账户', 'users_cannot_delete_guest' => '您不能删除访客用户', + 'users_could_not_send_invite' => '由于邀请电子邮件发送失败,无法创建用户', // Roles 'role_cannot_be_edited' => '无法编辑该角色', @@ -103,6 +105,18 @@ 'app_down' => ':appName现在正在关闭', 'back_soon' => '请耐心等待网站的恢复。', + // Import + 'import_zip_cant_read' => '无法读取 ZIP 文件。', + 'import_zip_cant_decode_data' => '无法找到并解码 ZIP data.json 内容。', + 'import_zip_no_data' => 'ZIP 文件数据没有预期的书籍、章节或页面内容。', + 'import_validation_failed' => '导入 ZIP 验证失败,出现错误:', + 'import_zip_failed_notification' => 'ZIP 文件导入失败。', + 'import_perms_books' => '您缺少创建书籍所需的权限。', + 'import_perms_chapters' => '您缺少创建章节所需的权限。', + 'import_perms_pages' => '您缺少创建页面所需的权限。', + 'import_perms_images' => '您缺少创建图片所需的权限。', + 'import_perms_attachments' => '您缺少创建附件所需的权限。', + // API errors 'api_no_authorization_found' => '未在请求中找到授权令牌', 'api_bad_authorization_format' => '已在请求中找到授权令牌,但格式貌似不正确', diff --git a/lang/zh_CN/preferences.php b/lang/zh_CN/preferences.php index 0b5044137d6..f1ef3957d1e 100644 --- a/lang/zh_CN/preferences.php +++ b/lang/zh_CN/preferences.php @@ -27,7 +27,7 @@ 'notifications_save' => '保存偏好设置', 'notifications_update_success' => '通知偏好设置已更新!', 'notifications_watched' => '已关注和忽略的项目', - 'notifications_watched_desc' => ' 下面是已应用自定义关注选项的项目。要更新您的关注设置,请查看该项目,然后在该项目的侧边栏中找到关注选项。', + 'notifications_watched_desc' => '下面是已应用自定义关注选项的项目。要更新您的关注设置,请查看该项目,然后在该项目的侧边栏中找到关注选项。', 'auth' => '访问与安全', 'auth_change_password' => '更改密码', diff --git a/lang/zh_CN/settings.php b/lang/zh_CN/settings.php index 4bf868af6f8..2114c36480c 100644 --- a/lang/zh_CN/settings.php +++ b/lang/zh_CN/settings.php @@ -45,7 +45,7 @@ 'app_footer_links_add' => '添加页脚链接', 'app_disable_comments' => '禁用评论', 'app_disable_comments_toggle' => '禁用评论', - 'app_disable_comments_desc' => '在站点的所有页面上禁用评论,现有评论也不会显示出来。', + 'app_disable_comments_desc' => '在站点的所有页面上禁用评论,
已有评论也不会显示出来。', // Color settings 'color_scheme' => '应用程序配色方案', @@ -74,6 +74,36 @@ 'reg_confirm_restrict_domain_desc' => '输入您想要限制注册的电子邮件域名列表(即只允许使用这些电子邮件域名注册),多个域名用英文逗号隔开。在允许用户与应用程序交互之前,系统将向用户发送一封电子邮件以确认其电子邮件地址。
请注意,用户在注册成功后仍然可以更改他们的电子邮件地址。', 'reg_confirm_restrict_domain_placeholder' => '尚未设置限制', + // Sorting Settings + 'sorting' => '排序', + 'sorting_book_default' => '默认书卷排序', + 'sorting_book_default_desc' => '选择要应用于新书的默认排序规则。这不会影响现有书,并且可以每本书覆盖。', + 'sorting_rules' => '排序规则', + 'sorting_rules_desc' => '这些是预定义的排序操作,可应用于系统中的内容。', + 'sort_rule_assigned_to_x_books' => '分配给 :count Book|分配给 :count Books', + 'sort_rule_create' => '创建排序规则', + 'sort_rule_edit' => '编辑排序规则', + 'sort_rule_delete' => '删除排序规则', + 'sort_rule_delete_desc' => '从系统中删除这种排序规则。使用这种类型的书本将恢复到手动排序。', + 'sort_rule_delete_warn_books' => '此排序规则目前用于:count book(s)。您确定要删除吗?', + 'sort_rule_delete_warn_default' => '此排序规则目前被用作书籍的默认值。您确定要删除吗?', + 'sort_rule_details' => '排序规则详细信息', + 'sort_rule_details_desc' => '为此排序规则设置一个名称,当用户选择排序时,该名称将出现在列表中。', + 'sort_rule_operations' => '排序选项', + 'sort_rule_operations_desc' => '配置通过将它们从可用操作列表中移动来执行的排序操作。 一旦使用,操作将按顺序从上到下顺序进行。 这里所做的任何更改都将在保存时适用于所有分配的书本。', + 'sort_rule_available_operations' => '可用操作', + 'sort_rule_available_operations_empty' => '没有剩余操作', + 'sort_rule_configured_operations' => '配置选项', + 'sort_rule_configured_operations_empty' => '从“可用操作”列表中拖动/添加操作', + 'sort_rule_op_asc' => '(Asc)', + 'sort_rule_op_desc' => '(Desc)', + 'sort_rule_op_name' => '名称-按字母顺序排序', + 'sort_rule_op_name_numeric' => '名称-按数字顺序排序', + 'sort_rule_op_created_date' => '创建时间', + 'sort_rule_op_updated_date' => '更新时间', + 'sort_rule_op_chapters_first' => '章节正序', + 'sort_rule_op_chapters_last' => '章节倒序', + // Maintenance settings 'maint' => '维护', 'maint_image_cleanup' => '清理图像', @@ -162,6 +192,7 @@ 'role_access_api' => '访问系统 API', 'role_manage_settings' => '管理 App 设置', 'role_export_content' => '导出内容', + 'role_import_content' => '导入内容', 'role_editor_change' => '更改页面编辑器', 'role_notifications' => '管理和接收通知', 'role_asset' => '资源许可', @@ -276,6 +307,14 @@ 'webhooks_last_errored' => '最后一个错误:', 'webhooks_last_error_message' => '最后一个错误消息:', + // Licensing + 'licenses' => '许可证', + 'licenses_desc' => '除了 BookStack 中使用的项目和库之外,此页面还详细介绍了 BookStack 的许可证信息。列出的许多项目只能在开发环境中使用。', + 'licenses_bookstack' => 'BookStack 许可证', + 'licenses_php' => 'PHP 库许可证', + 'licenses_js' => 'JavaScript 库许可证', + 'licenses_other' => '其他许可证', + 'license_details' => '许可证细节', //! If editing translations files directly please ignore this in all //! languages apart from en. Content will be auto-copied from en. @@ -287,6 +326,7 @@ 'bs' => 'Bosanski', 'ca' => '加泰罗尼亚语', 'cs' => 'Česky', + 'cy' => 'Cymraeg', 'da' => '丹麦', 'de' => 'Deutsch (Sie)', 'de_informal' => 'Deutsch (Du)', @@ -308,6 +348,7 @@ 'lt' => 'Lietuvių Kalba', 'lv' => 'Latviešu Valoda', 'nb' => '挪威语 (Bokmål)', + 'ne' => 'नेपाली', 'nn' => 'Nynorsk', 'nl' => 'Nederlands', 'pl' => 'Polski', diff --git a/lang/zh_CN/validation.php b/lang/zh_CN/validation.php index 9f24f3339da..1a576e6bca4 100644 --- a/lang/zh_CN/validation.php +++ b/lang/zh_CN/validation.php @@ -18,10 +18,10 @@ 'backup_codes' => '您输入的认证码无效或已被使用。', 'before' => ':attribute 必须是在 :date 前的日期。', 'between' => [ - 'numeric' => ':attribute 必须在:min到:max之间。', - 'file' => ':attribute 必须为:min到:max KB。', - 'string' => ':attribute 必须在:min到:max个字符之间。', - 'array' => ':attribute 必须在:min到:max项之间.', + 'numeric' => ':attribute 必须在 :min 到 :max 之间。', + 'file' => ':attribute 必须为 :min 到 :max 之间。', + 'string' => ':attribute 必须在 :min 到 :max 个字符之间。', + 'array' => ':attribute 必须在 :min 到 :max 项之间.', ], 'boolean' => ':attribute 字段必须为真或假。', 'confirmed' => ':attribute 确认不符。', @@ -31,7 +31,7 @@ 'digits' => ':attribute 必须为:digits位数。', 'digits_between' => ':attribute 必须为:min到:max位数。', 'email' => ':attribute 必须是有效的电子邮件地址。', - 'ends_with' => ' :attribute 必须以 :values 后缀结尾', + 'ends_with' => ':attribute 必须以: :values 后缀结尾。', 'file' => ':attribute 必须是一个有效的文件。', 'filled' => ':attribute 字段是必需的。', 'gt' => [ @@ -105,6 +105,11 @@ 'url' => ':attribute 格式无效。', 'uploaded' => '无法上传文件。 服务器可能不接受此大小的文件。', + 'zip_file' => ':attribute 需要引用 ZIP 内的文件。', + 'zip_file_mime' => ':attribute 需要引用类型为 :validTypes 的文件,找到 :foundType 。', + 'zip_model_expected' => '预期的数据对象,但找到了 ":type" 。', + 'zip_unique' => '对于 ZIP 中的对象类型来说,:attribute 必须是唯一的。', + // Custom validation lines 'custom' => [ 'password-confirm' => [ diff --git a/lang/zh_TW/activities.php b/lang/zh_TW/activities.php index afbaa12fca2..65ba0a56597 100644 --- a/lang/zh_TW/activities.php +++ b/lang/zh_TW/activities.php @@ -47,34 +47,34 @@ 'bookshelf_update' => '更新書棧', 'bookshelf_update_notification' => '書棧已更新', 'bookshelf_delete' => '刪除書棧', - 'bookshelf_delete_notification' => '書棧已刪除', + 'bookshelf_delete_notification' => '書架已刪除', // Revisions - 'revision_restore' => 'restored revision', - 'revision_delete' => 'deleted revision', - 'revision_delete_notification' => 'Revision successfully deleted', + 'revision_restore' => '還原的版本', + 'revision_delete' => '刪除的版本', + 'revision_delete_notification' => '修訂已成功刪除', // Favourites 'favourite_add_notification' => '":name" 已加入到你的最愛', 'favourite_remove_notification' => '":name" 已從你的最愛移除', // Watching - 'watch_update_level_notification' => 'Watch preferences successfully updated', + 'watch_update_level_notification' => '追蹤偏好設定已成功更新', // Auth - 'auth_login' => 'logged in', - 'auth_register' => 'registered as new user', - 'auth_password_reset_request' => 'requested user password reset', - 'auth_password_reset_update' => 'reset user password', - 'mfa_setup_method' => 'configured MFA method', + 'auth_login' => '已登入', + 'auth_register' => '註冊為新用戶', + 'auth_password_reset_request' => '請求重置用戶密碼', + 'auth_password_reset_update' => '重置使用者密碼', + 'mfa_setup_method' => '設定MFA方式', 'mfa_setup_method_notification' => '多重身份驗證已設定成功', - 'mfa_remove_method' => 'removed MFA method', + 'mfa_remove_method' => '移除MFA方式', 'mfa_remove_method_notification' => '多重身份驗證已移除成功', // Settings - 'settings_update' => 'updated settings', + 'settings_update' => '更新設定', 'settings_update_notification' => '設定更新成功', - 'maintenance_action_run' => 'ran maintenance action', + 'maintenance_action_run' => '執行維護動作', // Webhooks 'webhook_create' => '建立 Webhook', @@ -84,40 +84,56 @@ 'webhook_delete' => 'webhook 已刪除', 'webhook_delete_notification' => 'Webhook 已刪除成功', + // Imports + 'import_create' => '已建立匯入', + 'import_create_notification' => '成功上傳匯入', + 'import_run' => '已更新匯入', + 'import_run_notification' => '成功匯入內容', + 'import_delete' => '已刪除匯入', + 'import_delete_notification' => '匯入刪除成功', + // Users - 'user_create' => 'created user', - 'user_create_notification' => 'User successfully created', - 'user_update' => 'updated user', + 'user_create' => '建立使用者', + 'user_create_notification' => '使用者已成功建立。', + 'user_update' => '更新使用者', 'user_update_notification' => '使用者已成功更新。', - 'user_delete' => 'deleted user', + 'user_delete' => '已刪除使用者', 'user_delete_notification' => '使用者移除成功', // API Tokens - 'api_token_create' => 'created api token', - 'api_token_create_notification' => 'API token successfully created', - 'api_token_update' => 'updated api token', - 'api_token_update_notification' => 'API token successfully updated', - 'api_token_delete' => 'deleted api token', - 'api_token_delete_notification' => 'API token successfully deleted', + 'api_token_create' => '建立 API 權杖', + 'api_token_create_notification' => '成功建立 API 權杖', + 'api_token_update' => '已更新 API 權杖', + 'api_token_update_notification' => '成功更新 API 權杖', + 'api_token_delete' => '已刪除 API 權杖', + 'api_token_delete_notification' => 'API 權杖已成功刪除', // Roles - 'role_create' => 'created role', + 'role_create' => '創建角色', 'role_create_notification' => '建立角色成功', - 'role_update' => 'updated role', + 'role_update' => '已更新角色', 'role_update_notification' => '更新角色成功', - 'role_delete' => 'deleted role', + 'role_delete' => '已刪除角色', 'role_delete_notification' => '刪除角色成功', // Recycle Bin - 'recycle_bin_empty' => 'emptied recycle bin', - 'recycle_bin_restore' => 'restored from recycle bin', - 'recycle_bin_destroy' => 'removed from recycle bin', + 'recycle_bin_empty' => '清理資源回收筒', + 'recycle_bin_restore' => '從資源回收筒復原', + 'recycle_bin_destroy' => '從資源回收筒刪除', // Comments 'commented_on' => '評論', - 'comment_create' => 'added comment', - 'comment_update' => 'updated comment', - 'comment_delete' => 'deleted comment', + 'comment_create' => '新增評論', + 'comment_update' => '更新評論', + 'comment_delete' => '已刪除之評論', + + // Sort Rules + 'sort_rule_create' => '已建立的排序規則', + 'sort_rule_create_notification' => '排序規則已成功建立', + 'sort_rule_update' => '已更新排序規則', + 'sort_rule_update_notification' => '排序規則已成功更新', + 'sort_rule_delete' => '已刪除排序規則', + 'sort_rule_delete_notification' => '排序規則已成功刪除', // Other 'permissions_update' => '更新權限', diff --git a/lang/zh_TW/auth.php b/lang/zh_TW/auth.php index 7a3d8031d47..1168b48b270 100644 --- a/lang/zh_TW/auth.php +++ b/lang/zh_TW/auth.php @@ -7,7 +7,7 @@ return [ 'failed' => '使用者名稱或密碼錯誤。', - 'throttle' => '您的登入次數過多,請在:seconds秒後重試。', + 'throttle' => '您的登入次數過多,請在 :seconds 秒後重試。', // Login & Register 'sign_up' => '註冊', @@ -91,7 +91,7 @@ 'mfa_option_totp_title' => '手機App', 'mfa_option_totp_desc' => '您必須在行動裝置上安裝了支援TOTP的身份驗證程式(例如Google Authenticator, Authy 或是 Microsoft Authenticator)才能使用雙重身份驗證。', 'mfa_option_backup_codes_title' => '備用驗證碼', - 'mfa_option_backup_codes_desc' => '妥善保存好您的一次性備用驗證碼,以便日後驗證您的身份。', + 'mfa_option_backup_codes_desc' => '產生一次使用的備份代碼,你可以在登入時用來辨識身份。請確認代碼被存放在安全的地方。', 'mfa_gen_confirm_and_enable' => '確認並啟用', 'mfa_gen_backup_codes_title' => '備援代碼設定', 'mfa_gen_backup_codes_desc' => '將以下代碼列表儲存在安全的地方。存取系統時,您可以使用其中一個代碼作為第二個身份驗證機制。', diff --git a/lang/zh_TW/common.php b/lang/zh_TW/common.php index 53b2a531037..0c64ac8357a 100644 --- a/lang/zh_TW/common.php +++ b/lang/zh_TW/common.php @@ -20,7 +20,7 @@ 'description' => '描述', 'role' => '角色', 'cover_image' => '封面圖片', - 'cover_image_description' => '此圖片大小應約為 440x250px。', + 'cover_image_description' => '雖然圖片會在不同情境下自動調整顯示方式,但應接近 440x250 像素', // Actions 'actions' => '動作', @@ -30,6 +30,8 @@ 'create' => '建立', 'update' => '更新', 'edit' => '編輯', + 'archive' => '歸檔', + 'unarchive' => 'Un-Archive', 'sort' => '排序', 'move' => '移動', 'copy' => '複製', @@ -107,4 +109,7 @@ // Not directly used but available for convenience to users. 'privacy_policy' => '隱私權政策', 'terms_of_service' => '服務條款', + + // OpenSearch + 'opensearch_description' => '搜尋 :appName', ]; diff --git a/lang/zh_TW/components.php b/lang/zh_TW/components.php index 682a8ed4d8c..d66dd383e8d 100644 --- a/lang/zh_TW/components.php +++ b/lang/zh_TW/components.php @@ -10,16 +10,16 @@ 'image_details' => '圖片詳細資訊', 'image_upload' => '上傳圖片', 'image_intro' => '您可以在這裡選取和管理上傳到系統的圖片。', - 'image_intro_upload' => 'Upload a new image by dragging an image file into this window, or by using the "Upload Image" button above.', + 'image_intro_upload' => '透過拖曳圖檔至視窗中,或是使用下方的「上傳圖片」按鍵', 'image_all' => '全部', 'image_all_title' => '檢視所有圖片', 'image_book_title' => '檢視上傳到此書本的圖片', 'image_page_title' => '檢視上傳到此頁面的圖片', 'image_search_hint' => '以圖片名稱搜尋', 'image_uploaded' => '上傳於 :uploadedDate', - 'image_uploaded_by' => 'Uploaded by :userName', - 'image_uploaded_to' => 'Uploaded to :pageLink', - 'image_updated' => 'Updated :updateDate', + 'image_uploaded_by' => '由 :username 上傳', + 'image_uploaded_to' => '上傳到 :pageLink', + 'image_updated' => ':updateDate 更新', 'image_load_more' => '載入更多', 'image_image_name' => '圖片名稱', 'image_delete_used' => '此圖片用於以下頁面。', @@ -34,8 +34,8 @@ 'image_delete_success' => '圖片刪除成功', 'image_replace' => '替換圖片', 'image_replace_success' => '圖片更新成功', - 'image_rebuild_thumbs' => 'Regenerate Size Variations', - 'image_rebuild_thumbs_success' => 'Image size variations successfully rebuilt!', + 'image_rebuild_thumbs' => '重建影像縮圖', + 'image_rebuild_thumbs_success' => '縮圖建立成功', // Code Editor 'code_editor' => '編輯程式碼', diff --git a/lang/zh_TW/editor.php b/lang/zh_TW/editor.php index 4189e39dd0d..93c8d571d2d 100644 --- a/lang/zh_TW/editor.php +++ b/lang/zh_TW/editor.php @@ -13,6 +13,7 @@ 'cancel' => '取消', 'save' => '保存', 'close' => '關閉', + 'apply' => '套用', 'undo' => '復原', 'redo' => '重做', 'left' => '左側', @@ -47,6 +48,7 @@ 'superscript' => '上標', 'subscript' => '下標', 'text_color' => '文本顏色', + 'highlight_color' => 'Highlight color', 'custom_color' => '自訂顏色', 'remove_color' => '移除颜色', 'background_color' => '背景顏色', @@ -81,6 +83,9 @@ 'table_properties' => '表格屬性', 'table_properties_title' => '表格屬性', 'delete_table' => '刪除表格', + 'table_clear_formatting' => '清除格式', + 'resize_to_contents' => '將大小調整到符合內容', + 'row_header' => '列標題', 'insert_row_before' => '插入上方列', 'insert_row_after' => '插入下方列', 'delete_row' => '刪除列', @@ -90,85 +95,88 @@ 'table_cell' => '儲存格', 'table_row' => '行', 'table_column' => '欄', - 'cell_properties' => 'Cell properties', - 'cell_properties_title' => 'Cell Properties', - 'cell_type' => 'Cell type', - 'cell_type_cell' => 'Cell', - 'cell_scope' => 'Scope', - 'cell_type_header' => 'Header cell', - 'merge_cells' => 'Merge cells', - 'split_cell' => 'Split cell', - 'table_row_group' => 'Row Group', - 'table_column_group' => 'Column Group', - 'horizontal_align' => 'Horizontal align', - 'vertical_align' => 'Vertical align', - 'border_width' => 'Border width', - 'border_style' => 'Border style', - 'border_color' => 'Border color', - 'row_properties' => 'Row properties', - 'row_properties_title' => 'Row Properties', - 'cut_row' => 'Cut row', - 'copy_row' => 'Copy row', - 'paste_row_before' => 'Paste row before', - 'paste_row_after' => 'Paste row after', - 'row_type' => 'Row type', + 'cell_properties' => '表格屬性', + 'cell_properties_title' => '表格屬性', + 'cell_type' => '儲存格類型', + 'cell_type_cell' => '儲存格', + 'cell_scope' => '範圍', + 'cell_type_header' => '標題儲存格', + 'merge_cells' => '合併儲存格', + 'split_cell' => '分割儲存格', + 'table_row_group' => '依行分組', + 'table_column_group' => '依列分組', + 'horizontal_align' => '水平對齊', + 'vertical_align' => '垂直對齊', + 'border_width' => '邊框寬度', + 'border_style' => '邊框樣式', + 'border_color' => '邊框顏色', + 'row_properties' => '行屬性', + 'row_properties_title' => '行屬性', + 'cut_row' => '剪切行', + 'copy_row' => '複製行', + 'paste_row_before' => '插入上方行', + 'paste_row_after' => '插入下方行', + 'row_type' => '行類型', 'row_type_header' => '頁眉', 'row_type_body' => '正文', 'row_type_footer' => '页脚', - 'alignment' => 'Alignment', - 'cut_column' => 'Cut column', - 'copy_column' => 'Copy column', - 'paste_column_before' => 'Paste column before', - 'paste_column_after' => 'Paste column after', - 'cell_padding' => 'Cell padding', - 'cell_spacing' => 'Cell spacing', - 'caption' => 'Caption', - 'show_caption' => 'Show caption', - 'constrain' => 'Constrain proportions', - 'cell_border_solid' => 'Solid', - 'cell_border_dotted' => 'Dotted', - 'cell_border_dashed' => 'Dashed', - 'cell_border_double' => 'Double', - 'cell_border_groove' => 'Groove', - 'cell_border_ridge' => 'Ridge', - 'cell_border_inset' => 'Inset', - 'cell_border_outset' => 'Outset', - 'cell_border_none' => 'None', - 'cell_border_hidden' => 'Hidden', + 'alignment' => '對齊', + 'cut_column' => '剪切列', + 'copy_column' => '剪切列', + 'paste_column_before' => '插入前方列', + 'paste_column_after' => '插入後方列', + 'cell_padding' => '單元格填充', + 'cell_spacing' => '單元格間距', + 'caption' => '標題', + 'show_caption' => '顯示標題', + 'constrain' => '維持比例', + 'cell_border_solid' => '實線', + 'cell_border_dotted' => '點線', + 'cell_border_dashed' => '虛線', + 'cell_border_double' => '兩倍', + 'cell_border_groove' => '凹線', + 'cell_border_ridge' => '浮出', + 'cell_border_inset' => '嵌入', + 'cell_border_outset' => '外嵌', + 'cell_border_none' => '無', + 'cell_border_hidden' => '隱藏', // Images, links, details/summary & embed 'source' => '來源', - 'alt_desc' => 'Alternative description', - 'embed' => 'Embed', - 'paste_embed' => 'Paste your embed code below:', + 'alt_desc' => '替代描述', + 'embed' => '內嵌', + 'paste_embed' => '在下面黏貼您的嵌入代碼:', 'url' => '網址', - 'text_to_display' => 'Text to display', - 'title' => 'Title', - 'open_link' => 'Open link', - 'open_link_in' => 'Open link in...', - 'open_link_current' => 'Current window', - 'open_link_new' => 'New window', - 'remove_link' => 'Remove link', - 'insert_collapsible' => 'Insert collapsible block', - 'collapsible_unwrap' => 'Unwrap', + 'text_to_display' => '要顯示的文字', + 'title' => '標題', + 'browse_links' => '瀏覽連結', + 'open_link' => '開啟連結', + 'open_link_in' => '打開連結於……', + 'open_link_current' => '當前視窗', + 'open_link_new' => '新視窗', + 'remove_link' => '移除連結', + 'insert_collapsible' => '插入可折疊塊', + 'collapsible_unwrap' => '展開', 'edit_label' => '編輯標記', - 'toggle_open_closed' => 'Toggle open/closed', - 'collapsible_edit' => 'Edit collapsible block', - 'toggle_label' => 'Toggle label', + 'toggle_open_closed' => '切換打開/關閉', + 'collapsible_edit' => '編輯可折疊塊', + 'toggle_label' => '切換標籤', // About view - 'about' => 'About the editor', - 'about_title' => 'About the WYSIWYG Editor', - 'editor_license' => 'Editor License & Copyright', - 'editor_tiny_license' => 'This editor is built using :tinyLink which is provided under the MIT license.', - 'editor_tiny_license_link' => 'The copyright and license details of TinyMCE can be found here.', - 'save_continue' => 'Save Page & Continue', - 'callouts_cycle' => '(Keep pressing to toggle through types)', - 'link_selector' => 'Link to content', - 'shortcuts' => 'Shortcuts', - 'shortcut' => 'Shortcut', - 'shortcuts_intro' => 'The following shortcuts are available in the editor:', + 'about' => '關於編輯器', + 'about_title' => '關於所見即所得(WYSIWYG)編輯器', + 'editor_license' => '編輯器許可證與版權信息', + 'editor_lexical_license' => '這個編輯器是從 :lexicalLink 分支出來的,而 :lexicalLink 是以 MIT 授權條款散佈。', + 'editor_lexical_license_link' => '完整的授權條款詳細資訊可在這裡找到。', + 'editor_tiny_license' => '此編輯器是用 :tinyLink 構建的,基於 MIT 許可證。', + 'editor_tiny_license_link' => 'TinyMCE 的版權和許可證詳細信息可以在這裡找到。', + 'save_continue' => '保存頁面並繼續', + 'callouts_cycle' => '(繼續按下以切換類型)', + 'link_selector' => '鏈接到內容', + 'shortcuts' => '快捷鍵', + 'shortcut' => '快捷鍵', + 'shortcuts_intro' => '編輯器中提供了以下快捷鍵:', 'windows_linux' => '(Windows/Linux)', 'mac' => '(Mac)', - 'description' => 'Description', + 'description' => '說明', ]; diff --git a/lang/zh_TW/entities.php b/lang/zh_TW/entities.php index a7eb9b151d3..06db353b215 100644 --- a/lang/zh_TW/entities.php +++ b/lang/zh_TW/entities.php @@ -23,9 +23,9 @@ 'meta_updated' => '更新於 :timeLength', 'meta_updated_name' => '由 :user 更新於 :timeLength', 'meta_owned_name' => ':user 所擁有', - 'meta_reference_count' => 'Referenced by :count item|Referenced by :count items', + 'meta_reference_count' => '被 :count 個項目引用', 'entity_select' => '選取項目', - 'entity_select_lack_permission' => 'You don\'t have the required permissions to select this item', + 'entity_select_lack_permission' => '你沒有權限使用此項目', 'images' => '圖片', 'my_recent_drafts' => '我最近的草稿', 'my_recently_viewed' => '我最近檢視', @@ -39,18 +39,46 @@ 'export_pdf' => 'PDF 檔案', 'export_text' => '純文字檔案', 'export_md' => 'Markdown 檔案', + 'export_zip' => '可攜式 ZIP', + 'default_template' => '預設頁面範本', + 'default_template_explain' => '請設定一個頁面範本,作為新頁面的預設內容。請注意,這僅限於作者擁有頁面範本讀取權限時才能夠使用。', + 'default_template_select' => '選擇一個頁面範本', + 'import' => '匯入', + 'import_validate' => '驗證匯入', + 'import_desc' => '使用從相同或不同站點匯出的可攜式壓縮檔匯入書本、章節與頁面。選取 ZIP 檔案繼續。檔案上傳並通過驗證後,您就可以在下一個檢視中設定並確認匯入。', + 'import_zip_select' => '選取要上傳的 ZIP 檔案', + 'import_zip_validation_errors' => '驗證提供的 ZIP 檔案時偵測到錯誤:', + 'import_pending' => '擱置中的匯入', + 'import_pending_none' => '尚未開始匯入。', + 'import_continue' => '繼續匯入', + 'import_continue_desc' => '檢視要從上傳的 ZIP 檔匯入的內容。準備就緒後,執行匯入以將其內容加入本系統。成功匯入後,上傳的 ZIP 匯入檔案會自動移除。', + 'import_details' => '匯入詳細資訊', + 'import_run' => '執行匯入', + 'import_size' => ':size 匯入 ZIP 大小', + 'import_uploaded_at' => ':relativeTime 已上傳', + 'import_uploaded_by' => '上傳者', + 'import_location' => '匯入位置', + 'import_location_desc' => '為您匯入的內容選取目標位置。您需要相關權限才能在您選擇的位置內建立。', + 'import_delete_confirm' => '您確定要刪除此匯入嗎?', + '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', // Permissions and restrictions 'permissions' => '權限', - 'permissions_desc' => 'Set permissions here to override the default permissions provided by user roles.', - 'permissions_book_cascade' => 'Permissions set on books will automatically cascade to child chapters and pages, unless they have their own permissions defined.', - 'permissions_chapter_cascade' => 'Permissions set on chapters will automatically cascade to child pages, unless they have their own permissions defined.', + 'permissions_desc' => '設定權限,並覆蓋角色預設權限', + 'permissions_book_cascade' => '除非章節、頁面有自訂權限,否則書籍設定的權限將自動套用', + 'permissions_chapter_cascade' => '除非章節、頁面有自訂權限,否則章節的權限將自動套用', 'permissions_save' => '儲存權限', 'permissions_owner' => '擁有者', - 'permissions_role_everyone_else' => 'Everyone Else', - 'permissions_role_everyone_else_desc' => 'Set permissions for all roles not specifically overridden.', - 'permissions_role_override' => 'Override permissions for role', - 'permissions_inherit_defaults' => 'Inherit defaults', + 'permissions_role_everyone_else' => '所有其他人', + 'permissions_role_everyone_else_desc' => '設定未被指派角色時的權限', + 'permissions_role_override' => '覆蓋角色權限', + 'permissions_inherit_defaults' => '繼承預設值', // Search 'search_results' => '搜尋結果', @@ -93,24 +121,24 @@ 'shelves_save' => '儲存書架', 'shelves_books' => '此書架上的書本', 'shelves_add_books' => '新增書本至此書架', - 'shelves_drag_books' => 'Drag books below to add them to this shelf', + 'shelves_drag_books' => '拖曳書籍至下方,以便新增至此書架', 'shelves_empty_contents' => '此書架沒有分配任何書本', 'shelves_edit_and_assign' => '編輯書架以分配書本', - 'shelves_edit_named' => 'Edit Shelf :name', - 'shelves_edit' => 'Edit Shelf', - 'shelves_delete' => 'Delete Shelf', - 'shelves_delete_named' => 'Delete Shelf :name', + 'shelves_edit_named' => '編輯書架', + 'shelves_edit' => '編輯書架', + 'shelves_delete' => '刪除書架', + 'shelves_delete_named' => '刪除書架「:name」', 'shelves_delete_explain' => "這將刪除名為「:name」的書架。但其中的書本不會被刪除。", 'shelves_delete_confirmation' => '您確定要刪除此書架嗎?', - 'shelves_permissions' => 'Shelf Permissions', - 'shelves_permissions_updated' => 'Shelf Permissions Updated', - 'shelves_permissions_active' => 'Shelf Permissions Active', - 'shelves_permissions_cascade_warning' => 'Permissions on shelves do not automatically cascade to contained books. This is because a book can exist on multiple shelves. Permissions can however be copied down to child books using the option found below.', - 'shelves_permissions_create' => 'Shelf create permissions are only used for copying permissions to child books using the action below. They do not control the ability to create books.', + 'shelves_permissions' => '書架權限', + 'shelves_permissions_updated' => '已更新書架權限', + 'shelves_permissions_active' => '已啟用書架權限', + 'shelves_permissions_cascade_warning' => '因書籍可位於多個書架,因此書架權限不會自動套用至書籍上。但仍可透過下方的選項來複製權限到子書籍。', + 'shelves_permissions_create' => '書架創建權限僅用於使用下面的操作將權限複製到子圖書。這個權限不是用來控制創建書籍的。', 'shelves_copy_permissions_to_books' => '將權限複製到書本', 'shelves_copy_permissions' => '複製權限', - 'shelves_copy_permissions_explain' => 'This will apply the current permission settings of this shelf to all books contained within. Before activating, ensure any changes to the permissions of this shelf have been saved.', - 'shelves_copy_permission_success' => 'Shelf permissions copied to :count books', + 'shelves_copy_permissions_explain' => '此操作會將此書架的當前權限設置應用於其中包含的所有圖書上。 啓用前請確保已保存對此書架權限的任何更改。', + 'shelves_copy_permission_success' => '書架權限已複製到 :count 本書籍', // Books 'book' => '書本', @@ -132,9 +160,6 @@ 'books_edit_named' => '編輯書本「:bookName」', 'books_form_book_name' => '書本名稱', 'books_save' => '儲存書本', - 'books_default_template' => 'Default Page Template', - 'books_default_template_explain' => 'Assign a page template that will be used as the default content for all new pages in this book. Keep in mind this will only be used if the page creator has view access to those chosen template page.', - 'books_default_template_select' => 'Select a template page', 'books_permissions' => '書本權限', 'books_permissions_updated' => '書本權限已更新', 'books_empty_contents' => '本書目前沒有頁面或章節。', @@ -145,7 +170,9 @@ 'books_search_this' => '搜尋此書本', 'books_navigation' => '書本導覽', 'books_sort' => '排序書本內容', - 'books_sort_desc' => 'Move chapters and pages within a book to reorganise its contents. Other books can be added which allows easy moving of chapters and pages between books.', + 'books_sort_desc' => '在書籍中移動章節和頁面,重新安排其內容。可加入其他書籍,方便在書籍之間移動章節與頁面。可選擇設定自動排序規則,以便在變更時自動排序此書籍的內容。', + 'books_sort_auto_sort' => '自動排序選項', + 'books_sort_auto_sort_active' => '自動排序啟動::sortName', 'books_sort_named' => '排序書本 :bookName', 'books_sort_name' => '按名稱排序', 'books_sort_created' => '按建立時間排序', @@ -154,19 +181,19 @@ 'books_sort_chapters_last' => '最後一章', 'books_sort_show_other' => '顯示其他書本', 'books_sort_save' => '儲存新順序', - 'books_sort_show_other_desc' => 'Add other books here to include them in the sort operation, and allow easy cross-book reorganisation.', - 'books_sort_move_up' => 'Move Up', - 'books_sort_move_down' => 'Move Down', - 'books_sort_move_prev_book' => 'Move to Previous Book', - 'books_sort_move_next_book' => 'Move to Next Book', - 'books_sort_move_prev_chapter' => 'Move Into Previous Chapter', - 'books_sort_move_next_chapter' => 'Move Into Next Chapter', - 'books_sort_move_book_start' => 'Move to Start of Book', - 'books_sort_move_book_end' => 'Move to End of Book', - 'books_sort_move_before_chapter' => 'Move to Before Chapter', - 'books_sort_move_after_chapter' => 'Move to After Chapter', - 'books_copy' => 'Copy Book', - 'books_copy_success' => 'Book successfully copied', + 'books_sort_show_other_desc' => '新增書籍到排序清單中,以便跨書籍重組資料', + 'books_sort_move_up' => '上移', + 'books_sort_move_down' => '下移', + 'books_sort_move_prev_book' => '移動至前一書籍', + 'books_sort_move_next_book' => '移動至前一書籍', + 'books_sort_move_prev_chapter' => '移動至前一章節', + 'books_sort_move_next_chapter' => '移動至下一章節', + 'books_sort_move_book_start' => '移動到書籍開頭', + 'books_sort_move_book_end' => '移動到書籍結尾', + 'books_sort_move_before_chapter' => '移動到章節之前', + 'books_sort_move_after_chapter' => '移動到章節之後', + 'books_copy' => '複製書籍', + 'books_copy_success' => '書籍已成功被複製', // Chapters 'chapter' => '章節', @@ -184,14 +211,14 @@ 'chapters_save' => '儲存章節', 'chapters_move' => '移動章節', 'chapters_move_named' => '移動章節 :chapterName', - 'chapters_copy' => 'Copy Chapter', - 'chapters_copy_success' => 'Chapter successfully copied', + 'chapters_copy' => '複製章節', + 'chapters_copy_success' => '章節已成功被複製', 'chapters_permissions' => '章節權限', 'chapters_empty' => '本章目前沒有頁面。', 'chapters_permissions_active' => '章節權限已啟用', 'chapters_permissions_success' => '章節權限已更新', 'chapters_search_this' => '搜尋此章節', - 'chapter_sort_book' => 'Sort Book', + 'chapter_sort_book' => '排序書籍內容', // Pages 'page' => '頁面', @@ -207,7 +234,7 @@ 'pages_delete_draft' => '刪除草稿頁面', 'pages_delete_success' => '頁面已刪除', 'pages_delete_draft_success' => '草稿頁面已刪除', - 'pages_delete_warning_template' => 'This page is in active use as a book default page template. These books will no longer have a default page template assigned after this page is deleted.', + 'pages_delete_warning_template' => '此頁面是當前書籍或章節的默認頁面模板。刪除此頁面後,這些書籍或章節的默認頁面模板將被取消。', 'pages_delete_confirm' => '您確定要刪除此頁面嗎?', 'pages_delete_draft_confirm' => '您確定要刪除此草稿頁面嗎?', 'pages_editing_named' => '正在編輯頁面 :pageName', @@ -218,21 +245,23 @@ 'pages_editing_page' => '正在編輯頁面', 'pages_edit_draft_save_at' => '草稿儲存於 ', 'pages_edit_delete_draft' => '刪除草稿', - 'pages_edit_delete_draft_confirm' => 'Are you sure you want to delete your draft page changes? All of your changes, since the last full save, will be lost and the editor will be updated with the latest page non-draft save state.', + 'pages_edit_delete_draft_confirm' => '您確定要刪除您的草稿頁面更改嗎?自上次完整保存以來的所有更改都將丟失,編輯器將更新為最新非草稿頁面。', 'pages_edit_discard_draft' => '放棄草稿', - 'pages_edit_switch_to_markdown' => 'Switch to Markdown Editor', - 'pages_edit_switch_to_markdown_clean' => '(Clean Content)', - 'pages_edit_switch_to_markdown_stable' => '(Stable Content)', - 'pages_edit_switch_to_wysiwyg' => 'Switch to WYSIWYG Editor', + 'pages_edit_switch_to_markdown' => '切換到 Markdown 編輯器', + 'pages_edit_switch_to_markdown_clean' => '(清除內容)', + 'pages_edit_switch_to_markdown_stable' => '(保留內容)', + 'pages_edit_switch_to_wysiwyg' => '切換到所見即所得編輯器', + 'pages_edit_switch_to_new_wysiwyg' => '切換為新的所見即所得編輯器', + 'pages_edit_switch_to_new_wysiwyg_desc' => '(In Beta Testing)', 'pages_edit_set_changelog' => '設定變更日誌', 'pages_edit_enter_changelog_desc' => '輸入對您所做變動的簡易描述', 'pages_edit_enter_changelog' => '輸入變更日誌', 'pages_editor_switch_title' => '切換編輯器', - 'pages_editor_switch_are_you_sure' => 'Are you sure you want to change the editor for this page?', - 'pages_editor_switch_consider_following' => 'Consider the following when changing editors:', - 'pages_editor_switch_consideration_a' => 'Once saved, the new editor option will be used by any future editors, including those that may not be able to change editor type themselves.', - 'pages_editor_switch_consideration_b' => 'This can potentially lead to a loss of detail and syntax in certain circumstances.', - 'pages_editor_switch_consideration_c' => 'Tag or changelog changes, made since last save, won\'t persist across this change.', + 'pages_editor_switch_are_you_sure' => '你想要更改這頁所使用的編輯器嗎?', + 'pages_editor_switch_consider_following' => '更換編輯器時請考慮以下事項:', + 'pages_editor_switch_consideration_a' => '一旦選擇使用新的編輯器,其他頁面以及沒有權限更換編輯器的使用者都將使用新的編輯器', + 'pages_editor_switch_consideration_b' => '在某些情況下,將遺失細部設定以及語法', + 'pages_editor_switch_consideration_c' => '切換編輯器時,本次的標籤設定、版本修訂記錄將不會被保留', 'pages_save' => '儲存頁面', 'pages_title' => '頁面標題', 'pages_name' => '頁面名稱', @@ -242,9 +271,10 @@ 'pages_md_insert_link' => '插入連結', 'pages_md_insert_drawing' => '插入繪圖', 'pages_md_show_preview' => '顯示預覽', - 'pages_md_sync_scroll' => 'Sync preview scroll', - 'pages_drawing_unsaved' => 'Unsaved Drawing Found', - 'pages_drawing_unsaved_confirm' => 'Unsaved drawing data was found from a previous failed drawing save attempt. Would you like to restore and continue editing this unsaved drawing?', + 'pages_md_sync_scroll' => '預覽頁面同步捲動', + 'pages_md_plain_editor' => 'Plaintext editor', + 'pages_drawing_unsaved' => '偵測到未儲存的繪圖', + 'pages_drawing_unsaved_confirm' => '從之前保存失敗的繪圖中發現了可恢復的數據。您想恢復並繼續編輯這個未保存的繪圖嗎?', 'pages_not_in_chapter' => '頁面不在章節中', 'pages_move' => '移動頁面', 'pages_copy' => '複製頁面', @@ -254,17 +284,17 @@ 'pages_permissions_success' => '頁面權限已更新', 'pages_revision' => '修訂版本', 'pages_revisions' => '頁面修訂版本', - 'pages_revisions_desc' => 'Listed below are all the past revisions of this page. You can look back upon, compare, and restore old page versions if permissions allow. The full history of the page may not be fully reflected here since, depending on system configuration, old revisions could be auto-deleted.', + 'pages_revisions_desc' => '下面列出的是該頁面的所有過去修訂。如果權限允許,您可以回顧、比較和恢復舊的頁面版本。頁面的完整歷史可能不會在這裡完全反映出來,因為根據系統配置,舊的修訂可能會被自動刪除。', 'pages_revisions_named' => ':pageName 頁面修訂版本', 'pages_revision_named' => ':pageName 頁面修訂版本', 'pages_revision_restored_from' => '從 #:id; :summary 復原', 'pages_revisions_created_by' => '建立者', 'pages_revisions_date' => '修訂日期', 'pages_revisions_number' => '#', - 'pages_revisions_sort_number' => 'Revision Number', + 'pages_revisions_sort_number' => '修訂版號', 'pages_revisions_numbered' => '修訂版本 #:id', 'pages_revisions_numbered_changes' => '修訂版本 #:id 變更', - 'pages_revisions_editor' => 'Editor Type', + 'pages_revisions_editor' => '編輯器類型', 'pages_revisions_changelog' => '變動日誌', 'pages_revisions_changes' => '變動', 'pages_revisions_current' => '目前版本', @@ -272,20 +302,20 @@ 'pages_revisions_restore' => '還原', 'pages_revisions_none' => '此頁面沒有修訂', 'pages_copy_link' => '複製連結', - 'pages_edit_content_link' => 'Jump to section in editor', - 'pages_pointer_enter_mode' => 'Enter section select mode', - 'pages_pointer_label' => 'Page Section Options', - 'pages_pointer_permalink' => 'Page Section Permalink', - 'pages_pointer_include_tag' => 'Page Section Include Tag', - 'pages_pointer_toggle_link' => 'Permalink mode, Press to show include tag', - 'pages_pointer_toggle_include' => 'Include tag mode, Press to show permalink', + 'pages_edit_content_link' => '移動到編輯器區段', + 'pages_pointer_enter_mode' => '進入區段選取模式', + 'pages_pointer_label' => '頁面區段選項', + 'pages_pointer_permalink' => '頁面區段永久連結', + 'pages_pointer_include_tag' => '包含標籤的頁面區段', + 'pages_pointer_toggle_link' => '永久連結模式,點擊顯示包含的標籤', + 'pages_pointer_toggle_include' => '包含標籤模式,按下顯示永久鏈接', 'pages_permissions_active' => '頁面權限已啟用', 'pages_initial_revision' => '初次發布', - 'pages_references_update_revision' => 'System auto-update of internal links', + 'pages_references_update_revision' => '系統自動更新的內部鏈接', 'pages_initial_name' => '新頁面', 'pages_editing_draft_notification' => '您正在編輯最後儲存為 :timeDiff 的草稿。', 'pages_draft_edited_notification' => '此頁面已經被更新過。建議您放棄此草稿。', - 'pages_draft_page_changed_since_creation' => 'This page has been updated since this draft was created. It is recommended that you discard this draft or take care not to overwrite any page changes.', + 'pages_draft_page_changed_since_creation' => '這個頁面在您的草稿創建後被其他用戶更新了,您目前的草稿不包含新的內容。建議您放棄此草稿,或是注意不要覆蓋新的頁面更改。', 'pages_draft_edit_active' => [ 'start_a' => ':count 位使用者已經開始編輯此頁面', 'start_b' => '使用者 :userName 已經開始編輯此頁面', @@ -293,44 +323,44 @@ 'time_b' => '在最近:minCount分鐘', 'message' => ':start :time。注意不要覆寫其他人的更新!', ], - 'pages_draft_discarded' => 'Draft discarded! The editor has been updated with the current page content', - 'pages_draft_deleted' => 'Draft deleted! The editor has been updated with the current page content', + 'pages_draft_discarded' => '草稿已丟棄!編輯器已更新到當前頁面內容', + 'pages_draft_deleted' => '草稿已刪除!編輯器已更新為當前頁面內容', 'pages_specific' => '特定頁面', 'pages_is_template' => '頁面模板', // Editor Sidebar - 'toggle_sidebar' => 'Toggle Sidebar', + 'toggle_sidebar' => '切換側邊欄', 'page_tags' => '頁面標籤', 'chapter_tags' => '章節標籤', 'book_tags' => '書本標籤', 'shelf_tags' => '書架標籤', 'tag' => '標籤', 'tags' => '標籤', - 'tags_index_desc' => 'Tags can be applied to content within the system to apply a flexible form of categorization. Tags can have both a key and value, with the value being optional. Once applied, content can then be queried using the tag name and value.', + 'tags_index_desc' => '為內容加上標籤可靈活得進行分類。標籤可以設定名稱與數值,其中數值為選用。一旦標籤設定完成,便可透過標籤名稱與數值來進行搜尋。', 'tag_name' => '標籤名稱', 'tag_value' => '標籤值(選擇性)', 'tags_explain' => "加入一些標籤以更好地對您的內容進行分類。 \n 您可以為標籤分配一個值,以進行更深入的組織。", 'tags_add' => '新增另一個標籤', 'tags_remove' => '移除此標籤', - 'tags_usages' => 'Total tag usages', - 'tags_assigned_pages' => 'Assigned to Pages', - 'tags_assigned_chapters' => 'Assigned to Chapters', - 'tags_assigned_books' => 'Assigned to Books', - 'tags_assigned_shelves' => 'Assigned to Shelves', - 'tags_x_unique_values' => ':count unique values', - 'tags_all_values' => 'All values', + 'tags_usages' => '總用量', + 'tags_assigned_pages' => '頁面', + 'tags_assigned_chapters' => '章節', + 'tags_assigned_books' => '書籍', + 'tags_assigned_shelves' => '書架', + 'tags_x_unique_values' => ':count 個不同的數值', + 'tags_all_values' => '所有數值', 'tags_view_tags' => '檢視標籤', 'tags_view_existing_tags' => '檢視已存在的標籤', - 'tags_list_empty_hint' => 'Tags can be assigned via the page editor sidebar or while editing the details of a book, chapter or shelf.', + 'tags_list_empty_hint' => '可在編輯書架、書籍時設定標籤,或是在編輯章節、頁面時透過側邊欄設定', 'attachments' => '附件', 'attachments_explain' => '上傳一些檔案或附加連結以顯示在您的網頁上。將顯示在在頁面的側邊欄。', 'attachments_explain_instant_save' => '此處的變動將會立刻儲存。', 'attachments_upload' => '上傳檔案', 'attachments_link' => '附加連結', - 'attachments_upload_drop' => 'Alternatively you can drag and drop a file here to upload it as an attachment.', + 'attachments_upload_drop' => '你也可以將檔案拖曳到此來上傳附加檔案', 'attachments_set_link' => '設定連結', 'attachments_delete' => '您確定要刪除此附件嗎?', - 'attachments_dropzone' => 'Drop files here to upload', + 'attachments_dropzone' => '將檔案拖曳至此來上傳', 'attachments_no_files' => '尚未上傳檔案', 'attachments_explain_link' => '如果您不想上傳檔案,則可以附加連結。這可以是指向其他頁面的連結,也可以是指向雲端檔案的連結。', 'attachments_link_name' => '連結名稱', @@ -367,19 +397,28 @@ 'comment' => '評論', 'comments' => '評論', 'comment_add' => '新增評論', + 'comment_none' => 'No comments to display', 'comment_placeholder' => '在這裡評論', - 'comment_count' => '{0} 無評論 |{1} :count 則評論 |[2,*] :count 則評論', + 'comment_thread_count' => ':count Comment Thread|:count Comment Threads', + 'comment_archived_count' => ':count Archived', + 'comment_archived_threads' => 'Archived Threads', 'comment_save' => '儲存評論', 'comment_new' => '新評論', 'comment_created' => '評論於 :createDiff', 'comment_updated' => '由 :username 於 :updateDiff 更新', - 'comment_updated_indicator' => 'Updated', + 'comment_updated_indicator' => '已更新', '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_delete_confirm' => '您確定要刪除這則評論?', 'comment_in_reply_to' => '回覆 :commentId', - 'comment_editor_explain' => 'Here are the comments that have been left on this page. Comments can be added & managed when viewing the saved page.', + 'comment_reference' => 'Reference', + 'comment_reference_outdated' => '(Outdated)', + 'comment_editor_explain' => '此為頁面上的評論。在查看檢視與儲存頁面的同時,可以新增和管理評論。', // Revision 'revision_delete_confirm' => '您確定要刪除此修訂版本嗎?', @@ -387,51 +426,51 @@ 'revision_cannot_delete_latest' => '無法刪除最新修訂版本。', // Copy view - 'copy_consider' => 'Please consider the below when copying content.', - 'copy_consider_permissions' => 'Custom permission settings will not be copied.', - 'copy_consider_owner' => 'You will become the owner of all copied content.', - 'copy_consider_images' => 'Page image files will not be duplicated & the original images will retain their relation to the page they were originally uploaded to.', - 'copy_consider_attachments' => 'Page attachments will not be copied.', - 'copy_consider_access' => 'A change of location, owner or permissions may result in this content being accessible to those previously without access.', + 'copy_consider' => '複製內容時請注意以下事項', + 'copy_consider_permissions' => '自定義權限設置將不會被複製。', + 'copy_consider_owner' => '您將成為所有已複製內容的所有者。', + 'copy_consider_images' => '頁面中的圖像文件不會被複製,原始圖像將保留它們與最初上傳到的頁面的關係。', + 'copy_consider_attachments' => '頁面中的附件不會被複製。', + 'copy_consider_access' => '改變位置、所有者或權限可能會導致此內容被以前無法訪問的人訪問。', // Conversions - 'convert_to_shelf' => 'Convert to Shelf', - 'convert_to_shelf_contents_desc' => 'You can convert this book to a new shelf with the same contents. Chapters contained within this book will be converted to new books. If this book contains any pages, that are not in a chapter, this book will be renamed and contain such pages, and this book will become part of the new shelf.', - 'convert_to_shelf_permissions_desc' => 'Any permissions set on this book will be copied to the new shelf and to all new child books that don\'t have their own permissions enforced. Note that permissions on shelves do not auto-cascade to content within, as they do for books.', - 'convert_book' => 'Convert Book', - 'convert_book_confirm' => 'Are you sure you want to convert this book?', - 'convert_undo_warning' => 'This cannot be as easily undone.', - 'convert_to_book' => 'Convert to Book', - 'convert_to_book_desc' => 'You can convert this chapter to a new book with the same contents. Any permissions set on this chapter will be copied to the new book but any inherited permissions, from the parent book, will not be copied which could lead to a change of access control.', - 'convert_chapter' => 'Convert Chapter', - 'convert_chapter_confirm' => 'Are you sure you want to convert this chapter?', + 'convert_to_shelf' => '轉換成書架', + 'convert_to_shelf_contents_desc' => '你可以將此書籍轉換成包含相同內容的新書架,書籍中的章節則會轉換成書籍。若書籍中有不屬於任何章節的頁面,這些頁面會自動轉換成新書架中的書籍。', + 'convert_to_shelf_permissions_desc' => '在這本書上設置的任何權限都將複製到所有未強制執行權限的新書架和新子圖書上。請注意,書架上的權限不會像圖書那樣繼承到內容物上。', + 'convert_book' => '轉換成書本', + 'convert_book_confirm' => '您確定要轉換此書本嗎?', + 'convert_undo_warning' => '這可不能輕易撤消。', + 'convert_to_book' => '轉換成書籍', + 'convert_to_book_desc' => '您可以將此章節轉換為具有相同內容的新書本。此章節中設置的任何權限都將複製到新書本上,但從父圖書繼承的任何權限都不會被複製,這可能會導致訪問控制發生變化。', + 'convert_chapter' => '轉換章節', + 'convert_chapter_confirm' => '您確定要轉換此章節嗎?', // References - 'references' => 'References', - 'references_none' => 'There are no tracked references to this item.', - 'references_to_desc' => 'Listed below is all the known content in the system that links to this item.', + 'references' => '引用', + 'references_none' => '沒有跟蹤到對此項目的引用。', + 'references_to_desc' => '下方列出了系統中鏈接到此項目的所有已知內容。', // Watch Options - 'watch' => 'Watch', + 'watch' => '追蹤', 'watch_title_default' => '預設偏好設定', - 'watch_desc_default' => 'Revert watching to just your default notification preferences.', - 'watch_title_ignore' => 'Ignore', - 'watch_desc_ignore' => 'Ignore all notifications, including those from user-level preferences.', - 'watch_title_new' => 'New Pages', - 'watch_desc_new' => 'Notify when any new page is created within this item.', - 'watch_title_updates' => 'All Page Updates', - 'watch_desc_updates' => 'Notify upon all new pages and page changes.', - 'watch_desc_updates_page' => 'Notify upon all page changes.', - 'watch_title_comments' => 'All Page Updates & Comments', - 'watch_desc_comments' => 'Notify upon all new pages, page changes and new comments.', - 'watch_desc_comments_page' => 'Notify upon page changes and new comments.', - 'watch_change_default' => 'Change default notification preferences', - 'watch_detail_ignore' => 'Ignoring notifications', - 'watch_detail_new' => 'Watching for new pages', - 'watch_detail_updates' => 'Watching new pages and updates', - 'watch_detail_comments' => 'Watching new pages, updates & comments', - 'watch_detail_parent_book' => 'Watching via parent book', - 'watch_detail_parent_book_ignore' => 'Ignoring via parent book', - 'watch_detail_parent_chapter' => 'Watching via parent chapter', - 'watch_detail_parent_chapter_ignore' => 'Ignoring via parent chapter', + 'watch_desc_default' => '還原成預設的通知設定', + 'watch_title_ignore' => '忽略', + 'watch_desc_ignore' => '忽略所有通知,包括來自用戶級偏好的通知。', + 'watch_title_new' => '新頁面', + 'watch_desc_new' => '在此項目中創建任何新頁面時通知我。', + 'watch_title_updates' => '所有頁面更新', + 'watch_desc_updates' => '在所有新頁面和頁面更改時通知我。', + 'watch_desc_updates_page' => '在有頁面發生更改時通知我。', + 'watch_title_comments' => '所有頁面更新和評論', + 'watch_desc_comments' => '在有新頁面、頁面更改和新評論時通知我。', + 'watch_desc_comments_page' => '在有頁面更改和新評論時通知我。', + 'watch_change_default' => '更改默認通知偏好', + 'watch_detail_ignore' => '忽略通知', + 'watch_detail_new' => '追蹤新頁面', + 'watch_detail_updates' => '追蹤新頁面與異動', + 'watch_detail_comments' => '追蹤新頁面、自動與評論', + 'watch_detail_parent_book' => '已關注—繼承自父書本', + 'watch_detail_parent_book_ignore' => '已忽略—繼承自父書本', + 'watch_detail_parent_chapter' => '已關注—繼承自父章節', + 'watch_detail_parent_chapter_ignore' => '已忽略—繼承自父章節', ]; diff --git a/lang/zh_TW/errors.php b/lang/zh_TW/errors.php index 044377a754d..d4239ed6c64 100644 --- a/lang/zh_TW/errors.php +++ b/lang/zh_TW/errors.php @@ -10,6 +10,7 @@ // Auth 'error_user_exists_different_creds' => '電子郵件為 :email 已存在,但帳號密碼不同。', + 'auth_pre_register_theme_prevention' => '無法使用資料建立帳號', 'email_already_confirmed' => '已確認電子郵件,請嘗試登入。', 'email_confirmation_invalid' => '這個確認權杖無效或已被使用,請嘗試重新註冊。', 'email_confirmation_expired' => '這個確認權杖無效或已被使用,已傳送新的確認電子郵件。', @@ -23,7 +24,6 @@ 'saml_invalid_response_id' => '此應用程式啟動的處理程序無法識別來自外部認證系統的請求。登入後回上一頁可能會造成此問題。', 'saml_fail_authed' => '使用 :system 登入失敗,系統未提供成功的授權', 'oidc_already_logged_in' => '已登入', - 'oidc_user_not_registered' => '使用者 :name 未註冊,並已停用自動註冊', 'oidc_no_email_address' => '在外部認證系統提供的資料中找不到該使用者的電子郵件地址', 'oidc_fail_authed' => '使用 :system 登入失敗,系統未提供成功的授權', 'social_no_action_defined' => '未定義動作', @@ -37,36 +37,37 @@ 'social_driver_not_found' => '找不到社交驅動程式', 'social_driver_not_configured' => '您的 :socialAccount 社交設定不正確。', 'invite_token_expired' => '此邀請連結已過期。您可以嘗試重設您的帳號密碼。', + 'login_user_not_found' => '使用者不存在', // System 'path_not_writable' => '無法上傳到 :filePath 檔案路徑。請確定其對伺服器來說是可寫入的。', 'cannot_get_image_from_url' => '無法從 :url 取得圖片', 'cannot_create_thumbs' => '伺服器無法建立縮圖。請檢查您是否安裝了 PHP 的 GD 擴充程式。', 'server_upload_limit' => '伺服器不允許上傳這個大的檔案。請嘗試較小的檔案。', - 'server_post_limit' => 'The server cannot receive the provided amount of data. Try again with less data or a smaller file.', + 'server_post_limit' => '伺服器無法處理提供的資料,請嘗試刪減內容或較小的檔案', 'uploaded' => '伺服器不允許上傳這個大的檔案。請嘗試較小的檔案。', // Drawing & Images 'image_upload_error' => '上傳圖片時發生錯誤', 'image_upload_type_error' => '上傳圖片類型無效', - 'image_upload_replace_type' => 'Image file replacements must be of the same type', - 'image_upload_memory_limit' => 'Failed to handle image upload and/or create thumbnails due to system resource limits.', - 'image_thumbnail_memory_limit' => 'Failed to create image size variations due to system resource limits.', - 'image_gallery_thumbnail_memory_limit' => 'Failed to create gallery thumbnails due to system resource limits.', + 'image_upload_replace_type' => '必須使用的檔案類型才能置換圖檔', + 'image_upload_memory_limit' => '由於系統限制,無法處理上傳的檔案或縮圖', + 'image_thumbnail_memory_limit' => '由於系統限制,無法建立不同尺寸的圖片', + 'image_gallery_thumbnail_memory_limit' => '由於系統限制,無法建立縮圖', 'drawing_data_not_found' => '無法載入繪圖資料,繪圖檔案可能不存在,或您可能沒有權限存取它。', // Attachments 'attachment_not_found' => '找不到附件', - 'attachment_upload_error' => 'An error occurred uploading the attachment file', + 'attachment_upload_error' => '上傳檔案時發生錯誤', // Pages 'page_draft_autosave_fail' => '無法儲存草稿。請確保您在儲存此頁面前已連線至網際網路', - 'page_draft_delete_fail' => 'Failed to delete page draft and fetch current page saved content', + 'page_draft_delete_fail' => '無法刪除草稿並取得最新的頁面存檔', 'page_custom_home_deletion' => '無法刪除被設定為首頁的頁面', // Entities 'entity_not_found' => '找不到實體', - 'bookshelf_not_found' => '未找到書棧', + 'bookshelf_not_found' => '未找到書架', 'book_not_found' => '找不到書本', 'page_not_found' => '找不到頁面', 'chapter_not_found' => '找不到章節', @@ -77,6 +78,7 @@ // Users 'users_cannot_delete_only_admin' => '您不能刪除唯一的管理員帳號', 'users_cannot_delete_guest' => '您不能刪除訪客使用者', + 'users_could_not_send_invite' => '由於寄送邀請電子郵件失敗,因此無法建立使用者', // Roles 'role_cannot_be_edited' => '無法編輯這個角色', @@ -103,6 +105,18 @@ 'app_down' => ':appName 離線中', 'back_soon' => '它應該很快就會重新上線。', + // Import + 'import_zip_cant_read' => '無法讀取 ZIP 檔案。', + 'import_zip_cant_decode_data' => '無法尋找並解碼 ZIP data.json 內容。', + 'import_zip_no_data' => 'ZIP 檔案資料沒有預期的書本、章節或頁面內容。', + 'import_validation_failed' => '匯入 ZIP 驗證失敗,發生錯誤:', + 'import_zip_failed_notification' => '匯入 ZIP 檔案失敗。', + 'import_perms_books' => '您缺乏建立書本所需的權限。', + 'import_perms_chapters' => '您缺乏建立章節所需的權限。', + 'import_perms_pages' => '您缺乏建立頁面所需的權限。', + 'import_perms_images' => '您缺乏建立影像所需的權限。', + 'import_perms_attachments' => '您缺乏建立附件所需的權限。', + // API errors 'api_no_authorization_found' => '在請求上找不到授權權杖', 'api_bad_authorization_format' => '在請求中找到授權權杖,但格式似乎不正確', @@ -115,5 +129,5 @@ 'maintenance_test_email_failure' => '寄送測試電子郵件時發生錯誤:', // HTTP errors - 'http_ssr_url_no_match' => 'The URL does not match the configured allowed SSR hosts', + 'http_ssr_url_no_match' => 'URL 與設置的 SSR 主機不符', ]; diff --git a/lang/zh_TW/notifications.php b/lang/zh_TW/notifications.php index 1afd23f1dc4..7fcfa640090 100644 --- a/lang/zh_TW/notifications.php +++ b/lang/zh_TW/notifications.php @@ -4,24 +4,24 @@ */ return [ - 'new_comment_subject' => 'New comment on page: :pageName', - 'new_comment_intro' => 'A user has commented on a page in :appName:', - 'new_page_subject' => 'New page: :pageName', - 'new_page_intro' => 'A new page has been created in :appName:', - 'updated_page_subject' => 'Updated page: :pageName', - 'updated_page_intro' => 'A page has been updated in :appName:', - 'updated_page_debounce' => 'To prevent a mass of notifications, for a while you won\'t be sent notifications for further edits to this page by the same editor.', + 'new_comment_subject' => '頁面上有新評論::pageName', + 'new_comment_intro' => '一位用戶在 :appName: 的頁面上發表了評論', + 'new_page_subject' => '新頁面::pageName', + 'new_page_intro' => ':appName: 中創建了一個新頁面', + 'updated_page_subject' => '頁面更新::pageName', + 'updated_page_intro' => ':appName: 中的一個頁面已被更新', + 'updated_page_debounce' => '為了防止出現大量通知,一段時間內您不會收到同一編輯者再次編輯本頁面的通知。', - 'detail_page_name' => 'Page Name:', - 'detail_page_path' => 'Page Path:', - 'detail_commenter' => 'Commenter:', - 'detail_comment' => 'Comment:', - 'detail_created_by' => 'Created By:', - 'detail_updated_by' => 'Updated By:', + 'detail_page_name' => '頁面名稱:', + 'detail_page_path' => '頁面路徑:', + 'detail_commenter' => '評論者:', + 'detail_comment' => '評論:', + 'detail_created_by' => '建立者:', + 'detail_updated_by' => '更新者:', - 'action_view_comment' => 'View Comment', - 'action_view_page' => 'View Page', + 'action_view_comment' => '檢視評論', + 'action_view_page' => '查看頁面', - 'footer_reason' => 'This notification was sent to you because :link cover this type of activity for this item.', - 'footer_reason_link' => 'your notification preferences', + 'footer_reason' => '向您發送此通知是因為 :link 涵蓋了該項目的此類活動。', + 'footer_reason_link' => '個人偏好通知設定', ]; diff --git a/lang/zh_TW/preferences.php b/lang/zh_TW/preferences.php index 9bed83fd132..4900ca15264 100644 --- a/lang/zh_TW/preferences.php +++ b/lang/zh_TW/preferences.php @@ -5,10 +5,10 @@ */ return [ - 'my_account' => 'My Account', + 'my_account' => '帳號設定', 'shortcuts' => '快捷鍵', - 'shortcuts_interface' => 'UI Shortcut Preferences', + 'shortcuts_interface' => '快速鍵設定', 'shortcuts_toggle_desc' => '您可以在此處啟用或停用鍵盤系統介面快捷鍵,這些快捷鍵用於導覽與操作。', 'shortcuts_customize_desc' => '您可以自訂下方的每個快捷鍵。只要在選取快捷鍵輸入後按下您想要使用的按鍵組合即可。', 'shortcuts_toggle_label' => '鍵盤快捷鍵已啟用', @@ -17,35 +17,35 @@ 'shortcuts_save' => '儲存快捷鍵', 'shortcuts_overlay_desc' => '注意:當快捷鍵啟用時,可以按下「?」來使用小幫手覆蓋畫面。這將會在目前的畫面上突顯可見動作的快捷鍵。', 'shortcuts_update_success' => '快捷鍵偏好設定已更新!', - 'shortcuts_overview_desc' => 'Manage keyboard shortcuts you can use to navigate the system user interface.', + 'shortcuts_overview_desc' => '你可使用鍵盤快速鍵來快速瀏覽系統界面', - 'notifications' => 'Notification Preferences', - 'notifications_desc' => 'Control the email notifications you receive when certain activity is performed within the system.', - 'notifications_opt_own_page_changes' => 'Notify upon changes to pages I own', - 'notifications_opt_own_page_comments' => 'Notify upon comments on pages I own', - 'notifications_opt_comment_replies' => 'Notify upon replies to my comments', + 'notifications' => '通知設定', + 'notifications_desc' => '控制在系統有特定活動時,是否要接收電子郵件通知', + 'notifications_opt_own_page_changes' => '當我的頁面有異動時發送通知', + 'notifications_opt_own_page_comments' => '當我的頁面有評論時發送通知', + 'notifications_opt_comment_replies' => '當我的評論有新的回覆時發送通知', 'notifications_save' => '儲存偏好設定', - 'notifications_update_success' => 'Notification preferences have been updated!', - 'notifications_watched' => 'Watched & Ignored Items', - 'notifications_watched_desc' => ' Below are the items that have custom watch preferences applied. To update your preferences for these, view the item then find the watch options in the sidebar.', + 'notifications_update_success' => '通知設定已更新', + 'notifications_watched' => '追蹤與忽略的項目', + 'notifications_watched_desc' => '以下是已經套用自訂追蹤設定的項目。若要調整,請至該項目側邊欄中的「追蹤」更新設定', - 'auth' => 'Access & Security', - 'auth_change_password' => 'Change Password', - 'auth_change_password_desc' => 'Change the password you use to log-in to the application. This must be at least 8 characters long.', - 'auth_change_password_success' => 'Password has been updated!', + 'auth' => '存取、安全', + 'auth_change_password' => '變更密碼', + 'auth_change_password_desc' => '修改登入時使用的密碼,密碼長度至少需要有 8 個字', + 'auth_change_password_success' => '密碼已更新', - 'profile' => 'Profile Details', - 'profile_desc' => 'Manage the details of your account which represents you to other users, in addition to details that are used for communication and system personalisation.', - 'profile_view_public' => 'View Public Profile', - 'profile_name_desc' => 'Configure your display name which will be visible to other users in the system through the activity you perform, and content you own.', - 'profile_email_desc' => 'This email will be used for notifications and, depending on active system authentication, system access.', - 'profile_email_no_permission' => 'Unfortunately you don\'t have permission to change your email address. If you want to change this, you\'d need to ask an administrator to change this for you.', - 'profile_avatar_desc' => 'Select an image which will be used to represent yourself to others in the system. Ideally this image should be square and about 256px in width and height.', - 'profile_admin_options' => 'Administrator Options', - 'profile_admin_options_desc' => 'Additional administrator-level options, like those to manage role assignments, can be found for your user account in the "Settings > Users" area of the application.', + 'profile' => '個人檔案', + 'profile_desc' => '管理欲呈現給其他使用的個人資料,另外資料也用於交流與系統個人化', + 'profile_view_public' => '檢視公開的個人檔案', + 'profile_name_desc' => '設定欲公開顯示的名稱,系統中的其他使用者將會透過內容、活動記錄看到您設定的名稱', + 'profile_email_desc' => 'Email 將用於使用者通知以及系統存取授權 (若有啟用)', + 'profile_email_no_permission' => '很遺憾您沒有權限變更您的 email,如果有需要,請洽詢系統管理員協助變更 email', + 'profile_avatar_desc' => ' 選一張圖片供顯示給其他使用者看,理想的圖片大小為 256x256 像素', + 'profile_admin_options' => '管理選項', + 'profile_admin_options_desc' => '系統管理層選項可在「設定」=>「使用者」找到,像是角色管理等', - 'delete_account' => 'Delete Account', - 'delete_my_account' => 'Delete My Account', - 'delete_my_account_desc' => 'This will fully delete your user account from the system. You will not be able to recover this account or revert this action. Content you\'ve created, such as created pages and uploaded images, will remain.', - 'delete_my_account_warning' => 'Are you sure you want to delete your account?', + 'delete_account' => '刪除帳號', + 'delete_my_account' => '刪除我的帳號', + 'delete_my_account_desc' => '將從系統中刪除所有帳號資料,此動作無法復原。你所建立的內容、頁面以及上傳的圖片將會保留。', + 'delete_my_account_warning' => '您確定要刪除您的帳戶嗎?', ]; diff --git a/lang/zh_TW/settings.php b/lang/zh_TW/settings.php index c3c86177026..752d3f2a74e 100644 --- a/lang/zh_TW/settings.php +++ b/lang/zh_TW/settings.php @@ -27,14 +27,14 @@ 'app_secure_images_toggle' => '啟用更高安全性的圖片上傳', 'app_secure_images_desc' => '因為效能因素,所有圖片都是公開的。此選項會在圖片的網址前加入一串隨機且難以猜測的字串。確保未啟用目錄索引,讓直接進入變得更困難。', 'app_default_editor' => '預設頁面編輯器', - 'app_default_editor_desc' => '选择编辑新页面时默认使用的编辑器。这可以在权限允许的页面级别被覆盖。', + 'app_default_editor_desc' => '選擇編輯頁面時預設使用的編輯器,這項設定值可被頁面的權限覆蓋', 'app_custom_html' => '自訂 HTML 標題內容', 'app_custom_html_desc' => '此處加入的任何內容都將插入到每個頁面的 部分的底部,這對於覆蓋樣式或加入分析程式碼很方便。', 'app_custom_html_disabled_notice' => '在此設定頁面上停用了自訂 HTML 標題內容,以確保任何重大變更都能被還原。', 'app_logo' => '應用程式圖示', 'app_logo_desc' => '這個設定會被使用在應用程式標題欄等區域;圖片的高度應為 86 像素,大型圖片將會按比例縮小。', 'app_icon' => '應用程式圖示', - 'app_icon_desc' => 'This icon is used for browser tabs and shortcut icons. This should be a 256px square PNG image.', + 'app_icon_desc' => '這個圖示將顯示在瀏覽器分頁以及捷徑,應為 256 像素的的正方形 PNG 圖片', 'app_homepage' => '應用程式首頁', 'app_homepage_desc' => '選取要作為首頁的頁面,這將會取代預設首頁。選定頁面的頁面權限將會被忽略。', 'app_homepage_select' => '選取頁面', @@ -49,8 +49,8 @@ // Color settings 'color_scheme' => '應用程式配色', - 'color_scheme_desc' => 'Set the colors to use in the application user interface. Colors can be configured separately for dark and light modes to best fit the theme and ensure legibility.', - 'ui_colors_desc' => 'Set the application primary color and default link color. The primary color is mainly used for the header banner, buttons and interface decorations. The default link color is used for text-based links and actions, both within written content and in the application interface.', + 'color_scheme_desc' => '設定使用者頁面欲使用的顏色。可分別針對淺色模式與暗色模式設定顏色,以確保容易閱讀', + 'ui_colors_desc' => '設定頁面主要色彩以及頁面連結預設顏色。主要色彩用於標題橫幅、按鈕以及主要操作界面,頁面連結顏色,主要用於主頁面、編輯頁面中的文字連結、操作按鈕。', 'app_color' => '主要顏色', 'link_color' => '連結預設顏色', 'content_colors_desc' => '設定頁面層次結構中的元素顏色;為了提高可讀性,建議選擇亮度與預設顏色相似的顏色。', @@ -74,6 +74,36 @@ 'reg_confirm_restrict_domain_desc' => '輸入您想要限制註冊的電子郵件網域列表,以英文逗號分隔。在可以與應用程式互動前,使用者將會收到電子郵件以確認他們的電子郵件地址。
注意,使用者可以在註冊成功後變更他們的電子郵件地址。', 'reg_confirm_restrict_domain_placeholder' => '尚未設定限制', + // Sorting Settings + 'sorting' => '排序', + 'sorting_book_default' => '預設書籍排序', + 'sorting_book_default_desc' => '選取要套用至新書籍的預設排序規則。這不會影響現有書籍,並可按書籍覆寫。', + 'sorting_rules' => '排序規則', + 'sorting_rules_desc' => '這些是預先定義的排序作業,可套用於系統中的內容。', + 'sort_rule_assigned_to_x_books' => '指定給 :count 本書', + 'sort_rule_create' => '建立排序規則', + 'sort_rule_edit' => '編輯排序規則', + 'sort_rule_delete' => '刪除排序規則', + 'sort_rule_delete_desc' => '從系統移除此排序規則。使用此排序的書籍將會還原為手動排序。', + 'sort_rule_delete_warn_books' => '此排序規則目前用於 :count 本書。您確定您想要刪除嗎?', + 'sort_rule_delete_warn_default' => '此排序規則目前為書籍的預設值。您確定您想要刪除嗎?', + 'sort_rule_details' => '排序規則詳細資訊', + 'sort_rule_details_desc' => '設定此排序規則的名稱,當使用者選取排序時,該名稱將會出現在清單中。', + 'sort_rule_operations' => '排序選項', + 'sort_rule_operations_desc' => '設定要執行的排序動作,方法是從可用的操作清單中移動它們。使用時,操作將依從上到下的順序套用。儲存時,在此處所做的任何變更都會套用至所有指定的書籍。', + 'sort_rule_available_operations' => '可用操作', + 'sort_rule_available_operations_empty' => '無剩餘操作', + 'sort_rule_configured_operations' => '已設定的操作', + 'sort_rule_configured_operations_empty' => '從「可用操作」清單中拖曳/新增操作', + 'sort_rule_op_asc' => '(遞增)', + 'sort_rule_op_desc' => '(遞減)', + 'sort_rule_op_name' => '名稱 - 按字母順序排列', + 'sort_rule_op_name_numeric' => '名稱 - 數字', + 'sort_rule_op_created_date' => '建立日期', + 'sort_rule_op_updated_date' => '更新日期', + 'sort_rule_op_chapters_first' => '第一章', + 'sort_rule_op_chapters_last' => '最後一章', + // Maintenance settings 'maint' => '維護', 'maint_image_cleanup' => '清理圖片', @@ -92,9 +122,9 @@ 'maint_send_test_email_mail_text' => '恭喜!您收到這封電子郵件通知時,代表您的電子郵件設定已正確設定。', 'maint_recycle_bin_desc' => '刪除的書架、書本、章節與頁面將會被傳送到回收桶,這樣仍可以還原或永久刪除。回收桶中較舊的項目可能會在一段時間後自動移除,取決於您的系統設定。', 'maint_recycle_bin_open' => '開啟回收桶', - 'maint_regen_references' => 'Regenerate References', - 'maint_regen_references_desc' => 'This action will rebuild the cross-item reference index within the database. This is usually handled automatically but this action can be useful to index old content or content added via unofficial methods.', - 'maint_regen_references_success' => 'Reference index has been regenerated!', + 'maint_regen_references' => '重新生成引用', + 'maint_regen_references_desc' => '此操作將重建數據庫中的跨項目引用索引。這通常是自動處理的,但可能有助於索引舊內容或通過非官方方法添加的內容。', + 'maint_regen_references_success' => '引用索引已重新生成!', 'maint_timeout_command_note' => '備註:這項操作需要較長的時間,可能導致多數的網路環境發生連線逾時的問題。若有需要,可以透過終端機指令來替代。', // Recycle Bin @@ -109,7 +139,7 @@ 'recycle_bin_contents_empty' => '回收桶目前是空的', 'recycle_bin_empty' => '清空回收桶', 'recycle_bin_empty_confirm' => '這將會永久破壞回收桶中的所有項目,包括每個項目中包含的內容。您確定您想要清空回收桶嗎?', - 'recycle_bin_destroy_confirm' => '此動作將會從系統中永久移除此項目以及下方列出的所有下層元素,您將無法還原此內容。您確定您想要永久刪除此項目嗎?', + 'recycle_bin_destroy_confirm' => '此操作將從系統中永久刪除此項目以及下面列出的所有子元素,並且您將無法還原此內容。您確定要永久刪除該項目嗎?', 'recycle_bin_destroy_list' => '要被銷毀的項目', 'recycle_bin_restore_list' => '要被還原的項目', 'recycle_bin_restore_confirm' => '此動作將會還原已被刪除的項目(包含任何下層元素)到其原始位置。如果原始位置已被刪除,且目前位於垃圾桶裡,那麼上層項目也需要被還原。', @@ -136,11 +166,12 @@ // Role Settings 'roles' => '角色', 'role_user_roles' => '使用者角色', - 'roles_index_desc' => 'Roles are used to group users & provide system permission to their members. When a user is a member of multiple roles the privileges granted will stack and the user will inherit all abilities.', - 'roles_x_users_assigned' => ':count user assigned|:count users assigned', - 'roles_x_permissions_provided' => ':count permission|:count permissions', - 'roles_assigned_users' => 'Assigned Users', - 'roles_permissions_provided' => 'Provided Permissions', + 'roles_index_desc' => '「角色」用於將系統權限套用至使用者群組。當使用者擁有多角色時, +使用者會自動繼承角色中的所有系統權限', + 'roles_x_users_assigned' => ':count 位用戶已分配|:count 位用戶已分配', + 'roles_x_permissions_provided' => ':count 個權限|:count 個權限', + 'roles_assigned_users' => '已分配用戶', + 'roles_permissions_provided' => '已提供權限', 'role_create' => '建立新角色', 'role_delete' => '刪除角色', 'role_delete_confirm' => '這將會刪除名為「:roleName」的角色.', @@ -162,13 +193,14 @@ 'role_access_api' => '存取系統 API', 'role_manage_settings' => '管理應用程式設定', 'role_export_content' => '匯出內容', + 'role_import_content' => '匯入內容', 'role_editor_change' => '重設頁面編輯器', - 'role_notifications' => 'Receive & manage notifications', + 'role_notifications' => '管理和接收通知', 'role_asset' => '資源權限', 'roles_system_warning' => '請注意,有上述三項權限中的任一項的使用者都可以更改自己或系統中其他人的權限。有這些權限的角色只應分配給受信任的使用者。', 'role_asset_desc' => '對系統內資源的預設權限將由這裡的權限控制。若有單獨設定在書本、章節和頁面上的權限,將會覆寫這裡的權限設定。', 'role_asset_admins' => '管理員會自動取得對所有內容的存取權,但這些選項可能會顯示或隱藏使用者介面的選項。', - 'role_asset_image_view_note' => 'This relates to visibility within the image manager. Actual access of uploaded image files will be dependant upon system image storage option.', + 'role_asset_image_view_note' => '這與圖像管理器中的可見性有關。已經上傳的圖片的實際訪問取決於系統圖像存儲選項。', 'role_all' => '全部', 'role_own' => '擁有', 'role_controlled_by_asset' => '依據隸屬的資源來決定', @@ -193,8 +225,8 @@ 'users_send_invite_text' => '您可以選擇向此使用者傳送邀請電子郵件,讓他們可以設定自己的密碼,您也可以自行設定他們的密碼。', 'users_send_invite_option' => '傳送邀請電子郵件給使用者', 'users_external_auth_id' => '外部身份驗證 ID', - 'users_external_auth_id_desc' => 'When an external authentication system is in use (such as SAML2, OIDC or LDAP) this is the ID which links this BookStack user to the authentication system account. You can ignore this field if using the default email-based authentication.', - 'users_password_warning' => 'Only fill the below if you would like to change the password for this user.', + 'users_external_auth_id_desc' => '使用外部驗證系統時 (如 SAML2、OIDC、LDAP),將使這個帳號與驗證系統帳號連結。若使用一般的 email 認證方式,可以忽略此欄位。', + 'users_password_warning' => '如果您想更改此用戶的密碼,請填寫以下內容:', 'users_system_public' => '此使用者代表造訪您站台的任何訪客使用者。其不能用於登入,而會自動分配。', 'users_delete' => '刪除使用者', 'users_delete_named' => '刪除使用者 :userName', @@ -210,23 +242,23 @@ 'users_preferred_language' => '偏好語言', 'users_preferred_language_desc' => '此選項將會變更用於應用程式使用者介面的語言。不會影響任何使用者建立的內容。', 'users_social_accounts' => '社群網站帳號', - 'users_social_accounts_desc' => 'View the status of the connected social accounts for this user. Social accounts can be used in addition to the primary authentication system for system access.', + 'users_social_accounts_desc' => '查看此用戶已連接的社交賬戶狀態。 除了主要認證系統外,社交賬戶也可用於系統訪問。', 'users_social_accounts_info' => '您可以在此處連結您其他的帳號以供快速登入。從此處取消連結帳號並不會撤銷先前已授權的存取。請從您連結的社群網站帳號的個人設定中撤銷存取權。', 'users_social_connect' => '連結帳號', 'users_social_disconnect' => '取消連結帳號', - 'users_social_status_connected' => 'Connected', - 'users_social_status_disconnected' => 'Disconnected', + 'users_social_status_connected' => '已連接', + 'users_social_status_disconnected' => '已斷開連接', 'users_social_connected' => ':socialAccount 帳號已經成功連結到您的個人資料。', 'users_social_disconnected' => ':socialAccount 帳號已經成功取消連結。', 'users_api_tokens' => 'API 權杖', - 'users_api_tokens_desc' => 'Create and manage the access tokens used to authenticate with the BookStack REST API. Permissions for the API are managed via the user that the token belongs to.', + 'users_api_tokens_desc' => '創建和管理用於 BookStack REST API 認證的訪問令牌。 API 的權限是通過令牌所屬的用戶管理的。', 'users_api_tokens_none' => '尚未為此使用者建立 API 權杖', 'users_api_tokens_create' => '建立權杖', 'users_api_tokens_expires' => '過期', 'users_api_tokens_docs' => 'API 文件', 'users_mfa' => '多重身分驗證', 'users_mfa_desc' => '設定多重身份驗證為您的帳戶多增加了一道防線', - 'users_mfa_x_methods' => ':count method configured|:count methods configured', + 'users_mfa_x_methods' => ':count 個措施已配置|:count 個措施已配置', 'users_mfa_configure' => '方式設置', // API Tokens @@ -249,33 +281,41 @@ // Webhooks 'webhooks' => 'Webhooks', - 'webhooks_index_desc' => 'Webhooks are a way to send data to external URLs when certain actions and events occur within the system which allows event-based integration with external platforms such as messaging or notification systems.', - 'webhooks_x_trigger_events' => ':count trigger event|:count trigger events', + 'webhooks_index_desc' => 'Webhook 是一種在系統內發生某些操作和事件時將數據發送到外部 URL 的方法,它允許與外部平台(例如消息傳遞或通知系統)進行基於事件的集成。', + 'webhooks_x_trigger_events' => ':count 個觸發事件 |:count 個觸發事件', 'webhooks_create' => '建立 Webhook', 'webhooks_none_created' => '沒有已建立的 Webhook', 'webhooks_edit' => '設置 Webhook', 'webhooks_save' => '儲存 Webhook', 'webhooks_details' => 'WebHook 詳細資料', - 'webhooks_details_desc' => 'Provide a user friendly name and a POST endpoint as a location for the webhook data to be sent to.', - 'webhooks_events' => 'Webhook Events', - 'webhooks_events_desc' => 'Select all the events that should trigger this webhook to be called.', - 'webhooks_events_warning' => 'Keep in mind that these events will be triggered for all selected events, even if custom permissions are applied. Ensure that use of this webhook won\'t expose confidential content.', + 'webhooks_details_desc' => '提供一個用戶友好的名稱和一個 POST Endpoint 作為 Webhook 數據發送的位置。', + 'webhooks_events' => 'Webhook 事件', + 'webhooks_events_desc' => '選擇所有應觸發此 Webhook 的事件。', + 'webhooks_events_warning' => '請記住,即使應用了自定義權限,所有選定的事件也仍然會被觸發。 確保使用此 Webhook 不會洩露機密內容。', 'webhooks_events_all' => '全部系統活動', 'webhooks_name' => 'Webhook 名稱', - 'webhooks_timeout' => 'Webhook Request Timeout (Seconds)', - 'webhooks_endpoint' => 'Webhook Endpoint', - 'webhooks_active' => 'Webhook Active', + 'webhooks_timeout' => 'Webhook 請求超時(秒)', + 'webhooks_endpoint' => 'Webhook 端點', + 'webhooks_active' => 'Webhook 啟用', 'webhook_events_table_header' => '事件', 'webhooks_delete' => '刪除 Webhook', - 'webhooks_delete_warning' => 'This will fully delete this webhook, with the name \':webhookName\', from the system.', - 'webhooks_delete_confirm' => 'Are you sure you want to delete this webhook?', + 'webhooks_delete_warning' => '這將會從系統中完全刪除名為 “:webhookName” 的 webhook。', + 'webhooks_delete_confirm' => '確定要刪除此 Webhook 嗎?', 'webhooks_format_example' => 'Webhook 格式範例', - 'webhooks_format_example_desc' => 'Webhook data is sent as a POST request to the configured endpoint as JSON following the format below. The "related_item" and "url" properties are optional and will depend on the type of event triggered.', + 'webhooks_format_example_desc' => 'Webhook 數據會以 POST 請求按照以下 JSON 格式發送到設置的 Endpoint。 “related_item” 和 “url” 屬性是可選的,取決於觸發的事件類型。', 'webhooks_status' => 'Webhook 狀態', - 'webhooks_last_called' => 'Last Called:', + 'webhooks_last_called' => '最後一次調用:', 'webhooks_last_errored' => '上次錯誤', 'webhooks_last_error_message' => '上次錯誤信息', + // Licensing + 'licenses' => '授權', + 'licenses_desc' => '本頁提供 BookStack 使用到的專案以及函式庫的詳細授權資料,其中部份專案及函式庫僅開開發環境中使用。', + 'licenses_bookstack' => 'BookStack 授權', + 'licenses_php' => 'PHP 函式庫授權', + 'licenses_js' => 'JavaScript 函式庫授權', + 'licenses_other' => '其它授權', + 'license_details' => '詳細授權資料', //! If editing translations files directly please ignore this in all //! languages apart from en. Content will be auto-copied from en. @@ -287,6 +327,7 @@ 'bs' => 'Bosanski', 'ca' => '加泰隆尼亞語', 'cs' => 'Česky', + 'cy' => 'Cymraeg', 'da' => '丹麥', 'de' => 'Deutsch (Sie)', 'de_informal' => 'Deutsch (Du)', @@ -308,6 +349,7 @@ 'lt' => 'Lietuvių Kalba', 'lv' => 'Latviešu Valoda', 'nb' => 'Norsk (Bokmål)', + 'ne' => 'नेपाली', 'nn' => 'Nynorsk', 'nl' => 'Nederlands', 'pl' => 'Polski', diff --git a/lang/zh_TW/validation.php b/lang/zh_TW/validation.php index 71ba5acadf8..30031470749 100644 --- a/lang/zh_TW/validation.php +++ b/lang/zh_TW/validation.php @@ -103,7 +103,12 @@ 'totp' => '提供的代碼無效或已過期。', 'unique' => ':attribute 已經被使用。', 'url' => ':attribute 格式無效。', - 'uploaded' => '無法上傳文件, 服務器可能不接受此大小的文件。', + 'uploaded' => '無法上傳文檔案, 伺服器可能不接受此大小的檔案。', + + 'zip_file' => ':attribute 需要參照 ZIP 中的檔案。', + 'zip_file_mime' => ':attribute 需要參照類型為 :validTypes 的檔案,找到 :foundType。', + 'zip_model_expected' => '預期為資料物件,但找到「:type」。', + 'zip_unique' => '對於 ZIP 中的物件類型,:attribute 必須是唯一的。', // Custom validation lines 'custom' => [ diff --git a/package-lock.json b/package-lock.json index 99dbf1da8ac..fb6a1d4fa01 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,95 +5,620 @@ "packages": { "": { "dependencies": { - "@codemirror/commands": "^6.3.2", - "@codemirror/lang-css": "^6.2.1", - "@codemirror/lang-html": "^6.4.7", - "@codemirror/lang-javascript": "^6.2.1", - "@codemirror/lang-json": "^6.0.1", - "@codemirror/lang-markdown": "^6.2.3", - "@codemirror/lang-php": "^6.0.1", - "@codemirror/lang-xml": "^6.0.2", - "@codemirror/language": "^6.9.3", - "@codemirror/legacy-modes": "^6.3.3", - "@codemirror/state": "^6.3.3", - "@codemirror/theme-one-dark": "^6.1.2", - "@codemirror/view": "^6.22.2", - "@lezer/highlight": "^1.2.0", + "@codemirror/commands": "^6.8.1", + "@codemirror/lang-css": "^6.3.1", + "@codemirror/lang-html": "^6.4.9", + "@codemirror/lang-javascript": "^6.2.4", + "@codemirror/lang-json": "^6.0.2", + "@codemirror/lang-markdown": "^6.3.4", + "@codemirror/lang-php": "^6.0.2", + "@codemirror/lang-xml": "^6.1.0", + "@codemirror/language": "^6.11.3", + "@codemirror/legacy-modes": "^6.5.1", + "@codemirror/state": "^6.5.2", + "@codemirror/theme-one-dark": "^6.1.3", + "@codemirror/view": "^6.38.1", + "@lezer/highlight": "^1.2.1", "@ssddanbrown/codemirror-lang-smarty": "^1.0.0", "@ssddanbrown/codemirror-lang-twig": "^1.0.0", - "codemirror": "^6.0.1", - "idb-keyval": "^6.2.1", - "markdown-it": "^13.0.2", + "@types/jest": "^30.0.0", + "codemirror": "^6.0.2", + "idb-keyval": "^6.2.2", + "markdown-it": "^14.1.0", "markdown-it-task-lists": "^2.1.1", - "snabbdom": "^3.5.1", - "sortablejs": "^1.15.1" + "snabbdom": "^3.6.2", + "sortablejs": "^1.15.6" }, "devDependencies": { - "@lezer/generator": "^1.5.1", + "@eslint/js": "^9.34.0", + "@lezer/generator": "^1.8.0", + "@types/markdown-it": "^14.1.2", + "@types/sortablejs": "^1.15.8", "chokidar-cli": "^3.0", - "esbuild": "^0.19", - "eslint": "^8.55.0", - "eslint-config-airbnb-base": "^15.0.0", - "eslint-plugin-import": "^2.29.0", - "livereload": "^0.9.3", + "esbuild": "^0.25.9", + "eslint": "^9.34.0", + "eslint-plugin-import": "^2.32.0", + "jest": "^30.1.1", + "jest-environment-jsdom": "^30.1.1", + "livereload": "^0.10.3", "npm-run-all": "^4.1.5", - "punycode": "^2.3.1", - "sass": "^1.69.5" + "sass": "^1.91.0", + "ts-jest": "^29.4.1", + "ts-node": "^10.9.2", + "typescript": "5.9.*" } }, - "node_modules/@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, "engines": { - "node": ">=0.10.0" + "node": ">=6.0.0" } }, - "node_modules/@codemirror/autocomplete": { - "version": "6.10.2", - "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.10.2.tgz", - "integrity": "sha512-3dCL7b0j2GdtZzWN5j7HDpRAJ26ip07R4NGYz7QYthIYMiX8I4E4TNrYcdTayPJGeVQtd/xe7lWU4XL7THFb/w==", + "node_modules/@asamuzakjp/css-color": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz", + "integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==", + "dev": true, + "license": "MIT", "dependencies": { - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.17.0", - "@lezer/common": "^1.0.0" + "@csstools/css-calc": "^2.1.3", + "@csstools/css-color-parser": "^3.0.9", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "lru-cache": "^10.4.3" + } + }, + "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz", + "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.0.tgz", + "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.0", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.27.3", + "@babel/helpers": "^7.27.6", + "@babel/parser": "^7.28.0", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.0", + "@babel/types": "^7.28.0", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.0.tgz", + "integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.0", + "@babel/types": "^7.28.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", + "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.6.tgz", + "integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.27.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", + "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", + "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", + "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", + "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.0.tgz", + "integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.0", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", + "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@codemirror/autocomplete": { + "version": "6.18.6", + "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.18.6.tgz", + "integrity": "sha512-PHHBXFomUs5DF+9tCOM/UoW6XQ4R44lLNNhRaW9PKPTU0D7lIjRg3ElxaJnTwsl/oHiR93WSXDBrekhoUGCPtg==", + "license": "MIT", + "dependencies": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", + "@codemirror/view": "^6.17.0", "@lezer/common": "^1.0.0" } }, "node_modules/@codemirror/commands": { - "version": "6.3.2", - "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.3.2.tgz", - "integrity": "sha512-tjoi4MCWDNxgIpoLZ7+tezdS9OEB6pkiDKhfKx9ReJ/XBcs2G2RXIu+/FxXBlWsPTsz6C9q/r4gjzrsxpcnqCQ==", + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.8.1.tgz", + "integrity": "sha512-KlGVYufHMQzxbdQONiLyGQDUW0itrLZwq3CcY7xpv9ZLRHqzkBSoteocBHtMCoY7/Ci4xhzSrToIeLg7FxHuaw==", + "license": "MIT", "dependencies": { "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.2.0", - "@codemirror/view": "^6.0.0", + "@codemirror/state": "^6.4.0", + "@codemirror/view": "^6.27.0", "@lezer/common": "^1.1.0" } }, "node_modules/@codemirror/lang-css": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@codemirror/lang-css/-/lang-css-6.2.1.tgz", - "integrity": "sha512-/UNWDNV5Viwi/1lpr/dIXJNWiwDxpw13I4pTUAsNxZdg6E0mI2kTQb0P2iHczg1Tu+H4EBgJR+hYhKiHKko7qg==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/@codemirror/lang-css/-/lang-css-6.3.1.tgz", + "integrity": "sha512-kr5fwBGiGtmz6l0LSJIbno9QrifNMUusivHbnA1H6Dmqy4HZFte3UAICix1VuKo0lMPKQr2rqB+0BkKi/S3Ejg==", + "license": "MIT", "dependencies": { "@codemirror/autocomplete": "^6.0.0", "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.0.0", "@lezer/common": "^1.0.2", - "@lezer/css": "^1.0.0" + "@lezer/css": "^1.1.7" } }, "node_modules/@codemirror/lang-html": { - "version": "6.4.7", - "resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.7.tgz", - "integrity": "sha512-y9hWSSO41XlcL4uYwWyk0lEgTHcelWWfRuqmvcAmxfCs0HNWZdriWo/EU43S63SxEZpc1Hd50Itw7ktfQvfkUg==", + "version": "6.4.9", + "resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.9.tgz", + "integrity": "sha512-aQv37pIMSlueybId/2PVSP6NPnmurFDVmZwzc7jszd2KAF8qd4VBbvNYPXWQq90WIARjsdVkPbw29pszmHws3Q==", + "license": "MIT", "dependencies": { "@codemirror/autocomplete": "^6.0.0", "@codemirror/lang-css": "^6.0.0", @@ -107,9 +632,10 @@ } }, "node_modules/@codemirror/lang-javascript": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.1.tgz", - "integrity": "sha512-jlFOXTejVyiQCW3EQwvKH0m99bUYIw40oPmFjSX2VS78yzfe0HELZ+NEo9Yfo1MkGRpGlj3Gnu4rdxV1EnAs5A==", + "version": "6.2.4", + "resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.4.tgz", + "integrity": "sha512-0WVmhp1QOqZ4Rt6GlVGwKJN3KW7Xh4H2q8ZZNGZaP6lRdxXJzmjm4FqvmOojVj6khWJHIb9sp7U/72W7xQgqAA==", + "license": "MIT", "dependencies": { "@codemirror/autocomplete": "^6.0.0", "@codemirror/language": "^6.6.0", @@ -121,32 +647,35 @@ } }, "node_modules/@codemirror/lang-json": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.1.tgz", - "integrity": "sha512-+T1flHdgpqDDlJZ2Lkil/rLiRy684WMLc74xUnjJH48GQdfJo/pudlTRreZmKwzP8/tGdKf83wlbAdOCzlJOGQ==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.2.tgz", + "integrity": "sha512-x2OtO+AvwEHrEwR0FyyPtfDUiloG3rnVTSZV1W8UteaLL8/MajQd8DpvUb2YVzC+/T18aSDv0H9mu+xw0EStoQ==", + "license": "MIT", "dependencies": { "@codemirror/language": "^6.0.0", "@lezer/json": "^1.0.0" } }, "node_modules/@codemirror/lang-markdown": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/@codemirror/lang-markdown/-/lang-markdown-6.2.3.tgz", - "integrity": "sha512-wCewRLWpdefWi7uVkHIDiE8+45Fe4buvMDZkihqEom5uRUQrl76Zb13emjeK3W+8pcRgRfAmwelURBbxNEKCIg==", + "version": "6.3.4", + "resolved": "https://registry.npmjs.org/@codemirror/lang-markdown/-/lang-markdown-6.3.4.tgz", + "integrity": "sha512-fBm0BO03azXnTAsxhONDYHi/qWSI+uSEIpzKM7h/bkIc9fHnFp9y7KTMXKON0teNT97pFhc1a9DQTtWBYEZ7ug==", + "license": "MIT", "dependencies": { "@codemirror/autocomplete": "^6.7.1", "@codemirror/lang-html": "^6.0.0", "@codemirror/language": "^6.3.0", "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.0.0", - "@lezer/common": "^1.0.0", + "@lezer/common": "^1.2.1", "@lezer/markdown": "^1.0.0" } }, "node_modules/@codemirror/lang-php": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@codemirror/lang-php/-/lang-php-6.0.1.tgz", - "integrity": "sha512-ublojMdw/PNWa7qdN5TMsjmqkNuTBD3k6ndZ4Z0S25SBAiweFGyY68AS3xNcIOlb6DDFDvKlinLQ40vSLqf8xA==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@codemirror/lang-php/-/lang-php-6.0.2.tgz", + "integrity": "sha512-ZKy2v1n8Fc8oEXj0Th0PUMXzQJ0AIR6TaZU+PbDHExFwdu+guzOA4jmCHS1Nz4vbFezwD7LyBdDnddSJeScMCA==", + "license": "MIT", "dependencies": { "@codemirror/lang-html": "^6.0.0", "@codemirror/language": "^6.0.0", @@ -156,24 +685,27 @@ } }, "node_modules/@codemirror/lang-xml": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@codemirror/lang-xml/-/lang-xml-6.0.2.tgz", - "integrity": "sha512-JQYZjHL2LAfpiZI2/qZ/qzDuSqmGKMwyApYmEUUCTxLM4MWS7sATUEfIguZQr9Zjx/7gcdnewb039smF6nC2zw==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@codemirror/lang-xml/-/lang-xml-6.1.0.tgz", + "integrity": "sha512-3z0blhicHLfwi2UgkZYRPioSgVTo9PV5GP5ducFH6FaHy0IAJRg+ixj5gTR1gnT/glAIC8xv4w2VL1LoZfs+Jg==", + "license": "MIT", "dependencies": { "@codemirror/autocomplete": "^6.0.0", "@codemirror/language": "^6.4.0", "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", "@lezer/common": "^1.0.0", "@lezer/xml": "^1.0.0" } }, "node_modules/@codemirror/language": { - "version": "6.9.3", - "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.9.3.tgz", - "integrity": "sha512-qq48pYzoi6ldYWV/52+Z9Ou6QouVI+8YwvxFbUypI33NbjG2UeRHKENRyhwljTTiOqjQ33FjyZj6EREQ9apAOQ==", + "version": "6.11.3", + "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.11.3.tgz", + "integrity": "sha512-9HBM2XnwDj7fnu0551HkGdrUrrqmYq/WC5iv6nbY2WdicXdGbhR/gfbZOH73Aqj4351alY1+aoG9rCNfiwS1RA==", + "license": "MIT", "dependencies": { "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", + "@codemirror/view": "^6.23.0", "@lezer/common": "^1.1.0", "@lezer/highlight": "^1.0.0", "@lezer/lr": "^1.0.0", @@ -181,27 +713,30 @@ } }, "node_modules/@codemirror/legacy-modes": { - "version": "6.3.3", - "resolved": "https://registry.npmjs.org/@codemirror/legacy-modes/-/legacy-modes-6.3.3.tgz", - "integrity": "sha512-X0Z48odJ0KIoh/HY8Ltz75/4tDYc9msQf1E/2trlxFaFFhgjpVHjZ/BCXe1Lk7s4Gd67LL/CeEEHNI+xHOiESg==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@codemirror/legacy-modes/-/legacy-modes-6.5.1.tgz", + "integrity": "sha512-DJYQQ00N1/KdESpZV7jg9hafof/iBNp9h7TYo1SLMk86TWl9uDsVdho2dzd81K+v4retmK6mdC7WpuOQDytQqw==", + "license": "MIT", "dependencies": { "@codemirror/language": "^6.0.0" } }, "node_modules/@codemirror/lint": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.4.2.tgz", - "integrity": "sha512-wzRkluWb1ptPKdzlsrbwwjYCPLgzU6N88YBAmlZi8WFyuiEduSd05MnJYNogzyc8rPK7pj6m95ptUApc8sHKVA==", + "version": "6.8.5", + "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.8.5.tgz", + "integrity": "sha512-s3n3KisH7dx3vsoeGMxsbRAgKe4O1vbrnKBClm99PU0fWxmxsx5rR2PfqQgIt+2MMJBHbiJ5rfIdLYfB9NNvsA==", + "license": "MIT", "dependencies": { "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", + "@codemirror/view": "^6.35.0", "crelt": "^1.0.5" } }, "node_modules/@codemirror/search": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.4.tgz", - "integrity": "sha512-YoTrvjv9e8EbPs58opjZKyJ3ewFrVSUzQ/4WXlULQLSDDr1nGPJ67mMXFNNVYwdFhybzhrzrtqgHmtpJwIF+8g==", + "version": "6.5.11", + "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.11.tgz", + "integrity": "sha512-KmWepDE6jUdL6n8cAAqIpRmLPBZ5ZKnicE8oGU/s3QrAVID+0VhLFrzUucVKHG5035/BSykhExDL/Xm7dHthiA==", + "license": "MIT", "dependencies": { "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.0.0", @@ -209,14 +744,19 @@ } }, "node_modules/@codemirror/state": { - "version": "6.3.3", - "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.3.3.tgz", - "integrity": "sha512-0wufKcTw2dEwEaADajjHf6hBy1sh3M6V0e+q4JKIhLuiMSe5td5HOWpUdvKth1fT1M9VYOboajoBHpkCd7PG7A==" + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.2.tgz", + "integrity": "sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==", + "license": "MIT", + "dependencies": { + "@marijn/find-cluster-break": "^1.0.0" + } }, "node_modules/@codemirror/theme-one-dark": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-6.1.2.tgz", - "integrity": "sha512-F+sH0X16j/qFLMAfbciKTxVOwkdAS336b7AXTKOZhy8BR3eH/RelsnLgLFINrpST63mmN2OuwUt0W2ndUgYwUA==", + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-6.1.3.tgz", + "integrity": "sha512-NzBdIvEJmx6fjeremiGp3t/okrLPYT0d9orIc7AFun8oZcRk58aejkqhv6spnz4MLAevrKNPMQYXEWMg4s+sKA==", + "license": "MIT", "dependencies": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.0.0", @@ -225,401 +765,723 @@ } }, "node_modules/@codemirror/view": { - "version": "6.22.2", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.22.2.tgz", - "integrity": "sha512-cJp64cPXm7QfSBWEXK+76+hsZCGHupUgy8JAbSzMG6Lr0rfK73c1CaWITVW6hZVkOnAFxJTxd0PIuynNbzxYPw==", + "version": "6.38.1", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.38.1.tgz", + "integrity": "sha512-RmTOkE7hRU3OVREqFVITWHz6ocgBjv08GoePscAakgVQfciA3SGCEk7mb9IzwW61cKKmlTpHXG6DUE5Ubx+MGQ==", + "license": "MIT", "dependencies": { - "@codemirror/state": "^6.1.4", + "@codemirror/state": "^6.5.0", + "crelt": "^1.0.6", "style-mod": "^4.1.0", "w3c-keyname": "^2.2.4" } }, - "node_modules/@esbuild/android-arm": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.5.tgz", - "integrity": "sha512-bhvbzWFF3CwMs5tbjf3ObfGqbl/17ict2/uwOSfr3wmxDE6VdS2GqY/FuzIPe0q0bdhj65zQsvqfArI9MY6+AA==", - "cpu": [ - "arm" - ], + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", "dev": true, - "optional": true, - "os": [ - "android" - ], + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, "engines": { "node": ">=12" } }, - "node_modules/@esbuild/android-arm64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.5.tgz", - "integrity": "sha512-5d1OkoJxnYQfmC+Zd8NBFjkhyCNYwM4n9ODrycTFY6Jk1IGiZ+tjVJDDSwDt77nK+tfpGP4T50iMtVi4dEGzhQ==", + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@csstools/color-helpers": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz", + "integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/css-calc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz", + "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz", + "integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^5.1.0", + "@csstools/css-calc": "^2.1.4" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", + "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", + "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@emnapi/core": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.5.0.tgz", + "integrity": "sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.1.0", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.5.0.tgz", + "integrity": "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", + "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz", + "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz", + "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz", + "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-x64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.5.tgz", - "integrity": "sha512-9t+28jHGL7uBdkBjL90QFxe7DVA+KGqWlHCF8ChTKyaKO//VLuoBricQCgwhOjA1/qOczsw843Fy4cbs4H3DVA==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz", + "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.5.tgz", - "integrity": "sha512-mvXGcKqqIqyKoxq26qEDPHJuBYUA5KizJncKOAf9eJQez+L9O+KfvNFu6nl7SCZ/gFb2QPaRqqmG0doSWlgkqw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz", + "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.5.tgz", - "integrity": "sha512-Ly8cn6fGLNet19s0X4unjcniX24I0RqjPv+kurpXabZYSXGM4Pwpmf85WHJN3lAgB8GSth7s5A0r856S+4DyiA==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz", + "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.5.tgz", - "integrity": "sha512-GGDNnPWTmWE+DMchq1W8Sd0mUkL+APvJg3b11klSGUDvRXh70JqLAO56tubmq1s2cgpVCSKYywEiKBfju8JztQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz", + "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.5.tgz", - "integrity": "sha512-1CCwDHnSSoA0HNwdfoNY0jLfJpd7ygaLAp5EHFos3VWJCRX9DMwWODf96s9TSse39Br7oOTLryRVmBoFwXbuuQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz", + "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-arm": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.5.tgz", - "integrity": "sha512-lrWXLY/vJBzCPC51QN0HM71uWgIEpGSjSZZADQhq7DKhPcI6NH1IdzjfHkDQws2oNpJKpR13kv7/pFHBbDQDwQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz", + "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.5.tgz", - "integrity": "sha512-o3vYippBmSrjjQUCEEiTZ2l+4yC0pVJD/Dl57WfPwwlvFkrxoSO7rmBZFii6kQB3Wrn/6GwJUPLU5t52eq2meA==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz", + "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.5.tgz", - "integrity": "sha512-MkjHXS03AXAkNp1KKkhSKPOCYztRtK+KXDNkBa6P78F8Bw0ynknCSClO/ztGszILZtyO/lVKpa7MolbBZ6oJtQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz", + "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.5.tgz", - "integrity": "sha512-42GwZMm5oYOD/JHqHska3Jg0r+XFb/fdZRX+WjADm3nLWLcIsN27YKtqxzQmGNJgu0AyXg4HtcSK9HuOk3v1Dw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz", + "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==", "cpu": [ "loong64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.5.tgz", - "integrity": "sha512-kcjndCSMitUuPJobWCnwQ9lLjiLZUR3QLQmlgaBfMX23UEa7ZOrtufnRds+6WZtIS9HdTXqND4yH8NLoVVIkcg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz", + "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==", "cpu": [ "mips64el" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.5.tgz", - "integrity": "sha512-yJAxJfHVm0ZbsiljbtFFP1BQKLc8kUF6+17tjQ78QjqjAQDnhULWiTA6u0FCDmYT1oOKS9PzZ2z0aBI+Mcyj7Q==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz", + "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==", "cpu": [ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.5.tgz", - "integrity": "sha512-5u8cIR/t3gaD6ad3wNt1MNRstAZO+aNyBxu2We8X31bA8XUNyamTVQwLDA1SLoPCUehNCymhBhK3Qim1433Zag==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz", + "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==", "cpu": [ "riscv64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.5.tgz", - "integrity": "sha512-Z6JrMyEw/EmZBD/OFEFpb+gao9xJ59ATsoTNlj39jVBbXqoZm4Xntu6wVmGPB/OATi1uk/DB+yeDPv2E8PqZGw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz", + "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==", "cpu": [ "s390x" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-x64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.5.tgz", - "integrity": "sha512-psagl+2RlK1z8zWZOmVdImisMtrUxvwereIdyJTmtmHahJTKb64pAcqoPlx6CewPdvGvUKe2Jw+0Z/0qhSbG1A==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz", + "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz", + "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.5.tgz", - "integrity": "sha512-kL2l+xScnAy/E/3119OggX8SrWyBEcqAh8aOY1gr4gPvw76la2GlD4Ymf832UCVbmuWeTf2adkZDK+h0Z/fB4g==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz", + "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "netbsd" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz", + "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.5.tgz", - "integrity": "sha512-sPOfhtzFufQfTBgRnE1DIJjzsXukKSvZxloZbkJDG383q0awVAq600pc1nfqBcl0ice/WN9p4qLc39WhBShRTA==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz", + "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "openbsd" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz", + "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.5.tgz", - "integrity": "sha512-dGZkBXaafuKLpDSjKcB0ax0FL36YXCvJNnztjKV+6CO82tTYVDSH2lifitJ29jxRMoUhgkg9a+VA/B03WK5lcg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz", + "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "sunos" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.5.tgz", - "integrity": "sha512-dWVjD9y03ilhdRQ6Xig1NWNgfLtf2o/STKTS+eZuF90fI2BhbwD6WlaiCGKptlqXlURVB5AUOxUj09LuwKGDTg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz", + "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.5.tgz", - "integrity": "sha512-4liggWIA4oDgUxqpZwrDhmEfAH4d0iljanDOK7AnVU89T6CzHon/ony8C5LeOdfgx60x5cnQJFZwEydVlYx4iw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz", + "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-x64": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.5.tgz", - "integrity": "sha512-czTrygUsB/jlM8qEW5MD8bgYU2Xg14lo6kBDXW6HdxKjh8M5PzETGiSHaz9MtbXBYDloHNUAUW2tMiKW4KM9Mw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz", + "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", "dev": true, + "license": "MIT", "dependencies": { - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.3" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, + "funding": { + "url": "https://opencollective.com/eslint" + }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@eslint-community/regexpp": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.9.1.tgz", - "integrity": "sha512-Y27x+MBLjXa+0JWDhykM3+JE+il3kHKAEqabfEWq3SDhZjLYb6/BHL/JKFnH3fe207JaXkyDo685Oc2Glt6ifA==", + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", "dev": true, + "license": "MIT", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, + "node_modules/@eslint/config-array": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", + "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", + "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", "dev": true, + "license": "MIT", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", + "espree": "^10.0.1", + "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -627,33 +1489,85 @@ "strip-json-comments": "^3.1.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/@eslint/js": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.55.0.tgz", - "integrity": "sha512-qQfo2mxH5yVom1kacMtZZJFVdW+E70mqHMJvVg6WTLo+VBuQJ4TojZlfWBjK0ve5BdEeNAVxOsl/nvNMpJOaJA==", + "version": "9.34.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.34.0.tgz", + "integrity": "sha512-EoyvqQnBNsV1CWaEJ559rxXL4c8V92gxirbawSmVUOWXlsRxxQXl6LmCpdUblgxgSkDIqKnhzba2SjRTI/A5Rw==", "dev": true, + "license": "MIT", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.13", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", - "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", + "node_modules/@eslint/plugin-kit": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", + "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@humanwhocodes/object-schema": "^2.0.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" + "@eslint/core": "^0.15.2", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" }, "engines": { - "node": ">=10.10.0" + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, "node_modules/@humanwhocodes/module-importer": { @@ -661,6 +1575,7 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=12.22" }, @@ -669,652 +1584,3708 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", - "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", - "dev": true - }, - "node_modules/@lezer/common": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.1.0.tgz", - "integrity": "sha512-XPIN3cYDXsoJI/oDWoR2tD++juVrhgIago9xyKhZ7IhGlzdDM9QgC8D8saKNCz5pindGcznFr2HBSsEQSWnSjw==" - }, - "node_modules/@lezer/css": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.1.3.tgz", - "integrity": "sha512-SjSM4pkQnQdJDVc80LYzEaMiNy9txsFbI7HsMgeVF28NdLaAdHNtQ+kB/QqDUzRBV/75NTXjJ/R5IdC8QQGxMg==", - "dependencies": { - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0" - } - }, - "node_modules/@lezer/generator": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@lezer/generator/-/generator-1.5.1.tgz", - "integrity": "sha512-vodJv2JPwsFsiBBHE463yBhvUI9TmhIu5duF/8MH304xNS6FyWH/vTyG61pjhERm5f+VBP94co0eiN+afWcvXw==", + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", "dev": true, - "dependencies": { - "@lezer/common": "^1.0.2", - "@lezer/lr": "^1.3.0" + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" }, - "bin": { - "lezer-generator": "src/lezer-generator.cjs" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@lezer/highlight": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.0.tgz", - "integrity": "sha512-WrS5Mw51sGrpqjlh3d4/fOwpEV2Hd3YOkp9DBt4k8XZQcoTHZFB7sx030A6OcahF4J1nDQAa3jXlTVVYH50IFA==", + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", "dependencies": { - "@lezer/common": "^1.0.0" + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" } }, - "node_modules/@lezer/html": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/@lezer/html/-/html-1.3.6.tgz", - "integrity": "sha512-Kk9HJARZTc0bAnMQUqbtuhFVsB4AnteR2BFUWfZV7L/x1H0aAKz6YabrfJ2gk/BEgjh9L3hg5O4y2IDZRBdzuQ==", - "dependencies": { - "@lezer/common": "^1.0.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0" + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@lezer/javascript": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.4.8.tgz", - "integrity": "sha512-QRmw/5xrcyRLyWr3JT3KCzn2XZr5NYNqQMGsqnYy+FghbQn9DZPuj6JDkE6uSXvfMLpdapu8KBIaeoJFaR4QVw==", - "dependencies": { - "@lezer/highlight": "^1.1.3", - "@lezer/lr": "^1.3.0" - } + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" }, - "node_modules/@lezer/json": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@lezer/json/-/json-1.0.1.tgz", - "integrity": "sha512-nkVC27qiEZEjySbi6gQRuMwa2sDu2PtfjSgz0A4QF81QyRGm3kb2YRzLcOPcTEtmcwvrX/cej7mlhbwViA4WJw==", + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", "dependencies": { - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0" + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@lezer/lr": { - "version": "1.3.13", - "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.3.13.tgz", - "integrity": "sha512-RLAbau/4uSzKgIKj96mI5WUtG1qtiR0Frn0Ei9zhPj8YOkHM+1Bb8SgdVvmR/aWJCFIzjo2KFnDiRZ75Xf5NdQ==", + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", "dependencies": { - "@lezer/common": "^1.0.0" + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/@lezer/markdown": { + "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@lezer/markdown/-/markdown-1.1.0.tgz", - "integrity": "sha512-JYOI6Lkqbl83semCANkO3CKbKc0pONwinyagBufWBm+k4yhIcqfCF8B8fpEpvJLmIy7CAfwiq7dQ/PzUZA340g==", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "license": "ISC", "dependencies": { - "@lezer/common": "^1.0.0", - "@lezer/highlight": "^1.0.0" + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/@lezer/php": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@lezer/php/-/php-1.0.1.tgz", - "integrity": "sha512-aqdCQJOXJ66De22vzdwnuC502hIaG9EnPK2rSi+ebXyUd+j7GAX1mRjWZOVOmf3GST1YUfUCu6WXDiEgDGOVwA==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", "dependencies": { - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.1.0" + "sprintf-js": "~1.0.2" } }, - "node_modules/@lezer/xml": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@lezer/xml/-/xml-1.0.2.tgz", - "integrity": "sha512-dlngsWceOtQBMuBPw5wtHpaxdPJ71aVntqjbpGkFtWsp4WtQmCnuTjQGocviymydN6M18fhj6UQX3oiEtSuY7w==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", "dependencies": { - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", "dev": true, + "license": "MIT", "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" + "argparse": "^1.0.7", + "esprima": "^4.0.0" }, - "engines": { - "node": ">= 8" + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, "engines": { - "node": ">= 8" + "node": ">=8" } }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, + "license": "MIT", "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" + "p-try": "^2.0.0" }, "engines": { - "node": ">= 8" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@ssddanbrown/codemirror-lang-smarty": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@ssddanbrown/codemirror-lang-smarty/-/codemirror-lang-smarty-1.0.0.tgz", - "integrity": "sha512-F0ut1kmdbT3eORk3xVIKfQsGCZiQdh+6sLayBa0+FTex2gyIQlVQZRRA7bPSlchI3uZtWwNnqGNz5O/QLWRlFg==" - }, - "node_modules/@ssddanbrown/codemirror-lang-twig": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@ssddanbrown/codemirror-lang-twig/-/codemirror-lang-twig-1.0.0.tgz", - "integrity": "sha512-7WIMIh8Ssc54TooGCY57WU2rKEqZZrcV2tZSVRPtd0gKYsrDEKCSLWpQjUWEx7bdgh3NKHUjq1O4ugIzI/+dwQ==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", "dependencies": { - "@codemirror/language": "^6.0.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0" + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "dev": true - }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dev": true - }, - "node_modules/acorn": { - "version": "8.11.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", - "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, - "bin": { - "acorn": "bin/acorn" - }, + "license": "MIT", "engines": { - "node": ">=0.4.0" + "node": ">=8" } }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "30.1.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.1.1.tgz", + "integrity": "sha512-f7TGqR1k4GtN5pyFrKmq+ZVndesiwLU33yDpJIGMS9aW+j6hKjue7ljeAdznBsH9kAnxUWe2Y+Y3fLV/FJt3gA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.0.5", + "@types/node": "*", + "chalk": "^4.1.2", + "jest-message-util": "30.1.0", + "jest-util": "30.0.5", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/core": { + "version": "30.1.1", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.1.1.tgz", + "integrity": "sha512-3ncU9peZ3D2VdgRkdZtUceTrDgX5yiDRwAFjtxNfU22IiZrpVWlv/FogzDLYSJQptQGfFo3PcHK86a2oG6WUGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "30.1.1", + "@jest/pattern": "30.0.1", + "@jest/reporters": "30.1.1", + "@jest/test-result": "30.1.1", + "@jest/transform": "30.1.1", + "@jest/types": "30.0.5", + "@types/node": "*", + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "exit-x": "^0.2.2", + "graceful-fs": "^4.2.11", + "jest-changed-files": "30.0.5", + "jest-config": "30.1.1", + "jest-haste-map": "30.1.0", + "jest-message-util": "30.1.0", + "jest-regex-util": "30.0.1", + "jest-resolve": "30.1.0", + "jest-resolve-dependencies": "30.1.1", + "jest-runner": "30.1.1", + "jest-runtime": "30.1.1", + "jest-snapshot": "30.1.1", + "jest-util": "30.0.5", + "jest-validate": "30.1.0", + "jest-watcher": "30.1.1", + "micromatch": "^4.0.8", + "pretty-format": "30.0.5", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "node_modules/@jest/diff-sequences": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.0.1.tgz", + "integrity": "sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==", + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/environment": { + "version": "30.1.1", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.1.1.tgz", + "integrity": "sha512-yWHbU+3j7ehQE+NRpnxRvHvpUhoohIjMePBbIr8lfe0cWVb0WeTf80DNux1GPJa18CDHiIU5DtksGUfxcDE+Rw==", "dev": true, + "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "@jest/fake-timers": "30.1.1", + "@jest/types": "30.0.5", + "@types/node": "*", + "jest-mock": "30.0.5" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/@jest/environment-jsdom-abstract": { + "version": "30.1.1", + "resolved": "https://registry.npmjs.org/@jest/environment-jsdom-abstract/-/environment-jsdom-abstract-30.1.1.tgz", + "integrity": "sha512-d7pP9SeIOI6qnrNIS/ds1hlS9jpqh8EywHK0dALSLODZKo2QEGnDNvnPvhRKI0FHWDnE2EMl8CDTP0jM9lhlOA==", "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.1.1", + "@jest/fake-timers": "30.1.1", + "@jest/types": "30.0.5", + "@types/jsdom": "^21.1.7", + "@types/node": "*", + "jest-mock": "30.0.5", + "jest-util": "30.0.5" + }, "engines": { - "node": ">=8" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "canvas": "^3.0.0", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } } }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@jest/expect": { + "version": "30.1.1", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.1.1.tgz", + "integrity": "sha512-3vHIHsF+qd3D8FU2c7U5l3rg1fhDwAYcGyHyZAi94YIlTwcJ+boNhRyJf373cl4wxbOX+0Q7dF40RTrTFTSuig==", "dev": true, + "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "expect": "30.1.1", + "jest-snapshot": "30.1.1" }, "engines": { - "node": ">=8" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "30.1.1", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.1.1.tgz", + "integrity": "sha512-5YUHr27fpJ64dnvtu+tt11ewATynrHkGYD+uSFgRr8V2eFJis/vEXgToyLwccIwqBihVfz9jwio+Zr1ab1Zihw==", + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "node_modules/@jest/fake-timers": { + "version": "30.1.1", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.1.1.tgz", + "integrity": "sha512-fK/25dNgBNYPw3eLi2CRs57g1H04qBAFNMsUY3IRzkfx/m4THe0E1zF+yGQBOMKKc2XQVdc9EYbJ4hEm7/2UtA==", "dev": true, + "license": "MIT", "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" + "@jest/types": "30.0.5", + "@sinonjs/fake-timers": "^13.0.0", + "@types/node": "*", + "jest-message-util": "30.1.0", + "jest-mock": "30.0.5", + "jest-util": "30.0.5" }, "engines": { - "node": ">= 8" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + "node_modules/@jest/get-type": { + "version": "30.1.0", + "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz", + "integrity": "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==", + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", - "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "node_modules/@jest/globals": { + "version": "30.1.1", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.1.1.tgz", + "integrity": "sha512-NNUUkHT2TU/xztZl6r1UXvJL+zvCwmZsQDmK69fVHHcB9fBtlu3FInnzOve/ZoyKnWY8JXWJNT+Lkmu1+ubXUA==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "is-array-buffer": "^3.0.1" + "@jest/environment": "30.1.1", + "@jest/expect": "30.1.1", + "@jest/types": "30.0.5", + "jest-mock": "30.0.5" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/array-includes": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz", - "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==", - "dev": true, + "node_modules/@jest/pattern": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.0.1.tgz", + "integrity": "sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "is-string": "^1.0.7" + "@types/node": "*", + "jest-regex-util": "30.0.1" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/array.prototype.findlastindex": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz", - "integrity": "sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA==", + "node_modules/@jest/reporters": { + "version": "30.1.1", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-30.1.1.tgz", + "integrity": "sha512-Hb2Bq80kahOC6Sv2waEaH1rEU6VdFcM6WHaRBWQF9tf30+nJHxhl/Upbgo9+25f0mOgbphxvbwSMjSgy9gW/FA==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0", - "get-intrinsic": "^1.2.1" + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "30.1.1", + "@jest/test-result": "30.1.1", + "@jest/transform": "30.1.1", + "@jest/types": "30.0.5", + "@jridgewell/trace-mapping": "^0.3.25", + "@types/node": "*", + "chalk": "^4.1.2", + "collect-v8-coverage": "^1.0.2", + "exit-x": "^0.2.2", + "glob": "^10.3.10", + "graceful-fs": "^4.2.11", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^5.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "30.1.0", + "jest-util": "30.0.5", + "jest-worker": "30.1.0", + "slash": "^3.0.0", + "string-length": "^4.0.2", + "v8-to-istanbul": "^9.0.1" }, "engines": { - "node": ">= 0.4" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "node_modules/array.prototype.flat": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", - "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", - "dev": true, + "node_modules/@jest/schemas": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", + "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" + "@sinclair/typebox": "^0.34.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/array.prototype.flatmap": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", - "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", + "node_modules/@jest/snapshot-utils": { + "version": "30.1.1", + "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.1.1.tgz", + "integrity": "sha512-TkVBc9wuN22TT8hESRFmjjg/xIMu7z0J3UDYtIRydzCqlLPTB7jK1DDBKdnTUZ4zL3z3rnPpzV6rL1Uzh87sXg==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" + "@jest/types": "30.0.5", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "natural-compare": "^1.4.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz", - "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==", + "node_modules/@jest/source-map": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-30.0.1.tgz", + "integrity": "sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==", "dev": true, + "license": "MIT", "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "is-array-buffer": "^3.0.2", - "is-shared-array-buffer": "^1.0.2" + "@jridgewell/trace-mapping": "^0.3.25", + "callsites": "^3.1.0", + "graceful-fs": "^4.2.11" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "node_modules/@jest/test-result": { + "version": "30.1.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.1.1.tgz", + "integrity": "sha512-bMdj7fNu8iZuBPSnbVir5ezvWmVo4jrw7xDE+A33Yb3ENCoiJK9XgOLgal+rJ9XSKjsL7aPUMIo87zhN7I5o2w==", "dev": true, - "engines": { - "node": ">= 0.4" + "license": "MIT", + "dependencies": { + "@jest/console": "30.1.1", + "@jest/types": "30.0.5", + "@types/istanbul-lib-coverage": "^2.0.6", + "collect-v8-coverage": "^1.0.2" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "node_modules/@jest/test-sequencer": { + "version": "30.1.1", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.1.1.tgz", + "integrity": "sha512-yruRdLXSA3HYD/MTNykgJ6VYEacNcXDFRMqKVAwlYegmxICUiT/B++CNuhJnYJzKYks61iYnjVsMwbUqmmAYJg==", "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "30.1.1", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.1.0", + "slash": "^3.0.0" + }, "engines": { - "node": ">=8" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/@jest/transform": { + "version": "30.1.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.1.1.tgz", + "integrity": "sha512-PHIA2AbAASBfk6evkNifvmx9lkOSkmvaQoO6VSpuL8+kQqDMHeDoJ7RU3YP1wWAMD7AyQn9UL5iheuFYCC4lqQ==", "dev": true, + "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "@babel/core": "^7.27.4", + "@jest/types": "30.0.5", + "@jridgewell/trace-mapping": "^0.3.25", + "babel-plugin-istanbul": "^7.0.0", + "chalk": "^4.1.2", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.1.0", + "jest-regex-util": "30.0.1", + "jest-util": "30.0.5", + "micromatch": "^4.0.8", + "pirates": "^4.0.7", + "slash": "^3.0.0", + "write-file-atomic": "^5.0.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, + "node_modules/@jest/types": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.0.5.tgz", + "integrity": "sha512-aREYa3aku9SSnea4aX6bhKn4bgv3AXkgijoQgbYV3yvbiGt6z+MQ85+6mIhx9DsKW2BuB/cLR/A+tcMThx+KLQ==", + "license": "MIT", "dependencies": { - "fill-range": "^7.0.1" + "@jest/pattern": "30.0.1", + "@jest/schemas": "30.0.5", + "@types/istanbul-lib-coverage": "^2.0.6", + "@types/istanbul-reports": "^3.0.4", + "@types/node": "*", + "@types/yargs": "^17.0.33", + "chalk": "^4.1.2" }, "engines": { - "node": ">=8" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/call-bind": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", - "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.12", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", + "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", "dev": true, + "license": "MIT", "dependencies": { - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.1", - "set-function-length": "^1.1.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" } }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, + "license": "MIT", "engines": { - "node": ">=6" + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", + "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.29", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", + "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@lezer/common": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.3.tgz", + "integrity": "sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==", + "license": "MIT" + }, + "node_modules/@lezer/css": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.3.0.tgz", + "integrity": "sha512-pBL7hup88KbI7hXnZV3PQsn43DHy6TWyzuyk2AO9UyoXcDltvIdqWKE1dLL/45JVZ+YZkHe1WVHqO6wugZZWcw==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.3.0" + } + }, + "node_modules/@lezer/generator": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@lezer/generator/-/generator-1.8.0.tgz", + "integrity": "sha512-/SF4EDWowPqV1jOgoGSGTIFsE7Ezdr7ZYxyihl5eMKVO5tlnpIhFcDavgm1hHY5GEonoOAEnJ0CU0x+tvuAuUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.1.0", + "@lezer/lr": "^1.3.0" + }, + "bin": { + "lezer-generator": "src/lezer-generator.cjs" + } + }, + "node_modules/@lezer/highlight": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.1.tgz", + "integrity": "sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@lezer/html": { + "version": "1.3.10", + "resolved": "https://registry.npmjs.org/@lezer/html/-/html-1.3.10.tgz", + "integrity": "sha512-dqpT8nISx/p9Do3AchvYGV3qYc4/rKr3IBZxlHmpIKam56P47RSHkSF5f13Vu9hebS1jM0HmtJIwLbWz1VIY6w==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/javascript": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.5.1.tgz", + "integrity": "sha512-ATOImjeVJuvgm3JQ/bpo2Tmv55HSScE2MTPnKRMRIPx2cLhHGyX2VnqpHhtIV1tVzIjZDbcWQm+NCTF40ggZVw==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.1.3", + "@lezer/lr": "^1.3.0" + } + }, + "node_modules/@lezer/json": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@lezer/json/-/json-1.0.3.tgz", + "integrity": "sha512-BP9KzdF9Y35PDpv04r0VeSTKDeox5vVr3efE7eBbx3r4s3oNLfunchejZhjArmeieBH+nVOpgIiBJpEAv8ilqQ==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/lr": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.2.tgz", + "integrity": "sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@lezer/markdown": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/@lezer/markdown/-/markdown-1.4.3.tgz", + "integrity": "sha512-kfw+2uMrQ/wy/+ONfrH83OkdFNM0ye5Xq96cLlaCy7h5UT9FO54DU4oRoIc0CSBh5NWmWuiIJA7NGLMJbQ+Oxg==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.0.0", + "@lezer/highlight": "^1.0.0" + } + }, + "node_modules/@lezer/php": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@lezer/php/-/php-1.0.4.tgz", + "integrity": "sha512-D2dJ0t8Z28/G1guztRczMFvPDUqzeMLSQbdWQmaiHV7urc8NlEOnjYk9UrZ531OcLiRxD4Ihcbv7AsDpNKDRaQ==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.1.0" + } + }, + "node_modules/@lezer/xml": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@lezer/xml/-/xml-1.0.6.tgz", + "integrity": "sha512-CdDwirL0OEaStFue/66ZmFSeppuL6Dwjlk8qk153mSQwiSH/Dlri4GNymrNWnUmPl2Um7QfV1FO9KFUyX3Twww==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@marijn/find-cluster-break": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz", + "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==", + "license": "MIT" + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", + "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@tybys/wasm-util": "^0.10.0" + } + }, + "node_modules/@parcel/watcher": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz", + "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.1", + "@parcel/watcher-darwin-arm64": "2.5.1", + "@parcel/watcher-darwin-x64": "2.5.1", + "@parcel/watcher-freebsd-x64": "2.5.1", + "@parcel/watcher-linux-arm-glibc": "2.5.1", + "@parcel/watcher-linux-arm-musl": "2.5.1", + "@parcel/watcher-linux-arm64-glibc": "2.5.1", + "@parcel/watcher-linux-arm64-musl": "2.5.1", + "@parcel/watcher-linux-x64-glibc": "2.5.1", + "@parcel/watcher-linux-x64-musl": "2.5.1", + "@parcel/watcher-win32-arm64": "2.5.1", + "@parcel/watcher-win32-ia32": "2.5.1", + "@parcel/watcher-win32-x64": "2.5.1" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz", + "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz", + "integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz", + "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz", + "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz", + "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz", + "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz", + "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz", + "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz", + "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz", + "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz", + "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz", + "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", + "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@pkgr/core": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", + "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/pkgr" + } + }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sinclair/typebox": { + "version": "0.34.41", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz", + "integrity": "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==", + "license": "MIT" + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "13.0.5", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz", + "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1" + } + }, + "node_modules/@ssddanbrown/codemirror-lang-smarty": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@ssddanbrown/codemirror-lang-smarty/-/codemirror-lang-smarty-1.0.0.tgz", + "integrity": "sha512-F0ut1kmdbT3eORk3xVIKfQsGCZiQdh+6sLayBa0+FTex2gyIQlVQZRRA7bPSlchI3uZtWwNnqGNz5O/QLWRlFg==", + "license": "MIT" + }, + "node_modules/@ssddanbrown/codemirror-lang-twig": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@ssddanbrown/codemirror-lang-twig/-/codemirror-lang-twig-1.0.0.tgz", + "integrity": "sha512-7WIMIh8Ssc54TooGCY57WU2rKEqZZrcV2tZSVRPtd0gKYsrDEKCSLWpQjUWEx7bdgh3NKHUjq1O4ugIzI/+dwQ==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.0.tgz", + "integrity": "sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "30.0.0", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-30.0.0.tgz", + "integrity": "sha512-XTYugzhuwqWjws0CVz8QpM36+T+Dz5mTEBKhNs/esGLnCIlGdRy+Dq78NRjd7ls7r8BC8ZRMOrKlkO1hU0JOwA==", + "license": "MIT", + "dependencies": { + "expect": "^30.0.0", + "pretty-format": "^30.0.0" + } + }, + "node_modules/@types/jsdom": { + "version": "21.1.7", + "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-21.1.7.tgz", + "integrity": "sha512-yOriVnggzrnQ3a9OKOCxaVuSug3w3/SbOj5i7VwXWZEyUNl3bLF9V3MfxGbZKuwqJOQyRfqXyROBB1CoZLFWzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/tough-cookie": "*", + "parse5": "^7.0.0" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/markdown-it": { + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", + "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, + "node_modules/@types/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.1.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.1.0.tgz", + "integrity": "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.8.0" + } + }, + "node_modules/@types/sortablejs": { + "version": "1.15.8", + "resolved": "https://registry.npmjs.org/@types/sortablejs/-/sortablejs-1.15.8.tgz", + "integrity": "sha512-b79830lW+RZfwaztgs1aVPgbasJ8e7AXtZYHTELNXZPsERt4ymJdjV4OccDbHQAvHrCcFpbF78jkm0R6h/pZVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "license": "MIT" + }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "license": "MIT" + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true, + "license": "ISC" + }, + "node_modules/@unrs/resolver-binding-android-arm-eabi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", + "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-android-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", + "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", + "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", + "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-freebsd-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz", + "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz", + "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz", + "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz", + "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz", + "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz", + "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz", + "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz", + "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz", + "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz", + "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz", + "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-wasm32-wasi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz", + "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^0.2.11" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz", + "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz", + "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-x64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz", + "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz", + "integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", + "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.0", + "es-object-atoms": "^1.1.1", + "get-intrinsic": "^1.3.0", + "is-string": "^1.1.1", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", + "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-shim-unscopables": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/babel-jest": { + "version": "30.1.1", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.1.1.tgz", + "integrity": "sha512-1bZfC/V03qBCzASvZpNFhx3Ouj6LgOd4KFJm4br/fYOS+tSSvVCE61QmcAVbMTwq/GoB7KN4pzGMoyr9cMxSvQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/transform": "30.1.1", + "@types/babel__core": "^7.20.5", + "babel-plugin-istanbul": "^7.0.0", + "babel-preset-jest": "30.0.1", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.11.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-7.0.0.tgz", + "integrity": "sha512-C5OzENSx/A+gt7t4VH1I2XsflxyPUmXRFPKBxt33xncdOmq7oROVM3bZv9Ysjjkv8OJYDMa+tKuKMvqU/H3xdw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-instrument": "^6.0.2", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.0.1.tgz", + "integrity": "sha512-zTPME3pI50NsFW8ZBaVIOeAxzEY7XHlmWeXXu9srI+9kNfzCUTy8MFan46xOGZY8NZThMqq+e3qZUKsvXbasnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.27.3", + "@types/babel__core": "^7.20.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz", + "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-30.0.1.tgz", + "integrity": "sha512-+YHejD5iTWI46cZmcc/YtX4gaKBtdqCHCVfuVinizVpbmyjO3zYmeuyFdfA8duRqQZfgCAMlsfmkVbJ+e2MAJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "babel-plugin-jest-hoist": "30.0.1", + "babel-preset-current-node-syntax": "^1.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.11.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.25.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz", + "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001726", + "electron-to-chromium": "^1.5.173", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001727", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz", + "integrity": "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar-cli": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chokidar-cli/-/chokidar-cli-3.0.0.tgz", + "integrity": "sha512-xVW+Qeh7z15uZRxHOkP93Ux8A0xbPzwK4GaqD8dQOYc34TlkqUhVSS59fK36DOp5WdJlrRzlYSy02Ht99FjZqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.2", + "lodash.debounce": "^4.0.8", + "lodash.throttle": "^4.1.1", + "yargs": "^13.3.0" + }, + "bin": { + "chokidar": "index.js" + }, + "engines": { + "node": ">= 8.10.0" + } + }, + "node_modules/ci-info": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.0.tgz", + "integrity": "sha512-l+2bNRMiQgcfILUi33labAZYIWlH1kWDp+ecNo5iisRKrbm0xcRyCww71/YU0Fkw0mAFpz9bJayXPjey6vkmaQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.1.0.tgz", + "integrity": "sha512-UX0OwmYRYQQetfrLEZeewIFFI+wSTofC+pMBLNuH3RUuu/xzG1oz84UCEDOSoQlN3fZ4+AzmV50ZYvGqkMh9yA==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/codemirror": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.2.tgz", + "integrity": "sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/commands": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/search": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/crelt": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", + "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==", + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cssstyle": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.6.0.tgz", + "integrity": "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/css-color": "^3.2.0", + "rrweb-cssom": "^0.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/data-urls": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", + "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "dev": true, + "license": "MIT" + }, + "node_modules/dedent": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.6.0.tgz", + "integrity": "sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.190", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.190.tgz", + "integrity": "sha512-k4McmnB2091YIsdCgkS0fMVMPOJgxl93ltFzaryXqwip1AaxeDqKCGLxkXODDA5Ab/D+tV5EL5+aTx76RvLRxw==", + "dev": true, + "license": "ISC" + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true, + "license": "MIT" + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.24.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz", + "integrity": "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/esbuild": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz", + "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.9", + "@esbuild/android-arm": "0.25.9", + "@esbuild/android-arm64": "0.25.9", + "@esbuild/android-x64": "0.25.9", + "@esbuild/darwin-arm64": "0.25.9", + "@esbuild/darwin-x64": "0.25.9", + "@esbuild/freebsd-arm64": "0.25.9", + "@esbuild/freebsd-x64": "0.25.9", + "@esbuild/linux-arm": "0.25.9", + "@esbuild/linux-arm64": "0.25.9", + "@esbuild/linux-ia32": "0.25.9", + "@esbuild/linux-loong64": "0.25.9", + "@esbuild/linux-mips64el": "0.25.9", + "@esbuild/linux-ppc64": "0.25.9", + "@esbuild/linux-riscv64": "0.25.9", + "@esbuild/linux-s390x": "0.25.9", + "@esbuild/linux-x64": "0.25.9", + "@esbuild/netbsd-arm64": "0.25.9", + "@esbuild/netbsd-x64": "0.25.9", + "@esbuild/openbsd-arm64": "0.25.9", + "@esbuild/openbsd-x64": "0.25.9", + "@esbuild/openharmony-arm64": "0.25.9", + "@esbuild/sunos-x64": "0.25.9", + "@esbuild/win32-arm64": "0.25.9", + "@esbuild/win32-ia32": "0.25.9", + "@esbuild/win32-x64": "0.25.9" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.34.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.34.0.tgz", + "integrity": "sha512-RNCHRX5EwdrESy3Jc9o8ie8Bog+PeYvvSR8sDGoZxNFTvZ4dlxUB3WzQ3bQMztFrSRODGrLLj8g6OFuGY/aiQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.3.1", + "@eslint/core": "^0.15.2", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.34.0", + "@eslint/plugin-kit": "^0.3.5", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz", + "integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.32.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", + "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.9", + "array.prototype.findlastindex": "^1.2.6", + "array.prototype.flat": "^1.3.3", + "array.prototype.flatmap": "^1.3.3", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.12.1", + "hasown": "^2.0.2", + "is-core-module": "^2.16.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.1", + "semver": "^6.3.1", + "string.prototype.trimend": "^1.0.9", + "tsconfig-paths": "^3.15.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit-x": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/exit-x/-/exit-x-0.2.2.tgz", + "integrity": "sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "30.1.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-30.1.1.tgz", + "integrity": "sha512-OKe7cdic4qbfWd/CcgwJvvCrNX2KWfuMZee9AfJHL1gTYmvqjBjZG1a2NwfhspBzxzlXwsN75WWpKTYfsJpBxg==", + "license": "MIT", + "dependencies": { + "@jest/expect-utils": "30.1.1", + "@jest/get-type": "30.1.0", + "jest-matcher-utils": "30.1.1", + "jest-message-util": "30.1.0", + "jest-mock": "30.0.5", + "jest-util": "30.0.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, "engines": { - "node": ">=6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, + "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "flatted": "^3.2.9", + "keyv": "^4.5.4" }, "engines": { - "node": ">=10" + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], + "license": "ISC", "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" }, "engines": { - "node": ">= 8.10.0" + "node": ">=14" }, - "optionalDependencies": { - "fsevents": "~2.3.2" + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/chokidar-cli": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chokidar-cli/-/chokidar-cli-3.0.0.tgz", - "integrity": "sha512-xVW+Qeh7z15uZRxHOkP93Ux8A0xbPzwK4GaqD8dQOYc34TlkqUhVSS59fK36DOp5WdJlrRzlYSy02Ht99FjZqQ==", + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "dev": true, + "license": "MIT", "dependencies": { - "chokidar": "^3.5.2", - "lodash.debounce": "^4.0.8", - "lodash.throttle": "^4.1.1", - "yargs": "^13.3.0" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" }, - "bin": { - "chokidar": "index.js" + "engines": { + "node": ">= 0.4" }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", "engines": { - "node": ">= 8.10.0" + "node": ">=6.9.0" } }, - "node_modules/cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "dev": true, + "license": "MIT", "dependencies": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/cliui/node_modules/ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", "dev": true, + "license": "MIT", "engines": { - "node": ">=6" + "node": ">=8.0.0" } }, - "node_modules/cliui/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", "dev": true, + "license": "MIT", "dependencies": { - "ansi-regex": "^4.1.0" + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" }, "engines": { - "node": ">=6" + "node": ">= 0.4" } }, - "node_modules/codemirror": { + "node_modules/get-stream": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.1.tgz", - "integrity": "sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==", - "dependencies": { - "@codemirror/autocomplete": "^6.0.0", - "@codemirror/commands": "^6.0.0", - "@codemirror/language": "^6.0.0", - "@codemirror/lint": "^6.0.0", - "@codemirror/search": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0" + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", "dev": true, + "license": "MIT", "dependencies": { - "color-name": "~1.1.4" + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" }, "engines": { - "node": ">=7.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true + "node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, - "node_modules/confusing-browser-globals": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", - "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", - "dev": true + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } }, - "node_modules/crelt": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", - "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==" + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "node_modules/glob/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, + "license": "ISC", "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">= 8" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", "dependencies": { - "ms": "2.1.2" + "define-properties": "^1.2.1", + "gopd": "^1.0.1" }, "engines": { - "node": ">=6.0" + "node": ">= 0.4" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/decamelize": { + "node_modules/gopd": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" }, - "node_modules/define-data-property": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", - "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", "dev": true, + "license": "MIT", "dependencies": { - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" }, "engines": { - "node": ">= 0.4" + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" } }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", "dev": true, - "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -1322,89 +5293,65 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dev": true, + "license": "MIT", "dependencies": { - "esutils": "^2.0.2" + "es-define-property": "^1.0.0" }, - "engines": { - "node": ">=6.0.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "node_modules/entities": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", - "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, "engines": { - "node": ">=0.12" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "dev": true, - "dependencies": { - "is-arrayish": "^0.2.1" + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es-abstract": { - "version": "1.22.2", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.2.tgz", - "integrity": "sha512-YoxfFcDmhjOgWPWsV13+2RNjq1F6UQnfs+8TftwNqtzlmFzEXvlUwdrNrYeaizfjQzRMxkZ6ElWMOJIFKdVqwA==", + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dev": true, + "license": "MIT", "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "arraybuffer.prototype.slice": "^1.0.2", - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-set-tostringtag": "^2.0.1", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.1", - "get-symbol-description": "^1.0.0", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.5", - "is-array-buffer": "^3.0.2", - "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.12", - "is-weakref": "^1.0.2", - "object-inspect": "^1.12.3", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.1", - "safe-array-concat": "^1.0.1", - "safe-regex-test": "^1.0.0", - "string.prototype.trim": "^1.2.8", - "string.prototype.trimend": "^1.0.7", - "string.prototype.trimstart": "^1.0.7", - "typed-array-buffer": "^1.0.0", - "typed-array-byte-length": "^1.0.0", - "typed-array-byte-offset": "^1.0.0", - "typed-array-length": "^1.0.4", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.11" + "has-symbols": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -1413,501 +5360,504 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es-set-tostringtag": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", - "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dev": true, + "license": "MIT", "dependencies": { - "get-intrinsic": "^1.1.3", - "has": "^1.0.3", - "has-tostringtag": "^1.0.0" + "function-bind": "^1.1.2" }, "engines": { "node": ">= 0.4" } }, - "node_modules/es-shim-unscopables": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", - "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true, + "license": "ISC" + }, + "node_modules/html-encoding-sniffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", + "dev": true, + "license": "MIT", "dependencies": { - "has": "^1.0.3" + "whatwg-encoding": "^3.1.1" + }, + "engines": { + "node": ">=18" } }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true, + "license": "MIT" + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" + "agent-base": "^7.1.0", + "debug": "^4.3.4" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 14" } }, - "node_modules/esbuild": { - "version": "0.19.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.5.tgz", - "integrity": "sha512-bUxalY7b1g8vNhQKdB24QDmHeY4V4tw/s6Ak5z+jJX9laP5MoQseTOMemAr0gxssjNcH0MCViG8ONI2kksvfFQ==", + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "dev": true, - "hasInstallScript": true, - "bin": { - "esbuild": "bin/esbuild" + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" }, "engines": { - "node": ">=12" + "node": ">= 14" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" }, - "optionalDependencies": { - "@esbuild/android-arm": "0.19.5", - "@esbuild/android-arm64": "0.19.5", - "@esbuild/android-x64": "0.19.5", - "@esbuild/darwin-arm64": "0.19.5", - "@esbuild/darwin-x64": "0.19.5", - "@esbuild/freebsd-arm64": "0.19.5", - "@esbuild/freebsd-x64": "0.19.5", - "@esbuild/linux-arm": "0.19.5", - "@esbuild/linux-arm64": "0.19.5", - "@esbuild/linux-ia32": "0.19.5", - "@esbuild/linux-loong64": "0.19.5", - "@esbuild/linux-mips64el": "0.19.5", - "@esbuild/linux-ppc64": "0.19.5", - "@esbuild/linux-riscv64": "0.19.5", - "@esbuild/linux-s390x": "0.19.5", - "@esbuild/linux-x64": "0.19.5", - "@esbuild/netbsd-x64": "0.19.5", - "@esbuild/openbsd-x64": "0.19.5", - "@esbuild/sunos-x64": "0.19.5", - "@esbuild/win32-arm64": "0.19.5", - "@esbuild/win32-ia32": "0.19.5", - "@esbuild/win32-x64": "0.19.5" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "node_modules/idb-keyval": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.2.tgz", + "integrity": "sha512-yjD9nARJ/jb1g+CvD0tlhUHOrJ9Sy0P8T9MF3YaLlHnSRpwPfpTX0XIvpmw3gAJUmEu3FiICLBDPXVwyEvrleg==", + "license": "Apache-2.0" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, + "license": "MIT", "engines": { - "node": ">=10" + "node": ">= 4" + } + }, + "node_modules/immutable": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.3.tgz", + "integrity": "sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==", + "dev": true, + "license": "MIT" + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.55.0.tgz", - "integrity": "sha512-iyUUAM0PCKj5QpwGfmCAG9XXbZCWsqP/eWAWrG/W0umvjuLRBECwSFdt+rCntju0xEH7teIABPwXpahftIaTdA==", + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", "dev": true, + "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.55.0", - "@humanwhocodes/config-array": "^0.11.13", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" }, "bin": { - "eslint": "bin/eslint.js" + "import-local-fixture": "fixtures/cli.js" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=8" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint-config-airbnb-base": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz", - "integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==", + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, - "dependencies": { - "confusing-browser-globals": "^1.0.10", - "object.assign": "^4.1.2", - "object.entries": "^1.1.5", - "semver": "^6.3.0" - }, + "license": "MIT", "engines": { - "node": "^10.12.0 || >=12.0.0" - }, - "peerDependencies": { - "eslint": "^7.32.0 || ^8.2.0", - "eslint-plugin-import": "^2.25.2" + "node": ">=0.8.19" } }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", - "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dev": true, + "license": "ISC", "dependencies": { - "debug": "^3.2.7", - "is-core-module": "^2.13.0", - "resolve": "^1.22.4" + "once": "^1.3.0", + "wrappy": "1" } }, - "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true, - "dependencies": { - "ms": "^2.1.1" - } + "license": "ISC" }, - "node_modules/eslint-module-utils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", - "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", "dev": true, + "license": "MIT", "dependencies": { - "debug": "^3.2.7" + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" }, "engines": { - "node": ">=4" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } + "node": ">= 0.4" } }, - "node_modules/eslint-module-utils/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", "dev": true, + "license": "MIT", "dependencies": { - "ms": "^2.1.1" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint-plugin-import": { - "version": "2.29.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.0.tgz", - "integrity": "sha512-QPOO5NO6Odv5lpoTkddtutccQjysJuFxoPS7fAHO+9m9udNHvTCPSAMW9zGAYj8lAIdr40I8yPCdUYrncXtrwg==", + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true, - "dependencies": { - "array-includes": "^3.1.7", - "array.prototype.findlastindex": "^1.2.3", - "array.prototype.flat": "^1.3.2", - "array.prototype.flatmap": "^1.3.2", - "debug": "^3.2.7", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.9", - "eslint-module-utils": "^2.8.0", - "hasown": "^2.0.0", - "is-core-module": "^2.13.1", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.fromentries": "^2.0.7", - "object.groupby": "^1.0.1", - "object.values": "^1.1.7", - "semver": "^6.3.1", - "tsconfig-paths": "^3.14.2" + "license": "MIT" + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" }, "engines": { - "node": ">=4" + "node": ">= 0.4" }, - "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint-plugin-import/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", "dev": true, + "license": "MIT", "dependencies": { - "ms": "^2.1.1" + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint-plugin-import/node_modules/doctrine": { + "node_modules/is-binary-path": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, + "license": "MIT", "dependencies": { - "esutils": "^2.0.2" + "binary-extensions": "^2.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", "dev": true, + "license": "MIT", "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">= 0.4" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "dev": true, + "license": "MIT", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">= 0.4" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "dev": true, + "license": "MIT", "dependencies": { - "is-glob": "^4.0.3" + "hasown": "^2.0.2" }, "engines": { - "node": ">=10.13.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", "dev": true, + "license": "MIT", "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">= 0.4" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", "dev": true, + "license": "MIT", "dependencies": { - "estraverse": "^5.1.0" + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" }, "engines": { - "node": ">=0.10" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, - "dependencies": { - "estraverse": "^5.2.0" - }, + "license": "MIT", "engines": { - "node": ">=4.0" + "node": ">=0.10.0" } }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, "engines": { - "node": ">=4.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=4" } }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "node_modules/fast-json-stable-stringify": { + "node_modules/is-generator-fn": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "node_modules/fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", "dev": true, - "dependencies": { - "reusify": "^1.0.4" + "license": "MIT", + "engines": { + "node": ">=6" } }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "node_modules/is-generator-function": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", "dev": true, + "license": "MIT", "dependencies": { - "flat-cache": "^3.0.4" + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, + "license": "MIT", "dependencies": { - "to-regex-range": "^5.0.1" + "is-extglob": "^2.1.1" }, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, + "license": "MIT", "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/flat-cache": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.1.tgz", - "integrity": "sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q==", + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", "dev": true, - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" - }, + "license": "MIT", "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/flatted": { - "version": "3.2.9", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", - "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", - "dev": true - }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dev": true, - "dependencies": { - "is-callable": "^1.1.3" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "license": "MIT", "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + "node": ">=0.12.0" } }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/function.prototype.name": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", - "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "functions-have-names": "^1.2.3" + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -1916,47 +5866,57 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, "engines": { - "node": "6.* || 8.* || >= 10.*" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/get-intrinsic": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", - "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true, - "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3" + "license": "MIT", + "engines": { + "node": ">=8" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -1965,60 +5925,61 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", "dev": true, + "license": "MIT", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" }, "engines": { - "node": "*" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", "dev": true, + "license": "MIT", "dependencies": { - "is-glob": "^4.0.1" + "which-typed-array": "^1.1.16" }, "engines": { - "node": ">= 6" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/globals": { - "version": "13.23.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", - "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", "dev": true, + "license": "MIT", "dependencies": { - "define-properties": "^1.1.3" + "call-bound": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -2027,477 +5988,890 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", "dev": true, + "license": "MIT", "dependencies": { - "get-intrinsic": "^1.1.3" + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" }, - "node_modules/has": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.4.tgz", - "integrity": "sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==", + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", "dev": true, + "license": "BSD-3-Clause", "engines": { - "node": ">= 0.4.0" + "node": ">=8" } }, - "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" } }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, "engines": { - "node": ">=8" + "node": ">=10" } }, - "node_modules/has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "get-intrinsic": "^1.1.1" + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=10" } }, - "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "node_modules/istanbul-lib-source-maps": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", + "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", "dev": true, - "engines": { - "node": ">= 0.4" + "license": "BSD-3-Clause", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.23", + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=10" } }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, "engines": { - "node": ">= 0.4" + "node": ">=8" + } + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "node_modules/jest": { + "version": "30.1.1", + "resolved": "https://registry.npmjs.org/jest/-/jest-30.1.1.tgz", + "integrity": "sha512-yC3JvpP/ZcAZX5rYCtXO/g9k6VTCQz0VFE2v1FpxytWzUqfDtu0XL/pwnNvptzYItvGwomh1ehomRNMOyhCJKw==", "dev": true, + "license": "MIT", "dependencies": { - "has-symbols": "^1.0.2" + "@jest/core": "30.1.1", + "@jest/types": "30.0.5", + "import-local": "^3.2.0", + "jest-cli": "30.1.1" + }, + "bin": { + "jest": "bin/jest.js" }, "engines": { - "node": ">= 0.4" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "node_modules/hasown": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", - "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "node_modules/jest-changed-files": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-30.0.5.tgz", + "integrity": "sha512-bGl2Ntdx0eAwXuGpdLdVYVr5YQHnSZlQ0y9HVDu565lCUAe9sj6JOtBbMmBBikGIegne9piDDIOeiLVoqTkz4A==", "dev": true, + "license": "MIT", "dependencies": { - "function-bind": "^1.1.2" + "execa": "^5.1.1", + "jest-util": "30.0.5", + "p-limit": "^3.1.0" }, "engines": { - "node": ">= 0.4" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, - "node_modules/idb-keyval": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.1.tgz", - "integrity": "sha512-8Sb3veuYCyrZL+VBt9LJfZjLUPWVvqn8tG28VqYNFCo43KHcKuq+b4EiXGeuaLAQWL2YmyDgMp2aSpH9JHsEQg==" - }, - "node_modules/ignore": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", - "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", + "node_modules/jest-circus": { + "version": "30.1.1", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-30.1.1.tgz", + "integrity": "sha512-M3Vd4x5wD7eSJspuTvRF55AkOOBndRxgW3gqQBDlFvbH3X+ASdi8jc+EqXEeAFd/UHulVYIlC4XKJABOhLw6UA==", "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.1.1", + "@jest/expect": "30.1.1", + "@jest/test-result": "30.1.1", + "@jest/types": "30.0.5", + "@types/node": "*", + "chalk": "^4.1.2", + "co": "^4.6.0", + "dedent": "^1.6.0", + "is-generator-fn": "^2.1.0", + "jest-each": "30.1.0", + "jest-matcher-utils": "30.1.1", + "jest-message-util": "30.1.0", + "jest-runtime": "30.1.1", + "jest-snapshot": "30.1.1", + "jest-util": "30.0.5", + "p-limit": "^3.1.0", + "pretty-format": "30.0.5", + "pure-rand": "^7.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.6" + }, "engines": { - "node": ">= 4" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/immutable": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.4.tgz", - "integrity": "sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==", - "dev": true - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "node_modules/jest-cli": { + "version": "30.1.1", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-30.1.1.tgz", + "integrity": "sha512-xm9llxuh5OoI5KZaYzlMhklryHBwg9LZy/gEaaMlXlxb+cZekGNzukU0iblbDo3XOBuN6N0CgK4ykgNRYSEb6g==", "dev": true, + "license": "MIT", "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" + "@jest/core": "30.1.1", + "@jest/test-result": "30.1.1", + "@jest/types": "30.0.5", + "chalk": "^4.1.2", + "exit-x": "^0.2.2", + "import-local": "^3.2.0", + "jest-config": "30.1.1", + "jest-util": "30.0.5", + "jest-validate": "30.1.0", + "yargs": "^17.7.2" + }, + "bin": { + "jest": "bin/jest.js" }, "engines": { - "node": ">=6" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "node_modules/jest-cli/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.8.19" + "node": ">=8" } }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "node_modules/jest-cli/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, + "license": "ISC", "dependencies": { - "once": "^1.3.0", - "wrappy": "1" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" } }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "node_modules/jest-cli/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" }, - "node_modules/internal-slot": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", - "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", + "node_modules/jest-cli/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-cli/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, + "license": "MIT", "dependencies": { - "get-intrinsic": "^1.2.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">= 0.4" + "node": ">=8" } }, - "node_modules/is-array-buffer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", - "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "node_modules/jest-cli/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", - "is-typed-array": "^1.1.10" + "ansi-regex": "^5.0.1" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=8" } }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, - "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "node_modules/jest-cli/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, + "license": "MIT", "dependencies": { - "has-bigints": "^1.0.1" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "node_modules/jest-cli/node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-cli/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, + "license": "MIT", "dependencies": { - "binary-extensions": "^2.0.0" + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" }, "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "node_modules/jest-config": { + "version": "30.1.1", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.1.1.tgz", + "integrity": "sha512-xuPGUGDw+9fPPnGmddnLnHS/mhKUiJOW7K65vErYmglEPKq65NKwSRchkQ7iv6gqjs2l+YNEsAtbsplxozdOWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.27.4", + "@jest/get-type": "30.1.0", + "@jest/pattern": "30.0.1", + "@jest/test-sequencer": "30.1.1", + "@jest/types": "30.0.5", + "babel-jest": "30.1.1", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "deepmerge": "^4.3.1", + "glob": "^10.3.10", + "graceful-fs": "^4.2.11", + "jest-circus": "30.1.1", + "jest-docblock": "30.0.1", + "jest-environment-node": "30.1.1", + "jest-regex-util": "30.0.1", + "jest-resolve": "30.1.0", + "jest-runner": "30.1.1", + "jest-util": "30.0.5", + "jest-validate": "30.1.0", + "micromatch": "^4.0.8", + "parse-json": "^5.2.0", + "pretty-format": "30.0.5", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "esbuild-register": ">=3.4.0", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "esbuild-register": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-diff": { + "version": "30.1.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.1.1.tgz", + "integrity": "sha512-LUU2Gx8EhYxpdzTR6BmjL1ifgOAQJQELTHOiPv9KITaKjZvJ9Jmgigx01tuZ49id37LorpGc9dPBPlXTboXScw==", + "license": "MIT", + "dependencies": { + "@jest/diff-sequences": "30.0.1", + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "pretty-format": "30.0.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-30.0.1.tgz", + "integrity": "sha512-/vF78qn3DYphAaIc3jy4gA7XSAz167n9Bm/wn/1XhTLW7tTBIzXtCJpb/vcmc73NIIeeohCbdL94JasyXUZsGA==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "detect-newline": "^3.1.0" }, "engines": { - "node": ">= 0.4" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-each": { + "version": "30.1.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-30.1.0.tgz", + "integrity": "sha512-A+9FKzxPluqogNahpCv04UJvcZ9B3HamqpDNWNKDjtxVRYB8xbZLFuCr8JAJFpNp83CA0anGQFlpQna9Me+/tQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0", + "@jest/types": "30.0.5", + "chalk": "^4.1.2", + "jest-util": "30.0.5", + "pretty-format": "30.0.5" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "node_modules/jest-environment-jsdom": { + "version": "30.1.1", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-30.1.1.tgz", + "integrity": "sha512-fInyXsHSuPaERmRiub4V6jl6KERXowGqY8AISJrXZjOq7vdP46qecm+GnTngjcUPeHFqrxp1PfP0XuFfKTzA2A==", "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.1.1", + "@jest/environment-jsdom-abstract": "30.1.1", + "@types/jsdom": "^21.1.7", + "@types/node": "*", + "jsdom": "^26.1.0" + }, "engines": { - "node": ">= 0.4" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } } }, - "node_modules/is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "node_modules/jest-environment-node": { + "version": "30.1.1", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.1.1.tgz", + "integrity": "sha512-IaMoaA6saxnJimqCppUDqKck+LKM0Jg+OxyMUIvs1yGd2neiC22o8zXo90k04+tO+49OmgMR4jTgM5e4B0S62Q==", "dev": true, + "license": "MIT", "dependencies": { - "hasown": "^2.0.0" + "@jest/environment": "30.1.1", + "@jest/fake-timers": "30.1.1", + "@jest/types": "30.0.5", + "@types/node": "*", + "jest-mock": "30.0.5", + "jest-util": "30.0.5", + "jest-validate": "30.1.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "node_modules/jest-haste-map": { + "version": "30.1.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.1.0.tgz", + "integrity": "sha512-JLeM84kNjpRkggcGpQLsV7B8W4LNUWz7oDNVnY1Vjj22b5/fAb3kk3htiD+4Na8bmJmjJR7rBtS2Rmq/NEcADg==", "dev": true, + "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "@jest/types": "30.0.5", + "@types/node": "*", + "anymatch": "^3.1.3", + "fb-watchman": "^2.0.2", + "graceful-fs": "^4.2.11", + "jest-regex-util": "30.0.1", + "jest-util": "30.0.5", + "jest-worker": "30.1.0", + "micromatch": "^4.0.8", + "walker": "^1.0.8" }, "engines": { - "node": ">= 0.4" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "optionalDependencies": { + "fsevents": "^2.3.3" } }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "node_modules/jest-leak-detector": { + "version": "30.1.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.1.0.tgz", + "integrity": "sha512-AoFvJzwxK+4KohH60vRuHaqXfWmeBATFZpzpmzNmYTtmRMiyGPVhkXpBqxUQunw+dQB48bDf4NpUs6ivVbRv1g==", "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0", + "pretty-format": "30.0.5" + }, "engines": { - "node": ">=0.10.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", - "dev": true, + "node_modules/jest-matcher-utils": { + "version": "30.1.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.1.1.tgz", + "integrity": "sha512-SuH2QVemK48BNTqReti6FtjsMPFsSOD/ZzRxU1TttR7RiRsRSe78d03bb4Cx6D4bQC/80Q8U4VnaaAH9FlbZ9w==", + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "jest-diff": "30.1.1", + "pretty-format": "30.0.5" + }, "engines": { - "node": ">=4" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, + "node_modules/jest-message-util": { + "version": "30.1.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.1.0.tgz", + "integrity": "sha512-HizKDGG98cYkWmaLUHChq4iN+oCENohQLb7Z5guBPumYs+/etonmNFlg1Ps6yN9LTPyZn+M+b/9BbnHx3WTMDg==", + "license": "MIT", "dependencies": { - "is-extglob": "^2.1.1" + "@babel/code-frame": "^7.27.1", + "@jest/types": "30.0.5", + "@types/stack-utils": "^2.0.3", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "micromatch": "^4.0.8", + "pretty-format": "30.0.5", + "slash": "^3.0.0", + "stack-utils": "^2.0.6" }, "engines": { - "node": ">=0.10.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", - "dev": true, - "engines": { - "node": ">= 0.4" + "node_modules/jest-mock": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.0.5.tgz", + "integrity": "sha512-Od7TyasAAQX/6S+QCbN6vZoWOMwlTtzzGuxJku1GhGanAjz9y+QsQkpScDmETvdc9aSXyJ/Op4rhpMYBWW91wQ==", + "license": "MIT", + "dependencies": { + "@jest/types": "30.0.5", + "@types/node": "*", + "jest-util": "30.0.5" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.12.0" + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } } }, - "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "node_modules/jest-regex-util": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.0.1.tgz", + "integrity": "sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==", + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "30.1.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-30.1.0.tgz", + "integrity": "sha512-hASe7D/wRtZw8Cm607NrlF7fi3HWC5wmA5jCVc2QjQAB2pTwP9eVZILGEi6OeSLNUtE1zb04sXRowsdh5CUjwA==", "dev": true, + "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.1.0", + "jest-pnp-resolver": "^1.2.3", + "jest-util": "30.0.5", + "jest-validate": "30.1.0", + "slash": "^3.0.0", + "unrs-resolver": "^1.7.11" }, - "engines": { - "node": ">= 0.4" + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "30.1.1", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-30.1.1.tgz", + "integrity": "sha512-tRtaaoH8Ws1Gn1o/9pedt19dvVgr81WwdmvJSP9Ow3amOUOP2nN9j94u5jC9XlIfa2Q1FQKIWWQwL4ajqsjCGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-regex-util": "30.0.1", + "jest-snapshot": "30.1.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-runner": { + "version": "30.1.1", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-30.1.1.tgz", + "integrity": "sha512-ATe6372SOfJvCRExtCAr06I4rGujwFdKg44b6i7/aOgFnULwjxzugJ0Y4AnG+jeSeQi8dU7R6oqLGmsxRUbErQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "30.1.1", + "@jest/environment": "30.1.1", + "@jest/test-result": "30.1.1", + "@jest/transform": "30.1.1", + "@jest/types": "30.0.5", + "@types/node": "*", + "chalk": "^4.1.2", + "emittery": "^0.13.1", + "exit-x": "^0.2.2", + "graceful-fs": "^4.2.11", + "jest-docblock": "30.0.1", + "jest-environment-node": "30.1.1", + "jest-haste-map": "30.1.0", + "jest-leak-detector": "30.1.0", + "jest-message-util": "30.1.0", + "jest-resolve": "30.1.0", + "jest-runtime": "30.1.1", + "jest-util": "30.0.5", + "jest-watcher": "30.1.1", + "jest-worker": "30.1.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "30.1.1", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.1.1.tgz", + "integrity": "sha512-7sOyR0Oekw4OesQqqBHuYJRB52QtXiq0NNgLRzVogiMSxKCMiliUd6RrXHCnG5f12Age/ggidCBiQftzcA9XKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.1.1", + "@jest/fake-timers": "30.1.1", + "@jest/globals": "30.1.1", + "@jest/source-map": "30.0.1", + "@jest/test-result": "30.1.1", + "@jest/transform": "30.1.1", + "@jest/types": "30.0.5", + "@types/node": "*", + "chalk": "^4.1.2", + "cjs-module-lexer": "^2.1.0", + "collect-v8-coverage": "^1.0.2", + "glob": "^10.3.10", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.1.0", + "jest-message-util": "30.1.0", + "jest-mock": "30.0.5", + "jest-regex-util": "30.0.1", + "jest-resolve": "30.1.0", + "jest-snapshot": "30.1.1", + "jest-util": "30.0.5", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "30.1.1", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.1.1.tgz", + "integrity": "sha512-7/iBEzoJqEt2TjkQY+mPLHP8cbPhLReZVkkxjTMzIzoTC4cZufg7HzKo/n9cIkXKj2LG0x3mmBHsZto+7TOmFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.27.4", + "@babel/generator": "^7.27.5", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/plugin-syntax-typescript": "^7.27.1", + "@babel/types": "^7.27.3", + "@jest/expect-utils": "30.1.1", + "@jest/get-type": "30.1.0", + "@jest/snapshot-utils": "30.1.1", + "@jest/transform": "30.1.1", + "@jest/types": "30.0.5", + "babel-preset-current-node-syntax": "^1.1.0", + "chalk": "^4.1.2", + "expect": "30.1.1", + "graceful-fs": "^4.2.11", + "jest-diff": "30.1.1", + "jest-matcher-utils": "30.1.1", + "jest-message-util": "30.1.0", + "jest-util": "30.0.5", + "pretty-format": "30.0.5", + "semver": "^7.7.2", + "synckit": "^0.11.8" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, "engines": { - "node": ">=8" + "node": ">=10" } }, - "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, + "node_modules/jest-util": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.0.5.tgz", + "integrity": "sha512-pvyPWssDZR0FlfMxCBoc0tvM8iUEskaRFALUtGQYzVEAqisAztmy+R8LnU14KT4XA0H/a5HMVTXat1jLne010g==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "@jest/types": "30.0.5", + "@types/node": "*", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "graceful-fs": "^4.2.11", + "picomatch": "^4.0.2" }, "engines": { - "node": ">= 0.4" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-util/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "license": "MIT", + "engines": { + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "node_modules/jest-validate": { + "version": "30.1.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.1.0.tgz", + "integrity": "sha512-7P3ZlCFW/vhfQ8pE7zW6Oi4EzvuB4sgR72Q1INfW9m0FGo0GADYlPwIkf4CyPq7wq85g+kPMtPOHNAdWHeBOaA==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2" + "@jest/get-type": "30.1.0", + "@jest/types": "30.0.5", + "camelcase": "^6.3.0", + "chalk": "^4.1.2", + "leven": "^3.1.0", + "pretty-format": "30.0.5" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, + "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "node_modules/jest-watcher": { + "version": "30.1.1", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-30.1.1.tgz", + "integrity": "sha512-CrAQ73LlaS6KGQQw6NBi71g7qvP7scy+4+2c0jKX6+CWaYg85lZiig5nQQVTsS5a5sffNPL3uxXnaE9d7v9eQg==", "dev": true, + "license": "MIT", "dependencies": { - "has-symbols": "^1.0.2" + "@jest/test-result": "30.1.1", + "@jest/types": "30.0.5", + "@types/node": "*", + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "emittery": "^0.13.1", + "jest-util": "30.0.5", + "string-length": "^4.0.2" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/is-typed-array": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", - "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", + "node_modules/jest-worker": { + "version": "30.1.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.1.0.tgz", + "integrity": "sha512-uvWcSjlwAAgIu133Tt77A05H7RIk3Ho8tZL50bQM2AkvLdluw9NG48lRCl3Dt+MOH719n/0nnb5YxUwcuJiKRA==", "dev": true, + "license": "MIT", "dependencies": { - "which-typed-array": "^1.1.11" + "@types/node": "*", + "@ungap/structured-clone": "^1.3.0", + "jest-util": "30.0.5", + "merge-stream": "^2.0.0", + "supports-color": "^8.1.1" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2" + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -2505,40 +6879,105 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsdom": { + "version": "26.1.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.1.0.tgz", + "integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssstyle": "^4.2.1", + "data-urls": "^5.0.0", + "decimal.js": "^10.5.0", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.6", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.16", + "parse5": "^7.2.1", + "rrweb-cssom": "^0.8.0", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^5.1.1", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.1.1", + "ws": "^8.18.0", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, - "dependencies": { - "minimist": "^1.2.0" - }, + "license": "MIT", "bin": { "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" } }, "node_modules/keyv": { @@ -2546,15 +6985,27 @@ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, + "license": "MIT", "dependencies": { "json-buffer": "3.0.1" } }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, + "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -2563,24 +7014,33 @@ "node": ">= 0.8.0" } }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, "node_modules/linkify-it": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-4.0.1.tgz", - "integrity": "sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "license": "MIT", "dependencies": { - "uc.micro": "^1.0.1" + "uc.micro": "^2.0.0" } }, "node_modules/livereload": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/livereload/-/livereload-0.9.3.tgz", - "integrity": "sha512-q7Z71n3i4X0R9xthAryBdNGVGAO2R5X+/xXpmKeuPMrteg+W2U8VusTKV3YiJbXZwKsOlFlHe+go6uSNjfxrZw==", + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/livereload/-/livereload-0.10.3.tgz", + "integrity": "sha512-llSb8HrtSH7ByPFMc8WTTeW3oy++smwgSA8JVGzEn8KiDPESq6jt1M4ZKKkhKTrhn2wvUOadQq4ip10E5daZ3w==", "dev": true, + "license": "MIT", "dependencies": { - "chokidar": "^3.5.0", - "livereload-js": "^3.3.1", - "opts": ">= 1.2.0", - "ws": "^7.4.3" + "chokidar": "^4.0.3", + "livereload-js": "^4.0.2", + "opts": "^2.0.2", + "ws": "^8.4.3" }, "bin": { "livereload": "bin/livereload.js" @@ -2590,16 +7050,48 @@ } }, "node_modules/livereload-js": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/livereload-js/-/livereload-js-3.4.1.tgz", - "integrity": "sha512-5MP0uUeVCec89ZbNOT/i97Mc+q3SxXmiUGhRFOTmhrGPn//uWVQdCvcLJDy64MSBR5MidFdOR7B9viumoavy6g==", - "dev": true + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/livereload-js/-/livereload-js-4.0.2.tgz", + "integrity": "sha512-Fy7VwgQNiOkynYyNBTo3v9hQUhcW5pFAheJN148+DTgpShjsy/22pLHKKwDK5v0kOsZsJBK+6q1PMgLvRmrwFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/livereload/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/livereload/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } }, "node_modules/load-json-file": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", "dev": true, + "license": "MIT", "dependencies": { "graceful-fs": "^4.1.2", "parse-json": "^4.0.0", @@ -2610,63 +7102,168 @@ "node": ">=4" } }, + "node_modules/load-json-file/node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dev": true, + "license": "MIT", + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/load-json-file/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, + "license": "MIT", "dependencies": { "p-locate": "^5.0.0" }, "engines": { "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.throttle": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", + "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, - "node_modules/lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", - "dev": true - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" }, - "node_modules/lodash.throttle": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", - "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==", - "dev": true + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tmpl": "1.0.5" + } }, "node_modules/markdown-it": { - "version": "13.0.2", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-13.0.2.tgz", - "integrity": "sha512-FtwnEuuK+2yVU7goGn/MJ0WBZMM9ZPgU9spqlFs7/A/pDIUNSOQZhUgOqYCficIuR2QaFnrt8LHqBWsbTAoI5w==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "license": "MIT", "dependencies": { "argparse": "^2.0.1", - "entities": "~3.0.1", - "linkify-it": "^4.0.1", - "mdurl": "^1.0.1", - "uc.micro": "^1.0.5" + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" }, "bin": { - "markdown-it": "bin/markdown-it.js" + "markdown-it": "bin/markdown-it.mjs" } }, "node_modules/markdown-it-task-lists": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/markdown-it-task-lists/-/markdown-it-task-lists-2.1.1.tgz", - "integrity": "sha512-TxFAc76Jnhb2OUu+n3yz9RMu4CwGfaT788br6HhEDlvWfdeJcLUsxk1Hgw2yJio0OXsxv7pyIPmvECY7bMbluA==" + "integrity": "sha512-TxFAc76Jnhb2OUu+n3yz9RMu4CwGfaT788br6HhEDlvWfdeJcLUsxk1Hgw2yJio0OXsxv7pyIPmvECY7bMbluA==", + "license": "ISC" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } }, "node_modules/mdurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "license": "MIT" }, "node_modules/memorystream": { "version": "0.3.1", @@ -2677,11 +7274,42 @@ "node": ">= 0.10.0" } }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -2694,33 +7322,93 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/napi-postinstall": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.3.tgz", + "integrity": "sha512-uTp172LLXSxuSYHv/kou+f6KW3SMppU9ivthaVTXian9sOt3XM/zHYHpRZiLgQoxeWfYUnslNWQHF1+G71xcow==", + "dev": true, + "license": "MIT", + "bin": { + "napi-postinstall": "lib/cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/napi-postinstall" + } }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true + "dev": true, + "license": "MIT" + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT" }, "node_modules/nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true + "dev": true, + "license": "MIT" + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true, + "license": "MIT" }, "node_modules/normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "hosted-git-info": "^2.1.4", "resolve": "^1.10.0", @@ -2733,6 +7421,7 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver" } @@ -2742,6 +7431,7 @@ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -2751,6 +7441,7 @@ "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^3.2.1", "chalk": "^2.4.1", @@ -2776,6 +7467,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^1.9.0" }, @@ -2788,6 +7480,7 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -2802,6 +7495,7 @@ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, + "license": "MIT", "dependencies": { "color-name": "1.1.3" } @@ -2810,13 +7504,15 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/npm-run-all/node_modules/cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", + "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", "dev": true, + "license": "MIT", "dependencies": { "nice-try": "^1.0.4", "path-key": "^2.0.1", @@ -2833,6 +7529,7 @@ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.8.0" } @@ -2842,6 +7539,7 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } @@ -2851,6 +7549,7 @@ "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } @@ -2860,6 +7559,7 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver" } @@ -2869,6 +7569,7 @@ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", "dev": true, + "license": "MIT", "dependencies": { "shebang-regex": "^1.0.0" }, @@ -2881,6 +7582,7 @@ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -2890,6 +7592,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^3.0.0" }, @@ -2902,6 +7605,7 @@ "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dev": true, + "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, @@ -2909,11 +7613,35 @@ "which": "bin/which" } }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nwsapi": { + "version": "2.2.21", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.21.tgz", + "integrity": "sha512-o6nIY3qwiSXl7/LuOU0Dmuctd34Yay0yeuZRLFmDPrrdHpXKFndPj3hM+YEPVHYC5fx2otBx4Ilc/gyYSAUaIA==", + "dev": true, + "license": "MIT" + }, "node_modules/object-inspect": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", - "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -2923,19 +7651,23 @@ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" } }, "node_modules/object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "has-symbols": "^1.0.3", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", "object-keys": "^1.1.1" }, "engines": { @@ -2945,29 +7677,17 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/object.entries": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.7.tgz", - "integrity": "sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/object.fromentries": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz", - "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -2977,26 +7697,31 @@ } }, "node_modules/object.groupby": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.1.tgz", - "integrity": "sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" + }, + "engines": { + "node": ">= 0.4" } }, "node_modules/object.values": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz", - "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -3010,22 +7735,40 @@ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, + "license": "ISC", "dependencies": { "wrappy": "1" } }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, + "license": "MIT", "dependencies": { - "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" }, "engines": { "node": ">= 0.8.0" @@ -3035,13 +7778,33 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/opts/-/opts-2.0.2.tgz", "integrity": "sha512-k41FwbcLnlgnFh69f4qdUfvDQ+5vaSDnVPFI/y5XuhKRq97EnVVneO9F1ESVCdiVu4fCS2L8usX3mU331hB7pg==", - "dev": true + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, + "license": "MIT", "dependencies": { "yocto-queue": "^0.1.0" }, @@ -3057,6 +7820,7 @@ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, + "license": "MIT", "dependencies": { "p-limit": "^3.0.2" }, @@ -3072,15 +7836,24 @@ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, + "license": "MIT", "dependencies": { "callsites": "^3.0.0" }, @@ -3089,16 +7862,48 @@ } }, "node_modules/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "dev": true, + "license": "MIT", "dependencies": { + "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" }, "engines": { - "node": ">=4" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" } }, "node_modules/path-exists": { @@ -3106,6 +7911,7 @@ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -3115,6 +7921,7 @@ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -3124,6 +7931,7 @@ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -3132,13 +7940,39 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true + "dev": true, + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" }, "node_modules/path-type": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", "dev": true, + "license": "MIT", "dependencies": { "pify": "^3.0.0" }, @@ -3146,11 +7980,17 @@ "node": ">=4" } }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, + "license": "MIT", "engines": { "node": ">=8.6" }, @@ -3163,6 +8003,7 @@ "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", "dev": true, + "license": "MIT", "bin": { "pidtree": "bin/pidtree.js" }, @@ -3175,53 +8016,184 @@ "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8.0" } }, + "node_modules/pretty-format": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.0.5.tgz", + "integrity": "sha512-D1tKtYvByrBkFLe2wHJl2bwMJIiT8rW+XA+TiataH79/FszLQMrpGEvzUVkzPau7OCO0Qnrhpe87PqtOAIB8Yw==", + "license": "MIT", + "dependencies": { + "@jest/schemas": "30.0.5", + "ansi-styles": "^5.2.0", + "react-is": "^18.3.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pure-rand": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-7.0.1.tgz", + "integrity": "sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==", "dev": true, "funding": [ { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" }, { - "type": "consulting", - "url": "https://feross.org/support" + "type": "opencollective", + "url": "https://opencollective.com/fast-check" } - ] + ], + "license": "MIT" + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "license": "MIT" }, "node_modules/read-pkg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", "dev": true, + "license": "MIT", "dependencies": { "load-json-file": "^4.0.0", "normalize-package-data": "^2.3.2", @@ -3236,6 +8208,7 @@ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, + "license": "MIT", "dependencies": { "picomatch": "^2.2.1" }, @@ -3243,15 +8216,42 @@ "node": ">=8.10.0" } }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/regexp.prototype.flags": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", - "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "set-function-name": "^2.0.0" + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -3265,6 +8265,7 @@ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -3273,122 +8274,141 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", "dev": true, + "license": "MIT", "dependencies": { - "is-core-module": "^2.13.0", + "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "node_modules/rrweb-cssom": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", + "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", "dev": true, - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } + "license": "MIT" }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "node_modules/safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", "dev": true, + "license": "MIT", "dependencies": { - "glob": "^7.1.3" + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" }, - "bin": { - "rimraf": "bin.js" + "engines": { + "node": ">=0.4" }, "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/safe-array-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz", - "integrity": "sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==", + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1", - "has-symbols": "^1.0.3", + "es-errors": "^1.3.0", "isarray": "^2.0.5" }, "engines": { - "node": ">=0.4" + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/safe-regex-test": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", - "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "is-regex": "^1.1.4" + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, "node_modules/sass": { - "version": "1.69.5", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.69.5.tgz", - "integrity": "sha512-qg2+UCJibLr2LCVOt3OlPhr/dqVHWOa9XtZf2OjbLs/T4VPSJ00udtgJxH3neXZm+QqX8B+3cU7RaLqp1iVfcQ==", + "version": "1.91.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.91.0.tgz", + "integrity": "sha512-aFOZHGf+ur+bp1bCHZ+u8otKGh77ZtmFyXDo4tlYvT7PWql41Kwd8wdkPqhhT+h2879IVblcHFglIMofsFd1EA==", "dev": true, + "license": "MIT", "dependencies": { - "chokidar": ">=3.0.0 <4.0.0", - "immutable": "^4.0.0", + "chokidar": "^4.0.0", + "immutable": "^5.0.2", "source-map-js": ">=0.6.2 <2.0.0" }, "bin": { @@ -3396,6 +8416,52 @@ }, "engines": { "node": ">=14.0.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" + } + }, + "node_modules/sass/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/sass/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" } }, "node_modules/semver": { @@ -3403,6 +8469,7 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" } @@ -3411,32 +8478,53 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/set-function-length": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", - "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "dev": true, + "license": "MIT", "dependencies": { - "define-data-property": "^1.1.1", - "get-intrinsic": "^1.2.1", + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" + "has-property-descriptors": "^1.0.2" }, "engines": { "node": ">= 0.4" } }, "node_modules/set-function-name": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", - "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", "dev": true, + "license": "MIT", "dependencies": { - "define-data-property": "^1.0.1", + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.0" + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -3447,6 +8535,7 @@ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, + "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" }, @@ -3459,99 +8548,346 @@ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", + "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/snabbdom": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/snabbdom/-/snabbdom-3.6.2.tgz", + "integrity": "sha512-ig5qOnCDbugFntKi6c7Xlib8bA6xiJVk8O+WdFrV3wxbMqeHO0hXFQC4nAhPVWfZfi8255lcZkNhtIBINCc4+Q==", + "license": "MIT", + "engines": { + "node": ">=12.17.0" + } + }, + "node_modules/sortablejs": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.6.tgz", + "integrity": "sha512-aNfiuwMEpfBM/CN6LY0ibyhxPfPbyFeBTYJKCvzkJ2GkUpazIt3H+QIPAMHwqQ7tMKaHz1Qj+rJJCqljnf4p3A==", + "license": "MIT" + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.21", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz", + "integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-length/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-length/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, "engines": { "node": ">=8" } }, - "node_modules/shell-quote": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", - "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", + "node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "license": "MIT", + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" } }, - "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/snabbdom": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/snabbdom/-/snabbdom-3.5.1.tgz", - "integrity": "sha512-wHMNIOjkm/YNE5EM3RCbr/+DVgPg6AqQAX1eOxO46zYNvCXjKP5Y865tqQj3EXnaMBjkxmQA5jFuDpDK/dbfiA==", "engines": { - "node": ">=8.3.0" + "node": ">=8" } }, - "node_modules/sortablejs": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.1.tgz", - "integrity": "sha512-P5Cjvb0UG1ZVNiDPj/n4V+DinttXG6K8n7vM/HQf0C25K3YKQTQY6fsr/sEGsJGpQ9exmPxluHxKBc0mLKU1lQ==" - }, - "node_modules/source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/spdx-correct": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", - "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true, - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true + "license": "MIT" }, - "node_modules/spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "node_modules/string-width-cjs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" + "license": "MIT", + "engines": { + "node": ">=8" } }, - "node_modules/spdx-license-ids": { - "version": "3.0.16", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.16.tgz", - "integrity": "sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw==", - "dev": true - }, - "node_modules/string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, + "license": "MIT", "dependencies": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">=6" + "node": ">=8" } }, "node_modules/string-width/node_modules/ansi-regex": { @@ -3559,6 +8895,7 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -3568,6 +8905,7 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^4.1.0" }, @@ -3576,14 +8914,16 @@ } }, "node_modules/string.prototype.padend": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.5.tgz", - "integrity": "sha512-DOB27b/2UTTD+4myKUFh+/fXWcu/UDyASIXfg+7VzoCNNGOfWvoyU/x5pvVHr++ztyt/oSYI1BcWBBG/hmlNjA==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.6.tgz", + "integrity": "sha512-XZpspuSB7vJWhvJc9DLSlrXl1mcA2BdoY5jjnS135ydXqLoqhs96JjDtCkjJEQHvfqZIp9hBuBMgI589peyx9Q==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -3593,14 +8933,19 @@ } }, "node_modules/string.prototype.trim": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", - "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -3610,38 +8955,65 @@ } }, "node_modules/string.prototype.trimend": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", - "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/string.prototype.trimstart": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", - "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -3649,13 +9021,34 @@ "node": ">=8" } }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", "dev": true, + "license": "MIT", "engines": { - "node": ">=4" + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" } }, "node_modules/strip-json-comments": { @@ -3663,6 +9056,7 @@ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" }, @@ -3671,15 +9065,16 @@ } }, "node_modules/style-mod": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.0.tgz", - "integrity": "sha512-Ca5ib8HrFn+f+0n4N4ScTIA9iTOQ7MaGS1ylHcoVqW9J7w2w8PzN6g9gKmTYgGEBH8e120+RCmhpje6jC5uGWA==" + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz", + "integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==", + "license": "MIT" }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -3692,6 +9087,7 @@ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -3699,41 +9095,304 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT" + }, + "node_modules/synckit": { + "version": "0.11.11", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz", + "integrity": "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pkgr/core": "^0.2.9" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/synckit" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/tldts": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz", + "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^6.1.86" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz", + "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tough-cookie": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", + "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^6.1.32" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tr46": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/ts-jest": { + "version": "29.4.1", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.1.tgz", + "integrity": "sha512-SaeUtjfpg9Uqu8IbeDKtdaS0g8lS6FT6OzM3ezrDfErPJPHNDo/Ey+VFGP1bQIDfagYDLyRpd7O15XpG1Es2Uw==", + "dev": true, + "license": "MIT", + "dependencies": { + "bs-logger": "^0.2.6", + "fast-json-stable-stringify": "^2.1.0", + "handlebars": "^4.7.8", + "json5": "^2.2.3", + "lodash.memoize": "^4.1.2", + "make-error": "^1.3.6", + "semver": "^7.7.2", + "type-fest": "^4.41.0", + "yargs-parser": "^21.1.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/transform": "^29.0.0 || ^30.0.0", + "@jest/types": "^29.0.0 || ^30.0.0", + "babel-jest": "^29.0.0 || ^30.0.0", + "jest": "^29.0.0 || ^30.0.0", + "jest-util": "^29.0.0 || ^30.0.0", + "typescript": ">=4.3 <6" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/transform": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jest-util": { + "optional": true + } + } + }, + "node_modules/ts-jest/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ts-jest/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/tsconfig-paths/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, + "license": "MIT", "engines": { - "node": ">=8.0" + "node": ">=4" } }, - "node_modules/tsconfig-paths": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", - "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==", + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "dev": true, - "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - } + "license": "0BSD", + "optional": true }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, + "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1" }, @@ -3741,11 +9400,22 @@ "node": ">= 0.8.0" } }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true, + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" }, @@ -3754,29 +9424,32 @@ } }, "node_modules/typed-array-buffer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", - "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1", - "is-typed-array": "^1.1.10" + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" }, "engines": { "node": ">= 0.4" } }, "node_modules/typed-array-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", - "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", + "call-bind": "^1.0.8", "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" }, "engines": { "node": ">= 0.4" @@ -3786,16 +9459,19 @@ } }, "node_modules/typed-array-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", - "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", "dev": true, + "license": "MIT", "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" }, "engines": { "node": ">= 0.4" @@ -3805,53 +9481,189 @@ } }, "node_modules/typed-array-length": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", - "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", + "call-bind": "^1.0.7", "for-each": "^0.3.3", - "is-typed-array": "^1.1.9" + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/typescript": { + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", + "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/uc.micro": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", - "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "license": "MIT" + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } }, "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", + "call-bound": "^1.0.3", "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/undici-types": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", + "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", + "license": "MIT" + }, + "node_modules/unrs-resolver": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz", + "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "napi-postinstall": "^0.3.0" + }, + "funding": { + "url": "https://opencollective.com/unrs-resolver" + }, + "optionalDependencies": { + "@unrs/resolver-binding-android-arm-eabi": "1.11.1", + "@unrs/resolver-binding-android-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-x64": "1.11.1", + "@unrs/resolver-binding-freebsd-x64": "1.11.1", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", + "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-musl": "1.11.1", + "@unrs/resolver-binding-wasm32-wasi": "1.11.1", + "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", + "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", + "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" } }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, + "license": "MIT" + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, "node_modules/validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", "dev": true, + "license": "Apache-2.0", "dependencies": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" @@ -3860,13 +9672,85 @@ "node_modules/w3c-keyname": { "version": "2.2.8", "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", - "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==" + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", + "license": "MIT" + }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-url": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, + "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, @@ -3878,16 +9762,67 @@ } }, "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", "dev": true, + "license": "MIT", "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3897,19 +9832,23 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/which-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz", - "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==", + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", "dev": true, + "license": "MIT", "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.4", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -3918,11 +9857,29 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, + "license": "MIT" + }, "node_modules/wrap-ansi": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^3.2.0", "string-width": "^3.0.0", @@ -3932,11 +9889,86 @@ "node": ">=6" } }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/wrap-ansi/node_modules/ansi-regex": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -3946,6 +9978,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^1.9.0" }, @@ -3958,6 +9991,7 @@ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, + "license": "MIT", "dependencies": { "color-name": "1.1.3" } @@ -3966,13 +10000,15 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/wrap-ansi/node_modules/strip-ansi": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^4.1.0" }, @@ -3984,19 +10020,48 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "dev": true, + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/write-file-atomic/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, "node_modules/ws": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", "dev": true, + "license": "MIT", "engines": { - "node": ">=8.3.0" + "node": ">=10.0.0" }, "peerDependencies": { "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" + "utf-8-validate": ">=5.0.2" }, "peerDependenciesMeta": { "bufferutil": { @@ -4007,17 +10072,43 @@ } } }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT" + }, "node_modules/y18n": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true + "dev": true, + "license": "ISC" + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" }, "node_modules/yargs": { "version": "13.3.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", "dev": true, + "license": "MIT", "dependencies": { "cliui": "^5.0.0", "find-up": "^3.0.0", @@ -4032,13 +10123,13 @@ } }, "node_modules/yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" + "license": "ISC", + "engines": { + "node": ">=12" } }, "node_modules/yargs/node_modules/find-up": { @@ -4046,6 +10137,7 @@ "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "dev": true, + "license": "MIT", "dependencies": { "locate-path": "^3.0.0" }, @@ -4058,6 +10150,7 @@ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", "dev": true, + "license": "MIT", "dependencies": { "p-locate": "^3.0.0", "path-exists": "^3.0.0" @@ -4071,6 +10164,7 @@ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, + "license": "MIT", "dependencies": { "p-try": "^2.0.0" }, @@ -4086,6 +10180,7 @@ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "dev": true, + "license": "MIT", "dependencies": { "p-limit": "^2.0.0" }, @@ -4098,15 +10193,38 @@ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, + "node_modules/yargs/node_modules/yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, diff --git a/package.json b/package.json index ca5f6907156..9ec94acc29a 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "build:css:watch": "sass ./resources/sass:./public/dist --watch --embed-sources", "build:css:production": "sass ./resources/sass:./public/dist -s compressed", "build:js:dev": "node dev/build/esbuild.js", - "build:js:watch": "chokidar --initial \"./resources/**/*.js\" \"./resources/**/*.mjs\" -c \"npm run build:js:dev\"", + "build:js:watch": "chokidar --initial \"./resources/**/*.js\" \"./resources/**/*.mjs\" \"./resources/**/*.ts\" -c \"npm run build:js:dev\"", "build:js:production": "node dev/build/esbuild.js production", "build": "npm-run-all --parallel build:*:dev", "production": "npm-run-all --parallel build:*:production", @@ -14,122 +14,51 @@ "livereload": "livereload ./public/dist/", "permissions": "chown -R $USER:$USER bootstrap/cache storage public/uploads", "lint": "eslint \"resources/**/*.js\" \"resources/**/*.mjs\"", - "fix": "eslint --fix \"resources/**/*.js\" \"resources/**/*.mjs\"" + "fix": "eslint --fix \"resources/**/*.js\" \"resources/**/*.mjs\"", + "ts:lint": "tsc --noEmit", + "test": "jest" }, "devDependencies": { - "@lezer/generator": "^1.5.1", + "@eslint/js": "^9.34.0", + "@lezer/generator": "^1.8.0", + "@types/markdown-it": "^14.1.2", + "@types/sortablejs": "^1.15.8", "chokidar-cli": "^3.0", - "esbuild": "^0.19", - "eslint": "^8.55.0", - "eslint-config-airbnb-base": "^15.0.0", - "eslint-plugin-import": "^2.29.0", - "livereload": "^0.9.3", + "esbuild": "^0.25.9", + "eslint": "^9.34.0", + "eslint-plugin-import": "^2.32.0", + "jest": "^30.1.1", + "jest-environment-jsdom": "^30.1.1", + "livereload": "^0.10.3", "npm-run-all": "^4.1.5", - "punycode": "^2.3.1", - "sass": "^1.69.5" + "sass": "^1.91.0", + "ts-jest": "^29.4.1", + "ts-node": "^10.9.2", + "typescript": "5.9.*" }, "dependencies": { - "@codemirror/commands": "^6.3.2", - "@codemirror/lang-css": "^6.2.1", - "@codemirror/lang-html": "^6.4.7", - "@codemirror/lang-javascript": "^6.2.1", - "@codemirror/lang-json": "^6.0.1", - "@codemirror/lang-markdown": "^6.2.3", - "@codemirror/lang-php": "^6.0.1", - "@codemirror/lang-xml": "^6.0.2", - "@codemirror/language": "^6.9.3", - "@codemirror/legacy-modes": "^6.3.3", - "@codemirror/state": "^6.3.3", - "@codemirror/theme-one-dark": "^6.1.2", - "@codemirror/view": "^6.22.2", - "@lezer/highlight": "^1.2.0", + "@codemirror/commands": "^6.8.1", + "@codemirror/lang-css": "^6.3.1", + "@codemirror/lang-html": "^6.4.9", + "@codemirror/lang-javascript": "^6.2.4", + "@codemirror/lang-json": "^6.0.2", + "@codemirror/lang-markdown": "^6.3.4", + "@codemirror/lang-php": "^6.0.2", + "@codemirror/lang-xml": "^6.1.0", + "@codemirror/language": "^6.11.3", + "@codemirror/legacy-modes": "^6.5.1", + "@codemirror/state": "^6.5.2", + "@codemirror/theme-one-dark": "^6.1.3", + "@codemirror/view": "^6.38.1", + "@lezer/highlight": "^1.2.1", "@ssddanbrown/codemirror-lang-smarty": "^1.0.0", "@ssddanbrown/codemirror-lang-twig": "^1.0.0", - "codemirror": "^6.0.1", - "idb-keyval": "^6.2.1", - "markdown-it": "^13.0.2", + "@types/jest": "^30.0.0", + "codemirror": "^6.0.2", + "idb-keyval": "^6.2.2", + "markdown-it": "^14.1.0", "markdown-it-task-lists": "^2.1.1", - "snabbdom": "^3.5.1", - "sortablejs": "^1.15.1" - }, - "eslintConfig": { - "root": true, - "env": { - "browser": true, - "es2021": true - }, - "extends": "airbnb-base", - "ignorePatterns": [ - "resources/**/*-stub.js" - ], - "overrides": [], - "parserOptions": { - "ecmaVersion": "latest", - "sourceType": "module" - }, - "rules": { - "indent": [ - "error", - 4 - ], - "arrow-parens": [ - "error", - "as-needed" - ], - "padded-blocks": [ - "error", - { - "blocks": "never", - "classes": "always" - } - ], - "object-curly-spacing": [ - "error", - "never" - ], - "space-before-function-paren": [ - "error", - { - "anonymous": "never", - "named": "never", - "asyncArrow": "always" - } - ], - "import/prefer-default-export": "off", - "no-plusplus": [ - "error", - { - "allowForLoopAfterthoughts": true - } - ], - "arrow-body-style": "off", - "no-restricted-syntax": "off", - "no-continue": "off", - "prefer-destructuring": "off", - "class-methods-use-this": "off", - "no-param-reassign": "off", - "no-console": [ - "warn", - { - "allow": [ - "error", - "warn" - ] - } - ], - "no-new": "off", - "max-len": [ - "error", - { - "code": 110, - "tabWidth": 4, - "ignoreUrls": true, - "ignoreComments": false, - "ignoreRegExpLiterals": true, - "ignoreStrings": true, - "ignoreTemplateLiterals": true - } - ] - } + "snabbdom": "^3.6.2", + "sortablejs": "^1.15.6" } } diff --git a/phpcs.xml b/phpcs.xml index 8d4c6b70208..8337d5aac50 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -14,7 +14,7 @@ - + ./tests/* diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 5fec36c2f08..72189222fcf 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -7,9 +7,11 @@ parameters: - app # The level 8 is the highest level - level: 1 + level: 3 - phpVersion: 80002 + phpVersion: + min: 80200 + max: 80400 bootstrapFiles: - bootstrap/phpstan.php @@ -19,5 +21,4 @@ parameters: excludePaths: - ./Config/**/*.php - - checkMissingIterableValueType: false \ No newline at end of file + - ./dev/**/*.php \ No newline at end of file diff --git a/phpunit.xml b/phpunit.xml index 183e456378c..8a7ab9cb7a3 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,13 +1,9 @@ - - - app/ - - ./tests/ @@ -20,7 +16,10 @@ + + + @@ -28,6 +27,7 @@ + @@ -59,7 +59,13 @@ + + + + app/ + + diff --git a/public/book_default_cover.png b/public/book_default_cover.png index 7b6c9953e91..6d088d8b82c 100644 Binary files a/public/book_default_cover.png and b/public/book_default_cover.png differ diff --git a/public/icon-128.png b/public/icon-128.png index 46cc2811b5e..f271e849d20 100644 Binary files a/public/icon-128.png and b/public/icon-128.png differ diff --git a/public/icon-180.png b/public/icon-180.png index 6533a05b3ec..4d4ceac3486 100644 Binary files a/public/icon-180.png and b/public/icon-180.png differ diff --git a/public/icon-32.png b/public/icon-32.png index 7307ed8f1e8..deac72d893e 100644 Binary files a/public/icon-32.png and b/public/icon-32.png differ diff --git a/public/icon-64.png b/public/icon-64.png index 854d80faacb..afe4b766dba 100644 Binary files a/public/icon-64.png and b/public/icon-64.png differ diff --git a/public/icon.ico b/public/icon.ico index 41655ccba55..2581b9b335f 100644 Binary files a/public/icon.ico and b/public/icon.ico differ diff --git a/public/icon.png b/public/icon.png index b9f0125a856..939b97e4ff0 100644 Binary files a/public/icon.png and b/public/icon.png differ diff --git a/public/index.php b/public/index.php index fdf6e720f53..2b5c57b8f40 100644 --- a/public/index.php +++ b/public/index.php @@ -5,45 +5,16 @@ define('LARAVEL_START', microtime(true)); -/* -|-------------------------------------------------------------------------- -| Check If The Application Is Under Maintenance -|-------------------------------------------------------------------------- -| -| If the application is in maintenance / demo mode via the "down" command -| we will load this file so that any pre-rendered content can be shown -| instead of starting the framework, which could cause an exception. -| -*/ - +// Determine if the application is in maintenance mode... if (file_exists(__DIR__ . '/../storage/framework/maintenance.php')) { require __DIR__ . '/../storage/framework/maintenance.php'; } -/* -|-------------------------------------------------------------------------- -| Register The Auto Loader -|-------------------------------------------------------------------------- -| -| Composer provides a convenient, automatically generated class loader for -| this application. We just need to utilize it! We'll simply require it -| into the script here so we don't need to manually load our classes. -| -*/ - +// Register the Composer autoloader... require __DIR__ . '/../vendor/autoload.php'; -/* -|-------------------------------------------------------------------------- -| Run The Application -|-------------------------------------------------------------------------- -| -| Once we have the application, we can handle the incoming request using -| the application's HTTP kernel. Then, we will send the response back -| to this client's browser, allowing them to enjoy our application. -| -*/ +// Run the application $app = require_once __DIR__ . '/../bootstrap/app.php'; $app->alias('request', Request::class); diff --git a/public/libs/tinymce/models/dom/model.min.js b/public/libs/tinymce/models/dom/model.min.js index a6b5053210f..cbd12878816 100644 --- a/public/libs/tinymce/models/dom/model.min.js +++ b/public/libs/tinymce/models/dom/model.min.js @@ -1,4 +1,4 @@ /** - * TinyMCE version 6.7.2 (2023-10-25) + * TinyMCE version 6.8.4 (2024-06-19) */ !function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.ModelManager");const t=e=>t=>(e=>{const t=typeof e;return null===e?"null":"object"===t&&Array.isArray(e)?"array":"object"===t&&(o=n=e,(r=String).prototype.isPrototypeOf(o)||(null===(s=n.constructor)||void 0===s?void 0:s.name)===r.name)?"string":t;var o,n,r,s})(t)===e,o=e=>t=>typeof t===e,n=e=>t=>e===t,r=t("string"),s=t("object"),l=t("array"),a=n(null),c=o("boolean"),i=n(void 0),m=e=>!(e=>null==e)(e),d=o("function"),u=o("number"),f=()=>{},g=e=>()=>e,h=e=>e,p=(e,t)=>e===t;function w(e,...t){return(...o)=>{const n=t.concat(o);return e.apply(null,n)}}const b=e=>t=>!e(t),v=e=>e(),y=g(!1),x=g(!0);class C{constructor(e,t){this.tag=e,this.value=t}static some(e){return new C(!0,e)}static none(){return C.singletonNone}fold(e,t){return this.tag?t(this.value):e()}isSome(){return this.tag}isNone(){return!this.tag}map(e){return this.tag?C.some(e(this.value)):C.none()}bind(e){return this.tag?e(this.value):C.none()}exists(e){return this.tag&&e(this.value)}forall(e){return!this.tag||e(this.value)}filter(e){return!this.tag||e(this.value)?this:C.none()}getOr(e){return this.tag?this.value:e}or(e){return this.tag?this:e}getOrThunk(e){return this.tag?this.value:e()}orThunk(e){return this.tag?this:e()}getOrDie(e){if(this.tag)return this.value;throw new Error(null!=e?e:"Called getOrDie on None")}static from(e){return m(e)?C.some(e):C.none()}getOrNull(){return this.tag?this.value:null}getOrUndefined(){return this.value}each(e){this.tag&&e(this.value)}toArray(){return this.tag?[this.value]:[]}toString(){return this.tag?`some(${this.value})`:"none()"}}C.singletonNone=new C(!1);const S=Array.prototype.slice,T=Array.prototype.indexOf,R=Array.prototype.push,D=(e,t)=>{return o=e,n=t,T.call(o,n)>-1;var o,n},O=(e,t)=>{for(let o=0,n=e.length;o{const o=[];for(let n=0;n{const o=e.length,n=new Array(o);for(let r=0;r{for(let o=0,n=e.length;o{const o=[],n=[];for(let r=0,s=e.length;r{const o=[];for(let n=0,r=e.length;n(((e,t)=>{for(let o=e.length-1;o>=0;o--)t(e[o],o)})(e,((e,n)=>{o=t(o,e,n)})),o),A=(e,t,o)=>(N(e,((e,n)=>{o=t(o,e,n)})),o),L=(e,t)=>((e,t,o)=>{for(let n=0,r=e.length;n{for(let o=0,n=e.length;o{const t=[];for(let o=0,n=e.length;oM(E(e,t)),P=(e,t)=>{for(let o=0,n=e.length;o{const o={};for(let n=0,r=e.length;nt>=0&&tF(e,0),$=e=>F(e,e.length-1),V=(e,t)=>{for(let o=0;o{const o=q(e);for(let n=0,r=o.length;nY(e,((e,o)=>({k:o,v:t(e,o)}))),Y=(e,t)=>{const o={};return G(e,((e,n)=>{const r=t(e,n);o[r.k]=r.v})),o},J=(e,t)=>{const o=[];return G(e,((e,n)=>{o.push(t(e,n))})),o},Q=e=>J(e,h),X=(e,t)=>U.call(e,t),Z="undefined"!=typeof window?window:Function("return this;")(),ee=(e,t)=>((e,t)=>{let o=null!=t?t:Z;for(let t=0;t{const t=ee("ownerDocument.defaultView",e);return s(e)&&((e=>((e,t)=>{const o=((e,t)=>ee(e,t))(e,t);if(null==o)throw new Error(e+" not available on this browser");return o})("HTMLElement",e))(t).prototype.isPrototypeOf(e)||/^HTML\w*Element$/.test(te(e).constructor.name))},ne=e=>e.dom.nodeName.toLowerCase(),re=e=>e.dom.nodeType,se=e=>t=>re(t)===e,le=e=>8===re(e)||"#comment"===ne(e),ae=e=>ce(e)&&oe(e.dom),ce=se(1),ie=se(3),me=se(9),de=se(11),ue=e=>t=>ce(t)&&ne(t)===e,fe=(e,t,o)=>{if(!(r(o)||c(o)||u(o)))throw console.error("Invalid call to Attribute.set. Key ",t,":: Value ",o,":: Element ",e),new Error("Attribute value was not simple");e.setAttribute(t,o+"")},ge=(e,t,o)=>{fe(e.dom,t,o)},he=(e,t)=>{const o=e.dom;G(t,((e,t)=>{fe(o,t,e)}))},pe=(e,t)=>{const o=e.dom.getAttribute(t);return null===o?void 0:o},we=(e,t)=>C.from(pe(e,t)),be=(e,t)=>{e.dom.removeAttribute(t)},ve=e=>A(e.dom.attributes,((e,t)=>(e[t.name]=t.value,e)),{}),ye=e=>{if(null==e)throw new Error("Node cannot be null or undefined");return{dom:e}},xe={fromHtml:(e,t)=>{const o=(t||document).createElement("div");if(o.innerHTML=e,!o.hasChildNodes()||o.childNodes.length>1){const t="HTML does not have a single root node";throw console.error(t,e),new Error(t)}return ye(o.childNodes[0])},fromTag:(e,t)=>{const o=(t||document).createElement(e);return ye(o)},fromText:(e,t)=>{const o=(t||document).createTextNode(e);return ye(o)},fromDom:ye,fromPoint:(e,t,o)=>C.from(e.dom.elementFromPoint(t,o)).map(ye)},Ce=(e,t)=>{const o=e.dom;if(1!==o.nodeType)return!1;{const e=o;if(void 0!==e.matches)return e.matches(t);if(void 0!==e.msMatchesSelector)return e.msMatchesSelector(t);if(void 0!==e.webkitMatchesSelector)return e.webkitMatchesSelector(t);if(void 0!==e.mozMatchesSelector)return e.mozMatchesSelector(t);throw new Error("Browser lacks native selectors")}},Se=e=>1!==e.nodeType&&9!==e.nodeType&&11!==e.nodeType||0===e.childElementCount,Te=(e,t)=>{const o=void 0===t?document:t.dom;return Se(o)?C.none():C.from(o.querySelector(e)).map(xe.fromDom)},Re=(e,t)=>e.dom===t.dom,De=(e,t)=>{const o=e.dom,n=t.dom;return o!==n&&o.contains(n)},Oe=Ce,ke=e=>xe.fromDom(e.dom.ownerDocument),Ee=e=>me(e)?e:ke(e),Ne=e=>C.from(e.dom.parentNode).map(xe.fromDom),Be=e=>C.from(e.dom.parentElement).map(xe.fromDom),_e=(e,t)=>{const o=d(t)?t:y;let n=e.dom;const r=[];for(;null!==n.parentNode&&void 0!==n.parentNode;){const e=n.parentNode,t=xe.fromDom(e);if(r.push(t),!0===o(t))break;n=e}return r},ze=e=>C.from(e.dom.previousSibling).map(xe.fromDom),Ae=e=>C.from(e.dom.nextSibling).map(xe.fromDom),Le=e=>E(e.dom.childNodes,xe.fromDom),We=(e,t)=>{const o=e.dom.childNodes;return C.from(o[t]).map(xe.fromDom)},Me=(e,t)=>{Ne(e).each((o=>{o.dom.insertBefore(t.dom,e.dom)}))},je=(e,t)=>{Ae(e).fold((()=>{Ne(e).each((e=>{Ie(e,t)}))}),(e=>{Me(e,t)}))},Pe=(e,t)=>{const o=(e=>We(e,0))(e);o.fold((()=>{Ie(e,t)}),(o=>{e.dom.insertBefore(t.dom,o.dom)}))},Ie=(e,t)=>{e.dom.appendChild(t.dom)},Fe=(e,t)=>{Me(e,t),Ie(t,e)},He=(e,t)=>{N(t,((o,n)=>{const r=0===n?e:t[n-1];je(r,o)}))},$e=(e,t)=>{N(t,(t=>{Ie(e,t)}))},Ve=e=>{e.dom.textContent="",N(Le(e),(e=>{qe(e)}))},qe=e=>{const t=e.dom;null!==t.parentNode&&t.parentNode.removeChild(t)},Ue=e=>{const t=Le(e);t.length>0&&He(e,t),qe(e)},Ge=(e,t)=>xe.fromDom(e.dom.cloneNode(t)),Ke=e=>Ge(e,!1),Ye=e=>Ge(e,!0),Je=(e,t)=>{const o=xe.fromTag(t),n=ve(e);return he(o,n),o},Qe=["tfoot","thead","tbody","colgroup"],Xe=(e,t,o)=>({element:e,rowspan:t,colspan:o}),Ze=(e,t,o)=>({element:e,cells:t,section:o}),et=(e,t,o)=>({element:e,isNew:t,isLocked:o}),tt=(e,t,o,n)=>({element:e,cells:t,section:o,isNew:n}),ot=d(Element.prototype.attachShadow)&&d(Node.prototype.getRootNode),nt=g(ot),rt=ot?e=>xe.fromDom(e.dom.getRootNode()):Ee,st=e=>xe.fromDom(e.dom.host),lt=e=>{const t=ie(e)?e.dom.parentNode:e.dom;if(null==t||null===t.ownerDocument)return!1;const o=t.ownerDocument;return(e=>{const t=rt(e);return de(o=t)&&m(o.dom.host)?C.some(t):C.none();var o})(xe.fromDom(t)).fold((()=>o.body.contains(t)),(n=lt,r=st,e=>n(r(e))));var n,r},at=e=>{const t=e.dom.body;if(null==t)throw new Error("Body is not available yet");return xe.fromDom(t)},ct=(e,t)=>{let o=[];return N(Le(e),(e=>{t(e)&&(o=o.concat([e])),o=o.concat(ct(e,t))})),o},it=(e,t,o)=>((e,o,n)=>_(_e(e,n),(e=>Ce(e,t))))(e,0,o),mt=(e,t)=>((e,o)=>_(Le(e),(e=>Ce(e,t))))(e),dt=(e,t)=>((e,t)=>{const o=void 0===t?document:t.dom;return Se(o)?[]:E(o.querySelectorAll(e),xe.fromDom)})(t,e);var ut=(e,t,o,n,r)=>e(o,n)?C.some(o):d(r)&&r(o)?C.none():t(o,n,r);const ft=(e,t,o)=>{let n=e.dom;const r=d(o)?o:y;for(;n.parentNode;){n=n.parentNode;const e=xe.fromDom(n);if(t(e))return C.some(e);if(r(e))break}return C.none()},gt=(e,t,o)=>ut(((e,t)=>t(e)),ft,e,t,o),ht=(e,t,o)=>ft(e,(e=>Ce(e,t)),o),pt=(e,t)=>((e,o)=>L(e.dom.childNodes,(e=>{return o=xe.fromDom(e),Ce(o,t);var o})).map(xe.fromDom))(e),wt=(e,t)=>Te(t,e),bt=(e,t,o)=>ut(((e,t)=>Ce(e,t)),ht,e,t,o),vt=(e,t,o=p)=>e.exists((e=>o(e,t))),yt=e=>{const t=[],o=e=>{t.push(e)};for(let t=0;te?C.some(t):C.none(),Ct=(e,t,o)=>""===t||e.length>=t.length&&e.substr(o,o+t.length)===t,St=(e,t,o=0,n)=>{const r=e.indexOf(t,o);return-1!==r&&(!!i(n)||r+t.length<=n)},Tt=(e,t)=>Ct(e,t,0),Rt=(e,t)=>Ct(e,t,e.length-t.length),Dt=(e=>t=>t.replace(e,""))(/^\s+|\s+$/g),Ot=e=>e.length>0,kt=e=>void 0!==e.style&&d(e.style.getPropertyValue),Et=(e,t,o)=>{if(!r(o))throw console.error("Invalid call to CSS.set. Property ",t,":: Value ",o,":: Element ",e),new Error("CSS value must be a string: "+o);kt(e)&&e.style.setProperty(t,o)},Nt=(e,t,o)=>{const n=e.dom;Et(n,t,o)},Bt=(e,t)=>{const o=e.dom;G(t,((e,t)=>{Et(o,t,e)}))},_t=(e,t)=>{const o=e.dom,n=window.getComputedStyle(o).getPropertyValue(t);return""!==n||lt(e)?n:zt(o,t)},zt=(e,t)=>kt(e)?e.style.getPropertyValue(t):"",At=(e,t)=>{const o=e.dom,n=zt(o,t);return C.from(n).filter((e=>e.length>0))},Lt=(e,t)=>{((e,t)=>{kt(e)&&e.style.removeProperty(t)})(e.dom,t),vt(we(e,"style").map(Dt),"")&&be(e,"style")},Wt=(e,t,o=0)=>we(e,t).map((e=>parseInt(e,10))).getOr(o),Mt=(e,t)=>Wt(e,t,1),jt=e=>ue("col")(e)?Wt(e,"span",1)>1:Mt(e,"colspan")>1,Pt=e=>Mt(e,"rowspan")>1,It=(e,t)=>parseInt(_t(e,t),10),Ft=g(10),Ht=g(10),$t=(e,t)=>Vt(e,t,x),Vt=(e,t,o)=>j(Le(e),(e=>Ce(e,t)?o(e)?[e]:[]:Vt(e,t,o))),qt=(e,t)=>((e,t,o=y)=>o(t)?C.none():D(e,ne(t))?C.some(t):ht(t,e.join(","),(e=>Ce(e,"table")||o(e))))(["td","th"],e,t),Ut=e=>$t(e,"th,td"),Gt=e=>Ce(e,"colgroup")?mt(e,"col"):j(Jt(e),(e=>mt(e,"col"))),Kt=(e,t)=>bt(e,"table",t),Yt=e=>$t(e,"tr"),Jt=e=>Kt(e).fold(g([]),(e=>mt(e,"colgroup"))),Qt=(e,t)=>E(e,(e=>{if("colgroup"===ne(e)){const t=E(Gt(e),(e=>{const t=Wt(e,"span",1);return Xe(e,1,t)}));return Ze(e,t,"colgroup")}{const o=E(Ut(e),(e=>{const t=Wt(e,"rowspan",1),o=Wt(e,"colspan",1);return Xe(e,t,o)}));return Ze(e,o,t(e))}})),Xt=e=>Ne(e).map((e=>{const t=ne(e);return(e=>D(Qe,e))(t)?t:"tbody"})).getOr("tbody"),Zt=e=>{const t=Yt(e),o=[...Jt(e),...t];return Qt(o,Xt)},eo=e=>{let t,o=!1;return(...n)=>(o||(o=!0,t=e.apply(null,n)),t)},to=()=>oo(0,0),oo=(e,t)=>({major:e,minor:t}),no={nu:oo,detect:(e,t)=>{const o=String(t).toLowerCase();return 0===e.length?to():((e,t)=>{const o=((e,t)=>{for(let o=0;oNumber(t.replace(o,"$"+e));return oo(n(1),n(2))})(e,o)},unknown:to},ro=(e,t)=>{const o=String(t).toLowerCase();return L(e,(e=>e.search(o)))},so=/.*?version\/\ ?([0-9]+)\.([0-9]+).*/,lo=e=>t=>St(t,e),ao=[{name:"Edge",versionRegexes:[/.*?edge\/ ?([0-9]+)\.([0-9]+)$/],search:e=>St(e,"edge/")&&St(e,"chrome")&&St(e,"safari")&&St(e,"applewebkit")},{name:"Chromium",brand:"Chromium",versionRegexes:[/.*?chrome\/([0-9]+)\.([0-9]+).*/,so],search:e=>St(e,"chrome")&&!St(e,"chromeframe")},{name:"IE",versionRegexes:[/.*?msie\ ?([0-9]+)\.([0-9]+).*/,/.*?rv:([0-9]+)\.([0-9]+).*/],search:e=>St(e,"msie")||St(e,"trident")},{name:"Opera",versionRegexes:[so,/.*?opera\/([0-9]+)\.([0-9]+).*/],search:lo("opera")},{name:"Firefox",versionRegexes:[/.*?firefox\/\ ?([0-9]+)\.([0-9]+).*/],search:lo("firefox")},{name:"Safari",versionRegexes:[so,/.*?cpu os ([0-9]+)_([0-9]+).*/],search:e=>(St(e,"safari")||St(e,"mobile/"))&&St(e,"applewebkit")}],co=[{name:"Windows",search:lo("win"),versionRegexes:[/.*?windows\ nt\ ?([0-9]+)\.([0-9]+).*/]},{name:"iOS",search:e=>St(e,"iphone")||St(e,"ipad"),versionRegexes:[/.*?version\/\ ?([0-9]+)\.([0-9]+).*/,/.*cpu os ([0-9]+)_([0-9]+).*/,/.*cpu iphone os ([0-9]+)_([0-9]+).*/]},{name:"Android",search:lo("android"),versionRegexes:[/.*?android\ ?([0-9]+)\.([0-9]+).*/]},{name:"macOS",search:lo("mac os x"),versionRegexes:[/.*?mac\ os\ x\ ?([0-9]+)_([0-9]+).*/]},{name:"Linux",search:lo("linux"),versionRegexes:[]},{name:"Solaris",search:lo("sunos"),versionRegexes:[]},{name:"FreeBSD",search:lo("freebsd"),versionRegexes:[]},{name:"ChromeOS",search:lo("cros"),versionRegexes:[/.*?chrome\/([0-9]+)\.([0-9]+).*/]}],io={browsers:g(ao),oses:g(co)},mo="Edge",uo="Chromium",fo="Opera",go="Firefox",ho="Safari",po=e=>{const t=e.current,o=e.version,n=e=>()=>t===e;return{current:t,version:o,isEdge:n(mo),isChromium:n(uo),isIE:n("IE"),isOpera:n(fo),isFirefox:n(go),isSafari:n(ho)}},wo=()=>po({current:void 0,version:no.unknown()}),bo=po,vo=(g(mo),g(uo),g("IE"),g(fo),g(go),g(ho),"Windows"),yo="Android",xo="Linux",Co="macOS",So="Solaris",To="FreeBSD",Ro="ChromeOS",Do=e=>{const t=e.current,o=e.version,n=e=>()=>t===e;return{current:t,version:o,isWindows:n(vo),isiOS:n("iOS"),isAndroid:n(yo),isMacOS:n(Co),isLinux:n(xo),isSolaris:n(So),isFreeBSD:n(To),isChromeOS:n(Ro)}},Oo=()=>Do({current:void 0,version:no.unknown()}),ko=Do,Eo=(g(vo),g("iOS"),g(yo),g(xo),g(Co),g(So),g(To),g(Ro),e=>window.matchMedia(e).matches);let No=eo((()=>((e,t,o)=>{const n=io.browsers(),r=io.oses(),s=t.bind((e=>((e,t)=>V(t.brands,(t=>{const o=t.brand.toLowerCase();return L(e,(e=>{var t;return o===(null===(t=e.brand)||void 0===t?void 0:t.toLowerCase())})).map((e=>({current:e.name,version:no.nu(parseInt(t.version,10),0)})))})))(n,e))).orThunk((()=>((e,t)=>ro(e,t).map((e=>{const o=no.detect(e.versionRegexes,t);return{current:e.name,version:o}})))(n,e))).fold(wo,bo),l=((e,t)=>ro(e,t).map((e=>{const o=no.detect(e.versionRegexes,t);return{current:e.name,version:o}})))(r,e).fold(Oo,ko),a=((e,t,o,n)=>{const r=e.isiOS()&&!0===/ipad/i.test(o),s=e.isiOS()&&!r,l=e.isiOS()||e.isAndroid(),a=l||n("(pointer:coarse)"),c=r||!s&&l&&n("(min-device-width:768px)"),i=s||l&&!c,m=t.isSafari()&&e.isiOS()&&!1===/safari/i.test(o),d=!i&&!c&&!m;return{isiPad:g(r),isiPhone:g(s),isTablet:g(c),isPhone:g(i),isTouch:g(a),isAndroid:e.isAndroid,isiOS:e.isiOS,isWebView:g(m),isDesktop:g(d)}})(l,s,e,o);return{browser:s,os:l,deviceType:a}})(navigator.userAgent,C.from(navigator.userAgentData),Eo)));const Bo=()=>No(),_o=(e,t)=>{const o=o=>{const n=t(o);if(n<=0||null===n){const t=_t(o,e);return parseFloat(t)||0}return n},n=(e,t)=>A(t,((t,o)=>{const n=_t(e,o),r=void 0===n?0:parseInt(n,10);return isNaN(r)?t:t+r}),0);return{set:(t,o)=>{if(!u(o)&&!o.match(/^[0-9]+$/))throw new Error(e+".set accepts only positive integer values. Value was "+o);const n=t.dom;kt(n)&&(n.style[e]=o+"px")},get:o,getOuter:o,aggregate:n,max:(e,t,o)=>{const r=n(e,o);return t>r?t-r:0}}},zo=(e,t,o)=>((e,t)=>(e=>{const t=parseFloat(e);return isNaN(t)?C.none():C.some(t)})(e).getOr(t))(_t(e,t),o),Ao=_o("width",(e=>e.dom.offsetWidth)),Lo=e=>Ao.get(e),Wo=e=>Ao.getOuter(e),Mo=e=>((e,t)=>{const o=e.dom,n=o.getBoundingClientRect().width||o.offsetWidth;return"border-box"===t?n:((e,t,o,n)=>t-zo(e,`padding-${o}`,0)-zo(e,`padding-${n}`,0)-zo(e,`border-${o}-width`,0)-zo(e,`border-${n}-width`,0))(e,n,"left","right")})(e,"content-box"),jo=(e,t,o)=>{const n=e.cells,r=n.slice(0,t),s=n.slice(t),l=r.concat(o).concat(s);return Fo(e,l)},Po=(e,t,o)=>jo(e,t,[o]),Io=(e,t,o)=>{e.cells[t]=o},Fo=(e,t)=>tt(e.element,t,e.section,e.isNew),Ho=(e,t)=>e.cells[t],$o=(e,t)=>Ho(e,t).element,Vo=e=>e.cells.length,qo=e=>{const t=B(e,(e=>"colgroup"===e.section));return{rows:t.fail,cols:t.pass}},Uo=(e,t,o)=>{const n=E(e.cells,o);return tt(t(e.element),n,e.section,!0)},Go="data-snooker-locked-cols",Ko=e=>we(e,Go).bind((e=>C.from(e.match(/\d+/g)))).map((e=>I(e,x))),Yo=e=>{const t=A(qo(e).rows,((e,t)=>(N(t.cells,((t,o)=>{t.isLocked&&(e[o]=!0)})),e)),{}),o=J(t,((e,t)=>parseInt(t,10)));return((e,t)=>{const o=S.call(e,0);return o.sort(void 0),o})(o)},Jo=(e,t)=>e+","+t,Qo=(e,t)=>{const o=j(e.all,(e=>e.cells));return _(o,t)},Xo=e=>{const t={},o=[],n=H(e).map((e=>e.element)).bind(Kt).bind(Ko).getOr({});let r=0,s=0,l=0;const{pass:a,fail:c}=B(e,(e=>"colgroup"===e.section));N(c,(e=>{const a=[];N(e.cells,(e=>{let o=0;for(;void 0!==t[Jo(l,o)];)o++;const r=((e,t)=>X(e,t)&&void 0!==e[t]&&null!==e[t])(n,o.toString()),c=((e,t,o,n,r,s)=>({element:e,rowspan:t,colspan:o,row:n,column:r,isLocked:s}))(e.element,e.rowspan,e.colspan,l,o,r);for(let n=0;n{const t=(e=>{const t={};let o=0;return N(e.cells,(e=>{const n=e.colspan;k(n,(r=>{const s=o+r;t[s]=((e,t,o)=>({element:e,colspan:t,column:o}))(e.element,n,s)})),o+=n})),t})(e),o=((e,t)=>({element:e,columns:t}))(e.element,Q(t));return{colgroups:[o],columns:t}})).getOrThunk((()=>({colgroups:[],columns:{}}))),d=((e,t)=>({rows:e,columns:t}))(r,s);return{grid:d,access:t,all:o,columns:i,colgroups:m}},Zo=e=>{const t=Zt(e);return Xo(t)},en=Xo,tn=(e,t,o)=>C.from(e.access[Jo(t,o)]),on=(e,t,o)=>{const n=Qo(e,(e=>o(t,e.element)));return n.length>0?C.some(n[0]):C.none()},nn=Qo,rn=e=>j(e.all,(e=>e.cells)),sn=e=>Q(e.columns),ln=e=>q(e.columns).length>0,an=(e,t)=>C.from(e.columns[t]),cn=(e,t=x)=>{const o=e.grid,n=k(o.columns,h),r=k(o.rows,h);return E(n,(o=>mn((()=>j(r,(t=>tn(e,t,o).filter((e=>e.column===o)).toArray()))),(e=>1===e.colspan&&t(e.element)),(()=>tn(e,0,o)))))},mn=(e,t,o)=>{const n=e();return L(n,t).orThunk((()=>C.from(n[0]).orThunk(o))).map((e=>e.element))},dn=e=>{const t=e.grid,o=k(t.rows,h),n=k(t.columns,h);return E(o,(t=>mn((()=>j(n,(o=>tn(e,t,o).filter((e=>e.row===t)).fold(g([]),(e=>[e]))))),(e=>1===e.rowspan),(()=>tn(e,t,0)))))},un=(e,t)=>o=>"rtl"===fn(o)?t:e,fn=e=>"rtl"===_t(e,"direction")?"rtl":"ltr",gn=_o("height",(e=>{const t=e.dom;return lt(e)?t.getBoundingClientRect().height:t.offsetHeight})),hn=e=>gn.get(e),pn=e=>gn.getOuter(e),wn=(e,t)=>({left:e,top:t,translate:(o,n)=>wn(e+o,t+n)}),bn=wn,vn=(e,t)=>void 0!==e?e:void 0!==t?t:0,yn=e=>{const t=e.dom.ownerDocument,o=t.body,n=t.defaultView,r=t.documentElement;if(o===e.dom)return bn(o.offsetLeft,o.offsetTop);const s=vn(null==n?void 0:n.pageYOffset,r.scrollTop),l=vn(null==n?void 0:n.pageXOffset,r.scrollLeft),a=vn(r.clientTop,o.clientTop),c=vn(r.clientLeft,o.clientLeft);return xn(e).translate(l-c,s-a)},xn=e=>{const t=e.dom,o=t.ownerDocument.body;return o===t?bn(o.offsetLeft,o.offsetTop):lt(e)?(e=>{const t=e.getBoundingClientRect();return bn(t.left,t.top)})(t):bn(0,0)},Cn=(e,t)=>({row:e,y:t}),Sn=(e,t)=>({col:e,x:t}),Tn=e=>yn(e).left+Wo(e),Rn=e=>yn(e).left,Dn=(e,t)=>Sn(e,Rn(t)),On=(e,t)=>Sn(e,Tn(t)),kn=e=>yn(e).top,En=(e,t)=>Cn(e,kn(t)),Nn=(e,t)=>Cn(e,kn(t)+pn(t)),Bn=(e,t,o)=>{if(0===o.length)return[];const n=E(o.slice(1),((t,o)=>t.map((t=>e(o,t))))),r=o[o.length-1].map((e=>t(o.length-1,e)));return n.concat([r])},_n={delta:h,positions:e=>Bn(En,Nn,e),edge:kn},zn=un({delta:h,edge:Rn,positions:e=>Bn(Dn,On,e)},{delta:e=>-e,edge:Tn,positions:e=>Bn(On,Dn,e)}),An={delta:(e,t)=>zn(t).delta(e,t),positions:(e,t)=>zn(t).positions(e,t),edge:e=>zn(e).edge(e)},Ln={unsupportedLength:["em","ex","cap","ch","ic","rem","lh","rlh","vw","vh","vi","vb","vmin","vmax","cm","mm","Q","in","pc","pt","px"],fixed:["px","pt"],relative:["%"],empty:[""]},Wn=(()=>{const e="[0-9]+",t="[eE][+-]?"+e,o=e=>`(?:${e})?`,n=["Infinity",e+"\\."+o(e)+o(t),"\\."+e+o(t),e+o(t)].join("|");return new RegExp(`^([+-]?(?:${n}))(.*)$`)})(),Mn=/(\d+(\.\d+)?)%/,jn=/(\d+(\.\d+)?)px|em/,Pn=ue("col"),In=(e,t,o)=>{const n=Be(e).getOrThunk((()=>at(ke(e))));return t(e)/o(n)*100},Fn=(e,t)=>{Nt(e,"width",t+"px")},Hn=(e,t)=>{Nt(e,"width",t+"%")},$n=(e,t)=>{Nt(e,"height",t+"px")},Vn=e=>{const t=(e=>{return zo(t=e,"height",t.dom.offsetHeight)+"px";var t})(e);return t?((e,t,o,n)=>{const r=parseFloat(e);return Rt(e,"%")&&"table"!==ne(t)?((e,t,o,n)=>{const r=Kt(e).map((e=>{const n=o(e);return Math.floor(t/100*n)})).getOr(t);return n(e,r),r})(t,r,o,n):r})(t,e,hn,$n):hn(e)},qn=(e,t)=>At(e,t).orThunk((()=>we(e,t).map((e=>e+"px")))),Un=e=>qn(e,"width"),Gn=e=>In(e,Lo,Mo),Kn=e=>{return Pn(e)?Lo(e):zo(t=e,"width",t.dom.offsetWidth);var t},Yn=e=>((e,t,o)=>o(e)/Mt(e,"rowspan"))(e,0,Vn),Jn=(e,t,o)=>{Nt(e,"width",t+o)},Qn=e=>In(e,Lo,Mo)+"%",Xn=g(Mn),Zn=ue("col"),er=e=>Un(e).getOrThunk((()=>Kn(e)+"px")),tr=e=>{return(t=e,qn(t,"height")).getOrThunk((()=>Yn(e)+"px"));var t},or=(e,t,o,n,r,s)=>e.filter(n).fold((()=>s(((e,t)=>{if(t<0||t>=e.length-1)return C.none();const o=e[t].fold((()=>{const o=(e=>{const t=S.call(e,0);return t.reverse(),t})(e.slice(0,t));return V(o,((e,t)=>e.map((e=>({value:e,delta:t+1})))))}),(e=>C.some({value:e,delta:0}))),n=e[t+1].fold((()=>{const o=e.slice(t+1);return V(o,((e,t)=>e.map((e=>({value:e,delta:t+1})))))}),(e=>C.some({value:e,delta:1})));return o.bind((e=>n.map((t=>{const o=t.delta+e.delta;return Math.abs(t.value-e.value)/o}))))})(o,t))),(e=>r(e))),nr=(e,t,o,n)=>{const r=cn(e),s=ln(e)?(e=>E(sn(e),(e=>C.from(e.element))))(e):r,l=[C.some(An.edge(t))].concat(E(An.positions(r,t),(e=>e.map((e=>e.x))))),a=b(jt);return E(s,((e,t)=>or(e,t,l,a,(e=>{if((e=>{const t=Bo().browser,o=t.isChromium()||t.isFirefox();return!Zn(e)||o})(e))return o(e);{const e=null!=(s=r[t])?h(s):C.none();return or(e,t,l,a,(e=>n(C.some(Lo(e)))),n)}var s}),n)))},rr=e=>e.map((e=>e+"px")).getOr(""),sr=(e,t,o)=>nr(e,t,Kn,(e=>e.getOrThunk(o.minCellWidth))),lr=(e,t,o,n,r)=>{const s=dn(e),l=[C.some(o.edge(t))].concat(E(o.positions(s,t),(e=>e.map((e=>e.y)))));return E(s,((e,t)=>or(e,t,l,b(Pt),n,r)))},ar=(e,t)=>()=>lt(e)?t(e):parseFloat(At(e,"width").getOr("0")),cr=e=>{const t=ar(e,(e=>parseFloat(Qn(e)))),o=ar(e,Lo);return{width:t,pixelWidth:o,getWidths:(t,o)=>((e,t,o)=>nr(e,t,Gn,(e=>e.fold((()=>o.minCellWidth()),(e=>e/o.pixelWidth()*100)))))(t,e,o),getCellDelta:e=>e/o()*100,singleColumnWidth:(e,t)=>[100-e],minCellWidth:()=>Ft()/o()*100,setElementWidth:Hn,adjustTableWidth:o=>{const n=t();Hn(e,n+o/100*n)},isRelative:!0,label:"percent"}},ir=e=>{const t=ar(e,Lo);return{width:t,pixelWidth:t,getWidths:(t,o)=>sr(t,e,o),getCellDelta:h,singleColumnWidth:(e,t)=>[Math.max(Ft(),e+t)-e],minCellWidth:Ft,setElementWidth:Fn,adjustTableWidth:o=>{const n=t()+o;Fn(e,n)},isRelative:!1,label:"pixel"}},mr=e=>Un(e).fold((()=>(e=>{const t=ar(e,Lo),o=g(0);return{width:t,pixelWidth:t,getWidths:(t,o)=>sr(t,e,o),getCellDelta:o,singleColumnWidth:g([0]),minCellWidth:o,setElementWidth:f,adjustTableWidth:f,isRelative:!0,label:"none"}})(e)),(t=>((e,t)=>null!==Xn().exec(t)?cr(e):ir(e))(e,t))),dr=ir,ur=cr,fr=(e,t,o)=>{const n=e[o].element,r=xe.fromTag("td");Ie(r,xe.fromTag("br")),(t?Ie:Pe)(n,r)},gr=((e,t)=>{const o=t=>e(t)?C.from(t.dom.nodeValue):C.none();return{get:t=>{if(!e(t))throw new Error("Can only get text value of a text node");return o(t).getOr("")},getOption:o,set:(t,o)=>{if(!e(t))throw new Error("Can only set raw text value of a text node");t.dom.nodeValue=o}}})(ie),hr=e=>gr.get(e),pr=e=>gr.getOption(e),wr=(e,t)=>gr.set(e,t),br=e=>"img"===ne(e)?1:pr(e).fold((()=>Le(e).length),(e=>e.length)),vr=["img","br"],yr=e=>pr(e).filter((e=>0!==e.trim().length||e.indexOf("\xa0")>-1)).isSome()||D(vr,ne(e))||(e=>ae(e)&&"false"===pe(e,"contenteditable"))(e),xr=e=>((e,t)=>{const o=e=>{for(let n=0;nSr(e,yr),Sr=(e,t)=>{const o=e=>{const n=Le(e);for(let e=n.length-1;e>=0;e--){const r=n[e];if(t(r))return C.some(r);const s=o(r);if(s.isSome())return s}return C.none()};return o(e)},Tr={scope:["row","col"]},Rr=e=>()=>{const t=xe.fromTag("td",e.dom);return Ie(t,xe.fromTag("br",e.dom)),t},Dr=e=>()=>xe.fromTag("col",e.dom),Or=e=>()=>xe.fromTag("colgroup",e.dom),kr=e=>()=>xe.fromTag("tr",e.dom),Er=(e,t,o)=>{const n=((e,t)=>{const o=Je(e,t),n=Le(Ye(e));return $e(o,n),o})(e,t);return G(o,((e,t)=>{null===e?be(n,t):ge(n,t,e)})),n},Nr=e=>e,Br=(e,t,o)=>{const n=(e,t)=>{((e,t)=>{const o=e.dom,n=t.dom;kt(o)&&kt(n)&&(n.style.cssText=o.style.cssText)})(e.element,t),Lt(t,"height"),1!==e.colspan&&Lt(t,"width")};return{col:o=>{const r=xe.fromTag(ne(o.element),t.dom);return n(o,r),e(o.element,r),r},colgroup:Or(t),row:kr(t),cell:r=>{const s=xe.fromTag(ne(r.element),t.dom),l=o.getOr(["strong","em","b","i","span","font","h1","h2","h3","h4","h5","h6","p","div"]),a=l.length>0?((e,t,o)=>xr(e).map((n=>{const r=o.join(","),s=it(n,r,(t=>Re(t,e)));return z(s,((e,t)=>{const o=Ke(t);return Ie(e,o),o}),t)})).getOr(t))(r.element,s,l):s;return Ie(a,xe.fromTag("br")),n(r,s),((e,t)=>{G(Tr,((o,n)=>we(e,n).filter((e=>D(o,e))).each((e=>ge(t,n,e)))))})(r.element,s),e(r.element,s),s},replace:Er,colGap:Dr(t),gap:Rr(t)}},_r=e=>({col:Dr(e),colgroup:Or(e),row:kr(e),cell:Rr(e),replace:Nr,colGap:Dr(e),gap:Rr(e)}),zr=e=>t=>t.options.get(e),Ar="100%",Lr=e=>{var t;const o=e.dom,n=null!==(t=o.getParent(e.selection.getStart(),o.isBlock))&&void 0!==t?t:e.getBody();return Mo(xe.fromDom(n))+"px"},Wr=e=>C.from(e.options.get("table_clone_elements")),Mr=zr("table_header_type"),jr=zr("table_column_resizing"),Pr=e=>"preservetable"===jr(e),Ir=e=>"resizetable"===jr(e),Fr=zr("table_sizing_mode"),Hr=e=>"relative"===Fr(e),$r=e=>"fixed"===Fr(e),Vr=e=>"responsive"===Fr(e),qr=zr("table_resize_bars"),Ur=zr("table_style_by_css"),Gr=zr("table_merge_content_on_paste"),Kr=e=>{const t=e.options,o=t.get("table_default_attributes");return t.isSet("table_default_attributes")?o:((e,t)=>Vr(e)||Ur(e)?t:$r(e)?{...t,width:Lr(e)}:{...t,width:Ar})(e,o)},Yr=zr("table_use_colgroups"),Jr=e=>bt(e,"[contenteditable]"),Qr=(e,t=!1)=>lt(e)?e.dom.isContentEditable:Jr(e).fold(g(t),(e=>"true"===Xr(e))),Xr=e=>e.dom.contentEditable,Zr=e=>xe.fromDom(e.getBody()),es=e=>t=>Re(t,Zr(e)),ts=e=>{be(e,"data-mce-style");const t=e=>be(e,"data-mce-style");N(Ut(e),t),N(Gt(e),t),N(Yt(e),t)},os=e=>xe.fromDom(e.selection.getStart()),ns=e=>e.getBoundingClientRect().width,rs=e=>e.getBoundingClientRect().height,ss=e=>gt(e,ue("table")).exists(Qr),ls=(e,t)=>{const o=t.column,n=t.column+t.colspan-1,r=t.row,s=t.row+t.rowspan-1;return o<=e.finishCol&&n>=e.startCol&&r<=e.finishRow&&s>=e.startRow},as=(e,t)=>t.column>=e.startCol&&t.column+t.colspan-1<=e.finishCol&&t.row>=e.startRow&&t.row+t.rowspan-1<=e.finishRow,cs=(e,t,o)=>{const n=on(e,t,Re),r=on(e,o,Re);return n.bind((e=>r.map((t=>{return o=e,n=t,{startRow:Math.min(o.row,n.row),startCol:Math.min(o.column,n.column),finishRow:Math.max(o.row+o.rowspan-1,n.row+n.rowspan-1),finishCol:Math.max(o.column+o.colspan-1,n.column+n.colspan-1)};var o,n}))))},is=(e,t,o)=>cs(e,t,o).map((t=>{const o=nn(e,w(ls,t));return E(o,(e=>e.element))})),ms=(e,t)=>on(e,t,((e,t)=>De(t,e))).map((e=>e.element)),ds=(e,t,o)=>{const n=fs(e);return is(n,t,o)},us=(e,t,o,n,r)=>{const s=fs(e),l=Re(e,o)?C.some(t):ms(s,t),a=Re(e,r)?C.some(n):ms(s,n);return l.bind((e=>a.bind((t=>is(s,e,t)))))},fs=Zo;var gs=["body","p","div","article","aside","figcaption","figure","footer","header","nav","section","ol","ul","li","table","thead","tbody","tfoot","caption","tr","td","th","h1","h2","h3","h4","h5","h6","blockquote","pre","address"],hs=()=>({up:g({selector:ht,closest:bt,predicate:ft,all:_e}),down:g({selector:dt,predicate:ct}),styles:g({get:_t,getRaw:At,set:Nt,remove:Lt}),attrs:g({get:pe,set:ge,remove:be,copyTo:(e,t)=>{const o=ve(e);he(t,o)}}),insert:g({before:Me,after:je,afterAll:He,append:Ie,appendAll:$e,prepend:Pe,wrap:Fe}),remove:g({unwrap:Ue,remove:qe}),create:g({nu:xe.fromTag,clone:e=>xe.fromDom(e.dom.cloneNode(!1)),text:xe.fromText}),query:g({comparePosition:(e,t)=>e.dom.compareDocumentPosition(t.dom),prevSibling:ze,nextSibling:Ae}),property:g({children:Le,name:ne,parent:Ne,document:e=>Ee(e).dom,isText:ie,isComment:le,isElement:ce,isSpecial:e=>{const t=ne(e);return D(["script","noscript","iframe","noframes","noembed","title","style","textarea","xmp"],t)},getLanguage:e=>ce(e)?we(e,"lang"):C.none(),getText:hr,setText:wr,isBoundary:e=>!!ce(e)&&("body"===ne(e)||D(gs,ne(e))),isEmptyTag:e=>!!ce(e)&&D(["br","img","hr","input"],ne(e)),isNonEditable:e=>ce(e)&&"false"===pe(e,"contenteditable")}),eq:Re,is:Oe});const ps=(e,t,o,n)=>{const r=t(e,o);return z(n,((o,n)=>{const r=t(e,n);return ws(e,o,r)}),r)},ws=(e,t,o)=>t.bind((t=>o.filter(w(e.eq,t)))),bs=hs(),vs=(e,t)=>((e,t,o)=>o.length>0?((e,t,o,n)=>n(e,t,o[0],o.slice(1)))(e,t,o,ps):C.none())(bs,((t,o)=>e(o)),t),ys=e=>ht(e,"table"),xs=(e,t,o)=>{const n=e=>t=>void 0!==o&&o(t)||Re(t,e);return Re(e,t)?C.some({boxes:C.some([e]),start:e,finish:t}):ys(e).bind((r=>ys(t).bind((s=>{if(Re(r,s))return C.some({boxes:ds(r,e,t),start:e,finish:t});if(De(r,s)){const o=it(t,"td,th",n(r)),l=o.length>0?o[o.length-1]:t;return C.some({boxes:us(r,e,r,t,s),start:e,finish:l})}if(De(s,r)){const o=it(e,"td,th",n(s)),l=o.length>0?o[o.length-1]:e;return C.some({boxes:us(s,e,r,t,s),start:e,finish:l})}return((e,t,o)=>((e,t,o,n=y)=>{const r=[t].concat(e.up().all(t)),s=[o].concat(e.up().all(o)),l=e=>W(e,n).fold((()=>e),(t=>e.slice(0,t+1))),a=l(r),c=l(s),i=L(a,(t=>O(c,((e,t)=>w(e.eq,t))(e,t))));return{firstpath:a,secondpath:c,shared:i}})(bs,e,t,void 0))(e,t).shared.bind((l=>bt(l,"table",o).bind((o=>{const l=it(t,"td,th",n(o)),a=l.length>0?l[l.length-1]:t,c=it(e,"td,th",n(o)),i=c.length>0?c[c.length-1]:e;return C.some({boxes:us(o,e,r,t,s),start:i,finish:a})}))))}))))},Cs=(e,t)=>{const o=dt(e,t);return o.length>0?C.some(o):C.none()},Ss=(e,t,o)=>wt(e,t).bind((t=>wt(e,o).bind((e=>vs(ys,[t,e]).map((o=>({first:t,last:e,table:o}))))))),Ts=(e,t,o,n,r)=>((e,t)=>L(e,(e=>Ce(e,t))))(e,r).bind((e=>((e,t,o)=>Kt(e).bind((n=>((e,t,o,n)=>on(e,t,Re).bind((t=>{const r=o>0?t.row+t.rowspan-1:t.row,s=n>0?t.column+t.colspan-1:t.column;return tn(e,r+o,s+n).map((e=>e.element))})))(fs(n),e,t,o))))(e,t,o).bind((e=>((e,t)=>ht(e,"table").bind((o=>wt(o,t).bind((t=>xs(t,e).bind((e=>e.boxes.map((t=>({boxes:t,start:e.start,finish:e.finish}))))))))))(e,n))))),Rs=(e,t)=>Cs(e,t),Ds=(e,t,o)=>Ss(e,t,o).bind((t=>{const o=t=>Re(e,t),n="thead,tfoot,tbody,table",r=ht(t.first,n,o),s=ht(t.last,n,o);return r.bind((e=>s.bind((o=>Re(e,o)?((e,t,o)=>((e,t,o)=>cs(e,t,o).bind((t=>((e,t)=>{let o=!0;const n=w(as,t);for(let r=t.startRow;r<=t.finishRow;r++)for(let s=t.startCol;s<=t.finishCol;s++)o=o&&tn(e,r,s).exists(n);return o?C.some(t):C.none()})(e,t))))(fs(e),t,o))(t.table,t.first,t.last):C.none()))))})),Os=h,ks=e=>{const t=(e,t)=>we(e,t).exists((e=>parseInt(e,10)>1));return e.length>0&&P(e,(e=>t(e,"rowspan")||t(e,"colspan")))?C.some(e):C.none()},Es=(e,t,o)=>t.length<=1?C.none():Ds(e,o.firstSelectedSelector,o.lastSelectedSelector).map((e=>({bounds:e,cells:t}))),Ns="data-mce-selected",Bs="data-mce-first-selected",_s="data-mce-last-selected",zs="["+Ns+"]",As={selected:Ns,selectedSelector:"td["+Ns+"],th["+Ns+"]",firstSelected:Bs,firstSelectedSelector:"td["+Bs+"],th["+Bs+"]",lastSelected:_s,lastSelectedSelector:"td["+_s+"],th["+_s+"]"},Ls=(e,t,o)=>({element:o,mergable:Es(t,e,As),unmergable:ks(e),selection:Os(e)}),Ws=e=>(t,o)=>{const n=ne(t),r="col"===n||"colgroup"===n?Kt(s=t).bind((e=>Rs(e,As.firstSelectedSelector))).fold(g(s),(e=>e[0])):t;var s;return bt(r,e,o)},Ms=Ws("th,td,caption"),js=Ws("th,td"),Ps=e=>{return t=e.model.table.getSelectedCells(),E(t,xe.fromDom);var t},Is=(e,t)=>{e.on("BeforeGetContent",(t=>{const o=o=>{t.preventDefault(),(e=>Kt(e[0]).map((e=>{const t=((e,t)=>{const o=e=>Ce(e.element,t),n=Ye(e),r=Zt(n),s=mr(e),l=en(r),a=((e,t)=>{const o=e.grid.columns;let n=e.grid.rows,r=o,s=0,l=0;const a=[],c=[];return G(e.access,(e=>{if(a.push(e),t(e)){c.push(e);const t=e.row,o=t+e.rowspan-1,a=e.column,i=a+e.colspan-1;ts&&(s=o),al&&(l=i)}})),((e,t,o,n,r,s)=>({minRow:e,minCol:t,maxRow:o,maxCol:n,allCells:r,selectedCells:s}))(n,r,s,l,a,c)})(l,o),c="th:not("+t+"),td:not("+t+")",i=Vt(n,"th,td",(e=>Ce(e,c)));N(i,qe),((e,t,o,n)=>{const r=_(e,(e=>"colgroup"!==e.section)),s=t.grid.columns,l=t.grid.rows;for(let e=0;eo.maxRow||ao.maxCol||(tn(t,e,a).filter(n).isNone()?fr(r,l,e):l=!0)}})(r,l,a,o);const m=((e,t,o,n)=>{if(0===n.minCol&&t.grid.columns===n.maxCol+1)return 0;const r=sr(t,e,o),s=A(r,((e,t)=>e+t),0),l=A(r.slice(n.minCol,n.maxCol+1),((e,t)=>e+t),0),a=l/s*o.pixelWidth()-o.pixelWidth();return o.getCellDelta(a)})(e,Zo(e),s,a);return((e,t,o,n)=>{G(o.columns,(e=>{(e.columnt.maxCol)&&qe(e.element)}));const r=_($t(e,"tr"),(e=>0===e.dom.childElementCount));N(r,qe),t.minCol!==t.maxCol&&t.minRow!==t.maxRow||N($t(e,"th,td"),(e=>{be(e,"rowspan"),be(e,"colspan")})),be(e,Go),be(e,"data-snooker-col-series"),mr(e).adjustTableWidth(n)})(n,a,l,m),n})(e,zs);return ts(t),[t]})))(o).each((o=>{t.content="text"===t.format?(e=>E(e,(e=>e.dom.innerText)).join(""))(o):((e,t)=>E(t,(t=>e.selection.serializer.serialize(t.dom,{}))).join(""))(e,o)}))};if(!0===t.selection){const t=(e=>_(Ps(e),(e=>Ce(e,As.selectedSelector))))(e);t.length>=1&&o(t)}})),e.on("BeforeSetContent",(o=>{if(!0===o.selection&&!0===o.paste){const n=Ps(e);H(n).each((n=>{Kt(n).each((r=>{const s=_(((e,t)=>{const o=document.createElement("div");return o.innerHTML=e,Le(xe.fromDom(o))})(o.content),(e=>"meta"!==ne(e))),l=ue("table");if(Gr(e)&&1===s.length&&l(s[0])){o.preventDefault();const l=xe.fromDom(e.getDoc()),a=_r(l),c=((e,t,o)=>({element:e,clipboard:t,generators:o}))(n,s[0],a);t.pasteCells(r,c).each((()=>{e.focus()}))}}))}))}}))},Fs=(e,t)=>({element:e,offset:t}),Hs=(e,t,o)=>e.property().isText(t)&&0===e.property().getText(t).trim().length||e.property().isComment(t)?o(t).bind((t=>Hs(e,t,o).orThunk((()=>C.some(t))))):C.none(),$s=(e,t)=>e.property().isText(t)?e.property().getText(t).length:e.property().children(t).length,Vs=(e,t)=>{const o=Hs(e,t,e.query().prevSibling).getOr(t);if(e.property().isText(o))return Fs(o,$s(e,o));const n=e.property().children(o);return n.length>0?Vs(e,n[n.length-1]):Fs(o,$s(e,o))},qs=Vs,Us=hs(),Gs=(e,t)=>{if(!jt(e)){const o=(e=>Un(e).bind((e=>{return t=e,o=["fixed","relative","empty"],C.from(Wn.exec(t)).bind((e=>{const t=Number(e[1]),n=e[2];return((e,t)=>O(t,(t=>O(Ln[t],(t=>e===t)))))(n,o)?C.some({value:t,unit:n}):C.none()}));var t,o})))(e);o.each((o=>{const n=o.value/2;Jn(e,n,o.unit),Jn(t,n,o.unit)}))}},Ks=e=>E(e,g(0)),Ys=(e,t,o,n,r)=>r(e.slice(0,t)).concat(n).concat(r(e.slice(o))),Js=e=>(t,o,n,r)=>{if(e(n)){const e=Math.max(r,t[o]-Math.abs(n)),s=Math.abs(e-t[o]);return n>=0?s:-s}return n},Qs=Js((e=>e<0)),Xs=Js(x),Zs=()=>{const e=(e,t,o,n)=>{const r=(100+o)/100,s=Math.max(n,(e[t]+o)/r);return E(e,((e,o)=>(o===t?s:e/r)-e))},t=(t,o,n,r,s,l)=>l?e(t,o,r,s):((e,t,o,n,r)=>{const s=Qs(e,t,n,r);return Ys(e,t,o+1,[s,0],Ks)})(t,o,n,r,s);return{resizeTable:(e,t)=>e(t),clampTableDelta:Qs,calcLeftEdgeDeltas:t,calcMiddleDeltas:(e,o,n,r,s,l,a)=>t(e,n,r,s,l,a),calcRightEdgeDeltas:(t,o,n,r,s,l)=>{if(l)return e(t,n,r,s);{const e=Qs(t,n,r,s);return Ks(t.slice(0,n)).concat([e])}},calcRedestributedWidths:(e,t,o,n)=>{if(n){const n=(t+o)/t,r=E(e,(e=>e/n));return{delta:100*n-100,newSizes:r}}return{delta:o,newSizes:e}}}},el=()=>{const e=(e,t,o,n,r)=>{const s=Xs(e,n>=0?o:t,n,r);return Ys(e,t,o+1,[s,-s],Ks)};return{resizeTable:(e,t,o)=>{o&&e(t)},clampTableDelta:(e,t,o,n,r)=>{if(r){if(o>=0)return o;{const t=A(e,((e,t)=>e+t-n),0);return Math.max(-t,o)}}return Qs(e,t,o,n)},calcLeftEdgeDeltas:e,calcMiddleDeltas:(t,o,n,r,s,l)=>e(t,n,r,s,l),calcRightEdgeDeltas:(e,t,o,n,r,s)=>{if(s)return Ks(e);{const t=n/e.length;return E(e,g(t))}},calcRedestributedWidths:(e,t,o,n)=>({delta:0,newSizes:e})}},tl=e=>Zo(e).grid,ol=ue("th"),nl=e=>P(e,(e=>ol(e.element))),rl=(e,t)=>e&&t?"sectionCells":e?"section":"cells",sl=e=>{const t="thead"===e.section,o=vt(ll(e.cells),"th");return"tfoot"===e.section?{type:"footer"}:t||o?{type:"header",subType:rl(t,o)}:{type:"body"}},ll=e=>{const t=_(e,(e=>ol(e.element)));return 0===t.length?C.some("td"):t.length===e.length?C.some("th"):C.none()},al=(e,t,o)=>et(o(e.element,t),!0,e.isLocked),cl=(e,t)=>e.section!==t?tt(e.element,e.cells,t,e.isNew):e,il=()=>({transformRow:cl,transformCell:(e,t,o)=>{const n=o(e.element,t),r="td"!==ne(n)?((e,t)=>{const o=Je(e,"td");je(e,o);const n=Le(e);return $e(o,n),qe(e),o})(n):n;return et(r,e.isNew,e.isLocked)}}),ml=()=>({transformRow:cl,transformCell:al}),dl=()=>({transformRow:(e,t)=>cl(e,"thead"===t?"tbody":t),transformCell:al}),ul=il,fl=ml,gl=dl,hl=()=>({transformRow:h,transformCell:al}),pl=(e,t,o,n)=>{o===n?be(e,t):ge(e,t,o)},wl=(e,t,o)=>{$(mt(e,t)).fold((()=>Pe(e,o)),(e=>je(e,o)))},bl=(e,t)=>{const o=[],n=[],r=e=>E(e,(e=>{e.isNew&&o.push(e.element);const t=e.element;return Ve(t),N(e.cells,(e=>{e.isNew&&n.push(e.element),pl(e.element,"colspan",e.colspan,1),pl(e.element,"rowspan",e.rowspan,1),Ie(t,e.element)})),t})),s=e=>j(e,(e=>E(e.cells,(e=>(pl(e.element,"span",e.colspan,1),e.element))))),l=(t,o)=>{const n=((e,t)=>{const o=pt(e,t).getOrThunk((()=>{const o=xe.fromTag(t,ke(e).dom);return"thead"===t?wl(e,"caption,colgroup",o):"colgroup"===t?wl(e,"caption",o):Ie(e,o),o}));return Ve(o),o})(e,o),l=("colgroup"===o?s:r)(t);$e(n,l)},a=(t,o)=>{t.length>0?l(t,o):(t=>{pt(e,t).each(qe)})(o)},c=[],i=[],m=[],d=[];return N(t,(e=>{switch(e.section){case"thead":c.push(e);break;case"tbody":i.push(e);break;case"tfoot":m.push(e);break;case"colgroup":d.push(e)}})),a(d,"colgroup"),a(c,"thead"),a(i,"tbody"),a(m,"tfoot"),{newRows:o,newCells:n}},vl=(e,t)=>{if(0===e.length)return 0;const o=e[0];return W(e,(e=>!t(o.element,e.element))).getOr(e.length)},yl=(e,t)=>{const o=E(e,(e=>E(e.cells,y)));return E(e,((n,r)=>{const s=j(n.cells,((n,s)=>{if(!1===o[r][s]){const m=((e,t,o,n)=>{const r=((e,t)=>e[t])(e,t),s="colgroup"===r.section,l=vl(r.cells.slice(o),n),a=s?1:vl(((e,t)=>E(e,(e=>Ho(e,t))))(e.slice(t),o),n);return{colspan:l,rowspan:a}})(e,r,s,t);return((e,t,n,r)=>{for(let s=e;s({element:e,cells:t,section:o,isNew:n}))(n.element,s,n.section,n.isNew)}))},xl=(e,t,o)=>{const n=[];N(e.colgroups,(r=>{const s=[];for(let n=0;net(e.element,o,!1))).getOrThunk((()=>et(t.colGap(),!0,!1)));s.push(r)}n.push(tt(r.element,s,"colgroup",o))}));for(let r=0;ret(e.element,o,e.isLocked))).getOrThunk((()=>et(t.gap(),!0,!1)));s.push(l)}const l=e.all[r],a=tt(l.element,s,l.section,o);n.push(a)}return n},Cl=e=>yl(e,Re),Sl=(e,t)=>V(e.all,(e=>L(e.cells,(e=>Re(t,e.element))))),Tl=(e,t,o)=>{const n=E(t.selection,(t=>qt(t).bind((t=>Sl(e,t))).filter(o))),r=yt(n);return xt(r.length>0,r)},Rl=(e,t,o,n,r)=>(s,l,a,c)=>{const i=Zo(s),m=C.from(null==c?void 0:c.section).getOrThunk(hl);return t(i,l).map((t=>{const o=((e,t)=>xl(e,t,!1))(i,a),n=e(o,t,Re,r(a),m),s=Yo(n.grid);return{info:t,grid:Cl(n.grid),cursor:n.cursor,lockedColumns:s}})).bind((e=>{const t=bl(s,e.grid),r=C.from(null==c?void 0:c.sizing).getOrThunk((()=>mr(s))),l=C.from(null==c?void 0:c.resize).getOrThunk(el);return o(s,e.grid,e.info,{sizing:r,resize:l,section:m}),n(s),be(s,Go),e.lockedColumns.length>0&&ge(s,Go,e.lockedColumns.join(",")),C.some({cursor:e.cursor,newRows:t.newRows,newCells:t.newCells})}))},Dl=(e,t)=>Tl(e,t,x).map((e=>({cells:e,generators:t.generators,clipboard:t.clipboard}))),Ol=(e,t)=>Tl(e,t,x),kl=(e,t)=>Tl(e,t,(e=>!e.isLocked)),El=(e,t)=>P(t,(t=>((e,t)=>Sl(e,t).exists((e=>!e.isLocked)))(e,t))),Nl=(e,t,o,n)=>{const r=qo(e).rows;let s=!0;for(let e=0;e{const t=t=>t(e),o=g(e),n=()=>r,r={tag:!0,inner:e,fold:(t,o)=>o(e),isValue:x,isError:y,map:t=>zl.value(t(e)),mapError:n,bind:t,exists:t,forall:t,getOr:o,or:n,getOrThunk:o,orThunk:n,getOrDie:o,each:t=>{t(e)},toOptional:()=>C.some(e)};return r},_l=e=>{const t=()=>o,o={tag:!1,inner:e,fold:(t,o)=>t(e),isValue:y,isError:x,map:t,mapError:t=>zl.error(t(e)),bind:t,exists:y,forall:x,getOr:h,or:h,getOrThunk:v,orThunk:v,getOrDie:(n=String(e),()=>{throw new Error(n)}),each:f,toOptional:C.none};var n;return o},zl={value:Bl,error:_l,fromOption:(e,t)=>e.fold((()=>_l(t)),Bl)},Al=(e,t)=>({rowDelta:0,colDelta:Vo(e[0])-Vo(t[0])}),Ll=(e,t)=>({rowDelta:e.length-t.length,colDelta:0}),Wl=(e,t,o,n)=>{const r="colgroup"===t.section?o.col:o.cell;return k(e,(e=>et(r(),!0,n(e))))},Ml=(e,t,o,n)=>{const r=e[e.length-1];return e.concat(k(t,(()=>{const e="colgroup"===r.section?o.colgroup:o.row,t=Uo(r,e,h),s=Wl(t.cells.length,t,o,(e=>X(n,e.toString())));return Fo(t,s)})))},jl=(e,t,o,n)=>E(e,(e=>{const r=Wl(t,e,o,y);return jo(e,n,r)})),Pl=(e,t,o)=>{const n=t.colDelta<0?jl:h,r=t.rowDelta<0?Ml:h,s=Yo(e),l=Vo(e[0]),a=O(s,(e=>e===l-1)),c=n(e,Math.abs(t.colDelta),o,a?l-1:l),i=Yo(c);return r(c,Math.abs(t.rowDelta),o,I(i,x))},Il=(e,t,o,n)=>{const r=w(n,Ho(e[t],o).element),s=e[t];return e.length>1&&Vo(s)>1&&(o>0&&r($o(s,o-1))||o0&&r($o(e[t-1],o))||t_(o,(o=>o>=e.column&&o<=Vo(t[0])+e.column)),Hl=(e,t,o,n,r)=>{((e,t,o,n)=>{t>0&&t{const r=e.cells[t-1];let s=0;const l=n();for(;e.cells.length>t+s&&o(r.element,e.cells[t+s].element);)Io(e,t+s,et(l,!0,e.cells[t+s].isLocked)),s++}))})(t,e,r,n.cell);const s=Ll(o,t),l=Pl(o,s,n),a=Ll(t,l),c=Pl(t,a,n);return E(c,((t,o)=>jo(t,e,l[o].cells)))},$l=(e,t,o,n,r)=>{((e,t,o,n)=>{const r=qo(e).rows;if(t>0&&tA(e,((e,o)=>O(e,(e=>t(e.element,o.element)))?e:e.concat([o])),[]))(r[t-1].cells,o);N(e,(e=>{let s=C.none();for(let l=t;l{Io(a,t,et(e,!0,c.isLocked))})))}}))}})(t,e,r,n.cell);const s=Yo(t),l=Al(t,o),a={...l,colDelta:l.colDelta-s.length},c=Pl(t,a,n),{cols:i,rows:m}=qo(c),d=Yo(c),u=Al(o,t),f={...u,colDelta:u.colDelta+d.length},g=(p=n,w=d,E(o,(e=>A(w,((t,o)=>{const n=Wl(1,e,p,x)[0];return Po(t,o,n)}),e)))),h=Pl(g,f,n);var p,w;return[...i,...m.slice(0,e),...h,...m.slice(e,m.length)]},Vl=(e,t,o,n,r)=>{const{rows:s,cols:l}=qo(e),a=s.slice(0,t),c=s.slice(t);return[...l,...a,((e,t,o,n)=>Uo(e,(e=>n(e,o)),t))(s[o],((e,o)=>t>0&&tE(e,(e=>{const s=t>0&&t{if("colgroup"!==o&&n)return Ho(e,t);{const t=Ho(e,r);return et(l(t.element,s),!0,!1)}})(e,t,e.section,s,o,n,r);return Po(e,t,l)})),Ul=(e,t,o,n)=>((e,t,o,n)=>void 0!==$o(e[t],o)&&t>0&&n($o(e[t-1],o),$o(e[t],o)))(e,t,o,n)||((e,t,o)=>t>0&&o($o(e,t-1),$o(e,t)))(e[t],o,n),Gl=(e,t,o,n)=>{const r=e=>(e=>"row"===e?Pt(t):jt(t))(e)?`${e}group`:e;return e?ol(t)?r(o):null:n&&ol(t)?r("row"===o?"col":"row"):null},Kl=(e,t,o)=>et(o(e.element,t),!0,e.isLocked),Yl=(e,t,o,n,r,s,l)=>E(e,((e,a)=>((e,c)=>{const i=e.cells,m=E(i,((e,c)=>{if((e=>O(t,(t=>o(e.element,t.element))))(e)){const t=l(e,a,c)?r(e,o,n):e;return s(t,a,c).each((e=>{var o,n;o=t.element,n={scope:C.from(e)},G(n,((e,t)=>{e.fold((()=>{be(o,t)}),(e=>{fe(o.dom,t,e)}))}))})),t}return e}));return tt(e.element,m,e.section,e.isNew)})(e))),Jl=(e,t,o)=>j(e,((n,r)=>Ul(e,r,t,o)?[]:[Ho(n,t)])),Ql=(e,t,o,n,r)=>{const s=qo(e).rows,l=j(t,(e=>Jl(s,e,n))),a=E(s,(e=>nl(e.cells))),c=((e,t)=>P(t,h)&&nl(e)?x:(e,o,n)=>!("th"===ne(e.element)&&t[o]))(l,a),i=((e,t)=>(o,n)=>C.some(Gl(e,o.element,"row",t[n])))(o,a);return Yl(e,l,n,r,Kl,i,c)},Xl=(e,t,o,n)=>{const r=qo(e).rows,s=E(t,(e=>Ho(r[e.row],e.column)));return Yl(e,s,o,n,Kl,C.none,x)},Zl=e=>{if(!l(e))throw new Error("cases must be an array");if(0===e.length)throw new Error("there must be at least one case");const t=[],o={};return N(e,((n,r)=>{const s=q(n);if(1!==s.length)throw new Error("one and only one name per case");const a=s[0],c=n[a];if(void 0!==o[a])throw new Error("duplicate key detected:"+a);if("cata"===a)throw new Error("cannot have a case named cata (sorry)");if(!l(c))throw new Error("case arguments must be an array");t.push(a),o[a]=(...o)=>{const n=o.length;if(n!==c.length)throw new Error("Wrong number of arguments to case "+a+". Expected "+c.length+" ("+c+"), got "+n);return{fold:(...t)=>{if(t.length!==e.length)throw new Error("Wrong number of arguments to fold. Expected "+e.length+", got "+t.length);return t[r].apply(null,o)},match:e=>{const n=q(e);if(t.length!==n.length)throw new Error("Wrong number of arguments to match. Expected: "+t.join(",")+"\nActual: "+n.join(","));if(!P(t,(e=>D(n,e))))throw new Error("Not all branches were specified when using match. Specified: "+n.join(", ")+"\nRequired: "+t.join(", "));return e[a].apply(null,o)},log:e=>{console.log(e,{constructors:t,constructor:a,params:o})}}}})),o},ea={...Zl([{none:[]},{only:["index"]},{left:["index","next"]},{middle:["prev","index","next"]},{right:["prev","index"]}])},ta=(e,t,o)=>{let n=0;for(let r=e;r{const o=rn(e);return E(o,(e=>{const o=ta(e.row,e.row+e.rowspan,t);return{element:e.element,height:o,rowspan:e.rowspan}}))},na=(e,t,o)=>{const n=((e,t)=>ln(e)?((e,t)=>{const o=sn(e);return E(o,((e,o)=>({element:e.element,width:t[o],colspan:e.colspan})))})(e,t):((e,t)=>{const o=rn(e);return E(o,(e=>{const o=ta(e.column,e.column+e.colspan,t);return{element:e.element,width:o,colspan:e.colspan}}))})(e,t))(e,t);N(n,(e=>{o.setElementWidth(e.element,e.width)}))},ra=(e,t,o,n,r)=>{const s=Zo(e),l=r.getCellDelta(t),a=r.getWidths(s,r),c=o===s.grid.columns-1,i=n.clampTableDelta(a,o,l,r.minCellWidth(),c),m=((e,t,o,n,r)=>{const s=e.slice(0),l=((e,t)=>0===e.length?ea.none():1===e.length?ea.only(0):0===t?ea.left(0,1):t===e.length-1?ea.right(t-1,t):t>0&&tn.singleColumnWidth(s[e],o)),((e,t)=>r.calcLeftEdgeDeltas(s,e,t,o,n.minCellWidth(),n.isRelative)),((e,t,l)=>r.calcMiddleDeltas(s,e,t,l,o,n.minCellWidth(),n.isRelative)),((e,t)=>r.calcRightEdgeDeltas(s,e,t,o,n.minCellWidth(),n.isRelative)))})(a,o,i,r,n),d=E(m,((e,t)=>e+a[t]));na(s,d,r),n.resizeTable(r.adjustTableWidth,i,c)},sa=e=>A(e,((e,t)=>O(e,(e=>e.column===t.column))?e:e.concat([t])),[]).sort(((e,t)=>e.column-t.column)),la=ue("col"),aa=ue("colgroup"),ca=e=>"tr"===ne(e)||aa(e),ia=e=>({element:e,colspan:Wt(e,"colspan",1),rowspan:Wt(e,"rowspan",1)}),ma=e=>we(e,"scope").map((e=>e.substr(0,3))),da=(e,t=ia)=>{const o=o=>{if(ca(o))return aa((r={element:o}).element)?e.colgroup(r):e.row(r);{const r=o,s=(t=>la(t.element)?e.col(t):e.cell(t))(t(r));return n=C.some({item:r,replacement:s}),s}var r};let n=C.none();return{getOrInit:(e,t)=>n.fold((()=>o(e)),(n=>t(e,n.item)?n.replacement:o(e)))}},ua=e=>t=>{const o=[],n=n=>{const r="td"===e?{scope:null}:{},s=t.replace(n,e,r);return o.push({item:n,sub:s}),s};return{replaceOrInit:(e,t)=>{if(ca(e)||la(e))return e;{const r=e;return((e,t)=>L(o,(o=>t(o.item,e))))(r,t).fold((()=>n(r)),(o=>t(e,o.item)?o.sub:n(r)))}}}},fa=e=>({unmerge:t=>{const o=ma(t);return o.each((e=>ge(t,"scope",e))),()=>{const n=e.cell({element:t,colspan:1,rowspan:1});return Lt(n,"width"),Lt(t,"width"),o.each((e=>ge(n,"scope",e))),n}},merge:e=>(Lt(e[0],"width"),(()=>{const t=yt(E(e,ma));if(0===t.length)return C.none();{const e=t[0],o=["row","col"];return O(t,(t=>t!==e&&D(o,t)))?C.none():C.from(e)}})().fold((()=>be(e[0],"scope")),(t=>ge(e[0],"scope",t+"group"))),g(e[0]))}),ga=["body","p","div","article","aside","figcaption","figure","footer","header","nav","section","ol","ul","table","thead","tfoot","tbody","caption","tr","td","th","h1","h2","h3","h4","h5","h6","blockquote","pre","address"],ha=hs(),pa=e=>((e,t)=>{const o=e.property().name(t);return D(ga,o)})(ha,e),wa=e=>((e,t)=>{const o=e.property().name(t);return D(["ol","ul"],o)})(ha,e),ba=e=>{const t=ue("br"),o=e=>Cr(e).bind((o=>{const n=Ae(o).map((e=>!!pa(e)||!!((e,t)=>D(["br","img","hr","input"],e.property().name(t)))(ha,e)&&"img"!==ne(e))).getOr(!1);return Ne(o).map((r=>{return!0===n||("li"===ne(s=r)||ft(s,wa).isSome())||t(o)||pa(r)&&!Re(e,r)?[]:[xe.fromTag("br")];var s}))})).getOr([]),n=(()=>{const n=j(e,(e=>{const n=Le(e);return(e=>P(e,(e=>t(e)||ie(e)&&0===hr(e).trim().length)))(n)?[]:n.concat(o(e))}));return 0===n.length?[xe.fromTag("br")]:n})();Ve(e[0]),$e(e[0],n)},va=e=>Qr(e,!0),ya=e=>{0===Ut(e).length&&qe(e)},xa=(e,t)=>({grid:e,cursor:t}),Ca=(e,t,o)=>{const n=((e,t,o)=>{var n,r;const s=qo(e).rows;return C.from(null===(r=null===(n=s[t])||void 0===n?void 0:n.cells[o])||void 0===r?void 0:r.element).filter(va).orThunk((()=>(e=>V(e,(e=>V(e.cells,(e=>{const t=e.element;return xt(va(t),t)})))))(s)))})(e,t,o);return xa(e,n)},Sa=e=>A(e,((e,t)=>O(e,(e=>e.row===t.row))?e:e.concat([t])),[]).sort(((e,t)=>e.row-t.row)),Ta=(e,t)=>(o,n,r,s,l)=>{const a=Sa(n),c=E(a,(e=>e.row)),i=((e,t,o,n,r,s,l)=>{const{cols:a,rows:c}=qo(e),i=c[t[0]],m=j(t,(e=>((e,t,o)=>{const n=e[t];return j(n.cells,((n,r)=>Ul(e,t,r,o)?[]:[n]))})(c,e,r))),d=E(i.cells,((e,t)=>nl(Jl(c,t,r)))),u=[...c];N(t,(e=>{u[e]=l.transformRow(c[e],o)}));const f=[...a,...u],g=((e,t)=>P(t,h)&&nl(e.cells)?x:(e,o,n)=>!("th"===ne(e.element)&&t[n]))(i,d),p=((e,t)=>(o,n,r)=>C.some(Gl(e,o.element,"col",t[r])))(n,d);return Yl(f,m,r,s,l.transformCell,p,g)})(o,c,e,t,r,s.replaceOrInit,l);return Ca(i,n[0].row,n[0].column)},Ra=Ta("thead",!0),Da=Ta("tbody",!1),Oa=Ta("tfoot",!1),ka=(e,t,o)=>{const n=((e,t)=>Qt(e,(()=>t)))(e,o.section),r=en(n);return xl(r,t,!0)},Ea=(e,t,o,n)=>((e,t,o,n)=>{const r=en(t),s=n.getWidths(r,n);na(r,s,n)})(0,t,0,n.sizing),Na=(e,t,o,n)=>((e,t,o,n,r)=>{const s=en(t),l=n.getWidths(s,n),a=n.pixelWidth(),{newSizes:c,delta:i}=r.calcRedestributedWidths(l,a,o.pixelDelta,n.isRelative);na(s,c,n),n.adjustTableWidth(i)})(0,t,o,n.sizing,n.resize),Ba=(e,t)=>O(t,(e=>0===e.column&&e.isLocked)),_a=(e,t)=>O(t,(t=>t.column+t.colspan>=e.grid.columns&&t.isLocked)),za=(e,t)=>{const o=cn(e),n=sa(t);return A(n,((e,t)=>e+o[t.column].map(Wo).getOr(0)),0)},Aa=e=>(t,o)=>Ol(t,o).filter((o=>!(e?Ba:_a)(t,o))).map((e=>({details:e,pixelDelta:za(t,e)}))),La=e=>(t,o)=>Dl(t,o).filter((o=>!(e?Ba:_a)(t,o.cells))),Wa=ua("th"),Ma=ua("td"),ja=Rl(((e,t,o,n)=>{const r=t[0].row,s=Sa(t),l=z(s,((e,t)=>({grid:Vl(e.grid,r,t.row+e.delta,o,n.getOrInit),delta:e.delta+1})),{grid:e,delta:0}).grid;return Ca(l,r,t[0].column)}),Ol,f,f,da),Pa=Rl(((e,t,o,n)=>{const r=Sa(t),s=r[r.length-1],l=s.row+s.rowspan,a=z(r,((e,t)=>Vl(e,l,t.row,o,n.getOrInit)),e);return Ca(a,l,t[0].column)}),Ol,f,f,da),Ia=Rl(((e,t,o,n)=>{const r=t.details,s=sa(r),l=s[0].column,a=z(s,((e,t)=>({grid:ql(e.grid,l,t.column+e.delta,o,n.getOrInit),delta:e.delta+1})),{grid:e,delta:0}).grid;return Ca(a,r[0].row,l)}),Aa(!0),Na,f,da),Fa=Rl(((e,t,o,n)=>{const r=t.details,s=r[r.length-1],l=s.column+s.colspan,a=sa(r),c=z(a,((e,t)=>ql(e,l,t.column,o,n.getOrInit)),e);return Ca(c,r[0].row,l)}),Aa(!1),Na,f,da),Ha=Rl(((e,t,o,n)=>{const r=sa(t.details),s=((e,t)=>j(e,(e=>{const o=e.cells,n=z(t,((e,t)=>t>=0&&t0?[tt(e.element,n,e.section,e.isNew)]:[]})))(e,E(r,(e=>e.column))),l=s.length>0?s[0].cells.length-1:0;return Ca(s,r[0].row,Math.min(r[0].column,l))}),((e,t)=>kl(e,t).map((t=>({details:t,pixelDelta:-za(e,t)})))),Na,ya,da),$a=Rl(((e,t,o,n)=>{const r=Sa(t),s=((e,t,o)=>{const{rows:n,cols:r}=qo(e);return[...r,...n.slice(0,t),...n.slice(o+1)]})(e,r[0].row,r[r.length-1].row),l=s.length>0?s.length-1:0;return Ca(s,Math.min(t[0].row,l),t[0].column)}),Ol,f,ya,da),Va=Rl(((e,t,o,n)=>{const r=sa(t),s=E(r,(e=>e.column)),l=Ql(e,s,!0,o,n.replaceOrInit);return Ca(l,t[0].row,t[0].column)}),kl,f,f,Wa),qa=Rl(((e,t,o,n)=>{const r=sa(t),s=E(r,(e=>e.column)),l=Ql(e,s,!1,o,n.replaceOrInit);return Ca(l,t[0].row,t[0].column)}),kl,f,f,Ma),Ua=Rl(Ra,kl,f,f,Wa),Ga=Rl(Da,kl,f,f,Ma),Ka=Rl(Oa,kl,f,f,Ma),Ya=Rl(((e,t,o,n)=>{const r=Xl(e,t,o,n.replaceOrInit);return Ca(r,t[0].row,t[0].column)}),kl,f,f,Wa),Ja=Rl(((e,t,o,n)=>{const r=Xl(e,t,o,n.replaceOrInit);return Ca(r,t[0].row,t[0].column)}),kl,f,f,Ma),Qa=Rl(((e,t,o,n)=>{const r=t.cells;ba(r);const s=((e,t,o,n)=>{const r=qo(e).rows;if(0===r.length)return e;for(let e=t.startRow;e<=t.finishRow;e++)for(let o=t.startCol;o<=t.finishCol;o++){const t=r[e],s=Ho(t,o).isLocked;Io(t,o,et(n(),!1,s))}return e})(e,t.bounds,0,n.merge(r));return xa(s,C.from(r[0]))}),((e,t)=>((e,t)=>t.mergable)(0,t).filter((t=>El(e,t.cells)))),Ea,f,fa),Xa=Rl(((e,t,o,n)=>{const r=z(t,((e,t)=>Nl(e,t,o,n.unmerge(t))),e);return xa(r,C.from(t[0]))}),((e,t)=>((e,t)=>t.unmergable)(0,t).filter((t=>El(e,t)))),Ea,f,fa),Za=Rl(((e,t,o,n)=>{const r=((e,t)=>{const o=Zo(e);return xl(o,t,!0)})(t.clipboard,t.generators);var s,l;return((e,t,o,n,r)=>{const s=Yo(t),l=((e,t,o)=>{const n=Vo(t[0]),r=qo(t).cols.length+e.row,s=k(n-e.column,(t=>t+e.column));return{row:r,column:L(s,(e=>P(o,(t=>t!==e)))).getOr(n-1)}})(e,t,s),a=qo(o).rows,c=Fl(l,a,s),i=((e,t,o)=>{if(e.row>=t.length||e.column>Vo(t[0]))return zl.error("invalid start address out of table bounds, row: "+e.row+", column: "+e.column);const n=t.slice(e.row),r=n[0].cells.slice(e.column),s=Vo(o[0]),l=o.length;return zl.value({rowDelta:n.length-l,colDelta:r.length-s})})(l,t,a);return i.map((e=>{const o={...e,colDelta:e.colDelta-c.length},s=Pl(t,o,n),i=Yo(s),m=Fl(l,a,i);return((e,t,o,n,r,s)=>{const l=e.row,a=e.column,c=l+o.length,i=a+Vo(o[0])+s.length,m=I(s,x);for(let e=l;exa(e,C.some(t.element))),(e=>Ca(e,t.row,t.column)))}),((e,t)=>qt(t.element).bind((o=>Sl(e,o).map((e=>({...e,generators:t.generators,clipboard:t.clipboard})))))),Ea,f,da),ec=Rl(((e,t,o,n)=>{const r=qo(e).rows,s=t.cells[0].column,l=r[t.cells[0].row],a=ka(t.clipboard,t.generators,l),c=Hl(s,e,a,t.generators,o);return Ca(c,t.cells[0].row,t.cells[0].column)}),La(!0),f,f,da),tc=Rl(((e,t,o,n)=>{const r=qo(e).rows,s=t.cells[t.cells.length-1].column+t.cells[t.cells.length-1].colspan,l=r[t.cells[0].row],a=ka(t.clipboard,t.generators,l),c=Hl(s,e,a,t.generators,o);return Ca(c,t.cells[0].row,t.cells[0].column)}),La(!1),f,f,da),oc=Rl(((e,t,o,n)=>{const r=qo(e).rows,s=t.cells[0].row,l=r[s],a=ka(t.clipboard,t.generators,l),c=$l(s,e,a,t.generators,o);return Ca(c,t.cells[0].row,t.cells[0].column)}),Dl,f,f,da),nc=Rl(((e,t,o,n)=>{const r=qo(e).rows,s=t.cells[t.cells.length-1].row+t.cells[t.cells.length-1].rowspan,l=r[t.cells[0].row],a=ka(t.clipboard,t.generators,l),c=$l(s,e,a,t.generators,o);return Ca(c,t.cells[0].row,t.cells[0].column)}),Dl,f,f,da),rc=(e,t)=>{const o=Zo(e);return Ol(o,t).bind((e=>{const t=e[e.length-1],n=e[0].column,r=t.column+t.colspan,s=M(E(o.all,(e=>_(e.cells,(e=>e.column>=n&&e.column{const o=Zo(e);return Ol(o,t).bind(ll).getOr("")},lc=(e,t)=>{const o=Zo(e);return Ol(o,t).bind((e=>{const t=e[e.length-1],n=e[0].row,r=t.row+t.rowspan;return(e=>{const t=E(e,(e=>sl(e).type)),o=D(t,"header"),n=D(t,"footer");if(o||n){const e=D(t,"body");return!o||e||n?o||e||!n?C.none():C.some("footer"):C.some("header")}return C.some("body")})(o.all.slice(n,r))})).getOr("")},ac=(e,t)=>e.dispatch("NewRow",{node:t}),cc=(e,t)=>e.dispatch("NewCell",{node:t}),ic=(e,t,o)=>{e.dispatch("TableModified",{...o,table:t})},mc={structure:!1,style:!0},dc={structure:!0,style:!1},uc={structure:!0,style:!0},fc=(e,t)=>Hr(e)?ur(t):$r(e)?dr(t):mr(t),gc=(e,t,o)=>{const n=e=>"table"===ne(Zr(e)),r=Wr(e),s=Ir(e)?f:Gs,l=t=>{switch(Mr(e)){case"section":return ul();case"sectionCells":return fl();case"cells":return gl();default:return((e,t)=>{var o;switch((o=Zo(e),V(o.all,(e=>{const t=sl(e);return"header"===t.type?C.from(t.subType):C.none()}))).getOr(t)){case"section":return il();case"sectionCells":return ml();case"cells":return dl()}})(t,"section")}},a=(n,s,a,c)=>(i,m,d=!1)=>{ts(i);const u=xe.fromDom(e.getDoc()),f=Br(a,u,r),g={sizing:fc(e,i),resize:Ir(e)?Zs():el(),section:l(i)};return s(i)?n(i,m,f,g).bind((n=>{t.refresh(i.dom),N(n.newRows,(t=>{ac(e,t.dom)})),N(n.newCells,(t=>{cc(e,t.dom)}));const r=((t,n)=>n.cursor.fold((()=>{const n=Ut(t);return H(n).filter(lt).map((n=>{o.clearSelectedCells(t.dom);const r=e.dom.createRng();return r.selectNode(n.dom),e.selection.setRng(r),ge(n,"data-mce-selected","1"),r}))}),(n=>{const r=qs(Us,n),s=e.dom.createRng();return s.setStart(r.element.dom,r.offset),s.setEnd(r.element.dom,r.offset),e.selection.setRng(s),o.clearSelectedCells(t.dom),C.some(s)})))(i,n);return lt(i)&&(ts(i),d||ic(e,i.dom,c)),r.map((e=>({rng:e,effect:c})))})):C.none()},c=a($a,(t=>!n(e)||tl(t).rows>1),f,dc),i=a(Ha,(t=>!n(e)||tl(t).columns>1),f,dc);return{deleteRow:c,deleteColumn:i,insertRowsBefore:a(ja,x,f,dc),insertRowsAfter:a(Pa,x,f,dc),insertColumnsBefore:a(Ia,x,s,dc),insertColumnsAfter:a(Fa,x,s,dc),mergeCells:a(Qa,x,f,dc),unmergeCells:a(Xa,x,f,dc),pasteColsBefore:a(ec,x,f,dc),pasteColsAfter:a(tc,x,f,dc),pasteRowsBefore:a(oc,x,f,dc),pasteRowsAfter:a(nc,x,f,dc),pasteCells:a(Za,x,f,uc),makeCellsHeader:a(Ya,x,f,dc),unmakeCellsHeader:a(Ja,x,f,dc),makeColumnsHeader:a(Va,x,f,dc),unmakeColumnsHeader:a(qa,x,f,dc),makeRowsHeader:a(Ua,x,f,dc),makeRowsBody:a(Ga,x,f,dc),makeRowsFooter:a(Ka,x,f,dc),getTableRowType:lc,getTableCellType:sc,getTableColType:rc}},hc=(e,t,o)=>{const n=Wt(e,t,1);1===o||n<=1?be(e,t):ge(e,t,Math.min(o,n))},pc=(e,t)=>o=>{const n=o.column+o.colspan-1,r=o.column;return n>=e&&r{const n=o.substring(0,o.length-e.length),r=parseFloat(n);return n===r.toString()?t(r):wc.invalid(o)},vc={...wc,from:e=>Rt(e,"%")?bc("%",wc.percent,e):Rt(e,"px")?bc("px",wc.pixels,e):wc.invalid(e)},yc=(e,t,o)=>{const n=vc.from(o),r=P(e,(e=>"0px"===e))?((e,t)=>{const o=e.fold((()=>g("")),(e=>g(e/t+"px")),(()=>g(100/t+"%")));return k(t,o)})(n,e.length):((e,t,o)=>e.fold((()=>t),(e=>((e,t,o)=>{const n=o/t;return E(e,(e=>vc.from(e).fold((()=>e),(e=>e*n+"px"),(e=>e/100*o+"px"))))})(t,o,e)),(e=>((e,t)=>E(e,(e=>vc.from(e).fold((()=>e),(e=>e/t*100+"%"),(e=>e+"%")))))(t,o))))(n,e,t);return Sc(r)},xc=(e,t)=>0===e.length?t:z(e,((e,t)=>vc.from(t).fold(g(0),h,h)+e),0),Cc=(e,t)=>vc.from(e).fold(g(e),(e=>e+t+"px"),(e=>e+t+"%")),Sc=e=>{if(0===e.length)return e;const t=z(e,((e,t)=>{const o=vc.from(t).fold((()=>({value:t,remainder:0})),(e=>((e,t)=>{const o=Math.floor(e);return{value:o+"px",remainder:e-o}})(e)),(e=>({value:e+"%",remainder:0})));return{output:[o.value].concat(e.output),remainder:e.remainder+o.remainder}}),{output:[],remainder:0}),o=t.output;return o.slice(0,o.length-1).concat([Cc(o[o.length-1],Math.round(t.remainder))])},Tc=vc.from,Rc=e=>Tc(e).fold(g("px"),g("px"),g("%")),Dc=(e,t,o)=>{const n=Zo(e),r=n.all,s=rn(n),l=sn(n);t.each((t=>{const o=Rc(t),r=Lo(e),a=((e,t)=>nr(e,t,er,rr))(n,e),c=yc(a,r,t);ln(n)?((e,t,o)=>{N(t,((t,n)=>{const r=xc([e[n]],Ft());Nt(t.element,"width",r+o)}))})(c,l,o):((e,t,o)=>{N(t,(t=>{const n=e.slice(t.column,t.colspan+t.column),r=xc(n,Ft());Nt(t.element,"width",r+o)}))})(c,s,o),Nt(e,"width",t)})),o.each((t=>{const o=Rc(t),l=hn(e),a=((e,t,o)=>lr(e,t,o,tr,rr))(n,e,_n);((e,t,o,n)=>{N(o,(t=>{const o=e.slice(t.row,t.rowspan+t.row),r=xc(o,Ht());Nt(t.element,"height",r+n)})),N(t,((t,o)=>{Nt(t.element,"height",e[o])}))})(yc(a,l,t),r,s,o),Nt(e,"height",t)}))},Oc=e=>Un(e).exists((e=>Mn.test(e))),kc=e=>Un(e).exists((e=>jn.test(e))),Ec=e=>Un(e).isNone(),Nc=e=>{be(e,"width")},Bc=e=>{const t=Qn(e);Dc(e,C.some(t),C.none()),Nc(e)},_c=e=>{const t=(e=>Lo(e)+"px")(e);Dc(e,C.some(t),C.none()),Nc(e)},zc=e=>{Lt(e,"width");const t=Gt(e),o=t.length>0?t:Ut(e);N(o,(e=>{Lt(e,"width"),Nc(e)})),Nc(e)},Ac={styles:{"border-collapse":"collapse",width:"100%"},attributes:{border:"1"},colGroups:!1},Lc=(e,t,o,n)=>k(e,(e=>((e,t,o,n)=>{const r=xe.fromTag("tr");for(let s=0;s{e.selection.select(t.dom,!0),e.selection.collapse(!0)},Mc=(e,t,o,n,s)=>{const l=(e=>{const t=e.options,o=t.get("table_default_styles");return t.isSet("table_default_styles")?o:((e,t)=>Vr(e)||!Ur(e)?t:$r(e)?{...t,width:Lr(e)}:{...t,width:Ar})(e,o)})(e),a={styles:l,attributes:Kr(e),colGroups:Yr(e)};return e.undoManager.ignore((()=>{const r=((e,t,o,n,r,s=Ac)=>{const l=xe.fromTag("table"),a="cells"!==r;Bt(l,s.styles),he(l,s.attributes),s.colGroups&&Ie(l,(e=>{const t=xe.fromTag("colgroup");return k(e,(()=>Ie(t,xe.fromTag("col")))),t})(t));const c=Math.min(e,o);if(a&&o>0){const e=xe.fromTag("thead");Ie(l,e);const s=Lc(o,t,"sectionCells"===r?c:0,n);$e(e,s)}const i=xe.fromTag("tbody");Ie(l,i);const m=Lc(a?e-c:e,t,a?0:o,n);return $e(i,m),l})(o,t,s,n,Mr(e),a);ge(r,"data-mce-id","__mce");const l=(e=>{const t=xe.fromTag("div"),o=xe.fromDom(e.dom.cloneNode(!0));return Ie(t,o),(e=>e.dom.innerHTML)(t)})(r);e.insertContent(l),e.addVisual()})),wt(Zr(e),'table[data-mce-id="__mce"]').map((t=>($r(e)?_c(t):Vr(e)?zc(t):(Hr(e)||(e=>r(e)&&-1!==e.indexOf("%"))(l.width))&&Bc(t),ts(t),be(t,"data-mce-id"),((e,t)=>{N(dt(t,"tr"),(t=>{ac(e,t.dom),N(dt(t,"th,td"),(t=>{cc(e,t.dom)}))}))})(e,t),((e,t)=>{wt(t,"td,th").each(w(Wc,e))})(e,t),t.dom))).getOrNull()};var jc=tinymce.util.Tools.resolve("tinymce.FakeClipboard");const Pc="x-tinymce/dom-table-",Ic=Pc+"rows",Fc=Pc+"columns",Hc=e=>{const t=jc.FakeClipboardItem(e);jc.write([t])},$c=e=>{var t;const o=null!==(t=jc.read())&&void 0!==t?t:[];return V(o,(t=>C.from(t.getType(e))))},Vc=e=>{$c(e).isSome()&&jc.clear()},qc=e=>{e.fold(Gc,(e=>Hc({[Ic]:e})))},Uc=()=>$c(Ic),Gc=()=>Vc(Ic),Kc=e=>{e.fold(Jc,(e=>Hc({[Fc]:e})))},Yc=()=>$c(Fc),Jc=()=>Vc(Fc),Qc=e=>Ms(os(e),es(e)).filter(ss),Xc=(e,t)=>{const o=es(e),n=e=>Kt(e,o),l=t=>(e=>js(os(e),es(e)).filter(ss))(e).bind((e=>n(e).map((o=>t(o,e))))),a=t=>{e.focus()},c=(t,o=!1)=>l(((n,r)=>{const s=Ls(Ps(e),n,r);t(n,s,o).each(a)})),i=()=>l(((t,o)=>((e,t,o)=>{const n=Zo(e);return Ol(n,t).bind((e=>{const t=xl(n,o,!1),r=qo(t).rows.slice(e[0].row,e[e.length-1].row+e[e.length-1].rowspan),s=j(r,(e=>{const t=_(e.cells,(e=>!e.isLocked));return t.length>0?[{...e,cells:t}]:[]})),l=Cl(s);return xt(l.length>0,l)})).map((e=>E(e,(e=>{const t=Ke(e.element);return N(e.cells,(e=>{const o=Ye(e.element);pl(o,"colspan",e.colspan,1),pl(o,"rowspan",e.rowspan,1),Ie(t,o)})),t}))))})(t,Ls(Ps(e),t,o),Br(f,xe.fromDom(e.getDoc()),C.none())))),m=()=>l(((t,o)=>((e,t)=>{const o=Zo(e);return kl(o,t).map((e=>{const t=e[e.length-1],n=e[0].column,r=t.column+t.colspan,s=((e,t,o)=>{if(ln(e)){const n=_(sn(e),pc(t,o)),r=E(n,(e=>{const n=Ye(e.element);return hc(n,"span",o-t),n})),s=xe.fromTag("colgroup");return $e(s,r),[s]}return[]})(o,n,r),l=((e,t,o)=>E(e.all,(e=>{const n=_(e.cells,pc(t,o)),r=E(n,(e=>{const n=Ye(e.element);return hc(n,"colspan",o-t),n})),s=xe.fromTag("tr");return $e(s,r),s})))(o,n,r);return[...s,...l]}))})(t,Ls(Ps(e),t,o)))),d=(t,o)=>o().each((o=>{const n=E(o,(e=>Ye(e)));l(((o,r)=>{const s=_r(xe.fromDom(e.getDoc())),l=((e,t,o,n)=>({selection:Os(e),clipboard:o,generators:n}))(Ps(e),0,n,s);t(o,l).each(a)}))})),g=e=>(t,o)=>((e,t)=>X(e,t)?C.from(e[t]):C.none())(o,"type").each((t=>{c(e(t),o.no_events)}));G({mceTableSplitCells:()=>c(t.unmergeCells),mceTableMergeCells:()=>c(t.mergeCells),mceTableInsertRowBefore:()=>c(t.insertRowsBefore),mceTableInsertRowAfter:()=>c(t.insertRowsAfter),mceTableInsertColBefore:()=>c(t.insertColumnsBefore),mceTableInsertColAfter:()=>c(t.insertColumnsAfter),mceTableDeleteCol:()=>c(t.deleteColumn),mceTableDeleteRow:()=>c(t.deleteRow),mceTableCutCol:()=>m().each((e=>{Kc(e),c(t.deleteColumn)})),mceTableCutRow:()=>i().each((e=>{qc(e),c(t.deleteRow)})),mceTableCopyCol:()=>m().each((e=>Kc(e))),mceTableCopyRow:()=>i().each((e=>qc(e))),mceTablePasteColBefore:()=>d(t.pasteColsBefore,Yc),mceTablePasteColAfter:()=>d(t.pasteColsAfter,Yc),mceTablePasteRowBefore:()=>d(t.pasteRowsBefore,Uc),mceTablePasteRowAfter:()=>d(t.pasteRowsAfter,Uc),mceTableDelete:()=>Qc(e).each((t=>{Kt(t,o).filter(b(o)).each((t=>{const o=xe.fromText("");if(je(t,o),qe(t),e.dom.isEmpty(e.getBody()))e.setContent(""),e.selection.setCursorLocation();else{const t=e.dom.createRng();t.setStart(o.dom,0),t.setEnd(o.dom,0),e.selection.setRng(t),e.nodeChanged()}}))})),mceTableCellToggleClass:(t,o)=>{l((t=>{const n=Ps(e),r=P(n,(t=>e.formatter.match("tablecellclass",{value:o},t.dom))),s=r?e.formatter.remove:e.formatter.apply;N(n,(e=>s("tablecellclass",{value:o},e.dom))),ic(e,t.dom,mc)}))},mceTableToggleClass:(t,o)=>{l((t=>{e.formatter.toggle("tableclass",{value:o},t.dom),ic(e,t.dom,mc)}))},mceTableToggleCaption:()=>{Qc(e).each((t=>{Kt(t,o).each((o=>{pt(o,"caption").fold((()=>{const t=xe.fromTag("caption");Ie(t,xe.fromText("Caption")),((e,t,o)=>{We(e,0).fold((()=>{Ie(e,t)}),(e=>{Me(e,t)}))})(o,t),e.selection.setCursorLocation(t.dom,0)}),(n=>{ue("caption")(t)&&Te("td",o).each((t=>e.selection.setCursorLocation(t.dom,0))),qe(n)})),ic(e,o.dom,dc)}))}))},mceTableSizingMode:(t,n)=>(t=>Qc(e).each((n=>{Vr(e)||$r(e)||Hr(e)||Kt(n,o).each((o=>{"relative"!==t||Oc(o)?"fixed"!==t||kc(o)?"responsive"!==t||Ec(o)||zc(o):_c(o):Bc(o),ts(o),ic(e,o.dom,dc)}))})))(n),mceTableCellType:g((e=>"th"===e?t.makeCellsHeader:t.unmakeCellsHeader)),mceTableColType:g((e=>"th"===e?t.makeColumnsHeader:t.unmakeColumnsHeader)),mceTableRowType:g((e=>{switch(e){case"header":return t.makeRowsHeader;case"footer":return t.makeRowsFooter;default:return t.makeRowsBody}}))},((t,o)=>e.addCommand(o,t))),e.addCommand("mceInsertTable",((t,o)=>{((e,t,o,n={})=>{const r=e=>u(e)&&e>0;if(r(t)&&r(o)){const r=n.headerRows||0,s=n.headerColumns||0;return Mc(e,o,t,s,r)}console.error("Invalid values for mceInsertTable - rows and columns values are required to insert a table.")})(e,o.rows,o.columns,o.options)})),e.addCommand("mceTableApplyCellStyle",((t,o)=>{const l=e=>"tablecell"+e.toLowerCase().replace("-","");if(!s(o))return;const a=_(Ps(e),ss);if(0===a.length)return;const c=((e,t)=>{const o={};return((e,t,o,n)=>{G(e,((e,r)=>{(t(e,r)?o:n)(e,r)}))})(e,t,(e=>(t,o)=>{e[o]=t})(o),f),o})(o,((t,o)=>e.formatter.has(l(o))&&r(t)));(e=>{for(const t in e)if(U.call(e,t))return!1;return!0})(c)||(G(c,((t,o)=>{const n=l(o);N(a,(o=>{""===t?e.formatter.remove(n,{value:null},o.dom,!0):e.formatter.apply(n,{value:t},o.dom)}))})),n(a[0]).each((t=>ic(e,t.dom,mc))))}))},Zc=Zl([{before:["element"]},{on:["element","offset"]},{after:["element"]}]),ei={before:Zc.before,on:Zc.on,after:Zc.after,cata:(e,t,o,n)=>e.fold(t,o,n),getStart:e=>e.fold(h,h,h)},ti=(e,t)=>({selection:e,kill:t}),oi=(e,t)=>{const o=e.document.createRange();return o.selectNode(t.dom),o},ni=(e,t)=>{const o=e.document.createRange();return ri(o,t),o},ri=(e,t)=>e.selectNodeContents(t.dom),si=(e,t,o)=>{const n=e.document.createRange();var r;return r=n,t.fold((e=>{r.setStartBefore(e.dom)}),((e,t)=>{r.setStart(e.dom,t)}),(e=>{r.setStartAfter(e.dom)})),((e,t)=>{t.fold((t=>{e.setEndBefore(t.dom)}),((t,o)=>{e.setEnd(t.dom,o)}),(t=>{e.setEndAfter(t.dom)}))})(n,o),n},li=(e,t,o,n,r)=>{const s=e.document.createRange();return s.setStart(t.dom,o),s.setEnd(n.dom,r),s},ai=e=>({left:e.left,top:e.top,right:e.right,bottom:e.bottom,width:e.width,height:e.height}),ci=Zl([{ltr:["start","soffset","finish","foffset"]},{rtl:["start","soffset","finish","foffset"]}]),ii=(e,t,o)=>t(xe.fromDom(o.startContainer),o.startOffset,xe.fromDom(o.endContainer),o.endOffset),mi=(e,t)=>{const o=((e,t)=>t.match({domRange:e=>({ltr:g(e),rtl:C.none}),relative:(t,o)=>({ltr:eo((()=>si(e,t,o))),rtl:eo((()=>C.some(si(e,o,t))))}),exact:(t,o,n,r)=>({ltr:eo((()=>li(e,t,o,n,r))),rtl:eo((()=>C.some(li(e,n,r,t,o))))})}))(e,t);return((e,t)=>{const o=t.ltr();return o.collapsed?t.rtl().filter((e=>!1===e.collapsed)).map((e=>ci.rtl(xe.fromDom(e.endContainer),e.endOffset,xe.fromDom(e.startContainer),e.startOffset))).getOrThunk((()=>ii(0,ci.ltr,o))):ii(0,ci.ltr,o)})(0,o)},di=(e,t)=>mi(e,t).match({ltr:(t,o,n,r)=>{const s=e.document.createRange();return s.setStart(t.dom,o),s.setEnd(n.dom,r),s},rtl:(t,o,n,r)=>{const s=e.document.createRange();return s.setStart(n.dom,r),s.setEnd(t.dom,o),s}});ci.ltr,ci.rtl;const ui=(e,t,o,n)=>({start:e,soffset:t,finish:o,foffset:n}),fi=(e,t,o,n)=>({start:ei.on(e,t),finish:ei.on(o,n)}),gi=(e,t)=>{const o=di(e,t);return ui(xe.fromDom(o.startContainer),o.startOffset,xe.fromDom(o.endContainer),o.endOffset)},hi=fi,pi=(e,t,o,n,r)=>Re(o,n)?C.none():xs(o,n,t).bind((t=>{const n=t.boxes.getOr([]);return n.length>1?(r(e,n,t.start,t.finish),C.some(ti(C.some(hi(o,0,o,br(o))),!0))):C.none()})),wi=(e,t)=>({item:e,mode:t}),bi=(e,t,o,n=vi)=>e.property().parent(t).map((e=>wi(e,n))),vi=(e,t,o,n=yi)=>o.sibling(e,t).map((e=>wi(e,n))),yi=(e,t,o,n=yi)=>{const r=e.property().children(t);return o.first(r).map((e=>wi(e,n)))},xi=[{current:bi,next:vi,fallback:C.none()},{current:vi,next:yi,fallback:C.some(bi)},{current:yi,next:yi,fallback:C.some(vi)}],Ci=(e,t,o,n,r=xi)=>L(r,(e=>e.current===o)).bind((o=>o.current(e,t,n,o.next).orThunk((()=>o.fallback.bind((o=>Ci(e,t,o,n))))))),Si=(e,t,o,n,r,s)=>Ci(e,t,n,r).bind((t=>s(t.item)?C.none():o(t.item)?C.some(t.item):Si(e,t.item,o,t.mode,r,s))),Ti=e=>t=>0===e.property().children(t).length,Ri=(e,t,o,n)=>Si(e,t,o,vi,{sibling:(e,t)=>e.query().prevSibling(t),first:e=>e.length>0?C.some(e[e.length-1]):C.none()},n),Di=(e,t,o,n)=>Si(e,t,o,vi,{sibling:(e,t)=>e.query().nextSibling(t),first:e=>e.length>0?C.some(e[0]):C.none()},n),Oi=hs(),ki=(e,t)=>((e,t,o)=>Ri(e,t,Ti(e),o))(Oi,e,t),Ei=(e,t)=>((e,t,o)=>Di(e,t,Ti(e),o))(Oi,e,t),Ni=Zl([{none:["message"]},{success:[]},{failedUp:["cell"]},{failedDown:["cell"]}]),Bi=e=>bt(e,"tr"),_i={...Ni,verify:(e,t,o,n,r,s,l)=>bt(n,"td,th",l).bind((o=>bt(t,"td,th",l).map((t=>Re(o,t)?Re(n,o)&&br(o)===r?s(t):Ni.none("in same cell"):vs(Bi,[o,t]).fold((()=>((e,t,o)=>{const n=e.getRect(t),r=e.getRect(o);return r.right>n.left&&r.lefts(t))))))).getOr(Ni.none("default")),cata:(e,t,o,n,r)=>e.fold(t,o,n,r)},zi=ue("br"),Ai=(e,t,o)=>t(e,o).bind((e=>ie(e)&&0===hr(e).trim().length?Ai(e,t,o):C.some(e))),Li=(e,t,o,n)=>((e,t)=>We(e,t).filter(zi).orThunk((()=>We(e,t-1).filter(zi))))(t,o).bind((t=>n.traverse(t).fold((()=>Ai(t,n.gather,e).map(n.relative)),(e=>(e=>Ne(e).bind((t=>{const o=Le(t);return((e,t)=>W(e,w(Re,t)))(o,e).map((n=>((e,t,o,n)=>({parent:e,children:t,element:o,index:n}))(t,o,e,n)))})))(e).map((e=>ei.on(e.parent,e.index))))))),Wi=(e,t)=>({left:e.left,top:e.top+t,right:e.right,bottom:e.bottom+t}),Mi=(e,t)=>({left:e.left,top:e.top-t,right:e.right,bottom:e.bottom-t}),ji=(e,t,o)=>({left:e.left+t,top:e.top+o,right:e.right+t,bottom:e.bottom+o}),Pi=e=>({left:e.left,top:e.top,right:e.right,bottom:e.bottom}),Ii=(e,t)=>C.some(e.getRect(t)),Fi=(e,t,o)=>ce(t)?Ii(e,t).map(Pi):ie(t)?((e,t,o)=>o>=0&&o0?e.getRangedRect(t,o-1,t,o):C.none())(e,t,o).map(Pi):C.none(),Hi=(e,t)=>ce(t)?Ii(e,t).map(Pi):ie(t)?e.getRangedRect(t,0,t,br(t)).map(Pi):C.none(),$i=Zl([{none:[]},{retry:["caret"]}]),Vi=(e,t,o)=>gt(t,pa).fold(y,(t=>Hi(e,t).exists((e=>((e,t)=>e.leftt.right)(o,e))))),qi={point:e=>e.bottom,adjuster:(e,t,o,n,r)=>{const s=Wi(r,5);return Math.abs(o.bottom-n.bottom)<1||o.top>r.bottom?$i.retry(s):o.top===r.bottom?$i.retry(Wi(r,1)):Vi(e,t,r)?$i.retry(ji(s,5,0)):$i.none()},move:Wi,gather:Ei},Ui=(e,t,o,n,r)=>0===r?C.some(n):((e,t,o)=>e.elementFromPoint(t,o).filter((e=>"table"===ne(e))).isSome())(e,n.left,t.point(n))?((e,t,o,n,r)=>Ui(e,t,o,t.move(n,5),r))(e,t,o,n,r-1):e.situsFromPoint(n.left,t.point(n)).bind((s=>s.start.fold(C.none,(s=>Hi(e,s).bind((l=>t.adjuster(e,s,l,o,n).fold(C.none,(n=>Ui(e,t,o,n,r-1))))).orThunk((()=>C.some(n)))),C.none))),Gi=(e,t,o)=>{const n=e.move(o,5),r=Ui(t,e,o,n,100).getOr(n);return((e,t,o)=>e.point(t)>o.getInnerHeight()?C.some(e.point(t)-o.getInnerHeight()):e.point(t)<0?C.some(-e.point(t)):C.none())(e,r,t).fold((()=>t.situsFromPoint(r.left,e.point(r))),(o=>(t.scrollBy(0,o),t.situsFromPoint(r.left,e.point(r)-o))))},Ki={tryUp:w(Gi,{point:e=>e.top,adjuster:(e,t,o,n,r)=>{const s=Mi(r,5);return Math.abs(o.top-n.top)<1||o.bottome.getSelection().bind((n=>((e,t,o,n)=>{const r=zi(t)?((e,t,o)=>o.traverse(t).orThunk((()=>Ai(t,o.gather,e))).map(o.relative))(e,t,n):Li(e,t,o,n);return r.map((e=>({start:e,finish:e})))})(t,n.finish,n.foffset,o).fold((()=>C.some(Fs(n.finish,n.foffset))),(r=>{const s=e.fromSitus(r);return l=_i.verify(e,n.finish,n.foffset,s.finish,s.foffset,o.failure,t),_i.cata(l,(e=>C.none()),(()=>C.none()),(e=>C.some(Fs(e,0))),(e=>C.some(Fs(e,br(e)))));var l})))),Ji=(e,t,o,n,r,s)=>0===s?C.none():Zi(e,t,o,n,r).bind((l=>{const a=e.fromSitus(l),c=_i.verify(e,o,n,a.finish,a.foffset,r.failure,t);return _i.cata(c,(()=>C.none()),(()=>C.some(l)),(l=>Re(o,l)&&0===n?Qi(e,o,n,Mi,r):Ji(e,t,l,0,r,s-1)),(l=>Re(o,l)&&n===br(l)?Qi(e,o,n,Wi,r):Ji(e,t,l,br(l),r,s-1)))})),Qi=(e,t,o,n,r)=>Fi(e,t,o).bind((t=>Xi(e,r,n(t,Ki.getJumpSize())))),Xi=(e,t,o)=>{const n=Bo().browser;return n.isChromium()||n.isSafari()||n.isFirefox()?t.retry(e,o):C.none()},Zi=(e,t,o,n,r)=>Fi(e,o,n).bind((t=>Xi(e,r,t))),em=(e,t,o,n,r)=>bt(n,"td,th",t).bind((n=>bt(n,"table",t).bind((s=>((e,t)=>ft(e,(e=>Ne(e).exists((e=>Re(e,t)))),void 0).isSome())(r,s)?((e,t,o)=>Yi(e,t,o).bind((n=>Ji(e,t,n.element,n.offset,o,20).map(e.fromSitus))))(e,t,o).bind((e=>bt(e.finish,"td,th",t).map((t=>({start:n,finish:t,range:e}))))):C.none())))),tm=(e,t,o,n,r,s)=>s(n,t).orThunk((()=>em(e,t,o,n,r).map((e=>{const t=e.range;return ti(C.some(hi(t.start,t.soffset,t.finish,t.foffset)),!0)})))),om=(e,t)=>bt(e,"tr",t).bind((e=>bt(e,"table",t).bind((o=>{const n=dt(o,"tr");return Re(e,n[0])?((e,t,o)=>Ri(Oi,e,(e=>Cr(e).isSome()),o))(o,0,t).map((e=>{const t=br(e);return ti(C.some(hi(e,t,e,t)),!0)})):C.none()})))),nm=(e,t)=>bt(e,"tr",t).bind((e=>bt(e,"table",t).bind((o=>{const n=dt(o,"tr");return Re(e,n[n.length-1])?((e,t,o)=>Di(Oi,e,(e=>xr(e).isSome()),o))(o,0,t).map((e=>ti(C.some(hi(e,0,e,0)),!0))):C.none()})))),rm=(e,t,o,n,r,s,l)=>em(e,o,n,r,s).bind((e=>pi(t,o,e.start,e.finish,l))),sm=e=>{let t=e;return{get:()=>t,set:e=>{t=e}}},lm=()=>{const e=(e=>{const t=sm(C.none()),o=()=>t.get().each(e);return{clear:()=>{o(),t.set(C.none())},isSet:()=>t.get().isSome(),get:()=>t.get(),set:e=>{o(),t.set(C.some(e))}}})(f);return{...e,on:t=>e.get().each(t)}},am=(e,t)=>bt(e,"td,th",t),cm=e=>Be(e).exists(Qr),im={traverse:Ae,gather:Ei,relative:ei.before,retry:Ki.tryDown,failure:_i.failedDown},mm={traverse:ze,gather:ki,relative:ei.before,retry:Ki.tryUp,failure:_i.failedUp},dm=e=>t=>t===e,um=dm(38),fm=dm(40),gm=e=>e>=37&&e<=40,hm={isBackward:dm(37),isForward:dm(39)},pm={isBackward:dm(39),isForward:dm(37)},wm=Zl([{domRange:["rng"]},{relative:["startSitu","finishSitu"]},{exact:["start","soffset","finish","foffset"]}]),bm={domRange:wm.domRange,relative:wm.relative,exact:wm.exact,exactFromRange:e=>wm.exact(e.start,e.soffset,e.finish,e.foffset),getWin:e=>{const t=(e=>e.match({domRange:e=>xe.fromDom(e.startContainer),relative:(e,t)=>ei.getStart(e),exact:(e,t,o,n)=>e}))(e);return xe.fromDom(Ee(t).dom.defaultView)},range:ui},vm=document.caretPositionFromPoint?(e,t,o)=>{var n,r;return C.from(null===(r=(n=e.dom).caretPositionFromPoint)||void 0===r?void 0:r.call(n,t,o)).bind((t=>{if(null===t.offsetNode)return C.none();const o=e.dom.createRange();return o.setStart(t.offsetNode,t.offset),o.collapse(),C.some(o)}))}:document.caretRangeFromPoint?(e,t,o)=>{var n,r;return C.from(null===(r=(n=e.dom).caretRangeFromPoint)||void 0===r?void 0:r.call(n,t,o))}:C.none,ym=(e,t)=>{const o=ne(e);return"input"===o?ei.after(e):D(["br","img"],o)?0===t?ei.before(e):ei.after(e):ei.on(e,t)},xm=e=>C.from(e.getSelection()),Cm=(e,t)=>{xm(e).each((e=>{e.removeAllRanges(),e.addRange(t)}))},Sm=(e,t,o,n,r)=>{const s=li(e,t,o,n,r);Cm(e,s)},Tm=(e,t)=>mi(e,t).match({ltr:(t,o,n,r)=>{Sm(e,t,o,n,r)},rtl:(t,o,n,r)=>{xm(e).each((s=>{if(s.setBaseAndExtent)s.setBaseAndExtent(t.dom,o,n.dom,r);else if(s.extend)try{((e,t,o,n,r,s)=>{t.collapse(o.dom,n),t.extend(r.dom,s)})(0,s,t,o,n,r)}catch(s){Sm(e,n,r,t,o)}else Sm(e,n,r,t,o)}))}}),Rm=(e,t,o,n,r)=>{const s=((e,t,o,n)=>{const r=ym(e,t),s=ym(o,n);return bm.relative(r,s)})(t,o,n,r);Tm(e,s)},Dm=(e,t,o)=>{const n=((e,t)=>{const o=e.fold(ei.before,ym,ei.after),n=t.fold(ei.before,ym,ei.after);return bm.relative(o,n)})(t,o);Tm(e,n)},Om=e=>{if(e.rangeCount>0){const t=e.getRangeAt(0),o=e.getRangeAt(e.rangeCount-1);return C.some(ui(xe.fromDom(t.startContainer),t.startOffset,xe.fromDom(o.endContainer),o.endOffset))}return C.none()},km=e=>{if(null===e.anchorNode||null===e.focusNode)return Om(e);{const t=xe.fromDom(e.anchorNode),o=xe.fromDom(e.focusNode);return((e,t,o,n)=>{const r=((e,t,o,n)=>{const r=ke(e).dom.createRange();return r.setStart(e.dom,t),r.setEnd(o.dom,n),r})(e,t,o,n),s=Re(e,o)&&t===n;return r.collapsed&&!s})(t,e.anchorOffset,o,e.focusOffset)?C.some(ui(t,e.anchorOffset,o,e.focusOffset)):Om(e)}},Em=(e,t,o=!0)=>{const n=(o?ni:oi)(e,t);Cm(e,n)},Nm=e=>(e=>xm(e).filter((e=>e.rangeCount>0)).bind(km))(e).map((e=>bm.exact(e.start,e.soffset,e.finish,e.foffset))),Bm=e=>({elementFromPoint:(t,o)=>xe.fromPoint(xe.fromDom(e.document),t,o),getRect:e=>e.dom.getBoundingClientRect(),getRangedRect:(t,o,n,r)=>{const s=bm.exact(t,o,n,r);return((e,t)=>(e=>{const t=e.getClientRects(),o=t.length>0?t[0]:e.getBoundingClientRect();return o.width>0||o.height>0?C.some(o).map(ai):C.none()})(di(e,t)))(e,s)},getSelection:()=>Nm(e).map((t=>gi(e,t))),fromSitus:t=>{const o=bm.relative(t.start,t.finish);return gi(e,o)},situsFromPoint:(t,o)=>((e,t,o)=>((e,t,o)=>{const n=xe.fromDom(e.document);return vm(n,t,o).map((e=>ui(xe.fromDom(e.startContainer),e.startOffset,xe.fromDom(e.endContainer),e.endOffset)))})(e,t,o))(e,t,o).map((e=>fi(e.start,e.soffset,e.finish,e.foffset))),clearSelection:()=>{(e=>{xm(e).each((e=>e.removeAllRanges()))})(e)},collapseSelection:(t=!1)=>{Nm(e).each((o=>o.fold((e=>e.collapse(t)),((o,n)=>{const r=t?o:n;Dm(e,r,r)}),((o,n,r,s)=>{const l=t?o:r,a=t?n:s;Rm(e,l,a,l,a)}))))},setSelection:t=>{Rm(e,t.start,t.soffset,t.finish,t.foffset)},setRelativeSelection:(t,o)=>{Dm(e,t,o)},selectNode:t=>{Em(e,t,!1)},selectContents:t=>{Em(e,t)},getInnerHeight:()=>e.innerHeight,getScrollY:()=>(e=>{const t=void 0!==e?e.dom:document,o=t.body.scrollLeft||t.documentElement.scrollLeft,n=t.body.scrollTop||t.documentElement.scrollTop;return bn(o,n)})(xe.fromDom(e.document)).top,scrollBy:(t,o)=>{((e,t,o)=>{const n=(void 0!==o?o.dom:document).defaultView;n&&n.scrollBy(e,t)})(t,o,xe.fromDom(e.document))}}),_m=(e,t)=>({rows:e,cols:t}),zm=e=>gt(e,ae).exists(Qr),Am=(e,t)=>zm(e)||zm(t),Lm=e=>void 0!==e.dom.classList,Wm=(e,t)=>((e,t,o)=>{const n=((e,t)=>{const o=pe(e,t);return void 0===o||""===o?[]:o.split(" ")})(e,t).concat([o]);return ge(e,t,n.join(" ")),!0})(e,"class",t),Mm=(e,t)=>{Lm(e)?e.dom.classList.add(t):Wm(e,t)},jm=(e,t)=>Lm(e)&&e.dom.classList.contains(t),Pm=()=>({tag:"none"}),Im=e=>({tag:"multiple",elements:e}),Fm=e=>({tag:"single",element:e}),Hm=e=>{const t=xe.fromDom((e=>{if(nt()&&m(e.target)){const t=xe.fromDom(e.target);if(ce(t)&&m(t.dom.shadowRoot)&&e.composed&&e.composedPath){const t=e.composedPath();if(t)return H(t)}}return C.from(e.target)})(e).getOr(e.target)),o=()=>e.stopPropagation(),n=()=>e.preventDefault(),r=(s=n,l=o,(...e)=>s(l.apply(null,e)));var s,l;return((e,t,o,n,r,s,l)=>({target:e,x:t,y:o,stop:n,prevent:r,kill:s,raw:l}))(t,e.clientX,e.clientY,o,n,r,e)},$m=(e,t,o,n)=>{e.dom.removeEventListener(t,o,n)},Vm=x,qm=(e,t,o)=>((e,t,o,n)=>((e,t,o,n,r)=>{const s=((e,t)=>o=>{e(o)&&t(Hm(o))})(o,n);return e.dom.addEventListener(t,s,r),{unbind:w($m,e,t,s,r)}})(e,t,o,n,!1))(e,t,Vm,o),Um=Hm,Gm=e=>!jm(xe.fromDom(e.target),"ephox-snooker-resizer-bar"),Km=(e,t)=>{const o=(r=As.selectedSelector,{get:()=>Rs(xe.fromDom(e.getBody()),r).fold((()=>js(os(e),es(e)).fold(Pm,Fm)),Im)}),n=((e,t,o)=>{const n=t=>{be(t,e.selected),be(t,e.firstSelected),be(t,e.lastSelected)},r=t=>{ge(t,e.selected,"1")},s=e=>{l(e),o()},l=t=>{const o=dt(t,`${e.selectedSelector},${e.firstSelectedSelector},${e.lastSelectedSelector}`);N(o,n)};return{clearBeforeUpdate:l,clear:s,selectRange:(o,n,l,a)=>{s(o),N(n,r),ge(l,e.firstSelected,"1"),ge(a,e.lastSelected,"1"),t(n,l,a)},selectedSelector:e.selectedSelector,firstSelectedSelector:e.firstSelectedSelector,lastSelectedSelector:e.lastSelectedSelector}})(As,((t,o,n)=>{Kt(o).each((r=>{const s=Wr(e),l=Br(f,xe.fromDom(e.getDoc()),s),a=((e,t,o)=>{const n=Zo(e);return Ol(n,t).map((e=>{const t=xl(n,o,!1),{rows:r}=qo(t),s=((e,t)=>{const o=e.slice(0,t[t.length-1].row+1),n=Cl(o);return j(n,(e=>{const o=e.cells.slice(0,t[t.length-1].column+1);return E(o,(e=>e.element))}))})(r,e),l=((e,t)=>{const o=e.slice(t[0].row+t[0].rowspan-1,e.length),n=Cl(o);return j(n,(e=>{const o=e.cells.slice(t[0].column+t[0].colspan-1,e.cells.length);return E(o,(e=>e.element))}))})(r,e);return{upOrLeftCells:s,downOrRightCells:l}}))})(r,{selection:Ps(e)},l);((e,t,o,n,r)=>{e.dispatch("TableSelectionChange",{cells:t,start:o,finish:n,otherCells:r})})(e,t,o,n,a)}))}),(()=>(e=>{e.dispatch("TableSelectionClear")})(e)));var r;return e.on("init",(o=>{const r=e.getWin(),s=Zr(e),l=es(e),a=((e,t,o,n)=>{const r=((e,t,o,n)=>{const r=lm(),s=r.clear,l=s=>{r.on((r=>{n.clearBeforeUpdate(t),am(s.target,o).each((l=>{xs(r,l,o).each((o=>{const r=o.boxes.getOr([]);if(1===r.length){const o=r[0],l="false"===Xr(o),a=vt(Jr(s.target),o,Re);l&&a&&(n.selectRange(t,r,o,o),e.selectContents(o))}else r.length>1&&(n.selectRange(t,r,o.start,o.finish),e.selectContents(l))}))}))}))};return{clearstate:s,mousedown:e=>{n.clear(t),am(e.target,o).filter(cm).each(r.set)},mouseover:e=>{l(e)},mouseup:e=>{l(e),s()}}})(Bm(e),t,o,n);return{clearstate:r.clearstate,mousedown:r.mousedown,mouseover:r.mouseover,mouseup:r.mouseup}})(r,s,l,n),c=((e,t,o,n)=>{const r=Bm(e),s=()=>(n.clear(t),C.none());return{keydown:(e,l,a,c,i,m)=>{const d=e.raw,u=d.which,f=!0===d.shiftKey,g=Cs(t,n.selectedSelector).fold((()=>(gm(u)&&!f&&n.clearBeforeUpdate(t),gm(u)&&f&&!Am(l,c)?C.none:fm(u)&&f?w(rm,r,t,o,im,c,l,n.selectRange):um(u)&&f?w(rm,r,t,o,mm,c,l,n.selectRange):fm(u)?w(tm,r,o,im,c,l,nm):um(u)?w(tm,r,o,mm,c,l,om):C.none)),(e=>{const o=o=>()=>{const s=V(o,(o=>((e,t,o,n,r)=>Ts(n,e,t,r.firstSelectedSelector,r.lastSelectedSelector).map((e=>(r.clearBeforeUpdate(o),r.selectRange(o,e.boxes,e.start,e.finish),e.boxes))))(o.rows,o.cols,t,e,n)));return s.fold((()=>Ss(t,n.firstSelectedSelector,n.lastSelectedSelector).map((e=>{const o=fm(u)||m.isForward(u)?ei.after:ei.before;return r.setRelativeSelection(ei.on(e.first,0),o(e.table)),n.clear(t),ti(C.none(),!0)}))),(e=>C.some(ti(C.none(),!0))))};return gm(u)&&f&&!Am(l,c)?C.none:fm(u)&&f?o([_m(1,0)]):um(u)&&f?o([_m(-1,0)]):m.isBackward(u)&&f?o([_m(0,-1),_m(-1,0)]):m.isForward(u)&&f?o([_m(0,1),_m(1,0)]):gm(u)&&!f?s:C.none}));return g()},keyup:(e,r,s,l,a)=>Cs(t,n.selectedSelector).fold((()=>{const c=e.raw,i=c.which;return!0===c.shiftKey&&gm(i)&&Am(r,l)?((e,t,o,n,r,s,l)=>Re(o,r)&&n===s?C.none():bt(o,"td,th",t).bind((o=>bt(r,"td,th",t).bind((n=>pi(e,t,o,n,l))))))(t,o,r,s,l,a,n.selectRange):C.none()}),C.none)}})(r,s,l,n),i=((e,t,o,n)=>{const r=Bm(e);return(e,s)=>{n.clearBeforeUpdate(t),xs(e,s,o).each((e=>{const o=e.boxes.getOr([]);n.selectRange(t,o,e.start,e.finish),r.selectContents(s),r.collapseSelection()}))}})(r,s,l,n);e.on("TableSelectorChange",(e=>i(e.start,e.finish)));const m=(t,o)=>{(e=>!0===e.raw.shiftKey)(t)&&(o.kill&&t.kill(),o.selection.each((t=>{const o=bm.relative(t.start,t.finish),n=di(r,o);e.selection.setRng(n)})))},d=e=>0===e.button,u=(()=>{const e=sm(xe.fromDom(s)),t=sm(0);return{touchEnd:o=>{const n=xe.fromDom(o.target);if(ue("td")(n)||ue("th")(n)){const r=e.get(),s=t.get();Re(r,n)&&o.timeStamp-s<300&&(o.preventDefault(),i(n,n))}e.set(n),t.set(o.timeStamp)}}})();e.on("dragstart",(e=>{a.clearstate()})),e.on("mousedown",(e=>{d(e)&&Gm(e)&&a.mousedown(Um(e))})),e.on("mouseover",(e=>{var t;void 0!==(t=e).buttons&&0==(1&t.buttons)||!Gm(e)||a.mouseover(Um(e))})),e.on("mouseup",(e=>{d(e)&&Gm(e)&&a.mouseup(Um(e))})),e.on("touchend",u.touchEnd),e.on("keyup",(t=>{const o=Um(t);if(o.raw.shiftKey&&gm(o.raw.which)){const t=e.selection.getRng(),n=xe.fromDom(t.startContainer),r=xe.fromDom(t.endContainer);c.keyup(o,n,t.startOffset,r,t.endOffset).each((e=>{m(o,e)}))}})),e.on("keydown",(o=>{const n=Um(o);t.hide();const r=e.selection.getRng(),s=xe.fromDom(r.startContainer),l=xe.fromDom(r.endContainer),a=un(hm,pm)(xe.fromDom(e.selection.getStart()));c.keydown(n,s,r.startOffset,l,r.endOffset,a).each((e=>{m(n,e)})),t.show()})),e.on("NodeChange",(()=>{const t=e.selection,o=xe.fromDom(t.getStart()),r=xe.fromDom(t.getEnd());vs(Kt,[o,r]).fold((()=>n.clear(s)),f)}))})),e.on("PreInit",(()=>{e.serializer.addTempAttr(As.firstSelected),e.serializer.addTempAttr(As.lastSelected)})),{getSelectedCells:()=>((e,t,o,n)=>{switch(e.tag){case"none":return t();case"single":return(e=>[e.dom])(e.element);case"multiple":return(e=>E(e,(e=>e.dom)))(e.elements)}})(o.get(),g([])),clearSelectedCells:e=>n.clear(xe.fromDom(e))}},Ym=e=>{let t=[];return{bind:e=>{if(void 0===e)throw new Error("Event bind error: undefined handler");t.push(e)},unbind:e=>{t=_(t,(t=>t!==e))},trigger:(...o)=>{const n={};N(e,((e,t)=>{n[e]=o[t]})),N(t,(e=>{e(n)}))}}},Jm=e=>({registry:K(e,(e=>({bind:e.bind,unbind:e.unbind}))),trigger:K(e,(e=>e.trigger))}),Qm=e=>e.slice(0).sort(),Xm=(e,t)=>{const o=_(t,(t=>!D(e,t)));o.length>0&&(e=>{throw new Error("Unsupported keys for object: "+Qm(e).join(", "))})(o)},Zm=e=>((e,t)=>((e,t,o)=>{if(0===t.length)throw new Error("You must specify at least one required field.");return((e,t)=>{if(!l(t))throw new Error("The "+e+" fields must be an array. Was: "+t+".");N(t,(t=>{if(!r(t))throw new Error("The value "+t+" in the "+e+" fields was not a string.")}))})("required",t),(e=>{const t=Qm(e);L(t,((e,o)=>o{throw new Error("The field: "+e+" occurs more than once in the combined fields: ["+t.join(", ")+"].")}))})(t),n=>{const r=q(n);P(t,(e=>D(r,e)))||((e,t)=>{throw new Error("All required keys ("+Qm(e).join(", ")+") were not specified. Specified keys were: "+Qm(t).join(", ")+".")})(t,r),e(t,r);const s=_(t,(e=>!o.validate(n[e],e)));return s.length>0&&((e,t)=>{throw new Error("All values need to be of type: "+t+". Keys ("+Qm(e).join(", ")+") were not.")})(s,o.label),n}})(e,t,{validate:d,label:"function"}))(Xm,e),ed=Zm(["compare","extract","mutate","sink"]),td=Zm(["element","start","stop","destroy"]),od=Zm(["forceDrop","drop","move","delayDrop"]),nd=()=>{const e=(()=>{const e=Jm({move:Ym(["info"])});return{onEvent:f,reset:f,events:e.registry}})(),t=(()=>{let e=C.none();const t=Jm({move:Ym(["info"])});return{onEvent:(o,n)=>{n.extract(o).each((o=>{const r=((t,o)=>{const n=e.map((e=>t.compare(e,o)));return e=C.some(o),n})(n,o);r.each((e=>{t.trigger.move(e)}))}))},reset:()=>{e=C.none()},events:t.registry}})();let o=e;return{on:()=>{o.reset(),o=t},off:()=>{o.reset(),o=e},isOn:()=>o===t,onEvent:(e,t)=>{o.onEvent(e,t)},events:t.events}},rd=e=>{const t=e.replace(/\./g,"-");return{resolve:e=>t+"-"+e}},sd=rd("ephox-dragster").resolve;var ld=ed({compare:(e,t)=>bn(t.left-e.left,t.top-e.top),extract:e=>C.some(bn(e.x,e.y)),sink:(e,t)=>{const o=(e=>{const t={layerClass:sd("blocker"),...e},o=xe.fromTag("div");return ge(o,"role","presentation"),Bt(o,{position:"fixed",left:"0px",top:"0px",width:"100%",height:"100%"}),Mm(o,sd("blocker")),Mm(o,t.layerClass),{element:g(o),destroy:()=>{qe(o)}}})(t),n=qm(o.element(),"mousedown",e.forceDrop),r=qm(o.element(),"mouseup",e.drop),s=qm(o.element(),"mousemove",e.move),l=qm(o.element(),"mouseout",e.delayDrop);return td({element:o.element,start:e=>{Ie(e,o.element())},stop:()=>{qe(o.element())},destroy:()=>{o.destroy(),r.unbind(),s.unbind(),l.unbind(),n.unbind()}})},mutate:(e,t)=>{e.mutate(t.left,t.top)}});const ad=rd("ephox-snooker").resolve,cd=ad("resizer-bar"),id=ad("resizer-rows"),md=ad("resizer-cols"),dd=e=>{const t=dt(e.parent(),"."+cd);N(t,qe)},ud=(e,t,o)=>{const n=e.origin();N(t,(t=>{t.each((t=>{const r=o(n,t);Mm(r,cd),Ie(e.parent(),r)}))}))},fd=(e,t,o,n,r)=>{const s=yn(o),l=t.isResizable,a=n.length>0?_n.positions(n,o):[],c=a.length>0?((e,t)=>j(e.all,((e,o)=>t(e.element)?[o]:[])))(e,l):[];((e,t,o,n)=>{ud(e,t,((e,t)=>{const r=((e,t,o,n,r)=>{const s=xe.fromTag("div");return Bt(s,{position:"absolute",left:t+"px",top:o-3.5+"px",height:"7px",width:n+"px"}),he(s,{"data-row":e,role:"presentation"}),s})(t.row,o.left-e.left,t.y-e.top,n);return Mm(r,id),r}))})(t,_(a,((e,t)=>O(c,(e=>t===e)))),s,Wo(o));const i=r.length>0?An.positions(r,o):[],m=i.length>0?((e,t)=>{const o=[];return k(e.grid.columns,(n=>{an(e,n).map((e=>e.element)).forall(t)&&o.push(n)})),_(o,(o=>{const n=nn(e,(e=>e.column===o));return P(n,(e=>t(e.element)))}))})(e,l):[];((e,t,o,n)=>{ud(e,t,((e,t)=>{const r=((e,t,o,n,r)=>{const s=xe.fromTag("div");return Bt(s,{position:"absolute",left:t-3.5+"px",top:o+"px",height:r+"px",width:"7px"}),he(s,{"data-column":e,role:"presentation"}),s})(t.col,t.x-e.left,o.top-e.top,0,n);return Mm(r,md),r}))})(t,_(i,((e,t)=>O(m,(e=>t===e)))),s,pn(o))},gd=(e,t)=>{if(dd(e),e.isResizable(t)){const o=Zo(t),n=dn(o),r=cn(o);fd(o,e,t,n,r)}},hd=(e,t)=>{const o=dt(e.parent(),"."+cd);N(o,t)},pd=e=>{hd(e,(e=>{Nt(e,"display","none")}))},wd=e=>{hd(e,(e=>{Nt(e,"display","block")}))},bd=ad("resizer-bar-dragging"),vd=e=>{const t=(()=>{const e=Jm({drag:Ym(["xDelta","yDelta","target"])});let t=C.none();const o=(()=>{const e=Jm({drag:Ym(["xDelta","yDelta"])});return{mutate:(t,o)=>{e.trigger.drag(t,o)},events:e.registry}})();return o.events.drag.bind((o=>{t.each((t=>{e.trigger.drag(o.xDelta,o.yDelta,t)}))})),{assign:e=>{t=C.some(e)},get:()=>t,mutate:o.mutate,events:e.registry}})(),o=((e,t={})=>{var o;return((e,t,o)=>{let n=!1;const r=Jm({start:Ym([]),stop:Ym([])}),s=nd(),l=()=>{m.stop(),s.isOn()&&(s.off(),r.trigger.stop())},c=((e,t)=>{let o=null;const n=()=>{a(o)||(clearTimeout(o),o=null)};return{cancel:n,throttle:(...t)=>{n(),o=setTimeout((()=>{o=null,e.apply(null,t)}),200)}}})(l);s.events.move.bind((o=>{t.mutate(e,o.info)}));const i=e=>(...t)=>{n&&e.apply(null,t)},m=t.sink(od({forceDrop:l,drop:i(l),move:i((e=>{c.cancel(),s.onEvent(e,t)})),delayDrop:i(c.throttle)}),o);return{element:m.element,go:e=>{m.start(e),s.on(),r.trigger.start()},on:()=>{n=!0},off:()=>{n=!1},isActive:()=>n,destroy:()=>{m.destroy()},events:r.registry}})(e,null!==(o=t.mode)&&void 0!==o?o:ld,t)})(t,{});let n=C.none();const r=(e,t)=>C.from(pe(e,t));t.events.drag.bind((e=>{r(e.target,"data-row").each((t=>{const o=It(e.target,"top");Nt(e.target,"top",o+e.yDelta+"px")})),r(e.target,"data-column").each((t=>{const o=It(e.target,"left");Nt(e.target,"left",o+e.xDelta+"px")}))}));const s=(e,t)=>It(e,t)-Wt(e,"data-initial-"+t,0);o.events.stop.bind((()=>{t.get().each((t=>{n.each((o=>{r(t,"data-row").each((e=>{const n=s(t,"top");be(t,"data-initial-top"),d.trigger.adjustHeight(o,n,parseInt(e,10))})),r(t,"data-column").each((e=>{const n=s(t,"left");be(t,"data-initial-left"),d.trigger.adjustWidth(o,n,parseInt(e,10))})),gd(e,o)}))}))}));const l=(n,r)=>{d.trigger.startAdjust(),t.assign(n),ge(n,"data-initial-"+r,It(n,r)),Mm(n,bd),Nt(n,"opacity","0.2"),o.go(e.parent())},c=qm(e.parent(),"mousedown",(e=>{var t;t=e.target,jm(t,id)&&l(e.target,"top"),(e=>jm(e,md))(e.target)&&l(e.target,"left")})),i=t=>Re(t,e.view()),m=qm(e.view(),"mouseover",(t=>{var r;(r=t.target,bt(r,"table",i).filter(Qr)).fold((()=>{lt(t.target)&&dd(e)}),(t=>{o.isActive()&&(n=C.some(t),gd(e,t))}))})),d=Jm({adjustHeight:Ym(["table","delta","row"]),adjustWidth:Ym(["table","delta","column"]),startAdjust:Ym([])});return{destroy:()=>{c.unbind(),m.unbind(),o.destroy(),dd(e)},refresh:t=>{gd(e,t)},on:o.on,off:o.off,hideBars:w(pd,e),showBars:w(wd,e),events:d.registry}},yd=(e,t,o)=>{const n=_n,r=An,s=vd(e),l=Jm({beforeResize:Ym(["table","type"]),afterResize:Ym(["table","type"]),startDrag:Ym([])});return s.events.adjustHeight.bind((e=>{const t=e.table;l.trigger.beforeResize(t,"row");((e,t,o,n)=>{const r=Zo(e),s=((e,t,o)=>lr(e,t,o,Yn,(e=>e.getOrThunk(Ht))))(r,e,n),l=E(s,((e,n)=>o===n?Math.max(t+e,Ht()):e)),a=oa(r,l),c=((e,t)=>E(e.all,((e,o)=>({element:e.element,height:t[o]}))))(r,l);N(c,(e=>{$n(e.element,e.height)})),N(a,(e=>{$n(e.element,e.height)}));const i=z(l,((e,t)=>e+t),0);$n(e,i)})(t,n.delta(e.delta,t),e.row,n),l.trigger.afterResize(t,"row")})),s.events.startAdjust.bind((e=>{l.trigger.startDrag()})),s.events.adjustWidth.bind((e=>{const n=e.table;l.trigger.beforeResize(n,"col");const s=r.delta(e.delta,n),a=o(n);ra(n,s,e.column,t,a),l.trigger.afterResize(n,"col")})),{on:s.on,off:s.off,refreshBars:s.refresh,hideBars:s.hideBars,showBars:s.showBars,destroy:s.destroy,events:l.registry}},xd=e=>m(e)&&"TABLE"===e.nodeName,Cd="bar-",Sd=e=>"false"!==pe(e,"data-mce-resize"),Td=e=>{const t=lm(),o=lm(),n=lm();let r,s;const l=t=>fc(e,t),a=()=>Pr(e)?el():Zs();return e.on("init",(()=>{const r=((e,t)=>e.inline?((e,t,o)=>({parent:g(t),view:g(e),origin:g(bn(0,0)),isResizable:o}))(xe.fromDom(e.getBody()),(()=>{const e=xe.fromTag("div");return Bt(e,{position:"static",height:"0",width:"0",padding:"0",margin:"0",border:"0"}),Ie(at(xe.fromDom(document)),e),e})(),t):((e,t)=>{const o=me(e)?(e=>xe.fromDom(Ee(e).dom.documentElement))(e):e;return{parent:g(o),view:g(e),origin:g(bn(0,0)),isResizable:t}})(xe.fromDom(e.getDoc()),t))(e,Sd);if(n.set(r),(e=>{const t=e.options.get("object_resizing");return D(t.split(","),"table")})(e)&&qr(e)){const n=a(),s=yd(r,n,l);s.on(),s.events.startDrag.bind((o=>{t.set(e.selection.getRng())})),s.events.beforeResize.bind((t=>{const o=t.table.dom;((e,t,o,n,r)=>{e.dispatch("ObjectResizeStart",{target:t,width:o,height:n,origin:r})})(e,o,ns(o),rs(o),Cd+t.type)})),s.events.afterResize.bind((o=>{const n=o.table,r=n.dom;ts(n),t.on((t=>{e.selection.setRng(t),e.focus()})),((e,t,o,n,r)=>{e.dispatch("ObjectResized",{target:t,width:o,height:n,origin:r})})(e,r,ns(r),rs(r),Cd+o.type),e.undoManager.add()})),o.set(s)}})),e.on("ObjectResizeStart",(t=>{const o=t.target;if(xd(o)){const n=xe.fromDom(o);N(e.dom.select(".mce-clonedresizable"),(t=>{e.dom.addClass(t,"mce-"+jr(e)+"-columns")})),!kc(n)&&$r(e)?_c(n):!Oc(n)&&Hr(e)&&Bc(n),Ec(n)&&Tt(t.origin,Cd)&&Bc(n),r=t.width,s=Vr(e)?"":((e,t)=>{const o=e.dom.getStyle(t,"width")||e.dom.getAttrib(t,"width");return C.from(o).filter(Ot)})(e,o).getOr("")}})),e.on("ObjectResized",(t=>{const o=t.target;if(xd(o)){const n=xe.fromDom(o),c=t.origin;Tt(c,"corner-")&&((t,o,n)=>{const c=Rt(o,"e");if(""===s&&Bc(t),n!==r&&""!==s){Nt(t,"width",s);const o=a(),i=l(t),m=Pr(e)||c?(e=>tl(e).columns)(t)-1:0;ra(t,n-r,m,o,i)}else if((e=>/^(\d+(\.\d+)?)%$/.test(e))(s)){const e=parseFloat(s.replace("%",""));Nt(t,"width",n*e/r+"%")}(e=>/^(\d+(\.\d+)?)px$/.test(e))(s)&&(e=>{const t=Zo(e);ln(t)||N(Ut(e),(e=>{const t=_t(e,"width");Nt(e,"width",t),be(e,"width")}))})(t)})(n,c,t.width),ts(n),ic(e,n.dom,mc)}})),e.on("SwitchMode",(()=>{o.on((t=>{e.mode.isReadOnly()?t.hideBars():t.showBars()}))})),e.on("dragstart dragend",(e=>{o.on((t=>{"dragstart"===e.type?(t.hideBars(),t.off()):(t.on(),t.showBars())}))})),e.on("remove",(()=>{o.on((e=>{e.destroy()})),n.on((t=>{((e,t)=>{e.inline&&qe(t.parent())})(e,t)}))})),{refresh:e=>{o.on((t=>t.refreshBars(xe.fromDom(e))))},hide:()=>{o.on((e=>e.hideBars()))},show:()=>{o.on((e=>e.showBars()))}}},Rd=e=>{(e=>{const t=e.options.register;t("table_clone_elements",{processor:"string[]"}),t("table_use_colgroups",{processor:"boolean",default:!0}),t("table_header_type",{processor:e=>{const t=D(["section","cells","sectionCells","auto"],e);return t?{value:e,valid:t}:{valid:!1,message:"Must be one of: section, cells, sectionCells or auto."}},default:"section"}),t("table_sizing_mode",{processor:"string",default:"auto"}),t("table_default_attributes",{processor:"object",default:{border:"1"}}),t("table_default_styles",{processor:"object",default:{"border-collapse":"collapse"}}),t("table_column_resizing",{processor:e=>{const t=D(["preservetable","resizetable"],e);return t?{value:e,valid:t}:{valid:!1,message:"Must be preservetable, or resizetable."}},default:"preservetable"}),t("table_resize_bars",{processor:"boolean",default:!0}),t("table_style_by_css",{processor:"boolean",default:!0}),t("table_merge_content_on_paste",{processor:"boolean",default:!0})})(e);const t=Td(e),o=Km(e,t),n=gc(e,t,o);return Xc(e,n),((e,t)=>{const o=es(e),n=t=>js(os(e)).bind((n=>Kt(n,o).map((o=>{const r=Ls(Ps(e),o,n);return t(o,r)})))).getOr("");G({mceTableRowType:()=>n(t.getTableRowType),mceTableCellType:()=>n(t.getTableCellType),mceTableColType:()=>n(t.getTableColType)},((t,o)=>e.addQueryValueHandler(o,t)))})(e,n),Is(e,n),{getSelectedCells:o.getSelectedCells,clearSelectedCells:o.clearSelectedCells}};e.add("dom",(e=>({table:Rd(e)})))}(); \ No newline at end of file diff --git a/public/libs/tinymce/plugins/advlist/plugin.min.js b/public/libs/tinymce/plugins/advlist/plugin.min.js index 244b01974ce..8be78ea4211 100644 --- a/public/libs/tinymce/plugins/advlist/plugin.min.js +++ b/public/libs/tinymce/plugins/advlist/plugin.min.js @@ -1,4 +1,4 @@ /** - * TinyMCE version 6.7.2 (2023-10-25) + * TinyMCE version 6.8.4 (2024-06-19) */ !function(){"use strict";var t=tinymce.util.Tools.resolve("tinymce.PluginManager");const e=(t,e,s)=>{const r="UL"===e?"InsertUnorderedList":"InsertOrderedList";t.execCommand(r,!1,!1===s?null:{"list-style-type":s})},s=t=>e=>e.options.get(t),r=s("advlist_number_styles"),n=s("advlist_bullet_styles"),i=t=>null==t,l=t=>!i(t);var o=tinymce.util.Tools.resolve("tinymce.util.Tools");class a{constructor(t,e){this.tag=t,this.value=e}static some(t){return new a(!0,t)}static none(){return a.singletonNone}fold(t,e){return this.tag?e(this.value):t()}isSome(){return this.tag}isNone(){return!this.tag}map(t){return this.tag?a.some(t(this.value)):a.none()}bind(t){return this.tag?t(this.value):a.none()}exists(t){return this.tag&&t(this.value)}forall(t){return!this.tag||t(this.value)}filter(t){return!this.tag||t(this.value)?this:a.none()}getOr(t){return this.tag?this.value:t}or(t){return this.tag?this:t}getOrThunk(t){return this.tag?this.value:t()}orThunk(t){return this.tag?this:t()}getOrDie(t){if(this.tag)return this.value;throw new Error(null!=t?t:"Called getOrDie on None")}static from(t){return l(t)?a.some(t):a.none()}getOrNull(){return this.tag?this.value:null}getOrUndefined(){return this.value}each(t){this.tag&&t(this.value)}toArray(){return this.tag?[this.value]:[]}toString(){return this.tag?`some(${this.value})`:"none()"}}a.singletonNone=new a(!1);const u=t=>e=>l(e)&&t.test(e.nodeName),d=u(/^(OL|UL|DL)$/),g=u(/^(TH|TD)$/),c=t=>i(t)||"default"===t?"":t,h=(t,e)=>s=>((t,e)=>{const s=t.selection.getNode();return e({parents:t.dom.getParents(s),element:s}),t.on("NodeChange",e),()=>t.off("NodeChange",e)})(t,(r=>((t,r)=>{const n=t.selection.getStart(!0);s.setActive(((t,e,s)=>((t,e,s)=>{for(let e=0,n=t.length;ee.nodeName===s&&((t,e)=>t.dom.isChildOf(e,t.getBody()))(t,e))))(t,r,e)),s.setEnabled(!((t,e)=>{const s=t.dom.getParent(e,"ol,ul,dl");return((t,e)=>null!==e&&!t.dom.isEditable(e))(t,s)&&t.selection.isEditable()})(t,n)&&t.selection.isEditable())})(t,r.parents))),m=(t,s,r,n,i,l)=>{l.length>1?((t,s,r,n,i,l)=>{t.ui.registry.addSplitButton(s,{tooltip:r,icon:"OL"===i?"ordered-list":"unordered-list",presets:"listpreview",columns:3,fetch:t=>{t(o.map(l,(t=>{const e="OL"===i?"num":"bull",s="disc"===t||"decimal"===t?"default":t,r=c(t),n=(t=>t.replace(/\-/g," ").replace(/\b\w/g,(t=>t.toUpperCase())))(t);return{type:"choiceitem",value:r,icon:"list-"+e+"-"+s,text:n}})))},onAction:()=>t.execCommand(n),onItemAction:(s,r)=>{e(t,i,r)},select:e=>{const s=(t=>{const e=t.dom.getParent(t.selection.getNode(),"ol,ul"),s=t.dom.getStyle(e,"listStyleType");return a.from(s)})(t);return s.map((t=>e===t)).getOr(!1)},onSetup:h(t,i)})})(t,s,r,n,i,l):((t,s,r,n,i,l)=>{t.ui.registry.addToggleButton(s,{active:!1,tooltip:r,icon:"OL"===i?"ordered-list":"unordered-list",onSetup:h(t,i),onAction:()=>t.queryCommandState(n)||""===l?t.execCommand(n):e(t,i,l)})})(t,s,r,n,i,c(l[0]))};t.add("advlist",(t=>{t.hasPlugin("lists")?((t=>{const e=t.options.register;e("advlist_number_styles",{processor:"string[]",default:"default,lower-alpha,lower-greek,lower-roman,upper-alpha,upper-roman".split(",")}),e("advlist_bullet_styles",{processor:"string[]",default:"default,circle,square".split(",")})})(t),(t=>{m(t,"numlist","Numbered list","InsertOrderedList","OL",r(t)),m(t,"bullist","Bullet list","InsertUnorderedList","UL",n(t))})(t),(t=>{t.addCommand("ApplyUnorderedListStyle",((s,r)=>{e(t,"UL",r["list-style-type"])})),t.addCommand("ApplyOrderedListStyle",((s,r)=>{e(t,"OL",r["list-style-type"])}))})(t)):console.error("Please use the Lists plugin together with the Advanced List plugin.")}))}(); \ No newline at end of file diff --git a/public/libs/tinymce/plugins/anchor/plugin.min.js b/public/libs/tinymce/plugins/anchor/plugin.min.js index e1c650ff1c3..8049b4d3ae5 100644 --- a/public/libs/tinymce/plugins/anchor/plugin.min.js +++ b/public/libs/tinymce/plugins/anchor/plugin.min.js @@ -1,4 +1,4 @@ /** - * TinyMCE version 6.7.2 (2023-10-25) + * TinyMCE version 6.8.4 (2024-06-19) */ !function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager"),t=tinymce.util.Tools.resolve("tinymce.dom.RangeUtils"),o=tinymce.util.Tools.resolve("tinymce.util.Tools");const n=("allow_html_in_named_anchor",e=>e.options.get("allow_html_in_named_anchor"));const a="a:not([href])",r=e=>!e,i=e=>e.getAttribute("id")||e.getAttribute("name")||"",l=e=>(e=>"a"===e.nodeName.toLowerCase())(e)&&!e.getAttribute("href")&&""!==i(e),s=e=>e.dom.getParent(e.selection.getStart(),a),d=(e,a)=>{const r=s(e);r?((e,t,o)=>{o.removeAttribute("name"),o.id=t,e.addVisual(),e.undoManager.add()})(e,a,r):((e,a)=>{e.undoManager.transact((()=>{n(e)||e.selection.collapse(!0),e.selection.isCollapsed()?e.insertContent(e.dom.createHTML("a",{id:a})):((e=>{const n=e.dom;t(n).walk(e.selection.getRng(),(e=>{o.each(e,(e=>{var t;l(t=e)&&!t.firstChild&&n.remove(e,!1)}))}))})(e),e.formatter.remove("namedAnchor",void 0,void 0,!0),e.formatter.apply("namedAnchor",{value:a}),e.addVisual())}))})(e,a),e.focus()},c=e=>(e=>r(e.attr("href"))&&!r(e.attr("id")||e.attr("name")))(e)&&!e.firstChild,m=e=>t=>{for(let o=0;ot=>{const o=()=>{t.setEnabled(e.selection.isEditable())};return e.on("NodeChange",o),o(),()=>{e.off("NodeChange",o)}};e.add("anchor",(e=>{(e=>{(0,e.options.register)("allow_html_in_named_anchor",{processor:"boolean",default:!1})})(e),(e=>{e.on("PreInit",(()=>{e.parser.addNodeFilter("a",m("false")),e.serializer.addNodeFilter("a",m(null))}))})(e),(e=>{e.addCommand("mceAnchor",(()=>{(e=>{const t=(e=>{const t=s(e);return t?i(t):""})(e);e.windowManager.open({title:"Anchor",size:"normal",body:{type:"panel",items:[{name:"id",type:"input",label:"ID",placeholder:"example"}]},buttons:[{type:"cancel",name:"cancel",text:"Cancel"},{type:"submit",name:"save",text:"Save",primary:!0}],initialData:{id:t},onSubmit:t=>{((e,t)=>/^[A-Za-z][A-Za-z0-9\-:._]*$/.test(t)?(d(e,t),!0):(e.windowManager.alert("ID should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores."),!1))(e,t.getData().id)&&t.close()}})})(e)}))})(e),(e=>{const t=()=>e.execCommand("mceAnchor");e.ui.registry.addToggleButton("anchor",{icon:"bookmark",tooltip:"Anchor",onAction:t,onSetup:t=>{const o=e.selection.selectorChangedWithUnbind("a:not([href])",t.setActive).unbind,n=u(e)(t);return()=>{o(),n()}}}),e.ui.registry.addMenuItem("anchor",{icon:"bookmark",text:"Anchor...",onAction:t,onSetup:u(e)})})(e),e.on("PreInit",(()=>{(e=>{e.formatter.register("namedAnchor",{inline:"a",selector:a,remove:"all",split:!0,deep:!0,attributes:{id:"%value"},onmatch:(e,t,o)=>l(e)})})(e)}))}))}(); \ No newline at end of file diff --git a/public/libs/tinymce/plugins/autolink/plugin.min.js b/public/libs/tinymce/plugins/autolink/plugin.min.js index 220664d5ac9..32772191f62 100644 --- a/public/libs/tinymce/plugins/autolink/plugin.min.js +++ b/public/libs/tinymce/plugins/autolink/plugin.min.js @@ -1,4 +1,4 @@ /** - * TinyMCE version 6.7.2 (2023-10-25) + * TinyMCE version 6.8.4 (2024-06-19) */ !function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager");const t=e=>t=>t.options.get(e),n=t("autolink_pattern"),o=t("link_default_target"),r=t("link_default_protocol"),a=t("allow_unsafe_link_target"),s=("string",e=>"string"===(e=>{const t=typeof e;return null===e?"null":"object"===t&&Array.isArray(e)?"array":"object"===t&&(n=o=e,(r=String).prototype.isPrototypeOf(n)||(null===(a=o.constructor)||void 0===a?void 0:a.name)===r.name)?"string":t;var n,o,r,a})(e));const l=(void 0,e=>undefined===e);const i=e=>!(e=>null==e)(e),c=Object.hasOwnProperty,d=e=>"\ufeff"===e;var u=tinymce.util.Tools.resolve("tinymce.dom.TextSeeker");const f=e=>/^[(\[{ \u00a0]$/.test(e),g=(e,t,n)=>{for(let o=t-1;o>=0;o--){const t=e.charAt(o);if(!d(t)&&n(t))return o}return-1},m=(e,t)=>{var o;const a=e.schema.getVoidElements(),s=n(e),{dom:i,selection:d}=e;if(null!==i.getParent(d.getNode(),"a[href]"))return null;const m=d.getRng(),k=u(i,(e=>{return i.isBlock(e)||(t=a,n=e.nodeName.toLowerCase(),c.call(t,n))||"false"===i.getContentEditable(e);var t,n})),{container:p,offset:y}=((e,t)=>{let n=e,o=t;for(;1===n.nodeType&&n.childNodes[o];)n=n.childNodes[o],o=3===n.nodeType?n.data.length:n.childNodes.length;return{container:n,offset:o}})(m.endContainer,m.endOffset),w=null!==(o=i.getParent(p,i.isBlock))&&void 0!==o?o:i.getRoot(),h=k.backwards(p,y+t,((e,t)=>{const n=e.data,o=g(n,t,(r=f,e=>!r(e)));var r,a;return-1===o||(a=n[o],/[?!,.;:]/.test(a))?o:o+1}),w);if(!h)return null;let v=h.container;const _=k.backwards(h.container,h.offset,((e,t)=>{v=e;const n=g(e.data,t,f);return-1===n?n:n+1}),w),A=i.createRng();_?A.setStart(_.container,_.offset):A.setStart(v,0),A.setEnd(h.container,h.offset);const C=A.toString().replace(/\uFEFF/g,"").match(s);if(C){let t=C[0];return $="www.",(b=t).length>=4&&b.substr(0,4)===$?t=r(e)+"://"+t:((e,t,n=0,o)=>{const r=e.indexOf(t,n);return-1!==r&&(!!l(o)||r+t.length<=o)})(t,"@")&&!(e=>/^([A-Za-z][A-Za-z\d.+-]*:\/\/)|mailto:/.test(e))(t)&&(t="mailto:"+t),{rng:A,url:t}}var b,$;return null},k=(e,t)=>{const{dom:n,selection:r}=e,{rng:l,url:i}=t,c=r.getBookmark();r.setRng(l);const d="createlink",u={command:d,ui:!1,value:i};if(!e.dispatch("BeforeExecCommand",u).isDefaultPrevented()){e.getDoc().execCommand(d,!1,i),e.dispatch("ExecCommand",u);const t=o(e);if(s(t)){const o=r.getNode();n.setAttrib(o,"target",t),"_blank"!==t||a(e)||n.setAttrib(o,"rel","noopener")}}r.moveToBookmark(c),e.nodeChanged()},p=e=>{const t=m(e,-1);i(t)&&k(e,t)},y=p;e.add("autolink",(e=>{(e=>{const t=e.options.register;t("autolink_pattern",{processor:"regexp",default:new RegExp("^"+/(?:[A-Za-z][A-Za-z\d.+-]{0,14}:\/\/(?:[-.~*+=!&;:'%@?^${}(),\w]+@)?|www\.|[-;:&=+$,.\w]+@)[A-Za-z\d-]+(?:\.[A-Za-z\d-]+)*(?::\d+)?(?:\/(?:[-.~*+=!;:'%@$(),\/\w]*[-~*+=%@$()\/\w])?)?(?:\?(?:[-.~*+=!&;:'%@?^${}(),\/\w]+))?(?:#(?:[-.~*+=!&;:'%@?^${}(),\/\w]+))?/g.source+"$","i")}),t("link_default_target",{processor:"string"}),t("link_default_protocol",{processor:"string",default:"https"})})(e),(e=>{e.on("keydown",(t=>{13!==t.keyCode||t.isDefaultPrevented()||(e=>{const t=m(e,0);i(t)&&k(e,t)})(e)})),e.on("keyup",(t=>{32===t.keyCode?p(e):(48===t.keyCode&&t.shiftKey||221===t.keyCode)&&y(e)}))})(e)}))}(); \ No newline at end of file diff --git a/public/libs/tinymce/plugins/autoresize/plugin.min.js b/public/libs/tinymce/plugins/autoresize/plugin.min.js index 0432684a406..939bd27cad6 100644 --- a/public/libs/tinymce/plugins/autoresize/plugin.min.js +++ b/public/libs/tinymce/plugins/autoresize/plugin.min.js @@ -1,4 +1,4 @@ /** - * TinyMCE version 6.7.2 (2023-10-25) + * TinyMCE version 6.8.4 (2024-06-19) */ !function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager"),t=tinymce.util.Tools.resolve("tinymce.Env");const o=e=>t=>t.options.get(e),s=o("min_height"),i=o("max_height"),n=o("autoresize_overflow_padding"),r=o("autoresize_bottom_margin"),l=(e,t)=>{const o=e.getBody();o&&(o.style.overflowY=t?"":"hidden",t||(o.scrollTop=0))},g=(e,t,o,s)=>{var i;const n=parseInt(null!==(i=e.getStyle(t,o,s))&&void 0!==i?i:"",10);return isNaN(n)?0:n},a=(e,o,r,c)=>{var d;const f=e.dom,u=e.getDoc();if(!u)return;if((e=>e.plugins.fullscreen&&e.plugins.fullscreen.isFullscreen())(e))return void l(e,!0);const m=u.documentElement,h=c?c():n(e),p=null!==(d=s(e))&&void 0!==d?d:e.getElement().offsetHeight;let y=p;const S=g(f,m,"margin-top",!0),v=g(f,m,"margin-bottom",!0);let C=m.offsetHeight+S+v+h;C<0&&(C=0);const b=e.getContainer().offsetHeight-e.getContentAreaContainer().offsetHeight;C+b>p&&(y=C+b);const w=i(e);if(w&&y>w?(y=w,l(e,!0)):l(e,!1),y!==o.get()){const s=y-o.get();if(f.setStyle(e.getContainer(),"height",y+"px"),o.set(y),(e=>{e.dispatch("ResizeEditor")})(e),t.browser.isSafari()&&(t.os.isMacOS()||t.os.isiOS())){const t=e.getWin();t.scrollTo(t.pageXOffset,t.pageYOffset)}e.hasFocus()&&(e=>{if("setcontent"===(null==e?void 0:e.type.toLowerCase())){const t=e;return!0===t.selection||!0===t.paste}return!1})(r)&&e.selection.scrollIntoView(),(t.browser.isSafari()||t.browser.isChromium())&&s<0&&a(e,o,r,c)}};e.add("autoresize",(e=>{if((e=>{const t=e.options.register;t("autoresize_overflow_padding",{processor:"number",default:1}),t("autoresize_bottom_margin",{processor:"number",default:50})})(e),e.options.isSet("resize")||e.options.set("resize",!1),!e.inline){const o=(e=>{let t=0;return{get:()=>t,set:e=>{t=e}}})();((e,t)=>{e.addCommand("mceAutoResize",(()=>{a(e,t)}))})(e,o),((e,o)=>{let s,i,l=()=>r(e);e.on("init",(i=>{s=0;const r=n(e),g=e.dom;g.setStyles(e.getDoc().documentElement,{height:"auto"}),t.browser.isEdge()||t.browser.isIE()?g.setStyles(e.getBody(),{paddingLeft:r,paddingRight:r,"min-height":0}):g.setStyles(e.getBody(),{paddingLeft:r,paddingRight:r}),a(e,o,i,l),s+=1})),e.on("NodeChange SetContent keyup FullscreenStateChanged ResizeContent",(t=>{if(1===s)i=e.getContainer().offsetHeight,a(e,o,t,l),s+=1;else if(2===s){const t=i0):l,s+=1}else a(e,o,t,l)}))})(e,o)}}))}(); \ No newline at end of file diff --git a/public/libs/tinymce/plugins/autosave/plugin.min.js b/public/libs/tinymce/plugins/autosave/plugin.min.js index 362325ff77f..b8a68e4a62e 100644 --- a/public/libs/tinymce/plugins/autosave/plugin.min.js +++ b/public/libs/tinymce/plugins/autosave/plugin.min.js @@ -1,4 +1,4 @@ /** - * TinyMCE version 6.7.2 (2023-10-25) + * TinyMCE version 6.8.4 (2024-06-19) */ !function(){"use strict";var t=tinymce.util.Tools.resolve("tinymce.PluginManager");const e=("string",t=>"string"===(t=>{const e=typeof t;return null===t?"null":"object"===e&&Array.isArray(t)?"array":"object"===e&&(r=o=t,(a=String).prototype.isPrototypeOf(r)||(null===(s=o.constructor)||void 0===s?void 0:s.name)===a.name)?"string":e;var r,o,a,s})(t));const r=(void 0,t=>undefined===t);var o=tinymce.util.Tools.resolve("tinymce.util.Delay"),a=tinymce.util.Tools.resolve("tinymce.util.LocalStorage"),s=tinymce.util.Tools.resolve("tinymce.util.Tools");const n=t=>{const e=/^(\d+)([ms]?)$/.exec(t);return(e&&e[2]?{s:1e3,m:6e4}[e[2]]:1)*parseInt(t,10)},i=t=>e=>e.options.get(t),u=i("autosave_ask_before_unload"),l=i("autosave_restore_when_empty"),c=i("autosave_interval"),d=i("autosave_retention"),m=t=>{const e=document.location;return t.options.get("autosave_prefix").replace(/{path}/g,e.pathname).replace(/{query}/g,e.search).replace(/{hash}/g,e.hash).replace(/{id}/g,t.id)},v=(t,e)=>{if(r(e))return t.dom.isEmpty(t.getBody());{const r=s.trim(e);if(""===r)return!0;{const e=(new DOMParser).parseFromString(r,"text/html");return t.dom.isEmpty(e)}}},f=t=>{var e;const r=parseInt(null!==(e=a.getItem(m(t)+"time"))&&void 0!==e?e:"0",10)||0;return!((new Date).getTime()-r>d(t)&&(p(t,!1),1))},p=(t,e)=>{const r=m(t);a.removeItem(r+"draft"),a.removeItem(r+"time"),!1!==e&&(t=>{t.dispatch("RemoveDraft")})(t)},g=t=>{const e=m(t);!v(t)&&t.isDirty()&&(a.setItem(e+"draft",t.getContent({format:"raw",no_events:!0})),a.setItem(e+"time",(new Date).getTime().toString()),(t=>{t.dispatch("StoreDraft")})(t))},y=t=>{var e;const r=m(t);f(t)&&(t.setContent(null!==(e=a.getItem(r+"draft"))&&void 0!==e?e:"",{format:"raw"}),(t=>{t.dispatch("RestoreDraft")})(t))};var D=tinymce.util.Tools.resolve("tinymce.EditorManager");const h=t=>e=>{e.setEnabled(f(t));const r=()=>e.setEnabled(f(t));return t.on("StoreDraft RestoreDraft RemoveDraft",r),()=>t.off("StoreDraft RestoreDraft RemoveDraft",r)};t.add("autosave",(t=>((t=>{const r=t.options.register,o=t=>{const r=e(t);return r?{value:n(t),valid:r}:{valid:!1,message:"Must be a string."}};r("autosave_ask_before_unload",{processor:"boolean",default:!0}),r("autosave_prefix",{processor:"string",default:"tinymce-autosave-{path}{query}{hash}-{id}-"}),r("autosave_restore_when_empty",{processor:"boolean",default:!1}),r("autosave_interval",{processor:o,default:"30s"}),r("autosave_retention",{processor:o,default:"20m"})})(t),(t=>{t.editorManager.on("BeforeUnload",(t=>{let e;s.each(D.get(),(t=>{t.plugins.autosave&&t.plugins.autosave.storeDraft(),!e&&t.isDirty()&&u(t)&&(e=t.translate("You have unsaved changes are you sure you want to navigate away?"))})),e&&(t.preventDefault(),t.returnValue=e)}))})(t),(t=>{(t=>{const e=c(t);o.setEditorInterval(t,(()=>{g(t)}),e)})(t);const e=()=>{(t=>{t.undoManager.transact((()=>{y(t),p(t)})),t.focus()})(t)};t.ui.registry.addButton("restoredraft",{tooltip:"Restore last draft",icon:"restore-draft",onAction:e,onSetup:h(t)}),t.ui.registry.addMenuItem("restoredraft",{text:"Restore last draft",icon:"restore-draft",onAction:e,onSetup:h(t)})})(t),t.on("init",(()=>{l(t)&&t.dom.isEmpty(t.getBody())&&y(t)})),(t=>({hasDraft:()=>f(t),storeDraft:()=>g(t),restoreDraft:()=>y(t),removeDraft:e=>p(t,e),isEmpty:e=>v(t,e)}))(t))))}(); \ No newline at end of file diff --git a/public/libs/tinymce/plugins/charmap/plugin.min.js b/public/libs/tinymce/plugins/charmap/plugin.min.js index 9660ebd9e72..6c12ece2b23 100644 --- a/public/libs/tinymce/plugins/charmap/plugin.min.js +++ b/public/libs/tinymce/plugins/charmap/plugin.min.js @@ -1,4 +1,4 @@ /** - * TinyMCE version 6.7.2 (2023-10-25) + * TinyMCE version 6.8.4 (2024-06-19) */ !function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager");const t=(e,t)=>{const r=((e,t)=>e.dispatch("insertCustomChar",{chr:t}))(e,t).chr;e.execCommand("mceInsertContent",!1,r)},r=e=>t=>e===t,a=("array",e=>"array"===(e=>{const t=typeof e;return null===e?"null":"object"===t&&Array.isArray(e)?"array":"object"===t&&(r=a=e,(n=String).prototype.isPrototypeOf(r)||(null===(i=a.constructor)||void 0===i?void 0:i.name)===n.name)?"string":t;var r,a,n,i})(e));const n=r(null),i=r(void 0),o=e=>"function"==typeof e,s=(!1,()=>false);class l{constructor(e,t){this.tag=e,this.value=t}static some(e){return new l(!0,e)}static none(){return l.singletonNone}fold(e,t){return this.tag?t(this.value):e()}isSome(){return this.tag}isNone(){return!this.tag}map(e){return this.tag?l.some(e(this.value)):l.none()}bind(e){return this.tag?e(this.value):l.none()}exists(e){return this.tag&&e(this.value)}forall(e){return!this.tag||e(this.value)}filter(e){return!this.tag||e(this.value)?this:l.none()}getOr(e){return this.tag?this.value:e}or(e){return this.tag?this:e}getOrThunk(e){return this.tag?this.value:e()}orThunk(e){return this.tag?this:e()}getOrDie(e){if(this.tag)return this.value;throw new Error(null!=e?e:"Called getOrDie on None")}static from(e){return null==e?l.none():l.some(e)}getOrNull(){return this.tag?this.value:null}getOrUndefined(){return this.value}each(e){this.tag&&e(this.value)}toArray(){return this.tag?[this.value]:[]}toString(){return this.tag?`some(${this.value})`:"none()"}}l.singletonNone=new l(!1);const c=Array.prototype.push,u=(e,t)=>{const r=e.length,a=new Array(r);for(let n=0;nt=>t.options.get(e),m=h("charmap"),p=h("charmap_append"),d=g.isArray,f="User Defined",y=e=>{return d(e)?(t=e,g.grep(t,(e=>d(e)&&2===e.length))):"function"==typeof e?e():[];var t},b=e=>{const t=((e,t)=>{const r=m(e);r&&(t=[{name:f,characters:y(r)}]);const a=p(e);if(a){const e=g.grep(t,(e=>e.name===f));return e.length?(e[0].characters=[...e[0].characters,...y(a)],t):t.concat({name:f,characters:y(a)})}return t})(e,[{name:"Currency",characters:[[36,"dollar sign"],[162,"cent sign"],[8364,"euro sign"],[163,"pound sign"],[165,"yen sign"],[164,"currency sign"],[8352,"euro-currency sign"],[8353,"colon sign"],[8354,"cruzeiro sign"],[8355,"french franc sign"],[8356,"lira sign"],[8357,"mill sign"],[8358,"naira sign"],[8359,"peseta sign"],[8360,"rupee sign"],[8361,"won sign"],[8362,"new sheqel sign"],[8363,"dong sign"],[8365,"kip sign"],[8366,"tugrik sign"],[8367,"drachma sign"],[8368,"german penny symbol"],[8369,"peso sign"],[8370,"guarani sign"],[8371,"austral sign"],[8372,"hryvnia sign"],[8373,"cedi sign"],[8374,"livre tournois sign"],[8375,"spesmilo sign"],[8376,"tenge sign"],[8377,"indian rupee sign"],[8378,"turkish lira sign"],[8379,"nordic mark sign"],[8380,"manat sign"],[8381,"ruble sign"],[20870,"yen character"],[20803,"yuan character"],[22291,"yuan character, in hong kong and taiwan"],[22278,"yen/yuan character variant one"]]},{name:"Text",characters:[[169,"copyright sign"],[174,"registered sign"],[8482,"trade mark sign"],[8240,"per mille sign"],[181,"micro sign"],[183,"middle dot"],[8226,"bullet"],[8230,"three dot leader"],[8242,"minutes / feet"],[8243,"seconds / inches"],[167,"section sign"],[182,"paragraph sign"],[223,"sharp s / ess-zed"]]},{name:"Quotations",characters:[[8249,"single left-pointing angle quotation mark"],[8250,"single right-pointing angle quotation mark"],[171,"left pointing guillemet"],[187,"right pointing guillemet"],[8216,"left single quotation mark"],[8217,"right single quotation mark"],[8220,"left double quotation mark"],[8221,"right double quotation mark"],[8218,"single low-9 quotation mark"],[8222,"double low-9 quotation mark"],[60,"less-than sign"],[62,"greater-than sign"],[8804,"less-than or equal to"],[8805,"greater-than or equal to"],[8211,"en dash"],[8212,"em dash"],[175,"macron"],[8254,"overline"],[164,"currency sign"],[166,"broken bar"],[168,"diaeresis"],[161,"inverted exclamation mark"],[191,"turned question mark"],[710,"circumflex accent"],[732,"small tilde"],[176,"degree sign"],[8722,"minus sign"],[177,"plus-minus sign"],[247,"division sign"],[8260,"fraction slash"],[215,"multiplication sign"],[185,"superscript one"],[178,"superscript two"],[179,"superscript three"],[188,"fraction one quarter"],[189,"fraction one half"],[190,"fraction three quarters"]]},{name:"Mathematical",characters:[[402,"function / florin"],[8747,"integral"],[8721,"n-ary sumation"],[8734,"infinity"],[8730,"square root"],[8764,"similar to"],[8773,"approximately equal to"],[8776,"almost equal to"],[8800,"not equal to"],[8801,"identical to"],[8712,"element of"],[8713,"not an element of"],[8715,"contains as member"],[8719,"n-ary product"],[8743,"logical and"],[8744,"logical or"],[172,"not sign"],[8745,"intersection"],[8746,"union"],[8706,"partial differential"],[8704,"for all"],[8707,"there exists"],[8709,"diameter"],[8711,"backward difference"],[8727,"asterisk operator"],[8733,"proportional to"],[8736,"angle"]]},{name:"Extended Latin",characters:[[192,"A - grave"],[193,"A - acute"],[194,"A - circumflex"],[195,"A - tilde"],[196,"A - diaeresis"],[197,"A - ring above"],[256,"A - macron"],[198,"ligature AE"],[199,"C - cedilla"],[200,"E - grave"],[201,"E - acute"],[202,"E - circumflex"],[203,"E - diaeresis"],[274,"E - macron"],[204,"I - grave"],[205,"I - acute"],[206,"I - circumflex"],[207,"I - diaeresis"],[298,"I - macron"],[208,"ETH"],[209,"N - tilde"],[210,"O - grave"],[211,"O - acute"],[212,"O - circumflex"],[213,"O - tilde"],[214,"O - diaeresis"],[216,"O - slash"],[332,"O - macron"],[338,"ligature OE"],[352,"S - caron"],[217,"U - grave"],[218,"U - acute"],[219,"U - circumflex"],[220,"U - diaeresis"],[362,"U - macron"],[221,"Y - acute"],[376,"Y - diaeresis"],[562,"Y - macron"],[222,"THORN"],[224,"a - grave"],[225,"a - acute"],[226,"a - circumflex"],[227,"a - tilde"],[228,"a - diaeresis"],[229,"a - ring above"],[257,"a - macron"],[230,"ligature ae"],[231,"c - cedilla"],[232,"e - grave"],[233,"e - acute"],[234,"e - circumflex"],[235,"e - diaeresis"],[275,"e - macron"],[236,"i - grave"],[237,"i - acute"],[238,"i - circumflex"],[239,"i - diaeresis"],[299,"i - macron"],[240,"eth"],[241,"n - tilde"],[242,"o - grave"],[243,"o - acute"],[244,"o - circumflex"],[245,"o - tilde"],[246,"o - diaeresis"],[248,"o slash"],[333,"o macron"],[339,"ligature oe"],[353,"s - caron"],[249,"u - grave"],[250,"u - acute"],[251,"u - circumflex"],[252,"u - diaeresis"],[363,"u - macron"],[253,"y - acute"],[254,"thorn"],[255,"y - diaeresis"],[563,"y - macron"],[913,"Alpha"],[914,"Beta"],[915,"Gamma"],[916,"Delta"],[917,"Epsilon"],[918,"Zeta"],[919,"Eta"],[920,"Theta"],[921,"Iota"],[922,"Kappa"],[923,"Lambda"],[924,"Mu"],[925,"Nu"],[926,"Xi"],[927,"Omicron"],[928,"Pi"],[929,"Rho"],[931,"Sigma"],[932,"Tau"],[933,"Upsilon"],[934,"Phi"],[935,"Chi"],[936,"Psi"],[937,"Omega"],[945,"alpha"],[946,"beta"],[947,"gamma"],[948,"delta"],[949,"epsilon"],[950,"zeta"],[951,"eta"],[952,"theta"],[953,"iota"],[954,"kappa"],[955,"lambda"],[956,"mu"],[957,"nu"],[958,"xi"],[959,"omicron"],[960,"pi"],[961,"rho"],[962,"final sigma"],[963,"sigma"],[964,"tau"],[965,"upsilon"],[966,"phi"],[967,"chi"],[968,"psi"],[969,"omega"]]},{name:"Symbols",characters:[[8501,"alef symbol"],[982,"pi symbol"],[8476,"real part symbol"],[978,"upsilon - hook symbol"],[8472,"Weierstrass p"],[8465,"imaginary part"]]},{name:"Arrows",characters:[[8592,"leftwards arrow"],[8593,"upwards arrow"],[8594,"rightwards arrow"],[8595,"downwards arrow"],[8596,"left right arrow"],[8629,"carriage return"],[8656,"leftwards double arrow"],[8657,"upwards double arrow"],[8658,"rightwards double arrow"],[8659,"downwards double arrow"],[8660,"left right double arrow"],[8756,"therefore"],[8834,"subset of"],[8835,"superset of"],[8836,"not a subset of"],[8838,"subset of or equal to"],[8839,"superset of or equal to"],[8853,"circled plus"],[8855,"circled times"],[8869,"perpendicular"],[8901,"dot operator"],[8968,"left ceiling"],[8969,"right ceiling"],[8970,"left floor"],[8971,"right floor"],[9001,"left-pointing angle bracket"],[9002,"right-pointing angle bracket"],[9674,"lozenge"],[9824,"black spade suit"],[9827,"black club suit"],[9829,"black heart suit"],[9830,"black diamond suit"],[8194,"en space"],[8195,"em space"],[8201,"thin space"],[8204,"zero width non-joiner"],[8205,"zero width joiner"],[8206,"left-to-right mark"],[8207,"right-to-left mark"]]}]);return t.length>1?[{name:"All",characters:(r=t,n=e=>e.characters,(e=>{const t=[];for(let r=0,n=e.length;r{let t=e;return{get:()=>t,set:e=>{t=e}}},v=(e,t,r=0,a)=>{const n=e.indexOf(t,r);return-1!==n&&(!!i(a)||n+t.length<=a)},k=String.fromCodePoint,C=(e,t)=>{const r=[],a=t.toLowerCase();return((e,t)=>{for(let t=0,i=e.length;t!!v(k(e).toLowerCase(),r)||v(t.toLowerCase(),r)||v(t.toLowerCase().replace(/\s+/g,""),r))((n=e[t])[0],n[1],a)&&r.push(n);var n})(e.characters),u(r,(e=>({text:e[1],value:k(e[0]),icon:k(e[0])})))},x="pattern",A=(e,r)=>{const a=()=>[{label:"Search",type:"input",name:x},{type:"collection",name:"results"}],i=1===r.length?w(f):w("All"),o=((e,t)=>{let r=null;const a=()=>{n(r)||(clearTimeout(r),r=null)};return{cancel:a,throttle:(...t)=>{a(),r=setTimeout((()=>{r=null,e.apply(null,t)}),40)}}})((e=>{const t=e.getData().pattern;((e,t)=>{var a,n;(a=r,n=e=>e.name===i.get(),((e,t,r)=>{for(let a=0,n=e.length;a{const a=C(r,t);e.setData({results:a})}))})(e,t)})),c={title:"Special Character",size:"normal",body:1===r.length?{type:"panel",items:a()}:{type:"tabpanel",tabs:u(r,(e=>({title:e.name,name:e.name,items:a()})))},buttons:[{type:"cancel",name:"close",text:"Close",primary:!0}],initialData:{pattern:"",results:C(r[0],"")},onAction:(r,a)=>{"results"===a.name&&(t(e,a.value),r.close())},onTabChange:(e,t)=>{i.set(t.newTabName),o.throttle(e)},onChange:(e,t)=>{t.name===x&&o.throttle(e)}};e.windowManager.open(c).focus(x)},q=e=>t=>{const r=()=>{t.setEnabled(e.selection.isEditable())};return e.on("NodeChange",r),r(),()=>{e.off("NodeChange",r)}};e.add("charmap",(e=>{(e=>{const t=e.options.register,r=e=>o(e)||a(e);t("charmap",{processor:r}),t("charmap_append",{processor:r})})(e);const r=b(e);return((e,t)=>{e.addCommand("mceShowCharmap",(()=>{A(e,t)}))})(e,r),(e=>{const t=()=>e.execCommand("mceShowCharmap");e.ui.registry.addButton("charmap",{icon:"insert-character",tooltip:"Special character",onAction:t,onSetup:q(e)}),e.ui.registry.addMenuItem("charmap",{icon:"insert-character",text:"Special character...",onAction:t,onSetup:q(e)})})(e),((e,t)=>{e.ui.registry.addAutocompleter("charmap",{trigger:":",columns:"auto",minChars:2,fetch:(e,r)=>new Promise(((r,a)=>{r(C(t,e))})),onAction:(t,r,a)=>{e.selection.setRng(r),e.insertContent(a),t.hide()}})})(e,r[0]),(e=>({getCharMap:()=>b(e),insertChar:r=>{t(e,r)}}))(e)}))}(); \ No newline at end of file diff --git a/public/libs/tinymce/plugins/code/plugin.min.js b/public/libs/tinymce/plugins/code/plugin.min.js index 644eee54c31..1928a12f774 100644 --- a/public/libs/tinymce/plugins/code/plugin.min.js +++ b/public/libs/tinymce/plugins/code/plugin.min.js @@ -1,4 +1,4 @@ /** - * TinyMCE version 6.7.2 (2023-10-25) + * TinyMCE version 6.8.4 (2024-06-19) */ !function(){"use strict";tinymce.util.Tools.resolve("tinymce.PluginManager").add("code",(e=>((e=>{e.addCommand("mceCodeEditor",(()=>{(e=>{const o=(e=>e.getContent({source_view:!0}))(e);e.windowManager.open({title:"Source Code",size:"large",body:{type:"panel",items:[{type:"textarea",name:"code"}]},buttons:[{type:"cancel",name:"cancel",text:"Cancel"},{type:"submit",name:"save",text:"Save",primary:!0}],initialData:{code:o},onSubmit:o=>{((e,o)=>{e.focus(),e.undoManager.transact((()=>{e.setContent(o)})),e.selection.setCursorLocation(),e.nodeChanged()})(e,o.getData().code),o.close()}})})(e)}))})(e),(e=>{const o=()=>e.execCommand("mceCodeEditor");e.ui.registry.addButton("code",{icon:"sourcecode",tooltip:"Source code",onAction:o}),e.ui.registry.addMenuItem("code",{icon:"sourcecode",text:"Source code",onAction:o})})(e),{})))}(); \ No newline at end of file diff --git a/public/libs/tinymce/plugins/codesample/plugin.min.js b/public/libs/tinymce/plugins/codesample/plugin.min.js index 71d3e4f26f7..504ea9e5213 100644 --- a/public/libs/tinymce/plugins/codesample/plugin.min.js +++ b/public/libs/tinymce/plugins/codesample/plugin.min.js @@ -1,4 +1,4 @@ /** - * TinyMCE version 6.7.2 (2023-10-25) + * TinyMCE version 6.8.4 (2024-06-19) */ !function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager");const t=e=>!(e=>null==e)(e),n=()=>{};class a{constructor(e,t){this.tag=e,this.value=t}static some(e){return new a(!0,e)}static none(){return a.singletonNone}fold(e,t){return this.tag?t(this.value):e()}isSome(){return this.tag}isNone(){return!this.tag}map(e){return this.tag?a.some(e(this.value)):a.none()}bind(e){return this.tag?e(this.value):a.none()}exists(e){return this.tag&&e(this.value)}forall(e){return!this.tag||e(this.value)}filter(e){return!this.tag||e(this.value)?this:a.none()}getOr(e){return this.tag?this.value:e}or(e){return this.tag?this:e}getOrThunk(e){return this.tag?this.value:e()}orThunk(e){return this.tag?this:e()}getOrDie(e){if(this.tag)return this.value;throw new Error(null!=e?e:"Called getOrDie on None")}static from(e){return t(e)?a.some(e):a.none()}getOrNull(){return this.tag?this.value:null}getOrUndefined(){return this.value}each(e){this.tag&&e(this.value)}toArray(){return this.tag?[this.value]:[]}toString(){return this.tag?`some(${this.value})`:"none()"}}a.singletonNone=new a(!1);var s=tinymce.util.Tools.resolve("tinymce.dom.DOMUtils");const r="undefined"!=typeof window?window:Function("return this;")(),i=function(e,t,n){const a=window.Prism;window.Prism={manual:!0};var s=function(e){var t=/(?:^|\s)lang(?:uage)?-([\w-]+)(?=\s|$)/i,n=0,a={},s={manual:e.Prism&&e.Prism.manual,disableWorkerMessageHandler:e.Prism&&e.Prism.disableWorkerMessageHandler,util:{encode:function e(t){return t instanceof r?new r(t.type,e(t.content),t.alias):Array.isArray(t)?t.map(e):t.replace(/&/g,"&").replace(/=d.reach);x+=_.value.length,_=_.next){var F=_.value;if(t.length>e.length)return;if(!(F instanceof r)){var A,S=1;if(y){if(!(A=i(v,x,e,m))||A.index>=e.length)break;var $=A.index,z=A.index+A[0].length,E=x;for(E+=_.value.length;$>=E;)E+=(_=_.next).value.length;if(x=E-=_.value.length,_.value instanceof r)continue;for(var C=_;C!==t.tail&&(Ed.reach&&(d.reach=O);var P=_.prev;if(B&&(P=u(t,P,B),x+=B.length),c(t,P,S),_=u(t,P,new r(g,f?s.tokenize(j,f):j,w,j)),T&&u(t,_,T),S>1){var N={cause:g+","+b,reach:O};o(e,t,n,_.prev,x,N),d&&N.reach>d.reach&&(d.reach=N.reach)}}}}}}function l(){var e={value:null,prev:null,next:null},t={value:null,prev:e,next:null};e.next=t,this.head=e,this.tail=t,this.length=0}function u(e,t,n){var a=t.next,s={value:n,prev:t,next:a};return t.next=s,a.prev=s,e.length++,s}function c(e,t,n){for(var a=t.next,s=0;s"+r.content+""},!e.document)return e.addEventListener?(s.disableWorkerMessageHandler||e.addEventListener("message",(function(t){var n=JSON.parse(t.data),a=n.language,r=n.code,i=n.immediateClose;e.postMessage(s.highlight(r,s.languages[a],a)),i&&e.close()}),!1),s):s;var d=s.util.currentScript();function g(){s.manual||s.highlightAll()}if(d&&(s.filename=d.src,d.hasAttribute("data-manual")&&(s.manual=!0)),!s.manual){var p=document.readyState;"loading"===p||"interactive"===p&&d&&d.defer?document.addEventListener("DOMContentLoaded",g):window.requestAnimationFrame?window.requestAnimationFrame(g):window.setTimeout(g,16)}return s}("undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{});return s.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0,greedy:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/(\b(?:class|extends|implements|instanceof|interface|new|trait)\s+|\bcatch\s+\()[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:break|catch|continue|do|else|finally|for|function|if|in|instanceof|new|null|return|throw|try|while)\b/,boolean:/\b(?:false|true)\b/,function:/\b\w+(?=\()/,number:/\b0x[\da-f]+\b|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/[<>]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,punctuation:/[{}[\];(),.:]/},function(e){function t(e,t){return"___"+e.toUpperCase()+t+"___"}Object.defineProperties(e.languages["markup-templating"]={},{buildPlaceholders:{value:function(n,a,s,r){if(n.language===a){var i=n.tokenStack=[];n.code=n.code.replace(s,(function(e){if("function"==typeof r&&!r(e))return e;for(var s,o=i.length;-1!==n.code.indexOf(s=t(a,o));)++o;return i[o]=e,s})),n.grammar=e.languages.markup}}},tokenizePlaceholders:{value:function(n,a){if(n.language===a&&n.tokenStack){n.grammar=e.languages[a];var s=0,r=Object.keys(n.tokenStack);!function i(o){for(var l=0;l=r.length);l++){var u=o[l];if("string"==typeof u||u.content&&"string"==typeof u.content){var c=r[s],d=n.tokenStack[c],g="string"==typeof u?u:u.content,p=t(a,c),b=g.indexOf(p);if(b>-1){++s;var h=g.substring(0,b),f=new e.Token(a,e.tokenize(d,n.grammar),"language-"+a,d),m=g.substring(b+p.length),y=[];h&&y.push.apply(y,i([h])),y.push(f),m&&y.push.apply(y,i([m])),"string"==typeof u?o.splice.apply(o,[l,1].concat(y)):u.content=y}}else u.content&&i(u.content)}return o}(n.tokens)}}}})}(s),s.languages.c=s.languages.extend("clike",{comment:{pattern:/\/\/(?:[^\r\n\\]|\\(?:\r\n?|\n|(?![\r\n])))*|\/\*[\s\S]*?(?:\*\/|$)/,greedy:!0},string:{pattern:/"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"/,greedy:!0},"class-name":{pattern:/(\b(?:enum|struct)\s+(?:__attribute__\s*\(\([\s\S]*?\)\)\s*)?)\w+|\b[a-z]\w*_t\b/,lookbehind:!0},keyword:/\b(?:_Alignas|_Alignof|_Atomic|_Bool|_Complex|_Generic|_Imaginary|_Noreturn|_Static_assert|_Thread_local|__attribute__|asm|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|inline|int|long|register|return|short|signed|sizeof|static|struct|switch|typedef|typeof|union|unsigned|void|volatile|while)\b/,function:/\b[a-z_]\w*(?=\s*\()/i,number:/(?:\b0x(?:[\da-f]+(?:\.[\da-f]*)?|\.[\da-f]+)(?:p[+-]?\d+)?|(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[+-]?\d+)?)[ful]{0,4}/i,operator:/>>=?|<<=?|->|([-+&|:])\1|[?:~]|[-+*/%&|^!=<>]=?/}),s.languages.insertBefore("c","string",{char:{pattern:/'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n]){0,32}'/,greedy:!0}}),s.languages.insertBefore("c","string",{macro:{pattern:/(^[\t ]*)#\s*[a-z](?:[^\r\n\\/]|\/(?!\*)|\/\*(?:[^*]|\*(?!\/))*\*\/|\\(?:\r\n|[\s\S]))*/im,lookbehind:!0,greedy:!0,alias:"property",inside:{string:[{pattern:/^(#\s*include\s*)<[^>]+>/,lookbehind:!0},s.languages.c.string],char:s.languages.c.char,comment:s.languages.c.comment,"macro-name":[{pattern:/(^#\s*define\s+)\w+\b(?!\()/i,lookbehind:!0},{pattern:/(^#\s*define\s+)\w+\b(?=\()/i,lookbehind:!0,alias:"function"}],directive:{pattern:/^(#\s*)[a-z]+/,lookbehind:!0,alias:"keyword"},"directive-hash":/^#/,punctuation:/##|\\(?=[\r\n])/,expression:{pattern:/\S[\s\S]*/,inside:s.languages.c}}}}),s.languages.insertBefore("c","function",{constant:/\b(?:EOF|NULL|SEEK_CUR|SEEK_END|SEEK_SET|__DATE__|__FILE__|__LINE__|__TIMESTAMP__|__TIME__|__func__|stderr|stdin|stdout)\b/}),delete s.languages.c.boolean,function(e){var t=/\b(?:alignas|alignof|asm|auto|bool|break|case|catch|char|char16_t|char32_t|char8_t|class|co_await|co_return|co_yield|compl|concept|const|const_cast|consteval|constexpr|constinit|continue|decltype|default|delete|do|double|dynamic_cast|else|enum|explicit|export|extern|final|float|for|friend|goto|if|import|inline|int|int16_t|int32_t|int64_t|int8_t|long|module|mutable|namespace|new|noexcept|nullptr|operator|override|private|protected|public|register|reinterpret_cast|requires|return|short|signed|sizeof|static|static_assert|static_cast|struct|switch|template|this|thread_local|throw|try|typedef|typeid|typename|uint16_t|uint32_t|uint64_t|uint8_t|union|unsigned|using|virtual|void|volatile|wchar_t|while)\b/,n=/\b(?!)\w+(?:\s*\.\s*\w+)*\b/.source.replace(//g,(function(){return t.source}));e.languages.cpp=e.languages.extend("c",{"class-name":[{pattern:RegExp(/(\b(?:class|concept|enum|struct|typename)\s+)(?!)\w+/.source.replace(//g,(function(){return t.source}))),lookbehind:!0},/\b[A-Z]\w*(?=\s*::\s*\w+\s*\()/,/\b[A-Z_]\w*(?=\s*::\s*~\w+\s*\()/i,/\b\w+(?=\s*<(?:[^<>]|<(?:[^<>]|<[^<>]*>)*>)*>\s*::\s*\w+\s*\()/],keyword:t,number:{pattern:/(?:\b0b[01']+|\b0x(?:[\da-f']+(?:\.[\da-f']*)?|\.[\da-f']+)(?:p[+-]?[\d']+)?|(?:\b[\d']+(?:\.[\d']*)?|\B\.[\d']+)(?:e[+-]?[\d']+)?)[ful]{0,4}/i,greedy:!0},operator:/>>=?|<<=?|->|--|\+\+|&&|\|\||[?:~]|<=>|[-+*/%&|^!=<>]=?|\b(?:and|and_eq|bitand|bitor|not|not_eq|or|or_eq|xor|xor_eq)\b/,boolean:/\b(?:false|true)\b/}),e.languages.insertBefore("cpp","string",{module:{pattern:RegExp(/(\b(?:import|module)\s+)/.source+"(?:"+/"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|<[^<>\r\n]*>/.source+"|"+/(?:\s*:\s*)?|:\s*/.source.replace(//g,(function(){return n}))+")"),lookbehind:!0,greedy:!0,inside:{string:/^[<"][\s\S]+/,operator:/:/,punctuation:/\./}},"raw-string":{pattern:/R"([^()\\ ]{0,16})\([\s\S]*?\)\1"/,alias:"string",greedy:!0}}),e.languages.insertBefore("cpp","keyword",{"generic-function":{pattern:/\b(?!operator\b)[a-z_]\w*\s*<(?:[^<>]|<[^<>]*>)*>(?=\s*\()/i,inside:{function:/^\w+/,generic:{pattern:/<[\s\S]+/,alias:"class-name",inside:e.languages.cpp}}}}),e.languages.insertBefore("cpp","operator",{"double-colon":{pattern:/::/,alias:"punctuation"}}),e.languages.insertBefore("cpp","class-name",{"base-clause":{pattern:/(\b(?:class|struct)\s+\w+\s*:\s*)[^;{}"'\s]+(?:\s+[^;{}"'\s]+)*(?=\s*[;{])/,lookbehind:!0,greedy:!0,inside:e.languages.extend("cpp",{})}}),e.languages.insertBefore("inside","double-colon",{"class-name":/\b[a-z_]\w*\b(?!\s*::)/i},e.languages.cpp["base-clause"])}(s),function(e){function t(e,t){return e.replace(/<<(\d+)>>/g,(function(e,n){return"(?:"+t[+n]+")"}))}function n(e,n,a){return RegExp(t(e,n),a||"")}function a(e,t){for(var n=0;n>/g,(function(){return"(?:"+e+")"}));return e.replace(/<>/g,"[^\\s\\S]")}var s="bool byte char decimal double dynamic float int long object sbyte short string uint ulong ushort var void",r="class enum interface record struct",i="add alias and ascending async await by descending from(?=\\s*(?:\\w|$)) get global group into init(?=\\s*;) join let nameof not notnull on or orderby partial remove select set unmanaged value when where with(?=\\s*{)",o="abstract as base break case catch checked const continue default delegate do else event explicit extern finally fixed for foreach goto if implicit in internal is lock namespace new null operator out override params private protected public readonly ref return sealed sizeof stackalloc static switch this throw try typeof unchecked unsafe using virtual volatile while yield";function l(e){return"\\b(?:"+e.trim().replace(/ /g,"|")+")\\b"}var u=l(r),c=RegExp(l(s+" "+r+" "+i+" "+o)),d=l(r+" "+i+" "+o),g=l(s+" "+r+" "+o),p=a(/<(?:[^<>;=+\-*/%&|^]|<>)*>/.source,2),b=a(/\((?:[^()]|<>)*\)/.source,2),h=/@?\b[A-Za-z_]\w*\b/.source,f=t(/<<0>>(?:\s*<<1>>)?/.source,[h,p]),m=t(/(?!<<0>>)<<1>>(?:\s*\.\s*<<1>>)*/.source,[d,f]),y=/\[\s*(?:,\s*)*\]/.source,w=t(/<<0>>(?:\s*(?:\?\s*)?<<1>>)*(?:\s*\?)?/.source,[m,y]),k=t(/[^,()<>[\];=+\-*/%&|^]|<<0>>|<<1>>|<<2>>/.source,[p,b,y]),v=t(/\(<<0>>+(?:,<<0>>+)+\)/.source,[k]),_=t(/(?:<<0>>|<<1>>)(?:\s*(?:\?\s*)?<<2>>)*(?:\s*\?)?/.source,[v,m,y]),x={keyword:c,punctuation:/[<>()?,.:[\]]/},F=/'(?:[^\r\n'\\]|\\.|\\[Uux][\da-fA-F]{1,8})'/.source,A=/"(?:\\.|[^\\"\r\n])*"/.source,S=/@"(?:""|\\[\s\S]|[^\\"])*"(?!")/.source;e.languages.csharp=e.languages.extend("clike",{string:[{pattern:n(/(^|[^$\\])<<0>>/.source,[S]),lookbehind:!0,greedy:!0},{pattern:n(/(^|[^@$\\])<<0>>/.source,[A]),lookbehind:!0,greedy:!0}],"class-name":[{pattern:n(/(\busing\s+static\s+)<<0>>(?=\s*;)/.source,[m]),lookbehind:!0,inside:x},{pattern:n(/(\busing\s+<<0>>\s*=\s*)<<1>>(?=\s*;)/.source,[h,_]),lookbehind:!0,inside:x},{pattern:n(/(\busing\s+)<<0>>(?=\s*=)/.source,[h]),lookbehind:!0},{pattern:n(/(\b<<0>>\s+)<<1>>/.source,[u,f]),lookbehind:!0,inside:x},{pattern:n(/(\bcatch\s*\(\s*)<<0>>/.source,[m]),lookbehind:!0,inside:x},{pattern:n(/(\bwhere\s+)<<0>>/.source,[h]),lookbehind:!0},{pattern:n(/(\b(?:is(?:\s+not)?|as)\s+)<<0>>/.source,[w]),lookbehind:!0,inside:x},{pattern:n(/\b<<0>>(?=\s+(?!<<1>>|with\s*\{)<<2>>(?:\s*[=,;:{)\]]|\s+(?:in|when)\b))/.source,[_,g,h]),inside:x}],keyword:c,number:/(?:\b0(?:x[\da-f_]*[\da-f]|b[01_]*[01])|(?:\B\.\d+(?:_+\d+)*|\b\d+(?:_+\d+)*(?:\.\d+(?:_+\d+)*)?)(?:e[-+]?\d+(?:_+\d+)*)?)(?:[dflmu]|lu|ul)?\b/i,operator:/>>=?|<<=?|[-=]>|([-+&|])\1|~|\?\?=?|[-+*/%&|^!=<>]=?/,punctuation:/\?\.?|::|[{}[\];(),.:]/}),e.languages.insertBefore("csharp","number",{range:{pattern:/\.\./,alias:"operator"}}),e.languages.insertBefore("csharp","punctuation",{"named-parameter":{pattern:n(/([(,]\s*)<<0>>(?=\s*:)/.source,[h]),lookbehind:!0,alias:"punctuation"}}),e.languages.insertBefore("csharp","class-name",{namespace:{pattern:n(/(\b(?:namespace|using)\s+)<<0>>(?:\s*\.\s*<<0>>)*(?=\s*[;{])/.source,[h]),lookbehind:!0,inside:{punctuation:/\./}},"type-expression":{pattern:n(/(\b(?:default|sizeof|typeof)\s*\(\s*(?!\s))(?:[^()\s]|\s(?!\s)|<<0>>)*(?=\s*\))/.source,[b]),lookbehind:!0,alias:"class-name",inside:x},"return-type":{pattern:n(/<<0>>(?=\s+(?:<<1>>\s*(?:=>|[({]|\.\s*this\s*\[)|this\s*\[))/.source,[_,m]),inside:x,alias:"class-name"},"constructor-invocation":{pattern:n(/(\bnew\s+)<<0>>(?=\s*[[({])/.source,[_]),lookbehind:!0,inside:x,alias:"class-name"},"generic-method":{pattern:n(/<<0>>\s*<<1>>(?=\s*\()/.source,[h,p]),inside:{function:n(/^<<0>>/.source,[h]),generic:{pattern:RegExp(p),alias:"class-name",inside:x}}},"type-list":{pattern:n(/\b((?:<<0>>\s+<<1>>|record\s+<<1>>\s*<<5>>|where\s+<<2>>)\s*:\s*)(?:<<3>>|<<4>>|<<1>>\s*<<5>>|<<6>>)(?:\s*,\s*(?:<<3>>|<<4>>|<<6>>))*(?=\s*(?:where|[{;]|=>|$))/.source,[u,f,h,_,c.source,b,/\bnew\s*\(\s*\)/.source]),lookbehind:!0,inside:{"record-arguments":{pattern:n(/(^(?!new\s*\()<<0>>\s*)<<1>>/.source,[f,b]),lookbehind:!0,greedy:!0,inside:e.languages.csharp},keyword:c,"class-name":{pattern:RegExp(_),greedy:!0,inside:x},punctuation:/[,()]/}},preprocessor:{pattern:/(^[\t ]*)#.*/m,lookbehind:!0,alias:"property",inside:{directive:{pattern:/(#)\b(?:define|elif|else|endif|endregion|error|if|line|nullable|pragma|region|undef|warning)\b/,lookbehind:!0,alias:"keyword"}}}});var $=A+"|"+F,z=t(/\/(?![*/])|\/\/[^\r\n]*[\r\n]|\/\*(?:[^*]|\*(?!\/))*\*\/|<<0>>/.source,[$]),E=a(t(/[^"'/()]|<<0>>|\(<>*\)/.source,[z]),2),C=/\b(?:assembly|event|field|method|module|param|property|return|type)\b/.source,j=t(/<<0>>(?:\s*\(<<1>>*\))?/.source,[m,E]);e.languages.insertBefore("csharp","class-name",{attribute:{pattern:n(/((?:^|[^\s\w>)?])\s*\[\s*)(?:<<0>>\s*:\s*)?<<1>>(?:\s*,\s*<<1>>)*(?=\s*\])/.source,[C,j]),lookbehind:!0,greedy:!0,inside:{target:{pattern:n(/^<<0>>(?=\s*:)/.source,[C]),alias:"keyword"},"attribute-arguments":{pattern:n(/\(<<0>>*\)/.source,[E]),inside:e.languages.csharp},"class-name":{pattern:RegExp(m),inside:{punctuation:/\./}},punctuation:/[:,]/}}});var B=/:[^}\r\n]+/.source,T=a(t(/[^"'/()]|<<0>>|\(<>*\)/.source,[z]),2),O=t(/\{(?!\{)(?:(?![}:])<<0>>)*<<1>>?\}/.source,[T,B]),P=a(t(/[^"'/()]|\/(?!\*)|\/\*(?:[^*]|\*(?!\/))*\*\/|<<0>>|\(<>*\)/.source,[$]),2),N=t(/\{(?!\{)(?:(?![}:])<<0>>)*<<1>>?\}/.source,[P,B]);function R(t,a){return{interpolation:{pattern:n(/((?:^|[^{])(?:\{\{)*)<<0>>/.source,[t]),lookbehind:!0,inside:{"format-string":{pattern:n(/(^\{(?:(?![}:])<<0>>)*)<<1>>(?=\}$)/.source,[a,B]),lookbehind:!0,inside:{punctuation:/^:/}},punctuation:/^\{|\}$/,expression:{pattern:/[\s\S]+/,alias:"language-csharp",inside:e.languages.csharp}}},string:/[\s\S]+/}}e.languages.insertBefore("csharp","string",{"interpolation-string":[{pattern:n(/(^|[^\\])(?:\$@|@\$)"(?:""|\\[\s\S]|\{\{|<<0>>|[^\\{"])*"/.source,[O]),lookbehind:!0,greedy:!0,inside:R(O,T)},{pattern:n(/(^|[^@\\])\$"(?:\\.|\{\{|<<0>>|[^\\"{])*"/.source,[N]),lookbehind:!0,greedy:!0,inside:R(N,P)}],char:{pattern:RegExp(F),greedy:!0}}),e.languages.dotnet=e.languages.cs=e.languages.csharp}(s),function(e){var t=/(?:"(?:\\(?:\r\n|[\s\S])|[^"\\\r\n])*"|'(?:\\(?:\r\n|[\s\S])|[^'\\\r\n])*')/;e.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:RegExp("@[\\w-](?:"+/[^;{\s"']|\s+(?!\s)/.source+"|"+t.source+")*?"+/(?:;|(?=\s*\{))/.source),inside:{rule:/^@[\w-]+/,"selector-function-argument":{pattern:/(\bselector\s*\(\s*(?![\s)]))(?:[^()\s]|\s+(?![\s)])|\((?:[^()]|\([^()]*\))*\))+(?=\s*\))/,lookbehind:!0,alias:"selector"},keyword:{pattern:/(^|[^\w-])(?:and|not|only|or)(?![\w-])/,lookbehind:!0}}},url:{pattern:RegExp("\\burl\\((?:"+t.source+"|"+/(?:[^\\\r\n()"']|\\[\s\S])*/.source+")\\)","i"),greedy:!0,inside:{function:/^url/i,punctuation:/^\(|\)$/,string:{pattern:RegExp("^"+t.source+"$"),alias:"url"}}},selector:{pattern:RegExp("(^|[{}\\s])[^{}\\s](?:[^{};\"'\\s]|\\s+(?![\\s{])|"+t.source+")*(?=\\s*\\{)"),lookbehind:!0},string:{pattern:t,greedy:!0},property:{pattern:/(^|[^-\w\xA0-\uFFFF])(?!\s)[-_a-z\xA0-\uFFFF](?:(?!\s)[-\w\xA0-\uFFFF])*(?=\s*:)/i,lookbehind:!0},important:/!important\b/i,function:{pattern:/(^|[^-a-z0-9])[-a-z0-9]+(?=\()/i,lookbehind:!0},punctuation:/[(){};:,]/},e.languages.css.atrule.inside.rest=e.languages.css;var n=e.languages.markup;n&&(n.tag.addInlined("style","css"),n.tag.addAttribute("style","css"))}(s),function(e){var t=/\b(?:abstract|assert|boolean|break|byte|case|catch|char|class|const|continue|default|do|double|else|enum|exports|extends|final|finally|float|for|goto|if|implements|import|instanceof|int|interface|long|module|native|new|non-sealed|null|open|opens|package|permits|private|protected|provides|public|record(?!\s*[(){}[\]<>=%~.:,;?+\-*/&|^])|requires|return|sealed|short|static|strictfp|super|switch|synchronized|this|throw|throws|to|transient|transitive|try|uses|var|void|volatile|while|with|yield)\b/,n=/(?:[a-z]\w*\s*\.\s*)*(?:[A-Z]\w*\s*\.\s*)*/.source,a={pattern:RegExp(/(^|[^\w.])/.source+n+/[A-Z](?:[\d_A-Z]*[a-z]\w*)?\b/.source),lookbehind:!0,inside:{namespace:{pattern:/^[a-z]\w*(?:\s*\.\s*[a-z]\w*)*(?:\s*\.)?/,inside:{punctuation:/\./}},punctuation:/\./}};e.languages.java=e.languages.extend("clike",{string:{pattern:/(^|[^\\])"(?:\\.|[^"\\\r\n])*"/,lookbehind:!0,greedy:!0},"class-name":[a,{pattern:RegExp(/(^|[^\w.])/.source+n+/[A-Z]\w*(?=\s+\w+\s*[;,=()]|\s*(?:\[[\s,]*\]\s*)?::\s*new\b)/.source),lookbehind:!0,inside:a.inside},{pattern:RegExp(/(\b(?:class|enum|extends|implements|instanceof|interface|new|record|throws)\s+)/.source+n+/[A-Z]\w*\b/.source),lookbehind:!0,inside:a.inside}],keyword:t,function:[e.languages.clike.function,{pattern:/(::\s*)[a-z_]\w*/,lookbehind:!0}],number:/\b0b[01][01_]*L?\b|\b0x(?:\.[\da-f_p+-]+|[\da-f_]+(?:\.[\da-f_p+-]+)?)\b|(?:\b\d[\d_]*(?:\.[\d_]*)?|\B\.\d[\d_]*)(?:e[+-]?\d[\d_]*)?[dfl]?/i,operator:{pattern:/(^|[^.])(?:<<=?|>>>?=?|->|--|\+\+|&&|\|\||::|[?:~]|[-+*/%&|^!=<>]=?)/m,lookbehind:!0},constant:/\b[A-Z][A-Z_\d]+\b/}),e.languages.insertBefore("java","string",{"triple-quoted-string":{pattern:/"""[ \t]*[\r\n](?:(?:"|"")?(?:\\.|[^"\\]))*"""/,greedy:!0,alias:"string"},char:{pattern:/'(?:\\.|[^'\\\r\n]){1,6}'/,greedy:!0}}),e.languages.insertBefore("java","class-name",{annotation:{pattern:/(^|[^.])@\w+(?:\s*\.\s*\w+)*/,lookbehind:!0,alias:"punctuation"},generics:{pattern:/<(?:[\w\s,.?]|&(?!&)|<(?:[\w\s,.?]|&(?!&)|<(?:[\w\s,.?]|&(?!&)|<(?:[\w\s,.?]|&(?!&))*>)*>)*>)*>/,inside:{"class-name":a,keyword:t,punctuation:/[<>(),.:]/,operator:/[?&|]/}},import:[{pattern:RegExp(/(\bimport\s+)/.source+n+/(?:[A-Z]\w*|\*)(?=\s*;)/.source),lookbehind:!0,inside:{namespace:a.inside.namespace,punctuation:/\./,operator:/\*/,"class-name":/\w+/}},{pattern:RegExp(/(\bimport\s+static\s+)/.source+n+/(?:\w+|\*)(?=\s*;)/.source),lookbehind:!0,alias:"static",inside:{namespace:a.inside.namespace,static:/\b\w+$/,punctuation:/\./,operator:/\*/,"class-name":/\w+/}}],namespace:{pattern:RegExp(/(\b(?:exports|import(?:\s+static)?|module|open|opens|package|provides|requires|to|transitive|uses|with)\s+)(?!)[a-z]\w*(?:\.[a-z]\w*)*\.?/.source.replace(//g,(function(){return t.source}))),lookbehind:!0,inside:{punctuation:/\./}}})}(s),s.languages.javascript=s.languages.extend("clike",{"class-name":[s.languages.clike["class-name"],{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$A-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\.(?:constructor|prototype))/,lookbehind:!0}],keyword:[{pattern:/((?:^|\})\s*)catch\b/,lookbehind:!0},{pattern:/(^|[^.]|\.\.\.\s*)\b(?:as|assert(?=\s*\{)|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally(?=\s*(?:\{|$))|for|from(?=\s*(?:['"]|$))|function|(?:get|set)(?=\s*(?:[#\[$\w\xA0-\uFFFF]|$))|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,lookbehind:!0}],function:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,number:{pattern:RegExp(/(^|[^\w$])/.source+"(?:"+/NaN|Infinity/.source+"|"+/0[bB][01]+(?:_[01]+)*n?/.source+"|"+/0[oO][0-7]+(?:_[0-7]+)*n?/.source+"|"+/0[xX][\dA-Fa-f]+(?:_[\dA-Fa-f]+)*n?/.source+"|"+/\d+(?:_\d+)*n/.source+"|"+/(?:\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\.\d+(?:_\d+)*)(?:[Ee][+-]?\d+(?:_\d+)*)?/.source+")"+/(?![\w$])/.source),lookbehind:!0},operator:/--|\+\+|\*\*=?|=>|&&=?|\|\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?\?=?|\?\.?|[~:]/}),s.languages.javascript["class-name"][0].pattern=/(\b(?:class|extends|implements|instanceof|interface|new)\s+)[\w.\\]+/,s.languages.insertBefore("javascript","keyword",{regex:{pattern:RegExp(/((?:^|[^$\w\xA0-\uFFFF."'\])\s]|\b(?:return|yield))\s*)/.source+/\//.source+"(?:"+/(?:\[(?:[^\]\\\r\n]|\\.)*\]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}/.source+"|"+/(?:\[(?:[^[\]\\\r\n]|\\.|\[(?:[^[\]\\\r\n]|\\.|\[(?:[^[\]\\\r\n]|\\.)*\])*\])*\]|\\.|[^/\\\[\r\n])+\/[dgimyus]{0,7}v[dgimyus]{0,7}/.source+")"+/(?=(?:\s|\/\*(?:[^*]|\*(?!\/))*\*\/)*(?:$|[\r\n,.;:})\]]|\/\/))/.source),lookbehind:!0,greedy:!0,inside:{"regex-source":{pattern:/^(\/)[\s\S]+(?=\/[a-z]*$)/,lookbehind:!0,alias:"language-regex",inside:s.languages.regex},"regex-delimiter":/^\/|\/$/,"regex-flags":/^[a-z]+$/}},"function-variable":{pattern:/#?(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*)?\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\))/,lookbehind:!0,inside:s.languages.javascript},{pattern:/(^|[^$\w\xA0-\uFFFF])(?!\s)[_$a-z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*=>)/i,lookbehind:!0,inside:s.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*=>)/,lookbehind:!0,inside:s.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*\s*)\(\s*|\]\s*\(\s*)(?!\s)(?:[^()\s]|\s+(?![\s)])|\([^()]*\))+(?=\s*\)\s*\{)/,lookbehind:!0,inside:s.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),s.languages.insertBefore("javascript","string",{hashbang:{pattern:/^#!.*/,greedy:!0,alias:"comment"},"template-string":{pattern:/`(?:\\[\s\S]|\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}|(?!\$\{)[^\\`])*`/,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\$\{(?:[^{}]|\{(?:[^{}]|\{[^}]*\})*\})+\}/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\$\{|\}$/,alias:"punctuation"},rest:s.languages.javascript}},string:/[\s\S]+/}},"string-property":{pattern:/((?:^|[,{])[ \t]*)(["'])(?:\\(?:\r\n|[\s\S])|(?!\2)[^\\\r\n])*\2(?=\s*:)/m,lookbehind:!0,greedy:!0,alias:"property"}}),s.languages.insertBefore("javascript","operator",{"literal-property":{pattern:/((?:^|[,{])[ \t]*)(?!\s)[_$a-zA-Z\xA0-\uFFFF](?:(?!\s)[$\w\xA0-\uFFFF])*(?=\s*:)/m,lookbehind:!0,alias:"property"}}),s.languages.markup&&(s.languages.markup.tag.addInlined("script","javascript"),s.languages.markup.tag.addAttribute(/on(?:abort|blur|change|click|composition(?:end|start|update)|dblclick|error|focus(?:in|out)?|key(?:down|up)|load|mouse(?:down|enter|leave|move|out|over|up)|reset|resize|scroll|select|slotchange|submit|unload|wheel)/.source,"javascript")),s.languages.js=s.languages.javascript,s.languages.markup={comment:{pattern://,greedy:!0},prolog:{pattern:/<\?[\s\S]+?\?>/,greedy:!0},doctype:{pattern:/"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:[^<"'\]]|"[^"]*"|'[^']*'|<(?!!--)|)*\]\s*)?>/i,greedy:!0,inside:{"internal-subset":{pattern:/(^[^\[]*\[)[\s\S]+(?=\]>$)/,lookbehind:!0,greedy:!0,inside:null},string:{pattern:/"[^"]*"|'[^']*'/,greedy:!0},punctuation:/^$|[[\]]/,"doctype-tag":/^DOCTYPE/i,name:/[^\s<>'"]+/}},cdata:{pattern://i,greedy:!0},tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"special-attr":[],"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/,inside:{punctuation:[{pattern:/^=/,alias:"attr-equals"},{pattern:/^(\s*)["']|["']$/,lookbehind:!0}]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:[{pattern:/&[\da-z]{1,8};/i,alias:"named-entity"},/&#x?[\da-f]{1,8};/i]},s.languages.markup.tag.inside["attr-value"].inside.entity=s.languages.markup.entity,s.languages.markup.doctype.inside["internal-subset"].inside=s.languages.markup,s.hooks.add("wrap",(function(e){"entity"===e.type&&(e.attributes.title=e.content.replace(/&/,"&"))})),Object.defineProperty(s.languages.markup.tag,"addInlined",{value:function(e,t){var n={};n["language-"+t]={pattern:/(^$)/i,lookbehind:!0,inside:s.languages[t]},n.cdata=/^$/i;var a={"included-cdata":{pattern://i,inside:n}};a["language-"+t]={pattern:/[\s\S]+/,inside:s.languages[t]};var r={};r[e]={pattern:RegExp(/(<__[^>]*>)(?:))*\]\]>|(?!)/.source.replace(/__/g,(function(){return e})),"i"),lookbehind:!0,greedy:!0,inside:a},s.languages.insertBefore("markup","cdata",r)}}),Object.defineProperty(s.languages.markup.tag,"addAttribute",{value:function(e,t){s.languages.markup.tag.inside["special-attr"].push({pattern:RegExp(/(^|["'\s])/.source+"(?:"+e+")"+/\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))/.source,"i"),lookbehind:!0,inside:{"attr-name":/^[^\s=]+/,"attr-value":{pattern:/=[\s\S]+/,inside:{value:{pattern:/(^=\s*(["']|(?!["'])))\S[\s\S]*(?=\2$)/,lookbehind:!0,alias:[t,"language-"+t],inside:s.languages[t]},punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}}}})}}),s.languages.html=s.languages.markup,s.languages.mathml=s.languages.markup,s.languages.svg=s.languages.markup,s.languages.xml=s.languages.extend("markup",{}),s.languages.ssml=s.languages.xml,s.languages.atom=s.languages.xml,s.languages.rss=s.languages.xml,function(e){var t=/\/\*[\s\S]*?\*\/|\/\/.*|#(?!\[).*/,n=[{pattern:/\b(?:false|true)\b/i,alias:"boolean"},{pattern:/(::\s*)\b[a-z_]\w*\b(?!\s*\()/i,greedy:!0,lookbehind:!0},{pattern:/(\b(?:case|const)\s+)\b[a-z_]\w*(?=\s*[;=])/i,greedy:!0,lookbehind:!0},/\b(?:null)\b/i,/\b[A-Z_][A-Z0-9_]*\b(?!\s*\()/],a=/\b0b[01]+(?:_[01]+)*\b|\b0o[0-7]+(?:_[0-7]+)*\b|\b0x[\da-f]+(?:_[\da-f]+)*\b|(?:\b\d+(?:_\d+)*\.?(?:\d+(?:_\d+)*)?|\B\.\d+)(?:e[+-]?\d+)?/i,s=/|\?\?=?|\.{3}|\??->|[!=]=?=?|::|\*\*=?|--|\+\+|&&|\|\||<<|>>|[?~]|[/^|%*&<>.+-]=?/,r=/[{}\[\](),:;]/;e.languages.php={delimiter:{pattern:/\?>$|^<\?(?:php(?=\s)|=)?/i,alias:"important"},comment:t,variable:/\$+(?:\w+\b|(?=\{))/,package:{pattern:/(namespace\s+|use\s+(?:function\s+)?)(?:\\?\b[a-z_]\w*)+\b(?!\\)/i,lookbehind:!0,inside:{punctuation:/\\/}},"class-name-definition":{pattern:/(\b(?:class|enum|interface|trait)\s+)\b[a-z_]\w*(?!\\)\b/i,lookbehind:!0,alias:"class-name"},"function-definition":{pattern:/(\bfunction\s+)[a-z_]\w*(?=\s*\()/i,lookbehind:!0,alias:"function"},keyword:[{pattern:/(\(\s*)\b(?:array|bool|boolean|float|int|integer|object|string)\b(?=\s*\))/i,alias:"type-casting",greedy:!0,lookbehind:!0},{pattern:/([(,?]\s*)\b(?:array(?!\s*\()|bool|callable|(?:false|null)(?=\s*\|)|float|int|iterable|mixed|object|self|static|string)\b(?=\s*\$)/i,alias:"type-hint",greedy:!0,lookbehind:!0},{pattern:/(\)\s*:\s*(?:\?\s*)?)\b(?:array(?!\s*\()|bool|callable|(?:false|null)(?=\s*\|)|float|int|iterable|mixed|never|object|self|static|string|void)\b/i,alias:"return-type",greedy:!0,lookbehind:!0},{pattern:/\b(?:array(?!\s*\()|bool|float|int|iterable|mixed|object|string|void)\b/i,alias:"type-declaration",greedy:!0},{pattern:/(\|\s*)(?:false|null)\b|\b(?:false|null)(?=\s*\|)/i,alias:"type-declaration",greedy:!0,lookbehind:!0},{pattern:/\b(?:parent|self|static)(?=\s*::)/i,alias:"static-context",greedy:!0},{pattern:/(\byield\s+)from\b/i,lookbehind:!0},/\bclass\b/i,{pattern:/((?:^|[^\s>:]|(?:^|[^-])>|(?:^|[^:]):)\s*)\b(?:abstract|and|array|as|break|callable|case|catch|clone|const|continue|declare|default|die|do|echo|else|elseif|empty|enddeclare|endfor|endforeach|endif|endswitch|endwhile|enum|eval|exit|extends|final|finally|fn|for|foreach|function|global|goto|if|implements|include|include_once|instanceof|insteadof|interface|isset|list|match|namespace|never|new|or|parent|print|private|protected|public|readonly|require|require_once|return|self|static|switch|throw|trait|try|unset|use|var|while|xor|yield|__halt_compiler)\b/i,lookbehind:!0}],"argument-name":{pattern:/([(,]\s*)\b[a-z_]\w*(?=\s*:(?!:))/i,lookbehind:!0},"class-name":[{pattern:/(\b(?:extends|implements|instanceof|new(?!\s+self|\s+static))\s+|\bcatch\s*\()\b[a-z_]\w*(?!\\)\b/i,greedy:!0,lookbehind:!0},{pattern:/(\|\s*)\b[a-z_]\w*(?!\\)\b/i,greedy:!0,lookbehind:!0},{pattern:/\b[a-z_]\w*(?!\\)\b(?=\s*\|)/i,greedy:!0},{pattern:/(\|\s*)(?:\\?\b[a-z_]\w*)+\b/i,alias:"class-name-fully-qualified",greedy:!0,lookbehind:!0,inside:{punctuation:/\\/}},{pattern:/(?:\\?\b[a-z_]\w*)+\b(?=\s*\|)/i,alias:"class-name-fully-qualified",greedy:!0,inside:{punctuation:/\\/}},{pattern:/(\b(?:extends|implements|instanceof|new(?!\s+self\b|\s+static\b))\s+|\bcatch\s*\()(?:\\?\b[a-z_]\w*)+\b(?!\\)/i,alias:"class-name-fully-qualified",greedy:!0,lookbehind:!0,inside:{punctuation:/\\/}},{pattern:/\b[a-z_]\w*(?=\s*\$)/i,alias:"type-declaration",greedy:!0},{pattern:/(?:\\?\b[a-z_]\w*)+(?=\s*\$)/i,alias:["class-name-fully-qualified","type-declaration"],greedy:!0,inside:{punctuation:/\\/}},{pattern:/\b[a-z_]\w*(?=\s*::)/i,alias:"static-context",greedy:!0},{pattern:/(?:\\?\b[a-z_]\w*)+(?=\s*::)/i,alias:["class-name-fully-qualified","static-context"],greedy:!0,inside:{punctuation:/\\/}},{pattern:/([(,?]\s*)[a-z_]\w*(?=\s*\$)/i,alias:"type-hint",greedy:!0,lookbehind:!0},{pattern:/([(,?]\s*)(?:\\?\b[a-z_]\w*)+(?=\s*\$)/i,alias:["class-name-fully-qualified","type-hint"],greedy:!0,lookbehind:!0,inside:{punctuation:/\\/}},{pattern:/(\)\s*:\s*(?:\?\s*)?)\b[a-z_]\w*(?!\\)\b/i,alias:"return-type",greedy:!0,lookbehind:!0},{pattern:/(\)\s*:\s*(?:\?\s*)?)(?:\\?\b[a-z_]\w*)+\b(?!\\)/i,alias:["class-name-fully-qualified","return-type"],greedy:!0,lookbehind:!0,inside:{punctuation:/\\/}}],constant:n,function:{pattern:/(^|[^\\\w])\\?[a-z_](?:[\w\\]*\w)?(?=\s*\()/i,lookbehind:!0,inside:{punctuation:/\\/}},property:{pattern:/(->\s*)\w+/,lookbehind:!0},number:a,operator:s,punctuation:r};var i={pattern:/\{\$(?:\{(?:\{[^{}]+\}|[^{}]+)\}|[^{}])+\}|(^|[^\\{])\$+(?:\w+(?:\[[^\r\n\[\]]+\]|->\w+)?)/,lookbehind:!0,inside:e.languages.php},o=[{pattern:/<<<'([^']+)'[\r\n](?:.*[\r\n])*?\1;/,alias:"nowdoc-string",greedy:!0,inside:{delimiter:{pattern:/^<<<'[^']+'|[a-z_]\w*;$/i,alias:"symbol",inside:{punctuation:/^<<<'?|[';]$/}}}},{pattern:/<<<(?:"([^"]+)"[\r\n](?:.*[\r\n])*?\1;|([a-z_]\w*)[\r\n](?:.*[\r\n])*?\2;)/i,alias:"heredoc-string",greedy:!0,inside:{delimiter:{pattern:/^<<<(?:"[^"]+"|[a-z_]\w*)|[a-z_]\w*;$/i,alias:"symbol",inside:{punctuation:/^<<<"?|[";]$/}},interpolation:i}},{pattern:/`(?:\\[\s\S]|[^\\`])*`/,alias:"backtick-quoted-string",greedy:!0},{pattern:/'(?:\\[\s\S]|[^\\'])*'/,alias:"single-quoted-string",greedy:!0},{pattern:/"(?:\\[\s\S]|[^\\"])*"/,alias:"double-quoted-string",greedy:!0,inside:{interpolation:i}}];e.languages.insertBefore("php","variable",{string:o,attribute:{pattern:/#\[(?:[^"'\/#]|\/(?![*/])|\/\/.*$|#(?!\[).*$|\/\*(?:[^*]|\*(?!\/))*\*\/|"(?:\\[\s\S]|[^\\"])*"|'(?:\\[\s\S]|[^\\'])*')+\](?=\s*[a-z$#])/im,greedy:!0,inside:{"attribute-content":{pattern:/^(#\[)[\s\S]+(?=\]$)/,lookbehind:!0,inside:{comment:t,string:o,"attribute-class-name":[{pattern:/([^:]|^)\b[a-z_]\w*(?!\\)\b/i,alias:"class-name",greedy:!0,lookbehind:!0},{pattern:/([^:]|^)(?:\\?\b[a-z_]\w*)+/i,alias:["class-name","class-name-fully-qualified"],greedy:!0,lookbehind:!0,inside:{punctuation:/\\/}}],constant:n,number:a,operator:s,punctuation:r}},delimiter:{pattern:/^#\[|\]$/,alias:"punctuation"}}}}),e.hooks.add("before-tokenize",(function(t){/<\?/.test(t.code)&&e.languages["markup-templating"].buildPlaceholders(t,"php",/<\?(?:[^"'/#]|\/(?![*/])|("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|(?:\/\/|#(?!\[))(?:[^?\n\r]|\?(?!>))*(?=$|\?>|[\r\n])|#\[|\/\*(?:[^*]|\*(?!\/))*(?:\*\/|$))*?(?:\?>|$)/g)})),e.hooks.add("after-tokenize",(function(t){e.languages["markup-templating"].tokenizePlaceholders(t,"php")}))}(s),s.languages.python={comment:{pattern:/(^|[^\\])#.*/,lookbehind:!0,greedy:!0},"string-interpolation":{pattern:/(?:f|fr|rf)(?:("""|''')[\s\S]*?\1|("|')(?:\\.|(?!\2)[^\\\r\n])*\2)/i,greedy:!0,inside:{interpolation:{pattern:/((?:^|[^{])(?:\{\{)*)\{(?!\{)(?:[^{}]|\{(?!\{)(?:[^{}]|\{(?!\{)(?:[^{}])+\})+\})+\}/,lookbehind:!0,inside:{"format-spec":{pattern:/(:)[^:(){}]+(?=\}$)/,lookbehind:!0},"conversion-option":{pattern:/![sra](?=[:}]$)/,alias:"punctuation"},rest:null}},string:/[\s\S]+/}},"triple-quoted-string":{pattern:/(?:[rub]|br|rb)?("""|''')[\s\S]*?\1/i,greedy:!0,alias:"string"},string:{pattern:/(?:[rub]|br|rb)?("|')(?:\\.|(?!\1)[^\\\r\n])*\1/i,greedy:!0},function:{pattern:/((?:^|\s)def[ \t]+)[a-zA-Z_]\w*(?=\s*\()/g,lookbehind:!0},"class-name":{pattern:/(\bclass\s+)\w+/i,lookbehind:!0},decorator:{pattern:/(^[\t ]*)@\w+(?:\.\w+)*/m,lookbehind:!0,alias:["annotation","punctuation"],inside:{punctuation:/\./}},keyword:/\b(?:_(?=\s*:)|and|as|assert|async|await|break|case|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|match|nonlocal|not|or|pass|print|raise|return|try|while|with|yield)\b/,builtin:/\b(?:__import__|abs|all|any|apply|ascii|basestring|bin|bool|buffer|bytearray|bytes|callable|chr|classmethod|cmp|coerce|compile|complex|delattr|dict|dir|divmod|enumerate|eval|execfile|file|filter|float|format|frozenset|getattr|globals|hasattr|hash|help|hex|id|input|int|intern|isinstance|issubclass|iter|len|list|locals|long|map|max|memoryview|min|next|object|oct|open|ord|pow|property|range|raw_input|reduce|reload|repr|reversed|round|set|setattr|slice|sorted|staticmethod|str|sum|super|tuple|type|unichr|unicode|vars|xrange|zip)\b/,boolean:/\b(?:False|None|True)\b/,number:/\b0(?:b(?:_?[01])+|o(?:_?[0-7])+|x(?:_?[a-f0-9])+)\b|(?:\b\d+(?:_\d+)*(?:\.(?:\d+(?:_\d+)*)?)?|\B\.\d+(?:_\d+)*)(?:e[+-]?\d+(?:_\d+)*)?j?(?!\w)/i,operator:/[-+%=]=?|!=|:=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]/,punctuation:/[{}[\];(),.:]/},s.languages.python["string-interpolation"].inside.interpolation.inside.rest=s.languages.python,s.languages.py=s.languages.python,function(e){e.languages.ruby=e.languages.extend("clike",{comment:{pattern:/#.*|^=begin\s[\s\S]*?^=end/m,greedy:!0},"class-name":{pattern:/(\b(?:class|module)\s+|\bcatch\s+\()[\w.\\]+|\b[A-Z_]\w*(?=\s*\.\s*new\b)/,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:BEGIN|END|alias|and|begin|break|case|class|def|define_method|defined|do|each|else|elsif|end|ensure|extend|for|if|in|include|module|new|next|nil|not|or|prepend|private|protected|public|raise|redo|require|rescue|retry|return|self|super|then|throw|undef|unless|until|when|while|yield)\b/,operator:/\.{2,3}|&\.|===||[!=]?~|(?:&&|\|\||<<|>>|\*\*|[+\-*/%<>!^&|=])=?|[?:]/,punctuation:/[(){}[\].,;]/}),e.languages.insertBefore("ruby","operator",{"double-colon":{pattern:/::/,alias:"punctuation"}});var t={pattern:/((?:^|[^\\])(?:\\{2})*)#\{(?:[^{}]|\{[^{}]*\})*\}/,lookbehind:!0,inside:{content:{pattern:/^(#\{)[\s\S]+(?=\}$)/,lookbehind:!0,inside:e.languages.ruby},delimiter:{pattern:/^#\{|\}$/,alias:"punctuation"}}};delete e.languages.ruby.function;var n="(?:"+[/([^a-zA-Z0-9\s{(\[<=])(?:(?!\1)[^\\]|\\[\s\S])*\1/.source,/\((?:[^()\\]|\\[\s\S]|\((?:[^()\\]|\\[\s\S])*\))*\)/.source,/\{(?:[^{}\\]|\\[\s\S]|\{(?:[^{}\\]|\\[\s\S])*\})*\}/.source,/\[(?:[^\[\]\\]|\\[\s\S]|\[(?:[^\[\]\\]|\\[\s\S])*\])*\]/.source,/<(?:[^<>\\]|\\[\s\S]|<(?:[^<>\\]|\\[\s\S])*>)*>/.source].join("|")+")",a=/(?:"(?:\\.|[^"\\\r\n])*"|(?:\b[a-zA-Z_]\w*|[^\s\0-\x7F]+)[?!]?|\$.)/.source;e.languages.insertBefore("ruby","keyword",{"regex-literal":[{pattern:RegExp(/%r/.source+n+/[egimnosux]{0,6}/.source),greedy:!0,inside:{interpolation:t,regex:/[\s\S]+/}},{pattern:/(^|[^/])\/(?!\/)(?:\[[^\r\n\]]+\]|\\.|[^[/\\\r\n])+\/[egimnosux]{0,6}(?=\s*(?:$|[\r\n,.;})#]))/,lookbehind:!0,greedy:!0,inside:{interpolation:t,regex:/[\s\S]+/}}],variable:/[@$]+[a-zA-Z_]\w*(?:[?!]|\b)/,symbol:[{pattern:RegExp(/(^|[^:]):/.source+a),lookbehind:!0,greedy:!0},{pattern:RegExp(/([\r\n{(,][ \t]*)/.source+a+/(?=:(?!:))/.source),lookbehind:!0,greedy:!0}],"method-definition":{pattern:/(\bdef\s+)\w+(?:\s*\.\s*\w+)?/,lookbehind:!0,inside:{function:/\b\w+$/,keyword:/^self\b/,"class-name":/^\w+/,punctuation:/\./}}}),e.languages.insertBefore("ruby","string",{"string-literal":[{pattern:RegExp(/%[qQiIwWs]?/.source+n),greedy:!0,inside:{interpolation:t,string:/[\s\S]+/}},{pattern:/("|')(?:#\{[^}]+\}|#(?!\{)|\\(?:\r\n|[\s\S])|(?!\1)[^\\#\r\n])*\1/,greedy:!0,inside:{interpolation:t,string:/[\s\S]+/}},{pattern:/<<[-~]?([a-z_]\w*)[\r\n](?:.*[\r\n])*?[\t ]*\1/i,alias:"heredoc-string",greedy:!0,inside:{delimiter:{pattern:/^<<[-~]?[a-z_]\w*|\b[a-z_]\w*$/i,inside:{symbol:/\b\w+/,punctuation:/^<<[-~]?/}},interpolation:t,string:/[\s\S]+/}},{pattern:/<<[-~]?'([a-z_]\w*)'[\r\n](?:.*[\r\n])*?[\t ]*\1/i,alias:"heredoc-string",greedy:!0,inside:{delimiter:{pattern:/^<<[-~]?'[a-z_]\w*'|\b[a-z_]\w*$/i,inside:{symbol:/\b\w+/,punctuation:/^<<[-~]?'|'$/}},string:/[\s\S]+/}}],"command-literal":[{pattern:RegExp(/%x/.source+n),greedy:!0,inside:{interpolation:t,command:{pattern:/[\s\S]+/,alias:"string"}}},{pattern:/`(?:#\{[^}]+\}|#(?!\{)|\\(?:\r\n|[\s\S])|[^\\`#\r\n])*`/,greedy:!0,inside:{interpolation:t,command:{pattern:/[\s\S]+/,alias:"string"}}}]}),delete e.languages.ruby.string,e.languages.insertBefore("ruby","number",{builtin:/\b(?:Array|Bignum|Binding|Class|Continuation|Dir|Exception|FalseClass|File|Fixnum|Float|Hash|IO|Integer|MatchData|Method|Module|NilClass|Numeric|Object|Proc|Range|Regexp|Stat|String|Struct|Symbol|TMS|Thread|ThreadGroup|Time|TrueClass)\b/,constant:/\b[A-Z][A-Z0-9_]*(?:[?!]|\b)/}),e.languages.rb=e.languages.ruby}(s),window.Prism=a,s}(),o=e=>t=>t.options.get(e),l=o("codesample_languages"),u=o("codesample_global_prismjs"),c=e=>r.Prism&&u(e)?r.Prism:i,d=e=>t(e)&&"PRE"===e.nodeName&&-1!==e.className.indexOf("language-"),g=e=>{const t=e.selection?e.selection.getNode():null;return d(t)?a.some(t):a.none()},p=e=>{const t=(e=>l(e)||[{text:"HTML/XML",value:"markup"},{text:"JavaScript",value:"javascript"},{text:"CSS",value:"css"},{text:"PHP",value:"php"},{text:"Ruby",value:"ruby"},{text:"Python",value:"python"},{text:"Java",value:"java"},{text:"C",value:"c"},{text:"C#",value:"csharp"},{text:"C++",value:"cpp"}])(e),n=(r=t,((e,t)=>0""),(e=>e.value));var r;const i=((e,t)=>g(e).fold((()=>t),(e=>{const n=e.className.match(/language-(\w+)/);return n?n[1]:t})))(e,n),o=(e=>g(e).bind((e=>a.from(e.textContent))).getOr(""))(e);e.windowManager.open({title:"Insert/Edit Code Sample",size:"large",body:{type:"panel",items:[{type:"listbox",name:"language",label:"Language",items:t},{type:"textarea",name:"code",label:"Code view"}]},buttons:[{type:"cancel",name:"cancel",text:"Cancel"},{type:"submit",name:"save",text:"Save",primary:!0}],initialData:{language:i,code:o},onSubmit:t=>{const n=t.getData();((e,t,n)=>{const a=e.dom;e.undoManager.transact((()=>{const r=g(e);return n=s.DOM.encode(n),r.fold((()=>{e.insertContent('
'+n+"
");const s=a.select("#__new")[0];a.setAttrib(s,"id",null),e.selection.select(s)}),(s=>{a.setAttrib(s,"class","language-"+t),s.innerHTML=n,c(e).highlightElement(s),e.selection.select(s)}))}))})(e,n.language,n.code),t.close()}})},b=(h=/^\s+|\s+$/g,e=>e.replace(h,""));var h,f=tinymce.util.Tools.resolve("tinymce.util.Tools");const m=(e,t=n)=>n=>{const a=()=>{n.setEnabled(e.selection.isEditable()),t(n)};return e.on("NodeChange",a),a(),()=>{e.off("NodeChange",a)}};e.add("codesample",(e=>{(e=>{const t=e.options.register;t("codesample_languages",{processor:"object[]"}),t("codesample_global_prismjs",{processor:"boolean",default:!1})})(e),(e=>{e.on("PreProcess",(t=>{const n=e.dom,a=n.select("pre[contenteditable=false]",t.node);f.each(f.grep(a,d),(e=>{const t=e.textContent;let a;for(n.setAttrib(e,"class",b(n.getAttrib(e,"class"))),n.setAttrib(e,"contentEditable",null),n.setAttrib(e,"data-mce-highlighted",null);a=e.firstChild;)e.removeChild(a);n.add(e,"code").textContent=t}))})),e.on("SetContent",(()=>{const t=e.dom,n=f.grep(t.select("pre"),(e=>d(e)&&"true"!==t.getAttrib(e,"data-mce-highlighted")));n.length&&e.undoManager.transact((()=>{f.each(n,(n=>{var a;f.each(t.select("br",n),(n=>{t.replace(e.getDoc().createTextNode("\n"),n)})),n.innerHTML=t.encode(null!==(a=n.textContent)&&void 0!==a?a:""),c(e).highlightElement(n),t.setAttrib(n,"data-mce-highlighted",!0),n.className=b(n.className)}))}))})),e.on("PreInit",(()=>{e.parser.addNodeFilter("pre",(e=>{var t;for(let n=0,a=e.length;n{const t=()=>e.execCommand("codesample");e.ui.registry.addToggleButton("codesample",{icon:"code-sample",tooltip:"Insert/edit code sample",onAction:t,onSetup:m(e,(t=>{t.setActive((e=>{const t=e.selection.getStart();return e.dom.is(t,'pre[class*="language-"]')})(e))}))}),e.ui.registry.addMenuItem("codesample",{text:"Code sample...",icon:"code-sample",onAction:t,onSetup:m(e)})})(e),(e=>{e.addCommand("codesample",(()=>{const t=e.selection.getNode();e.selection.isCollapsed()||d(t)?p(e):e.formatter.toggle("code")}))})(e),e.on("dblclick",(t=>{d(t.target)&&p(e)}))}))}(); \ No newline at end of file diff --git a/public/libs/tinymce/plugins/directionality/plugin.min.js b/public/libs/tinymce/plugins/directionality/plugin.min.js index d6a2eacef7f..aa497696281 100644 --- a/public/libs/tinymce/plugins/directionality/plugin.min.js +++ b/public/libs/tinymce/plugins/directionality/plugin.min.js @@ -1,4 +1,4 @@ /** - * TinyMCE version 6.7.2 (2023-10-25) + * TinyMCE version 6.8.4 (2024-06-19) */ !function(){"use strict";var t=tinymce.util.Tools.resolve("tinymce.PluginManager");const e=t=>e=>typeof e===t,o=t=>"string"===(t=>{const e=typeof t;return null===t?"null":"object"===e&&Array.isArray(t)?"array":"object"===e&&(o=r=t,(n=String).prototype.isPrototypeOf(o)||(null===(i=r.constructor)||void 0===i?void 0:i.name)===n.name)?"string":e;var o,r,n,i})(t),r=e("boolean"),n=t=>!(t=>null==t)(t),i=e("function"),s=e("number"),l=(!1,()=>false);class a{constructor(t,e){this.tag=t,this.value=e}static some(t){return new a(!0,t)}static none(){return a.singletonNone}fold(t,e){return this.tag?e(this.value):t()}isSome(){return this.tag}isNone(){return!this.tag}map(t){return this.tag?a.some(t(this.value)):a.none()}bind(t){return this.tag?t(this.value):a.none()}exists(t){return this.tag&&t(this.value)}forall(t){return!this.tag||t(this.value)}filter(t){return!this.tag||t(this.value)?this:a.none()}getOr(t){return this.tag?this.value:t}or(t){return this.tag?this:t}getOrThunk(t){return this.tag?this.value:t()}orThunk(t){return this.tag?this:t()}getOrDie(t){if(this.tag)return this.value;throw new Error(null!=t?t:"Called getOrDie on None")}static from(t){return n(t)?a.some(t):a.none()}getOrNull(){return this.tag?this.value:null}getOrUndefined(){return this.value}each(t){this.tag&&t(this.value)}toArray(){return this.tag?[this.value]:[]}toString(){return this.tag?`some(${this.value})`:"none()"}}a.singletonNone=new a(!1);const u=(t,e)=>{for(let o=0,r=t.length;o{if(null==t)throw new Error("Node cannot be null or undefined");return{dom:t}},d=c,h=(t,e)=>{const o=t.dom;if(1!==o.nodeType)return!1;{const t=o;if(void 0!==t.matches)return t.matches(e);if(void 0!==t.msMatchesSelector)return t.msMatchesSelector(e);if(void 0!==t.webkitMatchesSelector)return t.webkitMatchesSelector(e);if(void 0!==t.mozMatchesSelector)return t.mozMatchesSelector(e);throw new Error("Browser lacks native selectors")}};"undefined"!=typeof window?window:Function("return this;")();const m=t=>e=>(t=>t.dom.nodeType)(e)===t,g=m(1),f=m(3),v=m(9),y=m(11),p=(t,e)=>{t.dom.removeAttribute(e)},w=i(Element.prototype.attachShadow)&&i(Node.prototype.getRootNode)?t=>d(t.dom.getRootNode()):t=>v(t)?t:d(t.dom.ownerDocument),b=t=>d(t.dom.host),N=t=>{const e=f(t)?t.dom.parentNode:t.dom;if(null==e||null===e.ownerDocument)return!1;const o=e.ownerDocument;return(t=>{const e=w(t);return y(o=e)&&n(o.dom.host)?a.some(e):a.none();var o})(d(e)).fold((()=>o.body.contains(e)),(r=N,i=b,t=>r(i(t))));var r,i},S=t=>"rtl"===((t,e)=>{const o=t.dom,r=window.getComputedStyle(o).getPropertyValue(e);return""!==r||N(t)?r:((t,e)=>(t=>void 0!==t.style&&i(t.style.getPropertyValue))(t)?t.style.getPropertyValue(e):"")(o,e)})(t,"direction")?"rtl":"ltr",A=(t,e)=>((t,o)=>((t,e)=>{const o=[];for(let r=0,n=t.length;r{const o=t.length,r=new Array(o);for(let n=0;nh(t,e))))(t),E=("li",t=>g(t)&&"li"===t.dom.nodeName.toLowerCase());const T=(t,e,n)=>{u(e,(e=>{const c=d(e),m=E(c),f=((t,e)=>{return(e?(o=t,r="ol,ul",((t,e,o)=>{let n=t.dom;const s=i(o)?o:l;for(;n.parentNode;){n=n.parentNode;const t=d(n);if(h(t,r))return a.some(t);if(s(t))break}return a.none()})(o,0,n)):a.some(t)).getOr(t);var o,r,n})(c,m);var v;(v=f,(t=>a.from(t.dom.parentNode).map(d))(v).filter(g)).each((e=>{if(t.setStyle(f.dom,"direction",null),S(e)===n?p(f,"dir"):((t,e,n)=>{((t,e,n)=>{if(!(o(n)||r(n)||s(n)))throw console.error("Invalid call to Attribute.set. Key ",e,":: Value ",n,":: Element ",t),new Error("Attribute value was not simple");t.setAttribute(e,n+"")})(t.dom,e,n)})(f,"dir",n),S(f)!==n&&t.setStyle(f.dom,"direction",n),m){const e=A(f,"li[dir],li[style]");u(e,(e=>{p(e,"dir"),t.setStyle(e.dom,"direction",null)}))}}))}))},C=(t,e)=>{t.selection.isEditable()&&(T(t.dom,t.selection.getSelectedBlocks(),e),t.nodeChanged())},D=(t,e)=>o=>{const r=r=>{const n=d(r.element);o.setActive(S(n)===e),o.setEnabled(t.selection.isEditable())};return t.on("NodeChange",r),o.setEnabled(t.selection.isEditable()),()=>t.off("NodeChange",r)};t.add("directionality",(t=>{(t=>{t.addCommand("mceDirectionLTR",(()=>{C(t,"ltr")})),t.addCommand("mceDirectionRTL",(()=>{C(t,"rtl")}))})(t),(t=>{t.ui.registry.addToggleButton("ltr",{tooltip:"Left to right",icon:"ltr",onAction:()=>t.execCommand("mceDirectionLTR"),onSetup:D(t,"ltr")}),t.ui.registry.addToggleButton("rtl",{tooltip:"Right to left",icon:"rtl",onAction:()=>t.execCommand("mceDirectionRTL"),onSetup:D(t,"rtl")})})(t)}))}(); \ No newline at end of file diff --git a/public/libs/tinymce/plugins/fullscreen/plugin.min.js b/public/libs/tinymce/plugins/fullscreen/plugin.min.js index f9cdcf9862d..9032e46bb46 100644 --- a/public/libs/tinymce/plugins/fullscreen/plugin.min.js +++ b/public/libs/tinymce/plugins/fullscreen/plugin.min.js @@ -1,4 +1,4 @@ /** - * TinyMCE version 6.7.2 (2023-10-25) + * TinyMCE version 6.8.4 (2024-06-19) */ -!function(){"use strict";const e=e=>{let t=e;return{get:()=>t,set:e=>{t=e}}};var t=tinymce.util.Tools.resolve("tinymce.PluginManager");const n=e=>t=>(e=>{const t=typeof e;return null===e?"null":"object"===t&&Array.isArray(e)?"array":"object"===t&&(n=r=e,(o=String).prototype.isPrototypeOf(n)||(null===(s=r.constructor)||void 0===s?void 0:s.name)===o.name)?"string":t;var n,r,o,s})(t)===e,r=e=>t=>typeof t===e,o=e=>t=>e===t,s=n("string"),i=n("array"),l=o(null),a=r("boolean"),c=o(void 0),u=e=>!(e=>null==e)(e),d=r("function"),m=r("number"),h=()=>{},g=e=>()=>e;function p(e,...t){return(...n)=>{const r=t.concat(n);return e.apply(null,r)}}const f=g(!1),v=g(!0);class w{constructor(e,t){this.tag=e,this.value=t}static some(e){return new w(!0,e)}static none(){return w.singletonNone}fold(e,t){return this.tag?t(this.value):e()}isSome(){return this.tag}isNone(){return!this.tag}map(e){return this.tag?w.some(e(this.value)):w.none()}bind(e){return this.tag?e(this.value):w.none()}exists(e){return this.tag&&e(this.value)}forall(e){return!this.tag||e(this.value)}filter(e){return!this.tag||e(this.value)?this:w.none()}getOr(e){return this.tag?this.value:e}or(e){return this.tag?this:e}getOrThunk(e){return this.tag?this.value:e()}orThunk(e){return this.tag?this:e()}getOrDie(e){if(this.tag)return this.value;throw new Error(null!=e?e:"Called getOrDie on None")}static from(e){return u(e)?w.some(e):w.none()}getOrNull(){return this.tag?this.value:null}getOrUndefined(){return this.value}each(e){this.tag&&e(this.value)}toArray(){return this.tag?[this.value]:[]}toString(){return this.tag?`some(${this.value})`:"none()"}}w.singletonNone=new w(!1);const y=t=>{const n=e(w.none()),r=()=>n.get().each(t);return{clear:()=>{r(),n.set(w.none())},isSet:()=>n.get().isSome(),get:()=>n.get(),set:e=>{r(),n.set(w.some(e))}}},b=()=>y((e=>e.unbind())),S=Array.prototype.push,x=(e,t)=>{const n=e.length,r=new Array(n);for(let o=0;o{for(let n=0,r=e.length;n{const n=[];for(let r=0,o=e.length;r((e,t,n)=>{for(let r=0,o=e.length;r{const o=e.indexOf(t,n);return-1!==o&&(!!c(r)||o+t.length<=r)},C=e=>void 0!==e.style&&d(e.style.getPropertyValue),A=e=>{if(null==e)throw new Error("Node cannot be null or undefined");return{dom:e}},R=A;"undefined"!=typeof window?window:Function("return this;")();const L=e=>t=>(e=>e.dom.nodeType)(t)===e,M=L(1),N=L(3),P=L(9),D=L(11),W=(e,t)=>{const n=e.dom;if(1!==n.nodeType)return!1;{const e=n;if(void 0!==e.matches)return e.matches(t);if(void 0!==e.msMatchesSelector)return e.msMatchesSelector(t);if(void 0!==e.webkitMatchesSelector)return e.webkitMatchesSelector(t);if(void 0!==e.mozMatchesSelector)return e.mozMatchesSelector(t);throw new Error("Browser lacks native selectors")}},q=e=>R(e.dom.ownerDocument),H=e=>x(e.dom.childNodes,R),I=d(Element.prototype.attachShadow)&&d(Node.prototype.getRootNode),B=g(I),V=I?e=>R(e.dom.getRootNode()):e=>P(e)?e:q(e),_=e=>{const t=V(e);return D(n=t)&&u(n.dom.host)?w.some(t):w.none();var n},j=e=>R(e.dom.host),z=e=>{const t=N(e)?e.dom.parentNode:e.dom;if(null==t||null===t.ownerDocument)return!1;const n=t.ownerDocument;return _(R(t)).fold((()=>n.body.contains(t)),(r=z,o=j,e=>r(o(e))));var r,o},$=(e,t)=>{const n=e.dom.getAttribute(t);return null===n?void 0:n},U=(e,t)=>{e.dom.removeAttribute(t)},K=(e,t)=>{const n=e.dom;((e,t)=>{const n=T(e);for(let r=0,o=n.length;r{((e,t,n)=>{if(!s(n))throw console.error("Invalid call to CSS.set. Property ",t,":: Value ",n,":: Element ",e),new Error("CSS value must be a string: "+n);C(e)&&e.style.setProperty(t,n)})(n,t,e)}))},X=e=>{const t=R((e=>{if(B()&&u(e.target)){const t=R(e.target);if(M(t)&&u(t.dom.shadowRoot)&&e.composed&&e.composedPath){const t=e.composedPath();if(t)return((e,t)=>0e.stopPropagation(),r=()=>e.preventDefault(),o=(s=r,i=n,(...e)=>s(i.apply(null,e)));var s,i;return((e,t,n,r,o,s,i)=>({target:e,x:t,y:n,stop:r,prevent:o,kill:s,raw:i}))(t,e.clientX,e.clientY,n,r,o,e)},Y=(e,t,n,r)=>{e.dom.removeEventListener(t,n,r)},G=v,J=(e,t,n)=>((e,t,n,r)=>((e,t,n,r,o)=>{const s=((e,t)=>n=>{e(n)&&t(X(n))})(n,r);return e.dom.addEventListener(t,s,o),{unbind:p(Y,e,t,s,o)}})(e,t,n,r,!1))(e,t,G,n),Q=()=>Z(0,0),Z=(e,t)=>({major:e,minor:t}),ee={nu:Z,detect:(e,t)=>{const n=String(t).toLowerCase();return 0===e.length?Q():((e,t)=>{const n=((e,t)=>{for(let n=0;nNumber(t.replace(n,"$"+e));return Z(r(1),r(2))})(e,n)},unknown:Q},te=(e,t)=>{const n=String(t).toLowerCase();return O(e,(e=>e.search(n)))},ne=/.*?version\/\ ?([0-9]+)\.([0-9]+).*/,re=e=>t=>k(t,e),oe=[{name:"Edge",versionRegexes:[/.*?edge\/ ?([0-9]+)\.([0-9]+)$/],search:e=>k(e,"edge/")&&k(e,"chrome")&&k(e,"safari")&&k(e,"applewebkit")},{name:"Chromium",brand:"Chromium",versionRegexes:[/.*?chrome\/([0-9]+)\.([0-9]+).*/,ne],search:e=>k(e,"chrome")&&!k(e,"chromeframe")},{name:"IE",versionRegexes:[/.*?msie\ ?([0-9]+)\.([0-9]+).*/,/.*?rv:([0-9]+)\.([0-9]+).*/],search:e=>k(e,"msie")||k(e,"trident")},{name:"Opera",versionRegexes:[ne,/.*?opera\/([0-9]+)\.([0-9]+).*/],search:re("opera")},{name:"Firefox",versionRegexes:[/.*?firefox\/\ ?([0-9]+)\.([0-9]+).*/],search:re("firefox")},{name:"Safari",versionRegexes:[ne,/.*?cpu os ([0-9]+)_([0-9]+).*/],search:e=>(k(e,"safari")||k(e,"mobile/"))&&k(e,"applewebkit")}],se=[{name:"Windows",search:re("win"),versionRegexes:[/.*?windows\ nt\ ?([0-9]+)\.([0-9]+).*/]},{name:"iOS",search:e=>k(e,"iphone")||k(e,"ipad"),versionRegexes:[/.*?version\/\ ?([0-9]+)\.([0-9]+).*/,/.*cpu os ([0-9]+)_([0-9]+).*/,/.*cpu iphone os ([0-9]+)_([0-9]+).*/]},{name:"Android",search:re("android"),versionRegexes:[/.*?android\ ?([0-9]+)\.([0-9]+).*/]},{name:"macOS",search:re("mac os x"),versionRegexes:[/.*?mac\ os\ x\ ?([0-9]+)_([0-9]+).*/]},{name:"Linux",search:re("linux"),versionRegexes:[]},{name:"Solaris",search:re("sunos"),versionRegexes:[]},{name:"FreeBSD",search:re("freebsd"),versionRegexes:[]},{name:"ChromeOS",search:re("cros"),versionRegexes:[/.*?chrome\/([0-9]+)\.([0-9]+).*/]}],ie={browsers:g(oe),oses:g(se)},le="Edge",ae="Chromium",ce="Opera",ue="Firefox",de="Safari",me=e=>{const t=e.current,n=e.version,r=e=>()=>t===e;return{current:t,version:n,isEdge:r(le),isChromium:r(ae),isIE:r("IE"),isOpera:r(ce),isFirefox:r(ue),isSafari:r(de)}},he=()=>me({current:void 0,version:ee.unknown()}),ge=me,pe=(g(le),g(ae),g("IE"),g(ce),g(ue),g(de),"Windows"),fe="Android",ve="Linux",we="macOS",ye="Solaris",be="FreeBSD",Se="ChromeOS",xe=e=>{const t=e.current,n=e.version,r=e=>()=>t===e;return{current:t,version:n,isWindows:r(pe),isiOS:r("iOS"),isAndroid:r(fe),isMacOS:r(we),isLinux:r(ve),isSolaris:r(ye),isFreeBSD:r(be),isChromeOS:r(Se)}},Ee=()=>xe({current:void 0,version:ee.unknown()}),Fe=xe,Oe=(g(pe),g("iOS"),g(fe),g(ve),g(we),g(ye),g(be),g(Se),(e,t,n)=>{const r=ie.browsers(),o=ie.oses(),s=t.bind((e=>((e,t)=>((e,t)=>{for(let n=0;n{const n=t.brand.toLowerCase();return O(e,(e=>{var t;return n===(null===(t=e.brand)||void 0===t?void 0:t.toLowerCase())})).map((e=>({current:e.name,version:ee.nu(parseInt(t.version,10),0)})))})))(r,e))).orThunk((()=>((e,t)=>te(e,t).map((e=>{const n=ee.detect(e.versionRegexes,t);return{current:e.name,version:n}})))(r,e))).fold(he,ge),i=((e,t)=>te(e,t).map((e=>{const n=ee.detect(e.versionRegexes,t);return{current:e.name,version:n}})))(o,e).fold(Ee,Fe),l=((e,t,n,r)=>{const o=e.isiOS()&&!0===/ipad/i.test(n),s=e.isiOS()&&!o,i=e.isiOS()||e.isAndroid(),l=i||r("(pointer:coarse)"),a=o||!s&&i&&r("(min-device-width:768px)"),c=s||i&&!a,u=t.isSafari()&&e.isiOS()&&!1===/safari/i.test(n),d=!c&&!a&&!u;return{isiPad:g(o),isiPhone:g(s),isTablet:g(a),isPhone:g(c),isTouch:g(l),isAndroid:e.isAndroid,isiOS:e.isiOS,isWebView:g(u),isDesktop:g(d)}})(i,s,e,n);return{browser:s,os:i,deviceType:l}}),Te=e=>window.matchMedia(e).matches;let ke=(e=>{let t,n=!1;return(...r)=>(n||(n=!0,t=e.apply(null,r)),t)})((()=>Oe(navigator.userAgent,w.from(navigator.userAgentData),Te)));const Ce=(e,t)=>({left:e,top:t,translate:(n,r)=>Ce(e+n,t+r)}),Ae=Ce,Re=e=>{const t=void 0===e?window:e;return ke().browser.isFirefox()?w.none():w.from(t.visualViewport)},Le=(e,t,n,r)=>({x:e,y:t,width:n,height:r,right:e+n,bottom:t+r}),Me=e=>{const t=void 0===e?window:e,n=t.document,r=(e=>{const t=void 0!==e?e.dom:document,n=t.body.scrollLeft||t.documentElement.scrollLeft,r=t.body.scrollTop||t.documentElement.scrollTop;return Ae(n,r)})(R(n));return Re(t).fold((()=>{const e=t.document.documentElement,n=e.clientWidth,o=e.clientHeight;return Le(r.left,r.top,n,o)}),(e=>Le(Math.max(e.pageLeft,r.left),Math.max(e.pageTop,r.top),e.width,e.height)))},Ne=(e,t,n)=>Re(n).map((n=>{const r=e=>t(X(e));return n.addEventListener(e,r),{unbind:()=>n.removeEventListener(e,r)}})).getOrThunk((()=>({unbind:h})));var Pe=tinymce.util.Tools.resolve("tinymce.dom.DOMUtils"),De=tinymce.util.Tools.resolve("tinymce.Env");const We=(e,t)=>{e.dispatch("FullscreenStateChanged",{state:t}),e.dispatch("ResizeEditor")},qe=("fullscreen_native",e=>e.options.get("fullscreen_native"));const He=e=>{return e.dom===(void 0!==(t=q(e).dom).fullscreenElement?t.fullscreenElement:void 0!==t.msFullscreenElement?t.msFullscreenElement:void 0!==t.webkitFullscreenElement?t.webkitFullscreenElement:null);var t},Ie=(e,t,n)=>((e,t,n)=>F(((e,t)=>{const n=d(t)?t:f;let r=e.dom;const o=[];for(;null!==r.parentNode&&void 0!==r.parentNode;){const e=r.parentNode,t=R(e);if(o.push(t),!0===n(t))break;r=e}return o})(e,n),t))(e,(e=>W(e,t)),n),Be=(e,t)=>((e,n)=>{return F((e=>w.from(e.dom.parentNode).map(R))(r=e).map(H).map((e=>F(e,(e=>{return t=e,!(r.dom===t.dom);var t})))).getOr([]),(e=>W(e,t)));var r})(e),Ve="data-ephox-mobile-fullscreen-style",_e="position:absolute!important;",je="top:0!important;left:0!important;margin:0!important;padding:0!important;width:100%!important;height:100%!important;overflow:visible!important;",ze=De.os.isAndroid(),$e=e=>{const t=((e,t)=>{const n=e.dom,r=window.getComputedStyle(n).getPropertyValue(t);return""!==r||z(e)?r:((e,t)=>C(e)?e.style.getPropertyValue(t):"")(n,t)})(e,"background-color");return void 0!==t&&""!==t?"background-color:"+t+"!important":"background-color:rgb(255,255,255)!important;"},Ue=Pe.DOM,Ke=Re().fold((()=>({bind:h,unbind:h})),(e=>{const t=(()=>{const e=y(h);return{...e,on:t=>e.get().each(t)}})(),n=b(),r=b(),o=((e,t)=>{let n=null;return{cancel:()=>{l(n)||(clearTimeout(n),n=null)},throttle:(...t)=>{l(n)&&(n=setTimeout((()=>{n=null,e.apply(null,t)}),50))}}})((()=>{document.body.scrollTop=0,document.documentElement.scrollTop=0,window.requestAnimationFrame((()=>{t.on((t=>K(t,{top:e.offsetTop+"px",left:e.offsetLeft+"px",height:e.height+"px",width:e.width+"px"})))}))}));return{bind:e=>{t.set(e),o.throttle(),n.set(Ne("resize",o.throttle)),r.set(Ne("scroll",o.throttle))},unbind:()=>{t.on((()=>{n.clear(),r.clear()})),t.clear()}}})),Xe=(e,t)=>{const n=document.body,r=document.documentElement,o=e.getContainer(),l=R(o),c=(e=>{const t=R(e.getElement());return _(t).map(j).getOrThunk((()=>(e=>{const t=e.dom.body;if(null==t)throw new Error("Body is not available yet");return R(t)})(q(t))))})(e),u=t.get(),d=R(e.getBody()),h=De.deviceType.isTouch(),g=o.style,p=e.iframeElement,f=null==p?void 0:p.style,v=e=>{e(n,"tox-fullscreen"),e(r,"tox-fullscreen"),e(o,"tox-fullscreen"),_(l).map((e=>j(e).dom)).each((t=>{e(t,"tox-fullscreen"),e(t,"tox-shadowhost")}))},y=()=>{h&&(e=>{const t=((e,t)=>{const n=document;return 1!==(r=n).nodeType&&9!==r.nodeType&&11!==r.nodeType||0===r.childElementCount?[]:x(n.querySelectorAll(e),R);var r})("["+Ve+"]");E(t,(t=>{const n=$(t,Ve);n&&"no-styles"!==n?K(t,e.parseStyle(n)):U(t,"style"),U(t,Ve)}))})(e.dom),v(Ue.removeClass),Ke.unbind(),w.from(t.get()).each((e=>e.fullscreenChangeHandler.unbind()))};if(u)u.fullscreenChangeHandler.unbind(),qe(e)&&He(c)&&(e=>{const t=e.dom;t.exitFullscreen?t.exitFullscreen():t.msExitFullscreen?t.msExitFullscreen():t.webkitCancelFullScreen&&t.webkitCancelFullScreen()})(q(c)),f.width=u.iframeWidth,f.height=u.iframeHeight,g.width=u.containerWidth,g.height=u.containerHeight,g.top=u.containerTop,g.left=u.containerLeft,y(),b=u.scrollPos,window.scrollTo(b.x,b.y),t.set(null),We(e,!1),e.off("remove",y);else{const n=J(q(c),void 0!==document.fullscreenElement?"fullscreenchange":void 0!==document.msFullscreenElement?"MSFullscreenChange":void 0!==document.webkitFullscreenElement?"webkitfullscreenchange":"fullscreenchange",(n=>{qe(e)&&(He(c)||null===t.get()||Xe(e,t))})),r={scrollPos:Me(window),containerWidth:g.width,containerHeight:g.height,containerTop:g.top,containerLeft:g.left,iframeWidth:f.width,iframeHeight:f.height,fullscreenChangeHandler:n};h&&((e,t,n)=>{const r=t=>n=>{const r=$(n,"style"),o=void 0===r?"no-styles":r.trim();o!==t&&(((e,t,n)=>{((e,t,n)=>{if(!(s(n)||a(n)||m(n)))throw console.error("Invalid call to Attribute.set. Key ",t,":: Value ",n,":: Element ",e),new Error("Attribute value was not simple");e.setAttribute(t,n+"")})(e.dom,t,n)})(n,Ve,o),K(n,e.parseStyle(t)))},o=Ie(t,"*"),l=(e=>{const t=[];for(let n=0,r=e.length;nBe(e,"*:not(.tox-silver-sink)")))),c=$e(n);E(l,r("display:none!important;")),E(o,r(_e+je+c)),r((!0===ze?"":_e)+je+c)(t)})(e.dom,l,d),f.width=f.height="100%",g.width=g.height="",v(Ue.addClass),Ke.bind(l),e.on("remove",y),t.set(r),qe(e)&&(e=>{const t=e.dom;t.requestFullscreen?t.requestFullscreen():t.msRequestFullscreen?t.msRequestFullscreen():t.webkitRequestFullScreen&&t.webkitRequestFullScreen()})(c),We(e,!0)}var b},Ye=(e,t)=>n=>{n.setActive(null!==t.get());const r=e=>n.setActive(e.state);return e.on("FullscreenStateChanged",r),()=>e.off("FullscreenStateChanged",r)};t.add("fullscreen",(t=>{const n=e(null);return t.inline||((e=>{(0,e.options.register)("fullscreen_native",{processor:"boolean",default:!1})})(t),((e,t)=>{e.addCommand("mceFullScreen",(()=>{Xe(e,t)}))})(t,n),((e,t)=>{const n=()=>e.execCommand("mceFullScreen");e.ui.registry.addToggleMenuItem("fullscreen",{text:"Fullscreen",icon:"fullscreen",shortcut:"Meta+Shift+F",onAction:n,onSetup:Ye(e,t)}),e.ui.registry.addToggleButton("fullscreen",{tooltip:"Fullscreen",icon:"fullscreen",onAction:n,onSetup:Ye(e,t)})})(t,n),t.addShortcut("Meta+Shift+F","","mceFullScreen")),(e=>({isFullscreen:()=>null!==e.get()}))(n)}))}(); \ No newline at end of file +!function(){"use strict";const e=e=>{let t=e;return{get:()=>t,set:e=>{t=e}}};var t=tinymce.util.Tools.resolve("tinymce.PluginManager");const n=e=>t=>(e=>{const t=typeof e;return null===e?"null":"object"===t&&Array.isArray(e)?"array":"object"===t&&(n=r=e,(o=String).prototype.isPrototypeOf(n)||(null===(s=r.constructor)||void 0===s?void 0:s.name)===o.name)?"string":t;var n,r,o,s})(t)===e,r=e=>t=>typeof t===e,o=e=>t=>e===t,s=n("string"),i=n("object"),l=n("array"),a=o(null),c=r("boolean"),u=o(void 0),d=e=>!(e=>null==e)(e),m=r("function"),h=r("number"),g=()=>{},p=e=>()=>e;function f(e,...t){return(...n)=>{const r=t.concat(n);return e.apply(null,r)}}const v=p(!1),w=p(!0);class b{constructor(e,t){this.tag=e,this.value=t}static some(e){return new b(!0,e)}static none(){return b.singletonNone}fold(e,t){return this.tag?t(this.value):e()}isSome(){return this.tag}isNone(){return!this.tag}map(e){return this.tag?b.some(e(this.value)):b.none()}bind(e){return this.tag?e(this.value):b.none()}exists(e){return this.tag&&e(this.value)}forall(e){return!this.tag||e(this.value)}filter(e){return!this.tag||e(this.value)?this:b.none()}getOr(e){return this.tag?this.value:e}or(e){return this.tag?this:e}getOrThunk(e){return this.tag?this.value:e()}orThunk(e){return this.tag?this:e()}getOrDie(e){if(this.tag)return this.value;throw new Error(null!=e?e:"Called getOrDie on None")}static from(e){return d(e)?b.some(e):b.none()}getOrNull(){return this.tag?this.value:null}getOrUndefined(){return this.value}each(e){this.tag&&e(this.value)}toArray(){return this.tag?[this.value]:[]}toString(){return this.tag?`some(${this.value})`:"none()"}}b.singletonNone=new b(!1);const y=Array.prototype.push,S=(e,t)=>{const n=e.length,r=new Array(n);for(let o=0;o{for(let n=0,r=e.length;n{const n=[];for(let r=0,o=e.length;r((e,t,n)=>{for(let r=0,o=e.length;r{const n=e(b.none()),r=()=>n.get().each(t);return{clear:()=>{r(),n.set(b.none())},isSet:()=>n.get().isSome(),get:()=>n.get(),set:e=>{r(),n.set(b.some(e))}}},k=()=>O((e=>e.unbind())),T=Object.keys,C="undefined"!=typeof window?window:Function("return this;")(),A=(e,t)=>((e,t)=>{let n=null!=t?t:C;for(let t=0;t{const t=A("ownerDocument.defaultView",e);return i(e)&&((e=>((e,t)=>{const n=((e,t)=>A(e,t))(e,t);if(null==n)throw new Error(e+" not available on this browser");return n})("HTMLElement",e))(t).prototype.isPrototypeOf(e)||/^HTML\w*Element$/.test(R(e).constructor.name))},M=e=>t=>(e=>e.dom.nodeType)(t)===e,P=M(1),D=M(3),N=M(9),H=M(11),W=(e,t)=>{const n=e.dom.getAttribute(t);return null===n?void 0:n},q=(e,t)=>{e.dom.removeAttribute(t)},I=(e,t,n=0,r)=>{const o=e.indexOf(t,n);return-1!==o&&(!!u(r)||o+t.length<=r)},V=e=>void 0!==e.style&&m(e.style.getPropertyValue),j=e=>{if(null==e)throw new Error("Node cannot be null or undefined");return{dom:e}},B=j,_=(e,t)=>{const n=e.dom;if(1!==n.nodeType)return!1;{const e=n;if(void 0!==e.matches)return e.matches(t);if(void 0!==e.msMatchesSelector)return e.msMatchesSelector(t);if(void 0!==e.webkitMatchesSelector)return e.webkitMatchesSelector(t);if(void 0!==e.mozMatchesSelector)return e.mozMatchesSelector(t);throw new Error("Browser lacks native selectors")}},z=e=>B(e.dom.ownerDocument),$=e=>S(e.dom.childNodes,B),U=m(Element.prototype.attachShadow)&&m(Node.prototype.getRootNode),K=p(U),X=U?e=>B(e.dom.getRootNode()):e=>N(e)?e:z(e),Y=e=>{const t=X(e);return H(n=t)&&d(n.dom.host)?b.some(t):b.none();var n},G=e=>B(e.dom.host),J=e=>{const t=D(e)?e.dom.parentNode:e.dom;if(null==t||null===t.ownerDocument)return!1;const n=t.ownerDocument;return Y(B(t)).fold((()=>n.body.contains(t)),(r=J,o=G,e=>r(o(e))));var r,o},Q=(e,t,n)=>{if(!s(n))throw console.error("Invalid call to CSS.set. Property ",t,":: Value ",n,":: Element ",e),new Error("CSS value must be a string: "+n);V(e)&&e.style.setProperty(t,n)},Z=(e,t,n)=>{const r=e.dom;Q(r,t,n)},ee=(e,t)=>{const n=e.dom;((e,t)=>{const n=T(e);for(let r=0,o=n.length;r{Q(n,t,e)}))},te=(e,t)=>{const n=e.dom,r=window.getComputedStyle(n).getPropertyValue(t);return""!==r||J(e)?r:ne(n,t)},ne=(e,t)=>V(e)?e.style.getPropertyValue(t):"",re=e=>{const t=B((e=>{if(K()&&d(e.target)){const t=B(e.target);if(P(t)&&d(t.dom.shadowRoot)&&e.composed&&e.composedPath){const t=e.composedPath();if(t)return((e,t)=>0e.stopPropagation(),r=()=>e.preventDefault(),o=(s=r,i=n,(...e)=>s(i.apply(null,e)));var s,i;return((e,t,n,r,o,s,i)=>({target:e,x:t,y:n,stop:r,prevent:o,kill:s,raw:i}))(t,e.clientX,e.clientY,n,r,o,e)},oe=(e,t,n,r)=>{e.dom.removeEventListener(t,n,r)},se=w,ie=(e,t,n)=>((e,t,n,r)=>((e,t,n,r,o)=>{const s=((e,t)=>n=>{e(n)&&t(re(n))})(n,r);return e.dom.addEventListener(t,s,o),{unbind:f(oe,e,t,s,o)}})(e,t,n,r,!1))(e,t,se,n),le=()=>ae(0,0),ae=(e,t)=>({major:e,minor:t}),ce={nu:ae,detect:(e,t)=>{const n=String(t).toLowerCase();return 0===e.length?le():((e,t)=>{const n=((e,t)=>{for(let n=0;nNumber(t.replace(n,"$"+e));return ae(r(1),r(2))})(e,n)},unknown:le},ue=(e,t)=>{const n=String(t).toLowerCase();return F(e,(e=>e.search(n)))},de=/.*?version\/\ ?([0-9]+)\.([0-9]+).*/,me=e=>t=>I(t,e),he=[{name:"Edge",versionRegexes:[/.*?edge\/ ?([0-9]+)\.([0-9]+)$/],search:e=>I(e,"edge/")&&I(e,"chrome")&&I(e,"safari")&&I(e,"applewebkit")},{name:"Chromium",brand:"Chromium",versionRegexes:[/.*?chrome\/([0-9]+)\.([0-9]+).*/,de],search:e=>I(e,"chrome")&&!I(e,"chromeframe")},{name:"IE",versionRegexes:[/.*?msie\ ?([0-9]+)\.([0-9]+).*/,/.*?rv:([0-9]+)\.([0-9]+).*/],search:e=>I(e,"msie")||I(e,"trident")},{name:"Opera",versionRegexes:[de,/.*?opera\/([0-9]+)\.([0-9]+).*/],search:me("opera")},{name:"Firefox",versionRegexes:[/.*?firefox\/\ ?([0-9]+)\.([0-9]+).*/],search:me("firefox")},{name:"Safari",versionRegexes:[de,/.*?cpu os ([0-9]+)_([0-9]+).*/],search:e=>(I(e,"safari")||I(e,"mobile/"))&&I(e,"applewebkit")}],ge=[{name:"Windows",search:me("win"),versionRegexes:[/.*?windows\ nt\ ?([0-9]+)\.([0-9]+).*/]},{name:"iOS",search:e=>I(e,"iphone")||I(e,"ipad"),versionRegexes:[/.*?version\/\ ?([0-9]+)\.([0-9]+).*/,/.*cpu os ([0-9]+)_([0-9]+).*/,/.*cpu iphone os ([0-9]+)_([0-9]+).*/]},{name:"Android",search:me("android"),versionRegexes:[/.*?android\ ?([0-9]+)\.([0-9]+).*/]},{name:"macOS",search:me("mac os x"),versionRegexes:[/.*?mac\ os\ x\ ?([0-9]+)_([0-9]+).*/]},{name:"Linux",search:me("linux"),versionRegexes:[]},{name:"Solaris",search:me("sunos"),versionRegexes:[]},{name:"FreeBSD",search:me("freebsd"),versionRegexes:[]},{name:"ChromeOS",search:me("cros"),versionRegexes:[/.*?chrome\/([0-9]+)\.([0-9]+).*/]}],pe={browsers:p(he),oses:p(ge)},fe="Edge",ve="Chromium",we="Opera",be="Firefox",ye="Safari",Se=e=>{const t=e.current,n=e.version,r=e=>()=>t===e;return{current:t,version:n,isEdge:r(fe),isChromium:r(ve),isIE:r("IE"),isOpera:r(we),isFirefox:r(be),isSafari:r(ye)}},xe=()=>Se({current:void 0,version:ce.unknown()}),Ee=Se,Fe=(p(fe),p(ve),p("IE"),p(we),p(be),p(ye),"Windows"),Oe="Android",ke="Linux",Te="macOS",Ce="Solaris",Ae="FreeBSD",Re="ChromeOS",Le=e=>{const t=e.current,n=e.version,r=e=>()=>t===e;return{current:t,version:n,isWindows:r(Fe),isiOS:r("iOS"),isAndroid:r(Oe),isMacOS:r(Te),isLinux:r(ke),isSolaris:r(Ce),isFreeBSD:r(Ae),isChromeOS:r(Re)}},Me=()=>Le({current:void 0,version:ce.unknown()}),Pe=Le,De=(p(Fe),p("iOS"),p(Oe),p(ke),p(Te),p(Ce),p(Ae),p(Re),(e,t,n)=>{const r=pe.browsers(),o=pe.oses(),s=t.bind((e=>((e,t)=>((e,t)=>{for(let n=0;n{const n=t.brand.toLowerCase();return F(e,(e=>{var t;return n===(null===(t=e.brand)||void 0===t?void 0:t.toLowerCase())})).map((e=>({current:e.name,version:ce.nu(parseInt(t.version,10),0)})))})))(r,e))).orThunk((()=>((e,t)=>ue(e,t).map((e=>{const n=ce.detect(e.versionRegexes,t);return{current:e.name,version:n}})))(r,e))).fold(xe,Ee),i=((e,t)=>ue(e,t).map((e=>{const n=ce.detect(e.versionRegexes,t);return{current:e.name,version:n}})))(o,e).fold(Me,Pe),l=((e,t,n,r)=>{const o=e.isiOS()&&!0===/ipad/i.test(n),s=e.isiOS()&&!o,i=e.isiOS()||e.isAndroid(),l=i||r("(pointer:coarse)"),a=o||!s&&i&&r("(min-device-width:768px)"),c=s||i&&!a,u=t.isSafari()&&e.isiOS()&&!1===/safari/i.test(n),d=!c&&!a&&!u;return{isiPad:p(o),isiPhone:p(s),isTablet:p(a),isPhone:p(c),isTouch:p(l),isAndroid:e.isAndroid,isiOS:e.isiOS,isWebView:p(u),isDesktop:p(d)}})(i,s,e,n);return{browser:s,os:i,deviceType:l}}),Ne=e=>window.matchMedia(e).matches;let He=(e=>{let t,n=!1;return(...r)=>(n||(n=!0,t=e.apply(null,r)),t)})((()=>De(navigator.userAgent,b.from(navigator.userAgentData),Ne)));const We=(e,t)=>({left:e,top:t,translate:(n,r)=>We(e+n,t+r)}),qe=We,Ie=e=>{const t=void 0===e?window:e;return He().browser.isFirefox()?b.none():b.from(t.visualViewport)},Ve=(e,t,n,r)=>({x:e,y:t,width:n,height:r,right:e+n,bottom:t+r}),je=e=>{const t=void 0===e?window:e,n=t.document,r=(e=>{const t=void 0!==e?e.dom:document,n=t.body.scrollLeft||t.documentElement.scrollLeft,r=t.body.scrollTop||t.documentElement.scrollTop;return qe(n,r)})(B(n));return Ie(t).fold((()=>{const e=t.document.documentElement,n=e.clientWidth,o=e.clientHeight;return Ve(r.left,r.top,n,o)}),(e=>Ve(Math.max(e.pageLeft,r.left),Math.max(e.pageTop,r.top),e.width,e.height)))},Be=(e,t,n)=>Ie(n).map((n=>{const r=e=>t(re(e));return n.addEventListener(e,r),{unbind:()=>n.removeEventListener(e,r)}})).getOrThunk((()=>({unbind:g})));var _e=tinymce.util.Tools.resolve("tinymce.dom.DOMUtils"),ze=tinymce.util.Tools.resolve("tinymce.Env");const $e=(e,t)=>{e.dispatch("FullscreenStateChanged",{state:t}),e.dispatch("ResizeEditor")},Ue=("fullscreen_native",e=>e.options.get("fullscreen_native"));const Ke=e=>{return e.dom===(void 0!==(t=z(e).dom).fullscreenElement?t.fullscreenElement:void 0!==t.msFullscreenElement?t.msFullscreenElement:void 0!==t.webkitFullscreenElement?t.webkitFullscreenElement:null);var t},Xe=(e,t,n)=>((e,t,n)=>E(((e,t)=>{const n=m(t)?t:v;let r=e.dom;const o=[];for(;null!==r.parentNode&&void 0!==r.parentNode;){const e=r.parentNode,t=B(e);if(o.push(t),!0===n(t))break;r=e}return o})(e,n),t))(e,(e=>_(e,t)),n),Ye=(e,t)=>((e,n)=>{return E((e=>b.from(e.dom.parentNode).map(B))(r=e).map($).map((e=>E(e,(e=>{return t=e,!(r.dom===t.dom);var t})))).getOr([]),(e=>_(e,t)));var r})(e),Ge="data-ephox-mobile-fullscreen-style",Je="position:absolute!important;",Qe="top:0!important;left:0!important;margin:0!important;padding:0!important;width:100%!important;height:100%!important;overflow:visible!important;",Ze=ze.os.isAndroid(),et=(e,t,n)=>{const r=t=>n=>{const r=W(n,"style"),o=void 0===r?"no-styles":r.trim();o!==t&&(((e,t,n)=>{((e,t,n)=>{if(!(s(n)||c(n)||h(n)))throw console.error("Invalid call to Attribute.set. Key ",t,":: Value ",n,":: Element ",e),new Error("Attribute value was not simple");e.setAttribute(t,n+"")})(e.dom,t,n)})(n,Ge,o),ee(n,e.parseStyle(t)))},o=Xe(t,"*"),i=(e=>{const t=[];for(let n=0,r=e.length;nYe(e,"*:not(.tox-silver-sink)")))),a=(e=>{const t=te(e,"background-color");return void 0!==t&&""!==t?"background-color:"+t+"!important":"background-color:rgb(255,255,255)!important;"})(n);x(i,r("display:none!important;")),x(o,r(Je+Qe+a)),r((!0===Ze?"":Je)+Qe+a)(t)},tt=_e.DOM,nt=Ie().fold((()=>({bind:g,unbind:g})),(e=>{const t=(()=>{const e=O(g);return{...e,on:t=>e.get().each(t)}})(),n=k(),r=k(),o=((e,t)=>{let n=null;return{cancel:()=>{a(n)||(clearTimeout(n),n=null)},throttle:(...t)=>{a(n)&&(n=setTimeout((()=>{n=null,e.apply(null,t)}),50))}}})((()=>{document.body.scrollTop=0,document.documentElement.scrollTop=0,window.requestAnimationFrame((()=>{t.on((t=>ee(t,{top:e.offsetTop+"px",left:e.offsetLeft+"px",height:e.height+"px",width:e.width+"px"})))}))}));return{bind:e=>{t.set(e),o.throttle(),n.set(Be("resize",o.throttle)),r.set(Be("scroll",o.throttle))},unbind:()=>{t.on((()=>{n.clear(),r.clear()})),t.clear()}}})),rt=(e,t)=>{const n=document.body,r=document.documentElement,o=e.getContainer(),s=B(o),i=(l=s,b.from(l.dom.nextSibling).map(B)).filter((e=>(e=>P(e)&&L(e.dom))(e)&&((e,t)=>(e=>void 0!==e.dom.classList)(e)&&e.dom.classList.contains("tox-silver-sink"))(e)));var l;const a=(e=>{const t=B(e.getElement());return Y(t).map(G).getOrThunk((()=>(e=>{const t=e.dom.body;if(null==t)throw new Error("Body is not available yet");return B(t)})(z(t))))})(e),c=t.get(),u=B(e.getBody()),d=ze.deviceType.isTouch(),m=o.style,h=e.iframeElement,g=null==h?void 0:h.style,p=e=>{e(n,"tox-fullscreen"),e(r,"tox-fullscreen"),e(o,"tox-fullscreen"),Y(s).map((e=>G(e).dom)).each((t=>{e(t,"tox-fullscreen"),e(t,"tox-shadowhost")}))},f=()=>{d&&(e=>{const t=((e,t)=>{const n=document;return 1!==(r=n).nodeType&&9!==r.nodeType&&11!==r.nodeType||0===r.childElementCount?[]:S(n.querySelectorAll(e),B);var r})("["+Ge+"]");x(t,(t=>{const n=W(t,Ge);n&&"no-styles"!==n?ee(t,e.parseStyle(n)):q(t,"style"),q(t,Ge)}))})(e.dom),p(tt.removeClass),nt.unbind(),b.from(t.get()).each((e=>e.fullscreenChangeHandler.unbind()))};if(c)c.fullscreenChangeHandler.unbind(),Ue(e)&&Ke(a)&&(e=>{const t=e.dom;t.exitFullscreen?t.exitFullscreen():t.msExitFullscreen?t.msExitFullscreen():t.webkitCancelFullScreen&&t.webkitCancelFullScreen()})(z(a)),g.width=c.iframeWidth,g.height=c.iframeHeight,m.width=c.containerWidth,m.height=c.containerHeight,m.top=c.containerTop,m.left=c.containerLeft,w=i,y=c.sinkCssPosition,E=(e,t)=>{Z(e,"position",t)},w.isSome()&&y.isSome()?b.some(E(w.getOrDie(),y.getOrDie())):b.none(),f(),v=c.scrollPos,window.scrollTo(v.x,v.y),t.set(null),$e(e,!1),e.off("remove",f);else{const n=ie(z(a),void 0!==document.fullscreenElement?"fullscreenchange":void 0!==document.msFullscreenElement?"MSFullscreenChange":void 0!==document.webkitFullscreenElement?"webkitfullscreenchange":"fullscreenchange",(n=>{Ue(e)&&(Ke(a)||null===t.get()||rt(e,t))})),r={scrollPos:je(window),containerWidth:m.width,containerHeight:m.height,containerTop:m.top,containerLeft:m.left,iframeWidth:g.width,iframeHeight:g.height,fullscreenChangeHandler:n,sinkCssPosition:i.map((e=>te(e,"position")))};d&&et(e.dom,s,u),g.width=g.height="100%",m.width=m.height="",p(tt.addClass),i.each((e=>{Z(e,"position","fixed")})),nt.bind(s),e.on("remove",f),t.set(r),Ue(e)&&(e=>{const t=e.dom;t.requestFullscreen?t.requestFullscreen():t.msRequestFullscreen?t.msRequestFullscreen():t.webkitRequestFullScreen&&t.webkitRequestFullScreen()})(a),$e(e,!0)}var v,w,y,E},ot=(e,t)=>n=>{n.setActive(null!==t.get());const r=e=>n.setActive(e.state);return e.on("FullscreenStateChanged",r),()=>e.off("FullscreenStateChanged",r)};t.add("fullscreen",(t=>{const n=e(null);return t.inline||((e=>{(0,e.options.register)("fullscreen_native",{processor:"boolean",default:!1})})(t),((e,t)=>{e.addCommand("mceFullScreen",(()=>{rt(e,t)}))})(t,n),((e,t)=>{const n=()=>e.execCommand("mceFullScreen");e.ui.registry.addToggleMenuItem("fullscreen",{text:"Fullscreen",icon:"fullscreen",shortcut:"Meta+Shift+F",onAction:n,onSetup:ot(e,t)}),e.ui.registry.addToggleButton("fullscreen",{tooltip:"Fullscreen",icon:"fullscreen",onAction:n,onSetup:ot(e,t)})})(t,n),t.addShortcut("Meta+Shift+F","","mceFullScreen")),(e=>({isFullscreen:()=>null!==e.get()}))(n)}))}(); \ No newline at end of file diff --git a/public/libs/tinymce/plugins/image/plugin.min.js b/public/libs/tinymce/plugins/image/plugin.min.js index 905b47535e0..d8dfd216163 100644 --- a/public/libs/tinymce/plugins/image/plugin.min.js +++ b/public/libs/tinymce/plugins/image/plugin.min.js @@ -1,4 +1,4 @@ /** - * TinyMCE version 6.7.2 (2023-10-25) + * TinyMCE version 6.8.4 (2024-06-19) */ -!function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager");const t=Object.getPrototypeOf,a=(e,t,a)=>{var i;return!!a(e,t.prototype)||(null===(i=e.constructor)||void 0===i?void 0:i.name)===t.name},i=e=>t=>(e=>{const t=typeof e;return null===e?"null":"object"===t&&Array.isArray(e)?"array":"object"===t&&a(e,String,((e,t)=>t.isPrototypeOf(e)))?"string":t})(t)===e,s=e=>t=>typeof t===e,r=i("string"),o=i("object"),n=e=>((e,i)=>o(e)&&a(e,i,((e,a)=>t(e)===a)))(e,Object),l=i("array"),c=(null,e=>null===e);const m=s("boolean"),d=e=>!(e=>null==e)(e),g=s("function"),u=s("number"),p=()=>{};class h{constructor(e,t){this.tag=e,this.value=t}static some(e){return new h(!0,e)}static none(){return h.singletonNone}fold(e,t){return this.tag?t(this.value):e()}isSome(){return this.tag}isNone(){return!this.tag}map(e){return this.tag?h.some(e(this.value)):h.none()}bind(e){return this.tag?e(this.value):h.none()}exists(e){return this.tag&&e(this.value)}forall(e){return!this.tag||e(this.value)}filter(e){return!this.tag||e(this.value)?this:h.none()}getOr(e){return this.tag?this.value:e}or(e){return this.tag?this:e}getOrThunk(e){return this.tag?this.value:e()}orThunk(e){return this.tag?this:e()}getOrDie(e){if(this.tag)return this.value;throw new Error(null!=e?e:"Called getOrDie on None")}static from(e){return d(e)?h.some(e):h.none()}getOrNull(){return this.tag?this.value:null}getOrUndefined(){return this.value}each(e){this.tag&&e(this.value)}toArray(){return this.tag?[this.value]:[]}toString(){return this.tag?`some(${this.value})`:"none()"}}h.singletonNone=new h(!1);const b=Object.keys,v=Object.hasOwnProperty,y=(e,t)=>v.call(e,t),f=Array.prototype.push,w=e=>{const t=[];for(let a=0,i=e.length;a{((e,t,a)=>{if(!(r(a)||m(a)||u(a)))throw console.error("Invalid call to Attribute.set. Key ",t,":: Value ",a,":: Element ",e),new Error("Attribute value was not simple");e.setAttribute(t,a+"")})(e.dom,t,a)},D=e=>{if(null==e)throw new Error("Node cannot be null or undefined");return{dom:e}},_=D;var C=tinymce.util.Tools.resolve("tinymce.dom.DOMUtils"),I=tinymce.util.Tools.resolve("tinymce.util.URI");const U=e=>e.length>0,S=e=>t=>t.options.get(e),x=S("image_dimensions"),N=S("image_advtab"),T=S("image_uploadtab"),O=S("image_prepend_url"),E=S("image_class_list"),L=S("image_description"),j=S("image_title"),M=S("image_caption"),R=S("image_list"),k=S("a11y_advanced_options"),z=S("automatic_uploads"),P=(e,t)=>Math.max(parseInt(e,10),parseInt(t,10)),B=e=>(e&&(e=e.replace(/px$/,"")),e),F=e=>(e.length>0&&/^[0-9]+$/.test(e)&&(e+="px"),e),H=e=>"IMG"===e.nodeName&&(e.hasAttribute("data-mce-object")||e.hasAttribute("data-mce-placeholder")),G=(e,t)=>{const a=e.options.get;return I.isDomSafe(t,"img",{allow_html_data_urls:a("allow_html_data_urls"),allow_script_urls:a("allow_script_urls"),allow_svg_data_urls:a("allow_svg_data_urls")})},W=C.DOM,$=e=>e.style.marginLeft&&e.style.marginRight&&e.style.marginLeft===e.style.marginRight?B(e.style.marginLeft):"",V=e=>e.style.marginTop&&e.style.marginBottom&&e.style.marginTop===e.style.marginBottom?B(e.style.marginTop):"",K=e=>e.style.borderWidth?B(e.style.borderWidth):"",Z=(e,t)=>{var a;return e.hasAttribute(t)&&null!==(a=e.getAttribute(t))&&void 0!==a?a:""},q=e=>null!==e.parentNode&&"FIGURE"===e.parentNode.nodeName,J=(e,t,a)=>{""===a||null===a?e.removeAttribute(t):e.setAttribute(t,a)},Q=(e,t)=>{const a=e.getAttribute("style"),i=t(null!==a?a:"");i.length>0?(e.setAttribute("style",i),e.setAttribute("data-mce-style",i)):e.removeAttribute("style")},X=(e,t)=>(e,a,i)=>{const s=e.style;s[a]?(s[a]=F(i),Q(e,t)):J(e,a,i)},Y=(e,t)=>e.style[t]?B(e.style[t]):Z(e,t),ee=(e,t)=>{const a=F(t);e.style.marginLeft=a,e.style.marginRight=a},te=(e,t)=>{const a=F(t);e.style.marginTop=a,e.style.marginBottom=a},ae=(e,t)=>{const a=F(t);e.style.borderWidth=a},ie=(e,t)=>{e.style.borderStyle=t},se=e=>{var t;return null!==(t=e.style.borderStyle)&&void 0!==t?t:""},re=e=>d(e)&&"FIGURE"===e.nodeName,oe=e=>0===W.getAttrib(e,"alt").length&&"presentation"===W.getAttrib(e,"role"),ne=e=>oe(e)?"":Z(e,"alt"),le=(e,t)=>{var a;const i=document.createElement("img");return J(i,"style",t.style),($(i)||""!==t.hspace)&&ee(i,t.hspace),(V(i)||""!==t.vspace)&&te(i,t.vspace),(K(i)||""!==t.border)&&ae(i,t.border),(se(i)||""!==t.borderStyle)&&ie(i,t.borderStyle),e(null!==(a=i.getAttribute("style"))&&void 0!==a?a:"")},ce=(e,t)=>({src:Z(t,"src"),alt:ne(t),title:Z(t,"title"),width:Y(t,"width"),height:Y(t,"height"),class:Z(t,"class"),style:e(Z(t,"style")),caption:q(t),hspace:$(t),vspace:V(t),border:K(t),borderStyle:se(t),isDecorative:oe(t)}),me=(e,t,a,i,s)=>{a[i]!==t[i]&&s(e,i,String(a[i]))},de=(e,t,a)=>{if(a){W.setAttrib(e,"role","presentation");const t=_(e);A(t,"alt","")}else{if(c(t)){"alt",_(e).dom.removeAttribute("alt")}else{const a=_(e);A(a,"alt",t)}"presentation"===W.getAttrib(e,"role")&&W.setAttrib(e,"role","")}},ge=(e,t)=>(a,i,s)=>{e(a,s),Q(a,t)},ue=(e,t,a)=>{const i=ce(e,a);me(a,i,t,"caption",((e,t,a)=>(e=>{q(e)?(e=>{const t=e.parentNode;d(t)&&(W.insertAfter(e,t),W.remove(t))})(e):(e=>{const t=W.create("figure",{class:"image"});W.insertAfter(t,e),t.appendChild(e),t.appendChild(W.create("figcaption",{contentEditable:"true"},"Caption")),t.contentEditable="false"})(e)})(e))),me(a,i,t,"src",J),me(a,i,t,"title",J),me(a,i,t,"width",X(0,e)),me(a,i,t,"height",X(0,e)),me(a,i,t,"class",J),me(a,i,t,"style",ge(((e,t)=>J(e,"style",t)),e)),me(a,i,t,"hspace",ge(ee,e)),me(a,i,t,"vspace",ge(te,e)),me(a,i,t,"border",ge(ae,e)),me(a,i,t,"borderStyle",ge(ie,e)),((e,t,a)=>{a.alt===t.alt&&a.isDecorative===t.isDecorative||de(e,a.alt,a.isDecorative)})(a,i,t)},pe=(e,t)=>{const a=(e=>{if(e.margin){const t=String(e.margin).split(" ");switch(t.length){case 1:e["margin-top"]=e["margin-top"]||t[0],e["margin-right"]=e["margin-right"]||t[0],e["margin-bottom"]=e["margin-bottom"]||t[0],e["margin-left"]=e["margin-left"]||t[0];break;case 2:e["margin-top"]=e["margin-top"]||t[0],e["margin-right"]=e["margin-right"]||t[1],e["margin-bottom"]=e["margin-bottom"]||t[0],e["margin-left"]=e["margin-left"]||t[1];break;case 3:e["margin-top"]=e["margin-top"]||t[0],e["margin-right"]=e["margin-right"]||t[1],e["margin-bottom"]=e["margin-bottom"]||t[2],e["margin-left"]=e["margin-left"]||t[1];break;case 4:e["margin-top"]=e["margin-top"]||t[0],e["margin-right"]=e["margin-right"]||t[1],e["margin-bottom"]=e["margin-bottom"]||t[2],e["margin-left"]=e["margin-left"]||t[3]}delete e.margin}return e})(e.dom.styles.parse(t)),i=e.dom.styles.parse(e.dom.styles.serialize(a));return e.dom.styles.serialize(i)},he=e=>{const t=e.selection.getNode(),a=e.dom.getParent(t,"figure.image");return a?e.dom.select("img",a)[0]:t&&("IMG"!==t.nodeName||H(t))?null:t},be=(e,t)=>{var a;const i=e.dom,s=((t,a)=>{const i={};var s;return((e,t,a,i)=>{((e,t)=>{const a=b(e);for(let i=0,s=a.length;i{(t(e,s)?a:i)(e,s)}))})(t,((t,a)=>!e.schema.isValidChild(a,"figure")),(s=i,(e,t)=>{s[t]=e}),p),i})(e.schema.getTextBlockElements()),r=i.getParent(t.parentNode,(e=>{return t=s,a=e.nodeName,y(t,a)&&void 0!==t[a]&&null!==t[a];var t,a}),e.getBody());return r&&null!==(a=i.split(r,t))&&void 0!==a?a:t},ve=(e,t)=>{const a=((t,a)=>{const i=document.createElement("img");if(ue((t=>pe(e,t)),{...a,caption:!1},i),de(i,a.alt,a.isDecorative),a.caption){const e=W.create("figure",{class:"image"});return e.appendChild(i),e.appendChild(W.create("figcaption",{contentEditable:"true"},"Caption")),e.contentEditable="false",e}return i})(0,t);e.dom.setAttrib(a,"data-mce-id","__mcenew"),e.focus(),e.selection.setContent(a.outerHTML);const i=e.dom.select('*[data-mce-id="__mcenew"]')[0];if(e.dom.setAttrib(i,"data-mce-id",null),re(i)){const t=be(e,i);e.selection.select(t)}else e.selection.select(i)},ye=(e,t)=>{const a=he(e);if(a){const i={...ce((t=>pe(e,t)),a),...t},s=((e,t)=>{const a=t.src;return{...t,src:G(e,a)?a:""}})(e,i);i.src?((e,t)=>{const a=he(e);if(a)if(ue((t=>pe(e,t)),t,a),((e,t)=>{e.dom.setAttrib(t,"src",t.getAttribute("src"))})(e,a),re(a.parentNode)){const t=a.parentNode;be(e,t),e.selection.select(a.parentNode)}else e.selection.select(a),((e,t,a)=>{const i=()=>{a.onload=a.onerror=null,e.selection&&(e.selection.select(a),e.nodeChanged())};a.onload=()=>{t.width||t.height||!x(e)||e.dom.setAttribs(a,{width:String(a.clientWidth),height:String(a.clientHeight)}),i()},a.onerror=i})(e,t,a)})(e,s):((e,t)=>{if(t){const a=e.dom.is(t.parentNode,"figure.image")?t.parentNode:t;e.dom.remove(a),e.focus(),e.nodeChanged(),e.dom.isEmpty(e.getBody())&&(e.setContent(""),e.selection.setCursorLocation())}})(e,a)}else t.src&&ve(e,{src:"",alt:"",title:"",width:"",height:"",class:"",style:"",caption:!1,hspace:"",vspace:"",border:"",borderStyle:"",isDecorative:!1,...t})},fe=(we=(e,t)=>n(e)&&n(t)?fe(e,t):t,(...e)=>{if(0===e.length)throw new Error("Can't merge zero objects");const t={};for(let a=0;ar(e.value)?e.value:"",Ce=(e,t)=>{const a=[];return De.each(e,(e=>{const i=(e=>r(e.text)?e.text:r(e.title)?e.title:"")(e);if(void 0!==e.menu){const s=Ce(e.menu,t);a.push({text:i,items:s})}else{const s=t(e);a.push({text:i,value:s})}})),a},Ie=(e=_e)=>t=>t?h.from(t).map((t=>Ce(t,e))):h.none(),Ue=(e,t)=>((e,a)=>{for(let a=0;ay(e,"items"))(i=e[a])?Ue(i.items,t):i.value===t?h.some(i):h.none();if(s.isSome())return s}var i;return h.none()})(e),Se=Ie,xe=(e,t)=>e.bind((e=>Ue(e,t))),Ne=e=>{const t=Se((t=>e.convertURL(t.value||t.url||"","src"))),a=new Promise((a=>{((e,t)=>{const a=R(e);r(a)?fetch(a).then((e=>{e.ok&&e.json().then(t)})):g(a)?a(t):t(a)})(e,(e=>{a(t(e).map((e=>w([[{text:"None",value:""}],e]))))}))})),i=(A=E(e),Ie(_e)(A)),s=N(e),o=T(e),n=(e=>U(e.options.get("images_upload_url")))(e),l=(e=>d(e.options.get("images_upload_handler")))(e),c=(e=>{const t=he(e);return t?ce((t=>pe(e,t)),t):{src:"",alt:"",title:"",width:"",height:"",class:"",style:"",caption:!1,hspace:"",vspace:"",border:"",borderStyle:"",isDecorative:!1}})(e),m=L(e),u=j(e),p=x(e),b=M(e),v=k(e),y=z(e),f=h.some(O(e)).filter((e=>r(e)&&e.length>0));var A;return a.then((e=>({image:c,imageList:e,classList:i,hasAdvTab:s,hasUploadTab:o,hasUploadUrl:n,hasUploadHandler:l,hasDescription:m,hasImageTitle:u,hasDimensions:p,hasImageCaption:b,prependURL:f,hasAccessibilityOptions:v,automaticUploads:y})))},Te=e=>{const t=e.imageList.map((e=>({name:"images",type:"listbox",label:"Image list",items:e}))),a={name:"alt",type:"input",label:"Alternative description",enabled:!(e.hasAccessibilityOptions&&e.image.isDecorative)},i=e.classList.map((e=>({name:"classes",type:"listbox",label:"Class",items:e})));return w([[{name:"src",type:"urlinput",filetype:"image",label:"Source"}],t.toArray(),e.hasAccessibilityOptions&&e.hasDescription?[{type:"label",label:"Accessibility",items:[{name:"isDecorative",type:"checkbox",label:"Image is decorative"}]}]:[],e.hasDescription?[a]:[],e.hasImageTitle?[{name:"title",type:"input",label:"Image title"}]:[],e.hasDimensions?[{name:"dimensions",type:"sizeinput"}]:[],[{...(s=e.classList.isSome()&&e.hasImageCaption,s?{type:"grid",columns:2}:{type:"panel"}),items:w([i.toArray(),e.hasImageCaption?[{type:"label",label:"Caption",items:[{type:"checkbox",name:"caption",label:"Show caption"}]}]:[]])}]]);var s},Oe=e=>({title:"General",name:"general",items:Te(e)}),Ee=Te,Le=e=>({src:{value:e.src,meta:{}},images:e.src,alt:e.alt,title:e.title,dimensions:{width:e.width,height:e.height},classes:e.class,caption:e.caption,style:e.style,vspace:e.vspace,border:e.border,hspace:e.hspace,borderstyle:e.borderStyle,fileinput:[],isDecorative:e.isDecorative}),je=(e,t)=>({src:e.src.value,alt:null!==e.alt&&0!==e.alt.length||!t?e.alt:null,title:e.title,width:e.dimensions.width,height:e.dimensions.height,class:e.classes,style:e.style,caption:e.caption,hspace:e.hspace,vspace:e.vspace,border:e.border,borderStyle:e.borderstyle,isDecorative:e.isDecorative}),Me=(e,t,a,i)=>{((e,t)=>{const a=t.getData();((e,t)=>/^(?:[a-zA-Z]+:)?\/\//.test(t)?h.none():e.prependURL.bind((e=>t.substring(0,e.length)!==e?h.some(e+t):h.none())))(e,a.src.value).each((e=>{t.setData({src:{value:e,meta:a.src.meta}})}))})(t,i),((e,t)=>{const a=t.getData(),i=a.src.meta;if(void 0!==i){const s=fe({},a);((e,t,a)=>{e.hasDescription&&r(a.alt)&&(t.alt=a.alt),e.hasAccessibilityOptions&&(t.isDecorative=a.isDecorative||t.isDecorative||!1),e.hasImageTitle&&r(a.title)&&(t.title=a.title),e.hasDimensions&&(r(a.width)&&(t.dimensions.width=a.width),r(a.height)&&(t.dimensions.height=a.height)),r(a.class)&&xe(e.classList,a.class).each((e=>{t.classes=e.value})),e.hasImageCaption&&m(a.caption)&&(t.caption=a.caption),e.hasAdvTab&&(r(a.style)&&(t.style=a.style),r(a.vspace)&&(t.vspace=a.vspace),r(a.border)&&(t.border=a.border),r(a.hspace)&&(t.hspace=a.hspace),r(a.borderstyle)&&(t.borderstyle=a.borderstyle))})(e,s,i),t.setData(s)}})(t,i),((e,t,a,i)=>{const s=i.getData(),r=s.src.value,o=s.src.meta||{};o.width||o.height||!t.hasDimensions||(U(r)?e.imageSize(r).then((e=>{a.open&&i.setData({dimensions:e})})).catch((e=>console.error(e))):i.setData({dimensions:{width:"",height:""}}))})(e,t,a,i),((e,t,a)=>{const i=a.getData(),s=xe(e.imageList,i.src.value);t.prevImage=s,a.setData({images:s.map((e=>e.value)).getOr("")})})(t,a,i)},Re=(e,t,a,i)=>{const s=i.getData();var r;i.block("Uploading image"),(r=s.fileinput,((e,t)=>0{i.unblock()}),(s=>{const r=URL.createObjectURL(s),o=()=>{i.unblock(),URL.revokeObjectURL(r)},n=s=>{i.setData({src:{value:s,meta:{}}}),i.showTab("general"),Me(e,t,a,i)};var l;(l=s,new Promise(((e,t)=>{const a=new FileReader;a.onload=()=>{e(a.result)},a.onerror=()=>{var e;t(null===(e=a.error)||void 0===e?void 0:e.message)},a.readAsDataURL(l)}))).then((a=>{const l=e.createBlobCache(s,r,a);t.automaticUploads?e.uploadImage(l).then((e=>{n(e.url),o()})).catch((t=>{o(),e.alertErr(t)})):(e.addToBlobCache(l),n(l.blobUri()),i.unblock())}))}))},ke=(e,t,a)=>(i,s)=>{"src"===s.name?Me(e,t,a,i):"images"===s.name?((e,t,a,i)=>{const s=i.getData(),r=xe(t.imageList,s.images);r.each((e=>{const t=""===s.alt||a.prevImage.map((e=>e.text===s.alt)).getOr(!1);t?""===e.value?i.setData({src:e,alt:a.prevAlt}):i.setData({src:e,alt:e.text}):i.setData({src:e})})),a.prevImage=r,Me(e,t,a,i)})(e,t,a,i):"alt"===s.name?a.prevAlt=i.getData().alt:"fileinput"===s.name?Re(e,t,a,i):"isDecorative"===s.name&&i.setEnabled("alt",!i.getData().isDecorative)},ze=e=>()=>{e.open=!1},Pe=e=>e.hasAdvTab||e.hasUploadUrl||e.hasUploadHandler?{type:"tabpanel",tabs:w([[Oe(e)],e.hasAdvTab?[{title:"Advanced",name:"advanced",items:[{type:"grid",columns:2,items:[{type:"input",label:"Vertical space",name:"vspace",inputMode:"numeric"},{type:"input",label:"Horizontal space",name:"hspace",inputMode:"numeric"},{type:"input",label:"Border width",name:"border",inputMode:"numeric"},{type:"listbox",name:"borderstyle",label:"Border style",items:[{text:"Select...",value:""},{text:"Solid",value:"solid"},{text:"Dotted",value:"dotted"},{text:"Dashed",value:"dashed"},{text:"Double",value:"double"},{text:"Groove",value:"groove"},{text:"Ridge",value:"ridge"},{text:"Inset",value:"inset"},{text:"Outset",value:"outset"},{text:"None",value:"none"},{text:"Hidden",value:"hidden"}]}]}]}]:[],e.hasUploadTab&&(e.hasUploadUrl||e.hasUploadHandler)?[{title:"Upload",name:"upload",items:[{type:"dropzone",name:"fileinput"}]}]:[]])}:{type:"panel",items:Ee(e)},Be=(e,t,a)=>i=>{const s=fe(Le(t.image),i.getData()),r={...s,style:le(a.normalizeCss,je(s,!1))};e.execCommand("mceUpdateImage",!1,je(r,t.hasAccessibilityOptions)),e.editorUpload.uploadImagesAuto(),i.close()},Fe=e=>t=>G(e,t)?(e=>new Promise((t=>{const a=document.createElement("img"),i=e=>{a.onload=a.onerror=null,a.parentNode&&a.parentNode.removeChild(a),t(e)};a.onload=()=>{const e={width:P(a.width,a.clientWidth),height:P(a.height,a.clientHeight)};i(Promise.resolve(e))},a.onerror=()=>{i(Promise.reject(`Failed to get image dimensions for: ${e}`))};const s=a.style;s.visibility="hidden",s.position="fixed",s.bottom=s.left="0px",s.width=s.height="auto",document.body.appendChild(a),a.src=e})))(e.documentBaseURI.toAbsolute(t)).then((e=>({width:String(e.width),height:String(e.height)}))):Promise.resolve({width:"",height:""}),He=e=>(t,a,i)=>{var s;return e.editorUpload.blobCache.create({blob:t,blobUri:a,name:null===(s=t.name)||void 0===s?void 0:s.replace(/\.[^\.]+$/,""),filename:t.name,base64:i.split(",")[1]})},Ge=e=>t=>{e.editorUpload.blobCache.add(t)},We=e=>t=>{e.windowManager.alert(t)},$e=e=>t=>pe(e,t),Ve=e=>t=>e.dom.parseStyle(t),Ke=e=>(t,a)=>e.dom.serializeStyle(t,a),Ze=e=>t=>Ae(e).upload([t],!1).then((e=>{var t;return 0===e.length?Promise.reject("Failed to upload image"):!1===e[0].status?Promise.reject(null===(t=e[0].error)||void 0===t?void 0:t.message):e[0]})),qe=e=>{const t={imageSize:Fe(e),addToBlobCache:Ge(e),createBlobCache:He(e),alertErr:We(e),normalizeCss:$e(e),parseStyle:Ve(e),serializeStyle:Ke(e),uploadImage:Ze(e)};return{open:()=>{Ne(e).then((a=>{const i=(e=>({prevImage:xe(e.imageList,e.image.src),prevAlt:e.image.alt,open:!0}))(a);return{title:"Insert/Edit Image",size:"normal",body:Pe(a),buttons:[{type:"cancel",name:"cancel",text:"Cancel"},{type:"submit",name:"save",text:"Save",primary:!0}],initialData:Le(a.image),onSubmit:Be(e,a,t),onChange:ke(t,a,i),onClose:ze(i)}})).then(e.windowManager.open)}}},Je=e=>{const t=e.attr("class");return d(t)&&/\bimage\b/.test(t)},Qe=e=>t=>{let a=t.length;const i=t=>{t.attr("contenteditable",e?"true":null)};for(;a--;){const s=t[a];Je(s)&&(s.attr("contenteditable",e?"false":null),De.each(s.getAll("figcaption"),i))}},Xe=e=>t=>{const a=()=>{t.setEnabled(e.selection.isEditable())};return e.on("NodeChange",a),a(),()=>{e.off("NodeChange",a)}};e.add("image",(e=>{(e=>{const t=e.options.register;t("image_dimensions",{processor:"boolean",default:!0}),t("image_advtab",{processor:"boolean",default:!1}),t("image_uploadtab",{processor:"boolean",default:!0}),t("image_prepend_url",{processor:"string",default:""}),t("image_class_list",{processor:"object[]"}),t("image_description",{processor:"boolean",default:!0}),t("image_title",{processor:"boolean",default:!1}),t("image_caption",{processor:"boolean",default:!1}),t("image_list",{processor:e=>{const t=!1===e||r(e)||((e,t)=>{if(l(e)){for(let a=0,i=e.length;a{e.on("PreInit",(()=>{e.parser.addNodeFilter("figure",Qe(!0)),e.serializer.addNodeFilter("figure",Qe(!1))}))})(e),(e=>{e.ui.registry.addToggleButton("image",{icon:"image",tooltip:"Insert/edit image",onAction:qe(e).open,onSetup:t=>{t.setActive(d(he(e)));const a=e.selection.selectorChangedWithUnbind("img:not([data-mce-object]):not([data-mce-placeholder]),figure.image",t.setActive).unbind,i=Xe(e)(t);return()=>{a(),i()}}}),e.ui.registry.addMenuItem("image",{icon:"image",text:"Image...",onAction:qe(e).open,onSetup:Xe(e)}),e.ui.registry.addContextMenu("image",{update:t=>e.selection.isEditable()&&(re(t)||"IMG"===t.nodeName&&!H(t))?["image"]:[]})})(e),(e=>{e.addCommand("mceImage",qe(e).open),e.addCommand("mceUpdateImage",((t,a)=>{e.undoManager.transact((()=>ye(e,a)))}))})(e)}))}(); \ No newline at end of file +!function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager");const t=Object.getPrototypeOf,a=(e,t,a)=>{var i;return!!a(e,t.prototype)||(null===(i=e.constructor)||void 0===i?void 0:i.name)===t.name},i=e=>t=>(e=>{const t=typeof e;return null===e?"null":"object"===t&&Array.isArray(e)?"array":"object"===t&&a(e,String,((e,t)=>t.isPrototypeOf(e)))?"string":t})(t)===e,s=e=>t=>typeof t===e,r=i("string"),o=i("object"),n=e=>((e,i)=>o(e)&&a(e,i,((e,a)=>t(e)===a)))(e,Object),l=i("array"),c=(null,e=>null===e);const m=s("boolean"),d=e=>!(e=>null==e)(e),g=s("function"),u=s("number"),p=()=>{};class h{constructor(e,t){this.tag=e,this.value=t}static some(e){return new h(!0,e)}static none(){return h.singletonNone}fold(e,t){return this.tag?t(this.value):e()}isSome(){return this.tag}isNone(){return!this.tag}map(e){return this.tag?h.some(e(this.value)):h.none()}bind(e){return this.tag?e(this.value):h.none()}exists(e){return this.tag&&e(this.value)}forall(e){return!this.tag||e(this.value)}filter(e){return!this.tag||e(this.value)?this:h.none()}getOr(e){return this.tag?this.value:e}or(e){return this.tag?this:e}getOrThunk(e){return this.tag?this.value:e()}orThunk(e){return this.tag?this:e()}getOrDie(e){if(this.tag)return this.value;throw new Error(null!=e?e:"Called getOrDie on None")}static from(e){return d(e)?h.some(e):h.none()}getOrNull(){return this.tag?this.value:null}getOrUndefined(){return this.value}each(e){this.tag&&e(this.value)}toArray(){return this.tag?[this.value]:[]}toString(){return this.tag?`some(${this.value})`:"none()"}}h.singletonNone=new h(!1);const b=Object.keys,v=Object.hasOwnProperty,y=(e,t)=>v.call(e,t),f=Array.prototype.push,w=e=>{const t=[];for(let a=0,i=e.length;a{((e,t,a)=>{if(!(r(a)||m(a)||u(a)))throw console.error("Invalid call to Attribute.set. Key ",t,":: Value ",a,":: Element ",e),new Error("Attribute value was not simple");e.setAttribute(t,a+"")})(e.dom,t,a)},D=e=>{if(null==e)throw new Error("Node cannot be null or undefined");return{dom:e}},_=D;var C=tinymce.util.Tools.resolve("tinymce.dom.DOMUtils"),I=tinymce.util.Tools.resolve("tinymce.util.URI");const U=e=>e.length>0,x=e=>t=>t.options.get(e),S=x("image_dimensions"),N=x("image_advtab"),T=x("image_uploadtab"),O=x("image_prepend_url"),E=x("image_class_list"),L=x("image_description"),j=x("image_title"),M=x("image_caption"),R=x("image_list"),k=x("a11y_advanced_options"),z=x("automatic_uploads"),B=(e,t)=>Math.max(parseInt(e,10),parseInt(t,10)),P=e=>(e&&(e=e.replace(/px$/,"")),e),F=e=>(e.length>0&&/^[0-9]+$/.test(e)&&(e+="px"),e),H=e=>"IMG"===e.nodeName&&(e.hasAttribute("data-mce-object")||e.hasAttribute("data-mce-placeholder")),G=(e,t)=>{const a=e.options.get;return I.isDomSafe(t,"img",{allow_html_data_urls:a("allow_html_data_urls"),allow_script_urls:a("allow_script_urls"),allow_svg_data_urls:a("allow_svg_data_urls")})},W=C.DOM,$=e=>e.style.marginLeft&&e.style.marginRight&&e.style.marginLeft===e.style.marginRight?P(e.style.marginLeft):"",V=e=>e.style.marginTop&&e.style.marginBottom&&e.style.marginTop===e.style.marginBottom?P(e.style.marginTop):"",K=e=>e.style.borderWidth?P(e.style.borderWidth):"",Z=(e,t)=>{var a;return e.hasAttribute(t)&&null!==(a=e.getAttribute(t))&&void 0!==a?a:""},q=e=>null!==e.parentNode&&"FIGURE"===e.parentNode.nodeName,J=(e,t,a)=>{""===a||null===a?e.removeAttribute(t):e.setAttribute(t,a)},Q=(e,t)=>{const a=e.getAttribute("style"),i=t(null!==a?a:"");i.length>0?(e.setAttribute("style",i),e.setAttribute("data-mce-style",i)):e.removeAttribute("style")},X=(e,t)=>(e,a,i)=>{const s=e.style;s[a]?(s[a]=F(i),Q(e,t)):J(e,a,i)},Y=(e,t)=>e.style[t]?P(e.style[t]):Z(e,t),ee=(e,t)=>{const a=F(t);e.style.marginLeft=a,e.style.marginRight=a},te=(e,t)=>{const a=F(t);e.style.marginTop=a,e.style.marginBottom=a},ae=(e,t)=>{const a=F(t);e.style.borderWidth=a},ie=(e,t)=>{e.style.borderStyle=t},se=e=>{var t;return null!==(t=e.style.borderStyle)&&void 0!==t?t:""},re=e=>d(e)&&"FIGURE"===e.nodeName,oe=e=>0===W.getAttrib(e,"alt").length&&"presentation"===W.getAttrib(e,"role"),ne=e=>oe(e)?"":Z(e,"alt"),le=(e,t)=>{var a;const i=document.createElement("img");return J(i,"style",t.style),($(i)||""!==t.hspace)&&ee(i,t.hspace),(V(i)||""!==t.vspace)&&te(i,t.vspace),(K(i)||""!==t.border)&&ae(i,t.border),(se(i)||""!==t.borderStyle)&&ie(i,t.borderStyle),e(null!==(a=i.getAttribute("style"))&&void 0!==a?a:"")},ce=(e,t)=>({src:Z(t,"src"),alt:ne(t),title:Z(t,"title"),width:Y(t,"width"),height:Y(t,"height"),class:Z(t,"class"),style:e(Z(t,"style")),caption:q(t),hspace:$(t),vspace:V(t),border:K(t),borderStyle:se(t),isDecorative:oe(t)}),me=(e,t,a,i,s)=>{a[i]!==t[i]&&s(e,i,String(a[i]))},de=(e,t,a)=>{if(a){W.setAttrib(e,"role","presentation");const t=_(e);A(t,"alt","")}else{if(c(t)){"alt",_(e).dom.removeAttribute("alt")}else{const a=_(e);A(a,"alt",t)}"presentation"===W.getAttrib(e,"role")&&W.setAttrib(e,"role","")}},ge=(e,t)=>(a,i,s)=>{e(a,s),Q(a,t)},ue=(e,t,a)=>{const i=ce(e,a);me(a,i,t,"caption",((e,t,a)=>(e=>{q(e)?(e=>{const t=e.parentNode;d(t)&&(W.insertAfter(e,t),W.remove(t))})(e):(e=>{const t=W.create("figure",{class:"image"});W.insertAfter(t,e),t.appendChild(e),t.appendChild(W.create("figcaption",{contentEditable:"true"},"Caption")),t.contentEditable="false"})(e)})(e))),me(a,i,t,"src",J),me(a,i,t,"title",J),me(a,i,t,"width",X(0,e)),me(a,i,t,"height",X(0,e)),me(a,i,t,"class",J),me(a,i,t,"style",ge(((e,t)=>J(e,"style",t)),e)),me(a,i,t,"hspace",ge(ee,e)),me(a,i,t,"vspace",ge(te,e)),me(a,i,t,"border",ge(ae,e)),me(a,i,t,"borderStyle",ge(ie,e)),((e,t,a)=>{a.alt===t.alt&&a.isDecorative===t.isDecorative||de(e,a.alt,a.isDecorative)})(a,i,t)},pe=(e,t)=>{const a=(e=>{if(e.margin){const t=String(e.margin).split(" ");switch(t.length){case 1:e["margin-top"]=e["margin-top"]||t[0],e["margin-right"]=e["margin-right"]||t[0],e["margin-bottom"]=e["margin-bottom"]||t[0],e["margin-left"]=e["margin-left"]||t[0];break;case 2:e["margin-top"]=e["margin-top"]||t[0],e["margin-right"]=e["margin-right"]||t[1],e["margin-bottom"]=e["margin-bottom"]||t[0],e["margin-left"]=e["margin-left"]||t[1];break;case 3:e["margin-top"]=e["margin-top"]||t[0],e["margin-right"]=e["margin-right"]||t[1],e["margin-bottom"]=e["margin-bottom"]||t[2],e["margin-left"]=e["margin-left"]||t[1];break;case 4:e["margin-top"]=e["margin-top"]||t[0],e["margin-right"]=e["margin-right"]||t[1],e["margin-bottom"]=e["margin-bottom"]||t[2],e["margin-left"]=e["margin-left"]||t[3]}delete e.margin}return e})(e.dom.styles.parse(t)),i=e.dom.styles.parse(e.dom.styles.serialize(a));return e.dom.styles.serialize(i)},he=e=>{const t=e.selection.getNode(),a=e.dom.getParent(t,"figure.image");return a?e.dom.select("img",a)[0]:t&&("IMG"!==t.nodeName||H(t))?null:t},be=(e,t)=>{var a;const i=e.dom,s=((t,a)=>{const i={};var s;return((e,t,a,i)=>{((e,t)=>{const a=b(e);for(let i=0,s=a.length;i{(t(e,s)?a:i)(e,s)}))})(t,((t,a)=>!e.schema.isValidChild(a,"figure")),(s=i,(e,t)=>{s[t]=e}),p),i})(e.schema.getTextBlockElements()),r=i.getParent(t.parentNode,(e=>{return t=s,a=e.nodeName,y(t,a)&&void 0!==t[a]&&null!==t[a];var t,a}),e.getBody());return r&&null!==(a=i.split(r,t))&&void 0!==a?a:t},ve=(e,t)=>{const a=((t,a)=>{const i=document.createElement("img");if(ue((t=>pe(e,t)),{...a,caption:!1},i),de(i,a.alt,a.isDecorative),a.caption){const e=W.create("figure",{class:"image"});return e.appendChild(i),e.appendChild(W.create("figcaption",{contentEditable:"true"},"Caption")),e.contentEditable="false",e}return i})(0,t);e.dom.setAttrib(a,"data-mce-id","__mcenew"),e.focus(),e.selection.setContent(a.outerHTML);const i=e.dom.select('*[data-mce-id="__mcenew"]')[0];if(e.dom.setAttrib(i,"data-mce-id",null),re(i)){const t=be(e,i);e.selection.select(t)}else e.selection.select(i)},ye=(e,t)=>{const a=he(e);if(a){const i={...ce((t=>pe(e,t)),a),...t},s=((e,t)=>{const a=t.src;return{...t,src:G(e,a)?a:""}})(e,i);i.src?((e,t)=>{const a=he(e);if(a)if(ue((t=>pe(e,t)),t,a),((e,t)=>{e.dom.setAttrib(t,"src",t.getAttribute("src"))})(e,a),re(a.parentNode)){const t=a.parentNode;be(e,t),e.selection.select(a.parentNode)}else e.selection.select(a),((e,t,a)=>{const i=()=>{a.onload=a.onerror=null,e.selection&&(e.selection.select(a),e.nodeChanged())};a.onload=()=>{t.width||t.height||!S(e)||e.dom.setAttribs(a,{width:String(a.clientWidth),height:String(a.clientHeight)}),i()},a.onerror=i})(e,t,a)})(e,s):((e,t)=>{if(t){const a=e.dom.is(t.parentNode,"figure.image")?t.parentNode:t;e.dom.remove(a),e.focus(),e.nodeChanged(),e.dom.isEmpty(e.getBody())&&(e.setContent(""),e.selection.setCursorLocation())}})(e,a)}else t.src&&ve(e,{src:"",alt:"",title:"",width:"",height:"",class:"",style:"",caption:!1,hspace:"",vspace:"",border:"",borderStyle:"",isDecorative:!1,...t})},fe=(we=(e,t)=>n(e)&&n(t)?fe(e,t):t,(...e)=>{if(0===e.length)throw new Error("Can't merge zero objects");const t={};for(let a=0;ar(e.value)?e.value:"",Ce=(e,t)=>{const a=[];return De.each(e,(e=>{const i=(e=>r(e.text)?e.text:r(e.title)?e.title:"")(e);if(void 0!==e.menu){const s=Ce(e.menu,t);a.push({text:i,items:s})}else{const s=t(e);a.push({text:i,value:s})}})),a},Ie=(e=_e)=>t=>t?h.from(t).map((t=>Ce(t,e))):h.none(),Ue=(e,t)=>((e,a)=>{for(let a=0;ay(e,"items"))(i=e[a])?Ue(i.items,t):i.value===t?h.some(i):h.none();if(s.isSome())return s}var i;return h.none()})(e),xe=Ie,Se=(e,t)=>e.bind((e=>Ue(e,t))),Ne=e=>{const t=xe((t=>e.convertURL(t.value||t.url||"","src"))),a=new Promise((a=>{((e,t)=>{const a=R(e);r(a)?fetch(a).then((e=>{e.ok&&e.json().then(t)})):g(a)?a(t):t(a)})(e,(e=>{a(t(e).map((e=>w([[{text:"None",value:""}],e]))))}))})),i=(A=E(e),Ie(_e)(A)),s=N(e),o=T(e),n=(e=>U(e.options.get("images_upload_url")))(e),l=(e=>d(e.options.get("images_upload_handler")))(e),c=(e=>{const t=he(e);return t?ce((t=>pe(e,t)),t):{src:"",alt:"",title:"",width:"",height:"",class:"",style:"",caption:!1,hspace:"",vspace:"",border:"",borderStyle:"",isDecorative:!1}})(e),m=L(e),u=j(e),p=S(e),b=M(e),v=k(e),y=z(e),f=h.some(O(e)).filter((e=>r(e)&&e.length>0));var A;return a.then((e=>({image:c,imageList:e,classList:i,hasAdvTab:s,hasUploadTab:o,hasUploadUrl:n,hasUploadHandler:l,hasDescription:m,hasImageTitle:u,hasDimensions:p,hasImageCaption:b,prependURL:f,hasAccessibilityOptions:v,automaticUploads:y})))},Te=e=>{const t=e.imageList.map((e=>({name:"images",type:"listbox",label:"Image list",items:e}))),a={name:"alt",type:"input",label:"Alternative description",enabled:!(e.hasAccessibilityOptions&&e.image.isDecorative)},i=e.classList.map((e=>({name:"classes",type:"listbox",label:"Class",items:e})));return w([[{name:"src",type:"urlinput",filetype:"image",label:"Source",picker_text:"Browse files"}],t.toArray(),e.hasAccessibilityOptions&&e.hasDescription?[{type:"label",label:"Accessibility",items:[{name:"isDecorative",type:"checkbox",label:"Image is decorative"}]}]:[],e.hasDescription?[a]:[],e.hasImageTitle?[{name:"title",type:"input",label:"Image title"}]:[],e.hasDimensions?[{name:"dimensions",type:"sizeinput"}]:[],[{...(s=e.classList.isSome()&&e.hasImageCaption,s?{type:"grid",columns:2}:{type:"panel"}),items:w([i.toArray(),e.hasImageCaption?[{type:"label",label:"Caption",items:[{type:"checkbox",name:"caption",label:"Show caption"}]}]:[]])}]]);var s},Oe=e=>({title:"General",name:"general",items:Te(e)}),Ee=Te,Le=e=>({src:{value:e.src,meta:{}},images:e.src,alt:e.alt,title:e.title,dimensions:{width:e.width,height:e.height},classes:e.class,caption:e.caption,style:e.style,vspace:e.vspace,border:e.border,hspace:e.hspace,borderstyle:e.borderStyle,fileinput:[],isDecorative:e.isDecorative}),je=(e,t)=>({src:e.src.value,alt:null!==e.alt&&0!==e.alt.length||!t?e.alt:null,title:e.title,width:e.dimensions.width,height:e.dimensions.height,class:e.classes,style:e.style,caption:e.caption,hspace:e.hspace,vspace:e.vspace,border:e.border,borderStyle:e.borderstyle,isDecorative:e.isDecorative}),Me=(e,t,a,i)=>{((e,t)=>{const a=t.getData();((e,t)=>/^(?:[a-zA-Z]+:)?\/\//.test(t)?h.none():e.prependURL.bind((e=>t.substring(0,e.length)!==e?h.some(e+t):h.none())))(e,a.src.value).each((e=>{t.setData({src:{value:e,meta:a.src.meta}})}))})(t,i),((e,t)=>{const a=t.getData(),i=a.src.meta;if(void 0!==i){const s=fe({},a);((e,t,a)=>{e.hasDescription&&r(a.alt)&&(t.alt=a.alt),e.hasAccessibilityOptions&&(t.isDecorative=a.isDecorative||t.isDecorative||!1),e.hasImageTitle&&r(a.title)&&(t.title=a.title),e.hasDimensions&&(r(a.width)&&(t.dimensions.width=a.width),r(a.height)&&(t.dimensions.height=a.height)),r(a.class)&&Se(e.classList,a.class).each((e=>{t.classes=e.value})),e.hasImageCaption&&m(a.caption)&&(t.caption=a.caption),e.hasAdvTab&&(r(a.style)&&(t.style=a.style),r(a.vspace)&&(t.vspace=a.vspace),r(a.border)&&(t.border=a.border),r(a.hspace)&&(t.hspace=a.hspace),r(a.borderstyle)&&(t.borderstyle=a.borderstyle))})(e,s,i),t.setData(s)}})(t,i),((e,t,a,i)=>{const s=i.getData(),r=s.src.value,o=s.src.meta||{};o.width||o.height||!t.hasDimensions||(U(r)?e.imageSize(r).then((e=>{a.open&&i.setData({dimensions:e})})).catch((e=>console.error(e))):i.setData({dimensions:{width:"",height:""}}))})(e,t,a,i),((e,t,a)=>{const i=a.getData(),s=Se(e.imageList,i.src.value);t.prevImage=s,a.setData({images:s.map((e=>e.value)).getOr("")})})(t,a,i)},Re=(e,t,a,i)=>{const s=i.getData();var r;i.block("Uploading image"),(r=s.fileinput,((e,t)=>0{i.unblock()}),(s=>{const r=URL.createObjectURL(s),o=()=>{i.unblock(),URL.revokeObjectURL(r)},n=s=>{i.setData({src:{value:s,meta:{}}}),i.showTab("general"),Me(e,t,a,i)};var l;(l=s,new Promise(((e,t)=>{const a=new FileReader;a.onload=()=>{e(a.result)},a.onerror=()=>{var e;t(null===(e=a.error)||void 0===e?void 0:e.message)},a.readAsDataURL(l)}))).then((a=>{const l=e.createBlobCache(s,r,a);t.automaticUploads?e.uploadImage(l).then((e=>{n(e.url),o()})).catch((t=>{o(),e.alertErr(t)})):(e.addToBlobCache(l),n(l.blobUri()),i.unblock())}))}))},ke=(e,t,a)=>(i,s)=>{"src"===s.name?Me(e,t,a,i):"images"===s.name?((e,t,a,i)=>{const s=i.getData(),r=Se(t.imageList,s.images);r.each((e=>{const t=""===s.alt||a.prevImage.map((e=>e.text===s.alt)).getOr(!1);t?""===e.value?i.setData({src:e,alt:a.prevAlt}):i.setData({src:e,alt:e.text}):i.setData({src:e})})),a.prevImage=r,Me(e,t,a,i)})(e,t,a,i):"alt"===s.name?a.prevAlt=i.getData().alt:"fileinput"===s.name?Re(e,t,a,i):"isDecorative"===s.name&&i.setEnabled("alt",!i.getData().isDecorative)},ze=e=>()=>{e.open=!1},Be=e=>e.hasAdvTab||e.hasUploadUrl||e.hasUploadHandler?{type:"tabpanel",tabs:w([[Oe(e)],e.hasAdvTab?[{title:"Advanced",name:"advanced",items:[{type:"grid",columns:2,items:[{type:"input",label:"Vertical space",name:"vspace",inputMode:"numeric"},{type:"input",label:"Horizontal space",name:"hspace",inputMode:"numeric"},{type:"input",label:"Border width",name:"border",inputMode:"numeric"},{type:"listbox",name:"borderstyle",label:"Border style",items:[{text:"Select...",value:""},{text:"Solid",value:"solid"},{text:"Dotted",value:"dotted"},{text:"Dashed",value:"dashed"},{text:"Double",value:"double"},{text:"Groove",value:"groove"},{text:"Ridge",value:"ridge"},{text:"Inset",value:"inset"},{text:"Outset",value:"outset"},{text:"None",value:"none"},{text:"Hidden",value:"hidden"}]}]}]}]:[],e.hasUploadTab&&(e.hasUploadUrl||e.hasUploadHandler)?[{title:"Upload",name:"upload",items:[{type:"dropzone",name:"fileinput"}]}]:[]])}:{type:"panel",items:Ee(e)},Pe=(e,t,a)=>i=>{const s=fe(Le(t.image),i.getData()),r={...s,style:le(a.normalizeCss,je(s,!1))};e.execCommand("mceUpdateImage",!1,je(r,t.hasAccessibilityOptions)),e.editorUpload.uploadImagesAuto(),i.close()},Fe=e=>t=>G(e,t)?(e=>new Promise((t=>{const a=document.createElement("img"),i=e=>{a.onload=a.onerror=null,a.parentNode&&a.parentNode.removeChild(a),t(e)};a.onload=()=>{const e={width:B(a.width,a.clientWidth),height:B(a.height,a.clientHeight)};i(Promise.resolve(e))},a.onerror=()=>{i(Promise.reject(`Failed to get image dimensions for: ${e}`))};const s=a.style;s.visibility="hidden",s.position="fixed",s.bottom=s.left="0px",s.width=s.height="auto",document.body.appendChild(a),a.src=e})))(e.documentBaseURI.toAbsolute(t)).then((e=>({width:String(e.width),height:String(e.height)}))):Promise.resolve({width:"",height:""}),He=e=>(t,a,i)=>{var s;return e.editorUpload.blobCache.create({blob:t,blobUri:a,name:null===(s=t.name)||void 0===s?void 0:s.replace(/\.[^\.]+$/,""),filename:t.name,base64:i.split(",")[1]})},Ge=e=>t=>{e.editorUpload.blobCache.add(t)},We=e=>t=>{e.windowManager.alert(t)},$e=e=>t=>pe(e,t),Ve=e=>t=>e.dom.parseStyle(t),Ke=e=>(t,a)=>e.dom.serializeStyle(t,a),Ze=e=>t=>Ae(e).upload([t],!1).then((e=>{var t;return 0===e.length?Promise.reject("Failed to upload image"):!1===e[0].status?Promise.reject(null===(t=e[0].error)||void 0===t?void 0:t.message):e[0]})),qe=e=>{const t={imageSize:Fe(e),addToBlobCache:Ge(e),createBlobCache:He(e),alertErr:We(e),normalizeCss:$e(e),parseStyle:Ve(e),serializeStyle:Ke(e),uploadImage:Ze(e)};return{open:()=>{Ne(e).then((a=>{const i=(e=>({prevImage:Se(e.imageList,e.image.src),prevAlt:e.image.alt,open:!0}))(a);return{title:"Insert/Edit Image",size:"normal",body:Be(a),buttons:[{type:"cancel",name:"cancel",text:"Cancel"},{type:"submit",name:"save",text:"Save",primary:!0}],initialData:Le(a.image),onSubmit:Pe(e,a,t),onChange:ke(t,a,i),onClose:ze(i)}})).then(e.windowManager.open)}}},Je=e=>{const t=e.attr("class");return d(t)&&/\bimage\b/.test(t)},Qe=e=>t=>{let a=t.length;const i=t=>{t.attr("contenteditable",e?"true":null)};for(;a--;){const s=t[a];Je(s)&&(s.attr("contenteditable",e?"false":null),De.each(s.getAll("figcaption"),i))}},Xe=e=>t=>{const a=()=>{t.setEnabled(e.selection.isEditable())};return e.on("NodeChange",a),a(),()=>{e.off("NodeChange",a)}};e.add("image",(e=>{(e=>{const t=e.options.register;t("image_dimensions",{processor:"boolean",default:!0}),t("image_advtab",{processor:"boolean",default:!1}),t("image_uploadtab",{processor:"boolean",default:!0}),t("image_prepend_url",{processor:"string",default:""}),t("image_class_list",{processor:"object[]"}),t("image_description",{processor:"boolean",default:!0}),t("image_title",{processor:"boolean",default:!1}),t("image_caption",{processor:"boolean",default:!1}),t("image_list",{processor:e=>{const t=!1===e||r(e)||((e,t)=>{if(l(e)){for(let a=0,i=e.length;a{e.on("PreInit",(()=>{e.parser.addNodeFilter("figure",Qe(!0)),e.serializer.addNodeFilter("figure",Qe(!1))}))})(e),(e=>{e.ui.registry.addToggleButton("image",{icon:"image",tooltip:"Insert/edit image",onAction:qe(e).open,onSetup:t=>{t.setActive(d(he(e)));const a=e.selection.selectorChangedWithUnbind("img:not([data-mce-object]):not([data-mce-placeholder]),figure.image",t.setActive).unbind,i=Xe(e)(t);return()=>{a(),i()}}}),e.ui.registry.addMenuItem("image",{icon:"image",text:"Image...",onAction:qe(e).open,onSetup:Xe(e)}),e.ui.registry.addContextMenu("image",{update:t=>e.selection.isEditable()&&(re(t)||"IMG"===t.nodeName&&!H(t))?["image"]:[]})})(e),(e=>{e.addCommand("mceImage",qe(e).open),e.addCommand("mceUpdateImage",((t,a)=>{e.undoManager.transact((()=>ye(e,a)))}))})(e)}))}(); \ No newline at end of file diff --git a/public/libs/tinymce/plugins/importcss/plugin.min.js b/public/libs/tinymce/plugins/importcss/plugin.min.js index 9afd216c861..4175a248d04 100644 --- a/public/libs/tinymce/plugins/importcss/plugin.min.js +++ b/public/libs/tinymce/plugins/importcss/plugin.min.js @@ -1,4 +1,4 @@ /** - * TinyMCE version 6.7.2 (2023-10-25) + * TinyMCE version 6.8.4 (2024-06-19) */ -!function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager");const t=e=>t=>(e=>{const t=typeof e;return null===e?"null":"object"===t&&Array.isArray(e)?"array":"object"===t&&(s=r=e,(o=String).prototype.isPrototypeOf(s)||(null===(n=r.constructor)||void 0===n?void 0:n.name)===o.name)?"string":t;var s,r,o,n})(t)===e,s=t("string"),r=t("object"),o=t("array"),n=("function",e=>"function"==typeof e);var c=tinymce.util.Tools.resolve("tinymce.dom.DOMUtils"),i=tinymce.util.Tools.resolve("tinymce.EditorManager"),l=tinymce.util.Tools.resolve("tinymce.Env"),a=tinymce.util.Tools.resolve("tinymce.util.Tools");const p=e=>t=>t.options.get(e),u=p("importcss_merge_classes"),m=p("importcss_exclusive"),f=p("importcss_selector_converter"),y=p("importcss_selector_filter"),d=p("importcss_groups"),h=p("importcss_append"),_=p("importcss_file_filter"),g=p("skin"),v=p("skin_url"),b=Array.prototype.push,x=/^\.(?:ephox|tiny-pageembed|mce)(?:[.-]+\w+)+$/,T=e=>s(e)?t=>-1!==t.indexOf(e):e instanceof RegExp?t=>e.test(t):e,S=(e,t)=>{let s={};const r=/^(?:([a-z0-9\-_]+))?(\.[a-z0-9_\-\.]+)$/i.exec(t);if(!r)return;const o=r[1],n=r[2].substr(1).split(".").join(" "),c=a.makeMap("a,img");return r[1]?(s={title:t},e.schema.getTextBlockElements()[o]?s.block=o:e.schema.getBlockElements()[o]||c[o.toLowerCase()]?s.selector=o:s.inline=o):r[2]&&(s={inline:"span",title:t.substr(1),classes:n}),u(e)?s.classes=n:s.attributes={class:n},s},k=(e,t)=>null===t||m(e),w=e=>{e.on("init",(()=>{const t=(()=>{const e=[],t=[],s={};return{addItemToGroup:(e,r)=>{s[e]?s[e].push(r):(t.push(e),s[e]=[r])},addItem:t=>{e.push(t)},toFormats:()=>{return(r=t,n=e=>{const t=s[e];return 0===t.length?[]:[{title:e,items:t}]},(e=>{const t=[];for(let s=0,r=e.length;s{const s=e.length,r=new Array(s);for(let o=0;oa.map(e,(e=>a.extend({},e,{original:e,selectors:{},filter:T(e.filter)}))))(d(e)),u=(t,s)=>{if(((e,t,s,r)=>!(k(e,s)?t in r:t in s.selectors))(e,t,s,r)){((e,t,s,r)=>{k(e,s)?r[t]=!0:s.selectors[t]=!0})(e,t,s,r);const o=((e,t,s,r)=>{let o;const n=f(e);return o=r&&r.selector_converter?r.selector_converter:n||(()=>S(e,s)),o.call(t,s,r)})(e,e.plugins.importcss,t,s);if(o){const t=o.name||c.DOM.uniqueId();return e.formatter.register(t,o),{title:o.title,format:t}}}return null};a.each(((e,t,r)=>{const o=[],n={},c=(t,n)=>{let p,u=t.href;if(u=(e=>{const t=l.cacheSuffix;return s(e)&&(e=e.replace("?"+t,"").replace("&"+t,"")),e})(u),u&&(!r||r(u,n))&&!((e,t)=>{const s=g(e);if(s){const r=v(e),o=r?e.documentBaseURI.toAbsolute(r):i.baseURL+"/skins/ui/"+s,n=i.baseURL+"/skins/content/";return t===o+"/content"+(e.inline?".inline":"")+".min.css"||-1!==t.indexOf(n)}return!1})(e,u)){a.each(t.imports,(e=>{c(e,!0)}));try{p=t.cssRules||t.rules}catch(e){}a.each(p,(e=>{e.styleSheet?c(e.styleSheet,!0):e.selectorText&&a.each(e.selectorText.split(","),(e=>{o.push(a.trim(e))}))}))}};a.each(e.contentCSS,(e=>{n[e]=!0})),r||(r=(e,t)=>t||n[e]);try{a.each(t.styleSheets,(e=>{c(e)}))}catch(e){}return o})(e,e.getDoc(),T(_(e))),(e=>{if(!x.test(e)&&(!n||n(e))){const s=((e,t)=>a.grep(e,(e=>!e.filter||e.filter(t))))(p,e);if(s.length>0)a.each(s,(s=>{const r=u(e,s);r&&t.addItemToGroup(s.title,r)}));else{const s=u(e,null);s&&t.addItem(s)}}}));const m=t.toFormats();e.dispatch("addStyleModifications",{items:m,replace:!h(e)})}))};e.add("importcss",(e=>((e=>{const t=e.options.register,o=e=>s(e)||n(e)||r(e);t("importcss_merge_classes",{processor:"boolean",default:!0}),t("importcss_exclusive",{processor:"boolean",default:!0}),t("importcss_selector_converter",{processor:"function"}),t("importcss_selector_filter",{processor:o}),t("importcss_file_filter",{processor:o}),t("importcss_groups",{processor:"object[]"}),t("importcss_append",{processor:"boolean",default:!1})})(e),w(e),(e=>({convertSelectorToFormat:t=>S(e,t)}))(e))))}(); \ No newline at end of file +!function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager");const t=e=>t=>(e=>{const t=typeof e;return null===e?"null":"object"===t&&Array.isArray(e)?"array":"object"===t&&(s=r=e,(o=String).prototype.isPrototypeOf(s)||(null===(n=r.constructor)||void 0===n?void 0:n.name)===o.name)?"string":t;var s,r,o,n})(t)===e,s=t("string"),r=t("object"),o=t("array"),n=("function",e=>"function"==typeof e);var c=tinymce.util.Tools.resolve("tinymce.dom.DOMUtils"),i=tinymce.util.Tools.resolve("tinymce.EditorManager"),l=tinymce.util.Tools.resolve("tinymce.Env"),a=tinymce.util.Tools.resolve("tinymce.util.Tools");const p=e=>t=>t.options.get(e),u=p("importcss_merge_classes"),m=p("importcss_exclusive"),f=p("importcss_selector_converter"),y=p("importcss_selector_filter"),d=p("importcss_groups"),h=p("importcss_append"),_=p("importcss_file_filter"),g=p("skin"),v=p("skin_url"),b=Array.prototype.push,x=/^\.(?:ephox|tiny-pageembed|mce)(?:[.-]+\w+)+$/,T=e=>s(e)?t=>-1!==t.indexOf(e):e instanceof RegExp?t=>e.test(t):e,S=(e,t)=>{let s={};const r=/^(?:([a-z0-9\-_]+))?(\.[a-z0-9_\-\.]+)$/i.exec(t);if(!r)return;const o=r[1],n=r[2].substr(1).split(".").join(" "),c=a.makeMap("a,img");return r[1]?(s={title:t},e.schema.getTextBlockElements()[o]?s.block=o:e.schema.getBlockElements()[o]||c[o.toLowerCase()]?s.selector=o:s.inline=o):r[2]&&(s={inline:"span",title:t.substr(1),classes:n}),u(e)?s.classes=n:s.attributes={class:n},s},k=(e,t)=>null===t||m(e),w=e=>{e.on("init",(()=>{const t=(()=>{const e=[],t=[],s={};return{addItemToGroup:(e,r)=>{s[e]?s[e].push(r):(t.push(e),s[e]=[r])},addItem:t=>{e.push(t)},toFormats:()=>{return(r=t,n=e=>{const t=s[e];return 0===t.length?[]:[{title:e,items:t}]},(e=>{const t=[];for(let s=0,r=e.length;s{const s=e.length,r=new Array(s);for(let o=0;oa.map(e,(e=>a.extend({},e,{original:e,selectors:{},filter:T(e.filter)}))))(d(e)),u=(t,s)=>{if(((e,t,s,r)=>!(k(e,s)?t in r:t in s.selectors))(e,t,s,r)){((e,t,s,r)=>{k(e,s)?r[t]=!0:s.selectors[t]=!0})(e,t,s,r);const o=((e,t,s,r)=>{let o;const n=f(e);return o=r&&r.selector_converter?r.selector_converter:n||(()=>S(e,s)),o.call(t,s,r)})(e,e.plugins.importcss,t,s);if(o){const t=o.name||c.DOM.uniqueId();return e.formatter.register(t,o),{title:o.title,format:t}}}return null};a.each(((e,t,r)=>{const o=[],n={},c=(t,n)=>{let p,u=t.href;if(u=(e=>{const t=l.cacheSuffix;return s(e)&&(e=e.replace("?"+t,"").replace("&"+t,"")),e})(u),u&&(!r||r(u,n))&&!((e,t)=>{const s=g(e);if(s){const r=v(e),o=r?e.documentBaseURI.toAbsolute(r):i.baseURL+"/skins/ui/"+s,n=i.baseURL+"/skins/content/";return t===o+"/content"+(e.inline?".inline":"")+".min.css"||-1!==t.indexOf(n)}return!1})(e,u)){a.each(t.imports,(e=>{c(e,!0)}));try{p=t.cssRules||t.rules}catch(e){}a.each(p,(e=>{e.styleSheet&&e.styleSheet?c(e.styleSheet,!0):e.selectorText&&a.each(e.selectorText.split(","),(e=>{o.push(a.trim(e))}))}))}};a.each(e.contentCSS,(e=>{n[e]=!0})),r||(r=(e,t)=>t||n[e]);try{a.each(t.styleSheets,(e=>{c(e)}))}catch(e){}return o})(e,e.getDoc(),T(_(e))),(e=>{if(!x.test(e)&&(!n||n(e))){const s=((e,t)=>a.grep(e,(e=>!e.filter||e.filter(t))))(p,e);if(s.length>0)a.each(s,(s=>{const r=u(e,s);r&&t.addItemToGroup(s.title,r)}));else{const s=u(e,null);s&&t.addItem(s)}}}));const m=t.toFormats();e.dispatch("addStyleModifications",{items:m,replace:!h(e)})}))};e.add("importcss",(e=>((e=>{const t=e.options.register,o=e=>s(e)||n(e)||r(e);t("importcss_merge_classes",{processor:"boolean",default:!0}),t("importcss_exclusive",{processor:"boolean",default:!0}),t("importcss_selector_converter",{processor:"function"}),t("importcss_selector_filter",{processor:o}),t("importcss_file_filter",{processor:o}),t("importcss_groups",{processor:"object[]"}),t("importcss_append",{processor:"boolean",default:!1})})(e),w(e),(e=>({convertSelectorToFormat:t=>S(e,t)}))(e))))}(); \ No newline at end of file diff --git a/public/libs/tinymce/plugins/insertdatetime/plugin.min.js b/public/libs/tinymce/plugins/insertdatetime/plugin.min.js index e0c4d38c60f..801cfe9d986 100644 --- a/public/libs/tinymce/plugins/insertdatetime/plugin.min.js +++ b/public/libs/tinymce/plugins/insertdatetime/plugin.min.js @@ -1,4 +1,4 @@ /** - * TinyMCE version 6.7.2 (2023-10-25) + * TinyMCE version 6.8.4 (2024-06-19) */ !function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager");const t=e=>t=>t.options.get(e),a=t("insertdatetime_dateformat"),n=t("insertdatetime_timeformat"),r=t("insertdatetime_formats"),s=t("insertdatetime_element"),i="Sun Mon Tue Wed Thu Fri Sat Sun".split(" "),o="Sunday Monday Tuesday Wednesday Thursday Friday Saturday Sunday".split(" "),l="Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" "),m="January February March April May June July August September October November December".split(" "),c=(e,t)=>{if((e=""+e).length(t=(t=(t=(t=(t=(t=(t=(t=(t=(t=(t=(t=(t=(t=(t=t.replace("%D","%m/%d/%Y")).replace("%r","%I:%M:%S %p")).replace("%Y",""+a.getFullYear())).replace("%y",""+a.getYear())).replace("%m",c(a.getMonth()+1,2))).replace("%d",c(a.getDate(),2))).replace("%H",""+c(a.getHours(),2))).replace("%M",""+c(a.getMinutes(),2))).replace("%S",""+c(a.getSeconds(),2))).replace("%I",""+((a.getHours()+11)%12+1))).replace("%p",a.getHours()<12?"AM":"PM")).replace("%B",""+e.translate(m[a.getMonth()]))).replace("%b",""+e.translate(l[a.getMonth()]))).replace("%A",""+e.translate(o[a.getDay()]))).replace("%a",""+e.translate(i[a.getDay()]))).replace("%%","%"),u=(e,t)=>{if(s(e)){const a=d(e,t);let n;n=/%[HMSIp]/.test(t)?d(e,"%Y-%m-%dT%H:%M"):d(e,"%Y-%m-%d");const r=e.dom.getParent(e.selection.getStart(),"time");r?((e,t,a,n)=>{const r=e.dom.create("time",{datetime:a},n);e.dom.replace(r,t),e.selection.select(r,!0),e.selection.collapse(!1)})(e,r,n,a):e.insertContent('")}else e.insertContent(d(e,t))};var p=tinymce.util.Tools.resolve("tinymce.util.Tools");const g=e=>t=>{const a=()=>{t.setEnabled(e.selection.isEditable())};return e.on("NodeChange",a),a(),()=>{e.off("NodeChange",a)}};e.add("insertdatetime",(e=>{(e=>{const t=e.options.register;t("insertdatetime_dateformat",{processor:"string",default:e.translate("%Y-%m-%d")}),t("insertdatetime_timeformat",{processor:"string",default:e.translate("%H:%M:%S")}),t("insertdatetime_formats",{processor:"string[]",default:["%H:%M:%S","%Y-%m-%d","%I:%M:%S %p","%D"]}),t("insertdatetime_element",{processor:"boolean",default:!1})})(e),(e=>{e.addCommand("mceInsertDate",((t,n)=>{u(e,null!=n?n:a(e))})),e.addCommand("mceInsertTime",((t,a)=>{u(e,null!=a?a:n(e))}))})(e),(e=>{const t=r(e),a=(e=>{let t=e;return{get:()=>t,set:e=>{t=e}}})((e=>{const t=r(e);return t.length>0?t[0]:n(e)})(e)),s=t=>e.execCommand("mceInsertDate",!1,t);e.ui.registry.addSplitButton("insertdatetime",{icon:"insert-time",tooltip:"Insert date/time",select:e=>e===a.get(),fetch:a=>{a(p.map(t,(t=>({type:"choiceitem",text:d(e,t),value:t}))))},onAction:e=>{s(a.get())},onItemAction:(e,t)=>{a.set(t),s(t)},onSetup:g(e)});const i=e=>()=>{a.set(e),s(e)};e.ui.registry.addNestedMenuItem("insertdatetime",{icon:"insert-time",text:"Date/time",getSubmenuItems:()=>p.map(t,(t=>({type:"menuitem",text:d(e,t),onAction:i(t)}))),onSetup:g(e)})})(e)}))}(); \ No newline at end of file diff --git a/public/libs/tinymce/plugins/link/plugin.min.js b/public/libs/tinymce/plugins/link/plugin.min.js index fa1f87339f9..ab5bfdbf984 100644 --- a/public/libs/tinymce/plugins/link/plugin.min.js +++ b/public/libs/tinymce/plugins/link/plugin.min.js @@ -1,4 +1,4 @@ /** - * TinyMCE version 6.7.2 (2023-10-25) + * TinyMCE version 6.8.4 (2024-06-19) */ -!function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager");const t=e=>t=>(e=>{const t=typeof e;return null===e?"null":"object"===t&&Array.isArray(e)?"array":"object"===t&&(n=o=e,(r=String).prototype.isPrototypeOf(n)||(null===(l=o.constructor)||void 0===l?void 0:l.name)===r.name)?"string":t;var n,o,r,l})(t)===e,n=e=>t=>typeof t===e,o=t("string"),r=t("object"),l=t("array"),a=(null,e=>null===e);const i=n("boolean"),s=e=>!(e=>null==e)(e),c=n("function"),u=(e,t)=>{if(l(e)){for(let n=0,o=e.length;n{},d=(e,t)=>e===t;class m{constructor(e,t){this.tag=e,this.value=t}static some(e){return new m(!0,e)}static none(){return m.singletonNone}fold(e,t){return this.tag?t(this.value):e()}isSome(){return this.tag}isNone(){return!this.tag}map(e){return this.tag?m.some(e(this.value)):m.none()}bind(e){return this.tag?e(this.value):m.none()}exists(e){return this.tag&&e(this.value)}forall(e){return!this.tag||e(this.value)}filter(e){return!this.tag||e(this.value)?this:m.none()}getOr(e){return this.tag?this.value:e}or(e){return this.tag?this:e}getOrThunk(e){return this.tag?this.value:e()}orThunk(e){return this.tag?this:e()}getOrDie(e){if(this.tag)return this.value;throw new Error(null!=e?e:"Called getOrDie on None")}static from(e){return s(e)?m.some(e):m.none()}getOrNull(){return this.tag?this.value:null}getOrUndefined(){return this.value}each(e){this.tag&&e(this.value)}toArray(){return this.tag?[this.value]:[]}toString(){return this.tag?`some(${this.value})`:"none()"}}m.singletonNone=new m(!1);const h=Array.prototype.indexOf,f=Array.prototype.push,p=e=>{const t=[];for(let n=0,o=e.length;n{for(let n=0;ne.exists((e=>n(e,t))),y=e=>{const t=[],n=e=>{t.push(e)};for(let t=0;te?m.some(t):m.none(),b=e=>t=>t.options.get(e),_=b("link_assume_external_targets"),w=b("link_context_toolbar"),C=b("link_list"),O=b("link_default_target"),N=b("link_default_protocol"),A=b("link_target_list"),S=b("link_rel_list"),E=b("link_class_list"),T=b("link_title"),R=b("allow_unsafe_link_target"),P=b("link_quicklink");var L=tinymce.util.Tools.resolve("tinymce.util.Tools");const M=e=>o(e.value)?e.value:"",D=(e,t)=>{const n=[];return L.each(e,(e=>{const r=(e=>o(e.text)?e.text:o(e.title)?e.title:"")(e);if(void 0!==e.menu){const o=D(e.menu,t);n.push({text:r,items:o})}else{const o=t(e);n.push({text:r,value:o})}})),n},B=(e=M)=>t=>m.from(t).map((t=>D(t,e))),I=e=>B(M)(e),j=B,K=(e,t)=>n=>({name:e,type:"listbox",label:t,items:n}),U=M,q=Object.keys,F=Object.hasOwnProperty,V=(e,t)=>F.call(e,t);var $=tinymce.util.Tools.resolve("tinymce.dom.TreeWalker"),z=tinymce.util.Tools.resolve("tinymce.util.URI");const G=e=>s(e)&&"a"===e.nodeName.toLowerCase(),H=e=>G(e)&&!!Q(e),J=(e,t)=>{if(e.collapsed)return[];{const n=e.cloneContents(),o=n.firstChild,r=new $(o,n),l=[];let a=o;do{t(a)&&l.push(a)}while(a=r.next());return l}},W=e=>/^\w+:/i.test(e),Q=e=>{var t,n;return null!==(n=null!==(t=e.getAttribute("data-mce-href"))&&void 0!==t?t:e.getAttribute("href"))&&void 0!==n?n:""},X=(e,t)=>{const n=["noopener"],o=e?e.split(/\s+/):[],r=e=>e.filter((e=>-1===L.inArray(n,e))),l=t?(e=>(e=r(e)).length>0?e.concat(n):n)(o):r(o);return l.length>0?(e=>L.trim(e.sort().join(" ")))(l):""},Y=(e,t)=>(t=t||te(e.selection.getRng())[0]||e.selection.getNode(),le(t)?m.from(e.dom.select("a[href]",t)[0]):m.from(e.dom.getParent(t,"a[href]"))),Z=(e,t)=>Y(e,t).isSome(),ee=(e,t)=>t.fold((()=>e.getContent({format:"text"})),(e=>e.innerText||e.textContent||"")).replace(/\uFEFF/g,""),te=e=>J(e,H),ne=e=>L.grep(e,H),oe=e=>ne(e).length>0,re=e=>{const t=e.schema.getTextInlineElements();if(Y(e).exists((e=>e.hasAttribute("data-mce-block"))))return!1;const n=e.selection.getRng();return!!n.collapsed||0===J(n,(e=>1===e.nodeType&&!G(e)&&!V(t,e.nodeName.toLowerCase()))).length},le=e=>s(e)&&"FIGURE"===e.nodeName&&/\bimage\b/i.test(e.className),ae=(e,t,n)=>{const o=e.selection.getNode(),r=Y(e,o),l=((e,t)=>{const n={...t};if(0===S(e).length&&!R(e)){const e=X(n.rel,"_blank"===n.target);n.rel=e||null}return m.from(n.target).isNone()&&!1===A(e)&&(n.target=O(e)),n.href=((e,t)=>"http"!==t&&"https"!==t||W(e)?e:t+"://"+e)(n.href,_(e)),n})(e,(e=>{return t=["title","rel","class","target"],n=(t,n)=>(e[n].each((e=>{t[n]=e.length>0?e:null})),t),o={href:e.href},((e,t)=>{for(let n=0,o=e.length;n{o=n(o,e)})),o;var t,n,o})(n));e.undoManager.transact((()=>{n.href===t.href&&t.attach(),r.fold((()=>{((e,t,n,o)=>{const r=e.dom;le(t)?ge(r,t,o):n.fold((()=>{e.execCommand("mceInsertLink",!1,o)}),(t=>{e.insertContent(r.createHTML("a",o,r.encode(t)))}))})(e,o,n.text,l)}),(t=>{e.focus(),((e,t,n,o)=>{n.each((e=>{V(t,"innerText")?t.innerText=e:t.textContent=e})),e.dom.setAttribs(t,o),e.selection.select(t)})(e,t,n.text,l)}))}))},ie=e=>{const{class:t,href:n,rel:o,target:r,text:l,title:i}=e;return((e,t)=>{const n={};var o;return((e,t,n,o)=>{((e,t)=>{const n=q(e);for(let o=0,r=n.length;o{(t(e,r)?n:o)(e,r)}))})(e,((e,t)=>!1===a(e)),(o=n,(e,t)=>{o[t]=e}),g),n})({class:t.getOrNull(),href:n,rel:o.getOrNull(),target:r.getOrNull(),text:l.getOrNull(),title:i.getOrNull()})},se=(e,t,n)=>{const o=((e,t)=>{const n=e.options.get,o={allow_html_data_urls:n("allow_html_data_urls"),allow_script_urls:n("allow_script_urls"),allow_svg_data_urls:n("allow_svg_data_urls")},r=t.href;return{...t,href:z.isDomSafe(r,"a",o)?r:""}})(e,n);e.hasPlugin("rtc",!0)?e.execCommand("createlink",!1,ie(o)):ae(e,t,o)},ce=e=>{e.hasPlugin("rtc",!0)?e.execCommand("unlink"):(e=>{e.undoManager.transact((()=>{const t=e.selection.getNode();le(t)?ue(e,t):(e=>{const t=e.dom,n=e.selection,o=n.getBookmark(),r=n.getRng().cloneRange(),l=t.getParent(r.startContainer,"a[href]",e.getBody()),a=t.getParent(r.endContainer,"a[href]",e.getBody());l&&r.setStartBefore(l),a&&r.setEndAfter(a),n.setRng(r),e.execCommand("unlink"),n.moveToBookmark(o)})(e),e.focus()}))})(e)},ue=(e,t)=>{var n;const o=e.dom.select("img",t)[0];if(o){const r=e.dom.getParents(o,"a[href]",t)[0];r&&(null===(n=r.parentNode)||void 0===n||n.insertBefore(o,r),e.dom.remove(r))}},ge=(e,t,n)=>{var o;const r=e.select("img",t)[0];if(r){const t=e.create("a",n);null===(o=r.parentNode)||void 0===o||o.insertBefore(t,r),t.appendChild(r)}},de=(e,t)=>k(t,(t=>(e=>{return V(t=e,n="items")&&void 0!==t[n]&&null!==t[n];var t,n})(t)?de(e,t.items):x(t.value===e,t))),me=(e,t)=>{const n={text:e.text,title:e.title},o=(e,o)=>{const r=(l=t,a=o,"link"===a?l.link:"anchor"===a?l.anchor:m.none()).getOr([]);var l,a;return((e,t,n,o)=>{const r=o[t],l=e.length>0;return void 0!==r?de(r,n).map((t=>({url:{value:t.value,meta:{text:l?e:t.text,attach:g}},text:l?e:t.text}))):m.none()})(n.text,o,r,e)};return{onChange:(e,t)=>{const r=t.name;return"url"===r?(e=>{const t=(o=e.url,x(n.text.length<=0,m.from(null===(r=o.meta)||void 0===r?void 0:r.text).getOr(o.value)));var o,r;const l=(e=>{var t;return x(n.title.length<=0,m.from(null===(t=e.meta)||void 0===t?void 0:t.title).getOr(""))})(e.url);return t.isSome()||l.isSome()?m.some({...t.map((e=>({text:e}))).getOr({}),...l.map((e=>({title:e}))).getOr({})}):m.none()})(e()):((e,t)=>h.call(e,t))(["anchor","link"],r)>-1?o(e(),r):"text"===r||"title"===r?(n[r]=e()[r],m.none()):m.none()}}};var he=tinymce.util.Tools.resolve("tinymce.util.Delay");const fe=e=>{const t=e.href;return t.indexOf("@")>0&&-1===t.indexOf("/")&&-1===t.indexOf("mailto:")?m.some({message:"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?",preprocess:e=>({...e,href:"mailto:"+t})}):m.none()},pe=(e,t)=>n=>{const o=n.href;return 1===e&&!W(o)||0===e&&/^\s*www(\.|\d\.)/i.test(o)?m.some({message:`The URL you entered seems to be an external link. Do you want to add the required ${t}:// prefix?`,preprocess:e=>({...e,href:t+"://"+o})}):m.none()},ke=e=>{const t=e.dom.select("a:not([href])"),n=p(((e,t)=>{const n=e.length,o=new Array(n);for(let r=0;r{const t=e.name||e.id;return t?[{text:t,value:"#"+t}]:[]})));return n.length>0?m.some([{text:"None",value:""}].concat(n)):m.none()},ve=e=>{const t=E(e);return t.length>0?I(t):m.none()},ye=e=>{try{return m.some(JSON.parse(e))}catch(e){return m.none()}},xe=(e,t)=>{const n=S(e);if(n.length>0){const o=v(t,"_blank"),r=e=>X(U(e),o);return(!1===R(e)?j(r):I)(n)}return m.none()},be=[{text:"Current window",value:""},{text:"New window",value:"_blank"}],_e=e=>{const t=A(e);return l(t)?I(t).orThunk((()=>m.some(be))):!1===t?m.none():m.some(be)},we=(e,t,n)=>{const o=e.getAttrib(t,n);return null!==o&&o.length>0?m.some(o):m.none()},Ce=(e,t)=>(e=>{const t=t=>e.convertURL(t.value||t.url||"","href"),n=C(e);return new Promise((e=>{o(n)?fetch(n).then((e=>e.ok?e.text().then(ye):Promise.reject())).then(e,(()=>e(m.none()))):c(n)?n((t=>e(m.some(t)))):e(m.from(n))})).then((e=>e.bind(j(t)).map((e=>e.length>0?[{text:"None",value:""}].concat(e):e))))})(e).then((n=>{const o=((e,t)=>{const n=e.dom,o=re(e)?m.some(ee(e.selection,t)):m.none(),r=t.bind((e=>m.from(n.getAttrib(e,"href")))),l=t.bind((e=>m.from(n.getAttrib(e,"target")))),a=t.bind((e=>we(n,e,"rel"))),i=t.bind((e=>we(n,e,"class")));return{url:r,text:o,title:t.bind((e=>we(n,e,"title"))),target:l,rel:a,linkClass:i}})(e,t);return{anchor:o,catalogs:{targets:_e(e),rels:xe(e,o.target),classes:ve(e),anchor:ke(e),link:n},optNode:t,flags:{titleEnabled:T(e)}}})),Oe=e=>{const t=(e=>{const t=Y(e);return Ce(e,t)})(e);t.then((t=>{const n=((e,t)=>n=>{const o=n.getData();if(!o.url.value)return ce(e),void n.close();const r=e=>m.from(o[e]).filter((n=>!v(t.anchor[e],n))),l={href:o.url.value,text:r("text"),target:r("target"),rel:r("rel"),class:r("linkClass"),title:r("title")},a={href:o.url.value,attach:void 0!==o.url.meta&&o.url.meta.attach?o.url.meta.attach:g};((e,t)=>k([fe,pe(_(e),N(e))],(e=>e(t))).fold((()=>Promise.resolve(t)),(n=>new Promise((o=>{((e,t,n)=>{const o=e.selection.getRng();he.setEditorTimeout(e,(()=>{e.windowManager.confirm(t,(t=>{e.selection.setRng(o),n(t)}))}))})(e,n.message,(e=>{o(e?n.preprocess(t):t)}))})))))(e,l).then((t=>{se(e,a,t)})),n.close()})(e,t);return((e,t,n)=>{const o=e.anchor.text.map((()=>({name:"text",type:"input",label:"Text to display"}))).toArray(),r=e.flags.titleEnabled?[{name:"title",type:"input",label:"Title"}]:[],l=((e,t)=>{const n=e.anchor,o=n.url.getOr("");return{url:{value:o,meta:{original:{value:o}}},text:n.text.getOr(""),title:n.title.getOr(""),anchor:o,link:o,rel:n.rel.getOr(""),target:n.target.or(t).getOr(""),linkClass:n.linkClass.getOr("")}})(e,m.from(O(n))),a=e.catalogs,i=me(l,a);return{title:"Insert/Edit Link",size:"normal",body:{type:"panel",items:p([[{name:"url",type:"urlinput",filetype:"file",label:"URL"}],o,r,y([a.anchor.map(K("anchor","Anchors")),a.rels.map(K("rel","Rel")),a.targets.map(K("target","Open link in...")),a.link.map(K("link","Link list")),a.classes.map(K("linkClass","Class"))])])},buttons:[{type:"cancel",name:"cancel",text:"Cancel"},{type:"submit",name:"save",text:"Save",primary:!0}],initialData:l,onChange:(e,{name:t})=>{i.onChange(e.getData,{name:t}).each((t=>{e.setData(t)}))},onSubmit:t}})(t,n,e)})).then((t=>{e.windowManager.open(t)}))};var Ne=tinymce.util.Tools.resolve("tinymce.util.VK");const Ae=(e,t)=>e.dom.getParent(t,"a[href]"),Se=e=>Ae(e,e.selection.getStart()),Ee=(e,t)=>{if(t){const n=Q(t);if(/^#/.test(n)){const t=e.dom.select(n);t.length&&e.selection.scrollIntoView(t[0],!0)}else(e=>{const t=document.createElement("a");t.target="_blank",t.href=e,t.rel="noreferrer noopener";const n=document.createEvent("MouseEvents");n.initMouseEvent("click",!0,!0,window,0,0,0,0,0,!1,!1,!1,!1,0,null),((e,t)=>{document.body.appendChild(e),e.dispatchEvent(t),document.body.removeChild(e)})(t,n)})(t.href)}},Te=e=>()=>{e.execCommand("mceLink",!1,{dialog:!0})},Re=e=>()=>{Ee(e,Se(e))},Pe=(e,t)=>(e.on("NodeChange",t),()=>e.off("NodeChange",t)),Le=e=>t=>{const n=()=>{t.setActive(!e.mode.isReadOnly()&&Z(e,e.selection.getNode())),t.setEnabled(e.selection.isEditable())};return n(),Pe(e,n)},Me=e=>t=>{const n=()=>{t.setEnabled(e.selection.isEditable())};return n(),Pe(e,n)},De=e=>t=>{const n=()=>t.setEnabled((e=>1===(e.selection.isCollapsed()?ne(e.dom.getParents(e.selection.getStart())):te(e.selection.getRng())).length)(e));return n(),Pe(e,n)},Be=e=>t=>{const n=e.dom.getParents(e.selection.getStart()),o=n=>{t.setEnabled((t=>{return oe(t)||(n=e.selection.getRng(),te(n).length>0);var n})(n)&&e.selection.isEditable())};return o(n),Pe(e,(e=>o(e.parents)))};e.add("link",(e=>{(e=>{const t=e.options.register;t("link_assume_external_targets",{processor:e=>{const t=o(e)||i(e);return t?!0===e?{value:1,valid:t}:"http"===e||"https"===e?{value:e,valid:t}:{value:0,valid:t}:{valid:!1,message:"Must be a string or a boolean."}},default:!1}),t("link_context_toolbar",{processor:"boolean",default:!1}),t("link_list",{processor:e=>o(e)||c(e)||u(e,r)}),t("link_default_target",{processor:"string"}),t("link_default_protocol",{processor:"string",default:"https"}),t("link_target_list",{processor:e=>i(e)||u(e,r),default:!0}),t("link_rel_list",{processor:"object[]",default:[]}),t("link_class_list",{processor:"object[]",default:[]}),t("link_title",{processor:"boolean",default:!0}),t("allow_unsafe_link_target",{processor:"boolean",default:!1}),t("link_quicklink",{processor:"boolean",default:!1})})(e),(e=>{e.ui.registry.addToggleButton("link",{icon:"link",tooltip:"Insert/edit link",onAction:Te(e),onSetup:Le(e)}),e.ui.registry.addButton("openlink",{icon:"new-tab",tooltip:"Open link",onAction:Re(e),onSetup:De(e)}),e.ui.registry.addButton("unlink",{icon:"unlink",tooltip:"Remove link",onAction:()=>ce(e),onSetup:Be(e)})})(e),(e=>{e.ui.registry.addMenuItem("openlink",{text:"Open link",icon:"new-tab",onAction:Re(e),onSetup:De(e)}),e.ui.registry.addMenuItem("link",{icon:"link",text:"Link...",shortcut:"Meta+K",onSetup:Me(e),onAction:Te(e)}),e.ui.registry.addMenuItem("unlink",{icon:"unlink",text:"Remove link",onAction:()=>ce(e),onSetup:Be(e)})})(e),(e=>{e.ui.registry.addContextMenu("link",{update:t=>e.dom.isEditable(t)?oe(e.dom.getParents(t,"a"))?"link unlink openlink":"link":""})})(e),(e=>{const t=t=>{const n=e.selection.getNode();return t.setEnabled(Z(e,n)),g};e.ui.registry.addContextForm("quicklink",{launch:{type:"contextformtogglebutton",icon:"link",tooltip:"Link",onSetup:Le(e)},label:"Link",predicate:t=>w(e)&&Z(e,t),initValue:()=>Y(e).fold((()=>""),Q),commands:[{type:"contextformtogglebutton",icon:"link",tooltip:"Link",primary:!0,onSetup:t=>{const n=e.selection.getNode();return t.setActive(Z(e,n)),Le(e)(t)},onAction:t=>{const n=t.getValue(),o=(t=>{const n=Y(e),o=re(e);if(n.isNone()&&o){const o=ee(e.selection,n);return x(0===o.length,t)}return m.none()})(n);se(e,{href:n,attach:g},{href:n,text:o,title:m.none(),rel:m.none(),target:m.none(),class:m.none()}),(e=>{e.selection.collapse(!1)})(e),t.hide()}},{type:"contextformbutton",icon:"unlink",tooltip:"Remove link",onSetup:t,onAction:t=>{ce(e),t.hide()}},{type:"contextformbutton",icon:"new-tab",tooltip:"Open link",onSetup:t,onAction:t=>{Re(e)(),t.hide()}}]})})(e),(e=>{e.on("click",(t=>{const n=Ae(e,t.target);n&&Ne.metaKeyPressed(t)&&(t.preventDefault(),Ee(e,n))})),e.on("keydown",(t=>{if(!t.isDefaultPrevented()&&13===t.keyCode&&(e=>!0===e.altKey&&!1===e.shiftKey&&!1===e.ctrlKey&&!1===e.metaKey)(t)){const n=Se(e);n&&(t.preventDefault(),Ee(e,n))}}))})(e),(e=>{e.addCommand("mceLink",((t,n)=>{!0!==(null==n?void 0:n.dialog)&&P(e)?e.dispatch("contexttoolbar-show",{toolbarKey:"quicklink"}):Oe(e)}))})(e),(e=>{e.addShortcut("Meta+K","",(()=>{e.execCommand("mceLink")}))})(e)}))}(); \ No newline at end of file +!function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager");const t=e=>t=>(e=>{const t=typeof e;return null===e?"null":"object"===t&&Array.isArray(e)?"array":"object"===t&&(n=o=e,(r=String).prototype.isPrototypeOf(n)||(null===(l=o.constructor)||void 0===l?void 0:l.name)===r.name)?"string":t;var n,o,r,l})(t)===e,n=e=>t=>typeof t===e,o=t("string"),r=t("object"),l=t("array"),i=(null,e=>null===e);const a=n("boolean"),s=e=>!(e=>null==e)(e),c=n("function"),u=(e,t)=>{if(l(e)){for(let n=0,o=e.length;n{},d=(e,t)=>e===t;class m{constructor(e,t){this.tag=e,this.value=t}static some(e){return new m(!0,e)}static none(){return m.singletonNone}fold(e,t){return this.tag?t(this.value):e()}isSome(){return this.tag}isNone(){return!this.tag}map(e){return this.tag?m.some(e(this.value)):m.none()}bind(e){return this.tag?e(this.value):m.none()}exists(e){return this.tag&&e(this.value)}forall(e){return!this.tag||e(this.value)}filter(e){return!this.tag||e(this.value)?this:m.none()}getOr(e){return this.tag?this.value:e}or(e){return this.tag?this:e}getOrThunk(e){return this.tag?this.value:e()}orThunk(e){return this.tag?this:e()}getOrDie(e){if(this.tag)return this.value;throw new Error(null!=e?e:"Called getOrDie on None")}static from(e){return s(e)?m.some(e):m.none()}getOrNull(){return this.tag?this.value:null}getOrUndefined(){return this.value}each(e){this.tag&&e(this.value)}toArray(){return this.tag?[this.value]:[]}toString(){return this.tag?`some(${this.value})`:"none()"}}m.singletonNone=new m(!1);const h=Array.prototype.indexOf,f=Array.prototype.push,p=e=>{const t=[];for(let n=0,o=e.length;n{for(let n=0;ne.exists((e=>n(e,t))),x=e=>{const t=[],n=e=>{t.push(e)};for(let t=0;te?m.some(t):m.none(),b=e=>t=>t.options.get(e),_=b("link_assume_external_targets"),w=b("link_context_toolbar"),C=b("link_list"),O=b("link_default_target"),N=b("link_default_protocol"),A=b("link_target_list"),S=b("link_rel_list"),E=b("link_class_list"),T=b("link_title"),R=b("allow_unsafe_link_target"),P=b("link_quicklink");var L=tinymce.util.Tools.resolve("tinymce.util.Tools");const M=e=>o(e.value)?e.value:"",D=(e,t)=>{const n=[];return L.each(e,(e=>{const r=(e=>o(e.text)?e.text:o(e.title)?e.title:"")(e);if(void 0!==e.menu){const o=D(e.menu,t);n.push({text:r,items:o})}else{const o=t(e);n.push({text:r,value:o})}})),n},B=(e=M)=>t=>m.from(t).map((t=>D(t,e))),I=e=>B(M)(e),j=B,K=(e,t)=>n=>({name:e,type:"listbox",label:t,items:n}),U=M,q=Object.keys,F=Object.hasOwnProperty,V=(e,t)=>F.call(e,t);var $=tinymce.util.Tools.resolve("tinymce.dom.TreeWalker"),z=tinymce.util.Tools.resolve("tinymce.util.URI");const G=e=>s(e)&&"a"===e.nodeName.toLowerCase(),H=e=>G(e)&&!!Q(e),J=(e,t)=>{if(e.collapsed)return[];{const n=e.cloneContents(),o=n.firstChild,r=new $(o,n),l=[];let i=o;do{t(i)&&l.push(i)}while(i=r.next());return l}},W=e=>/^\w+:/i.test(e),Q=e=>{var t,n;return null!==(n=null!==(t=e.getAttribute("data-mce-href"))&&void 0!==t?t:e.getAttribute("href"))&&void 0!==n?n:""},X=(e,t)=>{const n=["noopener"],o=e?e.split(/\s+/):[],r=e=>e.filter((e=>-1===L.inArray(n,e))),l=t?(e=>(e=r(e)).length>0?e.concat(n):n)(o):r(o);return l.length>0?(e=>L.trim(e.sort().join(" ")))(l):""},Y=(e,t)=>(t=t||te(e.selection.getRng())[0]||e.selection.getNode(),le(t)?m.from(e.dom.select("a[href]",t)[0]):m.from(e.dom.getParent(t,"a[href]"))),Z=(e,t)=>Y(e,t).isSome(),ee=(e,t)=>t.fold((()=>e.getContent({format:"text"})),(e=>e.innerText||e.textContent||"")).replace(/\uFEFF/g,""),te=e=>J(e,H),ne=e=>L.grep(e,H),oe=e=>ne(e).length>0,re=e=>{const t=e.schema.getTextInlineElements();if(Y(e).exists((e=>e.hasAttribute("data-mce-block"))))return!1;const n=e.selection.getRng();return!!n.collapsed||0===J(n,(e=>1===e.nodeType&&!G(e)&&!V(t,e.nodeName.toLowerCase()))).length},le=e=>s(e)&&"FIGURE"===e.nodeName&&/\bimage\b/i.test(e.className),ie=(e,t,n)=>{const o=e.selection.getNode(),r=Y(e,o),l=((e,t)=>{const n={...t};if(0===S(e).length&&!R(e)){const e=X(n.rel,"_blank"===n.target);n.rel=e||null}return m.from(n.target).isNone()&&!1===A(e)&&(n.target=O(e)),n.href=((e,t)=>"http"!==t&&"https"!==t||W(e)?e:t+"://"+e)(n.href,_(e)),n})(e,(e=>{return t=["title","rel","class","target"],n=(t,n)=>(e[n].each((e=>{t[n]=e.length>0?e:null})),t),o={href:e.href},((e,t)=>{for(let n=0,o=e.length;n{o=n(o,e)})),o;var t,n,o})(n));e.undoManager.transact((()=>{n.href===t.href&&t.attach(),r.fold((()=>{((e,t,n,o)=>{const r=e.dom;le(t)?ge(r,t,o):n.fold((()=>{e.execCommand("mceInsertLink",!1,o)}),(t=>{e.insertContent(r.createHTML("a",o,r.encode(t)))}))})(e,o,n.text,l)}),(t=>{e.focus(),((e,t,n,o)=>{n.each((e=>{V(t,"innerText")?t.innerText=e:t.textContent=e})),e.dom.setAttribs(t,o),e.selection.select(t)})(e,t,n.text,l)}))}))},ae=e=>{const{class:t,href:n,rel:o,target:r,text:l,title:a}=e;return((e,t)=>{const n={};var o;return((e,t,n,o)=>{((e,t)=>{const n=q(e);for(let o=0,r=n.length;o{(t(e,r)?n:o)(e,r)}))})(e,((e,t)=>!1===i(e)),(o=n,(e,t)=>{o[t]=e}),g),n})({class:t.getOrNull(),href:n,rel:o.getOrNull(),target:r.getOrNull(),text:l.getOrNull(),title:a.getOrNull()})},se=(e,t,n)=>{const o=((e,t)=>{const n=e.options.get,o={allow_html_data_urls:n("allow_html_data_urls"),allow_script_urls:n("allow_script_urls"),allow_svg_data_urls:n("allow_svg_data_urls")},r=t.href;return{...t,href:z.isDomSafe(r,"a",o)?r:""}})(e,n);e.hasPlugin("rtc",!0)?e.execCommand("createlink",!1,ae(o)):ie(e,t,o)},ce=e=>{e.hasPlugin("rtc",!0)?e.execCommand("unlink"):(e=>{e.undoManager.transact((()=>{const t=e.selection.getNode();le(t)?ue(e,t):(e=>{const t=e.dom,n=e.selection,o=n.getBookmark(),r=n.getRng().cloneRange(),l=t.getParent(r.startContainer,"a[href]",e.getBody()),i=t.getParent(r.endContainer,"a[href]",e.getBody());l&&r.setStartBefore(l),i&&r.setEndAfter(i),n.setRng(r),e.execCommand("unlink"),n.moveToBookmark(o)})(e),e.focus()}))})(e)},ue=(e,t)=>{var n;const o=e.dom.select("img",t)[0];if(o){const r=e.dom.getParents(o,"a[href]",t)[0];r&&(null===(n=r.parentNode)||void 0===n||n.insertBefore(o,r),e.dom.remove(r))}},ge=(e,t,n)=>{var o;const r=e.select("img",t)[0];if(r){const t=e.create("a",n);null===(o=r.parentNode)||void 0===o||o.insertBefore(t,r),t.appendChild(r)}},de=(e,t)=>k(t,(t=>(e=>{return V(t=e,n="items")&&void 0!==t[n]&&null!==t[n];var t,n})(t)?de(e,t.items):y(t.value===e,t))),me=(e,t)=>{const n={text:e.text,title:e.title},o=(e,o)=>{const r=(l=t,i=o,"link"===i?l.link:"anchor"===i?l.anchor:m.none()).getOr([]);var l,i;return((e,t,n,o)=>{const r=o[t],l=e.length>0;return void 0!==r?de(r,n).map((t=>({url:{value:t.value,meta:{text:l?e:t.text,attach:g}},text:l?e:t.text}))):m.none()})(n.text,o,r,e)};return{onChange:(e,t)=>{const r=t.name;return"url"===r?(e=>{const t=(o=e.url,y(n.text.length<=0,m.from(null===(r=o.meta)||void 0===r?void 0:r.text).getOr(o.value)));var o,r;const l=(e=>{var t;return y(n.title.length<=0,m.from(null===(t=e.meta)||void 0===t?void 0:t.title).getOr(""))})(e.url);return t.isSome()||l.isSome()?m.some({...t.map((e=>({text:e}))).getOr({}),...l.map((e=>({title:e}))).getOr({})}):m.none()})(e()):((e,t)=>h.call(e,t))(["anchor","link"],r)>-1?o(e(),r):"text"===r||"title"===r?(n[r]=e()[r],m.none()):m.none()}}};var he=tinymce.util.Tools.resolve("tinymce.util.Delay");const fe=e=>{const t=e.href;return t.indexOf("@")>0&&-1===t.indexOf("/")&&-1===t.indexOf("mailto:")?m.some({message:"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?",preprocess:e=>({...e,href:"mailto:"+t})}):m.none()},pe=(e,t)=>n=>{const o=n.href;return 1===e&&!W(o)||0===e&&/^\s*www(\.|\d\.)/i.test(o)?m.some({message:`The URL you entered seems to be an external link. Do you want to add the required ${t}:// prefix?`,preprocess:e=>({...e,href:t+"://"+o})}):m.none()},ke=e=>{const t=e.dom.select("a:not([href])"),n=p(((e,t)=>{const n=e.length,o=new Array(n);for(let r=0;r{const t=e.name||e.id;return t?[{text:t,value:"#"+t}]:[]})));return n.length>0?m.some([{text:"None",value:""}].concat(n)):m.none()},ve=e=>{const t=E(e);return t.length>0?I(t):m.none()},xe=e=>{try{return m.some(JSON.parse(e))}catch(e){return m.none()}},ye=(e,t)=>{const n=S(e);if(n.length>0){const o=v(t,"_blank"),r=e=>X(U(e),o);return(!1===R(e)?j(r):I)(n)}return m.none()},be=[{text:"Current window",value:""},{text:"New window",value:"_blank"}],_e=e=>{const t=A(e);return l(t)?I(t).orThunk((()=>m.some(be))):!1===t?m.none():m.some(be)},we=(e,t,n)=>{const o=e.getAttrib(t,n);return null!==o&&o.length>0?m.some(o):m.none()},Ce=(e,t)=>(e=>{const t=t=>e.convertURL(t.value||t.url||"","href"),n=C(e);return new Promise((e=>{o(n)?fetch(n).then((e=>e.ok?e.text().then(xe):Promise.reject())).then(e,(()=>e(m.none()))):c(n)?n((t=>e(m.some(t)))):e(m.from(n))})).then((e=>e.bind(j(t)).map((e=>e.length>0?[{text:"None",value:""}].concat(e):e))))})(e).then((n=>{const o=((e,t)=>{const n=e.dom,o=re(e)?m.some(ee(e.selection,t)):m.none(),r=t.bind((e=>m.from(n.getAttrib(e,"href")))),l=t.bind((e=>m.from(n.getAttrib(e,"target")))),i=t.bind((e=>we(n,e,"rel"))),a=t.bind((e=>we(n,e,"class")));return{url:r,text:o,title:t.bind((e=>we(n,e,"title"))),target:l,rel:i,linkClass:a}})(e,t);return{anchor:o,catalogs:{targets:_e(e),rels:ye(e,o.target),classes:ve(e),anchor:ke(e),link:n},optNode:t,flags:{titleEnabled:T(e)}}})),Oe=e=>{const t=(e=>{const t=Y(e);return Ce(e,t)})(e);t.then((t=>{const n=((e,t)=>n=>{const o=n.getData();if(!o.url.value)return ce(e),void n.close();const r=e=>m.from(o[e]).filter((n=>!v(t.anchor[e],n))),l={href:o.url.value,text:r("text"),target:r("target"),rel:r("rel"),class:r("linkClass"),title:r("title")},i={href:o.url.value,attach:void 0!==o.url.meta&&o.url.meta.attach?o.url.meta.attach:g};((e,t)=>k([fe,pe(_(e),N(e))],(e=>e(t))).fold((()=>Promise.resolve(t)),(n=>new Promise((o=>{((e,t,n)=>{const o=e.selection.getRng();he.setEditorTimeout(e,(()=>{e.windowManager.confirm(t,(t=>{e.selection.setRng(o),n(t)}))}))})(e,n.message,(e=>{o(e?n.preprocess(t):t)}))})))))(e,l).then((t=>{se(e,i,t)})),n.close()})(e,t);return((e,t,n)=>{const o=e.anchor.text.map((()=>({name:"text",type:"input",label:"Text to display"}))).toArray(),r=e.flags.titleEnabled?[{name:"title",type:"input",label:"Title"}]:[],l=((e,t)=>{const n=e.anchor,o=n.url.getOr("");return{url:{value:o,meta:{original:{value:o}}},text:n.text.getOr(""),title:n.title.getOr(""),anchor:o,link:o,rel:n.rel.getOr(""),target:n.target.or(t).getOr(""),linkClass:n.linkClass.getOr("")}})(e,m.from(O(n))),i=e.catalogs,a=me(l,i);return{title:"Insert/Edit Link",size:"normal",body:{type:"panel",items:p([[{name:"url",type:"urlinput",filetype:"file",label:"URL",picker_text:"Browse links"}],o,r,x([i.anchor.map(K("anchor","Anchors")),i.rels.map(K("rel","Rel")),i.targets.map(K("target","Open link in...")),i.link.map(K("link","Link list")),i.classes.map(K("linkClass","Class"))])])},buttons:[{type:"cancel",name:"cancel",text:"Cancel"},{type:"submit",name:"save",text:"Save",primary:!0}],initialData:l,onChange:(e,{name:t})=>{a.onChange(e.getData,{name:t}).each((t=>{e.setData(t)}))},onSubmit:t}})(t,n,e)})).then((t=>{e.windowManager.open(t)}))};var Ne=tinymce.util.Tools.resolve("tinymce.util.VK");const Ae=(e,t)=>e.dom.getParent(t,"a[href]"),Se=e=>Ae(e,e.selection.getStart()),Ee=(e,t)=>{if(t){const n=Q(t);if(/^#/.test(n)){const t=e.dom.select(n);t.length&&e.selection.scrollIntoView(t[0],!0)}else(e=>{const t=document.createElement("a");t.target="_blank",t.href=e,t.rel="noreferrer noopener";const n=document.createEvent("MouseEvents");n.initMouseEvent("click",!0,!0,window,0,0,0,0,0,!1,!1,!1,!1,0,null),((e,t)=>{document.body.appendChild(e),e.dispatchEvent(t),document.body.removeChild(e)})(t,n)})(t.href)}},Te=e=>()=>{e.execCommand("mceLink",!1,{dialog:!0})},Re=e=>()=>{Ee(e,Se(e))},Pe=(e,t)=>(e.on("NodeChange",t),()=>e.off("NodeChange",t)),Le=e=>t=>{const n=()=>{t.setActive(!e.mode.isReadOnly()&&Z(e,e.selection.getNode())),t.setEnabled(e.selection.isEditable())};return n(),Pe(e,n)},Me=e=>t=>{const n=()=>{t.setEnabled(e.selection.isEditable())};return n(),Pe(e,n)},De=e=>t=>{const n=()=>t.setEnabled((e=>1===(e.selection.isCollapsed()?ne(e.dom.getParents(e.selection.getStart())):te(e.selection.getRng())).length)(e));return n(),Pe(e,n)},Be=e=>t=>{const n=e.dom.getParents(e.selection.getStart()),o=n=>{t.setEnabled((t=>{return oe(t)||(n=e.selection.getRng(),te(n).length>0);var n})(n)&&e.selection.isEditable())};return o(n),Pe(e,(e=>o(e.parents)))};e.add("link",(e=>{(e=>{const t=e.options.register;t("link_assume_external_targets",{processor:e=>{const t=o(e)||a(e);return t?!0===e?{value:1,valid:t}:"http"===e||"https"===e?{value:e,valid:t}:{value:0,valid:t}:{valid:!1,message:"Must be a string or a boolean."}},default:!1}),t("link_context_toolbar",{processor:"boolean",default:!1}),t("link_list",{processor:e=>o(e)||c(e)||u(e,r)}),t("link_default_target",{processor:"string"}),t("link_default_protocol",{processor:"string",default:"https"}),t("link_target_list",{processor:e=>a(e)||u(e,r),default:!0}),t("link_rel_list",{processor:"object[]",default:[]}),t("link_class_list",{processor:"object[]",default:[]}),t("link_title",{processor:"boolean",default:!0}),t("allow_unsafe_link_target",{processor:"boolean",default:!1}),t("link_quicklink",{processor:"boolean",default:!1})})(e),(e=>{e.ui.registry.addToggleButton("link",{icon:"link",tooltip:"Insert/edit link",onAction:Te(e),onSetup:Le(e)}),e.ui.registry.addButton("openlink",{icon:"new-tab",tooltip:"Open link",onAction:Re(e),onSetup:De(e)}),e.ui.registry.addButton("unlink",{icon:"unlink",tooltip:"Remove link",onAction:()=>ce(e),onSetup:Be(e)})})(e),(e=>{e.ui.registry.addMenuItem("openlink",{text:"Open link",icon:"new-tab",onAction:Re(e),onSetup:De(e)}),e.ui.registry.addMenuItem("link",{icon:"link",text:"Link...",shortcut:"Meta+K",onSetup:Me(e),onAction:Te(e)}),e.ui.registry.addMenuItem("unlink",{icon:"unlink",text:"Remove link",onAction:()=>ce(e),onSetup:Be(e)})})(e),(e=>{e.ui.registry.addContextMenu("link",{update:t=>e.dom.isEditable(t)?oe(e.dom.getParents(t,"a"))?"link unlink openlink":"link":""})})(e),(e=>{const t=t=>{const n=e.selection.getNode();return t.setEnabled(Z(e,n)),g};e.ui.registry.addContextForm("quicklink",{launch:{type:"contextformtogglebutton",icon:"link",tooltip:"Link",onSetup:Le(e)},label:"Link",predicate:t=>w(e)&&Z(e,t),initValue:()=>Y(e).fold((()=>""),Q),commands:[{type:"contextformtogglebutton",icon:"link",tooltip:"Link",primary:!0,onSetup:t=>{const n=e.selection.getNode();return t.setActive(Z(e,n)),Le(e)(t)},onAction:t=>{const n=t.getValue(),o=(t=>{const n=Y(e),o=re(e);if(n.isNone()&&o){const o=ee(e.selection,n);return y(0===o.length,t)}return m.none()})(n);se(e,{href:n,attach:g},{href:n,text:o,title:m.none(),rel:m.none(),target:m.none(),class:m.none()}),(e=>{e.selection.collapse(!1)})(e),t.hide()}},{type:"contextformbutton",icon:"unlink",tooltip:"Remove link",onSetup:t,onAction:t=>{ce(e),t.hide()}},{type:"contextformbutton",icon:"new-tab",tooltip:"Open link",onSetup:t,onAction:t=>{Re(e)(),t.hide()}}]})})(e),(e=>{e.on("click",(t=>{const n=Ae(e,t.target);n&&Ne.metaKeyPressed(t)&&(t.preventDefault(),Ee(e,n))})),e.on("keydown",(t=>{if(!t.isDefaultPrevented()&&13===t.keyCode&&(e=>!0===e.altKey&&!1===e.shiftKey&&!1===e.ctrlKey&&!1===e.metaKey)(t)){const n=Se(e);n&&(t.preventDefault(),Ee(e,n))}}))})(e),(e=>{e.addCommand("mceLink",((t,n)=>{!0!==(null==n?void 0:n.dialog)&&P(e)?e.dispatch("contexttoolbar-show",{toolbarKey:"quicklink"}):Oe(e)}))})(e),(e=>{e.addShortcut("Meta+K","",(()=>{e.execCommand("mceLink")}))})(e)}))}(); \ No newline at end of file diff --git a/public/libs/tinymce/plugins/lists/plugin.min.js b/public/libs/tinymce/plugins/lists/plugin.min.js index e72e1336b9f..33edd00017d 100644 --- a/public/libs/tinymce/plugins/lists/plugin.min.js +++ b/public/libs/tinymce/plugins/lists/plugin.min.js @@ -1,4 +1,4 @@ /** - * TinyMCE version 6.7.2 (2023-10-25) + * TinyMCE version 6.8.4 (2024-06-19) */ -!function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager");const t=e=>t=>(e=>{const t=typeof e;return null===e?"null":"object"===t&&Array.isArray(e)?"array":"object"===t&&(n=o=e,(r=String).prototype.isPrototypeOf(n)||(null===(s=o.constructor)||void 0===s?void 0:s.name)===r.name)?"string":t;var n,o,r,s})(t)===e,n=e=>t=>typeof t===e,o=t("string"),r=t("object"),s=t("array"),i=n("boolean"),l=e=>!(e=>null==e)(e),a=n("function"),d=n("number"),c=()=>{},m=(e,t)=>e===t,u=e=>t=>!e(t),p=(!1,()=>false);class g{constructor(e,t){this.tag=e,this.value=t}static some(e){return new g(!0,e)}static none(){return g.singletonNone}fold(e,t){return this.tag?t(this.value):e()}isSome(){return this.tag}isNone(){return!this.tag}map(e){return this.tag?g.some(e(this.value)):g.none()}bind(e){return this.tag?e(this.value):g.none()}exists(e){return this.tag&&e(this.value)}forall(e){return!this.tag||e(this.value)}filter(e){return!this.tag||e(this.value)?this:g.none()}getOr(e){return this.tag?this.value:e}or(e){return this.tag?this:e}getOrThunk(e){return this.tag?this.value:e()}orThunk(e){return this.tag?this:e()}getOrDie(e){if(this.tag)return this.value;throw new Error(null!=e?e:"Called getOrDie on None")}static from(e){return l(e)?g.some(e):g.none()}getOrNull(){return this.tag?this.value:null}getOrUndefined(){return this.value}each(e){this.tag&&e(this.value)}toArray(){return this.tag?[this.value]:[]}toString(){return this.tag?`some(${this.value})`:"none()"}}g.singletonNone=new g(!1);const h=Array.prototype.slice,f=Array.prototype.indexOf,y=Array.prototype.push,v=(e,t)=>{return n=e,o=t,f.call(n,o)>-1;var n,o},C=(e,t)=>{for(let n=0,o=e.length;n{const n=e.length,o=new Array(n);for(let r=0;r{for(let n=0,o=e.length;n{const n=[];for(let o=0,r=e.length;o(S(e,((e,o)=>{n=t(n,e,o)})),n),O=(e,t,n)=>{for(let o=0,r=e.length;oO(e,t,p),x=(e,t)=>(e=>{const t=[];for(let n=0,o=e.length;n{const t=h.call(e,0);return t.reverse(),t},T=(e,t)=>t>=0&&tT(e,0),w=e=>T(e,e.length-1),D=(e,t)=>{const n=[],o=a(t)?e=>C(n,(n=>t(n,e))):e=>v(n,e);for(let t=0,r=e.length;te.exists((e=>n(e,t))),I=(e,t,n)=>e.isSome()&&t.isSome()?g.some(n(e.getOrDie(),t.getOrDie())):g.none(),P=e=>{if(null==e)throw new Error("Node cannot be null or undefined");return{dom:e}},M=(e,t)=>{const n=(t||document).createElement("div");if(n.innerHTML=e,!n.hasChildNodes()||n.childNodes.length>1){const t="HTML does not have a single root node";throw console.error(t,e),new Error(t)}return P(n.childNodes[0])},R=(e,t)=>{const n=(t||document).createElement(e);return P(n)},U=P,$=(e,t)=>e.dom===t.dom;"undefined"!=typeof window?window:Function("return this;")();const _=e=>e.dom.nodeName.toLowerCase(),H=e=>e.dom.nodeType,V=(1,e=>1===H(e));const F=e=>t=>V(t)&&_(t)===e,j=e=>g.from(e.dom.parentNode).map(U),K=e=>b(e.dom.childNodes,U),z=(e,t)=>{const n=e.dom.childNodes;return g.from(n[t]).map(U)},Q=e=>z(e,0),W=e=>z(e,e.dom.childNodes.length-1),q=(e,t,n)=>{let o=e.dom;const r=a(n)?n:p;for(;o.parentNode;){o=o.parentNode;const e=U(o);if(t(e))return g.some(e);if(r(e))break}return g.none()},Z=(e,t,n)=>((e,t,n,o,r)=>o(n)?g.some(n):a(r)&&r(n)?g.none():t(n,o,r))(0,q,e,t,n),G=(e,t)=>{j(e).each((n=>{n.dom.insertBefore(t.dom,e.dom)}))},J=(e,t)=>{e.dom.appendChild(t.dom)},X=(e,t)=>{S(t,(t=>{J(e,t)}))},Y=e=>{e.dom.textContent="",S(K(e),(e=>{ee(e)}))},ee=e=>{const t=e.dom;null!==t.parentNode&&t.parentNode.removeChild(t)};var te=tinymce.util.Tools.resolve("tinymce.dom.RangeUtils"),ne=tinymce.util.Tools.resolve("tinymce.dom.TreeWalker"),oe=tinymce.util.Tools.resolve("tinymce.util.VK");const re=e=>b(e,U),se=Object.keys,ie=(e,t)=>{const n=se(e);for(let o=0,r=n.length;o{const n=e.dom;ie(t,((e,t)=>{((e,t,n)=>{if(!(o(n)||i(n)||d(n)))throw console.error("Invalid call to Attribute.set. Key ",t,":: Value ",n,":: Element ",e),new Error("Attribute value was not simple");e.setAttribute(t,n+"")})(n,t,e)}))},ae=e=>L(e.dom.attributes,((e,t)=>(e[t.name]=t.value,e)),{}),de=e=>((e,t)=>U(e.dom.cloneNode(!0)))(e),ce=(e,t)=>{const n=((e,t)=>{const n=R(t),o=ae(e);return le(n,o),n})(e,t);var o,r;r=n,(e=>g.from(e.dom.nextSibling).map(U))(o=e).fold((()=>{j(o).each((e=>{J(e,r)}))}),(e=>{G(e,r)}));const s=K(e);return X(n,s),ee(e),n};var me=tinymce.util.Tools.resolve("tinymce.dom.DOMUtils"),ue=tinymce.util.Tools.resolve("tinymce.util.Tools");const pe=e=>t=>l(t)&&t.nodeName.toLowerCase()===e,ge=e=>t=>l(t)&&e.test(t.nodeName),he=e=>l(e)&&3===e.nodeType,fe=e=>l(e)&&1===e.nodeType,ye=ge(/^(OL|UL|DL)$/),ve=ge(/^(OL|UL)$/),Ce=pe("ol"),be=ge(/^(LI|DT|DD)$/),Se=ge(/^(DT|DD)$/),Ne=ge(/^(TH|TD)$/),Le=pe("br"),Oe=(e,t)=>l(t)&&t.nodeName in e.schema.getTextBlockElements(),Ae=(e,t)=>l(e)&&e.nodeName in t,xe=(e,t)=>l(t)&&t.nodeName in e.schema.getVoidElements(),ke=(e,t,n)=>{const o=e.isEmpty(t);return!(n&&e.select("span[data-mce-type=bookmark]",t).length>0)&&o},Te=(e,t)=>e.isChildOf(t,e.getRoot()),Ee=e=>t=>t.options.get(e),we=Ee("lists_indent_on_tab"),De=Ee("forced_root_block"),Be=Ee("forced_root_block_attrs"),Ie=(e,t)=>{const n=e.dom,o=e.schema.getBlockElements(),r=n.createFragment(),s=De(e),i=Be(e);let l,a,d=!1;for(a=n.create(s,i),Ae(t.firstChild,o)||r.appendChild(a);l=t.firstChild;){const e=l.nodeName;d||"SPAN"===e&&"bookmark"===l.getAttribute("data-mce-type")||(d=!0),Ae(l,o)?(r.appendChild(l),a=null):(a||(a=n.create(s,i),r.appendChild(a)),a.appendChild(l))}return!d&&a&&a.appendChild(n.create("br",{"data-mce-bogus":"1"})),r},Pe=me.DOM,Me=F("dd"),Re=F("dt"),Ue=(e,t)=>{var n;Me(t)?ce(t,"dt"):Re(t)&&(n=t,g.from(n.dom.parentElement).map(U)).each((n=>((e,t,n)=>{const o=Pe.select('span[data-mce-type="bookmark"]',t),r=Ie(e,n),s=Pe.createRng();s.setStartAfter(n),s.setEndAfter(t);const i=s.extractContents();for(let t=i.firstChild;t;t=t.firstChild)if("LI"===t.nodeName&&e.dom.isEmpty(t)){Pe.remove(t);break}e.dom.isEmpty(i)||Pe.insertAfter(i,t),Pe.insertAfter(r,t);const l=n.parentElement;l&&ke(e.dom,l)&&(e=>{const t=e.parentNode;t&&ue.each(o,(e=>{t.insertBefore(e,n.parentNode)})),Pe.remove(e)})(l),Pe.remove(n),ke(e.dom,t)&&Pe.remove(t)})(e,n.dom,t.dom)))},$e=e=>{Re(e)&&ce(e,"dd")},_e=(e,t)=>{if(he(e))return{container:e,offset:t};const n=te.getNode(e,t);return he(n)?{container:n,offset:t>=e.childNodes.length?n.data.length:0}:n.previousSibling&&he(n.previousSibling)?{container:n.previousSibling,offset:n.previousSibling.data.length}:n.nextSibling&&he(n.nextSibling)?{container:n.nextSibling,offset:0}:{container:e,offset:t}},He=e=>{const t=e.cloneRange(),n=_e(e.startContainer,e.startOffset);t.setStart(n.container,n.offset);const o=_e(e.endContainer,e.endOffset);return t.setEnd(o.container,o.offset),t},Ve=["OL","UL","DL"],Fe=Ve.join(","),je=(e,t)=>{const n=t||e.selection.getStart(!0);return e.dom.getParent(n,Fe,Qe(e,n))},Ke=e=>{const t=e.selection.getSelectedBlocks();return N(((e,t)=>{const n=ue.map(t,(t=>e.dom.getParent(t,"li,dd,dt",Qe(e,t))||t));return D(n)})(e,t),be)},ze=(e,t)=>{const n=e.dom.getParents(t,"TD,TH");return n.length>0?n[0]:e.getBody()},Qe=(e,t)=>{const n=e.dom.getParents(t,e.dom.isBlock),o=A(n,(t=>{return n=e.schema,!ye(o=t)&&!be(o)&&C(Ve,(e=>n.isValidChild(o.nodeName,e)));var n,o}));return o.getOr(e.getBody())},We=(e,t)=>{const n=e.dom.getParents(t,"ol,ul",Qe(e,t));return w(n)},qe=(e,t)=>{const n=b(t,(t=>We(e,t).getOr(t)));return D(n)},Ze=e=>/\btox\-/.test(e.className),Ge=(e,t)=>O(e,ye,Ne).exists((e=>e.nodeName===t&&!Ze(e))),Je=(e,t)=>null!==t&&!e.dom.isEditable(t),Xe=(e,t)=>{const n=e.dom.getParent(t,"ol,ul,dl");return Je(e,n)},Ye=(e,t)=>{const n=e.selection.getNode();return t({parents:e.dom.getParents(n),element:n}),e.on("NodeChange",t),()=>e.off("NodeChange",t)},et=(e,t)=>{const n=(t||document).createDocumentFragment();return S(e,(e=>{n.appendChild(e.dom)})),U(n)},tt=(e,t,n)=>e.dispatch("ListMutation",{action:t,element:n}),nt=(ot=/^\s+|\s+$/g,e=>e.replace(ot,""));var ot;const rt=(e,t,n)=>{((e,t,n)=>{if(!o(n))throw console.error("Invalid call to CSS.set. Property ",t,":: Value ",n,":: Element ",e),new Error("CSS value must be a string: "+n);(e=>void 0!==e.style&&a(e.style.getPropertyValue))(e)&&e.style.setProperty(t,n)})(e.dom,t,n)},st=e=>((e,t)=>{const n=e.dom;if(1!==n.nodeType)return!1;{const e=n;if(void 0!==e.matches)return e.matches(t);if(void 0!==e.msMatchesSelector)return e.msMatchesSelector(t);if(void 0!==e.webkitMatchesSelector)return e.webkitMatchesSelector(t);if(void 0!==e.mozMatchesSelector)return e.mozMatchesSelector(t);throw new Error("Browser lacks native selectors")}})(e,"OL,UL"),it=e=>Q(e).exists(st),lt=e=>"listAttributes"in e,at=e=>"isComment"in e,dt=e=>e.depth>0,ct=e=>e.isSelected,mt=e=>{const t=K(e),n=W(e).exists(st)?t.slice(0,-1):t;return b(n,de)},ut=(e,t)=>{J(e.item,t.list)},pt=(e,t)=>{const n={list:R(t,e),item:R("li",e)};return J(n.list,n.item),n},gt=(e,t,n)=>{const o=t.slice(0,n.depth);return w(o).each((t=>{if(lt(n)){const o=((e,t,n)=>{const o=R("li",e);return le(o,t),X(o,n),o})(e,n.itemAttributes,n.content);((e,t)=>{J(e.list,t),e.item=t})(t,o),((e,t)=>{_(e.list)!==t.listType&&(e.list=ce(e.list,t.listType)),le(e.list,t.listAttributes)})(t,n)}else if((e=>"isInPreviousLi"in e)(n)){if(n.isInPreviousLi){const o=((e,t,n,o)=>{const r=R(o,e);return le(r,t),X(r,n),r})(e,n.attributes,n.content,n.type);J(t.item,o)}}else{const e=M(`\x3c!--${n.content}--\x3e`);J(t.list,e)}})),o},ht=(e,t)=>{let n=g.none();const o=L(t,((t,o,r)=>lt(o)?o.depth>t.length?((e,t,n)=>{const o=((e,t,n)=>{const o=[];for(let r=0;r{for(let t=1;t{for(let t=0;t{le(e.list,t.listAttributes),le(e.item,t.itemAttributes),X(e.item,t.content)}))})(o,n),r=o,I(w(t),E(r),ut),t.concat(o)})(e,t,o):gt(e,t,o):0===r&&at(o)?(n=g.some(o),t):gt(e,t,o)),[]);return n.each((e=>{const t=M(`\x3c!--${e.content}--\x3e`);E(o).each((e=>{((e,t)=>{Q(e).fold((()=>{J(e,t)}),(n=>{e.dom.insertBefore(t.dom,n.dom)}))})(e.list,t)}))})),E(o).map((e=>e.list))},ft=e=>(S(e,((t,n)=>{((e,t)=>{const n=e[t].depth,o=e=>e.depth===n&&!e.dirty,r=e=>e.depthO(e.slice(t+1),o,r)))})(e,n).fold((()=>{t.dirty&<(t)&&(e=>{e.listAttributes=((e,t)=>{const n={};var o;return((e,t,n,o)=>{ie(e,((e,r)=>{(t(e,r)?n:o)(e,r)}))})(e,t,(o=n,(e,t)=>{o[t]=e}),c),n})(e.listAttributes,((e,t)=>"start"!==t))})(t)}),(e=>{return o=e,void(lt(n=t)&<(o)&&(n.listType=o.listType,n.listAttributes={...o.listAttributes}));var n,o}))})),e),yt=(e,t,n,o)=>{var r,s;if(8===H(s=o)||"#comment"===_(s))return[{depth:e+1,content:null!==(r=o.dom.nodeValue)&&void 0!==r?r:"",dirty:!1,isSelected:!1,isComment:!0}];t.each((e=>{$(e.start,o)&&n.set(!0)}));const i=((e,t,n)=>j(e).filter(V).map((o=>({depth:t,dirty:!1,isSelected:n,content:mt(e),itemAttributes:ae(e),listAttributes:ae(o),listType:_(o),isInPreviousLi:!1}))))(o,e,n.get());t.each((e=>{$(e.end,o)&&n.set(!1)}));const l=W(o).filter(st).map((o=>Ct(e,t,n,o))).getOr([]);return i.toArray().concat(l)},vt=(e,t,n,o)=>Q(o).filter(st).fold((()=>yt(e,t,n,o)),(r=>{const s=L(K(o),((o,r,s)=>{if(0===s)return o;{const s=yt(e,t,n,r).map((e=>((e,t,n)=>lt(e)?{depth:e.depth,dirty:e.dirty,content:e.content,isSelected:e.isSelected,type:t,attributes:e.itemAttributes,isInPreviousLi:!0}:e)(e,r.dom.nodeName.toLowerCase())));return o.concat(s)}}),[]);return Ct(e,t,n,r).concat(s)})),Ct=(e,t,n,o)=>x(K(o),(o=>(st(o)?Ct:vt)(e+1,t,n,o))),bt=(e,t,n)=>{const o=((e,t)=>{const n=(e=>{let t=!1;return{get:()=>t,set:e=>{t=e}}})();return b(e,(e=>({sourceList:e,entries:Ct(0,t,n,e)})))})(t,(e=>{const t=b(Ke(e),U);return I(A(t,u(it)),A(k(t),u(it)),((e,t)=>({start:e,end:t})))})(e));S(o,(t=>{((e,t)=>{S(N(e,ct),(e=>((e,t)=>{switch(e){case"Indent":t.depth++;break;case"Outdent":t.depth--;break;case"Flatten":t.depth=0}t.dirty=!0})(t,e)))})(t.entries,n);const o=((e,t)=>x(((e,t)=>{if(0===e.length)return[];{let n=t(e[0]);const o=[];let r=[];for(let s=0,i=e.length;sE(t).exists(dt)?((e,t)=>{const n=ft(t);return ht(e.contentDocument,n).toArray()})(e,t):((e,t)=>{const n=ft(t);return b(n,(t=>{const n=at(t)?et([M(`\x3c!--${t.content}--\x3e`)]):et(t.content);return U(Ie(e,n.dom))}))})(e,t))))(e,t.entries);var r;S(o,(t=>{tt(e,"Indent"===n?"IndentList":"OutdentList",t.dom)})),r=t.sourceList,S(o,(e=>{G(r,e)})),ee(t.sourceList)}))},St=(e,t)=>{const n=re((e=>{const t=(e=>{const t=We(e,e.selection.getStart()),n=N(e.selection.getSelectedBlocks(),ve);return t.toArray().concat(n)})(e),n=(e=>{const t=e.selection.getStart();return e.dom.getParents(t,"ol,ul",Qe(e,t))})(e);return A(n,(e=>{return t=U(e),j(t).exists((e=>be(e.dom)&&Q(e).exists((e=>!ye(e.dom)))&&W(e).exists((e=>!ye(e.dom)))));var t})).fold((()=>qe(e,t)),(e=>[e]))})(e)),o=re((e=>N(Ke(e),Se))(e));let r=!1;if(n.length||o.length){const s=e.selection.getBookmark();bt(e,n,t),((e,t,n)=>{S(n,"Indent"===t?$e:t=>Ue(e,t))})(e,t,o),e.selection.moveToBookmark(s),e.selection.setRng(He(e.selection.getRng())),e.nodeChanged(),r=!0}return r},Nt=(e,t)=>!(e=>{const t=je(e);return Je(e,t)})(e)&&St(e,t),Lt=e=>Nt(e,"Indent"),Ot=e=>Nt(e,"Outdent"),At=e=>Nt(e,"Flatten"),xt=e=>"\ufeff"===e;var kt=tinymce.util.Tools.resolve("tinymce.dom.BookmarkManager");const Tt=me.DOM,Et=e=>{const t={},n=n=>{let o=e[n?"startContainer":"endContainer"],r=e[n?"startOffset":"endOffset"];if(fe(o)){const e=Tt.create("span",{"data-mce-type":"bookmark"});o.hasChildNodes()?(r=Math.min(r,o.childNodes.length-1),n?o.insertBefore(e,o.childNodes[r]):Tt.insertAfter(e,o.childNodes[r])):o.appendChild(e),o=e,r=0}t[n?"startContainer":"endContainer"]=o,t[n?"startOffset":"endOffset"]=r};return n(!0),e.collapsed||n(),t},wt=e=>{const t=t=>{let n=e[t?"startContainer":"endContainer"],o=e[t?"startOffset":"endOffset"];if(n){if(fe(n)&&n.parentNode){const e=n;o=(e=>{var t;let n=null===(t=e.parentNode)||void 0===t?void 0:t.firstChild,o=0;for(;n;){if(n===e)return o;fe(n)&&"bookmark"===n.getAttribute("data-mce-type")||o++,n=n.nextSibling}return-1})(n),n=n.parentNode,Tt.remove(e),!n.hasChildNodes()&&Tt.isBlock(n)&&n.appendChild(Tt.create("br"))}e[t?"startContainer":"endContainer"]=n,e[t?"startOffset":"endOffset"]=o}};t(!0),t();const n=Tt.createRng();return n.setStart(e.startContainer,e.startOffset),e.endContainer&&n.setEnd(e.endContainer,e.endOffset),He(n)},Dt=e=>{switch(e){case"UL":return"ToggleUlList";case"OL":return"ToggleOlList";case"DL":return"ToggleDLList"}},Bt=(e,t)=>{ue.each(t,((t,n)=>{e.setAttribute(n,t)}))},It=(e,t,n)=>{((e,t,n)=>{const o=n["list-style-type"]?n["list-style-type"]:null;e.setStyle(t,"list-style-type",o)})(e,t,n),((e,t,n)=>{Bt(t,n["list-attributes"]),ue.each(e.select("li",t),(e=>{Bt(e,n["list-item-attributes"])}))})(e,t,n)},Pt=(e,t)=>l(t)&&!Ae(t,e.schema.getBlockElements()),Mt=(e,t,n,o)=>{let r=t[n?"startContainer":"endContainer"];const s=t[n?"startOffset":"endOffset"];fe(r)&&(r=r.childNodes[Math.min(s,r.childNodes.length-1)]||r),!n&&Le(r.nextSibling)&&(r=r.nextSibling);const i=(t,n)=>{var r;const s=new ne(t,(t=>{for(;!e.dom.isBlock(t)&&t.parentNode&&o!==t;)t=t.parentNode;return t})(t)),i=n?"next":"prev";let l;for(;l=s[i]();)if(!xe(e,l)&&!xt(l.textContent)&&0!==(null===(r=l.textContent)||void 0===r?void 0:r.length))return g.some(l);return g.none()};if(n&&he(r))if(xt(r.textContent))r=i(r,!1).getOr(r);else for(null!==r.parentNode&&Pt(e,r.parentNode)&&(r=r.parentNode);null!==r.previousSibling&&(Pt(e,r.previousSibling)||he(r.previousSibling));)r=r.previousSibling;if(!n&&he(r))if(xt(r.textContent))r=i(r,!0).getOr(r);else for(null!==r.parentNode&&Pt(e,r.parentNode)&&(r=r.parentNode);null!==r.nextSibling&&(Pt(e,r.nextSibling)||he(r.nextSibling));)r=r.nextSibling;for(;r.parentNode!==o;){const t=r.parentNode;if(Oe(e,r))return r;if(/^(TD|TH)$/.test(t.nodeName))return r;r=t}return r},Rt=(e,t,n)=>{const o=e.selection.getRng();let r="LI";const s=Qe(e,((e,t)=>{const n=e.selection.getStart(!0),o=Mt(e,t,!0,e.getBody());return r=U(o),s=U(t.commonAncestorContainer),i=r,l=function(e,...t){return(...n)=>{const o=t.concat(n);return e.apply(null,o)}}($,s),q(i,l,void 0).isSome()?t.commonAncestorContainer:n;var r,s,i,l})(e,o)),i=e.dom;if("false"===i.getContentEditable(e.selection.getNode()))return;"DL"===(t=t.toUpperCase())&&(r="DT");const l=Et(o),a=N(((e,t,n)=>{const o=[],r=e.dom,s=Mt(e,t,!0,n),i=Mt(e,t,!1,n);let l;const a=[];for(let e=s;e&&(a.push(e),e!==i);e=e.nextSibling);return ue.each(a,(t=>{var s;if(Oe(e,t))return o.push(t),void(l=null);if(r.isBlock(t)||Le(t))return Le(t)&&r.remove(t),void(l=null);const i=t.nextSibling;kt.isBookmarkNode(t)&&(ye(i)||Oe(e,i)||!i&&t.parentNode===n)?l=null:(l||(l=r.create("p"),null===(s=t.parentNode)||void 0===s||s.insertBefore(l,t),o.push(l)),l.appendChild(t))})),o})(e,o,s),e.dom.isEditable);ue.each(a,(o=>{let s;const l=o.previousSibling,a=o.parentNode;be(a)||(l&&ye(l)&&l.nodeName===t&&((e,t,n)=>{const o=e.getStyle(t,"list-style-type");let r=n?n["list-style-type"]:"";return r=null===r?"":r,o===r})(i,l,n)?(s=l,o=i.rename(o,r),l.appendChild(o)):(s=i.create(t),a.insertBefore(s,o),s.appendChild(o),o=i.rename(o,r)),((e,t,n)=>{ue.each(["margin","margin-right","margin-bottom","margin-left","margin-top","padding","padding-right","padding-bottom","padding-left","padding-top"],(n=>e.setStyle(t,n,"")))})(i,o),It(i,s,n),$t(e.dom,s))})),e.selection.setRng(wt(l))},Ut=(e,t,n)=>{return((e,t)=>ye(e)&&e.nodeName===(null==t?void 0:t.nodeName))(t,n)&&((e,t,n)=>e.getStyle(t,"list-style-type",!0)===e.getStyle(n,"list-style-type",!0))(e,t,n)&&(o=n,t.className===o.className);var o},$t=(e,t)=>{let n,o=t.nextSibling;if(Ut(e,t,o)){const r=o;for(;n=r.firstChild;)t.appendChild(n);e.remove(r)}if(o=t.previousSibling,Ut(e,t,o)){const r=o;for(;n=r.lastChild;)t.insertBefore(n,t.firstChild);e.remove(r)}},_t=(e,t,n,o)=>{if(t.nodeName!==n){const r=e.dom.rename(t,n);It(e.dom,r,o),tt(e,Dt(n),r)}else It(e.dom,t,o),tt(e,Dt(n),t)},Ht=(e,t,n,o)=>{if(t.classList.forEach(((e,n,o)=>{e.startsWith("tox-")&&(o.remove(e),0===o.length&&t.removeAttribute("class"))})),t.nodeName!==n){const r=e.dom.rename(t,n);It(e.dom,r,o),tt(e,Dt(n),r)}else It(e.dom,t,o),tt(e,Dt(n),t)},Vt=e=>"list-style-type"in e,Ft=(e,t,n)=>{const o=je(e);if(Xe(e,o))return;const s=(e=>{const t=je(e),n=e.selection.getSelectedBlocks();return((e,t)=>l(e)&&1===t.length&&t[0]===e)(t,n)?(e=>N(e.querySelectorAll(Fe),ye))(t):N(n,(e=>ye(e)&&t!==e))})(e),i=r(n)?n:{};s.length>0?((e,t,n,o,r)=>{const s=ye(t);if(!s||t.nodeName!==o||Vt(r)||Ze(t)){Rt(e,o,r);const i=Et(e.selection.getRng()),l=s?[t,...n]:n,a=s&&Ze(t)?Ht:_t;ue.each(l,(t=>{a(e,t,o,r)})),e.selection.setRng(wt(i))}else At(e)})(e,o,s,t,i):((e,t,n,o)=>{if(t!==e.getBody())if(t)if(t.nodeName!==n||Vt(o)||Ze(t)){const r=Et(e.selection.getRng());Ze(t)&&t.classList.forEach(((e,n,o)=>{e.startsWith("tox-")&&(o.remove(e),0===o.length&&t.removeAttribute("class"))})),It(e.dom,t,o);const s=e.dom.rename(t,n);$t(e.dom,s),e.selection.setRng(wt(r)),Rt(e,n,o),tt(e,Dt(n),s)}else At(e);else Rt(e,n,o),tt(e,Dt(n),t)})(e,o,t,i)},jt=me.DOM,Kt=(e,t)=>{const n=ue.grep(e.select("ol,ul",t));ue.each(n,(t=>{((e,t)=>{const n=t.parentElement;if(n&&"LI"===n.nodeName&&n.firstChild===t){const o=n.previousSibling;o&&"LI"===o.nodeName?(o.appendChild(t),ke(e,n)&&jt.remove(n)):jt.setStyle(n,"listStyleType","none")}if(ye(n)){const e=n.previousSibling;e&&"LI"===e.nodeName&&e.appendChild(t)}})(e,t)}))},zt=(e,t,n,o)=>{let r=t.startContainer;const s=t.startOffset;if(he(r)&&(n?s0))return r;const i=e.schema.getNonEmptyElements();fe(r)&&(r=te.getNode(r,s));const l=new ne(r,o);n&&((e,t)=>!!Le(t)&&e.isBlock(t.nextSibling)&&!Le(t.previousSibling))(e.dom,r)&&l.next();const a=n?l.next.bind(l):l.prev2.bind(l);for(;r=a();){if("LI"===r.nodeName&&!r.hasChildNodes())return r;if(i[r.nodeName])return r;if(he(r)&&r.data.length>0)return r}return null},Qt=(e,t)=>{const n=t.childNodes;return 1===n.length&&!ye(n[0])&&e.isBlock(n[0])},Wt=(e,t,n)=>{let o;const r=t.parentNode;if(!Te(e,t)||!Te(e,n))return;ye(n.lastChild)&&(o=n.lastChild),r===n.lastChild&&Le(r.previousSibling)&&e.remove(r.previousSibling);const s=n.lastChild;s&&Le(s)&&t.hasChildNodes()&&e.remove(s),ke(e,n,!0)&&Y(U(n)),((e,t,n)=>{let o;const r=Qt(e,n)?n.firstChild:n;if(((e,t)=>{Qt(e,t)&&e.remove(t.firstChild,!0)})(e,t),!ke(e,t,!0))for(;o=t.firstChild;)r.appendChild(o)})(e,t,n),o&&n.appendChild(o);const i=((e,t)=>{const n=e.dom,o=t.dom;return n!==o&&n.contains(o)})(U(n),U(t))?e.getParents(t,ye,n):[];e.remove(t),S(i,(t=>{ke(e,t)&&t!==e.getRoot()&&e.remove(t)}))},qt=(e,t)=>{const n=e.dom,o=e.selection,r=o.getStart(),s=ze(e,r),i=n.getParent(o.getStart(),"LI",s);if(i){const r=i.parentElement;if(r===e.getBody()&&ke(n,r))return!0;const l=He(o.getRng()),a=n.getParent(zt(e,l,t,s),"LI",s);if(a&&a!==i)return e.undoManager.transact((()=>{var n,o;t?((e,t,n,o)=>{const r=e.dom;if(r.isEmpty(o))((e,t,n)=>{Y(U(n)),Wt(e.dom,t,n),e.selection.setCursorLocation(n,0)})(e,n,o);else{const s=Et(t);Wt(r,n,o),e.selection.setRng(wt(s))}})(e,l,a,i):(null===(o=(n=i).parentNode)||void 0===o?void 0:o.firstChild)===n?Ot(e):((e,t,n,o)=>{const r=Et(t);Wt(e.dom,n,o);const s=wt(r);e.selection.setRng(s)})(e,l,i,a)})),!0;if(!a&&!t&&0===l.startOffset&&0===l.endOffset)return e.undoManager.transact((()=>{At(e)})),!0}return!1},Zt=e=>{const t=e.selection.getStart(),n=ze(e,t);return e.dom.getParent(t,"LI,DT,DD",n)||Ke(e).length>0},Gt=(e,t)=>{const n=e.selection;return!Xe(e,n.getNode())&&(n.isCollapsed()?((e,t)=>qt(e,t)||((e,t)=>{const n=e.dom,o=e.selection.getStart(),r=ze(e,o),s=n.getParent(o,n.isBlock,r);if(s&&n.isEmpty(s)){const o=He(e.selection.getRng()),i=n.getParent(zt(e,o,t,r),"LI",r);if(i){const l=e=>v(["td","th","caption"],_(e)),a=e=>e.dom===r;return!!((e,t,n=m)=>I(e,t,n).getOr(e.isNone()&&t.isNone()))(Z(U(i),l,a),Z(U(o.startContainer),l,a),$)&&(e.undoManager.transact((()=>{const o=i.parentNode;((e,t,n)=>{const o=e.getParent(t.parentNode,e.isBlock,n);e.remove(t),o&&e.isEmpty(o)&&e.remove(o)})(n,s,r),$t(n,o),e.selection.select(i,!0),e.selection.collapse(t)})),!0)}}return!1})(e,t))(e,t):(e=>!!Zt(e)&&(e.undoManager.transact((()=>{e.execCommand("Delete"),Kt(e.dom,e.getBody())})),!0))(e))},Jt=e=>{const t=k(nt(e).split("")),n=b(t,((e,t)=>{const n=e.toUpperCase().charCodeAt(0)-"A".charCodeAt(0)+1;return Math.pow(26,t)*n}));return L(n,((e,t)=>e+t),0)},Xt=e=>{if(--e<0)return"";{const t=e%26,n=Math.floor(e/26);return Xt(n)+String.fromCharCode("A".charCodeAt(0)+t)}},Yt=e=>{const t=parseInt(e.start,10);return B(e.listStyleType,"upper-alpha")?Xt(t):B(e.listStyleType,"lower-alpha")?Xt(t).toLowerCase():e.start},en=(e,t)=>()=>{const n=je(e);return l(n)&&n.nodeName===t},tn=e=>{e.addCommand("mceListProps",(()=>{(e=>{const t=je(e);Ce(t)&&!Xe(e,t)&&e.windowManager.open({title:"List Properties",body:{type:"panel",items:[{type:"input",name:"start",label:"Start list at number",inputMode:"numeric"}]},initialData:{start:Yt({start:e.dom.getAttrib(t,"start","1"),listStyleType:g.from(e.dom.getStyle(t,"list-style-type"))})},buttons:[{type:"cancel",name:"cancel",text:"Cancel"},{type:"submit",name:"save",text:"Save",primary:!0}],onSubmit:t=>{(e=>{switch((e=>/^[0-9]+$/.test(e)?2:/^[A-Z]+$/.test(e)?0:/^[a-z]+$/.test(e)?1:e.length>0?4:3)(e)){case 2:return g.some({listStyleType:g.none(),start:e});case 0:return g.some({listStyleType:g.some("upper-alpha"),start:Jt(e).toString()});case 1:return g.some({listStyleType:g.some("lower-alpha"),start:Jt(e).toString()});case 3:return g.some({listStyleType:g.none(),start:""});case 4:return g.none()}})(t.getData().start).each((t=>{e.execCommand("mceListUpdate",!1,{attrs:{start:"1"===t.start?"":t.start},styles:{"list-style-type":t.listStyleType.getOr("")}})})),t.close()}})})(e)}))};var nn=tinymce.util.Tools.resolve("tinymce.html.Node");const on=e=>3===e.type,rn=e=>0===e.length,sn=e=>{const t=(t,n)=>{const o=nn.create("li");S(t,(e=>o.append(e))),n?e.insert(o,n,!0):e.append(o)},n=L(e.children(),((e,n)=>on(n)?[...e,n]:rn(e)||on(n)?e:(t(e,n),[])),[]);rn(n)||t(n)},ln=(e,t)=>n=>(n.setEnabled(e.selection.isEditable()),Ye(e,(o=>{n.setActive(Ge(o.parents,t)),n.setEnabled(!Xe(e,o.element)&&e.selection.isEditable())}))),an=(e,t)=>n=>Ye(e,(o=>n.setEnabled(Ge(o.parents,t)&&!Xe(e,o.element))));e.add("lists",(e=>((e=>{(0,e.options.register)("lists_indent_on_tab",{processor:"boolean",default:!0})})(e),(e=>{e.on("PreInit",(()=>{const{parser:t}=e;t.addNodeFilter("ul,ol",(e=>S(e,sn)))}))})(e),e.hasPlugin("rtc",!0)?tn(e):((e=>{we(e)&&(e=>{e.on("keydown",(t=>{t.keyCode!==oe.TAB||oe.metaKeyPressed(t)||e.undoManager.transact((()=>{(t.shiftKey?Ot(e):Lt(e))&&t.preventDefault()}))}))})(e),(e=>{e.on("ExecCommand",(t=>{const n=t.command.toLowerCase();"delete"!==n&&"forwarddelete"!==n||!Zt(e)||Kt(e.dom,e.getBody())})),e.on("keydown",(t=>{t.keyCode===oe.BACKSPACE?Gt(e,!1)&&t.preventDefault():t.keyCode===oe.DELETE&&Gt(e,!0)&&t.preventDefault()}))})(e)})(e),(e=>{e.on("BeforeExecCommand",(t=>{const n=t.command.toLowerCase();"indent"===n?Lt(e):"outdent"===n&&Ot(e)})),e.addCommand("InsertUnorderedList",((t,n)=>{Ft(e,"UL",n)})),e.addCommand("InsertOrderedList",((t,n)=>{Ft(e,"OL",n)})),e.addCommand("InsertDefinitionList",((t,n)=>{Ft(e,"DL",n)})),e.addCommand("RemoveList",(()=>{At(e)})),tn(e),e.addCommand("mceListUpdate",((t,n)=>{r(n)&&((e,t)=>{const n=je(e);null===n||Xe(e,n)||e.undoManager.transact((()=>{r(t.styles)&&e.dom.setStyles(n,t.styles),r(t.attrs)&&ie(t.attrs,((t,o)=>e.dom.setAttrib(n,o,t)))}))})(e,n)})),e.addQueryStateHandler("InsertUnorderedList",en(e,"UL")),e.addQueryStateHandler("InsertOrderedList",en(e,"OL")),e.addQueryStateHandler("InsertDefinitionList",en(e,"DL"))})(e)),(e=>{const t=t=>()=>e.execCommand(t);e.hasPlugin("advlist")||(e.ui.registry.addToggleButton("numlist",{icon:"ordered-list",active:!1,tooltip:"Numbered list",onAction:t("InsertOrderedList"),onSetup:ln(e,"OL")}),e.ui.registry.addToggleButton("bullist",{icon:"unordered-list",active:!1,tooltip:"Bullet list",onAction:t("InsertUnorderedList"),onSetup:ln(e,"UL")}))})(e),(e=>{const t={text:"List properties...",icon:"ordered-list",onAction:()=>e.execCommand("mceListProps"),onSetup:an(e,"OL")};e.ui.registry.addMenuItem("listprops",t),e.ui.registry.addContextMenu("lists",{update:t=>{const n=je(e,t);return Ce(n)?["listprops"]:[]}})})(e),(e=>({backspaceDelete:t=>{Gt(e,t)}}))(e))))}(); \ No newline at end of file +!function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager");const t=e=>t=>(e=>{const t=typeof e;return null===e?"null":"object"===t&&Array.isArray(e)?"array":"object"===t&&(n=o=e,(r=String).prototype.isPrototypeOf(n)||(null===(s=o.constructor)||void 0===s?void 0:s.name)===r.name)?"string":t;var n,o,r,s})(t)===e,n=e=>t=>typeof t===e,o=t("string"),r=t("object"),s=t("array"),i=n("boolean"),l=e=>!(e=>null==e)(e),a=n("function"),d=n("number"),c=()=>{},m=e=>()=>e,u=(e,t)=>e===t,p=e=>t=>!e(t),g=m(!1);class h{constructor(e,t){this.tag=e,this.value=t}static some(e){return new h(!0,e)}static none(){return h.singletonNone}fold(e,t){return this.tag?t(this.value):e()}isSome(){return this.tag}isNone(){return!this.tag}map(e){return this.tag?h.some(e(this.value)):h.none()}bind(e){return this.tag?e(this.value):h.none()}exists(e){return this.tag&&e(this.value)}forall(e){return!this.tag||e(this.value)}filter(e){return!this.tag||e(this.value)?this:h.none()}getOr(e){return this.tag?this.value:e}or(e){return this.tag?this:e}getOrThunk(e){return this.tag?this.value:e()}orThunk(e){return this.tag?this:e()}getOrDie(e){if(this.tag)return this.value;throw new Error(null!=e?e:"Called getOrDie on None")}static from(e){return l(e)?h.some(e):h.none()}getOrNull(){return this.tag?this.value:null}getOrUndefined(){return this.value}each(e){this.tag&&e(this.value)}toArray(){return this.tag?[this.value]:[]}toString(){return this.tag?`some(${this.value})`:"none()"}}h.singletonNone=new h(!1);const f=Array.prototype.slice,y=Array.prototype.indexOf,v=Array.prototype.push,C=(e,t)=>{return n=e,o=t,y.call(n,o)>-1;var n,o},b=(e,t)=>{for(let n=0,o=e.length;n{const n=e.length,o=new Array(n);for(let r=0;r{for(let n=0,o=e.length;n{const n=[];for(let o=0,r=e.length;o(S(e,((e,o)=>{n=t(n,e,o)})),n),A=(e,t,n)=>{for(let o=0,r=e.length;oA(e,t,g),x=(e,t)=>(e=>{const t=[];for(let n=0,o=e.length;n{const t=f.call(e,0);return t.reverse(),t},w=(e,t)=>t>=0&&tw(e,0),D=e=>w(e,e.length-1),B=(e,t)=>{const n=[],o=a(t)?e=>b(n,(n=>t(n,e))):e=>C(n,e);for(let t=0,r=e.length;te.exists((e=>n(e,t))),P=(e,t,n)=>e.isSome()&&t.isSome()?h.some(n(e.getOrDie(),t.getOrDie())):h.none(),I=e=>{if(null==e)throw new Error("Node cannot be null or undefined");return{dom:e}},R=(e,t)=>{const n=(t||document).createElement("div");if(n.innerHTML=e,!n.hasChildNodes()||n.childNodes.length>1){const t="HTML does not have a single root node";throw console.error(t,e),new Error(t)}return I(n.childNodes[0])},U=(e,t)=>{const n=(t||document).createElement(e);return I(n)},$=I,_=(e,t)=>{const n=e.dom;if(1!==n.nodeType)return!1;{const e=n;if(void 0!==e.matches)return e.matches(t);if(void 0!==e.msMatchesSelector)return e.msMatchesSelector(t);if(void 0!==e.webkitMatchesSelector)return e.webkitMatchesSelector(t);if(void 0!==e.mozMatchesSelector)return e.mozMatchesSelector(t);throw new Error("Browser lacks native selectors")}},H=(e,t)=>e.dom===t.dom,F=_,V="undefined"!=typeof window?window:Function("return this;")(),j=(e,t)=>((e,t)=>{let n=null!=t?t:V;for(let t=0;t{const t=j("ownerDocument.defaultView",e);return r(e)&&((e=>((e,t)=>{const n=((e,t)=>j(e,t))(e,t);if(null==n)throw new Error(e+" not available on this browser");return n})("HTMLElement",e))(t).prototype.isPrototypeOf(e)||/^HTML\w*Element$/.test(K(e).constructor.name))},Q=e=>e.dom.nodeName.toLowerCase(),W=e=>e.dom.nodeType,q=e=>t=>W(t)===e,Z=e=>G(e)&&z(e.dom),G=q(1),J=q(3),X=q(9),Y=q(11),ee=e=>t=>G(t)&&Q(t)===e,te=e=>h.from(e.dom.parentNode).map($),ne=e=>N(e.dom.childNodes,$),oe=(e,t)=>{const n=e.dom.childNodes;return h.from(n[t]).map($)},re=e=>oe(e,0),se=e=>oe(e,e.dom.childNodes.length-1),ie=a(Element.prototype.attachShadow)&&a(Node.prototype.getRootNode)?e=>$(e.dom.getRootNode()):e=>X(e)?e:$(e.dom.ownerDocument),le=e=>$(e.dom.host),ae=e=>{const t=J(e)?e.dom.parentNode:e.dom;if(null==t||null===t.ownerDocument)return!1;const n=t.ownerDocument;return(e=>{const t=ie(e);return Y(n=t)&&l(n.dom.host)?h.some(t):h.none();var n})($(t)).fold((()=>n.body.contains(t)),(o=ae,r=le,e=>o(r(e))));var o,r};var de=(e,t,n,o,r)=>e(n,o)?h.some(n):a(r)&&r(n)?h.none():t(n,o,r);const ce=(e,t,n)=>{let o=e.dom;const r=a(n)?n:g;for(;o.parentNode;){o=o.parentNode;const e=$(o);if(t(e))return h.some(e);if(r(e))break}return h.none()},me=(e,t,n)=>de(((e,t)=>t(e)),ce,e,t,n),ue=(e,t,n)=>ce(e,(e=>_(e,t)),n),pe=(e,t)=>{te(e).each((n=>{n.dom.insertBefore(t.dom,e.dom)}))},ge=(e,t)=>{e.dom.appendChild(t.dom)},he=(e,t)=>{S(t,(t=>{ge(e,t)}))},fe=e=>{e.dom.textContent="",S(ne(e),(e=>{ye(e)}))},ye=e=>{const t=e.dom;null!==t.parentNode&&t.parentNode.removeChild(t)};var ve=tinymce.util.Tools.resolve("tinymce.dom.RangeUtils"),Ce=tinymce.util.Tools.resolve("tinymce.dom.TreeWalker"),be=tinymce.util.Tools.resolve("tinymce.util.VK");const Ne=e=>N(e,$),Se=Object.keys,Le=(e,t)=>{const n=Se(e);for(let o=0,r=n.length;o{const n=e.dom;Le(t,((e,t)=>{((e,t,n)=>{if(!(o(n)||i(n)||d(n)))throw console.error("Invalid call to Attribute.set. Key ",t,":: Value ",n,":: Element ",e),new Error("Attribute value was not simple");e.setAttribute(t,n+"")})(n,t,e)}))},Ae=e=>O(e.dom.attributes,((e,t)=>(e[t.name]=t.value,e)),{}),Te=e=>((e,t)=>$(e.dom.cloneNode(!0)))(e),xe=(e,t)=>{const n=((e,t)=>{const n=U(t),o=Ae(e);return Oe(n,o),n})(e,t);var o,r;r=n,(e=>h.from(e.dom.nextSibling).map($))(o=e).fold((()=>{te(o).each((e=>{ge(e,r)}))}),(e=>{pe(e,r)}));const s=ne(e);return he(n,s),ye(e),n};var Ee=tinymce.util.Tools.resolve("tinymce.dom.DOMUtils"),we=tinymce.util.Tools.resolve("tinymce.util.Tools");const ke=e=>t=>l(t)&&t.nodeName.toLowerCase()===e,De=e=>t=>l(t)&&e.test(t.nodeName),Be=e=>l(e)&&3===e.nodeType,Me=e=>l(e)&&1===e.nodeType,Pe=De(/^(OL|UL|DL)$/),Ie=De(/^(OL|UL)$/),Re=ke("ol"),Ue=De(/^(LI|DT|DD)$/),$e=De(/^(DT|DD)$/),_e=De(/^(TH|TD)$/),He=ke("br"),Fe=(e,t)=>l(t)&&t.nodeName in e.schema.getTextBlockElements(),Ve=(e,t)=>l(e)&&e.nodeName in t,je=(e,t)=>l(t)&&t.nodeName in e.schema.getVoidElements(),Ke=(e,t,n)=>{const o=e.isEmpty(t);return!(n&&e.select("span[data-mce-type=bookmark]",t).length>0)&&o},ze=(e,t)=>e.isChildOf(t,e.getRoot()),Qe=e=>t=>t.options.get(e),We=Qe("lists_indent_on_tab"),qe=Qe("forced_root_block"),Ze=Qe("forced_root_block_attrs"),Ge=(e,t)=>{const n=e.dom,o=e.schema.getBlockElements(),r=n.createFragment(),s=qe(e),i=Ze(e);let l,a,d=!1;for(a=n.create(s,i),Ve(t.firstChild,o)||r.appendChild(a);l=t.firstChild;){const e=l.nodeName;d||"SPAN"===e&&"bookmark"===l.getAttribute("data-mce-type")||(d=!0),Ve(l,o)?(r.appendChild(l),a=null):(a||(a=n.create(s,i),r.appendChild(a)),a.appendChild(l))}return!d&&a&&a.appendChild(n.create("br",{"data-mce-bogus":"1"})),r},Je=Ee.DOM,Xe=ee("dd"),Ye=ee("dt"),et=(e,t)=>{var n;Xe(t)?xe(t,"dt"):Ye(t)&&(n=t,h.from(n.dom.parentElement).map($)).each((n=>((e,t,n)=>{const o=Je.select('span[data-mce-type="bookmark"]',t),r=Ge(e,n),s=Je.createRng();s.setStartAfter(n),s.setEndAfter(t);const i=s.extractContents();for(let t=i.firstChild;t;t=t.firstChild)if("LI"===t.nodeName&&e.dom.isEmpty(t)){Je.remove(t);break}e.dom.isEmpty(i)||Je.insertAfter(i,t),Je.insertAfter(r,t);const l=n.parentElement;l&&Ke(e.dom,l)&&(e=>{const t=e.parentNode;t&&we.each(o,(e=>{t.insertBefore(e,n.parentNode)})),Je.remove(e)})(l),Je.remove(n),Ke(e.dom,t)&&Je.remove(t)})(e,n.dom,t.dom)))},tt=e=>{Ye(e)&&xe(e,"dd")},nt=(e,t)=>{if(Be(e))return{container:e,offset:t};const n=ve.getNode(e,t);return Be(n)?{container:n,offset:t>=e.childNodes.length?n.data.length:0}:n.previousSibling&&Be(n.previousSibling)?{container:n.previousSibling,offset:n.previousSibling.data.length}:n.nextSibling&&Be(n.nextSibling)?{container:n.nextSibling,offset:0}:{container:e,offset:t}},ot=e=>{const t=e.cloneRange(),n=nt(e.startContainer,e.startOffset);t.setStart(n.container,n.offset);const o=nt(e.endContainer,e.endOffset);return t.setEnd(o.container,o.offset),t},rt=["OL","UL","DL"],st=rt.join(","),it=(e,t)=>{const n=t||e.selection.getStart(!0);return e.dom.getParent(n,st,dt(e,n))},lt=e=>{const t=e.selection.getSelectedBlocks();return L(((e,t)=>{const n=we.map(t,(t=>e.dom.getParent(t,"li,dd,dt",dt(e,t))||t));return B(n)})(e,t),Ue)},at=(e,t)=>{const n=e.dom.getParents(t,"TD,TH");return n.length>0?n[0]:e.getBody()},dt=(e,t)=>{const n=e.dom.getParents(t,e.dom.isBlock),o=T(n,(t=>{return n=e.schema,!Pe(o=t)&&!Ue(o)&&b(rt,(e=>n.isValidChild(o.nodeName,e)));var n,o}));return o.getOr(e.getBody())},ct=(e,t)=>{const n=e.dom.getParents(t,"ol,ul",dt(e,t));return D(n)},mt=(e,t)=>{const n=N(t,(t=>ct(e,t).getOr(t)));return B(n)},ut=e=>/\btox\-/.test(e.className),pt=(e,t)=>A(e,Pe,_e).exists((e=>e.nodeName===t&&!ut(e))),gt=(e,t)=>null!==t&&!e.dom.isEditable(t),ht=(e,t)=>{const n=e.dom.getParent(t,"ol,ul,dl");return gt(e,n)},ft=(e,t)=>{const n=e.selection.getNode();return t({parents:e.dom.getParents(n),element:n}),e.on("NodeChange",t),()=>e.off("NodeChange",t)},yt=(e,t)=>{const n=(t||document).createDocumentFragment();return S(e,(e=>{n.appendChild(e.dom)})),$(n)},vt=(e,t,n)=>e.dispatch("ListMutation",{action:t,element:n}),Ct=(bt=/^\s+|\s+$/g,e=>e.replace(bt,""));var bt;const Nt=(e,t,n)=>{((e,t,n)=>{if(!o(n))throw console.error("Invalid call to CSS.set. Property ",t,":: Value ",n,":: Element ",e),new Error("CSS value must be a string: "+n);(e=>void 0!==e.style&&a(e.style.getPropertyValue))(e)&&e.style.setProperty(t,n)})(e.dom,t,n)},St=e=>F(e,"OL,UL"),Lt=e=>re(e).exists(St),Ot=e=>"listAttributes"in e,At=e=>"isComment"in e,Tt=e=>e.depth>0,xt=e=>e.isSelected,Et=e=>{const t=ne(e),n=se(e).exists(St)?t.slice(0,-1):t;return N(n,Te)},wt=(e,t)=>{ge(e.item,t.list)},kt=(e,t)=>{const n={list:U(t,e),item:U("li",e)};return ge(n.list,n.item),n},Dt=(e,t,n)=>{const o=t.slice(0,n.depth);return D(o).each((t=>{if(Ot(n)){const o=((e,t,n)=>{const o=U("li",e);return Oe(o,t),he(o,n),o})(e,n.itemAttributes,n.content);((e,t)=>{ge(e.list,t),e.item=t})(t,o),((e,t)=>{Q(e.list)!==t.listType&&(e.list=xe(e.list,t.listType)),Oe(e.list,t.listAttributes)})(t,n)}else if((e=>"isFragment"in e)(n))he(t.item,n.content);else{const e=R(`\x3c!--${n.content}--\x3e`);ge(t.list,e)}})),o},Bt=(e,t)=>{let n=h.none();const o=O(t,((t,o,r)=>At(o)?0===r?(n=h.some(o),t):Dt(e,t,o):o.depth>t.length?((e,t,n)=>{const o=((e,t,n)=>{const o=[];for(let r=0;r{for(let t=1;t{for(let t=0;t{Ot(t)&&(Oe(e.list,t.listAttributes),Oe(e.item,t.itemAttributes)),he(e.item,t.content)}))})(o,n),r=o,P(D(t),k(r),wt),t.concat(o)})(e,t,o):Dt(e,t,o)),[]);return n.each((e=>{const t=R(`\x3c!--${e.content}--\x3e`);k(o).each((e=>{((e,t)=>{re(e).fold((()=>{ge(e,t)}),(n=>{e.dom.insertBefore(t.dom,n.dom)}))})(e.list,t)}))})),k(o).map((e=>e.list))},Mt=e=>(S(e,((t,n)=>{((e,t)=>{const n=e[t].depth,o=e=>e.depth===n&&!e.dirty,r=e=>e.depthA(e.slice(t+1),o,r)))})(e,n).fold((()=>{t.dirty&&Ot(t)&&(e=>{e.listAttributes=((e,t)=>{const n={};var o;return((e,t,n,o)=>{Le(e,((e,r)=>{(t(e,r)?n:o)(e,r)}))})(e,t,(o=n,(e,t)=>{o[t]=e}),c),n})(e.listAttributes,((e,t)=>"start"!==t))})(t)}),(e=>{return o=e,void(Ot(n=t)&&Ot(o)&&(n.listType=o.listType,n.listAttributes={...o.listAttributes}));var n,o}))})),e),Pt=(e,t,n,o)=>{var r,s;if(8===W(s=o)||"#comment"===Q(s))return[{depth:e+1,content:null!==(r=o.dom.nodeValue)&&void 0!==r?r:"",dirty:!1,isSelected:!1,isComment:!0}];t.each((e=>{H(e.start,o)&&n.set(!0)}));const i=((e,t,n)=>te(e).filter(G).map((o=>({depth:t,dirty:!1,isSelected:n,content:Et(e),itemAttributes:Ae(e),listAttributes:Ae(o),listType:Q(o),isInPreviousLi:!1}))))(o,e,n.get());t.each((e=>{H(e.end,o)&&n.set(!1)}));const l=se(o).filter(St).map((o=>Rt(e,t,n,o))).getOr([]);return i.toArray().concat(l)},It=(e,t,n,o)=>re(o).filter(St).fold((()=>Pt(e,t,n,o)),(r=>{const s=O(ne(o),((o,s,i)=>{if(0===i)return o;if(F(s,"LI"))return o.concat(Pt(e,t,n,s));{const t={isFragment:!0,depth:e,content:[s],isSelected:!1,dirty:!1,parentListType:Q(r)};return o.concat(t)}}),[]);return Rt(e,t,n,r).concat(s)})),Rt=(e,t,n,o)=>x(ne(o),(o=>(St(o)?Rt:It)(e+1,t,n,o))),Ut=(e,t,n)=>{const o=((e,t)=>{const n=(e=>{let t=!1;return{get:()=>t,set:e=>{t=e}}})();return N(e,(e=>({sourceList:e,entries:Rt(0,t,n,e)})))})(t,(e=>{const t=N(lt(e),$);return P(T(t,p(Lt)),T(E(t),p(Lt)),((e,t)=>({start:e,end:t})))})(e));S(o,(t=>{((e,t)=>{S(L(e,xt),(e=>((e,t)=>{switch(e){case"Indent":t.depth++;break;case"Outdent":t.depth--;break;case"Flatten":t.depth=0}t.dirty=!0})(t,e)))})(t.entries,n);const o=((e,t)=>x(((e,t)=>{if(0===e.length)return[];{let n=t(e[0]);const o=[];let r=[];for(let s=0,i=e.length;sk(t).exists(Tt)?((e,t)=>{const n=Mt(t);return Bt(e.contentDocument,n).toArray()})(e,t):((e,t)=>{const n=Mt(t);return N(n,(t=>{const n=At(t)?yt([R(`\x3c!--${t.content}--\x3e`)]):yt(t.content);return $(Ge(e,n.dom))}))})(e,t))))(e,t.entries);var r;S(o,(t=>{vt(e,"Indent"===n?"IndentList":"OutdentList",t.dom)})),r=t.sourceList,S(o,(e=>{pe(r,e)})),ye(t.sourceList)}))},$t=(e,t)=>{const n=Ne((e=>{const t=(e=>{const t=ct(e,e.selection.getStart()),n=L(e.selection.getSelectedBlocks(),Ie);return t.toArray().concat(n)})(e),n=(e=>{const t=e.selection.getStart();return e.dom.getParents(t,"ol,ul",dt(e,t))})(e);return T(n,(e=>{return t=$(e),te(t).exists((e=>Ue(e.dom)&&re(e).exists((e=>!Pe(e.dom)))&&se(e).exists((e=>!Pe(e.dom)))));var t})).fold((()=>mt(e,t)),(e=>[e]))})(e)),o=Ne((e=>L(lt(e),$e))(e));let r=!1;if(n.length||o.length){const s=e.selection.getBookmark();Ut(e,n,t),((e,t,n)=>{S(n,"Indent"===t?tt:t=>et(e,t))})(e,t,o),e.selection.moveToBookmark(s),e.selection.setRng(ot(e.selection.getRng())),e.nodeChanged(),r=!0}return r},_t=(e,t)=>!(e=>{const t=it(e);return gt(e,t)})(e)&&$t(e,t),Ht=e=>_t(e,"Indent"),Ft=e=>_t(e,"Outdent"),Vt=e=>_t(e,"Flatten"),jt=e=>"\ufeff"===e;var Kt=tinymce.util.Tools.resolve("tinymce.dom.BookmarkManager");const zt=Ee.DOM,Qt=e=>{const t={},n=n=>{let o=e[n?"startContainer":"endContainer"],r=e[n?"startOffset":"endOffset"];if(Me(o)){const e=zt.create("span",{"data-mce-type":"bookmark"});o.hasChildNodes()?(r=Math.min(r,o.childNodes.length-1),n?o.insertBefore(e,o.childNodes[r]):zt.insertAfter(e,o.childNodes[r])):o.appendChild(e),o=e,r=0}t[n?"startContainer":"endContainer"]=o,t[n?"startOffset":"endOffset"]=r};return n(!0),e.collapsed||n(),t},Wt=e=>{const t=t=>{let n=e[t?"startContainer":"endContainer"],o=e[t?"startOffset":"endOffset"];if(n){if(Me(n)&&n.parentNode){const e=n;o=(e=>{var t;let n=null===(t=e.parentNode)||void 0===t?void 0:t.firstChild,o=0;for(;n;){if(n===e)return o;Me(n)&&"bookmark"===n.getAttribute("data-mce-type")||o++,n=n.nextSibling}return-1})(n),n=n.parentNode,zt.remove(e),!n.hasChildNodes()&&zt.isBlock(n)&&n.appendChild(zt.create("br"))}e[t?"startContainer":"endContainer"]=n,e[t?"startOffset":"endOffset"]=o}};t(!0),t();const n=zt.createRng();return n.setStart(e.startContainer,e.startOffset),e.endContainer&&n.setEnd(e.endContainer,e.endOffset),ot(n)},qt=e=>{switch(e){case"UL":return"ToggleUlList";case"OL":return"ToggleOlList";case"DL":return"ToggleDLList"}},Zt=(e,t)=>{we.each(t,((t,n)=>{e.setAttribute(n,t)}))},Gt=(e,t,n)=>{((e,t,n)=>{const o=n["list-style-type"]?n["list-style-type"]:null;e.setStyle(t,"list-style-type",o)})(e,t,n),((e,t,n)=>{Zt(t,n["list-attributes"]),we.each(e.select("li",t),(e=>{Zt(e,n["list-item-attributes"])}))})(e,t,n)},Jt=(e,t)=>l(t)&&!Ve(t,e.schema.getBlockElements()),Xt=(e,t,n,o)=>{let r=t[n?"startContainer":"endContainer"];const s=t[n?"startOffset":"endOffset"];Me(r)&&(r=r.childNodes[Math.min(s,r.childNodes.length-1)]||r),!n&&He(r.nextSibling)&&(r=r.nextSibling);const i=(t,n)=>{var r;const s=new Ce(t,(t=>{for(;!e.dom.isBlock(t)&&t.parentNode&&o!==t;)t=t.parentNode;return t})(t)),i=n?"next":"prev";let l;for(;l=s[i]();)if(!je(e,l)&&!jt(l.textContent)&&0!==(null===(r=l.textContent)||void 0===r?void 0:r.length))return h.some(l);return h.none()};if(n&&Be(r))if(jt(r.textContent))r=i(r,!1).getOr(r);else for(null!==r.parentNode&&Jt(e,r.parentNode)&&(r=r.parentNode);null!==r.previousSibling&&(Jt(e,r.previousSibling)||Be(r.previousSibling));)r=r.previousSibling;if(!n&&Be(r))if(jt(r.textContent))r=i(r,!0).getOr(r);else for(null!==r.parentNode&&Jt(e,r.parentNode)&&(r=r.parentNode);null!==r.nextSibling&&(Jt(e,r.nextSibling)||Be(r.nextSibling));)r=r.nextSibling;for(;r.parentNode!==o;){const t=r.parentNode;if(Fe(e,r))return r;if(/^(TD|TH)$/.test(t.nodeName))return r;r=t}return r},Yt=(e,t,n)=>{const o=e.selection.getRng();let r="LI";const s=dt(e,((e,t)=>{const n=e.selection.getStart(!0),o=Xt(e,t,!0,e.getBody());return r=$(o),s=$(t.commonAncestorContainer),i=r,l=function(e,...t){return(...n)=>{const o=t.concat(n);return e.apply(null,o)}}(H,s),ce(i,l,void 0).isSome()?t.commonAncestorContainer:n;var r,s,i,l})(e,o)),i=e.dom;if("false"===i.getContentEditable(e.selection.getNode()))return;"DL"===(t=t.toUpperCase())&&(r="DT");const l=Qt(o),a=L(((e,t,n)=>{const o=[],r=e.dom,s=Xt(e,t,!0,n),i=Xt(e,t,!1,n);let l;const a=[];for(let e=s;e&&(a.push(e),e!==i);e=e.nextSibling);return we.each(a,(t=>{var s;if(Fe(e,t))return o.push(t),void(l=null);if(r.isBlock(t)||He(t))return He(t)&&r.remove(t),void(l=null);const i=t.nextSibling;Kt.isBookmarkNode(t)&&(Pe(i)||Fe(e,i)||!i&&t.parentNode===n)?l=null:(l||(l=r.create("p"),null===(s=t.parentNode)||void 0===s||s.insertBefore(l,t),o.push(l)),l.appendChild(t))})),o})(e,o,s),e.dom.isEditable);we.each(a,(o=>{let s;const l=o.previousSibling,a=o.parentNode;Ue(a)||(l&&Pe(l)&&l.nodeName===t&&((e,t,n)=>{const o=e.getStyle(t,"list-style-type");let r=n?n["list-style-type"]:"";return r=null===r?"":r,o===r})(i,l,n)?(s=l,o=i.rename(o,r),l.appendChild(o)):(s=i.create(t),a.insertBefore(s,o),s.appendChild(o),o=i.rename(o,r)),((e,t,n)=>{we.each(["margin","margin-right","margin-bottom","margin-left","margin-top","padding","padding-right","padding-bottom","padding-left","padding-top"],(n=>e.setStyle(t,n,"")))})(i,o),Gt(i,s,n),tn(e.dom,s))})),e.selection.setRng(Wt(l))},en=(e,t,n)=>{return((e,t)=>Pe(e)&&e.nodeName===(null==t?void 0:t.nodeName))(t,n)&&((e,t,n)=>e.getStyle(t,"list-style-type",!0)===e.getStyle(n,"list-style-type",!0))(e,t,n)&&(o=n,t.className===o.className);var o},tn=(e,t)=>{let n,o=t.nextSibling;if(en(e,t,o)){const r=o;for(;n=r.firstChild;)t.appendChild(n);e.remove(r)}if(o=t.previousSibling,en(e,t,o)){const r=o;for(;n=r.lastChild;)t.insertBefore(n,t.firstChild);e.remove(r)}},nn=(e,t,n,o)=>{if(t.nodeName!==n){const r=e.dom.rename(t,n);Gt(e.dom,r,o),vt(e,qt(n),r)}else Gt(e.dom,t,o),vt(e,qt(n),t)},on=(e,t,n,o)=>{if(t.classList.forEach(((e,n,o)=>{e.startsWith("tox-")&&(o.remove(e),0===o.length&&t.removeAttribute("class"))})),t.nodeName!==n){const r=e.dom.rename(t,n);Gt(e.dom,r,o),vt(e,qt(n),r)}else Gt(e.dom,t,o),vt(e,qt(n),t)},rn=e=>"list-style-type"in e,sn=(e,t,n)=>{const o=it(e);if(ht(e,o))return;const s=(e=>{const t=it(e),n=e.selection.getSelectedBlocks();return((e,t)=>l(e)&&1===t.length&&t[0]===e)(t,n)?(e=>L(e.querySelectorAll(st),Pe))(t):L(n,(e=>Pe(e)&&t!==e))})(e),i=r(n)?n:{};s.length>0?((e,t,n,o,r)=>{const s=Pe(t);if(!s||t.nodeName!==o||rn(r)||ut(t)){Yt(e,o,r);const i=Qt(e.selection.getRng()),l=s?[t,...n]:n,a=s&&ut(t)?on:nn;we.each(l,(t=>{a(e,t,o,r)})),e.selection.setRng(Wt(i))}else Vt(e)})(e,o,s,t,i):((e,t,n,o)=>{if(t!==e.getBody())if(t)if(t.nodeName!==n||rn(o)||ut(t)){const r=Qt(e.selection.getRng());ut(t)&&t.classList.forEach(((e,n,o)=>{e.startsWith("tox-")&&(o.remove(e),0===o.length&&t.removeAttribute("class"))})),Gt(e.dom,t,o);const s=e.dom.rename(t,n);tn(e.dom,s),e.selection.setRng(Wt(r)),Yt(e,n,o),vt(e,qt(n),s)}else Vt(e);else Yt(e,n,o),vt(e,qt(n),t)})(e,o,t,i)},ln=Ee.DOM,an=(e,t)=>{const n=we.grep(e.select("ol,ul",t));we.each(n,(t=>{((e,t)=>{const n=t.parentElement;if(n&&"LI"===n.nodeName&&n.firstChild===t){const o=n.previousSibling;o&&"LI"===o.nodeName?(o.appendChild(t),Ke(e,n)&&ln.remove(n)):ln.setStyle(n,"listStyleType","none")}if(Pe(n)){const e=n.previousSibling;e&&"LI"===e.nodeName&&e.appendChild(t)}})(e,t)}))},dn=(e,t,n,o)=>{let r=t.startContainer;const s=t.startOffset;if(Be(r)&&(n?s0))return r;const i=e.schema.getNonEmptyElements();Me(r)&&(r=ve.getNode(r,s));const l=new Ce(r,o);n&&((e,t)=>!!He(t)&&e.isBlock(t.nextSibling)&&!He(t.previousSibling))(e.dom,r)&&l.next();const a=n?l.next.bind(l):l.prev2.bind(l);for(;r=a();){if("LI"===r.nodeName&&!r.hasChildNodes())return r;if(i[r.nodeName])return r;if(Be(r)&&r.data.length>0)return r}return null},cn=(e,t)=>{const n=t.childNodes;return 1===n.length&&!Pe(n[0])&&e.isBlock(n[0])},mn=e=>h.from(e).map($).filter(Z).exists((e=>((e,t=!1)=>{return ae(e)?e.dom.isContentEditable:(n=e,de(((e,t)=>_(e,t)),ue,n,"[contenteditable]",void 0)).fold(m(t),(e=>"true"===(e=>e.dom.contentEditable)(e)));var n})(e)&&!C(["details"],Q(e)))),un=(e,t,n)=>{let o;const r=cn(e,n)?n.firstChild:n;if(((e,t)=>{cn(e,t)&&mn(t.firstChild)&&e.remove(t.firstChild,!0)})(e,t),!Ke(e,t,!0))for(;o=t.firstChild;)r.appendChild(o)},pn=(e,t,n)=>{let o;const r=t.parentNode;if(!ze(e,t)||!ze(e,n))return;Pe(n.lastChild)&&(o=n.lastChild),r===n.lastChild&&He(r.previousSibling)&&e.remove(r.previousSibling);const s=n.lastChild;s&&He(s)&&t.hasChildNodes()&&e.remove(s),Ke(e,n,!0)&&fe($(n)),un(e,t,n),o&&n.appendChild(o);const i=((e,t)=>{const n=e.dom,o=t.dom;return n!==o&&n.contains(o)})($(n),$(t))?e.getParents(t,Pe,n):[];e.remove(t),S(i,(t=>{Ke(e,t)&&t!==e.getRoot()&&e.remove(t)}))},gn=(e,t)=>{const n=e.dom,o=e.selection,r=o.getStart(),s=at(e,r),i=n.getParent(o.getStart(),"LI",s);if(i){const r=i.parentElement;if(r===e.getBody()&&Ke(n,r))return!0;const l=ot(o.getRng()),a=n.getParent(dn(e,l,t,s),"LI",s),d=a&&(t?n.isChildOf(i,a):n.isChildOf(a,i));if(a&&a!==i&&!d)return e.undoManager.transact((()=>{var n,o;t?((e,t,n,o)=>{const r=e.dom;if(r.isEmpty(o))((e,t,n)=>{fe($(n)),pn(e.dom,t,n),e.selection.setCursorLocation(n,0)})(e,n,o);else{const s=Qt(t);pn(r,n,o),e.selection.setRng(Wt(s))}})(e,l,a,i):(null===(o=(n=i).parentNode)||void 0===o?void 0:o.firstChild)===n?Ft(e):((e,t,n,o)=>{const r=Qt(t);pn(e.dom,n,o);const s=Wt(r);e.selection.setRng(s)})(e,l,i,a)})),!0;if(d&&!t&&a!==i)return e.undoManager.transact((()=>{if(l.commonAncestorContainer.parentElement){const t=Qt(l),o=l.commonAncestorContainer.parentElement;un(n,l.commonAncestorContainer.parentElement,a),o.remove();const r=Wt(t);e.selection.setRng(r)}})),!0;if(!a&&!t&&0===l.startOffset&&0===l.endOffset)return e.undoManager.transact((()=>{Vt(e)})),!0}return!1},hn=e=>{const t=e.selection.getStart(),n=at(e,t);return e.dom.getParent(t,"LI,DT,DD",n)||lt(e).length>0},fn=(e,t)=>{const n=e.selection;return!ht(e,n.getNode())&&(n.isCollapsed()?((e,t)=>gn(e,t)||((e,t)=>{const n=e.dom,o=e.selection.getStart(),r=at(e,o),s=n.getParent(o,n.isBlock,r);if(s&&n.isEmpty(s)){const o=ot(e.selection.getRng()),i=n.getParent(dn(e,o,t,r),"LI",r);if(i){const l=e=>C(["td","th","caption"],Q(e)),a=e=>e.dom===r;return!!((e,t,n=u)=>P(e,t,n).getOr(e.isNone()&&t.isNone()))(me($(i),l,a),me($(o.startContainer),l,a),H)&&(e.undoManager.transact((()=>{const o=i.parentNode;((e,t,n)=>{const o=e.getParent(t.parentNode,e.isBlock,n);e.remove(t),o&&e.isEmpty(o)&&e.remove(o)})(n,s,r),tn(n,o),e.selection.select(i,!0),e.selection.collapse(t)})),!0)}}return!1})(e,t))(e,t):(e=>!!hn(e)&&(e.undoManager.transact((()=>{e.execCommand("Delete"),an(e.dom,e.getBody())})),!0))(e))},yn=e=>{const t=E(Ct(e).split("")),n=N(t,((e,t)=>{const n=e.toUpperCase().charCodeAt(0)-"A".charCodeAt(0)+1;return Math.pow(26,t)*n}));return O(n,((e,t)=>e+t),0)},vn=e=>{if(--e<0)return"";{const t=e%26,n=Math.floor(e/26);return vn(n)+String.fromCharCode("A".charCodeAt(0)+t)}},Cn=e=>{const t=parseInt(e.start,10);return M(e.listStyleType,"upper-alpha")?vn(t):M(e.listStyleType,"lower-alpha")?vn(t).toLowerCase():e.start},bn=(e,t)=>()=>{const n=it(e);return l(n)&&n.nodeName===t},Nn=e=>{e.addCommand("mceListProps",(()=>{(e=>{const t=it(e);Re(t)&&!ht(e,t)&&e.windowManager.open({title:"List Properties",body:{type:"panel",items:[{type:"input",name:"start",label:"Start list at number",inputMode:"numeric"}]},initialData:{start:Cn({start:e.dom.getAttrib(t,"start","1"),listStyleType:h.from(e.dom.getStyle(t,"list-style-type"))})},buttons:[{type:"cancel",name:"cancel",text:"Cancel"},{type:"submit",name:"save",text:"Save",primary:!0}],onSubmit:t=>{(e=>{switch((e=>/^[0-9]+$/.test(e)?2:/^[A-Z]+$/.test(e)?0:/^[a-z]+$/.test(e)?1:e.length>0?4:3)(e)){case 2:return h.some({listStyleType:h.none(),start:e});case 0:return h.some({listStyleType:h.some("upper-alpha"),start:yn(e).toString()});case 1:return h.some({listStyleType:h.some("lower-alpha"),start:yn(e).toString()});case 3:return h.some({listStyleType:h.none(),start:""});case 4:return h.none()}})(t.getData().start).each((t=>{e.execCommand("mceListUpdate",!1,{attrs:{start:"1"===t.start?"":t.start},styles:{"list-style-type":t.listStyleType.getOr("")}})})),t.close()}})})(e)}))};var Sn=tinymce.util.Tools.resolve("tinymce.html.Node");const Ln=e=>3===e.type,On=e=>0===e.length,An=e=>{const t=(t,n)=>{const o=Sn.create("li");S(t,(e=>o.append(e))),n?e.insert(o,n,!0):e.append(o)},n=O(e.children(),((e,n)=>Ln(n)?[...e,n]:On(e)||Ln(n)?e:(t(e,n),[])),[]);On(n)||t(n)},Tn=(e,t)=>n=>(n.setEnabled(e.selection.isEditable()),ft(e,(o=>{n.setActive(pt(o.parents,t)),n.setEnabled(!ht(e,o.element)&&e.selection.isEditable())}))),xn=(e,t)=>n=>ft(e,(o=>n.setEnabled(pt(o.parents,t)&&!ht(e,o.element))));e.add("lists",(e=>((e=>{(0,e.options.register)("lists_indent_on_tab",{processor:"boolean",default:!0})})(e),(e=>{e.on("PreInit",(()=>{const{parser:t}=e;t.addNodeFilter("ul,ol",(e=>S(e,An)))}))})(e),e.hasPlugin("rtc",!0)?Nn(e):((e=>{We(e)&&(e=>{e.on("keydown",(t=>{t.keyCode!==be.TAB||be.metaKeyPressed(t)||e.undoManager.transact((()=>{(t.shiftKey?Ft(e):Ht(e))&&t.preventDefault()}))}))})(e),(e=>{e.on("ExecCommand",(t=>{const n=t.command.toLowerCase();"delete"!==n&&"forwarddelete"!==n||!hn(e)||an(e.dom,e.getBody())})),e.on("keydown",(t=>{t.keyCode===be.BACKSPACE?fn(e,!1)&&t.preventDefault():t.keyCode===be.DELETE&&fn(e,!0)&&t.preventDefault()}))})(e)})(e),(e=>{e.on("BeforeExecCommand",(t=>{const n=t.command.toLowerCase();"indent"===n?Ht(e):"outdent"===n&&Ft(e)})),e.addCommand("InsertUnorderedList",((t,n)=>{sn(e,"UL",n)})),e.addCommand("InsertOrderedList",((t,n)=>{sn(e,"OL",n)})),e.addCommand("InsertDefinitionList",((t,n)=>{sn(e,"DL",n)})),e.addCommand("RemoveList",(()=>{Vt(e)})),Nn(e),e.addCommand("mceListUpdate",((t,n)=>{r(n)&&((e,t)=>{const n=it(e);null===n||ht(e,n)||e.undoManager.transact((()=>{r(t.styles)&&e.dom.setStyles(n,t.styles),r(t.attrs)&&Le(t.attrs,((t,o)=>e.dom.setAttrib(n,o,t)))}))})(e,n)})),e.addQueryStateHandler("InsertUnorderedList",bn(e,"UL")),e.addQueryStateHandler("InsertOrderedList",bn(e,"OL")),e.addQueryStateHandler("InsertDefinitionList",bn(e,"DL"))})(e)),(e=>{const t=t=>()=>e.execCommand(t);e.hasPlugin("advlist")||(e.ui.registry.addToggleButton("numlist",{icon:"ordered-list",active:!1,tooltip:"Numbered list",onAction:t("InsertOrderedList"),onSetup:Tn(e,"OL")}),e.ui.registry.addToggleButton("bullist",{icon:"unordered-list",active:!1,tooltip:"Bullet list",onAction:t("InsertUnorderedList"),onSetup:Tn(e,"UL")}))})(e),(e=>{const t={text:"List properties...",icon:"ordered-list",onAction:()=>e.execCommand("mceListProps"),onSetup:xn(e,"OL")};e.ui.registry.addMenuItem("listprops",t),e.ui.registry.addContextMenu("lists",{update:t=>{const n=it(e,t);return Re(n)?["listprops"]:[]}})})(e),(e=>({backspaceDelete:t=>{fn(e,t)}}))(e))))}(); \ No newline at end of file diff --git a/public/libs/tinymce/plugins/media/plugin.min.js b/public/libs/tinymce/plugins/media/plugin.min.js index f9b72e957e8..28cc7bdae32 100644 --- a/public/libs/tinymce/plugins/media/plugin.min.js +++ b/public/libs/tinymce/plugins/media/plugin.min.js @@ -1,4 +1,4 @@ /** - * TinyMCE version 6.7.2 (2023-10-25) + * TinyMCE version 6.8.4 (2024-06-19) */ -!function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager");const t=e=>t=>(e=>{const t=typeof e;return null===e?"null":"object"===t&&Array.isArray(e)?"array":"object"===t&&(r=o=e,(a=String).prototype.isPrototypeOf(r)||(null===(s=o.constructor)||void 0===s?void 0:s.name)===a.name)?"string":t;var r,o,a,s})(t)===e,r=t("string"),o=t("object"),a=t("array"),s=e=>!(e=>null==e)(e);class i{constructor(e,t){this.tag=e,this.value=t}static some(e){return new i(!0,e)}static none(){return i.singletonNone}fold(e,t){return this.tag?t(this.value):e()}isSome(){return this.tag}isNone(){return!this.tag}map(e){return this.tag?i.some(e(this.value)):i.none()}bind(e){return this.tag?e(this.value):i.none()}exists(e){return this.tag&&e(this.value)}forall(e){return!this.tag||e(this.value)}filter(e){return!this.tag||e(this.value)?this:i.none()}getOr(e){return this.tag?this.value:e}or(e){return this.tag?this:e}getOrThunk(e){return this.tag?this.value:e()}orThunk(e){return this.tag?this:e()}getOrDie(e){if(this.tag)return this.value;throw new Error(null!=e?e:"Called getOrDie on None")}static from(e){return s(e)?i.some(e):i.none()}getOrNull(){return this.tag?this.value:null}getOrUndefined(){return this.value}each(e){this.tag&&e(this.value)}toArray(){return this.tag?[this.value]:[]}toString(){return this.tag?`some(${this.value})`:"none()"}}i.singletonNone=new i(!1);const n=Array.prototype.push,l=(e,t)=>{for(let r=0,o=e.length;r{const t=[];for(let r=0,o=e.length;rh(e,t)?i.from(e[t]):i.none(),h=(e,t)=>u.call(e,t),p=e=>t=>t.options.get(e),g=p("audio_template_callback"),b=p("video_template_callback"),w=p("iframe_template_callback"),v=p("media_live_embeds"),f=p("media_filter_html"),y=p("media_url_resolver"),x=p("media_alt_source"),_=p("media_poster"),k=p("media_dimensions");var j=tinymce.util.Tools.resolve("tinymce.util.Tools"),O=tinymce.util.Tools.resolve("tinymce.dom.DOMUtils"),A=tinymce.util.Tools.resolve("tinymce.html.DomParser");const S=O.DOM,$=e=>e.replace(/px$/,""),C=e=>{const t=e.attr("style"),r=t?S.parseStyle(t):{};return{type:"ephox-embed-iri",source:e.attr("data-ephox-embed-iri"),altsource:"",poster:"",width:d(r,"max-width").map($).getOr(""),height:d(r,"max-height").map($).getOr("")}},T=(e,t)=>{let r={};for(let o=A({validate:!1,forced_root_block:!1},t).parse(e);o;o=o.walk())if(1===o.type){const e=o.name;if(o.attr("data-ephox-embed-iri")){r=C(o);break}r.source||"param"!==e||(r.source=o.attr("movie")),"iframe"!==e&&"object"!==e&&"embed"!==e&&"video"!==e&&"audio"!==e||(r.type||(r.type=e),r=j.extend(o.attributes.map,r)),"source"===e&&(r.source?r.altsource||(r.altsource=o.attr("src")):r.source=o.attr("src")),"img"!==e||r.poster||(r.poster=o.attr("src"))}return r.source=r.source||r.src||"",r.altsource=r.altsource||"",r.poster=r.poster||"",r},z=e=>{var t;const r=null!==(t=e.toLowerCase().split(".").pop())&&void 0!==t?t:"";return d({mp3:"audio/mpeg",m4a:"audio/x-m4a",wav:"audio/wav",mp4:"video/mp4",webm:"video/webm",ogg:"video/ogg",swf:"application/x-shockwave-flash"},r).getOr("")};var D=tinymce.util.Tools.resolve("tinymce.html.Node"),F=tinymce.util.Tools.resolve("tinymce.html.Serializer");const M=(e,t={})=>A({forced_root_block:!1,validate:!1,allow_conditional_comments:!0,...t},e),N=O.DOM,R=e=>/^[0-9.]+$/.test(e)?e+"px":e,E=(e,t)=>{const r=t.attr("style"),o=r?N.parseStyle(r):{};s(e.width)&&(o["max-width"]=R(e.width)),s(e.height)&&(o["max-height"]=R(e.height)),t.attr("style",N.serializeStyle(o))},U=["source","altsource"],P=(e,t,r,o)=>{let a=0,s=0;const i=M(o);i.addNodeFilter("source",(e=>a=e.length));const n=i.parse(e);for(let e=n;e;e=e.walk())if(1===e.type){const o=e.name;if(e.attr("data-ephox-embed-iri")){E(t,e);break}switch(o){case"video":case"object":case"embed":case"img":case"iframe":void 0!==t.height&&void 0!==t.width&&(e.attr("width",t.width),e.attr("height",t.height))}if(r)switch(o){case"video":e.attr("poster",t.poster),e.attr("src",null);for(let r=a;r<2;r++)if(t[U[r]]){const o=new D("source",1);o.attr("src",t[U[r]]),o.attr("type",t[U[r]+"mime"]||null),e.append(o)}break;case"iframe":e.attr("src",t.source);break;case"object":const r=e.getAll("img").length>0;if(t.poster&&!r){e.attr("src",t.poster);const r=new D("img",1);r.attr("src",t.poster),r.attr("width",t.width),r.attr("height",t.height),e.append(r)}break;case"source":if(s<2&&(e.attr("src",t[U[s]]),e.attr("type",t[U[s]+"mime"]||null),!t[U[s]])){e.remove();continue}s++;break;case"img":t.poster||e.remove()}}return F({},o).serialize(n)},L=[{regex:/youtu\.be\/([\w\-_\?&=.]+)/i,type:"iframe",w:560,h:314,url:"www.youtube.com/embed/$1",allowFullscreen:!0},{regex:/youtube\.com(.+)v=([^&]+)(&([a-z0-9&=\-_]+))?/i,type:"iframe",w:560,h:314,url:"www.youtube.com/embed/$2?$4",allowFullscreen:!0},{regex:/youtube.com\/embed\/([a-z0-9\?&=\-_]+)/i,type:"iframe",w:560,h:314,url:"www.youtube.com/embed/$1",allowFullscreen:!0},{regex:/vimeo\.com\/([0-9]+)\?h=(\w+)/,type:"iframe",w:425,h:350,url:"player.vimeo.com/video/$1?h=$2&title=0&byline=0&portrait=0&color=8dc7dc",allowFullscreen:!0},{regex:/vimeo\.com\/(.*)\/([0-9]+)\?h=(\w+)/,type:"iframe",w:425,h:350,url:"player.vimeo.com/video/$2?h=$3&title=0&byline=0",allowFullscreen:!0},{regex:/vimeo\.com\/([0-9]+)/,type:"iframe",w:425,h:350,url:"player.vimeo.com/video/$1?title=0&byline=0&portrait=0&color=8dc7dc",allowFullscreen:!0},{regex:/vimeo\.com\/(.*)\/([0-9]+)/,type:"iframe",w:425,h:350,url:"player.vimeo.com/video/$2?title=0&byline=0",allowFullscreen:!0},{regex:/maps\.google\.([a-z]{2,3})\/maps\/(.+)msid=(.+)/,type:"iframe",w:425,h:350,url:'maps.google.com/maps/ms?msid=$2&output=embed"',allowFullscreen:!1},{regex:/dailymotion\.com\/video\/([^_]+)/,type:"iframe",w:480,h:270,url:"www.dailymotion.com/embed/video/$1",allowFullscreen:!0},{regex:/dai\.ly\/([^_]+)/,type:"iframe",w:480,h:270,url:"www.dailymotion.com/embed/video/$1",allowFullscreen:!0}],I=(e,t)=>{const r=(e=>{const t=e.match(/^(https?:\/\/|www\.)(.+)$/i);return t&&t.length>1?"www."===t[1]?"https://":t[1]:"https://"})(t),o=e.regex.exec(t);let a=r+e.url;if(s(o))for(let e=0;eo[e]?o[e]:""));return a.replace(/\?$/,"")},B=e=>{const t=L.filter((t=>t.regex.test(e)));return t.length>0?j.extend({},t[0],{url:I(t[0],e)}):null},G=(e,t)=>{var r;const o=j.extend({},t);if(!o.source&&(j.extend(o,T(null!==(r=o.embed)&&void 0!==r?r:"",e.schema)),!o.source))return"";o.altsource||(o.altsource=""),o.poster||(o.poster=""),o.source=e.convertURL(o.source,"source"),o.altsource=e.convertURL(o.altsource,"source"),o.sourcemime=z(o.source),o.altsourcemime=z(o.altsource),o.poster=e.convertURL(o.poster,"poster");const a=B(o.source);if(a&&(o.source=a.url,o.type=a.type,o.allowfullscreen=a.allowFullscreen,o.width=o.width||String(a.w),o.height=o.height||String(a.h)),o.embed)return P(o.embed,o,!0,e.schema);{const t=g(e),r=b(e),a=w(e);return o.width=o.width||"300",o.height=o.height||"150",j.each(o,((t,r)=>{o[r]=e.dom.encode(""+t)})),"iframe"===o.type?((e,t)=>{if(t)return t(e);{const t=e.allowfullscreen?' allowFullscreen="1"':"";return'"}})(o,a):"application/x-shockwave-flash"===o.sourcemime?(e=>{let t='';return e.poster&&(t+=''),t+="",t})(o):-1!==o.sourcemime.indexOf("audio")?((e,t)=>t?t(e):'")(o,t):((e,t)=>t?t(e):'")(o,r)}},W=e=>e.hasAttribute("data-mce-object")||e.hasAttribute("data-ephox-embed-iri"),q={},H=e=>t=>G(e,t),J=(e,t)=>{const r=y(e);return r?((e,t,r)=>new Promise(((o,a)=>{const s=r=>(r.html&&(q[e.source]=r),o({url:e.source,html:r.html?r.html:t(e)}));q[e.source]?s(q[e.source]):r({url:e.source},s,a)})))(t,H(e),r):((e,t)=>Promise.resolve({html:t(e),url:e.source}))(t,H(e))},K=(e,t)=>{const r={};return d(e,"dimensions").each((e=>{l(["width","height"],(o=>{d(t,o).orThunk((()=>d(e,o))).each((e=>r[o]=e))}))})),r},Q=(e,t)=>{const r=t&&"dimensions"!==t?((e,t)=>d(t,e).bind((e=>d(e,"meta"))))(t,e).getOr({}):{},a=((e,t,r)=>a=>{const s=()=>d(e,a),n=()=>d(t,a),l=e=>d(e,"value").bind((e=>e.length>0?i.some(e):i.none()));return{[a]:(a===r?s().bind((e=>o(e)?l(e).orThunk(n):n().orThunk((()=>i.from(e))))):n().orThunk((()=>s().bind((e=>o(e)?l(e):i.from(e)))))).getOr("")}})(e,r,t);return{...a("source"),...a("altsource"),...a("poster"),...a("embed"),...K(e,r)}},V=e=>{const t={...e,source:{value:d(e,"source").getOr("")},altsource:{value:d(e,"altsource").getOr("")},poster:{value:d(e,"poster").getOr("")}};return l(["width","height"],(r=>{d(e,r).each((e=>{const o=t.dimensions||{};o[r]=e,t.dimensions=o}))})),t},X=e=>t=>{const r=t&&t.msg?"Media embed handler error: "+t.msg:"Media embed handler threw unknown error.";e.notificationManager.open({type:"error",text:r})},Y=(e,t)=>o=>{if(r(o.url)&&o.url.trim().length>0){const r=o.html,a={...T(r,t.schema),source:o.url,embed:r};e.setData(V(a))}},Z=(e,t)=>{const r=e.dom.select("*[data-mce-object]");e.insertContent(t),((e,t)=>{const r=e.dom.select("*[data-mce-object]");for(let e=0;e=0;o--)t[e]===r[o]&&r.splice(o,1);e.selection.select(r[0])})(e,r),e.nodeChanged()},ee=(e,t)=>s(t)&&"ephox-embed-iri"===t&&s(B(e)),te=(e,t)=>((e,t)=>e.width!==t.width||e.height!==t.height)(e,t)&&ee(t.source,e.type),re=e=>{const t=(e=>{const t=e.selection.getNode(),r=W(t)?e.serializer.serialize(t,{selection:!0}):"",o=T(r,e.schema),a=(()=>{if(ee(o.source,o.type)){const r=e.dom.getRect(t);return{width:r.w.toString().replace(/px$/,""),height:r.h.toString().replace(/px$/,"")}}return{}})();return{embed:r,...o,...a}})(e),r=(e=>{let t=e;return{get:()=>t,set:e=>{t=e}}})(t),o=V(t),a=k(e)?[{type:"sizeinput",name:"dimensions",label:"Constrain proportions",constrain:!0}]:[],s={title:"General",name:"general",items:c([[{name:"source",type:"urlinput",filetype:"media",label:"Source"}],a])},i=[];x(e)&&i.push({name:"altsource",type:"urlinput",filetype:"media",label:"Alternative source URL"}),_(e)&&i.push({name:"poster",type:"urlinput",filetype:"image",label:"Media poster (Image URL)"});const n={title:"Advanced",name:"advanced",items:i},l=[s,{title:"Embed",items:[{type:"textarea",name:"embed",label:"Paste your embed code below:"}]}];i.length>0&&l.push(n);const m={type:"tabpanel",tabs:l},u=e.windowManager.open({title:"Insert/Edit Media",size:"normal",body:m,buttons:[{type:"cancel",name:"cancel",text:"Cancel"},{type:"submit",name:"save",text:"Save",primary:!0}],onSubmit:t=>{const o=Q(t.getData());((e,t,r)=>{var o,a;t.embed=te(e,t)&&k(r)?G(r,{...t,embed:""}):P(null!==(o=t.embed)&&void 0!==o?o:"",t,!1,r.schema),t.embed&&(e.source===t.source||(a=t.source,h(q,a)))?Z(r,t.embed):J(r,t).then((e=>{Z(r,e.html)})).catch(X(r))})(r.get(),o,e),t.close()},onChange:(t,o)=>{switch(o.name){case"source":((t,r)=>{const o=Q(r.getData(),"source");t.source!==o.source&&(Y(u,e)({url:o.source,html:""}),J(e,o).then(Y(u,e)).catch(X(e)))})(r.get(),t);break;case"embed":(t=>{var r;const o=Q(t.getData()),a=T(null!==(r=o.embed)&&void 0!==r?r:"",e.schema);t.setData(V(a))})(t);break;case"dimensions":case"altsource":case"poster":((t,r,o)=>{const a=Q(t.getData(),r),s=te(o,a)&&k(e)?{...a,embed:""}:a,i=G(e,s);t.setData(V({...s,embed:i}))})(t,o.name,r.get())}r.set(Q(t.getData()))},initialData:o})};var oe=tinymce.util.Tools.resolve("tinymce.Env");const ae=e=>{const t=e.name;return"iframe"===t||"video"===t||"audio"===t},se=(e,t,r,o=null)=>{const a=e.attr(r);return s(a)?a:h(t,r)?null:o},ie=(e,t,r)=>{const o="img"===t.name||"video"===e.name,a=o?"300":null,s="audio"===e.name?"30":"150",i=o?s:null;t.attr({width:se(e,r,"width",a),height:se(e,r,"height",i)})},ne=(e,t)=>{const r=t.name,o=new D("img",1);return ce(e,t,o),ie(t,o,{}),o.attr({style:t.attr("style"),src:oe.transparentSrc,"data-mce-object":r,class:"mce-object mce-object-"+r}),o},le=(e,t)=>{var r;const o=t.name,a=new D("span",1);a.attr({contentEditable:"false",style:t.attr("style"),"data-mce-object":o,class:"mce-preview-object mce-object-"+o}),ce(e,t,a);const i=e.dom.parseStyle(null!==(r=t.attr("style"))&&void 0!==r?r:""),n=new D(o,1);if(ie(t,n,i),n.attr({src:t.attr("src"),style:t.attr("style"),class:t.attr("class")}),"iframe"===o)n.attr({allowfullscreen:t.attr("allowfullscreen"),frameborder:"0"});else{l(["controls","crossorigin","currentTime","loop","muted","poster","preload"],(e=>{n.attr(e,t.attr(e))}));const r=a.attr("data-mce-html");s(r)&&((e,t,r,o)=>{const a=M(e.schema).parse(o,{context:t});for(;a.firstChild;)r.append(a.firstChild)})(e,o,n,unescape(r))}const c=new D("span",1);return c.attr("class","mce-shim"),a.append(n),a.append(c),a},ce=(e,t,r)=>{var o;const a=null!==(o=t.attributes)&&void 0!==o?o:[];let s=a.length;for(;s--;){const t=a[s].name;let o=a[s].value;"width"===t||"height"===t||"style"===t||(n="data-mce-",(i=t).length>=9&&i.substr(0,9)===n)||("data"!==t&&"src"!==t||(o=e.convertURL(o,t)),r.attr("data-mce-p-"+t,o))}var i,n;const c=F({inner:!0},e.schema),m=new D("div",1);l(t.children(),(e=>m.append(e)));const u=c.serialize(m);u&&(r.attr("data-mce-html",escape(u)),r.empty())},me=e=>{const t=e.attr("class");return r(t)&&/\btiny-pageembed\b/.test(t)},ue=e=>{let t=e;for(;t=t.parent;)if(t.attr("data-ephox-embed-iri")||me(t))return!0;return!1},de=(e,t,r)=>{const o=(0,e.options.get)("xss_sanitization"),a=f(e);return M(e.schema,{sanitize:o,validate:a}).parse(r,{context:t})},he=e=>t=>{const r=()=>{t.setEnabled(e.selection.isEditable())};return e.on("NodeChange",r),r(),()=>{e.off("NodeChange",r)}};e.add("media",(e=>((e=>{const t=e.options.register;t("audio_template_callback",{processor:"function"}),t("video_template_callback",{processor:"function"}),t("iframe_template_callback",{processor:"function"}),t("media_live_embeds",{processor:"boolean",default:!0}),t("media_filter_html",{processor:"boolean",default:!0}),t("media_url_resolver",{processor:"function"}),t("media_alt_source",{processor:"boolean",default:!0}),t("media_poster",{processor:"boolean",default:!0}),t("media_dimensions",{processor:"boolean",default:!0})})(e),(e=>{e.addCommand("mceMedia",(()=>{re(e)}))})(e),(e=>{const t=()=>e.execCommand("mceMedia");e.ui.registry.addToggleButton("media",{tooltip:"Insert/edit media",icon:"embed",onAction:t,onSetup:t=>{const r=e.selection;t.setActive(W(r.getNode()));const o=r.selectorChangedWithUnbind("img[data-mce-object],span[data-mce-object],div[data-ephox-embed-iri]",t.setActive).unbind,a=he(e)(t);return()=>{o(),a()}}}),e.ui.registry.addMenuItem("media",{icon:"embed",text:"Media...",onAction:t,onSetup:he(e)})})(e),(e=>{e.on("ResolveName",(e=>{let t;1===e.target.nodeType&&(t=e.target.getAttribute("data-mce-object"))&&(e.name=t)}))})(e),(e=>{e.on("PreInit",(()=>{const{schema:t,serializer:r,parser:o}=e,a=t.getBoolAttrs();l("webkitallowfullscreen mozallowfullscreen".split(" "),(e=>{a[e]={}})),((e,t)=>{const r=m(e);for(let o=0,a=r.length;o{const o=t.getElementRule(r);o&&l(e,(e=>{o.attributes[e]={},o.attributesOrder.push(e)}))})),o.addNodeFilter("iframe,video,audio,object,embed",(e=>t=>{let r,o=t.length;for(;o--;)r=t[o],r.parent&&(r.parent.attr("data-mce-object")||(ae(r)&&v(e)?ue(r)||r.replace(le(e,r)):ue(r)||r.replace(ne(e,r))))})(e)),r.addAttributeFilter("data-mce-object",((t,r)=>{var o;let a=t.length;for(;a--;){const s=t[a];if(!s.parent)continue;const i=s.attr(r),n=new D(i,1);if("audio"!==i){const e=s.attr("class");e&&-1!==e.indexOf("mce-preview-object")&&s.firstChild?n.attr({width:s.firstChild.attr("width"),height:s.firstChild.attr("height")}):n.attr({width:s.attr("width"),height:s.attr("height")})}n.attr({style:s.attr("style")});const c=null!==(o=s.attributes)&&void 0!==o?o:[];let m=c.length;for(;m--;){const e=c[m].name;0===e.indexOf("data-mce-p-")&&n.attr(e.substr(11),c[m].value)}const u=s.attr("data-mce-html");if(u){const t=de(e,i,unescape(u));l(t.children(),(e=>n.append(e)))}s.replace(n)}}))})),e.on("SetContent",(()=>{const t=e.dom;l(t.select("span.mce-preview-object"),(e=>{0===t.select("span.mce-shim",e).length&&t.add(e,"span",{class:"mce-shim"})}))}))})(e),(e=>{e.on("click keyup touchend",(()=>{const t=e.selection.getNode();t&&e.dom.hasClass(t,"mce-preview-object")&&e.dom.getAttrib(t,"data-mce-selected")&&t.setAttribute("data-mce-selected","2")})),e.on("ObjectResized",(t=>{const r=t.target;if(r.getAttribute("data-mce-object")){let o=r.getAttribute("data-mce-html");o&&(o=unescape(o),r.setAttribute("data-mce-html",escape(P(o,{width:String(t.width),height:String(t.height)},!1,e.schema))))}}))})(e),(e=>({showDialog:()=>{re(e)}}))(e))))}(); \ No newline at end of file +!function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager");const t=e=>t=>(e=>{const t=typeof e;return null===e?"null":"object"===t&&Array.isArray(e)?"array":"object"===t&&(r=o=e,(a=String).prototype.isPrototypeOf(r)||(null===(s=o.constructor)||void 0===s?void 0:s.name)===a.name)?"string":t;var r,o,a,s})(t)===e,r=t("string"),o=t("object"),a=t("array"),s=e=>!(e=>null==e)(e);class i{constructor(e,t){this.tag=e,this.value=t}static some(e){return new i(!0,e)}static none(){return i.singletonNone}fold(e,t){return this.tag?t(this.value):e()}isSome(){return this.tag}isNone(){return!this.tag}map(e){return this.tag?i.some(e(this.value)):i.none()}bind(e){return this.tag?e(this.value):i.none()}exists(e){return this.tag&&e(this.value)}forall(e){return!this.tag||e(this.value)}filter(e){return!this.tag||e(this.value)?this:i.none()}getOr(e){return this.tag?this.value:e}or(e){return this.tag?this:e}getOrThunk(e){return this.tag?this.value:e()}orThunk(e){return this.tag?this:e()}getOrDie(e){if(this.tag)return this.value;throw new Error(null!=e?e:"Called getOrDie on None")}static from(e){return s(e)?i.some(e):i.none()}getOrNull(){return this.tag?this.value:null}getOrUndefined(){return this.value}each(e){this.tag&&e(this.value)}toArray(){return this.tag?[this.value]:[]}toString(){return this.tag?`some(${this.value})`:"none()"}}i.singletonNone=new i(!1);const n=Array.prototype.push,l=(e,t)=>{for(let r=0,o=e.length;r{const t=[];for(let r=0,o=e.length;rh(e,t)?i.from(e[t]):i.none(),h=(e,t)=>u.call(e,t),p=e=>t=>t.options.get(e),g=p("audio_template_callback"),b=p("video_template_callback"),w=p("iframe_template_callback"),v=p("media_live_embeds"),f=p("media_filter_html"),y=p("media_url_resolver"),x=p("media_alt_source"),_=p("media_poster"),k=p("media_dimensions");var j=tinymce.util.Tools.resolve("tinymce.util.Tools"),O=tinymce.util.Tools.resolve("tinymce.dom.DOMUtils"),A=tinymce.util.Tools.resolve("tinymce.html.DomParser");const S=O.DOM,$=e=>e.replace(/px$/,""),C=e=>{const t=e.attr("style"),r=t?S.parseStyle(t):{};return{type:"ephox-embed-iri",source:e.attr("data-ephox-embed-iri"),altsource:"",poster:"",width:d(r,"max-width").map($).getOr(""),height:d(r,"max-height").map($).getOr("")}},T=(e,t)=>{let r={};for(let o=A({validate:!1,forced_root_block:!1},t).parse(e);o;o=o.walk())if(1===o.type){const e=o.name;if(o.attr("data-ephox-embed-iri")){r=C(o);break}r.source||"param"!==e||(r.source=o.attr("movie")),"iframe"!==e&&"object"!==e&&"embed"!==e&&"video"!==e&&"audio"!==e||(r.type||(r.type=e),r=j.extend(o.attributes.map,r)),"source"===e&&(r.source?r.altsource||(r.altsource=o.attr("src")):r.source=o.attr("src")),"img"!==e||r.poster||(r.poster=o.attr("src"))}return r.source=r.source||r.src||"",r.altsource=r.altsource||"",r.poster=r.poster||"",r},z=e=>{var t;const r=null!==(t=e.toLowerCase().split(".").pop())&&void 0!==t?t:"";return d({mp3:"audio/mpeg",m4a:"audio/x-m4a",wav:"audio/wav",mp4:"video/mp4",webm:"video/webm",ogg:"video/ogg",swf:"application/x-shockwave-flash"},r).getOr("")};var D=tinymce.util.Tools.resolve("tinymce.html.Node"),F=tinymce.util.Tools.resolve("tinymce.html.Serializer");const M=(e,t={})=>A({forced_root_block:!1,validate:!1,allow_conditional_comments:!0,...t},e),N=O.DOM,R=e=>/^[0-9.]+$/.test(e)?e+"px":e,E=(e,t)=>{const r=t.attr("style"),o=r?N.parseStyle(r):{};s(e.width)&&(o["max-width"]=R(e.width)),s(e.height)&&(o["max-height"]=R(e.height)),t.attr("style",N.serializeStyle(o))},U=["source","altsource"],P=(e,t,r,o)=>{let a=0,s=0;const i=M(o);i.addNodeFilter("source",(e=>a=e.length));const n=i.parse(e);for(let e=n;e;e=e.walk())if(1===e.type){const o=e.name;if(e.attr("data-ephox-embed-iri")){E(t,e);break}switch(o){case"video":case"object":case"embed":case"img":case"iframe":void 0!==t.height&&void 0!==t.width&&(e.attr("width",t.width),e.attr("height",t.height))}if(r)switch(o){case"video":e.attr("poster",t.poster),e.attr("src",null);for(let r=a;r<2;r++)if(t[U[r]]){const o=new D("source",1);o.attr("src",t[U[r]]),o.attr("type",t[U[r]+"mime"]||null),e.append(o)}break;case"iframe":e.attr("src",t.source);break;case"object":const r=e.getAll("img").length>0;if(t.poster&&!r){e.attr("src",t.poster);const r=new D("img",1);r.attr("src",t.poster),r.attr("width",t.width),r.attr("height",t.height),e.append(r)}break;case"source":if(s<2&&(e.attr("src",t[U[s]]),e.attr("type",t[U[s]+"mime"]||null),!t[U[s]])){e.remove();continue}s++;break;case"img":t.poster||e.remove()}}return F({},o).serialize(n)},L=[{regex:/youtu\.be\/([\w\-_\?&=.]+)/i,type:"iframe",w:560,h:314,url:"www.youtube.com/embed/$1",allowFullscreen:!0},{regex:/youtube\.com(.+)v=([^&]+)(&([a-z0-9&=\-_]+))?/i,type:"iframe",w:560,h:314,url:"www.youtube.com/embed/$2?$4",allowFullscreen:!0},{regex:/youtube.com\/embed\/([a-z0-9\?&=\-_]+)/i,type:"iframe",w:560,h:314,url:"www.youtube.com/embed/$1",allowFullscreen:!0},{regex:/vimeo\.com\/([0-9]+)\?h=(\w+)/,type:"iframe",w:425,h:350,url:"player.vimeo.com/video/$1?h=$2&title=0&byline=0&portrait=0&color=8dc7dc",allowFullscreen:!0},{regex:/vimeo\.com\/(.*)\/([0-9]+)\?h=(\w+)/,type:"iframe",w:425,h:350,url:"player.vimeo.com/video/$2?h=$3&title=0&byline=0",allowFullscreen:!0},{regex:/vimeo\.com\/([0-9]+)/,type:"iframe",w:425,h:350,url:"player.vimeo.com/video/$1?title=0&byline=0&portrait=0&color=8dc7dc",allowFullscreen:!0},{regex:/vimeo\.com\/(.*)\/([0-9]+)/,type:"iframe",w:425,h:350,url:"player.vimeo.com/video/$2?title=0&byline=0",allowFullscreen:!0},{regex:/maps\.google\.([a-z]{2,3})\/maps\/(.+)msid=(.+)/,type:"iframe",w:425,h:350,url:'maps.google.com/maps/ms?msid=$2&output=embed"',allowFullscreen:!1},{regex:/dailymotion\.com\/video\/([^_]+)/,type:"iframe",w:480,h:270,url:"www.dailymotion.com/embed/video/$1",allowFullscreen:!0},{regex:/dai\.ly\/([^_]+)/,type:"iframe",w:480,h:270,url:"www.dailymotion.com/embed/video/$1",allowFullscreen:!0}],I=(e,t)=>{const r=(e=>{const t=e.match(/^(https?:\/\/|www\.)(.+)$/i);return t&&t.length>1?"www."===t[1]?"https://":t[1]:"https://"})(t),o=e.regex.exec(t);let a=r+e.url;if(s(o))for(let e=0;eo[e]?o[e]:""));return a.replace(/\?$/,"")},B=e=>{const t=L.filter((t=>t.regex.test(e)));return t.length>0?j.extend({},t[0],{url:I(t[0],e)}):null},G=(e,t)=>{var r;const o=j.extend({},t);if(!o.source&&(j.extend(o,T(null!==(r=o.embed)&&void 0!==r?r:"",e.schema)),!o.source))return"";o.altsource||(o.altsource=""),o.poster||(o.poster=""),o.source=e.convertURL(o.source,"source"),o.altsource=e.convertURL(o.altsource,"source"),o.sourcemime=z(o.source),o.altsourcemime=z(o.altsource),o.poster=e.convertURL(o.poster,"poster");const a=B(o.source);if(a&&(o.source=a.url,o.type=a.type,o.allowfullscreen=a.allowFullscreen,o.width=o.width||String(a.w),o.height=o.height||String(a.h)),o.embed)return P(o.embed,o,!0,e.schema);{const t=g(e),r=b(e),a=w(e);return o.width=o.width||"300",o.height=o.height||"150",j.each(o,((t,r)=>{o[r]=e.dom.encode(""+t)})),"iframe"===o.type?((e,t)=>{if(t)return t(e);{const t=e.allowfullscreen?' allowFullscreen="1"':"";return'"}})(o,a):"application/x-shockwave-flash"===o.sourcemime?(e=>{let t='';return e.poster&&(t+=''),t+="",t})(o):-1!==o.sourcemime.indexOf("audio")?((e,t)=>t?t(e):'")(o,t):((e,t)=>t?t(e):'")(o,r)}},W=e=>e.hasAttribute("data-mce-object")||e.hasAttribute("data-ephox-embed-iri"),q={},H=e=>t=>G(e,t),J=(e,t)=>{const r=y(e);return r?((e,t,r)=>new Promise(((o,a)=>{const s=r=>(r.html&&(q[e.source]=r),o({url:e.source,html:r.html?r.html:t(e)}));q[e.source]?s(q[e.source]):r({url:e.source},s,a)})))(t,H(e),r):((e,t)=>Promise.resolve({html:t(e),url:e.source}))(t,H(e))},K=(e,t)=>{const r={};return d(e,"dimensions").each((e=>{l(["width","height"],(o=>{d(t,o).orThunk((()=>d(e,o))).each((e=>r[o]=e))}))})),r},Q=(e,t)=>{const r=t&&"dimensions"!==t?((e,t)=>d(t,e).bind((e=>d(e,"meta"))))(t,e).getOr({}):{},a=((e,t,r)=>a=>{const s=()=>d(e,a),n=()=>d(t,a),l=e=>d(e,"value").bind((e=>e.length>0?i.some(e):i.none()));return{[a]:(a===r?s().bind((e=>o(e)?l(e).orThunk(n):n().orThunk((()=>i.from(e))))):n().orThunk((()=>s().bind((e=>o(e)?l(e):i.from(e)))))).getOr("")}})(e,r,t);return{...a("source"),...a("altsource"),...a("poster"),...a("embed"),...K(e,r)}},V=e=>{const t={...e,source:{value:d(e,"source").getOr("")},altsource:{value:d(e,"altsource").getOr("")},poster:{value:d(e,"poster").getOr("")}};return l(["width","height"],(r=>{d(e,r).each((e=>{const o=t.dimensions||{};o[r]=e,t.dimensions=o}))})),t},X=e=>t=>{const r=t&&t.msg?"Media embed handler error: "+t.msg:"Media embed handler threw unknown error.";e.notificationManager.open({type:"error",text:r})},Y=(e,t)=>o=>{if(r(o.url)&&o.url.trim().length>0){const r=o.html,a={...T(r,t.schema),source:o.url,embed:r};e.setData(V(a))}},Z=(e,t)=>{const r=e.dom.select("*[data-mce-object]");e.insertContent(t),((e,t)=>{const r=e.dom.select("*[data-mce-object]");for(let e=0;e=0;o--)t[e]===r[o]&&r.splice(o,1);e.selection.select(r[0])})(e,r),e.nodeChanged()},ee=(e,t)=>s(t)&&"ephox-embed-iri"===t&&s(B(e)),te=(e,t)=>((e,t)=>e.width!==t.width||e.height!==t.height)(e,t)&&ee(t.source,e.type),re=e=>{const t=(e=>{const t=e.selection.getNode(),r=W(t)?e.serializer.serialize(t,{selection:!0}):"",o=T(r,e.schema),a=(()=>{if(ee(o.source,o.type)){const r=e.dom.getRect(t);return{width:r.w.toString().replace(/px$/,""),height:r.h.toString().replace(/px$/,"")}}return{}})();return{embed:r,...o,...a}})(e),r=(e=>{let t=e;return{get:()=>t,set:e=>{t=e}}})(t),o=V(t),a=k(e)?[{type:"sizeinput",name:"dimensions",label:"Constrain proportions",constrain:!0}]:[],s={title:"General",name:"general",items:c([[{name:"source",type:"urlinput",filetype:"media",label:"Source",picker_text:"Browse files"}],a])},i=[];x(e)&&i.push({name:"altsource",type:"urlinput",filetype:"media",label:"Alternative source URL"}),_(e)&&i.push({name:"poster",type:"urlinput",filetype:"image",label:"Media poster (Image URL)"});const n={title:"Advanced",name:"advanced",items:i},l=[s,{title:"Embed",items:[{type:"textarea",name:"embed",label:"Paste your embed code below:"}]}];i.length>0&&l.push(n);const m={type:"tabpanel",tabs:l},u=e.windowManager.open({title:"Insert/Edit Media",size:"normal",body:m,buttons:[{type:"cancel",name:"cancel",text:"Cancel"},{type:"submit",name:"save",text:"Save",primary:!0}],onSubmit:t=>{const o=Q(t.getData());((e,t,r)=>{var o,a;t.embed=te(e,t)&&k(r)?G(r,{...t,embed:""}):P(null!==(o=t.embed)&&void 0!==o?o:"",t,!1,r.schema),t.embed&&(e.source===t.source||(a=t.source,h(q,a)))?Z(r,t.embed):J(r,t).then((e=>{Z(r,e.html)})).catch(X(r))})(r.get(),o,e),t.close()},onChange:(t,o)=>{switch(o.name){case"source":((t,r)=>{const o=Q(r.getData(),"source");t.source!==o.source&&(Y(u,e)({url:o.source,html:""}),J(e,o).then(Y(u,e)).catch(X(e)))})(r.get(),t);break;case"embed":(t=>{var r;const o=Q(t.getData()),a=T(null!==(r=o.embed)&&void 0!==r?r:"",e.schema);t.setData(V(a))})(t);break;case"dimensions":case"altsource":case"poster":((t,r,o)=>{const a=Q(t.getData(),r),s=te(o,a)&&k(e)?{...a,embed:""}:a,i=G(e,s);t.setData(V({...s,embed:i}))})(t,o.name,r.get())}r.set(Q(t.getData()))},initialData:o})};var oe=tinymce.util.Tools.resolve("tinymce.Env");const ae=e=>{const t=e.name;return"iframe"===t||"video"===t||"audio"===t},se=(e,t,r,o=null)=>{const a=e.attr(r);return s(a)?a:h(t,r)?null:o},ie=(e,t,r)=>{const o="img"===t.name||"video"===e.name,a=o?"300":null,s="audio"===e.name?"30":"150",i=o?s:null;t.attr({width:se(e,r,"width",a),height:se(e,r,"height",i)})},ne=(e,t)=>{const r=t.name,o=new D("img",1);return ce(e,t,o),ie(t,o,{}),o.attr({style:t.attr("style"),src:oe.transparentSrc,"data-mce-object":r,class:"mce-object mce-object-"+r}),o},le=(e,t)=>{var r;const o=t.name,a=new D("span",1);a.attr({contentEditable:"false",style:t.attr("style"),"data-mce-object":o,class:"mce-preview-object mce-object-"+o}),ce(e,t,a);const i=e.dom.parseStyle(null!==(r=t.attr("style"))&&void 0!==r?r:""),n=new D(o,1);if(ie(t,n,i),n.attr({src:t.attr("src"),style:t.attr("style"),class:t.attr("class")}),"iframe"===o)n.attr({allowfullscreen:t.attr("allowfullscreen"),frameborder:"0",sandbox:t.attr("sandbox")});else{l(["controls","crossorigin","currentTime","loop","muted","poster","preload"],(e=>{n.attr(e,t.attr(e))}));const r=a.attr("data-mce-html");s(r)&&((e,t,r,o)=>{const a=M(e.schema).parse(o,{context:t});for(;a.firstChild;)r.append(a.firstChild)})(e,o,n,unescape(r))}const c=new D("span",1);return c.attr("class","mce-shim"),a.append(n),a.append(c),a},ce=(e,t,r)=>{var o;const a=null!==(o=t.attributes)&&void 0!==o?o:[];let s=a.length;for(;s--;){const t=a[s].name;let o=a[s].value;"width"===t||"height"===t||"style"===t||(n="data-mce-",(i=t).length>=9&&i.substr(0,9)===n)||("data"!==t&&"src"!==t||(o=e.convertURL(o,t)),r.attr("data-mce-p-"+t,o))}var i,n;const c=F({inner:!0},e.schema),m=new D("div",1);l(t.children(),(e=>m.append(e)));const u=c.serialize(m);u&&(r.attr("data-mce-html",escape(u)),r.empty())},me=e=>{const t=e.attr("class");return r(t)&&/\btiny-pageembed\b/.test(t)},ue=e=>{let t=e;for(;t=t.parent;)if(t.attr("data-ephox-embed-iri")||me(t))return!0;return!1},de=(e,t,r)=>{const o=(0,e.options.get)("xss_sanitization"),a=f(e);return M(e.schema,{sanitize:o,validate:a}).parse(r,{context:t})},he=e=>t=>{const r=()=>{t.setEnabled(e.selection.isEditable())};return e.on("NodeChange",r),r(),()=>{e.off("NodeChange",r)}};e.add("media",(e=>((e=>{const t=e.options.register;t("audio_template_callback",{processor:"function"}),t("video_template_callback",{processor:"function"}),t("iframe_template_callback",{processor:"function"}),t("media_live_embeds",{processor:"boolean",default:!0}),t("media_filter_html",{processor:"boolean",default:!0}),t("media_url_resolver",{processor:"function"}),t("media_alt_source",{processor:"boolean",default:!0}),t("media_poster",{processor:"boolean",default:!0}),t("media_dimensions",{processor:"boolean",default:!0})})(e),(e=>{e.addCommand("mceMedia",(()=>{re(e)}))})(e),(e=>{const t=()=>e.execCommand("mceMedia");e.ui.registry.addToggleButton("media",{tooltip:"Insert/edit media",icon:"embed",onAction:t,onSetup:t=>{const r=e.selection;t.setActive(W(r.getNode()));const o=r.selectorChangedWithUnbind("img[data-mce-object],span[data-mce-object],div[data-ephox-embed-iri]",t.setActive).unbind,a=he(e)(t);return()=>{o(),a()}}}),e.ui.registry.addMenuItem("media",{icon:"embed",text:"Media...",onAction:t,onSetup:he(e)})})(e),(e=>{e.on("ResolveName",(e=>{let t;1===e.target.nodeType&&(t=e.target.getAttribute("data-mce-object"))&&(e.name=t)}))})(e),(e=>{e.on("PreInit",(()=>{const{schema:t,serializer:r,parser:o}=e,a=t.getBoolAttrs();l("webkitallowfullscreen mozallowfullscreen".split(" "),(e=>{a[e]={}})),((e,t)=>{const r=m(e);for(let o=0,a=r.length;o{const o=t.getElementRule(r);o&&l(e,(e=>{o.attributes[e]={},o.attributesOrder.push(e)}))})),o.addNodeFilter("iframe,video,audio,object,embed",(e=>t=>{let r,o=t.length;for(;o--;)r=t[o],r.parent&&(r.parent.attr("data-mce-object")||(ae(r)&&v(e)?ue(r)||r.replace(le(e,r)):ue(r)||r.replace(ne(e,r))))})(e)),r.addAttributeFilter("data-mce-object",((t,r)=>{var o;let a=t.length;for(;a--;){const s=t[a];if(!s.parent)continue;const i=s.attr(r),n=new D(i,1);if("audio"!==i){const e=s.attr("class");e&&-1!==e.indexOf("mce-preview-object")&&s.firstChild?n.attr({width:s.firstChild.attr("width"),height:s.firstChild.attr("height")}):n.attr({width:s.attr("width"),height:s.attr("height")})}n.attr({style:s.attr("style")});const c=null!==(o=s.attributes)&&void 0!==o?o:[];let m=c.length;for(;m--;){const e=c[m].name;0===e.indexOf("data-mce-p-")&&n.attr(e.substr(11),c[m].value)}const u=s.attr("data-mce-html");if(u){const t=de(e,i,unescape(u));l(t.children(),(e=>n.append(e)))}s.replace(n)}}))})),e.on("SetContent",(()=>{const t=e.dom;l(t.select("span.mce-preview-object"),(e=>{0===t.select("span.mce-shim",e).length&&t.add(e,"span",{class:"mce-shim"})}))}))})(e),(e=>{e.on("click keyup touchend",(()=>{const t=e.selection.getNode();t&&e.dom.hasClass(t,"mce-preview-object")&&e.dom.getAttrib(t,"data-mce-selected")&&t.setAttribute("data-mce-selected","2")})),e.on("ObjectResized",(t=>{const r=t.target;if(r.getAttribute("data-mce-object")){let o=r.getAttribute("data-mce-html");o&&(o=unescape(o),r.setAttribute("data-mce-html",escape(P(o,{width:String(t.width),height:String(t.height)},!1,e.schema))))}}))})(e),(e=>({showDialog:()=>{re(e)}}))(e))))}(); \ No newline at end of file diff --git a/public/libs/tinymce/plugins/nonbreaking/plugin.min.js b/public/libs/tinymce/plugins/nonbreaking/plugin.min.js index 1406420fa44..4baee129054 100644 --- a/public/libs/tinymce/plugins/nonbreaking/plugin.min.js +++ b/public/libs/tinymce/plugins/nonbreaking/plugin.min.js @@ -1,4 +1,4 @@ /** - * TinyMCE version 6.7.2 (2023-10-25) + * TinyMCE version 6.8.4 (2024-06-19) */ !function(){"use strict";var n=tinymce.util.Tools.resolve("tinymce.PluginManager");const e=n=>e=>typeof e===n,o=e("boolean"),a=e("number"),t=n=>e=>e.options.get(n),i=t("nonbreaking_force_tab"),s=t("nonbreaking_wrap"),r=(n,e)=>{let o="";for(let a=0;a{const o=s(n)||n.plugins.visualchars?`${r(" ",e)}`:r(" ",e);n.undoManager.transact((()=>n.insertContent(o)))};var l=tinymce.util.Tools.resolve("tinymce.util.VK");const u=n=>e=>{const o=()=>{e.setEnabled(n.selection.isEditable())};return n.on("NodeChange",o),o(),()=>{n.off("NodeChange",o)}};n.add("nonbreaking",(n=>{(n=>{const e=n.options.register;e("nonbreaking_force_tab",{processor:n=>o(n)?{value:n?3:0,valid:!0}:a(n)?{value:n,valid:!0}:{valid:!1,message:"Must be a boolean or number."},default:!1}),e("nonbreaking_wrap",{processor:"boolean",default:!0})})(n),(n=>{n.addCommand("mceNonBreaking",(()=>{c(n,1)}))})(n),(n=>{const e=()=>n.execCommand("mceNonBreaking");n.ui.registry.addButton("nonbreaking",{icon:"non-breaking",tooltip:"Nonbreaking space",onAction:e,onSetup:u(n)}),n.ui.registry.addMenuItem("nonbreaking",{icon:"non-breaking",text:"Nonbreaking space",onAction:e,onSetup:u(n)})})(n),(n=>{const e=i(n);e>0&&n.on("keydown",(o=>{if(o.keyCode===l.TAB&&!o.isDefaultPrevented()){if(o.shiftKey)return;o.preventDefault(),o.stopImmediatePropagation(),c(n,e)}}))})(n)}))}(); \ No newline at end of file diff --git a/public/libs/tinymce/plugins/pagebreak/plugin.min.js b/public/libs/tinymce/plugins/pagebreak/plugin.min.js index d27a40e5ac9..6ed62f51ed9 100644 --- a/public/libs/tinymce/plugins/pagebreak/plugin.min.js +++ b/public/libs/tinymce/plugins/pagebreak/plugin.min.js @@ -1,4 +1,4 @@ /** - * TinyMCE version 6.7.2 (2023-10-25) + * TinyMCE version 6.8.4 (2024-06-19) */ !function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager"),a=tinymce.util.Tools.resolve("tinymce.Env");const t=e=>a=>a.options.get(e),n=t("pagebreak_separator"),o=t("pagebreak_split_block"),r="mce-pagebreak",s=e=>{const t=``;return e?`

${t}

`:t},c=e=>a=>{const t=()=>{a.setEnabled(e.selection.isEditable())};return e.on("NodeChange",t),t(),()=>{e.off("NodeChange",t)}};e.add("pagebreak",(e=>{(e=>{const a=e.options.register;a("pagebreak_separator",{processor:"string",default:"\x3c!-- pagebreak --\x3e"}),a("pagebreak_split_block",{processor:"boolean",default:!1})})(e),(e=>{e.addCommand("mcePageBreak",(()=>{e.insertContent(s(o(e)))}))})(e),(e=>{const a=()=>e.execCommand("mcePageBreak");e.ui.registry.addButton("pagebreak",{icon:"page-break",tooltip:"Page break",onAction:a,onSetup:c(e)}),e.ui.registry.addMenuItem("pagebreak",{text:"Page break",icon:"page-break",onAction:a,onSetup:c(e)})})(e),(e=>{const a=n(e),t=()=>o(e),c=new RegExp(a.replace(/[\?\.\*\[\]\(\)\{\}\+\^\$\:]/g,(e=>"\\"+e)),"gi");e.on("BeforeSetContent",(e=>{e.content=e.content.replace(c,s(t()))})),e.on("PreInit",(()=>{e.serializer.addNodeFilter("img",(n=>{let o,s,c=n.length;for(;c--;)if(o=n[c],s=o.attr("class"),s&&-1!==s.indexOf(r)){const n=o.parent;if(n&&e.schema.getBlockElements()[n.name]&&t()){n.type=3,n.value=a,n.raw=!0,o.remove();continue}o.type=3,o.value=a,o.raw=!0}}))}))})(e),(e=>{e.on("ResolveName",(a=>{"IMG"===a.target.nodeName&&e.dom.hasClass(a.target,r)&&(a.name="pagebreak")}))})(e)}))}(); \ No newline at end of file diff --git a/public/libs/tinymce/plugins/preview/plugin.min.js b/public/libs/tinymce/plugins/preview/plugin.min.js index 97eb3761ac0..14b0af4beb0 100644 --- a/public/libs/tinymce/plugins/preview/plugin.min.js +++ b/public/libs/tinymce/plugins/preview/plugin.min.js @@ -1,4 +1,4 @@ /** - * TinyMCE version 6.7.2 (2023-10-25) + * TinyMCE version 6.8.4 (2024-06-19) */ !function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager"),t=tinymce.util.Tools.resolve("tinymce.Env"),o=tinymce.util.Tools.resolve("tinymce.util.Tools");const n=e=>t=>t.options.get(e),i=n("content_style"),s=n("content_css_cors"),c=n("body_class"),r=n("body_id");e.add("preview",(e=>{(e=>{e.addCommand("mcePreview",(()=>{(e=>{const n=(e=>{var n;let l="";const a=e.dom.encode,d=null!==(n=i(e))&&void 0!==n?n:"";l+='';const m=s(e)?' crossorigin="anonymous"':"";o.each(e.contentCSS,(t=>{l+='"})),d&&(l+='");const y=r(e),u=c(e),v=' -@endpush - {{ csrf_field() }}
@@ -40,24 +36,10 @@
-
-

- {{ trans('entities.books_default_template_explain') }} -

- -
- @include('form.page-picker', [ - 'name' => 'default_template_id', - 'placeholder' => trans('entities.books_default_template_select'), - 'value' => $book->default_template_id ?? null, - 'selectorEndpoint' => '/search/entity-selector-templates', - ]) -
-
- + @include('entities.template-selector', ['entity' => $book ?? null])
diff --git a/resources/views/books/parts/list.blade.php b/resources/views/books/parts/list.blade.php index 8b658c711a1..13784d24e8d 100644 --- a/resources/views/books/parts/list.blade.php +++ b/resources/views/books/parts/list.blade.php @@ -24,7 +24,7 @@
@else

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

- @if(userCan('book-create-all')) + @if(userCan(\BookStack\Permissions\Permission::BookCreateAll)) +
+ @if($book->sortRule) + @icon('auto-sort') + @endif +
- - - - - + + + + +
    diff --git a/resources/views/books/show.blade.php b/resources/views/books/show.blade.php index dbb09fc9e87..e28d9564829 100644 --- a/resources/views/books/show.blade.php +++ b/resources/views/books/show.blade.php @@ -43,13 +43,13 @@

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

    - @if(userCan('page-create', $book)) + @if(userCan(\BookStack\Permissions\Permission::PageCreate, $book)) @icon('page') {{ trans('entities.books_empty_create_page') }} @endif - @if(userCan('chapter-create', $book)) + @if(userCan(\BookStack\Permissions\Permission::ChapterCreate, $book)) @icon('chapter') {{ trans('entities.books_empty_add_chapter') }} @@ -73,7 +73,7 @@ @include('entities.meta', ['entity' => $book, 'watchOptions' => $watchOptions]) @if($book->hasPermissions())
    - @if(userCan('restrictions-manage', $book)) + @if(userCan(\BookStack\Permissions\Permission::RestrictionsManage, $book)) @icon('lock')
    {{ trans('entities.books_permissions_active') }}
    @@ -93,13 +93,13 @@
    {{ trans('common.actions') }}
    diff --git a/resources/views/books/sort.blade.php b/resources/views/books/sort.blade.php index c82ad4e3b1e..e090708b1d7 100644 --- a/resources/views/books/sort.blade.php +++ b/resources/views/books/sort.blade.php @@ -18,14 +18,38 @@

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

    -

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

    + +
    +

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

    +
    + @php + $autoSortVal = intval(old('auto-sort') ?? $book->sort_rule_id ?? 0); + @endphp + + +
    +
    @include('books.parts.sort-box', ['book' => $book, 'bookChildren' => $bookChildren])
    -
    - {!! csrf_field() !!} + + {{ csrf_field() }}
    diff --git a/resources/views/chapters/edit.blade.php b/resources/views/chapters/edit.blade.php index 36058eff83b..5ace966f584 100644 --- a/resources/views/chapters/edit.blade.php +++ b/resources/views/chapters/edit.blade.php @@ -23,7 +23,7 @@ - @if(userCan('chapter-delete', $chapter) && userCan('book-create-all')) + @if(userCan(\BookStack\Permissions\Permission::ChapterDelete, $chapter) && userCan(\BookStack\Permissions\Permission::BookCreateAll)) @include('chapters.parts.convert-to-book') @endif diff --git a/resources/views/chapters/parts/form.blade.php b/resources/views/chapters/parts/form.blade.php index c6052c93af4..70721631d43 100644 --- a/resources/views/chapters/parts/form.blade.php +++ b/resources/views/chapters/parts/form.blade.php @@ -1,7 +1,3 @@ -@push('head') - -@endpush - {{ csrf_field() }}
    @@ -22,6 +18,15 @@
    +
    + +
    + @include('entities.template-selector', ['entity' => $chapter ?? null]) +
    +
    +
    {{ trans('common.cancel') }} diff --git a/resources/views/chapters/show.blade.php b/resources/views/chapters/show.blade.php index 45e43ad96a9..da914b32d21 100644 --- a/resources/views/chapters/show.blade.php +++ b/resources/views/chapters/show.blade.php @@ -37,13 +37,13 @@

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

    - @if(userCan('page-create', $chapter)) + @if(userCan(\BookStack\Permissions\Permission::PageCreate, $chapter)) @icon('page') {{ trans('entities.books_empty_create_page') }} @endif - @if(userCan('book-update', $book)) + @if(userCan(\BookStack\Permissions\Permission::BookUpdate, $book)) @icon('book') {{ trans('entities.books_empty_sort_current_book') }} @@ -71,7 +71,7 @@ @if($book->hasPermissions())
    - @if(userCan('restrictions-manage', $book)) + @if(userCan(\BookStack\Permissions\Permission::RestrictionsManage, $book)) @icon('lock')
    {{ trans('entities.books_permissions_active') }}
    @@ -87,7 +87,7 @@ @if($chapter->hasPermissions())
    - @if(userCan('restrictions-manage', $chapter)) + @if(userCan(\BookStack\Permissions\Permission::RestrictionsManage, $chapter)) @icon('lock')
    {{ trans('entities.chapters_permissions_active') }}
    @@ -107,7 +107,7 @@
    {{ trans('common.actions') }}
    diff --git a/resources/views/comments/comment-branch.blade.php b/resources/views/comments/comment-branch.blade.php index 78d19ac3ea4..658c33219c3 100644 --- a/resources/views/comments/comment-branch.blade.php +++ b/resources/views/comments/comment-branch.blade.php @@ -1,13 +1,16 @@ +{{-- +$branch CommentTreeNode +--}}
    -
    - @include('comments.comment', ['comment' => $branch['comment']]) +
    + @include('comments.comment', ['comment' => $branch->comment])
    - @foreach($branch['children'] as $childBranch) + @foreach($branch->children as $childBranch) @include('comments.comment-branch', ['branch' => $childBranch]) @endforeach
    diff --git a/resources/views/comments/comment.blade.php b/resources/views/comments/comment.blade.php index 1cb70916087..10cd02035fd 100644 --- a/resources/views/comments/comment.blade.php +++ b/resources/views/comments/comment.blade.php @@ -1,16 +1,21 @@ +@php + $commentHtml = $comment->safeHtml(); +@endphp
    @if ($comment->createdBy)
    - {{ $comment->createdBy->name }} + {{ $comment->createdBy->name }}
    @endif
    @@ -19,44 +24,55 @@ class="comment-box"> @else {{ trans('common.deleted_user') }} @endif -  {{ trans('entities.comment_created', ['createDiff' => $comment->created]) }} +  {{ trans('entities.comment_created', ['createDiff' => $dates->relative($comment->created_at) ]) }} @if($comment->isUpdated()) • - + {{ trans('entities.comment_updated_indicator') }} @endif
    - @if(!$readOnly && (userCan('comment-create-all') || userCan('comment-update', $comment) || userCan('comment-delete', $comment))) -
    - @if(userCan('comment-create-all')) - - @endif - @if(userCan('comment-update', $comment)) - - @endif - @if(userCan('comment-delete', $comment)) - - @endif - + @if(!$readOnly && (userCan(\BookStack\Permissions\Permission::CommentCreateAll) || userCan(\BookStack\Permissions\Permission::CommentUpdate, $comment) || userCan(\BookStack\Permissions\Permission::CommentDelete, $comment))) +
    + @if(userCan(\BookStack\Permissions\Permission::CommentCreateAll)) + + @endif + @if(!$comment->parent_id && (userCan(\BookStack\Permissions\Permission::CommentUpdate, $comment) || userCan(\BookStack\Permissions\Permission::CommentDelete, $comment))) + + @endif + @if(userCan(\BookStack\Permissions\Permission::CommentUpdate, $comment)) + + @endif + @if(userCan(\BookStack\Permissions\Permission::CommentDelete, $comment)) + + @endif +  •  -
    +
    @endif
    @@ -66,19 +82,33 @@ class="comment-box"> - @if(!$readOnly && userCan('comment-update', $comment)) + @if(!$readOnly && userCan(\BookStack\Permissions\Permission::CommentUpdate, $comment)) diff --git a/resources/views/comments/comments.blade.php b/resources/views/comments/comments.blade.php index 26d286290c2..9dc0a9b8982 100644 --- a/resources/views/comments/comments.blade.php +++ b/resources/views/comments/comments.blade.php @@ -1,37 +1,80 @@ -
    -
    -
    {{ trans_choice('entities.comment_count', $commentTree->count(), ['count' => $commentTree->count()]) }}
    - @if ($commentTree->empty() && userCan('comment-create-all')) -
    +
    +
    + + +
    + @if ($commentTree->empty() && userCan(\BookStack\Permissions\Permission::CommentCreateAll)) +
    + class="button outline mb-m ml-auto">{{ trans('entities.comment_add') }}
    @endif
    -
    - @foreach($commentTree->get() as $branch) +
    +
    + @foreach($commentTree->getActive() as $branch) + @include('comments.comment-branch', ['branch' => $branch, 'readOnly' => false]) + @endforeach +
    + +

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

    + + @if(userCan(\BookStack\Permissions\Permission::CommentCreateAll)) + @include('comments.create') + @if (!$commentTree->empty()) +
    + +
    + @endif + @endif +
    + + - @if(userCan('comment-create-all')) - @include('comments.create') - - @if (!$commentTree->empty()) -
    - -
    - @endif + @if(userCan(\BookStack\Permissions\Permission::CommentCreateAll) || $commentTree->canUpdateAny()) + @push('body-end') + @include('form.editor-translations') + @include('entities.selector-popup') + @endpush @endif
    \ No newline at end of file diff --git a/resources/views/comments/create.blade.php b/resources/views/comments/create.blade.php index cb7905ddc44..134ed516425 100644 --- a/resources/views/comments/create.blade.php +++ b/resources/views/comments/create.blade.php @@ -12,11 +12,21 @@
    +
    -
    diff --git a/resources/views/common/activity-item.blade.php b/resources/views/common/activity-item.blade.php index 89d44b15231..1d3c7bd755f 100644 --- a/resources/views/common/activity-item.blade.php +++ b/resources/views/common/activity-item.blade.php @@ -16,15 +16,15 @@ {{ $activity->getText() }} - @if($activity->entity && is_null($activity->entity->deleted_at)) - {{ $activity->entity->name }} + @if($activity->loggable && is_null($activity->loggable->deleted_at)) + {{ $activity->loggable->name }} @endif - @if($activity->entity && !is_null($activity->entity->deleted_at)) - "{{ $activity->entity->name }}" + @if($activity->loggable && !is_null($activity->loggable->deleted_at)) + "{{ $activity->loggable->name }}" @endif
    - @icon('time'){{ $activity->created_at->diffForHumans() }} + @icon('time'){{ $dates->relative($activity->created_at) }}
    diff --git a/resources/views/common/dark-mode-toggle.blade.php b/resources/views/common/dark-mode-toggle.blade.php index 531755109ad..38493251902 100644 --- a/resources/views/common/dark-mode-toggle.blade.php +++ b/resources/views/common/dark-mode-toggle.blade.php @@ -3,8 +3,8 @@ {{ method_field('patch') }} @if(setting()->getForCurrentUser('dark-mode-enabled')) - + @else - + @endif \ No newline at end of file diff --git a/resources/views/common/sort.blade.php b/resources/views/common/sort.blade.php index e5336d3a255..d47351cb347 100644 --- a/resources/views/common/sort.blade.php +++ b/resources/views/common/sort.blade.php @@ -29,10 +29,14 @@
    diff --git a/resources/views/entities/body-tag-classes.blade.php b/resources/views/entities/body-tag-classes.blade.php index 08427f1a5ed..f9ba023c37e 100644 --- a/resources/views/entities/body-tag-classes.blade.php +++ b/resources/views/entities/body-tag-classes.blade.php @@ -1 +1 @@ -@push('body-class', e((new \BookStack\Activity\Tools\TagClassGenerator($entity->tags->all()))->generateAsString() . ' ')) \ No newline at end of file +@push('body-class', e((new \BookStack\Activity\Tools\TagClassGenerator($entity))->generateAsString() . ' ')) \ No newline at end of file diff --git a/resources/views/entities/book-tree.blade.php b/resources/views/entities/book-tree.blade.php index ce016143a30..c95b1eebc43 100644 --- a/resources/views/entities/book-tree.blade.php +++ b/resources/views/entities/book-tree.blade.php @@ -5,7 +5,7 @@ class="book-tree mb-xl"
    {{ trans('entities.books_navigation') }}
    \ No newline at end of file diff --git a/resources/views/entities/list-item-basic.blade.php b/resources/views/entities/list-item-basic.blade.php index 398c33b93f6..4c66eec3dfa 100644 --- a/resources/views/entities/list-item-basic.blade.php +++ b/resources/views/entities/list-item-basic.blade.php @@ -1,5 +1,8 @@ getType(); ?> - + @icon($type)

    {{ $entity->preview_name ?? $entity->name }}

    diff --git a/resources/views/entities/list-item.blade.php b/resources/views/entities/list-item.blade.php index 2fadef19193..5174de43197 100644 --- a/resources/views/entities/list-item.blade.php +++ b/resources/views/entities/list-item.blade.php @@ -27,9 +27,9 @@ @endif @if(($showUpdatedBy ?? false) && $entity->relationLoaded('updatedBy') && $entity->updatedBy) - + {!! trans('entities.meta_updated_name', [ - 'timeLength' => $entity->updated_at->diffForHumans(), + 'timeLength' => $dates->relative($entity->updated_at), 'user' => e($entity->updatedBy->name) ]) !!} diff --git a/resources/views/entities/meta.blade.php b/resources/views/entities/meta.blade.php index 9d3c4b956a7..060c197a466 100644 --- a/resources/views/entities/meta.blade.php +++ b/resources/views/entities/meta.blade.php @@ -31,7 +31,7 @@ @icon('star')
    @@ -39,7 +39,7 @@ @else
    @icon('star') - {{ trans('entities.meta_created', ['timeLength' => $entity->created_at->diffForHumans()]) }} + {{ trans('entities.meta_created', ['timeLength' => $dates->relative($entity->created_at)]) }}
    @endif @@ -48,7 +48,7 @@ @icon('edit')
    {!! trans('entities.meta_updated_name', [ - 'timeLength' => '' . $entity->updated_at->diffForHumans() .'', + 'timeLength' => '' . $dates->relative($entity->updated_at) .'', 'user' => "".e($entity->updatedBy->name). "" ]) !!}
    @@ -56,7 +56,7 @@ @elseif (!$entity->isA('revision'))
    @icon('edit') - {{ trans('entities.meta_updated', ['timeLength' => $entity->updated_at->diffForHumans()]) }} + {{ trans('entities.meta_updated', ['timeLength' => $dates->relative($entity->updated_at)]) }}
    @endif diff --git a/resources/views/entities/selector.blade.php b/resources/views/entities/selector.blade.php index c1280cfb2f7..0cdf4376cc7 100644 --- a/resources/views/entities/selector.blade.php +++ b/resources/views/entities/selector.blade.php @@ -1,3 +1,11 @@ +{{-- +$name - string +$autofocus - boolean, optional +$entityTypes - string, optional +$entityPermission - string, optional +$selectorEndpoint - string, optional +$selectorSize - string, optional (compact) +--}}
    +

    + {{ trans('entities.default_template_explain') }} +

    + +
    + @include('form.page-picker', [ + 'name' => 'default_template_id', + 'placeholder' => trans('entities.default_template_select'), + 'value' => $entity->default_template_id ?? null, + 'selectorEndpoint' => '/search/entity-selector-templates', + ]) +
    +
    \ No newline at end of file diff --git a/resources/views/entities/watch-controls.blade.php b/resources/views/entities/watch-controls.blade.php index 4a119c272cd..81e80ed981c 100644 --- a/resources/views/entities/watch-controls.blade.php +++ b/resources/views/entities/watch-controls.blade.php @@ -1,6 +1,11 @@ -
  • +

  • @endforeach
  • {{ trans('entities.watch_change_default') }}
  • diff --git a/resources/views/errors/404.blade.php b/resources/views/errors/404.blade.php index 27d66b30bb9..dc24a558d84 100644 --- a/resources/views/errors/404.blade.php +++ b/resources/views/errors/404.blade.php @@ -1,5 +1,5 @@ @extends('layouts.simple') - +@inject('popular', \BookStack\Entities\Queries\QueryPopular::class) @section('content')
    @@ -28,7 +28,7 @@

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

    - @include('entities.list', ['entities' => (new \BookStack\Entities\Queries\Popular)->run(10, 0, ['page']), 'style' => 'compact']) + @include('entities.list', ['entities' => $popular->run(10, 0, ['page']), 'style' => 'compact'])
    @@ -36,7 +36,7 @@

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

    - @include('entities.list', ['entities' => (new \BookStack\Entities\Queries\Popular)->run(10, 0, ['book']), 'style' => 'compact']) + @include('entities.list', ['entities' => $popular->run(10, 0, ['book']), 'style' => 'compact'])
    @@ -44,7 +44,7 @@

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

    - @include('entities.list', ['entities' => (new \BookStack\Entities\Queries\Popular)->run(10, 0, ['chapter']), 'style' => 'compact']) + @include('entities.list', ['entities' => $popular->run(10, 0, ['chapter']), 'style' => 'compact'])
    diff --git a/resources/views/exports/import-show.blade.php b/resources/views/exports/import-show.blade.php new file mode 100644 index 00000000000..1c46b7a0b87 --- /dev/null +++ b/resources/views/exports/import-show.blade.php @@ -0,0 +1,88 @@ +@extends('layouts.simple') + +@section('body') +
    + +
    +

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

    +

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

    + + @if(session()->has('import_errors')) +
    + +

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

    + @foreach(session()->get('import_errors') ?? [] as $error) +

    {{ $error }}

    + @endforeach +
    +
    + @endif + +
    + +
    +
    + @include('exports.parts.import-item', ['type' => $import->type, 'model' => $data]) +
    +
    +
    {{ trans('entities.import_size', ['size' => $import->getSizeString()]) }}
    +
    {{ trans('entities.import_uploaded_at', ['relativeTime' => $dates->relative($import->created_at)]) }}
    + @if($import->createdBy) +
    + {{ trans('entities.import_uploaded_by') }} + {{ $import->createdBy->name }} +
    + @endif +
    +
    +
    + +
    + {{ csrf_field() }} + + @if($import->type === 'page' || $import->type === 'chapter') +
    + +

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

    + @if($errors->has('parent')) +
    + @include('form.errors', ['name' => 'parent']) +
    + @endif + @include('entities.selector', [ + 'name' => 'parent', + 'entityTypes' => $import->type === 'page' ? 'chapter,book' : 'book', + 'entityPermission' => "{$import->type}-create", + 'selectorSize' => 'compact small', + ]) + @endif + +
    + {{ trans('common.cancel') }} +
    + + +
    + +
    +
    +
    +
    + +
    + {{ method_field('DELETE') }} + {{ csrf_field() }} +
    + +@stop diff --git a/resources/views/exports/import.blade.php b/resources/views/exports/import.blade.php new file mode 100644 index 00000000000..be9de4c0e91 --- /dev/null +++ b/resources/views/exports/import.blade.php @@ -0,0 +1,56 @@ +@extends('layouts.simple') + +@section('body') + +
    + +
    +

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

    +
    + {{ csrf_field() }} +
    +

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

    +
    +
    + + + @include('form.errors', ['name' => 'file']) +
    +
    +
    + + @if(count($zipErrors) > 0) +

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

    +
      + @foreach($zipErrors as $key => $error) +
    • [{{ $key }}]: {{ $error }}
    • + @endforeach +
    + @endif + +
    + {{ trans('common.cancel') }} + +
    +
    +
    + +
    +

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

    + @if(count($imports) === 0) +

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

    + @else +
    + @foreach($imports as $import) + @include('exports.parts.import', ['import' => $import]) + @endforeach +
    + @endif +
    +
    + +@stop diff --git a/resources/views/exports/parts/import-item.blade.php b/resources/views/exports/parts/import-item.blade.php new file mode 100644 index 00000000000..5da4b21405d --- /dev/null +++ b/resources/views/exports/parts/import-item.blade.php @@ -0,0 +1,28 @@ +{{-- +$type - string +$model - object +--}} +
    +

    @icon($type){{ $model->name }}

    +
    +
    + @if($model->attachments ?? []) + @icon('attach'){{ count($model->attachments) }} + @endif + @if($model->images ?? []) + @icon('image'){{ count($model->images) }} + @endif + @if($model->tags ?? []) + @icon('tag'){{ count($model->tags) }} + @endif +
    + @if(method_exists($model, 'children')) + @foreach($model->children() as $child) + @include('exports.parts.import-item', [ + 'type' => ($child instanceof \BookStack\Exports\ZipExports\Models\ZipExportPage) ? 'page' : 'chapter', + 'model' => $child + ]) + @endforeach + @endif +
    +
    \ No newline at end of file diff --git a/resources/views/exports/parts/import.blade.php b/resources/views/exports/parts/import.blade.php new file mode 100644 index 00000000000..dc287370e70 --- /dev/null +++ b/resources/views/exports/parts/import.blade.php @@ -0,0 +1,10 @@ +
    + +
    +
    {{ $import->getSizeString() }}
    +
    @icon('time'){{ $dates->relative($import->created_at) }}
    +
    +
    \ No newline at end of file diff --git a/resources/views/exports/parts/meta.blade.php b/resources/views/exports/parts/meta.blade.php index d4128898bc8..00117f4a157 100644 --- a/resources/views/exports/parts/meta.blade.php +++ b/resources/views/exports/parts/meta.blade.php @@ -4,13 +4,13 @@ @endif @icon('star'){!! trans('entities.meta_created' . ($entity->createdBy ? '_name' : ''), [ - 'timeLength' => $entity->created_at->isoFormat('D MMMM Y HH:mm:ss'), + 'timeLength' => $dates->absolute($entity->created_at), 'user' => e($entity->createdBy->name ?? ''), ]) !!}
    @icon('edit'){!! trans('entities.meta_updated' . ($entity->updatedBy ? '_name' : ''), [ - 'timeLength' => $entity->updated_at->isoFormat('D MMMM Y HH:mm:ss'), + 'timeLength' => $dates->absolute($entity->updated_at), 'user' => e($entity->updatedBy->name ?? '') ]) !!}
    \ No newline at end of file diff --git a/resources/views/form/description-html-input.blade.php b/resources/views/form/description-html-input.blade.php index 3cf726ba489..52244eda6f9 100644 --- a/resources/views/form/description-html-input.blade.php +++ b/resources/views/form/description-html-input.blade.php @@ -1,5 +1,4 @@ diff --git a/resources/views/form/errors.blade.php b/resources/views/form/errors.blade.php index 03cd4be88f0..72d41ee56c7 100644 --- a/resources/views/form/errors.blade.php +++ b/resources/views/form/errors.blade.php @@ -1,3 +1,6 @@ +{{-- +$name - string +--}} @if($errors->has($name))
    {{ $errors->first($name) }}
    @endif \ No newline at end of file diff --git a/resources/views/help/licenses.blade.php b/resources/views/help/licenses.blade.php new file mode 100644 index 00000000000..09126ddadb6 --- /dev/null +++ b/resources/views/help/licenses.blade.php @@ -0,0 +1,67 @@ +@extends('layouts.simple') + +@section('body') + +
    + +
     
    + + + +
    +

    {{ trans('settings.licenses_bookstack') }}

    +
    {{ $license }}
    +

    BookStack® is a UK registered trade mark of Daniel Brown.

    +
    + +
    +

    {{ trans('settings.licenses_php') }}

    +
    {{ $phpLibData }}
    +
    + +
    +

    {{ trans('settings.licenses_js') }}

    +
    {{ $jsLibData }}
    +
    + +
    +

    {{ trans('settings.licenses_other') }}

    +
    BookStack makes heavy use of PHP: + License: PHP License, version 3.01 + License File: https://www.php.net/license/3_01.txt + Copyright: Copyright (c) 1999 - 2019 The PHP Group. All rights reserved. + Link: https://www.php.net/ + ----------- + BookStack uses Icons from Google Material Icons: + License: Apache License Version 2.0 + License File: https://github.com/google/material-design-icons/blob/master/LICENSE + Copyright: Copyright 2020 Google LLC + Link: https://github.com/google/material-design-icons + ----------- + BookStack is distributed with TinyMCE: + License: MIT + License File: https://github.com/tinymce/tinymce/blob/release/6.7/LICENSE.TXT + Copyright: Copyright (c) 2022 Ephox Corporation DBA Tiny Technologies, Inc. + Link: https://github.com/tinymce/tinymce + ----------- + BookStack's newer WYSIWYG editor is based upon lexical code: + License: MIT + License File: https://github.com/facebook/lexical/blob/v0.17.1/LICENSE + Copyright: Copyright (c) Meta Platforms, Inc. and affiliates. + Link: https://github.com/facebook/lexical +
    +
    +
    + +@endsection \ No newline at end of file diff --git a/resources/views/help/tinymce.blade.php b/resources/views/help/tinymce.blade.php new file mode 100644 index 00000000000..8ff59c8d664 --- /dev/null +++ b/resources/views/help/tinymce.blade.php @@ -0,0 +1,146 @@ +@extends('layouts.plain') +@section('document-class', 'bg-white ' . (setting()->getForCurrentUser('dark-mode-enabled') ? 'dark-mode ' : '')) + +@section('content') +
    + +

    {{ trans('editor.editor_license') }}

    +

    + {!! trans('editor.editor_tiny_license', ['tinyLink' => 'TinyMCE']) !!} +
    + {{ trans('editor.editor_tiny_license_link') }} +

    + +

    {{ trans('editor.shortcuts') }}

    + +

    {{ trans('editor.shortcuts_intro') }}

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    {{ trans('editor.shortcut') }} {{ trans('editor.windows_linux') }}{{ trans('editor.shortcut') }} {{ trans('editor.mac') }}{{ trans('editor.description') }}
    Ctrl+SCmd+S{{ trans('entities.pages_edit_save_draft') }}
    Ctrl+EnterCmd+Enter{{ trans('editor.save_continue') }}
    Ctrl+BCmd+B{{ trans('editor.bold') }}
    Ctrl+ICmd+I{{ trans('editor.italic') }}
    + Ctrl+1
    + Ctrl+2
    + Ctrl+3
    + Ctrl+4 +
    + Cmd+1
    + Cmd+2
    + Cmd+3
    + Cmd+4 +
    + {{ trans('editor.header_large') }}
    + {{ trans('editor.header_medium') }}
    + {{ trans('editor.header_small') }}
    + {{ trans('editor.header_tiny') }} +
    + Ctrl+5
    + Ctrl+D +
    + Cmd+5
    + Cmd+D +
    {{ trans('editor.paragraph') }}
    + Ctrl+6
    + Ctrl+Q +
    + Cmd+6
    + Cmd+Q +
    {{ trans('editor.blockquote') }}
    + Ctrl+7
    + Ctrl+E +
    + Cmd+7
    + Cmd+E +
    {{ trans('editor.insert_code_block') }}
    + Ctrl+8
    + Ctrl+Shift+E +
    + Cmd+8
    + Cmd+Shift+E +
    {{ trans('editor.inline_code') }}
    Ctrl+9Cmd+9 + {{ trans('editor.callouts') }}
    + {{ trans('editor.callouts_cycle') }} +
    + Ctrl+O
    + Ctrl+P +
    + Cmd+O
    + Cmd+P +
    + {{ trans('editor.list_numbered') }}
    + {{ trans('editor.list_bullet') }} +
    + Ctrl+Shift+K + + Cmd+Shift+K + {{ trans('editor.link_selector') }}
    + +
    +@endsection + diff --git a/resources/views/help/wysiwyg.blade.php b/resources/views/help/wysiwyg.blade.php index 8fc0912586d..4fc00b0e1e6 100644 --- a/resources/views/help/wysiwyg.blade.php +++ b/resources/views/help/wysiwyg.blade.php @@ -1,146 +1,138 @@ -@extends('layouts.plain') -@section('document-class', 'bg-white ' . (setting()->getForCurrentUser('dark-mode-enabled') ? 'dark-mode ' : '')) +

    {{ trans('editor.shortcuts') }}

    -@section('content') -
    - -

    {{ trans('editor.editor_license') }}

    -

    - {!! trans('editor.editor_tiny_license', ['tinyLink' => 'TinyMCE']) !!} -
    - {{ trans('editor.editor_tiny_license_link') }} -

    - -

    {{ trans('editor.shortcuts') }}

    - -

    {{ trans('editor.shortcuts_intro') }}

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    {{ trans('editor.shortcut') }} {{ trans('editor.windows_linux') }}{{ trans('editor.shortcut') }} {{ trans('editor.mac') }}{{ trans('editor.description') }}
    Ctrl+SCmd+S{{ trans('entities.pages_edit_save_draft') }}
    Ctrl+EnterCmd+Enter{{ trans('editor.save_continue') }}
    Ctrl+BCmd+B{{ trans('editor.bold') }}
    Ctrl+ICmd+I{{ trans('editor.italic') }}
    - Ctrl+1
    - Ctrl+2
    - Ctrl+3
    - Ctrl+4 -
    - Cmd+1
    - Cmd+2
    - Cmd+3
    - Cmd+4 -
    - {{ trans('editor.header_large') }}
    - {{ trans('editor.header_medium') }}
    - {{ trans('editor.header_small') }}
    - {{ trans('editor.header_tiny') }} -
    - Ctrl+5
    - Ctrl+D -
    - Cmd+5
    - Cmd+D -
    {{ trans('editor.paragraph') }}
    - Ctrl+6
    - Ctrl+Q -
    - Cmd+6
    - Cmd+Q -
    {{ trans('editor.blockquote') }}
    - Ctrl+7
    - Ctrl+E -
    - Cmd+7
    - Cmd+E -
    {{ trans('editor.insert_code_block') }}
    - Ctrl+Shift+8
    - Ctrl+Shift+E -
    - Cmd+Shift+8
    - Cmd+Shift+E -
    {{ trans('editor.inline_code') }}
    Ctrl+9Cmd+9 - {{ trans('editor.callouts') }}
    - {{ trans('editor.callouts_cycle') }} -
    - Ctrl+O
    - Ctrl+P -
    - Cmd+O
    - Cmd+P -
    - {{ trans('editor.list_numbered') }}
    - {{ trans('editor.list_bullet') }} -
    - Ctrl+Shift+K - - Cmd+Shift+K - {{ trans('editor.link_selector') }}
    - -
    -@endsection +

    {{ trans('editor.shortcuts_intro') }}

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    {{ trans('editor.shortcut') }} {{ trans('editor.windows_linux') }}{{ trans('editor.shortcut') }} {{ trans('editor.mac') }}{{ trans('editor.description') }}
    Ctrl+SCmd+S{{ trans('entities.pages_edit_save_draft') }}
    Ctrl+EnterCmd+Enter{{ trans('editor.save_continue') }}
    Ctrl+BCmd+B{{ trans('editor.bold') }}
    Ctrl+ICmd+I{{ trans('editor.italic') }}
    + Ctrl+1
    + Ctrl+2
    + Ctrl+3
    + Ctrl+4 +
    + Cmd+1
    + Cmd+2
    + Cmd+3
    + Cmd+4 +
    + {{ trans('editor.header_large') }}
    + {{ trans('editor.header_medium') }}
    + {{ trans('editor.header_small') }}
    + {{ trans('editor.header_tiny') }} +
    + Ctrl+5
    + Ctrl+D +
    + Cmd+5
    + Cmd+D +
    {{ trans('editor.paragraph') }}
    + Ctrl+6
    + Ctrl+Q +
    + Cmd+6
    + Cmd+Q +
    {{ trans('editor.blockquote') }}
    + Ctrl+7
    + Ctrl+E +
    + Cmd+7
    + Cmd+E +
    {{ trans('editor.insert_code_block') }}
    + Ctrl+8
    + Ctrl+Shift+E +
    + Cmd+8
    + Cmd+Shift+E +
    {{ trans('editor.inline_code') }}
    Ctrl+9Cmd+9 + {{ trans('editor.callouts') }}
    + {{ trans('editor.callouts_cycle') }} +
    + Ctrl+O
    + Ctrl+P +
    + Cmd+O
    + Cmd+P +
    + {{ trans('editor.list_numbered') }}
    + {{ trans('editor.list_bullet') }} +
    + Ctrl+Shift+K + + Cmd+Shift+K + {{ trans('editor.link_selector') }}
    +

    {{ trans('editor.editor_license') }}

    +

    + {!! trans('editor.editor_lexical_license', ['lexicalLink' => 'Lexical']) !!} +
    + Copyright (c) Meta Platforms, Inc. and affiliates. +
    + {{ trans('editor.editor_lexical_license_link') }} +

    \ No newline at end of file diff --git a/resources/views/home/books.blade.php b/resources/views/home/books.blade.php index a2f2bf79605..afd3f30fce2 100644 --- a/resources/views/home/books.blade.php +++ b/resources/views/home/books.blade.php @@ -12,7 +12,7 @@
    {{ trans('common.actions') }}
    diff --git a/resources/views/settings/parts/navbar.blade.php b/resources/views/settings/parts/navbar.blade.php index e229f5e654f..cda8bdae0c1 100644 --- a/resources/views/settings/parts/navbar.blade.php +++ b/resources/views/settings/parts/navbar.blade.php @@ -1,19 +1,19 @@ \ No newline at end of file diff --git a/resources/views/settings/recycle-bin/parts/recycle-bin-list-item.blade.php b/resources/views/settings/recycle-bin/parts/recycle-bin-list-item.blade.php index 8af598b1e51..b18f9cbe08c 100644 --- a/resources/views/settings/recycle-bin/parts/recycle-bin-list-item.blade.php +++ b/resources/views/settings/recycle-bin/parts/recycle-bin-list-item.blade.php @@ -38,10 +38,14 @@
    {{ trans('settings.recycle_bin_deleted_at') }}:
    {{ $deletion->created_at }}
    diff --git a/resources/views/settings/roles/edit.blade.php b/resources/views/settings/roles/edit.blade.php index bd1dbb76322..da7835e1a02 100644 --- a/resources/views/settings/roles/edit.blade.php +++ b/resources/views/settings/roles/edit.blade.php @@ -35,11 +35,11 @@ {{ $user->name }}
    - @if(userCan('users-manage') || user()->id == $user->id) + @if(userCan(\BookStack\Permissions\Permission::UsersManage) || user()->id == $user->id) id}") }}"> @endif {{ $user->name }} - @if(userCan('users-manage') || user()->id == $user->id) + @if(userCan(\BookStack\Permissions\Permission::UsersManage) || user()->id == $user->id) @endif
    diff --git a/resources/views/settings/roles/index.blade.php b/resources/views/settings/roles/index.blade.php index 27ee9ce3f90..3ccdd9b851c 100644 --- a/resources/views/settings/roles/index.blade.php +++ b/resources/views/settings/roles/index.blade.php @@ -24,6 +24,7 @@
    diff --git a/resources/views/settings/roles/parts/form.blade.php b/resources/views/settings/roles/parts/form.blade.php index 9fa76f2bfd7..a77b80e4c69 100644 --- a/resources/views/settings/roles/parts/form.blade.php +++ b/resources/views/settings/roles/parts/form.blade.php @@ -37,6 +37,7 @@
    @include('settings.roles.parts.checkbox', ['permission' => 'templates-manage', 'label' => trans('settings.role_manage_page_templates')])
    @include('settings.roles.parts.checkbox', ['permission' => 'access-api', 'label' => trans('settings.role_access_api')])
    @include('settings.roles.parts.checkbox', ['permission' => 'content-export', 'label' => trans('settings.role_export_content')])
    +
    @include('settings.roles.parts.checkbox', ['permission' => 'content-import', 'label' => trans('settings.role_import_content')])
    @include('settings.roles.parts.checkbox', ['permission' => 'editor-change', 'label' => trans('settings.role_editor_change')])
    @include('settings.roles.parts.checkbox', ['permission' => 'receive-notifications', 'label' => trans('settings.role_notifications')])
    diff --git a/resources/views/settings/sort-rules/create.blade.php b/resources/views/settings/sort-rules/create.blade.php new file mode 100644 index 00000000000..e1d5c7c4630 --- /dev/null +++ b/resources/views/settings/sort-rules/create.blade.php @@ -0,0 +1,24 @@ +@extends('layouts.simple') + +@section('body') + +
    + + @include('settings.parts.navbar', ['selected' => 'settings']) + +
    +

    {{ trans('settings.sort_rule_create') }}

    + +
    + {{ csrf_field() }} + @include('settings.sort-rules.parts.form', ['model' => null]) + +
    + {{ trans('common.cancel') }} + +
    +
    +
    +
    + +@stop diff --git a/resources/views/settings/sort-rules/edit.blade.php b/resources/views/settings/sort-rules/edit.blade.php new file mode 100644 index 00000000000..8bf04701fe0 --- /dev/null +++ b/resources/views/settings/sort-rules/edit.blade.php @@ -0,0 +1,54 @@ +@extends('layouts.simple') + +@section('body') + +
    + + @include('settings.parts.navbar', ['selected' => 'settings']) + +
    +

    {{ trans('settings.sort_rule_edit') }}

    + +
    + {{ method_field('PUT') }} + {{ csrf_field() }} + + @include('settings.sort-rules.parts.form', ['model' => $rule]) + +
    + {{ trans('common.cancel') }} + +
    +
    +
    + +
    +
    +
    +

    {{ trans('settings.sort_rule_delete') }}

    +

    {{ trans('settings.sort_rule_delete_desc') }}

    + @if($errors->has('delete')) + @foreach($errors->get('delete') as $error) +

    {{ $error }}

    + @endforeach + @endif +
    +
    +
    + {{ method_field('DELETE') }} + {{ csrf_field() }} + + @if($errors->has('delete')) + + @endif + +
    + +
    +
    +
    +
    +
    +
    + +@stop diff --git a/resources/views/settings/sort-rules/parts/form.blade.php b/resources/views/settings/sort-rules/parts/form.blade.php new file mode 100644 index 00000000000..d6de947b6c7 --- /dev/null +++ b/resources/views/settings/sort-rules/parts/form.blade.php @@ -0,0 +1,56 @@ +
    +
    +
    + +

    {{ trans('settings.sort_rule_details_desc') }}

    +
    +
    +
    + + @include('form.text', ['name' => 'name']) +
    +
    +
    + +
    + +

    {{ trans('settings.sort_rule_operations_desc') }}

    + @include('form.errors', ['name' => 'sequence']) + + + + @php + $configuredOps = old('sequence') ? \BookStack\Sorting\SortRuleOperation::fromSequence(old('sequence')) : ($model?->getOperations() ?? []); + @endphp + +
    +
    + +
      +
    • {{ trans('settings.sort_rule_configured_operations_empty') }}
    • + + @foreach($configuredOps as $operation) + @include('settings.sort-rules.parts.operation', ['operation' => $operation]) + @endforeach +
    +
    + +
    + +
      +
    • {{ trans('settings.sort_rule_available_operations_empty') }}
    • + @foreach(\BookStack\Sorting\SortRuleOperation::allExcluding($configuredOps) as $operation) + @include('settings.sort-rules.parts.operation', ['operation' => $operation]) + @endforeach +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/resources/views/settings/sort-rules/parts/operation.blade.php b/resources/views/settings/sort-rules/parts/operation.blade.php new file mode 100644 index 00000000000..3feb68a4742 --- /dev/null +++ b/resources/views/settings/sort-rules/parts/operation.blade.php @@ -0,0 +1,15 @@ +
  • +
    @icon('grip')
    +
    {{ $operation->getLabel() }}
    +
    + + + + +
    +
  • \ No newline at end of file diff --git a/resources/views/settings/sort-rules/parts/sort-rule-list-item.blade.php b/resources/views/settings/sort-rules/parts/sort-rule-list-item.blade.php new file mode 100644 index 00000000000..5236cb412a3 --- /dev/null +++ b/resources/views/settings/sort-rules/parts/sort-rule-list-item.blade.php @@ -0,0 +1,12 @@ +
    + +
    + {{ implode(', ', array_map(fn ($op) => $op->getLabel(), $rule->getOperations())) }} +
    +
    + @icon('book'){{ $rule->books_count ?? 0 }} +
    +
    \ No newline at end of file diff --git a/resources/views/settings/webhooks/edit.blade.php b/resources/views/settings/webhooks/edit.blade.php index 680fc1207b0..22d0c0183c2 100644 --- a/resources/views/settings/webhooks/edit.blade.php +++ b/resources/views/settings/webhooks/edit.blade.php @@ -14,9 +14,17 @@

    - {{ trans('settings.webhooks_last_called') }} {{ $webhook->last_called_at ? $webhook->last_called_at->diffForHumans() : trans('common.never') }} + @if($webhook->last_called_at) + {{ trans('settings.webhooks_last_called') }} {{ $dates->relative($webhook->last_called_at) }} + @else + {{ trans('settings.webhooks_last_called') }} {{ trans('common.never') }} + @endif
    - {{ trans('settings.webhooks_last_errored') }} {{ $webhook->last_errored_at ? $webhook->last_errored_at->diffForHumans() : trans('common.never') }} + @if($webhook->last_errored_at) + {{ trans('settings.webhooks_last_errored') }} {{ $dates->relative($webhook->last_errored_at) }} + @else + {{ trans('settings.webhooks_last_errored') }} {{ trans('common.never') }} + @endif

    diff --git a/resources/views/shelves/index.blade.php b/resources/views/shelves/index.blade.php index 85663b41307..bb7c57e0fde 100644 --- a/resources/views/shelves/index.blade.php +++ b/resources/views/shelves/index.blade.php @@ -9,7 +9,7 @@
    {{ trans('common.actions') }}
    @else

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

    - @if(userCan('bookshelf-create-all')) + @if(userCan(\BookStack\Permissions\Permission::BookshelfCreateAll))
    diff --git a/resources/views/shelves/show.blade.php b/resources/views/shelves/show.blade.php index 11baccaf463..633f959f3c9 100644 --- a/resources/views/shelves/show.blade.php +++ b/resources/views/shelves/show.blade.php @@ -48,13 +48,13 @@

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

    - @if(userCan('book-create-all') && userCan('bookshelf-update', $shelf)) + @if(userCan(\BookStack\Permissions\Permission::BookCreateAll) && userCan(\BookStack\Permissions\Permission::BookshelfUpdate, $shelf)) @icon('add') {{ trans('entities.books_create') }} @endif - @if(userCan('bookshelf-update', $shelf)) + @if(userCan(\BookStack\Permissions\Permission::BookshelfUpdate, $shelf)) @icon('edit') {{ trans('entities.shelves_edit_and_assign') }} @@ -82,7 +82,7 @@ @include('entities.meta', ['entity' => $shelf, 'watchOptions' => null]) @if($shelf->hasPermissions())
    - @if(userCan('restrictions-manage', $shelf)) + @if(userCan(\BookStack\Permissions\Permission::RestrictionsManage, $shelf)) @icon('lock')
    {{ trans('entities.shelves_permissions_active') }}
    @@ -111,7 +111,7 @@
    {{ trans('common.actions') }}
    - @if(!userCan('users-manage')) + @if(!userCan(\BookStack\Permissions\Permission::UsersManage))

    {{ trans('preferences.profile_email_no_permission') }}

    @endif
    @@ -75,7 +75,7 @@ class="setting-list-label">{{ trans('settings.users_avatar') }} - @if(userCan('users-manage')) + @if(userCan(\BookStack\Permissions\Permission::UsersManage))
    diff --git a/resources/views/users/api-tokens/edit.blade.php b/resources/views/users/api-tokens/edit.blade.php index aa3e49dedf8..3a1ff49d3c9 100644 --- a/resources/views/users/api-tokens/edit.blade.php +++ b/resources/views/users/api-tokens/edit.blade.php @@ -42,12 +42,12 @@
    - - {{ trans('settings.user_api_token_created', ['timeAgo' => $token->created_at->diffForHumans()]) }} + + {{ trans('settings.user_api_token_created', ['timeAgo' => $dates->relative($token->created_at)]) }}
    - - {{ trans('settings.user_api_token_updated', ['timeAgo' => $token->created_at->diffForHumans()]) }} + + {{ trans('settings.user_api_token_updated', ['timeAgo' => $dates->relative($token->created_at)]) }}
    diff --git a/resources/views/users/api-tokens/parts/list.blade.php b/resources/views/users/api-tokens/parts/list.blade.php index 70aaa58f3f6..bd8dd53cc35 100644 --- a/resources/views/users/api-tokens/parts/list.blade.php +++ b/resources/views/users/api-tokens/parts/list.blade.php @@ -2,7 +2,7 @@

    {{ trans('settings.users_api_tokens') }}

    - @if(userCan('access-api')) + @if(userCan(\BookStack\Permissions\Permission::AccessApi)) {{ trans('settings.users_api_tokens_docs') }} {{ trans('settings.users_api_tokens_create') }} @endif diff --git a/resources/views/users/create.blade.php b/resources/views/users/create.blade.php index dafc623e120..daffc91a39d 100644 --- a/resources/views/users/create.blade.php +++ b/resources/views/users/create.blade.php @@ -18,7 +18,7 @@
    diff --git a/resources/views/users/edit.blade.php b/resources/views/users/edit.blade.php index 2b736d81ede..611653d6a80 100644 --- a/resources/views/users/edit.blade.php +++ b/resources/views/users/edit.blade.php @@ -34,7 +34,9 @@ class="setting-list-label">{{ trans('settings.users_avatar') }}
    - @include('users.parts.language-option-row', ['value' => old('language') ?? $user->getLocale()->appLocale()]) + @if(!$user->isGuest()) + @include('users.parts.language-option-row', ['value' => old('language') ?? $user->getLocale()->appLocale()]) + @endif
    diff --git a/resources/views/users/index.blade.php b/resources/views/users/index.blade.php index 0dd607f8c7d..5d546076f98 100644 --- a/resources/views/users/index.blade.php +++ b/resources/views/users/index.blade.php @@ -22,6 +22,7 @@
    diff --git a/resources/views/users/parts/form.blade.php b/resources/views/users/parts/form.blade.php index bf1eb08a760..86287646f05 100644 --- a/resources/views/users/parts/form.blade.php +++ b/resources/views/users/parts/form.blade.php @@ -17,9 +17,9 @@ @include('form.text', ['name' => 'name'])
    - @if($authMethod !== 'ldap' || userCan('users-manage')) + @if($authMethod !== 'ldap' || userCan(\BookStack\Permissions\Permission::UsersManage)) - @include('form.text', ['name' => 'email', 'disabled' => !userCan('users-manage')]) + @include('form.text', ['name' => 'email', 'disabled' => !userCan(\BookStack\Permissions\Permission::UsersManage)]) @endif
    diff --git a/resources/views/users/parts/users-list-item.blade.php b/resources/views/users/parts/users-list-item.blade.php index dc7c9f272b8..9c7ecd14761 100644 --- a/resources/views/users/parts/users-list-item.blade.php +++ b/resources/views/users/parts/users-list-item.blade.php @@ -20,7 +20,7 @@ @if($user->last_activity_at) {{ trans('settings.users_latest_activity') }}
    - {{ $user->last_activity_at->diffForHumans() }} + {{ $dates->relative($user->last_activity_at) }} @endif
    diff --git a/resources/views/users/profile.blade.php b/resources/views/users/profile.blade.php index a8be8a4c198..9879091a5b9 100644 --- a/resources/views/users/profile.blade.php +++ b/resources/views/users/profile.blade.php @@ -23,7 +23,7 @@

    {{ $user->name }}

    - {{ trans('entities.profile_user_for_x', ['time' => $user->created_at->diffForHumans(null, true)]) }} + {{ trans('entities.profile_user_for_x', ['time' => $dates->relative($user->created_at, false)]) }}

    diff --git a/routes/api.php b/routes/api.php index 04c94a9664d..99df24aed0a 100644 --- a/routes/api.php +++ b/routes/api.php @@ -6,8 +6,11 @@ * Controllers all end with "ApiController" */ +use BookStack\Activity\Controllers\AuditLogApiController; use BookStack\Api\ApiDocsController; +use BookStack\App\SystemApiController; use BookStack\Entities\Controllers as EntityControllers; +use BookStack\Exports\Controllers as ExportControllers; use BookStack\Permissions\ContentPermissionApiController; use BookStack\Search\SearchApiController; use BookStack\Uploads\Controllers\AttachmentApiController; @@ -30,21 +33,22 @@ Route::put('books/{id}', [EntityControllers\BookApiController::class, 'update']); Route::delete('books/{id}', [EntityControllers\BookApiController::class, 'delete']); -Route::get('books/{id}/export/html', [EntityControllers\BookExportApiController::class, 'exportHtml']); -Route::get('books/{id}/export/pdf', [EntityControllers\BookExportApiController::class, 'exportPdf']); -Route::get('books/{id}/export/plaintext', [EntityControllers\BookExportApiController::class, 'exportPlainText']); -Route::get('books/{id}/export/markdown', [EntityControllers\BookExportApiController::class, 'exportMarkdown']); +Route::get('books/{id}/export/html', [ExportControllers\BookExportApiController::class, 'exportHtml']); +Route::get('books/{id}/export/pdf', [ExportControllers\BookExportApiController::class, 'exportPdf']); +Route::get('books/{id}/export/plaintext', [ExportControllers\BookExportApiController::class, 'exportPlainText']); +Route::get('books/{id}/export/markdown', [ExportControllers\BookExportApiController::class, 'exportMarkdown']); +Route::get('books/{id}/export/zip', [ExportControllers\BookExportApiController::class, 'exportZip']); Route::get('chapters', [EntityControllers\ChapterApiController::class, 'list']); Route::post('chapters', [EntityControllers\ChapterApiController::class, 'create']); Route::get('chapters/{id}', [EntityControllers\ChapterApiController::class, 'read']); Route::put('chapters/{id}', [EntityControllers\ChapterApiController::class, 'update']); Route::delete('chapters/{id}', [EntityControllers\ChapterApiController::class, 'delete']); - -Route::get('chapters/{id}/export/html', [EntityControllers\ChapterExportApiController::class, 'exportHtml']); -Route::get('chapters/{id}/export/pdf', [EntityControllers\ChapterExportApiController::class, 'exportPdf']); -Route::get('chapters/{id}/export/plaintext', [EntityControllers\ChapterExportApiController::class, 'exportPlainText']); -Route::get('chapters/{id}/export/markdown', [EntityControllers\ChapterExportApiController::class, 'exportMarkdown']); +Route::get('chapters/{id}/export/html', [ExportControllers\ChapterExportApiController::class, 'exportHtml']); +Route::get('chapters/{id}/export/pdf', [ExportControllers\ChapterExportApiController::class, 'exportPdf']); +Route::get('chapters/{id}/export/plaintext', [ExportControllers\ChapterExportApiController::class, 'exportPlainText']); +Route::get('chapters/{id}/export/markdown', [ExportControllers\ChapterExportApiController::class, 'exportMarkdown']); +Route::get('chapters/{id}/export/zip', [ExportControllers\ChapterExportApiController::class, 'exportZip']); Route::get('pages', [EntityControllers\PageApiController::class, 'list']); Route::post('pages', [EntityControllers\PageApiController::class, 'create']); @@ -52,10 +56,11 @@ Route::put('pages/{id}', [EntityControllers\PageApiController::class, 'update']); Route::delete('pages/{id}', [EntityControllers\PageApiController::class, 'delete']); -Route::get('pages/{id}/export/html', [EntityControllers\PageExportApiController::class, 'exportHtml']); -Route::get('pages/{id}/export/pdf', [EntityControllers\PageExportApiController::class, 'exportPdf']); -Route::get('pages/{id}/export/plaintext', [EntityControllers\PageExportApiController::class, 'exportPlainText']); -Route::get('pages/{id}/export/markdown', [EntityControllers\PageExportApiController::class, 'exportMarkdown']); +Route::get('pages/{id}/export/html', [ExportControllers\PageExportApiController::class, 'exportHtml']); +Route::get('pages/{id}/export/pdf', [ExportControllers\PageExportApiController::class, 'exportPdf']); +Route::get('pages/{id}/export/plaintext', [ExportControllers\PageExportApiController::class, 'exportPlainText']); +Route::get('pages/{id}/export/markdown', [ExportControllers\PageExportApiController::class, 'exportMarkdown']); +Route::get('pages/{id}/export/zip', [ExportControllers\PageExportApiController::class, 'exportZip']); Route::get('image-gallery', [ImageGalleryApiController::class, 'list']); Route::post('image-gallery', [ImageGalleryApiController::class, 'create']); @@ -83,9 +88,19 @@ Route::put('roles/{id}', [RoleApiController::class, 'update']); Route::delete('roles/{id}', [RoleApiController::class, 'delete']); +Route::get('imports', [ExportControllers\ImportApiController::class, 'list']); +Route::post('imports', [ExportControllers\ImportApiController::class, 'create']); +Route::get('imports/{id}', [ExportControllers\ImportApiController::class, 'read']); +Route::post('imports/{id}', [ExportControllers\ImportApiController::class, 'run']); +Route::delete('imports/{id}', [ExportControllers\ImportApiController::class, 'delete']); + Route::get('recycle-bin', [EntityControllers\RecycleBinApiController::class, 'list']); Route::put('recycle-bin/{deletionId}', [EntityControllers\RecycleBinApiController::class, 'restore']); Route::delete('recycle-bin/{deletionId}', [EntityControllers\RecycleBinApiController::class, 'destroy']); Route::get('content-permissions/{contentType}/{contentId}', [ContentPermissionApiController::class, 'read']); Route::put('content-permissions/{contentType}/{contentId}', [ContentPermissionApiController::class, 'update']); + +Route::get('audit-log', [AuditLogApiController::class, 'list']); + +Route::get('system', [SystemApiController::class, 'read']); diff --git a/routes/web.php b/routes/web.php index 4620cd08bc3..ea3efe1ac77 100644 --- a/routes/web.php +++ b/routes/web.php @@ -5,22 +5,29 @@ use BookStack\Api\ApiDocsController; use BookStack\Api\UserApiTokenController; use BookStack\App\HomeController; +use BookStack\App\MetaController; use BookStack\Entities\Controllers as EntityControllers; +use BookStack\Exports\Controllers as ExportControllers; use BookStack\Http\Middleware\VerifyCsrfToken; use BookStack\Permissions\PermissionsController; use BookStack\References\ReferenceController; use BookStack\Search\SearchController; use BookStack\Settings as SettingControllers; +use BookStack\Sorting as SortingControllers; +use BookStack\Theming\ThemeController; use BookStack\Uploads\Controllers as UploadControllers; use BookStack\Users\Controllers as UserControllers; use Illuminate\Session\Middleware\StartSession; use Illuminate\Support\Facades\Route; use Illuminate\View\Middleware\ShareErrorsFromSession; +// Status & Meta routes Route::get('/status', [SettingControllers\StatusController::class, 'show']); -Route::get('/robots.txt', [HomeController::class, 'robots']); -Route::get('/favicon.ico', [HomeController::class, 'favicon']); -Route::get('/manifest.json', [HomeController::class, 'pwaManifest']); +Route::get('/robots.txt', [MetaController::class, 'robots']); +Route::get('/favicon.ico', [MetaController::class, 'favicon']); +Route::get('/manifest.json', [MetaController::class, 'pwaManifest']); +Route::get('/licenses', [MetaController::class, 'licenses']); +Route::get('/opensearch.xml', [MetaController::class, 'opensearch']); // Authenticated routes... Route::middleware('auth')->group(function () { @@ -60,7 +67,7 @@ Route::get('/books/{slug}/edit', [EntityControllers\BookController::class, 'edit']); Route::put('/books/{slug}', [EntityControllers\BookController::class, 'update']); Route::delete('/books/{id}', [EntityControllers\BookController::class, 'destroy']); - Route::get('/books/{slug}/sort-item', [EntityControllers\BookSortController::class, 'showItem']); + Route::get('/books/{slug}/sort-item', [SortingControllers\BookSortController::class, 'showItem']); Route::get('/books/{slug}', [EntityControllers\BookController::class, 'show']); Route::get('/books/{bookSlug}/permissions', [PermissionsController::class, 'showForBook']); Route::put('/books/{bookSlug}/permissions', [PermissionsController::class, 'updateForBook']); @@ -68,14 +75,14 @@ Route::get('/books/{bookSlug}/copy', [EntityControllers\BookController::class, 'showCopy']); Route::post('/books/{bookSlug}/copy', [EntityControllers\BookController::class, 'copy']); Route::post('/books/{bookSlug}/convert-to-shelf', [EntityControllers\BookController::class, 'convertToShelf']); - Route::get('/books/{bookSlug}/sort', [EntityControllers\BookSortController::class, 'show']); - Route::put('/books/{bookSlug}/sort', [EntityControllers\BookSortController::class, 'update']); + Route::get('/books/{bookSlug}/sort', [SortingControllers\BookSortController::class, 'show']); + Route::put('/books/{bookSlug}/sort', [SortingControllers\BookSortController::class, 'update']); Route::get('/books/{slug}/references', [ReferenceController::class, 'book']); - Route::get('/books/{bookSlug}/export/html', [EntityControllers\BookExportController::class, 'html']); - Route::get('/books/{bookSlug}/export/pdf', [EntityControllers\BookExportController::class, 'pdf']); - Route::get('/books/{bookSlug}/export/markdown', [EntityControllers\BookExportController::class, 'markdown']); - Route::get('/books/{bookSlug}/export/zip', [EntityControllers\BookExportController::class, 'zip']); - Route::get('/books/{bookSlug}/export/plaintext', [EntityControllers\BookExportController::class, 'plainText']); + Route::get('/books/{bookSlug}/export/html', [ExportControllers\BookExportController::class, 'html']); + Route::get('/books/{bookSlug}/export/pdf', [ExportControllers\BookExportController::class, 'pdf']); + Route::get('/books/{bookSlug}/export/markdown', [ExportControllers\BookExportController::class, 'markdown']); + Route::get('/books/{bookSlug}/export/zip', [ExportControllers\BookExportController::class, 'zip']); + Route::get('/books/{bookSlug}/export/plaintext', [ExportControllers\BookExportController::class, 'plainText']); // Pages Route::get('/books/{bookSlug}/create-page', [EntityControllers\PageController::class, 'create']); @@ -83,10 +90,11 @@ Route::get('/books/{bookSlug}/draft/{pageId}', [EntityControllers\PageController::class, 'editDraft']); Route::post('/books/{bookSlug}/draft/{pageId}', [EntityControllers\PageController::class, 'store']); Route::get('/books/{bookSlug}/page/{pageSlug}', [EntityControllers\PageController::class, 'show']); - Route::get('/books/{bookSlug}/page/{pageSlug}/export/pdf', [EntityControllers\PageExportController::class, 'pdf']); - Route::get('/books/{bookSlug}/page/{pageSlug}/export/html', [EntityControllers\PageExportController::class, 'html']); - Route::get('/books/{bookSlug}/page/{pageSlug}/export/markdown', [EntityControllers\PageExportController::class, 'markdown']); - Route::get('/books/{bookSlug}/page/{pageSlug}/export/plaintext', [EntityControllers\PageExportController::class, 'plainText']); + Route::get('/books/{bookSlug}/page/{pageSlug}/export/pdf', [ExportControllers\PageExportController::class, 'pdf']); + Route::get('/books/{bookSlug}/page/{pageSlug}/export/html', [ExportControllers\PageExportController::class, 'html']); + Route::get('/books/{bookSlug}/page/{pageSlug}/export/markdown', [ExportControllers\PageExportController::class, 'markdown']); + Route::get('/books/{bookSlug}/page/{pageSlug}/export/plaintext', [ExportControllers\PageExportController::class, 'plainText']); + Route::get('/books/{bookSlug}/page/{pageSlug}/export/zip', [ExportControllers\PageExportController::class, 'zip']); Route::get('/books/{bookSlug}/page/{pageSlug}/edit', [EntityControllers\PageController::class, 'edit']); Route::get('/books/{bookSlug}/page/{pageSlug}/move', [EntityControllers\PageController::class, 'showMove']); Route::put('/books/{bookSlug}/page/{pageSlug}/move', [EntityControllers\PageController::class, 'move']); @@ -123,10 +131,11 @@ Route::get('/books/{bookSlug}/chapter/{chapterSlug}/edit', [EntityControllers\ChapterController::class, 'edit']); Route::post('/books/{bookSlug}/chapter/{chapterSlug}/convert-to-book', [EntityControllers\ChapterController::class, 'convertToBook']); Route::get('/books/{bookSlug}/chapter/{chapterSlug}/permissions', [PermissionsController::class, 'showForChapter']); - Route::get('/books/{bookSlug}/chapter/{chapterSlug}/export/pdf', [EntityControllers\ChapterExportController::class, 'pdf']); - Route::get('/books/{bookSlug}/chapter/{chapterSlug}/export/html', [EntityControllers\ChapterExportController::class, 'html']); - Route::get('/books/{bookSlug}/chapter/{chapterSlug}/export/markdown', [EntityControllers\ChapterExportController::class, 'markdown']); - Route::get('/books/{bookSlug}/chapter/{chapterSlug}/export/plaintext', [EntityControllers\ChapterExportController::class, 'plainText']); + Route::get('/books/{bookSlug}/chapter/{chapterSlug}/export/pdf', [ExportControllers\ChapterExportController::class, 'pdf']); + Route::get('/books/{bookSlug}/chapter/{chapterSlug}/export/html', [ExportControllers\ChapterExportController::class, 'html']); + Route::get('/books/{bookSlug}/chapter/{chapterSlug}/export/markdown', [ExportControllers\ChapterExportController::class, 'markdown']); + Route::get('/books/{bookSlug}/chapter/{chapterSlug}/export/plaintext', [ExportControllers\ChapterExportController::class, 'plainText']); + Route::get('/books/{bookSlug}/chapter/{chapterSlug}/export/zip', [ExportControllers\ChapterExportController::class, 'zip']); Route::put('/books/{bookSlug}/chapter/{chapterSlug}/permissions', [PermissionsController::class, 'updateForChapter']); Route::get('/books/{bookSlug}/chapter/{chapterSlug}/references', [ReferenceController::class, 'chapter']); Route::get('/books/{bookSlug}/chapter/{chapterSlug}/delete', [EntityControllers\ChapterController::class, 'showDelete']); @@ -170,6 +179,8 @@ // Comments Route::post('/comment/{pageId}', [ActivityControllers\CommentController::class, 'savePageComment']); + Route::put('/comment/{id}/archive', [ActivityControllers\CommentController::class, 'archive']); + Route::put('/comment/{id}/unarchive', [ActivityControllers\CommentController::class, 'unarchive']); Route::put('/comment/{id}', [ActivityControllers\CommentController::class, 'update']); Route::delete('/comment/{id}', [ActivityControllers\CommentController::class, 'destroy']); @@ -200,6 +211,13 @@ // Watching Route::put('/watching/update', [ActivityControllers\WatchController::class, 'update']); + // Importing + Route::get('/import', [ExportControllers\ImportController::class, 'start']); + Route::post('/import', [ExportControllers\ImportController::class, 'upload']); + Route::get('/import/{id}', [ExportControllers\ImportController::class, 'show']); + Route::post('/import/{id}', [ExportControllers\ImportController::class, 'run']); + Route::delete('/import/{id}', [ExportControllers\ImportController::class, 'delete']); + // Other Pages Route::get('/', [HomeController::class, 'index']); Route::get('/home', [HomeController::class, 'index']); @@ -279,6 +297,13 @@ Route::get('/settings/webhooks/{id}/delete', [ActivityControllers\WebhookController::class, 'delete']); Route::delete('/settings/webhooks/{id}', [ActivityControllers\WebhookController::class, 'destroy']); + // Sort Rules + Route::get('/settings/sorting/rules/new', [SortingControllers\SortRuleController::class, 'create']); + Route::post('/settings/sorting/rules', [SortingControllers\SortRuleController::class, 'store']); + Route::get('/settings/sorting/rules/{id}', [SortingControllers\SortRuleController::class, 'edit']); + Route::put('/settings/sorting/rules/{id}', [SortingControllers\SortRuleController::class, 'update']); + Route::delete('/settings/sorting/rules/{id}', [SortingControllers\SortRuleController::class, 'destroy']); + // Settings Route::get('/settings', [SettingControllers\SettingController::class, 'index'])->name('settings'); Route::get('/settings/{category}', [SettingControllers\SettingController::class, 'category'])->name('settings.category'); @@ -315,8 +340,8 @@ Route::get('/register/confirm/awaiting', [AccessControllers\ConfirmEmailController::class, 'showAwaiting']); Route::post('/register/confirm/resend', [AccessControllers\ConfirmEmailController::class, 'resend']); Route::get('/register/confirm/{token}', [AccessControllers\ConfirmEmailController::class, 'showAcceptForm']); -Route::post('/register/confirm/accept', [AccessControllers\ConfirmEmailController::class, 'confirm']); -Route::post('/register', [AccessControllers\RegisterController::class, 'postRegister']); +Route::post('/register/confirm/accept', [AccessControllers\ConfirmEmailController::class, 'confirm'])->middleware('throttle:public'); +Route::post('/register', [AccessControllers\RegisterController::class, 'postRegister'])->middleware('throttle:public'); // SAML routes Route::post('/saml2/login', [AccessControllers\Saml2Controller::class, 'login']); @@ -336,18 +361,23 @@ Route::post('/oidc/logout', [AccessControllers\OidcController::class, 'logout']); // User invitation routes -Route::get('/register/invite/{token}', [AccessControllers\UserInviteController::class, 'showSetPassword']); -Route::post('/register/invite/{token}', [AccessControllers\UserInviteController::class, 'setPassword']); +Route::get('/register/invite/{token}', [AccessControllers\UserInviteController::class, 'showSetPassword'])->middleware('throttle:public'); +Route::post('/register/invite/{token}', [AccessControllers\UserInviteController::class, 'setPassword'])->middleware('throttle:public'); // Password reset link request routes Route::get('/password/email', [AccessControllers\ForgotPasswordController::class, 'showLinkRequestForm']); -Route::post('/password/email', [AccessControllers\ForgotPasswordController::class, 'sendResetLinkEmail']); +Route::post('/password/email', [AccessControllers\ForgotPasswordController::class, 'sendResetLinkEmail'])->middleware('throttle:public'); // Password reset routes Route::get('/password/reset/{token}', [AccessControllers\ResetPasswordController::class, 'showResetForm']); -Route::post('/password/reset', [AccessControllers\ResetPasswordController::class, 'reset']); +Route::post('/password/reset', [AccessControllers\ResetPasswordController::class, 'reset'])->middleware('throttle:public'); -// Metadata routes +// Help & Info routes +Route::view('/help/tinymce', 'help.tinymce'); Route::view('/help/wysiwyg', 'help.wysiwyg'); -Route::fallback([HomeController::class, 'notFound'])->name('fallback'); +// Theme Routes +Route::get('/theme/{theme}/{path}', [ThemeController::class, 'publicFile']) + ->where('path', '.*$'); + +Route::fallback([MetaController::class, 'notFound'])->name('fallback'); diff --git a/tests/Activity/AuditLogApiTest.php b/tests/Activity/AuditLogApiTest.php new file mode 100644 index 00000000000..2212ae5991a --- /dev/null +++ b/tests/Activity/AuditLogApiTest.php @@ -0,0 +1,60 @@ +users->editor(); + + $assertPermissionErrorOnCall = function () use ($editor) { + $resp = $this->actingAsForApi($editor)->getJson('/api/audit-log'); + $resp->assertStatus(403); + $resp->assertJson($this->permissionErrorResponse()); + }; + + $assertPermissionErrorOnCall(); + $this->permissions->grantUserRolePermissions($editor, ['users-manage']); + $assertPermissionErrorOnCall(); + $this->permissions->removeUserRolePermissions($editor, ['users-manage']); + $this->permissions->grantUserRolePermissions($editor, ['settings-manage']); + $assertPermissionErrorOnCall(); + + $this->permissions->grantUserRolePermissions($editor, ['settings-manage', 'users-manage']); + $resp = $this->actingAsForApi($editor)->getJson('/api/audit-log'); + $resp->assertOk(); + } + + public function test_index_endpoint_returns_expected_data() + { + $page = $this->entities->page(); + $admin = $this->users->admin(); + $this->actingAsForApi($admin); + Activity::add(ActivityType::PAGE_UPDATE, $page); + + $resp = $this->get("/api/audit-log?filter[loggable_id]={$page->id}"); + $resp->assertJson(['data' => [ + [ + 'type' => 'page_update', + 'detail' => "({$page->id}) {$page->name}", + 'user_id' => $admin->id, + 'loggable_id' => $page->id, + 'loggable_type' => 'page', + 'ip' => '127.0.0.1', + 'user' => [ + 'id' => $admin->id, + 'name' => $admin->name, + 'slug' => $admin->slug, + ], + ] + ]]); + } +} diff --git a/tests/Actions/AuditLogTest.php b/tests/Activity/AuditLogTest.php similarity index 98% rename from tests/Actions/AuditLogTest.php rename to tests/Activity/AuditLogTest.php index 5e355ca096b..6b435544df7 100644 --- a/tests/Actions/AuditLogTest.php +++ b/tests/Activity/AuditLogTest.php @@ -1,6 +1,6 @@ ActivityType::PAGE_UPDATE, 'ip' => '192.123.45.1', 'user_id' => $editor->id, - 'entity_id' => $page->id, + 'loggable_id' => $page->id, ]); $resp = $this->asAdmin()->get('/settings/audit'); @@ -207,7 +207,7 @@ public function test_ip_address_not_logged_in_demo_mode() 'type' => ActivityType::PAGE_UPDATE, 'ip' => '127.0.0.1', 'user_id' => $editor->id, - 'entity_id' => $page->id, + 'loggable_id' => $page->id, ]); } @@ -229,7 +229,7 @@ public function test_ip_address_respects_precision_setting() 'type' => ActivityType::PAGE_UPDATE, 'ip' => '192.123.x.x', 'user_id' => $editor->id, - 'entity_id' => $page->id, + 'loggable_id' => $page->id, ]); } } diff --git a/tests/Activity/WatchTest.php b/tests/Activity/WatchTest.php index 38935bbf5c9..c405b07aed7 100644 --- a/tests/Activity/WatchTest.php +++ b/tests/Activity/WatchTest.php @@ -13,6 +13,8 @@ use BookStack\Activity\WatchLevels; use BookStack\Entities\Models\Entity; use BookStack\Settings\UserNotificationPreferences; +use Illuminate\Contracts\Notifications\Dispatcher; +use Illuminate\Support\Facades\Mail; use Illuminate\Support\Facades\Notification; use Tests\TestCase; @@ -196,7 +198,7 @@ public function test_notify_own_page_comments() $notifications = Notification::fake(); $this->asAdmin()->post("/comment/{$entities['page']->id}", [ - 'text' => 'My new comment' + 'html' => '

    My new comment

    ' ]); $notifications->assertSentTo($editor, CommentCreationNotification::class); } @@ -217,12 +219,12 @@ public function test_notify_comment_replies() $notifications = Notification::fake(); $this->actingAs($editor)->post("/comment/{$entities['page']->id}", [ - 'text' => 'My new comment' + 'html' => '

    My new comment

    ' ]); $comment = $entities['page']->comments()->orderBy('id', 'desc')->first(); $this->asAdmin()->post("/comment/{$entities['page']->id}", [ - 'text' => 'My new comment response', + 'html' => '

    My new comment response

    ', 'parent_id' => $comment->local_id, ]); $notifications->assertSentTo($editor, CommentCreationNotification::class); @@ -257,7 +259,7 @@ public function test_notify_watch_parent_book_comments() // Comment post $this->actingAs($admin)->post("/comment/{$entities['page']->id}", [ - 'text' => 'My new comment response', + 'html' => '

    My new comment response

    ', ]); $notifications->assertSentTo($editor, function (CommentCreationNotification $notification) use ($editor, $admin, $entities) { @@ -365,6 +367,29 @@ public function test_notifications_sent_in_right_language() } } + public function test_failed_notifications_dont_block_and_log_errors() + { + $logger = $this->withTestLogger(); + $editor = $this->users->editor(); + $admin = $this->users->admin(); + $page = $this->entities->page(); + $book = $page->book; + $activityLogger = app()->make(ActivityLogger::class); + + $watches = new UserEntityWatchOptions($editor, $book); + $watches->updateLevelByValue(WatchLevels::UPDATES); + + $mockDispatcher = $this->mock(Dispatcher::class); + $mockDispatcher->shouldReceive('send')->once() + ->andThrow(\Exception::class, 'Failed to connect to mail server'); + + $this->actingAs($admin); + + $activityLogger->add(ActivityType::PAGE_UPDATE, $page); + + $this->assertTrue($logger->hasErrorThatContains("Failed to send email notification to user [id:{$editor->id}] with error: Failed to connect to mail server")); + } + public function test_notifications_not_sent_if_lacking_view_permission_for_related_item() { $notifications = Notification::fake(); @@ -376,7 +401,7 @@ public function test_notifications_not_sent_if_lacking_view_permission_for_relat $this->permissions->disableEntityInheritedPermissions($page); $this->asAdmin()->post("/comment/{$page->id}", [ - 'text' => 'My new comment response', + 'html' => '

    My new comment response

    ', ])->assertOk(); $notifications->assertNothingSentTo($editor); diff --git a/tests/Actions/WebhookCallTest.php b/tests/Activity/WebhookCallTest.php similarity index 99% rename from tests/Actions/WebhookCallTest.php rename to tests/Activity/WebhookCallTest.php index 16986ba2e97..162e05ec505 100644 --- a/tests/Actions/WebhookCallTest.php +++ b/tests/Activity/WebhookCallTest.php @@ -1,6 +1,6 @@ actingAsApiAdmin(); + $page = $this->entities->page(); + $attachment = $this->createAttachmentForPage($page); + + $resp = $this->putJson("{$this->baseEndpoint}/{$attachment->id}", [ + 'link' => 'https://example.com/donkey', + ]); + + $resp->assertStatus(200); + $this->assertDatabaseHas('attachments', [ + 'id' => $attachment->id, + 'path' => 'https://example.com/donkey', + ]); + } + + public function test_update_does_not_require_name() { $this->actingAsApiAdmin(); $page = $this->entities->page(); diff --git a/tests/Api/BooksApiTest.php b/tests/Api/BooksApiTest.php index b31bd7d37ee..22ccfb482c9 100644 --- a/tests/Api/BooksApiTest.php +++ b/tests/Api/BooksApiTest.php @@ -3,6 +3,7 @@ namespace Tests\Api; use BookStack\Entities\Models\Book; +use BookStack\Entities\Repos\BaseRepo; use Carbon\Carbon; use Illuminate\Support\Facades\DB; use Tests\TestCase; @@ -24,6 +25,31 @@ public function test_index_endpoint_returns_expected_book() 'id' => $firstBook->id, 'name' => $firstBook->name, 'slug' => $firstBook->slug, + 'owned_by' => $firstBook->owned_by, + 'created_by' => $firstBook->created_by, + 'updated_by' => $firstBook->updated_by, + 'cover' => null, + ], + ]]); + } + + public function test_index_endpoint_includes_cover_if_set() + { + $this->actingAsApiEditor(); + $book = $this->entities->book(); + + $baseRepo = $this->app->make(BaseRepo::class); + $image = $this->files->uploadedImage('book_cover'); + $baseRepo->updateCoverImage($book, $image); + + $resp = $this->getJson($this->baseEndpoint . '?filter[id]=' . $book->id); + $resp->assertJson(['data' => [ + [ + 'id' => $book->id, + 'cover' => [ + 'id' => $book->cover->id, + 'url' => $book->cover->url, + ], ], ]]); } @@ -146,6 +172,23 @@ public function test_read_endpoint_includes_chapter_and_page_contents() ]); } + public function test_read_endpoint_contents_nested_pages_has_permissions_applied() + { + $this->actingAsApiEditor(); + + $book = $this->entities->bookHasChaptersAndPages(); + $chapter = $book->chapters()->first(); + $chapterPage = $chapter->pages()->first(); + $customName = 'MyNonVisiblePageWithinAChapter'; + $chapterPage->name = $customName; + $chapterPage->save(); + + $this->permissions->disableEntityInheritedPermissions($chapterPage); + + $resp = $this->getJson($this->baseEndpoint . "/{$book->id}"); + $resp->assertJsonMissing(['name' => $customName]); + } + public function test_update_endpoint() { $this->actingAsApiEditor(); @@ -244,62 +287,4 @@ public function test_delete_endpoint() $resp->assertStatus(204); $this->assertActivityExists('book_delete'); } - - public function test_export_html_endpoint() - { - $this->actingAsApiEditor(); - $book = $this->entities->book(); - - $resp = $this->get($this->baseEndpoint . "/{$book->id}/export/html"); - $resp->assertStatus(200); - $resp->assertSee($book->name); - $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $book->slug . '.html"'); - } - - public function test_export_plain_text_endpoint() - { - $this->actingAsApiEditor(); - $book = $this->entities->book(); - - $resp = $this->get($this->baseEndpoint . "/{$book->id}/export/plaintext"); - $resp->assertStatus(200); - $resp->assertSee($book->name); - $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $book->slug . '.txt"'); - } - - public function test_export_pdf_endpoint() - { - $this->actingAsApiEditor(); - $book = $this->entities->book(); - - $resp = $this->get($this->baseEndpoint . "/{$book->id}/export/pdf"); - $resp->assertStatus(200); - $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $book->slug . '.pdf"'); - } - - public function test_export_markdown_endpoint() - { - $this->actingAsApiEditor(); - $book = Book::visible()->has('pages')->has('chapters')->first(); - - $resp = $this->get($this->baseEndpoint . "/{$book->id}/export/markdown"); - $resp->assertStatus(200); - $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $book->slug . '.md"'); - $resp->assertSee('# ' . $book->name); - $resp->assertSee('# ' . $book->pages()->first()->name); - $resp->assertSee('# ' . $book->chapters()->first()->name); - } - - public function test_cant_export_when_not_have_permission() - { - $types = ['html', 'plaintext', 'pdf', 'markdown']; - $this->actingAsApiEditor(); - $this->permissions->removeUserRolePermissions($this->users->editor(), ['content-export']); - - $book = $this->entities->book(); - foreach ($types as $type) { - $resp = $this->get($this->baseEndpoint . "/{$book->id}/export/{$type}"); - $this->assertPermissionError($resp); - } - } } diff --git a/tests/Api/ChaptersApiTest.php b/tests/Api/ChaptersApiTest.php index 002046c3a3b..5d7b0530891 100644 --- a/tests/Api/ChaptersApiTest.php +++ b/tests/Api/ChaptersApiTest.php @@ -28,6 +28,9 @@ public function test_index_endpoint_returns_expected_chapter() 'book_id' => $firstChapter->book->id, 'priority' => $firstChapter->priority, 'book_slug' => $firstChapter->book->slug, + 'owned_by' => $firstChapter->owned_by, + 'created_by' => $firstChapter->created_by, + 'updated_by' => $firstChapter->updated_by, ], ]]); } @@ -36,6 +39,7 @@ public function test_create_endpoint() { $this->actingAsApiEditor(); $book = $this->entities->book(); + $templatePage = $this->entities->templatePage(); $details = [ 'name' => 'My API chapter', 'description' => 'A chapter created via the API', @@ -47,6 +51,7 @@ public function test_create_endpoint() ], ], 'priority' => 15, + 'default_template_id' => $templatePage->id, ]; $resp = $this->postJson($this->baseEndpoint, $details); @@ -147,8 +152,19 @@ public function test_read_endpoint() 'id' => $page->id, 'slug' => $page->slug, 'name' => $page->name, + 'owned_by' => $page->owned_by, + 'created_by' => $page->created_by, + 'updated_by' => $page->updated_by, + 'book_id' => $page->id, + 'chapter_id' => $chapter->id, + 'priority' => $page->priority, + 'book_slug' => $chapter->book->slug, + 'draft' => $page->draft, + 'template' => $page->template, + 'editor' => $page->editor, ], ], + 'default_template_id' => null, ]); $resp->assertJsonMissingPath('book'); $resp->assertJsonCount($chapter->pages()->count(), 'pages'); @@ -158,6 +174,7 @@ public function test_update_endpoint() { $this->actingAsApiEditor(); $chapter = $this->entities->chapter(); + $templatePage = $this->entities->templatePage(); $details = [ 'name' => 'My updated API chapter', 'description' => 'A chapter updated via the API', @@ -168,6 +185,7 @@ public function test_update_endpoint() ], ], 'priority' => 15, + 'default_template_id' => $templatePage->id, ]; $resp = $this->putJson($this->baseEndpoint . "/{$chapter->id}", $details); @@ -251,61 +269,4 @@ public function test_delete_endpoint() $resp->assertStatus(204); $this->assertActivityExists('chapter_delete'); } - - public function test_export_html_endpoint() - { - $this->actingAsApiEditor(); - $chapter = $this->entities->chapter(); - - $resp = $this->get($this->baseEndpoint . "/{$chapter->id}/export/html"); - $resp->assertStatus(200); - $resp->assertSee($chapter->name); - $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $chapter->slug . '.html"'); - } - - public function test_export_plain_text_endpoint() - { - $this->actingAsApiEditor(); - $chapter = $this->entities->chapter(); - - $resp = $this->get($this->baseEndpoint . "/{$chapter->id}/export/plaintext"); - $resp->assertStatus(200); - $resp->assertSee($chapter->name); - $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $chapter->slug . '.txt"'); - } - - public function test_export_pdf_endpoint() - { - $this->actingAsApiEditor(); - $chapter = $this->entities->chapter(); - - $resp = $this->get($this->baseEndpoint . "/{$chapter->id}/export/pdf"); - $resp->assertStatus(200); - $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $chapter->slug . '.pdf"'); - } - - public function test_export_markdown_endpoint() - { - $this->actingAsApiEditor(); - $chapter = Chapter::visible()->has('pages')->first(); - - $resp = $this->get($this->baseEndpoint . "/{$chapter->id}/export/markdown"); - $resp->assertStatus(200); - $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $chapter->slug . '.md"'); - $resp->assertSee('# ' . $chapter->name); - $resp->assertSee('# ' . $chapter->pages()->first()->name); - } - - public function test_cant_export_when_not_have_permission() - { - $types = ['html', 'plaintext', 'pdf', 'markdown']; - $this->actingAsApiEditor(); - $this->permissions->removeUserRolePermissions($this->users->editor(), ['content-export']); - - $chapter = Chapter::visible()->has('pages')->first(); - foreach ($types as $type) { - $resp = $this->get($this->baseEndpoint . "/{$chapter->id}/export/{$type}"); - $this->assertPermissionError($resp); - } - } } diff --git a/tests/Api/ExportsApiTest.php b/tests/Api/ExportsApiTest.php new file mode 100644 index 00000000000..e1ac698d0a4 --- /dev/null +++ b/tests/Api/ExportsApiTest.php @@ -0,0 +1,209 @@ +actingAsApiEditor(); + $book = $this->entities->book(); + + $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"'); + } + + public function test_book_plain_text_endpoint() + { + $this->actingAsApiEditor(); + $book = $this->entities->book(); + + $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"'); + } + + public function test_book_pdf_endpoint() + { + $this->actingAsApiEditor(); + $book = $this->entities->book(); + + $resp = $this->get("/api/books/{$book->id}/export/pdf"); + $resp->assertStatus(200); + $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $book->slug . '.pdf"'); + } + + public function test_book_markdown_endpoint() + { + $this->actingAsApiEditor(); + $book = Book::visible()->has('pages')->has('chapters')->first(); + + $resp = $this->get("/api/books/{$book->id}/export/markdown"); + $resp->assertStatus(200); + $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $book->slug . '.md"'); + $resp->assertSee('# ' . $book->name); + $resp->assertSee('# ' . $book->pages()->first()->name); + $resp->assertSee('# ' . $book->chapters()->first()->name); + } + + public function test_book_zip_endpoint() + { + $this->actingAsApiEditor(); + $book = Book::visible()->has('pages')->has('chapters')->first(); + + $resp = $this->get("/api/books/{$book->id}/export/zip"); + $resp->assertStatus(200); + $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $book->slug . '.zip"'); + + $zip = ZipTestHelper::extractFromZipResponse($resp); + $this->assertArrayHasKey('book', $zip->data); + } + + public function test_chapter_html_endpoint() + { + $this->actingAsApiEditor(); + $chapter = $this->entities->chapter(); + + $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"'); + } + + public function test_chapter_plain_text_endpoint() + { + $this->actingAsApiEditor(); + $chapter = $this->entities->chapter(); + + $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"'); + } + + public function test_chapter_pdf_endpoint() + { + $this->actingAsApiEditor(); + $chapter = $this->entities->chapter(); + + $resp = $this->get("/api/chapters/{$chapter->id}/export/pdf"); + $resp->assertStatus(200); + $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $chapter->slug . '.pdf"'); + } + + public function test_chapter_markdown_endpoint() + { + $this->actingAsApiEditor(); + $chapter = Chapter::visible()->has('pages')->first(); + + $resp = $this->get("/api/chapters/{$chapter->id}/export/markdown"); + $resp->assertStatus(200); + $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $chapter->slug . '.md"'); + $resp->assertSee('# ' . $chapter->name); + $resp->assertSee('# ' . $chapter->pages()->first()->name); + } + + public function test_chapter_zip_endpoint() + { + $this->actingAsApiEditor(); + $chapter = Chapter::visible()->has('pages')->first(); + + $resp = $this->get("/api/chapters/{$chapter->id}/export/zip"); + $resp->assertStatus(200); + $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $chapter->slug . '.zip"'); + + $zip = ZipTestHelper::extractFromZipResponse($resp); + $this->assertArrayHasKey('chapter', $zip->data); + } + + public function test_page_html_endpoint() + { + $this->actingAsApiEditor(); + $page = $this->entities->page(); + + $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"'); + } + + public function test_page_plain_text_endpoint() + { + $this->actingAsApiEditor(); + $page = $this->entities->page(); + + $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"'); + } + + public function test_page_pdf_endpoint() + { + $this->actingAsApiEditor(); + $page = $this->entities->page(); + + $resp = $this->get("/api/pages/{$page->id}/export/pdf"); + $resp->assertStatus(200); + $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $page->slug . '.pdf"'); + } + + public function test_page_markdown_endpoint() + { + $this->actingAsApiEditor(); + $page = $this->entities->page(); + + $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"'); + } + + public function test_page_zip_endpoint() + { + $this->actingAsApiEditor(); + $page = $this->entities->page(); + + $resp = $this->get("/api/pages/{$page->id}/export/zip"); + $resp->assertStatus(200); + $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $page->slug . '.zip"'); + + $zip = ZipTestHelper::extractFromZipResponse($resp); + $this->assertArrayHasKey('page', $zip->data); + } + + public function test_cant_export_when_not_have_permission() + { + $types = ['html', 'plaintext', 'pdf', 'markdown', 'zip']; + $this->actingAsApiEditor(); + $this->permissions->removeUserRolePermissions($this->users->editor(), ['content-export']); + + $book = $this->entities->book(); + foreach ($types as $type) { + $resp = $this->get("/api/books/{$book->id}/export/{$type}"); + $this->assertPermissionError($resp); + } + + $chapter = Chapter::visible()->has('pages')->first(); + foreach ($types as $type) { + $resp = $this->get("/api/chapters/{$chapter->id}/export/{$type}"); + $this->assertPermissionError($resp); + } + + $page = $this->entities->page(); + foreach ($types as $type) { + $resp = $this->get("/api/pages/{$page->id}/export/{$type}"); + $this->assertPermissionError($resp); + } + } +} diff --git a/tests/Api/ImportsApiTest.php b/tests/Api/ImportsApiTest.php new file mode 100644 index 00000000000..7026868c913 --- /dev/null +++ b/tests/Api/ImportsApiTest.php @@ -0,0 +1,175 @@ +entities->book(); + $zip = ZipTestHelper::zipUploadFromData([ + 'page' => [ + 'name' => 'My API import page', + 'tags' => [ + [ + 'name' => 'My api tag', + 'value' => 'api test value' + ] + ], + ], + ]); + + $resp = $this->actingAsApiAdmin()->call('POST', $this->baseEndpoint, [], [], ['file' => $zip]); + $resp->assertStatus(200); + + $importId = $resp->json('id'); + $import = Import::query()->findOrFail($importId); + $this->assertEquals('page', $import->type); + + $resp = $this->post($this->baseEndpoint . "/{$import->id}", [ + 'parent_type' => 'book', + 'parent_id' => $book->id, + ]); + $resp->assertJson([ + 'name' => 'My API import page', + 'book_id' => $book->id, + ]); + $resp->assertJsonMissingPath('book'); + + $page = Page::query()->where('name', '=', 'My API import page')->first(); + $this->assertEquals('My api tag', $page->tags()->first()->name); + } + + public function test_create_validation_error(): void + { + $zip = ZipTestHelper::zipUploadFromData([ + 'page' => [ + 'tags' => [ + [ + 'name' => 'My api tag', + 'value' => 'api test value' + ] + ], + ], + ]); + + $resp = $this->actingAsApiAdmin()->call('POST', $this->baseEndpoint, [], [], ['file' => $zip]); + $resp->assertStatus(422); + $message = $resp->json('message'); + + $this->assertStringContainsString('ZIP upload failed with the following validation errors:', $message); + $this->assertStringContainsString('[page.name] The name field is required.', $message); + } + + public function test_list(): void + { + $imports = Import::factory()->count(10)->create(); + + $resp = $this->actingAsApiAdmin()->get($this->baseEndpoint); + $resp->assertJsonCount(10, 'data'); + $resp->assertJsonPath('total', 10); + + $firstImport = $imports->first(); + $resp = $this->actingAsApiAdmin()->get($this->baseEndpoint . '?filter[id]=' . $firstImport->id); + $resp->assertJsonCount(1, 'data'); + $resp->assertJsonPath('data.0.id', $firstImport->id); + $resp->assertJsonPath('data.0.name', $firstImport->name); + $resp->assertJsonPath('data.0.size', $firstImport->size); + $resp->assertJsonPath('data.0.type', $firstImport->type); + } + + public function test_list_visibility_limited(): void + { + $user = $this->users->editor(); + $admin = $this->users->admin(); + $userImport = Import::factory()->create(['name' => 'MySuperUserImport', 'created_by' => $user->id]); + $adminImport = Import::factory()->create(['name' => 'MySuperAdminImport', 'created_by' => $admin->id]); + $this->permissions->grantUserRolePermissions($user, ['content-import']); + + $resp = $this->actingAsForApi($user)->get($this->baseEndpoint); + $resp->assertJsonCount(1, 'data'); + $resp->assertJsonPath('data.0.name', 'MySuperUserImport'); + + $this->permissions->grantUserRolePermissions($user, ['settings-manage']); + + $resp = $this->actingAsForApi($user)->get($this->baseEndpoint); + $resp->assertJsonCount(2, 'data'); + $resp->assertJsonPath('data.1.name', 'MySuperAdminImport'); + } + + public function test_read(): void + { + $zip = ZipTestHelper::zipUploadFromData([ + 'book' => [ + 'name' => 'My API import book', + 'pages' => [ + [ + 'name' => 'My import page', + 'tags' => [ + [ + 'name' => 'My api tag', + 'value' => 'api test value' + ] + ] + ] + ], + ], + ]); + + $resp = $this->actingAsApiAdmin()->call('POST', $this->baseEndpoint, [], [], ['file' => $zip]); + $resp->assertStatus(200); + + $resp = $this->get($this->baseEndpoint . "/{$resp->json('id')}"); + $resp->assertStatus(200); + + $resp->assertJsonPath('details.name', 'My API import book'); + $resp->assertJsonPath('details.pages.0.name', 'My import page'); + $resp->assertJsonPath('details.pages.0.tags.0.name', 'My api tag'); + $resp->assertJsonMissingPath('metadata'); + } + + public function test_delete(): void + { + $import = Import::factory()->create(); + + $resp = $this->actingAsApiAdmin()->delete($this->baseEndpoint . "/{$import->id}"); + $resp->assertStatus(204); + } + + public function test_content_import_permissions_needed(): void + { + $user = $this->users->viewer(); + $this->permissions->grantUserRolePermissions($user, ['access-api']); + $this->actingAsForApi($user); + $requests = [ + ['GET', $this->baseEndpoint], + ['POST', $this->baseEndpoint], + ['GET', $this->baseEndpoint . "/1"], + ['POST', $this->baseEndpoint . "/1"], + ['DELETE', $this->baseEndpoint . "/1"], + ]; + + foreach ($requests as $request) { + [$method, $endpoint] = $request; + $resp = $this->json($method, $endpoint); + $resp->assertStatus(403); + } + + $this->permissions->grantUserRolePermissions($user, ['content-import']); + + foreach ($requests as $request) { + [$method, $endpoint] = $request; + $resp = $this->call($method, $endpoint); + $this->assertNotEquals(403, $resp->status(), "A {$method} request to {$endpoint} returned 403"); + } + } +} diff --git a/tests/Api/PagesApiTest.php b/tests/Api/PagesApiTest.php index 0d084472d1a..ced8954eb11 100644 --- a/tests/Api/PagesApiTest.php +++ b/tests/Api/PagesApiTest.php @@ -27,6 +27,10 @@ public function test_index_endpoint_returns_expected_page() 'slug' => $firstPage->slug, 'book_id' => $firstPage->book->id, 'priority' => $firstPage->priority, + 'owned_by' => $firstPage->owned_by, + 'created_by' => $firstPage->created_by, + 'updated_by' => $firstPage->updated_by, + 'revision_count' => $firstPage->revision_count, ], ]]); } @@ -304,60 +308,4 @@ public function test_delete_endpoint() $resp->assertStatus(204); $this->assertActivityExists('page_delete', $page); } - - public function test_export_html_endpoint() - { - $this->actingAsApiEditor(); - $page = $this->entities->page(); - - $resp = $this->get($this->baseEndpoint . "/{$page->id}/export/html"); - $resp->assertStatus(200); - $resp->assertSee($page->name); - $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $page->slug . '.html"'); - } - - public function test_export_plain_text_endpoint() - { - $this->actingAsApiEditor(); - $page = $this->entities->page(); - - $resp = $this->get($this->baseEndpoint . "/{$page->id}/export/plaintext"); - $resp->assertStatus(200); - $resp->assertSee($page->name); - $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $page->slug . '.txt"'); - } - - public function test_export_pdf_endpoint() - { - $this->actingAsApiEditor(); - $page = $this->entities->page(); - - $resp = $this->get($this->baseEndpoint . "/{$page->id}/export/pdf"); - $resp->assertStatus(200); - $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $page->slug . '.pdf"'); - } - - public function test_export_markdown_endpoint() - { - $this->actingAsApiEditor(); - $page = $this->entities->page(); - - $resp = $this->get($this->baseEndpoint . "/{$page->id}/export/markdown"); - $resp->assertStatus(200); - $resp->assertSee('# ' . $page->name); - $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $page->slug . '.md"'); - } - - public function test_cant_export_when_not_have_permission() - { - $types = ['html', 'plaintext', 'pdf', 'markdown']; - $this->actingAsApiEditor(); - $this->permissions->removeUserRolePermissions($this->users->editor(), ['content-export']); - - $page = $this->entities->page(); - foreach ($types as $type) { - $resp = $this->get($this->baseEndpoint . "/{$page->id}/export/{$type}"); - $this->assertPermissionError($resp); - } - } } diff --git a/tests/Api/SearchApiTest.php b/tests/Api/SearchApiTest.php index 2a186e8d632..9da7900ca9a 100644 --- a/tests/Api/SearchApiTest.php +++ b/tests/Api/SearchApiTest.php @@ -13,7 +13,7 @@ class SearchApiTest extends TestCase { use TestsApi; - protected $baseEndpoint = '/api/search'; + protected string $baseEndpoint = '/api/search'; public function test_all_endpoint_returns_search_filtered_results_with_query() { @@ -45,7 +45,7 @@ public function test_all_endpoint_returns_entity_url() $resp = $this->actingAsApiAdmin()->getJson($this->baseEndpoint . '?query=superuniquevalue'); $resp->assertJsonFragment([ 'type' => 'page', - 'url' => $page->getUrl(), + 'url' => $page->getUrl(), ]); } @@ -57,10 +57,10 @@ public function test_all_endpoint_returns_items_with_preview_html() $resp = $this->actingAsApiAdmin()->getJson($this->baseEndpoint . '?query=superuniquevalue'); $resp->assertJsonFragment([ - 'type' => 'book', - 'url' => $book->getUrl(), + 'type' => 'book', + 'url' => $book->getUrl(), 'preview_html' => [ - 'name' => 'name with superuniquevalue within', + 'name' => 'name with superuniquevalue within', 'content' => 'Description with superuniquevalue within', ], ]); @@ -74,4 +74,46 @@ public function test_all_endpoint_requires_query_parameter() $resp = $this->actingAsApiEditor()->get($this->baseEndpoint . '?query=myqueryvalue'); $resp->assertOk(); } + + public function test_all_endpoint_includes_parent_details_where_visible() + { + $page = $this->entities->pageWithinChapter(); + $chapter = $page->chapter; + $book = $page->book; + + $page->update(['name' => 'name with superextrauniquevalue within']); + $page->indexForSearch(); + + $editor = $this->users->editor(); + $this->actingAsApiEditor(); + $resp = $this->getJson($this->baseEndpoint . '?query=superextrauniquevalue'); + $resp->assertJsonFragment([ + 'id' => $page->id, + 'type' => 'page', + 'book' => [ + 'id' => $book->id, + 'name' => $book->name, + 'slug' => $book->slug, + ], + 'chapter' => [ + 'id' => $chapter->id, + 'name' => $chapter->name, + 'slug' => $chapter->slug, + ], + ]); + + $this->permissions->disableEntityInheritedPermissions($chapter); + $this->permissions->setEntityPermissions($page, ['view'], [$editor->roles()->first()]); + + $resp = $this->getJson($this->baseEndpoint . '?query=superextrauniquevalue'); + $resp->assertJsonPath('data.0.id', $page->id); + $resp->assertJsonPath('data.0.book.name', $book->name); + $resp->assertJsonMissingPath('data.0.chapter'); + + $this->permissions->disableEntityInheritedPermissions($book); + + $resp = $this->getJson($this->baseEndpoint . '?query=superextrauniquevalue'); + $resp->assertJsonPath('data.0.id', $page->id); + $resp->assertJsonMissingPath('data.0.book.name'); + } } diff --git a/tests/Api/ShelvesApiTest.php b/tests/Api/ShelvesApiTest.php index f1b8ed98553..ba13c0153b1 100644 --- a/tests/Api/ShelvesApiTest.php +++ b/tests/Api/ShelvesApiTest.php @@ -4,6 +4,7 @@ use BookStack\Entities\Models\Book; use BookStack\Entities\Models\Bookshelf; +use BookStack\Entities\Repos\BaseRepo; use Carbon\Carbon; use Illuminate\Support\Facades\DB; use Tests\TestCase; @@ -25,6 +26,31 @@ public function test_index_endpoint_returns_expected_shelf() 'id' => $firstBookshelf->id, 'name' => $firstBookshelf->name, 'slug' => $firstBookshelf->slug, + 'owned_by' => $firstBookshelf->owned_by, + 'created_by' => $firstBookshelf->created_by, + 'updated_by' => $firstBookshelf->updated_by, + 'cover' => null, + ], + ]]); + } + + public function test_index_endpoint_includes_cover_if_set() + { + $this->actingAsApiEditor(); + $shelf = $this->entities->shelf(); + + $baseRepo = $this->app->make(BaseRepo::class); + $image = $this->files->uploadedImage('shelf_cover'); + $baseRepo->updateCoverImage($shelf, $image); + + $resp = $this->getJson($this->baseEndpoint . '?filter[id]=' . $shelf->id); + $resp->assertJson(['data' => [ + [ + 'id' => $shelf->id, + 'cover' => [ + 'id' => $shelf->cover->id, + 'url' => $shelf->cover->url, + ], ], ]]); } diff --git a/tests/Api/SystemApiTest.php b/tests/Api/SystemApiTest.php new file mode 100644 index 00000000000..e915286e888 --- /dev/null +++ b/tests/Api/SystemApiTest.php @@ -0,0 +1,22 @@ +actingAsApiEditor()->get('/api/system'); + $data = $resp->json(); + + $this->assertStringStartsWith('v', $data['version']); + $this->assertEquals(setting('instance-id'), $data['instance_id']); + $this->assertEquals(setting('app-name'), $data['app_name']); + $this->assertEquals(url('/logo.png'), $data['app_logo']); + $this->assertEquals(url('/'), $data['base_url']); + } +} diff --git a/tests/Auth/AuthTest.php b/tests/Auth/AuthTest.php index 0164978d85d..bffd8bbdbcb 100644 --- a/tests/Auth/AuthTest.php +++ b/tests/Auth/AuthTest.php @@ -3,6 +3,7 @@ namespace Tests\Auth; use BookStack\Access\Mfa\MfaSession; +use Illuminate\Support\Facades\Hash; use Illuminate\Testing\TestResponse; use Tests\TestCase; @@ -144,6 +145,25 @@ public function test_login_attempts_are_rate_limited() $resp->assertSee('Too many login attempts. Please try again in'); } + public function test_login_specifically_disabled_for_guest_account() + { + $guest = $this->users->guest(); + + $resp = $this->post('/login', ['email' => $guest->email, 'password' => 'password']); + $resp->assertRedirect('/login'); + $resp = $this->followRedirects($resp); + $resp->assertSee('These credentials do not match our records.'); + + // Test login even with password somehow set + $guest->password = Hash::make('password'); + $guest->save(); + + $resp = $this->post('/login', ['email' => $guest->email, 'password' => 'password']); + $resp->assertRedirect('/login'); + $resp = $this->followRedirects($resp); + $resp->assertSee('These credentials do not match our records.'); + } + /** * Perform a login. */ diff --git a/tests/Auth/LdapTest.php b/tests/Auth/LdapTest.php index 34900ce6f70..d1f128a50d9 100644 --- a/tests/Auth/LdapTest.php +++ b/tests/Auth/LdapTest.php @@ -4,6 +4,7 @@ use BookStack\Access\Ldap; use BookStack\Access\LdapService; +use BookStack\Exceptions\LdapException; use BookStack\Users\Models\Role; use BookStack\Users\Models\User; use Illuminate\Testing\TestResponse; @@ -32,9 +33,10 @@ protected function setUp(): void 'services.ldap.id_attribute' => 'uid', 'services.ldap.user_to_groups' => false, 'services.ldap.version' => '3', - 'services.ldap.user_filter' => '(&(uid=${user}))', + 'services.ldap.user_filter' => '(&(uid={user}))', 'services.ldap.follow_referrals' => false, 'services.ldap.tls_insecure' => false, + 'services.ldap.tls_ca_cert' => false, 'services.ldap.thumbnail_attribute' => null, ]); $this->mockLdap = $this->mock(Ldap::class); @@ -74,7 +76,7 @@ protected function mockUserLogin(?string $email = null): TestResponse /** * Set LDAP method mocks for things we commonly call without altering. */ - protected function commonLdapMocks(int $connects = 1, int $versions = 1, int $options = 2, int $binds = 4, int $escapes = 2, int $explodes = 0) + protected function commonLdapMocks(int $connects = 1, int $versions = 1, int $options = 2, int $binds = 4, int $escapes = 2, int $explodes = 0, int $groups = 0) { $this->mockLdap->shouldReceive('connect')->times($connects)->andReturn($this->resourceId); $this->mockLdap->shouldReceive('setVersion')->times($versions); @@ -82,6 +84,13 @@ protected function commonLdapMocks(int $connects = 1, int $versions = 1, int $op $this->mockLdap->shouldReceive('bind')->times($binds)->andReturn(true); $this->mockEscapes($escapes); $this->mockExplodes($explodes); + $this->mockGroupLookups($groups); + } + + protected function mockGroupLookups(int $times = 1): void + { + $this->mockLdap->shouldReceive('read')->times($times)->andReturn(['count' => 0]); + $this->mockLdap->shouldReceive('getEntries')->times($times)->andReturn(['count' => 0]); } public function test_login() @@ -157,6 +166,26 @@ public function test_login_works_when_no_uid_provided_by_ldap_server() $this->assertDatabaseHas('users', ['email' => $this->mockUser->email, 'email_confirmed' => false, 'external_auth_id' => $ldapDn]); } + public function test_login_works_when_ldap_server_does_not_provide_a_cn_value() + { + $ldapDn = 'cn=test-user,dc=test' . config('services.ldap.base_dn'); + + $this->commonLdapMocks(1, 1, 1, 2, 1); + $this->mockLdap->shouldReceive('searchAndGetEntries')->times(1) + ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array')) + ->andReturn(['count' => 1, 0 => [ + 'dn' => $ldapDn, + 'mail' => [$this->mockUser->email], + ]]); + + $resp = $this->mockUserLogin(); + $resp->assertRedirect('/'); + $this->assertDatabaseHas('users', [ + 'name' => 'test-user', + 'email' => $this->mockUser->email, + ]); + } + public function test_a_custom_uid_attribute_can_be_specified_and_is_used_properly() { config()->set(['services.ldap.id_attribute' => 'my_custom_id']); @@ -178,6 +207,38 @@ public function test_a_custom_uid_attribute_can_be_specified_and_is_used_properl $this->assertDatabaseHas('users', ['email' => $this->mockUser->email, 'email_confirmed' => false, 'external_auth_id' => 'cooluser456']); } + public function test_user_filter_default_placeholder_format() + { + config()->set('services.ldap.user_filter', '(&(uid={user}))'); + $this->mockUser->name = 'barryldapuser'; + $expectedFilter = '(&(uid=\62\61\72\72\79\6c\64\61\70\75\73\65\72))'; + + $this->commonLdapMocks(1, 1, 1, 1, 1); + $this->mockLdap->shouldReceive('searchAndGetEntries') + ->once() + ->with($this->resourceId, config('services.ldap.base_dn'), $expectedFilter, \Mockery::type('array')) + ->andReturn(['count' => 0, 0 => []]); + + $resp = $this->mockUserLogin(); + $resp->assertRedirect('/login'); + } + + public function test_user_filter_old_placeholder_format() + { + config()->set('services.ldap.user_filter', '(&(username=${user}))'); + $this->mockUser->name = 'barryldapuser'; + $expectedFilter = '(&(username=\62\61\72\72\79\6c\64\61\70\75\73\65\72))'; + + $this->commonLdapMocks(1, 1, 1, 1, 1); + $this->mockLdap->shouldReceive('searchAndGetEntries') + ->once() + ->with($this->resourceId, config('services.ldap.base_dn'), $expectedFilter, \Mockery::type('array')) + ->andReturn(['count' => 0, 0 => []]); + + $resp = $this->mockUserLogin(); + $resp->assertRedirect('/login'); + } + public function test_initial_incorrect_credentials() { $this->commonLdapMocks(1, 1, 1, 0, 1); @@ -273,8 +334,8 @@ public function test_login_maps_roles_and_retains_existing_roles() 'services.ldap.remove_from_groups' => false, ]); - $this->commonLdapMocks(1, 1, 4, 5, 4, 6); - $this->mockLdap->shouldReceive('searchAndGetEntries')->times(4) + $this->commonLdapMocks(1, 1, 4, 5, 2, 2, 2); + $this->mockLdap->shouldReceive('searchAndGetEntries')->times(2) ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array')) ->andReturn(['count' => 1, 0 => [ 'uid' => [$this->mockUser->name], @@ -318,8 +379,8 @@ public function test_login_maps_roles_and_removes_old_roles_if_set() 'services.ldap.remove_from_groups' => true, ]); - $this->commonLdapMocks(1, 1, 3, 4, 3, 2); - $this->mockLdap->shouldReceive('searchAndGetEntries')->times(3) + $this->commonLdapMocks(1, 1, 3, 4, 2, 1, 1); + $this->mockLdap->shouldReceive('searchAndGetEntries')->times(2) ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array')) ->andReturn(['count' => 1, 0 => [ 'uid' => [$this->mockUser->name], @@ -360,22 +421,26 @@ public function test_dump_user_groups_shows_group_related_details_as_json() 'dn' => 'dc=test,' . config('services.ldap.base_dn'), 'mail' => [$this->mockUser->email], ]]; - $this->commonLdapMocks(1, 1, 4, 5, 4, 2); - $this->mockLdap->shouldReceive('searchAndGetEntries')->times(4) + $this->commonLdapMocks(1, 1, 4, 5, 2, 2, 0); + $this->mockLdap->shouldReceive('searchAndGetEntries')->times(2) ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array')) ->andReturn($userResp, ['count' => 1, - 0 => [ - 'dn' => 'dc=test,' . config('services.ldap.base_dn'), + 0 => [ + 'dn' => 'dc=test,' . config('services.ldap.base_dn'), 'memberof' => [ 'count' => 1, - 0 => 'cn=ldaptester,ou=groups,dc=example,dc=com', + 0 => 'cn=ldaptester,ou=groups,dc=example,dc=com', ], ], - ], [ + ]); + + $this->mockLdap->shouldReceive('read')->times(2); + $this->mockLdap->shouldReceive('getEntries')->times(2) + ->andReturn([ 'count' => 1, - 0 => [ - 'dn' => 'cn=ldaptester,ou=groups,dc=example,dc=com', - 'memberof' => [ + 0 => [ + 'dn' => 'cn=ldaptester,ou=groups,dc=example,dc=com', + 'memberof' => [ 'count' => 1, 0 => 'cn=monsters,ou=groups,dc=example,dc=com', ], @@ -392,15 +457,60 @@ public function test_dump_user_groups_shows_group_related_details_as_json() ], ], 'parsed_direct_user_groups' => [ - 'ldaptester', + 'cn=ldaptester,ou=groups,dc=example,dc=com', ], 'parsed_recursive_user_groups' => [ + 'cn=ldaptester,ou=groups,dc=example,dc=com', + 'cn=monsters,ou=groups,dc=example,dc=com', + ], + 'parsed_resulting_group_names' => [ 'ldaptester', 'monsters', ], ]); } + public function test_recursive_group_search_queries_via_full_dn() + { + app('config')->set([ + 'services.ldap.user_to_groups' => true, + 'services.ldap.group_attribute' => 'memberOf', + ]); + + $userResp = ['count' => 1, 0 => [ + 'uid' => [$this->mockUser->name], + 'cn' => [$this->mockUser->name], + 'dn' => 'dc=test,' . config('services.ldap.base_dn'), + 'mail' => [$this->mockUser->email], + ]]; + $groupResp = ['count' => 1, + 0 => [ + 'dn' => 'dc=test,' . config('services.ldap.base_dn'), + 'memberof' => [ + 'count' => 1, + 0 => 'cn=ldaptester,ou=groups,dc=example,dc=com', + ], + ], + ]; + + $this->commonLdapMocks(1, 1, 3, 4, 2, 1); + + $escapedName = ldap_escape($this->mockUser->name); + $this->mockLdap->shouldReceive('searchAndGetEntries')->twice() + ->with($this->resourceId, config('services.ldap.base_dn'), "(&(uid={$escapedName}))", \Mockery::type('array')) + ->andReturn($userResp, $groupResp); + + $this->mockLdap->shouldReceive('read')->times(1) + ->with($this->resourceId, 'cn=ldaptester,ou=groups,dc=example,dc=com', '(objectClass=*)', ['memberof']) + ->andReturn(['count' => 0]); + $this->mockLdap->shouldReceive('getEntries')->times(1) + ->with($this->resourceId, ['count' => 0]) + ->andReturn(['count' => 0]); + + $resp = $this->mockUserLogin(); + $resp->assertRedirect('/'); + } + public function test_external_auth_id_visible_in_roles_page_when_ldap_active() { $role = Role::factory()->create(['display_name' => 'ldaptester', 'external_auth_id' => 'ex-auth-a, test-second-param']); @@ -419,8 +529,8 @@ public function test_login_maps_roles_using_external_auth_ids_if_set() 'services.ldap.remove_from_groups' => true, ]); - $this->commonLdapMocks(1, 1, 3, 4, 3, 2); - $this->mockLdap->shouldReceive('searchAndGetEntries')->times(3) + $this->commonLdapMocks(1, 1, 3, 4, 2, 1, 1); + $this->mockLdap->shouldReceive('searchAndGetEntries')->times(2) ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array')) ->andReturn(['count' => 1, 0 => [ 'uid' => [$this->mockUser->name], @@ -460,8 +570,8 @@ public function test_login_group_mapping_does_not_conflict_with_default_role() 'services.ldap.remove_from_groups' => true, ]); - $this->commonLdapMocks(1, 1, 4, 5, 4, 6); - $this->mockLdap->shouldReceive('searchAndGetEntries')->times(4) + $this->commonLdapMocks(1, 1, 4, 5, 2, 2, 2); + $this->mockLdap->shouldReceive('searchAndGetEntries')->times(2) ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array')) ->andReturn(['count' => 1, 0 => [ 'uid' => [$this->mockUser->name], @@ -513,6 +623,33 @@ public function test_login_uses_specified_display_name_attribute() $this->assertDatabaseHas('users', ['email' => $this->mockUser->email, 'email_confirmed' => false, 'external_auth_id' => $this->mockUser->name, 'name' => 'displayNameAttribute']); } + public function test_login_uses_multiple_display_properties_if_defined() + { + app('config')->set([ + 'services.ldap.display_name_attribute' => 'firstname|middlename|noname|lastname', + ]); + + $this->commonLdapMocks(1, 1, 1, 2, 1); + $this->mockLdap->shouldReceive('searchAndGetEntries')->times(1) + ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array')) + ->andReturn(['count' => 1, 0 => [ + 'uid' => [$this->mockUser->name], + 'cn' => [$this->mockUser->name], + 'dn' => 'dc=test' . config('services.ldap.base_dn'), + 'firstname' => ['Barry'], + 'middlename' => ['Elliott'], + 'lastname' => ['Chuckle'], + 'mail' => [$this->mockUser->email], + ]]); + + $this->mockUserLogin(); + + $this->assertDatabaseHas('users', [ + 'email' => $this->mockUser->email, + 'name' => 'Barry Elliott Chuckle', + ]); + } + public function test_login_uses_default_display_name_attribute_if_specified_not_present() { app('config')->set([ @@ -700,9 +837,9 @@ public function test_login_with_email_confirmation_required_maps_groups_but_show 'services.ldap.remove_from_groups' => true, ]); - $this->commonLdapMocks(1, 1, 6, 8, 6, 4); + $this->commonLdapMocks(1, 1, 6, 8, 4, 2, 2); $this->mockLdap->shouldReceive('searchAndGetEntries') - ->times(6) + ->times(4) ->andReturn(['count' => 1, 0 => [ 'uid' => [$user->name], 'cn' => [$user->name], @@ -767,4 +904,34 @@ public function test_thumbnail_attribute_used_as_user_avatar_if_configured() $this->assertNotNull($user->avatar); $this->assertEquals('8c90748342f19b195b9c6b4eff742ded', md5_file(public_path($user->avatar->path))); } + + public function test_tls_ca_cert_option_throws_if_set_to_invalid_location() + { + $path = 'non_found_' . time(); + config()->set(['services.ldap.tls_ca_cert' => $path]); + + $this->commonLdapMocks(0, 0, 0, 0, 0); + + $this->assertThrows(function () { + $this->withoutExceptionHandling()->mockUserLogin(); + }, LdapException::class, "Provided path [{$path}] for LDAP TLS CA certs could not be resolved to an existing location"); + } + + public function test_tls_ca_cert_option_used_if_set_to_a_folder() + { + $path = $this->files->testFilePath(''); + config()->set(['services.ldap.tls_ca_cert' => $path]); + + $this->mockLdap->shouldReceive('setOption')->once()->with(null, LDAP_OPT_X_TLS_CACERTDIR, rtrim($path, '/'))->andReturn(true); + $this->runFailedAuthLogin(); + } + + public function test_tls_ca_cert_option_used_if_set_to_a_file() + { + $path = $this->files->testFilePath('test-file.txt'); + config()->set(['services.ldap.tls_ca_cert' => $path]); + + $this->mockLdap->shouldReceive('setOption')->once()->with(null, LDAP_OPT_X_TLS_CACERTFILE, $path)->andReturn(true); + $this->runFailedAuthLogin(); + } } diff --git a/tests/Auth/MfaVerificationTest.php b/tests/Auth/MfaVerificationTest.php index 2fa272e331c..76c59bc748b 100644 --- a/tests/Auth/MfaVerificationTest.php +++ b/tests/Auth/MfaVerificationTest.php @@ -57,6 +57,15 @@ public function test_totp_verification_fails_on_missing_invalid_code() $this->assertNull(auth()->user()); } + public function test_totp_form_has_autofill_configured() + { + [$user, $secret, $loginResp] = $this->startTotpLogin(); + $html = $this->withHtml($this->get('/mfa/verify')); + + $html->assertElementExists('form[autocomplete="off"][action$="/verify"]'); + $html->assertElementExists('input[autocomplete="one-time-code"][name="code"]'); + } + public function test_backup_code_verification() { [$user, $codes, $loginResp] = $this->startBackupCodeLogin(); @@ -138,6 +147,15 @@ public function test_backup_code_verification_shows_warning_when_limited_codes_r $resp->assertSeeText('You have less than 5 backup codes remaining, Please generate and store a new set before you run out of codes to prevent being locked out of your account.'); } + public function test_backup_code_form_has_autofill_configured() + { + [$user, $codes, $loginResp] = $this->startBackupCodeLogin(); + $html = $this->withHtml($this->get('/mfa/verify')); + + $html->assertElementExists('form[autocomplete="off"][action$="/verify"]'); + $html->assertElementExists('input[autocomplete="one-time-code"][name="code"]'); + } + public function test_both_mfa_options_available_if_set_on_profile() { $user = $this->users->editor(); diff --git a/tests/Auth/OidcTest.php b/tests/Auth/OidcTest.php index dbf26f1bd30..a0db1c2ba00 100644 --- a/tests/Auth/OidcTest.php +++ b/tests/Auth/OidcTest.php @@ -5,6 +5,7 @@ use BookStack\Activity\ActivityType; use BookStack\Facades\Theme; use BookStack\Theming\ThemeEvents; +use BookStack\Uploads\UserAvatars; use BookStack\Users\Models\Role; use BookStack\Users\Models\User; use GuzzleHttp\Psr7\Response; @@ -37,9 +38,11 @@ protected function setUp(): void 'oidc.issuer' => OidcJwtHelper::defaultIssuer(), 'oidc.authorization_endpoint' => 'https://oidc.local/auth', 'oidc.token_endpoint' => 'https://oidc.local/token', + 'oidc.userinfo_endpoint' => 'https://oidc.local/userinfo', 'oidc.discover' => false, 'oidc.dump_user_details' => false, 'oidc.additional_scopes' => '', + 'odic.fetch_avatar' => false, 'oidc.user_to_groups' => false, 'oidc.groups_claim' => 'group', 'oidc.remove_from_groups' => false, @@ -208,6 +211,8 @@ public function test_dump_user_details_option_outputs_as_expected() public function test_auth_fails_if_no_email_exists_in_user_data() { + config()->set('oidc.userinfo_endpoint', null); + $this->runLogin([ 'email' => '', 'sub' => 'benny505', @@ -270,10 +275,38 @@ public function test_auth_login_with_invalid_token_fails() ]); $resp = $this->followRedirects($resp); - $resp->assertSeeText('ID token validate failed with error: Missing token subject value'); + $resp->assertSeeText('ID token validation failed with error: Missing token subject value'); $this->assertFalse(auth()->check()); } + public function test_auth_fails_if_endpoints_start_with_https() + { + $endpointConfigKeys = [ + 'oidc.token_endpoint' => 'tokenEndpoint', + 'oidc.authorization_endpoint' => 'authorizationEndpoint', + 'oidc.userinfo_endpoint' => 'userinfoEndpoint', + ]; + + foreach ($endpointConfigKeys as $endpointConfigKey => $endpointName) { + $logger = $this->withTestLogger(); + $original = config()->get($endpointConfigKey); + $new = str_replace('https://', 'http://', $original); + config()->set($endpointConfigKey, $new); + + $this->withoutExceptionHandling(); + $err = null; + try { + $resp = $this->runLogin(); + $resp->assertRedirect('/login'); + } catch (\Exception $exception) { + $err = $exception; + } + $this->assertEquals("Endpoint value for \"{$endpointName}\" must start with https://", $err->getMessage()); + + config()->set($endpointConfigKey, $original); + } + } + public function test_auth_login_with_autodiscovery() { $this->withAutodiscovery(); @@ -426,6 +459,105 @@ public function test_auth_uses_mulitple_display_name_claims_if_configured() ]); } + public function test_user_avatar_fetched_from_picture_on_first_login_if_enabled() + { + config()->set(['oidc.fetch_avatar' => true]); + + $this->runLogin([ + 'email' => 'avatar@example.com', + 'picture' => 'https://example.com/my-avatar.jpg', + ], [ + new Response(200, ['Content-Type' => 'image/jpeg'], $this->files->jpegImageData()) + ]); + + $user = User::query()->where('email', '=', 'avatar@example.com')->first(); + $this->assertNotNull($user); + + $this->assertTrue($user->avatar()->exists()); + } + + public function test_user_avatar_fetched_for_existing_user_when_no_avatar_already_assigned() + { + config()->set(['oidc.fetch_avatar' => true]); + $editor = $this->users->editor(); + $editor->external_auth_id = 'benny509'; + $editor->save(); + + $this->assertFalse($editor->avatar()->exists()); + + $this->runLogin([ + 'picture' => 'https://example.com/my-avatar.jpg', + 'sub' => 'benny509', + ], [ + new Response(200, ['Content-Type' => 'image/jpeg'], $this->files->jpegImageData()) + ]); + + $editor->refresh(); + $this->assertTrue($editor->avatar()->exists()); + } + + public function test_user_avatar_not_fetched_if_image_data_format_unknown() + { + config()->set(['oidc.fetch_avatar' => true]); + + $this->runLogin([ + 'email' => 'avatar-format@example.com', + 'picture' => 'https://example.com/my-avatar.jpg', + ], [ + new Response(200, ['Content-Type' => 'image/jpeg'], str_repeat('abc123', 5)) + ]); + + $user = User::query()->where('email', '=', 'avatar-format@example.com')->first(); + $this->assertNotNull($user); + + $this->assertFalse($user->avatar()->exists()); + } + + public function test_user_avatar_not_fetched_when_avatar_already_assigned() + { + config()->set(['oidc.fetch_avatar' => true]); + $editor = $this->users->editor(); + $editor->external_auth_id = 'benny509'; + $editor->save(); + + $avatars = $this->app->make(UserAvatars::class); + $originalImageData = $this->files->pngImageData(); + $avatars->assignToUserFromExistingData($editor, $originalImageData, 'png'); + + $this->runLogin([ + 'picture' => 'https://example.com/my-avatar.jpg', + 'sub' => 'benny509', + ], [ + new Response(200, ['Content-Type' => 'image/jpeg'], $this->files->jpegImageData()) + ]); + + $editor->refresh(); + $newAvatarData = file_get_contents($this->files->relativeToFullPath($editor->avatar->path)); + $this->assertEquals($originalImageData, $newAvatarData); + } + + public function test_user_avatar_fetch_follows_up_to_three_redirects() + { + config()->set(['oidc.fetch_avatar' => true]); + + $logger = $this->withTestLogger(); + + $this->runLogin([ + 'email' => 'avatar@example.com', + 'picture' => 'https://example.com/my-avatar.jpg', + ], [ + new Response(302, ['Location' => 'https://example.com/a']), + new Response(302, ['Location' => 'https://example.com/b']), + new Response(302, ['Location' => 'https://example.com/c']), + new Response(302, ['Location' => 'https://example.com/d']), + ]); + + $user = User::query()->where('email', '=', 'avatar@example.com')->first(); + $this->assertFalse($user->avatar()->exists()); + + $this->assertStringContainsString('"Failed to fetch image, max redirect limit of 3 tries reached. Last fetched URL: https://example.com/c"', $logger->getRecords()[0]->formatted); + } + public function test_login_group_sync() { config()->set([ @@ -594,10 +726,16 @@ public function test_logout_redirect_contains_id_token_hint_if_existing() { config()->set(['oidc.end_session_endpoint' => 'https://example.com/logout']); - $this->runLogin(); + // Fix times so our token is predictable + $claimOverrides = [ + 'iat' => time(), + 'exp' => time() + 720, + 'auth_time' => time() + ]; + $this->runLogin($claimOverrides); $resp = $this->asEditor()->post('/oidc/logout'); - $query = 'id_token_hint=' . urlencode(OidcJwtHelper::idToken()) . '&post_logout_redirect_uri=' . urlencode(url('/')); + $query = 'id_token_hint=' . urlencode(OidcJwtHelper::idToken($claimOverrides)) . '&post_logout_redirect_uri=' . urlencode(url('/')); $resp->assertRedirect('https://example.com/logout?' . $query); } @@ -655,22 +793,214 @@ public function test_oidc_id_token_pre_validate_theme_event_with_return() ]); } - protected function withAutodiscovery() + public function test_pkce_used_on_authorize_and_access() + { + // Start auth + $resp = $this->post('/oidc/login'); + $state = session()->get('oidc_state'); + + $pkceCode = session()->get('oidc_pkce_code'); + $this->assertGreaterThan(30, strlen($pkceCode)); + + $expectedCodeChallenge = trim(strtr(base64_encode(hash('sha256', $pkceCode, true)), '+/', '-_'), '='); + $redirect = $resp->headers->get('Location'); + $redirectParams = []; + parse_str(parse_url($redirect, PHP_URL_QUERY), $redirectParams); + $this->assertEquals($expectedCodeChallenge, $redirectParams['code_challenge']); + $this->assertEquals('S256', $redirectParams['code_challenge_method']); + + $transactions = $this->mockHttpClient([$this->getMockAuthorizationResponse([ + 'email' => 'benny@example.com', + 'sub' => 'benny1010101', + ])]); + + $this->get('/oidc/callback?code=SplxlOBeZQQYbYS6WxSbIA&state=' . $state); + $tokenRequest = $transactions->latestRequest(); + $bodyParams = []; + parse_str($tokenRequest->getBody(), $bodyParams); + $this->assertEquals($pkceCode, $bodyParams['code_verifier']); + } + + public function test_userinfo_endpoint_used_if_missing_claims_in_id_token() + { + config()->set('oidc.display_name_claims', 'first_name|last_name'); + $this->post('/oidc/login'); + $state = session()->get('oidc_state'); + + $client = $this->mockHttpClient([ + $this->getMockAuthorizationResponse(['name' => null]), + new Response(200, [ + 'Content-Type' => 'application/json', + ], json_encode([ + 'sub' => OidcJwtHelper::defaultPayload()['sub'], + 'first_name' => 'Barry', + 'last_name' => 'Userinfo', + ])) + ]); + + $resp = $this->get('/oidc/callback?code=SplxlOBeZQQYbYS6WxSbIA&state=' . $state); + $resp->assertRedirect('/'); + $this->assertEquals(2, $client->requestCount()); + + $userinfoRequest = $client->requestAt(1); + $this->assertEquals('GET', $userinfoRequest->getMethod()); + $this->assertEquals('https://oidc.local/userinfo', (string) $userinfoRequest->getUri()); + + $this->assertEquals('Barry Userinfo', user()->name); + } + + public function test_userinfo_endpoint_fetch_with_different_sub_throws_error() + { + $userinfoResponseData = ['sub' => 'dcba4321']; + $userinfoResponse = new Response(200, ['Content-Type' => 'application/json'], json_encode($userinfoResponseData)); + $resp = $this->runLogin(['name' => null], [$userinfoResponse]); + $resp->assertRedirect('/login'); + $this->assertSessionError('Userinfo endpoint response validation failed with error: Subject value provided in the userinfo endpoint does not match the provided ID token value'); + } + + public function test_userinfo_endpoint_fetch_returning_no_sub_throws_error() + { + $userinfoResponseData = ['name' => 'testing']; + $userinfoResponse = new Response(200, ['Content-Type' => 'application/json'], json_encode($userinfoResponseData)); + $resp = $this->runLogin(['name' => null], [$userinfoResponse]); + $resp->assertRedirect('/login'); + $this->assertSessionError('Userinfo endpoint response validation failed with error: No valid subject value found in userinfo data'); + } + + public function test_userinfo_endpoint_fetch_can_parsed_nested_groups() + { + config()->set([ + 'oidc.user_to_groups' => true, + 'oidc.groups_claim' => 'my.nested.groups.attr', + 'oidc.remove_from_groups' => false, + ]); + + $roleA = Role::factory()->create(['display_name' => 'Ducks']); + $userinfoResponseData = [ + 'sub' => OidcJwtHelper::defaultPayload()['sub'], + 'my' => ['nested' => ['groups' => ['attr' => ['Ducks', 'Donkeys']]]] + ]; + $userinfoResponse = new Response(200, ['Content-Type' => 'application/json'], json_encode($userinfoResponseData)); + $resp = $this->runLogin(['groups' => null], [$userinfoResponse]); + $resp->assertRedirect('/'); + + $user = User::where('email', OidcJwtHelper::defaultPayload()['email'])->first(); + $this->assertTrue($user->hasRole($roleA->id)); + } + + public function test_userinfo_endpoint_response_with_complex_json_content_type_handled() + { + $userinfoResponseData = [ + 'sub' => OidcJwtHelper::defaultPayload()['sub'], + 'name' => 'Barry', + ]; + $userinfoResponse = new Response(200, ['Content-Type' => 'Application/Json ; charset=utf-8'], json_encode($userinfoResponseData)); + $resp = $this->runLogin(['name' => null], [$userinfoResponse]); + $resp->assertRedirect('/'); + + $user = User::where('email', OidcJwtHelper::defaultPayload()['email'])->first(); + $this->assertEquals('Barry', $user->name); + } + + public function test_userinfo_endpoint_jwks_response_handled() + { + $userinfoResponseData = OidcJwtHelper::idToken(['name' => 'Barry Jwks']); + $userinfoResponse = new Response(200, ['Content-Type' => 'application/jwt'], $userinfoResponseData); + + $resp = $this->runLogin(['name' => null], [$userinfoResponse]); + $resp->assertRedirect('/'); + + $user = User::where('email', OidcJwtHelper::defaultPayload()['email'])->first(); + $this->assertEquals('Barry Jwks', $user->name); + } + + public function test_userinfo_endpoint_jwks_response_returning_no_sub_throws() + { + $userinfoResponseData = OidcJwtHelper::idToken(['sub' => null]); + $userinfoResponse = new Response(200, ['Content-Type' => 'application/jwt'], $userinfoResponseData); + + $resp = $this->runLogin(['name' => null], [$userinfoResponse]); + $resp->assertRedirect('/login'); + $this->assertSessionError('Userinfo endpoint response validation failed with error: No valid subject value found in userinfo data'); + } + + public function test_userinfo_endpoint_jwks_response_returning_non_matching_sub_throws() + { + $userinfoResponseData = OidcJwtHelper::idToken(['sub' => 'zzz123']); + $userinfoResponse = new Response(200, ['Content-Type' => 'application/jwt'], $userinfoResponseData); + + $resp = $this->runLogin(['name' => null], [$userinfoResponse]); + $resp->assertRedirect('/login'); + $this->assertSessionError('Userinfo endpoint response validation failed with error: Subject value provided in the userinfo endpoint does not match the provided ID token value'); + } + + public function test_userinfo_endpoint_jwks_response_with_invalid_signature_throws() + { + $userinfoResponseData = OidcJwtHelper::idToken(); + $exploded = explode('.', $userinfoResponseData); + $exploded[2] = base64_encode(base64_decode($exploded[2]) . 'ABC'); + $userinfoResponse = new Response(200, ['Content-Type' => 'application/jwt'], implode('.', $exploded)); + + $resp = $this->runLogin(['name' => null], [$userinfoResponse]); + $resp->assertRedirect('/login'); + $this->assertSessionError('Userinfo endpoint response validation failed with error: Token signature could not be validated using the provided keys'); + } + + public function test_userinfo_endpoint_jwks_response_with_invalid_signature_alg_throws() + { + $userinfoResponseData = OidcJwtHelper::idToken([], ['alg' => 'ZZ512']); + $userinfoResponse = new Response(200, ['Content-Type' => 'application/jwt'], $userinfoResponseData); + + $resp = $this->runLogin(['name' => null], [$userinfoResponse]); + $resp->assertRedirect('/login'); + $this->assertSessionError('Userinfo endpoint response validation failed with error: Only RS256 signature validation is supported. Token reports using ZZ512'); + } + + public function test_userinfo_endpoint_response_with_invalid_content_type_throws() + { + $userinfoResponse = new Response(200, ['Content-Type' => 'application/beans'], json_encode(OidcJwtHelper::defaultPayload())); + $resp = $this->runLogin(['name' => null], [$userinfoResponse]); + $resp->assertRedirect('/login'); + $this->assertSessionError('Userinfo endpoint response validation failed with error: No valid subject value found in userinfo data'); + } + + public function test_userinfo_endpoint_not_called_if_empty_groups_array_provided_in_id_token() + { + config()->set([ + 'oidc.user_to_groups' => true, + 'oidc.groups_claim' => 'groups', + 'oidc.remove_from_groups' => false, + ]); + + $this->post('/oidc/login'); + $state = session()->get('oidc_state'); + $client = $this->mockHttpClient([$this->getMockAuthorizationResponse([ + 'groups' => [], + ])]); + + $resp = $this->get('/oidc/callback?code=SplxlOBeZQQYbYS6WxSbIA&state=' . $state); + $resp->assertRedirect('/'); + $this->assertEquals(1, $client->requestCount()); + $this->assertTrue(auth()->check()); + } + + protected function withAutodiscovery(): void { config()->set([ 'oidc.issuer' => OidcJwtHelper::defaultIssuer(), 'oidc.discover' => true, 'oidc.authorization_endpoint' => null, 'oidc.token_endpoint' => null, + 'oidc.userinfo_endpoint' => null, 'oidc.jwt_public_key' => null, ]); } - protected function runLogin($claimOverrides = []): TestResponse + protected function runLogin($claimOverrides = [], $additionalHttpResponses = []): TestResponse { $this->post('/oidc/login'); $state = session()->get('oidc_state'); - $this->mockHttpClient([$this->getMockAuthorizationResponse($claimOverrides)]); + $this->mockHttpClient([$this->getMockAuthorizationResponse($claimOverrides), ...$additionalHttpResponses]); return $this->get('/oidc/callback?code=SplxlOBeZQQYbYS6WxSbIA&state=' . $state); } @@ -684,6 +1014,7 @@ protected function getAutoDiscoveryResponse($responseOverrides = []): Response ], json_encode(array_merge([ 'token_endpoint' => OidcJwtHelper::defaultIssuer() . '/oidc/token', 'authorization_endpoint' => OidcJwtHelper::defaultIssuer() . '/oidc/authorize', + 'userinfo_endpoint' => OidcJwtHelper::defaultIssuer() . '/oidc/userinfo', 'jwks_uri' => OidcJwtHelper::defaultIssuer() . '/oidc/keys', 'issuer' => OidcJwtHelper::defaultIssuer(), 'end_session_endpoint' => OidcJwtHelper::defaultIssuer() . '/oidc/logout', diff --git a/tests/Auth/RegistrationTest.php b/tests/Auth/RegistrationTest.php index ff1a9d66b11..2666fa3b4c7 100644 --- a/tests/Auth/RegistrationTest.php +++ b/tests/Auth/RegistrationTest.php @@ -25,6 +25,9 @@ public function test_confirmed_registration() $resp->assertRedirect('/register/confirm'); $this->assertDatabaseHas('users', ['name' => $user->name, 'email' => $user->email, 'email_confirmed' => false]); + $resp = $this->get('/register/confirm'); + $resp->assertSee('Thanks for registering!'); + // Ensure notification sent /** @var User $dbUser */ $dbUser = User::query()->where('email', '=', $user->email)->first(); @@ -184,4 +187,107 @@ public function test_registration_validation() $resp->assertSee('The email must be a valid email address.'); $resp->assertSee('The password must be at least 8 characters.'); } + + public function test_registration_simple_honeypot_active() + { + $this->setSettings(['registration-enabled' => 'true']); + + $resp = $this->get('/register'); + $this->withHtml($resp)->assertElementExists('form input[name="username"]'); + + $resp = $this->post('/register', [ + 'name' => 'Barry', + 'email' => 'barrybot@example.com', + 'password' => 'barryIsTheBestBot', + 'username' => 'MyUsername' + ]); + $resp->assertRedirect('/register'); + + $resp = $this->followRedirects($resp); + $this->withHtml($resp)->assertElementExists('form input[name="username"].text-neg'); + } + + public function test_registration_endpoint_throttled() + { + $this->setSettings(['registration-enabled' => 'true']); + + for ($i = 0; $i < 11; $i++) { + $response = $this->post('/register/', [ + 'name' => "Barry{$i}", + 'email' => "barry{$i}@example.com", + 'password' => "barryIsTheBest{$i}", + ]); + auth()->logout(); + } + + $response->assertStatus(429); + } + + public function test_registration_confirmation_throttled() + { + $this->setSettings(['registration-enabled' => 'true']); + + for ($i = 0; $i < 11; $i++) { + $response = $this->post('/register/confirm/accept', [ + 'token' => "token{$i}", + ]); + } + + $response->assertStatus(429); + } + + public function test_registration_confirmation_resend() + { + Notification::fake(); + $this->setSettings(['registration-enabled' => 'true', 'registration-confirmation' => 'true']); + $user = User::factory()->make(); + + $resp = $this->post('/register', $user->only('name', 'email', 'password')); + $resp->assertRedirect('/register/confirm'); + $dbUser = User::query()->where('email', '=', $user->email)->first(); + + $resp = $this->post('/login', ['email' => $user->email, 'password' => $user->password]); + $resp->assertRedirect('/register/confirm/awaiting'); + + $resp = $this->post('/register/confirm/resend'); + $resp->assertRedirect('/register/confirm'); + Notification::assertSentToTimes($dbUser, ConfirmEmailNotification::class, 2); + } + + public function test_registration_confirmation_expired_resend() + { + Notification::fake(); + $this->setSettings(['registration-enabled' => 'true', 'registration-confirmation' => 'true']); + $user = User::factory()->make(); + + $resp = $this->post('/register', $user->only('name', 'email', 'password')); + $resp->assertRedirect('/register/confirm'); + $dbUser = User::query()->where('email', '=', $user->email)->first(); + + $resp = $this->post('/login', ['email' => $user->email, 'password' => $user->password]); + $resp->assertRedirect('/register/confirm/awaiting'); + + $emailConfirmation = DB::table('email_confirmations')->where('user_id', '=', $dbUser->id)->first(); + $this->travel(2)->days(); + + $resp = $this->post("/register/confirm/accept", [ + 'token' => $emailConfirmation->token, + ]); + $resp->assertRedirect('/register/confirm'); + $this->assertSessionError('The confirmation token has expired, A new confirmation email has been sent.'); + + Notification::assertSentToTimes($dbUser, ConfirmEmailNotification::class, 2); + } + + public function test_registration_confirmation_awaiting_and_resend_returns_to_log_if_no_login_attempt_user_found() + { + $this->setSettings(['registration-enabled' => 'true', 'registration-confirmation' => 'true']); + + $this->get('/register/confirm/awaiting')->assertRedirect('/login'); + $this->assertSessionError('A user for this action could not be found.'); + $this->flushSession(); + + $this->post('/register/confirm/resend')->assertRedirect('/login'); + $this->assertSessionError('A user for this action could not be found.'); + } } diff --git a/tests/Auth/ResetPasswordTest.php b/tests/Auth/ResetPasswordTest.php index e60ac5643ac..026f8c5ba3f 100644 --- a/tests/Auth/ResetPasswordTest.php +++ b/tests/Auth/ResetPasswordTest.php @@ -4,11 +4,19 @@ use BookStack\Access\Notifications\ResetPasswordNotification; use BookStack\Users\Models\User; +use Carbon\CarbonInterval; use Illuminate\Support\Facades\Notification; +use Illuminate\Support\Sleep; use Tests\TestCase; class ResetPasswordTest extends TestCase { + protected function setUp(): void + { + parent::setUp(); + Sleep::fake(); + } + public function test_reset_flow() { Notification::fake(); @@ -75,6 +83,17 @@ public function test_reset_flow_shows_success_message_even_if_wrong_password_to_ ->assertSee('The password reset token is invalid for this email address.'); } + public function test_reset_request_with_not_found_user_still_has_delay() + { + $this->followingRedirects()->post('/password/email', [ + 'email' => 'barrynotfoundrandomuser@example.com', + ]); + + Sleep::assertSlept(function (CarbonInterval $duration): bool { + return $duration->totalMilliseconds > 999; + }, 1); + } + public function test_reset_page_shows_sign_links() { $this->setSettings(['registration-enabled' => 'true']); @@ -95,7 +114,30 @@ public function test_reset_request_is_throttled() $resp = $this->followingRedirects()->post('/password/email', [ 'email' => $editor->email, ]); - Notification::assertTimesSent(1, ResetPasswordNotification::class); + Notification::assertSentTimes(ResetPasswordNotification::class, 1); $resp->assertSee('A password reset link will be sent to ' . $editor->email . ' if that email address is found in the system.'); } + + public function test_reset_request_with_not_found_user_is_throttled() + { + for ($i = 0; $i < 11; $i++) { + $response = $this->post('/password/email', [ + 'email' => 'barrynotfoundrandomuser@example.com', + ]); + } + + $response->assertStatus(429); + } + + public function test_reset_call_is_throttled() + { + for ($i = 0; $i < 11; $i++) { + $response = $this->post('/password/reset', [ + 'email' => "arandomuser{$i}@example.com", + 'token' => "randomtoken{$i}", + ]); + } + + $response->assertStatus(429); + } } diff --git a/tests/Auth/UserInviteTest.php b/tests/Auth/UserInviteTest.php index a9dee0007f5..434de6aa6e9 100644 --- a/tests/Auth/UserInviteTest.php +++ b/tests/Auth/UserInviteTest.php @@ -137,4 +137,24 @@ public function test_token_expires_after_two_weeks() $setPasswordPageResp->assertRedirect('/password/email'); $setPasswordPageResp->assertSessionHas('error', 'This invitation link has expired. You can instead try to reset your account password.'); } + + public function test_set_password_view_is_throttled() + { + for ($i = 0; $i < 11; $i++) { + $response = $this->get("/register/invite/tokenhere{$i}"); + } + + $response->assertStatus(429); + } + + public function test_set_password_post_is_throttled() + { + for ($i = 0; $i < 11; $i++) { + $response = $this->post("/register/invite/tokenhere{$i}", [ + 'password' => 'my test password', + ]); + } + + $response->assertStatus(429); + } } diff --git a/tests/Commands/AssignSortRuleCommandTest.php b/tests/Commands/AssignSortRuleCommandTest.php new file mode 100644 index 00000000000..934196471a7 --- /dev/null +++ b/tests/Commands/AssignSortRuleCommandTest.php @@ -0,0 +1,112 @@ +createMany(10); + + $commandRun = $this->artisan('bookstack:assign-sort-rule') + ->expectsOutputToContain('Sort rule ID required!') + ->assertExitCode(1); + + foreach ($sortRules as $sortRule) { + $commandRun->expectsOutputToContain("{$sortRule->id}: {$sortRule->name}"); + } + } + + public function test_run_without_options_advises_help() + { + $this->artisan("bookstack:assign-sort-rule 100") + ->expectsOutput("No option provided to specify target. Run with the -h option to see all available options.") + ->assertExitCode(1); + } + + public function test_run_without_valid_sort_advises_help() + { + $this->artisan("bookstack:assign-sort-rule 100342 --all-books") + ->expectsOutput("Sort rule of provided id 100342 not found!") + ->assertExitCode(1); + } + + public function test_confirmation_required() + { + $sortRule = SortRule::factory()->create(); + + $this->artisan("bookstack:assign-sort-rule {$sortRule->id} --all-books") + ->expectsConfirmation('Are you sure you want to continue?', 'no') + ->assertExitCode(1); + + $booksWithSort = Book::query()->whereNotNull('sort_rule_id')->count(); + $this->assertEquals(0, $booksWithSort); + } + + public function test_assign_to_all_books() + { + $sortRule = SortRule::factory()->create(); + $booksWithoutSort = Book::query()->whereNull('sort_rule_id')->count(); + $this->assertGreaterThan(0, $booksWithoutSort); + + $this->artisan("bookstack:assign-sort-rule {$sortRule->id} --all-books") + ->expectsOutputToContain("This will apply sort rule [{$sortRule->id}: {$sortRule->name}] to {$booksWithoutSort} book(s)") + ->expectsConfirmation('Are you sure you want to continue?', 'yes') + ->expectsOutputToContain("Sort applied to {$booksWithoutSort} book(s)") + ->assertExitCode(0); + + $booksWithoutSort = Book::query()->whereNull('sort_rule_id')->count(); + $this->assertEquals(0, $booksWithoutSort); + } + + public function test_assign_to_all_books_without_sort() + { + $totalBooks = Book::query()->count(); + $book = $this->entities->book(); + $sortRuleA = SortRule::factory()->create(); + $sortRuleB = SortRule::factory()->create(); + $book->sort_rule_id = $sortRuleA->id; + $book->save(); + + $booksWithoutSort = Book::query()->whereNull('sort_rule_id')->count(); + $this->assertEquals($totalBooks, $booksWithoutSort + 1); + + $this->artisan("bookstack:assign-sort-rule {$sortRuleB->id} --books-without-sort") + ->expectsConfirmation('Are you sure you want to continue?', 'yes') + ->expectsOutputToContain("Sort applied to {$booksWithoutSort} book(s)") + ->assertExitCode(0); + + $booksWithoutSort = Book::query()->whereNull('sort_rule_id')->count(); + $this->assertEquals(0, $booksWithoutSort); + $this->assertEquals($totalBooks, $sortRuleB->books()->count() + 1); + } + + public function test_assign_to_all_books_with_sort() + { + $book = $this->entities->book(); + $sortRuleA = SortRule::factory()->create(); + $sortRuleB = SortRule::factory()->create(); + $book->sort_rule_id = $sortRuleA->id; + $book->save(); + + $this->artisan("bookstack:assign-sort-rule {$sortRuleB->id} --books-with-sort={$sortRuleA->id}") + ->expectsConfirmation('Are you sure you want to continue?', 'yes') + ->expectsOutputToContain("Sort applied to 1 book(s)") + ->assertExitCode(0); + + $book->refresh(); + $this->assertEquals($sortRuleB->id, $book->sort_rule_id); + $this->assertEquals(1, $sortRuleB->books()->count()); + } + + public function test_assign_to_all_books_with_sort_id_is_validated() + { + $this->artisan("bookstack:assign-sort-rule 50 --books-with-sort=beans") + ->expectsOutputToContain("Provided --books-with-sort option value is invalid") + ->assertExitCode(1); + } +} diff --git a/tests/Commands/ClearActivityCommandTest.php b/tests/Commands/ClearActivityCommandTest.php index 410a39aa870..a3d7456b68e 100644 --- a/tests/Commands/ClearActivityCommandTest.php +++ b/tests/Commands/ClearActivityCommandTest.php @@ -18,7 +18,7 @@ public function test_clear_activity_command() $this->assertDatabaseHas('activities', [ 'type' => 'page_update', - 'entity_id' => $page->id, + 'loggable_id' => $page->id, 'user_id' => $this->users->editor()->id, ]); diff --git a/tests/Commands/CreateAdminCommandTest.php b/tests/Commands/CreateAdminCommandTest.php index 95a39c497e4..f389dd94235 100644 --- a/tests/Commands/CreateAdminCommandTest.php +++ b/tests/Commands/CreateAdminCommandTest.php @@ -2,8 +2,11 @@ namespace Tests\Commands; +use BookStack\Users\Models\Role; use BookStack\Users\Models\User; +use Illuminate\Support\Facades\Artisan; use Illuminate\Support\Facades\Auth; +use Illuminate\Support\Facades\Hash; use Tests\TestCase; class CreateAdminCommandTest extends TestCase @@ -11,14 +14,14 @@ class CreateAdminCommandTest extends TestCase public function test_standard_command_usage() { $this->artisan('bookstack:create-admin', [ - '--email' => 'admintest@example.com', - '--name' => 'Admin Test', + '--email' => 'admintest@example.com', + '--name' => 'Admin Test', '--password' => 'testing-4', ])->assertExitCode(0); $this->assertDatabaseHas('users', [ 'email' => 'admintest@example.com', - 'name' => 'Admin Test', + 'name' => 'Admin Test', ]); /** @var User $user */ @@ -30,14 +33,14 @@ public function test_standard_command_usage() public function test_providing_external_auth_id() { $this->artisan('bookstack:create-admin', [ - '--email' => 'admintest@example.com', - '--name' => 'Admin Test', + '--email' => 'admintest@example.com', + '--name' => 'Admin Test', '--external-auth-id' => 'xX_admin_Xx', ])->assertExitCode(0); $this->assertDatabaseHas('users', [ - 'email' => 'admintest@example.com', - 'name' => 'Admin Test', + 'email' => 'admintest@example.com', + 'name' => 'Admin Test', 'external_auth_id' => 'xX_admin_Xx', ]); @@ -50,14 +53,178 @@ public function test_password_required_if_external_auth_id_not_given() { $this->artisan('bookstack:create-admin', [ '--email' => 'admintest@example.com', - '--name' => 'Admin Test', + '--name' => 'Admin Test', ])->expectsQuestion('Please specify a password for the new admin user (8 characters min)', 'hunter2000') ->assertExitCode(0); $this->assertDatabaseHas('users', [ 'email' => 'admintest@example.com', - 'name' => 'Admin Test', + 'name' => 'Admin Test', ]); $this->assertTrue(Auth::attempt(['email' => 'admintest@example.com', 'password' => 'hunter2000'])); } + + public function test_generate_password_option() + { + $this->withoutMockingConsoleOutput() + ->artisan('bookstack:create-admin', [ + '--email' => 'admintest@example.com', + '--name' => 'Admin Test', + '--generate-password' => true, + ]); + + $output = trim(Artisan::output()); + $this->assertMatchesRegularExpression('/^[a-zA-Z0-9]{32}$/', $output); + + $user = User::query()->where('email', '=', 'admintest@example.com')->first(); + $this->assertTrue(Hash::check($output, $user->password)); + } + + public function test_initial_option_updates_default_admin() + { + $defaultAdmin = User::query()->where('email', '=', 'admin@admin.com')->first(); + + $this->artisan('bookstack:create-admin', [ + '--email' => 'firstadmin@example.com', + '--name' => 'Admin Test', + '--password' => 'testing-7', + '--initial' => true, + ])->expectsOutput('The default admin user has been updated with the provided details!') + ->assertExitCode(0); + + $defaultAdmin->refresh(); + + $this->assertEquals('firstadmin@example.com', $defaultAdmin->email); + } + + public function test_initial_option_does_not_update_if_only_non_default_admin_exists() + { + $defaultAdmin = User::query()->where('email', '=', 'admin@admin.com')->first(); + $defaultAdmin->email = 'testadmin@example.com'; + $defaultAdmin->save(); + + $this->artisan('bookstack:create-admin', [ + '--email' => 'firstadmin@example.com', + '--name' => 'Admin Test', + '--password' => 'testing-7', + '--initial' => true, + ])->expectsOutput('Non-default admin user already exists. Skipping creation of new admin user.') + ->assertExitCode(2); + + $defaultAdmin->refresh(); + + $this->assertEquals('testadmin@example.com', $defaultAdmin->email); + } + + public function test_initial_option_updates_creates_new_admin_if_none_exists() + { + $adminRole = Role::getSystemRole('admin'); + $adminRole->users()->delete(); + $this->assertEquals(0, $adminRole->users()->count()); + + $this->artisan('bookstack:create-admin', [ + '--email' => 'firstadmin@example.com', + '--name' => 'My initial admin', + '--password' => 'testing-7', + '--initial' => true, + ])->expectsOutput("Admin account with email \"firstadmin@example.com\" successfully created!") + ->assertExitCode(0); + + $this->assertEquals(1, $adminRole->users()->count()); + $this->assertDatabaseHas('users', [ + 'email' => 'firstadmin@example.com', + 'name' => 'My initial admin', + ]); + } + + public function test_initial_rerun_does_not_error_but_skips() + { + $adminRole = Role::getSystemRole('admin'); + $adminRole->users()->delete(); + + $this->artisan('bookstack:create-admin', [ + '--email' => 'firstadmin@example.com', + '--name' => 'My initial admin', + '--password' => 'testing-7', + '--initial' => true, + ])->expectsOutput("Admin account with email \"firstadmin@example.com\" successfully created!") + ->assertExitCode(0); + + $this->artisan('bookstack:create-admin', [ + '--email' => 'firstadmin@example.com', + '--name' => 'My initial admin', + '--password' => 'testing-7', + '--initial' => true, + ])->expectsOutput("Non-default admin user already exists. Skipping creation of new admin user.") + ->assertExitCode(2); + } + + public function test_initial_option_creation_errors_if_email_already_exists() + { + $adminRole = Role::getSystemRole('admin'); + $adminRole->users()->delete(); + $editor = $this->users->editor(); + + $this->artisan('bookstack:create-admin', [ + '--email' => $editor->email, + '--name' => 'My initial admin', + '--password' => 'testing-7', + '--initial' => true, + ])->expectsOutput("Could not create admin account.") + ->expectsOutput("An account with the email address \"{$editor->email}\" already exists.") + ->assertExitCode(1); + } + + public function test_initial_option_updating_errors_if_email_already_exists() + { + $editor = $this->users->editor(); + $defaultAdmin = User::query()->where('email', '=', 'admin@admin.com')->first(); + $this->assertNotNull($defaultAdmin); + + $this->artisan('bookstack:create-admin', [ + '--email' => $editor->email, + '--name' => 'My initial admin', + '--password' => 'testing-7', + '--initial' => true, + ])->expectsOutput("Could not create admin account.") + ->expectsOutput("An account with the email address \"{$editor->email}\" already exists.") + ->assertExitCode(1); + } + + public function test_initial_option_does_not_require_name_or_email_to_be_passed() + { + $adminRole = Role::getSystemRole('admin'); + $adminRole->users()->delete(); + $this->assertEquals(0, $adminRole->users()->count()); + + $this->artisan('bookstack:create-admin', [ + '--generate-password' => true, + '--initial' => true, + ])->assertExitCode(0); + + $this->assertEquals(1, $adminRole->users()->count()); + $this->assertDatabaseHas('users', [ + 'email' => 'admin@example.com', + 'name' => 'Admin', + ]); + } + + public function test_initial_option_updating_existing_user_with_generate_password_only_outputs_password() + { + $defaultAdmin = User::query()->where('email', '=', 'admin@admin.com')->first(); + + $this->withoutMockingConsoleOutput() + ->artisan('bookstack:create-admin', [ + '--email' => 'firstadmin@example.com', + '--name' => 'Admin Test', + '--generate-password' => true, + '--initial' => true, + ]); + + $output = Artisan::output(); + $this->assertMatchesRegularExpression('/^[a-zA-Z0-9]{32}$/', $output); + + $defaultAdmin->refresh(); + $this->assertEquals('firstadmin@example.com', $defaultAdmin->email); + } } diff --git a/tests/Commands/RegenerateCommentContentCommandTest.php b/tests/Commands/RegenerateCommentContentCommandTest.php deleted file mode 100644 index 4940d66c343..00000000000 --- a/tests/Commands/RegenerateCommentContentCommandTest.php +++ /dev/null @@ -1,31 +0,0 @@ -forceCreate([ - 'html' => 'some_old_content', - 'text' => 'some_fresh_content', - ]); - - $this->assertDatabaseHas('comments', [ - 'html' => 'some_old_content', - ]); - - $exitCode = \Artisan::call('bookstack:regenerate-comment-content'); - $this->assertTrue($exitCode === 0, 'Command executed successfully'); - - $this->assertDatabaseMissing('comments', [ - 'html' => 'some_old_content', - ]); - $this->assertDatabaseHas('comments', [ - 'html' => "

    some_fresh_content

    \n", - ]); - } -} diff --git a/tests/Commands/UpdateUrlCommandTest.php b/tests/Commands/UpdateUrlCommandTest.php index 62c39c2741f..d336e05a240 100644 --- a/tests/Commands/UpdateUrlCommandTest.php +++ b/tests/Commands/UpdateUrlCommandTest.php @@ -87,6 +87,27 @@ public function test_command_updates_array_settings() $this->assertEquals('a https://cats.example.com/donkey/cat url', $settingVal[0]['name']); } + public function test_command_updates_page_revisions() + { + $page = $this->entities->page(); + + for ($i = 0; $i < 2; $i++) { + $this->entities->updatePage($page, [ + 'name' => $page->name, + 'markdown' => "[A link {$i}](https://example.com/donkey/cat)" + ]); + } + + $this->runUpdate('https://example.com', 'https://cats.example.com'); + setting()->flushCache(); + + $this->assertDatabaseHas('page_revisions', [ + 'page_id' => $page->id, + 'markdown' => '[A link 1](https://cats.example.com/donkey/cat)', + 'html' => '' . "\n" + ]); + } + protected function runUpdate(string $oldUrl, string $newUrl) { $this->artisan("bookstack:update-url {$oldUrl} {$newUrl}") diff --git a/tests/CreatesApplication.php b/tests/CreatesApplication.php index b1cefbb6584..3258d05bc92 100644 --- a/tests/CreatesApplication.php +++ b/tests/CreatesApplication.php @@ -3,15 +3,14 @@ namespace Tests; use Illuminate\Contracts\Console\Kernel; +use Illuminate\Foundation\Application; trait CreatesApplication { /** * Creates the application. - * - * @return \Illuminate\Foundation\Application */ - public function createApplication() + public function createApplication(): Application { $app = require __DIR__ . '/../bootstrap/app.php'; $app->make(Kernel::class)->bootstrap(); diff --git a/tests/Entity/BookDefaultTemplateTest.php b/tests/Entity/BookDefaultTemplateTest.php deleted file mode 100644 index d4cd5b2c3a9..00000000000 --- a/tests/Entity/BookDefaultTemplateTest.php +++ /dev/null @@ -1,185 +0,0 @@ -entities->templatePage(); - $details = [ - 'name' => 'My book with default template', - 'default_template_id' => $templatePage->id, - ]; - - $this->asEditor()->post('/books', $details); - $this->assertDatabaseHas('books', $details); - } - - public function test_updating_book_with_default_template() - { - $book = $this->entities->book(); - $templatePage = $this->entities->templatePage(); - - $this->asEditor()->put("/books/{$book->slug}", ['name' => $book->name, 'default_template_id' => strval($templatePage->id)]); - $this->assertDatabaseHas('books', ['id' => $book->id, 'default_template_id' => $templatePage->id]); - - $this->asEditor()->put("/books/{$book->slug}", ['name' => $book->name, 'default_template_id' => '']); - $this->assertDatabaseHas('books', ['id' => $book->id, 'default_template_id' => null]); - } - - public function test_default_template_cannot_be_set_if_not_a_template() - { - $book = $this->entities->book(); - $page = $this->entities->page(); - $this->assertFalse($page->template); - - $this->asEditor()->put("/books/{$book->slug}", ['name' => $book->name, 'default_template_id' => $page->id]); - $this->assertDatabaseHas('books', ['id' => $book->id, 'default_template_id' => null]); - } - - public function test_default_template_cannot_be_set_if_not_have_access() - { - $book = $this->entities->book(); - $templatePage = $this->entities->templatePage(); - $this->permissions->disableEntityInheritedPermissions($templatePage); - - $this->asEditor()->put("/books/{$book->slug}", ['name' => $book->name, 'default_template_id' => $templatePage->id]); - $this->assertDatabaseHas('books', ['id' => $book->id, 'default_template_id' => null]); - } - - public function test_inaccessible_default_template_can_be_set_if_unchanged() - { - $templatePage = $this->entities->templatePage(); - $book = $this->bookUsingDefaultTemplate($templatePage); - $this->permissions->disableEntityInheritedPermissions($templatePage); - - $this->asEditor()->put("/books/{$book->slug}", ['name' => $book->name, 'default_template_id' => $templatePage->id]); - $this->assertDatabaseHas('books', ['id' => $book->id, 'default_template_id' => $templatePage->id]); - } - - public function test_default_page_template_option_shows_on_book_form() - { - $templatePage = $this->entities->templatePage(); - $book = $this->bookUsingDefaultTemplate($templatePage); - - $resp = $this->asEditor()->get($book->getUrl('/edit')); - $this->withHtml($resp)->assertElementExists('input[name="default_template_id"][value="' . $templatePage->id . '"]'); - } - - public function test_default_page_template_option_only_shows_template_name_if_visible() - { - $templatePage = $this->entities->templatePage(); - $book = $this->bookUsingDefaultTemplate($templatePage); - - $resp = $this->asEditor()->get($book->getUrl('/edit')); - $this->withHtml($resp)->assertElementContains('#template-control a.text-page', "#{$templatePage->id}, {$templatePage->name}"); - - $this->permissions->disableEntityInheritedPermissions($templatePage); - - $resp = $this->asEditor()->get($book->getUrl('/edit')); - $this->withHtml($resp)->assertElementNotContains('#template-control a.text-page', "#{$templatePage->id}, {$templatePage->name}"); - $this->withHtml($resp)->assertElementContains('#template-control a.text-page', "#{$templatePage->id}"); - } - - public function test_creating_book_page_uses_default_template() - { - $templatePage = $this->entities->templatePage(); - $templatePage->forceFill(['html' => '

    My template page

    ', 'markdown' => '# My template page'])->save(); - $book = $this->bookUsingDefaultTemplate($templatePage); - - $this->asEditor()->get($book->getUrl('/create-page')); - $latestPage = $book->pages() - ->where('draft', '=', true) - ->where('template', '=', false) - ->latest()->first(); - - $this->assertEquals('

    My template page

    ', $latestPage->html); - $this->assertEquals('# My template page', $latestPage->markdown); - } - - public function test_creating_chapter_page_uses_default_template() - { - $templatePage = $this->entities->templatePage(); - $templatePage->forceFill(['html' => '

    My template page in chapter

    ', 'markdown' => '# My template page in chapter'])->save(); - $book = $this->bookUsingDefaultTemplate($templatePage); - $chapter = $book->chapters()->first(); - - $this->asEditor()->get($chapter->getUrl('/create-page')); - $latestPage = $chapter->pages() - ->where('draft', '=', true) - ->where('template', '=', false) - ->latest()->first(); - - $this->assertEquals('

    My template page in chapter

    ', $latestPage->html); - $this->assertEquals('# My template page in chapter', $latestPage->markdown); - } - - public function test_creating_book_page_as_guest_uses_default_template() - { - $templatePage = $this->entities->templatePage(); - $templatePage->forceFill(['html' => '

    My template page

    ', 'markdown' => '# My template page'])->save(); - $book = $this->bookUsingDefaultTemplate($templatePage); - $guest = $this->users->guest(); - - $this->permissions->makeAppPublic(); - $this->permissions->grantUserRolePermissions($guest, ['page-create-all', 'page-update-all']); - - $resp = $this->post($book->getUrl('/create-guest-page'), [ - 'name' => 'My guest page with template' - ]); - $latestPage = $book->pages() - ->where('draft', '=', false) - ->where('template', '=', false) - ->where('created_by', '=', $guest->id) - ->latest()->first(); - - $this->assertEquals('

    My template page

    ', $latestPage->html); - $this->assertEquals('# My template page', $latestPage->markdown); - } - - public function test_creating_book_page_does_not_use_template_if_not_visible() - { - $templatePage = $this->entities->templatePage(); - $templatePage->forceFill(['html' => '

    My template page

    ', 'markdown' => '# My template page'])->save(); - $book = $this->bookUsingDefaultTemplate($templatePage); - $this->permissions->disableEntityInheritedPermissions($templatePage); - - $this->asEditor()->get($book->getUrl('/create-page')); - $latestPage = $book->pages() - ->where('draft', '=', true) - ->where('template', '=', false) - ->latest()->first(); - - $this->assertEquals('', $latestPage->html); - $this->assertEquals('', $latestPage->markdown); - } - - public function test_template_page_delete_removes_book_template_usage() - { - $templatePage = $this->entities->templatePage(); - $book = $this->bookUsingDefaultTemplate($templatePage); - - $book->refresh(); - $this->assertEquals($templatePage->id, $book->default_template_id); - - $this->asEditor()->delete($templatePage->getUrl()); - $this->asAdmin()->post('/settings/recycle-bin/empty'); - - $book->refresh(); - $this->assertEquals(null, $book->default_template_id); - } - - protected function bookUsingDefaultTemplate(Page $page): Book - { - $book = $this->entities->book(); - $book->default_template_id = $page->id; - $book->save(); - - return $book; - } -} diff --git a/tests/Entity/BookShelfTest.php b/tests/Entity/BookShelfTest.php index fb9862931ae..ad1d64e7126 100644 --- a/tests/Entity/BookShelfTest.php +++ b/tests/Entity/BookShelfTest.php @@ -259,6 +259,35 @@ public function test_shelf_edit() $this->assertDatabaseHas('bookshelves_books', ['bookshelf_id' => $shelf->id, 'book_id' => $booksToInclude[1]->id]); } + public function test_shelf_edit_does_not_alter_books_we_dont_have_access_to() + { + $shelf = $this->entities->shelf(); + $shelf->books()->detach(); + $this->entities->book(); + $this->entities->book(); + + $newBooks = [$this->entities->book(), $this->entities->book()]; + $originalBooks = [$this->entities->book(), $this->entities->book()]; + foreach ($originalBooks as $book) { + $this->permissions->disableEntityInheritedPermissions($book); + $shelf->books()->attach($book->id); + } + + $this->asEditor()->put($shelf->getUrl(), [ + 'name' => $shelf->name, + 'books' => "{$newBooks[0]->id},{$newBooks[1]->id}", + ])->assertRedirect($shelf->getUrl()); + + $resultingBooksById = $shelf->books()->get()->keyBy('id')->toArray(); + $this->assertCount(4, $resultingBooksById); + foreach ($newBooks as $book) { + $this->assertArrayHasKey($book->id, $resultingBooksById); + } + foreach ($originalBooks as $book) { + $this->assertArrayHasKey($book->id, $resultingBooksById); + } + } + public function test_shelf_create_new_book() { $shelf = $this->entities->shelf(); diff --git a/tests/Entity/BookTest.php b/tests/Entity/BookTest.php index 3740892460b..51bf65d10bb 100644 --- a/tests/Entity/BookTest.php +++ b/tests/Entity/BookTest.php @@ -266,8 +266,8 @@ public function test_description_limited_to_specific_html() { $book = $this->entities->book(); - $input = '

    Test

    Contenta

    Hello

    '; - $expected = '

    Contenta

    '; + $input = '

    Test

    Contenta

    Hello

    '; + $expected = '

    Contenta

    '; $this->asEditor()->put($book->getUrl(), [ 'name' => $book->name, @@ -317,7 +317,7 @@ public function test_copy() $copy = Book::query()->where('name', '=', 'My copy book')->first(); $resp->assertRedirect($copy->getUrl()); - $this->assertEquals($book->getDirectChildren()->count(), $copy->getDirectChildren()->count()); + $this->assertEquals($book->getDirectVisibleChildren()->count(), $copy->getDirectVisibleChildren()->count()); $this->get($copy->getUrl())->assertSee($book->description_html, false); } @@ -329,7 +329,7 @@ public function test_copy_does_not_copy_non_visible_content() // Hide child content /** @var BookChild $page */ - foreach ($book->getDirectChildren() as $child) { + foreach ($book->getDirectVisibleChildren() as $child) { $this->permissions->setEntityPermissions($child, [], []); } @@ -337,7 +337,7 @@ public function test_copy_does_not_copy_non_visible_content() /** @var Book $copy */ $copy = Book::query()->where('name', '=', 'My copy book')->first(); - $this->assertEquals(0, $copy->getDirectChildren()->count()); + $this->assertEquals(0, $copy->getDirectVisibleChildren()->count()); } public function test_copy_does_not_copy_pages_or_chapters_if_user_cant_create() diff --git a/tests/Entity/CommentDisplayTest.php b/tests/Entity/CommentDisplayTest.php new file mode 100644 index 00000000000..bffe29fa95b --- /dev/null +++ b/tests/Entity/CommentDisplayTest.php @@ -0,0 +1,131 @@ +asAdmin(); + $page = $this->entities->page(); + + $this->postJson("/comment/$page->id", ['html' => '

    My new comment

    ']); + $this->postJson("/comment/$page->id", ['html' => '

    My new comment

    ']); + + $respHtml = $this->withHtml($this->get($page->getUrl())); + $respHtml->assertElementCount('.comment-branch', 3); + $respHtml->assertElementNotExists('.comment-branch .comment-branch'); + + $comment = $page->comments()->first(); + $resp = $this->postJson("/comment/$page->id", [ + 'html' => '

    My nested comment

    ', 'parent_id' => $comment->local_id + ]); + $resp->assertStatus(200); + + $respHtml = $this->withHtml($this->get($page->getUrl())); + $respHtml->assertElementCount('.comment-branch', 4); + $respHtml->assertElementContains('.comment-branch .comment-branch', 'My nested comment'); + } + + public function test_comments_are_visible_in_the_page_editor() + { + $page = $this->entities->page(); + + $this->asAdmin()->postJson("/comment/$page->id", ['html' => '

    My great comment to see in the editor

    ']); + + $respHtml = $this->withHtml($this->get($page->getUrl('/edit'))); + $respHtml->assertElementContains('.comment-box .content', 'My great comment to see in the editor'); + } + + public function test_comment_creator_name_truncated() + { + [$longNamedUser] = $this->users->newUserWithRole(['name' => 'Wolfeschlegelsteinhausenbergerdorff'], ['comment-create-all', 'page-view-all']); + $page = $this->entities->page(); + + $comment = Comment::factory()->make(); + $this->actingAs($longNamedUser)->postJson("/comment/$page->id", $comment->getAttributes()); + + $pageResp = $this->asAdmin()->get($page->getUrl()); + $pageResp->assertSee('Wolfeschlegels…'); + } + + public function test_comment_editor_js_loaded_with_create_or_edit_permissions() + { + $editor = $this->users->editor(); + $page = $this->entities->page(); + + $resp = $this->actingAs($editor)->get($page->getUrl()); + $resp->assertSee('window.editor_translations', false); + $resp->assertSee('component="entity-selector"', false); + + $this->permissions->removeUserRolePermissions($editor, ['comment-create-all']); + $this->permissions->grantUserRolePermissions($editor, ['comment-update-own']); + + $resp = $this->actingAs($editor)->get($page->getUrl()); + $resp->assertDontSee('window.editor_translations', false); + $resp->assertDontSee('component="entity-selector"', false); + + Comment::factory()->create([ + 'created_by' => $editor->id, + 'entity_type' => 'page', + 'entity_id' => $page->id, + ]); + + $resp = $this->actingAs($editor)->get($page->getUrl()); + $resp->assertSee('window.editor_translations', false); + $resp->assertSee('component="entity-selector"', false); + } + + public function test_comment_displays_relative_times() + { + $page = $this->entities->page(); + $comment = Comment::factory()->create(['entity_id' => $page->id, 'entity_type' => $page->getMorphClass()]); + $comment->created_at = now()->subWeek(); + $comment->updated_at = now()->subDay(); + $comment->save(); + + $pageResp = $this->asAdmin()->get($page->getUrl()); + $html = $this->withHtml($pageResp); + + // Create date shows relative time as text to user + $html->assertElementContains('.comment-box', 'commented 1 week ago'); + // Updated indicator has full time as title + $html->assertElementContains('.comment-box span[title^="Updated ' . $comment->updated_at->format('Y-m-d') . '"]', 'Updated'); + } + + public function test_comment_displays_reference_if_set() + { + $page = $this->entities->page(); + $comment = Comment::factory()->make([ + 'content_ref' => 'bkmrk-a:abc:4-1', + 'local_id' => 10, + ]); + $page->comments()->save($comment); + + $html = $this->withHtml($this->asEditor()->get($page->getUrl())); + $html->assertElementExists('#comment10 .comment-reference-indicator-wrap a'); + } + + public function test_archived_comments_are_shown_in_their_own_container() + { + $page = $this->entities->page(); + $comment = Comment::factory()->make(['local_id' => 44]); + $page->comments()->save($comment); + + $html = $this->withHtml($this->asEditor()->get($page->getUrl())); + $html->assertElementExists('#comment-tab-panel-active #comment44'); + $html->assertElementNotExists('#comment-tab-panel-archived .comment-box'); + + $comment->archived = true; + $comment->save(); + + $html = $this->withHtml($this->asEditor()->get($page->getUrl())); + $html->assertElementExists('#comment-tab-panel-archived #comment44.comment-box'); + $html->assertElementNotExists('#comment-tab-panel-active #comment44'); + } +} diff --git a/tests/Entity/CommentStoreTest.php b/tests/Entity/CommentStoreTest.php new file mode 100644 index 00000000000..de093a3a6e9 --- /dev/null +++ b/tests/Entity/CommentStoreTest.php @@ -0,0 +1,275 @@ +asAdmin(); + $page = $this->entities->page(); + + $comment = Comment::factory()->make(['parent_id' => 2]); + $resp = $this->postJson("/comment/$page->id", $comment->getAttributes()); + + $resp->assertStatus(200); + $resp->assertSee($comment->html, false); + + $pageResp = $this->get($page->getUrl()); + $pageResp->assertSee($comment->html, false); + + $this->assertDatabaseHas('comments', [ + 'local_id' => 1, + 'entity_id' => $page->id, + 'entity_type' => Page::newModelInstance()->getMorphClass(), + 'parent_id' => 2, + ]); + + $this->assertActivityExists(ActivityType::COMMENT_CREATE); + } + public function test_add_comment_stores_content_reference_only_if_format_valid() + { + $validityByRefs = [ + 'bkmrk-my-title:4589284922:4-3' => true, + 'bkmrk-my-title:4589284922:' => true, + 'bkmrk-my-title:4589284922:abc' => false, + 'my-title:4589284922:' => false, + 'bkmrk-my-title-4589284922:' => false, + ]; + + $page = $this->entities->page(); + + foreach ($validityByRefs as $ref => $valid) { + $this->asAdmin()->postJson("/comment/$page->id", [ + 'html' => '

    My comment

    ', + 'parent_id' => null, + 'content_ref' => $ref, + ]); + + if ($valid) { + $this->assertDatabaseHas('comments', ['entity_id' => $page->id, 'content_ref' => $ref]); + } else { + $this->assertDatabaseMissing('comments', ['entity_id' => $page->id, 'content_ref' => $ref]); + } + } + } + + public function test_comment_edit() + { + $this->asAdmin(); + $page = $this->entities->page(); + + $comment = Comment::factory()->make(); + $this->postJson("/comment/$page->id", $comment->getAttributes()); + + $comment = $page->comments()->first(); + $newHtml = '

    updated text content

    '; + $resp = $this->putJson("/comment/$comment->id", [ + 'html' => $newHtml, + ]); + + $resp->assertStatus(200); + $resp->assertSee($newHtml, false); + $resp->assertDontSee($comment->html, false); + + $this->assertDatabaseHas('comments', [ + 'html' => $newHtml, + 'entity_id' => $page->id, + ]); + + $this->assertActivityExists(ActivityType::COMMENT_UPDATE); + } + + public function test_comment_delete() + { + $this->asAdmin(); + $page = $this->entities->page(); + + $comment = Comment::factory()->make(); + $this->postJson("/comment/$page->id", $comment->getAttributes()); + + $comment = $page->comments()->first(); + + $resp = $this->delete("/comment/$comment->id"); + $resp->assertStatus(200); + + $this->assertDatabaseMissing('comments', [ + 'id' => $comment->id, + ]); + + $this->assertActivityExists(ActivityType::COMMENT_DELETE); + } + + public function test_comment_archive_and_unarchive() + { + $this->asAdmin(); + $page = $this->entities->page(); + + $comment = Comment::factory()->make(); + $page->comments()->save($comment); + $comment->refresh(); + + $this->put("/comment/$comment->id/archive"); + + $this->assertDatabaseHas('comments', [ + 'id' => $comment->id, + 'archived' => true, + ]); + + $this->assertActivityExists(ActivityType::COMMENT_UPDATE); + + $this->put("/comment/$comment->id/unarchive"); + + $this->assertDatabaseHas('comments', [ + 'id' => $comment->id, + 'archived' => false, + ]); + + $this->assertActivityExists(ActivityType::COMMENT_UPDATE); + } + + public function test_archive_endpoints_require_delete_or_edit_permissions() + { + $viewer = $this->users->viewer(); + $page = $this->entities->page(); + + $comment = Comment::factory()->make(); + $page->comments()->save($comment); + $comment->refresh(); + + $endpoints = ["/comment/$comment->id/archive", "/comment/$comment->id/unarchive"]; + + foreach ($endpoints as $endpoint) { + $resp = $this->actingAs($viewer)->put($endpoint); + $this->assertPermissionError($resp); + } + + $this->permissions->grantUserRolePermissions($viewer, ['comment-delete-all']); + + foreach ($endpoints as $endpoint) { + $resp = $this->actingAs($viewer)->put($endpoint); + $resp->assertOk(); + } + + $this->permissions->removeUserRolePermissions($viewer, ['comment-delete-all']); + $this->permissions->grantUserRolePermissions($viewer, ['comment-update-all']); + + foreach ($endpoints as $endpoint) { + $resp = $this->actingAs($viewer)->put($endpoint); + $resp->assertOk(); + } + } + + public function test_non_top_level_comments_cant_be_archived_or_unarchived() + { + $this->asAdmin(); + $page = $this->entities->page(); + + $comment = Comment::factory()->make(); + $page->comments()->save($comment); + $subComment = Comment::factory()->make(['parent_id' => $comment->id]); + $page->comments()->save($subComment); + $subComment->refresh(); + + $resp = $this->putJson("/comment/$subComment->id/archive"); + $resp->assertStatus(400); + + $this->assertDatabaseHas('comments', [ + 'id' => $subComment->id, + 'archived' => false, + ]); + + $resp = $this->putJson("/comment/$subComment->id/unarchive"); + $resp->assertStatus(400); + } + + public function test_scripts_cannot_be_injected_via_comment_html() + { + $page = $this->entities->page(); + + $script = '

    My lovely comment

    '; + $this->asAdmin()->postJson("/comment/$page->id", [ + 'html' => $script, + ]); + + $pageView = $this->get($page->getUrl()); + $pageView->assertDontSee($script, false); + $pageView->assertDontSee('sneakyscript', false); + $pageView->assertSee('

    My lovely comment

    ', false); + + $comment = $page->comments()->first(); + $this->putJson("/comment/$comment->id", [ + 'html' => $script . '

    updated

    ', + ]); + + $pageView = $this->get($page->getUrl()); + $pageView->assertDontSee($script, false); + $pageView->assertDontSee('sneakyscript', false); + $pageView->assertSee('

    My lovely comment

    updated

    '); + } + + public function test_scripts_are_removed_even_if_already_in_db() + { + $page = $this->entities->page(); + Comment::factory()->create([ + 'html' => '

    scriptincommentest

    ', + 'entity_type' => 'page', 'entity_id' => $page + ]); + + $resp = $this->asAdmin()->get($page->getUrl()); + $resp->assertSee('scriptincommentest', false); + $resp->assertDontSee('superbadscript', false); + $resp->assertDontSee('superbadonclick', false); + } + + public function test_comment_html_is_limited() + { + $page = $this->entities->page(); + $input = '

    Test

    Contenta

    Hello
    there

    '; + $expected = '

    Contenta

    '; + + $resp = $this->asAdmin()->post("/comment/{$page->id}", ['html' => $input]); + $resp->assertOk(); + $this->assertDatabaseHas('comments', [ + 'entity_type' => 'page', + 'entity_id' => $page->id, + 'html' => $expected, + ]); + + $comment = $page->comments()->first(); + $resp = $this->put("/comment/{$comment->id}", ['html' => $input]); + $resp->assertOk(); + $this->assertDatabaseHas('comments', [ + 'id' => $comment->id, + 'html' => $expected, + ]); + } + + public function test_comment_html_spans_are_cleaned() + { + $page = $this->entities->page(); + $input = '

    Hello do you have biscuits?

    '; + $expected = '

    Hello do you have biscuits?

    '; + + $resp = $this->asAdmin()->post("/comment/{$page->id}", ['html' => $input]); + $resp->assertOk(); + $this->assertDatabaseHas('comments', [ + 'entity_type' => 'page', + 'entity_id' => $page->id, + 'html' => $expected, + ]); + + $comment = $page->comments()->first(); + $resp = $this->put("/comment/{$comment->id}", ['html' => $input]); + $resp->assertOk(); + $this->assertDatabaseHas('comments', [ + 'id' => $comment->id, + 'html' => $expected, + ]); + } +} diff --git a/tests/Entity/CommentTest.php b/tests/Entity/CommentTest.php deleted file mode 100644 index 23fc6819741..00000000000 --- a/tests/Entity/CommentTest.php +++ /dev/null @@ -1,167 +0,0 @@ -asAdmin(); - $page = $this->entities->page(); - - $comment = Comment::factory()->make(['parent_id' => 2]); - $resp = $this->postJson("/comment/$page->id", $comment->getAttributes()); - - $resp->assertStatus(200); - $resp->assertSee($comment->text); - - $pageResp = $this->get($page->getUrl()); - $pageResp->assertSee($comment->text); - - $this->assertDatabaseHas('comments', [ - 'local_id' => 1, - 'entity_id' => $page->id, - 'entity_type' => Page::newModelInstance()->getMorphClass(), - 'text' => $comment->text, - 'parent_id' => 2, - ]); - - $this->assertActivityExists(ActivityType::COMMENT_CREATE); - } - - public function test_comment_edit() - { - $this->asAdmin(); - $page = $this->entities->page(); - - $comment = Comment::factory()->make(); - $this->postJson("/comment/$page->id", $comment->getAttributes()); - - $comment = $page->comments()->first(); - $newText = 'updated text content'; - $resp = $this->putJson("/comment/$comment->id", [ - 'text' => $newText, - ]); - - $resp->assertStatus(200); - $resp->assertSee($newText); - $resp->assertDontSee($comment->text); - - $this->assertDatabaseHas('comments', [ - 'text' => $newText, - 'entity_id' => $page->id, - ]); - - $this->assertActivityExists(ActivityType::COMMENT_UPDATE); - } - - public function test_comment_delete() - { - $this->asAdmin(); - $page = $this->entities->page(); - - $comment = Comment::factory()->make(); - $this->postJson("/comment/$page->id", $comment->getAttributes()); - - $comment = $page->comments()->first(); - - $resp = $this->delete("/comment/$comment->id"); - $resp->assertStatus(200); - - $this->assertDatabaseMissing('comments', [ - 'id' => $comment->id, - ]); - - $this->assertActivityExists(ActivityType::COMMENT_DELETE); - } - - public function test_comments_converts_markdown_input_to_html() - { - $page = $this->entities->page(); - $this->asAdmin()->postJson("/comment/$page->id", [ - 'text' => '# My Title', - ]); - - $this->assertDatabaseHas('comments', [ - 'entity_id' => $page->id, - 'entity_type' => $page->getMorphClass(), - 'text' => '# My Title', - 'html' => "

    My Title

    \n", - ]); - - $pageView = $this->get($page->getUrl()); - $pageView->assertSee('

    My Title

    ', false); - } - - public function test_html_cannot_be_injected_via_comment_content() - { - $this->asAdmin(); - $page = $this->entities->page(); - - $script = '\n\n# sometextinthecomment'; - $this->postJson("/comment/$page->id", [ - 'text' => $script, - ]); - - $pageView = $this->get($page->getUrl()); - $pageView->assertDontSee($script, false); - $pageView->assertSee('sometextinthecomment'); - - $comment = $page->comments()->first(); - $this->putJson("/comment/$comment->id", [ - 'text' => $script . 'updated', - ]); - - $pageView = $this->get($page->getUrl()); - $pageView->assertDontSee($script, false); - $pageView->assertSee('sometextinthecommentupdated'); - } - - public function test_reply_comments_are_nested() - { - $this->asAdmin(); - $page = $this->entities->page(); - - $this->postJson("/comment/$page->id", ['text' => 'My new comment']); - $this->postJson("/comment/$page->id", ['text' => 'My new comment']); - - $respHtml = $this->withHtml($this->get($page->getUrl())); - $respHtml->assertElementCount('.comment-branch', 3); - $respHtml->assertElementNotExists('.comment-branch .comment-branch'); - - $comment = $page->comments()->first(); - $resp = $this->postJson("/comment/$page->id", ['text' => 'My nested comment', 'parent_id' => $comment->local_id]); - $resp->assertStatus(200); - - $respHtml = $this->withHtml($this->get($page->getUrl())); - $respHtml->assertElementCount('.comment-branch', 4); - $respHtml->assertElementContains('.comment-branch .comment-branch', 'My nested comment'); - } - - public function test_comments_are_visible_in_the_page_editor() - { - $page = $this->entities->page(); - - $this->asAdmin()->postJson("/comment/$page->id", ['text' => 'My great comment to see in the editor']); - - $respHtml = $this->withHtml($this->get($page->getUrl('/edit'))); - $respHtml->assertElementContains('.comment-box .content', 'My great comment to see in the editor'); - } - - public function test_comment_creator_name_truncated() - { - [$longNamedUser] = $this->users->newUserWithRole(['name' => 'Wolfeschlegelsteinhausenbergerdorff'], ['comment-create-all', 'page-view-all']); - $page = $this->entities->page(); - - $comment = Comment::factory()->make(); - $this->actingAs($longNamedUser)->postJson("/comment/$page->id", $comment->getAttributes()); - - $pageResp = $this->asAdmin()->get($page->getUrl()); - $pageResp->assertSee('Wolfeschlegels…'); - } -} diff --git a/tests/Entity/DefaultTemplateTest.php b/tests/Entity/DefaultTemplateTest.php new file mode 100644 index 00000000000..5369a5430bc --- /dev/null +++ b/tests/Entity/DefaultTemplateTest.php @@ -0,0 +1,341 @@ +entities->templatePage(); + $details = [ + 'name' => 'My book with default template', + 'default_template_id' => $templatePage->id, + ]; + + $this->asEditor()->post('/books', $details); + $this->assertDatabaseHas('books', $details); + } + + public function test_creating_chapter_with_default_template() + { + $templatePage = $this->entities->templatePage(); + $book = $this->entities->book(); + $details = [ + 'name' => 'My chapter with default template', + 'default_template_id' => $templatePage->id, + ]; + + $this->asEditor()->post($book->getUrl('/create-chapter'), $details); + $this->assertDatabaseHas('chapters', $details); + } + + public function test_updating_book_with_default_template() + { + $book = $this->entities->book(); + $templatePage = $this->entities->templatePage(); + + $this->asEditor()->put($book->getUrl(), ['name' => $book->name, 'default_template_id' => strval($templatePage->id)]); + $this->assertDatabaseHas('books', ['id' => $book->id, 'default_template_id' => $templatePage->id]); + + $this->asEditor()->put($book->getUrl(), ['name' => $book->name, 'default_template_id' => '']); + $this->assertDatabaseHas('books', ['id' => $book->id, 'default_template_id' => null]); + } + + public function test_updating_chapter_with_default_template() + { + $chapter = $this->entities->chapter(); + $templatePage = $this->entities->templatePage(); + + $this->asEditor()->put($chapter->getUrl(), ['name' => $chapter->name, 'default_template_id' => strval($templatePage->id)]); + $this->assertDatabaseHas('chapters', ['id' => $chapter->id, 'default_template_id' => $templatePage->id]); + + $this->asEditor()->put($chapter->getUrl(), ['name' => $chapter->name, 'default_template_id' => '']); + $this->assertDatabaseHas('chapters', ['id' => $chapter->id, 'default_template_id' => null]); + } + + public function test_default_book_template_cannot_be_set_if_not_a_template() + { + $book = $this->entities->book(); + $page = $this->entities->page(); + $this->assertFalse($page->template); + + $this->asEditor()->put("/books/{$book->slug}", ['name' => $book->name, 'default_template_id' => $page->id]); + $this->assertDatabaseHas('books', ['id' => $book->id, 'default_template_id' => null]); + } + + public function test_default_chapter_template_cannot_be_set_if_not_a_template() + { + $chapter = $this->entities->chapter(); + $page = $this->entities->page(); + $this->assertFalse($page->template); + + $this->asEditor()->put("/chapters/{$chapter->slug}", ['name' => $chapter->name, 'default_template_id' => $page->id]); + $this->assertDatabaseHas('chapters', ['id' => $chapter->id, 'default_template_id' => null]); + } + + + public function test_default_book_template_cannot_be_set_if_not_have_access() + { + $book = $this->entities->book(); + $templatePage = $this->entities->templatePage(); + $this->permissions->disableEntityInheritedPermissions($templatePage); + + $this->asEditor()->put("/books/{$book->slug}", ['name' => $book->name, 'default_template_id' => $templatePage->id]); + $this->assertDatabaseHas('books', ['id' => $book->id, 'default_template_id' => null]); + } + + public function test_default_chapter_template_cannot_be_set_if_not_have_access() + { + $chapter = $this->entities->chapter(); + $templatePage = $this->entities->templatePage(); + $this->permissions->disableEntityInheritedPermissions($templatePage); + + $this->asEditor()->put("/chapters/{$chapter->slug}", ['name' => $chapter->name, 'default_template_id' => $templatePage->id]); + $this->assertDatabaseHas('chapters', ['id' => $chapter->id, 'default_template_id' => null]); + } + + public function test_inaccessible_book_default_template_can_be_set_if_unchanged() + { + $templatePage = $this->entities->templatePage(); + $book = $this->bookUsingDefaultTemplate($templatePage); + $this->permissions->disableEntityInheritedPermissions($templatePage); + + $this->asEditor()->put("/books/{$book->slug}", ['name' => $book->name, 'default_template_id' => $templatePage->id]); + $this->assertDatabaseHas('books', ['id' => $book->id, 'default_template_id' => $templatePage->id]); + } + + public function test_inaccessible_chapter_default_template_can_be_set_if_unchanged() + { + $templatePage = $this->entities->templatePage(); + $chapter = $this->chapterUsingDefaultTemplate($templatePage); + $this->permissions->disableEntityInheritedPermissions($templatePage); + + $this->asEditor()->put("/chapters/{$chapter->slug}", ['name' => $chapter->name, 'default_template_id' => $templatePage->id]); + $this->assertDatabaseHas('chapters', ['id' => $chapter->id, 'default_template_id' => $templatePage->id]); + } + + public function test_default_page_template_option_shows_on_book_form() + { + $templatePage = $this->entities->templatePage(); + $book = $this->bookUsingDefaultTemplate($templatePage); + + $resp = $this->asEditor()->get($book->getUrl('/edit')); + $this->withHtml($resp)->assertElementExists('input[name="default_template_id"][value="' . $templatePage->id . '"]'); + } + + public function test_default_page_template_option_shows_on_chapter_form() + { + $templatePage = $this->entities->templatePage(); + $chapter = $this->chapterUsingDefaultTemplate($templatePage); + + $resp = $this->asEditor()->get($chapter->getUrl('/edit')); + $this->withHtml($resp)->assertElementExists('input[name="default_template_id"][value="' . $templatePage->id . '"]'); + } + + public function test_book_default_page_template_option_only_shows_template_name_if_visible() + { + $templatePage = $this->entities->templatePage(); + $book = $this->bookUsingDefaultTemplate($templatePage); + + $resp = $this->asEditor()->get($book->getUrl('/edit')); + $this->withHtml($resp)->assertElementContains('#template-control a.text-page', "#{$templatePage->id}, {$templatePage->name}"); + + $this->permissions->disableEntityInheritedPermissions($templatePage); + + $resp = $this->asEditor()->get($book->getUrl('/edit')); + $this->withHtml($resp)->assertElementNotContains('#template-control a.text-page', "#{$templatePage->id}, {$templatePage->name}"); + $this->withHtml($resp)->assertElementContains('#template-control a.text-page', "#{$templatePage->id}"); + } + + public function test_chapter_default_page_template_option_only_shows_template_name_if_visible() + { + $templatePage = $this->entities->templatePage(); + $chapter = $this->chapterUsingDefaultTemplate($templatePage); + + $resp = $this->asEditor()->get($chapter->getUrl('/edit')); + $this->withHtml($resp)->assertElementContains('#template-control a.text-page', "#{$templatePage->id}, {$templatePage->name}"); + + $this->permissions->disableEntityInheritedPermissions($templatePage); + + $resp = $this->asEditor()->get($chapter->getUrl('/edit')); + $this->withHtml($resp)->assertElementNotContains('#template-control a.text-page', "#{$templatePage->id}, {$templatePage->name}"); + $this->withHtml($resp)->assertElementContains('#template-control a.text-page', "#{$templatePage->id}"); + } + + public function test_creating_book_page_uses_book_default_template() + { + $templatePage = $this->entities->templatePage(); + $templatePage->forceFill(['html' => '

    My template page

    ', 'markdown' => '# My template page'])->save(); + $book = $this->bookUsingDefaultTemplate($templatePage); + + $this->asEditor()->get($book->getUrl('/create-page')); + $latestPage = $book->pages() + ->where('draft', '=', true) + ->where('template', '=', false) + ->latest()->first(); + + $this->assertEquals('

    My template page

    ', $latestPage->html); + $this->assertEquals('# My template page', $latestPage->markdown); + } + + public function test_creating_chapter_page_uses_chapter_default_template() + { + $templatePage = $this->entities->templatePage(); + $templatePage->forceFill(['html' => '

    My chapter template page

    ', 'markdown' => '# My chapter template page'])->save(); + $chapter = $this->chapterUsingDefaultTemplate($templatePage); + + $this->asEditor()->get($chapter->getUrl('/create-page')); + $latestPage = $chapter->pages() + ->where('draft', '=', true) + ->where('template', '=', false) + ->latest()->first(); + + $this->assertEquals('

    My chapter template page

    ', $latestPage->html); + $this->assertEquals('# My chapter template page', $latestPage->markdown); + } + + public function test_creating_chapter_page_uses_book_default_template_if_no_chapter_template_set() + { + $templatePage = $this->entities->templatePage(); + $templatePage->forceFill(['html' => '

    My template page in chapter

    ', 'markdown' => '# My template page in chapter'])->save(); + $book = $this->bookUsingDefaultTemplate($templatePage); + $chapter = $book->chapters()->first(); + + $this->asEditor()->get($chapter->getUrl('/create-page')); + $latestPage = $chapter->pages() + ->where('draft', '=', true) + ->where('template', '=', false) + ->latest()->first(); + + $this->assertEquals('

    My template page in chapter

    ', $latestPage->html); + $this->assertEquals('# My template page in chapter', $latestPage->markdown); + } + + public function test_creating_chapter_page_uses_chapter_template_instead_of_book_template() + { + $bookTemplatePage = $this->entities->templatePage(); + $bookTemplatePage->forceFill(['html' => '

    My book template

    ', 'markdown' => '# My book template'])->save(); + $book = $this->bookUsingDefaultTemplate($bookTemplatePage); + + $chapterTemplatePage = $this->entities->templatePage(); + $chapterTemplatePage->forceFill(['html' => '

    My chapter template

    ', 'markdown' => '# My chapter template'])->save(); + $chapter = $book->chapters()->first(); + $chapter->default_template_id = $chapterTemplatePage->id; + $chapter->save(); + + $this->asEditor()->get($chapter->getUrl('/create-page')); + $latestPage = $chapter->pages() + ->where('draft', '=', true) + ->where('template', '=', false) + ->latest()->first(); + + $this->assertEquals('

    My chapter template

    ', $latestPage->html); + $this->assertEquals('# My chapter template', $latestPage->markdown); + } + + public function test_creating_page_as_guest_uses_default_template() + { + $templatePage = $this->entities->templatePage(); + $templatePage->forceFill(['html' => '

    My template page

    ', 'markdown' => '# My template page'])->save(); + $book = $this->bookUsingDefaultTemplate($templatePage); + $chapter = $this->chapterUsingDefaultTemplate($templatePage); + $guest = $this->users->guest(); + + $this->permissions->makeAppPublic(); + $this->permissions->grantUserRolePermissions($guest, ['page-create-all', 'page-update-all']); + + $this->post($book->getUrl('/create-guest-page'), [ + 'name' => 'My guest page with template' + ]); + $latestBookPage = $book->pages() + ->where('draft', '=', false) + ->where('template', '=', false) + ->where('created_by', '=', $guest->id) + ->latest()->first(); + + $this->assertEquals('

    My template page

    ', $latestBookPage->html); + $this->assertEquals('# My template page', $latestBookPage->markdown); + + $this->post($chapter->getUrl('/create-guest-page'), [ + 'name' => 'My guest page with template' + ]); + $latestChapterPage = $chapter->pages() + ->where('draft', '=', false) + ->where('template', '=', false) + ->where('created_by', '=', $guest->id) + ->latest()->first(); + + $this->assertEquals('

    My template page

    ', $latestChapterPage->html); + $this->assertEquals('# My template page', $latestChapterPage->markdown); + } + + public function test_templates_not_used_if_not_visible() + { + $templatePage = $this->entities->templatePage(); + $templatePage->forceFill(['html' => '

    My template page

    ', 'markdown' => '# My template page'])->save(); + $book = $this->bookUsingDefaultTemplate($templatePage); + $chapter = $this->chapterUsingDefaultTemplate($templatePage); + + $this->permissions->disableEntityInheritedPermissions($templatePage); + + $this->asEditor()->get($book->getUrl('/create-page')); + $latestBookPage = $book->pages() + ->where('draft', '=', true) + ->where('template', '=', false) + ->latest()->first(); + + $this->assertEquals('', $latestBookPage->html); + $this->assertEquals('', $latestBookPage->markdown); + + $this->asEditor()->get($chapter->getUrl('/create-page')); + $latestChapterPage = $chapter->pages() + ->where('draft', '=', true) + ->where('template', '=', false) + ->latest()->first(); + + $this->assertEquals('', $latestChapterPage->html); + $this->assertEquals('', $latestChapterPage->markdown); + } + + public function test_template_page_delete_removes_template_usage() + { + $templatePage = $this->entities->templatePage(); + $book = $this->bookUsingDefaultTemplate($templatePage); + $chapter = $this->chapterUsingDefaultTemplate($templatePage); + + $book->refresh(); + $this->assertEquals($templatePage->id, $book->default_template_id); + $this->assertEquals($templatePage->id, $chapter->default_template_id); + + $this->asEditor()->delete($templatePage->getUrl()); + $this->asAdmin()->post('/settings/recycle-bin/empty'); + + $book->refresh(); + $chapter->refresh(); + $this->assertEquals(null, $book->default_template_id); + $this->assertEquals(null, $chapter->default_template_id); + } + + protected function bookUsingDefaultTemplate(Page $page): Book + { + $book = $this->entities->book(); + $book->default_template_id = $page->id; + $book->save(); + + return $book; + } + + protected function chapterUsingDefaultTemplate(Page $page): Chapter + { + $chapter = $this->entities->chapter(); + $chapter->default_template_id = $page->id; + $chapter->save(); + + return $chapter; + } +} diff --git a/tests/Entity/ExportTest.php b/tests/Entity/ExportTest.php deleted file mode 100644 index eedcb672c99..00000000000 --- a/tests/Entity/ExportTest.php +++ /dev/null @@ -1,518 +0,0 @@ -entities->page(); - $this->asEditor(); - - $resp = $this->get($page->getUrl('/export/plaintext')); - $resp->assertStatus(200); - $resp->assertSee($page->name); - $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $page->slug . '.txt"'); - } - - public function test_page_pdf_export() - { - $page = $this->entities->page(); - $this->asEditor(); - - $resp = $this->get($page->getUrl('/export/pdf')); - $resp->assertStatus(200); - $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $page->slug . '.pdf"'); - } - - public function test_page_html_export() - { - $page = $this->entities->page(); - $this->asEditor(); - - $resp = $this->get($page->getUrl('/export/html')); - $resp->assertStatus(200); - $resp->assertSee($page->name); - $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $page->slug . '.html"'); - } - - public function test_book_text_export() - { - $book = $this->entities->bookHasChaptersAndPages(); - $directPage = $book->directPages()->first(); - $chapter = $book->chapters()->first(); - $chapterPage = $chapter->pages()->first(); - $this->entities->updatePage($directPage, ['html' => '

    My awesome page

    ']); - $this->entities->updatePage($chapterPage, ['html' => '

    My little nested page

    ']); - $this->asEditor(); - - $resp = $this->get($book->getUrl('/export/plaintext')); - $resp->assertStatus(200); - $resp->assertSee($book->name); - $resp->assertSee($chapterPage->name); - $resp->assertSee($chapter->name); - $resp->assertSee($directPage->name); - $resp->assertSee('My awesome page'); - $resp->assertSee('My little nested page'); - $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $book->slug . '.txt"'); - } - - public function test_book_text_export_format() - { - $entities = $this->entities->createChainBelongingToUser($this->users->viewer()); - $this->entities->updatePage($entities['page'], ['html' => '

    My great page

    Full of great stuff

    ', 'name' => 'My wonderful page!']); - $entities['chapter']->name = 'Export chapter'; - $entities['chapter']->description = "A test chapter to be exported\nIt has loads of info within"; - $entities['book']->name = 'Export Book'; - $entities['book']->description = "This is a book with stuff to export"; - $entities['chapter']->save(); - $entities['book']->save(); - - $resp = $this->asEditor()->get($entities['book']->getUrl('/export/plaintext')); - - $expected = "Export Book\nThis is a book with stuff to export\n\nExport chapter\nA test chapter to be exported\nIt has loads of info within\n\n"; - $expected .= "My wonderful page!\nMy great page Full of great stuff"; - $resp->assertSee($expected); - } - - public function test_book_pdf_export() - { - $page = $this->entities->page(); - $book = $page->book; - $this->asEditor(); - - $resp = $this->get($book->getUrl('/export/pdf')); - $resp->assertStatus(200); - $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $book->slug . '.pdf"'); - } - - public function test_book_html_export() - { - $page = $this->entities->page(); - $book = $page->book; - $this->asEditor(); - - $resp = $this->get($book->getUrl('/export/html')); - $resp->assertStatus(200); - $resp->assertSee($book->name); - $resp->assertSee($page->name); - $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $book->slug . '.html"'); - } - - public function test_book_html_export_shows_html_descriptions() - { - $book = $this->entities->bookHasChaptersAndPages(); - $chapter = $book->chapters()->first(); - $book->description_html = '

    A description with HTML within!

    '; - $chapter->description_html = '

    A chapter description with HTML within!

    '; - $book->save(); - $chapter->save(); - - $resp = $this->asEditor()->get($book->getUrl('/export/html')); - $resp->assertSee($book->description_html, false); - $resp->assertSee($chapter->description_html, false); - } - - public function test_chapter_text_export() - { - $chapter = $this->entities->chapter(); - $page = $chapter->pages[0]; - $this->entities->updatePage($page, ['html' => '

    This is content within the page!

    ']); - $this->asEditor(); - - $resp = $this->get($chapter->getUrl('/export/plaintext')); - $resp->assertStatus(200); - $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"'); - } - - public function test_chapter_text_export_format() - { - $entities = $this->entities->createChainBelongingToUser($this->users->viewer()); - $this->entities->updatePage($entities['page'], ['html' => '

    My great page

    Full of great stuff

    ', 'name' => 'My wonderful page!']); - $entities['chapter']->name = 'Export chapter'; - $entities['chapter']->description = "A test chapter to be exported\nIt has loads of info within"; - $entities['chapter']->save(); - - $resp = $this->asEditor()->get($entities['book']->getUrl('/export/plaintext')); - - $expected = "Export chapter\nA test chapter to be exported\nIt has loads of info within\n\n"; - $expected .= "My wonderful page!\nMy great page Full of great stuff"; - $resp->assertSee($expected); - } - - public function test_chapter_pdf_export() - { - $chapter = $this->entities->chapter(); - $this->asEditor(); - - $resp = $this->get($chapter->getUrl('/export/pdf')); - $resp->assertStatus(200); - $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $chapter->slug . '.pdf"'); - } - - public function test_chapter_html_export() - { - $chapter = $this->entities->chapter(); - $page = $chapter->pages[0]; - $this->asEditor(); - - $resp = $this->get($chapter->getUrl('/export/html')); - $resp->assertStatus(200); - $resp->assertSee($chapter->name); - $resp->assertSee($page->name); - $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $chapter->slug . '.html"'); - } - - public function test_chapter_html_export_shows_html_descriptions() - { - $chapter = $this->entities->chapter(); - $chapter->description_html = '

    A description with HTML within!

    '; - $chapter->save(); - - $resp = $this->asEditor()->get($chapter->getUrl('/export/html')); - $resp->assertSee($chapter->description_html, false); - } - - public function test_page_html_export_contains_custom_head_if_set() - { - $page = $this->entities->page(); - - $customHeadContent = ''; - $this->setSettings(['app-custom-head' => $customHeadContent]); - - $resp = $this->asEditor()->get($page->getUrl('/export/html')); - $resp->assertSee($customHeadContent, false); - } - - public function test_page_html_export_does_not_break_with_only_comments_in_custom_head() - { - $page = $this->entities->page(); - - $customHeadContent = ''; - $this->setSettings(['app-custom-head' => $customHeadContent]); - - $resp = $this->asEditor()->get($page->getUrl('/export/html')); - $resp->assertStatus(200); - $resp->assertSee($customHeadContent, false); - } - - public function test_page_html_export_use_absolute_dates() - { - $page = $this->entities->page(); - - $resp = $this->asEditor()->get($page->getUrl('/export/html')); - $resp->assertSee($page->created_at->isoFormat('D MMMM Y HH:mm:ss')); - $resp->assertDontSee($page->created_at->diffForHumans()); - $resp->assertSee($page->updated_at->isoFormat('D MMMM Y HH:mm:ss')); - $resp->assertDontSee($page->updated_at->diffForHumans()); - } - - public function test_page_export_does_not_include_user_or_revision_links() - { - $page = $this->entities->page(); - - $resp = $this->asEditor()->get($page->getUrl('/export/html')); - $resp->assertDontSee($page->getUrl('/revisions')); - $resp->assertDontSee($page->createdBy->getProfileUrl()); - $resp->assertSee($page->createdBy->name); - } - - public function test_page_export_sets_right_data_type_for_svg_embeds() - { - $page = $this->entities->page(); - Storage::disk('local')->makeDirectory('uploads/images/gallery'); - Storage::disk('local')->put('uploads/images/gallery/svg_test.svg', ''); - $page->html = ''; - $page->save(); - - $this->asEditor(); - $resp = $this->get($page->getUrl('/export/html')); - Storage::disk('local')->delete('uploads/images/gallery/svg_test.svg'); - - $resp->assertStatus(200); - $resp->assertSee(''; - $page->save(); - - $resp = $this->asEditor()->get($page->getUrl('/export/html')); - Storage::disk('local')->delete('uploads/images/gallery/svg_test.svg'); - Storage::disk('local')->delete('uploads/images/gallery/svg_test2.svg'); - - $resp->assertDontSee('http://localhost/uploads/images/gallery/svg_test'); - } - - public function test_page_export_contained_html_image_fetches_only_run_when_url_points_to_image_upload_folder() - { - $page = $this->entities->page(); - $page->html = '' - . '' - . ''; - $storageDisk = Storage::disk('local'); - $storageDisk->makeDirectory('uploads/images/gallery'); - $storageDisk->put('uploads/images/gallery/svg_test.svg', 'good'); - $storageDisk->put('uploads/svg_test.svg', 'bad'); - $page->save(); - - $resp = $this->asEditor()->get($page->getUrl('/export/html')); - - $storageDisk->delete('uploads/images/gallery/svg_test.svg'); - $storageDisk->delete('uploads/svg_test.svg'); - - $resp->assertDontSee('http://localhost/uploads/images/gallery/svg_test.svg', false); - $resp->assertSee('http://localhost/uploads/svg_test.svg'); - $resp->assertSee('src="/uploads/svg_test.svg"', false); - } - - public function test_page_export_contained_html_does_not_allow_upward_traversal_with_local() - { - $contents = file_get_contents(public_path('.htaccess')); - config()->set('filesystems.images', 'local'); - - $page = $this->entities->page(); - $page->html = ''; - $page->save(); - - $resp = $this->asEditor()->get($page->getUrl('/export/html')); - $resp->assertDontSee(base64_encode($contents)); - } - - public function test_page_export_contained_html_does_not_allow_upward_traversal_with_local_secure() - { - $testFilePath = storage_path('logs/test.txt'); - config()->set('filesystems.images', 'local_secure'); - file_put_contents($testFilePath, 'I am a cat'); - - $page = $this->entities->page(); - $page->html = ''; - $page->save(); - - $resp = $this->asEditor()->get($page->getUrl('/export/html')); - $resp->assertDontSee(base64_encode('I am a cat')); - unlink($testFilePath); - } - - public function test_exports_removes_scripts_from_custom_head() - { - $entities = [ - Page::query()->first(), Chapter::query()->first(), Book::query()->first(), - ]; - setting()->put('app-custom-head', ''); - - foreach ($entities as $entity) { - $resp = $this->asEditor()->get($entity->getUrl('/export/html')); - $resp->assertDontSee('window.donkey'); - $resp->assertDontSee('assertSee('.my-test-class { color: red; }'); - } - } - - public function test_page_export_with_deleted_creator_and_updater() - { - $user = $this->users->viewer(['name' => 'ExportWizardTheFifth']); - $page = $this->entities->page(); - $page->created_by = $user->id; - $page->updated_by = $user->id; - $page->save(); - - $resp = $this->asEditor()->get($page->getUrl('/export/html')); - $resp->assertSee('ExportWizardTheFifth'); - - $user->delete(); - $resp = $this->get($page->getUrl('/export/html')); - $resp->assertStatus(200); - $resp->assertDontSee('ExportWizardTheFifth'); - } - - public function test_page_pdf_export_converts_iframes_to_links() - { - $page = Page::query()->first()->forceFill([ - 'html' => '', - ]); - $page->save(); - - $pdfHtml = ''; - $mockPdfGenerator = $this->mock(PdfGenerator::class); - $mockPdfGenerator->shouldReceive('fromHtml') - ->with(\Mockery::capture($pdfHtml)) - ->andReturn(''); - $mockPdfGenerator->shouldReceive('getActiveEngine')->andReturn(PdfGenerator::ENGINE_DOMPDF); - - $this->asEditor()->get($page->getUrl('/export/pdf')); - $this->assertStringNotContainsString('iframe>', $pdfHtml); - $this->assertStringContainsString('

    https://www.youtube.com/embed/ShqUjt33uOs

    ', $pdfHtml); - } - - public function test_page_pdf_export_opens_details_blocks() - { - $page = $this->entities->page()->forceFill([ - 'html' => '
    Hello

    Content!

    ', - ]); - $page->save(); - - $pdfHtml = ''; - $mockPdfGenerator = $this->mock(PdfGenerator::class); - $mockPdfGenerator->shouldReceive('fromHtml') - ->with(\Mockery::capture($pdfHtml)) - ->andReturn(''); - $mockPdfGenerator->shouldReceive('getActiveEngine')->andReturn(PdfGenerator::ENGINE_DOMPDF); - - $this->asEditor()->get($page->getUrl('/export/pdf')); - $this->assertStringContainsString('
    entities->page(); - - $resp = $this->asEditor()->get($page->getUrl('/export/markdown')); - $resp->assertStatus(200); - $resp->assertSee($page->name); - $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $page->slug . '.md"'); - } - - public function test_page_markdown_export_uses_existing_markdown_if_apparent() - { - $page = $this->entities->page()->forceFill([ - 'markdown' => '# A header', - 'html' => '

    Dogcat

    ', - ]); - $page->save(); - - $resp = $this->asEditor()->get($page->getUrl('/export/markdown')); - $resp->assertSee('A header'); - $resp->assertDontSee('Dogcat'); - } - - public function test_page_markdown_export_converts_html_where_no_markdown() - { - $page = $this->entities->page()->forceFill([ - 'markdown' => '', - 'html' => '

    Dogcat

    Some bold text

    ', - ]); - $page->save(); - - $resp = $this->asEditor()->get($page->getUrl('/export/markdown')); - $resp->assertSee("# Dogcat\n\nSome **bold** text"); - } - - public function test_chapter_markdown_export() - { - $chapter = $this->entities->chapter(); - $page = $chapter->pages()->first(); - $resp = $this->asEditor()->get($chapter->getUrl('/export/markdown')); - - $resp->assertSee('# ' . $chapter->name); - $resp->assertSee('# ' . $page->name); - } - - public function test_book_markdown_export() - { - $book = Book::query()->whereHas('pages')->whereHas('chapters')->first(); - $chapter = $book->chapters()->first(); - $page = $chapter->pages()->first(); - $resp = $this->asEditor()->get($book->getUrl('/export/markdown')); - - $resp->assertSee('# ' . $book->name); - $resp->assertSee('# ' . $chapter->name); - $resp->assertSee('# ' . $page->name); - } - - public function test_book_markdown_export_concats_immediate_pages_with_newlines() - { - /** @var Book $book */ - $book = Book::query()->whereHas('pages')->first(); - - $this->asEditor()->get($book->getUrl('/create-page')); - $this->get($book->getUrl('/create-page')); - - [$pageA, $pageB] = $book->pages()->where('chapter_id', '=', 0)->get(); - $pageA->html = '

    hello tester

    '; - $pageA->save(); - $pageB->name = 'The second page in this test'; - $pageB->save(); - - $resp = $this->get($book->getUrl('/export/markdown')); - $resp->assertDontSee('hello tester# The second page in this test'); - $resp->assertSee("hello tester\n\n# The second page in this test"); - } - - public function test_export_option_only_visible_and_accessible_with_permission() - { - $book = Book::query()->whereHas('pages')->whereHas('chapters')->first(); - $chapter = $book->chapters()->first(); - $page = $chapter->pages()->first(); - $entities = [$book, $chapter, $page]; - $user = $this->users->viewer(); - $this->actingAs($user); - - foreach ($entities as $entity) { - $resp = $this->get($entity->getUrl()); - $resp->assertSee('/export/pdf'); - } - - $this->permissions->removeUserRolePermissions($user, ['content-export']); - - foreach ($entities as $entity) { - $resp = $this->get($entity->getUrl()); - $resp->assertDontSee('/export/pdf'); - $resp = $this->get($entity->getUrl('/export/pdf')); - $this->assertPermissionError($resp); - } - } - - public function test_wkhtmltopdf_only_used_when_allow_untrusted_is_true() - { - $page = $this->entities->page(); - - config()->set('snappy.pdf.binary', '/abc123'); - config()->set('app.allow_untrusted_server_fetching', false); - - $resp = $this->asEditor()->get($page->getUrl('/export/pdf')); - $resp->assertStatus(200); // Sucessful response with invalid snappy binary indicates dompdf usage. - - config()->set('app.allow_untrusted_server_fetching', true); - $resp = $this->get($page->getUrl('/export/pdf')); - $resp->assertStatus(500); // Bad response indicates wkhtml usage - } - - public function test_html_exports_contain_csp_meta_tag() - { - $entities = [ - $this->entities->page(), - $this->entities->book(), - $this->entities->chapter(), - ]; - - foreach ($entities as $entity) { - $resp = $this->asEditor()->get($entity->getUrl('/export/html')); - $this->withHtml($resp)->assertElementExists('head meta[http-equiv="Content-Security-Policy"][content*="script-src "]'); - } - } - - public function test_html_exports_contain_body_classes_for_export_identification() - { - $page = $this->entities->page(); - - $resp = $this->asEditor()->get($page->getUrl('/export/html')); - $this->withHtml($resp)->assertElementExists('body.export.export-format-html.export-engine-none'); - } -} diff --git a/tests/Entity/PageContentTest.php b/tests/Entity/PageContentTest.php index 28897c14d2f..23a38b5735b 100644 --- a/tests/Entity/PageContentTest.php +++ b/tests/Entity/PageContentTest.php @@ -469,6 +469,20 @@ public function test_get_page_nav_shifts_headers_if_only_smaller_ones_are_used() ], $navMap[2]); } + public function test_get_page_nav_respects_non_breaking_spaces() + { + $content = '

    Hello There

    '; + $pageContent = new PageContent(new Page(['html' => $content])); + $navMap = $pageContent->getNavigation($content); + + $this->assertEquals([ + 'nodeName' => 'h1', + 'link' => '#testa', + 'text' => 'Hello There', + 'level' => 1, + ], $navMap[0]); + } + public function test_page_text_decodes_html_entities() { $page = $this->entities->page(); diff --git a/tests/Entity/PageEditorTest.php b/tests/Entity/PageEditorTest.php index b2fb8595543..ad753c96647 100644 --- a/tests/Entity/PageEditorTest.php +++ b/tests/Entity/PageEditorTest.php @@ -4,6 +4,7 @@ use BookStack\Entities\Models\Chapter; use BookStack\Entities\Models\Page; +use BookStack\Entities\Tools\PageEditorType; use Tests\TestCase; class PageEditorTest extends TestCase @@ -23,9 +24,24 @@ public function test_default_editor_is_wysiwyg_for_new_pages() $this->withHtml($this->followRedirects($resp))->assertElementExists('#html-editor'); } + public function test_editor_set_for_new_pages() + { + $book = $this->page->book; + + $this->asEditor()->get($book->getUrl('/create-page')); + $newPage = $book->pages()->orderBy('id', 'desc')->first(); + $this->assertEquals('wysiwyg', $newPage->editor); + + $this->setSettings(['app-editor' => PageEditorType::Markdown->value]); + + $this->asEditor()->get($book->getUrl('/create-page')); + $newPage = $book->pages()->orderBy('id', 'desc')->first(); + $this->assertEquals('markdown', $newPage->editor); + } + public function test_markdown_setting_shows_markdown_editor_for_new_pages() { - $this->setSettings(['app-editor' => 'markdown']); + $this->setSettings(['app-editor' => PageEditorType::Markdown->value]); $resp = $this->asAdmin()->get($this->page->book->getUrl('/create-page')); $this->withHtml($this->followRedirects($resp)) @@ -37,7 +53,7 @@ public function test_markdown_content_given_to_editor() { $mdContent = '# hello. This is a test'; $this->page->markdown = $mdContent; - $this->page->editor = 'markdown'; + $this->page->editor = PageEditorType::Markdown; $this->page->save(); $resp = $this->asAdmin()->get($this->page->getUrl('/edit')); @@ -135,6 +151,19 @@ public function test_switching_from_markdown_to_wysiwyg_works() $resp = $this->asAdmin()->get($page->getUrl('/edit?editor=wysiwyg')); $resp->assertStatus(200); + $this->withHtml($resp)->assertElementExists('[component="wysiwyg-editor-tinymce"]'); + $resp->assertSee("

    A Header

    \n

    Some content with bold text!

    ", true); + } + + public function test_switching_from_markdown_to_wysiwyg2024_works() + { + $page = $this->entities->page(); + $page->html = ''; + $page->markdown = "## A Header\n\nSome content with **bold** text!"; + $page->save(); + + $resp = $this->asAdmin()->get($page->getUrl('/edit?editor=wysiwyg2024')); + $resp->assertStatus(200); $this->withHtml($resp)->assertElementExists('[component="wysiwyg-editor"]'); $resp->assertSee("

    A Header

    \n

    Some content with bold text!

    ", true); } @@ -142,7 +171,7 @@ public function test_switching_from_markdown_to_wysiwyg_works() public function test_page_editor_changes_with_editor_property() { $resp = $this->asAdmin()->get($this->page->getUrl('/edit')); - $this->withHtml($resp)->assertElementExists('[component="wysiwyg-editor"]'); + $this->withHtml($resp)->assertElementExists('[component="wysiwyg-editor-tinymce"]'); $this->page->markdown = "## A Header\n\nSome content with **bold** text!"; $this->page->editor = 'markdown'; @@ -150,25 +179,32 @@ public function test_page_editor_changes_with_editor_property() $resp = $this->asAdmin()->get($this->page->getUrl('/edit')); $this->withHtml($resp)->assertElementExists('[component="markdown-editor"]'); + + $this->page->editor = 'wysiwyg2024'; + $this->page->save(); + + $resp = $this->asAdmin()->get($this->page->getUrl('/edit')); + $this->withHtml($resp)->assertElementExists('[component="wysiwyg-editor"]'); } public function test_editor_type_switch_options_show() { $resp = $this->asAdmin()->get($this->page->getUrl('/edit')); $editLink = $this->page->getUrl('/edit') . '?editor='; - $this->withHtml($resp)->assertElementContains("a[href=\"${editLink}markdown-clean\"]", '(Clean Content)'); - $this->withHtml($resp)->assertElementContains("a[href=\"${editLink}markdown-stable\"]", '(Stable Content)'); + $this->withHtml($resp)->assertElementContains("a[href=\"{$editLink}markdown-clean\"]", '(Clean Content)'); + $this->withHtml($resp)->assertElementContains("a[href=\"{$editLink}markdown-stable\"]", '(Stable Content)'); + $this->withHtml($resp)->assertElementContains("a[href=\"{$editLink}wysiwyg2024\"]", '(In Beta Testing)'); $resp = $this->asAdmin()->get($this->page->getUrl('/edit?editor=markdown-stable')); $editLink = $this->page->getUrl('/edit') . '?editor='; - $this->withHtml($resp)->assertElementContains("a[href=\"${editLink}wysiwyg\"]", 'Switch to WYSIWYG Editor'); + $this->withHtml($resp)->assertElementContains("a[href=\"{$editLink}wysiwyg\"]", 'Switch to WYSIWYG Editor'); } public function test_editor_type_switch_options_dont_show_if_without_change_editor_permissions() { $resp = $this->asEditor()->get($this->page->getUrl('/edit')); $editLink = $this->page->getUrl('/edit') . '?editor='; - $this->withHtml($resp)->assertElementNotExists("a[href*=\"${editLink}\"]"); + $this->withHtml($resp)->assertElementNotExists("a[href*=\"{$editLink}\"]"); } public function test_page_editor_type_switch_does_not_work_without_change_editor_permissions() @@ -179,7 +215,7 @@ public function test_page_editor_type_switch_does_not_work_without_change_editor $resp = $this->asEditor()->get($page->getUrl('/edit?editor=markdown-stable')); $resp->assertStatus(200); - $this->withHtml($resp)->assertElementExists('[component="wysiwyg-editor"]'); + $this->withHtml($resp)->assertElementExists('[component="wysiwyg-editor-tinymce"]'); $this->withHtml($resp)->assertElementNotExists('[component="markdown-editor"]'); } @@ -193,4 +229,40 @@ public function test_page_save_does_not_change_active_editor_without_change_edit $this->asEditor()->put($page->getUrl(), ['name' => $page->name, 'markdown' => '## Updated content abc']); $this->assertEquals('wysiwyg', $page->refresh()->editor); } + + public function test_editor_type_change_to_wysiwyg_infers_type_from_request_or_uses_system_default() + { + $tests = [ + [ + 'setting' => 'wysiwyg', + 'request' => 'wysiwyg2024', + 'expected' => 'wysiwyg2024', + ], + [ + 'setting' => 'wysiwyg2024', + 'request' => 'wysiwyg', + 'expected' => 'wysiwyg', + ], + [ + 'setting' => 'wysiwyg', + 'request' => null, + 'expected' => 'wysiwyg', + ], + [ + 'setting' => 'wysiwyg2024', + 'request' => null, + 'expected' => 'wysiwyg2024', + ] + ]; + + $page = $this->entities->page(); + foreach ($tests as $test) { + $page->editor = 'markdown'; + $page->save(); + + $this->setSettings(['app-editor' => $test['setting']]); + $this->asAdmin()->put($page->getUrl(), ['name' => $page->name, 'html' => '

    Hello

    ', 'editor' => $test['request']]); + $this->assertEquals($test['expected'], $page->refresh()->editor, "Failed asserting global editor {$test['setting']} with request editor {$test['request']} results in {$test['expected']} set for the page"); + } + } } diff --git a/tests/Entity/PageRevisionTest.php b/tests/Entity/PageRevisionTest.php index a272dc38bdb..9040254f78f 100644 --- a/tests/Entity/PageRevisionTest.php +++ b/tests/Entity/PageRevisionTest.php @@ -203,6 +203,18 @@ public function test_revision_list_shows_editor_type() $this->withHtml($resp)->assertElementContains('.item-list-row > div:nth-child(2)', 'Markdown)'); } + public function test_revision_changes_link_not_shown_for_oldest_revision() + { + $page = $this->entities->page(); + $this->createRevisions($page, 3, ['html' => 'new page html']); + + $resp = $this->asAdmin()->get($page->refresh()->getUrl('/revisions')); + $html = $this->withHtml($resp); + + $html->assertElementNotExists('.item-list > .item-list-row:last-child a[href*="/changes"]'); + $html->assertElementContains('.item-list > .item-list-row:nth-child(2)', 'Changes'); + } + public function test_revision_restore_action_only_visible_with_permission() { $page = $this->entities->page(); diff --git a/tests/Entity/PageTest.php b/tests/Entity/PageTest.php index daad82e76dc..d2c448bf4b7 100644 --- a/tests/Entity/PageTest.php +++ b/tests/Entity/PageTest.php @@ -86,6 +86,32 @@ public function test_page_creation_with_markdown_content() $resp->assertSee('# a title'); } + public function test_page_creation_allows_summary_to_be_set() + { + $book = $this->entities->book(); + + $this->asEditor()->get($book->getUrl('/create-page')); + $draft = Page::query()->where('book_id', '=', $book->id) + ->where('draft', '=', true)->first(); + + $details = [ + 'html' => '

    a title

    ', + 'name' => 'My page with summary', + 'summary' => 'Here is my changelog message for a new page!', + ]; + $resp = $this->post($book->getUrl("/draft/{$draft->id}"), $details); + $resp->assertRedirect(); + + $this->assertDatabaseHas('page_revisions', [ + 'page_id' => $draft->id, + 'summary' => 'Here is my changelog message for a new page!', + ]); + + $draft->refresh(); + $resp = $this->get($draft->getUrl('/revisions')); + $resp->assertSee('Here is my changelog message for a new page!'); + } + public function test_page_delete() { $page = $this->entities->page(); @@ -274,7 +300,7 @@ public function test_recently_updated_pages_view_shows_updated_by_details() ]); $resp = $this->asAdmin()->get('/pages/recently-updated'); - $this->withHtml($resp)->assertElementContains('.entity-list .page:nth-child(1)', 'Updated 1 second ago by ' . $user->name); + $this->withHtml($resp)->assertElementContains('.entity-list .page:nth-child(1) small', 'by ' . $user->name); } public function test_recently_updated_pages_view_shows_parent_chain() @@ -330,4 +356,14 @@ public function test_recently_updated_pages_on_home() $resp = $this->get('/'); $this->withHtml($resp)->assertElementContains('#recently-updated-pages', $page->name); } + + public function test_page_edit_without_update_permissions_but_with_view_redirects_to_page() + { + $page = $this->entities->page(); + + $resp = $this->asViewer()->get($page->getUrl('/edit')); + $resp->assertRedirect($page->getUrl()); + + $resp->assertSessionHas('error', 'You do not have permission to access the requested page.'); + } } diff --git a/tests/Entity/SearchOptionsTest.php b/tests/Entity/SearchOptionsTest.php deleted file mode 100644 index ea4d727a428..00000000000 --- a/tests/Entity/SearchOptionsTest.php +++ /dev/null @@ -1,80 +0,0 @@ -assertEquals(['cat'], $options->searches); - $this->assertEquals(['dog'], $options->exacts); - $this->assertEquals(['tag=good'], $options->tags); - $this->assertEquals(['is_tree' => ''], $options->filters); - } - - public function test_from_string_properly_parses_escaped_quotes() - { - $options = SearchOptions::fromString('"\"cat\"" surprise "\"\"" "\"donkey" "\"" "\\\\"'); - - $this->assertEquals(['"cat"', '""', '"donkey', '"', '\\'], $options->exacts); - } - - public function test_to_string_includes_all_items_in_the_correct_format() - { - $expected = 'cat "dog" [tag=good] {is_tree}'; - $options = new SearchOptions(); - $options->searches = ['cat']; - $options->exacts = ['dog']; - $options->tags = ['tag=good']; - $options->filters = ['is_tree' => '']; - - $output = $options->toString(); - foreach (explode(' ', $expected) as $term) { - $this->assertStringContainsString($term, $output); - } - } - - public function test_to_string_escapes_as_expected() - { - $options = new SearchOptions(); - $options->exacts = ['"cat"', '""', '"donkey', '"', '\\', '\\"']; - - $output = $options->toString(); - $this->assertEquals('"\"cat\"" "\"\"" "\"donkey" "\"" "\\\\" "\\\\\""', $output); - } - - public function test_correct_filter_values_are_set_from_string() - { - $opts = SearchOptions::fromString('{is_tree} {name:dan} {cat:happy}'); - - $this->assertEquals([ - 'is_tree' => '', - 'name' => 'dan', - 'cat' => 'happy', - ], $opts->filters); - } - public function test_it_cannot_parse_out_empty_exacts() - { - $options = SearchOptions::fromString('"" test ""'); - - $this->assertEmpty($options->exacts); - $this->assertCount(1, $options->searches); - } - - public function test_from_request_properly_parses_exacts_from_search_terms() - { - $request = new Request([ - 'search' => 'biscuits "cheese" "" "baked beans"' - ]); - - $options = SearchOptions::fromRequest($request); - $this->assertEquals(["biscuits"], $options->searches); - $this->assertEquals(['"cheese"', '""', '"baked', 'beans"'], $options->exacts); - } -} diff --git a/tests/Entity/TagTest.php b/tests/Entity/TagTest.php index c1240e95538..63f037d9cc0 100644 --- a/tests/Entity/TagTest.php +++ b/tests/Entity/TagTest.php @@ -9,7 +9,7 @@ class TagTest extends TestCase { - protected $defaultTagCount = 20; + protected int $defaultTagCount = 20; /** * Get an instance of a page that has many tags. @@ -193,6 +193,24 @@ public function test_tag_index_shows_message_on_no_results() $resp->assertSee('Tags can be assigned via the page editor sidebar'); } + public function test_tag_index_does_not_include_tags_on_recycle_bin_items() + { + $page = $this->entities->page(); + $page->tags()->create(['name' => 'DeleteRecord', 'value' => 'itemToDeleteTest']); + + $resp = $this->asEditor()->get('/tags'); + $resp->assertSee('DeleteRecord'); + $resp = $this->asEditor()->get('/tags?name=DeleteRecord'); + $resp->assertSee('itemToDeleteTest'); + + $this->entities->sendToRecycleBin($page); + + $resp = $this->asEditor()->get('/tags'); + $resp->assertDontSee('DeleteRecord'); + $resp = $this->asEditor()->get('/tags?name=DeleteRecord'); + $resp->assertDontSee('itemToDeleteTest'); + } + public function test_tag_classes_visible_on_entities() { $this->asEditor(); @@ -212,4 +230,39 @@ public function test_tag_classes_are_escaped() $resp->assertDontSee('tag-name-<>', false); $resp->assertSee('tag-name-<>', false); } + + public function test_parent_tag_classes_visible() + { + $page = $this->entities->pageWithinChapter(); + $page->chapter->tags()->create(['name' => 'My Chapter Tag', 'value' => 'abc123']); + $page->book->tags()->create(['name' => 'My Book Tag', 'value' => 'def456']); + $this->asEditor(); + + $html = $this->withHtml($this->get($page->getUrl())); + $html->assertElementExists('body.chapter-tag-pair-mychaptertag-abc123'); + $html->assertElementExists('body.book-tag-pair-mybooktag-def456'); + + $html = $this->withHtml($this->get($page->chapter->getUrl())); + $html->assertElementExists('body.book-tag-pair-mybooktag-def456'); + } + + public function test_parent_tag_classes_not_visible_if_cannot_see_parent() + { + $page = $this->entities->pageWithinChapter(); + $page->chapter->tags()->create(['name' => 'My Chapter Tag', 'value' => 'abc123']); + $page->book->tags()->create(['name' => 'My Book Tag', 'value' => 'def456']); + $editor = $this->users->editor(); + $this->actingAs($editor); + + $this->permissions->setEntityPermissions($page, ['view'], [$editor->roles()->first()]); + $this->permissions->disableEntityInheritedPermissions($page->chapter); + + $html = $this->withHtml($this->get($page->getUrl())); + $html->assertElementNotExists('body.chapter-tag-pair-mychaptertag-abc123'); + $html->assertElementExists('body.book-tag-pair-mybooktag-def456'); + + $this->permissions->disableEntityInheritedPermissions($page->book); + $html = $this->withHtml($this->get($page->getUrl())); + $html->assertElementNotExists('body.book-tag-pair-mybooktag-def456'); + } } diff --git a/tests/ErrorTest.php b/tests/ErrorTest.php index 0d2ef808c48..642945d4388 100644 --- a/tests/ErrorTest.php +++ b/tests/ErrorTest.php @@ -23,6 +23,35 @@ public function test_404_page_does_not_show_login() $notFound->assertSeeText('tester'); } + public function test_404_page_does_not_non_visible_content() + { + $editor = $this->users->editor(); + $book = $this->entities->book(); + + $this->actingAs($editor)->get($book->getUrl())->assertOk(); + + $this->permissions->disableEntityInheritedPermissions($book); + + $this->actingAs($editor)->get($book->getUrl())->assertNotFound(); + } + + public function test_404_page_shows_visible_content_within_non_visible_parent() + { + $editor = $this->users->editor(); + $book = $this->entities->book(); + $page = $book->pages()->first(); + + $this->actingAs($editor)->get($page->getUrl())->assertOk(); + + $this->permissions->disableEntityInheritedPermissions($book); + $this->permissions->addEntityPermission($page, ['view'], $editor->roles()->first()); + + $resp = $this->actingAs($editor)->get($book->getUrl()); + $resp->assertNotFound(); + $resp->assertSee($page->name); + $resp->assertDontSee($book->name); + } + public function test_item_not_found_does_not_get_logged_to_file() { $this->actingAs($this->users->viewer()); diff --git a/tests/Exports/ExportUiTest.php b/tests/Exports/ExportUiTest.php new file mode 100644 index 00000000000..77b26ad8929 --- /dev/null +++ b/tests/Exports/ExportUiTest.php @@ -0,0 +1,33 @@ +whereHas('pages')->whereHas('chapters')->first(); + $chapter = $book->chapters()->first(); + $page = $chapter->pages()->first(); + $entities = [$book, $chapter, $page]; + $user = $this->users->viewer(); + $this->actingAs($user); + + foreach ($entities as $entity) { + $resp = $this->get($entity->getUrl()); + $resp->assertSee('/export/pdf'); + } + + $this->permissions->removeUserRolePermissions($user, ['content-export']); + + foreach ($entities as $entity) { + $resp = $this->get($entity->getUrl()); + $resp->assertDontSee('/export/pdf'); + $resp = $this->get($entity->getUrl('/export/pdf')); + $this->assertPermissionError($resp); + } + } +} diff --git a/tests/Exports/HtmlExportTest.php b/tests/Exports/HtmlExportTest.php new file mode 100644 index 00000000000..e039fb2cc2b --- /dev/null +++ b/tests/Exports/HtmlExportTest.php @@ -0,0 +1,253 @@ +entities->page(); + $this->asEditor(); + + $resp = $this->get($page->getUrl('/export/html')); + $resp->assertStatus(200); + $resp->assertSee($page->name); + $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $page->slug . '.html"'); + } + + public function test_book_html_export() + { + $page = $this->entities->page(); + $book = $page->book; + $this->asEditor(); + + $resp = $this->get($book->getUrl('/export/html')); + $resp->assertStatus(200); + $resp->assertSee($book->name); + $resp->assertSee($page->name); + $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $book->slug . '.html"'); + } + + public function test_book_html_export_shows_html_descriptions() + { + $book = $this->entities->bookHasChaptersAndPages(); + $chapter = $book->chapters()->first(); + $book->description_html = '

    A description with HTML within!

    '; + $chapter->description_html = '

    A chapter description with HTML within!

    '; + $book->save(); + $chapter->save(); + + $resp = $this->asEditor()->get($book->getUrl('/export/html')); + $resp->assertSee($book->description_html, false); + $resp->assertSee($chapter->description_html, false); + } + + public function test_chapter_html_export() + { + $chapter = $this->entities->chapter(); + $page = $chapter->pages[0]; + $this->asEditor(); + + $resp = $this->get($chapter->getUrl('/export/html')); + $resp->assertStatus(200); + $resp->assertSee($chapter->name); + $resp->assertSee($page->name); + $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $chapter->slug . '.html"'); + } + + public function test_chapter_html_export_shows_html_descriptions() + { + $chapter = $this->entities->chapter(); + $chapter->description_html = '

    A description with HTML within!

    '; + $chapter->save(); + + $resp = $this->asEditor()->get($chapter->getUrl('/export/html')); + $resp->assertSee($chapter->description_html, false); + } + + public function test_page_html_export_contains_custom_head_if_set() + { + $page = $this->entities->page(); + + $customHeadContent = ''; + $this->setSettings(['app-custom-head' => $customHeadContent]); + + $resp = $this->asEditor()->get($page->getUrl('/export/html')); + $resp->assertSee($customHeadContent, false); + } + + public function test_page_html_export_does_not_break_with_only_comments_in_custom_head() + { + $page = $this->entities->page(); + + $customHeadContent = ''; + $this->setSettings(['app-custom-head' => $customHeadContent]); + + $resp = $this->asEditor()->get($page->getUrl('/export/html')); + $resp->assertStatus(200); + $resp->assertSee($customHeadContent, false); + } + + public function test_page_html_export_use_absolute_dates() + { + $page = $this->entities->page(); + + $resp = $this->asEditor()->get($page->getUrl('/export/html')); + $resp->assertSee($page->created_at->format('Y-m-d H:i:s T')); + $resp->assertDontSee($page->created_at->diffForHumans()); + $resp->assertSee($page->updated_at->format('Y-m-d H:i:s T')); + $resp->assertDontSee($page->updated_at->diffForHumans()); + } + + public function test_page_export_does_not_include_user_or_revision_links() + { + $page = $this->entities->page(); + + $resp = $this->asEditor()->get($page->getUrl('/export/html')); + $resp->assertDontSee($page->getUrl('/revisions')); + $resp->assertDontSee($page->createdBy->getProfileUrl()); + $resp->assertSee($page->createdBy->name); + } + + public function test_page_export_sets_right_data_type_for_svg_embeds() + { + $page = $this->entities->page(); + Storage::disk('local')->makeDirectory('uploads/images/gallery'); + Storage::disk('local')->put('uploads/images/gallery/svg_test.svg', ''); + $page->html = ''; + $page->save(); + + $this->asEditor(); + $resp = $this->get($page->getUrl('/export/html')); + Storage::disk('local')->delete('uploads/images/gallery/svg_test.svg'); + + $resp->assertStatus(200); + $resp->assertSee(''; + $page->save(); + + $resp = $this->asEditor()->get($page->getUrl('/export/html')); + Storage::disk('local')->delete('uploads/images/gallery/svg_test.svg'); + Storage::disk('local')->delete('uploads/images/gallery/svg_test2.svg'); + + $resp->assertDontSee('http://localhost/uploads/images/gallery/svg_test'); + } + + public function test_page_export_contained_html_image_fetches_only_run_when_url_points_to_image_upload_folder() + { + $page = $this->entities->page(); + $page->html = '' + . '' + . ''; + $storageDisk = Storage::disk('local'); + $storageDisk->makeDirectory('uploads/images/gallery'); + $storageDisk->put('uploads/images/gallery/svg_test.svg', 'good'); + $storageDisk->put('uploads/svg_test.svg', 'bad'); + $page->save(); + + $resp = $this->asEditor()->get($page->getUrl('/export/html')); + + $storageDisk->delete('uploads/images/gallery/svg_test.svg'); + $storageDisk->delete('uploads/svg_test.svg'); + + $resp->assertDontSee('http://localhost/uploads/images/gallery/svg_test.svg', false); + $resp->assertSee('http://localhost/uploads/svg_test.svg'); + $resp->assertSee('src="/uploads/svg_test.svg"', false); + } + + public function test_page_export_contained_html_does_not_allow_upward_traversal_with_local() + { + $contents = file_get_contents(public_path('.htaccess')); + config()->set('filesystems.images', 'local'); + + $page = $this->entities->page(); + $page->html = ''; + $page->save(); + + $resp = $this->asEditor()->get($page->getUrl('/export/html')); + $resp->assertDontSee(base64_encode($contents)); + } + + public function test_page_export_contained_html_does_not_allow_upward_traversal_with_local_secure() + { + $testFilePath = storage_path('logs/test.txt'); + config()->set('filesystems.images', 'local_secure'); + file_put_contents($testFilePath, 'I am a cat'); + + $page = $this->entities->page(); + $page->html = ''; + $page->save(); + + $resp = $this->asEditor()->get($page->getUrl('/export/html')); + $resp->assertDontSee(base64_encode('I am a cat')); + unlink($testFilePath); + } + + public function test_exports_removes_scripts_from_custom_head() + { + $entities = [ + Page::query()->first(), Chapter::query()->first(), Book::query()->first(), + ]; + setting()->put('app-custom-head', ''); + + foreach ($entities as $entity) { + $resp = $this->asEditor()->get($entity->getUrl('/export/html')); + $resp->assertDontSee('window.donkey'); + $resp->assertDontSee('assertSee('.my-test-class { color: red; }'); + } + } + + public function test_page_export_with_deleted_creator_and_updater() + { + $user = $this->users->viewer(['name' => 'ExportWizardTheFifth']); + $page = $this->entities->page(); + $page->created_by = $user->id; + $page->updated_by = $user->id; + $page->save(); + + $resp = $this->asEditor()->get($page->getUrl('/export/html')); + $resp->assertSee('ExportWizardTheFifth'); + + $user->delete(); + $resp = $this->get($page->getUrl('/export/html')); + $resp->assertStatus(200); + $resp->assertDontSee('ExportWizardTheFifth'); + } + + public function test_html_exports_contain_csp_meta_tag() + { + $entities = [ + $this->entities->page(), + $this->entities->book(), + $this->entities->chapter(), + ]; + + foreach ($entities as $entity) { + $resp = $this->asEditor()->get($entity->getUrl('/export/html')); + $this->withHtml($resp)->assertElementExists('head meta[http-equiv="Content-Security-Policy"][content*="script-src "]'); + } + } + + public function test_html_exports_contain_body_classes_for_export_identification() + { + $page = $this->entities->page(); + + $resp = $this->asEditor()->get($page->getUrl('/export/html')); + $this->withHtml($resp)->assertElementExists('body.export.export-format-html.export-engine-none'); + } +} diff --git a/tests/Exports/MarkdownExportTest.php b/tests/Exports/MarkdownExportTest.php new file mode 100644 index 00000000000..3bccd468270 --- /dev/null +++ b/tests/Exports/MarkdownExportTest.php @@ -0,0 +1,97 @@ +entities->page(); + + $resp = $this->asEditor()->get($page->getUrl('/export/markdown')); + $resp->assertStatus(200); + $resp->assertSee($page->name); + $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $page->slug . '.md"'); + } + + public function test_page_markdown_export_uses_existing_markdown_if_apparent() + { + $page = $this->entities->page()->forceFill([ + 'markdown' => '# A header', + 'html' => '

    Dogcat

    ', + ]); + $page->save(); + + $resp = $this->asEditor()->get($page->getUrl('/export/markdown')); + $resp->assertSee('A header'); + $resp->assertDontSee('Dogcat'); + } + + public function test_page_markdown_export_converts_html_where_no_markdown() + { + $page = $this->entities->page()->forceFill([ + 'markdown' => '', + 'html' => '

    Dogcat

    Some bold text

    ', + ]); + $page->save(); + + $resp = $this->asEditor()->get($page->getUrl('/export/markdown')); + $resp->assertSee("# Dogcat\n\nSome **bold** text"); + } + + public function test_chapter_markdown_export() + { + $chapter = $this->entities->chapter(); + $chapter->description_html = '

    My chapter description

    '; + $chapter->save(); + $page = $chapter->pages()->first(); + + $resp = $this->asEditor()->get($chapter->getUrl('/export/markdown')); + + $resp->assertSee('# ' . $chapter->name); + $resp->assertSee('# ' . $page->name); + $resp->assertSee('My **chapter** description'); + } + + public function test_book_markdown_export() + { + $book = Book::query()->whereHas('pages')->whereHas('chapters')->first(); + $book->description_html = '

    My book description

    '; + $book->save(); + + $chapter = $book->chapters()->first(); + $chapter->description_html = '

    My chapter description

    '; + $chapter->save(); + + $page = $chapter->pages()->first(); + $resp = $this->asEditor()->get($book->getUrl('/export/markdown')); + + $resp->assertSee('# ' . $book->name); + $resp->assertSee('# ' . $chapter->name); + $resp->assertSee('# ' . $page->name); + $resp->assertSee('My **book** description'); + $resp->assertSee('My **chapter** description'); + } + + public function test_book_markdown_export_concats_immediate_pages_with_newlines() + { + /** @var Book $book */ + $book = Book::query()->whereHas('pages')->first(); + + $this->asEditor()->get($book->getUrl('/create-page')); + $this->get($book->getUrl('/create-page')); + + [$pageA, $pageB] = $book->pages()->where('chapter_id', '=', 0)->get(); + $pageA->html = '

    hello tester

    '; + $pageA->save(); + $pageB->name = 'The second page in this test'; + $pageB->save(); + + $resp = $this->get($book->getUrl('/export/markdown')); + $resp->assertDontSee('hello tester# The second page in this test'); + $resp->assertSee("hello tester\n\n# The second page in this test"); + } +} diff --git a/tests/Exports/PdfExportTest.php b/tests/Exports/PdfExportTest.php new file mode 100644 index 00000000000..e4de87d0d2f --- /dev/null +++ b/tests/Exports/PdfExportTest.php @@ -0,0 +1,162 @@ +entities->page(); + $this->asEditor(); + + $resp = $this->get($page->getUrl('/export/pdf')); + $resp->assertStatus(200); + $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $page->slug . '.pdf"'); + } + + public function test_book_pdf_export() + { + $page = $this->entities->page(); + $book = $page->book; + $this->asEditor(); + + $resp = $this->get($book->getUrl('/export/pdf')); + $resp->assertStatus(200); + $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $book->slug . '.pdf"'); + } + + public function test_chapter_pdf_export() + { + $chapter = $this->entities->chapter(); + $this->asEditor(); + + $resp = $this->get($chapter->getUrl('/export/pdf')); + $resp->assertStatus(200); + $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $chapter->slug . '.pdf"'); + } + + + public function test_page_pdf_export_converts_iframes_to_links() + { + $page = Page::query()->first()->forceFill([ + 'html' => '', + ]); + $page->save(); + + $pdfHtml = ''; + $mockPdfGenerator = $this->mock(PdfGenerator::class); + $mockPdfGenerator->shouldReceive('fromHtml') + ->with(\Mockery::capture($pdfHtml)) + ->andReturn(''); + $mockPdfGenerator->shouldReceive('getActiveEngine')->andReturn(PdfGenerator::ENGINE_DOMPDF); + + $this->asEditor()->get($page->getUrl('/export/pdf')); + $this->assertStringNotContainsString('iframe>', $pdfHtml); + $this->assertStringContainsString('

    https://www.youtube.com/embed/ShqUjt33uOs

    ', $pdfHtml); + } + + public function test_page_pdf_export_opens_details_blocks() + { + $page = $this->entities->page()->forceFill([ + 'html' => '
    Hello

    Content!

    ', + ]); + $page->save(); + + $pdfHtml = ''; + $mockPdfGenerator = $this->mock(PdfGenerator::class); + $mockPdfGenerator->shouldReceive('fromHtml') + ->with(\Mockery::capture($pdfHtml)) + ->andReturn(''); + $mockPdfGenerator->shouldReceive('getActiveEngine')->andReturn(PdfGenerator::ENGINE_DOMPDF); + + $this->asEditor()->get($page->getUrl('/export/pdf')); + $this->assertStringContainsString('
    entities->page(); + + config()->set('exports.snappy.pdf_binary', '/abc123'); + config()->set('app.allow_untrusted_server_fetching', false); + + $resp = $this->asEditor()->get($page->getUrl('/export/pdf')); + $resp->assertStatus(200); // Sucessful response with invalid snappy binary indicates dompdf usage. + + config()->set('app.allow_untrusted_server_fetching', true); + $resp = $this->get($page->getUrl('/export/pdf')); + $resp->assertStatus(500); // Bad response indicates wkhtml usage + } + + public function test_pdf_command_option_used_if_set() + { + $page = $this->entities->page(); + $command = 'cp {input_html_path} {output_pdf_path}'; + config()->set('exports.pdf_command', $command); + + $resp = $this->asEditor()->get($page->getUrl('/export/pdf')); + $download = $resp->getContent(); + + $this->assertStringContainsString(e($page->name), $download); + $this->assertStringContainsString('set('exports.pdf_command', $command); + + $this->assertThrows(function () use ($page) { + $this->withoutExceptionHandling()->asEditor()->get($page->getUrl('/export/pdf')); + }, PdfExportException::class); + } + + public function test_pdf_command_option_errors_if_command_returns_error_status() + { + $page = $this->entities->page(); + $command = 'exit 1'; + config()->set('exports.pdf_command', $command); + + $this->assertThrows(function () use ($page) { + $this->withoutExceptionHandling()->asEditor()->get($page->getUrl('/export/pdf')); + }, PdfExportException::class); + } + + public function test_pdf_command_timeout_option_limits_export_time() + { + $page = $this->entities->page(); + $command = 'php -r \'sleep(4);\''; + config()->set('exports.pdf_command', $command); + config()->set('exports.pdf_command_timeout', 1); + + $this->assertThrows(function () use ($page) { + $start = time(); + $this->withoutExceptionHandling()->asEditor()->get($page->getUrl('/export/pdf')); + + $this->assertTrue(time() < ($start + 3)); + }, PdfExportException::class, + "PDF Export via command failed due to timeout at 1 second(s)"); + } + + public function test_pdf_command_option_does_not_leave_temp_files() + { + $tempDir = sys_get_temp_dir(); + $startTempFileCount = iterator_count((new FileSystemIterator($tempDir, FilesystemIterator::SKIP_DOTS))); + + $page = $this->entities->page(); + $command = 'cp {input_html_path} {output_pdf_path}'; + config()->set('exports.pdf_command', $command); + + $this->asEditor()->get($page->getUrl('/export/pdf')); + + $afterTempFileCount = iterator_count((new FileSystemIterator($tempDir, FilesystemIterator::SKIP_DOTS))); + $this->assertEquals($startTempFileCount, $afterTempFileCount); + } +} diff --git a/tests/Exports/TextExportTest.php b/tests/Exports/TextExportTest.php new file mode 100644 index 00000000000..c593a6585cb --- /dev/null +++ b/tests/Exports/TextExportTest.php @@ -0,0 +1,88 @@ +entities->page(); + $this->asEditor(); + + $resp = $this->get($page->getUrl('/export/plaintext')); + $resp->assertStatus(200); + $resp->assertSee($page->name); + $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $page->slug . '.txt"'); + } + + public function test_book_text_export() + { + $book = $this->entities->bookHasChaptersAndPages(); + $directPage = $book->directPages()->first(); + $chapter = $book->chapters()->first(); + $chapterPage = $chapter->pages()->first(); + $this->entities->updatePage($directPage, ['html' => '

    My awesome page

    ']); + $this->entities->updatePage($chapterPage, ['html' => '

    My little nested page

    ']); + $this->asEditor(); + + $resp = $this->get($book->getUrl('/export/plaintext')); + $resp->assertStatus(200); + $resp->assertSee($book->name); + $resp->assertSee($chapterPage->name); + $resp->assertSee($chapter->name); + $resp->assertSee($directPage->name); + $resp->assertSee('My awesome page'); + $resp->assertSee('My little nested page'); + $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $book->slug . '.txt"'); + } + + public function test_book_text_export_format() + { + $entities = $this->entities->createChainBelongingToUser($this->users->viewer()); + $this->entities->updatePage($entities['page'], ['html' => '

    My great page

    Full of great stuff

    ', 'name' => 'My wonderful page!']); + $entities['chapter']->name = 'Export chapter'; + $entities['chapter']->description = "A test chapter to be exported\nIt has loads of info within"; + $entities['book']->name = 'Export Book'; + $entities['book']->description = "This is a book with stuff to export"; + $entities['chapter']->save(); + $entities['book']->save(); + + $resp = $this->asEditor()->get($entities['book']->getUrl('/export/plaintext')); + + $expected = "Export Book\nThis is a book with stuff to export\n\nExport chapter\nA test chapter to be exported\nIt has loads of info within\n\n"; + $expected .= "My wonderful page!\nMy great page Full of great stuff"; + $resp->assertSee($expected); + } + + public function test_chapter_text_export() + { + $chapter = $this->entities->chapter(); + $page = $chapter->pages[0]; + $this->entities->updatePage($page, ['html' => '

    This is content within the page!

    ']); + $this->asEditor(); + + $resp = $this->get($chapter->getUrl('/export/plaintext')); + $resp->assertStatus(200); + $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"'); + } + + public function test_chapter_text_export_format() + { + $entities = $this->entities->createChainBelongingToUser($this->users->viewer()); + $this->entities->updatePage($entities['page'], ['html' => '

    My great page

    Full of great stuff

    ', 'name' => 'My wonderful page!']); + $entities['chapter']->name = 'Export chapter'; + $entities['chapter']->description = "A test chapter to be exported\nIt has loads of info within"; + $entities['chapter']->save(); + + $resp = $this->asEditor()->get($entities['book']->getUrl('/export/plaintext')); + + $expected = "Export chapter\nA test chapter to be exported\nIt has loads of info within\n\n"; + $expected .= "My wonderful page!\nMy great page Full of great stuff"; + $resp->assertSee($expected); + } +} diff --git a/tests/Exports/ZipExportTest.php b/tests/Exports/ZipExportTest.php new file mode 100644 index 00000000000..1310dcc2456 --- /dev/null +++ b/tests/Exports/ZipExportTest.php @@ -0,0 +1,447 @@ +entities->page(); + $response = $this->asEditor()->get($page->getUrl("/export/zip")); + + $zipData = $response->streamedContent(); + $zipFile = tempnam(sys_get_temp_dir(), 'bstesta-'); + file_put_contents($zipFile, $zipData); + $zip = new ZipArchive(); + $zip->open($zipFile, ZipArchive::RDONLY); + + $this->assertNotFalse($zip->locateName('data.json')); + $this->assertNotFalse($zip->locateName('files/')); + + $data = json_decode($zip->getFromName('data.json'), true); + $this->assertIsArray($data); + $this->assertGreaterThan(0, count($data)); + + $zip->close(); + unlink($zipFile); + } + + public function test_export_metadata() + { + $page = $this->entities->page(); + $zipResp = $this->asEditor()->get($page->getUrl("/export/zip")); + $zip = ZipTestHelper::extractFromZipResponse($zipResp); + + $this->assertEquals($page->id, $zip->data['page']['id'] ?? null); + $this->assertArrayNotHasKey('book', $zip->data); + $this->assertArrayNotHasKey('chapter', $zip->data); + + $now = time(); + $date = Carbon::parse($zip->data['exported_at'])->unix(); + $this->assertLessThan($now + 2, $date); + $this->assertGreaterThan($now - 2, $date); + + $version = trim(file_get_contents(base_path('version'))); + $this->assertEquals($version, $zip->data['instance']['version']); + + $zipInstanceId = $zip->data['instance']['id']; + $instanceId = setting('instance-id'); + $this->assertNotEmpty($instanceId); + $this->assertEquals($instanceId, $zipInstanceId); + } + + public function test_export_leaves_no_temp_files() + { + $tempDir = sys_get_temp_dir(); + $startTempFileCount = iterator_count((new FileSystemIterator($tempDir, FilesystemIterator::SKIP_DOTS))); + + $page = $this->entities->pageWithinChapter(); + $this->asEditor(); + $pageResp = $this->get($page->getUrl("/export/zip")); + $pageResp->streamedContent(); + $pageResp->assertOk(); + $this->get($page->chapter->getUrl("/export/zip"))->assertOk(); + $this->get($page->book->getUrl("/export/zip"))->assertOk(); + + $afterTempFileCount = iterator_count((new FileSystemIterator($tempDir, FilesystemIterator::SKIP_DOTS))); + + $this->assertEquals($startTempFileCount, $afterTempFileCount); + } + + public function test_page_export() + { + $page = $this->entities->page(); + $zipResp = $this->asEditor()->get($page->getUrl("/export/zip")); + $zip = ZipTestHelper::extractFromZipResponse($zipResp); + + $pageData = $zip->data['page']; + $this->assertEquals([ + 'id' => $page->id, + 'name' => $page->name, + 'html' => (new PageContent($page))->render(), + 'priority' => $page->priority, + 'attachments' => [], + 'images' => [], + 'tags' => [], + ], $pageData); + } + + public function test_page_export_with_markdown() + { + $page = $this->entities->page(); + $markdown = "# My page\n\nwritten in markdown for export\n"; + $page->markdown = $markdown; + $page->save(); + + $zipResp = $this->asEditor()->get($page->getUrl("/export/zip")); + $zip = ZipTestHelper::extractFromZipResponse($zipResp); + + $pageData = $zip->data['page']; + $this->assertEquals($markdown, $pageData['markdown']); + $this->assertNotEmpty($pageData['html']); + } + + public function test_page_export_with_tags() + { + $page = $this->entities->page(); + $page->tags()->saveMany([ + new Tag(['name' => 'Exporty', 'value' => 'Content', 'order' => 1]), + new Tag(['name' => 'Another', 'value' => '', 'order' => 2]), + ]); + + $zipResp = $this->asEditor()->get($page->getUrl("/export/zip")); + $zip = ZipTestHelper::extractFromZipResponse($zipResp); + + $pageData = $zip->data['page']; + $this->assertEquals([ + [ + 'name' => 'Exporty', + 'value' => 'Content', + ], + [ + 'name' => 'Another', + 'value' => '', + ] + ], $pageData['tags']); + } + + public function test_page_export_with_images() + { + $this->asEditor(); + $page = $this->entities->page(); + $result = $this->files->uploadGalleryImageToPage($this, $page); + $displayThumb = $result['response']->thumbs->gallery ?? ''; + $page->html = '

    My image

    '; + $page->save(); + $image = Image::findOrFail($result['response']->id); + + $zipResp = $this->asEditor()->get($page->getUrl("/export/zip")); + $zip = ZipTestHelper::extractFromZipResponse($zipResp); + $pageData = $zip->data['page']; + + $this->assertCount(1, $pageData['images']); + $imageData = $pageData['images'][0]; + $this->assertEquals($image->id, $imageData['id']); + $this->assertEquals($image->name, $imageData['name']); + $this->assertEquals('gallery', $imageData['type']); + $this->assertNotEmpty($imageData['file']); + + $filePath = $zip->extractPath("files/{$imageData['file']}"); + $this->assertFileExists($filePath); + $this->assertEquals(file_get_contents(public_path($image->path)), file_get_contents($filePath)); + + $this->assertEquals('

    My image

    ', $pageData['html']); + } + + public function test_page_export_file_attachments() + { + $contents = 'My great attachment content!'; + + $page = $this->entities->page(); + $this->asAdmin(); + $attachment = $this->files->uploadAttachmentDataToPage($this, $page, 'PageAttachmentExport.txt', $contents, 'text/plain'); + + $zipResp = $this->get($page->getUrl("/export/zip")); + $zip = ZipTestHelper::extractFromZipResponse($zipResp); + + $pageData = $zip->data['page']; + $this->assertCount(1, $pageData['attachments']); + + $attachmentData = $pageData['attachments'][0]; + $this->assertEquals('PageAttachmentExport.txt', $attachmentData['name']); + $this->assertEquals($attachment->id, $attachmentData['id']); + $this->assertArrayNotHasKey('link', $attachmentData); + $this->assertNotEmpty($attachmentData['file']); + + $fileRef = $attachmentData['file']; + $filePath = $zip->extractPath("/files/$fileRef"); + $this->assertFileExists($filePath); + $this->assertEquals($contents, file_get_contents($filePath)); + } + + public function test_page_export_link_attachments() + { + $page = $this->entities->page(); + $this->asEditor(); + $attachment = Attachment::factory()->create([ + 'name' => 'My link attachment for export', + 'path' => 'https://example.com/cats', + 'external' => true, + 'uploaded_to' => $page->id, + 'order' => 1, + ]); + + $zipResp = $this->get($page->getUrl("/export/zip")); + $zip = ZipTestHelper::extractFromZipResponse($zipResp); + + $pageData = $zip->data['page']; + $this->assertCount(1, $pageData['attachments']); + + $attachmentData = $pageData['attachments'][0]; + $this->assertEquals('My link attachment for export', $attachmentData['name']); + $this->assertEquals($attachment->id, $attachmentData['id']); + $this->assertEquals('https://example.com/cats', $attachmentData['link']); + $this->assertArrayNotHasKey('file', $attachmentData); + } + + public function test_book_export() + { + $book = $this->entities->bookHasChaptersAndPages(); + $book->tags()->saveMany(Tag::factory()->count(2)->make()); + + $zipResp = $this->asEditor()->get($book->getUrl("/export/zip")); + $zip = ZipTestHelper::extractFromZipResponse($zipResp); + $this->assertArrayHasKey('book', $zip->data); + + $bookData = $zip->data['book']; + $this->assertEquals($book->id, $bookData['id']); + $this->assertEquals($book->name, $bookData['name']); + $this->assertEquals($book->descriptionHtml(), $bookData['description_html']); + $this->assertCount(2, $bookData['tags']); + $this->assertCount($book->directPages()->count(), $bookData['pages']); + $this->assertCount($book->chapters()->count(), $bookData['chapters']); + $this->assertArrayNotHasKey('cover', $bookData); + } + + public function test_book_export_with_cover_image() + { + $book = $this->entities->book(); + $bookRepo = $this->app->make(BookRepo::class); + $coverImageFile = $this->files->uploadedImage('cover.png'); + $bookRepo->updateCoverImage($book, $coverImageFile); + $coverImage = $book->cover()->first(); + + $zipResp = $this->asEditor()->get($book->getUrl("/export/zip")); + $zip = ZipTestHelper::extractFromZipResponse($zipResp); + + $this->assertArrayHasKey('cover', $zip->data['book']); + $coverRef = $zip->data['book']['cover']; + $coverPath = $zip->extractPath("/files/$coverRef"); + $this->assertFileExists($coverPath); + $this->assertEquals(file_get_contents(public_path($coverImage->path)), file_get_contents($coverPath)); + } + + public function test_chapter_export() + { + $chapter = $this->entities->chapter(); + $chapter->tags()->saveMany(Tag::factory()->count(2)->make()); + + $zipResp = $this->asEditor()->get($chapter->getUrl("/export/zip")); + $zip = ZipTestHelper::extractFromZipResponse($zipResp); + $this->assertArrayHasKey('chapter', $zip->data); + + $chapterData = $zip->data['chapter']; + $this->assertEquals($chapter->id, $chapterData['id']); + $this->assertEquals($chapter->name, $chapterData['name']); + $this->assertEquals($chapter->descriptionHtml(), $chapterData['description_html']); + $this->assertCount(2, $chapterData['tags']); + $this->assertEquals($chapter->priority, $chapterData['priority']); + $this->assertCount($chapter->pages()->count(), $chapterData['pages']); + } + + public function test_draft_pages_are_not_included() + { + $editor = $this->users->editor(); + $entities = $this->entities->createChainBelongingToUser($editor); + $book = $entities['book']; + $page = $entities['page']; + $chapter = $entities['chapter']; + $book->tags()->saveMany(Tag::factory()->count(2)->make()); + + $page->created_by = $editor->id; + $page->draft = true; + $page->save(); + + $zipResp = $this->actingAs($editor)->get($book->getUrl("/export/zip")); + $zip = ZipTestHelper::extractFromZipResponse($zipResp); + $this->assertCount(0, $zip->data['book']['chapters'][0]['pages'] ?? ['cat']); + + $zipResp = $this->actingAs($editor)->get($chapter->getUrl("/export/zip")); + $zip = ZipTestHelper::extractFromZipResponse($zipResp); + $this->assertCount(0, $zip->data['chapter']['pages'] ?? ['cat']); + + $page->chapter_id = 0; + $page->save(); + + $zipResp = $this->actingAs($editor)->get($book->getUrl("/export/zip")); + $zip = ZipTestHelper::extractFromZipResponse($zipResp); + $this->assertCount(0, $zip->data['book']['pages'] ?? ['cat']); + } + + + public function test_cross_reference_links_are_converted() + { + $book = $this->entities->bookHasChaptersAndPages(); + $chapter = $book->chapters()->first(); + $page = $chapter->pages()->first(); + + $book->description_html = '

    Link to chapter

    '; + $book->save(); + $chapter->description_html = '

    Link to page

    '; + $chapter->save(); + $page->html = '

    Link to book

    '; + $page->save(); + + $zipResp = $this->asEditor()->get($book->getUrl("/export/zip")); + $zip = ZipTestHelper::extractFromZipResponse($zipResp); + $bookData = $zip->data['book']; + $chapterData = $bookData['chapters'][0]; + $pageData = $chapterData['pages'][0]; + + $this->assertStringContainsString('href="[[bsexport:chapter:' . $chapter->id . ']]"', $bookData['description_html']); + $this->assertStringContainsString('href="[[bsexport:page:' . $page->id . ']]#section2"', $chapterData['description_html']); + $this->assertStringContainsString('href="[[bsexport:book:' . $book->id . ']]?view=true"', $pageData['html']); + } + + public function test_book_and_chapter_description_links_to_images_in_pages_are_converted() + { + $book = $this->entities->bookHasChaptersAndPages(); + $chapter = $book->chapters()->first(); + $page = $chapter->pages()->first(); + + $this->asEditor(); + $this->files->uploadGalleryImageToPage($this, $page); + /** @var Image $image */ + $image = Image::query()->where('type', '=', 'gallery') + ->where('uploaded_to', '=', $page->id)->first(); + + $book->description_html = '

    Link to image

    '; + $book->save(); + $chapter->description_html = '

    Link to image

    '; + $chapter->save(); + + $zipResp = $this->get($book->getUrl("/export/zip")); + $zip = ZipTestHelper::extractFromZipResponse($zipResp); + $bookData = $zip->data['book']; + $chapterData = $bookData['chapters'][0]; + + $this->assertStringContainsString('href="[[bsexport:image:' . $image->id . ']]"', $bookData['description_html']); + $this->assertStringContainsString('href="[[bsexport:image:' . $image->id . ']]"', $chapterData['description_html']); + } + + public function test_image_links_are_handled_when_using_external_storage_url() + { + $page = $this->entities->page(); + + $this->asEditor(); + $this->files->uploadGalleryImageToPage($this, $page); + /** @var Image $image */ + $image = Image::query()->where('type', '=', 'gallery') + ->where('uploaded_to', '=', $page->id)->first(); + + config()->set('filesystems.url', 'https://i.example.com/content'); + + $storageUrl = 'https://i.example.com/content/' . ltrim($image->path, '/'); + $page->html = '

    Original URLStorage URL

    '; + $page->save(); + + $zipResp = $this->get($page->getUrl("/export/zip")); + $zip = ZipTestHelper::extractFromZipResponse($zipResp); + $pageData = $zip->data['page']; + + $ref = '[[bsexport:image:' . $image->id . ']]'; + $this->assertStringContainsString("Original URLStorage URL", $pageData['html']); + } + + public function test_cross_reference_links_external_to_export_are_not_converted() + { + $page = $this->entities->page(); + $page->html = '

    Link to book

    '; + $page->save(); + + $zipResp = $this->asEditor()->get($page->getUrl("/export/zip")); + $zip = ZipTestHelper::extractFromZipResponse($zipResp); + $pageData = $zip->data['page']; + + $this->assertStringContainsString('href="' . $page->book->getUrl() . '"', $pageData['html']); + } + + public function test_attachments_links_are_converted() + { + $page = $this->entities->page(); + $attachment = Attachment::factory()->create([ + 'name' => 'My link attachment for export reference', + 'path' => 'https://example.com/cats/ref', + 'external' => true, + 'uploaded_to' => $page->id, + 'order' => 1, + ]); + + $page->html = '

    id}") . '?open=true">Link to attachment

    '; + $page->save(); + + $zipResp = $this->asEditor()->get($page->getUrl("/export/zip")); + $zip = ZipTestHelper::extractFromZipResponse($zipResp); + $pageData = $zip->data['page']; + + $this->assertStringContainsString('href="[[bsexport:attachment:' . $attachment->id . ']]?open=true"', $pageData['html']); + } + + public function test_links_in_markdown_are_parsed() + { + $chapter = $this->entities->chapterHasPages(); + $page = $chapter->pages()->first(); + + $page->markdown = "[Link to chapter]({$chapter->getUrl()})"; + $page->save(); + + $zipResp = $this->asEditor()->get($chapter->getUrl("/export/zip")); + $zip = ZipTestHelper::extractFromZipResponse($zipResp); + $pageData = $zip->data['chapter']['pages'][0]; + + $this->assertStringContainsString("[Link to chapter]([[bsexport:chapter:{$chapter->id}]])", $pageData['markdown']); + } + + public function test_exports_rate_limited_low_for_guest_viewers() + { + $this->setSettings(['app-public' => 'true']); + + $page = $this->entities->page(); + for ($i = 0; $i < 4; $i++) { + $this->get($page->getUrl("/export/zip"))->assertOk(); + } + $this->get($page->getUrl("/export/zip"))->assertStatus(429); + } + + public function test_exports_rate_limited_higher_for_logged_in_viewers() + { + $this->asAdmin(); + + $page = $this->entities->page(); + for ($i = 0; $i < 10; $i++) { + $this->get($page->getUrl("/export/zip"))->assertOk(); + } + $this->get($page->getUrl("/export/zip"))->assertStatus(429); + } +} diff --git a/tests/Exports/ZipExportValidatorTest.php b/tests/Exports/ZipExportValidatorTest.php new file mode 100644 index 00000000000..c453ef294d4 --- /dev/null +++ b/tests/Exports/ZipExportValidatorTest.php @@ -0,0 +1,93 @@ +filesToRemove as $file) { + unlink($file); + } + + parent::tearDown(); + } + + protected function getValidatorForData(array $zipData, array $files = []): ZipExportValidator + { + $upload = ZipTestHelper::zipUploadFromData($zipData, $files); + $path = $upload->getRealPath(); + $this->filesToRemove[] = $path; + $reader = new ZipExportReader($path); + return new ZipExportValidator($reader); + } + + public function test_ids_have_to_be_unique() + { + $validator = $this->getValidatorForData([ + 'book' => [ + 'id' => 4, + 'name' => 'My book', + 'pages' => [ + [ + 'id' => 4, + 'name' => 'My page', + 'markdown' => 'hello', + 'attachments' => [ + ['id' => 4, 'name' => 'Attachment A', 'link' => 'https://example.com'], + ['id' => 4, 'name' => 'Attachment B', 'link' => 'https://example.com'] + ], + 'images' => [ + ['id' => 4, 'name' => 'Image A', 'type' => 'gallery', 'file' => 'cat'], + ['id' => 4, 'name' => 'Image b', 'type' => 'gallery', 'file' => 'cat'], + ], + ], + ['id' => 4, 'name' => 'My page', 'markdown' => 'hello'], + ], + 'chapters' => [ + ['id' => 4, 'name' => 'Chapter 1'], + ['id' => 4, 'name' => 'Chapter 2'] + ] + ] + ], ['cat' => $this->files->testFilePath('test-image.png')]); + + $results = $validator->validate(); + $this->assertCount(4, $results); + + $expectedMessage = 'The id must be unique for the object type within the ZIP.'; + $this->assertEquals($expectedMessage, $results['book.pages.0.attachments.1.id']); + $this->assertEquals($expectedMessage, $results['book.pages.0.images.1.id']); + $this->assertEquals($expectedMessage, $results['book.pages.1.id']); + $this->assertEquals($expectedMessage, $results['book.chapters.1.id']); + } + + public function test_image_files_need_to_be_a_valid_detected_image_file() + { + $validator = $this->getValidatorForData([ + 'page' => [ + 'id' => 4, + 'name' => 'My page', + 'markdown' => 'hello', + 'images' => [ + ['id' => 4, 'name' => 'Image A', 'type' => 'gallery', 'file' => 'cat'], + ], + ] + ], ['cat' => $this->files->testFilePath('test-file.txt')]); + + $results = $validator->validate(); + $this->assertCount(1, $results); + + $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']); + } +} diff --git a/tests/Exports/ZipImportRunnerTest.php b/tests/Exports/ZipImportRunnerTest.php new file mode 100644 index 00000000000..68ffb4231bd --- /dev/null +++ b/tests/Exports/ZipImportRunnerTest.php @@ -0,0 +1,434 @@ +runner = app()->make(ZipImportRunner::class); + } + + public function test_book_import() + { + $testImagePath = $this->files->testFilePath('test-image.png'); + $testFilePath = $this->files->testFilePath('test-file.txt'); + $import = ZipTestHelper::importFromData([], [ + 'book' => [ + 'id' => 5, + 'name' => 'Import test', + 'cover' => 'book_cover_image', + 'description_html' => '

    Link to chapter page

    ', + 'tags' => [ + ['name' => 'Animal', 'value' => 'Cat'], + ['name' => 'Category', 'value' => 'Test'], + ], + 'chapters' => [ + [ + 'id' => 6, + 'name' => 'Chapter A', + 'description_html' => '

    Link to book

    ', + 'priority' => 1, + 'tags' => [ + ['name' => 'Reviewed'], + ['name' => 'Category', 'value' => 'Test Chapter'], + ], + 'pages' => [ + [ + 'id' => 3, + 'name' => 'Page A', + 'priority' => 6, + 'html' => ' +

    Link to self

    +

    Link to cat image

    +

    Link to text attachment

    ', + 'tags' => [ + ['name' => 'Unreviewed'], + ], + 'attachments' => [ + [ + 'id' => 4, + 'name' => 'Text attachment', + 'file' => 'file_attachment' + ], + [ + 'name' => 'Cats', + 'link' => 'https://example.com/cats', + ] + ], + 'images' => [ + [ + 'id' => 1, + 'name' => 'Cat', + 'type' => 'gallery', + 'file' => 'cat_image' + ], + [ + 'id' => 2, + 'name' => 'Dog Drawing', + 'type' => 'drawio', + 'file' => 'dog_image' + ] + ], + ], + ], + ], + [ + 'name' => 'Chapter child B', + 'priority' => 5, + ] + ], + 'pages' => [ + [ + 'name' => 'Page C', + 'markdown' => '[Link to text]([[bsexport:attachment:4]]?scale=big)', + 'priority' => 3, + ] + ], + ], + ], [ + 'book_cover_image' => $testImagePath, + 'file_attachment' => $testFilePath, + 'cat_image' => $testImagePath, + 'dog_image' => $testImagePath, + ]); + + $this->asAdmin(); + /** @var Book $book */ + $book = $this->runner->run($import); + + // Book checks + $this->assertEquals('Import test', $book->name); + $this->assertFileExists(public_path($book->cover->path)); + $this->assertCount(2, $book->tags); + $this->assertEquals('Cat', $book->tags()->first()->value); + $this->assertCount(2, $book->chapters); + $this->assertEquals(1, $book->directPages()->count()); + + // Chapter checks + $chapterA = $book->chapters()->where('name', 'Chapter A')->first(); + $this->assertCount(2, $chapterA->tags); + $firstChapterTag = $chapterA->tags()->first(); + $this->assertEquals('Reviewed', $firstChapterTag->name); + $this->assertEquals('', $firstChapterTag->value); + $this->assertCount(1, $chapterA->pages); + + // Page checks + /** @var Page $pageA */ + $pageA = $chapterA->pages->first(); + $this->assertEquals('Page A', $pageA->name); + $this->assertCount(1, $pageA->tags); + $firstPageTag = $pageA->tags()->first(); + $this->assertEquals('Unreviewed', $firstPageTag->name); + $this->assertCount(2, $pageA->attachments); + $firstAttachment = $pageA->attachments->first(); + $this->assertEquals('Text attachment', $firstAttachment->name); + $this->assertFileEquals($testFilePath, storage_path($firstAttachment->path)); + $this->assertFalse($firstAttachment->external); + $secondAttachment = $pageA->attachments->last(); + $this->assertEquals('Cats', $secondAttachment->name); + $this->assertEquals('https://example.com/cats', $secondAttachment->path); + $this->assertTrue($secondAttachment->external); + $pageAImages = Image::where('uploaded_to', '=', $pageA->id)->whereIn('type', ['gallery', 'drawio'])->get(); + $this->assertCount(2, $pageAImages); + $this->assertEquals('Cat', $pageAImages[0]->name); + $this->assertEquals('gallery', $pageAImages[0]->type); + $this->assertFileEquals($testImagePath, public_path($pageAImages[0]->path)); + $this->assertEquals('Dog Drawing', $pageAImages[1]->name); + $this->assertEquals('drawio', $pageAImages[1]->type); + + // Book order check + $children = $book->getDirectVisibleChildren()->values()->all(); + $this->assertEquals($children[0]->name, 'Chapter A'); + $this->assertEquals($children[1]->name, 'Page C'); + $this->assertEquals($children[2]->name, 'Chapter child B'); + + // Reference checks + $textAttachmentUrl = $firstAttachment->getUrl(); + $this->assertStringContainsString($pageA->getUrl(), $book->description_html); + $this->assertStringContainsString($book->getUrl(), $chapterA->description_html); + $this->assertStringContainsString($pageA->getUrl(), $pageA->html); + $this->assertStringContainsString($pageAImages[0]->getThumb(1680, null, true), $pageA->html); + $this->assertStringContainsString($firstAttachment->getUrl(), $pageA->html); + + // Reference in converted markdown + $pageC = $children[1]; + $this->assertStringContainsString("href=\"{$textAttachmentUrl}?scale=big\"", $pageC->html); + + ZipTestHelper::deleteZipForImport($import); + } + + public function test_chapter_import() + { + $testImagePath = $this->files->testFilePath('test-image.png'); + $testFilePath = $this->files->testFilePath('test-file.txt'); + $parent = $this->entities->book(); + + $import = ZipTestHelper::importFromData([], [ + 'chapter' => [ + 'id' => 6, + 'name' => 'Chapter A', + 'description_html' => '

    Link to page

    ', + 'priority' => 1, + 'tags' => [ + ['name' => 'Reviewed', 'value' => '2024'], + ], + 'pages' => [ + [ + 'id' => 3, + 'name' => 'Page A', + 'priority' => 6, + 'html' => '

    Link to chapter

    +

    Link to dog drawing

    +

    Link to text attachment

    ', + 'tags' => [ + ['name' => 'Unreviewed'], + ], + 'attachments' => [ + [ + 'id' => 4, + 'name' => 'Text attachment', + 'file' => 'file_attachment' + ] + ], + 'images' => [ + [ + 'id' => 2, + 'name' => 'Dog Drawing', + 'type' => 'drawio', + 'file' => 'dog_image' + ] + ], + ], + [ + 'name' => 'Page B', + 'markdown' => '[Link to page A]([[bsexport:page:3]])', + 'priority' => 9, + ], + ], + ], + ], [ + 'file_attachment' => $testFilePath, + 'dog_image' => $testImagePath, + ]); + + $this->asAdmin(); + /** @var Chapter $chapter */ + $chapter = $this->runner->run($import, $parent); + + // Chapter checks + $this->assertEquals('Chapter A', $chapter->name); + $this->assertEquals($parent->id, $chapter->book_id); + $this->assertCount(1, $chapter->tags); + $firstChapterTag = $chapter->tags()->first(); + $this->assertEquals('Reviewed', $firstChapterTag->name); + $this->assertEquals('2024', $firstChapterTag->value); + $this->assertCount(2, $chapter->pages); + + // Page checks + /** @var Page $pageA */ + $pageA = $chapter->pages->first(); + $this->assertEquals('Page A', $pageA->name); + $this->assertCount(1, $pageA->tags); + $this->assertCount(1, $pageA->attachments); + $pageAImages = Image::where('uploaded_to', '=', $pageA->id)->whereIn('type', ['gallery', 'drawio'])->get(); + $this->assertCount(1, $pageAImages); + + // Reference checks + $attachment = $pageA->attachments->first(); + $this->assertStringContainsString($pageA->getUrl(), $chapter->description_html); + $this->assertStringContainsString($chapter->getUrl(), $pageA->html); + $this->assertStringContainsString($pageAImages[0]->url, $pageA->html); + $this->assertStringContainsString($attachment->getUrl(), $pageA->html); + + ZipTestHelper::deleteZipForImport($import); + } + + public function test_page_import() + { + $testImagePath = $this->files->testFilePath('test-image.png'); + $testFilePath = $this->files->testFilePath('test-file.txt'); + $parent = $this->entities->chapter(); + + $import = ZipTestHelper::importFromData([], [ + 'page' => [ + 'id' => 3, + 'name' => 'Page A', + 'priority' => 6, + 'html' => '

    Link to self

    +

    Link to dog drawing

    +

    Link to text attachment

    ', + 'tags' => [ + ['name' => 'Unreviewed'], + ], + 'attachments' => [ + [ + 'id' => 4, + 'name' => 'Text attachment', + 'file' => 'file_attachment' + ] + ], + 'images' => [ + [ + 'id' => 2, + 'name' => 'Dog Drawing', + 'type' => 'drawio', + 'file' => 'dog_image' + ] + ], + ], + ], [ + 'file_attachment' => $testFilePath, + 'dog_image' => $testImagePath, + ]); + + $this->asAdmin(); + /** @var Page $page */ + $page = $this->runner->run($import, $parent); + + // Page checks + $this->assertEquals('Page A', $page->name); + $this->assertCount(1, $page->tags); + $this->assertCount(1, $page->attachments); + $pageImages = Image::where('uploaded_to', '=', $page->id)->whereIn('type', ['gallery', 'drawio'])->get(); + $this->assertCount(1, $pageImages); + $this->assertFileEquals($testImagePath, public_path($pageImages[0]->path)); + + // Reference checks + $this->assertStringContainsString($page->getUrl(), $page->html); + $this->assertStringContainsString($pageImages[0]->url, $page->html); + $this->assertStringContainsString($page->attachments->first()->getUrl(), $page->html); + + ZipTestHelper::deleteZipForImport($import); + } + + public function test_revert_cleans_up_uploaded_files() + { + $testImagePath = $this->files->testFilePath('test-image.png'); + $testFilePath = $this->files->testFilePath('test-file.txt'); + $parent = $this->entities->chapter(); + + $import = ZipTestHelper::importFromData([], [ + 'page' => [ + 'name' => 'Page A', + 'html' => '

    Hello

    ', + 'attachments' => [ + [ + 'name' => 'Text attachment', + 'file' => 'file_attachment' + ] + ], + 'images' => [ + [ + 'name' => 'Dog Image', + 'type' => 'gallery', + 'file' => 'dog_image' + ] + ], + ], + ], [ + 'file_attachment' => $testFilePath, + 'dog_image' => $testImagePath, + ]); + + $this->asAdmin(); + /** @var Page $page */ + $page = $this->runner->run($import, $parent); + + $attachment = $page->attachments->first(); + $image = Image::query()->where('uploaded_to', '=', $page->id)->where('type', '=', 'gallery')->first(); + + $this->assertFileExists(public_path($image->path)); + $this->assertFileExists(storage_path($attachment->path)); + + $this->runner->revertStoredFiles(); + + $this->assertFileDoesNotExist(public_path($image->path)); + $this->assertFileDoesNotExist(storage_path($attachment->path)); + + ZipTestHelper::deleteZipForImport($import); + } + + public function test_imported_images_have_their_detected_extension_added() + { + $testImagePath = $this->files->testFilePath('test-image.png'); + $parent = $this->entities->chapter(); + + $import = ZipTestHelper::importFromData([], [ + 'page' => [ + 'name' => 'Page A', + 'html' => '

    hello

    ', + 'images' => [ + [ + 'id' => 2, + 'name' => 'Cat', + 'type' => 'gallery', + 'file' => 'cat_image' + ] + ], + ], + ], [ + 'cat_image' => $testImagePath, + ]); + + $this->asAdmin(); + /** @var Page $page */ + $page = $this->runner->run($import, $parent); + + $pageImages = Image::where('uploaded_to', '=', $page->id)->whereIn('type', ['gallery', 'drawio'])->get(); + + $this->assertCount(1, $pageImages); + $this->assertStringEndsWith('.png', $pageImages[0]->url); + $this->assertStringEndsWith('.png', $pageImages[0]->path); + + ZipTestHelper::deleteZipForImport($import); + } + + public function test_drawing_references_are_updated_within_content() + { + $testImagePath = $this->files->testFilePath('test-image.png'); + $parent = $this->entities->chapter(); + + $import = ZipTestHelper::importFromData([], [ + 'page' => [ + 'name' => 'Page A', + 'html' => '
    ', + 'images' => [ + [ + 'id' => 1125, + 'name' => 'Cat', + 'type' => 'drawio', + 'file' => 'my_drawing' + ] + ], + ], + ], [ + 'my_drawing' => $testImagePath, + ]); + + $this->asAdmin(); + /** @var Page $page */ + $page = $this->runner->run($import, $parent); + + $pageImages = Image::where('uploaded_to', '=', $page->id)->whereIn('type', ['gallery', 'drawio'])->get(); + $this->assertCount(1, $pageImages); + $this->assertEquals('drawio', $pageImages[0]->type); + + $drawingId = $pageImages[0]->id; + $this->assertStringContainsString("drawio-diagram=\"{$drawingId}\"", $page->html); + $this->assertStringNotContainsString('[[bsexport:image:1125]]', $page->html); + $this->assertStringNotContainsString('drawio-diagram="1125"', $page->html); + + ZipTestHelper::deleteZipForImport($import); + } +} diff --git a/tests/Exports/ZipImportTest.php b/tests/Exports/ZipImportTest.php new file mode 100644 index 00000000000..54e589aa39e --- /dev/null +++ b/tests/Exports/ZipImportTest.php @@ -0,0 +1,396 @@ +asAdmin()->get('/import'); + $resp->assertSee('Import'); + $this->withHtml($resp)->assertElementExists('form input[type="file"][name="file"]'); + } + + public function test_permissions_needed_for_import_page() + { + $user = $this->users->viewer(); + $this->actingAs($user); + + $resp = $this->get('/books'); + $this->withHtml($resp)->assertLinkNotExists(url('/import')); + $resp = $this->get('/import'); + $resp->assertRedirect('/'); + + $this->permissions->grantUserRolePermissions($user, ['content-import']); + + $resp = $this->get('/books'); + $this->withHtml($resp)->assertLinkExists(url('/import')); + $resp = $this->get('/import'); + $resp->assertOk(); + $resp->assertSeeText('Select ZIP file to upload'); + } + + public function test_import_page_pending_import_visibility_limited() + { + $user = $this->users->viewer(); + $admin = $this->users->admin(); + $userImport = Import::factory()->create(['name' => 'MySuperUserImport', 'created_by' => $user->id]); + $adminImport = Import::factory()->create(['name' => 'MySuperAdminImport', 'created_by' => $admin->id]); + $this->permissions->grantUserRolePermissions($user, ['content-import']); + + $resp = $this->actingAs($user)->get('/import'); + $resp->assertSeeText('MySuperUserImport'); + $resp->assertDontSeeText('MySuperAdminImport'); + + $this->permissions->grantUserRolePermissions($user, ['settings-manage']); + + $resp = $this->actingAs($user)->get('/import'); + $resp->assertSeeText('MySuperUserImport'); + $resp->assertSeeText('MySuperAdminImport'); + } + + public function test_zip_read_errors_are_shown_on_validation() + { + $invalidUpload = $this->files->uploadedImage('image.zip'); + + $this->asAdmin(); + $resp = $this->runImportFromFile($invalidUpload); + $resp->assertRedirect('/import'); + + $resp = $this->followRedirects($resp); + $resp->assertSeeText('Could not read ZIP file'); + } + + public function test_error_shown_if_missing_data() + { + $zipFile = tempnam(sys_get_temp_dir(), 'bstest-'); + $zip = new ZipArchive(); + $zip->open($zipFile, ZipArchive::OVERWRITE); + $zip->addFromString('beans', 'cat'); + $zip->close(); + + $this->asAdmin(); + $upload = new UploadedFile($zipFile, 'upload.zip', 'application/zip', null, true); + $resp = $this->runImportFromFile($upload); + $resp->assertRedirect('/import'); + + $resp = $this->followRedirects($resp); + $resp->assertSeeText('Could not find and decode ZIP data.json content.'); + } + + public function test_error_shown_if_no_importable_key() + { + $this->asAdmin(); + $resp = $this->runImportFromFile(ZipTestHelper::zipUploadFromData([ + 'instance' => [] + ])); + + $resp->assertRedirect('/import'); + $resp = $this->followRedirects($resp); + $resp->assertSeeText('ZIP file data has no expected book, chapter or page content.'); + } + + public function test_zip_data_validation_messages_shown() + { + $this->asAdmin(); + $resp = $this->runImportFromFile(ZipTestHelper::zipUploadFromData([ + 'book' => [ + 'id' => 4, + 'pages' => [ + 'cat', + [ + 'name' => 'My inner page', + 'tags' => [ + [ + 'value' => 5 + ] + ], + ] + ], + ] + ])); + + $resp->assertRedirect('/import'); + $resp = $this->followRedirects($resp); + + $resp->assertSeeText('[book.name]: The name field is required.'); + $resp->assertSeeText('[book.pages.0.0]: Data object expected but "string" found.'); + $resp->assertSeeText('[book.pages.1.tags.0.name]: The name field is required.'); + $resp->assertSeeText('[book.pages.1.tags.0.value]: The value must be a string.'); + } + + public function test_import_upload_success() + { + $admin = $this->users->admin(); + $this->actingAs($admin); + $data = [ + 'book' => [ + 'name' => 'My great book name', + 'chapters' => [ + [ + 'name' => 'my chapter', + 'pages' => [ + [ + 'name' => 'my chapter page', + ] + ] + ] + ], + 'pages' => [ + [ + 'name' => 'My page', + ] + ], + ], + ]; + + $resp = $this->runImportFromFile(ZipTestHelper::zipUploadFromData($data)); + + $this->assertDatabaseHas('imports', [ + 'name' => 'My great book name', + 'type' => 'book', + 'created_by' => $admin->id, + ]); + + /** @var Import $import */ + $import = Import::query()->latest()->first(); + $resp->assertRedirect("/import/{$import->id}"); + $this->assertFileExists(storage_path($import->path)); + $this->assertActivityExists(ActivityType::IMPORT_CREATE); + + ZipTestHelper::deleteZipForImport($import); + } + + public function test_import_show_page() + { + $exportBook = new ZipExportBook(); + $exportBook->name = 'My exported book'; + $exportChapter = new ZipExportChapter(); + $exportChapter->name = 'My exported chapter'; + $exportPage = new ZipExportPage(); + $exportPage->name = 'My exported page'; + $exportBook->chapters = [$exportChapter]; + $exportChapter->pages = [$exportPage]; + + $import = Import::factory()->create([ + 'name' => 'MySuperAdminImport', + 'metadata' => json_encode($exportBook) + ]); + + $resp = $this->asAdmin()->get("/import/{$import->id}"); + $resp->assertOk(); + $resp->assertSeeText('My exported book'); + $resp->assertSeeText('My exported chapter'); + $resp->assertSeeText('My exported page'); + } + + public function test_import_show_page_access_limited() + { + $user = $this->users->viewer(); + $admin = $this->users->admin(); + $userImport = Import::factory()->create(['name' => 'MySuperUserImport', 'created_by' => $user->id]); + $adminImport = Import::factory()->create(['name' => 'MySuperAdminImport', 'created_by' => $admin->id]); + $this->actingAs($user); + + $this->get("/import/{$userImport->id}")->assertRedirect('/'); + $this->get("/import/{$adminImport->id}")->assertRedirect('/'); + + $this->permissions->grantUserRolePermissions($user, ['content-import']); + + $this->get("/import/{$userImport->id}")->assertOk(); + $this->get("/import/{$adminImport->id}")->assertStatus(404); + + $this->permissions->grantUserRolePermissions($user, ['settings-manage']); + + $this->get("/import/{$userImport->id}")->assertOk(); + $this->get("/import/{$adminImport->id}")->assertOk(); + } + + public function test_import_delete() + { + $this->asAdmin(); + $this->runImportFromFile(ZipTestHelper::zipUploadFromData([ + 'book' => [ + 'name' => 'My great book name' + ], + ])); + + /** @var Import $import */ + $import = Import::query()->latest()->first(); + $this->assertDatabaseHas('imports', [ + 'id' => $import->id, + 'name' => 'My great book name' + ]); + $this->assertFileExists(storage_path($import->path)); + + $resp = $this->delete("/import/{$import->id}"); + + $resp->assertRedirect('/import'); + $this->assertActivityExists(ActivityType::IMPORT_DELETE); + $this->assertDatabaseMissing('imports', [ + 'id' => $import->id, + ]); + $this->assertFileDoesNotExist(storage_path($import->path)); + } + + public function test_import_delete_access_limited() + { + $user = $this->users->viewer(); + $admin = $this->users->admin(); + $userImport = Import::factory()->create(['name' => 'MySuperUserImport', 'created_by' => $user->id]); + $adminImport = Import::factory()->create(['name' => 'MySuperAdminImport', 'created_by' => $admin->id]); + $this->actingAs($user); + + $this->delete("/import/{$userImport->id}")->assertRedirect('/'); + $this->delete("/import/{$adminImport->id}")->assertRedirect('/'); + + $this->permissions->grantUserRolePermissions($user, ['content-import']); + + $this->delete("/import/{$userImport->id}")->assertRedirect('/import'); + $this->delete("/import/{$adminImport->id}")->assertStatus(404); + + $this->permissions->grantUserRolePermissions($user, ['settings-manage']); + + $this->delete("/import/{$adminImport->id}")->assertRedirect('/import'); + } + + public function test_run_simple_success_scenario() + { + $import = ZipTestHelper::importFromData([], [ + 'book' => [ + 'name' => 'My imported book', + 'pages' => [ + [ + 'name' => 'My imported book page', + 'html' => '

    Hello there from child page!

    ' + ] + ], + ] + ]); + + $resp = $this->asAdmin()->post("/import/{$import->id}"); + $book = Book::query()->where('name', '=', 'My imported book')->latest()->first(); + $resp->assertRedirect($book->getUrl()); + + $resp = $this->followRedirects($resp); + $resp->assertSee('My imported book page'); + $resp->assertSee('Hello there from child page!'); + + $this->assertDatabaseMissing('imports', ['id' => $import->id]); + $this->assertFileDoesNotExist(storage_path($import->path)); + $this->assertActivityExists(ActivityType::IMPORT_RUN, null, $import->logDescriptor()); + } + + public function test_import_run_access_limited() + { + $user = $this->users->editor(); + $admin = $this->users->admin(); + $userImport = Import::factory()->create(['name' => 'MySuperUserImport', 'created_by' => $user->id]); + $adminImport = Import::factory()->create(['name' => 'MySuperAdminImport', 'created_by' => $admin->id]); + $this->actingAs($user); + + $this->post("/import/{$userImport->id}")->assertRedirect('/'); + $this->post("/import/{$adminImport->id}")->assertRedirect('/'); + + $this->permissions->grantUserRolePermissions($user, ['content-import']); + + $this->post("/import/{$userImport->id}")->assertRedirect($userImport->getUrl()); // Getting validation response instead of access issue response + $this->post("/import/{$adminImport->id}")->assertStatus(404); + + $this->permissions->grantUserRolePermissions($user, ['settings-manage']); + + $this->post("/import/{$adminImport->id}")->assertRedirect($adminImport->getUrl()); // Getting validation response instead of access issue response + } + + public function test_run_revalidates_content() + { + $import = ZipTestHelper::importFromData([], [ + 'book' => [ + 'id' => 'abc', + ] + ]); + + $resp = $this->asAdmin()->post("/import/{$import->id}"); + $resp->assertRedirect($import->getUrl()); + + $resp = $this->followRedirects($resp); + $resp->assertSeeText('The name field is required.'); + $resp->assertSeeText('The id must be an integer.'); + + ZipTestHelper::deleteZipForImport($import); + } + + public function test_run_checks_permissions_on_import() + { + $viewer = $this->users->viewer(); + $this->permissions->grantUserRolePermissions($viewer, ['content-import']); + $import = ZipTestHelper::importFromData(['created_by' => $viewer->id], [ + 'book' => ['name' => 'My import book'], + ]); + + $resp = $this->asViewer()->post("/import/{$import->id}"); + $resp->assertRedirect($import->getUrl()); + + $resp = $this->followRedirects($resp); + $resp->assertSeeText('You are lacking the required permissions to create books.'); + + ZipTestHelper::deleteZipForImport($import); + } + + public function test_run_requires_parent_for_chapter_and_page_imports() + { + $book = $this->entities->book(); + $pageImport = ZipTestHelper::importFromData([], [ + 'page' => ['name' => 'My page', 'html' => '

    page test!

    '], + ]); + $chapterImport = ZipTestHelper::importFromData([], [ + 'chapter' => ['name' => 'My chapter'], + ]); + + $resp = $this->asAdmin()->post("/import/{$pageImport->id}"); + $resp->assertRedirect($pageImport->getUrl()); + $this->followRedirects($resp)->assertSee('The parent field is required.'); + + $resp = $this->asAdmin()->post("/import/{$pageImport->id}", ['parent' => "book:{$book->id}"]); + $resp->assertRedirectContains($book->getUrl()); + + $resp = $this->asAdmin()->post("/import/{$chapterImport->id}"); + $resp->assertRedirect($chapterImport->getUrl()); + $this->followRedirects($resp)->assertSee('The parent field is required.'); + + $resp = $this->asAdmin()->post("/import/{$chapterImport->id}", ['parent' => "book:{$book->id}"]); + $resp->assertRedirectContains($book->getUrl()); + } + + public function test_run_validates_correct_parent_type() + { + $chapter = $this->entities->chapter(); + $import = ZipTestHelper::importFromData([], [ + 'chapter' => ['name' => 'My chapter'], + ]); + + $resp = $this->asAdmin()->post("/import/{$import->id}", ['parent' => "chapter:{$chapter->id}"]); + $resp->assertRedirect($import->getUrl()); + + $resp = $this->followRedirects($resp); + $resp->assertSee('Parent book required for chapter import.'); + + ZipTestHelper::deleteZipForImport($import); + } + + protected function runImportFromFile(UploadedFile $file): TestResponse + { + return $this->call('POST', '/import', [], [], ['file' => $file]); + } +} diff --git a/tests/Exports/ZipResultData.php b/tests/Exports/ZipResultData.php new file mode 100644 index 00000000000..7725004c7be --- /dev/null +++ b/tests/Exports/ZipResultData.php @@ -0,0 +1,22 @@ +extractedDirPath . DIRECTORY_SEPARATOR . ltrim($relPath, DIRECTORY_SEPARATOR); + } +} diff --git a/tests/Exports/ZipTestHelper.php b/tests/Exports/ZipTestHelper.php new file mode 100644 index 00000000000..c5a7235a0c4 --- /dev/null +++ b/tests/Exports/ZipTestHelper.php @@ -0,0 +1,86 @@ +create($importData); + $zip = static::zipUploadFromData($zipData, $files); + $targetPath = storage_path($import->path); + $targetDir = dirname($targetPath); + + if (!file_exists($targetDir)) { + mkdir($targetDir); + } + + rename($zip->getRealPath(), $targetPath); + + return $import; + } + + public static function deleteZipForImport(Import $import): void + { + $path = storage_path($import->path); + if (file_exists($path)) { + unlink($path); + } + } + + public static function zipUploadFromData(array $data, array $files = []): UploadedFile + { + $zipFile = tempnam(sys_get_temp_dir(), 'bstest-'); + + $zip = new ZipArchive(); + $zip->open($zipFile, ZipArchive::OVERWRITE); + $zip->addFromString('data.json', json_encode($data)); + + foreach ($files as $name => $file) { + $zip->addFile($file, "files/$name"); + } + + $zip->close(); + + return new UploadedFile($zipFile, 'upload.zip', 'application/zip', null, true); + } + + public static function extractFromZipResponse(TestResponse $response): ZipResultData + { + $zipData = $response->streamedContent(); + $zipFile = tempnam(sys_get_temp_dir(), 'bstest-'); + + file_put_contents($zipFile, $zipData); + $extractDir = tempnam(sys_get_temp_dir(), 'bstestextracted-'); + if (file_exists($extractDir)) { + unlink($extractDir); + } + mkdir($extractDir); + + $zip = new ZipArchive(); + $zip->open($zipFile, ZipArchive::RDONLY); + $zip->extractTo($extractDir); + + $dataJson = file_get_contents($extractDir . DIRECTORY_SEPARATOR . "data.json"); + $data = json_decode($dataJson, true); + + return new ZipResultData( + $zipFile, + $extractDir, + $data, + ); + } +} diff --git a/tests/Helpers/EntityProvider.php b/tests/Helpers/EntityProvider.php index 98206342145..c794f9478ab 100644 --- a/tests/Helpers/EntityProvider.php +++ b/tests/Helpers/EntityProvider.php @@ -33,9 +33,9 @@ class EntityProvider ]; /** - * Get an un-fetched page from the system. + * Get an unfetched page from the system. */ - public function page(callable $queryFilter = null): Page + public function page(callable|null $queryFilter = null): Page { /** @var Page $page */ $page = Page::query()->when($queryFilter, $queryFilter)->whereNotIn('id', $this->fetchCache['page'])->first(); @@ -63,9 +63,9 @@ public function templatePage(): Page } /** - * Get an un-fetched chapter from the system. + * Get an unfetched chapter from the system. */ - public function chapter(callable $queryFilter = null): Chapter + public function chapter(callable|null $queryFilter = null): Chapter { /** @var Chapter $chapter */ $chapter = Chapter::query()->when($queryFilter, $queryFilter)->whereNotIn('id', $this->fetchCache['chapter'])->first(); @@ -79,9 +79,9 @@ public function chapterHasPages(): Chapter } /** - * Get an un-fetched book from the system. + * Get an unfetched book from the system. */ - public function book(callable $queryFilter = null): Book + public function book(callable|null $queryFilter = null): Book { /** @var Book $book */ $book = Book::query()->when($queryFilter, $queryFilter)->whereNotIn('id', $this->fetchCache['book'])->first(); @@ -100,9 +100,9 @@ public function bookHasChaptersAndPages(): Book } /** - * Get an un-fetched shelf from the system. + * Get an unfetched shelf from the system. */ - public function shelf(callable $queryFilter = null): Bookshelf + public function shelf(callable|null $queryFilter = null): Bookshelf { /** @var Bookshelf $shelf */ $shelf = Bookshelf::query()->when($queryFilter, $queryFilter)->whereNotIn('id', $this->fetchCache['bookshelf'])->first(); @@ -207,6 +207,29 @@ public function newDraftPage(array $input = ['name' => 'test page', 'html' => 'M return $draftPage; } + /** + * Send an entity to the recycle bin. + */ + public function sendToRecycleBin(Entity $entity) + { + $trash = app()->make(TrashCan::class); + + if ($entity instanceof Page) { + $trash->softDestroyPage($entity); + } elseif ($entity instanceof Chapter) { + $trash->softDestroyChapter($entity); + } elseif ($entity instanceof Book) { + $trash->softDestroyBook($entity); + } elseif ($entity instanceof Bookshelf) { + $trash->softDestroyBookshelf($entity); + } + + $entity->refresh(); + if (is_null($entity->deleted_at)) { + throw new \Exception("Could not send entity type [{$entity->getMorphClass()}] to the recycle bin"); + } + } + /** * Fully destroy the given entity from the system, bypassing the recycle bin * stage. Still runs through main app deletion logic. diff --git a/tests/Helpers/FileProvider.php b/tests/Helpers/FileProvider.php index 442e036ff24..a455e0fb4ea 100644 --- a/tests/Helpers/FileProvider.php +++ b/tests/Helpers/FileProvider.php @@ -60,6 +60,14 @@ public function pngImageData(): string return file_get_contents($this->testFilePath('test-image.png')); } + /** + * Get raw data for a Jpeg image test file. + */ + public function jpegImageData(): string + { + return file_get_contents($this->testFilePath('test-image.jpg')); + } + /** * Get the expected relative path for an uploaded image of the given type and filename. */ diff --git a/tests/Helpers/PermissionsProvider.php b/tests/Helpers/PermissionsProvider.php index cb036fe97c7..c9ae309196e 100644 --- a/tests/Helpers/PermissionsProvider.php +++ b/tests/Helpers/PermissionsProvider.php @@ -5,6 +5,7 @@ use BookStack\Entities\Models\Entity; use BookStack\Permissions\Models\EntityPermission; use BookStack\Permissions\Models\RolePermission; +use BookStack\Permissions\Permission; use BookStack\Settings\SettingService; use BookStack\Users\Models\Role; use BookStack\Users\Models\User; @@ -139,8 +140,8 @@ protected function addEntityPermissionEntries(Entity $entity, array $entityPermi protected function actionListToEntityPermissionData(array $actionList, int $roleId = 0): array { $permissionData = ['role_id' => $roleId]; - foreach (EntityPermission::PERMISSIONS as $possibleAction) { - $permissionData[$possibleAction] = in_array($possibleAction, $actionList); + foreach (Permission::genericForEntity() as $permission) { + $permissionData[$permission->value] = in_array($permission->value, $actionList); } return $permissionData; diff --git a/tests/LanguageTest.php b/tests/LanguageTest.php index 6b6856184c9..28491c3af9d 100644 --- a/tests/LanguageTest.php +++ b/tests/LanguageTest.php @@ -27,15 +27,18 @@ public function test_locales_list_set_properly() } // Not part of standard phpunit test runs since we sometimes expect non-added langs. - public function do_test_locales_all_have_language_dropdown_entry() + public function test_locales_all_have_language_dropdown_entry() { + $this->markTestSkipped('Only used when checking language inclusion'); + $dropdownLocales = array_keys(trans('settings.language_select', [], 'en')); sort($dropdownLocales); sort($this->langs); $diffs = array_diff($this->langs, $dropdownLocales); if (count($diffs) > 0) { $diffText = implode(',', $diffs); - $this->addWarning("Languages: {$diffText} found in files but not in language select dropdown."); + $warning = "Languages: {$diffText} found in files but not in language select dropdown."; + $this->fail($warning); } $this->assertTrue(true); } diff --git a/tests/HelpTest.php b/tests/Meta/HelpTest.php similarity index 59% rename from tests/HelpTest.php rename to tests/Meta/HelpTest.php index 9cf80717fe0..acce65394a3 100644 --- a/tests/HelpTest.php +++ b/tests/Meta/HelpTest.php @@ -1,12 +1,14 @@ get('/help/wysiwyg'); + $resp = $this->get('/help/tinymce'); $resp->assertOk(); $this->withHtml($resp)->assertElementExists('a[href="https://www.tiny.cloud/"]'); $this->withHtml($resp)->assertElementExists('a[href="' . url('/libs/tinymce/license.txt') . '"]'); @@ -20,4 +22,12 @@ public function test_tiny_license_exists_where_expected() $contents = file_get_contents($expectedPath); $this->assertStringContainsString('MIT License', $contents); } + + public function test_wysiwyg_help_shows_lexical_and_licenses_link() + { + $resp = $this->get('/help/wysiwyg'); + $resp->assertOk(); + $this->withHtml($resp)->assertElementExists('a[href="https://lexical.dev/"]'); + $this->withHtml($resp)->assertElementExists('a[href="' . url('/licenses') . '"]'); + } } diff --git a/tests/Meta/LicensesTest.php b/tests/Meta/LicensesTest.php new file mode 100644 index 00000000000..a50fbdb9095 --- /dev/null +++ b/tests/Meta/LicensesTest.php @@ -0,0 +1,26 @@ +get('/licenses'); + $resp->assertOk(); + $resp->assertSee('Licenses'); + $resp->assertSee('PHP Library Licenses'); + $resp->assertSee('Dan Brown and the BookStack project contributors'); + $resp->assertSee('league/commonmark'); + $resp->assertSee('@codemirror/lang-html'); + } + + public function test_licenses_linked_to_from_settings() + { + $resp = $this->asAdmin()->get('/settings/features'); + $html = $this->withHtml($resp); + $html->assertLinkExists(url('/licenses'), 'License Details'); + } +} diff --git a/tests/OpenGraphTest.php b/tests/Meta/OpenGraphTest.php similarity index 98% rename from tests/OpenGraphTest.php rename to tests/Meta/OpenGraphTest.php index 545fa4c864c..96e622da052 100644 --- a/tests/OpenGraphTest.php +++ b/tests/Meta/OpenGraphTest.php @@ -1,11 +1,12 @@ put('app-name', $appName); + $resultUrl = url('/search') . '?term={searchTerms}'; + $selfUrl = url('/opensearch.xml'); + + $resp = $this->get('/opensearch.xml'); + $resp->assertOk(); + $resp->assertSee('' . "\n", false); + + $html = $this->withHtml($resp); + + $html->assertElementExists('OpenSearchDescription > ShortName'); + $html->assertElementContains('OpenSearchDescription > ShortName', mb_strimwidth($appName, 0, 16)); + $html->assertElementNotContains('OpenSearchDescription > ShortName', $appName); + + $html->assertElementExists('OpenSearchDescription > Description'); + $html->assertElementContains('OpenSearchDescription > Description', "Search {$appName}"); + $html->assertElementExists('OpenSearchDescription > Image'); + $html->assertElementExists('OpenSearchDescription > Url[rel="results"][template="' . htmlspecialchars($resultUrl) . '"]'); + $html->assertElementExists('OpenSearchDescription > Url[rel="self"][template="' . htmlspecialchars($selfUrl) . '"]'); + } + + public function test_opensearch_linked_to_from_home() + { + $appName = setting('app-name'); + $endpointUrl = url('/opensearch.xml'); + + $resp = $this->asViewer()->get('/'); + $html = $this->withHtml($resp); + + $html->assertElementExists('head > link[rel="search"][type="application/opensearchdescription+xml"][title="' . htmlspecialchars($appName) . '"][href="' . htmlspecialchars($endpointUrl) . '"]'); + } +} diff --git a/tests/PwaManifestTest.php b/tests/Meta/PwaManifestTest.php similarity index 98% rename from tests/PwaManifestTest.php rename to tests/Meta/PwaManifestTest.php index c66f8b36052..fc6d19b5459 100644 --- a/tests/PwaManifestTest.php +++ b/tests/Meta/PwaManifestTest.php @@ -1,6 +1,8 @@ get('/robots.txt')->assertSee("User-agent: *\nDisallow: /"); + + $this->setSettings(['app-public' => 'true']); + + $resp = $this->get('/robots.txt'); + $resp->assertSee("User-agent: *\nDisallow:"); + $resp->assertDontSee('Disallow: /'); + } + + public function test_robots_effected_by_setting() + { + $this->get('/robots.txt')->assertSee("User-agent: *\nDisallow: /"); + + config()->set('app.allow_robots', true); + + $resp = $this->get('/robots.txt'); + $resp->assertSee("User-agent: *\nDisallow:"); + $resp->assertDontSee('Disallow: /'); + + // Check config overrides app-public setting + config()->set('app.allow_robots', false); + $this->setSettings(['app-public' => 'true']); + $this->get('/robots.txt')->assertSee("User-agent: *\nDisallow: /"); + } +} diff --git a/tests/Permissions/EntityPermissionsTest.php b/tests/Permissions/EntityPermissionsTest.php index 6ea0257b81e..ec2756b1213 100644 --- a/tests/Permissions/EntityPermissionsTest.php +++ b/tests/Permissions/EntityPermissionsTest.php @@ -7,6 +7,7 @@ use BookStack\Entities\Models\Chapter; use BookStack\Entities\Models\Entity; use BookStack\Entities\Models\Page; +use BookStack\Permissions\Permission; use BookStack\Users\Models\Role; use BookStack\Users\Models\User; use Exception; @@ -25,7 +26,7 @@ protected function setUp(): void $this->viewer = $this->users->viewer(); } - protected function setRestrictionsForTestRoles(Entity $entity, array $actions = []) + protected function setRestrictionsForTestRoles(Entity $entity, array $actions = []): void { $roles = [ $this->user->roles->first(), @@ -184,7 +185,7 @@ public function test_book_update_restriction() $this->get($bookUrl . '/edit')->assertRedirect('/'); $this->get('/')->assertSee('You do not have permission'); - $this->get($bookPage->getUrl() . '/edit')->assertRedirect('/'); + $this->get($bookPage->getUrl() . '/edit')->assertRedirect($bookPage->getUrl()); $this->get('/')->assertSee('You do not have permission'); $this->get($bookChapter->getUrl() . '/edit')->assertRedirect('/'); $this->get('/')->assertSee('You do not have permission'); @@ -282,7 +283,7 @@ public function test_chapter_update_restriction() $this->get($chapterUrl . '/edit')->assertRedirect('/'); $this->get('/')->assertSee('You do not have permission'); - $this->get($chapterPage->getUrl() . '/edit')->assertRedirect('/'); + $this->get($chapterPage->getUrl() . '/edit')->assertRedirect($chapterPage->getUrl()); $this->get('/')->assertSee('You do not have permission'); $this->setRestrictionsForTestRoles($chapter, ['view', 'update']); @@ -341,7 +342,7 @@ public function test_page_update_restriction() $this->setRestrictionsForTestRoles($page, ['view', 'delete']); - $this->get($pageUrl . '/edit')->assertRedirect('/'); + $this->get($pageUrl . '/edit')->assertRedirect($pageUrl); $this->get('/')->assertSee('You do not have permission'); $this->setRestrictionsForTestRoles($page, ['view', 'update']); @@ -565,7 +566,7 @@ public function test_book_update_restriction_override() $this->get($bookUrl . '/edit')->assertRedirect('/'); $this->get('/')->assertSee('You do not have permission'); - $this->get($bookPage->getUrl() . '/edit')->assertRedirect('/'); + $this->get($bookPage->getUrl() . '/edit')->assertRedirect($bookPage->getUrl()); $this->get('/')->assertSee('You do not have permission'); $this->get($bookChapter->getUrl() . '/edit')->assertRedirect('/'); $this->get('/')->assertSee('You do not have permission'); @@ -676,7 +677,7 @@ public function test_access_to_item_prevented_if_inheritance_active_but_permissi $this->permissions->setEntityPermissions($book, ['update'], [$viewerRole], false); $this->permissions->setEntityPermissions($chapter, [], [$viewerRole], true); - $this->assertFalse(userCan('chapter-update', $chapter)); + $this->assertFalse(userCan(Permission::ChapterUpdate, $chapter)); } public function test_access_to_item_allowed_if_inheritance_active_and_permission_prevented_via_role_but_allowed_via_parent() @@ -692,7 +693,7 @@ public function test_access_to_item_allowed_if_inheritance_active_and_permission $this->permissions->setEntityPermissions($chapter, [], [$viewerRole], true); $this->actingAs($user); - $this->assertTrue(userCan('chapter-update', $chapter)); + $this->assertTrue(userCan(Permission::ChapterUpdate, $chapter)); } public function test_book_permissions_can_be_generated_without_error_if_child_chapter_is_in_recycle_bin() diff --git a/tests/Permissions/RolePermissionsTest.php b/tests/Permissions/RolePermissionsTest.php index ccb158faf5f..97cce681754 100644 --- a/tests/Permissions/RolePermissionsTest.php +++ b/tests/Permissions/RolePermissionsTest.php @@ -2,7 +2,6 @@ namespace Tests\Permissions; -use BookStack\Activity\ActivityType; use BookStack\Activity\Models\Comment; use BookStack\Entities\Models\Book; use BookStack\Entities\Models\Bookshelf; @@ -10,7 +9,6 @@ use BookStack\Entities\Models\Entity; use BookStack\Entities\Models\Page; use BookStack\Uploads\Image; -use BookStack\Users\Models\Role; use BookStack\Users\Models\User; use Illuminate\Testing\TestResponse; use Tests\TestCase; @@ -152,10 +150,14 @@ public function test_restrictions_manage_own_permission() /** * Check a standard entity access permission. */ - private function checkAccessPermission(string $permission, array $accessUrls = [], array $visibles = []) - { + private function checkAccessPermission( + string $permission, + array $accessUrls = [], + array $visibles = [], + string $expectedRedirectUri = '/', + ) { foreach ($accessUrls as $url) { - $this->actingAs($this->user)->get($url)->assertRedirect('/'); + $this->actingAs($this->user)->get($url)->assertRedirect($expectedRedirectUri); } foreach ($visibles as $url => $text) { @@ -535,11 +537,11 @@ public function test_page_edit_own_permission() $ownPage->getUrl() . '/edit', ], [ $ownPage->getUrl() => 'Edit', - ]); + ], $ownPage->getUrl()); $resp = $this->get($otherPage->getUrl()); $this->withHtml($resp)->assertElementNotContains('.action-buttons', 'Edit'); - $this->get($otherPage->getUrl() . '/edit')->assertRedirect('/'); + $this->get($otherPage->getUrl() . '/edit')->assertRedirect($otherPage->getUrl()); } public function test_page_edit_all_permission() @@ -550,7 +552,7 @@ public function test_page_edit_all_permission() $otherPage->getUrl('/edit'), ], [ $otherPage->getUrl() => 'Edit', - ]); + ], $otherPage->getUrl()); } public function test_page_delete_own_permission() @@ -738,16 +740,12 @@ public function test_comment_delete_all_permission() private function addComment(Page $page): TestResponse { - $comment = Comment::factory()->make(); - - return $this->postJson("/comment/$page->id", $comment->only('text', 'html')); + return $this->postJson("/comment/$page->id", ['html' => '

    New comment content

    ']); } private function updateComment(Comment $comment): TestResponse { - $commentData = Comment::factory()->make(); - - return $this->putJson("/comment/{$comment->id}", $commentData->only('text', 'html')); + return $this->putJson("/comment/{$comment->id}", ['html' => '

    Updated comment content

    ']); } private function deleteComment(Comment $comment): TestResponse diff --git a/tests/PublicActionTest.php b/tests/PublicActionTest.php index 875b279a81c..76745aaac72 100644 --- a/tests/PublicActionTest.php +++ b/tests/PublicActionTest.php @@ -128,33 +128,6 @@ public function test_content_not_listed_on_404_for_public_users() $resp->assertDontSee($page->name); } - public function test_robots_effected_by_public_status() - { - $this->get('/robots.txt')->assertSee("User-agent: *\nDisallow: /"); - - $this->setSettings(['app-public' => 'true']); - - $resp = $this->get('/robots.txt'); - $resp->assertSee("User-agent: *\nDisallow:"); - $resp->assertDontSee('Disallow: /'); - } - - public function test_robots_effected_by_setting() - { - $this->get('/robots.txt')->assertSee("User-agent: *\nDisallow: /"); - - config()->set('app.allow_robots', true); - - $resp = $this->get('/robots.txt'); - $resp->assertSee("User-agent: *\nDisallow:"); - $resp->assertDontSee('Disallow: /'); - - // Check config overrides app-public setting - config()->set('app.allow_robots', false); - $this->setSettings(['app-public' => 'true']); - $this->get('/robots.txt')->assertSee("User-agent: *\nDisallow: /"); - } - public function test_default_favicon_file_created_upon_access() { $faviconPath = public_path('favicon.ico'); diff --git a/tests/References/ReferencesTest.php b/tests/References/ReferencesTest.php index 715f7143534..f8698d02885 100644 --- a/tests/References/ReferencesTest.php +++ b/tests/References/ReferencesTest.php @@ -271,7 +271,31 @@ public function test_description_links_from_book_chapter_shelf_updated_on_url_ch } } - protected function createReference(Model $from, Model $to) + public function test_reference_from_deleted_item_does_not_count_or_show_in_references_page() + { + $page = $this->entities->page(); + $referencingPageA = $this->entities->page(); + $referencingPageB = $this->entities->page(); + + $this->asEditor(); + $this->createReference($referencingPageA, $page); + $this->createReference($referencingPageB, $page); + + $resp = $this->get($page->getUrl()); + $resp->assertSee('Referenced by 2 items'); + + $this->delete($referencingPageA->getUrl()); + + $resp = $this->get($page->getUrl()); + $resp->assertSee('Referenced by 1 item'); + + $resp = $this->get($page->getUrl('/references')); + $resp->assertOk(); + $resp->assertSee($referencingPageB->getUrl()); + $resp->assertDontSee($referencingPageA->getUrl()); + } + + protected function createReference(Model $from, Model $to): void { (new Reference())->forceFill([ 'from_type' => $from->getMorphClass(), diff --git a/tests/Entity/EntitySearchTest.php b/tests/Search/EntitySearchTest.php similarity index 78% rename from tests/Entity/EntitySearchTest.php rename to tests/Search/EntitySearchTest.php index 9b77a32ab80..d3d85998615 100644 --- a/tests/Entity/EntitySearchTest.php +++ b/tests/Search/EntitySearchTest.php @@ -1,11 +1,9 @@ assertStatus(200)->assertDontSee($page->name); } + public function test_negated_searches() + { + $page = $this->entities->newPage(['name' => 'My new test negation page', 'html' => '

    An angry tortoise wore trumpeted plimsoles

    ']); + $page->tags()->saveMany([new Tag(['name' => 'DonkCount', 'value' => '500'])]); + $page->created_by = $this->users->admin()->id; + $page->save(); + + $editor = $this->users->editor(); + $this->actingAs($editor); + + $exactSearch = $this->get('/search?term=' . urlencode('negation -"tortoise"')); + $exactSearch->assertStatus(200)->assertDontSeeText($page->name); + + $tagSearchA = $this->get('/search?term=' . urlencode('negation [DonkCount=500]')); + $tagSearchA->assertStatus(200)->assertSeeText($page->name); + $tagSearchB = $this->get('/search?term=' . urlencode('negation -[DonkCount=500]')); + $tagSearchB->assertStatus(200)->assertDontSeeText($page->name); + + $filterSearchA = $this->get('/search?term=' . urlencode('negation -{created_by:me}')); + $filterSearchA->assertStatus(200)->assertSeeText($page->name); + $page->created_by = $editor->id; + $page->save(); + $filterSearchB = $this->get('/search?term=' . urlencode('negation -{created_by:me}')); + $filterSearchB->assertStatus(200)->assertDontSeeText($page->name); + } + public function test_search_terms_with_delimiters_are_converted_to_exact_matches() { $this->asEditor(); @@ -285,77 +309,6 @@ public function test_entity_template_selector_search() $defaultListTest->assertDontSee($templatePage->name); } - public function test_sibling_search_for_pages() - { - $chapter = $this->entities->chapterHasPages(); - $this->assertGreaterThan(2, count($chapter->pages), 'Ensure we\'re testing with at least 1 sibling'); - $page = $chapter->pages->first(); - - $search = $this->actingAs($this->users->viewer())->get("/search/entity/siblings?entity_id={$page->id}&entity_type=page"); - $search->assertSuccessful(); - foreach ($chapter->pages as $page) { - $search->assertSee($page->name); - } - - $search->assertDontSee($chapter->name); - } - - public function test_sibling_search_for_pages_without_chapter() - { - $page = $this->entities->pageNotWithinChapter(); - $bookChildren = $page->book->getDirectChildren(); - $this->assertGreaterThan(2, count($bookChildren), 'Ensure we\'re testing with at least 1 sibling'); - - $search = $this->actingAs($this->users->viewer())->get("/search/entity/siblings?entity_id={$page->id}&entity_type=page"); - $search->assertSuccessful(); - foreach ($bookChildren as $child) { - $search->assertSee($child->name); - } - - $search->assertDontSee($page->book->name); - } - - public function test_sibling_search_for_chapters() - { - $chapter = $this->entities->chapter(); - $bookChildren = $chapter->book->getDirectChildren(); - $this->assertGreaterThan(2, count($bookChildren), 'Ensure we\'re testing with at least 1 sibling'); - - $search = $this->actingAs($this->users->viewer())->get("/search/entity/siblings?entity_id={$chapter->id}&entity_type=chapter"); - $search->assertSuccessful(); - foreach ($bookChildren as $child) { - $search->assertSee($child->name); - } - - $search->assertDontSee($chapter->book->name); - } - - public function test_sibling_search_for_books() - { - $books = Book::query()->take(10)->get(); - $book = $books->first(); - $this->assertGreaterThan(2, count($books), 'Ensure we\'re testing with at least 1 sibling'); - - $search = $this->actingAs($this->users->viewer())->get("/search/entity/siblings?entity_id={$book->id}&entity_type=book"); - $search->assertSuccessful(); - foreach ($books as $expectedBook) { - $search->assertSee($expectedBook->name); - } - } - - public function test_sibling_search_for_shelves() - { - $shelves = Bookshelf::query()->take(10)->get(); - $shelf = $shelves->first(); - $this->assertGreaterThan(2, count($shelves), 'Ensure we\'re testing with at least 1 sibling'); - - $search = $this->actingAs($this->users->viewer())->get("/search/entity/siblings?entity_id={$shelf->id}&entity_type=bookshelf"); - $search->assertSuccessful(); - foreach ($shelves as $expectedShelf) { - $search->assertSee($expectedShelf->name); - } - } - public function test_search_works_on_updated_page_content() { $page = $this->entities->page(); @@ -390,56 +343,6 @@ public function test_search_ranks_common_words_lower() $this->withHtml($search)->assertElementContains('.entity-list > .page:nth-child(2)', 'Test page A'); } - public function test_terms_in_headers_have_an_adjusted_index_score() - { - $page = $this->entities->newPage(['name' => 'Test page A', 'html' => ' -

    TermA

    -

    TermB TermNested

    -

    TermC

    -

    TermD

    -

    TermE

    -
    TermF
    -
    TermG
    - ']); - - $scoreByTerm = $page->searchTerms()->pluck('score', 'term'); - - $this->assertEquals(1, $scoreByTerm->get('TermA')); - $this->assertEquals(10, $scoreByTerm->get('TermB')); - $this->assertEquals(10, $scoreByTerm->get('TermNested')); - $this->assertEquals(5, $scoreByTerm->get('TermC')); - $this->assertEquals(4, $scoreByTerm->get('TermD')); - $this->assertEquals(3, $scoreByTerm->get('TermE')); - $this->assertEquals(2, $scoreByTerm->get('TermF')); - // Is 1.5 but stored as integer, rounding up - $this->assertEquals(2, $scoreByTerm->get('TermG')); - } - - public function test_name_and_content_terms_are_merged_to_single_score() - { - $page = $this->entities->newPage(['name' => 'TermA', 'html' => ' -

    TermA

    - ']); - - $scoreByTerm = $page->searchTerms()->pluck('score', 'term'); - - // Scores 40 for being in the name then 1 for being in the content - $this->assertEquals(41, $scoreByTerm->get('TermA')); - } - - public function test_tag_names_and_values_are_indexed_for_search() - { - $page = $this->entities->newPage(['name' => 'PageA', 'html' => '

    content

    ', 'tags' => [ - ['name' => 'Animal', 'value' => 'MeowieCat'], - ['name' => 'SuperImportant'], - ]]); - - $scoreByTerm = $page->searchTerms()->pluck('score', 'term'); - $this->assertEquals(5, $scoreByTerm->get('MeowieCat')); - $this->assertEquals(3, $scoreByTerm->get('Animal')); - $this->assertEquals(3, $scoreByTerm->get('SuperImportant')); - } - public function test_matching_terms_in_search_results_are_highlighted() { $this->entities->newPage(['name' => 'My Meowie Cat', 'html' => '

    A superimportant page about meowieable animals

    ', 'tags' => [ @@ -509,11 +412,18 @@ public function test_backslashes_can_be_searched_upon() $search->assertSee($page->getUrl(), false); } - public function test_searches_with_user_filters_adds_them_into_advanced_search_form() + public function test_searches_with_terms_without_controls_includes_them_in_extras() + { + $resp = $this->asEditor()->get('/search?term=' . urlencode('test {updated_by:dan} {created_by:dan} -{viewed_by_me} -[a=b] -"dog" {is_template} {sort_by:last_commented}')); + $this->withHtml($resp)->assertFieldHasValue('extras', '{updated_by:dan} {created_by:dan} {is_template} {sort_by:last_commented} -"dog" -[a=b] -{viewed_by_me}'); + } + + public function test_negated_searches_dont_show_in_inputs() { - $resp = $this->asEditor()->get('/search?term=' . urlencode('test {updated_by:dan} {created_by:dan}')); - $this->withHtml($resp)->assertElementExists('form input[name="filters[updated_by]"][value="dan"]'); - $this->withHtml($resp)->assertElementExists('form input[name="filters[created_by]"][value="dan"]'); + $resp = $this->asEditor()->get('/search?term=' . urlencode('-{created_by:me} -[a=b] -"dog"')); + $this->withHtml($resp)->assertElementNotExists('input[name="tags[]"][value="a=b"]'); + $this->withHtml($resp)->assertElementNotExists('input[name="exact[]"][value="dog"]'); + $this->withHtml($resp)->assertElementNotExists('input[name="filters[created_by]"][value="me"][checked="checked"]'); } public function test_searches_with_user_filters_using_me_adds_them_into_advanced_search_form() diff --git a/tests/Search/SearchIndexingTest.php b/tests/Search/SearchIndexingTest.php new file mode 100644 index 00000000000..d2bbb2905b4 --- /dev/null +++ b/tests/Search/SearchIndexingTest.php @@ -0,0 +1,119 @@ +entities->newPage(['name' => 'Test page A', 'html' => ' +

    TermA

    +

    TermB TermNested

    +

    TermC

    +

    TermD

    +

    TermE

    +
    TermF
    +
    TermG
    + ']); + + $scoreByTerm = $page->searchTerms()->pluck('score', 'term'); + + $this->assertEquals(1, $scoreByTerm->get('TermA')); + $this->assertEquals(10, $scoreByTerm->get('TermB')); + $this->assertEquals(10, $scoreByTerm->get('TermNested')); + $this->assertEquals(5, $scoreByTerm->get('TermC')); + $this->assertEquals(4, $scoreByTerm->get('TermD')); + $this->assertEquals(3, $scoreByTerm->get('TermE')); + $this->assertEquals(2, $scoreByTerm->get('TermF')); + // Is 1.5 but stored as integer, rounding up + $this->assertEquals(2, $scoreByTerm->get('TermG')); + } + + public function test_indexing_works_as_expected_for_page_with_lots_of_terms() + { + $this->markTestSkipped('Time consuming test'); + + $count = 100000; + $text = ''; + $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_#'; + for ($i = 0; $i < $count; $i++) { + $text .= substr(str_shuffle($chars), 0, 5) . ' '; + } + + $page = $this->entities->newPage(['name' => 'Test page A', 'html' => '

    ' . $text . '

    ']); + + $termCount = $page->searchTerms()->count(); + + // Expect at least 90% unique rate + $this->assertGreaterThan($count * 0.9, $termCount); + } + + public function test_name_and_content_terms_are_merged_to_single_score() + { + $page = $this->entities->newPage(['name' => 'TermA', 'html' => ' +

    TermA

    + ']); + + $scoreByTerm = $page->searchTerms()->pluck('score', 'term'); + + // Scores 40 for being in the name then 1 for being in the content + $this->assertEquals(41, $scoreByTerm->get('TermA')); + } + + public function test_tag_names_and_values_are_indexed_for_search() + { + $page = $this->entities->newPage(['name' => 'PageA', 'html' => '

    content

    ', 'tags' => [ + ['name' => 'Animal', 'value' => 'MeowieCat'], + ['name' => 'SuperImportant'], + ]]); + + $scoreByTerm = $page->searchTerms()->pluck('score', 'term'); + $this->assertEquals(5, $scoreByTerm->get('MeowieCat')); + $this->assertEquals(3, $scoreByTerm->get('Animal')); + $this->assertEquals(3, $scoreByTerm->get('SuperImportant')); + } + + public function test_terms_containing_guillemets_handled() + { + $page = $this->entities->newPage(['html' => '

    «Hello there» and « there »

    ']); + + $scoreByTerm = $page->searchTerms()->pluck('score', 'term'); + $expected = ['Hello', 'there', 'and']; + foreach ($expected as $term) { + $this->assertNotNull($scoreByTerm->get($term), "Failed asserting that \"$term\" is indexed"); + } + + $nonExpected = ['«', '»']; + foreach ($nonExpected as $term) { + $this->assertNull($scoreByTerm->get($term), "Failed asserting that \"$term\" is not indexed"); + } + } + + public function test_terms_containing_punctuation_within_retain_original_form_and_split_form_in_index() + { + $page = $this->entities->newPage(['html' => '

    super.duper awesome-beans big- barry cheese.

    biscuits

    a-bs

    ']); + + $scoreByTerm = $page->searchTerms()->pluck('score', 'term'); + $expected = ['super', 'duper', 'super.duper', 'awesome-beans', 'awesome', 'beans', 'big', 'barry', 'cheese', 'biscuits', 'a-bs', 'a', 'bs']; + foreach ($expected as $term) { + $this->assertNotNull($scoreByTerm->get($term), "Failed asserting that \"$term\" is indexed"); + } + + $nonExpected = ['big-', 'big-barry', 'cheese.', 'cheese.biscuits']; + foreach ($nonExpected as $term) { + $this->assertNull($scoreByTerm->get($term), "Failed asserting that \"$term\" is not indexed"); + } + } + + public function test_non_breaking_spaces_handled_as_spaces() + { + $page = $this->entities->newPage(['html' => '

    a tigerbadger is a dangerous animal

    ']); + + $scoreByTerm = $page->searchTerms()->pluck('score', 'term'); + $this->assertNotNull($scoreByTerm->get('tigerbadger')); + $this->assertNotNull($scoreByTerm->get('dangerous')); + $this->assertNotNull($scoreByTerm->get('animal')); + } +} diff --git a/tests/Search/SearchOptionsTest.php b/tests/Search/SearchOptionsTest.php new file mode 100644 index 00000000000..2ebf273dd55 --- /dev/null +++ b/tests/Search/SearchOptionsTest.php @@ -0,0 +1,145 @@ +assertEquals(['cat'], $options->searches->toValueArray()); + $this->assertEquals(['dog'], $options->exacts->toValueArray()); + $this->assertEquals(['tag=good'], $options->tags->toValueArray()); + $this->assertEquals(['is_tree' => ''], $options->filters->toValueMap()); + } + + public function test_from_string_parses_negations() + { + $options = SearchOptions::fromString('cat -"dog" -[tag=good] -{is_tree}'); + + $this->assertEquals(['cat'], $options->searches->toValueArray()); + $this->assertTrue($options->exacts->all()[0]->negated); + $this->assertTrue($options->tags->all()[0]->negated); + $this->assertTrue($options->filters->all()[0]->negated); + } + + public function test_from_string_properly_parses_escaped_quotes() + { + $options = SearchOptions::fromString('"\"cat\"" surprise "\"\"" "\"donkey" "\"" "\\\\"'); + + $this->assertEquals(['"cat"', '""', '"donkey', '"', '\\'], $options->exacts->toValueArray()); + } + + public function test_to_string_includes_all_items_in_the_correct_format() + { + $expected = 'cat "dog" [tag=good] {is_tree} {beans:valid}'; + $options = new SearchOptions(); + $options->searches = SearchOptionSet::fromValueArray(['cat'], TermSearchOption::class); + $options->exacts = SearchOptionSet::fromValueArray(['dog'], ExactSearchOption::class); + $options->tags = SearchOptionSet::fromValueArray(['tag=good'], TagSearchOption::class); + $options->filters = new SearchOptionSet([ + new FilterSearchOption('', 'is_tree'), + new FilterSearchOption('valid', 'beans'), + ]); + + $output = $options->toString(); + foreach (explode(' ', $expected) as $term) { + $this->assertStringContainsString($term, $output); + } + } + + public function test_to_string_handles_negations_as_expected() + { + $expected = 'cat -"dog" -[tag=good] -{is_tree}'; + $options = new SearchOptions(); + $options->searches = new SearchOptionSet([new TermSearchOption('cat')]); + $options->exacts = new SearchOptionSet([new ExactSearchOption('dog', true)]); + $options->tags = new SearchOptionSet([new TagSearchOption('tag=good', true)]); + $options->filters = new SearchOptionSet([ + new FilterSearchOption('', 'is_tree', true), + ]); + + $output = $options->toString(); + foreach (explode(' ', $expected) as $term) { + $this->assertStringContainsString($term, $output); + } + } + + public function test_to_string_escapes_as_expected() + { + $options = new SearchOptions(); + $options->exacts = SearchOptionSet::fromValueArray(['"cat"', '""', '"donkey', '"', '\\', '\\"'], ExactSearchOption::class); + + $output = $options->toString(); + $this->assertEquals('"\"cat\"" "\"\"" "\"donkey" "\"" "\\\\" "\\\\\""', $output); + } + + public function test_correct_filter_values_are_set_from_string() + { + $opts = SearchOptions::fromString('{is_tree} {name:dan} {cat:happy}'); + + $this->assertEquals([ + 'is_tree' => '', + 'name' => 'dan', + 'cat' => 'happy', + ], $opts->filters->toValueMap()); + } + public function test_it_cannot_parse_out_empty_exacts() + { + $options = SearchOptions::fromString('"" test ""'); + + $this->assertEmpty($options->exacts->toValueArray()); + $this->assertCount(1, $options->searches->toValueArray()); + } + + public function test_from_request_properly_parses_exacts_from_search_terms() + { + $request = new Request([ + 'search' => 'biscuits "cheese" "" "baked beans"' + ]); + + $options = SearchOptions::fromRequest($request); + $this->assertEquals(["biscuits"], $options->searches->toValueArray()); + $this->assertEquals(['"cheese"', '""', '"baked', 'beans"'], $options->exacts->toValueArray()); + } + + public function test_from_request_properly_parses_provided_types() + { + $request = new Request([ + 'search' => '', + 'types' => ['page', 'book'], + ]); + + $options = SearchOptions::fromRequest($request); + $filters = $options->filters->toValueMap(); + $this->assertCount(1, $filters); + $this->assertEquals('page|book', $filters['type'] ?? 'notfound'); + } + + public function test_from_request_properly_parses_out_extras_as_string() + { + $request = new Request([ + 'search' => '', + 'tags' => ['a=b'], + 'extras' => '-[b=c] -{viewed_by_me} -"dino"' + ]); + + $options = SearchOptions::fromRequest($request); + $this->assertCount(2, $options->tags->all()); + $this->assertEquals('b=c', $options->tags->negated()->all()[0]->value); + $this->assertEquals('viewed_by_me', $options->filters->all()[0]->getKey()); + $this->assertTrue($options->filters->all()[0]->negated); + $this->assertEquals('dino', $options->exacts->all()[0]->value); + $this->assertTrue($options->exacts->all()[0]->negated); + } +} diff --git a/tests/Search/SiblingSearchTest.php b/tests/Search/SiblingSearchTest.php new file mode 100644 index 00000000000..e626a8843c5 --- /dev/null +++ b/tests/Search/SiblingSearchTest.php @@ -0,0 +1,117 @@ +entities->chapterHasPages(); + $this->assertGreaterThan(2, count($chapter->pages), 'Ensure we\'re testing with at least 1 sibling'); + $page = $chapter->pages->first(); + + $search = $this->actingAs($this->users->viewer())->get("/search/entity/siblings?entity_id={$page->id}&entity_type=page"); + $search->assertSuccessful(); + foreach ($chapter->pages as $page) { + $search->assertSee($page->name); + } + + $search->assertDontSee($chapter->name); + } + + public function test_sibling_search_for_pages_without_chapter() + { + $page = $this->entities->pageNotWithinChapter(); + $bookChildren = $page->book->getDirectVisibleChildren(); + $this->assertGreaterThan(2, count($bookChildren), 'Ensure we\'re testing with at least 1 sibling'); + + $search = $this->actingAs($this->users->viewer())->get("/search/entity/siblings?entity_id={$page->id}&entity_type=page"); + $search->assertSuccessful(); + foreach ($bookChildren as $child) { + $search->assertSee($child->name); + } + + $search->assertDontSee($page->book->name); + } + + public function test_sibling_search_for_chapters() + { + $chapter = $this->entities->chapter(); + $bookChildren = $chapter->book->getDirectVisibleChildren(); + $this->assertGreaterThan(2, count($bookChildren), 'Ensure we\'re testing with at least 1 sibling'); + + $search = $this->actingAs($this->users->viewer())->get("/search/entity/siblings?entity_id={$chapter->id}&entity_type=chapter"); + $search->assertSuccessful(); + foreach ($bookChildren as $child) { + $search->assertSee($child->name); + } + + $search->assertDontSee($chapter->book->name); + } + + public function test_sibling_search_for_books() + { + $books = Book::query()->take(10)->get(); + $book = $books->first(); + $this->assertGreaterThan(2, count($books), 'Ensure we\'re testing with at least 1 sibling'); + + $search = $this->actingAs($this->users->viewer())->get("/search/entity/siblings?entity_id={$book->id}&entity_type=book"); + $search->assertSuccessful(); + foreach ($books as $expectedBook) { + $search->assertSee($expectedBook->name); + } + } + + public function test_sibling_search_for_shelves() + { + $shelves = Bookshelf::query()->take(10)->get(); + $shelf = $shelves->first(); + $this->assertGreaterThan(2, count($shelves), 'Ensure we\'re testing with at least 1 sibling'); + + $search = $this->actingAs($this->users->viewer())->get("/search/entity/siblings?entity_id={$shelf->id}&entity_type=bookshelf"); + $search->assertSuccessful(); + foreach ($shelves as $expectedShelf) { + $search->assertSee($expectedShelf->name); + } + } + + public function test_sibling_search_for_books_provides_results_in_alphabetical_order() + { + $contextBook = $this->entities->book(); + $searchBook = $this->entities->book(); + + $searchBook->name = 'Zebras'; + $searchBook->save(); + + $search = $this->actingAs($this->users->viewer())->get("/search/entity/siblings?entity_id={$contextBook->id}&entity_type=book"); + $this->withHtml($search)->assertElementNotContains('a:first-child', 'Zebras'); + + $searchBook->name = '1AAAAAAArdvarks'; + $searchBook->save(); + + $search = $this->actingAs($this->users->viewer())->get("/search/entity/siblings?entity_id={$contextBook->id}&entity_type=book"); + $this->withHtml($search)->assertElementContains('a:first-child', '1AAAAAAArdvarks'); + } + + public function test_sibling_search_for_shelves_provides_results_in_alphabetical_order() + { + $contextShelf = $this->entities->shelf(); + $searchShelf = $this->entities->shelf(); + + $searchShelf->name = 'Zebras'; + $searchShelf->save(); + + $search = $this->actingAs($this->users->viewer())->get("/search/entity/siblings?entity_id={$contextShelf->id}&entity_type=bookshelf"); + $this->withHtml($search)->assertElementNotContains('a:first-child', 'Zebras'); + + $searchShelf->name = '1AAAAAAArdvarks'; + $searchShelf->save(); + + $search = $this->actingAs($this->users->viewer())->get("/search/entity/siblings?entity_id={$contextShelf->id}&entity_type=bookshelf"); + $this->withHtml($search)->assertElementContains('a:first-child', '1AAAAAAArdvarks'); + } +} diff --git a/tests/SecurityHeaderTest.php b/tests/SecurityHeaderTest.php index d369e695c51..fe98e32080b 100644 --- a/tests/SecurityHeaderTest.php +++ b/tests/SecurityHeaderTest.php @@ -17,7 +17,7 @@ public function test_cookies_samesite_lax_by_default() public function test_cookies_samesite_none_when_iframe_hosts_set() { - $this->runWithEnv('ALLOWED_IFRAME_HOSTS', 'http://example.com', function () { + $this->runWithEnv(['ALLOWED_IFRAME_HOSTS' => 'http://example.com'], function () { $resp = $this->get('/'); foreach ($resp->headers->getCookies() as $cookie) { $this->assertEquals('none', $cookie->getSameSite()); @@ -27,14 +27,14 @@ public function test_cookies_samesite_none_when_iframe_hosts_set() public function test_secure_cookies_controlled_by_app_url() { - $this->runWithEnv('APP_URL', 'http://example.com', function () { + $this->runWithEnv(['APP_URL' => 'http://example.com'], function () { $resp = $this->get('/'); foreach ($resp->headers->getCookies() as $cookie) { $this->assertFalse($cookie->isSecure()); } }); - $this->runWithEnv('APP_URL', 'https://example.com', function () { + $this->runWithEnv(['APP_URL' => 'https://example.com'], function () { $resp = $this->get('/'); foreach ($resp->headers->getCookies() as $cookie) { $this->assertTrue($cookie->isSecure()); @@ -52,7 +52,7 @@ public function test_iframe_csp_self_only_by_default() public function test_iframe_csp_includes_extra_hosts_if_configured() { - $this->runWithEnv('ALLOWED_IFRAME_HOSTS', 'https://a.example.com https://b.example.com', function () { + $this->runWithEnv(['ALLOWED_IFRAME_HOSTS' => 'https://a.example.com https://b.example.com'], function () { $resp = $this->get('/'); $frameHeader = $this->getCspHeader($resp, 'frame-ancestors'); @@ -139,6 +139,18 @@ public function test_frame_src_csp_header_has_drawio_host_added() $this->assertEquals('frame-src \'self\' https://example.com https://diagrams.example.com', $scriptHeader); } + public function test_frame_src_csp_header_drawio_host_includes_port_if_existing() + { + config()->set([ + 'app.iframe_sources' => 'https://example.com', + 'services.drawio' => 'https://diagrams.example.com:8080/testing?cat=dog', + ]); + + $resp = $this->get('/'); + $scriptHeader = $this->getCspHeader($resp, 'frame-src'); + $this->assertEquals('frame-src \'self\' https://example.com https://diagrams.example.com:8080', $scriptHeader); + } + public function test_cache_control_headers_are_set_on_responses() { // Public access diff --git a/tests/Settings/RecycleBinTest.php b/tests/Settings/RecycleBinTest.php index 8adc92f2538..33284b4b3ff 100644 --- a/tests/Settings/RecycleBinTest.php +++ b/tests/Settings/RecycleBinTest.php @@ -153,22 +153,22 @@ public function test_permanent_entity_delete_updates_existing_activity_with_enti $this->assertDatabaseHas('activities', [ 'type' => 'page_delete', - 'entity_id' => $page->id, - 'entity_type' => $page->getMorphClass(), + 'loggable_id' => $page->id, + 'loggable_type' => $page->getMorphClass(), ]); $this->asAdmin()->delete("/settings/recycle-bin/{$deletion->id}"); $this->assertDatabaseMissing('activities', [ 'type' => 'page_delete', - 'entity_id' => $page->id, - 'entity_type' => $page->getMorphClass(), + 'loggable_id' => $page->id, + 'loggable_type' => $page->getMorphClass(), ]); $this->assertDatabaseHas('activities', [ 'type' => 'page_delete', - 'entity_id' => null, - 'entity_type' => null, + 'loggable_id' => null, + 'loggable_type' => null, 'detail' => $page->name, ]); } diff --git a/tests/Entity/SortTest.php b/tests/Sorting/BookSortTest.php similarity index 51% rename from tests/Entity/SortTest.php rename to tests/Sorting/BookSortTest.php index 9a5a2fe175a..4737ec231b6 100644 --- a/tests/Entity/SortTest.php +++ b/tests/Sorting/BookSortTest.php @@ -1,15 +1,27 @@ entities->book(); + + $resp = $this->asAdmin()->get($bookToSort->getUrl()); + $this->withHtml($resp)->assertElementExists('a[href="' . $bookToSort->getUrl('/sort') . '"]'); + + $resp = $this->get($bookToSort->getUrl('/sort')); + $resp->assertStatus(200); + $resp->assertSee($bookToSort->name); + } + public function test_drafts_do_not_show_up() { $this->asAdmin(); @@ -20,232 +32,10 @@ public function test_drafts_do_not_show_up() $resp = $this->get($book->getUrl()); $resp->assertSee($draft->name); - $resp = $this->get($book->getUrl() . '/sort'); + $resp = $this->get($book->getUrl('/sort')); $resp->assertDontSee($draft->name); } - public function test_page_move_into_book() - { - $page = $this->entities->page(); - $currentBook = $page->book; - $newBook = Book::query()->where('id', '!=', $currentBook->id)->first(); - - $resp = $this->asEditor()->get($page->getUrl('/move')); - $resp->assertSee('Move Page'); - - $movePageResp = $this->put($page->getUrl('/move'), [ - 'entity_selection' => 'book:' . $newBook->id, - ]); - $page->refresh(); - - $movePageResp->assertRedirect($page->getUrl()); - $this->assertTrue($page->book->id == $newBook->id, 'Page book is now the new book'); - - $newBookResp = $this->get($newBook->getUrl()); - $newBookResp->assertSee('moved page'); - $newBookResp->assertSee($page->name); - } - - public function test_page_move_into_chapter() - { - $page = $this->entities->page(); - $currentBook = $page->book; - $newBook = Book::query()->where('id', '!=', $currentBook->id)->first(); - $newChapter = $newBook->chapters()->first(); - - $movePageResp = $this->actingAs($this->users->editor())->put($page->getUrl('/move'), [ - 'entity_selection' => 'chapter:' . $newChapter->id, - ]); - $page->refresh(); - - $movePageResp->assertRedirect($page->getUrl()); - $this->assertTrue($page->book->id == $newBook->id, 'Page parent is now the new chapter'); - - $newChapterResp = $this->get($newChapter->getUrl()); - $newChapterResp->assertSee($page->name); - } - - public function test_page_move_from_chapter_to_book() - { - $oldChapter = Chapter::query()->first(); - $page = $oldChapter->pages()->first(); - $newBook = Book::query()->where('id', '!=', $oldChapter->book_id)->first(); - - $movePageResp = $this->actingAs($this->users->editor())->put($page->getUrl('/move'), [ - 'entity_selection' => 'book:' . $newBook->id, - ]); - $page->refresh(); - - $movePageResp->assertRedirect($page->getUrl()); - $this->assertTrue($page->book->id == $newBook->id, 'Page parent is now the new book'); - $this->assertTrue($page->chapter === null, 'Page has no parent chapter'); - - $newBookResp = $this->get($newBook->getUrl()); - $newBookResp->assertSee($page->name); - } - - public function test_page_move_requires_create_permissions_on_parent() - { - $page = $this->entities->page(); - $currentBook = $page->book; - $newBook = Book::query()->where('id', '!=', $currentBook->id)->first(); - $editor = $this->users->editor(); - - $this->permissions->setEntityPermissions($newBook, ['view', 'update', 'delete'], $editor->roles->all()); - - $movePageResp = $this->actingAs($editor)->put($page->getUrl('/move'), [ - 'entity_selection' => 'book:' . $newBook->id, - ]); - $this->assertPermissionError($movePageResp); - - $this->permissions->setEntityPermissions($newBook, ['view', 'update', 'delete', 'create'], $editor->roles->all()); - $movePageResp = $this->put($page->getUrl('/move'), [ - 'entity_selection' => 'book:' . $newBook->id, - ]); - - $page->refresh(); - $movePageResp->assertRedirect($page->getUrl()); - - $this->assertTrue($page->book->id == $newBook->id, 'Page book is now the new book'); - } - - public function test_page_move_requires_delete_permissions() - { - $page = $this->entities->page(); - $currentBook = $page->book; - $newBook = Book::query()->where('id', '!=', $currentBook->id)->first(); - $editor = $this->users->editor(); - - $this->permissions->setEntityPermissions($newBook, ['view', 'update', 'create', 'delete'], $editor->roles->all()); - $this->permissions->setEntityPermissions($page, ['view', 'update', 'create'], $editor->roles->all()); - - $movePageResp = $this->actingAs($editor)->put($page->getUrl('/move'), [ - 'entity_selection' => 'book:' . $newBook->id, - ]); - $this->assertPermissionError($movePageResp); - $pageView = $this->get($page->getUrl()); - $pageView->assertDontSee($page->getUrl('/move')); - - $this->permissions->setEntityPermissions($page, ['view', 'update', 'create', 'delete'], $editor->roles->all()); - $movePageResp = $this->put($page->getUrl('/move'), [ - 'entity_selection' => 'book:' . $newBook->id, - ]); - - $page->refresh(); - $movePageResp->assertRedirect($page->getUrl()); - $this->assertTrue($page->book->id == $newBook->id, 'Page book is now the new book'); - } - - public function test_chapter_move() - { - $chapter = $this->entities->chapter(); - $currentBook = $chapter->book; - $pageToCheck = $chapter->pages->first(); - $newBook = Book::query()->where('id', '!=', $currentBook->id)->first(); - - $chapterMoveResp = $this->asEditor()->get($chapter->getUrl('/move')); - $chapterMoveResp->assertSee('Move Chapter'); - - $moveChapterResp = $this->put($chapter->getUrl('/move'), [ - 'entity_selection' => 'book:' . $newBook->id, - ]); - - $chapter = Chapter::query()->find($chapter->id); - $moveChapterResp->assertRedirect($chapter->getUrl()); - $this->assertTrue($chapter->book->id === $newBook->id, 'Chapter Book is now the new book'); - - $newBookResp = $this->get($newBook->getUrl()); - $newBookResp->assertSee('moved chapter'); - $newBookResp->assertSee($chapter->name); - - $pageToCheck = Page::query()->find($pageToCheck->id); - $this->assertTrue($pageToCheck->book_id === $newBook->id, 'Chapter child page\'s book id has changed to the new book'); - $pageCheckResp = $this->get($pageToCheck->getUrl()); - $pageCheckResp->assertSee($newBook->name); - } - - public function test_chapter_move_requires_delete_permissions() - { - $chapter = $this->entities->chapter(); - $currentBook = $chapter->book; - $newBook = Book::query()->where('id', '!=', $currentBook->id)->first(); - $editor = $this->users->editor(); - - $this->permissions->setEntityPermissions($newBook, ['view', 'update', 'create', 'delete'], $editor->roles->all()); - $this->permissions->setEntityPermissions($chapter, ['view', 'update', 'create'], $editor->roles->all()); - - $moveChapterResp = $this->actingAs($editor)->put($chapter->getUrl('/move'), [ - 'entity_selection' => 'book:' . $newBook->id, - ]); - $this->assertPermissionError($moveChapterResp); - $pageView = $this->get($chapter->getUrl()); - $pageView->assertDontSee($chapter->getUrl('/move')); - - $this->permissions->setEntityPermissions($chapter, ['view', 'update', 'create', 'delete'], $editor->roles->all()); - $moveChapterResp = $this->put($chapter->getUrl('/move'), [ - 'entity_selection' => 'book:' . $newBook->id, - ]); - - $chapter = Chapter::query()->find($chapter->id); - $moveChapterResp->assertRedirect($chapter->getUrl()); - $this->assertTrue($chapter->book->id == $newBook->id, 'Page book is now the new book'); - } - - public function test_chapter_move_requires_create_permissions_in_new_book() - { - $chapter = $this->entities->chapter(); - $currentBook = $chapter->book; - $newBook = Book::query()->where('id', '!=', $currentBook->id)->first(); - $editor = $this->users->editor(); - - $this->permissions->setEntityPermissions($newBook, ['view', 'update', 'delete'], [$editor->roles->first()]); - $this->permissions->setEntityPermissions($chapter, ['view', 'update', 'create', 'delete'], [$editor->roles->first()]); - - $moveChapterResp = $this->actingAs($editor)->put($chapter->getUrl('/move'), [ - 'entity_selection' => 'book:' . $newBook->id, - ]); - $this->assertPermissionError($moveChapterResp); - - $this->permissions->setEntityPermissions($newBook, ['view', 'update', 'create', 'delete'], [$editor->roles->first()]); - $moveChapterResp = $this->put($chapter->getUrl('/move'), [ - 'entity_selection' => 'book:' . $newBook->id, - ]); - - $chapter = Chapter::query()->find($chapter->id); - $moveChapterResp->assertRedirect($chapter->getUrl()); - $this->assertTrue($chapter->book->id == $newBook->id, 'Page book is now the new book'); - } - - public function test_chapter_move_changes_book_for_deleted_pages_within() - { - /** @var Chapter $chapter */ - $chapter = Chapter::query()->whereHas('pages')->first(); - $currentBook = $chapter->book; - $pageToCheck = $chapter->pages->first(); - $newBook = Book::query()->where('id', '!=', $currentBook->id)->first(); - - $pageToCheck->delete(); - - $this->asEditor()->put($chapter->getUrl('/move'), [ - 'entity_selection' => 'book:' . $newBook->id, - ]); - - $pageToCheck->refresh(); - $this->assertEquals($newBook->id, $pageToCheck->book_id); - } - - public function test_book_sort_page_shows() - { - $bookToSort = $this->entities->book(); - - $resp = $this->asAdmin()->get($bookToSort->getUrl()); - $this->withHtml($resp)->assertElementExists('a[href="' . $bookToSort->getUrl('/sort') . '"]'); - - $resp = $this->get($bookToSort->getUrl('/sort')); - $resp->assertStatus(200); - $resp->assertSee($bookToSort->name); - } - public function test_book_sort() { $oldBook = $this->entities->book(); @@ -417,13 +207,39 @@ public function test_book_sort_makes_no_changes_if_no_delete_permissions_on_move ]); } + public function test_book_sort_does_not_change_timestamps_on_just_order_changes() + { + $book = $this->entities->bookHasChaptersAndPages(); + $chapter = $book->chapters()->first(); + \DB::table('chapters')->where('id', '=', $chapter->id)->update([ + 'priority' => 10001, + 'updated_at' => \Carbon\Carbon::now()->subYear(5), + ]); + + $chapter->refresh(); + $oldUpdatedAt = $chapter->updated_at->unix(); + + $sortData = [ + 'id' => $chapter->id, + 'sort' => 0, + 'parentChapter' => false, + 'type' => 'chapter', + 'book' => $book->id, + ]; + $this->asEditor()->put($book->getUrl('/sort'), ['sort-tree' => json_encode([$sortData])])->assertRedirect(); + + $chapter->refresh(); + $this->assertNotEquals(10001, $chapter->priority); + $this->assertEquals($oldUpdatedAt, $chapter->updated_at->unix()); + } + public function test_book_sort_item_returns_book_content() { $bookToSort = $this->entities->book(); $firstPage = $bookToSort->pages[0]; $firstChapter = $bookToSort->chapters[0]; - $resp = $this->asAdmin()->get($bookToSort->getUrl() . '/sort-item'); + $resp = $this->asAdmin()->get($bookToSort->getUrl('/sort-item')); // Ensure book details are returned $resp->assertSee($bookToSort->name); @@ -431,6 +247,53 @@ public function test_book_sort_item_returns_book_content() $resp->assertSee($firstChapter->name); } + public function test_book_sort_item_shows_auto_sort_status() + { + $sort = SortRule::factory()->create(['name' => 'My sort']); + $book = $this->entities->book(); + + $resp = $this->asAdmin()->get($book->getUrl('/sort-item')); + $this->withHtml($resp)->assertElementNotExists("span[title='Auto Sort Active: My sort']"); + + $book->sort_rule_id = $sort->id; + $book->save(); + + $resp = $this->asAdmin()->get($book->getUrl('/sort-item')); + $this->withHtml($resp)->assertElementExists("span[title='Auto Sort Active: My sort']"); + } + + public function test_auto_sort_options_shown_on_sort_page() + { + $sort = SortRule::factory()->create(); + $book = $this->entities->book(); + $resp = $this->asAdmin()->get($book->getUrl('/sort')); + + $this->withHtml($resp)->assertElementExists('select[name="auto-sort"] option[value="' . $sort->id . '"]'); + } + + public function test_auto_sort_option_submit_saves_to_book() + { + $sort = SortRule::factory()->create(); + $book = $this->entities->book(); + $bookPage = $book->pages()->first(); + $bookPage->priority = 10000; + $bookPage->save(); + + $resp = $this->asAdmin()->put($book->getUrl('/sort'), [ + 'auto-sort' => $sort->id, + ]); + + $resp->assertRedirect($book->getUrl()); + $book->refresh(); + $bookPage->refresh(); + + $this->assertEquals($sort->id, $book->sort_rule_id); + $this->assertNotEquals(10000, $bookPage->priority); + + $resp = $this->get($book->getUrl('/sort')); + $this->withHtml($resp)->assertElementExists('select[name="auto-sort"] option[value="' . $sort->id . '"][selected]'); + } + public function test_pages_in_book_show_sorted_by_priority() { $book = $this->entities->bookHasChaptersAndPages(); diff --git a/tests/Sorting/MoveTest.php b/tests/Sorting/MoveTest.php new file mode 100644 index 00000000000..606b23c680f --- /dev/null +++ b/tests/Sorting/MoveTest.php @@ -0,0 +1,221 @@ +entities->page(); + $currentBook = $page->book; + $newBook = Book::query()->where('id', '!=', $currentBook->id)->first(); + + $resp = $this->asEditor()->get($page->getUrl('/move')); + $resp->assertSee('Move Page'); + + $movePageResp = $this->put($page->getUrl('/move'), [ + 'entity_selection' => 'book:' . $newBook->id, + ]); + $page->refresh(); + + $movePageResp->assertRedirect($page->getUrl()); + $this->assertTrue($page->book->id == $newBook->id, 'Page book is now the new book'); + + $newBookResp = $this->get($newBook->getUrl()); + $newBookResp->assertSee('moved page'); + $newBookResp->assertSee($page->name); + } + + public function test_page_move_into_chapter() + { + $page = $this->entities->page(); + $currentBook = $page->book; + $newBook = Book::query()->where('id', '!=', $currentBook->id)->first(); + $newChapter = $newBook->chapters()->first(); + + $movePageResp = $this->actingAs($this->users->editor())->put($page->getUrl('/move'), [ + 'entity_selection' => 'chapter:' . $newChapter->id, + ]); + $page->refresh(); + + $movePageResp->assertRedirect($page->getUrl()); + $this->assertTrue($page->book->id == $newBook->id, 'Page parent is now the new chapter'); + + $newChapterResp = $this->get($newChapter->getUrl()); + $newChapterResp->assertSee($page->name); + } + + public function test_page_move_from_chapter_to_book() + { + $oldChapter = Chapter::query()->first(); + $page = $oldChapter->pages()->first(); + $newBook = Book::query()->where('id', '!=', $oldChapter->book_id)->first(); + + $movePageResp = $this->actingAs($this->users->editor())->put($page->getUrl('/move'), [ + 'entity_selection' => 'book:' . $newBook->id, + ]); + $page->refresh(); + + $movePageResp->assertRedirect($page->getUrl()); + $this->assertTrue($page->book->id == $newBook->id, 'Page parent is now the new book'); + $this->assertTrue($page->chapter === null, 'Page has no parent chapter'); + + $newBookResp = $this->get($newBook->getUrl()); + $newBookResp->assertSee($page->name); + } + + public function test_page_move_requires_create_permissions_on_parent() + { + $page = $this->entities->page(); + $currentBook = $page->book; + $newBook = Book::query()->where('id', '!=', $currentBook->id)->first(); + $editor = $this->users->editor(); + + $this->permissions->setEntityPermissions($newBook, ['view', 'update', 'delete'], $editor->roles->all()); + + $movePageResp = $this->actingAs($editor)->put($page->getUrl('/move'), [ + 'entity_selection' => 'book:' . $newBook->id, + ]); + $this->assertPermissionError($movePageResp); + + $this->permissions->setEntityPermissions($newBook, ['view', 'update', 'delete', 'create'], $editor->roles->all()); + $movePageResp = $this->put($page->getUrl('/move'), [ + 'entity_selection' => 'book:' . $newBook->id, + ]); + + $page->refresh(); + $movePageResp->assertRedirect($page->getUrl()); + + $this->assertTrue($page->book->id == $newBook->id, 'Page book is now the new book'); + } + + public function test_page_move_requires_delete_permissions() + { + $page = $this->entities->page(); + $currentBook = $page->book; + $newBook = Book::query()->where('id', '!=', $currentBook->id)->first(); + $editor = $this->users->editor(); + + $this->permissions->setEntityPermissions($newBook, ['view', 'update', 'create', 'delete'], $editor->roles->all()); + $this->permissions->setEntityPermissions($page, ['view', 'update', 'create'], $editor->roles->all()); + + $movePageResp = $this->actingAs($editor)->put($page->getUrl('/move'), [ + 'entity_selection' => 'book:' . $newBook->id, + ]); + $this->assertPermissionError($movePageResp); + $pageView = $this->get($page->getUrl()); + $pageView->assertDontSee($page->getUrl('/move')); + + $this->permissions->setEntityPermissions($page, ['view', 'update', 'create', 'delete'], $editor->roles->all()); + $movePageResp = $this->put($page->getUrl('/move'), [ + 'entity_selection' => 'book:' . $newBook->id, + ]); + + $page->refresh(); + $movePageResp->assertRedirect($page->getUrl()); + $this->assertTrue($page->book->id == $newBook->id, 'Page book is now the new book'); + } + + public function test_chapter_move() + { + $chapter = $this->entities->chapter(); + $currentBook = $chapter->book; + $pageToCheck = $chapter->pages->first(); + $newBook = Book::query()->where('id', '!=', $currentBook->id)->first(); + + $chapterMoveResp = $this->asEditor()->get($chapter->getUrl('/move')); + $chapterMoveResp->assertSee('Move Chapter'); + + $moveChapterResp = $this->put($chapter->getUrl('/move'), [ + 'entity_selection' => 'book:' . $newBook->id, + ]); + + $chapter = Chapter::query()->find($chapter->id); + $moveChapterResp->assertRedirect($chapter->getUrl()); + $this->assertTrue($chapter->book->id === $newBook->id, 'Chapter Book is now the new book'); + + $newBookResp = $this->get($newBook->getUrl()); + $newBookResp->assertSee('moved chapter'); + $newBookResp->assertSee($chapter->name); + + $pageToCheck = Page::query()->find($pageToCheck->id); + $this->assertTrue($pageToCheck->book_id === $newBook->id, 'Chapter child page\'s book id has changed to the new book'); + $pageCheckResp = $this->get($pageToCheck->getUrl()); + $pageCheckResp->assertSee($newBook->name); + } + + public function test_chapter_move_requires_delete_permissions() + { + $chapter = $this->entities->chapter(); + $currentBook = $chapter->book; + $newBook = Book::query()->where('id', '!=', $currentBook->id)->first(); + $editor = $this->users->editor(); + + $this->permissions->setEntityPermissions($newBook, ['view', 'update', 'create', 'delete'], $editor->roles->all()); + $this->permissions->setEntityPermissions($chapter, ['view', 'update', 'create'], $editor->roles->all()); + + $moveChapterResp = $this->actingAs($editor)->put($chapter->getUrl('/move'), [ + 'entity_selection' => 'book:' . $newBook->id, + ]); + $this->assertPermissionError($moveChapterResp); + $pageView = $this->get($chapter->getUrl()); + $pageView->assertDontSee($chapter->getUrl('/move')); + + $this->permissions->setEntityPermissions($chapter, ['view', 'update', 'create', 'delete'], $editor->roles->all()); + $moveChapterResp = $this->put($chapter->getUrl('/move'), [ + 'entity_selection' => 'book:' . $newBook->id, + ]); + + $chapter = Chapter::query()->find($chapter->id); + $moveChapterResp->assertRedirect($chapter->getUrl()); + $this->assertTrue($chapter->book->id == $newBook->id, 'Page book is now the new book'); + } + + public function test_chapter_move_requires_create_permissions_in_new_book() + { + $chapter = $this->entities->chapter(); + $currentBook = $chapter->book; + $newBook = Book::query()->where('id', '!=', $currentBook->id)->first(); + $editor = $this->users->editor(); + + $this->permissions->setEntityPermissions($newBook, ['view', 'update', 'delete'], [$editor->roles->first()]); + $this->permissions->setEntityPermissions($chapter, ['view', 'update', 'create', 'delete'], [$editor->roles->first()]); + + $moveChapterResp = $this->actingAs($editor)->put($chapter->getUrl('/move'), [ + 'entity_selection' => 'book:' . $newBook->id, + ]); + $this->assertPermissionError($moveChapterResp); + + $this->permissions->setEntityPermissions($newBook, ['view', 'update', 'create', 'delete'], [$editor->roles->first()]); + $moveChapterResp = $this->put($chapter->getUrl('/move'), [ + 'entity_selection' => 'book:' . $newBook->id, + ]); + + $chapter = Chapter::query()->find($chapter->id); + $moveChapterResp->assertRedirect($chapter->getUrl()); + $this->assertTrue($chapter->book->id == $newBook->id, 'Page book is now the new book'); + } + + public function test_chapter_move_changes_book_for_deleted_pages_within() + { + /** @var Chapter $chapter */ + $chapter = Chapter::query()->whereHas('pages')->first(); + $currentBook = $chapter->book; + $pageToCheck = $chapter->pages->first(); + $newBook = Book::query()->where('id', '!=', $currentBook->id)->first(); + + $pageToCheck->delete(); + + $this->asEditor()->put($chapter->getUrl('/move'), [ + 'entity_selection' => 'book:' . $newBook->id, + ]); + + $pageToCheck->refresh(); + $this->assertEquals($newBook->id, $pageToCheck->book_id); + } +} diff --git a/tests/Sorting/SortRuleTest.php b/tests/Sorting/SortRuleTest.php new file mode 100644 index 00000000000..4a9d3a7b33f --- /dev/null +++ b/tests/Sorting/SortRuleTest.php @@ -0,0 +1,271 @@ +create(); + $user = $this->users->viewer(); + $this->actingAs($user); + + $actions = [ + ['GET', '/settings/sorting'], + ['POST', '/settings/sorting/rules'], + ['GET', "/settings/sorting/rules/{$rule->id}"], + ['PUT', "/settings/sorting/rules/{$rule->id}"], + ['DELETE', "/settings/sorting/rules/{$rule->id}"], + ]; + + foreach ($actions as [$method, $path]) { + $resp = $this->call($method, $path); + $this->assertPermissionError($resp); + } + + $this->permissions->grantUserRolePermissions($user, ['settings-manage']); + + foreach ($actions as [$method, $path]) { + $resp = $this->call($method, $path); + $this->assertNotPermissionError($resp); + } + } + + public function test_create_flow() + { + $resp = $this->asAdmin()->get('/settings/sorting'); + $this->withHtml($resp)->assertLinkExists(url('/settings/sorting/rules/new')); + + $resp = $this->get('/settings/sorting/rules/new'); + $this->withHtml($resp)->assertElementExists('form[action$="/settings/sorting/rules"] input[name="name"]'); + $resp->assertSeeText('Name - Alphabetical (Asc)'); + + $details = ['name' => 'My new sort', 'sequence' => 'name_asc']; + $resp = $this->post('/settings/sorting/rules', $details); + $resp->assertRedirect('/settings/sorting'); + + $this->assertActivityExists(ActivityType::SORT_RULE_CREATE); + $this->assertDatabaseHas('sort_rules', $details); + } + + public function test_listing_in_settings() + { + $rule = SortRule::factory()->create(['name' => 'My super sort rule', 'sequence' => 'name_asc']); + $books = Book::query()->limit(5)->get(); + foreach ($books as $book) { + $book->sort_rule_id = $rule->id; + $book->save(); + } + + $resp = $this->asAdmin()->get('/settings/sorting'); + $resp->assertSeeText('My super sort rule'); + $resp->assertSeeText('Name - Alphabetical (Asc)'); + $this->withHtml($resp)->assertElementContains('.item-list-row [title="Assigned to 5 Books"]', '5'); + } + + public function test_update_flow() + { + $rule = SortRule::factory()->create(['name' => 'My sort rule to update', 'sequence' => 'name_asc']); + + $resp = $this->asAdmin()->get("/settings/sorting/rules/{$rule->id}"); + $respHtml = $this->withHtml($resp); + $respHtml->assertElementContains('.configured-option-list', 'Name - Alphabetical (Asc)'); + $respHtml->assertElementNotContains('.available-option-list', 'Name - Alphabetical (Asc)'); + + $updateData = ['name' => 'My updated sort', 'sequence' => 'name_desc,chapters_last']; + $resp = $this->put("/settings/sorting/rules/{$rule->id}", $updateData); + + $resp->assertRedirect('/settings/sorting'); + $this->assertActivityExists(ActivityType::SORT_RULE_UPDATE); + $this->assertDatabaseHas('sort_rules', $updateData); + } + + public function test_update_triggers_resort_on_assigned_books() + { + $book = $this->entities->bookHasChaptersAndPages(); + $chapter = $book->chapters()->first(); + $rule = SortRule::factory()->create(['name' => 'My sort rule to update', 'sequence' => 'name_asc']); + $book->sort_rule_id = $rule->id; + $book->save(); + $chapter->priority = 10000; + $chapter->save(); + + $resp = $this->asAdmin()->put("/settings/sorting/rules/{$rule->id}", ['name' => $rule->name, 'sequence' => 'chapters_last']); + $resp->assertRedirect('/settings/sorting'); + + $chapter->refresh(); + $this->assertNotEquals(10000, $chapter->priority); + } + + public function test_delete_flow() + { + $rule = SortRule::factory()->create(); + + $resp = $this->asAdmin()->get("/settings/sorting/rules/{$rule->id}"); + $resp->assertSeeText('Delete Sort Rule'); + + $resp = $this->delete("settings/sorting/rules/{$rule->id}"); + $resp->assertRedirect('/settings/sorting'); + + $this->assertActivityExists(ActivityType::SORT_RULE_DELETE); + $this->assertDatabaseMissing('sort_rules', ['id' => $rule->id]); + } + + public function test_delete_requires_confirmation_if_books_assigned() + { + $rule = SortRule::factory()->create(); + $books = Book::query()->limit(5)->get(); + foreach ($books as $book) { + $book->sort_rule_id = $rule->id; + $book->save(); + } + + $resp = $this->asAdmin()->get("/settings/sorting/rules/{$rule->id}"); + $resp->assertSeeText('Delete Sort Rule'); + + $resp = $this->delete("settings/sorting/rules/{$rule->id}"); + $resp->assertRedirect("/settings/sorting/rules/{$rule->id}#delete"); + $resp = $this->followRedirects($resp); + + $resp->assertSeeText('This sort rule is currently used on 5 book(s). Are you sure you want to delete this?'); + $this->assertDatabaseHas('sort_rules', ['id' => $rule->id]); + + $resp = $this->delete("settings/sorting/rules/{$rule->id}", ['confirm' => 'true']); + $resp->assertRedirect('/settings/sorting'); + $this->assertDatabaseMissing('sort_rules', ['id' => $rule->id]); + $this->assertDatabaseMissing('books', ['sort_rule_id' => $rule->id]); + } + + public function test_page_create_triggers_book_sort() + { + $book = $this->entities->bookHasChaptersAndPages(); + $rule = SortRule::factory()->create(['sequence' => 'name_asc,chapters_first']); + $book->sort_rule_id = $rule->id; + $book->save(); + + $resp = $this->actingAsApiEditor()->post("/api/pages", [ + 'book_id' => $book->id, + 'name' => '1111 page', + 'markdown' => 'Hi' + ]); + $resp->assertOk(); + + $this->assertDatabaseHas('pages', [ + 'book_id' => $book->id, + 'name' => '1111 page', + 'priority' => $book->chapters()->count() + 1, + ]); + } + + public function test_auto_book_sort_does_not_touch_timestamps() + { + $book = $this->entities->bookHasChaptersAndPages(); + $rule = SortRule::factory()->create(['sequence' => 'name_asc,chapters_first']); + $book->sort_rule_id = $rule->id; + $book->save(); + $page = $book->pages()->first(); + $chapter = $book->chapters()->first(); + + $resp = $this->actingAsApiEditor()->put("/api/pages/{$page->id}", [ + 'name' => '1111 page', + ]); + $resp->assertOk(); + + $oldTime = $chapter->updated_at->unix(); + $oldPriority = $chapter->priority; + $chapter->refresh(); + $this->assertEquals($oldTime, $chapter->updated_at->unix()); + $this->assertNotEquals($oldPriority, $chapter->priority); + } + + public function test_name_alphabetical_ordering() + { + $book = Book::factory()->create(); + $rule = SortRule::factory()->create(['sequence' => 'name_asc']); + $book->sort_rule_id = $rule->id; + $book->save(); + $this->permissions->regenerateForEntity($book); + + $namesToAdd = [ + "Beans", + "bread", + "Éclaire", + "egg", + "É😀ire", + "É🫠ire", + "Milk", + "pizza", + "Tomato", + ]; + + $reverseNamesToAdd = array_reverse($namesToAdd); + foreach ($reverseNamesToAdd as $name) { + $this->actingAsApiEditor()->post("/api/pages", [ + 'book_id' => $book->id, + 'name' => $name, + 'markdown' => 'Hello' + ]); + } + + foreach ($namesToAdd as $index => $name) { + $this->assertDatabaseHas('pages', [ + 'book_id' => $book->id, + 'name' => $name, + 'priority' => $index + 1, + ]); + } + } + + public function test_name_numeric_ordering() + { + $book = Book::factory()->create(); + $rule = SortRule::factory()->create(['sequence' => 'name_numeric_asc']); + $book->sort_rule_id = $rule->id; + $book->save(); + $this->permissions->regenerateForEntity($book); + + $namesToAdd = [ + "1 - Pizza", + "2.0 - Tomato", + "2.5 - Beans", + "10 - Bread", + "20 - Milk", + ]; + + $reverseNamesToAdd = array_reverse($namesToAdd); + foreach ($reverseNamesToAdd as $name) { + $this->actingAsApiEditor()->post("/api/pages", [ + 'book_id' => $book->id, + 'name' => $name, + 'markdown' => 'Hello' + ]); + } + + foreach ($namesToAdd as $index => $name) { + $this->assertDatabaseHas('pages', [ + 'book_id' => $book->id, + 'name' => $name, + 'priority' => $index + 1, + ]); + } + } + + public function test_each_sort_rule_operation_has_a_comparison_function() + { + $operations = SortRuleOperation::cases(); + + foreach ($operations as $operation) { + $comparisonFunc = $operation->getSortFunction(); + $this->assertIsCallable($comparisonFunc); + } + } +} diff --git a/tests/TestCase.php b/tests/TestCase.php index c59f843e94b..a8636fb158e 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -118,32 +118,41 @@ protected function mockHttpClient(array $responses = []): HttpClientHistory * Database config is juggled so the value can be restored when * parallel testing are used, where multiple databases exist. */ - protected function runWithEnv(string $name, $value, callable $callback) + protected function runWithEnv(array $valuesByKey, callable $callback, bool $handleDatabase = true): void { Env::disablePutenv(); - $originalVal = $_SERVER[$name] ?? null; - - if (is_null($value)) { - unset($_SERVER[$name]); - } else { - $_SERVER[$name] = $value; + $originals = []; + foreach ($valuesByKey as $key => $value) { + $originals[$key] = $_SERVER[$key] ?? null; + + if (is_null($value)) { + unset($_SERVER[$key]); + } else { + $_SERVER[$key] = $value; + } } $database = config('database.connections.mysql_testing.database'); $this->refreshApplication(); - DB::purge(); - config()->set('database.connections.mysql_testing.database', $database); - DB::beginTransaction(); + if ($handleDatabase) { + DB::purge(); + config()->set('database.connections.mysql_testing.database', $database); + DB::beginTransaction(); + } $callback(); - DB::rollBack(); + if ($handleDatabase) { + DB::rollBack(); + } - if (is_null($originalVal)) { - unset($_SERVER[$name]); - } else { - $_SERVER[$name] = $originalVal; + foreach ($originals as $key => $value) { + if (is_null($value)) { + unset($_SERVER[$key]); + } else { + $_SERVER[$key] = $value; + } } } @@ -248,8 +257,8 @@ protected function assertActivityExists(string $type, ?Entity $entity = null, st $detailsToCheck = ['type' => $type]; if ($entity) { - $detailsToCheck['entity_type'] = $entity->getMorphClass(); - $detailsToCheck['entity_id'] = $entity->id; + $detailsToCheck['loggable_type'] = $entity->getMorphClass(); + $detailsToCheck['loggable_id'] = $entity->id; } if ($detail) { diff --git a/tests/ThemeTest.php b/tests/ThemeTest.php index a7a46521a6e..4dff384186c 100644 --- a/tests/ThemeTest.php +++ b/tests/ThemeTest.php @@ -46,7 +46,7 @@ public function test_theme_functions_file_used_and_app_boot_event_runs() $functionsFile = theme_path('functions.php'); app()->alias('cat', 'dog'); file_put_contents($functionsFile, "alias('cat', 'dog');});"); - $this->runWithEnv('APP_THEME', $themeFolder, function () { + $this->runWithEnv(['APP_THEME' => $themeFolder], function () { $this->assertEquals('cat', $this->app->getAlias('dog')); }); }); @@ -61,7 +61,7 @@ public function test_theme_functions_loads_errors_are_caught_and_logged() $this->expectException(ThemeException::class); $this->expectExceptionMessageMatches('/Failed loading theme functions file at ".*?" with error: Class "BookStack\\\\Biscuits" not found/'); - $this->runWithEnv('APP_THEME', $themeFolder, fn() => null); + $this->runWithEnv(['APP_THEME' => $themeFolder], fn() => null); }); } @@ -178,6 +178,43 @@ public function test_event_auth_register_standard() $this->assertInstanceOf(User::class, $args[1]); } + public function test_event_auth_pre_register() + { + $args = []; + $callback = function (...$eventArgs) use (&$args) { + $args = $eventArgs; + }; + Theme::listen(ThemeEvents::AUTH_PRE_REGISTER, $callback); + $this->setSettings(['registration-enabled' => 'true']); + + $user = User::factory()->make(); + $this->post('/register', ['email' => $user->email, 'name' => $user->name, 'password' => 'password']); + + $this->assertCount(2, $args); + $this->assertEquals('standard', $args[0]); + $this->assertEquals([ + 'email' => $user->email, + 'name' => $user->name, + 'password' => 'password', + ], $args[1]); + $this->assertDatabaseHas('users', ['email' => $user->email]); + } + + public function test_event_auth_pre_register_with_false_return_blocks_registration() + { + $callback = function () { + return false; + }; + Theme::listen(ThemeEvents::AUTH_PRE_REGISTER, $callback); + $this->setSettings(['registration-enabled' => 'true']); + + $user = User::factory()->make(); + $resp = $this->post('/register', ['email' => $user->email, 'name' => $user->name, 'password' => 'password']); + $resp->assertRedirect('/login'); + $this->assertSessionError('User account could not be registered for the provided details'); + $this->assertDatabaseMissing('users', ['email' => $user->email]); + } + public function test_event_webhook_call_before() { $args = []; @@ -414,6 +451,47 @@ public function test_header_links_start_template_file_can_be_used() }); } + public function test_custom_settings_category_page_can_be_added_via_view_file() + { + $content = 'My SuperCustomSettings'; + + $this->usingThemeFolder(function (string $folder) use ($content) { + $viewDir = theme_path('settings/categories'); + mkdir($viewDir, 0777, true); + file_put_contents($viewDir . '/beans.blade.php', $content); + + $this->asAdmin()->get('/settings/beans')->assertSee($content); + }); + } + + public function test_public_folder_contents_accessible_via_route() + { + $this->usingThemeFolder(function (string $themeFolderName) { + $publicDir = theme_path('public'); + mkdir($publicDir, 0777, true); + + $text = 'some-text ' . md5(random_bytes(5)); + $css = "body { background-color: tomato !important; }"; + file_put_contents("{$publicDir}/file.txt", $text); + file_put_contents("{$publicDir}/file.css", $css); + copy($this->files->testFilePath('test-image.png'), "{$publicDir}/image.png"); + + $resp = $this->asAdmin()->get("/theme/{$themeFolderName}/file.txt"); + $resp->assertStreamedContent($text); + $resp->assertHeader('Content-Type', 'text/plain; charset=UTF-8'); + $resp->assertHeader('Cache-Control', 'max-age=86400, private'); + + $resp = $this->asAdmin()->get("/theme/{$themeFolderName}/image.png"); + $resp->assertHeader('Content-Type', 'image/png'); + $resp->assertHeader('Cache-Control', 'max-age=86400, private'); + + $resp = $this->asAdmin()->get("/theme/{$themeFolderName}/file.css"); + $resp->assertStreamedContent($css); + $resp->assertHeader('Content-Type', 'text/css; charset=UTF-8'); + $resp->assertHeader('Cache-Control', 'max-age=86400, private'); + }); + } + protected function usingThemeFolder(callable $callback) { // Create a folder and configure a theme @@ -426,7 +504,7 @@ protected function usingThemeFolder(callable $callback) $this->beforeApplicationDestroyed(fn() => File::deleteDirectory($themeFolderPath)); // Run provided callback with theme env option set - $this->runWithEnv('APP_THEME', $themeFolderName, function () use ($callback, $themeFolderName) { + $this->runWithEnv(['APP_THEME' => $themeFolderName], function () use ($callback, $themeFolderName) { call_user_func($callback, $themeFolderName); }); } diff --git a/tests/Unit/ConfigTest.php b/tests/Unit/ConfigTest.php index 2de32c1b8c8..7795a861a0c 100644 --- a/tests/Unit/ConfigTest.php +++ b/tests/Unit/ConfigTest.php @@ -16,7 +16,7 @@ class ConfigTest extends TestCase { public function test_filesystem_images_falls_back_to_storage_type_var() { - $this->runWithEnv('STORAGE_TYPE', 'local_secure', function () { + $this->runWithEnv(['STORAGE_TYPE' => 'local_secure'], function () { $this->checkEnvConfigResult('STORAGE_IMAGE_TYPE', 's3', 'filesystems.images', 's3'); $this->checkEnvConfigResult('STORAGE_IMAGE_TYPE', null, 'filesystems.images', 'local_secure'); }); @@ -24,7 +24,7 @@ public function test_filesystem_images_falls_back_to_storage_type_var() public function test_filesystem_attachments_falls_back_to_storage_type_var() { - $this->runWithEnv('STORAGE_TYPE', 'local_secure', function () { + $this->runWithEnv(['STORAGE_TYPE' => 'local_secure'], function () { $this->checkEnvConfigResult('STORAGE_ATTACHMENT_TYPE', 's3', 'filesystems.attachments', 's3'); $this->checkEnvConfigResult('STORAGE_ATTACHMENT_TYPE', null, 'filesystems.attachments', 'local_secure'); }); @@ -80,22 +80,22 @@ public function test_saml2_idp_authn_context_string_parsed_as_space_separated_ar public function test_dompdf_remote_fetching_controlled_by_allow_untrusted_server_fetching_false() { - $this->checkEnvConfigResult('ALLOW_UNTRUSTED_SERVER_FETCHING', 'false', 'dompdf.options.enable_remote', false); - $this->checkEnvConfigResult('ALLOW_UNTRUSTED_SERVER_FETCHING', 'true', 'dompdf.options.enable_remote', true); + $this->checkEnvConfigResult('ALLOW_UNTRUSTED_SERVER_FETCHING', 'false', 'exports.dompdf.enable_remote', false); + $this->checkEnvConfigResult('ALLOW_UNTRUSTED_SERVER_FETCHING', 'true', 'exports.dompdf.enable_remote', true); } public function test_dompdf_paper_size_options_are_limited() { - $this->checkEnvConfigResult('EXPORT_PAGE_SIZE', 'cat', 'dompdf.options.default_paper_size', 'a4'); - $this->checkEnvConfigResult('EXPORT_PAGE_SIZE', 'letter', 'dompdf.options.default_paper_size', 'letter'); - $this->checkEnvConfigResult('EXPORT_PAGE_SIZE', 'a4', 'dompdf.options.default_paper_size', 'a4'); + $this->checkEnvConfigResult('EXPORT_PAGE_SIZE', 'cat', 'exports.dompdf.default_paper_size', 'a4'); + $this->checkEnvConfigResult('EXPORT_PAGE_SIZE', 'letter', 'exports.dompdf.default_paper_size', 'letter'); + $this->checkEnvConfigResult('EXPORT_PAGE_SIZE', 'a4', 'exports.dompdf.default_paper_size', 'a4'); } public function test_snappy_paper_size_options_are_limited() { - $this->checkEnvConfigResult('EXPORT_PAGE_SIZE', 'cat', 'snappy.pdf.options.page-size', 'A4'); - $this->checkEnvConfigResult('EXPORT_PAGE_SIZE', 'letter', 'snappy.pdf.options.page-size', 'Letter'); - $this->checkEnvConfigResult('EXPORT_PAGE_SIZE', 'a4', 'snappy.pdf.options.page-size', 'A4'); + $this->checkEnvConfigResult('EXPORT_PAGE_SIZE', 'cat', 'exports.snappy.options.page-size', 'A4'); + $this->checkEnvConfigResult('EXPORT_PAGE_SIZE', 'letter', 'exports.snappy.options.page-size', 'Letter'); + $this->checkEnvConfigResult('EXPORT_PAGE_SIZE', 'a4', 'exports.snappy.options.page-size', 'A4'); } public function test_sendmail_command_is_configurable() @@ -114,7 +114,7 @@ public function test_mail_disable_ssl_verification_alters_mailer() $this->assertEmpty($getStreamOptions()); - $this->runWithEnv('MAIL_VERIFY_SSL', 'false', function () use ($getStreamOptions) { + $this->runWithEnv(['MAIL_VERIFY_SSL' => 'false'], function () use ($getStreamOptions) { $options = $getStreamOptions(); $this->assertArrayHasKey('ssl', $options); $this->assertFalse($options['ssl']['verify_peer']); @@ -124,52 +124,60 @@ public function test_mail_disable_ssl_verification_alters_mailer() public function test_non_null_mail_encryption_options_enforce_smtp_scheme() { - $this->checkEnvConfigResult('MAIL_ENCRYPTION', 'tls', 'mail.mailers.smtp.tls_required', true); - $this->checkEnvConfigResult('MAIL_ENCRYPTION', 'ssl', 'mail.mailers.smtp.tls_required', true); - $this->checkEnvConfigResult('MAIL_ENCRYPTION', 'null', 'mail.mailers.smtp.tls_required', false); + $this->checkEnvConfigResult('MAIL_ENCRYPTION', 'tls', 'mail.mailers.smtp.require_tls', true); + $this->checkEnvConfigResult('MAIL_ENCRYPTION', 'ssl', 'mail.mailers.smtp.require_tls', true); + $this->checkEnvConfigResult('MAIL_ENCRYPTION', 'null', 'mail.mailers.smtp.require_tls', false); } public function test_smtp_scheme_and_certain_port_forces_tls_usage() { $isMailTlsRequired = function () { - /** @var \BookStack\App\Mail\EsmtpTransport $transport */ + /** @var EsmtpTransport $transport */ $transport = Mail::mailer('smtp')->getSymfonyTransport(); Mail::purge('smtp'); - return $transport->getTlsRequirement(); + return $transport->isTlsRequired(); }; - config()->set([ - 'mail.mailers.smtp.tls_required' => null, - 'mail.mailers.smtp.port' => 587, - ]); - - $this->assertFalse($isMailTlsRequired()); - - config()->set([ - 'mail.mailers.smtp.tls_required' => 'tls', - 'mail.mailers.smtp.port' => 587, - ]); - - $this->assertTrue($isMailTlsRequired()); + $runTest = function (string $tlsOption, int $port, bool $expectedResult) use ($isMailTlsRequired) { + $this->runWithEnv(['MAIL_ENCRYPTION' => $tlsOption, 'MAIL_PORT' => $port], function () use ($isMailTlsRequired, $port, $expectedResult) { + $this->assertEquals($expectedResult, $isMailTlsRequired()); + }); + }; - config()->set([ - 'mail.mailers.smtp.tls_required' => null, - 'mail.mailers.smtp.port' => 465, - ]); + $runTest('null', 587, false); + $runTest('tls', 587, true); + $runTest('null', 465, true); + } - $this->assertTrue($isMailTlsRequired()); + public function test_mysql_host_parsed_as_expected() + { + $cases = [ + '127.0.0.1' => ['127.0.0.1', 3306], + '127.0.0.1:3307' => ['127.0.0.1', 3307], + 'a.example.com' => ['a.example.com', 3306], + 'a.example.com:3307' => ['a.example.com', 3307], + '[::1]' => ['[::1]', 3306], + '[::1]:123' => ['[::1]', 123], + '[2001:db8:3c4d:0015:0000:0000:1a2f]' => ['[2001:db8:3c4d:0015:0000:0000:1a2f]', 3306], + '[2001:db8:3c4d:0015:0000:0000:1a2f]:4567' => ['[2001:db8:3c4d:0015:0000:0000:1a2f]', 4567], + ]; + + foreach ($cases as $host => [$expectedHost, $expectedPort]) { + $this->runWithEnv(["DB_HOST" => $host], function () use ($expectedHost, $expectedPort) { + $this->assertEquals($expectedHost, config("database.connections.mysql.host")); + $this->assertEquals($expectedPort, config("database.connections.mysql.port")); + }, false); + } } /** * Set an environment variable of the given name and value * then check the given config key to see if it matches the given result. * Providing a null $envVal clears the variable. - * - * @param mixed $expectedResult */ - protected function checkEnvConfigResult(string $envName, ?string $envVal, string $configKey, $expectedResult) + protected function checkEnvConfigResult(string $envName, ?string $envVal, string $configKey, mixed $expectedResult): void { - $this->runWithEnv($envName, $envVal, function () use ($configKey, $expectedResult) { + $this->runWithEnv([$envName => $envVal], function () use ($configKey, $expectedResult) { $this->assertEquals($expectedResult, config($configKey)); }); } diff --git a/tests/Unit/OidcIdTokenTest.php b/tests/Unit/OidcIdTokenTest.php index 6302f84c75f..73932326694 100644 --- a/tests/Unit/OidcIdTokenTest.php +++ b/tests/Unit/OidcIdTokenTest.php @@ -113,7 +113,7 @@ public function test_token_claim_error_cases() // 2. aud claim present ['Missing token audience value', ['aud' => null]], // 2. aud claim validates all values against those expected (Only expect single) - ['Token audience value has 2 values, Expected 1', ['aud' => ['abc', 'def']]], + ['Token audience value has 2 values, Expected 1', ['aud' => ['xxyyzz.aaa.bbccdd.123', 'def']]], // 2. aud claim matches client id ['Token audience value did not match the expected client_id', ['aud' => 'xxyyzz.aaa.bbccdd.456']], // 4. azp claim matches client id if present diff --git a/tests/Uploads/AttachmentTest.php b/tests/Uploads/AttachmentTest.php index bd03c339c43..2eaf21d9c6f 100644 --- a/tests/Uploads/AttachmentTest.php +++ b/tests/Uploads/AttachmentTest.php @@ -267,6 +267,50 @@ public function test_data_and_js_links_cannot_be_attached_to_a_page() } } + public function test_attachment_delete_only_shows_with_permission() + { + $this->asAdmin(); + $page = $this->entities->page(); + $this->files->uploadAttachmentFile($this, 'upload_test.txt', $page->id); + $attachment = $page->attachments()->first(); + $viewer = $this->users->viewer(); + + $this->permissions->grantUserRolePermissions($viewer, ['page-update-all', 'attachment-create-all']); + + $resp = $this->actingAs($viewer)->get($page->getUrl('/edit')); + $html = $this->withHtml($resp); + $html->assertElementExists(".card[data-id=\"{$attachment->id}\"]"); + $html->assertElementNotExists(".card[data-id=\"{$attachment->id}\"] button[title=\"Delete\"]"); + + $this->permissions->grantUserRolePermissions($viewer, ['attachment-delete-all']); + + $resp = $this->actingAs($viewer)->get($page->getUrl('/edit')); + $html = $this->withHtml($resp); + $html->assertElementExists(".card[data-id=\"{$attachment->id}\"] button[title=\"Delete\"]"); + } + + public function test_attachment_edit_only_shows_with_permission() + { + $this->asAdmin(); + $page = $this->entities->page(); + $this->files->uploadAttachmentFile($this, 'upload_test.txt', $page->id); + $attachment = $page->attachments()->first(); + $viewer = $this->users->viewer(); + + $this->permissions->grantUserRolePermissions($viewer, ['page-update-all', 'attachment-create-all']); + + $resp = $this->actingAs($viewer)->get($page->getUrl('/edit')); + $html = $this->withHtml($resp); + $html->assertElementExists(".card[data-id=\"{$attachment->id}\"]"); + $html->assertElementNotExists(".card[data-id=\"{$attachment->id}\"] button[title=\"Edit\"]"); + + $this->permissions->grantUserRolePermissions($viewer, ['attachment-update-all']); + + $resp = $this->actingAs($viewer)->get($page->getUrl('/edit')); + $html = $this->withHtml($resp); + $html->assertElementExists(".card[data-id=\"{$attachment->id}\"] button[title=\"Edit\"]"); + } + public function test_file_access_with_open_query_param_provides_inline_response_with_correct_content_type() { $page = $this->entities->page(); @@ -316,4 +360,112 @@ public function test_file_upload_works_when_local_secure_restricted_is_in_use() $this->assertFileExists(storage_path($attachment->path)); $this->files->deleteAllAttachmentFiles(); } + + public function test_file_get_range_access() + { + $page = $this->entities->page(); + $this->asAdmin(); + $attachment = $this->files->uploadAttachmentDataToPage($this, $page, 'my_text.txt', 'abc123456', 'text/plain'); + + // Download access + $resp = $this->get($attachment->getUrl(), ['Range' => 'bytes=3-5']); + $resp->assertStatus(206); + $resp->assertStreamedContent('123'); + $resp->assertHeader('Content-Length', '3'); + $resp->assertHeader('Content-Range', 'bytes 3-5/9'); + + // Inline access + $resp = $this->get($attachment->getUrl(true), ['Range' => 'bytes=5-7']); + $resp->assertStatus(206); + $resp->assertStreamedContent('345'); + $resp->assertHeader('Content-Length', '3'); + $resp->assertHeader('Content-Range', 'bytes 5-7/9'); + + $this->files->deleteAllAttachmentFiles(); + } + + public function test_file_head_range_returns_no_content() + { + $page = $this->entities->page(); + $this->asAdmin(); + $attachment = $this->files->uploadAttachmentDataToPage($this, $page, 'my_text.txt', 'abc123456', 'text/plain'); + + $resp = $this->head($attachment->getUrl(), ['Range' => 'bytes=0-9']); + $resp->assertStreamedContent(''); + $resp->assertHeader('Content-Length', '9'); + $resp->assertStatus(200); + + $this->files->deleteAllAttachmentFiles(); + } + + public function test_file_head_range_edge_cases() + { + $page = $this->entities->page(); + $this->asAdmin(); + + // Mime-type "sniffing" happens on first 2k bytes, hence this content (2005 bytes) + $content = '01234' . str_repeat('a', 1990) . '0123456789'; + $attachment = $this->files->uploadAttachmentDataToPage($this, $page, 'my_text.txt', $content, 'text/plain'); + + // Test for both inline and download attachment serving + foreach ([true, false] as $isInline) { + // No end range + $resp = $this->get($attachment->getUrl($isInline), ['Range' => 'bytes=5-']); + $resp->assertStreamedContent(substr($content, 5)); + $resp->assertHeader('Content-Length', '2000'); + $resp->assertHeader('Content-Range', 'bytes 5-2004/2005'); + $resp->assertStatus(206); + + // End only range + $resp = $this->get($attachment->getUrl($isInline), ['Range' => 'bytes=-10']); + $resp->assertStreamedContent('0123456789'); + $resp->assertHeader('Content-Length', '10'); + $resp->assertHeader('Content-Range', 'bytes 1995-2004/2005'); + $resp->assertStatus(206); + + // Range across sniff point + $resp = $this->get($attachment->getUrl($isInline), ['Range' => 'bytes=1997-2002']); + $resp->assertStreamedContent('234567'); + $resp->assertHeader('Content-Length', '6'); + $resp->assertHeader('Content-Range', 'bytes 1997-2002/2005'); + $resp->assertStatus(206); + + // Range up to sniff point + $resp = $this->get($attachment->getUrl($isInline), ['Range' => 'bytes=0-1997']); + $resp->assertHeader('Content-Length', '1998'); + $resp->assertHeader('Content-Range', 'bytes 0-1997/2005'); + $resp->assertStreamedContent(substr($content, 0, 1998)); + $resp->assertStatus(206); + + // Range beyond sniff point + $resp = $this->get($attachment->getUrl($isInline), ['Range' => 'bytes=2001-2003']); + $resp->assertStreamedContent('678'); + $resp->assertHeader('Content-Length', '3'); + $resp->assertHeader('Content-Range', 'bytes 2001-2003/2005'); + $resp->assertStatus(206); + + // Range beyond content + $resp = $this->get($attachment->getUrl($isInline), ['Range' => 'bytes=0-2010']); + $resp->assertStreamedContent($content); + $resp->assertHeader('Content-Length', '2005'); + $resp->assertHeader('Content-Range', 'bytes 0-2004/2005'); + $resp->assertStatus(206); + + // Range start before end + $resp = $this->get($attachment->getUrl($isInline), ['Range' => 'bytes=50-10']); + $resp->assertStreamedContent($content); + $resp->assertHeader('Content-Length', '2005'); + $resp->assertHeader('Content-Range', 'bytes */2005'); + $resp->assertStatus(416); + + // Full range request + $resp = $this->get($attachment->getUrl($isInline), ['Range' => 'bytes=0-']); + $resp->assertStreamedContent($content); + $resp->assertHeader('Content-Length', '2005'); + $resp->assertHeader('Content-Range', 'bytes 0-2004/2005'); + $resp->assertStatus(206); + } + + $this->files->deleteAllAttachmentFiles(); + } } diff --git a/tests/Uploads/ImageStorageTest.php b/tests/Uploads/ImageStorageTest.php new file mode 100644 index 00000000000..117da7f5299 --- /dev/null +++ b/tests/Uploads/ImageStorageTest.php @@ -0,0 +1,30 @@ +markTestSkipped('Test only works on Linux'); + } + + config()->set('filesystems.default', 'local'); + $storage = $this->app->make(ImageStorage::class); + $dirToCheck = 'test-dir-perms-' . substr(md5(random_bytes(16)), 0, 6); + + $disk = $storage->getDisk('gallery'); + $disk->put("{$dirToCheck}/image.png", 'abc', true); + + $expectedPath = public_path("uploads/images/{$dirToCheck}"); + $permissionsApplied = substr(sprintf('%o', fileperms($expectedPath)), -4); + $this->assertEquals('0755', $permissionsApplied); + + @unlink("{$expectedPath}/image.png"); + @rmdir($expectedPath); + } +} diff --git a/tests/Uploads/ImageTest.php b/tests/Uploads/ImageTest.php index af249951f30..a2f03df34d4 100644 --- a/tests/Uploads/ImageTest.php +++ b/tests/Uploads/ImageTest.php @@ -68,7 +68,20 @@ public function test_image_display_thumbnail_generation_for_apng_images_uses_ori $this->files->deleteAtRelativePath($imgDetails['path']); $this->assertStringContainsString('thumbs-', $imgDetails['response']->thumbs->gallery); - $this->assertStringNotContainsString('thumbs-', $imgDetails['response']->thumbs->display); + $this->assertStringNotContainsString('scaled-', $imgDetails['response']->thumbs->display); + } + + public function test_image_display_thumbnail_generation_for_animated_avif_images_uses_original_file() + { + $page = $this->entities->page(); + $admin = $this->users->admin(); + $this->actingAs($admin); + + $imgDetails = $this->files->uploadGalleryImageToPage($this, $page, 'animated.avif'); + $this->files->deleteAtRelativePath($imgDetails['path']); + + $this->assertStringContainsString('thumbs-', $imgDetails['response']->thumbs->gallery); + $this->assertStringNotContainsString('scaled-', $imgDetails['response']->thumbs->display); } public function test_image_edit() @@ -119,6 +132,33 @@ public function test_image_file_update() $this->files->deleteAtRelativePath($relPath); } + public function test_image_file_update_allows_case_differences() + { + $page = $this->entities->page(); + $this->asEditor(); + + $imgDetails = $this->files->uploadGalleryImageToPage($this, $page); + $relPath = $imgDetails['path']; + + $newUpload = $this->files->uploadedImage('updated-image.PNG', 'compressed.png'); + $this->assertFileEquals($this->files->testFilePath('test-image.png'), public_path($relPath)); + + $imageId = $imgDetails['response']->id; + $image = Image::findOrFail($imageId); + $image->updated_at = now()->subMonth(); + $image->save(); + + $this->call('PUT', "/images/{$imageId}/file", [], [], ['file' => $newUpload]) + ->assertOk(); + + $this->assertFileEquals($this->files->testFilePath('compressed.png'), public_path($relPath)); + + $image->refresh(); + $this->assertTrue($image->updated_at->gt(now()->subMinute())); + + $this->files->deleteAtRelativePath($relPath); + } + public function test_image_file_update_does_not_allow_change_in_image_extension() { $page = $this->entities->page(); @@ -383,6 +423,29 @@ public function test_system_images_remain_public_with_local_secure() } } + public function test_secure_images_not_tracked_in_session_history() + { + config()->set('filesystems.images', 'local_secure'); + $this->asEditor(); + $page = $this->entities->page(); + $result = $this->files->uploadGalleryImageToPage($this, $page); + $expectedPath = storage_path($result['path']); + $this->assertFileExists($expectedPath); + + $this->get('/books'); + $this->assertEquals(url('/books'), session()->previousUrl()); + + $resp = $this->get($result['path']); + $resp->assertOk(); + $resp->assertHeader('Content-Type', 'image/png'); + + $this->assertEquals(url('/books'), session()->previousUrl()); + + if (file_exists($expectedPath)) { + unlink($expectedPath); + } + } + public function test_system_images_remain_public_with_local_secure_restricted() { config()->set('filesystems.images', 'local_secure_restricted'); @@ -576,6 +639,40 @@ public function test_image_manager_regen_thumbnails() $this->files->deleteAtRelativePath($relPath); } + public function test_gif_thumbnail_generation() + { + $this->asAdmin(); + $originalFile = $this->files->testFilePath('animated.gif'); + $originalFileSize = filesize($originalFile); + + $imgDetails = $this->files->uploadGalleryImageToPage($this, $this->entities->page(), 'animated.gif'); + $relPath = $imgDetails['path']; + + $this->assertTrue(file_exists(public_path($relPath)), 'Uploaded image found at path: ' . public_path($relPath)); + $galleryThumb = $imgDetails['response']->thumbs->gallery; + $displayThumb = $imgDetails['response']->thumbs->display; + + // Ensure display thumbnail is original image + $this->assertStringEndsWith($imgDetails['path'], $displayThumb); + $this->assertStringNotContainsString('thumbs', $displayThumb); + + // Ensure gallery thumbnail is reduced image (single frame) + $galleryThumbRelPath = implode('/', array_slice(explode('/', $galleryThumb), 3)); + $galleryThumbPath = public_path($galleryThumbRelPath); + $galleryFileSize = filesize($galleryThumbPath); + + // Basic scan of GIF content to check frame count + $originalFrameCount = count(explode("\x00\x21\xF9", file_get_contents($originalFile))); + $galleryFrameCount = count(explode("\x00\x21\xF9", file_get_contents($galleryThumbPath))); + + $this->files->deleteAtRelativePath($relPath); + $this->files->deleteAtRelativePath($galleryThumbRelPath); + + $this->assertNotEquals($originalFileSize, $galleryFileSize); + $this->assertEquals(3, $originalFrameCount); + $this->assertEquals(1, $galleryFrameCount); + } + protected function getTestProfileImage() { $imageName = 'profile.png'; diff --git a/tests/UrlTest.php b/tests/UrlTest.php index c1e133804df..c1a4d4f1c27 100644 --- a/tests/UrlTest.php +++ b/tests/UrlTest.php @@ -8,14 +8,14 @@ class UrlTest extends TestCase { public function test_url_helper_takes_custom_url_into_account() { - $this->runWithEnv('APP_URL', 'http://example.com/bookstack', function () { + $this->runWithEnv(['APP_URL' => 'http://example.com/bookstack'], function () { $this->assertEquals('http://example.com/bookstack/books', url('/books')); }); } public function test_url_helper_sets_correct_scheme_even_when_request_scheme_is_different() { - $this->runWithEnv('APP_URL', 'https://example.com/', function () { + $this->runWithEnv(['APP_URL' => 'https://example.com/'], function () { $this->get('http://example.com/login')->assertSee('https://example.com/dist/styles.css'); }); } diff --git a/tests/User/RoleManagementTest.php b/tests/User/RoleManagementTest.php index 9e5cf78dd84..8683fcb6e86 100644 --- a/tests/User/RoleManagementTest.php +++ b/tests/User/RoleManagementTest.php @@ -96,6 +96,31 @@ public function test_role_create_update_delete_flow() $this->assertActivityExists(ActivityType::ROLE_DELETE); } + public function test_role_external_auth_id_validation() + { + config()->set('auth.method', 'oidc'); + $role = Role::query()->first(); + $routeByMethod = [ + 'post' => '/settings/roles/new', + 'put' => "/settings/roles/{$role->id}", + ]; + + foreach ($routeByMethod as $method => $route) { + $resp = $this->asAdmin()->get($route); + $resp->assertDontSee('The external auth id'); + + $resp = $this->asAdmin()->call($method, $route, [ + 'display_name' => 'Test role for auth id validation', + 'description' => '', + 'external_auth_id' => str_repeat('a', 181), + ]); + + $resp->assertRedirect($route); + $resp = $this->followRedirects($resp); + $resp->assertSee('The external auth id may not be greater than 180 characters.'); + } + } + public function test_admin_role_cannot_be_removed_if_user_last_admin() { /** @var Role $adminRole */ diff --git a/tests/User/UserManagementTest.php b/tests/User/UserManagementTest.php index 93d35f5d047..d92f13f0b3f 100644 --- a/tests/User/UserManagementTest.php +++ b/tests/User/UserManagementTest.php @@ -2,6 +2,7 @@ namespace Tests\User; +use BookStack\Access\UserInviteException; use BookStack\Access\UserInviteService; use BookStack\Activity\ActivityType; use BookStack\Uploads\Image; @@ -10,7 +11,6 @@ use Illuminate\Support\Facades\Hash; use Illuminate\Support\Str; use Mockery\MockInterface; -use RuntimeException; use Tests\TestCase; class UserManagementTest extends TestCase @@ -85,6 +85,16 @@ public function test_user_password_update() $this->assertTrue(Hash::check('newpassword', $userPassword)); } + public function test_user_can_be_updated_with_single_char_name() + { + $user = $this->users->viewer(); + $this->asAdmin()->put("/settings/users/{$user->id}", [ + 'name' => 'b' + ])->assertRedirect('/settings/users'); + + $this->assertEquals('b', $user->refresh()->name); + } + public function test_user_cannot_be_deleted_if_last_admin() { $adminRole = Role::getRole('admin'); @@ -192,9 +202,13 @@ public function test_delete_removes_user_preferences() public function test_guest_profile_shows_limited_form() { $guest = $this->users->guest(); + $resp = $this->asAdmin()->get('/settings/users/' . $guest->id); $resp->assertSee('Guest'); - $this->withHtml($resp)->assertElementNotExists('#password'); + $html = $this->withHtml($resp); + + $html->assertElementNotExists('#password'); + $html->assertElementNotExists('[name="language"]'); } public function test_guest_profile_cannot_be_deleted() @@ -229,7 +243,7 @@ public function test_user_creation_is_not_performed_if_the_invitation_sending_fa // Simulate an invitation sending failure $this->mock(UserInviteService::class, function (MockInterface $mock) { - $mock->shouldReceive('sendInvitation')->once()->andThrow(RuntimeException::class); + $mock->shouldReceive('sendInvitation')->once()->andThrow(UserInviteException::class); }); $this->asAdmin()->post('/settings/users/create', [ @@ -247,22 +261,42 @@ public function test_user_create_activity_is_not_persisted_if_the_invitation_sen { /** @var User $user */ $user = User::factory()->make(); - $adminRole = Role::getRole('admin'); $this->mock(UserInviteService::class, function (MockInterface $mock) { - $mock->shouldReceive('sendInvitation')->once()->andThrow(RuntimeException::class); + $mock->shouldReceive('sendInvitation')->once()->andThrow(UserInviteException::class); }); $this->asAdmin()->post('/settings/users/create', [ 'name' => $user->name, 'email' => $user->email, 'send_invite' => 'true', - 'roles[' . $adminRole->id . ']' => 'true', ]); $this->assertDatabaseMissing('activities', ['type' => 'USER_CREATE']); } + public function test_return_to_form_with_warning_if_the_invitation_sending_fails() + { + $logger = $this->withTestLogger(); + /** @var User $user */ + $user = User::factory()->make(); + + $this->mock(UserInviteService::class, function (MockInterface $mock) { + $mock->shouldReceive('sendInvitation')->once()->andThrow(UserInviteException::class); + }); + + $resp = $this->asAdmin()->post('/settings/users/create', [ + 'name' => $user->name, + 'email' => $user->email, + 'send_invite' => 'true', + ]); + + $resp->assertRedirect('/settings/users/create'); + $this->assertSessionError('Could not create user since invite email failed to send'); + $this->assertEquals($user->email, session()->getOldInput('email')); + $this->assertTrue($logger->hasErrorThatContains('Failed to send user invite with error:')); + } + public function test_user_create_update_fails_if_locale_is_invalid() { $user = $this->users->editor(); diff --git a/tests/User/UserProfileTest.php b/tests/User/UserProfileTest.php index 4bfb3c87822..065ae8dc896 100644 --- a/tests/User/UserProfileTest.php +++ b/tests/User/UserProfileTest.php @@ -2,8 +2,8 @@ namespace Tests\User; -use Activity; use BookStack\Activity\ActivityType; +use BookStack\Facades\Activity; use BookStack\Users\Models\User; use Tests\TestCase; diff --git a/tests/Util/DateFormatterTest.php b/tests/Util/DateFormatterTest.php new file mode 100644 index 00000000000..1c0a458e078 --- /dev/null +++ b/tests/Util/DateFormatterTest.php @@ -0,0 +1,37 @@ +absolute($dateTime); + $this->assertEquals('2020-06-01 13:00:00 BST', $result); + } + + public function test_iso_with_timezone_works_from_non_utc_dates() + { + $formatter = new DateFormatter('Asia/Shanghai'); + $dateTime = new Carbon('2025-06-10 15:25:00', 'America/New_York'); + + $result = $formatter->absolute($dateTime); + $this->assertEquals('2025-06-11 03:25:00 CST', $result); + } + + public function test_relative() + { + $formatter = new DateFormatter('Europe/London'); + $dateTime = (new Carbon('now', 'UTC'))->subMinutes(50); + + $result = $formatter->relative($dateTime); + $this->assertEquals('50 minutes ago', $result); + } +} diff --git a/tests/test-data/animated.avif b/tests/test-data/animated.avif new file mode 100644 index 00000000000..92f71459bb8 Binary files /dev/null and b/tests/test-data/animated.avif differ diff --git a/tests/test-data/animated.gif b/tests/test-data/animated.gif new file mode 100644 index 00000000000..13c9d05243a Binary files /dev/null and b/tests/test-data/animated.gif differ diff --git a/tests/test-data/test-image.jpg b/tests/test-data/test-image.jpg new file mode 100644 index 00000000000..9bb74ff0483 Binary files /dev/null and b/tests/test-data/test-image.jpg differ diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000000..dacaefea279 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,22 @@ +{ + "include": ["resources/js/**/*"], + "exclude": ["resources/js/wysiwyg/lexical/yjs/*"], + "compilerOptions": { + "target": "es2022", + "module": "commonjs", + "rootDir": "./resources/js/", + "baseUrl": "./", + "paths": { + "@icons/*": ["resources/icons/*"], + "lexical": ["resources/js/wysiwyg/lexical/core/index.ts"], + "lexical/*": ["resources/js/wysiwyg/lexical/core/*"], + "@lexical/*": ["resources/js/wysiwyg/lexical/*"] + }, + "resolveJsonModule": true, + "allowJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true + } +} diff --git a/version b/version index 05cbc2e92a7..51b8fdb6012 100644 --- a/version +++ b/version @@ -1 +1 @@ -v23.12.3 +v25.02-dev