diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..13566b8
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644
index 0000000..15a15b2
--- /dev/null
+++ b/.idea/encodings.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..8afd5b0
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/spt-crm-backend.iml b/.idea/spt-crm-backend.iml
new file mode 100644
index 0000000..c956989
--- /dev/null
+++ b/.idea/spt-crm-backend.iml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/httpdocs/.env b/httpdocs/.env
new file mode 100644
index 0000000..d42006a
--- /dev/null
+++ b/httpdocs/.env
@@ -0,0 +1,46 @@
+# In all environments, the following files are loaded if they exist,
+# the latter taking precedence over the former:
+#
+# * .env contains default values for the environment variables needed by the app
+# * .env.local uncommitted file with local overrides
+# * .env.$APP_ENV committed environment-specific defaults
+# * .env.$APP_ENV.local uncommitted environment-specific overrides
+#
+# Real environment variables win over .env files.
+#
+# DO NOT DEFINE PRODUCTION SECRETS IN THIS FILE NOR IN ANY OTHER COMMITTED FILES.
+#
+# Run "composer dump-env prod" to compile .env files for production use (requires symfony/flex >=1.2).
+# https://symfony.com/doc/current/best_practices/configuration.html#infrastructure-related-configuration
+
+###> symfony/framework-bundle ###
+APP_ENV=dev
+APP_SECRET=87663e220c9c6c788a7a600f2c718777
+#TRUSTED_PROXIES=127.0.0.1,127.0.0.2
+#TRUSTED_HOSTS='^localhost|example\.com$'
+###< symfony/framework-bundle ###
+
+###> doctrine/doctrine-bundle ###
+# Format described at https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url
+# For an SQLite database, use: "sqlite:///%kernel.project_dir%/var/data.db"
+# For a PostgreSQL database, use: "postgresql://db_user:db_password@127.0.0.1:5432/db_name?serverVersion=11"
+# IMPORTANT: You MUST also configure your db driver and server_version in config/packages/doctrine.yaml
+DATABASE_URL=mysql://root@127.0.0.1:3306/spt_crm
+###< doctrine/doctrine-bundle ###
+
+###> nelmio/cors-bundle ###
+CORS_ALLOW_ORIGIN=^https?://localhost:?[0-9]*$
+###< nelmio/cors-bundle ###
+
+###> symfony/swiftmailer-bundle ###
+# For Gmail as a transport, use: "gmail://username:password@localhost"
+# For a generic SMTP server, use: "smtp://localhost:25?encryption=&auth_mode="
+# Delivery is disabled by default via "null://localhost"
+#MAILER_URL=null://localhost
+MAILER_DSN=smtp://spawntree.de?auth_mode=login&username=re@spawntree.de&password=EisenKnud2016
+#MAILER_URL=smtp://localhost:465?encryption=ssl&auth_mode=login&username=&password=
+###< symfony/swiftmailer-bundle ###
+
+###> symfony/mailer ###
+# MAILER_DSN=null://null
+###< symfony/mailer ###
diff --git a/httpdocs/.gitignore b/httpdocs/.gitignore
new file mode 100644
index 0000000..a67f91e
--- /dev/null
+++ b/httpdocs/.gitignore
@@ -0,0 +1,10 @@
+
+###> symfony/framework-bundle ###
+/.env.local
+/.env.local.php
+/.env.*.local
+/config/secrets/prod/prod.decrypt.private.php
+/public/bundles/
+/var/
+/vendor/
+###< symfony/framework-bundle ###
diff --git a/httpdocs/bin/console b/httpdocs/bin/console
new file mode 100755
index 0000000..c933dc5
--- /dev/null
+++ b/httpdocs/bin/console
@@ -0,0 +1,17 @@
+#!/usr/bin/env php
+=8.1",
+ "ext-ctype": "*",
+ "ext-iconv": "*",
+ "doctrine/annotations": "^2.0",
+ "doctrine/doctrine-bundle": "^2.9",
+ "doctrine/doctrine-migrations-bundle": "^3.2",
+ "doctrine/orm": "^2.15",
+ "dompdf/dompdf": "*",
+ "phpoffice/phpspreadsheet": "*",
+ "sensio/framework-extra-bundle": "*",
+ "symfony/console": "6.2.*",
+ "symfony/dotenv": "6.2.*",
+ "symfony/flex": "^2",
+ "symfony/framework-bundle": "6.2.*",
+ "symfony/mailer": "6.2.*",
+ "symfony/runtime": "6.2.*",
+ "symfony/security-bundle": "6.2.*",
+ "symfony/yaml": "6.2.*"
+ },
+ "config": {
+ "allow-plugins": {
+ "php-http/discovery": true,
+ "symfony/flex": true,
+ "symfony/runtime": true
+ },
+ "sort-packages": true
+ },
+ "autoload": {
+ "psr-4": {
+ "App\\": "src/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "App\\Tests\\": "tests/"
+ }
+ },
+ "replace": {
+ "symfony/polyfill-ctype": "*",
+ "symfony/polyfill-iconv": "*",
+ "symfony/polyfill-php72": "*",
+ "symfony/polyfill-php73": "*",
+ "symfony/polyfill-php74": "*",
+ "symfony/polyfill-php80": "*",
+ "symfony/polyfill-php81": "*"
+ },
+ "scripts": {
+ "auto-scripts": {
+ "cache:clear": "symfony-cmd",
+ "assets:install %PUBLIC_DIR%": "symfony-cmd"
+ },
+ "post-install-cmd": [
+ "@auto-scripts"
+ ],
+ "post-update-cmd": [
+ "@auto-scripts"
+ ]
+ },
+ "conflict": {
+ "symfony/symfony": "*"
+ },
+ "extra": {
+ "symfony": {
+ "allow-contrib": false,
+ "require": "6.2.*"
+ }
+ }
+}
diff --git a/httpdocs/composer.lock b/httpdocs/composer.lock
new file mode 100644
index 0000000..8e87fcf
--- /dev/null
+++ b/httpdocs/composer.lock
@@ -0,0 +1,5829 @@
+{
+ "_readme": [
+ "This file locks the dependencies of your project to a known state",
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
+ "This file is @generated automatically"
+ ],
+ "content-hash": "9de26d479d080ba6069e18310ae49ab9",
+ "packages": [
+ {
+ "name": "doctrine/annotations",
+ "version": "2.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/annotations.git",
+ "reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/annotations/zipball/e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f",
+ "reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f",
+ "shasum": ""
+ },
+ "require": {
+ "doctrine/lexer": "^2 || ^3",
+ "ext-tokenizer": "*",
+ "php": "^7.2 || ^8.0",
+ "psr/cache": "^1 || ^2 || ^3"
+ },
+ "require-dev": {
+ "doctrine/cache": "^2.0",
+ "doctrine/coding-standard": "^10",
+ "phpstan/phpstan": "^1.8.0",
+ "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
+ "symfony/cache": "^5.4 || ^6",
+ "vimeo/psalm": "^4.10"
+ },
+ "suggest": {
+ "php": "PHP 8.0 or higher comes with attributes, a native replacement for annotations"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations"
+ }
+ },
+ "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": "Benjamin Eberlei",
+ "email": "kontakt@beberlei.de"
+ },
+ {
+ "name": "Jonathan Wage",
+ "email": "jonwage@gmail.com"
+ },
+ {
+ "name": "Johannes Schmitt",
+ "email": "schmittjoh@gmail.com"
+ }
+ ],
+ "description": "Docblock Annotations Parser",
+ "homepage": "https://www.doctrine-project.org/projects/annotations.html",
+ "keywords": [
+ "annotations",
+ "docblock",
+ "parser"
+ ],
+ "support": {
+ "issues": "https://github.com/doctrine/annotations/issues",
+ "source": "https://github.com/doctrine/annotations/tree/2.0.1"
+ },
+ "time": "2023-02-02T22:02:53+00:00"
+ },
+ {
+ "name": "doctrine/cache",
+ "version": "2.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/cache.git",
+ "reference": "1ca8f21980e770095a31456042471a57bc4c68fb"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/cache/zipball/1ca8f21980e770095a31456042471a57bc4c68fb",
+ "reference": "1ca8f21980e770095a31456042471a57bc4c68fb",
+ "shasum": ""
+ },
+ "require": {
+ "php": "~7.1 || ^8.0"
+ },
+ "conflict": {
+ "doctrine/common": ">2.2,<2.4"
+ },
+ "require-dev": {
+ "cache/integration-tests": "dev-master",
+ "doctrine/coding-standard": "^9",
+ "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
+ "psr/cache": "^1.0 || ^2.0 || ^3.0",
+ "symfony/cache": "^4.4 || ^5.4 || ^6",
+ "symfony/var-exporter": "^4.4 || ^5.4 || ^6"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache"
+ }
+ },
+ "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": "Benjamin Eberlei",
+ "email": "kontakt@beberlei.de"
+ },
+ {
+ "name": "Jonathan Wage",
+ "email": "jonwage@gmail.com"
+ },
+ {
+ "name": "Johannes Schmitt",
+ "email": "schmittjoh@gmail.com"
+ }
+ ],
+ "description": "PHP Doctrine Cache library is a popular cache implementation that supports many different drivers such as redis, memcache, apc, mongodb and others.",
+ "homepage": "https://www.doctrine-project.org/projects/cache.html",
+ "keywords": [
+ "abstraction",
+ "apcu",
+ "cache",
+ "caching",
+ "couchdb",
+ "memcached",
+ "php",
+ "redis",
+ "xcache"
+ ],
+ "support": {
+ "issues": "https://github.com/doctrine/cache/issues",
+ "source": "https://github.com/doctrine/cache/tree/2.2.0"
+ },
+ "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%2Fcache",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-05-20T20:07:39+00:00"
+ },
+ {
+ "name": "doctrine/collections",
+ "version": "2.1.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/collections.git",
+ "reference": "db8cda536a034337f7dd63febecc713d4957f9ee"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/collections/zipball/db8cda536a034337f7dd63febecc713d4957f9ee",
+ "reference": "db8cda536a034337f7dd63febecc713d4957f9ee",
+ "shasum": ""
+ },
+ "require": {
+ "doctrine/deprecations": "^1",
+ "php": "^8.1"
+ },
+ "require-dev": {
+ "doctrine/coding-standard": "^10.0",
+ "ext-json": "*",
+ "phpstan/phpstan": "^1.8",
+ "phpstan/phpstan-phpunit": "^1.0",
+ "phpunit/phpunit": "^9.5",
+ "vimeo/psalm": "^4.22"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Doctrine\\Common\\Collections\\": "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": "Benjamin Eberlei",
+ "email": "kontakt@beberlei.de"
+ },
+ {
+ "name": "Jonathan Wage",
+ "email": "jonwage@gmail.com"
+ },
+ {
+ "name": "Johannes Schmitt",
+ "email": "schmittjoh@gmail.com"
+ }
+ ],
+ "description": "PHP Doctrine Collections library that adds additional functionality on top of PHP arrays.",
+ "homepage": "https://www.doctrine-project.org/projects/collections.html",
+ "keywords": [
+ "array",
+ "collections",
+ "iterators",
+ "php"
+ ],
+ "support": {
+ "issues": "https://github.com/doctrine/collections/issues",
+ "source": "https://github.com/doctrine/collections/tree/2.1.2"
+ },
+ "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%2Fcollections",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-12-27T23:41:38+00:00"
+ },
+ {
+ "name": "doctrine/common",
+ "version": "3.4.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/common.git",
+ "reference": "8b5e5650391f851ed58910b3e3d48a71062eeced"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/common/zipball/8b5e5650391f851ed58910b3e3d48a71062eeced",
+ "reference": "8b5e5650391f851ed58910b3e3d48a71062eeced",
+ "shasum": ""
+ },
+ "require": {
+ "doctrine/persistence": "^2.0 || ^3.0",
+ "php": "^7.1 || ^8.0"
+ },
+ "require-dev": {
+ "doctrine/coding-standard": "^9.0 || ^10.0",
+ "doctrine/collections": "^1",
+ "phpstan/phpstan": "^1.4.1",
+ "phpstan/phpstan-phpunit": "^1",
+ "phpunit/phpunit": "^7.5.20 || ^8.5 || ^9.0",
+ "squizlabs/php_codesniffer": "^3.0",
+ "symfony/phpunit-bridge": "^6.1",
+ "vimeo/psalm": "^4.4"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Doctrine\\Common\\": "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": "Benjamin Eberlei",
+ "email": "kontakt@beberlei.de"
+ },
+ {
+ "name": "Jonathan Wage",
+ "email": "jonwage@gmail.com"
+ },
+ {
+ "name": "Johannes Schmitt",
+ "email": "schmittjoh@gmail.com"
+ },
+ {
+ "name": "Marco Pivetta",
+ "email": "ocramius@gmail.com"
+ }
+ ],
+ "description": "PHP Doctrine Common project is a library that provides additional functionality that other Doctrine projects depend on such as better reflection support, proxies and much more.",
+ "homepage": "https://www.doctrine-project.org/projects/common.html",
+ "keywords": [
+ "common",
+ "doctrine",
+ "php"
+ ],
+ "support": {
+ "issues": "https://github.com/doctrine/common/issues",
+ "source": "https://github.com/doctrine/common/tree/3.4.3"
+ },
+ "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%2Fcommon",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-10-09T11:47:59+00:00"
+ },
+ {
+ "name": "doctrine/dbal",
+ "version": "3.6.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/dbal.git",
+ "reference": "b4bd1cfbd2b916951696d82e57d054394d84864c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/dbal/zipball/b4bd1cfbd2b916951696d82e57d054394d84864c",
+ "reference": "b4bd1cfbd2b916951696d82e57d054394d84864c",
+ "shasum": ""
+ },
+ "require": {
+ "composer-runtime-api": "^2",
+ "doctrine/cache": "^1.11|^2.0",
+ "doctrine/deprecations": "^0.5.3|^1",
+ "doctrine/event-manager": "^1|^2",
+ "php": "^7.4 || ^8.0",
+ "psr/cache": "^1|^2|^3",
+ "psr/log": "^1|^2|^3"
+ },
+ "require-dev": {
+ "doctrine/coding-standard": "11.1.0",
+ "fig/log-test": "^1",
+ "jetbrains/phpstorm-stubs": "2022.3",
+ "phpstan/phpstan": "1.10.9",
+ "phpstan/phpstan-strict-rules": "^1.5",
+ "phpunit/phpunit": "9.6.6",
+ "psalm/plugin-phpunit": "0.18.4",
+ "squizlabs/php_codesniffer": "3.7.2",
+ "symfony/cache": "^5.4|^6.0",
+ "symfony/console": "^4.4|^5.4|^6.0",
+ "vimeo/psalm": "4.30.0"
+ },
+ "suggest": {
+ "symfony/console": "For helpful console commands such as SQL execution and import of files."
+ },
+ "bin": [
+ "bin/doctrine-dbal"
+ ],
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Doctrine\\DBAL\\": "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": "Benjamin Eberlei",
+ "email": "kontakt@beberlei.de"
+ },
+ {
+ "name": "Jonathan Wage",
+ "email": "jonwage@gmail.com"
+ }
+ ],
+ "description": "Powerful PHP database abstraction layer (DBAL) with many features for database schema introspection and management.",
+ "homepage": "https://www.doctrine-project.org/projects/dbal.html",
+ "keywords": [
+ "abstraction",
+ "database",
+ "db2",
+ "dbal",
+ "mariadb",
+ "mssql",
+ "mysql",
+ "oci8",
+ "oracle",
+ "pdo",
+ "pgsql",
+ "postgresql",
+ "queryobject",
+ "sasql",
+ "sql",
+ "sqlite",
+ "sqlserver",
+ "sqlsrv"
+ ],
+ "support": {
+ "issues": "https://github.com/doctrine/dbal/issues",
+ "source": "https://github.com/doctrine/dbal/tree/3.6.2"
+ },
+ "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%2Fdbal",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2023-04-14T07:25:38+00:00"
+ },
+ {
+ "name": "doctrine/deprecations",
+ "version": "v1.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/deprecations.git",
+ "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/deprecations/zipball/0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de",
+ "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.1|^8.0"
+ },
+ "require-dev": {
+ "doctrine/coding-standard": "^9",
+ "phpunit/phpunit": "^7.5|^8.5|^9.5",
+ "psr/log": "^1|^2|^3"
+ },
+ "suggest": {
+ "psr/log": "Allows logging deprecations via PSR-3 logger implementation"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.",
+ "homepage": "https://www.doctrine-project.org/",
+ "support": {
+ "issues": "https://github.com/doctrine/deprecations/issues",
+ "source": "https://github.com/doctrine/deprecations/tree/v1.0.0"
+ },
+ "time": "2022-05-02T15:47:09+00:00"
+ },
+ {
+ "name": "doctrine/doctrine-bundle",
+ "version": "2.9.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/DoctrineBundle.git",
+ "reference": "7539b3c8bd620f7df6c2c6d510204bd2ce0064e3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/DoctrineBundle/zipball/7539b3c8bd620f7df6c2c6d510204bd2ce0064e3",
+ "reference": "7539b3c8bd620f7df6c2c6d510204bd2ce0064e3",
+ "shasum": ""
+ },
+ "require": {
+ "doctrine/cache": "^1.11 || ^2.0",
+ "doctrine/dbal": "^3.6.0",
+ "doctrine/persistence": "^2.2 || ^3",
+ "doctrine/sql-formatter": "^1.0.1",
+ "php": "^7.4 || ^8.0",
+ "symfony/cache": "^5.4 || ^6.0",
+ "symfony/config": "^5.4 || ^6.0",
+ "symfony/console": "^5.4 || ^6.0",
+ "symfony/dependency-injection": "^5.4 || ^6.0",
+ "symfony/deprecation-contracts": "^2.1 || ^3",
+ "symfony/doctrine-bridge": "^5.4.19 || ^6.0.7",
+ "symfony/framework-bundle": "^5.4 || ^6.0",
+ "symfony/service-contracts": "^1.1.1 || ^2.0 || ^3"
+ },
+ "conflict": {
+ "doctrine/annotations": ">=3.0",
+ "doctrine/orm": "<2.11 || >=3.0",
+ "twig/twig": "<1.34 || >=2.0 <2.4"
+ },
+ "require-dev": {
+ "doctrine/annotations": "^1 || ^2",
+ "doctrine/coding-standard": "^9.0",
+ "doctrine/deprecations": "^1.0",
+ "doctrine/orm": "^2.11 || ^3.0",
+ "friendsofphp/proxy-manager-lts": "^1.0",
+ "phpunit/phpunit": "^9.5.26 || ^10.0",
+ "psalm/plugin-phpunit": "^0.18.4",
+ "psalm/plugin-symfony": "^4",
+ "psr/log": "^1.1.4 || ^2.0 || ^3.0",
+ "symfony/phpunit-bridge": "^6.1",
+ "symfony/property-info": "^5.4 || ^6.0",
+ "symfony/proxy-manager-bridge": "^5.4 || ^6.0",
+ "symfony/security-bundle": "^5.4 || ^6.0",
+ "symfony/twig-bridge": "^5.4 || ^6.0",
+ "symfony/validator": "^5.4 || ^6.0",
+ "symfony/web-profiler-bundle": "^5.4 || ^6.0",
+ "symfony/yaml": "^5.4 || ^6.0",
+ "twig/twig": "^1.34 || ^2.12 || ^3.0",
+ "vimeo/psalm": "^4.30"
+ },
+ "suggest": {
+ "doctrine/orm": "The Doctrine ORM integration is optional in the bundle.",
+ "ext-pdo": "*",
+ "symfony/web-profiler-bundle": "To use the data collector."
+ },
+ "type": "symfony-bundle",
+ "autoload": {
+ "psr-4": {
+ "Doctrine\\Bundle\\DoctrineBundle\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Benjamin Eberlei",
+ "email": "kontakt@beberlei.de"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ },
+ {
+ "name": "Doctrine Project",
+ "homepage": "https://www.doctrine-project.org/"
+ }
+ ],
+ "description": "Symfony DoctrineBundle",
+ "homepage": "https://www.doctrine-project.org",
+ "keywords": [
+ "database",
+ "dbal",
+ "orm",
+ "persistence"
+ ],
+ "support": {
+ "issues": "https://github.com/doctrine/DoctrineBundle/issues",
+ "source": "https://github.com/doctrine/DoctrineBundle/tree/2.9.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%2Fdoctrine-bundle",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2023-04-14T05:39:34+00:00"
+ },
+ {
+ "name": "doctrine/doctrine-migrations-bundle",
+ "version": "3.2.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/DoctrineMigrationsBundle.git",
+ "reference": "3393f411ba25ade21969c33f2053220044854d01"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/DoctrineMigrationsBundle/zipball/3393f411ba25ade21969c33f2053220044854d01",
+ "reference": "3393f411ba25ade21969c33f2053220044854d01",
+ "shasum": ""
+ },
+ "require": {
+ "doctrine/doctrine-bundle": "~1.0|~2.0",
+ "doctrine/migrations": "^3.2",
+ "php": "^7.2|^8.0",
+ "symfony/framework-bundle": "~3.4|~4.0|~5.0|~6.0"
+ },
+ "require-dev": {
+ "doctrine/coding-standard": "^8.0",
+ "doctrine/orm": "^2.6",
+ "doctrine/persistence": "^1.3||^2.0",
+ "phpstan/phpstan": "^0.12",
+ "phpstan/phpstan-deprecation-rules": "^0.12",
+ "phpstan/phpstan-phpunit": "^0.12",
+ "phpstan/phpstan-strict-rules": "^0.12",
+ "phpunit/phpunit": "^8.0|^9.0",
+ "vimeo/psalm": "^4.11"
+ },
+ "type": "symfony-bundle",
+ "autoload": {
+ "psr-4": {
+ "Doctrine\\Bundle\\MigrationsBundle\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Doctrine Project",
+ "homepage": "https://www.doctrine-project.org"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony DoctrineMigrationsBundle",
+ "homepage": "https://www.doctrine-project.org",
+ "keywords": [
+ "dbal",
+ "migrations",
+ "schema"
+ ],
+ "support": {
+ "issues": "https://github.com/doctrine/DoctrineMigrationsBundle/issues",
+ "source": "https://github.com/doctrine/DoctrineMigrationsBundle/tree/3.2.2"
+ },
+ "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%2Fdoctrine-migrations-bundle",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-02-01T18:08:07+00:00"
+ },
+ {
+ "name": "doctrine/event-manager",
+ "version": "2.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/event-manager.git",
+ "reference": "750671534e0241a7c50ea5b43f67e23eb5c96f32"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/event-manager/zipball/750671534e0241a7c50ea5b43f67e23eb5c96f32",
+ "reference": "750671534e0241a7c50ea5b43f67e23eb5c96f32",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^8.1"
+ },
+ "conflict": {
+ "doctrine/common": "<2.9"
+ },
+ "require-dev": {
+ "doctrine/coding-standard": "^10",
+ "phpstan/phpstan": "^1.8.8",
+ "phpunit/phpunit": "^9.5",
+ "vimeo/psalm": "^4.28"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Doctrine\\Common\\": "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": "Benjamin Eberlei",
+ "email": "kontakt@beberlei.de"
+ },
+ {
+ "name": "Jonathan Wage",
+ "email": "jonwage@gmail.com"
+ },
+ {
+ "name": "Johannes Schmitt",
+ "email": "schmittjoh@gmail.com"
+ },
+ {
+ "name": "Marco Pivetta",
+ "email": "ocramius@gmail.com"
+ }
+ ],
+ "description": "The Doctrine Event Manager is a simple PHP event system that was built to be used with the various Doctrine projects.",
+ "homepage": "https://www.doctrine-project.org/projects/event-manager.html",
+ "keywords": [
+ "event",
+ "event dispatcher",
+ "event manager",
+ "event system",
+ "events"
+ ],
+ "support": {
+ "issues": "https://github.com/doctrine/event-manager/issues",
+ "source": "https://github.com/doctrine/event-manager/tree/2.0.0"
+ },
+ "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%2Fevent-manager",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-10-12T20:59:15+00:00"
+ },
+ {
+ "name": "doctrine/inflector",
+ "version": "2.0.6",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/inflector.git",
+ "reference": "d9d313a36c872fd6ee06d9a6cbcf713eaa40f024"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/inflector/zipball/d9d313a36c872fd6ee06d9a6cbcf713eaa40f024",
+ "reference": "d9d313a36c872fd6ee06d9a6cbcf713eaa40f024",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "require-dev": {
+ "doctrine/coding-standard": "^10",
+ "phpstan/phpstan": "^1.8",
+ "phpstan/phpstan-phpunit": "^1.1",
+ "phpstan/phpstan-strict-rules": "^1.3",
+ "phpunit/phpunit": "^8.5 || ^9.5",
+ "vimeo/psalm": "^4.25"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Doctrine\\Inflector\\": "lib/Doctrine/Inflector"
+ }
+ },
+ "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": "Benjamin Eberlei",
+ "email": "kontakt@beberlei.de"
+ },
+ {
+ "name": "Jonathan Wage",
+ "email": "jonwage@gmail.com"
+ },
+ {
+ "name": "Johannes Schmitt",
+ "email": "schmittjoh@gmail.com"
+ }
+ ],
+ "description": "PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.",
+ "homepage": "https://www.doctrine-project.org/projects/inflector.html",
+ "keywords": [
+ "inflection",
+ "inflector",
+ "lowercase",
+ "manipulation",
+ "php",
+ "plural",
+ "singular",
+ "strings",
+ "uppercase",
+ "words"
+ ],
+ "support": {
+ "issues": "https://github.com/doctrine/inflector/issues",
+ "source": "https://github.com/doctrine/inflector/tree/2.0.6"
+ },
+ "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%2Finflector",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-10-20T09:10:12+00:00"
+ },
+ {
+ "name": "doctrine/instantiator",
+ "version": "2.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/instantiator.git",
+ "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0",
+ "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^8.1"
+ },
+ "require-dev": {
+ "doctrine/coding-standard": "^11",
+ "ext-pdo": "*",
+ "ext-phar": "*",
+ "phpbench/phpbench": "^1.2",
+ "phpstan/phpstan": "^1.9.4",
+ "phpstan/phpstan-phpunit": "^1.3",
+ "phpunit/phpunit": "^9.5.27",
+ "vimeo/psalm": "^5.4"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Marco Pivetta",
+ "email": "ocramius@gmail.com",
+ "homepage": "https://ocramius.github.io/"
+ }
+ ],
+ "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors",
+ "homepage": "https://www.doctrine-project.org/projects/instantiator.html",
+ "keywords": [
+ "constructor",
+ "instantiate"
+ ],
+ "support": {
+ "issues": "https://github.com/doctrine/instantiator/issues",
+ "source": "https://github.com/doctrine/instantiator/tree/2.0.0"
+ },
+ "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%2Finstantiator",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-12-30T00:23:10+00:00"
+ },
+ {
+ "name": "doctrine/lexer",
+ "version": "2.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/lexer.git",
+ "reference": "39ab8fcf5a51ce4b85ca97c7a7d033eb12831124"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/lexer/zipball/39ab8fcf5a51ce4b85ca97c7a7d033eb12831124",
+ "reference": "39ab8fcf5a51ce4b85ca97c7a7d033eb12831124",
+ "shasum": ""
+ },
+ "require": {
+ "doctrine/deprecations": "^1.0",
+ "php": "^7.1 || ^8.0"
+ },
+ "require-dev": {
+ "doctrine/coding-standard": "^9 || ^10",
+ "phpstan/phpstan": "^1.3",
+ "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
+ "psalm/plugin-phpunit": "^0.18.3",
+ "vimeo/psalm": "^4.11 || ^5.0"
+ },
+ "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/2.1.0"
+ },
+ "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": "2022-12-14T08:49:07+00:00"
+ },
+ {
+ "name": "doctrine/migrations",
+ "version": "3.6.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/migrations.git",
+ "reference": "e542ad8bcd606d7a18d0875babb8a6d963c9c059"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/migrations/zipball/e542ad8bcd606d7a18d0875babb8a6d963c9c059",
+ "reference": "e542ad8bcd606d7a18d0875babb8a6d963c9c059",
+ "shasum": ""
+ },
+ "require": {
+ "composer-runtime-api": "^2",
+ "doctrine/dbal": "^3.5.1",
+ "doctrine/deprecations": "^0.5.3 || ^1",
+ "doctrine/event-manager": "^1.2 || ^2.0",
+ "php": "^8.1",
+ "psr/log": "^1.1.3 || ^2 || ^3",
+ "symfony/console": "^4.4.16 || ^5.4 || ^6.0",
+ "symfony/stopwatch": "^4.4 || ^5.4 || ^6.0",
+ "symfony/var-exporter": "^6.2"
+ },
+ "conflict": {
+ "doctrine/orm": "<2.12"
+ },
+ "require-dev": {
+ "doctrine/coding-standard": "^9",
+ "doctrine/orm": "^2.13",
+ "doctrine/persistence": "^2 || ^3",
+ "doctrine/sql-formatter": "^1.0",
+ "ext-pdo_sqlite": "*",
+ "phpstan/phpstan": "^1.5",
+ "phpstan/phpstan-deprecation-rules": "^1",
+ "phpstan/phpstan-phpunit": "^1.1",
+ "phpstan/phpstan-strict-rules": "^1.1",
+ "phpstan/phpstan-symfony": "^1.1",
+ "phpunit/phpunit": "^9.5.24",
+ "symfony/cache": "^4.4 || ^5.4 || ^6.0",
+ "symfony/process": "^4.4 || ^5.4 || ^6.0",
+ "symfony/yaml": "^4.4 || ^5.4 || ^6.0"
+ },
+ "suggest": {
+ "doctrine/sql-formatter": "Allows to generate formatted SQL with the diff command.",
+ "symfony/yaml": "Allows the use of yaml for migration configuration files."
+ },
+ "bin": [
+ "bin/doctrine-migrations"
+ ],
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Doctrine\\Migrations\\": "lib/Doctrine/Migrations"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Benjamin Eberlei",
+ "email": "kontakt@beberlei.de"
+ },
+ {
+ "name": "Jonathan Wage",
+ "email": "jonwage@gmail.com"
+ },
+ {
+ "name": "Michael Simonson",
+ "email": "contact@mikesimonson.com"
+ }
+ ],
+ "description": "PHP Doctrine Migrations project offer additional functionality on top of the database abstraction layer (DBAL) for versioning your database schema and easily deploying changes to it. It is a very easy to use and a powerful tool.",
+ "homepage": "https://www.doctrine-project.org/projects/migrations.html",
+ "keywords": [
+ "database",
+ "dbal",
+ "migrations"
+ ],
+ "support": {
+ "issues": "https://github.com/doctrine/migrations/issues",
+ "source": "https://github.com/doctrine/migrations/tree/3.6.0"
+ },
+ "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%2Fmigrations",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2023-02-15T18:49:46+00:00"
+ },
+ {
+ "name": "doctrine/orm",
+ "version": "2.15.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/orm.git",
+ "reference": "9bc6f5b4ac6f1e7d4248b2efbd01a748782075bc"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/orm/zipball/9bc6f5b4ac6f1e7d4248b2efbd01a748782075bc",
+ "reference": "9bc6f5b4ac6f1e7d4248b2efbd01a748782075bc",
+ "shasum": ""
+ },
+ "require": {
+ "composer-runtime-api": "^2",
+ "doctrine/cache": "^1.12.1 || ^2.1.1",
+ "doctrine/collections": "^1.5 || ^2.1",
+ "doctrine/common": "^3.0.3",
+ "doctrine/dbal": "^2.13.1 || ^3.2",
+ "doctrine/deprecations": "^0.5.3 || ^1",
+ "doctrine/event-manager": "^1.2 || ^2",
+ "doctrine/inflector": "^1.4 || ^2.0",
+ "doctrine/instantiator": "^1.3 || ^2",
+ "doctrine/lexer": "^2",
+ "doctrine/persistence": "^2.4 || ^3",
+ "ext-ctype": "*",
+ "php": "^7.1 || ^8.0",
+ "psr/cache": "^1 || ^2 || ^3",
+ "symfony/console": "^4.2 || ^5.0 || ^6.0",
+ "symfony/polyfill-php72": "^1.23",
+ "symfony/polyfill-php80": "^1.16"
+ },
+ "conflict": {
+ "doctrine/annotations": "<1.13 || >= 3.0"
+ },
+ "require-dev": {
+ "doctrine/annotations": "^1.13 || ^2",
+ "doctrine/coding-standard": "^9.0.2 || ^12.0",
+ "phpbench/phpbench": "^0.16.10 || ^1.0",
+ "phpstan/phpstan": "~1.4.10 || 1.10.14",
+ "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6",
+ "psr/log": "^1 || ^2 || ^3",
+ "squizlabs/php_codesniffer": "3.7.2",
+ "symfony/cache": "^4.4 || ^5.4 || ^6.0",
+ "symfony/var-exporter": "^4.4 || ^5.4 || ^6.2",
+ "symfony/yaml": "^3.4 || ^4.0 || ^5.0 || ^6.0",
+ "vimeo/psalm": "4.30.0 || 5.11.0"
+ },
+ "suggest": {
+ "ext-dom": "Provides support for XSD validation for XML mapping files",
+ "symfony/cache": "Provides cache support for Setup Tool with doctrine/cache 2.0",
+ "symfony/yaml": "If you want to use YAML Metadata Mapping Driver"
+ },
+ "bin": [
+ "bin/doctrine"
+ ],
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Doctrine\\ORM\\": "lib/Doctrine/ORM"
+ }
+ },
+ "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": "Benjamin Eberlei",
+ "email": "kontakt@beberlei.de"
+ },
+ {
+ "name": "Jonathan Wage",
+ "email": "jonwage@gmail.com"
+ },
+ {
+ "name": "Marco Pivetta",
+ "email": "ocramius@gmail.com"
+ }
+ ],
+ "description": "Object-Relational-Mapper for PHP",
+ "homepage": "https://www.doctrine-project.org/projects/orm.html",
+ "keywords": [
+ "database",
+ "orm"
+ ],
+ "support": {
+ "issues": "https://github.com/doctrine/orm/issues",
+ "source": "https://github.com/doctrine/orm/tree/2.15.1"
+ },
+ "time": "2023-05-07T18:56:25+00:00"
+ },
+ {
+ "name": "doctrine/persistence",
+ "version": "3.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/persistence.git",
+ "reference": "63fee8c33bef740db6730eb2a750cd3da6495603"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/persistence/zipball/63fee8c33bef740db6730eb2a750cd3da6495603",
+ "reference": "63fee8c33bef740db6730eb2a750cd3da6495603",
+ "shasum": ""
+ },
+ "require": {
+ "doctrine/event-manager": "^1 || ^2",
+ "php": "^7.2 || ^8.0",
+ "psr/cache": "^1.0 || ^2.0 || ^3.0"
+ },
+ "conflict": {
+ "doctrine/common": "<2.10"
+ },
+ "require-dev": {
+ "composer/package-versions-deprecated": "^1.11",
+ "doctrine/coding-standard": "^11",
+ "doctrine/common": "^3.0",
+ "phpstan/phpstan": "1.9.4",
+ "phpstan/phpstan-phpunit": "^1",
+ "phpstan/phpstan-strict-rules": "^1.1",
+ "phpunit/phpunit": "^8.5 || ^9.5",
+ "symfony/cache": "^4.4 || ^5.4 || ^6.0",
+ "vimeo/psalm": "4.30.0 || 5.3.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Doctrine\\Persistence\\": "src/Persistence"
+ }
+ },
+ "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": "Benjamin Eberlei",
+ "email": "kontakt@beberlei.de"
+ },
+ {
+ "name": "Jonathan Wage",
+ "email": "jonwage@gmail.com"
+ },
+ {
+ "name": "Johannes Schmitt",
+ "email": "schmittjoh@gmail.com"
+ },
+ {
+ "name": "Marco Pivetta",
+ "email": "ocramius@gmail.com"
+ }
+ ],
+ "description": "The Doctrine Persistence project is a set of shared interfaces and functionality that the different Doctrine object mappers share.",
+ "homepage": "https://www.doctrine-project.org/projects/persistence.html",
+ "keywords": [
+ "mapper",
+ "object",
+ "odm",
+ "orm",
+ "persistence"
+ ],
+ "support": {
+ "issues": "https://github.com/doctrine/persistence/issues",
+ "source": "https://github.com/doctrine/persistence/tree/3.2.0"
+ },
+ "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%2Fpersistence",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2023-05-17T18:32:04+00:00"
+ },
+ {
+ "name": "doctrine/sql-formatter",
+ "version": "1.1.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/sql-formatter.git",
+ "reference": "25a06c7bf4c6b8218f47928654252863ffc890a5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/sql-formatter/zipball/25a06c7bf4c6b8218f47928654252863ffc890a5",
+ "reference": "25a06c7bf4c6b8218f47928654252863ffc890a5",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.1 || ^8.0"
+ },
+ "require-dev": {
+ "bamarni/composer-bin-plugin": "^1.4"
+ },
+ "bin": [
+ "bin/sql-formatter"
+ ],
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Doctrine\\SqlFormatter\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jeremy Dorn",
+ "email": "jeremy@jeremydorn.com",
+ "homepage": "https://jeremydorn.com/"
+ }
+ ],
+ "description": "a PHP SQL highlighting library",
+ "homepage": "https://github.com/doctrine/sql-formatter/",
+ "keywords": [
+ "highlight",
+ "sql"
+ ],
+ "support": {
+ "issues": "https://github.com/doctrine/sql-formatter/issues",
+ "source": "https://github.com/doctrine/sql-formatter/tree/1.1.3"
+ },
+ "time": "2022-05-23T21:33:49+00:00"
+ },
+ {
+ "name": "dompdf/dompdf",
+ "version": "v2.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/dompdf/dompdf.git",
+ "reference": "e8d2d5e37e8b0b30f0732a011295ab80680d7e85"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/dompdf/dompdf/zipball/e8d2d5e37e8b0b30f0732a011295ab80680d7e85",
+ "reference": "e8d2d5e37e8b0b30f0732a011295ab80680d7e85",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-mbstring": "*",
+ "masterminds/html5": "^2.0",
+ "phenx/php-font-lib": ">=0.5.4 <1.0.0",
+ "phenx/php-svg-lib": ">=0.3.3 <1.0.0",
+ "php": "^7.1 || ^8.0"
+ },
+ "require-dev": {
+ "ext-json": "*",
+ "ext-zip": "*",
+ "mockery/mockery": "^1.3",
+ "phpunit/phpunit": "^7.5 || ^8 || ^9",
+ "squizlabs/php_codesniffer": "^3.5"
+ },
+ "suggest": {
+ "ext-gd": "Needed to process images",
+ "ext-gmagick": "Improves image processing performance",
+ "ext-imagick": "Improves image processing performance",
+ "ext-zlib": "Needed for pdf stream compression"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Dompdf\\": "src/"
+ },
+ "classmap": [
+ "lib/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "LGPL-2.1"
+ ],
+ "authors": [
+ {
+ "name": "The Dompdf Community",
+ "homepage": "https://github.com/dompdf/dompdf/blob/master/AUTHORS.md"
+ }
+ ],
+ "description": "DOMPDF is a CSS 2.1 compliant HTML to PDF converter",
+ "homepage": "https://github.com/dompdf/dompdf",
+ "support": {
+ "issues": "https://github.com/dompdf/dompdf/issues",
+ "source": "https://github.com/dompdf/dompdf/tree/v2.0.3"
+ },
+ "time": "2023-02-07T12:51:48+00:00"
+ },
+ {
+ "name": "egulias/email-validator",
+ "version": "4.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/egulias/EmailValidator.git",
+ "reference": "3a85486b709bc384dae8eb78fb2eec649bdb64ff"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/3a85486b709bc384dae8eb78fb2eec649bdb64ff",
+ "reference": "3a85486b709bc384dae8eb78fb2eec649bdb64ff",
+ "shasum": ""
+ },
+ "require": {
+ "doctrine/lexer": "^2.0 || ^3.0",
+ "php": ">=8.1",
+ "symfony/polyfill-intl-idn": "^1.26"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.5.27",
+ "vimeo/psalm": "^4.30"
+ },
+ "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.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/egulias",
+ "type": "github"
+ }
+ ],
+ "time": "2023-01-14T14:17:03+00:00"
+ },
+ {
+ "name": "ezyang/htmlpurifier",
+ "version": "v4.16.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/ezyang/htmlpurifier.git",
+ "reference": "523407fb06eb9e5f3d59889b3978d5bfe94299c8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/523407fb06eb9e5f3d59889b3978d5bfe94299c8",
+ "reference": "523407fb06eb9e5f3d59889b3978d5bfe94299c8",
+ "shasum": ""
+ },
+ "require": {
+ "php": "~5.6.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0"
+ },
+ "require-dev": {
+ "cerdic/css-tidy": "^1.7 || ^2.0",
+ "simpletest/simpletest": "dev-master"
+ },
+ "suggest": {
+ "cerdic/css-tidy": "If you want to use the filter 'Filter.ExtractStyleBlocks'.",
+ "ext-bcmath": "Used for unit conversion and imagecrash protection",
+ "ext-iconv": "Converts text to and from non-UTF-8 encodings",
+ "ext-tidy": "Used for pretty-printing HTML"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "library/HTMLPurifier.composer.php"
+ ],
+ "psr-0": {
+ "HTMLPurifier": "library/"
+ },
+ "exclude-from-classmap": [
+ "/library/HTMLPurifier/Language/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "LGPL-2.1-or-later"
+ ],
+ "authors": [
+ {
+ "name": "Edward Z. Yang",
+ "email": "admin@htmlpurifier.org",
+ "homepage": "http://ezyang.com"
+ }
+ ],
+ "description": "Standards compliant HTML filter written in PHP",
+ "homepage": "http://htmlpurifier.org/",
+ "keywords": [
+ "html"
+ ],
+ "support": {
+ "issues": "https://github.com/ezyang/htmlpurifier/issues",
+ "source": "https://github.com/ezyang/htmlpurifier/tree/v4.16.0"
+ },
+ "time": "2022-09-18T07:06:19+00:00"
+ },
+ {
+ "name": "maennchen/zipstream-php",
+ "version": "v2.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/maennchen/ZipStream-PHP.git",
+ "reference": "3fa72e4c71a43f9e9118752a5c90e476a8dc9eb3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/3fa72e4c71a43f9e9118752a5c90e476a8dc9eb3",
+ "reference": "3fa72e4c71a43f9e9118752a5c90e476a8dc9eb3",
+ "shasum": ""
+ },
+ "require": {
+ "ext-mbstring": "*",
+ "myclabs/php-enum": "^1.5",
+ "php": "^8.0",
+ "psr/http-message": "^1.0"
+ },
+ "require-dev": {
+ "ext-zip": "*",
+ "friendsofphp/php-cs-fixer": "^3.9",
+ "guzzlehttp/guzzle": "^6.5.3 || ^7.2.0",
+ "mikey179/vfsstream": "^1.6",
+ "php-coveralls/php-coveralls": "^2.4",
+ "phpunit/phpunit": "^8.5.8 || ^9.4.2",
+ "vimeo/psalm": "^5.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "ZipStream\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Paul Duncan",
+ "email": "pabs@pablotron.org"
+ },
+ {
+ "name": "Jonatan Männchen",
+ "email": "jonatan@maennchen.ch"
+ },
+ {
+ "name": "Jesse Donat",
+ "email": "donatj@gmail.com"
+ },
+ {
+ "name": "András Kolesár",
+ "email": "kolesar@kolesar.hu"
+ }
+ ],
+ "description": "ZipStream is a library for dynamically streaming dynamic zip files from PHP without writing to the disk at all on the server.",
+ "keywords": [
+ "stream",
+ "zip"
+ ],
+ "support": {
+ "issues": "https://github.com/maennchen/ZipStream-PHP/issues",
+ "source": "https://github.com/maennchen/ZipStream-PHP/tree/v2.4.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/maennchen",
+ "type": "github"
+ },
+ {
+ "url": "https://opencollective.com/zipstream",
+ "type": "open_collective"
+ }
+ ],
+ "time": "2022-12-08T12:29:14+00:00"
+ },
+ {
+ "name": "markbaker/complex",
+ "version": "3.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/MarkBaker/PHPComplex.git",
+ "reference": "95c56caa1cf5c766ad6d65b6344b807c1e8405b9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/MarkBaker/PHPComplex/zipball/95c56caa1cf5c766ad6d65b6344b807c1e8405b9",
+ "reference": "95c56caa1cf5c766ad6d65b6344b807c1e8405b9",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "require-dev": {
+ "dealerdirect/phpcodesniffer-composer-installer": "dev-master",
+ "phpcompatibility/php-compatibility": "^9.3",
+ "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0",
+ "squizlabs/php_codesniffer": "^3.7"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Complex\\": "classes/src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Mark Baker",
+ "email": "mark@lange.demon.co.uk"
+ }
+ ],
+ "description": "PHP Class for working with complex numbers",
+ "homepage": "https://github.com/MarkBaker/PHPComplex",
+ "keywords": [
+ "complex",
+ "mathematics"
+ ],
+ "support": {
+ "issues": "https://github.com/MarkBaker/PHPComplex/issues",
+ "source": "https://github.com/MarkBaker/PHPComplex/tree/3.0.2"
+ },
+ "time": "2022-12-06T16:21:08+00:00"
+ },
+ {
+ "name": "markbaker/matrix",
+ "version": "3.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/MarkBaker/PHPMatrix.git",
+ "reference": "728434227fe21be27ff6d86621a1b13107a2562c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/MarkBaker/PHPMatrix/zipball/728434227fe21be27ff6d86621a1b13107a2562c",
+ "reference": "728434227fe21be27ff6d86621a1b13107a2562c",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.1 || ^8.0"
+ },
+ "require-dev": {
+ "dealerdirect/phpcodesniffer-composer-installer": "dev-master",
+ "phpcompatibility/php-compatibility": "^9.3",
+ "phpdocumentor/phpdocumentor": "2.*",
+ "phploc/phploc": "^4.0",
+ "phpmd/phpmd": "2.*",
+ "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0",
+ "sebastian/phpcpd": "^4.0",
+ "squizlabs/php_codesniffer": "^3.7"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Matrix\\": "classes/src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Mark Baker",
+ "email": "mark@demon-angel.eu"
+ }
+ ],
+ "description": "PHP Class for working with matrices",
+ "homepage": "https://github.com/MarkBaker/PHPMatrix",
+ "keywords": [
+ "mathematics",
+ "matrix",
+ "vector"
+ ],
+ "support": {
+ "issues": "https://github.com/MarkBaker/PHPMatrix/issues",
+ "source": "https://github.com/MarkBaker/PHPMatrix/tree/3.0.1"
+ },
+ "time": "2022-12-02T22:17:43+00:00"
+ },
+ {
+ "name": "masterminds/html5",
+ "version": "2.8.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/Masterminds/html5-php.git",
+ "reference": "3c5d5a56d56f48a1ca08a0670f0f80c1dad368f3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/3c5d5a56d56f48a1ca08a0670f0f80c1dad368f3",
+ "reference": "3c5d5a56d56f48a1ca08a0670f0f80c1dad368f3",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "php": ">=5.3.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.7-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Masterminds\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Matt Butcher",
+ "email": "technosophos@gmail.com"
+ },
+ {
+ "name": "Matt Farina",
+ "email": "matt@mattfarina.com"
+ },
+ {
+ "name": "Asmir Mustafic",
+ "email": "goetas@gmail.com"
+ }
+ ],
+ "description": "An HTML5 parser and serializer.",
+ "homepage": "http://masterminds.github.io/html5-php",
+ "keywords": [
+ "HTML5",
+ "dom",
+ "html",
+ "parser",
+ "querypath",
+ "serializer",
+ "xml"
+ ],
+ "support": {
+ "issues": "https://github.com/Masterminds/html5-php/issues",
+ "source": "https://github.com/Masterminds/html5-php/tree/2.8.0"
+ },
+ "time": "2023-04-26T07:27:39+00:00"
+ },
+ {
+ "name": "myclabs/php-enum",
+ "version": "1.8.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/myclabs/php-enum.git",
+ "reference": "a867478eae49c9f59ece437ae7f9506bfaa27483"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/myclabs/php-enum/zipball/a867478eae49c9f59ece437ae7f9506bfaa27483",
+ "reference": "a867478eae49c9f59ece437ae7f9506bfaa27483",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "php": "^7.3 || ^8.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.5",
+ "squizlabs/php_codesniffer": "1.*",
+ "vimeo/psalm": "^4.6.2"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "MyCLabs\\Enum\\": "src/"
+ },
+ "classmap": [
+ "stubs/Stringable.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP Enum contributors",
+ "homepage": "https://github.com/myclabs/php-enum/graphs/contributors"
+ }
+ ],
+ "description": "PHP Enum implementation",
+ "homepage": "http://github.com/myclabs/php-enum",
+ "keywords": [
+ "enum"
+ ],
+ "support": {
+ "issues": "https://github.com/myclabs/php-enum/issues",
+ "source": "https://github.com/myclabs/php-enum/tree/1.8.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/mnapoli",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/myclabs/php-enum",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-08-04T09:53:51+00:00"
+ },
+ {
+ "name": "phenx/php-font-lib",
+ "version": "0.5.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/dompdf/php-font-lib.git",
+ "reference": "dd448ad1ce34c63d09baccd05415e361300c35b4"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/dompdf/php-font-lib/zipball/dd448ad1ce34c63d09baccd05415e361300c35b4",
+ "reference": "dd448ad1ce34c63d09baccd05415e361300c35b4",
+ "shasum": ""
+ },
+ "require": {
+ "ext-mbstring": "*"
+ },
+ "require-dev": {
+ "symfony/phpunit-bridge": "^3 || ^4 || ^5"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "FontLib\\": "src/FontLib"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "LGPL-3.0"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Ménager",
+ "email": "fabien.menager@gmail.com"
+ }
+ ],
+ "description": "A library to read, parse, export and make subsets of different types of font files.",
+ "homepage": "https://github.com/PhenX/php-font-lib",
+ "support": {
+ "issues": "https://github.com/dompdf/php-font-lib/issues",
+ "source": "https://github.com/dompdf/php-font-lib/tree/0.5.4"
+ },
+ "time": "2021-12-17T19:44:54+00:00"
+ },
+ {
+ "name": "phenx/php-svg-lib",
+ "version": "0.5.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/dompdf/php-svg-lib.git",
+ "reference": "76876c6cf3080bcb6f249d7d59705108166a6685"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/dompdf/php-svg-lib/zipball/76876c6cf3080bcb6f249d7d59705108166a6685",
+ "reference": "76876c6cf3080bcb6f249d7d59705108166a6685",
+ "shasum": ""
+ },
+ "require": {
+ "ext-mbstring": "*",
+ "php": "^7.1 || ^8.0",
+ "sabberworm/php-css-parser": "^8.4"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Svg\\": "src/Svg"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "LGPL-3.0"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Ménager",
+ "email": "fabien.menager@gmail.com"
+ }
+ ],
+ "description": "A library to read, parse and export to PDF SVG files.",
+ "homepage": "https://github.com/PhenX/php-svg-lib",
+ "support": {
+ "issues": "https://github.com/dompdf/php-svg-lib/issues",
+ "source": "https://github.com/dompdf/php-svg-lib/tree/0.5.0"
+ },
+ "time": "2022-09-06T12:16:56+00:00"
+ },
+ {
+ "name": "phpoffice/phpspreadsheet",
+ "version": "1.28.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/PHPOffice/PhpSpreadsheet.git",
+ "reference": "6e81cf39bbd93ebc3a4e8150444c41e8aa9b769a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/6e81cf39bbd93ebc3a4e8150444c41e8aa9b769a",
+ "reference": "6e81cf39bbd93ebc3a4e8150444c41e8aa9b769a",
+ "shasum": ""
+ },
+ "require": {
+ "ext-ctype": "*",
+ "ext-dom": "*",
+ "ext-fileinfo": "*",
+ "ext-gd": "*",
+ "ext-iconv": "*",
+ "ext-libxml": "*",
+ "ext-mbstring": "*",
+ "ext-simplexml": "*",
+ "ext-xml": "*",
+ "ext-xmlreader": "*",
+ "ext-xmlwriter": "*",
+ "ext-zip": "*",
+ "ext-zlib": "*",
+ "ezyang/htmlpurifier": "^4.15",
+ "maennchen/zipstream-php": "^2.1",
+ "markbaker/complex": "^3.0",
+ "markbaker/matrix": "^3.0",
+ "php": "^7.4 || ^8.0",
+ "psr/http-client": "^1.0",
+ "psr/http-factory": "^1.0",
+ "psr/simple-cache": "^1.0 || ^2.0 || ^3.0"
+ },
+ "require-dev": {
+ "dealerdirect/phpcodesniffer-composer-installer": "dev-main",
+ "dompdf/dompdf": "^1.0 || ^2.0",
+ "friendsofphp/php-cs-fixer": "^3.2",
+ "mitoteam/jpgraph": "^10.2.4",
+ "mpdf/mpdf": "^8.1.1",
+ "phpcompatibility/php-compatibility": "^9.3",
+ "phpstan/phpstan": "^1.1",
+ "phpstan/phpstan-phpunit": "^1.0",
+ "phpunit/phpunit": "^8.5 || ^9.0",
+ "squizlabs/php_codesniffer": "^3.7",
+ "tecnickcom/tcpdf": "^6.5"
+ },
+ "suggest": {
+ "dompdf/dompdf": "Option for rendering PDF with PDF Writer",
+ "ext-intl": "PHP Internationalization Functions",
+ "mitoteam/jpgraph": "Option for rendering charts, or including charts with PDF or HTML Writers",
+ "mpdf/mpdf": "Option for rendering PDF with PDF Writer",
+ "tecnickcom/tcpdf": "Option for rendering PDF with PDF Writer"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "PhpOffice\\PhpSpreadsheet\\": "src/PhpSpreadsheet"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Maarten Balliauw",
+ "homepage": "https://blog.maartenballiauw.be"
+ },
+ {
+ "name": "Mark Baker",
+ "homepage": "https://markbakeruk.net"
+ },
+ {
+ "name": "Franck Lefevre",
+ "homepage": "https://rootslabs.net"
+ },
+ {
+ "name": "Erik Tilt"
+ },
+ {
+ "name": "Adrien Crivelli"
+ }
+ ],
+ "description": "PHPSpreadsheet - Read, Create and Write Spreadsheet documents in PHP - Spreadsheet engine",
+ "homepage": "https://github.com/PHPOffice/PhpSpreadsheet",
+ "keywords": [
+ "OpenXML",
+ "excel",
+ "gnumeric",
+ "ods",
+ "php",
+ "spreadsheet",
+ "xls",
+ "xlsx"
+ ],
+ "support": {
+ "issues": "https://github.com/PHPOffice/PhpSpreadsheet/issues",
+ "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/1.28.0"
+ },
+ "time": "2023-02-25T12:24:49+00:00"
+ },
+ {
+ "name": "psr/cache",
+ "version": "3.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/cache.git",
+ "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf",
+ "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.0.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Cache\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for caching libraries",
+ "keywords": [
+ "cache",
+ "psr",
+ "psr-6"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/cache/tree/3.0.0"
+ },
+ "time": "2021-02-03T23:26:27+00:00"
+ },
+ {
+ "name": "psr/container",
+ "version": "2.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/container.git",
+ "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963",
+ "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.4.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Container\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common Container Interface (PHP FIG PSR-11)",
+ "homepage": "https://github.com/php-fig/container",
+ "keywords": [
+ "PSR-11",
+ "container",
+ "container-interface",
+ "container-interop",
+ "psr"
+ ],
+ "support": {
+ "issues": "https://github.com/php-fig/container/issues",
+ "source": "https://github.com/php-fig/container/tree/2.0.2"
+ },
+ "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.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/http-client.git",
+ "reference": "0955afe48220520692d2d09f7ab7e0f93ffd6a31"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/http-client/zipball/0955afe48220520692d2d09f7ab7e0f93ffd6a31",
+ "reference": "0955afe48220520692d2d09f7ab7e0f93ffd6a31",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.0 || ^8.0",
+ "psr/http-message": "^1.0 || ^2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Client\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for HTTP clients",
+ "homepage": "https://github.com/php-fig/http-client",
+ "keywords": [
+ "http",
+ "http-client",
+ "psr",
+ "psr-18"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/http-client/tree/1.0.2"
+ },
+ "time": "2023-04-10T20:12:12+00:00"
+ },
+ {
+ "name": "psr/http-factory",
+ "version": "1.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/http-factory.git",
+ "reference": "e616d01114759c4c489f93b099585439f795fe35"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/http-factory/zipball/e616d01114759c4c489f93b099585439f795fe35",
+ "reference": "e616d01114759c4c489f93b099585439f795fe35",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.0.0",
+ "psr/http-message": "^1.0 || ^2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Message\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interfaces for PSR-7 HTTP message factories",
+ "keywords": [
+ "factory",
+ "http",
+ "message",
+ "psr",
+ "psr-17",
+ "psr-7",
+ "request",
+ "response"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/http-factory/tree/1.0.2"
+ },
+ "time": "2023-04-10T20:10:41+00:00"
+ },
+ {
+ "name": "psr/http-message",
+ "version": "1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/http-message.git",
+ "reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/http-message/zipball/cb6ce4845ce34a8ad9e68117c10ee90a29919eba",
+ "reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Message\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for HTTP messages",
+ "homepage": "https://github.com/php-fig/http-message",
+ "keywords": [
+ "http",
+ "http-message",
+ "psr",
+ "psr-7",
+ "request",
+ "response"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/http-message/tree/1.1"
+ },
+ "time": "2023-04-04T09:50:52+00:00"
+ },
+ {
+ "name": "psr/log",
+ "version": "3.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/log.git",
+ "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001",
+ "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.0.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Log\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for logging libraries",
+ "homepage": "https://github.com/php-fig/log",
+ "keywords": [
+ "log",
+ "psr",
+ "psr-3"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/log/tree/3.0.0"
+ },
+ "time": "2021-07-14T16:46:02+00:00"
+ },
+ {
+ "name": "psr/simple-cache",
+ "version": "3.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/simple-cache.git",
+ "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865",
+ "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.0.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\SimpleCache\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interfaces for simple caching",
+ "keywords": [
+ "cache",
+ "caching",
+ "psr",
+ "psr-16",
+ "simple-cache"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/simple-cache/tree/3.0.0"
+ },
+ "time": "2021-10-29T13:26:27+00:00"
+ },
+ {
+ "name": "sabberworm/php-css-parser",
+ "version": "8.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sabberworm/PHP-CSS-Parser.git",
+ "reference": "e41d2140031d533348b2192a83f02d8dd8a71d30"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sabberworm/PHP-CSS-Parser/zipball/e41d2140031d533348b2192a83f02d8dd8a71d30",
+ "reference": "e41d2140031d533348b2192a83f02d8dd8a71d30",
+ "shasum": ""
+ },
+ "require": {
+ "ext-iconv": "*",
+ "php": ">=5.6.20"
+ },
+ "require-dev": {
+ "codacy/coverage": "^1.4",
+ "phpunit/phpunit": "^4.8.36"
+ },
+ "suggest": {
+ "ext-mbstring": "for parsing UTF-8 CSS"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Sabberworm\\CSS\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Raphael Schweikert"
+ }
+ ],
+ "description": "Parser for CSS Files written in PHP",
+ "homepage": "https://www.sabberworm.com/blog/2010/6/10/php-css-parser",
+ "keywords": [
+ "css",
+ "parser",
+ "stylesheet"
+ ],
+ "support": {
+ "issues": "https://github.com/sabberworm/PHP-CSS-Parser/issues",
+ "source": "https://github.com/sabberworm/PHP-CSS-Parser/tree/8.4.0"
+ },
+ "time": "2021-12-11T13:40:54+00:00"
+ },
+ {
+ "name": "sensio/framework-extra-bundle",
+ "version": "v6.2.10",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sensiolabs/SensioFrameworkExtraBundle.git",
+ "reference": "2f886f4b31f23c76496901acaedfedb6936ba61f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sensiolabs/SensioFrameworkExtraBundle/zipball/2f886f4b31f23c76496901acaedfedb6936ba61f",
+ "reference": "2f886f4b31f23c76496901acaedfedb6936ba61f",
+ "shasum": ""
+ },
+ "require": {
+ "doctrine/annotations": "^1.0|^2.0",
+ "php": ">=7.2.5",
+ "symfony/config": "^4.4|^5.0|^6.0",
+ "symfony/dependency-injection": "^4.4|^5.0|^6.0",
+ "symfony/framework-bundle": "^4.4|^5.0|^6.0",
+ "symfony/http-kernel": "^4.4|^5.0|^6.0"
+ },
+ "conflict": {
+ "doctrine/doctrine-cache-bundle": "<1.3.1",
+ "doctrine/persistence": "<1.3"
+ },
+ "require-dev": {
+ "doctrine/dbal": "^2.10|^3.0",
+ "doctrine/doctrine-bundle": "^1.11|^2.0",
+ "doctrine/orm": "^2.5",
+ "symfony/browser-kit": "^4.4|^5.0|^6.0",
+ "symfony/doctrine-bridge": "^4.4|^5.0|^6.0",
+ "symfony/dom-crawler": "^4.4|^5.0|^6.0",
+ "symfony/expression-language": "^4.4|^5.0|^6.0",
+ "symfony/finder": "^4.4|^5.0|^6.0",
+ "symfony/monolog-bridge": "^4.0|^5.0|^6.0",
+ "symfony/monolog-bundle": "^3.2",
+ "symfony/phpunit-bridge": "^4.4.9|^5.0.9|^6.0",
+ "symfony/security-bundle": "^4.4|^5.0|^6.0",
+ "symfony/twig-bundle": "^4.4|^5.0|^6.0",
+ "symfony/yaml": "^4.4|^5.0|^6.0",
+ "twig/twig": "^1.34|^2.4|^3.0"
+ },
+ "type": "symfony-bundle",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "6.1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Sensio\\Bundle\\FrameworkExtraBundle\\": "src/"
+ },
+ "exclude-from-classmap": [
+ "/tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ }
+ ],
+ "description": "This bundle provides a way to configure your controllers with annotations",
+ "keywords": [
+ "annotations",
+ "controllers"
+ ],
+ "support": {
+ "source": "https://github.com/sensiolabs/SensioFrameworkExtraBundle/tree/v6.2.10"
+ },
+ "abandoned": "Symfony",
+ "time": "2023-02-24T14:57:12+00:00"
+ },
+ {
+ "name": "symfony/cache",
+ "version": "v6.2.10",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/cache.git",
+ "reference": "1ce7ed8e7ca6948892b6a3a52bb60cf2b04f7c94"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/cache/zipball/1ce7ed8e7ca6948892b6a3a52bb60cf2b04f7c94",
+ "reference": "1ce7ed8e7ca6948892b6a3a52bb60cf2b04f7c94",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "psr/cache": "^2.0|^3.0",
+ "psr/log": "^1.1|^2|^3",
+ "symfony/cache-contracts": "^1.1.7|^2|^3",
+ "symfony/service-contracts": "^1.1|^2|^3",
+ "symfony/var-exporter": "^6.2.10"
+ },
+ "conflict": {
+ "doctrine/dbal": "<2.13.1",
+ "symfony/dependency-injection": "<5.4",
+ "symfony/http-kernel": "<5.4",
+ "symfony/var-dumper": "<5.4"
+ },
+ "provide": {
+ "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/dbal": "^2.13.1|^3.0",
+ "predis/predis": "^1.1",
+ "psr/simple-cache": "^1.0|^2.0|^3.0",
+ "symfony/config": "^5.4|^6.0",
+ "symfony/dependency-injection": "^5.4|^6.0",
+ "symfony/filesystem": "^5.4|^6.0",
+ "symfony/http-kernel": "^5.4|^6.0",
+ "symfony/messenger": "^5.4|^6.0",
+ "symfony/var-dumper": "^5.4|^6.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Cache\\": ""
+ },
+ "classmap": [
+ "Traits/ValueWrapper.php"
+ ],
+ "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 extended PSR-6, PSR-16 (and tags) implementations",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "caching",
+ "psr6"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/cache/tree/v6.2.10"
+ },
+ "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": "2023-04-21T15:42:15+00:00"
+ },
+ {
+ "name": "symfony/cache-contracts",
+ "version": "v3.2.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/cache-contracts.git",
+ "reference": "eeb71f04b6f7f34ca6d15633df82e014528b1632"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/eeb71f04b6f7f34ca6d15633df82e014528b1632",
+ "reference": "eeb71f04b6f7f34ca6d15633df82e014528b1632",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "psr/cache": "^3.0"
+ },
+ "suggest": {
+ "symfony/cache-implementation": ""
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.3-dev"
+ },
+ "thanks": {
+ "name": "symfony/contracts",
+ "url": "https://github.com/symfony/contracts"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Contracts\\Cache\\": ""
+ }
+ },
+ "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 caching",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "abstractions",
+ "contracts",
+ "decoupling",
+ "interfaces",
+ "interoperability",
+ "standards"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/cache-contracts/tree/v3.2.1"
+ },
+ "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": "2023-03-01T10:32:47+00:00"
+ },
+ {
+ "name": "symfony/config",
+ "version": "v6.2.7",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/config.git",
+ "reference": "249271da6f545d6579e0663374f8249a80be2893"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/config/zipball/249271da6f545d6579e0663374f8249a80be2893",
+ "reference": "249271da6f545d6579e0663374f8249a80be2893",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "symfony/deprecation-contracts": "^2.1|^3",
+ "symfony/filesystem": "^5.4|^6.0",
+ "symfony/polyfill-ctype": "~1.8"
+ },
+ "conflict": {
+ "symfony/finder": "<5.4"
+ },
+ "require-dev": {
+ "symfony/event-dispatcher": "^5.4|^6.0",
+ "symfony/finder": "^5.4|^6.0",
+ "symfony/messenger": "^5.4|^6.0",
+ "symfony/service-contracts": "^1.1|^2|^3",
+ "symfony/yaml": "^5.4|^6.0"
+ },
+ "suggest": {
+ "symfony/yaml": "To use the yaml reference dumper"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Config\\": ""
+ },
+ "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 you find, load, combine, autofill and validate configuration values of any kind",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/config/tree/v6.2.7"
+ },
+ "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": "2023-02-14T08:44:56+00:00"
+ },
+ {
+ "name": "symfony/console",
+ "version": "v6.2.10",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/console.git",
+ "reference": "12288d9f4500f84a4d02254d4aa968b15488476f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/console/zipball/12288d9f4500f84a4d02254d4aa968b15488476f",
+ "reference": "12288d9f4500f84a4d02254d4aa968b15488476f",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "symfony/deprecation-contracts": "^2.1|^3",
+ "symfony/polyfill-mbstring": "~1.0",
+ "symfony/service-contracts": "^1.1|^2|^3",
+ "symfony/string": "^5.4|^6.0"
+ },
+ "conflict": {
+ "symfony/dependency-injection": "<5.4",
+ "symfony/dotenv": "<5.4",
+ "symfony/event-dispatcher": "<5.4",
+ "symfony/lock": "<5.4",
+ "symfony/process": "<5.4"
+ },
+ "provide": {
+ "psr/log-implementation": "1.0|2.0|3.0"
+ },
+ "require-dev": {
+ "psr/log": "^1|^2|^3",
+ "symfony/config": "^5.4|^6.0",
+ "symfony/dependency-injection": "^5.4|^6.0",
+ "symfony/event-dispatcher": "^5.4|^6.0",
+ "symfony/lock": "^5.4|^6.0",
+ "symfony/process": "^5.4|^6.0",
+ "symfony/var-dumper": "^5.4|^6.0"
+ },
+ "suggest": {
+ "psr/log": "For using the console logger",
+ "symfony/event-dispatcher": "",
+ "symfony/lock": "",
+ "symfony/process": ""
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Console\\": ""
+ },
+ "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": "Eases the creation of beautiful and testable command line interfaces",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "cli",
+ "command-line",
+ "console",
+ "terminal"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/console/tree/v6.2.10"
+ },
+ "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": "2023-04-28T13:37:43+00:00"
+ },
+ {
+ "name": "symfony/dependency-injection",
+ "version": "v6.2.10",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/dependency-injection.git",
+ "reference": "d732a66a2672669232c0b4536c8c96724a679780"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/d732a66a2672669232c0b4536c8c96724a679780",
+ "reference": "d732a66a2672669232c0b4536c8c96724a679780",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "psr/container": "^1.1|^2.0",
+ "symfony/deprecation-contracts": "^2.1|^3",
+ "symfony/service-contracts": "^1.1.6|^2.0|^3.0",
+ "symfony/var-exporter": "^6.2.7"
+ },
+ "conflict": {
+ "ext-psr": "<1.1|>=2",
+ "symfony/config": "<6.1",
+ "symfony/finder": "<5.4",
+ "symfony/proxy-manager-bridge": "<6.2",
+ "symfony/yaml": "<5.4"
+ },
+ "provide": {
+ "psr/container-implementation": "1.1|2.0",
+ "symfony/service-implementation": "1.1|2.0|3.0"
+ },
+ "require-dev": {
+ "symfony/config": "^6.1",
+ "symfony/expression-language": "^5.4|^6.0",
+ "symfony/yaml": "^5.4|^6.0"
+ },
+ "suggest": {
+ "symfony/config": "",
+ "symfony/expression-language": "For using expressions in service container configuration",
+ "symfony/finder": "For using double-star glob patterns or when GLOB_BRACE portability is required",
+ "symfony/yaml": ""
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\DependencyInjection\\": ""
+ },
+ "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 you to standardize and centralize the way objects are constructed in your application",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/dependency-injection/tree/v6.2.10"
+ },
+ "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": "2023-04-21T15:42:15+00:00"
+ },
+ {
+ "name": "symfony/deprecation-contracts",
+ "version": "v3.2.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/deprecation-contracts.git",
+ "reference": "e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e",
+ "reference": "e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.3-dev"
+ },
+ "thanks": {
+ "name": "symfony/contracts",
+ "url": "https://github.com/symfony/contracts"
+ }
+ },
+ "autoload": {
+ "files": [
+ "function.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "A generic function and convention to trigger deprecation notices",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/deprecation-contracts/tree/v3.2.1"
+ },
+ "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": "2023-03-01T10:25:55+00:00"
+ },
+ {
+ "name": "symfony/doctrine-bridge",
+ "version": "v6.2.9",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/doctrine-bridge.git",
+ "reference": "4b3aeaa90d41c5527d7ba211d12102cedf06936e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/doctrine-bridge/zipball/4b3aeaa90d41c5527d7ba211d12102cedf06936e",
+ "reference": "4b3aeaa90d41c5527d7ba211d12102cedf06936e",
+ "shasum": ""
+ },
+ "require": {
+ "doctrine/event-manager": "^1.2|^2",
+ "doctrine/persistence": "^2|^3",
+ "php": ">=8.1",
+ "symfony/deprecation-contracts": "^2.1|^3",
+ "symfony/polyfill-ctype": "~1.8",
+ "symfony/polyfill-mbstring": "~1.0",
+ "symfony/service-contracts": "^1.1|^2|^3"
+ },
+ "conflict": {
+ "doctrine/dbal": "<2.13.1",
+ "doctrine/lexer": "<1.1",
+ "doctrine/orm": "<2.7.4",
+ "phpunit/phpunit": "<5.4.3",
+ "symfony/cache": "<5.4",
+ "symfony/dependency-injection": "<5.4",
+ "symfony/form": "<5.4.21|>=6,<6.2.7",
+ "symfony/http-kernel": "<6.2",
+ "symfony/messenger": "<5.4",
+ "symfony/property-info": "<5.4",
+ "symfony/security-bundle": "<5.4",
+ "symfony/security-core": "<6.0",
+ "symfony/validator": "<5.4"
+ },
+ "require-dev": {
+ "doctrine/annotations": "^1.10.4|^2",
+ "doctrine/collections": "^1.0|^2.0",
+ "doctrine/data-fixtures": "^1.1",
+ "doctrine/dbal": "^2.13.1|^3.0",
+ "doctrine/orm": "^2.7.4",
+ "psr/log": "^1|^2|^3",
+ "symfony/cache": "^5.4|^6.0",
+ "symfony/config": "^5.4|^6.0",
+ "symfony/dependency-injection": "^5.4|^6.0",
+ "symfony/doctrine-messenger": "^5.4|^6.0",
+ "symfony/expression-language": "^5.4|^6.0",
+ "symfony/form": "^5.4.21|^6.2.7",
+ "symfony/http-kernel": "^6.2",
+ "symfony/messenger": "^5.4|^6.0",
+ "symfony/property-access": "^5.4|^6.0",
+ "symfony/property-info": "^5.4|^6.0",
+ "symfony/proxy-manager-bridge": "^5.4|^6.0",
+ "symfony/security-core": "^6.0",
+ "symfony/stopwatch": "^5.4|^6.0",
+ "symfony/translation": "^5.4|^6.0",
+ "symfony/uid": "^5.4|^6.0",
+ "symfony/validator": "^5.4|^6.0",
+ "symfony/var-dumper": "^5.4|^6.0"
+ },
+ "suggest": {
+ "doctrine/data-fixtures": "",
+ "doctrine/dbal": "",
+ "doctrine/orm": "",
+ "symfony/form": "",
+ "symfony/property-info": "",
+ "symfony/validator": ""
+ },
+ "type": "symfony-bridge",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Bridge\\Doctrine\\": ""
+ },
+ "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 integration for Doctrine with various Symfony components",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/doctrine-bridge/tree/v6.2.9"
+ },
+ "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": "2023-04-11T16:08:35+00:00"
+ },
+ {
+ "name": "symfony/dotenv",
+ "version": "v6.2.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/dotenv.git",
+ "reference": "4481aa45be7a11d2335c1d5b5bbe2f0c6199b105"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/dotenv/zipball/4481aa45be7a11d2335c1d5b5bbe2f0c6199b105",
+ "reference": "4481aa45be7a11d2335c1d5b5bbe2f0c6199b105",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "conflict": {
+ "symfony/console": "<5.4",
+ "symfony/process": "<5.4"
+ },
+ "require-dev": {
+ "symfony/console": "^5.4|^6.0",
+ "symfony/process": "^5.4|^6.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Dotenv\\": ""
+ },
+ "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": "Registers environment variables from a .env file",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "dotenv",
+ "env",
+ "environment"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/dotenv/tree/v6.2.8"
+ },
+ "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": "2023-03-10T10:06:03+00:00"
+ },
+ {
+ "name": "symfony/error-handler",
+ "version": "v6.2.10",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/error-handler.git",
+ "reference": "8b7e9f124640cb0611624a9383176c3e5f7d8cfb"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/error-handler/zipball/8b7e9f124640cb0611624a9383176c3e5f7d8cfb",
+ "reference": "8b7e9f124640cb0611624a9383176c3e5f7d8cfb",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "psr/log": "^1|^2|^3",
+ "symfony/var-dumper": "^5.4|^6.0"
+ },
+ "require-dev": {
+ "symfony/deprecation-contracts": "^2.1|^3",
+ "symfony/http-kernel": "^5.4|^6.0",
+ "symfony/serializer": "^5.4|^6.0"
+ },
+ "bin": [
+ "Resources/bin/patch-type-declarations"
+ ],
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\ErrorHandler\\": ""
+ },
+ "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 to manage errors and ease debugging PHP code",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/error-handler/tree/v6.2.10"
+ },
+ "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": "2023-04-18T13:46:08+00:00"
+ },
+ {
+ "name": "symfony/event-dispatcher",
+ "version": "v6.2.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/event-dispatcher.git",
+ "reference": "04046f35fd7d72f9646e721fc2ecb8f9c67d3339"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/04046f35fd7d72f9646e721fc2ecb8f9c67d3339",
+ "reference": "04046f35fd7d72f9646e721fc2ecb8f9c67d3339",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "symfony/event-dispatcher-contracts": "^2|^3"
+ },
+ "conflict": {
+ "symfony/dependency-injection": "<5.4"
+ },
+ "provide": {
+ "psr/event-dispatcher-implementation": "1.0",
+ "symfony/event-dispatcher-implementation": "2.0|3.0"
+ },
+ "require-dev": {
+ "psr/log": "^1|^2|^3",
+ "symfony/config": "^5.4|^6.0",
+ "symfony/dependency-injection": "^5.4|^6.0",
+ "symfony/error-handler": "^5.4|^6.0",
+ "symfony/expression-language": "^5.4|^6.0",
+ "symfony/http-foundation": "^5.4|^6.0",
+ "symfony/service-contracts": "^1.1|^2|^3",
+ "symfony/stopwatch": "^5.4|^6.0"
+ },
+ "suggest": {
+ "symfony/dependency-injection": "",
+ "symfony/http-kernel": ""
+ },
+ "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/v6.2.8"
+ },
+ "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": "2023-03-20T16:06:02+00:00"
+ },
+ {
+ "name": "symfony/event-dispatcher-contracts",
+ "version": "v3.2.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/event-dispatcher-contracts.git",
+ "reference": "0ad3b6f1e4e2da5690fefe075cd53a238646d8dd"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/0ad3b6f1e4e2da5690fefe075cd53a238646d8dd",
+ "reference": "0ad3b6f1e4e2da5690fefe075cd53a238646d8dd",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "psr/event-dispatcher": "^1"
+ },
+ "suggest": {
+ "symfony/event-dispatcher-implementation": ""
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.3-dev"
+ },
+ "thanks": {
+ "name": "symfony/contracts",
+ "url": "https://github.com/symfony/contracts"
+ }
+ },
+ "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.2.1"
+ },
+ "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": "2023-03-01T10:32:47+00:00"
+ },
+ {
+ "name": "symfony/filesystem",
+ "version": "v6.2.10",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/filesystem.git",
+ "reference": "fd588debf7d1bc16a2c84b4b3b71145d9946b894"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/filesystem/zipball/fd588debf7d1bc16a2c84b4b3b71145d9946b894",
+ "reference": "fd588debf7d1bc16a2c84b4b3b71145d9946b894",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "symfony/polyfill-ctype": "~1.8",
+ "symfony/polyfill-mbstring": "~1.8"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Filesystem\\": ""
+ },
+ "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 basic utilities for the filesystem",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/filesystem/tree/v6.2.10"
+ },
+ "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": "2023-04-18T13:46:08+00:00"
+ },
+ {
+ "name": "symfony/finder",
+ "version": "v6.2.7",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/finder.git",
+ "reference": "20808dc6631aecafbe67c186af5dcb370be3a0eb"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/finder/zipball/20808dc6631aecafbe67c186af5dcb370be3a0eb",
+ "reference": "20808dc6631aecafbe67c186af5dcb370be3a0eb",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "symfony/filesystem": "^6.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Finder\\": ""
+ },
+ "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": "Finds files and directories via an intuitive fluent interface",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/finder/tree/v6.2.7"
+ },
+ "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": "2023-02-16T09:57:23+00:00"
+ },
+ {
+ "name": "symfony/flex",
+ "version": "v2.2.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/flex.git",
+ "reference": "2ff8465e7172790a47ab3c129f2b514eb2d8a286"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/flex/zipball/2ff8465e7172790a47ab3c129f2b514eb2d8a286",
+ "reference": "2ff8465e7172790a47ab3c129f2b514eb2d8a286",
+ "shasum": ""
+ },
+ "require": {
+ "composer-plugin-api": "^2.1",
+ "php": ">=8.0"
+ },
+ "require-dev": {
+ "composer/composer": "^2.1",
+ "symfony/dotenv": "^5.4|^6.0",
+ "symfony/filesystem": "^5.4|^6.0",
+ "symfony/phpunit-bridge": "^5.4|^6.0",
+ "symfony/process": "^5.4|^6.0"
+ },
+ "type": "composer-plugin",
+ "extra": {
+ "class": "Symfony\\Flex\\Flex"
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Flex\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien.potencier@gmail.com"
+ }
+ ],
+ "description": "Composer plugin for Symfony",
+ "support": {
+ "issues": "https://github.com/symfony/flex/issues",
+ "source": "https://github.com/symfony/flex/tree/v2.2.5"
+ },
+ "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": "2023-02-18T08:03:15+00:00"
+ },
+ {
+ "name": "symfony/framework-bundle",
+ "version": "v6.2.10",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/framework-bundle.git",
+ "reference": "823f285befde4e97bb70d97cae57997c38e4d6fd"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/823f285befde4e97bb70d97cae57997c38e4d6fd",
+ "reference": "823f285befde4e97bb70d97cae57997c38e4d6fd",
+ "shasum": ""
+ },
+ "require": {
+ "composer-runtime-api": ">=2.1",
+ "ext-xml": "*",
+ "php": ">=8.1",
+ "symfony/cache": "^5.4|^6.0",
+ "symfony/config": "^6.1",
+ "symfony/dependency-injection": "^6.2.8",
+ "symfony/deprecation-contracts": "^2.1|^3",
+ "symfony/error-handler": "^6.1",
+ "symfony/event-dispatcher": "^5.4|^6.0",
+ "symfony/filesystem": "^5.4|^6.0",
+ "symfony/finder": "^5.4|^6.0",
+ "symfony/http-foundation": "^6.2",
+ "symfony/http-kernel": "^6.2.1",
+ "symfony/polyfill-mbstring": "~1.0",
+ "symfony/routing": "^5.4|^6.0"
+ },
+ "conflict": {
+ "doctrine/annotations": "<1.13.1",
+ "doctrine/persistence": "<1.3",
+ "phpdocumentor/reflection-docblock": "<3.2.2",
+ "phpdocumentor/type-resolver": "<1.4.0",
+ "phpunit/phpunit": "<5.4.3",
+ "symfony/asset": "<5.4",
+ "symfony/console": "<5.4",
+ "symfony/dom-crawler": "<5.4",
+ "symfony/dotenv": "<5.4",
+ "symfony/form": "<5.4",
+ "symfony/http-client": "<5.4",
+ "symfony/lock": "<5.4",
+ "symfony/mailer": "<5.4",
+ "symfony/messenger": "<6.2",
+ "symfony/mime": "<6.2",
+ "symfony/property-access": "<5.4",
+ "symfony/property-info": "<5.4",
+ "symfony/security-core": "<5.4",
+ "symfony/security-csrf": "<5.4",
+ "symfony/serializer": "<6.1",
+ "symfony/stopwatch": "<5.4",
+ "symfony/translation": "<6.2.8",
+ "symfony/twig-bridge": "<5.4",
+ "symfony/twig-bundle": "<5.4",
+ "symfony/validator": "<5.4",
+ "symfony/web-profiler-bundle": "<5.4",
+ "symfony/workflow": "<5.4"
+ },
+ "require-dev": {
+ "doctrine/annotations": "^1.13.1|^2",
+ "doctrine/persistence": "^1.3|^2|^3",
+ "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0",
+ "symfony/asset": "^5.4|^6.0",
+ "symfony/browser-kit": "^5.4|^6.0",
+ "symfony/console": "^5.4.9|^6.0.9",
+ "symfony/css-selector": "^5.4|^6.0",
+ "symfony/dom-crawler": "^5.4|^6.0",
+ "symfony/dotenv": "^5.4|^6.0",
+ "symfony/expression-language": "^5.4|^6.0",
+ "symfony/form": "^5.4|^6.0",
+ "symfony/html-sanitizer": "^6.1",
+ "symfony/http-client": "^5.4|^6.0",
+ "symfony/lock": "^5.4|^6.0",
+ "symfony/mailer": "^5.4|^6.0",
+ "symfony/messenger": "^6.2",
+ "symfony/mime": "^6.2",
+ "symfony/notifier": "^5.4|^6.0",
+ "symfony/polyfill-intl-icu": "~1.0",
+ "symfony/process": "^5.4|^6.0",
+ "symfony/property-info": "^5.4|^6.0",
+ "symfony/rate-limiter": "^5.4|^6.0",
+ "symfony/security-bundle": "^5.4|^6.0",
+ "symfony/semaphore": "^5.4|^6.0",
+ "symfony/serializer": "^6.1",
+ "symfony/stopwatch": "^5.4|^6.0",
+ "symfony/string": "^5.4|^6.0",
+ "symfony/translation": "^6.2.8",
+ "symfony/twig-bundle": "^5.4|^6.0",
+ "symfony/uid": "^5.4|^6.0",
+ "symfony/validator": "^5.4|^6.0",
+ "symfony/web-link": "^5.4|^6.0",
+ "symfony/workflow": "^5.4|^6.0",
+ "symfony/yaml": "^5.4|^6.0",
+ "twig/twig": "^2.10|^3.0"
+ },
+ "suggest": {
+ "ext-apcu": "For best performance of the system caches",
+ "symfony/console": "For using the console commands",
+ "symfony/form": "For using forms",
+ "symfony/property-info": "For using the property_info service",
+ "symfony/serializer": "For using the serializer service",
+ "symfony/validator": "For using validation",
+ "symfony/web-link": "For using web links, features such as preloading, prefetching or prerendering",
+ "symfony/yaml": "For using the debug:config and lint:yaml commands"
+ },
+ "type": "symfony-bundle",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Bundle\\FrameworkBundle\\": ""
+ },
+ "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 a tight integration between Symfony components and the Symfony full-stack framework",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/framework-bundle/tree/v6.2.10"
+ },
+ "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": "2023-04-23T08:23:35+00:00"
+ },
+ {
+ "name": "symfony/http-foundation",
+ "version": "v6.2.10",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/http-foundation.git",
+ "reference": "49adbb92bcb4e3c2943719d2756271e8b9602acc"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/http-foundation/zipball/49adbb92bcb4e3c2943719d2756271e8b9602acc",
+ "reference": "49adbb92bcb4e3c2943719d2756271e8b9602acc",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "symfony/deprecation-contracts": "^2.1|^3",
+ "symfony/polyfill-mbstring": "~1.1"
+ },
+ "conflict": {
+ "symfony/cache": "<6.2"
+ },
+ "require-dev": {
+ "predis/predis": "~1.0",
+ "symfony/cache": "^5.4|^6.0",
+ "symfony/dependency-injection": "^5.4|^6.0",
+ "symfony/expression-language": "^5.4|^6.0",
+ "symfony/http-kernel": "^5.4.12|^6.0.12|^6.1.4",
+ "symfony/mime": "^5.4|^6.0",
+ "symfony/rate-limiter": "^5.2|^6.0"
+ },
+ "suggest": {
+ "symfony/mime": "To use the file extension guesser"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\HttpFoundation\\": ""
+ },
+ "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": "Defines an object-oriented layer for the HTTP specification",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/http-foundation/tree/v6.2.10"
+ },
+ "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": "2023-04-18T13:46:08+00:00"
+ },
+ {
+ "name": "symfony/http-kernel",
+ "version": "v6.2.10",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/http-kernel.git",
+ "reference": "81064a65a5496f17d2b6984f6519406f98864215"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/http-kernel/zipball/81064a65a5496f17d2b6984f6519406f98864215",
+ "reference": "81064a65a5496f17d2b6984f6519406f98864215",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "psr/log": "^1|^2|^3",
+ "symfony/deprecation-contracts": "^2.1|^3",
+ "symfony/error-handler": "^6.1",
+ "symfony/event-dispatcher": "^5.4|^6.0",
+ "symfony/http-foundation": "^5.4.21|^6.2.7",
+ "symfony/polyfill-ctype": "^1.8"
+ },
+ "conflict": {
+ "symfony/browser-kit": "<5.4",
+ "symfony/cache": "<5.4",
+ "symfony/config": "<6.1",
+ "symfony/console": "<5.4",
+ "symfony/dependency-injection": "<6.2",
+ "symfony/doctrine-bridge": "<5.4",
+ "symfony/form": "<5.4",
+ "symfony/http-client": "<5.4",
+ "symfony/mailer": "<5.4",
+ "symfony/messenger": "<5.4",
+ "symfony/translation": "<5.4",
+ "symfony/twig-bridge": "<5.4",
+ "symfony/validator": "<5.4",
+ "twig/twig": "<2.13"
+ },
+ "provide": {
+ "psr/log-implementation": "1.0|2.0|3.0"
+ },
+ "require-dev": {
+ "psr/cache": "^1.0|^2.0|^3.0",
+ "symfony/browser-kit": "^5.4|^6.0",
+ "symfony/config": "^6.1",
+ "symfony/console": "^5.4|^6.0",
+ "symfony/css-selector": "^5.4|^6.0",
+ "symfony/dependency-injection": "^6.2",
+ "symfony/dom-crawler": "^5.4|^6.0",
+ "symfony/expression-language": "^5.4|^6.0",
+ "symfony/finder": "^5.4|^6.0",
+ "symfony/http-client-contracts": "^1.1|^2|^3",
+ "symfony/process": "^5.4|^6.0",
+ "symfony/routing": "^5.4|^6.0",
+ "symfony/stopwatch": "^5.4|^6.0",
+ "symfony/translation": "^5.4|^6.0",
+ "symfony/translation-contracts": "^1.1|^2|^3",
+ "symfony/uid": "^5.4|^6.0",
+ "twig/twig": "^2.13|^3.0.4"
+ },
+ "suggest": {
+ "symfony/browser-kit": "",
+ "symfony/config": "",
+ "symfony/console": "",
+ "symfony/dependency-injection": ""
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\HttpKernel\\": ""
+ },
+ "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 a structured process for converting a Request into a Response",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/http-kernel/tree/v6.2.10"
+ },
+ "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": "2023-04-28T13:50:28+00:00"
+ },
+ {
+ "name": "symfony/mailer",
+ "version": "v6.2.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/mailer.git",
+ "reference": "bfcfa015c67e19c6fdb7ca6fe70700af1e740a17"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/mailer/zipball/bfcfa015c67e19c6fdb7ca6fe70700af1e740a17",
+ "reference": "bfcfa015c67e19c6fdb7ca6fe70700af1e740a17",
+ "shasum": ""
+ },
+ "require": {
+ "egulias/email-validator": "^2.1.10|^3|^4",
+ "php": ">=8.1",
+ "psr/event-dispatcher": "^1",
+ "psr/log": "^1|^2|^3",
+ "symfony/event-dispatcher": "^5.4|^6.0",
+ "symfony/mime": "^6.2",
+ "symfony/service-contracts": "^1.1|^2|^3"
+ },
+ "conflict": {
+ "symfony/http-kernel": "<5.4",
+ "symfony/messenger": "<6.2",
+ "symfony/mime": "<6.2",
+ "symfony/twig-bridge": "<6.2.1"
+ },
+ "require-dev": {
+ "symfony/console": "^5.4|^6.0",
+ "symfony/http-client": "^5.4|^6.0",
+ "symfony/messenger": "^6.2",
+ "symfony/twig-bridge": "^6.2"
+ },
+ "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/v6.2.8"
+ },
+ "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": "2023-03-14T15:00:05+00:00"
+ },
+ {
+ "name": "symfony/mime",
+ "version": "v6.2.10",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/mime.git",
+ "reference": "b6c137fc53a9f7c4c951cd3f362b3734c7a97723"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/mime/zipball/b6c137fc53a9f7c4c951cd3f362b3734c7a97723",
+ "reference": "b6c137fc53a9f7c4c951cd3f362b3734c7a97723",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "symfony/polyfill-intl-idn": "^1.10",
+ "symfony/polyfill-mbstring": "^1.0"
+ },
+ "conflict": {
+ "egulias/email-validator": "~3.0.0",
+ "phpdocumentor/reflection-docblock": "<3.2.2",
+ "phpdocumentor/type-resolver": "<1.4.0",
+ "symfony/mailer": "<5.4",
+ "symfony/serializer": "<6.2"
+ },
+ "require-dev": {
+ "egulias/email-validator": "^2.1.10|^3.1|^4",
+ "league/html-to-markdown": "^5.0",
+ "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0",
+ "symfony/dependency-injection": "^5.4|^6.0",
+ "symfony/property-access": "^5.4|^6.0",
+ "symfony/property-info": "^5.4|^6.0",
+ "symfony/serializer": "^6.2"
+ },
+ "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/v6.2.10"
+ },
+ "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": "2023-04-19T09:54:16+00:00"
+ },
+ {
+ "name": "symfony/password-hasher",
+ "version": "v6.2.7",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/password-hasher.git",
+ "reference": "67820d8570bf1c2c2cd87cb76d9d12a9d52ab808"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/password-hasher/zipball/67820d8570bf1c2c2cd87cb76d9d12a9d52ab808",
+ "reference": "67820d8570bf1c2c2cd87cb76d9d12a9d52ab808",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "conflict": {
+ "symfony/security-core": "<5.4"
+ },
+ "require-dev": {
+ "symfony/console": "^5.4|^6.0",
+ "symfony/security-core": "^5.4|^6.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\PasswordHasher\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Robin Chalas",
+ "email": "robin.chalas@gmail.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides password hashing utilities",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "hashing",
+ "password"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/password-hasher/tree/v6.2.7"
+ },
+ "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": "2023-02-14T08:44:56+00:00"
+ },
+ {
+ "name": "symfony/polyfill-intl-grapheme",
+ "version": "v1.27.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-intl-grapheme.git",
+ "reference": "511a08c03c1960e08a883f4cffcacd219b758354"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/511a08c03c1960e08a883f4cffcacd219b758354",
+ "reference": "511a08c03c1960e08a883f4cffcacd219b758354",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "suggest": {
+ "ext-intl": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.27-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Intl\\Grapheme\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for intl's grapheme_* functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "grapheme",
+ "intl",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.27.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": "2022-11-03T14:55:06+00:00"
+ },
+ {
+ "name": "symfony/polyfill-intl-idn",
+ "version": "v1.27.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-intl-idn.git",
+ "reference": "639084e360537a19f9ee352433b84ce831f3d2da"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/639084e360537a19f9ee352433b84ce831f3d2da",
+ "reference": "639084e360537a19f9ee352433b84ce831f3d2da",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1",
+ "symfony/polyfill-intl-normalizer": "^1.10",
+ "symfony/polyfill-php72": "^1.10"
+ },
+ "suggest": {
+ "ext-intl": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.27-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/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.27.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": "2022-11-03T14:55:06+00:00"
+ },
+ {
+ "name": "symfony/polyfill-intl-normalizer",
+ "version": "v1.27.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-intl-normalizer.git",
+ "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/19bd1e4fcd5b91116f14d8533c57831ed00571b6",
+ "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "suggest": {
+ "ext-intl": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.27-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Intl\\Normalizer\\": ""
+ },
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for intl's Normalizer class and related functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "intl",
+ "normalizer",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.27.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": "2022-11-03T14:55:06+00:00"
+ },
+ {
+ "name": "symfony/polyfill-mbstring",
+ "version": "v1.27.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-mbstring.git",
+ "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534",
+ "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "provide": {
+ "ext-mbstring": "*"
+ },
+ "suggest": {
+ "ext-mbstring": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.27-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Mbstring\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for the Mbstring extension",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "mbstring",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.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": "2022-11-03T14:55:06+00:00"
+ },
+ {
+ "name": "symfony/property-access",
+ "version": "v6.2.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/property-access.git",
+ "reference": "2ad1e0a07b8cab3e09905659d14f3b248e916374"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/property-access/zipball/2ad1e0a07b8cab3e09905659d14f3b248e916374",
+ "reference": "2ad1e0a07b8cab3e09905659d14f3b248e916374",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "symfony/deprecation-contracts": "^2.1|^3",
+ "symfony/property-info": "^5.4|^6.0"
+ },
+ "require-dev": {
+ "symfony/cache": "^5.4|^6.0"
+ },
+ "suggest": {
+ "psr/cache-implementation": "To cache access methods."
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\PropertyAccess\\": ""
+ },
+ "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 functions to read and write from/to an object or array using a simple string notation",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "access",
+ "array",
+ "extraction",
+ "index",
+ "injection",
+ "object",
+ "property",
+ "property-path",
+ "reflection"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/property-access/tree/v6.2.8"
+ },
+ "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": "2023-03-14T15:00:05+00:00"
+ },
+ {
+ "name": "symfony/property-info",
+ "version": "v6.2.10",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/property-info.git",
+ "reference": "617177c24e1a92e011851948ba973758429a68b2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/property-info/zipball/617177c24e1a92e011851948ba973758429a68b2",
+ "reference": "617177c24e1a92e011851948ba973758429a68b2",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "symfony/string": "^5.4|^6.0"
+ },
+ "conflict": {
+ "phpdocumentor/reflection-docblock": "<5.2",
+ "phpdocumentor/type-resolver": "<1.5.1",
+ "symfony/dependency-injection": "<5.4"
+ },
+ "require-dev": {
+ "doctrine/annotations": "^1.10.4|^2",
+ "phpdocumentor/reflection-docblock": "^5.2",
+ "phpstan/phpdoc-parser": "^1.0",
+ "symfony/cache": "^5.4|^6.0",
+ "symfony/dependency-injection": "^5.4|^6.0",
+ "symfony/serializer": "^5.4|^6.0"
+ },
+ "suggest": {
+ "phpdocumentor/reflection-docblock": "To use the PHPDoc",
+ "psr/cache-implementation": "To cache results",
+ "symfony/doctrine-bridge": "To use Doctrine metadata",
+ "symfony/serializer": "To use Serializer metadata"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\PropertyInfo\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Kévin Dunglas",
+ "email": "dunglas@gmail.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Extracts information about PHP class' properties using metadata of popular sources",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "doctrine",
+ "phpdoc",
+ "property",
+ "symfony",
+ "type",
+ "validator"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/property-info/tree/v6.2.10"
+ },
+ "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": "2023-04-18T13:46:08+00:00"
+ },
+ {
+ "name": "symfony/routing",
+ "version": "v6.2.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/routing.git",
+ "reference": "69062e2823f03b82265d73a966999660f0e1e404"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/routing/zipball/69062e2823f03b82265d73a966999660f0e1e404",
+ "reference": "69062e2823f03b82265d73a966999660f0e1e404",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "conflict": {
+ "doctrine/annotations": "<1.12",
+ "symfony/config": "<6.2",
+ "symfony/dependency-injection": "<5.4",
+ "symfony/yaml": "<5.4"
+ },
+ "require-dev": {
+ "doctrine/annotations": "^1.12|^2",
+ "psr/log": "^1|^2|^3",
+ "symfony/config": "^6.2",
+ "symfony/dependency-injection": "^5.4|^6.0",
+ "symfony/expression-language": "^5.4|^6.0",
+ "symfony/http-foundation": "^5.4|^6.0",
+ "symfony/yaml": "^5.4|^6.0"
+ },
+ "suggest": {
+ "symfony/config": "For using the all-in-one router or any loader",
+ "symfony/expression-language": "For using expression matching",
+ "symfony/http-foundation": "For using a Symfony Request object",
+ "symfony/yaml": "For using the YAML loader"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Routing\\": ""
+ },
+ "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": "Maps an HTTP request to a set of configuration variables",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "router",
+ "routing",
+ "uri",
+ "url"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/routing/tree/v6.2.8"
+ },
+ "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": "2023-03-14T15:00:05+00:00"
+ },
+ {
+ "name": "symfony/runtime",
+ "version": "v6.2.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/runtime.git",
+ "reference": "f8b0751b33888329be8f8f0481bb81d279ec4157"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/runtime/zipball/f8b0751b33888329be8f8f0481bb81d279ec4157",
+ "reference": "f8b0751b33888329be8f8f0481bb81d279ec4157",
+ "shasum": ""
+ },
+ "require": {
+ "composer-plugin-api": "^1.0|^2.0",
+ "php": ">=8.1"
+ },
+ "conflict": {
+ "symfony/dotenv": "<5.4"
+ },
+ "require-dev": {
+ "composer/composer": "^1.0.2|^2.0",
+ "symfony/console": "^5.4|^6.0",
+ "symfony/dotenv": "^5.4|^6.0",
+ "symfony/http-foundation": "^5.4|^6.0",
+ "symfony/http-kernel": "^5.4|^6.0"
+ },
+ "type": "composer-plugin",
+ "extra": {
+ "class": "Symfony\\Component\\Runtime\\Internal\\ComposerPlugin"
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Runtime\\": "",
+ "Symfony\\Runtime\\Symfony\\Component\\": "Internal/"
+ },
+ "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": "Enables decoupling PHP applications from global state",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "runtime"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/runtime/tree/v6.2.8"
+ },
+ "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": "2023-03-14T15:48:35+00:00"
+ },
+ {
+ "name": "symfony/security-bundle",
+ "version": "v6.2.10",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/security-bundle.git",
+ "reference": "b12dcedbcf423ae6d34d79cfaa6791a21c90bd14"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/security-bundle/zipball/b12dcedbcf423ae6d34d79cfaa6791a21c90bd14",
+ "reference": "b12dcedbcf423ae6d34d79cfaa6791a21c90bd14",
+ "shasum": ""
+ },
+ "require": {
+ "composer-runtime-api": ">=2.1",
+ "ext-xml": "*",
+ "php": ">=8.1",
+ "symfony/config": "^6.1",
+ "symfony/dependency-injection": "^6.2",
+ "symfony/event-dispatcher": "^5.4|^6.0",
+ "symfony/http-foundation": "^6.2",
+ "symfony/http-kernel": "^6.2",
+ "symfony/password-hasher": "^5.4|^6.0",
+ "symfony/security-core": "^6.2",
+ "symfony/security-csrf": "^5.4|^6.0",
+ "symfony/security-http": "^6.2.10"
+ },
+ "conflict": {
+ "symfony/browser-kit": "<5.4",
+ "symfony/console": "<5.4",
+ "symfony/framework-bundle": "<5.4",
+ "symfony/ldap": "<5.4",
+ "symfony/twig-bundle": "<5.4"
+ },
+ "require-dev": {
+ "doctrine/annotations": "^1.10.4|^2",
+ "symfony/asset": "^5.4|^6.0",
+ "symfony/browser-kit": "^5.4|^6.0",
+ "symfony/console": "^5.4|^6.0",
+ "symfony/css-selector": "^5.4|^6.0",
+ "symfony/dom-crawler": "^5.4|^6.0",
+ "symfony/expression-language": "^5.4|^6.0",
+ "symfony/form": "^5.4|^6.0",
+ "symfony/framework-bundle": "^5.4|^6.0",
+ "symfony/ldap": "^5.4|^6.0",
+ "symfony/process": "^5.4|^6.0",
+ "symfony/rate-limiter": "^5.4|^6.0",
+ "symfony/serializer": "^5.4|^6.0",
+ "symfony/translation": "^5.4|^6.0",
+ "symfony/twig-bridge": "^5.4|^6.0",
+ "symfony/twig-bundle": "^5.4|^6.0",
+ "symfony/validator": "^5.4|^6.0",
+ "symfony/yaml": "^5.4|^6.0",
+ "twig/twig": "^2.13|^3.0.4"
+ },
+ "type": "symfony-bundle",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Bundle\\SecurityBundle\\": ""
+ },
+ "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 a tight integration of the Security component into the Symfony full-stack framework",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/security-bundle/tree/v6.2.10"
+ },
+ "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": "2023-04-21T15:49:06+00:00"
+ },
+ {
+ "name": "symfony/security-core",
+ "version": "v6.2.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/security-core.git",
+ "reference": "c141337bc7451f9a9e464733f1e536bf38d1d2fb"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/security-core/zipball/c141337bc7451f9a9e464733f1e536bf38d1d2fb",
+ "reference": "c141337bc7451f9a9e464733f1e536bf38d1d2fb",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "symfony/event-dispatcher-contracts": "^1.1|^2|^3",
+ "symfony/password-hasher": "^5.4|^6.0",
+ "symfony/service-contracts": "^1.1.6|^2|^3"
+ },
+ "conflict": {
+ "symfony/event-dispatcher": "<5.4",
+ "symfony/http-foundation": "<5.4",
+ "symfony/ldap": "<5.4",
+ "symfony/security-guard": "<5.4",
+ "symfony/validator": "<5.4"
+ },
+ "require-dev": {
+ "psr/cache": "^1.0|^2.0|^3.0",
+ "psr/container": "^1.1|^2.0",
+ "psr/log": "^1|^2|^3",
+ "symfony/cache": "^5.4|^6.0",
+ "symfony/event-dispatcher": "^5.4|^6.0",
+ "symfony/expression-language": "^5.4|^6.0",
+ "symfony/http-foundation": "^5.4|^6.0",
+ "symfony/ldap": "^5.4|^6.0",
+ "symfony/translation": "^5.4|^6.0",
+ "symfony/validator": "^5.4|^6.0"
+ },
+ "suggest": {
+ "psr/container-implementation": "To instantiate the Security class",
+ "symfony/event-dispatcher": "",
+ "symfony/expression-language": "For using the expression voter",
+ "symfony/http-foundation": "",
+ "symfony/ldap": "For using LDAP integration",
+ "symfony/validator": "For using the user password constraint"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Security\\Core\\": ""
+ },
+ "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 Security Component - Core Library",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/security-core/tree/v6.2.8"
+ },
+ "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": "2023-03-10T10:06:03+00:00"
+ },
+ {
+ "name": "symfony/security-csrf",
+ "version": "v6.2.7",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/security-csrf.git",
+ "reference": "6cce7efdce68e0670d2f19acebc21dcd0798e333"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/security-csrf/zipball/6cce7efdce68e0670d2f19acebc21dcd0798e333",
+ "reference": "6cce7efdce68e0670d2f19acebc21dcd0798e333",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "symfony/security-core": "^5.4|^6.0"
+ },
+ "conflict": {
+ "symfony/http-foundation": "<5.4"
+ },
+ "require-dev": {
+ "symfony/http-foundation": "^5.4|^6.0"
+ },
+ "suggest": {
+ "symfony/http-foundation": "For using the class SessionTokenStorage."
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Security\\Csrf\\": ""
+ },
+ "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 Security Component - CSRF Library",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/security-csrf/tree/v6.2.7"
+ },
+ "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": "2023-02-16T09:57:23+00:00"
+ },
+ {
+ "name": "symfony/security-http",
+ "version": "v6.2.10",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/security-http.git",
+ "reference": "c468f059fac27680acf7e84cea07ba5ffff8942a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/security-http/zipball/c468f059fac27680acf7e84cea07ba5ffff8942a",
+ "reference": "c468f059fac27680acf7e84cea07ba5ffff8942a",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "symfony/deprecation-contracts": "^2.1|^3",
+ "symfony/http-foundation": "^5.4|^6.0",
+ "symfony/http-kernel": "^6.2",
+ "symfony/polyfill-mbstring": "~1.0",
+ "symfony/property-access": "^5.4|^6.0",
+ "symfony/security-core": "~6.0.19|~6.1.11|^6.2.5"
+ },
+ "conflict": {
+ "symfony/event-dispatcher": "<5.4.9|>=6,<6.0.9",
+ "symfony/security-bundle": "<5.4",
+ "symfony/security-csrf": "<5.4"
+ },
+ "require-dev": {
+ "psr/log": "^1|^2|^3",
+ "symfony/cache": "^5.4|^6.0",
+ "symfony/expression-language": "^5.4|^6.0",
+ "symfony/rate-limiter": "^5.4|^6.0",
+ "symfony/routing": "^5.4|^6.0",
+ "symfony/security-csrf": "^5.4|^6.0",
+ "symfony/translation": "^5.4|^6.0"
+ },
+ "suggest": {
+ "symfony/routing": "For using the HttpUtils class to create sub-requests, redirect the user, and match URLs",
+ "symfony/security-csrf": "For using tokens to protect authentication/logout attempts"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Security\\Http\\": ""
+ },
+ "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 Security Component - HTTP Integration",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/security-http/tree/v6.2.10"
+ },
+ "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": "2023-04-21T11:56:14+00:00"
+ },
+ {
+ "name": "symfony/service-contracts",
+ "version": "v3.2.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/service-contracts.git",
+ "reference": "a8c9cedf55f314f3a186041d19537303766df09a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/service-contracts/zipball/a8c9cedf55f314f3a186041d19537303766df09a",
+ "reference": "a8c9cedf55f314f3a186041d19537303766df09a",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "psr/container": "^2.0"
+ },
+ "conflict": {
+ "ext-psr": "<1.1|>=2"
+ },
+ "suggest": {
+ "symfony/service-implementation": ""
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.3-dev"
+ },
+ "thanks": {
+ "name": "symfony/contracts",
+ "url": "https://github.com/symfony/contracts"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Contracts\\Service\\": ""
+ },
+ "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 writing services",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "abstractions",
+ "contracts",
+ "decoupling",
+ "interfaces",
+ "interoperability",
+ "standards"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/service-contracts/tree/v3.2.1"
+ },
+ "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": "2023-03-01T10:32:47+00:00"
+ },
+ {
+ "name": "symfony/stopwatch",
+ "version": "v6.2.7",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/stopwatch.git",
+ "reference": "f3adc98c1061875dd2edcd45e5b04e63d0e29f8f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/stopwatch/zipball/f3adc98c1061875dd2edcd45e5b04e63d0e29f8f",
+ "reference": "f3adc98c1061875dd2edcd45e5b04e63d0e29f8f",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "symfony/service-contracts": "^1|^2|^3"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Stopwatch\\": ""
+ },
+ "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 a way to profile code",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/stopwatch/tree/v6.2.7"
+ },
+ "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": "2023-02-14T08:44:56+00:00"
+ },
+ {
+ "name": "symfony/string",
+ "version": "v6.2.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/string.git",
+ "reference": "193e83bbd6617d6b2151c37fff10fa7168ebddef"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/string/zipball/193e83bbd6617d6b2151c37fff10fa7168ebddef",
+ "reference": "193e83bbd6617d6b2151c37fff10fa7168ebddef",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "symfony/polyfill-ctype": "~1.8",
+ "symfony/polyfill-intl-grapheme": "~1.0",
+ "symfony/polyfill-intl-normalizer": "~1.0",
+ "symfony/polyfill-mbstring": "~1.0"
+ },
+ "conflict": {
+ "symfony/translation-contracts": "<2.0"
+ },
+ "require-dev": {
+ "symfony/error-handler": "^5.4|^6.0",
+ "symfony/http-client": "^5.4|^6.0",
+ "symfony/intl": "^6.2",
+ "symfony/translation-contracts": "^2.0|^3.0",
+ "symfony/var-exporter": "^5.4|^6.0"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "Resources/functions.php"
+ ],
+ "psr-4": {
+ "Symfony\\Component\\String\\": ""
+ },
+ "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 an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "grapheme",
+ "i18n",
+ "string",
+ "unicode",
+ "utf-8",
+ "utf8"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/string/tree/v6.2.8"
+ },
+ "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": "2023-03-20T16:06:02+00:00"
+ },
+ {
+ "name": "symfony/var-dumper",
+ "version": "v6.2.10",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/var-dumper.git",
+ "reference": "41a750a23412ca76fdbbf5096943b4134272c1ab"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/var-dumper/zipball/41a750a23412ca76fdbbf5096943b4134272c1ab",
+ "reference": "41a750a23412ca76fdbbf5096943b4134272c1ab",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "symfony/polyfill-mbstring": "~1.0"
+ },
+ "conflict": {
+ "phpunit/phpunit": "<5.4.3",
+ "symfony/console": "<5.4"
+ },
+ "require-dev": {
+ "ext-iconv": "*",
+ "symfony/console": "^5.4|^6.0",
+ "symfony/process": "^5.4|^6.0",
+ "symfony/uid": "^5.4|^6.0",
+ "twig/twig": "^2.13|^3.0.4"
+ },
+ "suggest": {
+ "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).",
+ "ext-intl": "To show region name in time zone dump",
+ "symfony/console": "To use the ServerDumpCommand and/or the bin/var-dump-server script"
+ },
+ "bin": [
+ "Resources/bin/var-dump-server"
+ ],
+ "type": "library",
+ "autoload": {
+ "files": [
+ "Resources/functions/dump.php"
+ ],
+ "psr-4": {
+ "Symfony\\Component\\VarDumper\\": ""
+ },
+ "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 mechanisms for walking through any arbitrary PHP variable",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "debug",
+ "dump"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/var-dumper/tree/v6.2.10"
+ },
+ "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": "2023-04-18T13:46:08+00:00"
+ },
+ {
+ "name": "symfony/var-exporter",
+ "version": "v6.2.10",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/var-exporter.git",
+ "reference": "9a07920c2058bafee921ce4d90aeef2193837d63"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/var-exporter/zipball/9a07920c2058bafee921ce4d90aeef2193837d63",
+ "reference": "9a07920c2058bafee921ce4d90aeef2193837d63",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "symfony/var-dumper": "^5.4|^6.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\VarExporter\\": ""
+ },
+ "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": "Allows exporting any serializable PHP data structure to plain PHP code",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "clone",
+ "construct",
+ "export",
+ "hydrate",
+ "instantiate",
+ "lazy-loading",
+ "proxy",
+ "serialize"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/var-exporter/tree/v6.2.10"
+ },
+ "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": "2023-04-21T08:33:05+00:00"
+ },
+ {
+ "name": "symfony/yaml",
+ "version": "v6.2.10",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/yaml.git",
+ "reference": "61916f3861b1e9705b18cfde723921a71dd1559d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/yaml/zipball/61916f3861b1e9705b18cfde723921a71dd1559d",
+ "reference": "61916f3861b1e9705b18cfde723921a71dd1559d",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.1",
+ "symfony/polyfill-ctype": "^1.8"
+ },
+ "conflict": {
+ "symfony/console": "<5.4"
+ },
+ "require-dev": {
+ "symfony/console": "^5.4|^6.0"
+ },
+ "suggest": {
+ "symfony/console": "For validating YAML files using the lint command"
+ },
+ "bin": [
+ "Resources/bin/yaml-lint"
+ ],
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Yaml\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Loads and dumps YAML files",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/yaml/tree/v6.2.10"
+ },
+ "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": "2023-04-28T13:25:36+00:00"
+ }
+ ],
+ "packages-dev": [],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": [],
+ "prefer-stable": true,
+ "prefer-lowest": false,
+ "platform": {
+ "php": ">=8.1",
+ "ext-ctype": "*",
+ "ext-iconv": "*"
+ },
+ "platform-dev": [],
+ "plugin-api-version": "2.3.0"
+}
diff --git a/httpdocs/config/bundles.php b/httpdocs/config/bundles.php
new file mode 100644
index 0000000..c2cb463
--- /dev/null
+++ b/httpdocs/config/bundles.php
@@ -0,0 +1,9 @@
+ ['all' => true],
+ Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],
+ Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true],
+ Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle::class => ['all' => true],
+ Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true],
+];
diff --git a/httpdocs/config/packages/cache.yaml b/httpdocs/config/packages/cache.yaml
new file mode 100644
index 0000000..6899b72
--- /dev/null
+++ b/httpdocs/config/packages/cache.yaml
@@ -0,0 +1,19 @@
+framework:
+ cache:
+ # Unique name of your app: used to compute stable namespaces for cache keys.
+ #prefix_seed: your_vendor_name/app_name
+
+ # The "app" cache stores to the filesystem by default.
+ # The data in this cache should persist between deploys.
+ # Other options include:
+
+ # Redis
+ #app: cache.adapter.redis
+ #default_redis_provider: redis://localhost
+
+ # APCu (not recommended with heavy random-write workloads as memory fragmentation can cause perf issues)
+ #app: cache.adapter.apcu
+
+ # Namespaced pools use the above "app" backend by default
+ #pools:
+ #my.dedicated.cache: null
diff --git a/httpdocs/config/packages/doctrine.yaml b/httpdocs/config/packages/doctrine.yaml
new file mode 100644
index 0000000..bdff96f
--- /dev/null
+++ b/httpdocs/config/packages/doctrine.yaml
@@ -0,0 +1,44 @@
+doctrine:
+ dbal:
+ url: '%env(resolve:DATABASE_URL)%'
+
+ # IMPORTANT: You MUST configure your server version,
+ # either here or in the DATABASE_URL env var (see .env file)
+ #server_version: '15'
+ orm:
+ auto_generate_proxy_classes: true
+ enable_lazy_ghost_objects: true
+ naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
+ auto_mapping: true
+ mappings:
+ App:
+ is_bundle: false
+ dir: '%kernel.project_dir%/src/Entity'
+ prefix: 'App\Entity'
+ alias: App
+
+when@test:
+ doctrine:
+ dbal:
+ # "TEST_TOKEN" is typically set by ParaTest
+ dbname_suffix: '_test%env(default::TEST_TOKEN)%'
+
+when@prod:
+ doctrine:
+ orm:
+ auto_generate_proxy_classes: false
+ proxy_dir: '%kernel.build_dir%/doctrine/orm/Proxies'
+ query_cache_driver:
+ type: pool
+ pool: doctrine.system_cache_pool
+ result_cache_driver:
+ type: pool
+ pool: doctrine.result_cache_pool
+
+ framework:
+ cache:
+ pools:
+ doctrine.result_cache_pool:
+ adapter: cache.app
+ doctrine.system_cache_pool:
+ adapter: cache.system
diff --git a/httpdocs/config/packages/doctrine_migrations.yaml b/httpdocs/config/packages/doctrine_migrations.yaml
new file mode 100644
index 0000000..29231d9
--- /dev/null
+++ b/httpdocs/config/packages/doctrine_migrations.yaml
@@ -0,0 +1,6 @@
+doctrine_migrations:
+ migrations_paths:
+ # namespace is arbitrary but should be different from App\Migrations
+ # as migrations classes should NOT be autoloaded
+ 'DoctrineMigrations': '%kernel.project_dir%/migrations'
+ enable_profiler: false
diff --git a/httpdocs/config/packages/framework.yaml b/httpdocs/config/packages/framework.yaml
new file mode 100644
index 0000000..6d85c29
--- /dev/null
+++ b/httpdocs/config/packages/framework.yaml
@@ -0,0 +1,25 @@
+# see https://symfony.com/doc/current/reference/configuration/framework.html
+framework:
+ secret: '%env(APP_SECRET)%'
+ #csrf_protection: true
+ http_method_override: false
+ handle_all_throwables: true
+
+ # Enables session support. Note that the session will ONLY be started if you read or write from it.
+ # Remove or comment this section to explicitly disable session support.
+ session:
+ handler_id: null
+ cookie_secure: auto
+ cookie_samesite: lax
+ storage_factory_id: session.storage.factory.native
+
+ #esi: true
+ #fragments: true
+ php_errors:
+ log: true
+
+when@test:
+ framework:
+ test: true
+ session:
+ storage_factory_id: session.storage.factory.mock_file
diff --git a/httpdocs/config/packages/mailer.yaml b/httpdocs/config/packages/mailer.yaml
new file mode 100644
index 0000000..56a650d
--- /dev/null
+++ b/httpdocs/config/packages/mailer.yaml
@@ -0,0 +1,3 @@
+framework:
+ mailer:
+ dsn: '%env(MAILER_DSN)%'
diff --git a/httpdocs/config/packages/routing.yaml b/httpdocs/config/packages/routing.yaml
new file mode 100644
index 0000000..4b766ce
--- /dev/null
+++ b/httpdocs/config/packages/routing.yaml
@@ -0,0 +1,12 @@
+framework:
+ router:
+ utf8: true
+
+ # Configure how to generate URLs in non-HTTP contexts, such as CLI commands.
+ # See https://symfony.com/doc/current/routing.html#generating-urls-in-commands
+ #default_uri: http://localhost
+
+when@prod:
+ framework:
+ router:
+ strict_requirements: null
diff --git a/httpdocs/config/packages/security.yaml b/httpdocs/config/packages/security.yaml
new file mode 100644
index 0000000..367af25
--- /dev/null
+++ b/httpdocs/config/packages/security.yaml
@@ -0,0 +1,39 @@
+security:
+ # https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords
+ password_hashers:
+ Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
+ # https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider
+ providers:
+ users_in_memory: { memory: null }
+ firewalls:
+ dev:
+ pattern: ^/(_(profiler|wdt)|css|images|js)/
+ security: false
+ main:
+ lazy: true
+ provider: users_in_memory
+
+ # activate different ways to authenticate
+ # https://symfony.com/doc/current/security.html#the-firewall
+
+ # https://symfony.com/doc/current/security/impersonating_user.html
+ # switch_user: true
+
+ # Easy way to control access for large sections of your site
+ # Note: Only the *first* access control that matches will be used
+ access_control:
+ # - { path: ^/admin, roles: ROLE_ADMIN }
+ # - { path: ^/profile, roles: ROLE_USER }
+
+when@test:
+ security:
+ password_hashers:
+ # By default, password hashers are resource intensive and take time. This is
+ # important to generate secure password hashes. In tests however, secure hashes
+ # are not important, waste resources and increase test times. The following
+ # reduces the work factor to the lowest possible values.
+ Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface:
+ algorithm: auto
+ cost: 4 # Lowest possible value for bcrypt
+ time_cost: 3 # Lowest possible value for argon
+ memory_cost: 10 # Lowest possible value for argon
diff --git a/httpdocs/config/packages/sensio_framework_extra.yaml b/httpdocs/config/packages/sensio_framework_extra.yaml
new file mode 100644
index 0000000..1821ccc
--- /dev/null
+++ b/httpdocs/config/packages/sensio_framework_extra.yaml
@@ -0,0 +1,3 @@
+sensio_framework_extra:
+ router:
+ annotations: false
diff --git a/httpdocs/config/preload.php b/httpdocs/config/preload.php
new file mode 100644
index 0000000..5ebcdb2
--- /dev/null
+++ b/httpdocs/config/preload.php
@@ -0,0 +1,5 @@
+em = $em;
+ $this->container = $container;
+ $this->mailer = $mailer;
+ $kernel = $this->container->get('kernel');
+ $this->environment = $kernel->getEnvironment();
+ parent::__construct();
+ }
+
+ /**
+ * @param InputInterface $input
+ * @param OutputInterface $output
+ * @return int|null|void
+ * @throws \Exception
+ */
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ Utils::sendMail($this->mailer, 'test', 'd.knudsen@spawntree.de', 'this is a test', $this->environment);
+
+ $usersById = Utils::getSortedObjectsById($this->em->getRepository(EntUser::class)->findAll());
+ $customersById = Utils::getSortedObjectsById($this->em->getRepository(EntCustomer::class)->findAll());
+
+ $now = (new \DateTime())->format("Y-m-d H:i:s");
+ $checkStartDay = (new \DateTime())->modify('+2 day')->format("Y-m-d H:i:s");
+ $checkStartHours = (new \DateTime())->modify('+2 hours')->format("Y-m-d H:i:s");
+
+ // customer
+ /** @var Query $query */
+ $query = $this->em->createQuery(
+ "SELECT cm FROM ".
+ "App\Entity\EntCustomerMeeting cm ".
+ "WHERE cm.start_date < '".$checkStartDay."' ".
+ "AND cm.start_date > '".$now."' ".
+ "AND cm.first_reminder_sent = 0"
+ );
+ $meetingsCustomer2Days = $query->getResult();
+
+ $query = $this->em->createQuery(
+ "SELECT cm FROM ".
+ "App\Entity\EntCustomerMeeting cm ".
+ "WHERE cm.start_date < '".$checkStartHours."' ".
+ "AND cm.start_date > '".$now."' ".
+ "AND cm.second_reminder_sent = 0"
+ );
+ $meetingsCustomer2Hours = $query->getResult();
+
+ // internal
+ $query = $this->em->createQuery(
+ "SELECT im FROM ".
+ "App\Entity\EntInternalMeeting im ".
+ "WHERE im.start_date < '".$checkStartDay."' ".
+ "AND im.start_date > '".$now."' ".
+ "AND im.first_reminder_sent = 0"
+ );
+ $meetingsInternal2Days = $query->getResult();
+
+ $query = $this->em->createQuery(
+ "SELECT im FROM ".
+ "App\Entity\EntInternalMeeting im ".
+ "WHERE im.start_date < '".$checkStartHours."' ".
+ "AND im.start_date > '".$now."' ".
+ "AND im.second_reminder_sent = 0"
+ );
+ $meetingsInternal2Hours = $query->getResult();
+
+ $this->sendCustomerReminder($meetingsCustomer2Days, $customersById, $usersById, true);
+ $this->sendCustomerReminder($meetingsCustomer2Hours, $customersById, $usersById, false);
+ $this->sendInternalReminder($meetingsInternal2Days, $usersById, true);
+ $this->sendInternalReminder($meetingsInternal2Hours, $usersById, false);
+
+ // Report reminder
+ $checkStartDay = (new \DateTime())->modify('-3 day')->format("Y-m-d H:i:s");
+ $query = $this->em->createQuery(
+ "SELECT cm FROM ".
+ "App\Entity\EntCustomerMeeting cm ".
+ "WHERE cm.end_date < '".$checkStartDay."' ".
+ "AND cm.report IS NULL ".
+ "AND cm.report_reminder_sent = 0"
+ );
+ $meetingsCustomerNoReport = $query->getResult();
+ $this->sendCustomerMeetingReportReminder($meetingsCustomerNoReport, $customersById, $usersById);
+
+ $query = $this->em->createQuery(
+ "SELECT im FROM ".
+ "App\Entity\EntInternalMeeting im ".
+ "WHERE im.end_date < '".$checkStartDay."' ".
+ "AND im.report IS NULL ".
+ "AND im.report_reminder_sent = 0"
+ );
+ $meetingsInternalNoReport = $query->getResult();
+ $this->sendInternalMeetingReportReminder($meetingsInternalNoReport, $usersById);
+
+ }
+
+ protected function sendCustomerReminder($meetings, $customersById, $usersById, $firstReminder)
+ {
+ /** @var EntCustomerMeeting $meeting */
+ foreach ($meetings as $meeting) {
+ /** @var EntUser $owner */
+ $owner = $usersById[$meeting->getOwnerUserId()];
+ /** @var EntCustomer $customer */
+ $customer = $customersById[$meeting->getCustomerId()];
+ $participantsByUserId = Utils::getSortedObjects('getParticipantUserId',
+ $this->em->getRepository('App:EntCustomerMeetingParticipant')->findBy(['customer_meeting_id' => $meeting->getId()]));
+
+ $subject = "Termin-Erinnerung: ". $customer->getName(). " ".$meeting->getStartDate()->format('d.m.Y H:i');
+ $body = $this->createCustomerMeetingReminderEmailBody($meeting, $subject, $customer, $owner, $participantsByUserId, $usersById);
+ Utils::sendMail($this->mailer, $subject, $owner->getEmail(), $body, $this->environment);
+
+ /*** @var EntCustomerMeetingParticipant $participant */
+ foreach ($participantsByUserId as $userId => $participant) {
+ /** @var EntUser $pUser */
+ $pUser = $usersById[$participant->getParticipantUserId()];
+ Utils::sendMail($this->mailer, $subject, $pUser->getEmail(), $body, $this->environment);
+ }
+
+ $firstReminder ? $meeting->setFirstReminderSent(true) : $meeting->setSecondReminderSent(true);
+ $this->em->persist($meeting);
+ $this->em->flush();
+ }
+ }
+
+ protected function sendInternalReminder($meetings, $usersById, $firstReminder)
+ {
+ /** @var EntInternalMeeting $meeting */
+ foreach ($meetings as $meeting) {
+ /** @var EntUser $owner */
+ $owner = $usersById[$meeting->getOwnerUserId()];
+ $participantsByUserId = Utils::getSortedObjects('getParticipantUserId',
+ $this->em->getRepository('App:EntInternalMeetingParticipant')->findBy(['internal_meeting_id' => $meeting->getId()]));
+
+ $subject = "Termin-Erinnerung: PLP intern ".$meeting->getStartDate()->format('d.m.Y H:i');
+ $body = $this->createInternalMeetingReminderEmailBody($meeting, $subject, $owner, $participantsByUserId, $usersById);
+ Utils::sendMail($this->mailer, $subject, $owner->getEmail(), $body, $this->environment);
+
+ /*** @var EntInternalMeetingParticipant $participant */
+ foreach ($participantsByUserId as $userId => $participant) {
+ /** @var EntUser $pUser */
+ $pUser = $usersById[$participant->getParticipantUserId()];
+ Utils::sendMail($this->mailer, $subject, $pUser->getEmail(), $body, $this->environment);
+ }
+
+ $firstReminder ? $meeting->setFirstReminderSent(true) : $meeting->setSecondReminderSent(true);
+ $this->em->persist($meeting);
+ $this->em->flush();
+ }
+ }
+
+ protected function createCustomerMeetingReminderEmailBody(EntCustomerMeeting $meeting, $subject, EntCustomer $customer, EntUser $owner, $participantsByUserId, $usersById)
+ {
+ $body = $subject." ";
+ $body.= "Kunde: ".$customer->getName()."";
+ $body.= "Betreff: ".$meeting->getTitle()."";
+ $body.= "Interner Verantwortlicher: ".$owner->getFirstname()." ".$owner->getLastname()."";
+ $body.= "Start: ".$meeting->getStartDate()->format('d.m.Y H:i')."";
+ $body.= "Ende: ".$meeting->getEndDate()->format('d.m.Y H:i')."";
+ $body.= "Ansprechpartner: ";
+ $body.= "Name: ";
+ $body.= !is_null($meeting->getGender()) ? Utils::getTranslatedGender($meeting->getGender())." " : "";
+ $body.= !is_null($meeting->getFirstname()) ? $meeting->getFirstname()." " : "";
+ $body.= !is_null($meeting->getLastname()) ? $meeting->getLastname()." " : "";
+ $body.= "";
+ $body.= "Abteilung: ".!is_null($meeting->getDescription()) ? $meeting->getDescription()."" : "";
+ $body.= "Email: ".!is_null($meeting->getEmail()) ? $meeting->getEmail()."" : "";
+ $body.= "Telefon: ".!is_null($meeting->getPhoneNo()) ? $meeting->getPhoneNo()."" : "";
+ $body.= "Mobil: ".!is_null($meeting->getMobileNo()) ? $meeting->getMobileNo()."" : "";
+ $body.= "Adresse: ";
+ $body.= !is_null($meeting->getStreet()) ? $meeting->getStreet()." " : "";
+ $body.= !is_null($meeting->getStreetNo()) ? $meeting->getStreetNo()." " : "";
+ $body.= !is_null($meeting->getZip()) ? $meeting->getZip()." " : "";
+ $body.= !is_null($meeting->getCity()) ? $meeting->getCity()." " : "";
+ $body.= "";
+ $body.= "Bemerkung: ".$meeting->getDescription()."";
+ $body.= "Options-Termin: ".($meeting->getIsOptionMeeting() ? "ja" : "nein")."";
+ $body.= "Interne Teilnehmer: ";
+ foreach ($participantsByUserId as $pUserId => $item) {
+ /** @var EntUser $participant */
+ $participant = $usersById[$pUserId];
+ $body.= $participant->getFirstname(). " ". $participant->getLastname(). "";
+ }
+ return $body;
+ }
+
+ protected function createInternalMeetingReminderEmailBody(EntInternalMeeting $meeting, $subject, EntUser $owner, $participantsByUserId, $usersById)
+ {
+ $body = $subject." ";
+ $body.= "Betreff: ".$meeting->getTitle()."";
+ $body.= "Interner Verantwortlicher: ".$owner->getFirstname()." ".$owner->getLastname()."";
+ $body.= "Start: ".$meeting->getStartDate()->format('d.m.Y H:i')."";
+ $body.= "Ende: ".$meeting->getEndDate()->format('d.m.Y H:i')."";
+ $body.= "Bemerkung: ".$meeting->getDescription()."";
+ $body.= "Interne Teilnehmer: ";
+ foreach ($participantsByUserId as $pUserId => $item) {
+ /** @var EntUser $participant */
+ $participant = $usersById[$pUserId];
+ $body.= $participant->getFirstname(). " ". $participant->getLastname(). "";
+ }
+ return $body;
+ }
+
+ protected function sendCustomerMeetingReportReminder($meetings, $customersById, $usersById)
+ {
+ /** @var EntCustomerMeeting $meeting */
+ foreach ($meetings as $meeting) {
+ /** @var EntUser $owner */
+ $owner = $usersById[$meeting->getOwnerUserId()];
+ /** @var EntCustomer $customer */
+ $customer = $customersById[$meeting->getCustomerId()];
+
+ $subject = "Termin-Report-Erinnerung (Kundentermin) ". $customer->getName();
+ $body = $this->createReportReminderEmailBody($meeting, $subject, $owner);
+ Utils::sendMail($this->mailer, $subject, $owner->getEmail(), $body, $this->environment);
+
+ $meeting->setReportReminderSent(true);
+ $this->em->persist($meeting);
+ $this->em->flush();
+ }
+ }
+
+ protected function sendInternalMeetingReportReminder($meetings, $usersById)
+ {
+ /** @var EntInternalMeeting $meeting */
+ foreach ($meetings as $meeting) {
+ /** @var EntUser $owner */
+ $owner = $usersById[$meeting->getOwnerUserId()];
+
+ $subject = "Termin-Report-Erinnerung (PLP intern)";
+ $body = $this->createReportReminderEmailBody($meeting, $subject, $owner);
+ Utils::sendMail($this->mailer, $subject, $owner->getEmail(), $body, $this->environment);
+
+ $meeting->setReportReminderSent(true);
+ $this->em->persist($meeting);
+ $this->em->flush();
+ }
+ }
+
+ protected function createReportReminderEmailBody($meeting, $subject, EntUser $owner)
+ {
+ $body = $subject." ";
+ $body.= "Für folgenden Termin wurde bisher noch kein Report angelegt.";
+ $body.= "Betreff: ".$meeting->getTitle()."";
+ $body.= "Interner Verantwortlicher: ".$owner->getFirstname()." ".$owner->getLastname()."";
+ $body.= "Start: ".$meeting->getStartDate()->format('d.m.Y H:i')."";
+ $body.= "Ende: ".$meeting->getEndDate()->format('d.m.Y H:i')."";
+ $body.= "Bemerkung: ".$meeting->getDescription()."";
+ return $body;
+ }
+
+}
\ No newline at end of file
diff --git a/httpdocs/src/Controller/.gitignore b/httpdocs/src/Controller/.gitignore
new file mode 100644
index 0000000..e69de29
diff --git a/httpdocs/src/Controller/AdminController.php b/httpdocs/src/Controller/AdminController.php
new file mode 100644
index 0000000..a2e08c5
--- /dev/null
+++ b/httpdocs/src/Controller/AdminController.php
@@ -0,0 +1,87 @@
+request->get('type');
+ $em = $this->getDoctrine()->getManager();
+ $entityCache = [];
+
+ switch ($type) {
+ case self::XMAS_CONTACT_PDF_TYPE_CUSTOMER:
+ $sql = "SELECT cc.*, c.name AS company FROM customer_contact cc, customer c WHERE cc.is_xmas_mail_recipient = 1 AND cc.customer_id = c.id ORDER BY cc.lastname ASC";
+ $statement = $em->getConnection()->prepare($sql);
+ $statement->execute();
+ $entries = $statement->fetchAll();
+ break;
+ default:
+ throw new Exception('invalid type given');
+ }
+
+ $data = [];
+ foreach ($entries as $entry) {
+ $cacheKey = trim($entry['firstname'].$entry['lastname'].$entry['street'].$entry['street_no'].$entry['zip'].$entry['city']);
+ if (!array_key_exists($cacheKey, $entityCache)) {
+ $entityCache[$cacheKey] = 1;
+ /** @var EntCountry $entCountry */
+ $entCountry = !is_null($entry['country_id']) ? EntCountry::getCountryById($em, $entry['country_id']) : null;
+ $data[] = [
+ 'company' => $entry['company'],
+ 'firstname' => $entry['firstname'],
+ 'lastname' => $entry['lastname'],
+ 'street' => $entry['street'],
+ 'street_no' => $entry['street_no'],
+ 'zip' => $entry['zip'],
+ 'city' => $entry['city'],
+ 'country' => !is_null($entCountry) ? $entCountry->getName() : ""
+ ];
+ }
+ }
+
+ // Configure Dompdf according to your needs
+ $pdfOptions = new Options();
+ $pdfOptions->set('defaultFont', 'Arial');
+
+ // Instantiate Dompdf with our options
+ $dompdf = new Dompdf($pdfOptions);
+
+ // Retrieve the HTML generated in our twig file
+ $html = $this->renderView('xmas_addresses.html.twig', [
+ 'data' => $data
+ ]);
+
+ // Load HTML to Dompdf
+ $dompdf->loadHtml($html);
+
+ // (Optional) Setup the paper size and orientation 'portrait' or 'portrait'
+ $dompdf->setPaper('A4', 'portrait');
+
+ // Render the HTML as PDF
+ $dompdf->render();
+
+ return Reply::getSteamedFileResponse($dompdf->output());
+ }
+
+}
\ No newline at end of file
diff --git a/httpdocs/src/Controller/CustomerEditController.php b/httpdocs/src/Controller/CustomerEditController.php
new file mode 100644
index 0000000..bcd545f
--- /dev/null
+++ b/httpdocs/src/Controller/CustomerEditController.php
@@ -0,0 +1,227 @@
+request->get('customer'));
+ $em = $this->getDoctrine()->getManager();
+ /** @var EntCustomer $entCustomer */
+ $entCustomer = new EntCustomer($em, $customerClient->name);
+ $entCustomer->setClientData($em, $customerClient);
+ $em->persist($entCustomer);
+ $em->flush();
+
+ $fullMappedCustomer = $entCustomer->clientMapper($em, true);
+ $serviceData = new ServiceData();
+ $serviceData->addServiceData(ServiceData::SERVICE_DATA_TYPE_CUSTOMERS, ServiceData::ACTION_ADD, $fullMappedCustomer);
+ return Reply::getResponse($fullMappedCustomer, Message::SUCCESS_CUSTOMER_CREATE, 0, $serviceData);
+ }
+
+ #[Route('/edit-customer', name: 'edit_customer')]
+ public function editCustomer(Request $request)
+ {
+ $customerClient = json_decode($request->request->get('customer'));
+ $em = $this->getDoctrine()->getManager();
+ /** @var EntCustomer $entCustomer */
+ $entCustomer = $em->getRepository('App:EntCustomer')->find($customerClient->id);
+ if (is_null($entCustomer)) {
+ return Reply::getErrorResponse(Message::ERROR_DEFAULT);
+ }
+ $entCustomer->setClientData($em, $customerClient);
+ $em->persist($entCustomer);
+ $em->flush();
+
+ $fullMappedCustomer = $entCustomer->clientMapper($em, true);
+ $serviceData = new ServiceData();
+ $serviceData->addServiceData(ServiceData::SERVICE_DATA_TYPE_CUSTOMERS, ServiceData::ACTION_EDIT, $fullMappedCustomer);
+ return Reply::getResponse($fullMappedCustomer, Message::SUCCESS_CUSTOMER_EDIT, 0, $serviceData);
+ }
+
+ #[Route('/create-customer-contact', name: 'create_customer_contact')]
+ public function createCustomerContact(Request $request)
+ {
+ $customerContactClient = json_decode($request->request->get('customerContact'));
+ $em = $this->getDoctrine()->getManager();
+ /** @var EntCustomer $entCustomer */
+ $entCustomer = $em->getRepository('App:EntCustomer')->find($customerContactClient->customer_id);
+ if (is_null($entCustomer)) {
+ return Reply::getErrorResponse(Message::ERROR_DEFAULT);
+ }
+
+ /** @var EntCustomerContact $entCustomerContact */
+ $entCustomerContact = new EntCustomerContact($em, $entCustomer, $customerContactClient->lastname);
+ $entCustomerContact->setClientData($em, $customerContactClient);
+ $em->persist($entCustomerContact);
+ $em->flush();
+
+ $fullMappedCustomerContact = $entCustomerContact->clientMapper($em, true);
+ $serviceData = new ServiceData();
+ $serviceData->addServiceData(ServiceData::SERVICE_DATA_TYPE_CUSTOMER_CONTACTS, ServiceData::ACTION_ADD, $fullMappedCustomerContact);
+ return Reply::getResponse($entCustomer->clientMapper($em, true), Message::SUCCESS_CUSTOMER_CONTACT_CREATE, 0, $serviceData);
+ }
+
+ #[Route('/edit-customer-contact', name: 'edit_customer_contact')]
+ public function editCustomerContact(Request $request)
+ {
+ $customerContactClient = json_decode($request->request->get('customerContact'));
+ $em = $this->getDoctrine()->getManager();
+ /** @var EntCustomerContact $entCustomerContact */
+ $entCustomerContact = $em->getRepository('App:EntCustomerContact')->find($customerContactClient->id);
+ if (is_null($entCustomerContact)) {
+ return Reply::getErrorResponse(Message::ERROR_NON_EXISTING_DATA);
+ }
+
+ /** @var EntCustomer $entCustomer */
+ $entCustomer = $em->getRepository('App:EntCustomer')->find($entCustomerContact->getCustomerId());
+ if (is_null($entCustomer)) {
+ return Reply::getErrorResponse(Message::ERROR_DEFAULT);
+ }
+
+ $entCustomerContact->setClientData($em, $customerContactClient);
+ $em->persist($entCustomerContact);
+ $em->flush();
+
+ $fullMappedCustomerContact = $entCustomerContact->clientMapper($em, true);
+ $serviceData = new ServiceData();
+ $serviceData->addServiceData(ServiceData::SERVICE_DATA_TYPE_CUSTOMER_CONTACTS, ServiceData::ACTION_EDIT, $fullMappedCustomerContact);
+ return Reply::getResponse($entCustomer->clientMapper($em, true), Message::SUCCESS_CUSTOMER_CONTACT_EDIT, 0, $serviceData);
+ }
+
+ #[Route('/delete-customer-contact', name: 'delete_customer_contact')]
+ public function deleteCustomerContact(Request $request)
+ {
+ $customerContactIdClient = json_decode($request->request->get('customerContactId'));
+ $em = $this->getDoctrine()->getManager();
+ /** @var EntCustomerContact $entCustomerContact */
+ $entCustomerContact = $em->getRepository('App:EntCustomerContact')->find($customerContactIdClient);
+ if (is_null($entCustomerContact)) {
+ return Reply::getErrorResponse(Message::ERROR_NON_EXISTING_DATA);
+ }
+
+ /** @var EntCustomer $entCustomer */
+ $entCustomer = $em->getRepository('App:EntCustomer')->find($entCustomerContact->getCustomerId());
+ if (is_null($entCustomer)) {
+ return Reply::getErrorResponse(Message::ERROR_DEFAULT);
+ }
+ $fullMappedCustomerContact = $entCustomerContact->clientMapper($em, true);
+ $em->remove($entCustomerContact);
+ $em->flush();
+
+ $sql = "UPDATE `customer_meeting` SET `customer_contact_id` = NULL WHERE `customer_contact_id` = ". $customerContactIdClient;
+ $statement = $em->getConnection()->prepare($sql);
+ $statement->execute();
+
+ $sql = "UPDATE `customer_note` SET `customer_contact_id` = NULL WHERE `customer_contact_id` = ". $customerContactIdClient;
+ $statement = $em->getConnection()->prepare($sql);
+ $statement->execute();
+
+ $serviceData = new ServiceData();
+ $serviceData->addServiceData(ServiceData::SERVICE_DATA_TYPE_CUSTOMER_CONTACTS, ServiceData::ACTION_DELETE, $fullMappedCustomerContact);
+ return Reply::getResponse($entCustomer->clientMapper($em, true), Message::SUCCESS_CUSTOMER_CONTACT_DELETE, 0, $serviceData);
+ }
+
+ #[Route('/create-customer-note', name: 'create_customer_note')]
+ public function createCustomerNote(Request $request)
+ {
+ $customerNoteClient = json_decode($request->request->get('customerNote'));
+ $em = $this->getDoctrine()->getManager();
+ /** @var EntUser $user */
+ $user = $this->getUser();
+ /** @var EntCustomer $entCustomer */
+ $entCustomer = $em->getRepository('App:EntCustomer')->find($customerNoteClient->customer_id);
+ if (is_null($entCustomer)) {
+ return Reply::getErrorResponse(Message::ERROR_DEFAULT);
+ }
+
+ /** @var EntCustomerNote $entCustomerNote */
+ $entCustomerNote = new EntCustomerNote($em, $entCustomer, $user, $customerNoteClient->title);
+ $entCustomerNote->setClientData($em, $customerNoteClient);
+ $em->persist($entCustomerNote);
+ $em->flush();
+
+ return Reply::getResponse($entCustomer->clientMapper($em, true), Message::SUCCESS_CUSTOMER_NOTE_CREATE);
+ }
+
+ #[Route('/edit-customer-note', name: 'edit_customer_note')]
+ public function editCustomerNote(Request $request)
+ {
+ $customerNoteClient = json_decode($request->request->get('customerNote'));
+ $em = $this->getDoctrine()->getManager();
+ /** @var EntUser $user */
+ $user = $this->getUser();
+ /** @var EntCustomerNote $entCustomerNote */
+ $entCustomerNote = $em->getRepository(EntCustomerNote::class)->find($customerNoteClient->id);
+ $bHasRightsToEdit = $entCustomerNote->getCreationUserId() == $user->getId() || $user->isAdmin();
+ if (is_null($entCustomerNote) || !$bHasRightsToEdit) {
+ return Reply::getErrorResponse(Message::ERROR_DEFAULT);
+ }
+
+ /** @var EntCustomer $entCustomer */
+ $entCustomer = $em->getRepository(EntCustomer::class)->find($entCustomerNote->getCustomerId());
+ if (is_null($entCustomer)) {
+ return Reply::getErrorResponse(Message::ERROR_DEFAULT);
+ }
+
+ $entCustomerNote->setClientData($em, $customerNoteClient);
+ $em->persist($entCustomerNote);
+ $em->flush();
+
+ return Reply::getResponse($entCustomer->clientMapper($em, true), Message::SUCCESS_CUSTOMER_NOTE_EDIT);
+ }
+
+ #[Route('/delete-customer-note', name: 'delete_customer_note')]
+ public function deleteCustomerNote(Request $request)
+ {
+ $customerNoteIdClient = json_decode($request->request->get('customerNoteId'));
+ $em = $this->getDoctrine()->getManager();
+ /** @var EntUser $user */
+ $user = $this->getUser();
+
+ /** @var EntCustomerNote $entCustomerNote */
+ $entCustomerNote = $em->getRepository('App:EntCustomerNote')->find($customerNoteIdClient);
+ $bHasRightsToDelete = $entCustomerNote->getCreationUserId() == $user->getId() || $user->isAdmin();
+ if (is_null($entCustomerNote) || !$bHasRightsToDelete) {
+ return Reply::getErrorResponse(Message::ERROR_DEFAULT);
+ }
+
+ /** @var EntCustomer $entCustomer */
+ $entCustomer = $em->getRepository('App:EntCustomer')->find($entCustomerNote->getCustomerId());
+ if (is_null($entCustomer)) {
+ return Reply::getErrorResponse(Message::ERROR_DEFAULT);
+ }
+
+ $em->remove($entCustomerNote);
+ $em->flush();
+
+ return Reply::getResponse($entCustomer->clientMapper($em, true), Message::SUCCESS_CUSTOMER_NOTE_DELETE);
+ }
+}
\ No newline at end of file
diff --git a/httpdocs/src/Controller/CustomerMeetingEditController.php b/httpdocs/src/Controller/CustomerMeetingEditController.php
new file mode 100644
index 0000000..8ff207e
--- /dev/null
+++ b/httpdocs/src/Controller/CustomerMeetingEditController.php
@@ -0,0 +1,281 @@
+request->get('customerMeeting'));
+ $em = $this->getDoctrine()->getManager();
+ /** @var EntUser $user */
+ $user = $this->getUser();
+ /** @var EntCustomer $entCustomer */
+ $entCustomer = $em->getRepository(EntCustomer::class)->find($customerMeetingClient->customer_id);
+ if (is_null($entCustomer)) {
+ return Reply::getErrorResponse(Message::ERROR_DEFAULT);
+ }
+
+ /** @var EntUser $entOwnerUser */
+ $entOwnerUser = $em->getRepository(EntUser::class)->find($customerMeetingClient->owner_user_id);
+ if (is_null($entOwnerUser)) {
+ return Reply::getErrorResponse(Message::ERROR_DEFAULT);
+ }
+
+ /** @var EntMeetingType $entMeetingType */
+ $entMeetingType = $em->getRepository(EntMeetingType::class)->find($customerMeetingClient->meeting_type_id);
+ if (is_null($entMeetingType)) {
+ return Reply::getErrorResponse(Message::ERROR_DEFAULT);
+ }
+
+ // Database manipulation in transaction
+ $dbCon = $this->getDoctrine()->getConnection();
+ $dbCon->beginTransaction();
+
+ try {
+ /** @var EntCustomerMeeting $entCustomerMeeting */
+ $entCustomerMeeting = new EntCustomerMeeting($em, $entCustomer, $user, $entOwnerUser, $entMeetingType, $customerMeetingClient->is_option_meeting,
+ $customerMeetingClient->title, $customerMeetingClient->start_date, $customerMeetingClient->end_date);
+ $entCustomerMeeting->setClientData($em, $customerMeetingClient);
+ $em->persist($entCustomerMeeting);
+ $em->flush();
+ $cntParticipants = count($customerMeetingClient->v_participants);
+ for ($i = 0; $i < $cntParticipants; $i++) {
+ /** @var EntUser $participantUser */
+ $participantUser = $em->getRepository('App:EntUser')->find($customerMeetingClient->v_participants[$i]->participant_user_id);
+ if (is_null($participantUser)) {
+ throw new Exception('invalid participant user');
+ }
+
+ /** @var EntCustomerMeetingParticipant $entParticipant */
+ $entParticipant = new EntCustomerMeetingParticipant($em, $entCustomerMeeting, $participantUser);
+ $em->persist($entParticipant);
+ }
+ $em->flush();
+
+ } catch (Exception $e) {
+ return Reply::getErrorResponse(Message::ERROR_INVALID_DATA);
+ }
+ // All data stored correctly
+ $dbCon->commit();
+
+ $serviceData = new ServiceData();
+ $serviceData->addServiceData(ServiceData::SERVICE_DATA_TYPE_CUSTOMER_MEETINGS, ServiceData::ACTION_ADD, $entCustomerMeeting->clientMapper($em, true));
+ return Reply::getResponse($entCustomer->clientMapper($em, true), Message::SUCCESS_CUSTOMER_MEETING_CREATE, 0, $serviceData);
+ }
+
+ #[Route('/edit-customer-meeting', name: 'edit_customer_meeting')]
+ public function editCustomerMeeting(Request $request)
+ {
+ $customerMeetingClient = json_decode($request->request->get('customerMeeting'));
+ $em = $this->getDoctrine()->getManager();
+ /** @var EntUser $user */
+ $user = $this->getUser();
+ /** @var EntCustomer $entCustomer */
+ $entCustomer = $em->getRepository('App:EntCustomer')->find($customerMeetingClient->customer_id);
+ if (is_null($entCustomer)) {
+ return Reply::getErrorResponse(Message::ERROR_DEFAULT);
+ }
+
+ /** @var EntUser $entOwnerUser */
+ $entOwnerUser = $em->getRepository('App:EntUser')->find($customerMeetingClient->owner_user_id);
+ if (is_null($entOwnerUser)) {
+ return Reply::getErrorResponse(Message::ERROR_DEFAULT);
+ }
+
+ /** @var EntMeetingType $entMeetingType */
+ $entMeetingType = $em->getRepository('App:EntMeetingType')->find($customerMeetingClient->meeting_type_id);
+ if (is_null($entMeetingType)) {
+ return Reply::getErrorResponse(Message::ERROR_DEFAULT);
+ }
+
+ /** @var EntCustomerMeeting $entCustomerMeeting */
+ $entCustomerMeeting = $em->getRepository('App:EntCustomerMeeting')->find($customerMeetingClient->id);
+ if (is_null($entCustomerMeeting)) {
+ return Reply::getErrorResponse(Message::ERROR_NON_EXISTING_DATA);
+ }
+
+ if (!$entCustomerMeeting->isEditDeletable()) {
+ return Reply::getErrorResponse(Message::ERROR_MEETING_NOT_EDIT_DELETABLE);
+ }
+
+ $bHasRightsToEdit =
+ $entCustomerMeeting->getCreationUserId() == $user->getId() ||
+ $entCustomerMeeting->getOwnerUserId() == $user->getId() ||
+ $user->isAdmin();
+ if (!$bHasRightsToEdit) {
+ return Reply::getErrorResponse(Message::ERROR_NOT_ENOUGH_RIGHTS);
+ }
+
+ // Database manipulation in transaction
+ $dbCon = $this->getDoctrine()->getConnection();
+ $dbCon->beginTransaction();
+
+ try {
+ $entCustomerMeeting->setClientData($em, $customerMeetingClient);
+ $em->persist($entCustomerMeeting);
+ $em->flush();
+
+ $formerParticipantsByUserId = Utils::getSortedObjects('getParticipantUserId',
+ $em->getRepository('App:EntCustomerMeetingParticipant')->findBy(['customer_meeting_id' => $entCustomerMeeting->getId()]));
+ $pUserIdsToKeep = [];
+ $cntParticipants = count($customerMeetingClient->v_participants);
+ for ($i = 0; $i < $cntParticipants; $i++) {
+ if (!array_key_exists($customerMeetingClient->v_participants[$i]->participant_user_id, $formerParticipantsByUserId)) {
+ /** @var EntUser $participantUser */
+ $participantUser = $em->getRepository('App:EntUser')->find($customerMeetingClient->v_participants[$i]->participant_user_id);
+ if (is_null($participantUser)) {
+ throw new Exception('invalid participant user');
+ }
+
+ /** @var EntCustomerMeetingParticipant $entParticipant */
+ $entParticipant = new EntCustomerMeetingParticipant($em, $entCustomerMeeting, $participantUser);
+ $em->persist($entParticipant);
+ } else {
+ $pUserIdsToKeep[$customerMeetingClient->v_participants[$i]->participant_user_id] = 1;
+ }
+ }
+
+ /** @var EntCustomerMeetingParticipant $formerParticipant */
+ foreach ($formerParticipantsByUserId as $pUserId => $formerParticipant) {
+ if (!array_key_exists($pUserId, $pUserIdsToKeep)) {
+ $em->remove($formerParticipant);
+ }
+ }
+ $em->flush();
+
+ } catch (Exception $e) {
+ return Reply::getErrorResponse(Message::ERROR_INVALID_DATA);
+ }
+ // All data stored correctly
+ $dbCon->commit();
+ $serviceData = new ServiceData();
+ $serviceData->addServiceData(ServiceData::SERVICE_DATA_TYPE_CUSTOMER_MEETINGS, ServiceData::ACTION_EDIT, $entCustomerMeeting->clientMapper($em, true));
+ return Reply::getResponse($entCustomer->clientMapper($em, true), Message::SUCCESS_CUSTOMER_MEETING_EDIT, 0, $serviceData);
+ }
+
+ #[Route('/delete-customer-meeting', name: 'delete_customer_meeting')]
+ public function deleteCustomerMeeting(Request $request)
+ {
+ $customerMeetingIdClient = json_decode($request->request->get('customerMeetingId'));
+ $em = $this->getDoctrine()->getManager();
+ /** @var EntUser $user */
+ $user = $this->getUser();
+
+ /** @var EntCustomerMeeting $entCustomerMeeting */
+ $entCustomerMeeting = $em->getRepository('App:EntCustomerMeeting')->find($customerMeetingIdClient);
+ if (is_null($entCustomerMeeting)) {
+ return Reply::getErrorResponse(Message::ERROR_NON_EXISTING_DATA);
+ }
+
+ $bHasRightsToDelete =
+ $entCustomerMeeting->getCreationUserId() == $user->getId() ||
+ $entCustomerMeeting->getOwnerUserId() == $user->getId() ||
+ $user->isAdmin();
+ if (!$bHasRightsToDelete) {
+ return Reply::getErrorResponse(Message::ERROR_DEFAULT);
+ }
+
+ if (!$entCustomerMeeting->isEditDeletable()) {
+ return Reply::getErrorResponse(Message::ERROR_MEETING_NOT_EDIT_DELETABLE);
+ }
+
+ /** @var EntCustomer $entCustomer */
+ $entCustomer = $em->getRepository('App:EntCustomer')->find($entCustomerMeeting->getCustomerId());
+ if (is_null($entCustomer)) {
+ return Reply::getErrorResponse(Message::ERROR_DEFAULT);
+ }
+ $mappedCustomerMeeting = $entCustomerMeeting->clientMapper($em, true);
+ $em->remove($entCustomerMeeting);
+
+ $participants = $em->getRepository('App:EntCustomerMeetingParticipant')->findBy(['customer_meeting_id' => $entCustomerMeeting->getId()]);
+ /** @var EntCustomerMeetingParticipant $participant */
+ foreach ($participants as $participant) {
+ $em->remove($participant);
+ }
+ $em->flush();
+
+ $serviceData = new ServiceData();
+ $serviceData->addServiceData(ServiceData::SERVICE_DATA_TYPE_CUSTOMER_MEETINGS, ServiceData::ACTION_DELETE, $mappedCustomerMeeting);
+ return Reply::getResponse($entCustomer->clientMapper($em, true), Message::SUCCESS_CUSTOMER_MEETING_DELETE, 0, $serviceData);
+ }
+
+ #[Route('/check-customer-meeting-report', name: 'check_customer_meeting_report')]
+ public function checkCustomerMeetingReport(Request $request)
+ {
+ $customerMeetingIdClient = json_decode($request->request->get('customerMeetingId'));
+ $em = $this->getDoctrine()->getManager();
+ /** @var EntUser $user */
+ $user = $this->getUser();
+
+ /** @var EntCustomerMeeting $entCustomerMeeting */
+ $entCustomerMeeting = $em->getRepository('App:EntCustomerMeeting')->find($customerMeetingIdClient);
+ $bHasRightsToMakeReport =
+ $entCustomerMeeting->getCreationUserId() == $user->getId() ||
+ $entCustomerMeeting->getOwnerUserId() == $user->getId() ||
+ $user->isAdmin();
+ if (is_null($entCustomerMeeting) || !$bHasRightsToMakeReport) {
+ return Reply::getErrorResponse(Message::ERROR_DEFAULT);
+ }
+
+ $check = new \DateTime() > $entCustomerMeeting->getStartDate();
+ return $check ? Reply::getResponse($check) : Reply::getErrorResponse(Message::ERROR_REPORT_NOT_EDITABLE_YET);
+ }
+
+ #[Route('/edit-customer-meeting-report', name: 'edit_customer_meeting_report')]
+ public function editCustomerMeetingReport(Request $request)
+ {
+ $customerMeetingIdClient = json_decode($request->request->get('customerMeetingId'));
+ $customerMeetingReportClient = json_decode($request->request->get('customerMeetingReport'));
+ $em = $this->getDoctrine()->getManager();
+ /** @var EntUser $user */
+ $user = $this->getUser();
+
+
+ /** @var EntCustomerMeeting $entCustomerMeeting */
+ $entCustomerMeeting = $em->getRepository('App:EntCustomerMeeting')->find($customerMeetingIdClient);
+ $bHasRightsToMakeReport =
+ $entCustomerMeeting->getOwnerUserId() == $user->getId() ||
+ $user->isAdmin();
+ if (is_null($entCustomerMeeting) || !$bHasRightsToMakeReport) {
+ return Reply::getErrorResponse(Message::ERROR_DEFAULT);
+ }
+
+ $entCustomer = $em->getRepository('App:EntCustomer')->find($entCustomerMeeting->getCustomerId());
+ if (is_null($entCustomer)) {
+ return Reply::getErrorResponse(Message::ERROR_DEFAULT);
+ }
+
+ $entCustomerMeeting->setReport($customerMeetingReportClient);
+ $em->persist($entCustomerMeeting);
+ $em->flush();
+
+ $serviceData = new ServiceData();
+ $serviceData->addServiceData(ServiceData::SERVICE_DATA_TYPE_CUSTOMER_MEETINGS, ServiceData::ACTION_EDIT, $entCustomerMeeting->clientMapper($em, true));
+ return Reply::getResponse($entCustomer->clientMapper($em, true), Message::SUCCESS_CUSTOMER_MEETING_REPORT_SET, 0, $serviceData);
+ }
+}
\ No newline at end of file
diff --git a/httpdocs/src/Controller/CustomerViewController.php b/httpdocs/src/Controller/CustomerViewController.php
new file mode 100644
index 0000000..a557534
--- /dev/null
+++ b/httpdocs/src/Controller/CustomerViewController.php
@@ -0,0 +1,49 @@
+getDoctrine()->getManager();
+ return Reply::getResponse(
+ [
+ 'customers' => Utils::clientMap($em, $em->getRepository('App:EntCustomer')->findAll()),
+ 'customerContacts' => Utils::clientMap($em, $em->getRepository('App:EntCustomerContact')->findAll()),
+ 'customerMeetings' => Utils::clientMap($em, $em->getRepository('App:EntCustomerMeeting')->findAll()),
+ ]
+ );
+ }
+
+ #[Route('/get-customer-full', name: 'get_customer_full')]
+ public function getCustomerFull(Request $request)
+ {
+ $customerId = json_decode($request->request->get('customerId'));
+ $em = $this->getDoctrine()->getManager();
+ /** @var EntCustomer $entCustomer */
+ $entCustomer = $em->getRepository('App:EntCustomer')->find($customerId);
+ if (is_null($entCustomer)) {
+ return Reply::getErrorResponse(Message::ERROR_DEFAULT);
+ }
+
+ return Reply::getResponse($entCustomer->clientMapper($em, true));
+ }
+
+}
\ No newline at end of file
diff --git a/httpdocs/src/Controller/ExportController.php b/httpdocs/src/Controller/ExportController.php
new file mode 100644
index 0000000..4153f8a
--- /dev/null
+++ b/httpdocs/src/Controller/ExportController.php
@@ -0,0 +1,80 @@
+request->get('type');
+ $em = $this->getDoctrine()->getManager();
+ $title = "";
+ /** @var EntUser $user */
+ $user = $this->getUser();
+
+
+ switch ($type) {
+ case self::CONTACT_PDF_TYPE_CUSTOMER:
+ if (!$user->isAdmin()) {
+ throw new Exception('invalid role');
+ }
+ $sql = "SELECT cc.*, c.name AS company
+ FROM customer_contact cc, customer c
+ WHERE cc.customer_id = c.id
+ ORDER BY company ASC, cc.lastname ASC";
+ $statement = $em->getConnection()->prepare($sql);
+ $statement->execute();
+ $entries = $statement->fetchAll();
+ $title = "Kunden Kontakte";
+ break;
+ default:
+ throw new Exception('invalid type given');
+ }
+
+ // Configure Dompdf according to your needs
+ $pdfOptions = new Options();
+ $pdfOptions->set('defaultFont', 'Arial');
+
+ // Instantiate Dompdf with our options
+ $dompdf = new Dompdf($pdfOptions);
+
+ // Retrieve the HTML generated in our twig file
+ $html = $this->renderView('contacts_export.html.twig', [
+ 'data' => $entries,
+ 'title' => $title
+ ]);
+
+ // Load HTML to Dompdf
+ $dompdf->loadHtml($html);
+
+ // (Optional) Setup the paper size and orientation 'portrait' or 'portrait'
+ $dompdf->setPaper('A4', 'landscape');
+
+ // Render the HTML as PDF
+ $dompdf->render();
+
+ return Reply::getSteamedFileResponse($dompdf->output());
+ }
+
+}
\ No newline at end of file
diff --git a/httpdocs/src/Controller/InternalMeetingEditController.php b/httpdocs/src/Controller/InternalMeetingEditController.php
new file mode 100644
index 0000000..e0206ad
--- /dev/null
+++ b/httpdocs/src/Controller/InternalMeetingEditController.php
@@ -0,0 +1,247 @@
+request->get('internalMeeting'));
+ $em = $this->getDoctrine()->getManager();
+ /** @var EntUser $user */
+ $user = $this->getUser();
+
+ /** @var EntUser $entOwnerUser */
+ $entOwnerUser = $em->getRepository('App:EntUser')->find($internalMeetingClient->owner_user_id);
+ if (is_null($entOwnerUser)) {
+ return Reply::getErrorResponse(Message::ERROR_DEFAULT);
+ }
+
+ // Database manipulation in transaction
+ $dbCon = $this->getDoctrine()->getConnection();
+ $dbCon->beginTransaction();
+
+ try {
+ /** @var EntInternalMeeting $entInternalMeeting */
+ $entInternalMeeting = new EntInternalMeeting($em, $user, $entOwnerUser, $internalMeetingClient->title, $internalMeetingClient->start_date, $internalMeetingClient->end_date);
+ $entInternalMeeting->setClientData($em, $internalMeetingClient);
+ $em->persist($entInternalMeeting);
+ $em->flush();
+ $cntParticipants = count($internalMeetingClient->v_participants);
+ for ($i = 0; $i < $cntParticipants; $i++) {
+ /** @var EntUser $participantUser */
+ $participantUser = $em->getRepository('App:EntUser')->find($internalMeetingClient->v_participants[$i]->participant_user_id);
+ if (is_null($participantUser)) {
+ throw new Exception('invalid participant user');
+ }
+
+ /** @var EntInternalMeetingParticipant $entParticipant */
+ $entParticipant = new EntInternalMeetingParticipant($em, $entInternalMeeting, $participantUser);
+ $em->persist($entParticipant);
+ }
+ $em->flush();
+
+ } catch (Exception $e) {
+ return Reply::getErrorResponse(Message::ERROR_INVALID_DATA);
+ }
+ // All data stored correctly
+ $dbCon->commit();
+
+ $mappedInternalMeeting = $entInternalMeeting->clientMapper($em, true);
+ $serviceData = new ServiceData();
+ $serviceData->addServiceData(ServiceData::SERVICE_DATA_TYPE_INTERNAL_MEETINGS, ServiceData::ACTION_ADD, $mappedInternalMeeting);
+ return Reply::getResponse($mappedInternalMeeting, Message::SUCCESS_INTERNAL_MEETING_CREATE, 0, $serviceData);
+ }
+
+ #[Route('/edit-internal-meeting', name: 'edit_internal_meeting')]
+ public function editInternalMeeting(Request $request)
+ {
+ $internalMeetingClient = json_decode($request->request->get('internalMeeting'));
+ $em = $this->getDoctrine()->getManager();
+ /** @var EntUser $user */
+ $user = $this->getUser();
+
+ /** @var EntUser $entOwnerUser */
+ $entOwnerUser = $em->getRepository('App:EntUser')->find($internalMeetingClient->owner_user_id);
+ if (is_null($entOwnerUser)) {
+ return Reply::getErrorResponse(Message::ERROR_DEFAULT);
+ }
+
+ /** @var EntInternalMeeting $entInternalMeeting */
+ $entInternalMeeting = $em->getRepository('App:EntInternalMeeting')->find($internalMeetingClient->id);
+ if (is_null($entInternalMeeting)) {
+ return Reply::getErrorResponse(Message::ERROR_NON_EXISTING_DATA);
+ }
+
+ $bHasRightsToEdit =
+ $entInternalMeeting->getCreationUserId() == $user->getId() ||
+ $entInternalMeeting->getOwnerUserId() == $user->getId() ||
+ $user->isAdmin();
+ if (!$bHasRightsToEdit) {
+ return Reply::getErrorResponse(Message::ERROR_NOT_ENOUGH_RIGHTS);
+ }
+
+ if (!$entInternalMeeting->isEditDeletable()) {
+ return Reply::getErrorResponse(Message::ERROR_MEETING_NOT_EDIT_DELETABLE);
+ }
+
+ // Database manipulation in transaction
+ $dbCon = $this->getDoctrine()->getConnection();
+ $dbCon->beginTransaction();
+
+ try {
+ $entInternalMeeting->setClientData($em, $internalMeetingClient);
+ $em->persist($entInternalMeeting);
+ $em->flush();
+
+ $formerParticipantsByUserId = Utils::getSortedObjects('getParticipantUserId',
+ $em->getRepository('App:EntInternalMeetingParticipant')->findBy(['internal_meeting_id' => $entInternalMeeting->getId()]));
+ $pUserIdsToKeep = [];
+ $cntParticipants = count($internalMeetingClient->v_participants);
+ for ($i = 0; $i < $cntParticipants; $i++) {
+ if (!array_key_exists($internalMeetingClient->v_participants[$i]->participant_user_id, $formerParticipantsByUserId)) {
+ /** @var EntUser $participantUser */
+ $participantUser = $em->getRepository('App:EntUser')->find($internalMeetingClient->v_participants[$i]->participant_user_id);
+ if (is_null($participantUser)) {
+ throw new Exception('invalid participant user');
+ }
+
+ /** @var EntInternalMeetingParticipant $entParticipant */
+ $entParticipant = new EntInternalMeetingParticipant($em, $entInternalMeeting, $participantUser);
+ $em->persist($entParticipant);
+ } else {
+ $pUserIdsToKeep[$internalMeetingClient->v_participants[$i]->participant_user_id] = 1;
+ }
+ }
+
+ /** @var EntInternalMeetingParticipant $formerParticipant */
+ foreach ($formerParticipantsByUserId as $pUserId => $formerParticipant) {
+ if (!array_key_exists($pUserId, $pUserIdsToKeep)) {
+ $em->remove($formerParticipant);
+ }
+ }
+ $em->flush();
+
+ } catch (Exception $e) {
+ return Reply::getErrorResponse(Message::ERROR_INVALID_DATA);
+ }
+ // All data stored correctly
+ $dbCon->commit();
+
+ $mappedInternalMeeting = $entInternalMeeting->clientMapper($em, true);
+ $serviceData = new ServiceData();
+ $serviceData->addServiceData(ServiceData::SERVICE_DATA_TYPE_INTERNAL_MEETINGS, ServiceData::ACTION_EDIT, $mappedInternalMeeting);
+ return Reply::getResponse($mappedInternalMeeting, Message::SUCCESS_INTERNAL_MEETING_EDIT, 0, $serviceData);
+ }
+
+ #[Route('/delete-internal-meeting', name: 'delete_internal_meeting')]
+ public function deleteInternalMeeting(Request $request)
+ {
+ $internalMeetingIdClient = json_decode($request->request->get('internalMeetingId'));
+ $em = $this->getDoctrine()->getManager();
+ /** @var EntUser $user */
+ $user = $this->getUser();
+
+ /** @var EntInternalMeeting $entInternalMeeting */
+ $entInternalMeeting = $em->getRepository('App:EntInternalMeeting')->find($internalMeetingIdClient);
+ if (is_null($entInternalMeeting)) {
+ return Reply::getErrorResponse(Message::ERROR_NON_EXISTING_DATA);
+ }
+
+ $bHasRightsToDelete =
+ $entInternalMeeting->getCreationUserId() == $user->getId() ||
+ $entInternalMeeting->getOwnerUserId() == $user->getId() ||
+ $user->isAdmin();
+ if (!$bHasRightsToDelete) {
+ return Reply::getErrorResponse(Message::ERROR_DEFAULT);
+ }
+
+ if (!$entInternalMeeting->isEditDeletable()) {
+ return Reply::getErrorResponse(Message::ERROR_MEETING_NOT_EDIT_DELETABLE);
+ }
+
+ $mappedInternalMeeting = $entInternalMeeting->clientMapper($em, true);
+ $em->remove($entInternalMeeting);
+
+ $participants = $em->getRepository('App:EntInternalMeetingParticipant')->findBy(['internal_meeting_id' => $entInternalMeeting->getId()]);
+ /** @var EntInternalMeetingParticipant $participant */
+ foreach ($participants as $participant) {
+ $em->remove($participant);
+ }
+ $em->flush();
+
+ $serviceData = new ServiceData();
+ $serviceData->addServiceData(ServiceData::SERVICE_DATA_TYPE_INTERNAL_MEETINGS, ServiceData::ACTION_DELETE, $mappedInternalMeeting);
+ return Reply::getResponse($mappedInternalMeeting, Message::SUCCESS_INTERNAL_MEETING_DELETE, 0, $serviceData);
+ }
+
+ #[Route('/check-internal-meeting-report', name: 'check_internal_meeting_report')]
+ public function checkInternalMeetingReport(Request $request)
+ {
+ $internalMeetingIdClient = json_decode($request->request->get('internalMeetingId'));
+ $em = $this->getDoctrine()->getManager();
+ /** @var EntUser $user */
+ $user = $this->getUser();
+
+ /** @var EntInternalMeeting $entInternalMeeting */
+ $entInternalMeeting = $em->getRepository('App:EntInternalMeeting')->find($internalMeetingIdClient);
+ $bHasRightsToMakeReport =
+ $entInternalMeeting->getOwnerUserId() == $user->getId() ||
+ $user->isAdmin();
+ if (is_null($entInternalMeeting) || !$bHasRightsToMakeReport) {
+ return Reply::getErrorResponse(Message::ERROR_DEFAULT);
+ }
+
+ $check = new \DateTime() > $entInternalMeeting->getStartDate();
+ return $check ? Reply::getResponse($check) : Reply::getErrorResponse(Message::ERROR_REPORT_NOT_EDITABLE_YET);
+ }
+
+ #[Route('/edit-internal-meeting-report', name: 'edit_internal_meeting_report')]
+ public function editInternalMeetingReport(Request $request)
+ {
+ $internalMeetingIdClient = json_decode($request->request->get('internalMeetingId'));
+ $internalMeetingReportClient = json_decode($request->request->get('internalMeetingReport'));
+ $em = $this->getDoctrine()->getManager();
+ /** @var EntUser $user */
+ $user = $this->getUser();
+
+
+ /** @var EntInternalMeeting $entInternalMeeting */
+ $entInternalMeeting = $em->getRepository('App:EntInternalMeeting')->find($internalMeetingIdClient);
+ $bHasRightsToMakeReport =
+ $entInternalMeeting->getOwnerUserId() == $user->getId() ||
+ $user->isAdmin();
+ if (is_null($entInternalMeeting) || !$bHasRightsToMakeReport) {
+ return Reply::getErrorResponse(Message::ERROR_DEFAULT);
+ }
+
+ $entInternalMeeting->setReport($internalMeetingReportClient);
+ $em->persist($entInternalMeeting);
+ $em->flush();
+
+ $mappedInternalMeeting = $entInternalMeeting->clientMapper($em, true);
+ $serviceData = new ServiceData();
+ $serviceData->addServiceData(ServiceData::SERVICE_DATA_TYPE_INTERNAL_MEETINGS, ServiceData::ACTION_EDIT, $mappedInternalMeeting);
+ return Reply::getResponse($mappedInternalMeeting, Message::SUCCESS_INTERNAL_MEETING_REPORT_SET, 0, $serviceData);
+ }
+}
\ No newline at end of file
diff --git a/httpdocs/src/Controller/MeetingViewController.php b/httpdocs/src/Controller/MeetingViewController.php
new file mode 100644
index 0000000..6bee147
--- /dev/null
+++ b/httpdocs/src/Controller/MeetingViewController.php
@@ -0,0 +1,90 @@
+getDoctrine()->getManager();
+ return Reply::getResponse(
+ [
+ 'customerMeetings' => Utils::clientMap($em, $em->getRepository(EntCustomerMeeting::class)->findAll()),
+ 'internalMeetings' => Utils::clientMap($em, $em->getRepository(EntInternalMeeting::class)->findAll()),
+ ]
+ );
+ }
+
+ #[Route('/export-meeting-list', name: 'export_meeting_list')]
+ public function exportMeetingData(Request $request)
+ {
+ $clientData = json_decode($request->request->get('meetingList'));
+ $assetDir = $this->getParameter('asset_directory');
+
+ $spreadsheet = new Spreadsheet();
+ $activeSheet = $spreadsheet->getActiveSheet();
+
+ $writer = new Xlsx($spreadsheet);
+
+ Excel::setRangeColor($activeSheet, 'A', 1, 'I', 1, 'FF9bb3d4');
+
+ Excel::setColumn($activeSheet, 'A', 20, "Typ",
+ ExcelStyle::$CADASTER_BORDERS, 1,Alignment::HORIZONTAL_LEFT, Alignment::HORIZONTAL_LEFT, true, 'FFD9D8D9', null, true);
+ Excel::setColumn($activeSheet, 'B', 20, "Datum (Start)",
+ ExcelStyle::$CADASTER_BORDERS, 1,Alignment::HORIZONTAL_LEFT, Alignment::HORIZONTAL_LEFT, true, 'FFD9D8D9', null, true);
+ Excel::setColumn($activeSheet, 'C', 20, "Datum (Ende)",
+ ExcelStyle::$CADASTER_BORDERS, 1,Alignment::HORIZONTAL_LEFT, Alignment::HORIZONTAL_LEFT, true, 'FFD9D8D9', null, true);
+ Excel::setColumn($activeSheet, 'D', 30, "Kunde / Betreiber",
+ ExcelStyle::$CADASTER_BORDERS, 1,Alignment::HORIZONTAL_LEFT, Alignment::HORIZONTAL_LEFT, true, 'FFD9D8D9', null, true);
+ Excel::setColumn($activeSheet, 'E', 30, "PLP Verantwortlicher",
+ ExcelStyle::$CADASTER_BORDERS, 1,Alignment::HORIZONTAL_LEFT, Alignment::HORIZONTAL_LEFT, true, 'FFD9D8D9', null, true);
+ Excel::setColumn($activeSheet, 'F', 30, "Gesprächspartner",
+ ExcelStyle::$CADASTER_BORDERS, 1,Alignment::HORIZONTAL_LEFT, Alignment::HORIZONTAL_LEFT, true, 'FFD9D8D9', null, true);
+ Excel::setColumn($activeSheet, 'G', 40, "Titel",
+ ExcelStyle::$CADASTER_BORDERS, 1,Alignment::HORIZONTAL_LEFT, Alignment::HORIZONTAL_LEFT, true, 'FFD9D8D9', null, true);
+ Excel::setColumn($activeSheet, 'H', 10, "Termin-Art",
+ ExcelStyle::$CADASTER_BORDERS, 1,Alignment::HORIZONTAL_LEFT, Alignment::HORIZONTAL_LEFT, true, 'FFD9D8D9', null, true);
+ Excel::setColumn($activeSheet, 'I', 10, "Options-Termin",
+ ExcelStyle::$CADASTER_BORDERS, 1,Alignment::HORIZONTAL_LEFT, Alignment::HORIZONTAL_LEFT, true, 'FFD9D8D9', null, true);
+
+ $entryRowIndex = 2;
+ foreach ($clientData as $entry) {
+ $activeSheet->getCell('A'.$entryRowIndex)->setValue($entry->itemType === "customer" ? "Kundentermin" : ($entry->itemType === "internal" ? "Intern" : "Betreibertermin"));
+
+ $startDate = \DateTime::createFromFormat('Y-m-d H:i:s', $entry->dateStart);
+ $endDate = \DateTime::createFromFormat('Y-m-d H:i:s', $entry->dateEnd);
+ $activeSheet->getCell('B'.$entryRowIndex)->setValue(date_format($startDate, 'd.m.Y H:i'));
+ $activeSheet->getCell('C'.$entryRowIndex)->setValue(date_format($endDate, 'd.m.Y H:i'));
+ $activeSheet->getCell('D'.$entryRowIndex)->setValue($entry->objectName);
+ $activeSheet->getCell('E'.$entryRowIndex)->setValue($entry->owner);
+ $activeSheet->getCell('F'.$entryRowIndex)->setValue($entry->contactName);
+ $activeSheet->getCell('G'.$entryRowIndex)->setValue($entry->title);
+ $activeSheet->getCell('H'.$entryRowIndex)->setValue($entry->type);
+ $activeSheet->getCell('I'.$entryRowIndex)->setValue($entry->is_option_meeting ? "Ja" : "Nein");
+ $entryRowIndex++;
+ }
+
+ return Reply::getExcelResponse($spreadsheet, $writer, $assetDir, "Terminliste");
+ }
+}
\ No newline at end of file
diff --git a/httpdocs/src/Controller/PublicController.php b/httpdocs/src/Controller/PublicController.php
new file mode 100644
index 0000000..702c0ae
--- /dev/null
+++ b/httpdocs/src/Controller/PublicController.php
@@ -0,0 +1,57 @@
+ 'CRM api ready'
+ );
+ return new JsonResponse($data);
+ }
+
+ #[Route('/test', name: 'test')]
+ public function showTestAction()
+ {
+ $data = array(
+ // you might translate this message
+ 'message' => 'PHP api'
+ );
+ //return 1;
+ return new Response('
Debug data');
+ }
+
+ #[Route('/login', name: 'login')]
+ public function loginAction()
+ {
+ $error = $this->get('security.authentication_utils')->getLastAuthenticationError();
+
+ $data = [
+ 'error' => $error
+ ];
+
+ return new JsonResponse($data);
+ }
+
+ #[Route('/logout', name: 'security_logout')]
+ public function logoutAction()
+ {
+ throw new Exception('this should not be reached');
+ }
+
+}
\ No newline at end of file
diff --git a/httpdocs/src/Entity/.gitignore b/httpdocs/src/Entity/.gitignore
new file mode 100644
index 0000000..e69de29
diff --git a/httpdocs/src/Entity/EntCountry.php b/httpdocs/src/Entity/EntCountry.php
new file mode 100644
index 0000000..a45e08f
--- /dev/null
+++ b/httpdocs/src/Entity/EntCountry.php
@@ -0,0 +1,166 @@
+ 'Deutschland',
+ self::COUNTRY_AUSTRIA_ID => 'Österreich',
+ self::COUNTRY_SWITZERLAND_ID => 'Schweiz',
+ self::COUNTRY_FRANCE_ID => 'Frankreich',
+ );
+
+ /**
+ * @ORM\Id()
+ * @ORM\GeneratedValue()
+ * @ORM\Column(type="integer")
+ */
+ protected $id;
+
+ /**
+ * @ORM\Column(type="string", length=50, nullable=false)
+ */
+ protected $name;
+
+ /**
+ * @ORM\Column(type="string", length=3)
+ */
+ protected $iso_code;
+
+
+ /**
+ * Gets static countries
+ * @param EntityManagerInterface $em
+ * @return array
+ */
+ public static function getCountriesById(EntityManagerInterface $em)
+ {
+ if (is_null(self::$cacheCountriesById)) {
+ self::$cacheCountriesById = Utils::getSortedObjectsById($em->getRepository('App:EntCountry')->findAll());
+ }
+ return self::$cacheCountriesById;
+ }
+
+ /**
+ * Gets static country
+ * @param EntityManagerInterface $em
+ * @param $countryId
+ * @return mixed|null
+ */
+ public static function getCountryById(EntityManagerInterface $em, $countryId)
+ {
+ $entCountries = self::getCountriesById($em);
+ return array_key_exists($countryId, $entCountries) ? $entCountries[$countryId] : null;
+ }
+
+ /**
+ * Gets static country by name
+ * @param EntityManagerInterface $em
+ * @param $countryName
+ * @return mixed|null
+ */
+ public static function getCountryByName(EntityManagerInterface $em, $countryName)
+ {
+ $entCountries = self::getCountriesById($em);
+ if (is_null(self::$cacheCountriesByName)) {
+ self::$cacheCountriesByName = Utils::getSortedObjects('getName', $entCountries);
+ }
+ return array_key_exists($countryName, self::$cacheCountriesByName) ? self::$cacheCountriesByName[$countryName] : null;
+ }
+
+ /**
+ * EntCountry constructor.
+ * @param $countryName
+ * @param $isoCode
+ */
+ public function __construct($countryName, $isoCode)
+ {
+ if (!in_array($countryName, self::$validCountries)) {
+ throw new Exception('Invalid country name');
+ }
+
+ $this->name = $countryName;
+ $this->iso_code = $isoCode;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getId()
+ {
+ return $this->id;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getIsoCode()
+ {
+ return $this->iso_code;
+ }
+
+ /**
+ * Returns if country id is valid
+ * @param $countryId
+ * @return bool
+ */
+ public static function isValidCountryId($countryId)
+ {
+ return array_key_exists($countryId, self::$validCountries);
+ }
+
+ /**
+ * Returns country name by given country id
+ * @param $countryId
+ * @return mixed|string
+ */
+ public static function getCountryNameById($countryId)
+ {
+ if (!self::isValidCountryId($countryId)) {
+ return "";
+ }
+ return self::$validCountries[$countryId];
+ }
+
+ /**
+ * Clientmapper
+ * @param EntityManagerInterface $em
+ * @param bool $fullMapping
+ * @return array
+ */
+ public function clientMapper(EntityManagerInterface $em, $fullMapping = false)
+ {
+ return [
+ 'id' => $this->getId(),
+ 'name' => $this->getName(),
+ 'iso_code' => $this->getIsoCode(),
+ ];
+ }
+}
\ No newline at end of file
diff --git a/httpdocs/src/Entity/EntCustomer.php b/httpdocs/src/Entity/EntCustomer.php
new file mode 100644
index 0000000..15fc042
--- /dev/null
+++ b/httpdocs/src/Entity/EntCustomer.php
@@ -0,0 +1,458 @@
+name = $name;
+ $this->active = true;
+ $this->creation_date = new \DateTime();
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getId()
+ {
+ return $this->id;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getNameAdditional()
+ {
+ return $this->name_additional;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getConsultantUserId()
+ {
+ return $this->consultant_user_id;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getStreet()
+ {
+ return $this->street;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getStreetNo()
+ {
+ return $this->street_no;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getZip()
+ {
+ return $this->zip;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getCity()
+ {
+ return $this->city;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getCountryId()
+ {
+ return $this->country_id;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getUrl()
+ {
+ return $this->url;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getEmail()
+ {
+ return $this->email;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getPhoneNo()
+ {
+ return $this->phone_no;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getMobileNo()
+ {
+ return $this->mobile_no;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getFaxNo()
+ {
+ return $this->fax_no;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getComment()
+ {
+ return $this->comment;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getActive()
+ {
+ return $this->active;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getOldPlpId()
+ {
+ return $this->old_plp_id;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getCreationDate()
+ {
+ return $this->creation_date;
+ }
+
+ /**
+ * @param mixed $name
+ */
+ public function setName($name): void
+ {
+ $this->name = $name;
+ }
+
+ /**
+ * @param mixed $name_additional
+ */
+ public function setNameAdditional($name_additional): void
+ {
+ $this->name_additional = $name_additional;
+ }
+
+ /**
+ * @param mixed $consultant_user_id
+ */
+ public function setConsultantUserId($consultant_user_id): void
+ {
+ $this->consultant_user_id = $consultant_user_id;
+ }
+
+ /**
+ * @param mixed $street
+ */
+ public function setStreet($street): void
+ {
+ $this->street = $street;
+ }
+
+ /**
+ * @param mixed $street_no
+ */
+ public function setStreetNo($street_no): void
+ {
+ $this->street_no = $street_no;
+ }
+
+ /**
+ * @param mixed $zip
+ */
+ public function setZip($zip): void
+ {
+ $this->zip = $zip;
+ }
+
+ /**
+ * @param mixed $city
+ */
+ public function setCity($city): void
+ {
+ $this->city = $city;
+ }
+
+ /**
+ * @param mixed $country_id
+ */
+ public function setCountryId($country_id): void
+ {
+ $this->country_id = $country_id;
+ }
+
+ /**
+ * @param mixed $url
+ */
+ public function setUrl($url): void
+ {
+ $this->url = $url;
+ }
+
+ /**
+ * @param mixed $email
+ */
+ public function setEmail($email): void
+ {
+ $this->email = $email;
+ }
+
+ /**
+ * @param mixed $phone_no
+ */
+ public function setPhoneNo($phone_no): void
+ {
+ $this->phone_no = $phone_no;
+ }
+
+ /**
+ * @param mixed $mobile_no
+ */
+ public function setMobileNo($mobile_no): void
+ {
+ $this->mobile_no = $mobile_no;
+ }
+
+ /**
+ * @param mixed $fax_no
+ */
+ public function setFaxNo($fax_no): void
+ {
+ $this->fax_no = $fax_no;
+ }
+
+ /**
+ * @param mixed $active
+ */
+ public function setActive($active): void
+ {
+ $this->active = $active;
+ }
+
+ /**
+ * @param mixed $comment
+ */
+ public function setComment($comment): void
+ {
+ $this->comment = $comment;
+ }
+
+ /**
+ * @param mixed $old_plp_id
+ */
+ public function setOldPlpId($old_plp_id): void
+ {
+ $this->old_plp_id = $old_plp_id;
+ }
+
+ /**
+ * Sets client data
+ * @param EntityManagerInterface $em
+ * @param $clientData
+ */
+ public function setClientData(EntityManagerInterface $em, $clientData)
+ {
+ $this->setName($clientData->name);
+ $this->setNameAdditional($clientData->name_additional);
+ $this->setStreet($clientData->street);
+ $this->setStreetNo($clientData->street_no);
+ $this->setZip($clientData->zip);
+ $this->setCity($clientData->city);
+ $this->setCountryId($clientData->country_id);
+ $this->setUrl($clientData->url);
+ $this->setEmail($clientData->email);
+ $this->setPhoneNo($clientData->phone_no);
+ $this->setMobileNo($clientData->mobile_no);
+ $this->setFaxNo($clientData->fax_no);
+ $this->setComment($clientData->comment);
+ $this->setActive($clientData->active);
+ $this->setConsultantUserId($clientData->consultant_user_id);
+ }
+
+ /**
+ * Client mapper
+ * @param EntityManagerInterface $em
+ * @param bool $fullMapping
+ * @return array
+ */
+ public function clientMapper(EntityManagerInterface $em, $fullMapping = false)
+ {
+ $res = [
+ 'id' => $this->id,
+ 'old_plp_id' => $this->old_plp_id,
+ 'name' => $this->name,
+ 'name_additional' => $this->name_additional,
+ 'consultant_user_id' => $this->consultant_user_id,
+ 'street' => $this->street,
+ 'street_no' => $this->street_no,
+ 'zip' => $this->zip,
+ 'city' => $this->city,
+ 'country_id' => $this->country_id,
+ 'url' => $this->url,
+ 'email' => $this->email,
+ 'phone_no' => $this->phone_no,
+ 'mobile_no' => $this->mobile_no,
+ 'fax_no' => $this->fax_no,
+ 'comment' => $this->comment,
+ 'active' => $this->active,
+ 'v_customer_contacts' => [],
+ 'v_customer_notes' => [],
+ 'v_customer_meetings' => [],
+ ];
+
+ if ($fullMapping) {
+ $res['v_customer_contacts'] = Utils::clientMap($em, $em->getRepository('App:EntCustomerContact')->findBy(['customer_id' => $this->getId()]));
+ $res['v_customer_notes'] = Utils::clientMap($em, $em->getRepository('App:EntCustomerNote')->findBy(['customer_id' => $this->getId()]));
+ $res['v_customer_meetings'] = Utils::clientMap($em, $em->getRepository('App:EntCustomerMeeting')->findBy(['customer_id' => $this->getId()]));
+ }
+
+ return $res;
+ }
+
+}
diff --git a/httpdocs/src/Entity/EntCustomerContact.php b/httpdocs/src/Entity/EntCustomerContact.php
new file mode 100644
index 0000000..e4efac4
--- /dev/null
+++ b/httpdocs/src/Entity/EntCustomerContact.php
@@ -0,0 +1,473 @@
+getId())) {
+ throw new Exception('no id found');
+ }
+
+ $this->customer_id = $entCustomer->getId();
+ $this->lastname = $lastName;
+ $this->creation_date = new \DateTime();
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getId()
+ {
+ return $this->id;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getCustomerId()
+ {
+ return $this->customer_id;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getGender()
+ {
+ return $this->gender;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getFirstname()
+ {
+ return $this->firstname;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getLastname()
+ {
+ return $this->lastname;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getEmail()
+ {
+ return $this->email;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getPhoneNo()
+ {
+ return $this->phone_no;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getMobileNo()
+ {
+ return $this->mobile_no;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getFaxNo()
+ {
+ return $this->fax_no;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getDepartment()
+ {
+ return $this->department;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getCreationDate()
+ {
+ return $this->creation_date;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getDateOfBirth()
+ {
+ return $this->date_of_birth;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getStreet()
+ {
+ return $this->street;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getStreetNo()
+ {
+ return $this->street_no;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getZip()
+ {
+ return $this->zip;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getCity()
+ {
+ return $this->city;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getCountryId()
+ {
+ return $this->country_id;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getComment()
+ {
+ return $this->comment;
+ }
+
+ /**
+ * @param mixed $gender
+ */
+ public function setGender($gender): void
+ {
+ if (!is_null($gender) && !Utils::isValidGender($gender)) {
+ throw new Exception('invalid gender');
+ }
+ $this->gender = $gender;
+ }
+
+ /**
+ * @param mixed $firstname
+ */
+ public function setFirstname($firstname): void
+ {
+ $this->firstname = $firstname;
+ }
+
+ /**
+ * @param mixed $lastname
+ */
+ public function setLastname($lastname): void
+ {
+ $this->lastname = $lastname;
+ }
+
+ /**
+ * @param mixed $email
+ */
+ public function setEmail($email): void
+ {
+ $this->email = $email;
+ }
+
+ /**
+ * @param mixed $phone_no
+ */
+ public function setPhoneNo($phone_no): void
+ {
+ $this->phone_no = $phone_no;
+ }
+
+ /**
+ * @param mixed $mobile_no
+ */
+ public function setMobileNo($mobile_no): void
+ {
+ $this->mobile_no = $mobile_no;
+ }
+
+ /**
+ * @param mixed $fax_no
+ */
+ public function setFaxNo($fax_no): void
+ {
+ $this->fax_no = $fax_no;
+ }
+
+ /**
+ * @param mixed $department
+ */
+ public function setDepartment($department): void
+ {
+ $this->department = $department;
+ }
+
+ /**
+ * @param mixed $date_of_birth
+ */
+ public function setDateOfBirth($date_of_birth): void
+ {
+ $this->date_of_birth = !is_null($date_of_birth) ? \DateTime::createFromFormat('Y-m-d', $date_of_birth) : $date_of_birth;
+ }
+
+ /**
+ * @param mixed $street
+ */
+ public function setStreet($street): void
+ {
+ $this->street = $street;
+ }
+
+ /**
+ * @param mixed $street_no
+ */
+ public function setStreetNo($street_no): void
+ {
+ $this->street_no = $street_no;
+ }
+
+ /**
+ * @param mixed $zip
+ */
+ public function setZip($zip): void
+ {
+ $this->zip = $zip;
+ }
+
+ /**
+ * @param mixed $city
+ */
+ public function setCity($city): void
+ {
+ $this->city = $city;
+ }
+
+ /**
+ * @param mixed $country_id
+ */
+ public function setCountryId($country_id): void
+ {
+ $this->country_id = $country_id;
+ }
+
+ /**
+ * @param mixed $comment
+ */
+ public function setComment($comment): void
+ {
+ $this->comment = $comment;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isIsxmasMailRecipient(): bool
+ {
+ return $this->is_xmas_mail_recipient;
+ }
+
+ /**
+ * @param bool $is_xmas_mail_recipient
+ */
+ public function setIsxmasMailRecipient(bool $is_xmas_mail_recipient): void
+ {
+ $this->is_xmas_mail_recipient = $is_xmas_mail_recipient;
+ }
+
+ /**
+ * Sets client data
+ * @param EntityManagerInterface $em
+ * @param $clientData
+ */
+ public function setClientData(EntityManagerInterface $em, $clientData)
+ {
+ $this->setGender($clientData->gender);
+ $this->setFirstname($clientData->firstname);
+ $this->setLastname($clientData->lastname);
+ $this->setEmail($clientData->email);
+ $this->setPhoneNo($clientData->phone_no);
+ $this->setMobileNo($clientData->mobile_no);
+ $this->setFaxNo($clientData->fax_no);
+ $this->setDepartment($clientData->department);
+ $this->setDateOfBirth($clientData->date_of_birth);
+ $this->setStreet($clientData->street);
+ $this->setStreetNo($clientData->street_no);
+ $this->setZip($clientData->zip);
+ $this->setCity($clientData->city);
+ $this->setCountryId($clientData->country_id);
+ $this->setComment($clientData->comment);
+ $this->setIsxmasMailRecipient($clientData->is_xmas_mail_recipient);
+
+ }
+
+ /**
+ * Client mapper
+ * @param EntityManagerInterface $em
+ * @param bool $fullMapping
+ * @return array
+ */
+ public function clientMapper(EntityManagerInterface $em, $fullMapping = false)
+ {
+ return [
+ 'id' => $this->id,
+ 'customer_id' => $this->customer_id,
+ 'gender' => $this->gender,
+ 'firstname' => $this->firstname,
+ 'lastname' => $this->lastname,
+ 'email' => $this->email,
+ 'phone_no' => $this->phone_no,
+ 'mobile_no' => $this->mobile_no,
+ 'fax_no' => $this->fax_no,
+ 'department' => $this->department,
+ 'date_of_birth' => !is_null($this->date_of_birth) ? $this->date_of_birth->format('Y-m-d'): null,
+ 'street' => $this->street,
+ 'street_no' => $this->street_no,
+ 'zip' => $this->zip,
+ 'city' => $this->city,
+ 'country_id' => $this->country_id,
+ 'comment' => $this->comment,
+ 'is_xmas_mail_recipient' => $this->is_xmas_mail_recipient,
+ ];
+ }
+
+}
diff --git a/httpdocs/src/Entity/EntCustomerMeeting.php b/httpdocs/src/Entity/EntCustomerMeeting.php
new file mode 100644
index 0000000..ecc348b
--- /dev/null
+++ b/httpdocs/src/Entity/EntCustomerMeeting.php
@@ -0,0 +1,758 @@
+getId())) {
+ throw new Exception('no id found');
+ }
+
+ if (is_null($entCreationUser->getId())) {
+ throw new Exception('no id found');
+ }
+
+ if (is_null($entOwnerUser->getId())) {
+ throw new Exception('no id found');
+ }
+
+ $meetingTypesById = EntMeetingType::getMeetingTypesById($em);
+ if (is_null($entMeetingType->getId()) || !array_key_exists($entMeetingType->getId(), $meetingTypesById)) {
+ throw new Exception('no id found');
+ }
+
+ $this->customer_id = $entCustomer->getId();
+ $this->creation_user_id = $entCreationUser->getId();
+ $this->owner_user_id = $entOwnerUser->getId();
+ $this->meeting_type_id = $entMeetingType->getId();
+ $this->is_option_meeting = $isOptionMeeting;
+ $this->title = $title;
+ $this->setStartDate($startDate);
+ $this->setEndDate($endDate);
+ $this->first_reminder_sent = false;
+ $this->second_reminder_sent = false;
+ $this->report_done = false;
+ $this->report_reminder_sent = false;
+ $this->creation_date = new \DateTime();
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getId()
+ {
+ return $this->id;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getCustomerId()
+ {
+ return $this->customer_id;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getCreationUserId()
+ {
+ return $this->creation_user_id;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getOwnerUserId()
+ {
+ return $this->owner_user_id;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getMeetingTypeId()
+ {
+ return $this->meeting_type_id;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getIsOptionMeeting()
+ {
+ return $this->is_option_meeting;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getTitle()
+ {
+ return $this->title;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getDescription()
+ {
+ return $this->description;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getStartDate()
+ {
+ return $this->start_date;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getEndDate()
+ {
+ return $this->end_date;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getCustomerContactId()
+ {
+ return $this->customer_contact_id;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getGender()
+ {
+ return $this->gender;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getFirstname()
+ {
+ return $this->firstname;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getLastname()
+ {
+ return $this->lastname;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getEmail()
+ {
+ return $this->email;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getPhoneNo()
+ {
+ return $this->phone_no;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getMobileNo()
+ {
+ return $this->mobile_no;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getDepartment()
+ {
+ return $this->department;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getStreet()
+ {
+ return $this->street;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getStreetNo()
+ {
+ return $this->street_no;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getZip()
+ {
+ return $this->zip;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getCity()
+ {
+ return $this->city;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getCountryId()
+ {
+ return $this->country_id;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getReport()
+ {
+ return $this->report;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getFirstReminderSent()
+ {
+ return $this->first_reminder_sent;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getSecondReminderSent()
+ {
+ return $this->second_reminder_sent;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getReportDone()
+ {
+ return $this->report_done;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getReportReminderSent()
+ {
+ return $this->report_reminder_sent;
+ }
+
+ /**
+ * @return \DateTime
+ */
+ public function getCreationDate(): \DateTime
+ {
+ return $this->creation_date;
+ }
+
+ /**
+ * @param mixed $owner_user_id
+ */
+ public function setOwnerUserId($owner_user_id): void
+ {
+ $this->owner_user_id = $owner_user_id;
+ }
+
+ /**
+ * @param mixed $gender
+ */
+ public function setGender($gender): void
+ {
+ if (!is_null($gender) && !Utils::isValidGender($gender)) {
+ throw new Exception('invalid gender');
+ }
+ $this->gender = $gender;
+ }
+
+ /**
+ * @param mixed $meeting_type_id
+ */
+ public function setMeetingTypeId($meeting_type_id): void
+ {
+ $this->meeting_type_id = $meeting_type_id;
+ }
+
+ /**
+ * @param mixed $is_option_meeting
+ */
+ public function setIsOptionMeeting($is_option_meeting): void
+ {
+ $this->is_option_meeting = $is_option_meeting;
+ }
+
+ /**
+ * @param mixed $title
+ */
+ public function setTitle($title): void
+ {
+ $this->title = $title;
+ }
+
+ /**
+ * @param mixed $description
+ */
+ public function setDescription($description): void
+ {
+ $this->description = $description;
+ }
+
+ /**
+ * @param mixed $start_date
+ */
+ public function setStartDate($start_date): void
+ {
+ $this->start_date = \DateTime::createFromFormat('Y-m-d H:i:s', $start_date);
+ }
+
+ /**
+ * @param mixed $end_date
+ */
+ public function setEndDate($end_date): void
+ {
+ $this->end_date = \DateTime::createFromFormat('Y-m-d H:i:s', $end_date);
+ }
+
+ /**
+ * @param ObjectManager $em
+ * @param $customer_contact_id
+ */
+ public function setCustomerContactId(ObjectManager $em, $customer_contact_id): void
+ {
+ if ($customer_contact_id == 0) {
+ $this->customer_contact_id = $customer_contact_id;
+ } else {
+ /** @var EntCustomerContact $entCustomerContact */
+ $entCustomerContact = $em->getRepository('App:EntCustomerContact')->find($customer_contact_id);
+ // NOTE: Check if contact still exists
+ $this->customer_contact_id = !is_null($entCustomerContact) ? $customer_contact_id : null;
+ }
+ }
+
+ /**
+ * @param mixed $firstname
+ */
+ public function setFirstname($firstname): void
+ {
+ $this->firstname = $firstname;
+ }
+
+ /**
+ * @param mixed $lastname
+ */
+ public function setLastname($lastname): void
+ {
+ $this->lastname = $lastname;
+ }
+
+ /**
+ * @param mixed $email
+ */
+ public function setEmail($email): void
+ {
+ $this->email = $email;
+ }
+
+ /**
+ * @param mixed $phone_no
+ */
+ public function setPhoneNo($phone_no): void
+ {
+ $this->phone_no = $phone_no;
+ }
+
+ /**
+ * @param mixed $mobile_no
+ */
+ public function setMobileNo($mobile_no): void
+ {
+ $this->mobile_no = $mobile_no;
+ }
+
+ /**
+ * @param mixed $department
+ */
+ public function setDepartment($department): void
+ {
+ $this->department = $department;
+ }
+
+ /**
+ * @param mixed $street
+ */
+ public function setStreet($street): void
+ {
+ $this->street = $street;
+ }
+
+ /**
+ * @param mixed $street_no
+ */
+ public function setStreetNo($street_no): void
+ {
+ $this->street_no = $street_no;
+ }
+
+ /**
+ * @param mixed $zip
+ */
+ public function setZip($zip): void
+ {
+ $this->zip = $zip;
+ }
+
+ /**
+ * @param mixed $city
+ */
+ public function setCity($city): void
+ {
+ $this->city = $city;
+ }
+
+ /**
+ * @param mixed $country_id
+ */
+ public function setCountryId($country_id): void
+ {
+ $this->country_id = $country_id;
+ }
+
+ /**
+ * @param $report
+ * @throws \Exception
+ */
+ public function setReport($report): void
+ {
+ $now = new \DateTime();
+ if ($now < $this->getStartDate()) {
+ throw new Exception('meeting has not started yet');
+ }
+ $this->report = $report;
+ }
+
+ /**
+ * @param mixed $first_reminder_sent
+ */
+ public function setFirstReminderSent($first_reminder_sent): void
+ {
+ $this->first_reminder_sent = $first_reminder_sent;
+ }
+
+ /**
+ * @param mixed $second_reminder_sent
+ */
+ public function setSecondReminderSent($second_reminder_sent): void
+ {
+ $this->second_reminder_sent = $second_reminder_sent;
+ }
+
+ /**
+ * @param mixed $report_done
+ */
+ public function setReportDone($report_done): void
+ {
+ $this->report_done = $report_done;
+ }
+
+ /**
+ * @param mixed $report_reminder_sent
+ */
+ public function setReportReminderSent($report_reminder_sent): void
+ {
+ $this->report_reminder_sent = $report_reminder_sent;
+ }
+
+ /**
+ * Checks if meeting is editable or deletable
+ * @return bool
+ * @throws \Exception
+ */
+ public function isEditDeletable()
+ {
+ $now = new \DateTime();
+ return $now < $this->getStartDate();
+ }
+
+ /**
+ * Sets client data
+ * @param EntityManagerInterface $em
+ * @param $clientData
+ */
+ public function setClientData(EntityManagerInterface $em, $clientData)
+ {
+ $this->setOwnerUserId($clientData->owner_user_id);
+ $this->setMeetingTypeId($clientData->meeting_type_id);
+ $this->setIsOptionMeeting($clientData->is_option_meeting);
+ $this->setTitle($clientData->title);
+ $this->setDescription($clientData->description);
+ $this->setStartDate($clientData->start_date);
+ $this->setEndDate($clientData->end_date);
+ $this->setStreet($clientData->street);
+ $this->setStreetNo($clientData->street_no);
+ $this->setZip($clientData->zip);
+ $this->setCity($clientData->city);
+ $this->setCountryId($clientData->country_id);
+ $this->setCustomerContactId($em, $clientData->customer_contact_id);
+
+ if (is_null($this->customer_contact_id)) {
+ $this->setGender(null);
+ $this->setFirstname(null);
+ $this->setLastname(null);
+ $this->setEmail(null);
+ $this->setPhoneNo(null);
+ $this->setMobileNo(null);
+ $this->setDepartment(null);
+ } else {
+ $this->setGender($clientData->gender);
+ $this->setFirstname($clientData->firstname);
+ $this->setLastname($clientData->lastname);
+ $this->setEmail($clientData->email);
+ $this->setPhoneNo($clientData->phone_no);
+ $this->setMobileNo($clientData->mobile_no);
+ $this->setDepartment($clientData->department);
+ }
+ }
+
+ /**
+ * Client mapper
+ * @param EntityManagerInterface $em
+ * @param bool $fullMapping
+ * @return array
+ * @throws \Exception
+ */
+ public function clientMapper(EntityManagerInterface $em, $fullMapping = false)
+ {
+ $now = new \DateTime();
+ return [
+ 'id' => $this->id,
+ 'customer_id' => (int)$this->customer_id,
+ 'creation_user_id' => (int)$this->creation_user_id,
+ 'owner_user_id' => (int)$this->owner_user_id,
+ 'meeting_type_id' => (int)$this->meeting_type_id,
+ 'is_option_meeting' => $this->is_option_meeting,
+ 'title' => $this->title,
+ 'description' => $this->description,
+ 'start_date' => !is_null($this->start_date) ? $this->start_date->format('Y-m-d H:i:s'): null,
+ 'end_date' => !is_null($this->end_date) ? $this->end_date->format('Y-m-d H:i:s'): null,
+ 'customer_contact_id' => $this->customer_contact_id,
+ 'gender' => $this->gender,
+ 'firstname' => $this->firstname,
+ 'lastname' => $this->lastname,
+ 'email' => $this->email,
+ 'phone_no' => $this->phone_no,
+ 'mobile_no' => $this->mobile_no,
+ 'department' => $this->department,
+ 'street' => $this->street,
+ 'street_no' => $this->street_no,
+ 'zip' => $this->zip,
+ 'city' => $this->city,
+ 'country_id' => $this->country_id,
+ 'report' => $this->report,
+ 'first_reminder_sent' => $this->first_reminder_sent,
+ 'second_reminder_sent' => $this->second_reminder_sent,
+ 'report_done' => $this->report_done,
+ 'report_reminder_sent' => $this->report_reminder_sent,
+ 'creation_date' => !is_null($this->creation_date) ? $this->creation_date->format('Y-m-d H:i:s'): null,
+ 'v_participants' => Utils::clientMap($em, $em->getRepository('App:EntCustomerMeetingParticipant')->findBy(['customer_meeting_id' => $this->id])),
+ 'v_is_editable' => $now < $this->start_date,
+ ];
+ }
+
+}
diff --git a/httpdocs/src/Entity/EntCustomerMeetingParticipant.php b/httpdocs/src/Entity/EntCustomerMeetingParticipant.php
new file mode 100644
index 0000000..e26ccce
--- /dev/null
+++ b/httpdocs/src/Entity/EntCustomerMeetingParticipant.php
@@ -0,0 +1,96 @@
+getId())) {
+ throw new Exception('no id found');
+ }
+
+ if (is_null($entParticipantUser->getId())) {
+ throw new Exception('no id found');
+ }
+
+ $this->customer_meeting_id = $entCustomerMeeting->getId();
+ $this->participant_user_id = $entParticipantUser->getId();
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getId()
+ {
+ return $this->id;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getCustomerMeetingId()
+ {
+ return $this->customer_meeting_id;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getParticipantUserId()
+ {
+ return $this->participant_user_id;
+ }
+
+ /**
+ * Client mapper
+ * @param EntityManagerInterface $em
+ * @param bool $fullMapping
+ * @return array
+ */
+ public function clientMapper(EntityManagerInterface $em, $fullMapping = false)
+ {
+ return [
+ 'id' => $this->id,
+ 'customer_meeting_id' => $this->customer_meeting_id,
+ 'participant_user_id' => $this->participant_user_id,
+ ];
+ }
+
+}
diff --git a/httpdocs/src/Entity/EntCustomerNote.php b/httpdocs/src/Entity/EntCustomerNote.php
new file mode 100644
index 0000000..490d1fb
--- /dev/null
+++ b/httpdocs/src/Entity/EntCustomerNote.php
@@ -0,0 +1,440 @@
+getId())) {
+ throw new Exception('no id found');
+ }
+
+ if (is_null($entUser->getId())) {
+ throw new Exception('no id found');
+ }
+
+ $this->customer_id = $entCustomer->getId();
+ $this->creation_user_id = $entUser->getId();
+ $this->creation_user_firstname = $entUser->getFirstname();
+ $this->creation_user_lastname = $entUser->getLastname();
+ $this->title = $title;
+ $this->note_date = new \DateTime();
+ $this->creation_date = new \DateTime();
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getId()
+ {
+ return $this->id;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getCustomerId()
+ {
+ return $this->customer_id;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getCreationUserId()
+ {
+ return $this->creation_user_id;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getGender()
+ {
+ return $this->gender;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getFirstname()
+ {
+ return $this->firstname;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getLastname()
+ {
+ return $this->lastname;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getEmail()
+ {
+ return $this->email;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getPhoneNo()
+ {
+ return $this->phone_no;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getMobileNo()
+ {
+ return $this->mobile_no;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getFaxNo()
+ {
+ return $this->fax_no;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getDepartment()
+ {
+ return $this->department;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getTitle()
+ {
+ return $this->title;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getComment()
+ {
+ return $this->comment;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getNoteDate()
+ {
+ return $this->note_date;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getCreationUserFirstname()
+ {
+ return $this->creation_user_firstname;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getCreationUserLastname()
+ {
+ return $this->creation_user_lastname;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getCreationDate()
+ {
+ return $this->creation_date;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getCustomerContactId()
+ {
+ return $this->customer_contact_id;
+ }
+
+ /**
+ * @param ObjectManager $em
+ * @param $customer_contact_id
+ */
+ public function setCustomerContactId(ObjectManager $em, $customer_contact_id): void
+ {
+ if ($customer_contact_id == 0) {
+ $this->customer_contact_id = $customer_contact_id;
+ } else {
+ /** @var EntCustomerContact $entCustomerContact */
+ $entCustomerContact = $em->getRepository('App:EntCustomerContact')->find($customer_contact_id);
+ // NOTE: Check if contact still exists
+ $this->customer_contact_id = !is_null($entCustomerContact) ? $customer_contact_id : null;
+ }
+ }
+
+ /**
+ * @param mixed $gender
+ */
+ public function setGender($gender): void
+ {
+ if (!is_null($gender) && !Utils::isValidGender($gender)) {
+ throw new Exception('invalid gender');
+ }
+ $this->gender = $gender;
+ }
+
+ /**
+ * @param mixed $firstname
+ */
+ public function setFirstname($firstname): void
+ {
+ $this->firstname = $firstname;
+ }
+
+ /**
+ * @param mixed $lastname
+ */
+ public function setLastname($lastname): void
+ {
+ $this->lastname = $lastname;
+ }
+
+ /**
+ * @param mixed $email
+ */
+ public function setEmail($email): void
+ {
+ $this->email = $email;
+ }
+
+ /**
+ * @param mixed $phone_no
+ */
+ public function setPhoneNo($phone_no): void
+ {
+ $this->phone_no = $phone_no;
+ }
+
+ /**
+ * @param mixed $mobile_no
+ */
+ public function setMobileNo($mobile_no): void
+ {
+ $this->mobile_no = $mobile_no;
+ }
+
+ /**
+ * @param mixed $fax_no
+ */
+ public function setFaxNo($fax_no): void
+ {
+ $this->fax_no = $fax_no;
+ }
+
+ /**
+ * @param mixed $department
+ */
+ public function setDepartment($department): void
+ {
+ $this->department = $department;
+ }
+
+ /**
+ * @param mixed $title
+ */
+ public function setTitle($title): void
+ {
+ $this->title = $title;
+ }
+
+ /**
+ * @param mixed $comment
+ */
+ public function setComment($comment): void
+ {
+ $this->comment = $comment;
+ }
+
+ /**
+ * @param mixed $note_date
+ */
+ public function setNoteDate($note_date): void
+ {
+ $this->note_date = \DateTime::createFromFormat('Y-m-d', $note_date);
+ }
+
+ /**
+ * Sets client data
+ * @param EntityManagerInterface $em
+ * @param $clientData
+ */
+ public function setClientData(EntityManagerInterface $em, $clientData)
+ {
+ $this->setCustomerContactId($em, $clientData->customer_contact_id);
+ $this->setGender($clientData->gender);
+ $this->setFirstname($clientData->firstname);
+ $this->setLastname($clientData->lastname);
+ $this->setEmail($clientData->email);
+ $this->setPhoneNo($clientData->phone_no);
+ $this->setMobileNo($clientData->mobile_no);
+ $this->setFaxNo($clientData->fax_no);
+ $this->setDepartment($clientData->department);
+ $this->setTitle($clientData->title);
+ $this->setComment($clientData->comment);
+ $this->setNoteDate($clientData->note_date);
+ }
+
+ /**
+ * Client mapper
+ * @param EntityManagerInterface $em
+ * @param bool $fullMapping
+ * @return array
+ */
+ public function clientMapper(EntityManagerInterface $em, $fullMapping = false)
+ {
+ return [
+ 'id' => $this->id,
+ 'customer_id' => $this->customer_id,
+ 'customer_contact_id' => $this->customer_contact_id,
+ 'gender' => $this->gender,
+ 'firstname' => $this->firstname,
+ 'lastname' => $this->lastname,
+ 'email' => $this->email,
+ 'phone_no' => $this->phone_no,
+ 'mobile_no' => $this->mobile_no,
+ 'fax_no' => $this->fax_no,
+ 'department' => $this->department,
+ 'title' => $this->title,
+ 'comment' => $this->comment,
+ 'note_date' => !is_null($this->note_date) ? $this->note_date->format('Y-m-d'): null,
+ 'creation_date' => !is_null($this->creation_date) ? $this->creation_date->format('Y-m-d'): null,
+ 'creation_user_id' => $this->getCreationUserId(),
+ 'creation_user_firstname' => $this->getCreationUserFirstname(),
+ 'creation_user_lastname' => $this->getCreationUserLastname(),
+ ];
+ }
+
+}
diff --git a/httpdocs/src/Entity/EntInternalMeeting.php b/httpdocs/src/Entity/EntInternalMeeting.php
new file mode 100644
index 0000000..e635f6a
--- /dev/null
+++ b/httpdocs/src/Entity/EntInternalMeeting.php
@@ -0,0 +1,338 @@
+getId())) {
+ throw new Exception('no id found');
+ }
+
+ if (is_null($entOwnerUser->getId())) {
+ throw new Exception('no id found');
+ }
+
+ $this->creation_user_id = $entCreationUser->getId();
+ $this->owner_user_id = $entOwnerUser->getId();
+ $this->title = $title;
+ $this->setStartDate($startDate);
+ $this->setEndDate($endDate);
+ $this->first_reminder_sent = false;
+ $this->second_reminder_sent = false;
+ $this->report_reminder_sent = false;
+ $this->creation_date = new \DateTime();
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getId()
+ {
+ return $this->id;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getCreationUserId()
+ {
+ return $this->creation_user_id;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getOwnerUserId()
+ {
+ return $this->owner_user_id;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getTitle()
+ {
+ return $this->title;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getDescription()
+ {
+ return $this->description;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getStartDate()
+ {
+ return $this->start_date;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getEndDate()
+ {
+ return $this->end_date;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getReport()
+ {
+ return $this->report;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getFirstReminderSent()
+ {
+ return $this->first_reminder_sent;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getSecondReminderSent()
+ {
+ return $this->second_reminder_sent;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getReportReminderSent()
+ {
+ return $this->report_reminder_sent;
+ }
+
+ /**
+ * @return \DateTime
+ */
+ public function getCreationDate(): \DateTime
+ {
+ return $this->creation_date;
+ }
+
+ /**
+ * @param mixed $owner_user_id
+ */
+ public function setOwnerUserId($owner_user_id): void
+ {
+ $this->owner_user_id = $owner_user_id;
+ }
+
+ /**
+ * @param mixed $title
+ */
+ public function setTitle($title): void
+ {
+ $this->title = $title;
+ }
+
+ /**
+ * @param mixed $description
+ */
+ public function setDescription($description): void
+ {
+ $this->description = $description;
+ }
+
+ /**
+ * @param mixed $start_date
+ */
+ public function setStartDate($start_date): void
+ {
+ $this->start_date = \DateTime::createFromFormat('Y-m-d H:i:s', $start_date);
+ }
+
+ /**
+ * @param mixed $end_date
+ */
+ public function setEndDate($end_date): void
+ {
+ $this->end_date = \DateTime::createFromFormat('Y-m-d H:i:s', $end_date);
+ }
+
+ /**
+ * @param $report
+ * @throws \Exception
+ */
+ public function setReport($report): void
+ {
+ $now = new \DateTime();
+ if ($now < $this->getStartDate()) {
+ throw new Exception('meeting has not started yet');
+ }
+ $this->report = $report;
+ }
+
+ /**
+ * @param mixed $first_reminder_sent
+ */
+ public function setFirstReminderSent($first_reminder_sent): void
+ {
+ $this->first_reminder_sent = $first_reminder_sent;
+ }
+
+ /**
+ * @param mixed $second_reminder_sent
+ */
+ public function setSecondReminderSent($second_reminder_sent): void
+ {
+ $this->second_reminder_sent = $second_reminder_sent;
+ }
+
+ /**
+ * @param mixed $report_reminder_sent
+ */
+ public function setReportReminderSent($report_reminder_sent): void
+ {
+ $this->report_reminder_sent = $report_reminder_sent;
+ }
+
+ /**
+ * Checks if meeting is editable or deletable
+ * @return bool
+ * @throws \Exception
+ */
+ public function isEditDeletable()
+ {
+ $now = new \DateTime();
+ return $now < $this->getStartDate();
+ }
+
+ /**
+ * Sets client data
+ * @param EntityManagerInterface $em
+ * @param $clientData
+ */
+ public function setClientData(EntityManagerInterface $em, $clientData)
+ {
+ $this->setOwnerUserId($clientData->owner_user_id);
+ $this->setTitle($clientData->title);
+ $this->setDescription($clientData->description);
+ $this->setStartDate($clientData->start_date);
+ $this->setEndDate($clientData->end_date);
+ }
+
+ /**
+ * Client mapper
+ * @param EntityManagerInterface $em
+ * @param bool $fullMapping
+ * @return array
+ * @throws \Exception
+ */
+ public function clientMapper(EntityManagerInterface $em, $fullMapping = false)
+ {
+ $now = new \DateTime();
+ return [
+ 'id' => $this->id,
+ 'creation_user_id' => (int)$this->creation_user_id,
+ 'owner_user_id' => (int)$this->owner_user_id,
+ 'title' => $this->title,
+ 'description' => $this->description,
+ 'start_date' => !is_null($this->start_date) ? $this->start_date->format('Y-m-d H:i:s'): null,
+ 'end_date' => !is_null($this->end_date) ? $this->end_date->format('Y-m-d H:i:s'): null,
+ 'report' => $this->report,
+ 'first_reminder_sent' => $this->first_reminder_sent,
+ 'second_reminder_sent' => $this->second_reminder_sent,
+ 'report_reminder_sent' => $this->report_reminder_sent,
+ 'creation_date' => !is_null($this->creation_date) ? $this->creation_date->format('Y-m-d H:i:s'): null,
+ 'v_participants' => Utils::clientMap($em, $em->getRepository('App:EntInternalMeetingParticipant')->findBy(['internal_meeting_id' => $this->id])),
+ 'v_is_editable' => $now < $this->start_date,
+ ];
+ }
+
+}
diff --git a/httpdocs/src/Entity/EntInternalMeetingParticipant.php b/httpdocs/src/Entity/EntInternalMeetingParticipant.php
new file mode 100644
index 0000000..7092420
--- /dev/null
+++ b/httpdocs/src/Entity/EntInternalMeetingParticipant.php
@@ -0,0 +1,96 @@
+getId())) {
+ throw new Exception('no id found');
+ }
+
+ if (is_null($entParticipantUser->getId())) {
+ throw new Exception('no id found');
+ }
+
+ $this->internal_meeting_id = $entInternalMeeting->getId();
+ $this->participant_user_id = $entParticipantUser->getId();
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getId()
+ {
+ return $this->id;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getInternalMeetingId()
+ {
+ return $this->internal_meeting_id;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getParticipantUserId()
+ {
+ return $this->participant_user_id;
+ }
+
+ /**
+ * Client mapper
+ * @param EntityManagerInterface $em
+ * @param bool $fullMapping
+ * @return array
+ */
+ public function clientMapper(EntityManagerInterface $em, $fullMapping = false)
+ {
+ return [
+ 'id' => $this->id,
+ 'internal_meeting_id' => $this->internal_meeting_id,
+ 'participant_user_id' => $this->participant_user_id,
+ ];
+ }
+
+}
diff --git a/httpdocs/src/Entity/EntMeetingType.php b/httpdocs/src/Entity/EntMeetingType.php
new file mode 100644
index 0000000..50cc128
--- /dev/null
+++ b/httpdocs/src/Entity/EntMeetingType.php
@@ -0,0 +1,93 @@
+getRepository('App:EntMeetingType')->findAll());
+ }
+ return self::$cacheMeetingTypesById;
+ }
+
+ public function __construct($type, $name)
+ {
+ $this->type = $type;
+ $this->name = $name;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getId()
+ {
+ return $this->id;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getType()
+ {
+ return $this->type;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * Clientmapper
+ * @param EntityManagerInterface $em
+ * @param bool $fullMapping
+ * @return array
+ */
+ public function clientMapper(EntityManagerInterface $em, $fullMapping = false) {
+ return [
+ 'id' => $this->id,
+ 'type' => $this->type,
+ 'name' => $this->name,
+ ];
+ }
+
+
+}
diff --git a/httpdocs/src/Entity/EntUser.php b/httpdocs/src/Entity/EntUser.php
new file mode 100644
index 0000000..ed815bb
--- /dev/null
+++ b/httpdocs/src/Entity/EntUser.php
@@ -0,0 +1,349 @@
+setUserTypeId($userTypeIdCreatingUser, $userTypeId, $editingUser);
+
+ // Check password length
+ if (!Utils::isValidPasswordLength($password)) {
+ throw new Exception('invalid password length');
+ }
+
+ $this->email = $email;
+ $this->user_type_id = $userTypeId;
+ $this->firstname = $firstName;
+ $this->lastname = $lastName;
+ $this->password = password_hash($password, PASSWORD_BCRYPT, ["cost" => self::CRYPT_COST]);
+ $this->active = true;
+ $this->visible = true;
+ $this->creation_date = new \DateTime();
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getId()
+ {
+ return $this->id;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getEmail()
+ {
+ return $this->email;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getUserTypeId()
+ {
+ return $this->user_type_id;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getFirstname()
+ {
+ return $this->firstname;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getLastname()
+ {
+ return $this->lastname;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getPassword()
+ {
+ return $this->password;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isVisible(): bool
+ {
+ return $this->visible;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isActive(): bool
+ {
+ return $this->active;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getCreationDate()
+ {
+ return $this->creation_date;
+ }
+
+ /**
+ * Returns username as part of UserInterface needed in Symfony
+ * @return string
+ */
+ public function getUserIdentifier(): string
+ {
+ return $this->email;
+ }
+
+ /**
+ * Returns roles as part of UserInterface needed in Symfony
+ * @return string[]
+ */
+ public function getRoles(): array
+ {
+ return EntUserType::getUserRoles($this->user_type_id);
+ }
+
+ /**
+ * Returns if user has admin rights
+ * @return bool
+ */
+ public function isAdmin()
+ {
+ return in_array(EntUserType::USER_ROLE_ADMIN, $this->getRoles());
+ }
+
+ /**
+ * Erases credentials as part of UserInterface needed in Symfony
+ */
+ public function eraseCredentials() {}
+
+ /**
+ * Checks password as part of UserInterface needed in Symfony
+ * @param $password
+ * @return bool
+ */
+ public function checkPassword($password)
+ {
+ return $this->active && password_verify($password, $this->password);
+ }
+
+ /**
+ * Returns salt as part of UserInterface needed in Symfony
+ * @return null|string|void
+ */
+ public function getSalt() {}
+
+ /**
+ * @param $userTypeIdCreatingUser
+ * @param $userTypeId
+ * @param EntUser|null $editingUser
+ */
+ public function setUserTypeId($userTypeIdCreatingUser, $userTypeId, EntUser $editingUser = null): void
+ {
+ // Check user type id
+ if (!EntUserType::isValidUserTypeId($userTypeId)) {
+ throw new Exception('invalid user type id');
+ }
+
+ // Compare creating user type with this user type
+ $isEditingUser = is_null($editingUser) ? false : $editingUser->getId() == $this->id;
+ if (!EntUserType::isHigherUserType($userTypeIdCreatingUser, $userTypeId) && !$isEditingUser) {
+ throw new Exception('creating user has not enough rights to create this user type');
+ }
+ $this->user_type_id = $userTypeId;
+
+ }
+
+ /**
+ * Sets email with check on validity and existence
+ * @param ObjectManager $em
+ * @param $email
+ */
+ public function setEmail(ObjectManager $em, $email)
+ {
+ if ($email != $this->email) {
+ if (!Utils::validateEmail($em, $email)) {
+ throw new Exception('email is invalid or already exists: '.$email);
+ }
+ $this->email = $email;
+ }
+ }
+
+ /**
+ * @param bool|string $password
+ */
+ public function setPassword($password): void
+ {
+ $this->password = $password;
+ }
+
+ /**
+ * @param bool $active
+ */
+ public function setActive(bool $active): void
+ {
+ $this->active = $active;
+ }
+
+ /**
+ * @param mixed $lastname
+ */
+ public function setLastname($lastname): void
+ {
+ $this->lastname = $lastname;
+ }
+
+ /**
+ * @param string $firstname
+ * @return EntUser
+ */
+ public function setFirstname(string $firstname): self
+ {
+ $this->firstname = $firstname;
+
+ return $this;
+ }
+
+ /**
+ * @param bool $visible
+ */
+ public function setVisible(bool $visible): void
+ {
+ $this->visible = $visible;
+ }
+
+ /**
+ * Returns whether this user is on higher user role level than given user to compare with
+ * @param EntUser $compareUser
+ * @return bool
+ */
+ public function isHigherUser(EntUser $compareUser)
+ {
+ return EntUserType::isHigherUserType($this->user_type_id, $compareUser->getUserTypeId());
+ }
+
+
+ /**
+ * Sets client data
+ * @param EntityManagerInterface $em
+ * @param $clientData
+ */
+ public function setClientData(EntityManagerInterface $em, $clientData)
+ {
+ $this->setFirstname($clientData->firstname);
+ $this->setLastname($clientData->lastname);
+ $this->setEmail($em, $clientData->email);
+ }
+
+ /**
+ * Client mapper
+ * @param EntityManagerInterface $em
+ * @param bool $fullMapping
+ * @return array
+ */
+ public function clientMapper(EntityManagerInterface $em, $fullMapping = false)
+ {
+ /** @var EntUserType $userType */
+ $userType = $em->getRepository('App:EntUserType')->find($this->user_type_id);
+
+ return [
+ 'id' => $this->id,
+ 'email' => $this->email,
+ 'firstname' => $this->firstname,
+ 'lastname' => $this->lastname,
+ 'active' => $this->active,
+ 'visible' => $this->visible,
+ 'v_translated_role' => EntUserType::getTranslatedRole($this->user_type_id),
+ 'v_user_type' => $userType->clientMapper($em, $fullMapping),
+ ];
+ }
+
+}
diff --git a/httpdocs/src/Entity/EntUserType.php b/httpdocs/src/Entity/EntUserType.php
new file mode 100644
index 0000000..dc887eb
--- /dev/null
+++ b/httpdocs/src/Entity/EntUserType.php
@@ -0,0 +1,161 @@
+ array(self::USER_ROLE_ADMIN, self::USER_ROLE_USER, self::USER_ROLE_SALES, self::USER_ROLE_TECHNIQUE,
+ self::USER_ROLE_PRODUCTION, self::USER_ROLE_ACCOUNTING, self::USER_ROLE_SERVICE),
+ self::USER_TYPE_ID_SALES => array(self::USER_ROLE_USER, self::USER_ROLE_SALES),
+ self::USER_TYPE_ID_TECHNIQUE => array(self::USER_ROLE_USER, self::USER_ROLE_TECHNIQUE, self::USER_ROLE_PRODUCTION, self::USER_ROLE_SERVICE),
+ self::USER_TYPE_ID_PRODUCTION => array(self::USER_ROLE_USER, self::USER_ROLE_PRODUCTION, self::USER_ROLE_SERVICE),
+ self::USER_TYPE_ID_ACCOUNTING => array(self::USER_ROLE_USER, self::USER_ROLE_ACCOUNTING),
+ self::USER_TYPE_ID_SERVICE => array(self::USER_ROLE_USER, self::USER_ROLE_SERVICE, self::USER_ROLE_PRODUCTION),
+ );
+
+ // Role translations for client
+ private static $translatedRoles = array(
+ self::USER_TYPE_ID_ADMIN => 'Admin',
+ self::USER_TYPE_ID_SALES => 'Vertrieb',
+ self::USER_TYPE_ID_TECHNIQUE => 'Technik',
+ self::USER_TYPE_ID_PRODUCTION => 'Produktion',
+ self::USER_TYPE_ID_ACCOUNTING => 'Buchhaltung',
+ self::USER_TYPE_ID_SERVICE => 'Service'
+ );
+
+ /**
+ * @ORM\Id()
+ * @ORM\GeneratedValue()
+ * @ORM\Column(type="integer")
+ */
+ protected $id;
+
+ /**
+ * @ORM\Column(name="type", type="string", nullable=false, unique=true)
+ */
+ protected $type;
+
+
+ public function __construct($type)
+ {
+ $this->type = $type;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getId()
+ {
+ return $this->id;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getType()
+ {
+ return $this->type;
+ }
+
+ /**
+ * Returns all available user roles
+ * @return array
+ */
+ public static function getAllUserRoles()
+ {
+ return self::$userRoles;
+ }
+
+ /**
+ * Returns user roles
+ * @param $userTypeId
+ * @return string[]
+ */
+ public static function getUserRoles($userTypeId)
+ {
+ if (!array_key_exists($userTypeId, self::$userRoles)) {
+ throw new Exception("unknown user type");
+ }
+ return self::$userRoles[$userTypeId];
+ }
+
+ public static function getTranslatedRole($userTypeId)
+ {
+ if (!array_key_exists($userTypeId, self::$translatedRoles)) {
+ throw new Exception('invalid user type id given');
+ }
+ return self::$translatedRoles[$userTypeId];
+ }
+
+ /**
+ * Returns role config
+ * @return array
+ */
+ public static function getRolesConfig()
+ {
+ $rolesConfig = array();
+ foreach (self::$userRoles as $userTypeId => $ur) {
+ $rolesConfig[] = array(
+ 'user_type_id' => $userTypeId,
+ 'roles' => $ur,
+ 'translation' => self::$translatedRoles[$userTypeId]
+ );
+ }
+ return $rolesConfig;
+ }
+
+ public static function isValidUserTypeId($userTypeId)
+ {
+ return array_key_exists($userTypeId, self::$userRoles);
+ }
+
+ /**
+ * Check if a given user type is higher than given compare id
+ * @param $higherUserTypeId
+ * @return bool
+ */
+ public static function isHigherUserType($higherUserTypeId, $lowerUserTypeId)
+ {
+ return $higherUserTypeId < $lowerUserTypeId;
+ }
+
+ public function clientMapper(EntityManagerInterface $em, $fullMapping = false) {
+ return [
+ 'user_type_id' => $this->getId(),
+ 'type' => $this->type,
+ 'v_roles' => self::getUserRoles($this->getId())
+ ];
+ }
+
+
+}
diff --git a/httpdocs/src/Entity/IFace/IEntity.php b/httpdocs/src/Entity/IFace/IEntity.php
new file mode 100644
index 0000000..02a4375
--- /dev/null
+++ b/httpdocs/src/Entity/IFace/IEntity.php
@@ -0,0 +1,21 @@
+setIgnoreNotImportedAnnotations(true);
+$docParser->setImports([
+ 'orm' => 'Doctrine\ORM\Mapping',
+]);
+
+// Initialisiere den IndexedReader mit dem AnnotationReader und DocParser
+$indexedReader = new IndexedReader($annotationReader, $docParser);
+
+// Lade alle Entity-Klassen im Entity-Verzeichnis
+$directoryIterator = new \RecursiveDirectoryIterator($entityDir);
+$iterator = new \RecursiveIteratorIterator($directoryIterator);
+$regexIterator = new \RegexIterator($iterator, '/^.+\.php$/i', \RecursiveRegexIterator::GET_MATCH);
+
+// Schleife über alle Entity-Klassen
+foreach ($regexIterator as $file) {
+ $filePath = $file[0];
+
+ // Lade die Entity-Klasse und rufe die Annotationen ab
+ require_once $filePath;
+ $className = getClassNameFromFile($filePath);
+ $reflectionClass = new \ReflectionClass($className);
+ $reflectionProperties = $reflectionClass->getProperties();
+
+ // Konvertiere die Annotationen in die zweite Art
+ foreach ($reflectionProperties as $reflectionProperty) {
+ $annotations = $annotationReader->getPropertyAnnotations($reflectionProperty);
+
+ foreach ($annotations as $annotation) {
+ if ($annotation instanceof Column) {
+ $type = Types::STRING;
+ $length = $annotation->length;
+ $nullable = !$annotation->nullable;
+
+ $reflectionProperty->setAccessible(true);
+ $columnAnnotation = $reflectionProperty->getValue($reflectionClass);
+
+ $columnAnnotation->type = $type;
+ $columnAnnotation->length = $length;
+ $columnAnnotation->nullable = $nullable;
+
+ $reflectionProperty->setAccessible(false);
+ }
+ }
+ }
+}
+
+/**
+ * Liefert den Klassennamen aus einer Datei.
+ *
+ * @param string $filePath Der Pfad zur Datei.
+ *
+ * @return string|null Der Klassenname oder null, wenn kein gültiger Klassenname gefunden wurde.
+ */
+function getClassNameFromFile(string $filePath): ?string
+{
+ $phpCode = file_get_contents($filePath);
+ $tokens = token_get_all($phpCode);
+
+ $namespace = '';
+ $className = '';
+ $isNamespaceFound = false;
+ $isClassFound = false;
+
+ foreach ($tokens as $token) {
+ if (is_array($token)) {
+ list($tokenType, $tokenValue) = $token;
+
+ if ($tokenType === T_NAMESPACE) {
+ $isNamespaceFound = true;
+ $namespace = '';
+ } elseif ($tokenType === T_CLASS) {
+ $isClassFound = true;
+ $className = '';
+ } elseif ($isNamespaceFound && $tokenType === T_STRING) {
+ $namespace .= $tokenValue;
+ } elseif ($isClassFound && $tokenType === T_STRING) {
+ $className = $tokenValue;
+ break;
+ }
+ }
+ }
+
+ if ($isNamespaceFound && $isClassFound && !empty($className)) {
+ return $namespace . '\\' . $className;
+ }
+
+ return null;
+}
diff --git a/httpdocs/src/EntityVirtual/ServiceData.php b/httpdocs/src/EntityVirtual/ServiceData.php
new file mode 100644
index 0000000..2f3a805
--- /dev/null
+++ b/httpdocs/src/EntityVirtual/ServiceData.php
@@ -0,0 +1,100 @@
+ 1,
+ self::SERVICE_DATA_TYPE_CUSTOMER_CONTACTS => 1,
+ self::SERVICE_DATA_TYPE_CUSTOMER_MEETINGS => 1,
+ self::SERVICE_DATA_TYPE_INTERNAL_MEETINGS => 1,
+ ];
+
+ private static $validActions = [
+ self::ACTION_ADD => 1,
+ self::ACTION_EDIT => 1,
+ self::ACTION_DELETE => 1,
+ ];
+
+ private $serviceData;
+
+ /**
+ * ServiceData constructor.
+ */
+ public function __construct()
+ {
+ $this->serviceData = [];
+ foreach (self::$validServiceDataTypes as $serviceDataType => $dummy) {
+ $this->serviceData[$serviceDataType] = null;
+ }
+ }
+
+ public function getServiceData()
+ {
+ return $this->serviceData;
+ }
+
+ /**
+ * @param $serviceDataType
+ * @param $action
+ * @param $data
+ */
+ public function addServiceData($serviceDataType, $action, $data)
+ {
+ if (!$this->isValidServiceDataType($serviceDataType)) {
+ throw new Exception('invalid service data type');
+ }
+
+ if (!$this->isValidAction($action)) {
+ throw new Exception('invalid action');
+ }
+
+ if (is_null($this->serviceData[$serviceDataType])) {
+ $this->serviceData[$serviceDataType] = [
+ self::ACTION_ADD => [],
+ self::ACTION_EDIT => [],
+ self::ACTION_DELETE => [],
+ ];
+ }
+ $this->serviceData[$serviceDataType][$action][] = $data;
+ }
+
+ /**
+ * @param $serviceDataType
+ * @return bool
+ */
+ private function isValidServiceDataType($serviceDataType)
+ {
+ return array_key_exists($serviceDataType, self::$validServiceDataTypes);
+ }
+
+ /**
+ * @param $action
+ * @return bool
+ */
+ private function isValidAction($action)
+ {
+ return array_key_exists($action, self::$validActions);
+ }
+
+}
\ No newline at end of file
diff --git a/httpdocs/src/EventListener/ControllerListener.php b/httpdocs/src/EventListener/ControllerListener.php
new file mode 100755
index 0000000..149269f
--- /dev/null
+++ b/httpdocs/src/EventListener/ControllerListener.php
@@ -0,0 +1,69 @@
+router = $router;
+ $this->tokenStorage = $tokenStorage;
+ $this->em = $em;
+ }
+
+ /**
+ * On kernel request
+ * @param GetResponseEvent $event
+ */
+ public function onKernelRequest(GetResponseEvent $event)
+ {
+// ini_set("memory_limit","1024M");
+//
+// // NOTE: Increase execution time (be careful)
+// ini_set('max_execution_time', 180);
+
+// $user = $this->tokenStorage->getToken()->getUser();
+// $attributeParams = $event->getRequest()->attributes;
+// $requestParams = $event->getRequest()->request;
+// $route = $attributeParams->get('_route');
+
+ }
+
+ public function onKernelException(GetResponseForExceptionEvent $event)
+ {
+
+ }
+
+ public function onKernelResponse(FilterResponseEvent $event)
+ {
+ // Compress all responsesF
+ ob_start('ob_gzhandler');
+ }
+
+ public function onKernelTerminate(PostResponseEvent $event)
+ {
+ //sleep(60);
+ }
+
+}
\ No newline at end of file
diff --git a/httpdocs/src/EventListener/DatabaseListener.php b/httpdocs/src/EventListener/DatabaseListener.php
new file mode 100644
index 0000000..ca471df
--- /dev/null
+++ b/httpdocs/src/EventListener/DatabaseListener.php
@@ -0,0 +1,72 @@
+tokenStorage = $tokenStorage;
+ }
+
+
+ public function getSubscribedEvents()
+ {
+ return [
+ 'postPersist',
+ 'postUpdate'
+ ];
+ }
+
+ /**
+ * @param LifecycleEventArgs $args
+ * @throws \Doctrine\ORM\ORMException
+ * @throws \Doctrine\ORM\OptimisticLockException
+ */
+ public function postUpdate(LifecycleEventArgs $args)
+ {
+
+ }
+
+ /**
+ * @param LifecycleEventArgs $args
+ * @throws \Doctrine\ORM\ORMException
+ * @throws \Doctrine\ORM\OptimisticLockException
+ */
+ public function postPersist(LifecycleEventArgs $args)
+ {
+
+ }
+
+ /**
+ * Creates a log entry for deleted entity
+ * @param LifecycleEventArgs $args
+ * @throws \Doctrine\ORM\ORMException
+ * @throws \Doctrine\ORM\OptimisticLockException
+ */
+ public function preRemove(LifecycleEventArgs $args)
+ {
+
+ }
+
+}
\ No newline at end of file
diff --git a/httpdocs/src/Kernel.php b/httpdocs/src/Kernel.php
new file mode 100644
index 0000000..779cd1f
--- /dev/null
+++ b/httpdocs/src/Kernel.php
@@ -0,0 +1,11 @@
+em = $em;
+ $this->router = $router;
+ $this->user = null;
+ }
+
+ /**
+ * Called on every request to decide if this authenticator should be
+ * used for the request. Returning false will cause this authenticator
+ * to be skipped.
+ */
+ public function supports(Request $request)
+ {
+ if ($request->getPathInfo() != "/login" || !$request->isMethod('POST')) {
+ return false;
+ }
+
+ return !is_null($request->request->get('email')) &&
+ !is_null($request->request->get('password'));
+ }
+
+ /**
+ * Called on every request. Return whatever credentials you want to
+ * be passed to getUser() as $credentials.
+ */
+ public function getCredentials(Request $request)
+ {
+ return [
+ 'email' => $request->request->get('email'),
+ 'password' => $request->request->get('password'),
+ ];
+ }
+
+ public function getUser($credentials, UserProviderInterface $userProvider)
+ {
+ $email = $credentials['email'];
+ $this->user = $this->em->getRepository('App:EntUser')->findOneBy(['email' => $email]);
+
+ // if a User object, checkCredentials() is called
+ return $this->user;
+ }
+
+ public function checkCredentials($credentials, UserInterface $user)
+ {
+ /** @var $user EntUser */
+ return !is_null($user) && $user->checkPassword($credentials['password']);
+ }
+
+ public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
+ {
+ // on success, let the request continue
+ $data = array('message' => 'logged in successfully',
+ 'isLoggedIn' => self::LOGIN_SUCCESS,
+ 'user' => $this->user->clientMapper($this->em),
+ 'config' => Config::getConfig($this->em),
+ );
+ return new JsonResponse($data, Response::HTTP_ACCEPTED);
+ }
+
+ public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
+ {
+ $data = array(
+ 'message' => strtr($exception->getMessageKey(), $exception->getMessageData()),
+ 'isLoggedIn' => self::LOGIN_FAILED,
+ 'user' => null,
+ 'config' => null,
+
+ // or to translate this message
+ //$this->translator->trans($exception->getMessageKey(), $exception->getMessageData())
+ );
+ return new JsonResponse($data, Response::HTTP_FORBIDDEN);
+ }
+
+ /**
+ * Called when authentication is needed, but it's not sent
+ */
+ public function start(Request $request, AuthenticationException $authException = null)
+ {
+ $data = array(
+ // you might translate this message
+ 'message' => 'Authentication Required'
+ );
+
+ return new JsonResponse($data, Response::HTTP_UNAUTHORIZED);
+ }
+
+ public function supportsRememberMe()
+ {
+ return true;
+ }
+
+
+}
\ No newline at end of file
diff --git a/httpdocs/src/Utils/Config.php b/httpdocs/src/Utils/Config.php
new file mode 100644
index 0000000..db06a40
--- /dev/null
+++ b/httpdocs/src/Utils/Config.php
@@ -0,0 +1,37 @@
+ Utils::clientMap($em, $em->getRepository('App:EntUser')->findBy(['visible' => true])),
+ 'meeting_types' => Utils::clientMap($em, $em->getRepository('App:EntMeetingType')->findAll()),
+ 'countries' => Utils::clientMap($em, $em->getRepository('App:EntCountry')->findAll()),
+ ];
+ }
+
+}
\ No newline at end of file
diff --git a/httpdocs/src/Utils/Excel.php b/httpdocs/src/Utils/Excel.php
new file mode 100644
index 0000000..ec4eddb
--- /dev/null
+++ b/httpdocs/src/Utils/Excel.php
@@ -0,0 +1,530 @@
+ 1,
+ self::EXCEL_BOOLEAN_WORD_FALSE => 1,
+ self::EXCEL_BOOLEAN_TRUE => 1,
+ self::EXCEL_BOOLEAN_FALSE => 1,
+ '0' => 1,
+ '1' => 1,
+ '' => 1
+ ];
+
+ /**
+ * Returns spreadsheet with meta data
+ * @param ContainerInterface $container
+ * @param null $filename
+ * @return Spreadsheet
+ */
+ public static function createSpreadSheet(ContainerInterface $container, $filename = null)
+ {
+ /** @var ExcelFactory $phpSpreadsheet */
+ $phpSpreadSheet = $container->get('phpspreadsheet');
+ /** @var Spreadsheet $spreadSheet */
+ $spreadSheet = $phpSpreadSheet->createSpreadsheet($filename);
+ $spreadSheet->getProperties()->setCreator(self::EXCEL_CREATOR);
+ $spreadSheet->getProperties()->setLastModifiedBy(self::EXCEL_CREATOR);
+ //$phpSpreadSheet->createSheet()
+ return $spreadSheet;
+ }
+
+ /**
+ * Returns an excel writer
+ * @param ContainerInterface $container
+ * @param Spreadsheet $spreadsheet
+ * @return \PhpOffice\PhpSpreadsheet\Writer\IWriter
+ */
+ public static function createWriter(ContainerInterface $container, Spreadsheet $spreadsheet)
+ {
+ /** @var ExcelFactory $phpSpreadsheet */
+ $phpSpreadSheet = $container->get('phpspreadsheet');
+ $writer = $phpSpreadSheet->createWriter($spreadsheet, 'Xlsx');
+ return $writer;
+ }
+
+ /**
+ * Returns active sheet
+ * @param Spreadsheet $spreadSheet
+ * @param null $sheetName
+ * @param null $assetDir
+ * @param int $activeSheetIndex
+ * @return Worksheet
+ * @throws \PhpOffice\PhpSpreadsheet\Exception
+ */
+ public static function getActiveSheet(Spreadsheet $spreadSheet, $sheetName = null, $assetDir = null, $activeSheetIndex = 0)
+ {
+ $spreadSheet->setActiveSheetIndex($activeSheetIndex);
+ $activeSheet = $spreadSheet->getActiveSheet();
+ $activeSheet->setTitle($sheetName);
+ return $activeSheet;
+ }
+
+ /**
+ * Check if given excel value is a valid checkbox value
+ * @param $value
+ * @return bool
+ */
+ private static function isExcelCheckboxValue($value)
+ {
+ return array_key_exists($value, self::$validCheckboxValues);
+ }
+
+ /**
+ * Returns Excel checkbox value
+ * @param Worksheet $sheet
+ * @param $column
+ * @param $row
+ * @return bool
+ * @throws \PhpOffice\PhpSpreadsheet\Exception
+ */
+ public static function getExcelCheckboxValue(Worksheet $sheet, $column, $row)
+ {
+ $value = self::getExcelTextValue($sheet, $column, $row);
+ if (!self::isExcelCheckboxValue($value)) {
+ throw new Exception('invalid check box value');
+ }
+ return $value == "1" || $value == "ja" || $value == "x";
+ }
+
+ /**
+ * Return Excel date value
+ * @param Worksheet $sheet
+ * @param $column
+ * @param $row
+ * @return bool|null|string
+ * @throws \PhpOffice\PhpSpreadsheet\Exception
+ */
+ public static function getExcelDateValue(Worksheet $sheet, $column, $row)
+ {
+ $value = self::getExcelTextValue($sheet, $column, $row);
+ if (!is_null($value)) {
+ $date = null;
+ // Try all valid date formats of too1s
+ $date = \DateTime::createFromFormat("d.m.y", $value);
+ if ($date !== false) {
+ return $date->format('Y-m-d');
+ }
+ $date = \DateTime::createFromFormat("d.m.Y", $value);
+ if ($date !== false) {
+ return $date->format('Y-m-d');
+ }
+ $date = \DateTime::createFromFormat("j.n.y", $value);
+ if ($date !== false) {
+ return $date->format('Y-m-d');
+ }
+ $date = \DateTime::createFromFormat("j.n.Y", $value);
+ if ($date !== false) {
+ return $date->format('Y-m-d');
+ }
+ throw new Exception('invalid date value'); // No valid date
+ }
+ return null;
+ }
+
+ /**
+ * Gets date for excel export (german date format)
+ * @param $value
+ * @return bool|null|string
+ */
+ public static function getExcelExportDate($value)
+ {
+ if (!is_null($value)) {
+ $date = \DateTime::createFromFormat("Y-m-d", $value);
+ if ($date !== false) {
+ return $date->format('d.m.Y');
+ }
+ return false; // No valid date
+ }
+ return null;
+ }
+
+ /**
+ * Returns text value of Excel sheet
+ * @param Worksheet $sheet
+ * @param $column
+ * @param $row
+ * @param bool $stringResult
+ * @return null|string
+ * @throws \PhpOffice\PhpSpreadsheet\Exception
+ */
+ public static function getExcelTextValue(Worksheet $sheet, $column, $row, $stringResult = false)
+ {
+ $column = self::convertColumnToIndex($column);
+ $value = $sheet->getCell($column.$row);
+ return $value == "" ? ($stringResult ? "" : null) : trim($value);
+ }
+
+ /**
+ * Returns integer value of Excel sheet
+ * @param Worksheet $sheet
+ * @param $column
+ * @param $row
+ * @return float|null
+ * @throws \PhpOffice\PhpSpreadsheet\Exception
+ */
+ public static function getExcelIntValue(Worksheet $sheet, $column, $row)
+ {
+ $value = self::getExcelTextValue($sheet, $column, $row);
+ if (!is_null($value)) {
+ $value = intval($value);
+ if (!Utils::isIntValue($value)) {
+ throw new Exception('invalid integer value');
+ }
+ return $value;
+ }
+ return null;
+ }
+
+ /**
+ * Returns float value of Excel sheet
+ * @param Worksheet $sheet
+ * @param $column
+ * @param $row
+ * @return float|null
+ * @throws \PhpOffice\PhpSpreadsheet\Exception
+ */
+ public static function getExcelFloatValue(Worksheet $sheet, $column, $row)
+ {
+ $value = self::getExcelTextValue($sheet, $column, $row);
+ if (!is_null($value)) {
+ if (!Utils::isFloatValue($value)) {
+ throw new Exception('invalid float value');
+ }
+ return Utils::getFloatValue($value);
+ }
+ return null;
+ }
+
+ /**
+ * @param $value
+ * @return string
+ */
+ public static function getBooleanExcelValue($value)
+ {
+ return $value === true ? self::EXCEL_BOOLEAN_TRUE : self::EXCEL_BOOLEAN_FALSE;
+ }
+
+ /**
+ * @param $value
+ * @return string
+ */
+ public static function getBooleanExcelValueWord($value)
+ {
+ return $value === true ? 'x' : '';
+ }
+
+ /**
+ * Converts numeric column index to string column
+ * @param $column
+ * @return string
+ */
+ public static function convertColumnToIndex($column)
+ {
+ return is_numeric($column) ? Coordinate::stringFromColumnIndex($column) : $column;
+ }
+
+ /**
+ * Converts string column index to numeric column index
+ * @param $columnIndex
+ * @return int|string
+ * @throws \PhpOffice\PhpSpreadsheet\Exception
+ */
+ public static function convertIndexToColumn($columnIndex)
+ {
+ return !is_numeric($columnIndex) ? Coordinate::columnIndexFromString($columnIndex) : $columnIndex;
+ }
+
+ /**
+ * Returns calculated value of Excel sheet
+ * @param Worksheet $sheet
+ * @param $column
+ * @param $row
+ * @param bool $stringResult
+ * @return null|string
+ * @throws \PhpOffice\PhpSpreadsheet\Exception
+ */
+ public static function getExcelCalculatedValue(Worksheet $sheet, $column, $row, $stringResult = false)
+ {
+ $column = self::convertColumnToIndex($column);
+ $value = $sheet->getCell($column.$row)->getCalculatedValue();
+ return $value;
+ }
+
+ /**
+ * Sets a column of given worksheet with optional header content and border etc.
+ * @param Worksheet $worksheet
+ * @param $column
+ * @param int $width
+ * @param null $headerContent
+ * @param null $bordersArray
+ * @param int $colRow
+ * @param null $colAlign
+ * @param null $colCellAlign
+ * @param bool $wrapText
+ * @param null $colCellColor
+ * @param null $colCellFontColor
+ * @param bool $bold
+ * @throws \PhpOffice\PhpSpreadsheet\Exception
+ */
+ public static function setColumn(Worksheet $worksheet, $column, $width = 10, $headerContent = null,
+ $bordersArray = null, $colRow = 1, $colAlign = null, $colCellAlign = null,
+ $wrapText = true, $colCellColor = null, $colCellFontColor = null, $bold = false)
+ {
+ $column = self::convertColumnToIndex($column);
+ $worksheet->getColumnDimension($column)->setWidth($width);
+ $cell = $column.$colRow;
+ !is_null($headerContent) ? $worksheet->getCell($cell)->setValue($headerContent) : null;
+ !is_null($bordersArray) ? $worksheet->getStyle($cell)->applyFromArray($bordersArray) : null;
+ !is_null($colAlign) ? $worksheet->getStyle($column.":".$column)->getAlignment()->setHorizontal($colAlign) : null;
+ !is_null($colCellAlign) ? $worksheet->getStyle($cell)->getAlignment()->setHorizontal($colCellAlign) : null;
+ $worksheet->getStyle($cell)->getAlignment()->setWrapText($wrapText);
+
+ !is_null($colCellColor) ? $worksheet->getStyle($cell)->getFill()->setFillType(Fill::FILL_SOLID): null;
+ !is_null($colCellColor) ? $worksheet->getStyle($cell)->getFill()->getStartColor()->setARGB($colCellColor): null;
+ !is_null($colCellFontColor) ? $worksheet->getStyle($cell)->getFont()->getColor()->setARGB($colCellFontColor): null;
+ $worksheet->getStyle($cell)->getFont()->setBold($bold);
+ }
+
+ /**
+ * Sets a cell value of given worksheet
+ * @param Worksheet $worksheet
+ * @param $column
+ * @param $row
+ * @param $value
+ * @param null $cellColor
+ * @param null $cellFontColor
+ * @param bool $bold
+ * @param bool $wrapText
+ * @throws \PhpOffice\PhpSpreadsheet\Exception
+ */
+ public static function setCellValue(Worksheet $worksheet, $column, $row, $value,
+ $cellColor = null, $cellFontColor = null, $bold = false, $wrapText = true)
+ {
+ $column = self::convertColumnToIndex($column);
+ $cell = $column.$row;
+ self::setCellValueSimple($worksheet, $column, $row, $value);
+ !is_null($cellColor) ? $worksheet->getStyle($cell)->getFill()->setFillType(Fill::FILL_SOLID): null;
+ !is_null($cellColor) ? $worksheet->getStyle($cell)->getFill()->getStartColor()->setARGB($cellColor): null;
+ !is_null($cellFontColor) ? $worksheet->getStyle($cell)->getFont()->getColor()->setARGB($cellFontColor): null;
+ $worksheet->getStyle($cell)->getFont()->setBold($bold);
+ $worksheet->getStyle($cell)->getAlignment()->setWrapText($wrapText);
+ }
+
+ /**
+ * Just set value
+ * @param Worksheet $worksheet
+ * @param $column
+ * @param $row
+ * @param $value
+ * @param bool $bAsPlainText
+ * @throws \PhpOffice\PhpSpreadsheet\Exception
+ */
+ public static function setCellValueSimple(Worksheet $worksheet, $column, $row, $value, $bAsPlainText = false)
+ {
+ $column = self::convertColumnToIndex($column);
+ if ($bAsPlainText) {
+ // @TODO: this is a hack to force excel to render value not as number
+ $worksheet->getCell($column.$row)->setValue(strval($value)." ");
+ self::setFormatCode($worksheet, $column, $row, $column, $row, NumberFormat::FORMAT_TEXT);
+ } else {
+ $worksheet->getCell($column.$row)->setValue($value);
+ }
+ }
+
+ /**
+ * Sets cells unlocked (need to implement: $activeSheet->getProtection()->setSheet(true); )
+ * @param Worksheet $worksheet
+ * @param $startCol
+ * @param $startRow
+ * @param $endCol
+ * @param $endRow
+ * @throws \PhpOffice\PhpSpreadsheet\Exception
+ */
+ public static function setUnLocked(Worksheet $worksheet, $startCol, $startRow, $endCol, $endRow)
+ {
+ $startCol = self::convertColumnToIndex($startCol);
+ $endCol = self::convertColumnToIndex($endCol);
+ $startCol = is_numeric($startCol) ? Coordinate::stringFromColumnIndex($startCol) : $startCol;
+ $endCol = is_numeric($endCol) ? Coordinate::stringFromColumnIndex($endCol) : $endCol;
+ $worksheet->getStyle($startCol.$startRow.':'.$endCol.$endRow)->getProtection()->setLocked(Protection::PROTECTION_UNPROTECTED);
+ }
+
+ /**
+ * Sets Conditional
+ * @param Worksheet $worksheet
+ * @param $condition_type
+ * @param $operator_type
+ * @param $condition_value
+ * @param $startCol
+ * @param $startRow
+ * @param null $endCol
+ * @param null $endRow
+ * @param string $background_color
+ * @throws \PhpOffice\PhpSpreadsheet\Exception
+ */
+ public static function setConditional(Worksheet $worksheet, $condition_type, $operator_type, $condition_value,
+ $startCol, $startRow, $endCol = null, $endRow = null, $background_color = Color::COLOR_WHITE)
+ {
+ $startCol = self::convertColumnToIndex($startCol);
+ $endCol = self::convertColumnToIndex($endCol);
+ $conditional1 = new Conditional();
+ $conditional1->setConditionType($condition_type);
+ $conditional1->setOperatorType($operator_type);
+ $conditional1->addCondition($condition_value);
+ $conditional1->getStyle()->getFill()->setFillType(Fill::FILL_SOLID);
+ $conditional1->getStyle()->getFill()->getEndColor()->setARGB($background_color);
+
+ $conditionalStyles = $worksheet->getStyle($startCol.$startRow)->getConditionalStyles();
+ $conditionalStyles[] = $conditional1;
+
+ if (!is_null($endCol) && !is_null($endRow)) {
+ $worksheet->getStyle($startCol.$startRow.':'.$endCol.$endRow)->setConditionalStyles($conditionalStyles);
+ } else {
+ $worksheet->getStyle($startCol.$startRow)->setConditionalStyles($conditionalStyles);
+ }
+ }
+
+ /**
+ * Merges cells of given range
+ * @param Worksheet $worksheet
+ * @param $startCol
+ * @param $endCol
+ * @param $startRow
+ * @param $endRow
+ * @throws \PhpOffice\PhpSpreadsheet\Exception
+ */
+ public static function mergeCells(Worksheet $worksheet, $startCol, $startRow, $endCol, $endRow)
+ {
+ $startCol = self::convertColumnToIndex($startCol);
+ $endCol = self::convertColumnToIndex($endCol);
+ $worksheet->mergeCells($startCol.$startRow.':'.$endCol.$endRow);
+ }
+
+ /**
+ * Unmerges cells of given range
+ * @param Worksheet $worksheet
+ * @param $startCol
+ * @param $startRow
+ * @param $endCol
+ * @param $endRow
+ * @throws \PhpOffice\PhpSpreadsheet\Exception
+ */
+ public static function unMergeCells(Worksheet $worksheet, $startCol, $startRow, $endCol, $endRow)
+ {
+ $startCol = self::convertColumnToIndex($startCol);
+ $endCol = self::convertColumnToIndex($endCol);
+ $worksheet->unmergeCells($startCol.$startRow.':'.$endCol.$endRow);
+ }
+
+ /**
+ * Sets color for cell range
+ * @param Worksheet $worksheet
+ * @param $startCol
+ * @param $startRow
+ * @param $endCol
+ * @param $endRow
+ * @param string $color
+ * @param string $fontColor
+ * @throws \PhpOffice\PhpSpreadsheet\Exception
+ */
+ public static function setRangeColor(Worksheet $worksheet, $startCol, $startRow, $endCol, $endRow,
+ $color = Color::COLOR_WHITE, $fontColor = Color::COLOR_BLACK)
+ {
+ $startCol = self::convertColumnToIndex($startCol);
+ $endCol = self::convertColumnToIndex($endCol);
+ $worksheet->getStyle($startCol.$startRow.':'.$endCol.$endRow)->getFill()->setFillType(Fill::FILL_SOLID);
+ $worksheet->getStyle($startCol.$startRow.':'.$endCol.$endRow)->getFill()->getStartColor()->setARGB($color);
+ $worksheet->getStyle($startCol.$startRow.':'.$endCol.$endRow)->getFont()->getColor()->setARGB($fontColor);
+ }
+
+ /**
+ * Sets range border
+ * @param Worksheet $worksheet
+ * @param $startCol
+ * @param $startRow
+ * @param $endCol
+ * @param $endRow
+ * @param $bordersArray
+ * @throws \PhpOffice\PhpSpreadsheet\Exception
+ */
+ public static function setRangeBorder(Worksheet $worksheet, $startCol, $startRow, $endCol, $endRow, $bordersArray)
+ {
+ $startCol = self::convertColumnToIndex($startCol);
+ $endCol = self::convertColumnToIndex($endCol);
+ $worksheet->getStyle($startCol.$startRow.':'.$endCol.$endRow)->applyFromArray($bordersArray);
+ }
+
+ /**
+ * Sets format code
+ * @param Worksheet $worksheet
+ * @param $startCol
+ * @param $startRow
+ * @param $endCol
+ * @param $endRow
+ * @param $formatCode
+ * @throws \PhpOffice\PhpSpreadsheet\Exception
+ */
+ public static function setFormatCode(Worksheet $worksheet, $startCol, $startRow, $endCol, $endRow, $formatCode)
+ {
+ $startCol = self::convertColumnToIndex($startCol);
+ $endCol = self::convertColumnToIndex($endCol);
+ $worksheet->getStyle($startCol.$startRow.':'.$endCol.$endRow)->getNumberFormat()->setFormatCode($formatCode);
+ }
+
+ /**
+ * Sets styles from style array
+ * @param Worksheet $worksheet
+ * @param $startCol
+ * @param $startRow
+ * @param $endCol
+ * @param $endRow
+ * @param $styleArray
+ * @throws \PhpOffice\PhpSpreadsheet\Exception
+ */
+ public static function setStyleArray(Worksheet $worksheet, $startCol, $startRow, $endCol, $endRow, $styleArray)
+ {
+ $startCol = self::convertColumnToIndex($startCol);
+ $endCol = self::convertColumnToIndex($endCol);
+ $worksheet->getStyle($startCol.$startRow.':'.$endCol.$endRow)->applyFromArray($styleArray);
+ }
+}
\ No newline at end of file
diff --git a/httpdocs/src/Utils/ExcelStyle.php b/httpdocs/src/Utils/ExcelStyle.php
new file mode 100644
index 0000000..2f5ddbe
--- /dev/null
+++ b/httpdocs/src/Utils/ExcelStyle.php
@@ -0,0 +1,139 @@
+ [
+ 'horizontal' => Alignment::HORIZONTAL_CENTER,
+ 'vertical' => Alignment::VERTICAL_CENTER,
+ 'wrapText' => true
+ ],
+ 'font' => [
+ 'size' => 11,
+ 'name' => 'Arial'
+ ],
+ ];
+
+ // Font styles for cadaster excels
+ public static $CADASTER_FONT = [
+ 'font' => [
+ 'size' => 11,
+ 'name' => 'Arial'
+ ],
+ ];
+
+ // Bold style
+ public static $FONT_BOLD = [
+ 'font' => [
+ 'bold' => true
+ ],
+ ];
+
+ // Font align right
+ public static $ALIGN_LEFT = [
+ 'alignment' => [
+ 'horizontal' => Alignment::HORIZONTAL_LEFT,
+ 'vertical' => Alignment::VERTICAL_CENTER,
+ 'wrapText' => true
+ ]
+ ];
+
+ // Font align right
+ public static $ALIGN_RIGHT = [
+ 'alignment' => [
+ 'horizontal' => Alignment::HORIZONTAL_RIGHT,
+ 'vertical' => Alignment::VERTICAL_CENTER,
+ 'wrapText' => true
+ ]
+ ];
+
+ // Font align center
+ public static $ALIGN_CENTER = [
+ 'alignment' => [
+ 'horizontal' => Alignment::HORIZONTAL_CENTER,
+ 'vertical' => Alignment::VERTICAL_CENTER,
+ 'wrapText' => true
+ ]
+ ];
+
+ // Borders for cadaster excels
+ public static $CADASTER_BORDERS = [
+ 'borders' => [
+ 'top' => [
+ 'borderStyle' => Border::BORDER_THIN,
+ 'color' => array('argb' => 'FF000000'),
+ ],
+ 'bottom' => [
+ 'borderStyle' => Border::BORDER_THIN,
+ 'color' => array('argb' => 'FF000000'),
+ ],
+ 'left' => [
+ 'borderStyle' => Border::BORDER_THIN,
+ 'color' => array('argb' => 'FF000000'),
+ ],
+ 'right' => [
+ 'borderStyle' => Border::BORDER_THIN,
+ 'color' => array('argb' => 'FF000000'),
+ ],
+ ],
+ ];
+
+ // Medium borders for cadaster excels
+ public static $CADASTER_BORDERS_MEDIUM = [
+ 'borders' => [
+ 'allBorders' => [
+ 'borderStyle' => Border::BORDER_THIN,
+ 'color' => array('argb' => 'FF000000'),
+ ],
+ 'top' => [
+ 'borderStyle' => Border::BORDER_MEDIUM,
+ 'color' => array('argb' => 'FF000000'),
+ ],
+ 'bottom' => [
+ 'borderStyle' => Border::BORDER_MEDIUM,
+ 'color' => array('argb' => 'FF000000'),
+ ],
+ 'left' => [
+ 'borderStyle' => Border::BORDER_MEDIUM,
+ 'color' => array('argb' => 'FF000000'),
+ ],
+ 'right' => [
+ 'borderStyle' => Border::BORDER_MEDIUM,
+ 'color' => array('argb' => 'FF000000'),
+ ],
+ ],
+ ];
+
+ // Dashed border
+ public static $DASHED_BORDERS = [
+ 'borders' => [
+ 'bottom' => [
+ 'borderStyle' => Border::BORDER_MEDIUMDASHED,
+ 'color' => array('argb' => 'FF000000'),
+ ]
+ ],
+ ];
+}
diff --git a/httpdocs/src/Utils/Message.php b/httpdocs/src/Utils/Message.php
new file mode 100644
index 0000000..1182754
--- /dev/null
+++ b/httpdocs/src/Utils/Message.php
@@ -0,0 +1,209 @@
+ "Es ist ein Fehler aufgetreten",
+ self::ERROR_INVALID_DATA => "Notwendige Daten fehlen oder sind ungültig",
+ self::ERROR_NON_EXISTING_DATA => "Die von Ihnen bearbeiteten Daten existieren nicht mehr. Bitte aktualisieren Sie die Seite.",
+ self::ERROR_REPORT_NOT_EDITABLE_YET => "Der Report kann erst nach Start des Termins bearbeitet werden.",
+ self::ERROR_MEETING_NOT_EDIT_DELETABLE => "Ein Termin kann nach Beginn nicht mehr verändert bzw. gelöscht werden.",
+ self::ERROR_NOT_ENOUGH_RIGHTS => "Sie sind nicht der Ersteller / Verantwortlicher für diesen Datensatz oder verfügen nicht über entsprechenden Recht Änderungen vorzunehmen.",
+ // Admin errors
+
+ ];
+
+ // Success messages
+ public static $successMessages = [
+ self::SUCCESS_CUSTOMER_CREATE => "Kunde wurde erfolgreich angelegt.",
+ self::SUCCESS_CUSTOMER_EDIT => "Kunde wurde erfolgreich geändert.",
+ self::SUCCESS_CUSTOMER_CONTACT_CREATE => "Ansprechpartner von Kunde wurde erfolgreich angelegt.",
+ self::SUCCESS_CUSTOMER_CONTACT_EDIT => "Ansprechpartner von Kunde wurde erfolgreich geändert.",
+ self::SUCCESS_CUSTOMER_CONTACT_DELETE => "Ansprechpartner von Kunde wurde erfolgreich gelöscht.",
+ self::SUCCESS_CUSTOMER_MEETING_CREATE => "Termin mit Kunde wurde erfolgreich angelegt.",
+ self::SUCCESS_CUSTOMER_MEETING_EDIT => "Termin mit Kunde wurde erfolgreich geändert.",
+ self::SUCCESS_CUSTOMER_MEETING_DELETE => "Termin mit Kunde wurde erfolgreich gelöscht.",
+ self::SUCCESS_CUSTOMER_MEETING_REPORT_SET => "Report für Kundenmeeting wurde erfolgreich bearbeitet.",
+ self::SUCCESS_CUSTOMER_NOTE_CREATE => "Notiz für Kunde wurde erfolgreich angelegt.",
+ self::SUCCESS_CUSTOMER_NOTE_EDIT => "Notiz für Kunde wurde erfolgreich geändert.",
+ self::SUCCESS_CUSTOMER_NOTE_DELETE => "Notiz für Kunde wurde erfolgreich gelöscht.",
+ self::SUCCESS_INTERNAL_MEETING_CREATE => "Interner Termin wurde erfolgreich angelegt.",
+ self::SUCCESS_INTERNAL_MEETING_EDIT => "Interner Termin wurde erfolgreich geändert.",
+ self::SUCCESS_INTERNAL_MEETING_DELETE => "Interner Termin wurde erfolgreich gelöscht.",
+ self::SUCCESS_INTERNAL_MEETING_REPORT_SET => "Report für internen Termin wurde erfolgreich bearbeitet.",
+ self::SUCCESS_OPERATOR_CREATE => "Betreiber wurde erfolgreich angelegt.",
+ self::SUCCESS_OPERATOR_EDIT => "Betreiber wurde erfolgreich geändert.",
+ self::SUCCESS_OPERATOR_CONTACT_CREATE => "Ansprechpartner von Betreiber wurde erfolgreich angelegt.",
+ self::SUCCESS_OPERATOR_CONTACT_EDIT => "Ansprechpartner von Betreiber wurde erfolgreich geändert.",
+ self::SUCCESS_OPERATOR_CONTACT_DELETE => "Ansprechpartner von Betreiber wurde erfolgreich gelöscht.",
+ self::SUCCESS_OPERATOR_MEETING_CREATE => "Termin mit Betreiber wurde erfolgreich angelegt.",
+ self::SUCCESS_OPERATOR_MEETING_EDIT => "Termin mit Betreiber wurde erfolgreich geändert.",
+ self::SUCCESS_OPERATOR_MEETING_DELETE => "Termin mit Betreiber wurde erfolgreich gelöscht.",
+ self::SUCCESS_OPERATOR_MEETING_REPORT_SET => "Report für Termin mit Betreiber wurde erfolgreich bearbeitet.",
+ self::SUCCESS_OPERATOR_NOTE_CREATE => "Notiz für Betreiber wurde erfolgreich angelegt.",
+ self::SUCCESS_OPERATOR_NOTE_EDIT => "Notiz für Betreiber wurde erfolgreich geändert.",
+ self::SUCCESS_OPERATOR_NOTE_DELETE => "Notiz für Betreiber wurde erfolgreich gelöscht.",
+ self::SUCCESS_PRODUCTION_CREATE => "Produzent wurde erfolgreich angelegt.",
+ self::SUCCESS_PRODUCTION_EDIT => "Produzent wurde erfolgreich geändert.",
+ self::SUCCESS_PRODUCTION_CONTACT_CREATE => "Ansprechpartner von Produzent wurde erfolgreich angelegt.",
+ self::SUCCESS_PRODUCTION_CONTACT_EDIT => "Ansprechpartner von Produzent wurde erfolgreich geändert.",
+ self::SUCCESS_PRODUCTION_CONTACT_DELETE => "Ansprechpartner von Produzent wurde erfolgreich gelöscht.",
+ self::SUCCESS_PRODUCTION_MEETING_CREATE => "Termin mit Produzent wurde erfolgreich angelegt.",
+ self::SUCCESS_PRODUCTION_MEETING_EDIT => "Termin mit Produzent wurde erfolgreich geändert.",
+ self::SUCCESS_PRODUCTION_MEETING_DELETE => "Termin mit Produzent wurde erfolgreich gelöscht.",
+ self::SUCCESS_PRODUCTION_MEETING_REPORT_SET => "Report für Termin mit Produzent wurde erfolgreich bearbeitet.",
+ self::SUCCESS_PRODUCTION_NOTE_CREATE => "Notiz für Produzent wurde erfolgreich angelegt.",
+ self::SUCCESS_PRODUCTION_NOTE_EDIT => "Notiz für Produzent wurde erfolgreich geändert.",
+ self::SUCCESS_PRODUCTION_NOTE_DELETE => "Notiz für Produzent wurde erfolgreich gelöscht.",
+ self::SUCCESS_SERVICE_CREATE => "Service-Dienstleister wurde erfolgreich angelegt.",
+ self::SUCCESS_SERVICE_EDIT => "Service-Dienstleister wurde erfolgreich geändert.",
+ self::SUCCESS_SERVICE_CONTACT_CREATE => "Ansprechpartner von Service-Dienstleister wurde erfolgreich angelegt.",
+ self::SUCCESS_SERVICE_CONTACT_EDIT => "Ansprechpartner von Service-Dienstleister wurde erfolgreich geändert.",
+ self::SUCCESS_SERVICE_CONTACT_DELETE => "Ansprechpartner von Service-Dienstleister wurde erfolgreich gelöscht.",
+ self::SUCCESS_SERVICE_MEETING_CREATE => "Termin mit Service-Dienstleister wurde erfolgreich angelegt.",
+ self::SUCCESS_SERVICE_MEETING_EDIT => "Termin mit Service-Dienstleister wurde erfolgreich geändert.",
+ self::SUCCESS_SERVICE_MEETING_DELETE => "Termin mit Service-Dienstleister wurde erfolgreich gelöscht.",
+ self::SUCCESS_SERVICE_MEETING_REPORT_SET => "Report für Termin mit Service-Dienstleister wurde erfolgreich bearbeitet.",
+ self::SUCCESS_SERVICE_NOTE_CREATE => "Notiz für Service-Dienstleister wurde erfolgreich angelegt.",
+ self::SUCCESS_SERVICE_NOTE_EDIT => "Notiz für Service-Dienstleister wurde erfolgreich geändert.",
+ self::SUCCESS_SERVICE_NOTE_DELETE => "Notiz für Service-Dienstleister wurde erfolgreich gelöscht.",
+ ];
+ // Error messages
+
+ // Info messages
+ public static $infoMessages = [
+ self::INFO_DATA_UNKNOWN => "Ein oder mehrere gefundene(r) Wert(e) sind unbekannt oder fehlerhaft und wurden zurückgesetzt. Bitte überprüfen Sie Ihre Eingaben.",
+ ];
+
+ /**
+ * Returns success message
+ * @param null $successCode
+ * @return mixed|string
+ */
+ public static function getSuccessMessage($successCode = null)
+ {
+ if ($successCode == self::SUCCESS_NONE) {
+ return null;
+ }
+ if (is_null($successCode) || !array_key_exists($successCode, self::$successMessages)) {
+ return self::SUCCESS_DEFAULT;
+ } else {
+ return self::$successMessages[$successCode];
+ }
+ }
+
+ /**
+ * Returns error message
+ * @param null $errorCode
+ * @return mixed|string
+ */
+ public static function getErrorMessage($errorCode = null)
+ {
+ if (is_null($errorCode) || !array_key_exists($errorCode, self::$errorMessages)) {
+ return self::ERROR_DEFAULT;
+ } else {
+ return self::$errorMessages[$errorCode];
+ }
+ }
+
+ /**
+ * Returns info message
+ * @param null $infoCode
+ * @return mixed|string
+ */
+ public static function getInfoMessage($infoCode = null)
+ {
+ if ($infoCode == self::INFO_NONE) {
+ return null;
+ }
+ if (is_null($infoCode) || !array_key_exists($infoCode, self::$infoMessages)) {
+ return self::INFO_DEFAULT;
+ } else {
+ return self::$infoMessages[$infoCode];
+ }
+ }
+}
\ No newline at end of file
diff --git a/httpdocs/src/Utils/Reply.php b/httpdocs/src/Utils/Reply.php
new file mode 100644
index 0000000..0bd1a78
--- /dev/null
+++ b/httpdocs/src/Utils/Reply.php
@@ -0,0 +1,185 @@
+ $result,
+ 'success_code' => $success,
+ 'success_msg' => Message::getSuccessMessage($success),
+ 'info_code' => $info,
+ 'info_msg' => Message::getInfoMessage($info),
+ 'service_data' => !is_null($serviceData) ? $serviceData->getServiceData() : null,
+ ];
+ return new JsonResponse($res);
+ }
+
+ /**
+ * Returns error response according to error code
+ * @param string $error
+ * @param null $optionalInfo
+ * @return JsonResponse
+ */
+ public static function getErrorResponse($error = null, $optionalInfo = null)
+ {
+ $res = [
+ 'error_code' => $error,
+ 'error_msg' => Message::getErrorMessage($error),
+ 'optional_info' => $optionalInfo
+ ];
+
+ return new JsonResponse($res, 419);
+ }
+
+ /**
+ * Returns file as response
+ * @param $filePath
+ * @param $fileName
+ * @return Response
+ */
+ public static function getFileResponse($filePath, $fileName, $zipped = false)
+ {
+ $response = new Response(file_get_contents($filePath . $fileName));
+ if ($zipped) {
+ $response->headers->set('Content-Type', 'application/zip');
+ } else {
+ $response->headers->set('Content-Type', 'application/octet-stream');
+ }
+ $response->headers->set('Content-Disposition', 'attachment;filename="' . $fileName . '"');
+ $response->headers->set('Content-Transfer-Encoding','binary');
+ $response->headers->set('Expires', '0');
+ $response->headers->set('Cache-Control', 'must-revalidate, post-check=0, pre-check=0');
+ $response->headers->set('Pragma', 'public');
+ $response->headers->set('Content-length', filesize($filePath . $fileName));
+
+ return $response;
+ }
+
+ /**
+ * Creates a file response with temporary file (not in filesystem)
+ * @param $fileObject
+ * @param string $fileName
+ * @return Response
+ */
+ public static function getSteamedFileResponse($fileObject, $fileName = "")
+ {
+ $response = new Response($fileObject);
+ $response->headers->set('Content-Type', 'application/octet-stream');
+ $response->headers->set('Content-Disposition', 'attachment;filename="' . $fileName . '"');
+ $response->headers->set('Content-Transfer-Encoding','binary');
+ $response->headers->set('Expires', '0');
+ $response->headers->set('Cache-Control', 'must-revalidate, post-check=0, pre-check=0');
+ $response->headers->set('Pragma', 'public');
+ return $response;
+ }
+
+ /**
+ * @param Spreadsheet $phpSpreadsheet
+ * @param Xlsx $writer
+ * @param $filePath
+ * @param $fileName
+ * @param $zipped
+ * @param $fileNameWithDate
+ * @return Response|StreamedResponse
+ * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
+ */
+ public static function getExcelResponse(Spreadsheet $phpSpreadsheet, Xlsx $writer, $filePath, $fileName, $zipped = false, $fileNameWithDate = true)
+ {
+ if (!file_exists($filePath)) {
+ mkdir($filePath, 0777, true);
+ }
+
+ $date = date('Y-m-d');
+ $outputFilename = ($fileNameWithDate ? $date : "") . '_' . $fileName . '.xlsx';
+ if (!$zipped) {
+ // Just plain excel file
+ $headers = array(
+ 'Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet; charset=utf-8',
+ 'Content-Disposition' => 'attachment; filename=' . $outputFilename,
+ 'Content-Description' => 'File Transfer',
+ 'Pragma' => 'public',
+ 'Expires' => '0',
+ 'Accept-Ranges' => 'bytes',
+ 'Content-Transfer-Encoding' => 'binary',
+ 'Cache-Control' => 'maxage=1'
+ );
+ // Return excel as blob file response
+ $response = new StreamedResponse(
+ function () use ($writer) {
+ $writer->save('php://output');
+ },
+ 200,
+ $headers
+ );
+
+ } else {
+ // Return zip file
+ // Save file temporarily
+ $writer->save($filePath . $outputFilename);
+
+ $files[] = [
+ 'filePath' => $filePath,
+ 'fileName' => $outputFilename
+ ];
+
+ $response = self::getZippedFileResponse($filePath, $outputFilename, $files);
+
+ // Delete file
+ unlink($filePath . $outputFilename);
+ }
+ return $response;
+ }
+
+ /**
+ * Returns zipped response of given files
+ * @param $zipFilePath
+ * @param $zipFileName
+ * @param $files
+ * @param bool $deleteZip
+ * @param bool $deleteFiles
+ * @return Response
+ */
+ public static function getZippedFileResponse($zipFilePath, $zipFileName, $files, $deleteZip = false, $deleteFiles = false)
+ {
+ $resZipFileName = Utils::createZipFile($zipFilePath, $zipFileName, $files);
+ $response = self::getFileResponse($zipFilePath, $resZipFileName);
+
+ if ($deleteZip) {
+ unlink($zipFilePath . $resZipFileName);
+ }
+
+ if ($deleteFiles) {
+ foreach ($files as $file2) {
+ unlink($file2['filePath'] . $file2['fileName']);
+ }
+ }
+ return $response;
+ }
+
+
+}
\ No newline at end of file
diff --git a/httpdocs/src/Utils/Utils.php b/httpdocs/src/Utils/Utils.php
new file mode 100644
index 0000000..ac35958
--- /dev/null
+++ b/httpdocs/src/Utils/Utils.php
@@ -0,0 +1,610 @@
+getRepository('App:EntUser')->findOneBy(['email' => $email]);
+ return !is_null($existingUserEmail);
+ }
+
+ /**
+ * Validates email and checks existence
+ * @param ObjectManager $em
+ * @param $email
+ * @return bool
+ */
+ public static function validateEmail(ObjectManager $em, $email)
+ {
+ return self::isValidEmail($email) && !self::emailUserExists($em, $email);
+ }
+
+ /**
+ * Checks if uploaded file is an excel file
+ * @param UploadedFile $uploadedFile
+ * @return bool
+ */
+ public static function isExcelFile(UploadedFile $uploadedFile)
+ {
+ // Check client mime type
+ return $uploadedFile->getClientMimeType() == 'application/vnd.ms-excel' ||
+ $uploadedFile->getClientMimeType() == 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
+ }
+
+ /**
+ * Checks length of password
+ * @param $password
+ * @return bool
+ */
+ public static function isValidPasswordLength($password)
+ {
+ return strlen($password) >= self::PASSWORD_MIN_LENGTH &&
+ strlen($password) <= self::PASSWORD_MAX_LENGTH;
+ }
+
+ /**
+ * Returns sorted array by getter name
+ * @param $getterName
+ * @param $arr
+ * @return array
+ */
+ public static function getSortedObjects($getterName, $arr)
+ {
+ $res = [];
+ foreach ($arr as $item) {
+ $res[$item->{$getterName}()] = $item;
+ }
+ return $res;
+ }
+
+ /**
+ * Returns sorted array by getter name
+ * @param $arr
+ * @return array
+ */
+ public static function getSortedObjectsById($arr)
+ {
+ return self::getSortedObjects('getId', $arr);
+ }
+
+ /**
+ * Returns sorted array by getter name
+ * @param $firstGetterName
+ * @param $secondGetterName
+ * @param $arr
+ * @return array
+ */
+ public static function getSortedMultipleObjects($firstGetterName, $secondGetterName, $arr)
+ {
+ $res = [];
+ foreach ($arr as $item) {
+ $res[$item->{$firstGetterName}()][$item->{$secondGetterName}()] = $item;
+ }
+ return $res;
+ }
+
+ /**
+ * Returns sorted array by key
+ * @param $key
+ * @param $arr
+ * @return array
+ */
+ public static function getSortedArray($key, $arr)
+ {
+ $res = [];
+ foreach ($arr as $item) {
+ $res[$item[$key]] = $item;
+ }
+ return $res;
+ }
+
+ /**
+ * Returns inverted array (values are keys)
+ * @param $arr
+ * @return array
+ */
+ public static function getInvertedArray($arr)
+ {
+ $res = [];
+ foreach ($arr as $key => $value) {
+ $res[$value] = $key;
+ }
+ return $res;
+ }
+
+ public static function getGermanDateTime(\DateTime $dateTime = null)
+ {
+ if (is_null($dateTime)) {
+ $dateTime = new \DateTime();
+ }
+ $localTimeZone = new \DateTimeZone('Europe/Berlin');
+ $dateTime->setTimezone($localTimeZone);
+ return $dateTime;
+ }
+
+ /**
+ * Returns array of client mappable items
+ * @param ObjectManager $em
+ * @param $arr
+ * @param bool $fullMapping
+ * @return array
+ */
+ public static function clientMap(ObjectManager $em, $arr, $fullMapping = false)
+ {
+ $res = [];
+ foreach ($arr as $key => $value) {
+
+ if (!$value instanceof IEntity) {
+ throw new Exception("Items must implement IEntity");
+ }
+ $res[] = $value->clientMapper($em, $fullMapping);
+
+ }
+ return $res;
+ }
+
+ /**
+ * Returns array with file path info
+ * @param $filePath
+ * @param $fileName
+ * @return array
+ */
+ public static function getFilePathArray($filePath, $fileName)
+ {
+ return [
+ 'filePath' => $filePath,
+ 'fileName' => $fileName
+ ];
+ }
+
+ /**
+ * Creates a zip file of given files
+ * @param $zipFilePath
+ * @param $zipFileName
+ * @param $files
+ * @return string
+ */
+ public static function createZipFile($zipFilePath, $zipFileName, $files)
+ {
+ $zip = new \ZipArchive();
+ $zipFileName .= ".zip";
+
+ $zip->open($zipFilePath . $zipFileName, \ZipArchive::CREATE);
+ foreach ($files as $file) {
+ $zip->addFromString(basename($file['filePath'] . $file['fileName']), file_get_contents($file['filePath'] . $file['fileName']));
+ }
+ $zip->close();
+ return $zipFileName;
+ }
+
+ /**
+ * Creates a zip file from folder and optionally excluded files
+ * @param $zipFilePath
+ * @param $zipFileName
+ * @param array $skippedFileNames
+ * @param bool $includeZipPathFolder
+ */
+ public static function createZipFileFromFolder($zipFilePath, $zipFileName, $skippedFileNames = [], $includeZipPathFolder = false)
+ {
+ $skippedFileNamesByName = Utils::getInvertedArray($skippedFileNames);
+
+ // Initialize archive object
+ $zip = new \ZipArchive();
+ $zip->open($zipFilePath.$zipFileName.'.zip', \ZipArchive::CREATE | \ZipArchive::OVERWRITE);
+
+ // Create recursive directory iterator
+ /** @var \SplFileInfo[] $files */
+ $files = new \RecursiveIteratorIterator(
+ new \RecursiveDirectoryIterator($zipFilePath),
+ \RecursiveIteratorIterator::LEAVES_ONLY
+ );
+
+ $folderSuffix = "";
+ if ($includeZipPathFolder) {
+ $zipFilePathParts = explode('/', $zipFilePath);
+ $folderSuffix = $zipFilePathParts[count($zipFilePathParts)-2]."/";
+ }
+
+ foreach ($files as $name => $file)
+ {
+ // Skip directories (they would be added automatically)
+ if (!$file->isDir())
+ {
+ // Get real and relative path for current file
+ $filePath = $file->getRealPath();
+ $relativePath = substr($filePath, strlen($zipFilePath));
+ $relativePathParts = explode('/', $relativePath);
+ $fileName = $relativePathParts[count($relativePathParts)-1];
+
+ if (!array_key_exists($fileName, $skippedFileNamesByName)) {
+ // Add current file to archive
+ $zip->addFile($filePath, $folderSuffix.$relativePath);
+ }
+ }
+ }
+
+ // Zip archive will be created only after closing object
+ $zip->close();
+ }
+
+ /**
+ * Checks file size of uploaded file
+ */
+ public static function checkUploadedFileSize()
+ {
+ foreach ($_FILES as $filename => $file) {
+ if ($file['error'] == 1) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Checks if given file has valid image format
+ * @param UploadedFile $imgFile
+ * @return bool
+ */
+ public static function isValidImageFile(UploadedFile $imgFile)
+ {
+ return in_array(strtolower($imgFile->getClientOriginalExtension()), self::$validImageFormats);
+ }
+
+ /**
+ * @param UploadedFile $imgFile
+ * @param $path
+ * @param $filename
+ * @param $assetDir
+ */
+ public static function saveResizedImage(UploadedFile $imgFile, $path, $filename, $assetDir)
+ {
+ if (!self::isValidImageFile($imgFile)) {
+ throw new Exception('invalid image file format');
+ }
+
+ // Save file temporarily
+ $tmpFilePath = $assetDir. '/tmp/';
+ $imgFile->move($tmpFilePath, $filename);
+
+ // Calc new dimension
+ list($width, $height) = getimagesize($tmpFilePath.$filename);
+ $ratio = $width / $height;
+
+ $newWidth = self::MAX_IMAGE_WIDTH;
+ $newHeight = $newWidth / $ratio;
+
+ $source = null;
+ $thumb = imagecreatetruecolor($newWidth, $newHeight);
+ switch (strtolower($imgFile->getClientOriginalExtension())) {
+ case self::IMAGE_FORMAT_JPG:
+ case self::IMAGE_FORMAT_JPEG:
+ $source = imagecreatefromjpeg($tmpFilePath.$filename);
+ imagecopyresized($thumb, $source, 0, 0, 0, 0, $newWidth, $newHeight, $width, $height);
+ imagejpeg($thumb);
+ break;
+ case self::IMAGE_FORMAT_PNG:
+ $source = imagecreatefrompng($tmpFilePath.$filename);
+ imagecopyresized($thumb, $source, 0, 0, 0, 0, $newWidth, $newHeight, $width, $height);
+ imagepng($thumb, $path.$filename);
+ break;
+ case self::IMAGE_FORMAT_GIF:
+ $source = imagecreatefromgif($tmpFilePath.$filename);
+ imagecopyresized($thumb, $source, 0, 0, 0, 0, $newWidth, $newHeight, $width, $height);
+ break;
+ case self::IMAGE_FORMAT_BMP:
+ $source = imagecreatefromwbmp($tmpFilePath.$filename);
+ imagecopyresized($thumb, $source, 0, 0, 0, 0, $newWidth, $newHeight, $width, $height);
+ break;
+ default:
+ throw new Exception("Invalid file type");
+ }
+
+ // Remove temporary file
+ unlink($tmpFilePath.$filename);
+ }
+
+ /**
+ * Copies a folder recursively
+ * @param $sourcPath
+ * @param $destinationPath
+ */
+ public static function copyFolderRecursively($sourcPath, $destinationPath)
+ {
+ $dir = opendir($sourcPath);
+ @mkdir($destinationPath);
+ while(false !== ( $file = readdir($dir)) ) {
+ if (( $file != '.' ) && ( $file != '..' )) {
+ if ( is_dir($sourcPath . '/' . $file) ) {
+ self::copyFolderRecursively($sourcPath . '/' . $file,$destinationPath . '/' . $file);
+ }
+ else {
+ copy($sourcPath . '/' . $file,$destinationPath . '/' . $file);
+ }
+ }
+ }
+ closedir($dir);
+ }
+
+ /**
+ * Deletes directory with contained files
+ * @param $dirPath
+ */
+ public static function deleteDirectory($dirPath) {
+ $dir = opendir($dirPath);
+ while(false !== ( $file = readdir($dir)) ) {
+ if (( $file != '.' ) && ( $file != '..' )) {
+ $full = $dirPath . '/' . $file;
+ if ( is_dir($full) ) {
+ self::deleteDirectory($full);
+ }
+ else {
+ unlink($full);
+ }
+ }
+ }
+ closedir($dir);
+ rmdir($dirPath);
+ }
+
+ /**
+ * Checks if a process is already running
+ * @param $processName
+ * @return bool
+ */
+ public static function checkOnRunningProcess($processName)
+ {
+ $processCount = 0;
+ exec("ps aux | grep -i ".$processName, $pids);
+ foreach ($pids as $pid) {
+ if (strpos($pid, "bin/console ".$processName) !== false) {
+ $processCount++;
+ }
+ }
+ // If process is running, there will be two instances (running process and the one that just started!)
+ return $processCount > 1;
+ }
+
+ /**
+ * Returns gender value from translated gender
+ * @param $gender
+ * @return string
+ */
+ public static function getGenderFromTranslatedGender($gender)
+ {
+ switch ($gender) {
+ case Utils::GENDER_MALE_TRANSLATED:
+ return Utils::GENDER_MALE;
+ break;
+ case Utils::GENDER_FEMALE_TRANSLATED:
+ return Utils::GENDER_FEMALE;
+ break;
+ default:
+ return Utils::GENDER_DIVERSE;
+ }
+ }
+
+ /**
+ * Returns translated gender
+ * @param $gender
+ * @return string
+ */
+ public static function getTranslatedGender($gender)
+ {
+ switch ($gender) {
+ case Utils::GENDER_MALE:
+ return Utils::GENDER_MALE_TRANSLATED;
+ break;
+ case Utils::GENDER_FEMALE:
+ return Utils::GENDER_FEMALE_TRANSLATED;
+ break;
+ default:
+ return Utils::GENDER_DIVERSE_TRANSLATED;
+ }
+ }
+
+ /**
+ * Sends mail depending on environment
+ * @param MailerInterface $mailer
+ * @param $subject
+ * @param $emailAddress
+ * @param $body
+ * @param $environment
+ */
+ public static function sendMail(MailerInterface $mailer, $subject, $emailAddress, $body, $environment)
+ {
+ $sendFrom = Utils::SYS_EMAIL;
+ if ($environment === Utils::ENV_DEV || $environment === Utils::ENV_BETA) {
+ $emailAddress = self::ADMIN_EMAIL_BETA;
+ $sendFrom = $environment == Utils::ENV_DEV ? self::ADMIN_EMAIL_BETA : self::SYS_EMAIL_BETA;
+ }
+
+ // Send Email to target from admin / sys mail
+ $email = (new Email())
+ ->from($sendFrom)
+ ->to($emailAddress)
+ //->cc('cc@example.com')
+ //->bcc('bcc@example.com')
+ //->replyTo('fabien@example.com')
+ //->priority(Email::PRIORITY_HIGH)
+ ->subject($subject)
+ ->text('Sending emails is fun again!')
+ ->html($body);
+
+ $mailer->send($email);
+ }
+
+ /**
+ * Returns db connection to old database depending on environment
+ * @param $environment
+ * @return \Doctrine\DBAL\Connection
+ * @throws \Doctrine\DBAL\DBALException
+ */
+ public static function getOldDbConnection($environment)
+ {
+ $dbConnectionParams = null;
+ switch($environment) {
+ case self::ENV_DEV:
+ $dbConnectionParams = array(
+ 'dbname' => 'plpp',
+ 'user' => 'root',
+ 'password' => '',
+ 'host' => '127.0.0.1',
+ 'driver' => 'pdo_mysql',
+ );
+ break;
+ case self::ENV_BETA:
+ case self::ENV_GAMMA:
+ $dbConnectionParams = array(
+ 'dbname' => 'plpp',
+ 'user' => 'plp',
+ 'password' => 'Ba06TH%dG,7f',
+ 'host' => '127.0.0.1',
+ 'driver' => 'pdo_mysql',
+ );
+ break;
+ default:
+ throw new Exception('unknown environment');
+ }
+ $dbHandle = DriverManager::getConnection($dbConnectionParams);
+ $dbHandle->query("SET NAMES 'utf8'");
+ return $dbHandle;
+ }
+}
\ No newline at end of file
diff --git a/httpdocs/symfony.lock b/httpdocs/symfony.lock
new file mode 100644
index 0000000..3fbc0fc
--- /dev/null
+++ b/httpdocs/symfony.lock
@@ -0,0 +1,130 @@
+{
+ "doctrine/annotations": {
+ "version": "2.0",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "main",
+ "version": "1.10",
+ "ref": "64d8583af5ea57b7afa4aba4b159907f3a148b05"
+ }
+ },
+ "doctrine/doctrine-bundle": {
+ "version": "2.9",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "main",
+ "version": "2.8",
+ "ref": "67961f095352f829a24c035639b3d0d3378ffbf2"
+ },
+ "files": [
+ "config/packages/doctrine.yaml",
+ "src/Entity/.gitignore",
+ "src/Repository/.gitignore"
+ ]
+ },
+ "doctrine/doctrine-migrations-bundle": {
+ "version": "3.2",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "main",
+ "version": "3.1",
+ "ref": "1d01ec03c6ecbd67c3375c5478c9a423ae5d6a33"
+ },
+ "files": [
+ "config/packages/doctrine_migrations.yaml",
+ "migrations/.gitignore"
+ ]
+ },
+ "sensio/framework-extra-bundle": {
+ "version": "6.2",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "main",
+ "version": "5.2",
+ "ref": "fb7e19da7f013d0d422fa9bce16f5c510e27609b"
+ },
+ "files": [
+ "config/packages/sensio_framework_extra.yaml"
+ ]
+ },
+ "symfony/console": {
+ "version": "6.2",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "main",
+ "version": "5.3",
+ "ref": "da0c8be8157600ad34f10ff0c9cc91232522e047"
+ },
+ "files": [
+ "bin/console"
+ ]
+ },
+ "symfony/flex": {
+ "version": "2.2",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "main",
+ "version": "1.0",
+ "ref": "146251ae39e06a95be0fe3d13c807bcf3938b172"
+ },
+ "files": [
+ ".env"
+ ]
+ },
+ "symfony/framework-bundle": {
+ "version": "6.2",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "main",
+ "version": "6.2",
+ "ref": "af47254c5e4cd543e6af3e4508298ffebbdaddd3"
+ },
+ "files": [
+ "config/packages/cache.yaml",
+ "config/packages/framework.yaml",
+ "config/preload.php",
+ "config/routes/framework.yaml",
+ "config/services.yaml",
+ "public/index.php",
+ "src/Controller/.gitignore",
+ "src/Kernel.php"
+ ]
+ },
+ "symfony/mailer": {
+ "version": "6.2",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "main",
+ "version": "4.3",
+ "ref": "2bf89438209656b85b9a49238c4467bff1b1f939"
+ },
+ "files": [
+ "config/packages/mailer.yaml"
+ ]
+ },
+ "symfony/routing": {
+ "version": "6.2",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "main",
+ "version": "6.2",
+ "ref": "e0a11b4ccb8c9e70b574ff5ad3dfdcd41dec5aa6"
+ },
+ "files": [
+ "config/packages/routing.yaml",
+ "config/routes.yaml"
+ ]
+ },
+ "symfony/security-bundle": {
+ "version": "6.2",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "main",
+ "version": "6.0",
+ "ref": "8a5b112826f7d3d5b07027f93786ae11a1c7de48"
+ },
+ "files": [
+ "config/packages/security.yaml"
+ ]
+ }
+}
diff --git a/httpdocs/templates/base.html.twig b/httpdocs/templates/base.html.twig
new file mode 100644
index 0000000..043f42d
--- /dev/null
+++ b/httpdocs/templates/base.html.twig
@@ -0,0 +1,12 @@
+
+
+
+
+ {% block title %}Welcome!{% endblock %}
+ {% block stylesheets %}{% endblock %}
+
+
+ {% block body %}{% endblock %}
+ {% block javascripts %}{% endblock %}
+
+
diff --git a/httpdocs/templates/contacts_export.html.twig b/httpdocs/templates/contacts_export.html.twig
new file mode 100644
index 0000000..9473a99
--- /dev/null
+++ b/httpdocs/templates/contacts_export.html.twig
@@ -0,0 +1,46 @@
+
+
+
+
+ {{ title }}
+
+
+
+
+
+ | Firma |
+ Name |
+ Email |
+ Straße |
+ Ort |
+ Abteilung |
+ Telefon |
+ Mobil |
+
+ {% for key, value in data %}
+
+ | {{ value.company }} |
+ {{ value.firstname }} {{ value.lastname }} |
+ {{ value.email }} |
+ {{ value.street }} {{ value.street_no }} |
+ {{ value.zip }} {{ value.city }} |
+ {{ value.department }} |
+ {{ value.phone_no }} |
+ {{ value.mobile_no }} |
+
+ {% endfor %}
+
+
+
\ No newline at end of file
diff --git a/httpdocs/templates/xmas_addresses.html.twig b/httpdocs/templates/xmas_addresses.html.twig
new file mode 100644
index 0000000..6fcac4b
--- /dev/null
+++ b/httpdocs/templates/xmas_addresses.html.twig
@@ -0,0 +1,46 @@
+
+
+
+
+ Weihnachtspost Etiketten
+
+
+
+{% for key, value in data %}
+ {% if key == 0 or key % 14 == 0 %}
+
+ {% endif %}
+ {% if key % 2 == 0 %}
+
+ {% endif %}
+
+
+ PLP Parkhauswerbung GmbH | Siemensstraße 19 | 40721 Hilden
+ {{ value.firstname }} {{ value.lastname }}
+ {{ value.company }}
+ {{ value.street }} {{ value.street_no }}
+ {{ value.zip }} {{ value.city }}
+ {{ value.country }}
+
+ |
+ {% if key % 2 != 0 %}
+
+ {% endif %}
+ {% if (key + 1) % 14 == 0 or data|length == key + 1 %}
+
+ {% endif %}
+{% endfor %}
+
+
\ No newline at end of file