From b7d729b44e4fe115a570bf2780301596b24c9c03 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 24 May 2023 17:18:31 +0200 Subject: [PATCH] code base wip --- .idea/.gitignore | 8 + .idea/encodings.xml | 4 + .idea/modules.xml | 8 + .idea/spt-crm-backend.iml | 8 + .idea/vcs.xml | 6 + httpdocs/.env | 46 + httpdocs/.gitignore | 10 + httpdocs/bin/console | 17 + httpdocs/composer.json | 74 + httpdocs/composer.lock | 5829 +++++++++++++++++ httpdocs/config/bundles.php | 9 + httpdocs/config/packages/cache.yaml | 19 + httpdocs/config/packages/doctrine.yaml | 44 + .../config/packages/doctrine_migrations.yaml | 6 + httpdocs/config/packages/framework.yaml | 25 + httpdocs/config/packages/mailer.yaml | 3 + httpdocs/config/packages/routing.yaml | 12 + httpdocs/config/packages/security.yaml | 39 + .../packages/sensio_framework_extra.yaml | 3 + httpdocs/config/preload.php | 5 + httpdocs/config/routes.yaml | 5 + httpdocs/config/routes/framework.yaml | 4 + httpdocs/config/services.yaml | 32 + httpdocs/migrations/.gitignore | 0 httpdocs/public/index.php | 9 + .../src/Command/CmdSendReminderEmails.php | 292 + httpdocs/src/Controller/.gitignore | 0 httpdocs/src/Controller/AdminController.php | 87 + .../src/Controller/CustomerEditController.php | 227 + .../CustomerMeetingEditController.php | 281 + .../src/Controller/CustomerViewController.php | 49 + httpdocs/src/Controller/ExportController.php | 80 + .../InternalMeetingEditController.php | 247 + .../src/Controller/MeetingViewController.php | 90 + httpdocs/src/Controller/PublicController.php | 57 + httpdocs/src/Entity/.gitignore | 0 httpdocs/src/Entity/EntCountry.php | 166 + httpdocs/src/Entity/EntCustomer.php | 458 ++ httpdocs/src/Entity/EntCustomerContact.php | 473 ++ httpdocs/src/Entity/EntCustomerMeeting.php | 758 +++ .../Entity/EntCustomerMeetingParticipant.php | 96 + httpdocs/src/Entity/EntCustomerNote.php | 440 ++ httpdocs/src/Entity/EntInternalMeeting.php | 338 + .../Entity/EntInternalMeetingParticipant.php | 96 + httpdocs/src/Entity/EntMeetingType.php | 93 + httpdocs/src/Entity/EntUser.php | 349 + httpdocs/src/Entity/EntUserType.php | 161 + httpdocs/src/Entity/IFace/IEntity.php | 21 + httpdocs/src/Entity/convertAnnotations.php | 106 + httpdocs/src/EntityVirtual/ServiceData.php | 100 + .../src/EventListener/ControllerListener.php | 69 + .../src/EventListener/DatabaseListener.php | 72 + httpdocs/src/Kernel.php | 11 + httpdocs/src/Repository/.gitignore | 0 httpdocs/src/Security/JsonAuthenticator.php | 132 + httpdocs/src/Utils/Config.php | 37 + httpdocs/src/Utils/Excel.php | 530 ++ httpdocs/src/Utils/ExcelStyle.php | 139 + httpdocs/src/Utils/Message.php | 209 + httpdocs/src/Utils/Reply.php | 185 + httpdocs/src/Utils/Utils.php | 610 ++ httpdocs/symfony.lock | 130 + httpdocs/templates/base.html.twig | 12 + httpdocs/templates/contacts_export.html.twig | 46 + httpdocs/templates/xmas_addresses.html.twig | 46 + 65 files changed, 13518 insertions(+) create mode 100644 .idea/.gitignore create mode 100644 .idea/encodings.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/spt-crm-backend.iml create mode 100644 .idea/vcs.xml create mode 100644 httpdocs/.env create mode 100644 httpdocs/.gitignore create mode 100755 httpdocs/bin/console create mode 100644 httpdocs/composer.json create mode 100644 httpdocs/composer.lock create mode 100644 httpdocs/config/bundles.php create mode 100644 httpdocs/config/packages/cache.yaml create mode 100644 httpdocs/config/packages/doctrine.yaml create mode 100644 httpdocs/config/packages/doctrine_migrations.yaml create mode 100644 httpdocs/config/packages/framework.yaml create mode 100644 httpdocs/config/packages/mailer.yaml create mode 100644 httpdocs/config/packages/routing.yaml create mode 100644 httpdocs/config/packages/security.yaml create mode 100644 httpdocs/config/packages/sensio_framework_extra.yaml create mode 100644 httpdocs/config/preload.php create mode 100644 httpdocs/config/routes.yaml create mode 100644 httpdocs/config/routes/framework.yaml create mode 100644 httpdocs/config/services.yaml create mode 100644 httpdocs/migrations/.gitignore create mode 100644 httpdocs/public/index.php create mode 100755 httpdocs/src/Command/CmdSendReminderEmails.php create mode 100644 httpdocs/src/Controller/.gitignore create mode 100644 httpdocs/src/Controller/AdminController.php create mode 100644 httpdocs/src/Controller/CustomerEditController.php create mode 100644 httpdocs/src/Controller/CustomerMeetingEditController.php create mode 100644 httpdocs/src/Controller/CustomerViewController.php create mode 100644 httpdocs/src/Controller/ExportController.php create mode 100644 httpdocs/src/Controller/InternalMeetingEditController.php create mode 100644 httpdocs/src/Controller/MeetingViewController.php create mode 100644 httpdocs/src/Controller/PublicController.php create mode 100644 httpdocs/src/Entity/.gitignore create mode 100644 httpdocs/src/Entity/EntCountry.php create mode 100644 httpdocs/src/Entity/EntCustomer.php create mode 100644 httpdocs/src/Entity/EntCustomerContact.php create mode 100644 httpdocs/src/Entity/EntCustomerMeeting.php create mode 100644 httpdocs/src/Entity/EntCustomerMeetingParticipant.php create mode 100644 httpdocs/src/Entity/EntCustomerNote.php create mode 100644 httpdocs/src/Entity/EntInternalMeeting.php create mode 100644 httpdocs/src/Entity/EntInternalMeetingParticipant.php create mode 100644 httpdocs/src/Entity/EntMeetingType.php create mode 100644 httpdocs/src/Entity/EntUser.php create mode 100644 httpdocs/src/Entity/EntUserType.php create mode 100644 httpdocs/src/Entity/IFace/IEntity.php create mode 100644 httpdocs/src/Entity/convertAnnotations.php create mode 100644 httpdocs/src/EntityVirtual/ServiceData.php create mode 100755 httpdocs/src/EventListener/ControllerListener.php create mode 100644 httpdocs/src/EventListener/DatabaseListener.php create mode 100644 httpdocs/src/Kernel.php create mode 100644 httpdocs/src/Repository/.gitignore create mode 100644 httpdocs/src/Security/JsonAuthenticator.php create mode 100644 httpdocs/src/Utils/Config.php create mode 100644 httpdocs/src/Utils/Excel.php create mode 100644 httpdocs/src/Utils/ExcelStyle.php create mode 100644 httpdocs/src/Utils/Message.php create mode 100644 httpdocs/src/Utils/Reply.php create mode 100644 httpdocs/src/Utils/Utils.php create mode 100644 httpdocs/symfony.lock create mode 100644 httpdocs/templates/base.html.twig create mode 100644 httpdocs/templates/contacts_export.html.twig create mode 100644 httpdocs/templates/xmas_addresses.html.twig 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 }} + + + + + + + + + + + + + + + {% for key, value in data %} + + + + + + + + + + + {% endfor %} +
FirmaNameEmailStraßeOrtAbteilungTelefonMobil
{{ 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 }}
+ + \ 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 %} + + {% if key % 2 != 0 %} + + {% endif %} + {% if (key + 1) % 14 == 0 or data|length == key + 1 %} +
+
+ 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 }}

+
+
+ {% endif %} +{% endfor %} + + \ No newline at end of file