Upgrade to 3.3.4

This commit is contained in:
Bastian Allgeier
2020-02-18 09:52:14 +01:00
parent 9c87f2fbc3
commit 211e325864
16 changed files with 11378 additions and 145 deletions

View File

@@ -1,7 +1,7 @@
{
"name": "getkirby/cms",
"description": "The Kirby 3 core",
"version": "3.3.3",
"version": "3.3.4",
"license": "proprietary",
"keywords": ["kirby", "cms", "core"],
"homepage": "https://getkirby.com",

14
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": "d555f3a70378b49cf4e1eb249f8aeb85",
"content-hash": "fb4c488a44bae1ff2733bf8802b2dbba",
"packages": [
{
"name": "claviska/simpleimage",
@@ -514,16 +514,16 @@
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.13.1",
"version": "v1.14.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "7b4aab9743c30be783b73de055d24a39cf4b954f"
"reference": "34094cfa9abe1f0f14f48f490772db7a775559f2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/7b4aab9743c30be783b73de055d24a39cf4b954f",
"reference": "7b4aab9743c30be783b73de055d24a39cf4b954f",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/34094cfa9abe1f0f14f48f490772db7a775559f2",
"reference": "34094cfa9abe1f0f14f48f490772db7a775559f2",
"shasum": ""
},
"require": {
@@ -535,7 +535,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.13-dev"
"dev-master": "1.14-dev"
}
},
"autoload": {
@@ -569,7 +569,7 @@
"portable",
"shim"
],
"time": "2019-11-27T14:18:11+00:00"
"time": "2020-01-13T11:15:53+00:00"
},
{
"name": "true/punycode",

View File

@@ -38,7 +38,7 @@ return [
return null;
}
return (float)Str::float($value);
return is_float($value) === true ? $value : (float)Str::float($value);
}
],
'validations' => [

11174
kirby/i18n/rules/ko.json Executable file

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
{
"add": "\ucd94\uac00",
"avatar": "\ud504\ub85c\ud544 \uc774\ubbf8\uc9c0",
"back": "돌아가기",
"back": "복귀",
"cancel": "\ucde8\uc18c",
"change": "\ubcc0\uacbd",
"close": "\ub2eb\uae30",
@@ -43,11 +43,11 @@
"error.avatar.create.fail": "프로필 이미지를 업로드할 수 없습니다.",
"error.avatar.delete.fail": "\ud504\ub85c\ud544 \uc774\ubbf8\uc9c0\ub97c \uc0ad\uc81c\ud560 \uc218 \uc5c6\uc2b5\ub2c8\ub2e4.",
"error.avatar.dimensions.invalid":
"프로필 이미지의 크기를 3,000픽셀 이하로 설정하세요.",
"프로필 이미지의 너비와 높이를 3,000픽셀 이하로 설정하세요.",
"error.avatar.mime.forbidden":
"\uc5c5\ub85c\ub4dc\ud560 \uc218 \uc5c6\ub294 MIME \ud615\uc2dd\uc785\ub2c8\ub2e4.",
"프로필 이미지의 확장자(JPG, JPEG, PNG)를 확인하세요.",
"error.blueprint.notFound": "블루프린트({name})를 불러올 수 없습니다.",
"error.blueprint.notFound": "블루프린트({name})를 확인할 수 없습니다.",
"error.email.preset.notFound": "기본 이메일 주소({name})가 없습니다.",
@@ -69,10 +69,10 @@
"error.file.mime.forbidden": "이 MIME 형식({mime})은 업로드할 수 없습니다.",
"error.file.mime.invalid": "MIME 형식({mime})이 올바르지 않습니다.",
"error.file.mime.missing":
"파일({filename})의 형식을 수 없습니다.",
"error.file.minheight": "이미지의 높이는 적어도 {height}픽셀 이상이어야 합니다.",
"파일({filename})의 형식을 확인할 수 없습니다.",
"error.file.minheight": "{height}픽셀 이상으로 이미지의 높이를 설정하세요.",
"error.file.minsize": "파일이 너무 작습니다.",
"error.file.minwidth": "이미지의 너비는 적어도 {width}픽셀 이상이어야 합니다.",
"error.file.minwidth": "{width}픽셀 이상으로 이미지의 너비를 설정하세요.",
"error.file.name.missing": "파일명을 입력하세요.",
"error.file.notFound": "파일({filename})이 없습니다.",
"error.file.orientation": "이미지의 비율({orientation})을 확인하세요.",
@@ -83,7 +83,7 @@
"error.form.notSaved": "항목을 저장할 수 없습니다.",
"error.language.code": "올바른 언어 코드를 입력하세요.",
"error.language.duplicate": "이미 등록 언어입니다.",
"error.language.duplicate": "이미 등록 언어입니다.",
"error.language.name": "올바른 언어명을 입력하세요.",
"error.license.format": "올바른 라이선스 키를 입력하세요.",
@@ -104,15 +104,15 @@
"페이지({slug})의 템플릿을 변경할 권한이 없습니다.",
"error.page.changeTitle.empty": "제목을 입력하세요.",
"error.page.changeTitle.permission":
"페이지({slug})의 제목을 삭제할 권한이 없습니다.",
"페이지({slug})의 제목을 변경할 권한이 없습니다.",
"error.page.create.permission": "페이지({slug})를 등록할 권한이 없습니다.",
"error.page.delete": "페이지({slug})를 삭제할 수 없습니다.",
"error.page.delete.confirm": "페이지 제목을 입력하세요.",
"error.page.delete.confirm": "페이지를 삭제하려면 페이지의 제목을 입력하세요.",
"error.page.delete.hasChildren":
"하위 페이지가 있는 페이지는 삭제할 수 없습니다.",
"error.page.delete.permission": "페이지({slug})를 삭제할 권한이 없습니다.",
"error.page.draft.duplicate":
"고유 주소({slug})가 같은 초안 있습니다.",
"고유 주소({slug})가 같은 초안 페이지가 있습니다.",
"error.page.duplicate":
"고유 주소({slug})가 같은 페이지가 있습니다.",
"error.page.duplicate.permission": "페이지({slug})를 복제할 권한이 없습니다.",
@@ -130,20 +130,20 @@
"error.section.files.max.singular":
"이 섹션({section})에는 파일을 하나 이상 추가할 수 없습니다.",
"error.section.files.min.plural":
"이 섹션({section})에는 적어도 {min}개 이상의 파일이 필요합니다.",
"이 섹션({section})에는 파일이 {min}개 이상 필요합니다.",
"error.section.files.min.singular":
"이 섹션({section})에는 적어도 하나 이상의 파일이 필요합니다.",
"이 섹션({section})에는 파일이 하나 이상 필요합니다.",
"error.section.pages.max.plural":
"이 섹션({section})에는 페이지를 {max}개 이상 추가할 수 없습니다.",
"error.section.pages.max.singular":
"이 섹션({section})에는 페이지를 하나 이상 추가할 수 없습니다.",
"error.section.pages.min.plural":
"이 섹션({section})에 적어도 {min}개 이상의 페이지가 필요합니다.",
"이 섹션({section})에는 페이지가 {min}개 이상 필요합니다.",
"error.section.pages.min.singular":
"이 섹션({section})에 적어도 하나 이상의 페이지가 필요합니다.",
"이 섹션({section})에는 페이지가 하나 이상 필요합니다.",
"error.section.notLoaded": "섹션({name})을 불러올 수 없습니다.",
"error.section.notLoaded": "섹션({name})을 확인할 수 없습니다.",
"error.section.type.invalid": "섹션의 형식({type})이 올바르지 않습니다.",
"error.site.changeTitle.empty": "제목을 입력하세요.",
@@ -179,7 +179,7 @@
"error.user.language.invalid": "올바른 언어를 입력하세요.",
"error.user.notFound": "사용자({name})가 없습니다.",
"error.user.password.invalid":
"올바른 암호를 입력하세요.",
"암호를 8자 이상으로 설정하세요.",
"error.user.password.notSame": "\uc554\ud638\ub97c \ud655\uc778\ud558\uc138\uc694.",
"error.user.password.undefined": "암호가 설정되지 않았습니다.",
"error.user.role.invalid": "올바른 역할을 입력하세요.",
@@ -187,14 +187,14 @@
"사용자({name})의 정보를 변경할 권한이 없습니다.",
"error.validation.accepted": "확인하세요.",
"error.validation.alpha": "알파벳만 입력할 수 있습니다.",
"error.validation.alpha": "로마자(a~z)만 입력할 수 있습니다.",
"error.validation.alphanum":
"알파벳 또는 숫자만 입력할 수 있습니다.",
"로마자(a~z) 또는 숫자(0~9)만 입력할 수 있습니다.",
"error.validation.between":
"{min} {max} 사이의 값을 입력하세요.",
"{min}, {max} 사이의 값을 입력하세요.",
"error.validation.boolean": "확인하거나 취소하세요.",
"error.validation.contains":
"{needle}에 포함 값을 입력하세요.",
"다음을 포함 값을 입력하세요: {needle}",
"error.validation.date": "올바른 날짜를 입력하세요.",
"error.validation.date.after": "{date} 이후 날짜를 입력하세요.",
"error.validation.date.before": "{date} 이전 날짜를 입력하세요.",
@@ -202,21 +202,21 @@
"error.validation.denied": "취소하세요.",
"error.validation.different": "{other}에 포함된 값은 입력할 수 없습니다.",
"error.validation.email": "올바른 이메일 주소를 입력하세요.",
"error.validation.endswith": "값은 {end}(으)로 끝나야 합니다.",
"error.validation.endswith": "값은 다음으로 끝나야 합니다: {end}",
"error.validation.filename": "올바른 파일명을 입력하세요.",
"error.validation.in": "{in}에 포함된 값을 입력하세요.",
"error.validation.in": "다음 중 하나를 입력하세요: {in}",
"error.validation.integer": "올바른 정수를 입력하세요.",
"error.validation.ip": "올바른 IP 주소를 입력하세요.",
"error.validation.less": "{max}보다 작은 값을 입력하세요.",
"error.validation.less": "{max} 미만의 값을 입력하세요.",
"error.validation.match": "입력한 값이 예상 패턴과 일치하지 않습니다.",
"error.validation.max": "{max} 이하의 값을 입력하세요.",
"error.validation.maxlength":
"{max} 이하의 값을 입력하세요.",
"error.validation.maxwords": "{max}자 이하의 값을 입력하세요.",
"{max} 이하의 값을 입력하세요.",
"error.validation.maxwords": "{max}자 이하 입력하세요.",
"error.validation.min": "{min} 이상의 값을 입력하세요.",
"error.validation.minlength":
"{min} 이상의 값을 입력하세요.",
"error.validation.minwords": "{min}자 이상의 값을 입력하세요.",
"{min} 이상의 값을 입력하세요.",
"error.validation.minwords": "{min}자 이상을 입력하세요.",
"error.validation.more": "{min} 이상의 값을 입력하세요.",
"error.validation.notcontains":
"{needle}에 포함된 값은 입력할 수 없습니다.",
@@ -225,18 +225,18 @@
"error.validation.option": "올바른 옵션을 선택하세요.",
"error.validation.num": "올바른 숫자를 입력하세요.",
"error.validation.required": "아무거나 입력하세요.",
"error.validation.same": "{other}를(을) 입력하세요.",
"error.validation.size": "값은 {size}과(와) 같아야 합니다.",
"error.validation.startswith": "값은 {start}(으)로 시작해야 합니다.",
"error.validation.time": "올바른 시을 입력하세요.",
"error.validation.same": "다음을 입력하세요: {other}",
"error.validation.size": "값의 크기는 다음과 같아야 합니다: {size}",
"error.validation.startswith": "값은 다음으로 시작해야 합니다: {start}",
"error.validation.time": "올바른 시을 입력하세요.",
"error.validation.url": "올바른 URL을 입력하세요.",
"field.required": "필드가 필요합니다.",
"field.files.empty": "선택 파일이 없습니다.",
"field.pages.empty": "선택 페이지가 없습니다.",
"field.structure.delete.confirm": "이 을 삭제할까요?",
"field.required": "필드를 채우세요.",
"field.files.empty": "선택 파일이 없습니다.",
"field.pages.empty": "선택 페이지가 없습니다.",
"field.structure.delete.confirm": "이 항목을 삭제할까요?",
"field.structure.empty": "항목이 없습니다.",
"field.users.empty": "선택 사용자가 없습니다.",
"field.users.empty": "선택 사용자가 없습니다.",
"file.delete.confirm":
"<strong>파일({filename})</strong>을 삭제할까요?",
@@ -244,7 +244,7 @@
"files": "파일",
"files.empty": "파일이 없습니다.",
"hour": "시",
"hour": "시",
"insert": "\uc0bd\uc785",
"install": "설치",
@@ -263,14 +263,14 @@
"폴더(<code>/media</code>)에 쓰기 권한이 없습니다.",
"installation.issues.php": "<code>PHP</code> 버전이 7 이상인지 확인하세요.",
"installation.issues.server":
"<code>Apache</code>, <code>Nginx</code>, 또는 <code>Caddy</code>가 필요합니다.",
"Kirby를 실행하려면 <code>Apache</code>, <code>Nginx</code>, 또는 <code>Caddy</code>가 필요합니다.",
"installation.issues.sessions": "폴더(<code>/site/sessions</code>)에 쓰기 권한이 없습니다.",
"language": "\uc5b8\uc5b4",
"language.code": "언어 코드",
"language.convert": "기본 언어로 설정",
"language.convert.confirm":
"이 <strong>언어({name})</strong>를 기본 언어로 설정할까요? 설정한 뒤에는 복원할 수 없으며, 번역되지 않은 항목은 올바르게 표시되지 않을 수 있습니다.",
"이 <strong>언어({name})</strong>를 기본 언어로 설정할까요? 설정한 뒤에는 복원할 수 없으며, 이 언어로 번역되지 않은 항목은 올바르게 표시되지 않을 수 있습니다.",
"language.create": "새 언어 추가",
"language.delete.confirm":
"<strong>언어({name})</strong>를 삭제할까요? 삭제한 뒤에는 복원할 수 없습니다.",
@@ -279,7 +279,7 @@
"language.direction.ltr": "왼쪽에서 오른쪽",
"language.direction.rtl": "오른쪽에서 왼쪽",
"language.locale": "PHP 로캘 문자열",
"language.locale.warning": "커스텀 언어 설정를 사용 중입니다. /site/languages 폴더의 언어 파일을 수정하세요.",
"language.locale.warning": "사용자 지정 로캘을 사용 중입니다. 폴더(<code>/site/languages</code>)의 언어 파일을 수정하세요.",
"language.name": "이름",
"language.updated": "언어를 변경했습니다.",
@@ -301,15 +301,15 @@
"link": "\uc77c\ubc18 \ub9c1\ud06c",
"link.text": "\ubb38\uc790",
"loading": "로딩 중",
"loading": "로딩 중",
"lock.unsaved": "수정 사항이 저장되지 않았습니다.",
"lock.unsaved.empty": "저장되지 않은 페이지가 없습니다.",
"lock.unsaved": "저장되지 않은 수정 사항이 있습니다.",
"lock.unsaved.empty": "모든 페이지를 저장했습니다.",
"lock.isLocked": "<strong>다른 사용자({email})</strong>가 수정한 사항이 저장되지 않았습니다.",
"lock.file.isLocked": "다른 사용자({email})가 수정 중인 파일입니다.",
"lock.page.isLocked": "다른 사용자({email}가 수정 중인 페이지입니다.",
"lock.file.isLocked": "파일을 편집할 수 없습니다. 다른 사용자({email})가 편집 중입니다.",
"lock.page.isLocked": "페이지를 편집할 수 없습니다. 다른 사용자({email}가 편집 중입니다.",
"lock.unlock": "잠금",
"lock.isUnlocked": "다른 사용자가 이미 내용을 수정했으므로 현재 내용이 올바르게 저장되지 않았습니다. 저장되지 않은 내용 내려받아 수동으로 대치할 수 있습니다.",
"lock.isUnlocked": "다른 사용자가 이미 내용을 수정했으므로 현재 내용이 올바르게 저장되지 않았습니다. 저장되지 않은 내용 내려받아 수동으로 대치할 수 있습니다.",
"login": "\ub85c\uadf8\uc778",
"login.remember": "로그인 유지",
@@ -318,7 +318,7 @@
"menu": "메뉴",
"meridiem": "오전/오후",
"mime": "형식",
"mime": "미디어 형식",
"minutes": "분",
"month": "월",
@@ -349,7 +349,7 @@
"orientation.square": "정사각형",
"page.changeSlug": "고유 주소 변경",
"page.changeSlug.fromTitle": "\uc81c\ubaa9(\uc601\ubb38)\uc5d0\uc11c \uac00\uc838\uc624\uae30",
"page.changeSlug.fromTitle": "제목에서 가져오기",
"page.changeStatus": "상태 변경",
"page.changeStatus.position": "위치를 선택하세요.",
"page.changeStatus.select": "새 상태 선택",
@@ -359,7 +359,7 @@
"page.delete.confirm.subpages":
"<strong>페이지에 하위 페이지가 있습니다.</strong> 모든 하위 페이지가 삭제됩니다.",
"page.delete.confirm.title": "페이지 제목을 입력하세요.",
"page.draft.create": "초안 작성",
"page.draft.create": "초안 등록",
"page.duplicate.appendix": "복사",
"page.duplicate.files": "파일 복사",
"page.duplicate.pages": "페이지 복사",
@@ -392,7 +392,7 @@
"role": "역할",
"role.admin.description": "관리자는 모든 권한이 있습니다.",
"role.admin.title": "관리자",
"role.all": "전체 보기",
"role.all": "전체",
"role.empty": "이 역할에 해당하는 사용자가 없습니다.",
"role.description.placeholder": "설명이 없습니다.",
"role.nobody.description": "대체 사용자는 아무 권한이 없습니다.",
@@ -433,13 +433,13 @@
"translation.locale": "ko_KR",
"upload": "업로드",
"upload.error.cantMove": "업로드한 파일을 이동할 수 없습니다.",
"upload.error.cantMove": "파일을 이동할 수 없습니다.",
"upload.error.cantWrite": "디스크를 읽을 수 없습니다.",
"upload.error.default": "파일을 업로드할 수 없습니다.",
"upload.error.extension": "파일 확장자를 다시 한번 확인하세요.",
"upload.error.formSize": "허용된 크기를 초과해 파일을 업로드할 수 없습니다.",
"upload.error.iniPostSize": "허용된 크기를 초과해 파일을 업로드할 수 없습니다.",
"upload.error.iniSize": "허용된 크기를 초과해 파일을 업로드할 수 없습니다.",
"upload.error.extension": "파일 확장자를 확인하세요.",
"upload.error.formSize": "업로드한 파일이 허용된 크기(MAX_FILE_SIZE)를 초과했습니다.",
"upload.error.iniPostSize": "업로드한 파일이 허용된 크기(post_max_size)를 초과했습니다.",
"upload.error.iniSize": "업로드한 파일이 허용된 크기(upload_max_filesize)를 초과했습니다.",
"upload.error.noFile": "업로드한 파일이 없습니다.",
"upload.error.noFiles": "업로드한 파일이 없습니다.",
"upload.error.partial": "일부 파일만 업로드했습니다.",
@@ -452,7 +452,7 @@
"user": "사용자",
"user.blueprint":
"<strong>/site/blueprints/users/{role}.yml</strong> 파일에 섹션 및 폼 필드를 추가할 수 있습니다.",
"파일(<strong>/site/blueprints/users/{role}.yml</strong>)에 섹션 및 폼 필드를 추가할 수 있습니다.",
"user.changeEmail": "이메일 주소 변경",
"user.changeLanguage": "언어 변경",
"user.changeName": "사용자명 변경",
@@ -476,6 +476,6 @@
"view.site": "사이트",
"view.users": "\uc0ac\uc6a9\uc790",
"welcome": "환영합니다.",
"welcome": "안녕하세요?",
"year": "년"
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -18,6 +18,7 @@ class FileCache extends Cache
{
/**
* Full root including prefix
*
* @var string
*/
protected $root;
@@ -49,6 +50,16 @@ class FileCache extends Cache
Dir::make($this->root, true);
}
/**
* Returns the full root including prefix
*
* @return string
*/
public function root(): string
{
return $this->root;
}
/**
* Returns the full path to a file for a given key
*

View File

@@ -198,22 +198,16 @@ class Blueprint
return $props;
}
$mixin = static::find($extends);
if ($mixin === null) {
$props = $props;
} elseif (is_array($mixin) === true) {
try {
$mixin = static::find($extends);
$props = A::merge($mixin, $props, A::MERGE_REPLACE);
} else {
try {
$props = A::merge(Data::read($mixin), $props, A::MERGE_REPLACE);
} catch (Exception $e) {
$props = $props;
}
} catch (Exception $e) {
// keep the props unextended if the snippet wasn't found
}
// remove the extends flag
unset($props['extends']);
return $props;
}
@@ -268,22 +262,32 @@ class Blueprint
* Find a blueprint by name
*
* @param string $name
* @return string|array
* @return array
*/
public static function find(string $name)
public static function find(string $name): array
{
if (isset(static::$loaded[$name]) === true) {
return static::$loaded[$name];
}
$kirby = App::instance();
$root = $kirby->root('blueprints');
$file = $root . '/' . $name . '.yml';
if (F::exists($file, $root) === true) {
return $file;
// first try to find a site blueprint,
// then check in the plugin extensions
if (F::exists($file, $root) !== true) {
$file = $kirby->extension('blueprints', $name);
}
if ($blueprint = $kirby->extension('blueprints', $name)) {
return $blueprint;
// now ensure that we always return the data array
if (is_string($file) === true && F::exists($file) === true) {
return static::$loaded[$name] = Data::read($file);
} elseif (is_array($file) === true) {
return static::$loaded[$name] = $file;
}
// neither a valid file nor array data
throw new NotFoundException([
'key' => 'blueprint.notFound',
'data' => ['name' => $name]
@@ -320,13 +324,9 @@ class Blueprint
*/
public static function load(string $name): array
{
if (isset(static::$loaded[$name]) === true) {
return static::$loaded[$name];
}
$props = static::find($name);
$props = static::find($name);
$normalize = function ($props) use ($name) {
// inject the filename as name if no name is set
$props['name'] = $props['name'] ?? $name;
@@ -339,14 +339,7 @@ class Blueprint
return $props;
};
if (is_array($props) === true) {
return $normalize($props);
}
$file = $props;
$props = Data::read($file);
return static::$loaded[$name] = $normalize($props);
return $normalize($props);
}
/**

View File

@@ -40,6 +40,24 @@ class ContentLock
$this->data = $this->kirby()->locks()->get($model);
}
/**
* Clears the lock unconditionally
*
* @return bool
*/
protected function clearLock(): bool
{
// if no lock exists, skip
if (isset($this->data['lock']) === false) {
return true;
}
// remove lock
unset($this->data['lock']);
return $this->kirby()->locks()->set($this->model, $this->data);
}
/**
* Sets lock with the current user
*
@@ -74,19 +92,20 @@ class ContentLock
{
$data = $this->data['lock'] ?? [];
if (
empty($data) === false &&
$data['user'] !== $this->user()->id() &&
$user = $this->kirby()->user($data['user'])
) {
$time = (int)($data['time']);
if (empty($data) === false && $data['user'] !== $this->user()->id()) {
if ($user = $this->kirby()->user($data['user'])) {
$time = (int)($data['time']);
return [
'user' => $user->id(),
'email' => $user->email(),
'time' => $time,
'unlockable' => ($time + 60) <= time()
];
return [
'user' => $user->id(),
'email' => $user->email(),
'time' => $time,
'unlockable' => ($time + 60) <= time()
];
}
// clear lock if user not found
$this->clearLock();
}
return false;
@@ -150,10 +169,7 @@ class ContentLock
]);
}
// remove lock
unset($this->data['lock']);
return $this->kirby()->locks()->set($this->model, $this->data);
return $this->clearLock();
}
/**
@@ -193,10 +209,7 @@ class ContentLock
$this->data['unlock'] = $this->data['unlock'] ?? [];
$this->data['unlock'][] = $this->data['lock']['user'];
// remove lock
unset($this->data['lock']);
return $this->kirby()->locks()->set($this->model, $this->data);
return $this->clearLock();
}
/**

View File

@@ -364,7 +364,7 @@ class Language extends Model
return $this->code;
}
return Url::path($this->url());
return Url::path($this->url);
}
/**
@@ -636,9 +636,17 @@ class Language extends Model
*/
public function update(array $props = null)
{
// don't change the language code
unset($props['code']);
// make sure the slug is nice and clean
$props['slug'] = Str::slug($props['slug'] ?? null);
$kirby = App::instance();
$updated = $this->clone($props);
$kirby = App::instance();
$updated = $this->clone($props);
// validate the updated language
LanguageRules::update($updated);
// convert the current default to a non-default language
if ($updated->isDefault() === true) {

View File

@@ -19,25 +19,8 @@ class LanguageRules
{
public static function create(Language $language): bool
{
if (Str::length($language->code()) < 2) {
throw new InvalidArgumentException([
'key' => 'language.code',
'data' => [
'code' => $language->code(),
'name' => $language->name()
]
]);
}
if (Str::length($language->name()) < 1) {
throw new InvalidArgumentException([
'key' => 'language.name',
'data' => [
'code' => $language->code(),
'name' => $language->name()
]
]);
}
static::validLanguageCode($language);
static::validLanguageName($language);
if ($language->exists() === true) {
throw new DuplicateException([
@@ -50,4 +33,40 @@ class LanguageRules
return true;
}
public static function update(Language $language)
{
static::validLanguageCode($language);
static::validLanguageName($language);
}
public static function validLanguageCode(Language $language): bool
{
if (Str::length($language->code()) < 2) {
throw new InvalidArgumentException([
'key' => 'language.code',
'data' => [
'code' => $language->code(),
'name' => $language->name()
]
]);
}
return true;
}
public static function validLanguageName(Language $language): bool
{
if (Str::length($language->name()) < 1) {
throw new InvalidArgumentException([
'key' => 'language.name',
'data' => [
'code' => $language->code(),
'name' => $language->name()
]
]);
}
return true;
}
}

View File

@@ -56,7 +56,7 @@ class Search
$collection = clone $collection;
$searchwords = preg_replace('/(\s)/u', ',', $query);
$searchwords = Str::split($searchwords, ',', $options['minlength']);
$lowerQuery = strtolower($query);
$lowerQuery = mb_strtolower($query);
if (empty($options['stopwords']) === false) {
$searchwords = array_diff($searchwords, $options['stopwords']);
@@ -96,7 +96,7 @@ class Search
$score = $options['score'][$key] ?? 1;
$value = $data[$key] ?? (string)$item->$key();
$lowerValue = strtolower($value);
$lowerValue = mb_strtolower($value);
// check for exact matches
if ($lowerQuery == $lowerValue) {

View File

@@ -361,7 +361,7 @@ class Str
return $string;
}
return static::substr($string, 0, strrpos(static::substr($string, 0, $chars), ' ')) . ' ' . $rep;
return static::substr($string, 0, mb_strrpos(static::substr($string, 0, $chars), ' ')) . ' ' . $rep;
}
/**
@@ -373,6 +373,11 @@ class Str
*/
public static function float($value): string
{
// Convert exponential to decimal, 1e-8 as 0.00000001
if (strpos(strtolower($value), 'e') !== false) {
$value = rtrim(sprintf('%.16f', (float)$value), '0');
}
$value = str_replace(',', '.', $value);
$decimal = strlen(substr(strrchr($value, '.'), 1));
return number_format((float)$value, $decimal, '.', false);

View File

@@ -545,7 +545,14 @@ final class Mbstring
}
if ('UTF-8' === $encoding = self::getEncoding($encoding)) {
return preg_split("/(.{{$split_length}})/u", $string, null, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
$rx = '/(';
while (65535 < $split_length) {
$rx .= '.{65535}';
$split_length -= 65535;
}
$rx .= '.{'.$split_length.'})/us';
return preg_split($rx, $string, null, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
}
$result = array();

View File

@@ -1,6 +1,9 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Error</title>
<style>