From 84d9cf6187d51736eae6574003bd955d0b43c083 Mon Sep 17 00:00:00 2001
From: chevereto
Date: Wed, 8 Apr 2026 17:03:15 +0000
Subject: [PATCH] Automatic push 4.5.0
---
.github/test.yml | 2 +-
.github/workflows/docker.yml | 2 +-
.github/workflows/release-free.yml | 2 +-
.package/4.4.2.txt | 7 -
.package/4.5.0.txt | 28 +
app/bin/tenants | 30 +-
app/composer.json | 37 +-
app/composer.lock | 3210 ++++++++++++++---
app/env-default.php | 4 +-
app/legacy/commands/password-reset.php | 10 +-
.../commands/version-installed.php} | 12 +-
app/legacy/entrypoints/cli.php | 1 +
app/legacy/install/installer.php | 42 +-
app/legacy/load/app.php | 4 +-
app/legacy/load/register-handlers.php | 48 +-
app/legacy/load/web.php | 27 +-
app/legacy/routes/dashboard.php | 185 +-
app/legacy/routes/image.php | 7 +-
app/routes/tenants-api-v4.php | 10 +
app/routes/tenants-internal-api-v4.php | 29 +
app/schemas/mysql-8/_/tenants_stats.sql | 1 +
.../Controllers/Api/V4/TenantPlanPatch.php | 1 +
.../Api/V4/TenantUserPasswordResetPatch.php | 95 +
.../Api/V4/TenantsAuthVerifyPost.php | 28 +
.../Api/V4/TenantsConfigTraefikGet.php | 43 +
app/src/Legacy/Classes/DB.php | 4 +-
app/src/Legacy/Classes/Settings.php | 64 +-
app/src/Legacy/Classes/Stat.php | 14 +-
app/src/Legacy/Classes/Tag.php | 3 +-
app/src/Legacy/Classes/User.php | 59 +-
app/src/Legacy/functions.php | 215 +-
app/src/Tenants/Tenants.php | 48 +-
app/src/Tenants/TenantsConfig.php | 146 +
app/src/Vars/functions.php | 49 +
content/images/system/default/home_cover.jpg | Bin 463463 -> 352419 bytes
content/legacy/themes/Peafowl/lib/peafowl.css | 6 +
.../legacy/themes/Peafowl/lib/peafowl.min.css | 2 +-
.../Peafowl/snippets/anywhere_upload.php | 23 +-
.../views/dashboard/settings/email.php | 270 +-
.../views/dashboard/settings/homepage.php | 151 +
40 files changed, 4154 insertions(+), 765 deletions(-)
delete mode 100644 .package/4.4.2.txt
create mode 100644 .package/4.5.0.txt
rename app/{src/Legacy/Classes/Mailer.php => legacy/commands/version-installed.php} (52%)
create mode 100644 app/routes/tenants-internal-api-v4.php
create mode 100644 app/src/Http/Controllers/Api/V4/TenantUserPasswordResetPatch.php
create mode 100644 app/src/Http/Controllers/Api/V4/TenantsAuthVerifyPost.php
create mode 100644 app/src/Http/Controllers/Api/V4/TenantsConfigTraefikGet.php
create mode 100644 app/src/Tenants/TenantsConfig.php
create mode 100644 content/legacy/themes/Peafowl/views/dashboard/settings/homepage.php
diff --git a/.github/test.yml b/.github/test.yml
index f863c5e..c600796 100644
--- a/.github/test.yml
+++ b/.github/test.yml
@@ -13,7 +13,7 @@ jobs:
strategy:
matrix:
operating-system: [ubuntu-22.04]
- php-versions: ["8.0", "8.1"]
+ php-versions: ["8.2"]
env:
extensions: pcov, imagick
tools: composer
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index 43ed4b7..cc91c01 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -14,7 +14,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-22.04]
- php: ["8.1"]
+ php: ["8.2"]
env:
tools: composer
ini-values: default_charset='UTF-8'
diff --git a/.github/workflows/release-free.yml b/.github/workflows/release-free.yml
index 0a1978e..016720e 100644
--- a/.github/workflows/release-free.yml
+++ b/.github/workflows/release-free.yml
@@ -11,7 +11,7 @@ jobs:
strategy:
matrix:
operating-system: [ubuntu-latest]
- php-versions: ["8.1"]
+ php-versions: ["8.2"]
env:
tools: composer
ini-values: default_charset='UTF-8'
diff --git a/.package/4.4.2.txt b/.package/4.4.2.txt
deleted file mode 100644
index a170111..0000000
--- a/.package/4.4.2.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-Chevereto 4.4.2 (2026-01-06)
-
-- Fixed bug with "Upgrade now" button display
-- Fixed bug in /settings/api route display
-- Fixed bug in delete URL not working
-- Fixed bug in Free edition missing Follow class
-- Fixed API documentation links
diff --git a/.package/4.5.0.txt b/.package/4.5.0.txt
new file mode 100644
index 0000000..6e52dab
--- /dev/null
+++ b/.package/4.5.0.txt
@@ -0,0 +1,28 @@
+Chevereto 4.5.0 (2026-04-08)
+
+- Added /_/api/4/auth/verify route
+- Added /_/api/4/config/traefik internal HTTP provider route
+- Added /_/api/4/tenants/{id}/user-password-reset route
+- Added CHEVERETO_ENABLE_GUESTS env for controlling guest interactions
+- Added CHEVERETO_SERVICE_NAME env for specifying the service name
+- Added CHEVERETO_TRIAL_ENABLE_* keys support for controlling features enabled during trial
+- Added CHEVERETO_TRIAL_MAX_* keys support for controlling max limits during trial
+- Added CHEVERETO_TRIAL env for controlling trial mode
+- Added envTrialAware helper function for accessing trial-aware env variables
+- Added version-installed command
+- Added login_providers tenant stats
+- Added password parameter for password-reset command
+- Added port 8080 to the list of allowed ports
+- Added support for more email providers: AhaSend, Amazon SES, Azure, Brevo, Infobip, MailerSend, Mailgun, Mailjet, Mailomat, MailPace, Mailtrap, Mandrill, Microsoft Graph, Postal, Postmark, Resend, Scaleway, SendGrid and Sweego
+- Bumped minimum PHP version to 8.2
+- Fixed "Powered by" message
+- Fixed bug affecting homepage (free edition)
+- Fixed bug on /_/api/4/* routes missing error responses
+- Fixed bug on Tenants jobs:worker command when passing tenant id
+- Fixed bug on Tenants caching system
+- Fixed bug on tenants CLI database-migrate command
+- Fixed bug preventing Tenant installation
+- Fixed missing custom semantics parsing for image route description
+- Improved "Something went wrong" error page for both SaaS and self-hosted contexts
+- Improved album dropdown options on uploader
+- Renamed env variable CHEVERETO_JOBS_WORKER_INTERVAL to CHEVERETO_SCHEDULER_INTERVAL
diff --git a/app/bin/tenants b/app/bin/tenants
index 878d877..770bf7a 100755
--- a/app/bin/tenants
+++ b/app/bin/tenants
@@ -246,6 +246,7 @@ if($missingOptions ?? false) {
$envDefault = require dirname(__DIR__, 1) . '/env-default.php';
$envVar = array_merge($envDefault, ENV, getCheveretoEnv());
$envVar['CHEVERETO_DB_TABLE_ROOT_PREFIX'] = $envVar['CHEVERETO_DB_TABLE_PREFIX'];
+$envVar['CHEVERETO_CACHE_KEY_ROOT_PREFIX'] = $envVar['CHEVERETO_CACHE_KEY_PREFIX'];
$envVar['CHEVERETO_DB_TABLE_PREFIX'] .= '_'; // chv__
$envVar['CHEVERETO_CACHE_KEY_PREFIX'] .= '_:'; // chv:_:
new EnvVar($envVar);
@@ -273,6 +274,7 @@ $redis->connect(env()['CHEVERETO_CACHE_HOST'], (int) env()['CHEVERETO_CACHE_PORT
if (env()['CHEVERETO_CACHE_PASSWORD'] !== '') {
$redis->auth(env()['CHEVERETO_CACHE_PASSWORD']);
}
+$redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP);
new EncryptionInstance(
new Encryption(
new Key(env()['CHEVERETO_ENCRYPTION_KEY'])
@@ -494,7 +496,7 @@ $databaseMigrate = function() use ($tenants, $opts, $logger): void {
}
foreach ($tenantsUpdate as $tenant) {
runAppCommand(
- command: ['-C', 'database-update'],
+ command: ['-C', 'database-migrate'],
env: array_merge($_ENV, [
'CHEVERETO_TENANT' => $tenant['id'],
]),
@@ -527,7 +529,7 @@ $jobsWorker = function() use ($tenants, $opts, $logger): void {
while (true) {
$now = time();
if($opts['id'] ?? null) {
- $tenantsId = $opts['id'];
+ $tenantsId = [$opts['id']];
} else {
$tenantsId = $tenants->getTenantsIds(
['is_enabled' => true],
@@ -539,7 +541,7 @@ $jobsWorker = function() use ($tenants, $opts, $logger): void {
}
foreach ($tenantsId as $tenantId) {
$tenant = $tenants->getTenant($tenantId);
- $intervalSec = (int) ($tenant->env['CHEVERETO_JOBS_WORKER_INTERVAL'] ?? 300);
+ $intervalSec = (int) ($tenant->env['CHEVERETO_SCHEDULER_INTERVAL'] ?? 300);
$mustRun = $tenant->lastJobAt === null
|| (strtotime($tenant->lastJobAt) + $intervalSec) <= $now;
if ($mustRun) {
@@ -547,11 +549,27 @@ $jobsWorker = function() use ($tenants, $opts, $logger): void {
tenantId: $tenantId,
lastJobAt: datetimegmt()
);
+ $commandEnv = array_merge($_ENV, [
+ 'CHEVERETO_TENANT' => $tenantId,
+ ]);
+ $commandExit = runAppCommand(
+ command: ['-C', 'version-installed'],
+ env: $commandEnv,
+ isVerbose: $isVerbose,
+ logger: $logger
+ );
+ if($commandExit === 255) {
+ $logger->write(
+ << $tenantId,
- ]),
+ env: $commandEnv,
isVerbose: $isVerbose,
logger: $logger
);
diff --git a/app/composer.json b/app/composer.json
index 8c67bc6..89e284e 100644
--- a/app/composer.json
+++ b/app/composer.json
@@ -16,29 +16,27 @@
"composer/package-versions-deprecated": true
},
"platform": {
- "php": "8.1.28"
+ "php": "8.2"
}
},
"require": {
- "php": "^8.1",
+ "php": "^8.2",
"intervention/image": "^2.6",
"jeroendesloovere/xmp-metadata-extractor": "^2.0",
"guzzlehttp/psr7": "^1.7||^2",
"aws/aws-sdk-php": "^3.336.15",
- "phpmailer/phpmailer": "^6.5",
- "psr/cache": "^1",
+ "psr/cache": "^3.0",
"psr/log": "^1||^2||^3",
"phpseclib/phpseclib": "^3.0.37",
"mobiledetect/mobiledetectlib": "^2.8",
"mlocati/ip-lib": "^1.17",
"composer/ca-bundle": "^1.2",
- "chevere/throwable-handler": "^1.0.2",
"pragmarx/google2fa": "^8.0",
"pragmarx/google2fa-qrcode": "^3.0",
"phpseclib/bcmath_compat": "^2.0",
"chillerlan/php-qrcode": "^4.3",
- "firebase/php-jwt": "^6.3",
- "lychee-org/php-exif": "1.0.2",
+ "firebase/php-jwt": "^7.0",
+ "lychee-org/php-exif": "^1.0.2",
"p3k/emoji-detector": "^1.0",
"php-ffmpeg/php-ffmpeg": "^1.2",
"psy/psysh": "^0.11.8",
@@ -46,11 +44,32 @@
"chevere/var-dump": "^2.0.7",
"chevere/var-support": "^1.0",
"matthiasmullie/scrapbook": "^1.0",
- "xrdebug/php": "^3.0",
"donatj/phpuseragentparser": "^1.11",
"chevere/router": "^0.9.1",
"chevere/http": "^0.7.1",
- "laminas/laminas-httphandlerrunner": "^2.13"
+ "laminas/laminas-httphandlerrunner": "^2.13",
+ "chevere/throwable-handler": "^1.0",
+ "symfony/mailer": "^7.4",
+ "symfony/http-client": "^7.4",
+ "symfony/amazon-mailer": "^7.4",
+ "symfony/aha-send-mailer": "^7.4",
+ "symfony/azure-mailer": "^7.4",
+ "symfony/brevo-mailer": "^7.4",
+ "symfony/infobip-mailer": "^7.4",
+ "symfony/mailgun-mailer": "^7.4",
+ "symfony/mailjet-mailer": "^7.4",
+ "symfony/mailomat-mailer": "^7.4",
+ "symfony/mail-pace-mailer": "^7.4",
+ "symfony/mailer-send-mailer": "^7.4",
+ "symfony/mailtrap-mailer": "^7.4",
+ "symfony/mailchimp-mailer": "^7.4",
+ "symfony/microsoft-graph-mailer": "^7.4",
+ "symfony/postal-mailer": "^7.4",
+ "symfony/postmark-mailer": "^7.4",
+ "symfony/resend-mailer": "^7.4",
+ "symfony/scaleway-mailer": "^7.4",
+ "symfony/sendgrid-mailer": "^7.4",
+ "symfony/sweego-mailer": "^7.4"
},
"autoload": {
"files": [
diff --git a/app/composer.lock b/app/composer.lock
index 5d2fddf..b287898 100644
--- a/app/composer.lock
+++ b/app/composer.lock
@@ -4,8 +4,141 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "5e2bfdedaf52961199396451f17a71e2",
+ "content-hash": "43b717369b031292cb23399f2243b065",
"packages": [
+ {
+ "name": "async-aws/core",
+ "version": "1.28.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/async-aws/core.git",
+ "reference": "e8b02ac30b17afaf1352cbd352dceb789d792d39"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/async-aws/core/zipball/e8b02ac30b17afaf1352cbd352dceb789d792d39",
+ "reference": "e8b02ac30b17afaf1352cbd352dceb789d792d39",
+ "shasum": ""
+ },
+ "require": {
+ "ext-hash": "*",
+ "ext-simplexml": "*",
+ "php": "^8.2",
+ "psr/cache": "^1.0 || ^2.0 || ^3.0",
+ "psr/log": "^1.0 || ^2.0 || ^3.0",
+ "symfony/deprecation-contracts": "^2.1 || ^3.0",
+ "symfony/http-client": "^4.4.16 || ^5.1.7 || ^6.0 || ^7.0 || ^8.0",
+ "symfony/http-client-contracts": "^1.1.8 || ^2.0 || ^3.0",
+ "symfony/service-contracts": "^1.0 || ^2.0 || ^3.0"
+ },
+ "conflict": {
+ "async-aws/s3": "<1.1",
+ "symfony/http-client": "5.2.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.5.42",
+ "symfony/error-handler": "^7.3.2 || ^8.0",
+ "symfony/phpunit-bridge": "^7.3.2 || ^8.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.28-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "AsyncAws\\Core\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Core package to integrate with AWS. This is a lightweight AWS SDK provider by AsyncAws.",
+ "keywords": [
+ "amazon",
+ "async-aws",
+ "aws",
+ "sdk",
+ "sts"
+ ],
+ "support": {
+ "source": "https://github.com/async-aws/core/tree/1.28.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/jderusse",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nyholm",
+ "type": "github"
+ }
+ ],
+ "time": "2026-02-16T10:24:54+00:00"
+ },
+ {
+ "name": "async-aws/ses",
+ "version": "1.14.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/async-aws/ses.git",
+ "reference": "8ba4c7f5bbb4d1055f3ebedcf0ea1b8b79393e5b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/async-aws/ses/zipball/8ba4c7f5bbb4d1055f3ebedcf0ea1b8b79393e5b",
+ "reference": "8ba4c7f5bbb4d1055f3ebedcf0ea1b8b79393e5b",
+ "shasum": ""
+ },
+ "require": {
+ "async-aws/core": "^1.9",
+ "php": "^8.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^11.5.42",
+ "symfony/error-handler": "^7.3.2 || ^8.0",
+ "symfony/phpunit-bridge": "^7.3.2 || ^8.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.14-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "AsyncAws\\Ses\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "SES client, part of the AWS SDK provided by AsyncAws.",
+ "keywords": [
+ "amazon",
+ "async-aws",
+ "aws",
+ "sdk",
+ "ses"
+ ],
+ "support": {
+ "source": "https://github.com/async-aws/ses/tree/1.14.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/jderusse",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nyholm",
+ "type": "github"
+ }
+ ],
+ "time": "2026-02-16T10:24:54+00:00"
+ },
{
"name": "aws/aws-crt-php",
"version": "v1.2.7",
@@ -62,16 +195,16 @@
},
{
"name": "aws/aws-sdk-php",
- "version": "3.369.7",
+ "version": "3.377.0",
"source": {
"type": "git",
"url": "https://github.com/aws/aws-sdk-php.git",
- "reference": "8678ee11ac680d462fabbecb455112a17aa7728d"
+ "reference": "573c569d20710a902446688a0439b53fef256e3a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/8678ee11ac680d462fabbecb455112a17aa7728d",
- "reference": "8678ee11ac680d462fabbecb455112a17aa7728d",
+ "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/573c569d20710a902446688a0439b53fef256e3a",
+ "reference": "573c569d20710a902446688a0439b53fef256e3a",
"shasum": ""
},
"require": {
@@ -92,12 +225,12 @@
"aws/aws-php-sns-message-validator": "~1.0",
"behat/behat": "~3.0",
"composer/composer": "^2.7.8",
- "dms/phpunit-arraysubset-asserts": "^0.4.0",
+ "dms/phpunit-arraysubset-asserts": "^v0.5.0",
"doctrine/cache": "~1.4",
"ext-dom": "*",
"ext-openssl": "*",
"ext-sockets": "*",
- "phpunit/phpunit": "^9.6",
+ "phpunit/phpunit": "^10.0",
"psr/cache": "^2.0 || ^3.0",
"psr/simple-cache": "^2.0 || ^3.0",
"sebastian/comparator": "^1.2.3 || ^4.0 || ^5.0",
@@ -135,11 +268,11 @@
"authors": [
{
"name": "Amazon Web Services",
- "homepage": "http://aws.amazon.com"
+ "homepage": "https://aws.amazon.com"
}
],
"description": "AWS SDK for PHP - Use Amazon Web Services in your PHP project",
- "homepage": "http://aws.amazon.com/sdkforphp",
+ "homepage": "https://aws.amazon.com/sdk-for-php",
"keywords": [
"amazon",
"aws",
@@ -153,9 +286,9 @@
"support": {
"forum": "https://github.com/aws/aws-sdk-php/discussions",
"issues": "https://github.com/aws/aws-sdk-php/issues",
- "source": "https://github.com/aws/aws-sdk-php/tree/3.369.7"
+ "source": "https://github.com/aws/aws-sdk-php/tree/3.377.0"
},
- "time": "2026-01-05T19:05:14+00:00"
+ "time": "2026-04-07T18:14:36+00:00"
},
{
"name": "chevere/action",
@@ -690,68 +823,18 @@
},
"time": "2025-11-27T17:22:51+00:00"
},
- {
- "name": "chevere/standard",
- "version": "1.0.1",
- "source": {
- "type": "git",
- "url": "https://github.com/chevere/standard.git",
- "reference": "bf2b876149b42a93f3bca5a81671f31caf963528"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/chevere/standard/zipball/bf2b876149b42a93f3bca5a81671f31caf963528",
- "reference": "bf2b876149b42a93f3bca5a81671f31caf963528",
- "shasum": ""
- },
- "require": {
- "php": "^8.1"
- },
- "require-dev": {
- "phpstan/phpstan": "^1.9",
- "phpunit/phpunit": "^9.5",
- "symplify/easy-coding-standard": "^11.1"
- },
- "type": "library",
- "autoload": {
- "files": [
- "src/functions.php"
- ],
- "psr-4": {
- "Chevere\\Standard\\": "src/"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "Apache-2.0"
- ],
- "authors": [
- {
- "name": "Rodolfo Berrios",
- "email": "rodolfo@chevere.org",
- "homepage": "https://chevere.org"
- }
- ],
- "description": "A chevere standard package",
- "homepage": "https://chevere.org",
- "support": {
- "issues": "https://github.com/chevere/standard/issues",
- "source": "https://github.com/chevere/standard/tree/1.0.1"
- },
- "time": "2024-06-01T16:48:38+00:00"
- },
{
"name": "chevere/throwable-handler",
- "version": "1.0.7",
+ "version": "1.0.10",
"source": {
"type": "git",
"url": "https://github.com/chevere/throwable-handler.git",
- "reference": "352e59ac8998d0249a4dc6665ab6cbd465bce6bd"
+ "reference": "f191d05d83857c24b1485762d9c75fbcefb86312"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/chevere/throwable-handler/zipball/352e59ac8998d0249a4dc6665ab6cbd465bce6bd",
- "reference": "352e59ac8998d0249a4dc6665ab6cbd465bce6bd",
+ "url": "https://api.github.com/repos/chevere/throwable-handler/zipball/f191d05d83857c24b1485762d9c75fbcefb86312",
+ "reference": "f191d05d83857c24b1485762d9c75fbcefb86312",
"shasum": ""
},
"require": {
@@ -789,9 +872,9 @@
"homepage": "https://chevere.org",
"support": {
"issues": "https://github.com/chevere/throwable-handler/issues",
- "source": "https://github.com/chevere/throwable-handler/tree/1.0.7"
+ "source": "https://github.com/chevere/throwable-handler/tree/1.0.10"
},
- "time": "2025-09-12T21:25:18+00:00"
+ "time": "2026-03-08T13:33:43+00:00"
},
{
"name": "chevere/trace",
@@ -843,21 +926,21 @@
},
{
"name": "chevere/var-dump",
- "version": "2.0.7",
+ "version": "2.0.10",
"source": {
"type": "git",
"url": "https://github.com/chevere/var-dump.git",
- "reference": "1d4e2e0e31d1d384f5b4dd71b5962cddf55b43fa"
+ "reference": "5c865b3435e6dbf88deb124b169301ec353d3fff"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/chevere/var-dump/zipball/1d4e2e0e31d1d384f5b4dd71b5962cddf55b43fa",
- "reference": "1d4e2e0e31d1d384f5b4dd71b5962cddf55b43fa",
+ "url": "https://api.github.com/repos/chevere/var-dump/zipball/5c865b3435e6dbf88deb124b169301ec353d3fff",
+ "reference": "5c865b3435e6dbf88deb124b169301ec353d3fff",
"shasum": ""
},
"require": {
+ "chevere/data-structure": "^1.1",
"chevere/message": "^1.0.0",
- "chevere/parameter": "^1.0|^1.1|^1.2",
"chevere/writer": "^1.0.3",
"kevinlebrun/colors.php": "^1.0",
"php": "^8.1"
@@ -896,28 +979,28 @@
],
"support": {
"issues": "https://github.com/chevere/var-dump/issues",
- "source": "https://github.com/chevere/var-dump/tree/2.0.7"
+ "source": "https://github.com/chevere/var-dump/tree/2.0.10"
},
- "time": "2025-11-19T20:06:40+00:00"
+ "time": "2026-03-06T14:41:19+00:00"
},
{
"name": "chevere/var-support",
- "version": "1.0.1",
+ "version": "1.0.2",
"source": {
"type": "git",
"url": "https://github.com/chevere/var-support.git",
- "reference": "f5f9f9b45d1c9815b7041506683cec3e1dff087a"
+ "reference": "cf109264801a9985f195f1176ef84ff91cc621df"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/chevere/var-support/zipball/f5f9f9b45d1c9815b7041506683cec3e1dff087a",
- "reference": "f5f9f9b45d1c9815b7041506683cec3e1dff087a",
+ "url": "https://api.github.com/repos/chevere/var-support/zipball/cf109264801a9985f195f1176ef84ff91cc621df",
+ "reference": "cf109264801a9985f195f1176ef84ff91cc621df",
"shasum": ""
},
"require": {
"chevere/message": "^1.0.0",
"php": "^8.1",
- "symfony/var-exporter": "^6.1|^7.0"
+ "symfony/var-exporter": "^6.1|^7.0|^8.0"
},
"require-dev": {
"phpstan/phpstan": "^1.9",
@@ -948,9 +1031,9 @@
"homepage": "https://chevere.org",
"support": {
"issues": "https://github.com/chevere/var-support/issues",
- "source": "https://github.com/chevere/var-support/tree/1.0.1"
+ "source": "https://github.com/chevere/var-support/tree/1.0.2"
},
- "time": "2024-01-08T13:57:44+00:00"
+ "time": "2026-03-18T16:34:00+00:00"
},
{
"name": "chevere/writer",
@@ -1082,16 +1165,16 @@
},
{
"name": "chillerlan/php-settings-container",
- "version": "3.2.1",
+ "version": "3.3.0",
"source": {
"type": "git",
"url": "https://github.com/chillerlan/php-settings-container.git",
- "reference": "95ed3e9676a1d47cab2e3174d19b43f5dbf52681"
+ "reference": "a0a487cbf5344f721eb504bf0f59bada40c381b7"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/chillerlan/php-settings-container/zipball/95ed3e9676a1d47cab2e3174d19b43f5dbf52681",
- "reference": "95ed3e9676a1d47cab2e3174d19b43f5dbf52681",
+ "url": "https://api.github.com/repos/chillerlan/php-settings-container/zipball/a0a487cbf5344f721eb504bf0f59bada40c381b7",
+ "reference": "a0a487cbf5344f721eb504bf0f59bada40c381b7",
"shasum": ""
},
"require": {
@@ -1099,11 +1182,13 @@
"php": "^8.1"
},
"require-dev": {
+ "phan/phan": "^5.5.2",
"phpmd/phpmd": "^2.15",
- "phpstan/phpstan": "^1.11",
- "phpstan/phpstan-deprecation-rules": "^1.2",
+ "phpstan/phpstan": "^2.1.31",
+ "phpstan/phpstan-deprecation-rules": "^2.0.3",
"phpunit/phpunit": "^10.5",
- "squizlabs/php_codesniffer": "^3.10"
+ "slevomat/coding-standard": "^8.22",
+ "squizlabs/php_codesniffer": "^4.0"
},
"type": "library",
"autoload": {
@@ -1128,7 +1213,8 @@
"Settings",
"configuration",
"container",
- "helper"
+ "helper",
+ "property hook"
],
"support": {
"issues": "https://github.com/chillerlan/php-settings-container/issues",
@@ -1144,20 +1230,20 @@
"type": "ko_fi"
}
],
- "time": "2024-07-16T11:13:48+00:00"
+ "time": "2026-03-20T21:10:52+00:00"
},
{
"name": "composer/ca-bundle",
- "version": "1.5.10",
+ "version": "1.5.11",
"source": {
"type": "git",
"url": "https://github.com/composer/ca-bundle.git",
- "reference": "961a5e4056dd2e4a2eedcac7576075947c28bf63"
+ "reference": "68ff39175e8e94a4bb1d259407ce51a6a60f09e6"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/composer/ca-bundle/zipball/961a5e4056dd2e4a2eedcac7576075947c28bf63",
- "reference": "961a5e4056dd2e4a2eedcac7576075947c28bf63",
+ "url": "https://api.github.com/repos/composer/ca-bundle/zipball/68ff39175e8e94a4bb1d259407ce51a6a60f09e6",
+ "reference": "68ff39175e8e94a4bb1d259407ce51a6a60f09e6",
"shasum": ""
},
"require": {
@@ -1204,7 +1290,7 @@
"support": {
"irc": "irc://irc.freenode.org/composer",
"issues": "https://github.com/composer/ca-bundle/issues",
- "source": "https://github.com/composer/ca-bundle/tree/1.5.10"
+ "source": "https://github.com/composer/ca-bundle/tree/1.5.11"
},
"funding": [
{
@@ -1216,7 +1302,84 @@
"type": "github"
}
],
- "time": "2025-12-08T15:06:51+00:00"
+ "time": "2026-03-30T09:16:10+00:00"
+ },
+ {
+ "name": "doctrine/lexer",
+ "version": "3.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/lexer.git",
+ "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/lexer/zipball/31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd",
+ "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^8.1"
+ },
+ "require-dev": {
+ "doctrine/coding-standard": "^12",
+ "phpstan/phpstan": "^1.10",
+ "phpunit/phpunit": "^10.5",
+ "psalm/plugin-phpunit": "^0.18.3",
+ "vimeo/psalm": "^5.21"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Doctrine\\Common\\Lexer\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Guilherme Blanco",
+ "email": "guilhermeblanco@gmail.com"
+ },
+ {
+ "name": "Roman Borschel",
+ "email": "roman@code-factory.org"
+ },
+ {
+ "name": "Johannes Schmitt",
+ "email": "schmittjoh@gmail.com"
+ }
+ ],
+ "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.",
+ "homepage": "https://www.doctrine-project.org/projects/lexer.html",
+ "keywords": [
+ "annotations",
+ "docblock",
+ "lexer",
+ "parser",
+ "php"
+ ],
+ "support": {
+ "issues": "https://github.com/doctrine/lexer/issues",
+ "source": "https://github.com/doctrine/lexer/tree/3.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://www.doctrine-project.org/sponsorship.html",
+ "type": "custom"
+ },
+ {
+ "url": "https://www.patreon.com/phpdoctrine",
+ "type": "patreon"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-02-05T11:56:58+00:00"
},
{
"name": "donatj/phpuseragentparser",
@@ -1292,6 +1455,73 @@
],
"time": "2025-09-10T21:58:40+00:00"
},
+ {
+ "name": "egulias/email-validator",
+ "version": "4.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/egulias/EmailValidator.git",
+ "reference": "d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa",
+ "reference": "d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa",
+ "shasum": ""
+ },
+ "require": {
+ "doctrine/lexer": "^2.0 || ^3.0",
+ "php": ">=8.1",
+ "symfony/polyfill-intl-idn": "^1.26"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.2",
+ "vimeo/psalm": "^5.12"
+ },
+ "suggest": {
+ "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Egulias\\EmailValidator\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Eduardo Gulias Davis"
+ }
+ ],
+ "description": "A library for validating emails against several RFCs",
+ "homepage": "https://github.com/egulias/EmailValidator",
+ "keywords": [
+ "email",
+ "emailvalidation",
+ "emailvalidator",
+ "validation",
+ "validator"
+ ],
+ "support": {
+ "issues": "https://github.com/egulias/EmailValidator/issues",
+ "source": "https://github.com/egulias/EmailValidator/tree/4.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/egulias",
+ "type": "github"
+ }
+ ],
+ "time": "2025-03-06T22:45:56+00:00"
+ },
{
"name": "evenement/evenement",
"version": "v3.0.2",
@@ -1341,16 +1571,16 @@
},
{
"name": "firebase/php-jwt",
- "version": "v6.11.1",
+ "version": "v7.0.5",
"source": {
"type": "git",
"url": "https://github.com/firebase/php-jwt.git",
- "reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66"
+ "reference": "47ad26bab5e7c70ae8a6f08ed25ff83631121380"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/firebase/php-jwt/zipball/d1e91ecf8c598d073d0995afa8cd5c75c6e19e66",
- "reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66",
+ "url": "https://api.github.com/repos/firebase/php-jwt/zipball/47ad26bab5e7c70ae8a6f08ed25ff83631121380",
+ "reference": "47ad26bab5e7c70ae8a6f08ed25ff83631121380",
"shasum": ""
},
"require": {
@@ -1358,6 +1588,7 @@
},
"require-dev": {
"guzzlehttp/guzzle": "^7.4",
+ "phpfastcache/phpfastcache": "^9.2",
"phpspec/prophecy-phpunit": "^2.0",
"phpunit/phpunit": "^9.5",
"psr/cache": "^2.0||^3.0",
@@ -1398,9 +1629,9 @@
],
"support": {
"issues": "https://github.com/firebase/php-jwt/issues",
- "source": "https://github.com/firebase/php-jwt/tree/v6.11.1"
+ "source": "https://github.com/firebase/php-jwt/tree/v7.0.5"
},
- "time": "2025-04-09T20:32:01+00:00"
+ "time": "2026-04-01T20:38:03+00:00"
},
{
"name": "fylax/forceutf8",
@@ -1668,16 +1899,16 @@
},
{
"name": "guzzlehttp/psr7",
- "version": "2.8.0",
+ "version": "2.9.0",
"source": {
"type": "git",
"url": "https://github.com/guzzle/psr7.git",
- "reference": "21dc724a0583619cd1652f673303492272778051"
+ "reference": "7d0ed42f28e42d61352a7a79de682e5e67fec884"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/guzzle/psr7/zipball/21dc724a0583619cd1652f673303492272778051",
- "reference": "21dc724a0583619cd1652f673303492272778051",
+ "url": "https://api.github.com/repos/guzzle/psr7/zipball/7d0ed42f28e42d61352a7a79de682e5e67fec884",
+ "reference": "7d0ed42f28e42d61352a7a79de682e5e67fec884",
"shasum": ""
},
"require": {
@@ -1693,6 +1924,7 @@
"require-dev": {
"bamarni/composer-bin-plugin": "^1.8.2",
"http-interop/http-factory-tests": "0.9.0",
+ "jshttp/mime-db": "1.54.0.1",
"phpunit/phpunit": "^8.5.44 || ^9.6.25"
},
"suggest": {
@@ -1764,7 +1996,7 @@
],
"support": {
"issues": "https://github.com/guzzle/psr7/issues",
- "source": "https://github.com/guzzle/psr7/tree/2.8.0"
+ "source": "https://github.com/guzzle/psr7/tree/2.9.0"
},
"funding": [
{
@@ -1780,7 +2012,7 @@
"type": "tidelift"
}
],
- "time": "2025-08-23T21:21:41+00:00"
+ "time": "2026-03-10T16:41:02+00:00"
},
{
"name": "intervention/image",
@@ -2045,33 +2277,32 @@
},
{
"name": "lychee-org/php-exif",
- "version": "v1.0.2",
+ "version": "1.3.0",
"source": {
"type": "git",
"url": "https://github.com/LycheeOrg/php-exif.git",
- "reference": "12c4976d2dea44fc4eb3b9dd33428b95ba7461c0"
+ "reference": "4e4fad2f2617c94e35df26a099d9b04a49cdc4b4"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/LycheeOrg/php-exif/zipball/12c4976d2dea44fc4eb3b9dd33428b95ba7461c0",
- "reference": "12c4976d2dea44fc4eb3b9dd33428b95ba7461c0",
+ "url": "https://api.github.com/repos/LycheeOrg/php-exif/zipball/4e4fad2f2617c94e35df26a099d9b04a49cdc4b4",
+ "reference": "4e4fad2f2617c94e35df26a099d9b04a49cdc4b4",
"shasum": ""
},
"require": {
"ext-fileinfo": "*",
- "fylax/forceutf8": "^3.0.1",
- "php": "^8.1",
- "php-ffmpeg/php-ffmpeg": "^1.0",
- "thecodingmachine/safe": "^2.2"
+ "fylax/forceutf8": "^3.0.3",
+ "php": "^8.2 || ^8.3 || ^8.4 || ^8.5",
+ "php-ffmpeg/php-ffmpeg": "^1.4",
+ "thecodingmachine/safe": "^2.0 || ^3.0"
},
"require-dev": {
- "friendsofphp/php-cs-fixer": "^3.3",
- "infection/infection": "^0.26.13",
- "lychee-org/phpstan-lychee": "^1.0.1",
- "php-parallel-lint/php-parallel-lint": "^1.2",
- "phpmd/phpmd": "^2.9",
+ "friendsofphp/php-cs-fixer": "^3.51",
+ "lychee-org/phpstan-lychee": "^2.0.1|^1.0.0",
+ "php-parallel-lint/php-parallel-lint": "^1.3.2",
+ "phpmd/phpmd": "^2.15",
"phpunit/phpunit": "^9.5.10",
- "squizlabs/php_codesniffer": "^3.5",
+ "squizlabs/php_codesniffer": "^3.9",
"thecodingmachine/phpstan-safe-rule": "^1.2"
},
"suggest": {
@@ -2092,10 +2323,15 @@
"MIT"
],
"authors": [
+ {
+ "name": "LycheeOrg",
+ "homepage": "https://lycheeorg.dev/",
+ "role": "Maintainers"
+ },
{
"name": "Tom Van Herreweghe",
"homepage": "http://theanalogguy.be",
- "role": "Developer"
+ "role": "Creator"
}
],
"description": "Object-Oriented EXIF parsing",
@@ -2112,45 +2348,45 @@
],
"support": {
"issues": "https://github.com/LycheeOrg/php-exif/issues",
- "source": "https://github.com/LycheeOrg/php-exif/tree/v1.0.2"
+ "source": "https://github.com/LycheeOrg/php-exif/tree/1.3.0"
},
- "time": "2023-04-11T11:03:27+00:00"
+ "time": "2026-01-31T14:00:30+00:00"
},
{
"name": "matthiasmullie/scrapbook",
- "version": "1.4.9",
+ "version": "1.5.4",
"source": {
"type": "git",
"url": "https://github.com/matthiasmullie/scrapbook.git",
- "reference": "da4178c1882136a8931ffd791df2b84a5aa74219"
+ "reference": "6ca64d54d7106deffbb98cb9c6a6f5fdb13ce1f1"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/matthiasmullie/scrapbook/zipball/da4178c1882136a8931ffd791df2b84a5aa74219",
- "reference": "da4178c1882136a8931ffd791df2b84a5aa74219",
+ "url": "https://api.github.com/repos/matthiasmullie/scrapbook/zipball/6ca64d54d7106deffbb98cb9c6a6f5fdb13ce1f1",
+ "reference": "6ca64d54d7106deffbb98cb9c6a6f5fdb13ce1f1",
"shasum": ""
},
"require": {
- "php": ">=5.3.0",
- "psr/cache": "^1.0||~2.0",
- "psr/simple-cache": "^1.0||~2.0"
+ "php": ">=8.0.0",
+ "psr/cache": "^2.0||^3.0",
+ "psr/simple-cache": "^2.0||^3.0"
},
"provide": {
- "psr/cache-implementation": "^1.0||~2.0",
- "psr/simple-cache-implementation": "^1.0||~2.0"
+ "psr/cache-implementation": "^1.0||^2.0||^3.0",
+ "psr/simple-cache-implementation": "^1.0||^2.0||^3.0"
},
"require-dev": {
"ext-pcntl": "*",
- "friendsofphp/php-cs-fixer": ">=2.0",
- "phpunit/phpunit": ">=4.8"
+ "friendsofphp/php-cs-fixer": ">=3.0",
+ "phpunit/phpunit": ">=10.0"
},
"suggest": {
- "couchbase/couchbase": ">=2.0",
- "ext-apc": ">=3.1.1",
- "ext-couchbase": ">=2.0.0",
+ "couchbase/couchbase": ">=3.0",
+ "ext-apcu": ">=4.0.0",
+ "ext-couchbase": ">=3.0.0",
"ext-memcached": ">=2.0.0",
"ext-pdo": ">=0.1.0",
- "ext-redis": ">=2.2.0 || 0.0.0.0",
+ "ext-redis": ">=2.2.0||0.0.0.0",
"league/flysystem": ">=1.0"
},
"type": "library",
@@ -2171,7 +2407,7 @@
"role": "Developer"
}
],
- "description": "Scrapbook is a PHP cache library, with adapters for e.g. Memcached, Redis, Couchbase, APC(u), SQL and additional capabilities (e.g. transactions, stampede protection) built on top.",
+ "description": "Scrapbook is a PHP cache library, with adapters for e.g. Memcached, Redis, Couchbase, APCu, SQL and additional capabilities (e.g. transactions, stampede protection) built on top.",
"homepage": "https://scrapbook.cash",
"keywords": [
"Buffer",
@@ -2205,7 +2441,7 @@
],
"support": {
"issues": "https://github.com/matthiasmullie/scrapbook/issues",
- "source": "https://github.com/matthiasmullie/scrapbook/tree/1.4.9"
+ "source": "https://github.com/matthiasmullie/scrapbook/tree/1.5.4"
},
"funding": [
{
@@ -2213,7 +2449,7 @@
"type": "github"
}
],
- "time": "2022-11-10T09:28:49+00:00"
+ "time": "2024-12-20T11:47:12+00:00"
},
{
"name": "mlocati/ip-lib",
@@ -2761,25 +2997,25 @@
},
{
"name": "php-ffmpeg/php-ffmpeg",
- "version": "v1.3.2",
+ "version": "v1.4.0",
"source": {
"type": "git",
"url": "https://github.com/PHP-FFMpeg/PHP-FFMpeg.git",
- "reference": "8e74bdc07ad200da7a6cfb21ec2652875e4368e0"
+ "reference": "41fa00949ea2758cd34f076a8030df7198d57759"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/PHP-FFMpeg/PHP-FFMpeg/zipball/8e74bdc07ad200da7a6cfb21ec2652875e4368e0",
- "reference": "8e74bdc07ad200da7a6cfb21ec2652875e4368e0",
+ "url": "https://api.github.com/repos/PHP-FFMpeg/PHP-FFMpeg/zipball/41fa00949ea2758cd34f076a8030df7198d57759",
+ "reference": "41fa00949ea2758cd34f076a8030df7198d57759",
"shasum": ""
},
"require": {
"evenement/evenement": "^3.0",
- "php": "^8.0 || ^8.1 || ^8.2 || ^8.3 || ^8.4",
+ "php": "^8.0 || ^8.1 || ^8.2 || ^8.3 || ^8.4 || ^8.5",
"psr/log": "^1.0 || ^2.0 || ^3.0",
"spatie/temporary-directory": "^2.0",
- "symfony/cache": "^5.4 || ^6.0 || ^7.0",
- "symfony/process": "^5.4 || ^6.0 || ^7.0"
+ "symfony/cache": "^5.4 || ^6.0 || ^7.0 || ^8.0",
+ "symfony/process": "^5.4 || ^6.0 || ^7.0 || ^8.0"
},
"require-dev": {
"mockery/mockery": "^1.5",
@@ -2844,90 +3080,9 @@
],
"support": {
"issues": "https://github.com/PHP-FFMpeg/PHP-FFMpeg/issues",
- "source": "https://github.com/PHP-FFMpeg/PHP-FFMpeg/tree/v1.3.2"
+ "source": "https://github.com/PHP-FFMpeg/PHP-FFMpeg/tree/v1.4.0"
},
- "time": "2025-04-01T20:36:46+00:00"
- },
- {
- "name": "phpmailer/phpmailer",
- "version": "v6.12.0",
- "source": {
- "type": "git",
- "url": "https://github.com/PHPMailer/PHPMailer.git",
- "reference": "d1ac35d784bf9f5e61b424901d5a014967f15b12"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/d1ac35d784bf9f5e61b424901d5a014967f15b12",
- "reference": "d1ac35d784bf9f5e61b424901d5a014967f15b12",
- "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.12.0"
- },
- "funding": [
- {
- "url": "https://github.com/Synchro",
- "type": "github"
- }
- ],
- "time": "2025-10-15T16:49:08+00:00"
+ "time": "2026-01-19T21:15:14+00:00"
},
{
"name": "phpseclib/bcmath_compat",
@@ -2993,16 +3148,16 @@
},
{
"name": "phpseclib/phpseclib",
- "version": "3.0.48",
+ "version": "3.0.50",
"source": {
"type": "git",
"url": "https://github.com/phpseclib/phpseclib.git",
- "reference": "64065a5679c50acb886e82c07aa139b0f757bb89"
+ "reference": "aa6ad8321ed103dc3624fb600a25b66ebf78ec7b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/64065a5679c50acb886e82c07aa139b0f757bb89",
- "reference": "64065a5679c50acb886e82c07aa139b0f757bb89",
+ "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/aa6ad8321ed103dc3624fb600a25b66ebf78ec7b",
+ "reference": "aa6ad8321ed103dc3624fb600a25b66ebf78ec7b",
"shasum": ""
},
"require": {
@@ -3083,7 +3238,7 @@
],
"support": {
"issues": "https://github.com/phpseclib/phpseclib/issues",
- "source": "https://github.com/phpseclib/phpseclib/tree/3.0.48"
+ "source": "https://github.com/phpseclib/phpseclib/tree/3.0.50"
},
"funding": [
{
@@ -3099,7 +3254,7 @@
"type": "tidelift"
}
],
- "time": "2025-12-15T11:51:42+00:00"
+ "time": "2026-03-19T02:57:58+00:00"
},
{
"name": "pragmarx/google2fa",
@@ -3222,20 +3377,20 @@
},
{
"name": "psr/cache",
- "version": "1.0.1",
+ "version": "3.0.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/cache.git",
- "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8"
+ "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8",
- "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8",
+ "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf",
+ "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf",
"shasum": ""
},
"require": {
- "php": ">=5.3.0"
+ "php": ">=8.0.0"
},
"type": "library",
"extra": {
@@ -3255,7 +3410,7 @@
"authors": [
{
"name": "PHP-FIG",
- "homepage": "http://www.php-fig.org/"
+ "homepage": "https://www.php-fig.org/"
}
],
"description": "Common interface for caching libraries",
@@ -3265,9 +3420,57 @@
"psr-6"
],
"support": {
- "source": "https://github.com/php-fig/cache/tree/master"
+ "source": "https://github.com/php-fig/cache/tree/3.0.0"
},
- "time": "2016-08-06T20:24:11+00:00"
+ "time": "2021-02-03T23:26:27+00:00"
+ },
+ {
+ "name": "psr/clock",
+ "version": "1.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/clock.git",
+ "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d",
+ "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.0 || ^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Psr\\Clock\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for reading the clock.",
+ "homepage": "https://github.com/php-fig/clock",
+ "keywords": [
+ "clock",
+ "now",
+ "psr",
+ "psr-20",
+ "time"
+ ],
+ "support": {
+ "issues": "https://github.com/php-fig/clock/issues",
+ "source": "https://github.com/php-fig/clock/tree/1.0.0"
+ },
+ "time": "2022-11-25T14:36:26+00:00"
},
{
"name": "psr/container",
@@ -3322,6 +3525,56 @@
},
"time": "2021-11-05T16:47:00+00:00"
},
+ {
+ "name": "psr/event-dispatcher",
+ "version": "1.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/event-dispatcher.git",
+ "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0",
+ "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\EventDispatcher\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "description": "Standard interfaces for event handling.",
+ "keywords": [
+ "events",
+ "psr",
+ "psr-14"
+ ],
+ "support": {
+ "issues": "https://github.com/php-fig/event-dispatcher/issues",
+ "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0"
+ },
+ "time": "2019-01-08T18:20:26+00:00"
+ },
{
"name": "psr/http-client",
"version": "1.0.3",
@@ -3647,16 +3900,16 @@
},
{
"name": "psr/simple-cache",
- "version": "2.0.0",
+ "version": "3.0.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/simple-cache.git",
- "reference": "8707bf3cea6f710bf6ef05491234e3ab06f6432a"
+ "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/8707bf3cea6f710bf6ef05491234e3ab06f6432a",
- "reference": "8707bf3cea6f710bf6ef05491234e3ab06f6432a",
+ "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865",
+ "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865",
"shasum": ""
},
"require": {
@@ -3665,7 +3918,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "2.0.x-dev"
+ "dev-master": "3.0.x-dev"
}
},
"autoload": {
@@ -3692,22 +3945,22 @@
"simple-cache"
],
"support": {
- "source": "https://github.com/php-fig/simple-cache/tree/2.0.0"
+ "source": "https://github.com/php-fig/simple-cache/tree/3.0.0"
},
- "time": "2021-10-29T13:22:09+00:00"
+ "time": "2021-10-29T13:26:27+00:00"
},
{
"name": "psy/psysh",
- "version": "v0.11.22",
+ "version": "v0.11.23",
"source": {
"type": "git",
"url": "https://github.com/bobthecow/psysh.git",
- "reference": "128fa1b608be651999ed9789c95e6e2a31b5802b"
+ "reference": "e9ac4fae08b0bc7fb4f8391b220daa1dc55f03bf"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/bobthecow/psysh/zipball/128fa1b608be651999ed9789c95e6e2a31b5802b",
- "reference": "128fa1b608be651999ed9789c95e6e2a31b5802b",
+ "url": "https://api.github.com/repos/bobthecow/psysh/zipball/e9ac4fae08b0bc7fb4f8391b220daa1dc55f03bf",
+ "reference": "e9ac4fae08b0bc7fb4f8391b220daa1dc55f03bf",
"shasum": ""
},
"require": {
@@ -3772,9 +4025,9 @@
],
"support": {
"issues": "https://github.com/bobthecow/psysh/issues",
- "source": "https://github.com/bobthecow/psysh/tree/v0.11.22"
+ "source": "https://github.com/bobthecow/psysh/tree/v0.11.23"
},
- "time": "2023-10-14T21:56:36+00:00"
+ "time": "2026-01-30T05:01:10+00:00"
},
{
"name": "ralouphie/getallheaders",
@@ -3883,16 +4136,16 @@
},
{
"name": "spatie/temporary-directory",
- "version": "2.3.0",
+ "version": "2.3.1",
"source": {
"type": "git",
"url": "https://github.com/spatie/temporary-directory.git",
- "reference": "580eddfe9a0a41a902cac6eeb8f066b42e65a32b"
+ "reference": "662e481d6ec07ef29fd05010433428851a42cd07"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/spatie/temporary-directory/zipball/580eddfe9a0a41a902cac6eeb8f066b42e65a32b",
- "reference": "580eddfe9a0a41a902cac6eeb8f066b42e65a32b",
+ "url": "https://api.github.com/repos/spatie/temporary-directory/zipball/662e481d6ec07ef29fd05010433428851a42cd07",
+ "reference": "662e481d6ec07ef29fd05010433428851a42cd07",
"shasum": ""
},
"require": {
@@ -3928,7 +4181,7 @@
],
"support": {
"issues": "https://github.com/spatie/temporary-directory/issues",
- "source": "https://github.com/spatie/temporary-directory/tree/2.3.0"
+ "source": "https://github.com/spatie/temporary-directory/tree/2.3.1"
},
"funding": [
{
@@ -3940,62 +4193,348 @@
"type": "github"
}
],
- "time": "2025-01-13T13:04:43+00:00"
+ "time": "2026-01-12T07:42:22+00:00"
},
{
- "name": "symfony/cache",
- "version": "v5.4.46",
+ "name": "symfony/aha-send-mailer",
+ "version": "v7.4.4",
"source": {
"type": "git",
- "url": "https://github.com/symfony/cache.git",
- "reference": "0fe08ee32cec2748fbfea10c52d3ee02049e0f6b"
+ "url": "https://github.com/symfony/aha-send-mailer.git",
+ "reference": "dd146909b3e32663a874b8c7c2fcc86145b04ee9"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/cache/zipball/0fe08ee32cec2748fbfea10c52d3ee02049e0f6b",
- "reference": "0fe08ee32cec2748fbfea10c52d3ee02049e0f6b",
+ "url": "https://api.github.com/repos/symfony/aha-send-mailer/zipball/dd146909b3e32663a874b8c7c2fcc86145b04ee9",
+ "reference": "dd146909b3e32663a874b8c7c2fcc86145b04ee9",
"shasum": ""
},
"require": {
- "php": ">=7.2.5",
- "psr/cache": "^1.0|^2.0",
- "psr/log": "^1.1|^2|^3",
- "symfony/cache-contracts": "^1.1.7|^2",
- "symfony/deprecation-contracts": "^2.1|^3",
- "symfony/polyfill-php73": "^1.9",
- "symfony/polyfill-php80": "^1.16",
- "symfony/service-contracts": "^1.1|^2|^3",
- "symfony/var-exporter": "^4.4|^5.0|^6.0"
+ "php": ">=8.2",
+ "psr/event-dispatcher": "^1",
+ "symfony/mailer": "^7.3|^8.0"
+ },
+ "require-dev": {
+ "symfony/http-client": "^6.4|^7.0|^8.0",
+ "symfony/webhook": "^6.4|^7.0|^8.0"
+ },
+ "type": "symfony-mailer-bridge",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Mailer\\Bridge\\AhaSend\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Farhad Hedayatifard",
+ "email": "farhad@ahasend.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony AhaSend Mailer Bridge",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/aha-send-mailer/tree/v7.4.4"
+ },
+ "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": "2026-01-08T08:25:11+00:00"
+ },
+ {
+ "name": "symfony/amazon-mailer",
+ "version": "v7.4.6",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/amazon-mailer.git",
+ "reference": "6fedfa970a1b5b2c93fd32c598df7db7d03070b4"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/amazon-mailer/zipball/6fedfa970a1b5b2c93fd32c598df7db7d03070b4",
+ "reference": "6fedfa970a1b5b2c93fd32c598df7db7d03070b4",
+ "shasum": ""
+ },
+ "require": {
+ "async-aws/ses": "^1.8",
+ "php": ">=8.2",
+ "symfony/mailer": "^7.2|^8.0"
+ },
+ "require-dev": {
+ "symfony/http-client": "^6.4|^7.0|^8.0"
+ },
+ "type": "symfony-mailer-bridge",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Mailer\\Bridge\\Amazon\\": ""
+ },
+ "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": "Symfony Amazon Mailer Bridge",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/amazon-mailer/tree/v7.4.6"
+ },
+ "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": "2026-02-11T15:05:50+00:00"
+ },
+ {
+ "name": "symfony/azure-mailer",
+ "version": "v7.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/azure-mailer.git",
+ "reference": "c9a3e6ce0ca35798a9fc8ae62dd7d00fa9d9399b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/azure-mailer/zipball/c9a3e6ce0ca35798a9fc8ae62dd7d00fa9d9399b",
+ "reference": "c9a3e6ce0ca35798a9fc8ae62dd7d00fa9d9399b",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/mailer": "^7.2|^8.0"
+ },
+ "require-dev": {
+ "symfony/http-client": "^6.4|^7.0|^8.0"
+ },
+ "type": "symfony-mailer-bridge",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Mailer\\Bridge\\Azure\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Rafael Villa Verde",
+ "homepage": "https://github.com/hafael"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony Microsoft Azure Mailer Bridge",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/azure-mailer/tree/v7.4.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-08-04T07:05:15+00:00"
+ },
+ {
+ "name": "symfony/brevo-mailer",
+ "version": "v7.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/brevo-mailer.git",
+ "reference": "f1ef26ffe147e9185531030fc1503ed7b63fa0ef"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/brevo-mailer/zipball/f1ef26ffe147e9185531030fc1503ed7b63fa0ef",
+ "reference": "f1ef26ffe147e9185531030fc1503ed7b63fa0ef",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/mailer": "^7.2|^8.0"
},
"conflict": {
- "doctrine/dbal": "<2.13.1",
- "symfony/dependency-injection": "<4.4",
- "symfony/http-kernel": "<4.4",
- "symfony/var-dumper": "<4.4"
+ "symfony/mime": "<6.2"
+ },
+ "require-dev": {
+ "symfony/http-client": "^6.3|^7.0|^8.0",
+ "symfony/webhook": "^6.3|^7.0|^8.0"
+ },
+ "type": "symfony-mailer-bridge",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Mailer\\Bridge\\Brevo\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Pierre Tanguy",
+ "homepage": "https://github.com/petanguy"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony Brevo Mailer Bridge",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/brevo-mailer/tree/v7.4.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-08-04T08:00:56+00:00"
+ },
+ {
+ "name": "symfony/cache",
+ "version": "v7.4.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/cache.git",
+ "reference": "467464da294734b0fb17e853e5712abc8470f819"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/cache/zipball/467464da294734b0fb17e853e5712abc8470f819",
+ "reference": "467464da294734b0fb17e853e5712abc8470f819",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "psr/cache": "^2.0|^3.0",
+ "psr/log": "^1.1|^2|^3",
+ "symfony/cache-contracts": "^3.6",
+ "symfony/deprecation-contracts": "^2.5|^3",
+ "symfony/service-contracts": "^2.5|^3",
+ "symfony/var-exporter": "^6.4|^7.0|^8.0"
+ },
+ "conflict": {
+ "doctrine/dbal": "<3.6",
+ "ext-redis": "<6.1",
+ "ext-relay": "<0.12.1",
+ "symfony/dependency-injection": "<6.4",
+ "symfony/http-kernel": "<6.4",
+ "symfony/var-dumper": "<6.4"
},
"provide": {
- "psr/cache-implementation": "1.0|2.0",
- "psr/simple-cache-implementation": "1.0|2.0",
- "symfony/cache-implementation": "1.0|2.0"
+ "psr/cache-implementation": "2.0|3.0",
+ "psr/simple-cache-implementation": "1.0|2.0|3.0",
+ "symfony/cache-implementation": "1.1|2.0|3.0"
},
"require-dev": {
"cache/integration-tests": "dev-master",
- "doctrine/cache": "^1.6|^2.0",
- "doctrine/dbal": "^2.13.1|^3|^4",
+ "doctrine/dbal": "^3.6|^4",
"predis/predis": "^1.1|^2.0",
- "psr/simple-cache": "^1.0|^2.0",
- "symfony/config": "^4.4|^5.0|^6.0",
- "symfony/dependency-injection": "^4.4|^5.0|^6.0",
- "symfony/filesystem": "^4.4|^5.0|^6.0",
- "symfony/http-kernel": "^4.4|^5.0|^6.0",
- "symfony/messenger": "^4.4|^5.0|^6.0",
- "symfony/var-dumper": "^4.4|^5.0|^6.0"
+ "psr/simple-cache": "^1.0|^2.0|^3.0",
+ "symfony/clock": "^6.4|^7.0|^8.0",
+ "symfony/config": "^6.4|^7.0|^8.0",
+ "symfony/dependency-injection": "^6.4|^7.0|^8.0",
+ "symfony/filesystem": "^6.4|^7.0|^8.0",
+ "symfony/http-kernel": "^6.4|^7.0|^8.0",
+ "symfony/messenger": "^6.4|^7.0|^8.0",
+ "symfony/var-dumper": "^6.4|^7.0|^8.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Symfony\\Component\\Cache\\": ""
},
+ "classmap": [
+ "Traits/ValueWrapper.php"
+ ],
"exclude-from-classmap": [
"/Tests/"
]
@@ -4021,7 +4560,7 @@
"psr6"
],
"support": {
- "source": "https://github.com/symfony/cache/tree/v5.4.46"
+ "source": "https://github.com/symfony/cache/tree/v7.4.8"
},
"funding": [
{
@@ -4032,33 +4571,34 @@
"url": "https://github.com/fabpot",
"type": "github"
},
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
- "time": "2024-11-04T11:43:55+00:00"
+ "time": "2026-03-30T15:15:47+00:00"
},
{
"name": "symfony/cache-contracts",
- "version": "v2.5.4",
+ "version": "v3.6.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/cache-contracts.git",
- "reference": "517c3a3619dadfa6952c4651767fcadffb4df65e"
+ "reference": "5d68a57d66910405e5c0b63d6f0af941e66fc868"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/517c3a3619dadfa6952c4651767fcadffb4df65e",
- "reference": "517c3a3619dadfa6952c4651767fcadffb4df65e",
+ "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/5d68a57d66910405e5c0b63d6f0af941e66fc868",
+ "reference": "5d68a57d66910405e5c0b63d6f0af941e66fc868",
"shasum": ""
},
"require": {
- "php": ">=7.2.5",
- "psr/cache": "^1.0|^2.0|^3.0"
- },
- "suggest": {
- "symfony/cache-implementation": ""
+ "php": ">=8.1",
+ "psr/cache": "^3.0"
},
"type": "library",
"extra": {
@@ -4067,7 +4607,7 @@
"name": "symfony/contracts"
},
"branch-alias": {
- "dev-main": "2.5-dev"
+ "dev-main": "3.6-dev"
}
},
"autoload": {
@@ -4100,7 +4640,7 @@
"standards"
],
"support": {
- "source": "https://github.com/symfony/cache-contracts/tree/v2.5.4"
+ "source": "https://github.com/symfony/cache-contracts/tree/v3.6.0"
},
"funding": [
{
@@ -4116,20 +4656,98 @@
"type": "tidelift"
}
],
- "time": "2024-09-25T14:11:13+00:00"
+ "time": "2025-03-13T15:25:07+00:00"
},
{
- "name": "symfony/console",
- "version": "v6.4.31",
+ "name": "symfony/clock",
+ "version": "v7.4.8",
"source": {
"type": "git",
- "url": "https://github.com/symfony/console.git",
- "reference": "f9f8a889f54c264f9abac3fc0f7a371ffca51997"
+ "url": "https://github.com/symfony/clock.git",
+ "reference": "674fa3b98e21531dd040e613479f5f6fa8f32111"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/console/zipball/f9f8a889f54c264f9abac3fc0f7a371ffca51997",
- "reference": "f9f8a889f54c264f9abac3fc0f7a371ffca51997",
+ "url": "https://api.github.com/repos/symfony/clock/zipball/674fa3b98e21531dd040e613479f5f6fa8f32111",
+ "reference": "674fa3b98e21531dd040e613479f5f6fa8f32111",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "psr/clock": "^1.0",
+ "symfony/polyfill-php83": "^1.28"
+ },
+ "provide": {
+ "psr/clock-implementation": "1.0"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "Resources/now.php"
+ ],
+ "psr-4": {
+ "Symfony\\Component\\Clock\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "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": "Decouples applications from the system clock",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "clock",
+ "psr20",
+ "time"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/clock/tree/v7.4.8"
+ },
+ "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": "2026-03-24T13:12:05+00:00"
+ },
+ {
+ "name": "symfony/console",
+ "version": "v6.4.36",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/console.git",
+ "reference": "9f481cfb580db8bcecc9b2d4c63f3e13df022ad5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/console/zipball/9f481cfb580db8bcecc9b2d4c63f3e13df022ad5",
+ "reference": "9f481cfb580db8bcecc9b2d4c63f3e13df022ad5",
"shasum": ""
},
"require": {
@@ -4194,7 +4812,7 @@
"terminal"
],
"support": {
- "source": "https://github.com/symfony/console/tree/v6.4.31"
+ "source": "https://github.com/symfony/console/tree/v6.4.36"
},
"funding": [
{
@@ -4214,7 +4832,7 @@
"type": "tidelift"
}
],
- "time": "2025-12-22T08:30:34+00:00"
+ "time": "2026-03-27T15:30:51+00:00"
},
{
"name": "symfony/deprecation-contracts",
@@ -4284,26 +4902,187 @@
"time": "2024-09-25T14:21:43+00:00"
},
{
- "name": "symfony/filesystem",
- "version": "v6.4.30",
+ "name": "symfony/event-dispatcher",
+ "version": "v7.4.8",
"source": {
"type": "git",
- "url": "https://github.com/symfony/filesystem.git",
- "reference": "441c6b69f7222aadae7cbf5df588496d5ee37789"
+ "url": "https://github.com/symfony/event-dispatcher.git",
+ "reference": "f57b899fa736fd71121168ef268f23c206083f0a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/filesystem/zipball/441c6b69f7222aadae7cbf5df588496d5ee37789",
- "reference": "441c6b69f7222aadae7cbf5df588496d5ee37789",
+ "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/f57b899fa736fd71121168ef268f23c206083f0a",
+ "reference": "f57b899fa736fd71121168ef268f23c206083f0a",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/event-dispatcher-contracts": "^2.5|^3"
+ },
+ "conflict": {
+ "symfony/dependency-injection": "<6.4",
+ "symfony/service-contracts": "<2.5"
+ },
+ "provide": {
+ "psr/event-dispatcher-implementation": "1.0",
+ "symfony/event-dispatcher-implementation": "2.0|3.0"
+ },
+ "require-dev": {
+ "psr/log": "^1|^2|^3",
+ "symfony/config": "^6.4|^7.0|^8.0",
+ "symfony/dependency-injection": "^6.4|^7.0|^8.0",
+ "symfony/error-handler": "^6.4|^7.0|^8.0",
+ "symfony/expression-language": "^6.4|^7.0|^8.0",
+ "symfony/framework-bundle": "^6.4|^7.0|^8.0",
+ "symfony/http-foundation": "^6.4|^7.0|^8.0",
+ "symfony/service-contracts": "^2.5|^3",
+ "symfony/stopwatch": "^6.4|^7.0|^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\EventDispatcher\\": ""
+ },
+ "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": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/event-dispatcher/tree/v7.4.8"
+ },
+ "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": "2026-03-30T13:54:39+00:00"
+ },
+ {
+ "name": "symfony/event-dispatcher-contracts",
+ "version": "v3.6.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/event-dispatcher-contracts.git",
+ "reference": "59eb412e93815df44f05f342958efa9f46b1e586"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/59eb412e93815df44f05f342958efa9f46b1e586",
+ "reference": "59eb412e93815df44f05f342958efa9f46b1e586",
"shasum": ""
},
"require": {
"php": ">=8.1",
+ "psr/event-dispatcher": "^1"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/contracts",
+ "name": "symfony/contracts"
+ },
+ "branch-alias": {
+ "dev-main": "3.6-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Contracts\\EventDispatcher\\": ""
+ }
+ },
+ "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": "Generic abstractions related to dispatching event",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "abstractions",
+ "contracts",
+ "decoupling",
+ "interfaces",
+ "interoperability",
+ "standards"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/event-dispatcher-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/filesystem",
+ "version": "v7.4.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/filesystem.git",
+ "reference": "58b9790d12f9670b7f53a1c1738febd3108970a5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/filesystem/zipball/58b9790d12f9670b7f53a1c1738febd3108970a5",
+ "reference": "58b9790d12f9670b7f53a1c1738febd3108970a5",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
"symfony/polyfill-ctype": "~1.8",
"symfony/polyfill-mbstring": "~1.8"
},
"require-dev": {
- "symfony/process": "^5.4|^6.4|^7.0"
+ "symfony/process": "^6.4|^7.0|^8.0"
},
"type": "library",
"autoload": {
@@ -4331,7 +5110,7 @@
"description": "Provides basic utilities for the filesystem",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/filesystem/tree/v6.4.30"
+ "source": "https://github.com/symfony/filesystem/tree/v7.4.8"
},
"funding": [
{
@@ -4351,7 +5130,1027 @@
"type": "tidelift"
}
],
- "time": "2025-11-26T14:43:45+00:00"
+ "time": "2026-03-24T13:12:05+00:00"
+ },
+ {
+ "name": "symfony/http-client",
+ "version": "v7.4.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/http-client.git",
+ "reference": "01933e626c3de76bea1e22641e205e78f6a34342"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/http-client/zipball/01933e626c3de76bea1e22641e205e78f6a34342",
+ "reference": "01933e626c3de76bea1e22641e205e78f6a34342",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "psr/log": "^1|^2|^3",
+ "symfony/deprecation-contracts": "^2.5|^3",
+ "symfony/http-client-contracts": "~3.4.4|^3.5.2",
+ "symfony/polyfill-php83": "^1.29",
+ "symfony/service-contracts": "^2.5|^3"
+ },
+ "conflict": {
+ "amphp/amp": "<2.5",
+ "amphp/socket": "<1.1",
+ "php-http/discovery": "<1.15",
+ "symfony/http-foundation": "<6.4"
+ },
+ "provide": {
+ "php-http/async-client-implementation": "*",
+ "php-http/client-implementation": "*",
+ "psr/http-client-implementation": "1.0",
+ "symfony/http-client-implementation": "3.0"
+ },
+ "require-dev": {
+ "amphp/http-client": "^4.2.1|^5.0",
+ "amphp/http-tunnel": "^1.0|^2.0",
+ "guzzlehttp/promises": "^1.4|^2.0",
+ "nyholm/psr7": "^1.0",
+ "php-http/httplug": "^1.0|^2.0",
+ "psr/http-client": "^1.0",
+ "symfony/amphp-http-client-meta": "^1.0|^2.0",
+ "symfony/cache": "^6.4|^7.0|^8.0",
+ "symfony/dependency-injection": "^6.4|^7.0|^8.0",
+ "symfony/http-kernel": "^6.4|^7.0|^8.0",
+ "symfony/messenger": "^6.4|^7.0|^8.0",
+ "symfony/process": "^6.4|^7.0|^8.0",
+ "symfony/rate-limiter": "^6.4|^7.0|^8.0",
+ "symfony/stopwatch": "^6.4|^7.0|^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\HttpClient\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "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": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "http"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/http-client/tree/v7.4.8"
+ },
+ "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": "2026-03-30T12:55:43+00:00"
+ },
+ {
+ "name": "symfony/http-client-contracts",
+ "version": "v3.6.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/http-client-contracts.git",
+ "reference": "75d7043853a42837e68111812f4d964b01e5101c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/75d7043853a42837e68111812f4d964b01e5101c",
+ "reference": "75d7043853a42837e68111812f4d964b01e5101c",
+ "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": {
+ "psr-4": {
+ "Symfony\\Contracts\\HttpClient\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Test/"
+ ]
+ },
+ "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": "Generic abstractions related to HTTP clients",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "abstractions",
+ "contracts",
+ "decoupling",
+ "interfaces",
+ "interoperability",
+ "standards"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/http-client-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": "2025-04-29T11:18:49+00:00"
+ },
+ {
+ "name": "symfony/infobip-mailer",
+ "version": "v7.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/infobip-mailer.git",
+ "reference": "eb1253a830f35bb642a52084bbe87cfbde95261f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/infobip-mailer/zipball/eb1253a830f35bb642a52084bbe87cfbde95261f",
+ "reference": "eb1253a830f35bb642a52084bbe87cfbde95261f",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/mailer": "^7.2|^8.0",
+ "symfony/mime": "^6.4|^7.0|^8.0"
+ },
+ "conflict": {
+ "symfony/mime": "<6.4"
+ },
+ "require-dev": {
+ "symfony/http-client": "^6.4|^7.0|^8.0"
+ },
+ "type": "symfony-mailer-bridge",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Mailer\\Bridge\\Infobip\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jean-Baptiste Delhommeau",
+ "email": "jeanbadel@gmail.com"
+ },
+ {
+ "name": "Benoit Galati",
+ "email": "benoit.galati@gmail.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony Infobip Mailer Bridge",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/infobip-mailer/tree/v7.4.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-08-09T22:28:14+00:00"
+ },
+ {
+ "name": "symfony/mail-pace-mailer",
+ "version": "v7.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/mail-pace-mailer.git",
+ "reference": "f37364729794808f644ec03fc84335dce9fd3573"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/mail-pace-mailer/zipball/f37364729794808f644ec03fc84335dce9fd3573",
+ "reference": "f37364729794808f644ec03fc84335dce9fd3573",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "psr/event-dispatcher": "^1",
+ "symfony/mailer": "^7.2|^8.0"
+ },
+ "require-dev": {
+ "symfony/http-client": "^6.4|^7.0|^8.0"
+ },
+ "type": "symfony-mailer-bridge",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Mailer\\Bridge\\MailPace\\": ""
+ },
+ "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"
+ },
+ {
+ "name": "Paul Oms",
+ "homepage": "https://mailpace.com"
+ }
+ ],
+ "description": "Symfony MailPace Mailer Bridge",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/mail-pace-mailer/tree/v7.4.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-08-04T07:05:15+00:00"
+ },
+ {
+ "name": "symfony/mailchimp-mailer",
+ "version": "v7.4.6",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/mailchimp-mailer.git",
+ "reference": "d3d861e9ceeeadcd7b9874f41bd217e9537517a6"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/mailchimp-mailer/zipball/d3d861e9ceeeadcd7b9874f41bd217e9537517a6",
+ "reference": "d3d861e9ceeeadcd7b9874f41bd217e9537517a6",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/mailer": "^7.2|^8.0"
+ },
+ "conflict": {
+ "symfony/webhook": "<7.2"
+ },
+ "require-dev": {
+ "symfony/http-client": "^6.4|^7.0|^8.0",
+ "symfony/webhook": "^7.2|^8.0"
+ },
+ "type": "symfony-mailer-bridge",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Mailer\\Bridge\\Mailchimp\\": ""
+ },
+ "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": "Symfony Mailchimp Mailer Bridge",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/mailchimp-mailer/tree/v7.4.6"
+ },
+ "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": "2026-02-23T13:47:45+00:00"
+ },
+ {
+ "name": "symfony/mailer",
+ "version": "v7.4.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/mailer.git",
+ "reference": "f6ea532250b476bfc1b56699b388a1bdbf168f62"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/mailer/zipball/f6ea532250b476bfc1b56699b388a1bdbf168f62",
+ "reference": "f6ea532250b476bfc1b56699b388a1bdbf168f62",
+ "shasum": ""
+ },
+ "require": {
+ "egulias/email-validator": "^2.1.10|^3|^4",
+ "php": ">=8.2",
+ "psr/event-dispatcher": "^1",
+ "psr/log": "^1|^2|^3",
+ "symfony/event-dispatcher": "^6.4|^7.0|^8.0",
+ "symfony/mime": "^7.2|^8.0",
+ "symfony/service-contracts": "^2.5|^3"
+ },
+ "conflict": {
+ "symfony/http-client-contracts": "<2.5",
+ "symfony/http-kernel": "<6.4",
+ "symfony/messenger": "<6.4",
+ "symfony/mime": "<6.4",
+ "symfony/twig-bridge": "<6.4"
+ },
+ "require-dev": {
+ "symfony/console": "^6.4|^7.0|^8.0",
+ "symfony/http-client": "^6.4|^7.0|^8.0",
+ "symfony/messenger": "^6.4|^7.0|^8.0",
+ "symfony/twig-bridge": "^6.4|^7.0|^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Mailer\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Helps sending emails",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/mailer/tree/v7.4.8"
+ },
+ "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": "2026-03-24T13:12:05+00:00"
+ },
+ {
+ "name": "symfony/mailer-send-mailer",
+ "version": "v7.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/mailer-send-mailer.git",
+ "reference": "579a3a41316e4adf69a3e140b840d68f478f95cc"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/mailer-send-mailer/zipball/579a3a41316e4adf69a3e140b840d68f478f95cc",
+ "reference": "579a3a41316e4adf69a3e140b840d68f478f95cc",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/mailer": "^7.2|^8.0"
+ },
+ "require-dev": {
+ "symfony/http-client": "^6.4|^7.0|^8.0",
+ "symfony/webhook": "^6.3|^7.0|^8.0"
+ },
+ "type": "symfony-mailer-bridge",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Mailer\\Bridge\\MailerSend\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ramunas Pabreza",
+ "email": "doobas@gmail.com"
+ },
+ {
+ "name": "Tautvydas Tijunaitis",
+ "email": "fosron@gmail.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony MailerSend Mailer Bridge",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/mailer-send-mailer/tree/v7.4.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-08-04T07:05:15+00:00"
+ },
+ {
+ "name": "symfony/mailgun-mailer",
+ "version": "v7.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/mailgun-mailer.git",
+ "reference": "ffbcdbf93ed0700f083a6307acfb8f78dd3f091b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/mailgun-mailer/zipball/ffbcdbf93ed0700f083a6307acfb8f78dd3f091b",
+ "reference": "ffbcdbf93ed0700f083a6307acfb8f78dd3f091b",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/mailer": "^7.2|^8.0"
+ },
+ "conflict": {
+ "symfony/http-foundation": "<6.4"
+ },
+ "require-dev": {
+ "symfony/http-client": "^6.4|^7.0|^8.0",
+ "symfony/webhook": "^6.4|^7.0|^8.0"
+ },
+ "type": "symfony-mailer-bridge",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Mailer\\Bridge\\Mailgun\\": ""
+ },
+ "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": "Symfony Mailgun Mailer Bridge",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/mailgun-mailer/tree/v7.4.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-08-04T07:05:15+00:00"
+ },
+ {
+ "name": "symfony/mailjet-mailer",
+ "version": "v7.4.7",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/mailjet-mailer.git",
+ "reference": "e41b20b2bcb3bfc263395afc47b9df5911ceaa8f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/mailjet-mailer/zipball/e41b20b2bcb3bfc263395afc47b9df5911ceaa8f",
+ "reference": "e41b20b2bcb3bfc263395afc47b9df5911ceaa8f",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/mailer": "^7.3|^8.0"
+ },
+ "conflict": {
+ "symfony/mime": "<6.2"
+ },
+ "require-dev": {
+ "symfony/http-client": "^6.4|^7.0|^8.0",
+ "symfony/webhook": "^6.4|^7.0|^8.0"
+ },
+ "type": "symfony-mailer-bridge",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Mailer\\Bridge\\Mailjet\\": ""
+ },
+ "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": "Symfony Mailjet Mailer Bridge",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/mailjet-mailer/tree/v7.4.7"
+ },
+ "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": "2026-03-04T13:54:41+00:00"
+ },
+ {
+ "name": "symfony/mailomat-mailer",
+ "version": "v7.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/mailomat-mailer.git",
+ "reference": "bcadc6483b45c470a7e2c8c957162cb3a83f4300"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/mailomat-mailer/zipball/bcadc6483b45c470a7e2c8c957162cb3a83f4300",
+ "reference": "bcadc6483b45c470a7e2c8c957162cb3a83f4300",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/mailer": "^7.2|^8.0"
+ },
+ "conflict": {
+ "symfony/http-foundation": "<7.1"
+ },
+ "require-dev": {
+ "symfony/http-client": "^6.4|^7.0|^8.0",
+ "symfony/http-foundation": "^7.1|^8.0",
+ "symfony/webhook": "^6.4|^7.0|^8.0"
+ },
+ "type": "symfony-mailer-bridge",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Mailer\\Bridge\\Mailomat\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Patrick Landolt",
+ "email": "patrick.landolt@artack.ch"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony Mailomat Mailer Bridge",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/mailomat-mailer/tree/v7.4.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-08-04T07:05:15+00:00"
+ },
+ {
+ "name": "symfony/mailtrap-mailer",
+ "version": "v7.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/mailtrap-mailer.git",
+ "reference": "40f93b3bf73f5dd486ba31d2e825780d68ad4303"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/mailtrap-mailer/zipball/40f93b3bf73f5dd486ba31d2e825780d68ad4303",
+ "reference": "40f93b3bf73f5dd486ba31d2e825780d68ad4303",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "psr/event-dispatcher": "^1",
+ "symfony/mailer": "^7.2|^8.0"
+ },
+ "conflict": {
+ "symfony/webhook": "<7.2"
+ },
+ "require-dev": {
+ "symfony/http-client": "^6.4|^7.0|^8.0",
+ "symfony/webhook": "^7.2|^8.0"
+ },
+ "type": "symfony-mailer-bridge",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Mailer\\Bridge\\Mailtrap\\": ""
+ },
+ "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": "Symfony Mailtrap Mailer Bridge",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/mailtrap-mailer/tree/v7.4.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-08-22T08:09:19+00:00"
+ },
+ {
+ "name": "symfony/microsoft-graph-mailer",
+ "version": "v7.4.6",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/microsoft-graph-mailer.git",
+ "reference": "e9dc42d3c9fb0a3349a4947d51bdbe5b6638aa5c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/microsoft-graph-mailer/zipball/e9dc42d3c9fb0a3349a4947d51bdbe5b6638aa5c",
+ "reference": "e9dc42d3c9fb0a3349a4947d51bdbe5b6638aa5c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/clock": "^7.4|^8.0",
+ "symfony/http-client": "^6.4|^7.0|^8.0",
+ "symfony/mailer": "^7.4|^8.0"
+ },
+ "type": "symfony-mailer-bridge",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Mailer\\Bridge\\MicrosoftGraph\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Bob van de Vijver",
+ "homepage": "https://github.com/bobvandevijver"
+ },
+ {
+ "name": "Kevin Nguyen",
+ "homepage": "https://github.com/nguyenk"
+ },
+ {
+ "name": "The Coding Machine",
+ "homepage": "https://github.com/thecodingmachine"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony Microsoft Graph Mailer Bridge",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/microsoft-graph-mailer/tree/v7.4.6"
+ },
+ "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": "2026-02-09T12:07:29+00:00"
+ },
+ {
+ "name": "symfony/mime",
+ "version": "v7.4.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/mime.git",
+ "reference": "6df02f99998081032da3407a8d6c4e1dcb5d4379"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/mime/zipball/6df02f99998081032da3407a8d6c4e1dcb5d4379",
+ "reference": "6df02f99998081032da3407a8d6c4e1dcb5d4379",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/deprecation-contracts": "^2.5|^3",
+ "symfony/polyfill-intl-idn": "^1.10",
+ "symfony/polyfill-mbstring": "^1.0"
+ },
+ "conflict": {
+ "egulias/email-validator": "~3.0.0",
+ "phpdocumentor/reflection-docblock": "<5.2|>=7",
+ "phpdocumentor/type-resolver": "<1.5.1",
+ "symfony/mailer": "<6.4",
+ "symfony/serializer": "<6.4.3|>7.0,<7.0.3"
+ },
+ "require-dev": {
+ "egulias/email-validator": "^2.1.10|^3.1|^4",
+ "league/html-to-markdown": "^5.0",
+ "phpdocumentor/reflection-docblock": "^5.2|^6.0",
+ "symfony/dependency-injection": "^6.4|^7.0|^8.0",
+ "symfony/process": "^6.4|^7.0|^8.0",
+ "symfony/property-access": "^6.4|^7.0|^8.0",
+ "symfony/property-info": "^6.4|^7.0|^8.0",
+ "symfony/serializer": "^6.4.3|^7.0.3|^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Mime\\": ""
+ },
+ "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": "Allows manipulating MIME messages",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "mime",
+ "mime-type"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/mime/tree/v7.4.8"
+ },
+ "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": "2026-03-30T14:11:46+00:00"
},
{
"name": "symfony/polyfill-ctype",
@@ -4518,6 +6317,93 @@
],
"time": "2025-06-27T09:58:17+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",
@@ -4689,17 +6575,17 @@
"time": "2024-12-23T08:48:59+00:00"
},
{
- "name": "symfony/polyfill-php73",
+ "name": "symfony/polyfill-php83",
"version": "v1.33.0",
"source": {
"type": "git",
- "url": "https://github.com/symfony/polyfill-php73.git",
- "reference": "0f68c03565dcaaf25a890667542e8bd75fe7e5bb"
+ "url": "https://github.com/symfony/polyfill-php83.git",
+ "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/0f68c03565dcaaf25a890667542e8bd75fe7e5bb",
- "reference": "0f68c03565dcaaf25a890667542e8bd75fe7e5bb",
+ "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/17f6f9a6b1735c0f163024d959f700cfbc5155e5",
+ "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5",
"shasum": ""
},
"require": {
@@ -4717,7 +6603,7 @@
"bootstrap.php"
],
"psr-4": {
- "Symfony\\Polyfill\\Php73\\": ""
+ "Symfony\\Polyfill\\Php83\\": ""
},
"classmap": [
"Resources/stubs"
@@ -4737,7 +6623,7 @@
"homepage": "https://symfony.com/contributors"
}
],
- "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions",
+ "description": "Symfony polyfill backporting some PHP 8.3+ features to lower PHP versions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
@@ -4746,7 +6632,7 @@
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-php73/tree/v1.33.0"
+ "source": "https://github.com/symfony/polyfill-php83/tree/v1.33.0"
},
"funding": [
{
@@ -4766,41 +6652,36 @@
"type": "tidelift"
}
],
- "time": "2024-09-09T11:45:10+00:00"
+ "time": "2025-07-08T02:45:35+00:00"
},
{
- "name": "symfony/polyfill-php80",
- "version": "v1.33.0",
+ "name": "symfony/postal-mailer",
+ "version": "v7.4.6",
"source": {
"type": "git",
- "url": "https://github.com/symfony/polyfill-php80.git",
- "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608"
+ "url": "https://github.com/symfony/postal-mailer.git",
+ "reference": "a2f2d915861fac611323c1f256d9b841dce44c92"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608",
- "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608",
+ "url": "https://api.github.com/repos/symfony/postal-mailer/zipball/a2f2d915861fac611323c1f256d9b841dce44c92",
+ "reference": "a2f2d915861fac611323c1f256d9b841dce44c92",
"shasum": ""
},
"require": {
- "php": ">=7.2"
+ "php": ">=8.2",
+ "symfony/mailer": "^7.2|^8.0"
},
- "type": "library",
- "extra": {
- "thanks": {
- "url": "https://github.com/symfony/polyfill",
- "name": "symfony/polyfill"
- }
+ "require-dev": {
+ "symfony/http-client": "^6.4|^7.0|^8.0"
},
+ "type": "symfony-mailer-bridge",
"autoload": {
- "files": [
- "bootstrap.php"
- ],
"psr-4": {
- "Symfony\\Polyfill\\Php80\\": ""
+ "Symfony\\Component\\Mailer\\Bridge\\Postal\\": ""
},
- "classmap": [
- "Resources/stubs"
+ "exclude-from-classmap": [
+ "/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
@@ -4809,28 +6690,18 @@
],
"authors": [
{
- "name": "Ion Bazan",
- "email": "ion.bazan@gmail.com"
- },
- {
- "name": "Nicolas Grekas",
- "email": "p@tchwork.com"
+ "name": "Jonas Claes",
+ "homepage": "https://github.com/jonasclaes"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
- "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
+ "description": "Symfony Postal Mailer Bridge",
"homepage": "https://symfony.com",
- "keywords": [
- "compatibility",
- "polyfill",
- "portable",
- "shim"
- ],
"support": {
- "source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0"
+ "source": "https://github.com/symfony/postal-mailer/tree/v7.4.6"
},
"funding": [
{
@@ -4850,24 +6721,98 @@
"type": "tidelift"
}
],
- "time": "2025-01-02T08:10:11+00:00"
+ "time": "2026-02-23T11:56:35+00:00"
+ },
+ {
+ "name": "symfony/postmark-mailer",
+ "version": "v7.4.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/postmark-mailer.git",
+ "reference": "8b573474e89368f1ddb25b43fd86a6dd51343e9b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/postmark-mailer/zipball/8b573474e89368f1ddb25b43fd86a6dd51343e9b",
+ "reference": "8b573474e89368f1ddb25b43fd86a6dd51343e9b",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "psr/event-dispatcher": "^1",
+ "symfony/mailer": "^7.2|^8.0"
+ },
+ "conflict": {
+ "symfony/http-foundation": "<6.4"
+ },
+ "require-dev": {
+ "symfony/http-client": "^6.4|^7.0|^8.0",
+ "symfony/webhook": "^6.4|^7.0|^8.0"
+ },
+ "type": "symfony-mailer-bridge",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Mailer\\Bridge\\Postmark\\": ""
+ },
+ "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": "Symfony Postmark Mailer Bridge",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/postmark-mailer/tree/v7.4.4"
+ },
+ "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": "2026-01-08T08:25:11+00:00"
},
{
"name": "symfony/process",
- "version": "v6.4.31",
+ "version": "v7.4.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
- "reference": "8541b7308fca001320e90bca8a73a28aa5604a6e"
+ "reference": "60f19cd3badc8de688421e21e4305eba50f8089a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/process/zipball/8541b7308fca001320e90bca8a73a28aa5604a6e",
- "reference": "8541b7308fca001320e90bca8a73a28aa5604a6e",
+ "url": "https://api.github.com/repos/symfony/process/zipball/60f19cd3badc8de688421e21e4305eba50f8089a",
+ "reference": "60f19cd3badc8de688421e21e4305eba50f8089a",
"shasum": ""
},
"require": {
- "php": ">=8.1"
+ "php": ">=8.2"
},
"type": "library",
"autoload": {
@@ -4895,7 +6840,7 @@
"description": "Executes commands in sub-processes",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/process/tree/v6.4.31"
+ "source": "https://github.com/symfony/process/tree/v7.4.8"
},
"funding": [
{
@@ -4915,7 +6860,225 @@
"type": "tidelift"
}
],
- "time": "2025-12-15T19:26:35+00:00"
+ "time": "2026-03-24T13:12:05+00:00"
+ },
+ {
+ "name": "symfony/resend-mailer",
+ "version": "v7.4.6",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/resend-mailer.git",
+ "reference": "eb7f4d83128eef12fcceccf33e5b4b89f2e2474f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/resend-mailer/zipball/eb7f4d83128eef12fcceccf33e5b4b89f2e2474f",
+ "reference": "eb7f4d83128eef12fcceccf33e5b4b89f2e2474f",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/mailer": "^7.2|^8.0"
+ },
+ "conflict": {
+ "symfony/http-foundation": "<7.1"
+ },
+ "require-dev": {
+ "symfony/http-client": "^6.4|^7.0|^8.0",
+ "symfony/http-foundation": "^7.1|^8.0",
+ "symfony/webhook": "^6.4|^7.0|^8.0"
+ },
+ "type": "symfony-mailer-bridge",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Mailer\\Bridge\\Resend\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Mathieu Santostefano",
+ "homepage": "https://github.com/welcoMattic"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony Resend Mailer Bridge",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/resend-mailer/tree/v7.4.6"
+ },
+ "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": "2026-02-09T14:10:20+00:00"
+ },
+ {
+ "name": "symfony/scaleway-mailer",
+ "version": "v7.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/scaleway-mailer.git",
+ "reference": "bf9b8f6133d74b620b7c2e90b6fa4bd1469832e9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/scaleway-mailer/zipball/bf9b8f6133d74b620b7c2e90b6fa4bd1469832e9",
+ "reference": "bf9b8f6133d74b620b7c2e90b6fa4bd1469832e9",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/mailer": "^7.2|^8.0"
+ },
+ "require-dev": {
+ "symfony/http-client": "^6.4|^7.0|^8.0"
+ },
+ "type": "symfony-mailer-bridge",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Mailer\\Bridge\\Scaleway\\": ""
+ },
+ "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": "Symfony Scaleway Mailer Bridge",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/scaleway-mailer/tree/v7.4.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-08-04T07:05:15+00:00"
+ },
+ {
+ "name": "symfony/sendgrid-mailer",
+ "version": "v7.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/sendgrid-mailer.git",
+ "reference": "48e81fdbeb443a8094b7d5322d4c1a2023c40a57"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/sendgrid-mailer/zipball/48e81fdbeb443a8094b7d5322d4c1a2023c40a57",
+ "reference": "48e81fdbeb443a8094b7d5322d4c1a2023c40a57",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/mailer": "^7.2|^8.0"
+ },
+ "conflict": {
+ "symfony/http-foundation": "<6.4",
+ "symfony/mime": "<6.4",
+ "symfony/webhook": "<7.2"
+ },
+ "require-dev": {
+ "symfony/http-client": "^6.4|^7.0|^8.0",
+ "symfony/webhook": "^7.2|^8.0"
+ },
+ "type": "symfony-mailer-bridge",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Mailer\\Bridge\\Sendgrid\\": ""
+ },
+ "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": "Symfony Sendgrid Mailer Bridge",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/sendgrid-mailer/tree/v7.4.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-09-16T14:23:56+00:00"
},
{
"name": "symfony/service-contracts",
@@ -5006,22 +7169,23 @@
},
{
"name": "symfony/string",
- "version": "v6.4.30",
+ "version": "v7.4.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/string.git",
- "reference": "50590a057841fa6bf69d12eceffce3465b9e32cb"
+ "reference": "114ac57257d75df748eda23dd003878080b8e688"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/string/zipball/50590a057841fa6bf69d12eceffce3465b9e32cb",
- "reference": "50590a057841fa6bf69d12eceffce3465b9e32cb",
+ "url": "https://api.github.com/repos/symfony/string/zipball/114ac57257d75df748eda23dd003878080b8e688",
+ "reference": "114ac57257d75df748eda23dd003878080b8e688",
"shasum": ""
},
"require": {
- "php": ">=8.1",
+ "php": ">=8.2",
+ "symfony/deprecation-contracts": "^2.5|^3.0",
"symfony/polyfill-ctype": "~1.8",
- "symfony/polyfill-intl-grapheme": "~1.0",
+ "symfony/polyfill-intl-grapheme": "~1.33",
"symfony/polyfill-intl-normalizer": "~1.0",
"symfony/polyfill-mbstring": "~1.0"
},
@@ -5029,10 +7193,11 @@
"symfony/translation-contracts": "<2.5"
},
"require-dev": {
- "symfony/http-client": "^5.4|^6.0|^7.0",
- "symfony/intl": "^6.2|^7.0",
+ "symfony/emoji": "^7.1|^8.0",
+ "symfony/http-client": "^6.4|^7.0|^8.0",
+ "symfony/intl": "^6.4|^7.0|^8.0",
"symfony/translation-contracts": "^2.5|^3.0",
- "symfony/var-exporter": "^5.4|^6.0|^7.0"
+ "symfony/var-exporter": "^6.4|^7.0|^8.0"
},
"type": "library",
"autoload": {
@@ -5071,7 +7236,7 @@
"utf8"
],
"support": {
- "source": "https://github.com/symfony/string/tree/v6.4.30"
+ "source": "https://github.com/symfony/string/tree/v7.4.8"
},
"funding": [
{
@@ -5091,20 +7256,94 @@
"type": "tidelift"
}
],
- "time": "2025-11-21T18:03:05+00:00"
+ "time": "2026-03-24T13:12:05+00:00"
},
{
- "name": "symfony/var-dumper",
- "version": "v6.4.26",
+ "name": "symfony/sweego-mailer",
+ "version": "v7.4.0",
"source": {
"type": "git",
- "url": "https://github.com/symfony/var-dumper.git",
- "reference": "cfae1497a2f1eaad78dbc0590311c599c7178d4a"
+ "url": "https://github.com/symfony/sweego-mailer.git",
+ "reference": "8ab5f920ce2bb60757ab633cda9c0bac39f502a8"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/var-dumper/zipball/cfae1497a2f1eaad78dbc0590311c599c7178d4a",
- "reference": "cfae1497a2f1eaad78dbc0590311c599c7178d4a",
+ "url": "https://api.github.com/repos/symfony/sweego-mailer/zipball/8ab5f920ce2bb60757ab633cda9c0bac39f502a8",
+ "reference": "8ab5f920ce2bb60757ab633cda9c0bac39f502a8",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.2",
+ "symfony/mailer": "^7.2|^8.0"
+ },
+ "conflict": {
+ "symfony/http-foundation": "<7.1"
+ },
+ "require-dev": {
+ "symfony/http-client": "^6.4|^7.0|^8.0",
+ "symfony/http-foundation": "^7.1|^8.0",
+ "symfony/webhook": "^6.4|^7.0|^8.0"
+ },
+ "type": "symfony-mailer-bridge",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Mailer\\Bridge\\Sweego\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Mathieu Santostefano",
+ "homepage": "https://github.com/welcoMattic"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony Sweego Mailer Bridge",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/sweego-mailer/tree/v7.4.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-11-21T15:26:00+00:00"
+ },
+ {
+ "name": "symfony/var-dumper",
+ "version": "v6.4.36",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/var-dumper.git",
+ "reference": "7c8ad9ce4faf6c8a99948e70ce02b601a0439782"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/var-dumper/zipball/7c8ad9ce4faf6c8a99948e70ce02b601a0439782",
+ "reference": "7c8ad9ce4faf6c8a99948e70ce02b601a0439782",
"shasum": ""
},
"require": {
@@ -5159,7 +7398,7 @@
"dump"
],
"support": {
- "source": "https://github.com/symfony/var-dumper/tree/v6.4.26"
+ "source": "https://github.com/symfony/var-dumper/tree/v6.4.36"
},
"funding": [
{
@@ -5179,30 +7418,30 @@
"type": "tidelift"
}
],
- "time": "2025-09-25T15:37:27+00:00"
+ "time": "2026-03-30T15:36:00+00:00"
},
{
"name": "symfony/var-exporter",
- "version": "v6.4.26",
+ "version": "v7.4.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/var-exporter.git",
- "reference": "466fcac5fa2e871f83d31173f80e9c2684743bfc"
+ "reference": "398907e89a2a56fe426f7955c6fa943ec0c77225"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/var-exporter/zipball/466fcac5fa2e871f83d31173f80e9c2684743bfc",
- "reference": "466fcac5fa2e871f83d31173f80e9c2684743bfc",
+ "url": "https://api.github.com/repos/symfony/var-exporter/zipball/398907e89a2a56fe426f7955c6fa943ec0c77225",
+ "reference": "398907e89a2a56fe426f7955c6fa943ec0c77225",
"shasum": ""
},
"require": {
- "php": ">=8.1",
+ "php": ">=8.2",
"symfony/deprecation-contracts": "^2.5|^3"
},
"require-dev": {
- "symfony/property-access": "^6.4|^7.0",
- "symfony/serializer": "^6.4|^7.0",
- "symfony/var-dumper": "^5.4|^6.0|^7.0"
+ "symfony/property-access": "^6.4|^7.0|^8.0",
+ "symfony/serializer": "^6.4|^7.0|^8.0",
+ "symfony/var-dumper": "^6.4|^7.0|^8.0"
},
"type": "library",
"autoload": {
@@ -5240,7 +7479,7 @@
"serialize"
],
"support": {
- "source": "https://github.com/symfony/var-exporter/tree/v6.4.26"
+ "source": "https://github.com/symfony/var-exporter/tree/v7.4.8"
},
"funding": [
{
@@ -5260,50 +7499,35 @@
"type": "tidelift"
}
],
- "time": "2025-09-11T09:57:09+00:00"
+ "time": "2026-03-24T13:12:05+00:00"
},
{
"name": "thecodingmachine/safe",
- "version": "v2.5.0",
+ "version": "v3.4.0",
"source": {
"type": "git",
"url": "https://github.com/thecodingmachine/safe.git",
- "reference": "3115ecd6b4391662b4931daac4eba6b07a2ac1f0"
+ "reference": "705683a25bacf0d4860c7dea4d7947bfd09eea19"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/thecodingmachine/safe/zipball/3115ecd6b4391662b4931daac4eba6b07a2ac1f0",
- "reference": "3115ecd6b4391662b4931daac4eba6b07a2ac1f0",
+ "url": "https://api.github.com/repos/thecodingmachine/safe/zipball/705683a25bacf0d4860c7dea4d7947bfd09eea19",
+ "reference": "705683a25bacf0d4860c7dea4d7947bfd09eea19",
"shasum": ""
},
"require": {
- "php": "^8.0"
+ "php": "^8.1"
},
"require-dev": {
- "phpstan/phpstan": "^1.5",
- "phpunit/phpunit": "^9.5",
- "squizlabs/php_codesniffer": "^3.2",
- "thecodingmachine/phpstan-strict-rules": "^1.0"
+ "php-parallel-lint/php-parallel-lint": "^1.4",
+ "phpstan/phpstan": "^2",
+ "phpunit/phpunit": "^10",
+ "squizlabs/php_codesniffer": "^3.2"
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "2.2.x-dev"
- }
- },
"autoload": {
"files": [
- "deprecated/apc.php",
- "deprecated/array.php",
- "deprecated/datetime.php",
- "deprecated/libevent.php",
- "deprecated/misc.php",
- "deprecated/password.php",
- "deprecated/mssql.php",
- "deprecated/stats.php",
- "deprecated/strings.php",
"lib/special_cases.php",
- "deprecated/mysqli.php",
"generated/apache.php",
"generated/apcu.php",
"generated/array.php",
@@ -5343,6 +7567,7 @@
"generated/mbstring.php",
"generated/misc.php",
"generated/mysql.php",
+ "generated/mysqli.php",
"generated/network.php",
"generated/oci8.php",
"generated/opcache.php",
@@ -5355,6 +7580,7 @@
"generated/ps.php",
"generated/pspell.php",
"generated/readline.php",
+ "generated/rnp.php",
"generated/rpminfo.php",
"generated/rrd.php",
"generated/sem.php",
@@ -5386,7 +7612,6 @@
"lib/DateTime.php",
"lib/DateTimeImmutable.php",
"lib/Exceptions/",
- "deprecated/Exceptions/",
"generated/Exceptions/"
]
},
@@ -5397,76 +7622,27 @@
"description": "PHP core functions that throw exceptions instead of returning FALSE on error",
"support": {
"issues": "https://github.com/thecodingmachine/safe/issues",
- "source": "https://github.com/thecodingmachine/safe/tree/v2.5.0"
+ "source": "https://github.com/thecodingmachine/safe/tree/v3.4.0"
},
- "time": "2023-04-05T11:54:14+00:00"
- },
- {
- "name": "xrdebug/php",
- "version": "3.0.1",
- "source": {
- "type": "git",
- "url": "https://github.com/xrdebug/php.git",
- "reference": "9df68ba66668b0072abb91859d27407d6772f0fd"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/xrdebug/php/zipball/9df68ba66668b0072abb91859d27407d6772f0fd",
- "reference": "9df68ba66668b0072abb91859d27407d6772f0fd",
- "shasum": ""
- },
- "require": {
- "chevere/filesystem": "^1.0.3",
- "chevere/message": "^1.0.0",
- "chevere/standard": "^1.0.1",
- "chevere/throwable-handler": "^1.0.7",
- "chevere/trace": "^2.0.2",
- "chevere/var-dump": "^2.0.6",
- "ext-curl": "*",
- "ext-json": "*",
- "php": "^8.1",
- "phpseclib/phpseclib": "~3.0"
- },
- "require-dev": {
- "dg/bypass-finals": "^1.4",
- "phpstan/phpstan": "^2.0",
- "phpunit/phpunit": "^9.5",
- "symplify/easy-coding-standard": "^11.1"
- },
- "type": "library",
- "autoload": {
- "files": [
- "src/functions.php"
- ],
- "psr-4": {
- "Chevere\\xrDebug\\PHP\\": "src/"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "Apache-2.0"
- ],
- "authors": [
+ "funding": [
{
- "name": "Rodolfo Berrios",
- "email": "rodolfo@chevere.org",
- "homepage": "https://rodolfoberrios.com"
+ "url": "https://github.com/OskarStark",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/shish",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/silasjoisten",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/staabm",
+ "type": "github"
}
],
- "description": "PHP client library for xrDebug",
- "homepage": "https://github.com/xrdebug/php",
- "keywords": [
- "chevere",
- "debug",
- "debugging",
- "dump",
- "xrdebug"
- ],
- "support": {
- "issues": "https://github.com/xrdebug/php/issues",
- "source": "https://github.com/xrdebug/php/tree/3.0.1"
- },
- "time": "2025-09-12T21:27:45+00:00"
+ "time": "2026-02-04T18:08:13+00:00"
}
],
"packages-dev": [
@@ -5720,11 +7896,11 @@
},
{
"name": "phpstan/phpstan",
- "version": "1.12.32",
+ "version": "1.12.33",
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpstan/phpstan/zipball/2770dcdf5078d0b0d53f94317e06affe88419aa8",
- "reference": "2770dcdf5078d0b0d53f94317e06affe88419aa8",
+ "url": "https://api.github.com/repos/phpstan/phpstan/zipball/37982d6fc7cbb746dda7773530cda557cdf119e1",
+ "reference": "37982d6fc7cbb746dda7773530cda557cdf119e1",
"shasum": ""
},
"require": {
@@ -5769,7 +7945,7 @@
"type": "github"
}
],
- "time": "2025-09-30T10:16:31+00:00"
+ "time": "2026-02-28T20:30:03+00:00"
},
{
"name": "phpunit/php-code-coverage",
@@ -6092,16 +8268,16 @@
},
{
"name": "phpunit/phpunit",
- "version": "9.6.31",
+ "version": "9.6.34",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
- "reference": "945d0b7f346a084ce5549e95289962972c4272e5"
+ "reference": "b36f02317466907a230d3aa1d34467041271ef4a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/945d0b7f346a084ce5549e95289962972c4272e5",
- "reference": "945d0b7f346a084ce5549e95289962972c4272e5",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b36f02317466907a230d3aa1d34467041271ef4a",
+ "reference": "b36f02317466907a230d3aa1d34467041271ef4a",
"shasum": ""
},
"require": {
@@ -6123,7 +8299,7 @@
"phpunit/php-timer": "^5.0.3",
"sebastian/cli-parser": "^1.0.2",
"sebastian/code-unit": "^1.0.8",
- "sebastian/comparator": "^4.0.9",
+ "sebastian/comparator": "^4.0.10",
"sebastian/diff": "^4.0.6",
"sebastian/environment": "^5.1.5",
"sebastian/exporter": "^4.0.8",
@@ -6175,7 +8351,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
- "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.31"
+ "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.34"
},
"funding": [
{
@@ -6199,7 +8375,7 @@
"type": "tidelift"
}
],
- "time": "2025-12-06T07:45:52+00:00"
+ "time": "2026-01-27T05:45:00+00:00"
},
{
"name": "sebastian/cli-parser",
@@ -6370,16 +8546,16 @@
},
{
"name": "sebastian/comparator",
- "version": "4.0.9",
+ "version": "4.0.10",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/comparator.git",
- "reference": "67a2df3a62639eab2cc5906065e9805d4fd5dfc5"
+ "reference": "e4df00b9b3571187db2831ae9aada2c6efbd715d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/67a2df3a62639eab2cc5906065e9805d4fd5dfc5",
- "reference": "67a2df3a62639eab2cc5906065e9805d4fd5dfc5",
+ "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/e4df00b9b3571187db2831ae9aada2c6efbd715d",
+ "reference": "e4df00b9b3571187db2831ae9aada2c6efbd715d",
"shasum": ""
},
"require": {
@@ -6432,7 +8608,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/comparator/issues",
- "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.9"
+ "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.10"
},
"funding": [
{
@@ -6452,7 +8628,7 @@
"type": "tidelift"
}
],
- "time": "2025-08-10T06:51:50+00:00"
+ "time": "2026-01-24T09:22:56+00:00"
},
{
"name": "sebastian/complexity",
@@ -7330,11 +9506,11 @@
"prefer-stable": true,
"prefer-lowest": false,
"platform": {
- "php": "^8.1"
+ "php": "^8.2"
},
"platform-dev": {},
"platform-overrides": {
- "php": "8.1.28"
+ "php": "8.2"
},
"plugin-api-version": "2.9.0"
}
diff --git a/app/env-default.php b/app/env-default.php
index 177e459..6ab9c9a 100644
--- a/app/env-default.php
+++ b/app/env-default.php
@@ -90,7 +90,6 @@ return [
'CHEVERETO_HTTPS' => '1',
'CHEVERETO_IMAGE_FORMATS_AVAILABLE' => '["AVIF","JPEG","PNG","BMP","GIF","WEBP"]',
'CHEVERETO_IMAGE_LIBRARY' => 'imagick',
- 'CHEVERETO_JOBS_WORKER_INTERVAL' => '300',
'CHEVERETO_MAX_ADMINS' => '0',
'CHEVERETO_MAX_ALBUMS' => '0',
'CHEVERETO_MAX_CACHE_TTL' => '86400',
@@ -115,6 +114,8 @@ return [
'CHEVERETO_MIN_STORAGES_ACTIVE' => '0',
'CHEVERETO_PROVIDER_NAME' => 'Self-hosted Chevereto',
'CHEVERETO_PROVIDER_URL' => '',
+ 'CHEVERETO_SCHEDULER_INTERVAL' => '300',
+ 'CHEVERETO_SERVICE_NAME' => 'app',
'CHEVERETO_SERVICING' => 'server',
'CHEVERETO_SESSION_SAVE_HANDLER' => 'files',
'CHEVERETO_SESSION_SAVE_PATH' => '/tmp',
@@ -123,6 +124,7 @@ return [
'CHEVERETO_TENANTS_API_ALLOW_LIST' => '',
'CHEVERETO_TENANTS_API_KEY_SECRET' => '',
'CHEVERETO_TENANTS_API_REQUEST_SECRET' => '',
+ 'CHEVERETO_TRIAL' => '0',
'CHEVERETO_XRDEBUG_HOST' => 'localhost',
'CHEVERETO_XRDEBUG_HTTPS' => '0',
'CHEVERETO_XRDEBUG_KEY' => '',
diff --git a/app/legacy/commands/password-reset.php b/app/legacy/commands/password-reset.php
index 5bf31d2..b8a921d 100644
--- a/app/legacy/commands/password-reset.php
+++ b/app/legacy/commands/password-reset.php
@@ -10,16 +10,22 @@
*/
use Chevereto\Legacy\Classes\Login;
+use Chevereto\Legacy\Classes\Settings;
use Chevereto\Legacy\Classes\User;
use function Chevere\Standard\randomString;
-$opts = getopt('C:u:') ?: [];
+$opts = getopt('C:u:x:') ?: [];
$missing = [];
if (! isset($opts['u'])) {
echo '[Error] Missing username' . "\n";
exit(255);
}
-$password = randomString(24);
+$password = $opts['x'] ?? randomString(24);
+if (! preg_match('/' . Settings::USER_PASSWORD_PATTERN . '/', $password)) {
+ echo '[Error] Invalid password' . "\n";
+ exit(255);
+}
+
$user = User::getSingle($opts['u'], 'username');
if ($user === []) {
echo '[Error] User not found' . "\n";
diff --git a/app/src/Legacy/Classes/Mailer.php b/app/legacy/commands/version-installed.php
similarity index 52%
rename from app/src/Legacy/Classes/Mailer.php
rename to app/legacy/commands/version-installed.php
index b204c0a..bbce376 100644
--- a/app/src/Legacy/Classes/Mailer.php
+++ b/app/legacy/commands/version-installed.php
@@ -9,11 +9,11 @@
* file that was distributed with this source code.
*/
-namespace Chevereto\Legacy\Classes;
+use function Chevereto\Legacy\cheveretoVersionInstalled;
-use PHPMailer\PHPMailer\PHPMailer;
-
-class Mailer extends PHPMailer
-{
- public $XMailer = ' ';
+$version = cheveretoVersionInstalled();
+if ($version === '') {
+ echo 'Chevereto is not installed' . PHP_EOL;
+ exit(255);
}
+echo $version . PHP_EOL;
diff --git a/app/legacy/entrypoints/cli.php b/app/legacy/entrypoints/cli.php
index dd45242..616421e 100644
--- a/app/legacy/entrypoints/cli.php
+++ b/app/legacy/entrypoints/cli.php
@@ -41,6 +41,7 @@ $options = [
'setting-update',
'database-migrate',
'version',
+ 'version-installed',
'stats',
'stats-rebuild',
];
diff --git a/app/legacy/install/installer.php b/app/legacy/install/installer.php
index cc67b14..ec9d1cb 100644
--- a/app/legacy/install/installer.php
+++ b/app/legacy/install/installer.php
@@ -59,6 +59,16 @@ if (cheveretoVersionInstalled() !== ''
throw new LogicException(message('Request denied. You must be an admin to be here.'), 403);
}
+if ((env()['CHEVERETO_TENANT'] ?? '') !== '') {
+ if (env()['CHEVERETO_DB_TABLE_PREFIX'] === env()['CHEVERETO_DB_TABLE_ROOT_PREFIX']
+ || env()['CHEVERETO_CACHE_KEY_PREFIX'] === env()['CHEVERETO_CACHE_KEY_ROOT_PREFIX']
+ ) {
+ throw new LogicException(
+ 'Tenant namespace collision detected. Refusing install/update with root prefixes.',
+ 600
+ );
+ }
+}
if (function_exists('opcache_reset')) {
try {
opcache_reset();
@@ -121,7 +131,7 @@ $settings_updates = [
// 'google' => 0, // Deprecated in 4.0.0-beta.11
// 'google_client_id' => '',
// 'google_client_secret' => '',
- 'guest_uploads' => 1,
+ 'guest_uploads' => intval(env()['CHEVERETO_CONTEXT'] !== 'saas'),
'listing_items_per_page' => '24',
'maintenance' => 0,
'captcha' => 0, //recaptcha
@@ -660,6 +670,36 @@ $settings_updates = [
'4.4.0' => null,
'4.4.1' => null,
'4.4.2' => null,
+ '4.5.0' => [
+ 'email_ahasend_api_key' => '',
+ 'email_ses_access_key' => '',
+ 'email_ses_secret_key' => '',
+ 'email_azure_resource_name' => '',
+ 'email_azure_key' => '',
+ 'email_brevo_api_key' => '',
+ 'email_infobip_api_key' => '',
+ 'email_infobip_base_url' => '',
+ 'email_mailgun_api_key' => '',
+ 'email_mailgun_domain' => '',
+ 'email_mailjet_access_key' => '',
+ 'email_mailjet_secret_key' => '',
+ 'email_mailomat_api_key' => '',
+ 'email_mailpace_api_token' => '',
+ 'email_mailersend_api_key' => '',
+ 'email_mailtrap_api_token' => '',
+ 'email_mandrill_api_key' => '',
+ 'email_microsoftgraph_client_id' => '',
+ 'email_microsoftgraph_client_secret' => '',
+ 'email_microsoftgraph_tenant_id' => '',
+ 'email_postal_api_key' => '',
+ 'email_postal_base_url' => '',
+ 'email_postmark_api_token' => '',
+ 'email_resend_api_key' => '',
+ 'email_scaleway_project_id' => '',
+ 'email_scaleway_api_key' => '',
+ 'email_sendgrid_api_key' => '',
+ 'email_sweego_api_key' => '',
+ ],
];
/**
diff --git a/app/legacy/load/app.php b/app/legacy/load/app.php
index 8e5341a..a1deb22 100644
--- a/app/legacy/load/app.php
+++ b/app/legacy/load/app.php
@@ -9,5 +9,5 @@
* file that was distributed with this source code.
*/
-const APP_VERSION = '4.4.2';
-const APP_VERSION_AKA = 'vivaracho';
+const APP_VERSION = '4.5.0';
+const APP_VERSION_AKA = 'elevado';
diff --git a/app/legacy/load/register-handlers.php b/app/legacy/load/register-handlers.php
index 638ef73..bb16d0f 100644
--- a/app/legacy/load/register-handlers.php
+++ b/app/legacy/load/register-handlers.php
@@ -93,21 +93,49 @@ set_exception_handler(function (Throwable $throwable) {
$internalHandler = $publicHandler->withIsDebug(true);
$doDebug = in_array($debugLevel, [2, 3], true) || isDebug();
if ($doDebug === false) {
- $publicHandler = $publicHandler
- ->withIsDebug($doDebug)
- ->withPutExtra(
- 'Why am I seeing this?',
+ $incidentId = $publicHandler->id();
+ $providerName = getenv('CHEVERETO_PROVIDER_NAME') ?: '';
+ $providerUrl = getenv('CHEVERETO_PROVIDER_URL') ?: '#';
+ if (getenv('CHEVERETO_CONTEXT') === 'saas') {
+ $title = 'Service temporarily unavailable';
+ $message = "We're already on it! Our team has been automatically notified and is working to resolve this issue.";
+ $ownerGuide = strtr(
<<Our team is already investigating this incident. For immediate assistance, contact %providerLink% support with incident ID %id%.
+ HTML,
+ [
+ '%providerLink%' => <<{$providerName}
+ HTML,
+ '%id%' => $incidentId,
+ ]
+ );
+ } else {
+ $title = 'Something went wrong';
+ $message = 'Please try again later. If the error persists you may need to contact the website owner.';
+ $ownerGuide = <<This error has been logged with ID {$incidentId}. To diagnose the issue:
+
+ - Check your server error logs for this ID
+ - Review the debugging guide in the documentation
+ - Need help? Check our support alternatives
+
+ HTML;
+ }
+ $publicHandler = $publicHandler
+ ->withTitle($title)
+ ->withIsDebug($doDebug)
+ ->withMessage($message)
+ ->withPutExtra(
+ 'What happened?',
+ <<A technical error has occurred. The incident has been logged for investigation.
HTML
)
->withPutExtra(
- 'Administrator guide',
+ 'Are you the owner of this website?',
<<
- Refer to the Chevereto documentation to understand how to debug this error.
- Need help? Visit Chevereto support to open a ticket.
-
+ {$ownerGuide}
HTML
);
diff --git a/app/legacy/load/web.php b/app/legacy/load/web.php
index ba6801c..ab735c0 100644
--- a/app/legacy/load/web.php
+++ b/app/legacy/load/web.php
@@ -10,9 +10,11 @@
*/
use Chevere\Http\Exceptions\ControllerException;
+use Chevere\Http\Exceptions\MethodNotAllowedException;
use Chevere\Parameter\Interfaces\TypeInterface;
use Chevere\Parameter\Type;
use Chevere\Router\Container;
+use Chevere\Router\Exceptions\NotFoundException;
use Chevere\Writer\NullWriter;
use Chevereto\Config\Config;
use Chevereto\Legacy\Classes\Cache;
@@ -125,11 +127,32 @@ if ($isTenantsApiRouting) {
foreach ($headers as $name => $value) {
$serverRequest = $serverRequest->withHeader($name, $value);
}
- $router = router(require dirname(__DIR__, 2) . '/routes/tenants-api-v4.php');
+ $routerPath = dirname(__DIR__, 2) . '/routes/';
+ $routes = [
+ require $routerPath . 'tenants-api-v4.php',
+ ];
+ if (in_array(server()['SERVER_NAME'], ['localhost', '127.0.0.1', '::1', 'app'])) {
+ $routes[] = require $routerPath . 'tenants-internal-api-v4.php';
+ }
+ $router = router(...$routes);
$container = $container->withAutoInject(
$router->dependencies(),
);
- $routed = $router->getRouted($serverRequest, $psr17Factory, container: $container);
+
+ try {
+ $routed = $router->getRouted($serverRequest, $psr17Factory, container: $container);
+ } catch (NotFoundException|MethodNotAllowedException $e) {
+ http_response_code($e->getCode());
+ header('Content-Type: application/json');
+ echo json_encode([
+ 'error' => [
+ 'code' => $e->getCode(),
+ 'message' => $e->getMessage(),
+ 'list' => explode("\n", $e->getMessage()),
+ ],
+ ], JSON_PRETTY_PRINT);
+ exit();
+ }
if ($routed->hasThrowable()
&& ! ($routed->throwable() instanceof ControllerException)
) {
diff --git a/app/legacy/routes/dashboard.php b/app/legacy/routes/dashboard.php
index 1ca099f..9bab7b2 100644
--- a/app/legacy/routes/dashboard.php
+++ b/app/legacy/routes/dashboard.php
@@ -21,7 +21,6 @@ use Chevereto\Legacy\Classes\Image;
use Chevereto\Legacy\Classes\L10n;
use Chevereto\Legacy\Classes\Listing;
use Chevereto\Legacy\Classes\Login;
-use Chevereto\Legacy\Classes\Mailer;
use Chevereto\Legacy\Classes\Page;
use Chevereto\Legacy\Classes\ProjectArachnid;
use Chevereto\Legacy\Classes\Settings;
@@ -34,7 +33,8 @@ use Chevereto\Legacy\G\Handler;
use FFMpeg\FFMpeg;
use FFMpeg\FFProbe;
use Intervention\Image\ImageManagerStatic;
-use PHPMailer\PHPMailer\SMTP;
+use Symfony\Component\Mailer\Transport;
+use Symfony\Component\Mailer\Transport\Smtp\SmtpTransport;
use function Chevere\Standard\randomString;
use function Chevereto\Encryption\hasEncryption;
use function Chevereto\Legacy\badgePaid;
@@ -73,9 +73,11 @@ use function Chevereto\Legacy\getSystemNotices;
use function Chevereto\Legacy\getVariable;
use function Chevereto\Legacy\headersNoCache;
use function Chevereto\Legacy\strip_tags_content;
+use function Chevereto\Legacy\trialAwareLabel;
use function Chevereto\Legacy\updateCheveretoNews;
use function Chevereto\Legacy\upload_to_content_images;
use function Chevereto\Vars\env;
+use function Chevereto\Vars\envTrialAware;
use function Chevereto\Vars\files;
use function Chevereto\Vars\get;
use function Chevereto\Vars\post;
@@ -916,13 +918,13 @@ return function (Handler $handler) {
$settings_pages['title'] = _s('Add page');
$settings_pages['doing'] = 'add';
$pagesCount = Page::countAll();
- $maxPages = (int) env()['CHEVERETO_MAX_PAGES'];
+ $maxPages = (int) envTrialAware()['CHEVERETO_MAX_PAGES'];
if ($maxPages !== 0) {
if ($pagesCount >= $maxPages) {
$is_error = true;
$error_title = _s('Quota limit reached');
$error_message = _s(
- 'Maximum number of pages allowed reached (limit %s).',
+ 'Maximum number of pages allowed reached (limit %s).' . trialAwareLabel(),
$maxPages
);
$handler::setVar('error_title', $error_title);
@@ -1168,7 +1170,28 @@ return function (Handler $handler) {
// 'page_file_path_absolute' => $POST['page_file_path_absolute'],
]);
}
- $mailApis = ['smtp'];
+ $mailApis = [
+ 'smtp',
+ 'ahasend',
+ 'ses',
+ 'azure',
+ 'brevo',
+ 'infobip',
+ 'mailgun',
+ 'mailjet',
+ 'mailomat',
+ 'mailpace',
+ 'mailersend',
+ 'mailtrap',
+ 'mandrill',
+ 'microsoftgraph',
+ 'postal',
+ 'postmark',
+ 'resend',
+ 'scaleway',
+ 'sendgrid',
+ 'sweego',
+ ];
if (env()['CHEVERETO_SERVICING'] !== 'docker') {
$mailApis[] = 'mail';
}
@@ -1262,14 +1285,6 @@ return function (Handler $handler) {
'validate' => isset($POST['email_mode']) && in_array($POST['email_mode'], $mailApis, true),
'error_msg' => _s('Invalid email mode'),
],
- 'email_smtp_server_port' => [
- 'validate' => isset($POST['email_smtp_server_port']) && $POST['email_smtp_server_port'] > 0 && $POST['email_smtp_server_port'] < 65536,
- 'error_msg' => _s('Invalid SMTP port'),
- ],
- 'email_smtp_server_security' => [
- 'validate' => isset($POST['email_smtp_server_security']) && in_array($POST['email_smtp_server_security'], ['tls', 'ssl', 'unsecured'], true),
- 'error_msg' => _s('Invalid SMTP security'),
- ],
'website_mode' => [
'validate' => isset($POST['website_mode']) && in_array($POST['website_mode'], ['community', 'personal'], true),
'error_msg' => _s('Invalid website mode'),
@@ -1635,52 +1650,116 @@ return function (Handler $handler) {
}
}
- if (isset($POST['email_mode']) && $POST['email_mode'] === 'smtp') {
- $email_smtp_validate = [
- 'email_smtp_server' => _s('Invalid SMTP server'),
- // 'email_smtp_server_username' => _s('Invalid SMTP username'),
+ $emailMode = $POST['email_mode'] ?? '';
+ $emailApiRequiredFields = [
+ 'smtp' => ['email_smtp_server', 'email_smtp_server_port', 'email_smtp_server_security'],
+ 'ahasend' => ['email_ahasend_api_key'],
+ 'ses' => ['email_ses_access_key', 'email_ses_secret_key'],
+ 'azure' => ['email_azure_resource_name', 'email_azure_key'],
+ 'brevo' => ['email_brevo_api_key'],
+ 'infobip' => ['email_infobip_api_key', 'email_infobip_base_url'],
+ 'mailersend' => ['email_mailersend_api_key'],
+ 'mailgun' => ['email_mailgun_api_key', 'email_mailgun_domain'],
+ 'mailjet' => ['email_mailjet_access_key', 'email_mailjet_secret_key'],
+ 'mailomat' => ['email_mailomat_api_key'],
+ 'mailpace' => ['email_mailpace_api_token'],
+ 'mailtrap' => ['email_mailtrap_api_token'],
+ 'mandrill' => ['email_mandrill_api_key'],
+ 'microsoftgraph' => ['email_microsoftgraph_client_id', 'email_microsoftgraph_client_secret', 'email_microsoftgraph_tenant_id'],
+ 'postal' => ['email_postal_api_key', 'email_postal_base_url'],
+ 'postmark' => ['email_postmark_api_token'],
+ 'resend' => ['email_resend_api_key'],
+ 'scaleway' => ['email_scaleway_project_id', 'email_scaleway_api_key'],
+ 'sendgrid' => ['email_sendgrid_api_key'],
+ 'sweego' => ['email_sweego_api_key'],
+ ];
+ if ((env()['CHEVERETO_SERVICING'] !== 'server' && $emailMode === 'mail')
+ || (env()['CHEVERETO_CONTEXT'] === 'saas' && in_array($emailMode, ['smtp', 'mail'], true))
+ ) {
+ $validations['email_mode'] = [
+ 'validate' => false,
+ 'error_msg' => _s('The %s API is not available in this context', $emailMode),
];
- foreach ($email_smtp_validate as $k => $v) {
- $validations[$k] = [
- 'validate' => (bool) $POST[$k],
- 'error_msg' => $v,
+ }
+ if ($validations === [] && isset($emailApiRequiredFields[$emailMode])) {
+ foreach ($emailApiRequiredFields[$emailMode] as $field) {
+ $validations[$field] = [
+ 'validate' => ! empty($POST[$field]),
+ 'error_msg' => _s('Invalid value'),
];
}
-
- $email_validate = [
- 'email_smtp_server',
- 'email_smtp_server_port',
- // 'email_smtp_server_username',
- // 'email_smtp_server_password',
- 'email_smtp_server_security',
- ];
- $email_error = false;
- foreach ($email_validate as $k) {
- if (! $validations[$k]['validate']) {
- $email_error = true;
- }
+ $emailFieldsValid = array_reduce(
+ $emailApiRequiredFields[$emailMode],
+ fn (bool $carry, string $field) => $carry && ($validations[$field]['validate'] ?? false),
+ true
+ );
+ if ($emailMode === 'smtp') {
+ $validations['email_smtp_server'] = [
+ 'validate' => (bool) ($POST['email_smtp_server'] ?? ''),
+ 'error_msg' => _s('Invalid SMTP server'),
+ ];
+ $validations['email_smtp_server_port'] = [
+ 'validate' => isset($POST['email_smtp_server_port']) && $POST['email_smtp_server_port'] > 0 && $POST['email_smtp_server_port'] < 65536,
+ 'error_msg' => _s('Invalid SMTP port'),
+ ];
+ $validations['email_smtp_server_security'] = [
+ 'validate' => isset($POST['email_smtp_server_security']) && in_array($POST['email_smtp_server_security'], ['tls', 'ssl', 'unsecured'], true),
+ 'error_msg' => _s('Invalid SMTP security'),
+ ];
+ $emailFieldsValid = $validations['email_smtp_server']['validate']
+ && $validations['email_smtp_server_port']['validate']
+ && $validations['email_smtp_server_security']['validate'];
}
- if (! $email_error) {
+ if ($emailFieldsValid) {
try {
- $mail = new Mailer(true);
- $mail->Username = $POST['email_smtp_server_username'] ?? '';
- $mail->Password = $POST['email_smtp_server_password'] ?? '';
- $mail->SMTPAuth = $mail->Username !== '' || $mail->Password !== '';
- $mail->SMTPSecure = in_array($POST['email_smtp_server_security'], ['ssl', 'tls'])
- ? $POST['email_smtp_server_security']
- : '';
- $mail->SMTPAutoTLS = in_array($POST['email_smtp_server_security'], ['ssl', 'tls']);
- $mail->Host = (string) $POST['email_smtp_server'];
- $mail->Port = (int) $POST['email_smtp_server_port'];
- $mail->SMTPDebug = SMTP::DEBUG_SERVER;
- $GLOBALS['SMTPDebug'] = '';
- $mail->Debugoutput = function ($str) {
- $GLOBALS['SMTPDebug'] .= "{$str}\n";
+ $dsn = match ($emailMode) {
+ 'smtp' => (function () use ($POST): string {
+ $username = urlencode($POST['email_smtp_server_username'] ?? '');
+ $password = urlencode($POST['email_smtp_server_password'] ?? '');
+ $host = (string) $POST['email_smtp_server'];
+ $port = (int) $POST['email_smtp_server_port'];
+ $security = $POST['email_smtp_server_security'];
+ $scheme = $security === 'ssl' ? 'smtps' : 'smtp';
+ $auth = ($username !== '' || $password !== '') ? "{$username}:{$password}@" : '';
+ $dsn = "{$scheme}://{$auth}{$host}:{$port}";
+ if ($security === 'tls') {
+ $dsn .= '?encryption=tls';
+ } elseif (! in_array($security, ['ssl', 'tls'], true)) {
+ $dsn .= '?verify_peer=false';
+ }
+
+ return $dsn;
+ })(),
+ 'ahasend' => 'ahasend+api://' . urlencode($POST['email_ahasend_api_key']) . '@default',
+ 'ses' => 'ses+api://' . urlencode($POST['email_ses_access_key']) . ':' . urlencode($POST['email_ses_secret_key']) . '@default',
+ 'azure' => 'azure+api://' . urlencode($POST['email_azure_resource_name']) . ':' . urlencode($POST['email_azure_key']) . '@default',
+ 'brevo' => 'brevo+api://' . urlencode($POST['email_brevo_api_key']) . '@default',
+ 'infobip' => 'infobip+api://' . urlencode($POST['email_infobip_api_key']) . '@' . urlencode($POST['email_infobip_base_url']),
+ 'mailersend' => 'mailersend+api://' . urlencode($POST['email_mailersend_api_key']) . '@default',
+ 'mailgun' => 'mailgun+api://' . urlencode($POST['email_mailgun_api_key']) . ':' . urlencode($POST['email_mailgun_domain']) . '@default',
+ 'mailjet' => 'mailjet+api://' . urlencode($POST['email_mailjet_access_key']) . ':' . urlencode($POST['email_mailjet_secret_key']) . '@default',
+ 'mailomat' => 'mailomat+api://' . urlencode($POST['email_mailomat_api_key']) . '@default',
+ 'mailpace' => 'mailpace+api://' . urlencode($POST['email_mailpace_api_token']) . '@default',
+ 'mailtrap' => 'mailtrap+api://' . urlencode($POST['email_mailtrap_api_token']) . '@default',
+ 'mandrill' => 'mandrill+api://' . urlencode($POST['email_mandrill_api_key']) . '@default',
+ 'microsoftgraph' => 'microsoftgraph+api://' . urlencode($POST['email_microsoftgraph_client_id']) . ':' . urlencode($POST['email_microsoftgraph_client_secret']) . '@default?tenantId=' . urlencode($POST['email_microsoftgraph_tenant_id']),
+ 'postal' => 'postal+api://' . urlencode($POST['email_postal_api_key']) . '@' . urlencode($POST['email_postal_base_url']),
+ 'postmark' => 'postmark+api://' . urlencode($POST['email_postmark_api_token']) . '@default',
+ 'resend' => 'resend+api://' . urlencode($POST['email_resend_api_key']) . '@default',
+ 'scaleway' => 'scaleway+api://' . urlencode($POST['email_scaleway_project_id']) . ':' . urlencode($POST['email_scaleway_api_key']) . '@default',
+ 'sendgrid' => 'sendgrid+api://' . urlencode($POST['email_sendgrid_api_key']) . '@default',
+ 'sweego' => 'sweego+api://' . urlencode($POST['email_sweego_api_key']) . '@default',
};
- $GLOBALS['SMTPDebug'] = "SMTP Debug>>\n" . $GLOBALS['SMTPDebug'];
- $mail->SmtpConnect();
- } catch (Exception $e) {
- $GLOBALS['SMTPDebug'] = "SMTP Exception>>\n" . ($mail->ErrorInfo ?: $e->getMessage());
+ $transport = Transport::fromDsn($dsn);
+ if ($transport instanceof SmtpTransport) {
+ $GLOBALS['SMTPDebug'] = '';
+ $transport->start();
+ $GLOBALS['SMTPDebug'] = "SMTP Debug>>\nConnected successfully";
+ } else {
+ $GLOBALS['SMTPDebug'] = "Transport configured: {$emailMode}";
+ }
+ } catch (Throwable $e) {
+ $GLOBALS['SMTPDebug'] = "Error>>\n" . $e->getMessage();
}
}
}
diff --git a/app/legacy/routes/image.php b/app/legacy/routes/image.php
index 8a79bcc..1641a86 100644
--- a/app/legacy/routes/image.php
+++ b/app/legacy/routes/image.php
@@ -292,6 +292,7 @@ return function (Handler $handler) {
$meta_description = $image['description'];
} else {
$image_tr = [
+ '%s' => _s(mb_ucfirst($image['type'])),
'%i' => $image[$image['title'] === null ? 'filename' : 'title'],
'%a' => $image['album']['name'] ?? '',
'%w' => getSetting('website_name'),
@@ -301,11 +302,11 @@ return function (Handler $handler) {
|| (
! ((bool) ($image['user']['is_private'] ?? false)) && isset($image['album']['name'])
)) {
- $meta_description = _s('Image %i in %a album', $image_tr);
+ $meta_description = _s('%s %i in %a album', $image_tr);
} elseif (isset($image['category']['id'])) {
- $meta_description = _s('Image %i in %c category', $image_tr);
+ $meta_description = _s('%s %i in %c category', $image_tr);
} else {
- $meta_description = _s('Image %i hosted in %w', $image_tr);
+ $meta_description = _s('%s %i hosted in %w', $image_tr);
}
}
$handler::setVar('meta_description', $meta_description ?? '');
diff --git a/app/routes/tenants-api-v4.php b/app/routes/tenants-api-v4.php
index 5dacd30..b20bbd3 100644
--- a/app/routes/tenants-api-v4.php
+++ b/app/routes/tenants-api-v4.php
@@ -16,10 +16,12 @@ use Chevereto\Http\Controllers\Api\V4\TenantPatch;
use Chevereto\Http\Controllers\Api\V4\TenantPlanDelete;
use Chevereto\Http\Controllers\Api\V4\TenantPlanGet;
use Chevereto\Http\Controllers\Api\V4\TenantPlanPatch;
+use Chevereto\Http\Controllers\Api\V4\TenantsAuthVerifyPost;
use Chevereto\Http\Controllers\Api\V4\TenantsGet;
use Chevereto\Http\Controllers\Api\V4\TenantsPlansGet;
use Chevereto\Http\Controllers\Api\V4\TenantsPlansPost;
use Chevereto\Http\Controllers\Api\V4\TenantsPost;
+use Chevereto\Http\Controllers\Api\V4\TenantUserPasswordResetPatch;
use Chevereto\Http\Middlewares\RestrictIpAccess;
use Chevereto\Http\Middlewares\SignedRequest;
use Chevereto\Http\Middlewares\TenantsApiKeyAuthorization;
@@ -28,6 +30,10 @@ use function Chevere\Router\routes;
use function Chevereto\Vars\env;
return routes(
+ route(
+ '/_/api/4/auth/verify',
+ POST: TenantsAuthVerifyPost::class,
+ ),
route(
'/_/api/4/tenants',
POST: TenantsPost::class,
@@ -43,6 +49,10 @@ return routes(
'/_/api/4/tenants/{id}/install',
POST: TenantInstallPost::class,
),
+ route(
+ '/_/api/4/tenants/{id}/user-password-reset',
+ PATCH: TenantUserPasswordResetPatch::class,
+ ),
route(
'/_/api/4/tenants-plans',
POST: TenantsPlansPost::class,
diff --git a/app/routes/tenants-internal-api-v4.php b/app/routes/tenants-internal-api-v4.php
new file mode 100644
index 0000000..32f03bd
--- /dev/null
+++ b/app/routes/tenants-internal-api-v4.php
@@ -0,0 +1,29 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Chevereto\Http\Controllers\Api\V4\TenantsConfigTraefikGet;
+use Chevereto\Http\Middlewares\RestrictIpAccess;
+use Chevereto\Http\Middlewares\TenantsApiKeyAuthorization;
+use function Chevere\Router\route;
+use function Chevere\Router\routes;
+
+return routes(
+ route(
+ '/_/api/4/config/traefik',
+ GET: TenantsConfigTraefikGet::class,
+ ),
+)
+ ->withAppendMiddleware(
+ RestrictIpAccess::with(
+ allowList: '127.0.0.1,::1,172.16.0.0/12,192.168.65.0/24',
+ ),
+ TenantsApiKeyAuthorization::class,
+ );
diff --git a/app/schemas/mysql-8/_/tenants_stats.sql b/app/schemas/mysql-8/_/tenants_stats.sql
index 192ebe3..237f70f 100644
--- a/app/schemas/mysql-8/_/tenants_stats.sql
+++ b/app/schemas/mysql-8/_/tenants_stats.sql
@@ -17,6 +17,7 @@ CREATE TABLE `%table_root_prefix%tenants_stats` (
`pages` INT UNSIGNED NOT NULL DEFAULT '0',
`storages` INT UNSIGNED NOT NULL DEFAULT '0',
`categories` INT UNSIGNED NOT NULL DEFAULT '0',
+ `login_providers` INT UNSIGNED NOT NULL DEFAULT '0',
PRIMARY KEY (`tenant_id`),
KEY `updated_at` (`updated_at`),
FOREIGN KEY (tenant_id) REFERENCES `%table_root_prefix%tenants` (id) ON DELETE CASCADE
diff --git a/app/src/Http/Controllers/Api/V4/TenantPlanPatch.php b/app/src/Http/Controllers/Api/V4/TenantPlanPatch.php
index 14c1c38..38a4638 100644
--- a/app/src/Http/Controllers/Api/V4/TenantPlanPatch.php
+++ b/app/src/Http/Controllers/Api/V4/TenantPlanPatch.php
@@ -33,6 +33,7 @@ class TenantPlanPatch extends Controller
public function __invoke(string $id): void
{
try {
+ $this->tenants->getPlan($id);
$this->tenants->editPlan(
planId: $id,
limits: $this->bodyParsed()->optional('limits')?->array(),
diff --git a/app/src/Http/Controllers/Api/V4/TenantUserPasswordResetPatch.php b/app/src/Http/Controllers/Api/V4/TenantUserPasswordResetPatch.php
new file mode 100644
index 0000000..8243f7d
--- /dev/null
+++ b/app/src/Http/Controllers/Api/V4/TenantUserPasswordResetPatch.php
@@ -0,0 +1,95 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Chevereto\Http\Controllers\Api\V4;
+
+use Chevere\Http\Attributes\Response;
+use Chevere\Http\Controller;
+use Chevere\Http\Exceptions\ControllerException;
+use Chevere\Http\Status;
+use Chevere\Parameter\Interfaces\ParameterInterface;
+use Chevere\Writer\StreamWriter;
+use Chevereto\Exceptions\NotFoundException;
+use Chevereto\PCRE;
+use Chevereto\Tenants\Tenants;
+use function Chevere\Parameter\arrayp;
+use function Chevere\Parameter\string;
+use function Chevere\Standard\randomString;
+use function Chevere\Writer\streamTemp;
+use function Chevereto\Legacy\G\str_replace_last;
+use function Chevereto\Legacy\runAppCommand;
+use function Chevereto\Vars\env;
+
+#[Response(
+ new Status(201, fail: 404, conflict: 409),
+)]
+class TenantUserPasswordResetPatch extends Controller
+{
+ public function __construct(
+ private Tenants $tenants,
+ ) {
+ }
+
+ public function __invoke(string $id): string
+ {
+ try {
+ $tenant = $this->tenants->getTenant(tenantId: $id);
+ } catch (NotFoundException) {
+ throw new ControllerException(
+ 'Tenant not found',
+ 404
+ );
+ }
+ $password = $this->bodyParsed()->optional('password')?->string()
+ ?? randomString(16);
+ $logger = new StreamWriter(streamTemp());
+ $exit = runAppCommand(
+ command: [
+ '-C', 'password-reset',
+ '-u', $this->bodyParsed()->required('username')->string(),
+ '-x', $password,
+ ],
+ env: array_merge(env(), [
+ 'CHEVERETO_TENANT' => $tenant->id,
+ 'CHEVERETO_CACHE_KEY_PREFIX' => str_replace_last(
+ '_:',
+ '',
+ env()['CHEVERETO_CACHE_KEY_PREFIX']
+ ),
+ 'CHEVERETO_DB_TABLE_PREFIX' => str_replace_last(
+ '_',
+ '',
+ env()['CHEVERETO_DB_TABLE_PREFIX']
+ ),
+ ]),
+ isVerbose: true,
+ logger: $logger
+ );
+ xr($exit);
+ if ($exit === 0) {
+ return $password;
+ }
+
+ throw new ControllerException(
+ 'Password reset failed',
+ 500
+ );
+ }
+
+ public static function acceptBody(): ParameterInterface
+ {
+ return arrayp(
+ username: string(PCRE::USER_USERNAME->value),
+ )->withOptional(
+ password: string(PCRE::USER_PASSWORD->value)
+ );
+ }
+}
diff --git a/app/src/Http/Controllers/Api/V4/TenantsAuthVerifyPost.php b/app/src/Http/Controllers/Api/V4/TenantsAuthVerifyPost.php
new file mode 100644
index 0000000..6867cb7
--- /dev/null
+++ b/app/src/Http/Controllers/Api/V4/TenantsAuthVerifyPost.php
@@ -0,0 +1,28 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Chevereto\Http\Controllers\Api\V4;
+
+use Chevere\Http\Attributes\Response;
+use Chevere\Http\Controller;
+use Chevere\Http\Header;
+use Chevere\Http\Status;
+
+#[Response(
+ new Status(200),
+ new Header('Content-Type', 'application/json'),
+)]
+class TenantsAuthVerifyPost extends Controller
+{
+ public function __invoke(): void
+ {
+ }
+}
diff --git a/app/src/Http/Controllers/Api/V4/TenantsConfigTraefikGet.php b/app/src/Http/Controllers/Api/V4/TenantsConfigTraefikGet.php
new file mode 100644
index 0000000..ccd9597
--- /dev/null
+++ b/app/src/Http/Controllers/Api/V4/TenantsConfigTraefikGet.php
@@ -0,0 +1,43 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Chevereto\Http\Controllers\Api\V4;
+
+use Chevere\Http\Attributes\Response;
+use Chevere\Http\Controller;
+use Chevere\Http\Header;
+use Chevere\Http\Status;
+use Chevereto\Tenants\Tenants;
+use Chevereto\Tenants\TenantsConfig;
+use function Chevereto\Vars\env;
+
+#[Response(
+ new Status(200),
+ new Header('Content-Type', 'application/json'),
+)]
+class TenantsConfigTraefikGet extends Controller
+{
+ public function __construct(
+ private Tenants $tenants,
+ private TenantsConfig $tenantsConfig,
+ ) {
+ }
+
+ public function __invoke(): array
+ {
+ return $this->tenantsConfig->getConfig(
+ tenants: $this->tenants,
+ service: env()['CHEVERETO_SERVICE_NAME'],
+ port: 80,
+ middleware: ['cf-only']
+ );
+ }
+}
diff --git a/app/src/Legacy/Classes/DB.php b/app/src/Legacy/Classes/DB.php
index a1a5739..83a24e8 100644
--- a/app/src/Legacy/Classes/DB.php
+++ b/app/src/Legacy/Classes/DB.php
@@ -118,7 +118,9 @@ class DB
];
$this->pdo_options = $this->pdo_default_attrs + $this->pdoAttrs;
$this->pdo_options[PDO::ATTR_ERRMODE] = PDO::ERRMODE_EXCEPTION;
- $this->pdo_options[PDO::MYSQL_ATTR_INIT_COMMAND] = "SET time_zone = '+00:00', NAMES 'utf8mb4'";
+ $attrInitOption = class_exists('Pdo\\Mysql') ? \Pdo\Mysql::ATTR_INIT_COMMAND : PDO::MYSQL_ATTR_INIT_COMMAND;
+ $this->pdo_options[] = "SET time_zone = '+00:00', NAMES 'utf8mb4'";
+ $this->pdo_options[$attrInitOption] = "SET time_zone = '+00:00', NAMES 'utf8mb4'";
self::$dbh = new PDO($pdo_connect, $this->user, $this->pass, $this->pdo_options);
self::$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
self::$instance = $this;
diff --git a/app/src/Legacy/Classes/Settings.php b/app/src/Legacy/Classes/Settings.php
index 0c01bf7..ce860a9 100644
--- a/app/src/Legacy/Classes/Settings.php
+++ b/app/src/Legacy/Classes/Settings.php
@@ -32,6 +32,34 @@ class Settings
'email_smtp_server_password',
'email_smtp_server_port',
'email_smtp_server_username',
+ 'email_ahasend_api_key',
+ 'email_ses_access_key',
+ 'email_ses_secret_key',
+ 'email_azure_resource_name',
+ 'email_azure_key',
+ 'email_brevo_api_key',
+ 'email_infobip_api_key',
+ 'email_infobip_base_url',
+ 'email_mailgun_api_key',
+ 'email_mailgun_domain',
+ 'email_mailjet_access_key',
+ 'email_mailjet_secret_key',
+ 'email_mailomat_api_key',
+ 'email_mailpace_api_token',
+ 'email_mailersend_api_key',
+ 'email_mailtrap_api_token',
+ 'email_mandrill_api_key',
+ 'email_microsoftgraph_client_id',
+ 'email_microsoftgraph_client_secret',
+ 'email_microsoftgraph_tenant_id',
+ 'email_postal_api_key',
+ 'email_postal_base_url',
+ 'email_postmark_api_token',
+ 'email_resend_api_key',
+ 'email_scaleway_project_id',
+ 'email_scaleway_api_key',
+ 'email_sendgrid_api_key',
+ 'email_sweego_api_key',
'captcha_secret',
'disqus_secret_key',
'akismet_api_key',
@@ -312,6 +340,13 @@ class Settings
'upload_image_path' => 'images',
],
],
+ 'CHEVERETO_ENABLE_GUESTS' => ['0',
+ [
+ 'enable_api_guest' => false,
+ 'guest_uploads' => false,
+ 'guest_albums' => false,
+ ],
+ ],
];
public const STOCK = [
@@ -421,6 +456,34 @@ class Settings
'theme_palette_user_select' => true,
'enable_api_user' => true,
'enable_api_guest' => false,
+ 'email_ahasend_api_key' => '',
+ 'email_ses_access_key' => '',
+ 'email_ses_secret_key' => '',
+ 'email_azure_resource_name' => '',
+ 'email_azure_key' => '',
+ 'email_brevo_api_key' => '',
+ 'email_infobip_api_key' => '',
+ 'email_infobip_base_url' => '',
+ 'email_mailgun_api_key' => '',
+ 'email_mailgun_domain' => '',
+ 'email_mailjet_access_key' => '',
+ 'email_mailjet_secret_key' => '',
+ 'email_mailomat_api_key' => '',
+ 'email_mailpace_api_token' => '',
+ 'email_mailersend_api_key' => '',
+ 'email_mailtrap_api_token' => '',
+ 'email_mandrill_api_key' => '',
+ 'email_microsoftgraph_client_id' => '',
+ 'email_microsoftgraph_client_secret' => '',
+ 'email_microsoftgraph_tenant_id' => '',
+ 'email_postal_api_key' => '',
+ 'email_postal_base_url' => '',
+ 'email_postmark_api_token' => '',
+ 'email_resend_api_key' => '',
+ 'email_scaleway_project_id' => '',
+ 'email_scaleway_api_key' => '',
+ 'email_sendgrid_api_key' => '',
+ 'email_sweego_api_key' => '',
];
public const USERNAME_MIN_LENGTH = 3;
@@ -804,7 +867,6 @@ class Settings
foreach ($binds as $bindK => $bindV) {
$db->bind($bindK, $bindV);
}
-
$return = $db->exec();
if ($return) {
self::cache();
diff --git a/app/src/Legacy/Classes/Stat.php b/app/src/Legacy/Classes/Stat.php
index d3923f0..861b9b3 100644
--- a/app/src/Legacy/Classes/Stat.php
+++ b/app/src/Legacy/Classes/Stat.php
@@ -17,7 +17,9 @@ use LogicException;
use OverflowException;
use function Chevere\Message\message;
use function Chevereto\Legacy\G\datetimegmt;
+use function Chevereto\Legacy\trialAwareLabel;
use function Chevereto\Vars\env;
+use function Chevereto\Vars\envTrialAware;
class Stat
{
@@ -127,7 +129,7 @@ class Stat
600
);
}
- $maxLimit = (int) (env()[$env] ?? 0);
+ $maxLimit = (int) (envTrialAware()[$env] ?? 0);
if ($maxLimit === 0) {
return;
}
@@ -135,7 +137,7 @@ class Stat
if (($count + $add) > $maxLimit) {
throw new OverflowException(
message(
- 'Maximum %t% reached (limit %s%).',
+ 'Maximum %t% reached (limit %s%).' . trialAwareLabel(),
t: $env,
s: strval($maxLimit),
),
@@ -449,6 +451,7 @@ class Stat
'file_likes' => 's.stat_image_likes',
'album_likes' => 's.stat_album_likes',
'storage_used' => 's.stat_disk_used',
+ 'login_providers' => 'u.login_providers',
'admins' => 'u.admins',
'managers' => 'u.managers',
'pages' => 'u.pages',
@@ -469,9 +472,7 @@ class Stat
$selectColumns = implode(',', $pairs);
$select = match ($asJsonColumn) {
true => << << 0) {
$currentTotalTags = Stat::getTotals()['tags'] ?? 0;
if ($currentTotalTags >= $maxTags) {
diff --git a/app/src/Legacy/Classes/User.php b/app/src/Legacy/Classes/User.php
index db24a84..9526a47 100644
--- a/app/src/Legacy/Classes/User.php
+++ b/app/src/Legacy/Classes/User.php
@@ -37,7 +37,9 @@ use function Chevereto\Legacy\getSetting;
use function Chevereto\Legacy\headersNoCache;
use function Chevereto\Legacy\linkify_redirector;
use function Chevereto\Legacy\system_notification_email;
+use function Chevereto\Legacy\trialAwareLabel;
use function Chevereto\Vars\env;
+use function Chevereto\Vars\envTrialAware;
class User
{
@@ -134,37 +136,40 @@ class User
} else {
$userAlbums = [];
$user_stream = self::getStreamAlbum($var);
- if ($user_stream === null || $user_stream['user_album_count'] === 0) {
+ if ($user_stream === null) {
return [];
}
+ $userAlbumsCount = $user_stream['user_album_count'];
unset($user_stream['user_album_count']);
$userAlbums['stream'] = $user_stream;
$map = [];
$children = [];
- $columns = [
- 'album_id',
- 'album_name',
- 'album_privacy',
- 'album_parent_id',
- 'album_image_count',
- 'album_cover_id',
- ];
- $columnsString = implode(', ', $columns);
- $tableAlbums = DB::getTable('albums');
- $db = DB::getInstance();
- $db->query(
- <<bind(':limit', intval(env()['CHEVERETO_MAX_USER_ALBUMS_LIST']));
- $db->bind(':image_user_id', $id);
- $user_albums_db = $db->fetchAll();
- if ($user_albums_db) {
- $userAlbums += $user_albums_db;
+ if ($userAlbumsCount > 0) {
+ $columns = [
+ 'album_id',
+ 'album_name',
+ 'album_privacy',
+ 'album_parent_id',
+ 'album_image_count',
+ 'album_cover_id',
+ ];
+ $columnsString = implode(', ', $columns);
+ $tableAlbums = DB::getTable('albums');
+ $db = DB::getInstance();
+ $db->query(
+ <<bind(':limit', intval(env()['CHEVERETO_MAX_USER_ALBUMS_LIST']));
+ $db->bind(':image_user_id', $id);
+ $user_albums_db = $db->fetchAll();
+ if ($user_albums_db) {
+ $userAlbums += $user_albums_db;
+ }
}
foreach ($userAlbums as $k => &$v) {
$album_id = isset($v['album_id'])
@@ -343,7 +348,7 @@ class User
if (! array_key_exists($role, $roles)) {
throw new Exception('Invalid role', 600);
}
- $maxLimit = (int) env()[$roleHandle] ?? 0;
+ $maxLimit = (int) envTrialAware()[$roleHandle] ?? 0;
if ($maxLimit === 0) {
return;
}
@@ -358,7 +363,7 @@ class User
if (($count + 1) > $maxLimit) {
throw new OverflowException(
message(
- 'Maximum %u% for role %r% reached (limit %c%)',
+ 'Maximum %u% for role %r% reached (limit %c%)' . trialAwareLabel(),
u: _n('user', 'users', 20),
c: strval($maxLimit),
r: mb_strtolower($roleLabel),
diff --git a/app/src/Legacy/functions.php b/app/src/Legacy/functions.php
index 1c8e4a0..eb1a8c2 100644
--- a/app/src/Legacy/functions.php
+++ b/app/src/Legacy/functions.php
@@ -31,7 +31,6 @@ use Chevereto\Legacy\Classes\KeyValue;
use Chevereto\Legacy\Classes\KeyValueNull;
use Chevereto\Legacy\Classes\L10n;
use Chevereto\Legacy\Classes\Login;
-use Chevereto\Legacy\Classes\Mailer;
use Chevereto\Legacy\Classes\Settings;
use Chevereto\Legacy\Classes\StorageApis;
use Chevereto\Legacy\Classes\Upload;
@@ -56,9 +55,12 @@ use LogicException;
use OutOfBoundsException;
use OverflowException;
use PDO;
-use PHPMailer\PHPMailer\SMTP;
use Redis;
use RuntimeException;
+use Symfony\Component\Mailer\Mailer;
+use Symfony\Component\Mailer\Transport;
+use Symfony\Component\Mime\Address;
+use Symfony\Component\Mime\Email;
use Symfony\Component\Process\Process;
use Throwable;
use function Chevere\Filesystem\filePhpForPath;
@@ -94,6 +96,7 @@ use function Chevereto\Legacy\G\starts_with;
use function Chevereto\Legacy\G\unlinkIfExists;
use function Chevereto\Vars\cookie;
use function Chevereto\Vars\env;
+use function Chevereto\Vars\envTrialAware;
use function Chevereto\Vars\post;
use function Chevereto\Vars\server;
use function Chevereto\Vars\session;
@@ -195,55 +198,76 @@ function send_mail($to, $subject, $body): bool
if (! filter_var($to, FILTER_VALIDATE_EMAIL)) {
throw new Exception('Invalid to email', 100);
}
- $writer = new StreamWriter(streamFor('php://temp', 'r+'));
$body = trim($body);
- $mail = new Mailer();
- $mail->SMTPDebug = SMTP::DEBUG_SERVER;
- $mail->Debugoutput = function ($str, $level) use ($writer) {
- $writer->write("{$str} \n");
- };
- $alt_body = $mail->html2text($body);
- $mail->CharSet = 'UTF-8';
- if (getSetting('email_mode') === 'smtp') {
- $mail->isSMTP();
- $mail->Username = getSetting('email_smtp_server_username') ?? '';
- $mail->Password = getSetting('email_smtp_server_password') ?? '';
- $mail->SMTPAuth = $mail->Username !== '' || $mail->Password !== '';
- $mail->SMTPSecure = in_array(getSetting('email_smtp_server_security'), ['ssl', 'tls'], true)
- ? getSetting('email_smtp_server_security')
- : '';
- $mail->SMTPAutoTLS = in_array(getSetting('email_smtp_server_security'), ['ssl', 'tls'], true);
- $mail->Port = getSetting('email_smtp_server_port');
- $mail->Host = getSetting('email_smtp_server');
- }
- $mail->Timeout = 30;
- $mail->Subject = $subject;
- if ($body !== $alt_body) {
- $mail->IsHTML(true);
- $mail->Body = $mail->normalizeBreaks($body);
- $mail->AltBody = $mail->normalizeBreaks($alt_body);
- } else {
- $mail->Body = $body;
- }
- $mail->addAddress($to);
+ $alt_body = strip_tags($body);
+ $email = new Email();
+ $email->subject($subject);
+ $email->to($to);
+ $email->from(new Address($from[0], $from[1]));
if ($reply_to && is_array($reply_to)) {
foreach ($reply_to as $v) {
- $mail->addReplyTo($v);
+ $email->addReplyTo($v);
}
}
- $mail->setFrom($from[0], $from[1]);
- if ($mail->Send()) {
- return true;
+ if ($body !== $alt_body) {
+ $email->html($body);
+ $email->text($alt_body);
+ } else {
+ $email->text($body);
}
- $mailerWrap = "\n----------- MAILER DEBUG -----------\n\n";
- $error = str_replace('-', '>', $mailerWrap)
- . $writer->__toString()
- . str_replace('-', '<', $mailerWrap);
- writers()->error()
- ->write($error);
- xr(mailer: $error, to: $to, subject: $subject, body: $body);
+ $emailMode = getSetting('email_mode') ?? 'mail';
+ $dsn = match ($emailMode) {
+ 'smtp' => (function (): string {
+ $username = urlencode(getSetting('email_smtp_server_username') ?? '');
+ $password = urlencode(getSetting('email_smtp_server_password') ?? '');
+ $host = getSetting('email_smtp_server') ?? 'localhost';
+ $port = getSetting('email_smtp_server_port') ?? 25;
+ $security = getSetting('email_smtp_server_security');
+ $scheme = $security === 'ssl' ? 'smtps' : 'smtp';
+ $auth = ($username !== '' || $password !== '') ? "{$username}:{$password}@" : '';
+ $dsn = "{$scheme}://{$auth}{$host}:{$port}";
+ if ($security === 'tls') {
+ $dsn .= '?encryption=tls';
+ } elseif (! in_array($security, ['ssl', 'tls'], true)) {
+ $dsn .= '?verify_peer=false';
+ }
- throw new Exception($mail->ErrorInfo, 606);
+ return $dsn;
+ })(),
+ 'ahasend' => 'ahasend+api://' . urlencode(getSetting('email_ahasend_api_key') ?? '') . '@default',
+ 'ses' => 'ses+api://' . urlencode(getSetting('email_ses_access_key') ?? '') . ':' . urlencode(getSetting('email_ses_secret_key') ?? '') . '@default',
+ 'azure' => 'azure+api://' . urlencode(getSetting('email_azure_resource_name') ?? '') . ':' . urlencode(getSetting('email_azure_key') ?? '') . '@default',
+ 'brevo' => 'brevo+api://' . urlencode(getSetting('email_brevo_api_key') ?? '') . '@default',
+ 'infobip' => 'infobip+api://' . urlencode(getSetting('email_infobip_api_key') ?? '') . '@' . urlencode(getSetting('email_infobip_base_url') ?? 'default'),
+ 'mailgun' => 'mailgun+api://' . urlencode(getSetting('email_mailgun_api_key') ?? '') . ':' . urlencode(getSetting('email_mailgun_domain') ?? '') . '@default',
+ 'mailjet' => 'mailjet+api://' . urlencode(getSetting('email_mailjet_access_key') ?? '') . ':' . urlencode(getSetting('email_mailjet_secret_key') ?? '') . '@default',
+ 'mailomat' => 'mailomat+api://' . urlencode(getSetting('email_mailomat_api_key') ?? '') . '@default',
+ 'mailpace' => 'mailpace+api://' . urlencode(getSetting('email_mailpace_api_token') ?? '') . '@default',
+ 'mailersend' => 'mailersend+api://' . urlencode(getSetting('email_mailersend_api_key') ?? '') . '@default',
+ 'mailtrap' => 'mailtrap+api://' . urlencode(getSetting('email_mailtrap_api_token') ?? '') . '@default',
+ 'mandrill' => 'mandrill+api://' . urlencode(getSetting('email_mandrill_api_key') ?? '') . '@default',
+ 'microsoftgraph' => 'microsoftgraph+api://' . urlencode(getSetting('email_microsoftgraph_client_id') ?? '') . ':' . urlencode(getSetting('email_microsoftgraph_client_secret') ?? '') . '@default?tenantId=' . urlencode(getSetting('email_microsoftgraph_tenant_id') ?? ''),
+ 'postal' => 'postal+api://' . urlencode(getSetting('email_postal_api_key') ?? '') . '@' . urlencode(getSetting('email_postal_base_url') ?? 'default'),
+ 'postmark' => 'postmark+api://' . urlencode(getSetting('email_postmark_api_token') ?? '') . '@default',
+ 'resend' => 'resend+api://' . urlencode(getSetting('email_resend_api_key') ?? '') . '@default',
+ 'scaleway' => 'scaleway+api://' . urlencode(getSetting('email_scaleway_project_id') ?? '') . ':' . urlencode(getSetting('email_scaleway_api_key') ?? '') . '@default',
+ 'sendgrid' => 'sendgrid+api://' . urlencode(getSetting('email_sendgrid_api_key') ?? '') . '@default',
+ 'sweego' => 'sweego+api://' . urlencode(getSetting('email_sweego_api_key') ?? '') . '@default',
+ default => 'sendmail://default',
+ };
+
+ try {
+ $transport = Transport::fromDsn($dsn);
+ $mailer = new Mailer($transport);
+ $mailer->send($email);
+ } catch (\Throwable $e) {
+ writers()->error()->write($e->getMessage());
+ xr(mailer: $e->getMessage(), to: $to, subject: $subject, body: $body);
+
+ throw new Exception($e->getMessage(), 606);
+ }
+
+ return true;
}
function get_chevereto_version(bool $full = true): string
@@ -1179,12 +1203,27 @@ function loaderHandler(
?? '';
$envVar['CHEVERETO_TENANT_HANDLE'] = '';
$envVar['CHEVERETO_DB_TABLE_ROOT_PREFIX'] = $envVar['CHEVERETO_DB_TABLE_PREFIX'];
- if ($envVar['CHEVERETO_ENABLE_TENANTS'] === '1') {
+ $envVar['CHEVERETO_CACHE_KEY_ROOT_PREFIX'] = $envVar['CHEVERETO_CACHE_KEY_PREFIX'];
+ // try {
+ // $xrArguments = [
+ // 'isEnabled' => true,
+ // 'isHttps' => false,
+ // 'host' => 'host.docker.internal',
+ // 'port' => 27420,
+ // ];
+
+ // new XrInstance(new Xr(...$xrArguments));
+ // } catch (Throwable) {
+ // // Silent failover
+ // }
+ $isTenantsApi = false;
+ if ($envVar['CHEVERETO_ENABLE_TENANTS'] === '1' || $envVar['CHEVERETO_TENANT'] !== '') {
$redis = new Redis();
$redis->connect($envVar['CHEVERETO_CACHE_HOST'], (int) $envVar['CHEVERETO_CACHE_PORT']);
if ($envVar['CHEVERETO_CACHE_PASSWORD'] !== '') {
$redis->auth($envVar['CHEVERETO_CACHE_PASSWORD']);
}
+ $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP);
$lookupNamespace = $envVar['CHEVERETO_CACHE_KEY_PREFIX'] . '_:';
if (PHP_SAPI === 'cli') {
$websiteId = $envVar['CHEVERETO_TENANT'];
@@ -1195,13 +1234,19 @@ function loaderHandler(
PLAIN;
exit(255);
}
- $websiteOptions = $redis->get($lookupNamespace . 'tenant:' . $websiteId);
- $websiteOptions = unserialize($websiteOptions);
- $hostname = $websiteOptions['hostname'];
+ /** @var array $websiteOptions */
+ $websiteOptions = $redis->get($lookupNamespace . 'tenant:' . $websiteId) ?: [];
+ $hostname = $websiteOptions['hostname'] ?? '';
} else {
$hostname = $_server['SERVER_NAME'];
+ if (($envVar['CHEVERETO_SERVICING'] ?? '') === 'docker'
+ && $hostname === 'host.docker.internal'
+ ) {
+ $hostname = $envVar['CHEVERETO_HOSTNAME'];
+ }
$isRootHostname = hash_equals($envVar['CHEVERETO_HOSTNAME'], $hostname);
- $isTenantsApi = $isRootHostname
+ $isLocalhost = in_array($hostname, ['localhost', '127.0.0.1', '::1', $envVar['CHEVERETO_SERVICE_NAME']], true);
+ $isTenantsApi = ($isRootHostname || $isLocalhost)
&& str_starts_with(
$_server['REQUEST_URI'],
$envVar['CHEVERETO_HOSTNAME_PATH']
@@ -1220,17 +1265,15 @@ function loaderHandler(
} else {
$websiteId = $redis->get($lookupNamespace . 'hostname:' . $hostname);
if ($websiteId === false) {
- if ($isRootHostname) {
- redirect($envVar['CHEVERETO_PROVIDER'] ?? 'https://chevereto.com');
- }
+ http_response_code(404);
echo <<get($lookupNamespace . 'tenant:' . $websiteId);
- $websiteOptions = unserialize($websiteOptions);
+ /** @var array $websiteOptions */
+ $websiteOptions = $redis->get($lookupNamespace . 'tenant:' . $websiteId) ?: [];
}
}
if ($websiteOptions === []) {
@@ -1268,8 +1311,8 @@ function loaderHandler(
$envVar['CHEVERETO_TENANT_HANDLE'] = "{$websiteId}_";
$envVar['CHEVERETO_HOSTNAME'] = $hostname;
if ($websiteId !== '') {
- $envVar['CHEVERETO_CACHE_KEY_PREFIX'] .= "{$websiteId}:"; // chv:ABC:
- $envVar['CHEVERETO_DB_TABLE_PREFIX'] .= "{$websiteId}_"; // chv_ABC_
+ $envVar['CHEVERETO_CACHE_KEY_PREFIX'] .= "{$websiteId}:"; // chv:ABC: (tenant)
+ $envVar['CHEVERETO_DB_TABLE_PREFIX'] .= "{$websiteId}_"; // chv_ABC_ (tenant)
} else {
$envVar['CHEVERETO_CACHE_KEY_PREFIX'] .= '_:'; // chv:_: (global)
$envVar['CHEVERETO_DB_TABLE_PREFIX'] .= '_'; // chv__ (global)
@@ -1423,6 +1466,7 @@ function loaderHandler(
if (env()['CHEVERETO_CACHE_PASSWORD'] !== '') {
$redis->auth(env()['CHEVERETO_CACHE_PASSWORD']);
}
+ $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP);
$keyValue = new KeyValue(
$redis,
env()['CHEVERETO_CACHE_KEY_PREFIX'],
@@ -1435,7 +1479,13 @@ function loaderHandler(
);
}
new Cache($keyValue);
- if ($_session === []
+ $isAPI = str_starts_with(
+ server()['REQUEST_URI'] ?? '',
+ env()['CHEVERETO_HOSTNAME_PATH']
+ . '/api/',
+ );
+ if (! ($isAPI || $isTenantsApi)
+ && $_session === []
&& session_status() === PHP_SESSION_NONE
&& ACCESS === 'web'
) {
@@ -1489,7 +1539,8 @@ function loaderHandler(
);
}
define('HTTP_APP_PROTOCOL', Config::host()->isHttps() ? 'https' : 'http');
- $httpPort = ! in_array(server()['SERVER_PORT'] ?? '80', ['80', '443'], false)
+ // TODO: Enable ENV to force using the port?
+ $httpPort = ! in_array(server()['SERVER_PORT'] ?? '80', ['80', '8080', '443'], false)
? ':' . server()['SERVER_PORT']
: '';
define('URL_APP_PUBLIC', HTTP_APP_PROTOCOL . '://' . Config::host()->hostname() . $httpPort . Config::host()->hostnamePath());
@@ -1851,6 +1902,14 @@ function getCounts(string ...$table): array
return DB::queryFetchSingle($query);
}
+function trialAwareLabel(): string
+{
+ return match ('1') {
+ env()['CHEVERETO_TRIAL'] => ' [trial]',
+ default => '',
+ };
+}
+
function assertMaxCount(string $table): void
{
$tablesToEnv = [
@@ -1867,7 +1926,7 @@ function assertMaxCount(string $table): void
code: 400
);
}
- $maxLimit = (int) (env()[$tablesToEnv[$table]] ?? 0);
+ $maxLimit = (int) (envTrialAware()[$tablesToEnv[$table]] ?? 0);
if ($maxLimit === 0) {
return;
}
@@ -1875,7 +1934,7 @@ function assertMaxCount(string $table): void
if (($count + 1) > $maxLimit) {
throw new OverflowException(
message(
- 'Maximum number of %t% reached (limit %s%).',
+ 'Maximum number of %t% reached (limit %s%).' . trialAwareLabel(),
t: $table,
s: strval($maxLimit),
),
@@ -1959,20 +2018,41 @@ function hashString(string $string): string
function getPoweredByRemarks(): array
{
+ $responsible = match (env()['CHEVERETO_CONTEXT']) {
+ 'saas' => _s('operator'),
+ default => _s('owner'),
+ };
$termsLink = 'Terms of Service';
- $softwareLicenseLink = 'Chevereto License';
- if (env()['CHEVERETO_EDITION'] === 'free') {
- $softwareLicenseLink = 'AGPL-3.0 license';
+ $softwareLicenseLink = match (env()['CHEVERETO_EDITION']) {
+ 'free' => 'AGPL-3.0 license',
+ default => 'Chevereto License',
+ };
+ $websiteName = getSetting('website_name');
+ if (strtolower($websiteName) === 'chevereto') {
+ $websiteName = 'This website';
}
- $providerLink = '' . env()['CHEVERETO_PROVIDER_NAME'] . '';
+ $provider = match (env()['CHEVERETO_CONTEXT']) {
+ 'saas' => [
+ 'url' => env()['CHEVERETO_PROVIDER_URL'],
+ 'label' => env()['CHEVERETO_PROVIDER_NAME'],
+ ],
+ default => [
+ 'url' => get_public_url(),
+ 'label' => $websiteName,
+ ],
+ };
+ $providerLink = message(
+ '{{ label }}',
+ ...$provider
+ );
$about = _s('This service is based on Chevereto %edition edition software licensed under the %license.', [
'%edition' => ucfirst(env()['CHEVERETO_EDITION']),
'%license' => $softwareLicenseLink,
]);
$liability = _s("This website is hosted in a service layer not provided by Chevereto Software, which hereby declare to do not have any control nor access to the management layer of this website and it won't be responsible for this service neither the damages that this service may cause.");
- $content = _s('File uploads are stored and served from storage facilities provided by %s and managed by The Service Operator.', $providerLink);
+ $content = _s('File uploads are stored and served from storage facilities provided and managed by the %s of this website.', $responsible);
if (env()['CHEVERETO_CONTEXT'] === 'saas') {
$about = _s('This service operates using Chevereto %edition edition software licensed under the %license.', [
'%edition' => ucfirst(env()['CHEVERETO_EDITION']),
@@ -1989,7 +2069,7 @@ function getPoweredByRemarks(): array
$liability = _s('This website is hosted on a service layer provided by %s. Chevereto Software is not responsible for the operation of this service, nor for any damages that may result from its use.', $providerLink);
}
if (env()['CHEVERETO_ENABLE_LOCAL_STORAGE'] === '0') {
- $content = _s('File uploads are stored and served using external storage providers configured by The Service Operator.')
+ $content = _s('File uploads are stored and served using external storage providers configured by the %s of this website.', $responsible)
. ' '
. _s('%s only hosts the database and application service layer.', $providerLink)
. ' '
@@ -2029,8 +2109,7 @@ function runAppCommand(
}
$logger->write(
<<cacheRootPrefix = env()['CHEVERETO_CACHE_KEY_ROOT_PREFIX']; // chv:
$this->cachePrefix = env()['CHEVERETO_CACHE_KEY_PREFIX']; // chv::
$this->tableRootPrefix = env()['CHEVERETO_DB_TABLE_ROOT_PREFIX']; // chv__
}
@@ -226,7 +230,7 @@ class Tenants
$tenantKey = $this->getCacheKey('tenant', $tenantId);
$tenantCache = $this->redis->get($tenantKey);
if ($tenantCache && $tenant === null) {
- $tenant = unserialize($tenantCache);
+ $tenant = $tenantCache;
}
$hostname = $tenant['hostname'] ?? null;
if ($hostname !== null) {
@@ -250,6 +254,24 @@ class Tenants
PLAIN
);
}
+ $tenantCachePattern = $this->cacheRootPrefix . $tenantId . ':*';
+ $iterator = null;
+ while ($iterator !== 0) {
+ $scan = $this->redis->scan($iterator, "{$tenantCachePattern}");
+ foreach ($scan as $key) {
+ $result = (bool) $this->redis->del($key);
+ $status = 'DELETE';
+ if ($result === false && ! $this->redis->get($key)) {
+ $status = ' 404';
+ }
+ $this->logger->write(
+ << {$key}
+
+ PLAIN
+ );
+ }
+ }
if ($dropTables) {
$likePattern = "{$this->tableRootPrefix}{$tenantId}_%";
$this->db->query(
@@ -371,7 +393,8 @@ class Tenants
'managers', s.managers,
'pages', s.pages,
'storages', s.storages,
- 'categories', s.categories
+ 'categories', s.categories,
+ 'login_providers', s.login_providers
) AS stats
FROM `{$tableTenantsStats}` AS s
WHERE s.tenant_id = t.id
@@ -464,12 +487,14 @@ class Tenants
$this->mergeTenantCacheable($tenant);
$cached = $this->redis->get($tenantKey);
if ($cached) {
- /** @var array $current */
- $current = unserialize($cached);
- $currentHostname = $current['hostname'];
- if ($currentHostname !== $tenant['hostname']) {
- $currentKey = $this->getCacheKey('hostname', $tenant['hostname']);
- $this->redis->del($currentKey);
+ try {
+ /** @var array $cached */
+ $cachedHostname = $cached['hostname'];
+ if ($cachedHostname !== $tenant['hostname']) {
+ $cachedKey = $this->getCacheKey('hostname', $cachedHostname);
+ $this->redis->del($cachedKey);
+ }
+ } catch (ErrorException) {
}
}
$this->cacheTenantArray($tenant);
@@ -700,8 +725,6 @@ class Tenants
$tableTenants = $this->db::getTable('tenants');
$tableTenantsPlans = $this->db::getTable('tenants_plans');
$tableTenantsStats = $this->db::getTable('tenants_stats');
- $tablePrefixTenant = $this->tableRootPrefix . $tenantId . '_';
- $statQuery = Stat::getStatQuery($tablePrefixTenant);
$this->db->query(
<<redis->mset(
[
$hostnameKey => $tenant['id'],
- $tenantKey => serialize($tenant),
+ $tenantKey => $tenant,
]
);
}
diff --git a/app/src/Tenants/TenantsConfig.php b/app/src/Tenants/TenantsConfig.php
new file mode 100644
index 0000000..5dd5618
--- /dev/null
+++ b/app/src/Tenants/TenantsConfig.php
@@ -0,0 +1,146 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Chevereto\Tenants;
+
+use Redis;
+use stdClass;
+use Throwable;
+
+/**
+ * Generates Traefik dynamic configuration for all tenants.
+ */
+final class TenantsConfig
+{
+ public const CF_RANGES = [
+ '173.245.48.0/20',
+ '103.21.244.0/22',
+ '103.22.200.0/22',
+ '103.31.4.0/22',
+ '141.101.64.0/18',
+ '108.162.192.0/18',
+ '190.93.240.0/20',
+ '188.114.96.0/20',
+ '197.234.240.0/22',
+ '198.41.128.0/17',
+ '162.158.0.0/15',
+ '104.16.0.0/13',
+ '104.24.0.0/14',
+ '172.64.0.0/13',
+ '131.0.72.0/22',
+ '2400:cb00::/32',
+ '2606:4700::/32',
+ '2803:f800::/32',
+ '2405:b500::/32',
+ '2405:8100::/32',
+ '2a06:98c0::/29',
+ '2c0f:f248::/32',
+ ];
+
+ public function __construct(
+ public readonly Redis $redis,
+ ) {
+ }
+
+ public function getConfig(
+ Tenants $tenants,
+ string $service,
+ int $port,
+ array $middleware
+ ): array {
+ $middlewares = [];
+ if (in_array('cf-only', $middleware)) {
+ $middlewares['cf-only'] = [
+ 'ipAllowList' => [
+ 'sourceRange' => $this->getCFRanges(),
+ ],
+ ];
+ }
+ $rows = $tenants->getTenantsRows();
+ $routers = [];
+ foreach ($rows as $row) {
+ $row = array_merge($row, [
+ 'limits' => [],
+ 'env' => [],
+ ]);
+ $tenant = Tenant::fromRow($row);
+ $routers[$tenant->id] = $this->getTenantRouter(
+ $tenant,
+ $service,
+ array_keys($middlewares)
+ );
+ }
+
+ return [
+ 'http' => [
+ 'routers' => $routers,
+ 'services' => [
+ $service => [
+ 'loadBalancer' => [
+ 'servers' => [
+ [
+ 'url' => sprintf(
+ 'http://%s:%d',
+ $service,
+ $port
+ ),
+ ],
+ ],
+ 'passHostHeader' => true,
+ ],
+ ],
+ ],
+ 'middlewares' => $middlewares,
+ ],
+ ];
+ }
+
+ public function getTenantRouter(Tenant $tenant, string $service, array $middlewares): array
+ {
+ return [
+ 'rule' => "Host(`{$tenant->hostname}`)",
+ 'service' => $service,
+ 'entryPoints' => ['websecure'],
+ 'tls' => new stdClass(),
+ 'middlewares' => $middlewares,
+ ];
+ }
+
+ public function getCFRanges(): array
+ {
+ $cacheKey = 'cf:ip-ranges';
+ $cached = $this->redis->get($cacheKey);
+ if ($cached !== false) {
+ return json_decode($cached, true);
+ }
+
+ try {
+ $v4 = file_get_contents('https://www.cloudflare.com/ips-v4');
+ $v6 = file_get_contents('https://www.cloudflare.com/ips-v6');
+ $ranges = array_values(
+ array_filter(
+ array_map(
+ 'trim',
+ array_merge(
+ explode("\n", $v4),
+ explode("\n", $v6),
+ )
+ )
+ )
+ );
+ } catch (Throwable) {
+ return self::CF_RANGES;
+ }
+ $this->redis->setex($cacheKey, 3600, json_encode($ranges));
+
+ return $ranges;
+ }
+}
diff --git a/app/src/Vars/functions.php b/app/src/Vars/functions.php
index 393733f..28706da 100644
--- a/app/src/Vars/functions.php
+++ b/app/src/Vars/functions.php
@@ -28,6 +28,55 @@ function env(): array
return $cache;
}
+/**
+ * Returns the ENV array but limited when CHEVERETO_TRIAL='1' by
+ * * `CHEVERETO_TRIAL_MAX_*` (numeric limits) and
+ * * `CHEVERETO_TRIAL_ENABLE_*` (boolean flags).
+ *
+ * The trial variables may only be used to *decrease* the value of the
+ * corresponding default. In other words, if the trial value is more
+ * permissive than the default, the default value will always be returned.
+ * This keeps the SaaS trial from accidentally granting greater privileges
+ * than the shipped product.
+ */
+function envTrialAware(): array
+{
+ if (env()['CHEVERETO_TRIAL'] !== '1') {
+ return env();
+ }
+ static $cache;
+ if (! isset($cache)) {
+ $cache = [];
+ /** @var string $value */
+ foreach (env() as $key => $value) {
+ $defaultValue = $value;
+ if (str_starts_with($key, 'CHEVERETO_MAX_')) {
+ $trialMax = 'CHEVERETO_TRIAL_MAX_' . substr($key, strlen('CHEVERETO_MAX_'));
+ /** @var string $trialValue */
+ $trialValue = array_key_exists($trialMax, env())
+ ? env()[$trialMax]
+ : '0';
+ $cache[$key] = intval($trialValue) > intval($defaultValue)
+ ? $defaultValue
+ : $trialValue;
+ } elseif (str_starts_with($key, 'CHEVERETO_ENABLE_')) {
+ $trialEnable = 'CHEVERETO_TRIAL_ENABLE_' . substr($key, strlen('CHEVERETO_ENABLE_'));
+ /** @var string $trialValue */
+ $trialValue = array_key_exists($trialEnable, env())
+ ? env()[$trialEnable]
+ : '0';
+ $cache[$key] = intval($trialValue) > intval($defaultValue)
+ ? $defaultValue
+ : $trialValue;
+ } else {
+ $cache[$key] = $value;
+ }
+ }
+ }
+
+ return $cache;
+}
+
function request(): array
{
static $cache;
diff --git a/content/images/system/default/home_cover.jpg b/content/images/system/default/home_cover.jpg
index 489f54b0bfb9aeccf7959a07281c852a8ba04edc..8356d6d49432a94fafb4ce33fd66a59ddafbf9f4 100644
GIT binary patch
literal 352419
zcmbTe2V4|a8!voj+|gy$vf#3@;)1R&>!PtY&;daymH`153ohb{6&0}?GZ2W1u`gIq
z91)bFf?%)MBZ$NpTkJ84Exw608k5}r88mtC{eAa--*=Dh%$YN%KK*&l8UDWZ_g$h5
z?ibXLFf2m?@sIrdQ(bFN-@c>7u)+O;1_Tg>A*62LfbsEDSS=xw;}a9Y`uiEIQNs*%
zE)bSDk=mpKAu+Mjrw$y_FPtC+1@tvcMA$Bsk(
zbL8tLgopJ--kT70-MIhCkN&TGqJ1{zNl4!*Q|Ba%j~kb0FvWTrI`rt#-4JM>HOZcs
z=q1L)PK-$yW9U0&^3<64IfT@#nYJP>6t)Hcxod|WUAua9YzOfFlm3q{|EcwV&QRRe
z)OZ=H;Ed6q|F`Vlw*Qt*$tI-xYrtm3zh$G>5wb6pkVZfLTh>Y@q(KTHd#nCyJ$gzn
z;}R36_H5sN)~s3W#@l1tQGouF{$DHnQ}chH`LFTX(eeI)ox$HeHfF}8L<0qC?377U
zW+WJ|^Z-_IL~x2`U>O9|v^Dj~SmheoDMyeEk0o
z!~d7f{%a02TSM2#G|c&n=ti|8+A1BX^~-spYN#Q#Mr7d)^AFwzI7Jf5c|%)N*U&xk
zc&}Xlrwh9rkL>jEaRyr4H!R!`J0oEx&4VZCg(Ey^Ky;)raU(5>iFlEYq#Nl)1QI}k
z$zT#rqDVA}CF96sl0asW`6P*aO_q=qB%Q1$n@A4HCp$?A*+&kOj7(dmBh#Jf%>*z5m@p=au`zK>JTrrt&wRx!VOBBg
znaxZ-vx_;v9B0lk7n$qKJ?1ggRYR3Q<)!MS3RFd?VpY>r3sm2z)~oVVdsU}YmsIyuzpDP?
z>T%7uwp>pxh_iBW+$`>EE`!_3?cq*ym$?Vro7%N&>ua~E-K}BVi
z7fbLjMqq-wVGnhx0-vJcXeIrn(OwhJEHE4y36Wrt6Nt0dfk`x
z>eXvqPpCJnUShpv_44bTtarQK@AVtjZ&yF4{+Rk-*3YcJxBmC_pEq!7U~J&qAiBZa
z1{n=X8&oxTrghS`*7nuLXcuTRwFk6UwXdBUI(Kj$>^#}|8|MP&bIy-k>bSIa32+(X
zlH#((<)q8~hP4{DY}mJ9Y{Rb_<}^Ir@PV$ju8l58H$k^Vw?lV9_p7UmYiHL;*V(R_
zu18$&>TBs+>x1-@^vm@n`YZbPpEdu?_cQxvi$B}>+4rBlZPc`p(8%5>tx-{<%Z=VQ
zHZ%@sJhAc0#`_xIYNBdlZW7vLR+FqIr<*)$s%zS-X>8N9rX@{(Y{oV-Hw$Yvui2Jn
z=bOE5?$*42^Qq0(H9y(>nVa5CaGT)fa69VuxJAPjy<3cL;b?KJ#S??8!PhX!u-0(O
z@T+?>_dxdK5->R_Hk47hB
zcjGwY8sk~x+tzJb4{g1$^}g1R+B9kt)Mjp*!ZtTe^-O(C(@fc>A3SS&cK4jUHYZ>B~<0JN?$V
zZD)Jutj^cEH0(04%fc=vy8PL-Ti1lHJG(ya)~cJW+lFpeyX(4(-M{JnZ4a)8Uym<)
z9PaV1XV;#IJxhB2r8
z=oi
zkR2F2aP`2OgN%dX2kje71_upZJ@}T`Mw})d3auR~hGvF74C@d!KkV#~hC@aTDH!r5
z+%J52_>Blt#Egg&kqsh8MCM1n9qK=H)zG`v_SP?~6;Vy1#z*ZRR%ck`u&u*h4-Xig
zKK#LmE+f7kab;wik+Vmhi*6DWq6cd(X_A`TMNMS*5d`XUESzKgT?0@to?p{pRM*Q_YK=cVfQ#{G|E!
zzVQ8G^8#kU=mjUfZ24u%mk*@=QeG0DG%=|%`Save$*;Z|`qjad<|$Ij{nY-cg-J!#!zxnZ0xN{46zPtOQ_Fp^@eBi{v
zUI+IaYIkVcVfVwCM_i9AKgu6XIr{gpxyN3YO)YzNJns1Y6QfStIB7lk!>Q0y7fy$q
z{`O42Gbhjbo;~)h&$oxq^*nc=ynFe+imnxV&v!Y$r?N}so(o+s?5*lnwg0;w-yOW@
zeevk`eZD__$^X)sAA)|UxIF0c#VZk4u3a5*_3pJX*B)J;bp5vtjSx3lkf-YL4<_3n{-{`b!RG~}ln_haur{dxM&A0DJUtoJaZxAy14bM%_cmGHD&-q`izdraa;kUnEeDkvL%WbbZzdG?+e0}@P
z&(=0X8t}!no?h<
z7VHOVsnjHft)=2>*TKt1DF2^c!2rU`zkeikYcc4fmNN=o)+y{_Di$r-$Nh=Sv6}^=
zF%gwa2+yA4LJ296NHGj4J<)K%YAdy=I2TC}1yX?FTus4h6XAkgQe?DYL=$1lQ6X7U
zrXnW^JeQ8=U_x?a9(I(pS!3b|o8)RDB$p6bi|)`y(lI7pY2wKT;LiJ7b-}5+3=O9;
zslo$jEl*ceqn+~%N)WL82o&Y{hZuy|rNdYGOZy68%d+_}
zv;u6@e7+#blF8`gYjU_ORw}1TsKwQlP3lp~3;&Q@b=$$yvyMUEUmSSKW^dc#>OvEA!BwQw>
z7#E8xdPeUhl1#K}jesh2C_*f{V8UrN9MRNZJGq=qCT58kOd1PEEqb%7iE|H=WQ(hb
z3FRrpqCY>$3y>>Ug=7A!B*F)q1d2c)o>ITSCHX5Ph6R3{4OTDUji@EA9Anb55k!mX
z0nP#zSu+tj85PLW*~VIBU*{OTcHxCJHy!}^a+D3gL@lcs-v!4=lhNff+QUiQ)Z{Oqd2KE--v76bG
zL)OU#oT-W#OL*R@#T`>JTJ*5Lc_)C+Kp9dEL+KQ}vkFmaT(QV}fzf-c05x-r4OhmN
zM^~m7!GVMr+0(@8sw_>W&%A?USD67Lx+V-Nk>PAEi#s63`K2;;mNOC^?cE$|Ct;N0
zKhm{M%|{7IL3U*c#o#M62fGF1CUX*zaYy%(awGZ(dgmxAk=SyNG7vs%mN732>v4jT
zWAFH^J0^{(MDheh&y_NdS6StB}L_3Otpb=#LJjbm@|0Pd@JJYf|G$
zfnl};8l*W?WLF(W3t}k-ttOWfxR&fG6D4^qh7m2Yxk4d7w%kR+XkbOTW}0Zt;D;m%
zTxtz}sjN9NEiuW&0|bm@k?{&i1%e1wGFHN?tVS0rqi@k=@Dzv0In*+!i?Kt9JC7sG
zXtQ|{Dl)bxz(Xc3xd5So1g8riO^nP43=bMj%`-`|;GlR&&ZYfAcsPho1C)G37M7=@
z^&uG`lp+8$){+e=$vGU7iA05i?o1#KP6E9P*%WOb*QKVVi!d}X0O4qi3m!Pa1b&hg
zSXZ`%!dMYAkod^yB(R{gM4esh;lh38C6ef=5H*`7p)tdRXsN&;W
z1qEd#DXz`{F1rfv0W`vH*EmsO52U7Wj4iwveS}FAX_kUua5=g{6o3jD=s!%;6OGtB
zp23(3-aV^s=yguEP!n+k&}Ukx0qd}<(y&-QIYmZcO+p>ir+H7tS0~Vn
z%BtH1>0#75$Gq%&Zrf)DLAntP#yQwhYRGa;eC~j3ZGtwAK-i+$eue2F7g!oE4EyI48Yc!otxN
z>PiqQ=-Sy&^pk`zQmn&d3_w$W$6{I@W%kF9w2
zB1wRZ^|TbwdU_LU(&AnmUZAXkf*#_r7+9d7<2+5^XMvb7FU%Oj$db%bwC+`!(Ka$x
z$x1TG2~eyTj^~6DYH{$WE>OHth?JDUz}YS{fFzbeanAt$3d#`GF=7VJ0;~x}FcCdn
zOU`B!x+M&LBBdVOYm%oaKSs1@Ou;UTZB||&4uP;%lF_2m;5f=)M28t6vAK(6N`91O
z6igAFK?cYaH2^`LZzF6nm;_aI9MLK=50y_dlMEUi0dy(A89`T!Ww4kKCJ-u+JSrTa
zl(7O?w8FF^Tm~aV33}1MN}Pv8*hN$dvXV+BEM1(0<^}?-tkEL2A)Hj4XSNz?-{^;P5{N6$iny_KaR8g8IA0)60%R5>3dOCMED_N!
zLsSuCiG*j)J;;vw;d)Vr3K#%b(Q%YdMMD^nC~{07rCreqnVRY#3Rgw>+Gyt<(9F=t
zoQsT(*hRT%xP3|h24E-{2h)h_Sj$I)$0DVc4iIu^qxh@iV`jk9%0ZO*ygvglO
zN3SZ!W3UmBtC7^7s^QMW*i-4_iYUP-5hIm_*B>fo0ZXjJG7JbHMl;U@QgfXYYj}^S
zaRxdWDk=eqJd|RQbaAef+}&Y#z$Ubk{2)<=tO9jeNy%bShLKB!TSSsTqyqH7ffgN*
z(rBa05IK?xO&EqcI?ia|IiQw@DdUH(IfBG{oGAB5$u^~-WtsY%U=4USzjUy15aA|OVsL-lGTZ}6JN>>sU(ma@DqEgfL)bbj;^7&Eiw<1qPk0F
zPst@^=!WCb2$Du3phJ53H&HSx4&TgS*`Jc0b9kgEJWfk?S@fZ1K#8Fbj1T}yn*ZIK
zITC~vn@p$08Nk;{w=!01p+&Sa{VIwwHOeTnaLK7E4$}i@xFj122~cGP
z(t>s1ZLnn`E6ZG%9^w|*LLY1doV^_Asx+c=NP3Yspnp&3DVacPvSo@RqW))PlvR){
zVLS$w7Fbm}p^Wlvdg-RQ^e*rHD2jHXe*Otxik3!90k|4{{A$
z;ZNYv3u6E-6oB-tOgJ;{DvEz1Q|4n=;u(H6*rHByB7L1K|SJvDu`E_qL^4a34ew8kj`SwZyCA|$P@IJ72LbERA%Ak0DtJVPfN
zr5G*Iw&?70RJ2$*gNzq2-7p_U2$Xq_3Bsd83;n|8f^IPxyzvDfxKxo8oBZ@NHbd1J
zBT{)6Wn-anfnyjEn6ick=O}20wGM)SkuHcP%$%{wKz-*&trvyh73L?
z1MU_Lrjui}P&`m2sKu>B!bLGMSV?ar*}MzoUnDj(2Dq%TK2&7>k3L6-V>{9lwi-_O+F*=e;2Xs3>qBN!A8$(JmJT58Jh04RYe5Gev$6%6ZhL8%xB
zPm-t?JtXvmTG?!}j%x}GC(r82xr^bnKuIb+
z(WOdtDjUSD1u506nnnjxWvHeo%8q(#Dry!OP>5NnHVcf+f^Zp8gZO|J5#dBaJR~ae
zsODmV0tveq_=L5c<{;VV0`e1)5W<47>_8Mx95S_*!r;#cR`?1e%u~=2cRK(D5yAvg
zM+3
zIXqT;$|`qtc+F#r&ZFYiTgXR#M3*r%kf7oe{{;|L2-DS$<c@>miv!$H1NlwTD5F%BTx;n$ie0
zS7;7e-@QfwvB^d!KMFvR3OOXsfX-=hDbrZYnwmVgz(lm+W?uRfi6Z3Perl~3E0!&t
zx%=pgDjDsAF$5L_r48{V5rQ%#=P(JHi~Wv-5M8j7fEUnDuAqIOBqv*f>k7jMJ}pW>
zSkPZU54;Oi_9}@-9ET@iX#qlx49bIYqQ*EV7y@Sn1qg;^Q$S7_C2$EehlEH7@i1+s
zvrl)HidElbnd;T>1>T$p^~FK=;G
zl*1FSz|gQAI>94^nSMn!5GWHS#6!ZobPgrV2Mlq>WQpR7>=L*PDB-X$RAU{YHb1}!
zVG<^q)C7n^6G&BvQjw$24oo4NT5&FmFti$(Pl@v=tC3eIP<<-=$YLKKS$K|m(tFZ8
z@4HL543o!+?}Af_fR`HZONy+plc2<$`I5~hzSx>mA`!L>DhH^5q>^adhnjQ<(J}rY
zxmA)$uaz-Js2X}oOmV;TF#lv$SF!?V6g@pr$^?6Yzg9^(vZqNGEYKB6c%(Qj#Crw9
zR4ijAHDNwvY7$$2G2tY6F+gSfIJ^S?k4*Hb$W=qJh&Mb!geJ&d%HRxsMzxtw
zmSva|#UJZ(q(P=z)#rHHrFc`c7(gT80iYEL!5_K?YA8Shqf|!B$f(4F2=C-4vE`6o
za<&AGpXMUT%xFpDB7uIPm~;V7f&kJAMt4!*Bw0to`Cx>r@g9|O(5)#LPwe&_#qL*R
z5FK5y8vcvy0QWr92>wKJYC0JZAh=k?X_Y*^$XF%T$-36LfIx+W)x+Vcd5iT>$p#2g
z^05PZ$r6A=%|MBXwCFWvnpc8Qj9oH|p?07Kj;*3zYyhIGoe9h%Y#EeIwg|%_f*SC$
zLFODLcv7|X
z3=3jAr73^xTc%OB)#_@N*cHGEfSRkIf@&-sa%m!DI5eujaV}7D7Rojyt0cXkc{fS7Gk=)Y^G1vQ8ilg3ax5LMBO8f{W(V`HiWx>6BcoeK@(*2CvT
zZ3~~gLPkF+)N+VOvdJ}UPto=<au=x3
z%*YQ|{tIbbPP@
zs!Rq7Q|?*SW!Y2(hc&BZVi7jbd>jif!5syV#`s8l;I>G3qd|QIw`j3
zkbykaQ!|=*neMY-g3^kr9tSZHxJB;c2)hD$kpb!qG~h>#JgW%?#Uh2*6orJCIrj7-
z0!218Dkv~2P=?tj>f8lt{h1^kwP?e_IFBTti3tiYV@NH}Qi%sG1-*btq6XOZzwiNi
zA`CAnv!-*>g9MZ1Jd>)x5kL$%W{*_`z#*J2ywm3BISF8)r1mMv=nlzIt2*ZC%WzpG
z(=Lu(2E|^s=)egkJ?y43R0F1fJ_bEIH7yf
zj7NcPDCdN6UiXMM&KYGO^zhMNihKtq)-GQPzdxW
zJfP?dItEQz{22VO*8he-eMOT2Q#UKzK$lLmd7wLN-uf3SkmiZsie3I1!nCYD_v~dD
z0tZ}%t2r4kV^<)m4)9Rzigr{RP%!Rhb$;a(Lk3dFmWQASUJ@9MH*dGdUL+t`2i)v}
zcaSIY!9pRNC=)c23;>AwN3WKZ(^Vuveo14%9!z|75h#
z@w}fhCHJruD2DU&F}X;Dr1~PPKp42b?_s8!7=-w&!2~blbOD@8r664l-D?7rby%VS
zdJF0wB8TG9YNY544M=tMwZm819BIV~3z!laqNqzV0*}B*r~rMV54AyC!83vUl{wlg
z1N)K=1Z?2;Db&vh>3(3PHrA5kLG_kOk^33LJ{6|%79(<7on`#Uu9R0x3fa4H)
zEy37nga{fS%~F700N%)ilVsXI5prOFmz?HhbCM{V(^Nzz#bF4QY2i5VFg08i-hhPQ
z7zD$DF>pB$B!FtccZ8vf9Hu}xQ1^SVS15wbSJ}fJMmBvyC$+sb*
zoD7ok1NCvPfnsPlL{pSqiTG>he6>1xOf
zn7O&hWwpxOj(v{60QJ>nRKQ*huEo2tfPEB>OC|ApllA}Tl_fru%`mdPY9gUV(-Y`jn#$M}X@qXP7cbgq7!#|g;z
zq)1PljtS>rDTxS4P%9Th2Ef3<5I74FL5Y~L3!*^iRybbPB$X!^L{Ulx!sumcCD*)Q
zAr)W>B-yoy3L?Rq##&b7;R-=RWsNq+tU>JCPeu_H`HbHQ4$P*9^?;j&IQj8kop3wW
z-QZ~kP8G(&87#%KTcM_|BFK`V29rSfpHVcZi2VbEHwsJ^-c)G8Q4mjTvx?@lV0Uc3
z?J`jZ?^mr?HE
z40;G04&9?)37uiP8c2XW!9t#CxV(V$K!HNe37Qo=NAaQs@+i7LB^*6TmOnjcDGF5z
z;tW*O8}fh^$V2mSUwZqK=qRU<>aI`rR0}7^h^b5`dE7Mr&zlo*1
zLggS}tf^W5zb}=X%9SlEwqOZB9Il9#(SRA)5&8qtQIGgL5sL9v9iENofW(#o*ET}j
z46o;up%@7wq~mu0Ac1}zaT?2DDj^&LA
zo^w%wru0DAQM9GUqHIE=F4P5ue~BJ>Is~O};+IQ|Z~>_b5|dY;KnpyvANdK)q`ZP@
zqm+Poa8xeI_K5?Px?xs|9iWRN=}vw$RH>ynRhbpgK%!Dk1HAHrCE^|Wg{e
zus}WbK9iH|SUOd79LuJ%^ni&08%BaoWPEL6u9;)TkB%@#q>uLUNVSq&S1!um#D#H2
zTE~F#DHK8H&?n#`zniru#bjEqUFrIH)7HB3`@xXWq?qKXI|^&GY327vCkJ=Pi|kms
z_SCsW`yIr2eA>yj()Em$M+!Zs$BvePVjz*49*nIRy;^`(MaC*)C>IC#54Md8s*web
zsGXxONS47-cAdvcnexNad_HeV3=J5vPVdU!O{T;0Y^Puk_41k0(8Vr
z@)Q`H9hOZz$;mPCe4#1Fl%0I4fX_aWvRgw6I5t^As4Ypr6ag%iDA8UN>B}Cs4VeCQ
zqe&+!69%4NF=kcA{e352YCAo%Y0sO9&kpxJ)~Acx#usPvz4y;uUFo*uLi(e__a1xS
zdvvGg+0ETwL?1f8CjZ->H#2(1`E;uczcBtx|Vt4^tC6e|u-n!jOQGmqvD6)i|i`
z&RX8PFD)4H=<&Dqwna>$q;PS_jjtyek@^xsvxYljf_ga#gLu~{>1>wu75$z8jwnelzsJ>UFzW>W1l
zyGllP8r>29My`9jYfnMNo+8hL(Vb#PUmHHUYh0n{jx}LjD-$o=v!7!*-&K^^Q&T+DdS
zb1Uky3&%2b8tjhbrD{Q30dxRMb_G4<$E#!k$H>fV3M2w{Gug5nQ&<`wTx?F~*zJWb
z--$VVwnXF{9W1OIh*KSv?dTo4UP&gS)ad-KRwZSvN^;8Ab~;mh)_c;45i{1MW&}l!
zu4w9PQArS1^jIvGkSy<`G%MgFvUTX;G6=)bKn$Ev04Kb<%$zT
zgagy<4a+H3wJQAP#F>W8{Z~f?Eh}}bw4`~IQxsCXcdQ|80a$T0?f*VkGDqxd+Aiv8
z$+L>cuxI-_?O1z1E$;I@@hy9$u8CzfURh(mJmA`puAb9l>aCkl-qq;f(*0Mh91k=7
zlOQsHa%2h7`V>k=)Ff~R^bEz+dxCJHq08j#WT$Z;7^pdv;7qK5pr88$KKVF!2!vMb
zz4`IWq|qxBE8G+70pwtIvsNyx*eqeHyZqAVi8q~$m_82`lOq?p6qN5Wrv?{<2!K%m
zBFHNkU&O)$dSDYesw72L$%KJBEu%m1Y%3G-jS1~%*}Saree1ofBLhCKi2kkB`f}r<
z40V#fyC)SJzF`uMuu@L|j?D^JQ*o$9lYZvRP?u-6jBm}Bb4%Cs+F6m=WRJyiE;4HJ
znl1^ubOiyyg!c_~bwzt;s+Iz*5Dic}PU0RenOg4bqI8fD%ytQuyN$TxKa@cFN5Y{p
z%jEY1k?15rO?r}ABcjAPiLc(31sfn$=x&`Zz=)tGYSGX$+7n&A9S2V(y@X(An?VoG6T%I7!%~3#BpboztD)FJ
z-8gFXLuWA>o>NSJ#U4>nUxFGdNCxOVU@D<9k3QuJlGil
zDqo!R_Q)l9!;*9L#(mD|rw)v;#7zy^=hHZD`15Z{ckCM!T5@mI4_Jyz3KbNT)NrBx
z0ML((bvh4ss2bc-VGV`3>O6kX6V+xll^}Ymm{6ZkmjWHQ+G_L~NXzJ3y}Z8`WRB2-
z-bx~1uaq1YsNhTk=ptCt5@$=U$Mp!?-lzbMC30v0w>Tn#+Am&<$B9nkcp~euyX(+L
zsAV1fJVN=xht3Ac*(#^5v4rkg7VHAxPTP}urefNu?XFW}?Pu=AHtn*mUC%{l9-i8z
z@>?Ou1Rg$UEE~-rSfAI1a6~
zw;M1CEbwuSSSU;t0DwJCp#)li%}otHYL|HKjbTuD
z`ihbkXYPED5Be|~M+}z`qsd*+FQQTf_O;VES>g~eB559F_|D_HT^8YBiK5jjDw2!v
zaEN+D%iup7IXKC#fjA$`lK=5RRtQ%1LrQDe^B-+s=1K6QoIilp0!$(Qo2D|i9A5MHJR9^h`^lnXWzmom^W
zCuxfTqp<3!TPL~vQV|(-<-_i{jg$6YA1A5IIQjz3tXE7u95sPY46z9cz|Cd5PLZut
z)&^6ptyF@)GRy#fOoGD=QS2vDE&``f*0Qu+H1j852{d9}L2_i0Bl22>Ct%R)J%e<+
z{6kG0@u5@_!UO365qej9x?d`nK>-sbq;bjELJk!WYsG)6Rb)dxOaaVB@f7`?EqnDh
zLobNjbvqsoKqtD<7Ys8zZQOh7sJ#2ycV^&}LBBb-~~
zVqlMup=!X%rscU{OGx^Oic(9*3wx^z$Ie(~%rU8{QlUe$STD6+;CRY;2ukOf5q(f}
zk2@70P)I6Iz#|(!foh`TkuIkc8KfA(WrplU0E(v8(ztkm9*aR3KnpfND3YFV_cW<^
zHVpzkfav^^cs4JmP-80e3@+sHg$q8FDrW?^HZTXb({Ha>2`Cl+^+Mf6IF~9HE5|bF
zXDR}}&Rb^E<#!(MOLy0_GE+8$(IY)>{=A|w`*C3F)U3Dy9VGw16^fgz)S
ze#%9G;g6k=Q^PG+hJRkwv%)*!!j6nkC}N-$_}7zUK4X#DBf9`g1(CT=fbXC!CKL?t
z>Wfuo#WBbOSQQ#k$OnxnBu`{e>yAoXRV~NNhZs>yNrD&(l-()ptmk5QSRC~1LrK2L
zl{1%{j|b<3NhVLTq%q;Rm|r>Lmjb5;%Ma6`1L}`cW~TlZ-AGb9*>EB?gK1|aCT{(9DqwO4}dP$F0W>t
zDEH)LjX9r{EV3)2_ntUktT@bIZ3L^S>%}FBI6##Ig5V^`GJ2xTHFRZ%19q!DYka};
zt&2Cc7!ZH4$m`|V$j94XR(@aUwRY`*-#V-tt&Z{!h1}uka%jSVL>=~;!g1ggPwahR
zShAc=g#(V>Fs8Ic*_?HarMP_J*tw-=_I9Y~2{8%$mcfRn8KB$3iWF27US(9{stY7Y
zdeBCVXhGyVpO*lA8Z1DI6a`LcXinR@nQ7U_#WBg((jd7Yv;iNn=BtAIfMf~rLe3zvBT7zGPOca<`^u>U->IM@D34+W
zP#Y!*EigSr*%J5ANEr2M#C&LA++7TZg&%;t_5An8q#e0xkV*?TZhk*?zbs2avve)bN-*wo1x}$YS(=m|I73pdRn=OtIM78W{=h$zfd6Dogjp^$Fjxw>`+f=>ql^vBhQp
zwGwO!!1aja)7fEE$>-viG#VF)t(%kU_XGa2O|W8f)o?L&fU?W=s~)Ue7B%6(tu-4L
zMt|{sM%*RGwJK}*z|cd{+Y968AIRM1w*FP|%XbZ?3~k^w^Fc<=jpen!x_D_@(!7Mr
zPlvYqb8WxMb?)oe-Aq{H=Jp@&>WfeAZ(CFKfgA-()f~nl^Cmo4Z|OR}{95J~wP==ZhJ)BGS*S
ztIlfuS5}KbBNFapjCj84#rRhqa}Ld%b1*Bz`-_gd9+gL1=Dyx=X-h-@TWL=+r*%31
z^ZvYysBxA1bO&yBTrX2$tKo`4gy3M6qGt%SN*?hj!1gs|YwG;Sv;uS!V=jY;p|9zs
zK+>`zOGP!l!1~sNbFng2YrvBctRWMbnxr~tpvcfbYz|xTK@ab4liAJV2%^l%in;`r
zu93VnjDs95X8bhTJWt+Xz`4J6{d$ZE+SH;?^nx{gT0h@Ve0J-r>ePofN3QRdS>4X@
z^kn7TqPW3JZXP<&^R3(1J45-_LGbW8{`_N`)lZg1Z{OSRvjvwQ+)7_mo;kKhNJjUU
zTc54nIily(k`B*y4qUL@|7d?X=b?GYvSI&N+BOWBz{
z*Ru{ESeBnLZCm}+IZ=rZq6fDrtd1($vM{G&!3Ea}pLq>4-L6faa;@X5*#}?lSeDsr
zUtYrP!e-0%A6{M3Y;{qS2WN|FEvf(Q`gVVYq+Hy;BC>L6jEA-ubto_^v9cn>n()s}AqQ+ZO>s=-&j
zAJ1xRia}OWzI2FLr%zaDpWS+AMTN+ZwjX;^n!Y2^G7=}%%Fyxy7h
z<*2+D`{v&4kTv2D%K^`3e+b9zFdq1y=?q{8Fw`gAbUZvf}
z4qLge*)HSRfXOE#PtIET?3ZO;FB|&I+g!P(_xr2We|UGPp8Ci7NiSwSsLXdfsqS?B
zOs^v?dU?)_zj$F@^tP(GH(y^L+F9P)Va(^5{WkRM@8+|AX!^sqb9z;kcDk`~+=EAZ
zD)OtQUw%4!<5hb7#l9XsquM9@xuE-?VTX@?-S%bG@P$z)o?dQ0=H(yL4`1HsQXLQi
zor;9;Zb{?7j*G1DqNz5A_@t_wq8_4?3e6;&f?LTQkN!qMX+zZ-6c#EnD~bv4A&ERr
zm8Hlyc_Ky7V=iX;IixiWXxBl5)9n{`=x;bjdf2jQ$!S*D<&t`r#`cjRYw+rT+ob1B
zyVu$CyS{ueedbSRw@iudKH|p*mfg&MHCfA-rscYy=l%La}U&?bZ~Q3)x5&$g!hBmdVk@w;o#a=kDe@@{rrdN
za~CW)`E=Ir(N$i*?7w!-j!j<^n{#t!{*OqoS
zI=ua>?<4!G_{P&O`xaHb?sB!;SHD!oov3J@nOS_v{_4W+Y)>X9rg9|0b`Fee)#tOoNH%JAZDG@
zakSSdFeO7x6hbjHm*Fh;K-_9b~PG!@Iw8=
z(|1NM^8TxR$hk|s7koSZ$-(8rOHaH?cr|t1!$)W9K0W_vT
zMvT4LGILAs>CruZe|jaW-~C4k?{4RRajyD()U?=rUmqE<|6ohE!TqCN{y
z{JJ+!rcFys9NK5y<4N5QefT9VuCVe@w?i#j{IK-prmT_Csh5U-xcy??W=MFvp
zxbjxT^}*@Y@9(r3a(YI>_3M`(?m8A)akXlD#z1O}b3(A15hb`3ih0k0wJCpZ=KR9F
zU_8M{qP!l1ss@EV@J_G8j<+l4hvTFW5cpQRcry09WSrHJK@cp$GWg=&lb+VZxsL$9
z?<81v%2yc5ma{Wx3I{oGDo2ItRE*S^wicZ(`{p+Z6`eH8tNycTOLeDLb6#95DvEme
z{H$+Q)5PnyrhR+kVBopJ^#^CXwA?BU_~yVvP1U|Q^V_+-8{}+UU9YA0v@$Hv98n
z_X~d&wYk`?c6~GTIsJ?N9d$$6d}082I5z
z(O1u>x=op~>07b~w8Z3|ug*6PhaWtDu$JURh
zbepn%z4xZYtPb*6$ke|@?JW<{<^zI
zx0e6<*H`^QpS(M|t?P_Una`$gTl#GJ#2uNfziPhj-RoDG-nT!DxY)MY=Fa~)-eZ2%
zs6o|BFBSO=*!Ap%z2b-a*WH#cy>qAi#z(V%eqDNbZ*(8u@)_@LebMpV@Pm7zD()1n
zeN}w%AS{SS`(O8-(tdDORiClejW>>ddF{cCd6sGWo$Nrd9}FquX3Cw&m(dV`0Up+7
z8VAsChIa+xfKZlDjhb10WjKt05JJsPIz9&)hm&Ve`1qVO4Tp(vBw9j1UZi1hHJgh+
z_TwOmag3%^nJ@Y%-w~0LjW`)#gI8+uT32`Hm@2QYdwN&(+1LEXS2Nz;yZYe%rIC%J
z$DHWkl{wckajP4WD#*)1ZWx)fr9$3xrA
zEA27oi$M*KXYDMw|Ln-h2jkxN(Z250dCIc1s&4&`++MJu_wv?}Q+|H>U`_Oq_uKDe
zY`E~pxr^N7YhmU4#>av-t$s57kV|<lqdlF)EI%qpYEbb2`=L$Do);ic~?O4BDxoYBZd$Y0!#8i6~*}CPmjM_z}RF?
z(E&M#vPce`ITooIn1{pcAhb^xnX!AV`>gBn(smPjesF8v{KKD3dUkwTG2-%*_Pu9S
z4fyTQ)1e`zebHl&+<0GaUZZ})wr`(qo80+q?;-q?lWwbrb{pGf$)#!0+Y`QDletf*
z8oD#?K+68d`?qeK^XnD;v(7O$l1}%UIx=hd>Sz5nwZHna_wB=7hVHxb@U4Bz%&w&;
z7c83gzIROJ*T1ZvbawJG?|J>6XMOSg^Apq0Ea(>3Zfo~FLx+C()t%<^|7`qYggvF?sIM2?86@i
zW_W-2_0_XW8y}7w;kK{&r0N^nuV*bicW!yn(B;?P?fmxrzzu)&xV5HkhIp}XZ<~fi
z5hH%P(BtaDfrmf*Xu0!r{@)~f?(N8CPanTqzwi3x
zvP<2%egFIGqRO|2ed4x6FDrleYSzTcSM$I5V(qVg`pn<+pQH0GRsIr_5#MC)?yJkY
z{rakER^f~2uj6OW`!V;|IX*?bhAjO4;<@%k-RB*cx!PjaYV$%kdj16>;5fTD+@FSA
z5G{o1mn@-FkFyjMWwJoD`F_ePsCnF-=S86tn+VaVZu)*a(6kDW!AT3>>
zl_45H3W$r*D@|Hix#44Qgk-H2d<5-Dy=To^7~sZrO$lkGG9D`1zz#pOG8x
z`;4r8d(ysTJ7!m8bYA;n{MD$L(`SU7KGf7YVPe4b{aI`DE^lYw-W%6-=ktc64!k&W
z_PdylX(z|*ANS~fY4nlnSB@@TcOkxqPqWSyhei!EmA4AGy{xugRQw_B&M@ms~n|_|{K<^jyB_tarDbM~<~D?DqYG$2T&MKc0A|
z>x4V<)#{le2Mt&+p$k*x~f!
z5RLnqKK(aFb$Py{XU|z#LwkP_Is2f`q^E1HzPmGQy37A6I(#v{is8E;d@Rx1&U|g3}|zt{2A@EsT2p
zTYT1E^Iso47~6kAmv%#=I&5*OJ^XNG+gsb#j9*fkbNA{qTh^psZXfOZrhC2Shh2OA
z_F&trS&I)pUcdSJ=IxK3Ui@s(#zU9ly3XGCW9j{_Gv95j*tDT~VDq_qn@3If@$jwg
z)y=L|r%$_mSaZ2@<>Ts#i_L~a->-b|eoC|Jw_d)xdO7a?+;_1RSLLh6`p0a&xBKLj
zZb7BBU8+w%da=HHuQ_{qB+i(AW$wHL@7uTM5034(Z2!aRCtF8GPnmsoZ%9?iIG@Sh
zM|ORcX-^pc{KbW5Tep0F=i&Qlha#_MI{FSex^%|p2QAZP#zxPa{Y%exb3Y`+UVL;l
z>CUl=p7RdJmoC^8Qa-3^;)5@OzBv4$s_$nVK8q}egczEi9#gcSPUf-?GrD(r-8rlN
zFYBLvS^A>)tD~m|jOsXdRG<7;%bQ-UtXvjqGB)aXX4j3%9lhRl8*_B*H?y83&i-q8
z>d3?I4?c@63VQPXnCH<^v#NV9A2Gjb#L4M1|N3IuvsXBLpL6M_x2u<5UO0Hx=|A=H
zcUnIFe9iHT*S8I+zrA(h*)4SwFLzvZ&W6w%-D(s*hTsGSe@+g>L8EZkYYBvuV6dM{
zl?gJ!F3=0e1GGU&=IA}gm7s`apOy3E`9PhfOzF9AfC|g+mTyp5Hk8)wr|u4{yDd`7H72wT^4o-tSTG
z@cUh@drcqNbmr?eeFhybTF|)hqg!b=cR%kuDg11+#GTFl+ShZ=yScNoqI*|0`K`)-
z!;KE#&29Ya%ykn_#+-aKZCTc!tXTc9$^%a?6wPX}x!vKZWmQY!b8Z|P9GW%vknzQD
zZ@hKT`_HnC2iG0Anb7Fj4^wCTe*2eou^H>;
zc<(qocTMEH!svG^3x|#^?f0R>;2~KvDjrTgu)CGefSn`~AhNH*?aTPK&$TZ2ra(
zt{H!mPFFsEH9!8V8)ui7SHAC2d9>q|OG6?n#?JSdyKMNeuXjCue7F1mpzBTGpB~*4(4aS->F_>hTCR-6>
zm$fLJ|Mlpc_x*o9@8`XLzcYDep7p-3=en=^TE1V+s)=$BQHHqJ(4cDQ>bIYt8@NVP
z)HX(RraTusi?w~+CEp7s?cdZ4sv9Egjcn{**UYZnB%LZhp=(@Ur?XmKlv1dZT>iiQ
z*CTSo=bs%^M1#GKui!DIgFe~{9+_SKr#j}!E=JVMzNMe`m|XJKO}TlyxAWuV^n|t?
zi6#}u&iD3T{c-W#+ERq}(%ke?WI$hAic-7u8^`|u$4>TW0-Lv5v)A^Rc?k)zKs+OreH0p2K&)<19)r{2^6*mvb*CY$5*9O@}Wm97}hwhGkn*0{4
zxe-2-MLi+Z@Y6}BQhNb=KxJxXth}Ia!f;J_Y&tz!KJ4>~dXyUpKwmdE*b8+G7J%d#+X0Tb!YIe*Ui9u2W_?Y_zEGq_ERb`PJm7m38y*
z>WxEhX*Zubg^V#AvQ8e&!eu>~SzG^}U-WA2xWeuEu+9GP=+OL@tg#JIjbPOaD|OZM
zla=Fk#WO#0$>N2>mL-no5)0(_Z`!kB>+{dvnC?^=rzRiAZ|Hhu*4Eba*qw-uBWxZ}
zAk48RHq$`MHbh3_gnD2X^0is9LaP3!Db49xK
zTlU~Z=1q;Bwqo}ghL`l*pyUYcKbytqUE7owpB)&F&~+HG9?#dzcRl+=alY@o
zVO;v{{`8m4_=%zXip6^IF1e~%lYr8z*`f}K9_zix0{7a>SN`tev|JQIizsv=BW=uAA=?~diF1eh1J)e9$oR9
zD9!vbq9EV!6Enke%G#9!Qix)<6w$vq(TE?rB1Z7K)t07$tr$>0u8=+-i{*cYXG?^71#ulb`5uw?lt7SH0LY
z#-wUyuP9?&rE+D3=VQDS^4oZ+k-a4&|H;wtXUwrMU46T9X4V+i*UvE@`16O=B6o=%K>IN)G
zjJO8f^)2bBPi9iQCYN;P#ZLZiJgY`Z5)+{_
z7`ed&f^bj>K}TS6V}EpVFr%2fw@sNCu%VuvM7{mT0lUuL$*GTBI*y+<-p%z;jzkV0
zh;SzPt8NBQ%&pY*_%P)IRr_AnSzPpe8D_9=A@o
zlrDP(7Z-_1-M^q9|C|weHtB=QpJo|^&}kMPZ*pn1Zsd%Qwx;@W$qSV~o&>Q~%DWgh
z9ePN^J{i+X;it?4+g-Nn>`=u~iIN_~cYcYoOuN6dvCnfnM?0;JyfaI?beNKY`|}U4eJOooYk0zoS-R%f
zVnd3kcA0J)?nF7;uj|F7uhY5_xxEup$$p7JtP$q;2BCpsoHV#E6ALI9+{-7m~He7kj*PI?`Gi3$|MBm%c=VO}=Q&d2!!o
zByRuu9|savhb^-S^V60JM~9ca+2c{W3HuLNza;$p2i3mDTB!?L869e{Y+;&{OV#IF
z+e+^?-+pkkD@&^+IBZ}!>Znp@OIAe~v$v-xj=iaxpzW6!X4eo1cimAKjA$BUZN@2O
zgsjVvlEi~xbR#JTkdu$tPhfU~2>s{tNB$OjOfy9crmK0$403ByX9`)<0_w$pliNZAeHLT;SI+k>yyHS?S
zBwP5@_*iJ`7HVcjoy?rq`4QyZN(rh;&z<;rlQtanyWOj9apr-Lj7X7@wz^elA@|hzlCT4k_mL7HsxL
zgiKa251m&5j>AWCumualg58i)fzR3ZgWi+trF|a~qfV;kxY8wq{HIksi?4*@P2ED>
zodTQgzhynm7`u}(mJw?|XET&ttHU#bX)e522EV8KUpfsYSMg5#uxD^dt8;)wLTR
zj`v3nZLCg*cBryl{w$*Ww$m@$dLqc#vb3%ayBZ#uVK!g4(|6zXF5W98d(wXw19
zJQEw)4*QCV&5wsiV#oWMC(X)lHU^B&PcD}*=W(CJ!Z&xOt&b?b`wVw~s%2xlbHuSK
zc53@UV^M6!o!XEjafR}6Z-Q7vFB8Zp5h}I(=0VCpaXw?<8Ljdnw#wT4^!LcLt~XV|=kL_vun_uKRkZTKDp%
zw%wR!{XeMRivGlro2Sz$xm3&`KF(?LkV%fugY@71{cAd!=NllJFt)!bsJw^Z+$N
zZOPiVx{{KSU_7l@BO-w5#Mt}V4IB&luKXZYSHY7Rn@NWXB;_CmB$qN&4QUZOXcmVCt6!9+0L0^r~4E~h_W9W2yoK)x->XGkYDOF
z5Yp*J&2LZl*A>6fHO$kMutyDJMwE4LKX`AB?eBD_vdz9#dp;#{xCADvb;Hk1A8M%2z0;`6r{e81MEq$uaUv1w;(ul@Lj{lPG&0TM1N5}i4t0G69
zCv7lKSgJhqte|BTyV1I{wd*!zNy7i294*3zPgWKYMrOVZYeyxmk38zE@fpC>91?Xs
zB6KMrbcLyww(tcU<{xYE8#_lxE6e3}xZ4!Xg>`@biO}2OKmI|HaiM2(29ko<`F_EM
z8nvevhZn%(7g2)`@-Q7gpgix>yBHF#YrDKse|Fa#tzKoG-FV>IwD$C}4kMS{A6>*)
z-e7i4uhwcfEPqWsk+An=pt1%PZ
ztk^=e6Q+3Imfx7Pj_KB~oz?8{iLlnA+?@Q7#`N$hA}`?91Yh|t$>TPSfp0)5ZcUo-
z(FBXqcesMTGn`~UBOCBl0nF1aV)t34_c{Vk$H!T<#XpsNRB}J@voTh-QpD*<+G`Of
z@7~=4*o0e@eMkWRUy~61MC8;qZkTYmFW*H@Aj@yHM^DSb56;$uIq>Dh9GE%rUkxJ|RKJJ&ck*6;73vB{o>h>*n<
zhg|ug3$bAn9@CeCEpUvUQbN!y3m-E4q*OCVaC+dX`^)zaWJUKTjtT?_-mC0*rBK>8ra7h&i=2MR&pULgcUVH>Q?z0ZPq)^oCwpIzc`lC
zGdGfhH=nKhd7_?Hvc61xr?Gi^*Z#9Ua-xM~vU-ci-OgOKd~mnG3dRyyteq@wbp0kSw?4^JvzVoW0<_+lt~?5q-+E&N8WzA==*V5XTK&cfB0LY
z!O7BWvBBCeOW~&L7DFyLOTzbmP%3d#mQicrI^qYMBD5lhGMEwDmrEVxrCQD@v*_Il
z2XYp|GP8fGb+0=zia%~-7}w`ylh3S<<&gaUw9OgQR({#x$4PJOWGNJg)~v8;$NOtY
z(nkeBD$$Tk1=9Gd9o$--a{jl8=Dq&->9xlmC4iA-zZ=87<(u<6Bz;ILrpv>Si_
zvGUZ(z6V~38^@)6k16$k@adZ2i+5B~d{*NWQl_QS6TR2t);q>+t@(+?
z7vu(?&%L!|WB#=7Xr=WL{iWsRHHjQNR-+v9^;vZ%I`Cd)5^
zzqyoW^yI2;I{1icTvUADN^agkN>6*?6xjEURDSX%GiP>r)9(HzkqU=pQm>IdGe;iL|Za>pslS>BsTGu`pfqbO3>r~Lim*W5FUt6qZ@#jjt_t*=EEx)h}kkQN@T
z>r{UI{b=tPvuUv*KLMX5f54cr>tudgUAUO8alqutw}F$e(pfpwSEr*sY(DR|Jt2o#
zrH-F@Sw~9rODRuzcvd<0!}9FMW!;ol!y4?%xh1${(Gu3vb3W4(?K&jg=#ClZA({$#
ze@6c<857rEGRFaFC|iyeWWjDnJ6r
zKw)JQ&?Oki#4DaQeFQsILi&;(;1T>QgHDwnaDB10lb?HH7dxpTTCEFOCoaU+*Ekf_
zj;X|MYKY)*&tRhEWQYfMb%tIjoP9;pFu74cJE&~n^t9^!eaG!p?oJh~qkbagkV#mz
z5;)rzKp}U{X;d_ydwMq;s)s#Hy024ksy@ZwlU|gs_`gQ(x({mf3Z(i}=C`9gkM#Pf^j+s(d6ujDPkwF-
zr~W8+)cUeE%Pg6sfhmyX(({gyMRM5;y+BX>5V}IpaTxJRT657LQZ-gK_NnBYQ5wL%
zrq2;B1Usuz(mfgm;jbu)&P;f4ODk|4m@gJ)MZ&g$v3+Z2L+pX#lP>)jFvXBiG1K&dfH#66@
z3Xc+dsl!Kv3}W(31#dyt9FhtN!L16AOmP1F(xz5=04T_1<}g1#*~_{?BajnF?C82B
zGh+DmC3cVFGVS3`9j~9BHT0cjc=s7%=GZxAPKMKkR1Y(^z&)bbho2?-UThJYA(qx9
zB(a{Pr5%3O%6?ZDajZ?T-ITt6QRLI}P`B7qcBIfh{JL}_S4I>+)>JFj&4@}Je(~}x
z2Zy_GN8*}Wx4$%AOJ$5C99}~hjt#f?F;H0l`|rK$y)$b+=3lRn7dN&2FaM#CO8Uvr
zst+1h%UD@nXrHN@Xow))A19F&_$WJY7e5bYtFJE4Xs&N4Ru)BHPm$@QpQ)wbgDl2x
zpU9QhTBkTId>$!Oc`;2Ui=Q#s-xg^J6e|h_HDUUl6x11f1cS|n1^|clK4@k0B$8~SN
z>5VGP$&RcI-}mEU@T-*{mf2tGcR7r${-|$T(IiBbIfpOxkoUCUC$*?Fr54P5g!a(0iGh&`=H-G>
zzm&mg#1ntkx|5pjoGvQDmATep6W`Z7?OjfsfTZoExv_q-7TF-8LE)@r=xDLiTF82h
z_`;tDo~U#)k_U%%TynGfYZ`5;%^$?K0Winbj11E>QlEGPr3W58B+UXg5cz{O_MEY1
z%vGD+=yu@Yff5Hj#L)vkfFn`g?KG@cz#F(VDZ?a>#_TS4&gdKpa<%^v=^Jo@U-zZ`%3dd9X_JS
zlubWQTj_ecP3s40CH>4NrMP5wM>%p->xIt72glux(Y05-m6`>wXXqQ|Veb+TACa-e
z4lwCe>4lE3W9`X0{(p2kXMNMcchjiP)it6R+d40sEFU9@Bx6}Qi>$N6oHMJWC8x(SjQ@Yu!KNCLvOcztA{L)ZD4ck<}tg)|UEQCakP_o8@
zM{>a#J&eoGni;lC^q!*~(l{4?;|{+Q-MP!lLkJPm$YJzO8x>6-1oc->P-I-_D*c92|RQQEd=&41&);
zE|+~C*8T^Dn;0jt)8@<<#v{Oun=3xw?X0bFF30{;?OA2J6RMR%1EV40MQ0NR6bh*A
z3$2tIE#>-@n-%mEhRK+Sin0S*tkK>tn-@l0%yXv3Le@7{lYTTTB%U{4O=6v&ao%iD
zFb|#kJekR`++;P#H{#^XZO}eQmHuXQ|A&b9|Ej~+-rYr{eRCy$4
z5sQ;LGTaj@+gn8slY2hCZ^#w5(_*uyS|{dQ>%Q{gsg{F#u&vf<_m$&r+sRTcyihES
z)tGuS(%EyBs=hh6(6LstE=^qx@=oviyheCVYP1{snYC{uU)Ox0d154=LfX&`kPbUK
z`{QRcI0WYp6n!SM@f*XZnun%+$XN&eqNl(6TK9YCzRt+mk<&$i=9T?5ZE=;!+d5qD
zjaK8o%=Hd1k_WUqfBe{BJkLL%6B@iOmcAz?#cNrKcATZ9VON}TqI7R~gQdgBTlJ0g
zg~AyY6q8@*=^I_pB}OYm<&aKB2a=bTb<8idJH&QukIc0bJNX;9xo0#tG5E}}$*CTN
zgqybY%Pi*DO3wZyljO1~oV>$mNYMI+f(_mBd#kr2cFH$Ij0BJ8cpiJ>k*Ym2!YC4+
zw_Hx|{93J|-5Bwjg}`xN`+bg^GU6(YP8d7dx)9+$Jd7$Lam-jF!xJa
z%m0i@bF50gEhRYWIs1*&71~W}#LV5+wGH|d+t)q*UHl>=B>F^9U3TNCPlJm)@aT<(
zlTS#wf0c_lQNz{bM95xZoLlAdzYXo@lQJ=*_0yP-czzAgF2
zMoYocQt}n~n*d
zUC};ya^Eyl-E(xb`=*Oy_=dFYr;oVbxF(ftsf|Vn7
zAG&Bq`5o0JC6lPy7Bx#w-_NXsvqs{-Mp88hluoZ{MwQY^w9iw<26exE{8dGgXcz#f?U)UQpop;Wc{n;FM>}(Lnm?()%-S
zCNrP1Nmb*tRF5K+tO1IFk5|bh^2HYP{P-J|${WUhLQix_RSh$3z}~02Fs%EUHXnH}
zj%@4GL!emrPz-|Hvh7n;Z>*30S$N;Dmhor&bkueIP#GrJx6rN(PK
z^-YPA;Px*a+0N?eH`&qj9qdun&FHM$JuK#@+!N&S`nd8_*N8Niuyb{!(92167d(jT
z9(`>yW{iA)4Leb_mBmjA5BerPHvEIiy}iHokPeE+
z?@~2&{cBQWgvyL))X6=ESlz{j)F=jNGJQ%lOs9Gzx;V9NV`J4jkl@l<@4;C8ahf#0
zfB2?u-H(Rkd^S}}o}6FYveMb0_I9aA9MYs7ju%IhvwC`7KUo;rQ+w7CmlV*5llLW;
zWxVc}-AmAYUjAD#`D)i}*c84yrz?qCV?hdiki^L7R;rn1q^qoidBpUn?{3lFn37G;Xzyo$|t@dz^Nz
zhQiImC6)K21@~@eQ**tt0;&fR4LuK%&U}@x(>%V*t2zgx?4xmAb%rK5_cCRl`Nvw{
z#KFK=4fmR^N_)m{em~`I+Li>D8{p&_r(VxzE_|M<9+9GZ6&Y(q)(z{rbVV&U8cm$f
z+z{7I_FZ5r%}q?vid@8$T^ez@lw9Y5ORTJhvFlzfySD!f?dx3ut1a01^xN)Q3p=};
zMQ_*ZS+qAkYYUdq3Pm$vOB;l_CFyVL%6&bFEYytDuVI0NSBwS-ztBO6616hkADhqR8
z*32fSyD`kq-|I4I3i)imlc5S~lw99cKbFc;M!2q>rEc>4PRJHKN6yv0R5Lv}+U2ud
z&GC;`UZyc
zAY{w}yvy#bI%RMh1CGIOD(T`rs`%Gz)%Lv~{b~cs5>>{ZkTlL&e06jTKF^dgOaOi6
z3iK~#GLUi4ormJ&Le>e8`o_WgDvv1J&I8DMoE0#g!h#gdzJU=+kbC7BAflLt@4K7R
zuU!2X=CGQR;CSNQ07D_qcnV~8kux$EhvLe$m6hpw9#pI^!_HuuF6TqEGFRO}z};%m
zHowH$waq?%e_L!ArXwNQej=&XjN+x`nI+|O`SLc05tq8JUd!hh!CBSya!Tv#-V0T$
z-YNEk==mc{-y#}>*T3hGPK1Pqi|wx-i>lR#%+k?m{4+S|vfU!-L)5n2j577vrkdpi
z^&LdFfsf%+ePa>(=`H1$`5=NWL%K-1u4a{?+a;bk-$QNhBBiG{It)G7?sMGd8Qpkx
zXQ?BmW_kWYouldna&B~;11n@n;rV2kc$wx{Co}HzG%I#}z9-%0(nZrL#q{!&tOBK;
z#Z`uNXZQ!rq}GMd(ioAdHhSWEaGp{lI=I*U!1zwvq#4r
z#V>@;tBL`vPJ~SvJQyGcNLBzZik~-KRLNB8YP%64p^znj{M76&1Xu;a)-o|t2{IW{
zX+V{ha1?jq52MP=L8aFFI1*Vm>O+dyhY;z8Qn4Kx--GB+$FcOQvrKpQ+FAM$kc_zU
zBfBsckG*gjtjxmCI5|4wo(I_$0x0MF=r)-{-JNgiwW%-6CYCln
z$P*~OZ$6GtNQ}P}{`3k?KTup&H&;8?#X46@(d%Ybj8A=X`g(rAE6nh?<%6TZ&MuZ?LPBT8yX(3uSbF
zUg*+}8)&^>)e1JkR-r8-1xV)MuO{GXugRnNU!(`b0I;qRX@Hm~G{I}TBy9K1jwChKuAka2kFfBdNLs-*z<0Swt4JG5?K6=ZZzRwEM
z^HKaprV@bn4m2XDtFKYq==Q31fc@dQ0OpKom8=KFxu=*%4p)AGc&6vfBI`yMhw8oEv$e>97uzbUBjIZinS$&<2`$w_{W^i@EmcGmN@1Kh*
zGA$$PI#h07mJ`9@PcO~3$n98qX+iXz3Gyg9poY`wq!pW#pD3bx<%r#vj-7TMcr=_)
zcWLc8i;4;D^eERhSMI**%WNcdiaZ-!^ZTf9@()|@w=h~UiXsGa8~TE|P=$oo^GpVXK6
zs`^;wdax-L5%W>+XGofBBA%rpLt*{&{KM^fEf^p3k&dn|XKHarxb_~6vD^CjF0+^%
z?P?MYmh4HZ>rvmO8$yfUE~_!#(P*pZCVHl(UF4!fP8{`mcnfew(X#h=CANkbKKK#3
z#NoJi|BA&{P1V0~M6fF{4{aYYEHg?xlN?f0+dpEij!Ir}Gbo7jT6$6r$
zheB`V!g6wP3rX+^=*#dvHOIevlOStP*;fo0{#RUKN^y??Z2(3GP}p&xt&GitIBhWY
z0s-w+biTs2yFS{U-mF4;a#*2;X~-wismNtH6M4sE--&i+so+FYeYW#E@IpT{uR2bC
zEm_i9nN;oO%PMls+4n+zgQ@mmqTP7{J5zIJY_`Q9+%6@Esxl-?lXuiHud82nKC>Po
z7QM$Vi?I2>6CFWi3#ZuMZ=Y%!ZbZhOZoP$11EFs
zSx@?oUhGpNM5wUxQ+g(9hGKsdj8FO~u^{tS{TP|N(*(~f&*jt6{uW~4?@>ZGRwuL4
zrTd?|F*7f@HHv2KNE+W)NWaZLd
zjh27qruR%(YOgJuyzuEdBlQcW_di)Me4x*HWXEU_j1r*ZdhT`NVhQq^
zA=4I)fkl|&>{af$&m$^CYGSo#Vv0;_M}@(w>u*xL>U@VTwRbGD+lObXzgo;qjY2Mj
z&goNb$9=19m7^8QKHczT+T#+T{E*
z-tA*B*-xZsq~R!8zV%ZAE=hJ8
z3m@OGdWsw-$6og+7?%WU91nH-Lp9Xg&$|sT7=w%xGzo4YBz+XM?VFk_{KJV9QzQQY
zA}8v&=rixkR;vnr@SDUybtM{*7gB*u2{HQ&@~U2fYKJ)yn9qikmlQB7>mK;>;7+k0(=Y`iX&JLMq$92>^cj|a}Kh^yDD;~pqKBAgN
zhg^o|EfzKFXoOQlAtgA#o%!IYr#v-TrBA&(_fNkpjW2Z3oUk?5Z2B(k!q>Fo>lZVp
zJt!QcpV2SA20B|)@>43qGedBMA@$a;l9Vq#t)nkWsy!d6%$f(b-Veqv=(t#f7p`Mx
zQayLnYdcQV2GAYtbhLJ8e!n|0KgGnk)Xfejs(!+k+k%VOgUZ?B+aG>LQ?$W*`<8F3k|7=XTe
z;%-7zXz%Y(RbNe^Ek(dVZ@
z=Q(FAOU*K}s(iLr-0OD=acv!(P2`4!p8V?xCiRR*?Ud?FlXr$|`%A|pmz=@x6VEzU
zn8x$_#~Q_*OM_-;CJ&wba9N1T;=)ww33
z6|015kHc8Y=plU#oe6ff9?JORTcqc=+Hckr%O`>0kj_I(0VxLT0GbjeNDf!{2Z&@O
z7rKsz5NQS!GV
zTp7DCfl7(Q@v^enXTGK38^>9(cz34|_gba<1
zZ8~Bez`A-6>-$=<0!PZEmSq>P6yG#M4iRTXyhX-x@q_Z?6u}|Kg9SushpDz>7ke*G
ze|)i#o)S4#8Shkqa{|X5xie(UOGDofdsR>Xz{a`CR%ICS
zKDK}L*pM6Z;Nq>v%GK%GUfU*)Geo#vJ4hc+7i#0LO5nQVVEFi4D)@1EQz`pOE*x|4
ztbR9^Djl{X&g@*ehu}D=wpix%J$H9pV(f*#ADW??%hm5ce!hK)Y3KVgYag9dS7GiI
zpm9)t>!WHgYghv=!vnak-K@>F}kfcQ(5`x
z#aK0g)KmaLGeuw^Wh9%E&%G?bk&Ti%l+FlA7&L7y0Z^z9(lNjKLGfKy&}fY?*n7ax
zhT;NxtqOe;qyMLm;15oCGZ1QkFw$vw$l{(q3jLK2lWTjFG9ZzuIoYb+z4Wo0WtxUqHbfdjZch
zt}FPL8Lv|_1l#3R0^Q6mJt7|F(!clD5gC1`Y|-}<>Kd~u!TvMzW~84=%2h%!?}t8fU!l
zQNK5EUD^GpKnmLPaB+aJy`VIrek)A5kC>mAw$jT>u<$Lo*GvkxZg9)I8uRO^x4pBl
zhd2KQ3bpUK1JxGP$x|zXW+YmLBJ%Rykbz_*J%b1TRwW~cK(xMqM;}lPe&vnu=YhEC
zp(LB2Dhz`)OS0`<}lMGnaanX$04VfYAV=_y^ccNvXQpiSEDHMHz%rqQ^Z{`&QFTI
z4>>({H-La!aoY331z=TxV5D;Jnr-VZ={YQj+i}Wocd~sF(=u>8+4^tOn5#`CZZJ;Z
z)6%>CSngGgwBZXudtQjJIz0kgkUYNzKld+Zdz=%l%
zI^25*;R_T&RUj1IdMJR0K-RuU(pQWu7C8hS!p-y+cI#~xyj3ZTD!AeaxjUXWl<2%T
z9=*L8iUk~K^cx!#np?>1h*FF&9f&dY^)W|GTvN9WNBR@k$sb}kP2*ok-bj^f_T&L)
zNel;xv-4J$`(EF)YZ&vYe$_FhbpGq7^<7bwB8qL;L&b$0e>!x1;UH<|JyiRe|1bA
zJRVB1-65V1yUMkuA{z1kZk$yyTbnmfk5EEeno{@(NCw9K2!JD-+-ZI2c}d=PTcBiK
zTO9rlpt-^R{5I%r0V5s|0qkQCPi&0jT{kN$Q;=vRXwPFTTZPVR6MPTQUT|iEN8pPV
zFswKi!_5cFz-HGPcVdaa-vXGYfU_wi;9B`N*EMMsZ!8#o1yX$5I-ZTozj=rR*|yO!
zZw0R;*A5h)d1(``kGv4To(v%B!ca2=Of6PWr+Wkh=l|O{Jb^a@U_(H$KvILuu;ZB~HN{_7{SY@UG?V3(W6D$B%Ro}%=Gv9I|ho3T&j
z?&v)d;y5AH4uu%7aASD*{vaLGh3dzQX6@GwDF7(PzL@a(pBg%C>&
zY(r9lr+Hqg5z+K&vlZBAa8+4bEbl>?1X+xHr_Bf25i_veT{E9NeKFl%BY=k91^*gC
zEY2knv$eY`LQnz-1;6fE27xd@N%43>Hy3bX{#(F`Ho+B>FW_?V&LEcs;
zAeItnAR8G8xKIoVO_XecA|5o>Dgg|mU+*FEZeIaDA+V-6WGn6OfFZ_#{l8~vl(p2=
zrhnxft`4ulqY4m@k~{%;GGN01=4&g?Jd
z!QkKSgd`f62h0SU03fem?`LxnGYMqR)c>RaHY!j?5?cf9;E9F}XlQth8x^v!CV=Oc
z(1TtquoC7=D%?)=uzF%dgs%bb7j8&Tn9ofQK$k$CUk;up?v~+1TMON52j=i*uypjX
zaqyDc%g7?C6|!qzE2f>}FoZFMhAt7R-1=k7b`nfYHM-i2eQ6DRc-X6mYv4vqo`9;;3x0UrQ+zGWPka)DDK1~5KNQD%IohYo*Z9rOU3A80obwmBGA(BaT{l&%Y-
zP(WJNj%@vEoLq<|L(^q$fSUvPiUawO0pZaH1*>;O4`ZZn5W}VK38wp=-RJ&OgnBVn
zd0Uju0)_{H!=4W%_|WE&RVqdXlod=ZJSM`{Kx~tM2o;A6Z~-*&Bd6ui`%PNI6b-_S
zG$cL;j72ti21mBVfV|k`ZSA!68?xZihbKe>OUS`giTE}FSLAzpEJ`Ys!6%}pXC;Lq
z0_S}NfFi*Z4*~$5Rmmopm^fun37k)_pn&z^^c6kipsmKu%cJ-2AILuv@LC92p&XGr
zfiX77dkj@=53Nv-_l}#3c(^yG>_a*oC18R=zkG|TijlB+fD$m^fjVE=0s(g$Ue2e`
ztMJ4qlu2q8dhi0a9a+9eTU|jvyj6jM=YS_y@^HU_#=t`gJ(Rg#Re**&Gku~b0n155
zGSM(VsM9=K>Wr5gMT~)P0gM+~@G0O&!yb7Mf9AkkIWGY(Ck8k@;42hV-620AP2g{9
znFN&JEzogSMmAv^vq9H}x?w1`f#IeT(CC{H=<-
z1Q17o^C-%`bFJ-YYheyoJvmbjX=`{=BJwP}TlXaa__9Y#^kG*aJ>G+|r_E5N@Gill
zNFd{cz5zBe@(N$^%!5xZgC@8@<=^lDG!6)wC9-ZqH$kA~7U136%cOZi{t-~4@k1BN
zm?2aTznbJrkOB!5mxEs3ZcY?1Z{~5Lcub*MRucM1a?rcP2+k#V*WJh(tkk=^Wv>WJ
zpltZKO!Kx5hiS^yUPcPG(AKb@0L1utQmZh+*6wFgLCAPff^n{SXiNoID|#@XT-Oep
z>2X>MOIR7ad@HrLDh>lrmB*ofs~05^Ujg(~F{kzQQd0$+-C)R?c=KS=)fVLEUb<1db3_Ljnf=&Oz`laewq4c;Q
zZ%dEkybyABe&l_Di(H4k6aX!Bu{8~0f8{Z>mNrwg#|V!q8S++|h!|KKVn8{-o_ZYa
z_dtRWbTEiZp%Mgj$@dBh41R|9O`_#e8pb2oRc=Rx+(;Qa%yfHAmC;>Mt
zkOjc;&{yz0M?4Xp@1|648OfC0Hd}+tr$>}P2{Lnz}o_^&G7vqyb
zmpBSuIq2R?5#Yq=0Tw5Wfs{cWbfoY|F%bx79g}SGP>0(OZ_Q6*o_TPcc=%899(o7-L%dVqV9SlUVq+rv
z;0SQK#M^^zLk12}WHF$AuN?H0($_;b!K#FJYZ`-VA+IOE4gxm|Y`m7d1<^8C!oz!d
zwcv36?q8+`5!>v>`|#CsEu)E$Pdk~OD
zl#P+R!zlsITR4{#X!#0+)vKKoC^+1>96=O!@5Ww%pAc*Lpp_MZ*I0G$3Zn0U3-p02
z3OOSN^pXKf$jD6CUf=I{6)V7#3v_^B36Fb8JeXxzQ`nS@QvxL{^EWS6@``YQJ|8cC
z!PUN0er^MUE(gSmD
zo8%QqqReSdN1?Phq9kTFpnctS6PB>4NQFD#K{^#+rrV8TDk{K6xdE*7dRDt-5Jxxa
z#+ARVz^P{=4DN5ZRrDR$7fFxD#t7a53Fbh-t1iG5=gI|4LLd~7mV><^y!SokxizO$
z#d8=suz4i8_;?H}^*H1zlipOpTp^6&`8!n)F&~k}d72*u;N=Ic^^WkT`R>Lz@>zi~
z%43WvxFdm*;=9$xTj5{`R<|%rINWebew2XWZtQiqQ^-dIs<+de958)gc4MRjugDO&
z1e-m#i1lEFi}^o{y#-iQ&-XBXcVUs17EqCnWkEtvQE(}V1zC^|$ps};Ku|(mQd*>>
zgarlZZVW^k6qOE(Fi=oYQN(!9#m_f>zyI_6-}j(*Z_J%LbLPyM6Ekz9A&5AIfTfi>
zJA1N7M~TKVOWbHZtlC}699hepMgUbx8v0ik31!WjY#AvfTBI?8T#6y^aCJzdmI!(!
z5-NdZKoT4WaAzAQXgX$HE{OjLn1d+4Xgmr$1iBJ28{l-}0aj&405u8JNv;kE{{aF9
z8Dsqj;TqX~g3nM!Qi}{uX{bLG9?^nuI%vKijM6{?`a9ziG2?@t4r7X;cwc4(O=puU
zKs1l=4g|w>@Ax)Ba~>@aD5KEudT2UBVXhQ&X|6J89V$-I6!bjU7Z4tRHwuBl9K>X@
z`C)x~&qus56}oX;R7Nr%6g&KiQw?I_QVR)Dfawj^ay=!muV5OIrey;gIBA8GX{Jy$
zdKv4=4up{xhqX3G2n9eT
zSZjO=b(8E6
zW?=#LB^U|h%^WqX9=_1UpWfZh~c9UQ%*Wn!exL%7}zoms!iLh
z5P?OS2v5=7uR)8^hY<9JvTh)({y;^Fhord7NTJP9I&2iiVh+W?i9@iK1{m-oGT3v>Vy^nCm}+HndMvq6pk{E
ze+BG?R2hhQAq3Sz=y8A~L+H;`^xaJW$s8sLCZol0B7x5zOtiWgVE2X22Qy8t??{iT
zA#oM`tC27iyl96xGgk+QpIT`9K??!^R_XDqh4=xgc|5co{~{g~L!gR(z)&;{$YT?5
zqM)51OhhEzsfMcpZ_R;`wx(iSi6BLhLAQkLaH23?Lp&`YBcmc>tW%8{54t-M0QR0H
zA0h#+Y|+lgaG6%Vc^3{Cn61KtgmHCGy9J8iyFLl)MxiHoHO
zOG`j?{{b)|m?2yxk+#$#DuNE709;ghLLms365-vbf`-t5*AvkP{{{{$jfMeyBSab%
zg5DEF!^y%R&2@l4E*D0CsXbanV86f#)1)P^u=m`50E>@7FQ9XR`xv&%DMCmx(nRzY
ziV9fOijL9$4L17BS~1pO=@+gwP~x{Qh>NV*Y-C}SC7><#Z(uCziP0qf`x3k_C7QS>
z1cVg~ti8?vh7=_XJO|buTEmzJ?S~;op-c~WpCO{DPe#`$@2ybId_O^2$&v~V=6P|-iB;Rl*U
zOe9`rQyqd!aKgmnBQ>3v4S}_GfFBY&K!V#f5ROyCi3h{NLgq1q>|vlchalhR=!OWJ
z7eRSIe1dZcPSigFCjj6Lf}Pp~BoHd-AR_o$3S%u~37r(|K8TGbYExuHIGV^1*$gRR
zBEmq%2pd?65Mj-G9DJaTDuHkmFlnJyKqjqCdm*QxD7=qalm8na8f^(&OQcEAXt0GL
z4WUsP!L}p(7bPU5D5g{sjV1`Maoxj11}JU7X8yVo!f=WKUMazd1}&yhLB7Nx>6%V;
zxHM@{nt*U)%%nF{5l|hN3Q>2VEJRdmG>n2ug;N2ORLBju8dSjkgjpEIjKatP?ns1}
zf(WDR6P!UrU@@?@7`O>3VEzV#p|8^ZIglYOlBR>sgOR2u)X*~E4bU&c?|DLTV>PAY
zL_4W4CqfwP_hg9pssE2n3WAM-X8|cG3#Y~uLTrjKPaafufkyr-A0st2Gz42Hjf9B~
zfGvXnQJ;XhAlO;&2KZq_2t4$KprC>=t_cJ~RK5X0pKqQu^GV0QX5=7Xq=Ye40@y7q
z@SwtLP;_+hHx23vt|v}FD4>yoNZ`qgHK_*;badfC4H=C_2zE}?%Y_0Cv-$I%0|%Tk
z|Gfzz0kIvZ(F=+MLICLEAh@99h1;AUo(3{w2hA*{4+;*Q{(o>Qly~t7W3Z6~GZ;X?
z$_`Bf!e2=|Xb=cTcvMYMObN4S2izOr0Nf!Y5pND41?zG~j1vv`1(c2xhPiY;D$oE0
z-erWAf>WRq_$441;iB*u_yiB224NiFGSh|Me_&JgAY-Ak(EfF6^Oy1O71|7&Nvy5G
zMz?tnRWLA{_z*FRK`^?gof5?lun17u7?I7Gp>i;YqC%)4yw8j0X!<9+znQ`jwo>E)
zV;332i;#)JSX)L8VH&WVP2j0%&=(yQ#t;b*BpZ}07?2?b9J*Q>x&stOn4T;x48lB2
zEP%OEgfiF|kC>(#BSPGKn?^-{Kv;u*1GPmDe}NIkGoZa25}+X<@U19dLz`((6#W|*
z(Q~Z8-)m+fH2r@81p1cd>Y}AnqDo4-le^PO3T1>5iN6UTQXfGy4ksf4-2rSW8O%{N
zhR~LbPz6I^I#6ZcSTQxsPz?
z?M1x9iAnZE^F
zpehV)MqxOaI?llZEK(CL{{akmrg>Sg1>WTFB6<><2;)M9qGGA$;HF7~P^!|@5^z*Q
zlff_p5BQ+5V9-bLie>@*?Jqz-M!yI*8F3QywD9CoSpHE2eB~dCKvSWdu}BnX7IOOs
zHj(tN3+Uzuj1g$H{sQ5@i4ib>1F;<}O6bGzWcU(+8DD_^$GVL!3qVcUrCNXAW#QE)
zl0m>px-2*u3-K2OJgK@!!|~`y7NhlvThO!+4#Hw)9`e8IzVd9`2fvN&kL9
zSjGwc&R@<0c3#kX$r+{yj*7s<$W1>xaBk=h2<_jmpfW5HkI|^Ur)?tXA54KrPLD&V@FEuS!QxdmDHxYVqe*=lXEg1(`qerm-a|le2b5S8bXbdtV
zdV`1{ku2Cm@JpjtJcbB(jQ?j_qq9J0@la`ICVh~AH*3iZX*XXt1A8(yme|k@;Xb>7
zHR@zR(RrZkg&>$l!%VS0mmybWe%P`P{_t?yDAJTKb12djBK%5Ip?+xW&NQ58CxHb#
zYkYEauUsNAaRD!sla%tWr_yWra`&u_|d;<
zsD_1gp>#T!6(CeGy0;M=XeDvbcBs5nD257}eYzSv3godJ)hM+CsL~kk`s{>~o=tnTp__7LD!i^eU3^5HC
zm1ZbP1&fFh|4^ixf}29CVUHQnoQ5Zhl4%_1RC}y66-5C0F9R`kvOtSYpuqvz3{Z_t
z2?vs3)(z9LX4Zw9kc@|*ONFx=WK(IFWf2FZwg&Gm5FSW6|3#=MGSMLewN;{-{@)?Z
z1tzz`d_hoLbYx(}vF15!-npRYo?w$8d9N}PP5}pug>s4%z|z0|1n%>Z<4Ovp6RyOI
zz?9EN49e2FX#|u$u@J@@qeBMMwQjg+=Po=df-Xf;uu$;}HnQ
zkP!UBV2^@XGU(Q?Q8aM1fC7Mydtju3r|=6Sdm1Ae(7p(ilkj93h9?7BakH18@Ba_L
z31dj~H&UUOL@36?F#*C7lfZ6^O^GC@L8F^8>r!u^jb6?tYTHs0C;G&Q25rQ7m{5=~
zl&QpDC$E~iorLx==t>;0XG9^wck$jAS*yXMZ0Y{FAaX`ONXEtP&dLf|zwJ|5w9wo=)(x`$
zAl%9d4ZoolDEvYAJ_B+}pkja`0^Av1g}nfj+?jqmyI+hC${+2#7#9SE%^%ac7$<48
znc(N}>i1#oLxZ1LjE0x)9qJk~b{FZ0>Y5y0o#zJNXyIIzM2xyN;SWOaRRquwIp^mt
zS#NXW$qDpQ(B)!!CIIzFZ49up`s^?pT1Upjp8n=5qqSMD&`+2&)-Uw7|E{-*{SUeVakku~b@7t>DVtUvVP3X-px%H{6j;*^!BwSx-HS~2b=)S{lOAjJ)*&~z6s
zoy*Kj^dtPz&YIzn6i!tReb8+<6!4JANh(e6ZZF$?AjCl&ODqJC|E6PU_d~?KRUj&Vmoj>$AHs@*y(8BFMvQilbe-b}~J>>?yOJ
zA!cor^jlo3G`3=VFR@gNd*?evmFyuSEj8zyJ^bp%w9xo%ZYKVMG4vF@h7a5688~Ya
zZYLQ17Z}8{nkp))D;^T%RHW~Vs4drB|EKWzj;@?zTwm{G@R0I}P7MruM3$gZqPP7uvI1
zmh0Z-oOm|;h$i;>6WSsuV$s%DiE=K`r7}?rhbfvaN#9l_%CyAF$H(SmBaOTI^Uqag
zvz5e8zf;M6;EuM-UxHCo3G*nHTIghobNVg|o0$@H9;)u|5zUku9nA9BX{(NL9vE1U
zX1P+VBksY?Z4(om|1w`|uei2rLl=umWK`a((+jv#1CPj_R`cH_*dX@%O)9<>brLG^
z2CKOnzokxV(H~jNZ@KWq$bLwHw~9eoG4`{hN7t19Dn>0~8=bY|mgJ$&GoIwOfp@3L
zO&ZpZZw1>(8Trg*oO*)&!N!>sAECxBsg}RxjUwf0rN=oSoN?XbwSX9}f7KRFI+Nuk#o3?f_*~t7c!q6-
zU-r0qRG%CE=xnIY;j&Pr^PRzTSzn$SN&goA^s0-ub}@g~yY05E=O~L6T|mUQjIPj?
zn|y3(yKzBNxgI-26!+*Fg-FrREsZQ;kB!{Lqf^3Pjd24uefiFX$&
z7R&n)!QeW3jln&{-02k4&^rIyxe@19vh4mbl0a^|-&M>vRV)1Y{q$@TFD0e?o)rB-
zj(CO*Wc5wU?0;(z&^a}!{K!Ph?V;Fcv$k#e#V@ODd(QO;TTI`Z5zD%Hd!o-jhfwlq
z*i0hR-B`fRlC;P7q`oSHYAJDma?;H5-OF9Oj-VW=-rCYj6nwJNxg67>zI}m5Qf%
zaMi9Ro>O
zz6XqI&jF(~35C88^OYg#$CZZ9@;RmXFAs~^mTpfTR*ZdH?4H7{@^+B%_gi=E$PngF
zjOm}=4cEF)njJjxJgMQD&qtfv^Mb!|?KY1EJ{+j4l5Dx8njFQ%;1Vk3F5T7Za)5c{
z0Ys#0VDs4{+h|wMVp?#t?urvj1Acdy0_mC&WmRwB*wAQKtxpDFt?kY+8!~qiKNow~
zj^e;Cioah**)Lf%aXnvK^0~S?*(ny@e0{HuwaS5(y0L=zPuCa)sL`eGM1w6Sj~-XJ
z+uTP_DzCF#*|x5zxZl|Qj=^EUT&O{kS5|Douvf1y0h{?8}}zd}220x?9Jc4$!*;gLD#|odE#mc)SMOZ5YkUmPT5y*tN2=v3Bvg4l
zZ?DPLwUu%Wyx2HezLPU=(WkQ7vN~NcYk4@q1{n_)%GsBvsm#B2=kqtiANIWtk4ct3
zwf4ee+mjgGPaY61>n6t2H7{`weAbmcyKBHsrPar}cwoTH1K(JlKf+<-9i=bZ@9PkF
z@@_xnnbLlvZ#(Qf`0lrVX_(}XH_s{MD+^6~#+kG4XU1x@zz^nVRr!=6-RcJviol~+
z4U{Ho@k?#1xm%A{DzhRFM(N*VyWgZ^dQNffl2cK4PtPgKSSv_9
z-8QTFCU+oAIP_Gh6r=iQY>lG=-2yS}uz>&8E{6SUPh*`Fc7O2P;aqm?@Y$mqXZ?0w
zx+>G_`_eurpr5FfqC|av;o2
zZ|m+`Fe7AG#J*_5TAUBl*us6arfvGMk2jH=70WNbGgN`MO;V6IC)>O5<@wXG0o$Yd
z0xOqWL=Jo7j%Z@GJO5_q;@K;yo}}-@$Xx%5l_`@!eAqcyUEi9afkVAjJd+XG`9i2e
zL4!hDw4q%6x-c2_Ens&)`}rPv*0MXV*0*!J)OFQQwJY>=|3UmF*+r;Y%3L*ZGhV;n
zm0>HZGuQU-F5~esoG7nbddAmt$OMnKalG5NV(W_g^ap8{J^M+lLx7}milByv1+QCu
zg6s|%Wn#NR$ZF%mHTar*pL4Dy7$HQ-Xy>W%`8moDo2%m
zjCY@=8*?sg*fuC5c!>SpK|Z=@GYnTwr5$g?)|a>A@5;J7e5N;9G4FTFsH!4!>SU4G
zQlNV?gH5lyR8k)sYpw0TiuRQ+*6d|0muhu0jvB9>vk+4WkgH6koLssmTbJiv)=)&E
z*sAa4%9=85-g(&gby!*ZdF`sl-n`5Nzh^|L0{g)-`KC|#1FiB$C5|M%9+%y+>qUFp
z_*R+hAUTOU##ugyL%Gef3In{PvPvqqLc@?aQ_z|8JC5halpY202Jal&hzQB9nsu?t
zRHtodzG05d7%ewGl=f3Q{y-W5BXGUB4okk-)?2rDT9$mgy<>m+MbKXP3D2mh)M95ZC@nMb6q*N*!C?>u;B;$=>cN{5KUq*Ex`Mx@9%{
zn$FWR%gcd`YPd$6#_sNvTQXjI2n+jG?q!{A2s&eTzKP+0%(O|2^NMtS(&w!G%v{T{
zDqL0rq2Yydxjc2F)=l_oLxmlF+k(?BbUb@
z^%!%QZ%bHN{7Z?H7G8(r^=?+I8aFTbouW;K9yyX@)ifVY+?IS|Zpyhm*7T8KDhJKv6yNa~WlQS3?Y>2*n>DJnIez22#g2VZnp@31qbm<@x72kMk@c6^R*?VV
zf}}(fv)5TKNyVLwy1#lKliaK@lUR|{)*;6t+J_$0vb=Jq%gB3d=ai@&{hh6B;+y3w
zd95io^Qig21~%L2B{I9@AEd36D33*;?A9jH+>`=r&rIKVO~dp9nVQP5
z2-^N=mt7H43Y_6bW{b9pPD(`i*J5wppPZSkjt)QA+E^I(^Ms~gn_C=@-CV74gi}sj
z`SI$8wx|{>r`$4}TxFEqFrB^GD=*h4o}M#Gvu}^i1r#7EYL)H}?03W_M6+@Tt6H`S
z2I@7e+*6A0EAR`JULaWx3P`gZI-^&$XuqVz!gWbRGv7E~U~($A1mCAxHF$g2YNwZn
z!_xJnhUlJemfobbx+)c)dg+Sz1U)JMUHtct}
zY6H)C?EIPj2MGg>mK4PM#HYr7tj3EVV-d1_yEskLLC@vEa;AH}+oyV=wC$2pIUUJW
z!en=tqiryiy6ocIBfD1DBH_Jn=6T+X_zS+;82R