diff --git a/.gitignore b/.gitignore index dbbf27f2..0d1093dc 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,4 @@ /public/assets/vendor /public/artifacts -!/public/artifacts/.gitkeep +!/public/artifacts/.gitkeep \ No newline at end of file diff --git a/composer.json b/composer.json index 24016281..5909cce5 100644 --- a/composer.json +++ b/composer.json @@ -92,6 +92,7 @@ "npm-asset/sprintf-js": "~1.0.0", "npm-asset/codemirror": "~5.23.0", + "npm-asset/notifyjs": "~3.0.0", "bower-asset/admin-lte": "~2.3.0", "bower-asset/font-awesome": "~4.7.0", "bower-asset/ionicons": "~2.0.0", diff --git a/composer.lock b/composer.lock index 11d3e97c..5475238b 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,8 @@ "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": "8c2289ac491f1f1019f88be4f375b8a9", + "hash": "c36664adbc6a11c510a1a2ca21370a2a", + "content-hash": "dff41335ce906ddb872e66114ace5e50", "packages": [ { "name": "behat/gherkin", @@ -63,7 +64,7 @@ "gherkin", "parser" ], - "time": "2016-10-30T11:50:56+00:00" + "time": "2016-10-30 11:50:56" }, { "name": "block8/php-docblock-checker", @@ -116,7 +117,7 @@ "phpci", "testing" ], - "time": "2017-02-15T17:31:26+00:00" + "time": "2017-02-15 17:31:26" }, { "name": "bower-asset/admin-lte", @@ -310,7 +311,7 @@ "functional testing", "unit testing" ], - "time": "2018-02-26T23:29:41+00:00" + "time": "2018-02-26 23:29:41" }, { "name": "codeception/stub", @@ -343,20 +344,20 @@ "MIT" ], "description": "Flexible Stub wrapper for PHPUnit's Mock Builder", - "time": "2018-02-18T13:56:56+00:00" + "time": "2018-02-18 13:56:56" }, { "name": "composer/ca-bundle", - "version": "1.1.0", + "version": "1.1.1", "source": { "type": "git", "url": "https://github.com/composer/ca-bundle.git", - "reference": "943b2c4fcad1ef178d16a713c2468bf7e579c288" + "reference": "d2c0a83b7533d6912e8d516756ebd34f893e9169" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/ca-bundle/zipball/943b2c4fcad1ef178d16a713c2468bf7e579c288", - "reference": "943b2c4fcad1ef178d16a713c2468bf7e579c288", + "url": "https://api.github.com/repos/composer/ca-bundle/zipball/d2c0a83b7533d6912e8d516756ebd34f893e9169", + "reference": "d2c0a83b7533d6912e8d516756ebd34f893e9169", "shasum": "" }, "require": { @@ -365,7 +366,7 @@ "php": "^5.3.2 || ^7.0" }, "require-dev": { - "phpunit/phpunit": "^4.8.35", + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5", "psr/log": "^1.0", "symfony/process": "^2.5 || ^3.0 || ^4.0" }, @@ -399,7 +400,7 @@ "ssl", "tls" ], - "time": "2017-11-29T09:37:33+00:00" + "time": "2018-03-29 19:57:20" }, { "name": "composer/installers", @@ -519,7 +520,7 @@ "zend", "zikula" ], - "time": "2017-12-29T09:13:20+00:00" + "time": "2017-12-29 09:13:20" }, { "name": "doctrine/instantiator", @@ -573,7 +574,7 @@ "constructor", "instantiate" ], - "time": "2015-06-14T21:17:01+00:00" + "time": "2015-06-14 21:17:01" }, { "name": "facebook/webdriver", @@ -628,7 +629,7 @@ "selenium", "webdriver" ], - "time": "2017-11-15T11:08:09+00:00" + "time": "2017-11-15 11:08:09" }, { "name": "guzzlehttp/guzzle", @@ -690,7 +691,7 @@ "rest", "web service" ], - "time": "2017-02-28T22:50:30+00:00" + "time": "2017-02-28 22:50:30" }, { "name": "guzzlehttp/promises", @@ -741,7 +742,7 @@ "keywords": [ "promise" ], - "time": "2016-12-20T10:07:11+00:00" + "time": "2016-12-20 10:07:11" }, { "name": "guzzlehttp/psr7", @@ -806,7 +807,7 @@ "uri", "url" ], - "time": "2017-03-20T17:10:46+00:00" + "time": "2017-03-20 17:10:46" }, { "name": "jakub-onderka/php-parallel-lint", @@ -853,7 +854,7 @@ ], "description": "This tool check syntax of PHP files about 20x faster than serial check.", "homepage": "https://github.com/JakubOnderka/PHP-Parallel-Lint", - "time": "2015-12-15T10:42:16+00:00" + "time": "2015-12-15 10:42:16" }, { "name": "jasongrimes/paginator", @@ -898,7 +899,7 @@ "pagination", "paginator" ], - "time": "2015-10-15T09:42:44+00:00" + "time": "2015-10-15 09:42:44" }, { "name": "monolog/monolog", @@ -976,7 +977,7 @@ "logging", "psr-3" ], - "time": "2017-03-13T07:08:03+00:00" + "time": "2017-03-13 07:08:03" }, { "name": "myclabs/deep-copy", @@ -1021,7 +1022,7 @@ "object", "object graph" ], - "time": "2017-10-19T19:58:43+00:00" + "time": "2017-10-19 19:58:43" }, { "name": "nikic/php-parser", @@ -1072,7 +1073,7 @@ "parser", "php" ], - "time": "2018-02-28T20:30:58+00:00" + "time": "2018-02-28 20:30:58" }, { "name": "npm-asset/codemirror", @@ -1088,6 +1089,20 @@ "MIT" ] }, + { + "name": "npm-asset/notifyjs", + "version": "3.0.0", + "dist": { + "type": "tar", + "url": "https://registry.npmjs.org/notifyjs/-/notifyjs-3.0.0.tgz", + "reference": null, + "shasum": null + }, + "type": "npm-asset", + "license": [ + "MIT" + ] + }, { "name": "npm-asset/sprintf-js", "version": "1.0.3", @@ -1142,20 +1157,20 @@ ], "description": "Extend the composer/installers plugin to accept any arbitrary package type.", "homepage": "http://www.oomphinc.com/", - "time": "2017-03-31T16:57:39+00:00" + "time": "2017-03-31 16:57:39" }, { "name": "paragonie/random_compat", - "version": "v2.0.11", + "version": "v2.0.12", "source": { "type": "git", "url": "https://github.com/paragonie/random_compat.git", - "reference": "5da4d3c796c275c55f057af5a643ae297d96b4d8" + "reference": "258c89a6b97de7dfaf5b8c7607d0478e236b04fb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/random_compat/zipball/5da4d3c796c275c55f057af5a643ae297d96b4d8", - "reference": "5da4d3c796c275c55f057af5a643ae297d96b4d8", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/258c89a6b97de7dfaf5b8c7607d0478e236b04fb", + "reference": "258c89a6b97de7dfaf5b8c7607d0478e236b04fb", "shasum": "" }, "require": { @@ -1190,7 +1205,7 @@ "pseudorandom", "random" ], - "time": "2017-09-27T21:40:39+00:00" + "time": "2018-04-04 21:24:14" }, { "name": "pda/pheanstalk", @@ -1240,7 +1255,7 @@ "keywords": [ "beanstalkd" ], - "time": "2015-08-07T21:42:41+00:00" + "time": "2015-08-07 21:42:41" }, { "name": "pdepend/pdepend", @@ -1280,7 +1295,7 @@ "BSD-3-Clause" ], "description": "Official version of pdepend to be handled with Composer", - "time": "2017-12-13T13:21:38+00:00" + "time": "2017-12-13 13:21:38" }, { "name": "phpdocumentor/reflection-docblock", @@ -1329,7 +1344,7 @@ "email": "mike.vanriel@naenius.com" } ], - "time": "2016-01-25T08:17:30+00:00" + "time": "2016-01-25 08:17:30" }, { "name": "phploc/phploc", @@ -1378,7 +1393,7 @@ ], "description": "A tool for quickly measuring the size of a PHP project.", "homepage": "https://github.com/sebastianbergmann/phploc", - "time": "2017-11-18T17:35:43+00:00" + "time": "2017-11-18 17:35:43" }, { "name": "phpmd/phpmd", @@ -1444,7 +1459,7 @@ "phpmd", "pmd" ], - "time": "2017-01-20T14:41:10+00:00" + "time": "2017-01-20 14:41:10" }, { "name": "phpspec/prophecy", @@ -1507,7 +1522,7 @@ "spy", "stub" ], - "time": "2018-02-19T10:16:54+00:00" + "time": "2018-02-19 10:16:54" }, { "name": "phpunit/php-code-coverage", @@ -1570,7 +1585,7 @@ "testing", "xunit" ], - "time": "2017-04-02T07:44:40+00:00" + "time": "2017-04-02 07:44:40" }, { "name": "phpunit/php-file-iterator", @@ -1617,7 +1632,7 @@ "filesystem", "iterator" ], - "time": "2017-11-27T13:52:08+00:00" + "time": "2017-11-27 13:52:08" }, { "name": "phpunit/php-text-template", @@ -1658,7 +1673,7 @@ "keywords": [ "template" ], - "time": "2015-06-21T13:50:34+00:00" + "time": "2015-06-21 13:50:34" }, { "name": "phpunit/php-timer", @@ -1707,7 +1722,7 @@ "keywords": [ "timer" ], - "time": "2017-02-26T11:10:40+00:00" + "time": "2017-02-26 11:10:40" }, { "name": "phpunit/php-token-stream", @@ -1756,7 +1771,7 @@ "keywords": [ "tokenizer" ], - "time": "2017-12-04T08:55:13+00:00" + "time": "2017-12-04 08:55:13" }, { "name": "phpunit/phpunit", @@ -1838,7 +1853,7 @@ "testing", "xunit" ], - "time": "2018-02-01T05:50:59+00:00" + "time": "2018-02-01 05:50:59" }, { "name": "phpunit/phpunit-mock-objects", @@ -1897,7 +1912,7 @@ "mock", "xunit" ], - "time": "2017-06-30T09:13:00+00:00" + "time": "2017-06-30 09:13:00" }, { "name": "pimple/pimple", @@ -1943,7 +1958,7 @@ "container", "dependency injection" ], - "time": "2015-09-11T15:10:35+00:00" + "time": "2015-09-11 15:10:35" }, { "name": "psr/cache", @@ -1989,7 +2004,7 @@ "psr", "psr-6" ], - "time": "2016-08-06T20:24:11+00:00" + "time": "2016-08-06 20:24:11" }, { "name": "psr/container", @@ -2038,7 +2053,7 @@ "container-interop", "psr" ], - "time": "2017-02-14T16:28:37+00:00" + "time": "2017-02-14 16:28:37" }, { "name": "psr/http-message", @@ -2088,7 +2103,7 @@ "request", "response" ], - "time": "2016-08-06T14:39:51+00:00" + "time": "2016-08-06 14:39:51" }, { "name": "psr/log", @@ -2135,7 +2150,7 @@ "psr", "psr-3" ], - "time": "2016-10-10T12:19:37+00:00" + "time": "2016-10-10 12:19:37" }, { "name": "psr/simple-cache", @@ -2183,7 +2198,7 @@ "psr-16", "simple-cache" ], - "time": "2017-10-23T01:57:42+00:00" + "time": "2017-10-23 01:57:42" }, { "name": "robmorgan/phinx", @@ -2249,7 +2264,7 @@ "migrations", "phinx" ], - "time": "2017-06-05T13:30:19+00:00" + "time": "2017-06-05 13:30:19" }, { "name": "sebastian/code-unit-reverse-lookup", @@ -2294,7 +2309,7 @@ ], "description": "Looks up which function or method a line of code belongs to", "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "time": "2017-03-04T06:30:41+00:00" + "time": "2017-03-04 06:30:41" }, { "name": "sebastian/comparator", @@ -2358,7 +2373,7 @@ "compare", "equality" ], - "time": "2017-01-29T09:50:25+00:00" + "time": "2017-01-29 09:50:25" }, { "name": "sebastian/diff", @@ -2410,7 +2425,7 @@ "keywords": [ "diff" ], - "time": "2017-05-22T07:24:03+00:00" + "time": "2017-05-22 07:24:03" }, { "name": "sebastian/environment", @@ -2460,7 +2475,7 @@ "environment", "hhvm" ], - "time": "2016-11-26T07:53:53+00:00" + "time": "2016-11-26 07:53:53" }, { "name": "sebastian/exporter", @@ -2527,7 +2542,7 @@ "export", "exporter" ], - "time": "2016-11-19T08:54:04+00:00" + "time": "2016-11-19 08:54:04" }, { "name": "sebastian/finder-facade", @@ -2566,7 +2581,7 @@ ], "description": "FinderFacade is a convenience wrapper for Symfony's Finder component.", "homepage": "https://github.com/sebastianbergmann/finder-facade", - "time": "2017-11-18T17:31:49+00:00" + "time": "2017-11-18 17:31:49" }, { "name": "sebastian/global-state", @@ -2617,7 +2632,7 @@ "keywords": [ "global state" ], - "time": "2015-10-12T03:26:01+00:00" + "time": "2015-10-12 03:26:01" }, { "name": "sebastian/object-enumerator", @@ -2663,7 +2678,7 @@ ], "description": "Traverses array structures and object graphs to enumerate all referenced objects", "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "time": "2017-02-18T15:18:39+00:00" + "time": "2017-02-18 15:18:39" }, { "name": "sebastian/phpcpd", @@ -2714,7 +2729,7 @@ ], "description": "Copy/Paste Detector (CPD) for PHP code.", "homepage": "https://github.com/sebastianbergmann/phpcpd", - "time": "2016-04-17T19:32:49+00:00" + "time": "2016-04-17 19:32:49" }, { "name": "sebastian/recursion-context", @@ -2767,7 +2782,7 @@ ], "description": "Provides functionality to recursively process PHP variables", "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2016-11-19T07:33:16+00:00" + "time": "2016-11-19 07:33:16" }, { "name": "sebastian/resource-operations", @@ -2809,7 +2824,7 @@ ], "description": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "time": "2015-07-28T20:34:47+00:00" + "time": "2015-07-28 20:34:47" }, { "name": "sebastian/version", @@ -2852,7 +2867,7 @@ ], "description": "Library that helps with managing the version number of Git-hosted PHP projects", "homepage": "https://github.com/sebastianbergmann/version", - "time": "2016-10-03T07:35:21+00:00" + "time": "2016-10-03 07:35:21" }, { "name": "sensiolabs/ansi-to-html", @@ -2896,7 +2911,7 @@ } ], "description": "A library to convert a text with ANSI codes to HTML", - "time": "2017-05-02T00:53:29+00:00" + "time": "2017-05-02 00:53:29" }, { "name": "sensiolabs/security-checker", @@ -2941,7 +2956,7 @@ } ], "description": "A security checker for your composer.lock", - "time": "2017-07-24T11:42:56+00:00" + "time": "2017-07-24 11:42:56" }, { "name": "squizlabs/php_codesniffer", @@ -3019,7 +3034,7 @@ "phpcs", "standards" ], - "time": "2017-03-01T22:17:45+00:00" + "time": "2017-03-01 22:17:45" }, { "name": "swiftmailer/swiftmailer", @@ -3073,20 +3088,20 @@ "mail", "mailer" ], - "time": "2018-01-23T07:37:21+00:00" + "time": "2018-01-23 07:37:21" }, { "name": "symfony/browser-kit", - "version": "v3.4.6", + "version": "v3.4.7", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", - "reference": "490f27762705c8489bd042fe3e9377a191dba9b4" + "reference": "840bb6f0d5b3701fd768b68adf7193c2d0f98f79" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/490f27762705c8489bd042fe3e9377a191dba9b4", - "reference": "490f27762705c8489bd042fe3e9377a191dba9b4", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/840bb6f0d5b3701fd768b68adf7193c2d0f98f79", + "reference": "840bb6f0d5b3701fd768b68adf7193c2d0f98f79", "shasum": "" }, "require": { @@ -3130,20 +3145,20 @@ ], "description": "Symfony BrowserKit Component", "homepage": "https://symfony.com", - "time": "2018-01-03T07:37:34+00:00" + "time": "2018-03-19 22:32:39" }, { "name": "symfony/cache", - "version": "v3.4.6", + "version": "v3.4.7", "source": { "type": "git", "url": "https://github.com/symfony/cache.git", - "reference": "cce49c7aa2fc82077355c8a6dfcd9e619abe6e98" + "reference": "13255ddd056e49f3154747943f8ee175d555d394" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache/zipball/cce49c7aa2fc82077355c8a6dfcd9e619abe6e98", - "reference": "cce49c7aa2fc82077355c8a6dfcd9e619abe6e98", + "url": "https://api.github.com/repos/symfony/cache/zipball/13255ddd056e49f3154747943f8ee175d555d394", + "reference": "13255ddd056e49f3154747943f8ee175d555d394", "shasum": "" }, "require": { @@ -3200,20 +3215,20 @@ "caching", "psr6" ], - "time": "2018-02-11T14:42:07+00:00" + "time": "2018-04-02 14:35:16" }, { "name": "symfony/config", - "version": "v3.4.6", + "version": "v3.4.7", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "05e10567b529476a006b00746c5f538f1636810e" + "reference": "7c2a9d44f4433863e9bca682e7f03609234657f9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/05e10567b529476a006b00746c5f538f1636810e", - "reference": "05e10567b529476a006b00746c5f538f1636810e", + "url": "https://api.github.com/repos/symfony/config/zipball/7c2a9d44f4433863e9bca682e7f03609234657f9", + "reference": "7c2a9d44f4433863e9bca682e7f03609234657f9", "shasum": "" }, "require": { @@ -3263,20 +3278,20 @@ ], "description": "Symfony Config Component", "homepage": "https://symfony.com", - "time": "2018-02-14T10:03:57+00:00" + "time": "2018-03-19 22:32:39" }, { "name": "symfony/console", - "version": "v3.4.6", + "version": "v3.4.7", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "067339e9b8ec30d5f19f5950208893ff026b94f7" + "reference": "d4bb70fa24d540c309d88a9d6e43fb2d339b1fbf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/067339e9b8ec30d5f19f5950208893ff026b94f7", - "reference": "067339e9b8ec30d5f19f5950208893ff026b94f7", + "url": "https://api.github.com/repos/symfony/console/zipball/d4bb70fa24d540c309d88a9d6e43fb2d339b1fbf", + "reference": "d4bb70fa24d540c309d88a9d6e43fb2d339b1fbf", "shasum": "" }, "require": { @@ -3332,20 +3347,20 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2018-02-26T15:46:28+00:00" + "time": "2018-04-03 05:22:50" }, { "name": "symfony/css-selector", - "version": "v3.4.6", + "version": "v3.4.7", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "544655f1fc078a9cd839fdda2b7b1e64627c826a" + "reference": "519a80d7c1d95c6cc0b67f686d15fe27c6910de0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/544655f1fc078a9cd839fdda2b7b1e64627c826a", - "reference": "544655f1fc078a9cd839fdda2b7b1e64627c826a", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/519a80d7c1d95c6cc0b67f686d15fe27c6910de0", + "reference": "519a80d7c1d95c6cc0b67f686d15fe27c6910de0", "shasum": "" }, "require": { @@ -3385,20 +3400,20 @@ ], "description": "Symfony CssSelector Component", "homepage": "https://symfony.com", - "time": "2018-02-03T14:55:07+00:00" + "time": "2018-03-19 22:32:39" }, { "name": "symfony/debug", - "version": "v3.4.6", + "version": "v3.4.7", "source": { "type": "git", "url": "https://github.com/symfony/debug.git", - "reference": "9b1071f86e79e1999b3d3675d2e0e7684268b9bc" + "reference": "9cf7c2271cfb89ef9727db1b740ca77be57bf9d7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/9b1071f86e79e1999b3d3675d2e0e7684268b9bc", - "reference": "9b1071f86e79e1999b3d3675d2e0e7684268b9bc", + "url": "https://api.github.com/repos/symfony/debug/zipball/9cf7c2271cfb89ef9727db1b740ca77be57bf9d7", + "reference": "9cf7c2271cfb89ef9727db1b740ca77be57bf9d7", "shasum": "" }, "require": { @@ -3441,20 +3456,20 @@ ], "description": "Symfony Debug Component", "homepage": "https://symfony.com", - "time": "2018-02-28T21:49:22+00:00" + "time": "2018-04-03 05:22:50" }, { "name": "symfony/dependency-injection", - "version": "v3.4.6", + "version": "v3.4.7", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "12e901abc1cb0d637a0e5abe9923471361d96b07" + "reference": "24a68710c6ddc1e3d159a110cef94cedfcf3c611" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/12e901abc1cb0d637a0e5abe9923471361d96b07", - "reference": "12e901abc1cb0d637a0e5abe9923471361d96b07", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/24a68710c6ddc1e3d159a110cef94cedfcf3c611", + "reference": "24a68710c6ddc1e3d159a110cef94cedfcf3c611", "shasum": "" }, "require": { @@ -3512,20 +3527,20 @@ ], "description": "Symfony DependencyInjection Component", "homepage": "https://symfony.com", - "time": "2018-03-04T03:54:53+00:00" + "time": "2018-03-29 11:25:31" }, { "name": "symfony/dom-crawler", - "version": "v3.4.6", + "version": "v3.4.7", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "2bb5d3101cc01f4fe580e536daf4f1959bc2d24d" + "reference": "1a4cffeb059226ff6bee9f48acb388faf674afff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/2bb5d3101cc01f4fe580e536daf4f1959bc2d24d", - "reference": "2bb5d3101cc01f4fe580e536daf4f1959bc2d24d", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/1a4cffeb059226ff6bee9f48acb388faf674afff", + "reference": "1a4cffeb059226ff6bee9f48acb388faf674afff", "shasum": "" }, "require": { @@ -3568,11 +3583,11 @@ ], "description": "Symfony DomCrawler Component", "homepage": "https://symfony.com", - "time": "2018-02-22T10:48:49+00:00" + "time": "2018-03-19 22:32:39" }, { "name": "symfony/event-dispatcher", - "version": "v3.4.6", + "version": "v3.4.7", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", @@ -3631,11 +3646,11 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2018-02-14T10:03:57+00:00" + "time": "2018-02-14 10:03:57" }, { "name": "symfony/filesystem", - "version": "v3.4.6", + "version": "v3.4.7", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", @@ -3680,20 +3695,20 @@ ], "description": "Symfony Filesystem Component", "homepage": "https://symfony.com", - "time": "2018-02-22T10:48:49+00:00" + "time": "2018-02-22 10:48:49" }, { "name": "symfony/finder", - "version": "v3.4.6", + "version": "v3.4.7", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "a479817ce0a9e4adfd7d39c6407c95d97c254625" + "reference": "7a2e1299cc0c4162996f18e347b6356729a55317" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/a479817ce0a9e4adfd7d39c6407c95d97c254625", - "reference": "a479817ce0a9e4adfd7d39c6407c95d97c254625", + "url": "https://api.github.com/repos/symfony/finder/zipball/7a2e1299cc0c4162996f18e347b6356729a55317", + "reference": "7a2e1299cc0c4162996f18e347b6356729a55317", "shasum": "" }, "require": { @@ -3729,7 +3744,7 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2018-03-05T18:28:11+00:00" + "time": "2018-03-28 18:23:39" }, { "name": "symfony/polyfill-apcu", @@ -3785,7 +3800,7 @@ "portable", "shim" ], - "time": "2018-01-30T19:27:44+00:00" + "time": "2018-01-30 19:27:44" }, { "name": "symfony/polyfill-mbstring", @@ -3844,20 +3859,20 @@ "portable", "shim" ], - "time": "2018-01-30T19:27:44+00:00" + "time": "2018-01-30 19:27:44" }, { "name": "symfony/process", - "version": "v3.4.6", + "version": "v3.4.7", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "cc4aea21f619116aaf1c58016a944e4821c8e8af" + "reference": "4b7d64e852886319e93ddfdecff0d744ab87658b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/cc4aea21f619116aaf1c58016a944e4821c8e8af", - "reference": "cc4aea21f619116aaf1c58016a944e4821c8e8af", + "url": "https://api.github.com/repos/symfony/process/zipball/4b7d64e852886319e93ddfdecff0d744ab87658b", + "reference": "4b7d64e852886319e93ddfdecff0d744ab87658b", "shasum": "" }, "require": { @@ -3893,20 +3908,20 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2018-02-12T17:55:00+00:00" + "time": "2018-04-03 05:22:50" }, { "name": "symfony/yaml", - "version": "v3.4.6", + "version": "v3.4.7", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "6af42631dcf89e9c616242c900d6c52bd53bd1bb" + "reference": "a42f9da85c7c38d59f5e53f076fe81a091f894d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/6af42631dcf89e9c616242c900d6c52bd53bd1bb", - "reference": "6af42631dcf89e9c616242c900d6c52bd53bd1bb", + "url": "https://api.github.com/repos/symfony/yaml/zipball/a42f9da85c7c38d59f5e53f076fe81a091f894d0", + "reference": "a42f9da85c7c38d59f5e53f076fe81a091f894d0", "shasum": "" }, "require": { @@ -3951,7 +3966,7 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2018-02-16T09:50:28+00:00" + "time": "2018-04-03 05:14:20" }, { "name": "theseer/fdomdocument", @@ -3991,7 +4006,7 @@ ], "description": "The classes contained within this repository extend the standard DOM to use exceptions at all occasions of errors instead of PHP warnings or notices. They also add various custom methods and shortcuts for convenience and to simplify the usage of DOM.", "homepage": "https://github.com/theseer/fDOMDocument", - "time": "2017-06-30T11:53:12+00:00" + "time": "2017-06-30 11:53:12" } ], "packages-dev": [ @@ -4048,20 +4063,20 @@ "testing", "xunit" ], - "time": "2016-12-02T14:39:14+00:00" + "time": "2016-12-02 14:39:14" }, { "name": "symfony/var-dumper", - "version": "v3.4.6", + "version": "v3.4.7", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "80964679d81da3d5618519e0e4be488c3d7ecd7d" + "reference": "6f502127b1bb85f7f30f8bc1fb60570a10431863" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/80964679d81da3d5618519e0e4be488c3d7ecd7d", - "reference": "80964679d81da3d5618519e0e4be488c3d7ecd7d", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/6f502127b1bb85f7f30f8bc1fb60570a10431863", + "reference": "6f502127b1bb85f7f30f8bc1fb60570a10431863", "shasum": "" }, "require": { @@ -4117,7 +4132,7 @@ "debug", "dump" ], - "time": "2018-02-22T17:29:24+00:00" + "time": "2018-04-03 05:22:50" } ], "aliases": [], diff --git a/public/assets/js/app.js b/public/assets/js/app.js index dcf7c630..03fe6799 100644 --- a/public/assets/js/app.js +++ b/public/assets/js/app.js @@ -1,6 +1,18 @@ var PHPCensor = { intervals: {}, widgets: {}, + webNotifiedBuilds: [], + /* + @var STATUS Refer to \PHPCensor\Model\Build.php constants. + TODO: Transfer this variable to Build JS class so + Build JS itself can use it as well. + */ + STATUS: [ + 'Pending', + 'Running', + 'Success', + 'Failed' + ], init: function () { $(document).ready(function () { @@ -19,6 +31,128 @@ var PHPCensor = { }); }, + /** + * Shallow comparison that determines that the build + * has been shown as at least once as a web notification. + * Also adds the build to a list of shown web notifications + * if it's not found in the list. + * @param object build + * @return boolean + */ + isWebNotifiedBuild: function (build) { + var o = PHPCensor.webNotifiedBuilds; + for (var i = 0; i < o.length; i++) { + var webNotifiedBuild = o[i]; + var b = + webNotifiedBuild.projectTitle === build.projectTitle && + webNotifiedBuild.branch === build.branch && + webNotifiedBuild.status === build.status && + webNotifiedBuild.datePerformed === build.datePerformed && + webNotifiedBuild.dateFinished === build.dateFinished; + if (b) { + return true; + } + } + /* + It's impossible to remember or use all previously shown + builds. So let's clear them out once they reach 1000. + @var 1000 Estimated. + */ + if (PHPCensor.webNotifiedBuilds.length > 1000) { + PHPCensor.webNotifiedBuilds = []; + } + PHPCensor.webNotifiedBuilds.push(build); + return false; + }, + + /** + * Web notification. + * Chrome doesn't allow insecure protocols. + * Enable HTTPS even on localhost in order for + * web notifications to work properly. + * @param object data Contains an array of builds. + * @return void + */ + showWebNotification: function (data) { + var pending = data.pending; + var running = data.running; + var success = data.success; + var failed = data.failed; + var notification = null; + + //Determine which notification to show. + //TODO: Refactor. Use foreach. + if (pending && pending.count > 0) { + notification = pending; + } + else if (running && running.count > 0) { + notification = running; + } + else if (success && success.count > 0) { + notification = success; + } + else if (failed && failed.count > 0) { + notification = failed; + } + + if (notification) { + var msg = ''; + if (!Notify.needsPermission) { + var items = notification.items; + for (var item in items) { + var build = items[item].build; + var projTitle = build.project_title; + var branch = build.branch; + var status = PHPCensor.STATUS[build.status]; + var datePerformed = build.date_performed; + var dateFinished = build.date_finished; + var rn = "\r\n"; + + var build = { + projectTitle: projTitle, + branch: branch, + status: status, + datePerformed: datePerformed, + dateFinished: dateFinished + }; + + //Ignore if the last displayed notification is + //similar to what we're again about to display. + if (!PHPCensor.isWebNotifiedBuild(build)) { + msg += + 'Project title: ' + projTitle + rn + + 'Git branch: ' + branch + rn + + 'Status: ' + status + rn; + + //Build details is empty during + //widget-all-projects-update. + if (datePerformed.length > 0) { + msg += datePerformed + rn; + } + + if (dateFinished.length > 0) { + msg += dateFinished; + } + + new Notify( + 'PHP Censor Web Notification', + {body: msg} + ).show(); + } + + } + + } + else if (Notify.isSupported()) { + Notify.requestPermission(null, function(){ + msg = 'Web notifications permission ' + + 'has been denied by the user.' + console.warn(msg); + }); + } + } + }, + getBuilds: function () { $.ajax({ url: APP_URL + 'build/ajax-queue', @@ -29,6 +163,14 @@ var PHPCensor = { error: PHPCensor.handleFailedAjax }); + + $.ajax({ + url: APP_URL + 'web-notifications/builds-updated', + success: function (data) { + PHPCensor.showWebNotification(data); + }, + error: PHPCensor.handleFailedAjax + }); }, getProjectBuilds: function () { diff --git a/public/assets/js/dashboard-widgets/all_projects.js b/public/assets/js/dashboard-widgets/all_projects.js index a13bcf58..b7e166f7 100644 --- a/public/assets/js/dashboard-widgets/all_projects.js +++ b/public/assets/js/dashboard-widgets/all_projects.js @@ -33,6 +33,18 @@ PHPCensor.widgets.allProjects = { error: PHPCensor.handleFailedAjax }); + + //Let's build another mechanism for web notification + //since the above feature is tightly coupled to the view. + $.ajax({ + url: APP_URL + + 'web-notifications/widgets-all-projects-update/' + + projectId, + success: function (data) { + PHPCensor.showWebNotification(data); + }, + error: PHPCensor.handleFailedAjax + }); }); } }; diff --git a/src/Controller/BuildController.php b/src/Controller/BuildController.php index 3db8143b..b790d6fd 100644 --- a/src/Controller/BuildController.php +++ b/src/Controller/BuildController.php @@ -365,9 +365,17 @@ class BuildController extends WebController public function ajaxQueue() { + $sPending = 'pending'; + $sRunning = 'running'; + + $pending = $this->buildStore->getByStatus(Build::STATUS_PENDING); + $running = $this->buildStore->getByStatus(Build::STATUS_RUNNING); + $rtn = [ - 'pending' => $this->formatBuilds($this->buildStore->getByStatus(Build::STATUS_PENDING)), - 'running' => $this->formatBuilds($this->buildStore->getByStatus(Build::STATUS_RUNNING)), + + $sPending => $this->formatBuilds($pending), + $sRunning => $this->formatBuilds($running), + ]; $response = new JsonResponse(); diff --git a/src/Controller/WebNotificationsController.php b/src/Controller/WebNotificationsController.php new file mode 100644 index 00000000..cc8c8270 --- /dev/null +++ b/src/Controller/WebNotificationsController.php @@ -0,0 +1,114 @@ +buildStore = Factory::getStore('Build'); + $this->projectStore = Factory::getStore('Project'); + $this->groupStore = Factory::getStore('ProjectGroup'); + } + + /** + * Provides JSON format for web notification UI of all last + * builds that have success and failed status. + * This is similar to WidgetAllProjectsController::update() + * but instead, this only returns JSON. + * @param int $projectId + * @return \PHPCensor\Http\Response\JsonResponse + * @see \PHPCensor\Controller\WidgetAllProjectsController + */ + public function widgetsAllProjectsUpdate($projectId) + { + $success = $this->buildStore->getLastBuildByStatus($projectId, Build::STATUS_SUCCESS); + $failed = $this->buildStore->getLastBuildByStatus($projectId, Build::STATUS_FAILED); + + $oSuccess = WebNotificationService::formatBuild($success); + $oFailed = WebNotificationService::formatBuild($failed); + + //@keys count and items Follow the for-loop structure + //found in + //\PHPCensor\Service\WebNotificationService::formatBuilds() + $aSuccess = [ + 'count' => count($oSuccess), + 'items' => [$projectId => ['build' => $oSuccess]] + ]; + $aFailed = [ + 'count' => count($oFailed), + 'items' => [$projectId => ['build' => $oFailed]] + ]; + + $builds = [ + 'success' => $aSuccess, + 'failed' => $aFailed + ]; + + $response = new JsonResponse(); + $response->setContent($builds); + + return $response; + } + + + /** + * Provides JSON format for web notification UI of all last + * builds that have pending and running status. + * This is similar to WidgetAllProjectsController::update() + * but instead, this only returns JSON. + * @return \PHPCensor\Http\Response\JsonResponse + */ + public function buildsUpdated() + { + $pending = $this->buildStore->getByStatus(Build::STATUS_PENDING); + $running = $this->buildStore->getByStatus(Build::STATUS_RUNNING); + + $rtn = [ + 'pending' => WebNotificationService::formatBuilds($pending), + 'running' => WebNotificationService::formatBuilds($running) + ]; + + $response = new JsonResponse(); + $response->setContent($rtn); + + return $response; + } +} diff --git a/src/Controller/WidgetAllProjectsController.php b/src/Controller/WidgetAllProjectsController.php index 48822749..2db28de2 100644 --- a/src/Controller/WidgetAllProjectsController.php +++ b/src/Controller/WidgetAllProjectsController.php @@ -11,6 +11,8 @@ use PHPCensor\Http\Response; use PHPCensor\Store\BuildStore; use PHPCensor\Store\ProjectStore; use PHPCensor\Store\ProjectGroupStore; +use PHPCensor\Service\BuildService; +use b8\Http\Response\JsonResponse; /** * Widget All Projects Controller diff --git a/src/Service/WebNotificationService.php b/src/Service/WebNotificationService.php new file mode 100644 index 00000000..b982ea69 --- /dev/null +++ b/src/Service/WebNotificationService.php @@ -0,0 +1,82 @@ + $builds['count'], 'items' => []]; + + foreach ($builds['items'] as $buildItem) { + $build = self::formatBuild($buildItem); + $rtn['items'][$buildItem->getId()]['build'] = $build; + } + + ksort($rtn['items']); + return $rtn; + } + + /** + * Provides structured keys for web notification. + * @param Build $build + * @return array + */ + public static function formatBuild($build) + { + if (empty($build) || is_null($build)) { + return []; + } + $status = $build->getStatus(); + $datePerformed = ''; + $dateFinished = ''; + + /* + BUG: Lang::out() automatically renders the values for + either 'created_x' or 'started_x' instead of just + returning them. + */ + if ($status === Build::STATUS_PENDING) { + $datePerformed = 'Created: ' . $build->getCreateDate()->format('H:i'); + } elseif ($status === Build::STATUS_RUNNING) { + $datePerformed = 'Started: ' . $build->getStartDate()->format('H:i'); + } + + if (!is_null($build->getFinishDate())) { + $dateFinished = 'Finished: ' . $build->getFinishDate()->format('H:i'); + } + + return [ + 'branch' => $build->getBranch(), + 'url' => APP_URL . + 'build/view/' . + $build->getId(), + 'committer_email' => $build->getCommitterEmail(), + 'img_src' => 'https://www.gravatar.com/avatar/' . + md5($build->getCommitterEmail()) . + '?d=mm&s=40', + 'project_title' => $build->getProject()->getTitle(), + 'status' => $status, + 'date_performed' => $datePerformed, + 'date_finished' => $dateFinished + ]; + } +} diff --git a/src/View/layout.phtml b/src/View/layout.phtml index e4892348..ca646926 100644 --- a/src/View/layout.phtml +++ b/src/View/layout.phtml @@ -32,6 +32,7 @@ $user = $this->getUser(); +