diff --git a/.travis.yml b/.travis.yml
index 01cbde6..d473ac2 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -12,8 +12,15 @@ cache:
php:
- 7.2
+services:
+ - mysql
+
before_install:
- mv ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini{,.disabled} || echo "xdebug not available"
+ - mysql -e 'CREATE DATABASE doctrine_website_test;'
+ - sudo mysql -e "use mysql; update user set authentication_string=PASSWORD('VdtLtifRh4gt37T') where User='root'; update user set plugin='mysql_native_password'; FLUSH PRIVILEGES;"
+ - sudo mysql_upgrade -u root -pVdtLtifRh4gt37T
+ - sudo service mysql restart
install:
- rm composer.lock
@@ -37,11 +44,7 @@ jobs:
- mv ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini{.disabled,}
- if [[ ! $(php -m | grep -si xdebug) ]]; then echo "xdebug required for coverage"; exit 1; fi
script:
- - ./bin/console sync-repositories --env=test
- - ./bin/console build-website-data --env=test
- - ./bin/console build-docs --env=test
- - ./bin/console build-website --env=test
- - ./vendor/bin/phpunit --coverage-clover clover.xml
+ - ./phpunit --build-all --coverage-clover clover.xml
after_script:
- wget https://scrutinizer-ci.com/ocular.phar
- php ocular.phar code-coverage:upload --format=php-clover clover.xml
@@ -54,4 +57,4 @@ jobs:
- stage: Code Quality
env: STATIC_ANALYSIS
install: travis_retry composer install --prefer-dist
- script: vendor/bin/phpstan analyse
+ script: ./vendor/bin/phpstan analyse
diff --git a/composer.json b/composer.json
index 0a75d65..6243c92 100644
--- a/composer.json
+++ b/composer.json
@@ -15,13 +15,18 @@
"algolia/algoliasearch-client-php": "^1.27",
"cache/doctrine-adapter": "^1.0",
"doctrine/inflector": "^1.3",
+ "doctrine/migrations": "^2.0",
+ "doctrine/orm": "^2.6",
"doctrine/rst-parser": "^0.1",
"doctrine/skeleton-mapper": "^1.0",
"doctrine/static-website-generator": "^1.0",
"erusev/parsedown": "^1.7",
"knplabs/github-api": "^2.10",
+ "pelago/emogrifier": "^2.1",
"php-http/guzzle6-adapter": "^1.1",
"scrivo/highlight.php": "^9.14",
+ "sendgrid/sendgrid": "^7.3",
+ "stripe/stripe-php": "^6.34",
"symfony/config": "^4.2",
"symfony/console": "^4.2",
"symfony/dependency-injection": "^4.2",
@@ -38,8 +43,9 @@
"phpstan/phpstan-phpunit": "^0.10",
"phpstan/phpstan-strict-rules": "^0.10",
"phpunit/phpunit": "^7.0",
+ "symfony/css-selector": "^4.0",
"symfony/dom-crawler": "^4.0",
- "symfony/css-selector": "^4.0"
+ "symfony/var-dumper": "^4.2"
},
"config": {
"sort-packages": true
diff --git a/composer.lock b/composer.lock
index 2805796..cf52a57 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
- "content-hash": "823e65210c110d30b41f903f7954dbfd",
+ "content-hash": "9d6143b0c9edf1056e827a8110065460",
"packages": [
{
"name": "algolia/algoliasearch-client-php",
@@ -599,6 +599,88 @@
],
"time": "2018-11-21T01:24:55+00:00"
},
+ {
+ "name": "doctrine/dbal",
+ "version": "v2.9.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/dbal.git",
+ "reference": "22800bd651c1d8d2a9719e2a3dc46d5108ebfcc9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/dbal/zipball/22800bd651c1d8d2a9719e2a3dc46d5108ebfcc9",
+ "reference": "22800bd651c1d8d2a9719e2a3dc46d5108ebfcc9",
+ "shasum": ""
+ },
+ "require": {
+ "doctrine/cache": "^1.0",
+ "doctrine/event-manager": "^1.0",
+ "ext-pdo": "*",
+ "php": "^7.1"
+ },
+ "require-dev": {
+ "doctrine/coding-standard": "^5.0",
+ "jetbrains/phpstorm-stubs": "^2018.1.2",
+ "phpstan/phpstan": "^0.10.1",
+ "phpunit/phpunit": "^7.4",
+ "symfony/console": "^2.0.5|^3.0|^4.0",
+ "symfony/phpunit-bridge": "^3.4.5|^4.0.5"
+ },
+ "suggest": {
+ "symfony/console": "For helpful console commands such as SQL execution and import of files."
+ },
+ "bin": [
+ "bin/doctrine-dbal"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.9.x-dev",
+ "dev-develop": "3.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Doctrine\\DBAL\\": "lib/Doctrine/DBAL"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Roman Borschel",
+ "email": "roman@code-factory.org"
+ },
+ {
+ "name": "Benjamin Eberlei",
+ "email": "kontakt@beberlei.de"
+ },
+ {
+ "name": "Guilherme Blanco",
+ "email": "guilhermeblanco@gmail.com"
+ },
+ {
+ "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",
+ "dbal",
+ "mysql",
+ "persistence",
+ "pgsql",
+ "php",
+ "queryobject"
+ ],
+ "time": "2018-12-31T03:27:51+00:00"
+ },
{
"name": "doctrine/event-manager",
"version": "v1.0.0",
@@ -850,6 +932,170 @@
],
"time": "2014-09-09T13:34:57+00:00"
},
+ {
+ "name": "doctrine/migrations",
+ "version": "v2.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/migrations.git",
+ "reference": "43280c14b696a7896a9c70a5e0e4a312ff003187"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/migrations/zipball/43280c14b696a7896a9c70a5e0e4a312ff003187",
+ "reference": "43280c14b696a7896a9c70a5e0e4a312ff003187",
+ "shasum": ""
+ },
+ "require": {
+ "doctrine/dbal": "^2.6",
+ "ocramius/package-versions": "^1.3",
+ "ocramius/proxy-manager": "^2.0.2",
+ "php": "^7.1",
+ "symfony/console": "^3.4||^4.0",
+ "symfony/stopwatch": "^3.4||^4.0"
+ },
+ "require-dev": {
+ "doctrine/coding-standard": "^5.0",
+ "doctrine/orm": "^2.6",
+ "ext-pdo_sqlite": "*",
+ "jdorn/sql-formatter": "^1.1",
+ "mikey179/vfsstream": "^1.6",
+ "phpstan/phpstan": "^0.10",
+ "phpstan/phpstan-deprecation-rules": "^0.10",
+ "phpstan/phpstan-phpunit": "^0.10",
+ "phpstan/phpstan-strict-rules": "^0.10",
+ "phpunit/phpunit": "^7.0",
+ "symfony/process": "^3.4||^4.0",
+ "symfony/yaml": "^3.4||^4.0"
+ },
+ "suggest": {
+ "jdorn/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",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "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",
+ "php"
+ ],
+ "time": "2019-04-25T22:14:55+00:00"
+ },
+ {
+ "name": "doctrine/orm",
+ "version": "v2.6.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/orm.git",
+ "reference": "434820973cadf2da2d66e7184be370084cc32ca8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/orm/zipball/434820973cadf2da2d66e7184be370084cc32ca8",
+ "reference": "434820973cadf2da2d66e7184be370084cc32ca8",
+ "shasum": ""
+ },
+ "require": {
+ "doctrine/annotations": "~1.5",
+ "doctrine/cache": "~1.6",
+ "doctrine/collections": "^1.4",
+ "doctrine/common": "^2.7.1",
+ "doctrine/dbal": "^2.6",
+ "doctrine/instantiator": "~1.1",
+ "ext-pdo": "*",
+ "php": "^7.1",
+ "symfony/console": "~3.0|~4.0"
+ },
+ "require-dev": {
+ "doctrine/coding-standard": "^1.0",
+ "phpunit/phpunit": "^6.5",
+ "squizlabs/php_codesniffer": "^3.2",
+ "symfony/yaml": "~3.4|~4.0"
+ },
+ "suggest": {
+ "symfony/yaml": "If you want to use YAML Metadata Mapping Driver"
+ },
+ "bin": [
+ "bin/doctrine"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.6.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Doctrine\\ORM\\": "lib/Doctrine/ORM"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Roman Borschel",
+ "email": "roman@code-factory.org"
+ },
+ {
+ "name": "Benjamin Eberlei",
+ "email": "kontakt@beberlei.de"
+ },
+ {
+ "name": "Guilherme Blanco",
+ "email": "guilhermeblanco@gmail.com"
+ },
+ {
+ "name": "Jonathan Wage",
+ "email": "jonwage@gmail.com"
+ },
+ {
+ "name": "Marco Pivetta",
+ "email": "ocramius@gmail.com"
+ }
+ ],
+ "description": "Object-Relational-Mapper for PHP",
+ "homepage": "http://www.doctrine-project.org",
+ "keywords": [
+ "database",
+ "orm"
+ ],
+ "time": "2018-11-20T23:46:46+00:00"
+ },
{
"name": "doctrine/persistence",
"version": "v1.1.0",
@@ -1480,6 +1726,200 @@
],
"time": "2019-01-28T19:31:35+00:00"
},
+ {
+ "name": "ocramius/package-versions",
+ "version": "1.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/Ocramius/PackageVersions.git",
+ "reference": "a4d4b60d0e60da2487bd21a2c6ac089f85570dbb"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/Ocramius/PackageVersions/zipball/a4d4b60d0e60da2487bd21a2c6ac089f85570dbb",
+ "reference": "a4d4b60d0e60da2487bd21a2c6ac089f85570dbb",
+ "shasum": ""
+ },
+ "require": {
+ "composer-plugin-api": "^1.0.0",
+ "php": "^7.1.0"
+ },
+ "require-dev": {
+ "composer/composer": "^1.6.3",
+ "doctrine/coding-standard": "^5.0.1",
+ "ext-zip": "*",
+ "infection/infection": "^0.7.1",
+ "phpunit/phpunit": "^7.0.0"
+ },
+ "type": "composer-plugin",
+ "extra": {
+ "class": "PackageVersions\\Installer",
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "PackageVersions\\": "src/PackageVersions"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Marco Pivetta",
+ "email": "ocramius@gmail.com"
+ }
+ ],
+ "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)",
+ "time": "2019-02-21T12:16:21+00:00"
+ },
+ {
+ "name": "ocramius/proxy-manager",
+ "version": "2.2.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/Ocramius/ProxyManager.git",
+ "reference": "14b137b06b0f911944132df9d51e445a35920ab1"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/Ocramius/ProxyManager/zipball/14b137b06b0f911944132df9d51e445a35920ab1",
+ "reference": "14b137b06b0f911944132df9d51e445a35920ab1",
+ "shasum": ""
+ },
+ "require": {
+ "ocramius/package-versions": "^1.1.3",
+ "php": "^7.2.0",
+ "zendframework/zend-code": "^3.3.0"
+ },
+ "require-dev": {
+ "couscous/couscous": "^1.6.1",
+ "ext-phar": "*",
+ "humbug/humbug": "1.0.0-RC.0@RC",
+ "nikic/php-parser": "^3.1.1",
+ "padraic/phpunit-accelerator": "dev-master@DEV",
+ "phpbench/phpbench": "^0.12.2",
+ "phpstan/phpstan": "dev-master#856eb10a81c1d27c701a83f167dc870fd8f4236a as 0.9.999",
+ "phpstan/phpstan-phpunit": "dev-master#5629c0a1f4a9c417cb1077cf6693ad9753895761",
+ "phpunit/phpunit": "^6.4.3",
+ "squizlabs/php_codesniffer": "^2.9.1"
+ },
+ "suggest": {
+ "ocramius/generated-hydrator": "To have very fast object to array to object conversion for ghost objects",
+ "zendframework/zend-json": "To have the JsonRpc adapter (Remote Object feature)",
+ "zendframework/zend-soap": "To have the Soap adapter (Remote Object feature)",
+ "zendframework/zend-xmlrpc": "To have the XmlRpc adapter (Remote Object feature)"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-0": {
+ "ProxyManager\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Marco Pivetta",
+ "email": "ocramius@gmail.com",
+ "homepage": "http://ocramius.github.io/"
+ }
+ ],
+ "description": "A library providing utilities to generate, instantiate and generally operate with Object Proxies",
+ "homepage": "https://github.com/Ocramius/ProxyManager",
+ "keywords": [
+ "aop",
+ "lazy loading",
+ "proxy",
+ "proxy pattern",
+ "service proxies"
+ ],
+ "time": "2018-09-27T13:45:01+00:00"
+ },
+ {
+ "name": "pelago/emogrifier",
+ "version": "v2.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/MyIntervals/emogrifier.git",
+ "reference": "8ee7fb5ad772915451ed3415c1992bd3697d4983"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/MyIntervals/emogrifier/zipball/8ee7fb5ad772915451ed3415c1992bd3697d4983",
+ "reference": "8ee7fb5ad772915451ed3415c1992bd3697d4983",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-libxml": "*",
+ "php": "^5.5.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0",
+ "symfony/css-selector": "^3.4.0 || ^4.0.0"
+ },
+ "require-dev": {
+ "friendsofphp/php-cs-fixer": "^2.2.0",
+ "phpmd/phpmd": "^2.6.0",
+ "phpunit/phpunit": "^4.8.0",
+ "squizlabs/php_codesniffer": "^3.3.2"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Pelago\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "John Reeve",
+ "email": "jreeve@pelagodesign.com"
+ },
+ {
+ "name": "Cameron Brooks"
+ },
+ {
+ "name": "Jaime Prado"
+ },
+ {
+ "name": "Oliver Klee",
+ "email": "github@oliverklee.de"
+ },
+ {
+ "name": "Zoli Szabó",
+ "email": "zoli.szabo+github@gmail.com"
+ },
+ {
+ "name": "Jake Hotson",
+ "email": "jake@qzdesign.co.uk"
+ }
+ ],
+ "description": "Converts CSS styles into inline style attributes in your HTML code",
+ "homepage": "https://www.myintervals.com/emogrifier.php",
+ "keywords": [
+ "css",
+ "email",
+ "pre-processing"
+ ],
+ "time": "2018-12-10T10:36:30+00:00"
+ },
{
"name": "php-http/cache-plugin",
"version": "1.6.0",
@@ -2295,6 +2735,171 @@
],
"time": "2019-03-31T21:09:11+00:00"
},
+ {
+ "name": "sendgrid/php-http-client",
+ "version": "3.9.6",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sendgrid/php-http-client.git",
+ "reference": "e9a04d949ee2d19938ab83dc100933a3b41a8ec7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sendgrid/php-http-client/zipball/e9a04d949ee2d19938ab83dc100933a3b41a8ec7",
+ "reference": "e9a04d949ee2d19938ab83dc100933a3b41a8ec7",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.6"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.4",
+ "squizlabs/php_codesniffer": "~2.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "SendGrid\\": "lib/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Matt Bernier",
+ "email": "dx@sendgrid.com"
+ },
+ {
+ "name": "Elmer Thomas",
+ "email": "elmer@thinkingserious.com"
+ }
+ ],
+ "description": "HTTP REST client, simplified for PHP",
+ "homepage": "http://github.com/sendgrid/php-http-client",
+ "keywords": [
+ "api",
+ "fluent",
+ "http",
+ "rest",
+ "sendgrid"
+ ],
+ "time": "2018-04-10T18:06:08+00:00"
+ },
+ {
+ "name": "sendgrid/sendgrid",
+ "version": "7.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sendgrid/sendgrid-php.git",
+ "reference": "37fa19d3ae019842f07a2a43e92ed0f566ad927d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sendgrid/sendgrid-php/zipball/37fa19d3ae019842f07a2a43e92ed0f566ad927d",
+ "reference": "37fa19d3ae019842f07a2a43e92ed0f566ad927d",
+ "shasum": ""
+ },
+ "require": {
+ "ext-curl": "*",
+ "ext-json": "*",
+ "ext-mbstring": "*",
+ "ext-openssl": "*",
+ "php": ">=5.6",
+ "sendgrid/php-http-client": "~3.9"
+ },
+ "replace": {
+ "sendgrid/sendgrid-php": "*"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^5.7.9 || ^6.4.3",
+ "squizlabs/php_codesniffer": "3.*",
+ "swaggest/json-diff": "^3.4"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "SendGrid\\": "lib/",
+ "SendGrid\\Mail\\": "lib/mail/",
+ "SendGrid\\Contacts\\": "lib/contacts/",
+ "SendGrid\\Stats\\": "lib/stats/"
+ },
+ "files": [
+ "lib/SendGrid.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "This library allows you to quickly and easily send emails through Twilio SendGrid using PHP.",
+ "homepage": "http://github.com/sendgrid/sendgrid-php",
+ "keywords": [
+ "email",
+ "grid",
+ "send",
+ "sendgrid",
+ "twilio sendgrid"
+ ],
+ "time": "2019-04-15T17:27:21+00:00"
+ },
+ {
+ "name": "stripe/stripe-php",
+ "version": "v6.34.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/stripe/stripe-php.git",
+ "reference": "124fafb57c287cf6e13830bcf329cac160a6afe8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/stripe/stripe-php/zipball/124fafb57c287cf6e13830bcf329cac160a6afe8",
+ "reference": "124fafb57c287cf6e13830bcf329cac160a6afe8",
+ "shasum": ""
+ },
+ "require": {
+ "ext-curl": "*",
+ "ext-json": "*",
+ "ext-mbstring": "*",
+ "php": ">=5.4.0"
+ },
+ "require-dev": {
+ "php-coveralls/php-coveralls": "1.*",
+ "phpunit/phpunit": "~4.0",
+ "squizlabs/php_codesniffer": "~2.0",
+ "symfony/process": "~2.8"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Stripe\\": "lib/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Stripe and contributors",
+ "homepage": "https://github.com/stripe/stripe-php/contributors"
+ }
+ ],
+ "description": "Stripe PHP Library",
+ "homepage": "https://stripe.com/",
+ "keywords": [
+ "api",
+ "payment processing",
+ "stripe"
+ ],
+ "time": "2019-05-07T00:56:15+00:00"
+ },
{
"name": "symfony/config",
"version": "v4.2.6",
@@ -2498,6 +3103,59 @@
],
"time": "2018-12-05T08:06:11+00:00"
},
+ {
+ "name": "symfony/css-selector",
+ "version": "v4.2.6",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/css-selector.git",
+ "reference": "48eddf66950fa57996e1be4a55916d65c10c604a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/css-selector/zipball/48eddf66950fa57996e1be4a55916d65c10c604a",
+ "reference": "48eddf66950fa57996e1be4a55916d65c10c604a",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.1.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.2-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\CssSelector\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jean-François Simon",
+ "email": "jeanfrancois.simon@sensiolabs.com"
+ },
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony CssSelector Component",
+ "homepage": "https://symfony.com",
+ "time": "2019-01-16T20:31:39+00:00"
+ },
{
"name": "symfony/debug",
"version": "v4.2.6",
@@ -3229,6 +3887,56 @@
],
"time": "2019-04-03T13:26:22+00:00"
},
+ {
+ "name": "symfony/stopwatch",
+ "version": "v4.2.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/stopwatch.git",
+ "reference": "b1a5f646d56a3290230dbc8edf2a0d62cda23f67"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/stopwatch/zipball/b1a5f646d56a3290230dbc8edf2a0d62cda23f67",
+ "reference": "b1a5f646d56a3290230dbc8edf2a0d62cda23f67",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.1.3",
+ "symfony/contracts": "^1.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.2-dev"
+ }
+ },
+ "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": "Symfony Stopwatch Component",
+ "homepage": "https://symfony.com",
+ "time": "2019-01-16T20:31:39+00:00"
+ },
{
"name": "symfony/yaml",
"version": "v4.2.6",
@@ -3354,6 +4062,113 @@
"templating"
],
"time": "2019-04-16T12:36:25+00:00"
+ },
+ {
+ "name": "zendframework/zend-code",
+ "version": "3.3.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/zendframework/zend-code.git",
+ "reference": "c21db169075c6ec4b342149f446e7b7b724f95eb"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/zendframework/zend-code/zipball/c21db169075c6ec4b342149f446e7b7b724f95eb",
+ "reference": "c21db169075c6ec4b342149f446e7b7b724f95eb",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.1",
+ "zendframework/zend-eventmanager": "^2.6 || ^3.0"
+ },
+ "require-dev": {
+ "doctrine/annotations": "~1.0",
+ "ext-phar": "*",
+ "phpunit/phpunit": "^6.2.3",
+ "zendframework/zend-coding-standard": "^1.0.0",
+ "zendframework/zend-stdlib": "^2.7 || ^3.0"
+ },
+ "suggest": {
+ "doctrine/annotations": "Doctrine\\Common\\Annotations >=1.0 for annotation features",
+ "zendframework/zend-stdlib": "Zend\\Stdlib component"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.3.x-dev",
+ "dev-develop": "3.4.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Zend\\Code\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "description": "provides facilities to generate arbitrary code using an object oriented interface",
+ "homepage": "https://github.com/zendframework/zend-code",
+ "keywords": [
+ "code",
+ "zf2"
+ ],
+ "time": "2018-08-13T20:36:59+00:00"
+ },
+ {
+ "name": "zendframework/zend-eventmanager",
+ "version": "3.2.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/zendframework/zend-eventmanager.git",
+ "reference": "a5e2583a211f73604691586b8406ff7296a946dd"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/zendframework/zend-eventmanager/zipball/a5e2583a211f73604691586b8406ff7296a946dd",
+ "reference": "a5e2583a211f73604691586b8406ff7296a946dd",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.6 || ^7.0"
+ },
+ "require-dev": {
+ "athletic/athletic": "^0.1",
+ "container-interop/container-interop": "^1.1.0",
+ "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2",
+ "zendframework/zend-coding-standard": "~1.0.0",
+ "zendframework/zend-stdlib": "^2.7.3 || ^3.0"
+ },
+ "suggest": {
+ "container-interop/container-interop": "^1.1.0, to use the lazy listeners feature",
+ "zendframework/zend-stdlib": "^2.7.3 || ^3.0, to use the FilterChain feature"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.2-dev",
+ "dev-develop": "3.3-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Zend\\EventManager\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "description": "Trigger and listen to events within a PHP application",
+ "homepage": "https://github.com/zendframework/zend-eventmanager",
+ "keywords": [
+ "event",
+ "eventmanager",
+ "events",
+ "zf2"
+ ],
+ "time": "2018-04-25T15:33:34+00:00"
}
],
"packages-dev": [
@@ -4201,56 +5016,6 @@
],
"time": "2019-02-16T20:54:15+00:00"
},
- {
- "name": "ocramius/package-versions",
- "version": "1.4.0",
- "source": {
- "type": "git",
- "url": "https://github.com/Ocramius/PackageVersions.git",
- "reference": "a4d4b60d0e60da2487bd21a2c6ac089f85570dbb"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/Ocramius/PackageVersions/zipball/a4d4b60d0e60da2487bd21a2c6ac089f85570dbb",
- "reference": "a4d4b60d0e60da2487bd21a2c6ac089f85570dbb",
- "shasum": ""
- },
- "require": {
- "composer-plugin-api": "^1.0.0",
- "php": "^7.1.0"
- },
- "require-dev": {
- "composer/composer": "^1.6.3",
- "doctrine/coding-standard": "^5.0.1",
- "ext-zip": "*",
- "infection/infection": "^0.7.1",
- "phpunit/phpunit": "^7.0.0"
- },
- "type": "composer-plugin",
- "extra": {
- "class": "PackageVersions\\Installer",
- "branch-alias": {
- "dev-master": "2.0.x-dev"
- }
- },
- "autoload": {
- "psr-4": {
- "PackageVersions\\": "src/PackageVersions"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Marco Pivetta",
- "email": "ocramius@gmail.com"
- }
- ],
- "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)",
- "time": "2019-02-21T12:16:21+00:00"
- },
{
"name": "phar-io/manifest",
"version": "1.0.3",
@@ -5817,59 +6582,6 @@
],
"time": "2019-04-10T23:49:02+00:00"
},
- {
- "name": "symfony/css-selector",
- "version": "v4.2.6",
- "source": {
- "type": "git",
- "url": "https://github.com/symfony/css-selector.git",
- "reference": "48eddf66950fa57996e1be4a55916d65c10c604a"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/symfony/css-selector/zipball/48eddf66950fa57996e1be4a55916d65c10c604a",
- "reference": "48eddf66950fa57996e1be4a55916d65c10c604a",
- "shasum": ""
- },
- "require": {
- "php": "^7.1.3"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "4.2-dev"
- }
- },
- "autoload": {
- "psr-4": {
- "Symfony\\Component\\CssSelector\\": ""
- },
- "exclude-from-classmap": [
- "/Tests/"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Jean-François Simon",
- "email": "jeanfrancois.simon@sensiolabs.com"
- },
- {
- "name": "Fabien Potencier",
- "email": "fabien@symfony.com"
- },
- {
- "name": "Symfony Community",
- "homepage": "https://symfony.com/contributors"
- }
- ],
- "description": "Symfony CssSelector Component",
- "homepage": "https://symfony.com",
- "time": "2019-01-16T20:31:39+00:00"
- },
{
"name": "symfony/dom-crawler",
"version": "v4.2.6",
@@ -5927,6 +6639,137 @@
"homepage": "https://symfony.com",
"time": "2019-02-23T15:17:42+00:00"
},
+ {
+ "name": "symfony/polyfill-php72",
+ "version": "v1.11.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php72.git",
+ "reference": "ab50dcf166d5f577978419edd37aa2bb8eabce0c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/ab50dcf166d5f577978419edd37aa2bb8eabce0c",
+ "reference": "ab50dcf166d5f577978419edd37aa2bb8eabce0c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.11-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Polyfill\\Php72\\": ""
+ },
+ "files": [
+ "bootstrap.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": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "time": "2019-02-06T07:57:58+00:00"
+ },
+ {
+ "name": "symfony/var-dumper",
+ "version": "v4.2.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/var-dumper.git",
+ "reference": "3c4084cb1537c0e2ad41aad622bbf55a44a5c9ce"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/var-dumper/zipball/3c4084cb1537c0e2ad41aad622bbf55a44a5c9ce",
+ "reference": "3c4084cb1537c0e2ad41aad622bbf55a44a5c9ce",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.1.3",
+ "symfony/polyfill-mbstring": "~1.0",
+ "symfony/polyfill-php72": "~1.5"
+ },
+ "conflict": {
+ "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0",
+ "symfony/console": "<3.4"
+ },
+ "require-dev": {
+ "ext-iconv": "*",
+ "symfony/console": "~3.4|~4.0",
+ "symfony/process": "~3.4|~4.0",
+ "twig/twig": "~1.34|~2.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",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.2-dev"
+ }
+ },
+ "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": "Symfony mechanism for exploring and dumping PHP variables",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "debug",
+ "dump"
+ ],
+ "time": "2019-05-01T12:55:36+00:00"
+ },
{
"name": "theseer/tokenizer",
"version": "1.1.2",
diff --git a/config/config.yml b/config/config.yml
index cce7bc4..5c2235d 100644
--- a/config/config.yml
+++ b/config/config.yml
@@ -1,6 +1,9 @@
parameters:
doctrine.website.algolia.app_id: 'YVYTFT9XMW'
doctrine.website.algolia.admin_api_key: '1234'
+ doctrine.website.stripe.publishable_key: 'pk_test_V7rmF8ac3GbakeSJYvp4u0a8003RPO66vC'
+ doctrine.website.stripe.secret_key: ''
+ doctrine.website.send_grid.api_key: ''
doctrine.website.webpack_build_dir: '%doctrine.website.root_dir%/.webpack-build'
doctrine.website.projects_dir: '%doctrine.website.root_dir%/projects'
@@ -10,7 +13,8 @@ parameters:
doctrine.website.title: Doctrine
doctrine.website.subtitle: PHP Database Tools
- doctrine.website.url: http://lcl.doctrine-project.org
+ doctrine.website.url: https://lcl.doctrine-project.org
+ doctrine.website.assets_url: https://www.doctrine-project.org
doctrine.website.keywords: [php, mysql, orm, dbal, database, mongodb, odm, annotations, migrations, cache, couchdb]
doctrine.website.description: >
The Doctrine Project is the home to several PHP libraries primarily
diff --git a/config/config_prod.yml b/config/config_prod.yml
index 5057dd9..34da107 100644
--- a/config/config_prod.yml
+++ b/config/config_prod.yml
@@ -4,3 +4,4 @@ imports:
parameters:
doctrine.website.url: 'https://www.doctrine-project.org'
doctrine.website.google_analytics_tracking_id: 'UA-288343-7'
+ doctrine.website.stripe.publishable_key: 'pk_live_54QFCgqHDYHZ1tFxg95JhYRR00kICoxvNS'
diff --git a/config/config_test.yml b/config/config_test.yml
index b6279be..58de21a 100644
--- a/config/config_test.yml
+++ b/config/config_test.yml
@@ -1,6 +1,9 @@
imports:
- { resource: config.yml }
+parameters:
+ doctrine.website.mysql.password: 'VdtLtifRh4gt37T'
+
services:
Doctrine\Website\Github\GithubProjectContributors:
alias: Doctrine\Website\Github\TestGithubProjectContributors
diff --git a/config/routes.yml b/config/routes.yml
index d2c2129..846dbc3 100644
--- a/config/routes.yml
+++ b/config/routes.yml
@@ -23,6 +23,32 @@ parameters:
defaults:
title: RST Example
+ events:
+ path: /events.html
+ controller: Doctrine\Website\Controllers\EventsController::index
+ defaults:
+ menuSlug: events
+
+ event_suggest:
+ path: /events/suggest.html
+ controller: Doctrine\Website\Controllers\EventsController::suggest
+ defaults:
+ menuSlug: events
+
+ event:
+ path: /events/{id}/{slug}.html
+ controller: Doctrine\Website\Controllers\EventsController::view
+ provider: Doctrine\Website\Requests\EventRequests::getEvents
+ defaults:
+ menuSlug: events
+
+ event_cfp:
+ path: /events/{id}/{slug}/cfp.html
+ controller: Doctrine\Website\Controllers\EventsController::cfp
+ provider: Doctrine\Website\Requests\EventRequests::getEvents
+ defaults:
+ menuSlug: events
+
styleguide:
path: /styleguide.html
defaults:
@@ -46,6 +72,8 @@ parameters:
path: /partners/{slug}.html
controller: Doctrine\Website\Controllers\PartnersController::view
provider: Doctrine\Website\Requests\PartnerRequests::getPartners
+ defaults:
+ menuSlug: partners
consulting:
path: /consulting.html
diff --git a/config/services.xml b/config/services.xml
index 829a926..50fba50 100644
--- a/config/services.xml
+++ b/config/services.xml
@@ -3,6 +3,11 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd">
+
+ root
+
+
+
%doctrine.website.root_dir%
@@ -13,13 +18,10 @@
%doctrine.website.source_dir%
%doctrine.website.projects_data%
%doctrine.website.webpack_build_dir%
- %doctrine.website.team_members%
%doctrine.website.templates_dir%
- %doctrine.website.doctrine_users%
- %doctrine.website.sponsors%
- %doctrine.website.partners%
%doctrine.website.project_integration.types%
%doctrine.website.routes%
+ %doctrine.website.stripe.publishable_key%
@@ -55,6 +57,20 @@
+
+
+
+
+
+
+ %doctrine.website.templates_dir%
+
+
+
+
+
+
+
@@ -121,6 +137,9 @@
%doctrine.website.cache_dir%/fscache
+
+ %doctrine.website.send_grid.api_key%
+
@@ -128,6 +147,7 @@
+
@@ -140,6 +160,7 @@
+
@@ -151,7 +172,7 @@
%doctrine.website.algolia.admin_api_key%
-
+
%doctrine.website.title%
%doctrine.website.subtitle%
%doctrine.website.url%
@@ -159,8 +180,11 @@
%doctrine.website.description%
%doctrine.website.env%
%doctrine.website.google_analytics_tracking_id%
+ %doctrine.website.assets_url%
+
+
@@ -208,7 +232,7 @@
-
+
@@ -216,7 +240,7 @@
Doctrine\Website\Model\BlogPost
-
+
@@ -227,7 +251,7 @@
-
+
@@ -240,13 +264,17 @@
-
+
+
+ %doctrine.website.doctrine_users%
+
+
Doctrine\Website\Model\DoctrineUser
-
+
@@ -259,13 +287,17 @@
-
+
+
+ %doctrine.website.sponsors%
+
+
Doctrine\Website\Model\Sponsor
-
+
@@ -278,13 +310,17 @@
-
+
+
+ %doctrine.website.partners%
+
+
Doctrine\Website\Model\Partner
-
+
@@ -303,7 +339,7 @@
-
+
@@ -322,7 +358,7 @@
-
+
@@ -341,7 +377,7 @@
-
+
@@ -354,13 +390,17 @@
-
+
+
+ %doctrine.website.team_members%
+
+
Doctrine\Website\Model\TeamMember
-
+
@@ -368,6 +408,31 @@
Doctrine\Website\Model\TeamMember
+
+
+
+
+
+
+
+ %doctrine.website.events%
+
+
+ Doctrine\Website\Model\Event
+
+
+
+
+
+
+
+ %doctrine.website.env%
+
+
+
+ Doctrine\Website\Model\Event
+
+
Doctrine\Website\Model\BlogPost
@@ -409,6 +474,11 @@
+
+ Doctrine\Website\Model\Event
+
+
+
Doctrine\Website\Model\SitemapPage
@@ -422,5 +492,50 @@
+
+
+
+
+ %doctrine.website.root_dir%/lib/Model/Entity
+
+ %doctrine.website.debug%
+
+
+
+
+
+
+
+
+
+
+ Doctrine\Website\Model\Entity\EventParticipant
+
+
+
+
+
+ doctrine_website_%doctrine.website.env%
+ %doctrine.website.mysql.user%
+ %doctrine.website.mysql.password%
+ localhost
+ pdo_mysql
+
+
+
+
+
+ Doctrine Website Migrations
+
+
+ Doctrine\Website\Migrations
+
+
+ true
+
+
+ %doctrine.website.root_dir%/lib/Migrations
+
+
diff --git a/data/events.yml b/data/events.yml
new file mode 100644
index 0000000..699e08c
--- /dev/null
+++ b/data/events.yml
@@ -0,0 +1,99 @@
+parameters:
+ # New events go at the top and get an incremented ID.
+ doctrine.website.events:
+ -
+ id: 3
+ sku:
+ test: 'sku_F4LUc9SGRiCBUK'
+ prod: 'sku_F4M3RdEwiscrtD'
+ name: 'Getting started with Doctrine MongoDB ODM'
+ slug: 'getting-started-with-doctrine-mongodb-odm'
+ price: 5.00
+ joinUrl: 'https://zoom.us/webinar/register/9215578873469/WN_fwSHnCzCQ7WO_8Tao5rr7Q'
+ startDate: '2019-07-16'
+ endDate: '2019-07-16'
+ registrationStartDate: '2019-05-01'
+ registrationEndDate: '2019-07-16'
+ sponsors: []
+ speakers:
+ -
+ name: 'alcaeus'
+ topic: 'Getting started with Doctrine MongoDB ODM'
+ topicSlug: 'getting-started-with-doctrine-mongodb-odm'
+ description: >
+ You've used Doctrine ORM but never used MongoDB? With MongoDB ODM you can use familiar
+ tools with a different kind of database. This webinar will focus on getting MongoDB
+ set up and designing our first documents for MongoDB. We'll cover differences in schema
+ design between relational databases and MongoDB and also use more advanced features like
+ aggregation pipelines and document validation.
+
+ schedule:
+ -
+ topicSlug: 'getting-started-with-doctrine-mongodb-odm'
+ startDate: '2019-07-16 11:00'
+ endDate: '2019-07-16 12:00'
+
+ -
+ id: 2
+ sku:
+ test: 'sku_F4IaFRrVzZ4m4G'
+ prod: 'sku_F4M21kt1HC36by'
+ name: 'Doctrine for Beginners'
+ slug: 'doctrine-for-beginners'
+ price: 5.00
+ joinUrl: 'https://zoom.us/webinar/register/1315578887109/WN_4yo28dTASHyTqibhpuvo-Q'
+ startDate: '2019-05-28'
+ endDate: '2019-05-28'
+ registrationStartDate: '2019-05-01'
+ registrationEndDate: '2019-05-27'
+ sponsors: []
+ speakers:
+ -
+ name: 'jwage'
+ topic: 'Doctrine for Beginners'
+ topicSlug: 'doctrine-for-beginners'
+ description: >
+ Come to this talk prepared to learn about the Doctrine PHP open source project.
+ The Doctrine project has been around for over a decade and has evolved from database
+ abstraction software that dates back to the PEAR days. The packages provided by the
+ Doctrine project have been downloaded almost a billion times from packagist. In
+ this talk we will take you through how to get started with Doctrine and how to take
+ advantage of some of the more advanced features.
+
+ schedule:
+ -
+ topicSlug: 'doctrine-for-beginners'
+ startDate: '2019-05-28 11:00'
+ endDate: '2019-05-28 12:00'
+
+ -
+ id: 1
+ sku:
+ test: 'sku_F4LV13ZF4c5aUn'
+ prod: 'sku_F4M2PSGAcfOGyO'
+ name: 'PHP Internals for the Inquisitive Developer'
+ slug: 'php-internals-for-the-inquisitive-developer'
+ price: 5.00
+ joinUrl: 'https://zoom.us/webinar/register/8515577702351/WN_OBdv-AOHRWyNeIC2FXZ-IQ'
+ startDate: '2019-09-10'
+ endDate: '2019-09-10'
+ registrationStartDate: '2019-05-01'
+ registrationEndDate: '2019-09-10'
+ sponsors: []
+ speakers:
+ -
+ name: 'jmikola'
+ topic: 'PHP Internals for the Inquisitive Developer'
+ topicSlug: 'php-internals-for-the-inquisitive-developer'
+ description: >
+ Even if you have no intention of becoming a PHP core developer or creating a PECL
+ extension, cursory knowledge of PHP's inner workings can prove useful. This session
+ will examine the lifecycle of a PHP request and equip you with essential knowledge
+ and tools that can be used to diagnose the occasional segfault or language bug,
+ decipher what a poorly documented SPL class actually does, and confidently answer why
+ a PHP or C implementation is most warranted for a given problem.
+ schedule:
+ -
+ topicSlug: 'php-internals-for-the-inquisitive-developer'
+ startDate: '2019-09-10 11:00'
+ endDate: '2019-09-10 12:00'
diff --git a/config/partners.yml b/data/partners.yml
similarity index 100%
rename from config/partners.yml
rename to data/partners.yml
diff --git a/config/projects.yml b/data/projects.yml
similarity index 100%
rename from config/projects.yml
rename to data/projects.yml
diff --git a/config/sponsors.yml b/data/sponsors.yml
similarity index 100%
rename from config/sponsors.yml
rename to data/sponsors.yml
diff --git a/config/team_members.yml b/data/team_members.yml
similarity index 100%
rename from config/team_members.yml
rename to data/team_members.yml
diff --git a/lib/Application.php b/lib/Application.php
index f48cba9..c75a8a0 100644
--- a/lib/Application.php
+++ b/lib/Application.php
@@ -4,19 +4,34 @@ declare(strict_types=1);
namespace Doctrine\Website;
+use Doctrine\DBAL\Connection;
+use Doctrine\DBAL\Tools\Console\Command as DBALCommand;
+use Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper;
+use Doctrine\Migrations\Configuration\Configuration as MigrationsConfiguration;
+use Doctrine\Migrations\Tools\Console\Command as MigrationsCommand;
+use Doctrine\Migrations\Tools\Console\Helper\ConfigurationHelper;
+use Doctrine\ORM\EntityManager;
+use Doctrine\ORM\Tools\Console\Command as ORMCommand;
+use Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper;
+use Doctrine\Website\Commands\BuildAllCommand;
use Doctrine\Website\Commands\BuildDocsCommand;
use Doctrine\Website\Commands\BuildWebsiteCommand;
use Doctrine\Website\Commands\BuildWebsiteDataCommand;
use Doctrine\Website\Commands\ClearBuildCacheCommand;
use Doctrine\Website\Commands\DeployCommand;
+use Doctrine\Website\Commands\EventParticipantsCommand;
use Doctrine\Website\Commands\SyncRepositoriesCommand;
+use Stripe;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Console\Application as BaseApplication;
+use Symfony\Component\Console\Helper\HelperSet;
+use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
+use function date_default_timezone_set;
use function file_exists;
use function getenv;
use function realpath;
@@ -27,40 +42,72 @@ class Application
/** @var BaseApplication */
private $application;
- /** @var BuildDocsCommand */
- private $buildDocsCommand;
-
- /** @var BuildWebsiteCommand */
- private $buildWebsiteCommand;
-
- /** @var BuildWebsiteDataCommand */
- private $buildWebsiteDataCommand;
-
- /** @var ClearBuildCacheCommand */
- private $clearBuildCacheCommand;
-
- /** @var DeployCommand */
- private $deployCommand;
-
- /** @var SyncRepositoriesCommand */
- private $syncRepositoriesCommand;
-
public function __construct(
BaseApplication $application,
+ EntityManager $em,
+ Connection $connection,
+ MigrationsConfiguration $migrationsConfiguration,
+ BuildAllCommand $buildAllCommand,
BuildDocsCommand $buildDocsCommand,
BuildWebsiteCommand $buildWebsiteCommand,
BuildWebsiteDataCommand $buildWebsiteDataCommand,
ClearBuildCacheCommand $clearBuildCacheCommand,
DeployCommand $deployCommand,
- SyncRepositoriesCommand $syncRepositoriesCommand
+ SyncRepositoriesCommand $syncRepositoriesCommand,
+ EventParticipantsCommand $eventParticipantsCommand
) {
- $this->application = $application;
- $this->buildDocsCommand = $buildDocsCommand;
- $this->buildWebsiteCommand = $buildWebsiteCommand;
- $this->buildWebsiteDataCommand = $buildWebsiteDataCommand;
- $this->clearBuildCacheCommand = $clearBuildCacheCommand;
- $this->deployCommand = $deployCommand;
- $this->syncRepositoriesCommand = $syncRepositoriesCommand;
+ $this->application = $application;
+
+ $this->application->add($buildAllCommand);
+ $this->application->add($buildDocsCommand);
+ $this->application->add($buildWebsiteCommand);
+ $this->application->add($buildWebsiteDataCommand);
+ $this->application->add($clearBuildCacheCommand);
+ $this->application->add($deployCommand);
+ $this->application->add($syncRepositoriesCommand);
+ $this->application->add($eventParticipantsCommand);
+
+ $this->application->setHelperSet(new HelperSet([
+ 'question' => new QuestionHelper(),
+ 'db' => new ConnectionHelper($connection),
+ 'em' => new EntityManagerHelper($em),
+ 'configuration' => new ConfigurationHelper($connection, $migrationsConfiguration),
+ ]));
+
+ $this->application->addCommands([
+ // DBAL Commands
+ new DBALCommand\ReservedWordsCommand(),
+ new DBALCommand\RunSqlCommand(),
+
+ // ORM Commands
+ new ORMCommand\ClearCache\CollectionRegionCommand(),
+ new ORMCommand\ClearCache\EntityRegionCommand(),
+ new ORMCommand\ClearCache\MetadataCommand(),
+ new ORMCommand\ClearCache\QueryCommand(),
+ new ORMCommand\ClearCache\QueryRegionCommand(),
+ new ORMCommand\ClearCache\ResultCommand(),
+ new ORMCommand\SchemaTool\CreateCommand(),
+ new ORMCommand\SchemaTool\UpdateCommand(),
+ new ORMCommand\SchemaTool\DropCommand(),
+ new ORMCommand\EnsureProductionSettingsCommand(),
+ new ORMCommand\GenerateProxiesCommand(),
+ new ORMCommand\RunDqlCommand(),
+ new ORMCommand\ValidateSchemaCommand(),
+ new ORMCommand\InfoCommand(),
+ new ORMCommand\MappingDescribeCommand(),
+
+ // Migrations Commands
+ new MigrationsCommand\DumpSchemaCommand(),
+ new MigrationsCommand\ExecuteCommand(),
+ new MigrationsCommand\GenerateCommand(),
+ new MigrationsCommand\LatestCommand(),
+ new MigrationsCommand\MigrateCommand(),
+ new MigrationsCommand\RollupCommand(),
+ new MigrationsCommand\StatusCommand(),
+ new MigrationsCommand\VersionCommand(),
+ new MigrationsCommand\UpToDateCommand(),
+ new MigrationsCommand\DiffCommand(),
+ ]);
}
public function run(InputInterface $input) : int
@@ -74,43 +121,49 @@ class Application
);
$this->application->getDefinition()->addOption($inputOption);
- $this->application->add($this->buildDocsCommand);
- $this->application->add($this->buildWebsiteCommand);
- $this->application->add($this->buildWebsiteDataCommand);
- $this->application->add($this->clearBuildCacheCommand);
- $this->application->add($this->deployCommand);
- $this->application->add($this->syncRepositoriesCommand);
-
return $this->application->run($input);
}
+ public function getConsoleApplication() : BaseApplication
+ {
+ return $this->application;
+ }
+
public static function getContainer(string $env) : ContainerBuilder
{
$container = new ContainerBuilder();
$container->setParameter('doctrine.website.env', $env);
+ $container->setParameter('doctrine.website.debug', $env !== 'prod');
$container->setParameter('doctrine.website.root_dir', realpath(__DIR__ . '/..'));
$container->setParameter('doctrine.website.config_dir', realpath(__DIR__ . '/../config'));
$container->setParameter('doctrine.website.cache_dir', realpath(__DIR__ . '/../cache'));
$container->setParameter('doctrine.website.github.http_token', getenv('doctrine_website_github_http_token'));
- $loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../config'));
- $loader->load('services.xml');
+ $xmlConfigLoader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../config'));
+ $xmlConfigLoader->load('services.xml');
- $loader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/../config'));
- $loader->load('partners.yml');
- $loader->load('projects.yml');
- $loader->load('routes.yml');
- $loader->load('sponsors.yml');
- $loader->load('team_members.yml');
+ $yamlConfigLoader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/../config'));
+ $yamlConfigLoader->load('routes.yml');
- $loader->load(sprintf('config_%s.yml', $env));
+ $yamlConfigLoader->load(sprintf('config_%s.yml', $env));
if (file_exists($container->getParameter('doctrine.website.config_dir') . '/local.yml')) {
- $loader->load('local.yml');
+ $yamlConfigLoader->load('local.yml');
}
+ $dataLoader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/../data'));
+ $dataLoader->load('events.yml');
+ $dataLoader->load('partners.yml');
+ $dataLoader->load('projects.yml');
+ $dataLoader->load('sponsors.yml');
+ $dataLoader->load('team_members.yml');
+
$container->compile();
+ Stripe\Stripe::setApiKey($container->getParameter('doctrine.website.stripe.secret_key'));
+
+ date_default_timezone_set('America/New_York');
+
return $container;
}
}
diff --git a/lib/Commands/BuildAllCommand.php b/lib/Commands/BuildAllCommand.php
new file mode 100644
index 0000000..b177e97
--- /dev/null
+++ b/lib/Commands/BuildAllCommand.php
@@ -0,0 +1,109 @@
+rootDir = $rootDir;
+ $this->env = $env;
+
+ parent::__construct();
+ }
+
+ protected function configure() : void
+ {
+ $this
+ ->setName('build-all')
+ ->setDescription('Build all website components.')
+ ->addArgument(
+ 'build-dir',
+ InputArgument::OPTIONAL,
+ 'The directory where the build repository is cloned.',
+ sprintf('%s/build-%s', $this->rootDir, $this->env)
+ )
+ ->addOption(
+ 'publish',
+ null,
+ InputOption::VALUE_NONE,
+ 'Publish the build to GitHub Pages.'
+ )
+ ->addOption(
+ 'clear-build-cache',
+ null,
+ InputOption::VALUE_NONE,
+ 'Clear the build cache before building everything.'
+ );
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) : int
+ {
+ $commands = [
+ 'sync-repositories' => [],
+ 'build-website-data' => [],
+ 'build-docs' => [],
+ 'build-website' => [
+ 'build-dir' => $input->getArgument('build-dir'),
+ '--publish' => $input->getOption('publish'),
+ ],
+ ];
+
+ $clearBuildCache = $input->getOption('clear-build-cache');
+ assert(is_bool($clearBuildCache));
+
+ if ($clearBuildCache) {
+ array_unshift($commands, 'clear-build-cache');
+ }
+
+ foreach ($commands as $command => $arguments) {
+ assert(is_string($command));
+ assert(is_array($arguments));
+
+ $output->writeln(sprintf('Executing ./doctrine %s ', $command));
+
+ if ($this->runCommand($command, $arguments) === 1) {
+ $output->writeln(sprintf('Failed running command "%s".', $command));
+ return 1;
+ }
+ }
+
+ return 0;
+ }
+
+ /**
+ * @param mixed[] $arguments
+ */
+ private function runCommand(string $command, array $arguments) : int
+ {
+ $input = new ArrayInput(array_merge(['command' => $command], $arguments));
+
+ return $this->getApplication()->find($command)
+ ->run($input, new ConsoleOutput());
+ }
+}
diff --git a/lib/Commands/BuildWebsiteCommand.php b/lib/Commands/BuildWebsiteCommand.php
index 7e4a56b..9711afa 100644
--- a/lib/Commands/BuildWebsiteCommand.php
+++ b/lib/Commands/BuildWebsiteCommand.php
@@ -11,7 +11,11 @@ use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Finder\Finder;
+use Symfony\Component\Process\Process;
+use function array_map;
use function assert;
+use function date;
use function in_array;
use function ini_set;
use function is_bool;
@@ -20,9 +24,18 @@ use function is_string;
use function mkdir;
use function realpath;
use function sprintf;
+use function time;
class BuildWebsiteCommand extends Command
{
+ private const WATCH_DIRS = [
+ 'config',
+ 'data',
+ 'lib',
+ 'source',
+ 'templates',
+ ];
+
/** @var WebsiteBuilder */
private $websiteBuilder;
@@ -60,6 +73,12 @@ class BuildWebsiteCommand extends Command
null,
InputOption::VALUE_NONE,
'Publish the build to GitHub Pages.'
+ )
+ ->addOption(
+ 'watch',
+ null,
+ InputOption::VALUE_NONE,
+ 'Watch for changes and build the website when changes are detected.'
);
}
@@ -87,8 +106,60 @@ class BuildWebsiteCommand extends Command
throw new InvalidArgumentException(sprintf('Could not find build directory'));
}
- $this->websiteBuilder->build($output, $buildDir, $this->env, $publish);
+ $watch = $input->getOption('watch');
+ assert(is_bool($watch));
+
+ if ($watch) {
+ $this->watch($output);
+ } else {
+ $this->websiteBuilder->build($output, $buildDir, $this->env, $publish);
+ }
return 0;
}
+
+ private function watch(OutputInterface $output) : void
+ {
+ $lastWebsiteBuild = time();
+
+ while (true) {
+ $finder = $this->createWatchFinder($lastWebsiteBuild);
+
+ if (! $finder->hasResults()) {
+ continue;
+ }
+
+ $output->writeln('Found changes');
+
+ $this->buildWebsiteSubProcess($output);
+
+ $lastWebsiteBuild = time();
+ }
+ }
+
+ private function createWatchFinder(int $lastWebsiteBuild) : Finder
+ {
+ return (new Finder())
+ ->in($this->getWatchDirs())
+ ->date(sprintf('>= %s', date('Y-m-d H:i:s', $lastWebsiteBuild)));
+ }
+
+ /**
+ * @return string[]
+ */
+ private function getWatchDirs() : array
+ {
+ return array_map(function (string $dir) : string {
+ return $this->rootDir . '/' . $dir;
+ }, self::WATCH_DIRS);
+ }
+
+ private function buildWebsiteSubProcess(OutputInterface $output) : void
+ {
+ (new Process(['bin/console', 'build-website'], $this->rootDir))
+ ->setTty(true)
+ ->mustRun(static function ($type, $buffer) use ($output) : void {
+ $output->write($buffer);
+ });
+ }
}
diff --git a/lib/Commands/EventParticipantsCommand.php b/lib/Commands/EventParticipantsCommand.php
new file mode 100644
index 0000000..bfade65
--- /dev/null
+++ b/lib/Commands/EventParticipantsCommand.php
@@ -0,0 +1,183 @@
+eventRepository = $eventRepository;
+ $this->eventParticipantRepository = $eventParticipantRepository;
+ $this->getStripeEventParticipants = $getStripeEventParticipants;
+ $this->emailParticipants = $emailParticipants;
+ $this->entityManager = $entityManager;
+
+ parent::__construct();
+ }
+
+ protected function configure() : void
+ {
+ $this
+ ->setName('event-participants')
+ ->setDescription('Command to check for event participants using the Stripe API.')
+ ->addOption(
+ 'save',
+ null,
+ InputOption::VALUE_NONE,
+ 'Save new participants that are found.'
+ )
+ ->addOption(
+ 'email',
+ null,
+ InputOption::VALUE_NONE,
+ 'E-Mail new participants that are found.'
+ );
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) : int
+ {
+ $io = new SymfonyStyle($input, $output);
+
+ $events = $this->eventRepository->findUpcomingEvents();
+
+ foreach ($events as $event) {
+ $io->title(sprintf('%s Participants', $event->getName()));
+
+ $eventParticipants = $this->getStripeEventParticipants->__invoke($event);
+
+ if ($eventParticipants === []) {
+ $io->warning(sprintf('No participants found for "%s". Get out there and market!', $event->getName()));
+
+ continue;
+ }
+
+ $newEventParticipants = $this->getNewEventParticipants($eventParticipants);
+
+ $save = $input->getOption('save');
+ assert(is_bool($save));
+
+ if ($save) {
+ $this->saveEventParticipants($newEventParticipants);
+ }
+
+ $email = $input->getOption('email');
+ assert(is_bool($email));
+
+ if ($email) {
+ $this->emailEventParticipants($io, $event, $newEventParticipants);
+ }
+
+ $header = ['E-Mail', 'Quantity', 'New'];
+
+ $rows = $this->createEventParticipantsTableRows(
+ $eventParticipants,
+ $newEventParticipants
+ );
+
+ $io->table($header, $rows);
+ }
+
+ return 0;
+ }
+
+
+ /**
+ * @param EventParticipant[] $eventParticipants
+ * @param EventParticipant[] $newEventParticipants
+ *
+ * @return mixed[][]
+ */
+ private function createEventParticipantsTableRows(
+ array $eventParticipants,
+ array $newEventParticipants
+ ) : array {
+ return array_map(
+ static function (EventParticipant $participant) use ($newEventParticipants) : array {
+ $isNew = in_array($participant, $newEventParticipants, true) ? 'Yes' : 'No';
+
+ return [$participant->getEmail(), $participant->getQuantity(), $isNew];
+ },
+ $eventParticipants
+ );
+ }
+
+ /**
+ * @param EventParticipant[] $eventParticipants
+ *
+ * @return EventParticipant[]
+ */
+ private function getNewEventParticipants(array $eventParticipants) : array
+ {
+ return array_filter($eventParticipants, function (EventParticipant $eventParticipant) {
+ return $this->eventParticipantRepository
+ ->findOneByEmail($eventParticipant->getEmail()) === null;
+ });
+ }
+
+ /**
+ * @param EventParticipant[] $eventParticipants
+ */
+ private function saveEventParticipants(array $eventParticipants) : void
+ {
+ foreach ($eventParticipants as $eventParticipant) {
+ $this->entityManager->persist($eventParticipant);
+ }
+
+ $this->entityManager->flush();
+ }
+
+ /**
+ * @param EventParticipant[] $eventParticipants
+ */
+ private function emailEventParticipants(
+ SymfonyStyle $io,
+ Event $event,
+ array $eventParticipants
+ ) : void {
+ $this->emailParticipants->__invoke($event, $eventParticipants);
+
+ $io->text(sprintf('E-mailed %d participants.', count($eventParticipants)));
+ }
+}
diff --git a/lib/Controllers/EventsController.php b/lib/Controllers/EventsController.php
new file mode 100644
index 0000000..75460b1
--- /dev/null
+++ b/lib/Controllers/EventsController.php
@@ -0,0 +1,49 @@
+eventRepository = $eventRepository;
+ }
+
+ public function index() : Response
+ {
+ $upcomingEvents = $this->eventRepository->findUpcomingEvents();
+ $pastEvents = $this->eventRepository->findPastEvents();
+
+ return new Response([
+ 'upcomingEvents' => $upcomingEvents,
+ 'pastEvents' => $pastEvents,
+ ]);
+ }
+
+ public function view(string $id, string $slug) : Response
+ {
+ $event = $this->eventRepository->findOneById((int) $id);
+
+ return new Response(['event' => $event], '/event.html.twig');
+ }
+
+ public function cfp(string $id, string $slug) : Response
+ {
+ $event = $this->eventRepository->findOneById((int) $id);
+
+ return new Response(['event' => $event], '/event-cfp.html.twig');
+ }
+
+ public function suggest() : Response
+ {
+ return new Response([]);
+ }
+}
diff --git a/lib/DataSources/TeamMembers.php b/lib/DataSources/ArrayDataSource.php
similarity index 54%
rename from lib/DataSources/TeamMembers.php
rename to lib/DataSources/ArrayDataSource.php
index 96ffc93..81a52c8 100644
--- a/lib/DataSources/TeamMembers.php
+++ b/lib/DataSources/ArrayDataSource.php
@@ -6,17 +6,17 @@ namespace Doctrine\Website\DataSources;
use Doctrine\SkeletonMapper\DataSource\DataSource;
-class TeamMembers implements DataSource
+final class ArrayDataSource implements DataSource
{
/** @var mixed[] */
- private $teamMembers;
+ private $sourceRows;
/**
- * @param mixed[] $teamMembers
+ * @param mixed[] $sourceRows
*/
- public function __construct(array $teamMembers)
+ public function __construct(array $sourceRows)
{
- $this->teamMembers = $teamMembers;
+ $this->sourceRows = $sourceRows;
}
/**
@@ -24,6 +24,6 @@ class TeamMembers implements DataSource
*/
public function getSourceRows() : array
{
- return $this->teamMembers;
+ return $this->sourceRows;
}
}
diff --git a/lib/DataSources/DoctrineUsers.php b/lib/DataSources/DoctrineUsers.php
deleted file mode 100644
index 8848c9f..0000000
--- a/lib/DataSources/DoctrineUsers.php
+++ /dev/null
@@ -1,29 +0,0 @@
-doctrineUsers = $doctrineUsers;
- }
-
- /**
- * @return mixed[][]
- */
- public function getSourceRows() : array
- {
- return $this->doctrineUsers;
- }
-}
diff --git a/lib/DataSources/Partners.php b/lib/DataSources/Partners.php
deleted file mode 100644
index c11629e..0000000
--- a/lib/DataSources/Partners.php
+++ /dev/null
@@ -1,29 +0,0 @@
-partners = $partners;
- }
-
- /**
- * @return mixed[][]
- */
- public function getSourceRows() : array
- {
- return $this->partners;
- }
-}
diff --git a/lib/DataSources/Sponsors.php b/lib/DataSources/Sponsors.php
deleted file mode 100644
index c38dbf7..0000000
--- a/lib/DataSources/Sponsors.php
+++ /dev/null
@@ -1,29 +0,0 @@
-sponsors = $sponsors;
- }
-
- /**
- * @return mixed[][]
- */
- public function getSourceRows() : array
- {
- return $this->sponsors;
- }
-}
diff --git a/lib/Deployer.php b/lib/Deployer.php
index da51c15..456303d 100644
--- a/lib/Deployer.php
+++ b/lib/Deployer.php
@@ -72,9 +72,10 @@ class Deployer
$output->write($buffer);
});
- // build the docs, website and publish
+ // execute migrations, build the website and publish it.
$deployCommand = sprintf(
- 'cd /data/doctrine-website-%s && ./bin/console sync-repositories && ./bin/console build-website-data && ./bin/console build-docs && ./bin/console build-website /data/doctrine-website-build-%s --env=%s --publish',
+ 'cd /data/doctrine-website-%s && ./bin/console migrations:migrate --no-interaction --env=%s && ./bin/console build-all /data/doctrine-website-build-%s --env=%s --publish',
+ $this->env,
$this->env,
$this->env,
$this->env
diff --git a/lib/Email/RenderEmail.php b/lib/Email/RenderEmail.php
new file mode 100644
index 0000000..1da5a63
--- /dev/null
+++ b/lib/Email/RenderEmail.php
@@ -0,0 +1,80 @@
+emogrifier = $emogrifier;
+ $this->templatesDir = $templatesDir;
+ $this->extensions = $extensions;
+ }
+
+ /**
+ * @param mixed[] $parameters
+ */
+ public function __invoke(string $template, array $parameters) : RenderedEmail
+ {
+ $twig = $this->createTwigEnvironment($this->createFilesystemLoader());
+
+ $template = $twig->loadTemplate($template);
+
+ $subject = $template->renderBlock('subject', $parameters);
+ $inlineCss = $template->renderBlock('inline_css', $parameters);
+ $bodyText = $template->renderBlock('full_body_text', $parameters);
+ $bodyHtml = $template->renderBlock('full_body_html', $parameters);
+
+ if (trim($bodyText) === '') {
+ $bodyText = strip_tags($template->renderBlock('body_html', $parameters));
+ }
+
+ $this->emogrifier->setHtml($bodyHtml);
+ $this->emogrifier->setCss($inlineCss);
+
+ $mergedHtml = $this->emogrifier->emogrify();
+
+ return new RenderedEmail($subject, $bodyText, $mergedHtml);
+ }
+
+ private function createTwigEnvironment(LoaderInterface $loader) : Environment
+ {
+ $twig = new Environment($loader, ['strict_variables' => true]);
+
+ foreach ($this->extensions as $extension) {
+ $twig->addExtension($extension);
+ }
+
+ return $twig;
+ }
+
+ private function createFilesystemLoader() : FilesystemLoader
+ {
+ return new FilesystemLoader($this->templatesDir);
+ }
+}
diff --git a/lib/Email/RenderedEmail.php b/lib/Email/RenderedEmail.php
new file mode 100644
index 0000000..4acd6bc
--- /dev/null
+++ b/lib/Email/RenderedEmail.php
@@ -0,0 +1,39 @@
+subject = $subject;
+ $this->bodyText = $bodyText;
+ $this->bodyHtml = $bodyHtml;
+ }
+
+ public function getSubject() : string
+ {
+ return $this->subject;
+ }
+
+ public function getBodyText() : string
+ {
+ return $this->bodyText;
+ }
+
+ public function getBodyHtml() : string
+ {
+ return $this->bodyHtml;
+ }
+}
diff --git a/lib/Email/SendEmail.php b/lib/Email/SendEmail.php
new file mode 100644
index 0000000..677fe05
--- /dev/null
+++ b/lib/Email/SendEmail.php
@@ -0,0 +1,49 @@
+site = $site;
+ $this->sendGrid = $sendGrid;
+ $this->renderEmail = $renderEmail;
+ }
+
+ /**
+ * @param mixed[] $parameters
+ */
+ public function __invoke(string $to, string $template, array $parameters = []) : void
+ {
+ $parameters['site'] = $this->site;
+
+ $renderedEmail = $this->renderEmail->__invoke($template, $parameters);
+
+ $email = new SendGrid\Mail\Mail();
+ $email->setFrom('doctrine@doctrine-project.org', 'Doctrine');
+ $email->setSubject($renderedEmail->getSubject());
+ $email->addTo($to);
+ $email->addContent('text/plain', $renderedEmail->getBodyText());
+ $email->addContent('text/html', $renderedEmail->getBodyHtml());
+
+ $this->sendGrid->send($email);
+ }
+}
diff --git a/lib/Event/EmailParticipants.php b/lib/Event/EmailParticipants.php
new file mode 100644
index 0000000..91021eb
--- /dev/null
+++ b/lib/Event/EmailParticipants.php
@@ -0,0 +1,37 @@
+sendEmail = $sendEmail;
+ }
+
+ /**
+ * @param EventParticipant[] $participants
+ */
+ public function __invoke(Event $event, array $participants) : void
+ {
+ foreach ($participants as $participant) {
+ $this->sendEmail->__invoke(
+ $participant->getEmail(),
+ 'emails/events/participant-ticket.html.twig',
+ [
+ 'event' => $event,
+ 'participant' => $participant,
+ ]
+ );
+ }
+ }
+}
diff --git a/lib/Event/GetStripeEventParticipants.php b/lib/Event/GetStripeEventParticipants.php
new file mode 100644
index 0000000..f8e6c39
--- /dev/null
+++ b/lib/Event/GetStripeEventParticipants.php
@@ -0,0 +1,109 @@
+getAllEventStripeCheckouts($event);
+
+ $participants = [];
+
+ $customers = [];
+
+ foreach ($stripeCheckouts as $stripeCheckout) {
+ $item = $stripeCheckout['data']['object']['display_items'][0];
+ $sku = $item['sku']['id'];
+ $quantity = $item['quantity'];
+ $customerId = $stripeCheckout['data']['object']['customer'];
+
+ if ($sku !== $event->getSku()) {
+ continue;
+ }
+
+ if (! isset($customers[$customerId])) {
+ $customers[$customerId] = Stripe\Customer::retrieve($customerId);
+ }
+
+ $customer = $customers[$customerId];
+
+ if (! isset($participants[$customer['email']])) {
+ $participants[$customer['email']] = [
+ 'email' => $customer['email'],
+ 'quantity' => $quantity,
+ ];
+ } else {
+ $participants[$customer['email']]['quantity'] += $quantity;
+ }
+ }
+
+ return array_map(static function (array $participant) use ($event) : EventParticipant {
+ return new EventParticipant(
+ $event,
+ $participant['email'],
+ $participant['quantity']
+ );
+ }, array_values($participants));
+ }
+
+ /**
+ * @return mixed[][]
+ */
+ private function getAllEventStripeCheckouts(Event $event) : array
+ {
+ $allEventStripeCheckouts = [];
+ $startingAfter = null;
+
+ while (true) {
+ $eventStripeCheckouts = $this->getEventStripeCheckouts($event, $startingAfter);
+
+ $eventStripeCheckoutsArray = iterator_to_array($eventStripeCheckouts);
+
+ $allEventStripeCheckouts = array_merge(
+ $allEventStripeCheckouts,
+ $eventStripeCheckoutsArray
+ );
+
+ if ($eventStripeCheckouts['has_more'] === false) {
+ break;
+ }
+
+ $startingAfter = end($eventStripeCheckoutsArray)['id'];
+ }
+
+ return $allEventStripeCheckouts;
+ }
+
+ private function getEventStripeCheckouts(
+ Event $event,
+ ?string $startingAfter = null
+ ) : Stripe\Collection {
+ $parameters = [
+ 'created' => ['gt' => strtotime('1 year ago')],
+ 'limit' => 100,
+ 'type' => 'checkout.session.completed',
+ ];
+
+ if ($startingAfter !== null) {
+ $parameters['starting_after'] = $startingAfter;
+ }
+
+ return Stripe\Event::all($parameters);
+ }
+}
diff --git a/lib/Hydrators/BlogPostHydrator.php b/lib/Hydrators/BlogPostHydrator.php
new file mode 100644
index 0000000..c6cb971
--- /dev/null
+++ b/lib/Hydrators/BlogPostHydrator.php
@@ -0,0 +1,39 @@
+url = (string) $data['url'] ?? '';
+ $this->slug = (string) $data['slug'] ?? '';
+ $this->title = (string) $data['title'] ?? '';
+ $this->authorName = (string) $data['authorName'] ?? '';
+ $this->authorEmail = (string) $data['authorEmail'] ?? '';
+ $this->contents = (string) $data['contents'] ?? '';
+ $this->date = $data['date'] ?? new DateTimeImmutable();
+ }
+}
diff --git a/lib/Hydrators/ContributorHydrator.php b/lib/Hydrators/ContributorHydrator.php
new file mode 100644
index 0000000..8d13383
--- /dev/null
+++ b/lib/Hydrators/ContributorHydrator.php
@@ -0,0 +1,38 @@
+teamMember = $data['teamMember'] ?? null;
+ $this->github = (string) ($data['github'] ?? '');
+ $this->avatarUrl = (string) ($data['avatarUrl'] ?? '');
+ $this->numCommits = (int) ($data['numCommits'] ?? 0);
+ $this->numAdditions = (int) ($data['numAdditions'] ?? 0);
+ $this->numDeletions = (int) ($data['numDeletions'] ?? 0);
+ $this->projects = $data['projects'] ?? [];
+ }
+}
diff --git a/lib/Hydrators/DoctrineUserHydrator.php b/lib/Hydrators/DoctrineUserHydrator.php
new file mode 100644
index 0000000..92a2179
--- /dev/null
+++ b/lib/Hydrators/DoctrineUserHydrator.php
@@ -0,0 +1,28 @@
+name = (string) $data['name'];
+ $this->url = (string) $data['url'];
+ }
+}
diff --git a/lib/Hydrators/EventHydrator.php b/lib/Hydrators/EventHydrator.php
new file mode 100644
index 0000000..191c91d
--- /dev/null
+++ b/lib/Hydrators/EventHydrator.php
@@ -0,0 +1,169 @@
+ 'test',
+ 'prod' => 'prod',
+ 'staging' => 'test',
+ 'test' => 'test',
+ ];
+
+ /** @var EventParticipantRepository */
+ private $eventParticipantRepository;
+
+ /** @var string */
+ private $env;
+
+ public function __construct(
+ ObjectManagerInterface $objectManager,
+ EventParticipantRepository $eventParticipantRepository,
+ string $env
+ ) {
+ parent::__construct($objectManager);
+
+ $this->eventParticipantRepository = $eventParticipantRepository;
+ $this->env = $env;
+ }
+
+ protected function getClassName() : string
+ {
+ return Event::class;
+ }
+
+ /**
+ * @param mixed[] $data
+ */
+ protected function doHydrate(array $data) : void
+ {
+ $this->id = (int) ($data['id'] ?? 0);
+ $this->type = (string) ($data['type'] ?? EventType::WEBINAR);
+
+ if ($this->type === EventType::CONFERENCE) {
+ if (! isset($data['location'])) {
+ throw new InvalidArgumentException(
+ sprintf('Event type of "%s" must provide a "location" field.', $this->type)
+ );
+ }
+
+ $this->location = new EventLocation(
+ (string) ($data['location']['name'] ?? ''),
+ new Address(
+ (string) ($data['location']['address']['line1'] ?? ''),
+ (string) ($data['location']['address']['line2'] ?? ''),
+ (string) ($data['location']['address']['city'] ?? ''),
+ (string) ($data['location']['address']['state'] ?? ''),
+ (string) ($data['location']['address']['zipCode'] ?? ''),
+ (string) ($data['location']['address']['countryCode'] ?? '')
+ )
+ );
+ }
+
+ if (isset($data['sku'])) {
+ if (! isset(self::ENV_SKU_MAP[$this->env])) {
+ throw new InvalidArgumentException(sprintf('Invalid env "%s".', $this->env));
+ }
+
+ $skuKey = self::ENV_SKU_MAP[$this->env];
+
+ if (! isset($data['sku'][$skuKey])) {
+ throw new InvalidArgumentException(
+ sprintf('Sku key with "%s" does not exist.', $skuKey)
+ );
+ }
+
+ $this->sku = (string) ($data['sku'][$skuKey] ?? '');
+ } else {
+ $this->sku = '';
+ }
+
+ $this->name = (string) ($data['name'] ?? '');
+ $this->slug = (string) ($data['slug'] ?? '');
+ $this->joinUrl = (string) ($data['joinUrl'] ?? '');
+
+ $this->cfp = new EventCfp(
+ (string) ($data['cfp']['googleFormId'] ?? ''),
+ new DateTimeRange(
+ new DateTimeImmutable($data['cfp']['startDate'] ?? ''),
+ new DateTimeImmutable($data['cfp']['endDate'] ?? '')
+ )
+ );
+
+ $this->sponsors = new EventSponsors($data);
+ $this->speakers = new EventSpeakers($data, $this->objectManager);
+ $this->schedule = new EventSchedule($data, $this->speakers);
+
+ if ($data['schedule'] !== []) {
+ $firstSlot = current($data['schedule']);
+ $lastSlot = end($data['schedule']);
+
+ $this->dateTimeRange = new DateTimeRange(
+ new DateTimeImmutable($firstSlot['startDate'] ?? ''),
+ new DateTimeImmutable($lastSlot['endDate'] ?? '')
+ );
+ } else {
+ $this->dateTimeRange = new DateTimeRange(
+ new DateTimeImmutable($data['startDate'] ?? ''),
+ new DateTimeImmutable($data['endDate'] ?? '')
+ );
+ }
+
+ $this->registrationDateTimeRange = new DateTimeRange(
+ isset($data['registrationStartDate'])
+ ? new DateTimeImmutable($data['registrationStartDate'])
+ : $this->dateTimeRange->getStart(),
+ isset($data['registrationEndDate'])
+ ? new DateTimeImmutable($data['registrationEndDate'])
+ : $this->dateTimeRange->getEnd()
+ );
+
+ $this->description = (string) ($data['description'] ?? '');
+
+ $this->price = (float) ($data['price'] ?? 0.00);
+
+ $this->participants = new EventParticipants(
+ $data['id'],
+ $this->eventParticipantRepository
+ );
+ }
+}
diff --git a/lib/Hydrators/ModelHydrator.php b/lib/Hydrators/ModelHydrator.php
new file mode 100644
index 0000000..18c16a8
--- /dev/null
+++ b/lib/Hydrators/ModelHydrator.php
@@ -0,0 +1,81 @@
+objectManager = $objectManager;
+ $this->classMetadata = $this->objectManager->getClassMetadata($this->getClassName());
+ }
+
+ /**
+ * @param mixed[] $data
+ */
+ abstract protected function doHydrate(array $data) : void;
+
+ abstract protected function getClassName() : string;
+
+ /**
+ * @param object $object
+ * @param mixed[] $data
+ *
+ * @phpcsSuppress SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingParameterTypeHint
+ */
+ public function hydrate($object, array $data) : void
+ {
+ $this->object = $object;
+
+ $this->doHydrate($data);
+ }
+
+ /**
+ * @return mixed
+ */
+ public function __get(string $field)
+ {
+ return $this->getReflectionProperty($field)->getValue($this->object);
+ }
+
+ /**
+ * @param mixed $value
+ */
+ public function __set(string $field, $value) : void
+ {
+ $this->getReflectionProperty($field)->setValue($this->object, $value);
+ }
+
+ private function getReflectionProperty(string $field) : ReflectionProperty
+ {
+ $key = $this->getClassName() . '::' . $field;
+
+ if (! isset($this->reflectionProperties[$key])) {
+ $reflectionProperty = $this->classMetadata->getReflectionClass()->getProperty($field);
+ $reflectionProperty->setAccessible(true);
+
+ $this->reflectionProperties[$key] = $reflectionProperty;
+ }
+
+ return $this->reflectionProperties[$key];
+ }
+}
diff --git a/lib/Hydrators/PartnerHydrator.php b/lib/Hydrators/PartnerHydrator.php
new file mode 100644
index 0000000..7eaac10
--- /dev/null
+++ b/lib/Hydrators/PartnerHydrator.php
@@ -0,0 +1,57 @@
+name = (string) ($data['name'] ?? '');
+ $this->slug = (string) ($data['slug'] ?? '');
+ $this->url = (string) ($data['url'] ?? '');
+ $this->logo = (string) ($data['logo'] ?? '');
+ $this->bio = (string) ($data['bio'] ?? '');
+ $this->featured = (bool) ($data['featured'] ?? false);
+
+ $this->details = new PartnerDetails(
+ (string) ($data['details']['label'] ?? ''),
+ $data['details']['items'] ?? []
+ );
+
+ $this->utmParameters = new UtmParameters(
+ array_merge(
+ [
+ 'utm_source' => 'doctrine',
+ 'utm_medium' => 'website',
+ 'utm_campaign' => 'partners',
+ ],
+ $data['utmParameters'] ?? []
+ )
+ );
+ }
+}
diff --git a/lib/Hydrators/ProjectContributorHydrator.php b/lib/Hydrators/ProjectContributorHydrator.php
new file mode 100644
index 0000000..a85fc13
--- /dev/null
+++ b/lib/Hydrators/ProjectContributorHydrator.php
@@ -0,0 +1,41 @@
+teamMember = $data['teamMember'] ?? null;
+ $this->projectSlug = (string) ($data['projectSlug'] ?? '');
+ $this->project = $data['project'] ?? new Project();
+ $this->github = (string) ($data['github'] ?? '');
+ $this->avatarUrl = (string) ($data['avatarUrl'] ?? '');
+ $this->numCommits = (int) ($data['numCommits'] ?? 0);
+ $this->numAdditions = (int) ($data['numAdditions'] ?? 0);
+ $this->numDeletions = (int) ($data['numDeletions'] ?? 0);
+ }
+}
diff --git a/lib/Hydrators/ProjectHydrator.php b/lib/Hydrators/ProjectHydrator.php
new file mode 100644
index 0000000..abbca27
--- /dev/null
+++ b/lib/Hydrators/ProjectHydrator.php
@@ -0,0 +1,90 @@
+active = (bool) ($data['active'] ?? true);
+ $this->archived = (bool) ($data['archived'] ?? false);
+ $this->name = (string) ($data['name'] ?? '');
+ $this->shortName = (string) ($data['shortName'] ?? $this->name);
+ $this->slug = (string) ($data['slug'] ?? '');
+ $this->docsSlug = (string) ($data['docsSlug'] ?? $this->slug);
+ $this->composerPackageName = (string) ($data['composerPackageName'] ?? '');
+ $this->repositoryName = (string) ($data['repositoryName'] ?? '');
+ $this->isIntegration = (bool) ($data['integration'] ?? false);
+ $this->integrationFor = (string) ($data['integrationFor'] ?? '');
+ $this->docsRepositoryName = (string) ($data['docsRepositoryName'] ?? $this->repositoryName);
+ $this->docsPath = (string) ($data['docsPath'] ?? '/docs');
+ $this->codePath = (string) ($data['codePath'] ?? '/lib');
+ $this->description = (string) ($data['description'] ?? '');
+ $this->keywords = $data['keywords'] ?? [];
+
+ if (! isset($data['versions'])) {
+ return;
+ }
+
+ $versions = [];
+
+ foreach ($data['versions'] as $version) {
+ $versions[] = $version instanceof ProjectVersion
+ ? $version
+ : new ProjectVersion($version);
+ }
+
+ $this->versions = $versions;
+
+ if ($this->isIntegration) {
+ $this->projectIntegrationType = new ProjectIntegrationType($data['integrationType']);
+ }
+
+ $this->projectStats = new ProjectStats(
+ (int) ($data['packagistData']['package']['github_stars'] ?? 0),
+ (int) ($data['packagistData']['package']['github_watchers'] ?? 0),
+ (int) ($data['packagistData']['package']['github_forks'] ?? 0),
+ (int) ($data['packagistData']['package']['github_open_issues'] ?? 0),
+ (int) ($data['packagistData']['package']['dependents'] ?? 0),
+ (int) ($data['packagistData']['package']['suggesters'] ?? 0),
+ (int) ($data['packagistData']['package']['downloads']['total'] ?? 0),
+ (int) ($data['packagistData']['package']['downloads']['monthly'] ?? 0),
+ (int) ($data['packagistData']['package']['downloads']['daily'] ?? 0)
+ );
+ }
+}
diff --git a/lib/Hydrators/SitemapPageHydrator.php b/lib/Hydrators/SitemapPageHydrator.php
new file mode 100644
index 0000000..eb20b2a
--- /dev/null
+++ b/lib/Hydrators/SitemapPageHydrator.php
@@ -0,0 +1,29 @@
+url = (string) ($sitemapPage['url'] ?? '');
+ $this->date = $sitemapPage['date'] ?? new DateTimeImmutable();
+ }
+}
diff --git a/lib/Hydrators/SponsorHydrator.php b/lib/Hydrators/SponsorHydrator.php
new file mode 100644
index 0000000..1e5dd48
--- /dev/null
+++ b/lib/Hydrators/SponsorHydrator.php
@@ -0,0 +1,45 @@
+name = (string) ($data['name'] ?? '');
+ $this->url = (string) ($data['url'] ?? '');
+
+ $this->utmParameters = new UtmParameters(
+ array_merge(
+ [
+ 'utm_source' => 'doctrine',
+ 'utm_medium' => 'website',
+ 'utm_campaign' => 'sponsors',
+ ],
+ $data['utmParameters'] ?? []
+ )
+ );
+
+ $this->highlighted = (bool) ($data['highlighted'] ?? '');
+ }
+}
diff --git a/lib/Hydrators/TeamMemberHydrator.php b/lib/Hydrators/TeamMemberHydrator.php
new file mode 100644
index 0000000..5677fdb
--- /dev/null
+++ b/lib/Hydrators/TeamMemberHydrator.php
@@ -0,0 +1,57 @@
+name = (string) ($data['name'] ?? '');
+ $this->github = (string) ($data['github'] ?? '');
+ $this->twitter = (string) ($data['twitter'] ?? '');
+ $this->avatarUrl = (string) ($data['avatarUrl'] ?? '');
+ $this->website = (string) ($data['website'] ?? '');
+ $this->location = (string) ($data['location'] ?? '');
+ $this->maintains = $data['maintains'] ?? [];
+ $this->consultant = (bool) ($data['consultant'] ?? false);
+ $this->headshot = (string) ($data['headshot'] ?? '');
+ $this->bio = (string) ($data['bio'] ?? '');
+ $this->contributor = function (string $github) : Contributor {
+ $contributorRepository = $this->objectManager
+ ->getRepository(Contributor::class);
+
+ assert($contributorRepository instanceof ContributorRepository);
+
+ return $contributorRepository->findOneByGithub($github);
+ };
+ }
+}
diff --git a/lib/Migrations/Version20190515005142.php b/lib/Migrations/Version20190515005142.php
new file mode 100644
index 0000000..a740449
--- /dev/null
+++ b/lib/Migrations/Version20190515005142.php
@@ -0,0 +1,29 @@
+addSql('CREATE TABLE event_participants (id INT AUTO_INCREMENT NOT NULL, email VARCHAR(255) NOT NULL, quantity INT NOT NULL, eventId INT NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB');
+ }
+
+ public function down(Schema $schema) : void
+ {
+ $this->addSql('DROP TABLE event_participants');
+ }
+}
diff --git a/lib/Model/Address.php b/lib/Model/Address.php
new file mode 100644
index 0000000..27069ab
--- /dev/null
+++ b/lib/Model/Address.php
@@ -0,0 +1,87 @@
+line1 = $line1;
+ $this->line2 = $line2;
+ $this->city = $city;
+ $this->state = $state;
+ $this->zipCode = $zipCode;
+ $this->countryCode = $countryCode;
+ }
+
+ public function getLine1() : string
+ {
+ return $this->line1;
+ }
+
+ public function getLine2() : string
+ {
+ return $this->line2;
+ }
+
+ public function getCity() : string
+ {
+ return $this->city;
+ }
+
+ public function getState() : string
+ {
+ return $this->state;
+ }
+
+ public function getZipCode() : string
+ {
+ return $this->zipCode;
+ }
+
+ public function getCountryCode() : string
+ {
+ return $this->countryCode;
+ }
+
+ public function getString() : string
+ {
+ return sprintf(
+ '%s %s %s, %s %s %s',
+ $this->line1,
+ $this->line2,
+ $this->city,
+ $this->state,
+ $this->zipCode,
+ $this->countryCode
+ );
+ }
+}
diff --git a/lib/Model/BlogPost.php b/lib/Model/BlogPost.php
index 3437435..75d8052 100644
--- a/lib/Model/BlogPost.php
+++ b/lib/Model/BlogPost.php
@@ -5,12 +5,10 @@ declare(strict_types=1);
namespace Doctrine\Website\Model;
use DateTimeImmutable;
-use Doctrine\SkeletonMapper\Hydrator\HydratableInterface;
use Doctrine\SkeletonMapper\Mapping\ClassMetadataInterface;
use Doctrine\SkeletonMapper\Mapping\LoadMetadataInterface;
-use Doctrine\SkeletonMapper\ObjectManagerInterface;
-class BlogPost implements HydratableInterface, LoadMetadataInterface
+class BlogPost implements LoadMetadataInterface
{
/** @var string */
private $url;
@@ -56,20 +54,6 @@ class BlogPost implements HydratableInterface, LoadMetadataInterface
$metadata->setIdentifier(['slug']);
}
- /**
- * @param mixed[] $project
- */
- public function hydrate(array $project, ObjectManagerInterface $objectManager) : void
- {
- $this->url = (string) $project['url'] ?? '';
- $this->slug = (string) $project['slug'] ?? '';
- $this->title = (string) $project['title'] ?? '';
- $this->authorName = (string) $project['authorName'] ?? '';
- $this->authorEmail = (string) $project['authorEmail'] ?? '';
- $this->contents = (string) $project['contents'] ?? '';
- $this->date = $project['date'] ?? new DateTimeImmutable();
- }
-
public function getUrl() : string
{
return $this->url;
diff --git a/lib/Model/Contributor.php b/lib/Model/Contributor.php
index d1cc43d..06170c2 100644
--- a/lib/Model/Contributor.php
+++ b/lib/Model/Contributor.php
@@ -4,12 +4,10 @@ declare(strict_types=1);
namespace Doctrine\Website\Model;
-use Doctrine\SkeletonMapper\Hydrator\HydratableInterface;
use Doctrine\SkeletonMapper\Mapping\ClassMetadataInterface;
use Doctrine\SkeletonMapper\Mapping\LoadMetadataInterface;
-use Doctrine\SkeletonMapper\ObjectManagerInterface;
-class Contributor implements HydratableInterface, LoadMetadataInterface, CommitterStats
+class Contributor implements LoadMetadataInterface, CommitterStats
{
/** @var TeamMember|null */
private $teamMember;
@@ -37,20 +35,6 @@ class Contributor implements HydratableInterface, LoadMetadataInterface, Committ
$metadata->setIdentifier(['github']);
}
- /**
- * @param mixed[] $contributor
- */
- public function hydrate(array $contributor, ObjectManagerInterface $objectManager) : void
- {
- $this->teamMember = $contributor['teamMember'] ?? null;
- $this->github = (string) ($contributor['github'] ?? '');
- $this->avatarUrl = (string) ($contributor['avatarUrl'] ?? '');
- $this->numCommits = (int) ($contributor['numCommits'] ?? 0);
- $this->numAdditions = (int) ($contributor['numAdditions'] ?? 0);
- $this->numDeletions = (int) ($contributor['numDeletions'] ?? 0);
- $this->projects = $contributor['projects'] ?? [];
- }
-
public function getTeamMember() : ?TeamMember
{
return $this->teamMember;
diff --git a/lib/Model/DateTimeRange.php b/lib/Model/DateTimeRange.php
new file mode 100644
index 0000000..302cee7
--- /dev/null
+++ b/lib/Model/DateTimeRange.php
@@ -0,0 +1,109 @@
+start = $start;
+ $this->end = $end;
+ $this->now = $now ?? new DateTimeImmutable();
+ }
+
+ public function getStart() : DateTimeImmutable
+ {
+ return $this->start;
+ }
+
+ public function getEnd() : DateTimeImmutable
+ {
+ return $this->end;
+ }
+
+ public function isNow() : bool
+ {
+ return $this->start <= $this->now
+ && $this->end > $this->now;
+ }
+
+ public function isOver() : bool
+ {
+ return $this->end < $this->now;
+ }
+
+ public function isUpcoming() : bool
+ {
+ return $this->start > $this->now;
+ }
+
+ public function getNumDays() : int
+ {
+ $days = (int) $this->end
+ ->diff($this->start)
+ ->days;
+
+ if ($days > 0) {
+ return $days + 1;
+ }
+
+ return 0;
+ }
+
+ public function getNumHours() : int
+ {
+ $diff = $this->end->diff($this->start);
+
+ $numDays = $this->getNumDays();
+
+ if ($numDays === 1) {
+ return $diff->h;
+ }
+
+ return $diff->h + ($this->getNumDays() * 24);
+ }
+
+ public function getNumMinutes() : int
+ {
+ $diff = $this->end->diff($this->start);
+
+ $minutes = $diff->days * 24 * 60;
+ $minutes += $diff->h * 60;
+ $minutes += $diff->i;
+
+ return (int) $minutes;
+ }
+
+ public function getDuration() : string
+ {
+ $numDays = $this->getNumDays();
+
+ if ($numDays === 0) {
+ $numMinutes = $this->getNumMinutes();
+
+ if ($numMinutes >= 60) {
+ return $this->getNumHours() . '-hour';
+ }
+
+ return $numMinutes . '-minute';
+ }
+
+ return $this->getNumDays() . '-day';
+ }
+}
diff --git a/lib/Model/DoctrineUser.php b/lib/Model/DoctrineUser.php
index 4cf1da8..435293a 100644
--- a/lib/Model/DoctrineUser.php
+++ b/lib/Model/DoctrineUser.php
@@ -4,12 +4,10 @@ declare(strict_types=1);
namespace Doctrine\Website\Model;
-use Doctrine\SkeletonMapper\Hydrator\HydratableInterface;
use Doctrine\SkeletonMapper\Mapping\ClassMetadataInterface;
use Doctrine\SkeletonMapper\Mapping\LoadMetadataInterface;
-use Doctrine\SkeletonMapper\ObjectManagerInterface;
-class DoctrineUser implements HydratableInterface, LoadMetadataInterface
+class DoctrineUser implements LoadMetadataInterface
{
/** @var string */
private $name;
@@ -22,15 +20,6 @@ class DoctrineUser implements HydratableInterface, LoadMetadataInterface
$metadata->setIdentifier(['name']);
}
- /**
- * @param mixed[] $doctrineUser
- */
- public function hydrate(array $doctrineUser, ObjectManagerInterface $objectManager) : void
- {
- $this->name = (string) $doctrineUser['name'];
- $this->url = (string) $doctrineUser['url'];
- }
-
public function getName() : string
{
return $this->name;
diff --git a/lib/Model/Entity/EventParticipant.php b/lib/Model/Entity/EventParticipant.php
new file mode 100644
index 0000000..86da264
--- /dev/null
+++ b/lib/Model/Entity/EventParticipant.php
@@ -0,0 +1,65 @@
+eventId = $event->getId();
+ $this->email = $email;
+ $this->quantity = $quantity;
+ }
+
+ public function getId() : ?int
+ {
+ return $this->id;
+ }
+
+ public function getEmail() : string
+ {
+ return $this->email;
+ }
+
+ public function getQuantity() : int
+ {
+ return $this->quantity;
+ }
+
+ public function getEventId() : int
+ {
+ return $this->eventId;
+ }
+}
diff --git a/lib/Model/Entity/EventParticipantRepository.php b/lib/Model/Entity/EventParticipantRepository.php
new file mode 100644
index 0000000..4607721
--- /dev/null
+++ b/lib/Model/Entity/EventParticipantRepository.php
@@ -0,0 +1,29 @@
+findOneBy(['email' => $email]);
+
+ return $eventParticipant;
+ }
+
+ /**
+ * @return EventParticipant[]
+ */
+ public function findByEventId(int $eventId) : array
+ {
+ /** @var EventParticipant[] $eventParticipants */
+ $eventParticipants = $this->findBy(['eventId' => $eventId]);
+
+ return $eventParticipants;
+ }
+}
diff --git a/lib/Model/Event.php b/lib/Model/Event.php
new file mode 100644
index 0000000..c5c334b
--- /dev/null
+++ b/lib/Model/Event.php
@@ -0,0 +1,165 @@
+setIdentifier(['id']);
+ }
+
+ public function getId() : int
+ {
+ return $this->id;
+ }
+
+ public function isWebinar() : bool
+ {
+ return $this->type === EventType::WEBINAR;
+ }
+
+ public function isConference() : bool
+ {
+ return $this->type === EventType::CONFERENCE;
+ }
+
+ public function getSku() : string
+ {
+ return $this->sku;
+ }
+
+ public function getName() : string
+ {
+ return $this->name;
+ }
+
+ public function getSlug() : string
+ {
+ return $this->slug;
+ }
+
+ public function getJoinUrl() : string
+ {
+ return $this->joinUrl;
+ }
+
+ public function getDates() : DateTimeRange
+ {
+ return $this->dateTimeRange;
+ }
+
+ public function getRegistrationDates() : DateTimeRange
+ {
+ return $this->registrationDateTimeRange;
+ }
+
+ public function getStartDate() : DateTimeImmutable
+ {
+ return $this->dateTimeRange->getStart();
+ }
+
+ public function getEndDate() : DateTimeImmutable
+ {
+ return $this->dateTimeRange->getEnd();
+ }
+
+ public function getCfp() : EventCfp
+ {
+ return $this->cfp;
+ }
+
+ public function getLocation() : ?EventLocation
+ {
+ return $this->location;
+ }
+
+ public function getSponsors() : EventSponsors
+ {
+ return $this->sponsors;
+ }
+
+ public function getSpeakers() : EventSpeakers
+ {
+ return $this->speakers;
+ }
+
+ public function getSchedule() : EventSchedule
+ {
+ return $this->schedule;
+ }
+
+ public function getParticipants() : EventParticipants
+ {
+ return $this->participants;
+ }
+
+ public function getDescription() : string
+ {
+ return $this->description;
+ }
+
+ public function getPrice() : float
+ {
+ return $this->price;
+ }
+
+ public function isFree() : bool
+ {
+ return $this->price === 0.00;
+ }
+}
diff --git a/lib/Model/EventCfp.php b/lib/Model/EventCfp.php
new file mode 100644
index 0000000..374b696
--- /dev/null
+++ b/lib/Model/EventCfp.php
@@ -0,0 +1,47 @@
+googleFormId = $googleFormId;
+ $this->dateTimeRange = $dateTimeRange;
+ }
+
+ public function exists() : bool
+ {
+ return $this->googleFormId !== '';
+ }
+
+ public function getGoogleFormUrl() : string
+ {
+ if (! $this->exists()) {
+ throw new LogicException('Cannot call EventCfp::getGoogleFormUrl() when no googleFormId is set.');
+ }
+
+ return sprintf('https://docs.google.com/forms/d/e/%s/viewform', $this->googleFormId);
+ }
+
+ public function getEmbeddedGoogleFormUrl() : string
+ {
+ return sprintf('%s?embedded=true', $this->getGoogleFormUrl());
+ }
+
+ public function getDates() : DateTimeRange
+ {
+ return $this->dateTimeRange;
+ }
+}
diff --git a/lib/Model/EventLocation.php b/lib/Model/EventLocation.php
new file mode 100644
index 0000000..2a32613
--- /dev/null
+++ b/lib/Model/EventLocation.php
@@ -0,0 +1,30 @@
+name = $name;
+ $this->address = $address;
+ }
+
+ public function getName() : string
+ {
+ return $this->name;
+ }
+
+ public function getAddress() : Address
+ {
+ return $this->address;
+ }
+}
diff --git a/lib/Model/EventParticipants.php b/lib/Model/EventParticipants.php
new file mode 100644
index 0000000..9a2ea7a
--- /dev/null
+++ b/lib/Model/EventParticipants.php
@@ -0,0 +1,32 @@
+eventId = $eventId;
+ $this->eventParticipantRepository = $eventParticipantRepository;
+ }
+
+ protected function doInitialize() : void
+ {
+ $eventParticipants = $this->eventParticipantRepository
+ ->findByEventId($this->eventId);
+
+ $this->collection = new ArrayCollection($eventParticipants);
+ }
+}
diff --git a/lib/Model/EventSchedule.php b/lib/Model/EventSchedule.php
new file mode 100644
index 0000000..9097989
--- /dev/null
+++ b/lib/Model/EventSchedule.php
@@ -0,0 +1,53 @@
+event = $event;
+ $this->speakers = $speakers;
+ }
+
+ protected function doInitialize() : void
+ {
+ $slots = [];
+
+ foreach ($this->event['schedule'] as $slot) {
+ if (! isset($this->speakers[$slot['topicSlug']])) {
+ throw new InvalidArgumentException(sprintf(
+ 'Could not find speaker with topicSlug "%s".',
+ $slot['topicSlug']
+ ));
+ }
+
+ $eventSpeaker = $this->speakers[$slot['topicSlug']];
+
+ $slots[] = new EventScheduleSlot(
+ $eventSpeaker,
+ new DateTimeImmutable($slot['startDate'] ?? ''),
+ new DateTimeImmutable($slot['endDate'] ?? '')
+ );
+ }
+
+ $this->collection = new ArrayCollection($slots);
+ }
+}
diff --git a/lib/Model/EventScheduleSlot.php b/lib/Model/EventScheduleSlot.php
new file mode 100644
index 0000000..03368e8
--- /dev/null
+++ b/lib/Model/EventScheduleSlot.php
@@ -0,0 +1,44 @@
+speaker = $speaker;
+ $this->startDate = $startDate;
+ $this->endDate = $endDate;
+ }
+
+ public function getSpeaker() : EventSpeaker
+ {
+ return $this->speaker;
+ }
+
+ public function getStartDate() : DateTimeImmutable
+ {
+ return $this->startDate;
+ }
+
+ public function getEndDate() : DateTimeImmutable
+ {
+ return $this->endDate;
+ }
+}
diff --git a/lib/Model/EventSpeaker.php b/lib/Model/EventSpeaker.php
new file mode 100644
index 0000000..ef5777d
--- /dev/null
+++ b/lib/Model/EventSpeaker.php
@@ -0,0 +1,84 @@
+name = $name;
+ $this->avatarUrl = $avatarUrl;
+ $this->topic = $topic;
+ $this->topicSlug = $topicSlug;
+ $this->description = $description;
+ $this->youTubeVideoId = $youTubeVideoId;
+ }
+
+ public function getName() : string
+ {
+ return $this->name;
+ }
+
+ public function getAvatarUrl() : string
+ {
+ return $this->avatarUrl;
+ }
+
+ public function getTopic() : string
+ {
+ return $this->topic;
+ }
+
+ public function getTopicSlug() : string
+ {
+ return $this->topicSlug;
+ }
+
+ public function getDescription() : string
+ {
+ return $this->description;
+ }
+
+ public function hasYouTubeVideo() : bool
+ {
+ return $this->youTubeVideoId !== '';
+ }
+
+ public function getYouTubeVideoId() : string
+ {
+ return $this->youTubeVideoId;
+ }
+
+ public function getYouTubeUrl() : string
+ {
+ return sprintf('https://www.youtube.com/watch?v=%s', $this->youTubeVideoId);
+ }
+}
diff --git a/lib/Model/EventSpeakers.php b/lib/Model/EventSpeakers.php
new file mode 100644
index 0000000..02bd02c
--- /dev/null
+++ b/lib/Model/EventSpeakers.php
@@ -0,0 +1,58 @@
+event = $event;
+ $this->objectManager = $objectManager;
+ }
+
+ protected function doInitialize() : void
+ {
+ $teamMemberRepository = $this->objectManager->getRepository(TeamMember::class);
+ assert($teamMemberRepository instanceof TeamMemberRepository);
+
+ $speakers = [];
+
+ foreach ($this->event['speakers'] ?? [] as $speaker) {
+ $speakerName = (string) ($speaker['name'] ?? '');
+
+ $teamMember = $speakerName !== ''
+ ? $teamMemberRepository->findOneByGithub($speakerName)
+ : null;
+
+ $topicSlug = (string) ($speaker['topicSlug'] ?? '');
+
+ $speakers[$topicSlug] = new EventSpeaker(
+ $teamMember !== null ? $teamMember->getName() : $speakerName,
+ $teamMember !== null ? $teamMember->getAvatarUrl() : (string) ($speaker['avatarUrl'] ?? ''),
+ (string) ($speaker['topic'] ?? ''),
+ $topicSlug,
+ (string) ($speaker['description'] ?? ''),
+ (string) ($speaker['youTubeVideoId'] ?? '')
+ );
+ }
+
+ $this->collection = new ArrayCollection($speakers);
+ }
+}
diff --git a/lib/Model/EventSponsor.php b/lib/Model/EventSponsor.php
new file mode 100644
index 0000000..c94cd17
--- /dev/null
+++ b/lib/Model/EventSponsor.php
@@ -0,0 +1,51 @@
+name = $name;
+ $this->url = $url;
+ $this->logo = $logo;
+ $this->utmParameters = $utmParameters;
+ }
+
+ public function getName() : string
+ {
+ return $this->name;
+ }
+
+ public function getUrl() : string
+ {
+ return $this->url;
+ }
+
+ /**
+ * @param string[] $parameters
+ */
+ public function getUrlWithUtmParameters(array $parameters = []) : string
+ {
+ return $this->utmParameters->buildUrl($this->url, $parameters);
+ }
+
+ public function getLogo() : string
+ {
+ return $this->logo;
+ }
+}
diff --git a/lib/Model/EventSponsors.php b/lib/Model/EventSponsors.php
new file mode 100644
index 0000000..fc1159a
--- /dev/null
+++ b/lib/Model/EventSponsors.php
@@ -0,0 +1,48 @@
+event = $event;
+ }
+
+ protected function doInitialize() : void
+ {
+ $sponsors = [];
+
+ foreach ($this->event['sponsors'] ?? [] as $sponsor) {
+ $sponsors[] = new EventSponsor(
+ (string) ($sponsor['name'] ?? ''),
+ (string) ($sponsor['url'] ?? ''),
+ (string) ($sponsor['logo'] ?? ''),
+ new UtmParameters(
+ array_merge(
+ [
+ 'utm_source' => 'doctrine',
+ 'utm_medium' => 'website',
+ 'utm_campaign' => $this->event['slug'],
+ ],
+ $sponsor['utmParameters'] ?? []
+ )
+ )
+ );
+ }
+
+ $this->collection = new ArrayCollection($sponsors);
+ }
+}
diff --git a/lib/Model/EventType.php b/lib/Model/EventType.php
new file mode 100644
index 0000000..f3b1d76
--- /dev/null
+++ b/lib/Model/EventType.php
@@ -0,0 +1,15 @@
+setIdentifier(['slug']);
}
- /**
- * @param mixed[] $partner
- */
- public function hydrate(array $partner, ObjectManagerInterface $objectManager) : void
- {
- $this->name = (string) ($partner['name'] ?? '');
- $this->slug = (string) ($partner['slug'] ?? '');
- $this->url = (string) ($partner['url'] ?? '');
- $this->utmParameters = new UtmParameters(
- array_merge(
- [
- 'utm_source' => 'doctrine',
- 'utm_medium' => 'website',
- 'utm_campaign' => 'partners',
- ],
- $partner['utmParameters'] ?? []
- )
- );
- $this->logo = (string) ($partner['logo'] ?? '');
- $this->bio = (string) ($partner['bio'] ?? '');
- $this->details = new PartnerDetails(
- (string) ($partner['details']['label'] ?? ''),
- $partner['details']['items'] ?? []
- );
- $this->featured = (bool) ($partner['featured'] ?? false);
- }
-
public function getName() : string
{
return $this->name;
diff --git a/lib/Model/Project.php b/lib/Model/Project.php
index 82dd7f6..de0b860 100644
--- a/lib/Model/Project.php
+++ b/lib/Model/Project.php
@@ -5,16 +5,14 @@ declare(strict_types=1);
namespace Doctrine\Website\Model;
use Closure;
-use Doctrine\SkeletonMapper\Hydrator\HydratableInterface;
use Doctrine\SkeletonMapper\Mapping\ClassMetadataInterface;
use Doctrine\SkeletonMapper\Mapping\LoadMetadataInterface;
-use Doctrine\SkeletonMapper\ObjectManagerInterface;
use InvalidArgumentException;
use function array_filter;
use function array_values;
use function sprintf;
-class Project implements HydratableInterface, LoadMetadataInterface
+class Project implements LoadMetadataInterface
{
/** @var ProjectIntegrationType|null */
private $projectIntegrationType;
@@ -70,75 +68,11 @@ class Project implements HydratableInterface, LoadMetadataInterface
/** @var ProjectVersion[] */
private $versions = [];
- /**
- * @param mixed[] $project
- */
- public function __construct(array $project)
- {
- $this->doHydrate($project);
- }
-
public static function loadMetadata(ClassMetadataInterface $metadata) : void
{
$metadata->setIdentifier(['slug']);
}
- /**
- * @param mixed[] $project
- */
- public function hydrate(array $project, ObjectManagerInterface $objectManager) : void
- {
- $this->doHydrate($project);
- }
-
- /**
- * @param mixed[] $project
- */
- public function doHydrate(array $project) : void
- {
- $this->active = (bool) ($project['active'] ?? true);
- $this->archived = (bool) ($project['archived'] ?? false);
- $this->name = (string) ($project['name'] ?? '');
- $this->shortName = (string) ($project['shortName'] ?? $this->name);
- $this->slug = (string) ($project['slug'] ?? '');
- $this->docsSlug = (string) ($project['docsSlug'] ?? $this->slug);
- $this->composerPackageName = (string) ($project['composerPackageName'] ?? '');
- $this->repositoryName = (string) ($project['repositoryName'] ?? '');
- $this->isIntegration = (bool) ($project['integration'] ?? false);
- $this->integrationFor = (string) ($project['integrationFor'] ?? '');
- $this->docsRepositoryName = (string) ($project['docsRepositoryName'] ?? $this->repositoryName);
- $this->docsPath = (string) ($project['docsPath'] ?? '/docs');
- $this->codePath = (string) ($project['codePath'] ?? '/lib');
- $this->description = (string) ($project['description'] ?? '');
- $this->keywords = $project['keywords'] ?? [];
-
- if (! isset($project['versions'])) {
- return;
- }
-
- foreach ($project['versions'] as $version) {
- $this->versions[] = $version instanceof ProjectVersion
- ? $version
- : new ProjectVersion($version);
- }
-
- if ($this->isIntegration) {
- $this->projectIntegrationType = new ProjectIntegrationType($project['integrationType']);
- }
-
- $this->projectStats = new ProjectStats(
- (int) ($project['packagistData']['package']['github_stars'] ?? 0),
- (int) ($project['packagistData']['package']['github_watchers'] ?? 0),
- (int) ($project['packagistData']['package']['github_forks'] ?? 0),
- (int) ($project['packagistData']['package']['github_open_issues'] ?? 0),
- (int) ($project['packagistData']['package']['dependents'] ?? 0),
- (int) ($project['packagistData']['package']['suggesters'] ?? 0),
- (int) ($project['packagistData']['package']['downloads']['total'] ?? 0),
- (int) ($project['packagistData']['package']['downloads']['monthly'] ?? 0),
- (int) ($project['packagistData']['package']['downloads']['daily'] ?? 0)
- );
- }
-
public function getProjectIntegrationType() : ?ProjectIntegrationType
{
return $this->projectIntegrationType;
diff --git a/lib/Model/ProjectContributor.php b/lib/Model/ProjectContributor.php
index 6add572..e106ff7 100644
--- a/lib/Model/ProjectContributor.php
+++ b/lib/Model/ProjectContributor.php
@@ -4,12 +4,10 @@ declare(strict_types=1);
namespace Doctrine\Website\Model;
-use Doctrine\SkeletonMapper\Hydrator\HydratableInterface;
use Doctrine\SkeletonMapper\Mapping\ClassMetadataInterface;
use Doctrine\SkeletonMapper\Mapping\LoadMetadataInterface;
-use Doctrine\SkeletonMapper\ObjectManagerInterface;
-class ProjectContributor implements HydratableInterface, LoadMetadataInterface, CommitterStats
+class ProjectContributor implements LoadMetadataInterface, CommitterStats
{
/** @var TeamMember|null */
private $teamMember;
@@ -40,21 +38,6 @@ class ProjectContributor implements HydratableInterface, LoadMetadataInterface,
$metadata->setIdentifier(['projectSlug', 'github']);
}
- /**
- * @param mixed[] $projectContributor
- */
- public function hydrate(array $projectContributor, ObjectManagerInterface $objectManager) : void
- {
- $this->teamMember = $projectContributor['teamMember'] ?? null;
- $this->projectSlug = (string) ($projectContributor['projectSlug'] ?? '');
- $this->project = $projectContributor['project'] ?? new Project([]);
- $this->github = (string) ($projectContributor['github'] ?? '');
- $this->avatarUrl = (string) ($projectContributor['avatarUrl'] ?? '');
- $this->numCommits = (int) ($projectContributor['numCommits'] ?? 0);
- $this->numAdditions = (int) ($projectContributor['numAdditions'] ?? 0);
- $this->numDeletions = (int) ($projectContributor['numDeletions'] ?? 0);
- }
-
public function getTeamMember() : ?TeamMember
{
return $this->teamMember;
diff --git a/lib/Model/SitemapPage.php b/lib/Model/SitemapPage.php
index ce13f00..dbd3255 100644
--- a/lib/Model/SitemapPage.php
+++ b/lib/Model/SitemapPage.php
@@ -5,12 +5,10 @@ declare(strict_types=1);
namespace Doctrine\Website\Model;
use DateTimeImmutable;
-use Doctrine\SkeletonMapper\Hydrator\HydratableInterface;
use Doctrine\SkeletonMapper\Mapping\ClassMetadataInterface;
use Doctrine\SkeletonMapper\Mapping\LoadMetadataInterface;
-use Doctrine\SkeletonMapper\ObjectManagerInterface;
-class SitemapPage implements HydratableInterface, LoadMetadataInterface
+class SitemapPage implements LoadMetadataInterface
{
/** @var string */
private $url;
@@ -29,15 +27,6 @@ class SitemapPage implements HydratableInterface, LoadMetadataInterface
$metadata->setIdentifier(['url']);
}
- /**
- * @param mixed[] $sitemapPage
- */
- public function hydrate(array $sitemapPage, ObjectManagerInterface $objectManager) : void
- {
- $this->url = (string) ($sitemapPage['url'] ?? '');
- $this->date = $sitemapPage['date'] ?? new DateTimeImmutable();
- }
-
public function getUrl() : string
{
return $this->url;
diff --git a/lib/Model/Sponsor.php b/lib/Model/Sponsor.php
index 2a15515..9874147 100644
--- a/lib/Model/Sponsor.php
+++ b/lib/Model/Sponsor.php
@@ -4,13 +4,10 @@ declare(strict_types=1);
namespace Doctrine\Website\Model;
-use Doctrine\SkeletonMapper\Hydrator\HydratableInterface;
use Doctrine\SkeletonMapper\Mapping\ClassMetadataInterface;
use Doctrine\SkeletonMapper\Mapping\LoadMetadataInterface;
-use Doctrine\SkeletonMapper\ObjectManagerInterface;
-use function array_merge;
-final class Sponsor implements HydratableInterface, LoadMetadataInterface
+final class Sponsor implements LoadMetadataInterface
{
/** @var string */
private $name;
@@ -29,26 +26,6 @@ final class Sponsor implements HydratableInterface, LoadMetadataInterface
$metadata->setIdentifier(['name']);
}
- /**
- * @param mixed[] $sponsor
- */
- public function hydrate(array $sponsor, ObjectManagerInterface $objectManager) : void
- {
- $this->name = (string) ($sponsor['name'] ?? '');
- $this->url = (string) ($sponsor['url'] ?? '');
- $this->utmParameters = new UtmParameters(
- array_merge(
- [
- 'utm_source' => 'doctrine',
- 'utm_medium' => 'website',
- 'utm_campaign' => 'sponsors',
- ],
- $sponsor['utmParameters'] ?? []
- )
- );
- $this->highlighted = (bool) ($sponsor['highlighted'] ?? '');
- }
-
public function getName() : string
{
return $this->name;
diff --git a/lib/Model/TeamMember.php b/lib/Model/TeamMember.php
index 082b54c..51ca737 100644
--- a/lib/Model/TeamMember.php
+++ b/lib/Model/TeamMember.php
@@ -5,15 +5,11 @@ declare(strict_types=1);
namespace Doctrine\Website\Model;
use Closure;
-use Doctrine\SkeletonMapper\Hydrator\HydratableInterface;
use Doctrine\SkeletonMapper\Mapping\ClassMetadataInterface;
use Doctrine\SkeletonMapper\Mapping\LoadMetadataInterface;
-use Doctrine\SkeletonMapper\ObjectManagerInterface;
-use Doctrine\Website\Repositories\ContributorRepository;
-use function assert;
use function in_array;
-class TeamMember implements HydratableInterface, LoadMetadataInterface, CommitterStats
+class TeamMember implements LoadMetadataInterface, CommitterStats
{
/** @var string */
private $name;
@@ -53,31 +49,6 @@ class TeamMember implements HydratableInterface, LoadMetadataInterface, Committe
$metadata->setIdentifier(['github']);
}
- /**
- * @param mixed[] $teamMember
- */
- public function hydrate(array $teamMember, ObjectManagerInterface $objectManager) : void
- {
- $this->name = (string) ($teamMember['name'] ?? '');
- $this->github = (string) ($teamMember['github'] ?? '');
- $this->twitter = (string) ($teamMember['twitter'] ?? '');
- $this->avatarUrl = (string) ($teamMember['avatarUrl'] ?? '');
- $this->website = (string) ($teamMember['website'] ?? '');
- $this->location = (string) ($teamMember['location'] ?? '');
- $this->maintains = $teamMember['maintains'] ?? [];
- $this->consultant = (bool) ($teamMember['consultant'] ?? false);
- $this->headshot = (string) ($teamMember['headshot'] ?? '');
- $this->bio = (string) ($teamMember['bio'] ?? '');
- $this->contributor = static function (string $github) use ($objectManager) : Contributor {
- $contributorRepository = $objectManager
- ->getRepository(Contributor::class);
-
- assert($contributorRepository instanceof ContributorRepository);
-
- return $contributorRepository->findOneByGithub($github);
- };
- }
-
public function getName() : string
{
return $this->name;
diff --git a/lib/Repositories/EventRepository.php b/lib/Repositories/EventRepository.php
new file mode 100644
index 0000000..293b3b9
--- /dev/null
+++ b/lib/Repositories/EventRepository.php
@@ -0,0 +1,55 @@
+findOneBy(['id' => $id]);
+
+ if ($event === null) {
+ throw new InvalidArgumentException(sprintf('Could not find Event with id "%s"', $id));
+ }
+
+ return $event;
+ }
+
+ /**
+ * @return Event[]
+ */
+ public function findUpcomingEvents() : array
+ {
+ /** @var Event[] $events */
+ $events = $this->findBy([], ['startDate' => 'asc']);
+
+ $criteria = Criteria::create()
+ ->where(Criteria::expr()->gt('startDate', new DateTimeImmutable()));
+
+ return (new ArrayCollection($events))->matching($criteria)->toArray();
+ }
+
+ /**
+ * @return Event[]
+ */
+ public function findPastEvents() : array
+ {
+ /** @var Event[] $events */
+ $events = $this->findBy([], ['endDate' => 'desc']);
+
+ $criteria = Criteria::create()
+ ->where(Criteria::expr()->lt('endDate', new DateTimeImmutable()));
+
+ return (new ArrayCollection($events))->matching($criteria)->toArray();
+ }
+}
diff --git a/lib/Requests/EventRequests.php b/lib/Requests/EventRequests.php
new file mode 100644
index 0000000..0822f29
--- /dev/null
+++ b/lib/Requests/EventRequests.php
@@ -0,0 +1,38 @@
+eventRepository = $eventRepository;
+ }
+
+ public function getEvents() : RequestCollection
+ {
+ /** @var Event[] $events */
+ $events = $this->eventRepository->findAll();
+
+ $requests = [];
+
+ foreach ($events as $event) {
+ $requests[] = [
+ 'id' => $event->getId(),
+ 'slug' => $event->getSlug(),
+ ];
+ }
+
+ return new ArrayRequestCollection($requests);
+ }
+}
diff --git a/lib/Site.php b/lib/Site.php
new file mode 100644
index 0000000..ed2e7cd
--- /dev/null
+++ b/lib/Site.php
@@ -0,0 +1,44 @@
+assetsUrl = $assetsUrl;
+ }
+
+ public function getAssetsUrl() : string
+ {
+ return $this->assetsUrl;
+ }
+}
diff --git a/lib/Twig/MainExtension.php b/lib/Twig/MainExtension.php
index 34dc925..a93d67f 100644
--- a/lib/Twig/MainExtension.php
+++ b/lib/Twig/MainExtension.php
@@ -36,12 +36,21 @@ class MainExtension extends Twig_Extension
/** @var string */
private $webpackBuildDir;
- public function __construct(Parsedown $parsedown, AssetIntegrityGenerator $assetIntegrityGenerator, string $sourceDir, string $webpackBuildDir)
- {
+ /** @var string */
+ private $stripePublishableKey;
+
+ public function __construct(
+ Parsedown $parsedown,
+ AssetIntegrityGenerator $assetIntegrityGenerator,
+ string $sourceDir,
+ string $webpackBuildDir,
+ string $stripePublishableKey
+ ) {
$this->parsedown = $parsedown;
$this->assetIntegrityGenerator = $assetIntegrityGenerator;
$this->sourceDir = $sourceDir;
$this->webpackBuildDir = $webpackBuildDir;
+ $this->stripePublishableKey = $stripePublishableKey;
}
/**
@@ -55,6 +64,7 @@ class MainExtension extends Twig_Extension
new Twig_SimpleFunction('get_webpack_asset_url', [$this, 'getWebpackAssetUrl']),
new Twig_SimpleFunction('get_asset_integrity', [$this->assetIntegrityGenerator, 'getAssetIntegrity']),
new Twig_SimpleFunction('get_webpack_asset_integrity', [$this->assetIntegrityGenerator, 'getWebpackAssetIntegrity']),
+ new Twig_SimpleFunction('get_stripe_publishable_key', [$this, 'getStripePublishableKey']),
];
}
@@ -108,6 +118,11 @@ class MainExtension extends Twig_Extension
return $string;
}
+ public function getStripePublishableKey() : string
+ {
+ return $this->stripePublishableKey;
+ }
+
private function getAssetCacheBuster(string $path, string $rootPath) : string
{
$assetPath = realpath($rootPath . '/' . $path);
diff --git a/lib/WebsiteBuilder.php b/lib/WebsiteBuilder.php
index 64d56db..00411e8 100644
--- a/lib/WebsiteBuilder.php
+++ b/lib/WebsiteBuilder.php
@@ -15,6 +15,7 @@ use Symfony\Component\Filesystem\Filesystem;
use function chdir;
use function file_exists;
use function file_put_contents;
+use function getcwd;
use function glob;
use function in_array;
use function is_dir;
@@ -108,7 +109,7 @@ class WebsiteBuilder
$this->createProjectVersionAliases($buildDir);
- $this->copyWebsiteBuildData($buildDir);
+ $this->copyWebsiteBuildData($output, $buildDir);
if ($publish) {
$output->writeln(' - publishing build');
@@ -129,27 +130,38 @@ class WebsiteBuilder
*/
private function buildWebsite(OutputInterface $output, string $buildDir, bool $isPublishableEnv) : void
{
+ $output->writeln(sprintf(' - clearing build directory %s ', $buildDir));
+
// cleanup the build directory
$this->filesystem->remove(glob($buildDir . '/*'));
// Move webpack assets into build directory
$this->buildWebpackAssets($output, $buildDir, $isPublishableEnv);
- $this->sourceFilesBuilder->buildSourceFiles(
- $this->sourceFileRepository->getSourceFiles($buildDir)
- );
+ $output->writeln(' - calculating source files to build');
+
+ $sourceFiles = $this->sourceFileRepository->getSourceFiles($buildDir);
+
+ $output->writeln(sprintf(' - building source files to %s ', $buildDir));
+
+ $this->sourceFilesBuilder->buildSourceFiles($sourceFiles);
}
private function buildWebpackAssets(OutputInterface $output, string $buildDir, bool $isPublishableEnv) : void
{
- $output->writeln(sprintf(' - running npm run %s ', $isPublishableEnv ? 'build' : 'dev'));
+ $output->writeln(sprintf(' - running npm run %s ', $isPublishableEnv ? 'build' : 'dev'));
+
$this->filesystem->remove(glob($this->webpackBuildDir . '/*'));
+
$process = $this->processFactory->run(sprintf(
'cd %s && npm run %s',
$this->rootDir,
$isPublishableEnv ? 'build' : 'dev'
));
- $output->write($process->getOutput());
+
+ if ($output->isVerbose()) {
+ $output->write($process->getOutput());
+ }
// Copy built assets if this is a publishable build
if ($isPublishableEnv) {
@@ -181,9 +193,18 @@ class WebsiteBuilder
}
}
- private function copyWebsiteBuildData(string $buildDir) : void
+ private function copyWebsiteBuildData(OutputInterface $output, string $buildDir) : void
{
- $this->filesystem->mirror($this->cacheDir . '/data', $buildDir . '/website-data');
+ $from = $this->cacheDir . '/data';
+ $to = $buildDir . '/website-data';
+
+ $output->writeln(sprintf(
+ ' - copying website build data from %s to %s .',
+ $from,
+ $to
+ ));
+
+ $this->filesystem->mirror($from, $to);
}
private function createDocsProjectVersionAlias(
@@ -207,16 +228,22 @@ class WebsiteBuilder
return;
}
+ $cwd = getcwd();
+
chdir($dir);
if (file_exists($alias)) {
unlink($alias);
}
- if (! file_exists($version->getSlug())) {
+ if (file_exists($version->getSlug())) {
+ symlink($version->getSlug(), $alias);
+ }
+
+ if ($cwd === false) {
return;
}
- symlink($version->getSlug(), $alias);
+ chdir($cwd);
}
}
diff --git a/phpunit b/phpunit
new file mode 100755
index 0000000..607b3a5
--- /dev/null
+++ b/phpunit
@@ -0,0 +1,16 @@
+#!/usr/bin/env php
+Events
+
+ {% if upcomingEvents|length %}
+
+ {% for event in upcomingEvents %}
+
+ {% endfor %}
+
+
+ Don't see something that you are interested in? Suggest an event topic you would like to see and we will see what we can do!
+ {% else %}
+ No upcoming events are currently scheduled. Come back soon to check for new events! You may also suggest an event topic you would like to see.
+ {% endif %}
+
+ {% if pastEvents|length %}
+ Past Events
+
+
+ {% endif %}
+
+ {% include "carbonad-standard.html.twig" %}
+{% endblock %}
diff --git a/source/events/suggest.html b/source/events/suggest.html
new file mode 100644
index 0000000..c9fa4c6
--- /dev/null
+++ b/source/events/suggest.html
@@ -0,0 +1,12 @@
+{% block title %}Suggest a Doctrine Event{% endblock %}
+
+{% block content %}
+
+
+ Events
+ Suggest an Event
+
+
+
+
+{% endblock %}
diff --git a/source/js/event.js b/source/js/event.js
new file mode 100644
index 0000000..e14918c
--- /dev/null
+++ b/source/js/event.js
@@ -0,0 +1,59 @@
+export default function() {
+ function openEventModal(label, message) {
+ $('#event-modal-label').html(label);
+ $('#event-modal-body').html(message);
+
+ $('#event-modal').modal();
+ }
+
+ if (window.location.hash === '#success') {
+ openEventModal(
+ 'Purchase Successful',
+ 'Your ticket purchase for ' + window.event.name + ' was successful! You will be e-mailed a receipt for your purchase immediately and details for joining the event will e-mailed 1 week before the event is scheduled to start.'
+ );
+
+ window.location.hash = '';
+ }
+
+ if (window.location.hash === '#canceled') {
+ openEventModal(
+ 'Purchase Failure',
+ 'Oh no! Your ticket purchase for ' + window.event.name + ' was not successful. Please give it another try.'
+ );
+
+ window.location.hash = '';
+ }
+
+ if (window.location.hash === '#thanks') {
+ openEventModal(
+ 'Event Finished',
+ 'Thanks for attending ' + window.event.name + ' ! Keep your eyes open for more events in the future.'
+ );
+
+ window.location.hash = '';
+ }
+
+ $('#checkout-button').on('click', function() {
+ $(this).addClass('disabled');
+ });
+
+ $.getScript('https://js.stripe.com/v3', () => {
+ var stripe = Stripe(window.stripePublishableKey);
+
+ var checkoutButton = document.getElementById('checkout-button');
+
+ checkoutButton.addEventListener('click', function () {
+ stripe.redirectToCheckout({
+ items: [{sku: window.event.sku, quantity: 1}],
+ successUrl: window.event.url + '#success',
+ cancelUrl: window.event.url + '#canceled',
+ })
+ .then(function (result) {
+ if (result.error) {
+ var displayError = document.getElementById('stripe-error-message');
+ displayError.textContent = result.error.message;
+ }
+ });
+ });
+ });
+};
diff --git a/source/js/index.js b/source/js/index.js
index c2aa041..6e95c86 100644
--- a/source/js/index.js
+++ b/source/js/index.js
@@ -20,6 +20,12 @@ if ($('#sidebar').length > 0) {
});
}
+if (typeof window.event === 'object') {
+ import(/* webpackChunkName: "event" */ './event').then(module => {
+ module.default();
+ });
+}
+
window.googleTranslateElementInit = () => {
$('#google_translate_element').html('');
@@ -33,4 +39,5 @@ window.googleTranslateElementInit = () => {
googleAnalyticsEvent('Translate', 'click', language);
});
};
-$.getScript('https://translate.google.com/translate_a/element.js?cb=googleTranslateElementInit');
\ No newline at end of file
+
+$.getScript('https://translate.google.com/translate_a/element.js?cb=googleTranslateElementInit');
diff --git a/source/styles/index.scss b/source/styles/index.scss
index c4dd5fc..5a2de78 100644
--- a/source/styles/index.scss
+++ b/source/styles/index.scss
@@ -464,6 +464,10 @@ div.jsactive pre {
height: 14px;
}
+.modal-backdrop.in {
+ opacity: 0.7;
+}
+
@media (max-width: 900px) {
.sidebar {
display: none;
diff --git a/templates/emails/css/framework.css b/templates/emails/css/framework.css
new file mode 100644
index 0000000..19b55fc
--- /dev/null
+++ b/templates/emails/css/framework.css
@@ -0,0 +1,286 @@
+/* -------------------------------------
+ GLOBAL RESETS
+------------------------------------- */
+
+/*All the styling goes here*/
+
+img {
+ border: none;
+ -ms-interpolation-mode: bicubic;
+ max-width: 100%;
+}
+body {
+ background-color: #f6f6f6;
+ font-family: sans-serif;
+ -webkit-font-smoothing: antialiased;
+ font-size: 14px;
+ line-height: 1.4;
+ margin: 0;
+ padding: 0;
+ -ms-text-size-adjust: 100%;
+ -webkit-text-size-adjust: 100%;
+}
+table {
+ border-collapse: separate;
+ mso-table-lspace: 0pt;
+ mso-table-rspace: 0pt;
+ width: 100%; }
+ table td {
+ font-family: sans-serif;
+ font-size: 14px;
+ vertical-align: top;
+}
+/* -------------------------------------
+ BODY & CONTAINER
+------------------------------------- */
+.body {
+ background-color: #f6f6f6;
+ width: 100%;
+}
+/* Set a max-width, and make it display as block so it will automatically stretch to that width, but will also shrink down on a phone or something */
+.container {
+ display: block;
+ margin: 0 auto !important;
+ /* makes it centered */
+ max-width: 580px;
+ padding: 10px;
+ width: 580px;
+}
+/* This should also be a block element, so that it will fill 100% of the .container */
+.content {
+ box-sizing: border-box;
+ display: block;
+ margin: 0 auto;
+ max-width: 580px;
+ padding: 10px;
+}
+/* -------------------------------------
+ HEADER, FOOTER, MAIN
+------------------------------------- */
+.main {
+ background: #ffffff;
+ border-radius: 3px;
+ width: 100%;
+}
+.wrapper {
+ box-sizing: border-box;
+ padding: 20px;
+}
+.content-block {
+ padding-bottom: 10px;
+ padding-top: 10px;
+}
+.footer {
+ clear: both;
+ margin-top: 10px;
+ text-align: center;
+ width: 100%;
+}
+ .footer td,
+ .footer p,
+ .footer span,
+ .footer a {
+ color: #999999;
+ font-size: 12px;
+ text-align: center;
+}
+/* -------------------------------------
+ TYPOGRAPHY
+------------------------------------- */
+h1,
+h2,
+h3,
+h4 {
+ color: #000000;
+ font-family: sans-serif;
+ font-weight: 400;
+ line-height: 1.4;
+ margin: 0;
+ margin-bottom: 30px;
+}
+h1 {
+ font-size: 35px;
+ font-weight: 300;
+ text-align: center;
+ text-transform: capitalize;
+}
+p,
+ul,
+ol {
+ font-family: sans-serif;
+ font-size: 14px;
+ font-weight: normal;
+ margin: 0;
+ margin-bottom: 15px;
+}
+ p li,
+ ul li,
+ ol li {
+ list-style-position: inside;
+ margin-left: 5px;
+}
+a {
+ color: #3498db;
+ text-decoration: underline;
+}
+/* -------------------------------------
+ BUTTONS
+------------------------------------- */
+.btn {
+ box-sizing: border-box;
+ width: 100%; }
+ .btn > tbody > tr > td {
+ padding-bottom: 15px; }
+ .btn table {
+ width: auto;
+}
+ .btn table td {
+ background-color: #ffffff;
+ border-radius: 5px;
+ text-align: center;
+}
+ .btn a {
+ background-color: #ffffff;
+ border: solid 1px #3498db;
+ border-radius: 5px;
+ box-sizing: border-box;
+ color: #3498db;
+ cursor: pointer;
+ display: inline-block;
+ font-size: 14px;
+ font-weight: bold;
+ margin: 0;
+ padding: 12px 25px;
+ text-decoration: none;
+ text-transform: capitalize;
+}
+.btn-primary table td {
+ background-color: #3498db;
+}
+.btn-primary a {
+ background-color: #3498db;
+ border-color: #3498db;
+ color: #ffffff;
+}
+/* -------------------------------------
+ OTHER STYLES THAT MIGHT BE USEFUL
+------------------------------------- */
+.last {
+ margin-bottom: 0;
+}
+.first {
+ margin-top: 0;
+}
+.align-center {
+ text-align: center;
+}
+.align-right {
+ text-align: right;
+}
+.align-left {
+ text-align: left;
+}
+.clear {
+ clear: both;
+}
+.mt0 {
+ margin-top: 0;
+}
+.mb0 {
+ margin-bottom: 0;
+}
+.preheader {
+ color: transparent;
+ display: none;
+ height: 0;
+ max-height: 0;
+ max-width: 0;
+ opacity: 0;
+ overflow: hidden;
+ mso-hide: all;
+ visibility: hidden;
+ width: 0;
+}
+.powered-by a {
+ text-decoration: none;
+}
+hr {
+ border: 0;
+ border-bottom: 1px solid #f6f6f6;
+ margin: 20px 0;
+}
+/* -------------------------------------
+ RESPONSIVE AND MOBILE FRIENDLY STYLES
+------------------------------------- */
+@media only screen and (max-width: 620px) {
+ table[class=body] h1 {
+ font-size: 28px !important;
+ margin-bottom: 10px !important;
+ }
+ table[class=body] p,
+ table[class=body] ul,
+ table[class=body] ol,
+ table[class=body] td,
+ table[class=body] span,
+ table[class=body] a {
+ font-size: 16px !important;
+ }
+ table[class=body] .wrapper,
+ table[class=body] .article {
+ padding: 10px !important;
+ }
+ table[class=body] .content {
+ padding: 0 !important;
+ }
+ table[class=body] .container {
+ padding: 0 !important;
+ width: 100% !important;
+ }
+ table[class=body] .main {
+ border-left-width: 0 !important;
+ border-radius: 0 !important;
+ border-right-width: 0 !important;
+ }
+ table[class=body] .btn table {
+ width: 100% !important;
+ }
+ table[class=body] .btn a {
+ width: 100% !important;
+ }
+ table[class=body] .img-responsive {
+ height: auto !important;
+ max-width: 100% !important;
+ width: auto !important;
+ }
+}
+/* -------------------------------------
+ PRESERVE THESE STYLES IN THE HEAD
+------------------------------------- */
+@media all {
+ .ExternalClass {
+ width: 100%;
+ }
+ .ExternalClass,
+ .ExternalClass p,
+ .ExternalClass span,
+ .ExternalClass font,
+ .ExternalClass td,
+ .ExternalClass div {
+ line-height: 100%;
+ }
+ .apple-link a {
+ color: inherit !important;
+ font-family: inherit !important;
+ font-size: inherit !important;
+ font-weight: inherit !important;
+ line-height: inherit !important;
+ text-decoration: none !important;
+ }
+ .btn-primary table td:hover {
+ background-color: #34495e !important;
+ }
+ .btn-primary a:hover {
+ background-color: #34495e !important;
+ border-color: #34495e !important;
+ }
+}
diff --git a/templates/emails/css/layout.css b/templates/emails/css/layout.css
new file mode 100644
index 0000000..0a3d15f
--- /dev/null
+++ b/templates/emails/css/layout.css
@@ -0,0 +1,8 @@
+.header {
+ text-align: center;
+}
+
+.header img {
+ height: 60px;
+ margin-top: 20px;
+}
diff --git a/templates/emails/email.html.twig b/templates/emails/email.html.twig
new file mode 100644
index 0000000..134cca2
--- /dev/null
+++ b/templates/emails/email.html.twig
@@ -0,0 +1,13 @@
+{% block subject '' %}
+
+{% block head_css '' %}
+
+{% block inline_css '' %}
+
+{% block full_body_text %}
+ {% block body_text '' %}
+{% endblock %}
+
+{% block full_body_html %}
+ {% block body_html '' %}
+{% endblock %}
diff --git a/templates/emails/events/participant-ticket.html.twig b/templates/emails/events/participant-ticket.html.twig
new file mode 100644
index 0000000..166fef3
--- /dev/null
+++ b/templates/emails/events/participant-ticket.html.twig
@@ -0,0 +1,21 @@
+{% extends 'emails/layout.html.twig' %}
+
+{% block subject %}
+ [Doctrine Event] {{ event.name }}
+{% endblock %}
+
+{% block body_html %}
+ Hi {{ participant.email }},
+
+ You are receiving this e-mail because you registered for the Doctrine Event named {{ event.name }} .
+
+ {% if event.conference %}
+ This event is hosted at {{ event.location.name }} which is located at {{ event.location.address.string }} .
+ {% else %}
+ You can join the event at the following url: {{ event.joinUrl }}
+ {% endif %}
+
+ If you need help, e-mail us at events@doctrine-project.org .
+
+ Thanks, Doctrine Team!
+{% endblock %}
diff --git a/templates/emails/layout.html.twig b/templates/emails/layout.html.twig
new file mode 100644
index 0000000..543b441
--- /dev/null
+++ b/templates/emails/layout.html.twig
@@ -0,0 +1,64 @@
+{% extends 'emails/email.html.twig' %}
+
+{% block inline_css %}
+ {% include 'emails/css/framework.css' %}
+ {% include 'emails/css/layout.css' %}
+{% endblock %}
+
+{% block full_body_html %}
+
+
+
+
+
+ Doctrine
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {% block body_html '' %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+{% endblock %}
diff --git a/templates/event-cfp.html.twig b/templates/event-cfp.html.twig
new file mode 100644
index 0000000..742694d
--- /dev/null
+++ b/templates/event-cfp.html.twig
@@ -0,0 +1,27 @@
+{% block title %}{{ event.name }}{% endblock %}
+
+{% block meta_description event.description %}
+
+{% block content %}
+
+
+ Events
+ {{ event.name }}
+ Call For Papers
+
+
+
+ {% if event.cfp.exists %}
+ {% if event.cfp.dates.now %}
+ Call for Papers started on {{ event.cfp.dates.start|date('l, F jS Y') }} and ends on {{ event.cfp.dates.end|date('l, F jS Y') }} .
+
+
+ {% elseif event.cfp.dates.upcoming %}
+ Call for Papers starts on {{ event.cfp.dates.start|date('l, F jS Y') }} and ends on {{ event.cfp.dates.end|date('l, F jS Y') }} .
+ {% else %}
+ {% include "alert.html.twig" with {alertMessage:event.name ~' call for papers has ended!'} %}
+ {% endif %}
+ {% else %}
+ {% include "alert.html.twig" with {alertMessage:event.name ~' does not have a call for papers!'} %}
+ {% endif %}
+{% endblock %}
diff --git a/templates/event.html.twig b/templates/event.html.twig
new file mode 100644
index 0000000..c5fe7cd
--- /dev/null
+++ b/templates/event.html.twig
@@ -0,0 +1,173 @@
+{% block title %}{{ event.name }}{% endblock %}
+
+{% block meta_description event.description %}
+
+{% block content %}
+
+
+ Events
+ {{ event.name }}
+
+
+
+ {% if event.dates.over %}
+ {% set alertMessage %}
+ This event has ended. Watch for additional events in the future.
+ {% endset %}
+
+ {% include "alert.html.twig" with {alertMessage:alertMessage} %}
+ {% endif %}
+
+ {{ event.description }}
+
+
+
+
+
+
+ {% if event.free %}
+ FREE!
+ {% else %}
+ ${{ event.price|number_format }}
+ {% endif %}
+
+
+
This {{ event.dates.over ? 'was' : 'is' }} a {{ event.dates.duration }} event that {{ event.dates.over ? 'started' : 'starts' }} on {{ event.startDate|date('l, F jS Y') }} at {{ event.startDate|date('h:i A T') }}{% if event.dates.numDays > 1 %} and ends on {{ event.endDate|date('l, F jS Y') }} at {{ event.endDate|date('h:i A T') }}{% endif %}.
+
+ {% if event.registrationDates.now %}
+ {% if event.free %}
+ {% if event.joinUrl %}
+
Join Event
+ {% else %}
+
A join URL does not exist yet for this event yet. Check back when it gets closer to {{ event.startDate|date('l, F jS Y') }}.
+ {% endif %}
+ {% else %}
+
+ Purchase Ticket
+
+ {% endif %}
+ {% elseif event.registrationDates.over %}
+
Registration is over for this event.
+ {% else %}
+
Registration will open on {{ event.registrationDates.start|date('l, F jS Y') }}.
+ {% endif %}
+
+ {% if event.cfp.dates.now %}
+
Submit a Talk
+ {% endif %}
+
+
+
+ {% include "carbonad-standard.html.twig" %}
+
+
+
+
+ {% if event.speakers|length %}
+
+ {% endif %}
+
+
+ {% if event.conference %}
+ Location
+
+ This event is hosted at {{ event.location.name }} which is located at {{ event.location.address.string }} .
+
+
+ {% endif %}
+
+ {% if event.sponsors|length %}
+ Sponsors
+
+
+ Thanks to our sponsors! This event would not be possible without their support. If you are interested in becoming a sponsor, please contact us at sponsorship@doctrine-project.org
+
+
+
+ {% for sponsor in event.sponsors %}
+
+
+
+ {% endfor %}
+
+ {% endif %}
+
+ {% if event.schedule|length %}
+ Schedule
+
+
+ {% for slot in event.schedule %}
+
+ {{ slot.speaker.topic }} {{ slot.speaker.name }}
+ {{ slot.startDate|date('h:i A T') }}
+ {{ slot.endDate|date('h:i A T') }}
+ {% if slot.speaker.hasYouTubeVideo() %} {% endif %}
+
+ {% endfor %}
+
+ {% endif %}
+
+ {% if event.speakers|length %}
+ Speakers
+
+ {% for speaker in event.speakers %}
+
+
+
+
+
+
+
+
+
+ {{ speaker.topic }}
+
+ {% if speaker.hasYouTubeVideo() %}
+
+ {% endif %}
+
+
{{ speaker.name }}
+
{{ speaker.description|nl2br }}
+
+
+
+
+ {% endfor %}
+ {% endif %}
+
+
+
+
+
+
+{% endblock %}
diff --git a/templates/layouts/layout.html.twig b/templates/layouts/layout.html.twig
index 7fcf6dc..6515ec5 100644
--- a/templates/layouts/layout.html.twig
+++ b/templates/layouts/layout.html.twig
@@ -137,27 +137,26 @@
Contributor Workflow
Maintainer Workflow
Contribute to Website
+ Maintainers
+ Contributors
Policies
GitHub
Styleguide
-
-
+
-
@@ -211,6 +210,7 @@
Sponsorship
Community
Blog
+ Events
Consulting
diff --git a/tests/Assets/AssetIntegrityGeneratorTest.php b/tests/Assets/AssetIntegrityGeneratorTest.php
index a213dc4..9597e8c 100644
--- a/tests/Assets/AssetIntegrityGeneratorTest.php
+++ b/tests/Assets/AssetIntegrityGeneratorTest.php
@@ -5,7 +5,7 @@ declare(strict_types=1);
namespace Doctrine\Website\Tests\Assets;
use Doctrine\Website\Assets\AssetIntegrityGenerator;
-use PHPUnit\Framework\TestCase;
+use Doctrine\Website\Tests\TestCase;
class AssetIntegrityGeneratorTest extends TestCase
{
diff --git a/tests/BuildAllBootstrap.php b/tests/BuildAllBootstrap.php
new file mode 100644
index 0000000..d4fd987
--- /dev/null
+++ b/tests/BuildAllBootstrap.php
@@ -0,0 +1,40 @@
+get(Application::class);
+
+ $consoleApplication = $application->getConsoleApplication();
+ $consoleApplication->setAutoExit(false);
+
+ $input = new ArrayInput([
+ 'command' => self::COMMAND,
+ ]);
+
+ $consoleApplication->find(self::COMMAND)
+ ->run($input, new ConsoleOutput());
+ }
+}
+
+// only execute this for phpunit
+if (strpos($_SERVER['PHP_SELF'], 'phpunit') !== false) {
+ (new BuildAllBootstrap())->__invoke();
+}
diff --git a/tests/Cache/CacheClearerTest.php b/tests/Cache/CacheClearerTest.php
index 4892aa5..5a10003 100644
--- a/tests/Cache/CacheClearerTest.php
+++ b/tests/Cache/CacheClearerTest.php
@@ -5,8 +5,8 @@ declare(strict_types=1);
namespace Doctrine\Website\Tests\Cache;
use Doctrine\Website\Cache\CacheClearer;
+use Doctrine\Website\Tests\TestCase;
use PHPUnit\Framework\MockObject\MockObject;
-use PHPUnit\Framework\TestCase;
use Symfony\Component\Filesystem\Filesystem;
class CacheClearerTest extends TestCase
diff --git a/tests/DataBuilder/BlogPostDataBuilderTest.php b/tests/DataBuilder/BlogPostDataBuilderTest.php
index 112096a..3509fa1 100644
--- a/tests/DataBuilder/BlogPostDataBuilderTest.php
+++ b/tests/DataBuilder/BlogPostDataBuilderTest.php
@@ -10,8 +10,8 @@ use Doctrine\StaticWebsiteGenerator\SourceFile\SourceFileFilesystemReader;
use Doctrine\StaticWebsiteGenerator\SourceFile\SourceFileParameters;
use Doctrine\StaticWebsiteGenerator\SourceFile\SourceFiles;
use Doctrine\Website\DataBuilder\BlogPostDataBuilder;
+use Doctrine\Website\Tests\TestCase;
use PHPUnit\Framework\MockObject\MockObject;
-use PHPUnit\Framework\TestCase;
use function usort;
class BlogPostDataBuilderTest extends TestCase
diff --git a/tests/DataBuilder/ContributorDataBuilderTest.php b/tests/DataBuilder/ContributorDataBuilderTest.php
index 4e92346..0c94612 100644
--- a/tests/DataBuilder/ContributorDataBuilderTest.php
+++ b/tests/DataBuilder/ContributorDataBuilderTest.php
@@ -6,12 +6,10 @@ namespace Doctrine\Website\Tests\DataBuilder;
use Doctrine\SkeletonMapper\ObjectManagerInterface;
use Doctrine\Website\DataBuilder\ContributorDataBuilder;
-use Doctrine\Website\Model\Project;
-use Doctrine\Website\Model\ProjectContributor;
use Doctrine\Website\Model\TeamMember;
use Doctrine\Website\Repositories\ProjectContributorRepository;
+use Doctrine\Website\Tests\TestCase;
use PHPUnit\Framework\MockObject\MockObject;
-use PHPUnit\Framework\TestCase;
class ContributorDataBuilderTest extends TestCase
{
@@ -23,16 +21,15 @@ class ContributorDataBuilderTest extends TestCase
public function testBuild() : void
{
- $project1 = new Project(['slug' => 'dbal']);
- $project2 = new Project(['slug' => 'orm']);
+ $project1 = $this->createProject(['slug' => 'dbal']);
+ $project2 = $this->createProject(['slug' => 'orm']);
$jwageTeamMember = new TeamMember();
$ocramiusTeamMember = new TeamMember();
$objectManager = $this->createMock(ObjectManagerInterface::class);
- $projectContributor1 = new ProjectContributor();
- $projectContributor1->hydrate([
+ $projectContributor1 = $this->createProjectContributor([
'github' => 'jwage',
'teamMember' => $jwageTeamMember,
'avatarUrl' => 'https://avatars1.githubusercontent.com/u/97422?s=460&v=4',
@@ -40,10 +37,9 @@ class ContributorDataBuilderTest extends TestCase
'numAdditions' => 1,
'numDeletions' => 1,
'project' => $project1,
- ], $objectManager);
+ ]);
- $projectContributor2 = new ProjectContributor();
- $projectContributor2->hydrate([
+ $projectContributor2 = $this->createProjectContributor([
'github' => 'jwage',
'teamMember' => $jwageTeamMember,
'avatarUrl' => 'https://avatars1.githubusercontent.com/u/97422?s=460&v=4',
@@ -51,10 +47,9 @@ class ContributorDataBuilderTest extends TestCase
'numAdditions' => 1,
'numDeletions' => 1,
'project' => $project2,
- ], $objectManager);
+ ]);
- $projectContributor3 = new ProjectContributor();
- $projectContributor3->hydrate([
+ $projectContributor3 = $this->createProjectContributor([
'github' => 'ocramius',
'teamMember' => $ocramiusTeamMember,
'avatarUrl' => 'https://avatars0.githubusercontent.com/u/154256?s=460&v=4',
@@ -62,7 +57,7 @@ class ContributorDataBuilderTest extends TestCase
'numAdditions' => 1,
'numDeletions' => 1,
'project' => $project2,
- ], $objectManager);
+ ]);
$projectContributors = [$projectContributor1, $projectContributor2, $projectContributor3];
diff --git a/tests/DataBuilder/ProjectContributorDataBuilderTest.php b/tests/DataBuilder/ProjectContributorDataBuilderTest.php
index 096b4e1..c8ecade 100644
--- a/tests/DataBuilder/ProjectContributorDataBuilderTest.php
+++ b/tests/DataBuilder/ProjectContributorDataBuilderTest.php
@@ -4,15 +4,13 @@ declare(strict_types=1);
namespace Doctrine\Website\Tests\DataBuilder;
-use Doctrine\SkeletonMapper\ObjectManagerInterface;
use Doctrine\Website\DataBuilder\ProjectContributorDataBuilder;
use Doctrine\Website\Github\GithubProjectContributors;
-use Doctrine\Website\Model\Project;
use Doctrine\Website\Model\TeamMember;
use Doctrine\Website\Repositories\ProjectRepository;
use Doctrine\Website\Repositories\TeamMemberRepository;
+use Doctrine\Website\Tests\TestCase;
use PHPUnit\Framework\MockObject\MockObject;
-use PHPUnit\Framework\TestCase;
class ProjectContributorDataBuilderTest extends TestCase
{
@@ -30,10 +28,8 @@ class ProjectContributorDataBuilderTest extends TestCase
public function testBuild() : void
{
- $objectManager = $this->createMock(ObjectManagerInterface::class);
-
- $project1 = new Project(['slug' => 'orm']);
- $project2 = new Project(['slug' => 'dbal']);
+ $project1 = $this->createProject(['slug' => 'orm']);
+ $project2 = $this->createProject(['slug' => 'dbal']);
$jwageTeamMember = $this->createMock(TeamMember::class);
diff --git a/tests/DataBuilder/ProjectDataBuilderTest.php b/tests/DataBuilder/ProjectDataBuilderTest.php
index 828212d..a0399e4 100644
--- a/tests/DataBuilder/ProjectDataBuilderTest.php
+++ b/tests/DataBuilder/ProjectDataBuilderTest.php
@@ -14,8 +14,8 @@ use Doctrine\Website\Projects\ProjectDataReader;
use Doctrine\Website\Projects\ProjectDataRepository;
use Doctrine\Website\Projects\ProjectGitSyncer;
use Doctrine\Website\Projects\ProjectVersionsReader;
+use Doctrine\Website\Tests\TestCase;
use PHPUnit\Framework\MockObject\MockObject;
-use PHPUnit\Framework\TestCase;
class ProjectDataBuilderTest extends TestCase
{
diff --git a/tests/DataSources/DoctrineUsersTest.php b/tests/DataSources/ArrayDataSourceTest.php
similarity index 62%
rename from tests/DataSources/DoctrineUsersTest.php
rename to tests/DataSources/ArrayDataSourceTest.php
index 4efe9d2..501c8bb 100644
--- a/tests/DataSources/DoctrineUsersTest.php
+++ b/tests/DataSources/ArrayDataSourceTest.php
@@ -4,10 +4,10 @@ declare(strict_types=1);
namespace Doctrine\Website\Tests\DataSources;
-use Doctrine\Website\DataSources\DoctrineUsers;
-use PHPUnit\Framework\TestCase;
+use Doctrine\Website\DataSources\ArrayDataSource;
+use Doctrine\Website\Tests\TestCase;
-class DoctrineUsersTest extends TestCase
+class ArrayDataSourceTest extends TestCase
{
public function testGetSourceRows() : void
{
@@ -22,8 +22,8 @@ class DoctrineUsersTest extends TestCase
],
];
- $doctrineUsers = new DoctrineUsers($rows);
+ $dataSource = new ArrayDataSource($rows);
- self::assertEquals($rows, $doctrineUsers->getSourceRows());
+ self::assertEquals($rows, $dataSource->getSourceRows());
}
}
diff --git a/tests/DataSources/ContributorsTest.php b/tests/DataSources/ContributorsTest.php
index df2caad..1e438f4 100644
--- a/tests/DataSources/ContributorsTest.php
+++ b/tests/DataSources/ContributorsTest.php
@@ -12,8 +12,8 @@ use Doctrine\Website\Model\Project;
use Doctrine\Website\Model\TeamMember;
use Doctrine\Website\Repositories\ProjectRepository;
use Doctrine\Website\Repositories\TeamMemberRepository;
+use Doctrine\Website\Tests\TestCase;
use PHPUnit\Framework\MockObject\MockObject;
-use PHPUnit\Framework\TestCase;
class ContributorsTest extends TestCase
{
diff --git a/tests/DataSources/ProjectContributorsTest.php b/tests/DataSources/ProjectContributorsTest.php
index b365606..7ae6fac 100644
--- a/tests/DataSources/ProjectContributorsTest.php
+++ b/tests/DataSources/ProjectContributorsTest.php
@@ -12,8 +12,8 @@ use Doctrine\Website\Model\Project;
use Doctrine\Website\Model\TeamMember;
use Doctrine\Website\Repositories\ProjectRepository;
use Doctrine\Website\Repositories\TeamMemberRepository;
+use Doctrine\Website\Tests\TestCase;
use PHPUnit\Framework\MockObject\MockObject;
-use PHPUnit\Framework\TestCase;
class ProjectContributorsTest extends TestCase
{
diff --git a/tests/DataSources/TeamMembersTest.php b/tests/DataSources/TeamMembersTest.php
deleted file mode 100644
index c7dfb47..0000000
--- a/tests/DataSources/TeamMembersTest.php
+++ /dev/null
@@ -1,34 +0,0 @@
-teamMembers = new TeamMembers([
- ['name' => 'ocramius'],
- ['name' => 'jwage'],
- ['name' => 'romanb'],
- ]);
- }
-
- public function testGetSourceRows() : void
- {
- $teamMemberRows = $this->teamMembers->getSourceRows();
-
- self::assertSame([
- ['name' => 'ocramius'],
- ['name' => 'jwage'],
- ['name' => 'romanb'],
- ], $teamMemberRows);
- }
-}
diff --git a/tests/DeployerTest.php b/tests/DeployerTest.php
index 7e1b8a1..401c02b 100644
--- a/tests/DeployerTest.php
+++ b/tests/DeployerTest.php
@@ -87,7 +87,7 @@ class DeployerTest extends TestCase
$this->processFactory->expects(self::at(2))
->method('run')
- ->with('cd /data/doctrine-website-staging && ./bin/console sync-repositories && ./bin/console build-website-data && ./bin/console build-docs && ./bin/console build-website /data/doctrine-website-build-staging --env=staging --publish')
+ ->with('cd /data/doctrine-website-staging && ./bin/console migrations:migrate --no-interaction --env=staging && ./bin/console build-all /data/doctrine-website-build-staging --env=staging --publish')
->willReturn($process);
$deployer->deploy($output);
diff --git a/tests/Docs/RST/RSTBuilderTest.php b/tests/Docs/RST/RSTBuilderTest.php
index aa6af7a..881d16d 100644
--- a/tests/Docs/RST/RSTBuilderTest.php
+++ b/tests/Docs/RST/RSTBuilderTest.php
@@ -12,7 +12,6 @@ use Doctrine\Website\Docs\RST\RSTCopier;
use Doctrine\Website\Docs\RST\RSTFileRepository;
use Doctrine\Website\Docs\RST\RSTLanguage;
use Doctrine\Website\Docs\RST\RSTPostBuildProcessor;
-use Doctrine\Website\Model\Project;
use Doctrine\Website\Model\ProjectVersion;
use Doctrine\Website\Tests\TestCase;
use PHPUnit\Framework\MockObject\MockObject;
@@ -67,7 +66,7 @@ class RSTBuilderTest extends TestCase
public function testBuildRSTDocs() : void
{
- $project = new Project([
+ $project = $this->createProject([
'slug' => 'project-slug',
'docsSlug' => 'docs-slug',
]);
diff --git a/tests/Docs/SearchIndexerTest.php b/tests/Docs/SearchIndexerTest.php
index 0a19799..406a3c2 100644
--- a/tests/Docs/SearchIndexerTest.php
+++ b/tests/Docs/SearchIndexerTest.php
@@ -11,7 +11,6 @@ use Doctrine\RST\Configuration;
use Doctrine\RST\Environment;
use Doctrine\RST\Kernel;
use Doctrine\Website\Docs\SearchIndexer;
-use Doctrine\Website\Model\Project;
use Doctrine\Website\Model\ProjectVersion;
use Doctrine\Website\Tests\TestCase;
use PHPUnit\Framework\MockObject\MockObject;
@@ -66,7 +65,7 @@ class SearchIndexerTest extends TestCase
public function testBuildSearchIndexes() : void
{
- $project = new Project([
+ $project = $this->createProject([
'shortName' => 'ORM',
'docsSlug' => 'doctrine-orm',
'slug' => 'orm',
diff --git a/tests/Git/TagBranchGuesserTest.php b/tests/Git/TagBranchGuesserTest.php
index 09e7572..b40e480 100644
--- a/tests/Git/TagBranchGuesserTest.php
+++ b/tests/Git/TagBranchGuesserTest.php
@@ -8,8 +8,8 @@ use DateTimeImmutable;
use Doctrine\Website\Git\Tag;
use Doctrine\Website\Git\TagBranchGuesser;
use Doctrine\Website\ProcessFactory;
+use Doctrine\Website\Tests\TestCase;
use PHPUnit\Framework\MockObject\MockObject;
-use PHPUnit\Framework\TestCase;
use Symfony\Component\Process\Process;
class TagBranchGuesserTest extends TestCase
diff --git a/tests/Git/TagReaderTest.php b/tests/Git/TagReaderTest.php
index c7d3cde..4968e82 100644
--- a/tests/Git/TagReaderTest.php
+++ b/tests/Git/TagReaderTest.php
@@ -6,8 +6,8 @@ namespace Doctrine\Website\Tests\Git;
use Doctrine\Website\Git\TagReader;
use Doctrine\Website\ProcessFactory;
+use Doctrine\Website\Tests\TestCase;
use PHPUnit\Framework\MockObject\MockObject;
-use PHPUnit\Framework\TestCase;
use Symfony\Component\Process\Process;
class TagReaderTest extends TestCase
diff --git a/tests/Git/TagTest.php b/tests/Git/TagTest.php
index f798d82..7131d73 100644
--- a/tests/Git/TagTest.php
+++ b/tests/Git/TagTest.php
@@ -6,7 +6,7 @@ namespace Doctrine\Website\Tests\Git;
use DateTimeImmutable;
use Doctrine\Website\Git\Tag;
-use PHPUnit\Framework\TestCase;
+use Doctrine\Website\Tests\TestCase;
class TagTest extends TestCase
{
diff --git a/tests/Github/ProdGithubProjectContributorsTest.php b/tests/Github/ProdGithubProjectContributorsTest.php
index 2c98aba..001eb78 100644
--- a/tests/Github/ProdGithubProjectContributorsTest.php
+++ b/tests/Github/ProdGithubProjectContributorsTest.php
@@ -7,10 +7,10 @@ namespace Doctrine\Website\Tests\Github;
use Doctrine\Common\Cache\FilesystemCache;
use Doctrine\Website\Github\ProdGithubProjectContributors;
use Doctrine\Website\Model\Project;
+use Doctrine\Website\Tests\TestCase;
use Github\Api\Repo;
use Github\Client;
use PHPUnit\Framework\MockObject\MockObject;
-use PHPUnit\Framework\TestCase;
use RuntimeException;
class ProdGithubProjectContributorsTest extends TestCase
diff --git a/tests/Model/EventTest.php b/tests/Model/EventTest.php
new file mode 100644
index 0000000..e7bd6b6
--- /dev/null
+++ b/tests/Model/EventTest.php
@@ -0,0 +1,274 @@
+createTestEvent()->isWebinar());
+ self::assertFalse($this->createTestEvent([
+ 'type' => EventType::CONFERENCE,
+ 'location' => [],
+ ])->isWebinar());
+ }
+
+ public function testIsConference() : void
+ {
+ self::assertFalse($this->createTestEvent()->isConference());
+ self::assertTrue($this->createTestEvent([
+ 'type' => EventType::CONFERENCE,
+ 'location' => [],
+ ])->isConference());
+ }
+
+ public function testGetSkuProd() : void
+ {
+ self::assertSame('prod_123', $this->createTestEvent(['env' => 'prod'])->getSku());
+ }
+
+ public function testGetSkuTest() : void
+ {
+ self::assertSame('test_123', $this->createTestEvent()->getSku());
+ }
+
+ public function testGetName() : void
+ {
+ self::assertSame('Doctrine for Beginners', $this->createTestEvent()->getName());
+ }
+
+ public function testGetSlug() : void
+ {
+ self::assertSame('doctrine-for-beginners', $this->createTestEvent()->getSlug());
+ }
+
+ public function testGetJoinUrl() : void
+ {
+ self::assertSame('https://www.joinurl.com', $this->createTestEvent()->getJoinUrl());
+ }
+
+ public function testGetDates() : void
+ {
+ self::assertEquals(
+ new DateTimeImmutable('2019-05-28 11:00:00'),
+ $this->createTestEvent()->getDates()->getStart()
+ );
+
+ self::assertEquals(
+ new DateTimeImmutable('2019-05-28 11:00:00'),
+ $this->createTestEvent()->getStartDate()
+ );
+
+ self::assertEquals(
+ new DateTimeImmutable('2019-05-28 11:45:00'),
+ $this->createTestEvent()->getDates()->getEnd()
+ );
+
+ self::assertEquals(
+ new DateTimeImmutable('2019-05-28 11:45:00'),
+ $this->createTestEvent()->getEndDate()
+ );
+
+ self::assertSame(0, $this->createTestEvent()->getDates()->getNumDays());
+ self::assertSame(0, $this->createTestEvent()->getDates()->getNumHours());
+ self::assertSame(45, $this->createTestEvent()->getDates()->getNumMinutes());
+ self::assertSame('45-minute', $this->createTestEvent()->getDates()->getDuration());
+
+ self::assertSame(3, $this->createTestEvent([
+ 'schedule' => [],
+ 'startDate' => '2019-05-28',
+ 'endDate' => '2019-05-30',
+ ])->getDates()->getNumDays());
+
+ self::assertSame('3-day', $this->createTestEvent([
+ 'schedule' => [],
+ 'startDate' => '2019-05-28',
+ 'endDate' => '2019-05-30',
+ ])->getDates()->getDuration());
+
+ self::assertSame(71, $this->createTestEvent([
+ 'schedule' => [],
+ 'startDate' => '2019-05-28 11:00:00',
+ 'endDate' => '2019-05-30 10:00:00',
+ ])->getDates()->getNumHours());
+ }
+
+ public function testGetRegistrationDates() : void
+ {
+ self::assertEquals(
+ new DateTimeImmutable('2019-05-01'),
+ $this->createTestEvent()->getRegistrationDates()->getStart()
+ );
+
+ self::assertEquals(
+ new DateTimeImmutable('2019-05-27'),
+ $this->createTestEvent()->getRegistrationDates()->getEnd()
+ );
+ }
+
+ public function testGetCfp() : void
+ {
+ self::assertTrue($this->createTestEvent()->getCfp()->exists());
+
+ self::assertSame(
+ 'https://docs.google.com/forms/d/e/123/viewform',
+ $this->createTestEvent()->getCfp()->getGoogleFormUrl()
+ );
+
+ self::assertSame(
+ 'https://docs.google.com/forms/d/e/123/viewform?embedded=true',
+ $this->createTestEvent()->getCfp()->getEmbeddedGoogleFormUrl()
+ );
+
+ self::assertEquals(
+ new DateTimeImmutable('2019-05-01'),
+ $this->createTestEvent()->getCfp()->getDates()->getStart()
+ );
+
+ self::assertEquals(
+ new DateTimeImmutable('2019-05-02'),
+ $this->createTestEvent()->getCfp()->getDates()->getEnd()
+ );
+ }
+
+ public function testGetLocation() : void
+ {
+ self::assertNull($this->createTestEvent()->getLocation());
+
+ $event = $this->createTestEvent([
+ 'type' => EventType::CONFERENCE,
+ 'location' => [
+ 'name' => 'Awesome Hotel',
+ 'address' => [
+ 'line1' => 'Line 1',
+ 'line2' => 'Line 2',
+ 'city' => 'City',
+ 'state' => 'State',
+ 'zipCode' => 'Zip Code',
+ 'countryCode' => 'Country Code',
+ ],
+ ],
+ ]);
+
+ /** @var EventLocation $location */
+ $location = $event->getLocation();
+
+ self::assertSame('Awesome Hotel', $location->getName());
+ self::assertSame('Line 1', $location->getAddress()->getLine1());
+ self::assertSame('Line 2', $location->getAddress()->getLine2());
+ self::assertSame('City', $location->getAddress()->getCity());
+ self::assertSame('State', $location->getAddress()->getState());
+ self::assertSame('Zip Code', $location->getAddress()->getZipCode());
+ self::assertSame('Country Code', $location->getAddress()->getCountryCode());
+ }
+
+ public function testGetSponsors() : void
+ {
+ $sponsor = $this->createTestEvent()->getSponsors()->first();
+
+ self::assertSame('Blackfire.io', $sponsor->getName());
+ self::assertSame('https://blackfire.io/', $sponsor->getUrl());
+ self::assertSame(
+ 'https://blackfire.io/?utm_source=doctrine&utm_medium=website&utm_campaign=doctrine-for-beginners',
+ $sponsor->getUrlWithUtmParameters()
+ );
+ self::assertSame('/images/blackfire.svg', $sponsor->getLogo());
+ }
+
+ public function testGetSpeakers() : void
+ {
+ $speaker = $this->createTestEvent()->getSpeakers()->first();
+
+ self::assertSame('Jonathan H. Wage', $speaker->getName());
+ self::assertSame('Doctrine for Beginners', $speaker->getTopic());
+ self::assertSame('doctrine-for-beginners', $speaker->getTopicSlug());
+ self::assertSame('Come to this talk prepared to learn about the Doctrine PHP open source project. The Doctrine project has been around for over a decade and has evolved from database abstraction software that dates back to the PEAR days. The packages provided by the Doctrine project have been downloaded almost a billion times from packagist. In this talk we will take you through how to get started with Doctrine and how to take advantage of some of the more advanced features.', $speaker->getDescription());
+ }
+
+ public function testGetSchedule() : void
+ {
+ $event = $this->createTestEvent();
+
+ $speaker = $event->getSpeakers()->first();
+ $slot = $event->getSchedule()->first();
+
+ self::assertSame($speaker, $slot->getSpeaker());
+ }
+
+ public function testGetDescription() : void
+ {
+ self::assertSame('Test Description', $this->createTestEvent()->getDescription());
+ }
+
+ public function testGetPrice() : void
+ {
+ self::assertSame(0.00, $this->createTestEvent()->getPrice());
+ self::assertSame(5.00, $this->createTestEvent(['price' => 5.00])->getPrice());
+ }
+
+ public function testIsFree() : void
+ {
+ self::assertTrue($this->createTestEvent()->isFree());
+ self::assertFalse($this->createTestEvent(['price' => 5.00])->isFree());
+ }
+
+ /**
+ * @param mixed[] $data
+ */
+ private function createTestEvent(array $data = []) : Event
+ {
+ return $this->createEvent(array_merge([
+ 'env' => 'dev',
+ 'sku' => [
+ 'test' => 'test_123',
+ 'prod' => 'prod_123',
+ ],
+ 'cfp' => [
+ 'googleFormId' => '123',
+ 'startDate' => '2019-05-01',
+ 'endDate' => '2019-05-02',
+ ],
+ 'name' => 'Doctrine for Beginners',
+ 'slug' => 'doctrine-for-beginners',
+ 'description' => 'Test Description',
+ 'price' => 0.0,
+ 'joinUrl' => 'https://www.joinurl.com',
+ 'startDate' => '2019-05-28',
+ 'endDate' => '2019-05-28',
+ 'registrationStartDate' => '2019-05-01',
+ 'registrationEndDate' => '2019-05-27',
+ 'sponsors' => [
+ [
+ 'name' => 'Blackfire.io',
+ 'url' => 'https://blackfire.io/',
+ 'logo' => '/images/blackfire.svg',
+ 'utmParameters' => ['utm_source' => 'doctrine'],
+ ],
+ ],
+ 'speakers' => [
+ [
+ 'name' => 'jwage',
+ 'topic' => 'Doctrine for Beginners',
+ 'topicSlug' => 'doctrine-for-beginners',
+ 'description' => 'Come to this talk prepared to learn about the Doctrine PHP open source project. The Doctrine project has been around for over a decade and has evolved from database abstraction software that dates back to the PEAR days. The packages provided by the Doctrine project have been downloaded almost a billion times from packagist. In this talk we will take you through how to get started with Doctrine and how to take advantage of some of the more advanced features.',
+ ],
+ ],
+ 'schedule' => [
+ [
+ 'topicSlug' => 'doctrine-for-beginners',
+ 'startDate' => '2019-05-28 11:00',
+ 'endDate' => '2019-05-28 11:45',
+ ],
+ ],
+ ], $data));
+ }
+}
diff --git a/tests/Projects/ProjectTest.php b/tests/Projects/ProjectTest.php
index fd52111..21e40de 100644
--- a/tests/Projects/ProjectTest.php
+++ b/tests/Projects/ProjectTest.php
@@ -19,7 +19,7 @@ class ProjectTest extends TestCase
protected function setUp() : void
{
- $this->project = new Project([
+ $this->project = $this->createProject([
'name' => 'Test Project',
'shortName' => 'Test Project',
'slug' => 'test-project',
diff --git a/tests/Projects/ProjectVersionsReaderTest.php b/tests/Projects/ProjectVersionsReaderTest.php
index 1970d6b..121034f 100644
--- a/tests/Projects/ProjectVersionsReaderTest.php
+++ b/tests/Projects/ProjectVersionsReaderTest.php
@@ -9,8 +9,8 @@ use Doctrine\Website\Git\Tag;
use Doctrine\Website\Git\TagBranchGuesser;
use Doctrine\Website\Git\TagReader;
use Doctrine\Website\Projects\ProjectVersionsReader;
+use Doctrine\Website\Tests\TestCase;
use PHPUnit\Framework\MockObject\MockObject;
-use PHPUnit\Framework\TestCase;
class ProjectVersionsReaderTest extends TestCase
{
diff --git a/tests/Requests/ContributorRequestsTest.php b/tests/Requests/ContributorRequestsTest.php
index b382503..3e2a61d 100644
--- a/tests/Requests/ContributorRequestsTest.php
+++ b/tests/Requests/ContributorRequestsTest.php
@@ -4,13 +4,11 @@ declare(strict_types=1);
namespace Doctrine\Website\Tests\Requests;
-use Doctrine\SkeletonMapper\ObjectManagerInterface;
use Doctrine\StaticWebsiteGenerator\Request\ArrayRequestCollection;
-use Doctrine\Website\Model\Contributor;
use Doctrine\Website\Repositories\ContributorRepository;
use Doctrine\Website\Requests\ContributorRequests;
+use Doctrine\Website\Tests\TestCase;
use PHPUnit\Framework\MockObject\MockObject;
-use PHPUnit\Framework\TestCase;
class ContributorRequestsTest extends TestCase
{
@@ -22,13 +20,9 @@ class ContributorRequestsTest extends TestCase
public function testGetContributors() : void
{
- $objectManager = $this->createMock(ObjectManagerInterface::class);
+ $contributor1 = $this->createContributor(['github' => 'github1']);
- $contributor1 = new Contributor();
- $contributor1->hydrate(['github' => 'github1'], $objectManager);
-
- $contributor2 = new Contributor();
- $contributor2->hydrate(['github' => 'github2'], $objectManager);
+ $contributor2 = $this->createContributor(['github' => 'github2']);
$contributors = [$contributor1, $contributor2];
diff --git a/tests/Requests/ProjectRequestsTest.php b/tests/Requests/ProjectRequestsTest.php
index d45dfd5..7d96207 100644
--- a/tests/Requests/ProjectRequestsTest.php
+++ b/tests/Requests/ProjectRequestsTest.php
@@ -5,11 +5,10 @@ declare(strict_types=1);
namespace Doctrine\Website\Tests\Requests;
use Doctrine\StaticWebsiteGenerator\Request\ArrayRequestCollection;
-use Doctrine\Website\Model\Project;
use Doctrine\Website\Repositories\ProjectRepository;
use Doctrine\Website\Requests\ProjectRequests;
+use Doctrine\Website\Tests\TestCase;
use PHPUnit\Framework\MockObject\MockObject;
-use PHPUnit\Framework\TestCase;
class ProjectRequestsTest extends TestCase
{
@@ -21,8 +20,8 @@ class ProjectRequestsTest extends TestCase
public function testGetProjects() : void
{
- $project1 = new Project(['slug' => 'project1']);
- $project2 = new Project(['slug' => 'project2']);
+ $project1 = $this->createProject(['slug' => 'project1']);
+ $project2 = $this->createProject(['slug' => 'project2']);
$projects = [$project1, $project2];
diff --git a/tests/TestCase.php b/tests/TestCase.php
index fcd9031..ce19de7 100644
--- a/tests/TestCase.php
+++ b/tests/TestCase.php
@@ -4,21 +4,94 @@ declare(strict_types=1);
namespace Doctrine\Website\Tests;
+use Doctrine\SkeletonMapper\ObjectRepository\ObjectRepositoryInterface;
use Doctrine\Website\Application;
+use Doctrine\Website\Model\Contributor;
+use Doctrine\Website\Model\Event;
+use Doctrine\Website\Model\Project;
+use Doctrine\Website\Model\ProjectContributor;
+use Doctrine\Website\Repositories\ContributorRepository;
+use Doctrine\Website\Repositories\EventRepository;
+use Doctrine\Website\Repositories\ProjectContributorRepository;
+use Doctrine\Website\Repositories\ProjectRepository;
use PHPUnit\Framework\TestCase as BaseTestCase;
use Symfony\Component\DependencyInjection\ContainerBuilder;
+use function rand;
abstract class TestCase extends BaseTestCase
{
/** @var ContainerBuilder */
- private $container;
+ private static $container;
protected function getContainer() : ContainerBuilder
{
- if ($this->container === null) {
- $this->container = Application::getContainer('test');
+ if (self::$container === null) {
+ self::$container = Application::getContainer('test');
}
- return $this->container;
+ return self::$container;
+ }
+
+ /**
+ * @param mixed[] $data
+ */
+ protected function createModel(string $repositoryClassName, array $data) : object
+ {
+ /** @var ObjectRepositoryInterface $repository */
+ $repository = $this->getContainer()->get($repositoryClassName);
+
+ $object = $repository->create($repository->getClassName());
+
+ $repository->hydrate($object, $data);
+
+ return $object;
+ }
+
+
+ /**
+ * @param mixed[] $data
+ */
+ protected function createEvent(array $data) : Event
+ {
+ $data['id'] = rand();
+
+ /** @var Event $event */
+ $event = $this->createModel(EventRepository::class, $data);
+
+ return $event;
+ }
+
+
+ /**
+ * @param mixed[] $data
+ */
+ protected function createProject(array $data) : Project
+ {
+ /** @var Project $project */
+ $project = $this->createModel(ProjectRepository::class, $data);
+
+ return $project;
+ }
+
+ /**
+ * @param mixed[] $data
+ */
+ protected function createContributor(array $data) : Contributor
+ {
+ /** @var Contributor $contributor */
+ $contributor = $this->createModel(ContributorRepository::class, $data);
+
+ return $contributor;
+ }
+
+ /**
+ * @param mixed[] $data
+ */
+ protected function createProjectContributor(array $data) : ProjectContributor
+ {
+ /** @var ProjectContributor $projectContributor */
+ $projectContributor = $this->createModel(ProjectContributorRepository::class, $data);
+
+ return $projectContributor;
}
}
diff --git a/tests/Twig/MainExtensionTest.php b/tests/Twig/MainExtensionTest.php
index 8457f7d..74b2060 100644
--- a/tests/Twig/MainExtensionTest.php
+++ b/tests/Twig/MainExtensionTest.php
@@ -5,7 +5,6 @@ declare(strict_types=1);
namespace Doctrine\Website\Tests\Twig;
use Doctrine\Website\Assets\AssetIntegrityGenerator;
-use Doctrine\Website\Model\Project;
use Doctrine\Website\Model\ProjectVersion;
use Doctrine\Website\Tests\TestCase;
use Doctrine\Website\Twig\MainExtension;
@@ -39,7 +38,8 @@ class MainExtensionTest extends TestCase
$this->parsedown,
$this->assetIntegrityGenerator,
$this->sourceDir,
- $this->webpackBuildDir
+ $this->webpackBuildDir,
+ 'stripe-publishable-key'
);
}
@@ -49,13 +49,13 @@ class MainExtensionTest extends TestCase
self::assertSame('Search', $placeholder);
- $project = new Project(['shortName' => 'ORM']);
+ $project = $this->createProject(['shortName' => 'ORM']);
$placeholder = $this->mainExtension->getSearchBoxPlaceholder($project);
self::assertSame('Search ORM', $placeholder);
- $project = new Project([
+ $project = $this->createProject([
'shortName' => 'ORM',
'versions' => [
[
diff --git a/tests/phpunit-build-all.xml b/tests/phpunit-build-all.xml
new file mode 100644
index 0000000..1e5132a
--- /dev/null
+++ b/tests/phpunit-build-all.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+ ./tests
+
+
+
+
+
+ ./lib
+
+
+
diff --git a/webpack.config.js b/webpack.config.js
index 37671b5..f8e8eb0 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -25,7 +25,7 @@ const plugins = () => {
if (!isWatching) {
plugins.push(
new PurgecssPlugin({
- whitelistPatterns: [/^ais/, /^carbon/, /^badge/],
+ whitelistPatterns: [/^ais/, /^carbon/, /^badge/, /^modal-backdrop/],
paths: []
.concat(glob.sync(__dirname + '/templates/**/*.twig'))
.concat(glob.sync(__dirname + '/source/**/*.html'))