From 74f3b145f207ac7ac74a6850f949596f11aad2c5 Mon Sep 17 00:00:00 2001 From: "a.cianfarani" Date: Wed, 5 Jun 2013 16:48:01 +0200 Subject: [PATCH 001/933] Merge upstream/master --- LICENSE.md | 0 PHPCI/Application.php | 0 PHPCI/BuildFactory.php | 0 PHPCI/Builder.php | 0 PHPCI/Command/GenerateCommand.php | 0 PHPCI/Command/InstallCommand.php | 0 PHPCI/Command/RunCommand.php | 0 PHPCI/Controller.php | 0 PHPCI/Controller/BitbucketController.php | 0 PHPCI/Controller/BuildController.php | 0 PHPCI/Controller/BuildStatusController.php | 0 PHPCI/Controller/GithubController.php | 0 PHPCI/Controller/IndexController.php | 0 PHPCI/Controller/ProjectController.php | 0 PHPCI/Controller/SessionController.php | 0 PHPCI/Controller/UserController.php | 0 PHPCI/Helper/User.php | 0 PHPCI/Model/Base/BuildBase.php | 0 PHPCI/Model/Base/ProjectBase.php | 0 PHPCI/Model/Base/UserBase.php | 0 PHPCI/Model/Build.php | 0 PHPCI/Model/Build/BitbucketBuild.php | 0 PHPCI/Model/Build/GithubBuild.php | 0 PHPCI/Model/Build/LocalBuild.php | 0 PHPCI/Model/Build/RemoteGitBuild.php | 0 PHPCI/Model/Project.php | 0 PHPCI/Model/User.php | 0 PHPCI/Plugin.php | 0 PHPCI/Plugin/CleanBuild.php | 0 PHPCI/Plugin/Composer.php | 0 PHPCI/Plugin/CopyBuild.php | 0 PHPCI/Plugin/Env.php | 0 PHPCI/Plugin/Mysql.php | 0 PHPCI/Plugin/PackageBuild.php | 0 PHPCI/Plugin/Pgsql.php | 0 PHPCI/Plugin/PhpCodeSniffer.php | 0 PHPCI/Plugin/PhpCpd.php | 0 PHPCI/Plugin/PhpCsFixer.php | 0 PHPCI/Plugin/PhpMessDetector.php | 0 PHPCI/Plugin/PhpSpec.php | 0 PHPCI/Plugin/PhpUnit.php | 0 PHPCI/Plugin/Shell.php | 0 PHPCI/Store/Base/BuildStoreBase.php | 0 PHPCI/Store/Base/ProjectStoreBase.php | 0 PHPCI/Store/Base/UserStoreBase.php | 0 PHPCI/Store/BuildStore.php | 0 PHPCI/Store/ProjectStore.php | 0 PHPCI/Store/UserStore.php | 0 PHPCI/View/Build/view.phtml | 0 PHPCI/View/BuildsTable.phtml | 0 PHPCI/View/Index/index.phtml | 0 PHPCI/View/Project/view.phtml | 0 PHPCI/View/ProjectForm.phtml | 0 PHPCI/View/Session/login.phtml | 0 PHPCI/View/User/index.phtml | 0 PHPCI/View/UserForm.phtml | 0 PHPCI/View/layout.phtml | 0 PHPCI/build/.gitignore | 0 README.md | 0 Tests/PHPCI/Plugin/PHPUnitTest.php | 0 assets/css/phpci.css | 0 assets/img/build-failed.png | Bin assets/img/build-ok.png | Bin assets/img/glyphicons-halflings-white.png | Bin assets/img/glyphicons-halflings.png | Bin assets/img/icon-build-failed.png | Bin assets/img/icon-build-ok.png | Bin assets/img/icon-build-pending.png | Bin assets/img/icon-build-running.png | Bin assets/js/phpci.js | 0 bootstrap.php | 0 build/.gitignore | 0 composer.json | 0 index.php | 0 phpci.yml | 0 75 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 LICENSE.md mode change 100644 => 100755 PHPCI/Application.php mode change 100644 => 100755 PHPCI/BuildFactory.php mode change 100644 => 100755 PHPCI/Builder.php mode change 100644 => 100755 PHPCI/Command/GenerateCommand.php mode change 100644 => 100755 PHPCI/Command/InstallCommand.php mode change 100644 => 100755 PHPCI/Command/RunCommand.php mode change 100644 => 100755 PHPCI/Controller.php mode change 100644 => 100755 PHPCI/Controller/BitbucketController.php mode change 100644 => 100755 PHPCI/Controller/BuildController.php mode change 100644 => 100755 PHPCI/Controller/BuildStatusController.php mode change 100644 => 100755 PHPCI/Controller/GithubController.php mode change 100644 => 100755 PHPCI/Controller/IndexController.php mode change 100644 => 100755 PHPCI/Controller/ProjectController.php mode change 100644 => 100755 PHPCI/Controller/SessionController.php mode change 100644 => 100755 PHPCI/Controller/UserController.php mode change 100644 => 100755 PHPCI/Helper/User.php mode change 100644 => 100755 PHPCI/Model/Base/BuildBase.php mode change 100644 => 100755 PHPCI/Model/Base/ProjectBase.php mode change 100644 => 100755 PHPCI/Model/Base/UserBase.php mode change 100644 => 100755 PHPCI/Model/Build.php mode change 100644 => 100755 PHPCI/Model/Build/BitbucketBuild.php mode change 100644 => 100755 PHPCI/Model/Build/GithubBuild.php mode change 100644 => 100755 PHPCI/Model/Build/LocalBuild.php mode change 100644 => 100755 PHPCI/Model/Build/RemoteGitBuild.php mode change 100644 => 100755 PHPCI/Model/Project.php mode change 100644 => 100755 PHPCI/Model/User.php mode change 100644 => 100755 PHPCI/Plugin.php mode change 100644 => 100755 PHPCI/Plugin/CleanBuild.php mode change 100644 => 100755 PHPCI/Plugin/Composer.php mode change 100644 => 100755 PHPCI/Plugin/CopyBuild.php mode change 100644 => 100755 PHPCI/Plugin/Env.php mode change 100644 => 100755 PHPCI/Plugin/Mysql.php mode change 100644 => 100755 PHPCI/Plugin/PackageBuild.php mode change 100644 => 100755 PHPCI/Plugin/Pgsql.php mode change 100644 => 100755 PHPCI/Plugin/PhpCodeSniffer.php mode change 100644 => 100755 PHPCI/Plugin/PhpCpd.php mode change 100644 => 100755 PHPCI/Plugin/PhpCsFixer.php mode change 100644 => 100755 PHPCI/Plugin/PhpMessDetector.php mode change 100644 => 100755 PHPCI/Plugin/PhpSpec.php mode change 100644 => 100755 PHPCI/Plugin/PhpUnit.php mode change 100644 => 100755 PHPCI/Plugin/Shell.php mode change 100644 => 100755 PHPCI/Store/Base/BuildStoreBase.php mode change 100644 => 100755 PHPCI/Store/Base/ProjectStoreBase.php mode change 100644 => 100755 PHPCI/Store/Base/UserStoreBase.php mode change 100644 => 100755 PHPCI/Store/BuildStore.php mode change 100644 => 100755 PHPCI/Store/ProjectStore.php mode change 100644 => 100755 PHPCI/Store/UserStore.php mode change 100644 => 100755 PHPCI/View/Build/view.phtml mode change 100644 => 100755 PHPCI/View/BuildsTable.phtml mode change 100644 => 100755 PHPCI/View/Index/index.phtml mode change 100644 => 100755 PHPCI/View/Project/view.phtml mode change 100644 => 100755 PHPCI/View/ProjectForm.phtml mode change 100644 => 100755 PHPCI/View/Session/login.phtml mode change 100644 => 100755 PHPCI/View/User/index.phtml mode change 100644 => 100755 PHPCI/View/UserForm.phtml mode change 100644 => 100755 PHPCI/View/layout.phtml mode change 100644 => 100755 PHPCI/build/.gitignore mode change 100644 => 100755 README.md mode change 100644 => 100755 Tests/PHPCI/Plugin/PHPUnitTest.php mode change 100644 => 100755 assets/css/phpci.css mode change 100644 => 100755 assets/img/build-failed.png mode change 100644 => 100755 assets/img/build-ok.png mode change 100644 => 100755 assets/img/glyphicons-halflings-white.png mode change 100644 => 100755 assets/img/glyphicons-halflings.png mode change 100644 => 100755 assets/img/icon-build-failed.png mode change 100644 => 100755 assets/img/icon-build-ok.png mode change 100644 => 100755 assets/img/icon-build-pending.png mode change 100644 => 100755 assets/img/icon-build-running.png mode change 100644 => 100755 assets/js/phpci.js mode change 100644 => 100755 bootstrap.php mode change 100644 => 100755 build/.gitignore mode change 100644 => 100755 composer.json mode change 100644 => 100755 index.php mode change 100644 => 100755 phpci.yml diff --git a/LICENSE.md b/LICENSE.md old mode 100644 new mode 100755 diff --git a/PHPCI/Application.php b/PHPCI/Application.php old mode 100644 new mode 100755 diff --git a/PHPCI/BuildFactory.php b/PHPCI/BuildFactory.php old mode 100644 new mode 100755 diff --git a/PHPCI/Builder.php b/PHPCI/Builder.php old mode 100644 new mode 100755 diff --git a/PHPCI/Command/GenerateCommand.php b/PHPCI/Command/GenerateCommand.php old mode 100644 new mode 100755 diff --git a/PHPCI/Command/InstallCommand.php b/PHPCI/Command/InstallCommand.php old mode 100644 new mode 100755 diff --git a/PHPCI/Command/RunCommand.php b/PHPCI/Command/RunCommand.php old mode 100644 new mode 100755 diff --git a/PHPCI/Controller.php b/PHPCI/Controller.php old mode 100644 new mode 100755 diff --git a/PHPCI/Controller/BitbucketController.php b/PHPCI/Controller/BitbucketController.php old mode 100644 new mode 100755 diff --git a/PHPCI/Controller/BuildController.php b/PHPCI/Controller/BuildController.php old mode 100644 new mode 100755 diff --git a/PHPCI/Controller/BuildStatusController.php b/PHPCI/Controller/BuildStatusController.php old mode 100644 new mode 100755 diff --git a/PHPCI/Controller/GithubController.php b/PHPCI/Controller/GithubController.php old mode 100644 new mode 100755 diff --git a/PHPCI/Controller/IndexController.php b/PHPCI/Controller/IndexController.php old mode 100644 new mode 100755 diff --git a/PHPCI/Controller/ProjectController.php b/PHPCI/Controller/ProjectController.php old mode 100644 new mode 100755 diff --git a/PHPCI/Controller/SessionController.php b/PHPCI/Controller/SessionController.php old mode 100644 new mode 100755 diff --git a/PHPCI/Controller/UserController.php b/PHPCI/Controller/UserController.php old mode 100644 new mode 100755 diff --git a/PHPCI/Helper/User.php b/PHPCI/Helper/User.php old mode 100644 new mode 100755 diff --git a/PHPCI/Model/Base/BuildBase.php b/PHPCI/Model/Base/BuildBase.php old mode 100644 new mode 100755 diff --git a/PHPCI/Model/Base/ProjectBase.php b/PHPCI/Model/Base/ProjectBase.php old mode 100644 new mode 100755 diff --git a/PHPCI/Model/Base/UserBase.php b/PHPCI/Model/Base/UserBase.php old mode 100644 new mode 100755 diff --git a/PHPCI/Model/Build.php b/PHPCI/Model/Build.php old mode 100644 new mode 100755 diff --git a/PHPCI/Model/Build/BitbucketBuild.php b/PHPCI/Model/Build/BitbucketBuild.php old mode 100644 new mode 100755 diff --git a/PHPCI/Model/Build/GithubBuild.php b/PHPCI/Model/Build/GithubBuild.php old mode 100644 new mode 100755 diff --git a/PHPCI/Model/Build/LocalBuild.php b/PHPCI/Model/Build/LocalBuild.php old mode 100644 new mode 100755 diff --git a/PHPCI/Model/Build/RemoteGitBuild.php b/PHPCI/Model/Build/RemoteGitBuild.php old mode 100644 new mode 100755 diff --git a/PHPCI/Model/Project.php b/PHPCI/Model/Project.php old mode 100644 new mode 100755 diff --git a/PHPCI/Model/User.php b/PHPCI/Model/User.php old mode 100644 new mode 100755 diff --git a/PHPCI/Plugin.php b/PHPCI/Plugin.php old mode 100644 new mode 100755 diff --git a/PHPCI/Plugin/CleanBuild.php b/PHPCI/Plugin/CleanBuild.php old mode 100644 new mode 100755 diff --git a/PHPCI/Plugin/Composer.php b/PHPCI/Plugin/Composer.php old mode 100644 new mode 100755 diff --git a/PHPCI/Plugin/CopyBuild.php b/PHPCI/Plugin/CopyBuild.php old mode 100644 new mode 100755 diff --git a/PHPCI/Plugin/Env.php b/PHPCI/Plugin/Env.php old mode 100644 new mode 100755 diff --git a/PHPCI/Plugin/Mysql.php b/PHPCI/Plugin/Mysql.php old mode 100644 new mode 100755 diff --git a/PHPCI/Plugin/PackageBuild.php b/PHPCI/Plugin/PackageBuild.php old mode 100644 new mode 100755 diff --git a/PHPCI/Plugin/Pgsql.php b/PHPCI/Plugin/Pgsql.php old mode 100644 new mode 100755 diff --git a/PHPCI/Plugin/PhpCodeSniffer.php b/PHPCI/Plugin/PhpCodeSniffer.php old mode 100644 new mode 100755 diff --git a/PHPCI/Plugin/PhpCpd.php b/PHPCI/Plugin/PhpCpd.php old mode 100644 new mode 100755 diff --git a/PHPCI/Plugin/PhpCsFixer.php b/PHPCI/Plugin/PhpCsFixer.php old mode 100644 new mode 100755 diff --git a/PHPCI/Plugin/PhpMessDetector.php b/PHPCI/Plugin/PhpMessDetector.php old mode 100644 new mode 100755 diff --git a/PHPCI/Plugin/PhpSpec.php b/PHPCI/Plugin/PhpSpec.php old mode 100644 new mode 100755 diff --git a/PHPCI/Plugin/PhpUnit.php b/PHPCI/Plugin/PhpUnit.php old mode 100644 new mode 100755 diff --git a/PHPCI/Plugin/Shell.php b/PHPCI/Plugin/Shell.php old mode 100644 new mode 100755 diff --git a/PHPCI/Store/Base/BuildStoreBase.php b/PHPCI/Store/Base/BuildStoreBase.php old mode 100644 new mode 100755 diff --git a/PHPCI/Store/Base/ProjectStoreBase.php b/PHPCI/Store/Base/ProjectStoreBase.php old mode 100644 new mode 100755 diff --git a/PHPCI/Store/Base/UserStoreBase.php b/PHPCI/Store/Base/UserStoreBase.php old mode 100644 new mode 100755 diff --git a/PHPCI/Store/BuildStore.php b/PHPCI/Store/BuildStore.php old mode 100644 new mode 100755 diff --git a/PHPCI/Store/ProjectStore.php b/PHPCI/Store/ProjectStore.php old mode 100644 new mode 100755 diff --git a/PHPCI/Store/UserStore.php b/PHPCI/Store/UserStore.php old mode 100644 new mode 100755 diff --git a/PHPCI/View/Build/view.phtml b/PHPCI/View/Build/view.phtml old mode 100644 new mode 100755 diff --git a/PHPCI/View/BuildsTable.phtml b/PHPCI/View/BuildsTable.phtml old mode 100644 new mode 100755 diff --git a/PHPCI/View/Index/index.phtml b/PHPCI/View/Index/index.phtml old mode 100644 new mode 100755 diff --git a/PHPCI/View/Project/view.phtml b/PHPCI/View/Project/view.phtml old mode 100644 new mode 100755 diff --git a/PHPCI/View/ProjectForm.phtml b/PHPCI/View/ProjectForm.phtml old mode 100644 new mode 100755 diff --git a/PHPCI/View/Session/login.phtml b/PHPCI/View/Session/login.phtml old mode 100644 new mode 100755 diff --git a/PHPCI/View/User/index.phtml b/PHPCI/View/User/index.phtml old mode 100644 new mode 100755 diff --git a/PHPCI/View/UserForm.phtml b/PHPCI/View/UserForm.phtml old mode 100644 new mode 100755 diff --git a/PHPCI/View/layout.phtml b/PHPCI/View/layout.phtml old mode 100644 new mode 100755 diff --git a/PHPCI/build/.gitignore b/PHPCI/build/.gitignore old mode 100644 new mode 100755 diff --git a/README.md b/README.md old mode 100644 new mode 100755 diff --git a/Tests/PHPCI/Plugin/PHPUnitTest.php b/Tests/PHPCI/Plugin/PHPUnitTest.php old mode 100644 new mode 100755 diff --git a/assets/css/phpci.css b/assets/css/phpci.css old mode 100644 new mode 100755 diff --git a/assets/img/build-failed.png b/assets/img/build-failed.png old mode 100644 new mode 100755 diff --git a/assets/img/build-ok.png b/assets/img/build-ok.png old mode 100644 new mode 100755 diff --git a/assets/img/glyphicons-halflings-white.png b/assets/img/glyphicons-halflings-white.png old mode 100644 new mode 100755 diff --git a/assets/img/glyphicons-halflings.png b/assets/img/glyphicons-halflings.png old mode 100644 new mode 100755 diff --git a/assets/img/icon-build-failed.png b/assets/img/icon-build-failed.png old mode 100644 new mode 100755 diff --git a/assets/img/icon-build-ok.png b/assets/img/icon-build-ok.png old mode 100644 new mode 100755 diff --git a/assets/img/icon-build-pending.png b/assets/img/icon-build-pending.png old mode 100644 new mode 100755 diff --git a/assets/img/icon-build-running.png b/assets/img/icon-build-running.png old mode 100644 new mode 100755 diff --git a/assets/js/phpci.js b/assets/js/phpci.js old mode 100644 new mode 100755 diff --git a/bootstrap.php b/bootstrap.php old mode 100644 new mode 100755 diff --git a/build/.gitignore b/build/.gitignore old mode 100644 new mode 100755 diff --git a/composer.json b/composer.json old mode 100644 new mode 100755 diff --git a/index.php b/index.php old mode 100644 new mode 100755 diff --git a/phpci.yml b/phpci.yml old mode 100644 new mode 100755 From dd3671186fd4a93385698077b7477adce33131b8 Mon Sep 17 00:00:00 2001 From: japaveh Date: Sat, 13 Jul 2013 00:19:43 +0200 Subject: [PATCH 002/933] Included support for PhpLoc and Pdepend --- PHPCI/Plugin/Pdepend.php | 104 +++++++++++++++++++++++++++++++++++++++ PHPCI/Plugin/PhpLoc.php | 52 ++++++++++++++++++++ composer.json | 3 +- 3 files changed, 158 insertions(+), 1 deletion(-) create mode 100644 PHPCI/Plugin/Pdepend.php create mode 100644 PHPCI/Plugin/PhpLoc.php diff --git a/PHPCI/Plugin/Pdepend.php b/PHPCI/Plugin/Pdepend.php new file mode 100644 index 00000000..95a7a8c0 --- /dev/null +++ b/PHPCI/Plugin/Pdepend.php @@ -0,0 +1,104 @@ + + * @package PHPCI + * @subpackage Plugins + */ +class Pdepend implements \PHPCI\Plugin +{ + protected $args; + /** + * @var \PHPCI\Builder + */ + protected $phpci; + /** + * @var string Directory which needs to be scanned + */ + protected $directory; + /** + * @var string File where the summary.xml is stored + */ + protected $summary; + /** + * @var string File where the chart.svg is stored + */ + protected $chart; + /** + * @var string File where the pyramid.svg is stored + */ + protected $pyramid; + /** + * @var string Location on the server where the files are stored. Preferably in the webroot for inclusion + * in the readme.md of the repository + */ + protected $location; + + + public function __construct(\PHPCI\Builder $phpci, array $options = array()) + { + $this->phpci = $phpci; + + $this->directory = isset($options['directory']) ? $options['directory'] : $phpci->buildPath; + + $this->summary = $phpci->getBuildProjectTitle() . '-summary.xml'; + $this->pyramid = $phpci->getBuildProjectTitle() . '-pyramid.svg'; + $this->chart = $phpci->getBuildProjectTitle() . '-chart.svg'; + $this->location = $this->phpci->buildPath . '..' . DIRECTORY_SEPARATOR . 'pdepend'; + } + + /** + * Runs Pdepend with the given criteria as arguments + */ + public function execute() + { + if (!is_writable($this->location)) { + throw new \Exception(sprintf('The location %s is not writable.', $this->location)); + } + + $cmd = PHPCI_BIN_DIR . 'pdepend --summary-xml=%s --jdepend-chart=%s --overview-pyramid=%s "%s"'; + + //Remove the created files first + unlink($this->location . DIRECTORY_SEPARATOR . $this->summary); + unlink($this->location . DIRECTORY_SEPARATOR . $this->chart); + unlink($this->location . DIRECTORY_SEPARATOR . $this->pyramid); + + $success = $this->phpci->executeCommand( + $cmd, + $this->location . DIRECTORY_SEPARATOR . $this->summary, + $this->location . DIRECTORY_SEPARATOR . $this->chart, + $this->location . DIRECTORY_SEPARATOR . $this->pyramid, + $this->directory + ); + + $config = $this->phpci->getSystemConfig('phpci'); + + if ($success) { + $this->phpci->logSuccess( + sprintf( + "Pdepend successful. You can use %s\n, ![Chart](%s \"Pdepend Chart\")\n + and ![Pyramid](%s \"Pdepend Pyramid\")\n + for inclusion in the readme.md file", + $config['url'] . '/build/pdepend/' . $this->summary, + $config['url'] . '/build/pdepend/' . $this->chart, + $config['url'] . '/build/pdepend/' . $this->pyramid + ) + ); + } else { + $this->phpci->logFailure(sprintf("The function '%s' failed")); + } + + + return $success; + } +} \ No newline at end of file diff --git a/PHPCI/Plugin/PhpLoc.php b/PHPCI/Plugin/PhpLoc.php new file mode 100644 index 00000000..0ae62200 --- /dev/null +++ b/PHPCI/Plugin/PhpLoc.php @@ -0,0 +1,52 @@ + + * @package PHPCI + * @subpackage Plugins + */ +class PhpLoc implements \PHPCI\Plugin +{ + /** + * @var string + */ + protected $directory; + /** + * @var \PHPCI\Builder + */ + protected $phpci; + + public function __construct(\PHPCI\Builder $phpci, array $options = array()) + { + $this->phpci = $phpci; + $this->directory = isset($options['directory']) ? $options['directory'] : $phpci->buildPath; + } + + /** + * Runs PHP Copy/Paste Detector in a specified directory. + */ + public function execute() + { + $ignore = ''; + if (count($this->phpci->ignore)) { + $map = function ($item) { + return ' --exclude ' . (substr($item, -1) == '/' ? substr($item, 0, -1) : $item); + }; + $ignore = array_map($map, $this->phpci->ignore); + + $ignore = implode('', $ignore); + } + + return $this->phpci->executeCommand(PHPCI_BIN_DIR . 'phploc %s "%s"', $ignore, $this->phpci->buildPath); + } +} \ No newline at end of file diff --git a/composer.json b/composer.json index a4424830..a6368878 100644 --- a/composer.json +++ b/composer.json @@ -33,6 +33,7 @@ "symfony/yaml" : "2.2.x-dev", "symfony/console" : "2.2.*", "fabpot/php-cs-fixer" : "0.3.*@dev", - "swiftmailer/swiftmailer" : "v5.0.0" + "swiftmailer/swiftmailer" : "v5.0.0", + "phploc/phploc": "*" } } From 6793a5f373296146381d0c0bc453b06787379a33 Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Thu, 25 Jul 2013 13:44:08 +0100 Subject: [PATCH 003/933] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 16284708..aa1eff8a 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ PHPCI PHPCI is a free and open source continuous integration tool specifically designed for PHP. We've built it with simplicity in mind, so whilst it doesn't do *everything* Jenkins can do, it is a breeze to set up and use. -_**Please be aware that this is a beta-release project, so there will be bugs and missing features.**_ +_**Please be aware that PHPCI is a beta-release project, so whilst it is very stable, there may be bugs and/or missing features.**_ **Current Build Status** From 549a022b0e3babc8f521141e5f7c9d1ce15c3a16 Mon Sep 17 00:00:00 2001 From: mrafalko Date: Thu, 25 Jul 2013 23:58:16 +0300 Subject: [PATCH 004/933] replaced alpha word with beta [main logo] --- PHPCI/View/layout.phtml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PHPCI/View/layout.phtml b/PHPCI/View/layout.phtml index f5b95baf..2db749d3 100644 --- a/PHPCI/View/layout.phtml +++ b/PHPCI/View/layout.phtml @@ -17,7 +17,7 @@ @@ -80,9 +80,16 @@ From cadcdcd3d252cde1ad8d2c6e0e961553b072f1ef Mon Sep 17 00:00:00 2001 From: Alex Russell Date: Mon, 29 Jul 2013 17:34:21 +0100 Subject: [PATCH 007/933] Added new controller to accept Gitlab-like webhooks --- PHPCI/Application.php | 2 +- PHPCI/Controller/GitlabController.php | 61 +++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 PHPCI/Controller/GitlabController.php diff --git a/PHPCI/Application.php b/PHPCI/Application.php index 3378a050..d7d0a4fe 100644 --- a/PHPCI/Application.php +++ b/PHPCI/Application.php @@ -32,7 +32,7 @@ class Application extends b8\Application // Validate the user's session unless it is a login/logout action or a web hook: $sessionAction = ($this->controllerName == 'Session' && in_array($this->action, array('login', 'logout'))); - $externalAction = in_array($this->controllerName, array('Bitbucket', 'Github', 'BuildStatus')); + $externalAction = in_array($this->controllerName, array('Bitbucket', 'Github', 'Gitlab', 'BuildStatus')); $skipValidation = ($externalAction || $sessionAction); if($skipValidation || $this->validateSession()) { diff --git a/PHPCI/Controller/GitlabController.php b/PHPCI/Controller/GitlabController.php new file mode 100644 index 00000000..f3c1dd35 --- /dev/null +++ b/PHPCI/Controller/GitlabController.php @@ -0,0 +1,61 @@ +, Dan Cryer +* @package PHPCI +* @subpackage Web +*/ +class GitlabController extends \PHPCI\Controller +{ + public function init() + { + $this->_buildStore = Store\Factory::getStore('Build'); + } + + /** + * Called by Gitlab Webhooks: + */ + public function webhook($project) + { + $payload = json_decode(file_get_contents("php://input"), true); + + try { + $build = new Build(); + $build->setProjectId($project); + $build->setCommitId($payload['after']); + $build->setStatus(0); + $build->setLog(''); + $build->setCreated(new \DateTime()); + $build->setBranch(str_replace('refs/heads/', '', $payload['ref'])); + } catch (\Exception $ex) { + header('HTTP/1.1 400 Bad Request'); + header('Ex: ' . $ex->getMessage()); + die('FAIL'); + } + + try { + $build = $this->_buildStore->save($build); + $build->sendStatusPostback(); + } catch (\Exception $ex) { + header('HTTP/1.1 500 Internal Server Error'); + header('Ex: ' . $ex->getMessage()); + die('FAIL'); + } + + die('OK'); + } +} From b47dfbd0b34235226010bce6bb35f3b68480a124 Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Tue, 30 Jul 2013 02:55:29 +0100 Subject: [PATCH 008/933] Various bug fixes --- PHPCI/Application.php | 7 --- PHPCI/Builder.php | 6 +-- PHPCI/Controller/BuildController.php | 1 - PHPCI/Controller/GithubController.php | 5 +++ PHPCI/Controller/ProjectController.php | 3 +- PHPCI/Controller/UserController.php | 1 - PHPCI/Model/Base/BuildBase.php | 59 +++++++++++++++++++++----- PHPCI/Model/Base/ProjectBase.php | 12 +++--- PHPCI/Model/Base/UserBase.php | 10 ++--- PHPCI/Model/Build/GithubBuild.php | 2 +- PHPCI/Model/Build/RemoteGitBuild.php | 9 +++- PHPCI/View/ProjectForm.phtml | 2 +- PHPCI/View/SummaryTable.phtml | 4 +- bootstrap.php | 2 - index.php | 2 +- 15 files changed, 82 insertions(+), 43 deletions(-) diff --git a/PHPCI/Application.php b/PHPCI/Application.php index 3378a050..e7ce142b 100644 --- a/PHPCI/Application.php +++ b/PHPCI/Application.php @@ -10,7 +10,6 @@ namespace PHPCI; use b8; -use b8\Registry; use b8\Http\Response\RedirectResponse; use b8\View; @@ -25,9 +24,6 @@ class Application extends b8\Application */ public function handleRequest() { - // Registry legacy: - $registry = new b8\Registry($this->config, $this->request); - $this->initRequest(); // Validate the user's session unless it is a login/logout action or a web hook: @@ -36,9 +32,6 @@ class Application extends b8\Application $skipValidation = ($externalAction || $sessionAction); if($skipValidation || $this->validateSession()) { - if ( !empty($_SESSION['user']) ) { - Registry::getInstance()->set('user', $_SESSION['user']); - } parent::handleRequest(); } diff --git a/PHPCI/Builder.php b/PHPCI/Builder.php index f943a092..b8979452 100644 --- a/PHPCI/Builder.php +++ b/PHPCI/Builder.php @@ -11,7 +11,7 @@ namespace PHPCI; use PHPCI\Model\Build; use b8\Store; -use Symfony\Component\Yaml\Parser as YamlParser; +use b8\Config; /** * PHPCI Build Runner @@ -123,7 +123,7 @@ class Builder */ public function getSystemConfig($key) { - return \b8\Registry::getInstance()->get($key); + return Config::getInstance()->get($key); } /** @@ -319,7 +319,7 @@ class Builder { $commitId = $this->build->getCommitId(); $buildId = 'project' . $this->build->getProject()->getId() . '-build' . $this->build->getId(); - $this->ciDir = realpath(dirname(__FILE__) . '/../') . '/'; + $this->ciDir = dirname(__FILE__) . '/../'; $this->buildPath = $this->ciDir . 'build/' . $buildId . '/'; $this->setInterpolationVars(); diff --git a/PHPCI/Controller/BuildController.php b/PHPCI/Controller/BuildController.php index 56bde07c..b907b9bc 100644 --- a/PHPCI/Controller/BuildController.php +++ b/PHPCI/Controller/BuildController.php @@ -10,7 +10,6 @@ namespace PHPCI\Controller; use b8; -use b8\Registry; use PHPCI\Model\Build; /** diff --git a/PHPCI/Controller/GithubController.php b/PHPCI/Controller/GithubController.php index 90bc37fe..71ae0e3b 100644 --- a/PHPCI/Controller/GithubController.php +++ b/PHPCI/Controller/GithubController.php @@ -41,6 +41,11 @@ class GithubController extends \PHPCI\Controller $build->setLog(''); $build->setCreated(new \DateTime()); $build->setBranch(str_replace('refs/heads/', '', $payload['ref'])); + + if (!empty($payload['pusher']['email'])) { + $build->setCommitterEmail($payload['pusher']['email']); + } + } catch (\Exception $ex) { header('HTTP/1.1 400 Bad Request'); header('Ex: ' . $ex->getMessage()); diff --git a/PHPCI/Controller/ProjectController.php b/PHPCI/Controller/ProjectController.php index eaad90a1..47894266 100644 --- a/PHPCI/Controller/ProjectController.php +++ b/PHPCI/Controller/ProjectController.php @@ -15,7 +15,6 @@ use b8; use b8\Controller; use b8\Store; use b8\Form; -use b8\Registry; /** * Project Controller - Allows users to create, edit and view projects. @@ -175,7 +174,7 @@ class ProjectController extends \PHPCI\Controller */ protected function handleGithubResponse() { - $github = \b8\Registry::getInstance()->get('github_app'); + $github = \b8\Config::getInstance()->get('phpci.github'); $code = $this->getParam('code', null); if (!is_null($code)) { diff --git a/PHPCI/Controller/UserController.php b/PHPCI/Controller/UserController.php index 402c8792..0a814b9c 100644 --- a/PHPCI/Controller/UserController.php +++ b/PHPCI/Controller/UserController.php @@ -10,7 +10,6 @@ namespace PHPCI\Controller; use b8; -use b8\Registry; use PHPCI\Model\User; use b8\Form; diff --git a/PHPCI/Model/Base/BuildBase.php b/PHPCI/Model/Base/BuildBase.php index e051aac4..b681c110 100644 --- a/PHPCI/Model/Base/BuildBase.php +++ b/PHPCI/Model/Base/BuildBase.php @@ -42,6 +42,7 @@ class BuildBase extends Model 'started' => null, 'finished' => null, 'plugins' => null, + 'committer_email' => null, ); /** @@ -58,6 +59,7 @@ class BuildBase extends Model 'started' => 'getStarted', 'finished' => 'getFinished', 'plugins' => 'getPlugins', + 'committer_email' => 'getCommitterEmail', 'Project' => 'getProject', ); @@ -75,6 +77,7 @@ class BuildBase extends Model 'started' => 'setStarted', 'finished' => 'setFinished', 'plugins' => 'setPlugins', + 'committer_email' => 'setCommitterEmail', 'Project' => 'setProject', ); @@ -129,6 +132,11 @@ class BuildBase extends Model 'length' => '', 'nullable' => true, ), + 'committer_email' => array( + 'type' => 'varchar', + 'length' => '512', + 'nullable' => true, + ), ); /** @@ -299,6 +307,19 @@ class BuildBase extends Model return $rtn; } + /** + * Get the value of CommitterEmail / committer_email. + * + * @return string + */ + public function getCommitterEmail() + { + $rtn = $this->data['committer_email']; + + + return $rtn; + } + /** * Set the value of Id / id. * @@ -309,7 +330,7 @@ class BuildBase extends Model { $this->_validateNotNull('Id', $value); $this->_validateInt('Id', $value); - if ($this->data['id'] === $value) { + if ($this->data['id'] == $value) { return; } @@ -328,7 +349,7 @@ class BuildBase extends Model { $this->_validateNotNull('ProjectId', $value); $this->_validateInt('ProjectId', $value); - if ($this->data['project_id'] === $value) { + if ($this->data['project_id'] == $value) { return; } @@ -347,7 +368,7 @@ class BuildBase extends Model { $this->_validateNotNull('CommitId', $value); $this->_validateString('CommitId', $value); - if ($this->data['commit_id'] === $value) { + if ($this->data['commit_id'] == $value) { return; } @@ -366,7 +387,7 @@ class BuildBase extends Model { $this->_validateNotNull('Status', $value); $this->_validateInt('Status', $value); - if ($this->data['status'] === $value) { + if ($this->data['status'] == $value) { return; } @@ -384,7 +405,7 @@ class BuildBase extends Model { $this->_validateString('Log', $value); - if ($this->data['log'] === $value) { + if ($this->data['log'] == $value) { return; } @@ -403,7 +424,7 @@ class BuildBase extends Model { $this->_validateNotNull('Branch', $value); $this->_validateString('Branch', $value); - if ($this->data['branch'] === $value) { + if ($this->data['branch'] == $value) { return; } @@ -421,7 +442,7 @@ class BuildBase extends Model { $this->_validateDate('Created', $value); - if ($this->data['created'] === $value) { + if ($this->data['created'] == $value) { return; } @@ -439,7 +460,7 @@ class BuildBase extends Model { $this->_validateDate('Started', $value); - if ($this->data['started'] === $value) { + if ($this->data['started'] == $value) { return; } @@ -457,7 +478,7 @@ class BuildBase extends Model { $this->_validateDate('Finished', $value); - if ($this->data['finished'] === $value) { + if ($this->data['finished'] == $value) { return; } @@ -475,7 +496,7 @@ class BuildBase extends Model { $this->_validateString('Plugins', $value); - if ($this->data['plugins'] === $value) { + if ($this->data['plugins'] == $value) { return; } @@ -484,6 +505,24 @@ class BuildBase extends Model $this->_setModified('plugins'); } + /** + * Set the value of CommitterEmail / committer_email. + * + * @param $value string + */ + public function setCommitterEmail($value) + { + + $this->_validateString('CommitterEmail', $value); + if ($this->data['committer_email'] == $value) { + return; + } + + $this->data['committer_email'] = $value; + + $this->_setModified('committer_email'); + } + /** * Get the Project model for this Build by Id. * diff --git a/PHPCI/Model/Base/ProjectBase.php b/PHPCI/Model/Base/ProjectBase.php index 45481dd5..c9d577af 100644 --- a/PHPCI/Model/Base/ProjectBase.php +++ b/PHPCI/Model/Base/ProjectBase.php @@ -199,7 +199,7 @@ class ProjectBase extends Model { $this->_validateNotNull('Id', $value); $this->_validateInt('Id', $value); - if ($this->data['id'] === $value) { + if ($this->data['id'] == $value) { return; } @@ -218,7 +218,7 @@ class ProjectBase extends Model { $this->_validateNotNull('Title', $value); $this->_validateString('Title', $value); - if ($this->data['title'] === $value) { + if ($this->data['title'] == $value) { return; } @@ -237,7 +237,7 @@ class ProjectBase extends Model { $this->_validateNotNull('Reference', $value); $this->_validateString('Reference', $value); - if ($this->data['reference'] === $value) { + if ($this->data['reference'] == $value) { return; } @@ -256,7 +256,7 @@ class ProjectBase extends Model { $this->_validateNotNull('GitKey', $value); $this->_validateString('GitKey', $value); - if ($this->data['git_key'] === $value) { + if ($this->data['git_key'] == $value) { return; } @@ -275,7 +275,7 @@ class ProjectBase extends Model { $this->_validateNotNull('Type', $value); $this->_validateString('Type', $value); - if ($this->data['type'] === $value) { + if ($this->data['type'] == $value) { return; } @@ -293,7 +293,7 @@ class ProjectBase extends Model { $this->_validateString('Token', $value); - if ($this->data['token'] === $value) { + if ($this->data['token'] == $value) { return; } diff --git a/PHPCI/Model/Base/UserBase.php b/PHPCI/Model/Base/UserBase.php index 7ce2759c..aab1e21b 100644 --- a/PHPCI/Model/Base/UserBase.php +++ b/PHPCI/Model/Base/UserBase.php @@ -179,7 +179,7 @@ class UserBase extends Model { $this->_validateNotNull('Id', $value); $this->_validateInt('Id', $value); - if ($this->data['id'] === $value) { + if ($this->data['id'] == $value) { return; } @@ -198,7 +198,7 @@ class UserBase extends Model { $this->_validateNotNull('Email', $value); $this->_validateString('Email', $value); - if ($this->data['email'] === $value) { + if ($this->data['email'] == $value) { return; } @@ -217,7 +217,7 @@ class UserBase extends Model { $this->_validateNotNull('Hash', $value); $this->_validateString('Hash', $value); - if ($this->data['hash'] === $value) { + if ($this->data['hash'] == $value) { return; } @@ -236,7 +236,7 @@ class UserBase extends Model { $this->_validateNotNull('IsAdmin', $value); $this->_validateInt('IsAdmin', $value); - if ($this->data['is_admin'] === $value) { + if ($this->data['is_admin'] == $value) { return; } @@ -255,7 +255,7 @@ class UserBase extends Model { $this->_validateNotNull('Name', $value); $this->_validateString('Name', $value); - if ($this->data['name'] === $value) { + if ($this->data['name'] == $value) { return; } diff --git a/PHPCI/Model/Build/GithubBuild.php b/PHPCI/Model/Build/GithubBuild.php index 35ce439b..6518da92 100644 --- a/PHPCI/Model/Build/GithubBuild.php +++ b/PHPCI/Model/Build/GithubBuild.php @@ -67,7 +67,7 @@ class GithubBuild extends RemoteGitBuild break; } - $url = \b8\Registry::getInstance()->get('install_url'); + $url = \b8\Config::getInstance()->get('phpci.url'); $params = array( 'state' => $status, 'target_url' => $url . '/build/view/' . $this->getId()); $headers = array( diff --git a/PHPCI/Model/Build/RemoteGitBuild.php b/PHPCI/Model/Build/RemoteGitBuild.php index d1fe1c74..490b92ef 100644 --- a/PHPCI/Model/Build/RemoteGitBuild.php +++ b/PHPCI/Model/Build/RemoteGitBuild.php @@ -74,7 +74,14 @@ class RemoteGitBuild extends Build protected function cloneBySsh(Builder $builder, $to) { // Copy the project's keyfile to disk: - $keyFile = realpath($to) . '.key'; + $keyPath = realpath($to); + + if ($keyPath === false) { + $keyPath = dirname($to); + } + + $keyFile = $keyPath . '.key'; + file_put_contents($keyFile, $this->getProject()->getGitKey()); chmod($keyFile, 0600); diff --git a/PHPCI/View/ProjectForm.phtml b/PHPCI/View/ProjectForm.phtml index f59a461b..e52cbd9e 100644 --- a/PHPCI/View/ProjectForm.phtml +++ b/PHPCI/View/ProjectForm.phtml @@ -25,7 +25,7 @@ window.return_url = ; get('github_app', null); +$gh = \b8\Config::getInstance()->get('phpci.github', null); if($gh) { print 'window.github_app_id = ' . json_encode($gh['id']) . ';' . PHP_EOL; diff --git a/PHPCI/View/SummaryTable.phtml b/PHPCI/View/SummaryTable.phtml index de374e2c..3db900b2 100644 --- a/PHPCI/View/SummaryTable.phtml +++ b/PHPCI/View/SummaryTable.phtml @@ -80,8 +80,8 @@ foreach($projects as $projectId => $project): break; } - $health = ($project['health'] < 0 ? 'Stormy': ($project['health'] < 5? 'Overcast': 'Sunny')); - $subcls = ($project['health'] < 0 ? 'important': ($project['health'] < 5? 'warning': 'success')); + $health = ($project['health'] <= 0 ? 'Stormy': ($project['successes'] < $project['count']? 'Overcast': 'Sunny')); + $subcls = ($project['health'] <= 0 ? 'important': ($project['successes'] < $project['count']? 'warning': 'success')); ?> diff --git a/bootstrap.php b/bootstrap.php index a2b0e8d3..9cd035e9 100644 --- a/bootstrap.php +++ b/bootstrap.php @@ -42,8 +42,6 @@ $conf['b8']['app']['default_controller'] = 'Index'; $conf['b8']['view']['path'] = dirname(__FILE__) . '/PHPCI/View/'; $config = new b8\Config($conf); -$request = new b8\Http\Request(); -$registry = new b8\Registry($config, $request); if (file_exists(APPLICATION_PATH . 'config.php')) { require(APPLICATION_PATH . 'config.php'); diff --git a/index.php b/index.php index cab4239d..b4685c7d 100644 --- a/index.php +++ b/index.php @@ -14,5 +14,5 @@ ini_set('display_errors', 'on'); require_once('bootstrap.php'); -$fc = new PHPCI\Application($config, $request); +$fc = new PHPCI\Application($config, new b8\Http\Request()); print $fc->handleRequest(); From 4133bb79055fba9cfe5a5b93ca763724abb6897a Mon Sep 17 00:00:00 2001 From: Alex Russell Date: Tue, 30 Jul 2013 14:58:00 +0100 Subject: [PATCH 009/933] Extended phpcs plugin to allow for extra commandline arguments. - Now accepts paths in the --standards argument (and correctly prepends the build directory) so it actually works - Now accepts the --tabwidth argument so that if your project favours tabs over spaces you can still just use PSR2 and you don't have to sacrifice using the Generic.WhiteSpace.ScopeIndent rule because it is spaces-only - Now accpets the --encoding argument. I haven't used this before, but looking in the phpcs documentation it looks like a very useful one to have as most people code in utf-8 but phpcs defaults to iso-8859-1 and it can apparently "cause double-encoding problems when generating UTF-8 encoded XML reports" --- PHPCI/Plugin/PhpCodeSniffer.php | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/PHPCI/Plugin/PhpCodeSniffer.php b/PHPCI/Plugin/PhpCodeSniffer.php index 4c3c2680..8e28fd22 100644 --- a/PHPCI/Plugin/PhpCodeSniffer.php +++ b/PHPCI/Plugin/PhpCodeSniffer.php @@ -26,6 +26,8 @@ class PhpCodeSniffer implements \PHPCI\Plugin $this->phpci = $phpci; $this->directory = isset($options['directory']) ? $options['directory'] : $phpci->buildPath; $this->standard = isset($options['standard']) ? $options['standard'] : 'PSR2'; + $this->tab_width = isset($options['tab_width']) ? $options['tab_width'] : ''; + $this->encoding = isset($options['encoding']) ? $options['encoding'] : ''; } /** @@ -39,7 +41,27 @@ class PhpCodeSniffer implements \PHPCI\Plugin $ignore = ' --ignore=' . implode(',', $this->phpci->ignore); } - $cmd = PHPCI_BIN_DIR . 'phpcs --standard=%s %s "%s"'; - return $this->phpci->executeCommand($cmd, $this->standard, $ignore, $this->phpci->buildPath); + $standard = ''; + + if (strpos($this->standard, '/') !== false) { + $standard = ' --standard='.$this->directory.$this->standard; + } else { + $standard = ' --standard='.$this->standard; + } + + $tab_width = ''; + + if (strlen($this->tab_width)) { + $tab_width = ' --tab-width='.$this->tab_width; + } + + $encoding = ''; + + if (strlen($this->encoding)) { + $encoding = ' --encoding='.$this->encoding; + } + + $cmd = PHPCI_BIN_DIR . 'phpcs %s %s %s %s "%s"'; + return $this->phpci->executeCommand($cmd, $standard, $ignore, $tab_width, $encoding, $this->phpci->buildPath); } } From 05908905115c822b53be91accf97201081bc8802 Mon Sep 17 00:00:00 2001 From: Tobias Tom Date: Tue, 30 Jul 2013 19:45:27 +0200 Subject: [PATCH 010/933] Some fixes for subdirectory support. --- PHPCI/Application.php | 2 +- PHPCI/Controller/SessionController.php | 6 +++--- PHPCI/Controller/UserController.php | 2 +- assets/css/phpci.css | 8 ++++---- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/PHPCI/Application.php b/PHPCI/Application.php index 1c89c44e..bfae830c 100644 --- a/PHPCI/Application.php +++ b/PHPCI/Application.php @@ -65,7 +65,7 @@ class Application extends b8\Application $this->response->setContent(''); } else { $this->response = new RedirectResponse($this->response); - $this->response->setHeader('Location', '/session/login'); + $this->response->setHeader('Location', PHPCI_URL.'session/login'); } return false; diff --git a/PHPCI/Controller/SessionController.php b/PHPCI/Controller/SessionController.php index 214f9921..e2bb0aa1 100644 --- a/PHPCI/Controller/SessionController.php +++ b/PHPCI/Controller/SessionController.php @@ -21,7 +21,7 @@ class SessionController extends \PHPCI\Controller { public function init() { - $this->response->disableLayout(); + $this->response->disableLayout(); $this->_userStore = b8\Store\Factory::getStore('User'); } @@ -29,7 +29,7 @@ class SessionController extends \PHPCI\Controller * Handles user login (form and processing) */ public function login() - { + { if ($this->request->getMethod() == 'POST') { $user = $this->_userStore->getByEmail($this->getParam('email')); @@ -42,7 +42,7 @@ class SessionController extends \PHPCI\Controller $form = new b8\Form(); $form->setMethod('POST'); - $form->setAction('/session/login'); + $form->setAction(PHPCI_URL.'session/login'); $email = new b8\Form\Element\Email('email'); $email->setLabel('Email Address'); diff --git a/PHPCI/Controller/UserController.php b/PHPCI/Controller/UserController.php index 0a814b9c..92f1ed46 100644 --- a/PHPCI/Controller/UserController.php +++ b/PHPCI/Controller/UserController.php @@ -129,7 +129,7 @@ class UserController extends \PHPCI\Controller { $form = new Form(); $form->setMethod('POST'); - $form->setAction('/user/' . $type); + $form->setAction(PHPCI_URL.'user/' . $type); $form->addField(new Form\Element\Csrf('csrf')); $field = new Form\Element\Email('email'); diff --git a/assets/css/phpci.css b/assets/css/phpci.css index cfb886d4..71071625 100644 --- a/assets/css/phpci.css +++ b/assets/css/phpci.css @@ -57,22 +57,22 @@ td .label { margin-right: 5px; } .icon-build-ok { - background: url('/assets/img/icon-build-ok.png') no-repeat top left; + background: url('../img/icon-build-ok.png') no-repeat top left; } .icon-build-failed { - background: url('/assets/img/icon-build-failed.png') no-repeat top left; + background: url('../img/icon-build-failed.png') no-repeat top left; } .icon-build-pending { - background: url('/assets/img/icon-build-pending.png') no-repeat top left; + background: url('../img/icon-build-pending.png') no-repeat top left; } .icon-build-running { - background: url('/assets/img/icon-build-running.png') no-repeat top left; + background: url('../img/icon-build-running.png') no-repeat top left; } h3 From aa90f747fd36cc583b334246e2c16fa32e21ca3b Mon Sep 17 00:00:00 2001 From: Tobias Tom Date: Wed, 31 Jul 2013 08:08:19 +0200 Subject: [PATCH 011/933] Removed magix execution of composer. --- console | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/console b/console index f571c454..dd71d058 100755 --- a/console +++ b/console @@ -14,14 +14,9 @@ define('ENABLE_SHELL_PLUGIN', false); // If this is the first time ./console has been run, we probably don't have Composer or any of our dependencies yet. // So we need to install and run Composer. -if (!file_exists(PHPCI_DIR . 'vendor/autoload.php') || !file_exists(PHPCI_DIR . 'composer.phar')) { - print 'INSTALLING: Composer' . PHP_EOL; - file_put_contents(PHPCI_DIR . 'composerinstaller.php', file_get_contents('https://getcomposer.org/installer')); - shell_exec('php ' . escapeshellarg(PHPCI_DIR . 'composerinstaller.php')); - unlink(PHPCI_DIR . 'composerinstaller.php'); - - print 'RUNNING: Composer' . PHP_EOL; - shell_exec('php '.escapeshellarg(PHPCI_DIR.'composer.phar').' install'); +if (!file_exists(PHPCI_DIR . 'vendor/autoload.php')) { + file_put_contents('php://stderr', 'Please install PHPCI with "composer install" before using console'); + exit( 1 ); } require('bootstrap.php'); From 695d57c68cd86d5dd83d911c47b4045c9b3f1089 Mon Sep 17 00:00:00 2001 From: Tobias Tom Date: Wed, 31 Jul 2013 08:15:07 +0200 Subject: [PATCH 012/933] Added additional installation step. --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index aa1eff8a..7acf5f4c 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,8 @@ _**Please be aware that PHPCI is a beta-release project, so whilst it is very st ####Installing from Github: * Step 1: `git clone https://github.com/Block8/PHPCI.git` * Step 2: `cd PHPCI` -* Step 3: `chmod +x ./console && ./console phpci:install` +* Step 3: `composer install` +* Step 4: `chmod +x ./console && ./console phpci:install` * When prompted, enter your database host, username, password and the database name that PHPCI should use. * The script will attempt to create the database if it does not exist already. * If you intend to use the MySQL plugin to create / destroy databases, the user you entered above will need CREATE / DELETE permissions on the server. From e4685b9c297ea0f21c120aafb82d43d63e554740 Mon Sep 17 00:00:00 2001 From: Tobias Tom Date: Wed, 31 Jul 2013 08:55:19 +0200 Subject: [PATCH 013/933] Added initial version of the grunt plugin. --- PHPCI/Plugin/Grunt.php | 56 ++++++++++++++++++++++++++++++++++++++++++ README.md | 2 ++ 2 files changed, 58 insertions(+) create mode 100644 PHPCI/Plugin/Grunt.php diff --git a/PHPCI/Plugin/Grunt.php b/PHPCI/Plugin/Grunt.php new file mode 100644 index 00000000..892d8303 --- /dev/null +++ b/PHPCI/Plugin/Grunt.php @@ -0,0 +1,56 @@ + +* @package PHPCI +* @subpackage Plugins +*/ +class Grunt implements \PHPCI\Plugin +{ + protected $directory; + protected $task; + protected $preferDist; + protected $phpci; + protected $grunt; + protected $gruntfile; + + public function __construct(\PHPCI\Builder $phpci, array $options = array()) + { + $path = $phpci->buildPath; + $this->phpci = $phpci; + $this->directory = isset($options['directory']) ? $path . '/' . $options['directory'] : $path; + $this->task = isset($options['task']) ? $options['task'] : null; + $this->grunt = isset($options['grunt']) ? $options['grunt'] : 'grunt'; + $this->gruntfile = isset($options['gruntfile']) ? $options['gruntfile'] : 'Gruntfile.js'; + } + + /** + * Executes grunt and runs a specified command (e.g. install / update) + */ + public function execute() + { + // if npm does not work, we cannot use grunt, so we return false + if ( !$this->phpci->executeCommand( 'cd %s && npm install', $this->directory ) ) { + return false; + } + + // build the grunt command + $cmd = 'cd %s && ' . $this->grunt; + $cmd .= ' --no-color'; + $cmd .= ' --gruntfile %s'; + $cmd .= ' %s'; // the task that will be executed + + // and execute it + return $this->phpci->executeCommand($cmd, $this->directory, $this->gruntfile, $this->task); + } +} diff --git a/README.md b/README.md index aa1eff8a..2bdffefe 100644 --- a/README.md +++ b/README.md @@ -103,6 +103,8 @@ Similar to Travis CI, to support PHPCI in your project, you simply need to add a standard: "PSR2" php_cpd: allow_failures: true + grunt: + task: "build" complete: mysql: From 7fccd56c2b3a08504a4d901140ae922a9f11d450 Mon Sep 17 00:00:00 2001 From: Tobias Tom Date: Wed, 31 Jul 2013 11:30:20 +0200 Subject: [PATCH 014/933] Created one directory where all web-accessible files are located. --- {assets => public/assets}/css/bootstrap.min.css | 0 {assets => public/assets}/css/phpci.css | 0 {assets => public/assets}/img/build-failed.png | Bin {assets => public/assets}/img/build-ok.png | Bin .../assets}/img/glyphicons-halflings-white.png | Bin .../assets}/img/glyphicons-halflings.png | Bin {assets => public/assets}/img/icon-build-failed.png | Bin {assets => public/assets}/img/icon-build-ok.png | Bin .../assets}/img/icon-build-pending.png | Bin .../assets}/img/icon-build-running.png | Bin {assets => public/assets}/js/bootstrap.min.js | 0 {assets => public/assets}/js/phpci.js | 0 index.php => public/index.php | 2 +- 13 files changed, 1 insertion(+), 1 deletion(-) rename {assets => public/assets}/css/bootstrap.min.css (100%) rename {assets => public/assets}/css/phpci.css (100%) rename {assets => public/assets}/img/build-failed.png (100%) rename {assets => public/assets}/img/build-ok.png (100%) rename {assets => public/assets}/img/glyphicons-halflings-white.png (100%) rename {assets => public/assets}/img/glyphicons-halflings.png (100%) rename {assets => public/assets}/img/icon-build-failed.png (100%) rename {assets => public/assets}/img/icon-build-ok.png (100%) rename {assets => public/assets}/img/icon-build-pending.png (100%) rename {assets => public/assets}/img/icon-build-running.png (100%) rename {assets => public/assets}/js/bootstrap.min.js (100%) rename {assets => public/assets}/js/phpci.js (100%) rename index.php => public/index.php (91%) diff --git a/assets/css/bootstrap.min.css b/public/assets/css/bootstrap.min.css similarity index 100% rename from assets/css/bootstrap.min.css rename to public/assets/css/bootstrap.min.css diff --git a/assets/css/phpci.css b/public/assets/css/phpci.css similarity index 100% rename from assets/css/phpci.css rename to public/assets/css/phpci.css diff --git a/assets/img/build-failed.png b/public/assets/img/build-failed.png similarity index 100% rename from assets/img/build-failed.png rename to public/assets/img/build-failed.png diff --git a/assets/img/build-ok.png b/public/assets/img/build-ok.png similarity index 100% rename from assets/img/build-ok.png rename to public/assets/img/build-ok.png diff --git a/assets/img/glyphicons-halflings-white.png b/public/assets/img/glyphicons-halflings-white.png similarity index 100% rename from assets/img/glyphicons-halflings-white.png rename to public/assets/img/glyphicons-halflings-white.png diff --git a/assets/img/glyphicons-halflings.png b/public/assets/img/glyphicons-halflings.png similarity index 100% rename from assets/img/glyphicons-halflings.png rename to public/assets/img/glyphicons-halflings.png diff --git a/assets/img/icon-build-failed.png b/public/assets/img/icon-build-failed.png similarity index 100% rename from assets/img/icon-build-failed.png rename to public/assets/img/icon-build-failed.png diff --git a/assets/img/icon-build-ok.png b/public/assets/img/icon-build-ok.png similarity index 100% rename from assets/img/icon-build-ok.png rename to public/assets/img/icon-build-ok.png diff --git a/assets/img/icon-build-pending.png b/public/assets/img/icon-build-pending.png similarity index 100% rename from assets/img/icon-build-pending.png rename to public/assets/img/icon-build-pending.png diff --git a/assets/img/icon-build-running.png b/public/assets/img/icon-build-running.png similarity index 100% rename from assets/img/icon-build-running.png rename to public/assets/img/icon-build-running.png diff --git a/assets/js/bootstrap.min.js b/public/assets/js/bootstrap.min.js similarity index 100% rename from assets/js/bootstrap.min.js rename to public/assets/js/bootstrap.min.js diff --git a/assets/js/phpci.js b/public/assets/js/phpci.js similarity index 100% rename from assets/js/phpci.js rename to public/assets/js/phpci.js diff --git a/index.php b/public/index.php similarity index 91% rename from index.php rename to public/index.php index b4685c7d..134e9a7b 100644 --- a/index.php +++ b/public/index.php @@ -12,7 +12,7 @@ session_start(); error_reporting(E_ALL); ini_set('display_errors', 'on'); -require_once('bootstrap.php'); +require_once('../bootstrap.php'); $fc = new PHPCI\Application($config, new b8\Http\Request()); print $fc->handleRequest(); From 664f0d7d8eb2074701499ea1ad68798c2a1764af Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Wed, 31 Jul 2013 11:00:57 +0100 Subject: [PATCH 015/933] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3b08e3c2..917ec901 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ _**Please be aware that PHPCI is a beta-release project, so whilst it is very st **Apache Example**: RewriteEngine On - RewriteBase /path-to-phpci + RewriteBase /path-to-phpci/public RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule (.*)? index.php [L,E=PATH_INFO:/$1] From 8b5abc1f986406900b9e1bd6734804b84cc8ed84 Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Thu, 1 Aug 2013 11:55:10 +0100 Subject: [PATCH 016/933] Initial work on upgrading to Bootstrap v3 --- PHPCI/Controller/ProjectController.php | 11 +- PHPCI/Controller/SessionController.php | 4 +- PHPCI/Helper/Build.php | 24 + PHPCI/View/Build/view.phtml | 15 +- PHPCI/View/BuildsTable.phtml | 16 +- PHPCI/View/Index/index.phtml | 23 +- PHPCI/View/Project/view.phtml | 17 +- PHPCI/View/ProjectForm.phtml | 6 +- PHPCI/View/SummaryTable.phtml | 2 +- PHPCI/View/layout.phtml | 28 +- assets/css/bootstrap.min.css | 840 +------------------------ assets/js/bootstrap.min.js | 9 +- assets/js/phpci.js | 17 +- index.php | 1 + 14 files changed, 110 insertions(+), 903 deletions(-) create mode 100644 PHPCI/Helper/Build.php mode change 100755 => 100644 assets/css/bootstrap.min.css mode change 100755 => 100644 assets/js/bootstrap.min.js diff --git a/PHPCI/Controller/ProjectController.php b/PHPCI/Controller/ProjectController.php index 47894266..bd11f846 100644 --- a/PHPCI/Controller/ProjectController.php +++ b/PHPCI/Controller/ProjectController.php @@ -262,13 +262,13 @@ class ProjectController extends \PHPCI\Controller $field->setPattern('^(github|bitbucket|remote|local)'); $field->setOptions($options); $field->setLabel('Where is your project hosted?'); - $field->setClass('span4'); + $field->setClass('col-lg-4 form-control'); $form->addField($field); if (isset($_SESSION['github_token'])) { $field = new Form\Element\Select('github'); $field->setLabel('Choose a Github repository:'); - $field->setClass('span4'); + $field->setClass('col-lg-4 form-control'); $field->setOptions($this->getGithubRepositories()); $form->addField($field); } @@ -302,24 +302,25 @@ class ProjectController extends \PHPCI\Controller $field->setRequired(true); $field->setValidator($referenceValidator); $field->setLabel('Repository Name / URL (Remote) or Path (Local)'); - $field->setClass('span4'); + $field->setClass('col-lg-4 form-control'); $form->addField($field); $field = new Form\Element\Text('title'); $field->setRequired(true); $field->setLabel('Project Title'); - $field->setClass('span4'); + $field->setClass('col-lg-4 form-control'); $form->addField($field); $field = new Form\Element\TextArea('key'); $field->setRequired(false); $field->setLabel('Private key to use to access repository (leave blank for local and/or anonymous remotes)'); - $field->setClass('span7'); + $field->setClass('col-lg-7 form-control'); $field->setRows(6); $form->addField($field); $field = new Form\Element\Submit(); $field->setValue('Save Project'); + $field->setContainerClass('form-group'); $field->setClass('btn-success'); $form->addField($field); diff --git a/PHPCI/Controller/SessionController.php b/PHPCI/Controller/SessionController.php index 214f9921..b6fd0a43 100644 --- a/PHPCI/Controller/SessionController.php +++ b/PHPCI/Controller/SessionController.php @@ -47,13 +47,13 @@ class SessionController extends \PHPCI\Controller $email = new b8\Form\Element\Email('email'); $email->setLabel('Email Address'); $email->setRequired(true); - $email->setClass('span3'); + $email->setContainerClass('form-group'); $form->addField($email); $pwd = new b8\Form\Element\Password('password'); $pwd->setLabel('Password'); $pwd->setRequired(true); - $pwd->setClass('span3'); + $pwd->setContainerClass('form-group'); $form->addField($pwd); $pwd = new b8\Form\Element\Submit(); diff --git a/PHPCI/Helper/Build.php b/PHPCI/Helper/Build.php new file mode 100644 index 00000000..3aa7403f --- /dev/null +++ b/PHPCI/Helper/Build.php @@ -0,0 +1,24 @@ + + * @package PHPCI + * @subpackage Web + */ +class Build +{ + public function formatPluginName($name) + { + return str_replace('Php', 'PHP', ucwords(str_replace('_', ' ', $name))); + } +} diff --git a/PHPCI/View/Build/view.phtml b/PHPCI/View/Build/view.phtml index f57a1a45..25ea8809 100644 --- a/PHPCI/View/Build/view.phtml +++ b/PHPCI/View/Build/view.phtml @@ -4,21 +4,20 @@
-
-
-
-
+
diff --git a/PHPCI/View/BuildsTable.phtml b/PHPCI/View/BuildsTable.phtml index 34a5ca72..aa177832 100644 --- a/PHPCI/View/BuildsTable.phtml +++ b/PHPCI/View/BuildsTable.phtml @@ -48,28 +48,20 @@ switch($build->getStatus()) $plugins = array(); } if ( 0 === count($plugins) ) { - ?> - - - - $pluginstatus): $subcls = $pluginstatus?'label label-success':'label label-important'; - ?> - - - - + ?> Build()->formatPluginName($plugin); ?>
- + + + - - - + From 160097182f4cea7822d4b90c993d9a30517410ab Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Tue, 8 Oct 2013 16:28:46 +0100 Subject: [PATCH 073/933] New plugins screen --- PHPCI/Controller/PluginController.php | 161 +++++++++++++++++++++++ PHPCI/View/Home/index.phtml | 3 +- PHPCI/View/Plugin/index.phtml | 180 ++++++++++++++++++++++++++ public/assets/css/phpci.css | 16 ++- 4 files changed, 358 insertions(+), 2 deletions(-) create mode 100644 PHPCI/Controller/PluginController.php create mode 100644 PHPCI/View/Plugin/index.phtml diff --git a/PHPCI/Controller/PluginController.php b/PHPCI/Controller/PluginController.php new file mode 100644 index 00000000..2d9d74fe --- /dev/null +++ b/PHPCI/Controller/PluginController.php @@ -0,0 +1,161 @@ + + * @package PHPCI + * @subpackage Web + */ +class PluginController extends \PHPCI\Controller +{ + protected $required = array( + 'block8/b8framework', + 'ircmaxell/password-compat', + 'swiftmailer/swiftmailer', + 'symfony/yaml', + 'symfony/console' + ); + + protected $canInstall; + protected $composerPath; + + public function init() + { + parent::init(); + $this->canInstall = function_exists('shell_exec'); + + if ($this->canInstall) { + $this->composerPath = $this->findBinary(array('composer', 'composer.phar')); + + if (!$this->composerPath) { + $this->canInstall = false; + } + } + } + + public function index() + { + $this->view->canWrite = is_writable(APPLICATION_PATH . 'composer.json'); + $this->view->canInstall = $this->canInstall; + $this->view->required = $this->required; + + $json = $this->getComposerJson(); + $this->view->installed = $json['require']; + $this->view->suggested = $json['suggest']; + + return $this->view->render(); + } + + public function remove() + { + $package = $this->getParam('package', null); + $json = $this->getComposerJson(); + + if (!in_array($package, $this->required)) { + unset($json['require'][$package]); + $this->setComposerJson($json); + + if ($this->canInstall) { + $res = shell_exec($this->composerPath . ' update --working-dir=' . APPLICATION_PATH . ' > /dev/null 2>&1 &'); + } + + header('Location: ' . PHPCI_URL . 'plugin?r=' . $package); + die; + } + + header('Location: ' . PHPCI_URL); + die; + } + + public function install() + { + $package = $this->getParam('package', null); + $version = $this->getParam('version', '*'); + + $json = $this->getComposerJson(); + $json['require'][$package] = $version; + $this->setComposerJson($json); + + if ($this->canInstall) { + $res = shell_exec($this->composerPath . ' update --working-dir=' . APPLICATION_PATH . ' > /dev/null 2>&1 &'); + + header('Location: ' . PHPCI_URL . 'plugin?i=' . $package); + die; + } + + header('Location: ' . PHPCI_URL . 'plugin?w=' . $package); + die; + } + + protected function getComposerJson() + { + $json = file_get_contents(APPLICATION_PATH . 'composer.json'); + return json_decode($json, true); + } + + protected function setComposerJson($array) + { + $json = json_encode($array); + file_put_contents(APPLICATION_PATH . 'composer.json', $json); + } + + protected function findBinary($binary) + { + if (is_string($binary)) { + $binary = array($binary); + } + + foreach ($binary as $bin) { + // Check project root directory: + if (is_file(APPLICATION_PATH . $bin)) { + return APPLICATION_PATH . $bin; + } + + // Check Composer bin dir: + if (is_file(APPLICATION_PATH . 'vendor/bin/' . $bin)) { + return APPLICATION_PATH . 'vendor/bin/' . $bin; + } + + // Use "which" + $which = trim(shell_exec('which ' . $bin)); + + if (!empty($which)) { + return $which; + } + } + + return null; + } + + public function packagistSearch() + { + $q = $this->getParam('q', ''); + $http = new \b8\HttpClient(); + $http->setHeaders(array('User-Agent: PHPCI/1.0 (+http://www.phptesting.org)')); + $res = $http->get('https://packagist.org/search.json', array('q' => $q)); + + die(json_encode($res['body'])); + } + + public function packagistVersions() + { + $name = $this->getParam('p', ''); + $http = new \b8\HttpClient(); + $http->setHeaders(array('User-Agent: PHPCI/1.0 (+http://www.phptesting.org)')); + $res = $http->get('https://packagist.org/packages/'.$name.'.json'); + + die(json_encode($res['body'])); + } +} diff --git a/PHPCI/View/Home/index.phtml b/PHPCI/View/Home/index.phtml index a7024590..1e186694 100644 --- a/PHPCI/View/Home/index.phtml +++ b/PHPCI/View/Home/index.phtml @@ -5,7 +5,8 @@
diff --git a/PHPCI/View/Plugin/index.phtml b/PHPCI/View/Plugin/index.phtml new file mode 100644 index 00000000..a21c4a35 --- /dev/null +++ b/PHPCI/View/Plugin/index.phtml @@ -0,0 +1,180 @@ +

Plugins

+ +

PHPCI cannot automatically install/remove plugins for you, as the shell_exec() + function is disabled. PHPCI will update composer.json for you, but you will need to run Composer manually to make the changes.

+ + + +

PHPCI cannot update composer.json for you as it is not writable.

+ + + +

has been removed.

+ + + +

has been installed.

+ + + +

has been added to composer.json and will be installed next time you run composer update.

+ + +
+

Installed Plugins

+ +
- View + View User()->getIsAdmin()): ?> -
-
-
-
-
+ +
+

Project Overview

@@ -49,7 +50,7 @@ - + @@ -66,7 +67,7 @@ - + diff --git a/PHPCI/View/Project/view.phtml b/PHPCI/View/Project/view.phtml index 77d488fc..3067f5ed 100644 --- a/PHPCI/View/Project/view.phtml +++ b/PHPCI/View/Project/view.phtml @@ -3,13 +3,13 @@
-
-
-
-
+
Last Success Last Failure Success/Failures
Commit Branch Status
@@ -49,7 +48,7 @@ - + @@ -59,7 +58,7 @@
    '; + print '
      '; $pages = ceil($total / 10); $pages = $pages == 0 ? 1 : $pages; diff --git a/PHPCI/View/ProjectForm.phtml b/PHPCI/View/ProjectForm.phtml index e52cbd9e..d683bff1 100644 --- a/PHPCI/View/ProjectForm.phtml +++ b/PHPCI/View/ProjectForm.phtml @@ -3,7 +3,7 @@
    -
    +

    To make it easier to get started, we've generated a public / private key pair for you to use for this project. To use it, just add the following public key to the "deploy keys" section of your repository settings on Github / Bitbucket.

    @@ -16,8 +16,8 @@
    -
    - +
    +
    diff --git a/PHPCI/View/SummaryTable.phtml b/PHPCI/View/SummaryTable.phtml index 3db900b2..416f61e0 100644 --- a/PHPCI/View/SummaryTable.phtml +++ b/PHPCI/View/SummaryTable.phtml @@ -109,6 +109,6 @@ foreach($projects as $projectId => $project):
- + \ No newline at end of file diff --git a/PHPCI/View/layout.phtml b/PHPCI/View/layout.phtml index 2db749d3..30d789d5 100644 --- a/PHPCI/View/layout.phtml +++ b/PHPCI/View/layout.phtml @@ -15,19 +15,27 @@ ').addClass(data.plugins[plugin] ? 'success' : 'error'); - var name = $('",e.document[0]).attr("colspan",t(this).attr("colspan")||1).appendTo(n)}):"img"===s&&n.attr("src",e.currentItem.attr("src")),i||n.css("visibility","hidden"),n},update:function(t,n){(!i||s.forcePlaceholderSize)&&(n.height()||n.height(e.currentItem.innerHeight()-parseInt(e.currentItem.css("paddingTop")||0,10)-parseInt(e.currentItem.css("paddingBottom")||0,10)),n.width()||n.width(e.currentItem.innerWidth()-parseInt(e.currentItem.css("paddingLeft")||0,10)-parseInt(e.currentItem.css("paddingRight")||0,10)))}}),e.placeholder=t(s.placeholder.element.call(e.element,e.currentItem)),e.currentItem.after(e.placeholder),s.placeholder.update(e,e.placeholder)},_contactContainers:function(s){var n,o,a,r,h,l,c,u,d,p,f=null,g=null;for(n=this.containers.length-1;n>=0;n--)if(!t.contains(this.currentItem[0],this.containers[n].element[0]))if(this._intersectsWith(this.containers[n].containerCache)){if(f&&t.contains(this.containers[n].element[0],f.element[0]))continue;f=this.containers[n],g=n}else this.containers[n].containerCache.over&&(this.containers[n]._trigger("out",s,this._uiHash(this)),this.containers[n].containerCache.over=0);if(f)if(1===this.containers.length)this.containers[g].containerCache.over||(this.containers[g]._trigger("over",s,this._uiHash(this)),this.containers[g].containerCache.over=1);else{for(a=1e4,r=null,p=f.floating||i(this.currentItem),h=p?"left":"top",l=p?"width":"height",c=this.positionAbs[h]+this.offset.click[h],o=this.items.length-1;o>=0;o--)t.contains(this.containers[g].element[0],this.items[o].item[0])&&this.items[o].item[0]!==this.currentItem[0]&&(!p||e(this.positionAbs.top+this.offset.click.top,this.items[o].top,this.items[o].height))&&(u=this.items[o].item.offset()[h],d=!1,Math.abs(u-c)>Math.abs(u+this.items[o][l]-c)&&(d=!0,u+=this.items[o][l]),a>Math.abs(u-c)&&(a=Math.abs(u-c),r=this.items[o],this.direction=d?"up":"down"));if(!r&&!this.options.dropOnEmpty)return;if(this.currentContainer===this.containers[g])return;r?this._rearrange(s,r,null,!0):this._rearrange(s,null,this.containers[g].element,!0),this._trigger("change",s,this._uiHash()),this.containers[g]._trigger("change",s,this._uiHash(this)),this.currentContainer=this.containers[g],this.options.placeholder.update(this.currentContainer,this.placeholder),this.containers[g]._trigger("over",s,this._uiHash(this)),this.containers[g].containerCache.over=1}},_createHelper:function(e){var i=this.options,s=t.isFunction(i.helper)?t(i.helper.apply(this.element[0],[e,this.currentItem])):"clone"===i.helper?this.currentItem.clone():this.currentItem;return s.parents("body").length||t("parent"!==i.appendTo?i.appendTo:this.currentItem[0].parentNode)[0].appendChild(s[0]),s[0]===this.currentItem[0]&&(this._storedCSS={width:this.currentItem[0].style.width,height:this.currentItem[0].style.height,position:this.currentItem.css("position"),top:this.currentItem.css("top"),left:this.currentItem.css("left")}),(!s[0].style.width||i.forceHelperSize)&&s.width(this.currentItem.width()),(!s[0].style.height||i.forceHelperSize)&&s.height(this.currentItem.height()),s},_adjustOffsetFromHelper:function(e){"string"==typeof e&&(e=e.split(" ")),t.isArray(e)&&(e={left:+e[0],top:+e[1]||0}),"left"in e&&(this.offset.click.left=e.left+this.margins.left),"right"in e&&(this.offset.click.left=this.helperProportions.width-e.right+this.margins.left),"top"in e&&(this.offset.click.top=e.top+this.margins.top),"bottom"in e&&(this.offset.click.top=this.helperProportions.height-e.bottom+this.margins.top)},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var e=this.offsetParent.offset();return"absolute"===this.cssPosition&&this.scrollParent[0]!==document&&t.contains(this.scrollParent[0],this.offsetParent[0])&&(e.left+=this.scrollParent.scrollLeft(),e.top+=this.scrollParent.scrollTop()),(this.offsetParent[0]===document.body||this.offsetParent[0].tagName&&"html"===this.offsetParent[0].tagName.toLowerCase()&&t.ui.ie)&&(e={top:0,left:0}),{top:e.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:e.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if("relative"===this.cssPosition){var t=this.currentItem.position();return{top:t.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:t.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.currentItem.css("marginLeft"),10)||0,top:parseInt(this.currentItem.css("marginTop"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var e,i,s,n=this.options;"parent"===n.containment&&(n.containment=this.helper[0].parentNode),("document"===n.containment||"window"===n.containment)&&(this.containment=[0-this.offset.relative.left-this.offset.parent.left,0-this.offset.relative.top-this.offset.parent.top,t("document"===n.containment?document:window).width()-this.helperProportions.width-this.margins.left,(t("document"===n.containment?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top]),/^(document|window|parent)$/.test(n.containment)||(e=t(n.containment)[0],i=t(n.containment).offset(),s="hidden"!==t(e).css("overflow"),this.containment=[i.left+(parseInt(t(e).css("borderLeftWidth"),10)||0)+(parseInt(t(e).css("paddingLeft"),10)||0)-this.margins.left,i.top+(parseInt(t(e).css("borderTopWidth"),10)||0)+(parseInt(t(e).css("paddingTop"),10)||0)-this.margins.top,i.left+(s?Math.max(e.scrollWidth,e.offsetWidth):e.offsetWidth)-(parseInt(t(e).css("borderLeftWidth"),10)||0)-(parseInt(t(e).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left,i.top+(s?Math.max(e.scrollHeight,e.offsetHeight):e.offsetHeight)-(parseInt(t(e).css("borderTopWidth"),10)||0)-(parseInt(t(e).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top])},_convertPositionTo:function(e,i){i||(i=this.position);var s="absolute"===e?1:-1,n="absolute"!==this.cssPosition||this.scrollParent[0]!==document&&t.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,o=/(html|body)/i.test(n[0].tagName);return{top:i.top+this.offset.relative.top*s+this.offset.parent.top*s-("fixed"===this.cssPosition?-this.scrollParent.scrollTop():o?0:n.scrollTop())*s,left:i.left+this.offset.relative.left*s+this.offset.parent.left*s-("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():o?0:n.scrollLeft())*s}},_generatePosition:function(e){var i,s,n=this.options,o=e.pageX,a=e.pageY,r="absolute"!==this.cssPosition||this.scrollParent[0]!==document&&t.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,h=/(html|body)/i.test(r[0].tagName);return"relative"!==this.cssPosition||this.scrollParent[0]!==document&&this.scrollParent[0]!==this.offsetParent[0]||(this.offset.relative=this._getRelativeOffset()),this.originalPosition&&(this.containment&&(e.pageX-this.offset.click.leftthis.containment[2]&&(o=this.containment[2]+this.offset.click.left),e.pageY-this.offset.click.top>this.containment[3]&&(a=this.containment[3]+this.offset.click.top)),n.grid&&(i=this.originalPageY+Math.round((a-this.originalPageY)/n.grid[1])*n.grid[1],a=this.containment?i-this.offset.click.top>=this.containment[1]&&i-this.offset.click.top<=this.containment[3]?i:i-this.offset.click.top>=this.containment[1]?i-n.grid[1]:i+n.grid[1]:i,s=this.originalPageX+Math.round((o-this.originalPageX)/n.grid[0])*n.grid[0],o=this.containment?s-this.offset.click.left>=this.containment[0]&&s-this.offset.click.left<=this.containment[2]?s:s-this.offset.click.left>=this.containment[0]?s-n.grid[0]:s+n.grid[0]:s)),{top:a-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+("fixed"===this.cssPosition?-this.scrollParent.scrollTop():h?0:r.scrollTop()),left:o-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():h?0:r.scrollLeft())}},_rearrange:function(t,e,i,s){i?i[0].appendChild(this.placeholder[0]):e.item[0].parentNode.insertBefore(this.placeholder[0],"down"===this.direction?e.item[0]:e.item[0].nextSibling),this.counter=this.counter?++this.counter:1;var n=this.counter;this._delay(function(){n===this.counter&&this.refreshPositions(!s)})},_clear:function(t,e){this.reverting=!1;var i,s=[];if(!this._noFinalSort&&this.currentItem.parent().length&&this.placeholder.before(this.currentItem),this._noFinalSort=null,this.helper[0]===this.currentItem[0]){for(i in this._storedCSS)("auto"===this._storedCSS[i]||"static"===this._storedCSS[i])&&(this._storedCSS[i]="");this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper")}else this.currentItem.show();for(this.fromOutside&&!e&&s.push(function(t){this._trigger("receive",t,this._uiHash(this.fromOutside))}),!this.fromOutside&&this.domPosition.prev===this.currentItem.prev().not(".ui-sortable-helper")[0]&&this.domPosition.parent===this.currentItem.parent()[0]||e||s.push(function(t){this._trigger("update",t,this._uiHash())}),this!==this.currentContainer&&(e||(s.push(function(t){this._trigger("remove",t,this._uiHash())}),s.push(function(t){return function(e){t._trigger("receive",e,this._uiHash(this))}}.call(this,this.currentContainer)),s.push(function(t){return function(e){t._trigger("update",e,this._uiHash(this))}}.call(this,this.currentContainer)))),i=this.containers.length-1;i>=0;i--)e||s.push(function(t){return function(e){t._trigger("deactivate",e,this._uiHash(this))}}.call(this,this.containers[i])),this.containers[i].containerCache.over&&(s.push(function(t){return function(e){t._trigger("out",e,this._uiHash(this))}}.call(this,this.containers[i])),this.containers[i].containerCache.over=0);if(this.storedCursor&&(this.document.find("body").css("cursor",this.storedCursor),this.storedStylesheet.remove()),this._storedOpacity&&this.helper.css("opacity",this._storedOpacity),this._storedZIndex&&this.helper.css("zIndex","auto"===this._storedZIndex?"":this._storedZIndex),this.dragging=!1,this.cancelHelperRemoval){if(!e){for(this._trigger("beforeStop",t,this._uiHash()),i=0;s.length>i;i++)s[i].call(this,t);this._trigger("stop",t,this._uiHash())}return this.fromOutside=!1,!1}if(e||this._trigger("beforeStop",t,this._uiHash()),this.placeholder[0].parentNode.removeChild(this.placeholder[0]),this.helper[0]!==this.currentItem[0]&&this.helper.remove(),this.helper=null,!e){for(i=0;s.length>i;i++)s[i].call(this,t);this._trigger("stop",t,this._uiHash())}return this.fromOutside=!1,!0},_trigger:function(){t.Widget.prototype._trigger.apply(this,arguments)===!1&&this.cancel()},_uiHash:function(e){var i=e||this;return{helper:i.helper,placeholder:i.placeholder||t([]),position:i.position,originalPosition:i.originalPosition,offset:i.positionAbs,item:i.currentItem,sender:e?e.element:null}}})})(jQuery); \ No newline at end of file diff --git a/public/assets/js/phpci.js b/public/assets/js/phpci.js index 5272daa9..c14c41bc 100644 --- a/public/assets/js/phpci.js +++ b/public/assets/js/phpci.js @@ -13,95 +13,6 @@ function confirmDelete(url) } } -/** -* Updates the build screen. Called at regular intervals on /build/view/X -*/ -function updateBuildView(data) -{ - $('#status').attr('class', 'alert'); - - var cls; - var msg; - - switch(data.status) - { - case 0: - cls = 'alert-info'; - msg = 'This build has not yet started.'; - break; - - case 1: - cls = 'alert-warning'; - msg = 'This build is in progress.'; - break; - - case 2: - cls = 'alert-success'; - msg = 'This build was successful!'; - break; - - case 3: - cls = 'alert-error'; - msg = 'This build has failed.'; - break; - } - - $('#status').addClass(cls).text(msg); - - if(data.created) - { - $('#created').text(data.created); - } - else - { - $('#created').text('Not created yet.'); - } - - if(data.started) - { - $('#started').text(data.started); - } - else - { - $('#started').text('Not started yet.'); - } - - if(data.finished) - { - $('#finished').text(data.finished); - } - else - { - $('#finished').text('Not finished yet.'); - } - - if(data.plugins) - { - $('#plugins').empty(); - - for(var plugin in data.plugins) - { - var row = $('').addClass(data.plugins[plugin] ? 'success' : 'error'); - var name = $(''); - var col = $('
Commit Branch Status
/buildbuild now »
').html('' + plugin + ''); + var name = $('').html('' + formatPluginName(plugin) + ''); var status = $('').text(data.plugins[plugin] ? 'OK' : 'Failed'); row.append(name); @@ -168,4 +168,19 @@ function setupProjectForm() $('#element-token').val(''); } }); +} + + +function formatPluginName (name) { + name = name.replace(new RegExp('_', 'g'), ' '); + name = ucwords(name); + name = name.replace(new RegExp('Php', 'g'), 'PHP'); + + return name; +} + +function ucwords (str) { + return (str + '').replace(/^([a-z\u00E0-\u00FC])|\s+([a-z\u00E0-\u00FC])/g, function ($1) { + return $1.toUpperCase(); + }); } \ No newline at end of file diff --git a/index.php b/index.php index b4685c7d..a0b81e9d 100644 --- a/index.php +++ b/index.php @@ -16,3 +16,4 @@ require_once('bootstrap.php'); $fc = new PHPCI\Application($config, new b8\Http\Request()); print $fc->handleRequest(); + From eac86ff2bd6d06699bffcd16c11d271cfd536e5e Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Wed, 31 Jul 2013 21:04:34 +0100 Subject: [PATCH 017/933] Finishing updates to make PHPCI use Bootstrap v3, as per issue #99 --- PHPCI/Controller/ProjectController.php | 15 ++++++++++----- PHPCI/Controller/SessionController.php | 2 ++ PHPCI/Controller/UserController.php | 10 +++++++--- PHPCI/View/ProjectForm.phtml | 2 +- PHPCI/View/User/index.phtml | 16 +++++++--------- PHPCI/View/UserForm.phtml | 4 ++-- bootstrap.php | 23 ----------------------- 7 files changed, 29 insertions(+), 43 deletions(-) diff --git a/PHPCI/Controller/ProjectController.php b/PHPCI/Controller/ProjectController.php index bd11f846..3406cbd6 100644 --- a/PHPCI/Controller/ProjectController.php +++ b/PHPCI/Controller/ProjectController.php @@ -262,13 +262,15 @@ class ProjectController extends \PHPCI\Controller $field->setPattern('^(github|bitbucket|remote|local)'); $field->setOptions($options); $field->setLabel('Where is your project hosted?'); - $field->setClass('col-lg-4 form-control'); + $field->setClass('form-control'); + $field->setContainerClass('form-group'); $form->addField($field); if (isset($_SESSION['github_token'])) { $field = new Form\Element\Select('github'); $field->setLabel('Choose a Github repository:'); - $field->setClass('col-lg-4 form-control'); + $field->setClass('form-control'); + $field->setContainerClass('form-group'); $field->setOptions($this->getGithubRepositories()); $form->addField($field); } @@ -302,19 +304,22 @@ class ProjectController extends \PHPCI\Controller $field->setRequired(true); $field->setValidator($referenceValidator); $field->setLabel('Repository Name / URL (Remote) or Path (Local)'); - $field->setClass('col-lg-4 form-control'); + $field->setClass('form-control'); + $field->setContainerClass('form-group'); $form->addField($field); $field = new Form\Element\Text('title'); $field->setRequired(true); $field->setLabel('Project Title'); - $field->setClass('col-lg-4 form-control'); + $field->setClass('form-control'); + $field->setContainerClass('form-group'); $form->addField($field); $field = new Form\Element\TextArea('key'); $field->setRequired(false); $field->setLabel('Private key to use to access repository (leave blank for local and/or anonymous remotes)'); - $field->setClass('col-lg-7 form-control'); + $field->setClass('form-control'); + $field->setContainerClass('form-group'); $field->setRows(6); $form->addField($field); diff --git a/PHPCI/Controller/SessionController.php b/PHPCI/Controller/SessionController.php index 4dd87d81..af13cfc4 100644 --- a/PHPCI/Controller/SessionController.php +++ b/PHPCI/Controller/SessionController.php @@ -48,12 +48,14 @@ class SessionController extends \PHPCI\Controller $email->setLabel('Email Address'); $email->setRequired(true); $email->setContainerClass('form-group'); + $email->setClass('form-control'); $form->addField($email); $pwd = new b8\Form\Element\Password('password'); $pwd->setLabel('Password'); $pwd->setRequired(true); $pwd->setContainerClass('form-group'); + $pwd->setClass('form-control'); $form->addField($pwd); $pwd = new b8\Form\Element\Submit(); diff --git a/PHPCI/Controller/UserController.php b/PHPCI/Controller/UserController.php index 92f1ed46..c866fc52 100644 --- a/PHPCI/Controller/UserController.php +++ b/PHPCI/Controller/UserController.php @@ -135,25 +135,29 @@ class UserController extends \PHPCI\Controller $field = new Form\Element\Email('email'); $field->setRequired(true); $field->setLabel('Email Address'); - $field->setClass('span4'); + $field->setClass('form-control'); + $field->setContainerClass('form-group'); $form->addField($field); $field = new Form\Element\Text('name'); $field->setRequired(true); $field->setLabel('Name'); - $field->setClass('span4'); + $field->setClass('form-control'); + $field->setContainerClass('form-group'); $form->addField($field); $field = new Form\Element\Password('password'); $field->setRequired(true); $field->setLabel('Password' . ($type == 'edit' ? ' (leave blank to keep current password)' : '')); - $field->setClass('span4'); + $field->setClass('form-control'); + $field->setContainerClass('form-group'); $form->addField($field); $field = new Form\Element\Checkbox('admin'); $field->setRequired(false); $field->setCheckedValue(1); $field->setLabel('Is this user an administrator?'); + $field->setContainerClass('form-group'); $form->addField($field); $field = new Form\Element\Submit(); diff --git a/PHPCI/View/ProjectForm.phtml b/PHPCI/View/ProjectForm.phtml index d683bff1..f5c43e77 100644 --- a/PHPCI/View/ProjectForm.phtml +++ b/PHPCI/View/ProjectForm.phtml @@ -17,7 +17,7 @@
- +
diff --git a/PHPCI/View/User/index.phtml b/PHPCI/View/User/index.phtml index 9ac17fe7..318cfad6 100644 --- a/PHPCI/View/User/index.phtml +++ b/PHPCI/View/User/index.phtml @@ -3,25 +3,23 @@
-
-
-
-
+
- + @@ -47,8 +45,8 @@ @@ -71,4 +71,4 @@ switch($build->getStatus()) - \ No newline at end of file + diff --git a/PHPCI/View/SummaryTable.phtml b/PHPCI/View/SummaryTable.phtml index 416f61e0..d927e6af 100644 --- a/PHPCI/View/SummaryTable.phtml +++ b/PHPCI/View/SummaryTable.phtml @@ -60,12 +60,12 @@ foreach($projects as $projectId => $project): switch($project['lastbuildstatus']) { case 0: - $cls = 'info'; + $cls = 'active'; $status = 'Pending'; break; case 1: - $cls = 'warning'; + $cls = 'danger'; $status = 'Running'; break; @@ -81,7 +81,7 @@ foreach($projects as $projectId => $project): } $health = ($project['health'] <= 0 ? 'Stormy': ($project['successes'] < $project['count']? 'Overcast': 'Sunny')); - $subcls = ($project['health'] <= 0 ? 'important': ($project['successes'] < $project['count']? 'warning': 'success')); + $subcls = ($project['health'] <= 0 ? 'danger': ($project['successes'] < $project['count']? 'warning': 'success')); ?> - \ No newline at end of file + diff --git a/PHPCI/View/User/index.phtml b/PHPCI/View/User/index.phtml index 318cfad6..aacffcf0 100644 --- a/PHPCI/View/User/index.phtml +++ b/PHPCI/View/User/index.phtml @@ -60,4 +60,4 @@
Email Address Name Administrator
User()->getIsAdmin()): ?>
- Edit -
-
+

Fill in the form to the right to add a new user.

@@ -12,7 +12,7 @@
-
+
\ No newline at end of file diff --git a/bootstrap.php b/bootstrap.php index 9cd035e9..175bba00 100644 --- a/bootstrap.php +++ b/bootstrap.php @@ -43,29 +43,6 @@ $conf['b8']['view']['path'] = dirname(__FILE__) . '/PHPCI/View/'; $config = new b8\Config($conf); -if (file_exists(APPLICATION_PATH . 'config.php')) { - require(APPLICATION_PATH . 'config.php'); - - $conf = $config->get(null); - unset($conf['b8']['app']); - unset($conf['b8']['view']); - - $conf['phpci']['url'] = $conf['install_url']; - - if (isset($conf['github_app'])) { - $conf['phpci']['github'] = $conf['github_app']; - } - - unset($conf['install_url']); - unset($conf['github_app']); - - $dumper = new Symfony\Component\Yaml\Dumper(); - $yaml = $dumper->dump($conf); - - file_put_contents(APPLICATION_PATH . 'PHPCI/config.yml', $yaml); - unlink(APPLICATION_PATH . 'config.php'); -} - if (file_exists(APPLICATION_PATH . 'PHPCI/config.yml')) { $config->loadYaml(APPLICATION_PATH . 'PHPCI/config.yml'); From 9bb21fc01a471a4bb5d6b5e9bed76ea5da7ff76c Mon Sep 17 00:00:00 2001 From: Alex Russell Date: Thu, 1 Aug 2013 16:37:21 +0100 Subject: [PATCH 018/933] Fixed bootstrap table and label classes (mainly the danger ones) --- PHPCI/View/BuildsTable.phtml | 10 +++++----- PHPCI/View/SummaryTable.phtml | 8 ++++---- PHPCI/View/User/index.phtml | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/PHPCI/View/BuildsTable.phtml b/PHPCI/View/BuildsTable.phtml index aa177832..98cdce91 100644 --- a/PHPCI/View/BuildsTable.phtml +++ b/PHPCI/View/BuildsTable.phtml @@ -10,7 +10,7 @@ switch($build->getStatus()) { case 0: - $cls = 'info'; + $cls = 'active'; $subcls = 'info'; $status = 'Pending'; @@ -29,8 +29,8 @@ switch($build->getStatus()) break; case 3: - $cls = 'error'; - $subcls = 'important'; + $cls = 'danger'; + $subcls = 'danger'; $status = 'Failed'; break; } @@ -53,7 +53,7 @@ switch($build->getStatus()) ?> $pluginstatus): - $subcls = $pluginstatus?'label label-success':'label label-important'; + $subcls = $pluginstatus?'label label-success':'label label-danger'; ?> Build()->formatPluginName($plugin); ?>
@@ -111,4 +111,4 @@ foreach($projects as $projectId => $project): / build now »
-
\ No newline at end of file +
From 0723c5d964fa9dd4e436f1e8aed254887f3abb4f Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Fri, 2 Aug 2013 08:54:28 +0100 Subject: [PATCH 019/933] Updating PHPCI to use its new logo --- PHPCI/Controller/SessionController.php | 2 +- PHPCI/View/Session/login.phtml | 56 +++++++++++++++++++++---- PHPCI/View/layout.phtml | 2 +- public/assets/css/phpci.css | 4 ++ public/assets/img/logo-large.png | Bin 0 -> 4373 bytes public/assets/img/logo.png | Bin 0 -> 1711 bytes 6 files changed, 53 insertions(+), 11 deletions(-) create mode 100644 public/assets/img/logo-large.png create mode 100644 public/assets/img/logo.png diff --git a/PHPCI/Controller/SessionController.php b/PHPCI/Controller/SessionController.php index af13cfc4..06773305 100644 --- a/PHPCI/Controller/SessionController.php +++ b/PHPCI/Controller/SessionController.php @@ -59,7 +59,7 @@ class SessionController extends \PHPCI\Controller $form->addField($pwd); $pwd = new b8\Form\Element\Submit(); - $pwd->setValue('Login »'); + $pwd->setValue('Log in »'); $pwd->setClass('btn-success'); $form->addField($pwd); diff --git a/PHPCI/View/Session/login.phtml b/PHPCI/View/Session/login.phtml index 4d48892e..168d9625 100644 --- a/PHPCI/View/Session/login.phtml +++ b/PHPCI/View/Session/login.phtml @@ -1,7 +1,7 @@ - Log in + Log in to PHPCI @@ -11,39 +11,77 @@
+
- +
diff --git a/PHPCI/View/layout.phtml b/PHPCI/View/layout.phtml index 30d789d5..7dda7489 100644 --- a/PHPCI/View/layout.phtml +++ b/PHPCI/View/layout.phtml @@ -22,7 +22,7 @@ - PHPCI 1.0 Beta + +
- - - - - - - - - - - - - - - - -
Build CreatedBuild StartedBuild Finished
- - - - - - - - - - - - -
Build Log
-
+
+
- getStatus() == 0 || $build->getStatus() == 1 || true): ?> - setInterval(function() - { - $.getJSON('build/data/getId(); ?>', updateBuildView); - }, 10000); - +'; +} +?> - $(function() { - updateBuildView(window.initial); - - $('#delete-build').on('click', function (e) { - e.preventDefault(); - confirmDelete("build/delete/getId(); ?>"); - }); - }); + \ No newline at end of file diff --git a/PHPCI/View/Home/index.phtml b/PHPCI/View/Home/index.phtml index 5c645028..a7024590 100644 --- a/PHPCI/View/Home/index.phtml +++ b/PHPCI/View/Home/index.phtml @@ -41,7 +41,8 @@
-

Project Overview

+
+

Project Overview

@@ -57,8 +58,10 @@
+
-

Last 5 Builds

+
+

Last 5 Builds

@@ -74,6 +77,7 @@
+
diff --git a/PHPCI/View/ProjectForm.phtml b/PHPCI/View/ProjectForm.phtml index d59dc8c3..26f4727e 100644 --- a/PHPCI/View/ProjectForm.phtml +++ b/PHPCI/View/ProjectForm.phtml @@ -17,7 +17,9 @@
+
+
diff --git a/PHPCI/View/UserForm.phtml b/PHPCI/View/UserForm.phtml index ea1ae663..40ddcc19 100644 --- a/PHPCI/View/UserForm.phtml +++ b/PHPCI/View/UserForm.phtml @@ -13,6 +13,8 @@
+
+
\ No newline at end of file diff --git a/PHPCI/View/layout.phtml b/PHPCI/View/layout.phtml index a8ed5f79..a6fbfb36 100644 --- a/PHPCI/View/layout.phtml +++ b/PHPCI/View/layout.phtml @@ -9,9 +9,16 @@ + + + - - + + + + + +
 
').html('' + formatPluginName(plugin) + ''); - var status = $('').text(data.plugins[plugin] ? 'OK' : 'Failed'); - - row.append(name); - row.append(status); - $('#plugins').append(row); - } - } - else - { - var row = $('
').attr('colspan', 2).text('No plugins have run yet.'); - - row.append(col); - $('#plugins').empty().append(row); - } - - $('#log').html(data.log); -} - /** * Used to initialise the project form: */ @@ -170,17 +81,129 @@ function setupProjectForm() }); } +var PHPCIObject = Class.extend({ + buildId: null, + plugins: {}, + observers: {}, + buildData: {}, + queries: {}, + updateInterval: null, -function formatPluginName (name) { - name = name.replace(new RegExp('_', 'g'), ' '); - name = ucwords(name); - name = name.replace(new RegExp('Php', 'g'), 'PHP'); + init: function(build) { + this.buildId = build; + this.registerQuery('build-updated', 10); + }, - return name; -} + registerQuery: function(name, seconds, query) { + var self = this; + var uri = 'build/meta/' + self.buildId; + var query = query || {}; -function ucwords (str) { - return (str + '').replace(/^([a-z\u00E0-\u00FC])|\s+([a-z\u00E0-\u00FC])/g, function ($1) { - return $1.toUpperCase(); - }); -} \ No newline at end of file + if (name == 'build-updated') { + uri = 'build/data/' + self.buildId; + } + + var cb = function() { + $.getJSON(window.PHPCI_URL + uri, query, function(data) { + $(window).trigger({type: name, queryData: data}); + }); + }; + + if (seconds != -1) { + setInterval(cb, seconds * 1000); + } + + return cb; + }, + + registerPlugin: function(plugin) { + this.plugins[plugin.id] = plugin; + plugin.register(); + }, + + storePluginOrder: function () { + var renderOrder = []; + + $('.ui-plugin > div').each(function() { + renderOrder.push($(this).attr('id')); + }); + + localStorage.setItem('phpci-plugin-order', JSON.stringify(renderOrder)); + }, + + renderPlugins: function() { + var self = this; + var rendered = []; + var renderOrder = localStorage.getItem('phpci-plugin-order'); + + if (renderOrder) { + renderOrder = JSON.parse(renderOrder); + } else { + renderOrder = ['build-time', 'build-lines-chart', 'build-warnings-chart', 'build-log']; + } + + for (var idx in renderOrder) { + var key = renderOrder[idx]; + self.renderPlugin(self.plugins[key]); + rendered.push(key); + } + + for (var key in this.plugins) { + if (rendered.indexOf(key) == -1) { + self.renderPlugin(self.plugins[key]); + } + } + + $('#plugins').sortable({ + handle: '.title', + connectWith: '#plugins', + update: self.storePluginOrder + }); + + $(window).trigger({type: 'build-updated', queryData: self.buildData}); + }, + + renderPlugin: function(plugin) { + var output = $('
').addClass('box-content').append(plugin.render()); + var container = $('
').addClass('ui-plugin ' + plugin.css); + var content = $('
').attr('id', plugin.id).append(output); + + if (plugin.box) { + content.addClass('box'); + } + + if (plugin.title) { + content.prepend('

'+plugin.title+'

'); + } + + content.append(output); + container.append(content); + + $('#plugins').append(container); + }, + + UiPlugin: Class.extend({ + id: null, + css: 'col-lg-4 col-md-6 col-sm-12 col-xs-12', + box: true, + + init: function(){ + }, + + register: function() { + var self = this; + + $(window).on('build-updated', function(data) { + self.onUpdate(data); + }); + }, + + render: function () { + return ''; + }, + + onUpdate: function (build) { + + } + }) +}); \ No newline at end of file From cc09d95a3dc80df79d2ae80e4de61620553aeb87 Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Tue, 8 Oct 2013 07:45:20 +0100 Subject: [PATCH 059/933] SQL strict mode fixes. Closes #127 --- PHPCI/Command/UpdateCommand.php | 42 +++++++++++++++++++ PHPCI/Model/Base/BuildBase.php | 37 +++++++++++------ PHPCI/Model/Base/BuildMetaBase.php | 56 +++++++++++++++++++++++--- PHPCI/Model/Base/ProjectBase.php | 21 ++++++---- PHPCI/Model/Base/UserBase.php | 15 ++++--- console | 2 + public/assets/js/build-plugins/time.js | 4 +- 7 files changed, 145 insertions(+), 32 deletions(-) create mode 100644 PHPCI/Command/UpdateCommand.php diff --git a/PHPCI/Command/UpdateCommand.php b/PHPCI/Command/UpdateCommand.php new file mode 100644 index 00000000..e5806025 --- /dev/null +++ b/PHPCI/Command/UpdateCommand.php @@ -0,0 +1,42 @@ + + * @package PHPCI + * @subpackage Console + */ +class UpdateCommand extends Command +{ + protected function configure() + { + $this + ->setName('phpci:update') + ->setDescription('Update the database to reflect modified models.'); + } + + /** + * Generates Model and Store classes by reading database meta data. + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + // Update the database: + $gen = new \b8\Database\Generator(\b8\Database::getConnection(), 'PHPCI', './PHPCI/Model/Base/'); + $gen->generate(); + } +} diff --git a/PHPCI/Model/Base/BuildBase.php b/PHPCI/Model/Base/BuildBase.php index 33f3d406..e620452f 100644 --- a/PHPCI/Model/Base/BuildBase.php +++ b/PHPCI/Model/Base/BuildBase.php @@ -90,52 +90,64 @@ class BuildBase extends Model 'length' => '11', 'primary_key' => true, 'auto_increment' => true, + 'default' => null, ), 'project_id' => array( 'type' => 'int', 'length' => '11', + 'default' => null, ), 'commit_id' => array( 'type' => 'varchar', 'length' => '50', + 'nullable' => true, + 'default' => null, ), 'status' => array( 'type' => 'tinyint', 'length' => '4', + 'default' => '0', ), 'log' => array( 'type' => 'longtext', 'length' => '', 'nullable' => true, + 'default' => null, ), 'branch' => array( 'type' => 'varchar', 'length' => '50', + 'default' => 'master', ), 'created' => array( 'type' => 'datetime', 'length' => '', 'nullable' => true, + 'default' => null, ), 'started' => array( 'type' => 'datetime', 'length' => '', 'nullable' => true, + 'default' => null, ), 'finished' => array( 'type' => 'datetime', 'length' => '', 'nullable' => true, + 'default' => null, ), 'plugins' => array( 'type' => 'text', 'length' => '', 'nullable' => true, + 'default' => null, ), 'committer_email' => array( 'type' => 'varchar', 'length' => '512', 'nullable' => true, + 'default' => null, ), ); @@ -330,7 +342,7 @@ class BuildBase extends Model { $this->_validateNotNull('Id', $value); $this->_validateInt('Id', $value); - if ($this->data['id'] == $value) { + if ($this->data['id'] === $value) { return; } @@ -349,7 +361,7 @@ class BuildBase extends Model { $this->_validateNotNull('ProjectId', $value); $this->_validateInt('ProjectId', $value); - if ($this->data['project_id'] == $value) { + if ($this->data['project_id'] === $value) { return; } @@ -361,14 +373,13 @@ class BuildBase extends Model /** * Set the value of CommitId / commit_id. * - * Must not be null. * @param $value string */ public function setCommitId($value) { - $this->_validateNotNull('CommitId', $value); + $this->_validateString('CommitId', $value); - if ($this->data['commit_id'] == $value) { + if ($this->data['commit_id'] === $value) { return; } @@ -387,7 +398,7 @@ class BuildBase extends Model { $this->_validateNotNull('Status', $value); $this->_validateInt('Status', $value); - if ($this->data['status'] == $value) { + if ($this->data['status'] === $value) { return; } @@ -405,7 +416,7 @@ class BuildBase extends Model { $this->_validateString('Log', $value); - if ($this->data['log'] == $value) { + if ($this->data['log'] === $value) { return; } @@ -424,7 +435,7 @@ class BuildBase extends Model { $this->_validateNotNull('Branch', $value); $this->_validateString('Branch', $value); - if ($this->data['branch'] == $value) { + if ($this->data['branch'] === $value) { return; } @@ -442,7 +453,7 @@ class BuildBase extends Model { $this->_validateDate('Created', $value); - if ($this->data['created'] == $value) { + if ($this->data['created'] === $value) { return; } @@ -460,7 +471,7 @@ class BuildBase extends Model { $this->_validateDate('Started', $value); - if ($this->data['started'] == $value) { + if ($this->data['started'] === $value) { return; } @@ -478,7 +489,7 @@ class BuildBase extends Model { $this->_validateDate('Finished', $value); - if ($this->data['finished'] == $value) { + if ($this->data['finished'] === $value) { return; } @@ -496,7 +507,7 @@ class BuildBase extends Model { $this->_validateString('Plugins', $value); - if ($this->data['plugins'] == $value) { + if ($this->data['plugins'] === $value) { return; } @@ -514,7 +525,7 @@ class BuildBase extends Model { $this->_validateString('CommitterEmail', $value); - if ($this->data['committer_email'] == $value) { + if ($this->data['committer_email'] === $value) { return; } diff --git a/PHPCI/Model/Base/BuildMetaBase.php b/PHPCI/Model/Base/BuildMetaBase.php index f9aa94e5..8fb62832 100644 --- a/PHPCI/Model/Base/BuildMetaBase.php +++ b/PHPCI/Model/Base/BuildMetaBase.php @@ -33,6 +33,7 @@ class BuildMetaBase extends Model */ protected $data = array( 'id' => null, + 'project_id' => null, 'build_id' => null, 'key' => null, 'value' => null, @@ -43,6 +44,7 @@ class BuildMetaBase extends Model */ protected $getters = array( 'id' => 'getId', + 'project_id' => 'getProjectId', 'build_id' => 'getBuildId', 'key' => 'getKey', 'value' => 'getValue', @@ -54,6 +56,7 @@ class BuildMetaBase extends Model */ protected $setters = array( 'id' => 'setId', + 'project_id' => 'setProjectId', 'build_id' => 'setBuildId', 'key' => 'setKey', 'value' => 'setValue', @@ -69,19 +72,29 @@ class BuildMetaBase extends Model 'length' => '10', 'primary_key' => true, 'auto_increment' => true, + 'default' => null, + ), + 'project_id' => array( + 'type' => 'int', + 'length' => '11', + 'default' => null, ), 'build_id' => array( 'type' => 'int', 'length' => '11', + 'nullable' => true, + 'default' => null, ), 'key' => array( 'type' => 'varchar', 'length' => '255', + 'default' => '', ), 'value' => array( 'type' => 'text', 'length' => '', 'nullable' => true, + 'default' => null, ), ); @@ -120,6 +133,19 @@ class BuildMetaBase extends Model return $rtn; } + /** + * Get the value of ProjectId / project_id. + * + * @return int + */ + public function getProjectId() + { + $rtn = $this->data['project_id']; + + + return $rtn; + } + /** * Get the value of BuildId / build_id. * @@ -169,7 +195,7 @@ class BuildMetaBase extends Model { $this->_validateNotNull('Id', $value); $this->_validateInt('Id', $value); - if ($this->data['id'] == $value) { + if ($this->data['id'] === $value) { return; } @@ -179,16 +205,34 @@ class BuildMetaBase extends Model } /** - * Set the value of BuildId / build_id. + * Set the value of ProjectId / project_id. * * Must not be null. * @param $value int */ + public function setProjectId($value) + { + $this->_validateNotNull('ProjectId', $value); + $this->_validateInt('ProjectId', $value); + if ($this->data['project_id'] === $value) { + return; + } + + $this->data['project_id'] = $value; + + $this->_setModified('project_id'); + } + + /** + * Set the value of BuildId / build_id. + * + * @param $value int + */ public function setBuildId($value) { - $this->_validateNotNull('BuildId', $value); + $this->_validateInt('BuildId', $value); - if ($this->data['build_id'] == $value) { + if ($this->data['build_id'] === $value) { return; } @@ -207,7 +251,7 @@ class BuildMetaBase extends Model { $this->_validateNotNull('Key', $value); $this->_validateString('Key', $value); - if ($this->data['key'] == $value) { + if ($this->data['key'] === $value) { return; } @@ -225,7 +269,7 @@ class BuildMetaBase extends Model { $this->_validateString('Value', $value); - if ($this->data['value'] == $value) { + if ($this->data['value'] === $value) { return; } diff --git a/PHPCI/Model/Base/ProjectBase.php b/PHPCI/Model/Base/ProjectBase.php index d00916bb..6a128b42 100644 --- a/PHPCI/Model/Base/ProjectBase.php +++ b/PHPCI/Model/Base/ProjectBase.php @@ -76,31 +76,38 @@ class ProjectBase extends Model 'length' => '11', 'primary_key' => true, 'auto_increment' => true, + 'default' => null, ), 'title' => array( 'type' => 'varchar', 'length' => '250', + 'default' => '', ), 'reference' => array( 'type' => 'varchar', 'length' => '250', + 'default' => '', ), 'git_key' => array( 'type' => 'text', 'length' => '', + 'default' => null, ), 'type' => array( 'type' => 'varchar', 'length' => '50', + 'default' => '', ), 'token' => array( 'type' => 'varchar', 'length' => '50', 'nullable' => true, + 'default' => null, ), 'access_information' => array( 'type' => 'varchar', 'length' => '250', + 'default' => null, ), ); @@ -219,7 +226,7 @@ class ProjectBase extends Model { $this->_validateNotNull('Id', $value); $this->_validateInt('Id', $value); - if ($this->data['id'] == $value) { + if ($this->data['id'] === $value) { return; } @@ -238,7 +245,7 @@ class ProjectBase extends Model { $this->_validateNotNull('Title', $value); $this->_validateString('Title', $value); - if ($this->data['title'] == $value) { + if ($this->data['title'] === $value) { return; } @@ -257,7 +264,7 @@ class ProjectBase extends Model { $this->_validateNotNull('Reference', $value); $this->_validateString('Reference', $value); - if ($this->data['reference'] == $value) { + if ($this->data['reference'] === $value) { return; } @@ -276,7 +283,7 @@ class ProjectBase extends Model { $this->_validateNotNull('GitKey', $value); $this->_validateString('GitKey', $value); - if ($this->data['git_key'] == $value) { + if ($this->data['git_key'] === $value) { return; } @@ -295,7 +302,7 @@ class ProjectBase extends Model { $this->_validateNotNull('Type', $value); $this->_validateString('Type', $value); - if ($this->data['type'] == $value) { + if ($this->data['type'] === $value) { return; } @@ -313,7 +320,7 @@ class ProjectBase extends Model { $this->_validateString('Token', $value); - if ($this->data['token'] == $value) { + if ($this->data['token'] === $value) { return; } @@ -332,7 +339,7 @@ class ProjectBase extends Model { $this->_validateNotNull('AccessInformation', $value); $this->_validateString('AccessInformation', $value); - if ($this->data['access_information'] == $value) { + if ($this->data['access_information'] === $value) { return; } diff --git a/PHPCI/Model/Base/UserBase.php b/PHPCI/Model/Base/UserBase.php index aab1e21b..0f95e93b 100644 --- a/PHPCI/Model/Base/UserBase.php +++ b/PHPCI/Model/Base/UserBase.php @@ -70,22 +70,27 @@ class UserBase extends Model 'length' => '11', 'primary_key' => true, 'auto_increment' => true, + 'default' => null, ), 'email' => array( 'type' => 'varchar', 'length' => '250', + 'default' => '', ), 'hash' => array( 'type' => 'varchar', 'length' => '250', + 'default' => '', ), 'is_admin' => array( 'type' => 'tinyint', 'length' => '1', + 'default' => '0', ), 'name' => array( 'type' => 'varchar', 'length' => '250', + 'default' => '', ), ); @@ -179,7 +184,7 @@ class UserBase extends Model { $this->_validateNotNull('Id', $value); $this->_validateInt('Id', $value); - if ($this->data['id'] == $value) { + if ($this->data['id'] === $value) { return; } @@ -198,7 +203,7 @@ class UserBase extends Model { $this->_validateNotNull('Email', $value); $this->_validateString('Email', $value); - if ($this->data['email'] == $value) { + if ($this->data['email'] === $value) { return; } @@ -217,7 +222,7 @@ class UserBase extends Model { $this->_validateNotNull('Hash', $value); $this->_validateString('Hash', $value); - if ($this->data['hash'] == $value) { + if ($this->data['hash'] === $value) { return; } @@ -236,7 +241,7 @@ class UserBase extends Model { $this->_validateNotNull('IsAdmin', $value); $this->_validateInt('IsAdmin', $value); - if ($this->data['is_admin'] == $value) { + if ($this->data['is_admin'] === $value) { return; } @@ -255,7 +260,7 @@ class UserBase extends Model { $this->_validateNotNull('Name', $value); $this->_validateString('Name', $value); - if ($this->data['name'] == $value) { + if ($this->data['name'] === $value) { return; } diff --git a/console b/console index dd71d058..dd44b13b 100755 --- a/console +++ b/console @@ -23,6 +23,7 @@ require('bootstrap.php'); use PHPCI\Command\RunCommand; use PHPCI\Command\GenerateCommand; +use PHPCI\Command\UpdateCommand; use PHPCI\Command\InstallCommand; use PHPCI\Command\DaemonCommand; use Symfony\Component\Console\Application; @@ -30,6 +31,7 @@ use Symfony\Component\Console\Application; $application = new Application(); $application->add(new RunCommand); $application->add(new InstallCommand); +$application->add(new UpdateCommand); $application->add(new GenerateCommand); $application->add(new DaemonCommand); $application->run(); diff --git a/public/assets/js/build-plugins/time.js b/public/assets/js/build-plugins/time.js index 5705ed9a..4e697bf6 100644 --- a/public/assets/js/build-plugins/time.js +++ b/public/assets/js/build-plugins/time.js @@ -27,7 +27,9 @@ var timePlugin = PHPCI.UiPlugin.extend({ '
'; }, - onUpdate: function(build) { + onUpdate: function(e) { + var build = e.queryData; + $('#created').text(build.created); $('#started').text(build.started); $('#finished').text(build.finished); From deb71ec9e2f6b67761d34c866741ce845599b42e Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Tue, 8 Oct 2013 07:50:42 +0100 Subject: [PATCH 060/933] Fixes #125 --- PHPCI/Controller/SessionController.php | 5 +++++ PHPCI/View/Session/login.phtml | 3 +++ 2 files changed, 8 insertions(+) diff --git a/PHPCI/Controller/SessionController.php b/PHPCI/Controller/SessionController.php index 06773305..0c463132 100644 --- a/PHPCI/Controller/SessionController.php +++ b/PHPCI/Controller/SessionController.php @@ -30,6 +30,8 @@ class SessionController extends \PHPCI\Controller */ public function login() { + $isLoginFailure = false; + if ($this->request->getMethod() == 'POST') { $user = $this->_userStore->getByEmail($this->getParam('email')); @@ -37,6 +39,8 @@ class SessionController extends \PHPCI\Controller $_SESSION['user_id'] = $user->getId(); header('Location: ' . PHPCI_URL); die; + } else { + $isLoginFailure = true; } } @@ -64,6 +68,7 @@ class SessionController extends \PHPCI\Controller $form->addField($pwd); $this->view->form = $form->render(); + $this->view->failed = $isLoginFailure; return $this->view->render(); } diff --git a/PHPCI/View/Session/login.phtml b/PHPCI/View/Session/login.phtml index adcc8210..59027b0e 100644 --- a/PHPCI/View/Session/login.phtml +++ b/PHPCI/View/Session/login.phtml @@ -78,6 +78,9 @@
+ +

Incorrect email address or password

+
From df1dc0d666a6bd5c0f3e5528bde0e51533398916 Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Tue, 8 Oct 2013 08:23:07 +0100 Subject: [PATCH 061/933] Error reporting. See #142 --- PHPCI/Application.php | 42 +++++++++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/PHPCI/Application.php b/PHPCI/Application.php index bfae830c..9bacd78d 100644 --- a/PHPCI/Application.php +++ b/PHPCI/Application.php @@ -24,17 +24,45 @@ class Application extends b8\Application */ public function handleRequest() { - $this->initRequest(); + try { + $this->initRequest(); - // Validate the user's session unless it is a login/logout action or a web hook: - $sessionAction = ($this->controllerName == 'Session' && in_array($this->action, array('login', 'logout'))); - $externalAction = in_array($this->controllerName, array('Bitbucket', 'Github', 'Gitlab', 'BuildStatus')); - $skipValidation = ($externalAction || $sessionAction); + // Validate the user's session unless it is a login/logout action or a web hook: + $sessionAction = ($this->controllerName == 'Session' && in_array($this->action, array('login', 'logout'))); + $externalAction = in_array($this->controllerName, array('Bitbucket', 'Github', 'Gitlab', 'BuildStatus')); + $skipValidation = ($externalAction || $sessionAction); - if($skipValidation || $this->validateSession()) { - parent::handleRequest(); + if($skipValidation || $this->validateSession()) { + parent::handleRequest(); + } + } catch (\Exception $ex) { + $content = '

There was a problem with this request

Please paste the details below into a new bug report so that we can investigate and fix it.

'; + + ob_start(); + var_dump(array( + 'message' => $ex->getMessage(), + 'file' => $ex->getFile(), + 'line' => $ex->getLine(), + 'trace' => $ex->getTraceAsString() + )); + var_dump(array( + 'PATH_INFO' => $_SERVER['PATH_INFO'], + 'REDIRECT_PATH_INFO' => $_SERVER['REDIRECT_PATH_INFO'], + 'REQUEST_URI' => $_SERVER['REQUEST_URI'], + 'PHP_SELF' => $_SERVER['PHP_SELF'], + 'SCRIPT_NAME' => $_SERVER['SCRIPT_NAME'], + 'DOCUMENT_ROOT' => $_SERVER['DOCUMENT_ROOT'], + 'SCRIPT_FILENAME' => $_SERVER['SCRIPT_FILENAME'], + 'SERVER_SOFTWARE' => $_SERVER['SERVER_SOFTWARE'], + )); + $content .= ob_get_contents(); + ob_end_clean(); + + $this->response->setContent($content); + $this->response->disableLayout(); } + if (View::exists('layout') && $this->response->hasLayout()) { $view = new View('layout'); $view->content = $this->response->getContent(); From f7e466bdb11826c75a4a7d0b76090b6f8dcdd722 Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Tue, 8 Oct 2013 08:30:15 +0100 Subject: [PATCH 062/933] Fixes #147 --- PHPCI/Plugin/Composer.php | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/PHPCI/Plugin/Composer.php b/PHPCI/Plugin/Composer.php index 36357986..08060f83 100644 --- a/PHPCI/Plugin/Composer.php +++ b/PHPCI/Plugin/Composer.php @@ -36,7 +36,36 @@ class Composer implements \PHPCI\Plugin */ public function execute() { - $cmd = PHPCI_DIR . 'composer.phar --no-ansi --no-interaction '. ($this->preferDist ? '--prefer-dist' : null) .' --working-dir="%s" %s'; + $composerLocation = $this->whereIsComposer(); + + if (!$composerLocation) { + $this->phpci->logFailure('Could not find Composer.'); + return false; + } + + $cmd = $composerLocation . ' --no-ansi --no-interaction '. ($this->preferDist ? '--prefer-dist' : null) .' --working-dir="%s" %s'; + return $this->phpci->executeCommand($cmd, $this->directory, $this->action); } + + protected function whereIsComposer() + { + if (is_file(PHPCI_DIR . 'composer.phar')) { + return PHPCI_DIR . 'composer.phar'; + } + + $which = trim(shell_exec('which composer')); + + if (!empty($which)) { + return $which; + } + + $which = trim(shell_exec('which composer.phar')); + + if (!empty($which)) { + return $which; + } + + return null; + } } From 0c8d9c0f74babfc7306f4b8cd9052815bc09b372 Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Tue, 8 Oct 2013 08:50:10 +0100 Subject: [PATCH 063/933] Added findBinary, fixes #115 --- PHPCI/Builder.php | 33 ++++++++++++++++++++++++++++++++ PHPCI/Plugin/Atoum.php | 2 +- PHPCI/Plugin/Composer.php | 23 +--------------------- PHPCI/Plugin/Grunt.php | 2 +- PHPCI/Plugin/Pdepend.php | 9 ++++++++- PHPCI/Plugin/PhpCodeSniffer.php | 9 ++++++++- PHPCI/Plugin/PhpCpd.php | 9 ++++++++- PHPCI/Plugin/PhpCsFixer.php | 9 ++++++++- PHPCI/Plugin/PhpLoc.php | 9 ++++++++- PHPCI/Plugin/PhpMessDetector.php | 9 ++++++++- PHPCI/Plugin/PhpSpec.php | 10 +++++++++- PHPCI/Plugin/PhpUnit.php | 21 ++++++++++++++++++-- 12 files changed, 112 insertions(+), 33 deletions(-) diff --git a/PHPCI/Builder.php b/PHPCI/Builder.php index f62fd758..e21d1dbb 100644 --- a/PHPCI/Builder.php +++ b/PHPCI/Builder.php @@ -459,4 +459,37 @@ class Builder $value = json_encode($value); $this->store->setMeta($this->build->getProjectId(), $this->build->getId(), $key, $value); } + + /** + * Find a binary required by a plugin. + * @param $binary + * @return null|string + */ + public function findBinary($binary) + { + if (is_string($binary)) { + $binary = array($binary); + } + + foreach ($binary as $bin) { + // Check project root directory: + if (is_file(PHPCI_DIR . $bin)) { + return PHPCI_DIR . $bin; + } + + // Check Composer bin dir: + if (is_file(PHPCI_DIR . 'vendor/bin/' . $bin)) { + return PHPCI_DIR . 'vendor/bin/' . $bin; + } + + // Use "which" + $which = trim(shell_exec('which ' . $bin)); + + if (!empty($which)) { + return $which; + } + } + + return null; + } } diff --git a/PHPCI/Plugin/Atoum.php b/PHPCI/Plugin/Atoum.php index 54783b07..80dce4c3 100644 --- a/PHPCI/Plugin/Atoum.php +++ b/PHPCI/Plugin/Atoum.php @@ -15,7 +15,7 @@ class Atoum implements \PHPCI\Plugin if (isset($options['executable'])) { $this->executable = $this->phpci->buildPath . DIRECTORY_SEPARATOR.$options['executable']; } else { - $this->executable = PHPCI_BIN_DIR.'atoum'; + $this->executable = $this->phpci->findBinary('atoum'); } if (isset($options['args'])) { diff --git a/PHPCI/Plugin/Composer.php b/PHPCI/Plugin/Composer.php index 08060f83..ff1cbce4 100644 --- a/PHPCI/Plugin/Composer.php +++ b/PHPCI/Plugin/Composer.php @@ -36,7 +36,7 @@ class Composer implements \PHPCI\Plugin */ public function execute() { - $composerLocation = $this->whereIsComposer(); + $composerLocation = $this->phpci->findBinary(array('composer', 'composer.phar')); if (!$composerLocation) { $this->phpci->logFailure('Could not find Composer.'); @@ -47,25 +47,4 @@ class Composer implements \PHPCI\Plugin return $this->phpci->executeCommand($cmd, $this->directory, $this->action); } - - protected function whereIsComposer() - { - if (is_file(PHPCI_DIR . 'composer.phar')) { - return PHPCI_DIR . 'composer.phar'; - } - - $which = trim(shell_exec('which composer')); - - if (!empty($which)) { - return $which; - } - - $which = trim(shell_exec('which composer.phar')); - - if (!empty($which)) { - return $which; - } - - return null; - } } diff --git a/PHPCI/Plugin/Grunt.php b/PHPCI/Plugin/Grunt.php index 892d8303..ca024276 100644 --- a/PHPCI/Plugin/Grunt.php +++ b/PHPCI/Plugin/Grunt.php @@ -30,7 +30,7 @@ class Grunt implements \PHPCI\Plugin $this->phpci = $phpci; $this->directory = isset($options['directory']) ? $path . '/' . $options['directory'] : $path; $this->task = isset($options['task']) ? $options['task'] : null; - $this->grunt = isset($options['grunt']) ? $options['grunt'] : 'grunt'; + $this->grunt = isset($options['grunt']) ? $options['grunt'] : $this->phpci->findBinary('grunt'); $this->gruntfile = isset($options['gruntfile']) ? $options['gruntfile'] : 'Gruntfile.js'; } diff --git a/PHPCI/Plugin/Pdepend.php b/PHPCI/Plugin/Pdepend.php index 2a26cbb0..bb2fdea2 100644 --- a/PHPCI/Plugin/Pdepend.php +++ b/PHPCI/Plugin/Pdepend.php @@ -66,7 +66,14 @@ class Pdepend implements \PHPCI\Plugin throw new \Exception(sprintf('The location %s is not writable.', $this->location)); } - $cmd = PHPCI_BIN_DIR . 'pdepend --summary-xml="%s" --jdepend-chart="%s" --overview-pyramid="%s" %s "%s"'; + $pdepend = $this->phpci->findBinary('pdepend'); + + if (!$pdepend) { + $this->phpci->logFailure('Could not find pdepend.'); + return false; + } + + $cmd = $pdepend . ' --summary-xml="%s" --jdepend-chart="%s" --overview-pyramid="%s" %s "%s"'; $this->removeBuildArtifacts(); diff --git a/PHPCI/Plugin/PhpCodeSniffer.php b/PHPCI/Plugin/PhpCodeSniffer.php index 71a9008d..98d0c603 100755 --- a/PHPCI/Plugin/PhpCodeSniffer.php +++ b/PHPCI/Plugin/PhpCodeSniffer.php @@ -105,7 +105,14 @@ class PhpCodeSniffer implements \PHPCI\Plugin $encoding = ' --encoding='.$this->encoding; } - $cmd = PHPCI_BIN_DIR . 'phpcs %s %s %s %s %s "%s"'; + $phpcs = $this->phpci->findBinary('phpcs'); + + if (!$phpcs) { + $this->phpci->logFailure('Could not find phpcs.'); + return false; + } + + $cmd = $phpcs . ' %s %s %s %s %s "%s"'; $success = $this->phpci->executeCommand($cmd, $standard, $suffixes, $ignore, $tab_width, $encoding, $this->phpci->buildPath . $this->path); $output = $this->phpci->getLastOutput(); diff --git a/PHPCI/Plugin/PhpCpd.php b/PHPCI/Plugin/PhpCpd.php index 5a91dff4..5764258f 100755 --- a/PHPCI/Plugin/PhpCpd.php +++ b/PHPCI/Plugin/PhpCpd.php @@ -57,7 +57,14 @@ class PhpCpd implements \PHPCI\Plugin $ignore = implode('', $ignore); } - $success = $this->phpci->executeCommand(PHPCI_BIN_DIR . 'phpcpd %s "%s"', $ignore, $this->phpci->buildPath.$this->path); + $phpcpd = $this->phpci->findBinary('phpcpd'); + + if (!$phpcpd) { + $this->phpci->logFailure('Could not find phpcpd.'); + return false; + } + + $success = $this->phpci->executeCommand($phpcpd . ' %s "%s"', $ignore, $this->phpci->buildPath.$this->path); print $this->phpci->getLastOutput(); diff --git a/PHPCI/Plugin/PhpCsFixer.php b/PHPCI/Plugin/PhpCsFixer.php index 9aaa7536..6c42fcb8 100644 --- a/PHPCI/Plugin/PhpCsFixer.php +++ b/PHPCI/Plugin/PhpCsFixer.php @@ -42,7 +42,14 @@ class PhpCsFixer implements \PHPCI\Plugin $curdir = getcwd(); chdir($this->workingdir); - $cmd = PHPCI_BIN_DIR . 'php-cs-fixer fix . %s'; + $phpcsfixer = $this->phpci->findBinary('php-cs-fixer'); + + if (!$phpcsfixer) { + $this->phpci->logFailure('Could not find php-cs-fixer.'); + return false; + } + + $cmd = $phpcsfixer . ' fix . %s'; $success = $this->phpci->executeCommand($cmd, $this->args); chdir($curdir); diff --git a/PHPCI/Plugin/PhpLoc.php b/PHPCI/Plugin/PhpLoc.php index 51ba7108..6742de2b 100644 --- a/PHPCI/Plugin/PhpLoc.php +++ b/PHPCI/Plugin/PhpLoc.php @@ -47,7 +47,14 @@ class PhpLoc implements \PHPCI\Plugin $ignore = implode('', $ignore); } - $success = $this->phpci->executeCommand(PHPCI_BIN_DIR . 'phploc %s "%s"', $ignore, $this->phpci->buildPath); + $phploc = $this->phpci->findBinary('phploc'); + + if (!$phploc) { + $this->phpci->logFailure('Could not find phploc.'); + return false; + } + + $success = $this->phpci->executeCommand($phploc . ' %s "%s"', $ignore, $this->phpci->buildPath); $output = $this->phpci->getLastOutput(); if (preg_match_all('/\((LOC|CLOC|NCLOC|LLOC)\)\s+([0-9]+)/', $output, $matches)) { diff --git a/PHPCI/Plugin/PhpMessDetector.php b/PHPCI/Plugin/PhpMessDetector.php index c395c3dd..f1743b8d 100755 --- a/PHPCI/Plugin/PhpMessDetector.php +++ b/PHPCI/Plugin/PhpMessDetector.php @@ -82,7 +82,14 @@ class PhpMessDetector implements \PHPCI\Plugin $suffixes = ' --suffixes ' . implode(',', $this->suffixes); } - $cmd = PHPCI_BIN_DIR . 'phpmd "%s" text %s %s %s'; + $phpmd = $this->phpci->findBinary('phpmd'); + + if (!$phpmd) { + $this->phpci->logFailure('Could not find phpmd.'); + return false; + } + + $cmd = $phpmd . ' "%s" text %s %s %s'; $success = $this->phpci->executeCommand($cmd, $this->phpci->buildPath . $this->path, implode(',', $this->rules), $ignore, $suffixes); $errors = count(array_filter(explode(PHP_EOL, $this->phpci->getLastOutput()))); $this->phpci->storeBuildMeta('phpmd-warnings', $errors); diff --git a/PHPCI/Plugin/PhpSpec.php b/PHPCI/Plugin/PhpSpec.php index ace3f352..fbe981cc 100644 --- a/PHPCI/Plugin/PhpSpec.php +++ b/PHPCI/Plugin/PhpSpec.php @@ -31,7 +31,15 @@ class PhpSpec implements \PHPCI\Plugin { $curdir = getcwd(); chdir($this->phpci->buildPath); - $success = $this->phpci->executeCommand(PHPCI_BIN_DIR . 'phpspec'); + + $phpspec = $this->phpci->findBinary('phpspec'); + + if (!$phpspec) { + $this->phpci->logFailure('Could not find phpspec.'); + return false; + } + + $success = $this->phpci->executeCommand($phpspec); chdir($curdir); return $success; diff --git a/PHPCI/Plugin/PhpUnit.php b/PHPCI/Plugin/PhpUnit.php index 6925a772..dce241cf 100755 --- a/PHPCI/Plugin/PhpUnit.php +++ b/PHPCI/Plugin/PhpUnit.php @@ -102,7 +102,16 @@ class PhpUnit implements \PHPCI\Plugin chdir($this->phpci->buildPath.'/'.$this->runFrom); } - $cmd = PHPCI_BIN_DIR . 'phpunit %s -c "%s" ' . $this->coverage . $this->path; + + $phpunit = $this->phpci->findBinary('phpunit'); + + if (!$phpunit) { + $this->phpci->logFailure('Could not find phpunit.'); + return false; + } + + + $cmd = $phpunit . ' %s -c "%s" ' . $this->coverage . $this->path; $success = $this->phpci->executeCommand($cmd, $this->args, $this->phpci->buildPath . $configPath); if ($this->runFrom) { @@ -120,7 +129,15 @@ class PhpUnit implements \PHPCI\Plugin } else { $curdir = getcwd(); chdir($this->phpci->buildPath); - $cmd = PHPCI_BIN_DIR . 'phpunit %s "%s"'; + + $phpunit = $this->phpci->findBinary('phpunit'); + + if (!$phpunit) { + $this->phpci->logFailure('Could not find phpunit.'); + return false; + } + + $cmd = $phpunit . ' %s "%s"'; $success = $this->phpci->executeCommand($cmd, $this->args, $this->phpci->buildPath . $dirPath); chdir($curdir); return $success; From 42c5543cabfd9dd6ac186bd65a544add680163fa Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Tue, 8 Oct 2013 08:51:48 +0100 Subject: [PATCH 064/933] Moved non-dependencies to suggest in composer.json --- composer.json | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/composer.json b/composer.json index 858a3284..11c00c85 100644 --- a/composer.json +++ b/composer.json @@ -24,16 +24,19 @@ "require": { "block8/b8framework" : "dev-master", + "ircmaxell/password-compat": "1.x", + "swiftmailer/swiftmailer" : "v5.0.0", + "symfony/yaml" : "2.2.x-dev", + "symfony/console" : "2.2.*" + }, + + "suggest": { "phpunit/phpunit" : "3.*", "phpmd/phpmd" : "1.*", "sebastian/phpcpd" : "1.*", "squizlabs/php_codesniffer": "1.*", - "ircmaxell/password-compat": "1.x", "phpspec/phpspec" : "2.*", - "symfony/yaml" : "2.2.x-dev", - "symfony/console" : "2.2.*", "fabpot/php-cs-fixer" : "0.3.*@dev", - "swiftmailer/swiftmailer" : "v5.0.0", "phploc/phploc": "*", "atoum/atoum":"*", "jakub-onderka/php-parallel-lint": "dev-master" From 0dea956e872da1aea25355cca8f7f3dc375ee36f Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Tue, 8 Oct 2013 09:00:22 +0100 Subject: [PATCH 065/933] Adding default values to project table, fixes #158 --- PHPCI/Model/Base/ProjectBase.php | 10 +++++----- PHPCI/Model/Base/UserBase.php | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/PHPCI/Model/Base/ProjectBase.php b/PHPCI/Model/Base/ProjectBase.php index 6a128b42..6fc1a422 100644 --- a/PHPCI/Model/Base/ProjectBase.php +++ b/PHPCI/Model/Base/ProjectBase.php @@ -91,12 +91,13 @@ class ProjectBase extends Model 'git_key' => array( 'type' => 'text', 'length' => '', + 'nullable' => true, 'default' => null, ), 'type' => array( 'type' => 'varchar', 'length' => '50', - 'default' => '', + 'default' => '1', ), 'token' => array( 'type' => 'varchar', @@ -107,6 +108,7 @@ class ProjectBase extends Model 'access_information' => array( 'type' => 'varchar', 'length' => '250', + 'nullable' => true, 'default' => null, ), ); @@ -276,12 +278,11 @@ class ProjectBase extends Model /** * Set the value of GitKey / git_key. * - * Must not be null. * @param $value string */ public function setGitKey($value) { - $this->_validateNotNull('GitKey', $value); + $this->_validateString('GitKey', $value); if ($this->data['git_key'] === $value) { return; @@ -332,12 +333,11 @@ class ProjectBase extends Model /** * Set the value of AccessInformation / access_information. * - * Must not be null. * @param $value string */ public function setAccessInformation($value) { - $this->_validateNotNull('AccessInformation', $value); + $this->_validateString('AccessInformation', $value); if ($this->data['access_information'] === $value) { return; diff --git a/PHPCI/Model/Base/UserBase.php b/PHPCI/Model/Base/UserBase.php index 0f95e93b..8fad4ea1 100644 --- a/PHPCI/Model/Base/UserBase.php +++ b/PHPCI/Model/Base/UserBase.php @@ -90,7 +90,8 @@ class UserBase extends Model 'name' => array( 'type' => 'varchar', 'length' => '250', - 'default' => '', + 'nullable' => true, + 'default' => null, ), ); @@ -253,12 +254,11 @@ class UserBase extends Model /** * Set the value of Name / name. * - * Must not be null. * @param $value string */ public function setName($value) { - $this->_validateNotNull('Name', $value); + $this->_validateString('Name', $value); if ($this->data['name'] === $value) { return; From 8f6766d1aef50155d1b2d2c45a9450358c96cb1d Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Tue, 8 Oct 2013 09:08:52 +0100 Subject: [PATCH 066/933] Adding favicon, fixes #71 --- PHPCI/View/layout.phtml | 5 ++++- public/assets/img/favicon.png | Bin 0 -> 4499 bytes public/favicon.ico | Bin 0 -> 15086 bytes 3 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 public/assets/img/favicon.png create mode 100644 public/favicon.ico diff --git a/PHPCI/View/layout.phtml b/PHPCI/View/layout.phtml index a6fbfb36..3b3c2dc9 100644 --- a/PHPCI/View/layout.phtml +++ b/PHPCI/View/layout.phtml @@ -8,7 +8,10 @@ - + + + + diff --git a/public/assets/img/favicon.png b/public/assets/img/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..1e33cffa6f46cbf20937cfd1c04fa4b7593b6102 GIT binary patch literal 4499 zcmZ{oXH-+ox5f`agb-;#LX8jzy-G)r5(q_llNO3JMJb^MUW$}}Kq#@%MM1!VRHat~ z2vVeo5SkFWFGUcft2eHDKi&Vmv(~Ix^PIicK67UF%x_MLg_!{pBM&1008B=PdREl7 zl3L#}od2unaI#aIbHOMh8wP5LV(?6+_Q6;~hhPAJK>t>nvALKts>l(7whz$_^12b? z6W}jk_X+aC1q9s*7SOd; z3andF1_0JgBRw6P@Pe%;uD*Od@ZOGBtb&M|%Q}iF=xC9M_bb)#8?Q`t_=MxH#Z>Qs zBE+Y5i29)AQt6i|#2`wXO$BSdl}q?;QauzLf5qmyj@Yt0g5ligTNUIvP&+MK{BpM% zJNHHOHU6+qO>c(}1~<>9+D@ZO*dHjVD{!_S&dd*<#dJ6Xt@LxS>Wv+xY=K$v7mygi z`$TMt9*QIk1%tBe>kn$Ey>?u&0?v7lJK*rHAxWLh+v zRnHuoy`!m3dSX1iD-ZAiPRJH6x&^xS4aqz!cLaqe7OYG-mM#}V15^4iIk273pl~0E zYecb|4KNdGW<_F5OW?9}ZVcKlR*S9yv-)q_kr<{Dcol>a|9Wbn+KJbwwU|`8RgJDj zIs#lK>V+QRgWcLN98HdSKK+PyAWuGM9$u^3&emsZVK}`TWYG{TU<}r_8>i!)0UHgS zj4y&n{F1EgK8zwT0uqBRfe+K|v~ESnWt?lzXNwTn3jfvkj~Fym#{ng_$fpP|AYwT> zF?i76X&t0y-90_!3(d%j2$|3#YNpmKg|fz7VBi>rp9=cMrX>cCVg=LNP*I6GhG@_c_iKDoO6{ z*Uvv|+bW}*G0iI5T^zUAHPbuw+dFR zWZ@2~wa`O)4J zAPrGiOh@wT>Kgnq#t6pA%tG=fR*ZMY>rYGRr)MV<=qIb#qc>sSXohMex59M?x5hx= z22lOv$DyqBIa`Pv_oe6~7e(g*IP3}X1XvD9$;LaGat3sj-Kl!N+Fsa4TdyeFHMij? zbjWUfrO2oiIeWYDPn>P)R^`t-kAHILUSZolmPn-fq&84qKbCyNG+pCgSKID>&2@TL z<1zdJw%lk}d2`!>6Ze+tDhiS;RhE`_cpl7b)C!gi?nL%d=bd(~2rirs&4)hd(U! zIg4J8{j~CDP|gnR=WmobC?w@Z+CWuum8JX5PoHEfN$9 zn?txW+jf$OD{xseH>wHmC^{!y=v1I*X+m4sNIrPuwnpu@?DgihFksTthex*KK-1;d zg7uVCa-taJb!MSK&VD#;0X5NzcN;^wvr1y6V!Mi2R@%>CO=~U&8$U|d zPo{=!dr2OyQ3Q{hpTr9UQyJ#+_+t6YS82-G?(5w87Gk$d>50R47{hRv+#lp==0j0- zAFLl$7AF4sZckmD92?{CEK%EIaqF166WGtg06#sd@IxaF?x!_7;) ze5}){<1(0f*KC~}XxCn*Lh8=XHTrhReJ>lKhj+{3mg2sbg)(DE8RJoWj(BRkcoBtyqW4$TVbD+pqZHp6qpruzx2 z>h;^EyUH2vfu_grAfrCTv*tpiFl?i)2FLwHKQUp9NLFJ(==a*?@5-RE`Ip>ZlSn$s z9QPn&`)?dl4s50E1Am38Ms7z&MTE)UPJT+SvsSo3x;NF8eK9~ccU|sPS0XhfYrH&E zF7=(Ydbd^VE}r|Uswlzx17}X%TI-p`{(anu+wYl z=&O6Mlsf?}VA{;tdv`aZ7#ilt$jj1_PO9m#AH(lcV<0HCSDlJn=Rf^s3P0I(**@6v zQ@Qac*lASKMZ7GtzV{tbK^1oRC&ZGF&*pQsa1uJk!Sr-GtRrp3_WI(zocFizU*S%N zLEQY7lpx6S&|@ovv3h>isu#H?^!d66_cxl9V>!5Ef1Pf(G$w4Jb4$c`iaazE;?nHj zfgbmI=6AWL`=Ja>vyK}zccaHyHvXY@;UOU{f?IO z$U6%;)htIl8qk;K8M``$kQUX7f$UU8QCtzth!v}6&(BHmtpHQ&cMw!^-?Fc{o3bN5 z@?iGx+NrvdrPVb5{U|-W(}Lg8ibLp^W>MqMD1*+B%^9v+Y?b}fH32J{mSZfIuE`8` zw}ej8b)UyuzZ#n1FNFHU=zV;KH?`78Z%iC`z}|GY_ct+3vdSXzCq0iW1RMhfZqu-# zz~Y7a#RP@z&c#>8*}J_Rtuo|awjT)W&@%+B;LcpsLQ99m zoNTPCrCM8c*?MDBDR-ExsSiU=;JSiZ zSs!}*inPo#bGIj(c~&U?49YH^x%zp_SM^CJKm-*_UILnjv2GK;LME zdcS3KiYklR{SVDlSFO@A_Rc?J8QH=cyzteTX>!xY6vYV{Oaf=y7RbU4WX8>9&YN{N zR&&s4X0+x#O_3+S4&#)=VMo#|I+uKU2VTI3jn!T5f)WdE9wK>#x56)N;^kKL`0fAF zTYHjm@toe%uvXAuA!&S3FgA#1*8#YD9vjzSamg4uSL)?mHw6*6BdC-z_tJ9-6$q@u zrWk1l8C7T(uq9^uR(g#zh4gaWi)rQ60<%9_YvaD3G@o~KA~=Qo>|p5W#96mqk@7HU z1c+g+eC}MPS}el&nTV#^TTiK5COoxxC){pc{PyM_D43 zxEW48pKJK`*ot+zUkEb%>Ykpge|cxlW`+O3q6Coh>LZcH;j6w7Zyjwptl@&g*Ccmu z(V_=>4zu!uKgzTr_4?XI%MMa_h|L!S!T8t;b+zRVymWvSVs}H?r+4GtlbtIlekEtT zd`bw1cUOkIwZMjx?8PEg57jJK12n9%5FHdBY*Wp95FcS<4I(-WqF+J?PDTqYY6q!J zoJ>J0J_{`l_(jtznBvEX0~k)3L(P);-*I~8C>+@BYNNUYs@PPIm@YvUtmNNK4No<3 zXzWZYSHye%GE9{A(*-v`y;Br6AEM%;7q3x33iCq-^6j6+P@{}=Eh=xYcP!Zm&G1eM zOk3{h`EGLj5X_$&Hc)_$T5HcgULz5)bZ%7?8T_`s@0+P85}mizTO)p36|+NN>xR-Qo{MIg#{TB@Mv zk!rFMV@;-QGVS1tVBg_{Mn-Tp`|6sK9?={sKuHZ8KRB1n3a#YQnl1#`{{Kr05@Tk6 zwh^Z5k=tIU5lF#*T;$xQLwLKsuWRwH4em$&GdqDsLjdWaft~%OHu6-5 zPMH5W!^S#Rs6l`M?}3JRfL$I`+V5=1?51c$8q&zs3n zq28*M$*uxZHef-;twk;8_XeCA@#^_U&iccgX6Vrb{yRI&k z>f6lQttKXKF5N`uS^ttJVsCaicOESti>GmlKDuynVDOZnXy?MDkwV0lv%PNpH5P{x zVo?p+S2Qc+3{rn*M{f>7&N71Lr3IvfhkJHX+>w7mY8BJ3{`tpX|1zWBIdB6EMIx&m z!G831z%F@GA8ie9_Ns8+WsP`qYf&=^NxiKSWXHn)9rMySAn|f(Uc_oCxD<3A+&myO zGXM@V++xJm^VH2)M{r*1?*#AeRIOdL$f3^I%H-GHa{efZe7ez3v$@R){&SW90je%_ zdgiC1jCj(_Em_&&zZ&pAK#tCxXz#>55rcPgLn{S6D=I~<0nPt@nFc*-eh#HCa$D4{ zFGNeyf^nB}EHE>B09J0tdH4Xicpi+SvDe>nAy{+K%~f4z{}HJ67md3Pj|DIK>GviM z2jZhJOx0gnjRs~ciXX>GRRV7|U(mOe<{q4H=cS*Y7JCOTCvJH)&agOPZfii(7_y2DcrIFHDY2I7`Y^p4OPEpz@iqf=c)%)X$l8>^-0r`F4FBByN6<$CcG*Nov zX1RL(#Rjy$LI~yO`~R;GD$MtOb@S4>7p~-=XnFf)(bHDT{l<^NIFs+atHUS9ecAC+ zXzyl%_nhATmZcNBX67H+^#^eqnuEm~H&~i>&qW!ctKIHialHzAjdN z+s7`$s_A{xDy`NArT6moZR{%he(A&>!fLUoyvv{K=g*3{fiJ5vL=0$s+kCII3BENL z84hn-5-j;ojT|f4w!{17(*Mu9b$wKXglH@NcCI^M`j5Wu+x{-uZCmzz*>4 zukugbL_O7z_%AQJ+az{Kz*RX0$EO=OKv=wzI6I5`UmdtZ@yFFdt%q> zK5J(WPMJ3>ph&g}4{BAG^vx%SfonE>*ZPpBwD{^DsP8k^&l|U8-d77(e;>3pYCv04 z=)Y|GU-F`dod4`+=&3=mLp#`c*4LBoa|4@KY>FA1#J;Jk9MCO|&K*vF?}r)vH%Ihu zT`FmK0_P8Z^8(~eL>renR*y4j{KrSIhxlV{Wnh1=n-jdjQ@VOS>hivPa`(OW%gsKX z%D_ET{D`*->O$LFZ4aczkM@Lr_*lQ=ob`l$c)ylqZq&D8{$E-%YhdcO1ry>@B1WuS zF#6qdXyIa zaW0=dnAQdUl7p_~+&Gf9Y;a9$3p9GV!$0QD^AGkTwtcMRD{=Ezm+QAC z9e*Tksof8k^t5n8_?N5Q;XlKPACvrD;7)ztY4N$@-yQ#f`j!2o)P-7_ zM$fb%+ID=79MGop)|K*LU7{AcdmhZHWrmGj8mGtzG^KjR)88eOSDE1wN zX!~b<8|}L{_~-s!@@oLCg}GS)TAK3Na=+rS^`S%78MLwQJ>o~co$tzj_XhtrE}nhS zsQ)BidHYhkN}jbadu!4VS%+}>PW%;Y}kXntDp^bHV3`IKkX&-@I5Ur+UK0z zYXY@=%jbn-|6yM@=}9x~F@(y>N^vfGH)1y}Y~v8V=@Q;2TrlrYe>O!e6 z!~4A=?%cY8V#g~vS=S#!ur_aPZ9mw3iG`?j?Ck2?d&1; zbpRZOW9SY3i;I4Gp1!Ac&S;-=GS*hJVVuv}nCsy!%N+Q>QJ60xg4)>mhi~3W-v@m6 z2LCnKaN#Wen2{VBBIOM0+e&Ot3_;vxr#O(BDCQ0AXy+ZaAsf2tm-WRzW4w}&Y7lD= zd(>t|wCS%c{!f=<)!IN8$ujyU7f&5})u_EU`k(ff{Uhn=A$=}aJ_vIS^btaPzf{c^ zj354H0ms%E?<|K8Hb_rXzDFbWQa^$k@iIbzK zZ)%G9ojI`WBUkH={LqJEAF!V#EkeGN27Hv;MtsXM_!g72?Y#9&zaxIs+xe1C;JjCr_T~+dxlNvpJwr=Q8d+Brhr9+~zr6=@*>N+VZgxU2m0fzb1V~ ztYL9-&nCfn!;5{N5}$rUS4{mpPYd%7v~y+i>x2H!IO|7G8}uuSXcKhM>}M-CAzsJt1&E!h5Ppk0&{S1lwl`Pd z(Zd4YMk+p~i829rO*owt0GJNw2RN|`0w}(UQmH)UVU51laC1y@~iY6Ao*iC`6Zm7LLZwvQhwQw=w89^j*#cHn&R$& z+kQ?9XSwtj8$YV|z1zi4GUnrK&sAS?(Hmrm@ao@J=4w2JI4OI1Gr{>$0=wnH*@ ze{ka>h-bSh%x2XGoC6=EzHKyfdz63G^dND+qFnXm^7d_3<;S=b*0=o3mtntplRPAh z?krY@^tQ*}kjLQxnR;K(1;_WiMf%#CJ|bg14}(5@vS+lk`Mh&+NZ7-mO&W;9eC>)p zeH`Y2=?@d$xpn=i+VaNv#F#4cIvn4X-}c%K+ST^Ml>b`6$>zwNZPdpK=zk+0z5#m6 zWsXA2Bk5*uUNo#K&sU9}UjD^j{qq=OIQnw!J98|EQN66CWAscN2RV0X+j{F6IiOru z`C86l#NoF%qeuUH$%N0cjK0GT6r~}igFSaJ^+eM6kE-R0*v?s;oqd75)29x&q0^Y_ zNBL)$&m8$%8SFXNNceehj&doT? zAI2J))6wH)tOfmR&rd`Rezll$L0``k`fN+RtdU9ZJcd4I!F4M4>7iBUA=w7!ZujVU z^^`v1mfIcZ121B&?X+?&8pk+SGm*YzhA2mj|7t;=m_MSEs$&@g(bn~pKJ)2%`q1HT zOPtJ|Q+DP@NLyZ+>*G}VI~GlikaYx)kl{{l4DG|2V z2+l4|kaY|2?Cl03jqvFL3A z{FWv>vxGYKwZeD_*S(fDpP>iZN?Z~$W~#5pcj>RldW3Pq-ne8`SK?+ofiYwF*H7r{ zq3ri;PdqWf?Zv{eAE2M$e_~BBUQ6EC4rBBTX@~V`UUhc*fxm#*(R@8#ci#_gOc+7h z%O>>@=DW95|LaKF3U#bh8Nr9z-{@oY9d#Wb%Be@)rKhjw%Kvnrqo-T-&h~nop1fym zi2FC`Bv0!rpx;&bb2Cy@8985~2EAHr$OmHU7@q`Jw$B|t=-bealScf=caY2OA>*%B zjt%tn6SNtySxb^bdhg`g$GL-9mrfsU>kfW*>TvDC-#9Mi!QEb$M4+1RogoB zk9Zz{=Wc0}Ym27=v;zycECE88{cuARhE@pqZqeMP$Ndm$@UJ!j?g$4_T%MPBH#2oHMYuBZDyo)b-t8ZGuD zhY9+op3Y&~5xEXdAK2Q(8K3%c?vt#U1Djlel2Ys`><3gTxO Date: Tue, 8 Oct 2013 10:21:54 +0100 Subject: [PATCH 067/933] Adding @dongilbert's Codeception plugin. Fixes #86 --- PHPCI/Plugin/Codeception.php | 84 ++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 PHPCI/Plugin/Codeception.php diff --git a/PHPCI/Plugin/Codeception.php b/PHPCI/Plugin/Codeception.php new file mode 100644 index 00000000..827facf1 --- /dev/null +++ b/PHPCI/Plugin/Codeception.php @@ -0,0 +1,84 @@ + + * @package PHPCI + * @subpackage Plugins + */ +class Codeception implements \PHPCI\Plugin +{ + protected $args; + protected $phpci; + + /** + * @var string|string[] $xmlConfigFile The path (or array of paths) of an xml config for PHPUnit + */ + protected $xmlConfigFile; + + public function __construct(\PHPCI\Builder $phpci, array $options = array()) + { + $this->phpci = $phpci; + + if (isset($options['config'])) { + $this->xmlConfigFile = $options['config']; + } + + if (isset($options['args'])) { + $this->args = $options['args']; + } + } + + /** + * Runs Codeception tests, optionally using specified config file(s). + */ + public function execute() + { + $success = true; + + // Run any config files first. This can be either a single value or an array. + if ($this->xmlConfigFile !== null) { + $success &= $this->runConfigFile($this->xmlConfigFile); + } + + return $success; + } + + protected function runConfigFile($configPath) + { + if (is_array($configPath)) { + return $this->recurseArg($configPath, array($this, "runConfigFile")); + } else { + + $codecept = $this->phpci->findBinary('codecept'); + + if (!$codecept) { + $this->phpci->logFailure('Could not find codeception.'); + return false; + } + + $cmd = $codecept . ' run -c "%s"'; + $success = $this->phpci->executeCommand($cmd, $this->phpci->buildPath . $configPath); + + return $success; + } + } + + protected function recurseArg($array, $callable) + { + $success = true; + foreach ($array as $subItem) { + $success &= call_user_func($callable, $subItem); + } + return $success; + } +} \ No newline at end of file From 47672c014fc75ba09ffeeaa327bd3b1abd8066b3 Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Tue, 8 Oct 2013 10:26:11 +0100 Subject: [PATCH 068/933] Adding basic Behat plugin, fixes #101 --- PHPCI/Plugin/Behat.php | 47 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 PHPCI/Plugin/Behat.php diff --git a/PHPCI/Plugin/Behat.php b/PHPCI/Plugin/Behat.php new file mode 100644 index 00000000..c9c832ce --- /dev/null +++ b/PHPCI/Plugin/Behat.php @@ -0,0 +1,47 @@ + + * @package PHPCI + * @subpackage Plugins + */ +class Behat implements \PHPCI\Plugin +{ + protected $phpci; + + public function __construct(\PHPCI\Builder $phpci, array $options = array()) + { + $this->phpci = $phpci; + } + + /** + * Runs Behat tests. + */ + public function execute() + { + $curdir = getcwd(); + chdir($this->phpci->buildPath); + + $phpspec = $this->phpci->findBinary('phpspec'); + + if (!$phpspec) { + $this->phpci->logFailure('Could not find phpspec.'); + return false; + } + + $success = $this->phpci->executeCommand($phpspec); + chdir($curdir); + + return $success; + } +} From adab6b20c0ccd69ce1b2f4737f5c46ea0f2a6fc4 Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Tue, 8 Oct 2013 12:41:12 +0100 Subject: [PATCH 069/933] Fixing build status image. --- PHPCI/Controller/BuildStatusController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PHPCI/Controller/BuildStatusController.php b/PHPCI/Controller/BuildStatusController.php index 2a0f5c36..80c3e03a 100644 --- a/PHPCI/Controller/BuildStatusController.php +++ b/PHPCI/Controller/BuildStatusController.php @@ -45,6 +45,6 @@ class BuildStatusController extends \PHPCI\Controller } header('Content-Type: image/png'); - die(file_get_contents(APPLICATION_PATH . 'assets/img/build-' . $status . '.png')); + die(file_get_contents(APPLICATION_PATH . 'public/assets/img/build-' . $status . '.png')); } } From 524a0cc58a9714f9afb44a1ac5a4acb8ff01490a Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Tue, 8 Oct 2013 12:50:59 +0100 Subject: [PATCH 070/933] Replacing build status images --- PHPCI/Plugin/Email.php | 5 + composer.json | 22 +- composer.lock | 1554 ++++++---------------------- public/assets/img/build-failed.png | Bin 2059 -> 1959 bytes public/assets/img/build-ok.png | Bin 1839 -> 2344 bytes 5 files changed, 333 insertions(+), 1248 deletions(-) diff --git a/PHPCI/Plugin/Email.php b/PHPCI/Plugin/Email.php index 798a01b5..5e94cd8b 100644 --- a/PHPCI/Plugin/Email.php +++ b/PHPCI/Plugin/Email.php @@ -178,6 +178,11 @@ class Email implements \PHPCI\Plugin protected function getEmailAddresses() { $addresses = array(); + $committer = $this->phpci->getBuild()->getCommitterEmail(); + + if (isset($this->options['committer']) && !empty($committer)) { + $addresses[] = $committer; + } if (isset($this->options['addresses'])) { foreach ($this->options['addresses'] as $address) { diff --git a/composer.json b/composer.json index 11c00c85..fdd10392 100644 --- a/composer.json +++ b/composer.json @@ -27,18 +27,20 @@ "ircmaxell/password-compat": "1.x", "swiftmailer/swiftmailer" : "v5.0.0", "symfony/yaml" : "2.2.x-dev", - "symfony/console" : "2.2.*" + "symfony/console" : "2.2.*", + "behat/behat": "*" }, "suggest": { - "phpunit/phpunit" : "3.*", - "phpmd/phpmd" : "1.*", - "sebastian/phpcpd" : "1.*", - "squizlabs/php_codesniffer": "1.*", - "phpspec/phpspec" : "2.*", - "fabpot/php-cs-fixer" : "0.3.*@dev", - "phploc/phploc": "*", - "atoum/atoum":"*", - "jakub-onderka/php-parallel-lint": "dev-master" + "phpunit/phpunit": "PHP unit testing framework", + "phpmd/phpmd": "PHP Mess Detector", + "sebastian/phpcpd": "PHP Copy/Paste Detector", + "squizlabs/php_codesniffer": "PHP Code Sniffer", + "phpspec/phpspec": "PHP Spec", + "fabpot/php-cs-fixer": "PHP Code Sniffer Fixer", + "phploc/phploc": "PHP Lines of Code", + "atoum/atoum": "Atoum", + "jakub-onderka/php-parallel-lint": "Parallel Linting Tool", + "behat/behat": "Behat BDD Testing" } } diff --git a/composer.lock b/composer.lock index 1776e983..199c4836 100644 --- a/composer.lock +++ b/composer.lock @@ -3,81 +3,135 @@ "This file locks the dependencies of your project to a known state", "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" ], - "hash": "10373193ac4ded0a38f50fec0f5efeed", + "hash": "750d19d503481138217299f81787cea9", "packages": [ { - "name": "atoum/atoum", - "version": "dev-master", + "name": "behat/behat", + "version": "3.0.x-dev", "source": { "type": "git", - "url": "https://github.com/atoum/atoum.git", - "reference": "befbdffcb72841cca0e1d2fb193a9b6cf1f957a4" + "url": "https://github.com/Behat/Behat.git", + "reference": "c2015bdeaf8a091c97779b21603b524d16638ba1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/atoum/atoum/zipball/befbdffcb72841cca0e1d2fb193a9b6cf1f957a4", - "reference": "befbdffcb72841cca0e1d2fb193a9b6cf1f957a4", + "url": "https://api.github.com/repos/Behat/Behat/zipball/c2015bdeaf8a091c97779b21603b524d16638ba1", + "reference": "c2015bdeaf8a091c97779b21603b524d16638ba1", "shasum": "" }, "require": { - "ext-hash": "*", - "ext-json": "*", - "ext-session": "*", - "ext-tokenizer": "*", - "ext-xml": "*", - "php": ">=5.3.3" + "behat/gherkin": "~3.0.3", + "php": ">=5.3.3", + "symfony/class-loader": "~2.1", + "symfony/config": "~2.1", + "symfony/console": "~2.1", + "symfony/dependency-injection": "~2.1", + "symfony/event-dispatcher": "~2.1", + "symfony/finder": "~2.1", + "symfony/translation": "~2.1", + "symfony/yaml": "~2.1" }, - "replace": { - "mageekguy/atoum": "*" + "require-dev": { + "phpunit/phpunit": "~3.7.24" }, "suggest": { - "ext-mbstring": "Provides support for UTF-8 strings" + "behat/mink-extension": "for integration with Mink testing framework", + "behat/symfony2-extension": "for integration with Symfony2 web framework", + "behat/yii-extension": "for integration with Yii web framework" }, "bin": [ - "bin/atoum" + "bin/behat" ], "type": "library", + "extra": { + "branch-alias": { + "dev-develop": "3.0-dev" + } + }, "autoload": { - "classmap": [ - "classes/" - ] + "psr-0": { + "Behat\\Behat": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD" + "MIT" ], "authors": [ { - "name": "Frédéric Hardy", - "email": "frederic.hardy@atoum.org", - "homepage": "http://blog.mageekbox.net" - }, - { - "name": "François Dussert", - "email": "francois.dussert@atoum.org" - }, - { - "name": "Gérald Croes", - "email": "gerald.croes@atoum.org" - }, - { - "name": "Julien Bianchi", - "email": "julien.bianchi@atoum.org" - }, - { - "name": "Ludovic Fleury", - "email": "ludovic.fleury@atoum.org" + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" } ], - "description": "Simple modern and intuitive unit testing framework for PHP 5.3+", - "homepage": "http://www.atoum.org", + "description": "Scenario-oriented BDD framework for PHP 5.3", + "homepage": "http://behat.org/", "keywords": [ - "TDD", - "atoum", - "test", - "unit testing" + "BDD", + "Behat", + "Symfony2" ], - "time": "2013-10-02 12:05:26" + "time": "2013-10-01 07:12:51" + }, + { + "name": "behat/gherkin", + "version": "3.0.x-dev", + "source": { + "type": "git", + "url": "https://github.com/Behat/Gherkin.git", + "reference": "cbc0a34d195bec76eb2b5fb4fad8b9ee524727d4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Behat/Gherkin/zipball/cbc0a34d195bec76eb2b5fb4fad8b9ee524727d4", + "reference": "cbc0a34d195bec76eb2b5fb4fad8b9ee524727d4", + "shasum": "" + }, + "require": { + "php": ">=5.3.1", + "symfony/finder": "~2.0" + }, + "require-dev": { + "symfony/config": "~2.1", + "symfony/yaml": "~2.1" + }, + "suggest": { + "symfony/config": "If you want to use Config component to manage resources", + "symfony/yaml": "If you want to parse features, represented in YAML files" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-develop": "3.0-dev" + } + }, + "autoload": { + "psr-0": { + "Behat\\Gherkin": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + } + ], + "description": "Gherkin DSL parser for PHP 5.3", + "homepage": "http://behat.org/", + "keywords": [ + "BDD", + "Behat", + "Cucumber", + "DSL", + "gherkin", + "parser" + ], + "time": "2013-09-15 20:23:37" }, { "name": "block8/b8framework", @@ -85,12 +139,12 @@ "source": { "type": "git", "url": "https://github.com/Block8/b8framework.git", - "reference": "257b81a4a05a85225c372642456cb546b635e6c1" + "reference": "85424e277758695fcd4275c0bb13611e55a296ff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Block8/b8framework/zipball/257b81a4a05a85225c372642456cb546b635e6c1", - "reference": "257b81a4a05a85225c372642456cb546b635e6c1", + "url": "https://api.github.com/repos/Block8/b8framework/zipball/85424e277758695fcd4275c0bb13611e55a296ff", + "reference": "85424e277758695fcd4275c0bb13611e55a296ff", "shasum": "" }, "require": { @@ -124,55 +178,7 @@ "mvc", "php" ], - "time": "2013-07-30 04:53:35" - }, - { - "name": "fabpot/php-cs-fixer", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/fabpot/PHP-CS-Fixer.git", - "reference": "f6c4f78b3e34c1c67c8ae4543f5d4b7232e23011" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/fabpot/PHP-CS-Fixer/zipball/f6c4f78b3e34c1c67c8ae4543f5d4b7232e23011", - "reference": "f6c4f78b3e34c1c67c8ae4543f5d4b7232e23011", - "shasum": "" - }, - "require": { - "php": ">=5.3.3", - "sebastian/diff": "1.0.*@dev", - "symfony/console": "~2.1", - "symfony/filesystem": "~2.1", - "symfony/finder": "~2.1" - }, - "bin": [ - "php-cs-fixer" - ], - "type": "application", - "extra": { - "branch-alias": { - "dev-master": "0.3.x-dev" - } - }, - "autoload": { - "psr-0": { - "Symfony\\CS": "." - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - } - ], - "description": "A script to automatically fix Symfony Coding Standard", - "time": "2013-09-23 10:31:06" + "time": "2013-10-08 06:45:49" }, { "name": "ircmaxell/password-compat", @@ -213,973 +219,6 @@ ], "time": "2013-04-30 19:58:08" }, - { - "name": "jakub-onderka/php-parallel-lint", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/JakubOnderka/PHP-Parallel-Lint.git", - "reference": "b3d054c8cb380fa77806ab108bbd689fb5d7beb7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/JakubOnderka/PHP-Parallel-Lint/zipball/b3d054c8cb380fa77806ab108bbd689fb5d7beb7", - "reference": "b3d054c8cb380fa77806ab108bbd689fb5d7beb7", - "shasum": "" - }, - "bin": [ - "run" - ], - "type": "library", - "autoload": { - "classmap": [ - "./" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD" - ], - "authors": [ - { - "name": "Jakub Onderka", - "email": "jakub.onderka@gmail.com", - "homepage": "http://acci.cz/" - } - ], - "description": "This tool check syntax of PHP files about 20x faster than serial check.", - "homepage": "https://github.com/JakubOnderka/PHP-Parallel-Lint", - "time": "2013-09-10 19:07:09" - }, - { - "name": "pdepend/pdepend", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/pdepend/pdepend.git", - "reference": "820ceae75b405cd8ec49c5fd4c39a74ef1ea35d0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/pdepend/pdepend/zipball/820ceae75b405cd8ec49c5fd4c39a74ef1ea35d0", - "reference": "820ceae75b405cd8ec49c5fd4c39a74ef1ea35d0", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "require-dev": { - "phpunit/phpunit": "@stable", - "squizlabs/php_codesniffer": "@stable" - }, - "bin": [ - "src/bin/pdepend" - ], - "type": "library", - "notification-url": "https://packagist.org/downloads/", - "description": "Official version of pdepend to be handled with Composer", - "time": "2013-10-03 11:41:37" - }, - { - "name": "phploc/phploc", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phploc.git", - "reference": "41d3eb17c3a8185d9c7ee0138e3cf44bf6d053f4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phploc/zipball/41d3eb17c3a8185d9c7ee0138e3cf44bf6d053f4", - "reference": "41d3eb17c3a8185d9c7ee0138e3cf44bf6d053f4", - "shasum": "" - }, - "require": { - "php": ">=5.3.3", - "sebastian/finder-facade": ">=1.1.0", - "sebastian/git": ">=1.0.0", - "sebastian/version": ">=1.0.0", - "symfony/console": ">=2.2.0" - }, - "bin": [ - "composer/bin/phploc" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "A tool for quickly measuring the size of a PHP project.", - "homepage": "https://github.com/sebastianbergmann/phploc", - "time": "2013-09-19 13:37:38" - }, - { - "name": "phpmd/phpmd", - "version": "1.5.0", - "source": { - "type": "git", - "url": "https://github.com/phpmd/phpmd.git", - "reference": "692b7b1b64518091b2b1bea91b489dbb13598c07" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpmd/phpmd/zipball/692b7b1b64518091b2b1bea91b489dbb13598c07", - "reference": "692b7b1b64518091b2b1bea91b489dbb13598c07", - "shasum": "" - }, - "require": { - "pdepend/pdepend": ">=1.1.1", - "php": ">=5.3.0" - }, - "bin": [ - "src/bin/phpmd" - ], - "type": "library", - "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "../../pdepend/pdepend/src/main/php", - "src/main/php" - ], - "description": "Official version of PHPMD handled with Composer.", - "time": "2013-07-26 14:47:02" - }, - { - "name": "phpspec/php-diff", - "version": "v1.0.1", - "source": { - "type": "git", - "url": "https://github.com/phpspec/php-diff.git", - "reference": "8d82ac415225fac373a4073ba14b1fe286aa2312" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpspec/php-diff/zipball/8d82ac415225fac373a4073ba14b1fe286aa2312", - "reference": "8d82ac415225fac373a4073ba14b1fe286aa2312", - "shasum": "" - }, - "type": "library", - "autoload": { - "psr-0": { - "Diff": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Chris Boulton", - "homepage": "http://github.com/chrisboulton" - } - ], - "description": "A comprehensive library for generating differences between two hashable objects (strings or arrays).", - "time": "2012-11-08 08:55:45" - }, - { - "name": "phpspec/phpspec", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/phpspec/phpspec.git", - "reference": "d8f64251d27dc1bb17b76608d620374b6b51f9ff" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpspec/phpspec/zipball/d8f64251d27dc1bb17b76608d620374b6b51f9ff", - "reference": "d8f64251d27dc1bb17b76608d620374b6b51f9ff", - "shasum": "" - }, - "require": { - "php": ">=5.3.3", - "phpspec/php-diff": "~1.0.0", - "phpspec/prophecy": "~1.0.2", - "symfony/console": "~2.1", - "symfony/event-dispatcher": "~2.1", - "symfony/finder": "~2.1", - "symfony/yaml": "~2.1" - }, - "suggest": { - "whatthejeff/nyancat-scoreboard": "~1.1 – Enables the Nyan Cat formatter" - }, - "bin": [ - "bin/phpspec" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "psr-0": { - "PhpSpec": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - }, - { - "name": "Marcello Duarte", - "homepage": "http://marcelloduarte.net/" - } - ], - "description": "Specification-oriented BDD framework for PHP 5.3+", - "homepage": "http://phpspec.net/", - "keywords": [ - "BDD", - "SpecBDD", - "TDD", - "spec", - "specification", - "testing", - "tests" - ], - "time": "2013-10-04 08:02:36" - }, - { - "name": "phpspec/prophecy", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/phpspec/prophecy.git", - "reference": "82d11835df7eccf904ea0c84fbb7304080346bc1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/82d11835df7eccf904ea0c84fbb7304080346bc1", - "reference": "82d11835df7eccf904ea0c84fbb7304080346bc1", - "shasum": "" - }, - "require-dev": { - "phpspec/phpspec": "2.0.*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-0": { - "Prophecy\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - }, - { - "name": "Marcello Duarte", - "email": "marcello.duarte@gmail.com" - } - ], - "description": "Highly opinionated mocking framework for PHP 5.3+", - "homepage": "http://phpspec.org", - "keywords": [ - "Double", - "Dummy", - "fake", - "mock", - "spy", - "stub" - ], - "time": "2013-09-30 08:21:23" - }, - { - "name": "phpunit/php-code-coverage", - "version": "1.2.x-dev", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "466e7cd2554b4e264c9e3f31216d25ac0e5f3d94" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/466e7cd2554b4e264c9e3f31216d25ac0e5f3d94", - "reference": "466e7cd2554b4e264c9e3f31216d25ac0e5f3d94", - "shasum": "" - }, - "require": { - "php": ">=5.3.3", - "phpunit/php-file-iterator": ">=1.3.0@stable", - "phpunit/php-text-template": ">=1.1.1@stable", - "phpunit/php-token-stream": ">=1.1.3@stable" - }, - "require-dev": { - "phpunit/phpunit": "3.7.*@dev" - }, - "suggest": { - "ext-dom": "*", - "ext-xdebug": ">=2.0.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.2.x-dev" - } - }, - "autoload": { - "classmap": [ - "PHP/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "" - ], - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", - "homepage": "https://github.com/sebastianbergmann/php-code-coverage", - "keywords": [ - "coverage", - "testing", - "xunit" - ], - "time": "2013-09-10 08:14:32" - }, - { - "name": "phpunit/php-file-iterator", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "2deb24c65ea78e126daa8d45b2089ddc29ec1d26" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/2deb24c65ea78e126daa8d45b2089ddc29ec1d26", - "reference": "2deb24c65ea78e126daa8d45b2089ddc29ec1d26", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "autoload": { - "classmap": [ - "File/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "" - ], - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "FilterIterator implementation that filters files based on a list of suffixes.", - "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", - "keywords": [ - "filesystem", - "iterator" - ], - "time": "2013-01-07 10:47:05" - }, - { - "name": "phpunit/php-text-template", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "1eeef106193d2f8c539728e566bb4793071a9e18" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/1eeef106193d2f8c539728e566bb4793071a9e18", - "reference": "1eeef106193d2f8c539728e566bb4793071a9e18", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "autoload": { - "classmap": [ - "Text/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "" - ], - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", - "keywords": [ - "template" - ], - "time": "2013-01-07 10:56:17" - }, - { - "name": "phpunit/php-timer", - "version": "1.0.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/19689d4354b295ee3d8c54b4f42c3efb69cbc17c", - "reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "autoload": { - "classmap": [ - "PHP/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "" - ], - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", - "keywords": [ - "timer" - ], - "time": "2013-08-02 07:42:54" - }, - { - "name": "phpunit/php-token-stream", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "5220af2a7929aa35cf663d97c89ad3d50cf5fa3e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/5220af2a7929aa35cf663d97c89ad3d50cf5fa3e", - "reference": "5220af2a7929aa35cf663d97c89ad3d50cf5fa3e", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": ">=5.3.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.2-dev" - } - }, - "autoload": { - "classmap": [ - "PHP/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "" - ], - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "Wrapper around PHP's tokenizer extension.", - "homepage": "https://github.com/sebastianbergmann/php-token-stream/", - "keywords": [ - "tokenizer" - ], - "time": "2013-09-13 04:58:23" - }, - { - "name": "phpunit/phpunit", - "version": "3.7.x-dev", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "4b024e753e3421837afbcca962c8724c58b39376" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/4b024e753e3421837afbcca962c8724c58b39376", - "reference": "4b024e753e3421837afbcca962c8724c58b39376", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-pcre": "*", - "ext-reflection": "*", - "ext-spl": "*", - "php": ">=5.3.3", - "phpunit/php-code-coverage": "~1.2.1", - "phpunit/php-file-iterator": ">=1.3.1", - "phpunit/php-text-template": ">=1.1.1", - "phpunit/php-timer": ">=1.0.4", - "phpunit/phpunit-mock-objects": "~1.2.0", - "symfony/yaml": "~2.0" - }, - "require-dev": { - "pear-pear/pear": "1.9.4" - }, - "suggest": { - "ext-json": "*", - "ext-simplexml": "*", - "ext-tokenizer": "*", - "phpunit/php-invoker": ">=1.1.0,<1.2.0" - }, - "bin": [ - "composer/bin/phpunit" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.7.x-dev" - } - }, - "autoload": { - "classmap": [ - "PHPUnit/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "", - "../../symfony/yaml/" - ], - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "The PHP Unit Testing framework.", - "homepage": "http://www.phpunit.de/", - "keywords": [ - "phpunit", - "testing", - "xunit" - ], - "time": "2013-09-16 03:09:52" - }, - { - "name": "phpunit/phpunit-mock-objects", - "version": "1.2.x-dev", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "3e40f3b3f18c044a24688fe406440d7fd537744a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/3e40f3b3f18c044a24688fe406440d7fd537744a", - "reference": "3e40f3b3f18c044a24688fe406440d7fd537744a", - "shasum": "" - }, - "require": { - "php": ">=5.3.3", - "phpunit/php-text-template": ">=1.1.1@stable" - }, - "require-dev": { - "pear-pear/pear": "1.9.4", - "phpunit/phpunit": "3.7.*@dev" - }, - "suggest": { - "ext-soap": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.2.x-dev" - } - }, - "autoload": { - "classmap": [ - "PHPUnit/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "" - ], - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } - ], - "description": "Mock Object library for PHPUnit", - "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", - "keywords": [ - "mock", - "xunit" - ], - "time": "2013-07-23 04:42:59" - }, - { - "name": "sebastian/diff", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "6affc0bf53304452d7f296588f6d5837070b4789" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/6affc0bf53304452d7f296588f6d5837070b4789", - "reference": "6affc0bf53304452d7f296588f6d5837070b4789", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "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", - "role": "lead" - }, - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - } - ], - "description": "Diff implementation", - "homepage": "http://www.github.com/sebastianbergmann/diff", - "keywords": [ - "diff" - ], - "time": "2013-07-09 05:45:26" - }, - { - "name": "sebastian/finder-facade", - "version": "1.1.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/finder-facade.git", - "reference": "1e396fda3449fce9df032749fa4fa2619e0347e0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/finder-facade/zipball/1e396fda3449fce9df032749fa4fa2619e0347e0", - "reference": "1e396fda3449fce9df032749fa4fa2619e0347e0", - "shasum": "" - }, - "require": { - "symfony/finder": ">=2.2.0", - "theseer/fdomdocument": ">=1.3.1" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "FinderFacade is a convenience wrapper for Symfony's Finder component.", - "homepage": "https://github.com/sebastianbergmann/finder-facade", - "time": "2013-05-28 06:10:03" - }, - { - "name": "sebastian/git", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/git.git", - "reference": "9df628c8c27522ba122f85d4ca8f13bd6fe8b9aa" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/git/zipball/9df628c8c27522ba122f85d4ca8f13bd6fe8b9aa", - "reference": "9df628c8c27522ba122f85d4ca8f13bd6fe8b9aa", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.2-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Simple wrapper for Git", - "homepage": "http://www.github.com/sebastianbergmann/git", - "keywords": [ - "git" - ], - "time": "2013-08-09 07:12:19" - }, - { - "name": "sebastian/phpcpd", - "version": "1.4.x-dev", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpcpd.git", - "reference": "090575dbd6486bff545a7426f98e988c8c597c1b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpcpd/zipball/090575dbd6486bff545a7426f98e988c8c597c1b", - "reference": "090575dbd6486bff545a7426f98e988c8c597c1b", - "shasum": "" - }, - "require": { - "php": ">=5.3.3", - "phpunit/php-timer": ">=1.0.4,<1.1.0", - "sebastian/finder-facade": "~1.1", - "sebastian/version": ">=1.0.0", - "symfony/finder": ">=2.1.2", - "theseer/fdomdocument": "~1.4", - "zetacomponents/base": ">=1.8", - "zetacomponents/console-tools": ">=1.6" - }, - "bin": [ - "composer/bin/phpcpd" - ], - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Copy/Paste Detector (CPD) for PHP code.", - "homepage": "https://github.com/sebastianbergmann/phpcpd", - "time": "2013-07-30 14:37:42" - }, - { - "name": "sebastian/version", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "b5ed060223916b485002b02605e20ba04a741f03" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/b5ed060223916b485002b02605e20ba04a741f03", - "reference": "b5ed060223916b485002b02605e20ba04a741f03", - "shasum": "" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", - "time": "2013-07-31 05:26:57" - }, - { - "name": "squizlabs/php_codesniffer", - "version": "1.5.0RC4", - "source": { - "type": "git", - "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "146a9b54e4adeaca0a3ae073e0a8a03570d6cc43" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/146a9b54e4adeaca0a3ae073e0a8a03570d6cc43", - "reference": "146a9b54e4adeaca0a3ae073e0a8a03570d6cc43", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": ">=5.1.2" - }, - "suggest": { - "phpunit/php-timer": "dev-master" - }, - "bin": [ - "scripts/phpcs" - ], - "type": "library", - "autoload": { - "classmap": [ - "CodeSniffer.php", - "CodeSniffer/CLI.php", - "CodeSniffer/Exception.php", - "CodeSniffer/File.php", - "CodeSniffer/Report.php", - "CodeSniffer/Reporting.php", - "CodeSniffer/Sniff.php", - "CodeSniffer/Tokens.php", - "CodeSniffer/Reports/", - "CodeSniffer/CommentParser/", - "CodeSniffer/Tokenizers/", - "CodeSniffer/DocGenerators/", - "CodeSniffer/Standards/AbstractPatternSniff.php", - "CodeSniffer/Standards/AbstractScopeSniff.php", - "CodeSniffer/Standards/AbstractVariableSniff.php", - "CodeSniffer/Standards/IncorrectPatternException.php", - "CodeSniffer/Standards/Generic/Sniffs/", - "CodeSniffer/Standards/MySource/Sniffs/", - "CodeSniffer/Standards/PEAR/Sniffs/", - "CodeSniffer/Standards/PSR1/Sniffs/", - "CodeSniffer/Standards/PSR2/Sniffs/", - "CodeSniffer/Standards/Squiz/Sniffs/", - "CodeSniffer/Standards/Zend/Sniffs/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Greg Sherwood", - "role": "lead" - } - ], - "description": "PHP_CodeSniffer tokenises 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": "2013-09-26 00:14:02" - }, { "name": "swiftmailer/swiftmailer", "version": "v5.0.0", @@ -1229,6 +268,104 @@ ], "time": "2013-04-30 17:35:30" }, + { + "name": "symfony/class-loader", + "version": "dev-master", + "target-dir": "Symfony/Component/ClassLoader", + "source": { + "type": "git", + "url": "https://github.com/symfony/ClassLoader.git", + "reference": "7bb58f990b5c2d8fa0b70d29585eddb52d360a4c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/ClassLoader/zipball/7bb58f990b5c2d8fa0b70d29585eddb52d360a4c", + "reference": "7bb58f990b5c2d8fa0b70d29585eddb52d360a4c", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "symfony/finder": "~2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.4-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\ClassLoader\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony ClassLoader Component", + "homepage": "http://symfony.com", + "time": "2013-09-19 09:47:34" + }, + { + "name": "symfony/config", + "version": "dev-master", + "target-dir": "Symfony/Component/Config", + "source": { + "type": "git", + "url": "https://github.com/symfony/Config.git", + "reference": "9a3c831697349cf6e9b5302b7beb0b97e6cdac41" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Config/zipball/9a3c831697349cf6e9b5302b7beb0b97e6cdac41", + "reference": "9a3c831697349cf6e9b5302b7beb0b97e6cdac41", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "symfony/filesystem": "~2.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.4-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Config\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony Config Component", + "homepage": "http://symfony.com", + "time": "2013-09-19 09:47:34" + }, { "name": "symfony/console", "version": "2.2.x-dev", @@ -1276,6 +413,63 @@ "homepage": "http://symfony.com", "time": "2013-09-25 05:58:50" }, + { + "name": "symfony/dependency-injection", + "version": "dev-master", + "target-dir": "Symfony/Component/DependencyInjection", + "source": { + "type": "git", + "url": "https://github.com/symfony/DependencyInjection.git", + "reference": "48a1803399404ff780de635da7e9eb746c655c65" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/DependencyInjection/zipball/48a1803399404ff780de635da7e9eb746c655c65", + "reference": "48a1803399404ff780de635da7e9eb746c655c65", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "symfony/config": "~2.2", + "symfony/expression-language": "~2.4", + "symfony/yaml": "~2.0" + }, + "suggest": { + "symfony/config": "", + "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them", + "symfony/yaml": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.4-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\DependencyInjection\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony DependencyInjection Component", + "homepage": "http://symfony.com", + "time": "2013-09-29 19:43:28" + }, { "name": "symfony/event-dispatcher", "version": "dev-master", @@ -1424,6 +618,61 @@ "homepage": "http://symfony.com", "time": "2013-09-19 09:47:34" }, + { + "name": "symfony/translation", + "version": "dev-master", + "target-dir": "Symfony/Component/Translation", + "source": { + "type": "git", + "url": "https://github.com/symfony/Translation.git", + "reference": "d7e84f71f1856f75025618aca6cdaf074ab84220" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Translation/zipball/d7e84f71f1856f75025618aca6cdaf074ab84220", + "reference": "d7e84f71f1856f75025618aca6cdaf074ab84220", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "symfony/config": "~2.0", + "symfony/yaml": "~2.2" + }, + "suggest": { + "symfony/config": "", + "symfony/yaml": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.4-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Translation\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony Translation Component", + "homepage": "http://symfony.com", + "time": "2013-09-27 13:28:11" + }, { "name": "symfony/yaml", "version": "2.2.x-dev", @@ -1470,175 +719,6 @@ "description": "Symfony Yaml Component", "homepage": "http://symfony.com", "time": "2013-09-22 17:30:19" - }, - { - "name": "theseer/fdomdocument", - "version": "1.4.2", - "source": { - "type": "git", - "url": "https://github.com/theseer/fDOMDocument.git", - "reference": "871515e1a48aa216627ad7cbfc3516d0838578e1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/theseer/fDOMDocument/zipball/871515e1a48aa216627ad7cbfc3516d0838578e1", - "reference": "871515e1a48aa216627ad7cbfc3516d0838578e1", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "lib-libxml": "*", - "php": ">=5.3.3" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "lead" - } - ], - "description": "The classes contained within this repository extend the standard DOM to use exceptions at all occasions of errors instead of PHP warnings or notices. They also add various custom methods and shortcuts for convinience and to simplify the usage of DOM.", - "homepage": "https://github.com/theseer/fDOMDocument", - "time": "2013-06-30 12:26:39" - }, - { - "name": "zetacomponents/base", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/zetacomponents/Base.git", - "reference": "642f63a8a72c32996f1aaf8a317fdf746bc32ce7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/zetacomponents/Base/zipball/642f63a8a72c32996f1aaf8a317fdf746bc32ce7", - "reference": "642f63a8a72c32996f1aaf8a317fdf746bc32ce7", - "shasum": "" - }, - "require-dev": { - "zetacomponents/unit-test": "*" - }, - "type": "library", - "autoload": { - "classmap": [ - "src" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Sergey Alexeev" - }, - { - "name": "Sebastian Bergmann" - }, - { - "name": "Jan Borsodi" - }, - { - "name": "Raymond Bosman" - }, - { - "name": "Frederik Holljen" - }, - { - "name": "Kore Nordmann" - }, - { - "name": "Derick Rethans" - }, - { - "name": "Vadym Savchuk" - }, - { - "name": "Tobias Schlitt" - }, - { - "name": "Alexandru Stanoi" - } - ], - "description": "The Base package provides the basic infrastructure that all packages rely on. Therefore every component relies on this package.", - "homepage": "https://github.com/zetacomponents", - "time": "2012-05-21 11:21:36" - }, - { - "name": "zetacomponents/console-tools", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/zetacomponents/ConsoleTools.git", - "reference": "90156abef01e4215fda8b9740f77c322f126fb02" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/zetacomponents/ConsoleTools/zipball/90156abef01e4215fda8b9740f77c322f126fb02", - "reference": "90156abef01e4215fda8b9740f77c322f126fb02", - "shasum": "" - }, - "require": { - "zetacomponents/base": "*" - }, - "require-dev": { - "zetacomponents/unit-test": "*" - }, - "type": "library", - "autoload": { - "classmap": [ - "src" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Sergey Alexeev" - }, - { - "name": "Sebastian Bergmann" - }, - { - "name": "Jan Borsodi" - }, - { - "name": "Raymond Bosman" - }, - { - "name": "Frederik Holljen" - }, - { - "name": "Kore Nordmann" - }, - { - "name": "Derick Rethans" - }, - { - "name": "Vadym Savchuk" - }, - { - "name": "Tobias Schlitt" - }, - { - "name": "Alexandru Stanoi" - } - ], - "description": "A set of classes to do different actions with the console (also called shell). It can render a progress bar, tables and a status bar and contains a class for parsing command line options.", - "homepage": "https://github.com/zetacomponents", - "time": "2012-05-21 09:55:34" } ], "packages-dev": [ @@ -1650,9 +730,7 @@ "minimum-stability": "dev", "stability-flags": { "block8/b8framework": 20, - "symfony/yaml": 20, - "fabpot/php-cs-fixer": 20, - "jakub-onderka/php-parallel-lint": 20 + "symfony/yaml": 20 }, "platform": [ diff --git a/public/assets/img/build-failed.png b/public/assets/img/build-failed.png index 75b0893665fefa1b8198821d1f84660c3b037286..2aa1e125c5c0206873a645ae94319cb6216941d0 100644 GIT binary patch delta 1906 zcmV-&2aWiP5T_3ziBL{Q4GJ0x0000DNk~Le0001e0000U2nGNE00H0;(UBo2k!mM@ z02OpaSad^jWnpw_Z*Cw|X>DZyIWI9VFEKMQg)Y(n00#g`L_t(&f$f@qOw{)o$6w#? z0mmWS35Xrw0To?~LMx@SutV*-Sf>l5wM`amX4CGEonqE4%f`|*OSdd!WSMQ&SV))k zN7`^{T4xGfoHjb0M^&^P<8Y!xg*%mhBFOc7M>*h*yZv$Ax14gn@6g42{dM=b=kxhK znmtc5-HL6zjwkxoQ=wi}OfNONpo7Nn#*>YSB%X zuJPO-JJ`OllATXhv;V|7e*fMPs>_x!H1Z|Zi7DAOSFNJ4{$alR(7kbUJW2FK9o~mV zBs^{pKipJFUUnwiH&>xnDfw(**iUeP>oy0!d+!*gOV?s{I7#e89dqy3<{~OY{`9_) zAJ?sA|H*SmlT$2Ryomd>xy-nKX6e1?FY;Rl{=;tl#+aQBgv)HkX*LJ7Ni8ZOrKo7$ z-yB9G0LqFAM2#loGgP&;A;+WfQK8Y0R#6esKA4}u2rBxOF*~n!oaU@~jPjDD?5L|E zKQ|MA6@@uGw`mP;9q8fU=>gns58tUQ;iYHRp;@E^;P`(p^XoVN$*kLddxP|w$LJdz zrMx6BVn@TlZZKeNX$fkR-PA;OQ`4<;+uIr4wJWI2&CggqJ|1#B8XwbjItn^F!?I7& zYRTHR4UJwO_67(j`WNF9{H*mL*KH0~6)okM$&BgJ2=D&t2{Kh_0BrtNDeoUSgU17c z0FORcA`1>svuYW-axMRV>h%Nl`4l^cy6H{GX@^`n*hP}O= ztZmy8;hIb&KF(+FmL#OMHmofzxNSDB*42e2e85C*#QGHqQJ}Oi2Y}MTTtrb|!r`1_ z<)8R_=BWcT8A`wJ*S`EJs6)2{sjUrwwDNM&%F6-&lYgfoYBXdwHId)Z0eCm-*o!ZQ zo)_qc9raZxM3G&8uXga4&TfAC+Q%qFftpo?@$>o)z%5C#z*!9qX!LpjrnxC|Vj>wkSYX zMB_WF&P* zfYt6_e=8LJfkHJ;*X{FxVfnKt2t2ehKZN`7;bKnCyyN*D9eyR1y}cdqo^oIIo7K>O z-C)3OFp%5a993Bq4#V~J^Nt6r9s)6UlelfR;I^R_tFKupi~i?*-Sl0$#skamp|l_y zr)w5L`1&q?KYjixwG~>_8DE!dU1u)KYh`7mvA!a*9h^w)?d`ZM763A~Y>~$^wrr74 zjnvkbuuMME3L{>G6n$Mq5gQ&T=4kH_0Ec@9IovY{K!!3E0RhjAgznJ42RU_an6ly} zOwYLJxiI3l*2DJ|P<&5fPR-2(tCfBDgca3lM2$v&4!5F4gQ!-^S$v{Ik2HG^#@u_{ zmy8A?td88v&#vdSeW&>7({pm81-x~)4?zHBY6`Z=X-=NKa?2XZR0Yp(dN7>FEeH;y zQ7+TmHe0|@>#|rDqu53O{(Xil=JJ=(k*F+UX)cCzI6+(1D8tm)Y!B zrUlB-M;jZl8w_$86i&Bmp>UbaIE+TwKk$Zs_1xy>kaJ`DC->&HD|qK%FK&+qQ4kPs zwC3^_XOOozBbKET%G}0F=8gJxJ~Vp0UqTmd`4*^hxH-=VWCyoa8KLYodVScrM@()nYa>@xFn4Fs7!08J(U9+Jl-bUP#gw)y!c(deS_g`8?gU9l&t7kIoYVJaT^_FKk^K sFTau`keEeJ^8ZIN4&Q|&MZXLAA30aiIcre3>;M1&07*qoM6N<$f?;2^Q~&?~ delta 1989 zcmV;$2Ritt4~q~XiBL{Q4GJ0x0000DNk~Le0001F0000L2nGNE0I-}(Fp(iBk#Q3X zF*h$YFEKL(;W?6#Mk;>?WJyFpRA_t)4qN0H^2GK_xpd|`@J`#5W?xxr}ac4 zajLeqHlrwt!6w3=KKyW_LwGI-m|lYZ?_sC~7K9K0Xwsm!xA)CrvG`G;P-xq*C|?4T z8y&#&!vOk$_wK;^z#Z@f>FMb?t13LivgQj5b%Gvzx0`+bZCy_d;si;vX<+c5F)4pD2h_HUg`QU!uDbRr8d$o%0Fy+ zK~OvmKF}P;NtYu`LW&SBvb_>vilVF&Z(RT-1ef}kzDr9bY<59>q&3HJnxZUR*F^|H zM@I*Zjg0|mH*VaZP$;0PDqUS&6bc0Q>S=;93W}2Iu z12GDP0=IA9W@%{&MNv>y6;)LuMR6mZVTWZ=YZz-pdyvgikC#May3Wrs8NSWsSe~Ay z22g+o)1<~Q_&%4T(KM;`>lzS?@cmsb#}3n^-Y|l=7V~-LM@Fav)XJeYsgC1NtrUwz zOw(lV-o1a=wvAyJoIigaP19IjUJm+-#bQv79Xm!Mkzi$ICFrkYyJycHIy*aq`yD-c zl#3TH(%#;_#?H2FY}+myqw9-OuI#WZwwWf6fX9BB1(y8&S2G!2@9U!}n`IVw>bGw< zO?FroGeCW){zDbtb3oFvi2LnJy3XGIehy7f^R$1Zg(rA3x?HT9>qd<&Rv*}P$>;M7 z4GkfL;QICJ?A*DN`}gnTx-PEkQY;ox)m8m%+oo77;<_%5<3s}Tm)e2)~!Li5Q11NR(7*cEvfG|O?1OR10Lt|+|7Sv zs0C7%#ni|M^V8FOH8MhHCc}mPeiA;R!WSZ3m*kc69RseKCNjcWU@Oo7Y*|R;2IzQgEi(gn4k8`=e zr<^I_x>LdT79h@A0i}J?Gy(V~pQj0LhLk={)fo~d6+ALisTA>eJorC4I!Zd7=IYg} z%+1Y_PN$ijo#pP`yTKM%+R7qD2yc<)ecIdGIeYdj6B85Mym@oo4y)V@i5h=a!hi7X zzz+Yn4%ls3d^7l2mhbK>-Fg`xc@bECIsxmq{N-C9NXlRIdJkG?#1ZU2i zSwmd7o=&Gpr_%sTOiXa^-aUU>TU)tx>lOerGcybi4>K?@fU2tN1bdMs3p~Pg;77n# z!(jKRQ~a&Fn}3gu@d{7`zRKnJ-QXavm?q1t72xL`9sDJ9@4qSvF7Q@gA8+;b1?^v) zJBI@t?(5^Q-(L9gOD@0rE}xBzkYIHdmBy`fb#+xfy}`PKTe`%_(F=dkC-QQq{c?xH zaJ%#^&mmD~k=`3ODn7#^@E^SOJcYNWyTE___IjYn|9y(LlUl-C_hm9~0R9IQ@Xir6 zc*4egKZ|}_3~%<;08e~>GPlI=W>TZ?Po8DM&|KG@_2VfENuVU6bjw-o1YOz}Uc#bm z%jJwpuUH^@$*RW_LK1%|lk_&=HNUMMk02Fz6>le{!J6c}4Nn9+2=Ni#2a`>Q^qo5! z9l{Gif!hka-z&r&|NknQo11mVaZWjolfnDMxM^_W>#sLDgy+R{4fNN-&>utJ)c+48 Xd9{v@kJ6L?000R9NkvXXu0mjf^7gxU diff --git a/public/assets/img/build-ok.png b/public/assets/img/build-ok.png index ca59686f4b8fde6ae25da98acd00a0d1dd67558e..e74657b7a453901510565b9c57965c8ce7a88967 100644 GIT binary patch delta 2294 zcmVDZyIWI9VFEKMQg)Y(n00@CeL_t(&f$f@YY*XhI$A5Nk z(j;I9g4u|1a0hA0iwBfK$zn>ni7^r-YH6n_X`w1#<`A@Qnl?|YAIemrS<`5>DNWRA zv|XcERm;$v8U__CK*C#~%i<6cBEZ;x1PTdB>?Y2OL%a{hcQ$^B9h=e#|0GNI<^Not z=brnV=bYzW(ZIlfn*@&(;)iWOg0G*1(;q=%cKX}<&hWjTeZa5ZJHqJrWISw2@PHA; zGNXRkGBJ6V_ACBQe*e&a*rVMLlfl*zD^{m9;J8d8L!F_H z=o-y^%>bw~)W{^Vi1aPDTL4g|E78a`WT$7(NE^!jP<;ufkJSGA3G4LU{;zn~c#ezK zehO3zs482tASD&BZ`9kTQk08Vv(Ugx)a|Ha)Y z&kWLK?We=iM}bNnwV_@N76bZDeaQ0_stR_m*d6j_G*}wg(YgZwU7ikIzAogoOuZ(m zTdQ%n90BKwsdhU1z52%DMqv8>D$|rysw&Ywp^f+f2b?! ziNTGVCH zp5fXpJAeJvX4b8EoX^i*qUvoU$J(z^`b-W0qZ6JIO(gQP8FvMiY%7<3TX4GDOYwQbnoXdGnB&mcABY-{Er5U}>OIRT*$?@8~MXD4?;pQ5Z$K zd>yqtwUlO+3df><(T1}>+Wy*IcIqzGq3_h=a5*SzDvQYRArrY)hjQpkXEI@K_D;0jd*+8iwaEqyJRhs*(bsYD8Rasa!_9`=8u(LM+?at#%# z3LtcUde4Eq8a?^6ETdOhknu7j!G+JMp`{=d9A_H0C+~ZHw0IxDlk|K7%YZ>j9-_3ufsZG<-m;tLV{?p7?4V&ROM9p zO(oH2eJO7$4|;sMsv;P^Q;1_Cu!8Vq+Qpgj!W?1yPy5fZ>u<-|-`q|6mD>zECxj#p z;M}DfIDAI-sm|-PT(~am&q+(>S@naN-;`-eO0r5Y78}{UVz=M921^4rrwxFTtp7y- z-)-N2NC2Msvc9MuW3iD6RfQ139WDp@PCaE!Wi!(HQY0@5`ctMWW4II3GwtHcI(0Vd zzq^=Y?biS}+GgQsn+1Th6bT|kxbG!&NB(W0wfiQyi{~>wF-hCyTYlGi`pGO7e=GXM zgis9pcpxI3cP^Joq{xzGLim#<%aBT>LKYu?Y;eZHLGhd^_tk(wiBDvMoV8y zK!p&E7NXDI!{IxRdy~{qM6?Y1=Bv-LS+k5JvB>YZbKJ$$y_8)fg2$3);c$*~=3;Mv z!&4+lY}>dzq7KB9=Dud3Omny#lsA?8{k)4QHm6OP0zy%$DD`txunJ)&+IZj_sWc0J zG-0(7EyIdMB3|FThL@hr<$TXguG}2t()9r@So-{Q+kUiy!QoN z@};#bl1roMM_lkN$wDoL_%7`D*6endJ)i^&RenXIjW^Y;)xz|p&6$2>E~>S6#r!#b z+4tUmw(;TNcED4kr>4x}C&jCHQIku5m+cPYu1OZon@xV4R^Xz=I%4%J(`r`N1l3iy zR%5UjgmOonp%yam`l5O?a*dzXi?>3EMjOw*HTN|Oc7rFZ-=EI}Bc_+!o7ODl!^7=N zx!s6GBEV&KwJQ=n!O-Xiowy^Yn>20IV zPzyIc67Btjsy+aW#YVpeXga+%M;md*AJdmJ4M#d~j=8`M&anv&ox6-{^6vBvzXt3s zJGDKvfG4xR{|5S^&QSZU(5W-j9NTbA$nL$#n9XTJDv_eg(+ML=BiBT%jWta%XWvd!g!ka@Cg?sV$nV>h)G02RA_0!g}V zQ?YF(VMS8AbX95TBMNV6*oTO~Gub%_+&N=f`6Et8ajag%0B zjD&7=%fgarEbgS6#AfE+bNn!KZf>SCne0qp>;o^{d*1h+``mx$J?Gr}o-1&mySv+4 zTU&dqva&KFgb45$;ZN;@<)-lApwwNlwAkx<@7;m1FbSm;07CmuoH+3`@N1y8OcYDP z#8ba5H-%y`20TCUJ@po35)ul9_5yzfwv>@rNtk%*cjcx~3KoIRShu<^jvqhnQ%a2h z_2p(&3dVQ#mz#e=shAOhHn(M2rznsNy}l`>>|+2SM9$-U*E`{iclMthqvO`TcwI90aSHS^48o&yR0h{yQLoy!<*z<`f$ z4e<3HUA#QngK$AjwU=W%BRp74aPe=whzby{qHq0#VObW1ER{+T3 zn5LPNqm-0vo(eCoe>2MOzZGSR<;7fpm!9cmz z9Od{IB8-klNhUz1O5@;`1%}qj150AD7y|YOQc6;(6oz5g z*P5nDDwRS?iDg+NlS%v787rkE9*^67U%YscqeqVtiA0FSV%)!fAHy(kxm@;)d6Z|- zqteEU?Y->ySThHI7G&yQF^tXd+Hfy#z7XX%-?>a{PXP1DbnNndI2Z0mS01uWD zRQP|uQXop;vaT6onkGsqd_EtIjg1^Ra)di~?l3bmLn@U*DP@bS4OSB3^ed(88#RrAq*`wzd|W9ciOv`^x8^psQjs+%zv)55(R z>gN+R0iHb*B=JE4!wYNyNogG1R)L|VMF4+jYHGsg^WpdVnVp@rh0-FMrnzd3cP3N& zoypPf1wBi(A7Fld9)RlVYP(%2h0Ep2`LVDOGvp(iYj{7AKvjUq2n_JRNTmI4FV@3! zTTPXht(&}VNRYr$-#pM@pwG!-@o4$Qwyz^H^6_s z*6GiV)$zJbU(?jo1iufCyCL_ttaU6|PEoa(e!m}&$77!l4Gq!R*@@5RLrO_kR~PYkoQa7ETWlSx z&ACK5+oY82*|UeUXU{S@ImzhgXqJEQ`FNK8Qvb;xBYdf`jaR$-@hC4=5>B*6_|cco z@QXkEl*Cd3+-V)=lL_|s1VO-+A4aKu=*3ztokPJT%Q=EWp%9@^h=qj(Mn*;$92~?j z42Fh=@caD?3=H7$c$l1=T4`Y2n6=8vuXI&dxG8 zI7nY#ABJJ9N?3MZD?HbHjCJAqJ5lz$)WVMs^zcsa`v6S6dy8+M`x;lGmk}ONm6_); zVBSdZ{r)b7Ux;!g66LGE3i83G1frsN4x*!@BY*YA%CeKa9BJtH0IXe+=c@K|6^G7t z-M6ko)~ZFucU~*}w1uERJp_OG0Mr7gOp=8Gq6&-}pc3Rfs3kCJL2L&3pLCn3PJal9 zs^TfQrIc|XZ*iyv)Iwx`au%wfvwgaXFBF@rI?uPo0vlKuRs!CPsHqGWce-8A4m=ru zB`8-q-lzgZ`dk&sMJeKLDPp4aRISwD^TY%GJkutYk&X% From 56b8a57efdaca086090c51aa9a398541b60f541e Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Tue, 8 Oct 2013 13:00:38 +0100 Subject: [PATCH 071/933] Renaming key / value columns in build_meta with meta_key / meta_value to avoid SQL errors. --- PHPCI/Model/Base/BuildMetaBase.php | 56 +++++++++++----------- PHPCI/Store/BuildStore.php | 6 +-- public/assets/js/build-plugins/loc.js | 2 +- public/assets/js/build-plugins/warnings.js | 4 +- 4 files changed, 34 insertions(+), 34 deletions(-) diff --git a/PHPCI/Model/Base/BuildMetaBase.php b/PHPCI/Model/Base/BuildMetaBase.php index 8fb62832..939c66fd 100644 --- a/PHPCI/Model/Base/BuildMetaBase.php +++ b/PHPCI/Model/Base/BuildMetaBase.php @@ -35,8 +35,8 @@ class BuildMetaBase extends Model 'id' => null, 'project_id' => null, 'build_id' => null, - 'key' => null, - 'value' => null, + 'meta_key' => null, + 'meta_value' => null, ); /** @@ -46,8 +46,8 @@ class BuildMetaBase extends Model 'id' => 'getId', 'project_id' => 'getProjectId', 'build_id' => 'getBuildId', - 'key' => 'getKey', - 'value' => 'getValue', + 'meta_key' => 'getMetaKey', + 'meta_value' => 'getMetaValue', 'Build' => 'getBuild', ); @@ -58,8 +58,8 @@ class BuildMetaBase extends Model 'id' => 'setId', 'project_id' => 'setProjectId', 'build_id' => 'setBuildId', - 'key' => 'setKey', - 'value' => 'setValue', + 'meta_key' => 'setMetaKey', + 'meta_value' => 'setMetaValue', 'Build' => 'setBuild', ); @@ -85,12 +85,12 @@ class BuildMetaBase extends Model 'nullable' => true, 'default' => null, ), - 'key' => array( + 'meta_key' => array( 'type' => 'varchar', 'length' => '255', 'default' => '', ), - 'value' => array( + 'meta_value' => array( 'type' => 'text', 'length' => '', 'nullable' => true, @@ -103,7 +103,7 @@ class BuildMetaBase extends Model */ public $indexes = array( 'PRIMARY' => array('unique' => true, 'columns' => 'id'), - 'idx_meta_id' => array('unique' => true, 'columns' => 'build_id, key'), + 'idx_meta_id' => array('unique' => true, 'columns' => 'build_id, meta_key'), ); /** @@ -160,26 +160,26 @@ class BuildMetaBase extends Model } /** - * Get the value of Key / key. + * Get the value of MetaKey / meta_key. * * @return string */ - public function getKey() + public function getMetaKey() { - $rtn = $this->data['key']; + $rtn = $this->data['meta_key']; return $rtn; } /** - * Get the value of Value / value. + * Get the value of MetaValue / meta_value. * * @return string */ - public function getValue() + public function getMetaValue() { - $rtn = $this->data['value']; + $rtn = $this->data['meta_value']; return $rtn; @@ -242,40 +242,40 @@ class BuildMetaBase extends Model } /** - * Set the value of Key / key. + * Set the value of MetaKey / meta_key. * * Must not be null. * @param $value string */ - public function setKey($value) + public function setMetaKey($value) { - $this->_validateNotNull('Key', $value); - $this->_validateString('Key', $value); - if ($this->data['key'] === $value) { + $this->_validateNotNull('MetaKey', $value); + $this->_validateString('MetaKey', $value); + if ($this->data['meta_key'] === $value) { return; } - $this->data['key'] = $value; + $this->data['meta_key'] = $value; - $this->_setModified('key'); + $this->_setModified('meta_key'); } /** - * Set the value of Value / value. + * Set the value of MetaValue / meta_value. * * @param $value string */ - public function setValue($value) + public function setMetaValue($value) { - $this->_validateString('Value', $value); - if ($this->data['value'] === $value) { + $this->_validateString('MetaValue', $value); + if ($this->data['meta_value'] === $value) { return; } - $this->data['value'] = $value; + $this->data['meta_value'] = $value; - $this->_setModified('value'); + $this->_setModified('meta_value'); } /** diff --git a/PHPCI/Store/BuildStore.php b/PHPCI/Store/BuildStore.php index 71329359..3b5059c7 100644 --- a/PHPCI/Store/BuildStore.php +++ b/PHPCI/Store/BuildStore.php @@ -51,7 +51,7 @@ class BuildStore extends BuildStoreBase public function getMeta($key, $projectId, $buildId = null, $numResults = 1) { $and = $numResults > 1 ? ' AND (`build_id` <= :buildId) ' : ' AND (`build_id` = :buildId) '; - $query = 'SELECT `build_id`, `key`, `value` FROM `build_meta` WHERE `key` = :key AND `project_id` = :projectId ' . $and . ' ORDER BY id DESC LIMIT :numResults'; + $query = 'SELECT `build_id`, `meta_key`, `meta_value` FROM `build_meta` WHERE `meta_key` = :key AND `project_id` = :projectId ' . $and . ' ORDER BY id DESC LIMIT :numResults'; $stmt = \b8\Database::getConnection('read')->prepare($query); $stmt->bindValue(':key', $key, \PDO::PARAM_STR); @@ -64,7 +64,7 @@ class BuildStore extends BuildStoreBase $rtn = array_reverse($rtn); $rtn = array_map(function($item) { - $item['value'] = json_decode($item['value'], true); + $item['meta_value'] = json_decode($item['meta_value'], true); return $item; }, $rtn); @@ -81,7 +81,7 @@ class BuildStore extends BuildStoreBase public function setMeta($projectId, $buildId, $key, $value) { - $query = 'REPLACE INTO build_meta (project_id, build_id, `key`, `value`) VALUES (:projectId, :buildId, :key, :value)'; + $query = 'REPLACE INTO build_meta (project_id, build_id, `meta_key`, `meta_value`) VALUES (:projectId, :buildId, :key, :value)'; $stmt = \b8\Database::getConnection('read')->prepare($query); $stmt->bindValue(':key', $key, \PDO::PARAM_STR); diff --git a/public/assets/js/build-plugins/loc.js b/public/assets/js/build-plugins/loc.js index 70808f35..aa75c073 100644 --- a/public/assets/js/build-plugins/loc.js +++ b/public/assets/js/build-plugins/loc.js @@ -46,7 +46,7 @@ var locPlugin = PHPCI.UiPlugin.extend({ var data = [["Build", "Lines", "Comment Lines", "Non-Comment Lines", "Logical Lines"]]; for (var idx in build) { - data.push(['Build ' + build[idx].build_id, parseInt(build[idx].value.LOC), parseInt(build[idx].value.CLOC), parseInt(build[idx].value.NCLOC), parseInt(build[idx].value.LLOC)]); + data.push(['Build ' + build[idx].build_id, parseInt(build[idx].meta_value.LOC), parseInt(build[idx].meta_value.CLOC), parseInt(build[idx].meta_value.NCLOC), parseInt(build[idx].meta_value.LLOC)]); } var data = google.visualization.arrayToDataTable(data); diff --git a/public/assets/js/build-plugins/warnings.js b/public/assets/js/build-plugins/warnings.js index 0912e121..b77d7bfb 100644 --- a/public/assets/js/build-plugins/warnings.js +++ b/public/assets/js/build-plugins/warnings.js @@ -42,8 +42,8 @@ var plugin = PHPCI.UiPlugin.extend({ for (var i in build) { var buildId = build[i]['build_id']; - var metaKey = build[i]['key']; - var metaVal = build[i]['value']; + var metaKey = build[i]['meta_key']; + var metaVal = build[i]['meta_value']; if (!self.data[buildId]) { self.data[buildId] = {}; From edc4fc1b1bd2fd7f4c3f5de5e15a58de61c5a4d2 Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Tue, 8 Oct 2013 13:52:05 +0100 Subject: [PATCH 072/933] Fixes --- PHPCI/Controller/HomeController.php | 19 +++- PHPCI/Store/BuildStore.php | 20 ++++ PHPCI/View/SummaryTable.phtml | 163 +++++++++++++--------------- 3 files changed, 110 insertions(+), 92 deletions(-) diff --git a/PHPCI/Controller/HomeController.php b/PHPCI/Controller/HomeController.php index da9babf0..5c519c47 100644 --- a/PHPCI/Controller/HomeController.php +++ b/PHPCI/Controller/HomeController.php @@ -19,6 +19,16 @@ use b8; */ class HomeController extends \PHPCI\Controller { + /** + * @var \b8\Store\BuildStore + */ + protected $_buildStore; + + /** + * @var \b8\Store\ProjectStore + */ + protected $_projectStore; + public function init() { $this->_buildStore = b8\Store\Factory::getStore('Build'); @@ -31,10 +41,15 @@ class HomeController extends \PHPCI\Controller public function index() { $projects = $this->_projectStore->getWhere(array(), 50, 0, array(), array('title' => 'ASC')); - $summary = $this->_buildStore->getBuildSummary(); + + $summaryBuilds = array(); + foreach ($projects['items'] as $project) { + $summaryBuilds[$project->getId()] = $this->_buildStore->getLatestBuilds($project->getId()); + } $summaryView = new b8\View('SummaryTable'); - $summaryView->builds = $summary['items']; + $summaryView->projects = $projects['items']; + $summaryView->builds = $summaryBuilds; $this->view->builds = $this->getLatestBuildsHtml(); $this->view->projects = $projects['items']; diff --git a/PHPCI/Store/BuildStore.php b/PHPCI/Store/BuildStore.php index 3b5059c7..13bb849e 100644 --- a/PHPCI/Store/BuildStore.php +++ b/PHPCI/Store/BuildStore.php @@ -19,6 +19,26 @@ use PHPCI\Store\Base\BuildStoreBase; */ class BuildStore extends BuildStoreBase { + public function getLatestBuilds($projectId) + { + $query = 'SELECT * FROM build WHERE project_id = :pid ORDER BY id DESC LIMIT 5'; + $stmt = \b8\Database::getConnection('read')->prepare($query); + $stmt->bindValue(':pid', $projectId); + + if ($stmt->execute()) { + $res = $stmt->fetchAll(\PDO::FETCH_ASSOC); + + $map = function ($item) { + return new \PHPCI\Model\Build($item); + }; + $rtn = array_map($map, $res); + + return $rtn; + } else { + return array(); + } + } + public function getBuildSummary() { $query = 'SELECT COUNT(*) AS cnt FROM build b LEFT JOIN project p on p.id = b.project_id GROUP BY b.project_id ORDER BY p.title ASC, b.id DESC'; diff --git a/PHPCI/View/SummaryTable.phtml b/PHPCI/View/SummaryTable.phtml index d927e6af..3d31ac09 100644 --- a/PHPCI/View/SummaryTable.phtml +++ b/PHPCI/View/SummaryTable.phtml @@ -1,87 +1,80 @@ "; -// var_dump($builds); -// echo ""; -$maxbuildcount = 5; -$projects = array(); -$prevBuild = null; -$health = false; +foreach($projects as $project): + $statuses = array(); + $successes = 0; + $failures = 0; + $health = ''; + $subcls = ''; + $cls = ''; -foreach($builds as $build): + $success = null; + $failure = null; - if ($build->getStatus() < 2) { - continue; - } + if (count($builds[$project->getId()])) { - if ( is_null($prevBuild) || $build->getProjectId() !== $prevBuild->getProjectId() ) { - $health = false; - $projects[$build->getProjectId()]['count'] = 0; - $projects[$build->getProjectId()]['health'] = 0; - $projects[$build->getProjectId()]['successes'] = 0; - $projects[$build->getProjectId()]['failures'] = 0; - $projects[$build->getProjectId()]['lastbuildstatus'] = (int)$build->getStatus(); - } + // Use the latest build information to determine current status: + $latestBuild = $builds[$project->getId()][0]; - if ( - !is_null($prevBuild) && - $projects[$build->getProjectId()]['count'] >= $maxbuildcount && - $build->getProjectId() === $prevBuild->getProjectId() - ) { - $projects[$build->getProjectId()]['count']++; - continue; - } + switch ($latestBuild->getStatus()) { + case 0: + $cls = 'active'; + $status = 'Pending'; + break; - switch ((int)$build->getStatus()) { - case 2: - $projects[$build->getProjectId()]['health']++; - $projects[$build->getProjectId()]['successes']++; + case 1: + $cls = 'warning'; + $status = 'Running'; + break; - if ( empty($projects[$build->getProjectId()]['lastsuccess']) ) { - $projects[$build->getProjectId()]['lastsuccess'] = $build; + case 2: + $cls = 'success'; + $status = 'Success'; + break; + + case 3: + $cls = 'danger'; + $status = 'Failed'; + break; + } + + // Use the last 5 builds to determine project health: + $successes = 0; + $failures = 0; + + foreach ($builds[$project->getId()] as $build) { + switch ($build->getStatus()) { + case 0: + $statuses[] = 'pending'; + break; + case 1: + $statuses[] = 'running'; + break; + case 2: + $successes++; + $statuses[] = 'ok'; + $success = is_null($success) ? $build->getFinished()->format('Y-m-d H:i:s') : $success; + break; + case 3: + $failures++; + $statuses[] = 'failed'; + $failure = is_null($failure) ? $build->getFinished()->format('Y-m-d H:i:s') : $failure; + break; } - break; - case 3: - $projects[$build->getProjectId()]['health']--; - $projects[$build->getProjectId()]['failures']++; - - if ( empty($projects[$build->getProjectId()]['lastfailure']) ) { - $projects[$build->getProjectId()]['lastfailure'] = $build; - } - break; + } } - $projects[$build->getProjectId()]['count']++; - $projects[$build->getProjectId()]['projectname'] = $build->getProject()->getTitle(); - $prevBuild = $build; -endforeach; - -foreach($projects as $projectId => $project): - switch($project['lastbuildstatus']) - { - case 0: - $cls = 'active'; - $status = 'Pending'; - break; - - case 1: - $cls = 'danger'; - $status = 'Running'; - break; - - case 2: - $cls = 'success'; - $status = 'Success'; - break; - - case 3: - $cls = 'error'; - $status = 'Failed'; - break; + if ($failures == 0) { + $health = 'Good'; + $subcls = 'success'; + } elseif ($failures > $successes) { + $health = 'Bad'; + $subcls = 'danger'; + } else { + $health = 'Warning'; + $subcls = 'warning'; } - $health = ($project['health'] <= 0 ? 'Stormy': ($project['successes'] < $project['count']? 'Overcast': 'Sunny')); - $subcls = ($project['health'] <= 0 ? 'danger': ($project['successes'] < $project['count']? 'warning': 'success')); ?>
@@ -89,26 +82,16 @@ foreach($projects as $projectId => $project): getTitle() ?> - - getId() ?>'> - getStarted()->format("Y-m-d H:i:s") ?> - - + '; + } + ?> - - getId() ?>'> - getStarted()->format("Y-m-d H:i:s") ?> - - - /build now »build now »
+ + + + + + + + + $version): ?> + + + + + + + +
TitleVersion
+ + Remove » + +
+
+ +
+

Suggested Plugins

+ + + + + + + + + + + $version): ?> + + + + + + + + +
TitleDescription
+ + + +
+
+ +
+

Search Packagist for More Plugins

+ +
+ + + + +
+ + +
+ + + + + + +
Loading...
\ No newline at end of file diff --git a/public/assets/css/phpci.css b/public/assets/css/phpci.css index 6453f193..fce6ee20 100644 --- a/public/assets/css/phpci.css +++ b/public/assets/css/phpci.css @@ -127,4 +127,18 @@ h3 .ui-sortable-placeholder * { visibility: hidden; } -.ui-plugin { padding-top: 15px; } \ No newline at end of file +.ui-plugin { padding-top: 15px; } + + +#loading { + font-family: Roboto, Arial, Sans-Serif; + + background: #369; + color: #fff; + display: none; + position: fixed; + bottom: 20px; + font-size: 2em; + right: 20px; + padding: 15px 50px; +} \ No newline at end of file From 7d5760f35556cf818fbb5f2be0cc37743b5b7113 Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Tue, 8 Oct 2013 16:29:01 +0100 Subject: [PATCH 074/933] Stabilising composer dependencies. --- composer.json | 13 +- composer.lock | 555 ++++---------------------------------------------- 2 files changed, 43 insertions(+), 525 deletions(-) diff --git a/composer.json b/composer.json index fdd10392..9e3ff52a 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name" : "block8/phpci", "description" : "Simple continuous integration for PHP projects.", - "minimum-stability": "dev", + "minimum-stability": "stable", "type" : "library", "keywords" : ["php", "phpci", "ci", "continuous", "integration", "testing", "phpunit", "continuous integration", "jenkins", "travis"], "homepage" : "http://www.phptesting.org/", @@ -23,12 +23,11 @@ }, "require": { - "block8/b8framework" : "dev-master", - "ircmaxell/password-compat": "1.x", - "swiftmailer/swiftmailer" : "v5.0.0", - "symfony/yaml" : "2.2.x-dev", - "symfony/console" : "2.2.*", - "behat/behat": "*" + "block8/b8framework" : "1.*", + "ircmaxell/password-compat": "1.*", + "swiftmailer/swiftmailer" : "5.0.*", + "symfony/yaml" : "2.*", + "symfony/console" : "2.*" }, "suggest": { diff --git a/composer.lock b/composer.lock index 199c4836..4921df46 100644 --- a/composer.lock +++ b/composer.lock @@ -3,153 +3,25 @@ "This file locks the dependencies of your project to a known state", "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" ], - "hash": "750d19d503481138217299f81787cea9", + "hash": "04cca0ac809838a65555d04534cc95ae", "packages": [ - { - "name": "behat/behat", - "version": "3.0.x-dev", - "source": { - "type": "git", - "url": "https://github.com/Behat/Behat.git", - "reference": "c2015bdeaf8a091c97779b21603b524d16638ba1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Behat/Behat/zipball/c2015bdeaf8a091c97779b21603b524d16638ba1", - "reference": "c2015bdeaf8a091c97779b21603b524d16638ba1", - "shasum": "" - }, - "require": { - "behat/gherkin": "~3.0.3", - "php": ">=5.3.3", - "symfony/class-loader": "~2.1", - "symfony/config": "~2.1", - "symfony/console": "~2.1", - "symfony/dependency-injection": "~2.1", - "symfony/event-dispatcher": "~2.1", - "symfony/finder": "~2.1", - "symfony/translation": "~2.1", - "symfony/yaml": "~2.1" - }, - "require-dev": { - "phpunit/phpunit": "~3.7.24" - }, - "suggest": { - "behat/mink-extension": "for integration with Mink testing framework", - "behat/symfony2-extension": "for integration with Symfony2 web framework", - "behat/yii-extension": "for integration with Yii web framework" - }, - "bin": [ - "bin/behat" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-develop": "3.0-dev" - } - }, - "autoload": { - "psr-0": { - "Behat\\Behat": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - } - ], - "description": "Scenario-oriented BDD framework for PHP 5.3", - "homepage": "http://behat.org/", - "keywords": [ - "BDD", - "Behat", - "Symfony2" - ], - "time": "2013-10-01 07:12:51" - }, - { - "name": "behat/gherkin", - "version": "3.0.x-dev", - "source": { - "type": "git", - "url": "https://github.com/Behat/Gherkin.git", - "reference": "cbc0a34d195bec76eb2b5fb4fad8b9ee524727d4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Behat/Gherkin/zipball/cbc0a34d195bec76eb2b5fb4fad8b9ee524727d4", - "reference": "cbc0a34d195bec76eb2b5fb4fad8b9ee524727d4", - "shasum": "" - }, - "require": { - "php": ">=5.3.1", - "symfony/finder": "~2.0" - }, - "require-dev": { - "symfony/config": "~2.1", - "symfony/yaml": "~2.1" - }, - "suggest": { - "symfony/config": "If you want to use Config component to manage resources", - "symfony/yaml": "If you want to parse features, represented in YAML files" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-develop": "3.0-dev" - } - }, - "autoload": { - "psr-0": { - "Behat\\Gherkin": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - } - ], - "description": "Gherkin DSL parser for PHP 5.3", - "homepage": "http://behat.org/", - "keywords": [ - "BDD", - "Behat", - "Cucumber", - "DSL", - "gherkin", - "parser" - ], - "time": "2013-09-15 20:23:37" - }, { "name": "block8/b8framework", - "version": "dev-master", + "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/Block8/b8framework.git", - "reference": "85424e277758695fcd4275c0bb13611e55a296ff" + "reference": "0497ae34ba7ef828db23b35a1f75d4debfa7eed5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Block8/b8framework/zipball/85424e277758695fcd4275c0bb13611e55a296ff", - "reference": "85424e277758695fcd4275c0bb13611e55a296ff", + "url": "https://api.github.com/repos/Block8/b8framework/zipball/0497ae34ba7ef828db23b35a1f75d4debfa7eed5", + "reference": "0497ae34ba7ef828db23b35a1f75d4debfa7eed5", "shasum": "" }, "require": { "php": ">=5.3.0", - "symfony/yaml": "2.2.x-dev" + "symfony/yaml": "2.*" }, "type": "library", "autoload": { @@ -178,11 +50,11 @@ "mvc", "php" ], - "time": "2013-10-08 06:45:49" + "time": "2013-10-09 14:06:12" }, { "name": "ircmaxell/password-compat", - "version": "1.0.x-dev", + "version": "1.0.3", "source": { "type": "git", "url": "https://github.com/ircmaxell/password_compat.git", @@ -221,16 +93,16 @@ }, { "name": "swiftmailer/swiftmailer", - "version": "v5.0.0", + "version": "v5.0.2", "source": { "type": "git", "url": "https://github.com/swiftmailer/swiftmailer.git", - "reference": "e2915242824e32e28be3fc699c453c1d16fd6de1" + "reference": "f3917ecef35a4e4d98b303eb9fee463bc983f379" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/e2915242824e32e28be3fc699c453c1d16fd6de1", - "reference": "e2915242824e32e28be3fc699c453c1d16fd6de1", + "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/f3917ecef35a4e4d98b303eb9fee463bc983f379", + "reference": "f3917ecef35a4e4d98b303eb9fee463bc983f379", "shasum": "" }, "require": { @@ -239,7 +111,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-master": "5.1-dev" } }, "autoload": { @@ -266,128 +138,36 @@ "mail", "mailer" ], - "time": "2013-04-30 17:35:30" + "time": "2013-08-30 12:35:21" }, { - "name": "symfony/class-loader", - "version": "dev-master", - "target-dir": "Symfony/Component/ClassLoader", + "name": "symfony/console", + "version": "v2.3.5", + "target-dir": "Symfony/Component/Console", "source": { "type": "git", - "url": "https://github.com/symfony/ClassLoader.git", - "reference": "7bb58f990b5c2d8fa0b70d29585eddb52d360a4c" + "url": "https://github.com/symfony/Console.git", + "reference": "f880062d56edefb25b36f2defa65aafe65959dc7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/ClassLoader/zipball/7bb58f990b5c2d8fa0b70d29585eddb52d360a4c", - "reference": "7bb58f990b5c2d8fa0b70d29585eddb52d360a4c", + "url": "https://api.github.com/repos/symfony/Console/zipball/f880062d56edefb25b36f2defa65aafe65959dc7", + "reference": "f880062d56edefb25b36f2defa65aafe65959dc7", "shasum": "" }, "require": { "php": ">=5.3.3" }, "require-dev": { - "symfony/finder": "~2.0" + "symfony/event-dispatcher": "~2.1" + }, + "suggest": { + "symfony/event-dispatcher": "" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.4-dev" - } - }, - "autoload": { - "psr-0": { - "Symfony\\Component\\ClassLoader\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" - } - ], - "description": "Symfony ClassLoader Component", - "homepage": "http://symfony.com", - "time": "2013-09-19 09:47:34" - }, - { - "name": "symfony/config", - "version": "dev-master", - "target-dir": "Symfony/Component/Config", - "source": { - "type": "git", - "url": "https://github.com/symfony/Config.git", - "reference": "9a3c831697349cf6e9b5302b7beb0b97e6cdac41" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/Config/zipball/9a3c831697349cf6e9b5302b7beb0b97e6cdac41", - "reference": "9a3c831697349cf6e9b5302b7beb0b97e6cdac41", - "shasum": "" - }, - "require": { - "php": ">=5.3.3", - "symfony/filesystem": "~2.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.4-dev" - } - }, - "autoload": { - "psr-0": { - "Symfony\\Component\\Config\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" - } - ], - "description": "Symfony Config Component", - "homepage": "http://symfony.com", - "time": "2013-09-19 09:47:34" - }, - { - "name": "symfony/console", - "version": "2.2.x-dev", - "target-dir": "Symfony/Component/Console", - "source": { - "type": "git", - "url": "https://github.com/symfony/Console.git", - "reference": "7ec2f86df8a0164d1677368f43a4a548b80a1402" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/Console/zipball/7ec2f86df8a0164d1677368f43a4a548b80a1402", - "reference": "7ec2f86df8a0164d1677368f43a4a548b80a1402", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.2-dev" + "dev-master": "2.3-dev" } }, "autoload": { @@ -411,281 +191,21 @@ ], "description": "Symfony Console Component", "homepage": "http://symfony.com", - "time": "2013-09-25 05:58:50" - }, - { - "name": "symfony/dependency-injection", - "version": "dev-master", - "target-dir": "Symfony/Component/DependencyInjection", - "source": { - "type": "git", - "url": "https://github.com/symfony/DependencyInjection.git", - "reference": "48a1803399404ff780de635da7e9eb746c655c65" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/DependencyInjection/zipball/48a1803399404ff780de635da7e9eb746c655c65", - "reference": "48a1803399404ff780de635da7e9eb746c655c65", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "symfony/config": "~2.2", - "symfony/expression-language": "~2.4", - "symfony/yaml": "~2.0" - }, - "suggest": { - "symfony/config": "", - "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them", - "symfony/yaml": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.4-dev" - } - }, - "autoload": { - "psr-0": { - "Symfony\\Component\\DependencyInjection\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" - } - ], - "description": "Symfony DependencyInjection Component", - "homepage": "http://symfony.com", - "time": "2013-09-29 19:43:28" - }, - { - "name": "symfony/event-dispatcher", - "version": "dev-master", - "target-dir": "Symfony/Component/EventDispatcher", - "source": { - "type": "git", - "url": "https://github.com/symfony/EventDispatcher.git", - "reference": "9b4fe5757870682eb2251e283228a66d938265a8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/9b4fe5757870682eb2251e283228a66d938265a8", - "reference": "9b4fe5757870682eb2251e283228a66d938265a8", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "symfony/dependency-injection": "~2.0" - }, - "suggest": { - "symfony/dependency-injection": "", - "symfony/http-kernel": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.4-dev" - } - }, - "autoload": { - "psr-0": { - "Symfony\\Component\\EventDispatcher\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" - } - ], - "description": "Symfony EventDispatcher Component", - "homepage": "http://symfony.com", - "time": "2013-09-19 09:47:34" - }, - { - "name": "symfony/filesystem", - "version": "dev-master", - "target-dir": "Symfony/Component/Filesystem", - "source": { - "type": "git", - "url": "https://github.com/symfony/Filesystem.git", - "reference": "e558fd5d593ebe083dca199f52aed5374ab7b57a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/Filesystem/zipball/e558fd5d593ebe083dca199f52aed5374ab7b57a", - "reference": "e558fd5d593ebe083dca199f52aed5374ab7b57a", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.4-dev" - } - }, - "autoload": { - "psr-0": { - "Symfony\\Component\\Filesystem\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" - } - ], - "description": "Symfony Filesystem Component", - "homepage": "http://symfony.com", - "time": "2013-09-27 14:57:51" - }, - { - "name": "symfony/finder", - "version": "dev-master", - "target-dir": "Symfony/Component/Finder", - "source": { - "type": "git", - "url": "https://github.com/symfony/Finder.git", - "reference": "e2ce3164ab58b4d54612e630571f158035ee8603" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/Finder/zipball/e2ce3164ab58b4d54612e630571f158035ee8603", - "reference": "e2ce3164ab58b4d54612e630571f158035ee8603", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.4-dev" - } - }, - "autoload": { - "psr-0": { - "Symfony\\Component\\Finder\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" - } - ], - "description": "Symfony Finder Component", - "homepage": "http://symfony.com", - "time": "2013-09-19 09:47:34" - }, - { - "name": "symfony/translation", - "version": "dev-master", - "target-dir": "Symfony/Component/Translation", - "source": { - "type": "git", - "url": "https://github.com/symfony/Translation.git", - "reference": "d7e84f71f1856f75025618aca6cdaf074ab84220" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/Translation/zipball/d7e84f71f1856f75025618aca6cdaf074ab84220", - "reference": "d7e84f71f1856f75025618aca6cdaf074ab84220", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "symfony/config": "~2.0", - "symfony/yaml": "~2.2" - }, - "suggest": { - "symfony/config": "", - "symfony/yaml": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.4-dev" - } - }, - "autoload": { - "psr-0": { - "Symfony\\Component\\Translation\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" - } - ], - "description": "Symfony Translation Component", - "homepage": "http://symfony.com", - "time": "2013-09-27 13:28:11" + "time": "2013-09-25 06:04:15" }, { "name": "symfony/yaml", - "version": "2.2.x-dev", + "version": "v2.3.5", "target-dir": "Symfony/Component/Yaml", "source": { "type": "git", "url": "https://github.com/symfony/Yaml.git", - "reference": "bb697b6617b90c91b2a65d24ce32035afe5e3418" + "reference": "6bb881b948368482e1abf1a75c08bcf88a1c5fc3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Yaml/zipball/bb697b6617b90c91b2a65d24ce32035afe5e3418", - "reference": "bb697b6617b90c91b2a65d24ce32035afe5e3418", + "url": "https://api.github.com/repos/symfony/Yaml/zipball/6bb881b948368482e1abf1a75c08bcf88a1c5fc3", + "reference": "6bb881b948368482e1abf1a75c08bcf88a1c5fc3", "shasum": "" }, "require": { @@ -694,7 +214,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.2-dev" + "dev-master": "2.3-dev" } }, "autoload": { @@ -718,7 +238,7 @@ ], "description": "Symfony Yaml Component", "homepage": "http://symfony.com", - "time": "2013-09-22 17:30:19" + "time": "2013-09-22 18:04:39" } ], "packages-dev": [ @@ -727,11 +247,10 @@ "aliases": [ ], - "minimum-stability": "dev", - "stability-flags": { - "block8/b8framework": 20, - "symfony/yaml": 20 - }, + "minimum-stability": "stable", + "stability-flags": [ + + ], "platform": [ ], From 0d0ecb9901fd94c30119a9daf3f7066522139666 Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Tue, 8 Oct 2013 16:29:23 +0100 Subject: [PATCH 075/933] Updating phpci.yml to run phploc --- phpci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/phpci.yml b/phpci.yml index d45cad8a..1a2a658d 100644 --- a/phpci.yml +++ b/phpci.yml @@ -16,3 +16,4 @@ test: allow_failures: true php_code_sniffer: standard: "PSR2" + php_loc: From 357a95cb6128f100edc0dea173a0d9b023f4be09 Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Tue, 8 Oct 2013 16:38:05 +0100 Subject: [PATCH 076/933] Updating phpci.yml to allow some fails. --- phpci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/phpci.yml b/phpci.yml index 1a2a658d..ce9170f0 100644 --- a/phpci.yml +++ b/phpci.yml @@ -17,3 +17,4 @@ test: php_code_sniffer: standard: "PSR2" php_loc: + allow_failures: true From e38d9b646a65c5aa3a0b664b89cf26fbb9996aef Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Tue, 8 Oct 2013 17:19:44 +0100 Subject: [PATCH 077/933] Adding IRC plugin --- PHPCI/Builder.php | 18 ++++++++------ PHPCI/Plugin/Irc.php | 58 ++++++++++++++++++++++++++++++++++++++++++++ phpci.yml | 13 ++++++++++ 3 files changed, 82 insertions(+), 7 deletions(-) create mode 100644 PHPCI/Plugin/Irc.php diff --git a/PHPCI/Builder.php b/PHPCI/Builder.php index e21d1dbb..0667a551 100644 --- a/PHPCI/Builder.php +++ b/PHPCI/Builder.php @@ -186,13 +186,15 @@ class Builder // Run success or failure plugins: if ($this->success) { + $this->build->setStatus(2); + $this->executePlugins('success'); $this->logSuccess('BUILD SUCCESSFUL!'); - $this->build->setStatus(2); } else { + $this->build->setStatus(3); + $this->executePlugins('failure'); $this->logFailure('BUILD FAILED!'); - $this->build->setStatus(3); } $this->log(''); @@ -309,6 +311,7 @@ class Builder $trans_table = array(); foreach ($this->getInterpolationVars() as $key => $value) { $trans_table['%'.$key.'%'] = $value; + $trans_table['%PHPCI_'.$key.'%'] = $value; } return strtr($input, $trans_table); } @@ -321,11 +324,12 @@ class Builder { $this->interpolation_vars = array( 'PHPCI' => 1, - 'PHPCI_COMMIT' => $this->build->getCommitId(), - 'PHPCI_PROJECT' => $this->build->getProject()->getId(), - 'PHPCI_BUILD' => $this->build->getId(), - 'PHPCI_PROJECT_TITLE' => $this->build->getProject()->getTitle(), - 'PHPCI_BUILD_PATH' => $this->buildPath, + 'COMMIT' => $this->build->getCommitId(), + 'PROJECT' => $this->build->getProject()->getId(), + 'BUILD' => $this->build->getId(), + 'PROJECT_TITLE' => $this->build->getProject()->getTitle(), + 'BUILD_PATH' => $this->buildPath, + 'BUILD_URI' => PHPCI_URL . "build/view/" . $this->build->getId(), ); } diff --git a/PHPCI/Plugin/Irc.php b/PHPCI/Plugin/Irc.php new file mode 100644 index 00000000..8658b103 --- /dev/null +++ b/PHPCI/Plugin/Irc.php @@ -0,0 +1,58 @@ + + * @package PHPCI + * @subpackage Plugins + */ +class Irc implements \PHPCI\Plugin +{ + private $phpci; + private $message; + private $server; + private $port; + private $room; + private $nick; + + + public function __construct(\PHPCI\Builder $phpci, array $options = array()) + { + $this->phpci = $phpci; + $this->message = $options['message']; + + $buildSettings = $phpci->getConfig('build_settings'); + + + if (isset($buildSettings['irc'])) { + $irc = $buildSettings['irc']; + + $this->server = $irc['server']; + $this->port = $irc['port']; + $this->room = $irc['room']; + $this->nick = $irc['nick']; + } + } + + public function execute() + { + $msg = $this->phpci->interpolate($this->message); + + if (empty($this->server) || empty($this->room) || empty($this->nick)) { + $this->phpci->logFailure('You must configure a server, room and nick.'); + } + + if (empty($this->port)) { + $this->port = 6667; + } + + $sock = fsockopen($this->server, $this->port); + fputs($sock, 'USER ' . $this->nick . ' phptesting.org ' . $this->nick . ' :' . $this->nick . "\r\n"); + fputs($sock, 'NICK ' . $this->nick . "\r\n"); + fputs($sock, 'PRIVMSG ' . $this->room . ' :' . $msg . "\r\n"); + fclose($sock); + + return true; + } +} diff --git a/phpci.yml b/phpci.yml index ce9170f0..a5ab495a 100644 --- a/phpci.yml +++ b/phpci.yml @@ -6,6 +6,11 @@ build_settings: - "build" - "Tests" - "composer.phar" + irc: + server: "irc.freenode.net" + port: 6667 + room: "#phpci" + nick: "phpcidev" setup: composer: @@ -18,3 +23,11 @@ test: standard: "PSR2" php_loc: allow_failures: true + +success: + irc: + message: "Build Success! %PROJECT_TITLE% - %COMMIT% - %BUILD_URI%" + +failure: + irc: + message: "Build Failed :( %PROJECT_TITLE% - %COMMIT% - %BUILD_URI%" \ No newline at end of file From e869e41f25e6dffdd9ef7f7cee863ecb701925bc Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Tue, 8 Oct 2013 17:21:51 +0100 Subject: [PATCH 078/933] Fixing IRC Plugin --- phpci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/phpci.yml b/phpci.yml index a5ab495a..90b34255 100644 --- a/phpci.yml +++ b/phpci.yml @@ -26,8 +26,8 @@ test: success: irc: - message: "Build Success! %PROJECT_TITLE% - %COMMIT% - %BUILD_URI%" + message: "Build Success. %PROJECT_TITLE% - %COMMIT% - %BUILD_URI%" failure: irc: - message: "Build Failed :( %PROJECT_TITLE% - %COMMIT% - %BUILD_URI%" \ No newline at end of file + message: "Build Failed. %PROJECT_TITLE% - %COMMIT% - %BUILD_URI%" \ No newline at end of file From cccf0b98698d00b5a381b7607bc1b45fc5559483 Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Tue, 8 Oct 2013 17:23:21 +0100 Subject: [PATCH 079/933] Testing IRC Plugin --- PHPCI/Plugin/Irc.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/PHPCI/Plugin/Irc.php b/PHPCI/Plugin/Irc.php index 8658b103..64e8f523 100644 --- a/PHPCI/Plugin/Irc.php +++ b/PHPCI/Plugin/Irc.php @@ -51,6 +51,11 @@ class Irc implements \PHPCI\Plugin fputs($sock, 'USER ' . $this->nick . ' phptesting.org ' . $this->nick . ' :' . $this->nick . "\r\n"); fputs($sock, 'NICK ' . $this->nick . "\r\n"); fputs($sock, 'PRIVMSG ' . $this->room . ' :' . $msg . "\r\n"); + + while ($res = fgets($sock)) { + $this->phpci->log($res); + } + fclose($sock); return true; From d060227fbd774140eabec398e2682276f7873a9a Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Tue, 8 Oct 2013 18:24:20 +0100 Subject: [PATCH 080/933] Better docblock type hinting for stores. --- PHPCI/Controller.php | 10 +++++++ PHPCI/Controller/BitbucketController.php | 17 ++++++++---- PHPCI/Controller/BuildController.php | 23 ++++++++++------ PHPCI/Controller/BuildStatusController.php | 13 ++++++--- PHPCI/Controller/GithubController.php | 9 ++++-- PHPCI/Controller/GitlabController.php | 9 ++++-- PHPCI/Controller/HomeController.php | 14 +++++----- PHPCI/Controller/ProjectController.php | 32 ++++++++++++++-------- PHPCI/Controller/SessionController.php | 9 ++++-- PHPCI/Controller/UserController.php | 19 ++++++++----- 10 files changed, 105 insertions(+), 50 deletions(-) diff --git a/PHPCI/Controller.php b/PHPCI/Controller.php index b90e1c4e..e344a613 100644 --- a/PHPCI/Controller.php +++ b/PHPCI/Controller.php @@ -9,6 +9,16 @@ use b8\View; class Controller extends \b8\Controller { + /** + * @var \b8\View + */ + protected $controllerView; + + /** + * @var \b8\View + */ + protected $view; + public function init() {} public function __construct(Config $config, Request $request, Response $response) diff --git a/PHPCI/Controller/BitbucketController.php b/PHPCI/Controller/BitbucketController.php index dff0e8e6..9ec4bb39 100644 --- a/PHPCI/Controller/BitbucketController.php +++ b/PHPCI/Controller/BitbucketController.php @@ -21,9 +21,14 @@ use PHPCI\Model\Build; */ class BitbucketController extends \PHPCI\Controller { + /** + * @var \PHPCI\Store\BuildStore + */ + protected $buildStore; + public function init() { - $this->_buildStore = Store\Factory::getStore('Build'); + $this->buildStore = Store\Factory::getStore('Build'); } /** @@ -31,9 +36,9 @@ class BitbucketController extends \PHPCI\Controller */ public function webhook($project) { - $payload = json_decode($this->getParam('payload'), true); - $branches = array(); - $commits = array(); + $payload = json_decode($this->getParam('payload'), true); + $branches = array(); + $commits = array(); foreach ($payload['commits'] as $commit) { if (!in_array($commit['branch'], $branches)) { @@ -45,14 +50,14 @@ class BitbucketController extends \PHPCI\Controller foreach ($branches as $branch) { try { - $build = new Build(); + $build = new Build(); $build->setProjectId($project); $build->setCommitId($commits[$branch]); $build->setStatus(0); $build->setLog(''); $build->setCreated(new \DateTime()); $build->setBranch($branch); - $this->_buildStore->save($build); + $this->buildStore->save($build); } catch (\Exception $ex) { } } diff --git a/PHPCI/Controller/BuildController.php b/PHPCI/Controller/BuildController.php index 0dc927b7..cd8007b1 100644 --- a/PHPCI/Controller/BuildController.php +++ b/PHPCI/Controller/BuildController.php @@ -20,9 +20,14 @@ use PHPCI\Model\Build; */ class BuildController extends \PHPCI\Controller { + /** + * @var \PHPCI\Store\BuildStore + */ + protected $buildStore; + public function init() { - $this->_buildStore = b8\Store\Factory::getStore('Build'); + $this->buildStore = b8\Store\Factory::getStore('Build'); } /** @@ -30,7 +35,7 @@ class BuildController extends \PHPCI\Controller */ public function view($buildId) { - $build = $this->_buildStore->getById($buildId); + $build = $this->buildStore->getById($buildId); $this->view->plugins = $this->getUiPlugins(); $this->view->build = $build; $this->view->data = $this->getBuildData($buildId); @@ -66,13 +71,13 @@ class BuildController extends \PHPCI\Controller */ public function meta($buildId) { - $build = $this->_buildStore->getById($buildId); + $build = $this->buildStore->getById($buildId); $key = $this->getParam('key', null); $numBuilds = $this->getParam('num_builds', 1); $data = null; if ($key && $build) { - $data = $this->_buildStore->getMeta($key, $build->getProjectId(), $buildId, $numBuilds); + $data = $this->buildStore->getMeta($key, $build->getProjectId(), $buildId, $numBuilds); } die(json_encode($data)); @@ -83,7 +88,7 @@ class BuildController extends \PHPCI\Controller */ protected function getBuildData($buildId) { - $build = $this->_buildStore->getById($buildId); + $build = $this->buildStore->getById($buildId); $data = array(); $data['status'] = (int)$build->getStatus(); @@ -101,7 +106,7 @@ class BuildController extends \PHPCI\Controller */ public function rebuild($buildId) { - $copy = $this->_buildStore->getById($buildId); + $copy = $this->buildStore->getById($buildId); $build = new Build(); $build->setProjectId($copy->getProjectId()); @@ -110,7 +115,7 @@ class BuildController extends \PHPCI\Controller $build->setBranch($copy->getBranch()); $build->setCreated(new \DateTime()); - $build = $this->_buildStore->save($build); + $build = $this->buildStore->save($build); header('Location: '.PHPCI_URL.'build/view/' . $build->getId()); exit; @@ -125,8 +130,8 @@ class BuildController extends \PHPCI\Controller throw new \Exception('You do not have permission to do that.'); } - $build = $this->_buildStore->getById($buildId); - $this->_buildStore->delete($build); + $build = $this->buildStore->getById($buildId); + $this->buildStore->delete($build); header('Location: '.PHPCI_URL.'project/view/' . $build->getProjectId()); exit; diff --git a/PHPCI/Controller/BuildStatusController.php b/PHPCI/Controller/BuildStatusController.php index 80c3e03a..bc5c188d 100644 --- a/PHPCI/Controller/BuildStatusController.php +++ b/PHPCI/Controller/BuildStatusController.php @@ -22,9 +22,14 @@ use PHPCI\Model\Build; */ class BuildStatusController extends \PHPCI\Controller { + /** + * @var \PHPCI\Store\ProjectStore + */ + protected $projectStore; + public function init() { - $this->_projectStore = Store\Factory::getStore('Project'); + $this->projectStore = Store\Factory::getStore('Project'); } /** @@ -32,9 +37,9 @@ class BuildStatusController extends \PHPCI\Controller */ public function image($projectId) { - $branch = $this->getParam('branch', 'master'); - $project = $this->_projectStore->getById($projectId); - $status = 'ok'; + $branch = $this->getParam('branch', 'master'); + $project = $this->projectStore->getById($projectId); + $status = 'ok'; if (isset($project) && $project instanceof Project) { $build = $project->getLatestBuild($branch, array(2,3)); diff --git a/PHPCI/Controller/GithubController.php b/PHPCI/Controller/GithubController.php index 71ae0e3b..d59333b9 100644 --- a/PHPCI/Controller/GithubController.php +++ b/PHPCI/Controller/GithubController.php @@ -21,9 +21,14 @@ use PHPCI\Model\Build; */ class GithubController extends \PHPCI\Controller { + /** + * @var \PHPCI\Store\BuildStore + */ + protected $buildStore; + public function init() { - $this->_buildStore = Store\Factory::getStore('Build'); + $this->buildStore = Store\Factory::getStore('Build'); } /** @@ -53,7 +58,7 @@ class GithubController extends \PHPCI\Controller } try { - $build = $this->_buildStore->save($build); + $build = $this->buildStore->save($build); $build->sendStatusPostback(); } catch (\Exception $ex) { header('HTTP/1.1 500 Internal Server Error'); diff --git a/PHPCI/Controller/GitlabController.php b/PHPCI/Controller/GitlabController.php index f3c1dd35..020b6346 100644 --- a/PHPCI/Controller/GitlabController.php +++ b/PHPCI/Controller/GitlabController.php @@ -21,9 +21,14 @@ use PHPCI\Model\Build; */ class GitlabController extends \PHPCI\Controller { + /** + * @var \PHPCI\Store\BuildStore + */ + protected $buildStore; + public function init() { - $this->_buildStore = Store\Factory::getStore('Build'); + $this->buildStore = Store\Factory::getStore('Build'); } /** @@ -48,7 +53,7 @@ class GitlabController extends \PHPCI\Controller } try { - $build = $this->_buildStore->save($build); + $build = $this->buildStore->save($build); $build->sendStatusPostback(); } catch (\Exception $ex) { header('HTTP/1.1 500 Internal Server Error'); diff --git a/PHPCI/Controller/HomeController.php b/PHPCI/Controller/HomeController.php index 5c519c47..586f46f1 100644 --- a/PHPCI/Controller/HomeController.php +++ b/PHPCI/Controller/HomeController.php @@ -22,17 +22,17 @@ class HomeController extends \PHPCI\Controller /** * @var \b8\Store\BuildStore */ - protected $_buildStore; + protected $buildStore; /** * @var \b8\Store\ProjectStore */ - protected $_projectStore; + protected $projectStore; public function init() { - $this->_buildStore = b8\Store\Factory::getStore('Build'); - $this->_projectStore = b8\Store\Factory::getStore('Project'); + $this->buildStore = b8\Store\Factory::getStore('Build'); + $this->projectStore = b8\Store\Factory::getStore('Project'); } /** @@ -40,11 +40,11 @@ class HomeController extends \PHPCI\Controller */ public function index() { - $projects = $this->_projectStore->getWhere(array(), 50, 0, array(), array('title' => 'ASC')); + $projects = $this->projectStore->getWhere(array(), 50, 0, array(), array('title' => 'ASC')); $summaryBuilds = array(); foreach ($projects['items'] as $project) { - $summaryBuilds[$project->getId()] = $this->_buildStore->getLatestBuilds($project->getId()); + $summaryBuilds[$project->getId()] = $this->buildStore->getLatestBuilds($project->getId()); } $summaryView = new b8\View('SummaryTable'); @@ -71,7 +71,7 @@ class HomeController extends \PHPCI\Controller */ protected function getLatestBuildsHtml() { - $builds = $this->_buildStore->getWhere(array(), 5, 0, array(), array('id' => 'DESC')); + $builds = $this->buildStore->getWhere(array(), 5, 0, array(), array('id' => 'DESC')); $view = new b8\View('BuildsTable'); $view->builds = $builds['items']; diff --git a/PHPCI/Controller/ProjectController.php b/PHPCI/Controller/ProjectController.php index e1b3e613..2d61b882 100644 --- a/PHPCI/Controller/ProjectController.php +++ b/PHPCI/Controller/ProjectController.php @@ -24,10 +24,20 @@ use b8\Form; */ class ProjectController extends \PHPCI\Controller { + /** + * @var \PHPCI\Store\BuildStore + */ + protected $buildStore; + + /** + * @var \PHPCI\Store\ProjectStore + */ + protected $projectStore; + public function init() { - $this->_buildStore = Store\Factory::getStore('Build'); - $this->_projectStore = Store\Factory::getStore('Project'); + $this->buildStore = Store\Factory::getStore('Build'); + $this->projectStore = Store\Factory::getStore('Project'); } /** @@ -35,7 +45,7 @@ class ProjectController extends \PHPCI\Controller */ public function view($projectId) { - $project = $this->_projectStore->getById($projectId); + $project = $this->projectStore->getById($projectId); $page = $this->getParam('p', 1); $builds = $this->getLatestBuildsHtml($projectId, (($page - 1) * 10)); @@ -53,7 +63,7 @@ class ProjectController extends \PHPCI\Controller public function build($projectId) { /* @var \PHPCI\Model\Project $project */ - $project = $this->_projectStore->getById($projectId); + $project = $this->projectStore->getById($projectId); $build = new Build(); $build->setProjectId($projectId); @@ -62,7 +72,7 @@ class ProjectController extends \PHPCI\Controller $build->setBranch($project->getType() === 'hg' ? 'default' : 'master'); $build->setCreated(new \DateTime()); - $build = $this->_buildStore->save($build); + $build = $this->buildStore->save($build); header('Location: '.PHPCI_URL.'build/view/' . $build->getId()); exit; @@ -77,8 +87,8 @@ class ProjectController extends \PHPCI\Controller throw new \Exception('You do not have permission to do that.'); } - $project = $this->_projectStore->getById($projectId); - $this->_projectStore->delete($project); + $project = $this->projectStore->getById($projectId); + $this->projectStore->delete($project); header('Location: '.PHPCI_URL); exit; @@ -100,7 +110,7 @@ class ProjectController extends \PHPCI\Controller { $criteria = array('project_id' => $projectId); $order = array('id' => 'DESC'); - $builds = $this->_buildStore->getWhere($criteria, 10, $start, array(), $order); + $builds = $this->buildStore->getWhere($criteria, 10, $start, array(), $order); $view = new b8\View('BuildsTable'); $view->builds = $builds['items']; @@ -176,7 +186,7 @@ class ProjectController extends \PHPCI\Controller $project = new Project(); $project->setValues($values); - $project = $this->_projectStore->save($project); + $project = $this->projectStore->save($project); header('Location: '.PHPCI_URL.'project/view/' . $project->getId()); die; @@ -219,7 +229,7 @@ class ProjectController extends \PHPCI\Controller } $method = $this->request->getMethod(); - $project = $this->_projectStore->getById($projectId); + $project = $this->projectStore->getById($projectId); if ($method == 'POST') { $values = $this->getParams(); @@ -258,7 +268,7 @@ class ProjectController extends \PHPCI\Controller } $project->setValues($values); - $project = $this->_projectStore->save($project); + $project = $this->projectStore->save($project); header('Location: '.PHPCI_URL.'project/view/' . $project->getId()); die; diff --git a/PHPCI/Controller/SessionController.php b/PHPCI/Controller/SessionController.php index 0c463132..15563b19 100644 --- a/PHPCI/Controller/SessionController.php +++ b/PHPCI/Controller/SessionController.php @@ -19,10 +19,15 @@ use b8; */ class SessionController extends \PHPCI\Controller { + /** + * @var \PHPCI\Store\UserStore + */ + protected $userStore; + public function init() { $this->response->disableLayout(); - $this->_userStore = b8\Store\Factory::getStore('User'); + $this->userStore = b8\Store\Factory::getStore('User'); } /** @@ -33,7 +38,7 @@ class SessionController extends \PHPCI\Controller $isLoginFailure = false; if ($this->request->getMethod() == 'POST') { - $user = $this->_userStore->getByEmail($this->getParam('email')); + $user = $this->userStore->getByEmail($this->getParam('email')); if ($user && password_verify($this->getParam('password', ''), $user->getHash())) { $_SESSION['user_id'] = $user->getId(); diff --git a/PHPCI/Controller/UserController.php b/PHPCI/Controller/UserController.php index c866fc52..e0cbd5e5 100644 --- a/PHPCI/Controller/UserController.php +++ b/PHPCI/Controller/UserController.php @@ -21,9 +21,14 @@ use b8\Form; */ class UserController extends \PHPCI\Controller { + /** + * @var \PHPCI\Store\UserStore + */ + protected $userStore; + public function init() { - $this->_userStore = b8\Store\Factory::getStore('User'); + $this->userStore = b8\Store\Factory::getStore('User'); } /** @@ -31,7 +36,7 @@ class UserController extends \PHPCI\Controller */ public function index() { - $users = $this->_userStore->getWhere(array(), 1000, 0, array(), array('email' => 'ASC')); + $users = $this->userStore->getWhere(array(), 1000, 0, array(), array('email' => 'ASC')); $this->view->users = $users; return $this->view->render(); @@ -72,7 +77,7 @@ class UserController extends \PHPCI\Controller $user = new User(); $user->setValues($values); - $user = $this->_userStore->save($user); + $user = $this->userStore->save($user); header('Location: '.PHPCI_URL.'user'); die; @@ -88,7 +93,7 @@ class UserController extends \PHPCI\Controller } $method = $this->request->getMethod(); - $user = $this->_userStore->getById($userId); + $user = $this->userStore->getById($userId); if ($method == 'POST') { $values = $this->getParams(); @@ -116,7 +121,7 @@ class UserController extends \PHPCI\Controller } $user->setValues($values); - $user = $this->_userStore->save($user); + $user = $this->userStore->save($user); header('Location: '.PHPCI_URL.'user'); die; @@ -178,8 +183,8 @@ class UserController extends \PHPCI\Controller throw new \Exception('You do not have permission to do that.'); } - $user = $this->_userStore->getById($userId); - $this->_userStore->delete($user); + $user = $this->userStore->getById($userId); + $this->userStore->delete($user); header('Location: '.PHPCI_URL.'user'); die; From 4cbfc06022f840c1064c380497cddd2877950c72 Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Wed, 9 Oct 2013 17:21:33 +0100 Subject: [PATCH 081/933] Adding a more standard PHP Lint plugin. Fixes #111 --- PHPCI/Plugin/Lint.php | 104 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 PHPCI/Plugin/Lint.php diff --git a/PHPCI/Plugin/Lint.php b/PHPCI/Plugin/Lint.php new file mode 100644 index 00000000..c8c1b880 --- /dev/null +++ b/PHPCI/Plugin/Lint.php @@ -0,0 +1,104 @@ + + * @package PHPCI + * @subpackage Plugins + */ +class Lint implements \PHPCI\Plugin +{ + protected $directories; + protected $recursive = true; + protected $ignore; + protected $phpci; + + public function __construct(Builder $phpci, Build $build, array $options = array()) + { + $this->phpci = $phpci; + $this->directories = array(''); + $this->ignore = $phpci->ignore; + + if (!empty($options['directory'])) { + $this->directories[] = $options['directory']; + } + + if (!empty($options['directories'])) { + $this->directories = $options['directories']; + } + + if (array_key_exists('recursive', $options)) { + $this->recursive = $options['recursive']; + } + } + + /** + * Executes parallel lint + */ + public function execute() + { + $this->phpci->quiet = true; + $success = true; + + $php = $this->phpci->findBinary('php'); + + foreach ($this->directories as $dir) { + if (!$this->lintDirectory($php, $dir)) { + $success = false; + } + } + + $this->phpci->quiet = false; + + return $success; + } + + protected function lintDirectory($php, $path) + { + $success = true; + $directory = new \DirectoryIterator($this->phpci->buildPath . $path); + + foreach ($directory as $item) { + if ($item->isDot()) { + continue; + } + + $itemPath = $path . $item->getFilename(); + + if (in_array($itemPath, $this->ignore)) { + continue; + } + + if ($item->isFile() && $item->getExtension() == 'php' && !$this->lintFile($php, $itemPath)) { + $success = false; + } else if ($item->isDir() && $this->recursive && !$this->lintDirectory($php, $itemPath . '/')) { + $success = false; + } + } + + return $success; + } + + protected function lintFile($php, $path) + { + $success = true; + + if (!$this->phpci->executeCommand($php . ' -l "%s"', $this->phpci->buildPath . $path)) { + $this->phpci->logFailure($path); + $success = false; + } + + return $success; + } +} From 77fae9a56db15681a37b5889f6996257a9c0ed1c Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Thu, 10 Oct 2013 01:01:06 +0100 Subject: [PATCH 082/933] Holy cleanup, batman. --- PHPCI/Application.php | 7 +- PHPCI/Builder.php | 286 ++++++++++-------------- PHPCI/Command/DaemonCommand.php | 8 +- PHPCI/Command/DaemoniseCommand.php | 2 +- PHPCI/Command/GenerateCommand.php | 4 +- PHPCI/Command/InstallCommand.php | 15 +- PHPCI/Command/RunCommand.php | 4 +- PHPCI/Controller.php | 73 +++--- PHPCI/Controller/BuildController.php | 8 +- PHPCI/Controller/PluginController.php | 8 +- PHPCI/Controller/ProjectController.php | 84 +++---- PHPCI/Controller/UserController.php | 5 +- PHPCI/Model/Base/BuildBase.php | 96 ++++---- PHPCI/Model/Base/BuildMetaBase.php | 54 ++--- PHPCI/Model/Base/ProjectBase.php | 71 +++--- PHPCI/Model/Base/UserBase.php | 54 ++--- PHPCI/Model/Build.php | 23 +- PHPCI/Model/Build/LocalBuild.php | 80 ++++--- PHPCI/Model/Build/MercurialBuild.php | 4 +- PHPCI/Model/Build/RemoteGitBuild.php | 12 +- PHPCI/Plugin.php | 5 +- PHPCI/Plugin/Atoum.php | 8 +- PHPCI/Plugin/Behat.php | 19 +- PHPCI/Plugin/Campfire.php | 58 +++-- PHPCI/Plugin/CleanBuild.php | 5 +- PHPCI/Plugin/Codeception.php | 7 +- PHPCI/Plugin/Composer.php | 8 +- PHPCI/Plugin/CopyBuild.php | 4 +- PHPCI/Plugin/Email.php | 55 ++--- PHPCI/Plugin/Env.php | 5 +- PHPCI/Plugin/Grunt.php | 36 ++- PHPCI/Plugin/Irc.php | 11 +- PHPCI/Plugin/Lint.php | 20 +- PHPCI/Plugin/Mysql.php | 46 ++-- PHPCI/Plugin/PackageBuild.php | 7 +- PHPCI/Plugin/Pdepend.php | 14 +- PHPCI/Plugin/Pgsql.php | 4 +- PHPCI/Plugin/PhpCodeSniffer.php | 119 ++++++---- PHPCI/Plugin/PhpCpd.php | 25 ++- PHPCI/Plugin/PhpCsFixer.php | 55 ++--- PHPCI/Plugin/PhpLoc.php | 10 +- PHPCI/Plugin/PhpMessDetector.php | 49 ++-- PHPCI/Plugin/PhpParallelLint.php | 5 +- PHPCI/Plugin/PhpSpec.php | 19 +- PHPCI/Plugin/PhpUnit.php | 5 +- PHPCI/Plugin/Shell.php | 5 +- PHPCI/Store/Base/BuildMetaStoreBase.php | 28 +-- PHPCI/Store/Base/BuildStoreBase.php | 45 ++-- PHPCI/Store/Base/ProjectStoreBase.php | 43 +++- PHPCI/Store/Base/UserStoreBase.php | 17 +- PHPCI/View/Home/index.phtml | 27 +-- bootstrap.php | 25 +-- console | 11 +- daemonise | 4 +- phpci.yml | 11 +- public/index.php | 1 - public/install.php | 91 +++----- vars.php | 27 +++ 58 files changed, 976 insertions(+), 856 deletions(-) create mode 100644 vars.php diff --git a/PHPCI/Application.php b/PHPCI/Application.php index 9bacd78d..0723c2ad 100644 --- a/PHPCI/Application.php +++ b/PHPCI/Application.php @@ -32,11 +32,14 @@ class Application extends b8\Application $externalAction = in_array($this->controllerName, array('Bitbucket', 'Github', 'Gitlab', 'BuildStatus')); $skipValidation = ($externalAction || $sessionAction); - if($skipValidation || $this->validateSession()) { + if ($skipValidation || $this->validateSession()) { parent::handleRequest(); } } catch (\Exception $ex) { - $content = '

There was a problem with this request

Please paste the details below into a new bug report so that we can investigate and fix it.

'; + $content = '

There was a problem with this request

+

Please paste the details below into a + new bug report + so that we can investigate and fix it.

'; ob_start(); var_dump(array( diff --git a/PHPCI/Builder.php b/PHPCI/Builder.php index 0667a551..42f98ee1 100644 --- a/PHPCI/Builder.php +++ b/PHPCI/Builder.php @@ -52,12 +52,7 @@ class Builder /** * @var bool */ - protected $verbose = false; - - /** - * @var bool[] - */ - protected $plugins = array(); + protected $verbose = true; /** * @var \PHPCI\Model\Build @@ -84,7 +79,6 @@ class Builder * interpolation and environment variables * @var array * @see setInterpolationVars() - * @see getInterpolationVars() */ protected $interpolation_vars = array(); @@ -93,19 +87,21 @@ class Builder */ protected $store; + /** + * @var bool + */ + public $quiet = false; + /** * Set up the builder. * @param \PHPCI\Model\Build * @param callable */ - public function __construct(Build $build, $logCallback = null) + public function __construct(Build $build, callable $logCallback) { $this->build = $build; $this->store = Store\Factory::getStore('Build'); - - if (!is_null($logCallback) && is_callable($logCallback)) { - $this->logCallback = $logCallback; - } + $this->logCallback = $logCallback; } /** @@ -123,7 +119,13 @@ class Builder */ public function getConfig($key) { - return isset($this->config[$key]) ? $this->config[$key] : null; + $rtn = null; + + if (isset($this->config[$key])) { + $rtn = $this->config[$key]; + } + + return $rtn; } /** @@ -136,29 +138,11 @@ class Builder return Config::getInstance()->get($key); } - /** - * Access the build. - * @param Build - */ - public function getBuild() - { - return $this->build; - } - /** * @return string The title of the project being built. */ public function getBuildProjectTitle() { - return $this->getBuild()->getProject()->getTitle(); - } - - /** - * Indicates if the build has passed or failed. - * @return bool - */ - public function getSuccessStatus() - { - return $this->success; + return $this->build->getProject()->getTitle(); } /** @@ -173,47 +157,41 @@ class Builder $this->build->sendStatusPostback(); try { - if ($this->setupBuild()) { - // Run setup steps: - $this->executePlugins('setup'); + // Set up the build: + $this->setupBuild(); - // Run the any tests: - $this->executePlugins('test'); + // Run the core plugin stages: + foreach (array('setup', 'test', 'complete') as $stage) { + $this->executePlugins($stage); $this->log(''); - - // Run build complete steps: - $this->executePlugins('complete'); - - // Run success or failure plugins: - if ($this->success) { - $this->build->setStatus(2); - - $this->executePlugins('success'); - $this->logSuccess('BUILD SUCCESSFUL!'); - } else { - $this->build->setStatus(3); - - $this->executePlugins('failure'); - $this->logFailure('BUILD FAILED!'); - } - - $this->log(''); - } else { - $this->build->setStatus(3); } + + // Failed build? Execute failure plugins and then mark the build as failed. + if (!$this->success) { + $this->executePlugins('failure'); + throw new \Exception('BUILD FAILED!'); + } + + // If we got this far, the build was successful! + if ($this->success) { + $this->build->setStatus(2); + $this->executePlugins('success'); + $this->logSuccess('BUILD SUCCESSFUL!'); + } + } catch (\Exception $ex) { $this->logFailure($ex->getMessage()); $this->build->setStatus(3); } // Clean up: - $this->removeBuild(); + $this->log('Removing build.'); + shell_exec(sprintf('rm -Rf "%s"', $this->buildPath)); // Update the build in the database, ping any external services, etc. $this->build->sendStatusPostback(); $this->build->setFinished(new \DateTime()); $this->build->setLog($this->log); - $this->build->setPlugins(json_encode($this->plugins)); $this->store->save($this->build); } @@ -223,8 +201,10 @@ class Builder public function executeCommand() { $command = call_user_func_array('sprintf', func_get_args()); - - $this->log('Executing: ' . $command, ' '); + + if (!$this->quiet) { + $this->log('Executing: ' . $command, ' '); + } $status = 0; exec($command, $this->lastOutput, $status); @@ -233,7 +213,14 @@ class Builder $this->log($this->lastOutput, ' '); } - return ($status == 0) ? true : false; + + $rtn = false; + + if ($status == 0) { + $rtn = true; + } + + return $rtn; } /** @@ -251,25 +238,16 @@ class Builder */ public function log($message, $prefix = '') { - if (is_array($message)) { - foreach ($message as $item) { - if (is_callable($this->logCallback)) { - call_user_func_array($this->logCallback, array($prefix . $item)); - } - - $this->log .= $prefix . $item . PHP_EOL; - } - } else { - $message = $prefix . $message; - $this->log .= $message . PHP_EOL; + if (!is_array($message)) { + $message = array($message); + } - if (isset($this->logCallback) && is_callable($this->logCallback)) { - call_user_func_array($this->logCallback, array($message)); - } + foreach ($message as $item) { + call_user_func_array($this->logCallback, array($prefix . $item)); + $this->log .= $prefix . $item . PHP_EOL; } $this->build->setLog($this->log); - $this->build->setPlugins(json_encode($this->plugins)); $this->store->save($this->build); } @@ -291,15 +269,6 @@ class Builder $this->log("\033[0;31m" . $message . "\033[0m"); } - /** - * Get an array key => value pairs that are used for interpolation - * @return array - */ - public function getInterpolationVars() - { - return $this->interpolation_vars; - } - /** * Replace every occurance of the interpolation vars in the given string * Example: "This is build %PHPCI_BUILD%" => "This is build 182" @@ -308,12 +277,9 @@ class Builder */ public function interpolate($input) { - $trans_table = array(); - foreach ($this->getInterpolationVars() as $key => $value) { - $trans_table['%'.$key.'%'] = $value; - $trans_table['%PHPCI_'.$key.'%'] = $value; - } - return strtr($input, $trans_table); + $keys = array_keys($this->interpolation_vars); + $values = array_values($this->interpolation_vars); + return str_replace($keys, $values, $input); } /** @@ -322,15 +288,28 @@ class Builder */ protected function setInterpolationVars() { - $this->interpolation_vars = array( - 'PHPCI' => 1, - 'COMMIT' => $this->build->getCommitId(), - 'PROJECT' => $this->build->getProject()->getId(), - 'BUILD' => $this->build->getId(), - 'PROJECT_TITLE' => $this->build->getProject()->getTitle(), - 'BUILD_PATH' => $this->buildPath, - 'BUILD_URI' => PHPCI_URL . "build/view/" . $this->build->getId(), - ); + $this->interpolation_vars = array(); + $this->interpolation_vars['%PHPCI%'] = 1; + $this->interpolation_vars['%COMMIT%'] = $this->build->getCommitId(); + $this->interpolation_vars['%PROJECT%'] = $this->build->getProjectId(); + $this->interpolation_vars['%BUILD%'] = $this->build->getId(); + $this->interpolation_vars['%PROJECT_TITLE%'] = $this->getBuildProjectTitle(); + $this->interpolation_vars['%BUILD_PATH%'] = $this->buildPath; + $this->interpolation_vars['%BUILD_URI%'] = PHPCI_URL . "build/view/" . $this->build->getId(); + $this->interpolation_vars['%PHPCI_COMMIT%'] = $this->interpolation_vars['%COMMIT%']; + $this->interpolation_vars['%PHPCI_PROJECT%'] = $this->interpolation_vars['%PROJECT%']; + $this->interpolation_vars['%PHPCI_BUILD%'] = $this->interpolation_vars['%BUILD%']; + $this->interpolation_vars['%PHPCI_PROJECT_TITLE%'] = $this->interpolation_vars['%PROJECT_TITLE%']; + $this->interpolation_vars['%PHPCI_BUILD_PATH%'] = $this->interpolation_vars['%BUILD_PATH%']; + $this->interpolation_vars['%PHPCI_BUILD_URI%'] = $this->interpolation_vars['%BUILD_URI%']; + + putenv('PHPCI=1'); + putenv('PHPCI_COMMIT='.$this->interpolation_vars['%COMMIT%']); + putenv('PHPCI_PROJECT='.$this->interpolation_vars['%PROJECT%']); + putenv('PHPCI_BUILD='.$this->interpolation_vars['%BUILD%']); + putenv('PHPCI_PROJECT_TITLE='.$this->interpolation_vars['%PROJECT_TITLE%']); + putenv('PHPCI_BUILD_PATH='.$this->interpolation_vars['%BUILD_PATH%']); + putenv('PHPCI_BUILD_URI='.$this->interpolation_vars['%BUILD_URI%']); } /** @@ -338,28 +317,20 @@ class Builder */ protected function setupBuild() { - $commitId = $this->build->getCommitId(); $buildId = 'project' . $this->build->getProject()->getId() . '-build' . $this->build->getId(); $this->ciDir = dirname(__FILE__) . '/../'; $this->buildPath = $this->ciDir . 'build/' . $buildId . '/'; $this->setInterpolationVars(); - // Setup environment vars that will be accessible during exec() - foreach ($this->getInterpolationVars() as $key => $value) { - putenv($key.'='.$value); - } - // Create a working copy of the project: if (!$this->build->createWorkingCopy($this, $this->buildPath)) { - return false; + throw new \Exception('Could not create a working copy.'); } // Does the project's phpci.yml request verbose mode? if (!isset($this->config['build_settings']['verbose']) || !$this->config['build_settings']['verbose']) { $this->verbose = false; - } else { - $this->verbose = true; } // Does the project have any paths it wants plugins to ignore? @@ -390,78 +361,55 @@ class Builder $options['allow_failures'] = false; } - $class = str_replace('_', ' ', $plugin); - $class = ucwords($class); - $class = 'PHPCI\\Plugin\\' . str_replace(' ', '', $class); + // Try and execute it: + if ($this->executePlugin($plugin, $options)) { - if (!class_exists($class)) { - $this->logFailure('Plugin does not exist: ' . $plugin); + // Execution was successful: + $this->logSuccess('PLUGIN STATUS: SUCCESS!'); - if ($stage == 'test') { - $this->plugins[$plugin] = false; + } else { - if (!$options['allow_failures']) { - $this->success = false; - } - } - - continue; - } - - try { - $obj = new $class($this, $options); - - if (!$obj->execute()) { - if ($stage == 'test') { - $this->plugins[$plugin] = false; - - if (!$options['allow_failures']) { - $this->success = false; - } - } - - $this->logFailure('PLUGIN STATUS: FAILED'); - continue; - } - } catch (\Exception $ex) { - $this->logFailure('EXCEPTION: ' . $ex->getMessage()); - - if ($stage == 'test') { - $this->plugins[$plugin] = false; - - if (!$options['allow_failures']) { - $this->success = false; - } + // If we're in the "test" stage and the plugin is not allowed to fail, + // then mark the build as failed: + if ($stage == 'test' && !$options['allow_failures']) { + $this->success = false; } $this->logFailure('PLUGIN STATUS: FAILED'); - continue; } - - if ($stage == 'test') { - $this->plugins[$plugin] = true; - } - - $this->logSuccess('PLUGIN STATUS: SUCCESS!'); } } /** - * Clean up our working copy. - */ - protected function removeBuild() - { - $this->log('Removing build.'); - shell_exec(sprintf('rm -Rf "%s"', $this->buildPath)); - } - - /** - * Store build meta data + * Executes a given plugin, with options and returns the result. */ - public function storeBuildMeta($key, $value) + protected function executePlugin($plugin, $options) { - $value = json_encode($value); - $this->store->setMeta($this->build->getProjectId(), $this->build->getId(), $key, $value); + // Figure out the class name and check the plugin exists: + $class = str_replace('_', ' ', $plugin); + $class = ucwords($class); + $class = 'PHPCI\\Plugin\\' . str_replace(' ', '', $class); + + if (!class_exists($class)) { + $this->logFailure('Plugin does not exist: ' . $plugin); + return false; + } + + $rtn = true; + + // Try running it: + try { + $obj = new $class($this, $this->build, $options); + + if (!$obj->execute()) { + $rtn = false; + } + } catch (\Exception $ex) { + $this->logFailure('EXCEPTION: ' . $ex->getMessage()); + $rtn = false; + } + + return $rtn; } /** diff --git a/PHPCI/Command/DaemonCommand.php b/PHPCI/Command/DaemonCommand.php index 94ecb464..d6458f2b 100644 --- a/PHPCI/Command/DaemonCommand.php +++ b/PHPCI/Command/DaemonCommand.php @@ -66,7 +66,7 @@ class DaemonCommand extends Command protected function startDaemon() { - if ( file_exists(PHPCI_DIR.'/daemon/daemon.pid') ) { + if (file_exists(PHPCI_DIR.'/daemon/daemon.pid')) { echo "Already started\n"; return "alreadystarted"; } @@ -80,7 +80,7 @@ class DaemonCommand extends Command protected function stopDaemon() { - if ( !file_exists(PHPCI_DIR.'/daemon/daemon.pid') ) { + if (!file_exists(PHPCI_DIR.'/daemon/daemon.pid')) { echo "Not started\n"; return "notstarted"; } @@ -94,14 +94,14 @@ class DaemonCommand extends Command protected function statusDaemon() { - if ( !file_exists(PHPCI_DIR.'/daemon/daemon.pid') ) { + if (!file_exists(PHPCI_DIR.'/daemon/daemon.pid')) { echo "Not running\n"; return "notrunning"; } $pid = trim(file_get_contents(PHPCI_DIR.'/daemon/daemon.pid')); $pidcheck = sprintf("/proc/%s", $pid); - if ( is_dir($pidcheck) ) { + if (is_dir($pidcheck)) { echo "Running\n"; return "running"; } diff --git a/PHPCI/Command/DaemoniseCommand.php b/PHPCI/Command/DaemoniseCommand.php index f46e6948..3c490d75 100644 --- a/PHPCI/Command/DaemoniseCommand.php +++ b/PHPCI/Command/DaemoniseCommand.php @@ -57,7 +57,7 @@ class DaemoniseCommand extends Command if (0 == $buildCount && $this->sleep < 15) { $this->sleep++; - } else if (1 < $this->sleep) { + } elseif (1 < $this->sleep) { $this->sleep--; } echo '.'.(0 === $buildCount?'':'build'); diff --git a/PHPCI/Command/GenerateCommand.php b/PHPCI/Command/GenerateCommand.php index 54a729ca..3a97f70c 100644 --- a/PHPCI/Command/GenerateCommand.php +++ b/PHPCI/Command/GenerateCommand.php @@ -14,6 +14,8 @@ use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; +use b8\Database; +use b8\Database\CodeGenerator; /** * Generate console command - Reads the database and generates models and stores. @@ -35,7 +37,7 @@ class GenerateCommand extends Command */ protected function execute(InputInterface $input, OutputInterface $output) { - $gen = new \b8\Database\CodeGenerator(\b8\Database::getConnection(), 'PHPCI', PHPCI_DIR . '/PHPCI/'); + $gen = new CodeGenerator(Database::getConnection(), 'PHPCI', PHPCI_DIR . '/PHPCI/', false); $gen->generateModels(); $gen->generateStores(); } diff --git a/PHPCI/Command/InstallCommand.php b/PHPCI/Command/InstallCommand.php index dea4e560..7f9daf6d 100644 --- a/PHPCI/Command/InstallCommand.php +++ b/PHPCI/Command/InstallCommand.php @@ -44,7 +44,8 @@ class InstallCommand extends Command $conf['b8']['database']['name'] = $this->ask('Enter the database name PHPCI should use: '); $conf['b8']['database']['username'] = $this->ask('Enter your MySQL username: '); $conf['b8']['database']['password'] = $this->ask('Enter your MySQL password: ', true); - $conf['phpci']['url'] = $this->ask('Your PHPCI URL (without trailing slash): ', false, array(FILTER_VALIDATE_URL,"/[^\/]$/i")); + $ask = 'Your PHPCI URL (without trailing slash): '; + $conf['phpci']['url'] = $this->ask($ask, false, array(FILTER_VALIDATE_URL,"/[^\/]$/i")); $conf['phpci']['github']['id'] = $this->ask('(Optional) Github Application ID: ', true); $conf['phpci']['github']['secret'] = $this->ask('(Optional) Github Application Secret: ', true); @@ -54,7 +55,9 @@ class InstallCommand extends Command $conf['phpci']['email_settings']['smtp_username'] = $this->ask('(Optional) Smtp Username: ', true); $conf['phpci']['email_settings']['smtp_password'] = $this->ask('(Optional) Smtp Password: ', true); $conf['phpci']['email_settings']['from_address'] = $this->ask('(Optional) Email address to send from: ', true); - $conf['phpci']['email_settings']['default_mailto_address'] = $this->ask('(Optional) Default address to email notifications to: ', true); + + $ask = '(Optional) Default address to email notifications to: '; + $conf['phpci']['email_settings']['default_mailto_address'] = $this->ask($ask, true); $dbUser = $conf['b8']['database']['username']; $dbPass = $conf['b8']['database']['password']; @@ -126,7 +129,7 @@ class InstallCommand extends Command return $rtn; } - protected function controlFormat($valueToInspect,$filter,&$statusMessage) + protected function controlFormat($valueToInspect, $filter, &$statusMessage) { $filters = !(is_array($filter))? array($filter) : $filter; $statusMessage = ''; @@ -148,13 +151,13 @@ class InstallCommand extends Command switch ($filter) { - case FILTER_VALIDATE_URL : + case FILTER_VALIDATE_URL: $statusMessage = 'Incorrect url format.' . PHP_EOL; break; - case FILTER_VALIDATE_EMAIL : + case FILTER_VALIDATE_EMAIL: $statusMessage = 'Incorrect e-mail format.' . PHP_EOL; break; - case FILTER_VALIDATE_REGEXP : + case FILTER_VALIDATE_REGEXP: $statusMessage = 'Incorrect format.' . PHP_EOL; break; } diff --git a/PHPCI/Command/RunCommand.php b/PHPCI/Command/RunCommand.php index abd30c6c..94d191d3 100644 --- a/PHPCI/Command/RunCommand.php +++ b/PHPCI/Command/RunCommand.php @@ -52,7 +52,9 @@ class RunCommand extends Command if ($input->getOption('verbose')) { $builder = new Builder($build, array($this, 'logCallback')); } else { - $builder = new Builder($build); + $builder = new Builder($build, function () { + // Empty stub function. + }); } $builder->execute(); diff --git a/PHPCI/Controller.php b/PHPCI/Controller.php index e344a613..d95c24e0 100644 --- a/PHPCI/Controller.php +++ b/PHPCI/Controller.php @@ -19,46 +19,49 @@ class Controller extends \b8\Controller */ protected $view; - public function init() {} + public function init() + { + // Extended by actual controllers. + } - public function __construct(Config $config, Request $request, Response $response) - { - parent::__construct($config, $request, $response); + public function __construct(Config $config, Request $request, Response $response) + { + parent::__construct($config, $request, $response); - $class = explode('\\', get_class($this)); - $this->className = substr(array_pop($class), 0, -10); - $this->setControllerView(); - } + $class = explode('\\', get_class($this)); + $this->className = substr(array_pop($class), 0, -10); + $this->setControllerView(); + } - protected function setControllerView() - { - if (View::exists($this->className)) { - $this->controllerView = new View($this->className); - } else { - $this->controllerView = new View\UserView('{@content}'); - } - } + protected function setControllerView() + { + if (View::exists($this->className)) { + $this->controllerView = new View($this->className); + } else { + $this->controllerView = new View\UserView('{@content}'); + } + } - protected function setView($action) - { - if (View::exists($this->className . '/' . $action)) { - $this->view = new View($this->className . '/' . $action); - } - } + protected function setView($action) + { + if (View::exists($this->className . '/' . $action)) { + $this->view = new View($this->className . '/' . $action); + } + } - public function handleAction($action, $actionParams) - { - $this->setView($action); - $response = parent::handleAction($action, $actionParams); + public function handleAction($action, $actionParams) + { + $this->setView($action); + $response = parent::handleAction($action, $actionParams); - if (is_string($response)) { - $this->controllerView->content = $response; - } elseif (isset($this->view)) { - $this->controllerView->content = $this->view->render(); - } + if (is_string($response)) { + $this->controllerView->content = $response; + } elseif (isset($this->view)) { + $this->controllerView->content = $this->view->render(); + } - $this->response->setContent($this->controllerView->render()); + $this->response->setContent($this->controllerView->render()); - return $this->response; - } -} \ No newline at end of file + return $this->response; + } +} diff --git a/PHPCI/Controller/BuildController.php b/PHPCI/Controller/BuildController.php index cd8007b1..8c183774 100644 --- a/PHPCI/Controller/BuildController.php +++ b/PHPCI/Controller/BuildController.php @@ -38,7 +38,7 @@ class BuildController extends \PHPCI\Controller $build = $this->buildStore->getById($buildId); $this->view->plugins = $this->getUiPlugins(); $this->view->build = $build; - $this->view->data = $this->getBuildData($buildId); + $this->view->data = $this->getBuildData($build); } protected function getUiPlugins() @@ -63,7 +63,7 @@ class BuildController extends \PHPCI\Controller */ public function data($buildId) { - die($this->getBuildData($buildId)); + die($this->getBuildData($this->buildStore->getById($buildId))); } /** @@ -86,10 +86,8 @@ class BuildController extends \PHPCI\Controller /** * Get build data from database and json encode it: */ - protected function getBuildData($buildId) + protected function getBuildData($build) { - $build = $this->buildStore->getById($buildId); - $data = array(); $data['status'] = (int)$build->getStatus(); $data['log'] = $this->cleanLog($build->getLog()); diff --git a/PHPCI/Controller/PluginController.php b/PHPCI/Controller/PluginController.php index 2d9d74fe..0dd3f2b4 100644 --- a/PHPCI/Controller/PluginController.php +++ b/PHPCI/Controller/PluginController.php @@ -68,7 +68,7 @@ class PluginController extends \PHPCI\Controller $this->setComposerJson($json); if ($this->canInstall) { - $res = shell_exec($this->composerPath . ' update --working-dir=' . APPLICATION_PATH . ' > /dev/null 2>&1 &'); + shell_exec($this->composerPath . ' update --working-dir=' . APPLICATION_PATH . ' > /dev/null 2>&1 &'); } header('Location: ' . PHPCI_URL . 'plugin?r=' . $package); @@ -89,7 +89,7 @@ class PluginController extends \PHPCI\Controller $this->setComposerJson($json); if ($this->canInstall) { - $res = shell_exec($this->composerPath . ' update --working-dir=' . APPLICATION_PATH . ' > /dev/null 2>&1 &'); + shell_exec($this->composerPath . ' update --working-dir=' . APPLICATION_PATH . ' > /dev/null 2>&1 &'); header('Location: ' . PHPCI_URL . 'plugin?i=' . $package); die; @@ -141,10 +141,10 @@ class PluginController extends \PHPCI\Controller public function packagistSearch() { - $q = $this->getParam('q', ''); + $searchQuery = $this->getParam('q', ''); $http = new \b8\HttpClient(); $http->setHeaders(array('User-Agent: PHPCI/1.0 (+http://www.phptesting.org)')); - $res = $http->get('https://packagist.org/search.json', array('q' => $q)); + $res = $http->get('https://packagist.org/search.json', array('q' => $searchQuery)); die(json_encode($res['body'])); } diff --git a/PHPCI/Controller/ProjectController.php b/PHPCI/Controller/ProjectController.php index 2d61b882..db684051 100644 --- a/PHPCI/Controller/ProjectController.php +++ b/PHPCI/Controller/ProjectController.php @@ -173,7 +173,7 @@ class ProjectController extends \PHPCI\Controller $values = $form->getValues(); if ($values['type'] == "gitlab") { - preg_match('`^(.*)@(.*):(.*)/(.*)\.git`',$values['reference'],$matches); + preg_match('`^(.*)@(.*):(.*)/(.*)\.git`', $values['reference'], $matches); $info = array(); $info["user"] = $matches[1]; $info["domain"] = $matches[2]; @@ -238,7 +238,8 @@ class ProjectController extends \PHPCI\Controller $values['key'] = $values['git_key']; if ($values['type'] == "gitlab") { $accessInfo = $project->getAccessInformation(); - $values['reference'] = $accessInfo["user"].'@'.$accessInfo["domain"].':' . $project->getReference().".git"; + $reference = $accessInfo["user"].'@'.$accessInfo["domain"].':' . $project->getReference().".git"; + $values['reference'] = $reference; } } @@ -259,12 +260,12 @@ class ProjectController extends \PHPCI\Controller $values['git_key'] = $values['key']; if ($values['type'] == "gitlab") { - preg_match('`^(.*)@(.*):(.*)/(.*)\.git`',$values['reference'],$matches); + preg_match('`^(.*)@(.*):(.*)/(.*)\.git`', $values['reference'], $matches); $info = array(); $info["user"] = $matches[1]; $info["domain"] = $matches[2]; $values['access_information'] = serialize($info); - $values['reference'] = $matches[3]."/".$matches[4]; + $values['reference'] = $matches[3] . "/" . $matches[4]; } $project->setValues($values); @@ -314,44 +315,9 @@ class ProjectController extends \PHPCI\Controller $form->addField($field); } - $referenceValidator = function ($val) use ($values) { - $type = $values['type']; - - switch($type) { - case 'hg': - if (!preg_match('/^(https?):\/\//', $val)) { - throw new \Exception('Mercurial repository URL must be start with http:// or https://.'); - } - break; - case 'remote': - if (!preg_match('/^(git|https?):\/\//', $val)) { - throw new \Exception('Repository URL must be start with git://, http:// or https://.'); - } - break; - case 'local': - if (!is_dir($val)) { - throw new \Exception('The path you specified does not exist.'); - } - break; - case 'gitlab': - if (!preg_match('`^(.*)@(.*):(.*)/(.*)\.git`', $val)) { - throw new \Exception('GitLab Repository name must be in the format "user@domain.tld:owner/repo.git".'); - } - break; - case 'github': - case 'bitbucket': - if (!preg_match('/^[a-zA-Z0-9_\-]+\/[a-zA-Z0-9_\-\.]+$/', $val)) { - throw new \Exception('Repository name must be in the format "owner/repo".'); - } - break; - } - - return true; - }; - $field = new Form\Element\Text('reference'); $field->setRequired(true); - $field->setValidator($referenceValidator); + $field->setValidator($this->getReferenceValidator($values)); $field->setLabel('Repository Name / URL (Remote) or Path (Local)'); $field->setClass('form-control'); $field->setContainerClass('form-group'); @@ -401,4 +367,42 @@ class ProjectController extends \PHPCI\Controller return $rtn; } + + protected function getReferenceValidator($values) + { + return function ($val) use ($values) { + $type = $values['type']; + + $validators = array( + 'hg' => array( + 'regex' => '/^(https?):\/\//', + 'message' => 'Mercurial repository URL must be start with http:// or https://' + ), + 'remote' => array( + 'regex' => '/^(git|https?):\/\//', + 'message' => 'Repository URL must be start with git://, http:// or https://' + ), + 'gitlab' => array( + 'regex' => '`^(.*)@(.*):(.*)/(.*)\.git`', + 'message' => 'GitLab Repository name must be in the format "user@domain.tld:owner/repo.git"' + ), + 'github' => array( + 'regex' => '/^[a-zA-Z0-9_\-]+\/[a-zA-Z0-9_\-\.]+$/', + 'message' => 'Repository name must be in the format "owner/repo"' + ), + 'bitbucket' => array( + 'regex' => '/^[a-zA-Z0-9_\-]+\/[a-zA-Z0-9_\-\.]+$/', + 'message' => 'Repository name must be in the format "owner/repo"' + ), + ); + + if (in_array($type, $validators) && !preg_match($validators[$type]['regex'], $val)) { + throw new \Exception($validators[$type]['message']); + } elseif ($type == 'local' && !is_dir($val)) { + throw new \Exception('The path you specified does not exist.'); + } + + return true; + }; + } } diff --git a/PHPCI/Controller/UserController.php b/PHPCI/Controller/UserController.php index e0cbd5e5..ad41aff6 100644 --- a/PHPCI/Controller/UserController.php +++ b/PHPCI/Controller/UserController.php @@ -10,8 +10,9 @@ namespace PHPCI\Controller; use b8; -use PHPCI\Model\User; use b8\Form; +use PHPCI\Controller; +use PHPCI\Model\User; /** * User Controller - Allows an administrator to view, add, edit and delete users. @@ -19,7 +20,7 @@ use b8\Form; * @package PHPCI * @subpackage Web */ -class UserController extends \PHPCI\Controller +class UserController extends Controller { /** * @var \PHPCI\Store\UserStore diff --git a/PHPCI/Model/Base/BuildBase.php b/PHPCI/Model/Base/BuildBase.php index e620452f..52a549d5 100644 --- a/PHPCI/Model/Base/BuildBase.php +++ b/PHPCI/Model/Base/BuildBase.php @@ -7,6 +7,7 @@ namespace PHPCI\Model\Base; use b8\Model; +use b8\Store\Factory; /** * Build Base Model @@ -43,12 +44,13 @@ class BuildBase extends Model 'finished' => null, 'plugins' => null, 'committer_email' => null, - ); + ); /** * @var array */ protected $getters = array( + // Direct property getters: 'id' => 'getId', 'project_id' => 'getProjectId', 'commit_id' => 'getCommitId', @@ -60,13 +62,16 @@ class BuildBase extends Model 'finished' => 'getFinished', 'plugins' => 'getPlugins', 'committer_email' => 'getCommitterEmail', + + // Foreign key getters: 'Project' => 'getProject', - ); + ); /** * @var array */ protected $setters = array( + // Direct property setters: 'id' => 'setId', 'project_id' => 'setProjectId', 'commit_id' => 'setCommitId', @@ -78,8 +83,10 @@ class BuildBase extends Model 'finished' => 'setFinished', 'plugins' => 'setPlugins', 'committer_email' => 'setCommitterEmail', + + // Foreign key setters: 'Project' => 'setProject', - ); + ); /** * @var array @@ -87,69 +94,63 @@ class BuildBase extends Model public $columns = array( 'id' => array( 'type' => 'int', - 'length' => '11', + 'length' => 11, 'primary_key' => true, 'auto_increment' => true, 'default' => null, - ), + ), 'project_id' => array( 'type' => 'int', - 'length' => '11', + 'length' => 11, 'default' => null, - ), + ), 'commit_id' => array( 'type' => 'varchar', - 'length' => '50', + 'length' => 50, 'nullable' => true, 'default' => null, - ), + ), 'status' => array( 'type' => 'tinyint', - 'length' => '4', - 'default' => '0', - ), + 'length' => 4, + ), 'log' => array( 'type' => 'longtext', - 'length' => '', 'nullable' => true, 'default' => null, - ), + ), 'branch' => array( 'type' => 'varchar', - 'length' => '50', + 'length' => 50, 'default' => 'master', - ), + ), 'created' => array( 'type' => 'datetime', - 'length' => '', 'nullable' => true, 'default' => null, - ), + ), 'started' => array( 'type' => 'datetime', - 'length' => '', 'nullable' => true, 'default' => null, - ), + ), 'finished' => array( 'type' => 'datetime', - 'length' => '', 'nullable' => true, 'default' => null, - ), + ), 'plugins' => array( 'type' => 'text', - 'length' => '', 'nullable' => true, 'default' => null, - ), + ), 'committer_email' => array( 'type' => 'varchar', - 'length' => '512', + 'length' => 512, 'nullable' => true, 'default' => null, - ), - ); + ), + ); /** * @var array @@ -158,7 +159,7 @@ class BuildBase extends Model 'PRIMARY' => array('unique' => true, 'columns' => 'id'), 'project_id' => array('columns' => 'project_id'), 'idx_status' => array('columns' => 'status'), - ); + ); /** * @var array @@ -171,8 +172,7 @@ class BuildBase extends Model 'table' => 'project', 'col' => 'id' ), - ); - + ); /** * Get the value of Id / id. @@ -183,7 +183,6 @@ class BuildBase extends Model { $rtn = $this->data['id']; - return $rtn; } @@ -196,7 +195,6 @@ class BuildBase extends Model { $rtn = $this->data['project_id']; - return $rtn; } @@ -209,7 +207,6 @@ class BuildBase extends Model { $rtn = $this->data['commit_id']; - return $rtn; } @@ -222,7 +219,6 @@ class BuildBase extends Model { $rtn = $this->data['status']; - return $rtn; } @@ -235,7 +231,6 @@ class BuildBase extends Model { $rtn = $this->data['log']; - return $rtn; } @@ -248,7 +243,6 @@ class BuildBase extends Model { $rtn = $this->data['branch']; - return $rtn; } @@ -261,11 +255,9 @@ class BuildBase extends Model { $rtn = $this->data['created']; - if (!empty($rtn)) { $rtn = new \DateTime($rtn); } - return $rtn; } @@ -279,11 +271,9 @@ class BuildBase extends Model { $rtn = $this->data['started']; - if (!empty($rtn)) { $rtn = new \DateTime($rtn); } - return $rtn; } @@ -297,11 +287,9 @@ class BuildBase extends Model { $rtn = $this->data['finished']; - if (!empty($rtn)) { $rtn = new \DateTime($rtn); } - return $rtn; } @@ -315,7 +303,6 @@ class BuildBase extends Model { $rtn = $this->data['plugins']; - return $rtn; } @@ -328,7 +315,6 @@ class BuildBase extends Model { $rtn = $this->data['committer_email']; - return $rtn; } @@ -342,6 +328,7 @@ class BuildBase extends Model { $this->_validateNotNull('Id', $value); $this->_validateInt('Id', $value); + if ($this->data['id'] === $value) { return; } @@ -361,6 +348,7 @@ class BuildBase extends Model { $this->_validateNotNull('ProjectId', $value); $this->_validateInt('ProjectId', $value); + if ($this->data['project_id'] === $value) { return; } @@ -377,8 +365,8 @@ class BuildBase extends Model */ public function setCommitId($value) { - $this->_validateString('CommitId', $value); + if ($this->data['commit_id'] === $value) { return; } @@ -398,6 +386,7 @@ class BuildBase extends Model { $this->_validateNotNull('Status', $value); $this->_validateInt('Status', $value); + if ($this->data['status'] === $value) { return; } @@ -414,8 +403,8 @@ class BuildBase extends Model */ public function setLog($value) { - $this->_validateString('Log', $value); + if ($this->data['log'] === $value) { return; } @@ -435,6 +424,7 @@ class BuildBase extends Model { $this->_validateNotNull('Branch', $value); $this->_validateString('Branch', $value); + if ($this->data['branch'] === $value) { return; } @@ -451,8 +441,8 @@ class BuildBase extends Model */ public function setCreated($value) { - $this->_validateDate('Created', $value); + if ($this->data['created'] === $value) { return; } @@ -469,8 +459,8 @@ class BuildBase extends Model */ public function setStarted($value) { - $this->_validateDate('Started', $value); + if ($this->data['started'] === $value) { return; } @@ -487,8 +477,8 @@ class BuildBase extends Model */ public function setFinished($value) { - $this->_validateDate('Finished', $value); + if ($this->data['finished'] === $value) { return; } @@ -505,8 +495,8 @@ class BuildBase extends Model */ public function setPlugins($value) { - $this->_validateString('Plugins', $value); + if ($this->data['plugins'] === $value) { return; } @@ -523,8 +513,8 @@ class BuildBase extends Model */ public function setCommitterEmail($value) { - $this->_validateString('CommitterEmail', $value); + if ($this->data['committer_email'] === $value) { return; } @@ -553,7 +543,7 @@ class BuildBase extends Model $rtn = $this->cache->get($cacheKey, null); if (empty($rtn)) { - $rtn = \b8\Store\Factory::getStore('Project')->getById($key); + $rtn = Factory::getStore('Project')->getById($key); $this->cache->set($cacheKey, $rtn); } @@ -600,6 +590,6 @@ class BuildBase extends Model */ public function getBuildBuildMetas() { - return \b8\Store\Factory::getStore('BuildMeta')->getByBuildId($this->getId()); + return Factory::getStore('BuildMeta')->getByBuildId($this->getId()); } } diff --git a/PHPCI/Model/Base/BuildMetaBase.php b/PHPCI/Model/Base/BuildMetaBase.php index 939c66fd..9ef0f457 100644 --- a/PHPCI/Model/Base/BuildMetaBase.php +++ b/PHPCI/Model/Base/BuildMetaBase.php @@ -7,6 +7,7 @@ namespace PHPCI\Model\Base; use b8\Model; +use b8\Store\Factory; /** * BuildMeta Base Model @@ -37,31 +38,37 @@ class BuildMetaBase extends Model 'build_id' => null, 'meta_key' => null, 'meta_value' => null, - ); + ); /** * @var array */ protected $getters = array( + // Direct property getters: 'id' => 'getId', 'project_id' => 'getProjectId', 'build_id' => 'getBuildId', 'meta_key' => 'getMetaKey', 'meta_value' => 'getMetaValue', + + // Foreign key getters: 'Build' => 'getBuild', - ); + ); /** * @var array */ protected $setters = array( + // Direct property setters: 'id' => 'setId', 'project_id' => 'setProjectId', 'build_id' => 'setBuildId', 'meta_key' => 'setMetaKey', 'meta_value' => 'setMetaValue', + + // Foreign key setters: 'Build' => 'setBuild', - ); + ); /** * @var array @@ -69,34 +76,32 @@ class BuildMetaBase extends Model public $columns = array( 'id' => array( 'type' => 'int', - 'length' => '10', + 'length' => 10, 'primary_key' => true, 'auto_increment' => true, 'default' => null, - ), + ), 'project_id' => array( 'type' => 'int', - 'length' => '11', + 'length' => 11, 'default' => null, - ), + ), 'build_id' => array( 'type' => 'int', - 'length' => '11', + 'length' => 11, 'nullable' => true, 'default' => null, - ), + ), 'meta_key' => array( 'type' => 'varchar', - 'length' => '255', - 'default' => '', - ), + 'length' => 255, + ), 'meta_value' => array( 'type' => 'text', - 'length' => '', 'nullable' => true, 'default' => null, - ), - ); + ), + ); /** * @var array @@ -104,7 +109,7 @@ class BuildMetaBase extends Model public $indexes = array( 'PRIMARY' => array('unique' => true, 'columns' => 'id'), 'idx_meta_id' => array('unique' => true, 'columns' => 'build_id, meta_key'), - ); + ); /** * @var array @@ -117,8 +122,7 @@ class BuildMetaBase extends Model 'table' => 'build', 'col' => 'id' ), - ); - + ); /** * Get the value of Id / id. @@ -129,7 +133,6 @@ class BuildMetaBase extends Model { $rtn = $this->data['id']; - return $rtn; } @@ -142,7 +145,6 @@ class BuildMetaBase extends Model { $rtn = $this->data['project_id']; - return $rtn; } @@ -155,7 +157,6 @@ class BuildMetaBase extends Model { $rtn = $this->data['build_id']; - return $rtn; } @@ -168,7 +169,6 @@ class BuildMetaBase extends Model { $rtn = $this->data['meta_key']; - return $rtn; } @@ -181,7 +181,6 @@ class BuildMetaBase extends Model { $rtn = $this->data['meta_value']; - return $rtn; } @@ -195,6 +194,7 @@ class BuildMetaBase extends Model { $this->_validateNotNull('Id', $value); $this->_validateInt('Id', $value); + if ($this->data['id'] === $value) { return; } @@ -214,6 +214,7 @@ class BuildMetaBase extends Model { $this->_validateNotNull('ProjectId', $value); $this->_validateInt('ProjectId', $value); + if ($this->data['project_id'] === $value) { return; } @@ -230,8 +231,8 @@ class BuildMetaBase extends Model */ public function setBuildId($value) { - $this->_validateInt('BuildId', $value); + if ($this->data['build_id'] === $value) { return; } @@ -251,6 +252,7 @@ class BuildMetaBase extends Model { $this->_validateNotNull('MetaKey', $value); $this->_validateString('MetaKey', $value); + if ($this->data['meta_key'] === $value) { return; } @@ -267,8 +269,8 @@ class BuildMetaBase extends Model */ public function setMetaValue($value) { - $this->_validateString('MetaValue', $value); + if ($this->data['meta_value'] === $value) { return; } @@ -297,7 +299,7 @@ class BuildMetaBase extends Model $rtn = $this->cache->get($cacheKey, null); if (empty($rtn)) { - $rtn = \b8\Store\Factory::getStore('Build')->getById($key); + $rtn = Factory::getStore('Build')->getById($key); $this->cache->set($cacheKey, $rtn); } diff --git a/PHPCI/Model/Base/ProjectBase.php b/PHPCI/Model/Base/ProjectBase.php index 6fc1a422..f23e1d7f 100644 --- a/PHPCI/Model/Base/ProjectBase.php +++ b/PHPCI/Model/Base/ProjectBase.php @@ -7,6 +7,7 @@ namespace PHPCI\Model\Base; use b8\Model; +use b8\Store\Factory; /** * Project Base Model @@ -39,12 +40,13 @@ class ProjectBase extends Model 'type' => null, 'token' => null, 'access_information' => null, - ); + ); /** * @var array */ protected $getters = array( + // Direct property getters: 'id' => 'getId', 'title' => 'getTitle', 'reference' => 'getReference', @@ -52,12 +54,15 @@ class ProjectBase extends Model 'type' => 'getType', 'token' => 'getToken', 'access_information' => 'getAccessInformation', - ); + + // Foreign key getters: + ); /** * @var array */ protected $setters = array( + // Direct property setters: 'id' => 'setId', 'title' => 'setTitle', 'reference' => 'setReference', @@ -65,7 +70,9 @@ class ProjectBase extends Model 'type' => 'setType', 'token' => 'setToken', 'access_information' => 'setAccessInformation', - ); + + // Foreign key setters: + ); /** * @var array @@ -73,59 +80,56 @@ class ProjectBase extends Model public $columns = array( 'id' => array( 'type' => 'int', - 'length' => '11', + 'length' => 11, 'primary_key' => true, 'auto_increment' => true, 'default' => null, - ), + ), 'title' => array( 'type' => 'varchar', - 'length' => '250', - 'default' => '', - ), + 'length' => 250, + ), 'reference' => array( 'type' => 'varchar', - 'length' => '250', - 'default' => '', - ), + 'length' => 250, + ), 'git_key' => array( 'type' => 'text', - 'length' => '', 'nullable' => true, 'default' => null, - ), + ), 'type' => array( 'type' => 'varchar', - 'length' => '50', - 'default' => '1', - ), + 'length' => 50, + 'default' => 1, + ), 'token' => array( 'type' => 'varchar', - 'length' => '50', + 'length' => 50, 'nullable' => true, 'default' => null, - ), + ), 'access_information' => array( 'type' => 'varchar', - 'length' => '250', + 'length' => 250, 'nullable' => true, 'default' => null, - ), - ); + ), + ); /** * @var array */ public $indexes = array( 'PRIMARY' => array('unique' => true, 'columns' => 'id'), - ); + 'idx_project_title' => array('columns' => 'title'), + ); /** * @var array */ public $foreignKeys = array( - ); - + ); /** * Get the value of Id / id. @@ -136,7 +140,6 @@ class ProjectBase extends Model { $rtn = $this->data['id']; - return $rtn; } @@ -149,7 +152,6 @@ class ProjectBase extends Model { $rtn = $this->data['title']; - return $rtn; } @@ -162,7 +164,6 @@ class ProjectBase extends Model { $rtn = $this->data['reference']; - return $rtn; } @@ -175,7 +176,6 @@ class ProjectBase extends Model { $rtn = $this->data['git_key']; - return $rtn; } @@ -188,7 +188,6 @@ class ProjectBase extends Model { $rtn = $this->data['type']; - return $rtn; } @@ -201,7 +200,6 @@ class ProjectBase extends Model { $rtn = $this->data['token']; - return $rtn; } @@ -214,7 +212,6 @@ class ProjectBase extends Model { $rtn = $this->data['access_information']; - return $rtn; } @@ -228,6 +225,7 @@ class ProjectBase extends Model { $this->_validateNotNull('Id', $value); $this->_validateInt('Id', $value); + if ($this->data['id'] === $value) { return; } @@ -247,6 +245,7 @@ class ProjectBase extends Model { $this->_validateNotNull('Title', $value); $this->_validateString('Title', $value); + if ($this->data['title'] === $value) { return; } @@ -266,6 +265,7 @@ class ProjectBase extends Model { $this->_validateNotNull('Reference', $value); $this->_validateString('Reference', $value); + if ($this->data['reference'] === $value) { return; } @@ -282,8 +282,8 @@ class ProjectBase extends Model */ public function setGitKey($value) { - $this->_validateString('GitKey', $value); + if ($this->data['git_key'] === $value) { return; } @@ -303,6 +303,7 @@ class ProjectBase extends Model { $this->_validateNotNull('Type', $value); $this->_validateString('Type', $value); + if ($this->data['type'] === $value) { return; } @@ -319,8 +320,8 @@ class ProjectBase extends Model */ public function setToken($value) { - $this->_validateString('Token', $value); + if ($this->data['token'] === $value) { return; } @@ -337,8 +338,8 @@ class ProjectBase extends Model */ public function setAccessInformation($value) { - $this->_validateString('AccessInformation', $value); + if ($this->data['access_information'] === $value) { return; } @@ -357,6 +358,6 @@ class ProjectBase extends Model */ public function getProjectBuilds() { - return \b8\Store\Factory::getStore('Build')->getByProjectId($this->getId()); + return Factory::getStore('Build')->getByProjectId($this->getId()); } } diff --git a/PHPCI/Model/Base/UserBase.php b/PHPCI/Model/Base/UserBase.php index 8fad4ea1..e8035660 100644 --- a/PHPCI/Model/Base/UserBase.php +++ b/PHPCI/Model/Base/UserBase.php @@ -7,6 +7,7 @@ namespace PHPCI\Model\Base; use b8\Model; +use b8\Store\Factory; /** * User Base Model @@ -37,29 +38,35 @@ class UserBase extends Model 'hash' => null, 'is_admin' => null, 'name' => null, - ); + ); /** * @var array */ protected $getters = array( + // Direct property getters: 'id' => 'getId', 'email' => 'getEmail', 'hash' => 'getHash', 'is_admin' => 'getIsAdmin', 'name' => 'getName', - ); + + // Foreign key getters: + ); /** * @var array */ protected $setters = array( + // Direct property setters: 'id' => 'setId', 'email' => 'setEmail', 'hash' => 'setHash', 'is_admin' => 'setIsAdmin', 'name' => 'setName', - ); + + // Foreign key setters: + ); /** * @var array @@ -67,33 +74,30 @@ class UserBase extends Model public $columns = array( 'id' => array( 'type' => 'int', - 'length' => '11', + 'length' => 11, 'primary_key' => true, 'auto_increment' => true, 'default' => null, - ), + ), 'email' => array( 'type' => 'varchar', - 'length' => '250', - 'default' => '', - ), + 'length' => 250, + ), 'hash' => array( 'type' => 'varchar', - 'length' => '250', - 'default' => '', - ), + 'length' => 250, + ), 'is_admin' => array( 'type' => 'tinyint', - 'length' => '1', - 'default' => '0', - ), + 'length' => 1, + ), 'name' => array( 'type' => 'varchar', - 'length' => '250', + 'length' => 250, 'nullable' => true, 'default' => null, - ), - ); + ), + ); /** * @var array @@ -101,14 +105,13 @@ class UserBase extends Model public $indexes = array( 'PRIMARY' => array('unique' => true, 'columns' => 'id'), 'idx_email' => array('unique' => true, 'columns' => 'email'), - ); + ); /** * @var array */ public $foreignKeys = array( - ); - + ); /** * Get the value of Id / id. @@ -119,7 +122,6 @@ class UserBase extends Model { $rtn = $this->data['id']; - return $rtn; } @@ -132,7 +134,6 @@ class UserBase extends Model { $rtn = $this->data['email']; - return $rtn; } @@ -145,7 +146,6 @@ class UserBase extends Model { $rtn = $this->data['hash']; - return $rtn; } @@ -158,7 +158,6 @@ class UserBase extends Model { $rtn = $this->data['is_admin']; - return $rtn; } @@ -171,7 +170,6 @@ class UserBase extends Model { $rtn = $this->data['name']; - return $rtn; } @@ -185,6 +183,7 @@ class UserBase extends Model { $this->_validateNotNull('Id', $value); $this->_validateInt('Id', $value); + if ($this->data['id'] === $value) { return; } @@ -204,6 +203,7 @@ class UserBase extends Model { $this->_validateNotNull('Email', $value); $this->_validateString('Email', $value); + if ($this->data['email'] === $value) { return; } @@ -223,6 +223,7 @@ class UserBase extends Model { $this->_validateNotNull('Hash', $value); $this->_validateString('Hash', $value); + if ($this->data['hash'] === $value) { return; } @@ -242,6 +243,7 @@ class UserBase extends Model { $this->_validateNotNull('IsAdmin', $value); $this->_validateInt('IsAdmin', $value); + if ($this->data['is_admin'] === $value) { return; } @@ -258,8 +260,8 @@ class UserBase extends Model */ public function setName($value) { - $this->_validateString('Name', $value); + if ($this->data['name'] === $value) { return; } diff --git a/PHPCI/Model/Build.php b/PHPCI/Model/Build.php index e60ff64e..bc814560 100644 --- a/PHPCI/Model/Build.php +++ b/PHPCI/Model/Build.php @@ -9,8 +9,8 @@ namespace PHPCI\Model; +use b8\Store\Factory; use PHPCI\Model\Base\BuildBase; -use PHPCI\Builder; /** * Build Model @@ -21,6 +21,11 @@ use PHPCI\Builder; */ class Build extends BuildBase { + const STATUS_NEW = 0; + const STATUS_RUNNING = 1; + const STATUS_SUCCESS = 2; + const STATUS_FAILED = 3; + /** * Get link to commit from another source (i.e. Github) */ @@ -46,9 +51,19 @@ class Build extends BuildBase } /** - * Create a working copy by cloning, copying, or similar. - */ - public function createWorkingCopy(Builder $builder, $buildPath) + * Store build metadata + */ + public function storeMeta($key, $value) { + $value = json_encode($value); + Factory::getStore('Build')->setMeta($this->getProjectId(), $this->getId(), $key, $value); + } + + /** + * Is this build successful? + */ + public function isSuccessful() + { + return ($this->getStatus() === self::STATUS_SUCCESS); } } diff --git a/PHPCI/Model/Build/LocalBuild.php b/PHPCI/Model/Build/LocalBuild.php index 07698d42..6804f437 100644 --- a/PHPCI/Model/Build/LocalBuild.php +++ b/PHPCI/Model/Build/LocalBuild.php @@ -29,44 +29,68 @@ class LocalBuild extends Build $reference = $this->getProject()->getReference(); $reference = substr($reference, -1) == '/' ? substr($reference, 0, -1) : $reference; $buildPath = substr($buildPath, 0, -1); - $yamlParser = new YamlParser(); - - if(is_file($reference.'/config')) { - //We're probably looing at a bare repository. We'll open the config and check - $gitConfig = parse_ini_file($reference.'/config',TRUE); - if($gitConfig["core"]["bare"]) { - // Looks like we're right. We need to extract the archive! - $guid = uniqid(); - $builder->executeCommand('mkdir "/tmp/%s" && git --git-dir="%s" archive master | tar -x -C "/tmp/%s"', $guid, $reference, $guid); - $reference = '/tmp/'.$guid; - } + + // If there's a /config file in the reference directory, it is probably a bare repository + // which we'll extract into our build path directly. + if(is_file($reference.'/config') && $this->handleBareRepository($builder, $reference, $buildPath) === true) { + return true; } - if (!is_file($reference . '/phpci.yml')) { - $builder->logFailure('Project does not contain a phpci.yml file.'); + $buildSettings = $this->handleConfig($builder, $reference); + + if ($buildSettings === false) { return false; } - $yamlFile = file_get_contents($reference . '/phpci.yml'); - $builder->setConfigArray($yamlParser->parse($yamlFile)); - - $buildSettings = $builder->getConfig('build_settings'); - if (isset($buildSettings['prefer_symlink']) && $buildSettings['prefer_symlink'] === true) { - if (is_link($buildPath) && is_file($buildPath)) { - unlink($buildPath); - } - - $builder->log(sprintf('Symlinking: %s to %s', $reference, $buildPath)); - - if (!symlink($reference, $buildPath)) { - $builder->logFailure('Failed to symlink.'); - return false; - } + return $this->handleSymlink($builder, $reference, $buildPath); } else { $builder->executeCommand('cp -Rf "%s" "%s/"', $reference, $buildPath); } return true; } + + protected function handleBareRepository(Builder $builder, $reference, $buildPath) + { + $gitConfig = parse_ini_file($reference.'/config', true); + + // If it is indeed a bare repository, then extract it into our build path: + if($gitConfig['core']['bare']) { + $builder->executeCommand('git --git-dir="%s" archive master | tar -x -C "%s"', $reference, $buildPath); + return true; + } + + return false; + } + + protected function handleSymlink(Builder $builder, $reference, $buildPath) + { + if (is_link($buildPath) && is_file($buildPath)) { + unlink($buildPath); + } + + $builder->log(sprintf('Symlinking: %s to %s', $reference, $buildPath)); + + if (!symlink($reference, $buildPath)) { + $builder->logFailure('Failed to symlink.'); + return false; + } + + return true; + } + + protected function handleConfig(Builder $builder, $reference) + { + /** @todo Add support for database-based yml definition */ + if (!is_file($reference . '/phpci.yml')) { + $builder->logFailure('Project does not contain a phpci.yml file.'); + return false; + } + + $yamlParser = new YamlParser(); + $yamlFile = file_get_contents($reference . '/phpci.yml'); + $builder->setConfigArray($yamlParser->parse($yamlFile)); + return $builder->getConfig('build_settings'); + } } diff --git a/PHPCI/Model/Build/MercurialBuild.php b/PHPCI/Model/Build/MercurialBuild.php index de95c06b..dbfcf4e2 100644 --- a/PHPCI/Model/Build/MercurialBuild.php +++ b/PHPCI/Model/Build/MercurialBuild.php @@ -52,8 +52,8 @@ class MercurialBuild extends Build /** * Use an mercurial clone. */ - protected function cloneByHttp(Builder $builder, $to) + protected function cloneByHttp(Builder $builder, $cloneTo) { - return $builder->executeCommand('hg clone %s "%s" -r %s', $this->getCloneUrl(), $to, $this->getBranch()); + return $builder->executeCommand('hg clone %s "%s" -r %s', $this->getCloneUrl(), $cloneTo, $this->getBranch()); } } diff --git a/PHPCI/Model/Build/RemoteGitBuild.php b/PHPCI/Model/Build/RemoteGitBuild.php index 490b92ef..c4b03d2c 100644 --- a/PHPCI/Model/Build/RemoteGitBuild.php +++ b/PHPCI/Model/Build/RemoteGitBuild.php @@ -63,21 +63,21 @@ class RemoteGitBuild extends Build /** * Use an HTTP-based git clone. */ - protected function cloneByHttp(Builder $builder, $to) + protected function cloneByHttp(Builder $builder, $cloneTo) { - return $builder->executeCommand('git clone -b %s %s "%s"', $this->getBranch(), $this->getCloneUrl(), $to); + return $builder->executeCommand('git clone -b %s %s "%s"', $this->getBranch(), $this->getCloneUrl(), $cloneTo); } /** * Use an SSH-based git clone. */ - protected function cloneBySsh(Builder $builder, $to) + protected function cloneBySsh(Builder $builder, $cloneTo) { // Copy the project's keyfile to disk: - $keyPath = realpath($to); + $keyPath = realpath($cloneTo); if ($keyPath === false) { - $keyPath = dirname($to); + $keyPath = dirname($cloneTo); } $keyFile = $keyPath . '.key'; @@ -87,7 +87,7 @@ class RemoteGitBuild extends Build // Use the key file to do an SSH clone: $cmd = 'eval `ssh-agent -s` && ssh-add "%s" && git clone -b %s %s "%s" && ssh-agent -k'; - $success = $builder->executeCommand($cmd, $keyFile, $this->getBranch(), $this->getCloneUrl(), $to); + $success = $builder->executeCommand($cmd, $keyFile, $this->getBranch(), $this->getCloneUrl(), $cloneTo); // Remove the key file: unlink($keyFile); diff --git a/PHPCI/Plugin.php b/PHPCI/Plugin.php index 5aa06e9e..a924b39a 100644 --- a/PHPCI/Plugin.php +++ b/PHPCI/Plugin.php @@ -9,12 +9,15 @@ namespace PHPCI; +use PHPCI\Builder; +use PHPCI\Model\Build; + /** * PHPCI Plugin Interface - Used by all build plugins. * @author Dan Cryer */ interface Plugin { - public function __construct(\PHPCI\Builder $phpci, array $options = array()); + public function __construct(Builder $phpci, Build $build, array $options = array()); public function execute(); } diff --git a/PHPCI/Plugin/Atoum.php b/PHPCI/Plugin/Atoum.php index 80dce4c3..7a837b0a 100644 --- a/PHPCI/Plugin/Atoum.php +++ b/PHPCI/Plugin/Atoum.php @@ -2,15 +2,19 @@ namespace PHPCI\Plugin; +use PHPCI\Builder; +use PHPCI\Model\Build; + class Atoum implements \PHPCI\Plugin { private $args; private $config; private $directory; - public function __construct(\PHPCI\Builder $phpci, array $options = array()) + public function __construct(Builder $phpci, Build $build, array $options = array()) { $this->phpci = $phpci; + $this->build = $build; if (isset($options['executable'])) { $this->executable = $this->phpci->buildPath . DIRECTORY_SEPARATOR.$options['executable']; @@ -50,7 +54,7 @@ class Atoum implements \PHPCI\Plugin $status = true; exec($cmd, $output); - if (count(preg_grep("/Success \(/", $output)) == 0 ) { + if (count(preg_grep("/Success \(/", $output)) == 0) { $status = false; $this->phpci->log($output, ' '); } diff --git a/PHPCI/Plugin/Behat.php b/PHPCI/Plugin/Behat.php index c9c832ce..704d76cf 100644 --- a/PHPCI/Plugin/Behat.php +++ b/PHPCI/Plugin/Behat.php @@ -9,6 +9,9 @@ namespace PHPCI\Plugin; +use PHPCI\Builder; +use PHPCI\Model\Build; + /** * Behat BDD Plugin * @author Dan Cryer @@ -18,10 +21,16 @@ namespace PHPCI\Plugin; class Behat implements \PHPCI\Plugin { protected $phpci; + protected $features; - public function __construct(\PHPCI\Builder $phpci, array $options = array()) + public function __construct(Builder $phpci, Build $build, array $options = array()) { $this->phpci = $phpci; + $this->features = ''; + + if (!empty($options['features'])) { + $this->features = $options['features']; + } } /** @@ -32,14 +41,14 @@ class Behat implements \PHPCI\Plugin $curdir = getcwd(); chdir($this->phpci->buildPath); - $phpspec = $this->phpci->findBinary('phpspec'); + $behat = $this->phpci->findBinary('behat'); - if (!$phpspec) { - $this->phpci->logFailure('Could not find phpspec.'); + if (!$behat) { + $this->phpci->logFailure('Could not find behat.'); return false; } - $success = $this->phpci->executeCommand($phpspec); + $success = $this->phpci->executeCommand($behat . ' --no-time --format="failed" %s', $this->features); chdir($curdir); return $success; diff --git a/PHPCI/Plugin/Campfire.php b/PHPCI/Plugin/Campfire.php index f3dfced2..17b7f077 100644 --- a/PHPCI/Plugin/Campfire.php +++ b/PHPCI/Plugin/Campfire.php @@ -1,6 +1,10 @@ phpci = $phpci; + $this->build = $build; $this->message = $options['message']; - $this->userAgent = "Phpci/1.0 (http://www.phptesting.org/)"; + $this->userAgent = "PHPCI/1.0 (+http://www.phptesting.org/)"; $this->cookie = "phpcicookie"; $buildSettings = $phpci->getConfig('build_settings'); @@ -36,14 +37,14 @@ class Campfire implements \PHPCI\Plugin $this->authToken = $campfire['authToken']; $this->roomId = $campfire['roomId']; } else { - throw new \Exception("No connexion parameters given for Campfire plugin"); + throw new \Exception("No connection parameters given for Campfire plugin"); } } public function execute() { - $url = PHPCI_URL."build/view/".$this->phpci->getBuild()->getId(); + $url = PHPCI_URL."build/view/".$this->build->getId(); $message = str_replace("%buildurl%", $url, $this->message); $this->joinRoom($this->roomId); $status = $this->speak($message, $this->roomId); @@ -52,19 +53,15 @@ class Campfire implements \PHPCI\Plugin return $status; } + public function joinRoom($roomId) { - $this->_getPageByPost('/room/'.$roomId.'/join.json'); + $this->getPageByPost('/room/'.$roomId.'/join.json'); } public function leaveRoom($roomId) { - $this->_getPageByPost('/room/'.$roomId.'/leave.json'); - } - - public function logout() - { - // New API is stateless, no concept of logout + $this->getPageByPost('/room/'.$roomId.'/leave.json'); } public function speak($message, $roomId, $isPaste = false) @@ -76,12 +73,11 @@ class Campfire implements \PHPCI\Plugin $type = 'TextMessage'; } - return $this->_getPageByPost($page, - array('message' => array('type' => $type, 'body' => $message))); + return $this->getPageByPost($page, array('message' => array('type' => $type, 'body' => $message))); } - private function _getPageByPost($page, $data = null) + private function getPageByPost($page, $data = null) { $url = $this->url . $page; // The new API allows JSON, so we can pass @@ -89,21 +85,21 @@ class Campfire implements \PHPCI\Plugin $json = json_encode($data); // cURL init & config - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, $url); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_POST, 1); - curl_setopt($ch, CURLOPT_USERAGENT, $this->userAgent); - curl_setopt($ch, CURLOPT_VERBOSE, $this->verbose); - curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); - curl_setopt($ch, CURLOPT_USERPWD, $this->authToken . ':x'); - curl_setopt($ch, CURLOPT_HTTPHEADER, array("Content-type: application/json")); - curl_setopt($ch, CURLOPT_COOKIEFILE, $this->cookie); + $handle = curl_init(); + curl_setopt($handle, CURLOPT_URL, $url); + curl_setopt($handle, CURLOPT_RETURNTRANSFER, true); + curl_setopt($handle, CURLOPT_POST, 1); + curl_setopt($handle, CURLOPT_USERAGENT, $this->userAgent); + curl_setopt($handle, CURLOPT_VERBOSE, $this->verbose); + curl_setopt($handle, CURLOPT_FOLLOWLOCATION, 1); + curl_setopt($handle, CURLOPT_USERPWD, $this->authToken . ':x'); + curl_setopt($handle, CURLOPT_HTTPHEADER, array("Content-type: application/json")); + curl_setopt($handle, CURLOPT_COOKIEFILE, $this->cookie); - curl_setopt($ch, CURLOPT_POSTFIELDS, $json); - $output = curl_exec($ch); + curl_setopt($handle, CURLOPT_POSTFIELDS, $json); + $output = curl_exec($handle); - curl_close($ch); + curl_close($handle); // We tend to get one space with an otherwise blank response $output = trim($output); diff --git a/PHPCI/Plugin/CleanBuild.php b/PHPCI/Plugin/CleanBuild.php index a9709d8a..bfe96761 100644 --- a/PHPCI/Plugin/CleanBuild.php +++ b/PHPCI/Plugin/CleanBuild.php @@ -9,6 +9,8 @@ namespace PHPCI\Plugin; +use PHPCI\Builder; +use PHPCI\Model\Build; /** * Clean build removes Composer related files and allows PHPCI users to clean up their build directory. * Useful as a precursor to copy_build. @@ -21,9 +23,8 @@ class CleanBuild implements \PHPCI\Plugin protected $remove; protected $phpci; - public function __construct(\PHPCI\Builder $phpci, array $options = array()) + public function __construct(Builder $phpci, Build $build, array $options = array()) { - $path = $phpci->buildPath; $this->phpci = $phpci; $this->remove = isset($options['remove']) && is_array($options['remove']) ? $options['remove'] : array(); } diff --git a/PHPCI/Plugin/Codeception.php b/PHPCI/Plugin/Codeception.php index 827facf1..f2e580c7 100644 --- a/PHPCI/Plugin/Codeception.php +++ b/PHPCI/Plugin/Codeception.php @@ -9,6 +9,9 @@ namespace PHPCI\Plugin; +use PHPCI\Builder; +use PHPCI\Model\Build; + /** * Codeception Plugin - Enables full acceptance, unit, and functional testing. * @author Don Gilbert @@ -25,7 +28,7 @@ class Codeception implements \PHPCI\Plugin */ protected $xmlConfigFile; - public function __construct(\PHPCI\Builder $phpci, array $options = array()) + public function __construct(Builder $phpci, Build $build, array $options = array()) { $this->phpci = $phpci; @@ -81,4 +84,4 @@ class Codeception implements \PHPCI\Plugin } return $success; } -} \ No newline at end of file +} diff --git a/PHPCI/Plugin/Composer.php b/PHPCI/Plugin/Composer.php index ff1cbce4..1af4abf6 100644 --- a/PHPCI/Plugin/Composer.php +++ b/PHPCI/Plugin/Composer.php @@ -9,6 +9,9 @@ namespace PHPCI\Plugin; +use PHPCI\Builder; +use PHPCI\Model\Build; + /** * Composer Plugin - Provides access to Composer functionality. * @author Dan Cryer @@ -22,7 +25,7 @@ class Composer implements \PHPCI\Plugin protected $preferDist; protected $phpci; - public function __construct(\PHPCI\Builder $phpci, array $options = array()) + public function __construct(Builder $phpci, Build $build, array $options = array()) { $path = $phpci->buildPath; $this->phpci = $phpci; @@ -43,7 +46,8 @@ class Composer implements \PHPCI\Plugin return false; } - $cmd = $composerLocation . ' --no-ansi --no-interaction '. ($this->preferDist ? '--prefer-dist' : null) .' --working-dir="%s" %s'; + $cmd = $composerLocation . ' --no-ansi --no-interaction '; + $cmd .= ($this->preferDist ? '--prefer-dist' : null) . ' --working-dir="%s" %s'; return $this->phpci->executeCommand($cmd, $this->directory, $this->action); } diff --git a/PHPCI/Plugin/CopyBuild.php b/PHPCI/Plugin/CopyBuild.php index 15a5d56a..1ba06558 100644 --- a/PHPCI/Plugin/CopyBuild.php +++ b/PHPCI/Plugin/CopyBuild.php @@ -9,6 +9,8 @@ namespace PHPCI\Plugin; +use PHPCI\Builder; +use PHPCI\Model\Build; /** * Copy Build Plugin - Copies the entire build to another directory. * @author Dan Cryer @@ -20,7 +22,7 @@ class CopyBuild implements \PHPCI\Plugin protected $directory; protected $phpci; - public function __construct(\PHPCI\Builder $phpci, array $options = array()) + public function __construct(Builder $phpci, Build $build, array $options = array()) { $path = $phpci->buildPath; $this->phpci = $phpci; diff --git a/PHPCI/Plugin/Email.php b/PHPCI/Plugin/Email.php index 5e94cd8b..7b80d795 100644 --- a/PHPCI/Plugin/Email.php +++ b/PHPCI/Plugin/Email.php @@ -9,6 +9,8 @@ namespace PHPCI\Plugin; +use PHPCI\Builder; +use PHPCI\Model\Build; /** * Email Plugin - Provides simple email capability to PHPCI. @@ -18,7 +20,6 @@ namespace PHPCI\Plugin; */ class Email implements \PHPCI\Plugin { - /** * @var \PHPCI\Builder */ @@ -39,23 +40,15 @@ class Email implements \PHPCI\Plugin */ protected $mailer; - public function __construct(\PHPCI\Builder $phpci, - array $options = array(), - \Swift_Mailer $mailer = null) + public function __construct(Builder $phpci, Build $build, array $options = array()) { $phpCiSettings = $phpci->getSystemConfig('phpci'); $this->phpci = $phpci; + $this->build = $build; $this->options = $options; $this->emailConfig = isset($phpCiSettings['email_settings']) ? $phpCiSettings['email_settings'] : array(); - // Either a mailer will have been passed in or we load from the - // config. - if ($mailer === null) { - $this->loadSwiftMailerFromConfig(); - } - else { - $this->mailer = $mailer; - } + $this->loadSwiftMailerFromConfig(); } /** @@ -71,20 +64,17 @@ class Email implements \PHPCI\Plugin return false; } - $sendFailures = array(); - $subjectTemplate = "PHPCI - %s - %s"; $projectName = $this->phpci->getBuildProjectTitle(); - $logText = $this->phpci->getBuild()->getLog(); + $logText = $this->build->getLog(); - if($this->phpci->getSuccessStatus()) { + if ($this->build->isSuccessful()) { $sendFailures = $this->sendSeparateEmails( $addresses, sprintf($subjectTemplate, $projectName, "Passing Build"), sprintf("Log Output:
%s
", $logText) ); - } - else { + } else { $sendFailures = $this->sendSeparateEmails( $addresses, sprintf($subjectTemplate, $projectName, "Failing Build"), @@ -93,14 +83,9 @@ class Email implements \PHPCI\Plugin } // This is a success if we've not failed to send anything. - $this->phpci->log(sprintf( - "%d emails sent", - (count($addresses) - count($sendFailures))) - ); - $this->phpci->log(sprintf( - "%d emails failed to send", - count($sendFailures)) - ); + $this->phpci->log(sprintf("%d emails sent", (count($addresses) - count($sendFailures)))); + $this->phpci->log(sprintf("%d emails failed to send", count($sendFailures))); + return (count($sendFailures) == 0); } @@ -126,9 +111,9 @@ class Email implements \PHPCI\Plugin public function sendSeparateEmails(array $toAddresses, $subject, $body) { $failures = array(); - foreach($toAddresses as $address) { + foreach ($toAddresses as $address) { $newFailures = $this->sendEmail($address, $subject, $body); - foreach($newFailures as $failure) { + foreach ($newFailures as $failure) { $failures[] = $failure; } } @@ -151,13 +136,11 @@ class Email implements \PHPCI\Plugin protected function getMailConfig($configName) { - if (isset($this->emailConfig[$configName]) - && $this->emailConfig[$configName] != "") - { + if (isset($this->emailConfig[$configName]) && $this->emailConfig[$configName] != "") { return $this->emailConfig[$configName]; - } - // Check defaults - else { + } else { + // Check defaults + switch($configName) { case 'smtp_address': return "localhost"; @@ -178,7 +161,7 @@ class Email implements \PHPCI\Plugin protected function getEmailAddresses() { $addresses = array(); - $committer = $this->phpci->getBuild()->getCommitterEmail(); + $committer = $this->build->getCommitterEmail(); if (isset($this->options['committer']) && !empty($committer)) { $addresses[] = $committer; @@ -196,4 +179,4 @@ class Email implements \PHPCI\Plugin } return $addresses; } -} \ No newline at end of file +} diff --git a/PHPCI/Plugin/Env.php b/PHPCI/Plugin/Env.php index e09d3dbf..09ffbb5a 100644 --- a/PHPCI/Plugin/Env.php +++ b/PHPCI/Plugin/Env.php @@ -9,6 +9,9 @@ namespace PHPCI\Plugin; +use PHPCI\Builder; +use PHPCI\Model\Build; + /** * Environment variable plugin * @author Steve Kamerman @@ -20,7 +23,7 @@ class Env implements \PHPCI\Plugin protected $phpci; protected $env_vars; - public function __construct(\PHPCI\Builder $phpci, array $options = array()) + public function __construct(Builder $phpci, Build $build, array $options = array()) { $this->phpci = $phpci; $this->env_vars = $options; diff --git a/PHPCI/Plugin/Grunt.php b/PHPCI/Plugin/Grunt.php index ca024276..477a4849 100644 --- a/PHPCI/Plugin/Grunt.php +++ b/PHPCI/Plugin/Grunt.php @@ -9,6 +9,9 @@ namespace PHPCI\Plugin; +use PHPCI\Builder; +use PHPCI\Model\Build; + /** * Grunt Plugin - Provides access to grunt functionality. * @author Tobias Tom @@ -24,14 +27,31 @@ class Grunt implements \PHPCI\Plugin protected $grunt; protected $gruntfile; - public function __construct(\PHPCI\Builder $phpci, array $options = array()) + public function __construct(Builder $phpci, Build $build, array $options = array()) { - $path = $phpci->buildPath; - $this->phpci = $phpci; - $this->directory = isset($options['directory']) ? $path . '/' . $options['directory'] : $path; - $this->task = isset($options['task']) ? $options['task'] : null; - $this->grunt = isset($options['grunt']) ? $options['grunt'] : $this->phpci->findBinary('grunt'); - $this->gruntfile = isset($options['gruntfile']) ? $options['gruntfile'] : 'Gruntfile.js'; + $path = $phpci->buildPath; + $this->phpci = $phpci; + $this->directory = $path; + $this->task = null; + $this->grunt = $this->phpci->findBinary('grunt'); + $this->gruntfile = 'Gruntfile.js'; + + // Handle options: + if (isset($options['directory'])) { + $this->directory = $path . '/' . $options['directory']; + } + + if (isset($options['task'])) { + $this->task = $options['task']; + } + + if (isset($options['grunt'])) { + $this->grunt = $options['grunt']; + } + + if (isset($options['gruntfile'])) { + $this->gruntfile = $options['gruntfile']; + } } /** @@ -40,7 +60,7 @@ class Grunt implements \PHPCI\Plugin public function execute() { // if npm does not work, we cannot use grunt, so we return false - if ( !$this->phpci->executeCommand( 'cd %s && npm install', $this->directory ) ) { + if (!$this->phpci->executeCommand('cd %s && npm install', $this->directory)) { return false; } diff --git a/PHPCI/Plugin/Irc.php b/PHPCI/Plugin/Irc.php index 64e8f523..a4a3310b 100644 --- a/PHPCI/Plugin/Irc.php +++ b/PHPCI/Plugin/Irc.php @@ -1,6 +1,10 @@ @@ -16,8 +20,7 @@ class Irc implements \PHPCI\Plugin private $room; private $nick; - - public function __construct(\PHPCI\Builder $phpci, array $options = array()) + public function __construct(Builder $phpci, Build $build, array $options = array()) { $this->phpci = $phpci; $this->message = $options['message']; @@ -53,7 +56,9 @@ class Irc implements \PHPCI\Plugin fputs($sock, 'PRIVMSG ' . $this->room . ' :' . $msg . "\r\n"); while ($res = fgets($sock)) { - $this->phpci->log($res); + // We don't need to do anything, + // but the IRC server doesn't appear to post the message + // unless we wait for responses. } fclose($sock); diff --git a/PHPCI/Plugin/Lint.php b/PHPCI/Plugin/Lint.php index c8c1b880..ebab146e 100644 --- a/PHPCI/Plugin/Lint.php +++ b/PHPCI/Plugin/Lint.php @@ -11,6 +11,7 @@ namespace PHPCI\Plugin; use PHPCI\Builder; use PHPCI\Model\Build; + /** * PHP Lint Plugin - Provides access to PHP lint functionality. * @author Dan Cryer @@ -24,7 +25,7 @@ class Lint implements \PHPCI\Plugin protected $ignore; protected $phpci; - public function __construct(Builder $phpci, Build $build, array $options = array()) + public function __construct(Builder $phpci, Build $build, array $options = array()) { $this->phpci = $phpci; $this->directories = array(''); @@ -64,6 +65,19 @@ class Lint implements \PHPCI\Plugin return $success; } + protected function lintItem($php, $item, $itemPath) + { + $success = true; + + if ($item->isFile() && $item->getExtension() == 'php' && !$this->lintFile($php, $itemPath)) { + $success = false; + } elseif ($item->isDir() && $this->recursive && !$this->lintDirectory($php, $itemPath . '/')) { + $success = false; + } + + return $success; + } + protected function lintDirectory($php, $path) { $success = true; @@ -80,9 +94,7 @@ class Lint implements \PHPCI\Plugin continue; } - if ($item->isFile() && $item->getExtension() == 'php' && !$this->lintFile($php, $itemPath)) { - $success = false; - } else if ($item->isDir() && $this->recursive && !$this->lintDirectory($php, $itemPath . '/')) { + if (!$this->lintItem($php, $item, $itemPath)) { $success = false; } } diff --git a/PHPCI/Plugin/Mysql.php b/PHPCI/Plugin/Mysql.php index e6ac7d54..e2fda714 100755 --- a/PHPCI/Plugin/Mysql.php +++ b/PHPCI/Plugin/Mysql.php @@ -10,6 +10,8 @@ namespace PHPCI\Plugin; use PDO; +use PHPCI\Builder; +use PHPCI\Model\Build; /** * MySQL Plugin - Provides access to a MySQL database. @@ -37,24 +39,33 @@ class Mysql implements \PHPCI\Plugin */ protected $pdo; - public function __construct(\PHPCI\Builder $phpci, array $options = array()) + public function __construct(Builder $phpci, Build $build, array $options = array()) { - $this->phpci = $phpci; - $this->queries = $options; + $this->phpci = $phpci; + $this->queries = $options; - $config = \b8\Database::getConnection('write')->getDetails(); + $config = \b8\Database::getConnection('write')->getDetails(); $this->host =(defined('PHPCI_DB_HOST')) ? PHPCI_DB_HOST : null; $this->user = $config['user']; $this->pass = $config['pass']; $buildSettings = $phpci->getConfig('build_settings'); - if (isset($buildSettings['mysql'])) { - $sql = $buildSettings['mysql']; - $this->host = !empty($sql['host']) ? $sql['host'] : $this->phpci->interpolate($this->host); - $this->user = !empty($sql['user']) ? $sql['user'] : $this->phpci->interpolate($this->user); - $this->pass = array_key_exists('pass', $sql) ? $sql['pass'] : $this->pass; + if (!isset($buildSettings['mysql'])) { + return; + } + + if (!empty($buildSettings['mysql']['host'])) { + $this->host = $this->phpci->interpolate($buildSettings['mysql']['host']); + } + + if (!empty($buildSettings['mysql']['user'])) { + $this->user = $this->phpci->interpolate($buildSettings['mysql']['user']); + } + + if (array_key_exists('pass', $buildSettings['mysql'])) { + $this->pass = $buildSettings['mysql']['pass']; } } @@ -73,7 +84,7 @@ class Mysql implements \PHPCI\Plugin if (!is_array($query)) { // Simple query $this->pdo->query($this->phpci->interpolate($query)); - } else if (isset($query['import'])) { + } elseif (isset($query['import'])) { // SQL file execution $this->executeFile($query['import']); } else { @@ -96,15 +107,15 @@ class Mysql implements \PHPCI\Plugin $import_file = $this->phpci->buildPath . $this->phpci->interpolate($query['file']); if (!is_readable($import_file)) { - throw new \Exception("Cannot open SQL import file: $import_file"); + throw new \Exception("Cannot open SQL import file: $import_file"); } - $database = isset($query['database'])? $this->phpci->interpolate($query['database']): null; + $database = isset($query['database']) ? $this->phpci->interpolate($query['database']) : null; $import_command = $this->getImportCommand($import_file, $database); - if (!$this->phpci->executeCommand($import_command)) { - throw new \Exception("Unable to execute SQL file"); - } + if (!$this->phpci->executeCommand($import_command)) { + throw new \Exception("Unable to execute SQL file"); + } return true; } @@ -115,7 +126,8 @@ class Mysql implements \PHPCI\Plugin * @param string $database If specified, this database is selected before execution * @return string */ - protected function getImportCommand($import_file, $database=null) { + protected function getImportCommand($import_file, $database = null) + { $decompression = array( 'bz2' => '| bzip2 --decompress', 'gz' => '| gzip --decompress', @@ -136,4 +148,4 @@ class Mysql implements \PHPCI\Plugin ); return strtr('cat :import_file :decomp_cmd | mysql -u:user -p:pass :database', $args); } -} \ No newline at end of file +} diff --git a/PHPCI/Plugin/PackageBuild.php b/PHPCI/Plugin/PackageBuild.php index 333de105..d2332ed3 100644 --- a/PHPCI/Plugin/PackageBuild.php +++ b/PHPCI/Plugin/PackageBuild.php @@ -9,6 +9,8 @@ namespace PHPCI\Plugin; +use PHPCI\Builder; +use PHPCI\Model\Build; /** * Create a ZIP or TAR.GZ archive of the entire build. * @author Dan Cryer @@ -22,9 +24,10 @@ class PackageBuild implements \PHPCI\Plugin protected $format; protected $phpci; - public function __construct(\PHPCI\Builder $phpci, array $options = array()) + public function __construct(Builder $phpci, Build $build, array $options = array()) { $path = $phpci->buildPath; + $this->build = $build; $this->phpci = $phpci; $this->directory = isset($options['directory']) ? $options['directory'] : $path; $this->filename = isset($options['filename']) ? $options['filename'] : 'build'; @@ -37,7 +40,7 @@ class PackageBuild implements \PHPCI\Plugin public function execute() { $path = $this->phpci->buildPath; - $build = $this->phpci->getBuild(); + $build = $this->build; if ($this->directory == $path) { return false; diff --git a/PHPCI/Plugin/Pdepend.php b/PHPCI/Plugin/Pdepend.php index bb2fdea2..70b5e530 100644 --- a/PHPCI/Plugin/Pdepend.php +++ b/PHPCI/Plugin/Pdepend.php @@ -9,6 +9,9 @@ namespace PHPCI\Plugin; +use PHPCI\Builder; +use PHPCI\Model\Build; + /** * Pdepend Plugin - Allows Pdepend report * @author Johan van der Heide @@ -44,16 +47,17 @@ class Pdepend implements \PHPCI\Plugin */ protected $location; - - public function __construct(\PHPCI\Builder $phpci, array $options = array()) + public function __construct(Builder $phpci, Build $build, array $options = array()) { $this->phpci = $phpci; + $this->build = $build; $this->directory = isset($options['directory']) ? $options['directory'] : $phpci->buildPath; - $this->summary = $phpci->getBuildProjectTitle() . '-summary.xml'; - $this->pyramid = $phpci->getBuildProjectTitle() . '-pyramid.svg'; - $this->chart = $phpci->getBuildProjectTitle() . '-chart.svg'; + $title = $phpci->getBuildProjectTitle(); + $this->summary = $title . '-summary.xml'; + $this->pyramid = $title . '-pyramid.svg'; + $this->chart = $title . '-chart.svg'; $this->location = $this->phpci->buildPath . '..' . DIRECTORY_SEPARATOR . 'pdepend'; } diff --git a/PHPCI/Plugin/Pgsql.php b/PHPCI/Plugin/Pgsql.php index e746dd72..45155ecf 100644 --- a/PHPCI/Plugin/Pgsql.php +++ b/PHPCI/Plugin/Pgsql.php @@ -10,6 +10,8 @@ namespace PHPCI\Plugin; use PDO; +use PHPCI\Builder; +use PHPCI\Model\Build; /** * PgSQL Plugin - Provides access to a PgSQL database. @@ -26,7 +28,7 @@ class Pgsql implements \PHPCI\Plugin protected $user; protected $pass; - public function __construct(\PHPCI\Builder $phpci, array $options = array()) + public function __construct(Builder $phpci, Build $build, array $options = array()) { $this->phpci = $phpci; $this->queries = $options; diff --git a/PHPCI/Plugin/PhpCodeSniffer.php b/PHPCI/Plugin/PhpCodeSniffer.php index 98d0c603..44fa5893 100755 --- a/PHPCI/Plugin/PhpCodeSniffer.php +++ b/PHPCI/Plugin/PhpCodeSniffer.php @@ -9,6 +9,9 @@ namespace PHPCI\Plugin; +use PHPCI\Builder; +use PHPCI\Model\Build; + /** * PHP Code Sniffer Plugin - Allows PHP Code Sniffer testing. * @author Dan Cryer @@ -62,22 +65,88 @@ class PhpCodeSniffer implements \PHPCI\Plugin * @param \PHPCI\Builder $phpci * @param array $options */ - public function __construct(\PHPCI\Builder $phpci, array $options = array()) + public function __construct(Builder $phpci, Build $build, array $options = array()) { $this->phpci = $phpci; - $this->suffixes = isset($options['suffixes']) ? (array)$options['suffixes'] : array('php'); - $this->directory = isset($options['directory']) ? $options['directory'] : $phpci->buildPath; - $this->standard = isset($options['standard']) ? $options['standard'] : 'PSR2'; - $this->tab_width = isset($options['tab_width']) ? $options['tab_width'] : ''; - $this->encoding = isset($options['encoding']) ? $options['encoding'] : ''; - $this->path = (isset($options['path'])) ? $options['path'] : ''; - $this->ignore = (isset($options['ignore'])) ? (array)$options['ignore'] : $this->phpci->ignore; + $this->build = $build; + $this->suffixes = array('php'); + $this->directory = $phpci->buildPath; + $this->standard = 'PSR2'; + $this->tab_width = ''; + $this->encoding = ''; + $this->path = ''; + $this->ignore = $this->phpci->ignore; + + if (isset($options['suffixes'])) { + $this->suffixes = (array)$options['suffixes']; + } + + if (isset($options['directory'])) { + $this->directory = $options['directory']; + } + + if (isset($options['standard'])) { + $this->standard = $options['standard']; + } + + if (!empty($options['tab_width'])) { + $this->tab_width = ' --tab-width='.$options['tab_width']; + } + + if (!empty($options['encoding'])) { + $this->encoding = ' --encoding=' . $options['encoding']; + } + + if (isset($options['path'])) { + $this->path = $options['path']; + } + + if (isset($options['ignore'])) { + $this->ignore = $options['ignore']; + } } /** * Runs PHP Code Sniffer in a specified directory, to a specified standard. */ public function execute() + { + list($ignore, $standard, $suffixes) = $this->getFlags(); + + $phpcs = $this->phpci->findBinary('phpcs'); + + if (!$phpcs) { + $this->phpci->logFailure('Could not find phpcs.'); + return false; + } + + $cmd = $phpcs . ' %s %s %s %s %s "%s"'; + $success = $this->phpci->executeCommand( + $cmd, + $standard, + $suffixes, + $ignore, + $this->tab_width, + $this->encoding, + $this->phpci->buildPath . $this->path + ); + + $output = $this->phpci->getLastOutput(); + + $matches = array(); + if (preg_match_all('/WARNING/', $output, $matches)) { + $this->build->storeMeta('phpcs-warnings', count($matches[0])); + } + + $matches = array(); + if (preg_match_all('/ERROR/', $output, $matches)) { + $this->build->storeMeta('phpcs-errors', count($matches[0])); + } + + return $success; + } + + protected function getFlags() { $ignore = ''; if (count($this->ignore)) { @@ -95,38 +164,6 @@ class PhpCodeSniffer implements \PHPCI\Plugin $suffixes = ' --extensions=' . implode(',', $this->suffixes); } - $tab_width = ''; - if (strlen($this->tab_width)) { - $tab_width = ' --tab-width='.$this->tab_width; - } - - $encoding = ''; - if (strlen($this->encoding)) { - $encoding = ' --encoding='.$this->encoding; - } - - $phpcs = $this->phpci->findBinary('phpcs'); - - if (!$phpcs) { - $this->phpci->logFailure('Could not find phpcs.'); - return false; - } - - $cmd = $phpcs . ' %s %s %s %s %s "%s"'; - $success = $this->phpci->executeCommand($cmd, $standard, $suffixes, $ignore, $tab_width, $encoding, $this->phpci->buildPath . $this->path); - - $output = $this->phpci->getLastOutput(); - - $matches = array(); - if (preg_match_all('/WARNING/', $output, $matches)) { - $this->phpci->storeBuildMeta('phpcs-warnings', count($matches[0])); - } - - $matches = array(); - if (preg_match_all('/ERROR/', $output, $matches)) { - $this->phpci->storeBuildMeta('phpcs-errors', count($matches[0])); - } - - return $success; + return array($ignore, $standard, $suffixes); } } diff --git a/PHPCI/Plugin/PhpCpd.php b/PHPCI/Plugin/PhpCpd.php index 5764258f..816bf261 100755 --- a/PHPCI/Plugin/PhpCpd.php +++ b/PHPCI/Plugin/PhpCpd.php @@ -9,6 +9,9 @@ namespace PHPCI\Plugin; +use PHPCI\Builder; +use PHPCI\Model\Build; + /** * PHP Copy / Paste Detector - Allows PHP Copy / Paste Detector testing. * @author Dan Cryer @@ -32,14 +35,24 @@ class PhpCpd implements \PHPCI\Plugin */ protected $ignore; - public function __construct(\PHPCI\Builder $phpci, array $options = array()) + public function __construct(Builder $phpci, Build $build, array $options = array()) { - $this->phpci = $phpci; - $this->directory = isset($options['directory']) ? $options['directory'] : $phpci->buildPath; - $this->standard = isset($options['standard']) ? $options['standard'] : 'PSR2'; - $this->path = (isset($options['path'])) ? $options['path'] : ''; - $this->ignore = (isset($options['ignore'])) ? (array)$options['ignore'] : $this->phpci->ignore; + $this->phpci = $phpci; + $this->path = $phpci->buildPath; + $this->standard = 'PSR1'; + $this->ignore = $phpci->ignore; + if (!empty($options['path'])) { + $this->path = $phpci->buildPath . $options['path']; + } + + if (!empty($options['standard'])) { + $this->standard = $options['standard']; + } + + if (!empty($options['ignore'])) { + $this->ignore = $this->phpci->ignore; + } } /** diff --git a/PHPCI/Plugin/PhpCsFixer.php b/PHPCI/Plugin/PhpCsFixer.php index 6c42fcb8..ca07e9ef 100644 --- a/PHPCI/Plugin/PhpCsFixer.php +++ b/PHPCI/Plugin/PhpCsFixer.php @@ -9,6 +9,9 @@ namespace PHPCI\Plugin; +use PHPCI\Builder; +use PHPCI\Model\Build; + /** * PHP CS Fixer - Works with the PHP CS Fixer for testing coding standards. * @author Gabriel Baker @@ -19,16 +22,13 @@ class PhpCsFixer implements \PHPCI\Plugin { protected $phpci; - protected $args = ''; - protected $workingDir = ''; - protected $level = 'all'; - protected $dryRun = true; - protected $verbose = false; - protected $diff = false; + protected $level = ' --level=all'; + protected $verbose = ''; + protected $diff = ''; protected $levels = array('psr0', 'psr1', 'psr2', 'all'); - public function __construct(\PHPCI\Builder $phpci, array $options = array()) + public function __construct(Builder $phpci, Build $build, array $options = array()) { $this->phpci = $phpci; $this->workingdir = $this->phpci->buildPath; @@ -37,8 +37,6 @@ class PhpCsFixer implements \PHPCI\Plugin public function execute() { - $success = false; - $curdir = getcwd(); chdir($this->workingdir); @@ -49,8 +47,8 @@ class PhpCsFixer implements \PHPCI\Plugin return false; } - $cmd = $phpcsfixer . ' fix . %s'; - $success = $this->phpci->executeCommand($cmd, $this->args); + $cmd = $phpcsfixer . ' fix . %s %s %s'; + $success = $this->phpci->executeCommand($cmd, $this->verbose, $this->diff, $this->level); chdir($curdir); @@ -59,38 +57,21 @@ class PhpCsFixer implements \PHPCI\Plugin public function buildArgs($options) { - $argstring = ""; - - if ( array_key_exists('verbose', $options) && $options['verbose'] ) - { - $this->verbose = true; - $this->args .= ' --verbose'; + if (isset($options['verbose']) && $options['verbose']) { + $this->verbose = ' --verbose'; } - if ( array_key_exists('diff', $options) && $options['diff'] ) - { - $this->diff = true; - $this->args .= ' --diff'; + if (isset($options['diff']) && $options['diff']) { + $this->diff = ' --diff'; } - if ( array_key_exists('level', $options) && in_array($options['level'], $this->levels) ) - { - $this->level = $options['level']; - $this->args .= ' --level='.$options['level']; + if (isset($options['level']) && in_array($options['level'], $this->levels)) { + $this->level = ' --level='.$options['level']; } - if ( array_key_exists('dryrun', $options) && $options['dryrun'] ) - { - $this->dryRun = true; - $this->args .= ' --dry-run'; - } - - if ( array_key_exists('workingdir', $options) - && $options['workingdir'] - && is_dir($this->phpci->buildPath.$options['workingdir']) ) - { - $this->workingdir = $this->phpci->buildPath.$options['workingdir']; + if (isset($options['workingdir']) && $options['workingdir']) { + $this->workingdir = $this->phpci->buildPath . $options['workingdir']; } } -} \ No newline at end of file +} diff --git a/PHPCI/Plugin/PhpLoc.php b/PHPCI/Plugin/PhpLoc.php index 6742de2b..809b6409 100644 --- a/PHPCI/Plugin/PhpLoc.php +++ b/PHPCI/Plugin/PhpLoc.php @@ -9,6 +9,9 @@ namespace PHPCI\Plugin; +use PHPCI\Builder; +use PHPCI\Model\Build; + /** * PHP Loc - Allows PHP Copy / Lines of Code testing. * @author Johan van der Heide @@ -26,9 +29,10 @@ class PhpLoc implements \PHPCI\Plugin */ protected $phpci; - public function __construct(\PHPCI\Builder $phpci, array $options = array()) + public function __construct(Builder $phpci, Build $build, array $options = array()) { $this->phpci = $phpci; + $this->build = $build; $this->directory = isset($options['directory']) ? $options['directory'] : $phpci->buildPath; } @@ -63,9 +67,9 @@ class PhpLoc implements \PHPCI\Plugin $data[$v] = (int)$matches[2][$k]; } - $this->phpci->storeBuildMeta('phploc', $data); + $this->build->storeMeta('phploc', $data); } return $success; } -} \ No newline at end of file +} diff --git a/PHPCI/Plugin/PhpMessDetector.php b/PHPCI/Plugin/PhpMessDetector.php index f1743b8d..c6291a6e 100755 --- a/PHPCI/Plugin/PhpMessDetector.php +++ b/PHPCI/Plugin/PhpMessDetector.php @@ -9,6 +9,9 @@ namespace PHPCI\Plugin; +use PHPCI\Builder; +use PHPCI\Model\Build; + /** * PHP Mess Detector Plugin - Allows PHP Mess Detector testing. * @author Dan Cryer @@ -49,21 +52,21 @@ class PhpMessDetector implements \PHPCI\Plugin * @param \PHPCI\Builder $phpci * @param array $options */ - public function __construct(\PHPCI\Builder $phpci, array $options = array()) + public function __construct(Builder $phpci, Build $build, array $options = array()) { $this->phpci = $phpci; + $this->build = $build; + $this->suffixes = array('php'); + $this->ignore = $phpci->ignore; + $this->path = ''; + $this->rules = array('codesize', 'unusedcode', 'naming'); - $this->suffixes = isset($options['suffixes']) ? (array)$options['suffixes'] : array('php'); + if (!empty($options['path'])) { + $this->path = $options['path']; + } - $this->ignore = (isset($options['ignore'])) ? (array)$options['ignore'] : $this->phpci->ignore; - - $this->path = (isset($options['path'])) ? $options['path'] : ''; - - $this->rules = isset($options['rules']) ? (array)$options['rules'] : array('codesize', 'unusedcode', 'naming'); - foreach ($this->rules as &$rule) { - if ($rule[0] !== '/' && strpos($rule, '/') !== FALSE) { - $rule = $this->phpci->buildPath . $rule; - } + foreach (array('rules', 'ignore', 'suffixes') as $key) { + $this->overrideSetting($options, $key); } } @@ -82,6 +85,12 @@ class PhpMessDetector implements \PHPCI\Plugin $suffixes = ' --suffixes ' . implode(',', $this->suffixes); } + foreach ($this->rules as &$rule) { + if ($rule[0] !== '/' && strpos($rule, '/') !== false) { + $rule = $this->phpci->buildPath . $rule; + } + } + $phpmd = $this->phpci->findBinary('phpmd'); if (!$phpmd) { @@ -90,10 +99,24 @@ class PhpMessDetector implements \PHPCI\Plugin } $cmd = $phpmd . ' "%s" text %s %s %s'; - $success = $this->phpci->executeCommand($cmd, $this->phpci->buildPath . $this->path, implode(',', $this->rules), $ignore, $suffixes); + $success = $this->phpci->executeCommand( + $cmd, + $this->phpci->buildPath . $this->path, + implode(',', $this->rules), + $ignore, + $suffixes + ); + $errors = count(array_filter(explode(PHP_EOL, $this->phpci->getLastOutput()))); - $this->phpci->storeBuildMeta('phpmd-warnings', $errors); + $this->build->storeMeta('phpmd-warnings', $errors); return $success; } + + protected function overrideSetting($options, $key) + { + if (isset($options[$key]) && is_array($options['key'])) { + $this->{$key} = $options[$key]; + } + } } diff --git a/PHPCI/Plugin/PhpParallelLint.php b/PHPCI/Plugin/PhpParallelLint.php index dc880a5c..b7a8cd24 100644 --- a/PHPCI/Plugin/PhpParallelLint.php +++ b/PHPCI/Plugin/PhpParallelLint.php @@ -9,6 +9,9 @@ namespace PHPCI\Plugin; +use PHPCI\Builder; +use PHPCI\Model\Build; + /** * Php Parallel Lint Plugin - Provides access to PHP lint functionality. * @author Vaclav Makes @@ -21,7 +24,7 @@ class PhpParallelLint implements \PHPCI\Plugin protected $preferDist; protected $phpci; - public function __construct(\PHPCI\Builder $phpci, array $options = array()) + public function __construct(Builder $phpci, Build $build, array $options = array()) { $path = $phpci->buildPath; $this->phpci = $phpci; diff --git a/PHPCI/Plugin/PhpSpec.php b/PHPCI/Plugin/PhpSpec.php index fbe981cc..afd36699 100644 --- a/PHPCI/Plugin/PhpSpec.php +++ b/PHPCI/Plugin/PhpSpec.php @@ -9,6 +9,9 @@ namespace PHPCI\Plugin; +use PHPCI\Builder; +use PHPCI\Model\Build; + /** * PHP Spec Plugin - Allows PHP Spec testing. * @author Dan Cryer @@ -18,10 +21,15 @@ namespace PHPCI\Plugin; class PhpSpec implements \PHPCI\Plugin { protected $phpci; + protected $bootstrap; - public function __construct(\PHPCI\Builder $phpci, array $options = array()) + public function __construct(Builder $phpci, Build $build, array $options = array()) { $this->phpci = $phpci; + + if (!empty($options['bootstrap'])) { + $this->bootstrap = $this->buildPath . $options['bootstrap']; + } } /** @@ -32,14 +40,19 @@ class PhpSpec implements \PHPCI\Plugin $curdir = getcwd(); chdir($this->phpci->buildPath); - $phpspec = $this->phpci->findBinary('phpspec'); + $phpspec = $this->phpci->findBinary(array('phpspec', 'phpspec.php')); if (!$phpspec) { $this->phpci->logFailure('Could not find phpspec.'); return false; } - $success = $this->phpci->executeCommand($phpspec); + if ($this->bootstrap) { + $success = $this->phpci->executeCommand($phpspec . ' -f d'); + } else { + $success = $this->phpci->executeCommand($phpspec . ' -f d --bootstrap "%s"', $this->bootstrap); + } + chdir($curdir); return $success; diff --git a/PHPCI/Plugin/PhpUnit.php b/PHPCI/Plugin/PhpUnit.php index dce241cf..9b905fb6 100755 --- a/PHPCI/Plugin/PhpUnit.php +++ b/PHPCI/Plugin/PhpUnit.php @@ -9,6 +9,9 @@ namespace PHPCI\Plugin; +use PHPCI\Builder; +use PHPCI\Model\Build; + /** * PHP Unit Plugin - Allows PHP Unit testing. * @author Dan Cryer @@ -43,7 +46,7 @@ class PhpUnit implements \PHPCI\Plugin */ protected $xmlConfigFile; - public function __construct(\PHPCI\Builder $phpci, array $options = array()) + public function __construct(Builder $phpci, Build $build, array $options = array()) { $this->phpci = $phpci; diff --git a/PHPCI/Plugin/Shell.php b/PHPCI/Plugin/Shell.php index 83595ad8..d86b6e1b 100644 --- a/PHPCI/Plugin/Shell.php +++ b/PHPCI/Plugin/Shell.php @@ -9,6 +9,9 @@ namespace PHPCI\Plugin; +use PHPCI\Builder; +use PHPCI\Model\Build; + /** * Shell Plugin - Allows execute shell commands. * @author Kinn Coelho Julião @@ -25,7 +28,7 @@ class Shell implements \PHPCI\Plugin */ protected $command; - public function __construct(\PHPCI\Builder $phpci, array $options = array()) + public function __construct(Builder $phpci, Build $build, array $options = array()) { $this->phpci = $phpci; diff --git a/PHPCI/Store/Base/BuildMetaStoreBase.php b/PHPCI/Store/Base/BuildMetaStoreBase.php index b96b3875..01bb6964 100644 --- a/PHPCI/Store/Base/BuildMetaStoreBase.php +++ b/PHPCI/Store/Base/BuildMetaStoreBase.php @@ -6,7 +6,10 @@ namespace PHPCI\Store\Base; +use b8\Database; +use b8\Exception\HttpException; use b8\Store; +use PHPCI\Model\BuildMeta; /** * BuildMeta Base Store @@ -22,21 +25,19 @@ class BuildMetaStoreBase extends Store return $this->getById($value, $useConnection); } - - public function getById($value, $useConnection = 'read') { if (is_null($value)) { - throw new \b8\Exception\HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); + throw new HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); } $query = 'SELECT * FROM build_meta WHERE id = :id LIMIT 1'; - $stmt = \b8\Database::getConnection($useConnection)->prepare($query); + $stmt = Database::getConnection($useConnection)->prepare($query); $stmt->bindValue(':id', $value); if ($stmt->execute()) { if ($data = $stmt->fetch(\PDO::FETCH_ASSOC)) { - return new \PHPCI\Model\BuildMeta($data); + return new BuildMeta($data); } } @@ -46,7 +47,7 @@ class BuildMetaStoreBase extends Store public function getByBuildId($value, $limit = null, $useConnection = 'read') { if (is_null($value)) { - throw new \b8\Exception\HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); + throw new HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); } $add = ''; @@ -55,26 +56,17 @@ class BuildMetaStoreBase extends Store $add .= ' LIMIT ' . $limit; } - $query = 'SELECT COUNT(*) AS cnt FROM build_meta WHERE build_id = :build_id' . $add; - $stmt = \b8\Database::getConnection($useConnection)->prepare($query); - $stmt->bindValue(':build_id', $value); - - if ($stmt->execute()) { - $res = $stmt->fetch(\PDO::FETCH_ASSOC); - $count = (int)$res['cnt']; - } else { - $count = 0; - } + $count = null; $query = 'SELECT * FROM build_meta WHERE build_id = :build_id' . $add; - $stmt = \b8\Database::getConnection('read')->prepare($query); + $stmt = Database::getConnection($useConnection)->prepare($query); $stmt->bindValue(':build_id', $value); if ($stmt->execute()) { $res = $stmt->fetchAll(\PDO::FETCH_ASSOC); $map = function ($item) { - return new \PHPCI\Model\BuildMeta($item); + return new BuildMeta($item); }; $rtn = array_map($map, $res); diff --git a/PHPCI/Store/Base/BuildStoreBase.php b/PHPCI/Store/Base/BuildStoreBase.php index 89b3a4f9..f0e66085 100644 --- a/PHPCI/Store/Base/BuildStoreBase.php +++ b/PHPCI/Store/Base/BuildStoreBase.php @@ -6,7 +6,10 @@ namespace PHPCI\Store\Base; +use b8\Database; +use b8\Exception\HttpException; use b8\Store; +use PHPCI\Model\Build; /** * Build Base Store @@ -22,21 +25,19 @@ class BuildStoreBase extends Store return $this->getById($value, $useConnection); } - - public function getById($value, $useConnection = 'read') { if (is_null($value)) { - throw new \b8\Exception\HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); + throw new HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); } $query = 'SELECT * FROM build WHERE id = :id LIMIT 1'; - $stmt = \b8\Database::getConnection($useConnection)->prepare($query); + $stmt = Database::getConnection($useConnection)->prepare($query); $stmt->bindValue(':id', $value); if ($stmt->execute()) { if ($data = $stmt->fetch(\PDO::FETCH_ASSOC)) { - return new \PHPCI\Model\Build($data); + return new Build($data); } } @@ -46,7 +47,7 @@ class BuildStoreBase extends Store public function getByProjectId($value, $limit = null, $useConnection = 'read') { if (is_null($value)) { - throw new \b8\Exception\HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); + throw new HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); } $add = ''; @@ -55,26 +56,17 @@ class BuildStoreBase extends Store $add .= ' LIMIT ' . $limit; } - $query = 'SELECT COUNT(*) AS cnt FROM build WHERE project_id = :project_id' . $add; - $stmt = \b8\Database::getConnection($useConnection)->prepare($query); - $stmt->bindValue(':project_id', $value); - - if ($stmt->execute()) { - $res = $stmt->fetch(\PDO::FETCH_ASSOC); - $count = (int)$res['cnt']; - } else { - $count = 0; - } + $count = null; $query = 'SELECT * FROM build WHERE project_id = :project_id' . $add; - $stmt = \b8\Database::getConnection('read')->prepare($query); + $stmt = Database::getConnection($useConnection)->prepare($query); $stmt->bindValue(':project_id', $value); if ($stmt->execute()) { $res = $stmt->fetchAll(\PDO::FETCH_ASSOC); $map = function ($item) { - return new \PHPCI\Model\Build($item); + return new Build($item); }; $rtn = array_map($map, $res); @@ -87,7 +79,7 @@ class BuildStoreBase extends Store public function getByStatus($value, $limit = null, $useConnection = 'read') { if (is_null($value)) { - throw new \b8\Exception\HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); + throw new HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); } $add = ''; @@ -96,26 +88,17 @@ class BuildStoreBase extends Store $add .= ' LIMIT ' . $limit; } - $query = 'SELECT COUNT(*) AS cnt FROM build WHERE status = :status' . $add; - $stmt = \b8\Database::getConnection($useConnection)->prepare($query); - $stmt->bindValue(':status', $value); - - if ($stmt->execute()) { - $res = $stmt->fetch(\PDO::FETCH_ASSOC); - $count = (int)$res['cnt']; - } else { - $count = 0; - } + $count = null; $query = 'SELECT * FROM build WHERE status = :status' . $add; - $stmt = \b8\Database::getConnection('read')->prepare($query); + $stmt = Database::getConnection($useConnection)->prepare($query); $stmt->bindValue(':status', $value); if ($stmt->execute()) { $res = $stmt->fetchAll(\PDO::FETCH_ASSOC); $map = function ($item) { - return new \PHPCI\Model\Build($item); + return new Build($item); }; $rtn = array_map($map, $res); diff --git a/PHPCI/Store/Base/ProjectStoreBase.php b/PHPCI/Store/Base/ProjectStoreBase.php index 9a807373..8b78c055 100644 --- a/PHPCI/Store/Base/ProjectStoreBase.php +++ b/PHPCI/Store/Base/ProjectStoreBase.php @@ -6,7 +6,10 @@ namespace PHPCI\Store\Base; +use b8\Database; +use b8\Exception\HttpException; use b8\Store; +use PHPCI\Model\Project; /** * Project Base Store @@ -22,24 +25,54 @@ class ProjectStoreBase extends Store return $this->getById($value, $useConnection); } - - public function getById($value, $useConnection = 'read') { if (is_null($value)) { - throw new \b8\Exception\HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); + throw new HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); } $query = 'SELECT * FROM project WHERE id = :id LIMIT 1'; - $stmt = \b8\Database::getConnection($useConnection)->prepare($query); + $stmt = Database::getConnection($useConnection)->prepare($query); $stmt->bindValue(':id', $value); if ($stmt->execute()) { if ($data = $stmt->fetch(\PDO::FETCH_ASSOC)) { - return new \PHPCI\Model\Project($data); + return new Project($data); } } return null; } + + public function getByTitle($value, $limit = null, $useConnection = 'read') + { + if (is_null($value)) { + throw new HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); + } + + $add = ''; + + if ($limit) { + $add .= ' LIMIT ' . $limit; + } + + $count = null; + + $query = 'SELECT * FROM project WHERE title = :title' . $add; + $stmt = Database::getConnection($useConnection)->prepare($query); + $stmt->bindValue(':title', $value); + + if ($stmt->execute()) { + $res = $stmt->fetchAll(\PDO::FETCH_ASSOC); + + $map = function ($item) { + return new Project($item); + }; + $rtn = array_map($map, $res); + + return array('items' => $rtn, 'count' => $count); + } else { + return array('items' => array(), 'count' => 0); + } + } } diff --git a/PHPCI/Store/Base/UserStoreBase.php b/PHPCI/Store/Base/UserStoreBase.php index 49c0ed35..c4268804 100644 --- a/PHPCI/Store/Base/UserStoreBase.php +++ b/PHPCI/Store/Base/UserStoreBase.php @@ -6,7 +6,10 @@ namespace PHPCI\Store\Base; +use b8\Database; +use b8\Exception\HttpException; use b8\Store; +use PHPCI\Model\User; /** * User Base Store @@ -22,21 +25,19 @@ class UserStoreBase extends Store return $this->getById($value, $useConnection); } - - public function getById($value, $useConnection = 'read') { if (is_null($value)) { - throw new \b8\Exception\HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); + throw new HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); } $query = 'SELECT * FROM user WHERE id = :id LIMIT 1'; - $stmt = \b8\Database::getConnection($useConnection)->prepare($query); + $stmt = Database::getConnection($useConnection)->prepare($query); $stmt->bindValue(':id', $value); if ($stmt->execute()) { if ($data = $stmt->fetch(\PDO::FETCH_ASSOC)) { - return new \PHPCI\Model\User($data); + return new User($data); } } @@ -46,16 +47,16 @@ class UserStoreBase extends Store public function getByEmail($value, $useConnection = 'read') { if (is_null($value)) { - throw new \b8\Exception\HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); + throw new HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); } $query = 'SELECT * FROM user WHERE email = :email LIMIT 1'; - $stmt = \b8\Database::getConnection($useConnection)->prepare($query); + $stmt = Database::getConnection($useConnection)->prepare($query); $stmt->bindValue(':email', $value); if ($stmt->execute()) { if ($data = $stmt->fetch(\PDO::FETCH_ASSOC)) { - return new \PHPCI\Model\User($data); + return new User($data); } } diff --git a/PHPCI/View/Home/index.phtml b/PHPCI/View/Home/index.phtml index 1e186694..452ec7c6 100644 --- a/PHPCI/View/Home/index.phtml +++ b/PHPCI/View/Home/index.phtml @@ -12,31 +12,8 @@
Projects
diff --git a/bootstrap.php b/bootstrap.php index 4db3815e..b84f6428 100644 --- a/bootstrap.php +++ b/bootstrap.php @@ -27,20 +27,22 @@ $autoload = function ($class) { spl_autoload_register($autoload, true, true); -// Define our APPLICATION_PATH, if not already defined: -if (!defined('APPLICATION_PATH')) { - define('APPLICATION_PATH', dirname(__FILE__) . '/'); -} -if (!file_exists(APPLICATION_PATH . 'PHPCI/config.yml')) { - header('Location: install.php'); - die; + +if (!file_exists(dirname(__FILE__) . '/PHPCI/config.yml')) { + if (defined('PHPCI_IS_CONSOLE') && PHPCI_IS_CONSOLE) { + file_put_contents('php://stderr', 'Please install PHPCI with "composer install" before using console'); + exit(1); + } else { + header('Location: install.php'); + die; + } } // Load Composer autoloader: -require_once(APPLICATION_PATH . 'vendor/autoload.php'); +require_once(dirname(__FILE__) . '/vendor/autoload.php'); // Load configuration if present: $conf = array(); @@ -49,9 +51,6 @@ $conf['b8']['app']['default_controller'] = 'Home'; $conf['b8']['view']['path'] = dirname(__FILE__) . '/PHPCI/View/'; $config = new b8\Config($conf); -$config->loadYaml(APPLICATION_PATH . 'PHPCI/config.yml'); +$config->loadYaml(dirname(__FILE__) . '/PHPCI/config.yml'); -// Define our PHPCI_URL, if not already defined: -if (!defined('PHPCI_URL')) { - define('PHPCI_URL', $config->get('phpci.url', '') . '/'); -} +require_once(dirname(__FILE__) . '/vars.php'); diff --git a/console b/console index dd44b13b..3a56e7cd 100755 --- a/console +++ b/console @@ -8,16 +8,7 @@ * @link http://www.phptesting.org/ */ -define('PHPCI_BIN_DIR', dirname(__FILE__) . '/vendor/bin/'); -define('PHPCI_DIR', dirname(__FILE__) . '/'); -define('ENABLE_SHELL_PLUGIN', false); - -// If this is the first time ./console has been run, we probably don't have Composer or any of our dependencies yet. -// So we need to install and run Composer. -if (!file_exists(PHPCI_DIR . 'vendor/autoload.php')) { - file_put_contents('php://stderr', 'Please install PHPCI with "composer install" before using console'); - exit( 1 ); -} +define('PHPCI_IS_CONSOLE', true); require('bootstrap.php'); diff --git a/daemonise b/daemonise index 63ac409a..0f1a6d1d 100755 --- a/daemonise +++ b/daemonise @@ -8,9 +8,7 @@ * @link http://www.phptesting.org/ */ -define('PHPCI_BIN_DIR', dirname(__FILE__) . '/vendor/bin/'); -define('PHPCI_DIR', dirname(__FILE__) . '/'); -define('ENABLE_SHELL_PLUGIN', false); +define('PHPCI_IS_CONSOLE', true); require('bootstrap.php'); diff --git a/phpci.yml b/phpci.yml index 90b34255..42b2dba3 100644 --- a/phpci.yml +++ b/phpci.yml @@ -2,27 +2,20 @@ build_settings: verbose: false ignore: - "vendor" - - "assets" - - "build" - "Tests" - - "composer.phar" + - "PHPCI/Command" # PHPMD complains about un-used parameters, but they are required. + - "public/install.php" # PHPCS really doesn't like PHP mixed with HTML (and so it shouldn't) irc: server: "irc.freenode.net" port: 6667 room: "#phpci" nick: "phpcidev" -setup: - composer: - action: "install" - test: php_mess_detector: - allow_failures: true php_code_sniffer: standard: "PSR2" php_loc: - allow_failures: true success: irc: diff --git a/public/index.php b/public/index.php index e1754b62..134e9a7b 100644 --- a/public/index.php +++ b/public/index.php @@ -16,4 +16,3 @@ require_once('../bootstrap.php'); $fc = new PHPCI\Application($config, new b8\Http\Request()); print $fc->handleRequest(); - diff --git a/public/install.php b/public/install.php index 8003d353..e08e647e 100644 --- a/public/install.php +++ b/public/install.php @@ -1,15 +1,10 @@ -

Important! You need to run composer to install dependencies before running the installer.

+

+ Important! + You need to run composer to install dependencies before running the installer. +

-

Important! ./PHPCI/config.yml needs to be writeable to continue.

+

+ Important! + ./PHPCI/config.yml needs to be writeable to continue. +

@@ -391,30 +401,3 @@ switch ($installStage) {
- -get('phpci.url', '') . '/'); +} + +// Define PHPCI_BIN_DIR +if (!defined('PHPCI_BIN_DIR')) { + define('PHPCI_BIN_DIR', PHPCI_DIR . 'vendor/bin/'); +} + +// Should PHPCI run the Shell plugin? +if (!defined('ENABLE_SHELL_PLUGIN')) { + define('ENABLE_SHELL_PLUGIN', false); +} + +// If this is not already defined, we're not running in the console: +if (!defined('PHPCI_IS_CONSOLE')) { + define('PHPCI_IS_CONSOLE', false); +} \ No newline at end of file From 16003ff01bb7e67edb3d015e269c4de4ba886df0 Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Thu, 10 Oct 2013 01:12:30 +0100 Subject: [PATCH 083/933] Fixing some more PHPCS/PHPMD errors --- PHPCI/Builder.php | 3 ++- PHPCI/Model/Build/GitlabBuild.php | 10 +++++++--- PHPCI/Model/Build/LocalBuild.php | 6 +++--- PHPCI/Plugin/CleanBuild.php | 3 ++- PHPCI/Plugin/CopyBuild.php | 3 ++- PHPCI/Plugin/Irc.php | 2 +- PHPCI/Plugin/PackageBuild.php | 3 ++- PHPCI/Store/BuildMetaStore.php | 6 ++---- vars.php | 2 +- 9 files changed, 22 insertions(+), 16 deletions(-) diff --git a/PHPCI/Builder.php b/PHPCI/Builder.php index 42f98ee1..73486fee 100644 --- a/PHPCI/Builder.php +++ b/PHPCI/Builder.php @@ -141,7 +141,8 @@ class Builder /** * @return string The title of the project being built. */ - public function getBuildProjectTitle() { + public function getBuildProjectTitle() + { return $this->build->getProject()->getTitle(); } diff --git a/PHPCI/Model/Build/GitlabBuild.php b/PHPCI/Model/Build/GitlabBuild.php index 9f456b0a..f3f75ebe 100644 --- a/PHPCI/Model/Build/GitlabBuild.php +++ b/PHPCI/Model/Build/GitlabBuild.php @@ -25,7 +25,8 @@ class GitlabBuild extends RemoteGitBuild */ public function getCommitLink() { - return 'http://'.$this->getProject()->getAccessInformation()["domain"].'/' . $this->getProject()->getReference() . '/commit/' . $this->getCommitId(); + $domain = $this->getProject()->getAccessInformation()["domain"]; + return 'http://' . $domain . '/' . $this->getProject()->getReference() . '/commit/' . $this->getCommitId(); } /** @@ -33,7 +34,8 @@ class GitlabBuild extends RemoteGitBuild */ public function getBranchLink() { - return 'http://'.$this->getProject()->getAccessInformation()["domain"].'/' . $this->getProject()->getReference() . '/tree/' . $this->getBranch(); + $domain = $this->getProject()->getAccessInformation()["domain"]; + return 'http://' . $domain . '/' . $this->getProject()->getReference() . '/tree/' . $this->getBranch(); } /** @@ -44,7 +46,9 @@ class GitlabBuild extends RemoteGitBuild $key = trim($this->getProject()->getGitKey()); if (!empty($key)) { - return $this->getProject()->getAccessInformation()["user"].'@'.$this->getProject()->getAccessInformation()["domain"].':' . $this->getProject()->getReference() . '.git'; + $user = $this->getProject()->getAccessInformation()["user"]; + $domain = $this->getProject()->getAccessInformation()["domain"]; + return $user . '@' . $domain . ':' . $this->getProject()->getReference() . '.git'; } } } diff --git a/PHPCI/Model/Build/LocalBuild.php b/PHPCI/Model/Build/LocalBuild.php index 6804f437..bfb8ac9e 100644 --- a/PHPCI/Model/Build/LocalBuild.php +++ b/PHPCI/Model/Build/LocalBuild.php @@ -25,14 +25,14 @@ class LocalBuild extends Build * Create a working copy by cloning, copying, or similar. */ public function createWorkingCopy(Builder $builder, $buildPath) - { + { $reference = $this->getProject()->getReference(); $reference = substr($reference, -1) == '/' ? substr($reference, 0, -1) : $reference; $buildPath = substr($buildPath, 0, -1); // If there's a /config file in the reference directory, it is probably a bare repository // which we'll extract into our build path directly. - if(is_file($reference.'/config') && $this->handleBareRepository($builder, $reference, $buildPath) === true) { + if (is_file($reference.'/config') && $this->handleBareRepository($builder, $reference, $buildPath) === true) { return true; } @@ -56,7 +56,7 @@ class LocalBuild extends Build $gitConfig = parse_ini_file($reference.'/config', true); // If it is indeed a bare repository, then extract it into our build path: - if($gitConfig['core']['bare']) { + if ($gitConfig['core']['bare']) { $builder->executeCommand('git --git-dir="%s" archive master | tar -x -C "%s"', $reference, $buildPath); return true; } diff --git a/PHPCI/Plugin/CleanBuild.php b/PHPCI/Plugin/CleanBuild.php index bfe96761..a7db4fc9 100644 --- a/PHPCI/Plugin/CleanBuild.php +++ b/PHPCI/Plugin/CleanBuild.php @@ -11,6 +11,7 @@ namespace PHPCI\Plugin; use PHPCI\Builder; use PHPCI\Model\Build; + /** * Clean build removes Composer related files and allows PHPCI users to clean up their build directory. * Useful as a precursor to copy_build. @@ -23,7 +24,7 @@ class CleanBuild implements \PHPCI\Plugin protected $remove; protected $phpci; - public function __construct(Builder $phpci, Build $build, array $options = array()) + public function __construct(Builder $phpci, Build $build, array $options = array()) { $this->phpci = $phpci; $this->remove = isset($options['remove']) && is_array($options['remove']) ? $options['remove'] : array(); diff --git a/PHPCI/Plugin/CopyBuild.php b/PHPCI/Plugin/CopyBuild.php index 1ba06558..494a0d3f 100644 --- a/PHPCI/Plugin/CopyBuild.php +++ b/PHPCI/Plugin/CopyBuild.php @@ -11,6 +11,7 @@ namespace PHPCI\Plugin; use PHPCI\Builder; use PHPCI\Model\Build; + /** * Copy Build Plugin - Copies the entire build to another directory. * @author Dan Cryer @@ -22,7 +23,7 @@ class CopyBuild implements \PHPCI\Plugin protected $directory; protected $phpci; - public function __construct(Builder $phpci, Build $build, array $options = array()) + public function __construct(Builder $phpci, Build $build, array $options = array()) { $path = $phpci->buildPath; $this->phpci = $phpci; diff --git a/PHPCI/Plugin/Irc.php b/PHPCI/Plugin/Irc.php index a4a3310b..6b294d66 100644 --- a/PHPCI/Plugin/Irc.php +++ b/PHPCI/Plugin/Irc.php @@ -55,7 +55,7 @@ class Irc implements \PHPCI\Plugin fputs($sock, 'NICK ' . $this->nick . "\r\n"); fputs($sock, 'PRIVMSG ' . $this->room . ' :' . $msg . "\r\n"); - while ($res = fgets($sock)) { + while (fgets($sock)) { // We don't need to do anything, // but the IRC server doesn't appear to post the message // unless we wait for responses. diff --git a/PHPCI/Plugin/PackageBuild.php b/PHPCI/Plugin/PackageBuild.php index d2332ed3..3036d13b 100644 --- a/PHPCI/Plugin/PackageBuild.php +++ b/PHPCI/Plugin/PackageBuild.php @@ -11,6 +11,7 @@ namespace PHPCI\Plugin; use PHPCI\Builder; use PHPCI\Model\Build; + /** * Create a ZIP or TAR.GZ archive of the entire build. * @author Dan Cryer @@ -24,7 +25,7 @@ class PackageBuild implements \PHPCI\Plugin protected $format; protected $phpci; - public function __construct(Builder $phpci, Build $build, array $options = array()) + public function __construct(Builder $phpci, Build $build, array $options = array()) { $path = $phpci->buildPath; $this->build = $build; diff --git a/PHPCI/Store/BuildMetaStore.php b/PHPCI/Store/BuildMetaStore.php index 7450ac2a..ad17ef93 100644 --- a/PHPCI/Store/BuildMetaStore.php +++ b/PHPCI/Store/BuildMetaStore.php @@ -6,8 +6,6 @@ namespace PHPCI\Store; -require_once(APPLICATION_PATH . 'PHPCI/Store/Base/BuildMetaStoreBase.php'); - use PHPCI\Store\Base\BuildMetaStoreBase; /** @@ -16,5 +14,5 @@ use PHPCI\Store\Base\BuildMetaStoreBase; */ class BuildMetaStore extends BuildMetaStoreBase { - // This class has been left blank so that you can modify it - changes in this file will not be overwritten. -} \ No newline at end of file + // This class has been left blank so that you can modify it - changes in this file will not be overwritten. +} diff --git a/vars.php b/vars.php index eb5557f4..89a1d81e 100644 --- a/vars.php +++ b/vars.php @@ -24,4 +24,4 @@ if (!defined('ENABLE_SHELL_PLUGIN')) { // If this is not already defined, we're not running in the console: if (!defined('PHPCI_IS_CONSOLE')) { define('PHPCI_IS_CONSOLE', false); -} \ No newline at end of file +} From 05be06f9b37aa65a1a591ad864c05419092f2c01 Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Thu, 10 Oct 2013 01:18:05 +0100 Subject: [PATCH 084/933] Fixing some more PHPCS/PHPMD errors --- PHPCI/Model/BuildMeta.php | 4 +--- PHPCI/Store/BuildStore.php | 38 ++++++-------------------------------- 2 files changed, 7 insertions(+), 35 deletions(-) diff --git a/PHPCI/Model/BuildMeta.php b/PHPCI/Model/BuildMeta.php index afe50fb4..caa4b2fa 100644 --- a/PHPCI/Model/BuildMeta.php +++ b/PHPCI/Model/BuildMeta.php @@ -6,8 +6,6 @@ namespace PHPCI\Model; -require_once(APPLICATION_PATH . 'PHPCI/Model/Base/BuildMetaBase.php'); - use PHPCI\Model\Base\BuildMetaBase; /** @@ -16,5 +14,5 @@ use PHPCI\Model\Base\BuildMetaBase; */ class BuildMeta extends BuildMetaBase { - // This class has been left blank so that you can modify it - changes in this file will not be overwritten. + // This class has been left blank so that you can modify it - changes in this file will not be overwritten. } diff --git a/PHPCI/Store/BuildStore.php b/PHPCI/Store/BuildStore.php index 13bb849e..e419c9cc 100644 --- a/PHPCI/Store/BuildStore.php +++ b/PHPCI/Store/BuildStore.php @@ -39,39 +39,12 @@ class BuildStore extends BuildStoreBase } } - public function getBuildSummary() - { - $query = 'SELECT COUNT(*) AS cnt FROM build b LEFT JOIN project p on p.id = b.project_id GROUP BY b.project_id ORDER BY p.title ASC, b.id DESC'; - $stmt = \b8\Database::getConnection('read')->prepare($query); - - if ($stmt->execute()) { - $res = $stmt->fetch(\PDO::FETCH_ASSOC); - $count = (int)$res['cnt']; - } else { - $count = 0; - } - - $query = 'SELECT b.* FROM build b LEFT JOIN project p on p.id = b.project_id ORDER BY p.title ASC, b.id DESC'; - $stmt = \b8\Database::getConnection('read')->prepare($query); - - if ($stmt->execute()) { - $res = $stmt->fetchAll(\PDO::FETCH_ASSOC); - - $map = function ($item) { - return new \PHPCI\Model\Build($item); - }; - $rtn = array_map($map, $res); - - return array('items' => $rtn, 'count' => $count); - } else { - return array('items' => array(), 'count' => 0); - } - } - public function getMeta($key, $projectId, $buildId = null, $numResults = 1) { + $select = '`build_id`, `meta_key`, `meta_value`'; $and = $numResults > 1 ? ' AND (`build_id` <= :buildId) ' : ' AND (`build_id` = :buildId) '; - $query = 'SELECT `build_id`, `meta_key`, `meta_value` FROM `build_meta` WHERE `meta_key` = :key AND `project_id` = :projectId ' . $and . ' ORDER BY id DESC LIMIT :numResults'; + $where = '`meta_key` = :key AND `project_id` = :projectId ' . $and; + $query = 'SELECT '.$select.' FROM `build_meta` WHERE '.$where.' ORDER BY id DESC LIMIT :numResults'; $stmt = \b8\Database::getConnection('read')->prepare($query); $stmt->bindValue(':key', $key, \PDO::PARAM_STR); @@ -83,7 +56,7 @@ class BuildStore extends BuildStoreBase $rtn = $stmt->fetchAll(\PDO::FETCH_ASSOC); $rtn = array_reverse($rtn); - $rtn = array_map(function($item) { + $rtn = array_map(function ($item) { $item['meta_value'] = json_decode($item['meta_value'], true); return $item; }, $rtn); @@ -101,7 +74,8 @@ class BuildStore extends BuildStoreBase public function setMeta($projectId, $buildId, $key, $value) { - $query = 'REPLACE INTO build_meta (project_id, build_id, `meta_key`, `meta_value`) VALUES (:projectId, :buildId, :key, :value)'; + $cols = '`project_id`, `build_id`, `meta_key`, `meta_value`'; + $query = 'REPLACE INTO build_meta ('.$cols.') VALUES (:projectId, :buildId, :key, :value)'; $stmt = \b8\Database::getConnection('read')->prepare($query); $stmt->bindValue(':key', $key, \PDO::PARAM_STR); From a5734fb330af66a13bce5fc9b4bc5a9b3a0b1a90 Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Thu, 10 Oct 2013 01:19:42 +0100 Subject: [PATCH 085/933] Fixing some more PHPCS/PHPMD errors --- PHPCI/Model/Build/GithubBuild.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/PHPCI/Model/Build/GithubBuild.php b/PHPCI/Model/Build/GithubBuild.php index 6518da92..24ea63f7 100644 --- a/PHPCI/Model/Build/GithubBuild.php +++ b/PHPCI/Model/Build/GithubBuild.php @@ -76,7 +76,9 @@ class GithubBuild extends RemoteGitBuild ); $http->setHeaders($headers); - $http->request('POST', $url, json_encode($params)); + $res = $http->request('POST', $url, json_encode($params)); + + } /** From d46ea12dd73e3a84e8422045c22da3d87bc3e643 Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Thu, 10 Oct 2013 01:26:06 +0100 Subject: [PATCH 086/933] Fixing summary table so it doesn't completely die when there is a running build. --- PHPCI/Model/Build/GithubBuild.php | 2 +- PHPCI/View/SummaryTable.phtml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/PHPCI/Model/Build/GithubBuild.php b/PHPCI/Model/Build/GithubBuild.php index 24ea63f7..f208c568 100644 --- a/PHPCI/Model/Build/GithubBuild.php +++ b/PHPCI/Model/Build/GithubBuild.php @@ -78,7 +78,7 @@ class GithubBuild extends RemoteGitBuild $http->setHeaders($headers); $res = $http->request('POST', $url, json_encode($params)); - + } /** diff --git a/PHPCI/View/SummaryTable.phtml b/PHPCI/View/SummaryTable.phtml index 3d31ac09..087b5cef 100644 --- a/PHPCI/View/SummaryTable.phtml +++ b/PHPCI/View/SummaryTable.phtml @@ -53,12 +53,12 @@ foreach($projects as $project): case 2: $successes++; $statuses[] = 'ok'; - $success = is_null($success) ? $build->getFinished()->format('Y-m-d H:i:s') : $success; + $success = is_null($success) && !is_null($build->getFinished()) ? $build->getFinished()->format('Y-m-d H:i:s') : $success; break; case 3: $failures++; $statuses[] = 'failed'; - $failure = is_null($failure) ? $build->getFinished()->format('Y-m-d H:i:s') : $failure; + $failure = is_null($failure) && !is_null($build->getFinished()) ? $build->getFinished()->format('Y-m-d H:i:s') : $failure; break; } } From 5213a00a3a397347f2063ef6751861f31ed9b7a2 Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Thu, 10 Oct 2013 01:27:43 +0100 Subject: [PATCH 087/933] Adding some github debug stuff --- PHPCI/Model/Build/GithubBuild.php | 1 + 1 file changed, 1 insertion(+) diff --git a/PHPCI/Model/Build/GithubBuild.php b/PHPCI/Model/Build/GithubBuild.php index f208c568..b77dde8a 100644 --- a/PHPCI/Model/Build/GithubBuild.php +++ b/PHPCI/Model/Build/GithubBuild.php @@ -78,6 +78,7 @@ class GithubBuild extends RemoteGitBuild $http->setHeaders($headers); $res = $http->request('POST', $url, json_encode($params)); + var_dump($res); } From 13dbcc4260192531c1ce883bc9ee227afdd1f28c Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Thu, 10 Oct 2013 01:30:40 +0100 Subject: [PATCH 088/933] Fixing Github postbacks. --- PHPCI/Model/Build/GithubBuild.php | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/PHPCI/Model/Build/GithubBuild.php b/PHPCI/Model/Build/GithubBuild.php index b77dde8a..1b41fb75 100644 --- a/PHPCI/Model/Build/GithubBuild.php +++ b/PHPCI/Model/Build/GithubBuild.php @@ -67,19 +67,16 @@ class GithubBuild extends RemoteGitBuild break; } - $url = \b8\Config::getInstance()->get('phpci.url'); + $phpciUrl = \b8\Config::getInstance()->get('phpci.url'); $params = array( 'state' => $status, - 'target_url' => $url . '/build/view/' . $this->getId()); + 'target_url' => $phpciUrl . '/build/view/' . $this->getId()); $headers = array( 'Authorization: token ' . $project->getToken(), 'Content-Type: application/x-www-form-urlencoded' ); $http->setHeaders($headers); - $res = $http->request('POST', $url, json_encode($params)); - - var_dump($res); - + $http->request('POST', $url, json_encode($params)); } /** From cc86e85adb6224cc7d9b5778d79bddf3656a8671 Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Thu, 10 Oct 2013 01:38:32 +0100 Subject: [PATCH 089/933] Fixing Github Controller to ignore all-zeroes commit IDs --- PHPCI/Controller/GithubController.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/PHPCI/Controller/GithubController.php b/PHPCI/Controller/GithubController.php index d59333b9..b616cbb4 100644 --- a/PHPCI/Controller/GithubController.php +++ b/PHPCI/Controller/GithubController.php @@ -38,6 +38,12 @@ class GithubController extends \PHPCI\Controller { $payload = json_decode($this->getParam('payload'), true); + // Github sends a payload when you close a pull request with a + // non-existant commit. We don't want this. + if ($payload['after'] === '0000000000000000000000000000000000000000') { + die('OK'); + } + try { $build = new Build(); $build->setProjectId($project); From 2a47b08fec67ebd84048a946500a7674446f39ec Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Fri, 11 Oct 2013 21:51:23 +0100 Subject: [PATCH 090/933] Removed Github configuration from ProjectController / ProjectForm and added it to a new settings area. --- PHPCI/Controller/PluginController.php | 12 ++ PHPCI/Controller/ProjectController.php | 107 +++++++++--------- PHPCI/Controller/SettingsController.php | 140 ++++++++++++++++++++++++ PHPCI/Model/Build/GithubBuild.php | 9 +- PHPCI/View/Home/index.phtml | 2 - PHPCI/View/Plugin/index.phtml | 3 - PHPCI/View/Settings/index.phtml | 62 +++++++++++ PHPCI/View/layout.phtml | 27 ++++- public/assets/css/phpci.css | 24 ++-- public/assets/js/phpci.js | 32 ++++-- 10 files changed, 331 insertions(+), 87 deletions(-) create mode 100644 PHPCI/Controller/SettingsController.php create mode 100644 PHPCI/View/Settings/index.phtml diff --git a/PHPCI/Controller/PluginController.php b/PHPCI/Controller/PluginController.php index 0dd3f2b4..22b25bfc 100644 --- a/PHPCI/Controller/PluginController.php +++ b/PHPCI/Controller/PluginController.php @@ -47,6 +47,10 @@ class PluginController extends \PHPCI\Controller public function index() { + if (!$_SESSION['user']->getIsAdmin()) { + throw new \Exception('You do not have permission to do that.'); + } + $this->view->canWrite = is_writable(APPLICATION_PATH . 'composer.json'); $this->view->canInstall = $this->canInstall; $this->view->required = $this->required; @@ -60,6 +64,10 @@ class PluginController extends \PHPCI\Controller public function remove() { + if (!$_SESSION['user']->getIsAdmin()) { + throw new \Exception('You do not have permission to do that.'); + } + $package = $this->getParam('package', null); $json = $this->getComposerJson(); @@ -81,6 +89,10 @@ class PluginController extends \PHPCI\Controller public function install() { + if (!$_SESSION['user']->getIsAdmin()) { + throw new \Exception('You do not have permission to do that.'); + } + $package = $this->getParam('package', null); $version = $this->getParam('version', '*'); diff --git a/PHPCI/Controller/ProjectController.php b/PHPCI/Controller/ProjectController.php index db684051..1a4fc496 100644 --- a/PHPCI/Controller/ProjectController.php +++ b/PHPCI/Controller/ProjectController.php @@ -12,6 +12,7 @@ namespace PHPCI\Controller; use PHPCI\Model\Build; use PHPCI\Model\Project; use b8; +use b8\Config; use b8\Controller; use b8\Store; use b8\Form; @@ -87,7 +88,7 @@ class ProjectController extends \PHPCI\Controller throw new \Exception('You do not have permission to do that.'); } - $project = $this->projectStore->getById($projectId); + $project = $this->projectStore->getById($projectId); $this->projectStore->delete($project); header('Location: '.PHPCI_URL); @@ -127,7 +128,6 @@ class ProjectController extends \PHPCI\Controller } $method = $this->request->getMethod(); - $this->handleGithubResponse(); if ($method == 'POST') { $values = $this->getParams(); @@ -154,10 +154,10 @@ class ProjectController extends \PHPCI\Controller $pub = file_get_contents($keyFile . '.pub'); $prv = file_get_contents($keyFile); - $values = array('key' => $prv, 'pubkey' => $pub, 'token' => $_SESSION['github_token']); + $values = array('key' => $prv, 'pubkey' => $pub); } - $form = $this->projectForm($values); + $form = $this->projectForm($values); if ($method != 'POST' || ($method == 'POST' && !$form->validate())) { $view = new b8\View('ProjectForm'); @@ -165,12 +165,11 @@ class ProjectController extends \PHPCI\Controller $view->project = null; $view->form = $form; $view->key = $pub; - $view->token = $_SESSION['github_token']; return $view->render(); } - $values = $form->getValues(); + $values = $form->getValues(); if ($values['type'] == "gitlab") { preg_match('`^(.*)@(.*):(.*)/(.*)\.git`', $values['reference'], $matches); @@ -192,33 +191,6 @@ class ProjectController extends \PHPCI\Controller die; } - /** - * Handles log in with Github - */ - protected function handleGithubResponse() - { - $github = \b8\Config::getInstance()->get('phpci.github'); - $code = $this->getParam('code', null); - - if (!is_null($code)) { - $http = new \b8\HttpClient(); - $url = 'https://github.com/login/oauth/access_token'; - $params = array('client_id' => $github['id'], 'client_secret' => $github['secret'], 'code' => $code); - $resp = $http->post($url, $params); - - if ($resp['success']) { - parse_str($resp['body'], $resp); - $_SESSION['github_token'] = $resp['access_token']; - header('Location: '.PHPCI_URL.'project/add'); - die; - } - } - - if (!isset($_SESSION['github_token'])) { - $_SESSION['github_token'] = null; - } - } - /** * Edit a project. Handles both the form and processing. */ @@ -236,6 +208,7 @@ class ProjectController extends \PHPCI\Controller } else { $values = $project->getDataArray(); $values['key'] = $values['git_key']; + if ($values['type'] == "gitlab") { $accessInfo = $project->getAccessInformation(); $reference = $accessInfo["user"].'@'.$accessInfo["domain"].':' . $project->getReference().".git"; @@ -284,7 +257,6 @@ class ProjectController extends \PHPCI\Controller $form->setMethod('POST'); $form->setAction(PHPCI_URL.'project/' . $type); $form->addField(new Form\Element\Csrf('csrf')); - $form->addField(new Form\Element\Hidden('token')); $form->addField(new Form\Element\Hidden('pubkey')); $options = array( @@ -306,14 +278,16 @@ class ProjectController extends \PHPCI\Controller $field->setContainerClass('form-group'); $form->addField($field); - if (isset($_SESSION['github_token'])) { - $field = new Form\Element\Select('github'); - $field->setLabel('Choose a Github repository:'); - $field->setClass('form-control'); - $field->setContainerClass('form-group'); - $field->setOptions($this->getGithubRepositories()); - $form->addField($field); - } + + $container = new Form\ControlGroup('github-container'); + $container->setClass('github-container'); + + $field = new Form\Element\Select('github'); + $field->setLabel('Choose a Github repository:'); + $field->setClass('form-control'); + $field->setContainerClass('form-group'); + $container->addField($field); + $form->addField($container); $field = new Form\Element\Text('reference'); $field->setRequired(true); @@ -351,21 +325,48 @@ class ProjectController extends \PHPCI\Controller /** * Get an array of repositories from Github's API. */ - protected function getGithubRepositories() + protected function githubRepositories() { - $http = new \b8\HttpClient(); - $url = 'https://api.github.com/user/repos'; - $res = $http->get($url, array('type' => 'all', 'access_token' => $_SESSION['github_token'])); + $token = Config::getInstance()->get('phpci.github.token'); - $rtn = array(); - $rtn['choose'] = 'Select a repository...'; - if ($res['success']) { - foreach ($res['body'] as $repo) { - $rtn[$repo['full_name']] = $repo['full_name']; - } + if (!$token) { + die(json_encode(null)); } - return $rtn; + $cache = \b8\Cache::getCache(\b8\Cache::TYPE_APC); + $rtn = $cache->get('phpci_github_repos'); + + if (!$rtn) { + $orgs = $this->doGithubApiRequest('/user/orgs', array('access_token' => $token)); + + $params = array('type' => 'all', 'access_token' => $token); + $repos = array(); + $repos['user'] = $this->doGithubApiRequest('/user/repos', $params); + + + foreach ($orgs as $org) { + $repos[$org['login']] = $this->doGithubApiRequest('/orgs/'.$org['login'].'/repos', $params); + } + + $rtn = array(); + foreach ($repos as $repoGroup) { + foreach ($repoGroup as $repo) { + $rtn['repos'][] = $repo['full_name']; + } + } + + $cache->set('phpci_github_repos', $rtn); + } + + die(json_encode($rtn)); + } + + protected function doGithubApiRequest($url, $params) + { + $http = new \b8\HttpClient('https://api.github.com'); + $res = $http->get($url, $params); + + return $res['body']; } protected function getReferenceValidator($values) diff --git a/PHPCI/Controller/SettingsController.php b/PHPCI/Controller/SettingsController.php new file mode 100644 index 00000000..d0582f6a --- /dev/null +++ b/PHPCI/Controller/SettingsController.php @@ -0,0 +1,140 @@ + + * @package PHPCI + * @subpackage Web + */ +class SettingsController extends Controller +{ + protected $settings; + + public function init() + { + parent::init(); + + $parser = new Parser(); + $yaml = file_get_contents(APPLICATION_PATH . 'PHPCI/config.yml'); + $this->settings = $parser->parse($yaml); + } + + public function index() + { + $this->view->settings = $this->settings; + $this->view->github = $this->getGithubForm(); + + if (!empty($this->settings['phpci']['github']['token'])) { + $this->view->githubUser = $this->getGithubUser($this->settings['phpci']['github']['token']); + } + + return $this->view->render(); + } + + public function github() + { + $this->settings['phpci']['github']['id'] = $this->getParam('githubid', ''); + $this->settings['phpci']['github']['secret'] = $this->getParam('githubsecret', ''); + + $this->storeSettings(); + + header('Location: ' . PHPCI_URL . 'settings?saved=1'); + die; + } + + /** + * Github redirects users back to this URL when t + */ + public function githubCallback() + { + $code = $this->getParam('code', null); + $github = $this->settings['phpci']['github']; + + if (!is_null($code)) { + $http = new HttpClient(); + $url = 'https://github.com/login/oauth/access_token'; + $params = array('client_id' => $github['id'], 'client_secret' => $github['secret'], 'code' => $code); + $resp = $http->post($url, $params); + + if ($resp['success']) { + parse_str($resp['body'], $resp); + + $this->settings['phpci']['github']['token'] = $resp['access_token']; + $this->storeSettings(); + + header('Location: ' . PHPCI_URL . 'settings?linked=1'); + die; + } + } + + + header('Location: ' . PHPCI_URL . 'settings?linked=2'); + die; + } + + protected function storeSettings() + { + $dumper = new Dumper(); + $yaml = $dumper->dump($this->settings); + file_put_contents(APPLICATION_PATH . 'PHPCI/config.yml', $yaml); + } + + protected function getGithubForm() + { + $form = new Form(); + $form->setMethod('POST'); + $form->setAction(PHPCI_URL . 'settings/github'); + $form->addField(new Form\Element\Csrf('csrf')); + + $field = new Form\Element\Text('githubid'); + $field->setRequired(true); + $field->setPattern('[a-zA-Z0-9]+'); + $field->setLabel('Application ID'); + $field->setClass('form-control'); + $field->setContainerClass('form-group'); + $field->setValue($this->settings['phpci']['github']['id']); + $form->addField($field); + + $field = new Form\Element\Text('githubsecret'); + $field->setRequired(true); + $field->setPattern('[a-zA-Z0-9]+'); + $field->setLabel('Application Secret'); + $field->setClass('form-control'); + $field->setContainerClass('form-group'); + $field->setValue($this->settings['phpci']['github']['secret']); + $form->addField($field); + + + $field = new Form\Element\Submit(); + $field->setValue('Save »'); + $field->setClass('btn btn-success pull-right'); + $form->addField($field); + + return $form; + } + + protected function getGithubUser($token) + { + $http = new HttpClient('https://api.github.com'); + $user = $http->get('/user', array('access_token' => $token)); + + return $user['body']; + } +} diff --git a/PHPCI/Model/Build/GithubBuild.php b/PHPCI/Model/Build/GithubBuild.php index 1b41fb75..98d7a8eb 100644 --- a/PHPCI/Model/Build/GithubBuild.php +++ b/PHPCI/Model/Build/GithubBuild.php @@ -40,13 +40,14 @@ class GithubBuild extends RemoteGitBuild */ public function sendStatusPostback() { - $project = $this->getProject(); + $token = \b8\Config::getInstance()->get('phpci.github.token'); - // The postback will only work if we have an access token. - if (!$project->getToken()) { + if (empty($token)) { return; } + $project = $this->getProject(); + $url = 'https://api.github.com/repos/'.$project->getReference().'/statuses/'.$this->getCommitId(); $http = new \b8\HttpClient(); @@ -71,7 +72,7 @@ class GithubBuild extends RemoteGitBuild $params = array( 'state' => $status, 'target_url' => $phpciUrl . '/build/view/' . $this->getId()); $headers = array( - 'Authorization: token ' . $project->getToken(), + 'Authorization: token ' . $token, 'Content-Type: application/x-www-form-urlencoded' ); diff --git a/PHPCI/View/Home/index.phtml b/PHPCI/View/Home/index.phtml index 452ec7c6..d372786d 100644 --- a/PHPCI/View/Home/index.phtml +++ b/PHPCI/View/Home/index.phtml @@ -5,8 +5,6 @@
diff --git a/PHPCI/View/Plugin/index.phtml b/PHPCI/View/Plugin/index.phtml index a21c4a35..f3f10e60 100644 --- a/PHPCI/View/Plugin/index.phtml +++ b/PHPCI/View/Plugin/index.phtml @@ -175,6 +175,3 @@
- - -
Loading...
\ No newline at end of file diff --git a/PHPCI/View/Settings/index.phtml b/PHPCI/View/Settings/index.phtml new file mode 100644 index 00000000..c8206811 --- /dev/null +++ b/PHPCI/View/Settings/index.phtml @@ -0,0 +1,62 @@ + +

+ Your settings have been saved. +

+ + + +

+ Your Github account has been linked. +

+ + + +

+ Your Github account could not be linked. +

+ + +
+
+
+

Github Application

+ + + +

+ Before you can start using Github, you need to sign in and grant PHPCI access to your account. +

+ + + +

+ PHPCI is successfully linked to Github account + + + +

+ +
+ +
+ +
+ +
+
+
+

Where to find these...

+
+ +
+

If you own the application you would like to use, you can find this information within your + applications settings area.

+
+
+
+
+
diff --git a/PHPCI/View/layout.phtml b/PHPCI/View/layout.phtml index 3b3c2dc9..da33cd3c 100644 --- a/PHPCI/View/layout.phtml +++ b/PHPCI/View/layout.phtml @@ -6,7 +6,7 @@ - + @@ -36,12 +36,25 @@ @@ -52,5 +65,7 @@
+ +
Loading...
diff --git a/public/assets/css/phpci.css b/public/assets/css/phpci.css index fce6ee20..ce34fe82 100644 --- a/public/assets/css/phpci.css +++ b/public/assets/css/phpci.css @@ -7,6 +7,14 @@ body padding-top: 70px; } + strong, th, .control-label { + font-weight: 500; + } + + .btn, .dropdown-menu>li>a, .controls, .controls input, .controls label { + font-weight: 300; + } + #content { -border: 10px solid #369; @@ -61,6 +69,11 @@ td .label { padding: 0; } + h2 { + color: #246; + font-size: 1.8em; + } + .icon-build-ok { background: url('../img/icon-build-ok.png') no-repeat top left; @@ -81,13 +94,6 @@ td .label { background: url('../img/icon-build-running.png') no-repeat top left; } -h3 -{ - border-bottom: 1px solid #f0f0f0; - margin-top: 0; - padding-top: 0; -} - .navbar-brand { padding: 10px 15px; } @@ -102,7 +108,9 @@ h3 border-bottom: 1px solid #eee; cursor: move; font-size: 1.2em; - padding: 8px; + margin: 0; + margin-bottom: 20px; + padding: 8px 0; } .box .box-content { diff --git a/public/assets/js/phpci.js b/public/assets/js/phpci.js index c14c41bc..d662825b 100644 --- a/public/assets/js/phpci.js +++ b/public/assets/js/phpci.js @@ -18,6 +18,8 @@ function confirmDelete(url) */ function setupProjectForm() { + $('.github-container').hide(); + $('#element-reference').change(function() { var el = $(this); @@ -41,18 +43,26 @@ function setupProjectForm() $('#element-type').change(function() { - if(!window.github_app_id || $(this).val() != 'github' || window.github_token) { - return; - } - - // Show sign in with Github button. - var el = $('#element-reference'); - var rtn = window.return_url; - var url = 'https://github.com/login/oauth/authorize?client_id=' + window.github_app_id + '&scope=repo&redirect_uri=' + rtn; - var btn = $('').addClass('btn btn-inverse').text('Sign in with Github').attr('href', url); + if ($(this).val() == 'github') { + $('#loading').show(); - el.after(btn); - el.remove(); + $.getJSON(window.PHPCI_URL + 'project/github-repositories', function (data) { + $('#loading').hide(); + + if (data.repos) { + $('#element-github').empty(); + + for (var i in data.repos) { + var name = data.repos[i]; + $('#element-github').append($('').text(name).val(name)); + } + + $('.github-container').slideDown(); + } + }); + } else { + $('.github-container').slideUp(); + } }); $('#element-github').change(function() From 1e51e51596e01923583c22e25e5c47035074f4fd Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Tue, 15 Oct 2013 14:14:52 +0100 Subject: [PATCH 091/933] Fix checks in bootstrap.php for missing files, fixes #166, fixes #167, closes #168 --- bootstrap.php | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/bootstrap.php b/bootstrap.php index b84f6428..17aaf608 100644 --- a/bootstrap.php +++ b/bootstrap.php @@ -27,17 +27,14 @@ $autoload = function ($class) { spl_autoload_register($autoload, true, true); +if (!file_exists(dirname(__FILE__) . '/PHPCI/config.yml') && (!defined('PHPCI_IS_CONSOLE') || !PHPCI_IS_CONSOLE)) { + header('Location: install.php'); + die; +} - - -if (!file_exists(dirname(__FILE__) . '/PHPCI/config.yml')) { - if (defined('PHPCI_IS_CONSOLE') && PHPCI_IS_CONSOLE) { - file_put_contents('php://stderr', 'Please install PHPCI with "composer install" before using console'); - exit(1); - } else { - header('Location: install.php'); - die; - } +if (!file_exists(dirname(__FILE__) . '/vendor/autoload.php') && defined('PHPCI_IS_CONSOLE') && PHPCI_IS_CONSOLE) { + file_put_contents('php://stderr', 'Please install PHPCI with "composer install" before using console'); + exit(1); } From 53993a1add01e2945ae64de787b0634f0c0a10e9 Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Tue, 15 Oct 2013 14:29:23 +0100 Subject: [PATCH 092/933] Fixing callable error in run builds, fixes #170 --- PHPCI/Builder.php | 2 +- PHPCI/Command/RunCommand.php | 13 +++---------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/PHPCI/Builder.php b/PHPCI/Builder.php index 73486fee..abf5b477 100644 --- a/PHPCI/Builder.php +++ b/PHPCI/Builder.php @@ -97,7 +97,7 @@ class Builder * @param \PHPCI\Model\Build * @param callable */ - public function __construct(Build $build, callable $logCallback) + public function __construct(Build $build, Closure $logCallback) { $this->build = $build; $this->store = Store\Factory::getStore('Build'); diff --git a/PHPCI/Command/RunCommand.php b/PHPCI/Command/RunCommand.php index 94d191d3..dc8e21d2 100644 --- a/PHPCI/Command/RunCommand.php +++ b/PHPCI/Command/RunCommand.php @@ -50,7 +50,9 @@ class RunCommand extends Command $build = BuildFactory::getBuild($build); if ($input->getOption('verbose')) { - $builder = new Builder($build, array($this, 'logCallback')); + $builder = new Builder($build, function ($log) { + $this->output->writeln($log); + }); } else { $builder = new Builder($build, function () { // Empty stub function. @@ -62,13 +64,4 @@ class RunCommand extends Command return $builds; } - - /** - * Called when log entries are made in Builder / the plugins. - * @see \PHPCI\Builder::log() - */ - public function logCallback($log) - { - $this->output->writeln($log); - } } From b197a9c030842288c95e116789a97770f85bf7fa Mon Sep 17 00:00:00 2001 From: Mathieu Dumoulin Date: Tue, 15 Oct 2013 09:34:24 -0400 Subject: [PATCH 093/933] Added leading slash to closure to prevent reference to PHPCI namespace Don't forget to put \ in front of PHP namespaced objects or you end up in your own namespace. In reference to commit: https://github.com/Block8/PHPCI/commit/53993a1add01e2945ae64de787b0634f0c0a10e9 --- PHPCI/Builder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PHPCI/Builder.php b/PHPCI/Builder.php index abf5b477..6fa20be0 100644 --- a/PHPCI/Builder.php +++ b/PHPCI/Builder.php @@ -97,7 +97,7 @@ class Builder * @param \PHPCI\Model\Build * @param callable */ - public function __construct(Build $build, Closure $logCallback) + public function __construct(Build $build, \Closure $logCallback) { $this->build = $build; $this->store = Store\Factory::getStore('Build'); From e1207738038db7927504071c9e2b854d5b928497 Mon Sep 17 00:00:00 2001 From: Allister Antosik Date: Tue, 15 Oct 2013 19:19:44 +0100 Subject: [PATCH 094/933] Added warning message if install.php is detected If the file install.php is detected an alert-danger message is displayed on all pages. --- PHPCI/View/layout.phtml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/PHPCI/View/layout.phtml b/PHPCI/View/layout.phtml index da33cd3c..85313f1f 100644 --- a/PHPCI/View/layout.phtml +++ b/PHPCI/View/layout.phtml @@ -63,6 +63,14 @@
+ + +
+ + Warning! install.php detected, for security purposes please delete it immediately. +
+ +
From 69eb5a61044e8cf93400d73741b257a79f6a21b7 Mon Sep 17 00:00:00 2001 From: Taylor Blau Date: Tue, 15 Oct 2013 21:40:27 -0400 Subject: [PATCH 095/933] Added YML formatting to phpci.yml in README.md --- README.md | 99 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 51 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index f1233c2d..bf640b6e 100644 --- a/README.md +++ b/README.md @@ -64,10 +64,11 @@ _**Please be aware that PHPCI is a beta-release project, so whilst it is very st **Nginx Example**: - - location / { - try_files $uri $uri/ index.php - } +``` +location / { + try_files $uri $uri/ index.php +} +``` Finally, you'll want to set up PHPCI to run as a regular cronjob, so run `crontab -e` and enter the following: @@ -78,50 +79,52 @@ Obviously, make sure you change the `/path/to/phpci` to the directory in which y ##Adding support for PHPCI to your projects: Similar to Travis CI, to support PHPCI in your project, you simply need to add a `phpci.yml` file to the root of your repository. The file should look something like this: - build_settings: - ignore: - - "vendor" - - "tests" - mysql: - host: "localhost" - user: "root" - pass: "" - campfire: - url: "https://youraccount.campfirenow.com" - authToken: "605b32dd" - roomId: "570102" - setup: - mysql: - - "DROP DATABASE IF EXISTS test;" - - "CREATE DATABASE test;" - - "GRANT ALL PRIVILEGES ON test.* TO test@'localhost' IDENTIFIED BY 'test';" - composer: - action: "install" - - test: - php_unit: - config: - - "PHPUnit-all.xml" - - "PHPUnit-ubuntu-fix.xml" - directory: - - "tests/" - run_from: "phpunit/" - php_mess_detector: - allow_failures: true - php_code_sniffer: - standard: "PSR2" - php_cpd: - allow_failures: true - grunt: - task: "build" - - complete: - mysql: - - "DROP DATABASE IF EXISTS test;" - failure: - campfire: - message: "Phpci : build %buildurl% failed." - +```yml +build_settings: + ignore: + - "vendor" + - "tests" + mysql: + host: "localhost" + user: "root" + pass: "" + campfire: + url: "https://youraccount.campfirenow.com" + authToken: "605b32dd" + roomId: "570102" +setup: + mysql: + - "DROP DATABASE IF EXISTS test;" + - "CREATE DATABASE test;" + - "GRANT ALL PRIVILEGES ON test.* TO test@'localhost' IDENTIFIED BY 'test';" + composer: + action: "install" + +test: + php_unit: + config: + - "PHPUnit-all.xml" + - "PHPUnit-ubuntu-fix.xml" + directory: + - "tests/" + run_from: "phpunit/" + php_mess_detector: + allow_failures: true + php_code_sniffer: + standard: "PSR2" + php_cpd: + allow_failures: true + grunt: + task: "build" + +complete: + mysql: + - "DROP DATABASE IF EXISTS test;" +failure: + campfire: + message: "Phpci : build %buildurl% failed." +``` + As mentioned earlier, PHPCI is powered by plugins, there are several phases in which plugins can be run: * `setup` - This phase is designed to initialise the build procedure. From 7b3eea7cbbd87d0c636bd4f9bbbcf5842cdc44b2 Mon Sep 17 00:00:00 2001 From: Pavel Pavlov Date: Thu, 17 Oct 2013 02:12:42 +0400 Subject: [PATCH 096/933] Added Phing plugin --- PHPCI/Plugin/Phing.php | 232 +++++++++++++++++++++++++++++++++++++++++ README.md | 9 ++ 2 files changed, 241 insertions(+) create mode 100644 PHPCI/Plugin/Phing.php diff --git a/PHPCI/Plugin/Phing.php b/PHPCI/Plugin/Phing.php new file mode 100644 index 00000000..f9426235 --- /dev/null +++ b/PHPCI/Plugin/Phing.php @@ -0,0 +1,232 @@ + + * @package PHPCI + * @subpackage Plugins + */ +class Phing implements \PHPCI\Plugin +{ + + private $directory; + private $buildFile; + private $targets; + private $properties; + private $propertyFile; + + protected $phpci; + + public function __construct(Builder $phpci, Build $build, array $options = array()) + { + $this + ->setDirectory( + isset($options['directory']) ? $phpci->buildPath . '/' . $options['directory'] : $phpci->buildPath + ) + ->setPhpci($phpci) + ->setBuildFile(isset($options['build_file']) ? $options['build_file'] : 'build.xml') + ->setTargets(isset($options['targets']) ? $options['targets'] : 'build') + ->setProperties(isset($options['properties']) ? $options['properties'] : []); + + if (isset($options['property_file'])) { + $this->setPropertyFile($options['property_file']); + } + } + + /** + * Executes Phing and runs a specified targets + */ + public function execute() + { + $phingExecutable = $this->phpci->findBinary('phing'); + + if (!$phingExecutable) { + $this->phpci->logFailure('Could not find Phing executable.'); + return false; + } + +// $cmd[] = 'cd ' . $this->getDirectory() . ' &&'; + $cmd[] = $phingExecutable . ' -f ' . $this->getBuildFile(); + + if ($this->getPropertyFile()) { + $cmd[] = '-propertyfile ' . $this->getPropertyFile(); + } + + $cmd[] = $this->propertiesToString(); + + $cmd[] = '-logger phing.listener.DefaultLogger'; + $cmd[] = $this->targetsToString(); + $cmd[] = '2>&1'; + + return $this->phpci->executeCommand(implode(' ', $cmd), $this->directory, $this->targets); + } + + /** + * @return \PHPCI\Builder + */ + public function getPhpci() + { + return $this->phpci; + } + + /** + * @param \PHPCI\Builder $phpci + * + * @return $this + */ + public function setPhpci($phpci) + { + $this->phpci = $phpci; + return $this; + } + + /** + * @return string + */ + public function getDirectory() + { + return $this->directory; + } + + /** + * @param string $directory + * + * @return $this + */ + public function setDirectory($directory) + { + $this->directory = $directory; + return $this; + } + + /** + * @return string + */ + public function getTargets() + { + return $this->targets; + } + + private function targetsToString() + { + return implode(' ', $this->targets); + } + + /** + * @param array|string $targets + * + * @return $this + */ + public function setTargets($targets) + { + if (is_string($targets)) { + $targets = array($targets); + } + + $this->targets = $targets; + return $this; + } + + /** + * @return string + */ + public function getBuildFile() + { + return $this->buildFile; + } + + /** + * @param mixed $buildFile + * + * @return $this + * @throws \Exception + */ + public function setBuildFile($buildFile) + { + $buildFile = $this->getDirectory() . $buildFile; + if (!file_exists($buildFile)) { + throw new \Exception('Specified build file does not exists.'); + } + + $this->buildFile = $buildFile; + return $this; + } + + /** + * @return mixed + */ + public function getProperties() + { + return $this->properties; + } + + /** + * @return string + */ + public function propertiesToString() + { + if (empty($this->properties)) { + return ''; + } + + $propertiesString = array(); + + foreach ($this->properties as $name => $value) { + $propertiesString[] = '-D' . $name . '="' . $value . '"'; + } + + return implode(' ', $propertiesString); + } + + /** + * @param array|string $properties + * + * @return $this + */ + public function setProperties($properties) + { + if (is_string($properties)) { + $properties = array($properties); + } + + $this->properties = $properties; + return $this; + } + + /** + * @return string + */ + public function getPropertyFile() + { + return $this->propertyFile; + } + + /** + * @param string $propertyFile + * + * @return $this + * @throws \Exception + */ + public function setPropertyFile($propertyFile) + { + if (!file_exists($this->getDirectory() . '/' . $propertyFile)) { + throw new \Exception('Specified property file file does not exists.'); + } + + $this->propertyFile = $propertyFile; + return $this; + } +} diff --git a/README.md b/README.md index bf640b6e..4f24229d 100644 --- a/README.md +++ b/README.md @@ -116,6 +116,15 @@ test: allow_failures: true grunt: task: "build" + phing: + directory: '' # Relative path to a directory where to run phing (default [project build directory]) + build_file: 'build.xml' # Relative path to a build file to use (default "build.xml") + targets: # A targets to execute (default "build") + - "build:all" + properties: # Custom properties (optional) + someProperty: "someValue" + someProperty2: "someValue2" + property_file: "build.properties" # Relative path to a property file to use (optional) complete: mysql: From f1b13e48f84943bfab3203741bb7a13334c8b1b5 Mon Sep 17 00:00:00 2001 From: Pavel Pavlov Date: Thu, 17 Oct 2013 02:17:40 +0400 Subject: [PATCH 097/933] Cleanup --- PHPCI/Plugin/Phing.php | 1 - 1 file changed, 1 deletion(-) diff --git a/PHPCI/Plugin/Phing.php b/PHPCI/Plugin/Phing.php index f9426235..e5004240 100644 --- a/PHPCI/Plugin/Phing.php +++ b/PHPCI/Plugin/Phing.php @@ -58,7 +58,6 @@ class Phing implements \PHPCI\Plugin return false; } -// $cmd[] = 'cd ' . $this->getDirectory() . ' &&'; $cmd[] = $phingExecutable . ' -f ' . $this->getBuildFile(); if ($this->getPropertyFile()) { From 2c1c5bfbe929cfafdbf5979049c2eac1d2756644 Mon Sep 17 00:00:00 2001 From: Pavel Pavlov Date: Thu, 17 Oct 2013 04:17:06 +0400 Subject: [PATCH 098/933] Typo fix --- PHPCI/Plugin/Phing.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PHPCI/Plugin/Phing.php b/PHPCI/Plugin/Phing.php index e5004240..f639552a 100644 --- a/PHPCI/Plugin/Phing.php +++ b/PHPCI/Plugin/Phing.php @@ -222,7 +222,7 @@ class Phing implements \PHPCI\Plugin public function setPropertyFile($propertyFile) { if (!file_exists($this->getDirectory() . '/' . $propertyFile)) { - throw new \Exception('Specified property file file does not exists.'); + throw new \Exception('Specified property file does not exists.'); } $this->propertyFile = $propertyFile; From 24602766aac6179f147c4d05dc69de1e42a0d796 Mon Sep 17 00:00:00 2001 From: Gabriel Baker Date: Thu, 17 Oct 2013 08:50:37 +0100 Subject: [PATCH 099/933] fix for composer_home being missing --- .gitignore | 1 + PHPCI/Controller/PluginController.php | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 3904ba6d..2f2601ea 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ config.php .buildpath .htaccess PHPCI/config.yml +cache \ No newline at end of file diff --git a/PHPCI/Controller/PluginController.php b/PHPCI/Controller/PluginController.php index 22b25bfc..1facce06 100644 --- a/PHPCI/Controller/PluginController.php +++ b/PHPCI/Controller/PluginController.php @@ -76,7 +76,7 @@ class PluginController extends \PHPCI\Controller $this->setComposerJson($json); if ($this->canInstall) { - shell_exec($this->composerPath . ' update --working-dir=' . APPLICATION_PATH . ' > /dev/null 2>&1 &'); + shell_exec($this->composerPath . ' update --working-dir=' . APPLICATION_PATH . ' > /' . APPLICATION_PATH . '/phpci_composer_remove.log 2>&1 &'); } header('Location: ' . PHPCI_URL . 'plugin?r=' . $package); @@ -101,7 +101,7 @@ class PluginController extends \PHPCI\Controller $this->setComposerJson($json); if ($this->canInstall) { - shell_exec($this->composerPath . ' update --working-dir=' . APPLICATION_PATH . ' > /dev/null 2>&1 &'); + shell_exec('COMPOSER_HOME='.APPLICATION_PATH . ' ' . $this->composerPath . ' update --working-dir=' . APPLICATION_PATH . ' > /' . APPLICATION_PATH . '/phpci_composer_install.log 2>&1 &'); header('Location: ' . PHPCI_URL . 'plugin?i=' . $package); die; From 7a0893737aba5717cc9c0bc4991c3ad1c678fd73 Mon Sep 17 00:00:00 2001 From: Gabriel Baker Date: Thu, 17 Oct 2013 08:53:05 +0100 Subject: [PATCH 100/933] composer_home --- PHPCI/Controller/PluginController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PHPCI/Controller/PluginController.php b/PHPCI/Controller/PluginController.php index 1facce06..45aed22f 100644 --- a/PHPCI/Controller/PluginController.php +++ b/PHPCI/Controller/PluginController.php @@ -76,7 +76,7 @@ class PluginController extends \PHPCI\Controller $this->setComposerJson($json); if ($this->canInstall) { - shell_exec($this->composerPath . ' update --working-dir=' . APPLICATION_PATH . ' > /' . APPLICATION_PATH . '/phpci_composer_remove.log 2>&1 &'); + shell_exec('COMPOSER_HOME='.APPLICATION_PATH . ' ' . $this->composerPath . ' update --working-dir=' . APPLICATION_PATH . ' > /' . APPLICATION_PATH . '/phpci_composer_remove.log 2>&1 &'); } header('Location: ' . PHPCI_URL . 'plugin?r=' . $package); From 44fdee9745fc03ce4e4b0009177611aa7242ef69 Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Thu, 17 Oct 2013 11:44:36 +0100 Subject: [PATCH 101/933] Moving installation and getting started docs to the Wiki --- README.md | 123 ++---------------------------------------------------- 1 file changed, 3 insertions(+), 120 deletions(-) diff --git a/README.md b/README.md index 4f24229d..6f5a14af 100644 --- a/README.md +++ b/README.md @@ -13,13 +13,7 @@ _**Please be aware that PHPCI is a beta-release project, so whilst it is very st * Clones your project from Github, Bitbucket or a local path * Allows you to set up and tear down test databases. * Installs your project's Composer dependencies. -* Runs through any combination of the following plugins: - * PHP Unit - * PHP Mess Detector - * PHP Copy/Paste Detector - * PHP Code Sniffer - * PHP Spec - * Atoum +* Runs through any combination of the [supported plugins](https://github.com/Block8/PHPCI/wiki#plugins). * You can mark directories for the plugins to ignore. * You can mark certain plugins as being allowed to fail (but still run.) @@ -30,119 +24,8 @@ _**Please be aware that PHPCI is a beta-release project, so whilst it is very st * Install PEAR or PECL extensions. * Deployments. -##Installing PHPCI: -####Pre-requisites: -* PHP 5.3.3+ -* A web server. We prefer nginx. -* A MySQL server to connect to (doesn't have to be on the same server.) -* PHPCI needs to be able to run `exec()`, so make sure this is not disabled -* Php-openssl must be available. - - -####Installing from Github: -* Step 1: `git clone https://github.com/Block8/PHPCI.git` -* Step 2: `cd PHPCI` -* Step 3: `composer install` -* Step 4: `chmod +x ./console && ./console phpci:install` - * When prompted, enter your database host, username, password and the database name that PHPCI should use. - * The script will attempt to create the database if it does not exist already. - * If you intend to use the MySQL plugin to create / destroy databases, the user you entered above will need CREATE / DELETE permissions on the server. -* Add a virtual host to your web server, pointing to the directory "public" where you cloned PHPCI into. -* You'll need to set up rewrite rules to point all non-existant requests to PHPCI. - -**Apache Example (require mod_rewrite installed)**: - -```sh - - RewriteEngine On - RewriteBase /path/to/phpci/public - RewriteCond %{REQUEST_FILENAME} !-f - RewriteCond %{REQUEST_FILENAME} !-d - RewriteRule . /index.php [L] - -``` - -**Nginx Example**: - -``` -location / { - try_files $uri $uri/ index.php -} -``` - -Finally, you'll want to set up PHPCI to run as a regular cronjob, so run `crontab -e` and enter the following: - - * * * * * /usr/bin/php /path/to/phpci/console phpci:run-builds - -Obviously, make sure you change the `/path/to/phpci` to the directory in which you installed PHPCI, and update the PHP path if necessary. - -##Adding support for PHPCI to your projects: -Similar to Travis CI, to support PHPCI in your project, you simply need to add a `phpci.yml` file to the root of your repository. The file should look something like this: - -```yml -build_settings: - ignore: - - "vendor" - - "tests" - mysql: - host: "localhost" - user: "root" - pass: "" - campfire: - url: "https://youraccount.campfirenow.com" - authToken: "605b32dd" - roomId: "570102" -setup: - mysql: - - "DROP DATABASE IF EXISTS test;" - - "CREATE DATABASE test;" - - "GRANT ALL PRIVILEGES ON test.* TO test@'localhost' IDENTIFIED BY 'test';" - composer: - action: "install" - -test: - php_unit: - config: - - "PHPUnit-all.xml" - - "PHPUnit-ubuntu-fix.xml" - directory: - - "tests/" - run_from: "phpunit/" - php_mess_detector: - allow_failures: true - php_code_sniffer: - standard: "PSR2" - php_cpd: - allow_failures: true - grunt: - task: "build" - phing: - directory: '' # Relative path to a directory where to run phing (default [project build directory]) - build_file: 'build.xml' # Relative path to a build file to use (default "build.xml") - targets: # A targets to execute (default "build") - - "build:all" - properties: # Custom properties (optional) - someProperty: "someValue" - someProperty2: "someValue2" - property_file: "build.properties" # Relative path to a property file to use (optional) - -complete: - mysql: - - "DROP DATABASE IF EXISTS test;" -failure: - campfire: - message: "Phpci : build %buildurl% failed." -``` - -As mentioned earlier, PHPCI is powered by plugins, there are several phases in which plugins can be run: - -* `setup` - This phase is designed to initialise the build procedure. -* `test` - The tests that should be run during the build. Plugins run during this phase will contribute to the success or failure of the build. -* `complete` - Always called when the `test` phase completes, regardless of success or failure. -* `success` - Called upon success of the `test` phase. -* `failure` - Called upon the failure of the `test` phase. - -The `ignore` section is merely an array of paths that should be ignored in all tests (where possible.) +## Getting Started: +We've got documentation on our wiki on [installing PHPCI](https://github.com/Block8/PHPCI/wiki/Installing-PHPCI) and [adding support for PHPCI to your projects](https://github.com/Block8/PHPCI/wiki/Adding-PHPCI-Support-to-Your-Projects). ##Contributing Contributions from others would be very much appreciated! If you just want to make a simple change, simply fork the repository, and send us a pull request when you're ready. From d214c9dc0e7c14626ecfeb2eeaf7354dc33c3dcc Mon Sep 17 00:00:00 2001 From: Pavel Pavlov Date: Thu, 17 Oct 2013 14:51:20 +0400 Subject: [PATCH 102/933] Reduced cyclomatic complexity --- PHPCI/Plugin/Phing.php | 57 +++++++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 20 deletions(-) diff --git a/PHPCI/Plugin/Phing.php b/PHPCI/Plugin/Phing.php index f639552a..e90d4b4a 100644 --- a/PHPCI/Plugin/Phing.php +++ b/PHPCI/Plugin/Phing.php @@ -23,23 +23,42 @@ class Phing implements \PHPCI\Plugin { private $directory; - private $buildFile; - private $targets; - private $properties; + private $buildFile = 'build.xml'; + private $targets = array('build'); + private $properties = array(); private $propertyFile; protected $phpci; public function __construct(Builder $phpci, Build $build, array $options = array()) { - $this - ->setDirectory( - isset($options['directory']) ? $phpci->buildPath . '/' . $options['directory'] : $phpci->buildPath - ) - ->setPhpci($phpci) - ->setBuildFile(isset($options['build_file']) ? $options['build_file'] : 'build.xml') - ->setTargets(isset($options['targets']) ? $options['targets'] : 'build') - ->setProperties(isset($options['properties']) ? $options['properties'] : []); + $this->setPhpci($phpci); + + /* + * Set working directory + */ + if (isset($options['directory'])) { + $directory = $phpci->buildPath . '/' . $options['directory']; + } else { + $directory = $phpci->buildPath; + } + + $this->setDirectory($directory); + + /* + * Sen name of a non default build file + */ + if (isset($options['build_file'])) { + $this->setBuildFile($options['build_file']); + } + + if (isset($options['targets'])) { + $this->setTargets($options['targets']); + } + + if (isset($options['properties'])) { + $this->setProperties($options['properties']); + } if (isset($options['property_file'])) { $this->setPropertyFile($options['property_file']); @@ -58,7 +77,7 @@ class Phing implements \PHPCI\Plugin return false; } - $cmd[] = $phingExecutable . ' -f ' . $this->getBuildFile(); + $cmd[] = $phingExecutable . ' -f ' . $this->getBuildFilePath(); if ($this->getPropertyFile()) { $cmd[] = '-propertyfile ' . $this->getPropertyFile(); @@ -89,7 +108,6 @@ class Phing implements \PHPCI\Plugin public function setPhpci($phpci) { $this->phpci = $phpci; - return $this; } /** @@ -108,7 +126,6 @@ class Phing implements \PHPCI\Plugin public function setDirectory($directory) { $this->directory = $directory; - return $this; } /** @@ -136,7 +153,6 @@ class Phing implements \PHPCI\Plugin } $this->targets = $targets; - return $this; } /** @@ -155,13 +171,16 @@ class Phing implements \PHPCI\Plugin */ public function setBuildFile($buildFile) { - $buildFile = $this->getDirectory() . $buildFile; - if (!file_exists($buildFile)) { + if (!file_exists($this->getDirectory() . $buildFile)) { throw new \Exception('Specified build file does not exists.'); } $this->buildFile = $buildFile; - return $this; + } + + public function getBuildFilePath() + { + return $this->getDirectory() . $this->buildFile; } /** @@ -202,7 +221,6 @@ class Phing implements \PHPCI\Plugin } $this->properties = $properties; - return $this; } /** @@ -226,6 +244,5 @@ class Phing implements \PHPCI\Plugin } $this->propertyFile = $propertyFile; - return $this; } } From 17a19c0707078316a54134b55b7df6d76bf655ad Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Sat, 12 Oct 2013 09:44:51 +0100 Subject: [PATCH 103/933] Fixing PHPCS errors on PluginController --- PHPCI/Controller/PluginController.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/PHPCI/Controller/PluginController.php b/PHPCI/Controller/PluginController.php index 45aed22f..c4910f34 100644 --- a/PHPCI/Controller/PluginController.php +++ b/PHPCI/Controller/PluginController.php @@ -76,7 +76,10 @@ class PluginController extends \PHPCI\Controller $this->setComposerJson($json); if ($this->canInstall) { - shell_exec('COMPOSER_HOME='.APPLICATION_PATH . ' ' . $this->composerPath . ' update --working-dir=' . APPLICATION_PATH . ' > /' . APPLICATION_PATH . '/phpci_composer_remove.log 2>&1 &'); + $home = 'COMPOSER_HOME='.APPLICATION_PATH . ' '; + $action = ' update --working-dir=' . APPLICATION_PATH; + $toLog = APPLICATION_PATH . '/phpci_composer_remove.log 2>&1 &'; + shell_exec($home . $this->composerPath . $action . ' > /' . $toLog); } header('Location: ' . PHPCI_URL . 'plugin?r=' . $package); @@ -101,7 +104,10 @@ class PluginController extends \PHPCI\Controller $this->setComposerJson($json); if ($this->canInstall) { - shell_exec('COMPOSER_HOME='.APPLICATION_PATH . ' ' . $this->composerPath . ' update --working-dir=' . APPLICATION_PATH . ' > /' . APPLICATION_PATH . '/phpci_composer_install.log 2>&1 &'); + $home = 'COMPOSER_HOME='.APPLICATION_PATH . ' '; + $action = ' update --working-dir=' . APPLICATION_PATH; + $toLog = ' > /' . APPLICATION_PATH . '/phpci_composer_install.log 2>&1 &'; + shell_exec($home . $this->composerPath . $action . $toLog); header('Location: ' . PHPCI_URL . 'plugin?i=' . $package); die; From 4120cb683d5f5f801741ae40548294443e2774cc Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Sat, 12 Oct 2013 09:50:15 +0100 Subject: [PATCH 104/933] Fixing PHPMD warnings count... Hopefully. --- PHPCI/Plugin/PhpMessDetector.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PHPCI/Plugin/PhpMessDetector.php b/PHPCI/Plugin/PhpMessDetector.php index c6291a6e..66944ad6 100755 --- a/PHPCI/Plugin/PhpMessDetector.php +++ b/PHPCI/Plugin/PhpMessDetector.php @@ -107,7 +107,7 @@ class PhpMessDetector implements \PHPCI\Plugin $suffixes ); - $errors = count(array_filter(explode(PHP_EOL, $this->phpci->getLastOutput()))); + $errors = count(array_filter(explode(PHP_EOL, trim($this->phpci->getLastOutput())))); $this->build->storeMeta('phpmd-warnings', $errors); return $success; From 6b63e47cfd220706e5cc4d60280cc5c03185fb04 Mon Sep 17 00:00:00 2001 From: Sami Tikka Date: Wed, 18 Sep 2013 17:00:55 +0300 Subject: [PATCH 105/933] gitcontroller, allows calling webhook for local/remote git project --- PHPCI/Application.php | 2 +- PHPCI/Controller/GitController.php | 60 ++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 PHPCI/Controller/GitController.php diff --git a/PHPCI/Application.php b/PHPCI/Application.php index 0723c2ad..77bd7170 100644 --- a/PHPCI/Application.php +++ b/PHPCI/Application.php @@ -29,7 +29,7 @@ class Application extends b8\Application // Validate the user's session unless it is a login/logout action or a web hook: $sessionAction = ($this->controllerName == 'Session' && in_array($this->action, array('login', 'logout'))); - $externalAction = in_array($this->controllerName, array('Bitbucket', 'Github', 'Gitlab', 'BuildStatus')); + $externalAction = in_array($this->controllerName, array('Bitbucket', 'Github', 'Gitlab', 'BuildStatus', 'Git')); $skipValidation = ($externalAction || $sessionAction); if ($skipValidation || $this->validateSession()) { diff --git a/PHPCI/Controller/GitController.php b/PHPCI/Controller/GitController.php new file mode 100644 index 00000000..a56d057d --- /dev/null +++ b/PHPCI/Controller/GitController.php @@ -0,0 +1,60 @@ + + */ +class GitController extends \PHPCI\Controller +{ + public function init() + { + $this->_buildStore = Store\Factory::getStore('Build'); + } + + /** + * Called by POSTing to /git/webhook/?branch=&commit= + * + * @param string $project + */ + public function webhook($project) + { + $branch = $this->getParam('branch'); + $commit = $this->getParam('commit'); + + try { + $build = new Build(); + $build->setProjectId($project); + + if ($branch !== null && trim($branch) !== '') { + $build->setBranch($branch); + } else { + $build->setBranch('master'); + } + + if ($commit !== null && trim($commit) !== '') { + $build->setCommitId($commit); + } + + $build->setStatus(0); + $build->setLog(''); + $build->setCreated(new \DateTime()); + $this->_buildStore->save($build); + } catch (\Exception $ex) { + die('FAIL'); + } + + die('OK'); + } +} From 3d8be0e2186188729961cc4e1a432669f615259e Mon Sep 17 00:00:00 2001 From: Sami Tikka Date: Thu, 17 Oct 2013 11:53:08 +0300 Subject: [PATCH 106/933] error handling like in other webhooks --- PHPCI/Controller/GitController.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/PHPCI/Controller/GitController.php b/PHPCI/Controller/GitController.php index a56d057d..396ecaa9 100644 --- a/PHPCI/Controller/GitController.php +++ b/PHPCI/Controller/GitController.php @@ -50,8 +50,17 @@ class GitController extends \PHPCI\Controller $build->setStatus(0); $build->setLog(''); $build->setCreated(new \DateTime()); - $this->_buildStore->save($build); } catch (\Exception $ex) { + header('HTTP/1.1 400 Bad Request'); + header('Ex: ' . $ex->getMessage()); + die('FAIL'); + } + + try { + $this->_buildStore->save($build); + } catch (\Exception $ex) { + header('HTTP/1.1 500 Internal Server Error'); + header('Ex: ' . $ex->getMessage()); die('FAIL'); } From 02fefe99858a1b656324f21ad25ff0512244cb5c Mon Sep 17 00:00:00 2001 From: David Epely Date: Sat, 19 Oct 2013 16:49:52 +0200 Subject: [PATCH 107/933] fix error on missing files until installation is done --- bootstrap.php | 6 ++++-- public/install.php | 2 +- vars.php | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/bootstrap.php b/bootstrap.php index 17aaf608..67b9fbaf 100644 --- a/bootstrap.php +++ b/bootstrap.php @@ -47,7 +47,9 @@ $conf['b8']['app']['namespace'] = 'PHPCI'; $conf['b8']['app']['default_controller'] = 'Home'; $conf['b8']['view']['path'] = dirname(__FILE__) . '/PHPCI/View/'; -$config = new b8\Config($conf); -$config->loadYaml(dirname(__FILE__) . '/PHPCI/config.yml'); +if (file_exists(dirname(__FILE__) . '/PHPCI/config.yml')) { + $config = new b8\Config($conf); + $config->loadYaml(dirname(__FILE__) . '/PHPCI/config.yml'); +} require_once(dirname(__FILE__) . '/vars.php'); diff --git a/public/install.php b/public/install.php index e08e647e..d792bdae 100644 --- a/public/install.php +++ b/public/install.php @@ -5,7 +5,7 @@ require_once(dirname(__FILE__) . '/../vars.php'); $installStage = 'start'; $formAction = ''; $config = array(); -$ciUrl = ($_SERVER['HTTPS'] == "on" ? 'https' : 'http') . '://'; +$ciUrl = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == "on" ? 'https' : 'http') . '://'; $ciUrl .= $_SERVER['HTTP_HOST']; $ciUrl .= str_replace('/install.php', '', $_SERVER['REQUEST_URI']); diff --git a/vars.php b/vars.php index 89a1d81e..5725805e 100644 --- a/vars.php +++ b/vars.php @@ -7,7 +7,7 @@ if (!defined('APPLICATION_PATH')) { } // Define our PHPCI_URL, if not already defined: -if (!defined('PHPCI_URL')) { +if (!defined('PHPCI_URL') && isset($config)) { define('PHPCI_URL', $config->get('phpci.url', '') . '/'); } From a38a18f0fdd1d47464cf105e329b3c08a6873fbb Mon Sep 17 00:00:00 2001 From: meadsteve Date: Sat, 26 Oct 2013 12:07:06 +0100 Subject: [PATCH 108/933] Adding psr3 logging interface and monolog to the project's composer.json. --- composer.json | 4 +- composer.lock | 106 ++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 106 insertions(+), 4 deletions(-) diff --git a/composer.json b/composer.json index 9e3ff52a..e6fac6cb 100644 --- a/composer.json +++ b/composer.json @@ -27,7 +27,9 @@ "ircmaxell/password-compat": "1.*", "swiftmailer/swiftmailer" : "5.0.*", "symfony/yaml" : "2.*", - "symfony/console" : "2.*" + "symfony/console" : "2.*", + "psr/log": "1.0.0", + "monolog/monolog": "1.6.0" }, "suggest": { diff --git a/composer.lock b/composer.lock index 4921df46..43452ea3 100644 --- a/composer.lock +++ b/composer.lock @@ -3,7 +3,7 @@ "This file locks the dependencies of your project to a known state", "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" ], - "hash": "04cca0ac809838a65555d04534cc95ae", + "hash": "534baabecc11275d5cc7f375eecf738d", "packages": [ { "name": "block8/b8framework", @@ -91,6 +91,106 @@ ], "time": "2013-04-30 19:58:08" }, + { + "name": "monolog/monolog", + "version": "1.6.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/monolog.git", + "reference": "f72392d0e6eb855118f5a84e89ac2d257c704abd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/f72392d0e6eb855118f5a84e89ac2d257c704abd", + "reference": "f72392d0e6eb855118f5a84e89ac2d257c704abd", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "psr/log": "~1.0" + }, + "require-dev": { + "doctrine/couchdb": "dev-master", + "mlehner/gelf-php": "1.0.*", + "raven/raven": "0.5.*" + }, + "suggest": { + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-mongo": "Allow sending log messages to a MongoDB server", + "mlehner/gelf-php": "Allow sending log messages to a GrayLog2 server", + "raven/raven": "Allow sending log messages to a Sentry server" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.6.x-dev" + } + }, + "autoload": { + "psr-0": { + "Monolog": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be", + "role": "Developer" + } + ], + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "homepage": "http://github.com/Seldaek/monolog", + "keywords": [ + "log", + "logging", + "psr-3" + ], + "time": "2013-07-28 22:38:30" + }, + { + "name": "psr/log", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/fe0936ee26643249e916849d48e3a51d5f5e278b", + "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b", + "shasum": "" + }, + "type": "library", + "autoload": { + "psr-0": { + "Psr\\Log\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "time": "2012-12-21 11:40:51" + }, { "name": "swiftmailer/swiftmailer", "version": "v5.0.2", @@ -142,7 +242,7 @@ }, { "name": "symfony/console", - "version": "v2.3.5", + "version": "v2.3.6", "target-dir": "Symfony/Component/Console", "source": { "type": "git", @@ -195,7 +295,7 @@ }, { "name": "symfony/yaml", - "version": "v2.3.5", + "version": "v2.3.6", "target-dir": "Symfony/Component/Yaml", "source": { "type": "git", From 1989203635979804a72a22f9e7506b195a11a28b Mon Sep 17 00:00:00 2001 From: meadsteve Date: Sat, 26 Oct 2013 16:11:46 +0100 Subject: [PATCH 109/933] Adding two custom log handlers. One to link the logs to symphony console output to the logging and another to record build specific information in the DB. --- PHPCI/Command/RunCommand.php | 37 ++++++++++++++++++++++-------- PHPCI/Helper/BuildDBLogHandler.php | 33 ++++++++++++++++++++++++++ PHPCI/Helper/OutputLogHandler.php | 32 ++++++++++++++++++++++++++ 3 files changed, 93 insertions(+), 9 deletions(-) create mode 100644 PHPCI/Helper/BuildDBLogHandler.php create mode 100644 PHPCI/Helper/OutputLogHandler.php diff --git a/PHPCI/Command/RunCommand.php b/PHPCI/Command/RunCommand.php index dc8e21d2..26ef6c1e 100644 --- a/PHPCI/Command/RunCommand.php +++ b/PHPCI/Command/RunCommand.php @@ -9,6 +9,10 @@ namespace PHPCI\Command; +use Monolog\Logger; +use PHPCI\Helper\BuildDBLogHandler; +use PHPCI\Helper\OutputLogHandler; +use Psr\Log\LoggerAwareInterface; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -26,6 +30,11 @@ use PHPCI\BuildFactory; */ class RunCommand extends Command { + /** + * @var OutputInterface + */ + protected $output; + protected function configure() { $this @@ -40,26 +49,36 @@ class RunCommand extends Command { $this->output = $output; + $logger = new Logger("BuildLog"); + $store = Factory::getStore('Build'); $result = $store->getByStatus(0); $builds = 0; + // For verbose mode we want to output all informational and above + // messages to the symphony output interface. + if ($input->getOption('verbose')) { + $logger->pushHandler( + new OutputLogHandler($this->output, Logger::INFO) + ); + } + foreach ($result['items'] as $build) { $builds++; $build = BuildFactory::getBuild($build); - if ($input->getOption('verbose')) { - $builder = new Builder($build, function ($log) { - $this->output->writeln($log); - }); - } else { - $builder = new Builder($build, function () { - // Empty stub function. - }); - } + // Logging relevant to this build should be stored + // against the build itself. + $buildDbLog = new BuildDBLogHandler($build, Logger::INFO); + $logger->pushHandler($buildDbLog); + $builder = new Builder($build, $logger); $builder->execute(); + + // After execution we no longer want to record the information + // back to this specific build so the handler should be removed. + $logger->popHandler($buildDbLog); } return $builds; diff --git a/PHPCI/Helper/BuildDBLogHandler.php b/PHPCI/Helper/BuildDBLogHandler.php new file mode 100644 index 00000000..c886767a --- /dev/null +++ b/PHPCI/Helper/BuildDBLogHandler.php @@ -0,0 +1,33 @@ +build = $build; + // We want to add to any existing saved log information. + $this->logValue = $build->getLog(); + } + + protected function write(array $record) { + $this->logValue .= (string) $record['formatted']; + $this->build->setLog($this->logValue); + } +} \ No newline at end of file diff --git a/PHPCI/Helper/OutputLogHandler.php b/PHPCI/Helper/OutputLogHandler.php new file mode 100644 index 00000000..7dd46d20 --- /dev/null +++ b/PHPCI/Helper/OutputLogHandler.php @@ -0,0 +1,32 @@ +output = $output; + } + + + protected function write(array $record) + { + $this->output->writeln((string) $record['formatted']); + } + + +} \ No newline at end of file From 8c88581021736b5a3fc7ea4c056207554a8beb76 Mon Sep 17 00:00:00 2001 From: meadsteve Date: Sat, 26 Oct 2013 16:15:29 +0100 Subject: [PATCH 110/933] Modified the builder so that it expects to have a psr3 compliant logger attached. --- PHPCI/BuildFactory.php | 2 +- PHPCI/Builder.php | 79 +++++++++++++++++++++++++----------------- PHPCI/Plugin/Atoum.php | 4 +-- 3 files changed, 51 insertions(+), 34 deletions(-) diff --git a/PHPCI/BuildFactory.php b/PHPCI/BuildFactory.php index e4628434..0dd53058 100644 --- a/PHPCI/BuildFactory.php +++ b/PHPCI/BuildFactory.php @@ -22,7 +22,7 @@ class BuildFactory { /** * Takes a generic build and returns a type-specific build model. - * @return PHPCI\Model\Build\LocalBuild|PHPCI\Model\Build\GithubBuild|PHPCI\Model\Build\BitbucketBuild + * @return \PHPCI\Model\Build\LocalBuild|\PHPCI\Model\Build\GithubBuild|\PHPCI\Model\Build\BitbucketBuild */ public static function getBuild(Build $base) { diff --git a/PHPCI/Builder.php b/PHPCI/Builder.php index 6fa20be0..14a82f18 100644 --- a/PHPCI/Builder.php +++ b/PHPCI/Builder.php @@ -12,12 +12,15 @@ namespace PHPCI; use PHPCI\Model\Build; use b8\Store; use b8\Config; +use Psr\Log\LoggerAwareInterface; +use Psr\Log\LoggerInterface; +use Psr\Log\LogLevel; /** * PHPCI Build Runner * @author Dan Cryer */ -class Builder +class Builder implements LoggerAwareInterface { /** * @var string @@ -44,11 +47,6 @@ class Builder */ protected $success = true; - /** - * @var string - */ - protected $log = ''; - /** * @var bool */ @@ -59,10 +57,10 @@ class Builder */ protected $build; - /** - * @var callable - */ - protected $logCallback; + /** + * @var LoggerInterface + */ + protected $logger; /** * @var array @@ -94,14 +92,16 @@ class Builder /** * Set up the builder. - * @param \PHPCI\Model\Build - * @param callable + * @param \PHPCI\Model\Build $build + * @param LoggerInterface $logger */ - public function __construct(Build $build, \Closure $logCallback) + public function __construct(Build $build, $logger = null) { + if ($logger) { + $this->setLogger($logger); + } $this->build = $build; $this->store = Store\Factory::getStore('Build'); - $this->logCallback = $logCallback; } /** @@ -164,7 +164,6 @@ class Builder // Run the core plugin stages: foreach (array('setup', 'test', 'complete') as $stage) { $this->executePlugins($stage); - $this->log(''); } // Failed build? Execute failure plugins and then mark the build as failed. @@ -192,7 +191,6 @@ class Builder // Update the build in the database, ping any external services, etc. $this->build->sendStatusPostback(); $this->build->setFinished(new \DateTime()); - $this->build->setLog($this->log); $this->store->save($this->build); } @@ -204,14 +202,14 @@ class Builder $command = call_user_func_array('sprintf', func_get_args()); if (!$this->quiet) { - $this->log('Executing: ' . $command, ' '); + $this->log('Executing: ' . $command); } $status = 0; exec($command, $this->lastOutput, $status); if (!empty($this->lastOutput) && ($this->verbose || $status != 0)) { - $this->log($this->lastOutput, ' '); + $this->log($this->lastOutput); } @@ -232,24 +230,25 @@ class Builder return implode(PHP_EOL, $this->lastOutput); } - /** - * Add an entry to the build log. - * @param string|string[] - * @param string - */ - public function log($message, $prefix = '') + /** + * Add an entry to the build log. + * @param string|string[] $message + * @param string $level + * @param mixed[] $context + */ + public function log($message, $level = LogLevel::INFO, $context = array()) { + // Skip if no logger has been loaded. + if (!$this->logger) { + return; + } + if (!is_array($message)) { $message = array($message); } - foreach ($message as $item) { - call_user_func_array($this->logCallback, array($prefix . $item)); - $this->log .= $prefix . $item . PHP_EOL; + $this->logger->log($level, $item, $context); } - - $this->build->setLog($this->log); - $this->store->save($this->build); } /** @@ -354,7 +353,6 @@ class Builder } foreach ($this->config[$stage] as $plugin => $options) { - $this->log(''); $this->log('RUNNING PLUGIN: ' . $plugin); // Is this plugin allowed to fail? @@ -445,4 +443,23 @@ class Builder return null; } + + /** + * Sets a logger instance on the object + * + * @param LoggerInterface $logger + * @return null + */ + public function setLogger(LoggerInterface $logger) { + $this->logger = $logger; + } + + /** + * returns the logger attached to this builder. + * + * @return LoggerInterface + */ + public function getLogger() { + return $this->logger; + } } diff --git a/PHPCI/Plugin/Atoum.php b/PHPCI/Plugin/Atoum.php index 7a837b0a..c6e2f2e6 100644 --- a/PHPCI/Plugin/Atoum.php +++ b/PHPCI/Plugin/Atoum.php @@ -56,11 +56,11 @@ class Atoum implements \PHPCI\Plugin if (count(preg_grep("/Success \(/", $output)) == 0) { $status = false; - $this->phpci->log($output, ' '); + $this->phpci->log($output); } if (count($output) == 0) { $status = false; - $this->phpci->log("No test have been performed!", ' '); + $this->phpci->log("No test have been performed!"); } return $status; From b943c07f8725083aac0de7df5d713346e841a965 Mon Sep 17 00:00:00 2001 From: meadsteve Date: Sat, 26 Oct 2013 16:25:34 +0100 Subject: [PATCH 111/933] Fixing some formatting issues --- PHPCI/Builder.php | 223 +++++++++++++++-------------- PHPCI/Helper/BuildDBLogHandler.php | 38 ++--- PHPCI/Helper/OutputLogHandler.php | 31 ++-- 3 files changed, 153 insertions(+), 139 deletions(-) diff --git a/PHPCI/Builder.php b/PHPCI/Builder.php index 14a82f18..8533816a 100644 --- a/PHPCI/Builder.php +++ b/PHPCI/Builder.php @@ -1,11 +1,11 @@ -*/ + * PHPCI Build Runner + * @author Dan Cryer + */ class Builder implements LoggerAwareInterface { /** - * @var string - */ + * @var string + */ public $buildPath; /** - * @var string[] - */ - public $ignore = array(); + * @var string[] + */ + public $ignore = array(); /** - * @var string - */ + * @var string + */ protected $ciDir; /** - * @var string - */ + * @var string + */ protected $directory; /** - * @var bool - */ - protected $success = true; + * @var bool + */ + protected $success = true; /** - * @var bool - */ - protected $verbose = true; + * @var bool + */ + protected $verbose = true; /** - * @var \PHPCI\Model\Build - */ + * @var \PHPCI\Model\Build + */ protected $build; - /** - * @var LoggerInterface - */ - protected $logger; + /** + * @var LoggerInterface + */ + protected $logger; /** - * @var array - */ + * @var array + */ protected $config; /** @@ -73,7 +73,7 @@ class Builder implements LoggerAwareInterface protected $lastOutput; /** - * An array of key => value pairs that will be used for + * An array of key => value pairs that will be used for * interpolation and environment variables * @var array * @see setInterpolationVars() @@ -91,32 +91,32 @@ class Builder implements LoggerAwareInterface public $quiet = false; /** - * Set up the builder. - * @param \PHPCI\Model\Build $build - * @param LoggerInterface $logger - */ + * Set up the builder. + * @param \PHPCI\Model\Build $build + * @param LoggerInterface $logger + */ public function __construct(Build $build, $logger = null) { - if ($logger) { - $this->setLogger($logger); - } + if ($logger) { + $this->setLogger($logger); + } $this->build = $build; $this->store = Store\Factory::getStore('Build'); } /** - * Set the config array, as read from phpci.yml - * @param array - */ + * Set the config array, as read from phpci.yml + * @param array + */ public function setConfigArray(array $config) { $this->config = $config; } /** - * Access a variable from the phpci.yml file. - * @param string - */ + * Access a variable from the phpci.yml file. + * @param string + */ public function getConfig($key) { $rtn = null; @@ -147,8 +147,8 @@ class Builder implements LoggerAwareInterface } /** - * Run the active build. - */ + * Run the active build. + */ public function execute() { // Update the build in the database, ping any external services. @@ -183,7 +183,7 @@ class Builder implements LoggerAwareInterface $this->logFailure($ex->getMessage()); $this->build->setStatus(3); } - + // Clean up: $this->log('Removing build.'); shell_exec(sprintf('rm -Rf "%s"', $this->buildPath)); @@ -195,8 +195,8 @@ class Builder implements LoggerAwareInterface } /** - * Used by this class, and plugins, to execute shell commands. - */ + * Used by this class, and plugins, to execute shell commands. + */ public function executeCommand() { $command = call_user_func_array('sprintf', func_get_args()); @@ -230,45 +230,45 @@ class Builder implements LoggerAwareInterface return implode(PHP_EOL, $this->lastOutput); } - /** - * Add an entry to the build log. - * @param string|string[] $message - * @param string $level - * @param mixed[] $context - */ + /** + * Add an entry to the build log. + * @param string|string[] $message + * @param string $level + * @param mixed[] $context + */ public function log($message, $level = LogLevel::INFO, $context = array()) { - // Skip if no logger has been loaded. - if (!$this->logger) { - return; - } + // Skip if no logger has been loaded. + if (!$this->logger) { + return; + } if (!is_array($message)) { $message = array($message); } foreach ($message as $item) { - $this->logger->log($level, $item, $context); + $this->logger->log($level, $item, $context); } } /** - * Add a success-coloured message to the log. - * @param string - */ + * Add a success-coloured message to the log. + * @param string + */ public function logSuccess($message) { $this->log("\033[0;32m" . $message . "\033[0m"); } /** - * Add a failure-coloured message to the log. - * @param string - */ + * Add a failure-coloured message to the log. + * @param string + */ public function logFailure($message) { $this->log("\033[0;31m" . $message . "\033[0m"); } - + /** * Replace every occurance of the interpolation vars in the given string * Example: "This is build %PHPCI_BUILD%" => "This is build 182" @@ -283,7 +283,7 @@ class Builder implements LoggerAwareInterface } /** - * Sets the variables that will be used for interpolation. This must be run + * Sets the variables that will be used for interpolation. This must be run * from setupBuild() because prior to that, we don't know the buildPath */ protected function setInterpolationVars() @@ -293,9 +293,11 @@ class Builder implements LoggerAwareInterface $this->interpolation_vars['%COMMIT%'] = $this->build->getCommitId(); $this->interpolation_vars['%PROJECT%'] = $this->build->getProjectId(); $this->interpolation_vars['%BUILD%'] = $this->build->getId(); - $this->interpolation_vars['%PROJECT_TITLE%'] = $this->getBuildProjectTitle(); + $this->interpolation_vars['%PROJECT_TITLE%'] = $this->getBuildProjectTitle( + ); $this->interpolation_vars['%BUILD_PATH%'] = $this->buildPath; - $this->interpolation_vars['%BUILD_URI%'] = PHPCI_URL . "build/view/" . $this->build->getId(); + $this->interpolation_vars['%BUILD_URI%'] = PHPCI_URL . "build/view/" . $this->build->getId( + ); $this->interpolation_vars['%PHPCI_COMMIT%'] = $this->interpolation_vars['%COMMIT%']; $this->interpolation_vars['%PHPCI_PROJECT%'] = $this->interpolation_vars['%PROJECT%']; $this->interpolation_vars['%PHPCI_BUILD%'] = $this->interpolation_vars['%BUILD%']; @@ -304,25 +306,28 @@ class Builder implements LoggerAwareInterface $this->interpolation_vars['%PHPCI_BUILD_URI%'] = $this->interpolation_vars['%BUILD_URI%']; putenv('PHPCI=1'); - putenv('PHPCI_COMMIT='.$this->interpolation_vars['%COMMIT%']); - putenv('PHPCI_PROJECT='.$this->interpolation_vars['%PROJECT%']); - putenv('PHPCI_BUILD='.$this->interpolation_vars['%BUILD%']); - putenv('PHPCI_PROJECT_TITLE='.$this->interpolation_vars['%PROJECT_TITLE%']); - putenv('PHPCI_BUILD_PATH='.$this->interpolation_vars['%BUILD_PATH%']); - putenv('PHPCI_BUILD_URI='.$this->interpolation_vars['%BUILD_URI%']); + putenv('PHPCI_COMMIT=' . $this->interpolation_vars['%COMMIT%']); + putenv('PHPCI_PROJECT=' . $this->interpolation_vars['%PROJECT%']); + putenv('PHPCI_BUILD=' . $this->interpolation_vars['%BUILD%']); + putenv( + 'PHPCI_PROJECT_TITLE=' . $this->interpolation_vars['%PROJECT_TITLE%'] + ); + putenv('PHPCI_BUILD_PATH=' . $this->interpolation_vars['%BUILD_PATH%']); + putenv('PHPCI_BUILD_URI=' . $this->interpolation_vars['%BUILD_URI%']); } - + /** - * Set up a working copy of the project for building. - */ + * Set up a working copy of the project for building. + */ protected function setupBuild() { - $buildId = 'project' . $this->build->getProject()->getId() . '-build' . $this->build->getId(); - $this->ciDir = dirname(__FILE__) . '/../'; - $this->buildPath = $this->ciDir . 'build/' . $buildId . '/'; - + $buildId = 'project' . $this->build->getProject()->getId( + ) . '-build' . $this->build->getId(); + $this->ciDir = dirname(__FILE__) . '/../'; + $this->buildPath = $this->ciDir . 'build/' . $buildId . '/'; + $this->setInterpolationVars(); - + // Create a working copy of the project: if (!$this->build->createWorkingCopy($this, $this->buildPath)) { throw new \Exception('Could not create a working copy.'); @@ -343,12 +348,16 @@ class Builder implements LoggerAwareInterface } /** - * Execute a the appropriate set of plugins for a given build stage. - */ + * Execute a the appropriate set of plugins for a given build stage. + */ protected function executePlugins($stage) { // Ignore any stages for which we don't have plugins set: - if (!array_key_exists($stage, $this->config) || !is_array($this->config[$stage])) { + if (!array_key_exists( + $stage, + $this->config + ) || !is_array($this->config[$stage]) + ) { return; } @@ -444,22 +453,24 @@ class Builder implements LoggerAwareInterface return null; } - /** - * Sets a logger instance on the object - * - * @param LoggerInterface $logger - * @return null - */ - public function setLogger(LoggerInterface $logger) { - $this->logger = $logger; - } + /** + * Sets a logger instance on the object + * + * @param LoggerInterface $logger + * @return null + */ + public function setLogger(LoggerInterface $logger) + { + $this->logger = $logger; + } - /** - * returns the logger attached to this builder. - * - * @return LoggerInterface - */ - public function getLogger() { - return $this->logger; - } + /** + * returns the logger attached to this builder. + * + * @return LoggerInterface + */ + public function getLogger() + { + return $this->logger; + } } diff --git a/PHPCI/Helper/BuildDBLogHandler.php b/PHPCI/Helper/BuildDBLogHandler.php index c886767a..6ae34379 100644 --- a/PHPCI/Helper/BuildDBLogHandler.php +++ b/PHPCI/Helper/BuildDBLogHandler.php @@ -9,25 +9,27 @@ use PHPCI\Model\Build; class BuildDBLogHandler extends AbstractProcessingHandler { - /** - * @var Build - */ - protected $build; + /** + * @var Build + */ + protected $build; - protected $logValue; + protected $logValue; - function __construct(Build $build, - $level = LogLevel::INFO, - $bubble = true) - { - parent::__construct($level, $bubble); - $this->build = $build; - // We want to add to any existing saved log information. - $this->logValue = $build->getLog(); - } + function __construct( + Build $build, + $level = LogLevel::INFO, + $bubble = true + ) { + parent::__construct($level, $bubble); + $this->build = $build; + // We want to add to any existing saved log information. + $this->logValue = $build->getLog(); + } - protected function write(array $record) { - $this->logValue .= (string) $record['formatted']; - $this->build->setLog($this->logValue); - } + protected function write(array $record) + { + $this->logValue .= (string)$record['formatted']; + $this->build->setLog($this->logValue); + } } \ No newline at end of file diff --git a/PHPCI/Helper/OutputLogHandler.php b/PHPCI/Helper/OutputLogHandler.php index 7dd46d20..993f1c33 100644 --- a/PHPCI/Helper/OutputLogHandler.php +++ b/PHPCI/Helper/OutputLogHandler.php @@ -9,24 +9,25 @@ use Symfony\Component\Console\Output\OutputInterface; class OutputLogHandler extends AbstractProcessingHandler { - /** - * @var OutputInterface - */ - protected $output; + /** + * @var OutputInterface + */ + protected $output; - function __construct(OutputInterface $output, - $level = LogLevel::INFO, - $bubble = true) - { - parent::__construct($level, $bubble); - $this->output = $output; - } + function __construct( + OutputInterface $output, + $level = LogLevel::INFO, + $bubble = true + ) { + parent::__construct($level, $bubble); + $this->output = $output; + } - protected function write(array $record) - { - $this->output->writeln((string) $record['formatted']); - } + protected function write(array $record) + { + $this->output->writeln((string)$record['formatted']); + } } \ No newline at end of file From fc2434b65d3e67e7952919edecc7bc0bbf11ac08 Mon Sep 17 00:00:00 2001 From: meadsteve Date: Sun, 27 Oct 2013 12:51:49 +0000 Subject: [PATCH 112/933] Exceptions can now be passed in to the failure logging function. --- PHPCI/Builder.php | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/PHPCI/Builder.php b/PHPCI/Builder.php index 8533816a..2480dbe4 100644 --- a/PHPCI/Builder.php +++ b/PHPCI/Builder.php @@ -180,7 +180,7 @@ class Builder implements LoggerAwareInterface } } catch (\Exception $ex) { - $this->logFailure($ex->getMessage()); + $this->logFailure($ex->getMessage(), $ex); $this->build->setStatus(3); } @@ -262,11 +262,24 @@ class Builder implements LoggerAwareInterface /** * Add a failure-coloured message to the log. - * @param string + * @param string $message + * @param \Exception $exception The exception that caused the error. */ - public function logFailure($message) + public function logFailure($message, \Exception $exception = null) { - $this->log("\033[0;31m" . $message . "\033[0m"); + $context = array(); + + // The psr3 log interface stipulates that exceptions should be passed + // as the exception key in the context array. + if ($exception) { + $context['exception'] = $exception; + } + + $this->log( + "\033[0;31m" . $message . "\033[0m", + LogLevel::ERROR, + $context + ); } /** @@ -399,7 +412,7 @@ class Builder implements LoggerAwareInterface $class = 'PHPCI\\Plugin\\' . str_replace(' ', '', $class); if (!class_exists($class)) { - $this->logFailure('Plugin does not exist: ' . $plugin); + $this->logFailure('Plugin does not exist: ' . $plugin, $ex); return false; } @@ -413,7 +426,7 @@ class Builder implements LoggerAwareInterface $rtn = false; } } catch (\Exception $ex) { - $this->logFailure('EXCEPTION: ' . $ex->getMessage()); + $this->logFailure('EXCEPTION: ' . $ex->getMessage(), $ex); $rtn = false; } From e44c7b90d614be2177f7c788232a831b7ccfee5e Mon Sep 17 00:00:00 2001 From: meadsteve Date: Sun, 27 Oct 2013 14:21:08 +0000 Subject: [PATCH 113/933] All build logs calls now pass the build through as part of the context so this gets recorded in the log message. --- PHPCI/Builder.php | 5 ++++ PHPCI/Helper/LoggedBuildContextTidier.php | 31 +++++++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 PHPCI/Helper/LoggedBuildContextTidier.php diff --git a/PHPCI/Builder.php b/PHPCI/Builder.php index 2480dbe4..11e46082 100644 --- a/PHPCI/Builder.php +++ b/PHPCI/Builder.php @@ -246,6 +246,11 @@ class Builder implements LoggerAwareInterface if (!is_array($message)) { $message = array($message); } + + // The build is added to the context so the logger can use + // details from it if required. + $context['build'] = $this; + foreach ($message as $item) { $this->logger->log($level, $item, $context); } diff --git a/PHPCI/Helper/LoggedBuildContextTidier.php b/PHPCI/Helper/LoggedBuildContextTidier.php new file mode 100644 index 00000000..2a8a5e72 --- /dev/null +++ b/PHPCI/Helper/LoggedBuildContextTidier.php @@ -0,0 +1,31 @@ +tidyLoggedBuildContext(func_get_arg(0)); + } + + /** + * Removes the build object from the logged record and adds the ID as + * this is more useful to display. + * + * @param array $logRecord + * @return array + */ + protected function tidyLoggedBuildContext(array $logRecord) + { + if (isset($logRecord['context']['build'])) { + $build = $logRecord['context']['build']; + if ($build instanceof Build) { + $logRecord['context']['buildID'] = $build->getId(); + unset($logRecord['context']['build']); + } + } + return $logRecord; + } +} \ No newline at end of file From a453571fcbabe9a79aaca1370be3325c0ad5b0aa Mon Sep 17 00:00:00 2001 From: meadsteve Date: Sun, 27 Oct 2013 14:25:43 +0000 Subject: [PATCH 114/933] Adding the LoggedBuildContextTidier to the monolog logging so that the build id is added to log messages. --- PHPCI/Command/RunCommand.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/PHPCI/Command/RunCommand.php b/PHPCI/Command/RunCommand.php index 26ef6c1e..1efe68d1 100644 --- a/PHPCI/Command/RunCommand.php +++ b/PHPCI/Command/RunCommand.php @@ -11,6 +11,7 @@ namespace PHPCI\Command; use Monolog\Logger; use PHPCI\Helper\BuildDBLogHandler; +use PHPCI\Helper\LoggedBuildContextTidier; use PHPCI\Helper\OutputLogHandler; use Psr\Log\LoggerAwareInterface; use Symfony\Component\Console\Command\Command; @@ -63,6 +64,8 @@ class RunCommand extends Command ); } + $logger->pushProcessor(new LoggedBuildContextTidier()); + foreach ($result['items'] as $build) { $builds++; From 35b3db13d8d1b0d742cf1885d6bfb376800c8345 Mon Sep 17 00:00:00 2001 From: meadsteve Date: Sun, 27 Oct 2013 14:26:37 +0000 Subject: [PATCH 115/933] Fixing more tab -> space conversion issues. --- PHPCI/Command/RunCommand.php | 46 ++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/PHPCI/Command/RunCommand.php b/PHPCI/Command/RunCommand.php index 1efe68d1..2acf83dc 100644 --- a/PHPCI/Command/RunCommand.php +++ b/PHPCI/Command/RunCommand.php @@ -31,10 +31,10 @@ use PHPCI\BuildFactory; */ class RunCommand extends Command { - /** - * @var OutputInterface - */ - protected $output; + /** + * @var OutputInterface + */ + protected $output; protected function configure() { @@ -44,25 +44,25 @@ class RunCommand extends Command } /** - * Pulls all pending builds from the database and runs them. - */ + * Pulls all pending builds from the database and runs them. + */ protected function execute(InputInterface $input, OutputInterface $output) { $this->output = $output; - $logger = new Logger("BuildLog"); + $logger = new Logger("BuildLog"); - $store = Factory::getStore('Build'); + $store = Factory::getStore('Build'); $result = $store->getByStatus(0); $builds = 0; - // For verbose mode we want to output all informational and above - // messages to the symphony output interface. - if ($input->getOption('verbose')) { - $logger->pushHandler( - new OutputLogHandler($this->output, Logger::INFO) - ); - } + // For verbose mode we want to output all informational and above + // messages to the symphony output interface. + if ($input->getOption('verbose')) { + $logger->pushHandler( + new OutputLogHandler($this->output, Logger::INFO) + ); + } $logger->pushProcessor(new LoggedBuildContextTidier()); @@ -71,17 +71,17 @@ class RunCommand extends Command $build = BuildFactory::getBuild($build); - // Logging relevant to this build should be stored - // against the build itself. - $buildDbLog = new BuildDBLogHandler($build, Logger::INFO); - $logger->pushHandler($buildDbLog); + // Logging relevant to this build should be stored + // against the build itself. + $buildDbLog = new BuildDBLogHandler($build, Logger::INFO); + $logger->pushHandler($buildDbLog); - $builder = new Builder($build, $logger); + $builder = new Builder($build, $logger); $builder->execute(); - // After execution we no longer want to record the information - // back to this specific build so the handler should be removed. - $logger->popHandler($buildDbLog); + // After execution we no longer want to record the information + // back to this specific build so the handler should be removed. + $logger->popHandler($buildDbLog); } return $builds; From e2c7a4cd4367407dd9cb90f7adaf55dab6d656cb Mon Sep 17 00:00:00 2001 From: "a.cianfarani" Date: Mon, 28 Oct 2013 17:41:15 +0100 Subject: [PATCH 116/933] Added chdir because atoum as bundle need to be run from app root path --- PHPCI/Plugin/Atoum.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PHPCI/Plugin/Atoum.php b/PHPCI/Plugin/Atoum.php index 54783b07..464756be 100644 --- a/PHPCI/Plugin/Atoum.php +++ b/PHPCI/Plugin/Atoum.php @@ -45,7 +45,7 @@ class Atoum implements \PHPCI\Plugin $dirPath = $this->phpci->buildPath . DIRECTORY_SEPARATOR . $this->directory; $cmd .= " -d '{$dirPath}'"; } - + chdir($this->phpci->buildPath); $output = ''; $status = true; exec($cmd, $output); From 924dadcdcd4d2ef40ffca2795f8cb7ea3c43e833 Mon Sep 17 00:00:00 2001 From: Jimmy Cleuren Date: Fri, 1 Nov 2013 11:22:39 +0100 Subject: [PATCH 117/933] remove the double buildpath --- PHPCI/Plugin/PhpCpd.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) mode change 100755 => 100644 PHPCI/Plugin/PhpCpd.php diff --git a/PHPCI/Plugin/PhpCpd.php b/PHPCI/Plugin/PhpCpd.php old mode 100755 new mode 100644 index 816bf261..bbefd7e6 --- a/PHPCI/Plugin/PhpCpd.php +++ b/PHPCI/Plugin/PhpCpd.php @@ -77,7 +77,7 @@ class PhpCpd implements \PHPCI\Plugin return false; } - $success = $this->phpci->executeCommand($phpcpd . ' %s "%s"', $ignore, $this->phpci->buildPath.$this->path); + $success = $this->phpci->executeCommand($phpcpd . ' %s "%s"', $ignore, $this->path); print $this->phpci->getLastOutput(); From 6b015d31065e2d5e9565012a298f2b14fe57eff3 Mon Sep 17 00:00:00 2001 From: born_free Date: Sat, 2 Nov 2013 16:39:55 -0700 Subject: [PATCH 118/933] fixed XSS in user name and project name properties --- PHPCI/View/Build/view.phtml | 2 +- PHPCI/View/BuildsTable.phtml | 2 +- PHPCI/View/Home/index.phtml | 2 +- PHPCI/View/Project/view.phtml | 4 ++-- PHPCI/View/SummaryTable.phtml | 2 +- PHPCI/View/User/index.phtml | 2 +- PHPCI/View/UserForm.phtml | 2 +- PHPCI/View/layout.phtml | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/PHPCI/View/Build/view.phtml b/PHPCI/View/Build/view.phtml index a9cd569a..aa18e591 100644 --- a/PHPCI/View/Build/view.phtml +++ b/PHPCI/View/Build/view.phtml @@ -7,7 +7,7 @@
Options
From 1f0cd491427b361fd9489ae3fc2f35be43e8ebc4 Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Mon, 23 Feb 2015 19:40:31 +0000 Subject: [PATCH 736/933] Adding changelog. --- changelog.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 changelog.md diff --git a/changelog.md b/changelog.md new file mode 100644 index 00000000..15fd9560 --- /dev/null +++ b/changelog.md @@ -0,0 +1,10 @@ +# PHPCI Changelog + +## v.Next + +### New Features: +- SSH-based Mercurial clones. +- Ability to archive projects. + +### Bug Fixes and Tweaks: + From fba6f2372fffa9cca66afb08e1f5573453042146 Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Mon, 23 Feb 2015 19:45:33 +0000 Subject: [PATCH 737/933] Updating changelog. --- changelog.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/changelog.md b/changelog.md index 15fd9560..541e4a13 100644 --- a/changelog.md +++ b/changelog.md @@ -3,8 +3,8 @@ ## v.Next ### New Features: -- SSH-based Mercurial clones. -- Ability to archive projects. +- SSH-based Mercurial clones (Commit: [e98647bd](https://github.com/Block8/PHPCI/commit/e98647bd97d49741242d252514b8703504a62869), PR: [#812](https://github.com/Block8/PHPCI/pull/812)) +- Ability to archive projects (Commit: [1466ad06](https://github.com/Block8/PHPCI/commit/1466ad06ef708cbab2b53112fc59e8c1d70c2e33), PR: [#771](https://github.com/Block8/PHPCI/pull/771)) ### Bug Fixes and Tweaks: From f4a0804100a1a94bc7175e98cddc38735daff1d6 Mon Sep 17 00:00:00 2001 From: zviryatko Date: Tue, 24 Feb 2015 10:01:36 +0200 Subject: [PATCH 738/933] Fix username style in user panel block. --- PHPCI/View/layout.phtml | 2 +- public/assets/css/AdminLTE-custom.css | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/PHPCI/View/layout.phtml b/PHPCI/View/layout.phtml index 6c384a9e..569772c1 100644 --- a/PHPCI/View/layout.phtml +++ b/PHPCI/View/layout.phtml @@ -134,7 +134,7 @@
<?php print $this->User()->getName(); ?>
-
+

User()->getName()); ?>

diff --git a/public/assets/css/AdminLTE-custom.css b/public/assets/css/AdminLTE-custom.css index a0fb3dd7..b95f7b70 100644 --- a/public/assets/css/AdminLTE-custom.css +++ b/public/assets/css/AdminLTE-custom.css @@ -74,4 +74,14 @@ margin-left: 1px; margin-top: 19px; font-size: 11px; +} + +.user-panel > .image { + padding-right: 15px; +} + +.user-panel > .info { + padding: 0; + font-size: inherit; + line-height: inherit; } \ No newline at end of file From db90f2ea11a76d70e5805c1cab7ddba54a97bd3c Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Wed, 25 Feb 2015 09:36:50 +0000 Subject: [PATCH 739/933] Updating the UpdateCommand to check for a config key rather than a specific file. --- PHPCI/Command/UpdateCommand.php | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/PHPCI/Command/UpdateCommand.php b/PHPCI/Command/UpdateCommand.php index 8170675a..13e31f79 100644 --- a/PHPCI/Command/UpdateCommand.php +++ b/PHPCI/Command/UpdateCommand.php @@ -9,6 +9,7 @@ namespace PHPCI\Command; +use b8\Config; use Monolog\Logger; use PHPCI\Helper\Lang; use Symfony\Component\Console\Command\Command; @@ -59,19 +60,9 @@ class UpdateCommand extends Command protected function verifyInstalled(OutputInterface $output) { - if (!file_exists(PHPCI_DIR . 'PHPCI/config.yml')) { - $output->writeln(''.Lang::get('not_installed').''); - $output->writeln(''.Lang::get('install_instead').''); - return false; - } + $config = Config::getInstance(); + $phpciUrl = $config->get('phpci.url'); - $content = file_get_contents(PHPCI_DIR . 'PHPCI/config.yml'); - if (empty($content)) { - $output->writeln(''.Lang::get('not_installed').''); - $output->writeln(''.Lang::get('install_instead').''); - return false; - } - - return true; + return !empty($phpciUrl); } } From 18ff21174e56874c91cdef02cc62e3c675062e5a Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Wed, 25 Feb 2015 10:33:11 +0000 Subject: [PATCH 740/933] Fixing dates: Stop all dates from appearing as the current date/time. Fixes #820 --- public/assets/js/phpci.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/public/assets/js/phpci.js b/public/assets/js/phpci.js index a2d5954c..0d262235 100644 --- a/public/assets/js/phpci.js +++ b/public/assets/js/phpci.js @@ -9,8 +9,9 @@ var PHPCI = { $(document).ready(function () { // Format datetimes $('time[datetime]').each(function() { - var $this = $(this); - $this.text(moment(this.dateTime).format($this.data('format') || 'lll')); + var thisDate = $(this).attr('datetime'); + var formattedDate = moment(thisDate).format($(this).data('format') || 'lll'); + $(this).text(formattedDate); }); // Update latest builds every 5 seconds: From e423c73c4e0bbe87e1bf1d4506335be23b900662 Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Wed, 25 Feb 2015 14:13:28 +0000 Subject: [PATCH 741/933] Fixing comparison where commit ID is Manual. Fixes #823 --- PHPCI/Model/Build/GithubBuild.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/PHPCI/Model/Build/GithubBuild.php b/PHPCI/Model/Build/GithubBuild.php index 7c8f3f6f..1482f1c0 100644 --- a/PHPCI/Model/Build/GithubBuild.php +++ b/PHPCI/Model/Build/GithubBuild.php @@ -209,7 +209,9 @@ class GithubBuild extends RemoteGitBuild if (!empty($prNumber)) { $builder->executeCommand('cd %s && git diff origin/%s "%s"', $path, $this->getBranch(), $file); } else { - $builder->executeCommand('cd %s && git diff %s^! "%s"', $path, $this->getCommitId(), $file); + $commitId = $this->getCommitId(); + $compare = $commitId == 'Manual' ? 'HEAD' : $commitId; + $builder->executeCommand('cd %s && git diff %s^! "%s"', $path, $compare, $file); } $builder->logExecOutput(true); From 8ab098821b048bc09ef616aae5554f8345d15055 Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Wed, 25 Feb 2015 14:18:05 +0000 Subject: [PATCH 742/933] Updating Settings Controller to use the configured config file, rather than assuming config.yml --- PHPCI/Controller/SettingsController.php | 7 ++++--- bootstrap.php | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/PHPCI/Controller/SettingsController.php b/PHPCI/Controller/SettingsController.php index 3c5ab5f4..a13c9fd4 100644 --- a/PHPCI/Controller/SettingsController.php +++ b/PHPCI/Controller/SettingsController.php @@ -40,7 +40,7 @@ class SettingsController extends Controller parent::init(); $parser = new Parser(); - $yaml = file_get_contents(APPLICATION_PATH . 'PHPCI/config.yml'); + $yaml = file_get_contents(PHPCI_CONFIG_FILE); $this->settings = $parser->parse($yaml); } @@ -76,6 +76,7 @@ class SettingsController extends Controller $authSettings = $this->settings['phpci']['authentication_settings']; } + $this->view->configFile = PHPCI_CONFIG_FILE; $this->view->basicSettings = $this->getBasicForm($basicSettings); $this->view->buildSettings = $this->getBuildForm($buildSettings); $this->view->github = $this->getGithubForm(); @@ -241,7 +242,7 @@ class SettingsController extends Controller { $dumper = new Dumper(); $yaml = $dumper->dump($this->settings, 4); - file_put_contents(APPLICATION_PATH . 'PHPCI/config.yml', $yaml); + file_put_contents(PHPCI_CONFIG_FILE, $yaml); if (error_get_last()) { $error_get_last = error_get_last(); @@ -386,7 +387,7 @@ class SettingsController extends Controller */ protected function canWriteConfig() { - return is_writeable(APPLICATION_PATH . 'PHPCI/config.yml'); + return is_writeable(PHPCI_CONFIG_FILE); } /** diff --git a/bootstrap.php b/bootstrap.php index b15a7e59..252697a8 100644 --- a/bootstrap.php +++ b/bootstrap.php @@ -25,6 +25,8 @@ if (!empty($configEnv)) { $configFile = $configEnv; } +define('PHPCI_CONFIG_FILE', $configFile); + // If we don't have a config file at all, fail at this point and tell the user to install: if (!file_exists($configFile) && (!defined('PHPCI_IS_CONSOLE') || !PHPCI_IS_CONSOLE)) { $message = 'PHPCI has not yet been installed - Please use the command "./console phpci:install" '; From 4a84aad327c1c8b2ac2731d5d72617025bd8ac3a Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Thu, 26 Feb 2015 08:08:12 +0000 Subject: [PATCH 743/933] Updating basic Dockerfile. --- Dockerfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Dockerfile b/Dockerfile index af646d09..5fb6df83 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,4 +15,7 @@ RUN git config --global user.email "hello@php.ci" ADD ./ /phpci +RUN php -r "readfile('https://getcomposer.org/installer');" | php +RUN mv composer.phar /phpci/composer + CMD /phpci/daemonise phpci:daemonise From bfc56a753d9ac3669dfe74df79c2867f0436ec26 Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Thu, 26 Feb 2015 08:08:45 +0000 Subject: [PATCH 744/933] Updating dependencies. --- composer.json | 2 +- composer.lock | 55 ++++++++++++++++++++++----------------------------- 2 files changed, 25 insertions(+), 32 deletions(-) diff --git a/composer.json b/composer.json index 0e5a308b..2eec5e30 100644 --- a/composer.json +++ b/composer.json @@ -47,7 +47,7 @@ "phpunit/phpunit": "~4.0", "phpspec/prophecy-phpunit": "~1.0", "phpmd/phpmd": "~2.0", - "squizlabs/php_codesniffer": "~1.5", + "squizlabs/php_codesniffer": "~2.0", "block8/php-docblock-checker": "~1.0", "phploc/phploc": "~2.0" }, diff --git a/composer.lock b/composer.lock index 4ec183ff..c5abc102 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "b24b68e21f65f3de2b7c3c84a67a0e6f", + "hash": "4ae188c6be1c1388de6271a3b0e0475d", "packages": [ { "name": "block8/b8framework", @@ -255,16 +255,16 @@ }, { "name": "robmorgan/phinx", - "version": "v0.4.2.1", + "version": "v0.4.3", "source": { "type": "git", "url": "https://github.com/robmorgan/phinx.git", - "reference": "1bc1396392d4073b8b29ee5289e445889cbc12b5" + "reference": "0d1f9cb9939f65f506a8a3f5fee356764c310fd4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/robmorgan/phinx/zipball/1bc1396392d4073b8b29ee5289e445889cbc12b5", - "reference": "1bc1396392d4073b8b29ee5289e445889cbc12b5", + "url": "https://api.github.com/repos/robmorgan/phinx/zipball/0d1f9cb9939f65f506a8a3f5fee356764c310fd4", + "reference": "0d1f9cb9939f65f506a8a3f5fee356764c310fd4", "shasum": "" }, "require": { @@ -313,7 +313,7 @@ "migrations", "phinx" ], - "time": "2015-02-08 03:41:44" + "time": "2015-02-23 16:38:12" }, { "name": "swiftmailer/swiftmailer", @@ -935,21 +935,21 @@ }, { "name": "phpspec/prophecy-phpunit", - "version": "v1.0.1", - "target-dir": "Prophecy/PhpUnit", + "version": "v1.1.0", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy-phpunit.git", - "reference": "640c8c3bc9e02d7878e5ed22b1f79818d6bb6caf" + "reference": "0d06c84b9f26aab2b9940354f0fe6037dd9799c3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy-phpunit/zipball/640c8c3bc9e02d7878e5ed22b1f79818d6bb6caf", - "reference": "640c8c3bc9e02d7878e5ed22b1f79818d6bb6caf", + "url": "https://api.github.com/repos/phpspec/prophecy-phpunit/zipball/0d06c84b9f26aab2b9940354f0fe6037dd9799c3", + "reference": "0d06c84b9f26aab2b9940354f0fe6037dd9799c3", "shasum": "" }, "require": { - "phpspec/prophecy": "~1.0" + "php": ">=5.3.3", + "phpspec/prophecy": "~1.3" }, "suggest": { "phpunit/phpunit": "if it is not installed globally" @@ -961,7 +961,7 @@ } }, "autoload": { - "psr-0": { + "psr-4": { "Prophecy\\PhpUnit\\": "" } }, @@ -976,12 +976,12 @@ } ], "description": "PhpUnit test case integrating the Prophecy mocking library", - "homepage": "http://phpspec.org", + "homepage": "http://phpspec.net", "keywords": [ "phpunit", "prophecy" ], - "time": "2014-03-03 23:03:12" + "time": "2015-02-09 11:20:26" }, { "name": "phpunit/php-code-coverage", @@ -1812,46 +1812,39 @@ }, { "name": "squizlabs/php_codesniffer", - "version": "1.5.6", + "version": "2.2.0", "source": { "type": "git", "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "6f3e42d311b882b25b4d409d23a289f4d3b803d5" + "reference": "b301c98f19414d836fdaa678648745fcca5aeb4f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/6f3e42d311b882b25b4d409d23a289f4d3b803d5", - "reference": "6f3e42d311b882b25b4d409d23a289f4d3b803d5", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/b301c98f19414d836fdaa678648745fcca5aeb4f", + "reference": "b301c98f19414d836fdaa678648745fcca5aeb4f", "shasum": "" }, "require": { "ext-tokenizer": "*", "php": ">=5.1.2" }, - "suggest": { - "phpunit/php-timer": "dev-master" - }, "bin": [ - "scripts/phpcs" + "scripts/phpcs", + "scripts/phpcbf" ], "type": "library", - "extra": { - "branch-alias": { - "dev-phpcs-fixer": "2.0.x-dev" - } - }, "autoload": { "classmap": [ "CodeSniffer.php", "CodeSniffer/CLI.php", "CodeSniffer/Exception.php", "CodeSniffer/File.php", + "CodeSniffer/Fixer.php", "CodeSniffer/Report.php", "CodeSniffer/Reporting.php", "CodeSniffer/Sniff.php", "CodeSniffer/Tokens.php", "CodeSniffer/Reports/", - "CodeSniffer/CommentParser/", "CodeSniffer/Tokenizers/", "CodeSniffer/DocGenerators/", "CodeSniffer/Standards/AbstractPatternSniff.php", @@ -1877,13 +1870,13 @@ "role": "lead" } ], - "description": "PHP_CodeSniffer tokenises PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", + "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": "2014-12-04 22:32:15" + "time": "2015-01-21 22:44:05" }, { "name": "symfony/dependency-injection", From ab4396e00d5a47c360fbf5cbe7ac0f5cc98d92d6 Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Thu, 26 Feb 2015 08:31:58 +0000 Subject: [PATCH 745/933] Hopefully fixing a bug where reporting errors back to Github causes an infinite loop. --- PHPCI/Helper/Diff.php | 6 +++++- PHPCI/Model/Build/GithubBuild.php | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/PHPCI/Helper/Diff.php b/PHPCI/Helper/Diff.php index e50c7eaf..5dcb5388 100644 --- a/PHPCI/Helper/Diff.php +++ b/PHPCI/Helper/Diff.php @@ -26,11 +26,15 @@ class Diff */ public function getLinePositions($diff) { + if (empty($diff)) { + return null; + } + $rtn = array(); $diffLines = explode(PHP_EOL, $diff); - while (1) { + while (count($diffLines)) { $line = array_shift($diffLines); if (substr($line, 0, 2) == '@@') { diff --git a/PHPCI/Model/Build/GithubBuild.php b/PHPCI/Model/Build/GithubBuild.php index 1482f1c0..4d61b9d6 100644 --- a/PHPCI/Model/Build/GithubBuild.php +++ b/PHPCI/Model/Build/GithubBuild.php @@ -211,7 +211,7 @@ class GithubBuild extends RemoteGitBuild } else { $commitId = $this->getCommitId(); $compare = $commitId == 'Manual' ? 'HEAD' : $commitId; - $builder->executeCommand('cd %s && git diff %s^! "%s"', $path, $compare, $file); + $builder->executeCommand('cd %s && git diff %s^^ "%s"', $path, $compare, $file); } $builder->logExecOutput(true); From 86b9c05f98e3fa740565e2d45884afecd29eae24 Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Thu, 26 Feb 2015 08:45:42 +0000 Subject: [PATCH 746/933] Fixing PHPCS errors. --- PHPCI/Controller/PluginController.php | 2 +- PHPCI/Controller/WebhookController.php | 6 ++---- PHPCI/Helper/BaseCommandExecutor.php | 1 - PHPCI/Helper/Github.php | 6 ------ PHPCI/Helper/LoginIsDisabled.php | 2 +- PHPCI/Logging/Handler.php | 2 -- PHPCI/Plugin/Phar.php | 1 - PHPCI/Plugin/PhpTalLint.php | 1 - PHPCI/Plugin/Util/Executor.php | 2 -- PHPCI/Plugin/Util/FilesPluginInformation.php | 2 +- bootstrap.php | 8 ++------ vars.php | 6 ++++++ 12 files changed, 13 insertions(+), 26 deletions(-) diff --git a/PHPCI/Controller/PluginController.php b/PHPCI/Controller/PluginController.php index 20675e46..cefcdbb5 100644 --- a/PHPCI/Controller/PluginController.php +++ b/PHPCI/Controller/PluginController.php @@ -126,7 +126,7 @@ class PluginController extends \PHPCI\Controller /** * Convert array to json and save composer.json - * + * * @param $array */ protected function setComposerJson($array) diff --git a/PHPCI/Controller/WebhookController.php b/PHPCI/Controller/WebhookController.php index cef7dc53..619eec16 100644 --- a/PHPCI/Controller/WebhookController.php +++ b/PHPCI/Controller/WebhookController.php @@ -157,6 +157,8 @@ class WebhookController extends \PHPCI\Controller * Handle the payload when Github sends a commit webhook. * @param $project * @param array $payload + * @param b8\Http\Response\JsonResponse $response + * @return b8\Http\Response\JsonResponse */ protected function githubCommitRequest($project, array $payload, b8\Http\Response\JsonResponse $response) { @@ -167,7 +169,6 @@ class WebhookController extends \PHPCI\Controller } try { - if (isset($payload['commits']) && is_array($payload['commits'])) { // If we have a list of commits, then add them all as builds to be tested: @@ -265,13 +266,10 @@ class WebhookController extends \PHPCI\Controller $payload = json_decode($payloadString, true); try { - - // build on merge request events if (isset($payload['object_kind']) && $payload['object_kind'] == 'merge_request') { $attributes = $payload['object_attributes']; if ($attributes['state'] == 'opened' || $attributes['state'] == 'reopened') { - $branch = $attributes['source_branch']; $commit = $attributes['last_commit']; $committer = $commit['author']['email']; diff --git a/PHPCI/Helper/BaseCommandExecutor.php b/PHPCI/Helper/BaseCommandExecutor.php index 4c2f1fa8..5435d597 100644 --- a/PHPCI/Helper/BaseCommandExecutor.php +++ b/PHPCI/Helper/BaseCommandExecutor.php @@ -160,7 +160,6 @@ abstract class BaseCommandExecutor implements CommandExecutor $this->logger->log(Lang::get('looking_for_binary', $bin), LogLevel::DEBUG); if (is_dir($composerBin) && is_file($composerBin.'/'.$bin)) { - $this->logger->log(Lang::get('found_in_path', $composerBin, $bin), LogLevel::DEBUG); $binaryPath = $composerBin . '/' . $bin; break; diff --git a/PHPCI/Helper/Github.php b/PHPCI/Helper/Github.php index 00d959df..67173eb4 100644 --- a/PHPCI/Helper/Github.php +++ b/PHPCI/Helper/Github.php @@ -48,24 +48,18 @@ class Github $res = $http->get($url, $params); foreach ($res['body'] as $item) { - $results[] = $item; - } foreach ($res['headers'] as $header) { - if (preg_match('/^Link: <([^>]+)>; rel="next"/', $header, $r)) { - $host = parse_url($r[1]); parse_str($host['query'], $params); $results = $this->makeRecursiveRequest($host['path'], $params, $results); break; - } - } return $results; diff --git a/PHPCI/Helper/LoginIsDisabled.php b/PHPCI/Helper/LoginIsDisabled.php index d30fcd9f..437b95cd 100644 --- a/PHPCI/Helper/LoginIsDisabled.php +++ b/PHPCI/Helper/LoginIsDisabled.php @@ -20,7 +20,7 @@ use b8\Config; class LoginIsDisabled { /** - * Checks if + * Checks if * @param $method * @param array $params * @return mixed|null diff --git a/PHPCI/Logging/Handler.php b/PHPCI/Logging/Handler.php index ebfa2620..e22351da 100644 --- a/PHPCI/Logging/Handler.php +++ b/PHPCI/Logging/Handler.php @@ -70,7 +70,6 @@ class Handler public function handleError($level, $message, $file, $line) { if (error_reporting() & $level) { - $exception_level = isset($this->levels[$level]) ? $this->levels[$level] : $level; throw new \ErrorException( @@ -140,7 +139,6 @@ class Handler protected function log(\Exception $exception) { if (null !== $this->logger) { - $message = sprintf( '%s: %s (uncaught exception) at %s line %s', get_class($exception), diff --git a/PHPCI/Plugin/Phar.php b/PHPCI/Plugin/Phar.php index f04580e7..0500fddc 100644 --- a/PHPCI/Plugin/Phar.php +++ b/PHPCI/Plugin/Phar.php @@ -227,7 +227,6 @@ class Phar implements \PHPCI\Plugin $success = false; try { - $phar = new PHPPhar($this->getDirectory() . '/' . $this->getFilename(), 0, $this->getFilename()); $phar->buildFromDirectory($this->getPHPCI()->buildPath, $this->getRegExp()); diff --git a/PHPCI/Plugin/PhpTalLint.php b/PHPCI/Plugin/PhpTalLint.php index 146b0479..e12b6c34 100644 --- a/PHPCI/Plugin/PhpTalLint.php +++ b/PHPCI/Plugin/PhpTalLint.php @@ -210,7 +210,6 @@ class PhpTalLint implements PHPCI\Plugin $output = $this->phpci->getLastOutput(); if (preg_match('/Found (.+?) (error|warning)/i', $output, $matches)) { - $rows = explode(PHP_EOL, $output); unset($rows[0]); diff --git a/PHPCI/Plugin/Util/Executor.php b/PHPCI/Plugin/Util/Executor.php index 9482c81f..8c74707e 100644 --- a/PHPCI/Plugin/Util/Executor.php +++ b/PHPCI/Plugin/Util/Executor.php @@ -50,10 +50,8 @@ class Executor // Try and execute it: if ($this->executePlugin($plugin, $options)) { - // Execution was successful: $this->logger->logSuccess(Lang::get('plugin_success')); - } elseif ($stage == 'setup') { // If we're in the "setup" stage, execution should not continue after // a plugin has failed: diff --git a/PHPCI/Plugin/Util/FilesPluginInformation.php b/PHPCI/Plugin/Util/FilesPluginInformation.php index 2593ae22..d5ccebd5 100644 --- a/PHPCI/Plugin/Util/FilesPluginInformation.php +++ b/PHPCI/Plugin/Util/FilesPluginInformation.php @@ -69,7 +69,7 @@ class FilesPluginInformation implements InstalledPluginInformation { return array_map( function (\stdClass $plugin) { - return $plugin->class; + return $plugin->class; }, $this->getInstalledPlugins() ); diff --git a/bootstrap.php b/bootstrap.php index 252697a8..e3f9985e 100644 --- a/bootstrap.php +++ b/bootstrap.php @@ -16,17 +16,13 @@ if (empty($timezone)) { date_default_timezone_set('UTC'); } -// If the PHPCI config file is not where we expect it, try looking in -// env for an alternative config path. $configFile = dirname(__FILE__) . '/PHPCI/config.yml'; - $configEnv = getenv('phpci_config_file'); -if (!empty($configEnv)) { + +if (!empty($configEnv) && file_exists($configEnv)) { $configFile = $configEnv; } -define('PHPCI_CONFIG_FILE', $configFile); - // If we don't have a config file at all, fail at this point and tell the user to install: if (!file_exists($configFile) && (!defined('PHPCI_IS_CONSOLE') || !PHPCI_IS_CONSOLE)) { $message = 'PHPCI has not yet been installed - Please use the command "./console phpci:install" '; diff --git a/vars.php b/vars.php index 2408d41d..aed6b6f8 100644 --- a/vars.php +++ b/vars.php @@ -29,3 +29,9 @@ if (!defined('PHPCI_IS_CONSOLE')) { if (!defined('IS_WIN')) { define('IS_WIN', ((strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') ? true : false)); } + +// If an environment variable is set defining our config location, use that +// otherwise fall back to PHPCI/config.yml. +if (!defined('PHPCI_CONFIG_FILE')) { + define('PHPCI_CONFIG_FILE', $configFile); +} \ No newline at end of file From b8983b23a30b66058be1927c9848f8c851785f05 Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Thu, 26 Feb 2015 08:48:40 +0000 Subject: [PATCH 747/933] Fixing final PHPCS error. --- vars.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vars.php b/vars.php index aed6b6f8..b622ab31 100644 --- a/vars.php +++ b/vars.php @@ -34,4 +34,4 @@ if (!defined('IS_WIN')) { // otherwise fall back to PHPCI/config.yml. if (!defined('PHPCI_CONFIG_FILE')) { define('PHPCI_CONFIG_FILE', $configFile); -} \ No newline at end of file +} From da9be4930dee9607dc661cf65bc4f451903322c6 Mon Sep 17 00:00:00 2001 From: corpsee Date: Sat, 28 Feb 2015 23:13:02 +0600 Subject: [PATCH 748/933] Added total builds count to index and project page --- PHPCI/Controller/HomeController.php | 15 ++++++++++----- PHPCI/Controller/ProjectController.php | 2 +- PHPCI/View/Project/view.phtml | 3 +-- PHPCI/View/SummaryTable.phtml | 2 +- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/PHPCI/Controller/HomeController.php b/PHPCI/Controller/HomeController.php index 2455e709..8b57db58 100644 --- a/PHPCI/Controller/HomeController.php +++ b/PHPCI/Controller/HomeController.php @@ -98,12 +98,16 @@ class HomeController extends \PHPCI\Controller protected function getSummaryHtml($projects) { $summaryBuilds = array(); - $successes = array(); - $failures = array(); + $successes = array(); + $failures = array(); + $counts = array(); foreach ($projects['items'] as $project) { $summaryBuilds[$project->getId()] = $this->buildStore->getLatestBuilds($project->getId()); + $count = $this->buildStore->getWhere(array('project_id' => $project->getId()), 1, 0, array(), array('id' => 'DESC')); + $counts[$project->getId()] = $count['count']; + $success = $this->buildStore->getLastBuildByStatus($project->getId(), Build::STATUS_SUCCESS); $failure = $this->buildStore->getLastBuildByStatus($project->getId(), Build::STATUS_FAILED); @@ -112,10 +116,11 @@ class HomeController extends \PHPCI\Controller } $summaryView = new b8\View('SummaryTable'); - $summaryView->projects = $projects['items']; - $summaryView->builds = $summaryBuilds; + $summaryView->projects = $projects['items']; + $summaryView->builds = $summaryBuilds; $summaryView->successful = $successes; - $summaryView->failed = $failures; + $summaryView->failed = $failures; + $summaryView->counts = $counts; return $summaryView->render(); } diff --git a/PHPCI/Controller/ProjectController.php b/PHPCI/Controller/ProjectController.php index 285982a1..3a55f49b 100644 --- a/PHPCI/Controller/ProjectController.php +++ b/PHPCI/Controller/ProjectController.php @@ -86,7 +86,7 @@ class ProjectController extends PHPCI\Controller $this->view->builds = $builds[0]; $this->view->total = $builds[1]; $this->view->project = $project; - $this->view->branch = urldecode($branch); + $this->view->branch = urldecode($branch); $this->view->branches = $this->projectStore->getKnownBranches($projectId); $this->view->page = $page; $this->view->pages = $pages; diff --git a/PHPCI/View/Project/view.phtml b/PHPCI/View/Project/view.phtml index 0bfd5e59..900349c8 100644 --- a/PHPCI/View/Project/view.phtml +++ b/PHPCI/View/Project/view.phtml @@ -36,8 +36,7 @@
- -

+

()

diff --git a/PHPCI/View/SummaryTable.phtml b/PHPCI/View/SummaryTable.phtml index 56df6e38..9ddaabb0 100644 --- a/PHPCI/View/SummaryTable.phtml +++ b/PHPCI/View/SummaryTable.phtml @@ -123,7 +123,7 @@ foreach($projects as $project): - + (getId()]; ?>) From 9f534711866112429c84494f3d6fb5b5ebb4bc10 Mon Sep 17 00:00:00 2001 From: corpsee Date: Sat, 28 Feb 2015 23:38:56 +0600 Subject: [PATCH 749/933] Added Date column for builds table in project page. --- PHPCI/Languages/lang.da.php | 1 + PHPCI/Languages/lang.de.php | 1 + PHPCI/Languages/lang.el.php | 1 + PHPCI/Languages/lang.en.php | 1 + PHPCI/Languages/lang.fr.php | 1 + PHPCI/Languages/lang.it.php | 1 + PHPCI/Languages/lang.nl.php | 1 + PHPCI/Languages/lang.pl.php | 1 + PHPCI/Languages/lang.ru.php | 1 + PHPCI/Languages/lang.uk.php | 1 + PHPCI/View/BuildsTable.phtml | 1 + PHPCI/View/Project/view.phtml | 1 + 12 files changed, 12 insertions(+) diff --git a/PHPCI/Languages/lang.da.php b/PHPCI/Languages/lang.da.php index 5ba3d96f..fdf27bc6 100644 --- a/PHPCI/Languages/lang.da.php +++ b/PHPCI/Languages/lang.da.php @@ -126,6 +126,7 @@ i din foretrukne hosting-platform.', 'all_branches' => 'Alle branches', 'builds' => 'Builds', 'id' => 'ID', + 'date' => 'Date', 'project' => 'Projekt', 'commit' => 'Commit', 'branch' => 'Branch', diff --git a/PHPCI/Languages/lang.de.php b/PHPCI/Languages/lang.de.php index bdcc1459..a4b83580 100644 --- a/PHPCI/Languages/lang.de.php +++ b/PHPCI/Languages/lang.de.php @@ -127,6 +127,7 @@ generiert. Um es zu verwenden, fügen Sie einfach den folgenden Public Key im Ab 'all_branches' => 'Alle Branches', 'builds' => 'Builds', 'id' => 'ID', + 'date' => 'Date', 'project' => 'Projekt', 'commit' => 'Commit', 'branch' => 'Branch', diff --git a/PHPCI/Languages/lang.el.php b/PHPCI/Languages/lang.el.php index 82b3cf22..bf5649fb 100644 --- a/PHPCI/Languages/lang.el.php +++ b/PHPCI/Languages/lang.el.php @@ -127,6 +127,7 @@ PHPCI', 'all_branches' => 'Όλες οι διακλαδώσεις', 'builds' => 'Κατασκευές', 'id' => 'Αριθμός αναγνώρισης', + 'date' => 'Date', 'project' => 'Έργο', 'commit' => 'Συνεισφορά', 'branch' => 'Διακλάδωση', diff --git a/PHPCI/Languages/lang.en.php b/PHPCI/Languages/lang.en.php index 450be1ef..1e4ffa32 100644 --- a/PHPCI/Languages/lang.en.php +++ b/PHPCI/Languages/lang.en.php @@ -128,6 +128,7 @@ PHPCI', 'all_branches' => 'All Branches', 'builds' => 'Builds', 'id' => 'ID', + 'date' => 'Date', 'project' => 'Project', 'commit' => 'Commit', 'branch' => 'Branch', diff --git a/PHPCI/Languages/lang.fr.php b/PHPCI/Languages/lang.fr.php index 20410684..77e0d0ab 100644 --- a/PHPCI/Languages/lang.fr.php +++ b/PHPCI/Languages/lang.fr.php @@ -127,6 +127,7 @@ PHPCI', 'all_branches' => 'Toutes les branches', 'builds' => 'Builds', 'id' => 'ID', + 'date' => 'Date', 'project' => 'Projet', 'commit' => 'Commit', 'branch' => 'Branche', diff --git a/PHPCI/Languages/lang.it.php b/PHPCI/Languages/lang.it.php index 46ca14fd..50b59040 100644 --- a/PHPCI/Languages/lang.it.php +++ b/PHPCI/Languages/lang.it.php @@ -127,6 +127,7 @@ PHPCI', 'all_branches' => 'Tutti i Branche', 'builds' => 'Builds', 'id' => 'ID', + 'date' => 'Date', 'project' => 'Progetto', 'commit' => 'Commit', 'branch' => 'Branch', diff --git a/PHPCI/Languages/lang.nl.php b/PHPCI/Languages/lang.nl.php index 83d5e317..d62cd4e5 100644 --- a/PHPCI/Languages/lang.nl.php +++ b/PHPCI/Languages/lang.nl.php @@ -127,6 +127,7 @@ van je gekozen source code hosting platform', 'all_branches' => 'Alle brances', 'builds' => 'Builds', 'id' => 'ID', + 'date' => 'Date', 'project' => 'Project', 'commit' => 'Commit', 'branch' => 'Branch', diff --git a/PHPCI/Languages/lang.pl.php b/PHPCI/Languages/lang.pl.php index 9d6de6c8..640ac4e0 100644 --- a/PHPCI/Languages/lang.pl.php +++ b/PHPCI/Languages/lang.pl.php @@ -128,6 +128,7 @@ od wybranego kodu źródłowego platformy hostingowej.', 'all_branches' => 'Wszystkie Gałęzie', 'builds' => 'Budowania', 'id' => 'ID', + 'date' => 'Date', 'project' => 'Projekt', 'commit' => 'Commit', 'branch' => 'Gałąź', diff --git a/PHPCI/Languages/lang.ru.php b/PHPCI/Languages/lang.ru.php index 2f9ab0ee..dba0d934 100644 --- a/PHPCI/Languages/lang.ru.php +++ b/PHPCI/Languages/lang.ru.php @@ -126,6 +126,7 @@ PHPCI', 'all_branches' => 'Все ветки', 'builds' => 'Сборки', 'id' => 'ID', + 'date' => 'Дата', 'project' => 'Проект', 'commit' => 'Коммит', 'branch' => 'Ветка', diff --git a/PHPCI/Languages/lang.uk.php b/PHPCI/Languages/lang.uk.php index 7e4a2191..0fd6d66a 100644 --- a/PHPCI/Languages/lang.uk.php +++ b/PHPCI/Languages/lang.uk.php @@ -126,6 +126,7 @@ PHPCI', 'all_branches' => 'Усі гілки', 'builds' => 'Збірки', 'id' => 'ID', + 'date' => 'Дата', 'project' => 'Проект', 'commit' => 'Комміт', 'branch' => 'Гілка', diff --git a/PHPCI/View/BuildsTable.phtml b/PHPCI/View/BuildsTable.phtml index c9d44930..93ea593b 100644 --- a/PHPCI/View/BuildsTable.phtml +++ b/PHPCI/View/BuildsTable.phtml @@ -39,6 +39,7 @@ switch($build->getStatus()) ?> + + From 000aff9121c7dbeeae9cdd5f217cfd4bf5e8fcd3 Mon Sep 17 00:00:00 2001 From: corpsee Date: Sat, 28 Feb 2015 23:51:04 +0600 Subject: [PATCH 750/933] Code style fix --- PHPCI/Controller/HomeController.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/PHPCI/Controller/HomeController.php b/PHPCI/Controller/HomeController.php index 8b57db58..d0e5a14b 100644 --- a/PHPCI/Controller/HomeController.php +++ b/PHPCI/Controller/HomeController.php @@ -105,7 +105,13 @@ class HomeController extends \PHPCI\Controller foreach ($projects['items'] as $project) { $summaryBuilds[$project->getId()] = $this->buildStore->getLatestBuilds($project->getId()); - $count = $this->buildStore->getWhere(array('project_id' => $project->getId()), 1, 0, array(), array('id' => 'DESC')); + $count = $this->buildStore->getWhere( + array('project_id' => $project->getId()), + 1, + 0, + array(), + array('id' => 'DESC') + ); $counts[$project->getId()] = $count['count']; $success = $this->buildStore->getLastBuildByStatus($project->getId(), Build::STATUS_SUCCESS); From f97089529c332a83a314fefc1d9daf58407b5be4 Mon Sep 17 00:00:00 2001 From: corpsee Date: Mon, 2 Mar 2015 08:18:40 +0600 Subject: [PATCH 751/933] Fixed 'date' it lang string --- PHPCI/Languages/lang.it.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PHPCI/Languages/lang.it.php b/PHPCI/Languages/lang.it.php index 50b59040..b022068c 100644 --- a/PHPCI/Languages/lang.it.php +++ b/PHPCI/Languages/lang.it.php @@ -127,7 +127,7 @@ PHPCI', 'all_branches' => 'Tutti i Branche', 'builds' => 'Builds', 'id' => 'ID', - 'date' => 'Date', + 'date' => 'Data', 'project' => 'Progetto', 'commit' => 'Commit', 'branch' => 'Branch', From d2e6182a2ff847035b86d2e9d46184fa907563bf Mon Sep 17 00:00:00 2001 From: Mark Clements Date: Mon, 2 Mar 2015 22:31:01 +0000 Subject: [PATCH 752/933] Fixed an inconsistency in the way the prompts in the install scripts were being output. All the DB/host fields have a space after the colon, which is much better as it means the text you type is slightly separated from the prompt. However, the admin user fields didn't include this space which was inconsistent and made the install script a little less professional. I have therefore added the missing space for the prompts which didn't have it in the English language file, and have also updated all other language files to also use this format. Most of them followed the same inconsistency as the English version, though some were consistent but without a space, and some which were differently inconsistent (both internally, and between languages). --- PHPCI/Languages/lang.da.php | 16 ++++++++-------- PHPCI/Languages/lang.de.php | 6 +++--- PHPCI/Languages/lang.el.php | 12 ++++++------ PHPCI/Languages/lang.en.php | 6 +++--- PHPCI/Languages/lang.fr.php | 6 +++--- PHPCI/Languages/lang.it.php | 6 +++--- PHPCI/Languages/lang.nl.php | 16 ++++++++-------- PHPCI/Languages/lang.pl.php | 16 ++++++++-------- PHPCI/Languages/lang.ru.php | 6 +++--- PHPCI/Languages/lang.uk.php | 16 ++++++++-------- 10 files changed, 53 insertions(+), 53 deletions(-) diff --git a/PHPCI/Languages/lang.da.php b/PHPCI/Languages/lang.da.php index 5ba3d96f..3f8f2f38 100644 --- a/PHPCI/Languages/lang.da.php +++ b/PHPCI/Languages/lang.da.php @@ -296,15 +296,15 @@ du kører composer update.', Kontrollér venligst nedenstående fejl før du fortsætter.', 'must_be_valid_email' => 'Skal være en gyldig email-adresse.', 'must_be_valid_url' => 'Skal være en gyldig URL.', - 'enter_name' => 'Administrator-navn:', - 'enter_email' => 'Administrators email-adresse:', - 'enter_password' => 'Administrator-adgangskode:', - 'enter_phpci_url' => 'Din PHPCI URL (eksempelvis "http://phpci.local"):', + 'enter_name' => 'Administrator-navn: ', + 'enter_email' => 'Administrators email-adresse: ', + 'enter_password' => 'Administrator-adgangskode: ', + 'enter_phpci_url' => 'Din PHPCI URL (eksempelvis "http://phpci.local"): ', - 'enter_db_host' => 'Indtast dit MySQL-hostnavn [localhost]:', - 'enter_db_name' => 'Indtast dit MySQL database-navn [phpci]:', - 'enter_db_user' => 'Indtast dit MySQL-brugernavn [phpci]:', - 'enter_db_pass' => 'Indtast dit MySQL-password:', + 'enter_db_host' => 'Indtast dit MySQL-hostnavn [localhost]: ', + 'enter_db_name' => 'Indtast dit MySQL database-navn [phpci]: ', + 'enter_db_user' => 'Indtast dit MySQL-brugernavn [phpci]: ', + 'enter_db_pass' => 'Indtast dit MySQL-password: ', 'could_not_connect' => 'PHPCI kunne ikke forbinde til MySQL med de angivning oplysninger. Forsøg igen.', 'setting_up_db' => 'Indlæser database...', 'user_created' => 'Brugerkonto oprettet!', diff --git a/PHPCI/Languages/lang.de.php b/PHPCI/Languages/lang.de.php index bdcc1459..6a8bd5c4 100644 --- a/PHPCI/Languages/lang.de.php +++ b/PHPCI/Languages/lang.de.php @@ -294,9 +294,9 @@ generiert. Um es zu verwenden, fügen Sie einfach den folgenden Public Key im Ab Bitte überprüfen Sie die Fehler, bevor Sie fortfahren,', 'must_be_valid_email' => 'Muss eine gültige Emailadresse sein.', 'must_be_valid_url' => 'Muss eine valide URL sein.', - 'enter_name' => 'Name des Administrators:', - 'enter_email' => 'Emailadresse des Administrators:', - 'enter_password' => 'Passwort des Administrators:', + 'enter_name' => 'Name des Administrators: ', + 'enter_email' => 'Emailadresse des Administrators: ', + 'enter_password' => 'Passwort des Administrators: ', 'enter_phpci_url' => 'Ihre PHPCI-URL (z.B. "http://phpci.local"): ', 'enter_db_host' => 'Bitte geben Sie Ihren MySQL-Host ein [localhost]: ', diff --git a/PHPCI/Languages/lang.el.php b/PHPCI/Languages/lang.el.php index 82b3cf22..e3503c99 100644 --- a/PHPCI/Languages/lang.el.php +++ b/PHPCI/Languages/lang.el.php @@ -297,15 +297,15 @@ Services του Bitbucket αποθετηρίου σας.', Παρακαλούμε διαβάστε τα παραπάνω λάθη πριν συνεχίσετε.', 'must_be_valid_email' => 'Πρέπει να είναι μια έγκυρη διεύθυνση ηλεκτρονικού ταχυδρομείου.', 'must_be_valid_url' => 'Πρέπει να είναι μια έγκυρη διεύθυνση URL.', - 'enter_name' => 'Όνομα διαχειριστή:', - 'enter_email' => 'Ηλ. Διεύθυνση διαχειριστή:', - 'enter_password' => 'Κωδικός πρόσβασης διαχειριστή:', + 'enter_name' => 'Όνομα διαχειριστή: ', + 'enter_email' => 'Ηλ. Διεύθυνση διαχειριστή: ', + 'enter_password' => 'Κωδικός πρόσβασης διαχειριστή: ', 'enter_phpci_url' => 'Ο URL σύνδεσμος σας για το PHPCI ("http://phpci.local" για παράδειγμα): ', - 'enter_db_host' => 'Παρακαλώ εισάγετε τον MySQL οικοδεσπότη σας [localhost]:', + 'enter_db_host' => 'Παρακαλώ εισάγετε τον MySQL οικοδεσπότη σας [localhost]: ', 'enter_db_name' => 'Παρακαλώ εισάγετε το όνομα της MySQL βάσης δεδομένων σας [phpci]: ', - 'enter_db_user' => 'Παρακαλώ εισάγετε το όνομα χρήστη της MySQL σας [phpci]:', - 'enter_db_pass' => 'Παρακαλώ εισάγετε τον κωδικό χρήστη της MySQL σας:', + 'enter_db_user' => 'Παρακαλώ εισάγετε το όνομα χρήστη της MySQL σας [phpci]: ', + 'enter_db_pass' => 'Παρακαλώ εισάγετε τον κωδικό χρήστη της MySQL σας: ', 'could_not_connect' => 'Το PHPCI δεν μπόρεσε να συνδεθεί με την MySQL με τα στοχεία που δώσατε. Παρακαλώ δοκιμάστε ξανά.', 'setting_up_db' => 'Γίνεται ρύθμιση της βάσης δεδομένων σας ...', 'user_created' => 'Λογαριασμός χρήστη δημιουργήθηκε!', diff --git a/PHPCI/Languages/lang.en.php b/PHPCI/Languages/lang.en.php index 450be1ef..2040986d 100644 --- a/PHPCI/Languages/lang.en.php +++ b/PHPCI/Languages/lang.en.php @@ -301,9 +301,9 @@ PHPCI', Please review the errors above before continuing.', 'must_be_valid_email' => 'Must be a valid email address.', 'must_be_valid_url' => 'Must be a valid URL.', - 'enter_name' => 'Admin Name:', - 'enter_email' => 'Admin Email:', - 'enter_password' => 'Admin Password:', + 'enter_name' => 'Admin Name: ', + 'enter_email' => 'Admin Email: ', + 'enter_password' => 'Admin Password: ', 'enter_phpci_url' => 'Your PHPCI URL ("http://phpci.local" for example): ', 'enter_db_host' => 'Please enter your MySQL host [localhost]: ', diff --git a/PHPCI/Languages/lang.fr.php b/PHPCI/Languages/lang.fr.php index 20410684..7d438d2f 100644 --- a/PHPCI/Languages/lang.fr.php +++ b/PHPCI/Languages/lang.fr.php @@ -298,9 +298,9 @@ PHPCI', Merci de corriger les erreurs ci-dessus avant de continuer.', 'must_be_valid_email' => 'Doit être une adresse email valide.', 'must_be_valid_url' => 'Doit être une URL valide.', - 'enter_name' => 'Nom de l\'admin :', - 'enter_email' => 'Email de l\'admin :', - 'enter_password' => 'Mot de passe de l\'admin :', + 'enter_name' => 'Nom de l\'admin: ', + 'enter_email' => 'Email de l\'admin: ', + 'enter_password' => 'Mot de passe de l\'admin: ', 'enter_phpci_url' => 'Votre URL vers PHPCI (par exemple "http://phpci.local"): ', 'enter_db_host' => 'Merci d\'entrer le nom d\'hôte MySQL [localhost]: ', diff --git a/PHPCI/Languages/lang.it.php b/PHPCI/Languages/lang.it.php index 46ca14fd..ea023454 100644 --- a/PHPCI/Languages/lang.it.php +++ b/PHPCI/Languages/lang.it.php @@ -300,9 +300,9 @@ PHPCI', Per favore controlla gli errori riportati prima di proseguire.', 'must_be_valid_email' => 'Deve essere un indirizzo email valido.', 'must_be_valid_url' => 'Deve essere un URL valido.', - 'enter_name' => 'Nome dell\'amministratore:', - 'enter_email' => 'Email dell\'amministratore:', - 'enter_password' => 'Password dell\'amministratore:', + 'enter_name' => 'Nome dell\'amministratore: ', + 'enter_email' => 'Email dell\'amministratore: ', + 'enter_password' => 'Password dell\'amministratore: ', 'enter_phpci_url' => 'L\'URL di PHPCI ("http://phpci.locale" ad esempio): ', 'enter_db_host' => 'Per favore inserisci l\'host MySQL [localhost]: ', diff --git a/PHPCI/Languages/lang.nl.php b/PHPCI/Languages/lang.nl.php index 83d5e317..f118e5b3 100644 --- a/PHPCI/Languages/lang.nl.php +++ b/PHPCI/Languages/lang.nl.php @@ -298,15 +298,15 @@ keer je composer update uitvoert.', Gelieve de fouten na te kijken vooraleer verder te gaan.', 'must_be_valid_email' => 'Moet een geldig e-mailadres zijn.', 'must_be_valid_url' => 'Moet een geldige URL zijn.', - 'enter_name' => 'Administrator naam:', - 'enter_email' => 'Administrator e-mailadres:', - 'enter_password' => 'Administrator wachtwoord:', - 'enter_phpci_url' => 'Je PHPCI URL (bijvoorbeeld "http://phpci.local")', + 'enter_name' => 'Administrator naam: ', + 'enter_email' => 'Administrator e-mailadres: ', + 'enter_password' => 'Administrator wachtwoord: ', + 'enter_phpci_url' => 'Je PHPCI URL (bijvoorbeeld "http://phpci.local"): ', - 'enter_db_host' => 'Vul je MySQL host in [localhost]:', - 'enter_db_name' => 'Vul je MySQL databasenaam in [phpci]:', - 'enter_db_user' => 'Vul je MySQL gebruikersnaam in [phpci]:', - 'enter_db_pass' => 'Vul je MySQL watchtwoord in:', + 'enter_db_host' => 'Vul je MySQL host in [localhost]: ', + 'enter_db_name' => 'Vul je MySQL databasenaam in [phpci]: ', + 'enter_db_user' => 'Vul je MySQL gebruikersnaam in [phpci]: ', + 'enter_db_pass' => 'Vul je MySQL watchtwoord in: ', 'could_not_connect' => 'PHPCI kon met deze gegevens geen verbinding maken met MySQL. Gelieve opnieuw te proberen.', 'setting_up_db' => 'Database wordt aangemaakt...', 'user_created' => 'Gebruikersprofiel aangemaakt!', diff --git a/PHPCI/Languages/lang.pl.php b/PHPCI/Languages/lang.pl.php index 9d6de6c8..c3291d1d 100644 --- a/PHPCI/Languages/lang.pl.php +++ b/PHPCI/Languages/lang.pl.php @@ -299,15 +299,15 @@ wywołaniu polecenia composer update.', Przejrzyj powyższą listę błędów przed kontynuowaniem.', 'must_be_valid_email' => 'Poprawny adres email jest wymagany.', 'must_be_valid_url' => 'Poprawny URL jest wymagany.', - 'enter_name' => 'Imię Admina:', - 'enter_email' => 'Email Admina:', - 'enter_password' => 'Hasło Admina:', - 'enter_phpci_url' => 'URL PHPCI (na przykład "http://phpci.local"):', + 'enter_name' => 'Imię Admina: ', + 'enter_email' => 'Email Admina: ', + 'enter_password' => 'Hasło Admina: ', + 'enter_phpci_url' => 'URL PHPCI (na przykład "http://phpci.local"): ', - 'enter_db_host' => 'Wpisz hosta MySQL [host lokalny]:', - 'enter_db_name' => 'Wpisz nazwę bazy danych MySQL [phpci]:', - 'enter_db_user' => 'Wpisz nazwę użytkownika MySQL [phpci]:', - 'enter_db_pass' => 'Wpisz hasło MySQL:', + 'enter_db_host' => 'Wpisz hosta MySQL [host lokalny]: ', + 'enter_db_name' => 'Wpisz nazwę bazy danych MySQL [phpci]: ', + 'enter_db_user' => 'Wpisz nazwę użytkownika MySQL [phpci]: ', + 'enter_db_pass' => 'Wpisz hasło MySQL: ', 'could_not_connect' => 'Z podanymi ustawieniami PHPCI nie udało się połączyć z MySQL. Spróbuj ponownie.', 'setting_up_db' => 'Ustawianie Twojej bazy danych...', 'user_created' => 'Utworzono konto użytkownika!', diff --git a/PHPCI/Languages/lang.ru.php b/PHPCI/Languages/lang.ru.php index 2f9ab0ee..422f17e1 100644 --- a/PHPCI/Languages/lang.ru.php +++ b/PHPCI/Languages/lang.ru.php @@ -294,9 +294,9 @@ PHPCI', Пожалуйста, просмотрите возникшие ошибки перед тем, как продолжить.', 'must_be_valid_email' => 'Должен быть корректным email-адресом.', 'must_be_valid_url' => 'Должен быть корректным URL-адресом.', - 'enter_name' => 'Имя администратора:', - 'enter_email' => 'Email администратора:', - 'enter_password' => 'Пароль администратора:', + 'enter_name' => 'Имя администратора: ', + 'enter_email' => 'Email администратора: ', + 'enter_password' => 'Пароль администратора: ', 'enter_phpci_url' => 'URL-адрес вашего PHPCI (например: "http://phpci.local"): ', 'enter_db_host' => 'Пожалуйста, введите хост MySQL [localhost]: ', diff --git a/PHPCI/Languages/lang.uk.php b/PHPCI/Languages/lang.uk.php index 7e4a2191..118fae9e 100644 --- a/PHPCI/Languages/lang.uk.php +++ b/PHPCI/Languages/lang.uk.php @@ -298,15 +298,15 @@ PHPCI', Будь ласка, продивіться наявні помилки перед тим, як продовжити.', 'must_be_valid_email' => 'Повинно бути коректною email адресою.', 'must_be_valid_url' => 'Повинно бути коректним URL.', - 'enter_name' => 'Ім’я адміністратора:', - 'enter_email' => 'Email адміністратора:', - 'enter_password' => 'Пароль адміністратора:', - 'enter_phpci_url' => 'URL адреса вашого PHPCI (наприклад, "http://phpci.local"):', + 'enter_name' => 'Ім’я адміністратора: ', + 'enter_email' => 'Email адміністратора: ', + 'enter_password' => 'Пароль адміністратора: ', + 'enter_phpci_url' => 'URL адреса вашого PHPCI (наприклад, "http://phpci.local"): ', - 'enter_db_host' => 'Будь ласка, введіть хост MySQL [localhost]:', - 'enter_db_name' => 'Будь ласка, введить ім’я бази даних MySQL [phpci]:', - 'enter_db_user' => 'Будь ласка, введить ім’я користувача MySQL [phpci]:', - 'enter_db_pass' => 'Будь ласка, введить ваш пароль MySQL:', + 'enter_db_host' => 'Будь ласка, введіть хост MySQL [localhost]: ', + 'enter_db_name' => 'Будь ласка, введить ім’я бази даних MySQL [phpci]: ', + 'enter_db_user' => 'Будь ласка, введить ім’я користувача MySQL [phpci]: ', + 'enter_db_pass' => 'Будь ласка, введить ваш пароль MySQL: ', 'could_not_connect' => 'PHPCI не може підключитися до MySQL із наданими параметрами. Будь ласка, спробуйте ще раз.', 'setting_up_db' => 'Налаштування вашої бази даних...', 'user_created' => 'Аккаунт користувача створено!', From 8a96ec85517caac25864a03306b1efb9d027a882 Mon Sep 17 00:00:00 2001 From: Mark Clements Date: Mon, 2 Mar 2015 22:49:22 +0000 Subject: [PATCH 753/933] Fixed the install script, which bails-out with an error if the PHPCI path contains spaces. This occurs commonly on Windows, but from my reading of the code it would also be a problem on other platforms if spaces were present (though this is less likely, due to different naming conventions). It has been fixed by using escapeshellarg() on both of the paths used in the command. Fixes #698, which I've just noticed has a similar solution suggested in one of the comments, but was closed without anyone actually implementing it. --- PHPCI/Command/InstallCommand.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/PHPCI/Command/InstallCommand.php b/PHPCI/Command/InstallCommand.php index 04e0f2dd..a50eefec 100644 --- a/PHPCI/Command/InstallCommand.php +++ b/PHPCI/Command/InstallCommand.php @@ -323,7 +323,9 @@ class InstallCommand extends Command { $output->write(Lang::get('setting_up_db')); - shell_exec(PHPCI_DIR . 'vendor/bin/phinx migrate -c "' . PHPCI_DIR . 'phinx.php"'); + $phinxBinary = escapeshellarg(PHPCI_DIR . 'vendor/bin/phinx'); + $phinxScript = escapeshellarg(PHPCI_DIR . 'phinx.php'); + shell_exec($phinxBinary . ' migrate -c ' . $phinxScript); $output->writeln(''.Lang::get('ok').''); } From 610d0991a86e6c65bfb9276153070a1e117633b8 Mon Sep 17 00:00:00 2001 From: Nathan Jovin Date: Thu, 5 Mar 2015 00:12:42 -0800 Subject: [PATCH 754/933] Fix issue #840 Technical Debt not storing data nor displaying results in table --- PHPCI/Plugin/TechnicalDebt.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/PHPCI/Plugin/TechnicalDebt.php b/PHPCI/Plugin/TechnicalDebt.php index afe6c920..4d38ec59 100755 --- a/PHPCI/Plugin/TechnicalDebt.php +++ b/PHPCI/Plugin/TechnicalDebt.php @@ -207,5 +207,7 @@ class TechnicalDebt implements PHPCI\Plugin, PHPCI\ZeroConfigPlugin } } } + + return array( $errorCount, $data ); } } From 542d2a35457cc2ebfd12771eeeac875256b9166a Mon Sep 17 00:00:00 2001 From: Thomas Frei Date: Fri, 6 Mar 2015 17:44:22 +0100 Subject: [PATCH 755/933] Remove short array syntax to keep backwards compatibility with php5.3 --- PHPCI/Application.php | 2 +- PHPCI/Controller/SettingsController.php | 6 +++--- PHPCI/Plugin/TechnicalDebt.php | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/PHPCI/Application.php b/PHPCI/Application.php index 8582fc41..8dcdf76d 100644 --- a/PHPCI/Application.php +++ b/PHPCI/Application.php @@ -51,7 +51,7 @@ class Application extends b8\Application return false; }; - $skipAuth = [$this, 'shouldSkipAuth']; + $skipAuth = array($this, 'shouldSkipAuth'); // Handler for the route we're about to register, checks for a valid session where necessary: $routeHandler = function (&$route, Response &$response) use (&$request, $validateSession, $skipAuth) { diff --git a/PHPCI/Controller/SettingsController.php b/PHPCI/Controller/SettingsController.php index a13c9fd4..aaaa637f 100644 --- a/PHPCI/Controller/SettingsController.php +++ b/PHPCI/Controller/SettingsController.php @@ -351,7 +351,7 @@ class SettingsController extends Controller $form->addField($field); $field = new Form\Element\Select('smtp_encryption'); - $field->setOptions(['' => Lang::get('none'), 'tls' => Lang::get('tls'), 'ssl' => Lang::get('ssl')]); + $field->setOptions(array('' => Lang::get('none'), 'tls' => Lang::get('tls'), 'ssl' => Lang::get('ssl'))); $field->setRequired(false); $field->setLabel(Lang::get('use_smtp_encryption')); $field->setContainerClass('form-group'); @@ -406,13 +406,13 @@ class SettingsController extends Controller $field->setLabel(Lang::get('failed_after')); $field->setClass('form-control'); $field->setContainerClass('form-group'); - $field->setOptions([ + $field->setOptions(array( 300 => Lang::get('5_mins'), 900 => Lang::get('15_mins'), 1800 => Lang::get('30_mins'), 3600 => Lang::get('1_hour'), 10800 => Lang::get('3_hours'), - ]); + )); $field->setValue(1800); $form->addField($field); diff --git a/PHPCI/Plugin/TechnicalDebt.php b/PHPCI/Plugin/TechnicalDebt.php index 4d38ec59..eca05213 100755 --- a/PHPCI/Plugin/TechnicalDebt.php +++ b/PHPCI/Plugin/TechnicalDebt.php @@ -152,7 +152,7 @@ class TechnicalDebt implements PHPCI\Plugin, PHPCI\ZeroConfigPlugin { $dirIterator = new \RecursiveDirectoryIterator($this->directory); $iterator = new \RecursiveIteratorIterator($dirIterator, \RecursiveIteratorIterator::SELF_FIRST); - $files = []; + $files = array(); $ignores = $this->ignore; $ignores[] = 'phpci.yml'; @@ -207,7 +207,7 @@ class TechnicalDebt implements PHPCI\Plugin, PHPCI\ZeroConfigPlugin } } } - + return array( $errorCount, $data ); } } From a7b40ce176617156acc1348cba6fe19414cb4658 Mon Sep 17 00:00:00 2001 From: Leszek Date: Sat, 7 Mar 2015 13:26:12 +0000 Subject: [PATCH 756/933] archived --- PHPCI/Languages/lang.pl.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PHPCI/Languages/lang.pl.php b/PHPCI/Languages/lang.pl.php index 9d6de6c8..7b2d8cc7 100644 --- a/PHPCI/Languages/lang.pl.php +++ b/PHPCI/Languages/lang.pl.php @@ -114,7 +114,7 @@ od wybranego kodu źródłowego platformy hostingowej.', (jeśli nie możesz dodać pliku phpci.yml do repozytorium projektu)', 'default_branch' => 'Domyślna nazwa gałęzi', 'allow_public_status' => 'Włączyć status publiczny dla tego projektu?', - 'archived' => 'Archived', + 'archived' => 'W archiwum', 'save_project' => 'Zachowaj Projekt', 'error_mercurial' => 'URL repozytorium Mercurialnego powinno zaczynać się od http:// and https://', From 942127ffe6e8132b5d03123206ab049dab112d8f Mon Sep 17 00:00:00 2001 From: corpsee Date: Fri, 27 Feb 2015 15:56:05 +0600 Subject: [PATCH 757/933] Added default value in profile language select (current language) --- PHPCI/Controller/UserController.php | 1 + 1 file changed, 1 insertion(+) diff --git a/PHPCI/Controller/UserController.php b/PHPCI/Controller/UserController.php index 8d09ecd0..19a7313f 100644 --- a/PHPCI/Controller/UserController.php +++ b/PHPCI/Controller/UserController.php @@ -123,6 +123,7 @@ class UserController extends Controller $lang->setLabel(Lang::get('language')); $lang->setRequired(true); $lang->setOptions(Lang::getLanguageOptions()); + $lang->setValue(Lang::getLanguage()); $form->addField($lang); $submit = new Form\Element\Submit(); From 00b88630fbb4762f3bd32cc856e2238abffba5b9 Mon Sep 17 00:00:00 2001 From: Adirelle Date: Thu, 26 Feb 2015 15:20:44 +0100 Subject: [PATCH 758/933] Display a green border in passing build notifications. --- PHPCI/View/Email/success.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PHPCI/View/Email/success.phtml b/PHPCI/View/Email/success.phtml index a6dfccea..a3a604c5 100644 --- a/PHPCI/View/Email/success.phtml +++ b/PHPCI/View/Email/success.phtml @@ -1,4 +1,4 @@ -
+
getTitle(); ?> - Build #getId(); ?> From 4e8dc7c87b89f541a1753c2d107f5a039fb0661a Mon Sep 17 00:00:00 2001 From: Igor Timoshenko Date: Wed, 25 Feb 2015 16:05:30 +0000 Subject: [PATCH 759/933] Fixed typos in Ukrainian language --- PHPCI/Languages/lang.uk.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/PHPCI/Languages/lang.uk.php b/PHPCI/Languages/lang.uk.php index 7e4a2191..f7ac8627 100644 --- a/PHPCI/Languages/lang.uk.php +++ b/PHPCI/Languages/lang.uk.php @@ -8,7 +8,7 @@ */ $strings = array( - 'language_name' => 'Український', + 'language_name' => 'Українська', 'language' => 'Мова', // Log in: @@ -20,10 +20,10 @@ $strings = array( і вам буде надіслано листа із посиланням на скидання паролю.', 'reset_email_address' => 'Введіть свою email адресу:', 'reset_send_email' => 'Скидання пароля', - 'reset_enter_password' => 'Введіть будь-ласка новий пароль', + 'reset_enter_password' => 'Введіть, будь ласка, новий пароль', 'reset_new_password' => 'Новий пароль:', 'reset_change_password' => 'Змінити пароль', - 'reset_no_user_exists' => 'Не існує користувача з такою email адресою, будь-ласка повторіть знову.', + 'reset_no_user_exists' => 'Не існує користувача з такою email адресою, будь ласка, повторіть знову.', 'reset_email_body' => 'Привіт, %s, Ви отримали цей лист, тому що ви або хтось інший запросили скидання пароля в PHPCI. @@ -62,7 +62,7 @@ PHPCI', 'manage_users' => 'Управління користувачами', 'plugins' => 'Плагіни', 'view' => 'Переглянути', - 'build_now' => 'Збірати', + 'build_now' => 'Зібрати', 'edit_project' => 'Редагувати проект', 'delete_project' => 'Видалити проект', @@ -186,7 +186,7 @@ PHPCI', 'phpunit' => 'PHP Unit', 'file' => 'Файл', - 'line' => 'Строка', + 'line' => 'Рядок', 'class' => 'Клас', 'method' => 'Метод', 'message' => 'Повідомлення', From e75ffe0b763d152438c493ee11341c3c360e45c2 Mon Sep 17 00:00:00 2001 From: corpsee Date: Tue, 10 Mar 2015 15:44:33 +0600 Subject: [PATCH 760/933] Fixed 'date' nl lang string --- PHPCI/Languages/lang.nl.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PHPCI/Languages/lang.nl.php b/PHPCI/Languages/lang.nl.php index d62cd4e5..a4c95b6d 100644 --- a/PHPCI/Languages/lang.nl.php +++ b/PHPCI/Languages/lang.nl.php @@ -127,7 +127,7 @@ van je gekozen source code hosting platform', 'all_branches' => 'Alle brances', 'builds' => 'Builds', 'id' => 'ID', - 'date' => 'Date', + 'date' => 'Datum', 'project' => 'Project', 'commit' => 'Commit', 'branch' => 'Branch', From 33fc50a0b55b69289b343560eabf32150600d525 Mon Sep 17 00:00:00 2001 From: Gustavo Novaro Date: Tue, 10 Mar 2015 15:45:36 -0300 Subject: [PATCH 761/933] Remove blank style tag in header Remove the tag From a188afb0da25503d1800e1959da4d5f75504c273 Mon Sep 17 00:00:00 2001 From: vsguts Date: Thu, 5 Mar 2015 21:25:11 +0300 Subject: [PATCH 762/933] Fixing symlink removal. Closes #854 --- PHPCI/Builder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PHPCI/Builder.php b/PHPCI/Builder.php index d911e04b..619c0b38 100644 --- a/PHPCI/Builder.php +++ b/PHPCI/Builder.php @@ -223,7 +223,7 @@ class Builder implements LoggerAwareInterface if (IS_WIN) { $cmd = 'rmdir /S /Q "%s"'; } - $this->executeCommand($cmd, $this->buildPath); + $this->executeCommand($cmd, rtrim($this->buildPath, '/')); } catch (\Exception $ex) { $this->build->setStatus(Build::STATUS_FAILED); $this->buildLogger->logFailure(Lang::get('exception') . $ex->getMessage()); From 5f2de9a6798ffee196345da183b5f16573aab101 Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Tue, 10 Mar 2015 20:20:54 +0000 Subject: [PATCH 763/933] Update to only build the latest commit from a Github pull request webhook. --- PHPCI/Controller/WebhookController.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/PHPCI/Controller/WebhookController.php b/PHPCI/Controller/WebhookController.php index 619eec16..7e38d60a 100644 --- a/PHPCI/Controller/WebhookController.php +++ b/PHPCI/Controller/WebhookController.php @@ -232,6 +232,11 @@ class WebhookController extends \PHPCI\Controller } foreach ($response['body'] as $commit) { + // Skip all but the current HEAD commit ID: + if ($commit['sha'] != $payload['head']['sha']) { + continue; + } + $branch = str_replace('refs/heads/', '', $payload['pull_request']['base']['ref']); $committer = $commit['commit']['author']['email']; $message = $commit['commit']['message']; From ecc92b5f3eed0e2ea5bc36cf79add5086a42a9f1 Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Wed, 11 Mar 2015 07:48:22 +0000 Subject: [PATCH 764/933] Fixing pull request builds. --- PHPCI/Controller/WebhookController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PHPCI/Controller/WebhookController.php b/PHPCI/Controller/WebhookController.php index 7e38d60a..78824ddb 100644 --- a/PHPCI/Controller/WebhookController.php +++ b/PHPCI/Controller/WebhookController.php @@ -233,7 +233,7 @@ class WebhookController extends \PHPCI\Controller foreach ($response['body'] as $commit) { // Skip all but the current HEAD commit ID: - if ($commit['sha'] != $payload['head']['sha']) { + if ($commit['sha'] != $payload['pull_request']['head']['sha']) { continue; } From 70f0d2516f7c3155a0c04e03179176b844f5005d Mon Sep 17 00:00:00 2001 From: Stephen Ball Date: Fri, 13 Mar 2015 13:31:38 +0000 Subject: [PATCH 765/933] Removed log output so that it matches the other plugins which don't pollute the build log, and to prevent issues with the log output not being escaped --- PHPCI/Plugin/TechnicalDebt.php | 1 - 1 file changed, 1 deletion(-) diff --git a/PHPCI/Plugin/TechnicalDebt.php b/PHPCI/Plugin/TechnicalDebt.php index eca05213..6fdc81a7 100755 --- a/PHPCI/Plugin/TechnicalDebt.php +++ b/PHPCI/Plugin/TechnicalDebt.php @@ -193,7 +193,6 @@ class TechnicalDebt implements PHPCI\Plugin, PHPCI\ZeroConfigPlugin $content = trim($allLines[$lineNumber - 1]); $errorCount++; - $this->phpci->log("Found $search on line $lineNumber of $file:\n$content"); $fileName = str_replace($this->directory, '', $file); $data[] = array( From 9ad0e90fa19dc169bda65e1280c6aee41d223c8d Mon Sep 17 00:00:00 2001 From: Stephen Ball Date: Mon, 16 Mar 2015 11:05:33 +0000 Subject: [PATCH 766/933] Preventing the plugin failing due to an undefined variable --- PHPCI/Plugin/Wipe.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PHPCI/Plugin/Wipe.php b/PHPCI/Plugin/Wipe.php index 1a71e293..86e543b5 100644 --- a/PHPCI/Plugin/Wipe.php +++ b/PHPCI/Plugin/Wipe.php @@ -61,8 +61,8 @@ class Wipe implements \PHPCI\Plugin if (IS_WIN) { $cmd = 'rmdir /S /Q "%s"'; } - $success = $this->phpci->executeCommand($cmd, $this->directory); + return $this->phpci->executeCommand($cmd, $this->directory); } - return $success; + return true; } } From 52b2f87df22e4152dd2e3f5a288c0f4f091b0adf Mon Sep 17 00:00:00 2001 From: Stephen Ball Date: Mon, 16 Mar 2015 11:09:45 +0000 Subject: [PATCH 767/933] Parsing variables in the Wipe plugin --- PHPCI/Plugin/Wipe.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PHPCI/Plugin/Wipe.php b/PHPCI/Plugin/Wipe.php index 1a71e293..7de4b975 100644 --- a/PHPCI/Plugin/Wipe.php +++ b/PHPCI/Plugin/Wipe.php @@ -43,7 +43,7 @@ class Wipe implements \PHPCI\Plugin $path = $phpci->buildPath; $this->phpci = $phpci; $this->build = $build; - $this->directory = isset($options['directory']) ? $options['directory'] : $path; + $this->directory = isset($options['directory']) ? $this->phpci->interpolate($options['directory']) : $path; } /** From fdaaa1ede4a34d54955adb5449c53ce4a2fd0711 Mon Sep 17 00:00:00 2001 From: Stephen Ball Date: Mon, 16 Mar 2015 11:10:03 +0000 Subject: [PATCH 768/933] Parsing variables in the code coverage output directory for PHPUnit --- PHPCI/Plugin/PhpUnit.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PHPCI/Plugin/PhpUnit.php b/PHPCI/Plugin/PhpUnit.php index 70e0e74f..fc597f2d 100644 --- a/PHPCI/Plugin/PhpUnit.php +++ b/PHPCI/Plugin/PhpUnit.php @@ -133,7 +133,7 @@ class PhpUnit implements PHPCI\Plugin, PHPCI\ZeroConfigPlugin } if (isset($options['coverage'])) { - $this->coverage = " --coverage-html {$options['coverage']} "; + $this->coverage = ' --coverage-html ' . $this->phpci->interpolate($options['coverage']) . ' '; } } From 1dd1af2443157e28860ab18708e2d9563960b1bb Mon Sep 17 00:00:00 2001 From: Mark Clements Date: Wed, 18 Mar 2015 09:47:47 +0000 Subject: [PATCH 769/933] Switching tabs to spaces as per style guide. No functional changes. --- PHPCI/Command/InstallCommand.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PHPCI/Command/InstallCommand.php b/PHPCI/Command/InstallCommand.php index a50eefec..d10f34b7 100644 --- a/PHPCI/Command/InstallCommand.php +++ b/PHPCI/Command/InstallCommand.php @@ -323,8 +323,8 @@ class InstallCommand extends Command { $output->write(Lang::get('setting_up_db')); - $phinxBinary = escapeshellarg(PHPCI_DIR . 'vendor/bin/phinx'); - $phinxScript = escapeshellarg(PHPCI_DIR . 'phinx.php'); + $phinxBinary = escapeshellarg(PHPCI_DIR . 'vendor/bin/phinx'); + $phinxScript = escapeshellarg(PHPCI_DIR . 'phinx.php'); shell_exec($phinxBinary . ' migrate -c ' . $phinxScript); $output->writeln(''.Lang::get('ok').''); From 3467e77e7489112abef26c52537106b2c75b1559 Mon Sep 17 00:00:00 2001 From: Adirelle Date: Sun, 8 Mar 2015 17:57:16 +0100 Subject: [PATCH 770/933] Use a CSRF token on the login form to prevent CSRF attacks. --- PHPCI/Controller/SessionController.php | 46 +++++++++++++++++++++----- 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/PHPCI/Controller/SessionController.php b/PHPCI/Controller/SessionController.php index 540b043f..8fa9d48b 100644 --- a/PHPCI/Controller/SessionController.php +++ b/PHPCI/Controller/SessionController.php @@ -43,15 +43,23 @@ class SessionController extends \PHPCI\Controller $isLoginFailure = false; if ($this->request->getMethod() == 'POST') { - $user = $this->userStore->getByEmail($this->getParam('email')); - if ($user && password_verify($this->getParam('password', ''), $user->getHash())) { - $_SESSION['phpci_user_id'] = $user->getId(); - $response = new b8\Http\Response\RedirectResponse(); - $response->setHeader('Location', $this->getLoginRedirect()); - return $response; - } else { + $token = $this->getParam('token'); + if ($token === null || $token !== $_SESSION['login_token']) { $isLoginFailure = true; + } else { + unset($_SESSION['login_token']); + + $user = $this->userStore->getByEmail($this->getParam('email')); + + if ($user && password_verify($this->getParam('password', ''), $user->getHash())) { + $_SESSION['phpci_user_id'] = $user->getId(); + $response = new b8\Http\Response\RedirectResponse(); + $response->setHeader('Location', $this->getLoginRedirect()); + return $response; + } else { + $isLoginFailure = true; + } } } @@ -78,9 +86,15 @@ class SessionController extends \PHPCI\Controller $pwd->setClass('btn-success'); $form->addField($pwd); + $tokenValue = $this->generateToken(); + $_SESSION['login_token'] = $tokenValue; + $token = new b8\Form\Element\Hidden('token'); + $token->setValue($tokenValue); + $form->addField($token); + $this->view->form = $form->render(); $this->view->failed = $isLoginFailure; - + return $this->view->render(); } @@ -180,4 +194,20 @@ class SessionController extends \PHPCI\Controller return $rtn; } + + /** Generate a random token. + * + * @return string + */ + protected function generateToken() + { + if(function_exists('openssl_random_pseudo_bytes')) { + return bin2hex(openssl_random_pseudo_bytes(16)); + } + + return sprintf("%04x", mt_rand(0, 0xFFFF)) + . sprintf("%04x", mt_rand(0, 0xFFFF)) + . sprintf("%04x", mt_rand(0, 0xFFFF)) + . sprintf("%04x", mt_rand(0, 0xFFFF)); + } } From f29ff197c62181d1d7918f7baa768d6d81f19fa4 Mon Sep 17 00:00:00 2001 From: Adirelle Date: Sun, 8 Mar 2015 17:53:27 +0100 Subject: [PATCH 771/933] Generate an new session identifier on successful login to prevent session fixation attacks. --- PHPCI/Controller/SessionController.php | 1 + 1 file changed, 1 insertion(+) diff --git a/PHPCI/Controller/SessionController.php b/PHPCI/Controller/SessionController.php index 8fa9d48b..6ad2681e 100644 --- a/PHPCI/Controller/SessionController.php +++ b/PHPCI/Controller/SessionController.php @@ -53,6 +53,7 @@ class SessionController extends \PHPCI\Controller $user = $this->userStore->getByEmail($this->getParam('email')); if ($user && password_verify($this->getParam('password', ''), $user->getHash())) { + session_regenerate_id(true); $_SESSION['phpci_user_id'] = $user->getId(); $response = new b8\Http\Response\RedirectResponse(); $response->setHeader('Location', $this->getLoginRedirect()); From ea3b0c219afb0ba0e5bc09c4ab21d5990c593323 Mon Sep 17 00:00:00 2001 From: Adirelle Date: Tue, 10 Mar 2015 21:29:10 +0100 Subject: [PATCH 772/933] Code style fixed. --- PHPCI/Controller/SessionController.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/PHPCI/Controller/SessionController.php b/PHPCI/Controller/SessionController.php index 6ad2681e..eb7001df 100644 --- a/PHPCI/Controller/SessionController.php +++ b/PHPCI/Controller/SessionController.php @@ -43,7 +43,6 @@ class SessionController extends \PHPCI\Controller $isLoginFailure = false; if ($this->request->getMethod() == 'POST') { - $token = $this->getParam('token'); if ($token === null || $token !== $_SESSION['login_token']) { $isLoginFailure = true; @@ -202,7 +201,7 @@ class SessionController extends \PHPCI\Controller */ protected function generateToken() { - if(function_exists('openssl_random_pseudo_bytes')) { + if (function_exists('openssl_random_pseudo_bytes')) { return bin2hex(openssl_random_pseudo_bytes(16)); } From d8df6cab4a6311df1e4803364c9af0e5f73e47ff Mon Sep 17 00:00:00 2001 From: LAHAXE Arnaud Date: Tue, 17 Mar 2015 15:49:52 +0100 Subject: [PATCH 773/933] Fix french typo mistake --- PHPCI/Languages/lang.fr.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PHPCI/Languages/lang.fr.php b/PHPCI/Languages/lang.fr.php index 7d438d2f..da2e35b4 100644 --- a/PHPCI/Languages/lang.fr.php +++ b/PHPCI/Languages/lang.fr.php @@ -264,7 +264,7 @@ PHPCI', // Plugins 'cannot_update_composer' => 'PHPCI ne peut pas mettre à jour le fichier composer.json pour vous, il n\'est pas modifiable.', 'x_has_been_removed' => '%s a été supprimé.', - 'x_has_been_added' => '%s a été ajouté au fichier composer.json poru vous et il sera installé la prochaine fois + 'x_has_been_added' => '%s a été ajouté au fichier composer.json pour vous et il sera installé la prochaine fois que vous lancerez "composer update".', 'enabled_plugins' => 'Plugins activés', 'provided_by_package' => 'Fournis par le paquet', From 3a867eb9d56117ff9195cf34b60ff68621fa4893 Mon Sep 17 00:00:00 2001 From: corpsee Date: Mon, 16 Mar 2015 12:43:01 +0600 Subject: [PATCH 774/933] Fixed 'start' string for ru lang Fixed 'from' and 'to' strings for ru lang --- PHPCI/Languages/lang.ru.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/PHPCI/Languages/lang.ru.php b/PHPCI/Languages/lang.ru.php index 422f17e1..ed5a8eeb 100644 --- a/PHPCI/Languages/lang.ru.php +++ b/PHPCI/Languages/lang.ru.php @@ -188,10 +188,10 @@ PHPCI', 'class' => 'Класс', 'method' => 'Метод', 'message' => 'Сообщение', - 'start' => 'Запуск', + 'start' => 'Начало', 'end' => 'Конец', - 'from' => 'От', - 'to' => 'До', + 'from' => 'Из', + 'to' => 'В', 'suite' => 'Комплект', 'test' => 'Тест', 'result' => 'Результат', From d804438a8785760987c6c8ae9811217d136fb246 Mon Sep 17 00:00:00 2001 From: Adirelle Date: Thu, 26 Feb 2015 15:57:45 +0100 Subject: [PATCH 775/933] Use sensiolabs/ansi-to-html to parse the build logs. Added an AnsiConverter helper. Use the AnsiConverter in the email and page templates that display the build log. Use a dedicated stylesheet for the ANSI converter. It can be customized. It can be inlined in the notifications. Do not use ProphecyTestCase when not needed. --- PHPCI/Controller/BuildController.php | 7 +-- PHPCI/Helper/AnsiConverter.php | 55 ++++++++++++++++++++++++ PHPCI/View/Email/success.phtml | 4 +- PHPCI/View/layout.phtml | 1 + Tests/PHPCI/Helper/AnsiConverterTest.php | 20 +++++++++ composer.json | 3 +- composer.lock | 47 +++++++++++++++++++- public/assets/css/ansi-colors.css | 32 ++++++++++++++ public/assets/js/build-plugins/log.js | 2 +- 9 files changed, 162 insertions(+), 9 deletions(-) create mode 100644 PHPCI/Helper/AnsiConverter.php create mode 100644 Tests/PHPCI/Helper/AnsiConverterTest.php create mode 100644 public/assets/css/ansi-colors.css diff --git a/PHPCI/Controller/BuildController.php b/PHPCI/Controller/BuildController.php index bf898d2d..e4660ba7 100644 --- a/PHPCI/Controller/BuildController.php +++ b/PHPCI/Controller/BuildController.php @@ -13,6 +13,7 @@ use b8; use b8\Exception\HttpException\NotFoundException; use b8\Http\Response\JsonResponse; use PHPCI\BuildFactory; +use PHPCI\Helper\AnsiConverter; use PHPCI\Helper\Lang; use PHPCI\Model\Build; use PHPCI\Model\Project; @@ -198,11 +199,7 @@ class BuildController extends \PHPCI\Controller */ protected function cleanLog($log) { - $log = str_replace('[0;32m', '', $log); - $log = str_replace('[0;31m', '', $log); - $log = str_replace('[0m', '', $log); - - return $log; + return AnsiConverter::convert($log); } /** diff --git a/PHPCI/Helper/AnsiConverter.php b/PHPCI/Helper/AnsiConverter.php new file mode 100644 index 00000000..a5e42269 --- /dev/null +++ b/PHPCI/Helper/AnsiConverter.php @@ -0,0 +1,55 @@ +convert($text); + } + + /** + * Do not instanciate this class. + */ + private function __construct() + { + } +} diff --git a/PHPCI/View/Email/success.phtml b/PHPCI/View/Email/success.phtml index a3a604c5..342e3483 100644 --- a/PHPCI/View/Email/success.phtml +++ b/PHPCI/View/Email/success.phtml @@ -1,3 +1,5 @@ + +
@@ -8,7 +10,7 @@

Your commit getCommitId(); ?> genrate a successfull build in project getTitle(); ?>.

getCommitMessage(); ?>

-
getLog(); ?>
+
getLog()); ?>

You can review your commit and the build log.

diff --git a/PHPCI/View/layout.phtml b/PHPCI/View/layout.phtml index 33290fb3..72ef27ff 100644 --- a/PHPCI/View/layout.phtml +++ b/PHPCI/View/layout.phtml @@ -13,6 +13,7 @@ + diff --git a/Tests/PHPCI/Helper/AnsiConverterTest.php b/Tests/PHPCI/Helper/AnsiConverterTest.php new file mode 100644 index 00000000..4d1e8522 --- /dev/null +++ b/Tests/PHPCI/Helper/AnsiConverterTest.php @@ -0,0 +1,20 @@ +This is red !'; + + $actualOutput = AnsiConverter::convert($input); + + $this->assertEquals($expectedOutput, $actualOutput); + } +} diff --git a/composer.json b/composer.json index 2eec5e30..6658bffd 100644 --- a/composer.json +++ b/composer.json @@ -40,7 +40,8 @@ "psr/log": "~1.0", "monolog/monolog": "~1.6", "pimple/pimple": "~1.1", - "robmorgan/phinx": "~0.4" + "robmorgan/phinx": "~0.4", + "sensiolabs/ansi-to-html": "~1.1" }, "require-dev": { diff --git a/composer.lock b/composer.lock index c5abc102..56c28655 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "4ae188c6be1c1388de6271a3b0e0475d", + "hash": "5fc23800ea77b50b496d34f7aa5cf6b3", "packages": [ { "name": "block8/b8framework", @@ -315,6 +315,50 @@ ], "time": "2015-02-23 16:38:12" }, + { + "name": "sensiolabs/ansi-to-html", + "version": "v1.1.0", + "source": { + "type": "git", + "url": "https://github.com/sensiolabs/ansi-to-html.git", + "reference": "92d2ef7ffba5418be060d8ba8adaf7223d741f93" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sensiolabs/ansi-to-html/zipball/92d2ef7ffba5418be060d8ba8adaf7223d741f93", + "reference": "92d2ef7ffba5418be060d8ba8adaf7223d741f93", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "suggest": { + "twig/twig": "Provides nice templating features" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-0": { + "SensioLabs\\AnsiConverter": "." + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "A library to convert a text with ANSI codes to HTML", + "time": "2014-08-01 14:02:39" + }, { "name": "swiftmailer/swiftmailer", "version": "v5.3.1", @@ -2030,6 +2074,7 @@ "minimum-stability": "stable", "stability-flags": [], "prefer-stable": false, + "prefer-lowest": false, "platform": { "php": ">=5.3.8", "ext-pdo": "*", diff --git a/public/assets/css/ansi-colors.css b/public/assets/css/ansi-colors.css new file mode 100644 index 00000000..c3011299 --- /dev/null +++ b/public/assets/css/ansi-colors.css @@ -0,0 +1,32 @@ +.ansi_color_fg_black { color: black } +.ansi_color_bg_black { background-color: black } +.ansi_color_fg_red { color: darkred } +.ansi_color_bg_red { background-color: darkred } +.ansi_color_fg_green { color: green } +.ansi_color_bg_green { background-color: green } +.ansi_color_fg_yellow { color: yellow } +.ansi_color_bg_yellow { background-color: yellow } +.ansi_color_fg_blue { color: blue } +.ansi_color_bg_blue { background-color: blue } +.ansi_color_fg_magenta { color: darkmagenta } +.ansi_color_bg_magenta { background-color: darkmagenta } +.ansi_color_fg_cyan { color: cyan } +.ansi_color_bg_cyan { background-color: cyan } +.ansi_color_fg_white { color: white } +.ansi_color_bg_white { background-color: white } +.ansi_color_fg_brblack { color: black } +.ansi_color_bg_brblack { background-color: black } +.ansi_color_fg_brred { color: red } +.ansi_color_bg_brred { background-color: red } +.ansi_color_fg_brgreen { color: lightgreen } +.ansi_color_bg_brgreen { background-color: lightgreen } +.ansi_color_fg_bryellow { color: lightyellow } +.ansi_color_bg_bryellow { background-color: lightyellow } +.ansi_color_fg_brblue { color: lightblue } +.ansi_color_bg_brblue { background-color: lightblue } +.ansi_color_fg_brmagenta { color: magenta } +.ansi_color_bg_brmagenta { background-color: magenta } +.ansi_color_fg_brcyan { color: lightcyan } +.ansi_color_bg_brcyan { background-color: lightcyan } +.ansi_color_fg_brwhite { color: white } +.ansi_color_bg_brwhite { background-color: white } diff --git a/public/assets/js/build-plugins/log.js b/public/assets/js/build-plugins/log.js index 8798e23c..44ebab2b 100644 --- a/public/assets/js/build-plugins/log.js +++ b/public/assets/js/build-plugins/log.js @@ -8,7 +8,7 @@ var logPlugin = ActiveBuild.UiPlugin.extend({ }, render: function() { - var container = $('
');
+        var container = $('
');
         container.css({height: '300px', 'overflow-y': 'auto'});
         container.html(ActiveBuild.buildData.log);
 

From 2c43cd1cace4dbb9c25142dedc977d7507134018 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nicolpla=C3=A1s?= 
Date: Sun, 1 Mar 2015 19:56:16 -0200
Subject: [PATCH 776/933] Add spanish laguage support

---
 PHPCI/Languages/lang.es.php | 385 ++++++++++++++++++++++++++++++++++++
 1 file changed, 385 insertions(+)
 create mode 100644 PHPCI/Languages/lang.es.php

diff --git a/PHPCI/Languages/lang.es.php b/PHPCI/Languages/lang.es.php
new file mode 100644
index 00000000..7c9f5a62
--- /dev/null
+++ b/PHPCI/Languages/lang.es.php
@@ -0,0 +1,385 @@
+ 'Español',
+    'language' => 'Lenguaje',
+
+    // Log in:
+    'log_in_to_phpci' => 'Ingresar a PHPCI',
+    'login_error' => 'Email o contraseña incorrectos',
+    'forgotten_password_link' => '¿Olvidaste tu contraseña?',
+    'reset_emailed' => 'Te hemos enviado un email para reiniciar tu contraseña.',
+    'reset_header' => '¡No te preocupes!
Solo tienes que ingresar tu dirección de email + y te enviaremos por email un enlace para reiniciar tu contraseña.', + 'reset_email_address' => 'Ingresa tu dirección de email:', + 'reset_send_email' => 'Enviar enlace', + 'reset_enter_password' => 'Ingresa una nueva contraseña', + 'reset_new_password' => 'Nueva contraseña:', + 'reset_change_password' => 'Cambiar contraseña', + 'reset_no_user_exists' => 'No existe ningún usuario con ese email, por favor intenta nuevamente.', + 'reset_email_body' => 'Hola %s, + +Has recibido este correo porque tú, o alguien más, ha solicitado reiniciar la contraseña de PHPCI + +Si fuiste tú, por favor haz click en el siguiente enlace para reiniciar tu contraseña: %ssession/reset-password/%d/%s + +De lo contrario, por favor ignora este correo y ninguna acción será realizada. + +Gracias, + +PHPCI', + + 'reset_email_title' => 'Reiniciar contraseña de PHPCI para %s', + 'reset_invalid' => 'Pedido inválido.', + 'email_address' => 'Dirección de email', + 'password' => 'Contraseña', + 'log_in' => 'Ingresar', + + + // Top Nav + 'toggle_navigation' => 'Activar navegación', + 'n_builds_pending' => '%d builds pendientes', + 'n_builds_running' => '%d builds ejecutándose', + 'edit_profile' => 'Editar Perfil', + 'sign_out' => 'Cerrar Sesión', + 'branch_x' => 'Rama: %s', + 'created_x' => 'Creada el: %s', + 'started_x' => 'Comenzó: %s', + + // Sidebar + 'hello_name' => 'Hola, %s', + 'dashboard' => 'Escritorio', + 'admin_options' => 'Opciones de Admin.', + 'add_project' => 'Agregar Proyecto', + 'settings' => 'Configuración', + 'manage_users' => 'Administrar Usuarios', + 'plugins' => 'Plugins', + 'view' => 'Vista', + 'build_now' => 'Ejecutar Build', + 'edit_project' => 'Editar Proyecto', + 'delete_project' => 'Eliminar Proyecto', + + // Project Summary: + 'no_builds_yet' => '¡No existen builds aún!', + 'x_of_x_failed' => '%d de los últimos %d builds fallaron.', + 'x_of_x_failed_short' => '%d / %d fallaron.', + 'last_successful_build' => ' El último build exitoso fue %s.', + 'never_built_successfully' => ' Este proyecto nunca tuvo un build exitoso.', + 'all_builds_passed' => 'Todos los últimos %d builds pasaron.', + 'all_builds_passed_short' => '%d / %d pasaron.', + 'last_failed_build' => ' El último build en fallar fue %s.', + 'never_failed_build' => ' Este proyecto no tiene ningún build fallido.', + 'view_project' => 'Ver Proyecto', + + // Timeline: + 'latest_builds' => 'Últimos builds', + 'pending' => 'Pediente', + 'running' => 'Ejecutando', + 'success' => 'Éxito', + 'successful' => 'Exitoso', + 'failed' => 'Falló', + 'manual_build' => 'Build Manual', + + // Add/Edit Project: + 'new_project' => 'Nuevo Proyecto', + 'project_x_not_found' => 'El Proyecto con ID %d no existe.', + 'project_details' => 'Detalles del Proyecto', + 'public_key_help' => 'Para facilitarte, hemos generado un par de llaves SSH para que uses en este proyecto. + Para usarlo, sólo agrega la siguiente llave pública a la sección de "deploy keys" + de tu plataforma de hosting de versionado de código.', + 'select_repository_type' => 'Selecciona tipo de repositorio...', + 'github' => 'GitHub', + 'bitbucket' => 'Bitbucket', + 'gitlab' => 'GitLab', + 'remote' => 'URL Remota', + 'local' => 'Path local', + 'hg' => 'Mercurial', + 'svn' => 'Subversion', + + 'where_hosted' => '¿Dónde está alojado tu proyecto?', + 'choose_github' => 'Selecciona un repositorio de GitHub:', + + 'repo_name' => 'Nombre del repositorio / URL (Remoto) o Ruta (Local)', + 'project_title' => 'Titulo del proyecto', + 'project_private_key' => 'Clave privada a usar para acceder al repositorio + (dejar en blanco para remotos locales o anónimos)', + 'build_config' => 'Configuración PHPCI para builds del proyecto + (en caso que no puedas agregar el archivo phpci.yml al repositorio)', + 'default_branch' => 'Nombre de la rama por defecto', + 'allow_public_status' => '¿Activar página pública con el estado del proyecto?', + 'save_project' => 'Guardar Proyecto', + + 'error_mercurial' => 'La URL del repositorio de Mercurial debe comenzar con http:// or https://', + 'error_remote' => 'La URL del repositorio debe comenzar con git://, http:// or https://', + 'error_gitlab' => 'El nombre del repositorio de GitLab debe tener el formato "user@domain.tld:owner/repo.git"', + 'error_github' => 'El nombre del repositorio debe tener el formato "owner/repo"', + 'error_bitbucket' => 'El nombre del repo debe tener el formato "owner/repo"', + 'error_path' => 'La ruta especificada no existe.', + + // View Project: + 'all_branches' => 'Todas las ramas', + 'builds' => 'Builds', + 'id' => 'ID', + 'project' => 'Proyecto', + 'commit' => 'Commit', + 'branch' => 'Rama', + 'status' => 'Estado', + 'prev_link' => '« Anterior', + 'next_link' => 'Siguiente »', + 'public_key' => 'Llave pública', + 'delete_build' => 'Eliminar Build', + + 'webhooks' => 'Webhooks', + 'webhooks_help_github' => 'Para compilar automáticamente este proyecto cada vez que se realiza un commit, agreagar la siguiente URL + como un nuevo "webhook" en la sección Webhooks + and Services de tu repositorio en GitHub.', + + 'webhooks_help_gitlab' => 'Para compilar automáticamente este proyecto, cada vez que se realiza un commit, agreagar la siguiente URL + como una "WebHook URL" en la sección "web hooks" de tu repositorio en GitLab.', + + 'webhooks_help_bitbucket' => 'Para compilar automáticamente este proyecto, cada vez que se realiza un commit, agreagar la siguiente URL + como un servicio "POST" en la sección + + Services de tu repositorio en Bitbucket.', + + // View Build + 'build_x_not_found' => 'El build con ID %d no existe.', + 'build_n' => 'Build %d', + 'rebuild_now' => 'Rebuild Ahora', + + + 'committed_by_x' => 'Commit hecho por %s', + 'commit_id_x' => 'Commit: %s', + + 'chart_display' => 'Este gráfico será mostrado una vez que el build se haya completado.', + + 'build' => 'Build', + 'lines' => 'Líneas', + 'comment_lines' => 'Líneas de comentario', + 'noncomment_lines' => 'Líneas no comentario', + 'logical_lines' => 'Líneas lógicas', + 'lines_of_code' => 'Líneas de código', + 'build_log' => 'Log', + 'quality_trend' => 'Tendencia de calidad', + 'codeception_errors' => 'Errores de Codeception', + 'phpmd_warnings' => 'PHPMD Warnings', + 'phpcs_warnings' => 'PHPCS Warnings', + 'phpcs_errors' => 'PHPCS Errors', + 'phplint_errors' => 'Lint Errors', + 'phpunit_errors' => 'PHPUnit Errors', + 'phpdoccheck_warnings' => 'Docblocks faltantes', + 'issues' => 'Incidencias', + + 'codeception' => 'Codeception', + 'phpcpd' => 'PHP Copy/Paste Detector', + 'phpcs' => 'PHP Code Sniffer', + 'phpdoccheck' => 'Missing Docblocks', + 'phpmd' => 'PHP Mess Detector', + 'phpspec' => 'PHP Spec', + 'phpunit' => 'PHP Unit', + 'technical_debt' => 'Deuda Técnica', + 'behat' => 'Behat', + + 'file' => 'Archivo', + 'line' => 'Línea', + 'class' => 'Clase', + 'method' => 'Método', + 'message' => 'Mensaje', + 'start' => 'Inicio', + 'end' => 'Fin', + 'from' => 'De', + 'to' => 'Para', + 'suite' => 'Suite', + 'test' => 'Test', + 'result' => 'Resultado', + 'ok' => 'OK', + 'took_n_seconds' => 'Tomó %d segundos', + 'build_created' => 'Build Creado', + 'build_started' => 'Build Comenzado', + 'build_finished' => 'Build Terminado', + + // Users + 'name' => 'Nombre', + 'password_change' => 'Contraseña (dejar en blanco si no quiere cambiarla)', + 'save' => 'Guardar »', + 'update_your_details' => 'Actualizar los detalles', + 'your_details_updated' => 'Tus detalles han sido actualizados.', + 'add_user' => 'Agregar Usuario', + 'is_admin' => '¿Es Admin?', + 'yes' => 'Si', + 'no' => 'No', + 'edit' => 'Editar', + 'edit_user' => 'Editar Usuario', + 'delete_user' => 'Delete Usuario', + 'user_n_not_found' => 'Usuario con ID %d no existe.', + 'is_user_admin' => '¿Es un usuario administrador?', + 'save_user' => 'Guardar Usuario', + + // Settings: + 'settings_saved' => 'Tu configuración ha sido guardada.', + 'settings_check_perms' => 'Tu configuración no fue guardada, verificar los permisos del archivo config.yml.', + 'settings_cannot_write' => 'PHPCI no puede escribir en el archivo config.yml file, la configuración no será guardada correctamente + hasta no corregir esto.', + 'settings_github_linked' => 'Tu cuenta GitHub ha sido conectada.', + 'settings_github_not_linked' => 'No se pudo conectar a tu cuenta GitHub.', + 'build_settings' => 'Configuración del Build ', + 'github_application' => 'Aplicación GitHub', + 'github_sign_in' => 'Antes de comenzar a utilizar GitHub, tienes que ingresar y permitir + el acceso a tu cuenta a PHPCI.', + 'github_phpci_linked' => 'PHPCI ha sido conectado a tu cuenta GitHub.', + 'github_where_to_find' => 'Donde encontrar estos...', + 'github_where_help' => 'Si eres priopietario de la aplicaión que quieres usar, puedes encontrar esta información en + el área de configuración de aplicaciones.', + + 'email_settings' => 'Configuraciones de Email', + 'email_settings_help' => 'Para que PHPCI pueda enviar email con el status de los builds, + debes configurar las siguientes propiedades SMTP.', + + 'application_id' => 'ID de aplicación', + 'application_secret' => 'Application Secret', + + 'smtp_server' => 'Servidor SMTP', + 'smtp_port' => 'Puerto SMTP', + 'smtp_username' => 'Usuario SMTP', + 'smtp_password' => 'Contraseña SMTP', + 'from_email_address' => 'Dirección de email DE', + 'default_notification_address' => 'Dirección de correo de notificación por defecto', + 'use_smtp_encryption' => 'Usar encriptación SMTP?', + 'none' => 'None', + 'ssl' => 'SSL', + 'tls' => 'TLS', + + 'failed_after' => 'Considerar el build como fallido luego de ', + '5_mins' => '5 Minutos', + '15_mins' => '15 Minutos', + '30_mins' => '30 Minutos', + '1_hour' => '1 Hora', + '3_hours' => '3 Horas', + + // Plugins + 'cannot_update_composer' => 'PHPCI no puede actualizar composer.json porque no tiene permisos de escritura.', + 'x_has_been_removed' => '%s ha sido elimiando.', + 'x_has_been_added' => '%s ha sido agregado a composer.json y será instalado la próxima vez que ejecutes composer update.', + 'enabled_plugins' => 'Activar Plugins', + 'provided_by_package' => 'Provisto por Paquete', + 'installed_packages' => 'Paquetes Instalados', + 'suggested_packages' => 'Paquetes Sugeridos', + 'title' => 'Título', + 'description' => 'Descripción', + 'version' => 'Versión', + 'install' => 'Instalar »', + 'remove' => 'Eliminar »', + 'search_packagist_for_more' => 'Buscar más paquetes en Packagist', + 'search' => 'Buscar »', + + // Installer + 'installation_url' => 'URL de la instalación PHPCI', + 'db_host' => 'Host', + 'db_name' => 'Nombre de la base de datos', + 'db_user' => 'Usuario de la base de datos', + 'db_pass' => 'Clave de la base de datos', + 'admin_name' => 'Nombre del Admin', + 'admin_pass' => 'Clave del Admin', + 'admin_email' => 'Email de Admin', + 'config_path' => 'Ruta al archivo config', + 'install_phpci' => 'Instalar PHPCI', + 'welcome_to_phpci' => 'Bienvenido a PHPCI', + 'please_answer' => 'Por favor, responde las siguientes preguntas:', + 'phpci_php_req' => 'PHPCI requiere al menos PHP 5.3.8 para funcionar.', + 'extension_required' => 'Extensión requerida: %s', + 'function_required' => 'PHPCI debe poder invocar la función %s(). Está deshabilitada en php.ini?', + 'requirements_not_met' => 'PHPCI no pudo ser instalado, ya que no se cumplen todos los requerimientos. + Por favor, corrige los errores antes de continuar.', + 'must_be_valid_email' => 'Debe ser una dirección de correos válida.', + 'must_be_valid_url' => 'Debe ser una URL válida.', + 'enter_name' => 'Nombre del Admin:', + 'enter_email' => 'Email del Admin:', + 'enter_password' => 'Contraseña de Admin:', + 'enter_phpci_url' => 'La URL de PHPCI ("Por ejemplo: http://phpci.local"): ', + + 'enter_db_host' => 'Por favor, ingresa el servidor MySQL [localhost]: ', + 'enter_db_name' => 'Por favor, ingresa el nombre de la base de datos MySQL [phpci]: ', + 'enter_db_user' => 'Por favor, ingresa el usuario MySQL [phpci]: ', + 'enter_db_pass' => 'Por favor, ingresa la contraseña MySQL: ', + 'could_not_connect' => 'PHPCI no pudo conectarse a MySQL con los datos dados. Por favor, intenta nuevamente.', + 'setting_up_db' => 'Configurando base de datos... ', + 'user_created' => '¡Cuenta de usuario creada!', + 'failed_to_create' => 'PHPCI no pudo crear la cuenta de admin.', + 'config_exists' => 'El archivo config de PHPCI ya existe y no es vacío.', + 'update_instead' => 'Si está intentando actualizar PHPCI, por favor, utiliza phpci:update.', + + // Update + 'update_phpci' => 'Actuliza la base de datos para reflejar los modelos actualizados.', + 'updating_phpci' => 'Actualizando base de datos PHPCI: ', + 'not_installed' => 'PHPCI no está instalado.', + 'install_instead' => 'Por favor, instala PHPCI via phpci:install.', + + // Poll Command + 'poll_github' => 'Chequear en GitHub si se necesita comenzar un Build.', + 'no_token' => 'No se encontró ningún token GitHub', + 'finding_projects' => 'Buscando proyectos para chequear', + 'found_n_projects' => 'Se encontraron %d proyectos', + 'last_commit_is' => 'El último commit en GitHub para %s es %s', + 'adding_new_build' => 'Último commit es diferente a la base de datos, agregando nuevo build.', + 'finished_processing_builds' => 'Fin de procesamiento de builds.', + + // Create Admin + 'create_admin_user' => 'Crear un usuario Admin', + 'incorrect_format' => 'Formato incorrecto', + + // Run Command + 'run_all_pending' => 'Ejecutar todos los builds PHPCI pendientes.', + 'finding_builds' => 'Buscando builds a procesar', + 'found_n_builds' => 'Se encontraron %d builds', + 'skipping_build' => 'Saltando Build %d - Build del proyecto ya en ejecución.', + 'marked_as_failed' => 'Build %d falló debido a timeout.', + + // Builder + 'missing_phpci_yml' => 'Este proyecto no contiene el archivo phpci.yml o está vacío.', + 'build_success' => 'BUILD EXITOSO', + 'build_failed' => 'BUILD FALLIDO', + 'removing_build' => 'Eliminando Build.', + 'exception' => 'Excepción: ', + 'could_not_create_working' => 'Imposible crear copia de trabajo.', + 'working_copy_created' => 'Copia de trabajo creada: %s', + 'looking_for_binary' => 'Buscando binario: %s', + 'found_in_path' => 'Encontrado en %s: %s', + 'running_plugin' => 'EJECUTANDO PLUGIN: %s', + 'plugin_success' => 'PLUGIN: EXITO', + 'plugin_failed' => 'PLUGIN: FALLÓ', + 'plugin_missing' => 'No existe el plugin: %s', + 'tap_version' => 'TapParser únicamente soporta la verisón 13 de TAP', + 'tap_error' => 'Cadena de caracteres TAP inválida, el número de tests no coincide con la cuenta de tests declarada.', + + // Build Plugins: + 'no_tests_performed' => 'No se ejecutaron tests.', + 'could_not_find' => 'No se encontró %s', + 'no_campfire_settings' => 'No se especificaron parámetros de conexión para el plugin Campfire', + 'failed_to_wipe' => 'Imposible eliminar directorio existente %s antes de copiarlo', + 'passing_build' => 'Build Exitoso', + 'failing_build' => 'Build Fallido', + 'log_output' => 'Log de Salida: ', + 'n_emails_sent' => '%d emails enviados.', + 'n_emails_failed' => '%d emails no pudieron ser enviados.', + 'unable_to_set_env' => 'Imposible setear variable de entorno', + 'tag_created' => 'Tag creado por PHPCI: %s', + 'x_built_at_x' => 'Build de %PROJECT_TITLE% en %BUILD_URI%', + 'hipchat_settings' => 'Por favor, definir room y authToken para el plugin hipchat_notify', + 'irc_settings' => 'Debes configurar un servidor, room y apodo.', + 'invalid_command' => 'Comando inválido', + 'import_file_key' => 'Sentencia de importación debe contener una llave \'file\'', + 'cannot_open_import' => 'Imposible abrir archivo de importación SQL: %s', + 'unable_to_execute' => 'Imposible ejecutar archivo SQL', + 'phar_internal_error' => 'Error interno en plugin Phar', + 'build_file_missing' => 'El archivo de build especificado no existe.', + 'property_file_missing' => 'El archivo de propiedades especificado no existe.', + 'could_not_process_report' => 'Imposible procesar el reporte generado por la herramienta.', + 'shell_not_enabled' => 'El plugin shell no está habilitado. Por favor, habilitalo desde config.yml.' +); From 9d4116e3c95e782e127de4a24121af29480b1775 Mon Sep 17 00:00:00 2001 From: Adirelle Date: Tue, 3 Mar 2015 20:10:55 +0100 Subject: [PATCH 777/933] Reworked TapParser to be compliant and more robust. Added another test case from #571. Updated the output of TapParser::processTestLine. Broke TapParser::parse down in simpler methods. TapParser: ignore leading garbage and properly complain on missing TAP log. TapParser: detect and report duplicated TAP log. TapParser: got rid of the "test" and "suite" values. They are only available with PHPUnit. TapParser: append the message from yaml diagnostic to existing message. Reworked the dispaly of test results. PHPUnit plugin: pretty print test data. --- PHPCI/Languages/lang.da.php | 10 +- PHPCI/Languages/lang.de.php | 10 +- PHPCI/Languages/lang.el.php | 10 +- PHPCI/Languages/lang.en.php | 10 +- PHPCI/Languages/lang.fr.php | 10 +- PHPCI/Languages/lang.it.php | 10 +- PHPCI/Languages/lang.nl.php | 10 +- PHPCI/Languages/lang.pl.php | 10 +- PHPCI/Languages/lang.ru.php | 10 +- PHPCI/Languages/lang.uk.php | 10 +- PHPCI/Plugin/Util/TapParser.php | 249 ++++++++++++++++------ Tests/PHPCI/Plugin/Util/TapParserTest.php | 240 ++++++++++++++++++++- public/assets/css/AdminLTE-custom.css | 9 +- public/assets/js/build-plugins/phpunit.js | 94 ++++++-- 14 files changed, 587 insertions(+), 105 deletions(-) diff --git a/PHPCI/Languages/lang.da.php b/PHPCI/Languages/lang.da.php index 4e61c3db..883e52b0 100644 --- a/PHPCI/Languages/lang.da.php +++ b/PHPCI/Languages/lang.da.php @@ -194,14 +194,20 @@ Services sektionen under dit Bitbucket-repository.', 'end' => 'Slut', 'from' => 'Fra', 'to' => 'Til', - 'suite' => 'Suite', - 'test' => 'Test', 'result' => 'Resultat', 'ok' => 'OK', 'took_n_seconds' => 'Tog %d sekunder', 'build_created' => 'Build Oprettet', 'build_started' => 'Build Startet', 'build_finished' => 'Build Afsluttet', + 'test_message' => 'Message', + 'test_no_message' => 'No message', + 'test_success' => 'Succesfull: %d', + 'test_fail' => 'Failures: %d', + 'test_skipped' => 'Skipped: %d', + 'test_error' => 'Errors: %d', + 'test_todo' => 'Todos: %d', + 'test_total' => '%d test(s)', // Users 'name' => 'Navn', diff --git a/PHPCI/Languages/lang.de.php b/PHPCI/Languages/lang.de.php index a04e40e1..62504310 100644 --- a/PHPCI/Languages/lang.de.php +++ b/PHPCI/Languages/lang.de.php @@ -192,14 +192,20 @@ generiert. Um es zu verwenden, fügen Sie einfach den folgenden Public Key im Ab 'end' => 'Ende', 'from' => 'Von', 'to' => 'Bis', - 'suite' => 'Suite', - 'test' => 'Test', 'result' => 'Resultat', 'ok' => 'OK', 'took_n_seconds' => 'Benötigte %d Sekunden', 'build_created' => 'Build erstellt', 'build_started' => 'Build gestartet', 'build_finished' => 'Build abgeschlossen', + 'test_message' => 'Message', + 'test_no_message' => 'No message', + 'test_success' => 'Succesfull: %d', + 'test_fail' => 'Failures: %d', + 'test_skipped' => 'Skipped: %d', + 'test_error' => 'Errors: %d', + 'test_todo' => 'Todos: %d', + 'test_total' => '%d test(s)', // Users 'name' => 'Name', diff --git a/PHPCI/Languages/lang.el.php b/PHPCI/Languages/lang.el.php index 4b1e82ca..5afc6593 100644 --- a/PHPCI/Languages/lang.el.php +++ b/PHPCI/Languages/lang.el.php @@ -194,14 +194,20 @@ Services του Bitbucket αποθετηρίου σας.', 'end' => 'Τέλος', 'from' => 'Από', 'to' => 'Προς', - 'suite' => 'Σουίτα', - 'test' => 'Τέστ', 'result' => 'Αποτέλεσμα', 'ok' => 'ΟΚ', 'took_n_seconds' => 'Χρειάστηκαν %d δευτερόλεπτα', 'build_created' => 'Η κατασκευή δημιουργήθηκε', 'build_started' => 'Η κατασκευή άρχισε', 'build_finished' => 'Η κατασκευή ολοκληρώθηκε', + 'test_message' => 'Message', + 'test_no_message' => 'No message', + 'test_success' => 'Succesfull: %d', + 'test_fail' => 'Failures: %d', + 'test_skipped' => 'Skipped: %d', + 'test_error' => 'Errors: %d', + 'test_todo' => 'Todos: %d', + 'test_total' => '%d test(s)', // Users 'name' => 'Όνομα', diff --git a/PHPCI/Languages/lang.en.php b/PHPCI/Languages/lang.en.php index b23eff7b..393ef584 100644 --- a/PHPCI/Languages/lang.en.php +++ b/PHPCI/Languages/lang.en.php @@ -198,14 +198,20 @@ PHPCI', 'end' => 'End', 'from' => 'From', 'to' => 'To', - 'suite' => 'Suite', - 'test' => 'Test', 'result' => 'Result', 'ok' => 'OK', 'took_n_seconds' => 'Took %d seconds', 'build_created' => 'Build Created', 'build_started' => 'Build Started', 'build_finished' => 'Build Finished', + 'test_message' => 'Message', + 'test_no_message' => 'No message', + 'test_success' => 'Succesfull: %d', + 'test_fail' => 'Failures: %d', + 'test_skipped' => 'Skipped: %d', + 'test_error' => 'Errors: %d', + 'test_todo' => 'Todos: %d', + 'test_total' => '%d test(s)', // Users 'name' => 'Name', diff --git a/PHPCI/Languages/lang.fr.php b/PHPCI/Languages/lang.fr.php index 7be361fc..6965cb06 100644 --- a/PHPCI/Languages/lang.fr.php +++ b/PHPCI/Languages/lang.fr.php @@ -195,14 +195,20 @@ PHPCI', 'end' => 'Fin', 'from' => 'À partir de', 'to' => 'jusque', - 'suite' => 'Suite', - 'test' => 'Test', 'result' => 'Resultat', 'ok' => 'OK', 'took_n_seconds' => 'Exécuté en %d secondes', 'build_created' => 'Build créé', 'build_started' => 'Build démarré', 'build_finished' => 'Build terminé', + 'test_message' => 'Message', + 'test_no_message' => 'Pas de message', + 'test_success' => 'Réussi(s): %d', + 'test_fail' => 'Echec(s): %d', + 'test_skipped' => 'Passé(s): %d', + 'test_error' => 'Erreurs: %d', + 'test_todo' => 'Todos: %d', + 'test_total' => '%d test(s)', // Users 'name' => 'Nom', diff --git a/PHPCI/Languages/lang.it.php b/PHPCI/Languages/lang.it.php index a8a3e13c..db6b23fc 100644 --- a/PHPCI/Languages/lang.it.php +++ b/PHPCI/Languages/lang.it.php @@ -197,14 +197,20 @@ PHPCI', 'end' => 'Finisci', 'from' => 'Da', 'to' => 'A', - 'suite' => 'Suite', - 'test' => 'Test', 'result' => 'Risultati', 'ok' => 'OK', 'took_n_seconds' => 'Sono stati impiegati %d seconds', 'build_created' => 'Build Creata', 'build_started' => 'Build Avviata', 'build_finished' => 'Build Terminata', + 'test_message' => 'Message', + 'test_no_message' => 'No message', + 'test_success' => 'Succesfull: %d', + 'test_fail' => 'Failures: %d', + 'test_skipped' => 'Skipped: %d', + 'test_error' => 'Errors: %d', + 'test_todo' => 'Todos: %d', + 'test_total' => '%d test(s)', // Users 'name' => 'Nome', diff --git a/PHPCI/Languages/lang.nl.php b/PHPCI/Languages/lang.nl.php index 29e8b0f8..38e9466c 100644 --- a/PHPCI/Languages/lang.nl.php +++ b/PHPCI/Languages/lang.nl.php @@ -195,14 +195,20 @@ Services sectie van je Bitbucket repository toegevoegd worden.', 'end' => 'Einde', 'from' => 'Van', 'to' => 'Tot', - 'suite' => 'Suite', - 'test' => 'Test', 'result' => 'Resultaat', 'ok' => 'OK', 'took_n_seconds' => 'Duurde %d seconden', 'build_created' => 'Build aangemaakt', 'build_started' => 'Build gestart', 'build_finished' => 'Build beëindigd', + 'test_message' => 'Message', + 'test_no_message' => 'No message', + 'test_success' => 'Succesfull: %d', + 'test_fail' => 'Failures: %d', + 'test_skipped' => 'Skipped: %d', + 'test_error' => 'Errors: %d', + 'test_todo' => 'Todos: %d', + 'test_total' => '%d test(s)', // Users 'name' => 'Naam', diff --git a/PHPCI/Languages/lang.pl.php b/PHPCI/Languages/lang.pl.php index 816eb009..b4f8430c 100644 --- a/PHPCI/Languages/lang.pl.php +++ b/PHPCI/Languages/lang.pl.php @@ -198,14 +198,20 @@ Services repozytoria Bitbucket.', 'end' => 'Koniec', 'from' => 'Od', 'to' => 'Do', - 'suite' => 'Zestaw ', - 'test' => 'Test', 'result' => 'Wynik', 'ok' => 'OK', 'took_n_seconds' => 'Zajęło %d sekund', 'build_created' => 'Budowanie Stworzone', 'build_started' => 'Budowanie Rozpoczęte', 'build_finished' => 'Budowanie Zakończone', + 'test_message' => 'Message', + 'test_no_message' => 'No message', + 'test_success' => 'Succesfull: %d', + 'test_fail' => 'Failures: %d', + 'test_skipped' => 'Skipped: %d', + 'test_error' => 'Errors: %d', + 'test_todo' => 'Todos: %d', + 'test_total' => '%d test(s)', // Users 'name' => 'Nazwa', diff --git a/PHPCI/Languages/lang.ru.php b/PHPCI/Languages/lang.ru.php index 44ed1550..931c8910 100644 --- a/PHPCI/Languages/lang.ru.php +++ b/PHPCI/Languages/lang.ru.php @@ -193,14 +193,20 @@ PHPCI', 'end' => 'Конец', 'from' => 'Из', 'to' => 'В', - 'suite' => 'Комплект', - 'test' => 'Тест', 'result' => 'Результат', 'ok' => 'OK', 'took_n_seconds' => 'Заняло секунд: %d', 'build_created' => 'Сборка создана', 'build_started' => 'Сборка запущена', 'build_finished' => 'Сборка окончена', + 'test_message' => 'Message', + 'test_no_message' => 'No message', + 'test_success' => 'Succesfull: %d', + 'test_fail' => 'Failures: %d', + 'test_skipped' => 'Skipped: %d', + 'test_error' => 'Errors: %d', + 'test_todo' => 'Todos: %d', + 'test_total' => '%d test(s)', // Users 'name' => 'Имя', diff --git a/PHPCI/Languages/lang.uk.php b/PHPCI/Languages/lang.uk.php index 3e96459e..b080c65a 100644 --- a/PHPCI/Languages/lang.uk.php +++ b/PHPCI/Languages/lang.uk.php @@ -195,14 +195,20 @@ PHPCI', 'end' => 'Кінець', 'from' => 'Від', 'to' => 'До', - 'suite' => 'Комплект', - 'test' => 'Тест', 'result' => 'Результат', 'ok' => 'OK', 'took_n_seconds' => 'Зайняло %d секунд', 'build_created' => 'Збірка створена', 'build_started' => 'Збірка розпочата', 'build_finished' => 'Збірка завершена', + 'test_message' => 'Message', + 'test_no_message' => 'No message', + 'test_success' => 'Succesfull: %d', + 'test_fail' => 'Failures: %d', + 'test_skipped' => 'Skipped: %d', + 'test_error' => 'Errors: %d', + 'test_todo' => 'Todos: %d', + 'test_total' => '%d test(s)', // Users 'name' => 'Ім’я', diff --git a/PHPCI/Plugin/Util/TapParser.php b/PHPCI/Plugin/Util/TapParser.php index 57b92b55..18772a6a 100644 --- a/PHPCI/Plugin/Util/TapParser.php +++ b/PHPCI/Plugin/Util/TapParser.php @@ -2,7 +2,9 @@ namespace PHPCI\Plugin\Util; +use Exception; use PHPCI\Helper\Lang; +use Symfony\Component\Yaml\Yaml; /** * Processes TAP format strings into usable test result data. @@ -10,18 +12,41 @@ use PHPCI\Helper\Lang; */ class TapParser { - const TEST_COUNTS_PATTERN = '/([0-9]+)\.\.([0-9]+)/'; - const TEST_LINE_PATTERN = '/(ok|not ok)\s+[0-9]+\s+\-\s+([^\n]+)::([^\n]+)/'; - const TEST_MESSAGE_PATTERN = '/message\:\s+\'([^\']+)\'/'; - const TEST_COVERAGE_PATTERN = '/Generating code coverage report/'; - const TEST_SKIP_PATTERN = '/ok\s+[0-9]+\s+\-\s+#\s+SKIP/'; + const TEST_COUNTS_PATTERN = '/^\d+\.\.(\d+)/'; + const TEST_LINE_PATTERN = '/^(ok|not ok)(?:\s+\d+)?(?:\s+\-)?\s*(.*?)(?:\s*#\s*(skip|todo)\s*(.*))?\s*$/i'; + const TEST_YAML_START = '/^(\s*)---/'; + const TEST_DIAGNOSTIC = '/^#/'; /** * @var string */ protected $tapString; + + /** + * @var int + */ protected $failures = 0; + /** + * @var array + */ + protected $lines; + + /** + * @var integer + */ + protected $lineNumber; + + /** + * @var integer + */ + protected $testCount; + + /** + * @var array + */ + protected $results; + /** * Create a new TAP parser for a given string. * @param string $tapString The TAP format string to be parsed. @@ -38,81 +63,175 @@ class TapParser { // Split up the TAP string into an array of lines, then // trim all of the lines so there's no leading or trailing whitespace. - $lines = explode("\n", $this->tapString); - $lines = array_map(function ($line) { - return trim($line); - }, $lines); + $this->lines = array_map('rtrim', explode("\n", $this->tapString)); + $this->lineNumber = 0; - // Check TAP version: - $versionLine = array_shift($lines); + $this->testCount = false; + $this->results = array(); - if ($versionLine != 'TAP version 13') { - throw new \Exception(Lang::get('tap_version')); + $header = $this->findTapLog(); + + $line = $this->nextLine(); + if ($line === $header) { + throw new Exception("Duplicated TAP log, please check the configration."); } - if (isset($lines[count($lines) - 1]) && preg_match(self::TEST_COVERAGE_PATTERN, $lines[count($lines) - 1])) { - array_pop($lines); - if ($lines[count($lines) - 1] == "") { - array_pop($lines); + while ($line !== false && ($this->testCount === false || count($this->results) < $this->testCount)) { + $this->parseLine($line); + $line = $this->nextLine(); + } + + if (count($this->results) !== $this->testCount) { + throw new Exception(Lang::get('tap_error')); + } + + return $this->results; + } + + /** Looks for the start of the TAP log in the string. + * + * @return string The TAP header line. + * + * @throws Exception if no TAP log is found or versions mismatch. + */ + protected function findTapLog() + { + // Look for the beggning of the TAP output + do { + $header = $this->nextLine(); + } while ($header !== false && substr($header, 0, 12) !== 'TAP version '); + + // + if ($header === false) { + throw new Exception('No TAP log found, please check the configuration.'); + } elseif ($header !== 'TAP version 13') { + throw new Exception(Lang::get('tap_version')); + } + + return $header; + } + + /** Fetch the next line. + * + * @return string|false The next line or false if the end has been reached. + */ + protected function nextLine() + { + if ($this->lineNumber < count($this->lines)) { + return $this->lines[$this->lineNumber++]; + } + return false; + } + + /** Parse a single line. + * + * @param string $line + */ + protected function parseLine($line) + { + if (preg_match(self::TEST_COUNTS_PATTERN, $line, $matches)) { + $this->testCount = intval($matches[1]); + + } elseif (preg_match(self::TEST_DIAGNOSTIC, $line)) { + return; + + } elseif (preg_match(self::TEST_LINE_PATTERN, $line, $matches)) { + $this->results[] = $this->processTestLine( + $matches[1], + isset($matches[2]) ? $matches[2] : '', + isset($matches[3]) ? $matches[3] : null, + isset($matches[4]) ? $matches[4] : null + ); + + } elseif (preg_match(self::TEST_YAML_START, $line, $matches)) { + $diagnostic = $this->processYamlBlock($matches[1]); + $test = array_pop($this->results); + if (isset($test['message'], $diagnostic['message'])) { + $test['message'] .= PHP_EOL . $diagnostic['message']; + unset($diagnostic['message']); } + $this->results[] = array_replace($test, $diagnostic); + + } else { + throw new Exception(sprintf('Incorrect TAP data, line %d: %s', $this->lineNumber, $line)); } - - $matches = array(); - $totalTests = 0; - if (preg_match(self::TEST_COUNTS_PATTERN, $lines[0], $matches)) { - array_shift($lines); - $totalTests = (int) $matches[2]; - } - - if (isset($lines[count($lines) - 1]) && - preg_match(self::TEST_COUNTS_PATTERN, $lines[count($lines) - 1], $matches)) { - array_pop($lines); - $totalTests = (int) $matches[2]; - } - - $rtn = $this->processTestLines($lines); - - if ($totalTests != count($rtn)) { - throw new \Exception(Lang::get('tap_error')); - } - - return $rtn; } /** - * Process the individual test lines within a TAP string. - * @param $lines + * Process an individual test line. + * + * @param string $result + * @param string $message + * @param string $directive + * @param string $reason + * * @return array */ - protected function processTestLines($lines) + protected function processTestLine($result, $message, $directive, $reason) { - $rtn = array(); + $test = array( + 'pass' => true, + 'message' => $message, + 'severity' => 'success', + ); - foreach ($lines as $line) { - $matches = array(); - - if (preg_match(self::TEST_LINE_PATTERN, $line, $matches)) { - $ok = ($matches[1] == 'ok' ? true : false); - - if (!$ok) { - $this->failures++; - } - - $item = array( - 'pass' => $ok, - 'suite' => $matches[2], - 'test' => $matches[3], - ); - - $rtn[] = $item; - } elseif (preg_match(self::TEST_SKIP_PATTERN, $line, $matches)) { - $rtn[] = array('message' => 'SKIP'); - } elseif (preg_match(self::TEST_MESSAGE_PATTERN, $line, $matches)) { - $rtn[count($rtn) - 1]['message'] = $matches[1]; - } + if ($result !== 'ok') { + $test['pass'] = false; + $test['severity'] = substr($message, 0, 6) === 'Error:' ? 'error' : 'fail'; + $this->failures++; } - return $rtn; + if ($directive) { + $test = $this->processDirective($test, $directive, $reason); + } + + return $test; + } + + /** Process an indented Yaml block. + * + * @param string $indent The block indentation to ignore. + * + * @return array The processed Yaml content. + */ + protected function processYamlBlock($indent) + { + $startLine = $this->lineNumber+1; + $endLine = $indent.'...'; + $yamlLines = array(); + do { + $line = $this->nextLine(); + if ($line === false) { + throw new Exception(Lang::get('tap_error_endless_yaml', $startLine)); + } elseif ($line === $endLine) { + break; + } + $yamlLines[] = substr($line, strlen($indent)); + + } while (true); + + return Yaml::parse(join("\n", $yamlLines)); + } + + /** Process a TAP directive + * + * @param array $test + * @param string $directive + * @param string $reason + * @return array + */ + protected function processDirective($test, $directive, $reason) + { + $test['severity'] = strtolower($directive) === 'skip' ? 'skipped' : 'todo'; + + if (!empty($reason)) { + if (!empty($test['message'])) { + $test['message'] .= ', '.$test['severity'].': '; + } + $test['message'] .= $reason; + } + + return $test; } /** diff --git a/Tests/PHPCI/Plugin/Util/TapParserTest.php b/Tests/PHPCI/Plugin/Util/TapParserTest.php index c4438b79..75c04bfa 100644 --- a/Tests/PHPCI/Plugin/Util/TapParserTest.php +++ b/Tests/PHPCI/Plugin/Util/TapParserTest.php @@ -5,22 +5,254 @@ use PHPCI\Plugin\Util\TapParser; class TapParserTest extends \PHPUnit_Framework_TestCase { - public function testSkipped() + public function testSimple() + { + $content = <<parse(); + + $this->assertEquals(array( + array('pass' => true, 'severity' => 'success', 'message' => 'SomeTest::testAnother'), + array('pass' => false, 'severity' => 'fail', 'message' => ''), + ), $result); + + $this->assertEquals(1, $parser->getTotalFailures()); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessageRegExp /No TAP/ + */ + public function testNoTapData() + { + $content = <<parse(); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessageRegExp /Duplicated TAP/ + */ + public function testDuplicateOutput() + { + $content = <<parse(); + } + + public function testSuiteAndTest() { $content = <<parse(); + + $this->assertEquals(array( + array('pass' => true, 'severity' => 'success', 'message' => 'SomeTest::testAnother',), + array('pass' => false, 'severity' => 'fail', 'message' => 'Failure: SomeTest::testAnother'), + array('pass' => false, 'severity' => 'error', 'message' => 'Error: SomeTest::testAnother'), + ), $result); + + $this->assertEquals(2, $parser->getTotalFailures()); + } + + + public function testSkipped() + { + $content = <<parse(); + + $this->assertEquals(array( + array('pass' => true, 'severity' => 'skipped', 'message' => ''), + array('pass' => true, 'severity' => 'skipped', 'message' => 'foobar'), + array('pass' => true, 'severity' => 'skipped', 'message' => 'foo, skipped: bar'), + ), $result); + + $this->assertEquals(0, $parser->getTotalFailures()); + } + + public function testTodo() + { + $content = <<parse(); + + $this->assertEquals(array( + array('pass' => true, 'severity' => 'todo', 'message' => 'SomeTest::testAnother, todo: really implement this test'), + array('pass' => true, 'severity' => 'todo', 'message' => 'really implement this test'), + array('pass' => true, 'severity' => 'todo', 'message' => 'this is a message, todo: really implement this test'), + array('pass' => true, 'severity' => 'todo', 'message' => ''), + ), $result); + + $this->assertEquals(0, $parser->getTotalFailures()); + } + + public function testYamlDiagnostic() + { + // From https://phpunit.de/manual/current/en/logging.html#logging.tap + $content = <<parse(); + + $this->assertEquals(array( + array( + 'pass' => false, + 'severity' => 'fail', + 'message' => 'FOO' . PHP_EOL . 'BAR', + ), + ), $result); + + $this->assertEquals(1, $parser->getTotalFailures()); + } + + public function testFailureAndError() + { + // From https://phpunit.de/manual/current/en/logging.html#logging.tap + $content = <<parse(); $this->assertEquals(array( - array('pass' => true, 'suite' => 'SomeTest', 'test' => 'testAnother'), - array('message' => 'SKIP'), + array( + 'pass' => false, + 'severity' => 'fail', + 'message' => 'Failure: testFailure::FailureErrorTest', + ), + array( + 'pass' => false, + 'severity' => 'error', + 'message' => 'Error: testError::FailureErrorTest', + ) ), $result); + $this->assertEquals(2, $parser->getTotalFailures()); + } + + /** + * @expectedException \Exception + */ + public function testGarbage() + { + $content = "Garbage !"; + + $parser = new TapParser($content); + $parser->parse(); + } + + /** + * @expectedException \Exception + */ + public function testInvalidTestCount() + { + $content = <<parse(); + } + + /** + * @expectedException \Exception + */ + public function testEndlessYaml() + { + $content = <<parse(); + } + + public function testCodeception() + { + $content = <<< TAP +TAP version 13 +ok 1 - try to access the dashboard as a guest (Auth/GuestAccessDashboardAndRedirectCept) +ok 2 - see the login page (Auth/SeeLoginCept) +ok 3 - click forgot password and see the email form (Auth/SeeLoginForgotPasswordCept) +ok 4 - see powered by runmybusiness branding (Auth/ShouldSeePoweredByBrandingCept) +ok 5 - submit invalid credentials (Auth/SubmitLoginAndFailCept) +ok 6 - submit valid credentials and see the dashboard (Auth/SubmitLoginAndSucceedCept) +1..6 +TAP; + + $parser = new TapParser($content); + $result = $parser->parse(); + + $this->assertEquals( + array( + array('pass' => true, 'severity' => 'success', 'message' => 'try to access the dashboard as a guest (Auth/GuestAccessDashboardAndRedirectCept)'), + array('pass' => true, 'severity' => 'success', 'message' => 'see the login page (Auth/SeeLoginCept)'), + array('pass' => true, 'severity' => 'success', 'message' => 'click forgot password and see the email form (Auth/SeeLoginForgotPasswordCept)'), + array('pass' => true, 'severity' => 'success', 'message' => 'see powered by runmybusiness branding (Auth/ShouldSeePoweredByBrandingCept)'), + array('pass' => true, 'severity' => 'success', 'message' => 'submit invalid credentials (Auth/SubmitLoginAndFailCept)'), + array('pass' => true, 'severity' => 'success', 'message' => 'submit valid credentials and see the dashboard (Auth/SubmitLoginAndSucceedCept)'), + ), + $result + ); + $this->assertEquals(0, $parser->getTotalFailures()); + } } diff --git a/public/assets/css/AdminLTE-custom.css b/public/assets/css/AdminLTE-custom.css index b95f7b70..e37a0f48 100644 --- a/public/assets/css/AdminLTE-custom.css +++ b/public/assets/css/AdminLTE-custom.css @@ -84,4 +84,11 @@ padding: 0; font-size: inherit; line-height: inherit; -} \ No newline at end of file +} + +#phpunit-data th div { margin: 0 0.5em; } +#phpunit-data .success td { background: none; color: #00a65a; } +#phpunit-data .fail td { background: none; color: #f56954; } +#phpunit-data .error td { background: none; color: #f56954; } +#phpunit-data .skipped td { background: none; color: #e08e0b; } +#phpunit-data .todo td { background: none; color: #00c0ef; } diff --git a/public/assets/js/build-plugins/phpunit.js b/public/assets/js/build-plugins/phpunit.js index 4a55d905..66f010a1 100644 --- a/public/assets/js/build-plugins/phpunit.js +++ b/public/assets/js/build-plugins/phpunit.js @@ -6,6 +6,13 @@ var phpunitPlugin = ActiveBuild.UiPlugin.extend({ displayOnUpdate: false, box: true, rendered: false, + statusMap: { + success : 'ok', + fail: 'remove', + error: 'warning-sign', + todo: 'info-sign', + skipped: 'exclamation-sign' + }, register: function() { var self = this; @@ -21,6 +28,11 @@ var phpunitPlugin = ActiveBuild.UiPlugin.extend({ query(); } }); + + $(document).on('click', '#phpunit-data .test-toggle', function(ev) { + var input = $(ev.target); + $('#phpunit-data tbody ' + input.data('target')).toggle(input.prop('checked')); + }); }, render: function() { @@ -28,7 +40,7 @@ var phpunitPlugin = ActiveBuild.UiPlugin.extend({ return $('
#getId(), 6, '0', STR_PAD_LEFT); ?>getCreated()->format('Y-m-d H:i:s'); ?> getProject())) { diff --git a/PHPCI/View/Project/view.phtml b/PHPCI/View/Project/view.phtml index 900349c8..2e79c017 100644 --- a/PHPCI/View/Project/view.phtml +++ b/PHPCI/View/Project/view.phtml @@ -42,6 +42,7 @@
' + '' + '' + - ' ' + + ' ' + '' + '
'+Lang.get('test')+''+Lang.get('test_message')+'
'); }, @@ -43,7 +55,9 @@ var phpunitPlugin = ActiveBuild.UiPlugin.extend({ this.lastData = e.queryData; var tests = this.lastData[0].meta_value; + var thead = $('#phpunit-data thead tr'); var tbody = $('#phpunit-data tbody'); + thead.empty().append(''+Lang.get('test_message')+''); tbody.empty(); if (tests.length == 0) { @@ -51,24 +65,74 @@ var phpunitPlugin = ActiveBuild.UiPlugin.extend({ return; } + var counts = { success: 0, fail: 0, error: 0, skipped: 0, todo: 0 }, total = 0; + for (var i in tests) { - - var row = $('' + - ''+tests[i].suite+'' + - '::'+tests[i].test+'
' + - ''+(tests[i].message || '')+'' + - ''); - - if (!tests[i].pass) { - row.addClass('danger'); - } else { - row.addClass('success'); - } - - tbody.append(row); + var severity = tests[i].severity || 'success', + message = tests[i].message || ('' + Lang.get('test_no_message') + ''); + counts[severity]++; + total++; + tbody.append( + '' + + '' + + '
' + message + '
' + + (tests[i].data ? '
' + this.repr(tests[i].data) + '
' : '') + + '' + + '' + ); } + var checkboxes = $(''); + thead.append(checkboxes).append('' + Lang.get('test_total', total) + ''); + + for (var key in counts) { + var count = counts[key]; + if(count > 0) { + checkboxes.append( + '
 ' + + Lang.get('test_'+key, count)+ '
' + ); + } + } + + tbody.find('.success').hide(); + $('#build-phpunit-errors').show(); + }, + + repr: function(data) + { + switch(typeof(data)) { + case 'boolean': + return '' + (data ? 'true' : 'false') + ''; + case 'string': + return '"' + data + '"'; + case 'undefined': case null: + return 'null'; + case 'object': + var rows = []; + if(data instanceof Array) { + for(var i in data) { + rows.push('' + this.repr(data[i]) + ','); + } + } else { + for(var key in data) { + rows.push( + '' + + '' + this.repr(key) + '' + + '=>' + + '' + this.repr(data[key]) + ',' + + ''); + } + } + return '' + + '' + + rows.join('') + + '' + + '
array(
)
'; + } + return '???'; } }); From 01911f11aa7c2e20087aa139df25cc081eabf41c Mon Sep 17 00:00:00 2001 From: Tobias van Beek Date: Wed, 25 Mar 2015 11:10:47 +0100 Subject: [PATCH 778/933] Add the --recursive parameter to the git clone to get the submodules --- PHPCI/Model/Build/RemoteGitBuild.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PHPCI/Model/Build/RemoteGitBuild.php b/PHPCI/Model/Build/RemoteGitBuild.php index 80718011..5d0c9419 100644 --- a/PHPCI/Model/Build/RemoteGitBuild.php +++ b/PHPCI/Model/Build/RemoteGitBuild.php @@ -54,7 +54,7 @@ class RemoteGitBuild extends Build */ protected function cloneByHttp(Builder $builder, $cloneTo) { - $cmd = 'git clone '; + $cmd = 'git clone --recursive '; $depth = $builder->getConfig('clone_depth'); @@ -84,7 +84,7 @@ class RemoteGitBuild extends Build } // Do the git clone: - $cmd = 'git clone '; + $cmd = 'git clone --recursive '; $depth = $builder->getConfig('clone_depth'); From 524341a50b33c47b6d7ba1983d8446da9e57a7f5 Mon Sep 17 00:00:00 2001 From: Angel Koilov Date: Fri, 27 Mar 2015 14:09:03 +0200 Subject: [PATCH 779/933] remove unnecessary code --- PHPCI/Plugin/Behat.php | 1 - 1 file changed, 1 deletion(-) diff --git a/PHPCI/Plugin/Behat.php b/PHPCI/Plugin/Behat.php index 23686270..445d7191 100644 --- a/PHPCI/Plugin/Behat.php +++ b/PHPCI/Plugin/Behat.php @@ -98,7 +98,6 @@ class Behat implements \PHPCI\Plugin $lines = explode(PHP_EOL, $parts[1]); - $errorCount = 0; $storeFailures = false; $data = array(); From 731fd65453c7663129dc0175b208b61ce9e06d39 Mon Sep 17 00:00:00 2001 From: zviryatko Date: Sat, 28 Mar 2015 16:50:40 +0200 Subject: [PATCH 780/933] Change xmpp config and message files directory --- PHPCI/Plugin/Xmpp.php | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/PHPCI/Plugin/Xmpp.php b/PHPCI/Plugin/Xmpp.php index 8614e208..add778cb 100644 --- a/PHPCI/Plugin/Xmpp.php +++ b/PHPCI/Plugin/Xmpp.php @@ -132,8 +132,8 @@ class XMPP implements \PHPCI\Plugin */ public function findConfigFile() { - if (file_exists('.sendxmpprc')) { - if (md5(file_get_contents('.sendxmpprc')) !== md5($this->getConfigFormat())) { + if (file_exists($this->phpci->buildPath . '/.sendxmpprc')) { + if (md5(file_get_contents($this->phpci->buildPath . '/.sendxmpprc')) !== md5($this->getConfigFormat())) { return null; } @@ -165,9 +165,10 @@ class XMPP implements \PHPCI\Plugin /* * Try to build conf file */ + $config_file = $this->phpci->buildPath . '/.sendxmpprc'; if (is_null($this->findConfigFile())) { - file_put_contents('.sendxmpprc', $this->getConfigFormat()); - chmod('.sendxmpprc', 0600); + file_put_contents($config_file, $this->getConfigFormat()); + chmod($config_file, 0600); } /* @@ -178,7 +179,7 @@ class XMPP implements \PHPCI\Plugin $tls = ' -t'; } - $message_file = uniqid('xmppmessage'); + $message_file = $this->phpci->buildPath . '/' . uniqid('xmppmessage'); if ($this->buildMessage($message_file) === false) { return false; } @@ -186,10 +187,10 @@ class XMPP implements \PHPCI\Plugin /* * Send XMPP notification for all recipients */ - $cmd = $sendxmpp . "%s -f .sendxmpprc -m %s %s"; + $cmd = $sendxmpp . "%s -f %s -m %s %s"; $recipients = implode(' ', $this->recipients); - $success = $this->phpci->executeCommand($cmd, $tls, $message_file, $recipients); + $success = $this->phpci->executeCommand($cmd, $tls, $config_file, $message_file, $recipients); print $this->phpci->getLastOutput(); From f3c1a98cf10871eadd62bc461e4e02382384ba2f Mon Sep 17 00:00:00 2001 From: Adirelle Date: Wed, 25 Mar 2015 15:15:53 +0100 Subject: [PATCH 781/933] Detailed webhook responses. Fixed docblocks. Reworked WebhookController to enforce Json responses in ::handleAction. Check the project type match the webhook. When creating several builds, do not stop on first error. Try to create every builds and report 'ok' if at least one succeeds. CS fix. Fixed Uses. Fixed the types accepted by the git webhook. Added some really basic test. --- PHPCI/Controller/WebhookController.php | 378 ++++++++++-------- .../Controller/WebhookControllerTest.php | 41 ++ 2 files changed, 260 insertions(+), 159 deletions(-) create mode 100644 Tests/PHPCI/Controller/WebhookControllerTest.php diff --git a/PHPCI/Controller/WebhookController.php b/PHPCI/Controller/WebhookController.php index 78824ddb..e05cfbda 100644 --- a/PHPCI/Controller/WebhookController.php +++ b/PHPCI/Controller/WebhookController.php @@ -2,7 +2,7 @@ /** * PHPCI - Continuous Integration for PHP * - * @copyright Copyright 2014, Block 8 Limited. + * @copyright Copyright 2014-2015, Block 8 Limited. * @license https://github.com/Block8/PHPCI/blob/master/LICENSE.md * @link https://www.phptesting.org/ */ @@ -11,31 +11,37 @@ namespace PHPCI\Controller; use b8; use b8\Store; +use Exception; use PHPCI\BuildFactory; +use PHPCI\Model\Project; use PHPCI\Service\BuildService; +use PHPCI\Store\BuildStore; +use PHPCI\Store\ProjectStore; /** * Webhook Controller - Processes webhook pings from BitBucket, Github, Gitlab, etc. + * * @author Dan Cryer * @author Sami Tikka * @author Alex Russell + * @author Guillaume Perréal * @package PHPCI * @subpackage Web */ -class WebhookController extends \PHPCI\Controller +class WebhookController extends \b8\Controller { /** - * @var \PHPCI\Store\BuildStore + * @var BuildStore */ protected $buildStore; /** - * @var \PHPCI\Store\ProjectStore + * @var ProjectStore */ protected $projectStore; /** - * @var \PHPCI\Service\BuildService + * @var BuildService */ protected $buildService; @@ -49,194 +55,207 @@ class WebhookController extends \PHPCI\Controller $this->buildService = new BuildService($this->buildStore); } + /** Handle the action, Ensuring to return a JsonResponse. + * + * @param string $action + * @param mixed $actionParams + * + * @return \b8\Http\Response + */ + public function handleAction($action, $actionParams) + { + $response = new b8\Http\Response\JsonResponse(); + try { + $data = parent::handleAction($action, $actionParams); + if (isset($data['responseCode'])) { + $response->setResponseCode($data['responseCode']); + unset($data['responseCode']); + } + $response->setContent($data); + } catch (Exception $ex) { + $response->setResponseCode(500); + $response->setContent(array('status' => 'failed', 'error' => $ex->getMessage())); + } + return $response; + } + /** * Called by Bitbucket POST service. */ - public function bitbucket($project) + public function bitbucket($projectId) { - $response = new b8\Http\Response\JsonResponse(); - $response->setContent(array('status' => 'ok')); - + $project = $this->fetchProject($projectId, 'bitbucket'); $payload = json_decode($this->getParam('payload'), true); + $results = array(); + $status = 'failed'; foreach ($payload['commits'] as $commit) { try { $email = $commit['raw_author']; $email = substr($email, 0, strpos($email, '>')); $email = substr($email, strpos($email, '<') + 1); - $this->createBuild($project, $commit['raw_node'], $commit['branch'], $email, $commit['message']); - } catch (\Exception $ex) { - $response->setResponseCode(500); - $response->setContent(array('status' => 'failed', 'error' => $ex->getMessage())); - break; + $results[$commit['raw_node']] = $this->createBuild( + $project, + $commit['raw_node'], + $commit['branch'], + $email, + $commit['message'] + ); + $status = 'ok'; + } catch (Exception $ex) { + $results[$commit['raw_node']] = array('status' => 'failed', 'error' => $ex->getMessage()); } } - return $response; + return array('status' => $status, 'commits' => $results); } /** * Called by POSTing to /webhook/git/?branch=&commit= * - * @param string $project + * @param string $projectId */ - public function git($project) + public function git($projectId) { - $response = new b8\Http\Response\JsonResponse(); - $response->setContent(array('status' => 'ok')); - - $branch = $this->getParam('branch'); + $project = $this->fetchProject($projectId, array('local', 'remote')); + $branch = $this->getParam('branch', $project->getBranch()); $commit = $this->getParam('commit'); $commitMessage = $this->getParam('message'); $committer = $this->getParam('committer'); - try { - if (empty($branch)) { - $branch = 'master'; - } - - if (empty($commit)) { - $commit = null; - } - - if (empty($commitMessage)) { - $commitMessage = null; - } - - if (empty($committer)) { - $committer = null; - } - - $this->createBuild($project, $commit, $branch, $committer, $commitMessage); - } catch (\Exception $ex) { - $response->setResponseCode(500); - $response->setContent(array('status' => 'failed', 'error' => $ex->getMessage())); - } - - return $response; + return $this->createBuild($project, $commit, $branch, $committer, $commitMessage); } /** * Called by Github Webhooks: */ - public function github($project) + public function github($projectId) { - $response = new b8\Http\Response\JsonResponse(); - $response->setContent(array('status' => 'ok')); + $project = $this->fetchProject($projectId, 'github'); switch ($_SERVER['CONTENT_TYPE']) { case 'application/json': $payload = json_decode(file_get_contents('php://input'), true); break; - case 'application/x-www-form-urlencoded': $payload = json_decode($this->getParam('payload'), true); break; - default: - $response->setResponseCode(401); - $response->setContent(array('status' => 'failed', 'error' => 'Content type not supported.')); - return $response; + return array('status' => 'failed', 'error' => 'Content type not supported.', 'responseCode' => 401); } // Handle Pull Request web hooks: if (array_key_exists('pull_request', $payload)) { - return $this->githubPullRequest($project, $payload, $response); + return $this->githubPullRequest($project, $payload); } // Handle Push web hooks: if (array_key_exists('commits', $payload)) { - return $this->githubCommitRequest($project, $payload, $response); + return $this->githubCommitRequest($project, $payload); } - return $response; + return array('status' => 'ignored', 'message' => 'Unusable payload.'); } /** * Handle the payload when Github sends a commit webhook. - * @param $project + * + * @param Project $project * @param array $payload * @param b8\Http\Response\JsonResponse $response + * * @return b8\Http\Response\JsonResponse */ - protected function githubCommitRequest($project, array $payload, b8\Http\Response\JsonResponse $response) + protected function githubCommitRequest(Project $project, array $payload) { // Github sends a payload when you close a pull request with a // non-existant commit. We don't want this. if (array_key_exists('after', $payload) && $payload['after'] === '0000000000000000000000000000000000000000') { - return $response; + return array('status' => 'ignored'); } - try { - if (isset($payload['commits']) && is_array($payload['commits'])) { - // If we have a list of commits, then add them all as builds to be tested: + if (isset($payload['commits']) && is_array($payload['commits'])) { + // If we have a list of commits, then add them all as builds to be tested: - foreach ($payload['commits'] as $commit) { - if (!$commit['distinct']) { - continue; - } + $results = array(); + $status = 'failed'; + foreach ($payload['commits'] as $commit) { + if (!$commit['distinct']) { + $results[$commit['id']] = array('status' => 'ignored'); + continue; + } + try { $branch = str_replace('refs/heads/', '', $payload['ref']); $committer = $commit['committer']['email']; - $this->createBuild($project, $commit['id'], $branch, $committer, $commit['message']); + $results[$commit['id']] = $this->createBuild( + $project, + $commit['id'], + $branch, + $committer, + $commit['message'] + ); + $status = 'ok'; + } catch (Exception $ex) { + $results[$commit['id']] = array('status' => 'failed', 'error' => $ex->getMessage()); } - } elseif (substr($payload['ref'], 0, 10) == 'refs/tags/') { - // If we don't, but we're dealing with a tag, add that instead: - $branch = str_replace('refs/tags/', 'Tag: ', $payload['ref']); - $committer = $payload['pusher']['email']; - $message = $payload['head_commit']['message']; - $this->createBuild($project, $payload['after'], $branch, $committer, $message); } - - } catch (\Exception $ex) { - $response->setResponseCode(500); - $response->setContent(array('status' => 'failed', 'error' => $ex->getMessage())); - + return array('status' => $status, 'commits' => $results); } - return $response; + if (substr($payload['ref'], 0, 10) == 'refs/tags/') { + // If we don't, but we're dealing with a tag, add that instead: + $branch = str_replace('refs/tags/', 'Tag: ', $payload['ref']); + $committer = $payload['pusher']['email']; + $message = $payload['head_commit']['message']; + return $this->createBuild($project, $payload['after'], $branch, $committer, $message); + } + + return array('status' => 'ignored', 'message' => 'Unusable payload.'); } /** * Handle the payload when Github sends a Pull Request webhook. - * @param $projectId + * + * @param Project $project * @param array $payload */ - protected function githubPullRequest($projectId, array $payload, b8\Http\Response\JsonResponse $response) + protected function githubPullRequest(Project $project, array $payload) { // We only want to know about open pull requests: if (!in_array($payload['action'], array('opened', 'synchronize', 'reopened'))) { - return $response; + return array('status' => 'ok'); } - try { - $headers = array(); - $token = \b8\Config::getInstance()->get('phpci.github.token'); + $headers = array(); + $token = \b8\Config::getInstance()->get('phpci.github.token'); - if (!empty($token)) { - $headers[] = 'Authorization: token ' . $token; + if (!empty($token)) { + $headers[] = 'Authorization: token ' . $token; + } + + $url = $payload['pull_request']['commits_url']; + $http = new \b8\HttpClient(); + $http->setHeaders($headers); + $response = $http->get($url); + + // Check we got a success response: + if (!$response['success']) { + throw new Exception('Could not get commits, failed API request.'); + } + + $results = array(); + $status = 'failed'; + foreach ($response['body'] as $commit) { + // Skip all but the current HEAD commit ID: + $id = $commit['sha']; + if ($id != $payload['pull_request']['head']['sha']) { + $results[$id] = array('status' => 'ignored', 'message' => 'not branch head'); + continue; } - $url = $payload['pull_request']['commits_url']; - $http = new \b8\HttpClient(); - $http->setHeaders($headers); - $response = $http->get($url); - - // Check we got a success response: - if (!$response['success']) { - $message = 'Could not get commits, failed API request.'; - $response->setResponseCode(500); - $response->setContent(array('status' => 'failed', 'error' => $message)); - return $response; - } - - foreach ($response['body'] as $commit) { - // Skip all but the current HEAD commit ID: - if ($commit['sha'] != $payload['pull_request']['head']['sha']) { - continue; - } - + try { $branch = str_replace('refs/heads/', '', $payload['pull_request']['base']['ref']); $committer = $commit['commit']['author']['email']; $message = $commit['commit']['message']; @@ -249,83 +268,96 @@ class WebhookController extends \PHPCI\Controller 'remote_url' => $payload['pull_request']['head']['repo']['clone_url'], ); - $this->createBuild($projectId, $commit['sha'], $branch, $committer, $message, $extra); + $results[$id] = $this->createBuild($project, $id, $branch, $committer, $message, $extra); + $status = 'ok'; + } catch (Exception $ex) { + $results[$id] = array('status' => 'failed', 'error' => $ex->getMessage()); } - } catch (\Exception $ex) { - $response->setResponseCode(500); - $response->setContent(array('status' => 'failed', 'error' => $ex->getMessage())); } - return $response; + return array('status' => $status, 'commits' => $results); } /** * Called by Gitlab Webhooks: */ - public function gitlab($project) + public function gitlab($projectId) { - $response = new b8\Http\Response\JsonResponse(); - $response->setContent(array('status' => 'ok')); + $project = $this->fetchProject($projectId, 'gitlab'); $payloadString = file_get_contents("php://input"); $payload = json_decode($payloadString, true); - try { - // build on merge request events - if (isset($payload['object_kind']) && $payload['object_kind'] == 'merge_request') { - $attributes = $payload['object_attributes']; - if ($attributes['state'] == 'opened' || $attributes['state'] == 'reopened') { - $branch = $attributes['source_branch']; - $commit = $attributes['last_commit']; - $committer = $commit['author']['email']; + // build on merge request events + if (isset($payload['object_kind']) && $payload['object_kind'] == 'merge_request') { + $attributes = $payload['object_attributes']; + if ($attributes['state'] == 'opened' || $attributes['state'] == 'reopened') { + $branch = $attributes['source_branch']; + $commit = $attributes['last_commit']; + $committer = $commit['author']['email']; - $this->createBuild($project, $commit['id'], $branch, $committer, $commit['message']); - } + return $this->createBuild($project, $commit['id'], $branch, $committer, $commit['message']); } - - // build on push events - if (isset($payload['commits']) && is_array($payload['commits'])) { - // If we have a list of commits, then add them all as builds to be tested: - - foreach ($payload['commits'] as $commit) { - $branch = str_replace('refs/heads/', '', $payload['ref']); - $committer = $commit['author']['email']; - $this->createBuild($project, $commit['id'], $branch, $committer, $commit['message']); - } - } - - } catch (\Exception $ex) { - $response->setResponseCode(500); - $response->setContent(array('status' => 'failed', 'error' => $ex->getMessage())); } - return $response; + // build on push events + if (isset($payload['commits']) && is_array($payload['commits'])) { + // If we have a list of commits, then add them all as builds to be tested: + + $results = array(); + $status = 'failed'; + foreach ($payload['commits'] as $commit) { + try { + $branch = str_replace('refs/heads/', '', $payload['ref']); + $committer = $commit['author']['email']; + $results[$commit['id']] = $this->createBuild( + $project, + $commit['id'], + $branch, + $committer, + $commit['message'] + ); + $status = 'ok'; + } catch (Exception $ex) { + $results[$commit['id']] = array('status' => 'failed', 'error' => $ex->getMessage()); + } + } + return array('status' => $status, 'commits' => $results); + } + + return array('status' => 'ignored', 'message' => 'Unusable payload.'); } /** * Wrapper for creating a new build. - * @param $projectId - * @param $commitId - * @param $branch - * @param $committer - * @param $commitMessage - * @param null $extra - * @return bool - * @throws \Exception + * + * @param Project $project + * @param string $commitId + * @param string $branch + * @param string $committer + * @param string $commitMessage + * @param array $extra + * + * @return array + * + * @throws Exception */ - protected function createBuild($projectId, $commitId, $branch, $committer, $commitMessage, $extra = null) - { + protected function createBuild( + Project $project, + $commitId, + $branch, + $committer, + $commitMessage, + array $extra = null + ) { // Check if a build already exists for this commit ID: - $builds = $this->buildStore->getByProjectAndCommit($projectId, $commitId); + $builds = $this->buildStore->getByProjectAndCommit($project->getId(), $commitId); if ($builds['count']) { - return true; - } - - $project = $this->projectStore->getById($projectId); - - if (empty($project)) { - throw new \Exception('Project does not exist:' . $projectId); + return array( + 'status' => 'ignored', + 'message' => sprintf('Duplicate of build #%d', $builds['items'][0]->getId()) + ); } // If not, create a new build job for it: @@ -335,6 +367,34 @@ class WebhookController extends \PHPCI\Controller // Send a status postback if the build type provides one: $build->sendStatusPostback(); - return true; + return array('status' => 'ok', 'buildID' => $build->getID()); + } + + /** + * Fetch a project and check its type. + * + * @param int $projectId + * @param array|string $expectedType + * + * @return Project + * + * @throws Exception If the project does not exist or is not of the expected type. + */ + protected function fetchProject($projectId, $expectedType) + { + $project = $this->projectStore->getById($projectId); + + if (empty($projectId)) { + throw new Exception('Project does not exist: ' . $projectId); + } + + if (is_array($expectedType) + ? !in_array($project->getType(), $expectedType) + : $project->getType() !== $expectedType + ) { + throw new Exception('Wrong project type: ' . $project->getType()); + } + + return $project; } } diff --git a/Tests/PHPCI/Controller/WebhookControllerTest.php b/Tests/PHPCI/Controller/WebhookControllerTest.php new file mode 100644 index 00000000..ddd13fc0 --- /dev/null +++ b/Tests/PHPCI/Controller/WebhookControllerTest.php @@ -0,0 +1,41 @@ +prophesize('b8\Config')->reveal(), + $this->prophesize('b8\Http\Request')->reveal(), + $this->prophesize('b8\Http\Response')->reveal() + ); + + $error = $webController->handleAction('test', []); + + $this->assertInstanceOf('b8\Http\Response\JsonResponse', $error); + + $responseData = $error->getData(); + $this->assertEquals(500, $responseData['code']); + + $this->assertEquals('failed', $responseData['body']['status']); + + $this->assertEquals('application/json', $responseData['headers']['Content-Type']); + + // @todo: we can't text the result is JSON file with + // $this->assertJson((string) $error); + // since the flush method automatically add the header and break the + // testing framework. + } +} From 067a60983fce1497482adf1802901df5820ddcb6 Mon Sep 17 00:00:00 2001 From: zviryatko Date: Wed, 25 Feb 2015 16:16:23 +0200 Subject: [PATCH 782/933] Fix archive link. --- PHPCI/View/layout.phtml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/PHPCI/View/layout.phtml b/PHPCI/View/layout.phtml index 72ef27ff..82eabfe5 100644 --- a/PHPCI/View/layout.phtml +++ b/PHPCI/View/layout.phtml @@ -138,7 +138,7 @@
From f25b1d25dcea411f8273f4cb40dfc94a8e0c187b Mon Sep 17 00:00:00 2001 From: Adirelle Date: Tue, 14 Apr 2015 12:17:52 +0200 Subject: [PATCH 789/933] Let CommandExecutor::findBinary throw an exception when the binary is missing. Close #910 --- PHPCI/Builder.php | 8 +++--- PHPCI/Helper/BaseCommandExecutor.php | 31 +++++++++++----------- PHPCI/Helper/CommandExecutor.php | 7 +++-- PHPCI/Plugin/Codeception.php | 6 ----- PHPCI/Plugin/Composer.php | 5 ---- PHPCI/Plugin/Pdepend.php | 7 +---- PHPCI/Plugin/Phing.php | 5 ---- PHPCI/Plugin/PhpCodeSniffer.php | 5 ---- PHPCI/Plugin/PhpCpd.php | 7 +---- PHPCI/Plugin/PhpCsFixer.php | 5 ---- PHPCI/Plugin/PhpDocblockChecker.php | 5 ---- PHPCI/Plugin/PhpLoc.php | 5 ---- PHPCI/Plugin/PhpMessDetector.php | 5 ---- PHPCI/Plugin/PhpParallelLint.php | 5 ---- PHPCI/Plugin/PhpSpec.php | 5 ---- PHPCI/Plugin/PhpUnit.php | 12 --------- PHPCI/Plugin/Xmpp.php | 7 +---- Tests/PHPCI/Helper/CommandExecutorTest.php | 19 ++++++++++++- 18 files changed, 46 insertions(+), 103 deletions(-) diff --git a/PHPCI/Builder.php b/PHPCI/Builder.php index b2a135f4..39e8ca41 100644 --- a/PHPCI/Builder.php +++ b/PHPCI/Builder.php @@ -259,12 +259,14 @@ class Builder implements LoggerAwareInterface /** * Find a binary required by a plugin. - * @param $binary + * @param string $binary + * @param bool $quiet + * * @return null|string */ - public function findBinary($binary) + public function findBinary($binary, $quiet = false) { - return $this->commandExecutor->findBinary($binary, $this->buildPath); + return $this->commandExecutor->findBinary($binary, $quiet = false); } /** diff --git a/PHPCI/Helper/BaseCommandExecutor.php b/PHPCI/Helper/BaseCommandExecutor.php index 5435d597..bd948834 100644 --- a/PHPCI/Helper/BaseCommandExecutor.php +++ b/PHPCI/Helper/BaseCommandExecutor.php @@ -9,9 +9,9 @@ namespace PHPCI\Helper; -use \PHPCI\Logging\BuildLogger; +use Exception; +use PHPCI\Logging\BuildLogger; use Psr\Log\LogLevel; -use PHPCI\Helper\Lang; /** * Handles running system commands with variables. @@ -20,7 +20,7 @@ use PHPCI\Helper\Lang; abstract class BaseCommandExecutor implements CommandExecutor { /** - * @var \PHPCI\Logging\BuildLogger + * @var BuildLogger */ protected $logger; @@ -144,13 +144,12 @@ abstract class BaseCommandExecutor implements CommandExecutor /** * Find a binary required by a plugin. * @param string $binary - * @param null $buildPath + * @param bool $quiet * @return null|string */ - public function findBinary($binary, $buildPath = null) + public function findBinary($binary, $quiet = false) { - $binaryPath = null; - $composerBin = $this->getComposerBinDir(realpath($buildPath)); + $composerBin = $this->getComposerBinDir(realpath($this->buildPath)); if (is_string($binary)) { $binary = array($binary); @@ -161,30 +160,30 @@ abstract class BaseCommandExecutor implements CommandExecutor if (is_dir($composerBin) && is_file($composerBin.'/'.$bin)) { $this->logger->log(Lang::get('found_in_path', $composerBin, $bin), LogLevel::DEBUG); - $binaryPath = $composerBin . '/' . $bin; - break; + return $composerBin . '/' . $bin; } if (is_file($this->rootDir . $bin)) { $this->logger->log(Lang::get('found_in_path', 'root', $bin), LogLevel::DEBUG); - $binaryPath = $this->rootDir . $bin; - break; + return $this->rootDir . $bin; } if (is_file($this->rootDir . 'vendor/bin/' . $bin)) { $this->logger->log(Lang::get('found_in_path', 'vendor/bin', $bin), LogLevel::DEBUG); - $binaryPath = $this->rootDir . 'vendor/bin/' . $bin; - break; + return $this->rootDir . 'vendor/bin/' . $bin; } $findCmdResult = $this->findGlobalBinary($bin); if (is_file($findCmdResult)) { $this->logger->log(Lang::get('found_in_path', '', $bin), LogLevel::DEBUG); - $binaryPath = $findCmdResult; - break; + return $findCmdResult; } } - return $binaryPath; + + if ($quiet) { + return; + } + throw new Exception(Lang::get('could_not_find', implode('/', $binary))); } /** diff --git a/PHPCI/Helper/CommandExecutor.php b/PHPCI/Helper/CommandExecutor.php index bacd1a2e..4f0028eb 100644 --- a/PHPCI/Helper/CommandExecutor.php +++ b/PHPCI/Helper/CommandExecutor.php @@ -26,10 +26,13 @@ interface CommandExecutor /** * Find a binary required by a plugin. * @param string $binary - * @param string $buildPath the current build path + * @param bool $quiet Returns null instead of throwing an execption. + * * @return null|string + * + * @throws \Exception when no binary has been found and $quiet is false. */ - public function findBinary($binary, $buildPath = null); + public function findBinary($binary, $quiet = false); /** * Set the buildPath property. diff --git a/PHPCI/Plugin/Codeception.php b/PHPCI/Plugin/Codeception.php index 7d7fd81e..9a651bec 100644 --- a/PHPCI/Plugin/Codeception.php +++ b/PHPCI/Plugin/Codeception.php @@ -133,12 +133,6 @@ class Codeception implements PHPCI\Plugin, PHPCI\ZeroConfigPlugin } else { $codecept = $this->phpci->findBinary('codecept'); - if (!$codecept) { - $this->phpci->logFailure(Lang::get('could_not_find', 'codecept')); - - return false; - } - $cmd = 'cd "%s" && ' . $codecept . ' run -c "%s" --tap ' . $this->args; if (IS_WIN) { diff --git a/PHPCI/Plugin/Composer.php b/PHPCI/Plugin/Composer.php index 3930f3d6..87a558fe 100644 --- a/PHPCI/Plugin/Composer.php +++ b/PHPCI/Plugin/Composer.php @@ -81,11 +81,6 @@ class Composer implements PHPCI\Plugin, PHPCI\ZeroConfigPlugin { $composerLocation = $this->phpci->findBinary(array('composer', 'composer.phar')); - if (!$composerLocation) { - $this->phpci->logFailure(Lang::get('could_not_find', 'composer')); - return false; - } - $cmd = ''; if (IS_WIN) { diff --git a/PHPCI/Plugin/Pdepend.php b/PHPCI/Plugin/Pdepend.php index d1039554..73b669c1 100644 --- a/PHPCI/Plugin/Pdepend.php +++ b/PHPCI/Plugin/Pdepend.php @@ -79,15 +79,10 @@ class Pdepend implements \PHPCI\Plugin $pdepend = $this->phpci->findBinary('pdepend'); - if (!$pdepend) { - $this->phpci->logFailure(Lang::get('could_not_find', 'pdepend')); - return false; - } - $cmd = $pdepend . ' --summary-xml="%s" --jdepend-chart="%s" --overview-pyramid="%s" %s "%s"'; $this->removeBuildArtifacts(); - + // If we need to ignore directories if (count($this->phpci->ignore)) { $ignore = ' --ignore=' . implode(',', $this->phpci->ignore); diff --git a/PHPCI/Plugin/Phing.php b/PHPCI/Plugin/Phing.php index 2bf64cca..3a5bd834 100644 --- a/PHPCI/Plugin/Phing.php +++ b/PHPCI/Plugin/Phing.php @@ -81,11 +81,6 @@ class Phing implements \PHPCI\Plugin { $phingExecutable = $this->phpci->findBinary('phing'); - if (!$phingExecutable) { - $this->phpci->logFailure(Lang::get('could_not_find', 'phing')); - return false; - } - $cmd[] = $phingExecutable . ' -f ' . $this->getBuildFilePath(); if ($this->getPropertyFile()) { diff --git a/PHPCI/Plugin/PhpCodeSniffer.php b/PHPCI/Plugin/PhpCodeSniffer.php index df276eac..877d24c6 100644 --- a/PHPCI/Plugin/PhpCodeSniffer.php +++ b/PHPCI/Plugin/PhpCodeSniffer.php @@ -149,11 +149,6 @@ class PhpCodeSniffer implements PHPCI\Plugin, PHPCI\ZeroConfigPlugin $phpcs = $this->phpci->findBinary('phpcs'); - if (!$phpcs) { - $this->phpci->logFailure(PHPCI\Helper\Lang::get('could_not_find', 'phpcs')); - return false; - } - $this->phpci->logExecOutput(false); $cmd = $phpcs . ' --report=json %s %s %s %s %s "%s"'; diff --git a/PHPCI/Plugin/PhpCpd.php b/PHPCI/Plugin/PhpCpd.php index c663db5a..6433308f 100644 --- a/PHPCI/Plugin/PhpCpd.php +++ b/PHPCI/Plugin/PhpCpd.php @@ -90,18 +90,13 @@ class PhpCpd implements \PHPCI\Plugin $phpcpd = $this->phpci->findBinary('phpcpd'); - if (!$phpcpd) { - $this->phpci->logFailure(Lang::get('could_not_find', 'phpcpd')); - return false; - } - $tmpfilename = tempnam('/tmp', 'phpcpd'); $cmd = $phpcpd . ' --log-pmd "%s" %s "%s"'; $success = $this->phpci->executeCommand($cmd, $tmpfilename, $ignore, $this->path); print $this->phpci->getLastOutput(); - + list($errorCount, $data) = $this->processReport(file_get_contents($tmpfilename)); $this->build->storeMeta('phpcpd-warnings', $errorCount); $this->build->storeMeta('phpcpd-data', $data); diff --git a/PHPCI/Plugin/PhpCsFixer.php b/PHPCI/Plugin/PhpCsFixer.php index 1374bc71..e07db718 100644 --- a/PHPCI/Plugin/PhpCsFixer.php +++ b/PHPCI/Plugin/PhpCsFixer.php @@ -69,11 +69,6 @@ class PhpCsFixer implements \PHPCI\Plugin $phpcsfixer = $this->phpci->findBinary('php-cs-fixer'); - if (!$phpcsfixer) { - $this->phpci->logFailure(Lang::get('could_not_find', 'php-cs-fixer')); - return false; - } - $cmd = $phpcsfixer . ' fix . %s %s %s'; $success = $this->phpci->executeCommand($cmd, $this->verbose, $this->diff, $this->level); diff --git a/PHPCI/Plugin/PhpDocblockChecker.php b/PHPCI/Plugin/PhpDocblockChecker.php index 9f71afef..7fb0fe11 100644 --- a/PHPCI/Plugin/PhpDocblockChecker.php +++ b/PHPCI/Plugin/PhpDocblockChecker.php @@ -104,11 +104,6 @@ class PhpDocblockChecker implements PHPCI\Plugin, PHPCI\ZeroConfigPlugin // Check that the binary exists: $checker = $this->phpci->findBinary('phpdoccheck'); - if (!$checker) { - $this->phpci->logFailure(PHPCI\Helper\Lang::get('could_not_find', 'phpdoccheck')); - return false; - } - // Build ignore string: $ignore = ''; if (count($this->ignore)) { diff --git a/PHPCI/Plugin/PhpLoc.php b/PHPCI/Plugin/PhpLoc.php index 65398e05..76887493 100644 --- a/PHPCI/Plugin/PhpLoc.php +++ b/PHPCI/Plugin/PhpLoc.php @@ -80,11 +80,6 @@ class PhpLoc implements PHPCI\Plugin, PHPCI\ZeroConfigPlugin $phploc = $this->phpci->findBinary('phploc'); - if (!$phploc) { - $this->phpci->logFailure(PHPCI\Helper\Lang::get('could_not_find', 'phploc')); - return false; - } - $success = $this->phpci->executeCommand($phploc . ' %s "%s"', $ignore, $this->directory); $output = $this->phpci->getLastOutput(); diff --git a/PHPCI/Plugin/PhpMessDetector.php b/PHPCI/Plugin/PhpMessDetector.php index 21c171cb..b02f5f09 100644 --- a/PHPCI/Plugin/PhpMessDetector.php +++ b/PHPCI/Plugin/PhpMessDetector.php @@ -121,11 +121,6 @@ class PhpMessDetector implements PHPCI\Plugin, PHPCI\ZeroConfigPlugin $phpmdBinaryPath = $this->phpci->findBinary('phpmd'); - if (!$phpmdBinaryPath) { - $this->phpci->logFailure(PHPCI\Helper\Lang::get('could_not_find', 'phpmd')); - return false; - } - $this->executePhpMd($phpmdBinaryPath); list($errorCount, $data) = $this->processReport(trim($this->phpci->getLastOutput())); diff --git a/PHPCI/Plugin/PhpParallelLint.php b/PHPCI/Plugin/PhpParallelLint.php index fdaeaf65..8b7528de 100644 --- a/PHPCI/Plugin/PhpParallelLint.php +++ b/PHPCI/Plugin/PhpParallelLint.php @@ -78,11 +78,6 @@ class PhpParallelLint implements \PHPCI\Plugin $phplint = $this->phpci->findBinary('parallel-lint'); - if (!$phplint) { - $this->phpci->logFailure(Lang::get('could_not_find', 'parallel-lint')); - return false; - } - $cmd = $phplint . ' %s "%s"'; $success = $this->phpci->executeCommand( $cmd, diff --git a/PHPCI/Plugin/PhpSpec.php b/PHPCI/Plugin/PhpSpec.php index 681a5a6c..e468a718 100644 --- a/PHPCI/Plugin/PhpSpec.php +++ b/PHPCI/Plugin/PhpSpec.php @@ -59,11 +59,6 @@ class PhpSpec implements PHPCI\Plugin $phpspec = $this->phpci->findBinary(array('phpspec', 'phpspec.php')); - if (!$phpspec) { - $this->phpci->logFailure(PHPCI\Helper\Lang::get('could_not_find', 'phpspec')); - return false; - } - $success = $this->phpci->executeCommand($phpspec . ' --format=junit --no-code-generation run'); $output = $this->phpci->getLastOutput(); diff --git a/PHPCI/Plugin/PhpUnit.php b/PHPCI/Plugin/PhpUnit.php index 70e0e74f..7fb626df 100644 --- a/PHPCI/Plugin/PhpUnit.php +++ b/PHPCI/Plugin/PhpUnit.php @@ -197,15 +197,8 @@ class PhpUnit implements PHPCI\Plugin, PHPCI\ZeroConfigPlugin chdir($this->phpci->buildPath.'/'.$this->runFrom); } - $phpunit = $this->phpci->findBinary('phpunit'); - if (!$phpunit) { - $this->phpci->logFailure(PHPCI\Helper\Lang::get('could_not_find', 'phpunit')); - return false; - } - - $cmd = $phpunit . ' --tap %s -c "%s" ' . $this->coverage . $this->path; $success = $this->phpci->executeCommand($cmd, $this->args, $this->phpci->buildPath . $configPath); @@ -232,11 +225,6 @@ class PhpUnit implements PHPCI\Plugin, PHPCI\ZeroConfigPlugin $phpunit = $this->phpci->findBinary('phpunit'); - if (!$phpunit) { - $this->phpci->logFailure(PHPCI\Helper\Lang::get('could_not_find', 'phpunit')); - return false; - } - $cmd = $phpunit . ' --tap %s "%s"'; $success = $this->phpci->executeCommand($cmd, $this->args, $this->phpci->buildPath . $directory); chdir($curdir); diff --git a/PHPCI/Plugin/Xmpp.php b/PHPCI/Plugin/Xmpp.php index add778cb..0362aa85 100644 --- a/PHPCI/Plugin/Xmpp.php +++ b/PHPCI/Plugin/Xmpp.php @@ -148,12 +148,7 @@ class XMPP implements \PHPCI\Plugin */ public function execute() { - $sendxmpp = $this->phpci->findBinary('/usr/bin/sendxmpp'); - - if (!$sendxmpp) { - $this->phpci->logFailure('Could not find sendxmpp.'); - return false; - } + $sendxmpp = $this->phpci->findBinary('sendxmpp'); /* * Without recipients we can't send notification diff --git a/Tests/PHPCI/Helper/CommandExecutorTest.php b/Tests/PHPCI/Helper/CommandExecutorTest.php index 4cf14887..5d5dc08b 100644 --- a/Tests/PHPCI/Helper/CommandExecutorTest.php +++ b/Tests/PHPCI/Helper/CommandExecutorTest.php @@ -27,7 +27,8 @@ class CommandExecutorTest extends \PHPUnit_Framework_TestCase } parent::setUp(); $mockBuildLogger = $this->prophesize('PHPCI\Logging\BuildLogger'); - $this->testedExecutor = new UnixCommandExecutor($mockBuildLogger->reveal(), __DIR__ . "/"); + $class = IS_WIN ? 'PHPCI\Helper\WindowsCommandExecutor' : 'PHPCI\Helper\UnixCommandExecutor'; + $this->testedExecutor = new $class($mockBuildLogger->reveal(), __DIR__ . "/"); } public function testGetLastOutput_ReturnsOutputOfCommand() @@ -63,4 +64,20 @@ class CommandExecutorTest extends \PHPUnit_Framework_TestCase $returnValue = $this->testedExecutor->findBinary($thisFileName); $this->assertEquals(__DIR__ . "/" . $thisFileName, $returnValue); } + + /** + * @expectedException \Exception + * @expectedMessageRegex WorldWidePeace + */ + public function testFindBinary_ThrowsWhenNotFound() + { + $thisFileName = "WorldWidePeace"; + $this->testedExecutor->findBinary($thisFileName); + } + + public function testFindBinary_ReturnsNullWihQuietArgument() + { + $thisFileName = "WorldWidePeace"; + $this->assertNull($this->testedExecutor->findBinary($thisFileName, true)); + } } From 290c34a27da14b8632bcc3a2a5cc05e68f0ab3b3 Mon Sep 17 00:00:00 2001 From: Adirelle Date: Tue, 14 Apr 2015 09:43:21 +0200 Subject: [PATCH 790/933] Updated the php_codesniffer required version and added a default phpcs.xml. Added a PHPMD configuration file. Updated phpci.yml to use the configuration files. Close #913 --- composer.json | 2 +- composer.lock | 18 ++++++++++++------ phpci.yml | 4 +++- phpcs.xml | 19 +++++++++++++++++++ phpmd.xml | 17 +++++++++++++++++ 5 files changed, 52 insertions(+), 8 deletions(-) create mode 100644 phpcs.xml create mode 100644 phpmd.xml diff --git a/composer.json b/composer.json index 03d752a5..c6d8e92b 100644 --- a/composer.json +++ b/composer.json @@ -52,7 +52,7 @@ "require-dev": { "phpunit/phpunit": "~4.5", "phpmd/phpmd": "~2.0", - "squizlabs/php_codesniffer": "~2.0", + "squizlabs/php_codesniffer": "~2.3", "block8/php-docblock-checker": "~1.0", "phploc/phploc": "~2.0" }, diff --git a/composer.lock b/composer.lock index 60b54a4e..dbbedd22 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "0c9efd3a0b9e924c7465e03fc97ffed7", + "hash": "a4a95f2b83e336b9e1b285c04af26ce7", "packages": [ { "name": "block8/b8framework", @@ -1851,20 +1851,21 @@ }, { "name": "squizlabs/php_codesniffer", - "version": "2.2.0", + "version": "2.3.0", "source": { "type": "git", "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "b301c98f19414d836fdaa678648745fcca5aeb4f" + "reference": "5046b0e01c416fc2b06df961d0673c85bcdc896c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/b301c98f19414d836fdaa678648745fcca5aeb4f", - "reference": "b301c98f19414d836fdaa678648745fcca5aeb4f", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/5046b0e01c416fc2b06df961d0673c85bcdc896c", + "reference": "5046b0e01c416fc2b06df961d0673c85bcdc896c", "shasum": "" }, "require": { "ext-tokenizer": "*", + "ext-xmlwriter": "*", "php": ">=5.1.2" }, "bin": [ @@ -1872,6 +1873,11 @@ "scripts/phpcbf" ], "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, "autoload": { "classmap": [ "CodeSniffer.php", @@ -1915,7 +1921,7 @@ "phpcs", "standards" ], - "time": "2015-01-21 22:44:05" + "time": "2015-03-04 02:07:03" }, { "name": "symfony/dependency-injection", diff --git a/phpci.yml b/phpci.yml index 8f51c88f..e794939e 100644 --- a/phpci.yml +++ b/phpci.yml @@ -21,8 +21,10 @@ test: - vendor/ php_mess_detector: allowed_warnings: 0 + rules: + - phpmd.xml php_code_sniffer: - standard: "PSR2" + standard: phpcs.xml allowed_warnings: 0 allowed_errors: 0 php_loc: diff --git a/phpcs.xml b/phpcs.xml new file mode 100644 index 00000000..a392a201 --- /dev/null +++ b/phpcs.xml @@ -0,0 +1,19 @@ + + + + Codestyle ruleset for PHPCI + + + + PHPCI + + + + + PHPCI/Migrations/* + PHPCI/Model/Base/* + PHPCI/Languages/* + Tests/* + vendor/* + + diff --git a/phpmd.xml b/phpmd.xml new file mode 100644 index 00000000..69b822fd --- /dev/null +++ b/phpmd.xml @@ -0,0 +1,17 @@ + + + + PHPCI rule set + + + + + + + From 81fbc6a5a0d4e4ae693f209a7bbb4a7882577310 Mon Sep 17 00:00:00 2001 From: rm3nchaca Date: Wed, 15 Apr 2015 15:17:06 -0500 Subject: [PATCH 791/933] fix file link in plugins Running builds leave a file link with an error like "http://gitlab.example.com/root/project/blob/master/index.php#L6" but it is pointing to the actual file, not the file with a bug, example "http://gitlab.example.com/root/project/blob/97f0a6453d5913f4b55d660fbf2d629240ca7237/index.php#L6" Close #915 --- PHPCI/Model/Build/GitlabBuild.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PHPCI/Model/Build/GitlabBuild.php b/PHPCI/Model/Build/GitlabBuild.php index 086bc046..a1e0562c 100644 --- a/PHPCI/Model/Build/GitlabBuild.php +++ b/PHPCI/Model/Build/GitlabBuild.php @@ -47,7 +47,7 @@ class GitlabBuild extends RemoteGitBuild 'http://%s/%s/blob/%s/{FILE}#L{LINE}', $this->getProject()->getAccessInformation("domain"), $this->getProject()->getReference(), - $this->getBranch() + $this->getCommitId() ); } From f7ca64bf6de55d6556225c7207e46fe35ecdbf12 Mon Sep 17 00:00:00 2001 From: Adam Henley Date: Sun, 19 Apr 2015 20:32:55 +1200 Subject: [PATCH 792/933] SMTP Password not masked PR #921 Signed-off-by: Adam Henley Close #923 --- PHPCI/Controller/SettingsController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PHPCI/Controller/SettingsController.php b/PHPCI/Controller/SettingsController.php index aaaa637f..ea15bc3b 100644 --- a/PHPCI/Controller/SettingsController.php +++ b/PHPCI/Controller/SettingsController.php @@ -329,7 +329,7 @@ class SettingsController extends Controller $field->setContainerClass('form-group'); $form->addField($field); - $field = new Form\Element\Text('smtp_password'); + $field = new Form\Element\Password('smtp_password'); $field->setRequired(false); $field->setLabel(Lang::get('smtp_password')); $field->setClass('form-control'); From c0568d3a4b93b104061c3ad6216b693068b35a92 Mon Sep 17 00:00:00 2001 From: Alexander Garzon Date: Tue, 21 Apr 2015 18:52:42 -0400 Subject: [PATCH 793/933] Update lang.es.php Missing translation for "archived" Close #931 --- PHPCI/Languages/lang.es.php | 1 + 1 file changed, 1 insertion(+) diff --git a/PHPCI/Languages/lang.es.php b/PHPCI/Languages/lang.es.php index 7c9f5a62..95a6eb73 100644 --- a/PHPCI/Languages/lang.es.php +++ b/PHPCI/Languages/lang.es.php @@ -114,6 +114,7 @@ PHPCI', (en caso que no puedas agregar el archivo phpci.yml al repositorio)', 'default_branch' => 'Nombre de la rama por defecto', 'allow_public_status' => '¿Activar página pública con el estado del proyecto?', + 'archived' => 'Archivado', 'save_project' => 'Guardar Proyecto', 'error_mercurial' => 'La URL del repositorio de Mercurial debe comenzar con http:// or https://', From 1b7f0bbb4b79500b45dedf768e332f9cf187937a Mon Sep 17 00:00:00 2001 From: corpsee Date: Tue, 17 Mar 2015 15:11:05 +0600 Subject: [PATCH 794/933] Improved login: now you can login using name or email Close #873 --- PHPCI/Controller/SessionController.php | 4 +-- PHPCI/Languages/lang.da.php | 1 + PHPCI/Languages/lang.de.php | 1 + PHPCI/Languages/lang.el.php | 1 + PHPCI/Languages/lang.en.php | 1 + PHPCI/Languages/lang.fr.php | 1 + PHPCI/Languages/lang.it.php | 1 + PHPCI/Languages/lang.nl.php | 1 + PHPCI/Languages/lang.pl.php | 1 + PHPCI/Languages/lang.ru.php | 1 + PHPCI/Languages/lang.uk.php | 1 + ...4958_unique_email_and_name_user_fields.php | 30 +++++++++++++++++++ PHPCI/Store/Base/UserStoreBase.php | 28 ++++++++++++++++- 13 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 PHPCI/Migrations/20150324174958_unique_email_and_name_user_fields.php diff --git a/PHPCI/Controller/SessionController.php b/PHPCI/Controller/SessionController.php index eb7001df..a5f40ffb 100644 --- a/PHPCI/Controller/SessionController.php +++ b/PHPCI/Controller/SessionController.php @@ -49,7 +49,7 @@ class SessionController extends \PHPCI\Controller } else { unset($_SESSION['login_token']); - $user = $this->userStore->getByEmail($this->getParam('email')); + $user = $this->userStore->getByLoginOrEmail($this->getParam('email')); if ($user && password_verify($this->getParam('password', ''), $user->getHash())) { session_regenerate_id(true); @@ -68,7 +68,7 @@ class SessionController extends \PHPCI\Controller $form->setAction(PHPCI_URL.'session/login'); $email = new b8\Form\Element\Email('email'); - $email->setLabel(Lang::get('email_address')); + $email->setLabel(Lang::get('login')); $email->setRequired(true); $email->setContainerClass('form-group'); $email->setClass('form-control'); diff --git a/PHPCI/Languages/lang.da.php b/PHPCI/Languages/lang.da.php index b65f9ef9..20e403e1 100644 --- a/PHPCI/Languages/lang.da.php +++ b/PHPCI/Languages/lang.da.php @@ -39,6 +39,7 @@ PHPCI', 'reset_email_title' => 'PHPCI Adgangskode-nulstilling for %s', 'reset_invalid' => 'Ugyldig anmodning om adgangskode-nulstilling.', 'email_address' => 'Email-addresse', + 'login' => 'Login / Email Address', 'password' => 'Adgangskode', 'log_in' => 'Log ind', diff --git a/PHPCI/Languages/lang.de.php b/PHPCI/Languages/lang.de.php index 393459a1..8a08e37d 100644 --- a/PHPCI/Languages/lang.de.php +++ b/PHPCI/Languages/lang.de.php @@ -39,6 +39,7 @@ PHPCI', 'reset_email_title' => 'PHPCI Passwort zurücksetzen für %s', 'reset_invalid' => 'Fehlerhafte Anfrage für das Zurücksetzen eines Passwortes', 'email_address' => 'Emailadresse', + 'login' => 'Login / Email Address', 'password' => 'Passwort', 'log_in' => 'Einloggen', diff --git a/PHPCI/Languages/lang.el.php b/PHPCI/Languages/lang.el.php index e97da6d3..3e3a3b84 100644 --- a/PHPCI/Languages/lang.el.php +++ b/PHPCI/Languages/lang.el.php @@ -39,6 +39,7 @@ PHPCI', 'reset_email_title' => 'PHPCI Επαναφορά Κωδικού για %s', 'reset_invalid' => 'Μη έγκυρο αίτημα επαναφοράς κωδικού πρόσβασης.', 'email_address' => 'Διεύθυνση email', + 'login' => 'Login / Email Address', 'password' => 'Κωδικός πρόσβασης', 'log_in' => 'Είσοδος', diff --git a/PHPCI/Languages/lang.en.php b/PHPCI/Languages/lang.en.php index 19d41cbe..d11a8b39 100644 --- a/PHPCI/Languages/lang.en.php +++ b/PHPCI/Languages/lang.en.php @@ -39,6 +39,7 @@ PHPCI', 'reset_email_title' => 'PHPCI Password Reset for %s', 'reset_invalid' => 'Invalid password reset request.', 'email_address' => 'Email Address', + 'login' => 'Login / Email Address', 'password' => 'Password', 'log_in' => 'Log in', diff --git a/PHPCI/Languages/lang.fr.php b/PHPCI/Languages/lang.fr.php index a8de5991..f1ad7129 100644 --- a/PHPCI/Languages/lang.fr.php +++ b/PHPCI/Languages/lang.fr.php @@ -39,6 +39,7 @@ PHPCI', 'reset_email_title' => 'Réinitialisation du mot de passe PHPCI pour %s', 'reset_invalid' => 'Requête de réinitialisation de mot de passe invalide.', 'email_address' => 'Adresse email', + 'login' => 'Login / Email Address', 'password' => 'Mot de passe', 'log_in' => 'Connexion', diff --git a/PHPCI/Languages/lang.it.php b/PHPCI/Languages/lang.it.php index f8f41ea8..ff326d14 100644 --- a/PHPCI/Languages/lang.it.php +++ b/PHPCI/Languages/lang.it.php @@ -39,6 +39,7 @@ PHPCI', 'reset_email_title' => 'Ripristino della password di PHPCI per %s', 'reset_invalid' => 'Richeista di ripristino password non valida.', 'email_address' => 'Indirizzo Email', + 'login' => 'Login / Email Address', 'password' => 'Password', 'log_in' => 'Accedi', diff --git a/PHPCI/Languages/lang.nl.php b/PHPCI/Languages/lang.nl.php index f3c0a7c5..e0638aaf 100644 --- a/PHPCI/Languages/lang.nl.php +++ b/PHPCI/Languages/lang.nl.php @@ -39,6 +39,7 @@ PHPCI', 'reset_email_title' => 'PHPCI wachtwoord reset voor %s', 'reset_invalid' => 'Ongeldig wachtwoord reset verzoek', 'email_address' => 'E-mailadres', + 'login' => 'Login / Email Address', 'password' => 'Wachtwoord', 'log_in' => 'Log in', diff --git a/PHPCI/Languages/lang.pl.php b/PHPCI/Languages/lang.pl.php index 37a78fb6..d6f95ad6 100644 --- a/PHPCI/Languages/lang.pl.php +++ b/PHPCI/Languages/lang.pl.php @@ -39,6 +39,7 @@ PHPCI', 'reset_email_title' => 'Reset Hasła PHPCI dla %s', 'reset_invalid' => 'Prośba o zmianę hasła jest nieważna.', 'email_address' => 'Adres email', + 'login' => 'Login / Email Address', 'password' => 'Hasło', 'log_in' => 'Zaloguj się', diff --git a/PHPCI/Languages/lang.ru.php b/PHPCI/Languages/lang.ru.php index 7627db85..a13c4a18 100644 --- a/PHPCI/Languages/lang.ru.php +++ b/PHPCI/Languages/lang.ru.php @@ -38,6 +38,7 @@ PHPCI', 'reset_email_title' => 'Сброс пароля PHPCI для %s', 'reset_invalid' => 'Некорректный запрос на сброс пароля.', 'email_address' => 'Email', + 'login' => 'Логин / Email', 'password' => 'Пароль', 'log_in' => 'Войти', diff --git a/PHPCI/Languages/lang.uk.php b/PHPCI/Languages/lang.uk.php index 2b2cc027..a3ca1b37 100644 --- a/PHPCI/Languages/lang.uk.php +++ b/PHPCI/Languages/lang.uk.php @@ -39,6 +39,7 @@ PHPCI', 'reset_email_title' => 'Скидання пароль PHPCI для %s', 'reset_invalid' => 'Невірний запит скидання паролю.', 'email_address' => 'Email адреса', + 'login' => 'Логин / Email адреса', 'password' => 'Пароль', 'log_in' => 'Увійти', diff --git a/PHPCI/Migrations/20150324174958_unique_email_and_name_user_fields.php b/PHPCI/Migrations/20150324174958_unique_email_and_name_user_fields.php new file mode 100644 index 00000000..f8d24ba0 --- /dev/null +++ b/PHPCI/Migrations/20150324174958_unique_email_and_name_user_fields.php @@ -0,0 +1,30 @@ +table('user'); + $user_table + ->addIndex('email', array('unique' => true)) + ->addIndex('name', array('unique' => true)) + ->save(); + } + + /** + * Migrate Down. + */ + public function down() + { + $user_table = $this->table('user'); + $user_table + ->removeIndex('email', array('unique' => true)) + ->removeIndex('name', array('unique' => true)) + ->save(); + } +} diff --git a/PHPCI/Store/Base/UserStoreBase.php b/PHPCI/Store/Base/UserStoreBase.php index d91271d0..f2893ac5 100644 --- a/PHPCI/Store/Base/UserStoreBase.php +++ b/PHPCI/Store/Base/UserStoreBase.php @@ -59,7 +59,7 @@ class UserStoreBase extends Store /** * Returns a User model by Email. - * @param mixed $value + * @param string $value * @param string $useConnection * @throws HttpException * @return \@appNamespace\Model\User|null @@ -82,4 +82,30 @@ class UserStoreBase extends Store return null; } + + /** + * Returns a User model by Email. + * @param string $value + * @param string $useConnection + * @throws HttpException + * @return \@appNamespace\Model\User|null + */ + public function getByLoginOrEmail($value, $useConnection = 'read') + { + if (is_null($value)) { + throw new HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); + } + + $query = 'SELECT * FROM `user` WHERE `name` = :value OR `email` = :value LIMIT 1'; + $stmt = Database::getConnection($useConnection)->prepare($query); + $stmt->bindValue(':value', $value); + + if ($stmt->execute()) { + if ($data = $stmt->fetch(\PDO::FETCH_ASSOC)) { + return new User($data); + } + } + + return null; + } } From 634b246ed5eb4b395fecd9a282423190d9232168 Mon Sep 17 00:00:00 2001 From: corpsee Date: Wed, 22 Apr 2015 16:24:03 +0600 Subject: [PATCH 795/933] Fixed ru strings for create build command (https://github.com/Block8/PHPCI/pull/889) Fixed ru strings for 'archived' Close #932 --- PHPCI/Languages/lang.ru.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/PHPCI/Languages/lang.ru.php b/PHPCI/Languages/lang.ru.php index a13c4a18..aa0f40fb 100644 --- a/PHPCI/Languages/lang.ru.php +++ b/PHPCI/Languages/lang.ru.php @@ -113,7 +113,7 @@ PHPCI', (если вы не добавили файл phpci.yml в репозиторий вашего проекта)', 'default_branch' => 'Ветка по умолчанию', 'allow_public_status' => 'Разрешить публичный статус и изображение (статуса) для проекта', - 'archived' => 'Archived', + 'archived' => 'Запакован', 'save_project' => 'Сохранить проект', 'error_mercurial' => 'URL репозитория Mercurial должен начинаться с http:// или https://', @@ -338,10 +338,10 @@ PHPCI', 'incorrect_format' => 'Неверный формат', // Create Build Command - 'create_build_project' => 'Create a build for a project', - 'project_id_argument' => 'A project ID', - 'commit_id_option' => 'Commit ID to build', - 'branch_name_option' => 'Branch to build', + 'create_build_project' => 'Создать сборку проекта', + 'project_id_argument' => 'ID проекта', + 'commit_id_option' => 'ID коммита для сборки', + 'branch_name_option' => 'Ветка для сборки', // Run Command 'run_all_pending' => 'Запустить все ожидающие PHPCI сборки.', From 209454c5f67e9fa8d1042b2410cd8350eeb4fcb7 Mon Sep 17 00:00:00 2001 From: Adirelle Date: Tue, 21 Apr 2015 14:51:55 +0200 Subject: [PATCH 796/933] When starting a manual build, replace the "Manual" commit id with the HEAD hash. Close #928 --- PHPCI/Model/Build/RemoteGitBuild.php | 16 ++++++++-------- PHPCI/Service/BuildService.php | 2 ++ Tests/PHPCI/Service/BuildServiceTest.php | 2 +- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/PHPCI/Model/Build/RemoteGitBuild.php b/PHPCI/Model/Build/RemoteGitBuild.php index 5d0c9419..faaccfb5 100644 --- a/PHPCI/Model/Build/RemoteGitBuild.php +++ b/PHPCI/Model/Build/RemoteGitBuild.php @@ -124,16 +124,16 @@ class RemoteGitBuild extends Build $success = true; $commit = $this->getCommitId(); + $chdir = IS_WIN ? 'cd /d "%s"' : 'cd "%s"'; + if (!empty($commit) && $commit != 'Manual') { - $cmd = 'cd "%s"'; + $cmd = $chdir . ' && git checkout %s --quiet'; + $success = $builder->executeCommand($cmd, $cloneTo, $commit); + } - if (IS_WIN) { - $cmd = 'cd /d "%s"'; - } - - $cmd .= ' && git checkout %s --quiet'; - - $success = $builder->executeCommand($cmd, $cloneTo, $this->getCommitId()); + // Always update the commit hash with the actual HEAD hash + if ($builder->executeCommand($chdir . ' && git rev-parse HEAD', $cloneTo)) { + $this->setCommitId(trim($builder->getLastOutput())); } return $success; diff --git a/PHPCI/Service/BuildService.php b/PHPCI/Service/BuildService.php index a95ac44f..ca2fae4d 100644 --- a/PHPCI/Service/BuildService.php +++ b/PHPCI/Service/BuildService.php @@ -9,6 +9,7 @@ namespace PHPCI\Service; +use PHPCI\Helper\Lang; use PHPCI\Model\Build; use PHPCI\Model\Project; use PHPCI\Store\BuildStore; @@ -59,6 +60,7 @@ class BuildService $build->setCommitId($commitId); } else { $build->setCommitId('Manual'); + $build->setCommitMessage(Lang::get('manual_build')); } if (!is_null($branch)) { diff --git a/Tests/PHPCI/Service/BuildServiceTest.php b/Tests/PHPCI/Service/BuildServiceTest.php index 426845a9..985f3088 100644 --- a/Tests/PHPCI/Service/BuildServiceTest.php +++ b/Tests/PHPCI/Service/BuildServiceTest.php @@ -57,7 +57,7 @@ class BuildServiceTest extends \PHPUnit_Framework_TestCase $this->assertNull($returnValue->getStarted()); $this->assertNull($returnValue->getFinished()); $this->assertNull($returnValue->getLog()); - $this->assertEmpty($returnValue->getCommitMessage()); + $this->assertEquals(\PHPCI\Helper\Lang::get('manual_build'), $returnValue->getCommitMessage()); $this->assertNull($returnValue->getCommitterEmail()); $this->assertNull($returnValue->getExtra()); $this->assertEquals('master', $returnValue->getBranch()); From 408eb5b974cb1af734053d78822621bc06ddd843 Mon Sep 17 00:00:00 2001 From: Adam Cooper Date: Mon, 8 Sep 2014 11:45:19 +0100 Subject: [PATCH 797/933] An attempt at making the codeception plugin a little more complete. Codeception JS plugin and theme changes. Improvements to the display. Extra total information plus some test file locations. Close #588 --- PHPCI/Languages/lang.el.php | 1 + PHPCI/Languages/lang.en.php | 6 + PHPCI/Plugin/Codeception.php | 178 ++++++++++-------- .../Util/TestResultParsers/Codeception.php | 108 +++++++++++ .../TestResultParsers/ParserInterface.php | 16 ++ public/assets/css/bootstrap-theme.min.css | 4 +- public/assets/css/bootstrap.min.css | 4 +- public/assets/js/bootstrap.min.js | 8 +- public/assets/js/build-plugins/codeception.js | 72 +++++-- 9 files changed, 294 insertions(+), 103 deletions(-) create mode 100644 PHPCI/Plugin/Util/TestResultParsers/Codeception.php create mode 100644 PHPCI/Plugin/Util/TestResultParsers/ParserInterface.php diff --git a/PHPCI/Languages/lang.el.php b/PHPCI/Languages/lang.el.php index 3e3a3b84..cb7d169f 100644 --- a/PHPCI/Languages/lang.el.php +++ b/PHPCI/Languages/lang.el.php @@ -172,6 +172,7 @@ Services του Bitbucket αποθετηρίου σας.', 'codeception_errors' => 'Λάθη Codeception', 'phpmd_warnings' => 'Προειδοποιήσεις PHPMD', 'phpcs_warnings' => 'Προειδοποιήσεις PHPCS ', + 'codeception_errors' => 'Λάθη Codeception', 'phpcs_errors' => 'Λάθη PHPCS', 'phplint_errors' => 'Λάθη Lint', 'phpunit_errors' => 'Λάθη PHPUnit ', diff --git a/PHPCI/Languages/lang.en.php b/PHPCI/Languages/lang.en.php index d11a8b39..d6c48754 100644 --- a/PHPCI/Languages/lang.en.php +++ b/PHPCI/Languages/lang.en.php @@ -190,6 +190,12 @@ PHPCI', 'technical_debt' => 'Technical Debt', 'behat' => 'Behat', + 'codeception_feature' => 'Feature', + 'codeception_suite' => 'Suite', + 'codeception_time' => 'Time', + 'codeception_synopsis' => '%1$d tests carried out in %2$f seconds. + %3$d failures.', + 'file' => 'File', 'line' => 'Line', 'class' => 'Class', diff --git a/PHPCI/Plugin/Codeception.php b/PHPCI/Plugin/Codeception.php index 9a651bec..fe12db76 100644 --- a/PHPCI/Plugin/Codeception.php +++ b/PHPCI/Plugin/Codeception.php @@ -9,50 +9,76 @@ namespace PHPCI\Plugin; -use PHPCI; use PHPCI\Builder; use PHPCI\Helper\Lang; use PHPCI\Model\Build; -use PHPCI\Plugin\Util\TapParser; +use PHPCI\Plugin\Util\TestResultParsers\Codeception as Parser; +use Psr\Log\LogLevel; /** - * Codeception Plugin - Enables full acceptance, unit, and functional testing - * + * Codeception Plugin - Enables full acceptance, unit, and functional testing. * @author Don Gilbert * @author Igor Timoshenko + * @author Adam Cooper * @package PHPCI * @subpackage Plugins */ -class Codeception implements PHPCI\Plugin, PHPCI\ZeroConfigPlugin +class Codeception implements \PHPCI\Plugin, \PHPCI\ZeroConfigPlugin { - /** - * @var string - */ + /** @var string */ protected $args = ''; - /** - * @var Build - */ + /** @var Builder */ + protected $phpci; + + /** @var Build */ protected $build; /** - * @var Builder + * @var string $ymlConfigFile The path of a yml config for Codeception */ - protected $phpci; + protected $ymlConfigFile; /** - * @var string|string[] The path (or array of paths) of an yml config for Codeception + * @var string $path The path to the codeception tests folder. */ - protected $configFile; + protected $path; /** - * @var string The path where the reports and logs are stored + * @param $stage + * @param Builder $builder + * @param Build $build + * @return bool */ - protected $logPath = 'tests/_output'; + public static function canExecute($stage, Builder $builder, Build $build) + { + if ($stage == 'test' && !is_null(self::findConfigFile($builder->buildPath))) { + return true; + } + + return false; + } + + /** + * Try and find the codeception YML config file. + * @param $buildPath + * @return null|string + */ + public static function findConfigFile($buildPath) + { + if (file_exists($buildPath . 'codeception.yml')) { + return 'codeception.yml'; + } + + if (file_exists($buildPath . 'codeception.dist.yml')) { + return 'codeception.dist.yml'; + } + + return null; + } /** * Set up the plugin, configure options, etc. - * * @param Builder $phpci * @param Build $build * @param array $options @@ -61,104 +87,96 @@ class Codeception implements PHPCI\Plugin, PHPCI\ZeroConfigPlugin { $this->phpci = $phpci; $this->build = $build; + $this->path = 'tests/'; - if (isset($options['config'])) { - $this->configFile = $options['config']; + if (empty($options['config'])) { + $this->ymlConfigFile = self::findConfigFile($this->phpci->buildPath); + } + if (isset($options['config'])) { + $this->ymlConfigFile = $options['config']; } - if (isset($options['args'])) { $this->args = (string) $options['args']; } - - if (isset($options['log_path'])) { - $this->logPath = $options['log_path']; + if (isset($options['path'])) { + $this->path = $options['path']; } } /** - * {@inheritDoc} + * Runs Codeception tests, optionally using specified config file(s). */ public function execute() { + if (empty($this->ymlConfigFile)) { + $this->phpci->logFailure('No configuration file found'); + return false; + } + $success = true; - $this->phpci->logExecOutput(false); - - // Run any config files first. This can be either a single value or an array - if ($this->configFile !== null) { - $success &= $this->runConfigFile($this->configFile); - } - - $tapString = file_get_contents( - $this->phpci->buildPath . $this->logPath . DIRECTORY_SEPARATOR . 'report.tap.log' - ); - - try { - $tapParser = new TapParser($tapString); - $output = $tapParser->parse(); - } catch (\Exception $ex) { - $this->phpci->logFailure($tapString); - - throw $ex; - } - - $failures = $tapParser->getTotalFailures(); - - $this->build->storeMeta('codeception-errors', $failures); - $this->build->storeMeta('codeception-data', $output); - - $this->phpci->logExecOutput(true); + // Run any config files first. This can be either a single value or an array. + $success &= $this->runConfigFile($this->ymlConfigFile); return $success; } /** - * {@inheritDoc} - */ - public static function canExecute($stage, Builder $builder, Build $build) - { - return $stage === 'test'; - } - - /** - * Run tests from a Codeception config file - * - * @param string $configPath + * Run tests from a Codeception config file. + * @param $configPath * @return bool|mixed + * @throws \Exception */ protected function runConfigFile($configPath) { if (is_array($configPath)) { return $this->recurseArg($configPath, array($this, 'runConfigFile')); } else { + $this->phpci->logExecOutput(false); + $codecept = $this->phpci->findBinary('codecept'); - $cmd = 'cd "%s" && ' . $codecept . ' run -c "%s" --tap ' . $this->args; + if (!$codecept) { + $this->phpci->logFailure(Lang::get('could_not_find', 'codecept')); + return false; + } + + $cmd = 'cd "%s" && ' . $codecept . ' run -c "%s" --xml ' . $this->args; if (IS_WIN) { - $cmd = 'cd /d "%s" && ' . $codecept . ' run -c "%s" --tap ' . $this->args; + $cmd = 'cd /d "%s" && ' . $codecept . ' run -c "%s" --xml ' . $this->args; } $configPath = $this->phpci->buildPath . $configPath; $success = $this->phpci->executeCommand($cmd, $this->phpci->buildPath, $configPath); + + $this->phpci->log( + 'Codeception XML path: '. $this->phpci->buildPath . $this->path . '_output/report.xml', + Loglevel::DEBUG + ); + $xml = file_get_contents($this->phpci->buildPath . $this->path . '_output/report.xml', false); + + try { + $parser = new Parser($this->phpci, $xml); + $output = $parser->parse(); + } catch (\Exception $ex) { + throw $ex; + } + + $meta = array( + 'tests' => $parser->getTotalTests(), + 'timetaken' => $parser->getTotalTimeTaken(), + 'failures' => $parser->getTotalFailures() + ); + + $this->build->storeMeta('codeception-meta', $meta); + $this->build->storeMeta('codeception-data', $output); + $this->build->storeMeta('codeception-errors', $parser->getTotalFailures()); + + $this->phpci->logExecOutput(true); + return $success; } } - - /** - * @param array $array - * @param \Callback $callable - * @return bool|mixed - */ - protected function recurseArg(array $array, $callable) - { - $success = true; - - foreach ($array as $subItem) { - $success &= call_user_func($callable, $subItem); - } - - return $success; - } } diff --git a/PHPCI/Plugin/Util/TestResultParsers/Codeception.php b/PHPCI/Plugin/Util/TestResultParsers/Codeception.php new file mode 100644 index 00000000..39f1ce6f --- /dev/null +++ b/PHPCI/Plugin/Util/TestResultParsers/Codeception.php @@ -0,0 +1,108 @@ + + * @package PHPCI\Plugin\Util\TestResultParsers + */ +class Codeception implements ParserInterface +{ + protected $phpci; + protected $resultsXml; + + protected $results; + + protected $totalTests; + protected $totalTimeTaken; + protected $totalFailures; + + /** + * @param Builder $phpci + * @param $resultsXml + */ + public function __construct(Builder $phpci, $resultsXml) + { + $this->phpci = $phpci; + $this->resultsXml = $resultsXml; + + $this->totalTests = 0; + } + + /** + * @return array An array of key/value pairs for storage in the plugins result metadata + */ + public function parse() + { + $rtn = array(); + + $this->results = new \SimpleXMLElement($this->resultsXml); + + // calculate total results + foreach ($this->results->testsuite as $testsuite) { + $this->totalTests += (int) $testsuite['tests']; + $this->totalTimeTaken += (float) $testsuite['time']; + $this->totalFailures += (int) $testsuite['failures']; + + foreach ($testsuite->testcase as $testcase) { + $testresult = array( + 'suite' => (string) $testsuite['name'], + 'file' => str_replace($this->phpci->buildPath, '/', (string) $testcase['file']), + 'name' => (string) $testcase['name'], + 'feature' => (string) $testcase['feature'], + 'assertions' => (int) $testcase['assertions'], + 'time' => (float) $testcase['time'] + ); + + if (isset($testcase['class'])) { + $testresult['class'] = (string) $testcase['class']; + } + + if (isset($testcase->failure)) { + $testresult['pass'] = false; + $testresult['message'] = (string) $testcase->failure; + } else { + $testresult['pass'] = true; + } + + $rtn[] = $testresult; + } + } + + return $rtn; + } + + /** + * Get the total number of tests performed. + * + * @return int + */ + public function getTotalTests() + { + return $this->totalTests; + } + + /** + * The time take to complete all tests + * + * @return mixed + */ + public function getTotalTimeTaken() + { + return $this->totalTimeTaken; + } + + /** + * A count of the test failures + * + * @return mixed + */ + public function getTotalFailures() + { + return $this->totalFailures; + } +} diff --git a/PHPCI/Plugin/Util/TestResultParsers/ParserInterface.php b/PHPCI/Plugin/Util/TestResultParsers/ParserInterface.php new file mode 100644 index 00000000..d0c77cb7 --- /dev/null +++ b/PHPCI/Plugin/Util/TestResultParsers/ParserInterface.php @@ -0,0 +1,16 @@ +li>a:hover,.dropdown-menu>li>a:focus{background-image:-webkit-linear-gradient(top, #f5f5f5 0, #e8e8e8 100%);background-image:linear-gradient(to bottom, #f5f5f5 0, #e8e8e8 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-color:#e8e8e8}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{background-image:-webkit-linear-gradient(top, #428bca 0, #357ebd 100%);background-image:linear-gradient(to bottom, #428bca 0, #357ebd 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0);background-color:#357ebd}.navbar-default{background-image:-webkit-linear-gradient(top, #fff 0, #f8f8f8 100%);background-image:linear-gradient(to bottom, #fff 0, #f8f8f8 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 5px rgba(0,0,0,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 5px rgba(0,0,0,0.075)}.navbar-default .navbar-nav>.active>a{background-image:-webkit-linear-gradient(top, #ebebeb 0, #f3f3f3 100%);background-image:linear-gradient(to bottom, #ebebeb 0, #f3f3f3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff3f3f3', GradientType=0);-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,0.075);box-shadow:inset 0 3px 9px rgba(0,0,0,0.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,0.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top, #3c3c3c 0, #222 100%);background-image:linear-gradient(to bottom, #3c3c3c 0, #222 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false)}.navbar-inverse .navbar-nav>.active>a{background-image:-webkit-linear-gradient(top, #222 0, #282828 100%);background-image:linear-gradient(to bottom, #222 0, #282828 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff282828', GradientType=0);-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,0.25);box-shadow:inset 0 3px 9px rgba(0,0,0,0.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.navbar-static-top,.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}.alert{text-shadow:0 1px 0 rgba(255,255,255,0.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.25),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.25),0 1px 2px rgba(0,0,0,0.05)}.alert-success{background-image:-webkit-linear-gradient(top, #dff0d8 0, #c8e5bc 100%);background-image:linear-gradient(to bottom, #dff0d8 0, #c8e5bc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);border-color:#b2dba1}.alert-info{background-image:-webkit-linear-gradient(top, #d9edf7 0, #b9def0 100%);background-image:linear-gradient(to bottom, #d9edf7 0, #b9def0 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);border-color:#9acfea}.alert-warning{background-image:-webkit-linear-gradient(top, #fcf8e3 0, #f8efc0 100%);background-image:linear-gradient(to bottom, #fcf8e3 0, #f8efc0 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);border-color:#f5e79e}.alert-danger{background-image:-webkit-linear-gradient(top, #f2dede 0, #e7c3c3 100%);background-image:linear-gradient(to bottom, #f2dede 0, #e7c3c3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);border-color:#dca7a7}.progress{background-image:-webkit-linear-gradient(top, #ebebeb 0, #f5f5f5 100%);background-image:linear-gradient(to bottom, #ebebeb 0, #f5f5f5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0)}.progress-bar{background-image:-webkit-linear-gradient(top, #428bca 0, #3071a9 100%);background-image:linear-gradient(to bottom, #428bca 0, #3071a9 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0)}.progress-bar-success{background-image:-webkit-linear-gradient(top, #5cb85c 0, #449d44 100%);background-image:linear-gradient(to bottom, #5cb85c 0, #449d44 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0)}.progress-bar-info{background-image:-webkit-linear-gradient(top, #5bc0de 0, #31b0d5 100%);background-image:linear-gradient(to bottom, #5bc0de 0, #31b0d5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0)}.progress-bar-warning{background-image:-webkit-linear-gradient(top, #f0ad4e 0, #ec971f 100%);background-image:linear-gradient(to bottom, #f0ad4e 0, #ec971f 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0)}.progress-bar-danger{background-image:-webkit-linear-gradient(top, #d9534f 0, #c9302c 100%);background-image:linear-gradient(to bottom, #d9534f 0, #c9302c 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.075);box-shadow:0 1px 2px rgba(0,0,0,0.075)}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{text-shadow:0 -1px 0 #3071a9;background-image:-webkit-linear-gradient(top, #428bca 0, #3278b3 100%);background-image:linear-gradient(to bottom, #428bca 0, #3278b3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3278b3', GradientType=0);border-color:#3278b3}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.05);box-shadow:0 1px 2px rgba(0,0,0,0.05)}.panel-default>.box-header{background-image:-webkit-linear-gradient(top, #f5f5f5 0, #e8e8e8 100%);background-image:linear-gradient(to bottom, #f5f5f5 0, #e8e8e8 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0)}.panel-primary>.box-header{background-image:-webkit-linear-gradient(top, #428bca 0, #357ebd 100%);background-image:linear-gradient(to bottom, #428bca 0, #357ebd 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0)}.panel-success>.box-header{background-image:-webkit-linear-gradient(top, #dff0d8 0, #d0e9c6 100%);background-image:linear-gradient(to bottom, #dff0d8 0, #d0e9c6 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0)}.panel-info>.box-header{background-image:-webkit-linear-gradient(top, #d9edf7 0, #c4e3f3 100%);background-image:linear-gradient(to bottom, #d9edf7 0, #c4e3f3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0)}.panel-warning>.box-header{background-image:-webkit-linear-gradient(top, #fcf8e3 0, #faf2cc 100%);background-image:linear-gradient(to bottom, #fcf8e3 0, #faf2cc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0)}.panel-danger>.box-header{background-image:-webkit-linear-gradient(top, #f2dede 0, #ebcccc 100%);background-image:linear-gradient(to bottom, #f2dede 0, #ebcccc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0)}.well{background-image:-webkit-linear-gradient(top, #e8e8e8 0, #f5f5f5 100%);background-image:linear-gradient(to bottom, #e8e8e8 0, #f5f5f5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);border-color:#dcdcdc;-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,0.05),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 3px rgba(0,0,0,0.05),0 1px 0 rgba(255,255,255,0.1)} \ No newline at end of file +.btn-default,.btn-primary,.btn-success,.btn-info,.btn-warning,.btn-danger{text-shadow:0 -1px 0 rgba(0,0,0,0.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 1px rgba(0,0,0,0.075)}.btn-default:active,.btn-primary:active,.btn-success:active,.btn-info:active,.btn-warning:active,.btn-danger:active,.btn-default.active,.btn-primary.active,.btn-success.active,.btn-info.active,.btn-warning.active,.btn-danger.active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn:active,.btn.active{background-image:none}.btn-default{background-image:-webkit-linear-gradient(top, #fff 0, #e0e0e0 100%);background-image:linear-gradient(to bottom, #fff 0, #e0e0e0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background-repeat:repeat-x;border-color:#dbdbdb;text-shadow:0 1px 0 #fff;border-color:#ccc}.btn-default:hover,.btn-default:focus{background-color:#e0e0e0;background-position:0 -15px}.btn-default:active,.btn-default.active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-primary{background-image:-webkit-linear-gradient(top, #428bca 0, #2d6ca2 100%);background-image:linear-gradient(to bottom, #428bca 0, #2d6ca2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff2d6ca2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background-repeat:repeat-x;border-color:#2b669a}.btn-primary:hover,.btn-primary:focus{background-color:#2d6ca2;background-position:0 -15px}.btn-primary:active,.btn-primary.active{background-color:#2d6ca2;border-color:#2b669a}.btn-success{background-image:-webkit-linear-gradient(top, #5cb85c 0, #419641 100%);background-image:linear-gradient(to bottom, #5cb85c 0, #419641 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background-repeat:repeat-x;border-color:#3e8f3e}.btn-success:hover,.btn-success:focus{background-color:#419641;background-position:0 -15px}.btn-success:active,.btn-success.active{background-color:#419641;border-color:#3e8f3e}.btn-info{background-image:-webkit-linear-gradient(top, #5bc0de 0, #2aabd2 100%);background-image:linear-gradient(to bottom, #5bc0de 0, #2aabd2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background-repeat:repeat-x;border-color:#28a4c9}.btn-info:hover,.btn-info:focus{background-color:#2aabd2;background-position:0 -15px}.btn-info:active,.btn-info.active{background-color:#2aabd2;border-color:#28a4c9}.btn-warning{background-image:-webkit-linear-gradient(top, #f0ad4e 0, #eb9316 100%);background-image:linear-gradient(to bottom, #f0ad4e 0, #eb9316 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background-repeat:repeat-x;border-color:#e38d13}.btn-warning:hover,.btn-warning:focus{background-color:#eb9316;background-position:0 -15px}.btn-warning:active,.btn-warning.active{background-color:#eb9316;border-color:#e38d13}.btn-danger{background-image:-webkit-linear-gradient(top, #d9534f 0, #c12e2a 100%);background-image:linear-gradient(to bottom, #d9534f 0, #c12e2a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background-repeat:repeat-x;border-color:#b92c28}.btn-danger:hover,.btn-danger:focus{background-color:#c12e2a;background-position:0 -15px}.btn-danger:active,.btn-danger.active{background-color:#c12e2a;border-color:#b92c28}.thumbnail,.img-thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.075);box-shadow:0 1px 2px rgba(0,0,0,0.075)}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{background-image:-webkit-linear-gradient(top, #f5f5f5 0, #e8e8e8 100%);background-image:linear-gradient(to bottom, #f5f5f5 0, #e8e8e8 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-color:#e8e8e8}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{background-image:-webkit-linear-gradient(top, #428bca 0, #357ebd 100%);background-image:linear-gradient(to bottom, #428bca 0, #357ebd 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0);background-color:#357ebd}.navbar-default{background-image:-webkit-linear-gradient(top, #fff 0, #f8f8f8 100%);background-image:linear-gradient(to bottom, #fff 0, #f8f8f8 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 5px rgba(0,0,0,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 5px rgba(0,0,0,0.075)}.navbar-default .navbar-nav>.active>a{background-image:-webkit-linear-gradient(top, #ebebeb 0, #f3f3f3 100%);background-image:linear-gradient(to bottom, #ebebeb 0, #f3f3f3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff3f3f3', GradientType=0);-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,0.075);box-shadow:inset 0 3px 9px rgba(0,0,0,0.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,0.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top, #3c3c3c 0, #222 100%);background-image:linear-gradient(to bottom, #3c3c3c 0, #222 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false)}.navbar-inverse .navbar-nav>.active>a{background-image:-webkit-linear-gradient(top, #222 0, #282828 100%);background-image:linear-gradient(to bottom, #222 0, #282828 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff282828', GradientType=0);-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,0.25);box-shadow:inset 0 3px 9px rgba(0,0,0,0.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.navbar-static-top,.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}.alert{text-shadow:0 1px 0 rgba(255,255,255,0.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.25),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.25),0 1px 2px rgba(0,0,0,0.05)}.alert-success{background-image:-webkit-linear-gradient(top, #dff0d8 0, #c8e5bc 100%);background-image:linear-gradient(to bottom, #dff0d8 0, #c8e5bc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);border-color:#b2dba1}.alert-info{background-image:-webkit-linear-gradient(top, #d9edf7 0, #b9def0 100%);background-image:linear-gradient(to bottom, #d9edf7 0, #b9def0 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);border-color:#9acfea}.alert-warning{background-image:-webkit-linear-gradient(top, #fcf8e3 0, #f8efc0 100%);background-image:linear-gradient(to bottom, #fcf8e3 0, #f8efc0 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);border-color:#f5e79e}.alert-danger{background-image:-webkit-linear-gradient(top, #f2dede 0, #e7c3c3 100%);background-image:linear-gradient(to bottom, #f2dede 0, #e7c3c3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);border-color:#dca7a7}.progress{background-image:-webkit-linear-gradient(top, #ebebeb 0, #f5f5f5 100%);background-image:linear-gradient(to bottom, #ebebeb 0, #f5f5f5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0)}.progress-bar{background-image:-webkit-linear-gradient(top, #428bca 0, #3071a9 100%);background-image:linear-gradient(to bottom, #428bca 0, #3071a9 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0)}.progress-bar-success{background-image:-webkit-linear-gradient(top, #5cb85c 0, #449d44 100%);background-image:linear-gradient(to bottom, #5cb85c 0, #449d44 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0)}.progress-bar-info{background-image:-webkit-linear-gradient(top, #5bc0de 0, #31b0d5 100%);background-image:linear-gradient(to bottom, #5bc0de 0, #31b0d5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0)}.progress-bar-warning{background-image:-webkit-linear-gradient(top, #f0ad4e 0, #ec971f 100%);background-image:linear-gradient(to bottom, #f0ad4e 0, #ec971f 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0)}.progress-bar-danger{background-image:-webkit-linear-gradient(top, #d9534f 0, #c9302c 100%);background-image:linear-gradient(to bottom, #d9534f 0, #c9302c 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.075);box-shadow:0 1px 2px rgba(0,0,0,0.075)}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{text-shadow:0 -1px 0 #3071a9;background-image:-webkit-linear-gradient(top, #428bca 0, #3278b3 100%);background-image:linear-gradient(to bottom, #428bca 0, #3278b3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3278b3', GradientType=0);border-color:#3278b3}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.05);box-shadow:0 1px 2px rgba(0,0,0,0.05)}.panel-default>.box-header{background-image:-webkit-linear-gradient(top, #f5f5f5 0, #e8e8e8 100%);background-image:linear-gradient(to bottom, #f5f5f5 0, #e8e8e8 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0)}.panel-primary>.box-header{background-image:-webkit-linear-gradient(top, #428bca 0, #357ebd 100%);background-image:linear-gradient(to bottom, #428bca 0, #357ebd 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0)}.panel-success>.box-header{background-image:-webkit-linear-gradient(top, #dff0d8 0, #d0e9c6 100%);background-image:linear-gradient(to bottom, #dff0d8 0, #d0e9c6 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0)}.panel-info>.box-header{background-image:-webkit-linear-gradient(top, #d9edf7 0, #c4e3f3 100%);background-image:linear-gradient(to bottom, #d9edf7 0, #c4e3f3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0)}.panel-warning>.box-header{background-image:-webkit-linear-gradient(top, #fcf8e3 0, #faf2cc 100%);background-image:linear-gradient(to bottom, #fcf8e3 0, #faf2cc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0)}.panel-danger>.box-header{background-image:-webkit-linear-gradient(top, #f2dede 0, #ebcccc 100%);background-image:linear-gradient(to bottom, #f2dede 0, #ebcccc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0)}.well{background-image:-webkit-linear-gradient(top, #e8e8e8 0, #f5f5f5 100%);background-image:linear-gradient(to bottom, #e8e8e8 0, #f5f5f5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);border-color:#dcdcdc;-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,0.05),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 3px rgba(0,0,0,0.05),0 1px 0 rgba(255,255,255,0.1)} diff --git a/public/assets/css/bootstrap.min.css b/public/assets/css/bootstrap.min.css index 7af7ea7c..7e029af9 100755 --- a/public/assets/css/bootstrap.min.css +++ b/public/assets/css/bootstrap.min.css @@ -1,7 +1,7 @@ /*! - * Bootstrap v3.1.1 (http://getbootstrap.com) + * Bootstrap v3.2.0 (http://getbootstrap.com) * Copyright 2011-2014 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) */ -/*! normalize.css v3.0.0 | MIT License | git.io/normalize */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}@media print{*{text-shadow:none !important;color:#000 !important;background:transparent !important;box-shadow:none !important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}select{background:#fff !important}.navbar{display:none}.table td,.table th{background-color:#fff !important}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000 !important}.label{border:1px solid #000}.table{border-collapse:collapse !important}.table-bordered th,.table-bordered td{border:1px solid #ddd !important}}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}*:before,*:after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:62.5%;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#428bca;text-decoration:none}a:hover,a:focus{color:#2a6496;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small,.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small,h1 .small,h2 .small,h3 .small,h4 .small,h5 .small,h6 .small,.h1 .small,.h2 .small,.h3 .small,.h4 .small,.h5 .small,.h6 .small{font-weight:normal;line-height:1;color:#999}h1,.h1,h2,.h2,h3,.h3{margin-top:20px;margin-bottom:10px}h1 small,.h1 small,h2 small,.h2 small,h3 small,.h3 small,h1 .small,.h1 .small,h2 .small,.h2 .small,h3 .small,.h3 .small{font-size:65%}h4,.h4,h5,.h5,h6,.h6{margin-top:10px;margin-bottom:10px}h4 small,.h4 small,h5 small,.h5 small,h6 small,.h6 small,h4 .small,.h4 .small,h5 .small,.h5 .small,h6 .small,.h6 .small{font-size:75%}h1,.h1{font-size:36px}h2,.h2{font-size:30px}h3,.h3{font-size:24px}h4,.h4{font-size:18px}h5,.h5{font-size:14px}h6,.h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:200;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}small,.small{font-size:85%}cite{font-style:normal}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-muted{color:#999}.text-primary{color:#428bca}a.text-primary:hover{color:#3071a9}.text-success{color:#3c763d}a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#428bca}a.bg-primary:hover{background-color:#3071a9}.bg-success{background-color:#dff0d8}a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ul,ol{margin-top:0;margin-bottom:10px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none;margin-left:-5px}.list-inline>li{display:inline-block;padding-left:5px;padding-right:5px}dl{margin-top:0;margin-bottom:20px}dt,dd{line-height:1.42857143}dt{font-weight:bold}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #999}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}blockquote footer,blockquote small,blockquote .small{display:block;font-size:80%;line-height:1.42857143;color:#999}blockquote footer:before,blockquote small:before,blockquote .small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;border-right:5px solid #eee;border-left:0;text-align:right}.blockquote-reverse footer:before,blockquote.pull-right footer:before,.blockquote-reverse small:before,blockquote.pull-right small:before,.blockquote-reverse .small:before,blockquote.pull-right .small:before{content:''}.blockquote-reverse footer:after,blockquote.pull-right footer:after,.blockquote-reverse small:after,blockquote.pull-right small:after,.blockquote-reverse .small:after,blockquote.pull-right .small:after{content:'\00A0 \2014'}blockquote:before,blockquote:after{content:""}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;white-space:nowrap;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.25)}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;word-break:break-all;word-wrap:break-word;color:#333;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}.row{margin-left:-15px;margin-right:-15px}.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12{position:relative;min-height:1px;padding-left:15px;padding-right:15px}.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:0}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:0}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:0}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:0}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:0}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:0}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:0}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:0}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{max-width:100%;background-color:transparent}th{text-align:left}.table{width:100%;margin-bottom:20px}.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>thead>tr>th,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-child(odd)>td,.table-striped>tbody>tr:nth-child(odd)>th{background-color:#f9f9f9}.table-hover>tbody>tr:hover>td,.table-hover>tbody>tr:hover>th{background-color:#f5f5f5}table col[class*="col-"]{position:static;float:none;display:table-column}table td[class*="col-"],table th[class*="col-"]{position:static;float:none;display:table-cell}.table>thead>tr>td.active,.table>tbody>tr>td.active,.table>tfoot>tr>td.active,.table>thead>tr>th.active,.table>tbody>tr>th.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>tbody>tr.active>td,.table>tfoot>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr.active>th,.table>tfoot>tr.active>th{background-color:#f5f5f5}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th{background-color:#e8e8e8}.table>thead>tr>td.success,.table>tbody>tr>td.success,.table>tfoot>tr>td.success,.table>thead>tr>th.success,.table>tbody>tr>th.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>tbody>tr.success>td,.table>tfoot>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr.success>th,.table>tfoot>tr.success>th{background-color:#dff0d8}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th{background-color:#d0e9c6}.table>thead>tr>td.info,.table>tbody>tr>td.info,.table>tfoot>tr>td.info,.table>thead>tr>th.info,.table>tbody>tr>th.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>tbody>tr.info>td,.table>tfoot>tr.info>td,.table>thead>tr.info>th,.table>tbody>tr.info>th,.table>tfoot>tr.info>th{background-color:#d9edf7}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th{background-color:#c4e3f3}.table>thead>tr>td.warning,.table>tbody>tr>td.warning,.table>tfoot>tr>td.warning,.table>thead>tr>th.warning,.table>tbody>tr>th.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>tbody>tr.warning>td,.table>tfoot>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr.warning>th,.table>tfoot>tr.warning>th{background-color:#fcf8e3}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th{background-color:#faf2cc}.table>thead>tr>td.danger,.table>tbody>tr>td.danger,.table>tfoot>tr>td.danger,.table>thead>tr>th.danger,.table>tbody>tr>th.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>tbody>tr.danger>td,.table>tfoot>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr.danger>th,.table>tfoot>tr.danger>th{background-color:#f2dede}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th{background-color:#ebcccc}@media (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;overflow-x:scroll;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd;-webkit-overflow-scrolling:touch}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{padding:0;margin:0;border:0;min-width:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;margin-bottom:5px;font-weight:bold}input[type="search"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type="file"]{display:block}input[type="range"]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border-color ease-in-out .15s, box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s, box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{cursor:not-allowed;background-color:#eee;opacity:1}textarea.form-control{height:auto}input[type="search"]{-webkit-appearance:none}input[type="date"]{line-height:34px}.form-group{margin-bottom:15px}.radio,.checkbox{display:block;min-height:20px;margin-top:10px;margin-bottom:10px;padding-left:20px}.radio label,.checkbox label{display:inline;font-weight:normal;cursor:pointer}.radio input[type="radio"],.radio-inline input[type="radio"],.checkbox input[type="checkbox"],.checkbox-inline input[type="checkbox"]{float:left;margin-left:-20px}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{display:inline-block;padding-left:20px;margin-bottom:0;vertical-align:middle;font-weight:normal;cursor:pointer}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}input[type="radio"][disabled],input[type="checkbox"][disabled],.radio[disabled],.radio-inline[disabled],.checkbox[disabled],.checkbox-inline[disabled],fieldset[disabled] input[type="radio"],fieldset[disabled] input[type="checkbox"],fieldset[disabled] .radio,fieldset[disabled] .radio-inline,fieldset[disabled] .checkbox,fieldset[disabled] .checkbox-inline{cursor:not-allowed}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}textarea.input-sm,select[multiple].input-sm{height:auto}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}select.input-lg{height:46px;line-height:46px}textarea.input-lg,select[multiple].input-lg{height:auto}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.has-feedback .form-control-feedback{position:absolute;top:25px;right:0;display:block;width:34px;height:34px;line-height:34px;text-align:center}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;border-color:#3c763d;background-color:#dff0d8}.has-success .form-control-feedback{color:#3c763d}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;border-color:#8a6d3b;background-color:#fcf8e3}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;border-color:#a94442;background-color:#f2dede}.has-error .form-control-feedback{color:#a94442}.form-control-static{margin-bottom:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.form-inline .checkbox{display:inline-block;margin-top:0;margin-bottom:0;padding-left:0;vertical-align:middle}.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{float:none;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .control-label,.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{margin-top:0;margin-bottom:0;padding-top:7px}.form-horizontal .radio,.form-horizontal .checkbox{min-height:27px}.form-horizontal .form-group{margin-left:-15px;margin-right:-15px}.form-horizontal .form-control-static{padding-top:7px}@media (min-width:768px){.form-horizontal .control-label{text-align:right}}.form-horizontal .has-feedback .form-control-feedback{top:0;right:15px}.btn{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;vertical-align:middle;cursor:pointer;background-image:none;border:1px solid transparent;white-space:nowrap;padding:6px 12px;font-size:14px;line-height:1.42857143;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn:focus,.btn:active:focus,.btn.active:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus{color:#333;text-decoration:none}.btn:active,.btn.active{outline:0;background-image:none;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;pointer-events:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default:hover,.btn-default:focus,.btn-default:active,.btn-default.active,.open .dropdown-toggle.btn-default{color:#333;background-color:#ebebeb;border-color:#adadad}.btn-default:active,.btn-default.active,.open .dropdown-toggle.btn-default{background-image:none}.btn-default.disabled,.btn-default[disabled],fieldset[disabled] .btn-default,.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled:active,.btn-default[disabled]:active,fieldset[disabled] .btn-default:active,.btn-default.disabled.active,.btn-default[disabled].active,fieldset[disabled] .btn-default.active{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#428bca;border-color:#357ebd}.btn-primary:hover,.btn-primary:focus,.btn-primary:active,.btn-primary.active,.open .dropdown-toggle.btn-primary{color:#fff;background-color:#3276b1;border-color:#285e8e}.btn-primary:active,.btn-primary.active,.open .dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled,.btn-primary[disabled],fieldset[disabled] .btn-primary,.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled:active,.btn-primary[disabled]:active,fieldset[disabled] .btn-primary:active,.btn-primary.disabled.active,.btn-primary[disabled].active,fieldset[disabled] .btn-primary.active{background-color:#428bca;border-color:#357ebd}.btn-primary .badge{color:#428bca;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success:hover,.btn-success:focus,.btn-success:active,.btn-success.active,.open .dropdown-toggle.btn-success{color:#fff;background-color:#47a447;border-color:#398439}.btn-success:active,.btn-success.active,.open .dropdown-toggle.btn-success{background-image:none}.btn-success.disabled,.btn-success[disabled],fieldset[disabled] .btn-success,.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled:active,.btn-success[disabled]:active,fieldset[disabled] .btn-success:active,.btn-success.disabled.active,.btn-success[disabled].active,fieldset[disabled] .btn-success.active{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info:hover,.btn-info:focus,.btn-info:active,.btn-info.active,.open .dropdown-toggle.btn-info{color:#fff;background-color:#39b3d7;border-color:#269abc}.btn-info:active,.btn-info.active,.open .dropdown-toggle.btn-info{background-image:none}.btn-info.disabled,.btn-info[disabled],fieldset[disabled] .btn-info,.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled:active,.btn-info[disabled]:active,fieldset[disabled] .btn-info:active,.btn-info.disabled.active,.btn-info[disabled].active,fieldset[disabled] .btn-info.active{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning:hover,.btn-warning:focus,.btn-warning:active,.btn-warning.active,.open .dropdown-toggle.btn-warning{color:#fff;background-color:#ed9c28;border-color:#d58512}.btn-warning:active,.btn-warning.active,.open .dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-warning,.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled:active,.btn-warning[disabled]:active,fieldset[disabled] .btn-warning:active,.btn-warning.disabled.active,.btn-warning[disabled].active,fieldset[disabled] .btn-warning.active{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger:hover,.btn-danger:focus,.btn-danger:active,.btn-danger.active,.open .dropdown-toggle.btn-danger{color:#fff;background-color:#d2322d;border-color:#ac2925}.btn-danger:active,.btn-danger.active,.open .dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled,.btn-danger[disabled],fieldset[disabled] .btn-danger,.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled:active,.btn-danger[disabled]:active,fieldset[disabled] .btn-danger:active,.btn-danger.disabled.active,.btn-danger[disabled].active,fieldset[disabled] .btn-danger.active{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{color:#428bca;font-weight:normal;cursor:pointer;border-radius:0}.btn-link,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#2a6496;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#999;text-decoration:none}.btn-lg,.btn-group-lg>.btn{padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}.btn-sm,.btn-group-sm>.btn{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-xs,.btn-group-xs>.btn{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%;padding-left:0;padding-right:0}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition:height .35s ease;transition:height .35s ease}@font-face{font-family:'Glyphicons Halflings';src:url('../fonts/glyphicons-halflings-regular.eot');src:url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'),url('../fonts/glyphicons-halflings-regular.woff') format('woff'),url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'),url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:normal;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\2a"}.glyphicon-plus:before{content:"\2b"}.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px solid;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;font-size:14px;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,0.175);box-shadow:0 6px 12px rgba(0,0,0,0.175);background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:normal;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{text-decoration:none;color:#262626;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#fff;text-decoration:none;outline:0;background-color:#428bca}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#999}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);cursor:not-allowed}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{left:auto;right:0}.dropdown-menu-left{left:0;right:auto}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#999}.dropdown-backdrop{position:fixed;left:0;right:0;bottom:0;top:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid;content:""}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px}@media (min-width:768px){.navbar-right .dropdown-menu{left:auto;right:0}.navbar-right .dropdown-menu-left{left:0;right:auto}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover,.btn-group>.btn:focus,.btn-group-vertical>.btn:focus,.btn-group>.btn:active,.btn-group-vertical>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn.active{z-index:2}.btn-group>.btn:focus,.btn-group-vertical>.btn:focus{outline:none}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-top-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child>.btn:last-child,.btn-group>.btn-group:first-child>.dropdown-toggle{border-bottom-right-radius:0;border-top-right-radius:0}.btn-group>.btn-group:last-child>.btn:first-child{border-bottom-left-radius:0;border-top-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-left:8px;padding-right:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-left:12px;padding-right:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-bottom-left-radius:4px;border-top-right-radius:0;border-top-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-right-radius:0;border-top-left-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{float:none;display:table-cell;width:1%}.btn-group-justified>.btn-group .btn{width:100%}[data-toggle="buttons"]>.btn>input[type="radio"],[data-toggle="buttons"]>.btn>input[type="checkbox"]{display:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*="col-"]{float:none;padding-left:0;padding-right:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn,select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn,select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn{height:auto}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:normal;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type="radio"],.input-group-addon input[type="checkbox"]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group-btn:last-child>.btn-group:not(:last-child)>.btn{border-bottom-right-radius:0;border-top-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:first-child>.btn-group:not(:first-child)>.btn{border-bottom-left-radius:0;border-top-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:hover,.input-group-btn>.btn:focus,.input-group-btn>.btn:active{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{margin-left:-1px}.nav{margin-bottom:0;padding-left:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#999}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#999;text-decoration:none;background-color:transparent;cursor:not-allowed}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#eee;border-color:#428bca}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#555;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent;cursor:default}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{text-align:center;margin-bottom:5px}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#fff;background-color:#428bca}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{text-align:center;margin-bottom:5px}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-right-radius:0;border-top-left-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{max-height:340px;overflow-x:visible;padding-right:15px;padding-left:15px;border-top:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,0.1);-webkit-overflow-scrolling:touch}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;box-shadow:none}.navbar-collapse.collapse{display:block !important;height:auto !important;padding-bottom:0;overflow:visible !important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{padding-left:0;padding-right:0}}.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;padding:15px 15px;font-size:18px;line-height:20px;height:50px}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;margin-right:15px;padding:9px 10px;margin-top:8px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:none}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;box-shadow:none}.navbar-nav .open .dropdown-menu>li>a,.navbar-nav .open .dropdown-menu .dropdown-header{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:hover,.navbar-nav .open .dropdown-menu>li>a:focus{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}.navbar-nav.navbar-right:last-child{margin-right:-15px}}@media (min-width:768px){.navbar-left{float:left !important}.navbar-right{float:right !important}}.navbar-form{margin-left:-15px;margin-right:-15px;padding:10px 15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);margin-top:8px;margin-bottom:8px}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .radio,.navbar-form .checkbox{display:inline-block;margin-top:0;margin-bottom:0;padding-left:0;vertical-align:middle}.navbar-form .radio input[type="radio"],.navbar-form .checkbox input[type="checkbox"]{float:none;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}}@media (min-width:768px){.navbar-form{width:auto;border:0;margin-left:0;margin-right:0;padding-top:0;padding-bottom:0;-webkit-box-shadow:none;box-shadow:none}.navbar-form.navbar-right:last-child{margin-right:-15px}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-right-radius:0;border-top-left-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-left:15px;margin-right:15px}.navbar-text.navbar-right:last-child{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:hover,.navbar-default .navbar-brand:focus{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:hover,.navbar-default .navbar-nav>.disabled>a:focus{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:hover,.navbar-default .navbar-toggle:focus{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{background-color:#e7e7e7;color:#555}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#999}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#999}.navbar-inverse .navbar-nav>li>a{color:#999}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{background-color:#080808;color:#fff}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#999}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#999}.navbar-inverse .navbar-link:hover{color:#fff}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;line-height:1.42857143;text-decoration:none;color:#428bca;background-color:#fff;border:1px solid #ddd;margin-left:-1px}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-bottom-left-radius:4px;border-top-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-bottom-right-radius:4px;border-top-right-radius:4px}.pagination>li>a:hover,.pagination>li>span:hover,.pagination>li>a:focus,.pagination>li>span:focus{color:#2a6496;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>span,.pagination>.active>a:hover,.pagination>.active>span:hover,.pagination>.active>a:focus,.pagination>.active>span:focus{z-index:2;color:#fff;background-color:#428bca;border-color:#428bca;cursor:default}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#999;background-color:#fff;border-color:#ddd;cursor:not-allowed}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-bottom-left-radius:6px;border-top-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-bottom-right-radius:6px;border-top-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-bottom-left-radius:3px;border-top-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-bottom-right-radius:3px;border-top-right-radius:3px}.pager{padding-left:0;margin:20px 0;list-style:none;text-align:center}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#999;background-color:#fff;cursor:not-allowed}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:bold;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}.label[href]:hover,.label[href]:focus{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#999}.label-default[href]:hover,.label-default[href]:focus{background-color:#808080}.label-primary{background-color:#428bca}.label-primary[href]:hover,.label-primary[href]:focus{background-color:#3071a9}.label-success{background-color:#5cb85c}.label-success[href]:hover,.label-success[href]:focus{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:hover,.label-info[href]:focus{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:bold;color:#fff;line-height:1;vertical-align:baseline;white-space:nowrap;text-align:center;background-color:#999;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-xs .badge{top:0;padding:1px 5px}a.badge:hover,a.badge:focus{color:#fff;text-decoration:none;cursor:pointer}a.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#428bca;background-color:#fff}.nav-pills>li>a>.badge{margin-left:3px}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:bold}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable{padding-right:35px}.alert-dismissable .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{background-color:#dff0d8;border-color:#d6e9c6;color:#3c763d}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{background-color:#d9edf7;border-color:#bce8f1;color:#31708f}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{background-color:#fcf8e3;border-color:#faebcc;color:#8a6d3b}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{background-color:#f2dede;border-color:#ebccd1;color:#a94442}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}.list-group{margin-bottom:20px;padding-left:0}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-right-radius:4px;border-top-left-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}a.list-group-item{color:#555}a.list-group-item .list-group-item-heading{color:#333}a.list-group-item:hover,a.list-group-item:focus{text-decoration:none;background-color:#f5f5f5}a.list-group-item.active,a.list-group-item.active:hover,a.list-group-item.active:focus{z-index:2;color:#fff;background-color:#428bca;border-color:#428bca}a.list-group-item.active .list-group-item-heading,a.list-group-item.active:hover .list-group-item-heading,a.list-group-item.active:focus .list-group-item-heading{color:inherit}a.list-group-item.active .list-group-item-text,a.list-group-item.active:hover .list-group-item-text,a.list-group-item.active:focus .list-group-item-text{color:#e1edf7}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:hover,a.list-group-item-success:focus{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:hover,a.list-group-item-success.active:focus{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:hover,a.list-group-item-info:focus{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:hover,a.list-group-item-info.active:focus{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:hover,a.list-group-item-warning:focus{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:hover,a.list-group-item-danger:focus{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.05);box-shadow:0 1px 1px rgba(0,0,0,0.05)}.box-body{padding:15px}.box-header{padding:10px 15px;border-bottom:1px solid transparent;border-top-right-radius:3px;border-top-left-radius:3px}.box-header>.dropdown .dropdown-toggle{color:inherit}.box-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.box-title>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group{margin-bottom:0}.panel>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-right-radius:3px;border-top-left-radius:3px}.panel>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.box-header+.list-group .list-group-item:first-child{border-top-width:0}.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.table:first-child,.panel>.table-responsive:first-child>.table:first-child{border-top-right-radius:3px;border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table:last-child,.panel>.table-responsive:last-child>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.box-body+.table,.panel>.box-body+.table-responsive{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child th,.panel>.table>tbody:first-child>tr:first-child td{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{border:0;margin-bottom:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px;overflow:hidden}.panel-group .panel+.panel{margin-top:5px}.panel-group .box-header{border-bottom:0}.panel-group .box-header+.panel-collapse .box-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .box-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.box-header{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.box-header+.panel-collapse .box-body{border-top-color:#ddd}.panel-default>.panel-footer+.panel-collapse .box-body{border-bottom-color:#ddd}.panel-primary{border-color:#428bca}.panel-primary>.box-header{color:#fff;background-color:#428bca;border-color:#428bca}.panel-primary>.box-header+.panel-collapse .box-body{border-top-color:#428bca}.panel-primary>.panel-footer+.panel-collapse .box-body{border-bottom-color:#428bca}.panel-success{border-color:#d6e9c6}.panel-success>.box-header{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.box-header+.panel-collapse .box-body{border-top-color:#d6e9c6}.panel-success>.panel-footer+.panel-collapse .box-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.box-header{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.box-header+.panel-collapse .box-body{border-top-color:#bce8f1}.panel-info>.panel-footer+.panel-collapse .box-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.box-header{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.box-header+.panel-collapse .box-body{border-top-color:#faebcc}.panel-warning>.panel-footer+.panel-collapse .box-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.box-header{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.box-header+.panel-collapse .box-body{border-top-color:#ebccd1}.panel-danger>.panel-footer+.panel-collapse .box-body{border-bottom-color:#ebccd1}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:bold;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.2;filter:alpha(opacity=20)}.close:hover,.close:focus{color:#000;text-decoration:none;cursor:pointer;opacity:.5;filter:alpha(opacity=50)}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.modal-open{overflow:hidden}.modal{display:none;overflow:auto;overflow-y:scroll;position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transform:translate(0, -25%);-ms-transform:translate(0, -25%);transform:translate(0, -25%);-webkit-transition:-webkit-transform 0.3s ease-out;-moz-transition:-moz-transform 0.3s ease-out;-o-transition:-o-transform 0.3s ease-out;transition:transform 0.3s ease-out}.modal.in .modal-dialog{-webkit-transform:translate(0, 0);-ms-transform:translate(0, 0);transform:translate(0, 0)}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;border:1px solid #999;border:1px solid rgba(0,0,0,0.2);border-radius:6px;-webkit-box-shadow:0 3px 9px rgba(0,0,0,0.5);box-shadow:0 3px 9px rgba(0,0,0,0.5);background-clip:padding-box;outline:none}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0;filter:alpha(opacity=0)}.modal-backdrop.in{opacity:.5;filter:alpha(opacity=50)}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5;min-height:16.42857143px}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:20px}.modal-footer{margin-top:15px;padding:19px 20px 20px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-left:5px;margin-bottom:0}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,0.5);box-shadow:0 5px 15px rgba(0,0,0,0.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.clearfix:before,.clearfix:after,.container:before,.container:after,.container-fluid:before,.container-fluid:after,.row:before,.row:after,.form-horizontal .form-group:before,.form-horizontal .form-group:after,.btn-toolbar:before,.btn-toolbar:after,.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after,.nav:before,.nav:after,.navbar:before,.navbar:after,.navbar-header:before,.navbar-header:after,.navbar-collapse:before,.navbar-collapse:after,.pager:before,.pager:after,.box-body:before,.box-body:after,.modal-footer:before,.modal-footer:after{content:" ";display:table}.clearfix:after,.container:after,.container-fluid:after,.row:after,.form-horizontal .form-group:after,.btn-toolbar:after,.btn-group-vertical>.btn-group:after,.nav:after,.navbar:after,.navbar-header:after,.navbar-collapse:after,.pager:after,.box-body:after,.modal-footer:after{clear:both}.center-block{display:block;margin-left:auto;margin-right:auto}.pull-right{float:right !important}.pull-left{float:left !important}.hide{display:none !important}.show{display:block !important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none !important;visibility:hidden !important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-xs,.visible-sm,.visible-md,.visible-lg{display:none !important}@media (max-width:767px){.visible-xs{display:block !important}table.visible-xs{display:table}tr.visible-xs{display:table-row !important}th.visible-xs,td.visible-xs{display:table-cell !important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block !important}table.visible-sm{display:table}tr.visible-sm{display:table-row !important}th.visible-sm,td.visible-sm{display:table-cell !important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block !important}table.visible-md{display:table}tr.visible-md{display:table-row !important}th.visible-md,td.visible-md{display:table-cell !important}}@media (min-width:1200px){.visible-lg{display:block !important}table.visible-lg{display:table}tr.visible-lg{display:table-row !important}th.visible-lg,td.visible-lg{display:table-cell !important}}@media (max-width:767px){.hidden-xs{display:none !important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none !important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none !important}}@media (min-width:1200px){.hidden-lg{display:none !important}}.visible-print{display:none !important}@media print{.visible-print{display:block !important}table.visible-print{display:table}tr.visible-print{display:table-row !important}th.visible-print,td.visible-print{display:table-cell !important}}@media print{.hidden-print{display:none !important}} \ No newline at end of file +/*! normalize.css v3.0.0 | MIT License | git.io/normalize */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}@media print{*{text-shadow:none !important;color:#000 !important;background:transparent !important;box-shadow:none !important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}select{background:#fff !important}.navbar{display:none}.table td,.table th{background-color:#fff !important}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000 !important}.label{border:1px solid #000}.table{border-collapse:collapse !important}.table-bordered th,.table-bordered td{border:1px solid #ddd !important}}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}*:before,*:after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:62.5%;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#428bca;text-decoration:none}a:hover,a:focus{color:#2a6496;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small,.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small,h1 .small,h2 .small,h3 .small,h4 .small,h5 .small,h6 .small,.h1 .small,.h2 .small,.h3 .small,.h4 .small,.h5 .small,.h6 .small{font-weight:normal;line-height:1;color:#999}h1,.h1,h2,.h2,h3,.h3{margin-top:20px;margin-bottom:10px}h1 small,.h1 small,h2 small,.h2 small,h3 small,.h3 small,h1 .small,.h1 .small,h2 .small,.h2 .small,h3 .small,.h3 .small{font-size:65%}h4,.h4,h5,.h5,h6,.h6{margin-top:10px;margin-bottom:10px}h4 small,.h4 small,h5 small,.h5 small,h6 small,.h6 small,h4 .small,.h4 .small,h5 .small,.h5 .small,h6 .small,.h6 .small{font-size:75%}h1,.h1{font-size:36px}h2,.h2{font-size:30px}h3,.h3{font-size:24px}h4,.h4{font-size:18px}h5,.h5{font-size:14px}h6,.h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:200;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}small,.small{font-size:85%}cite{font-style:normal}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-muted{color:#999}.text-primary{color:#428bca}a.text-primary:hover{color:#3071a9}.text-success{color:#3c763d}a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#428bca}a.bg-primary:hover{background-color:#3071a9}.bg-success{background-color:#dff0d8}a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ul,ol{margin-top:0;margin-bottom:10px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none;margin-left:-5px}.list-inline>li{display:inline-block;padding-left:5px;padding-right:5px}dl{margin-top:0;margin-bottom:20px}dt,dd{line-height:1.42857143}dt{font-weight:bold}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #999}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}blockquote footer,blockquote small,blockquote .small{display:block;font-size:80%;line-height:1.42857143;color:#999}blockquote footer:before,blockquote small:before,blockquote .small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;border-right:5px solid #eee;border-left:0;text-align:right}.blockquote-reverse footer:before,blockquote.pull-right footer:before,.blockquote-reverse small:before,blockquote.pull-right small:before,.blockquote-reverse .small:before,blockquote.pull-right .small:before{content:''}.blockquote-reverse footer:after,blockquote.pull-right footer:after,.blockquote-reverse small:after,blockquote.pull-right small:after,.blockquote-reverse .small:after,blockquote.pull-right .small:after{content:'\00A0 \2014'}blockquote:before,blockquote:after{content:""}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;white-space:nowrap;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.25)}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;word-break:break-all;word-wrap:break-word;color:#333;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}.row{margin-left:-15px;margin-right:-15px}.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12{position:relative;min-height:1px;padding-left:15px;padding-right:15px}.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:0}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:0}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:0}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:0}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:0}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:0}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:0}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:0}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{max-width:100%;background-color:transparent}th{text-align:left}.table{width:100%;margin-bottom:20px}.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>thead>tr>th,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-child(odd)>td,.table-striped>tbody>tr:nth-child(odd)>th{background-color:#f9f9f9}.table-hover>tbody>tr:hover>td,.table-hover>tbody>tr:hover>th{background-color:#f5f5f5}table col[class*="col-"]{position:static;float:none;display:table-column}table td[class*="col-"],table th[class*="col-"]{position:static;float:none;display:table-cell}.table>thead>tr>td.active,.table>tbody>tr>td.active,.table>tfoot>tr>td.active,.table>thead>tr>th.active,.table>tbody>tr>th.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>tbody>tr.active>td,.table>tfoot>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr.active>th,.table>tfoot>tr.active>th{background-color:#f5f5f5}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th{background-color:#e8e8e8}.table>thead>tr>td.success,.table>tbody>tr>td.success,.table>tfoot>tr>td.success,.table>thead>tr>th.success,.table>tbody>tr>th.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>tbody>tr.success>td,.table>tfoot>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr.success>th,.table>tfoot>tr.success>th{background-color:#dff0d8}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th{background-color:#d0e9c6}.table>thead>tr>td.info,.table>tbody>tr>td.info,.table>tfoot>tr>td.info,.table>thead>tr>th.info,.table>tbody>tr>th.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>tbody>tr.info>td,.table>tfoot>tr.info>td,.table>thead>tr.info>th,.table>tbody>tr.info>th,.table>tfoot>tr.info>th{background-color:#d9edf7}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th{background-color:#c4e3f3}.table>thead>tr>td.warning,.table>tbody>tr>td.warning,.table>tfoot>tr>td.warning,.table>thead>tr>th.warning,.table>tbody>tr>th.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>tbody>tr.warning>td,.table>tfoot>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr.warning>th,.table>tfoot>tr.warning>th{background-color:#fcf8e3}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th{background-color:#faf2cc}.table>thead>tr>td.danger,.table>tbody>tr>td.danger,.table>tfoot>tr>td.danger,.table>thead>tr>th.danger,.table>tbody>tr>th.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>tbody>tr.danger>td,.table>tfoot>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr.danger>th,.table>tfoot>tr.danger>th{background-color:#f2dede}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th{background-color:#ebcccc}@media (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;overflow-x:scroll;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd;-webkit-overflow-scrolling:touch}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{padding:0;margin:0;border:0;min-width:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;margin-bottom:5px;font-weight:bold}input[type="search"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type="file"]{display:block}input[type="range"]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border-color ease-in-out .15s, box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s, box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{cursor:not-allowed;background-color:#eee;opacity:1}textarea.form-control{height:auto}input[type="search"]{-webkit-appearance:none}input[type="date"]{line-height:34px}.form-group{margin-bottom:15px}.radio,.checkbox{display:block;min-height:20px;margin-top:10px;margin-bottom:10px;padding-left:20px}.radio label,.checkbox label{display:inline;font-weight:normal;cursor:pointer}.radio input[type="radio"],.radio-inline input[type="radio"],.checkbox input[type="checkbox"],.checkbox-inline input[type="checkbox"]{float:left;margin-left:-20px}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{display:inline-block;padding-left:20px;margin-bottom:0;vertical-align:middle;font-weight:normal;cursor:pointer}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}input[type="radio"][disabled],input[type="checkbox"][disabled],.radio[disabled],.radio-inline[disabled],.checkbox[disabled],.checkbox-inline[disabled],fieldset[disabled] input[type="radio"],fieldset[disabled] input[type="checkbox"],fieldset[disabled] .radio,fieldset[disabled] .radio-inline,fieldset[disabled] .checkbox,fieldset[disabled] .checkbox-inline{cursor:not-allowed}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}textarea.input-sm,select[multiple].input-sm{height:auto}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}select.input-lg{height:46px;line-height:46px}textarea.input-lg,select[multiple].input-lg{height:auto}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.has-feedback .form-control-feedback{position:absolute;top:25px;right:0;display:block;width:34px;height:34px;line-height:34px;text-align:center}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;border-color:#3c763d;background-color:#dff0d8}.has-success .form-control-feedback{color:#3c763d}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;border-color:#8a6d3b;background-color:#fcf8e3}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;border-color:#a94442;background-color:#f2dede}.has-error .form-control-feedback{color:#a94442}.form-control-static{margin-bottom:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.form-inline .checkbox{display:inline-block;margin-top:0;margin-bottom:0;padding-left:0;vertical-align:middle}.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{float:none;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .control-label,.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{margin-top:0;margin-bottom:0;padding-top:7px}.form-horizontal .radio,.form-horizontal .checkbox{min-height:27px}.form-horizontal .form-group{margin-left:-15px;margin-right:-15px}.form-horizontal .form-control-static{padding-top:7px}@media (min-width:768px){.form-horizontal .control-label{text-align:right}}.form-horizontal .has-feedback .form-control-feedback{top:0;right:15px}.btn{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;vertical-align:middle;cursor:pointer;background-image:none;border:1px solid transparent;white-space:nowrap;padding:6px 12px;font-size:14px;line-height:1.42857143;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn:focus,.btn:active:focus,.btn.active:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus{color:#333;text-decoration:none}.btn:active,.btn.active{outline:0;background-image:none;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;pointer-events:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default:hover,.btn-default:focus,.btn-default:active,.btn-default.active,.open .dropdown-toggle.btn-default{color:#333;background-color:#ebebeb;border-color:#adadad}.btn-default:active,.btn-default.active,.open .dropdown-toggle.btn-default{background-image:none}.btn-default.disabled,.btn-default[disabled],fieldset[disabled] .btn-default,.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled:active,.btn-default[disabled]:active,fieldset[disabled] .btn-default:active,.btn-default.disabled.active,.btn-default[disabled].active,fieldset[disabled] .btn-default.active{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#428bca;border-color:#357ebd}.btn-primary:hover,.btn-primary:focus,.btn-primary:active,.btn-primary.active,.open .dropdown-toggle.btn-primary{color:#fff;background-color:#3276b1;border-color:#285e8e}.btn-primary:active,.btn-primary.active,.open .dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled,.btn-primary[disabled],fieldset[disabled] .btn-primary,.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled:active,.btn-primary[disabled]:active,fieldset[disabled] .btn-primary:active,.btn-primary.disabled.active,.btn-primary[disabled].active,fieldset[disabled] .btn-primary.active{background-color:#428bca;border-color:#357ebd}.btn-primary .badge{color:#428bca;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success:hover,.btn-success:focus,.btn-success:active,.btn-success.active,.open .dropdown-toggle.btn-success{color:#fff;background-color:#47a447;border-color:#398439}.btn-success:active,.btn-success.active,.open .dropdown-toggle.btn-success{background-image:none}.btn-success.disabled,.btn-success[disabled],fieldset[disabled] .btn-success,.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled:active,.btn-success[disabled]:active,fieldset[disabled] .btn-success:active,.btn-success.disabled.active,.btn-success[disabled].active,fieldset[disabled] .btn-success.active{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info:hover,.btn-info:focus,.btn-info:active,.btn-info.active,.open .dropdown-toggle.btn-info{color:#fff;background-color:#39b3d7;border-color:#269abc}.btn-info:active,.btn-info.active,.open .dropdown-toggle.btn-info{background-image:none}.btn-info.disabled,.btn-info[disabled],fieldset[disabled] .btn-info,.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled:active,.btn-info[disabled]:active,fieldset[disabled] .btn-info:active,.btn-info.disabled.active,.btn-info[disabled].active,fieldset[disabled] .btn-info.active{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning:hover,.btn-warning:focus,.btn-warning:active,.btn-warning.active,.open .dropdown-toggle.btn-warning{color:#fff;background-color:#ed9c28;border-color:#d58512}.btn-warning:active,.btn-warning.active,.open .dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-warning,.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled:active,.btn-warning[disabled]:active,fieldset[disabled] .btn-warning:active,.btn-warning.disabled.active,.btn-warning[disabled].active,fieldset[disabled] .btn-warning.active{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger:hover,.btn-danger:focus,.btn-danger:active,.btn-danger.active,.open .dropdown-toggle.btn-danger{color:#fff;background-color:#d2322d;border-color:#ac2925}.btn-danger:active,.btn-danger.active,.open .dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled,.btn-danger[disabled],fieldset[disabled] .btn-danger,.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled:active,.btn-danger[disabled]:active,fieldset[disabled] .btn-danger:active,.btn-danger.disabled.active,.btn-danger[disabled].active,fieldset[disabled] .btn-danger.active{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{color:#428bca;font-weight:normal;cursor:pointer;border-radius:0}.btn-link,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#2a6496;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#999;text-decoration:none}.btn-lg,.btn-group-lg>.btn{padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}.btn-sm,.btn-group-sm>.btn{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-xs,.btn-group-xs>.btn{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%;padding-left:0;padding-right:0}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition:height .35s ease;transition:height .35s ease}@font-face{font-family:'Glyphicons Halflings';src:url('../fonts/glyphicons-halflings-regular.eot');src:url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'),url('../fonts/glyphicons-halflings-regular.woff') format('woff'),url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'),url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:normal;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\2a"}.glyphicon-plus:before{content:"\2b"}.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px solid;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;font-size:14px;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,0.175);box-shadow:0 6px 12px rgba(0,0,0,0.175);background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:normal;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{text-decoration:none;color:#262626;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#fff;text-decoration:none;outline:0;background-color:#428bca}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#999}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);cursor:not-allowed}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{left:auto;right:0}.dropdown-menu-left{left:0;right:auto}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#999}.dropdown-backdrop{position:fixed;left:0;right:0;bottom:0;top:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid;content:""}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px}@media (min-width:768px){.navbar-right .dropdown-menu{left:auto;right:0}.navbar-right .dropdown-menu-left{left:0;right:auto}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover,.btn-group>.btn:focus,.btn-group-vertical>.btn:focus,.btn-group>.btn:active,.btn-group-vertical>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn.active{z-index:2}.btn-group>.btn:focus,.btn-group-vertical>.btn:focus{outline:none}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-top-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child>.btn:last-child,.btn-group>.btn-group:first-child>.dropdown-toggle{border-bottom-right-radius:0;border-top-right-radius:0}.btn-group>.btn-group:last-child>.btn:first-child{border-bottom-left-radius:0;border-top-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-left:8px;padding-right:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-left:12px;padding-right:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-bottom-left-radius:4px;border-top-right-radius:0;border-top-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-right-radius:0;border-top-left-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{float:none;display:table-cell;width:1%}.btn-group-justified>.btn-group .btn{width:100%}[data-toggle="buttons"]>.btn>input[type="radio"],[data-toggle="buttons"]>.btn>input[type="checkbox"]{display:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*="col-"]{float:none;padding-left:0;padding-right:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn,select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn,select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn{height:auto}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:normal;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type="radio"],.input-group-addon input[type="checkbox"]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group-btn:last-child>.btn-group:not(:last-child)>.btn{border-bottom-right-radius:0;border-top-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:first-child>.btn-group:not(:first-child)>.btn{border-bottom-left-radius:0;border-top-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:hover,.input-group-btn>.btn:focus,.input-group-btn>.btn:active{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{margin-left:-1px}.nav{margin-bottom:0;padding-left:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#999}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#999;text-decoration:none;background-color:transparent;cursor:not-allowed}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#eee;border-color:#428bca}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#555;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent;cursor:default}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{text-align:center;margin-bottom:5px}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#fff;background-color:#428bca}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{text-align:center;margin-bottom:5px}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-right-radius:0;border-top-left-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{max-height:340px;overflow-x:visible;padding-right:15px;padding-left:15px;border-top:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,0.1);-webkit-overflow-scrolling:touch}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;box-shadow:none}.navbar-collapse.collapse{display:block !important;height:auto !important;padding-bottom:0;overflow:visible !important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{padding-left:0;padding-right:0}}.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;padding:15px 15px;font-size:18px;line-height:20px;height:50px}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;margin-right:15px;padding:9px 10px;margin-top:8px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:none}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;box-shadow:none}.navbar-nav .open .dropdown-menu>li>a,.navbar-nav .open .dropdown-menu .dropdown-header{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:hover,.navbar-nav .open .dropdown-menu>li>a:focus{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}.navbar-nav.navbar-right:last-child{margin-right:-15px}}@media (min-width:768px){.navbar-left{float:left !important}.navbar-right{float:right !important}}.navbar-form{margin-left:-15px;margin-right:-15px;padding:10px 15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);margin-top:8px;margin-bottom:8px}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .radio,.navbar-form .checkbox{display:inline-block;margin-top:0;margin-bottom:0;padding-left:0;vertical-align:middle}.navbar-form .radio input[type="radio"],.navbar-form .checkbox input[type="checkbox"]{float:none;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}}@media (min-width:768px){.navbar-form{width:auto;border:0;margin-left:0;margin-right:0;padding-top:0;padding-bottom:0;-webkit-box-shadow:none;box-shadow:none}.navbar-form.navbar-right:last-child{margin-right:-15px}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-right-radius:0;border-top-left-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-left:15px;margin-right:15px}.navbar-text.navbar-right:last-child{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:hover,.navbar-default .navbar-brand:focus{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:hover,.navbar-default .navbar-nav>.disabled>a:focus{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:hover,.navbar-default .navbar-toggle:focus{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{background-color:#e7e7e7;color:#555}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#999}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#999}.navbar-inverse .navbar-nav>li>a{color:#999}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{background-color:#080808;color:#fff}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#999}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#999}.navbar-inverse .navbar-link:hover{color:#fff}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;line-height:1.42857143;text-decoration:none;color:#428bca;background-color:#fff;border:1px solid #ddd;margin-left:-1px}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-bottom-left-radius:4px;border-top-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-bottom-right-radius:4px;border-top-right-radius:4px}.pagination>li>a:hover,.pagination>li>span:hover,.pagination>li>a:focus,.pagination>li>span:focus{color:#2a6496;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>span,.pagination>.active>a:hover,.pagination>.active>span:hover,.pagination>.active>a:focus,.pagination>.active>span:focus{z-index:2;color:#fff;background-color:#428bca;border-color:#428bca;cursor:default}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#999;background-color:#fff;border-color:#ddd;cursor:not-allowed}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-bottom-left-radius:6px;border-top-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-bottom-right-radius:6px;border-top-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-bottom-left-radius:3px;border-top-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-bottom-right-radius:3px;border-top-right-radius:3px}.pager{padding-left:0;margin:20px 0;list-style:none;text-align:center}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#999;background-color:#fff;cursor:not-allowed}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:bold;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}.label[href]:hover,.label[href]:focus{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#999}.label-default[href]:hover,.label-default[href]:focus{background-color:#808080}.label-primary{background-color:#428bca}.label-primary[href]:hover,.label-primary[href]:focus{background-color:#3071a9}.label-success{background-color:#5cb85c}.label-success[href]:hover,.label-success[href]:focus{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:hover,.label-info[href]:focus{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:bold;color:#fff;line-height:1;vertical-align:baseline;white-space:nowrap;text-align:center;background-color:#999;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-xs .badge{top:0;padding:1px 5px}a.badge:hover,a.badge:focus{color:#fff;text-decoration:none;cursor:pointer}a.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#428bca;background-color:#fff}.nav-pills>li>a>.badge{margin-left:3px}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:bold}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable{padding-right:35px}.alert-dismissable .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{background-color:#dff0d8;border-color:#d6e9c6;color:#3c763d}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{background-color:#d9edf7;border-color:#bce8f1;color:#31708f}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{background-color:#fcf8e3;border-color:#faebcc;color:#8a6d3b}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{background-color:#f2dede;border-color:#ebccd1;color:#a94442}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}.list-group{margin-bottom:20px;padding-left:0}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-right-radius:4px;border-top-left-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}a.list-group-item{color:#555}a.list-group-item .list-group-item-heading{color:#333}a.list-group-item:hover,a.list-group-item:focus{text-decoration:none;background-color:#f5f5f5}a.list-group-item.active,a.list-group-item.active:hover,a.list-group-item.active:focus{z-index:2;color:#fff;background-color:#428bca;border-color:#428bca}a.list-group-item.active .list-group-item-heading,a.list-group-item.active:hover .list-group-item-heading,a.list-group-item.active:focus .list-group-item-heading{color:inherit}a.list-group-item.active .list-group-item-text,a.list-group-item.active:hover .list-group-item-text,a.list-group-item.active:focus .list-group-item-text{color:#e1edf7}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:hover,a.list-group-item-success:focus{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:hover,a.list-group-item-success.active:focus{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:hover,a.list-group-item-info:focus{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:hover,a.list-group-item-info.active:focus{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:hover,a.list-group-item-warning:focus{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:hover,a.list-group-item-danger:focus{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.05);box-shadow:0 1px 1px rgba(0,0,0,0.05)}.box-body{padding:15px}.box-header{padding:10px 15px;border-bottom:1px solid transparent;border-top-right-radius:3px;border-top-left-radius:3px}.box-header>.dropdown .dropdown-toggle{color:inherit}.box-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.box-title>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group{margin-bottom:0}.panel>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-right-radius:3px;border-top-left-radius:3px}.panel>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.box-header+.list-group .list-group-item:first-child{border-top-width:0}.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.table:first-child,.panel>.table-responsive:first-child>.table:first-child{border-top-right-radius:3px;border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table:last-child,.panel>.table-responsive:last-child>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.box-body+.table,.panel>.box-body+.table-responsive{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child th,.panel>.table>tbody:first-child>tr:first-child td{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{border:0;margin-bottom:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px;overflow:hidden}.panel-group .panel+.panel{margin-top:5px}.panel-group .box-header{border-bottom:0}.panel-group .box-header+.panel-collapse .box-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .box-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.box-header{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.box-header+.panel-collapse .box-body{border-top-color:#ddd}.panel-default>.panel-footer+.panel-collapse .box-body{border-bottom-color:#ddd}.panel-primary{border-color:#428bca}.panel-primary>.box-header{color:#fff;background-color:#428bca;border-color:#428bca}.panel-primary>.box-header+.panel-collapse .box-body{border-top-color:#428bca}.panel-primary>.panel-footer+.panel-collapse .box-body{border-bottom-color:#428bca}.panel-success{border-color:#d6e9c6}.panel-success>.box-header{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.box-header+.panel-collapse .box-body{border-top-color:#d6e9c6}.panel-success>.panel-footer+.panel-collapse .box-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.box-header{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.box-header+.panel-collapse .box-body{border-top-color:#bce8f1}.panel-info>.panel-footer+.panel-collapse .box-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.box-header{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.box-header+.panel-collapse .box-body{border-top-color:#faebcc}.panel-warning>.panel-footer+.panel-collapse .box-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.box-header{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.box-header+.panel-collapse .box-body{border-top-color:#ebccd1}.panel-danger>.panel-footer+.panel-collapse .box-body{border-bottom-color:#ebccd1}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:bold;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.2;filter:alpha(opacity=20)}.close:hover,.close:focus{color:#000;text-decoration:none;cursor:pointer;opacity:.5;filter:alpha(opacity=50)}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.modal-open{overflow:hidden}.modal{display:none;overflow:auto;overflow-y:scroll;position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transform:translate(0, -25%);-ms-transform:translate(0, -25%);transform:translate(0, -25%);-webkit-transition:-webkit-transform 0.3s ease-out;-moz-transition:-moz-transform 0.3s ease-out;-o-transition:-o-transform 0.3s ease-out;transition:transform 0.3s ease-out}.modal.in .modal-dialog{-webkit-transform:translate(0, 0);-ms-transform:translate(0, 0);transform:translate(0, 0)}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;border:1px solid #999;border:1px solid rgba(0,0,0,0.2);border-radius:6px;-webkit-box-shadow:0 3px 9px rgba(0,0,0,0.5);box-shadow:0 3px 9px rgba(0,0,0,0.5);background-clip:padding-box;outline:none}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0;filter:alpha(opacity=0)}.modal-backdrop.in{opacity:.5;filter:alpha(opacity=50)}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5;min-height:16.42857143px}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:20px}.modal-footer{margin-top:15px;padding:19px 20px 20px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-left:5px;margin-bottom:0}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,0.5);box-shadow:0 5px 15px rgba(0,0,0,0.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.clearfix:before,.clearfix:after,.container:before,.container:after,.container-fluid:before,.container-fluid:after,.row:before,.row:after,.form-horizontal .form-group:before,.form-horizontal .form-group:after,.btn-toolbar:before,.btn-toolbar:after,.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after,.nav:before,.nav:after,.navbar:before,.navbar:after,.navbar-header:before,.navbar-header:after,.navbar-collapse:before,.navbar-collapse:after,.pager:before,.pager:after,.box-body:before,.box-body:after,.modal-footer:before,.modal-footer:after{content:" ";display:table}.clearfix:after,.container:after,.container-fluid:after,.row:after,.form-horizontal .form-group:after,.btn-toolbar:after,.btn-group-vertical>.btn-group:after,.nav:after,.navbar:after,.navbar-header:after,.navbar-collapse:after,.pager:after,.box-body:after,.modal-footer:after{clear:both}.center-block{display:block;margin-left:auto;margin-right:auto}.pull-right{float:right !important}.pull-left{float:left !important}.hide{display:none !important}.show{display:block !important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none !important;visibility:hidden !important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-xs,.visible-sm,.visible-md,.visible-lg{display:none !important}@media (max-width:767px){.visible-xs{display:block !important}table.visible-xs{display:table}tr.visible-xs{display:table-row !important}th.visible-xs,td.visible-xs{display:table-cell !important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block !important}table.visible-sm{display:table}tr.visible-sm{display:table-row !important}th.visible-sm,td.visible-sm{display:table-cell !important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block !important}table.visible-md{display:table}tr.visible-md{display:table-row !important}th.visible-md,td.visible-md{display:table-cell !important}}@media (min-width:1200px){.visible-lg{display:block !important}table.visible-lg{display:table}tr.visible-lg{display:table-row !important}th.visible-lg,td.visible-lg{display:table-cell !important}}@media (max-width:767px){.hidden-xs{display:none !important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none !important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none !important}}@media (min-width:1200px){.hidden-lg{display:none !important}}.visible-print{display:none !important}@media print{.visible-print{display:block !important}table.visible-print{display:table}tr.visible-print{display:table-row !important}th.visible-print,td.visible-print{display:table-cell !important}}@media print{.hidden-print{display:none !important}} diff --git a/public/assets/js/bootstrap.min.js b/public/assets/js/bootstrap.min.js index 856dc42f..9e2add70 100755 --- a/public/assets/js/bootstrap.min.js +++ b/public/assets/js/bootstrap.min.js @@ -1,7 +1,11 @@ /*! - * Bootstrap v3.1.1 (http://getbootstrap.com) + * Bootstrap v3.2.0 (http://getbootstrap.com) * Copyright 2011-2014 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) */ -+function(a){"use strict";var b='[data-dismiss="alert"]',c=function(c){a(c).on("click",b,this.close)};c.prototype.close=function(b){function f(){e.trigger("closed.bs.alert").remove()}var c=a(this),d=c.attr("data-target");d||(d=c.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,""));var e=a(d);b&&b.preventDefault(),e.length||(e=c.hasClass("alert")?c:c.parent()),e.trigger(b=a.Event("close.bs.alert"));if(b.isDefaultPrevented())return;e.removeClass("in"),a.support.transition&&e.hasClass("fade")?e.one(a.support.transition.end,f).emulateTransitionEnd(150):f()};var d=a.fn.alert;a.fn.alert=function(b){return this.each(function(){var d=a(this),e=d.data("bs.alert");e||d.data("bs.alert",e=new c(this)),typeof b=="string"&&e[b].call(d)})},a.fn.alert.Constructor=c,a.fn.alert.noConflict=function(){return a.fn.alert=d,this},a(document).on("click.bs.alert.data-api",b,c.prototype.close)}(jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d),this.isLoading=!1};b.DEFAULTS={loadingText:"loading..."},b.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",f.resetText||d.data("resetText",d[e]()),d[e](f[b]||this.options[b]),setTimeout(a.proxy(function(){b=="loadingText"?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},b.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");c.prop("type")=="radio"&&(c.prop("checked")&&this.$element.hasClass("active")?a=!1:b.find(".active").removeClass("active")),a&&c.prop("checked",!this.$element.hasClass("active")).trigger("change")}a&&this.$element.toggleClass("active")};var c=a.fn.button;a.fn.button=function(c){return this.each(function(){var d=a(this),e=d.data("bs.button"),f=typeof c=="object"&&c;e||d.data("bs.button",e=new b(this,f)),c=="toggle"?e.toggle():c&&e.setState(c)})},a.fn.button.Constructor=b,a.fn.button.noConflict=function(){return a.fn.button=c,this},a(document).on("click.bs.button.data-api","[data-toggle^=button]",function(b){var c=a(b.target);c.hasClass("btn")||(c=c.closest(".btn")),c.button("toggle"),b.preventDefault()})}(jQuery),+function(a){"use strict";var b=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=this.sliding=this.interval=this.$active=this.$items=null,this.options.pause=="hover"&&this.$element.on("mouseenter",a.proxy(this.pause,this)).on("mouseleave",a.proxy(this.cycle,this))};b.DEFAULTS={interval:5e3,pause:"hover",wrap:!0},b.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},b.prototype.getActiveIndex=function(){return this.$active=this.$element.find(".item.active"),this.$items=this.$active.parent().children(),this.$items.index(this.$active)},b.prototype.to=function(b){var c=this,d=this.getActiveIndex();if(b>this.$items.length-1||b<0)return;return this.sliding?this.$element.one("slid.bs.carousel",function(){c.to(b)}):d==b?this.pause().cycle():this.slide(b>d?"next":"prev",a(this.$items[b]))},b.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},b.prototype.next=function(){if(this.sliding)return;return this.slide("next")},b.prototype.prev=function(){if(this.sliding)return;return this.slide("prev")},b.prototype.slide=function(b,c){var d=this.$element.find(".item.active"),e=c||d[b](),f=this.interval,g=b=="next"?"left":"right",h=b=="next"?"first":"last",i=this;if(!e.length){if(!this.options.wrap)return;e=this.$element.find(".item")[h]()}if(e.hasClass("active"))return this.sliding=!1;var j=a.Event("slide.bs.carousel",{relatedTarget:e[0],direction:g});this.$element.trigger(j);if(j.isDefaultPrevented())return;return this.sliding=!0,f&&this.pause(),this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid.bs.carousel",function(){var b=a(i.$indicators.children()[i.getActiveIndex()]);b&&b.addClass("active")})),a.support.transition&&this.$element.hasClass("slide")?(e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),d.one(a.support.transition.end,function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger("slid.bs.carousel")},0)}).emulateTransitionEnd(d.css("transition-duration").slice(0,-1)*1e3)):(d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger("slid.bs.carousel")),f&&this.cycle(),this};var c=a.fn.carousel;a.fn.carousel=function(c){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},b.DEFAULTS,d.data(),typeof c=="object"&&c),g=typeof c=="string"?c:f.slide;e||d.data("bs.carousel",e=new b(this,f)),typeof c=="number"?e.to(c):g?e[g]():f.interval&&e.pause().cycle()})},a.fn.carousel.Constructor=b,a.fn.carousel.noConflict=function(){return a.fn.carousel=c,this},a(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",function(b){var c=a(this),d,e=a(c.attr("data-target")||(d=c.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,"")),f=a.extend({},e.data(),c.data()),g=c.attr("data-slide-to");g&&(f.interval=!1),e.carousel(f),(g=c.attr("data-slide-to"))&&e.data("bs.carousel").to(g),b.preventDefault()}),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var b=a(this);b.carousel(b.data())})})}(jQuery),+function(a){function e(d){a(b).remove(),a(c).each(function(){var b=f(a(this)),c={relatedTarget:this};if(!b.hasClass("open"))return;b.trigger(d=a.Event("hide.bs.dropdown",c));if(d.isDefaultPrevented())return;b.removeClass("open").trigger("hidden.bs.dropdown",c)})}function f(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}"use strict";var b=".dropdown-backdrop",c="[data-toggle=dropdown]",d=function(b){a(b).on("click.bs.dropdown",this.toggle)};d.prototype.toggle=function(b){var c=a(this);if(c.is(".disabled, :disabled"))return;var d=f(c),g=d.hasClass("open");e();if(!g){"ontouchstart"in document.documentElement&&!d.closest(".navbar-nav").length&&a(''}),b.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),b.prototype.constructor=b,b.prototype.getDefaults=function(){return b.DEFAULTS},b.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content")[this.options.html?typeof c=="string"?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},b.prototype.hasContent=function(){return this.getTitle()||this.getContent()},b.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||(typeof b.content=="function"?b.content.call(a[0]):b.content)},b.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")},b.prototype.tip=function(){return this.$tip||(this.$tip=a(this.options.template)),this.$tip};var c=a.fn.popover;a.fn.popover=function(c){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f=typeof c=="object"&&c;if(!e&&c=="destroy")return;e||d.data("bs.popover",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.popover.Constructor=b,a.fn.popover.noConflict=function(){return a.fn.popover=c,this}}(jQuery),+function(a){"use strict";var b=function(b){this.element=a(b)};b.prototype.show=function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.data("target");d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,""));if(b.parent("li").hasClass("active"))return;var e=c.find(".active:last a")[0],f=a.Event("show.bs.tab",{relatedTarget:e});b.trigger(f);if(f.isDefaultPrevented())return;var g=a(d);this.activate(b.parent("li"),c),this.activate(g,g.parent(),function(){b.trigger({type:"shown.bs.tab",relatedTarget:e})})},b.prototype.activate=function(b,c,d){function g(){e.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),b.addClass("active"),f?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active"),d&&d()}var e=c.find("> .active"),f=d&&a.support.transition&&e.hasClass("fade");f?e.one(a.support.transition.end,g).emulateTransitionEnd(150):g(),e.removeClass("in")};var c=a.fn.tab;a.fn.tab=function(c){return this.each(function(){var d=a(this),e=d.data("bs.tab");e||d.data("bs.tab",e=new b(this)),typeof c=="string"&&e[c]()})},a.fn.tab.Constructor=b,a.fn.tab.noConflict=function(){return a.fn.tab=c,this},a(document).on("click.bs.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(b){b.preventDefault(),a(this).tab("show")})}(jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d),this.transitioning=null,this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};b.DEFAULTS={toggle:!0},b.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},b.prototype.show=function(){if(this.transitioning||this.$element.hasClass("in"))return;var b=a.Event("show.bs.collapse");this.$element.trigger(b);if(b.isDefaultPrevented())return;var c=this.$parent&&this.$parent.find("> .panel > .in");if(c&&c.length){var d=c.data("bs.collapse");if(d&&d.transitioning)return;c.collapse("hide"),d||c.data("bs.collapse",null)}var e=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[e](0),this.transitioning=1;var f=function(){this.$element.removeClass("collapsing").addClass("collapse in")[e]("auto"),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return f.call(this);var g=a.camelCase(["scroll",e].join("-"));this.$element.one(a.support.transition.end,a.proxy(f,this)).emulateTransitionEnd(350)[e](this.$element[0][g])},b.prototype.hide=function(){if(this.transitioning||!this.$element.hasClass("in"))return;var b=a.Event("hide.bs.collapse");this.$element.trigger(b);if(b.isDefaultPrevented())return;var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse").removeClass("in"),this.transitioning=1;var d=function(){this.transitioning=0,this.$element.trigger("hidden.bs.collapse").removeClass("collapsing").addClass("collapse")};if(!a.support.transition)return d.call(this);this.$element[c](0).one(a.support.transition.end,a.proxy(d,this)).emulateTransitionEnd(350)},b.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()};var c=a.fn.collapse;a.fn.collapse=function(c){return this.each(function(){var d=a(this),e=d.data("bs.collapse"),f=a.extend({},b.DEFAULTS,d.data(),typeof c=="object"&&c);!e&&f.toggle&&c=="show"&&(c=!c),e||d.data("bs.collapse",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.collapse.Constructor=b,a.fn.collapse.noConflict=function(){return a.fn.collapse=c,this},a(document).on("click.bs.collapse.data-api","[data-toggle=collapse]",function(b){var c=a(this),d,e=c.attr("data-target")||b.preventDefault()||(d=c.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""),f=a(e),g=f.data("bs.collapse"),h=g?"toggle":c.data(),i=c.attr("data-parent"),j=i&&a(i);if(!g||!g.transitioning)j&&j.find('[data-toggle=collapse][data-parent="'+i+'"]').not(c).addClass("collapsed"),c[f.hasClass("in")?"addClass":"removeClass"]("collapsed");f.collapse(h)})}(jQuery),+function(a){function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(a.style[c]!==undefined)return{end:b[c]};return!1}"use strict",a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one(a.support.transition.end,function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b()})}(jQuery) \ No newline at end of file +/*! + * Generated using the Bootstrap Customizer (http://getbootstrap.com/customize/?id=09c8c466aa06e64a7262) + * Config saved to config.json and https://gist.github.com/09c8c466aa06e64a7262 + */ +if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(t){"use strict";function e(e){return this.each(function(){var i=t(this),s=i.data("bs.alert");s||i.data("bs.alert",s=new o(this)),"string"==typeof e&&s[e].call(i)})}var i='[data-dismiss="alert"]',o=function(e){t(e).on("click",i,this.close)};o.VERSION="3.2.0",o.prototype.close=function(e){function i(){n.detach().trigger("closed.bs.alert").remove()}var o=t(this),s=o.attr("data-target");s||(s=o.attr("href"),s=s&&s.replace(/.*(?=#[^\s]*$)/,""));var n=t(s);e&&e.preventDefault(),n.length||(n=o.hasClass("alert")?o:o.parent()),n.trigger(e=t.Event("close.bs.alert")),e.isDefaultPrevented()||(n.removeClass("in"),t.support.transition&&n.hasClass("fade")?n.one("bsTransitionEnd",i).emulateTransitionEnd(150):i())};var s=t.fn.alert;t.fn.alert=e,t.fn.alert.Constructor=o,t.fn.alert.noConflict=function(){return t.fn.alert=s,this},t(document).on("click.bs.alert.data-api",i,o.prototype.close)}(jQuery),+function(t){"use strict";function e(e){return this.each(function(){var o=t(this),s=o.data("bs.button"),n="object"==typeof e&&e;s||o.data("bs.button",s=new i(this,n)),"toggle"==e?s.toggle():e&&s.setState(e)})}var i=function(e,o){this.$element=t(e),this.options=t.extend({},i.DEFAULTS,o),this.isLoading=!1};i.VERSION="3.2.0",i.DEFAULTS={loadingText:"loading..."},i.prototype.setState=function(e){var i="disabled",o=this.$element,s=o.is("input")?"val":"html",n=o.data();e+="Text",null==n.resetText&&o.data("resetText",o[s]()),o[s](null==n[e]?this.options[e]:n[e]),setTimeout(t.proxy(function(){"loadingText"==e?(this.isLoading=!0,o.addClass(i).attr(i,i)):this.isLoading&&(this.isLoading=!1,o.removeClass(i).removeAttr(i))},this),0)},i.prototype.toggle=function(){var t=!0,e=this.$element.closest('[data-toggle="buttons"]');if(e.length){var i=this.$element.find("input");"radio"==i.prop("type")&&(i.prop("checked")&&this.$element.hasClass("active")?t=!1:e.find(".active").removeClass("active")),t&&i.prop("checked",!this.$element.hasClass("active")).trigger("change")}t&&this.$element.toggleClass("active")};var o=t.fn.button;t.fn.button=e,t.fn.button.Constructor=i,t.fn.button.noConflict=function(){return t.fn.button=o,this},t(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(i){var o=t(i.target);o.hasClass("btn")||(o=o.closest(".btn")),e.call(o,"toggle"),i.preventDefault()})}(jQuery),+function(t){"use strict";function e(e){return this.each(function(){var o=t(this),s=o.data("bs.carousel"),n=t.extend({},i.DEFAULTS,o.data(),"object"==typeof e&&e),r="string"==typeof e?e:n.slide;s||o.data("bs.carousel",s=new i(this,n)),"number"==typeof e?s.to(e):r?s[r]():n.interval&&s.pause().cycle()})}var i=function(e,i){this.$element=t(e).on("keydown.bs.carousel",t.proxy(this.keydown,this)),this.$indicators=this.$element.find(".carousel-indicators"),this.options=i,this.paused=this.sliding=this.interval=this.$active=this.$items=null,"hover"==this.options.pause&&this.$element.on("mouseenter.bs.carousel",t.proxy(this.pause,this)).on("mouseleave.bs.carousel",t.proxy(this.cycle,this))};i.VERSION="3.2.0",i.DEFAULTS={interval:5e3,pause:"hover",wrap:!0},i.prototype.keydown=function(t){switch(t.which){case 37:this.prev();break;case 39:this.next();break;default:return}t.preventDefault()},i.prototype.cycle=function(e){return e||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(t.proxy(this.next,this),this.options.interval)),this},i.prototype.getItemIndex=function(t){return this.$items=t.parent().children(".item"),this.$items.index(t||this.$active)},i.prototype.to=function(e){var i=this,o=this.getItemIndex(this.$active=this.$element.find(".item.active"));return e>this.$items.length-1||0>e?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){i.to(e)}):o==e?this.pause().cycle():this.slide(e>o?"next":"prev",t(this.$items[e]))},i.prototype.pause=function(e){return e||(this.paused=!0),this.$element.find(".next, .prev").length&&t.support.transition&&(this.$element.trigger(t.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},i.prototype.next=function(){return this.sliding?void 0:this.slide("next")},i.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},i.prototype.slide=function(e,i){var o=this.$element.find(".item.active"),s=i||o[e](),n=this.interval,r="next"==e?"left":"right",a="next"==e?"first":"last",l=this;if(!s.length){if(!this.options.wrap)return;s=this.$element.find(".item")[a]()}if(s.hasClass("active"))return this.sliding=!1;var h=s[0],p=t.Event("slide.bs.carousel",{relatedTarget:h,direction:r});if(this.$element.trigger(p),!p.isDefaultPrevented()){if(this.sliding=!0,n&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var c=t(this.$indicators.children()[this.getItemIndex(s)]);c&&c.addClass("active")}var d=t.Event("slid.bs.carousel",{relatedTarget:h,direction:r});return t.support.transition&&this.$element.hasClass("slide")?(s.addClass(e),s[0].offsetWidth,o.addClass(r),s.addClass(r),o.one("bsTransitionEnd",function(){s.removeClass([e,r].join(" ")).addClass("active"),o.removeClass(["active",r].join(" ")),l.sliding=!1,setTimeout(function(){l.$element.trigger(d)},0)}).emulateTransitionEnd(1e3*o.css("transition-duration").slice(0,-1))):(o.removeClass("active"),s.addClass("active"),this.sliding=!1,this.$element.trigger(d)),n&&this.cycle(),this}};var o=t.fn.carousel;t.fn.carousel=e,t.fn.carousel.Constructor=i,t.fn.carousel.noConflict=function(){return t.fn.carousel=o,this},t(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",function(i){var o,s=t(this),n=t(s.attr("data-target")||(o=s.attr("href"))&&o.replace(/.*(?=#[^\s]+$)/,""));if(n.hasClass("carousel")){var r=t.extend({},n.data(),s.data()),a=s.attr("data-slide-to");a&&(r.interval=!1),e.call(n,r),a&&n.data("bs.carousel").to(a),i.preventDefault()}}),t(window).on("load",function(){t('[data-ride="carousel"]').each(function(){var i=t(this);e.call(i,i.data())})})}(jQuery),+function(t){"use strict";function e(e){e&&3===e.which||(t(s).remove(),t(n).each(function(){var o=i(t(this)),s={relatedTarget:this};o.hasClass("open")&&(o.trigger(e=t.Event("hide.bs.dropdown",s)),e.isDefaultPrevented()||o.removeClass("open").trigger("hidden.bs.dropdown",s))}))}function i(e){var i=e.attr("data-target");i||(i=e.attr("href"),i=i&&/#[A-Za-z]/.test(i)&&i.replace(/.*(?=#[^\s]*$)/,""));var o=i&&t(i);return o&&o.length?o:e.parent()}function o(e){return this.each(function(){var i=t(this),o=i.data("bs.dropdown");o||i.data("bs.dropdown",o=new r(this)),"string"==typeof e&&o[e].call(i)})}var s=".dropdown-backdrop",n='[data-toggle="dropdown"]',r=function(e){t(e).on("click.bs.dropdown",this.toggle)};r.VERSION="3.2.0",r.prototype.toggle=function(o){var s=t(this);if(!s.is(".disabled, :disabled")){var n=i(s),r=n.hasClass("open");if(e(),!r){"ontouchstart"in document.documentElement&&!n.closest(".navbar-nav").length&&t('