Update Kirby and dependencies

This commit is contained in:
Paul Nicoué 2022-08-31 15:02:43 +02:00
parent 503b339974
commit 399fa20902
439 changed files with 66915 additions and 64442 deletions

2301
composer.lock generated

File diff suppressed because it is too large Load diff

View file

@ -6,10 +6,19 @@
root = true root = true
[*.php] [*]
charset = utf-8 charset = utf-8
end_of_line = lf end_of_line = lf
insert_final_newline = true indent_style = tab
indent_size = 2
trim_trailing_whitespace = true trim_trailing_whitespace = true
[*.php]
indent_size = 4
insert_final_newline = true
[*.yml]
indent_style = space indent_style = space
indent_size = 4
[*.md]
trim_trailing_whitespace = false

View file

@ -5,31 +5,28 @@
* stop at older or too recent versions * stop at older or too recent versions
*/ */
if ( if (
version_compare(PHP_VERSION, '7.4.0', '>=') === false || version_compare(PHP_VERSION, '7.4.0', '>=') === false ||
version_compare(PHP_VERSION, '8.2.0', '<') === false version_compare(PHP_VERSION, '8.2.0', '<') === false
) { ) {
die(include __DIR__ . '/views/php.php'); die(include __DIR__ . '/views/php.php');
} }
if (is_file($autoloader = dirname(__DIR__) . '/vendor/autoload.php')) { if (is_file($autoloader = dirname(__DIR__) . '/vendor/autoload.php')) {
/**
/** * Always prefer a site-wide Composer autoloader
* Always prefer a site-wide Composer autoloader * if it exists, it means that the user has probably
* if it exists, it means that the user has probably * installed additional packages
* installed additional packages */
*/ include $autoloader;
include $autoloader;
} elseif (is_file($autoloader = __DIR__ . '/vendor/autoload.php')) { } elseif (is_file($autoloader = __DIR__ . '/vendor/autoload.php')) {
/**
/** * Fall back to the local autoloader if that exists
* Fall back to the local autoloader if that exists */
*/ include $autoloader;
include $autoloader;
} else { } else {
/**
/** * If neither one exists, don't bother searching;
* If neither one exists, don't bother searching; * it's a custom directory setup and the users need to
* it's a custom directory setup and the users need to * load the autoloader themselves
* load the autoloader themselves */
*/
} }

View file

@ -1,7 +1,7 @@
## ##
## Bundle of CA Root Certificates ## Bundle of CA Root Certificates
## ##
## Certificate data from Mozilla as of: Tue Apr 26 03:12:05 2022 GMT ## Certificate data from Mozilla as of: Tue Jul 19 03:12:06 2022 GMT
## ##
## This is a bundle of X.509 certificates of public Certificate Authorities ## This is a bundle of X.509 certificates of public Certificate Authorities
## (CA). These were automatically extracted from Mozilla's root certificates ## (CA). These were automatically extracted from Mozilla's root certificates
@ -14,7 +14,7 @@
## Just configure this file as the SSLCACertificateFile. ## Just configure this file as the SSLCACertificateFile.
## ##
## Conversion done with mk-ca-bundle.pl version 1.29. ## Conversion done with mk-ca-bundle.pl version 1.29.
## SHA256: 34a54d5191775c1bd37be6cfd3f09e831e072555dc3a2e51f4a2c4b0f8ada5cc ## SHA256: 9bf3799611fb58197f61d45e71ce3dc19f30e7dd73731915872ce5108a7bb066
## ##
@ -993,30 +993,6 @@ tnRGEmyR7jTV7JqR50S+kDFy1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29
mvVXIwAHIRc/SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03 mvVXIwAHIRc/SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03
-----END CERTIFICATE----- -----END CERTIFICATE-----
Hellenic Academic and Research Institutions RootCA 2011
=======================================================
-----BEGIN CERTIFICATE-----
MIIEMTCCAxmgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBlTELMAkGA1UEBhMCR1IxRDBCBgNVBAoT
O0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9y
aXR5MUAwPgYDVQQDEzdIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25z
IFJvb3RDQSAyMDExMB4XDTExMTIwNjEzNDk1MloXDTMxMTIwMTEzNDk1MlowgZUxCzAJBgNVBAYT
AkdSMUQwQgYDVQQKEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25z
IENlcnQuIEF1dGhvcml0eTFAMD4GA1UEAxM3SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNo
IEluc3RpdHV0aW9ucyBSb290Q0EgMjAxMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
AKlTAOMupvaO+mDYLZU++CwqVE7NuYRhlFhPjz2L5EPzdYmNUeTDN9KKiE15HrcS3UN4SoqS5tdI
1Q+kOilENbgH9mgdVc04UfCMJDGFr4PJfel3r+0ae50X+bOdOFAPplp5kYCvN66m0zH7tSYJnTxa
71HFK9+WXesyHgLacEnsbgzImjeN9/E2YEsmLIKe0HjzDQ9jpFEw4fkrJxIH2Oq9GGKYsFk3fb7u
8yBRQlqD75O6aRXxYp2fmTmCobd0LovUxQt7L/DICto9eQqakxylKHJzkUOap9FNhYS5qXSPFEDH
3N6sQWRstBmbAmNtJGSPRLIl6s5ddAxjMlyNh+UCAwEAAaOBiTCBhjAPBgNVHRMBAf8EBTADAQH/
MAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQUppFC/RNhSiOeCKQp5dgTBCPuQSUwRwYDVR0eBEAwPqA8
MAWCAy5ncjAFggMuZXUwBoIELmVkdTAGggQub3JnMAWBAy5ncjAFgQMuZXUwBoEELmVkdTAGgQQu
b3JnMA0GCSqGSIb3DQEBBQUAA4IBAQAf73lB4XtuP7KMhjdCSk4cNx6NZrokgclPEg8hwAOXhiVt
XdMiKahsog2p6z0GW5k6x8zDmjR/qw7IThzh+uTczQ2+vyT+bOdrwg3IBp5OjWEopmr95fZi6hg8
TqBTnbI6nOulnJEWtk2C4AwFSKls9cz4y51JtPACpf1wA+2KIaWuE4ZJwzNzvoc7dIsXRSZMFpGD
/md9zU1jZ/rzAxKWeAaNsWftjj++n08C9bMJL/NMh98qy5V8AcysNnq/onN694/BtZqhFLKPM58N
7yLcZnuEvUUXBj08yrl3NI/K6s8/MT7jiOOASSXIl7WdmplNsDz4SgCbZN2fOUvRJ9e4
-----END CERTIFICATE-----
Actalis Authentication Root CA Actalis Authentication Root CA
============================== ==============================
-----BEGIN CERTIFICATE----- -----BEGIN CERTIFICATE-----
@ -3345,3 +3321,140 @@ PUQtVHJ1c3QlMjBHbWJILEM9REU/Y2VydGlmaWNhdGVyZXZvY2F0aW9ubGlzdDAKBggqhkjOPQQD
AwNpADBmAjEAyjzGKnXCXnViOTYAYFqLwZOZzNnbQTs7h5kXO9XMT8oi96CAy/m0sRtW9XLS/BnR AwNpADBmAjEAyjzGKnXCXnViOTYAYFqLwZOZzNnbQTs7h5kXO9XMT8oi96CAy/m0sRtW9XLS/BnR
AjEAkfcwkz8QRitxpNA7RJvAKQIFskF3UfN5Wp6OFKBOQtJbgfM0agPnIjhQW+0ZT0MW AjEAkfcwkz8QRitxpNA7RJvAKQIFskF3UfN5Wp6OFKBOQtJbgfM0agPnIjhQW+0ZT0MW
-----END CERTIFICATE----- -----END CERTIFICATE-----
DigiCert TLS ECC P384 Root G5
=============================
-----BEGIN CERTIFICATE-----
MIICGTCCAZ+gAwIBAgIQCeCTZaz32ci5PhwLBCou8zAKBggqhkjOPQQDAzBOMQswCQYDVQQGEwJV
UzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJjAkBgNVBAMTHURpZ2lDZXJ0IFRMUyBFQ0MgUDM4
NCBSb290IEc1MB4XDTIxMDExNTAwMDAwMFoXDTQ2MDExNDIzNTk1OVowTjELMAkGA1UEBhMCVVMx
FzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMSYwJAYDVQQDEx1EaWdpQ2VydCBUTFMgRUNDIFAzODQg
Um9vdCBHNTB2MBAGByqGSM49AgEGBSuBBAAiA2IABMFEoc8Rl1Ca3iOCNQfN0MsYndLxf3c1Tzvd
lHJS7cI7+Oz6e2tYIOyZrsn8aLN1udsJ7MgT9U7GCh1mMEy7H0cKPGEQQil8pQgO4CLp0zVozptj
n4S1mU1YoI71VOeVyaNCMEAwHQYDVR0OBBYEFMFRRVBZqz7nLFr6ICISB4CIfBFqMA4GA1UdDwEB
/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMDA2gAMGUCMQCJao1H5+z8blUD2Wds
Jk6Dxv3J+ysTvLd6jLRl0mlpYxNjOyZQLgGheQaRnUi/wr4CMEfDFXuxoJGZSZOoPHzoRgaLLPIx
AJSdYsiJvRmEFOml+wG4DXZDjC5Ty3zfDBeWUA==
-----END CERTIFICATE-----
DigiCert TLS RSA4096 Root G5
============================
-----BEGIN CERTIFICATE-----
MIIFZjCCA06gAwIBAgIQCPm0eKj6ftpqMzeJ3nzPijANBgkqhkiG9w0BAQwFADBNMQswCQYDVQQG
EwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJTAjBgNVBAMTHERpZ2lDZXJ0IFRMUyBSU0E0
MDk2IFJvb3QgRzUwHhcNMjEwMTE1MDAwMDAwWhcNNDYwMTE0MjM1OTU5WjBNMQswCQYDVQQGEwJV
UzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJTAjBgNVBAMTHERpZ2lDZXJ0IFRMUyBSU0E0MDk2
IFJvb3QgRzUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCz0PTJeRGd/fxmgefM1eS8
7IE+ajWOLrfn3q/5B03PMJ3qCQuZvWxX2hhKuHisOjmopkisLnLlvevxGs3npAOpPxG02C+JFvuU
AT27L/gTBaF4HI4o4EXgg/RZG5Wzrn4DReW+wkL+7vI8toUTmDKdFqgpwgscONyfMXdcvyej/Ces
tyu9dJsXLfKB2l2w4SMXPohKEiPQ6s+d3gMXsUJKoBZMpG2T6T867jp8nVid9E6P/DsjyG244gXa
zOvswzH016cpVIDPRFtMbzCe88zdH5RDnU1/cHAN1DrRN/BsnZvAFJNY781BOHW8EwOVfH/jXOnV
DdXifBBiqmvwPXbzP6PosMH976pXTayGpxi0KcEsDr9kvimM2AItzVwv8n/vFfQMFawKsPHTDU9q
TXeXAaDxZre3zu/O7Oyldcqs4+Fj97ihBMi8ez9dLRYiVu1ISf6nL3kwJZu6ay0/nTvEF+cdLvvy
z6b84xQslpghjLSR6Rlgg/IwKwZzUNWYOwbpx4oMYIwo+FKbbuH2TbsGJJvXKyY//SovcfXWJL5/
MZ4PbeiPT02jP/816t9JXkGPhvnxd3lLG7SjXi/7RgLQZhNeXoVPzthwiHvOAbWWl9fNff2C+MIk
wcoBOU+NosEUQB+cZtUMCUbW8tDRSHZWOkPLtgoRObqME2wGtZ7P6wIDAQABo0IwQDAdBgNVHQ4E
FgQUUTMc7TZArxfTJc1paPKvTiM+s0EwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8w
DQYJKoZIhvcNAQEMBQADggIBAGCmr1tfV9qJ20tQqcQjNSH/0GEwhJG3PxDPJY7Jv0Y02cEhJhxw
GXIeo8mH/qlDZJY6yFMECrZBu8RHANmfGBg7sg7zNOok992vIGCukihfNudd5N7HPNtQOa27PShN
lnx2xlv0wdsUpasZYgcYQF+Xkdycx6u1UQ3maVNVzDl92sURVXLFO4uJ+DQtpBflF+aZfTCIITfN
MBc9uPK8qHWgQ9w+iUuQrm0D4ByjoJYJu32jtyoQREtGBzRj7TG5BO6jm5qu5jF49OokYTurWGT/
u4cnYiWB39yhL/btp/96j1EuMPikAdKFOV8BmZZvWltwGUb+hmA+rYAQCd05JS9Yf7vSdPD3Rh9G
OUrYU9DzLjtxpdRv/PNn5AeP3SYZ4Y1b+qOTEZvpyDrDVWiakuFSdjjo4bq9+0/V77PnSIMx8IIh
47a+p6tv75/fTM8BuGJqIz3nCU2AG3swpMPdB380vqQmsvZB6Akd4yCYqjdP//fx4ilwMUc/dNAU
FvohigLVigmUdy7yWSiLfFCSCmZ4OIN1xLVaqBHG5cGdZlXPU8Sv13WFqUITVuwhd4GTWgzqltlJ
yqEI8pc7bZsEGCREjnwB8twl2F6GmrE52/WRMmrRpnCKovfepEWFJqgejF0pW8hL2JpqA15w8oVP
bEtoL8pU9ozaMv7Da4M/OMZ+
-----END CERTIFICATE-----
Certainly Root R1
=================
-----BEGIN CERTIFICATE-----
MIIFRzCCAy+gAwIBAgIRAI4P+UuQcWhlM1T01EQ5t+AwDQYJKoZIhvcNAQELBQAwPTELMAkGA1UE
BhMCVVMxEjAQBgNVBAoTCUNlcnRhaW5seTEaMBgGA1UEAxMRQ2VydGFpbmx5IFJvb3QgUjEwHhcN
MjEwNDAxMDAwMDAwWhcNNDYwNDAxMDAwMDAwWjA9MQswCQYDVQQGEwJVUzESMBAGA1UEChMJQ2Vy
dGFpbmx5MRowGAYDVQQDExFDZXJ0YWlubHkgUm9vdCBSMTCCAiIwDQYJKoZIhvcNAQEBBQADggIP
ADCCAgoCggIBANA21B/q3avk0bbm+yLA3RMNansiExyXPGhjZjKcA7WNpIGD2ngwEc/csiu+kr+O
5MQTvqRoTNoCaBZ0vrLdBORrKt03H2As2/X3oXyVtwxwhi7xOu9S98zTm/mLvg7fMbedaFySpvXl
8wo0tf97ouSHocavFwDvA5HtqRxOcT3Si2yJ9HiG5mpJoM610rCrm/b01C7jcvk2xusVtyWMOvwl
DbMicyF0yEqWYZL1LwsYpfSt4u5BvQF5+paMjRcCMLT5r3gajLQ2EBAHBXDQ9DGQilHFhiZ5shGI
XsXwClTNSaa/ApzSRKft43jvRl5tcdF5cBxGX1HpyTfcX35pe0HfNEXgO4T0oYoKNp43zGJS4YkN
KPl6I7ENPT2a/Z2B7yyQwHtETrtJ4A5KVpK8y7XdeReJkd5hiXSSqOMyhb5OhaRLWcsrxXiOcVTQ
AjeZjOVJ6uBUcqQRBi8LjMFbvrWhsFNunLhgkR9Za/kt9JQKl7XsxXYDVBtlUrpMklZRNaBA2Cnb
rlJ2Oy0wQJuK0EJWtLeIAaSHO1OWzaMWj/Nmqhexx2DgwUMFDO6bW2BvBlyHWyf5QBGenDPBt+U1
VwV/J84XIIwc/PH72jEpSe31C4SnT8H2TsIonPru4K8H+zMReiFPCyEQtkA6qyI6BJyLm4SGcprS
p6XEtHWRqSsjAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud
DgQWBBTgqj8ljZ9EXME66C6ud0yEPmcM9DANBgkqhkiG9w0BAQsFAAOCAgEAuVevuBLaV4OPaAsz
HQNTVfSVcOQrPbA56/qJYv331hgELyE03fFo8NWWWt7CgKPBjcZq91l3rhVkz1t5BXdm6ozTaw3d
8VkswTOlMIAVRQdFGjEitpIAq5lNOo93r6kiyi9jyhXWx8bwPWz8HA2YEGGeEaIi1wrykXprOQ4v
MMM2SZ/g6Q8CRFA3lFV96p/2O7qUpUzpvD5RtOjKkjZUbVwlKNrdrRT90+7iIgXr0PK3aBLXWopB
GsaSpVo7Y0VPv+E6dyIvXL9G+VoDhRNCX8reU9ditaY1BMJH/5n9hN9czulegChB8n3nHpDYT3Y+
gjwN/KUD+nsa2UUeYNrEjvn8K8l7lcUq/6qJ34IxD3L/DCfXCh5WAFAeDJDBlrXYFIW7pw0WwfgH
JBu6haEaBQmAupVjyTrsJZ9/nbqkRxWbRHDxakvWOF5D8xh+UG7pWijmZeZ3Gzr9Hb4DJqPb1OG7
fpYnKx3upPvaJVQTA945xsMfTZDsjxtK0hzthZU4UHlG1sGQUDGpXJpuHfUzVounmdLyyCwzk5Iw
x06MZTMQZBf9JBeW0Y3COmor6xOLRPIh80oat3df1+2IpHLlOR+Vnb5nwXARPbv0+Em34yaXOp/S
X3z7wJl8OSngex2/DaeP0ik0biQVy96QXr8axGbqwua6OV+KmalBWQewLK8=
-----END CERTIFICATE-----
Certainly Root E1
=================
-----BEGIN CERTIFICATE-----
MIIB9zCCAX2gAwIBAgIQBiUzsUcDMydc+Y2aub/M+DAKBggqhkjOPQQDAzA9MQswCQYDVQQGEwJV
UzESMBAGA1UEChMJQ2VydGFpbmx5MRowGAYDVQQDExFDZXJ0YWlubHkgUm9vdCBFMTAeFw0yMTA0
MDEwMDAwMDBaFw00NjA0MDEwMDAwMDBaMD0xCzAJBgNVBAYTAlVTMRIwEAYDVQQKEwlDZXJ0YWlu
bHkxGjAYBgNVBAMTEUNlcnRhaW5seSBSb290IEUxMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE3m/4
fxzf7flHh4axpMCK+IKXgOqPyEpeKn2IaKcBYhSRJHpcnqMXfYqGITQYUBsQ3tA3SybHGWCA6TS9
YBk2QNYphwk8kXr2vBMj3VlOBF7PyAIcGFPBMdjaIOlEjeR2o0IwQDAOBgNVHQ8BAf8EBAMCAQYw
DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU8ygYy2R17ikq6+2uI1g4hevIIgcwCgYIKoZIzj0E
AwMDaAAwZQIxALGOWiDDshliTd6wT99u0nCK8Z9+aozmut6Dacpps6kFtZaSF4fC0urQe87YQVt8
rgIwRt7qy12a7DLCZRawTDBcMPPaTnOGBtjOiQRINzf43TNRnXCve1XYAS59BWQOhriR
-----END CERTIFICATE-----
E-Tugra Global Root CA RSA v3
=============================
-----BEGIN CERTIFICATE-----
MIIF8zCCA9ugAwIBAgIUDU3FzRYilZYIfrgLfxUGNPt5EDQwDQYJKoZIhvcNAQELBQAwgYAxCzAJ
BgNVBAYTAlRSMQ8wDQYDVQQHEwZBbmthcmExGTAXBgNVBAoTEEUtVHVncmEgRUJHIEEuUy4xHTAb
BgNVBAsTFEUtVHVncmEgVHJ1c3QgQ2VudGVyMSYwJAYDVQQDEx1FLVR1Z3JhIEdsb2JhbCBSb290
IENBIFJTQSB2MzAeFw0yMDAzMTgwOTA3MTdaFw00NTAzMTIwOTA3MTdaMIGAMQswCQYDVQQGEwJU
UjEPMA0GA1UEBxMGQW5rYXJhMRkwFwYDVQQKExBFLVR1Z3JhIEVCRyBBLlMuMR0wGwYDVQQLExRF
LVR1Z3JhIFRydXN0IENlbnRlcjEmMCQGA1UEAxMdRS1UdWdyYSBHbG9iYWwgUm9vdCBDQSBSU0Eg
djMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCiZvCJt3J77gnJY9LTQ91ew6aEOErx
jYG7FL1H6EAX8z3DeEVypi6Q3po61CBxyryfHUuXCscxuj7X/iWpKo429NEvx7epXTPcMHD4QGxL
sqYxYdE0PD0xesevxKenhOGXpOhL9hd87jwH7eKKV9y2+/hDJVDqJ4GohryPUkqWOmAalrv9c/SF
/YP9f4RtNGx/ardLAQO/rWm31zLZ9Vdq6YaCPqVmMbMWPcLzJmAy01IesGykNz709a/r4d+ABs8q
QedmCeFLl+d3vSFtKbZnwy1+7dZ5ZdHPOrbRsV5WYVB6Ws5OUDGAA5hH5+QYfERaxqSzO8bGwzrw
bMOLyKSRBfP12baqBqG3q+Sx6iEUXIOk/P+2UNOMEiaZdnDpwA+mdPy70Bt4znKS4iicvObpCdg6
04nmvi533wEKb5b25Y08TVJ2Glbhc34XrD2tbKNSEhhw5oBOM/J+JjKsBY04pOZ2PJ8QaQ5tndLB
eSBrW88zjdGUdjXnXVXHt6woq0bM5zshtQoK5EpZ3IE1S0SVEgpnpaH/WwAH0sDM+T/8nzPyAPiM
bIedBi3x7+PmBvrFZhNb/FAHnnGGstpvdDDPk1Po3CLW3iAfYY2jLqN4MpBs3KwytQXk9TwzDdbg
h3cXTJ2w2AmoDVf3RIXwyAS+XF1a4xeOVGNpf0l0ZAWMowIDAQABo2MwYTAPBgNVHRMBAf8EBTAD
AQH/MB8GA1UdIwQYMBaAFLK0ruYt9ybVqnUtdkvAG1Mh0EjvMB0GA1UdDgQWBBSytK7mLfcm1ap1
LXZLwBtTIdBI7zAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQELBQADggIBAImocn+M684uGMQQ
gC0QDP/7FM0E4BQ8Tpr7nym/Ip5XuYJzEmMmtcyQ6dIqKe6cLcwsmb5FJ+Sxce3kOJUxQfJ9emN4
38o2Fi+CiJ+8EUdPdk3ILY7r3y18Tjvarvbj2l0Upq7ohUSdBm6O++96SmotKygY/r+QLHUWnw/q
ln0F7psTpURs+APQ3SPh/QMSEgj0GDSz4DcLdxEBSL9htLX4GdnLTeqjjO/98Aa1bZL0SmFQhO3s
SdPkvmjmLuMxC1QLGpLWgti2omU8ZgT5Vdps+9u1FGZNlIM7zR6mK7L+d0CGq+ffCsn99t2HVhjY
sCxVYJb6CH5SkPVLpi6HfMsg2wY+oF0Dd32iPBMbKaITVaA9FCKvb7jQmhty3QUBjYZgv6Rn7rWl
DdF/5horYmbDB7rnoEgcOMPpRfunf/ztAmgayncSd6YAVSgU7NbHEqIbZULpkejLPoeJVF3Zr52X
nGnnCv8PWniLYypMfUeUP95L6VPQMPHF9p5J3zugkaOj/s1YzOrfr28oO6Bpm4/srK4rVJ2bBLFH
IK+WEj5jlB0E5y67hscMmoi/dkfv97ALl2bSRM9gUgfh1SxKOidhd8rXj+eHDjD/DLsE4mHDosiX
YY60MGo8bcIHX0pzLz/5FooBZu+6kcpSV3uu1OYP3Qt6f4ueJiDPO++BcYNZ
-----END CERTIFICATE-----
E-Tugra Global Root CA ECC v3
=============================
-----BEGIN CERTIFICATE-----
MIICpTCCAiqgAwIBAgIUJkYZdzHhT28oNt45UYbm1JeIIsEwCgYIKoZIzj0EAwMwgYAxCzAJBgNV
BAYTAlRSMQ8wDQYDVQQHEwZBbmthcmExGTAXBgNVBAoTEEUtVHVncmEgRUJHIEEuUy4xHTAbBgNV
BAsTFEUtVHVncmEgVHJ1c3QgQ2VudGVyMSYwJAYDVQQDEx1FLVR1Z3JhIEdsb2JhbCBSb290IENB
IEVDQyB2MzAeFw0yMDAzMTgwOTQ2NThaFw00NTAzMTIwOTQ2NThaMIGAMQswCQYDVQQGEwJUUjEP
MA0GA1UEBxMGQW5rYXJhMRkwFwYDVQQKExBFLVR1Z3JhIEVCRyBBLlMuMR0wGwYDVQQLExRFLVR1
Z3JhIFRydXN0IENlbnRlcjEmMCQGA1UEAxMdRS1UdWdyYSBHbG9iYWwgUm9vdCBDQSBFQ0MgdjMw
djAQBgcqhkjOPQIBBgUrgQQAIgNiAASOmCm/xxAeJ9urA8woLNheSBkQKczLWYHMjLiSF4mDKpL2
w6QdTGLVn9agRtwcvHbB40fQWxPa56WzZkjnIZpKT4YKfWzqTTKACrJ6CZtpS5iB4i7sAnCWH/31
Rs7K3IKjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU/4Ixcj75xGZsrTie0bBRiKWQ
zPUwHQYDVR0OBBYEFP+CMXI++cRmbK04ntGwUYilkMz1MA4GA1UdDwEB/wQEAwIBBjAKBggqhkjO
PQQDAwNpADBmAjEA5gVYaWHlLcoNy/EZCL3W/VGSGn5jVASQkZo1kTmZ+gepZpO6yGjUij/67W4W
Aie3AjEA3VoXK3YdZUKWpqxdinlW2Iob35reX8dQj7FbcQwm32pAAOwzkSFxvmjkI6TZraE3
-----END CERTIFICATE-----

View file

@ -1,90 +1,110 @@
{ {
"name": "getkirby/cms", "name": "getkirby/cms",
"description": "The Kirby 3 core", "description": "The Kirby 3 core",
"license": "proprietary", "license": "proprietary",
"type": "kirby-cms", "type": "kirby-cms",
"version": "3.6.6", "version": "3.7.5",
"keywords": [ "keywords": [
"kirby", "kirby",
"cms", "cms",
"core" "core"
], ],
"authors": [ "authors": [
{ {
"name": "Kirby Team", "name": "Kirby Team",
"email": "support@getkirby.com", "email": "support@getkirby.com",
"homepage": "https://getkirby.com" "homepage": "https://getkirby.com"
} }
], ],
"homepage": "https://getkirby.com", "homepage": "https://getkirby.com",
"support": { "support": {
"email": "support@getkirby.com", "email": "support@getkirby.com",
"issues": "https://github.com/getkirby/kirby/issues", "issues": "https://github.com/getkirby/kirby/issues",
"forum": "https://forum.getkirby.com", "forum": "https://forum.getkirby.com",
"source": "https://github.com/getkirby/kirby" "source": "https://github.com/getkirby/kirby"
}, },
"_comment": "TODO: psr/log is not used by Kirby; drop pinned version when Kirby no longer supports PHP 7", "require": {
"require": { "php": ">=7.4.0 <8.2.0",
"php": ">=7.4.0 <8.2.0", "ext-SimpleXML": "*",
"ext-ctype": "*", "ext-ctype": "*",
"ext-mbstring": "*", "ext-curl": "*",
"claviska/simpleimage": "3.6.5", "ext-dom": "*",
"filp/whoops": "2.14.5", "ext-filter": "*",
"getkirby/composer-installer": "^1.2.1", "ext-hash": "*",
"laminas/laminas-escaper": "2.10.0", "ext-iconv": "*",
"michelf/php-smartypants": "1.8.1", "ext-json": "*",
"phpmailer/phpmailer": "6.5.4", "ext-libxml": "*",
"psr/log": "1.1.4", "ext-mbstring": "*",
"symfony/polyfill-intl-idn": "1.25.0", "ext-openssl": "*",
"symfony/polyfill-mbstring": "1.25.0" "claviska/simpleimage": "3.7.0",
}, "filp/whoops": "2.14.5",
"replace": { "getkirby/composer-installer": "^1.2.1",
"symfony/polyfill-php72": "*" "laminas/laminas-escaper": "2.10.0",
}, "michelf/php-smartypants": "1.8.1",
"autoload": { "phpmailer/phpmailer": "6.6.4",
"psr-4": { "symfony/polyfill-intl-idn": "1.26.0",
"Kirby\\": "src/" "symfony/polyfill-mbstring": "1.26.0"
}, },
"classmap": [ "replace": {
"dependencies/" "symfony/polyfill-php72": "*"
], },
"files": [ "suggest": {
"config/setup.php", "ext-PDO": "Support for using databases",
"config/helpers.php" "ext-apcu": "Support for the Apcu cache driver",
] "ext-exif": "Support for exif information from images",
}, "ext-fileinfo": "Improved mime type detection for files",
"config": { "ext-intl": "Improved i18n number formatting",
"allow-plugins": { "ext-memcached": "Support for the Memcached cache driver",
"getkirby/composer-installer": true "ext-zip": "Support for ZIP archive file functions",
}, "ext-zlib": "Sanitization and validation for svgz files"
"optimize-autoloader": true, },
"platform-check": false "autoload": {
}, "psr-4": {
"extra": { "Kirby\\": "src/"
"unused": [ },
"symfony/polyfill-intl-idn" "classmap": [
] "dependencies/"
}, ],
"scripts": { "files": [
"post-update-cmd": "curl -o cacert.pem https://curl.se/ca/cacert.pem", "config/setup.php",
"analyze": [ "config/helpers.php"
"@analyze:composer", ]
"@analyze:psalm", },
"@analyze:phpcpd", "config": {
"@analyze:phpmd" "allow-plugins": {
], "getkirby/composer-installer": true
"analyze:composer": "composer validate --strict --no-check-version --no-check-all", },
"analyze:phpcpd": "phpcpd --fuzzy --exclude tests --exclude vendor .", "optimize-autoloader": true,
"analyze:phpmd": "phpmd . ansi phpmd.xml.dist --exclude 'dependencies/*,tests/*,vendor/*'", "platform": {
"analyze:psalm": "psalm", "php": "7.4.0"
"build": "./scripts/build", },
"ci": [ "platform-check": false
"@fix", },
"@analyze", "extra": {
"@test" "unused": [
], "symfony/polyfill-intl-idn"
"fix": "php-cs-fixer fix", ]
"test": "phpunit --stderr --coverage-html=tests/coverage", },
"zip": "composer archive --format=zip --file=dist" "scripts": {
} "post-update-cmd": "curl -o cacert.pem https://curl.se/ca/cacert.pem",
"analyze": [
"@analyze:composer",
"@analyze:psalm",
"@analyze:phpcpd",
"@analyze:phpmd"
],
"analyze:composer": "composer validate --strict --no-check-version --no-check-all",
"analyze:phpcpd": "phpcpd --fuzzy --exclude tests --exclude vendor .",
"analyze:phpmd": "phpmd . ansi phpmd.xml.dist --exclude 'dependencies/*,tests/*,vendor/*'",
"analyze:psalm": "psalm",
"build": "./scripts/build",
"ci": [
"@fix",
"@analyze",
"@test"
],
"fix": "php-cs-fixer fix",
"test": "phpunit --stderr --coverage-html=tests/coverage",
"zip": "composer archive --format=zip --file=dist"
}
} }

88
kirby/composer.lock generated
View file

@ -4,20 +4,20 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "cb6bffc372828b6d36107d104c0b2a3e", "content-hash": "fb087946fb5ac5910e25a4d263905d99",
"packages": [ "packages": [
{ {
"name": "claviska/simpleimage", "name": "claviska/simpleimage",
"version": "3.6.5", "version": "3.7.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/claviska/SimpleImage.git", "url": "https://github.com/claviska/SimpleImage.git",
"reference": "00f90662686696b9b7157dbb176183aabe89700f" "reference": "abd15ced313c7b8041d7d73d8d2398b4f2510cf1"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/claviska/SimpleImage/zipball/00f90662686696b9b7157dbb176183aabe89700f", "url": "https://api.github.com/repos/claviska/SimpleImage/zipball/abd15ced313c7b8041d7d73d8d2398b4f2510cf1",
"reference": "00f90662686696b9b7157dbb176183aabe89700f", "reference": "abd15ced313c7b8041d7d73d8d2398b4f2510cf1",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -45,7 +45,7 @@
"description": "A PHP class that makes working with images as simple as possible.", "description": "A PHP class that makes working with images as simple as possible.",
"support": { "support": {
"issues": "https://github.com/claviska/SimpleImage/issues", "issues": "https://github.com/claviska/SimpleImage/issues",
"source": "https://github.com/claviska/SimpleImage/tree/3.6.5" "source": "https://github.com/claviska/SimpleImage/tree/3.7.0"
}, },
"funding": [ "funding": [
{ {
@ -53,7 +53,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2021-12-01T12:42:55+00:00" "time": "2022-07-05T13:18:44+00:00"
}, },
{ {
"name": "filp/whoops", "name": "filp/whoops",
@ -349,16 +349,16 @@
}, },
{ {
"name": "phpmailer/phpmailer", "name": "phpmailer/phpmailer",
"version": "v6.5.4", "version": "v6.6.4",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/PHPMailer/PHPMailer.git", "url": "https://github.com/PHPMailer/PHPMailer.git",
"reference": "c0d9f7dd3c2aa247ca44791e9209233829d82285" "reference": "a94fdebaea6bd17f51be0c2373ab80d3d681269b"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/c0d9f7dd3c2aa247ca44791e9209233829d82285", "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/a94fdebaea6bd17f51be0c2373ab80d3d681269b",
"reference": "c0d9f7dd3c2aa247ca44791e9209233829d82285", "reference": "a94fdebaea6bd17f51be0c2373ab80d3d681269b",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -370,8 +370,8 @@
"require-dev": { "require-dev": {
"dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
"doctrine/annotations": "^1.2", "doctrine/annotations": "^1.2",
"php-parallel-lint/php-console-highlighter": "^0.5.0", "php-parallel-lint/php-console-highlighter": "^1.0.0",
"php-parallel-lint/php-parallel-lint": "^1.3.1", "php-parallel-lint/php-parallel-lint": "^1.3.2",
"phpcompatibility/php-compatibility": "^9.3.5", "phpcompatibility/php-compatibility": "^9.3.5",
"roave/security-advisories": "dev-latest", "roave/security-advisories": "dev-latest",
"squizlabs/php_codesniffer": "^3.6.2", "squizlabs/php_codesniffer": "^3.6.2",
@ -415,7 +415,7 @@
"description": "PHPMailer is a full-featured email creation and transfer class for PHP", "description": "PHPMailer is a full-featured email creation and transfer class for PHP",
"support": { "support": {
"issues": "https://github.com/PHPMailer/PHPMailer/issues", "issues": "https://github.com/PHPMailer/PHPMailer/issues",
"source": "https://github.com/PHPMailer/PHPMailer/tree/v6.5.4" "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.6.4"
}, },
"funding": [ "funding": [
{ {
@ -423,7 +423,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2022-02-17T08:19:04+00:00" "time": "2022-08-22T09:22:00+00:00"
}, },
{ {
"name": "psr/log", "name": "psr/log",
@ -477,16 +477,16 @@
}, },
{ {
"name": "symfony/polyfill-intl-idn", "name": "symfony/polyfill-intl-idn",
"version": "v1.25.0", "version": "v1.26.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-intl-idn.git", "url": "https://github.com/symfony/polyfill-intl-idn.git",
"reference": "749045c69efb97c70d25d7463abba812e91f3a44" "reference": "59a8d271f00dd0e4c2e518104cc7963f655a1aa8"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/749045c69efb97c70d25d7463abba812e91f3a44", "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/59a8d271f00dd0e4c2e518104cc7963f655a1aa8",
"reference": "749045c69efb97c70d25d7463abba812e91f3a44", "reference": "59a8d271f00dd0e4c2e518104cc7963f655a1aa8",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -500,7 +500,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "1.23-dev" "dev-main": "1.26-dev"
}, },
"thanks": { "thanks": {
"name": "symfony/polyfill", "name": "symfony/polyfill",
@ -544,7 +544,7 @@
"shim" "shim"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.25.0" "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.26.0"
}, },
"funding": [ "funding": [
{ {
@ -560,20 +560,20 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2021-09-14T14:02:44+00:00" "time": "2022-05-24T11:49:31+00:00"
}, },
{ {
"name": "symfony/polyfill-intl-normalizer", "name": "symfony/polyfill-intl-normalizer",
"version": "v1.25.0", "version": "v1.26.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-intl-normalizer.git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git",
"reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8" "reference": "219aa369ceff116e673852dce47c3a41794c14bd"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8590a5f561694770bdcd3f9b5c69dde6945028e8", "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/219aa369ceff116e673852dce47c3a41794c14bd",
"reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8", "reference": "219aa369ceff116e673852dce47c3a41794c14bd",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -585,7 +585,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "1.23-dev" "dev-main": "1.26-dev"
}, },
"thanks": { "thanks": {
"name": "symfony/polyfill", "name": "symfony/polyfill",
@ -628,7 +628,7 @@
"shim" "shim"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.25.0" "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.26.0"
}, },
"funding": [ "funding": [
{ {
@ -644,20 +644,20 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2021-02-19T12:13:01+00:00" "time": "2022-05-24T11:49:31+00:00"
}, },
{ {
"name": "symfony/polyfill-mbstring", "name": "symfony/polyfill-mbstring",
"version": "v1.25.0", "version": "v1.26.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git", "url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825" "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/0abb51d2f102e00a4eefcf46ba7fec406d245825", "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e",
"reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825", "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -672,7 +672,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "1.23-dev" "dev-main": "1.26-dev"
}, },
"thanks": { "thanks": {
"name": "symfony/polyfill", "name": "symfony/polyfill",
@ -711,7 +711,7 @@
"shim" "shim"
], ],
"support": { "support": {
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.25.0" "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.26.0"
}, },
"funding": [ "funding": [
{ {
@ -727,7 +727,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2021-11-30T18:21:41+00:00" "time": "2022-05-24T11:49:31+00:00"
} }
], ],
"packages-dev": [], "packages-dev": [],
@ -738,9 +738,21 @@
"prefer-lowest": false, "prefer-lowest": false,
"platform": { "platform": {
"php": ">=7.4.0 <8.2.0", "php": ">=7.4.0 <8.2.0",
"ext-simplexml": "*",
"ext-ctype": "*", "ext-ctype": "*",
"ext-mbstring": "*" "ext-curl": "*",
"ext-dom": "*",
"ext-filter": "*",
"ext-hash": "*",
"ext-iconv": "*",
"ext-json": "*",
"ext-libxml": "*",
"ext-mbstring": "*",
"ext-openssl": "*"
}, },
"platform-dev": [], "platform-dev": [],
"plugin-api-version": "2.1.0" "platform-overrides": {
"php": "7.4.0"
},
"plugin-api-version": "2.3.0"
} }

View file

@ -1,80 +1,81 @@
<?php <?php
return [ return [
// cms classes // cms classes
'collection' => 'Kirby\Cms\Collection', 'collection' => 'Kirby\Cms\Collection',
'field' => 'Kirby\Cms\Field', 'field' => 'Kirby\Cms\Field',
'file' => 'Kirby\Cms\File', 'file' => 'Kirby\Cms\File',
'files' => 'Kirby\Cms\Files', 'files' => 'Kirby\Cms\Files',
'find' => 'Kirby\Cms\Find', 'find' => 'Kirby\Cms\Find',
'html' => 'Kirby\Cms\Html', 'helpers' => 'Kirby\Cms\Helpers',
'kirby' => 'Kirby\Cms\App', 'html' => 'Kirby\Cms\Html',
'page' => 'Kirby\Cms\Page', 'kirby' => 'Kirby\Cms\App',
'pages' => 'Kirby\Cms\Pages', 'page' => 'Kirby\Cms\Page',
'pagination' => 'Kirby\Cms\Pagination', 'pages' => 'Kirby\Cms\Pages',
'r' => 'Kirby\Cms\R', 'pagination' => 'Kirby\Cms\Pagination',
'response' => 'Kirby\Cms\Response', 'r' => 'Kirby\Cms\R',
's' => 'Kirby\Cms\S', 'response' => 'Kirby\Cms\Response',
'sane' => 'Kirby\Sane\Sane', 's' => 'Kirby\Cms\S',
'site' => 'Kirby\Cms\Site', 'sane' => 'Kirby\Sane\Sane',
'structure' => 'Kirby\Cms\Structure', 'site' => 'Kirby\Cms\Site',
'url' => 'Kirby\Cms\Url', 'structure' => 'Kirby\Cms\Structure',
'user' => 'Kirby\Cms\User', 'url' => 'Kirby\Cms\Url',
'users' => 'Kirby\Cms\Users', 'user' => 'Kirby\Cms\User',
'visitor' => 'Kirby\Cms\Visitor', 'users' => 'Kirby\Cms\Users',
'visitor' => 'Kirby\Cms\Visitor',
// data handler // data handler
'data' => 'Kirby\Data\Data', 'data' => 'Kirby\Data\Data',
'json' => 'Kirby\Data\Json', 'json' => 'Kirby\Data\Json',
'yaml' => 'Kirby\Data\Yaml', 'yaml' => 'Kirby\Data\Yaml',
// file classes // file classes
'asset' => 'Kirby\Filesystem\Asset', 'asset' => 'Kirby\Filesystem\Asset',
'dir' => 'Kirby\Filesystem\Dir', 'dir' => 'Kirby\Filesystem\Dir',
'f' => 'Kirby\Filesystem\F', 'f' => 'Kirby\Filesystem\F',
'mime' => 'Kirby\Filesystem\Mime', 'mime' => 'Kirby\Filesystem\Mime',
// data classes // data classes
'database' => 'Kirby\Database\Database', 'database' => 'Kirby\Database\Database',
'db' => 'Kirby\Database\Db', 'db' => 'Kirby\Database\Db',
// exceptions // exceptions
'errorpageexception' => 'Kirby\Exception\ErrorPageException', 'errorpageexception' => 'Kirby\Exception\ErrorPageException',
// http classes // http classes
'cookie' => 'Kirby\Http\Cookie', 'cookie' => 'Kirby\Http\Cookie',
'header' => 'Kirby\Http\Header', 'header' => 'Kirby\Http\Header',
'remote' => 'Kirby\Http\Remote', 'remote' => 'Kirby\Http\Remote',
'server' => 'Kirby\Http\Server', 'server' => 'Kirby\Http\Server',
// image classes // image classes
'dimensions' => 'Kirby\Image\Dimensions', 'dimensions' => 'Kirby\Image\Dimensions',
// panel classes // panel classes
'panel' => 'Kirby\Panel\Panel', 'panel' => 'Kirby\Panel\Panel',
// toolkit classes // toolkit classes
'a' => 'Kirby\Toolkit\A', 'a' => 'Kirby\Toolkit\A',
'c' => 'Kirby\Toolkit\Config', 'c' => 'Kirby\Toolkit\Config',
'config' => 'Kirby\Toolkit\Config', 'config' => 'Kirby\Toolkit\Config',
'escape' => 'Kirby\Toolkit\Escape', 'escape' => 'Kirby\Toolkit\Escape',
'i18n' => 'Kirby\Toolkit\I18n', 'i18n' => 'Kirby\Toolkit\I18n',
'obj' => 'Kirby\Toolkit\Obj', 'obj' => 'Kirby\Toolkit\Obj',
'str' => 'Kirby\Toolkit\Str', 'str' => 'Kirby\Toolkit\Str',
'tpl' => 'Kirby\Toolkit\Tpl', 'tpl' => 'Kirby\Toolkit\Tpl',
'v' => 'Kirby\Toolkit\V', 'v' => 'Kirby\Toolkit\V',
'xml' => 'Kirby\Toolkit\Xml', 'xml' => 'Kirby\Toolkit\Xml',
// TODO: remove in 4.0.0 // TODO: remove in 4.0.0
'kirby\cms\asset' => 'Kirby\Filesystem\Asset', 'kirby\cms\asset' => 'Kirby\Filesystem\Asset',
'kirby\cms\dir' => 'Kirby\Filesystem\Dir', 'kirby\cms\dir' => 'Kirby\Filesystem\Dir',
'kirby\cms\filename' => 'Kirby\Filesystem\Filename', 'kirby\cms\filename' => 'Kirby\Filesystem\Filename',
'kirby\cms\filefoundation' => 'Kirby\Filesystem\IsFile', 'kirby\cms\filefoundation' => 'Kirby\Filesystem\IsFile',
'kirby\cms\form' => 'Kirby\Form\Form', 'kirby\cms\form' => 'Kirby\Form\Form',
'kirby\cms\kirbytag' => 'Kirby\Text\KirbyTag', 'kirby\cms\kirbytag' => 'Kirby\Text\KirbyTag',
'kirby\cms\kirbytags' => 'Kirby\Text\KirbyTags', 'kirby\cms\kirbytags' => 'Kirby\Text\KirbyTags',
'kirby\toolkit\dir' => 'Kirby\Filesystem\Dir', 'kirby\toolkit\dir' => 'Kirby\Filesystem\Dir',
'kirby\toolkit\f' => 'Kirby\Filesystem\F', 'kirby\toolkit\f' => 'Kirby\Filesystem\F',
'kirby\toolkit\file' => 'Kirby\Filesystem\File', 'kirby\toolkit\file' => 'Kirby\Filesystem\File',
'kirby\toolkit\mime' => 'Kirby\Filesystem\Mime', 'kirby\toolkit\mime' => 'Kirby\Filesystem\Mime',
]; ];

View file

@ -3,25 +3,25 @@
use Kirby\Exception\PermissionException; use Kirby\Exception\PermissionException;
return function () { return function () {
$auth = $this->kirby()->auth(); $auth = $this->kirby()->auth();
$allowImpersonation = $this->kirby()->option('api.allowImpersonation') ?? false; $allowImpersonation = $this->kirby()->option('api.allowImpersonation') ?? false;
// csrf token check // csrf token check
if ( if (
$auth->type($allowImpersonation) === 'session' && $auth->type($allowImpersonation) === 'session' &&
$auth->csrf() === false $auth->csrf() === false
) { ) {
throw new PermissionException('Unauthenticated'); throw new PermissionException('Unauthenticated');
} }
// get user from session or basic auth // get user from session or basic auth
if ($user = $auth->user(null, $allowImpersonation)) { if ($user = $auth->user(null, $allowImpersonation)) {
if ($user->role()->permissions()->for('access', 'panel') === false) { if ($user->role()->permissions()->for('access', 'panel') === false) {
throw new PermissionException(['key' => 'access.panel']); throw new PermissionException(['key' => 'access.panel']);
} }
return $user; return $user;
} }
throw new PermissionException('Unauthenticated'); throw new PermissionException('Unauthenticated');
}; };

View file

@ -5,66 +5,66 @@
*/ */
return [ return [
/** /**
* Children * Children
*/ */
'children' => [ 'children' => [
'model' => 'page', 'model' => 'page',
'type' => 'Kirby\Cms\Pages', 'type' => 'Kirby\Cms\Pages',
'view' => 'compact' 'view' => 'compact'
], ],
/** /**
* Files * Files
*/ */
'files' => [ 'files' => [
'model' => 'file', 'model' => 'file',
'type' => 'Kirby\Cms\Files' 'type' => 'Kirby\Cms\Files'
], ],
/** /**
* Languages * Languages
*/ */
'languages' => [ 'languages' => [
'model' => 'language', 'model' => 'language',
'type' => 'Kirby\Cms\Languages' 'type' => 'Kirby\Cms\Languages'
], ],
/** /**
* Pages * Pages
*/ */
'pages' => [ 'pages' => [
'model' => 'page', 'model' => 'page',
'type' => 'Kirby\Cms\Pages', 'type' => 'Kirby\Cms\Pages',
'view' => 'compact' 'view' => 'compact'
], ],
/** /**
* Roles * Roles
*/ */
'roles' => [ 'roles' => [
'model' => 'role', 'model' => 'role',
'type' => 'Kirby\Cms\Roles', 'type' => 'Kirby\Cms\Roles',
'view' => 'compact' 'view' => 'compact'
], ],
/** /**
* Translations * Translations
*/ */
'translations' => [ 'translations' => [
'model' => 'translation', 'model' => 'translation',
'type' => 'Kirby\Cms\Translations', 'type' => 'Kirby\Cms\Translations',
'view' => 'compact' 'view' => 'compact'
], ],
/** /**
* Users * Users
*/ */
'users' => [ 'users' => [
'default' => fn () => $this->users(), 'default' => fn () => $this->users(),
'model' => 'user', 'model' => 'user',
'type' => 'Kirby\Cms\Users', 'type' => 'Kirby\Cms\Users',
'view' => 'compact' 'view' => 'compact'
] ]
]; ];

View file

@ -4,17 +4,17 @@
* Api Model Definitions * Api Model Definitions
*/ */
return [ return [
'File' => include __DIR__ . '/models/File.php', 'File' => include __DIR__ . '/models/File.php',
'FileBlueprint' => include __DIR__ . '/models/FileBlueprint.php', 'FileBlueprint' => include __DIR__ . '/models/FileBlueprint.php',
'FileVersion' => include __DIR__ . '/models/FileVersion.php', 'FileVersion' => include __DIR__ . '/models/FileVersion.php',
'Language' => include __DIR__ . '/models/Language.php', 'Language' => include __DIR__ . '/models/Language.php',
'Page' => include __DIR__ . '/models/Page.php', 'Page' => include __DIR__ . '/models/Page.php',
'PageBlueprint' => include __DIR__ . '/models/PageBlueprint.php', 'PageBlueprint' => include __DIR__ . '/models/PageBlueprint.php',
'Role' => include __DIR__ . '/models/Role.php', 'Role' => include __DIR__ . '/models/Role.php',
'Site' => include __DIR__ . '/models/Site.php', 'Site' => include __DIR__ . '/models/Site.php',
'SiteBlueprint' => include __DIR__ . '/models/SiteBlueprint.php', 'SiteBlueprint' => include __DIR__ . '/models/SiteBlueprint.php',
'System' => include __DIR__ . '/models/System.php', 'System' => include __DIR__ . '/models/System.php',
'Translation' => include __DIR__ . '/models/Translation.php', 'Translation' => include __DIR__ . '/models/Translation.php',
'User' => include __DIR__ . '/models/User.php', 'User' => include __DIR__ . '/models/User.php',
'UserBlueprint' => include __DIR__ . '/models/UserBlueprint.php', 'UserBlueprint' => include __DIR__ . '/models/UserBlueprint.php',
]; ];

View file

@ -7,116 +7,109 @@ use Kirby\Form\Form;
* File * File
*/ */
return [ return [
'fields' => [ 'fields' => [
'blueprint' => fn (File $file) => $file->blueprint(), 'blueprint' => fn (File $file) => $file->blueprint(),
'content' => fn (File $file) => Form::for($file)->values(), 'content' => fn (File $file) => Form::for($file)->values(),
'dimensions' => fn (File $file) => $file->dimensions()->toArray(), 'dimensions' => fn (File $file) => $file->dimensions()->toArray(),
'dragText' => fn (File $file) => $file->panel()->dragText(), 'dragText' => fn (File $file) => $file->panel()->dragText(),
'exists' => fn (File $file) => $file->exists(), 'exists' => fn (File $file) => $file->exists(),
'extension' => fn (File $file) => $file->extension(), 'extension' => fn (File $file) => $file->extension(),
'filename' => fn (File $file) => $file->filename(), 'filename' => fn (File $file) => $file->filename(),
'id' => fn (File $file) => $file->id(), 'id' => fn (File $file) => $file->id(),
'link' => fn (File $file) => $file->panel()->url(true), 'link' => fn (File $file) => $file->panel()->url(true),
'mime' => fn (File $file) => $file->mime(), 'mime' => fn (File $file) => $file->mime(),
'modified' => fn (File $file) => $file->modified('c'), 'modified' => fn (File $file) => $file->modified('c'),
'name' => fn (File $file) => $file->name(), 'name' => fn (File $file) => $file->name(),
'next' => fn (File $file) => $file->next(), 'next' => fn (File $file) => $file->next(),
'nextWithTemplate' => function (File $file) { 'nextWithTemplate' => function (File $file) {
$files = $file->templateSiblings()->sorted(); $files = $file->templateSiblings()->sorted();
$index = $files->indexOf($file); $index = $files->indexOf($file);
return $files->nth($index + 1); return $files->nth($index + 1);
}, },
'niceSize' => fn (File $file) => $file->niceSize(), 'niceSize' => fn (File $file) => $file->niceSize(),
'options' => fn (File $file) => $file->panel()->options(), 'options' => fn (File $file) => $file->panel()->options(),
'panelIcon' => function (File $file) { 'panelImage' => fn (File $file) => $file->panel()->image(),
// TODO: remove in 3.7.0 'panelUrl' => fn (File $file) => $file->panel()->url(true),
// @codeCoverageIgnoreStart 'prev' => fn (File $file) => $file->prev(),
deprecated('The API field file.panelIcon has been deprecated and will be removed in 3.7.0. Use file.panelImage instead'); 'prevWithTemplate' => function (File $file) {
return $file->panel()->image(); $files = $file->templateSiblings()->sorted();
// @codeCoverageIgnoreEnd $index = $files->indexOf($file);
},
'panelImage' => fn (File $file) => $file->panel()->image(),
'panelUrl' => fn (File $file) => $file->panel()->url(true),
'prev' => fn (File $file) => $file->prev(),
'prevWithTemplate' => function (File $file) {
$files = $file->templateSiblings()->sorted();
$index = $files->indexOf($file);
return $files->nth($index - 1); return $files->nth($index - 1);
}, },
'parent' => fn (File $file) => $file->parent(), 'parent' => fn (File $file) => $file->parent(),
'parents' => fn (File $file) => $file->parents()->flip(), 'parents' => fn (File $file) => $file->parents()->flip(),
'size' => fn (File $file) => $file->size(), 'size' => fn (File $file) => $file->size(),
'template' => fn (File $file) => $file->template(), 'template' => fn (File $file) => $file->template(),
'thumbs' => function ($file) { 'thumbs' => function ($file) {
if ($file->isResizable() === false) { if ($file->isResizable() === false) {
return null; return null;
} }
return [ return [
'tiny' => $file->resize(128)->url(), 'tiny' => $file->resize(128)->url(),
'small' => $file->resize(256)->url(), 'small' => $file->resize(256)->url(),
'medium' => $file->resize(512)->url(), 'medium' => $file->resize(512)->url(),
'large' => $file->resize(768)->url(), 'large' => $file->resize(768)->url(),
'huge' => $file->resize(1024)->url(), 'huge' => $file->resize(1024)->url(),
]; ];
}, },
'type' => fn (File $file) => $file->type(), 'type' => fn (File $file) => $file->type(),
'url' => fn (File $file) => $file->url(), 'url' => fn (File $file) => $file->url(),
], ],
'type' => 'Kirby\Cms\File', 'type' => 'Kirby\Cms\File',
'views' => [ 'views' => [
'default' => [ 'default' => [
'content', 'content',
'dimensions', 'dimensions',
'exists', 'exists',
'extension', 'extension',
'filename', 'filename',
'id', 'id',
'link', 'link',
'mime', 'mime',
'modified', 'modified',
'name', 'name',
'next' => 'compact', 'next' => 'compact',
'niceSize', 'niceSize',
'parent' => 'compact', 'parent' => 'compact',
'options', 'options',
'prev' => 'compact', 'prev' => 'compact',
'size', 'size',
'template', 'template',
'type', 'type',
'url' 'url'
], ],
'compact' => [ 'compact' => [
'filename', 'filename',
'id', 'id',
'link', 'link',
'type', 'type',
'url', 'url',
], ],
'panel' => [ 'panel' => [
'blueprint', 'blueprint',
'content', 'content',
'dimensions', 'dimensions',
'extension', 'extension',
'filename', 'filename',
'id', 'id',
'link', 'link',
'mime', 'mime',
'modified', 'modified',
'name', 'name',
'nextWithTemplate' => 'compact', 'nextWithTemplate' => 'compact',
'niceSize', 'niceSize',
'options', 'options',
'panelIcon', 'panelIcon',
'panelImage', 'panelImage',
'parent' => 'compact', 'parent' => 'compact',
'parents' => ['id', 'slug', 'title'], 'parents' => ['id', 'slug', 'title'],
'prevWithTemplate' => 'compact', 'prevWithTemplate' => 'compact',
'template', 'template',
'type', 'type',
'url' 'url'
] ]
], ],
]; ];

View file

@ -6,13 +6,13 @@ use Kirby\Cms\FileBlueprint;
* FileBlueprint * FileBlueprint
*/ */
return [ return [
'fields' => [ 'fields' => [
'name' => fn (FileBlueprint $blueprint) => $blueprint->name(), 'name' => fn (FileBlueprint $blueprint) => $blueprint->name(),
'options' => fn (FileBlueprint $blueprint) => $blueprint->options(), 'options' => fn (FileBlueprint $blueprint) => $blueprint->options(),
'tabs' => fn (FileBlueprint $blueprint) => $blueprint->tabs(), 'tabs' => fn (FileBlueprint $blueprint) => $blueprint->tabs(),
'title' => fn (FileBlueprint $blueprint) => $blueprint->title(), 'title' => fn (FileBlueprint $blueprint) => $blueprint->title(),
], ],
'type' => 'Kirby\Cms\FileBlueprint', 'type' => 'Kirby\Cms\FileBlueprint',
'views' => [ 'views' => [
], ],
]; ];

View file

@ -6,54 +6,54 @@ use Kirby\Cms\FileVersion;
* FileVersion * FileVersion
*/ */
return [ return [
'fields' => [ 'fields' => [
'dimensions' => fn (FileVersion $file) => $file->dimensions()->toArray(), 'dimensions' => fn (FileVersion $file) => $file->dimensions()->toArray(),
'exists' => fn (FileVersion $file) => $file->exists(), 'exists' => fn (FileVersion $file) => $file->exists(),
'extension' => fn (FileVersion $file) => $file->extension(), 'extension' => fn (FileVersion $file) => $file->extension(),
'filename' => fn (FileVersion $file) => $file->filename(), 'filename' => fn (FileVersion $file) => $file->filename(),
'id' => fn (FileVersion $file) => $file->id(), 'id' => fn (FileVersion $file) => $file->id(),
'mime' => fn (FileVersion $file) => $file->mime(), 'mime' => fn (FileVersion $file) => $file->mime(),
'modified' => fn (FileVersion $file) => $file->modified('c'), 'modified' => fn (FileVersion $file) => $file->modified('c'),
'name' => fn (FileVersion $file) => $file->name(), 'name' => fn (FileVersion $file) => $file->name(),
'niceSize' => fn (FileVersion $file) => $file->niceSize(), 'niceSize' => fn (FileVersion $file) => $file->niceSize(),
'size' => fn (FileVersion $file) => $file->size(), 'size' => fn (FileVersion $file) => $file->size(),
'type' => fn (FileVersion $file) => $file->type(), 'type' => fn (FileVersion $file) => $file->type(),
'url' => fn (FileVersion $file) => $file->url(), 'url' => fn (FileVersion $file) => $file->url(),
], ],
'type' => 'Kirby\Cms\FileVersion', 'type' => 'Kirby\Cms\FileVersion',
'views' => [ 'views' => [
'default' => [ 'default' => [
'dimensions', 'dimensions',
'exists', 'exists',
'extension', 'extension',
'filename', 'filename',
'id', 'id',
'mime', 'mime',
'modified', 'modified',
'name', 'name',
'niceSize', 'niceSize',
'size', 'size',
'type', 'type',
'url' 'url'
], ],
'compact' => [ 'compact' => [
'filename', 'filename',
'id', 'id',
'type', 'type',
'url', 'url',
], ],
'panel' => [ 'panel' => [
'dimensions', 'dimensions',
'extension', 'extension',
'filename', 'filename',
'id', 'id',
'mime', 'mime',
'modified', 'modified',
'name', 'name',
'niceSize', 'niceSize',
'template', 'template',
'type', 'type',
'url' 'url'
] ]
], ],
]; ];

View file

@ -6,25 +6,25 @@ use Kirby\Cms\Language;
* Language * Language
*/ */
return [ return [
'fields' => [ 'fields' => [
'code' => fn (Language $language) => $language->code(), 'code' => fn (Language $language) => $language->code(),
'default' => fn (Language $language) => $language->isDefault(), 'default' => fn (Language $language) => $language->isDefault(),
'direction' => fn (Language $language) => $language->direction(), 'direction' => fn (Language $language) => $language->direction(),
'locale' => fn (Language $language) => $language->locale(), 'locale' => fn (Language $language) => $language->locale(),
'name' => fn (Language $language) => $language->name(), 'name' => fn (Language $language) => $language->name(),
'rules' => fn (Language $language) => $language->rules(), 'rules' => fn (Language $language) => $language->rules(),
'url' => fn (Language $language) => $language->url(), 'url' => fn (Language $language) => $language->url(),
], ],
'type' => 'Kirby\Cms\Language', 'type' => 'Kirby\Cms\Language',
'views' => [ 'views' => [
'default' => [ 'default' => [
'code', 'code',
'default', 'default',
'direction', 'direction',
'locale', 'locale',
'name', 'name',
'rules', 'rules',
'url' 'url'
] ]
] ]
]; ];

View file

@ -1,5 +1,6 @@
<?php <?php
use Kirby\Cms\Helpers;
use Kirby\Cms\Page; use Kirby\Cms\Page;
use Kirby\Form\Form; use Kirby\Form\Form;
@ -7,122 +8,116 @@ use Kirby\Form\Form;
* Page * Page
*/ */
return [ return [
'fields' => [ 'fields' => [
'blueprint' => fn (Page $page) => $page->blueprint(), 'blueprint' => fn (Page $page) => $page->blueprint(),
'blueprints' => fn (Page $page) => $page->blueprints(), 'blueprints' => fn (Page $page) => $page->blueprints(),
'children' => fn (Page $page) => $page->children(), 'children' => fn (Page $page) => $page->children(),
'content' => fn (Page $page) => Form::for($page)->values(), 'content' => fn (Page $page) => Form::for($page)->values(),
'drafts' => fn (Page $page) => $page->drafts(), 'drafts' => fn (Page $page) => $page->drafts(),
'errors' => fn (Page $page) => $page->errors(), 'errors' => fn (Page $page) => $page->errors(),
'files' => fn (Page $page) => $page->files()->sorted(), 'files' => fn (Page $page) => $page->files()->sorted(),
'hasChildren' => fn (Page $page) => $page->hasChildren(), 'hasChildren' => fn (Page $page) => $page->hasChildren(),
'hasDrafts' => fn (Page $page) => $page->hasDrafts(), 'hasDrafts' => fn (Page $page) => $page->hasDrafts(),
'hasFiles' => fn (Page $page) => $page->hasFiles(), 'hasFiles' => fn (Page $page) => $page->hasFiles(),
'id' => fn (Page $page) => $page->id(), 'id' => fn (Page $page) => $page->id(),
'isSortable' => fn (Page $page) => $page->isSortable(), 'isSortable' => fn (Page $page) => $page->isSortable(),
/** /**
* @deprecated 3.6.0 * @deprecated 3.6.0
* @todo Throw deprecated warning in 3.7.0 * @todo Remove in 3.8.0
* @todo Remove in 3.8.0 * @codeCoverageIgnore
* @codeCoverageIgnore */
*/ 'next' => function (Page $page) {
'next' => function (Page $page) { Helpers::deprecated('The API field page.next has been deprecated and will be removed in 3.8.0.');
return $page
->nextAll() return $page
->filter('intendedTemplate', $page->intendedTemplate()) ->nextAll()
->filter('status', $page->status()) ->filter('intendedTemplate', $page->intendedTemplate())
->filter('isReadable', true) ->filter('status', $page->status())
->first(); ->filter('isReadable', true)
}, ->first();
'num' => fn (Page $page) => $page->num(), },
'options' => fn (Page $page) => $page->panel()->options(['preview']), 'num' => fn (Page $page) => $page->num(),
/** 'options' => fn (Page $page) => $page->panel()->options(['preview']),
* @todo Remove in 3.7.0 'panelImage' => fn (Page $page) => $page->panel()->image(),
* @codeCoverageIgnore 'parent' => fn (Page $page) => $page->parent(),
*/ 'parents' => fn (Page $page) => $page->parents()->flip(),
'panelIcon' => function (Page $page) { /**
deprecated('The API field page.panelIcon has been deprecated and will be removed in 3.7.0. Use page.panelImage instead'); * @deprecated 3.6.0
return $page->panel()->image(); * @todo Remove in 3.8.0
}, * @codeCoverageIgnore
'panelImage' => fn (Page $page) => $page->panel()->image(), */
'parent' => fn (Page $page) => $page->parent(), 'prev' => function (Page $page) {
'parents' => fn (Page $page) => $page->parents()->flip(), Helpers::deprecated('The API field page.prev has been deprecated and will be removed in 3.8.0.');
/**
* @deprecated 3.6.0 return $page
* @todo Throw deprecated warning in 3.7.0 ->prevAll()
* @todo Remove in 3.8.0 ->filter('intendedTemplate', $page->intendedTemplate())
* @codeCoverageIgnore ->filter('status', $page->status())
*/ ->filter('isReadable', true)
'prev' => function (Page $page) { ->last();
return $page },
->prevAll() 'previewUrl' => fn (Page $page) => $page->previewUrl(),
->filter('intendedTemplate', $page->intendedTemplate()) 'siblings' => function (Page $page) {
->filter('status', $page->status()) if ($page->isDraft() === true) {
->filter('isReadable', true) return $page->parentModel()->children()->not($page);
->last(); } else {
}, return $page->siblings();
'previewUrl' => fn (Page $page) => $page->previewUrl(), }
'siblings' => function (Page $page) { },
if ($page->isDraft() === true) { 'slug' => fn (Page $page) => $page->slug(),
return $page->parentModel()->children()->not($page); 'status' => fn (Page $page) => $page->status(),
} else { 'template' => fn (Page $page) => $page->intendedTemplate()->name(),
return $page->siblings(); 'title' => fn (Page $page) => $page->title()->value(),
} 'url' => fn (Page $page) => $page->url(),
}, ],
'slug' => fn (Page $page) => $page->slug(), 'type' => 'Kirby\Cms\Page',
'status' => fn (Page $page) => $page->status(), 'views' => [
'template' => fn (Page $page) => $page->intendedTemplate()->name(), 'compact' => [
'title' => fn (Page $page) => $page->title()->value(), 'id',
'url' => fn (Page $page) => $page->url(), 'title',
], 'url',
'type' => 'Kirby\Cms\Page', 'num'
'views' => [ ],
'compact' => [ 'default' => [
'id', 'content',
'title', 'id',
'url', 'status',
'num' 'num',
], 'options',
'default' => [ 'parent' => 'compact',
'content', 'slug',
'id', 'template',
'status', 'title',
'num', 'url'
'options', ],
'parent' => 'compact', 'panel' => [
'slug', 'id',
'template', 'blueprint',
'title', 'content',
'url' 'status',
], 'options',
'panel' => [ 'next' => ['id', 'slug', 'title'],
'id', 'parents' => ['id', 'slug', 'title'],
'blueprint', 'prev' => ['id', 'slug', 'title'],
'content', 'previewUrl',
'status', 'slug',
'options', 'title',
'next' => ['id', 'slug', 'title'], 'url'
'parents' => ['id', 'slug', 'title'], ],
'prev' => ['id', 'slug', 'title'], 'selector' => [
'previewUrl', 'id',
'slug', 'title',
'title', 'parent' => [
'url' 'id',
], 'title'
'selector' => [ ],
'id', 'children' => [
'title', 'hasChildren',
'parent' => [ 'id',
'id', 'panelIcon',
'title' 'panelImage',
], 'title',
'children' => [ ],
'hasChildren', ]
'id', ],
'panelIcon',
'panelImage',
'title',
],
]
],
]; ];

View file

@ -6,16 +6,16 @@ use Kirby\Cms\PageBlueprint;
* PageBlueprint * PageBlueprint
*/ */
return [ return [
'fields' => [ 'fields' => [
'name' => fn (PageBlueprint $blueprint) => $blueprint->name(), 'name' => fn (PageBlueprint $blueprint) => $blueprint->name(),
'num' => fn (PageBlueprint $blueprint) => $blueprint->num(), 'num' => fn (PageBlueprint $blueprint) => $blueprint->num(),
'options' => fn (PageBlueprint $blueprint) => $blueprint->options(), 'options' => fn (PageBlueprint $blueprint) => $blueprint->options(),
'preview' => fn (PageBlueprint $blueprint) => $blueprint->preview(), 'preview' => fn (PageBlueprint $blueprint) => $blueprint->preview(),
'status' => fn (PageBlueprint $blueprint) => $blueprint->status(), 'status' => fn (PageBlueprint $blueprint) => $blueprint->status(),
'tabs' => fn (PageBlueprint $blueprint) => $blueprint->tabs(), 'tabs' => fn (PageBlueprint $blueprint) => $blueprint->tabs(),
'title' => fn (PageBlueprint $blueprint) => $blueprint->title(), 'title' => fn (PageBlueprint $blueprint) => $blueprint->title(),
], ],
'type' => 'Kirby\Cms\PageBlueprint', 'type' => 'Kirby\Cms\PageBlueprint',
'views' => [ 'views' => [
], ],
]; ];

View file

@ -6,18 +6,18 @@ use Kirby\Cms\Role;
* Role * Role
*/ */
return [ return [
'fields' => [ 'fields' => [
'description' => fn (Role $role) => $role->description(), 'description' => fn (Role $role) => $role->description(),
'name' => fn (Role $role) => $role->name(), 'name' => fn (Role $role) => $role->name(),
'permissions' => fn (Role $role) => $role->permissions()->toArray(), 'permissions' => fn (Role $role) => $role->permissions()->toArray(),
'title' => fn (Role $role) => $role->title(), 'title' => fn (Role $role) => $role->title(),
], ],
'type' => 'Kirby\Cms\Role', 'type' => 'Kirby\Cms\Role',
'views' => [ 'views' => [
'compact' => [ 'compact' => [
'description', 'description',
'name', 'name',
'title' 'title'
] ]
] ]
]; ];

View file

@ -7,46 +7,46 @@ use Kirby\Form\Form;
* Site * Site
*/ */
return [ return [
'default' => fn () => $this->site(), 'default' => fn () => $this->site(),
'fields' => [ 'fields' => [
'blueprint' => fn (Site $site) => $site->blueprint(), 'blueprint' => fn (Site $site) => $site->blueprint(),
'children' => fn (Site $site) => $site->children(), 'children' => fn (Site $site) => $site->children(),
'content' => fn (Site $site) => Form::for($site)->values(), 'content' => fn (Site $site) => Form::for($site)->values(),
'drafts' => fn (Site $site) => $site->drafts(), 'drafts' => fn (Site $site) => $site->drafts(),
'files' => fn (Site $site) => $site->files()->sorted(), 'files' => fn (Site $site) => $site->files()->sorted(),
'options' => fn (Site $site) => $site->permissions()->toArray(), 'options' => fn (Site $site) => $site->permissions()->toArray(),
'previewUrl' => fn (Site $site) => $site->previewUrl(), 'previewUrl' => fn (Site $site) => $site->previewUrl(),
'title' => fn (Site $site) => $site->title()->value(), 'title' => fn (Site $site) => $site->title()->value(),
'url' => fn (Site $site) => $site->url(), 'url' => fn (Site $site) => $site->url(),
], ],
'type' => 'Kirby\Cms\Site', 'type' => 'Kirby\Cms\Site',
'views' => [ 'views' => [
'compact' => [ 'compact' => [
'title', 'title',
'url' 'url'
], ],
'default' => [ 'default' => [
'content', 'content',
'options', 'options',
'title', 'title',
'url' 'url'
], ],
'panel' => [ 'panel' => [
'title', 'title',
'blueprint', 'blueprint',
'content', 'content',
'options', 'options',
'previewUrl', 'previewUrl',
'url' 'url'
], ],
'selector' => [ 'selector' => [
'title', 'title',
'children' => [ 'children' => [
'id', 'id',
'title', 'title',
'panelIcon', 'panelIcon',
'hasChildren' 'hasChildren'
], ],
] ]
] ]
]; ];

View file

@ -6,12 +6,12 @@ use Kirby\Cms\SiteBlueprint;
* SiteBlueprint * SiteBlueprint
*/ */
return [ return [
'fields' => [ 'fields' => [
'name' => fn (SiteBlueprint $blueprint) => $blueprint->name(), 'name' => fn (SiteBlueprint $blueprint) => $blueprint->name(),
'options' => fn (SiteBlueprint $blueprint) => $blueprint->options(), 'options' => fn (SiteBlueprint $blueprint) => $blueprint->options(),
'tabs' => fn (SiteBlueprint $blueprint) => $blueprint->tabs(), 'tabs' => fn (SiteBlueprint $blueprint) => $blueprint->tabs(),
'title' => fn (SiteBlueprint $blueprint) => $blueprint->title(), 'title' => fn (SiteBlueprint $blueprint) => $blueprint->title(),
], ],
'type' => 'Kirby\Cms\SiteBlueprint', 'type' => 'Kirby\Cms\SiteBlueprint',
'views' => [], 'views' => [],
]; ];

View file

@ -7,92 +7,92 @@ use Kirby\Toolkit\Str;
* System * System
*/ */
return [ return [
'fields' => [ 'fields' => [
'ascii' => fn () => Str::$ascii, 'ascii' => fn () => Str::$ascii,
'authStatus' => fn () => $this->kirby()->auth()->status()->toArray(), 'authStatus' => fn () => $this->kirby()->auth()->status()->toArray(),
'defaultLanguage' => fn () => $this->kirby()->panelLanguage(), 'defaultLanguage' => fn () => $this->kirby()->panelLanguage(),
'isOk' => fn (System $system) => $system->isOk(), 'isOk' => fn (System $system) => $system->isOk(),
'isInstallable' => fn (System $system) => $system->isInstallable(), 'isInstallable' => fn (System $system) => $system->isInstallable(),
'isInstalled' => fn (System $system) => $system->isInstalled(), 'isInstalled' => fn (System $system) => $system->isInstalled(),
'isLocal' => fn (System $system) => $system->isLocal(), 'isLocal' => fn (System $system) => $system->isLocal(),
'multilang' => fn () => $this->kirby()->option('languages', false) !== false, 'multilang' => fn () => $this->kirby()->option('languages', false) !== false,
'languages' => fn () => $this->kirby()->languages(), 'languages' => fn () => $this->kirby()->languages(),
'license' => fn (System $system) => $system->license(), 'license' => fn (System $system) => $system->license(),
'locales' => function () { 'locales' => function () {
$locales = []; $locales = [];
$translations = $this->kirby()->translations(); $translations = $this->kirby()->translations();
foreach ($translations as $translation) { foreach ($translations as $translation) {
$locales[$translation->code()] = $translation->locale(); $locales[$translation->code()] = $translation->locale();
} }
return $locales; return $locales;
}, },
'loginMethods' => fn (System $system) => array_keys($system->loginMethods()), 'loginMethods' => fn (System $system) => array_keys($system->loginMethods()),
'requirements' => fn (System $system) => $system->toArray(), 'requirements' => fn (System $system) => $system->toArray(),
'site' => fn (System $system) => $system->title(), 'site' => fn (System $system) => $system->title(),
'slugs' => fn () => Str::$language, 'slugs' => fn () => Str::$language,
'title' => fn () => $this->site()->title()->value(), 'title' => fn () => $this->site()->title()->value(),
'translation' => function () { 'translation' => function () {
if ($user = $this->user()) { if ($user = $this->user()) {
$translationCode = $user->language(); $translationCode = $user->language();
} else { } else {
$translationCode = $this->kirby()->panelLanguage(); $translationCode = $this->kirby()->panelLanguage();
} }
if ($translation = $this->kirby()->translation($translationCode)) { if ($translation = $this->kirby()->translation($translationCode)) {
return $translation; return $translation;
} else { } else {
return $this->kirby()->translation('en'); return $this->kirby()->translation('en');
} }
}, },
'kirbytext' => fn () => $this->kirby()->option('panel.kirbytext') ?? true, 'kirbytext' => fn () => $this->kirby()->option('panel.kirbytext') ?? true,
'user' => fn () => $this->user(), 'user' => fn () => $this->user(),
'version' => function () { 'version' => function () {
$user = $this->user(); $user = $this->user();
if ($user && $user->role()->permissions()->for('access', 'system') === true) { if ($user && $user->role()->permissions()->for('access', 'system') === true) {
return $this->kirby()->version(); return $this->kirby()->version();
} else { } else {
return null; return null;
} }
} }
], ],
'type' => 'Kirby\Cms\System', 'type' => 'Kirby\Cms\System',
'views' => [ 'views' => [
'login' => [ 'login' => [
'authStatus', 'authStatus',
'isOk', 'isOk',
'isInstallable', 'isInstallable',
'isInstalled', 'isInstalled',
'loginMethods', 'loginMethods',
'title', 'title',
'translation' 'translation'
], ],
'troubleshooting' => [ 'troubleshooting' => [
'isOk', 'isOk',
'isInstallable', 'isInstallable',
'isInstalled', 'isInstalled',
'title', 'title',
'translation', 'translation',
'requirements' 'requirements'
], ],
'panel' => [ 'panel' => [
'ascii', 'ascii',
'defaultLanguage', 'defaultLanguage',
'isOk', 'isOk',
'isInstalled', 'isInstalled',
'isLocal', 'isLocal',
'kirbytext', 'kirbytext',
'languages', 'languages',
'license', 'license',
'locales', 'locales',
'multilang', 'multilang',
'requirements', 'requirements',
'site', 'site',
'slugs', 'slugs',
'title', 'title',
'translation', 'translation',
'user' => 'auth', 'user' => 'auth',
'version' 'version'
] ]
], ],
]; ];

View file

@ -6,19 +6,19 @@ use Kirby\Cms\Translation;
* Translation * Translation
*/ */
return [ return [
'fields' => [ 'fields' => [
'author' => fn (Translation $translation) => $translation->author(), 'author' => fn (Translation $translation) => $translation->author(),
'data' => fn (Translation $translation) => $translation->dataWithFallback(), 'data' => fn (Translation $translation) => $translation->dataWithFallback(),
'direction' => fn (Translation $translation) => $translation->direction(), 'direction' => fn (Translation $translation) => $translation->direction(),
'id' => fn (Translation $translation) => $translation->id(), 'id' => fn (Translation $translation) => $translation->id(),
'name' => fn (Translation $translation) => $translation->name(), 'name' => fn (Translation $translation) => $translation->name(),
], ],
'type' => 'Kirby\Cms\Translation', 'type' => 'Kirby\Cms\Translation',
'views' => [ 'views' => [
'compact' => [ 'compact' => [
'direction', 'direction',
'id', 'id',
'name' 'name'
] ]
] ]
]; ];

View file

@ -7,71 +7,71 @@ use Kirby\Form\Form;
* User * User
*/ */
return [ return [
'default' => fn () => $this->user(), 'default' => fn () => $this->user(),
'fields' => [ 'fields' => [
'avatar' => fn (User $user) => $user->avatar() ? $user->avatar()->crop(512) : null, 'avatar' => fn (User $user) => $user->avatar() ? $user->avatar()->crop(512) : null,
'blueprint' => fn (User $user) => $user->blueprint(), 'blueprint' => fn (User $user) => $user->blueprint(),
'content' => fn (User $user) => Form::for($user)->values(), 'content' => fn (User $user) => Form::for($user)->values(),
'email' => fn (User $user) => $user->email(), 'email' => fn (User $user) => $user->email(),
'files' => fn (User $user) => $user->files()->sorted(), 'files' => fn (User $user) => $user->files()->sorted(),
'id' => fn (User $user) => $user->id(), 'id' => fn (User $user) => $user->id(),
'language' => fn (User $user) => $user->language(), 'language' => fn (User $user) => $user->language(),
'name' => fn (User $user) => $user->name()->value(), 'name' => fn (User $user) => $user->name()->value(),
'next' => fn (User $user) => $user->next(), 'next' => fn (User $user) => $user->next(),
'options' => fn (User $user) => $user->panel()->options(), 'options' => fn (User $user) => $user->panel()->options(),
'panelImage' => fn (User $user) => $user->panel()->image(), 'panelImage' => fn (User $user) => $user->panel()->image(),
'permissions' => fn (User $user) => $user->role()->permissions()->toArray(), 'permissions' => fn (User $user) => $user->role()->permissions()->toArray(),
'prev' => fn (User $user) => $user->prev(), 'prev' => fn (User $user) => $user->prev(),
'role' => fn (User $user) => $user->role(), 'role' => fn (User $user) => $user->role(),
'roles' => fn (User $user) => $user->roles(), 'roles' => fn (User $user) => $user->roles(),
'username' => fn (User $user) => $user->username() 'username' => fn (User $user) => $user->username()
], ],
'type' => 'Kirby\Cms\User', 'type' => 'Kirby\Cms\User',
'views' => [ 'views' => [
'default' => [ 'default' => [
'avatar', 'avatar',
'content', 'content',
'email', 'email',
'id', 'id',
'language', 'language',
'name', 'name',
'next' => 'compact', 'next' => 'compact',
'options', 'options',
'prev' => 'compact', 'prev' => 'compact',
'role', 'role',
'username' 'username'
], ],
'compact' => [ 'compact' => [
'avatar' => 'compact', 'avatar' => 'compact',
'id', 'id',
'email', 'email',
'language', 'language',
'name', 'name',
'role' => 'compact', 'role' => 'compact',
'username' 'username'
], ],
'auth' => [ 'auth' => [
'avatar' => 'compact', 'avatar' => 'compact',
'permissions', 'permissions',
'email', 'email',
'id', 'id',
'name', 'name',
'role', 'role',
'language' 'language'
], ],
'panel' => [ 'panel' => [
'avatar' => 'compact', 'avatar' => 'compact',
'blueprint', 'blueprint',
'content', 'content',
'email', 'email',
'id', 'id',
'language', 'language',
'name', 'name',
'next' => ['id', 'name'], 'next' => ['id', 'name'],
'options', 'options',
'prev' => ['id', 'name'], 'prev' => ['id', 'name'],
'role', 'role',
'username', 'username',
], ],
] ]
]; ];

View file

@ -6,13 +6,13 @@ use Kirby\Cms\UserBlueprint;
* UserBlueprint * UserBlueprint
*/ */
return [ return [
'fields' => [ 'fields' => [
'name' => fn (UserBlueprint $blueprint) => $blueprint->name(), 'name' => fn (UserBlueprint $blueprint) => $blueprint->name(),
'options' => fn (UserBlueprint $blueprint) => $blueprint->options(), 'options' => fn (UserBlueprint $blueprint) => $blueprint->options(),
'tabs' => fn (UserBlueprint $blueprint) => $blueprint->tabs(), 'tabs' => fn (UserBlueprint $blueprint) => $blueprint->tabs(),
'title' => fn (UserBlueprint $blueprint) => $blueprint->title(), 'title' => fn (UserBlueprint $blueprint) => $blueprint->title(),
], ],
'type' => 'Kirby\Cms\UserBlueprint', 'type' => 'Kirby\Cms\UserBlueprint',
'views' => [ 'views' => [
], ],
]; ];

View file

@ -4,23 +4,23 @@
* Api Routes Definitions * Api Routes Definitions
*/ */
return function ($kirby) { return function ($kirby) {
$routes = array_merge( $routes = array_merge(
include __DIR__ . '/routes/auth.php', include __DIR__ . '/routes/auth.php',
include __DIR__ . '/routes/pages.php', include __DIR__ . '/routes/pages.php',
include __DIR__ . '/routes/roles.php', include __DIR__ . '/routes/roles.php',
include __DIR__ . '/routes/site.php', include __DIR__ . '/routes/site.php',
include __DIR__ . '/routes/users.php', include __DIR__ . '/routes/users.php',
include __DIR__ . '/routes/files.php', include __DIR__ . '/routes/files.php',
include __DIR__ . '/routes/lock.php', include __DIR__ . '/routes/lock.php',
include __DIR__ . '/routes/system.php', include __DIR__ . '/routes/system.php',
include __DIR__ . '/routes/translations.php' include __DIR__ . '/routes/translations.php'
); );
// only add the language routes if the // only add the language routes if the
// multi language setup is activated // multi language setup is activated
if ($kirby->option('languages', false) !== false) { if ($kirby->option('languages', false) !== false) {
$routes = array_merge($routes, include __DIR__ . '/routes/languages.php'); $routes = array_merge($routes, include __DIR__ . '/routes/languages.php');
} }
return $routes; return $routes;
}; };

View file

@ -7,102 +7,102 @@ use Kirby\Exception\NotFoundException;
* Authentication * Authentication
*/ */
return [ return [
[ [
'pattern' => 'auth', 'pattern' => 'auth',
'method' => 'GET', 'method' => 'GET',
'action' => function () { 'action' => function () {
if ($user = $this->kirby()->auth()->user()) { if ($user = $this->kirby()->auth()->user()) {
return $this->resolve($user)->view('auth'); return $this->resolve($user)->view('auth');
} }
throw new NotFoundException('The user cannot be found'); throw new NotFoundException('The user cannot be found');
} }
], ],
[ [
'pattern' => 'auth/code', 'pattern' => 'auth/code',
'method' => 'POST', 'method' => 'POST',
'auth' => false, 'auth' => false,
'action' => function () { 'action' => function () {
$auth = $this->kirby()->auth(); $auth = $this->kirby()->auth();
// csrf token check // csrf token check
if ($auth->type() === 'session' && $auth->csrf() === false) { if ($auth->type() === 'session' && $auth->csrf() === false) {
throw new InvalidArgumentException('Invalid CSRF token'); throw new InvalidArgumentException('Invalid CSRF token');
} }
$user = $auth->verifyChallenge($this->requestBody('code')); $user = $auth->verifyChallenge($this->requestBody('code'));
return [ return [
'code' => 200, 'code' => 200,
'status' => 'ok', 'status' => 'ok',
'user' => $this->resolve($user)->view('auth')->toArray() 'user' => $this->resolve($user)->view('auth')->toArray()
]; ];
} }
], ],
[ [
'pattern' => 'auth/login', 'pattern' => 'auth/login',
'method' => 'POST', 'method' => 'POST',
'auth' => false, 'auth' => false,
'action' => function () { 'action' => function () {
$auth = $this->kirby()->auth(); $auth = $this->kirby()->auth();
$methods = $this->kirby()->system()->loginMethods(); $methods = $this->kirby()->system()->loginMethods();
// csrf token check // csrf token check
if ($auth->type() === 'session' && $auth->csrf() === false) { if ($auth->type() === 'session' && $auth->csrf() === false) {
throw new InvalidArgumentException('Invalid CSRF token'); throw new InvalidArgumentException('Invalid CSRF token');
} }
$email = $this->requestBody('email'); $email = $this->requestBody('email');
$long = $this->requestBody('long'); $long = $this->requestBody('long');
$password = $this->requestBody('password'); $password = $this->requestBody('password');
if ($password) { if ($password) {
if (isset($methods['password']) !== true) { if (isset($methods['password']) !== true) {
throw new InvalidArgumentException('Login with password is not enabled'); throw new InvalidArgumentException('Login with password is not enabled');
} }
if ( if (
isset($methods['password']['2fa']) === true && isset($methods['password']['2fa']) === true &&
$methods['password']['2fa'] === true $methods['password']['2fa'] === true
) { ) {
$status = $auth->login2fa($email, $password, $long); $status = $auth->login2fa($email, $password, $long);
} else { } else {
$user = $auth->login($email, $password, $long); $user = $auth->login($email, $password, $long);
} }
} else { } else {
if (isset($methods['code']) === true) { if (isset($methods['code']) === true) {
$mode = 'login'; $mode = 'login';
} elseif (isset($methods['password-reset']) === true) { } elseif (isset($methods['password-reset']) === true) {
$mode = 'password-reset'; $mode = 'password-reset';
} else { } else {
throw new InvalidArgumentException('Login without password is not enabled'); throw new InvalidArgumentException('Login without password is not enabled');
} }
$status = $auth->createChallenge($email, $long, $mode); $status = $auth->createChallenge($email, $long, $mode);
} }
if (isset($user)) { if (isset($user)) {
return [ return [
'code' => 200, 'code' => 200,
'status' => 'ok', 'status' => 'ok',
'user' => $this->resolve($user)->view('auth')->toArray() 'user' => $this->resolve($user)->view('auth')->toArray()
]; ];
} else { } else {
return [ return [
'code' => 200, 'code' => 200,
'status' => 'ok', 'status' => 'ok',
'challenge' => $status->challenge() 'challenge' => $status->challenge()
]; ];
} }
} }
], ],
[ [
'pattern' => 'auth/logout', 'pattern' => 'auth/logout',
'method' => 'POST', 'method' => 'POST',
'auth' => false, 'auth' => false,
'action' => function () { 'action' => function () {
$this->kirby()->auth()->logout(); $this->kirby()->auth()->logout();
return true; return true;
} }
], ],
]; ];

View file

@ -8,125 +8,125 @@ $pattern = '(account|pages/[^/]+|site|users/[^/]+)';
*/ */
return [ return [
[ [
'pattern' => $pattern . '/files/(:any)/sections/(:any)', 'pattern' => $pattern . '/files/(:any)/sections/(:any)',
'method' => 'GET', 'method' => 'GET',
'action' => function (string $path, string $filename, string $sectionName) { 'action' => function (string $path, string $filename, string $sectionName) {
if ($section = $this->file($path, $filename)->blueprint()->section($sectionName)) { if ($section = $this->file($path, $filename)->blueprint()->section($sectionName)) {
return $section->toResponse(); return $section->toResponse();
} }
} }
], ],
[ [
'pattern' => $pattern . '/files/(:any)/fields/(:any)/(:all?)', 'pattern' => $pattern . '/files/(:any)/fields/(:any)/(:all?)',
'method' => 'ALL', 'method' => 'ALL',
'action' => function (string $parent, string $filename, string $fieldName, string $path = null) { 'action' => function (string $parent, string $filename, string $fieldName, string $path = null) {
if ($file = $this->file($parent, $filename)) { if ($file = $this->file($parent, $filename)) {
return $this->fieldApi($file, $fieldName, $path); return $this->fieldApi($file, $fieldName, $path);
} }
} }
], ],
[ [
'pattern' => $pattern . '/files', 'pattern' => $pattern . '/files',
'method' => 'GET', 'method' => 'GET',
'action' => function (string $path) { 'action' => function (string $path) {
return $this->parent($path)->files()->sorted(); return $this->parent($path)->files()->sorted();
} }
], ],
[ [
'pattern' => $pattern . '/files', 'pattern' => $pattern . '/files',
'method' => 'POST', 'method' => 'POST',
'action' => function (string $path) { 'action' => function (string $path) {
// move_uploaded_file() not working with unit test // move_uploaded_file() not working with unit test
// @codeCoverageIgnoreStart // @codeCoverageIgnoreStart
return $this->upload(function ($source, $filename) use ($path) { return $this->upload(function ($source, $filename) use ($path) {
return $this->parent($path)->createFile([ return $this->parent($path)->createFile([
'content' => [ 'content' => [
'sort' => $this->requestBody('sort') 'sort' => $this->requestBody('sort')
], ],
'source' => $source, 'source' => $source,
'template' => $this->requestBody('template'), 'template' => $this->requestBody('template'),
'filename' => $filename 'filename' => $filename
]); ]);
}); });
// @codeCoverageIgnoreEnd // @codeCoverageIgnoreEnd
} }
], ],
[ [
'pattern' => $pattern . '/files/search', 'pattern' => $pattern . '/files/search',
'method' => 'GET|POST', 'method' => 'GET|POST',
'action' => function (string $path) { 'action' => function (string $path) {
$files = $this->parent($path)->files(); $files = $this->parent($path)->files();
if ($this->requestMethod() === 'GET') { if ($this->requestMethod() === 'GET') {
return $files->search($this->requestQuery('q')); return $files->search($this->requestQuery('q'));
} else { } else {
return $files->query($this->requestBody()); return $files->query($this->requestBody());
} }
} }
], ],
[ [
'pattern' => $pattern . '/files/sort', 'pattern' => $pattern . '/files/sort',
'method' => 'PATCH', 'method' => 'PATCH',
'action' => function (string $path) { 'action' => function (string $path) {
return $this->parent($path)->files()->changeSort( return $this->parent($path)->files()->changeSort(
$this->requestBody('files'), $this->requestBody('files'),
$this->requestBody('index') $this->requestBody('index')
); );
} }
], ],
[ [
'pattern' => $pattern . '/files/(:any)', 'pattern' => $pattern . '/files/(:any)',
'method' => 'GET', 'method' => 'GET',
'action' => function (string $path, string $filename) { 'action' => function (string $path, string $filename) {
return $this->file($path, $filename); return $this->file($path, $filename);
} }
], ],
[ [
'pattern' => $pattern . '/files/(:any)', 'pattern' => $pattern . '/files/(:any)',
'method' => 'PATCH', 'method' => 'PATCH',
'action' => function (string $path, string $filename) { 'action' => function (string $path, string $filename) {
return $this->file($path, $filename)->update($this->requestBody(), $this->language(), true); return $this->file($path, $filename)->update($this->requestBody(), $this->language(), true);
} }
], ],
[ [
'pattern' => $pattern . '/files/(:any)', 'pattern' => $pattern . '/files/(:any)',
'method' => 'POST', 'method' => 'POST',
'action' => function (string $path, string $filename) { 'action' => function (string $path, string $filename) {
return $this->upload(function ($source) use ($path, $filename) { return $this->upload(function ($source) use ($path, $filename) {
return $this->file($path, $filename)->replace($source); return $this->file($path, $filename)->replace($source);
}); });
} }
], ],
[ [
'pattern' => $pattern . '/files/(:any)', 'pattern' => $pattern . '/files/(:any)',
'method' => 'DELETE', 'method' => 'DELETE',
'action' => function (string $path, string $filename) { 'action' => function (string $path, string $filename) {
return $this->file($path, $filename)->delete(); return $this->file($path, $filename)->delete();
} }
], ],
[ [
'pattern' => $pattern . '/files/(:any)/name', 'pattern' => $pattern . '/files/(:any)/name',
'method' => 'PATCH', 'method' => 'PATCH',
'action' => function (string $path, string $filename) { 'action' => function (string $path, string $filename) {
return $this->file($path, $filename)->changeName($this->requestBody('name')); return $this->file($path, $filename)->changeName($this->requestBody('name'));
} }
], ],
[ [
'pattern' => 'files/search', 'pattern' => 'files/search',
'method' => 'GET|POST', 'method' => 'GET|POST',
'action' => function () { 'action' => function () {
$files = $this $files = $this
->site() ->site()
->index(true) ->index(true)
->filter('isReadable', true) ->filter('isReadable', true)
->files(); ->files();
if ($this->requestMethod() === 'GET') { if ($this->requestMethod() === 'GET') {
return $files->search($this->requestQuery('q')); return $files->search($this->requestQuery('q'));
} else { } else {
return $files->query($this->requestBody()); return $files->query($this->requestBody());
} }
} }
], ],
]; ];

View file

@ -4,43 +4,43 @@
* Roles Routes * Roles Routes
*/ */
return [ return [
[ [
'pattern' => 'languages', 'pattern' => 'languages',
'method' => 'GET', 'method' => 'GET',
'action' => function () { 'action' => function () {
return $this->kirby()->languages(); return $this->kirby()->languages();
} }
], ],
[ [
'pattern' => 'languages', 'pattern' => 'languages',
'method' => 'POST', 'method' => 'POST',
'action' => function () { 'action' => function () {
return $this->kirby()->languages()->create($this->requestBody()); return $this->kirby()->languages()->create($this->requestBody());
} }
], ],
[ [
'pattern' => 'languages/(:any)', 'pattern' => 'languages/(:any)',
'method' => 'GET', 'method' => 'GET',
'action' => function (string $code) { 'action' => function (string $code) {
return $this->kirby()->languages()->find($code); return $this->kirby()->languages()->find($code);
} }
], ],
[ [
'pattern' => 'languages/(:any)', 'pattern' => 'languages/(:any)',
'method' => 'PATCH', 'method' => 'PATCH',
'action' => function (string $code) { 'action' => function (string $code) {
if ($language = $this->kirby()->languages()->find($code)) { if ($language = $this->kirby()->languages()->find($code)) {
return $language->update($this->requestBody()); return $language->update($this->requestBody());
} }
} }
], ],
[ [
'pattern' => 'languages/(:any)', 'pattern' => 'languages/(:any)',
'method' => 'DELETE', 'method' => 'DELETE',
'action' => function (string $code) { 'action' => function (string $code) {
if ($language = $this->kirby()->languages()->find($code)) { if ($language = $this->kirby()->languages()->find($code)) {
return $language->delete(); return $language->delete();
} }
} }
] ]
]; ];

View file

@ -5,87 +5,40 @@
* Content Lock Routes * Content Lock Routes
*/ */
return [ return [
[ [
'pattern' => '(:all)/lock', 'pattern' => '(:all)/lock',
'method' => 'GET', 'method' => 'PATCH',
/** 'action' => function (string $path) {
* @deprecated 3.6.0 if ($lock = $this->parent($path)->lock()) {
* @todo Remove in 3.7.0 return $lock->create();
*/ }
'action' => function (string $path) { }
deprecated('The `GET (:all)/lock` API endpoint has been deprecated and will be removed in 3.7.0'); ],
[
if ($lock = $this->parent($path)->lock()) { 'pattern' => '(:all)/lock',
return [ 'method' => 'DELETE',
'supported' => true, 'action' => function (string $path) {
'locked' => $lock->get() if ($lock = $this->parent($path)->lock()) {
]; return $lock->remove();
} }
}
return [ ],
'supported' => false, [
'locked' => null 'pattern' => '(:all)/unlock',
]; 'method' => 'PATCH',
} 'action' => function (string $path) {
], if ($lock = $this->parent($path)->lock()) {
[ return $lock->unlock();
'pattern' => '(:all)/lock', }
'method' => 'PATCH', }
'action' => function (string $path) { ],
if ($lock = $this->parent($path)->lock()) { [
return $lock->create(); 'pattern' => '(:all)/unlock',
} 'method' => 'DELETE',
} 'action' => function (string $path) {
], if ($lock = $this->parent($path)->lock()) {
[ return $lock->resolve();
'pattern' => '(:all)/lock', }
'method' => 'DELETE', }
'action' => function (string $path) { ],
if ($lock = $this->parent($path)->lock()) {
return $lock->remove();
}
}
],
[
'pattern' => '(:all)/unlock',
'method' => 'GET',
/**
* @deprecated 3.6.0
* @todo Remove in 3.7.0
*/
'action' => function (string $path) {
deprecated('The `GET (:all)/unlock` API endpoint has been deprecated and will be removed in 3.7.0');
if ($lock = $this->parent($path)->lock()) {
return [
'supported' => true,
'unlocked' => $lock->isUnlocked()
];
}
return [
'supported' => false,
'unlocked' => null
];
}
],
[
'pattern' => '(:all)/unlock',
'method' => 'PATCH',
'action' => function (string $path) {
if ($lock = $this->parent($path)->lock()) {
return $lock->unlock();
}
}
],
[
'pattern' => '(:all)/unlock',
'method' => 'DELETE',
'action' => function (string $path) {
if ($lock = $this->parent($path)->lock()) {
return $lock->resolve();
}
}
],
]; ];

View file

@ -1,132 +1,121 @@
<?php <?php
/** /**
* Page Routes * Page Routes
*/ */
return [ return [
[ [
'pattern' => 'pages/(:any)', 'pattern' => 'pages/(:any)',
'method' => 'GET', 'method' => 'GET',
'action' => function (string $id) { 'action' => function (string $id) {
return $this->page($id); return $this->page($id);
} }
], ],
[ [
'pattern' => 'pages/(:any)', 'pattern' => 'pages/(:any)',
'method' => 'PATCH', 'method' => 'PATCH',
'action' => function (string $id) { 'action' => function (string $id) {
return $this->page($id)->update($this->requestBody(), $this->language(), true); return $this->page($id)->update($this->requestBody(), $this->language(), true);
} }
], ],
[ [
'pattern' => 'pages/(:any)', 'pattern' => 'pages/(:any)',
'method' => 'DELETE', 'method' => 'DELETE',
'action' => function (string $id) { 'action' => function (string $id) {
return $this->page($id)->delete($this->requestBody('force', false)); return $this->page($id)->delete($this->requestBody('force', false));
} }
], ],
[ [
'pattern' => 'pages/(:any)/blueprint', 'pattern' => 'pages/(:any)/blueprint',
'method' => 'GET', 'method' => 'GET',
'action' => function (string $id) { 'action' => function (string $id) {
return $this->page($id)->blueprint(); return $this->page($id)->blueprint();
} }
], ],
[ [
'pattern' => [ 'pattern' => 'pages/(:any)/blueprints',
'pages/(:any)/blueprints', 'method' => 'GET',
/** 'action' => function (string $id) {
* @deprecated return $this->page($id)->blueprints($this->requestQuery('section'));
* @todo remove in 3.7.0 }
*/ ],
'pages/(:any)/children/blueprints', [
], 'pattern' => 'pages/(:any)/children',
'method' => 'GET', 'method' => 'GET',
'action' => function (string $id) { 'action' => function (string $id) {
// @codeCoverageIgnoreStart return $this->pages($id, $this->requestQuery('status'));
if ($this->route->pattern() === 'pages/([a-zA-Z0-9\.\-_%= \+\@\(\)]+)/children/blueprints') { }
deprecated('`GET pages/(:any)/children/blueprints` API endpoint has been deprecated and will be removed in 3.7.0. Use `GET pages/(:any)/blueprints` instead'); ],
} [
// @codeCoverageIgnoreEnd 'pattern' => 'pages/(:any)/children',
return $this->page($id)->blueprints($this->requestQuery('section')); 'method' => 'POST',
} 'action' => function (string $id) {
], return $this->page($id)->createChild($this->requestBody());
[ }
'pattern' => 'pages/(:any)/children', ],
'method' => 'GET', [
'action' => function (string $id) { 'pattern' => 'pages/(:any)/children/search',
return $this->pages($id, $this->requestQuery('status')); 'method' => 'GET|POST',
} 'action' => function (string $id) {
], return $this->searchPages($id);
[ }
'pattern' => 'pages/(:any)/children', ],
'method' => 'POST', [
'action' => function (string $id) { 'pattern' => 'pages/(:any)/duplicate',
return $this->page($id)->createChild($this->requestBody()); 'method' => 'POST',
} 'action' => function (string $id) {
], return $this->page($id)->duplicate($this->requestBody('slug'), [
[ 'children' => $this->requestBody('children'),
'pattern' => 'pages/(:any)/children/search', 'files' => $this->requestBody('files'),
'method' => 'GET|POST', ]);
'action' => function (string $id) { }
return $this->searchPages($id); ],
} [
], 'pattern' => 'pages/(:any)/slug',
[ 'method' => 'PATCH',
'pattern' => 'pages/(:any)/duplicate', 'action' => function (string $id) {
'method' => 'POST', return $this->page($id)->changeSlug($this->requestBody('slug'));
'action' => function (string $id) { }
return $this->page($id)->duplicate($this->requestBody('slug'), [ ],
'children' => $this->requestBody('children'), [
'files' => $this->requestBody('files'), 'pattern' => 'pages/(:any)/status',
]); 'method' => 'PATCH',
} 'action' => function (string $id) {
], return $this->page($id)->changeStatus($this->requestBody('status'), $this->requestBody('position'));
[ }
'pattern' => 'pages/(:any)/slug', ],
'method' => 'PATCH', [
'action' => function (string $id) { 'pattern' => 'pages/(:any)/template',
return $this->page($id)->changeSlug($this->requestBody('slug')); 'method' => 'PATCH',
} 'action' => function (string $id) {
], return $this->page($id)->changeTemplate($this->requestBody('template'));
[ }
'pattern' => 'pages/(:any)/status', ],
'method' => 'PATCH', [
'action' => function (string $id) { 'pattern' => 'pages/(:any)/title',
return $this->page($id)->changeStatus($this->requestBody('status'), $this->requestBody('position')); 'method' => 'PATCH',
} 'action' => function (string $id) {
], return $this->page($id)->changeTitle($this->requestBody('title'));
[ }
'pattern' => 'pages/(:any)/template', ],
'method' => 'PATCH', [
'action' => function (string $id) { 'pattern' => 'pages/(:any)/sections/(:any)',
return $this->page($id)->changeTemplate($this->requestBody('template')); 'method' => 'GET',
} 'action' => function (string $id, string $sectionName) {
], if ($section = $this->page($id)->blueprint()->section($sectionName)) {
[ return $section->toResponse();
'pattern' => 'pages/(:any)/title', }
'method' => 'PATCH', }
'action' => function (string $id) { ],
return $this->page($id)->changeTitle($this->requestBody('title')); [
} 'pattern' => 'pages/(:any)/fields/(:any)/(:all?)',
], 'method' => 'ALL',
[ 'action' => function (string $id, string $fieldName, string $path = null) {
'pattern' => 'pages/(:any)/sections/(:any)', if ($page = $this->page($id)) {
'method' => 'GET', return $this->fieldApi($page, $fieldName, $path);
'action' => function (string $id, string $sectionName) { }
if ($section = $this->page($id)->blueprint()->section($sectionName)) { }
return $section->toResponse(); ],
}
}
],
[
'pattern' => 'pages/(:any)/fields/(:any)/(:all?)',
'method' => 'ALL',
'action' => function (string $id, string $fieldName, string $path = null) {
if ($page = $this->page($id)) {
return $this->fieldApi($page, $fieldName, $path);
}
}
],
]; ];

View file

@ -4,25 +4,27 @@
* Roles Routes * Roles Routes
*/ */
return [ return [
[ [
'pattern' => 'roles', 'pattern' => 'roles',
'method' => 'GET', 'method' => 'GET',
'action' => function () { 'action' => function () {
switch (get('canBe')) { $kirby = $this->kirby();
case 'changed':
return $this->kirby()->roles()->canBeChanged(); switch ($kirby->request()->get('canBe')) {
case 'created': case 'changed':
return $this->kirby()->roles()->canBeCreated(); return $kirby->roles()->canBeChanged();
default: case 'created':
return $this->kirby()->roles(); return $kirby->roles()->canBeCreated();
} default:
} return $kirby->roles();
], }
[ }
'pattern' => 'roles/(:any)', ],
'method' => 'GET', [
'action' => function (string $name) { 'pattern' => 'roles/(:any)',
return $this->kirby()->roles()->find($name); 'method' => 'GET',
} 'action' => function (string $name) {
] return $this->kirby()->roles()->find($name);
}
]
]; ];

View file

@ -1,115 +1,104 @@
<?php <?php
/** /**
* Site Routes * Site Routes
*/ */
return [ return [
[ [
'pattern' => 'site', 'pattern' => 'site',
'action' => function () { 'action' => function () {
return $this->site(); return $this->site();
} }
], ],
[ [
'pattern' => 'site', 'pattern' => 'site',
'method' => 'PATCH', 'method' => 'PATCH',
'action' => function () { 'action' => function () {
return $this->site()->update($this->requestBody(), $this->language(), true); return $this->site()->update($this->requestBody(), $this->language(), true);
} }
], ],
[ [
'pattern' => 'site/children', 'pattern' => 'site/children',
'method' => 'GET', 'method' => 'GET',
'action' => function () { 'action' => function () {
return $this->pages(null, $this->requestQuery('status')); return $this->pages(null, $this->requestQuery('status'));
} }
], ],
[ [
'pattern' => 'site/children', 'pattern' => 'site/children',
'method' => 'POST', 'method' => 'POST',
'action' => function () { 'action' => function () {
return $this->site()->createChild($this->requestBody()); return $this->site()->createChild($this->requestBody());
} }
], ],
[ [
'pattern' => 'site/children/search', 'pattern' => 'site/children/search',
'method' => 'GET|POST', 'method' => 'GET|POST',
'action' => function () { 'action' => function () {
return $this->searchPages(); return $this->searchPages();
} }
], ],
[ [
'pattern' => 'site/blueprint', 'pattern' => 'site/blueprint',
'method' => 'GET', 'method' => 'GET',
'action' => function () { 'action' => function () {
return $this->site()->blueprint(); return $this->site()->blueprint();
} }
], ],
[ [
'pattern' => [ 'pattern' => 'site/blueprints',
'site/blueprints', 'method' => 'GET',
/** 'action' => function () {
* @deprecated return $this->site()->blueprints($this->requestQuery('section'));
* @todo remove in 3.7.0 }
*/ ],
'site/children/blueprints', [
], 'pattern' => 'site/find',
'method' => 'GET', 'method' => 'POST',
'action' => function () { 'action' => function () {
// @codeCoverageIgnoreStart return $this->site()->find(false, ...$this->requestBody());
if ($this->route->pattern() === 'site/children/blueprints') { }
deprecated('`GET site/children/blueprints` API endpoint has been deprecated and will be removed in 3.7.0. Use `GET site/blueprints` instead.'); ],
} [
// @codeCoverageIgnoreEnd 'pattern' => 'site/title',
return $this->site()->blueprints($this->requestQuery('section')); 'method' => 'PATCH',
} 'action' => function () {
], return $this->site()->changeTitle($this->requestBody('title'));
[ }
'pattern' => 'site/find', ],
'method' => 'POST', [
'action' => function () { 'pattern' => 'site/search',
return $this->site()->find(false, ...$this->requestBody()); 'method' => 'GET|POST',
} 'action' => function () {
], $pages = $this
[ ->site()
'pattern' => 'site/title', ->index(true)
'method' => 'PATCH', ->filter('isReadable', true);
'action' => function () {
return $this->site()->changeTitle($this->requestBody('title'));
}
],
[
'pattern' => 'site/search',
'method' => 'GET|POST',
'action' => function () {
$pages = $this
->site()
->index(true)
->filter('isReadable', true);
if ($this->requestMethod() === 'GET') { if ($this->requestMethod() === 'GET') {
return $pages->search($this->requestQuery('q')); return $pages->search($this->requestQuery('q'));
} else { } else {
return $pages->query($this->requestBody()); return $pages->query($this->requestBody());
} }
} }
], ],
[ [
'pattern' => 'site/sections/(:any)', 'pattern' => 'site/sections/(:any)',
'method' => 'GET', 'method' => 'GET',
'action' => function (string $sectionName) { 'action' => function (string $sectionName) {
if ($section = $this->site()->blueprint()->section($sectionName)) { if ($section = $this->site()->blueprint()->section($sectionName)) {
return $section->toResponse(); return $section->toResponse();
} }
} }
], ],
[ [
'pattern' => 'site/fields/(:any)/(:all?)', 'pattern' => 'site/fields/(:any)/(:all?)',
'method' => 'ALL', 'method' => 'ALL',
'action' => function (string $fieldName, string $path = null) { 'action' => function (string $fieldName, string $path = null) {
return $this->fieldApi($this->site(), $fieldName, $path); return $this->fieldApi($this->site(), $fieldName, $path);
} }
] ]
]; ];

View file

@ -8,72 +8,72 @@ use Kirby\Exception\InvalidArgumentException;
*/ */
return [ return [
[ [
'pattern' => 'system', 'pattern' => 'system',
'method' => 'GET', 'method' => 'GET',
'auth' => false, 'auth' => false,
'action' => function () { 'action' => function () {
$system = $this->kirby()->system(); $system = $this->kirby()->system();
if ($this->kirby()->user()) { if ($this->kirby()->user()) {
return $system; return $system;
} else { } else {
if ($system->isOk() === true) { if ($system->isOk() === true) {
$info = $this->resolve($system)->view('login')->toArray(); $info = $this->resolve($system)->view('login')->toArray();
} else { } else {
$info = $this->resolve($system)->view('troubleshooting')->toArray(); $info = $this->resolve($system)->view('troubleshooting')->toArray();
} }
return [ return [
'status' => 'ok', 'status' => 'ok',
'data' => $info, 'data' => $info,
'type' => 'model' 'type' => 'model'
]; ];
} }
} }
], ],
[ [
'pattern' => 'system/register', 'pattern' => 'system/register',
'method' => 'POST', 'method' => 'POST',
'action' => function () { 'action' => function () {
return $this->kirby()->system()->register($this->requestBody('license'), $this->requestBody('email')); return $this->kirby()->system()->register($this->requestBody('license'), $this->requestBody('email'));
} }
], ],
[ [
'pattern' => 'system/install', 'pattern' => 'system/install',
'method' => 'POST', 'method' => 'POST',
'auth' => false, 'auth' => false,
'action' => function () { 'action' => function () {
$system = $this->kirby()->system(); $system = $this->kirby()->system();
$auth = $this->kirby()->auth(); $auth = $this->kirby()->auth();
// csrf token check // csrf token check
if ($auth->type() === 'session' && $auth->csrf() === false) { if ($auth->type() === 'session' && $auth->csrf() === false) {
throw new InvalidArgumentException('Invalid CSRF token'); throw new InvalidArgumentException('Invalid CSRF token');
} }
if ($system->isOk() === false) { if ($system->isOk() === false) {
throw new Exception('The server is not setup correctly'); throw new Exception('The server is not setup correctly');
} }
if ($system->isInstallable() === false) { if ($system->isInstallable() === false) {
throw new Exception('The Panel cannot be installed'); throw new Exception('The Panel cannot be installed');
} }
if ($system->isInstalled() === true) { if ($system->isInstalled() === true) {
throw new Exception('The Panel is already installed'); throw new Exception('The Panel is already installed');
} }
// create the first user // create the first user
$user = $this->users()->create($this->requestBody()); $user = $this->users()->create($this->requestBody());
$token = $user->login($this->requestBody('password')); $token = $user->login($this->requestBody('password'));
return [ return [
'status' => 'ok', 'status' => 'ok',
'token' => $token, 'token' => $token,
'user' => $this->resolve($user)->view('auth')->toArray() 'user' => $this->resolve($user)->view('auth')->toArray()
]; ];
} }
] ]
]; ];

View file

@ -4,21 +4,21 @@
* Translations Routes * Translations Routes
*/ */
return [ return [
[ [
'pattern' => 'translations', 'pattern' => 'translations',
'method' => 'GET', 'method' => 'GET',
'auth' => false, 'auth' => false,
'action' => function () { 'action' => function () {
return $this->kirby()->translations(); return $this->kirby()->translations();
} }
], ],
[ [
'pattern' => 'translations/(:any)', 'pattern' => 'translations/(:any)',
'method' => 'GET', 'method' => 'GET',
'auth' => false, 'auth' => false,
'action' => function (string $code) { 'action' => function (string $code) {
return $this->kirby()->translations()->find($code); return $this->kirby()->translations()->find($code);
} }
] ]
]; ];

View file

@ -6,202 +6,202 @@ use Kirby\Filesystem\F;
* User Routes * User Routes
*/ */
return [ return [
[ [
'pattern' => 'users', 'pattern' => 'users',
'method' => 'GET', 'method' => 'GET',
'action' => function () { 'action' => function () {
return $this->users()->sort('username', 'asc', 'email', 'asc'); return $this->users()->sort('username', 'asc', 'email', 'asc');
} }
], ],
[ [
'pattern' => 'users', 'pattern' => 'users',
'method' => 'POST', 'method' => 'POST',
'action' => function () { 'action' => function () {
return $this->users()->create($this->requestBody()); return $this->users()->create($this->requestBody());
} }
], ],
[ [
'pattern' => 'users/search', 'pattern' => 'users/search',
'method' => 'GET|POST', 'method' => 'GET|POST',
'action' => function () { 'action' => function () {
if ($this->requestMethod() === 'GET') { if ($this->requestMethod() === 'GET') {
return $this->users()->search($this->requestQuery('q')); return $this->users()->search($this->requestQuery('q'));
} else { } else {
return $this->users()->query($this->requestBody()); return $this->users()->query($this->requestBody());
} }
} }
], ],
[ [
'pattern' => [ 'pattern' => [
'(account)', '(account)',
'users/(:any)', 'users/(:any)',
], ],
'method' => 'GET', 'method' => 'GET',
'action' => function (string $id) { 'action' => function (string $id) {
return $this->user($id); return $this->user($id);
} }
], ],
[ [
'pattern' => [ 'pattern' => [
'(account)', '(account)',
'users/(:any)', 'users/(:any)',
], ],
'method' => 'PATCH', 'method' => 'PATCH',
'action' => function (string $id) { 'action' => function (string $id) {
return $this->user($id)->update($this->requestBody(), $this->language(), true); return $this->user($id)->update($this->requestBody(), $this->language(), true);
} }
], ],
[ [
'pattern' => [ 'pattern' => [
'(account)', '(account)',
'users/(:any)', 'users/(:any)',
], ],
'method' => 'DELETE', 'method' => 'DELETE',
'action' => function (string $id) { 'action' => function (string $id) {
return $this->user($id)->delete(); return $this->user($id)->delete();
} }
], ],
[ [
'pattern' => [ 'pattern' => [
'(account)/avatar', '(account)/avatar',
'users/(:any)/avatar', 'users/(:any)/avatar',
], ],
'method' => 'GET', 'method' => 'GET',
'action' => function (string $id) { 'action' => function (string $id) {
return $this->user($id)->avatar(); return $this->user($id)->avatar();
} }
], ],
// @codeCoverageIgnoreStart // @codeCoverageIgnoreStart
[ [
'pattern' => [ 'pattern' => [
'(account)/avatar', '(account)/avatar',
'users/(:any)/avatar', 'users/(:any)/avatar',
], ],
'method' => 'POST', 'method' => 'POST',
'action' => function (string $id) { 'action' => function (string $id) {
if ($avatar = $this->user($id)->avatar()) { if ($avatar = $this->user($id)->avatar()) {
$avatar->delete(); $avatar->delete();
} }
return $this->upload(function ($source, $filename) use ($id) { return $this->upload(function ($source, $filename) use ($id) {
return $this->user($id)->createFile([ return $this->user($id)->createFile([
'filename' => 'profile.' . F::extension($filename), 'filename' => 'profile.' . F::extension($filename),
'template' => 'avatar', 'template' => 'avatar',
'source' => $source 'source' => $source
]); ]);
}, $single = true); }, $single = true);
} }
], ],
// @codeCoverageIgnoreEnd // @codeCoverageIgnoreEnd
[ [
'pattern' => [ 'pattern' => [
'(account)/avatar', '(account)/avatar',
'users/(:any)/avatar', 'users/(:any)/avatar',
], ],
'method' => 'DELETE', 'method' => 'DELETE',
'action' => function (string $id) { 'action' => function (string $id) {
return $this->user($id)->avatar()->delete(); return $this->user($id)->avatar()->delete();
} }
], ],
[ [
'pattern' => [ 'pattern' => [
'(account)/blueprint', '(account)/blueprint',
'users/(:any)/blueprint', 'users/(:any)/blueprint',
], ],
'method' => 'GET', 'method' => 'GET',
'action' => function (string $id) { 'action' => function (string $id) {
return $this->user($id)->blueprint(); return $this->user($id)->blueprint();
} }
], ],
[ [
'pattern' => [ 'pattern' => [
'(account)/blueprints', '(account)/blueprints',
'users/(:any)/blueprints', 'users/(:any)/blueprints',
], ],
'method' => 'GET', 'method' => 'GET',
'action' => function (string $id) { 'action' => function (string $id) {
return $this->user($id)->blueprints($this->requestQuery('section')); return $this->user($id)->blueprints($this->requestQuery('section'));
} }
], ],
[ [
'pattern' => [ 'pattern' => [
'(account)/email', '(account)/email',
'users/(:any)/email', 'users/(:any)/email',
], ],
'method' => 'PATCH', 'method' => 'PATCH',
'action' => function (string $id) { 'action' => function (string $id) {
return $this->user($id)->changeEmail($this->requestBody('email')); return $this->user($id)->changeEmail($this->requestBody('email'));
} }
], ],
[ [
'pattern' => [ 'pattern' => [
'(account)/language', '(account)/language',
'users/(:any)/language', 'users/(:any)/language',
], ],
'method' => 'PATCH', 'method' => 'PATCH',
'action' => function (string $id) { 'action' => function (string $id) {
return $this->user($id)->changeLanguage($this->requestBody('language')); return $this->user($id)->changeLanguage($this->requestBody('language'));
} }
], ],
[ [
'pattern' => [ 'pattern' => [
'(account)/name', '(account)/name',
'users/(:any)/name', 'users/(:any)/name',
], ],
'method' => 'PATCH', 'method' => 'PATCH',
'action' => function (string $id) { 'action' => function (string $id) {
return $this->user($id)->changeName($this->requestBody('name')); return $this->user($id)->changeName($this->requestBody('name'));
} }
], ],
[ [
'pattern' => [ 'pattern' => [
'(account)/password', '(account)/password',
'users/(:any)/password', 'users/(:any)/password',
], ],
'method' => 'PATCH', 'method' => 'PATCH',
'action' => function (string $id) { 'action' => function (string $id) {
return $this->user($id)->changePassword($this->requestBody('password')); return $this->user($id)->changePassword($this->requestBody('password'));
} }
], ],
[ [
'pattern' => [ 'pattern' => [
'(account)/role', '(account)/role',
'users/(:any)/role', 'users/(:any)/role',
], ],
'method' => 'PATCH', 'method' => 'PATCH',
'action' => function (string $id) { 'action' => function (string $id) {
return $this->user($id)->changeRole($this->requestBody('role')); return $this->user($id)->changeRole($this->requestBody('role'));
} }
], ],
[ [
'pattern' => [ 'pattern' => [
'(account)/roles', '(account)/roles',
'users/(:any)/roles', 'users/(:any)/roles',
], ],
'action' => function (string $id) { 'action' => function (string $id) {
return $this->user($id)->roles(); return $this->user($id)->roles();
} }
], ],
[ [
'pattern' => [ 'pattern' => [
'(account)/sections/(:any)', '(account)/sections/(:any)',
'users/(:any)/sections/(:any)', 'users/(:any)/sections/(:any)',
], ],
'method' => 'GET', 'method' => 'GET',
'action' => function (string $id, string $sectionName) { 'action' => function (string $id, string $sectionName) {
if ($section = $this->user($id)->blueprint()->section($sectionName)) { if ($section = $this->user($id)->blueprint()->section($sectionName)) {
return $section->toResponse(); return $section->toResponse();
} }
} }
], ],
[ [
'pattern' => [ 'pattern' => [
'(account)/fields/(:any)/(:all?)', '(account)/fields/(:any)/(:all?)',
'users/(:any)/fields/(:any)/(:all?)', 'users/(:any)/fields/(:any)/(:all?)',
], ],
'method' => 'ALL', 'method' => 'ALL',
'action' => function (string $id, string $fieldName, string $path = null) { 'action' => function (string $id, string $fieldName, string $path = null) {
return $this->fieldApi($this->user($id), $fieldName, $path); return $this->fieldApi($this->user($id), $fieldName, $path);
} }
], ],
]; ];

View file

@ -1,12 +1,14 @@
<?php <?php
use Kirby\Toolkit\I18n;
return function () { return function () {
return [ return [
'icon' => 'account', 'icon' => 'account',
'label' => t('view.account'), 'label' => I18n::translate('view.account'),
'search' => 'users', 'search' => 'users',
'dialogs' => require __DIR__ . '/account/dialogs.php', 'dialogs' => require __DIR__ . '/account/dialogs.php',
'dropdowns' => require __DIR__ . '/account/dropdowns.php', 'dropdowns' => require __DIR__ . '/account/dropdowns.php',
'views' => require __DIR__ . '/account/views.php' 'views' => require __DIR__ . '/account/views.php'
]; ];
}; };

View file

@ -4,67 +4,67 @@ $dialogs = require __DIR__ . '/../users/dialogs.php';
return [ return [
// change email // change email
'account.changeEmail' => [ 'account.changeEmail' => [
'pattern' => '(account)/changeEmail', 'pattern' => '(account)/changeEmail',
'load' => $dialogs['user.changeEmail']['load'], 'load' => $dialogs['user.changeEmail']['load'],
'submit' => $dialogs['user.changeEmail']['submit'], 'submit' => $dialogs['user.changeEmail']['submit'],
], ],
// change language // change language
'account.changeLanguage' => [ 'account.changeLanguage' => [
'pattern' => '(account)/changeLanguage', 'pattern' => '(account)/changeLanguage',
'load' => $dialogs['user.changeLanguage']['load'], 'load' => $dialogs['user.changeLanguage']['load'],
'submit' => $dialogs['user.changeLanguage']['submit'], 'submit' => $dialogs['user.changeLanguage']['submit'],
], ],
// change name // change name
'account.changeName' => [ 'account.changeName' => [
'pattern' => '(account)/changeName', 'pattern' => '(account)/changeName',
'load' => $dialogs['user.changeName']['load'], 'load' => $dialogs['user.changeName']['load'],
'submit' => $dialogs['user.changeName']['submit'], 'submit' => $dialogs['user.changeName']['submit'],
], ],
// change password // change password
'account.changePassword' => [ 'account.changePassword' => [
'pattern' => '(account)/changePassword', 'pattern' => '(account)/changePassword',
'load' => $dialogs['user.changePassword']['load'], 'load' => $dialogs['user.changePassword']['load'],
'submit' => $dialogs['user.changePassword']['submit'], 'submit' => $dialogs['user.changePassword']['submit'],
], ],
// change role // change role
'account.changeRole' => [ 'account.changeRole' => [
'pattern' => '(account)/changeRole', 'pattern' => '(account)/changeRole',
'load' => $dialogs['user.changeRole']['load'], 'load' => $dialogs['user.changeRole']['load'],
'submit' => $dialogs['user.changeRole']['submit'], 'submit' => $dialogs['user.changeRole']['submit'],
], ],
// delete // delete
'account.delete' => [ 'account.delete' => [
'pattern' => '(account)/delete', 'pattern' => '(account)/delete',
'load' => $dialogs['user.delete']['load'], 'load' => $dialogs['user.delete']['load'],
'submit' => $dialogs['user.delete']['submit'], 'submit' => $dialogs['user.delete']['submit'],
], ],
// change file name // change file name
'account.file.changeName' => [ 'account.file.changeName' => [
'pattern' => '(account)/files/(:any)/changeName', 'pattern' => '(account)/files/(:any)/changeName',
'load' => $dialogs['user.file.changeName']['load'], 'load' => $dialogs['user.file.changeName']['load'],
'submit' => $dialogs['user.file.changeName']['submit'], 'submit' => $dialogs['user.file.changeName']['submit'],
], ],
// change file sort // change file sort
'account.file.changeSort' => [ 'account.file.changeSort' => [
'pattern' => '(account)/files/(:any)/changeSort', 'pattern' => '(account)/files/(:any)/changeSort',
'load' => $dialogs['user.file.changeSort']['load'], 'load' => $dialogs['user.file.changeSort']['load'],
'submit' => $dialogs['user.file.changeSort']['submit'], 'submit' => $dialogs['user.file.changeSort']['submit'],
], ],
// delete // delete
'account.file.delete' => [ 'account.file.delete' => [
'pattern' => '(account)/files/(:any)/delete', 'pattern' => '(account)/files/(:any)/delete',
'load' => $dialogs['user.file.delete']['load'], 'load' => $dialogs['user.file.delete']['load'],
'submit' => $dialogs['user.file.delete']['submit'], 'submit' => $dialogs['user.file.delete']['submit'],
], ],
]; ];

View file

@ -3,12 +3,12 @@
$dropdowns = require __DIR__ . '/../users/dropdowns.php'; $dropdowns = require __DIR__ . '/../users/dropdowns.php';
return [ return [
'account' => [ 'account' => [
'pattern' => '(account)', 'pattern' => '(account)',
'options' => $dropdowns['user']['options'] 'options' => $dropdowns['user']['options']
], ],
'account.file' => [ 'account.file' => [
'pattern' => '(account)/files/(:any)', 'pattern' => '(account)/files/(:any)',
'options' => $dropdowns['user.file']['options'] 'options' => $dropdowns['user.file']['options']
], ],
]; ];

View file

@ -1,34 +1,24 @@
<?php <?php
use Kirby\Cms\App;
use Kirby\Cms\Find; use Kirby\Cms\Find;
use Kirby\Panel\Panel;
return [ return [
'account' => [ 'account' => [
'pattern' => 'account', 'pattern' => 'account',
'action' => fn () => [ 'action' => fn () => [
'component' => 'k-account-view', 'component' => 'k-account-view',
'props' => kirby()->user()->panel()->props(), 'props' => App::instance()->user()->panel()->props(),
], ],
], ],
'account.file' => [ 'account.file' => [
'pattern' => 'account/files/(:any)', 'pattern' => 'account/files/(:any)',
'action' => function (string $filename) { 'action' => function (string $filename) {
return Find::file('account', $filename)->panel()->view(); return Find::file('account', $filename)->panel()->view();
} }
], ],
'account.logout' => [ 'account.password' => [
'pattern' => 'logout', 'pattern' => 'reset-password',
'auth' => false, 'action' => fn () => ['component' => 'k-reset-password-view']
'action' => function () { ]
if ($user = kirby()->user()) {
$user->logout();
}
Panel::go('login');
},
],
'account.password' => [
'pattern' => 'reset-password',
'action' => fn () => ['component' => 'k-reset-password-view']
]
]; ];

View file

@ -4,6 +4,7 @@ use Kirby\Cms\Find;
use Kirby\Panel\Field; use Kirby\Panel\Field;
use Kirby\Panel\Panel; use Kirby\Panel\Panel;
use Kirby\Toolkit\Escape; use Kirby\Toolkit\Escape;
use Kirby\Toolkit\I18n;
/** /**
* Shared file dialogs * Shared file dialogs
@ -13,119 +14,119 @@ use Kirby\Toolkit\Escape;
* the appropriate routes in the areas. * the appropriate routes in the areas.
*/ */
return [ return [
'changeName' => [ 'changeName' => [
'load' => function (string $path, string $filename) { 'load' => function (string $path, string $filename) {
$file = Find::file($path, $filename); $file = Find::file($path, $filename);
return [ return [
'component' => 'k-form-dialog', 'component' => 'k-form-dialog',
'props' => [ 'props' => [
'fields' => [ 'fields' => [
'name' => [ 'name' => [
'label' => t('name'), 'label' => I18n::translate('name'),
'type' => 'slug', 'type' => 'slug',
'required' => true, 'required' => true,
'icon' => 'title', 'icon' => 'title',
'allow' => '@._-', 'allow' => '@._-',
'after' => '.' . $file->extension(), 'after' => '.' . $file->extension(),
'preselect' => true 'preselect' => true
] ]
], ],
'submitButton' => t('rename'), 'submitButton' => I18n::translate('rename'),
'value' => [ 'value' => [
'name' => $file->name(), 'name' => $file->name(),
] ]
] ]
]; ];
}, },
'submit' => function (string $path, string $filename) { 'submit' => function (string $path, string $filename) {
$file = Find::file($path, $filename); $file = Find::file($path, $filename);
$renamed = $file->changeName(get('name')); $renamed = $file->changeName($file->kirby()->request()->get('name'));
$oldUrl = $file->panel()->url(true); $oldUrl = $file->panel()->url(true);
$newUrl = $renamed->panel()->url(true); $newUrl = $renamed->panel()->url(true);
$response = [ $response = [
'event' => 'file.changeName', 'event' => 'file.changeName',
'dispatch' => [ 'dispatch' => [
'content/move' => [ 'content/move' => [
$oldUrl, $oldUrl,
$newUrl $newUrl
] ]
], ],
]; ];
// check for a necessary redirect after the filename has changed // check for a necessary redirect after the filename has changed
if (Panel::referrer() === $oldUrl && $oldUrl !== $newUrl) { if (Panel::referrer() === $oldUrl && $oldUrl !== $newUrl) {
$response['redirect'] = $newUrl; $response['redirect'] = $newUrl;
} }
return $response; return $response;
} }
], ],
'changeSort' => [ 'changeSort' => [
'load' => function (string $path, string $filename) { 'load' => function (string $path, string $filename) {
$file = Find::file($path, $filename); $file = Find::file($path, $filename);
return [ return [
'component' => 'k-form-dialog', 'component' => 'k-form-dialog',
'props' => [ 'props' => [
'fields' => [ 'fields' => [
'position' => Field::filePosition($file) 'position' => Field::filePosition($file)
], ],
'submitButton' => t('change'), 'submitButton' => I18n::translate('change'),
'value' => [ 'value' => [
'position' => $file->sort()->isEmpty() ? $file->siblings(false)->count() + 1 : $file->sort()->toInt(), 'position' => $file->sort()->isEmpty() ? $file->siblings(false)->count() + 1 : $file->sort()->toInt(),
] ]
] ]
]; ];
}, },
'submit' => function (string $path, string $filename) { 'submit' => function (string $path, string $filename) {
$file = Find::file($path, $filename); $file = Find::file($path, $filename);
$files = $file->siblings()->sorted(); $files = $file->siblings()->sorted();
$ids = $files->keys(); $ids = $files->keys();
$newIndex = (int)(get('position')) - 1; $newIndex = (int)($file->kirby()->request()->get('position')) - 1;
$oldIndex = $files->indexOf($file); $oldIndex = $files->indexOf($file);
array_splice($ids, $oldIndex, 1); array_splice($ids, $oldIndex, 1);
array_splice($ids, $newIndex, 0, $file->id()); array_splice($ids, $newIndex, 0, $file->id());
$files->changeSort($ids); $files->changeSort($ids);
return [ return [
'event' => 'file.sort', 'event' => 'file.sort',
]; ];
} }
], ],
'delete' => [ 'delete' => [
'load' => function (string $path, string $filename) { 'load' => function (string $path, string $filename) {
$file = Find::file($path, $filename); $file = Find::file($path, $filename);
return [ return [
'component' => 'k-remove-dialog', 'component' => 'k-remove-dialog',
'props' => [ 'props' => [
'text' => tt('file.delete.confirm', [ 'text' => I18n::template('file.delete.confirm', [
'filename' => Escape::html($file->filename()) 'filename' => Escape::html($file->filename())
]), ]),
] ]
]; ];
}, },
'submit' => function (string $path, string $filename) { 'submit' => function (string $path, string $filename) {
$file = Find::file($path, $filename); $file = Find::file($path, $filename);
$redirect = false; $redirect = false;
$referrer = Panel::referrer(); $referrer = Panel::referrer();
$url = $file->panel()->url(true); $url = $file->panel()->url(true);
$file->delete(); $file->delete();
// redirect to the parent model URL // redirect to the parent model URL
// if the dialog has been opened in the file view // if the dialog has been opened in the file view
if ($referrer === $url) { if ($referrer === $url) {
$redirect = $file->parent()->panel()->url(true); $redirect = $file->parent()->panel()->url(true);
} }
return [ return [
'event' => 'file.delete', 'event' => 'file.delete',
'dispatch' => ['content/remove' => [$url]], 'dispatch' => ['content/remove' => [$url]],
'redirect' => $redirect 'redirect' => $redirect
]; ];
} }
], ],
]; ];

View file

@ -3,7 +3,7 @@
use Kirby\Cms\Find; use Kirby\Cms\Find;
return [ return [
'file' => function (string $parent, string $filename) { 'file' => function (string $parent, string $filename) {
return Find::file($parent, $filename)->panel()->dropdown(); return Find::file($parent, $filename)->panel()->dropdown();
} }
]; ];

View file

@ -1,39 +1,40 @@
<?php <?php
use Kirby\Panel\Panel; use Kirby\Panel\Panel;
use Kirby\Toolkit\I18n;
return function ($kirby) { return function ($kirby) {
return [ return [
'icon' => 'settings', 'icon' => 'settings',
'label' => t('view.installation'), 'label' => I18n::translate('view.installation'),
'views' => [ 'views' => [
'installation' => [ 'installation' => [
'pattern' => 'installation', 'pattern' => 'installation',
'auth' => false, 'auth' => false,
'action' => function () use ($kirby) { 'action' => function () use ($kirby) {
$system = $kirby->system(); $system = $kirby->system();
return [ return [
'component' => 'k-installation-view', 'component' => 'k-installation-view',
'props' => [ 'props' => [
'isInstallable' => $system->isInstallable(), 'isInstallable' => $system->isInstallable(),
'isInstalled' => $system->isInstalled(), 'isInstalled' => $system->isInstalled(),
'isOk' => $system->isOk(), 'isOk' => $system->isOk(),
'requirements' => $system->status(), 'requirements' => $system->status(),
'translations' => $kirby->translations()->values(function ($translation) { 'translations' => $kirby->translations()->values(function ($translation) {
return [ return [
'text' => $translation->name(), 'text' => $translation->name(),
'value' => $translation->code(), 'value' => $translation->code(),
]; ];
}), }),
] ]
]; ];
} }
], ],
'installation.fallback' => [ 'installation.fallback' => [
'pattern' => '(:all)', 'pattern' => '(:all)',
'auth' => false, 'auth' => false,
'action' => fn () => Panel::go('installation') 'action' => fn () => Panel::go('installation')
] ]
] ]
]; ];
}; };

View file

@ -1,11 +1,13 @@
<?php <?php
use Kirby\Toolkit\I18n;
return function ($kirby) { return function ($kirby) {
return [ return [
'icon' => 'globe', 'icon' => 'globe',
'label' => t('view.languages'), 'label' => I18n::translate('view.languages'),
'menu' => true, 'menu' => true,
'dialogs' => require __DIR__ . '/languages/dialogs.php', 'dialogs' => require __DIR__ . '/languages/dialogs.php',
'views' => require __DIR__ . '/languages/views.php' 'views' => require __DIR__ . '/languages/views.php'
]; ];
}; };

View file

@ -1,149 +1,155 @@
<?php <?php
use Kirby\Cms\App;
use Kirby\Cms\Find; use Kirby\Cms\Find;
use Kirby\Panel\Field; use Kirby\Panel\Field;
use Kirby\Toolkit\A; use Kirby\Toolkit\A;
use Kirby\Toolkit\Escape; use Kirby\Toolkit\Escape;
use Kirby\Toolkit\I18n;
$languageDialogFields = [ $languageDialogFields = [
'name' => [ 'name' => [
'label' => t('language.name'), 'label' => I18n::translate('language.name'),
'type' => 'text', 'type' => 'text',
'required' => true, 'required' => true,
'icon' => 'title' 'icon' => 'title'
], ],
'code' => [ 'code' => [
'label' => t('language.code'), 'label' => I18n::translate('language.code'),
'type' => 'text', 'type' => 'text',
'required' => true, 'required' => true,
'counter' => false, 'counter' => false,
'icon' => 'globe', 'icon' => 'globe',
'width' => '1/2' 'width' => '1/2'
], ],
'direction' => [ 'direction' => [
'label' => t('language.direction'), 'label' => I18n::translate('language.direction'),
'type' => 'select', 'type' => 'select',
'required' => true, 'required' => true,
'empty' => false, 'empty' => false,
'options' => [ 'options' => [
['value' => 'ltr', 'text' => t('language.direction.ltr')], ['value' => 'ltr', 'text' => I18n::translate('language.direction.ltr')],
['value' => 'rtl', 'text' => t('language.direction.rtl')] ['value' => 'rtl', 'text' => I18n::translate('language.direction.rtl')]
], ],
'width' => '1/2' 'width' => '1/2'
], ],
'locale' => [ 'locale' => [
'label' => t('language.locale'), 'label' => I18n::translate('language.locale'),
'type' => 'text', 'type' => 'text',
], ],
]; ];
return [ return [
// create language // create language
'language.create' => [ 'language.create' => [
'pattern' => 'languages/create', 'pattern' => 'languages/create',
'load' => function () use ($languageDialogFields) { 'load' => function () use ($languageDialogFields) {
return [ return [
'component' => 'k-language-dialog', 'component' => 'k-language-dialog',
'props' => [ 'props' => [
'fields' => $languageDialogFields, 'fields' => $languageDialogFields,
'submitButton' => t('language.create'), 'submitButton' => I18n::translate('language.create'),
'value' => [ 'value' => [
'code' => '', 'code' => '',
'direction' => 'ltr', 'direction' => 'ltr',
'locale' => '', 'locale' => '',
'name' => '', 'name' => '',
] ]
] ]
]; ];
}, },
'submit' => function () { 'submit' => function () {
kirby()->languages()->create([ $kirby = App::instance();
'code' => get('code'),
'direction' => get('direction'),
'locale' => get('locale'),
'name' => get('name'),
]);
return [
'event' => 'language.create'
];
}
],
// delete language $data = $kirby->request()->get([
'language.delete' => [ 'code',
'pattern' => 'languages/(:any)/delete', 'direction',
'load' => function (string $id) { 'locale',
$language = Find::language($id); 'name'
return [ ]);
'component' => 'k-remove-dialog', $kirby->languages()->create($data);
'props' => [
'text' => tt('language.delete.confirm', [
'name' => Escape::html($language->name())
])
]
];
},
'submit' => function (string $id) {
Find::language($id)->delete();
return [
'event' => 'language.delete',
];
}
],
// update language return [
'language.update' => [ 'event' => 'language.create'
'pattern' => 'languages/(:any)/update', ];
'load' => function (string $id) use ($languageDialogFields) { }
$language = Find::language($id); ],
$fields = $languageDialogFields;
$locale = $language->locale();
// use the first locale key if there's only one // delete language
if (count($locale) === 1) { 'language.delete' => [
$locale = A::first($locale); 'pattern' => 'languages/(:any)/delete',
} 'load' => function (string $id) {
$language = Find::language($id);
return [
'component' => 'k-remove-dialog',
'props' => [
'text' => I18n::template('language.delete.confirm', [
'name' => Escape::html($language->name())
])
]
];
},
'submit' => function (string $id) {
Find::language($id)->delete();
return [
'event' => 'language.delete',
];
}
],
// the code of an existing language cannot be changed // update language
$fields['code']['disabled'] = true; 'language.update' => [
'pattern' => 'languages/(:any)/update',
'load' => function (string $id) use ($languageDialogFields) {
$language = Find::language($id);
$fields = $languageDialogFields;
$locale = $language->locale();
// if the locale settings is more complex than just a // use the first locale key if there's only one
// single string, the text field won't do it anymore. if (count($locale) === 1) {
// Changes can only be made in the language file and $locale = A::first($locale);
// we display a warning box instead. }
if (is_array($locale) === true) {
$fields['locale'] = [
'label' => $fields['locale']['label'],
'type' => 'info',
'text' => t('language.locale.warning')
];
}
return [ // the code of an existing language cannot be changed
'component' => 'k-language-dialog', $fields['code']['disabled'] = true;
'props' => [
'fields' => $fields, // if the locale settings is more complex than just a
'submitButton' => t('save'), // single string, the text field won't do it anymore.
'value' => [ // Changes can only be made in the language file and
'code' => $language->code(), // we display a warning box instead.
'direction' => $language->direction(), if (is_array($locale) === true) {
'locale' => $locale, $fields['locale'] = [
'name' => $language->name(), 'label' => $fields['locale']['label'],
'rules' => $language->rules(), 'type' => 'info',
] 'text' => I18n::translate('language.locale.warning')
] ];
]; }
},
'submit' => function (string $id) { return [
$language = Find::language($id)->update([ 'component' => 'k-language-dialog',
'direction' => get('direction'), 'props' => [
'locale' => get('locale'), 'fields' => $fields,
'name' => get('name'), 'submitButton' => I18n::translate('save'),
]); 'value' => [
return [ 'code' => $language->code(),
'event' => 'language.update' 'direction' => $language->direction(),
]; 'locale' => $locale,
} 'name' => $language->name(),
], 'rules' => $language->rules(),
]
]
];
},
'submit' => function (string $id) {
$kirby = App::instance();
$data = $kirby->request()->get(['direction', 'locale', 'name']);
$language = Find::language($id)->update($data);
return [
'event' => 'language.update'
];
}
],
]; ];

View file

@ -1,24 +1,25 @@
<?php <?php
use Kirby\Cms\App;
use Kirby\Toolkit\Escape; use Kirby\Toolkit\Escape;
return [ return [
'languages' => [ 'languages' => [
'pattern' => 'languages', 'pattern' => 'languages',
'action' => function () { 'action' => function () {
$kirby = kirby(); $kirby = App::instance();
return [ return [
'component' => 'k-languages-view', 'component' => 'k-languages-view',
'props' => [ 'props' => [
'languages' => $kirby->languages()->values(fn ($language) => [ 'languages' => $kirby->languages()->values(fn ($language) => [
'default' => $language->isDefault(), 'default' => $language->isDefault(),
'id' => $language->code(), 'id' => $language->code(),
'info' => Escape::html($language->code()), 'info' => Escape::html($language->code()),
'text' => Escape::html($language->name()), 'text' => Escape::html($language->name()),
]) ])
] ]
]; ];
} }
], ],
]; ];

View file

@ -1,43 +1,44 @@
<?php <?php
use Kirby\Panel\Panel; use Kirby\Panel\Panel;
use Kirby\Toolkit\I18n;
return function ($kirby) { return function ($kirby) {
return [ return [
'icon' => 'user', 'icon' => 'user',
'label' => t('login'), 'label' => I18n::translate('login'),
'views' => [ 'views' => [
'login' => [ 'login' => [
'pattern' => 'login', 'pattern' => 'login',
'auth' => false, 'auth' => false,
'action' => function () use ($kirby) { 'action' => function () use ($kirby) {
$system = $kirby->system(); $system = $kirby->system();
$status = $kirby->auth()->status(); $status = $kirby->auth()->status();
return [ return [
'component' => 'k-login-view', 'component' => 'k-login-view',
'props' => [ 'props' => [
'methods' => array_keys($system->loginMethods()), 'methods' => array_keys($system->loginMethods()),
'pending' => [ 'pending' => [
'email' => $status->email(), 'email' => $status->email(),
'challenge' => $status->challenge() 'challenge' => $status->challenge()
] ]
], ],
]; ];
} }
], ],
'login.fallback' => [ 'login.fallback' => [
'pattern' => '(:all)', 'pattern' => '(:all)',
'auth' => false, 'auth' => false,
'action' => function ($path) use ($kirby) { 'action' => function ($path) use ($kirby) {
/** /**
* Store the current path in the session * Store the current path in the session
* Once the user is logged in, the path will * Once the user is logged in, the path will
* be used to redirect to that view again * be used to redirect to that view again
*/ */
$kirby->session()->set('panel.path', $path); $kirby->session()->set('panel.path', $path);
Panel::go('login'); Panel::go('login');
} }
] ]
] ]
]; ];
}; };

View file

@ -0,0 +1,21 @@
<?php
use Kirby\Panel\Panel;
use Kirby\Toolkit\I18n;
return function ($kirby) {
return [
'icon' => 'user',
'label' => I18n::translate('logout'),
'views' => [
'logout' => [
'pattern' => 'logout',
'auth' => false,
'action' => function () use ($kirby) {
$kirby->auth()->logout();
Panel::go('login');
},
]
]
];
};

View file

@ -1,17 +1,18 @@
<?php <?php
use Kirby\Toolkit\I18n;
return function ($kirby) { return function ($kirby) {
return [ return [
'breadcrumbLabel' => function () use ($kirby) { 'breadcrumbLabel' => function () use ($kirby) {
return $kirby->site()->title()->or(t('view.site'))->toString(); return $kirby->site()->title()->or(I18n::translate('view.site'))->toString();
}, },
'icon' => 'home', 'icon' => 'home',
'label' => $kirby->site()->blueprint()->title() ?? t('view.site'), 'label' => $kirby->site()->blueprint()->title() ?? I18n::translate('view.site'),
'menu' => true, 'menu' => true,
'dialogs' => require __DIR__ . '/site/dialogs.php', 'dialogs' => require __DIR__ . '/site/dialogs.php',
'dropdowns' => require __DIR__ . '/site/dropdowns.php', 'dropdowns' => require __DIR__ . '/site/dropdowns.php',
'searches' => require __DIR__ . '/site/searches.php', 'searches' => require __DIR__ . '/site/searches.php',
'views' => require __DIR__ . '/site/views.php', 'views' => require __DIR__ . '/site/views.php',
]; ];
}; };

File diff suppressed because it is too large Load diff

View file

@ -5,22 +5,22 @@ use Kirby\Panel\Dropdown;
$files = require __DIR__ . '/../files/dropdowns.php'; $files = require __DIR__ . '/../files/dropdowns.php';
return [ return [
'changes' => [ 'changes' => [
'pattern' => 'changes', 'pattern' => 'changes',
'options' => fn () => Dropdown::changes() 'options' => fn () => Dropdown::changes()
], ],
'page' => [ 'page' => [
'pattern' => 'pages/(:any)', 'pattern' => 'pages/(:any)',
'options' => function (string $path) { 'options' => function (string $path) {
return Find::page($path)->panel()->dropdown(); return Find::page($path)->panel()->dropdown();
} }
], ],
'page.file' => [ 'page.file' => [
'pattern' => '(pages/.*?)/files/(:any)', 'pattern' => '(pages/.*?)/files/(:any)',
'options' => $files['file'] 'options' => $files['file']
], ],
'site.file' => [ 'site.file' => [
'pattern' => '(site)/files/(:any)', 'pattern' => '(site)/files/(:any)',
'options' => $files['file'] 'options' => $files['file']
] ]
]; ];

View file

@ -1,55 +1,57 @@
<?php <?php
use Kirby\Cms\App;
use Kirby\Toolkit\Escape; use Kirby\Toolkit\Escape;
use Kirby\Toolkit\I18n;
return [ return [
'pages' => [ 'pages' => [
'label' => t('pages'), 'label' => I18n::translate('pages'),
'icon' => 'page', 'icon' => 'page',
'query' => function (string $query = null) { 'query' => function (string $query = null) {
$pages = site() $pages = App::instance()->site()
->index(true) ->index(true)
->search($query) ->search($query)
->filter('isReadable', true) ->filter('isReadable', true)
->limit(10); ->limit(10);
$results = []; $results = [];
foreach ($pages as $page) { foreach ($pages as $page) {
$results[] = [ $results[] = [
'image' => $page->panel()->image(), 'image' => $page->panel()->image(),
'text' => Escape::html($page->title()->value()), 'text' => Escape::html($page->title()->value()),
'link' => $page->panel()->url(true), 'link' => $page->panel()->url(true),
'info' => Escape::html($page->id()) 'info' => Escape::html($page->id())
]; ];
} }
return $results; return $results;
} }
], ],
'files' => [ 'files' => [
'label' => t('files'), 'label' => I18n::translate('files'),
'icon' => 'image', 'icon' => 'image',
'query' => function (string $query = null) { 'query' => function (string $query = null) {
$files = site() $files = App::instance()->site()
->index(true) ->index(true)
->filter('isReadable', true) ->filter('isReadable', true)
->files() ->files()
->search($query) ->search($query)
->limit(10); ->limit(10);
$results = []; $results = [];
foreach ($files as $file) { foreach ($files as $file) {
$results[] = [ $results[] = [
'image' => $file->panel()->image(), 'image' => $file->panel()->image(),
'text' => Escape::html($file->filename()), 'text' => Escape::html($file->filename()),
'link' => $file->panel()->url(true), 'link' => $file->panel()->url(true),
'info' => Escape::html($file->id()) 'info' => Escape::html($file->id())
]; ];
} }
return $results; return $results;
} }
] ]
]; ];

View file

@ -1,26 +1,27 @@
<?php <?php
use Kirby\Cms\App;
use Kirby\Cms\Find; use Kirby\Cms\Find;
return [ return [
'page' => [ 'page' => [
'pattern' => 'pages/(:any)', 'pattern' => 'pages/(:any)',
'action' => fn (string $path) => Find::page($path)->panel()->view() 'action' => fn (string $path) => Find::page($path)->panel()->view()
], ],
'page.file' => [ 'page.file' => [
'pattern' => 'pages/(:any)/files/(:any)', 'pattern' => 'pages/(:any)/files/(:any)',
'action' => function (string $id, string $filename) { 'action' => function (string $id, string $filename) {
return Find::file('pages/' . $id, $filename)->panel()->view(); return Find::file('pages/' . $id, $filename)->panel()->view();
} }
], ],
'site' => [ 'site' => [
'pattern' => 'site', 'pattern' => 'site',
'action' => fn () => site()->panel()->view() 'action' => fn () => App::instance()->site()->panel()->view()
], ],
'site.file' => [ 'site.file' => [
'pattern' => 'site/files/(:any)', 'pattern' => 'site/files/(:any)',
'action' => function (string $filename) { 'action' => function (string $filename) {
return Find::file('site', $filename)->panel()->view(); return Find::file('site', $filename)->panel()->view();
} }
], ],
]; ];

View file

@ -1,11 +1,13 @@
<?php <?php
use Kirby\Toolkit\I18n;
return function ($kirby) { return function ($kirby) {
return [ return [
'icon' => 'settings', 'icon' => 'settings',
'label' => t('view.system'), 'label' => I18n::translate('view.system'),
'menu' => true, 'menu' => true,
'dialogs' => require __DIR__ . '/system/dialogs.php', 'dialogs' => require __DIR__ . '/system/dialogs.php',
'views' => require __DIR__ . '/system/views.php' 'views' => require __DIR__ . '/system/views.php'
]; ];
}; };

View file

@ -1,43 +1,86 @@
<?php <?php
use Kirby\Cms\App;
use Kirby\Panel\Field; use Kirby\Panel\Field;
use Kirby\Toolkit\I18n;
return [ return [
// license registration // license key
'registration' => [ 'license' => [
'load' => function () { 'load' => function () {
return [ $license = App::instance()->system()->license();
'component' => 'k-form-dialog',
'props' => [ // @codeCoverageIgnoreStart
'fields' => [ // the system is registered but the license
'license' => [ // key is only visible for admins
'label' => t('license.register.label'), if ($license === true) {
'type' => 'text', $license = 'Kirby 3';
'required' => true, }
'counter' => false, // @codeCoverageIgnoreEnd
'placeholder' => 'K3-',
'help' => t('license.register.help') return [
], 'component' => 'k-form-dialog',
'email' => Field::email([ 'props' => [
'required' => true 'size' => 'medium',
]) 'fields' => [
], 'license' => [
'submitButton' => t('license.register'), 'type' => 'info',
'value' => [ 'label' => I18n::translate('license'),
'license' => null, 'text' => $license ? $license : I18n::translate('license.unregistered.label'),
'email' => null 'theme' => $license ? 'code' : 'negative',
] 'help' => $license ?
] // @codeCoverageIgnoreStart
]; '<a href="https://hub.getkirby.com">' . I18n::translate('license.manage') . ' &rarr;</a>' :
}, // @codeCoverageIgnoreEnd
'submit' => function () { '<a href="https://getkirby.com/buy">' . I18n::translate('license.buy') . ' &rarr;</a>'
// @codeCoverageIgnoreStart ]
kirby()->system()->register(get('license'), get('email')); ],
return [ 'submitButton' => false,
'event' => 'system.register', 'cancelButton' => false,
'message' => t('license.register.success') ]
]; ];
// @codeCoverageIgnoreEnd }
} ],
], // license registration
'registration' => [
'load' => function () {
return [
'component' => 'k-form-dialog',
'props' => [
'fields' => [
'license' => [
'label' => I18n::translate('license.register.label'),
'type' => 'text',
'required' => true,
'counter' => false,
'placeholder' => 'K3-',
'help' => I18n::translate('license.register.help')
],
'email' => Field::email([
'required' => true
])
],
'submitButton' => I18n::translate('license.register'),
'value' => [
'license' => null,
'email' => null
]
]
];
},
'submit' => function () {
// @codeCoverageIgnoreStart
$kirby = App::instance();
$kirby->system()->register(
$kirby->request()->get('license'),
$kirby->request()->get('email')
);
return [
'event' => 'system.register',
'message' => I18n::translate('license.register.success')
];
// @codeCoverageIgnoreEnd
}
],
]; ];

View file

@ -1,47 +1,55 @@
<?php <?php
use Kirby\Http\Server; use Kirby\Cms\App;
return [ return [
'system' => [ 'system' => [
'pattern' => 'system', 'pattern' => 'system',
'action' => function () { 'action' => function () {
$kirby = kirby(); $kirby = App::instance();
$system = $kirby->system(); $system = $kirby->system();
$license = $system->license(); $license = $system->license();
// @codeCoverageIgnoreStart // @codeCoverageIgnoreStart
if ($license === true) { if ($license === true) {
// valid license, but user is not admin // valid license, but user is not admin
$license = 'Kirby 3'; $license = 'Kirby 3';
} elseif ($license === false) { } elseif ($license === false) {
// no valid license // no valid license
$license = null; $license = null;
} }
// @codeCoverageIgnoreEnd // @codeCoverageIgnoreEnd
$plugins = $system->plugins()->values(function ($plugin) { $plugins = $system->plugins()->values(function ($plugin) {
return [ return [
'author' => $plugin->authorsNames(), 'author' => $plugin->authorsNames(),
'license' => $plugin->license(), 'license' => $plugin->license(),
'link' => $plugin->link(), 'name' => [
'name' => $plugin->name(), 'text' => $plugin->name(),
'version' => $plugin->version(), 'href' => $plugin->link(),
]; ],
}); 'version' => $plugin->version(),
];
});
return [ return [
'component' => 'k-system-view', 'component' => 'k-system-view',
'props' => [ 'props' => [
'debug' => $kirby->option('debug', false), 'debug' => $kirby->option('debug', false),
'license' => $license, 'license' => $license,
'plugins' => $plugins, 'plugins' => $plugins,
'php' => phpversion(), 'php' => phpversion(),
'server' => $system->serverSoftware(), 'server' => $system->serverSoftware(),
'https' => Server::https(), 'https' => $kirby->environment()->https(),
'version' => $kirby->version(), 'version' => $kirby->version(),
] 'urls' => [
]; 'content' => $system->exposedFileUrl('content'),
} 'git' => $system->exposedFileUrl('git'),
], 'kirby' => $system->exposedFileUrl('kirby'),
'site' => $system->exposedFileUrl('site')
]
]
];
}
],
]; ];

View file

@ -1,14 +1,16 @@
<?php <?php
use Kirby\Toolkit\I18n;
return function ($kirby) { return function ($kirby) {
return [ return [
'icon' => 'users', 'icon' => 'users',
'label' => t('view.users'), 'label' => I18n::translate('view.users'),
'search' => 'users', 'search' => 'users',
'menu' => true, 'menu' => true,
'dialogs' => require __DIR__ . '/users/dialogs.php', 'dialogs' => require __DIR__ . '/users/dialogs.php',
'dropdowns' => require __DIR__ . '/users/dropdowns.php', 'dropdowns' => require __DIR__ . '/users/dropdowns.php',
'searches' => require __DIR__ . '/users/searches.php', 'searches' => require __DIR__ . '/users/searches.php',
'views' => require __DIR__ . '/users/views.php' 'views' => require __DIR__ . '/users/views.php'
]; ];
}; };

View file

@ -1,295 +1,311 @@
<?php <?php
use Kirby\Cms\App;
use Kirby\Cms\Find; use Kirby\Cms\Find;
use Kirby\Cms\UserRules; use Kirby\Cms\UserRules;
use Kirby\Exception\InvalidArgumentException; use Kirby\Exception\InvalidArgumentException;
use Kirby\Panel\Field; use Kirby\Panel\Field;
use Kirby\Panel\Panel; use Kirby\Panel\Panel;
use Kirby\Toolkit\Escape; use Kirby\Toolkit\Escape;
use Kirby\Toolkit\I18n;
$files = require __DIR__ . '/../files/dialogs.php'; $files = require __DIR__ . '/../files/dialogs.php';
return [ return [
// create // create
'user.create' => [ 'user.create' => [
'pattern' => 'users/create', 'pattern' => 'users/create',
'load' => function () { 'load' => function () {
$kirby = kirby(); $kirby = App::instance();
return [ return [
'component' => 'k-form-dialog', 'component' => 'k-form-dialog',
'props' => [ 'props' => [
'fields' => [ 'fields' => [
'name' => Field::username(), 'name' => Field::username(),
'email' => Field::email([ 'email' => Field::email([
'link' => false, 'link' => false,
'required' => true 'required' => true
]), ]),
'password' => Field::password(), 'password' => Field::password(),
'translation' => Field::translation([ 'translation' => Field::translation([
'required' => true 'required' => true
]), ]),
'role' => Field::role([ 'role' => Field::role([
'required' => true 'required' => true
]) ])
], ],
'submitButton' => t('create'), 'submitButton' => I18n::translate('create'),
'value' => [ 'value' => [
'name' => '', 'name' => '',
'email' => '', 'email' => '',
'password' => '', 'password' => '',
'translation' => $kirby->panelLanguage(), 'translation' => $kirby->panelLanguage(),
'role' => $kirby->user()->role()->name() 'role' => $kirby->user()->role()->name()
] ]
] ]
]; ];
}, },
'submit' => function () { 'submit' => function () {
kirby()->users()->create([ $kirby = App::instance();
'name' => get('name'),
'email' => get('email'),
'password' => get('password'),
'language' => get('translation'),
'role' => get('role')
]);
return [
'event' => 'user.create'
];
}
],
// change email $kirby->users()->create([
'user.changeEmail' => [ 'name' => $kirby->request()->get('name'),
'pattern' => 'users/(:any)/changeEmail', 'email' => $kirby->request()->get('email'),
'load' => function (string $id) { 'password' => $kirby->request()->get('password'),
$user = Find::user($id); 'language' => $kirby->request()->get('translation'),
'role' => $kirby->request()->get('role')
]);
return [ return [
'component' => 'k-form-dialog', 'event' => 'user.create'
'props' => [ ];
'fields' => [ }
'email' => [ ],
'label' => t('email'),
'required' => true,
'type' => 'email',
'preselect' => true
]
],
'submitButton' => t('change'),
'value' => [
'email' => $user->email()
]
]
];
},
'submit' => function (string $id) {
Find::user($id)->changeEmail(get('email'));
return [
'event' => 'user.changeEmail'
];
}
],
// change language // change email
'user.changeLanguage' => [ 'user.changeEmail' => [
'pattern' => 'users/(:any)/changeLanguage', 'pattern' => 'users/(:any)/changeEmail',
'load' => function (string $id) { 'load' => function (string $id) {
$user = Find::user($id); $user = Find::user($id);
return [ return [
'component' => 'k-form-dialog', 'component' => 'k-form-dialog',
'props' => [ 'props' => [
'fields' => [ 'fields' => [
'translation' => Field::translation(['required' => true]) 'email' => [
], 'label' => I18n::translate('email'),
'submitButton' => t('change'), 'required' => true,
'value' => [ 'type' => 'email',
'translation' => $user->language() 'preselect' => true
] ]
] ],
]; 'submitButton' => I18n::translate('change'),
}, 'value' => [
'submit' => function (string $id) { 'email' => $user->email()
Find::user($id)->changeLanguage(get('translation')); ]
]
];
},
'submit' => function (string $id) {
$request = App::instance()->request();
return [ Find::user($id)->changeEmail($request->get('email'));
'event' => 'user.changeLanguage',
'reload' => [
'globals' => '$translation'
]
];
}
],
// change name return [
'user.changeName' => [ 'event' => 'user.changeEmail'
'pattern' => 'users/(:any)/changeName', ];
'load' => function (string $id) { }
$user = Find::user($id); ],
return [ // change language
'component' => 'k-form-dialog', 'user.changeLanguage' => [
'props' => [ 'pattern' => 'users/(:any)/changeLanguage',
'fields' => [ 'load' => function (string $id) {
'name' => Field::username([ $user = Find::user($id);
'preselect' => true
])
],
'submitButton' => t('rename'),
'value' => [
'name' => $user->name()->value()
]
]
];
},
'submit' => function (string $id) {
Find::user($id)->changeName(get('name'));
return [ return [
'event' => 'user.changeName' 'component' => 'k-form-dialog',
]; 'props' => [
} 'fields' => [
], 'translation' => Field::translation(['required' => true])
],
'submitButton' => I18n::translate('change'),
'value' => [
'translation' => $user->language()
]
]
];
},
'submit' => function (string $id) {
$request = App::instance()->request();
// change password Find::user($id)->changeLanguage($request->get('translation'));
'user.changePassword' => [
'pattern' => 'users/(:any)/changePassword',
'load' => function (string $id) {
$user = Find::user($id);
return [ return [
'component' => 'k-form-dialog', 'event' => 'user.changeLanguage',
'props' => [ 'reload' => [
'fields' => [ 'globals' => '$translation'
'password' => Field::password([ ]
'label' => t('user.changePassword.new'), ];
]), }
'passwordConfirmation' => Field::password([ ],
'label' => t('user.changePassword.new.confirm'),
])
],
'submitButton' => t('change'),
]
];
},
'submit' => function (string $id) {
$user = Find::user($id);
$password = get('password');
$passwordConfirmation = get('passwordConfirmation');
// validate the password // change name
UserRules::validPassword($user, $password ?? ''); 'user.changeName' => [
'pattern' => 'users/(:any)/changeName',
'load' => function (string $id) {
$user = Find::user($id);
// compare passwords return [
if ($password !== $passwordConfirmation) { 'component' => 'k-form-dialog',
throw new InvalidArgumentException([ 'props' => [
'key' => 'user.password.notSame' 'fields' => [
]); 'name' => Field::username([
} 'preselect' => true
])
],
'submitButton' => I18n::translate('rename'),
'value' => [
'name' => $user->name()->value()
]
]
];
},
'submit' => function (string $id) {
$request = App::instance()->request();
// change password if everything's fine Find::user($id)->changeName($request->get('name'));
$user->changePassword($password);
return [ return [
'event' => 'user.changePassword' 'event' => 'user.changeName'
]; ];
} }
], ],
// change role // change password
'user.changeRole' => [ 'user.changePassword' => [
'pattern' => 'users/(:any)/changeRole', 'pattern' => 'users/(:any)/changePassword',
'load' => function (string $id) { 'load' => function (string $id) {
$user = Find::user($id); $user = Find::user($id);
return [ return [
'component' => 'k-form-dialog', 'component' => 'k-form-dialog',
'props' => [ 'props' => [
'fields' => [ 'fields' => [
'role' => Field::role([ 'password' => Field::password([
'label' => t('user.changeRole.select'), 'label' => I18n::translate('user.changePassword.new'),
'required' => true, ]),
]) 'passwordConfirmation' => Field::password([
], 'label' => I18n::translate('user.changePassword.new.confirm'),
'submitButton' => t('user.changeRole'), ])
'value' => [ ],
'role' => $user->role()->name() 'submitButton' => I18n::translate('change'),
] ]
] ];
]; },
}, 'submit' => function (string $id) {
'submit' => function (string $id) { $request = App::instance()->request();
$user = Find::user($id)->changeRole(get('role'));
return [ $user = Find::user($id);
'event' => 'user.changeRole', $password = $request->get('password');
'user' => $user->toArray() $passwordConfirmation = $request->get('passwordConfirmation');
];
}
],
// delete // validate the password
'user.delete' => [ UserRules::validPassword($user, $password ?? '');
'pattern' => 'users/(:any)/delete',
'load' => function (string $id) {
$user = Find::user($id);
$i18nPrefix = $user->isLoggedIn() ? 'account' : 'user';
return [ // compare passwords
'component' => 'k-remove-dialog', if ($password !== $passwordConfirmation) {
'props' => [ throw new InvalidArgumentException([
'text' => tt($i18nPrefix . '.delete.confirm', [ 'key' => 'user.password.notSame'
'email' => Escape::html($user->email()) ]);
]) }
]
];
},
'submit' => function (string $id) {
$user = Find::user($id);
$redirect = false;
$referrer = Panel::referrer();
$url = $user->panel()->url(true);
$user->delete(); // change password if everything's fine
$user->changePassword($password);
// redirect to the users view return [
// if the dialog has been opened in the user view 'event' => 'user.changePassword'
if ($referrer === $url) { ];
$redirect = '/users'; }
} ],
// logout the user if they deleted themselves // change role
if ($user->isLoggedIn()) { 'user.changeRole' => [
$redirect = '/logout'; 'pattern' => 'users/(:any)/changeRole',
} 'load' => function (string $id) {
$user = Find::user($id);
return [ return [
'event' => 'user.delete', 'component' => 'k-form-dialog',
'dispatch' => ['content/remove' => [$url]], 'props' => [
'redirect' => $redirect 'fields' => [
]; 'role' => Field::role([
} 'label' => I18n::translate('user.changeRole.select'),
], 'required' => true,
])
],
'submitButton' => I18n::translate('user.changeRole'),
'value' => [
'role' => $user->role()->name()
]
]
];
},
'submit' => function (string $id) {
$request = App::instance()->request();
// change file name $user = Find::user($id)->changeRole($request->get('role'));
'user.file.changeName' => [
'pattern' => '(users/.*?)/files/(:any)/changeName',
'load' => $files['changeName']['load'],
'submit' => $files['changeName']['submit'],
],
// change file sort return [
'user.file.changeSort' => [ 'event' => 'user.changeRole',
'pattern' => '(users/.*?)/files/(:any)/changeSort', 'user' => $user->toArray()
'load' => $files['changeSort']['load'], ];
'submit' => $files['changeSort']['submit'], }
], ],
// delete file // delete
'user.file.delete' => [ 'user.delete' => [
'pattern' => '(users/.*?)/files/(:any)/delete', 'pattern' => 'users/(:any)/delete',
'load' => $files['delete']['load'], 'load' => function (string $id) {
'submit' => $files['delete']['submit'], $user = Find::user($id);
] $i18nPrefix = $user->isLoggedIn() ? 'account' : 'user';
return [
'component' => 'k-remove-dialog',
'props' => [
'text' => I18n::template($i18nPrefix . '.delete.confirm', [
'email' => Escape::html($user->email())
])
]
];
},
'submit' => function (string $id) {
$user = Find::user($id);
$redirect = false;
$referrer = Panel::referrer();
$url = $user->panel()->url(true);
$user->delete();
// redirect to the users view
// if the dialog has been opened in the user view
if ($referrer === $url) {
$redirect = '/users';
}
// logout the user if they deleted themselves
if ($user->isLoggedIn()) {
$redirect = '/logout';
}
return [
'event' => 'user.delete',
'dispatch' => ['content/remove' => [$url]],
'redirect' => $redirect
];
}
],
// change file name
'user.file.changeName' => [
'pattern' => '(users/.*?)/files/(:any)/changeName',
'load' => $files['changeName']['load'],
'submit' => $files['changeName']['submit'],
],
// change file sort
'user.file.changeSort' => [
'pattern' => '(users/.*?)/files/(:any)/changeSort',
'load' => $files['changeSort']['load'],
'submit' => $files['changeSort']['submit'],
],
// delete file
'user.file.delete' => [
'pattern' => '(users/.*?)/files/(:any)/delete',
'load' => $files['delete']['load'],
'submit' => $files['delete']['submit'],
]
]; ];

View file

@ -5,14 +5,14 @@ use Kirby\Cms\Find;
$files = require __DIR__ . '/../files/dropdowns.php'; $files = require __DIR__ . '/../files/dropdowns.php';
return [ return [
'user' => [ 'user' => [
'pattern' => 'users/(:any)', 'pattern' => 'users/(:any)',
'options' => function (string $id) { 'options' => function (string $id) {
return Find::user($id)->panel()->dropdown(); return Find::user($id)->panel()->dropdown();
} }
], ],
'user.file' => [ 'user.file' => [
'pattern' => '(users/.*?)/files/(:any)', 'pattern' => '(users/.*?)/files/(:any)',
'options' => $files['file'] 'options' => $files['file']
] ]
]; ];

View file

@ -1,25 +1,27 @@
<?php <?php
use Kirby\Cms\App;
use Kirby\Toolkit\Escape; use Kirby\Toolkit\Escape;
use Kirby\Toolkit\I18n;
return [ return [
'users' => [ 'users' => [
'label' => t('users'), 'label' => I18n::translate('users'),
'icon' => 'users', 'icon' => 'users',
'query' => function (string $query = null) { 'query' => function (string $query = null) {
$users = kirby()->users()->search($query)->limit(10); $users = App::instance()->users()->search($query)->limit(10);
$results = []; $results = [];
foreach ($users as $user) { foreach ($users as $user) {
$results[] = [ $results[] = [
'image' => $user->panel()->image(), 'image' => $user->panel()->image(),
'text' => Escape::html($user->username()), 'text' => Escape::html($user->username()),
'link' => $user->panel()->url(true), 'link' => $user->panel()->url(true),
'info' => Escape::html($user->role()->title()) 'info' => Escape::html($user->role()->title())
]; ];
} }
return $results; return $results;
} }
] ]
]; ];

View file

@ -1,65 +1,66 @@
<?php <?php
use Kirby\Cms\App;
use Kirby\Cms\Find; use Kirby\Cms\Find;
use Kirby\Toolkit\Escape; use Kirby\Toolkit\Escape;
return [ return [
'users' => [ 'users' => [
'pattern' => 'users', 'pattern' => 'users',
'action' => function () { 'action' => function () {
$kirby = kirby(); $kirby = App::instance();
$role = get('role'); $role = $kirby->request()->get('role');
$roles = $kirby->roles()->toArray(fn ($role) => [ $roles = $kirby->roles()->toArray(fn ($role) => [
'id' => $role->id(), 'id' => $role->id(),
'title' => $role->title(), 'title' => $role->title(),
]); ]);
return [ return [
'component' => 'k-users-view', 'component' => 'k-users-view',
'props' => [ 'props' => [
'role' => function () use ($kirby, $roles, $role) { 'role' => function () use ($kirby, $roles, $role) {
if ($role) { if ($role) {
return $roles[$role] ?? null; return $roles[$role] ?? null;
} }
}, },
'roles' => array_values($roles), 'roles' => array_values($roles),
'users' => function () use ($kirby, $role) { 'users' => function () use ($kirby, $role) {
$users = $kirby->users(); $users = $kirby->users();
if (empty($role) === false) { if (empty($role) === false) {
$users = $users->role($role); $users = $users->role($role);
} }
$users = $users->paginate([ $users = $users->paginate([
'limit' => 20, 'limit' => 20,
'page' => get('page') 'page' => $kirby->request()->get('page')
]); ]);
return [ return [
'data' => $users->values(fn ($user) => [ 'data' => $users->values(fn ($user) => [
'id' => $user->id(), 'id' => $user->id(),
'image' => $user->panel()->image(), 'image' => $user->panel()->image(),
'info' => Escape::html($user->role()->title()), 'info' => Escape::html($user->role()->title()),
'link' => $user->panel()->url(true), 'link' => $user->panel()->url(true),
'text' => Escape::html($user->username()) 'text' => Escape::html($user->username())
]), ]),
'pagination' => $users->pagination()->toArray() 'pagination' => $users->pagination()->toArray()
]; ];
}, },
] ]
]; ];
} }
], ],
'user' => [ 'user' => [
'pattern' => 'users/(:any)', 'pattern' => 'users/(:any)',
'action' => function (string $id) { 'action' => function (string $id) {
return Find::user($id)->panel()->view(); return Find::user($id)->panel()->view();
} }
], ],
'user.file' => [ 'user.file' => [
'pattern' => 'users/(:any)/files/(:any)', 'pattern' => 'users/(:any)/files/(:any)',
'action' => function (string $id, string $filename) { 'action' => function (string $id, string $filename) {
return Find::file('users/' . $id, $filename)->panel()->view(); return Find::file('users/' . $id, $filename)->panel()->view();
} }
], ],
]; ];

View file

@ -9,17 +9,17 @@ $ratio = $block->ratio()->or('auto');
$src = null; $src = null;
if ($block->location() == 'web') { if ($block->location() == 'web') {
$src = $block->src()->esc(); $src = $block->src()->esc();
} elseif ($image = $block->image()->toFile()) { } elseif ($image = $block->image()->toFile()) {
$alt = $alt ?? $image->alt(); $alt = $alt ?? $image->alt();
$src = $image->url(); $src = $image->url();
} }
?> ?>
<?php if ($src): ?> <?php if ($src): ?>
<figure<?= attr(['data-ratio' => $ratio, 'data-crop' => $crop], ' ') ?>> <figure<?= Html::attr(['data-ratio' => $ratio, 'data-crop' => $crop], null, ' ') ?>>
<?php if ($link->isNotEmpty()): ?> <?php if ($link->isNotEmpty()): ?>
<a href="<?= esc($link->toUrl()) ?>"> <a href="<?= Str::esc($link->toUrl()) ?>">
<img src="<?= $src ?>" alt="<?= $alt->esc() ?>"> <img src="<?= $src ?>" alt="<?= $alt->esc() ?>">
</a> </a>
<?php else: ?> <?php else: ?>

View file

@ -1,5 +1,9 @@
<?php /** @var \Kirby\Cms\Block $block */ ?> <?php
<?php if ($video = video($block->url())): ?> use Kirby\Cms\Html;
/** @var \Kirby\Cms\Block $block */
?>
<?php if ($video = Html::video($block->url())): ?>
<figure> <figure>
<?= $video ?> <?= $video ?>
<?php if ($block->caption()->isNotEmpty()): ?> <?php if ($block->caption()->isNotEmpty()): ?>

View file

@ -4,12 +4,12 @@ use Kirby\Cms\App;
use Kirby\Cms\Collection; use Kirby\Cms\Collection;
use Kirby\Cms\File; use Kirby\Cms\File;
use Kirby\Cms\FileVersion; use Kirby\Cms\FileVersion;
use Kirby\Cms\Helpers;
use Kirby\Cms\Template; use Kirby\Cms\Template;
use Kirby\Data\Data; use Kirby\Data\Data;
use Kirby\Email\PHPMailer as Emailer; use Kirby\Email\PHPMailer as Emailer;
use Kirby\Filesystem\F; use Kirby\Filesystem\F;
use Kirby\Filesystem\Filename; use Kirby\Filesystem\Filename;
use Kirby\Http\Server;
use Kirby\Http\Uri; use Kirby\Http\Uri;
use Kirby\Http\Url; use Kirby\Http\Url;
use Kirby\Image\Darkroom; use Kirby\Image\Darkroom;
@ -21,380 +21,394 @@ use Kirby\Toolkit\Tpl as Snippet;
return [ return [
/** /**
* Used by the `css()` helper * Used by the `css()` helper
* *
* @param \Kirby\Cms\App $kirby Kirby instance * @param \Kirby\Cms\App $kirby Kirby instance
* @param string $url Relative or absolute URL * @param string $url Relative or absolute URL
* @param string|array $options An array of attributes for the link tag or a media attribute string * @param string|array $options An array of attributes for the link tag or a media attribute string
*/ */
'css' => fn (App $kirby, string $url, $options = null): string => $url, 'css' => fn (App $kirby, string $url, $options = null): string => $url,
/** /**
* Object and variable dumper * Object and variable dumper
* to help with debugging. * to help with debugging.
* *
* @param \Kirby\Cms\App $kirby Kirby instance * @param \Kirby\Cms\App $kirby Kirby instance
* @param mixed $variable * @param mixed $variable
* @param bool $echo * @param bool $echo
* @return string * @return string
*/ *
'dump' => function (App $kirby, $variable, bool $echo = true) { * @deprecated 3.7.0 Disable `dump()` via `KIRBY_HELPER_DUMP` instead and create your own function
if (Server::cli() === true) { * @todo move to `Helpers::dump()`, remove component in 3.8.0
$output = print_r($variable, true) . PHP_EOL; */
} else { 'dump' => function (App $kirby, $variable, bool $echo = true) {
$output = '<pre>' . print_r($variable, true) . '</pre>'; if ($kirby->environment()->cli() === true) {
} $output = print_r($variable, true) . PHP_EOL;
} else {
$output = '<pre>' . print_r($variable, true) . '</pre>';
}
if ($echo === true) { if ($echo === true) {
echo $output; echo $output;
} }
return $output; return $output;
}, },
/** /**
* Add your own email provider * Add your own email provider
* *
* @param \Kirby\Cms\App $kirby Kirby instance * @param \Kirby\Cms\App $kirby Kirby instance
* @param array $props * @param array $props
* @param bool $debug * @param bool $debug
*/ */
'email' => function (App $kirby, array $props = [], bool $debug = false) { 'email' => function (App $kirby, array $props = [], bool $debug = false) {
return new Emailer($props, $debug); return new Emailer($props, $debug);
}, },
/** /**
* Modify URLs for file objects * Modify URLs for file objects
* *
* @param \Kirby\Cms\App $kirby Kirby instance * @param \Kirby\Cms\App $kirby Kirby instance
* @param \Kirby\Cms\File $file The original file object * @param \Kirby\Cms\File $file The original file object
* @return string * @return string
*/ */
'file::url' => function (App $kirby, File $file): string { 'file::url' => function (App $kirby, File $file): string {
return $file->mediaUrl(); return $file->mediaUrl();
}, },
/** /**
* Adapt file characteristics * Adapt file characteristics
* *
* @param \Kirby\Cms\App $kirby Kirby instance * @param \Kirby\Cms\App $kirby Kirby instance
* @param \Kirby\Cms\File|\Kirby\Filesystem\Asset $file The file object * @param \Kirby\Cms\File|\Kirby\Filesystem\Asset $file The file object
* @param array $options All thumb options (width, height, crop, blur, grayscale) * @param array $options All thumb options (width, height, crop, blur, grayscale)
* @return \Kirby\Cms\File|\Kirby\Cms\FileVersion|\Kirby\Filesystem\Asset * @return \Kirby\Cms\File|\Kirby\Cms\FileVersion|\Kirby\Filesystem\Asset
*/ */
'file::version' => function (App $kirby, $file, array $options = []) { 'file::version' => function (App $kirby, $file, array $options = []) {
// if file is not resizable, return // if file is not resizable, return
if ($file->isResizable() === false) { if ($file->isResizable() === false) {
return $file; return $file;
} }
// create url and root // create url and root
$mediaRoot = dirname($file->mediaRoot()); $mediaRoot = dirname($file->mediaRoot());
$template = $mediaRoot . '/{{ name }}{{ attributes }}.{{ extension }}'; $template = $mediaRoot . '/{{ name }}{{ attributes }}.{{ extension }}';
$thumbRoot = (new Filename($file->root(), $template, $options))->toString(); $thumbRoot = (new Filename($file->root(), $template, $options))->toString();
$thumbName = basename($thumbRoot); $thumbName = basename($thumbRoot);
// check if the thumb already exists // check if the thumb already exists
if (file_exists($thumbRoot) === false) { if (file_exists($thumbRoot) === false) {
// if not, create job file
$job = $mediaRoot . '/.jobs/' . $thumbName . '.json';
// if not, create job file try {
$job = $mediaRoot . '/.jobs/' . $thumbName . '.json'; Data::write($job, array_merge($options, [
'filename' => $file->filename()
]));
} catch (Throwable $e) {
// if thumb doesn't exist yet and job file cannot
// be created, return
return $file;
}
}
try { return new FileVersion([
Data::write($job, array_merge($options, [ 'modifications' => $options,
'filename' => $file->filename() 'original' => $file,
])); 'root' => $thumbRoot,
} catch (Throwable $e) { 'url' => dirname($file->mediaUrl()) . '/' . $thumbName,
// if thumb doesn't exist yet and job file cannot ]);
// be created, return },
return $file;
}
}
return new FileVersion([ /**
'modifications' => $options, * Used by the `js()` helper
'original' => $file, *
'root' => $thumbRoot, * @param \Kirby\Cms\App $kirby Kirby instance
'url' => dirname($file->mediaUrl()) . '/' . $thumbName, * @param string $url Relative or absolute URL
]); * @param string|array $options An array of attributes for the link tag or a media attribute string
}, */
'js' => fn (App $kirby, string $url, $options = null): string => $url,
/** /**
* Used by the `js()` helper * Add your own Markdown parser
* *
* @param \Kirby\Cms\App $kirby Kirby instance * @param \Kirby\Cms\App $kirby Kirby instance
* @param string $url Relative or absolute URL * @param string $text Text to parse
* @param string|array $options An array of attributes for the link tag or a media attribute string * @param array $options Markdown options
*/ * @param bool $inline Whether to wrap the text in `<p>` tags (deprecated: set via $options['inline'] instead)
'js' => fn (App $kirby, string $url, $options = null): string => $url, * @return string
* @todo remove $inline parameter in in 3.8.0
*/
'markdown' => function (App $kirby, string $text = null, array $options = [], bool $inline = false): string {
static $markdown;
static $config;
/** // warning for deprecated fourth parameter
* Add your own Markdown parser if (func_num_args() === 4 && isset($options['inline']) === false) {
* // @codeCoverageIgnoreStart
* @param \Kirby\Cms\App $kirby Kirby instance Helpers::deprecated('markdown component: the $inline parameter is deprecated and will be removed in Kirby 3.8.0. Use $options[\'inline\'] instead.');
* @param string $text Text to parse // @codeCoverageIgnoreEnd
* @param array $options Markdown options }
* @param bool $inline Whether to wrap the text in `<p>` tags (deprecated: set via $options['inline'] instead)
* @return string
* @todo add deprecation warning for $inline parameter in 3.7.0
* @todo remove $inline parameter in in 3.8.0
*/
'markdown' => function (App $kirby, string $text = null, array $options = [], bool $inline = false): string {
static $markdown;
static $config;
// support for the deprecated fourth argument // support for the deprecated fourth argument
$options['inline'] ??= $inline; $options['inline'] ??= $inline;
// if the config options have changed or the component is called for the first time, // if the config options have changed or the component is called for the first time,
// (re-)initialize the parser object // (re-)initialize the parser object
if ($config !== $options) { if ($config !== $options) {
$markdown = new Markdown($options); $markdown = new Markdown($options);
$config = $options; $config = $options;
} }
return $markdown->parse($text, $options['inline']); return $markdown->parse($text, $options['inline'] ?? false);
}, },
/** /**
* Add your own search engine * Add your own search engine
* *
* @param \Kirby\Cms\App $kirby Kirby instance * @param \Kirby\Cms\App $kirby Kirby instance
* @param \Kirby\Cms\Collection $collection Collection of searchable models * @param \Kirby\Cms\Collection $collection Collection of searchable models
* @param string $query * @param string $query
* @param mixed $params * @param mixed $params
* @return \Kirby\Cms\Collection|bool * @return \Kirby\Cms\Collection|bool
*/ */
'search' => function (App $kirby, Collection $collection, string $query = null, $params = []) { 'search' => function (App $kirby, Collection $collection, string $query = null, $params = []) {
if (empty(trim($query ?? '')) === true) { if (empty(trim($query ?? '')) === true) {
return $collection->limit(0); return $collection->limit(0);
} }
if (is_string($params) === true) { if (is_string($params) === true) {
$params = ['fields' => Str::split($params, '|')]; $params = ['fields' => Str::split($params, '|')];
} }
$defaults = [ $defaults = [
'fields' => [], 'fields' => [],
'minlength' => 2, 'minlength' => 2,
'score' => [], 'score' => [],
'words' => false, 'words' => false,
]; ];
$options = array_merge($defaults, $params); $options = array_merge($defaults, $params);
$collection = clone $collection; $collection = clone $collection;
$searchWords = preg_replace('/(\s)/u', ',', $query); $searchWords = preg_replace('/(\s)/u', ',', $query);
$searchWords = Str::split($searchWords, ',', $options['minlength']); $searchWords = Str::split($searchWords, ',', $options['minlength']);
$lowerQuery = Str::lower($query); $lowerQuery = Str::lower($query);
$exactQuery = $options['words'] ? '(\b' . preg_quote($query) . '\b)' : preg_quote($query); $exactQuery = $options['words'] ? '(\b' . preg_quote($query) . '\b)' : preg_quote($query);
if (empty($options['stopwords']) === false) { if (empty($options['stopwords']) === false) {
$searchWords = array_diff($searchWords, $options['stopwords']); $searchWords = array_diff($searchWords, $options['stopwords']);
} }
$searchWords = array_map(function ($value) use ($options) { $searchWords = array_map(function ($value) use ($options) {
return $options['words'] ? '\b' . preg_quote($value) . '\b' : preg_quote($value); return $options['words'] ? '\b' . preg_quote($value) . '\b' : preg_quote($value);
}, $searchWords); }, $searchWords);
$preg = '!(' . implode('|', $searchWords) . ')!i'; $preg = '!(' . implode('|', $searchWords) . ')!i';
$results = $collection->filter(function ($item) use ($query, $preg, $options, $lowerQuery, $exactQuery) { $results = $collection->filter(function ($item) use ($query, $preg, $options, $lowerQuery, $exactQuery) {
$data = $item->content()->toArray(); $data = $item->content()->toArray();
$keys = array_keys($data); $keys = array_keys($data);
$keys[] = 'id'; $keys[] = 'id';
if (is_a($item, 'Kirby\Cms\User') === true) { if (is_a($item, 'Kirby\Cms\User') === true) {
$keys[] = 'name'; $keys[] = 'name';
$keys[] = 'email'; $keys[] = 'email';
$keys[] = 'role'; $keys[] = 'role';
} elseif (is_a($item, 'Kirby\Cms\Page') === true) { } elseif (is_a($item, 'Kirby\Cms\Page') === true) {
// apply the default score for pages // apply the default score for pages
$options['score'] = array_merge([ $options['score'] = array_merge([
'id' => 64, 'id' => 64,
'title' => 64, 'title' => 64,
], $options['score']); ], $options['score']);
} }
if (empty($options['fields']) === false) { if (empty($options['fields']) === false) {
$fields = array_map('strtolower', $options['fields']); $fields = array_map('strtolower', $options['fields']);
$keys = array_intersect($keys, $fields); $keys = array_intersect($keys, $fields);
} }
$item->searchHits = 0; $item->searchHits = 0;
$item->searchScore = 0; $item->searchScore = 0;
foreach ($keys as $key) { foreach ($keys as $key) {
$score = $options['score'][$key] ?? 1; $score = $options['score'][$key] ?? 1;
$value = $data[$key] ?? (string)$item->$key(); $value = $data[$key] ?? (string)$item->$key();
$lowerValue = Str::lower($value); $lowerValue = Str::lower($value);
// check for exact matches // check for exact matches
if ($lowerQuery == $lowerValue) { if ($lowerQuery == $lowerValue) {
$item->searchScore += 16 * $score; $item->searchScore += 16 * $score;
$item->searchHits += 1; $item->searchHits += 1;
// check for exact beginning matches // check for exact beginning matches
} elseif ($options['words'] === false && Str::startsWith($lowerValue, $lowerQuery) === true) { } elseif ($options['words'] === false && Str::startsWith($lowerValue, $lowerQuery) === true) {
$item->searchScore += 8 * $score; $item->searchScore += 8 * $score;
$item->searchHits += 1; $item->searchHits += 1;
// check for exact query matches // check for exact query matches
} elseif ($matches = preg_match_all('!' . $exactQuery . '!i', $value, $r)) { } elseif ($matches = preg_match_all('!' . $exactQuery . '!i', $value, $r)) {
$item->searchScore += 2 * $score; $item->searchScore += 2 * $score;
$item->searchHits += $matches; $item->searchHits += $matches;
} }
// check for any match // check for any match
if ($matches = preg_match_all($preg, $value, $r)) { if ($matches = preg_match_all($preg, $value, $r)) {
$item->searchHits += $matches; $item->searchHits += $matches;
$item->searchScore += $matches * $score; $item->searchScore += $matches * $score;
} }
} }
return $item->searchHits > 0; return $item->searchHits > 0;
}); });
return $results->sort('searchScore', 'desc'); return $results->sort('searchScore', 'desc');
}, },
/** /**
* Add your own SmartyPants parser * Add your own SmartyPants parser
* *
* @param \Kirby\Cms\App $kirby Kirby instance * @param \Kirby\Cms\App $kirby Kirby instance
* @param string $text Text to parse * @param string $text Text to parse
* @param array $options SmartyPants options * @param array $options SmartyPants options
* @return string * @return string
*/ */
'smartypants' => function (App $kirby, string $text = null, array $options = []): string { 'smartypants' => function (App $kirby, string $text = null, array $options = []): string {
static $smartypants; static $smartypants;
static $config; static $config;
// if the config options have changed or the component is called for the first time, // if the config options have changed or the component is called for the first time,
// (re-)initialize the parser object // (re-)initialize the parser object
if ($config !== $options) { if ($config !== $options) {
$smartypants = new Smartypants($options); $smartypants = new Smartypants($options);
$config = $options; $config = $options;
} }
return $smartypants->parse($text); return $smartypants->parse($text);
}, },
/** /**
* Add your own snippet loader * Add your own snippet loader
* *
* @param \Kirby\Cms\App $kirby Kirby instance * @param \Kirby\Cms\App $kirby Kirby instance
* @param string|array $name Snippet name * @param string|array $name Snippet name
* @param array $data Data array for the snippet * @param array $data Data array for the snippet
* @return string|null * @return string|null
*/ */
'snippet' => function (App $kirby, $name, array $data = []): ?string { 'snippet' => function (App $kirby, $name, array $data = []): ?string {
$snippets = A::wrap($name); $snippets = A::wrap($name);
foreach ($snippets as $name) { foreach ($snippets as $name) {
$name = (string)$name; $name = (string)$name;
$file = $kirby->root('snippets') . '/' . $name . '.php'; $file = $kirby->root('snippets') . '/' . $name . '.php';
if (file_exists($file) === false) { if (file_exists($file) === false) {
$file = $kirby->extensions('snippets')[$name] ?? null; $file = $kirby->extensions('snippets')[$name] ?? null;
} }
if ($file) { if ($file) {
break; break;
} }
} }
return Snippet::load($file, $data); return Snippet::load($file, $data);
}, },
/** /**
* Add your own template engine * Add your own template engine
* *
* @param \Kirby\Cms\App $kirby Kirby instance * @param \Kirby\Cms\App $kirby Kirby instance
* @param string $name Template name * @param string $name Template name
* @param string $type Extension type * @param string $type Extension type
* @param string $defaultType Default extension type * @param string $defaultType Default extension type
* @return \Kirby\Cms\Template * @return \Kirby\Cms\Template
*/ */
'template' => function (App $kirby, string $name, string $type = 'html', string $defaultType = 'html') { 'template' => function (App $kirby, string $name, string $type = 'html', string $defaultType = 'html') {
return new Template($name, $type, $defaultType); return new Template($name, $type, $defaultType);
}, },
/** /**
* Add your own thumb generator * Add your own thumb generator
* *
* @param \Kirby\Cms\App $kirby Kirby instance * @param \Kirby\Cms\App $kirby Kirby instance
* @param string $src Root of the original file * @param string $src Root of the original file
* @param string $dst Template string for the root to the desired destination * @param string $dst Template string for the root to the desired destination
* @param array $options All thumb options that should be applied: `width`, `height`, `crop`, `blur`, `grayscale` * @param array $options All thumb options that should be applied: `width`, `height`, `crop`, `blur`, `grayscale`
* @return string * @return string
*/ */
'thumb' => function (App $kirby, string $src, string $dst, array $options): string { 'thumb' => function (App $kirby, string $src, string $dst, array $options): string {
$darkroom = Darkroom::factory( $darkroom = Darkroom::factory(
option('thumbs.driver', 'gd'), $kirby->option('thumbs.driver', 'gd'),
option('thumbs', []) $kirby->option('thumbs', [])
); );
$options = $darkroom->preprocess($src, $options); $options = $darkroom->preprocess($src, $options);
$root = (new Filename($src, $dst, $options))->toString(); $root = (new Filename($src, $dst, $options))->toString();
F::copy($src, $root, true); F::copy($src, $root, true);
$darkroom->process($root, $options); $darkroom->process($root, $options);
return $root; return $root;
}, },
/** /**
* Modify all URLs * Modify all URLs
* *
* @param \Kirby\Cms\App $kirby Kirby instance * @param \Kirby\Cms\App $kirby Kirby instance
* @param string|null $path URL path * @param string|null $path URL path
* @param array|string|null $options Array of options for the Uri class * @param array|string|null $options Array of options for the Uri class
* @return string * @return string
*/ */
'url' => function (App $kirby, string $path = null, $options = null): string { 'url' => function (App $kirby, string $path = null, $options = null): string {
$language = null; $language = null;
// get language from simple string option // get language from simple string option
if (is_string($options) === true) { if (is_string($options) === true) {
$language = $options; $language = $options;
$options = null; $options = null;
} }
// get language from array // get language from array
if (is_array($options) === true && isset($options['language']) === true) { if (is_array($options) === true && isset($options['language']) === true) {
$language = $options['language']; $language = $options['language'];
unset($options['language']); unset($options['language']);
} }
// get a language url for the linked page, if the page can be found // get a language url for the linked page, if the page can be found
if ($kirby->multilang() === true) { if ($kirby->multilang() === true) {
$parts = Str::split($path, '#'); $parts = Str::split($path, '#');
if ($page = page($parts[0] ?? null)) { if ($parts[0] ?? null) {
$path = $page->url($language); $page = $kirby->site()->find($parts[0]);
} else {
$page = $kirby->site()->page();
}
if (isset($parts[1]) === true) { if ($page) {
$path .= '#' . $parts[1]; $path = $page->url($language);
}
}
}
// keep relative urls if (isset($parts[1]) === true) {
if ( $path .= '#' . $parts[1];
$path !== null && }
(substr($path, 0, 2) === './' || substr($path, 0, 3) === '../') }
) { }
return $path;
}
$url = Url::makeAbsolute($path, $kirby->url()); // keep relative urls
if (
$path !== null &&
(substr($path, 0, 2) === './' || substr($path, 0, 3) === '../')
) {
return $path;
}
if ($options === null) { $url = Url::makeAbsolute($path, $kirby->url());
return $url;
}
return (new Uri($url, $options))->toString(); if ($options === null) {
}, return $url;
}
return (new Uri($url, $options))->toString();
},
]; ];

View file

@ -4,58 +4,58 @@ use Kirby\Toolkit\A;
use Kirby\Toolkit\Str; use Kirby\Toolkit\Str;
return [ return [
'mixins' => ['min', 'options'], 'mixins' => ['min', 'options'],
'props' => [ 'props' => [
/** /**
* Unset inherited props * Unset inherited props
*/ */
'after' => null, 'after' => null,
'before' => null, 'before' => null,
'icon' => null, 'icon' => null,
'placeholder' => null, 'placeholder' => null,
/** /**
* Arranges the checkboxes in the given number of columns * Arranges the checkboxes in the given number of columns
*/ */
'columns' => function (int $columns = 1) { 'columns' => function (int $columns = 1) {
return $columns; return $columns;
}, },
/** /**
* Default value for the field, which will be used when a page/file/user is created * Default value for the field, which will be used when a page/file/user is created
*/ */
'default' => function ($default = null) { 'default' => function ($default = null) {
return Str::split($default, ','); return Str::split($default, ',');
}, },
/** /**
* Maximum number of checked boxes * Maximum number of checked boxes
*/ */
'max' => function (int $max = null) { 'max' => function (int $max = null) {
return $max; return $max;
}, },
/** /**
* Minimum number of checked boxes * Minimum number of checked boxes
*/ */
'min' => function (int $min = null) { 'min' => function (int $min = null) {
return $min; return $min;
}, },
'value' => function ($value = null) { 'value' => function ($value = null) {
return Str::split($value, ','); return Str::split($value, ',');
}, },
], ],
'computed' => [ 'computed' => [
'default' => function () { 'default' => function () {
return $this->sanitizeOptions($this->default); return $this->sanitizeOptions($this->default);
}, },
'value' => function () { 'value' => function () {
return $this->sanitizeOptions($this->value); return $this->sanitizeOptions($this->value);
}, },
], ],
'save' => function ($value): string { 'save' => function ($value): string {
return A::join($value, ', '); return A::join($value, ', ');
}, },
'validations' => [ 'validations' => [
'options', 'options',
'max', 'max',
'min' 'min'
] ]
]; ];

View file

@ -7,148 +7,148 @@ use Kirby\Toolkit\I18n;
use Kirby\Toolkit\Str; use Kirby\Toolkit\Str;
return [ return [
'mixins' => ['datetime'], 'mixins' => ['datetime'],
'props' => [ 'props' => [
/** /**
* Unset inherited props * Unset inherited props
*/ */
'placeholder' => null, 'placeholder' => null,
/** /**
* Activate/deactivate the dropdown calendar * Activate/deactivate the dropdown calendar
*/ */
'calendar' => function (bool $calendar = true) { 'calendar' => function (bool $calendar = true) {
return $calendar; return $calendar;
}, },
/** /**
* Default date when a new page/file/user gets created * Default date when a new page/file/user gets created
*/ */
'default' => function (string $default = null): string { 'default' => function (string $default = null): string {
return $this->toDatetime($default) ?? ''; return $this->toDatetime($default) ?? '';
}, },
/** /**
* Custom format (dayjs tokens: `DD`, `MM`, `YYYY`) that is * Custom format (dayjs tokens: `DD`, `MM`, `YYYY`) that is
* used to display the field in the Panel * used to display the field in the Panel
*/ */
'display' => function ($display = 'YYYY-MM-DD') { 'display' => function ($display = 'YYYY-MM-DD') {
return I18n::translate($display, $display); return I18n::translate($display, $display);
}, },
/** /**
* Changes the calendar icon to something custom * Changes the calendar icon to something custom
*/ */
'icon' => function (string $icon = 'calendar') { 'icon' => function (string $icon = 'calendar') {
return $icon; return $icon;
}, },
/** /**
* Latest date, which can be selected/saved (Y-m-d) * Latest date, which can be selected/saved (Y-m-d)
*/ */
'max' => function (string $max = null): ?string { 'max' => function (string $max = null): ?string {
return Date::optional($max); return Date::optional($max);
}, },
/** /**
* Earliest date, which can be selected/saved (Y-m-d) * Earliest date, which can be selected/saved (Y-m-d)
*/ */
'min' => function (string $min = null): ?string { 'min' => function (string $min = null): ?string {
return Date::optional($min); return Date::optional($min);
}, },
/** /**
* Round to the nearest: sub-options for `unit` (day) and `size` (1) * Round to the nearest: sub-options for `unit` (day) and `size` (1)
*/ */
'step' => function ($step = null) { 'step' => function ($step = null) {
return $step; return $step;
}, },
/** /**
* Pass `true` or an array of time field options to show the time selector. * Pass `true` or an array of time field options to show the time selector.
*/ */
'time' => function ($time = false) { 'time' => function ($time = false) {
return $time; return $time;
}, },
/** /**
* Must be a parseable date string * Must be a parseable date string
*/ */
'value' => function ($value = null) { 'value' => function ($value = null) {
return $value; return $value;
} }
], ],
'computed' => [ 'computed' => [
'display' => function () { 'display' => function () {
if ($this->display) { if ($this->display) {
return Str::upper($this->display); return Str::upper($this->display);
} }
}, },
'format' => function () { 'format' => function () {
return $this->props['format'] ?? ($this->time === false ? 'Y-m-d' : 'Y-m-d H:i:s'); return $this->props['format'] ?? ($this->time === false ? 'Y-m-d' : 'Y-m-d H:i:s');
}, },
'time' => function () { 'time' => function () {
if ($this->time === false) { if ($this->time === false) {
return false; return false;
} }
$props = is_array($this->time) ? $this->time : []; $props = is_array($this->time) ? $this->time : [];
$props['model'] = $this->model(); $props['model'] = $this->model();
$field = new Field('time', $props); $field = new Field('time', $props);
return $field->toArray(); return $field->toArray();
}, },
'step' => function () { 'step' => function () {
if ($this->time === false || empty($this->time['step']) === true) { if ($this->time === false || empty($this->time['step']) === true) {
return Date::stepConfig($this->step, [ return Date::stepConfig($this->step, [
'size' => 1, 'size' => 1,
'unit' => 'day' 'unit' => 'day'
]); ]);
} }
return Date::stepConfig($this->time['step'], [ return Date::stepConfig($this->time['step'], [
'size' => 5, 'size' => 5,
'unit' => 'minute' 'unit' => 'minute'
]); ]);
}, },
'value' => function (): string { 'value' => function (): string {
return $this->toDatetime($this->value) ?? ''; return $this->toDatetime($this->value) ?? '';
}, },
], ],
'validations' => [ 'validations' => [
'date', 'date',
'minMax' => function ($value) { 'minMax' => function ($value) {
if (!$value = Date::optional($value)) { if (!$value = Date::optional($value)) {
return true; return true;
} }
$min = Date::optional($this->min); $min = Date::optional($this->min);
$max = Date::optional($this->max); $max = Date::optional($this->max);
$format = $this->time === false ? 'd.m.Y' : 'd.m.Y H:i'; $format = $this->time === false ? 'd.m.Y' : 'd.m.Y H:i';
if ($min && $max && $value->isBetween($min, $max) === false) { if ($min && $max && $value->isBetween($min, $max) === false) {
throw new Exception([ throw new Exception([
'key' => 'validation.date.between', 'key' => 'validation.date.between',
'data' => [ 'data' => [
'min' => $min->format($format), 'min' => $min->format($format),
'max' => $min->format($format) 'max' => $min->format($format)
] ]
]); ]);
} elseif ($min && $value->isMin($min) === false) { } elseif ($min && $value->isMin($min) === false) {
throw new Exception([ throw new Exception([
'key' => 'validation.date.after', 'key' => 'validation.date.after',
'data' => [ 'data' => [
'date' => $min->format($format), 'date' => $min->format($format),
] ]
]); ]);
} elseif ($max && $value->isMax($max) === false) { } elseif ($max && $value->isMax($max) === false) {
throw new Exception([ throw new Exception([
'key' => 'validation.date.before', 'key' => 'validation.date.before',
'data' => [ 'data' => [
'date' => $max->format($format), 'date' => $max->format($format),
] ]
]); ]);
} }
return true; return true;
}, },
] ]
]; ];

View file

@ -3,38 +3,38 @@
use Kirby\Toolkit\I18n; use Kirby\Toolkit\I18n;
return [ return [
'extends' => 'text', 'extends' => 'text',
'props' => [ 'props' => [
/** /**
* Unset inherited props * Unset inherited props
*/ */
'converter' => null, 'converter' => null,
'counter' => null, 'counter' => null,
/** /**
* Sets the HTML5 autocomplete mode for the input * Sets the HTML5 autocomplete mode for the input
*/ */
'autocomplete' => function (string $autocomplete = 'email') { 'autocomplete' => function (string $autocomplete = 'email') {
return $autocomplete; return $autocomplete;
}, },
/** /**
* Changes the email icon to something custom * Changes the email icon to something custom
*/ */
'icon' => function (string $icon = 'email') { 'icon' => function (string $icon = 'email') {
return $icon; return $icon;
}, },
/** /**
* Custom placeholder text, when the field is empty. * Custom placeholder text, when the field is empty.
*/ */
'placeholder' => function ($value = null) { 'placeholder' => function ($value = null) {
return I18n::translate($value, $value) ?? I18n::translate('email.placeholder'); return I18n::translate($value, $value) ?? I18n::translate('email.placeholder');
} }
], ],
'validations' => [ 'validations' => [
'minlength', 'minlength',
'maxlength', 'maxlength',
'email' 'email'
] ]
]; ];

View file

@ -4,128 +4,128 @@ use Kirby\Data\Data;
use Kirby\Toolkit\A; use Kirby\Toolkit\A;
return [ return [
'mixins' => [ 'mixins' => [
'filepicker', 'filepicker',
'layout', 'layout',
'min', 'min',
'picker', 'picker',
'upload' 'upload'
], ],
'props' => [ 'props' => [
/** /**
* Unset inherited props * Unset inherited props
*/ */
'after' => null, 'after' => null,
'before' => null, 'before' => null,
'autofocus' => null, 'autofocus' => null,
'icon' => null, 'icon' => null,
'placeholder' => null, 'placeholder' => null,
/** /**
* Sets the file(s), which are selected by default when a new page is created * Sets the file(s), which are selected by default when a new page is created
*/ */
'default' => function ($default = null) { 'default' => function ($default = null) {
return $default; return $default;
}, },
'value' => function ($value = null) { 'value' => function ($value = null) {
return $value; return $value;
} }
], ],
'computed' => [ 'computed' => [
'parentModel' => function () { 'parentModel' => function () {
if (is_string($this->parent) === true && $model = $this->model()->query($this->parent, 'Kirby\Cms\Model')) { if (is_string($this->parent) === true && $model = $this->model()->query($this->parent, 'Kirby\Cms\Model')) {
return $model; return $model;
} }
return $this->model(); return $this->model();
}, },
'parent' => function () { 'parent' => function () {
return $this->parentModel->apiUrl(true); return $this->parentModel->apiUrl(true);
}, },
'query' => function () { 'query' => function () {
return $this->query ?? $this->parentModel::CLASS_ALIAS . '.files'; return $this->query ?? $this->parentModel::CLASS_ALIAS . '.files';
}, },
'default' => function () { 'default' => function () {
return $this->toFiles($this->default); return $this->toFiles($this->default);
}, },
'value' => function () { 'value' => function () {
return $this->toFiles($this->value); return $this->toFiles($this->value);
}, },
], ],
'methods' => [ 'methods' => [
'fileResponse' => function ($file) { 'fileResponse' => function ($file) {
return $file->panel()->pickerData([ return $file->panel()->pickerData([
'image' => $this->image, 'image' => $this->image,
'info' => $this->info ?? false, 'info' => $this->info ?? false,
'layout' => $this->layout, 'layout' => $this->layout,
'model' => $this->model(), 'model' => $this->model(),
'text' => $this->text, 'text' => $this->text,
]); ]);
}, },
'toFiles' => function ($value = null) { 'toFiles' => function ($value = null) {
$files = []; $files = [];
foreach (Data::decode($value, 'yaml') as $id) { foreach (Data::decode($value, 'yaml') as $id) {
if (is_array($id) === true) { if (is_array($id) === true) {
$id = $id['id'] ?? null; $id = $id['id'] ?? null;
} }
if ($id !== null && ($file = $this->kirby()->file($id, $this->model()))) { if ($id !== null && ($file = $this->kirby()->file($id, $this->model()))) {
$files[] = $this->fileResponse($file); $files[] = $this->fileResponse($file);
} }
} }
return $files; return $files;
} }
], ],
'api' => function () { 'api' => function () {
return [ return [
[ [
'pattern' => '/', 'pattern' => '/',
'action' => function () { 'action' => function () {
$field = $this->field(); $field = $this->field();
return $field->filepicker([ return $field->filepicker([
'image' => $field->image(), 'image' => $field->image(),
'info' => $field->info(), 'info' => $field->info(),
'layout' => $field->layout(), 'layout' => $field->layout(),
'limit' => $field->limit(), 'limit' => $field->limit(),
'page' => $this->requestQuery('page'), 'page' => $this->requestQuery('page'),
'query' => $field->query(), 'query' => $field->query(),
'search' => $this->requestQuery('search'), 'search' => $this->requestQuery('search'),
'text' => $field->text() 'text' => $field->text()
]); ]);
} }
], ],
[ [
'pattern' => 'upload', 'pattern' => 'upload',
'method' => 'POST', 'method' => 'POST',
'action' => function () { 'action' => function () {
$field = $this->field(); $field = $this->field();
$uploads = $field->uploads(); $uploads = $field->uploads();
// move_uploaded_file() not working with unit test // move_uploaded_file() not working with unit test
// @codeCoverageIgnoreStart // @codeCoverageIgnoreStart
return $field->upload($this, $uploads, function ($file, $parent) use ($field) { return $field->upload($this, $uploads, function ($file, $parent) use ($field) {
return $file->panel()->pickerData([ return $file->panel()->pickerData([
'image' => $field->image(), 'image' => $field->image(),
'info' => $field->info(), 'info' => $field->info(),
'layout' => $field->layout(), 'layout' => $field->layout(),
'model' => $field->model(), 'model' => $field->model(),
'text' => $field->text(), 'text' => $field->text(),
]); ]);
}); });
// @codeCoverageIgnoreEnd // @codeCoverageIgnoreEnd
} }
] ]
]; ];
}, },
'save' => function ($value = null) { 'save' => function ($value = null) {
return A::pluck($value, 'uuid'); return A::pluck($value, 'uuid');
}, },
'validations' => [ 'validations' => [
'max', 'max',
'min' 'min'
] ]
]; ];

View file

@ -1,5 +1,5 @@
<?php <?php
return [ return [
'save' => false 'save' => false
]; ];

View file

@ -1,26 +1,26 @@
<?php <?php
return [ return [
'save' => false, 'save' => false,
'props' => [ 'props' => [
/** /**
* Unset inherited props * Unset inherited props
*/ */
'after' => null, 'after' => null,
'autofocus' => null, 'autofocus' => null,
'before' => null, 'before' => null,
'default' => null, 'default' => null,
'disabled' => null, 'disabled' => null,
'icon' => null, 'icon' => null,
'placeholder' => null, 'placeholder' => null,
'required' => null, 'required' => null,
'translate' => null, 'translate' => null,
/** /**
* If `false`, the prepended number will be hidden * If `false`, the prepended number will be hidden
*/ */
'numbered' => function (bool $numbered = true) { 'numbered' => function (bool $numbered = true) {
return $numbered; return $numbered;
} }
] ]
]; ];

View file

@ -3,42 +3,42 @@
use Kirby\Toolkit\I18n; use Kirby\Toolkit\I18n;
return [ return [
'props' => [ 'props' => [
/** /**
* Unset inherited props * Unset inherited props
*/ */
'after' => null, 'after' => null,
'autofocus' => null, 'autofocus' => null,
'before' => null, 'before' => null,
'default' => null, 'default' => null,
'disabled' => null, 'disabled' => null,
'icon' => null, 'icon' => null,
'placeholder' => null, 'placeholder' => null,
'required' => null, 'required' => null,
'translate' => null, 'translate' => null,
/** /**
* Text to be displayed * Text to be displayed
*/ */
'text' => function ($value = null) { 'text' => function ($value = null) {
return I18n::translate($value, $value); return I18n::translate($value, $value);
}, },
/** /**
* Change the design of the info box * Change the design of the info box
*/ */
'theme' => function (string $theme = null) { 'theme' => function (string $theme = null) {
return $theme; return $theme;
} }
], ],
'computed' => [ 'computed' => [
'text' => function () { 'text' => function () {
if ($text = $this->text) { if ($text = $this->text) {
$text = $this->model()->toSafeString($text); $text = $this->model()->toSafeString($text);
$text = $this->kirby()->kirbytext($text); $text = $this->kirby()->kirbytext($text);
return $text; return $text;
} }
} }
], ],
'save' => false, 'save' => false,
]; ];

View file

@ -1,5 +1,5 @@
<?php <?php
return [ return [
'save' => false 'save' => false
]; ];

View file

@ -1,17 +1,17 @@
<?php <?php
return [ return [
'props' => [ 'props' => [
/** /**
* Sets the allowed HTML formats. Available formats: `bold`, `italic`, `underline`, `strike`, `code`, `link`. Activate them all by passing `true`. Deactivate them all by passing `false` * Sets the allowed HTML formats. Available formats: `bold`, `italic`, `underline`, `strike`, `code`, `link`. Activate them all by passing `true`. Deactivate them all by passing `false`
*/ */
'marks' => function ($marks = true) { 'marks' => function ($marks = true) {
return $marks; return $marks;
} }
], ],
'computed' => [ 'computed' => [
'value' => function () { 'value' => function () {
return trim($this->value ?? ''); return trim($this->value ?? '');
} }
] ]
]; ];

View file

@ -3,33 +3,33 @@
use Kirby\Toolkit\Date; use Kirby\Toolkit\Date;
return [ return [
'props' => [ 'props' => [
/** /**
* Defines a custom format that is used when the field is saved * Defines a custom format that is used when the field is saved
*/ */
'format' => function (string $format = null) { 'format' => function (string $format = null) {
return $format; return $format;
} }
], ],
'methods' => [ 'methods' => [
'toDatetime' => function ($value, string $format = 'Y-m-d H:i:s') { 'toDatetime' => function ($value, string $format = 'Y-m-d H:i:s') {
if ($date = Date::optional($value)) { if ($date = Date::optional($value)) {
if ($this->step) { if ($this->step) {
$step = Date::stepConfig($this->step); $step = Date::stepConfig($this->step);
$date->round($step['unit'], $step['size']); $date->round($step['unit'], $step['size']);
} }
return $date->format($format); return $date->format($format);
} }
return null; return null;
} }
], ],
'save' => function ($value) { 'save' => function ($value) {
if ($date = Date::optional($value)) { if ($date = Date::optional($value)) {
return $date->format($this->format); return $date->format($this->format);
} }
return ''; return '';
}, },
]; ];

View file

@ -3,12 +3,12 @@
use Kirby\Cms\FilePicker; use Kirby\Cms\FilePicker;
return [ return [
'methods' => [ 'methods' => [
'filepicker' => function (array $params = []) { 'filepicker' => function (array $params = []) {
// fetch the parent model // fetch the parent model
$params['model'] = $this->model(); $params['model'] = $this->model();
return (new FilePicker($params))->toArray(); return (new FilePicker($params))->toArray();
} }
] ]
]; ];

View file

@ -1,21 +1,21 @@
<?php <?php
return [ return [
'props' => [ 'props' => [
/** /**
* Changes the layout of the selected entries. * Changes the layout of the selected entries.
* Available layouts: `list`, `cardlets`, `cards` * Available layouts: `list`, `cardlets`, `cards`
*/ */
'layout' => function (string $layout = 'list') { 'layout' => function (string $layout = 'list') {
$layouts = ['list', 'cardlets', 'cards']; $layouts = ['list', 'cardlets', 'cards'];
return in_array($layout, $layouts) ? $layout : 'list'; return in_array($layout, $layouts) ? $layout : 'list';
}, },
/** /**
* Layout size for cards: `tiny`, `small`, `medium`, `large` or `huge` * Layout size for cards: `tiny`, `small`, `medium`, `large` or `huge`
*/ */
'size' => function (string $size = 'auto') { 'size' => function (string $size = 'auto') {
return $size; return $size;
}, },
] ]
]; ];

View file

@ -1,22 +1,22 @@
<?php <?php
return [ return [
'computed' => [ 'computed' => [
'min' => function () { 'min' => function () {
// set min to at least 1, if required // set min to at least 1, if required
if ($this->required === true) { if ($this->required === true) {
return $this->min ?? 1; return $this->min ?? 1;
} }
return $this->min; return $this->min;
}, },
'required' => function () { 'required' => function () {
// set required to true if min is set // set required to true if min is set
if ($this->min) { if ($this->min) {
return true; return true;
} }
return $this->required; return $this->required;
} }
] ]
]; ];

View file

@ -3,46 +3,46 @@
use Kirby\Form\Options; use Kirby\Form\Options;
return [ return [
'props' => [ 'props' => [
/** /**
* API settings for options requests. This will only take affect when `options` is set to `api`. * API settings for options requests. This will only take affect when `options` is set to `api`.
*/ */
'api' => function ($api = null) { 'api' => function ($api = null) {
return $api; return $api;
}, },
/** /**
* An array with options * An array with options
*/ */
'options' => function ($options = []) { 'options' => function ($options = []) {
return $options; return $options;
}, },
/** /**
* Query settings for options queries. This will only take affect when `options` is set to `query`. * Query settings for options queries. This will only take affect when `options` is set to `query`.
*/ */
'query' => function ($query = null) { 'query' => function ($query = null) {
return $query; return $query;
}, },
], ],
'computed' => [ 'computed' => [
'options' => function (): array { 'options' => function (): array {
return $this->getOptions(); return $this->getOptions();
} }
], ],
'methods' => [ 'methods' => [
'getOptions' => function () { 'getOptions' => function () {
return Options::factory( return Options::factory(
$this->options(), $this->options(),
$this->props, $this->props,
$this->model() $this->model()
); );
}, },
'sanitizeOption' => function ($option) { 'sanitizeOption' => function ($option) {
$allowed = array_column($this->options(), 'value'); $allowed = array_column($this->options(), 'value');
return in_array($option, $allowed, true) === true ? $option : null; return in_array($option, $allowed, true) === true ? $option : null;
}, },
'sanitizeOptions' => function ($options) { 'sanitizeOptions' => function ($options) {
$allowed = array_column($this->options(), 'value'); $allowed = array_column($this->options(), 'value');
return array_intersect($options, $allowed); return array_intersect($options, $allowed);
}, },
] ]
]; ];

View file

@ -3,12 +3,12 @@
use Kirby\Cms\PagePicker; use Kirby\Cms\PagePicker;
return [ return [
'methods' => [ 'methods' => [
'pagepicker' => function (array $params = []) { 'pagepicker' => function (array $params = []) {
// inject the current model // inject the current model
$params['model'] = $this->model(); $params['model'] = $this->model();
return (new PagePicker($params))->toArray(); return (new PagePicker($params))->toArray();
} }
] ]
]; ];

View file

@ -3,76 +3,76 @@
use Kirby\Toolkit\I18n; use Kirby\Toolkit\I18n;
return [ return [
'props' => [ 'props' => [
/** /**
* The placeholder text if none have been selected yet * The placeholder text if none have been selected yet
*/ */
'empty' => function ($empty = null) { 'empty' => function ($empty = null) {
return I18n::translate($empty, $empty); return I18n::translate($empty, $empty);
}, },
/** /**
* Image settings for each item * Image settings for each item
*/ */
'image' => function ($image = null) { 'image' => function ($image = null) {
return $image; return $image;
}, },
/** /**
* Info text for each item * Info text for each item
*/ */
'info' => function (string $info = null) { 'info' => function (string $info = null) {
return $info; return $info;
}, },
/** /**
* Whether each item should be clickable * Whether each item should be clickable
*/ */
'link' => function (bool $link = true) { 'link' => function (bool $link = true) {
return $link; return $link;
}, },
/** /**
* The minimum number of required selected * The minimum number of required selected
*/ */
'min' => function (int $min = null) { 'min' => function (int $min = null) {
return $min; return $min;
}, },
/** /**
* The maximum number of allowed selected * The maximum number of allowed selected
*/ */
'max' => function (int $max = null) { 'max' => function (int $max = null) {
return $max; return $max;
}, },
/** /**
* If `false`, only a single one can be selected * If `false`, only a single one can be selected
*/ */
'multiple' => function (bool $multiple = true) { 'multiple' => function (bool $multiple = true) {
return $multiple; return $multiple;
}, },
/** /**
* Query for the items to be included in the picker * Query for the items to be included in the picker
*/ */
'query' => function (string $query = null) { 'query' => function (string $query = null) {
return $query; return $query;
}, },
/** /**
* Enable/disable the search field in the picker * Enable/disable the search field in the picker
*/ */
'search' => function (bool $search = true) { 'search' => function (bool $search = true) {
return $search; return $search;
}, },
/** /**
* Main text for each item * Main text for each item
*/ */
'text' => function (string $text = null) { 'text' => function (string $text = null) {
return $text; return $text;
}, },
], ],
]; ];

View file

@ -5,69 +5,69 @@ use Kirby\Cms\File;
use Kirby\Exception\Exception; use Kirby\Exception\Exception;
return [ return [
'props' => [ 'props' => [
/** /**
* Sets the upload options for linked files (since 3.2.0) * Sets the upload options for linked files (since 3.2.0)
*/ */
'uploads' => function ($uploads = []) { 'uploads' => function ($uploads = []) {
if ($uploads === false) { if ($uploads === false) {
return false; return false;
} }
if (is_string($uploads) === true) { if (is_string($uploads) === true) {
$uploads = ['template' => $uploads]; $uploads = ['template' => $uploads];
} }
if (is_array($uploads) === false) { if (is_array($uploads) === false) {
$uploads = []; $uploads = [];
} }
$template = $uploads['template'] ?? null; $template = $uploads['template'] ?? null;
if ($template) { if ($template) {
$file = new File([ $file = new File([
'filename' => 'tmp', 'filename' => 'tmp',
'parent' => $this->model(), 'parent' => $this->model(),
'template' => $template 'template' => $template
]); ]);
$uploads['accept'] = $file->blueprint()->acceptMime(); $uploads['accept'] = $file->blueprint()->acceptMime();
} else { } else {
$uploads['accept'] = '*'; $uploads['accept'] = '*';
} }
return $uploads; return $uploads;
}, },
], ],
'methods' => [ 'methods' => [
'upload' => function (Api $api, $params, Closure $map) { 'upload' => function (Api $api, $params, Closure $map) {
if ($params === false) { if ($params === false) {
throw new Exception('Uploads are disabled for this field'); throw new Exception('Uploads are disabled for this field');
} }
if ($parentQuery = ($params['parent'] ?? null)) { if ($parentQuery = ($params['parent'] ?? null)) {
$parent = $this->model()->query($parentQuery); $parent = $this->model()->query($parentQuery);
} else { } else {
$parent = $this->model(); $parent = $this->model();
} }
if (is_a($parent, 'Kirby\Cms\File') === true) { if (is_a($parent, 'Kirby\Cms\File') === true) {
$parent = $parent->parent(); $parent = $parent->parent();
} }
return $api->upload(function ($source, $filename) use ($parent, $params, $map) { return $api->upload(function ($source, $filename) use ($parent, $params, $map) {
$file = $parent->createFile([ $file = $parent->createFile([
'source' => $source, 'source' => $source,
'template' => $params['template'] ?? null, 'template' => $params['template'] ?? null,
'filename' => $filename, 'filename' => $filename,
]); ]);
if (is_a($file, 'Kirby\Cms\File') === false) { if (is_a($file, 'Kirby\Cms\File') === false) {
throw new Exception('The file could not be uploaded'); throw new Exception('The file could not be uploaded');
} }
return $map($file, $parent); return $map($file, $parent);
}); });
} }
] ]
]; ];

View file

@ -3,11 +3,11 @@
use Kirby\Cms\UserPicker; use Kirby\Cms\UserPicker;
return [ return [
'methods' => [ 'methods' => [
'userpicker' => function (array $params = []) { 'userpicker' => function (array $params = []) {
$params['model'] = $this->model(); $params['model'] = $this->model();
return (new UserPicker($params))->toArray(); return (new UserPicker($params))->toArray();
} }
] ]
]; ];

View file

@ -1,32 +1,32 @@
<?php <?php
return [ return [
'extends' => 'tags', 'extends' => 'tags',
'props' => [ 'props' => [
/** /**
* Unset inherited props * Unset inherited props
*/ */
'accept' => null, 'accept' => null,
/** /**
* Custom icon to replace the arrow down. * Custom icon to replace the arrow down.
*/ */
'icon' => function (string $icon = null) { 'icon' => function (string $icon = null) {
return $icon; return $icon;
}, },
/** /**
* Enable/disable the search in the dropdown * Enable/disable the search in the dropdown
* Also limit displayed items (display: 20) * Also limit displayed items (display: 20)
* and set minimum number of characters to search (min: 3) * and set minimum number of characters to search (min: 3)
*/ */
'search' => function ($search = true) { 'search' => function ($search = true) {
return $search; return $search;
}, },
/** /**
* If `true`, selected entries will be sorted * If `true`, selected entries will be sorted
* according to their position in the dropdown * according to their position in the dropdown
*/ */
'sort' => function (bool $sort = false) { 'sort' => function (bool $sort = false) {
return $sort; return $sort;
}, },
] ]
]; ];

View file

@ -3,46 +3,46 @@
use Kirby\Toolkit\Str; use Kirby\Toolkit\Str;
return [ return [
'props' => [ 'props' => [
/** /**
* Default number that will be saved when a new page/user/file is created * Default number that will be saved when a new page/user/file is created
*/ */
'default' => function ($default = null) { 'default' => function ($default = null) {
return $this->toNumber($default); return $this->toNumber($default);
}, },
/** /**
* The lowest allowed number * The lowest allowed number
*/ */
'min' => function (float $min = null) { 'min' => function (float $min = null) {
return $min; return $min;
}, },
/** /**
* The highest allowed number * The highest allowed number
*/ */
'max' => function (float $max = null) { 'max' => function (float $max = null) {
return $max; return $max;
}, },
/** /**
* Allowed incremental steps between numbers (i.e `0.5`) * Allowed incremental steps between numbers (i.e `0.5`)
*/ */
'step' => function ($step = null) { 'step' => function ($step = null) {
return $this->toNumber($step); return $this->toNumber($step);
}, },
'value' => function ($value = null) { 'value' => function ($value = null) {
return $this->toNumber($value); return $this->toNumber($value);
} }
], ],
'methods' => [ 'methods' => [
'toNumber' => function ($value) { 'toNumber' => function ($value) {
if ($this->isEmpty($value) === true) { if ($this->isEmpty($value) === true) {
return null; return null;
} }
return is_float($value) === true ? $value : (float)Str::float($value); return is_float($value) === true ? $value : (float)Str::float($value);
} }
], ],
'validations' => [ 'validations' => [
'min', 'min',
'max' 'max'
] ]
]; ];

View file

@ -1,110 +1,111 @@
<?php <?php
use Kirby\Cms\App;
use Kirby\Data\Data; use Kirby\Data\Data;
use Kirby\Toolkit\A; use Kirby\Toolkit\A;
return [ return [
'mixins' => [ 'mixins' => [
'layout', 'layout',
'min', 'min',
'pagepicker', 'pagepicker',
'picker', 'picker',
], ],
'props' => [ 'props' => [
/** /**
* Unset inherited props * Unset inherited props
*/ */
'after' => null, 'after' => null,
'autofocus' => null, 'autofocus' => null,
'before' => null, 'before' => null,
'icon' => null, 'icon' => null,
'placeholder' => null, 'placeholder' => null,
/** /**
* Default selected page(s) when a new page/file/user is created * Default selected page(s) when a new page/file/user is created
*/ */
'default' => function ($default = null) { 'default' => function ($default = null) {
return $this->toPages($default); return $this->toPages($default);
}, },
/** /**
* Optional query to select a specific set of pages * Optional query to select a specific set of pages
*/ */
'query' => function (string $query = null) { 'query' => function (string $query = null) {
return $query; return $query;
}, },
/** /**
* Optionally include subpages of pages * Optionally include subpages of pages
*/ */
'subpages' => function (bool $subpages = true) { 'subpages' => function (bool $subpages = true) {
return $subpages; return $subpages;
}, },
'value' => function ($value = null) { 'value' => function ($value = null) {
return $this->toPages($value); return $this->toPages($value);
}, },
], ],
'computed' => [ 'computed' => [
/** /**
* Unset inherited computed * Unset inherited computed
*/ */
'default' => null 'default' => null
], ],
'methods' => [ 'methods' => [
'pageResponse' => function ($page) { 'pageResponse' => function ($page) {
return $page->panel()->pickerData([ return $page->panel()->pickerData([
'image' => $this->image, 'image' => $this->image,
'info' => $this->info, 'info' => $this->info,
'layout' => $this->layout, 'layout' => $this->layout,
'text' => $this->text, 'text' => $this->text,
]); ]);
}, },
'toPages' => function ($value = null) { 'toPages' => function ($value = null) {
$pages = []; $pages = [];
$kirby = kirby(); $kirby = App::instance();
foreach (Data::decode($value, 'yaml') as $id) { foreach (Data::decode($value, 'yaml') as $id) {
if (is_array($id) === true) { if (is_array($id) === true) {
$id = $id['id'] ?? null; $id = $id['id'] ?? null;
} }
if ($id !== null && ($page = $kirby->page($id))) { if ($id !== null && ($page = $kirby->page($id))) {
$pages[] = $this->pageResponse($page); $pages[] = $this->pageResponse($page);
} }
} }
return $pages; return $pages;
} }
], ],
'api' => function () { 'api' => function () {
return [ return [
[ [
'pattern' => '/', 'pattern' => '/',
'action' => function () { 'action' => function () {
$field = $this->field(); $field = $this->field();
return $field->pagepicker([ return $field->pagepicker([
'image' => $field->image(), 'image' => $field->image(),
'info' => $field->info(), 'info' => $field->info(),
'layout' => $field->layout(), 'layout' => $field->layout(),
'limit' => $field->limit(), 'limit' => $field->limit(),
'page' => $this->requestQuery('page'), 'page' => $this->requestQuery('page'),
'parent' => $this->requestQuery('parent'), 'parent' => $this->requestQuery('parent'),
'query' => $field->query(), 'query' => $field->query(),
'search' => $this->requestQuery('search'), 'search' => $this->requestQuery('search'),
'subpages' => $field->subpages(), 'subpages' => $field->subpages(),
'text' => $field->text() 'text' => $field->text()
]); ]);
} }
] ]
]; ];
}, },
'save' => function ($value = null) { 'save' => function ($value = null) {
return A::pluck($value, 'id'); return A::pluck($value, 'id');
}, },
'validations' => [ 'validations' => [
'max', 'max',
'min' 'min'
] ]
]; ];

View file

@ -1,29 +1,29 @@
<?php <?php
return [ return [
'mixins' => ['options'], 'mixins' => ['options'],
'props' => [ 'props' => [
/** /**
* Unset inherited props * Unset inherited props
*/ */
'after' => null, 'after' => null,
'before' => null, 'before' => null,
'icon' => null, 'icon' => null,
'placeholder' => null, 'placeholder' => null,
/** /**
* Arranges the radio buttons in the given number of columns * Arranges the radio buttons in the given number of columns
*/ */
'columns' => function (int $columns = 1) { 'columns' => function (int $columns = 1) {
return $columns; return $columns;
}, },
], ],
'computed' => [ 'computed' => [
'default' => function () { 'default' => function () {
return $this->sanitizeOption($this->default); return $this->sanitizeOption($this->default);
}, },
'value' => function () { 'value' => function () {
return $this->sanitizeOption($this->value) ?? ''; return $this->sanitizeOption($this->value) ?? '';
} }
] ]
]; ];

View file

@ -1,24 +1,24 @@
<?php <?php
return [ return [
'extends' => 'number', 'extends' => 'number',
'props' => [ 'props' => [
/** /**
* Unset inherited props * Unset inherited props
*/ */
'placeholder' => null, 'placeholder' => null,
/** /**
* The maximum value on the slider * The maximum value on the slider
*/ */
'max' => function (float $max = 100) { 'max' => function (float $max = 100) {
return $max; return $max;
}, },
/** /**
* Enables/disables the tooltip and set the before and after values * Enables/disables the tooltip and set the before and after values
*/ */
'tooltip' => function ($tooltip = true) { 'tooltip' => function ($tooltip = true) {
return $tooltip; return $tooltip;
}, },
] ]
]; ];

View file

@ -1,24 +1,24 @@
<?php <?php
return [ return [
'extends' => 'radio', 'extends' => 'radio',
'props' => [ 'props' => [
/** /**
* Unset inherited props * Unset inherited props
*/ */
'columns' => null, 'columns' => null,
/** /**
* Custom icon to replace the arrow down. * Custom icon to replace the arrow down.
*/ */
'icon' => function (string $icon = null) { 'icon' => function (string $icon = null) {
return $icon; return $icon;
}, },
/** /**
* Custom placeholder string for empty option. * Custom placeholder string for empty option.
*/ */
'placeholder' => function (string $placeholder = '—') { 'placeholder' => function (string $placeholder = '—') {
return $placeholder; return $placeholder;
}, },
] ]
]; ];

View file

@ -2,54 +2,54 @@
return [ return [
'extends' => 'text', 'extends' => 'text',
'props' => [ 'props' => [
/** /**
* Unset inherited props * Unset inherited props
*/ */
'converter' => null, 'converter' => null,
'counter' => null, 'counter' => null,
'spellcheck' => null, 'spellcheck' => null,
/** /**
* Set of characters allowed in the slug * Set of characters allowed in the slug
*/ */
'allow' => function (string $allow = '') { 'allow' => function (string $allow = '') {
return $allow; return $allow;
}, },
/** /**
* Changes the link icon * Changes the link icon
*/ */
'icon' => function (string $icon = 'url') { 'icon' => function (string $icon = 'url') {
return $icon; return $icon;
}, },
/** /**
* Set prefix for the help text * Set prefix for the help text
*/ */
'path' => function (string $path = null) { 'path' => function (string $path = null) {
return $path; return $path;
}, },
/** /**
* Name of another field that should be used to * Name of another field that should be used to
* automatically update this field's value * automatically update this field's value
*/ */
'sync' => function (string $sync = null) { 'sync' => function (string $sync = null) {
return $sync; return $sync;
}, },
/** /**
* Set to object with keys `field` and `text` to add * Set to object with keys `field` and `text` to add
* button to generate from another field * button to generate from another field
*/ */
'wizard' => function ($wizard = false) { 'wizard' => function ($wizard = false) {
return $wizard; return $wizard;
} }
], ],
'validations' => [ 'validations' => [
'minlength', 'minlength',
'maxlength' 'maxlength'
], ],
]; ];

View file

@ -5,189 +5,199 @@ use Kirby\Form\Form;
use Kirby\Toolkit\I18n; use Kirby\Toolkit\I18n;
return [ return [
'mixins' => ['min'], 'mixins' => ['min'],
'props' => [ 'props' => [
/** /**
* Unset inherited props * Unset inherited props
*/ */
'after' => null, 'after' => null,
'before' => null, 'before' => null,
'autofocus' => null, 'autofocus' => null,
'icon' => null, 'icon' => null,
'placeholder' => null, 'placeholder' => null,
/** /**
* Optional columns definition to only show selected fields in the structure table. * Optional columns definition to only show selected fields in the structure table.
*/ */
'columns' => function (array $columns = []) { 'columns' => function (array $columns = []) {
// lower case all keys, because field names will // lower case all keys, because field names will
// be lowercase as well. // be lowercase as well.
return array_change_key_case($columns); return array_change_key_case($columns);
}, },
/** /**
* Toggles duplicating rows for the structure * Toggles duplicating rows for the structure
*/ */
'duplicate' => function (bool $duplicate = true) { 'duplicate' => function (bool $duplicate = true) {
return $duplicate; return $duplicate;
}, },
/** /**
* The placeholder text if no items have been added yet * The placeholder text if no items have been added yet
*/ */
'empty' => function ($empty = null) { 'empty' => function ($empty = null) {
return I18n::translate($empty, $empty); return I18n::translate($empty, $empty);
}, },
/** /**
* Set the default rows for the structure * Set the default rows for the structure
*/ */
'default' => function (array $default = null) { 'default' => function (array $default = null) {
return $default; return $default;
}, },
/** /**
* Fields setup for the structure form. Works just like fields in regular forms. * Fields setup for the structure form. Works just like fields in regular forms.
*/ */
'fields' => function (array $fields) { 'fields' => function (array $fields) {
return $fields; return $fields;
}, },
/** /**
* The number of entries that will be displayed on a single page. Afterwards pagination kicks in. * The number of entries that will be displayed on a single page. Afterwards pagination kicks in.
*/ */
'limit' => function (int $limit = null) { 'limit' => function (int $limit = null) {
return $limit; return $limit;
}, },
/** /**
* Maximum allowed entries in the structure. Afterwards the "Add" button will be switched off. * Maximum allowed entries in the structure. Afterwards the "Add" button will be switched off.
*/ */
'max' => function (int $max = null) { 'max' => function (int $max = null) {
return $max; return $max;
}, },
/** /**
* Minimum required entries in the structure * Minimum required entries in the structure
*/ */
'min' => function (int $min = null) { 'min' => function (int $min = null) {
return $min; return $min;
}, },
/** /**
* Toggles adding to the top or bottom of the list * Toggles adding to the top or bottom of the list
*/ */
'prepend' => function (bool $prepend = null) { 'prepend' => function (bool $prepend = null) {
return $prepend; return $prepend;
}, },
/** /**
* Toggles drag & drop sorting * Toggles drag & drop sorting
*/ */
'sortable' => function (bool $sortable = null) { 'sortable' => function (bool $sortable = null) {
return $sortable; return $sortable;
}, },
/** /**
* Sorts the entries by the given field and order (i.e. `title desc`) * Sorts the entries by the given field and order (i.e. `title desc`)
* Drag & drop is disabled in this case * Drag & drop is disabled in this case
*/ */
'sortBy' => function (string $sort = null) { 'sortBy' => function (string $sort = null) {
return $sort; return $sort;
} }
], ],
'computed' => [ 'computed' => [
'default' => function () { 'default' => function () {
return $this->rows($this->default); return $this->rows($this->default);
}, },
'value' => function () { 'value' => function () {
return $this->rows($this->value); return $this->rows($this->value);
}, },
'fields' => function () { 'fields' => function () {
if (empty($this->fields) === true) { if (empty($this->fields) === true) {
throw new Exception('Please provide some fields for the structure'); throw new Exception('Please provide some fields for the structure');
} }
return $this->form()->fields()->toArray(); return $this->form()->fields()->toArray();
}, },
'columns' => function () { 'columns' => function () {
$columns = []; $columns = [];
$mobile = 0;
if (empty($this->columns)) { if (empty($this->columns)) {
foreach ($this->fields as $field) { foreach ($this->fields as $field) {
// Skip hidden and unsaveable fields
// They should never be included as column
if ($field['type'] === 'hidden' || $field['saveable'] === false) {
continue;
}
// Skip hidden and unsaveable fields $columns[$field['name']] = [
// They should never be included as column 'type' => $field['type'],
if ($field['type'] === 'hidden' || $field['saveable'] === false) { 'label' => $field['label'] ?? $field['name']
continue; ];
} }
} else {
foreach ($this->columns as $columnName => $columnProps) {
if (is_array($columnProps) === false) {
$columnProps = [];
}
$columns[$field['name']] = [ $field = $this->fields[$columnName] ?? null;
'type' => $field['type'],
'label' => $field['label'] ?? $field['name']
];
}
} else {
foreach ($this->columns as $columnName => $columnProps) {
if (is_array($columnProps) === false) {
$columnProps = [];
}
$field = $this->fields[$columnName] ?? null; if (empty($field) === true || $field['saveable'] === false) {
continue;
}
if (empty($field) === true || $field['saveable'] === false) { if (($columnProps['mobile'] ?? false) === true) {
continue; $mobile++;
} }
$columns[$columnName] = array_merge($columnProps, [ $columns[$columnName] = array_merge($columnProps, [
'type' => $field['type'], 'type' => $field['type'],
'label' => $field['label'] ?? $field['name'] 'label' => $field['label'] ?? $field['name']
]); ]);
} }
} }
return $columns; // make the first column visible on mobile
} // if no other mobile columns are defined
], if ($mobile === 0) {
'methods' => [ $columns[array_key_first($columns)]['mobile'] = true;
'rows' => function ($value) { }
$rows = Data::decode($value, 'yaml');
$value = [];
foreach ($rows as $index => $row) { return $columns;
if (is_array($row) === false) { }
continue; ],
} 'methods' => [
'rows' => function ($value) {
$rows = Data::decode($value, 'yaml');
$value = [];
$value[] = $this->form($row)->values(); foreach ($rows as $index => $row) {
} if (is_array($row) === false) {
continue;
}
return $value; $value[] = $this->form($row)->values();
}, }
'form' => function (array $values = []) {
return new Form([
'fields' => $this->attrs['fields'],
'values' => $values,
'model' => $this->model
]);
},
],
'api' => function () {
return [
[
'pattern' => 'validate',
'method' => 'ALL',
'action' => function () {
return array_values($this->field()->form($this->requestBody())->errors());
}
]
];
},
'save' => function ($value) {
$data = [];
foreach ($value as $row) { return $value;
$data[] = $this->form($row)->content(); },
} 'form' => function (array $values = []) {
return new Form([
'fields' => $this->attrs['fields'],
'values' => $values,
'model' => $this->model
]);
},
],
'api' => function () {
return [
[
'pattern' => 'validate',
'method' => 'ALL',
'action' => function () {
return array_values($this->field()->form($this->requestBody())->errors());
}
]
];
},
'save' => function ($value) {
$data = [];
return $data; foreach ($value as $row) {
}, $data[] = $this->form($row)->content();
'validations' => [ }
'min',
'max' return $data;
] },
'validations' => [
'min',
'max'
]
]; ];

View file

@ -5,99 +5,98 @@ use Kirby\Toolkit\Str;
use Kirby\Toolkit\V; use Kirby\Toolkit\V;
return [ return [
'mixins' => ['min', 'options'], 'mixins' => ['min', 'options'],
'props' => [ 'props' => [
/** /**
* Unset inherited props * Unset inherited props
*/ */
'after' => null, 'after' => null,
'before' => null, 'before' => null,
'placeholder' => null, 'placeholder' => null,
/** /**
* If set to `all`, any type of input is accepted. If set to `options` only the predefined options are accepted as input. * If set to `all`, any type of input is accepted. If set to `options` only the predefined options are accepted as input.
*/ */
'accept' => function ($value = 'all') { 'accept' => function ($value = 'all') {
return V::in($value, ['all', 'options']) ? $value : 'all'; return V::in($value, ['all', 'options']) ? $value : 'all';
}, },
/** /**
* Changes the tag icon * Changes the tag icon
*/ */
'icon' => function ($icon = 'tag') { 'icon' => function ($icon = 'tag') {
return $icon; return $icon;
}, },
/** /**
* Set to `list` to display each tag with 100% width, * Set to `list` to display each tag with 100% width,
* otherwise the tags are displayed inline * otherwise the tags are displayed inline
*/ */
'layout' => function (?string $layout = null) { 'layout' => function (?string $layout = null) {
return $layout; return $layout;
}, },
/** /**
* Minimum number of required entries/tags * Minimum number of required entries/tags
*/ */
'min' => function (int $min = null) { 'min' => function (int $min = null) {
return $min; return $min;
}, },
/** /**
* Maximum number of allowed entries/tags * Maximum number of allowed entries/tags
*/ */
'max' => function (int $max = null) { 'max' => function (int $max = null) {
return $max; return $max;
}, },
/** /**
* Custom tags separator, which will be used to store tags in the content file * Custom tags separator, which will be used to store tags in the content file
*/ */
'separator' => function (string $separator = ',') { 'separator' => function (string $separator = ',') {
return $separator; return $separator;
}, },
], ],
'computed' => [ 'computed' => [
'default' => function (): array { 'default' => function (): array {
return $this->toTags($this->default); return $this->toTags($this->default);
}, },
'value' => function (): array { 'value' => function (): array {
return $this->toTags($this->value); return $this->toTags($this->value);
} }
], ],
'methods' => [ 'methods' => [
'toTags' => function ($value) { 'toTags' => function ($value) {
if (is_null($value) === true) { if (is_null($value) === true) {
return []; return [];
} }
$options = $this->options(); $options = $this->options();
// transform into value-text objects // transform into value-text objects
return array_map(function ($option) use ($options) { return array_map(function ($option) use ($options) {
// already a valid object
if (is_array($option) === true && isset($option['value'], $option['text']) === true) {
return $option;
}
// already a valid object $index = array_search($option, array_column($options, 'value'));
if (is_array($option) === true && isset($option['value'], $option['text']) === true) {
return $option;
}
$index = array_search($option, array_column($options, 'value')); if ($index !== false) {
return $options[$index];
}
if ($index !== false) { return [
return $options[$index]; 'value' => $option,
} 'text' => $option,
];
return [ }, Str::split($value, $this->separator()));
'value' => $option, }
'text' => $option, ],
]; 'save' => function (array $value = null): string {
}, Str::split($value, $this->separator())); return A::join(
} A::pluck($value, 'value'),
], $this->separator() . ' '
'save' => function (array $value = null): string { );
return A::join( },
A::pluck($value, 'value'), 'validations' => [
$this->separator() . ' ' 'min',
); 'max'
}, ]
'validations' => [
'min',
'max'
]
]; ];

View file

@ -1,27 +1,27 @@
<?php <?php
return [ return [
'extends' => 'text', 'extends' => 'text',
'props' => [ 'props' => [
/** /**
* Unset inherited props * Unset inherited props
*/ */
'converter' => null, 'converter' => null,
'counter' => null, 'counter' => null,
'spellcheck' => null, 'spellcheck' => null,
/** /**
* Sets the HTML5 autocomplete attribute * Sets the HTML5 autocomplete attribute
*/ */
'autocomplete' => function (string $autocomplete = 'tel') { 'autocomplete' => function (string $autocomplete = 'tel') {
return $autocomplete; return $autocomplete;
}, },
/** /**
* Changes the phone icon * Changes the phone icon
*/ */
'icon' => function (string $icon = 'phone') { 'icon' => function (string $icon = 'phone') {
return $icon; return $icon;
} }
] ]
]; ];

View file

@ -4,99 +4,99 @@ use Kirby\Exception\InvalidArgumentException;
use Kirby\Toolkit\Str; use Kirby\Toolkit\Str;
return [ return [
'props' => [ 'props' => [
/** /**
* The field value will be converted with the selected converter before the value gets saved. Available converters: `lower`, `upper`, `ucfirst`, `slug` * The field value will be converted with the selected converter before the value gets saved. Available converters: `lower`, `upper`, `ucfirst`, `slug`
*/ */
'converter' => function ($value = null) { 'converter' => function ($value = null) {
if ($value !== null && in_array($value, array_keys($this->converters())) === false) { if ($value !== null && in_array($value, array_keys($this->converters())) === false) {
throw new InvalidArgumentException([ throw new InvalidArgumentException([
'key' => 'field.converter.invalid', 'key' => 'field.converter.invalid',
'data' => ['converter' => $value] 'data' => ['converter' => $value]
]); ]);
} }
return $value; return $value;
}, },
/** /**
* Shows or hides the character counter in the top right corner * Shows or hides the character counter in the top right corner
*/ */
'counter' => function (bool $counter = true) { 'counter' => function (bool $counter = true) {
return $counter; return $counter;
}, },
/** /**
* Maximum number of allowed characters * Maximum number of allowed characters
*/ */
'maxlength' => function (int $maxlength = null) { 'maxlength' => function (int $maxlength = null) {
return $maxlength; return $maxlength;
}, },
/** /**
* Minimum number of required characters * Minimum number of required characters
*/ */
'minlength' => function (int $minlength = null) { 'minlength' => function (int $minlength = null) {
return $minlength; return $minlength;
}, },
/** /**
* A regular expression, which will be used to validate the input * A regular expression, which will be used to validate the input
*/ */
'pattern' => function (string $pattern = null) { 'pattern' => function (string $pattern = null) {
return $pattern; return $pattern;
}, },
/** /**
* If `false`, spellcheck will be switched off * If `false`, spellcheck will be switched off
*/ */
'spellcheck' => function (bool $spellcheck = false) { 'spellcheck' => function (bool $spellcheck = false) {
return $spellcheck; return $spellcheck;
}, },
], ],
'computed' => [ 'computed' => [
'default' => function () { 'default' => function () {
return $this->convert($this->default); return $this->convert($this->default);
}, },
'value' => function () { 'value' => function () {
return (string)$this->convert($this->value); return (string)$this->convert($this->value);
} }
], ],
'methods' => [ 'methods' => [
'convert' => function ($value) { 'convert' => function ($value) {
if ($this->converter() === null) { if ($this->converter() === null) {
return $value; return $value;
} }
$converter = $this->converters()[$this->converter()]; $converter = $this->converters()[$this->converter()];
if (is_array($value) === true) { if (is_array($value) === true) {
return array_map($converter, $value); return array_map($converter, $value);
} }
return call_user_func($converter, trim($value ?? '')); return call_user_func($converter, trim($value ?? ''));
}, },
'converters' => function (): array { 'converters' => function (): array {
return [ return [
'lower' => function ($value) { 'lower' => function ($value) {
return Str::lower($value); return Str::lower($value);
}, },
'slug' => function ($value) { 'slug' => function ($value) {
return Str::slug($value); return Str::slug($value);
}, },
'ucfirst' => function ($value) { 'ucfirst' => function ($value) {
return Str::ucfirst($value); return Str::ucfirst($value);
}, },
'upper' => function ($value) { 'upper' => function ($value) {
return Str::upper($value); return Str::upper($value);
}, },
]; ];
}, },
], ],
'validations' => [ 'validations' => [
'minlength', 'minlength',
'maxlength', 'maxlength',
'pattern' 'pattern'
] ]
]; ];

View file

@ -1,123 +1,123 @@
<?php <?php
return [ return [
'mixins' => ['filepicker', 'upload'], 'mixins' => ['filepicker', 'upload'],
'props' => [ 'props' => [
/** /**
* Unset inherited props * Unset inherited props
*/ */
'after' => null, 'after' => null,
'before' => null, 'before' => null,
/** /**
* Enables/disables the format buttons. Can either be `true`/`false` or a list of allowed buttons. Available buttons: `headlines`, `italic`, `bold`, `link`, `email`, `file`, `code`, `ul`, `ol` (as well as `|` for a divider) * Enables/disables the format buttons. Can either be `true`/`false` or a list of allowed buttons. Available buttons: `headlines`, `italic`, `bold`, `link`, `email`, `file`, `code`, `ul`, `ol` (as well as `|` for a divider)
*/ */
'buttons' => function ($buttons = true) { 'buttons' => function ($buttons = true) {
return $buttons; return $buttons;
}, },
/** /**
* Enables/disables the character counter in the top right corner * Enables/disables the character counter in the top right corner
*/ */
'counter' => function (bool $counter = true) { 'counter' => function (bool $counter = true) {
return $counter; return $counter;
}, },
/** /**
* Sets the default text when a new page/file/user is created * Sets the default text when a new page/file/user is created
*/ */
'default' => function (string $default = null) { 'default' => function (string $default = null) {
return trim($default ?? ''); return trim($default ?? '');
}, },
/** /**
* Sets the options for the files picker * Sets the options for the files picker
*/ */
'files' => function ($files = []) { 'files' => function ($files = []) {
if (is_string($files) === true) { if (is_string($files) === true) {
return ['query' => $files]; return ['query' => $files];
} }
if (is_array($files) === false) { if (is_array($files) === false) {
$files = []; $files = [];
} }
return $files; return $files;
}, },
/** /**
* Sets the font family (sans or monospace) * Sets the font family (sans or monospace)
*/ */
'font' => function (string $font = null) { 'font' => function (string $font = null) {
return $font === 'monospace' ? 'monospace' : 'sans-serif'; return $font === 'monospace' ? 'monospace' : 'sans-serif';
}, },
/** /**
* Maximum number of allowed characters * Maximum number of allowed characters
*/ */
'maxlength' => function (int $maxlength = null) { 'maxlength' => function (int $maxlength = null) {
return $maxlength; return $maxlength;
}, },
/** /**
* Minimum number of required characters * Minimum number of required characters
*/ */
'minlength' => function (int $minlength = null) { 'minlength' => function (int $minlength = null) {
return $minlength; return $minlength;
}, },
/** /**
* Changes the size of the textarea. Available sizes: `small`, `medium`, `large`, `huge` * Changes the size of the textarea. Available sizes: `small`, `medium`, `large`, `huge`
*/ */
'size' => function (string $size = null) { 'size' => function (string $size = null) {
return $size; return $size;
}, },
/** /**
* If `false`, spellcheck will be switched off * If `false`, spellcheck will be switched off
*/ */
'spellcheck' => function (bool $spellcheck = true) { 'spellcheck' => function (bool $spellcheck = true) {
return $spellcheck; return $spellcheck;
}, },
'value' => function (string $value = null) { 'value' => function (string $value = null) {
return trim($value ?? ''); return trim($value ?? '');
} }
], ],
'api' => function () { 'api' => function () {
return [ return [
[ [
'pattern' => 'files', 'pattern' => 'files',
'action' => function () { 'action' => function () {
$params = array_merge($this->field()->files(), [ $params = array_merge($this->field()->files(), [
'page' => $this->requestQuery('page'), 'page' => $this->requestQuery('page'),
'search' => $this->requestQuery('search') 'search' => $this->requestQuery('search')
]); ]);
return $this->field()->filepicker($params); return $this->field()->filepicker($params);
} }
], ],
[ [
'pattern' => 'upload', 'pattern' => 'upload',
'method' => 'POST', 'method' => 'POST',
'action' => function () { 'action' => function () {
$field = $this->field(); $field = $this->field();
$uploads = $field->uploads(); $uploads = $field->uploads();
return $this->field()->upload($this, $uploads, function ($file, $parent) use ($field) { return $this->field()->upload($this, $uploads, function ($file, $parent) use ($field) {
$absolute = $field->model()->is($parent) === false; $absolute = $field->model()->is($parent) === false;
return [ return [
'filename' => $file->filename(), 'filename' => $file->filename(),
'dragText' => $file->panel()->dragText('auto', $absolute), 'dragText' => $file->panel()->dragText('auto', $absolute),
]; ];
}); });
} }
] ]
]; ];
}, },
'validations' => [ 'validations' => [
'minlength', 'minlength',
'maxlength' 'maxlength'
] ]
]; ];

View file

@ -5,122 +5,122 @@ use Kirby\Toolkit\Date;
use Kirby\Toolkit\I18n; use Kirby\Toolkit\I18n;
return [ return [
'mixins' => ['datetime'], 'mixins' => ['datetime'],
'props' => [ 'props' => [
/** /**
* Unset inherited props * Unset inherited props
*/ */
'placeholder' => null, 'placeholder' => null,
/** /**
* Sets the default time when a new page/file/user is created * Sets the default time when a new page/file/user is created
*/ */
'default' => function ($default = null): ?string { 'default' => function ($default = null): ?string {
return $default; return $default;
}, },
/** /**
* Custom format (dayjs tokens: `HH`, `hh`, `mm`, `ss`, `a`) that is * Custom format (dayjs tokens: `HH`, `hh`, `mm`, `ss`, `a`) that is
* used to display the field in the Panel * used to display the field in the Panel
*/ */
'display' => function ($display = null) { 'display' => function ($display = null) {
return I18n::translate($display, $display); return I18n::translate($display, $display);
}, },
/** /**
* Changes the clock icon * Changes the clock icon
*/ */
'icon' => function (string $icon = 'clock') { 'icon' => function (string $icon = 'clock') {
return $icon; return $icon;
}, },
/** /**
* Latest time, which can be selected/saved (H:i or H:i:s) * Latest time, which can be selected/saved (H:i or H:i:s)
*/ */
'max' => function (string $max = null): ?string { 'max' => function (string $max = null): ?string {
return Date::optional($max); return Date::optional($max);
}, },
/** /**
* Earliest time, which can be selected/saved (H:i or H:i:s) * Earliest time, which can be selected/saved (H:i or H:i:s)
*/ */
'min' => function (string $min = null): ?string { 'min' => function (string $min = null): ?string {
return Date::optional($min); return Date::optional($min);
}, },
/** /**
* `12` or `24` hour notation. If `12`, an AM/PM selector will be shown. * `12` or `24` hour notation. If `12`, an AM/PM selector will be shown.
* If `display` is defined, that option will take priority. * If `display` is defined, that option will take priority.
*/ */
'notation' => function (int $value = 24) { 'notation' => function (int $value = 24) {
return $value === 24 ? 24 : 12; return $value === 24 ? 24 : 12;
}, },
/** /**
* Round to the nearest: sub-options for `unit` (minute) and `size` (5) * Round to the nearest: sub-options for `unit` (minute) and `size` (5)
*/ */
'step' => function ($step = null) { 'step' => function ($step = null) {
return Date::stepConfig($step, [ return Date::stepConfig($step, [
'size' => 5, 'size' => 5,
'unit' => 'minute', 'unit' => 'minute',
]); ]);
}, },
'value' => function ($value = null): ?string { 'value' => function ($value = null): ?string {
return $value; return $value;
} }
], ],
'computed' => [ 'computed' => [
'display' => function () { 'display' => function () {
if ($this->display) { if ($this->display) {
return $this->display; return $this->display;
} }
return $this->notation === 24 ? 'HH:mm' : 'hh:mm a'; return $this->notation === 24 ? 'HH:mm' : 'hh:mm a';
}, },
'default' => function (): string { 'default' => function (): string {
return $this->toDatetime($this->default, 'H:i:s') ?? ''; return $this->toDatetime($this->default, 'H:i:s') ?? '';
}, },
'format' => function () { 'format' => function () {
return $this->props['format'] ?? 'H:i:s'; return $this->props['format'] ?? 'H:i:s';
}, },
'value' => function (): ?string { 'value' => function (): ?string {
return $this->toDatetime($this->value, 'H:i:s') ?? ''; return $this->toDatetime($this->value, 'H:i:s') ?? '';
} }
], ],
'validations' => [ 'validations' => [
'time', 'time',
'minMax' => function ($value) { 'minMax' => function ($value) {
if (!$value = Date::optional($value)) { if (!$value = Date::optional($value)) {
return true; return true;
} }
$min = Date::optional($this->min); $min = Date::optional($this->min);
$max = Date::optional($this->max); $max = Date::optional($this->max);
$format = 'H:i:s'; $format = 'H:i:s';
if ($min && $max && $value->isBetween($min, $max) === false) { if ($min && $max && $value->isBetween($min, $max) === false) {
throw new Exception([ throw new Exception([
'key' => 'validation.time.between', 'key' => 'validation.time.between',
'data' => [ 'data' => [
'min' => $min->format($format), 'min' => $min->format($format),
'max' => $min->format($format) 'max' => $min->format($format)
] ]
]); ]);
} elseif ($min && $value->isMin($min) === false) { } elseif ($min && $value->isMin($min) === false) {
throw new Exception([ throw new Exception([
'key' => 'validation.time.after', 'key' => 'validation.time.after',
'data' => [ 'data' => [
'time' => $min->format($format), 'time' => $min->format($format),
] ]
]); ]);
} elseif ($max && $value->isMax($max) === false) { } elseif ($max && $value->isMax($max) === false) {
throw new Exception([ throw new Exception([
'key' => 'validation.time.before', 'key' => 'validation.time.before',
'data' => [ 'data' => [
'time' => $max->format($format), 'time' => $max->format($format),
] ]
]); ]);
} }
return true; return true;
}, },
] ]
]; ];

View file

@ -5,69 +5,69 @@ use Kirby\Toolkit\A;
use Kirby\Toolkit\I18n; use Kirby\Toolkit\I18n;
return [ return [
'props' => [ 'props' => [
/** /**
* Unset inherited props * Unset inherited props
*/ */
'placeholder' => null, 'placeholder' => null,
/** /**
* Default value which will be saved when a new page/user/file is created * Default value which will be saved when a new page/user/file is created
*/ */
'default' => function ($default = null) { 'default' => function ($default = null) {
return $this->default = $default; return $this->default = $default;
}, },
/** /**
* Sets the text next to the toggle. The text can be a string or an array of two options. The first one is the negative text and the second one the positive. The text will automatically switch when the toggle is triggered. * Sets the text next to the toggle. The text can be a string or an array of two options. The first one is the negative text and the second one the positive. The text will automatically switch when the toggle is triggered.
*/ */
'text' => function ($value = null) { 'text' => function ($value = null) {
$model = $this->model(); $model = $this->model();
if (is_array($value) === true) { if (is_array($value) === true) {
if (A::isAssociative($value) === true) { if (A::isAssociative($value) === true) {
return $model->toSafeString(I18n::translate($value, $value)); return $model->toSafeString(I18n::translate($value, $value));
} }
foreach ($value as $key => $val) { foreach ($value as $key => $val) {
$value[$key] = $model->toSafeString(I18n::translate($val, $val)); $value[$key] = $model->toSafeString(I18n::translate($val, $val));
} }
return $value; return $value;
} }
if (empty($value) === false) { if (empty($value) === false) {
return $model->toSafeString(I18n::translate($value, $value)); return $model->toSafeString(I18n::translate($value, $value));
} }
return $value; return $value;
}, },
], ],
'computed' => [ 'computed' => [
'default' => function () { 'default' => function () {
return $this->toBool($this->default); return $this->toBool($this->default);
}, },
'value' => function () { 'value' => function () {
if ($this->props['value'] === null) { if ($this->props['value'] === null) {
return $this->default(); return $this->default();
} else { } else {
return $this->toBool($this->props['value']); return $this->toBool($this->props['value']);
} }
} }
], ],
'methods' => [ 'methods' => [
'toBool' => function ($value) { 'toBool' => function ($value) {
return in_array($value, [true, 'true', 1, '1', 'on'], true) === true; return in_array($value, [true, 'true', 1, '1', 'on'], true) === true;
} }
], ],
'save' => function (): string { 'save' => function (): string {
return $this->value() === true ? 'true' : 'false'; return $this->value() === true ? 'true' : 'false';
}, },
'validations' => [ 'validations' => [
'boolean', 'boolean',
'required' => function ($value) { 'required' => function ($value) {
if ($this->isRequired() && ($value === false || $this->isEmpty($value))) { if ($this->isRequired() && ($value === false || $this->isEmpty($value))) {
throw new InvalidArgumentException(I18n::translate('field.required')); throw new InvalidArgumentException(I18n::translate('field.required'));
} }
}, },
] ]
]; ];

View file

@ -0,0 +1,41 @@
<?php
return [
'mixins' => ['options'],
'props' => [
/**
* Unset inherited props
*/
'after' => null,
'before' => null,
'icon' => null,
'placeholder' => null,
/**
* Toggles will automatically span the full width of the field. With the grow option, you can disable this behaviour for a more compact layout.
*/
'grow' => function (bool $grow = true) {
return $grow;
},
/**
* If `false` all labels will be hidden for icon-only toggles.
*/
'labels' => function (bool $labels = true) {
return $labels;
},
/**
* A toggle can be deactivated on click. If reset is `false` deactivating a toggle is no longer possible.
*/
'reset' => function (bool $reset = true) {
return $reset;
}
],
'computed' => [
'default' => function () {
return $this->sanitizeOption($this->default);
},
'value' => function () {
return $this->sanitizeOption($this->value) ?? '';
},
]
];

View file

@ -3,39 +3,40 @@
use Kirby\Toolkit\I18n; use Kirby\Toolkit\I18n;
return [ return [
'extends' => 'text', 'extends' => 'text',
'props' => [ 'props' => [
/** /**
* Unset inherited props * Unset inherited props
*/ */
'converter' => null, 'converter' => null,
'counter' => null, 'counter' => null,
'spellcheck' => null, 'pattern' => null,
'spellcheck' => null,
/** /**
* Sets the HTML5 autocomplete attribute * Sets the HTML5 autocomplete attribute
*/ */
'autocomplete' => function (string $autocomplete = 'url') { 'autocomplete' => function (string $autocomplete = 'url') {
return $autocomplete; return $autocomplete;
}, },
/** /**
* Changes the link icon * Changes the link icon
*/ */
'icon' => function (string $icon = 'url') { 'icon' => function (string $icon = 'url') {
return $icon; return $icon;
}, },
/** /**
* Sets custom placeholder text, when the field is empty * Sets custom placeholder text, when the field is empty
*/ */
'placeholder' => function ($value = null) { 'placeholder' => function ($value = null) {
return I18n::translate($value, $value) ?? 'https://example.com'; return I18n::translate($value, $value) ?? 'https://example.com';
} }
], ],
'validations' => [ 'validations' => [
'minlength', 'minlength',
'maxlength', 'maxlength',
'url' 'url'
], ],
]; ];

View file

@ -1,104 +1,105 @@
<?php <?php
use Kirby\Cms\App;
use Kirby\Data\Data; use Kirby\Data\Data;
use Kirby\Toolkit\A; use Kirby\Toolkit\A;
return [ return [
'mixins' => [ 'mixins' => [
'layout', 'layout',
'min', 'min',
'picker', 'picker',
'userpicker' 'userpicker'
], ],
'props' => [ 'props' => [
/** /**
* Unset inherited props * Unset inherited props
*/ */
'after' => null, 'after' => null,
'autofocus' => null, 'autofocus' => null,
'before' => null, 'before' => null,
'icon' => null, 'icon' => null,
'placeholder' => null, 'placeholder' => null,
/** /**
* Default selected user(s) when a new page/file/user is created * Default selected user(s) when a new page/file/user is created
*/ */
'default' => function ($default = null) { 'default' => function ($default = null) {
if ($default === false) { if ($default === false) {
return []; return [];
} }
if ($default === null && $user = $this->kirby()->user()) { if ($default === null && $user = $this->kirby()->user()) {
return [ return [
$this->userResponse($user) $this->userResponse($user)
]; ];
} }
return $this->toUsers($default); return $this->toUsers($default);
}, },
'value' => function ($value = null) { 'value' => function ($value = null) {
return $this->toUsers($value); return $this->toUsers($value);
}, },
], ],
'computed' => [ 'computed' => [
/** /**
* Unset inherited computed * Unset inherited computed
*/ */
'default' => null 'default' => null
], ],
'methods' => [ 'methods' => [
'userResponse' => function ($user) { 'userResponse' => function ($user) {
return $user->panel()->pickerData([ return $user->panel()->pickerData([
'info' => $this->info, 'info' => $this->info,
'image' => $this->image, 'image' => $this->image,
'layout' => $this->layout, 'layout' => $this->layout,
'text' => $this->text, 'text' => $this->text,
]); ]);
}, },
'toUsers' => function ($value = null) { 'toUsers' => function ($value = null) {
$users = []; $users = [];
$kirby = kirby(); $kirby = App::instance();
foreach (Data::decode($value, 'yaml') as $email) { foreach (Data::decode($value, 'yaml') as $email) {
if (is_array($email) === true) { if (is_array($email) === true) {
$email = $email['email'] ?? null; $email = $email['email'] ?? null;
} }
if ($email !== null && ($user = $kirby->user($email))) { if ($email !== null && ($user = $kirby->user($email))) {
$users[] = $this->userResponse($user); $users[] = $this->userResponse($user);
} }
} }
return $users; return $users;
} }
], ],
'api' => function () { 'api' => function () {
return [ return [
[ [
'pattern' => '/', 'pattern' => '/',
'action' => function () { 'action' => function () {
$field = $this->field(); $field = $this->field();
return $field->userpicker([ return $field->userpicker([
'image' => $field->image(), 'image' => $field->image(),
'info' => $field->info(), 'info' => $field->info(),
'layout' => $field->layout(), 'layout' => $field->layout(),
'limit' => $field->limit(), 'limit' => $field->limit(),
'page' => $this->requestQuery('page'), 'page' => $this->requestQuery('page'),
'query' => $field->query(), 'query' => $field->query(),
'search' => $this->requestQuery('search'), 'search' => $this->requestQuery('search'),
'text' => $field->text() 'text' => $field->text()
]); ]);
} }
] ]
]; ];
}, },
'save' => function ($value = null) { 'save' => function ($value = null) {
return A::pluck($value, 'id'); return A::pluck($value, 'id');
}, },
'validations' => [ 'validations' => [
'max', 'max',
'min' 'min'
] ]
]; ];

View file

@ -3,34 +3,34 @@
use Kirby\Sane\Sane; use Kirby\Sane\Sane;
return [ return [
'props' => [ 'props' => [
/** /**
* Enables inline mode, which will not wrap new lines in paragraphs and creates hard breaks instead. * Enables inline mode, which will not wrap new lines in paragraphs and creates hard breaks instead.
* *
* @param bool $inline * @param bool $inline
*/ */
'inline' => function (bool $inline = false) { 'inline' => function (bool $inline = false) {
return $inline; return $inline;
}, },
/** /**
* Sets the allowed HTML formats. Available formats: `bold`, `italic`, `underline`, `strike`, `code`, `link`, `email`. Activate them all by passing `true`. Deactivate them all by passing `false` * Sets the allowed HTML formats. Available formats: `bold`, `italic`, `underline`, `strike`, `code`, `link`, `email`. Activate them all by passing `true`. Deactivate them all by passing `false`
* @param array|bool $marks * @param array|bool $marks
*/ */
'marks' => function ($marks = true) { 'marks' => function ($marks = true) {
return $marks; return $marks;
}, },
/** /**
* Sets the allowed nodes. Available nodes: `paragraph`, `heading`, `bulletList`, `orderedList`. Activate/deactivate them all by passing `true`/`false`. Default nodes are `paragraph`, `heading`, `bulletList`, `orderedList`. * Sets the allowed nodes. Available nodes: `paragraph`, `heading`, `bulletList`, `orderedList`. Activate/deactivate them all by passing `true`/`false`. Default nodes are `paragraph`, `heading`, `bulletList`, `orderedList`.
* @param array|bool|null $nodes * @param array|bool|null $nodes
*/ */
'nodes' => function ($nodes = null) { 'nodes' => function ($nodes = null) {
return $nodes; return $nodes;
} }
], ],
'computed' => [ 'computed' => [
'value' => function () { 'value' => function () {
$value = trim($this->value ?? ''); $value = trim($this->value ?? '');
return Sane::sanitize($value, 'html'); return Sane::sanitize($value, 'html');
} }
], ],
]; ];

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

Some files were not shown because too many files have changed in this diff Show more