diff --git a/app/AppKernel.php b/app/AppKernel.php
index 49fa758..694ca22 100644
--- a/app/AppKernel.php
+++ b/app/AppKernel.php
@@ -18,6 +18,7 @@ class AppKernel extends Kernel
new Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle(),
new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(),
new Misd\GuzzleBundle\MisdGuzzleBundle(),
+ new Ob\HighchartsBundle\ObHighchartsBundle(),
new Skobkin\Bundle\PointToolsBundle\SkobkinPointToolsBundle(),
);
diff --git a/app/DoctrineMigrations/Version20151001210600.php b/app/DoctrineMigrations/Version20151001210600.php
new file mode 100644
index 0000000..101a533
--- /dev/null
+++ b/app/DoctrineMigrations/Version20151001210600.php
@@ -0,0 +1,34 @@
+abortIf($this->connection->getDatabasePlatform()->getName() != 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');
+
+ $this->addSql('DROP INDEX users.uniq_338adfc4aa08cb10');
+ }
+
+ /**
+ * @param Schema $schema
+ */
+ public function down(Schema $schema)
+ {
+ // this down() migration is auto-generated, please modify it to your needs
+ $this->abortIf($this->connection->getDatabasePlatform()->getName() != 'postgresql', 'Migration can only be executed safely on \'postgresql\'.');
+
+ $this->addSql('CREATE UNIQUE INDEX uniq_338adfc4aa08cb10 ON users.users (login)');
+ }
+}
diff --git a/app/Resources/views/base.html.twig b/app/Resources/views/base.html.twig
index 9afff0b..875f8f1 100644
--- a/app/Resources/views/base.html.twig
+++ b/app/Resources/views/base.html.twig
@@ -26,6 +26,7 @@
{{ 'Report a bug'|trans }}
@@ -57,4 +58,5 @@
+ {% include 'counters.html.twig' %}
{% endblock %}
\ No newline at end of file
diff --git a/app/Resources/views/counters.html.twig b/app/Resources/views/counters.html.twig
new file mode 100644
index 0000000..a438860
--- /dev/null
+++ b/app/Resources/views/counters.html.twig
@@ -0,0 +1,28 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/SymfonyRequirements.php b/app/SymfonyRequirements.php
index caabe40..abaf0c1 100644
--- a/app/SymfonyRequirements.php
+++ b/app/SymfonyRequirements.php
@@ -638,20 +638,20 @@ class SymfonyRequirements extends RequirementCollection
}
$this->addRecommendation(
- class_exists('Locale'),
+ extension_loaded('intl'),
'intl extension should be available',
'Install and enable the intl extension (used for validators).'
);
- if (class_exists('Collator')) {
+ if (extension_loaded('intl')) {
+ // in some WAMP server installations, new Collator() returns null
$this->addRecommendation(
null !== new Collator('fr_FR'),
'intl extension should be correctly configured',
'The intl extension does not behave properly. This problem is typical on PHP 5.3.X x64 WIN builds.'
);
- }
- if (class_exists('Locale')) {
+ // check for compatible ICU versions (only done when you have the intl extension)
if (defined('INTL_ICU_VERSION')) {
$version = INTL_ICU_VERSION;
} else {
@@ -670,6 +670,14 @@ class SymfonyRequirements extends RequirementCollection
'intl ICU version should be at least 4+',
'Upgrade your intl extension with a newer ICU version (4+).'
);
+
+ $this->addPhpIniRecommendation(
+ 'intl.error_level',
+ create_function('$cfgValue', 'return (int) $cfgValue === 0;'),
+ true,
+ 'intl.error_level should be 0 in php.ini',
+ 'Set "intl.error_level " to "0 " in php.ini* to inhibit the messages when an error occurs in ICU functions.'
+ );
}
$accelerator =
diff --git a/app/check.php b/app/check.php
index 90bad4a..60ae0a8 100755
--- a/app/check.php
+++ b/app/check.php
@@ -42,9 +42,9 @@ foreach ($symfonyRequirements->getRecommendations() as $req) {
}
if ($checkPassed) {
- echo_block('success', 'OK', 'Your system is ready to run Symfony2 projects', true);
+ echo_block('success', 'OK', 'Your system is ready to run Symfony2 projects');
} else {
- echo_block('error', 'ERROR', 'Your system is not ready to run Symfony2 projects', true);
+ echo_block('error', 'ERROR', 'Your system is not ready to run Symfony2 projects');
echo_title('Fix the following mandatory requirements', 'red');
diff --git a/composer.json b/composer.json
index 7c6a1af..796d57b 100644
--- a/composer.json
+++ b/composer.json
@@ -8,7 +8,7 @@
},
"require": {
"php": ">=5.3.3",
- "symfony/symfony": "2.7.x-dev",
+ "symfony/symfony": "2.7.*",
"doctrine/orm": "~2.2,>=2.2.3,<2.5",
"doctrine/dbal": "<2.5",
"doctrine/doctrine-bundle": "~1.4",
@@ -20,8 +20,8 @@
"sensio/framework-extra-bundle": "~3.0,>=3.0.2",
"incenteev/composer-parameter-handler": "~2.0",
"misd/guzzle-bundle": "~1.0",
- "doctrine/migrations": "1.0.*@dev",
- "doctrine/doctrine-migrations-bundle": "2.1.*@dev"
+ "ob/highcharts-bundle": "^1.2",
+ "doctrine/doctrine-migrations-bundle": "^1.0"
},
"require-dev": {
"sensio/generator-bundle": "~2.3"
diff --git a/composer.lock b/composer.lock
index e29b519..6639c4a 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,20 +4,21 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
- "hash": "6f8a78a0b471fceb335f2e83e43985ef",
+ "hash": "4fe9c0fca61b1bf505886a29726aac87",
+ "content-hash": "7a96c16b3360dea55a684da6fa90d8fe",
"packages": [
{
"name": "doctrine/annotations",
- "version": "v1.2.4",
+ "version": "v1.2.7",
"source": {
"type": "git",
"url": "https://github.com/doctrine/annotations.git",
- "reference": "b5202eb9e83f8db52e0e58867e0a46e63be8332e"
+ "reference": "f25c8aab83e0c3e976fd7d19875f198ccf2f7535"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/doctrine/annotations/zipball/b5202eb9e83f8db52e0e58867e0a46e63be8332e",
- "reference": "b5202eb9e83f8db52e0e58867e0a46e63be8332e",
+ "url": "https://api.github.com/repos/doctrine/annotations/zipball/f25c8aab83e0c3e976fd7d19875f198ccf2f7535",
+ "reference": "f25c8aab83e0c3e976fd7d19875f198ccf2f7535",
"shasum": ""
},
"require": {
@@ -72,20 +73,20 @@
"docblock",
"parser"
],
- "time": "2014-12-23 22:40:37"
+ "time": "2015-08-31 12:32:49"
},
{
"name": "doctrine/cache",
- "version": "v1.4.1",
+ "version": "v1.4.2",
"source": {
"type": "git",
"url": "https://github.com/doctrine/cache.git",
- "reference": "c9eadeb743ac6199f7eec423cb9426bc518b7b03"
+ "reference": "8c434000f420ade76a07c64cbe08ca47e5c101ca"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/doctrine/cache/zipball/c9eadeb743ac6199f7eec423cb9426bc518b7b03",
- "reference": "c9eadeb743ac6199f7eec423cb9426bc518b7b03",
+ "url": "https://api.github.com/repos/doctrine/cache/zipball/8c434000f420ade76a07c64cbe08ca47e5c101ca",
+ "reference": "8c434000f420ade76a07c64cbe08ca47e5c101ca",
"shasum": ""
},
"require": {
@@ -142,7 +143,7 @@
"cache",
"caching"
],
- "time": "2015-04-15 00:11:59"
+ "time": "2015-08-31 12:36:41"
},
{
"name": "doctrine/collections",
@@ -212,16 +213,16 @@
},
{
"name": "doctrine/common",
- "version": "v2.5.0",
+ "version": "v2.5.1",
"source": {
"type": "git",
"url": "https://github.com/doctrine/common.git",
- "reference": "cd8daf2501e10c63dced7b8b9b905844316ae9d3"
+ "reference": "0009b8f0d4a917aabc971fb089eba80e872f83f9"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/doctrine/common/zipball/cd8daf2501e10c63dced7b8b9b905844316ae9d3",
- "reference": "cd8daf2501e10c63dced7b8b9b905844316ae9d3",
+ "url": "https://api.github.com/repos/doctrine/common/zipball/0009b8f0d4a917aabc971fb089eba80e872f83f9",
+ "reference": "0009b8f0d4a917aabc971fb089eba80e872f83f9",
"shasum": ""
},
"require": {
@@ -281,7 +282,7 @@
"persistence",
"spl"
],
- "time": "2015-04-02 19:55:44"
+ "time": "2015-08-31 13:00:22"
},
{
"name": "doctrine/dbal",
@@ -348,16 +349,16 @@
},
{
"name": "doctrine/doctrine-bundle",
- "version": "v1.5.0",
+ "version": "v1.5.2",
"source": {
"type": "git",
"url": "https://github.com/doctrine/DoctrineBundle.git",
- "reference": "0b9e27037c4fdbad515ee5ec89842e9091a6480f"
+ "reference": "d63be7eb9a95d46720f7d6badac4e5bc2bcff2e3"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/doctrine/DoctrineBundle/zipball/0b9e27037c4fdbad515ee5ec89842e9091a6480f",
- "reference": "0b9e27037c4fdbad515ee5ec89842e9091a6480f",
+ "url": "https://api.github.com/repos/doctrine/DoctrineBundle/zipball/d63be7eb9a95d46720f7d6badac4e5bc2bcff2e3",
+ "reference": "d63be7eb9a95d46720f7d6badac4e5bc2bcff2e3",
"shasum": ""
},
"require": {
@@ -365,16 +366,16 @@
"doctrine/doctrine-cache-bundle": "~1.0",
"jdorn/sql-formatter": "~1.1",
"php": ">=5.3.2",
- "symfony/console": "~2.3",
- "symfony/doctrine-bridge": "~2.2",
- "symfony/framework-bundle": "~2.3"
+ "symfony/console": "~2.3|~3.0",
+ "symfony/doctrine-bridge": "~2.2|~3.0",
+ "symfony/framework-bundle": "~2.3|~3.0"
},
"require-dev": {
"doctrine/orm": "~2.3",
"phpunit/phpunit": "~4",
"satooshi/php-coveralls": "~0.6.1",
- "symfony/validator": "~2.2",
- "symfony/yaml": "~2.2",
+ "symfony/validator": "~2.2|~3.0",
+ "symfony/yaml": "~2.2|~3.0",
"twig/twig": "~1.10"
},
"suggest": {
@@ -384,7 +385,7 @@
"type": "symfony-bundle",
"extra": {
"branch-alias": {
- "dev-master": "1.5.x-dev"
+ "dev-master": "1.6.x-dev"
}
},
"autoload": {
@@ -422,7 +423,7 @@
"orm",
"persistence"
],
- "time": "2015-05-28 12:27:15"
+ "time": "2015-08-31 14:47:06"
},
{
"name": "doctrine/doctrine-cache-bundle",
@@ -510,34 +511,33 @@
},
{
"name": "doctrine/doctrine-migrations-bundle",
- "version": "dev-master",
- "target-dir": "Doctrine/Bundle/MigrationsBundle",
+ "version": "v1.1.0",
"source": {
"type": "git",
"url": "https://github.com/doctrine/DoctrineMigrationsBundle.git",
- "reference": "1e8cd4415bd2f893eb828216b529a75e8b61d579"
+ "reference": "93ec729e3f2f1bb882904cce9d2c1dde6f139ec8"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/doctrine/DoctrineMigrationsBundle/zipball/1e8cd4415bd2f893eb828216b529a75e8b61d579",
- "reference": "1e8cd4415bd2f893eb828216b529a75e8b61d579",
+ "url": "https://api.github.com/repos/doctrine/DoctrineMigrationsBundle/zipball/93ec729e3f2f1bb882904cce9d2c1dde6f139ec8",
+ "reference": "93ec729e3f2f1bb882904cce9d2c1dde6f139ec8",
"shasum": ""
},
"require": {
"doctrine/doctrine-bundle": "~1.0",
- "doctrine/migrations": "~1.0@dev",
+ "doctrine/migrations": "~1.0",
"php": ">=5.3.2",
"symfony/framework-bundle": "~2.3|~3.0"
},
"type": "symfony-bundle",
"extra": {
"branch-alias": {
- "dev-master": "2.1.x-dev"
+ "dev-master": "1.1-dev"
}
},
"autoload": {
- "psr-0": {
- "Doctrine\\Bundle\\MigrationsBundle": ""
+ "psr-4": {
+ "Doctrine\\Bundle\\MigrationsBundle\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -565,7 +565,7 @@
"migrations",
"schema"
],
- "time": "2015-05-06 08:32:15"
+ "time": "2015-09-29 10:07:00"
},
{
"name": "doctrine/inflector",
@@ -690,45 +690,47 @@
},
{
"name": "doctrine/migrations",
- "version": "dev-master",
+ "version": "v1.1.0",
"source": {
"type": "git",
"url": "https://github.com/doctrine/migrations.git",
- "reference": "abb87d84ed21fd30c27bd3b52252a495a36d32fb"
+ "reference": "d196ddc229f50c66c5a015c158adb78a2dfb4351"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/doctrine/migrations/zipball/abb87d84ed21fd30c27bd3b52252a495a36d32fb",
- "reference": "abb87d84ed21fd30c27bd3b52252a495a36d32fb",
+ "url": "https://api.github.com/repos/doctrine/migrations/zipball/d196ddc229f50c66c5a015c158adb78a2dfb4351",
+ "reference": "d196ddc229f50c66c5a015c158adb78a2dfb4351",
"shasum": ""
},
"require": {
- "doctrine/dbal": "~2.0",
- "php": ">=5.3.2",
+ "doctrine/dbal": "~2.2",
+ "php": ">=5.4.0",
"symfony/console": "~2.3",
"symfony/yaml": "~2.3"
},
- "conflict": {
- "doctrine/orm": "<2.4"
- },
"require-dev": {
"doctrine/coding-standard": "dev-master",
"doctrine/orm": "2.*",
- "phpunit/phpunit": "~4.0",
+ "johnkary/phpunit-speedtrap": "~1.0@dev",
+ "mockery/mockery": "^0.9.4",
+ "phpunit/phpunit": "~4.7",
"satooshi/php-coveralls": "0.6.*"
},
"suggest": {
"symfony/console": "to run the migration from the console"
},
+ "bin": [
+ "bin/doctrine-migrations"
+ ],
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.0.x-dev"
+ "dev-master": "v1.1.x-dev"
}
},
"autoload": {
- "psr-0": {
- "Doctrine\\DBAL\\Migrations": "lib"
+ "psr-4": {
+ "Doctrine\\DBAL\\Migrations\\": "lib/Doctrine/DBAL/Migrations"
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -751,20 +753,20 @@
"database",
"migrations"
],
- "time": "2015-05-26 15:30:26"
+ "time": "2015-09-29 11:13:06"
},
{
"name": "doctrine/orm",
- "version": "v2.4.7",
+ "version": "v2.4.8",
"source": {
"type": "git",
"url": "https://github.com/doctrine/doctrine2.git",
- "reference": "2bc4ff3cab2ae297bcd05f2e619d42e6a7ca9e68"
+ "reference": "5aedac1e5c5caaeac14798822c70325dc242d467"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/doctrine/doctrine2/zipball/2bc4ff3cab2ae297bcd05f2e619d42e6a7ca9e68",
- "reference": "2bc4ff3cab2ae297bcd05f2e619d42e6a7ca9e68",
+ "url": "https://api.github.com/repos/doctrine/doctrine2/zipball/5aedac1e5c5caaeac14798822c70325dc242d467",
+ "reference": "5aedac1e5c5caaeac14798822c70325dc242d467",
"shasum": ""
},
"require": {
@@ -824,7 +826,7 @@
"database",
"orm"
],
- "time": "2014-12-16 13:45:01"
+ "time": "2015-08-31 13:19:01"
},
{
"name": "guzzle/guzzle",
@@ -923,17 +925,16 @@
},
{
"name": "incenteev/composer-parameter-handler",
- "version": "v2.1.0",
- "target-dir": "Incenteev/ParameterHandler",
+ "version": "v2.1.1",
"source": {
"type": "git",
"url": "https://github.com/Incenteev/ParameterHandler.git",
- "reference": "143272a0a09c62616a3c8011fc165a10c6b35241"
+ "reference": "84a205fe80a46101607bafbc423019527893ddd0"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/Incenteev/ParameterHandler/zipball/143272a0a09c62616a3c8011fc165a10c6b35241",
- "reference": "143272a0a09c62616a3c8011fc165a10c6b35241",
+ "url": "https://api.github.com/repos/Incenteev/ParameterHandler/zipball/84a205fe80a46101607bafbc423019527893ddd0",
+ "reference": "84a205fe80a46101607bafbc423019527893ddd0",
"shasum": ""
},
"require": {
@@ -952,8 +953,8 @@
}
},
"autoload": {
- "psr-0": {
- "Incenteev\\ParameterHandler": ""
+ "psr-4": {
+ "Incenteev\\ParameterHandler\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -971,7 +972,7 @@
"keywords": [
"parameters management"
],
- "time": "2013-12-07 10:10:39"
+ "time": "2015-06-03 08:27:03"
},
{
"name": "jdorn/sql-formatter",
@@ -1025,35 +1026,39 @@
},
{
"name": "kriswallsmith/assetic",
- "version": "v1.2.1",
+ "version": "v1.3.0",
"source": {
"type": "git",
"url": "https://github.com/kriswallsmith/assetic.git",
- "reference": "b20efe38845d20458702f97f3ff625d80805897b"
+ "reference": "56cb5d6dec9e7a68a4da2fa89844f39d41092f31"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/kriswallsmith/assetic/zipball/b20efe38845d20458702f97f3ff625d80805897b",
- "reference": "b20efe38845d20458702f97f3ff625d80805897b",
+ "url": "https://api.github.com/repos/kriswallsmith/assetic/zipball/56cb5d6dec9e7a68a4da2fa89844f39d41092f31",
+ "reference": "56cb5d6dec9e7a68a4da2fa89844f39d41092f31",
"shasum": ""
},
"require": {
"php": ">=5.3.1",
"symfony/process": "~2.1"
},
+ "conflict": {
+ "twig/twig": "<1.12"
+ },
"require-dev": {
"cssmin/cssmin": "*",
"joliclic/javascript-packer": "*",
"kamicane/packager": "*",
- "leafo/lessphp": "*",
- "leafo/scssphp": "*",
- "leafo/scssphp-compass": "*",
+ "leafo/lessphp": "^0.3.7",
+ "leafo/scssphp": "*@dev",
+ "leafo/scssphp-compass": "*@dev",
"mrclay/minify": "*",
- "patchwork/jsqueeze": "~1.0",
- "phpunit/phpunit": "~4",
+ "patchwork/jsqueeze": "~1.0|~2.0",
+ "phpunit/phpunit": "~4.8",
"psr/log": "~1.0",
"ptachoire/cssembed": "*",
- "twig/twig": "~1.6"
+ "symfony/phpunit-bridge": "~2.7",
+ "twig/twig": "~1.8|~2.0"
},
"suggest": {
"leafo/lessphp": "Assetic provides the integration with the lessphp LESS compiler",
@@ -1066,7 +1071,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.2-dev"
+ "dev-master": "1.3-dev"
}
},
"autoload": {
@@ -1095,7 +1100,7 @@
"compression",
"minification"
],
- "time": "2014-12-12 05:04:05"
+ "time": "2015-08-31 19:07:16"
},
{
"name": "misd/guzzle-bundle",
@@ -1171,16 +1176,16 @@
},
{
"name": "monolog/monolog",
- "version": "1.13.1",
+ "version": "1.17.1",
"source": {
"type": "git",
"url": "https://github.com/Seldaek/monolog.git",
- "reference": "c31a2c4e8db5da8b46c74cf275d7f109c0f249ac"
+ "reference": "0524c87587ab85bc4c2d6f5b41253ccb930a5422"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/Seldaek/monolog/zipball/c31a2c4e8db5da8b46c74cf275d7f109c0f249ac",
- "reference": "c31a2c4e8db5da8b46c74cf275d7f109c0f249ac",
+ "url": "https://api.github.com/repos/Seldaek/monolog/zipball/0524c87587ab85bc4c2d6f5b41253ccb930a5422",
+ "reference": "0524c87587ab85bc4c2d6f5b41253ccb930a5422",
"shasum": ""
},
"require": {
@@ -1191,12 +1196,14 @@
"psr/log-implementation": "1.0.0"
},
"require-dev": {
- "aws/aws-sdk-php": "~2.4, >2.4.8",
+ "aws/aws-sdk-php": "^2.4.9",
"doctrine/couchdb": "~1.0@dev",
"graylog2/gelf-php": "~1.0",
- "phpunit/phpunit": "~4.0",
- "raven/raven": "~0.5",
- "ruflin/elastica": "0.90.*",
+ "php-console/php-console": "^3.1.3",
+ "phpunit/phpunit": "~4.5",
+ "phpunit/phpunit-mock-objects": "2.3.0",
+ "raven/raven": "~0.11",
+ "ruflin/elastica": ">=0.90 <3.0",
"swiftmailer/swiftmailer": "~5.3",
"videlalvaro/php-amqplib": "~2.4"
},
@@ -1206,6 +1213,7 @@
"ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)",
"ext-mongo": "Allow sending log messages to a MongoDB server",
"graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server",
+ "php-console/php-console": "Allow sending log messages to Google Chrome",
"raven/raven": "Allow sending log messages to a Sentry server",
"rollbar/rollbar": "Allow sending log messages to Rollbar",
"ruflin/elastica": "Allow sending log messages to an Elastic Search server",
@@ -1214,7 +1222,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.13.x-dev"
+ "dev-master": "1.16.x-dev"
}
},
"autoload": {
@@ -1240,7 +1248,62 @@
"logging",
"psr-3"
],
- "time": "2015-03-09 09:58:04"
+ "time": "2015-08-31 09:17:37"
+ },
+ {
+ "name": "ob/highcharts-bundle",
+ "version": "1.2",
+ "target-dir": "Ob/HighchartsBundle",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/marcaube/ObHighchartsBundle.git",
+ "reference": "cce67aa209f2a8b14db520c8e50a15cd5f5c23d0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/marcaube/ObHighchartsBundle/zipball/cce67aa209f2a8b14db520c8e50a15cd5f5c23d0",
+ "reference": "cce67aa209f2a8b14db520c8e50a15cd5f5c23d0",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0",
+ "symfony/symfony": "~2.3",
+ "zendframework/zend-json": "2.3.0"
+ },
+ "type": "symfony-bundle",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.2.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-0": {
+ "Ob\\HighchartsBundle": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Marc Aubé"
+ }
+ ],
+ "description": "Symfony2 Bundle that ease the use of highcharts to display rich graph and charts in your app",
+ "homepage": "https://github.com/marcaube/ObHighchartsBundle",
+ "keywords": [
+ "Symfony2",
+ "chart",
+ "charting",
+ "charts",
+ "graph",
+ "graphs",
+ "highcharts",
+ "marcaube",
+ "ob"
+ ],
+ "time": "2014-08-04 23:56:54"
},
{
"name": "psr/log",
@@ -1282,22 +1345,22 @@
},
{
"name": "sensio/distribution-bundle",
- "version": "v3.0.25",
+ "version": "v3.0.31",
"target-dir": "Sensio/Bundle/DistributionBundle",
"source": {
"type": "git",
"url": "https://github.com/sensiolabs/SensioDistributionBundle.git",
- "reference": "01931139b0f067a4016d5d56e82c2b3086533b89"
+ "reference": "3a900814bd57bf20f9453ae81ff8772bc95d7fff"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sensiolabs/SensioDistributionBundle/zipball/01931139b0f067a4016d5d56e82c2b3086533b89",
- "reference": "01931139b0f067a4016d5d56e82c2b3086533b89",
+ "url": "https://api.github.com/repos/sensiolabs/SensioDistributionBundle/zipball/3a900814bd57bf20f9453ae81ff8772bc95d7fff",
+ "reference": "3a900814bd57bf20f9453ae81ff8772bc95d7fff",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
- "sensiolabs/security-checker": "~2.0",
+ "sensiolabs/security-checker": "~3.0",
"symfony/class-loader": "~2.2",
"symfony/framework-bundle": "~2.3",
"symfony/process": "~2.2"
@@ -1338,20 +1401,20 @@
"configuration",
"distribution"
],
- "time": "2015-05-29 22:35:41"
+ "time": "2015-08-03 10:07:12"
},
{
"name": "sensio/framework-extra-bundle",
- "version": "v3.0.8",
+ "version": "v3.0.10",
"source": {
"type": "git",
"url": "https://github.com/sensiolabs/SensioFrameworkExtraBundle.git",
- "reference": "a30fc18bf147bc25faf6b1d54bf55cfad4b63cba"
+ "reference": "18fc2063c4d6569cdca47a39fbac32342eb65f3c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sensiolabs/SensioFrameworkExtraBundle/zipball/a30fc18bf147bc25faf6b1d54bf55cfad4b63cba",
- "reference": "a30fc18bf147bc25faf6b1d54bf55cfad4b63cba",
+ "url": "https://api.github.com/repos/sensiolabs/SensioFrameworkExtraBundle/zipball/18fc2063c4d6569cdca47a39fbac32342eb65f3c",
+ "reference": "18fc2063c4d6569cdca47a39fbac32342eb65f3c",
"shasum": ""
},
"require": {
@@ -1393,24 +1456,23 @@
"annotations",
"controllers"
],
- "time": "2015-05-29 18:27:23"
+ "time": "2015-08-03 11:59:27"
},
{
"name": "sensiolabs/security-checker",
- "version": "v2.0.5",
+ "version": "v3.0.1",
"source": {
"type": "git",
"url": "https://github.com/sensiolabs/security-checker.git",
- "reference": "2c2a71f1c77d9765c12638c4724d9ca23658a810"
+ "reference": "7735fd97ff7303d9df776b8dbc970f949399abc9"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sensiolabs/security-checker/zipball/2c2a71f1c77d9765c12638c4724d9ca23658a810",
- "reference": "2c2a71f1c77d9765c12638c4724d9ca23658a810",
+ "url": "https://api.github.com/repos/sensiolabs/security-checker/zipball/7735fd97ff7303d9df776b8dbc970f949399abc9",
+ "reference": "7735fd97ff7303d9df776b8dbc970f949399abc9",
"shasum": ""
},
"require": {
- "ext-curl": "*",
"symfony/console": "~2.0"
},
"bin": [
@@ -1419,7 +1481,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "2.0-dev"
+ "dev-master": "3.0-dev"
}
},
"autoload": {
@@ -1438,27 +1500,27 @@
}
],
"description": "A security checker for your composer.lock",
- "time": "2015-05-28 14:22:40"
+ "time": "2015-08-11 12:11:25"
},
{
"name": "swiftmailer/swiftmailer",
- "version": "v5.4.0",
+ "version": "v5.4.1",
"source": {
"type": "git",
"url": "https://github.com/swiftmailer/swiftmailer.git",
- "reference": "31454f258f10329ae7c48763eb898a75c39e0a9f"
+ "reference": "0697e6aa65c83edf97bb0f23d8763f94e3f11421"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/31454f258f10329ae7c48763eb898a75c39e0a9f",
- "reference": "31454f258f10329ae7c48763eb898a75c39e0a9f",
+ "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/0697e6aa65c83edf97bb0f23d8763f94e3f11421",
+ "reference": "0697e6aa65c83edf97bb0f23d8763f94e3f11421",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
- "mockery/mockery": "~0.9.1"
+ "mockery/mockery": "~0.9.1,<0.9.4"
},
"type": "library",
"extra": {
@@ -1487,39 +1549,45 @@
"description": "Swiftmailer, free feature-rich PHP mailer",
"homepage": "http://swiftmailer.org",
"keywords": [
+ "email",
"mail",
"mailer"
],
- "time": "2015-03-14 06:06:39"
+ "time": "2015-06-06 14:19:39"
},
{
"name": "symfony/assetic-bundle",
- "version": "v2.6.1",
+ "version": "v2.7.0",
"source": {
"type": "git",
- "url": "https://github.com/symfony/AsseticBundle.git",
- "reference": "422b0add2110f0cf9bc7a873a386ea053f4a89f0"
+ "url": "https://github.com/symfony/assetic-bundle.git",
+ "reference": "3ae5c8ca3079b6e0033cc9fbfb6500e2bc964da5"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/AsseticBundle/zipball/422b0add2110f0cf9bc7a873a386ea053f4a89f0",
- "reference": "422b0add2110f0cf9bc7a873a386ea053f4a89f0",
+ "url": "https://api.github.com/repos/symfony/assetic-bundle/zipball/3ae5c8ca3079b6e0033cc9fbfb6500e2bc964da5",
+ "reference": "3ae5c8ca3079b6e0033cc9fbfb6500e2bc964da5",
"shasum": ""
},
"require": {
- "kriswallsmith/assetic": "~1.2",
+ "kriswallsmith/assetic": "~1.3",
"php": ">=5.3.0",
"symfony/console": "~2.3",
"symfony/dependency-injection": "~2.3",
"symfony/framework-bundle": "~2.3",
"symfony/yaml": "~2.3"
},
+ "conflict": {
+ "kriswallsmith/spork": "<=0.2",
+ "twig/twig": "<1.20"
+ },
"require-dev": {
- "kriswallsmith/spork": "~0.2",
+ "kriswallsmith/spork": "~0.3",
"patchwork/jsqueeze": "~1.0",
"symfony/class-loader": "~2.3",
"symfony/css-selector": "~2.3",
"symfony/dom-crawler": "~2.3",
+ "symfony/phpunit-bridge": "~2.7",
"symfony/twig-bundle": "~2.3"
},
"suggest": {
@@ -1529,7 +1597,7 @@
"type": "symfony-bundle",
"extra": {
"branch-alias": {
- "dev-master": "2.5-dev"
+ "dev-master": "2.7-dev"
}
},
"autoload": {
@@ -1555,38 +1623,38 @@
"compression",
"minification"
],
- "time": "2015-01-27 12:45:16"
+ "time": "2015-09-01 00:05:29"
},
{
"name": "symfony/monolog-bundle",
- "version": "v2.7.1",
+ "version": "2.8.1",
"source": {
"type": "git",
- "url": "https://github.com/symfony/MonologBundle.git",
- "reference": "9320b6863404c70ebe111e9040dab96f251de7ac"
+ "url": "https://github.com/symfony/monolog-bundle.git",
+ "reference": "7117b9a145722e3c5768db4585f6ad0643ed5c4a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/MonologBundle/zipball/9320b6863404c70ebe111e9040dab96f251de7ac",
- "reference": "9320b6863404c70ebe111e9040dab96f251de7ac",
+ "url": "https://api.github.com/repos/symfony/monolog-bundle/zipball/7117b9a145722e3c5768db4585f6ad0643ed5c4a",
+ "reference": "7117b9a145722e3c5768db4585f6ad0643ed5c4a",
"shasum": ""
},
"require": {
"monolog/monolog": "~1.8",
"php": ">=5.3.2",
- "symfony/config": "~2.3",
- "symfony/dependency-injection": "~2.3",
- "symfony/http-kernel": "~2.3",
- "symfony/monolog-bridge": "~2.3"
+ "symfony/config": "~2.3|3.*",
+ "symfony/dependency-injection": "~2.3|3.*",
+ "symfony/http-kernel": "~2.3|3.*",
+ "symfony/monolog-bridge": "~2.3|3.*"
},
"require-dev": {
- "symfony/console": "~2.3",
+ "symfony/console": "~2.3|3.*",
"symfony/yaml": "~2.3"
},
"type": "symfony-bundle",
"extra": {
"branch-alias": {
- "dev-master": "2.7.x-dev"
+ "dev-master": "2.8.x-dev"
}
},
"autoload": {
@@ -1614,19 +1682,19 @@
"log",
"logging"
],
- "time": "2015-01-04 20:21:17"
+ "time": "2015-10-02 11:51:59"
},
{
"name": "symfony/swiftmailer-bundle",
"version": "v2.3.8",
"source": {
"type": "git",
- "url": "https://github.com/symfony/SwiftmailerBundle.git",
+ "url": "https://github.com/symfony/swiftmailer-bundle.git",
"reference": "970b13d01871207e81d17b17ddda025e7e21e797"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/SwiftmailerBundle/zipball/970b13d01871207e81d17b17ddda025e7e21e797",
+ "url": "https://api.github.com/repos/symfony/swiftmailer-bundle/zipball/970b13d01871207e81d17b17ddda025e7e21e797",
"reference": "970b13d01871207e81d17b17ddda025e7e21e797",
"shasum": ""
},
@@ -1675,23 +1743,23 @@
},
{
"name": "symfony/symfony",
- "version": "2.7.x-dev",
+ "version": "v2.7.5",
"source": {
"type": "git",
"url": "https://github.com/symfony/symfony.git",
- "reference": "7493c2bef54fb818c5304bdd9d2194890b839422"
+ "reference": "619528a274647cffc1792063c3ea04c4fa8266a0"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/symfony/zipball/7493c2bef54fb818c5304bdd9d2194890b839422",
- "reference": "7493c2bef54fb818c5304bdd9d2194890b839422",
+ "url": "https://api.github.com/repos/symfony/symfony/zipball/619528a274647cffc1792063c3ea04c4fa8266a0",
+ "reference": "619528a274647cffc1792063c3ea04c4fa8266a0",
"shasum": ""
},
"require": {
- "doctrine/common": "~2.3",
+ "doctrine/common": "~2.4",
"php": ">=5.3.9",
"psr/log": "~1.0",
- "twig/twig": "~1.18"
+ "twig/twig": "~1.20|~2.0"
},
"replace": {
"symfony/asset": "self.version",
@@ -1741,9 +1809,9 @@
},
"require-dev": {
"doctrine/data-fixtures": "1.0.*",
- "doctrine/dbal": "~2.2",
+ "doctrine/dbal": "~2.4",
"doctrine/doctrine-bundle": "~1.2",
- "doctrine/orm": "~2.2,>=2.2.3",
+ "doctrine/orm": "~2.4,>=2.4.5",
"egulias/email-validator": "~1.2",
"ircmaxell/password-compat": "~1.0",
"monolog/monolog": "~1.11",
@@ -1793,24 +1861,24 @@
"keywords": [
"framework"
],
- "time": "2015-05-30 17:16:04"
+ "time": "2015-09-25 11:16:52"
},
{
"name": "twig/extensions",
- "version": "v1.2.0",
+ "version": "v1.3.0",
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig-extensions.git",
- "reference": "8cf4b9fe04077bd54fc73f4fde83347040c3b8cd"
+ "reference": "449e3c8a9ffad7c2479c7864557275a32b037499"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/twigphp/Twig-extensions/zipball/8cf4b9fe04077bd54fc73f4fde83347040c3b8cd",
- "reference": "8cf4b9fe04077bd54fc73f4fde83347040c3b8cd",
+ "url": "https://api.github.com/repos/twigphp/Twig-extensions/zipball/449e3c8a9ffad7c2479c7864557275a32b037499",
+ "reference": "449e3c8a9ffad7c2479c7864557275a32b037499",
"shasum": ""
},
"require": {
- "twig/twig": "~1.12"
+ "twig/twig": "~1.20|~2.0"
},
"require-dev": {
"symfony/translation": "~2.3"
@@ -1821,7 +1889,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.2.x-dev"
+ "dev-master": "1.3-dev"
}
},
"autoload": {
@@ -1845,29 +1913,33 @@
"i18n",
"text"
],
- "time": "2014-10-30 14:30:03"
+ "time": "2015-08-22 16:38:35"
},
{
"name": "twig/twig",
- "version": "v1.18.1",
+ "version": "v1.22.2",
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig.git",
- "reference": "9f70492f44398e276d1b81c1b43adfe6751c7b7f"
+ "reference": "79249fc8c9ff62e41e217e0c630e2e00bcadda6a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/twigphp/Twig/zipball/9f70492f44398e276d1b81c1b43adfe6751c7b7f",
- "reference": "9f70492f44398e276d1b81c1b43adfe6751c7b7f",
+ "url": "https://api.github.com/repos/twigphp/Twig/zipball/79249fc8c9ff62e41e217e0c630e2e00bcadda6a",
+ "reference": "79249fc8c9ff62e41e217e0c630e2e00bcadda6a",
"shasum": ""
},
"require": {
"php": ">=5.2.7"
},
+ "require-dev": {
+ "symfony/debug": "~2.7",
+ "symfony/phpunit-bridge": "~2.7"
+ },
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.18-dev"
+ "dev-master": "1.22-dev"
}
},
"autoload": {
@@ -1902,7 +1974,116 @@
"keywords": [
"templating"
],
- "time": "2015-04-19 08:30:27"
+ "time": "2015-09-22 13:59:32"
+ },
+ {
+ "name": "zendframework/zend-json",
+ "version": "2.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/zendframework/zend-json.git",
+ "reference": "cf8e594a8a6516d06c25a3dc07e3be462fbea84d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/zendframework/zend-json/zipball/cf8e594a8a6516d06c25a3dc07e3be462fbea84d",
+ "reference": "cf8e594a8a6516d06c25a3dc07e3be462fbea84d",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.23",
+ "zendframework/zend-stdlib": "self.version"
+ },
+ "require-dev": {
+ "fabpot/php-cs-fixer": "1.7.*",
+ "phpunit/phpunit": "~4.0",
+ "satooshi/php-coveralls": "dev-master",
+ "zendframework/zend-http": "self.version",
+ "zendframework/zend-server": "self.version"
+ },
+ "suggest": {
+ "zendframework/zend-http": "Zend\\Http component",
+ "zendframework/zend-server": "Zend\\Server component",
+ "zendframework/zendxml": "To support Zend\\Json\\Json::fromXml() usage"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.2-dev",
+ "dev-develop": "2.3-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Zend\\Json\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "description": "provides convenience methods for serializing native PHP to JSON and decoding JSON to native PHP",
+ "homepage": "https://github.com/zendframework/zend-json",
+ "keywords": [
+ "json",
+ "zf2"
+ ],
+ "time": "2014-03-12 16:10:15"
+ },
+ {
+ "name": "zendframework/zend-stdlib",
+ "version": "2.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/zendframework/zend-stdlib.git",
+ "reference": "426b5396e89e7da2db9678bc9a0b57865f84fe0f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/zendframework/zend-stdlib/zipball/426b5396e89e7da2db9678bc9a0b57865f84fe0f",
+ "reference": "426b5396e89e7da2db9678bc9a0b57865f84fe0f",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.23"
+ },
+ "require-dev": {
+ "fabpot/php-cs-fixer": "1.7.*",
+ "phpunit/phpunit": "~4.0",
+ "satooshi/php-coveralls": "dev-master",
+ "zendframework/zend-eventmanager": "self.version",
+ "zendframework/zend-filter": "self.version",
+ "zendframework/zend-serializer": "self.version",
+ "zendframework/zend-servicemanager": "self.version"
+ },
+ "suggest": {
+ "zendframework/zend-eventmanager": "To support aggregate hydrator usage",
+ "zendframework/zend-filter": "To support naming strategy hydrator usage",
+ "zendframework/zend-serializer": "Zend\\Serializer component",
+ "zendframework/zend-servicemanager": "To support hydrator plugin manager usage"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.2-dev",
+ "dev-develop": "2.3-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Zend\\Stdlib\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "homepage": "https://github.com/zendframework/zend-stdlib",
+ "keywords": [
+ "stdlib",
+ "zf2"
+ ],
+ "time": "2014-03-12 16:10:15"
}
],
"packages-dev": [
@@ -1957,11 +2138,7 @@
],
"aliases": [],
"minimum-stability": "stable",
- "stability-flags": {
- "symfony/symfony": 20,
- "doctrine/migrations": 20,
- "doctrine/doctrine-migrations-bundle": 20
- },
+ "stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
diff --git a/src/Skobkin/Bundle/PointToolsBundle/Command/UpdateSubscriptionsCommand.php b/src/Skobkin/Bundle/PointToolsBundle/Command/UpdateSubscriptionsCommand.php
index 76c02ee..9b1b8d9 100644
--- a/src/Skobkin/Bundle/PointToolsBundle/Command/UpdateSubscriptionsCommand.php
+++ b/src/Skobkin/Bundle/PointToolsBundle/Command/UpdateSubscriptionsCommand.php
@@ -128,7 +128,7 @@ class UpdateSubscriptionsCommand extends ContainerAwareCommand
}
// @todo move to the config
- usleep(200000);
+ usleep(500000);
}
}
}
\ No newline at end of file
diff --git a/src/Skobkin/Bundle/PointToolsBundle/Controller/ApiController.php b/src/Skobkin/Bundle/PointToolsBundle/Controller/ApiController.php
new file mode 100644
index 0000000..67b62b9
--- /dev/null
+++ b/src/Skobkin/Bundle/PointToolsBundle/Controller/ApiController.php
@@ -0,0 +1,46 @@
+getDoctrine()->getRepository('SkobkinPointToolsBundle:SubscriptionEvent')->createQueryBuilder('se');
+ $qb
+ ->select(['se', 'sub'])
+ ->innerJoin('se.subscriber', 'sub')
+ ->where($qb->expr()->eq('se.author', ':author'))
+ ->orderBy('se.date', 'desc')
+ ->setParameter('author', $user)
+ ->setMaxResults(20)
+ ;
+
+ $data = [];
+
+ /** @var SubscriptionEvent $event */
+ foreach ($qb->getQuery()->getResult() as $event) {
+ $data[] = [
+ 'user' => $event->getSubscriber()->getLogin(),
+ 'action' => $event->getAction(),
+ 'datetime' => $event->getDate()->format('d.m.Y H:i:s'),
+ ];
+ }
+
+ return new JsonResponse($data);
+ }
+}
diff --git a/src/Skobkin/Bundle/PointToolsBundle/Controller/EventsController.php b/src/Skobkin/Bundle/PointToolsBundle/Controller/EventsController.php
new file mode 100644
index 0000000..1eab3e7
--- /dev/null
+++ b/src/Skobkin/Bundle/PointToolsBundle/Controller/EventsController.php
@@ -0,0 +1,20 @@
+getDoctrine()->getManager();
+
+ return $this->render('SkobkinPointToolsBundle:Events:last.html.twig', [
+ 'last_events' => $em->getRepository('SkobkinPointToolsBundle:SubscriptionEvent')->getLastSubscriptionEvents(20),
+ ]);
+ }
+}
diff --git a/src/Skobkin/Bundle/PointToolsBundle/Controller/MainController.php b/src/Skobkin/Bundle/PointToolsBundle/Controller/MainController.php
index 876a6d4..f6a3d34 100644
--- a/src/Skobkin/Bundle/PointToolsBundle/Controller/MainController.php
+++ b/src/Skobkin/Bundle/PointToolsBundle/Controller/MainController.php
@@ -13,40 +13,12 @@ class MainController extends Controller
/** @var EntityManager $em */
$em = $this->getDoctrine()->getManager();
- /** @var QueryBuilder $qb */
- $qb = $em->getRepository('SkobkinPointToolsBundle:User')->createQueryBuilder('u');
-
- // All users in the system count
- $usersCount = $qb->select('COUNT(u)')->getQuery()->getSingleScalarResult();
-
- $qb = $em->getRepository('SkobkinPointToolsBundle:Subscription')->createQueryBuilder('s');
-
- // Service subscribers count
- $subscribersCount = $qb
- ->select('COUNT(s)')
- ->innerJoin('s.author', 'a')
- ->where('a.login = :login')
- ->setParameter('login', $this->container->getParameter('point_login'))
- ->getQuery()->getSingleScalarResult()
- ;
-
- $qb = $em->getRepository('SkobkinPointToolsBundle:SubscriptionEvent')->createQueryBuilder('se');
-
- $now = new \DateTime();
-
- $eventsCount = $qb
- ->select('COUNT(se)')
- ->where('se.date > :time')
- ->setParameter('time', $now->sub(new \DateInterval('PT24H')))
- ->getQuery()->getSingleScalarResult()
- ;
-
return $this->render('SkobkinPointToolsBundle:Main:index.html.twig', [
- 'users_count' => $usersCount,
- 'subscribers_count' => $subscribersCount,
- 'events_count' => $eventsCount,
+ 'users_count' => $em->getRepository('SkobkinPointToolsBundle:User')->getUsersCount(),
+ 'subscribers_count' => $em->getRepository('SkobkinPointToolsBundle:Subscription')->getUserSubscribersCountById($this->container->getParameter('point_id')),
+ 'events_count' => $em->getRepository('SkobkinPointToolsBundle:SubscriptionEvent')->getLastDayEventsCount(),
'service_login' => $this->container->getParameter('point_login'),
+ 'last_events' => $em->getRepository('SkobkinPointToolsBundle:SubscriptionEvent')->getLastSubscriptionEvents(10),
]);
}
-
}
diff --git a/src/Skobkin/Bundle/PointToolsBundle/Controller/UserController.php b/src/Skobkin/Bundle/PointToolsBundle/Controller/UserController.php
index d294de5..4a78cc9 100644
--- a/src/Skobkin/Bundle/PointToolsBundle/Controller/UserController.php
+++ b/src/Skobkin/Bundle/PointToolsBundle/Controller/UserController.php
@@ -2,12 +2,12 @@
namespace Skobkin\Bundle\PointToolsBundle\Controller;
-use Doctrine\ORM\QueryBuilder;
use Doctrine\ORM\EntityManager;
use Skobkin\Bundle\PointToolsBundle\Entity\TopUserDTO;
use Skobkin\Bundle\PointToolsBundle\Entity\User;
use Skobkin\Bundle\PointToolsBundle\Service\UserApi;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
+use Ob\HighchartsBundle\Highcharts\Highchart;
use Symfony\Component\HttpFoundation\Request;
class UserController extends Controller
@@ -17,16 +17,11 @@ class UserController extends Controller
*/
public function showAction($login)
{
- /** @var QueryBuilder $qb */
- $qb = $this->getDoctrine()->getManager()->getRepository('SkobkinPointToolsBundle:User')->createQueryBuilder('u');
+ /** @var EntityManager $em */
+ $em = $this->getDoctrine()->getManager();
- $user = $qb
- ->select('u')
- ->where('LOWER(u.login) = LOWER(:login)')
- ->setMaxResults(1)
- ->setParameter('login', $login)
- ->getQuery()->getOneOrNullResult()
- ;
+ /** @var User $user */
+ $user = $em->getRepository('SkobkinPointToolsBundle:User')->findUserByLogin($login);
if (!$user) {
throw $this->createNotFoundException('User ' . $login . ' not found.');
@@ -34,56 +29,23 @@ class UserController extends Controller
$userApi = $this->container->get('skobkin_point_tools.api_user');
- $qb = $this->getDoctrine()->getManager()->getRepository('SkobkinPointToolsBundle:User')->createQueryBuilder('u');
-
- $subscribers = $qb
- ->select('u')
- ->innerJoin('u.subscriptions', 's')
- ->where('s.author = :author')
- ->orderBy('u.login', 'asc')
- ->setParameter('author', $user->getId())
- ->getQuery()->getResult()
- ;
-
- $qb = $this->getDoctrine()->getManager()->getRepository('SkobkinPointToolsBundle:SubscriptionEvent')->createQueryBuilder('se');
-
- $subscriptionsEvents = $qb
- ->select()
- ->where('se.author = :author')
- ->orderBy('se.date', 'desc')
- ->setMaxResults(10)
- ->setParameter('author', $user)
- ->getQuery()->getResult()
- ;
-
return $this->render('SkobkinPointToolsBundle:User:show.html.twig', [
'user' => $user,
- 'subscribers' => $subscribers,
- 'log' => $subscriptionsEvents,
+ 'subscribers' => $em->getRepository('SkobkinPointToolsBundle:User')->findUserSubscribersById($user->getId()),
+ 'log' => $em->getRepository('SkobkinPointToolsBundle:SubscriptionEvent')->getUserLastSubscribersEventsById($user, 10),
'avatar_url' => $userApi->getAvatarUrl($user, UserApi::AVATAR_SIZE_LARGE),
]);
}
public function topAction()
{
- /** @var EntityManager $em */
- $em = $this->getDoctrine()->getManager();
+ $topUsers = $this->getDoctrine()->getManager()->getRepository('SkobkinPointToolsBundle:User')->getTopUsers();
- /** @var QueryBuilder $qb */
- $qb = $em->getRepository('SkobkinPointToolsBundle:Subscription')->createQueryBuilder('s');
-
- /** @var TopUserDTO[] $topUsers */
- $topUsers = $qb
- ->select(['COUNT(s.subscriber) as cnt', 'NEW SkobkinPointToolsBundle:TopUserDTO(a.login, COUNT(s.subscriber))'])
- ->innerJoin('s.author', 'a')
- ->orderBy('cnt', 'desc')
- ->groupBy('a.id')
- ->setMaxResults(30)
- ->getQuery()->getResult()
- ;
+ $topChart = $this->createTopUsersGraph($topUsers);
return $this->render('@SkobkinPointTools/User/top.html.twig', [
- 'top_users' => $topUsers
+ 'top_users' => $topUsers,
+ 'top_chart' => $topChart,
]);
}
@@ -99,4 +61,47 @@ class UserController extends Controller
}
return $this->redirectToRoute('user_show', ['login' => $login]);
}
+
+ /**
+ * @param TopUserDTO[] $topUsers
+ * @return Highchart
+ */
+ private function createTopUsersGraph(array $topUsers = [])
+ {
+ $translator = $this->container->get('translator');
+
+ $chartData = [
+ 'titles' => [],
+ 'subscribers' => [],
+ ];
+
+ // Preparing chart data
+ foreach ($topUsers as $user) {
+ $chartData['titles'][] = $user->login;
+ $chartData['subscribers'][] = $user->subscribersCount;
+ }
+
+ // Chart
+ $series = [[
+ 'name' => $translator->trans('Subscribers'),
+ 'data' => $chartData['subscribers'],
+ ]];
+
+ // Initializing chart
+ $ob = new Highchart();
+ $ob->chart->renderTo('top-chart');
+ $ob->chart->type('bar');
+ $ob->title->text($translator->trans('Top users'));
+ $ob->xAxis->title(['text' => null]);
+ $ob->xAxis->categories($chartData['titles']);
+ $ob->yAxis->title(['text' => $translator->trans('amount')]);
+ $ob->plotOptions->bar([
+ 'dataLabels' => [
+ 'enabled' => true
+ ]
+ ]);
+ $ob->series($series);
+
+ return $ob;
+ }
}
diff --git a/src/Skobkin/Bundle/PointToolsBundle/Entity/Subscription.php b/src/Skobkin/Bundle/PointToolsBundle/Entity/Subscription.php
index 03b18a3..dfd4c31 100644
--- a/src/Skobkin/Bundle/PointToolsBundle/Entity/Subscription.php
+++ b/src/Skobkin/Bundle/PointToolsBundle/Entity/Subscription.php
@@ -7,10 +7,10 @@ use Doctrine\ORM\Mapping as ORM;
/**
* Subscription
*
- * @ORM\Table(name="subscriptions.subscriptions", uniqueConstraints={
+ * @ORM\Table(name="subscriptions.subscriptions", schema="subscriptions", uniqueConstraints={
* @ORM\UniqueConstraint(name="subscription_unique", columns={"author_id", "subscriber_id"})}
* )
- * @ORM\Entity
+ * @ORM\Entity(repositoryClass="Skobkin\Bundle\PointToolsBundle\Entity\SubscriptionRepository")
*/
class Subscription
{
diff --git a/src/Skobkin/Bundle/PointToolsBundle/Entity/SubscriptionEvent.php b/src/Skobkin/Bundle/PointToolsBundle/Entity/SubscriptionEvent.php
index 2b98686..22c4a4e 100644
--- a/src/Skobkin/Bundle/PointToolsBundle/Entity/SubscriptionEvent.php
+++ b/src/Skobkin/Bundle/PointToolsBundle/Entity/SubscriptionEvent.php
@@ -7,12 +7,12 @@ use Doctrine\ORM\Mapping as ORM;
/**
* SubscriptionEvent
*
- * @ORM\Table(name="subscriptions.log", indexes={
+ * @ORM\Table(name="subscriptions.log", schema="subscriptions", indexes={
* @ORM\Index(name="author_idx", columns={"author_id"}),
* @ORM\Index(name="subscriber_idx", columns={"subscriber_id"}),
* @ORM\Index(name="date_idx", columns={"date"})
* })
- * @ORM\Entity
+ * @ORM\Entity(repositoryClass="Skobkin\Bundle\PointToolsBundle\Entity\SubscriptionEventRepository")
* @ORM\HasLifecycleCallbacks
*/
class SubscriptionEvent
diff --git a/src/Skobkin/Bundle/PointToolsBundle/Entity/SubscriptionEventRepository.php b/src/Skobkin/Bundle/PointToolsBundle/Entity/SubscriptionEventRepository.php
new file mode 100644
index 0000000..b12332c
--- /dev/null
+++ b/src/Skobkin/Bundle/PointToolsBundle/Entity/SubscriptionEventRepository.php
@@ -0,0 +1,75 @@
+createQueryBuilder('se');
+
+ $now = new \DateTime();
+
+ return $qb
+ ->select('COUNT(se)')
+ ->where('se.date > :time')
+ ->setParameter('time', $now->sub(new \DateInterval('PT24H')))
+ ->getQuery()->getSingleScalarResult()
+ ;
+ }
+
+ /**
+ * @param User $user
+ * @param integer $limit
+ * @return SubscriptionEvent[]
+ */
+ public function getUserLastSubscribersEventsById(User $user, $limit)
+ {
+ if (!is_int($limit)) {
+ throw new \InvalidArgumentException('$limit must be an integer');
+ }
+
+ $qb = $this->createQueryBuilder('se');
+
+ return $qb
+ ->select(['se', 's'])
+ ->join('se.subscriber', 's')
+ ->where('se.author = :author')
+ ->orderBy('se.date', 'desc')
+ ->setMaxResults($limit)
+ ->setParameter('author', $user)
+ ->getQuery()->getResult()
+ ;
+ }
+
+ /**
+ * Get last $limit subscriptions
+ *
+ * @param integer $limit
+ * @return SubscriptionEvent[]
+ */
+ public function getLastSubscriptionEvents($limit)
+ {
+ if (!is_int($limit)) {
+ throw new \InvalidArgumentException('$limit must be an integer');
+ }
+
+ $qb = $this->createQueryBuilder('se');
+
+ return $qb
+ ->select()
+ ->orderBy('se.date', 'desc')
+ ->setMaxResults($limit)
+ ->getQuery()
+ ->setFetchMode('SkobkinPointToolsBundle:SubscriptionEvent', 'author', ClassMetadata::FETCH_EAGER)
+ ->setFetchMode('SkobkinPointToolsBundle:SubscriptionEvent', 'subscriber', ClassMetadata::FETCH_EAGER)
+ ->getResult()
+ ;
+ }
+}
\ No newline at end of file
diff --git a/src/Skobkin/Bundle/PointToolsBundle/Entity/SubscriptionRepository.php b/src/Skobkin/Bundle/PointToolsBundle/Entity/SubscriptionRepository.php
new file mode 100644
index 0000000..3bc7064
--- /dev/null
+++ b/src/Skobkin/Bundle/PointToolsBundle/Entity/SubscriptionRepository.php
@@ -0,0 +1,28 @@
+createQueryBuilder('s');
+ return $qb
+ ->select('COUNT(s)')
+ ->innerJoin('s.author', 'a')
+ ->where('a.id = :id')
+ ->setParameter('id', $id)
+ ->getQuery()->getSingleScalarResult()
+ ;
+ }
+}
\ No newline at end of file
diff --git a/src/Skobkin/Bundle/PointToolsBundle/Entity/User.php b/src/Skobkin/Bundle/PointToolsBundle/Entity/User.php
index 051f9be..038dee0 100644
--- a/src/Skobkin/Bundle/PointToolsBundle/Entity/User.php
+++ b/src/Skobkin/Bundle/PointToolsBundle/Entity/User.php
@@ -8,8 +8,8 @@ use Doctrine\ORM\Mapping as ORM;
/**
* User
*
- * @ORM\Table(name="users.users")
- * @ORM\Entity
+ * @ORM\Table(name="users.users", schema="users")
+ * @ORM\Entity(repositoryClass="Skobkin\Bundle\PointToolsBundle\Entity\UserRepository")
* @ORM\HasLifecycleCallbacks
*/
class User
@@ -25,7 +25,7 @@ class User
/**
* @var string
*
- * @ORM\Column(name="login", type="string", length=255, nullable=false, unique=true)
+ * @ORM\Column(name="login", type="string", length=255, nullable=false)
*/
private $login;
@@ -71,8 +71,17 @@ class User
private $newSubscriberEvents;
- public function __construct()
+ /**
+ * @param int $id
+ * @param string $login
+ * @param string $name
+ */
+ public function __construct($id = null, $login = null, $name = null)
{
+ $this->id = $id;
+ $this->login = $login;
+ $this->name = $name;
+
$this->subscribers = new ArrayCollection();
$this->subscriptions = new ArrayCollection();
$this->newSubscriberEvents = new ArrayCollection();
diff --git a/src/Skobkin/Bundle/PointToolsBundle/Entity/UserRepository.php b/src/Skobkin/Bundle/PointToolsBundle/Entity/UserRepository.php
new file mode 100644
index 0000000..3c9db8d
--- /dev/null
+++ b/src/Skobkin/Bundle/PointToolsBundle/Entity/UserRepository.php
@@ -0,0 +1,82 @@
+createQueryBuilder('u');
+
+ return $qb
+ ->select('u')
+ ->where('LOWER(u.login) = LOWER(:login)')
+ ->setMaxResults(1)
+ ->setParameter('login', $login)
+ ->getQuery()->getOneOrNullResult()
+ ;
+ }
+
+ /**
+ * @return integer
+ */
+ public function getUsersCount()
+ {
+ $qb = $this->createQueryBuilder('u');
+
+ return $qb->select('COUNT(u)')->getQuery()->getSingleScalarResult();
+ }
+
+ /**
+ * @param integer $id
+ * @return User[]
+ */
+ public function findUserSubscribersById($id)
+ {
+ if (!is_int($id)) {
+ throw new \InvalidArgumentException('$id must be an integer');
+ }
+
+ $qb = $this->createQueryBuilder('u');
+
+ return $qb
+ ->select('u')
+ ->innerJoin('u.subscriptions', 's')
+ ->where('s.author = :author')
+ ->orderBy('u.login', 'asc')
+ ->setParameter('author', $id)
+ ->getQuery()->getResult()
+ ;
+ }
+
+ /**
+ * @return TopUserDTO[]
+ */
+ public function getTopUsers($limit = 30)
+ {
+ if (!is_int($limit)) {
+ throw new \InvalidArgumentException('$limit must be an integer');
+ }
+
+ // TODO: refactor query
+ $qb = $this->getEntityManager()->getRepository('SkobkinPointToolsBundle:Subscription')->createQueryBuilder('s');
+
+ return $qb
+ ->select(['COUNT(s.subscriber) as cnt', 'NEW SkobkinPointToolsBundle:TopUserDTO(a.login, COUNT(s.subscriber))'])
+ ->innerJoin('s.author', 'a')
+ ->orderBy('cnt', 'desc')
+ ->groupBy('a.id')
+ ->setMaxResults($limit)
+ ->getQuery()->getResult()
+ ;
+ }
+}
\ No newline at end of file
diff --git a/src/Skobkin/Bundle/PointToolsBundle/Resources/config/api/routing.yml b/src/Skobkin/Bundle/PointToolsBundle/Resources/config/api/routing.yml
new file mode 100644
index 0000000..a094f12
--- /dev/null
+++ b/src/Skobkin/Bundle/PointToolsBundle/Resources/config/api/routing.yml
@@ -0,0 +1,5 @@
+last_user_events:
+ path: /user/id/{id}/events/subscribers
+ defaults: { _controller: SkobkinPointToolsBundle:Api:lastUserSubscribersById, _format: json }
+ requirements:
+ id: \d+
\ No newline at end of file
diff --git a/src/Skobkin/Bundle/PointToolsBundle/Resources/config/routing.yml b/src/Skobkin/Bundle/PointToolsBundle/Resources/config/routing.yml
index a84e4ae..98c91f7 100644
--- a/src/Skobkin/Bundle/PointToolsBundle/Resources/config/routing.yml
+++ b/src/Skobkin/Bundle/PointToolsBundle/Resources/config/routing.yml
@@ -15,4 +15,12 @@ user_show:
users_top:
path: /top
- defaults: { _controller: SkobkinPointToolsBundle:User:top }
\ No newline at end of file
+ defaults: { _controller: SkobkinPointToolsBundle:User:top }
+
+events_last:
+ path: /last
+ defaults: { _controller: SkobkinPointToolsBundle:Events:last }
+
+skobkin_point_tools:
+ resource: "@SkobkinPointToolsBundle/Resources/config/api/routing.yml"
+ prefix: /api/v1
diff --git a/src/Skobkin/Bundle/PointToolsBundle/Resources/translations/messages.ru.yml b/src/Skobkin/Bundle/PointToolsBundle/Resources/translations/messages.ru.yml
index ab55c66..e1e9d66 100644
--- a/src/Skobkin/Bundle/PointToolsBundle/Resources/translations/messages.ru.yml
+++ b/src/Skobkin/Bundle/PointToolsBundle/Resources/translations/messages.ru.yml
@@ -5,6 +5,7 @@ Toggle navigation: Переключить навигацию
Main: Главная
Top: Топ
Report a bug: Сообщить об ошибке
+Last: Последнее
# Подвал
Source code: Исходный код
@@ -13,6 +14,9 @@ Source code: Исходный код
All users: Всего пользователей
Subscribed users: Подписчиков сервиса
24 hours events: Событий за сутки
+Author: Автор
+Subscriber: Подписчик
+Last events: Последние события
Username: Имя пользователя
Search: Поиск
@@ -29,4 +33,5 @@ No log data found: Лог отсутствует
# Топ пользователей
Top users: Популярные пользователи
-Subscribers count: Подписчиков
\ No newline at end of file
+Subscribers count: Подписчиков
+amount: Количество
\ No newline at end of file
diff --git a/src/Skobkin/Bundle/PointToolsBundle/Resources/views/Events/last.html.twig b/src/Skobkin/Bundle/PointToolsBundle/Resources/views/Events/last.html.twig
new file mode 100644
index 0000000..8ab37a6
--- /dev/null
+++ b/src/Skobkin/Bundle/PointToolsBundle/Resources/views/Events/last.html.twig
@@ -0,0 +1,55 @@
+{% extends "::base.html.twig" %}
+
+{% block content %}
+ {# TODO classes #}
+
+ {% if last_events|length > 0 %}
+
+
+
+
+
+
+
+
+ {{ 'Subscriber'|trans }}
+ {{ 'Author'|trans }}
+ {{ 'Action'|trans }}
+ {{ 'Date'|trans }}
+
+
+
+ {% for event in last_events %}
+
+
+ @{{ event.subscriber.login }}
+
+
+ @{{ event.author.login }}
+
+
+
+
+
+ {# Use DateTime helper: https://sonata-project.org/bundles/intl/master/doc/reference/datetime.html #}
+ {{ event.date|date('d F Y H:i:s') }}
+
+
+ {% endfor %}
+
+
+
+
+
+
+ {% else %}
+
{{ 'No log data found'|trans }}
+ {% endif %}
+
+{% endblock %}
diff --git a/src/Skobkin/Bundle/PointToolsBundle/Resources/views/Main/index.html.twig b/src/Skobkin/Bundle/PointToolsBundle/Resources/views/Main/index.html.twig
index 2a2c3c9..5274662 100644
--- a/src/Skobkin/Bundle/PointToolsBundle/Resources/views/Main/index.html.twig
+++ b/src/Skobkin/Bundle/PointToolsBundle/Resources/views/Main/index.html.twig
@@ -26,7 +26,7 @@
{{ '24 hours events'|trans }}
-
{{ events_count }}
+
{% endblock %}
diff --git a/src/Skobkin/Bundle/PointToolsBundle/Resources/views/User/show.html.twig b/src/Skobkin/Bundle/PointToolsBundle/Resources/views/User/show.html.twig
index 1c06370..7630503 100644
--- a/src/Skobkin/Bundle/PointToolsBundle/Resources/views/User/show.html.twig
+++ b/src/Skobkin/Bundle/PointToolsBundle/Resources/views/User/show.html.twig
@@ -15,7 +15,7 @@
@@ -66,7 +66,7 @@
- {# Use DateTime helper: https://sonata-project.org/bundles/intl/master/doc/reference/datetime.html #}
+ {# @todo Use DateTime helper: https://sonata-project.org/bundles/intl/master/doc/reference/datetime.html #}
{{ event.date|date('d F Y H:i:s') }}
diff --git a/src/Skobkin/Bundle/PointToolsBundle/Resources/views/User/top.html.twig b/src/Skobkin/Bundle/PointToolsBundle/Resources/views/User/top.html.twig
index 8d9903c..d5ba95d 100644
--- a/src/Skobkin/Bundle/PointToolsBundle/Resources/views/User/top.html.twig
+++ b/src/Skobkin/Bundle/PointToolsBundle/Resources/views/User/top.html.twig
@@ -2,23 +2,17 @@
{% block header_title %}Top @ Point Tools{% endblock %}
-{% block content %}
- {{ 'Top users'|trans }}
-
-
-
-
- {{ 'User'|trans }}
- {{ 'Subscribers count'|trans }}
-
-
-
- {% for user in top_users %}
-
- @{{ user.login }}
- {{ user.subscribersCount }}
-
- {% endfor %}
-
-
+{% block head_js %}
+ {{ parent() }}
+
+
+
+{% endblock %}
+
+{% block content %}
+
+
+
{% endblock %}
diff --git a/src/Skobkin/Bundle/PointToolsBundle/Service/Exceptions/ApiException.php b/src/Skobkin/Bundle/PointToolsBundle/Service/Exceptions/ApiException.php
new file mode 100644
index 0000000..0eb8fd5
--- /dev/null
+++ b/src/Skobkin/Bundle/PointToolsBundle/Service/Exceptions/ApiException.php
@@ -0,0 +1,9 @@
+userId = $userId;
+ $this->login = $login;
+ }
+
+ /**
+ * Returns ID of user which was not found
+ *
+ * @return int
+ */
+ public function getUserId()
+ {
+ return $this->userId;
+ }
+
+ /**
+ * @return string
+ */
+ public function getLogin()
+ {
+ return $this->login;
+ }
+}
\ No newline at end of file
diff --git a/src/Skobkin/Bundle/PointToolsBundle/Service/UserApi.php b/src/Skobkin/Bundle/PointToolsBundle/Service/UserApi.php
index 8a05463..0da52af 100644
--- a/src/Skobkin/Bundle/PointToolsBundle/Service/UserApi.php
+++ b/src/Skobkin/Bundle/PointToolsBundle/Service/UserApi.php
@@ -5,8 +5,13 @@ namespace Skobkin\Bundle\PointToolsBundle\Service;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
+use Guzzle\Http\Exception\ClientErrorResponseException;
use Guzzle\Service\Client;
use Skobkin\Bundle\PointToolsBundle\Entity\User;
+use Skobkin\Bundle\PointToolsBundle\Service\Exceptions\ApiException;
+use Skobkin\Bundle\PointToolsBundle\Service\Exceptions\InvalidResponseException;
+use Skobkin\Bundle\PointToolsBundle\Service\Exceptions\UserNotFoundException;
+use Symfony\Component\HttpFoundation\Response;
/**
* Basic Point.im user API functions from /api/user/*
@@ -20,19 +25,25 @@ class UserApi extends AbstractApi
/**
* @var string Base URL for user avatars
*/
- protected $avatarsBaseUrl = 'point.im/avatar/';
+ protected $avatarsBaseUrl = '//point.im/avatar/';
/**
* @var EntityManager
*/
protected $em;
+ /**
+ * @var EntityRepository
+ */
+ protected $userRepository;
+
public function __construct(Client $httpClient, $https = true, $baseUrl = null, EntityManagerInterface $entityManager)
{
parent::__construct($httpClient, $https, $baseUrl);
$this->em = $entityManager;
+ $this->userRepository = $this->em->getRepository('SkobkinPointToolsBundle:User');
}
public function getName()
@@ -45,21 +56,33 @@ class UserApi extends AbstractApi
*
* @param string $login
* @return User[]
+ * @throws ApiException
+ * @throws InvalidResponseException
+ * @throws UserNotFoundException
*/
public function getUserSubscribersByLogin($login)
{
- $usersList = $this->getGetRequestData('/api/user/' . $login . '/subscribers', [], true);
+ try {
+ $usersList = $this->getGetRequestData('/api/user/'.urlencode($login).'/subscribers', [], true);
+ } catch (ClientErrorResponseException $e) {
+ if (Response::HTTP_NOT_FOUND === $e->getResponse()->getStatusCode()) {
+ throw new UserNotFoundException('User not found', 0, $e, null, $login);
+ } else {
+ throw $e;
+ }
+ }
- $users = $this->getUsersFromList($usersList);
-
- return $users;
+ return $this->getUsersFromList($usersList);
}
/**
* Get user subscribers by user id
*
- * @param int $id
+ * @param $id
* @return User[]
+ * @throws ApiException
+ * @throws InvalidResponseException
+ * @throws UserNotFoundException
*/
public function getUserSubscribersById($id)
{
@@ -67,57 +90,222 @@ class UserApi extends AbstractApi
throw new \InvalidArgumentException('$id must be an integer');
}
- $usersList = $this->getGetRequestData('/api/user/id/' . (int) $id . '/subscribers', [], true);
+ try {
+ $usersList = $this->getGetRequestData('/api/user/id/'.(int) $id.'/subscribers', [], true);
+ } catch (ClientErrorResponseException $e) {
+ if (Response::HTTP_NOT_FOUND === $e->getResponse()->getStatusCode()) {
+ throw new UserNotFoundException('User not found', 0, $e, $id);
+ } else {
+ throw $e;
+ }
+ }
- $users = $this->getUsersFromList($usersList);
-
- return $users;
+ return $this->getUsersFromList($usersList);
}
/**
+ * Get user subscriptions by user login
+ *
+ * @param string $login
* @return User[]
+ * @throws ApiException
+ * @throws InvalidResponseException
+ * @throws UserNotFoundException
+ */
+ public function getUserSubscriptionsByLogin($login)
+ {
+ try {
+ $usersList = $this->getGetRequestData('/api/user/'.urlencode($login).'/subscriptions', [], true);
+ } catch (ClientErrorResponseException $e) {
+ if (Response::HTTP_NOT_FOUND === $e->getResponse()->getStatusCode()) {
+ throw new UserNotFoundException('User not found', 0, $e, null, $login);
+ } else {
+ throw $e;
+ }
+ }
+
+ return $this->getUsersFromList($usersList);
+ }
+
+ /**
+ * Get user subscriptions by user id
+ *
+ * @param $id
+ * @return User[]
+ * @throws ApiException
+ * @throws InvalidResponseException
+ * @throws UserNotFoundException
+ */
+ public function getUserSubscriptionsById($id)
+ {
+ if (!is_numeric($id)) {
+ throw new \InvalidArgumentException('$id must be an integer');
+ }
+
+ try {
+ $usersList = $this->getGetRequestData('/api/user/id/'.(int) $id.'/subscriptions', [], true);
+ } catch (ClientErrorResponseException $e) {
+ if (Response::HTTP_NOT_FOUND === $e->getResponse()->getStatusCode()) {
+ throw new UserNotFoundException('User not found', 0, $e, $id);
+ } else {
+ throw $e;
+ }
+ }
+
+ return $this->getUsersFromList($usersList);
+ }
+
+ /**
+ * Get single user by login
+ *
+ * @param string $login
+ * @return User
+ * @throws UserNotFoundException
+ * @throws ClientErrorResponseException
+ */
+ public function getUserByLogin($login)
+ {
+ try {
+ $userInfo = $this->getGetRequestData('/api/user/login/'.urlencode($login), [], true);
+ } catch (ClientErrorResponseException $e) {
+ if (Response::HTTP_NOT_FOUND === $e->getResponse()->getStatusCode()) {
+ throw new UserNotFoundException('User not found', 0, $e, null, $login);
+ } else {
+ throw $e;
+ }
+ }
+
+ return $this->getUserFromUserInfo($userInfo);
+ }
+
+ /**
+ * Get single user by id
+ *
+ * @param $id
+ * @return User
+ * @throws UserNotFoundException
+ * @throws ClientErrorResponseException
+ */
+ public function getUserById($id)
+ {
+ if (!is_numeric($id)) {
+ throw new \InvalidArgumentException('$id must be an integer');
+ }
+
+ try {
+ $userInfo = $this->getGetRequestData('/api/user/id/'.(int) $id, [], true);
+ } catch (ClientErrorResponseException $e) {
+ if (Response::HTTP_NOT_FOUND === $e->getResponse()->getStatusCode()) {
+ throw new UserNotFoundException('User not found', 0, $e, $id);
+ } else {
+ throw $e;
+ }
+ }
+
+ return $this->getUserFromUserInfo($userInfo);
+ }
+
+ /**
+ * Finds and updates or create new user from API response data
+ *
+ * @param array $userInfo
+ * @return User
+ * @throws ApiException
+ * @throws InvalidResponseException
+ */
+ public function getUserFromUserInfo(array $userInfo)
+ {
+ if (!is_array($userInfo)) {
+ throw new \InvalidArgumentException('$userInfo must be an array');
+ }
+
+ // @todo Return ID existance check when @ap-Codkelden will fix this API behaviour
+ if (array_key_exists('id', $userInfo) && array_key_exists('login', $userInfo) && array_key_exists('name', $userInfo) && is_numeric($userInfo['id'])) {
+ /** @var User $user */
+ if (null === ($user = $this->userRepository->find($userInfo['id']))) {
+ // Creating new user
+ $user = new User($userInfo['id']);
+ $this->em->persist($user);
+ }
+
+ // Updating data
+ $user
+ ->setLogin($userInfo['login'])
+ ->setName($userInfo['name'])
+ ;
+
+ try {
+ $this->em->flush($user);
+ } catch (\Exception $e) {
+ throw new ApiException(sprintf('Error while flushing changes for [%d] %s: %s', $user->getId(), $user->getLogin(), $e->getMessage()), 0, $e);
+ }
+
+ return $user;
+ }
+
+ throw new InvalidResponseException('Invalid API response. Mandatory fields do not exist.');
+ }
+
+ /**
+ * Get array of User objects from API response containing user list
+ *
+ * @param array $users
+ * @return User[]
+ * @throws ApiException
+ * @throws InvalidResponseException
*/
private function getUsersFromList(array $users = [])
{
- /** @var EntityRepository $userRepo */
- $userRepo = $this->em->getRepository('SkobkinPointToolsBundle:User');
+ if (!is_array($users)) {
+ throw new \InvalidArgumentException('$users must be an array');
+ }
+ /** @var User[] $resultUsers */
$resultUsers = [];
- foreach ($users as $userData) {
- if (array_key_exists('id', $userData) && array_key_exists('login', $userData) && array_key_exists('name', $userData) && is_numeric($userData['id'])) {
+ foreach ($users as $userInfo) {
+ if (array_key_exists('id', $userInfo) && array_key_exists('login', $userInfo) && array_key_exists('name', $userInfo) && is_numeric($userInfo['id'])) {
// @todo Optimize with prehashed id's list
- $user = $userRepo->find($userData['id']);
-
- if (!$user) {
- $user = new User();
- $user->setId((int) $userData['id']);
+ if (null === ($user = $this->userRepository->find($userInfo['id']))) {
+ $user = new User((int) $userInfo['id']);
$this->em->persist($user);
}
// Updating data
- if ($user->getLogin() !== $userData['login']) {
- $user->setLogin($userData['login']);
- }
- if ($user->getName() !== $userData['name']) {
- $user->setName($userData['name']);
+ $user
+ ->setLogin($userInfo['login'])
+ ->setName($userInfo['name'])
+ ;
+
+ try {
+ $this->em->flush($user);
+ } catch (\Exception $e) {
+ throw new ApiException(sprintf('Error while flushing changes for [%d] %s: %s', $user->getId(), $user->getLogin(), $e->getMessage()), 0, $e);
}
$resultUsers[] = $user;
+ } else {
+ throw new InvalidResponseException('Invalid API response. Mandatory fields do not exist.');
}
}
- $this->em->flush();
-
return $resultUsers;
}
/**
- * @param $login
+ * Creates avatar with specified size URL for user
+ *
+ * @param User $user
+ * @param int $size
+ * @return string
*/
public function getAvatarUrl(User $user, $size)
{
- return ($this->useHttps ? 'https://' : 'http://') . $this->avatarsBaseUrl . $user->getLogin() . '/' . $size;
+ if (!in_array($size, [self::AVATAR_SIZE_SMALL, self::AVATAR_SIZE_MEDIUM, self::AVATAR_SIZE_LARGE], true)) {
+ throw new \InvalidArgumentException('Avatar size must be one of restricted variants. See UserApi class AVATAR_SIZE_* constants.');
+ }
+
+ return $this->avatarsBaseUrl.urlencode($user->getLogin()).'/'.$size;
}
}