Upgrade to 3.9.6

This commit is contained in:
Bastian Allgeier
2023-07-27 12:08:43 +02:00
parent f76fbaa53e
commit 7928c28702
58 changed files with 930 additions and 148 deletions

View File

@@ -90,8 +90,6 @@ The Panel doesn't have extensive test coverage yet. That's an area we are still
We use [vitest](https://vitest.dev) for unit tests for JavaScript and Vue components - `.test.js` files next to the actual JavaScript/Vue file.
For integration tests, we use [cypress](https://www.cypress.io) - `.e2e.js` files.
## And last…
Let us know [in the forum](https://forum.getkirby.com) if you have questions.

View File

@@ -3,7 +3,7 @@
"description": "The Kirby 3 core",
"license": "proprietary",
"type": "kirby-cms",
"version": "3.9.5",
"version": "3.9.6",
"keywords": [
"kirby",
"cms",

2
kirby/composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "e8ed43b6d96b2e40b435ed3672dd1369",
"content-hash": "71f213866ce84f54adbb09cab0316cd5",
"packages": [
{
"name": "claviska/simpleimage",

View File

@@ -199,7 +199,7 @@ return [
'slug' => Field::slug([
'required' => true,
'preselect' => $select === 'slug',
'path' => $page->parent() ? '/' . $page->parent()->id() . '/' : '/',
'path' => $page->parent() ? '/' . $page->parent()->uri() . '/' : '/',
'disabled' => $permissions->can('changeSlug') === false,
'wizard' => [
'text' => I18n::translate('page.changeSlug.fromTitle'),

View File

@@ -135,18 +135,14 @@ return [
/**
* Add your own search engine
*
* @param \Kirby\Cms\App $kirby Kirby instance
* @param \Kirby\Cms\Collection $collection Collection of searchable models
* @param string $query
* @param mixed $params
* @return \Kirby\Cms\Collection|bool
*/
'search' => function (App $kirby, Collection $collection, string $query = null, $params = []) {
// empty search query
if (empty(trim($query ?? '')) === true) {
return $collection->limit(0);
}
'search' => function (
App $kirby,
Collection $collection,
string $query = '',
$params = []
): Collection|bool {
if (is_string($params) === true) {
$params = ['fields' => Str::split($params, '|')];
}
@@ -158,30 +154,42 @@ return [
'words' => false,
];
$options = array_merge($defaults, $params);
$collection = clone $collection;
$options = array_merge($defaults, $params);
$query = trim($query);
// empty or too short search query
if (Str::length($query) < $options['minlength']) {
return $collection->limit(0);
}
$words = preg_replace('/(\s)/u', ',', $query);
$words = Str::split($words, ',', $options['minlength']);
$exact = $options['words'] ? '(\b' . preg_quote($query) . '\b)' : preg_quote($query);
$query = Str::lower($query);
if (empty($options['stopwords']) === false) {
$words = array_diff($words, $options['stopwords']);
}
$words = A::map(
$words,
fn ($value) => $options['words'] ? '\b' . preg_quote($value) . '\b' : preg_quote($value)
);
// returns an empty collection if there is no search word
if (empty($words) === true) {
return $collection->limit(0);
}
$words = A::map(
$words,
fn ($value) => Str::wrap(preg_quote($value), $options['words'] ? '\b' : '')
);
$exact = preg_quote($query);
if ($options['words']) {
$exact = '(\b' . $exact . '\b)';
}
$query = Str::lower($query);
$preg = '!(' . implode('|', $words) . ')!i';
$scores = [];
$results = $collection->filter(function ($item) use ($query, $exact, $preg, $options, &$scores) {
$data = $item->content()->toArray();
$keys = array_keys($data);
@@ -193,10 +201,10 @@ return [
$keys[] = 'role';
} elseif ($item instanceof Page) {
// apply the default score for pages
$options['score'] = array_merge([
'id' => 64,
'title' => 64,
], $options['score']);
$options['score'] = array_merge(
['id' => 64, 'title' => 64],
$options['score']
);
}
if (empty($options['fields']) === false) {
@@ -242,6 +250,7 @@ return [
}
$scores[$item->id()] = $scoring;
return $scoring['hits'] > 0;
});

View File

@@ -8,7 +8,7 @@ return [
* Default number that will be saved when a new page/user/file is created
*/
'default' => function ($default = null) {
return $this->toNumber($default);
return $this->toNumber($default) ?? '';
},
/**
* The lowest allowed number
@@ -26,10 +26,10 @@ return [
* Allowed incremental steps between numbers (i.e `0.5`)
*/
'step' => function ($step = null) {
return $this->toNumber($step);
return $this->toNumber($step) ?? '';
},
'value' => function ($value = null) {
return $this->toNumber($value);
return $this->toNumber($value) ?? '';
}
],
'methods' => [

View File

@@ -53,9 +53,9 @@ if (Helpers::hasOverride('collection') === false) { // @codeCoverageIgnore
* @return \Kirby\Toolkit\Collection|null
* @todo 5.0 Add return type declaration
*/
function collection(string $name)
function collection(string $name, array $options = [])
{
return App::instance()->collection($name);
return App::instance()->collection($name, $options);
}
}

View File

@@ -34,9 +34,9 @@ return [
],
'computed' => [
'reports' => function () {
$reports = [];
$model = $this->model();
$value = fn ($value) => $value === null ? null : $model->toString($value);
$reports = [];
$model = $this->model();
$toString = fn ($value) => $value === null ? null : $model->toString($value);
foreach ($this->reports as $report) {
if (is_string($report) === true) {
@@ -47,14 +47,17 @@ return [
continue;
}
$info = $report['info'] ?? null;
$info = $report['info'] ?? null;
$label = $report['label'] ?? null;
$link = $report['link'] ?? null;
$value = $report['value'] ?? null;
$reports[] = [
'label' => I18n::translate($report['label'], $report['label']),
'value' => $value($report['value'] ?? null),
'info' => $value(I18n::translate($info, $info)),
'link' => $value($report['link'] ?? null),
'theme' => $value($report['theme'] ?? null)
'info' => $toString(I18n::translate($info, $info)),
'label' => $toString(I18n::translate($label, $label)),
'link' => $toString(I18n::translate($link, $link)),
'theme' => $toString($report['theme'] ?? null),
'value' => $toString(I18n::translate($value, $value))
];
}

View File

@@ -183,6 +183,7 @@
"error.user.email.invalid": "Моля въведете валиден email адрес",
"error.user.language.invalid": "Моля въведете валиден език",
"error.user.notFound": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u044f\u0442 \u043d\u0435 \u043c\u043e\u0436\u0435 \u0434\u0430 \u0431\u044a\u0434\u0435 \u043d\u0430\u043c\u0435\u0440\u0435\u043d.",
"error.user.password.excessive": "Please enter a valid password. Passwords must not be longer than 1000 characters.",
"error.user.password.invalid": "Моля въведете валидна парола. Тя трабва да съдържа поне 8 символа.",
"error.user.password.notSame": "\u041c\u043e\u043b\u044f, \u043f\u043e\u0442\u0432\u044a\u0440\u0434\u0435\u0442\u0435 \u043f\u0430\u0440\u043e\u043b\u0430\u0442\u0430",
"error.user.password.undefined": "Потребителят няма парола",

View File

@@ -183,6 +183,7 @@
"error.user.email.invalid": "Si us plau, introdueix una adreça de correu electrònic vàlida",
"error.user.language.invalid": "Introduïu un idioma vàlid",
"error.user.notFound": "L'usuari \"{name}\" no s'ha trobat",
"error.user.password.excessive": "Please enter a valid password. Passwords must not be longer than 1000 characters.",
"error.user.password.invalid": "Introduïu una contrasenya vàlida. Les contrasenyes han de tenir com a mínim 8 caràcters.",
"error.user.password.notSame": "Les contrasenyes no coincideixen",
"error.user.password.undefined": "L'usuari no té una contrasenya",

View File

@@ -183,6 +183,7 @@
"error.user.email.invalid": "Zadejte prosím platnou emailovou adresu",
"error.user.language.invalid": "Zadejte prosím platný jazyk",
"error.user.notFound": "U\u017eivatele se nepoda\u0159ilo nal\u00e9zt",
"error.user.password.excessive": "Please enter a valid password. Passwords must not be longer than 1000 characters.",
"error.user.password.invalid": "Zadejte prosím platné heslo. Heslo musí být dlouhé alespoň 8 znaků.",
"error.user.password.notSame": "Pros\u00edm potvr\u010fte heslo",
"error.user.password.undefined": "Uživatel nemá nastavené heslo.",

View File

@@ -183,6 +183,7 @@
"error.user.email.invalid": "Indtast venligst en gyldig email adresse",
"error.user.language.invalid": "Indtast venligst et gyldigt sprog",
"error.user.notFound": "Brugeren kunne ikke findes",
"error.user.password.excessive": "Please enter a valid password. Passwords must not be longer than 1000 characters.",
"error.user.password.invalid": "Indtast venligst en gyldig adgangskode. Adgangskoder skal minimum være 8 tegn lange.",
"error.user.password.notSame": "Bekr\u00e6ft venligst adgangskoden",
"error.user.password.undefined": "Brugeren har ikke en adgangskode",

View File

@@ -183,6 +183,7 @@
"error.user.email.invalid": "Bitte gib eine gültige E-Mailadresse an",
"error.user.language.invalid": "Bitte gib eine gültige Sprache an",
"error.user.notFound": "Der Account \"{name}\" wurde nicht gefunden",
"error.user.password.excessive": "Please enter a valid password. Passwords must not be longer than 1000 characters.",
"error.user.password.invalid": "Bitte gib ein gültiges Passwort ein. Passwörter müssen mindestens 8 Zeichen lang sein.",
"error.user.password.notSame": "Die Passwörter stimmen nicht überein",
"error.user.password.undefined": "Der Account hat kein Passwort",

View File

@@ -183,6 +183,7 @@
"error.user.email.invalid": "Παρακαλώ εισάγετε μια έγκυρη διεύθυνση ηλεκτρονικού ταχυδρομείου",
"error.user.language.invalid": "Παρακαλώ εισαγάγετε μια έγκυρη γλώσσα",
"error.user.notFound": "Δεν είναι δυνατή η εύρεση του χρήστη \"{name}\"",
"error.user.password.excessive": "Please enter a valid password. Passwords must not be longer than 1000 characters.",
"error.user.password.invalid": "Παρακαλώ εισάγετε έναν έγκυρο κωδικό πρόσβασης. Οι κωδικοί πρόσβασης πρέπει να έχουν μήκος τουλάχιστον 8 χαρακτήρων.",
"error.user.password.notSame": "\u03a0\u03b1\u03c1\u03b1\u03ba\u03b1\u03bb\u03bf\u03cd\u03bc\u03b5 \u03b5\u03c0\u03b9\u03b2\u03b5\u03b2\u03b1\u03b9\u03ce\u03c3\u03c4\u03b5 \u03c4\u03bf\u03bd \u039a\u03c9\u03b4\u03b9\u03ba\u03cc \u03a0\u03c1\u03cc\u03c3\u03b2\u03b1\u03c3\u03b7\u03c2",
"error.user.password.undefined": "Ο χρήστης δεν έχει κωδικό πρόσβασης",

View File

@@ -183,6 +183,7 @@
"error.user.email.invalid": "Please enter a valid email address",
"error.user.language.invalid": "Please enter a valid language",
"error.user.notFound": "The user \"{name}\" cannot be found",
"error.user.password.excessive": "Please enter a valid password. Passwords must not be longer than 1000 characters.",
"error.user.password.invalid": "Please enter a valid password. Passwords must be at least 8 characters long.",
"error.user.password.notSame": "The passwords do not match",
"error.user.password.undefined": "The user does not have a password",

View File

@@ -183,6 +183,7 @@
"error.user.email.invalid": "Bonvolu entajpi validan retpoŝtadreson",
"error.user.language.invalid": "Bonvolu entajpi validan lingvon",
"error.user.notFound": "La uzanto \"{name}\" ne troveblas",
"error.user.password.excessive": "Please enter a valid password. Passwords must not be longer than 1000 characters.",
"error.user.password.invalid": "Bonvolu entajpi validan pasvorton. Pasvortoj devas esti almenaŭ 8 literojn longaj.",
"error.user.password.notSame": "La pasvortoj ne estas kongruantaj",
"error.user.password.undefined": "La uzanto ne havas pasvorton",

View File

@@ -183,6 +183,7 @@
"error.user.email.invalid": "Por favor ingresa un correo electrónico valido",
"error.user.language.invalid": "Por favor ingresa un idioma valido",
"error.user.notFound": "El usuario no pudo ser encontrado",
"error.user.password.excessive": "Please enter a valid password. Passwords must not be longer than 1000 characters.",
"error.user.password.invalid": "Por favor ingresa una contraseña valida. Las contraseñas deben tener al menos 8 caracteres de largo.",
"error.user.password.notSame": "Por favor confirma la contrase\u00f1a",
"error.user.password.undefined": "El usuario no tiene contraseña",

View File

@@ -183,6 +183,7 @@
"error.user.email.invalid": "Por favor, introduce una dirección de correo electrónico válida",
"error.user.language.invalid": "Por favor, introduce un idioma válido",
"error.user.notFound": "No se puede encontrar el usuario \"{name}\"",
"error.user.password.excessive": "Please enter a valid password. Passwords must not be longer than 1000 characters.",
"error.user.password.invalid": "Por favor, introduce una contraseña válida. Las contraseñas deben tener al menos 8 caracteres de largo.",
"error.user.password.notSame": "Las contraseñas no coinciden",
"error.user.password.undefined": "El usuario no tiene contraseña",

View File

@@ -183,6 +183,7 @@
"error.user.email.invalid": "لطفا یک ایمیل معتبر وارد کنید",
"error.user.language.invalid": "لطفا زبان معتبری انتخاب کنید",
"error.user.notFound": "کاربر «{name}» پیدا نشد",
"error.user.password.excessive": "Please enter a valid password. Passwords must not be longer than 1000 characters.",
"error.user.password.invalid": "لطفا گذرواژه صحیحی با حداقل طول 8 حرف وارد کنید. ",
"error.user.password.notSame": "\u0644\u0637\u0641\u0627 \u062a\u06a9\u0631\u0627\u0631 \u06af\u0630\u0631\u0648\u0627\u0698\u0647 \u0631\u0627 \u0648\u0627\u0631\u062f \u0646\u0645\u0627\u06cc\u06cc\u062f",
"error.user.password.undefined": "کاربر فاقد گذرواژه است",

View File

@@ -183,6 +183,7 @@
"error.user.email.invalid": "Anna kelpaava sähköpostiosoite",
"error.user.language.invalid": "Anna kelpaava kieli",
"error.user.notFound": "K\u00e4ytt\u00e4j\u00e4\u00e4 ei l\u00f6ytynyt",
"error.user.password.excessive": "Please enter a valid password. Passwords must not be longer than 1000 characters.",
"error.user.password.invalid": "Anna kelpaava salasana. Salasanan täytyy olla ainakin 8 merkkiä pitkä.",
"error.user.password.notSame": "Salasanat eivät täsmää",
"error.user.password.undefined": "Käyttäjällä ei ole salasanaa",

View File

@@ -183,6 +183,7 @@
"error.user.email.invalid": "Veuillez saisir un courriel valide",
"error.user.language.invalid": "Veuillez saisir une langue valide",
"error.user.notFound": "Lutilisateur « {name} » na pu être trouvé",
"error.user.password.excessive": "Please enter a valid password. Passwords must not be longer than 1000 characters.",
"error.user.password.invalid": "Veuillez saisir un mot de passe valide. Les mots de passe doivent comporter au moins 8 caractères.",
"error.user.password.notSame": "Les mots de passe ne sont pas identiques",
"error.user.password.undefined": "Cet utilisateur na pas de mot de passe",

View File

@@ -183,6 +183,7 @@
"error.user.email.invalid": "Kérlek adj meg egy valós email-címet",
"error.user.language.invalid": "Kérlek add meg a megfelelő nyelvi beállítást",
"error.user.notFound": "A felhaszn\u00e1l\u00f3 nem tal\u00e1lhat\u00f3",
"error.user.password.excessive": "Please enter a valid password. Passwords must not be longer than 1000 characters.",
"error.user.password.invalid": "Kérlek adj meg egy megfelelő jelszót. A jelszónak legalább 8 karakter hosszúságúnak kell lennie.",
"error.user.password.notSame": "K\u00e9rlek er\u0151s\u00edtsd meg a jelsz\u00f3t",
"error.user.password.undefined": "A felhasználónak nincs jelszó megadva",

View File

@@ -183,6 +183,7 @@
"error.user.email.invalid": "Masukkan surel yang valid",
"error.user.language.invalid": "Masukkan bahasa yang valid",
"error.user.notFound": "Pengguna \"{name}\" tidak dapat ditemukan",
"error.user.password.excessive": "Please enter a valid password. Passwords must not be longer than 1000 characters.",
"error.user.password.invalid": "Masukkan sandi yang valid. Sandi setidaknya mengandung 8 karakter.",
"error.user.password.notSame": "Sandi tidak cocok",
"error.user.password.undefined": "Pengguna tidak memiliki sandi",

View File

@@ -10,9 +10,9 @@
"cancel": "Hætta við",
"change": "Breyta",
"close": "Loka",
"confirm": "Ok",
"collapse": "Collapse",
"collapse.all": "Collapse All",
"confirm": "OK",
"collapse": "Fella",
"collapse.all": "Fella allt",
"copy": "Afrita",
"copy.all": "Afrita allt",
"create": "Stofna",
@@ -183,6 +183,7 @@
"error.user.email.invalid": "Vinsamlegast ákjósanlegt netfang",
"error.user.language.invalid": "Vinsamlegast ákjósanlegt tungumál",
"error.user.notFound": "Þessi notandi; \"{name}\" fannst ekki",
"error.user.password.excessive": "Please enter a valid password. Passwords must not be longer than 1000 characters.",
"error.user.password.invalid": "Veldu ákjósanlegt lykilorð. Minnst 8 stafa langt.",
"error.user.password.notSame": "Lykilorðin stemma ekki",
"error.user.password.undefined": "Þessi notandi hefur ekki lykilorð",
@@ -495,7 +496,7 @@
"show": "Sýna",
"site.blueprint": "Þessi vefur hefur ekki skipan (e. blueprint) ennþá. Þú mátt skilgreina skipanina í <strong>/site/blueprints/site.yml</strong>",
"size": "Stærð",
"slug": "Slögg",
"slug": "Slögg forskeyti",
"sort": "Raða",
"stats.empty": "Engar skýrslur",

View File

@@ -183,6 +183,7 @@
"error.user.email.invalid": "Inserisci un indirizzo email valido",
"error.user.language.invalid": "Inserisci una lingua valida",
"error.user.notFound": "L'utente non \u00e8 stato trovato",
"error.user.password.excessive": "Please enter a valid password. Passwords must not be longer than 1000 characters.",
"error.user.password.invalid": "Per favore inserisci una password valida. Le password devono essere lunghe almeno 8 caratteri",
"error.user.password.notSame": "Le password non corrispondono",
"error.user.password.undefined": "L'utente non ha una password",

View File

@@ -183,6 +183,7 @@
"error.user.email.invalid": "올바른 이메일 주소를 입력하세요.",
"error.user.language.invalid": "올바른 언어를 입력하세요.",
"error.user.notFound": "사용자({name})가 없습니다.",
"error.user.password.excessive": "Please enter a valid password. Passwords must not be longer than 1000 characters.",
"error.user.password.invalid": "암호를 8자 이상으로 설정하세요.",
"error.user.password.notSame": "\uc554\ud638\ub97c \ud655\uc778\ud558\uc138\uc694.",
"error.user.password.undefined": "암호가 설정되지 않았습니다.",
@@ -292,7 +293,7 @@
"field.pages.empty": "선택한 페이지가 없습니다.",
"field.structure.delete.confirm": "이 항목을 삭제할까요?",
"field.structure.delete.confirm.all": "Do you really want to delete all entries?",
"field.structure.delete.confirm.all": "모든 항목을 삭제할까요?",
"field.structure.empty": "항목이 없습니다.",
"field.users.empty": "선택한 사용자가 없습니다.",

View File

@@ -183,6 +183,7 @@
"error.user.email.invalid": "Įrašykite teisingą el. pašto adresą",
"error.user.language.invalid": "Įrašykite teisingą kalbą",
"error.user.notFound": "Vartotojas \"{name}\" nerastas",
"error.user.password.excessive": "Please enter a valid password. Passwords must not be longer than 1000 characters.",
"error.user.password.invalid": "Prašome įrašyti galiojantį slaptažodį. Slaptažodį turi sudaryti bent 8 simboliai.",
"error.user.password.notSame": "Slaptažodžiai nesutampa",
"error.user.password.undefined": "Vartotojas neturi slaptažodžio",

View File

@@ -183,6 +183,7 @@
"error.user.email.invalid": "Vennligst skriv inn en gyldig e-postadresse",
"error.user.language.invalid": "Vennligst skriv inn et gyldig språk",
"error.user.notFound": "Brukeren kunne ikke bli funnet",
"error.user.password.excessive": "Please enter a valid password. Passwords must not be longer than 1000 characters.",
"error.user.password.invalid": "Vennligst skriv inn et gyldig passord. Passordet må minst være 8 tegn langt.",
"error.user.password.notSame": "Vennligst bekreft passordet",
"error.user.password.undefined": "Brukeren har ikke et passord",

View File

@@ -183,6 +183,7 @@
"error.user.email.invalid": "Gelieve een geldig emailadres in te voeren",
"error.user.language.invalid": "Gelieve een geldige taal in te voeren",
"error.user.notFound": "De gebruiker \"{name}\" kan niet worden gevonden",
"error.user.password.excessive": "Please enter a valid password. Passwords must not be longer than 1000 characters.",
"error.user.password.invalid": "Gelieve een geldig wachtwoord in te voeren. Wachtwoorden moeten minstens 8 karakters lang zijn.",
"error.user.password.notSame": "De wachtwoorden komen niet overeen",
"error.user.password.undefined": "De gebruiker heeft geen wachtwoord",

View File

@@ -70,14 +70,14 @@
"error.blocks.max.singular": "Możesz dodać tylko jeden blok",
"error.blocks.min.plural": "Musisz dodać co najmniej {min} bloki/-ów",
"error.blocks.min.singular": "Musisz dodać co najmniej jeden blok",
"error.blocks.validation": "There's an error on the \"{field}\" field in block {index} using the \"{fieldset}\" block type",
"error.blocks.validation": "Wystąpił błąd w polu „{field}” w bloku {index} o typie bloku „{fieldset}”",
"error.cache.type.invalid": "Invalid cache type \"{type}\"",
"error.cache.type.invalid": "Nieprawidłowy typ pamięci podręcznej „{type}",
"error.email.preset.notFound": "Nie udało się załadować wzorca wiadomości e-mail \"{name}\"",
"error.field.converter.invalid": "Nieprawidłowy konwerter \"{converter}\"",
"error.field.type.missing": "Field \"{ name }\": The field type \"{ type }\" does not exist",
"error.field.type.missing": "Pole „{ name }: Typ pola „{ type }” nie istnieje",
"error.file.changeName.empty": "Imię nie może być puste",
"error.file.changeName.permission": "Nie masz uprawnień, by zmienić nazwę \"{filename}\"",
@@ -110,14 +110,14 @@
"error.language.name": "Wprowadź poprawną nazwę języka.",
"error.language.notFound": "Język nie został odnaleziony",
"error.layout.validation.block": "There's an error on the \"{field}\" field in block {blockIndex} using the \"{fieldset}\" block type in layout {layoutIndex}",
"error.layout.validation.block": "Wystąpił błąd w polu „{field}” w bloku {blockIndex} o typie bloku „{fieldset}” w układzie {layoutIndex}",
"error.layout.validation.settings": "W ustawieniach układu {index} jest błąd",
"error.license.format": "Wprowadź poprawny klucz licencyjny",
"error.license.email": "Wprowadź poprawny adres email",
"error.license.verification": "Nie udało się zweryfikować licencji",
"error.object.validation": "Theres an error in the \"{label}\" field:\n{message}",
"error.object.validation": "Wystąpił błąd w polu „{label}”:\n{message}",
"error.offline": "Panel jest obecnie offline",
@@ -183,6 +183,7 @@
"error.user.email.invalid": "Wprowadź poprawny adres email",
"error.user.language.invalid": "Proszę podać poprawny język",
"error.user.notFound": "Nie można znaleźć użytkownika \"{name}\"",
"error.user.password.excessive": "Please enter a valid password. Passwords must not be longer than 1000 characters.",
"error.user.password.invalid": "Wprowadź prawidłowe hasło. Hasła muszą mieć co najmniej 8 znaków.",
"error.user.password.notSame": "Hasła nie są takie same",
"error.user.password.undefined": "Użytkownik nie ma hasła",
@@ -262,7 +263,7 @@
"field.blocks.image.placeholder": "Wybierz obrazek",
"field.blocks.image.ratio": "Proporcje",
"field.blocks.image.url": "URL obrazka",
"field.blocks.line.name": "Linijka",
"field.blocks.line.name": "Linia",
"field.blocks.list.name": "Lista",
"field.blocks.markdown.name": "Markdown",
"field.blocks.markdown.label": "Tekst",
@@ -287,12 +288,12 @@
"field.layout.empty": "Nie ma jeszcze żadnych rzędów",
"field.layout.select": "Wybierz układ",
"field.object.empty": "No information yet",
"field.object.empty": "Brak informacji",
"field.pages.empty": "Nie wybrano jeszcze żadnych stron",
"field.structure.delete.confirm": "Czy na pewno chcesz usunąć ten wiersz?",
"field.structure.delete.confirm.all": "Do you really want to delete all entries?",
"field.structure.delete.confirm.all": "Czy na pewno chcesz usunąć wszystkie wpisy?",
"field.structure.empty": "Nie ma jeszcze \u017cadnych wpis\u00f3w.",
"field.users.empty": "Nie wybrano jeszcze żadnych użytkowników",
@@ -353,8 +354,8 @@
"license.manage": "Zarządzaj swoimi licencjami",
"license.register.help": "Po zakupieniu licencji otrzymałaś/-eś mailem klucz. Skopiuj go i wklej tutaj, aby dokonać rejestracji.",
"license.register.label": "Wprowadź swój kod licencji",
"license.register.domain": "Your license will be registered to <strong>{host}</strong>.",
"license.register.local": "You are about to register your license for your local domain <strong>{host}</strong>. If this site will be deployed to a public domain, please register it there instead. If {host} is the domain you want to license Kirby to, please continue.",
"license.register.domain": "Twoja licencja zostanie zarejestrowana na <strong>{host}</strong>.",
"license.register.local": "Zamierzasz zarejestrować licencję dla swojej domeny lokalnej <strong>{host}</strong>. Jeśli ta witryna zostanie wdrożona w domenie ogólnodostępnej, zarejestruj ją tam. Jeżeli {host} jest faktycznie domeną, do której chcesz przypisać licencję, kontynuuj.",
"license.register.success": "Dziękujemy za wspieranie Kirby",
"license.unregistered": "To jest niezarejestrowana wersja demonstracyjna Kirby",
"license.unregistered.label": "Niezarejestrowane",
@@ -460,7 +461,7 @@
"paste": "Wklej",
"paste.after": "Wklej po",
"pixel": "Piksel",
"plugin": "Plugin",
"plugin": "Wtyczka",
"plugins": "Wtyczki",
"prev": "Poprzednie",
"preview": "Podgląd",
@@ -500,24 +501,24 @@
"stats.empty": "Brak raportów",
"system.issues.content": "Zdaje się, że folder „content” jest wystawiony na publiczny dostęp",
"system.issues.eol.kirby": "Your installed Kirby version has reached end-of-life and will not receive further security updates",
"system.issues.eol.plugin": "Your installed version of the { plugin } plugin is has reached end-of-life and will not receive further security updates",
"system.issues.eol.kirby": "Twoja zainstalowana wersja Kirby osiągnęła koniec okresu wsparcia i nie będzie otrzymywać dalszych aktualizacji zabezpieczeń",
"system.issues.eol.plugin": "Twoja zainstalowana wersja wtyczki { plugin } osiągnęła koniec okresu wsparcia i nie będzie otrzymywać dalszych aktualizacji zabezpieczeń",
"system.issues.debug": "Debugowanie musi być wyłączone w środowisku produkcyjnym",
"system.issues.git": "Zdaje się, że folder „.git” jest wystawiony na publiczny dostęp",
"system.issues.https": "Zalecamy HTTPS dla wszystkich Twoich witryn",
"system.issues.kirby": "Zdaje się, że folder „kirby” jest wystawiony na publiczny dostęp",
"system.issues.site": "Zdaje się, że folder „site” jest wystawiony na publiczny dostęp",
"system.issues.vulnerability.kirby": "Your installation might be affected by the following vulnerability ({ severity } severity): { description }",
"system.issues.vulnerability.plugin": "Your installation might be affected by the following vulnerability in the { plugin } plugin ({ severity } severity): { description }",
"system.updateStatus": "Update status",
"system.updateStatus.error": "Could not check for updates",
"system.updateStatus.not-vulnerable": "No known vulnerabilities",
"system.updateStatus.security-update": "Free security update { version } available",
"system.updateStatus.security-upgrade": "Upgrade { version } with security fixes available",
"system.updateStatus.unreleased": "Unreleased version",
"system.updateStatus.up-to-date": "Up to date",
"system.updateStatus.update": "Free update { version } available",
"system.updateStatus.upgrade": "Upgrade { version } available",
"system.issues.vulnerability.kirby": "Twojej instalacji może zagrażać następująca luka w zabezpieczeniach ({ severity } stopień): { description }",
"system.issues.vulnerability.plugin": "Twojej instalacji może zagrażać następująca luka w zabezpieczeniach we wtyczce { plugin } ({ severity } poziom): { description }",
"system.updateStatus": "Stan aktualizacji",
"system.updateStatus.error": "Nie udało się sprawdzić dostępności aktualizacji",
"system.updateStatus.not-vulnerable": "Brak znanych luk bezpieczeństwa",
"system.updateStatus.security-update": "Dostępna darmowa aktualizacja { version } z poprawkami bezpieczeństwa",
"system.updateStatus.security-upgrade": "Dostępna aktualizacja { version } z poprawkami bezpieczeństwa",
"system.updateStatus.unreleased": "Niepublikowana wersja",
"system.updateStatus.up-to-date": "Aktualna",
"system.updateStatus.update": "Dostępna darmowa aktualizacja { version }",
"system.updateStatus.upgrade": "Dostępna aktualizacja { version }",
"title": "Tytuł",
"template": "Szablon",
@@ -584,9 +585,9 @@
"users": "Użytkownicy",
"version": "Wersja",
"version.current": "Current version",
"version.latest": "Latest version",
"versionInformation": "Version information",
"version.current": "Obecna wersja",
"version.latest": "Ostatnia wersja",
"versionInformation": "Informacje o wersji",
"view.account": "Twoje konto",
"view.installation": "Instalacja",

View File

@@ -183,6 +183,7 @@
"error.user.email.invalid": "Digite um endereço de email válido",
"error.user.language.invalid": "Digite um idioma válido",
"error.user.notFound": "Usuário \"{name}\" não encontrado",
"error.user.password.excessive": "Please enter a valid password. Passwords must not be longer than 1000 characters.",
"error.user.password.invalid": "Digite uma senha válida. Sua senha deve ter pelo menos 8 caracteres.",
"error.user.password.notSame": "As senhas não combinam",
"error.user.password.undefined": "O usuário não possui uma senha",

View File

@@ -183,6 +183,7 @@
"error.user.email.invalid": "Digite um endereço de email válido",
"error.user.language.invalid": "Digite um idioma válido",
"error.user.notFound": "Utilizador \"{name}\" não encontrado",
"error.user.password.excessive": "Please enter a valid password. Passwords must not be longer than 1000 characters.",
"error.user.password.invalid": "Digite uma palavra-passe válida. A sua palavra-passe deve ter pelo menos 8 caracteres.",
"error.user.password.notSame": "As palavras-passe não combinam",
"error.user.password.undefined": "O utilizador não possui uma palavra-passe",

View File

@@ -0,0 +1,603 @@
{
"account.changeName": "Schimbă-ți numele",
"account.delete": "Șterge-ți contul",
"account.delete.confirm": "Chiar vrei să îți ștergi contul? Vei fi deconectat imediat. Contul nu poate fi recuperat.",
"add": "Adaug\u0103",
"author": "Autor",
"avatar": "Imagine de profil",
"back": "Înapoi",
"cancel": "Anulează",
"change": "Modific\u0103",
"close": "\u00cenchide",
"confirm": "Ok",
"collapse": "Pliază",
"collapse.all": "Pliază toate",
"copy": "Copiază",
"copy.all": "Copiază toate",
"create": "Creează",
"date": "Data",
"date.select": "Alege o dată",
"day": "Ziua",
"days.fri": "Vin",
"days.mon": "Lun",
"days.sat": "S\u00e2m",
"days.sun": "Dum",
"days.thu": "Joi",
"days.tue": "Mar",
"days.wed": "Mie",
"debugging": "Depanare",
"delete": "\u0218terge",
"delete.all": "Șterge toate",
"dialog.files.empty": "Nu există fișiere de selectat",
"dialog.pages.empty": "Nu există pagini de selectat",
"dialog.users.empty": "Nu există utilizatori de selectat",
"dimensions": "Dimensiuni",
"disabled": "Dezactivat",
"discard": "Renun\u021b\u0103",
"download": "Descarcă",
"duplicate": "Duplică",
"edit": "Editeaz\u0103",
"email": "E-mail",
"email.placeholder": "email@exemplu.com",
"entries": "Întregistrări",
"entry": "Înregistrare",
"environment": "Mediu",
"error.access.code": "Cod invalid",
"error.access.login": "Conectare invalidă",
"error.access.panel": "Nu ai voie să accesezi panoul",
"error.access.view": "Nu ai voie să accesezi această parte a panoului",
"error.avatar.create.fail": "Imaginea de profil nu a putut fi încărcată",
"error.avatar.delete.fail": "Imaginea de profil nu a putut fi ștearsă",
"error.avatar.dimensions.invalid": "Păstrează te rog lățimea și înălțimea imaginii de profil sub 3000 de pixeli",
"error.avatar.mime.forbidden": "Imaginea de profil trebuie să fie un fișier JPEG sau PNG",
"error.blueprint.notFound": "Blueprint-ul \"{name}\" nu a putut fi încărcat",
"error.blocks.max.plural": "Nu poți adăuga mai mult de {max} blocuri",
"error.blocks.max.singular": "Nu poți adăuga mai mult de un bloc",
"error.blocks.min.plural": "Trebuie să adaugi cel puțin {min} blocuri",
"error.blocks.min.singular": "Trebuie să adaugi cel puțin un bloc",
"error.blocks.validation": "Există o eroare la câmpul \"{field}\" în blocul {index} care folosește tipul de bloc \"{fieldset}\"",
"error.cache.type.invalid": "Tipul de cache \"{type}\" este invalid",
"error.email.preset.notFound": "Preset-ul de e-mail \"{name}\" nu a fost găsit",
"error.field.converter.invalid": "Convertorul \"{converter}\" invalid",
"error.field.type.missing": "Câmpul \"{ name }\": Tipul de câmp \"{ type }\" nu există",
"error.file.changeName.empty": "Numele nu trebuie să fie gol",
"error.file.changeName.permission": "Nu ai voie să schimbi numele fișierului \"{filename}\"",
"error.file.duplicate": "Există deja un fișier cu numele \"{filename}\"",
"error.file.extension.forbidden": "Extensia de fișier \"{extension}\" nu este permisă",
"error.file.extension.invalid": "Extensie de fișier invalidă: {extension}",
"error.file.extension.missing": "Extensia de fișier pentru \"{filename}\" lipsește",
"error.file.maxheight": "Înălțimea imaginii nu poate depăși {height} pixeli",
"error.file.maxsize": "Fișierul este prea mare",
"error.file.maxwidth": "Lățimea imaginii nu poate depăși {width} pixeli",
"error.file.mime.differs": "Fișierul încărcat trebuie să aibă același tip mime \"{mime}\"",
"error.file.mime.forbidden": "Tipul media \"{mime}\" nu este permis",
"error.file.mime.invalid": "Tip mime invalid: {mime}",
"error.file.mime.missing": "Tipul media pentru \"{filename}\" nu poate fi detectat",
"error.file.minheight": "Imaginea trebuie să aibă înălțimea de minim {height} pixeli",
"error.file.minsize": "Fișierul este prea mic",
"error.file.minwidth": "Imaginea trebuie să aibă lățimea de minim {width} pixeli",
"error.file.name.missing": "Numele fișierului nu poate fi gol",
"error.file.notFound": "Fișierul \"{filename}\" nu a fost găsit",
"error.file.orientation": "Orientarea imaginii trebuie să fie \"{orientation}\"",
"error.file.type.forbidden": "Nu ai permisiunea să încarci fișiere {type}",
"error.file.type.invalid": "Tip invalid de fișier: {type}",
"error.file.undefined": "Fișierul nu a fost găsit",
"error.form.incomplete": "Te rog repară toate erorile din formular…",
"error.form.notSaved": "Formularul nu a putut fi salvat",
"error.language.code": "Te rog introdu un cod valid pentru limbă",
"error.language.duplicate": "Limba există deja",
"error.language.name": "Te rog introdu un nume valid pentru limbă",
"error.language.notFound": "Limba nu a fost găsită",
"error.layout.validation.block": "Există o eroare la câmpul \"{field}\" în blocul {blockIndex} care utilizează tipul de bloc \"{fieldset}\" în layout-ul {layoutIndex}",
"error.layout.validation.settings": "Există o eroare la setările layout-ului {index}",
"error.license.format": "Te rog introdu o cheie de licență validă",
"error.license.email": "Te rog introdu o adresă de e-mail validă",
"error.license.verification": "Licența nu a putut fi verificată",
"error.object.validation": "Există o eroare la câmpul \"{label}\":\n{message}",
"error.offline": "Panoul este momentan offline",
"error.page.changeSlug.permission": "Nu ai voie să schimbi apendicele URL pentru \"{slug}\"",
"error.page.changeStatus.incomplete": "Pagina are erori și nu poate fi publicată",
"error.page.changeStatus.permission": "Statutul acestei pagini nu poate fi schimbat",
"error.page.changeStatus.toDraft.invalid": "Pagina \"{slug}\" nu poate fi schimbată în ciornă",
"error.page.changeTemplate.invalid": "Șablonul paginii \"{slug}\" nu poate fi schimbat",
"error.page.changeTemplate.permission": "Nu ai voie să schimbi șablonul pentru \"{slug}\"",
"error.page.changeTitle.empty": "Titlul nu poate rămâne gol",
"error.page.changeTitle.permission": "Nu ai voie să schimbi titlul pentru \"{slug}\"",
"error.page.create.permission": "Nu ai voie să creezi \"{slug}\"",
"error.page.delete": "Pagina \"{slug}\" nu poate fi ștearsă",
"error.page.delete.confirm": "Te rog introdu titlul paginii pentru a confirma",
"error.page.delete.hasChildren": "Pagina are subpagini și nu poate fi ștearsă",
"error.page.delete.permission": "Nu ai voie să ștergi \"{slug}\"",
"error.page.draft.duplicate": "Există deja o ciornă cu apendicele URL \"{slug}\"",
"error.page.duplicate": "Există deja o pagină cu apendicele URL \"{slug}\"",
"error.page.duplicate.permission": "Nu ai voie să duplici \"{slug}\"",
"error.page.notFound": "Pagina \"{slug}\" nu a fost găsită",
"error.page.num.invalid": "Te rog introdu un număr de sortare valid. Numerele nu pot fi negative.",
"error.page.slug.invalid": "Te rog introdu un apendice URL valid",
"error.page.slug.maxlength": "Lungimea slug-ului nu poate depăși \"{length}\"",
"error.page.sort.permission": "Pagina \"{slug}\" nu poate fi sortată",
"error.page.status.invalid": "Te rog stabilește un statut valid pentru pagină",
"error.page.undefined": "Pagina nu a fost găsită",
"error.page.update.permission": "Nu ai voie să actualizezi \"{slug}\"",
"error.section.files.max.plural": "Nu poți avea mai mult de {max} fișiere în secțiunea \"{section}\"",
"error.section.files.max.singular": "Nu poți avea mai mult de un fișier în secțiunea \"{section}\"",
"error.section.files.min.plural": "Secțiunea \"{section}\" are nevoie de cel puțin {min} fișiere",
"error.section.files.min.singular": "Secțiunea \"{section}\" are nevoie de cel puțin un fișier",
"error.section.pages.max.plural": "Nu poți avea mai mult de {max} pagini în secțiunea \"{section}\"",
"error.section.pages.max.singular": "Nu poți avea mai mult de o pagină în secțiunea \"{section}\"",
"error.section.pages.min.plural": "Secțiunea \"{section}\" are nevoie de cel puțin {min} pagini",
"error.section.pages.min.singular": "Secțiunea \"{section}\" are nevoie de cel puțin o pagină",
"error.section.notLoaded": "Secțiunea \"{name}\" nu a putut fi încărcată",
"error.section.type.invalid": "Tipul de secțiune \"{type}\" nu este valid",
"error.site.changeTitle.empty": "Titlul nu poate să rămână gol",
"error.site.changeTitle.permission": "Nu ai voie să schimbi titlul site-ului",
"error.site.update.permission": "Nu ai voie să actualizezi site-ul",
"error.template.default.notFound": "Șablonul implicit nu există",
"error.unexpected": "S-a produs o eroare neașteptată! Activează modul depanare pentru mai multe informații: https://getkirby.com/docs/reference/system/options/debug",
"error.user.changeEmail.permission": "Nu ai voie să schimbi adresa de e-mail a utilizatorului \"{name}\"",
"error.user.changeLanguage.permission": "Nu ai voie să schimbi limba utilizatorului \"{name}\"",
"error.user.changeName.permission": "Nu ai voie să schimbi numele utilizatorului \"{name}\"",
"error.user.changePassword.permission": "Nu ai voie să schimbi parola utilizatorului \"{name}\"",
"error.user.changeRole.lastAdmin": "Rolul ultimului administrator nu poate fi schimbat",
"error.user.changeRole.permission": "Nu ai voie să schimbi rolul utilizatorului \"{name}\"",
"error.user.changeRole.toAdmin": "Nu ai voie să promovezi un utilizator la rolul de administrator",
"error.user.create.permission": "Nu ai voie să creezi acest utilizator",
"error.user.delete": "Utilizatorul \"{name}\" nu poate fi șters",
"error.user.delete.lastAdmin": "Ultimul administrator nu poate fi șters",
"error.user.delete.lastUser": "Ultimul utilizator nu poate fi șters",
"error.user.delete.permission": "Nu ai voie să ștergi utilizatorul \"{name}\"",
"error.user.duplicate": "Există deja un utilizator cu adresa e-mail \"{email}\"",
"error.user.email.invalid": "Te rog introdu o adresă de e-mail validă",
"error.user.language.invalid": "Te rog introdu o limbă validă",
"error.user.notFound": "Utilizatorul \"{name}\" nu a fost găsit",
"error.user.password.excessive": "Please enter a valid password. Passwords must not be longer than 1000 characters.",
"error.user.password.invalid": "Te rog introdu o parolă validă. Parola trebuie să aibă cel puțin 8 caractere.",
"error.user.password.notSame": "Parolele nu se potrivesc",
"error.user.password.undefined": "Utilizatorul nu are parolă",
"error.user.password.wrong": "Parolă greșită",
"error.user.role.invalid": "Te rog introdu un rol valid",
"error.user.undefined": "Utilizatorul nu a fost găsit",
"error.user.update.permission": "Nu ai voie să actualizezi utilizatorul \"{name}\"",
"error.validation.accepted": "Te rog confirmă",
"error.validation.alpha": "Te rog introdu doar caractere din intervalul a-z",
"error.validation.alphanum": "Te rog introdu doar caractere din intervalul a-z sau cifre 0-9",
"error.validation.between": "Te rog introdu o valoare între \"{min}\" și \"{max}\"",
"error.validation.boolean": "Te rog confirmă sau refuză",
"error.validation.contains": "Te rog introdu o valoare care conține \"{needle}\"",
"error.validation.date": "Te rog introdu o dată validă",
"error.validation.date.after": "Te rog introdu o dată după {date}",
"error.validation.date.before": "Te rog introdu o dată dinainte de {date}",
"error.validation.date.between": "Te rog introdu o dată între {min} și {max}",
"error.validation.denied": "Te rog refuză",
"error.validation.different": "Valoarea nu poate fi \"{other}\"",
"error.validation.email": "Te rog introdu o adresă de e-mail validă",
"error.validation.endswith": "Valoarea nu se poate termina cu \"{end}\"",
"error.validation.filename": "Te rog introdu un nume valid de fișier",
"error.validation.in": "Te rog introdu una dintre următoarele: ({in})",
"error.validation.integer": "Te rog introdu un număr întreg valid",
"error.validation.ip": "Te rog introdu o adresă IP validă",
"error.validation.less": "Te rog introdu o valoare mai mică decât {max}",
"error.validation.match": "Valoarea nu se potrivește cu forma așteptată",
"error.validation.max": "Te rog introdu o valoare mai mică sau egală cu {max}",
"error.validation.maxlength": "Te rog introdu o valoare mai scurtă. (max. {max} caractere)",
"error.validation.maxwords": "Te rog nu introduce mai mult de {max} cuvinte.",
"error.validation.min": "Te rog introdu o valoare mai mare sau egală cu {min}",
"error.validation.minlength": "Te rog introdu o valoare mai lungă. (min. {min} caractere)",
"error.validation.minwords": "Te rog introdu cel puțin {min} cuvinte",
"error.validation.more": "Te rog introdu o valoare mai mare decât {min}",
"error.validation.notcontains": "Te rog introdu o valoare care să nu conțină \"{needle}\"",
"error.validation.notin": "Te rog nu introduce niciuna dintre următoarele: ({notIn})",
"error.validation.option": "Te rog alege o opțiune validă",
"error.validation.num": "Te rog introdu un număr valid",
"error.validation.required": "Te rog introdu ceva",
"error.validation.same": "Te rog introdu \"{other}\"",
"error.validation.size": "Dimensiunea valorii trebuie să fie \"{size}\"",
"error.validation.startswith": "Valoarea trebuie să înceapă cu \"{start}\"",
"error.validation.time": "Te rog introdu un timp valid",
"error.validation.time.after": "Te rog introdu un timp după {time}",
"error.validation.time.before": "Te rog introdu un timp înainte de {time}",
"error.validation.time.between": "Te rog introdu un timp între {min} și {max}",
"error.validation.url": "Te rog introdu un URL valid",
"expand": "Extinde",
"expand.all": "Extinde toate",
"field.required": "Acest câmp este necesar",
"field.blocks.changeType": "Schimbă tipul",
"field.blocks.code.name": "Cod",
"field.blocks.code.language": "Limba",
"field.blocks.code.placeholder": "Codul tău …",
"field.blocks.delete.confirm": "Chiar vrei să ștergi acest bloc?",
"field.blocks.delete.confirm.all": "Chiar vrei să ștergi toate blocurile?",
"field.blocks.delete.confirm.selected": "Chiar vrei să ștergi blocurile selectate?",
"field.blocks.empty": "Niciun bloc deocamdată",
"field.blocks.fieldsets.label": "Te rog alege un tip de bloc …",
"field.blocks.fieldsets.paste": "Apasă <kbd>{{ shortcut }}</kbd> pentru a insera/aduce blocuri din clipboard-ul tău",
"field.blocks.gallery.name": "Galerie",
"field.blocks.gallery.images.empty": "Nicio imagine deocamdată",
"field.blocks.gallery.images.label": "Imagini",
"field.blocks.heading.level": "Nivel",
"field.blocks.heading.name": "Subtitlu",
"field.blocks.heading.text": "Text",
"field.blocks.heading.placeholder": "Subtitlu …",
"field.blocks.image.alt": "Text alternativ",
"field.blocks.image.caption": "Etichetă",
"field.blocks.image.crop": "Decupaj",
"field.blocks.image.link": "Legătură",
"field.blocks.image.location": "Localizare",
"field.blocks.image.name": "Imagine",
"field.blocks.image.placeholder": "Alege o imagine",
"field.blocks.image.ratio": "Raport",
"field.blocks.image.url": "URL-ul imaginii",
"field.blocks.line.name": "Linie",
"field.blocks.list.name": "Listă",
"field.blocks.markdown.name": "Markdown",
"field.blocks.markdown.label": "Text",
"field.blocks.markdown.placeholder": "Markdown …",
"field.blocks.quote.name": "Citat",
"field.blocks.quote.text.label": "Text",
"field.blocks.quote.text.placeholder": "Citat …",
"field.blocks.quote.citation.label": "Citare",
"field.blocks.quote.citation.placeholder": "de …",
"field.blocks.text.name": "Text",
"field.blocks.text.placeholder": "Text …",
"field.blocks.video.caption": "Etichetă",
"field.blocks.video.name": "Video",
"field.blocks.video.placeholder": "Introdu URL-ul video-ului",
"field.blocks.video.url.label": "URL-ul video-ului",
"field.blocks.video.url.placeholder": "https://youtube.com/?v=",
"field.files.empty": "Niciun fișier selectat deocamdată",
"field.layout.delete": "Șterge layout-ul",
"field.layout.delete.confirm": "Chiar vrei să ștergi acest layout?",
"field.layout.empty": "Niciun rând deocamdată",
"field.layout.select": "Alege un layout",
"field.object.empty": "Nicio informație deocamdată",
"field.pages.empty": "Nicio pagină aleasă deocamdată",
"field.structure.delete.confirm": "Chiar vrei să ștergi acest rând?",
"field.structure.delete.confirm.all": "Chiar vrei să ștergi toate înregistrările?",
"field.structure.empty": "Nicio înregistrare deocamdată",
"field.users.empty": "Niciun utilizator ales deocamdată",
"file.blueprint": "Acest fișier nu are încă un Blueprint. Poți să-l definești în <strong>/site/blueprints/files/{blueprint}.yml</strong>",
"file.delete.confirm": "Chiar vrei să ștergi <br><strong>{filename}</strong>?",
"file.sort": "Schimbă poziția",
"files": "Fișiere",
"files.empty": "Niciun fișier deocamdată",
"hide": "Ascunde",
"hour": "Ora",
"import": "Importă",
"info": "Informații",
"insert": "Inserează",
"insert.after": "Inserează după",
"insert.before": "Inserează înainte",
"install": "Instalează",
"installation": "Instalare",
"installation.completed": "Panoul a fost instalat",
"installation.disabled": "Instalarea panoului este dezactivată în mod implicit pe servere publice. Te rog rulează instalarea pe o mașină locală sau activează-l cu opțiunea <code>panel.install</code>.",
"installation.issues.accounts": "Directorul <i>/site/accounts</i> nu există sau nu are permisiuni de scriere.",
"installation.issues.content": "Directorul <code>/content</code> nu există sau nu are permisiuni de scriere.",
"installation.issues.curl": "Extensia <code>CURL</code> este necesară",
"installation.issues.headline": "Panoul nu poate fi instalat",
"installation.issues.mbstring": "Extensia <code>MB String</code> este necesară",
"installation.issues.media": "Directorul <code>/media</code> nu există sau nu are permisiuni de scriere",
"installation.issues.php": "Asigură-te că folosești <code>PHP 8+</code>",
"installation.issues.server": "Kirby are nevoie de <code>Apache</code>, <code>Nginx</code> sau <code>Caddy</code>",
"installation.issues.sessions": "Directorul <code>/site/sessions</code> folder nu există sau nu are permisiuni de scriere",
"language": "Limba",
"language.code": "Cod",
"language.convert": "Stabilește ca implicit",
"language.convert.confirm": "<p>Chiar vrei să transformi <strong>{name}</strong> în limba implicită? Această modificare este ireversibilă.</p><p>Dacă <strong>{name}</strong> are conținut netradus, unele părți din site s-ar putea să nu mai aibă conținut de rezervă, și vor apărea goale.</p>",
"language.create": "Adaugă o limbă nouă",
"language.delete.confirm": "Chiar vrei să ștergi limba <strong>{name}</strong>, inclusiv toate traducerile? Această operațiune este ireversibilă.",
"language.deleted": "Limba a fost ștearsă",
"language.direction": "Direcția de citire",
"language.direction.ltr": "De la stânga la dreapta",
"language.direction.rtl": "De la dreapta la stânga",
"language.locale": "String-ul PHP locale",
"language.locale.warning": "Folosești pentru localizare o formulă manuală. Modificările le poți face în fișierul de limbă în /site/languages",
"language.name": "Nume",
"language.updated": "Limba a fost actualizată",
"languages": "Limbi",
"languages.default": "Limba implicită",
"languages.empty": "Nu există limbi deocamdată",
"languages.secondary": "Limbi secundare",
"languages.secondary.empty": "Nu există limbi secundare deocamdată.",
"license": "Licența",
"license.buy": "Cumpără o licență",
"license.register": "Înregistrează-te",
"license.manage": "Gestionează-ți licențele",
"license.register.help": "Ai primit codul de licență pe adresa de e-mail după cumpărare. Te rog copiaz-o și insereaz-o pentru a te înregistra.",
"license.register.label": "Te rog introdu codul tău de licență",
"license.register.domain": "Licența îți va fi înregistrată pentru <strong>{host}</strong>.",
"license.register.local": "Ești pe punctul de a-ți înregistra licența pentru domeniul tău local <strong>{host}</strong>. Dacă acest site va fi instalat pe un domeniu public, te rog înregistrează licența acolo, nu aici. Dacă {host} este domeniul pentru care vrei licența Kirby, te rog continuă.",
"license.register.success": "Mulțumim că susții Kirby",
"license.unregistered": "Acesta este un demo Kirby neînregistrat",
"license.unregistered.label": "Neînregistrat",
"link": "Legătură",
"link.text": "Textul legăturii",
"loading": "Se încarcă",
"lock.unsaved": "Schimbări nesalvate",
"lock.unsaved.empty": "Nu mai există nicio schimbare nesalvată",
"lock.isLocked": "Schimbări nesalvate de <strong>{email}</strong>",
"lock.file.isLocked": "Fișierul este editat momentan de {email} și nu poate fi schimbat.",
"lock.page.isLocked": "Pagina este editată momentan de {email} și nu poate fi schimbată.",
"lock.unlock": "Deblochează",
"lock.isUnlocked": "Schimbările tale nesalvate au fost suprascrise de un alt utilizator. Poți să-ți descarci schimbările pentru a le încorpora manual.",
"login": "Conectează-te",
"login.code.label.login": "Cod de conectare",
"login.code.label.password-reset": "Cod de restabilire parolă",
"login.code.placeholder.email": "000 000",
"login.code.text.email": "Dacă adresa de e-mail este înregistrată, codul cerut a fost trimis pe adresă.",
"login.email.login.body": "Salut {user.nameOrEmail},\n\nAi cerut recent un cod de conectare pentru Panoul site-ului {site}.\nCodul de conectare de mai jos va fi valid pentru următoarele {timeout} minute:\n\n{code}\n\nDacă nu tu ai cerut un cod de conectare, te rog ignoră acest e-mail sau ia legătura cu administratorul site-ului dacă ai întrebări.\nDin motive de siguranță, te rog să NU trimiți acest email mai departe.",
"login.email.login.subject": "Codul tău de conectare",
"login.email.password-reset.body": "Salut {user.nameOrEmail},\n\nAi cerut recent un cod de restabilire a parolei pentru Panoul site-ului {site}.\nCodul de restabilire a parolei de mai jos este valabil pentru următoarele {timeout} minute:\n\n{code}\n\nDacă nu tu ai cerut codul de restabilire a parolei, te rog ignoră acest e-mail sau ia legătura cu administratorul site-ului dacă ai întrebări.\nDin motive de securitate, te rog să NU trimiți acest e-mail mai departe.",
"login.email.password-reset.subject": "Codul tău de restabilire a parolei",
"login.remember": "Ține-mă conectat",
"login.reset": "Restabilește parola",
"login.toggleText.code.email": "Conectare prin e-mail",
"login.toggleText.code.email-password": "Conectare cu parola",
"login.toggleText.password-reset.email": "Ți-ai uitat parola?",
"login.toggleText.password-reset.email-password": "← Înapoi la conectare",
"logout": "Deconectare",
"menu": "Meniu",
"meridiem": "AM/PM",
"mime": "Tipul media",
"minutes": "Minute",
"month": "Luna",
"months.april": "Aprilie",
"months.august": "August",
"months.december": "Decembrie",
"months.february": "Februarie",
"months.january": "Ianuarie",
"months.july": "Iulie",
"months.june": "Iunie",
"months.march": "Martie",
"months.may": "Mai",
"months.november": "Noiembrie",
"months.october": "Octombrie",
"months.september": "Septembrie",
"more": "Mai multe",
"name": "Nume",
"next": "Următoarea",
"no": "nu",
"off": "oprit",
"on": "pornit",
"open": "Deschide",
"open.newWindow": "Deschide în fereastră nouă",
"options": "Opțiuni",
"options.none": "Nicio opțiune",
"orientation": "Orientare",
"orientation.landscape": "Landscape",
"orientation.portrait": "Portrait",
"orientation.square": "Pătrată",
"page.blueprint": "Această pagină nu are încă un Blueprint. Poți să-l definești în <strong>/site/blueprints/pages/{blueprint}.yml</strong>",
"page.changeSlug": "Schimbă URL-ul",
"page.changeSlug.fromTitle": "Creează din titlu",
"page.changeStatus": "Schimbă statutul",
"page.changeStatus.position": "Te rog alege o poziție",
"page.changeStatus.select": "Alege un statut nou",
"page.changeTemplate": "Schimbă șablonul",
"page.delete.confirm": "Chiar vrei să ștergi <strong>{title}</strong>?",
"page.delete.confirm.subpages": "<strong>Această pagină are subpagini</strong>. <br>Subpaginile vor fi de asemenea toate șterse.",
"page.delete.confirm.title": "Introdu titlul paginii pentru a confirma",
"page.draft.create": "Creează ciornă",
"page.duplicate.appendix": "Copiază",
"page.duplicate.files": "Copiază fișierele",
"page.duplicate.pages": "Copiază paginile",
"page.sort": "Schimbă poziția",
"page.status": "Statut",
"page.status.draft": "Ciornă",
"page.status.draft.description": "Pagina este în modul ciornă și va fi vizibilă doar editorilor conectați sau printr-un link secret",
"page.status.listed": "Publică",
"page.status.listed.description": "Pagina este publică, accesibilă oricui",
"page.status.unlisted": "Nelistată",
"page.status.unlisted.description": "Pagina este accesibilă doar prin URL",
"pages": "Pagini",
"pages.empty": "Nicio pagină deocamdată",
"pages.status.draft": "Ciorne",
"pages.status.listed": "Publicate",
"pages.status.unlisted": "Nelistate",
"pagination.page": "Pagină",
"password": "Parola",
"paste": "Inserează",
"paste.after": "Inserează după",
"pixel": "Pixel",
"plugin": "Plugin",
"plugins": "Plugin-uri",
"prev": "Precedenta",
"preview": "Previzualizează",
"remove": "Înlătură",
"rename": "Redenumește",
"replace": "\u00cenlocuie\u0219te",
"retry": "Încearcă din nou",
"revert": "Renunță",
"revert.confirm": "Chiar vrei să ștergi <strong>toate schimbările nesalvate</strong>?",
"role": "Rol",
"role.admin.description": "Administratorul are toate drepturile",
"role.admin.title": "Administrator",
"role.all": "Toate",
"role.empty": "Nu există niciun utilizator cu acest rol",
"role.description.placeholder": "Nicio descriere",
"role.nobody.description": "Acesta este un rol de rezervă fără nicio permisiune.",
"role.nobody.title": "Nimeni",
"save": "Salveaz\u0103",
"search": "Caută",
"search.min": "Introdu {min} caractere pentru a căuta",
"search.all": "Arată toate",
"search.results.none": "Niciun rezultat",
"section.required": "Această secțiune este necesară",
"security": "Securitate",
"select": "Alege",
"server": "Server",
"settings": "Reglaje",
"show": "Arată",
"site.blueprint": "Site-ul nu are încă un Blueprint. Poți să-l definești în <strong>/site/blueprints/site.yml</strong>",
"size": "Dimensiune",
"slug": "Apendicele URL",
"sort": "Sortare",
"stats.empty": "Niciun raport",
"system.issues.content": "Directorul de conținut pare să fie expus",
"system.issues.eol.kirby": "Versiunea instalată de Kirby a ajuns la sfârșitul vieții utile și nu va mai primi actualizări de securitate.",
"system.issues.eol.plugin": "Versiunea instalată a plugin-ului { plugin } a ajuns la sfârșitul vieții utile și nu va mai primi actualizări de securitate.",
"system.issues.debug": "Modul depanare trebuie să fie oprit în producție",
"system.issues.git": "Directorul .git pare să fie expus",
"system.issues.https": "Recomandăm HTTPS pentru toate site-urile.",
"system.issues.kirby": "Directorul Kirby pare să fie expus",
"system.issues.site": "Directorul site pare să fie expus",
"system.issues.vulnerability.kirby": "Instalarea ta ar putea fi afectată de următoarea vulnerabilitate ({ severity } severity): { description }",
"system.issues.vulnerability.plugin": "Instalarea ta ar putea fi afectată de următoarea vulnerabilitate în plugin-ul { plugin } ({ severity } severity): { description }",
"system.updateStatus": "Statut actualizare",
"system.updateStatus.error": "Nu am putut căuta actualizări",
"system.updateStatus.not-vulnerable": "Nicio vulnerabilitate cunoscută",
"system.updateStatus.security-update": "Actualizare gratuită de securitate { version } disponibilă",
"system.updateStatus.security-upgrade": "Actualizarea { version } cu reparații de securitate disponibilă",
"system.updateStatus.unreleased": "Versiune nelansată",
"system.updateStatus.up-to-date": "La zi",
"system.updateStatus.update": "Actualizare gratuită { version } disponibilă",
"system.updateStatus.upgrade": "Actualizare { version } disponibilă",
"title": "Titlu",
"template": "Șablon",
"today": "Astăzi",
"toolbar.button.code": "Cod",
"toolbar.button.bold": "Bold",
"toolbar.button.email": "Adresă e-mail",
"toolbar.button.headings": "Subtitluri",
"toolbar.button.heading.1": "Subtitlu 1",
"toolbar.button.heading.2": "Subtitlu 2",
"toolbar.button.heading.3": "Subtitlu 3",
"toolbar.button.heading.4": "Subtitlu 4",
"toolbar.button.heading.5": "Subtitlu 5",
"toolbar.button.heading.6": "Subtitlu 6",
"toolbar.button.italic": "Italic",
"toolbar.button.file": "Fișier",
"toolbar.button.file.select": "Alege un fișier",
"toolbar.button.file.upload": "Încarcă un fișier",
"toolbar.button.link": "Link",
"toolbar.button.paragraph": "Paragraf",
"toolbar.button.strike": "Tăiat",
"toolbar.button.ol": "Listă ordonată",
"toolbar.button.underline": "Subliniat",
"toolbar.button.ul": "Listă cu puncte",
"translation.author": "Echipa Kirby",
"translation.direction": "ltr",
"translation.name": "Rom\u00e2n\u0103",
"translation.locale": "ro_RO",
"upload": "Încarcă",
"upload.error.cantMove": "Fișierul încărcat nu a putut fi mutat",
"upload.error.cantWrite": "Nu s-a putut scrie fișierul pe disc",
"upload.error.default": "Fișierul nu a putut fi încărcat",
"upload.error.extension": "Încărcarea fișierelor oprită de extensie",
"upload.error.formSize": "Fișierul încărcat depășește directiva MAX_FILE_SIZE specificată în formular",
"upload.error.iniPostSize": "Fișierul încărcat depășește directiva post_max_size din php.ini",
"upload.error.iniSize": "Fișierul încărcat depășește directiva upload_max_filesize din php.ini",
"upload.error.noFile": "Nu a fost încărcat niciun fișier",
"upload.error.noFiles": "Nu au fost încărcate fișiere",
"upload.error.partial": "Fișierul a fost încărcat doar parțial",
"upload.error.tmpDir": "Lipsește un director temporar",
"upload.errors": "Eroare",
"upload.progress": "Se încarcă...",
"url": "Url",
"url.placeholder": "https://example.com",
"user": "Utilizator",
"user.blueprint": "Poți defini secțiuni și câmpuri de formular suplimentare pentru acest rol de utilizator în <strong>/site/blueprints/users/{blueprint}.yml</strong>",
"user.changeEmail": "Schimbă adresa de e-mail",
"user.changeLanguage": "Schimbă limba",
"user.changeName": "Redenumește acest utilizator",
"user.changePassword": "Schimbă parola",
"user.changePassword.new": "Parola nouă",
"user.changePassword.new.confirm": "Confirmă parola nouă...",
"user.changeRole": "Schimbă rolul",
"user.changeRole.select": "Alege un rol nou",
"user.create": "Adaugă un nou utilizator",
"user.delete": "Șterge acest utilizator",
"user.delete.confirm": "Chiar vrei să ștergi <br><strong>{email}</strong>?",
"users": "Utilizatori",
"version": "Versiune",
"version.current": "Versiunea curentă",
"version.latest": "Ultima versiune",
"versionInformation": "Informații despre versiune",
"view.account": "Contul t\u0103u",
"view.installation": "Instalare",
"view.languages": "Limbi",
"view.resetPassword": "Restabilește parola",
"view.site": "Site",
"view.system": "Sistem",
"view.users": "Utilizatori",
"welcome": "Bun venit",
"year": "Anul",
"yes": "da"
}

View File

@@ -183,6 +183,7 @@
"error.user.email.invalid": "Пожалуйста, введите правильный адрес эл. почты",
"error.user.language.invalid": "Введите правильный язык",
"error.user.notFound": "Пользователь \"{name}\" не найден",
"error.user.password.excessive": "Please enter a valid password. Passwords must not be longer than 1000 characters.",
"error.user.password.invalid": "Пожалуйста, введите правильный пароль. Он должен состоять минимум из 8 символов.",
"error.user.password.notSame": "\u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u0435 \u043f\u0430\u0440\u043e\u043b\u044c",
"error.user.password.undefined": "У пользователя нет пароля",

View File

@@ -183,6 +183,7 @@
"error.user.email.invalid": "Prosím, zadajte platnú e-mailovú adresu",
"error.user.language.invalid": "Prosím, zadajte platný jazyk",
"error.user.notFound": "Užívateľa \"{name}\" nie je možné nájsť",
"error.user.password.excessive": "Please enter a valid password. Passwords must not be longer than 1000 characters.",
"error.user.password.invalid": "Prosím, zadajte platné heslo. Dĺžka hesla musí byť aspoň 8 znakov.",
"error.user.password.notSame": "Heslá nie sú rovnaké",
"error.user.password.undefined": "Užívateľ nemá heslo",

View File

@@ -72,12 +72,12 @@
"error.blocks.min.singular": "Du måste lägga till minst ett block",
"error.blocks.validation": "Det finns ett fel i fältet \"{field}\" i block {index} med blocktypen \"{fieldset}\"",
"error.cache.type.invalid": "Invalid cache type \"{type}\"",
"error.cache.type.invalid": "Ogiltig cachetyp \"{type}\"",
"error.email.preset.notFound": "E-postförinställningen \"{name}\" kan inte hittas",
"error.field.converter.invalid": "Ogiltig omvandlare \"{converter}\"",
"error.field.type.missing": "Field \"{ name }\": The field type \"{ type }\" does not exist",
"error.field.type.missing": "Fältet \"{ name }\": Fälttypen \"{ type }\" finns inte",
"error.file.changeName.empty": "Namnet får inte vara tomt",
"error.file.changeName.permission": "Du har inte behörighet att ändra namnet på \"{filename}\"",
@@ -163,7 +163,7 @@
"error.site.changeTitle.permission": "Du har inte behörighet att ändra titeln på webbplatsen",
"error.site.update.permission": "Du har inte behörighet att uppdatera webbplatsen",
"error.template.default.notFound": "Standardmallen existerar inte",
"error.template.default.notFound": "Standardmallen finns inte",
"error.unexpected": "Ett oväntat fel uppstod! Aktivera felsökningsläge för mer information: https://getkirby.com/docs/reference/system/options/debug",
@@ -183,6 +183,7 @@
"error.user.email.invalid": "Ange en giltig e-postadress",
"error.user.language.invalid": "Ange ett giltigt språk",
"error.user.notFound": "Användaren \"{name}\" kan ej hittas",
"error.user.password.excessive": "Please enter a valid password. Passwords must not be longer than 1000 characters.",
"error.user.password.invalid": "Ange ett giltigt lösenord. Lösenordet måste vara minst 8 tecken långt.",
"error.user.password.notSame": "Lösenorden matchar inte",
"error.user.password.undefined": "Användaren har inget lösenord",
@@ -292,7 +293,7 @@
"field.pages.empty": "Inga sidor valda än",
"field.structure.delete.confirm": "Vill du verkligen radera denna rad?",
"field.structure.delete.confirm.all": "Do you really want to delete all entries?",
"field.structure.delete.confirm.all": "Vill du verkligen radera alla poster?",
"field.structure.empty": "Inga poster än",
"field.users.empty": "Inga användare valda än",
@@ -353,8 +354,8 @@
"license.manage": "Hantera dina licenser",
"license.register.help": "Du fick din licenskod via e-post efter inköpet. Kopiera och klistra in den för att registrera licensen.",
"license.register.label": "Ange din licenskod",
"license.register.domain": "Your license will be registered to <strong>{host}</strong>.",
"license.register.local": "You are about to register your license for your local domain <strong>{host}</strong>. If this site will be deployed to a public domain, please register it there instead. If {host} is the domain you want to license Kirby to, please continue.",
"license.register.domain": "Din licens kommer att bli registrerad för <strong>{host}</strong>.",
"license.register.local": "Du är på väg att registrera din licens för din lokala domän {host}. Om den här webbplatsen kommer att distribueras till en offentlig domän, registrera den där istället. Om {host} är domänen du vill licensiera Kirby till, fortsätt.",
"license.register.success": "Tack för att du stödjer Kirby",
"license.unregistered": "Detta är en oregistrerad demo av Kirby",
"license.unregistered.label": "Oregistrerad",

View File

@@ -183,6 +183,7 @@
"error.user.email.invalid": "Lütfen geçerli bir e-posta adresi girin",
"error.user.language.invalid": "Lütfen geçerli bir dil girin",
"error.user.notFound": "\"{name}\" kullanıcısı bulunamadı",
"error.user.password.excessive": "Please enter a valid password. Passwords must not be longer than 1000 characters.",
"error.user.password.invalid": "Lütfen geçerli bir şifre giriniz. Şifreler en az 8 karakter uzunluğunda olmalıdır.",
"error.user.password.notSame": "L\u00fctfen \u015fifreyi do\u011frulay\u0131n",
"error.user.password.undefined": "Bu kullanıcının şifresi yok",

View File

@@ -1,11 +0,0 @@
const { defineConfig } = require("cypress");
module.exports = defineConfig({
video: false,
e2e: {
baseUrl: "http://sandbox.test",
specPattern: "src/**/*.e2e.js",
supportFile: false
}
});

File diff suppressed because one or more lines are too long

View File

@@ -367,14 +367,14 @@ class App
* @return \Kirby\Toolkit\Collection|null
* @todo 5.0 Add return type declaration
*/
public function collection(string $name)
public function collection(string $name, array $options = [])
{
return $this->collections()->get($name, [
return $this->collections()->get($name, array_merge($options, [
'kirby' => $this,
'site' => $this->site(),
'pages' => $this->site()->children(),
'site' => $site = $this->site(),
'pages' => $site->children(),
'users' => $this->users()
]);
]));
}
/**
@@ -580,7 +580,14 @@ class App
$visitor = $this->visitor();
foreach ($visitor->acceptedLanguages() as $acceptedLang) {
$closure = fn ($language) => $language->locale(LC_ALL) === $acceptedLang->locale();
$closure = function ($language) use ($acceptedLang) {
$languageLocale = $language->locale(LC_ALL);
$acceptedLocale = $acceptedLang->locale();
return $languageLocale === $acceptedLocale ||
$acceptedLocale === Str::substr($languageLocale, 0, 2);
};
if ($language = $languages->filter($closure)?->first()) {
return $language;
}

View File

@@ -279,18 +279,39 @@ class Auth
$id = $session->data()->get('kirby.userId');
// if no user is logged in, return immediately
if (is_string($id) !== true) {
return null;
}
if ($user = $this->kirby->users()->find($id)) {
// in case the session needs to be updated, do it now
// for better performance
$session->commit();
return $user;
// a user is logged in, ensure it exists
$user = $this->kirby->users()->find($id);
if ($user === null) {
return null;
}
return null;
if ($passwordTimestamp = $user->passwordTimestamp()) {
$loginTimestamp = $session->data()->get('kirby.loginTimestamp');
if (is_int($loginTimestamp) !== true) {
// session that was created before Kirby
// 3.5.8.3, 3.6.6.3, 3.7.5.2, 3.8.4.1 or 3.9.6
// or when the user didn't have a password set
$user->logout();
return null;
}
// invalidate the session if the password
// changed since the login
if ($loginTimestamp < $passwordTimestamp) {
$user->logout();
return null;
}
}
// in case the session needs to be updated, do it now
// for better performance
$session->commit();
return $user;
}
/**

View File

@@ -121,7 +121,10 @@ class Find
'site' => $kirby->site(),
'account' => static::user(),
'page' => static::page(basename($path)),
'file' => static::file(...explode('/files/', $path)),
// regular expression to split the path at the last
// occurrence of /files/ which separates parent path
// and filename
'file' => static::file(...preg_split('$.*\K(/files/)$', $path)),
'user' => $kirby->user(basename($path)),
default => throw new InvalidArgumentException('Invalid model type: ' . $modelType)
};

View File

@@ -441,6 +441,9 @@ class User extends ModelWithContent
$session->regenerateToken(); // privilege change
$session->data()->set('kirby.userId', $this->id());
if ($this->passwordTimestamp() !== null) {
$session->data()->set('kirby.loginTimestamp', time());
}
$this->kirby()->auth()->setUser($this);
$kirby->trigger('user.login:after', ['user' => $this, 'session' => $session]);
@@ -461,6 +464,7 @@ class User extends ModelWithContent
// remove the user from the session for future requests
$session->data()->remove('kirby.userId');
$session->data()->remove('kirby.loginTimestamp');
// clear the cached user object from the app state of the current request
$this->kirby()->auth()->flush();
@@ -607,6 +611,26 @@ class User extends ModelWithContent
return $this->password = $this->readPassword();
}
/**
* Returns the timestamp when the password
* was last changed
*/
public function passwordTimestamp(): int|null
{
$file = $this->passwordFile();
// ensure we have the latest information
// to prevent cache attacks
clearstatcache();
// user does not have a password
if (is_file($file) === false) {
return null;
}
return filemtime($file);
}
/**
* @return \Kirby\Cms\UserPermissions
*/
@@ -864,14 +888,29 @@ class User extends ModelWithContent
throw new NotFoundException(['key' => 'user.password.undefined']);
}
// `UserRules` enforces a minimum length of 8 characters,
// so everything below that is a typo
if (Str::length($password) < 8) {
throw new InvalidArgumentException(['key' => 'user.password.invalid']);
}
// too long passwords can cause DoS attacks
if (Str::length($password) > 1000) {
throw new InvalidArgumentException(['key' => 'user.password.excessive']);
}
if (password_verify($password, $this->password()) !== true) {
throw new InvalidArgumentException(['key' => 'user.password.wrong', 'httpCode' => 401]);
}
return true;
}
/**
* Returns the path to the password file
*/
protected function passwordFile(): string
{
return $this->root() . '/.htpasswd';
}
}

View File

@@ -118,6 +118,13 @@ trait UserActions
// update the users collection
$user->kirby()->users()->set($user->id(), $user);
// keep the user logged in to the current browser
// if they changed their own password
// (regenerate the session token, update the login timestamp)
if ($user->isLoggedIn() === true) {
$user->loginPasswordless();
}
return $user;
});
}
@@ -323,7 +330,7 @@ trait UserActions
*/
protected function readPassword()
{
return F::read($this->root() . '/.htpasswd');
return F::read($this->passwordFile());
}
/**
@@ -384,6 +391,6 @@ trait UserActions
#[SensitiveParameter]
string $password = null
): bool {
return F::write($this->root() . '/.htpasswd', $password);
return F::write($this->passwordFile(), $password);
}
}

View File

@@ -341,12 +341,23 @@ class UserRules
#[SensitiveParameter]
string $password
): bool {
// too short passwords are ineffective
if (Str::length($password ?? null) < 8) {
throw new InvalidArgumentException([
'key' => 'user.password.invalid',
]);
}
// too long passwords can cause DoS attacks
// and are therefore blocked in the auth system
// (blocked here as well to avoid passwords
// that cannot be used to log in)
if (Str::length($password ?? null) > 1000) {
throw new InvalidArgumentException([
'key' => 'user.password.excessive',
]);
}
return true;
}

View File

@@ -93,10 +93,14 @@ class Txt extends Handler
throw new InvalidArgumentException('Invalid TXT data; please pass a string');
}
// remove BOM
$string = str_replace("\xEF\xBB\xBF", '', $string);
// remove Unicode BOM at the beginning of the file
if (Str::startsWith($string, "\xEF\xBB\xBF") === true) {
$string = substr($string, 3);
}
// explode all fields by the line separator
$fields = preg_split('!\n----\s*\n*!', $string);
// start the data array
$data = [];

View File

@@ -176,6 +176,18 @@ class Response
'type' => F::extensionToMime(F::extension($file))
], $props);
// if we couldn't serve a correct MIME type, force
// the browser to display the file as plain text to
// harden against attacks from malicious file uploads
if ($props['type'] === null) {
if (isset($props['headers']) !== true) {
$props['headers'] = [];
}
$props['type'] = 'text/plain';
$props['headers']['X-Content-Type-Options'] = 'nosniff';
}
return new static($props);
}

View File

@@ -278,6 +278,8 @@ class Document
'panelUrl' => $uri->path()->toString(true) . '/',
]);
return new Response($body, 'text/html', $code);
return new Response($body, 'text/html', $code, [
'Content-Security-Policy' => "frame-ancestors 'none'"
]);
}
}

View File

@@ -11,6 +11,7 @@ use Kirby\Exception\NotFoundException;
use Kirby\Exception\PermissionException;
use Kirby\Http\Response;
use Kirby\Http\Router;
use Kirby\Http\Uri;
use Kirby\Http\Url;
use Kirby\Toolkit\Str;
use Kirby\Toolkit\Tpl;
@@ -538,15 +539,22 @@ class Panel
*/
public static function url(string|null $url = null): string
{
$slug = App::instance()->option('panel.slug', 'panel');
// only touch relative paths
if (Url::isAbsolute($url) === false) {
$path = trim($url, '/');
$kirby = App::instance();
$slug = $kirby->option('panel.slug', 'panel');
$path = trim($url, '/');
$baseUri = new Uri($kirby->url());
$basePath = trim($baseUri->path()->toString(), '/');
// removes base path if relative path contains it
if (empty($basePath) === false && Str::startsWith($path, $basePath) === true) {
$path = Str::after($path, $basePath);
}
// add the panel slug prefix if it it's not
// included in the path yet
if (Str::startsWith($path, $slug . '/') === false) {
elseif (Str::startsWith($path, $slug . '/') === false) {
$path = $slug . '/' . $path;
}

View File

@@ -70,6 +70,10 @@ class Argument
// numeric
if (is_numeric($argument) === true) {
if (strpos($argument, '.') === false) {
return new static((int)$argument);
}
return new static((float)$argument);
}

View File

@@ -117,7 +117,7 @@ Query::$entries['file'] = function (string $id): File|null {
};
Query::$entries['page'] = function (string $id): Page|null {
return App::instance()->site()->find($id);
return App::instance()->page($id);
};
Query::$entries['site'] = function (): Site {

View File

@@ -77,19 +77,17 @@ class Segment
/**
* Automatically resolves the segment depending on the
* segment position and the type of the base
*
* @param mixed $base Current value of the query chain
*/
public function resolve(mixed $base = null, array|object $data = []): mixed
{
// resolve arguments to array
$args = $this->arguments?->resolve($data) ?? [];
// 1st segment, start from $data array
// 1st segment, use $data as base
if ($this->position === 0) {
if (is_array($data) == true) {
return $this->resolveArray($data, $args);
}
return $this->resolveObject($data, $args);
$base = $data;
}
if (is_array($base) === true) {
@@ -109,26 +107,55 @@ class Segment
*/
protected function resolveArray(array $array, array $args): mixed
{
if (array_key_exists($this->method, $array) === false) {
static::error($array, $this->method, 'property');
// the directly provided array takes precedence
// to look up a matching entry
if (array_key_exists($this->method, $array) === true) {
$value = $array[$this->method];
// if this is a Closure we can directly use it, as
// Closures from the $array should always have priority
// over the Query::$entries Closures
if ($value instanceof Closure) {
return $value(...$args);
}
// if we have no arguments to pass, we also can directly
// use the value from the $array as it must not be different
// to the one from Query::$entries with the same name
if ($args === []) {
return $value;
}
}
$value = $array[$this->method];
if ($value instanceof Closure) {
return $value(...$args);
// fallback time: only if we are handling the first segment,
// we can also try to resolve the segment with an entry from the
// default Query::$entries
if ($this->position === 0) {
if (array_key_exists($this->method, Query::$entries) === true) {
return Query::$entries[$this->method](...$args);
}
}
if ($args !== []) {
// if we have not been able to return anything so far,
// we just need to differntiate between two different error messages
// this one is in case the original array contained the key,
// but was not a Closure while the segment had arguments
if (
array_key_exists($this->method, $array) &&
$args !== []
) {
throw new InvalidArgumentException('Cannot access array element "' . $this->method . '" with arguments');
}
return $value;
// last, the standard error for trying to access something
// that does not exist
static::error($array, $this->method, 'property');
}
/**
* Resolves segment by calling the method/accessing the property
* on the base object
* Resolves segment by calling the method/
* accessing the property on the base object
*/
protected function resolveObject(object $object, array $args): mixed
{
@@ -140,7 +167,8 @@ class Segment
}
if (
$args === [] && (
$args === [] &&
(
property_exists($object, $this->method) === true ||
method_exists($object, '__get') === true
)

View File

@@ -26,11 +26,25 @@ class Controller
public function arguments(array $data = []): array
{
$info = new ReflectionFunction($this->function);
$args = [];
return A::map(
$info->getParameters(),
fn ($parameter) => $data[$parameter->getName()] ?? null
);
foreach ($info->getParameters() as $param) {
$name = $param->getName();
if ($param->isVariadic() === true) {
// variadic ... argument collects all remaining values
$args += $data;
} elseif (isset($data[$name]) === true) {
// use provided argument value if available
$args[$name] = $data[$name];
} elseif ($param->isDefaultValueAvailable() === false) {
// use null for any other arguments that don't define
// a default value for themselves
$args[$name] = null;
}
}
return $args;
}
public function call($bind = null, $data = [])

View File

@@ -271,7 +271,7 @@ class Xml
*/
public static function parse(string $xml): array|null
{
$xml = @simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOENT);
$xml = @simplexml_load_string($xml);
if (is_object($xml) !== true) {
return null;

View File

@@ -143,10 +143,6 @@ return array(
'Kirby\\Cms\\UserRules' => $baseDir . '/src/Cms/UserRules.php',
'Kirby\\Cms\\Users' => $baseDir . '/src/Cms/Users.php',
'Kirby\\Cms\\Visitor' => $baseDir . '/src/Cms/Visitor.php',
'Kirby\\ComposerInstaller\\CmsInstaller' => $vendorDir . '/getkirby/composer-installer/src/ComposerInstaller/CmsInstaller.php',
'Kirby\\ComposerInstaller\\Installer' => $vendorDir . '/getkirby/composer-installer/src/ComposerInstaller/Installer.php',
'Kirby\\ComposerInstaller\\Plugin' => $vendorDir . '/getkirby/composer-installer/src/ComposerInstaller/Plugin.php',
'Kirby\\ComposerInstaller\\PluginInstaller' => $vendorDir . '/getkirby/composer-installer/src/ComposerInstaller/PluginInstaller.php',
'Kirby\\Data\\Data' => $baseDir . '/src/Data/Data.php',
'Kirby\\Data\\Handler' => $baseDir . '/src/Data/Handler.php',
'Kirby\\Data\\Json' => $baseDir . '/src/Data/Json.php',

View File

@@ -256,10 +256,6 @@ class ComposerStaticInita8011b477bb239488e5d139cdeb7b31e
'Kirby\\Cms\\UserRules' => __DIR__ . '/../..' . '/src/Cms/UserRules.php',
'Kirby\\Cms\\Users' => __DIR__ . '/../..' . '/src/Cms/Users.php',
'Kirby\\Cms\\Visitor' => __DIR__ . '/../..' . '/src/Cms/Visitor.php',
'Kirby\\ComposerInstaller\\CmsInstaller' => __DIR__ . '/..' . '/getkirby/composer-installer/src/ComposerInstaller/CmsInstaller.php',
'Kirby\\ComposerInstaller\\Installer' => __DIR__ . '/..' . '/getkirby/composer-installer/src/ComposerInstaller/Installer.php',
'Kirby\\ComposerInstaller\\Plugin' => __DIR__ . '/..' . '/getkirby/composer-installer/src/ComposerInstaller/Plugin.php',
'Kirby\\ComposerInstaller\\PluginInstaller' => __DIR__ . '/..' . '/getkirby/composer-installer/src/ComposerInstaller/PluginInstaller.php',
'Kirby\\Data\\Data' => __DIR__ . '/../..' . '/src/Data/Data.php',
'Kirby\\Data\\Handler' => __DIR__ . '/../..' . '/src/Data/Handler.php',
'Kirby\\Data\\Json' => __DIR__ . '/../..' . '/src/Data/Json.php',

View File

@@ -1,8 +1,8 @@
<?php return array(
'root' => array(
'name' => 'getkirby/cms',
'pretty_version' => '3.9.5',
'version' => '3.9.5.0',
'pretty_version' => '3.9.6',
'version' => '3.9.6.0',
'reference' => NULL,
'type' => 'kirby-cms',
'install_path' => __DIR__ . '/../../',
@@ -38,8 +38,8 @@
'dev_requirement' => false,
),
'getkirby/cms' => array(
'pretty_version' => '3.9.5',
'version' => '3.9.5.0',
'pretty_version' => '3.9.6',
'version' => '3.9.6.0',
'reference' => NULL,
'type' => 'kirby-cms',
'install_path' => __DIR__ . '/../../',