diff --git a/.travis.yml b/.travis.yml
index 9e888de..054d397 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,8 +2,6 @@ language: php
sudo: false
php:
- - 5.6
- - 5.5
- 7.1
- 7.2
@@ -18,5 +16,6 @@ cache:
script:
- composer install
+ - ./vendor/bin/phpcs --report=checkstyle
- phpunit --coverage-text
diff --git a/Readme.md b/Readme.md
index ec26e33..b595142 100644
--- a/Readme.md
+++ b/Readme.md
@@ -2,13 +2,13 @@
## Command line time-logger
* Entries stored in sqlite in your home directory
-* Sends complete entries to redmine
+* Sends complete entries to redmine or jira, or both at the same time
## Requirements
-* PHP 5.5.23+
-* php5-sqlite3 OR php7.0-sqlite3
-* php5-pdo_sqlite OR php7.0-pdo_sqlite
+* PHP 7.1+
+* php7.1-sqlite3
+* php7.1-pdo_sqlite
## Installation
@@ -57,7 +57,7 @@ tl self-update
## Usage
-Assuming you have two redmine tickets number 3546 and 4791.
+Assuming you have two tickets number 3546 and 4791.
```bash
# start work on 3546
tl start 3546
diff --git a/bin/tl.php b/bin/tl.php
index 5783430..3f6c365 100755
--- a/bin/tl.php
+++ b/bin/tl.php
@@ -18,6 +18,7 @@
$loader->load('services.yml');
$home = $_SERVER['HOME'];
$container->setParameter('directory', $home);
+$container->set('container', $container);
$application = new Application('Time logger', '@package_version@', $container);
$application->run();
diff --git a/composer.json b/composer.json
index ab61f31..6ad03ea 100644
--- a/composer.json
+++ b/composer.json
@@ -12,12 +12,14 @@
"herrera-io/phar-update": "^2.0",
"stecman/symfony-console-completion": "^0.6.0",
"monolog/monolog": "^1.17",
- "php": ">=5.5.23",
+ "php": ">=7.1",
"ext-sqlite3": "*",
- "ext-pdo_sqlite": "*"
+ "ext-pdo_sqlite": "*",
+ "lesstif/php-jira-rest-client": "^1.35"
},
"require-dev": {
- "phpunit/phpunit": "^4.8"
+ "phpunit/phpunit": "~6",
+ "drupal/coder": "~8"
},
"license": "GPL2",
"autoload": {
diff --git a/composer.lock b/composer.lock
index a6d932a..dc1d0da 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
- "content-hash": "ef372f1c94bfaf2cfd83abe221c64aec",
+ "content-hash": "2afe9257c203b8242097939279ba9982",
"packages": [
{
"name": "doctrine/annotations",
@@ -893,6 +893,66 @@
],
"time": "2016-01-25T15:43:01+00:00"
},
+ {
+ "name": "lesstif/php-jira-rest-client",
+ "version": "1.35.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/lesstif/php-jira-rest-client.git",
+ "reference": "45987fd5b66e80cfb64d94955f478136d03eb597"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/lesstif/php-jira-rest-client/zipball/45987fd5b66e80cfb64d94955f478136d03eb597",
+ "reference": "45987fd5b66e80cfb64d94955f478136d03eb597",
+ "shasum": ""
+ },
+ "require": {
+ "ext-curl": "*",
+ "ext-json": "*",
+ "monolog/monolog": "~1.12",
+ "netresearch/jsonmapper": "~0.11|^1.0",
+ "php": ">=5.5.9",
+ "vlucas/phpdotenv": "~1.0|~2.0"
+ },
+ "require-dev": {
+ "mockery/mockery": "^0.9.4",
+ "phpunit/phpunit": ">=5.7 <6",
+ "symfony/var-dumper": "~2.8|~3.0"
+ },
+ "type": "library",
+ "extra": {
+ "laravel": {
+ "providers": [
+ "JiraRestApi\\JiraRestApiServiceProvider"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "JiraRestApi\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "KwangSeob Jeong",
+ "email": "lesstif@gmail.com",
+ "homepage": "http://lesstif.com/"
+ }
+ ],
+ "description": "JIRA REST API Client for PHP Users.",
+ "keywords": [
+ "jira",
+ "jira-php",
+ "jira-rest",
+ "rest"
+ ],
+ "time": "2019-01-07T15:34:05+00:00"
+ },
{
"name": "monolog/monolog",
"version": "1.23.0",
@@ -971,6 +1031,48 @@
],
"time": "2017-06-19T01:22:40+00:00"
},
+ {
+ "name": "netresearch/jsonmapper",
+ "version": "v1.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/cweiske/jsonmapper.git",
+ "reference": "3868fe1128ce1169228acdb623359dca74db5ef3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/3868fe1128ce1169228acdb623359dca74db5ef3",
+ "reference": "3868fe1128ce1169228acdb623359dca74db5ef3",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.6"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.8.35 || ~5.7 || ~6.4",
+ "squizlabs/php_codesniffer": "~1.5"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-0": {
+ "JsonMapper": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "OSL-3.0"
+ ],
+ "authors": [
+ {
+ "name": "Christian Weiske",
+ "email": "cweiske@cweiske.de",
+ "homepage": "http://github.com/cweiske/jsonmapper/",
+ "role": "Developer"
+ }
+ ],
+ "description": "Map nested JSON structures onto PHP classes",
+ "time": "2017-11-28T21:30:01+00:00"
+ },
{
"name": "psr/http-message",
"version": "1.0.1",
@@ -1555,37 +1657,87 @@
"description": "Symfony Yaml Component",
"homepage": "https://symfony.com",
"time": "2018-01-03T07:36:31+00:00"
+ },
+ {
+ "name": "vlucas/phpdotenv",
+ "version": "v2.5.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/vlucas/phpdotenv.git",
+ "reference": "cfd5dc225767ca154853752abc93aeec040fcf36"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/cfd5dc225767ca154853752abc93aeec040fcf36",
+ "reference": "cfd5dc225767ca154853752abc93aeec040fcf36",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.9"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.8.35 || ^5.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.5-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Dotenv\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Vance Lucas",
+ "email": "vance@vancelucas.com",
+ "homepage": "http://www.vancelucas.com"
+ }
+ ],
+ "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.",
+ "keywords": [
+ "dotenv",
+ "env",
+ "environment"
+ ],
+ "time": "2018-10-30T17:29:25+00:00"
}
],
"packages-dev": [
{
"name": "doctrine/instantiator",
- "version": "1.0.5",
+ "version": "1.1.0",
"source": {
"type": "git",
"url": "https://github.com/doctrine/instantiator.git",
- "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d"
+ "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d",
- "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d",
+ "url": "https://api.github.com/repos/doctrine/instantiator/zipball/185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda",
+ "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda",
"shasum": ""
},
"require": {
- "php": ">=5.3,<8.0-DEV"
+ "php": "^7.1"
},
"require-dev": {
"athletic/athletic": "~0.1.8",
"ext-pdo": "*",
"ext-phar": "*",
- "phpunit/phpunit": "~4.0",
- "squizlabs/php_codesniffer": "~2.0"
+ "phpunit/phpunit": "^6.2.3",
+ "squizlabs/php_codesniffer": "^3.0.2"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.0.x-dev"
+ "dev-master": "1.2.x-dev"
}
},
"autoload": {
@@ -1610,7 +1762,194 @@
"constructor",
"instantiate"
],
- "time": "2015-06-14T21:17:01+00:00"
+ "time": "2017-07-22T11:58:36+00:00"
+ },
+ {
+ "name": "drupal/coder",
+ "version": "8.3.1",
+ "source": {
+ "type": "git",
+ "url": "https://git.drupal.org/project/coder.git",
+ "reference": "29a25627e7148b3119c84f18e087fc3b8c85b959"
+ },
+ "require": {
+ "ext-mbstring": "*",
+ "php": ">=5.4.0",
+ "squizlabs/php_codesniffer": "^3.0.1",
+ "symfony/yaml": ">=2.0.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": ">=3.7 <6"
+ },
+ "type": "phpcodesniffer-standard",
+ "autoload": {
+ "psr-0": {
+ "Drupal\\": "coder_sniffer/Drupal/",
+ "DrupalPractice\\": "coder_sniffer/Drupal/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "GPL-2.0+"
+ ],
+ "description": "Coder is a library to review Drupal code.",
+ "homepage": "https://www.drupal.org/project/coder",
+ "keywords": [
+ "code review",
+ "phpcs",
+ "standards"
+ ],
+ "time": "2018-09-21T14:22:49+00:00"
+ },
+ {
+ "name": "myclabs/deep-copy",
+ "version": "1.8.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/myclabs/DeepCopy.git",
+ "reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8",
+ "reference": "3e01bdad3e18354c3dce54466b7fbe33a9f9f7f8",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.1"
+ },
+ "replace": {
+ "myclabs/deep-copy": "self.version"
+ },
+ "require-dev": {
+ "doctrine/collections": "^1.0",
+ "doctrine/common": "^2.6",
+ "phpunit/phpunit": "^7.1"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "DeepCopy\\": "src/DeepCopy/"
+ },
+ "files": [
+ "src/DeepCopy/deep_copy.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Create deep copies (clones) of your objects",
+ "keywords": [
+ "clone",
+ "copy",
+ "duplicate",
+ "object",
+ "object graph"
+ ],
+ "time": "2018-06-11T23:09:50+00:00"
+ },
+ {
+ "name": "phar-io/manifest",
+ "version": "1.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phar-io/manifest.git",
+ "reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phar-io/manifest/zipball/2df402786ab5368a0169091f61a7c1e0eb6852d0",
+ "reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-phar": "*",
+ "phar-io/version": "^1.0.1",
+ "php": "^5.6 || ^7.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Arne Blankerts",
+ "email": "arne@blankerts.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Heuer",
+ "email": "sebastian@phpeople.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "Developer"
+ }
+ ],
+ "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)",
+ "time": "2017-03-05T18:14:27+00:00"
+ },
+ {
+ "name": "phar-io/version",
+ "version": "1.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phar-io/version.git",
+ "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phar-io/version/zipball/a70c0ced4be299a63d32fa96d9281d03e94041df",
+ "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.6 || ^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Arne Blankerts",
+ "email": "arne@blankerts.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Heuer",
+ "email": "sebastian@phpeople.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "Developer"
+ }
+ ],
+ "description": "Library for handling version information and constraints",
+ "time": "2017-03-05T17:38:23+00:00"
},
{
"name": "phpdocumentor/reflection-common",
@@ -1668,29 +2007,35 @@
},
{
"name": "phpdocumentor/reflection-docblock",
- "version": "3.2.2",
+ "version": "4.3.0",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
- "reference": "4aada1f93c72c35e22fb1383b47fee43b8f1d157"
+ "reference": "94fd0001232e47129dd3504189fa1c7225010d08"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/4aada1f93c72c35e22fb1383b47fee43b8f1d157",
- "reference": "4aada1f93c72c35e22fb1383b47fee43b8f1d157",
+ "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/94fd0001232e47129dd3504189fa1c7225010d08",
+ "reference": "94fd0001232e47129dd3504189fa1c7225010d08",
"shasum": ""
},
"require": {
- "php": ">=5.5",
- "phpdocumentor/reflection-common": "^1.0@dev",
- "phpdocumentor/type-resolver": "^0.3.0",
+ "php": "^7.0",
+ "phpdocumentor/reflection-common": "^1.0.0",
+ "phpdocumentor/type-resolver": "^0.4.0",
"webmozart/assert": "^1.0"
},
"require-dev": {
- "mockery/mockery": "^0.9.4",
- "phpunit/phpunit": "^4.4"
+ "doctrine/instantiator": "~1.0.5",
+ "mockery/mockery": "^1.0",
+ "phpunit/phpunit": "^6.4"
},
"type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.x-dev"
+ }
+ },
"autoload": {
"psr-4": {
"phpDocumentor\\Reflection\\": [
@@ -1709,20 +2054,20 @@
}
],
"description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
- "time": "2017-08-08T06:39:58+00:00"
+ "time": "2017-11-30T07:14:17+00:00"
},
{
"name": "phpdocumentor/type-resolver",
- "version": "0.3.0",
+ "version": "0.4.0",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/TypeResolver.git",
- "reference": "fb3933512008d8162b3cdf9e18dba9309b7c3773"
+ "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/fb3933512008d8162b3cdf9e18dba9309b7c3773",
- "reference": "fb3933512008d8162b3cdf9e18dba9309b7c3773",
+ "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7",
+ "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7",
"shasum": ""
},
"require": {
@@ -1756,37 +2101,37 @@
"email": "me@mikevanriel.com"
}
],
- "time": "2017-06-03T08:32:36+00:00"
+ "time": "2017-07-14T14:27:02+00:00"
},
{
"name": "phpspec/prophecy",
- "version": "1.7.5",
+ "version": "1.8.0",
"source": {
"type": "git",
"url": "https://github.com/phpspec/prophecy.git",
- "reference": "dfd6be44111a7c41c2e884a336cc4f461b3b2401"
+ "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpspec/prophecy/zipball/dfd6be44111a7c41c2e884a336cc4f461b3b2401",
- "reference": "dfd6be44111a7c41c2e884a336cc4f461b3b2401",
+ "url": "https://api.github.com/repos/phpspec/prophecy/zipball/4ba436b55987b4bf311cb7c6ba82aa528aac0a06",
+ "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06",
"shasum": ""
},
"require": {
"doctrine/instantiator": "^1.0.2",
"php": "^5.3|^7.0",
"phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0",
- "sebastian/comparator": "^1.1|^2.0",
+ "sebastian/comparator": "^1.1|^2.0|^3.0",
"sebastian/recursion-context": "^1.0|^2.0|^3.0"
},
"require-dev": {
"phpspec/phpspec": "^2.5|^3.2",
- "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5"
+ "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.7.x-dev"
+ "dev-master": "1.8.x-dev"
}
},
"autoload": {
@@ -1819,43 +2164,44 @@
"spy",
"stub"
],
- "time": "2018-02-19T10:16:54+00:00"
+ "time": "2018-08-05T17:53:17+00:00"
},
{
"name": "phpunit/php-code-coverage",
- "version": "2.2.4",
+ "version": "5.3.2",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
- "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979"
+ "reference": "c89677919c5dd6d3b3852f230a663118762218ac"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/eabf68b476ac7d0f73793aada060f1c1a9bf8979",
- "reference": "eabf68b476ac7d0f73793aada060f1c1a9bf8979",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/c89677919c5dd6d3b3852f230a663118762218ac",
+ "reference": "c89677919c5dd6d3b3852f230a663118762218ac",
"shasum": ""
},
"require": {
- "php": ">=5.3.3",
- "phpunit/php-file-iterator": "~1.3",
- "phpunit/php-text-template": "~1.2",
- "phpunit/php-token-stream": "~1.3",
- "sebastian/environment": "^1.3.2",
- "sebastian/version": "~1.0"
+ "ext-dom": "*",
+ "ext-xmlwriter": "*",
+ "php": "^7.0",
+ "phpunit/php-file-iterator": "^1.4.2",
+ "phpunit/php-text-template": "^1.2.1",
+ "phpunit/php-token-stream": "^2.0.1",
+ "sebastian/code-unit-reverse-lookup": "^1.0.1",
+ "sebastian/environment": "^3.0",
+ "sebastian/version": "^2.0.1",
+ "theseer/tokenizer": "^1.1"
},
"require-dev": {
- "ext-xdebug": ">=2.1.4",
- "phpunit/phpunit": "~4"
+ "phpunit/phpunit": "^6.0"
},
"suggest": {
- "ext-dom": "*",
- "ext-xdebug": ">=2.2.1",
- "ext-xmlwriter": "*"
+ "ext-xdebug": "^2.5.5"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "2.2.x-dev"
+ "dev-master": "5.3.x-dev"
}
},
"autoload": {
@@ -1870,7 +2216,7 @@
"authors": [
{
"name": "Sebastian Bergmann",
- "email": "sb@sebastian-bergmann.de",
+ "email": "sebastian@phpunit.de",
"role": "lead"
}
],
@@ -1881,7 +2227,7 @@
"testing",
"xunit"
],
- "time": "2015-10-06T15:47:00+00:00"
+ "time": "2018-04-06T15:36:58+00:00"
},
{
"name": "phpunit/php-file-iterator",
@@ -2022,29 +2368,29 @@
},
{
"name": "phpunit/php-token-stream",
- "version": "1.4.12",
+ "version": "2.0.2",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-token-stream.git",
- "reference": "1ce90ba27c42e4e44e6d8458241466380b51fa16"
+ "reference": "791198a2c6254db10131eecfe8c06670700904db"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/1ce90ba27c42e4e44e6d8458241466380b51fa16",
- "reference": "1ce90ba27c42e4e44e6d8458241466380b51fa16",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/791198a2c6254db10131eecfe8c06670700904db",
+ "reference": "791198a2c6254db10131eecfe8c06670700904db",
"shasum": ""
},
"require": {
"ext-tokenizer": "*",
- "php": ">=5.3.3"
+ "php": "^7.0"
},
"require-dev": {
- "phpunit/phpunit": "~4.2"
+ "phpunit/phpunit": "^6.2.4"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.4-dev"
+ "dev-master": "2.0-dev"
}
},
"autoload": {
@@ -2067,45 +2413,57 @@
"keywords": [
"tokenizer"
],
- "time": "2017-12-04T08:55:13+00:00"
+ "time": "2017-11-27T05:48:46+00:00"
},
{
"name": "phpunit/phpunit",
- "version": "4.8.36",
+ "version": "6.5.13",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
- "reference": "46023de9a91eec7dfb06cc56cb4e260017298517"
+ "reference": "0973426fb012359b2f18d3bd1e90ef1172839693"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/46023de9a91eec7dfb06cc56cb4e260017298517",
- "reference": "46023de9a91eec7dfb06cc56cb4e260017298517",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/0973426fb012359b2f18d3bd1e90ef1172839693",
+ "reference": "0973426fb012359b2f18d3bd1e90ef1172839693",
"shasum": ""
},
"require": {
"ext-dom": "*",
"ext-json": "*",
- "ext-pcre": "*",
- "ext-reflection": "*",
- "ext-spl": "*",
- "php": ">=5.3.3",
- "phpspec/prophecy": "^1.3.1",
- "phpunit/php-code-coverage": "~2.1",
- "phpunit/php-file-iterator": "~1.4",
- "phpunit/php-text-template": "~1.2",
- "phpunit/php-timer": "^1.0.6",
- "phpunit/phpunit-mock-objects": "~2.3",
- "sebastian/comparator": "~1.2.2",
- "sebastian/diff": "~1.2",
- "sebastian/environment": "~1.3",
- "sebastian/exporter": "~1.2",
- "sebastian/global-state": "~1.0",
- "sebastian/version": "~1.0",
- "symfony/yaml": "~2.1|~3.0"
+ "ext-libxml": "*",
+ "ext-mbstring": "*",
+ "ext-xml": "*",
+ "myclabs/deep-copy": "^1.6.1",
+ "phar-io/manifest": "^1.0.1",
+ "phar-io/version": "^1.0",
+ "php": "^7.0",
+ "phpspec/prophecy": "^1.7",
+ "phpunit/php-code-coverage": "^5.3",
+ "phpunit/php-file-iterator": "^1.4.3",
+ "phpunit/php-text-template": "^1.2.1",
+ "phpunit/php-timer": "^1.0.9",
+ "phpunit/phpunit-mock-objects": "^5.0.9",
+ "sebastian/comparator": "^2.1",
+ "sebastian/diff": "^2.0",
+ "sebastian/environment": "^3.1",
+ "sebastian/exporter": "^3.1",
+ "sebastian/global-state": "^2.0",
+ "sebastian/object-enumerator": "^3.0.3",
+ "sebastian/resource-operations": "^1.0",
+ "sebastian/version": "^2.0.1"
+ },
+ "conflict": {
+ "phpdocumentor/reflection-docblock": "3.0.2",
+ "phpunit/dbunit": "<3.0"
+ },
+ "require-dev": {
+ "ext-pdo": "*"
},
"suggest": {
- "phpunit/php-invoker": "~1.1"
+ "ext-xdebug": "*",
+ "phpunit/php-invoker": "^1.1"
},
"bin": [
"phpunit"
@@ -2113,7 +2471,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "4.8.x-dev"
+ "dev-master": "6.5.x-dev"
}
},
"autoload": {
@@ -2139,30 +2497,33 @@
"testing",
"xunit"
],
- "time": "2017-06-21T08:07:12+00:00"
+ "time": "2018-09-08T15:10:43+00:00"
},
{
"name": "phpunit/phpunit-mock-objects",
- "version": "2.3.8",
+ "version": "5.0.10",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git",
- "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983"
+ "reference": "cd1cf05c553ecfec36b170070573e540b67d3f1f"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/ac8e7a3db35738d56ee9a76e78a4e03d97628983",
- "reference": "ac8e7a3db35738d56ee9a76e78a4e03d97628983",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/cd1cf05c553ecfec36b170070573e540b67d3f1f",
+ "reference": "cd1cf05c553ecfec36b170070573e540b67d3f1f",
"shasum": ""
},
"require": {
- "doctrine/instantiator": "^1.0.2",
- "php": ">=5.3.3",
- "phpunit/php-text-template": "~1.2",
- "sebastian/exporter": "~1.2"
+ "doctrine/instantiator": "^1.0.5",
+ "php": "^7.0",
+ "phpunit/php-text-template": "^1.2.1",
+ "sebastian/exporter": "^3.1"
+ },
+ "conflict": {
+ "phpunit/phpunit": "<6.0"
},
"require-dev": {
- "phpunit/phpunit": "~4.4"
+ "phpunit/phpunit": "^6.5.11"
},
"suggest": {
"ext-soap": "*"
@@ -2170,7 +2531,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "2.3.x-dev"
+ "dev-master": "5.0.x-dev"
}
},
"autoload": {
@@ -2185,7 +2546,7 @@
"authors": [
{
"name": "Sebastian Bergmann",
- "email": "sb@sebastian-bergmann.de",
+ "email": "sebastian@phpunit.de",
"role": "lead"
}
],
@@ -2195,34 +2556,79 @@
"mock",
"xunit"
],
- "time": "2015-10-02T06:51:40+00:00"
+ "time": "2018-08-09T05:50:03+00:00"
+ },
+ {
+ "name": "sebastian/code-unit-reverse-lookup",
+ "version": "1.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
+ "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18",
+ "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.6 || ^7.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^5.7 || ^6.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "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"
},
{
"name": "sebastian/comparator",
- "version": "1.2.4",
+ "version": "2.1.3",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/comparator.git",
- "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be"
+ "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2b7424b55f5047b47ac6e5ccb20b2aea4011d9be",
- "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be",
+ "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/34369daee48eafb2651bea869b4b15d75ccc35f9",
+ "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9",
"shasum": ""
},
"require": {
- "php": ">=5.3.3",
- "sebastian/diff": "~1.2",
- "sebastian/exporter": "~1.2 || ~2.0"
+ "php": "^7.0",
+ "sebastian/diff": "^2.0 || ^3.0",
+ "sebastian/exporter": "^3.1"
},
"require-dev": {
- "phpunit/phpunit": "~4.4"
+ "phpunit/phpunit": "^6.4"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.2.x-dev"
+ "dev-master": "2.1.x-dev"
}
},
"autoload": {
@@ -2253,38 +2659,38 @@
}
],
"description": "Provides the functionality to compare PHP values for equality",
- "homepage": "http://www.github.com/sebastianbergmann/comparator",
+ "homepage": "https://github.com/sebastianbergmann/comparator",
"keywords": [
"comparator",
"compare",
"equality"
],
- "time": "2017-01-29T09:50:25+00:00"
+ "time": "2018-02-01T13:46:46+00:00"
},
{
"name": "sebastian/diff",
- "version": "1.4.3",
+ "version": "2.0.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/diff.git",
- "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4"
+ "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7f066a26a962dbe58ddea9f72a4e82874a3975a4",
- "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4",
+ "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/347c1d8b49c5c3ee30c7040ea6fc446790e6bddd",
+ "reference": "347c1d8b49c5c3ee30c7040ea6fc446790e6bddd",
"shasum": ""
},
"require": {
- "php": "^5.3.3 || ^7.0"
+ "php": "^7.0"
},
"require-dev": {
- "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0"
+ "phpunit/phpunit": "^6.2"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.4-dev"
+ "dev-master": "2.0-dev"
}
},
"autoload": {
@@ -2311,32 +2717,32 @@
"keywords": [
"diff"
],
- "time": "2017-05-22T07:24:03+00:00"
+ "time": "2017-08-03T08:09:46+00:00"
},
{
"name": "sebastian/environment",
- "version": "1.3.8",
+ "version": "3.1.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/environment.git",
- "reference": "be2c607e43ce4c89ecd60e75c6a85c126e754aea"
+ "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/be2c607e43ce4c89ecd60e75c6a85c126e754aea",
- "reference": "be2c607e43ce4c89ecd60e75c6a85c126e754aea",
+ "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/cd0871b3975fb7fc44d11314fd1ee20925fce4f5",
+ "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5",
"shasum": ""
},
"require": {
- "php": "^5.3.3 || ^7.0"
+ "php": "^7.0"
},
"require-dev": {
- "phpunit/phpunit": "^4.8 || ^5.0"
+ "phpunit/phpunit": "^6.1"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.3.x-dev"
+ "dev-master": "3.1.x-dev"
}
},
"autoload": {
@@ -2361,34 +2767,34 @@
"environment",
"hhvm"
],
- "time": "2016-08-18T05:49:44+00:00"
+ "time": "2017-07-01T08:51:00+00:00"
},
{
"name": "sebastian/exporter",
- "version": "1.2.2",
+ "version": "3.1.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/exporter.git",
- "reference": "42c4c2eec485ee3e159ec9884f95b431287edde4"
+ "reference": "234199f4528de6d12aaa58b612e98f7d36adb937"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/42c4c2eec485ee3e159ec9884f95b431287edde4",
- "reference": "42c4c2eec485ee3e159ec9884f95b431287edde4",
+ "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/234199f4528de6d12aaa58b612e98f7d36adb937",
+ "reference": "234199f4528de6d12aaa58b612e98f7d36adb937",
"shasum": ""
},
"require": {
- "php": ">=5.3.3",
- "sebastian/recursion-context": "~1.0"
+ "php": "^7.0",
+ "sebastian/recursion-context": "^3.0"
},
"require-dev": {
"ext-mbstring": "*",
- "phpunit/phpunit": "~4.4"
+ "phpunit/phpunit": "^6.0"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.3.x-dev"
+ "dev-master": "3.1.x-dev"
}
},
"autoload": {
@@ -2428,27 +2834,27 @@
"export",
"exporter"
],
- "time": "2016-06-17T09:04:28+00:00"
+ "time": "2017-04-03T13:19:02+00:00"
},
{
"name": "sebastian/global-state",
- "version": "1.1.1",
+ "version": "2.0.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/global-state.git",
- "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4"
+ "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4",
- "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4",
+ "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4",
+ "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4",
"shasum": ""
},
"require": {
- "php": ">=5.3.3"
+ "php": "^7.0"
},
"require-dev": {
- "phpunit/phpunit": "~4.2"
+ "phpunit/phpunit": "^6.0"
},
"suggest": {
"ext-uopz": "*"
@@ -2456,7 +2862,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.0-dev"
+ "dev-master": "2.0-dev"
}
},
"autoload": {
@@ -2479,32 +2885,124 @@
"keywords": [
"global state"
],
- "time": "2015-10-12T03:26:01+00:00"
+ "time": "2017-04-27T15:39:26+00:00"
+ },
+ {
+ "name": "sebastian/object-enumerator",
+ "version": "3.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/object-enumerator.git",
+ "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/7cfd9e65d11ffb5af41198476395774d4c8a84c5",
+ "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.0",
+ "sebastian/object-reflector": "^1.1.1",
+ "sebastian/recursion-context": "^3.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^6.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.0.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Traverses array structures and object graphs to enumerate all referenced objects",
+ "homepage": "https://github.com/sebastianbergmann/object-enumerator/",
+ "time": "2017-08-03T12:35:26+00:00"
+ },
+ {
+ "name": "sebastian/object-reflector",
+ "version": "1.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/object-reflector.git",
+ "reference": "773f97c67f28de00d397be301821b06708fca0be"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be",
+ "reference": "773f97c67f28de00d397be301821b06708fca0be",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^6.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.1-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Allows reflection of object attributes, including inherited and non-public ones",
+ "homepage": "https://github.com/sebastianbergmann/object-reflector/",
+ "time": "2017-03-29T09:07:27+00:00"
},
{
"name": "sebastian/recursion-context",
- "version": "1.0.5",
+ "version": "3.0.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/recursion-context.git",
- "reference": "b19cc3298482a335a95f3016d2f8a6950f0fbcd7"
+ "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/b19cc3298482a335a95f3016d2f8a6950f0fbcd7",
- "reference": "b19cc3298482a335a95f3016d2f8a6950f0fbcd7",
+ "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8",
+ "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8",
"shasum": ""
},
"require": {
- "php": ">=5.3.3"
+ "php": "^7.0"
},
"require-dev": {
- "phpunit/phpunit": "~4.4"
+ "phpunit/phpunit": "^6.0"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.0.x-dev"
+ "dev-master": "3.0.x-dev"
}
},
"autoload": {
@@ -2532,23 +3030,73 @@
],
"description": "Provides functionality to recursively process PHP variables",
"homepage": "http://www.github.com/sebastianbergmann/recursion-context",
- "time": "2016-10-03T07:41:43+00:00"
+ "time": "2017-03-03T06:23:57+00:00"
+ },
+ {
+ "name": "sebastian/resource-operations",
+ "version": "1.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/resource-operations.git",
+ "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ce990bb21759f94aeafd30209e8cfcdfa8bc3f52",
+ "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.6.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "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"
},
{
"name": "sebastian/version",
- "version": "1.0.6",
+ "version": "2.0.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/version.git",
- "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6"
+ "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/58b3a85e7999757d6ad81c787a1fbf5ff6c628c6",
- "reference": "58b3a85e7999757d6ad81c787a1fbf5ff6c628c6",
+ "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019",
+ "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019",
"shasum": ""
},
+ "require": {
+ "php": ">=5.6"
+ },
"type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
"autoload": {
"classmap": [
"src/"
@@ -2567,24 +3115,174 @@
],
"description": "Library that helps with managing the version number of Git-hosted PHP projects",
"homepage": "https://github.com/sebastianbergmann/version",
- "time": "2015-06-21T13:59:46+00:00"
+ "time": "2016-10-03T07:35:21+00:00"
+ },
+ {
+ "name": "squizlabs/php_codesniffer",
+ "version": "3.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
+ "reference": "379deb987e26c7cd103a7b387aea178baec96e48"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/379deb987e26c7cd103a7b387aea178baec96e48",
+ "reference": "379deb987e26c7cd103a7b387aea178baec96e48",
+ "shasum": ""
+ },
+ "require": {
+ "ext-simplexml": "*",
+ "ext-tokenizer": "*",
+ "ext-xmlwriter": "*",
+ "php": ">=5.4.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0"
+ },
+ "bin": [
+ "bin/phpcs",
+ "bin/phpcbf"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.x-dev"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Greg Sherwood",
+ "role": "lead"
+ }
+ ],
+ "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.",
+ "homepage": "http://www.squizlabs.com/php-codesniffer",
+ "keywords": [
+ "phpcs",
+ "standards"
+ ],
+ "time": "2018-12-19T23:57:18+00:00"
+ },
+ {
+ "name": "symfony/polyfill-ctype",
+ "version": "v1.10.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-ctype.git",
+ "reference": "e3d826245268269cd66f8326bd8bc066687b4a19"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/e3d826245268269cd66f8326bd8bc066687b4a19",
+ "reference": "e3d826245268269cd66f8326bd8bc066687b4a19",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.3"
+ },
+ "suggest": {
+ "ext-ctype": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.9-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Polyfill\\Ctype\\": ""
+ },
+ "files": [
+ "bootstrap.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ },
+ {
+ "name": "Gert de Pagter",
+ "email": "BackEndTea@gmail.com"
+ }
+ ],
+ "description": "Symfony polyfill for ctype functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "ctype",
+ "polyfill",
+ "portable"
+ ],
+ "time": "2018-08-06T14:22:27+00:00"
+ },
+ {
+ "name": "theseer/tokenizer",
+ "version": "1.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/theseer/tokenizer.git",
+ "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/theseer/tokenizer/zipball/cb2f008f3f05af2893a87208fe6a6c4985483f8b",
+ "reference": "cb2f008f3f05af2893a87208fe6a6c4985483f8b",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-tokenizer": "*",
+ "ext-xmlwriter": "*",
+ "php": "^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Arne Blankerts",
+ "email": "arne@blankerts.de",
+ "role": "Developer"
+ }
+ ],
+ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
+ "time": "2017-04-07T12:08:54+00:00"
},
{
"name": "webmozart/assert",
- "version": "1.3.0",
+ "version": "1.4.0",
"source": {
"type": "git",
"url": "https://github.com/webmozart/assert.git",
- "reference": "0df1908962e7a3071564e857d86874dad1ef204a"
+ "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/webmozart/assert/zipball/0df1908962e7a3071564e857d86874dad1ef204a",
- "reference": "0df1908962e7a3071564e857d86874dad1ef204a",
+ "url": "https://api.github.com/repos/webmozart/assert/zipball/83e253c8e0be5b0257b881e1827274667c5c17a9",
+ "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9",
"shasum": ""
},
"require": {
- "php": "^5.3.3 || ^7.0"
+ "php": "^5.3.3 || ^7.0",
+ "symfony/polyfill-ctype": "^1.8"
},
"require-dev": {
"phpunit/phpunit": "^4.6",
@@ -2617,7 +3315,7 @@
"check",
"validate"
],
- "time": "2018-01-29T19:49:41+00:00"
+ "time": "2018-12-25T11:19:39+00:00"
}
],
"aliases": [],
@@ -2626,7 +3324,7 @@
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
- "php": ">=5.5",
+ "php": ">=5.5.23",
"ext-sqlite3": "*",
"ext-pdo_sqlite": "*"
},
diff --git a/phpcs.xml b/phpcs.xml
new file mode 100644
index 0000000..a14c934
--- /dev/null
+++ b/phpcs.xml
@@ -0,0 +1,26 @@
+
+
+ Default PHP CodeSniffer configuration for tl.
+ ./src
+ ./tests
+
+
+ Commands
+ tests
+
+
+ 0
+
+
+ 0
+
+
+ Repository.php
+
+
+ Schema.php
+
+
+ Application.php
+
+
diff --git a/services.yml b/services.yml
index 6784ff9..20064a2 100644
--- a/services.yml
+++ b/services.yml
@@ -26,10 +26,20 @@ services:
class: Larowlan\Tl\Repository\DbRepository
arguments: ["@connection"]
connector:
+ class: Larowlan\Tl\Connector\Manager
+ arguments: ['@container', '@cache', '%config%', '%version%']
+ tags:
+ - { name: configurable }
+ connector.redmine:
class: Larowlan\Tl\Connector\RedmineConnector
arguments: ['@http_client', '@cache', '%config%', '%version%']
tags:
- - { name: configurable }
+ - { name: connector }
+ connector.jira:
+ class: Larowlan\Tl\Connector\JiraConnector
+ arguments: ['%config%', '@cache', '%config%', '%version%', '@http_client']
+ tags:
+ - { name: connector }
reviewer:
class: Larowlan\Tl\Reviewer
arguments: ["@connector", "@repository"]
diff --git a/src/Application.php b/src/Application.php
index 72caff4..420cce9 100644
--- a/src/Application.php
+++ b/src/Application.php
@@ -1,8 +1,4 @@
container->get('config.processor');
- /** @var ConfigurationCollector|ConfigurationInterface $configuration */
+ /** @var \Larowlan\Tl\Configuration\ConfigurationCollector|ConfigurationInterface|\Larowlan\Tl\Configuration\LoggerConfiguration $configuration */
$configuration = $this->container->get('config.configuration');
+ $configuration->setContainerBuilder($this->container);
$needs_config_ids = $this->container->getParameter('configurable_service_ids');
$needs_config = [];
foreach ($needs_config_ids as $id) {
diff --git a/src/CacheFactory.php b/src/CacheFactory.php
index f99086f..8318217 100644
--- a/src/CacheFactory.php
+++ b/src/CacheFactory.php
@@ -1,16 +1,20 @@
connector = $connector;
$this->repository = $repository;
diff --git a/src/Commands/Assigned.php b/src/Commands/Assigned.php
index 1a3a0e2..2fc8396 100644
--- a/src/Commands/Assigned.php
+++ b/src/Commands/Assigned.php
@@ -1,23 +1,19 @@
connector = $connector;
$this->repository = $repository;
@@ -56,20 +55,25 @@ protected function execute(InputInterface $input, OutputInterface $output) {
$table->setHeaders(['JobId', 'Title', 'Status']);
$rows = [];
$first = TRUE;
- foreach ($data as $project => $tickets) {
- if (!$first) {
- $rows[] = new TableSeparator();
- }
- $rows[] = ['', '' . $project . ''];
- $rows[] = new TableSeparator();
- foreach ($tickets as $id => $ticket_info) {
+ foreach ($data as $connector_id => $connector_data) {
+ foreach ($connector_data as $project => $tickets) {
+ if (!$first) {
+ $rows[] = new TableSeparator();
+ }
$rows[] = [
- $id,
- $ticket_info['title'],
- $ticket_info['status'],
+ '',
+ sprintf('%s [%s]', $project, $connector_id),
];
+ $rows[] = new TableSeparator();
+ foreach ($tickets as $id => $ticket_info) {
+ $rows[] = [
+ $id,
+ substr($ticket_info['title'], 0, 50) . '...',
+ $ticket_info['status'],
+ ];
+ }
+ $first = FALSE;
}
- $first = FALSE;
}
$table->setRows($rows);
$table->render();
diff --git a/src/Commands/Billable.php b/src/Commands/Billable.php
index a8a8d3d..5d7a573 100644
--- a/src/Commands/Billable.php
+++ b/src/Commands/Billable.php
@@ -1,8 +1,4 @@
connector = $connector;
$this->repository = $repository;
- $config = static::getDefaults($config);
+ $config = static::getDefaults($config, new ContainerBuilder());
$this->billablePercentage = $config['billable_percentage'];
$this->hoursPerDay = $config['hours_per_day'];
$this->targets = $config['days_per_month'];
@@ -162,27 +165,29 @@ protected function execute(InputInterface $input, OutputInterface $output) {
$projects = [];
$billable_projects = [];
$non_billable_projects = [];
- foreach ($this->repository->totalByTicket($date->getTimestamp(), $end->getTimestamp()) as $tid => $duration) {
- $details = $this->connector->ticketDetails($tid);
- if ($details) {
- if (!isset($projects[$details->getProjectId()])) {
- $projects[$details->getProjectId()] = 0;
- }
- if ($details->isBillable()) {
- $billable += $duration;
- $projects[$details->getProjectId()] += $duration;
- $billable_projects[$details->getProjectId()] = $details->getProjectId();
+ foreach ($this->repository->totalByTicket($date->getTimestamp(), $end->getTimestamp()) as $connector_id => $items) {
+ foreach ($items as $tid => $duration) {
+ $details = $this->connector->ticketDetails($tid, $connector_id);
+ if ($details) {
+ if (!isset($projects[$connector_id][$details->getProjectId()])) {
+ $projects[$connector_id][$details->getProjectId()] = 0;
+ }
+ if ($details->isBillable()) {
+ $billable += $duration;
+ $projects[$connector_id][$details->getProjectId()] += $duration;
+ $billable_projects[$connector_id][$details->getProjectId()] = $details->getProjectId();
+ }
+ else {
+ $non_billable += $duration;
+ $projects[$connector_id][$details->getProjectId()] += $duration;
+ $non_billable_projects[$connector_id][$details->getProjectId()] = $details->getProjectId();
+ }
}
else {
- $non_billable += $duration;
- $projects[$details->getProjectId()] += $duration;
- $non_billable_projects[$details->getProjectId()] = $details->getProjectId();
+ $unknown += $duration;
+ $unknowns[] = $tid;
}
}
- else {
- $unknown += $duration;
- $unknowns[] = $tid;
- }
}
$table = new Table($output);
if (!$project) {
@@ -199,29 +204,43 @@ protected function execute(InputInterface $input, OutputInterface $output) {
if ($project) {
$project_names = $this->connector->projectNames();
$rows[] = ['Billable', '', '', ''];
- foreach ($billable_projects as $project_id) {
- $project_name = isset($project_names[$project_id]) ? $project_names[$project_id] : "Project ID $project_id";
- $rows[] = ['', $project_name, Formatter::formatDuration($projects[$project_id]), ''];
+ foreach ($billable_projects as $connector_id => $connector_projects) {
+ foreach ($connector_projects as $project_id) {
+ $project_name = isset($project_names[$connector_id][$project_id]) ? $project_names[$connector_id][$project_id] : "Project ID $project_id";
+ $rows[] = [
+ '',
+ $project_name,
+ Formatter::formatDuration($projects[$connector_id][$project_id]),
+ '',
+ ];
+ }
}
$rows[] = new TableSeparator();
$rows[] = [
'Billable',
'',
Formatter::formatDuration($billable),
- "<$tag>" . ($total ? round(100 * $billable / $total, 2) : 0) . "%$tag>"
+ "<$tag>" . ($total ? round(100 * $billable / $total, 2) : 0) . "%$tag>",
];
$rows[] = new TableSeparator();
$rows[] = ['Non-Billable', '', '', ''];
- foreach ($non_billable_projects as $project_id) {
- $project_name = isset($project_names[$project_id]) ? $project_names[$project_id] : "Project ID $project_id";
- $rows[] = ['', $project_name, Formatter::formatDuration($projects[$project_id]), ''];
+ foreach ($non_billable_projects as $connector_id => $connector_projects) {
+ foreach ($connector_projects as $project_id) {
+ $project_name = isset($project_names[$connector_id][$project_id]) ? $project_names[$connector_id][$project_id] : "Project ID $project_id";
+ $rows[] = [
+ '',
+ $project_name,
+ Formatter::formatDuration($projects[$connector_id][$project_id]),
+ '',
+ ];
+ }
}
$rows[] = new TableSeparator();
$rows[] = [
'Non-billable',
'',
Formatter::formatDuration($non_billable),
- round(100 * $non_billable / $total, 2) . '%'
+ round(100 * $non_billable / $total, 2) . '%',
];
if ($unknown) {
$rows[] = new TableSeparator();
@@ -236,12 +255,12 @@ protected function execute(InputInterface $input, OutputInterface $output) {
$rows[] = [
'Billable',
Formatter::formatDuration($billable),
- "<$tag>" . ($total ? round(100 * $billable / $total, 2) : 0) . "%$tag>"
+ "<$tag>" . ($total ? round(100 * $billable / $total, 2) : 0) . "%$tag>",
];
$rows[] = [
'Non-billable',
Formatter::formatDuration($non_billable),
- ($total ? round(100 * $non_billable / $total, 2) : 0) . '%'
+ ($total ? round(100 * $non_billable / $total, 2) : 0) . '%',
];
if ($unknown) {
$rows[] = ['Unknown*', Formatter::formatDuration($unknown), round(100 * $unknown / $total, 2) . '%'];
@@ -280,6 +299,9 @@ protected function execute(InputInterface $input, OutputInterface $output) {
$table->render();
}
+ /**
+ *
+ */
protected function getTotalMonthHours($m, $y) {
$target_key = sprintf('%s_%s', $y, $m);
// If we have no customisations it's just number of days times hours per
@@ -340,10 +362,13 @@ protected function formatProgressRow($caption, $numerator, $denominator, $expect
return [
$caption,
"$numerator/$denominator ($difference)",
- round(100 * $numerator / $denominator, 2) . '%'
+ round(100 * $numerator / $denominator, 2) . '%',
];
}
+ /**
+ *
+ */
protected function getWeekdaysInMonth($m, $y) {
$target_key = sprintf('%s_%s', $y, $m);
if (isset($this->targets[$target_key])) {
@@ -364,6 +389,9 @@ protected function getWeekdaysInMonth($m, $y) {
return $weekdays + 20;
}
+ /**
+ *
+ */
protected function getWeekdaysPassedThisMonth($output, \DateTime $reference_point) {
$days_passed = $reference_point->format('d');
if ($reference_point->format('Y-m-t') < date('Y-m-d')) {
@@ -401,17 +429,17 @@ protected function getWeekdaysPassedThisMonth($output, \DateTime $reference_poin
/**
* {@inheritdoc}
*/
- public static function getConfiguration(NodeDefinition $root_node) {
+ public static function getConfiguration(NodeDefinition $root_node, ContainerBuilder $container) {
$root_node->children()
- ->scalarNode('billable_percentage')
- ->defaultValue(0.8)
- ->end()
- ->scalarNode('hours_per_day')
- ->defaultValue(8)
- ->end()
- ->arrayNode('days_per_month')
- ->prototype('scalar')
- ->end()
+ ->scalarNode('billable_percentage')
+ ->defaultValue(0.8)
+ ->end()
+ ->scalarNode('hours_per_day')
+ ->defaultValue(8)
+ ->end()
+ ->arrayNode('days_per_month')
+ ->prototype('scalar')
+ ->end()
->end();
return $root_node;
}
@@ -419,7 +447,7 @@ public static function getConfiguration(NodeDefinition $root_node) {
/**
* {@inheritdoc}
*/
- public static function askPreBootQuestions(QuestionHelper $helper, InputInterface $input, OutputInterface $output, array $config) {
+ public static function askPreBootQuestions(QuestionHelper $helper, InputInterface $input, OutputInterface $output, array $config, ContainerBuilder $container) {
$default_percentage = isset($config['billable_percentage']) ? $config['billable_percentage'] : 0.8;
$default_hours_per_day = isset($config['hours_per_day']) ? $config['hours_per_day'] : 8;
$config = ['billable_percentage' => '', 'hours_per_day' => ''] + $config;
@@ -441,7 +469,7 @@ public function askPostBootQuestions(QuestionHelper $helper, InputInterface $inp
/**
* {@inheritdoc}
*/
- public static function getDefaults($config) {
+ public static function getDefaults($config, ContainerBuilder $container) {
return $config + [
'billable_percentage' => 0.8,
'hours_per_day' => 8,
diff --git a/src/Commands/Bitbar.php b/src/Commands/Bitbar.php
index 4749ff1..acc8643 100644
--- a/src/Commands/Bitbar.php
+++ b/src/Commands/Bitbar.php
@@ -1,69 +1,65 @@
connector = $connector;
- $this->repository = $repository;
- parent::__construct();
- }
+ /**
+ *
+ */
+ public function __construct(Connector $connector, Repository $repository) {
+ $this->connector = $connector;
+ $this->repository = $repository;
+ parent::__construct();
+ }
- /**
- * {@inheritdoc}
- */
- protected function configure() {
- $this
- ->setName('bitbar')
- ->setDescription('Bitbar output')
- ->setHelp('Bitbar outputUsage: tl bitbar')
- ->addUsage('tl bitbar');
- }
+ /**
+ * {@inheritdoc}
+ */
+ protected function configure() {
+ $this
+ ->setName('bitbar')
+ ->setDescription('Bitbar output')
+ ->setHelp('Bitbar outputUsage: tl bitbar')
+ ->addUsage('tl bitbar');
+ }
- /**
- * {@inheritdoc}
- */
- protected function execute(InputInterface $input, OutputInterface $output) {
- if ($open = $this->repository->getActive()) {
- $text = $open->tid . ': ' . Formatter::formatDuration(time() - $open->start) . ' ';
- }
- else {
- $text = 'Inactive ';
- }
- $total = 0;
- foreach ($this->repository->review(Total::ALL) as $data) {
- $total += $data->duration;
- }
- $text .= '(' . $total . 'h)';
- $output->writeln($text);
+ /**
+ * {@inheritdoc}
+ */
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ if ($open = $this->repository->getActive()) {
+ $text = $open->tid . ': ' . Formatter::formatDuration(time() - $open->start) . ' ';
+ }
+ else {
+ $text = 'Inactive ';
+ }
+ $total = 0;
+ foreach ($this->repository->review(Total::ALL) as $data) {
+ $total += $data->duration;
}
+ $text .= '(' . $total . 'h)';
+ $output->writeln($text);
+ }
}
diff --git a/src/Commands/CacheClear.php b/src/Commands/CacheClear.php
index 5a64d3c..b06a4aa 100644
--- a/src/Commands/CacheClear.php
+++ b/src/Commands/CacheClear.php
@@ -1,17 +1,11 @@
cache = $cache;
parent::__construct();
diff --git a/src/Commands/Combine.php b/src/Commands/Combine.php
index 0788d99..d2021ba 100644
--- a/src/Commands/Combine.php
+++ b/src/Commands/Combine.php
@@ -1,12 +1,9 @@
connector = $connector;
$this->repository = $repository;
@@ -56,13 +59,14 @@ protected function execute(InputInterface $input, OutputInterface $output) {
// Create a new combined entry and then remove fields we don't want to keep
// around in the new entry.
$combined_entry = clone $entry1;
- unset($combined_entry->id, $combined_entry->category, $combined_entry->comment, $combined_entry->teid);
+ unset($combined_entry->id, $combined_entry->category, $combined_entry->comment, $combined_entry->teid, $combined_entry->connector_id);
+ $combined_entry->connector_id = ':connector_id';
// Extend the entry date by the amount of time logged in the second entry.
$combined_entry->end = $entry1->end + ($entry2->end - $entry2->start);
// Insert the new entry, if all is well delete the two existing ones.
- if ($new_slot = $this->repository->insert((array) $combined_entry)) {
+ if ($new_slot = $this->repository->insert((array) $combined_entry, [':connector_id' => $entry1->connector_id])) {
$this->repository->delete($entry1->id);
$this->repository->delete($entry2->id);
@@ -70,6 +74,9 @@ protected function execute(InputInterface $input, OutputInterface $output) {
}
}
+ /**
+ *
+ */
protected function validateSlots($slot1, $slot2, OutputInterface $output) {
if ($slot1 === $slot2) {
throw new \InvalidArgumentException('You cannot combine a slot with itself.');
@@ -85,6 +92,9 @@ protected function validateSlots($slot1, $slot2, OutputInterface $output) {
if (!$entry2 = $this->repository->slot($slot2)) {
throw new \InvalidArgumentException(sprintf('Invalid slot id %s', $slot2));
}
+ if ($entry1->connector_id !== $entry2->connector_id) {
+ throw new \InvalidArgumentException(sprintf('You cannot combine slots from %s backend with slots from %s backend', Manager::formatConnectorId($entry2->connector_id), Manager::formatConnectorId($entry1->connector_id)));
+ }
// Ensure we've not already sent the slots.
if (!empty($entry1->teid) || !empty($entry1->teid)) {
throw new \InvalidArgumentException('You cannot combine entries that have already been sent.');
@@ -96,9 +106,12 @@ protected function validateSlots($slot1, $slot2, OutputInterface $output) {
return [$entry1, $entry2];
}
+ /**
+ *
+ */
protected function stopTicket($slot_id, OutputInterface $output) {
if ($stop = $this->repository->stop($slot_id)) {
- $stopped = $this->connector->ticketDetails($stop->tid);
+ $stopped = $this->connector->ticketDetails($stop->tid, $stop->connector_id);
$output->writeln(sprintf('Closed slot %d against ticket %d: %s, duration %s',
$stop->id,
$stop->tid,
@@ -107,4 +120,5 @@ protected function stopTicket($slot_id, OutputInterface $output) {
));
}
}
+
}
diff --git a/src/Commands/Comment.php b/src/Commands/Comment.php
index 65d71b3..ec9d57a 100644
--- a/src/Commands/Comment.php
+++ b/src/Commands/Comment.php
@@ -1,23 +1,18 @@
connector = $connector;
$this->repository = $repository;
@@ -62,7 +60,7 @@ protected function execute(InputInterface $input, OutputInterface $output) {
if ($entry->comment && !$input->getOption('recomment')) {
continue;
}
- $title = $this->connector->ticketDetails($entry->tid);
+ $title = $this->connector->ticketDetails($entry->tid, $entry->connector_id);
$question = new Question(
sprintf('Enter comment for slot %d [%d]: %s [%s h] [%s]',
$entry->id,
diff --git a/src/Commands/Configure.php b/src/Commands/Configure.php
index 8457b2e..4cfc60f 100644
--- a/src/Commands/Configure.php
+++ b/src/Commands/Configure.php
@@ -1,12 +1,7 @@
directory = $directory;
$this->configurableServiceIds = $configurable_service_ids;
@@ -56,15 +54,15 @@ protected function execute(InputInterface $input, OutputInterface $output) {
}
foreach ($this->configurableServiceIds as $service_id) {
$service_definition = $this->container->getDefinition($service_id);
- /** @var ConfigurableService $service_class */
+ /** @var \Larowlan\Tl\Configuration\ConfigurableService $service_class */
$service_class = $service_definition->getClass();
- $config = $service_class::getDefaults($config);
- $config = $service_class::askPreBootQuestions($helper, $input, $output, $config);
+ $config = $service_class::getDefaults($config, $this->container);
+ $config = $service_class::askPreBootQuestions($helper, $input, $output, $config, $this->container);
}
$this->container->setParameter('config', $config);
// Now we can attempt boot.
foreach ($this->configurableServiceIds as $service_id) {
- /** @var ConfigurableService $service */
+ /** @var \Larowlan\Tl\Configuration\ConfigurableService $service */
$service = $this->container->get($service_id);
$config = $service->askPostBootQuestions($helper, $input, $output, $config);
}
@@ -79,4 +77,5 @@ protected function execute(InputInterface $input, OutputInterface $output) {
public function setContainerBuilder(ContainerBuilder $container) {
$this->container = $container;
}
+
}
diff --git a/src/Commands/ContainerAwareCommand.php b/src/Commands/ContainerAwareCommand.php
index eb5d907..1629ebf 100644
--- a/src/Commands/ContainerAwareCommand.php
+++ b/src/Commands/ContainerAwareCommand.php
@@ -1,8 +1,4 @@
connector = $connector;
$this->repository = $repository;
@@ -55,8 +57,8 @@ protected function execute(InputInterface $input, OutputInterface $output) {
$slot = $this->repository->latest();
}
if ($slot) {
- $details = $this->connector->ticketDetails($slot->tid);
- list($slot_id, $continued) = $this->repository->start($slot->tid, $slot->comment, $slot->id);
+ $details = $this->connector->ticketDetails($slot->tid, $slot->connector_id);
+ list($slot_id, $continued) = $this->repository->start($slot->tid, $slot->connector_id, $slot->comment, $slot->id);
$output->writeln(sprintf('[%s]> %s entry for %d: %s [slot:%d]',
(new \DateTime())->format('h:i'),
$continued ? 'Continued' : 'Started new',
diff --git a/src/Commands/Delete.php b/src/Commands/Delete.php
index ca3ff36..2f3cbb7 100644
--- a/src/Commands/Delete.php
+++ b/src/Commands/Delete.php
@@ -1,8 +1,4 @@
connector = $connector;
$this->repository = $repository;
@@ -54,11 +56,11 @@ protected function configure() {
protected function execute(InputInterface $input, OutputInterface $output) {
$slot_id = $input->getArgument('slot_id');
$helper = $this->getHelper('question');
- $question = new ConfirmationQuestion('Are you sure?', false);
+ $question = new ConfirmationQuestion('Are you sure?', FALSE);
$confirm = NULL;
if (($slot = $this->repository->slot($slot_id)) && ($confirm = ($input->getOption('confirm') || $helper->ask($input, $output, $question))) && $this->repository->delete($slot_id)) {
- $deleted = $this->connector->ticketDetails($slot->tid);
+ $deleted = $this->connector->ticketDetails($slot->tid, $slot->connector_id);
$output->writeln(sprintf('Deleted slot %d against ticket %d: %s, duration %s',
$slot->id,
$slot->tid,
diff --git a/src/Commands/Edit.php b/src/Commands/Edit.php
index 4edba35..a6c7181 100644
--- a/src/Commands/Edit.php
+++ b/src/Commands/Edit.php
@@ -1,20 +1,17 @@
connector = $connector;
$this->repository = $repository;
diff --git a/src/Commands/Install.php b/src/Commands/Install.php
index 6fc1e24..e546205 100644
--- a/src/Commands/Install.php
+++ b/src/Commands/Install.php
@@ -1,12 +1,7 @@
connection = $connection;
$this->directory = $directory;
diff --git a/src/Commands/Log.php b/src/Commands/Log.php
index c411f7d..403e5c6 100644
--- a/src/Commands/Log.php
+++ b/src/Commands/Log.php
@@ -1,8 +1,4 @@
directory = $directory;
parent::__construct();
diff --git a/src/Commands/LogAwareCommand.php b/src/Commands/LogAwareCommand.php
index 5ec5d8e..dda014e 100644
--- a/src/Commands/LogAwareCommand.php
+++ b/src/Commands/LogAwareCommand.php
@@ -1,8 +1,4 @@
connector = $connector;
$this->repository = $repository;
@@ -59,7 +54,9 @@ protected function execute(InputInterface $input, OutputInterface $output) {
$rows = [];
foreach ($entries as $entry) {
- $details = $this->connector->ticketDetails($entry->tid);
+ if (!$details = $this->connector->ticketDetails($entry->tid, $entry->connector_id)) {
+ continue;
+ }
$rows[] = [$entry->tid, $details->getTitle()];
}
$table->setRows($rows);
diff --git a/src/Commands/Open.php b/src/Commands/Open.php
index 276d6ba..e6f6144 100644
--- a/src/Commands/Open.php
+++ b/src/Commands/Open.php
@@ -1,20 +1,17 @@
connector = $connector;
$this->repository = $repository;
@@ -48,7 +48,7 @@ protected function configure() {
*/
protected function execute(InputInterface $input, OutputInterface $output) {
if ($data = $this->repository->getActive()) {
- $details = $this->connector->ticketDetails($data->tid);
+ $details = $this->connector->ticketDetails($data->tid, $data->connector_id);
$output->writeLn(sprintf('%s [%d] - %s [slot: %d]',
$details->getTitle(),
$data->tid,
diff --git a/src/Commands/PreinstallCommand.php b/src/Commands/PreinstallCommand.php
index 468c155..d171ad7 100644
--- a/src/Commands/PreinstallCommand.php
+++ b/src/Commands/PreinstallCommand.php
@@ -1,8 +1,4 @@
reviewer = $reviewer;
parent::__construct();
diff --git a/src/Commands/Send.php b/src/Commands/Send.php
index 1fc0f5f..1e46542 100644
--- a/src/Commands/Send.php
+++ b/src/Commands/Send.php
@@ -1,8 +1,4 @@
connector = $connector;
$this->repository = $repository;
diff --git a/src/Commands/Start.php b/src/Commands/Start.php
index 6bc1089..67143b6 100644
--- a/src/Commands/Start.php
+++ b/src/Commands/Start.php
@@ -1,13 +1,9 @@
connector = $connector;
$this->repository = $repository;
parent::__construct();
@@ -48,6 +50,7 @@ protected function configure() {
->addArgument('comment', InputArgument::OPTIONAL, 'Comment to start with')
->addOption('status', 's', InputOption::VALUE_NONE, 'Set issue to in progress')
->addOption('assign', 'a', InputOption::VALUE_NONE, 'Assign issue to you')
+ ->addOption('backend', 'b', InputOption::VALUE_OPTIONAL, 'Backend to use')
->addOption('redmine-comment', 'r', InputOption::VALUE_REQUIRED, 'Redmine comment')
->addUsage('tl start 12355')
->addUsage('tl start 12355 "Doin stuff"')
@@ -70,9 +73,21 @@ protected function execute(InputInterface $input, OutputInterface $output) {
if ($alias = $this->repository->loadAlias($ticket_id)) {
$ticket_id = $alias;
}
- if ($title = $this->connector->ticketDetails($ticket_id)) {
+ if ($connector_id = $input->getOption('backend')) {
+ $connector_id = 'connector.' . $connector_id;
+ }
+ else {
+ $connector_id = $this->connector->spotConnector($ticket_id, $input, $output);
+ }
+ if (!$connector_id) {
+ throw new \InvalidArgumentException('No such ticket was found in any backends.');
+ }
+ if ($alias = $this->connector->loadAlias($ticket_id, $connector_id)) {
+ $ticket_id = $alias;
+ }
+ if ($title = $this->connector->ticketDetails($ticket_id, $connector_id)) {
if ($stop = $this->repository->stop()) {
- $stopped = $this->connector->ticketDetails($stop->tid);
+ $stopped = $this->connector->ticketDetails($stop->tid, $stop->connector_id);
$output->writeln(sprintf('Closed slot %d against ticket %d: %s, duration %s',
$stop->id,
$stop->tid,
@@ -81,7 +96,7 @@ protected function execute(InputInterface $input, OutputInterface $output) {
));
}
try {
- list($slot_id, $continued) = $this->repository->start($ticket_id, $input->getArgument('comment'));
+ list($slot_id, $continued) = $this->repository->start($ticket_id, $connector_id, $input->getArgument('comment'));
$output->writeln(sprintf('[%s]> %s entry for %d: %s [slot:%d]',
(new \DateTime())->format('h:i'),
$continued ? 'Continued' : 'Started new',
@@ -90,7 +105,7 @@ protected function execute(InputInterface $input, OutputInterface $output) {
$slot_id
));
if ($input->getOption('status')) {
- if ($this->connector->setInProgress($ticket_id, $assign = $input->getOption('assign'), $input->getOption('redmine-comment') ?: 'Working on this')) {
+ if ($this->connector->setInProgress($ticket_id, $connector_id, $assign = $input->getOption('assign'), $input->getOption('redmine-comment') ?: 'Working on this')) {
$output->writeln(sprintf('Ticket %s set to in-progress.', $ticket_id));
if ($assign) {
$output->writeln(sprintf('Ticket %s assigned to you.', $ticket_id));
@@ -104,7 +119,7 @@ protected function execute(InputInterface $input, OutputInterface $output) {
}
}
elseif ($input->getOption('assign')) {
- if ($this->connector->assign($ticket_id, $input->getOption('redmine-comment') ?: 'Working on this')) {
+ if ($this->connector->assign($ticket_id, $connector_id, $input->getOption('redmine-comment') ?: 'Working on this')) {
$output->writeln(sprintf('Ticket %s assigned to you.',
$ticket_id));
}
@@ -118,7 +133,7 @@ protected function execute(InputInterface $input, OutputInterface $output) {
}
}
catch (\Exception $e) {
- $output->writeln(sprintf('Error creating slot: %s', $e->getMessage()));
+ $output->writeln(sprintf('Error creating slot: %s', $e->getMessage()));
}
}
else {
diff --git a/src/Commands/Status.php b/src/Commands/Status.php
index a74b174..21205e8 100644
--- a/src/Commands/Status.php
+++ b/src/Commands/Status.php
@@ -1,12 +1,7 @@
connector = $connector;
$this->repository = $repository;
@@ -64,7 +65,7 @@ protected function execute(InputInterface $input, OutputInterface $output) {
foreach ($data as $record) {
$record->duration = ($record->end ?: time()) - $record->start;
$total += $record->duration;
- $details = $this->connector->ticketDetails($record->tid);
+ $details = $this->connector->ticketDetails($record->tid, $record->connector_id);
if (empty($record->end)) {
$record->tid .= ' *';
}
diff --git a/src/Commands/Stop.php b/src/Commands/Stop.php
index f16f5ae..a2575a0 100644
--- a/src/Commands/Stop.php
+++ b/src/Commands/Stop.php
@@ -1,21 +1,18 @@
connector = $connector;
$this->repository = $repository;
@@ -54,7 +54,7 @@ protected function configure() {
*/
protected function execute(InputInterface $input, OutputInterface $output) {
if ($stop = $this->repository->stop()) {
- $stopped = $this->connector->ticketDetails($stop->tid);
+ $stopped = $this->connector->ticketDetails($stop->tid, $stop->connector_id);
$output->writeln(sprintf('[%s]> Closed slot %d against ticket %d: %s, duration %s',
(new \DateTime())->format('h:i'),
$stop->id,
@@ -63,7 +63,7 @@ protected function execute(InputInterface $input, OutputInterface $output) {
Formatter::formatDuration($stop->duration)
));
if (($comment = $input->getOption('comment')) || $input->getOption('pause')) {
- if ($this->connector->pause($stop->tid, $comment)) {
+ if ($this->connector->pause($stop->tid, $comment, $stop->connector_id)) {
$output->writeln(sprintf('Ticket %s set to paused.', $stop->tid));
}
else {
diff --git a/src/Commands/Tag.php b/src/Commands/Tag.php
index 6d7be7c..0637e78 100644
--- a/src/Commands/Tag.php
+++ b/src/Commands/Tag.php
@@ -1,12 +1,7 @@
connector = $connector;
$this->repository = $repository;
@@ -78,28 +78,30 @@ protected function tagAll(InputInterface $input, OutputInterface $output) {
$last = FALSE;
try {
$entries = $this->repository->review(Review::ALL, TRUE);
- $categories = $this->connector->fetchCategories();
+ $grouped_categories = $this->connector->fetchCategories();
}
catch (ConnectException $e) {
$output->writeln('You are offline, please try again later.');
return;
}
foreach ($entries as $entry) {
+ $categories = $grouped_categories[$entry->connector_id];
if ($entry->category && !$input->getOption('retag')) {
continue;
}
- $title = $this->connector->ticketDetails($entry->tid);
+ $title = $this->connector->ticketDetails($entry->tid, $entry->connector_id);
+ $default = reset($categories);
$question = new ChoiceQuestion(
sprintf('Enter tag for slot %d [%d]: %s [%s h] [%s] %s',
$entry->id,
$entry->tid,
$title->getTitle(),
$entry->duration,
- $last ?: static::DEFAULT_TAG,
- $entry->comment ? '- "' . $entry->comment . '"': ''
+ $last ?: $default,
+ $entry->comment ? '- "' . $entry->comment . '"' : ''
),
$categories,
- $last ?: static::DEFAULT_TAG
+ $last ?: $default
);
$tag_id = $helper->ask($input, $output, $question);
$tag = $categories[$tag_id];
@@ -123,23 +125,26 @@ protected function tagOne(InputInterface $input, OutputInterface $output, $slot_
if ($entry = $this->repository->slot($slot_id)) {
$helper = $this->getHelper('question');
try {
- $title = $this->connector->ticketDetails($entry->tid);
- $categories = $this->connector->fetchCategories();
- } catch (ConnectException $e) {
+ $title = $this->connector->ticketDetails($entry->tid, $entry->connector_id);
+ $grouped_categories = $this->connector->fetchCategories();
+ $categories = $grouped_categories[$entry->connector_id];
+ }
+ catch (ConnectException $e) {
$output->writeln('You are offline, please try again later.');
return;
}
+ $default = reset($categories);
$question = new ChoiceQuestion(
sprintf('Enter tag for slot %d [%d]: %s [%s h] [%s] %s',
$entry->id,
$entry->tid,
$title->getTitle(),
Formatter::formatDuration($entry->end - $entry->start),
- static::DEFAULT_TAG,
- $entry->comment ? '- "' . $entry->comment . '"': ''
+ $default,
+ $entry->comment ? '- "' . $entry->comment . '"' : ''
),
$categories,
- static::DEFAULT_TAG
+ $default
);
$tag_id = $helper->ask($input, $output, $question);
$tag = $categories[$tag_id];
diff --git a/src/Commands/TagAll.php b/src/Commands/TagAll.php
index 6ac9296..2a0c34a 100644
--- a/src/Commands/TagAll.php
+++ b/src/Commands/TagAll.php
@@ -1,23 +1,18 @@
connector = $connector;
$this->repository = $repository;
@@ -52,27 +50,31 @@ protected function configure() {
*/
protected function execute(InputInterface $input, OutputInterface $output) {
$entries = $this->repository->review(Review::ALL, TRUE);
+ $connector_ids = array_unique(array_map(function ($entry) {
+ return $entry->connector_id;
+ }, $entries));
$helper = $this->getHelper('question');
- $last = FALSE;
$categories = $this->connector->fetchCategories();
- $question = new ChoiceQuestion(
- sprintf('Select tag to use:[%s]',
- $last ?: Tag::DEFAULT_TAG
- ),
- $categories,
- $last ?: Tag::DEFAULT_TAG
- );
- $tag_id = $helper->ask($input, $output, $question);
- $tag = $categories[$tag_id];
- list(, $tag) = explode(':', $tag);
+ $tags = [];
+ foreach ($connector_ids as $connector_id) {
+ $question = new ChoiceQuestion(
+ sprintf('Select tag to use for %s tickets', Manager::formatConnectorId($connector_id)),
+ $categories[$connector_id]
+ );
+ $tag_id = $helper->ask($input, $output, $question);
+ $tag = $categories[$connector_id][$tag_id];
+ list(, $tag) = explode(':', $tag);
+ $tags[$connector_id] = $tag;
+ }
+ $tagged = FALSE;
foreach ($entries as $entry) {
if ($entry->category) {
continue;
}
- $this->repository->tag($tag, $entry->id);
- $last = $tag;
+ $this->repository->tag($tags[$entry->connector_id], $entry->id);
+ $tagged = TRUE;
}
- if (!$last) {
+ if (!$tagged) {
$output->writeln('All items already tagged, use --retag to retag');
}
}
diff --git a/src/Commands/Total.php b/src/Commands/Total.php
index 1e03103..7ff65c8 100644
--- a/src/Commands/Total.php
+++ b/src/Commands/Total.php
@@ -1,19 +1,15 @@
reviewer = $reviewer;
parent::__construct();
diff --git a/src/Commands/Update.php b/src/Commands/Update.php
index 26368c0..35263ce 100644
--- a/src/Commands/Update.php
+++ b/src/Commands/Update.php
@@ -1,8 +1,4 @@
setName('self-update')
- ->setDescription('Updates tl to the latest version from larowlan.github.io')
- ;
+ ->setDescription('Updates tl to the latest version from larowlan.github.io');
}
- protected function execute(InputInterface $input, OutputInterface $output)
- {
+ /**
+ *
+ */
+ protected function execute(InputInterface $input, OutputInterface $output) {
$manager = new Manager(Manifest::loadFile(self::MANIFEST_FILE));
- $manager->update($this->getApplication()->getVersion(), false, true);
+ $manager->update($this->getApplication()->getVersion(), FALSE, TRUE);
}
+
}
diff --git a/src/Commands/Visit.php b/src/Commands/Visit.php
index 1fadbb4..a428057 100644
--- a/src/Commands/Visit.php
+++ b/src/Commands/Visit.php
@@ -1,20 +1,17 @@
connector = $connector;
$this->repository = $repository;
parent::__construct();
@@ -59,7 +59,7 @@ protected function execute(InputInterface $input, OutputInterface $output) {
$output->writeln('No active ticket, please use tl visit {ticket_id} to specifiy a ticket.');
return;
}
- $url = $this->connector->ticketUrl($issue_number);
+ $url = $this->connector->ticketUrl($issue_number, isset($data) ? $data->connector_id : $this->getConnector($input, $output, $issue_number));
$this->open($url, $output);
}
@@ -94,4 +94,27 @@ protected function open($url, OutputInterface $output) {
}
}
+ /**
+ * Gets connector ID.
+ * @param \Symfony\Component\Console\Input\InputInterface $input
+ * Input.
+ * @param \Symfony\Component\Console\Output\OutputInterface $output
+ * Output.
+ * @param mixed $issue_number
+ * Issue number.
+ *
+ * @return string
+ * Connector ID.
+ *
+ * @throws \InvalidArgumentException
+ * When no such ticket exists.
+ */
+ protected function getConnector(InputInterface $input, OutputInterface $output, $issue_number) {
+ $connector_id = $this->connector->spotConnector($issue_number, $input, $output);
+ if (!$connector_id) {
+ throw new \InvalidArgumentException('No such ticket was found in any backends.');
+ }
+ return $connector_id;
+ }
+
}
diff --git a/src/Configuration/ConfigurableService.php b/src/Configuration/ConfigurableService.php
index 1104c99..a268911 100644
--- a/src/Configuration/ConfigurableService.php
+++ b/src/Configuration/ConfigurableService.php
@@ -1,8 +1,4 @@
root('tl');
foreach ($this->services as $service) {
- $service::getConfiguration($root);
+ $service::getConfiguration($root, $this->container);
}
return $tree;
}
@@ -37,4 +45,11 @@ public function setConfigurableServices(array $class_names) {
$this->services = $class_names;
}
+ /**
+ * {@inheritdoc}
+ */
+ public function setContainerBuilder(ContainerBuilder $container) {
+ $this->container = $container;
+ }
+
}
diff --git a/src/Connector/Connector.php b/src/Connector/Connector.php
index bba3764..e65e939 100644
--- a/src/Connector/Connector.php
+++ b/src/Connector/Connector.php
@@ -1,26 +1,32 @@
$configuration['jira_url'],
+ 'jiraUser' => $configuration['jira_username'],
+ 'jiraPassword' => $configuration['jira_api_token'],
+ ]);
+ $this->issueService = new IssueService($arrayConfiguration);
+ $this->projectService = new ProjectService($arrayConfiguration);
+ $this->cache = $cache;
+ $this->userName = $configuration['jira_username'];
+ $this->version = $version;
+ $this->httpClient = $httpClient;
+ $this->nonBillableProjects = array_map(function ($item) {
+ return (int) $item;
+ }, $config['jira_non_billable_projects'] ?? []);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function getConfiguration(NodeDefinition $root_node, ContainerBuilder $container) {
+ $root_node->children()
+ ->scalarNode('jira_username')
+ ->defaultValue('')
+ ->end()
+ ->scalarNode('jira_api_token')
+ ->defaultValue('')
+ ->end()
+ ->scalarNode('jira_url')
+ ->defaultValue(self::JIRA_URL)
+ ->end()
+ ->arrayNode('jira_non_billable_projects')
+ ->requiresAtLeastOneElement()
+ ->prototype('scalar')
+ ->end()
+ ->end()
+ ->end();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function askPreBootQuestions(QuestionHelper $helper, InputInterface $input, OutputInterface $output, array $config, ContainerBuilder $container) {
+ $default_url = isset($config['jira_url']) ? $config['jira_url'] : self::JIRA_URL;
+ $default_key = isset($config['jira_api_token']) ? $config['jira_api_token'] : '';
+ $default_username = isset($config['jira_username']) ? $config['jira_username'] : '';
+ // Reset.
+ $config = [
+ 'jira_url' => '',
+ 'jira_api_token' => '',
+ 'jira_username' => '',
+ ] + $config;
+ $question = new Question(sprintf('Enter your Jira URL: [%s]', $default_url), $default_url);
+ $config['jira_url'] = $helper->ask($input, $output, $question) ?: $default_url;
+ if (strpos($config['jira_url'], 'https') !== 0) {
+ $output->writeln('It is recommended to use https, POSTING over http is not supported');
+ }
+ $question = new Question(sprintf('Enter your Jira username: [%s]', $default_username), $default_username);
+ $config['jira_username'] = $helper->ask($input, $output, $question) ?: $default_username;
+ $question = new Question(sprintf('Enter your Jira API token: [%s]', $default_key), $default_key);
+ $question->setValidator(function ($value) {
+ if (trim($value) == '') {
+ throw new \Exception('The token cannot be empty');
+ }
+
+ return $value;
+ });
+ $question->setHidden(TRUE);
+ $config['jira_api_token'] = $helper->ask($input, $output, $question) ?: $default_key;
+ return $config;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function askPostBootQuestions(QuestionHelper $helper, InputInterface $input, OutputInterface $output, array $config) {
+ $default_non_billable = isset($config['jira_non_billable_projects']) ? $config['jira_non_billable_projects'] : [];
+ // Reset.
+ $config = ['jira_non_billable_projects' => []] + $config;
+ try {
+ $output->writeln('Bear with us while we configure which Jira projects are non billable');
+ $options = $this->projectNames();
+ }
+ catch (JiraException $e) {
+ $output->writeln('Could not connect to backend, please check your API key and that you are online');
+ return $config;
+ }
+ foreach ($options as $id => $project) {
+ $default = in_array($id, $default_non_billable);
+ $question = new ConfirmationQuestion(sprintf('Is the %s project non billable?[%s/%s]', $project, $default ? 'Y' : 'y', $default ? 'n' : 'N'), $default);
+ if ($helper->ask($input, $output, $question)) {
+ $config['jira_non_billable_projects'][] = $id;
+ }
+ }
+ return $config;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function getDefaults($config, ContainerBuilder $container) {
+ return $config = [
+ 'jira_url' => static::JIRA_URL,
+ 'jira_api_token' => '',
+ 'jira_username' => '',
+ ];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function getName() {
+ return 'Jira';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function ticketDetails($id, $connectorId) {
+ try {
+ $issue = $this->issueService->get($id);
+ }
+ catch (JiraException $e) {
+ try {
+ // Check if we're offline.
+ $this->httpClient->request('GET', self::JIRA_URL);
+ }
+ catch (ConnectException $e) {
+ return new Ticket(
+ 'Offline: please try again later',
+ 'Offline',
+ TRUE
+ );
+ }
+ return FALSE;
+ }
+ return new Ticket(sprintf('[%s] %s', $issue->key, $issue->fields->summary), $issue->fields->getProjectId(), !in_array((int) $issue->fields->getProjectId(), $this->nonBillableProjects, TRUE));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function loadAlias($ticket_id, $connectorId) {
+ if (($details = $this->cache->fetch($this->version . ':alias:' . $ticket_id))) {
+ return $details;
+ }
+ try {
+ $issue = $this->issueService->get($ticket_id);
+ }
+ catch (\Exception $e) {
+ return $ticket_id;
+ }
+
+ $this->cache->save($this->version . ':alias:' . $ticket_id, $issue->id, Manager::LIFETIME);
+ return $issue->id;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function fetchCategories() {
+ // Jira doesn't require time log entries to be classified.
+ return ['Work:1' => 'Work:1'];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sendEntry($entry) {
+ if ((float) $entry->duration == 0) {
+ // Zero time after rounding.
+ // Return 0 to ensure doesn't send again.
+ return 0;
+ }
+ $worklog = new Worklog();
+
+ $worklog->setComment($entry->comment)
+ ->setStarted(date('Y-m-d h:m:s', $entry->start))
+ ->setTimeSpent(sprintf('%sh %sm', floor($entry->duration), ($entry->duration - floor($entry->duration)) * 60));
+ $ret = $this->issueService->addWorklog($entry->tid, $worklog);
+ return $ret->id;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function ticketUrl($id, $connectorId) {
+ $id = $this->loadAlias($id, $connectorId);
+ $issue = $this->issueService->get($id);
+ return sprintf('%s/browse/%s', self::JIRA_URL, $issue->key);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function assigned($user) {
+ $search = $this->issueService->search('assignee = currentUser() and status not in (Resolved, closed, Done)', 0, 25);
+ $results = [];
+ foreach ($search->getIssues() as $issue) {
+ $results += [$issue->fields->project->name => []];
+ $results[$issue->fields->project->name][$issue->id] = [
+ 'title' => sprintf('[%s] %s', $issue->key, $issue->fields->summary),
+ 'status' => $issue->fields->status->name,
+ ];
+ }
+ return $results;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setInProgress($ticket_id, $connectorId, $assign = FALSE, $comment = 'Working on this') {
+ $transition = new Transition();
+ $transition->setTransitionName('In Progress');
+ $transition->setCommentBody($comment);
+ $this->issueService->transition($ticket_id, $transition);
+ if ($assign) {
+ $this->issueService->changeAssignee($ticket_id, $this->userName);
+ }
+ return TRUE;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function assign($ticket_id, $connectorId, $comment = 'Working on this') {
+ $this->issueService->changeAssignee($ticket_id, $this->userName);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function pause($ticket_id, $comment, $connectorId) {
+ // No paused status.
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function projectNames() {
+ $cid = 'jira-projects';
+ if (($details = $this->cache->fetch($this->version . ':' . $cid))) {
+ return $details;
+ }
+ $projects = $this->projectService->getAllProjects();
+ $return = [];
+ foreach ($projects as $project) {
+ $return[$project->id] = $project->name;
+ }
+ $this->cache->save($this->version . ':' . $cid, $return, Manager::LIFETIME * 4);
+ return $return;
+ }
+
+}
diff --git a/src/Connector/Manager.php b/src/Connector/Manager.php
new file mode 100644
index 0000000..2060e65
--- /dev/null
+++ b/src/Connector/Manager.php
@@ -0,0 +1,283 @@
+connectors[$id] = $container->get($id);
+ };
+ }
+ $this->cache = $cache;
+ $this->version = $version;
+ }
+
+ /**
+ * Loads the given connector.
+ *
+ * @param string $connector_id
+ * Connector ID.
+ *
+ * @return \Larowlan\Tl\Connector\Connector
+ * Connector.
+ */
+ protected function connector($connector_id) {
+ if (!$this->connectors[$connector_id]) {
+ throw new \InvalidArgumentException('No such backend connector');
+ }
+ return $this->connectors[$connector_id];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function spotConnector($id, InputInterface $input, OutputInterface $output) {
+ if ((!$backends = $this->cache->fetch($this->version . ':resolve_connector:' . $id))) {
+ // Ask each connector.
+ $backends = [];
+ foreach ($this->connectors as $connector_id => $connector) {
+ if ($connector->ticketDetails($id, $connector_id)) {
+ list(, $connector_id) = explode('.', $connector_id);
+ $backends[$connector_id] = call_user_func([get_class($connector), 'getName']);
+ }
+ }
+ // Cache result.
+ $this->cache->save($this->version . ':resolve_connector:' . $id, $backends, static::LIFETIME);
+ }
+ if (!$backends) {
+ return FALSE;
+ }
+ if (count($backends) == 1) {
+ return 'connector.' . key($backends);
+ }
+ // Ask for backend.
+ $question = new ChoiceQuestion(
+ 'The given ticket ID is found in more than one backend - which backend do you want to use',
+ $backends,
+ key($backends)
+ );
+ $helper = new QuestionHelper();
+ return 'connector.' . $helper->ask($input, $output, $question);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function ticketDetails($id, $connectorId) {
+ if (($details = $this->cache->fetch($this->version . ':' . $connectorId . ':' . $id))) {
+ return $details;
+ }
+ $ticket = $this->connector($connectorId)->ticketDetails($id, $connectorId);
+ $this->cache->save($this->version . ':' . $connectorId . ':' . $id, $ticket, static::LIFETIME);
+ return $ticket;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function fetchCategories() {
+ $categories = [];
+ foreach ($this->connectors as $id => $connector) {
+ $categories[$id] = $connector->fetchCategories();
+ }
+ return $categories;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function sendEntry($entry) {
+ return $this->connector($entry->connector_id)->sendEntry($entry);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function ticketUrl($id, $connectorId) {
+ return $this->connector($connectorId)->ticketUrl($id, $connectorId);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function assigned($user) {
+ $assigned = [];
+ foreach ($this->connectors as $id => $connector) {
+ $name = call_user_func([get_class($connector), 'getName']);
+ $assigned[$name] = $connector->assigned($user);
+ }
+ return $assigned;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setInProgress($ticket_id, $connectorId, $assign = FALSE, $comment = 'Working on this') {
+ return $this->connector($connectorId)->setInProgress($ticket_id, $connectorId, $assign, $comment);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function assign($ticket_id, $connectorId, $comment = 'Working on this') {
+ return $this->connector($connectorId)->assign($ticket_id, $connectorId, $comment);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function pause($ticket_id, $comment, $connectorId) {
+ return $this->connector($connectorId)->pause($ticket_id, $comment, $connectorId);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function projectNames() {
+ $projectNames = [];
+ foreach ($this->connectors as $id => $connector) {
+ $projectNames[$id] = $connector->projectNames();
+ }
+ return $projectNames;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function loadAlias($ticket_id, $connectorId) {
+ return $this->connector($connectorId)->loadAlias($ticket_id, $connectorId);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function getConfiguration(NodeDefinition $root_node, ContainerBuilder $container) {
+ foreach (array_keys($container->findTaggedServiceIds('connector')) as $id) {
+ $definition = $container->getDefinition($id);
+ $class = $definition->getClass();
+ $class::getConfiguration($root_node, $container);
+ }
+ $root_node->children()
+ ->arrayNode('connector_ids')
+ ->requiresAtLeastOneElement()
+ ->prototype('scalar')
+ ->end()
+ ->end()
+ ->end();
+ return $root_node;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function askPreBootQuestions(QuestionHelper $helper, InputInterface $input, OutputInterface $output, array $config, ContainerBuilder $container) {
+ $connectorIds = array_keys($container->findTaggedServiceIds('connector'));
+ $activeIds = [];
+ foreach ($connectorIds as $id) {
+ $definition = $container->getDefinition($id);
+ $class = $definition->getClass();
+ $name = call_user_func([$class, 'getName']);
+ $default = in_array($id, $config['connector_ids']);
+ $question = new ConfirmationQuestion(sprintf('Do you want to use the %s backend?[%s/%s]', $name, $default ? 'Y' : 'y', $default ? 'n' : 'N'), $default);
+ if ($helper->ask($input, $output, $question)) {
+ $activeIds[] = $id;
+ $config = $class::askPreBootQuestions($helper, $input, $output, $config, $container) + $config;
+ }
+ }
+ if (empty($activeIds)) {
+ throw new InvalidConfigurationException('You must select at least one backend');
+ }
+ $config['connector_ids'] = $activeIds;
+ return $config;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function askPostBootQuestions(QuestionHelper $helper, InputInterface $input, OutputInterface $output, array $config) {
+ foreach ($this->connectors as $connector) {
+ $config = $connector->askPostBootQuestions($helper, $input, $output, $config);
+ }
+ return $config;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function getDefaults($config, ContainerBuilder $container) {
+ foreach (array_keys($container->findTaggedServiceIds('connector')) as $id) {
+ $definition = $container->getDefinition($id);
+ $class = $definition->getClass();
+ $class::getDefaults($config, $container);
+ }
+ $config['connector_ids'] = [];
+ return $config;
+ }
+
+ /**
+ * Format conenctor id.
+ *
+ * @param string $connector_id
+ * Connector ID in connector.{id} format.
+ *
+ * @return string
+ * Connector ID with 'connector.' prefix stripped.
+ */
+ public static function formatConnectorId($connector_id) {
+ if (strpos($connector_id, 'connector.') !== 0) {
+ return $connector_id;
+ }
+ return explode('.', $connector_id)[1];
+ }
+
+}
diff --git a/src/Connector/RedmineConnector.php b/src/Connector/RedmineConnector.php
index 2a4ce8f..d1c0fb2 100644
--- a/src/Connector/RedmineConnector.php
+++ b/src/Connector/RedmineConnector.php
@@ -1,10 +1,5 @@
httpClient = $httpClient;
- $this->cache = $cache;
$this->url = $config['url'];
$this->apiKey = $config['api_key'];
- $this->nonBillableProjects = isset($config['non_billable_projects']) ? $config['non_billable_projects'] : [];
+ $this->nonBillableProjects = array_map(function ($item) {
+ return (int) $item;
+ }, $config['non_billable_projects'] ?? []);
+ $this->cache = $cache;
$this->version = $version;
}
/**
* {@inheritdoc}
*/
- public function ticketDetails($id) {
- if (($details = $this->cache->fetch($this->version . ':' . $id))) {
- return $details;
- }
- // We need to fetch it.
+ public function loadAlias($ticket_id, $connectorId) {
+ // Not supported.
+ return $ticket_id;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function ticketDetails($id, $connectorId) {
$url = $this->url . '/issues/' . $id . '.xml';
try {
if ($xml = $this->fetch($url, $this->apiKey)) {
@@ -68,8 +72,6 @@ public function ticketDetails($id) {
(string) $xml->project['id'],
$this->isBillable((string) $xml->project['id'])
);
- $this->cache->save($this->version . ':' . $id, $entry,
- static::LIFETIME);
return $entry;
}
}
@@ -83,6 +85,13 @@ public function ticketDetails($id) {
return FALSE;
}
+ /**
+ * {@inheritdoc}
+ */
+ public static function getName() {
+ return 'Redmine';
+ }
+
/**
* {@inheritdoc}
*/
@@ -115,7 +124,7 @@ public function sendEntry($entry) {
return 0;
}
$url = $this->url . '/time_entries.xml';
- $details = $this->ticketDetails($entry->tid);
+ $details = $this->ticketDetails($entry->tid, $entry->connector_id);
$data = [
'issue_id' => $entry->tid,
'project_id' => $details->getProjectId(),
@@ -194,11 +203,14 @@ protected function fetch($url, $redmine_key) {
/**
* {@inheritdoc}
*/
- public function ticketUrl($id) {
+ public function ticketUrl($id, $connectorId) {
return $this->url . '/issues/' . $id;
}
- public function assigned($user = 'me') {
+ /**
+ * {@inheritdoc}
+ */
+ public function assigned($user) {
$url = $this->url . '/issues.xml?assigned_to_id=' . $user;
$tickets = [];
if ($xml = $this->fetch($url, $this->apiKey)) {
@@ -215,7 +227,7 @@ public function assigned($user = 'me') {
// Sort by status.
foreach ($tickets as $project => &$project_issues) {
- uasort($project_issues, function($a, $b) {
+ uasort($project_issues, function ($a, $b) {
return strcmp($a['status'], $b['status']);
});
}
@@ -224,21 +236,23 @@ public function assigned($user = 'me') {
if ((int) $xml['total_count'] > (int) $xml['limit']) {
$tickets['...']['...'] = [
'title' => sprintf('Showing %s of %s', $xml['limit'], $xml['total_count']),
- 'status' => 'Too many Issues!'
+ 'status' => 'Too many Issues!',
];
}
else {
$tickets['...'][''] = [
'title' => sprintf('Showing %s issues', $xml['total_count']),
- 'status' => ''
+ 'status' => '',
];
}
-
return $tickets;
}
- public function setInProgress($ticket_id, $assign = FALSE, $comment = 'Working on this') {
+ /**
+ * {@inheritdoc}
+ */
+ public function setInProgress($ticket_id, $connectorId, $assign = FALSE, $comment = 'Working on this') {
$states = $this->getStates();
if (!isset($states['In progress'])) {
throw new \Exception('There is no "In progress" status');
@@ -250,10 +264,16 @@ public function setInProgress($ticket_id, $assign = FALSE, $comment = 'Working o
return $this->putUpdate($ticket_id, $updates, $comment);
}
- public function assign($ticket_id, $comment = 'Working on this') {
+ /**
+ * {@inheritdoc}
+ */
+ public function assign($ticket_id, $connectorId, $comment = 'Working on this') {
return $this->putUpdate($ticket_id, ['assigned_to_id' => $this->getUserId()], $comment);
}
+ /**
+ * {@inheritdoc}
+ */
protected function putUpdate($ticket_id, array $updates, $comment = 'Working on this') {
$url = $this->url . '/issues/' . $ticket_id . '.xml';
$xml = new \SimpleXMLElement('');
@@ -280,6 +300,9 @@ protected function putUpdate($ticket_id, array $updates, $comment = 'Working on
return FALSE;
}
+ /**
+ * {@inheritdoc}
+ */
protected function getStates() {
$cid = 'redmine-states';
if (($details = $this->cache->fetch($this->version . ':' . $cid))) {
@@ -299,6 +322,9 @@ protected function getStates() {
}
+ /**
+ * {@inheritdoc}
+ */
protected function getUserId() {
$url = $this->url . '/users/current.xml';
$cid = 'userid';
@@ -307,14 +333,17 @@ protected function getUserId() {
}
if ($xml = $this->fetch($url, $this->apiKey)) {
// Cache permanent.
- $uid = (int)$xml->id;
+ $uid = (int) $xml->id;
$this->cache->save($this->version . ':' . $cid, $uid, 0);
return $uid;
}
throw new \Exception('Could not determine your user ID');
}
- public function pause($ticket_id, $comment) {
+ /**
+ * {@inheritdoc}
+ */
+ public function pause($ticket_id, $comment, $connectorId) {
$states = $this->getStates();
if (!isset($states['Paused'])) {
throw new \Exception('There is no "Paused" status');
@@ -333,34 +362,34 @@ public function pause($ticket_id, $comment) {
* TRUE if billable.
*/
protected function isBillable($project_id) {
- return !in_array($project_id, $this->nonBillableProjects, TRUE);
+ return !in_array((int) $project_id, $this->nonBillableProjects, TRUE);
}
/**
* {@inheritdoc}
*/
- public static function getConfiguration(NodeDefinition $root_node) {
+ public static function getConfiguration(NodeDefinition $root_node, ContainerBuilder $container) {
$root_node->children()
- ->arrayNode('non_billable_projects')
- ->requiresAtLeastOneElement()
- ->prototype('scalar')
- ->end()
- ->end()
- ->scalarNode('api_key')
- ->isRequired()
- ->defaultValue('')
- ->end()
- ->scalarNode('url')
- ->defaultValue('https://redmine.previousnext.com.au')
- ->isRequired()
- ->end()
+ ->arrayNode('non_billable_projects')
+ ->requiresAtLeastOneElement()
+ ->prototype('scalar')
+ ->end()
+ ->end()
+ ->scalarNode('api_key')
+ ->isRequired()
+ ->defaultValue('')
+ ->end()
+ ->scalarNode('url')
+ ->defaultValue('https://redmine.previousnext.com.au')
+ ->isRequired()
+ ->end()
->end();
}
/**
* {@inheritdoc}
*/
- public static function askPreBootQuestions(QuestionHelper $helper, InputInterface $input, OutputInterface $output, array $config) {
+ public static function askPreBootQuestions(QuestionHelper $helper, InputInterface $input, OutputInterface $output, array $config, ContainerBuilder $container) {
$default_url = isset($config['url']) ? $config['url'] : 'https://redmine.previousnext.com.au';
$default_key = isset($config['api_key']) ? $config['api_key'] : '';
// Reset.
@@ -384,6 +413,7 @@ public function askPostBootQuestions(QuestionHelper $helper, InputInterface $inp
$config = ['non_billable_projects' => []] + $config;
try {
$options = $this->projectNames();
+ $output->writeln('Bear with us while we configure which Redmine projects are non billable');
}
catch (ConnectException $e) {
$output->writeln('Could not connect to backend, please check your API key and that you are online');
@@ -393,20 +423,20 @@ public function askPostBootQuestions(QuestionHelper $helper, InputInterface $inp
$output->writeln('An error occured trying to connect to the backend, please check your API key and that you are online');
throw $e;
}
-
- $question = new ChoiceQuestion(sprintf('Select non billable project IDs (separated by ,):'), $options, implode(',', $default_non_billable));
- $question->setMultiselect(TRUE);
- $config['non_billable_projects'] = $helper->ask($input, $output, $question) ?: $default_non_billable;
- $config['non_billable_projects'] = array_map(function($item) {
- return explode('::', $item, 2)[1];
- }, $config['non_billable_projects']);
+ foreach ($options as $id => $project) {
+ $default = in_array($id, $default_non_billable);
+ $question = new ConfirmationQuestion(sprintf('Is the %s project non billable?[%s/%s]', $project, $default ? 'Y' : 'y', $default ? 'n' : 'N'), $default);
+ if ($helper->ask($input, $output, $question)) {
+ $config['non_billable_projects'][] = $id;
+ }
+ }
return $config;
}
/**
* {@inheritdoc}
*/
- public static function getDefaults($config) {
+ public static function getDefaults($config, ContainerBuilder $container) {
if (!$config) {
$config = [];
}
@@ -420,7 +450,7 @@ public static function getDefaults($config) {
public function projectNames() {
$cid = 'redmine-projects';
if (($details = $this->cache->fetch($this->version . ':' . $cid))) {
- return $details;
+ return $details;
}
$options = [];
diff --git a/src/DateHelper.php b/src/DateHelper.php
index 019a3b7..38a6384 100644
--- a/src/DateHelper.php
+++ b/src/DateHelper.php
@@ -1,8 +1,4 @@
output = $output;
$this->log = $log;
diff --git a/src/LogHelper.php b/src/LogHelper.php
index 6fde10d..f43b25e 100644
--- a/src/LogHelper.php
+++ b/src/LogHelper.php
@@ -1,8 +1,4 @@
connection = $connection;
}
+ /**
+ * {@inheritdoc}
+ */
protected function qb() {
return $this->connection()->createQueryBuilder();
}
+
/**
* {@inheritdoc}
*/
@@ -50,6 +55,9 @@ public function stop($slot_id = NULL) {
return FALSE;
}
+ /**
+ * {@inheritdoc}
+ */
public function getActive($slot_id = NULL) {
$q = $this->qb()->select('*')
->from('slots', 's')
@@ -66,6 +74,9 @@ public function getActive($slot_id = NULL) {
return FALSE;
}
+ /**
+ * {@inheritdoc}
+ */
public function latest() {
$q = $this->qb()->select('*')
->from('slots', 's')
@@ -78,9 +89,13 @@ public function latest() {
return FALSE;
}
- public function start($ticket_id, $comment = '', $force_continue = FALSE) {
+ /**
+ * {@inheritdoc}
+ */
+ public function start($ticket_id, $connectorId, $comment = '', $force_continue = FALSE) {
$continue_query = $continue = $this->qb()->select('*')
->from('slots', 's')
+ ->where('s.connector_id = :connector_id')
->where('s.tid = :tid');
if (!$force_continue) {
$continue_query->andWhere('s.comment IS NULL')
@@ -91,6 +106,7 @@ public function start($ticket_id, $comment = '', $force_continue = FALSE) {
->setParameter('id', $force_continue);
}
$continue = $continue_query->setParameter('tid', $ticket_id)
+ ->setParameter('connector_id', $connectorId)
->execute()
->fetch(\PDO::FETCH_OBJ);
if ((!$comment || $force_continue) && $continue) {
@@ -101,21 +117,25 @@ public function start($ticket_id, $comment = '', $force_continue = FALSE) {
->set('end', ':end')
->setParameter(':end', NULL)
->execute();
- return array($continue->id, TRUE);
+ return [$continue->id, TRUE];
}
- $record = array(
+ $record = [
'tid' => $ticket_id,
'start' => $this::requestTime(),
- );
- $params = [];
+ 'connector_id' => ':connector_id',
+ ];
+ $params = [':connector_id' => $connectorId];
if ($comment) {
$record['comment'] = ':comment';
$params[':comment'] = $comment;
}
- return array($this->insert($record, $params), FALSE);
+ return [$this->insert($record, $params), FALSE];
}
+ /**
+ * {@inheritdoc}
+ */
public function insert($slot, $params = []) {
$query = $this->qb()->insert('slots')
->values($slot);
@@ -126,6 +146,9 @@ public function insert($slot, $params = []) {
return $this->connection()->lastInsertId();
}
+ /**
+ * {@inheritdoc}
+ */
public function status($date = NULL) {
if (!$date) {
$stamp = mktime('0', '0');
@@ -133,7 +156,7 @@ public function status($date = NULL) {
else {
$stamp = strtotime($date);
}
- $return = $this->qb()->select('id', 'tid', 'end', 'start')
+ $return = $this->qb()->select('id', 'tid', 'end', 'start', 'connector_id')
->from('slots')
->where('start > :start AND start < :end')
->setParameter(':start', $stamp)
@@ -143,6 +166,9 @@ public function status($date = NULL) {
return $return;
}
+ /**
+ * {@inheritdoc}
+ */
public function review($date = NULL, $check = FALSE) {
if (!$date) {
$stamp = mktime('0', '0');
@@ -150,8 +176,9 @@ public function review($date = NULL, $check = FALSE) {
else {
$stamp = strtotime($date);
}
- $query = $this->qb()->select('end', 'tid', 'category', 'comment', 'id', 'start')
- ->from('slots');
+ $query = $this->qb()
+ ->select('end', 'tid', 'category', 'comment', 'id', 'start', 'connector_id')
+ ->from('slots');
$where = $this->qb()->expr()->andX(
$this->qb()->expr()->isNull('teid'),
$this->qb()->expr()->gt('start', ':stamp')
@@ -172,10 +199,16 @@ public function review($date = NULL, $check = FALSE) {
return $return;
}
+ /**
+ * {@inheritdoc}
+ */
public function send() {
return $this->review('19780101');
}
+ /**
+ * {@inheritdoc}
+ */
public function store($entries) {
foreach ($entries as $tid => $entry_id) {
$this->qb()->update('slots')
@@ -187,6 +220,9 @@ public function store($entries) {
}
}
+ /**
+ * {@inheritdoc}
+ */
public function edit($slot_id, $duration) {
$request_time = $this::requestTime();
return $this->qb()->update('slots')
@@ -197,7 +233,7 @@ public function edit($slot_id, $duration) {
}
/**
- * Wraps REQUEST_TIME constant
+ * Wraps REQUEST_TIME constant.
*
* @return int
* Current request time.
@@ -206,6 +242,9 @@ protected static function requestTime() {
return time();
}
+ /**
+ * {@inheritdoc}
+ */
public function tag($tag_id, $slot_id = NULL) {
$query = $this->qb()->update('slots')
->set('category', ':tag')
@@ -217,6 +256,9 @@ public function tag($tag_id, $slot_id = NULL) {
return $query->execute();
}
+ /**
+ * {@inheritdoc}
+ */
public function comment($slot_id, $comment) {
return $this->qb()->update('slots')
->set('comment', ':comment')
@@ -227,9 +269,12 @@ public function comment($slot_id, $comment) {
->execute();
}
+ /**
+ * {@inheritdoc}
+ */
public function frequent() {
return $this->qb()
- ->select('tid')
+ ->select('tid', 'connector_id')
->from('slots', 's')
->groupBy('tid')
->orderBy('COUNT(*)', 'DESC')
@@ -238,6 +283,9 @@ public function frequent() {
->fetchAll(\PDO::FETCH_OBJ);
}
+ /**
+ * {@inheritdoc}
+ */
public function slot($slot_id) {
return $this->qb()->select('*')
->from('slots')
@@ -247,6 +295,9 @@ public function slot($slot_id) {
->fetch(\PDO::FETCH_OBJ);
}
+ /**
+ * {@inheritdoc}
+ */
public function delete($slot_id) {
return $this->qb()->delete('slots')
->where('id = :id')
@@ -256,10 +307,16 @@ public function delete($slot_id) {
->execute();
}
+ /**
+ * {@inheritdoc}
+ */
protected function connection() {
return $this->connection;
}
+ /**
+ * {@inheritdoc}
+ */
public function addAlias($ticket_id, $alias) {
return $this->qb()->insert('aliases')
->values([
@@ -271,6 +328,9 @@ public function addAlias($ticket_id, $alias) {
->execute();
}
+ /**
+ * {@inheritdoc}
+ */
public function removeAlias($ticket_id, $alias) {
return $this->qb()->delete('aliases')
->where('tid = :ticket_id')
@@ -280,6 +340,9 @@ public function removeAlias($ticket_id, $alias) {
->execute();
}
+ /**
+ * {@inheritdoc}
+ */
public function loadAlias($alias) {
return $this->qb()->select('tid')
->from('aliases')
@@ -289,6 +352,9 @@ public function loadAlias($alias) {
->fetchColumn();
}
+ /**
+ * {@inheritdoc}
+ */
public function listAliases($filter = '') {
$query = $this->qb()->select('alias', 'tid')
->from('aliases');
@@ -302,12 +368,15 @@ public function listAliases($filter = '') {
->fetchAll(\PDO::FETCH_OBJ);
}
+ /**
+ * {@inheritdoc}
+ */
public function totalByTicket($start, $end = NULL) {
if (!$end) {
// Some time in the future.
$end = time() + 86400;
}
- $return = $this->qb()->select('tid', 'end', 'start')
+ $return = $this->qb()->select('tid', 'end', 'start', 'connector_id')
->from('slots')
->where('start > :start AND start < :end')
->setParameter(':start', $start)
@@ -317,10 +386,10 @@ public function totalByTicket($start, $end = NULL) {
$totals = [];
foreach ($return as $row) {
$row->duration = round((($row->end ?: time()) - $row->start) / 900) * 900;
- if (!isset($totals[$row->tid])) {
- $totals[$row->tid] = 0;
+ if (!isset($totals[$row->connector_id][$row->tid])) {
+ $totals[$row->connector_id][$row->tid] = 0;
}
- $totals[$row->tid] += $row->duration;
+ $totals[$row->connector_id][$row->tid] += $row->duration;
}
return $totals;
}
diff --git a/src/Repository/Repository.php b/src/Repository/Repository.php
index 92f607e..12f60a6 100644
--- a/src/Repository/Repository.php
+++ b/src/Repository/Repository.php
@@ -1,12 +1,10 @@
addColumn('teid', 'bigint', ['unsigned' => TRUE])->setNotnull(FALSE);
$slots->addColumn('comment', 'string', ['length' => 255])->setNotnull(FALSE);
$slots->addColumn('category', 'string', ['length' => 255])->setNotnull(FALSE);
+ $slots->addColumn('connector_id', 'string', ['length' => 50])->setDefault('connector.redmine')->setNotnull(FALSE);
$slots->setPrimaryKey(['id']);
$slots->addIndex(['start']);
$slots->addIndex(['end']);
@@ -39,4 +45,5 @@ public function getSchema() {
$aliases->addIndex(['tid']);
return $schema;
}
+
}
diff --git a/src/Reviewer.php b/src/Reviewer.php
index fa04fb2..29de822 100644
--- a/src/Reviewer.php
+++ b/src/Reviewer.php
@@ -1,34 +1,53 @@
connector = $connector;
$this->repository = $repository;
}
+ /**
+ * Gets table headers.
+ *
+ * @param bool $exact
+ * TRUE to include exact column.
+ *
+ * @return array
+ * Headers.
+ */
public static function headers($exact = FALSE) {
$headers = [
'SlotID',
@@ -46,6 +65,22 @@ public static function headers($exact = FALSE) {
return $headers;
}
+ /**
+ * Gets summary of tickets.
+ *
+ * @param int $date
+ * Start date.
+ * @param bool $check
+ * Check if stored remotely already.
+ * @param bool $exact
+ * TRUE to include exact column.
+ *
+ * @return array
+ * Rows.
+ *
+ * @throws \Exception
+ * When all entries already stored and check is TRUE.
+ */
public function getSummary($date = 19780101, $check = FALSE, $exact = FALSE) {
$data = $this->repository->review($date, $check);
if (count($data) == 0 && !$check) {
@@ -63,15 +98,15 @@ public function getSummary($date = 19780101, $check = FALSE, $exact = FALSE) {
$exact_total = 0;
foreach ($data as $record) {
$total += $record->duration;
- $details = $this->connector->ticketDetails($record->tid);
+ $details = $this->connector->ticketDetails($record->tid, $record->connector_id);
$category_id = str_pad($record->category, 3, 0, STR_PAD_LEFT);
$category = '';
if ($record->category) {
if ($offline) {
$category = 'Offline';
}
- elseif (isset($categories[$category_id])) {
- $category = $categories[$category_id];
+ elseif (isset($categories[$record->connector_id][$category_id])) {
+ $category = $categories[$record->connector_id][$category_id];
}
else {
$category = 'Unknown';
@@ -103,7 +138,7 @@ public function getSummary($date = 19780101, $check = FALSE, $exact = FALSE) {
'' . Formatter::formatDuration($exact_total) . '',
'',
'',
- ''
+ '',
];
}
else {
@@ -113,9 +148,10 @@ public function getSummary($date = 19780101, $check = FALSE, $exact = FALSE) {
'' . $total . ' h',
'',
'',
- ''
+ '',
];
}
return $rows;
}
+
}
diff --git a/src/Ticket.php b/src/Ticket.php
index cdf3e95..9a66cfa 100644
--- a/src/Ticket.php
+++ b/src/Ticket.php
@@ -1,25 +1,29 @@
getMockConnector()->expects($this->any())
- ->method('ticketDetails')
- ->with(1234)
- ->willReturn(new Ticket('Running tests', 123));
+ $this->setupConnector();
$output = $this->executeCommand('alias', [
'ticket_id' => 1234,
'alias' => 'pony',
@@ -41,10 +32,7 @@ public function testCreate() {
* @covers ::execute
*/
public function testDelete() {
- $this->getMockConnector()->expects($this->any())
- ->method('ticketDetails')
- ->with(1234)
- ->willReturn(new Ticket('Running tests', 123));
+ $this->setupConnector();
$output = $this->executeCommand('alias', [
'ticket_id' => 1234,
'alias' => 'pony',
@@ -62,10 +50,7 @@ public function testDelete() {
* @covers ::execute
*/
public function testList() {
- $this->getMockConnector()->expects($this->any())
- ->method('ticketDetails')
- ->with(1234)
- ->willReturn(new Ticket('Running tests', 123));
+ $this->setupConnector();
$aliases = [
'some',
'drunk',
diff --git a/tests/Commands/ContinueTest.php b/tests/Commands/ContinueTest.php
index 5bf8ce1..4b77cbe 100644
--- a/tests/Commands/ContinueTest.php
+++ b/tests/Commands/ContinueTest.php
@@ -1,8 +1,4 @@
getMockConnector()->expects($this->any())
->method('ticketDetails')
->willReturnMap([
- ['1', new Ticket('Do something', 1)],
- ['2', new Ticket('Do something else', 2)],
- ['3', new Ticket('Do something more', 3)],
+ ['1', 'connector.redmine', new Ticket('Do something', 1)],
+ ['2', 'connector.redmine', new Ticket('Do something else', 2)],
+ ['3', 'connector.redmine', new Ticket('Do something more', 3)],
]);
+ $this->getMockConnector()->expects($this->any())
+ ->method('spotConnector')
+ ->willReturn('connector.redmine');
$repository = $this->getRepository();
// Five entries for today.
$start = time();
@@ -37,26 +37,30 @@ protected function setUp() {
$this->slotId1 = $repository->insert([
'tid' => 1,
'start' => $start,
- 'end' => $start + 3588 * 7
- ]);
+ 'end' => $start + 3588 * 7,
+ 'connector_id' => ':connector_id',
+ ], [':connector_id' => 'connector.redmine']);
// 1 hr.
$this->slotId2 = $repository->insert([
'tid' => 2,
'start' => $start,
- 'end' => $start + 3600
- ]);
+ 'end' => $start + 3600,
+ 'connector_id' => ':connector_id',
+ ], [':connector_id' => 'connector.redmine']);
// 9 hrs.
$this->slotId3 = $repository->insert([
'tid' => 3,
'start' => $start,
- 'end' => $start + 3600 * 9
- ]);
+ 'end' => $start + 3600 * 9,
+ 'connector_id' => ':connector_id',
+ ], [':connector_id' => 'connector.redmine']);
// Yesterday.
$repository->insert([
'tid' => 3,
'start' => $start - 86400,
- 'end' => $start - 86400 + 3600 * 9
- ]);
+ 'end' => $start - 86400 + 3600 * 9,
+ 'connector_id' => ':connector_id',
+ ], [':connector_id' => 'connector.redmine']);
}
/**
@@ -78,7 +82,7 @@ public function testContinueCommand() {
*/
public function testContinueCommandWithSlotId() {
$this->setUp();
- $result = $this->executeCommand('continue' ,['slot_id' => $this->slotId2]);
+ $result = $this->executeCommand('continue', ['slot_id' => $this->slotId2]);
$this->assertContains('Continued entry for 2: Do something else [slot:' . $this->slotId2 . ']', $result->getDisplay());
$this->assertTicketIsOpen(2);
$this->getRepository()->stop();
diff --git a/tests/Commands/FormatterTest.php b/tests/Commands/FormatterTest.php
index 1d84c15..563dd01 100644
--- a/tests/Commands/FormatterTest.php
+++ b/tests/Commands/FormatterTest.php
@@ -1,17 +1,14 @@
getMockConnector()->expects($this->any())
- ->method('ticketDetails')
- ->with(1234)
- ->willReturn(new Ticket('Running tests', 123));
- $output = new StreamOutput(fopen('php://memory', 'w', false));
+ $this->setupConnector();
+ $output = new StreamOutput(fopen('php://memory', 'w', FALSE));
$command = $this->container->get('app.command.start');
$command->setApplication($this->application);
$this->application->setAutoExit(FALSE);
diff --git a/tests/Commands/ReviewTest.php b/tests/Commands/ReviewTest.php
index a8197c3..3b8e12b 100644
--- a/tests/Commands/ReviewTest.php
+++ b/tests/Commands/ReviewTest.php
@@ -1,8 +1,4 @@
getMockConnector()->expects($this->any())
->method('ticketDetails')
->willReturnMap([
- ['1', new Ticket('Do something', 1)],
- ['2', new Ticket('Do something else ', 2)],
- ['3', new Ticket('Do something more', 3)],
+ ['1', 'connector.redmine', new Ticket('Do something', 1)],
+ ['2', 'connector.redmine', new Ticket('Do something else ', 2)],
+ ['3', 'connector.redmine', new Ticket('Do something more', 3)],
]);
+ $this->getMockConnector()->expects($this->any())
+ ->method('spotConnector')
+ ->willReturn('connector.redmine');
$repository = $this->getRepository();
// Five entries for today.
$start = time();
@@ -34,20 +33,23 @@ protected function setUp() {
$repository->insert([
'tid' => 1,
'start' => $start,
- 'end' => $start + 3588 * 7
- ]);
+ 'end' => $start + 3588 * 7,
+ 'connector_id' => ':connector_id',
+ ], [':connector_id' => 'connector.redmine']);
// 1 hr.
$repository->insert([
'tid' => 2,
'start' => $start,
- 'end' => $start + 3600
- ]);
+ 'end' => $start + 3600,
+ 'connector_id' => ':connector_id',
+ ], [':connector_id' => 'connector.redmine']);
// 3 hrs.
$repository->insert([
'tid' => 3,
'start' => $start,
- 'end' => $start + 3600 * 3
- ]);
+ 'end' => $start + 3600 * 3,
+ 'connector_id' => ':connector_id',
+ ], [':connector_id' => 'connector.redmine']);
}
/**
@@ -67,7 +69,7 @@ public function testReviewCommand() {
*/
public function testReviewExact() {
$this->setUp();
- $result = $this->executeCommand('review' ,['--exact' => TRUE]);
+ $result = $this->executeCommand('review', ['--exact' => TRUE]);
$this->assertContains('10:58:36', $result->getDisplay());
$this->assertContains('6:58:36', $result->getDisplay());
$this->assertContains('3:00:00', $result->getDisplay());
diff --git a/tests/Commands/StartTest.php b/tests/Commands/StartTest.php
index efadf4e..fe1d088 100644
--- a/tests/Commands/StartTest.php
+++ b/tests/Commands/StartTest.php
@@ -1,13 +1,7 @@
getMockConnector()->expects($this->any())
->method('ticketDetails')
- ->with(1234)
+ ->with(1234, 'connector.redmine')
->willReturn(new Ticket('Running tests', 123));
+ $this->getMockConnector()->expects($this->any())
+ ->method('spotConnector')
+ ->willReturn('connector.redmine');
$output = $this->executeCommand('start', ['issue_number' => 1234]);
$this->assertRegExp('/Started new entry for 1234: Running tests/', $output->getDisplay());
$this->assertTicketIsOpen(1234);
@@ -36,11 +33,8 @@ public function testStart() {
* @covers \Larowlan\Tl\Application::doRun
*/
public function testStartLogging() {
- $this->getMockConnector()->expects($this->any())
- ->method('ticketDetails')
- ->with(1234)
- ->willReturn(new Ticket('Running tests', 123));
- $output = new StreamOutput(fopen('php://memory', 'w', false));
+ $this->setupConnector();
+ $output = new StreamOutput(fopen('php://memory', 'w', FALSE));
$command = $this->container->get('app.command.start');
$command->setApplication($this->application);
$this->application->setAutoExit(FALSE);
@@ -63,10 +57,13 @@ public function testStopStart() {
$this->getMockConnector()->expects($this->any())
->method('ticketDetails')
->willReturnMap([
- [1234, new Ticket('Running tests', 123)],
- ["1234", new Ticket('Running tests', 123)],
- [4567, new Ticket('Running more tests', 123)],
+ [1234, 'connector.redmine', new Ticket('Running tests', 123)],
+ ["1234", 'connector.redmine', new Ticket('Running tests', 123)],
+ [4567, 'connector.redmine', new Ticket('Running more tests', 123)],
]);
+ $this->getMockConnector()->expects($this->any())
+ ->method('spotConnector')
+ ->willReturn('connector.redmine');
$output = $this->executeCommand('start', ['issue_number' => 1234]);
$this->assertRegExp('/Started new entry for 1234: Running tests/', $output->getDisplay());
$active = $this->assertTicketIsOpen(1234);
@@ -84,10 +81,7 @@ public function testStopStart() {
* @covers ::execute
*/
public function testStartWithComment() {
- $this->getMockConnector()->expects($this->any())
- ->method('ticketDetails')
- ->with(1234)
- ->willReturn(new Ticket('Running tests', 123));
+ $this->setupConnector();
$output = $this->executeCommand('start', [
'issue_number' => 1234,
'comment' => 'Doing stuff',
@@ -100,10 +94,7 @@ public function testStartWithComment() {
* @covers ::execute
*/
public function testAssign() {
- $this->getMockConnector()->expects($this->any())
- ->method('ticketDetails')
- ->with(1234)
- ->willReturn(new Ticket('Running tests', 123));
+ $this->setupConnector();
$this->getMockConnector()->expects($this->once())
->method('assign')
->with(1234)
@@ -121,10 +112,7 @@ public function testAssign() {
* @covers ::execute
*/
public function testAssignShortSyntax() {
- $this->getMockConnector()->expects($this->any())
- ->method('ticketDetails')
- ->with(1234)
- ->willReturn(new Ticket('Running tests', 123));
+ $this->setupConnector();
$this->getMockConnector()->expects($this->once())
->method('assign')
->with(1234)
@@ -142,10 +130,7 @@ public function testAssignShortSyntax() {
* @covers ::execute
*/
public function testAssignAlreadyAssigned() {
- $this->getMockConnector()->expects($this->any())
- ->method('ticketDetails')
- ->with(1234)
- ->willReturn(new Ticket('Running tests', 123));
+ $this->setupConnector();
$this->getMockConnector()->expects($this->once())
->method('assign')
->with(1234)
@@ -163,17 +148,14 @@ public function testAssignAlreadyAssigned() {
* @covers ::execute
*/
public function testStatus() {
- $this->getMockConnector()->expects($this->any())
- ->method('ticketDetails')
- ->with(1234)
- ->willReturn(new Ticket('Running tests', 123));
+ $this->setupConnector();
$this->getMockConnector()->expects($this->once())
->method('setInProgress')
->with(1234)
->willReturn(TRUE);
$output = $this->executeCommand('start', [
'issue_number' => 1234,
- '--status' => TRUE
+ '--status' => TRUE,
]);
$this->assertRegExp('/Started new entry for 1234: Running tests/', $output->getDisplay());
$this->assertRegExp('/Ticket 1234 set to in-progress/', $output->getDisplay());
@@ -186,11 +168,14 @@ public function testStatus() {
public function testStatusAndAssign() {
$this->getMockConnector()->expects($this->any())
->method('ticketDetails')
- ->with(1234)
+ ->with(1234, 'connector.redmine')
->willReturn(new Ticket('Running tests', 123));
+ $this->getMockConnector()->expects($this->any())
+ ->method('spotConnector')
+ ->willReturn('connector.redmine');
$this->getMockConnector()->expects($this->once())
->method('setInProgress')
- ->with(1234, TRUE)
+ ->with(1234, 'connector.redmine', TRUE)
->willReturn(TRUE);
$output = $this->executeCommand('start', [
'issue_number' => 1234,
@@ -206,20 +191,20 @@ public function testStatusAndAssign() {
/**
* @covers ::execute
*/
- public function testStatusAssignAndComment() {
+ public function testStatusAndAssignWithBackend() {
$this->getMockConnector()->expects($this->any())
->method('ticketDetails')
- ->with(1234)
+ ->with(1234, 'connector.redmine')
->willReturn(new Ticket('Running tests', 123));
$this->getMockConnector()->expects($this->once())
->method('setInProgress')
- ->with(1234, TRUE, "I will look on friday")
+ ->with(1234, 'connector.redmine', TRUE)
->willReturn(TRUE);
$output = $this->executeCommand('start', [
'issue_number' => 1234,
'--status' => TRUE,
'-a' => TRUE,
- '-r' => "I will look on friday"
+ '-b' => 'redmine',
]);
$this->assertRegExp('/Started new entry for 1234: Running tests/', $output->getDisplay());
$this->assertRegExp('/Ticket 1234 set to in-progress/', $output->getDisplay());
@@ -230,18 +215,42 @@ public function testStatusAssignAndComment() {
/**
* @covers ::execute
*/
- public function testStatusAlreadyInProgress() {
+ public function testStatusAssignAndComment() {
$this->getMockConnector()->expects($this->any())
->method('ticketDetails')
- ->with(1234)
+ ->with(1234, 'connector.redmine')
->willReturn(new Ticket('Running tests', 123));
+ $this->getMockConnector()->expects($this->any())
+ ->method('spotConnector')
+ ->willReturn('connector.redmine');
+ $this->getMockConnector()->expects($this->once())
+ ->method('setInProgress')
+ ->with(1234, 'connector.redmine', TRUE, "I will look on friday")
+ ->willReturn(TRUE);
+ $output = $this->executeCommand('start', [
+ 'issue_number' => 1234,
+ '--status' => TRUE,
+ '-a' => TRUE,
+ '-r' => "I will look on friday",
+ ]);
+ $this->assertRegExp('/Started new entry for 1234: Running tests/', $output->getDisplay());
+ $this->assertRegExp('/Ticket 1234 set to in-progress/', $output->getDisplay());
+ $this->assertRegExp('/Ticket 1234 assigned to you/', $output->getDisplay());
+ $this->assertTicketIsOpen(1234);
+ }
+
+ /**
+ * @covers ::execute
+ */
+ public function testStatusAlreadyInProgress() {
+ $this->setupConnector();
$this->getMockConnector()->expects($this->once())
->method('setInProgress')
->with(1234)
->willReturn(FALSE);
$output = $this->executeCommand('start', [
'issue_number' => 1234,
- '-s' => TRUE
+ '-s' => TRUE,
]);
$this->assertRegExp('/Started new entry for 1234: Running tests/', $output->getDisplay());
$this->assertRegExp('/Could not update ticket status/', $output->getDisplay());
diff --git a/tests/Commands/StatusTest.php b/tests/Commands/StatusTest.php
index 3c1ad55..840f718 100644
--- a/tests/Commands/StatusTest.php
+++ b/tests/Commands/StatusTest.php
@@ -1,13 +1,7 @@
'http://example.com']));
$container->setParameter('directory', $test_directory);
- $mock_connector = $this->getMock(Connector::class);
+ $mock_connector = $this->createMock(ConnectorManager::class);
$container->set('connector', $mock_connector);
- $configuration_processor = $this->getMock(Processor::class);
+ $configuration_processor = $this->createMock(Processor::class);
$configuration_processor->expects($this->any())
->method('processConfiguration')
->willReturn([]);
@@ -70,20 +67,23 @@ protected function setUp() {
$install = $container->get('app.command.install');
$install->setApplication($this->application);
$input = new ArrayInput(['command' => 'install']);
- $output = $this->getMock(OutputInterface::class);
+ $output = $this->createMock(OutputInterface::class);
$install->run($input, $output);
}
}
+ /**
+ *
+ */
protected function tearDown() {
- parent::tearDown(); // TODO: Change the autogenerated stub
+ // TODO: Change the autogenerated stub.
+ parent::tearDown();
}
-
/**
* Gets the mock connector.
*
- * @return \Larowlan\Tl\Connector\Connector|\PHPUnit_Framework_MockObject_MockObject $connector
+ * @return \Larowlan\Tl\Connector\Connector|\PHPUnit_Framework_MockObject_MockObject
* The Mock connector.
*/
protected function getMockConnector() {
@@ -123,7 +123,7 @@ protected function executeCommand($name, array $input = []) {
$command = $this->container->get('app.command.' . $name);
$command->setApplication($this->application);
$command_tester = new CommandTester($command);
- $command_tester->execute(['command' => $command->getName()] + $input);
+ $command_tester->execute(['command' => $command->getName()] + $input);
return $command_tester;
}
@@ -131,7 +131,7 @@ protected function executeCommand($name, array $input = []) {
* @return mixed
*/
protected function assertTicketIsOpen($ticket_id, $comment = NULL) {
- /** @var Repository $repository */
+ /** @var \Larowlan\Tl\Repository\Repository $repository */
$repository = $this->getRepository();
$active = $repository->getActive();
$this->assertEquals($ticket_id, $active->tid);
@@ -142,4 +142,17 @@ protected function assertTicketIsOpen($ticket_id, $comment = NULL) {
return $active;
}
+ /**
+ * Sets up connector.
+ */
+ protected function setupConnector() {
+ $this->getMockConnector()->expects($this->any())
+ ->method('ticketDetails')
+ ->with(1234, 'connector.redmine')
+ ->willReturn(new Ticket('Running tests', 123));
+ $this->getMockConnector()->expects($this->any())
+ ->method('spotConnector')
+ ->willReturn('connector.redmine');
+ }
+
}
diff --git a/tests/bootstrap.php b/tests/bootstrap.php
index 5395b02..09851f6 100644
--- a/tests/bootstrap.php
+++ b/tests/bootstrap.php
@@ -1,4 +1,5 @@