diff --git a/public/composer.lock b/public/composer.lock index 08f41ec..d615fae 100644 --- a/public/composer.lock +++ b/public/composer.lock @@ -1,1281 +1,1171 @@ { - "_readme": [ - "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", - "This file is @generated automatically" - ], - "content-hash": "fa260be9c3ea255684234b81e836c746", - "packages": [ - { - "name": "christian-riesen/base32", - "version": "1.6.0", - "source": { - "type": "git", - "url": "https://github.com/ChristianRiesen/base32.git", - "reference": "2e82dab3baa008e24a505649b0d583c31d31e894" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ChristianRiesen/base32/zipball/2e82dab3baa008e24a505649b0d583c31d31e894", - "reference": "2e82dab3baa008e24a505649b0d583c31d31e894", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^2.17", - "phpstan/phpstan": "^0.12", - "phpunit/phpunit": "^8.5.13 || ^9.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Base32\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Christian Riesen", - "email": "chris.riesen@gmail.com", - "homepage": "http://christianriesen.com", - "role": "Developer" - } - ], - "description": "Base32 encoder/decoder according to RFC 4648", - "homepage": "https://github.com/ChristianRiesen/base32", - "keywords": [ - "base32", - "decode", - "encode", - "rfc4648" - ], - "support": { - "issues": "https://github.com/ChristianRiesen/base32/issues", - "source": "https://github.com/ChristianRiesen/base32/tree/1.6.0" - }, - "time": "2021-02-26T10:19:33+00:00" - }, - { - "name": "claviska/simpleimage", - "version": "4.2.1", - "source": { - "type": "git", - "url": "https://github.com/claviska/SimpleImage.git", - "reference": "ec6d5021e5a7153a2520d64c59b86b6f3c4157c5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/claviska/SimpleImage/zipball/ec6d5021e5a7153a2520d64c59b86b6f3c4157c5", - "reference": "ec6d5021e5a7153a2520d64c59b86b6f3c4157c5", - "shasum": "" - }, - "require": { - "ext-gd": "*", - "league/color-extractor": "0.4.*", - "php": ">=8.0" - }, - "require-dev": { - "laravel/pint": "^1.5", - "phpstan/phpstan": "^1.10" - }, - "type": "library", - "autoload": { - "psr-0": { - "claviska": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Cory LaViska", - "homepage": "http://www.abeautifulsite.net/", - "role": "Developer" - } - ], - "description": "A PHP class that makes working with images as simple as possible.", - "support": { - "issues": "https://github.com/claviska/SimpleImage/issues", - "source": "https://github.com/claviska/SimpleImage/tree/4.2.1" - }, - "funding": [ - { - "url": "https://github.com/claviska", - "type": "github" - } - ], - "time": "2024-11-22T13:25:03+00:00" - }, - { - "name": "composer/semver", - "version": "3.4.4", - "source": { - "type": "git", - "url": "https://github.com/composer/semver.git", - "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/198166618906cb2de69b95d7d47e5fa8aa1b2b95", - "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95", - "shasum": "" - }, - "require": { - "php": "^5.3.2 || ^7.0 || ^8.0" - }, - "require-dev": { - "phpstan/phpstan": "^1.11", - "symfony/phpunit-bridge": "^3 || ^7" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.x-dev" - } - }, - "autoload": { - "psr-4": { - "Composer\\Semver\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nils Adermann", - "email": "naderman@naderman.de", - "homepage": "http://www.naderman.de" - }, - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - }, - { - "name": "Rob Bast", - "email": "rob.bast@gmail.com", - "homepage": "http://robbast.nl" - } - ], - "description": "Semver library that offers utilities, version constraint parsing and validation.", - "keywords": [ - "semantic", - "semver", - "validation", - "versioning" - ], - "support": { - "irc": "ircs://irc.libera.chat:6697/composer", - "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/3.4.4" - }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - } - ], - "time": "2025-08-20T19:15:30+00:00" - }, - { - "name": "filp/whoops", - "version": "2.18.4", - "source": { - "type": "git", - "url": "https://github.com/filp/whoops.git", - "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/filp/whoops/zipball/d2102955e48b9fd9ab24280a7ad12ed552752c4d", - "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0", - "psr/log": "^1.0.1 || ^2.0 || ^3.0" - }, - "require-dev": { - "mockery/mockery": "^1.0", - "phpunit/phpunit": "^7.5.20 || ^8.5.8 || ^9.3.3", - "symfony/var-dumper": "^4.0 || ^5.0" - }, - "suggest": { - "symfony/var-dumper": "Pretty print complex values better with var-dumper available", - "whoops/soap": "Formats errors as SOAP responses" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.7-dev" - } - }, - "autoload": { - "psr-4": { - "Whoops\\": "src/Whoops/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Filipe Dobreira", - "homepage": "https://github.com/filp", - "role": "Developer" - } - ], - "description": "php error handling for cool kids", - "homepage": "https://filp.github.io/whoops/", - "keywords": [ - "error", - "exception", - "handling", - "library", - "throwable", - "whoops" - ], - "support": { - "issues": "https://github.com/filp/whoops/issues", - "source": "https://github.com/filp/whoops/tree/2.18.4" - }, - "funding": [ - { - "url": "https://github.com/denis-sokolov", - "type": "github" - } - ], - "time": "2025-08-08T12:00:00+00:00" - }, - { - "name": "getkirby/cms", - "version": "5.1.1", - "source": { - "type": "git", - "url": "https://github.com/getkirby/kirby.git", - "reference": "fb11f5e3ec422e948fb1a52f16988335bb3489b4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/getkirby/kirby/zipball/fb11f5e3ec422e948fb1a52f16988335bb3489b4", - "reference": "fb11f5e3ec422e948fb1a52f16988335bb3489b4", - "shasum": "" - }, - "require": { - "christian-riesen/base32": "1.6.0", - "claviska/simpleimage": "4.2.1", - "composer/semver": "3.4.4", - "ext-ctype": "*", - "ext-curl": "*", - "ext-dom": "*", - "ext-filter": "*", - "ext-hash": "*", - "ext-iconv": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-openssl": "*", - "ext-simplexml": "*", - "filp/whoops": "2.18.4", - "getkirby/composer-installer": "^1.2.1", - "laminas/laminas-escaper": "2.17.0", - "michelf/php-smartypants": "1.8.1", - "php": "~8.2.0 || ~8.3.0 || ~8.4.0", - "phpmailer/phpmailer": "6.10.0", - "symfony/polyfill-intl-idn": "1.33.0", - "symfony/polyfill-mbstring": "1.33.0", - "symfony/yaml": "7.3.3" - }, - "replace": { - "symfony/polyfill-php72": "*" - }, - "suggest": { - "ext-PDO": "Support for using databases", - "ext-apcu": "Support for the Apcu cache driver", - "ext-exif": "Support for exif information from images", - "ext-fileinfo": "Improved mime type detection for files", - "ext-imagick": "Improved thumbnail generation", - "ext-intl": "Improved i18n number formatting", - "ext-memcached": "Support for the Memcached cache driver", - "ext-redis": "Support for the Redis cache driver", - "ext-sodium": "Support for the crypto class and more robust session handling", - "ext-zip": "Support for ZIP archive file functions", - "ext-zlib": "Sanitization and validation for svgz files" - }, - "type": "kirby-cms", - "extra": { - "unused": [ - "symfony/polyfill-intl-idn" - ] - }, - "autoload": { - "files": [ - "config/setup.php", - "config/helpers.php" - ], - "psr-4": { - "Kirby\\": "src/" - }, - "classmap": [ - "dependencies/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "proprietary" - ], - "authors": [ - { - "name": "Kirby Team", - "email": "support@getkirby.com", - "homepage": "https://getkirby.com" - } - ], - "description": "The Kirby core", - "homepage": "https://getkirby.com", - "keywords": [ - "cms", - "core", - "kirby" - ], - "support": { - "email": "support@getkirby.com", - "forum": "https://forum.getkirby.com", - "issues": "https://github.com/getkirby/kirby/issues", - "source": "https://github.com/getkirby/kirby" - }, - "funding": [ - { - "url": "https://getkirby.com/buy", - "type": "custom" - } - ], - "time": "2025-09-16T13:06:53+00:00" - }, - { - "name": "getkirby/composer-installer", - "version": "1.2.1", - "source": { - "type": "git", - "url": "https://github.com/getkirby/composer-installer.git", - "reference": "c98ece30bfba45be7ce457e1102d1b169d922f3d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/getkirby/composer-installer/zipball/c98ece30bfba45be7ce457e1102d1b169d922f3d", - "reference": "c98ece30bfba45be7ce457e1102d1b169d922f3d", - "shasum": "" - }, - "require": { - "composer-plugin-api": "^1.0 || ^2.0" - }, - "require-dev": { - "composer/composer": "^1.8 || ^2.0" - }, - "type": "composer-plugin", - "extra": { - "class": "Kirby\\ComposerInstaller\\Plugin" - }, - "autoload": { - "psr-4": { - "Kirby\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Kirby's custom Composer installer for the Kirby CMS and for Kirby plugins", - "homepage": "https://getkirby.com", - "support": { - "issues": "https://github.com/getkirby/composer-installer/issues", - "source": "https://github.com/getkirby/composer-installer/tree/1.2.1" - }, - "funding": [ - { - "url": "https://getkirby.com/buy", - "type": "custom" - } - ], - "time": "2020-12-28T12:54:39+00:00" - }, - { - "name": "getkirby/kql", - "version": "1.2.0", - "source": { - "type": "git", - "url": "https://github.com/getkirby/kql.git", - "reference": "4c8cdd88c076cdef5323efcd4f0fda38c0865eed" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/getkirby/kql/zipball/4c8cdd88c076cdef5323efcd4f0fda38c0865eed", - "reference": "4c8cdd88c076cdef5323efcd4f0fda38c0865eed", - "shasum": "" - }, - "require": { - "getkirby/composer-installer": "^1.2.1" - }, - "type": "kirby-plugin", - "autoload": { - "psr-4": { - "Kirby\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bastian Allgeier", - "email": "bastian@getkirby.com" - } - ], - "description": "Kirby Query Language", - "homepage": "https://getkirby.com", - "keywords": [ - "api", - "cms", - "headless", - "json", - "kirby", - "query" - ], - "support": { - "issues": "https://github.com/getkirby/kql/issues", - "source": "https://github.com/getkirby/kql/tree/1.2.0" - }, - "funding": [ - { - "url": "https://getkirby.com/buy", - "type": "custom" - } - ], - "time": "2022-10-06T10:44:26+00:00" - }, - { - "name": "laminas/laminas-escaper", - "version": "2.17.0", - "source": { - "type": "git", - "url": "https://github.com/laminas/laminas-escaper.git", - "reference": "df1ef9503299a8e3920079a16263b578eaf7c3ba" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-escaper/zipball/df1ef9503299a8e3920079a16263b578eaf7c3ba", - "reference": "df1ef9503299a8e3920079a16263b578eaf7c3ba", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "ext-mbstring": "*", - "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0" - }, - "conflict": { - "zendframework/zend-escaper": "*" - }, - "require-dev": { - "infection/infection": "^0.29.8", - "laminas/laminas-coding-standard": "~3.0.1", - "phpunit/phpunit": "^10.5.45", - "psalm/plugin-phpunit": "^0.19.2", - "vimeo/psalm": "^6.6.2" - }, - "type": "library", - "autoload": { - "psr-4": { - "Laminas\\Escaper\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "description": "Securely and safely escape HTML, HTML attributes, JavaScript, CSS, and URLs", - "homepage": "https://laminas.dev", - "keywords": [ - "escaper", - "laminas" - ], - "support": { - "chat": "https://laminas.dev/chat", - "docs": "https://docs.laminas.dev/laminas-escaper/", - "forum": "https://discourse.laminas.dev", - "issues": "https://github.com/laminas/laminas-escaper/issues", - "rss": "https://github.com/laminas/laminas-escaper/releases.atom", - "source": "https://github.com/laminas/laminas-escaper" - }, - "funding": [ - { - "url": "https://funding.communitybridge.org/projects/laminas-project", - "type": "community_bridge" - } - ], - "time": "2025-05-06T19:29:36+00:00" - }, - { - "name": "league/color-extractor", - "version": "0.4.0", - "source": { - "type": "git", - "url": "https://github.com/thephpleague/color-extractor.git", - "reference": "21fcac6249c5ef7d00eb83e128743ee6678fe505" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/thephpleague/color-extractor/zipball/21fcac6249c5ef7d00eb83e128743ee6678fe505", - "reference": "21fcac6249c5ef7d00eb83e128743ee6678fe505", - "shasum": "" - }, - "require": { - "ext-gd": "*", - "php": "^7.3 || ^8.0" - }, - "replace": { - "matthecat/colorextractor": "*" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "~2", - "phpunit/phpunit": "^9.5" - }, - "suggest": { - "ext-curl": "To download images from remote URLs if allow_url_fopen is disabled for security reasons" - }, - "type": "library", - "autoload": { - "psr-4": { - "League\\ColorExtractor\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mathieu Lechat", - "email": "math.lechat@gmail.com", - "homepage": "http://matthecat.com", - "role": "Developer" - } - ], - "description": "Extract colors from an image as a human would do.", - "homepage": "https://github.com/thephpleague/color-extractor", - "keywords": [ - "color", - "extract", - "human", - "image", - "palette" - ], - "support": { - "issues": "https://github.com/thephpleague/color-extractor/issues", - "source": "https://github.com/thephpleague/color-extractor/tree/0.4.0" - }, - "time": "2022-09-24T15:57:16+00:00" - }, - { - "name": "michelf/php-smartypants", - "version": "1.8.1", - "source": { - "type": "git", - "url": "https://github.com/michelf/php-smartypants.git", - "reference": "47d17c90a4dfd0ccf1f87e25c65e6c8012415aad" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/michelf/php-smartypants/zipball/47d17c90a4dfd0ccf1f87e25c65e6c8012415aad", - "reference": "47d17c90a4dfd0ccf1f87e25c65e6c8012415aad", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "autoload": { - "psr-0": { - "Michelf": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Michel Fortin", - "email": "michel.fortin@michelf.ca", - "homepage": "https://michelf.ca/", - "role": "Developer" - }, - { - "name": "John Gruber", - "homepage": "https://daringfireball.net/" - } - ], - "description": "PHP SmartyPants", - "homepage": "https://michelf.ca/projects/php-smartypants/", - "keywords": [ - "dashes", - "quotes", - "spaces", - "typographer", - "typography" - ], - "support": { - "issues": "https://github.com/michelf/php-smartypants/issues", - "source": "https://github.com/michelf/php-smartypants/tree/1.8.1" - }, - "time": "2016-12-13T01:01:17+00:00" - }, - { - "name": "phpmailer/phpmailer", - "version": "v6.10.0", - "source": { - "type": "git", - "url": "https://github.com/PHPMailer/PHPMailer.git", - "reference": "bf74d75a1fde6beaa34a0ddae2ec5fce0f72a144" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/bf74d75a1fde6beaa34a0ddae2ec5fce0f72a144", - "reference": "bf74d75a1fde6beaa34a0ddae2ec5fce0f72a144", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "ext-filter": "*", - "ext-hash": "*", - "php": ">=5.5.0" - }, - "require-dev": { - "dealerdirect/phpcodesniffer-composer-installer": "^1.0", - "doctrine/annotations": "^1.2.6 || ^1.13.3", - "php-parallel-lint/php-console-highlighter": "^1.0.0", - "php-parallel-lint/php-parallel-lint": "^1.3.2", - "phpcompatibility/php-compatibility": "^9.3.5", - "roave/security-advisories": "dev-latest", - "squizlabs/php_codesniffer": "^3.7.2", - "yoast/phpunit-polyfills": "^1.0.4" - }, - "suggest": { - "decomplexity/SendOauth2": "Adapter for using XOAUTH2 authentication", - "ext-mbstring": "Needed to send email in multibyte encoding charset or decode encoded addresses", - "ext-openssl": "Needed for secure SMTP sending and DKIM signing", - "greew/oauth2-azure-provider": "Needed for Microsoft Azure XOAUTH2 authentication", - "hayageek/oauth2-yahoo": "Needed for Yahoo XOAUTH2 authentication", - "league/oauth2-google": "Needed for Google XOAUTH2 authentication", - "psr/log": "For optional PSR-3 debug logging", - "symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)", - "thenetworg/oauth2-azure": "Needed for Microsoft XOAUTH2 authentication" - }, - "type": "library", - "autoload": { - "psr-4": { - "PHPMailer\\PHPMailer\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "LGPL-2.1-only" - ], - "authors": [ - { - "name": "Marcus Bointon", - "email": "phpmailer@synchromedia.co.uk" - }, - { - "name": "Jim Jagielski", - "email": "jimjag@gmail.com" - }, - { - "name": "Andy Prevost", - "email": "codeworxtech@users.sourceforge.net" - }, - { - "name": "Brent R. Matzelle" - } - ], - "description": "PHPMailer is a full-featured email creation and transfer class for PHP", - "support": { - "issues": "https://github.com/PHPMailer/PHPMailer/issues", - "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.10.0" - }, - "funding": [ - { - "url": "https://github.com/Synchro", - "type": "github" - } - ], - "time": "2025-04-24T15:19:31+00:00" - }, - { - "name": "psr/log", - "version": "3.0.2", - "source": { - "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", - "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", - "shasum": "" - }, - "require": { - "php": ">=8.0.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Log\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", - "keywords": [ - "log", - "psr", - "psr-3" - ], - "support": { - "source": "https://github.com/php-fig/log/tree/3.0.2" - }, - "time": "2024-09-11T13:17:53+00:00" - }, - { - "name": "symfony/deprecation-contracts", - "version": "v3.6.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", - "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/contracts", - "name": "symfony/contracts" - }, - "branch-alias": { - "dev-main": "3.6-dev" - } - }, - "autoload": { - "files": [ - "function.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "A generic function and convention to trigger deprecation notices", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-09-25T14:21:43+00:00" - }, - { - "name": "symfony/polyfill-ctype", - "version": "v1.33.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "provide": { - "ext-ctype": "*" - }, - "suggest": { - "ext-ctype": "For best performance" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for ctype functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "ctype", - "polyfill", - "portable" - ], - "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-09-09T11:45:10+00:00" - }, - { - "name": "symfony/polyfill-intl-idn", - "version": "v1.33.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/9614ac4d8061dc257ecc64cba1b140873dce8ad3", - "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3", - "shasum": "" - }, - "require": { - "php": ">=7.2", - "symfony/polyfill-intl-normalizer": "^1.10" - }, - "suggest": { - "ext-intl": "For best performance" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Intl\\Idn\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Laurent Bassin", - "email": "laurent@bassin.info" - }, - { - "name": "Trevor Rowbotham", - "email": "trevor.rowbotham@pm.me" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "idn", - "intl", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.33.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-09-10T14:38:51+00:00" - }, - { - "name": "symfony/polyfill-intl-normalizer", - "version": "v1.33.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "3833d7255cc303546435cb650316bff708a1c75c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", - "reference": "3833d7255cc303546435cb650316bff708a1c75c", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "suggest": { - "ext-intl": "For best performance" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Intl\\Normalizer\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for intl's Normalizer class and related functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "intl", - "normalizer", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-09-09T11:45:10+00:00" - }, - { - "name": "symfony/polyfill-mbstring", - "version": "v1.33.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", - "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", - "shasum": "" - }, - "require": { - "ext-iconv": "*", - "php": ">=7.2" - }, - "provide": { - "ext-mbstring": "*" - }, - "suggest": { - "ext-mbstring": "For best performance" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for the Mbstring extension", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-12-23T08:48:59+00:00" - }, - { - "name": "symfony/yaml", - "version": "v7.3.3", - "source": { - "type": "git", - "url": "https://github.com/symfony/yaml.git", - "reference": "d4f4a66866fe2451f61296924767280ab5732d9d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/d4f4a66866fe2451f61296924767280ab5732d9d", - "reference": "d4f4a66866fe2451f61296924767280ab5732d9d", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3.0", - "symfony/polyfill-ctype": "^1.8" - }, - "conflict": { - "symfony/console": "<6.4" - }, - "require-dev": { - "symfony/console": "^6.4|^7.0" - }, - "bin": [ - "Resources/bin/yaml-lint" - ], - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Yaml\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Loads and dumps YAML files", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/yaml/tree/v7.3.3" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-08-27T11:34:33+00:00" + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "fa260be9c3ea255684234b81e836c746", + "packages": [ + { + "name": "christian-riesen/base32", + "version": "1.6.0", + "source": { + "type": "git", + "url": "https://github.com/ChristianRiesen/base32.git", + "reference": "2e82dab3baa008e24a505649b0d583c31d31e894" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ChristianRiesen/base32/zipball/2e82dab3baa008e24a505649b0d583c31d31e894", + "reference": "2e82dab3baa008e24a505649b0d583c31d31e894", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.17", + "phpstan/phpstan": "^0.12", + "phpunit/phpunit": "^8.5.13 || ^9.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" } - ], - "packages-dev": [], - "aliases": [], - "minimum-stability": "stable", - "stability-flags": [], - "prefer-stable": false, - "prefer-lowest": false, - "platform": { - "php": "~8.1.0 || ~8.2.0 || ~8.3.0" + }, + "autoload": { + "psr-4": { + "Base32\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["MIT"], + "authors": [ + { + "name": "Christian Riesen", + "email": "chris.riesen@gmail.com", + "homepage": "http://christianriesen.com", + "role": "Developer" + } + ], + "description": "Base32 encoder/decoder according to RFC 4648", + "homepage": "https://github.com/ChristianRiesen/base32", + "keywords": ["base32", "decode", "encode", "rfc4648"], + "support": { + "issues": "https://github.com/ChristianRiesen/base32/issues", + "source": "https://github.com/ChristianRiesen/base32/tree/1.6.0" + }, + "time": "2021-02-26T10:19:33+00:00" }, - "platform-dev": [], - "platform-overrides": { - "php": "8.3.0" + { + "name": "claviska/simpleimage", + "version": "4.2.1", + "source": { + "type": "git", + "url": "https://github.com/claviska/SimpleImage.git", + "reference": "ec6d5021e5a7153a2520d64c59b86b6f3c4157c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/claviska/SimpleImage/zipball/ec6d5021e5a7153a2520d64c59b86b6f3c4157c5", + "reference": "ec6d5021e5a7153a2520d64c59b86b6f3c4157c5", + "shasum": "" + }, + "require": { + "ext-gd": "*", + "league/color-extractor": "0.4.*", + "php": ">=8.0" + }, + "require-dev": { + "laravel/pint": "^1.5", + "phpstan/phpstan": "^1.10" + }, + "type": "library", + "autoload": { + "psr-0": { + "claviska": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["MIT"], + "authors": [ + { + "name": "Cory LaViska", + "homepage": "http://www.abeautifulsite.net/", + "role": "Developer" + } + ], + "description": "A PHP class that makes working with images as simple as possible.", + "support": { + "issues": "https://github.com/claviska/SimpleImage/issues", + "source": "https://github.com/claviska/SimpleImage/tree/4.2.1" + }, + "funding": [ + { + "url": "https://github.com/claviska", + "type": "github" + } + ], + "time": "2024-11-22T13:25:03+00:00" }, - "plugin-api-version": "2.6.0" + { + "name": "composer/semver", + "version": "3.4.4", + "source": { + "type": "git", + "url": "https://github.com/composer/semver.git", + "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/semver/zipball/198166618906cb2de69b95d7d47e5fa8aa1b2b95", + "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.11", + "symfony/phpunit-bridge": "^3 || ^7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Semver\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["MIT"], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "Semver library that offers utilities, version constraint parsing and validation.", + "keywords": ["semantic", "semver", "validation", "versioning"], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.4.4" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + } + ], + "time": "2025-08-20T19:15:30+00:00" + }, + { + "name": "filp/whoops", + "version": "2.18.4", + "source": { + "type": "git", + "url": "https://github.com/filp/whoops.git", + "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/filp/whoops/zipball/d2102955e48b9fd9ab24280a7ad12ed552752c4d", + "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0", + "psr/log": "^1.0.1 || ^2.0 || ^3.0" + }, + "require-dev": { + "mockery/mockery": "^1.0", + "phpunit/phpunit": "^7.5.20 || ^8.5.8 || ^9.3.3", + "symfony/var-dumper": "^4.0 || ^5.0" + }, + "suggest": { + "symfony/var-dumper": "Pretty print complex values better with var-dumper available", + "whoops/soap": "Formats errors as SOAP responses" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Whoops\\": "src/Whoops/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["MIT"], + "authors": [ + { + "name": "Filipe Dobreira", + "homepage": "https://github.com/filp", + "role": "Developer" + } + ], + "description": "php error handling for cool kids", + "homepage": "https://filp.github.io/whoops/", + "keywords": [ + "error", + "exception", + "handling", + "library", + "throwable", + "whoops" + ], + "support": { + "issues": "https://github.com/filp/whoops/issues", + "source": "https://github.com/filp/whoops/tree/2.18.4" + }, + "funding": [ + { + "url": "https://github.com/denis-sokolov", + "type": "github" + } + ], + "time": "2025-08-08T12:00:00+00:00" + }, + { + "name": "getkirby/cms", + "version": "5.1.1", + "source": { + "type": "git", + "url": "https://github.com/getkirby/kirby.git", + "reference": "fb11f5e3ec422e948fb1a52f16988335bb3489b4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/getkirby/kirby/zipball/fb11f5e3ec422e948fb1a52f16988335bb3489b4", + "reference": "fb11f5e3ec422e948fb1a52f16988335bb3489b4", + "shasum": "" + }, + "require": { + "christian-riesen/base32": "1.6.0", + "claviska/simpleimage": "4.2.1", + "composer/semver": "3.4.4", + "ext-ctype": "*", + "ext-curl": "*", + "ext-dom": "*", + "ext-filter": "*", + "ext-hash": "*", + "ext-iconv": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-openssl": "*", + "ext-simplexml": "*", + "filp/whoops": "2.18.4", + "getkirby/composer-installer": "^1.2.1", + "laminas/laminas-escaper": "2.17.0", + "michelf/php-smartypants": "1.8.1", + "php": "~8.2.0 || ~8.3.0 || ~8.4.0", + "phpmailer/phpmailer": "6.10.0", + "symfony/polyfill-intl-idn": "1.33.0", + "symfony/polyfill-mbstring": "1.33.0", + "symfony/yaml": "7.3.3" + }, + "replace": { + "symfony/polyfill-php72": "*" + }, + "suggest": { + "ext-PDO": "Support for using databases", + "ext-apcu": "Support for the Apcu cache driver", + "ext-exif": "Support for exif information from images", + "ext-fileinfo": "Improved mime type detection for files", + "ext-imagick": "Improved thumbnail generation", + "ext-intl": "Improved i18n number formatting", + "ext-memcached": "Support for the Memcached cache driver", + "ext-redis": "Support for the Redis cache driver", + "ext-sodium": "Support for the crypto class and more robust session handling", + "ext-zip": "Support for ZIP archive file functions", + "ext-zlib": "Sanitization and validation for svgz files" + }, + "type": "kirby-cms", + "extra": { + "unused": ["symfony/polyfill-intl-idn"] + }, + "autoload": { + "files": ["config/setup.php", "config/helpers.php"], + "psr-4": { + "Kirby\\": "src/" + }, + "classmap": ["dependencies/"] + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["proprietary"], + "authors": [ + { + "name": "Kirby Team", + "email": "support@getkirby.com", + "homepage": "https://getkirby.com" + } + ], + "description": "The Kirby core", + "homepage": "https://getkirby.com", + "keywords": ["cms", "core", "kirby"], + "support": { + "email": "support@getkirby.com", + "forum": "https://forum.getkirby.com", + "issues": "https://github.com/getkirby/kirby/issues", + "source": "https://github.com/getkirby/kirby" + }, + "funding": [ + { + "url": "https://getkirby.com/buy", + "type": "custom" + } + ], + "time": "2025-09-16T13:06:53+00:00" + }, + { + "name": "getkirby/composer-installer", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/getkirby/composer-installer.git", + "reference": "c98ece30bfba45be7ce457e1102d1b169d922f3d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/getkirby/composer-installer/zipball/c98ece30bfba45be7ce457e1102d1b169d922f3d", + "reference": "c98ece30bfba45be7ce457e1102d1b169d922f3d", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0 || ^2.0" + }, + "require-dev": { + "composer/composer": "^1.8 || ^2.0" + }, + "type": "composer-plugin", + "extra": { + "class": "Kirby\\ComposerInstaller\\Plugin" + }, + "autoload": { + "psr-4": { + "Kirby\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["MIT"], + "description": "Kirby's custom Composer installer for the Kirby CMS and for Kirby plugins", + "homepage": "https://getkirby.com", + "support": { + "issues": "https://github.com/getkirby/composer-installer/issues", + "source": "https://github.com/getkirby/composer-installer/tree/1.2.1" + }, + "funding": [ + { + "url": "https://getkirby.com/buy", + "type": "custom" + } + ], + "time": "2020-12-28T12:54:39+00:00" + }, + { + "name": "getkirby/kql", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/getkirby/kql.git", + "reference": "4c8cdd88c076cdef5323efcd4f0fda38c0865eed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/getkirby/kql/zipball/4c8cdd88c076cdef5323efcd4f0fda38c0865eed", + "reference": "4c8cdd88c076cdef5323efcd4f0fda38c0865eed", + "shasum": "" + }, + "require": { + "getkirby/composer-installer": "^1.2.1" + }, + "type": "kirby-plugin", + "autoload": { + "psr-4": { + "Kirby\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["MIT"], + "authors": [ + { + "name": "Bastian Allgeier", + "email": "bastian@getkirby.com" + } + ], + "description": "Kirby Query Language", + "homepage": "https://getkirby.com", + "keywords": ["api", "cms", "headless", "json", "kirby", "query"], + "support": { + "issues": "https://github.com/getkirby/kql/issues", + "source": "https://github.com/getkirby/kql/tree/1.2.0" + }, + "funding": [ + { + "url": "https://getkirby.com/buy", + "type": "custom" + } + ], + "time": "2022-10-06T10:44:26+00:00" + }, + { + "name": "laminas/laminas-escaper", + "version": "2.17.0", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-escaper.git", + "reference": "df1ef9503299a8e3920079a16263b578eaf7c3ba" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-escaper/zipball/df1ef9503299a8e3920079a16263b578eaf7c3ba", + "reference": "df1ef9503299a8e3920079a16263b578eaf7c3ba", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-mbstring": "*", + "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0" + }, + "conflict": { + "zendframework/zend-escaper": "*" + }, + "require-dev": { + "infection/infection": "^0.29.8", + "laminas/laminas-coding-standard": "~3.0.1", + "phpunit/phpunit": "^10.5.45", + "psalm/plugin-phpunit": "^0.19.2", + "vimeo/psalm": "^6.6.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Laminas\\Escaper\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["BSD-3-Clause"], + "description": "Securely and safely escape HTML, HTML attributes, JavaScript, CSS, and URLs", + "homepage": "https://laminas.dev", + "keywords": ["escaper", "laminas"], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-escaper/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-escaper/issues", + "rss": "https://github.com/laminas/laminas-escaper/releases.atom", + "source": "https://github.com/laminas/laminas-escaper" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2025-05-06T19:29:36+00:00" + }, + { + "name": "league/color-extractor", + "version": "0.4.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/color-extractor.git", + "reference": "21fcac6249c5ef7d00eb83e128743ee6678fe505" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/color-extractor/zipball/21fcac6249c5ef7d00eb83e128743ee6678fe505", + "reference": "21fcac6249c5ef7d00eb83e128743ee6678fe505", + "shasum": "" + }, + "require": { + "ext-gd": "*", + "php": "^7.3 || ^8.0" + }, + "replace": { + "matthecat/colorextractor": "*" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "~2", + "phpunit/phpunit": "^9.5" + }, + "suggest": { + "ext-curl": "To download images from remote URLs if allow_url_fopen is disabled for security reasons" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\ColorExtractor\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["MIT"], + "authors": [ + { + "name": "Mathieu Lechat", + "email": "math.lechat@gmail.com", + "homepage": "http://matthecat.com", + "role": "Developer" + } + ], + "description": "Extract colors from an image as a human would do.", + "homepage": "https://github.com/thephpleague/color-extractor", + "keywords": ["color", "extract", "human", "image", "palette"], + "support": { + "issues": "https://github.com/thephpleague/color-extractor/issues", + "source": "https://github.com/thephpleague/color-extractor/tree/0.4.0" + }, + "time": "2022-09-24T15:57:16+00:00" + }, + { + "name": "michelf/php-smartypants", + "version": "1.8.1", + "source": { + "type": "git", + "url": "https://github.com/michelf/php-smartypants.git", + "reference": "47d17c90a4dfd0ccf1f87e25c65e6c8012415aad" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/michelf/php-smartypants/zipball/47d17c90a4dfd0ccf1f87e25c65e6c8012415aad", + "reference": "47d17c90a4dfd0ccf1f87e25c65e6c8012415aad", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "autoload": { + "psr-0": { + "Michelf": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["BSD-3-Clause"], + "authors": [ + { + "name": "Michel Fortin", + "email": "michel.fortin@michelf.ca", + "homepage": "https://michelf.ca/", + "role": "Developer" + }, + { + "name": "John Gruber", + "homepage": "https://daringfireball.net/" + } + ], + "description": "PHP SmartyPants", + "homepage": "https://michelf.ca/projects/php-smartypants/", + "keywords": ["dashes", "quotes", "spaces", "typographer", "typography"], + "support": { + "issues": "https://github.com/michelf/php-smartypants/issues", + "source": "https://github.com/michelf/php-smartypants/tree/1.8.1" + }, + "time": "2016-12-13T01:01:17+00:00" + }, + { + "name": "phpmailer/phpmailer", + "version": "v6.10.0", + "source": { + "type": "git", + "url": "https://github.com/PHPMailer/PHPMailer.git", + "reference": "bf74d75a1fde6beaa34a0ddae2ec5fce0f72a144" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/bf74d75a1fde6beaa34a0ddae2ec5fce0f72a144", + "reference": "bf74d75a1fde6beaa34a0ddae2ec5fce0f72a144", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-filter": "*", + "ext-hash": "*", + "php": ">=5.5.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "^1.0", + "doctrine/annotations": "^1.2.6 || ^1.13.3", + "php-parallel-lint/php-console-highlighter": "^1.0.0", + "php-parallel-lint/php-parallel-lint": "^1.3.2", + "phpcompatibility/php-compatibility": "^9.3.5", + "roave/security-advisories": "dev-latest", + "squizlabs/php_codesniffer": "^3.7.2", + "yoast/phpunit-polyfills": "^1.0.4" + }, + "suggest": { + "decomplexity/SendOauth2": "Adapter for using XOAUTH2 authentication", + "ext-mbstring": "Needed to send email in multibyte encoding charset or decode encoded addresses", + "ext-openssl": "Needed for secure SMTP sending and DKIM signing", + "greew/oauth2-azure-provider": "Needed for Microsoft Azure XOAUTH2 authentication", + "hayageek/oauth2-yahoo": "Needed for Yahoo XOAUTH2 authentication", + "league/oauth2-google": "Needed for Google XOAUTH2 authentication", + "psr/log": "For optional PSR-3 debug logging", + "symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)", + "thenetworg/oauth2-azure": "Needed for Microsoft XOAUTH2 authentication" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPMailer\\PHPMailer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["LGPL-2.1-only"], + "authors": [ + { + "name": "Marcus Bointon", + "email": "phpmailer@synchromedia.co.uk" + }, + { + "name": "Jim Jagielski", + "email": "jimjag@gmail.com" + }, + { + "name": "Andy Prevost", + "email": "codeworxtech@users.sourceforge.net" + }, + { + "name": "Brent R. Matzelle" + } + ], + "description": "PHPMailer is a full-featured email creation and transfer class for PHP", + "support": { + "issues": "https://github.com/PHPMailer/PHPMailer/issues", + "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.10.0" + }, + "funding": [ + { + "url": "https://github.com/Synchro", + "type": "github" + } + ], + "time": "2025-04-24T15:19:31+00:00" + }, + { + "name": "psr/log", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["MIT"], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": ["log", "psr", "psr-3"], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.2" + }, + "time": "2024-09-11T13:17:53+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" + } + }, + "autoload": { + "files": ["function.php"] + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["MIT"], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:21:43+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": ["bootstrap.php"], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["MIT"], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": ["compatibility", "ctype", "polyfill", "portable"], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-intl-idn", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-idn.git", + "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/9614ac4d8061dc257ecc64cba1b140873dce8ad3", + "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3", + "shasum": "" + }, + "require": { + "php": ">=7.2", + "symfony/polyfill-intl-normalizer": "^1.10" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": ["bootstrap.php"], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Idn\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["MIT"], + "authors": [ + { + "name": "Laurent Bassin", + "email": "laurent@bassin.info" + }, + { + "name": "Trevor Rowbotham", + "email": "trevor.rowbotham@pm.me" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "idn", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-10T14:38:51+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "3833d7255cc303546435cb650316bff708a1c75c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": ["bootstrap.php"], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": ["Resources/stubs"] + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["MIT"], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", + "shasum": "" + }, + "require": { + "ext-iconv": "*", + "php": ">=7.2" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": ["bootstrap.php"], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["MIT"], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": ["compatibility", "mbstring", "polyfill", "portable", "shim"], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-12-23T08:48:59+00:00" + }, + { + "name": "symfony/yaml", + "version": "v7.3.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "d4f4a66866fe2451f61296924767280ab5732d9d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/d4f4a66866fe2451f61296924767280ab5732d9d", + "reference": "d4f4a66866fe2451f61296924767280ab5732d9d", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3.0", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/console": "<6.4" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0" + }, + "bin": ["Resources/bin/yaml-lint"], + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": ["/Tests/"] + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["MIT"], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Loads and dumps YAML files", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/yaml/tree/v7.3.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-08-27T11:34:33+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": "~8.1.0 || ~8.2.0 || ~8.3.0" + }, + "platform-dev": [], + "platform-overrides": { + "php": "8.3.0" + }, + "plugin-api-version": "2.6.0" } diff --git a/public/kirby/composer.lock b/public/kirby/composer.lock index 29f7601..f6fdd0d 100644 --- a/public/kirby/composer.lock +++ b/public/kirby/composer.lock @@ -1,1132 +1,1044 @@ { - "_readme": [ - "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", - "This file is @generated automatically" - ], - "content-hash": "8d08002d38005e6ec1ff6a97deac20e2", - "packages": [ - { - "name": "christian-riesen/base32", - "version": "1.6.0", - "source": { - "type": "git", - "url": "https://github.com/ChristianRiesen/base32.git", - "reference": "2e82dab3baa008e24a505649b0d583c31d31e894" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ChristianRiesen/base32/zipball/2e82dab3baa008e24a505649b0d583c31d31e894", - "reference": "2e82dab3baa008e24a505649b0d583c31d31e894", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^2.17", - "phpstan/phpstan": "^0.12", - "phpunit/phpunit": "^8.5.13 || ^9.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Base32\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Christian Riesen", - "email": "chris.riesen@gmail.com", - "homepage": "http://christianriesen.com", - "role": "Developer" - } - ], - "description": "Base32 encoder/decoder according to RFC 4648", - "homepage": "https://github.com/ChristianRiesen/base32", - "keywords": [ - "base32", - "decode", - "encode", - "rfc4648" - ], - "support": { - "issues": "https://github.com/ChristianRiesen/base32/issues", - "source": "https://github.com/ChristianRiesen/base32/tree/1.6.0" - }, - "time": "2021-02-26T10:19:33+00:00" - }, - { - "name": "claviska/simpleimage", - "version": "4.2.1", - "source": { - "type": "git", - "url": "https://github.com/claviska/SimpleImage.git", - "reference": "ec6d5021e5a7153a2520d64c59b86b6f3c4157c5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/claviska/SimpleImage/zipball/ec6d5021e5a7153a2520d64c59b86b6f3c4157c5", - "reference": "ec6d5021e5a7153a2520d64c59b86b6f3c4157c5", - "shasum": "" - }, - "require": { - "ext-gd": "*", - "league/color-extractor": "0.4.*", - "php": ">=8.0" - }, - "require-dev": { - "laravel/pint": "^1.5", - "phpstan/phpstan": "^1.10" - }, - "type": "library", - "autoload": { - "psr-0": { - "claviska": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Cory LaViska", - "homepage": "http://www.abeautifulsite.net/", - "role": "Developer" - } - ], - "description": "A PHP class that makes working with images as simple as possible.", - "support": { - "issues": "https://github.com/claviska/SimpleImage/issues", - "source": "https://github.com/claviska/SimpleImage/tree/4.2.1" - }, - "funding": [ - { - "url": "https://github.com/claviska", - "type": "github" - } - ], - "time": "2024-11-22T13:25:03+00:00" - }, - { - "name": "composer/semver", - "version": "3.4.4", - "source": { - "type": "git", - "url": "https://github.com/composer/semver.git", - "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/198166618906cb2de69b95d7d47e5fa8aa1b2b95", - "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95", - "shasum": "" - }, - "require": { - "php": "^5.3.2 || ^7.0 || ^8.0" - }, - "require-dev": { - "phpstan/phpstan": "^1.11", - "symfony/phpunit-bridge": "^3 || ^7" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.x-dev" - } - }, - "autoload": { - "psr-4": { - "Composer\\Semver\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nils Adermann", - "email": "naderman@naderman.de", - "homepage": "http://www.naderman.de" - }, - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - }, - { - "name": "Rob Bast", - "email": "rob.bast@gmail.com", - "homepage": "http://robbast.nl" - } - ], - "description": "Semver library that offers utilities, version constraint parsing and validation.", - "keywords": [ - "semantic", - "semver", - "validation", - "versioning" - ], - "support": { - "irc": "ircs://irc.libera.chat:6697/composer", - "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/3.4.4" - }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - } - ], - "time": "2025-08-20T19:15:30+00:00" - }, - { - "name": "filp/whoops", - "version": "2.18.4", - "source": { - "type": "git", - "url": "https://github.com/filp/whoops.git", - "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/filp/whoops/zipball/d2102955e48b9fd9ab24280a7ad12ed552752c4d", - "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0", - "psr/log": "^1.0.1 || ^2.0 || ^3.0" - }, - "require-dev": { - "mockery/mockery": "^1.0", - "phpunit/phpunit": "^7.5.20 || ^8.5.8 || ^9.3.3", - "symfony/var-dumper": "^4.0 || ^5.0" - }, - "suggest": { - "symfony/var-dumper": "Pretty print complex values better with var-dumper available", - "whoops/soap": "Formats errors as SOAP responses" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.7-dev" - } - }, - "autoload": { - "psr-4": { - "Whoops\\": "src/Whoops/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Filipe Dobreira", - "homepage": "https://github.com/filp", - "role": "Developer" - } - ], - "description": "php error handling for cool kids", - "homepage": "https://filp.github.io/whoops/", - "keywords": [ - "error", - "exception", - "handling", - "library", - "throwable", - "whoops" - ], - "support": { - "issues": "https://github.com/filp/whoops/issues", - "source": "https://github.com/filp/whoops/tree/2.18.4" - }, - "funding": [ - { - "url": "https://github.com/denis-sokolov", - "type": "github" - } - ], - "time": "2025-08-08T12:00:00+00:00" - }, - { - "name": "getkirby/composer-installer", - "version": "1.2.1", - "source": { - "type": "git", - "url": "https://github.com/getkirby/composer-installer.git", - "reference": "c98ece30bfba45be7ce457e1102d1b169d922f3d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/getkirby/composer-installer/zipball/c98ece30bfba45be7ce457e1102d1b169d922f3d", - "reference": "c98ece30bfba45be7ce457e1102d1b169d922f3d", - "shasum": "" - }, - "require": { - "composer-plugin-api": "^1.0 || ^2.0" - }, - "require-dev": { - "composer/composer": "^1.8 || ^2.0" - }, - "type": "composer-plugin", - "extra": { - "class": "Kirby\\ComposerInstaller\\Plugin" - }, - "autoload": { - "psr-4": { - "Kirby\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Kirby's custom Composer installer for the Kirby CMS and for Kirby plugins", - "homepage": "https://getkirby.com", - "support": { - "issues": "https://github.com/getkirby/composer-installer/issues", - "source": "https://github.com/getkirby/composer-installer/tree/1.2.1" - }, - "funding": [ - { - "url": "https://getkirby.com/buy", - "type": "custom" - } - ], - "time": "2020-12-28T12:54:39+00:00" - }, - { - "name": "laminas/laminas-escaper", - "version": "2.17.0", - "source": { - "type": "git", - "url": "https://github.com/laminas/laminas-escaper.git", - "reference": "df1ef9503299a8e3920079a16263b578eaf7c3ba" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-escaper/zipball/df1ef9503299a8e3920079a16263b578eaf7c3ba", - "reference": "df1ef9503299a8e3920079a16263b578eaf7c3ba", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "ext-mbstring": "*", - "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0" - }, - "conflict": { - "zendframework/zend-escaper": "*" - }, - "require-dev": { - "infection/infection": "^0.29.8", - "laminas/laminas-coding-standard": "~3.0.1", - "phpunit/phpunit": "^10.5.45", - "psalm/plugin-phpunit": "^0.19.2", - "vimeo/psalm": "^6.6.2" - }, - "type": "library", - "autoload": { - "psr-4": { - "Laminas\\Escaper\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "description": "Securely and safely escape HTML, HTML attributes, JavaScript, CSS, and URLs", - "homepage": "https://laminas.dev", - "keywords": [ - "escaper", - "laminas" - ], - "support": { - "chat": "https://laminas.dev/chat", - "docs": "https://docs.laminas.dev/laminas-escaper/", - "forum": "https://discourse.laminas.dev", - "issues": "https://github.com/laminas/laminas-escaper/issues", - "rss": "https://github.com/laminas/laminas-escaper/releases.atom", - "source": "https://github.com/laminas/laminas-escaper" - }, - "funding": [ - { - "url": "https://funding.communitybridge.org/projects/laminas-project", - "type": "community_bridge" - } - ], - "time": "2025-05-06T19:29:36+00:00" - }, - { - "name": "league/color-extractor", - "version": "0.4.0", - "source": { - "type": "git", - "url": "https://github.com/thephpleague/color-extractor.git", - "reference": "21fcac6249c5ef7d00eb83e128743ee6678fe505" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/thephpleague/color-extractor/zipball/21fcac6249c5ef7d00eb83e128743ee6678fe505", - "reference": "21fcac6249c5ef7d00eb83e128743ee6678fe505", - "shasum": "" - }, - "require": { - "ext-gd": "*", - "php": "^7.3 || ^8.0" - }, - "replace": { - "matthecat/colorextractor": "*" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "~2", - "phpunit/phpunit": "^9.5" - }, - "suggest": { - "ext-curl": "To download images from remote URLs if allow_url_fopen is disabled for security reasons" - }, - "type": "library", - "autoload": { - "psr-4": { - "League\\ColorExtractor\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mathieu Lechat", - "email": "math.lechat@gmail.com", - "homepage": "http://matthecat.com", - "role": "Developer" - } - ], - "description": "Extract colors from an image as a human would do.", - "homepage": "https://github.com/thephpleague/color-extractor", - "keywords": [ - "color", - "extract", - "human", - "image", - "palette" - ], - "support": { - "issues": "https://github.com/thephpleague/color-extractor/issues", - "source": "https://github.com/thephpleague/color-extractor/tree/0.4.0" - }, - "time": "2022-09-24T15:57:16+00:00" - }, - { - "name": "michelf/php-smartypants", - "version": "1.8.1", - "source": { - "type": "git", - "url": "https://github.com/michelf/php-smartypants.git", - "reference": "47d17c90a4dfd0ccf1f87e25c65e6c8012415aad" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/michelf/php-smartypants/zipball/47d17c90a4dfd0ccf1f87e25c65e6c8012415aad", - "reference": "47d17c90a4dfd0ccf1f87e25c65e6c8012415aad", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "autoload": { - "psr-0": { - "Michelf": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Michel Fortin", - "email": "michel.fortin@michelf.ca", - "homepage": "https://michelf.ca/", - "role": "Developer" - }, - { - "name": "John Gruber", - "homepage": "https://daringfireball.net/" - } - ], - "description": "PHP SmartyPants", - "homepage": "https://michelf.ca/projects/php-smartypants/", - "keywords": [ - "dashes", - "quotes", - "spaces", - "typographer", - "typography" - ], - "support": { - "issues": "https://github.com/michelf/php-smartypants/issues", - "source": "https://github.com/michelf/php-smartypants/tree/1.8.1" - }, - "time": "2016-12-13T01:01:17+00:00" - }, - { - "name": "phpmailer/phpmailer", - "version": "v6.10.0", - "source": { - "type": "git", - "url": "https://github.com/PHPMailer/PHPMailer.git", - "reference": "bf74d75a1fde6beaa34a0ddae2ec5fce0f72a144" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/bf74d75a1fde6beaa34a0ddae2ec5fce0f72a144", - "reference": "bf74d75a1fde6beaa34a0ddae2ec5fce0f72a144", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "ext-filter": "*", - "ext-hash": "*", - "php": ">=5.5.0" - }, - "require-dev": { - "dealerdirect/phpcodesniffer-composer-installer": "^1.0", - "doctrine/annotations": "^1.2.6 || ^1.13.3", - "php-parallel-lint/php-console-highlighter": "^1.0.0", - "php-parallel-lint/php-parallel-lint": "^1.3.2", - "phpcompatibility/php-compatibility": "^9.3.5", - "roave/security-advisories": "dev-latest", - "squizlabs/php_codesniffer": "^3.7.2", - "yoast/phpunit-polyfills": "^1.0.4" - }, - "suggest": { - "decomplexity/SendOauth2": "Adapter for using XOAUTH2 authentication", - "ext-mbstring": "Needed to send email in multibyte encoding charset or decode encoded addresses", - "ext-openssl": "Needed for secure SMTP sending and DKIM signing", - "greew/oauth2-azure-provider": "Needed for Microsoft Azure XOAUTH2 authentication", - "hayageek/oauth2-yahoo": "Needed for Yahoo XOAUTH2 authentication", - "league/oauth2-google": "Needed for Google XOAUTH2 authentication", - "psr/log": "For optional PSR-3 debug logging", - "symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)", - "thenetworg/oauth2-azure": "Needed for Microsoft XOAUTH2 authentication" - }, - "type": "library", - "autoload": { - "psr-4": { - "PHPMailer\\PHPMailer\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "LGPL-2.1-only" - ], - "authors": [ - { - "name": "Marcus Bointon", - "email": "phpmailer@synchromedia.co.uk" - }, - { - "name": "Jim Jagielski", - "email": "jimjag@gmail.com" - }, - { - "name": "Andy Prevost", - "email": "codeworxtech@users.sourceforge.net" - }, - { - "name": "Brent R. Matzelle" - } - ], - "description": "PHPMailer is a full-featured email creation and transfer class for PHP", - "support": { - "issues": "https://github.com/PHPMailer/PHPMailer/issues", - "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.10.0" - }, - "funding": [ - { - "url": "https://github.com/Synchro", - "type": "github" - } - ], - "time": "2025-04-24T15:19:31+00:00" - }, - { - "name": "psr/log", - "version": "3.0.2", - "source": { - "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", - "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", - "shasum": "" - }, - "require": { - "php": ">=8.0.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Log\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", - "keywords": [ - "log", - "psr", - "psr-3" - ], - "support": { - "source": "https://github.com/php-fig/log/tree/3.0.2" - }, - "time": "2024-09-11T13:17:53+00:00" - }, - { - "name": "symfony/deprecation-contracts", - "version": "v3.6.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", - "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/contracts", - "name": "symfony/contracts" - }, - "branch-alias": { - "dev-main": "3.6-dev" - } - }, - "autoload": { - "files": [ - "function.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "A generic function and convention to trigger deprecation notices", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-09-25T14:21:43+00:00" - }, - { - "name": "symfony/polyfill-ctype", - "version": "v1.33.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "provide": { - "ext-ctype": "*" - }, - "suggest": { - "ext-ctype": "For best performance" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for ctype functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "ctype", - "polyfill", - "portable" - ], - "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-09-09T11:45:10+00:00" - }, - { - "name": "symfony/polyfill-intl-idn", - "version": "v1.33.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/9614ac4d8061dc257ecc64cba1b140873dce8ad3", - "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3", - "shasum": "" - }, - "require": { - "php": ">=7.2", - "symfony/polyfill-intl-normalizer": "^1.10" - }, - "suggest": { - "ext-intl": "For best performance" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Intl\\Idn\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Laurent Bassin", - "email": "laurent@bassin.info" - }, - { - "name": "Trevor Rowbotham", - "email": "trevor.rowbotham@pm.me" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "idn", - "intl", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.33.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-09-10T14:38:51+00:00" - }, - { - "name": "symfony/polyfill-intl-normalizer", - "version": "v1.33.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "3833d7255cc303546435cb650316bff708a1c75c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", - "reference": "3833d7255cc303546435cb650316bff708a1c75c", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "suggest": { - "ext-intl": "For best performance" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Intl\\Normalizer\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for intl's Normalizer class and related functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "intl", - "normalizer", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-09-09T11:45:10+00:00" - }, - { - "name": "symfony/polyfill-mbstring", - "version": "v1.33.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", - "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", - "shasum": "" - }, - "require": { - "ext-iconv": "*", - "php": ">=7.2" - }, - "provide": { - "ext-mbstring": "*" - }, - "suggest": { - "ext-mbstring": "For best performance" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for the Mbstring extension", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-12-23T08:48:59+00:00" - }, - { - "name": "symfony/yaml", - "version": "v7.3.3", - "source": { - "type": "git", - "url": "https://github.com/symfony/yaml.git", - "reference": "d4f4a66866fe2451f61296924767280ab5732d9d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/d4f4a66866fe2451f61296924767280ab5732d9d", - "reference": "d4f4a66866fe2451f61296924767280ab5732d9d", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3.0", - "symfony/polyfill-ctype": "^1.8" - }, - "conflict": { - "symfony/console": "<6.4" - }, - "require-dev": { - "symfony/console": "^6.4|^7.0" - }, - "bin": [ - "Resources/bin/yaml-lint" - ], - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Yaml\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Loads and dumps YAML files", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/yaml/tree/v7.3.3" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-08-27T11:34:33+00:00" - } - ], - "packages-dev": [], - "aliases": [], - "minimum-stability": "stable", - "stability-flags": {}, - "prefer-stable": false, - "prefer-lowest": false, - "platform": { - "php": "~8.2.0 || ~8.3.0 || ~8.4.0", - "ext-simplexml": "*", - "ext-ctype": "*", - "ext-curl": "*", - "ext-dom": "*", - "ext-filter": "*", - "ext-hash": "*", - "ext-iconv": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-openssl": "*" - }, - "platform-dev": {}, - "platform-overrides": { - "php": "8.2.0" - }, - "plugin-api-version": "2.6.0" + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "8d08002d38005e6ec1ff6a97deac20e2", + "packages": [ + { + "name": "christian-riesen/base32", + "version": "1.6.0", + "source": { + "type": "git", + "url": "https://github.com/ChristianRiesen/base32.git", + "reference": "2e82dab3baa008e24a505649b0d583c31d31e894" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ChristianRiesen/base32/zipball/2e82dab3baa008e24a505649b0d583c31d31e894", + "reference": "2e82dab3baa008e24a505649b0d583c31d31e894", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.17", + "phpstan/phpstan": "^0.12", + "phpunit/phpunit": "^8.5.13 || ^9.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Base32\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["MIT"], + "authors": [ + { + "name": "Christian Riesen", + "email": "chris.riesen@gmail.com", + "homepage": "http://christianriesen.com", + "role": "Developer" + } + ], + "description": "Base32 encoder/decoder according to RFC 4648", + "homepage": "https://github.com/ChristianRiesen/base32", + "keywords": ["base32", "decode", "encode", "rfc4648"], + "support": { + "issues": "https://github.com/ChristianRiesen/base32/issues", + "source": "https://github.com/ChristianRiesen/base32/tree/1.6.0" + }, + "time": "2021-02-26T10:19:33+00:00" + }, + { + "name": "claviska/simpleimage", + "version": "4.2.1", + "source": { + "type": "git", + "url": "https://github.com/claviska/SimpleImage.git", + "reference": "ec6d5021e5a7153a2520d64c59b86b6f3c4157c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/claviska/SimpleImage/zipball/ec6d5021e5a7153a2520d64c59b86b6f3c4157c5", + "reference": "ec6d5021e5a7153a2520d64c59b86b6f3c4157c5", + "shasum": "" + }, + "require": { + "ext-gd": "*", + "league/color-extractor": "0.4.*", + "php": ">=8.0" + }, + "require-dev": { + "laravel/pint": "^1.5", + "phpstan/phpstan": "^1.10" + }, + "type": "library", + "autoload": { + "psr-0": { + "claviska": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["MIT"], + "authors": [ + { + "name": "Cory LaViska", + "homepage": "http://www.abeautifulsite.net/", + "role": "Developer" + } + ], + "description": "A PHP class that makes working with images as simple as possible.", + "support": { + "issues": "https://github.com/claviska/SimpleImage/issues", + "source": "https://github.com/claviska/SimpleImage/tree/4.2.1" + }, + "funding": [ + { + "url": "https://github.com/claviska", + "type": "github" + } + ], + "time": "2024-11-22T13:25:03+00:00" + }, + { + "name": "composer/semver", + "version": "3.4.4", + "source": { + "type": "git", + "url": "https://github.com/composer/semver.git", + "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/semver/zipball/198166618906cb2de69b95d7d47e5fa8aa1b2b95", + "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.11", + "symfony/phpunit-bridge": "^3 || ^7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Semver\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["MIT"], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "Semver library that offers utilities, version constraint parsing and validation.", + "keywords": ["semantic", "semver", "validation", "versioning"], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.4.4" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + } + ], + "time": "2025-08-20T19:15:30+00:00" + }, + { + "name": "filp/whoops", + "version": "2.18.4", + "source": { + "type": "git", + "url": "https://github.com/filp/whoops.git", + "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/filp/whoops/zipball/d2102955e48b9fd9ab24280a7ad12ed552752c4d", + "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0", + "psr/log": "^1.0.1 || ^2.0 || ^3.0" + }, + "require-dev": { + "mockery/mockery": "^1.0", + "phpunit/phpunit": "^7.5.20 || ^8.5.8 || ^9.3.3", + "symfony/var-dumper": "^4.0 || ^5.0" + }, + "suggest": { + "symfony/var-dumper": "Pretty print complex values better with var-dumper available", + "whoops/soap": "Formats errors as SOAP responses" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Whoops\\": "src/Whoops/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["MIT"], + "authors": [ + { + "name": "Filipe Dobreira", + "homepage": "https://github.com/filp", + "role": "Developer" + } + ], + "description": "php error handling for cool kids", + "homepage": "https://filp.github.io/whoops/", + "keywords": [ + "error", + "exception", + "handling", + "library", + "throwable", + "whoops" + ], + "support": { + "issues": "https://github.com/filp/whoops/issues", + "source": "https://github.com/filp/whoops/tree/2.18.4" + }, + "funding": [ + { + "url": "https://github.com/denis-sokolov", + "type": "github" + } + ], + "time": "2025-08-08T12:00:00+00:00" + }, + { + "name": "getkirby/composer-installer", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/getkirby/composer-installer.git", + "reference": "c98ece30bfba45be7ce457e1102d1b169d922f3d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/getkirby/composer-installer/zipball/c98ece30bfba45be7ce457e1102d1b169d922f3d", + "reference": "c98ece30bfba45be7ce457e1102d1b169d922f3d", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0 || ^2.0" + }, + "require-dev": { + "composer/composer": "^1.8 || ^2.0" + }, + "type": "composer-plugin", + "extra": { + "class": "Kirby\\ComposerInstaller\\Plugin" + }, + "autoload": { + "psr-4": { + "Kirby\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["MIT"], + "description": "Kirby's custom Composer installer for the Kirby CMS and for Kirby plugins", + "homepage": "https://getkirby.com", + "support": { + "issues": "https://github.com/getkirby/composer-installer/issues", + "source": "https://github.com/getkirby/composer-installer/tree/1.2.1" + }, + "funding": [ + { + "url": "https://getkirby.com/buy", + "type": "custom" + } + ], + "time": "2020-12-28T12:54:39+00:00" + }, + { + "name": "laminas/laminas-escaper", + "version": "2.17.0", + "source": { + "type": "git", + "url": "https://github.com/laminas/laminas-escaper.git", + "reference": "df1ef9503299a8e3920079a16263b578eaf7c3ba" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laminas/laminas-escaper/zipball/df1ef9503299a8e3920079a16263b578eaf7c3ba", + "reference": "df1ef9503299a8e3920079a16263b578eaf7c3ba", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-mbstring": "*", + "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0" + }, + "conflict": { + "zendframework/zend-escaper": "*" + }, + "require-dev": { + "infection/infection": "^0.29.8", + "laminas/laminas-coding-standard": "~3.0.1", + "phpunit/phpunit": "^10.5.45", + "psalm/plugin-phpunit": "^0.19.2", + "vimeo/psalm": "^6.6.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Laminas\\Escaper\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["BSD-3-Clause"], + "description": "Securely and safely escape HTML, HTML attributes, JavaScript, CSS, and URLs", + "homepage": "https://laminas.dev", + "keywords": ["escaper", "laminas"], + "support": { + "chat": "https://laminas.dev/chat", + "docs": "https://docs.laminas.dev/laminas-escaper/", + "forum": "https://discourse.laminas.dev", + "issues": "https://github.com/laminas/laminas-escaper/issues", + "rss": "https://github.com/laminas/laminas-escaper/releases.atom", + "source": "https://github.com/laminas/laminas-escaper" + }, + "funding": [ + { + "url": "https://funding.communitybridge.org/projects/laminas-project", + "type": "community_bridge" + } + ], + "time": "2025-05-06T19:29:36+00:00" + }, + { + "name": "league/color-extractor", + "version": "0.4.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/color-extractor.git", + "reference": "21fcac6249c5ef7d00eb83e128743ee6678fe505" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/color-extractor/zipball/21fcac6249c5ef7d00eb83e128743ee6678fe505", + "reference": "21fcac6249c5ef7d00eb83e128743ee6678fe505", + "shasum": "" + }, + "require": { + "ext-gd": "*", + "php": "^7.3 || ^8.0" + }, + "replace": { + "matthecat/colorextractor": "*" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "~2", + "phpunit/phpunit": "^9.5" + }, + "suggest": { + "ext-curl": "To download images from remote URLs if allow_url_fopen is disabled for security reasons" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\ColorExtractor\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["MIT"], + "authors": [ + { + "name": "Mathieu Lechat", + "email": "math.lechat@gmail.com", + "homepage": "http://matthecat.com", + "role": "Developer" + } + ], + "description": "Extract colors from an image as a human would do.", + "homepage": "https://github.com/thephpleague/color-extractor", + "keywords": ["color", "extract", "human", "image", "palette"], + "support": { + "issues": "https://github.com/thephpleague/color-extractor/issues", + "source": "https://github.com/thephpleague/color-extractor/tree/0.4.0" + }, + "time": "2022-09-24T15:57:16+00:00" + }, + { + "name": "michelf/php-smartypants", + "version": "1.8.1", + "source": { + "type": "git", + "url": "https://github.com/michelf/php-smartypants.git", + "reference": "47d17c90a4dfd0ccf1f87e25c65e6c8012415aad" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/michelf/php-smartypants/zipball/47d17c90a4dfd0ccf1f87e25c65e6c8012415aad", + "reference": "47d17c90a4dfd0ccf1f87e25c65e6c8012415aad", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "autoload": { + "psr-0": { + "Michelf": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["BSD-3-Clause"], + "authors": [ + { + "name": "Michel Fortin", + "email": "michel.fortin@michelf.ca", + "homepage": "https://michelf.ca/", + "role": "Developer" + }, + { + "name": "John Gruber", + "homepage": "https://daringfireball.net/" + } + ], + "description": "PHP SmartyPants", + "homepage": "https://michelf.ca/projects/php-smartypants/", + "keywords": ["dashes", "quotes", "spaces", "typographer", "typography"], + "support": { + "issues": "https://github.com/michelf/php-smartypants/issues", + "source": "https://github.com/michelf/php-smartypants/tree/1.8.1" + }, + "time": "2016-12-13T01:01:17+00:00" + }, + { + "name": "phpmailer/phpmailer", + "version": "v6.10.0", + "source": { + "type": "git", + "url": "https://github.com/PHPMailer/PHPMailer.git", + "reference": "bf74d75a1fde6beaa34a0ddae2ec5fce0f72a144" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/bf74d75a1fde6beaa34a0ddae2ec5fce0f72a144", + "reference": "bf74d75a1fde6beaa34a0ddae2ec5fce0f72a144", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-filter": "*", + "ext-hash": "*", + "php": ">=5.5.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "^1.0", + "doctrine/annotations": "^1.2.6 || ^1.13.3", + "php-parallel-lint/php-console-highlighter": "^1.0.0", + "php-parallel-lint/php-parallel-lint": "^1.3.2", + "phpcompatibility/php-compatibility": "^9.3.5", + "roave/security-advisories": "dev-latest", + "squizlabs/php_codesniffer": "^3.7.2", + "yoast/phpunit-polyfills": "^1.0.4" + }, + "suggest": { + "decomplexity/SendOauth2": "Adapter for using XOAUTH2 authentication", + "ext-mbstring": "Needed to send email in multibyte encoding charset or decode encoded addresses", + "ext-openssl": "Needed for secure SMTP sending and DKIM signing", + "greew/oauth2-azure-provider": "Needed for Microsoft Azure XOAUTH2 authentication", + "hayageek/oauth2-yahoo": "Needed for Yahoo XOAUTH2 authentication", + "league/oauth2-google": "Needed for Google XOAUTH2 authentication", + "psr/log": "For optional PSR-3 debug logging", + "symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)", + "thenetworg/oauth2-azure": "Needed for Microsoft XOAUTH2 authentication" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPMailer\\PHPMailer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["LGPL-2.1-only"], + "authors": [ + { + "name": "Marcus Bointon", + "email": "phpmailer@synchromedia.co.uk" + }, + { + "name": "Jim Jagielski", + "email": "jimjag@gmail.com" + }, + { + "name": "Andy Prevost", + "email": "codeworxtech@users.sourceforge.net" + }, + { + "name": "Brent R. Matzelle" + } + ], + "description": "PHPMailer is a full-featured email creation and transfer class for PHP", + "support": { + "issues": "https://github.com/PHPMailer/PHPMailer/issues", + "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.10.0" + }, + "funding": [ + { + "url": "https://github.com/Synchro", + "type": "github" + } + ], + "time": "2025-04-24T15:19:31+00:00" + }, + { + "name": "psr/log", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["MIT"], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": ["log", "psr", "psr-3"], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.2" + }, + "time": "2024-09-11T13:17:53+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" + } + }, + "autoload": { + "files": ["function.php"] + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["MIT"], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:21:43+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": ["bootstrap.php"], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["MIT"], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": ["compatibility", "ctype", "polyfill", "portable"], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-intl-idn", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-idn.git", + "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/9614ac4d8061dc257ecc64cba1b140873dce8ad3", + "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3", + "shasum": "" + }, + "require": { + "php": ">=7.2", + "symfony/polyfill-intl-normalizer": "^1.10" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": ["bootstrap.php"], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Idn\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["MIT"], + "authors": [ + { + "name": "Laurent Bassin", + "email": "laurent@bassin.info" + }, + { + "name": "Trevor Rowbotham", + "email": "trevor.rowbotham@pm.me" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "idn", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-10T14:38:51+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "3833d7255cc303546435cb650316bff708a1c75c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": ["bootstrap.php"], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": ["Resources/stubs"] + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["MIT"], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", + "shasum": "" + }, + "require": { + "ext-iconv": "*", + "php": ">=7.2" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": ["bootstrap.php"], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["MIT"], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": ["compatibility", "mbstring", "polyfill", "portable", "shim"], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-12-23T08:48:59+00:00" + }, + { + "name": "symfony/yaml", + "version": "v7.3.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "d4f4a66866fe2451f61296924767280ab5732d9d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/d4f4a66866fe2451f61296924767280ab5732d9d", + "reference": "d4f4a66866fe2451f61296924767280ab5732d9d", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3.0", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/console": "<6.4" + }, + "require-dev": { + "symfony/console": "^6.4|^7.0" + }, + "bin": ["Resources/bin/yaml-lint"], + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": ["/Tests/"] + }, + "notification-url": "https://packagist.org/downloads/", + "license": ["MIT"], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Loads and dumps YAML files", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/yaml/tree/v7.3.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-08-27T11:34:33+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": {}, + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": "~8.2.0 || ~8.3.0 || ~8.4.0", + "ext-simplexml": "*", + "ext-ctype": "*", + "ext-curl": "*", + "ext-dom": "*", + "ext-filter": "*", + "ext-hash": "*", + "ext-iconv": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-openssl": "*" + }, + "platform-dev": {}, + "platform-overrides": { + "php": "8.2.0" + }, + "plugin-api-version": "2.6.0" } diff --git a/public/site/plugins/comments/routes/update.php b/public/site/plugins/comments/routes/update.php index 4622ff2..a3353c1 100644 --- a/public/site/plugins/comments/routes/update.php +++ b/public/site/plugins/comments/routes/update.php @@ -8,7 +8,6 @@ return [ $data = json_decode($json); $page = page($data->location->page->uri); - $project = page($data->location->project->uri); $file = $page->file($data->location->file->uuid); $isReply = $data->location->parent->id ?? false; diff --git a/public/site/plugins/kql/.editorconfig b/public/site/plugins/kql/.editorconfig new file mode 100644 index 0000000..fa9ba60 --- /dev/null +++ b/public/site/plugins/kql/.editorconfig @@ -0,0 +1,24 @@ +# This file is for unifying the coding style for different editors and IDEs +# editorconfig.org + +# PHP PSR-12 Coding Standards +# https://www.php-fig.org/psr/psr-12/ + +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_style = tab +indent_size = 2 +trim_trailing_whitespace = true + +[*.php] +indent_size = 4 +insert_final_newline = true + +[*.yml] +indent_style = space + +[*.md] +trim_trailing_whitespace = false diff --git a/public/site/plugins/kql/.github/FUNDING.yml b/public/site/plugins/kql/.github/FUNDING.yml new file mode 100644 index 0000000..b481582 --- /dev/null +++ b/public/site/plugins/kql/.github/FUNDING.yml @@ -0,0 +1 @@ +custom: ['https://getkirby.com/buy'] diff --git a/public/site/plugins/kql/.gitignore b/public/site/plugins/kql/.gitignore new file mode 100644 index 0000000..954190f --- /dev/null +++ b/public/site/plugins/kql/.gitignore @@ -0,0 +1,29 @@ +# OS files +.DS_Store + +# npm modules +/node_modules + +# files of Composer dependencies that are not needed for the plugin +/vendor/**/.* +/vendor/**/*.json +/vendor/**/*.txt +/vendor/**/*.md +/vendor/**/*.yml +/vendor/**/*.yaml +/vendor/**/*.xml +/vendor/**/*.dist +/vendor/**/readme.php +/vendor/**/LICENSE +/vendor/**/COPYING +/vendor/**/VERSION +/vendor/**/docs/* +/vendor/**/example/* +/vendor/**/examples/* +/vendor/**/test/* +/vendor/**/tests/* +/vendor/**/php4/* +/vendor/getkirby/composer-installer +/.php_cs.cache +/.phpunit.result.cache +/.php-cs-fixer.cache diff --git a/public/site/plugins/kql/.php-cs-fixer.dist.php b/public/site/plugins/kql/.php-cs-fixer.dist.php new file mode 100644 index 0000000..5dc5e7a --- /dev/null +++ b/public/site/plugins/kql/.php-cs-fixer.dist.php @@ -0,0 +1,60 @@ +exclude('dependencies') + ->exclude('panel/node_modules') + ->in(__DIR__); + +$config = new PhpCsFixer\Config(); +return $config + ->setRules([ + '@PSR12' => true, + 'align_multiline_comment' => ['comment_type' => 'phpdocs_like'], + 'array_indentation' => true, + 'array_syntax' => ['syntax' => 'short'], + 'cast_spaces' => ['space' => 'none'], + // 'class_keyword_remove' => true, // replaces static::class with 'static' (won't work) + 'combine_consecutive_issets' => true, + 'combine_consecutive_unsets' => true, + 'combine_nested_dirname' => true, + 'concat_space' => ['spacing' => 'one'], + 'declare_equal_normalize' => ['space' => 'single'], + 'dir_constant' => true, + 'function_typehint_space' => true, + 'include' => true, + 'logical_operators' => true, + 'lowercase_cast' => true, + 'lowercase_static_reference' => true, + 'magic_constant_casing' => true, + 'magic_method_casing' => true, + 'method_chaining_indentation' => true, + 'modernize_types_casting' => true, + 'multiline_comment_opening_closing' => true, + 'native_function_casing' => true, + 'native_function_type_declaration_casing' => true, + 'new_with_braces' => true, + 'no_blank_lines_after_class_opening' => true, + 'no_blank_lines_after_phpdoc' => true, + 'no_empty_comment' => true, + 'no_empty_phpdoc' => true, + 'no_empty_statement' => true, + 'no_leading_namespace_whitespace' => true, + 'no_mixed_echo_print' => ['use' => 'echo'], + 'no_unneeded_control_parentheses' => true, + 'no_unused_imports' => true, + 'no_useless_return' => true, + 'ordered_imports' => ['sort_algorithm' => 'alpha'], + // 'phpdoc_add_missing_param_annotation' => ['only_untyped' => false], // adds params in the wrong order + 'phpdoc_align' => ['align' => 'left'], + 'phpdoc_indent' => true, + 'phpdoc_scalar' => true, + 'phpdoc_trim' => true, + 'short_scalar_cast' => true, + 'single_line_comment_style' => true, + 'single_quote' => true, + 'ternary_to_null_coalescing' => true, + 'whitespace_after_comma_in_array' => true + ]) + ->setRiskyAllowed(true) + ->setIndent("\t") + ->setFinder($finder); diff --git a/public/site/plugins/kql/README.md b/public/site/plugins/kql/README.md index b85e616..6ad9ed9 100755 --- a/public/site/plugins/kql/README.md +++ b/public/site/plugins/kql/README.md @@ -16,21 +16,21 @@ Given a POST request to: `/api/query` ```json { - "query": "page('photography').children", - "select": { - "url": true, - "title": true, - "text": "page.text.markdown", - "images": { - "query": "page.moodboard", - "select": { - "url": true - } - } - }, - "pagination": { - "limit": 10 + "query": "page('photography').children", + "select": { + "url": true, + "title": true, + "text": "page.text.markdown", + "images": { + "query": "page.images", + "select": { + "url": true + } } + }, + "pagination": { + "limit": 10 + } } ``` @@ -39,48 +39,48 @@ Given a POST request to: `/api/query` ```json { - "code": 200, - "result": { - "data": [ - { - "url": "https://example.com/photography/trees", - "title": "Trees", - "text": "Lorem ipsum …", - "images": [ - { - "url": "https://example.com/media/pages/photography/trees/1353177920-1579007734/cheesy-autumn.jpg" - }, - { - "url": "https://example.com/media/pages/photography/trees/1940579124-1579007734/last-tree-standing.jpg" - }, - { - "url": "https://example.com/media/pages/photography/trees/3506294441-1579007734/monster-trees-in-the-fog.jpg" - } - ] - }, - { - "url": "https://example.com/photography/sky", - "title": "Sky", - "text": "

Dolor sit amet

…", - "images": [ - { - "url": "https://example.com/media/pages/photography/sky/183363500-1579007734/blood-moon.jpg" - }, - { - "url": "https://example.com/media/pages/photography/sky/3904851178-1579007734/coconut-milkyway.jpg" - } - ] - } - ], - "pagination": { - "page": 1, - "pages": 1, - "offset": 0, - "limit": 10, - "total": 2 - } - }, - "status": "ok" + "code": 200, + "result": { + "data": [ + { + "url": "https://example.com/photography/trees", + "title": "Trees", + "text": "Lorem ipsum …", + "images": [ + { + "url": "https://example.com/media/pages/photography/trees/1353177920-1579007734/cheesy-autumn.jpg" + }, + { + "url": "https://example.com/media/pages/photography/trees/1940579124-1579007734/last-tree-standing.jpg" + }, + { + "url": "https://example.com/media/pages/photography/trees/3506294441-1579007734/monster-trees-in-the-fog.jpg" + } + ] + }, + { + "url": "https://example.com/photography/sky", + "title": "Sky", + "text": "

Dolor sit amet

…", + "images": [ + { + "url": "https://example.com/media/pages/photography/sky/183363500-1579007734/blood-moon.jpg" + }, + { + "url": "https://example.com/media/pages/photography/sky/3904851178-1579007734/coconut-milkyway.jpg" + } + ] + } + ], + "pagination": { + "page": 1, + "pages": 1, + "offset": 0, + "limit": 10, + "total": 2 + } + }, + "status": "ok" } ``` @@ -116,35 +116,34 @@ return [ ### Sending POST Requests -You can use any HTTP request library in your language of choice to make regular POST requests to your `/api/query` endpoint. In this example, we are using [the `fetch` API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) and JavaScript to retrieve data from our Kirby installation. +You can use any HTTP request library in your language of choice to make regular POST requests to your `/api/query` endpoint. In this example, we are using [ohmyfetch](https://github.com/unjs/ohmyfetch) (a better fetch API for Node and the browser) and JavaScript to retreive data from our Kirby installation. ```js +import { $fetch } from "ohmyfetch"; + const api = "https://yoursite.com/api/query"; const username = "apiuser"; const password = "strong-secret-api-password"; const headers = { - Authorization: - "Basic " + Buffer.from(`${username}:${password}`).toString("base64"), - "Content-Type": "application/json", - Accept: "application/json", + Authorization: Buffer.from(`${username}:${password}`).toString("base64"), }; -const response = await fetch(api, { - method: "post", - body: JSON.stringify({ - query: "page('notes').children", - select: { - title: true, - text: "page.text.kirbytext", - slug: true, - date: "page.date.toDate('d.m.Y')", - }, - }), - headers, +const response = await $fetch(api, { + method: "post", + body: { + query: "page('notes').children", + select: { + title: true, + text: "page.text.kirbytext", + slug: true, + date: "page.date.toDate('d.m.Y')", + }, + }, + headers, }); -console.log(await response.json()); +console.log(response); ``` ### `query` @@ -158,15 +157,15 @@ When you don't pass the select option, Kirby will try to come up with the most u ##### Fetching the Site Title ```js -const response = await fetch(api, { - method: "post", - body: JSON.stringify({ - query: "site.title", - }), - headers, +const response = await $fetch(api, { + method: "post", + body: { + query: "site.title", + }, + headers, }); -console.log(await response.json()); +console.log(response); ```
@@ -185,15 +184,15 @@ console.log(await response.json()); ##### Fetching a List of Page IDs ```js -const response = await fetch(api, { - method: "post", - body: JSON.stringify({ - query: "site.children", - }), - headers, +const response = await $fetch(api, { + method: "post", + body: { + query: "site.children", + }, + headers, }); -console.log(await response.json()); +console.log(response); ```
@@ -220,15 +219,15 @@ console.log(await response.json()); Queries can even execute field methods. ```js -const response = await fetch(api, { - method: "post", - body: JSON.stringify({ - query: "site.title.upper", - }), - headers, +const response = await $fetch(api, { + method: "post", + body: { + query: "site.title.upper", + }, + headers, }); -console.log(await response.json()); +console.log(response); ```
@@ -253,16 +252,16 @@ KQL becomes really powerful by its flexible way to control the result set with t To include a property or field in your results, list them as an array. Check out our [reference for available properties](https://getkirby.com/docs/reference) for pages, users, files, etc. ```js -const response = await fetch(api, { - method: "post", - body: JSON.stringify({ - query: "site.children", - select: ["title", "url"], - }), - headers, +const response = await $fetch(api, { + method: "post", + body: { + query: "site.children", + select: ["title", "url"], + }, + headers, }); -console.log(await response.json()); +console.log(response); ```
@@ -311,19 +310,19 @@ console.log(await response.json()); You can also use the object notation and pass true for each key/property you want to include. ```js -const response = await fetch(api, { - method: "post", - body: JSON.stringify({ - query: "site.children", - select: { - title: true, - url: true, - }, - }), - headers, +const response = await $fetch(api, { + method: "post", + body: { + query: "site.children", + select: { + title: true, + url: true, + }, + }, + headers, }); -console.log(await response.json()); +console.log(response); ```
@@ -368,18 +367,18 @@ console.log(await response.json()); Instead of passing true, you can also pass a string query to specify what you want to return for each key in your select object. ```js -const response = await fetch(api, { - method: "post", - body: JSON.stringify({ - query: "site.children", - select: { - title: "page.title", - }, - }), - headers, +const response = await $fetch(api, { + method: "post", + body: { + query: "site.children", + select: { + title: "page.title", + }, + }, + headers, }); -console.log(await response.json()); +console.log(response); ```
@@ -409,18 +408,18 @@ console.log(await response.json()); #### Executing Field Methods ```js -const response = await fetch(api, { - method: "post", - body: JSON.stringify({ - query: "site.children", - select: { - title: "page.title.upper", - }, - }), - headers, +const response = await $fetch(api, { + method: "post", + body: { + query: "site.children", + select: { + title: "page.title.upper", + }, + }, + headers, }); -console.log(await response.json()); +console.log(response); ```
@@ -452,23 +451,21 @@ console.log(await response.json()); String queries are a perfect way to create aliases or return variations of the same field or property multiple times. ```js -const response = await fetch(api, { - method: "post", - body: JSON.stringify({ - query: "page('notes').children", - select: { - title: "page.title", - upperCaseTitle: "page.title.upper", - lowerCaseTitle: "page.title.lower", - guid: "page.id", - date: "page.date.toDate('d.m.Y')", - timestamp: "page.date.toTimestamp", - }, - }), - headers, +const response = await $fetch(api, { + method: "post", + body: { + query: "page('notes').children", + select: { + title: "page.title", + upperCaseTitle: "page.title.upper", + lowerCaseTitle: "page.title.lower", + guid: "page.id", + date: "page.date.toDate('d.m.Y')", + timestamp: "page.date.toTimestamp", + }, + }, + headers, }); - -console.log(await response.json()); ```
@@ -504,19 +501,17 @@ console.log(await response.json()); With such string queries you can of course also include nested data ```js -const response = await fetch(api, { - method: "post", - body: JSON.stringify({ - query: "page('photography').children", - select: { - title: "page.title", - images: "page.moodboard", - }, - }), - headers, +const response = await $fetch(api, { + method: "post", + body: { + query: "page('photography').children", + select: { + title: "page.title", + images: "page.images", + }, + }, + headers, }); - -console.log(await response.json()); ```
@@ -554,24 +549,22 @@ console.log(await response.json()); You can also pass an object with a `query` and a `select` option ```js -const response = await fetch(api, { - method: "post", - body: JSON.stringify({ - query: "page('photography').children", +const response = await $fetch(api, { + method: "post", + body: { + query: "page('photography').children", + select: { + title: "page.title", + images: { + query: "page.images", select: { - title: "page.title", - images: { - query: "page.moodboard", - select: { - filename: true, - }, - }, + filename: true, }, - }), - headers, + }, + }, + }, + headers, }); - -console.log(await response.json()); ```
@@ -623,21 +616,19 @@ Whenever you query a collection (pages, files, users, roles, languages) you can You can specify a custom limit with the limit option. The default limit for collections is 100 entries. ```js -const response = await fetch(api, { - method: "post", - body: JSON.stringify({ - query: "page('notes').children", - pagination: { - limit: 5, - }, - select: { - title: "page.title", - }, - }), - headers, +const response = await $fetch(api, { + method: "post", + body: { + query: "page('notes').children", + pagination: { + limit: 5, + }, + select: { + title: "page.title", + }, + }, + headers, }); - -console.log(await response.json()); ```
@@ -683,22 +674,20 @@ console.log(await response.json()); You can jump to any page in the resultset with the `page` option. ```js -const response = await fetch(api, { - method: "post", - body: JSON.stringify({ - query: "page('notes').children", - pagination: { - page: 2, - limit: 5, - }, - select: { - title: "page.title", - }, - }), - headers, +const response = await $fetch(api, { + method: "post", + body: { + query: "page('notes').children", + pagination: { + page: 2, + limit: 5, + }, + select: { + title: "page.title", + }, + }, + headers, }); - -console.log(await response.json()); ```
@@ -735,28 +724,26 @@ console.log(await response.json()); Pagination settings also work for subqueries. ```js -const response = await fetch(api, { - method: "post", - body: JSON.stringify({ - query: "page('photography').children", - select: { - title: "page.title", - images: { - query: "page.moodboard", - pagination: { - page: 2, - limit: 5, - }, - select: { - filename: true, - }, - }, +const response = await $fetch(api, { + method: "post", + body: { + query: "page('photography').children", + select: { + title: "page.title", + images: { + query: "page.images", + pagination: { + page: 2, + limit: 5, }, - }), - headers, + select: { + filename: true, + }, + }, + }, + }, + headers, }); - -console.log(await response.json()); ``` ### Multiple Queries in a Single Call @@ -764,45 +751,43 @@ console.log(await response.json()); With the power of selects and subqueries you can basically query the entire site in a single request ```js -const response = await fetch(api, { - method: "post", - body: JSON.stringify({ - query: "site", +const response = await $fetch(api, { + method: "post", + body: { + query: "site", + select: { + title: "site.title", + url: "site.url", + notes: { + query: "page('notes').children.listed", select: { - title: "site.title", - url: "site.url", - notes: { - query: "page('notes').children.listed", - select: { - title: true, - url: true, - date: "page.date.toDate('d.m.Y')", - text: "page.text.kirbytext", - }, - }, - photography: { - query: "page('photography').children.listed", - select: { - title: true, - images: { - query: "page.moodboard", - select: { - url: true, - alt: true, - caption: "file.caption.kirbytext", - }, - }, - }, - }, - about: { - text: "page.text.kirbytext", - }, + title: true, + url: true, + date: "page.date.toDate('d.m.Y')", + text: "page.text.kirbytext", }, - }), - headers, + }, + photography: { + query: "page('photography').children.listed", + select: { + title: true, + images: { + query: "page.images", + select: { + url: true, + alt: true, + caption: "file.caption.kirbytext", + }, + }, + }, + }, + about: { + text: "page.text.kirbytext", + }, + }, + }, + headers, }); - -console.log(await response.json()); ``` ### Allowing Methods @@ -951,7 +936,7 @@ If you want to fully allow access to an entire class without putting an intercep return [ 'kql' => [ 'classes' => [ - 'allowed' => [ + 'allow' => [ 'Kirby\Cms\System' ] ] @@ -967,23 +952,8 @@ KQL only offers access to data in your site. It does not support any mutations. ## Plugins -- [KQL + 11ty](https://github.com/getkirby/eleventykit) -- [KQL + Nuxt](https://nuxt-kql.jhnn.dev) - -## What's Kirby? - -- **[getkirby.com](https://getkirby.com)** – Get to know the CMS. -- **[Try it](https://getkirby.com/try)** – Take a test ride with our online demo. Or download one of our kits to get started. -- **[Documentation](https://getkirby.com/docs/guide)** – Read the official guide, reference and cookbook recipes. -- **[Issues](https://github.com/getkirby/kirby/issues)** – Report bugs and other problems. -- **[Feedback](https://feedback.getkirby.com)** – You have an idea for Kirby? Share it. -- **[Forum](https://forum.getkirby.com)** – Whenever you get stuck, don't hesitate to reach out for questions and support. -- **[Discord](https://chat.getkirby.com)** – Hang out and meet the community. -- **[Mastodon](https://mastodon.social/@getkirby)** – Spread the word. -- **[Instagram](https://www.instagram.com/getkirby/)** – Share your creations: #madewithkirby. - ---- +- [nuxt-kql](https://nuxt-kql.jhnn.dev): A Nuxt 3 module for KQL. ## License -[MIT](./LICENSE) License © 2020-2023 [Bastian Allgeier](https://getkirby.com) +[MIT](./LICENSE) License © 2020-2022 [Bastian Allgeier](https://getkirby.com) diff --git a/public/site/plugins/kql/composer.json b/public/site/plugins/kql/composer.json index 6939d91..a7172ae 100755 --- a/public/site/plugins/kql/composer.json +++ b/public/site/plugins/kql/composer.json @@ -2,8 +2,7 @@ "name": "getkirby/kql", "description": "Kirby Query Language", "license": "MIT", - "type": "kirby-plugin", - "version": "2.1.0", + "version": "1.2.0", "keywords": [ "kirby", "cms", @@ -12,62 +11,29 @@ "query", "headless" ], + "homepage": "https://getkirby.com", + "type": "kirby-plugin", "authors": [ { "name": "Bastian Allgeier", "email": "bastian@getkirby.com" - }, - { - "name": "Nico Hoffmann", - "email": "nico@getkirby.com" } ], - "homepage": "https://getkirby.com", - "support": { - "email": "support@getkirby.com", - "issues": "https://github.com/getkirby/kql/issues", - "forum": "https://forum.getkirby.com", - "source": "https://github.com/getkirby/kql" - }, "require": { - "php": ">=8.0.0 <8.3.0", - "getkirby/cms": ">=3.8.2", "getkirby/composer-installer": "^1.2.1" }, + "config": { + "optimize-autoloader": true, + "allow-plugins": { + "getkirby/composer-installer": false + } + }, "autoload": { "psr-4": { - "Kirby\\": [ - "tests/" - ] + "Kirby\\": "src/" } }, - "config": { - "allow-plugins": { - "getkirby/composer-installer": true - }, - "optimize-autoloader": true - }, - "extra": { - "installer-name": "kql", - "kirby-cms-path": false - }, "scripts": { - "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", - "ci": [ - "@fix", - "@analyze", - "@test" - ], - "fix": "php-cs-fixer fix", - "test": "phpunit --stderr --coverage-html=tests/coverage" + "fix": "php-cs-fixer fix" } } diff --git a/public/site/plugins/kql/composer.lock b/public/site/plugins/kql/composer.lock new file mode 100644 index 0000000..e5902db --- /dev/null +++ b/public/site/plugins/kql/composer.lock @@ -0,0 +1,66 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "b3661c52f0da1ad6c43e66f6001abb79", + "packages": [ + { + "name": "getkirby/composer-installer", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/getkirby/composer-installer.git", + "reference": "c98ece30bfba45be7ce457e1102d1b169d922f3d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/getkirby/composer-installer/zipball/c98ece30bfba45be7ce457e1102d1b169d922f3d", + "reference": "c98ece30bfba45be7ce457e1102d1b169d922f3d", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0 || ^2.0" + }, + "require-dev": { + "composer/composer": "^1.8 || ^2.0" + }, + "type": "composer-plugin", + "extra": { + "class": "Kirby\\ComposerInstaller\\Plugin" + }, + "autoload": { + "psr-4": { + "Kirby\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Kirby's custom Composer installer for the Kirby CMS and for Kirby plugins", + "homepage": "https://getkirby.com", + "support": { + "issues": "https://github.com/getkirby/composer-installer/issues", + "source": "https://github.com/getkirby/composer-installer/tree/1.2.1" + }, + "funding": [ + { + "url": "https://getkirby.com/buy", + "type": "custom" + } + ], + "time": "2020-12-28T12:54:39+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [], + "plugin-api-version": "2.3.0" +} diff --git a/public/site/plugins/kql/extensions/aliases.php b/public/site/plugins/kql/extensions/aliases.php deleted file mode 100644 index e3f1330..0000000 --- a/public/site/plugins/kql/extensions/aliases.php +++ /dev/null @@ -1,8 +0,0 @@ - function ($kirby) { - return [ - [ - 'pattern' => 'query', - 'method' => 'POST|GET', - 'auth' => $kirby->option('kql.auth') === false ? false : true, - 'action' => function () use ($kirby) { - $input = $kirby->request()->get(); - $result = Kql::run($input); - - return [ - 'code' => 200, - 'result' => $result, - 'status' => 'ok', - ]; - } - ] - ]; - } -]; diff --git a/public/site/plugins/kql/extensions/autoload.php b/public/site/plugins/kql/extensions/autoload.php deleted file mode 100644 index 274e1f0..0000000 --- a/public/site/plugins/kql/extensions/autoload.php +++ /dev/null @@ -1,21 +0,0 @@ - [ + 'routes' => function ($kirby) { + return [ + [ + 'pattern' => 'query', + 'method' => 'POST|GET', + 'auth' => $kirby->option('kql.auth') === false ? false : true, + 'action' => function () { + $result = Kql::run(get()); -require_once __DIR__ . '/extensions/aliases.php'; -require_once __DIR__ . '/extensions/helpers.php'; - -App::plugin('getkirby/kql', [ - 'api' => require_once 'extensions/api.php' + return [ + 'code' => 200, + 'result' => $result, + 'status' => 'ok', + ]; + } + ] + ]; + } + ] ]); diff --git a/public/site/plugins/kql/phpunit.xml.dist b/public/site/plugins/kql/phpunit.xml.dist new file mode 100644 index 0000000..c7d5502 --- /dev/null +++ b/public/site/plugins/kql/phpunit.xml.dist @@ -0,0 +1,13 @@ + + + + + ./src + + + + + ./tests/ + + + diff --git a/public/site/plugins/kql/src/Kql/Help.php b/public/site/plugins/kql/src/Kql/Help.php index faa6f21..eda82cf 100644 --- a/public/site/plugins/kql/src/Kql/Help.php +++ b/public/site/plugins/kql/src/Kql/Help.php @@ -2,46 +2,27 @@ namespace Kirby\Kql; -use Kirby\Toolkit\A; -use ReflectionClass; use ReflectionMethod; -/** - * Providing help information about - * queried objects, methods, arrays... - * - * @package Kirby KQL - * @author Bastian Allgeier - * @link https://getkirby.com - * @copyright Bastian Allgeier - * @license https://getkirby.com/license - */ class Help { - /** - * Provides information about passed value - * depending on its type - */ - public static function for($value): array + public static function for($object) { - if (is_array($value) === true) { - return static::forArray($value); + if (is_array($object) === true) { + return static::forArray($object); } - if (is_object($value) === true) { - return static::forObject($value); + if (is_object($object) === true) { + return static::forObject($object); } return [ - 'type' => gettype($value), - 'value' => $value + 'type' => gettype($object), + 'value' => $object ]; } - /** - * @internal - */ - public static function forArray(array $array): array + public static function forArray(array $array) { return [ 'type' => 'array', @@ -49,42 +30,42 @@ class Help ]; } - /** - * Gathers information for method about - * name, parameters, return type etc. - * @internal - */ - public static function forMethod(object $object, string $method): array + public static function forMethod($object, $method) { $reflection = new ReflectionMethod($object, $method); - $returns = $reflection->getReturnType()?->getName(); + $returns = null; $params = []; + if ($returnType = $reflection->getReturnType()) { + $returns = $returnType->getName(); + } + foreach ($reflection->getParameters() as $param) { - $name = $param->getName(); - $required = $param->isOptional() === false; - $type = $param->hasType() ? $param->getType()->getName() : null; - $default = null; + $p = [ + 'name' => $param->getName(), + 'required' => $param->isOptional() === false, + 'type' => $param->hasType() ? $param->getType()->getName() : null, + ]; if ($param->isDefaultValueAvailable()) { - $default = $param->getDefaultValue(); + $p['default'] = $param->getDefaultValue(); } - $call = ''; + $call = null; - if ($type !== null) { - $call = $type . ' '; + if ($p['type'] !== null) { + $call = $p['type'] . ' '; } - $call .= '$' . $name; + $call .= '$' . $p['name']; - if ($required === false && $default !== null) { - $call .= ' = ' . var_export($default, true); + if ($p['required'] === false && isset($p['default']) === true) { + $call .= ' = ' . var_export($p['default'], true); } $p['call'] = $call; - $params[$name] = compact('name', 'type', 'required', 'default', 'call'); + $params[$p['name']] = $p; } $call = '.' . $method; @@ -101,11 +82,7 @@ class Help ]; } - /** - * Gathers informations for each unique method - * @internal - */ - public static function forMethods(object $object, array $methods): array + public static function forMethods($object, $methods) { $methods = array_unique($methods); $reflection = []; @@ -123,30 +100,11 @@ class Help return $reflection; } - /** - * Retrieves info for objects either from Interceptor (to - * only list allowed methods) or via reflection - * @internal - */ - public static function forObject(object $object): array + public static function forObject($object) { - // get interceptor object to only return info on allowed methods - $interceptor = Interceptor::replace($object); + $original = $object; + $object = Interceptor::replace($original); - if ($interceptor instanceof Interceptor) { - return $interceptor->__debugInfo(); - } - - // for original classes, use reflection - $class = new ReflectionClass($object); - $methods = A::map( - $class->getMethods(), - fn ($method) => static::forMethod($object, $method->getName()) - ); - - return [ - 'type' => $class->getName(), - 'methods' => $methods - ]; + return $object->__debugInfo(); } } diff --git a/public/site/plugins/kql/src/Kql/Interceptor.php b/public/site/plugins/kql/src/Kql/Interceptor.php index 7f6e774..ee14959 100644 --- a/public/site/plugins/kql/src/Kql/Interceptor.php +++ b/public/site/plugins/kql/src/Kql/Interceptor.php @@ -2,294 +2,58 @@ namespace Kirby\Kql; -use Closure; -use Kirby\Cms\App; -use Kirby\Exception\InvalidArgumentException; +use Exception; use Kirby\Exception\PermissionException; -use Kirby\Toolkit\Str; -use ReflectionFunction; -use ReflectionMethod; -use Throwable; -/** - * Base class for proxying core classes to - * intercept method calls that are not allowed - * on the related core class - * - * @package Kirby KQL - * @author Bastian Allgeier - * @link https://getkirby.com - * @copyright Bastian Allgeier - * @license https://getkirby.com/license - */ -abstract class Interceptor +class Interceptor { - public const CLASS_ALIAS = null; - - protected $toArray = []; - - public function __construct(protected $object) - { - } - - /** - * Magic caller that prevents access - * to restricted methods - */ - public function __call(string $method, array $args = []) - { - if ($this->isAllowedMethod($method) === true) { - return $this->object->$method(...$args); - } - - $this->forbiddenMethod($method); - } - - /** - * Return information about corresponding object - * incl. information about allowed methods - */ - public function __debugInfo(): array - { - $help = Help::forMethods($this->object, $this->allowedMethods()); - - return [ - 'type' => $this::CLASS_ALIAS, - 'methods' => $help, - 'value' => $this->toArray() - ]; - } - - /** - * Returns list of allowed classes. Specific list - * to be implemented in specific interceptor child classes. - * @codeCoverageIgnore - */ - public function allowedMethods(): array - { - return []; - } - - /** - * Returns class name for Interceptor that responds - * to passed name string of a Kirby core class - * @internal - */ - public static function class(string $class): string - { - return str_replace('Kirby\\', 'Kirby\\Kql\\Interceptors\\', $class); - } - - /** - * Throws exception for accessing a restricted method - * @throws \Kirby\Exception\PermissionException - */ - protected function forbiddenMethod(string $method) - { - $name = get_class($this->object) . '::' . $method . '()'; - throw new PermissionException('The method "' . $name . '" is not allowed in the API context'); - } - - /** - * Checks if method is allowed to call - */ - public function isAllowedMethod($method) - { - $kirby = App::instance(); - $name = strtolower(get_class($this->object) . '::' . $method); - - // get list of blocked methods from config - $blocked = $kirby->option('kql.methods.blocked', []); - $blocked = array_map('strtolower', $blocked); - - // check in the block list from the config - if (in_array($name, $blocked) === true) { - return false; - } - - // check in class allow list - if (in_array($method, $this->allowedMethods()) === true) { - return true; - } - - // get list of explicitly allowed methods from config - $allowed = $kirby->option('kql.methods.allowed', []); - $allowed = array_map('strtolower', $allowed); - - // check in the allow list from the config - if (in_array($name, $allowed) === true) { - return true; - } - - // support for model methods with docblock comment - if ($this->isAllowedCallable($method) === true) { - return true; - } - - // support for custom methods with docblock comment - if ($this->isAllowedCustomMethod($method) === true) { - return true; - } - - return false; - } - - /** - * Checks if closure or object method is allowed - */ - protected function isAllowedCallable($method): bool - { - try { - $ref = match (true) { - $method instanceof Closure - => new ReflectionFunction($method), - is_string($method) === true - => new ReflectionMethod($this->object, $method), - default - => throw new InvalidArgumentException('Invalid method') - }; - - if ($comment = $ref->getDocComment()) { - if (Str::contains($comment, '@kql-allowed') === true) { - return true; - } - } - } catch (Throwable) { - return false; - } - - return false; - } - - protected function isAllowedCustomMethod(string $method): bool - { - // has no custom methods - if (property_exists($this->object, 'methods') === false) { - return false; - } - - // does not have that method - if (!$call = $this->method($method)) { - return false; - } - - // check for a docblock comment - if ($this->isAllowedCallable($call) === true) { - return true; - } - - return false; - } - - /** - * Returns a registered method by name, either from - * the current class or from a parent class ordered by - * inheritance order (top to bottom) - */ - protected function method(string $method) - { - if (isset($this->object::$methods[$method]) === true) { - return $this->object::$methods[$method]; - } - - foreach (class_parents($this->object) as $parent) { - if (isset($parent::$methods[$method]) === true) { - return $parent::$methods[$method]; - } - } - - return null; - } - - /** - * Tries to replace a Kirby core object with the - * corresponding interceptor. - * @throws \Kirby\Exception\InvalidArgumentException for non-objects - * @throws \Kirby\Exception\PermissionException when accessing blocked class - */ public static function replace($object) { if (is_object($object) === false) { - throw new InvalidArgumentException('Unsupported value: ' . gettype($object)); + throw new Exception('Unsupported value: ' . gettype($object)); } - $kirby = App::instance(); - $class = get_class($object); - $name = strtolower($class); - - // 1. Is $object class explicitly blocked? - // get list of blocked classes from config - $blocked = $kirby->option('kql.classes.blocked', []); - $blocked = array_map('strtolower', $blocked); + $className = get_class($object); + $fullName = strtolower($className); + $blocked = array_map('strtolower', option('kql.classes.blocked', [])); // check in the block list from the config - if (in_array($name, $blocked) === true) { - throw new PermissionException('Access to the class "' . $class . '" is blocked'); + if (in_array($fullName, $blocked) === true) { + throw new PermissionException('Access to the class "' . $className . '" is blocked'); } - // 2. Is $object already an interceptor? // directly return interceptor objects - if ($object instanceof Interceptor) { + if (is_a($object, 'Kirby\\Kql\\Interceptors\\Interceptor') === true) { return $object; } - // 3. Does an interceptor class for $object exist? // check for an interceptor class - $interceptors = $kirby->option('kql.interceptors', []); - $interceptors = array_change_key_case($interceptors, CASE_LOWER); + $interceptors = array_change_key_case(option('kql.interceptors', []), CASE_LOWER); // load an interceptor from config if it exists and otherwise fall back to a built-in interceptor - $interceptor = $interceptors[$name] ?? static::class($class); + $interceptor = $interceptors[$fullName] ?? str_replace('Kirby\\', 'Kirby\\Kql\\Interceptors\\', $className); // check for a valid interceptor class - if ($class !== $interceptor && class_exists($interceptor) === true) { + if ($className !== $interceptor && class_exists($interceptor) === true) { return new $interceptor($object); } - // 4. Also check for parent classes of $object // go through parents of the current object to use their interceptors as fallback foreach (class_parents($object) as $parent) { - $interceptor = static::class($parent); + $interceptor = str_replace('Kirby\\', 'Kirby\\Kql\\Interceptors\\', $parent); if (class_exists($interceptor) === true) { return new $interceptor($object); } } - // 5. $object has no interceptor but is explicitly allowed? // check for a class in the allow list - $allowed = $kirby->option('kql.classes.allowed', []); - $allowed = array_map('strtolower', $allowed); + $allowed = array_map('strtolower', option('kql.classes.allowed', [])); // return the plain object if it is allowed - if (in_array($name, $allowed) === true) { + if (in_array($fullName, $allowed) === true) { return $object; } - // 6. None of the above? Block class. - throw new PermissionException('Access to the class "' . $class . '" is not supported'); - } - - public function toArray(): array|null - { - $toArray = []; - - // filter methods which cannot be called - foreach ($this->toArray as $method) { - if ($this->isAllowedMethod($method) === true) { - $toArray[] = $method; - } - } - - return Kql::select($this, $toArray); - } - - /** - * Mirrors by default ::toArray but can be - * implemented differently by specifc interceptor. - * KQL will prefer ::toResponse over ::toArray - */ - public function toResponse() - { - return $this->toArray(); + throw new PermissionException('Access to the class "' . $className . '" is not supported'); } } diff --git a/public/site/plugins/kql/src/Kql/Interceptors/Cms/App.php b/public/site/plugins/kql/src/Kql/Interceptors/Cms/App.php index e984dbb..33acb65 100644 --- a/public/site/plugins/kql/src/Kql/Interceptors/Cms/App.php +++ b/public/site/plugins/kql/src/Kql/Interceptors/Cms/App.php @@ -2,7 +2,7 @@ namespace Kirby\Kql\Interceptors\Cms; -use Kirby\Kql\Interceptor; +use Kirby\Kql\Interceptors\Interceptor; class App extends Interceptor { diff --git a/public/site/plugins/kql/src/Kql/Interceptors/Cms/Blueprint.php b/public/site/plugins/kql/src/Kql/Interceptors/Cms/Blueprint.php index d05aea0..016a6a1 100644 --- a/public/site/plugins/kql/src/Kql/Interceptors/Cms/Blueprint.php +++ b/public/site/plugins/kql/src/Kql/Interceptors/Cms/Blueprint.php @@ -2,7 +2,7 @@ namespace Kirby\Kql\Interceptors\Cms; -use Kirby\Kql\Interceptor; +use Kirby\Kql\Interceptors\Interceptor; class Blueprint extends Interceptor { diff --git a/public/site/plugins/kql/src/Kql/Interceptors/Cms/Collection.php b/public/site/plugins/kql/src/Kql/Interceptors/Cms/Collection.php index 0608365..5d7dec6 100644 --- a/public/site/plugins/kql/src/Kql/Interceptors/Cms/Collection.php +++ b/public/site/plugins/kql/src/Kql/Interceptors/Cms/Collection.php @@ -2,7 +2,7 @@ namespace Kirby\Kql\Interceptors\Cms; -use Kirby\Kql\Interceptor; +use Kirby\Kql\Interceptors\Interceptor; class Collection extends Interceptor { diff --git a/public/site/plugins/kql/src/Kql/Interceptors/Content/Content.php b/public/site/plugins/kql/src/Kql/Interceptors/Cms/Content.php similarity index 88% rename from public/site/plugins/kql/src/Kql/Interceptors/Content/Content.php rename to public/site/plugins/kql/src/Kql/Interceptors/Cms/Content.php index e1e75e4..35e2309 100644 --- a/public/site/plugins/kql/src/Kql/Interceptors/Content/Content.php +++ b/public/site/plugins/kql/src/Kql/Interceptors/Cms/Content.php @@ -1,8 +1,8 @@ object = $object; + } + + public function __call($method, array $args = []) + { + if ($this->isAllowedMethod($method) === true) { + return $this->object->$method(...$args); + } + + $this->forbiddenMethod($method); + } + + public function allowedMethods(): array + { + return []; + } + + protected function forbiddenMethod(string $method) + { + $className = get_class($this->object); + throw new PermissionException('The method "' . $className . '::' . $method . '()" is not allowed in the API context'); + } + + /** + * Returns a registered method by name, either from + * the current class or from a parent class ordered by + * inheritance order (top to bottom) + * + * @param string $method + * @return \Closure|null + */ + protected function getMethod(string $method) + { + if (isset($this->object::$methods[$method]) === true) { + return $this->object::$methods[$method]; + } + + foreach (class_parents($this->object) as $parent) { + if (isset($parent::$methods[$method]) === true) { + return $parent::$methods[$method]; + } + } + + return null; + } + + protected function isAllowedCallable($method): bool + { + try { + if (is_a($method, 'Closure') === true) { + $ref = new ReflectionFunction($method); + } elseif (is_string($method) === true) { + $ref = new ReflectionMethod($this->object, $method); + } else { + throw new Exception('Invalid method'); + } + + if ($comment = $ref->getDocComment()) { + if (Str::contains($comment, '@kql-allowed') === true) { + return true; + } + } + } catch (Throwable $e) { + return false; + } + + return false; + } + + protected function isAllowedMethod($method) + { + $fullName = strtolower(get_class($this->object) . '::' . $method); + $blocked = array_map('strtolower', option('kql.methods.blocked', [])); + + // check in the block list from the config + if (in_array($fullName, $blocked) === true) { + return false; + } + + // check in class allow list + if (in_array($method, $this->allowedMethods()) === true) { + return true; + } + + $allowed = array_map('strtolower', option('kql.methods.allowed', [])); + + // check in the allow list from the config + if (in_array($fullName, $allowed) === true) { + return true; + } + + // support for model methods with docblock comment + if ($this->isAllowedCallable($method) === true) { + return true; + } + + // support for custom methods with docblock comment + if ($this->isAllowedCustomMethod($method) === true) { + return true; + } + + return false; + } + + protected function isAllowedCustomMethod(string $method): bool + { + // has no custom methods + if (property_exists($this->object, 'methods') === false) { + return false; + } + + // does not have that method + if (!$call = $this->getMethod($method)) { + return false; + } + + // check for a docblock comment + if ($this->isAllowedCallable($call) === true) { + return true; + } + + return false; + } + + public function __debugInfo(): array + { + return [ + 'type' => $this::CLASS_ALIAS, + 'methods' => Help::forMethods($this->object, $this->allowedMethods()), + 'value' => $this->toArray() + ]; + } + + public function toArray(): ?array + { + $toArray = []; + + // filter methods which cannot be called + foreach ($this->toArray as $method) { + if ($this->isAllowedMethod($method) === true) { + $toArray[] = $method; + } + } + + return Kql::select($this, $toArray); + } + + public function toResponse() + { + return $this->toArray(); + } +} diff --git a/public/site/plugins/kql/src/Kql/Interceptors/Panel/Model.php b/public/site/plugins/kql/src/Kql/Interceptors/Panel/Model.php index 006d47f..6c0bbda 100755 --- a/public/site/plugins/kql/src/Kql/Interceptors/Panel/Model.php +++ b/public/site/plugins/kql/src/Kql/Interceptors/Panel/Model.php @@ -2,7 +2,7 @@ namespace Kirby\Kql\Interceptors\Panel; -use Kirby\Kql\Interceptor; +use Kirby\Kql\Interceptors\Interceptor; class Model extends Interceptor { diff --git a/public/site/plugins/kql/src/Kql/Interceptors/Toolkit/Obj.php b/public/site/plugins/kql/src/Kql/Interceptors/Toolkit/Obj.php index 0ac2861..ba131f0 100755 --- a/public/site/plugins/kql/src/Kql/Interceptors/Toolkit/Obj.php +++ b/public/site/plugins/kql/src/Kql/Interceptors/Toolkit/Obj.php @@ -2,7 +2,7 @@ namespace Kirby\Kql\Interceptors\Toolkit; -use Kirby\Kql\Interceptor; +use Kirby\Kql\Interceptors\Interceptor; class Obj extends Interceptor { diff --git a/public/site/plugins/kql/src/Kql/Kql.php b/public/site/plugins/kql/src/Kql/Kql.php index 55d1016..bbfa631 100644 --- a/public/site/plugins/kql/src/Kql/Kql.php +++ b/public/site/plugins/kql/src/Kql/Kql.php @@ -3,81 +3,16 @@ namespace Kirby\Kql; use Exception; -use Kirby\Cms\App; use Kirby\Cms\Collection; use Kirby\Toolkit\Str; -/** - * ... - * - * @package Kirby KQL - * @author Bastian Allgeier - * @link https://getkirby.com - * @copyright Bastian Allgeier - * @license https://getkirby.com/license - */ class Kql { - public static function fetch($model, $key, $selection) - { - // simple key/value - if ($selection === true) { - return static::render($model->$key()); - } - - // selection without additional query - if ( - is_array($selection) === true && - empty($selection['query']) === true - ) { - return static::select( - $model->$key(), - $selection['select'] ?? null, - $selection['options'] ?? [] - ); - } - - // nested queries - return static::run($selection, $model); - } - - /** - * Returns helpful information about the object - * type as well as, if available, values and methods - */ - public static function help($object): array + public static function help($object) { return Help::for($object); } - public static function query(string $query, $model = null) - { - $model ??= App::instance()->site(); - $data = [$model::CLASS_ALIAS => $model]; - - return Query::factory($query)->resolve($data); - } - - public static function render($value) - { - if (is_object($value) === true) { - // replace actual object with intercepting proxy class - $object = Interceptor::replace($value); - - if (method_exists($object, 'toResponse') === true) { - return $object->toResponse(); - } - - if (method_exists($object, 'toArray') === true) { - return $object->toArray(); - } - - throw new Exception('The object "' . get_class($object) . '" cannot be rendered. Try querying one of its methods instead.'); - } - - return $value; - } - public static function run($input, $model = null) { // string queries @@ -97,9 +32,11 @@ class Kql return $result; } - $query = $input['query'] ?? 'site'; + $query = $input['query'] ?? 'site'; $select = $input['select'] ?? null; - $options = ['pagination' => $input['pagination'] ?? null]; + $options = [ + 'pagination' => $input['pagination'] ?? null, + ]; // check for invalid queries if (is_string($query) === false) { @@ -107,14 +44,74 @@ class Kql } $result = static::query($query, $model); + return static::select($result, $select, $options); } - public static function select( - $data, - array|string|null $select = null, - array $options = [] - ) { + public static function fetch($model, $key, $selection) + { + // simple key/value + if ($selection === true) { + return static::render($model->$key()); + } + + // selection without additional query + if (is_array($selection) === true && empty($selection['query']) === true) { + return static::select($model->$key(), $selection['select'] ?? null, $selection['options'] ?? []); + } + + // nested queries + return static::run($selection, $model); + } + + public static function query(string $query, $model = null) + { + $kirby = kirby(); + $site = $kirby->site(); + $model = $model ?? $site; + + $query = new Query($query, [ + 'collection' => function (string $id) use ($kirby) { + return $kirby->collection($id); + }, + 'file' => function (string $id) use ($kirby) { + return $kirby->file($id); + }, + 'kirby' => $kirby, + 'page' => function (string $id) use ($site) { + return $site->find($id); + }, + 'site' => $site, + 'user' => function (string $id = null) use ($kirby) { + return $kirby->user($id); + }, + $model::CLASS_ALIAS => $model + ]); + + return $query->result(); + } + + public static function render($value) + { + if (is_object($value) === true) { + $object = Interceptor::replace($value); + + if (method_exists($object, 'toResponse') === true) { + return $object->toResponse(); + } + + if (method_exists($object, 'toArray') === true) { + return $object->toArray(); + } + + throw new Exception('The object "' . get_class($object) . '" cannot be rendered. Try querying one of its methods instead.'); + } + + return $value; + } + + public static function select($data, $select, array $options = []) + { if ($select === null) { return static::render($data); } @@ -123,23 +120,20 @@ class Kql return static::help($data); } - if ($data instanceof Collection) { + if (is_a($data, 'Kirby\Cms\Collection') === true) { return static::selectFromCollection($data, $select, $options); } if (is_object($data) === true) { - return static::selectFromObject($data, $select); + return static::selectFromObject($data, $select, $options); } if (is_array($data) === true) { - return static::selectFromArray($data, $select); + return static::selectFromArray($data, $select, $options); } } - /** - * @internal - */ - public static function selectFromArray(array $array, array $select): array + public static function selectFromArray($array, $select, array $options = []) { $result = []; @@ -159,14 +153,8 @@ class Kql return $result; } - /** - * @internal - */ - public static function selectFromCollection( - Collection $collection, - array|string $select, - array $options = [] - ): array { + public static function selectFromCollection(Collection $collection, $select, array $options = []) + { if ($options['pagination'] ?? false) { $collection = $collection->paginate($options['pagination']); } @@ -193,14 +181,8 @@ class Kql return $data; } - /** - * @internal - */ - public static function selectFromObject( - object $object, - array|string $select - ): array { - // replace actual object with intercepting proxy class + public static function selectFromObject($object, $select, array $options = []) + { $object = Interceptor::replace($object); $result = []; diff --git a/public/site/plugins/kql/src/Kql/Query.php b/public/site/plugins/kql/src/Kql/Query.php index e4eebcb..7cd012b 100644 --- a/public/site/plugins/kql/src/Kql/Query.php +++ b/public/site/plugins/kql/src/Kql/Query.php @@ -2,28 +2,71 @@ namespace Kirby\Kql; -use Kirby\Query\Query as BaseQuery; +use Kirby\Toolkit\Query as BaseQuery; -/** - * Extends the core Query class with the KQL-specific - * functionalities to intercept the segments chain calls - * - * @package Kirby KQL - * @author Nico Hoffmann - * @link https://getkirby.com - * @copyright Bastian Allgeier - * @license https://getkirby.com/license - */ class Query extends BaseQuery { - /** - * Intercepts the chain of segments called - * on each other by replacing objects with - * their corresponding Interceptor which - * handles blocking calls to restricted methods - */ - public function intercept(mixed $result): mixed + protected function interceptor($object) { - return is_object($result) ? Interceptor::replace($result): $result; + return Interceptor::replace($object); + } + + /** + * Resolves the query if anything + * can be found. Otherwise returns null. + * + * @param string $query + * @return mixed + */ + protected function resolve(string $query) + { + // direct key access in arrays + if (is_array($this->data) === true && array_key_exists($query, $this->data) === true) { + $value = $this->data[$query]; + + // closure resolver + if (is_a($value, 'Closure') === true) { + $value = $value(); + } + + return $this->interceptor($value); + } + + $parts = $this->parts($query); + $data = $this->data; + $value = null; + + while (count($parts)) { + $part = array_shift($parts); + $info = $this->part($part); + $method = $info['method']; + $value = null; + + if (is_array($data)) { + $value = $data[$method] ?? null; + } elseif (is_object($data)) { + $data = $this->interceptor($data); + + if (method_exists($data, $method) || method_exists($data, '__call')) { + $value = $data->$method(...$info['args']); + } + } elseif (is_scalar($data)) { + return $data; + } else { + return null; + } + + if (is_a($value, 'Closure') === true) { + $value = $value(...$info['args']); + } + + if (is_array($value) === true) { + $data = $value; + } elseif (is_object($value) === true) { + $data = $this->interceptor($value); + } + } + + return $value; } } diff --git a/public/site/plugins/kql/tests/InterceptorTest.php b/public/site/plugins/kql/tests/InterceptorTest.php new file mode 100644 index 0000000..8bb146e --- /dev/null +++ b/public/site/plugins/kql/tests/InterceptorTest.php @@ -0,0 +1,175 @@ + new Page([ + 'slug' => 'test' + ]), + 'name' => 'test', + ]), + 'Kirby\\Kql\\Interceptors\\Cms\\Blueprint' + ], + [ + new Content(), + 'Kirby\\Kql\\Interceptors\\Cms\\Content' + ], + [ + new Field(null, 'key', 'value'), + 'Kirby\\Kql\\Interceptors\\Cms\\Field' + ], + [ + new File(['filename' => 'test.jpg']), + 'Kirby\\Kql\\Interceptors\\Cms\\File' + ], + [ + new FileBlueprint([ + 'model' => new File([ + 'filename' => 'test.jpg' + ]), + 'name' => 'test', + ]), + 'Kirby\\Kql\\Interceptors\\Cms\\Blueprint' + ], + [ + new FileExtended(['filename' => 'test.jpg']), + 'Kirby\\Kql\\Interceptors\\Cms\\File' + ], + [ + new FileVersion([ + 'original' => new File([ + 'filename' => 'test.jpg', + ]), + 'url' => '/test.jpg' + ]), + 'Kirby\\Kql\\Interceptors\\Cms\\FileVersion' + ], + [ + new Page(['slug' => 'test']), + 'Kirby\\Kql\\Interceptors\\Cms\\Page' + ], + [ + new PageBlueprint([ + 'model' => new Page([ + 'slug' => 'test' + ]), + 'name' => 'test', + ]), + 'Kirby\\Kql\\Interceptors\\Cms\\Blueprint' + ], + [ + new PageExtended(['slug' => 'test']), + 'Kirby\\Kql\\Interceptors\\Cms\\Page' + ], + [ + new Role(['name' => 'admin']), + 'Kirby\\Kql\\Interceptors\\Cms\\Role' + ], + [ + new Site(), + 'Kirby\\Kql\\Interceptors\\Cms\\Site' + ], + [ + new SiteBlueprint([ + 'model' => new Site(), + 'name' => 'test', + ]), + 'Kirby\\Kql\\Interceptors\\Cms\\Blueprint' + ], + [ + new SiteExtended(), + 'Kirby\\Kql\\Interceptors\\Cms\\Site' + ], + [ + new User(['email' => 'test@getkirby.com']), + 'Kirby\\Kql\\Interceptors\\Cms\\User' + ], + [ + new UserBlueprint([ + 'model' => new User(['email' => 'test@getkirby.com']), + 'name' => 'test', + ]), + 'Kirby\\Kql\\Interceptors\\Cms\\Blueprint' + ], + [ + new UserExtended(['email' => 'test@getkirby.com']), + 'Kirby\\Kql\\Interceptors\\Cms\\User' + ] + ]; + } + + /** + * @dataProvider objectProvider + */ + public function testReplace($object, $inspector) + { + $result = Interceptor::replace($object); + $this->assertInstanceOf($inspector, $result); + } + + public function testReplaceNonObject() + { + $this->expectException('Exception'); + $this->expectExceptionMessage('Unsupported value: string'); + + $result = Interceptor::replace('hello'); + } + + public function testReplaceUnknownObject() + { + $this->expectException('Kirby\Exception\PermissionException'); + $this->expectExceptionMessage('Access to the class "stdClass" is not supported'); + + $object = new \stdClass(); + $result = Interceptor::replace($object); + } +} diff --git a/public/site/plugins/kql/tests/KqlTest.php b/public/site/plugins/kql/tests/KqlTest.php new file mode 100644 index 0000000..f565938 --- /dev/null +++ b/public/site/plugins/kql/tests/KqlTest.php @@ -0,0 +1,158 @@ +app = new App([ + 'roots' => [ + 'index' => '/dev/null' + ], + 'site' => [ + 'children' => [ + [ + 'slug' => 'projects' + ], + [ + 'slug' => 'about' + ], + [ + 'slug' => 'contact' + ] + ], + 'content' => [ + 'title' => 'Test Site' + ], + ] + ]); + } + + public function testForbiddenMethod() + { + $this->expectException("Kirby\Exception\PermissionException"); + $this->expectExceptionMessage('The method "Kirby\Cms\Page::delete()" is not allowed in the API context'); + $result = Kql::run('site.children.first.delete'); + } + + public function testRun() + { + $result = Kql::run('site.title'); + $expected = 'Test Site'; + + $this->assertSame($expected, $result); + } + + public function testQuery() + { + $result = Kql::run([ + 'query' => 'site.children', + 'select' => 'slug' + ]); + + $expected = [ + [ + 'slug' => 'projects', + ], + [ + 'slug' => 'about', + ], + [ + 'slug' => 'contact', + ] + ]; + + $this->assertSame($expected, $result); + } + + public function testSelectWithAlias() + { + $result = Kql::run([ + 'select' => [ + 'myTitle' => 'site.title' + ] + ]); + + $expected = [ + 'myTitle' => 'Test Site', + ]; + + $this->assertSame($expected, $result); + } + + public function testSelectWithArray() + { + $result = Kql::run([ + 'select' => ['title', 'url'] + ]); + + $expected = [ + 'title' => 'Test Site', + 'url' => '/' + ]; + + $this->assertSame($expected, $result); + } + + public function testSelectWithBoolean() + { + $result = Kql::run([ + 'select' => [ + 'title' => true + ] + ]); + + $expected = [ + 'title' => 'Test Site' + ]; + + $this->assertSame($expected, $result); + } + + public function testSelectWithQuery() + { + $result = Kql::run([ + 'select' => [ + 'children' => [ + 'query' => 'site.children', + 'select' => 'slug' + ] + ] + ]); + + $expected = [ + 'children' => [ + [ + 'slug' => 'projects', + ], + [ + 'slug' => 'about', + ], + [ + 'slug' => 'contact', + ] + ] + ]; + + $this->assertSame($expected, $result); + } + + public function testSelectWithString() + { + $result = Kql::run([ + 'select' => [ + 'title' => 'site.title.upper' + ] + ]); + + $expected = [ + 'title' => 'TEST SITE' + ]; + + $this->assertSame($expected, $result); + } +} diff --git a/public/site/plugins/kql/tests/bootstrap.php b/public/site/plugins/kql/tests/bootstrap.php new file mode 100644 index 0000000..518b666 --- /dev/null +++ b/public/site/plugins/kql/tests/bootstrap.php @@ -0,0 +1,25 @@ +