From f5e156008015aca71ff621048dfc1adc11042b94 Mon Sep 17 00:00:00 2001 From: Dmitry Khomutov Date: Wed, 31 May 2017 21:24:14 +0700 Subject: [PATCH 01/19] test PhpCsFixer --- .php_cs.dist | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .php_cs.dist diff --git a/.php_cs.dist b/.php_cs.dist new file mode 100644 index 0000000..7ceacfb --- /dev/null +++ b/.php_cs.dist @@ -0,0 +1,8 @@ +setFinder( + PhpCsFixer\Finder::create() + ->in(__DIR__ . '/tests') + ) +; From 0aed9a83e8d843f69bae08652de5afb7a73fdce9 Mon Sep 17 00:00:00 2001 From: Dmitry Khomutov Date: Tue, 20 Jun 2017 18:37:10 +0700 Subject: [PATCH 02/19] test new PHP Censor test installation --- .php_cs.dist | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 .php_cs.dist diff --git a/.php_cs.dist b/.php_cs.dist deleted file mode 100644 index 7ceacfb..0000000 --- a/.php_cs.dist +++ /dev/null @@ -1,8 +0,0 @@ -setFinder( - PhpCsFixer\Finder::create() - ->in(__DIR__ . '/tests') - ) -; From 49a1758d7bea404b4588eb6e20e648aadee6e354 Mon Sep 17 00:00:00 2001 From: Dmitry Khomutov Date: Wed, 21 Jun 2017 22:32:39 +0700 Subject: [PATCH 03/19] test --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index fa604ed..fe21bf1 100644 --- a/README.md +++ b/README.md @@ -2,3 +2,4 @@ PHP Censor test repository ========================== PHP Censor test repository. + From ca9aad458915d90efc76b252437fa7a097f6e256 Mon Sep 17 00:00:00 2001 From: Dmitry Khomutov Date: Sun, 2 Jul 2017 16:55:46 +0700 Subject: [PATCH 04/19] test --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index fe21bf1..fa604ed 100644 --- a/README.md +++ b/README.md @@ -2,4 +2,3 @@ PHP Censor test repository ========================== PHP Censor test repository. - From b8ca42b94db7fd3eb57e8e96a71b24faa76b0a5c Mon Sep 17 00:00:00 2001 From: Dmitry Khomutov Date: Sat, 16 Sep 2017 12:11:30 +0700 Subject: [PATCH 05/19] Test master. --- bin/failed | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/bin/failed b/bin/failed index 7bb0843..d683525 100755 --- a/bin/failed +++ b/bin/failed @@ -7,3 +7,9 @@ require_once APPLICATION_DIR . '/vendor/autoload.php'; $app = new \PHPCensorTest\Application(); $app->failed(); + + + + + + From 69cea233c759c8e588d9355c41eddba42f59c01f Mon Sep 17 00:00:00 2001 From: Dmitry Khomutov Date: Sat, 16 Sep 2017 12:25:54 +0700 Subject: [PATCH 06/19] Test master (2). --- bin/failed | 7 ------- 1 file changed, 7 deletions(-) diff --git a/bin/failed b/bin/failed index d683525..6b2c9df 100755 --- a/bin/failed +++ b/bin/failed @@ -6,10 +6,3 @@ require_once APPLICATION_DIR . '/vendor/autoload.php'; $app = new \PHPCensorTest\Application(); $app->failed(); - - - - - - - From 12dbb8c5fb680efaa530b909e28c542a5cd6f6ce Mon Sep 17 00:00:00 2001 From: Dmitry Khomutov Date: Sun, 22 Oct 2017 15:34:41 +0700 Subject: [PATCH 07/19] test --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index fa604ed..fe21bf1 100644 --- a/README.md +++ b/README.md @@ -2,3 +2,4 @@ PHP Censor test repository ========================== PHP Censor test repository. + From 23e7c8d28d48ca2a235e15e4cee4afc3d9427f48 Mon Sep 17 00:00:00 2001 From: Dmitry Khomutov Date: Thu, 7 Dec 2017 20:00:42 +0700 Subject: [PATCH 08/19] TODO test --- .php-censor.yml | 2 ++ src/Application.php | 2 ++ 2 files changed, 4 insertions(+) diff --git a/.php-censor.yml b/.php-censor.yml index 1ff58e6..468420e 100644 --- a/.php-censor.yml +++ b/.php-censor.yml @@ -36,6 +36,8 @@ test: security_checker: allow_failures: true + technical_debt: + complete: email: default_mailto_address: poisoncorpsee@gmail.com diff --git a/src/Application.php b/src/Application.php index 8e904dc..713aed8 100644 --- a/src/Application.php +++ b/src/Application.php @@ -6,12 +6,14 @@ class Application { public function success() { + //TODO: 1 echo 'success command'; exit(0); } public function failed() { + //TODO: 2 echo 'failed command'; exit(1); } From 33c009bc14686b5b817f953ca87aa6fb865e98dd Mon Sep 17 00:00:00 2001 From: Dmitry Khomutov Date: Fri, 8 Dec 2017 20:33:07 +0700 Subject: [PATCH 09/19] FIXME test --- src/Application.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Application.php b/src/Application.php index 713aed8..1c3b6c3 100644 --- a/src/Application.php +++ b/src/Application.php @@ -6,15 +6,16 @@ class Application { public function success() { - //TODO: 1 + // TODO: 1 echo 'success command'; exit(0); } public function failed() { - //TODO: 2 + // TODO: 2 echo 'failed command'; + // FIXME: 3 exit(1); } } From 4f1edb37a8a76febee0d8aa301cad7637956d226 Mon Sep 17 00:00:00 2001 From: Dmitry Khomutov Date: Fri, 8 Dec 2017 22:49:41 +0700 Subject: [PATCH 10/19] FIXME test --- .php-censor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.php-censor.yml b/.php-censor.yml index 468420e..a760f43 100644 --- a/.php-censor.yml +++ b/.php-censor.yml @@ -37,6 +37,7 @@ test: allow_failures: true technical_debt: + allow_failures: true complete: email: From 8ffa3829a3c308911c428e9c5d6b7837710c8cf9 Mon Sep 17 00:00:00 2001 From: Dmitry Khomutov Date: Fri, 8 Dec 2017 23:29:19 +0700 Subject: [PATCH 11/19] FIXME test --- src/PHPCensor/Application.php | 172 +++ src/PHPCensor/BuildFactory.php | 83 ++ src/PHPCensor/Builder.php | 485 ++++++++ src/PHPCensor/BuilderException.php | 9 + src/PHPCensor/Command/CreateAdminCommand.php | 96 ++ src/PHPCensor/Command/CreateBuildCommand.php | 83 ++ src/PHPCensor/Command/InstallCommand.php | 557 +++++++++ src/PHPCensor/Command/RebuildCommand.php | 85 ++ src/PHPCensor/Command/RebuildQueueCommand.php | 73 ++ src/PHPCensor/Command/RunCommand.php | 173 +++ .../Command/ScheduleBuildCommand.php | 101 ++ src/PHPCensor/Command/WorkerCommand.php | 77 ++ src/PHPCensor/Console/Application.php | 140 +++ src/PHPCensor/Controller.php | 124 ++ src/PHPCensor/Controller/BuildController.php | 371 ++++++ .../Controller/BuildStatusController.php | 236 ++++ src/PHPCensor/Controller/GroupController.php | 126 ++ src/PHPCensor/Controller/HomeController.php | 42 + .../Controller/ProjectController.php | 591 ++++++++++ .../Controller/SessionController.php | 281 +++++ src/PHPCensor/Controller/UserController.php | 304 +++++ .../Controller/WebhookController.php | 760 ++++++++++++ .../WidgetAllProjectsController.php | 152 +++ .../WidgetBuildErrorsController.php | 90 ++ .../Controller/WidgetLastBuildsController.php | 70 ++ src/PHPCensor/Helper/AnsiConverter.php | 46 + src/PHPCensor/Helper/Bitbucket.php | 108 ++ src/PHPCensor/Helper/Build.php | 21 + src/PHPCensor/Helper/BuildInterpolator.php | 85 ++ src/PHPCensor/Helper/CommandExecutor.php | 343 ++++++ .../Helper/CommandExecutorInterface.php | 43 + src/PHPCensor/Helper/Diff.php | 50 + src/PHPCensor/Helper/Email.php | 144 +++ src/PHPCensor/Helper/Github.php | 170 +++ src/PHPCensor/Helper/Lang.php | 190 +++ src/PHPCensor/Helper/LoginIsDisabled.php | 31 + src/PHPCensor/Helper/MailerFactory.php | 85 ++ src/PHPCensor/Helper/SshKey.php | 42 + src/PHPCensor/Helper/User.php | 32 + src/PHPCensor/Languages/lang.da.php | 289 +++++ src/PHPCensor/Languages/lang.de.php | 325 +++++ src/PHPCensor/Languages/lang.el.php | 291 +++++ src/PHPCensor/Languages/lang.en.php | 426 +++++++ src/PHPCensor/Languages/lang.es.php | 285 +++++ src/PHPCensor/Languages/lang.fr.php | 416 +++++++ src/PHPCensor/Languages/lang.it.php | 291 +++++ src/PHPCensor/Languages/lang.nl.php | 291 +++++ src/PHPCensor/Languages/lang.pl.php | 292 +++++ src/PHPCensor/Languages/lang.pt-br.php | 328 ++++++ src/PHPCensor/Languages/lang.ru.php | 408 +++++++ src/PHPCensor/Languages/lang.uk.php | 291 +++++ src/PHPCensor/Languages/lang.zh.php | 319 +++++ src/PHPCensor/Logging/BuildDBLogHandler.php | 81 ++ src/PHPCensor/Logging/BuildLogger.php | 114 ++ src/PHPCensor/Logging/Handler.php | 145 +++ .../Logging/LoggedBuildContextTidier.php | 38 + src/PHPCensor/Logging/OutputLogHandler.php | 41 + .../20140513143726_initial_migration.php | 210 ++++ ...0513153133_change_build_keys_migration.php | 32 + .../20140611170618_choose_branch.php | 24 + .../20140730143702_fix_database_columns.php | 55 + .../20150131075425_archive_project.php | 24 + .../20150203105015_fix_column_types.php | 28 + .../20150308074509_add_user_providers.php | 34 + ...4958_unique_email_and_name_user_fields.php | 32 + .../20151008140800_add_project_groups.php | 49 + ...0151009100610_remove_unique_name_index.php | 19 + .../20151014091859_errors_table.php | 68 ++ .../20151015124825_convert_errors.php | 182 +++ .../20160425162114_branch_column_length.php | 24 + .../20160623100223_project_table_defaults.php | 13 + ...2_added_language_and_per_page_for_user.php | 40 + ...75400_fixed_build_error_message_column.php | 18 + ...13127_fixed_build_log_column_for_mysql.php | 26 + ...2922_fixed_build_log_column_for_mysql2.php | 26 + .../20170321131931_add_environment.php | 64 + ...256_added_source_column_to_build_table.php | 33 + .../20170416130610_fixed_environments.php | 28 + ...142131_added_tag_column_to_build_table.php | 28 + ...70711112805_fixed_build_meta_for_mysql.php | 26 + ...20170828142020_added_remember_me_login.php | 28 + ...170913141438_added_default_branch_only.php | 28 + ...348_removed_project_id_from_build_meta.php | 39 + ...0171015123827_added_additional_columns.php | 64 + ...171016143000_added_additional_columns2.php | 40 + ...171019143346_added_additional_columns3.php | 56 + src/PHPCensor/Model.php | 7 + src/PHPCensor/Model/Build.php | 1045 +++++++++++++++++ src/PHPCensor/Model/Build/BitbucketBuild.php | 288 +++++ .../Model/Build/BitbucketHgBuild.php | 65 + src/PHPCensor/Model/Build/GithubBuild.php | 274 +++++ src/PHPCensor/Model/Build/GitlabBuild.php | 67 ++ src/PHPCensor/Model/Build/GogsBuild.php | 36 + src/PHPCensor/Model/Build/LocalBuild.php | 91 ++ src/PHPCensor/Model/Build/MercurialBuild.php | 99 ++ src/PHPCensor/Model/Build/RemoteGitBuild.php | 162 +++ src/PHPCensor/Model/Build/SubversionBuild.php | 127 ++ src/PHPCensor/Model/BuildError.php | 449 +++++++ src/PHPCensor/Model/BuildMeta.php | 225 ++++ src/PHPCensor/Model/Environment.php | 161 +++ src/PHPCensor/Model/Project.php | 869 ++++++++++++++ src/PHPCensor/Model/ProjectGroup.php | 175 +++ src/PHPCensor/Model/User.php | 350 ++++++ src/PHPCensor/Plugin.php | 98 ++ src/PHPCensor/Plugin/Atoum.php | 109 ++ src/PHPCensor/Plugin/Behat.php | 128 ++ src/PHPCensor/Plugin/Campfire.php | 151 +++ src/PHPCensor/Plugin/CleanBuild.php | 59 + src/PHPCensor/Plugin/Codeception.php | 164 +++ src/PHPCensor/Plugin/Composer.php | 124 ++ src/PHPCensor/Plugin/CopyBuild.php | 91 ++ src/PHPCensor/Plugin/Deployer.php | 81 ++ src/PHPCensor/Plugin/Email.php | 214 ++++ src/PHPCensor/Plugin/Env.php | 46 + src/PHPCensor/Plugin/FlowdockNotify.php | 73 ++ src/PHPCensor/Plugin/Git.php | 157 +++ src/PHPCensor/Plugin/Grunt.php | 81 ++ src/PHPCensor/Plugin/Gulp.php | 81 ++ src/PHPCensor/Plugin/HipchatNotify.php | 93 ++ src/PHPCensor/Plugin/Irc.php | 119 ++ src/PHPCensor/Plugin/Lint.php | 140 +++ src/PHPCensor/Plugin/Mage.php | 116 ++ src/PHPCensor/Plugin/Mage3.php | 121 ++ src/PHPCensor/Plugin/Mysql.php | 160 +++ .../Plugin/Option/PhpUnitOptions.php | 265 +++++ src/PHPCensor/Plugin/PackageBuild.php | 86 ++ src/PHPCensor/Plugin/Pdepend.php | 132 +++ src/PHPCensor/Plugin/Pgsql.php | 76 ++ src/PHPCensor/Plugin/Phar.php | 212 ++++ src/PHPCensor/Plugin/Phing.php | 237 ++++ src/PHPCensor/Plugin/PhpCodeSniffer.php | 287 +++++ src/PHPCensor/Plugin/PhpCpd.php | 164 +++ src/PHPCensor/Plugin/PhpCsFixer.php | 98 ++ src/PHPCensor/Plugin/PhpDocblockChecker.php | 175 +++ src/PHPCensor/Plugin/PhpLoc.php | 93 ++ src/PHPCensor/Plugin/PhpMessDetector.php | 256 ++++ src/PHPCensor/Plugin/PhpParallelLint.php | 143 +++ src/PHPCensor/Plugin/PhpSpec.php | 118 ++ src/PHPCensor/Plugin/PhpTalLint.php | 237 ++++ src/PHPCensor/Plugin/PhpUnit.php | 177 +++ src/PHPCensor/Plugin/SecurityChecker.php | 98 ++ src/PHPCensor/Plugin/Shell.php | 77 ++ src/PHPCensor/Plugin/SlackNotify.php | 137 +++ src/PHPCensor/Plugin/Sqlite.php | 69 ++ src/PHPCensor/Plugin/TechnicalDebt.php | 202 ++++ src/PHPCensor/Plugin/Util/Executor.php | 288 +++++ src/PHPCensor/Plugin/Util/Factory.php | 214 ++++ src/PHPCensor/Plugin/Util/PhpUnitResult.php | 110 ++ .../Plugin/Util/PhpUnitResultJson.php | 145 +++ .../Plugin/Util/PhpUnitResultJunit.php | 130 ++ .../Util/TestResultParsers/Codeception.php | 110 ++ .../TestResultParsers/ParserInterface.php | 15 + src/PHPCensor/Plugin/Wipe.php | 55 + src/PHPCensor/Plugin/Xmpp.php | 196 ++++ src/PHPCensor/ProcessControl/Factory.php | 52 + .../ProcessControl/PosixProcessControl.php | 42 + .../ProcessControlInterface.php | 30 + .../ProcessControl/UnixProcessControl.php | 50 + .../LoginPasswordProviderInterface.php | 23 + .../Security/Authentication/Service.php | 106 ++ .../UserProvider/AbstractProvider.php | 35 + .../Authentication/UserProvider/Internal.php | 40 + .../Authentication/UserProvider/Ldap.php | 84 ++ .../Authentication/UserProviderInterface.php | 30 + src/PHPCensor/Service/BuildService.php | 193 +++ src/PHPCensor/Service/BuildStatusService.php | 228 ++++ src/PHPCensor/Service/ProjectService.php | 152 +++ src/PHPCensor/Service/UserService.php | 94 ++ src/PHPCensor/Store.php | 7 + src/PHPCensor/Store/BuildErrorStore.php | 228 ++++ src/PHPCensor/Store/BuildErrorWriter.php | 135 +++ src/PHPCensor/Store/BuildMetaStore.php | 169 +++ src/PHPCensor/Store/BuildStore.php | 501 ++++++++ src/PHPCensor/Store/EnvironmentStore.php | 105 ++ src/PHPCensor/Store/ProjectGroupStore.php | 99 ++ src/PHPCensor/Store/ProjectStore.php | 241 ++++ src/PHPCensor/Store/UserStore.php | 194 +++ src/PHPCensor/View/Build/errors.phtml | 38 + src/PHPCensor/View/Build/header-row.phtml | 30 + src/PHPCensor/View/Build/view.phtml | 315 +++++ src/PHPCensor/View/BuildStatus/view.phtml | 196 ++++ src/PHPCensor/View/Email/layout.phtml | 28 + src/PHPCensor/View/Email/long.phtml | 2 + src/PHPCensor/View/Email/short.phtml | 1 + src/PHPCensor/View/Group/edit.phtml | 10 + src/PHPCensor/View/Group/index.phtml | 48 + src/PHPCensor/View/Home/index.phtml | 29 + src/PHPCensor/View/Project/ajax-builds.phtml | 105 ++ src/PHPCensor/View/Project/edit.phtml | 47 + src/PHPCensor/View/Project/view.phtml | 222 ++++ src/PHPCensor/View/Session.phtml | 29 + .../View/Session/forgotPassword.phtml | 28 + src/PHPCensor/View/Session/login.phtml | 9 + .../View/Session/resetPassword.phtml | 23 + src/PHPCensor/View/User/edit.phtml | 9 + src/PHPCensor/View/User/index.phtml | 70 ++ src/PHPCensor/View/User/profile.phtml | 14 + .../WidgetAllProjects/index-projects.phtml | 149 +++ .../View/WidgetAllProjects/index.phtml | 15 + .../View/WidgetAllProjects/update.phtml | 145 +++ .../View/WidgetBuildErrors/empty.phtml | 5 + .../View/WidgetBuildErrors/index.phtml | 13 + .../View/WidgetBuildErrors/update.phtml | 157 +++ .../View/WidgetLastBuilds/index.phtml | 13 + .../View/WidgetLastBuilds/update.phtml | 117 ++ src/PHPCensor/View/exception.phtml | 16 + src/PHPCensor/View/layout.phtml | 257 ++++ src/PHPCensor/View/pagination.phtml | 28 + src/PHPCensor/Worker/BuildWorker.php | 174 +++ src/PHPCensor/ZeroConfigPluginInterface.php | 20 + 210 files changed, 29378 insertions(+) create mode 100644 src/PHPCensor/Application.php create mode 100644 src/PHPCensor/BuildFactory.php create mode 100644 src/PHPCensor/Builder.php create mode 100644 src/PHPCensor/BuilderException.php create mode 100644 src/PHPCensor/Command/CreateAdminCommand.php create mode 100644 src/PHPCensor/Command/CreateBuildCommand.php create mode 100644 src/PHPCensor/Command/InstallCommand.php create mode 100644 src/PHPCensor/Command/RebuildCommand.php create mode 100644 src/PHPCensor/Command/RebuildQueueCommand.php create mode 100644 src/PHPCensor/Command/RunCommand.php create mode 100644 src/PHPCensor/Command/ScheduleBuildCommand.php create mode 100644 src/PHPCensor/Command/WorkerCommand.php create mode 100644 src/PHPCensor/Console/Application.php create mode 100644 src/PHPCensor/Controller.php create mode 100644 src/PHPCensor/Controller/BuildController.php create mode 100644 src/PHPCensor/Controller/BuildStatusController.php create mode 100644 src/PHPCensor/Controller/GroupController.php create mode 100644 src/PHPCensor/Controller/HomeController.php create mode 100644 src/PHPCensor/Controller/ProjectController.php create mode 100644 src/PHPCensor/Controller/SessionController.php create mode 100644 src/PHPCensor/Controller/UserController.php create mode 100644 src/PHPCensor/Controller/WebhookController.php create mode 100644 src/PHPCensor/Controller/WidgetAllProjectsController.php create mode 100644 src/PHPCensor/Controller/WidgetBuildErrorsController.php create mode 100644 src/PHPCensor/Controller/WidgetLastBuildsController.php create mode 100644 src/PHPCensor/Helper/AnsiConverter.php create mode 100644 src/PHPCensor/Helper/Bitbucket.php create mode 100644 src/PHPCensor/Helper/Build.php create mode 100644 src/PHPCensor/Helper/BuildInterpolator.php create mode 100644 src/PHPCensor/Helper/CommandExecutor.php create mode 100644 src/PHPCensor/Helper/CommandExecutorInterface.php create mode 100644 src/PHPCensor/Helper/Diff.php create mode 100644 src/PHPCensor/Helper/Email.php create mode 100644 src/PHPCensor/Helper/Github.php create mode 100644 src/PHPCensor/Helper/Lang.php create mode 100644 src/PHPCensor/Helper/LoginIsDisabled.php create mode 100644 src/PHPCensor/Helper/MailerFactory.php create mode 100644 src/PHPCensor/Helper/SshKey.php create mode 100644 src/PHPCensor/Helper/User.php create mode 100644 src/PHPCensor/Languages/lang.da.php create mode 100644 src/PHPCensor/Languages/lang.de.php create mode 100644 src/PHPCensor/Languages/lang.el.php create mode 100644 src/PHPCensor/Languages/lang.en.php create mode 100644 src/PHPCensor/Languages/lang.es.php create mode 100644 src/PHPCensor/Languages/lang.fr.php create mode 100644 src/PHPCensor/Languages/lang.it.php create mode 100644 src/PHPCensor/Languages/lang.nl.php create mode 100644 src/PHPCensor/Languages/lang.pl.php create mode 100644 src/PHPCensor/Languages/lang.pt-br.php create mode 100644 src/PHPCensor/Languages/lang.ru.php create mode 100644 src/PHPCensor/Languages/lang.uk.php create mode 100644 src/PHPCensor/Languages/lang.zh.php create mode 100644 src/PHPCensor/Logging/BuildDBLogHandler.php create mode 100644 src/PHPCensor/Logging/BuildLogger.php create mode 100644 src/PHPCensor/Logging/Handler.php create mode 100644 src/PHPCensor/Logging/LoggedBuildContextTidier.php create mode 100644 src/PHPCensor/Logging/OutputLogHandler.php create mode 100644 src/PHPCensor/Migrations/20140513143726_initial_migration.php create mode 100644 src/PHPCensor/Migrations/20140513153133_change_build_keys_migration.php create mode 100644 src/PHPCensor/Migrations/20140611170618_choose_branch.php create mode 100644 src/PHPCensor/Migrations/20140730143702_fix_database_columns.php create mode 100644 src/PHPCensor/Migrations/20150131075425_archive_project.php create mode 100644 src/PHPCensor/Migrations/20150203105015_fix_column_types.php create mode 100644 src/PHPCensor/Migrations/20150308074509_add_user_providers.php create mode 100644 src/PHPCensor/Migrations/20150324174958_unique_email_and_name_user_fields.php create mode 100644 src/PHPCensor/Migrations/20151008140800_add_project_groups.php create mode 100644 src/PHPCensor/Migrations/20151009100610_remove_unique_name_index.php create mode 100644 src/PHPCensor/Migrations/20151014091859_errors_table.php create mode 100644 src/PHPCensor/Migrations/20151015124825_convert_errors.php create mode 100644 src/PHPCensor/Migrations/20160425162114_branch_column_length.php create mode 100644 src/PHPCensor/Migrations/20160623100223_project_table_defaults.php create mode 100644 src/PHPCensor/Migrations/20170103163312_added_language_and_per_page_for_user.php create mode 100644 src/PHPCensor/Migrations/20170218175400_fixed_build_error_message_column.php create mode 100644 src/PHPCensor/Migrations/20170223113127_fixed_build_log_column_for_mysql.php create mode 100644 src/PHPCensor/Migrations/20170226132922_fixed_build_log_column_for_mysql2.php create mode 100644 src/PHPCensor/Migrations/20170321131931_add_environment.php create mode 100644 src/PHPCensor/Migrations/20170413131256_added_source_column_to_build_table.php create mode 100644 src/PHPCensor/Migrations/20170416130610_fixed_environments.php create mode 100644 src/PHPCensor/Migrations/20170420142131_added_tag_column_to_build_table.php create mode 100644 src/PHPCensor/Migrations/20170711112805_fixed_build_meta_for_mysql.php create mode 100644 src/PHPCensor/Migrations/20170828142020_added_remember_me_login.php create mode 100644 src/PHPCensor/Migrations/20170913141438_added_default_branch_only.php create mode 100644 src/PHPCensor/Migrations/20171014173348_removed_project_id_from_build_meta.php create mode 100644 src/PHPCensor/Migrations/20171015123827_added_additional_columns.php create mode 100644 src/PHPCensor/Migrations/20171016143000_added_additional_columns2.php create mode 100644 src/PHPCensor/Migrations/20171019143346_added_additional_columns3.php create mode 100644 src/PHPCensor/Model.php create mode 100644 src/PHPCensor/Model/Build.php create mode 100644 src/PHPCensor/Model/Build/BitbucketBuild.php create mode 100644 src/PHPCensor/Model/Build/BitbucketHgBuild.php create mode 100644 src/PHPCensor/Model/Build/GithubBuild.php create mode 100644 src/PHPCensor/Model/Build/GitlabBuild.php create mode 100644 src/PHPCensor/Model/Build/GogsBuild.php create mode 100644 src/PHPCensor/Model/Build/LocalBuild.php create mode 100644 src/PHPCensor/Model/Build/MercurialBuild.php create mode 100644 src/PHPCensor/Model/Build/RemoteGitBuild.php create mode 100644 src/PHPCensor/Model/Build/SubversionBuild.php create mode 100644 src/PHPCensor/Model/BuildError.php create mode 100644 src/PHPCensor/Model/BuildMeta.php create mode 100644 src/PHPCensor/Model/Environment.php create mode 100644 src/PHPCensor/Model/Project.php create mode 100644 src/PHPCensor/Model/ProjectGroup.php create mode 100644 src/PHPCensor/Model/User.php create mode 100644 src/PHPCensor/Plugin.php create mode 100644 src/PHPCensor/Plugin/Atoum.php create mode 100644 src/PHPCensor/Plugin/Behat.php create mode 100644 src/PHPCensor/Plugin/Campfire.php create mode 100644 src/PHPCensor/Plugin/CleanBuild.php create mode 100644 src/PHPCensor/Plugin/Codeception.php create mode 100644 src/PHPCensor/Plugin/Composer.php create mode 100644 src/PHPCensor/Plugin/CopyBuild.php create mode 100644 src/PHPCensor/Plugin/Deployer.php create mode 100644 src/PHPCensor/Plugin/Email.php create mode 100644 src/PHPCensor/Plugin/Env.php create mode 100644 src/PHPCensor/Plugin/FlowdockNotify.php create mode 100644 src/PHPCensor/Plugin/Git.php create mode 100644 src/PHPCensor/Plugin/Grunt.php create mode 100644 src/PHPCensor/Plugin/Gulp.php create mode 100644 src/PHPCensor/Plugin/HipchatNotify.php create mode 100644 src/PHPCensor/Plugin/Irc.php create mode 100644 src/PHPCensor/Plugin/Lint.php create mode 100644 src/PHPCensor/Plugin/Mage.php create mode 100644 src/PHPCensor/Plugin/Mage3.php create mode 100644 src/PHPCensor/Plugin/Mysql.php create mode 100644 src/PHPCensor/Plugin/Option/PhpUnitOptions.php create mode 100644 src/PHPCensor/Plugin/PackageBuild.php create mode 100644 src/PHPCensor/Plugin/Pdepend.php create mode 100644 src/PHPCensor/Plugin/Pgsql.php create mode 100644 src/PHPCensor/Plugin/Phar.php create mode 100644 src/PHPCensor/Plugin/Phing.php create mode 100644 src/PHPCensor/Plugin/PhpCodeSniffer.php create mode 100644 src/PHPCensor/Plugin/PhpCpd.php create mode 100644 src/PHPCensor/Plugin/PhpCsFixer.php create mode 100644 src/PHPCensor/Plugin/PhpDocblockChecker.php create mode 100644 src/PHPCensor/Plugin/PhpLoc.php create mode 100644 src/PHPCensor/Plugin/PhpMessDetector.php create mode 100644 src/PHPCensor/Plugin/PhpParallelLint.php create mode 100644 src/PHPCensor/Plugin/PhpSpec.php create mode 100644 src/PHPCensor/Plugin/PhpTalLint.php create mode 100644 src/PHPCensor/Plugin/PhpUnit.php create mode 100644 src/PHPCensor/Plugin/SecurityChecker.php create mode 100644 src/PHPCensor/Plugin/Shell.php create mode 100644 src/PHPCensor/Plugin/SlackNotify.php create mode 100644 src/PHPCensor/Plugin/Sqlite.php create mode 100644 src/PHPCensor/Plugin/TechnicalDebt.php create mode 100644 src/PHPCensor/Plugin/Util/Executor.php create mode 100644 src/PHPCensor/Plugin/Util/Factory.php create mode 100644 src/PHPCensor/Plugin/Util/PhpUnitResult.php create mode 100644 src/PHPCensor/Plugin/Util/PhpUnitResultJson.php create mode 100644 src/PHPCensor/Plugin/Util/PhpUnitResultJunit.php create mode 100644 src/PHPCensor/Plugin/Util/TestResultParsers/Codeception.php create mode 100644 src/PHPCensor/Plugin/Util/TestResultParsers/ParserInterface.php create mode 100644 src/PHPCensor/Plugin/Wipe.php create mode 100644 src/PHPCensor/Plugin/Xmpp.php create mode 100644 src/PHPCensor/ProcessControl/Factory.php create mode 100644 src/PHPCensor/ProcessControl/PosixProcessControl.php create mode 100644 src/PHPCensor/ProcessControl/ProcessControlInterface.php create mode 100644 src/PHPCensor/ProcessControl/UnixProcessControl.php create mode 100644 src/PHPCensor/Security/Authentication/LoginPasswordProviderInterface.php create mode 100644 src/PHPCensor/Security/Authentication/Service.php create mode 100644 src/PHPCensor/Security/Authentication/UserProvider/AbstractProvider.php create mode 100644 src/PHPCensor/Security/Authentication/UserProvider/Internal.php create mode 100644 src/PHPCensor/Security/Authentication/UserProvider/Ldap.php create mode 100644 src/PHPCensor/Security/Authentication/UserProviderInterface.php create mode 100644 src/PHPCensor/Service/BuildService.php create mode 100644 src/PHPCensor/Service/BuildStatusService.php create mode 100644 src/PHPCensor/Service/ProjectService.php create mode 100644 src/PHPCensor/Service/UserService.php create mode 100644 src/PHPCensor/Store.php create mode 100644 src/PHPCensor/Store/BuildErrorStore.php create mode 100644 src/PHPCensor/Store/BuildErrorWriter.php create mode 100644 src/PHPCensor/Store/BuildMetaStore.php create mode 100644 src/PHPCensor/Store/BuildStore.php create mode 100644 src/PHPCensor/Store/EnvironmentStore.php create mode 100644 src/PHPCensor/Store/ProjectGroupStore.php create mode 100644 src/PHPCensor/Store/ProjectStore.php create mode 100644 src/PHPCensor/Store/UserStore.php create mode 100644 src/PHPCensor/View/Build/errors.phtml create mode 100644 src/PHPCensor/View/Build/header-row.phtml create mode 100644 src/PHPCensor/View/Build/view.phtml create mode 100644 src/PHPCensor/View/BuildStatus/view.phtml create mode 100644 src/PHPCensor/View/Email/layout.phtml create mode 100644 src/PHPCensor/View/Email/long.phtml create mode 100644 src/PHPCensor/View/Email/short.phtml create mode 100644 src/PHPCensor/View/Group/edit.phtml create mode 100644 src/PHPCensor/View/Group/index.phtml create mode 100644 src/PHPCensor/View/Home/index.phtml create mode 100644 src/PHPCensor/View/Project/ajax-builds.phtml create mode 100644 src/PHPCensor/View/Project/edit.phtml create mode 100644 src/PHPCensor/View/Project/view.phtml create mode 100644 src/PHPCensor/View/Session.phtml create mode 100644 src/PHPCensor/View/Session/forgotPassword.phtml create mode 100644 src/PHPCensor/View/Session/login.phtml create mode 100644 src/PHPCensor/View/Session/resetPassword.phtml create mode 100644 src/PHPCensor/View/User/edit.phtml create mode 100644 src/PHPCensor/View/User/index.phtml create mode 100644 src/PHPCensor/View/User/profile.phtml create mode 100644 src/PHPCensor/View/WidgetAllProjects/index-projects.phtml create mode 100644 src/PHPCensor/View/WidgetAllProjects/index.phtml create mode 100644 src/PHPCensor/View/WidgetAllProjects/update.phtml create mode 100644 src/PHPCensor/View/WidgetBuildErrors/empty.phtml create mode 100644 src/PHPCensor/View/WidgetBuildErrors/index.phtml create mode 100644 src/PHPCensor/View/WidgetBuildErrors/update.phtml create mode 100644 src/PHPCensor/View/WidgetLastBuilds/index.phtml create mode 100644 src/PHPCensor/View/WidgetLastBuilds/update.phtml create mode 100644 src/PHPCensor/View/exception.phtml create mode 100644 src/PHPCensor/View/layout.phtml create mode 100644 src/PHPCensor/View/pagination.phtml create mode 100644 src/PHPCensor/Worker/BuildWorker.php create mode 100644 src/PHPCensor/ZeroConfigPluginInterface.php diff --git a/src/PHPCensor/Application.php b/src/PHPCensor/Application.php new file mode 100644 index 0000000..495ffd6 --- /dev/null +++ b/src/PHPCensor/Application.php @@ -0,0 +1,172 @@ + + */ +class Application extends b8\Application +{ + /** + * @var \PHPCensor\Controller + */ + protected $controller; + + /** + * Initialise Application - Handles session verification, routing, etc. + */ + public function init() + { + $request =& $this->request; + $route = '/:controller/:action'; + $opts = ['controller' => 'Home', 'action' => 'index']; //FIXME : Initialise Application - Handles session verification, routing, etc. + + // Inlined as a closure to fix "using $this when not in object context" on 5.3 + $validateSession = function () { + if (!empty($_SESSION['php-censor-user-id'])) { + $user = b8\Store\Factory::getStore('User')->getByPrimaryKey($_SESSION['php-censor-user-id']); + + if ($user) { + $_SESSION['php-censor-user'] = $user; + return true; + } + + unset($_SESSION['php-censor-user-id']); + } + + return false; + }; + + $skipAuth = [$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) { + $skipValidation = in_array($route['controller'], ['session', 'webhook', 'build-status']); + + if (!$skipValidation && !$validateSession() && (!is_callable($skipAuth) || !$skipAuth())) { + if ($request->isAjax()) { + $response->setResponseCode(401); + $response->setContent(''); + } else { + $_SESSION['php-censor-login-redirect'] = substr($request->getPath(), 1); + $response = new RedirectResponse($response); + $response->setHeader('Location', APP_URL . 'session/login'); + } + + return false; + } + + return true; + }; + + $this->router->clearRoutes(); + $this->router->register($route, $opts, $routeHandler); + } + + /** + * Handle an incoming web request. + * + * @return Response + */ + public function handleRequest() + { + try { + $this->response = parent::handleRequest(); // FIX ME : Handle an incoming web request. + } catch (HttpException $ex) { + $this->config->set('page_title', 'Error'); + + $view = new View('exception'); + $view->exception = $ex; + + $this->response->setResponseCode($ex->getErrorCode()); + $this->response->setContent($view->render()); + } catch (\Exception $ex) { + $this->config->set('page_title', 'Error'); + + $view = new View('exception'); + $view->exception = $ex; + + $this->response->setResponseCode(500); + $this->response->setContent($view->render()); + } + + if ($this->response->hasLayout() && $this->controller && $this->controller->layout) { + $this->setLayoutVariables($this->controller->layout); + + $this->controller->layout->content = $this->response->getContent(); + $this->response->setContent($this->controller->layout->render()); + } + + return $this->response; + } + + /** + * Loads a particular controller, and injects our layout view into it. + * + * @param string $class + * + * @return b8\Controller + */ + protected function loadController($class) + { + $controller = parent::loadController($class); + $controller->layout = new View('layout'); + $controller->layout->title = 'PHP Censor'; + $controller->layout->breadcrumb = []; + + return $controller; + } + + /** + * Injects variables into the layout before rendering it. + * + * @param View $layout + */ + protected function setLayoutVariables(View &$layout) + { + $groups = []; + $groupStore = b8\Store\Factory::getStore('ProjectGroup'); + $groupList = $groupStore->getWhere([], 100, 0, [], ['title' => 'ASC']); + + foreach ($groupList['items'] as $group) { + $thisGroup = ['title' => $group->getTitle()]; + $projects = b8\Store\Factory::getStore('Project')->getByGroupId($group->getId(), false); + $thisGroup['projects'] = $projects['items']; + $groups[] = $thisGroup; + } + + $archived_projects = b8\Store\Factory::getStore('Project')->getAll(true); + $layout->archived_projects = $archived_projects['items']; + $layout->groups = $groups; + } + + /** + * Check whether we should skip auth (because it is disabled) + * + * @return boolean + */ + protected function shouldSkipAuth() + { + $config = b8\Config::getInstance(); + $disableAuth = (bool)$config->get('php-censor.security.disable_auth', false); + $defaultUserId = (integer)$config->get('php-censor.security.default_user_id', 1); + + if ($disableAuth && $defaultUserId) { + $user = b8\Store\Factory::getStore('User') + ->getByPrimaryKey($defaultUserId); + + if ($user) { + $_SESSION['php-censor-user'] = $user; + return true; + } + } + + return false; + } +} diff --git a/src/PHPCensor/BuildFactory.php b/src/PHPCensor/BuildFactory.php new file mode 100644 index 0000000..9569073 --- /dev/null +++ b/src/PHPCensor/BuildFactory.php @@ -0,0 +1,83 @@ + + */ +class BuildFactory +{ + /** + * @param $buildId + * + * @throws \Exception + * + * @return Build + */ + public static function getBuildById($buildId) + { + $build = Factory::getStore('Build')->getById($buildId); + + if (empty($build)) { + throw new \Exception('Build ID ' . $buildId . ' does not exist.'); + } + + return self::getBuild($build); + } + + /** + * Takes a generic build and returns a type-specific build model. + * + * @param Build $build The build from which to get a more specific build type. + * + * @return Build + */ + public static function getBuild(Build $build) + { + $project = $build->getProject(); + + if (!empty($project)) { + switch ($project->getType()) { + case 'remote': + $type = 'RemoteGitBuild'; + break; + case 'local': + $type = 'LocalBuild'; + break; + case 'github': + $type = 'GithubBuild'; + break; + case 'bitbucket': + $type = 'BitbucketBuild'; + break; + case 'bitbuckethg': + $type = 'BitbucketHgBuild'; + break; + case 'gitlab': + $type = 'GitlabBuild'; + break; + case 'hg': + $type = 'MercurialBuild'; + break; + case 'svn': + $type = 'SubversionBuild'; + break; + case 'gogs': + $type = 'GogsBuild'; + break; + default: + return $build; + } + + $class = '\\PHPCensor\\Model\\Build\\' . $type; + $build = new $class($build->getDataArray()); + } + + return $build; + } +} diff --git a/src/PHPCensor/Builder.php b/src/PHPCensor/Builder.php new file mode 100644 index 0000000..20ec48b --- /dev/null +++ b/src/PHPCensor/Builder.php @@ -0,0 +1,485 @@ + + */ +class Builder implements LoggerAwareInterface +{ + /** + * @var string + */ + public $buildPath; + + /** + * @var string[] + */ + public $ignore = []; + + /** + * @var string + */ + protected $directory; + + /** + * @var bool + */ + protected $verbose = true; + + /** + * @var \PHPCensor\Model\Build + */ + protected $build; + + /** + * @var LoggerInterface + */ + protected $logger; + + /** + * @var array + */ + protected $config = []; + + /** + * @var string + */ + protected $lastOutput; + + /** + * @var BuildInterpolator + */ + protected $interpolator; + + /** + * @var \PHPCensor\Store\BuildStore + */ + protected $store; + + /** + * @var bool + */ + public $quiet = false; + + /** + * @var \PHPCensor\Plugin\Util\Executor + */ + protected $pluginExecutor; + + /** + * @var Helper\CommandExecutorInterface + */ + protected $commandExecutor; + + /** + * @var Logging\BuildLogger + */ + protected $buildLogger; + + /** + * @var BuildErrorWriter + */ + private $buildErrorWriter; + + /** + * Set up the builder. + * + * @param \PHPCensor\Model\Build $build + * @param LoggerInterface $logger + */ + public function __construct(Build $build, LoggerInterface $logger = null) + { + $this->build = $build; + $this->store = Factory::getStore('Build', 'PHPCensor'); + + $this->buildLogger = new BuildLogger($logger, $build); + $pluginFactory = $this->buildPluginFactory($build); + $this->pluginExecutor = new Plugin\Util\Executor($pluginFactory, $this->buildLogger); + + $executorClass = 'PHPCensor\Helper\CommandExecutor'; + $this->commandExecutor = new $executorClass( + $this->buildLogger, + ROOT_DIR, + $this->quiet, + $this->verbose + ); + + $this->interpolator = new BuildInterpolator(); + $this->buildErrorWriter = new BuildErrorWriter($this->build->getId()); + } + + /** + * Set the config array, as read from .php-censor.yml + * + * @param array $config + * + * @throws \Exception + */ + public function setConfigArray($config) + { + if (is_null($config) || !is_array($config)) { + throw new \Exception('This project does not contain a .php-censor.yml (.phpci.yml|phpci.yml) file, or it is empty.'); + } + + $this->logDebug('Config: ' . json_encode($config)); + $this->config = $config; + } + + /** + * Access a variable from the .php-censor.yml file. + * + * @param string $key + * + * @return mixed + */ + public function getConfig($key) + { + $rtn = null; + + if (isset($this->config[$key])) { + $rtn = $this->config[$key]; + } + + return $rtn; + } + + /** + * Access a variable from the config.yml + * + * @param string $key + * + * @return mixed + */ + public function getSystemConfig($key) + { + return Config::getInstance()->get($key); + } + + /** + * @return string The title of the project being built. + */ + public function getBuildProjectTitle() + { + return $this->build->getProject()->getTitle(); + } + + /** + * Run the active build. + */ + public function execute() + { + // check current status + if ($this->build->getStatus() != Build::STATUS_PENDING) { + throw new BuilderException('Can`t build - status is not pending', BuilderException::FAIL_START); + } + // set status only if current status pending + if (!$this->build->setStatusSync(Build::STATUS_RUNNING)) { + throw new BuilderException('Can`t build - unable change status to running', BuilderException::FAIL_START); + } + + // Update the build in the database, ping any external services. + $this->build->setStartDate(new \DateTime()); + $this->store->save($this->build); + $this->build->sendStatusPostback(); + $success = true; + + $previous_build = $this->build->getProject()->getPreviousBuild($this->build->getBranch()); + + $previous_state = Build::STATUS_PENDING; + + if ($previous_build) { + $previous_state = $previous_build->getStatus(); + } + + try { + // Set up the build: + $this->setupBuild(); + + // Run the core plugin stages: + foreach ([Build::STAGE_SETUP, Build::STAGE_TEST, Build::STAGE_DEPLOY] as $stage) { + $success &= $this->pluginExecutor->executePlugins($this->config, $stage); + if (!$success) { + break; + } + } + + // Set the status so this can be used by complete, success and failure + // stages. + if ($success) { + $this->build->setStatus(Build::STATUS_SUCCESS); + } else { + $this->build->setStatus(Build::STATUS_FAILED); + } + } catch (\Exception $ex) { + $success = false; + $this->build->setStatus(Build::STATUS_FAILED); + $this->buildLogger->logFailure('Exception: ' . $ex->getMessage(), $ex); + } + + try { + if ($success) { + $this->pluginExecutor->executePlugins($this->config, Build::STAGE_SUCCESS); + + if ($previous_state == Build::STATUS_FAILED) { + $this->pluginExecutor->executePlugins($this->config, Build::STAGE_FIXED); + } + } else { + $this->pluginExecutor->executePlugins($this->config, Build::STAGE_FAILURE); + + if ($previous_state == Build::STATUS_SUCCESS || $previous_state == Build::STATUS_PENDING) { + $this->pluginExecutor->executePlugins($this->config, Build::STAGE_BROKEN); + } + } + } catch (\Exception $ex) { + $this->buildLogger->logFailure('Exception: ' . $ex->getMessage(), $ex); + } + + if (Build::STATUS_FAILED === $this->build->getStatus()) { + $this->buildLogger->logFailure("\nBUILD FAILED"); + } else { + $this->buildLogger->logSuccess("\nBUILD SUCCESS"); + } + + try { + // Complete stage plugins are always run + $this->pluginExecutor->executePlugins($this->config, Build::STAGE_COMPLETE); + } catch (\Exception $ex) { + $this->buildLogger->logFailure('Exception: ' . $ex->getMessage()); + } + + // Update the build in the database, ping any external services, etc. + $this->build->sendStatusPostback(); + $this->build->setFinishDate(new \DateTime()); + + $removeBuilds = (bool)Config::getInstance()->get('php-censor.build.remove_builds', true); + if ($removeBuilds) { + // Clean up: + $this->buildLogger->log("\nRemoving Build."); + $this->build->removeBuildDirectory(); + } + + $this->buildErrorWriter->flush(); + $this->store->save($this->build); + } + + /** + * Used by this class, and plugins, to execute shell commands. + * + * @return boolean + */ + public function executeCommand() + { + return $this->commandExecutor->executeCommand(func_get_args()); + } + + /** + * Returns the output from the last command run. + * + * @return string + */ + public function getLastOutput() + { + return $this->commandExecutor->getLastOutput(); + } + + /** + * Specify whether exec output should be logged. + * + * @param boolean $enableLog + */ + public function logExecOutput($enableLog = true) + { + $this->commandExecutor->logExecOutput = $enableLog; + } + + /** + * Find a binary required by a plugin. + * + * @param string $binary + * @param bool $quiet Returns null instead of throwing an exception. + * @param string $priorityPath + * + * @return null|string + * + * @throws \Exception when no binary has been found and $quiet is false. + */ + public function findBinary($binary, $quiet = false, $priorityPath = 'local') + { + return $this->commandExecutor->findBinary($binary, $quiet, $priorityPath); + } + + /** + * Replace every occurrence of the interpolation vars in the given string + * Example: "This is build %PHPCI_BUILD%" => "This is build 182" + * + * @param string $input + * + * @return string + */ + public function interpolate($input) + { + return $this->interpolator->interpolate($input); + } + + /** + * Set up a working copy of the project for building. + * + * @throws \Exception + * + * @return boolean + */ + protected function setupBuild() + { + $this->buildPath = $this->build->getBuildPath(); + + $this->interpolator->setupInterpolationVars( + $this->build, + $this->buildPath, + APP_URL + ); + + $this->commandExecutor->setBuildPath($this->buildPath); + + // Create a working copy of the project: + if (!$this->build->createWorkingCopy($this, $this->buildPath)) { + throw new \Exception('Could not create a working copy.'); + } + + // Does the project's .php-censor.yml request verbose mode? + if (!isset($this->config['build_settings']['verbose']) || !$this->config['build_settings']['verbose']) { + $this->verbose = false; + } + + // Does the project have any paths it wants plugins to ignore? + if (isset($this->config['build_settings']['ignore'])) { + $this->ignore = $this->config['build_settings']['ignore']; + } + + $this->buildLogger->logSuccess(sprintf('Working copy created: %s', $this->buildPath)); + + return true; + } + + /** + * Sets a logger instance on the object + * + * @param LoggerInterface $logger + */ + public function setLogger(LoggerInterface $logger) + { + $this->buildLogger->setLogger($logger); + } + + /** + * Write to the build log. + * + * @param string $message + * @param string $level + * @param array $context + */ + public function log($message, $level = LogLevel::INFO, $context = []) + { + $this->buildLogger->log($message, $level, $context); + } + + /** + * Add a success-coloured message to the log. + * + * @param string + */ + public function logSuccess($message) + { + $this->buildLogger->logSuccess($message); + } + + /** + * Add a failure-coloured message to the log. + * + * @param string $message + * @param \Exception $exception The exception that caused the error. + */ + public function logFailure($message, \Exception $exception = null) + { + $this->buildLogger->logFailure($message, $exception); + } + + /** + * Add a debug message to the log. + * + * @param string + */ + public function logDebug($message) + { + $this->buildLogger->logDebug($message); + } + + /** + * Returns a configured instance of the plugin factory. + * + * @param Build $build + * + * @return PluginFactory + */ + private function buildPluginFactory(Build $build) + { + $pluginFactory = new PluginFactory(); + + $self = $this; + $pluginFactory->registerResource( + function () use ($self) { + return $self; + }, + null, + 'PHPCensor\Builder' + ); + + $pluginFactory->registerResource( + function () use ($build) { + return $build; + }, + null, + 'PHPCensor\Model\Build' + ); + + $logger = $this->logger; + $pluginFactory->registerResource( + function () use ($logger) { + return $logger; + }, + null, + 'Psr\Log\LoggerInterface' + ); + + $pluginFactory->registerResource( + function () use ($self) { + $factory = new MailerFactory($self->getSystemConfig('php-censor')); + return $factory->getSwiftMailerFromConfig(); + }, + null, + 'Swift_Mailer' + ); + + return $pluginFactory; + } + + /** + * @return BuildErrorWriter + */ + public function getBuildErrorWriter() + { + return $this->buildErrorWriter; + } +} diff --git a/src/PHPCensor/BuilderException.php b/src/PHPCensor/BuilderException.php new file mode 100644 index 0000000..67a05b9 --- /dev/null +++ b/src/PHPCensor/BuilderException.php @@ -0,0 +1,9 @@ +userStore = $userStore; + } + + protected function configure() + { + $this + ->setName('php-censor:create-admin') + + ->addOption('admin-name', null, InputOption::VALUE_OPTIONAL, 'Admin name') + ->addOption('admin-password', null, InputOption::VALUE_OPTIONAL, 'Admin password') + ->addOption('admin-email', null, InputOption::VALUE_OPTIONAL, 'Admin email') + + ->setDescription('Create an admin user'); + } + + /** + * Creates an admin user in the existing database + * + * {@inheritDoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + /** @var $helper QuestionHelper */ + $helper = $this->getHelperSet()->get('question'); + + // Function to validate email address. + $mailValidator = function ($answer) { + if (!filter_var($answer, FILTER_VALIDATE_EMAIL)) { + throw new \InvalidArgumentException('Must be a valid email address.'); + } + + return $answer; + }; + + if ($adminEmail = $input->getOption('admin-email')) { + $adminEmail = $mailValidator($adminEmail); + } else { + $questionEmail = new Question('Admin email: '); + $adminEmail = $helper->ask($input, $output, $questionEmail); + } + + if (!$adminName = $input->getOption('admin-name')) { + $questionName = new Question('Admin name: '); + $adminName = $helper->ask($input, $output, $questionName); + } + + if (!$adminPassword = $input->getOption('admin-password')) { + $questionPassword = new Question('Admin password: '); + $questionPassword->setHidden(true); + $questionPassword->setHiddenFallback(false); + $adminPassword = $helper->ask($input, $output, $questionPassword); + } + + try { + $userService = new UserService($this->userStore); + $userService->createUser($adminName, $adminEmail, 'internal', json_encode(['type' => 'internal']), $adminPassword, true); + + $output->writeln('User account created!'); + } catch (\Exception $ex) { + $output->writeln('PHP Censor failed to create your admin account!'); + $output->writeln('' . $ex->getMessage() . ''); + } + } +} diff --git a/src/PHPCensor/Command/CreateBuildCommand.php b/src/PHPCensor/Command/CreateBuildCommand.php new file mode 100644 index 0000000..28b9759 --- /dev/null +++ b/src/PHPCensor/Command/CreateBuildCommand.php @@ -0,0 +1,83 @@ +projectStore = $projectStore; + $this->buildService = $buildService; + } + + /** + * {@inheritDoc} + */ + protected function configure() + { + $this + ->setName('php-censor:create-build') + ->setDescription('Create a build for a project') + ->addArgument('projectId', InputArgument::REQUIRED, 'A project ID') + ->addOption('commit', null, InputOption::VALUE_OPTIONAL, 'Commit ID to build') + ->addOption('branch', null, InputOption::VALUE_OPTIONAL, 'Branch to build') + ->addOption('email', null, InputOption::VALUE_OPTIONAL, 'Committer email') + ->addOption('message', null, InputOption::VALUE_OPTIONAL, 'Commit message'); + } + + /** + * {@inheritDoc} + */ + public function execute(InputInterface $input, OutputInterface $output) + { + $projectId = $input->getArgument('projectId'); + $commitId = $input->getOption('commit'); + $branch = $input->getOption('branch'); + $environment = $input->hasOption('environment') ? $input->getOption('environment') : null; + $ciEmail = $input->getOption('email'); + $ciMessage = $input->getOption('message'); + + $project = $this->projectStore->getById($projectId); + if (empty($project) || $project->getArchived()) { + throw new \InvalidArgumentException('Project does not exist: ' . $projectId); + } + + try { + $this->buildService->createBuild($project, $environment, $commitId, $branch, null, $ciEmail, $ciMessage, Build::SOURCE_MANUAL_CONSOLE); + $output->writeln('Build Created'); + } catch (\Exception $e) { + $output->writeln('Failed'); + $output->writeln(sprintf('%s', $e->getMessage())); + } + } +} diff --git a/src/PHPCensor/Command/InstallCommand.php b/src/PHPCensor/Command/InstallCommand.php new file mode 100644 index 0000000..16e2537 --- /dev/null +++ b/src/PHPCensor/Command/InstallCommand.php @@ -0,0 +1,557 @@ + + */ +class InstallCommand extends Command +{ + /** + * @var string + */ + protected $configPath = APP_DIR . 'config.yml'; + + protected function configure() + { + $this + ->setName('php-censor:install') + + ->addOption('url', null, InputOption::VALUE_OPTIONAL, 'PHP Censor installation URL') + ->addOption('db-type', null, InputOption::VALUE_OPTIONAL, 'Database type') + ->addOption('db-host', null, InputOption::VALUE_OPTIONAL, 'Database host') + ->addOption('db-port', null, InputOption::VALUE_OPTIONAL, 'Database port') + ->addOption('db-name', null, InputOption::VALUE_OPTIONAL, 'Database name') + ->addOption('db-user', null, InputOption::VALUE_OPTIONAL, 'Database user') + ->addOption('db-password', null, InputOption::VALUE_OPTIONAL, 'Database password') + ->addOption('admin-name', null, InputOption::VALUE_OPTIONAL, 'Admin name') + ->addOption('admin-password', null, InputOption::VALUE_OPTIONAL, 'Admin password') + ->addOption('admin-email', null, InputOption::VALUE_OPTIONAL, 'Admin email') + ->addOption('queue-use', null, InputOption::VALUE_OPTIONAL, 'Don\'t ask for queue details', true) + ->addOption('queue-host', null, InputOption::VALUE_OPTIONAL, 'Beanstalkd queue server hostname') + ->addOption('queue-name', null, InputOption::VALUE_OPTIONAL, 'Beanstalkd queue name') + ->addOption('config-from-file', null, InputOption::VALUE_OPTIONAL, 'Take config from file and ignore options', false) + + ->setDescription('Install PHP Censor'); + } + + /** + * Installs PHP Censor + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $configFromFile = (boolean)$input->getOption('config-from-file'); + + if (!$configFromFile && !$this->verifyNotInstalled($output)) { + return; + } + + $output->writeln(''); + $output->writeln('***************************************'); + $output->writeln('* Welcome to PHP Censor installation *'); + $output->writeln('***************************************'); + $output->writeln(''); + + $this->checkRequirements($output); + + if (!$configFromFile) { + $output->writeln(''); + $output->writeln('Please answer the following questions:'); + $output->writeln('--------------------------------------'); + $output->writeln(''); + + $connectionVerified = false; + while (!$connectionVerified) { + $db = $this->getDatabaseInformation($input, $output); + $connectionVerified = $this->verifyDatabaseDetails($db, $output); + } + $output->writeln(''); + + $conf = []; + $conf['b8']['database'] = $db; + $conf['php-censor'] = $this->getConfigInformation($input, $output); + + $this->writeConfigFile($conf); + } + + $this->reloadConfig(); + $this->setupDatabase($output); + + $admin = $this->getAdminInformation($input, $output); + $this->createAdminUser($admin, $output); + + $this->createDefaultGroup($output); + } + + /** + * @param OutputInterface $output + * + * @return bool + */ + protected function verifyNotInstalled(OutputInterface $output) + { + if (file_exists($this->configPath)) { + $content = file_get_contents($this->configPath); + + if (!empty($content)) { + $output->writeln('The PHP Censor config file exists and is not empty. PHP Censor is already installed!'); + return false; + } + } + + return true; + } + + /** + * Check PHP version, required modules and for disabled functions. + * + * @param OutputInterface $output + * + * @throws \Exception + */ + protected function checkRequirements(OutputInterface $output) + { + $output->writeln('Checking requirements...'); + $errors = false; + + if (!(version_compare(PHP_VERSION, '5.6.0') >= 0)) { + $output->writeln(''); + $output->writeln('PHP Censor requires at least PHP 5.6.0! Installed PHP ' . PHP_VERSION . ''); + $errors = true; + } + + $requiredExtensions = ['PDO', 'xml', 'json', 'curl', 'openssl']; + + foreach ($requiredExtensions as $extension) { + if (!extension_loaded($extension)) { + $output->writeln(''); + $output->writeln('Extension required: ' . $extension . ''); + $errors = true; + } + } + + $requiredFunctions = ['exec', 'shell_exec', 'proc_open', 'password_hash']; + + foreach ($requiredFunctions as $function) { + if (!function_exists($function)) { + $output->writeln(''); + $output->writeln('PHP Censor needs to be able to call the ' . $function . '() function. Is it disabled in php.ini?'); + $errors = true; + } + } + + if ($errors) { + throw new Exception('PHP Censor cannot be installed, as not all requirements are met. Please review the errors above before continuing.'); + } + + $output->writeln(''); + $output->writeln('OK'); + } + + /** + * Load information for admin user form CLI options or ask info to user. + * + * @param InputInterface $input + * @param OutputInterface $output + * @return array + */ + protected function getAdminInformation(InputInterface $input, OutputInterface $output) + { + $admin = []; + + /** @var $helper QuestionHelper */ + $helper = $this->getHelperSet()->get('question'); + + // Function to validate email address. + $mailValidator = function ($answer) { + if (!filter_var($answer, FILTER_VALIDATE_EMAIL)) { + throw new \InvalidArgumentException('Must be a valid email address.'); + } + + return $answer; + }; + + if ($adminEmail = $input->getOption('admin-email')) { + $adminEmail = $mailValidator($adminEmail); + } else { + $questionEmail = new Question('Admin email: '); + $adminEmail = $helper->ask($input, $output, $questionEmail); + } + + if (!$adminName = $input->getOption('admin-name')) { + $questionName = new Question('Admin name: '); + $adminName = $helper->ask($input, $output, $questionName); + } + + if (!$adminPassword = $input->getOption('admin-password')) { + $questionPassword = new Question('Admin password: '); + $questionPassword->setHidden(true); + $questionPassword->setHiddenFallback(false); + $adminPassword = $helper->ask($input, $output, $questionPassword); + } + + $admin['email'] = $adminEmail; + $admin['name'] = $adminName; + $admin['password'] = $adminPassword; + + return $admin; + } + + /** + * Load configuration form CLI options or ask info to user. + * + * @param InputInterface $input + * @param OutputInterface $output + * + * @return array + */ + protected function getConfigInformation(InputInterface $input, OutputInterface $output) + { + /** @var $helper QuestionHelper */ + $helper = $this->getHelperSet()->get('question'); + + $urlValidator = function ($answer) { + if (!filter_var($answer, FILTER_VALIDATE_URL)) { + throw new Exception('Must be a valid URL.'); + } + + return rtrim($answer, '/'); + }; + + if ($url = $input->getOption('url')) { + $url = $urlValidator($url); + } else { + $question = new Question('Your PHP Censor URL ("http://php-censor.local" for example): '); + $question->setValidator($urlValidator); + $url = $helper->ask($input, $output, $question); + } + + $queueConfig = $this->getQueueInformation($input, $output); + + return [ + 'language' => 'en', + 'per_page' => 10, + 'url' => $url, + 'queue' => $queueConfig, + 'log' => [ + 'rotate' => false, + 'max_files' => 0, + ], + 'email_settings' => [ + 'from_address' => 'PHP Censor ', + 'smtp_address' => null, + 'smtp_port' => null, + 'smtp_username' => null, + 'smtp_password' => null, + 'smtp_encryption' => false, + ], + 'bitbucket' => [ + 'username' => null, + 'app_password' => null, + 'comments' => [ + 'commit' => false, + 'pull_request' => false, + ], + ], + 'github' => [ + 'token' => null, + 'comments' => [ + 'commit' => false, + 'pull_request' => false, + ], + ], + 'build' => [ + 'remove_builds' => true, + 'writer_buffer_size' => 500, + ], + 'security' => [ + 'disable_auth' => false, + 'default_user_id' => 1, + 'auth_providers' => [ + 'internal' => [ + 'type' => 'internal', + ], + ], + ], + 'dashboard_widgets' => [ + 'all_projects' => [ + 'side' => 'left', + ], + 'last_builds' => [ + 'side' => 'right', + ], + ], + ]; + } + + /** + * If the user wants to use a queue, get the necessary details. + * + * @param InputInterface $input + * @param OutputInterface $output + * + * @return array + */ + protected function getQueueInformation(InputInterface $input, OutputInterface $output) + { + $skipQueueConfig = [ + 'use_queue' => false, + 'host' => null, + 'name' => null, + 'lifetime' => 600, + ]; + + if (!$input->getOption('queue-use')) { + return $skipQueueConfig; + } + + $queueConfig = [ + 'use_queue' => true, + 'host' => null, + 'name' => null, + 'lifetime' => 600, + ]; + + $queueConfig['host'] = $input->getOption('queue-host'); + $queueConfig['name'] = $input->getOption('queue-name'); + + if (!$queueConfig['host'] && !$queueConfig['name']) { + /** @var $helper QuestionHelper */ + $helper = $this->getHelper('question'); + $question = new ConfirmationQuestion('Use beanstalkd to manage build queue? ', false); + + if (!$helper->ask($input, $output, $question)) { + $output->writeln('Skipping beanstalkd configuration.'); + + return $skipQueueConfig; + } + + $questionQueue = new Question('Enter your beanstalkd hostname [localhost]: ', 'localhost'); + $queueConfig['host'] = $helper->ask($input, $output, $questionQueue); + + $questionName = new Question('Enter the queue (tube) name to use [php-censor-queue]: ', 'php-censor-queue'); + $queueConfig['name'] = $helper->ask($input, $output, $questionName); + } + + return $queueConfig; + } + + /** + * Load configuration for database form CLI options or ask info to user. + * + * @param InputInterface $input + * @param OutputInterface $output + * + * @return array + */ + protected function getDatabaseInformation(InputInterface $input, OutputInterface $output) + { + $db = []; + + /** @var $helper QuestionHelper */ + $helper = $this->getHelperSet()->get('question'); + + if (!$dbType = $input->getOption('db-type')) { + $questionType = new Question('Please enter your database type (mysql or pgsql): '); + $dbType = $helper->ask($input, $output, $questionType); + } + + if (!$dbHost = $input->getOption('db-host')) { + $questionHost = new Question('Please enter your database host (default: localhost): ', 'localhost'); + $dbHost = $helper->ask($input, $output, $questionHost); + } + + if (!$dbPort = $input->getOption('db-port')) { + $questionPort = new Question('Please enter your database port (default: empty): '); + $dbPort = $helper->ask($input, $output, $questionPort); + } + + if (!$dbName = $input->getOption('db-name')) { + $questionDb = new Question('Please enter your database name (default: php-censor-db): ', 'php-censor-db'); + $dbName = $helper->ask($input, $output, $questionDb); + } + + if (!$dbUser = $input->getOption('db-user')) { + $questionUser = new Question('Please enter your DB user (default: php-censor-user): ', 'php-censor-user'); + $dbUser = $helper->ask($input, $output, $questionUser); + } + + if (!$dbPass = $input->getOption('db-password')) { + $questionPass = new Question('Please enter your database password: '); + $questionPass->setHidden(true); + $questionPass->setHiddenFallback(false); + $dbPass = $helper->ask($input, $output, $questionPass); + } + + $dbServers = [ + [ + 'host' => $dbHost, + ] + ]; + + $dbPort = (integer)$dbPort; + if ($dbPort) { + $dbServers[0]['port'] = $dbPort; + } + + $db['servers']['read'] = $dbServers; + $db['servers']['write'] = $dbServers; + + $db['type'] = $dbType; + $db['name'] = $dbName; + $db['username'] = $dbUser; + $db['password'] = $dbPass; + + return $db; + } + + /** + * Try and connect to DB using the details provided + * + * @param array $db + * @param OutputInterface $output + * + * @return bool + */ + protected function verifyDatabaseDetails(array $db, OutputInterface $output) + { + $dns = $db['type'] . ':host=' . $db['servers']['write'][0]['host']; + if (isset($db['servers']['write'][0]['port'])) { + $dns .= ';port=' . (integer)$db['servers']['write'][0]['port']; + } + $dns .= ';dbname=' . $db['name']; + + $pdoOptions = [ + \PDO::ATTR_PERSISTENT => false, + \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION, + \PDO::ATTR_TIMEOUT => 2, + ]; + if ('mysql' === $db['type']) { + $pdoOptions[\PDO::MYSQL_ATTR_INIT_COMMAND] = "SET NAMES 'UTF8'"; + } + + try { + $pdo = new PDO( + $dns, + $db['username'], + $db['password'], + $pdoOptions + ); + + unset($pdo); + + return true; + + } catch (Exception $ex) { + $output->writeln('PHP Censor could not connect to database with the details provided. Please try again.'); + $output->writeln('' . $ex->getMessage() . ''); + } + + return false; + } + + /** + * Write the config.yml file. + * @param array $config + */ + protected function writeConfigFile(array $config) + { + $dumper = new Dumper(); + $yaml = $dumper->dump($config, 4); + + file_put_contents($this->configPath, $yaml); + } + + protected function setupDatabase(OutputInterface $output) + { + $output->write('Setting up your database...'); + + $outputMigration = shell_exec(ROOT_DIR . 'bin/console php-censor-migrations:migrate'); + + $output->writeln(''); + $output->writeln($outputMigration); + $output->writeln('OK'); + } + + /** + * Create admin user using information loaded before. + * + * @param array $admin + * @param OutputInterface $output + */ + protected function createAdminUser($admin, $output) + { + try { + /** @var UserStore $userStore */ + $userStore = Factory::getStore('User'); + $adminUser = $userStore->getByEmail($admin['email']); + if ($adminUser) { + throw new \RuntimeException('Admin account already exists!'); + } + + $userService = new UserService($userStore); + $userService->createUser($admin['name'], $admin['email'], 'internal', json_encode(['type' => 'internal']), $admin['password'], true); + + $output->writeln('User account created!'); + } catch (\Exception $ex) { + $output->writeln('PHP Censor failed to create your admin account!'); + $output->writeln('' . $ex->getMessage() . ''); + } + } + + /** + * @param OutputInterface $output + */ + protected function createDefaultGroup($output) + { + try { + /** @var ProjectGroupStore $projectGroupStore */ + $projectGroupStore = Factory::getStore('ProjectGroup'); + $projectGroup = $projectGroupStore->getByTitle('Projects'); + if ($projectGroup) { + throw new \RuntimeException('Default project group already exists!'); + } + + $group = new ProjectGroup(); + $group->setTitle('Projects'); + $group->setCreateDate(new \DateTime()); + $group->setUserId(0); + + Factory::getStore('ProjectGroup')->save($group); + + $output->writeln('Default project group created!'); + } catch (\Exception $ex) { + $output->writeln('PHP Censor failed to create default project group!'); + $output->writeln('' . $ex->getMessage() . ''); + } + } + + protected function reloadConfig() + { + $config = Config::getInstance(); + + if (file_exists($this->configPath)) { + $config->loadYaml($this->configPath); + } + } +} diff --git a/src/PHPCensor/Command/RebuildCommand.php b/src/PHPCensor/Command/RebuildCommand.php new file mode 100644 index 0000000..e62dcab --- /dev/null +++ b/src/PHPCensor/Command/RebuildCommand.php @@ -0,0 +1,85 @@ + + */ +class RebuildCommand extends Command +{ + /** + * @var Logger + */ + protected $logger; + + /** + * @var OutputInterface + */ + protected $output; + + /** + * @var boolean + */ + protected $run; + + /** + * @var int + */ + protected $sleep; + + /** + * @param \Monolog\Logger $logger + * @param string $name + */ + public function __construct(Logger $logger, $name = null) + { + parent::__construct($name); + $this->logger = $logger; + } + + protected function configure() + { + $this + ->setName('php-censor:rebuild') + ->setDescription('Re-runs the last run build.'); + } + + /** + * Loops through running. + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $runner = new RunCommand($this->logger); + $runner->setMaxBuilds(1); + + /** @var \PHPCensor\Store\BuildStore $store */ + $store = Factory::getStore('Build'); + $service = new BuildService($store); + + $builds = $store->getLatestBuilds(null, 1); + $lastBuild = array_shift($builds); + $service->createDuplicateBuild($lastBuild); + + $runner->run(new ArgvInput([]), $output); + } + + /** + * Called when log entries are made in Builder / the plugins. + * + * @see \PHPCensor\Builder::log() + */ + public function logCallback($log) + { + $this->output->writeln($log); + } +} diff --git a/src/PHPCensor/Command/RebuildQueueCommand.php b/src/PHPCensor/Command/RebuildQueueCommand.php new file mode 100644 index 0000000..4783702 --- /dev/null +++ b/src/PHPCensor/Command/RebuildQueueCommand.php @@ -0,0 +1,73 @@ + + */ +class RebuildQueueCommand extends Command +{ + /** + * @var OutputInterface + */ + protected $output; + + /** + * @var Logger + */ + protected $logger; + + /** + * @param \Monolog\Logger $logger + * @param string $name + */ + public function __construct(Logger $logger, $name = null) + { + parent::__construct($name); + $this->logger = $logger; + } + + protected function configure() + { + $this + ->setName('php-censor:rebuild-queue') + ->setDescription('Rebuilds the PHP Censor worker queue.'); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->output = $output; + + // For verbose mode we want to output all informational and above + // messages to the symphony output interface. + if ($input->hasOption('verbose') && $input->getOption('verbose')) { + $this->logger->pushHandler( + new OutputLogHandler($this->output, Logger::INFO) + ); + } + + $store = Factory::getStore('Build'); + $result = $store->getByStatus(0); + + $this->logger->addInfo(sprintf('Found %d builds', count($result['items']))); + + $buildService = new BuildService($store); + + while (count($result['items'])) { + $build = array_shift($result['items']); + $build = BuildFactory::getBuild($build); + + $this->logger->addInfo('Added build #' . $build->getId() . ' to queue.'); + $buildService->addBuildToQueue($build); + } + } +} diff --git a/src/PHPCensor/Command/RunCommand.php b/src/PHPCensor/Command/RunCommand.php new file mode 100644 index 0000000..2ff7f56 --- /dev/null +++ b/src/PHPCensor/Command/RunCommand.php @@ -0,0 +1,173 @@ + + */ +class RunCommand extends Command +{ + /** + * @var OutputInterface + */ + protected $output; + + /** + * @var Logger + */ + protected $logger; + + /** + * @var int + */ + protected $maxBuilds = 10; + + /** + * @param \Monolog\Logger $logger + * @param string $name + */ + public function __construct(Logger $logger, $name = null) + { + parent::__construct($name); + $this->logger = $logger; + } + + protected function configure() + { + $this + ->setName('php-censor:run-builds') + ->setDescription('Run all pending PHP Censor builds') + ->addOption('debug', null, null, 'Run PHP Censor in debug mode'); + } + + /** + * Pulls all pending builds from the database or queue and runs them. + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->output = $output; + + // For verbose mode we want to output all informational and above + // messages to the symphony output interface. + if ($input->hasOption('verbose') && $input->getOption('verbose')) { + $this->logger->pushHandler( + new OutputLogHandler($this->output, Logger::INFO) + ); + } + + // Allow PHP Censor to run in "debug mode" + if ($input->hasOption('debug') && $input->getOption('debug')) { + $output->writeln('Debug mode enabled.'); + define('DEBUG_MODE', true); + } + + $running = $this->validateRunningBuilds(); + + $this->logger->pushProcessor(new LoggedBuildContextTidier()); + $this->logger->addInfo('Finding builds to process'); + + /** @var BuildStore $buildStore */ + $buildStore = Factory::getStore('Build'); + $result = $buildStore->getByStatus(Build::STATUS_PENDING, $this->maxBuilds); + + $this->logger->addInfo(sprintf('Found %d builds', count($result['items']))); + + $builds = 0; + + while (count($result['items'])) { + $build = array_shift($result['items']); + $build = BuildFactory::getBuild($build); + + // Skip build (for now) if there's already a build running in that project: + if (!empty($running[$build->getProjectId()])) { + $this->logger->addInfo(sprintf('Skipping Build %d - Project build already in progress.', $build->getId())); + continue; + } + + $builds++; + + // Logging relevant to this build should be stored + // against the build itself. + $buildDbLog = new BuildDBLogHandler($build, Logger::INFO); + $this->logger->pushHandler($buildDbLog); + + try { + $builder = new Builder($build, $this->logger); + $builder->execute(); + } catch (\Exception $ex) { + $this->logger->addError($ex->getMessage()); + + $build->setStatus(Build::STATUS_FAILED); + $build->setFinishDate(new \DateTime()); + $build->setLog($build->getLog() . PHP_EOL . PHP_EOL . $ex->getMessage()); + $buildStore->save($build); + $build->sendStatusPostback(); + } + + // After execution we no longer want to record the information + // back to this specific build so the handler should be removed. + $this->logger->popHandler(); + // destructor implicitly call flush + unset($buildDbLog); + + // Re-run build validator: + $running = $this->validateRunningBuilds(); + } + + $this->logger->addInfo('Finished processing builds.'); + + return $builds; + } + + public function setMaxBuilds($numBuilds) + { + $this->maxBuilds = (int)$numBuilds; + } + + protected function validateRunningBuilds() + { + /** @var \PHPCensor\Store\BuildStore $store */ + $store = Factory::getStore('Build'); + $running = $store->getByStatus(Build::STATUS_RUNNING); + $rtn = []; + + $timeout = Config::getInstance()->get('php-censor.build.failed_after', 1800); + + foreach ($running['items'] as $build) { + /** @var \PHPCensor\Model\Build $build */ + $build = BuildFactory::getBuild($build); + + $now = time(); + $start = $build->getStartDate()->getTimestamp(); + + if (($now - $start) > $timeout) { + $this->logger->addInfo(sprintf('Build %d marked as failed due to timeout.', $build->getId())); + $build->setStatus(Build::STATUS_FAILED); + $build->setFinishDate(new \DateTime()); + $store->save($build); + $build->removeBuildDirectory(); + continue; + } + + $rtn[$build->getProjectId()] = true; + } + + return $rtn; + } +} diff --git a/src/PHPCensor/Command/ScheduleBuildCommand.php b/src/PHPCensor/Command/ScheduleBuildCommand.php new file mode 100644 index 0000000..57c02f9 --- /dev/null +++ b/src/PHPCensor/Command/ScheduleBuildCommand.php @@ -0,0 +1,101 @@ + + */ +class ScheduleBuildCommand extends Command +{ + /** + * @var ProjectStore + */ + protected $projectStore; + + /** + * @var BuildStore + */ + protected $buildStore; + + /** + * @var BuildService + */ + protected $buildService; + + /** + * @param ProjectStore $projectStore + * @param BuildStore $buildStore + * @param BuildService $buildService + */ + public function __construct(ProjectStore $projectStore, BuildStore $buildStore, BuildService $buildService) + { + parent::__construct(); + + $this->projectStore = $projectStore; + $this->buildService = $buildService; + $this->buildStore = $buildStore; + } + + /** + * {@inheritDoc} + */ + protected function configure() + { + $this + ->setName('php-censor:schedule-build') + ->setDescription('Schedules a build for active projects which have not been ran by X days') + ->addArgument('days', InputArgument::REQUIRED, 'Since specified days'); + } + + /** + * {@inheritDoc} + */ + public function execute(InputInterface $input, OutputInterface $output) + { + $sinceDays = $input->getArgument('days'); + $date = new \DateTime('now'); + $difference = new \DateInterval("P{$sinceDays}D"); + $date->sub($difference); + + $projects = $this->projectStore->getAll(); + $projects = $projects['items']; + /** @var Project $project */ + foreach ($projects as $project) { + $latestBuild = $this->buildStore->getLatestBuilds($project->getId(), 1); + + if ($latestBuild) { + /** @var Build $build */ + $build = $latestBuild[0]; + $status = (integer)$build->getStatus(); + if ($status === Build::STATUS_RUNNING || $status === Build::STATUS_PENDING) { + // If it's running or just created, we don't want to reschedule already. + continue; + } + if ($date < $build->getFinishDate()) { + // If finished date is newer then the specified since days, we don't want to reschedule + continue; + } + } + + try { + $this->buildService->createBuild($project, null, '', null, null, null, null, Build::SOURCE_PERIODICAL); + $output->writeln("Build Created for {$project->getTitle()}"); + } catch (\Exception $e) { + $output->writeln('Failed'); + $output->writeln(sprintf('%s', $e->getMessage())); + } + } + } +} diff --git a/src/PHPCensor/Command/WorkerCommand.php b/src/PHPCensor/Command/WorkerCommand.php new file mode 100644 index 0000000..3004c51 --- /dev/null +++ b/src/PHPCensor/Command/WorkerCommand.php @@ -0,0 +1,77 @@ + + */ +class WorkerCommand extends Command +{ + /** + * @var OutputInterface + */ + protected $output; + + /** + * @var Logger + */ + protected $logger; + + /** + * @param \Monolog\Logger $logger + * @param string $name + */ + public function __construct(Logger $logger, $name = null) + { + parent::__construct($name); + $this->logger = $logger; + } + + protected function configure() + { + $this + ->setName('php-censor:worker') + ->setDescription('Runs the PHP Censor build worker.') + ->addOption('debug', null, null, 'Run PHP Censor in Debug Mode'); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->output = $output; + + // For verbose mode we want to output all informational and above + // messages to the symphony output interface. + if ($input->hasOption('verbose') && $input->getOption('verbose')) { + $this->logger->pushHandler( + new OutputLogHandler($this->output, Logger::INFO) + ); + } + + // Allow to run in "debug mode" + if ($input->hasOption('debug') && $input->getOption('debug')) { + $output->writeln('Debug mode enabled.'); + define('DEBUG_MODE', true); + } + + $config = Config::getInstance()->get('php-censor.queue', []); + + if (empty($config['host']) || empty($config['name'])) { + $error = 'The worker is not configured. You must set a host and queue in your config.yml file.'; + throw new \Exception($error); + } + + $worker = new BuildWorker($config['host'], $config['name']); + $worker->setLogger($this->logger); + $worker->startWorker(); + } +} diff --git a/src/PHPCensor/Console/Application.php b/src/PHPCensor/Console/Application.php new file mode 100644 index 0000000..66310a0 --- /dev/null +++ b/src/PHPCensor/Console/Application.php @@ -0,0 +1,140 @@ +get('php-censor.log.rotate', false); + $maxFiles = (int)$applicationConfig->get('php-censor.log.max_files', 0); + + $loggerHandlers = []; + if ($rotate) { + $loggerHandlers[] = new RotatingFileHandler(RUNTIME_DIR . 'console.log', $maxFiles, Logger::DEBUG); + } else { + $loggerHandlers[] = new StreamHandler(RUNTIME_DIR . 'console.log', Logger::DEBUG); + } + + $logger = new Logger('php-censor', $loggerHandlers); + Handler::register($logger); + + return $logger; + } + + /** + * Constructor. + * + * @param string $name The name of the application + * @param string $version The version of the application + */ + public function __construct($name = 'PHP Censor - Continuous Integration for PHP', $version = '') + { + parent::__construct($name, $version); + + $applicationConfig = Config::getInstance(); + $databaseSettings = $applicationConfig->get('b8.database', []); + + $phinxSettings = []; + if ($databaseSettings) { + $phinxSettings = [ + 'paths' => [ + 'migrations' => ROOT_DIR . 'src/PHPCensor/Migrations', + ], + 'environments' => [ + 'default_migration_table' => 'migration', + 'default_database' => 'php-censor', + 'php-censor' => [ + 'adapter' => $databaseSettings['type'], + 'host' => $databaseSettings['servers']['write'][0]['host'], + 'name' => $databaseSettings['name'], + 'user' => $databaseSettings['username'], + 'pass' => $databaseSettings['password'], + ], + ], + ]; + } + + if (!empty($databaseSettings['port'])) { + $phinxSettings['environments']['php-censor']['port'] = (integer)$databaseSettings['port']; + } + + $phinxConfig = new PhinxConfig($phinxSettings); + + $this->add( + (new Create()) + ->setConfig($phinxConfig) + ->setName('php-censor-migrations:create') + ); + $this->add( + (new Migrate()) + ->setConfig($phinxConfig) + ->setName('php-censor-migrations:migrate') + ); + $this->add( + (new Rollback()) + ->setConfig($phinxConfig) + ->setName('php-censor-migrations:rollback') + ); + $this->add( + (new Status()) + ->setConfig($phinxConfig) + ->setName('php-censor-migrations:status') + ); + + /** @var UserStore $userStore */ + $userStore = Factory::getStore('User'); + + /** @var ProjectStore $projectStore */ + $projectStore = Factory::getStore('Project'); + + /** @var BuildStore $buildStore */ + $buildStore = Factory::getStore('Build'); + + $logger = $this->initLogger($applicationConfig); + + $this->add(new RunCommand($logger)); + $this->add(new RebuildCommand($logger)); + $this->add(new InstallCommand()); + $this->add(new CreateAdminCommand($userStore)); + $this->add(new CreateBuildCommand($projectStore, new BuildService($buildStore))); + $this->add(new WorkerCommand($logger)); + $this->add(new RebuildQueueCommand($logger)); + $this->add(new ScheduleBuildCommand($projectStore, $buildStore, new BuildService($buildStore))); + } +} diff --git a/src/PHPCensor/Controller.php b/src/PHPCensor/Controller.php new file mode 100644 index 0000000..e792af7 --- /dev/null +++ b/src/PHPCensor/Controller.php @@ -0,0 +1,124 @@ +className = substr(array_pop($class), 0, -10); + $this->setControllerView(); + } + + /** + * Set the view that this controller should use. + */ + protected function setControllerView() + { + if (View::exists($this->className)) { + $this->controllerView = new View($this->className); + } else { + $this->controllerView = new View\Template('{@content}'); + } + } + + /** + * Set the view that this controller action should use. + * + * @param string $action + */ + protected function setView($action) + { + if (View::exists($this->className . '/' . $action)) { + $this->view = new View($this->className . '/' . $action); + } + } + + /** + * Handle the incoming request. + * + * @param string $action + * @param array $actionParams + * + * @return Response + */ + public function handleAction($action, $actionParams) + { + $this->setView($action); + $response = parent::handleAction($action, $actionParams); + + if ($response instanceof Response) { + return $response; + } + + if (is_string($response)) { + $this->controllerView->content = $response; + } elseif (isset($this->view)) { + $this->controllerView->content = $this->view->render(); + } + + $this->response->setContent($this->controllerView->render()); + + return $this->response; + } + + /** + * Require that the currently logged in user is an administrator. + * + * @throws ForbiddenException + */ + protected function requireAdmin() + { + if (!$this->currentUserIsAdmin()) { + throw new ForbiddenException('You do not have permission to do that.'); + } + } + + /** + * Check if the currently logged in user is an administrator. + * + * @return boolean + */ + protected function currentUserIsAdmin() + { + return $_SESSION['php-censor-user']->getIsAdmin(); + } +} diff --git a/src/PHPCensor/Controller/BuildController.php b/src/PHPCensor/Controller/BuildController.php new file mode 100644 index 0000000..fc2350c --- /dev/null +++ b/src/PHPCensor/Controller/BuildController.php @@ -0,0 +1,371 @@ + + */ +class BuildController extends Controller +{ + /** + * @var \PHPCensor\Store\BuildStore + */ + protected $buildStore; + + /** + * @var \PHPCensor\Service\BuildService + */ + protected $buildService; + + /** + * Initialise the controller, set up stores and services. + */ + public function init() + { + $this->buildStore = b8\Store\Factory::getStore('Build'); + $this->buildService = new BuildService($this->buildStore); + } + + /** + * View a specific build. + * + * @param integer $buildId + * + * @throws NotFoundException + */ + public function view($buildId) + { + $page = (integer)$this->getParam('page', 1); + $plugin = $this->getParam('plugin', ''); + + $severity = $this->getParam('severity', null); + if (null !== $severity && '' !== $severity) { + $severity = (integer)$severity; + } else { + $severity = null; + } + + try { + $build = BuildFactory::getBuildById($buildId); + } catch (\Exception $ex) { + $build = null; + } + + if (empty($build)) { + throw new NotFoundException(Lang::get('build_x_not_found', $buildId)); + } + + /** @var User $user */ + $user = $_SESSION['php-censor-user']; + $perPage = $user->getFinalPerPage(); + $data = $this->getBuildData($build, $plugin, $severity, (($page - 1) * $perPage), $perPage); + $pages = ($data['errors'] === 0) + ? 1 + : (integer)ceil($data['errors'] / $perPage); + + if ($page > $pages) { + $page = $pages; + } + + /** @var \PHPCensor\Store\BuildErrorStore $errorStore */ + $errorStore = b8\Store\Factory::getStore('BuildError'); + + $this->view->uiPlugins = $this->getUiPlugins(); + $this->view->build = $build; + $this->view->data = $data; + + $this->view->plugin = urldecode($plugin); + $this->view->plugins = $errorStore->getKnownPlugins($buildId); + $this->view->severity = urldecode(null !== $severity ? $severity : ''); + $this->view->severities = $errorStore->getKnownSeverities($buildId, $plugin); + + $this->view->page = $page; + $this->view->perPage = $perPage; + $this->view->paginator = $this->getPaginatorHtml($buildId, $plugin, $severity, $data['errors'], $perPage, $page); + + $this->layout->title = Lang::get('build_n', $buildId); + $this->layout->subtitle = $build->getProjectTitle(); + + switch ($build->getStatus()) { + case 0: + $this->layout->skin = 'blue'; + break; + + case 1: + $this->layout->skin = 'yellow'; + break; + + case 2: + $this->layout->skin = 'green'; + break; + + case 3: + $this->layout->skin = 'red'; + break; + } + + $rebuild = Lang::get('rebuild_now'); + $rebuildLink = APP_URL . 'build/rebuild/' . $build->getId(); + + $delete = Lang::get('delete_build'); + $deleteLink = APP_URL . 'build/delete/' . $build->getId(); + + $project = b8\Store\Factory::getStore('Project')->getByPrimaryKey($build->getProjectId()); + + $actions = ''; + if (!$project->getArchived()) { + $actions .= "{$rebuild} "; + } + + if ($this->currentUserIsAdmin()) { + $actions .= " {$delete}"; + } + + $this->layout->actions = $actions; + } + + /** + * Returns an array of the JS plugins to include. + * @return array + */ + protected function getUiPlugins() + { + $rtn = []; + $path = PUBLIC_DIR . 'assets' . DIRECTORY_SEPARATOR . 'js' . DIRECTORY_SEPARATOR . 'build-plugins' . DIRECTORY_SEPARATOR; + $dir = opendir($path); + + while ($item = readdir($dir)) { + if (substr($item, 0, 1) == '.' || substr($item, -3) != '.js') { + continue; + } + + $rtn[] = $item; + } + + return $rtn; + } + + /** + * Get build data from database and json encode it. + * + * @param Build $build + * @param string $plugin + * @param integer $severity + * @param integer $start + * @param integer $perPage + * + * @return array + */ + protected function getBuildData(Build $build, $plugin, $severity, $start = 0, $perPage = 10) + { + $data = []; + $data['status'] = (int)$build->getStatus(); + $data['log'] = $this->cleanLog($build->getLog()); + $data['create_date'] = !is_null($build->getCreateDate()) ? $build->getCreateDate()->format('Y-m-d H:i:s') : null; + $data['start_date'] = !is_null($build->getStartDate()) ? $build->getStartDate()->format('Y-m-d H:i:s') : null; + $data['finish_date'] = !is_null($build->getFinishDate()) ? $build->getFinishDate()->format('Y-m-d H:i:s') : null; + $data['duration'] = $build->getDuration(); + + /** @var \PHPCensor\Store\BuildErrorStore $errorStore */ + $errorStore = b8\Store\Factory::getStore('BuildError'); + $errors = $errorStore->getByBuildId($build->getId(), $perPage, $start, $plugin, $severity); + + $errorView = new b8\View('Build/errors'); + $errorView->build = $build; + $errorView->errors = $errors['items']; + + $data['errors'] = $errorStore->getErrorTotalForBuild($build->getId(), $plugin, $severity); + $data['errors_total'] = $errorStore->getErrorTotalForBuild($build->getId()); + $data['error_html'] = $errorView->render(); + + return $data; + } + + /** + * @param integer $buildId + * @param string $plugin + * @param integer $severity + * @param integer $total + * @param integer $perPage + * @param integer $page + * + * @return string + */ + protected function getPaginatorHtml($buildId, $plugin, $severity, $total, $perPage, $page) + { + $view = new b8\View('pagination'); + + $urlPattern = APP_URL . 'build/view/' . $buildId; + $params = []; + if (!empty($plugin)) { + $params['plugin'] = $plugin; + } + + if (null !== $severity) { + $params['severity'] = $severity; + } + + $urlPattern = $urlPattern . '?' . str_replace('%28%3Anum%29', '(:num)', http_build_query(array_merge($params, ['page' => '(:num)']))) . '#errors'; + $paginator = new Paginator($total, $perPage, $page, $urlPattern); + + $view->paginator = $paginator; + + return $view->render(); + } + + /** + * Create a build using an existing build as a template: + */ + public function rebuild($buildId) + { + $copy = BuildFactory::getBuildById($buildId); + $project = b8\Store\Factory::getStore('Project')->getByPrimaryKey($copy->getProjectId()); + + if (empty($copy) || $project->getArchived()) { + throw new NotFoundException(Lang::get('build_x_not_found', $buildId)); + } + + $build = $this->buildService->createDuplicateBuild($copy); + + if ($this->buildService->queueError) { + $_SESSION['global_error'] = Lang::get('add_to_queue_failed'); + } + + $response = new b8\Http\Response\RedirectResponse(); + $response->setHeader('Location', APP_URL.'build/view/' . $build->getId()); + + return $response; + } + + /** + * Delete a build. + */ + public function delete($buildId) + { + $this->requireAdmin(); + + $build = BuildFactory::getBuildById($buildId); + + if (empty($build)) { + throw new NotFoundException(Lang::get('build_x_not_found', $buildId)); + } + + $this->buildService->deleteBuild($build); + + $response = new b8\Http\Response\RedirectResponse(); + $response->setHeader('Location', APP_URL.'project/view/' . $build->getProjectId()); + + return $response; + } + + /** + * Parse log for unix colours and replace with HTML. + */ + protected function cleanLog($log) + { + return AnsiConverter::convert($log); + } + + /** + * Formats a list of builds into rows suitable for the dropdowns in the header bar. + * + * @param $builds + * + * @return array + */ + protected function formatBuilds($builds) + { + Project::$sleepable = ['id', 'title', 'reference', 'type']; + + $rtn = ['count' => $builds['count'], 'items' => []]; + + foreach ($builds['items'] as $build) { + $item = $build->toArray(1); + + $header = new b8\View('Build/header-row'); + $header->build = $build; + + $item['header_row'] = $header->render(); + $rtn['items'][$item['id']] = $item; + } + + ksort($rtn['items']); + return $rtn; + } + + public function ajaxData($buildId) + { + $page = (integer)$this->getParam('page', 1); + $perPage = (integer)$this->getParam('per_page', 10); + $plugin = $this->getParam('plugin', ''); + + $severity = $this->getParam('severity', null); + if (null !== $severity && '' !== $severity) { + $severity = (integer)$severity; + } else { + $severity = null; + } + + $response = new JsonResponse(); + $build = BuildFactory::getBuildById($buildId); + + if (!$build) { + $response->setResponseCode(404); + $response->setContent([]); + + return $response; + } + + $data = $this->getBuildData($build, $plugin, $severity, (($page - 1) * $perPage), $perPage); + $data['paginator'] = $this->getPaginatorHtml($buildId, $plugin, $severity, $data['errors'], $perPage, $page); + + $response->setContent($data); + + return $response; + } + + public function ajaxMeta($buildId) + { + $build = BuildFactory::getBuildById($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, $build->getBranch(), $numBuilds); + } + + $response = new JsonResponse(); + $response->setContent($data); + + return $response; + } + + public function ajaxQueue() + { + $rtn = [ + 'pending' => $this->formatBuilds($this->buildStore->getByStatus(Build::STATUS_PENDING)), + 'running' => $this->formatBuilds($this->buildStore->getByStatus(Build::STATUS_RUNNING)), + ]; + + $response = new JsonResponse(); + $response->setContent($rtn); + + return $response; + } +} diff --git a/src/PHPCensor/Controller/BuildStatusController.php b/src/PHPCensor/Controller/BuildStatusController.php new file mode 100644 index 0000000..b7f697f --- /dev/null +++ b/src/PHPCensor/Controller/BuildStatusController.php @@ -0,0 +1,236 @@ + + */ +class BuildStatusController extends Controller +{ + /* @var \PHPCensor\Store\ProjectStore */ + protected $projectStore; + + /* @var \PHPCensor\Store\BuildStore */ + protected $buildStore; + + /** + * Initialise the controller, set up stores and services. + */ + public function init() + { + $this->response->disableLayout(); + + $this->buildStore = Store\Factory::getStore('Build'); + $this->projectStore = Store\Factory::getStore('Project'); + } + + /** + * Returns status of the last build + * + * @param $projectId + * + * @return string + */ + protected function getStatus($projectId) + { + $status = null; + $branch = $this->getParam('branch', 'master'); + + try { + $project = $this->projectStore->getById($projectId); + $status = 'passing'; + + if (isset($project) && $project instanceof Project) { + $build = $project->getLatestBuild($branch, [ + Build::STATUS_SUCCESS, + Build::STATUS_FAILED, + ]); + + if (isset($build) && $build instanceof Build && $build->getStatus() !== Build::STATUS_SUCCESS) { + $status = 'failed'; + } + } + } catch (\Exception $e) { + $status = 'error'; + } + + return $status; + } + + /** + * Displays projects information in ccmenu format + * + * @param $projectId + * + * @return bool + * + * @throws \Exception + * @throws b8\Exception\HttpException + */ + public function ccxml($projectId) + { + /* @var Project $project */ + $project = $this->projectStore->getById($projectId); + $xml = new \SimpleXMLElement(''); + + if (!$project instanceof Project || !$project->getAllowPublicStatus()) { + return $this->renderXml($xml); + } + + try { + $branchList = $this->buildStore->getBuildBranches($projectId); + + if (!$branchList) { + $branchList = [$project->getBranch()]; + } + + foreach ($branchList as $branch) { + $buildStatusService = new BuildStatusService($branch, $project, $project->getLatestBuild($branch)); + if ($attributes = $buildStatusService->toArray()) { + $projectXml = $xml->addChild('Project'); + foreach ($attributes as $attributeKey => $attributeValue) { + $projectXml->addAttribute($attributeKey, $attributeValue); + } + } + } + } catch (\Exception $e) { + $xml = new \SimpleXMLElement(''); + } + + return $this->renderXml($xml); + } + + /** + * @param \SimpleXMLElement $xml + * + * @return boolean + */ + protected function renderXml(\SimpleXMLElement $xml = null) + { + $this->response->setHeader('Content-Type', 'text/xml'); + $this->response->setContent($xml->asXML()); + $this->response->flush(); + echo $xml->asXML(); + + return true; + } + + /** + * Returns the appropriate build status image in SVG format for a given project. + * + * @param $projectId + * + * @return b8\Http\Response|b8\Http\Response\RedirectResponse + */ + public function image($projectId) + { + // plastic|flat|flat-squared|social + $style = $this->getParam('style', 'flat'); + $label = $this->getParam('label', 'build'); + + $optionalParams = [ + 'logo' => $this->getParam('logo'), + 'logoWidth' => $this->getParam('logoWidth'), + 'link' => $this->getParam('link'), + 'maxAge' => $this->getParam('maxAge'), + ]; + + $status = $this->getStatus($projectId); + + if (is_null($status)) { + $response = new b8\Http\Response\RedirectResponse(); + $response->setHeader('Location', '/'); + + return $response; + } + + $color = ($status == 'passing') ? 'green' : 'red'; + $imageUrl = sprintf( + 'http://img.shields.io/badge/%s-%s-%s.svg?style=%s', + $label, + $status, + $color, + $style + ); + + foreach ($optionalParams as $paramName => $param) { + if ($param) { + $imageUrl .= '&' . $paramName . '=' . $param; + } + } + + $cacheDir = RUNTIME_DIR . 'status_cache/'; + $cacheFile = $cacheDir . md5($imageUrl) . '.svg'; + if (!is_file($cacheFile)) { + $image = file_get_contents($imageUrl); + file_put_contents($cacheFile, $image); + } + + $image = file_get_contents($cacheFile); + + $this->response->disableLayout(); + $this->response->setHeader('Content-Type', 'image/svg+xml'); + $this->response->setContent($image); + return $this->response; + } + + /** + * View the public status page of a given project, if enabled. + * + * @param integer $projectId + * + * @return string + * + * @throws \b8\Exception\HttpException\NotFoundException + */ + public function view($projectId) + { + $project = $this->projectStore->getById($projectId); + + if (empty($project) || !$project->getAllowPublicStatus()) { + throw new NotFoundException('Project with id: ' . $projectId . ' not found'); + } + + $builds = $this->getLatestBuilds($projectId); + + if (count($builds)) { + $this->view->latest = $builds[0]; + } + + $this->view->builds = $builds; + $this->view->project = $project; + + return $this->view->render(); + } + + /** + * Render latest builds for project as HTML table. + * + * @param integer $projectId + * + * @return array + */ + protected function getLatestBuilds($projectId) + { + $criteria = ['project_id' => $projectId]; + $order = ['id' => 'DESC']; + $builds = $this->buildStore->getWhere($criteria, 10, 0, [], $order); + + foreach ($builds['items'] as &$build) { + $build = BuildFactory::getBuild($build); + } + + return $builds['items']; + } +} diff --git a/src/PHPCensor/Controller/GroupController.php b/src/PHPCensor/Controller/GroupController.php new file mode 100644 index 0000000..fc54f81 --- /dev/null +++ b/src/PHPCensor/Controller/GroupController.php @@ -0,0 +1,126 @@ + + */ +class GroupController extends Controller +{ + /** + * @var \PHPCensor\Store\ProjectGroupStore + */ + protected $groupStore; + + /** + * Set up this controller. + */ + public function init() + { + $this->groupStore = b8\Store\Factory::getStore('ProjectGroup'); + } + + /** + * List project groups. + */ + public function index() + { + $this->requireAdmin(); + + $groups = []; + $groupList = $this->groupStore->getWhere([], 100, 0, [], ['title' => 'ASC']); + + foreach ($groupList['items'] as $group) { + $thisGroup = [ + 'title' => $group->getTitle(), + 'id' => $group->getId(), + ]; + $projects_active = b8\Store\Factory::getStore('Project')->getByGroupId($group->getId(), false); + $projects_archived = b8\Store\Factory::getStore('Project')->getByGroupId($group->getId(), true); + + $thisGroup['projects'] = array_merge($projects_active['items'], $projects_archived['items']); + $groups[] = $thisGroup; + } + + $this->layout->title = Lang::get('group_projects'); + $this->view->groups = $groups; + } + + /** + * Add or edit a project group. + * @param null $groupId + * @return void|b8\Http\Response\RedirectResponse + */ + public function edit($groupId = null) + { + $this->requireAdmin(); + + if (!is_null($groupId)) { + $group = $this->groupStore->getById($groupId); + } else { + $group = new ProjectGroup(); + } + + if ($this->request->getMethod() == 'POST') { + $group->setTitle($this->getParam('title')); + if (is_null($groupId)) { + /** @var User $user */ + $user = $_SESSION['php-censor-user']; + + $group->setCreateDate(new \DateTime()); + $group->setUserId($user->getId()); + } + + $this->groupStore->save($group); + + $response = new b8\Http\Response\RedirectResponse(); + $response->setHeader('Location', APP_URL.'group'); + + return $response; + } + + $form = new Form(); + $form->setMethod('POST'); + $form->setAction(APP_URL . 'group/edit' . (!is_null($groupId) ? '/' . $groupId : '')); + + $title = new Form\Element\Text('title'); + $title->setContainerClass('form-group'); + $title->setClass('form-control'); + $title->setLabel(Lang::get('group_title')); + $title->setValue($group->getTitle()); + + $submit = new Form\Element\Submit(); + $submit->setClass('btn btn-success'); + $submit->setValue(Lang::get('group_save')); + + $form->addField($title); + $form->addField($submit); + + $this->view->form = $form; + } + + /** + * Delete a project group. + * @param $groupId + * @return b8\Http\Response\RedirectResponse + */ + public function delete($groupId) + { + $this->requireAdmin(); + $group = $this->groupStore->getById($groupId); + + $this->groupStore->delete($group); + $response = new b8\Http\Response\RedirectResponse(); + $response->setHeader('Location', APP_URL.'group'); + return $response; + } +} diff --git a/src/PHPCensor/Controller/HomeController.php b/src/PHPCensor/Controller/HomeController.php new file mode 100644 index 0000000..3cfbc2d --- /dev/null +++ b/src/PHPCensor/Controller/HomeController.php @@ -0,0 +1,42 @@ +layout->title = Lang::get('dashboard'); + + $widgets = [ + 'left' => [], + 'right' => [], + ]; + $widgets_config = b8\Config::getInstance()->get('php-censor.dashboard_widgets', [ + 'all_projects' => [ + 'side' => 'left', + ], + 'last_builds' => [ + 'side' => 'right', + ], + ]); + foreach($widgets_config as $name => $params) { + $side = (isset($params['side']) and ($params['side'] == 'right')) ? 'right' : 'left'; + $widgets[$side][$name] = $params; + } + + $this->view->widgets = $widgets; + + return $this->view->render(); + } +} diff --git a/src/PHPCensor/Controller/ProjectController.php b/src/PHPCensor/Controller/ProjectController.php new file mode 100644 index 0000000..c2b0673 --- /dev/null +++ b/src/PHPCensor/Controller/ProjectController.php @@ -0,0 +1,591 @@ + + */ +class ProjectController extends PHPCensor\Controller +{ + /** + * @var \PHPCensor\Store\ProjectStore + */ + protected $projectStore; + + /** + * @var \PHPCensor\Service\ProjectService + */ + protected $projectService; + + /** + * @var \PHPCensor\Store\BuildStore + */ + protected $buildStore; + + /** + * @var \PHPCensor\Service\BuildService + */ + protected $buildService; + + /** + * Initialise the controller, set up stores and services. + */ + public function init() + { + $this->buildStore = Store\Factory::getStore('Build'); + $this->projectStore = Store\Factory::getStore('Project'); + $this->projectService = new ProjectService($this->projectStore); + $this->buildService = new BuildService($this->buildStore); + } + + /** + * @param int $projectId + * + * @return b8\Http\Response + */ + public function ajaxBuilds($projectId) + { + $branch = $this->getParam('branch', ''); + $environment = $this->getParam('environment', ''); + $page = (integer)$this->getParam('page', 1); + $perPage = (integer)$this->getParam('per_page', 10); + $builds = $this->getLatestBuildsHtml($projectId, $branch, $environment, (($page - 1) * $perPage), $perPage); + + $this->response->disableLayout(); + $this->response->setContent($builds[0]); + + return $this->response; + } + + /** + * View a specific project. + * + * @param integer $projectId + * + * @throws NotFoundException + * + * @return string + */ + public function view($projectId) + { + $branch = $this->getParam('branch', ''); + $environment = $this->getParam('environment', ''); + $page = (integer)$this->getParam('page', 1); + $project = $this->projectStore->getById($projectId); + + if (empty($project)) { + throw new NotFoundException(Lang::get('project_x_not_found', $projectId)); + } + + /** @var PHPCensor\Model\User $user */ + $user = $_SESSION['php-censor-user']; + $perPage = $user->getFinalPerPage(); + $builds = $this->getLatestBuildsHtml($projectId, $branch, $environment, (($page - 1) * $perPage), $perPage); + $pages = ($builds[1] === 0) + ? 1 + : (integer)ceil($builds[1] / $perPage); + + if ($page > $pages) { + $page = $pages; + } + + $this->view->builds = $builds[0]; + $this->view->total = $builds[1]; + $this->view->project = $project; + $this->view->branch = urldecode($branch); + $this->view->branches = $this->projectStore->getKnownBranches($projectId); + $this->view->environment = urldecode($environment); + $this->view->environments = $project->getEnvironmentsNames(); + $this->view->page = $page; + $this->view->perPage = $perPage; + $this->view->paginator = $this->getPaginatorHtml($projectId, $branch, $environment, $builds[1], $perPage, $page); + + $this->layout->title = $project->getTitle(); + $this->layout->subtitle = ''; + + if (!empty($this->view->environment)) { + $this->layout->subtitle = ' ' . $this->view->environment; + } elseif (!empty($this->view->branch)) { + $this->layout->subtitle = ' ' . $this->view->branch; + } + + return $this->view->render(); + } + + /** + * @param integer $projectId + * @param string $branch + * @param string $environment + * @param integer $total + * @param integer $perPage + * @param integer $page + * + * @return string + */ + protected function getPaginatorHtml($projectId, $branch, $environment, $total, $perPage, $page) + { + $view = new b8\View('pagination'); + + $urlPattern = APP_URL . 'project/view/' . $projectId; + $params = []; + if (!empty($branch)) { + $params['branch'] = $branch; + } + + if (!empty($environment)) { + $params['environment'] = $environment; + } + + $urlPattern = $urlPattern . '?' . str_replace('%28%3Anum%29', '(:num)', http_build_query(array_merge($params, ['page' => '(:num)']))); + $paginator = new Paginator($total, $perPage, $page, $urlPattern); + + $view->paginator = $paginator; + + return $view->render(); + } + + /** + * Create a new pending build for a project. + * + * @param integer $projectId + * + * @throws NotFoundException + * + * @return RedirectResponse + * + */ + public function build($projectId) + { + /* @var \PHPCensor\Model\Project $project */ + $project = $this->projectStore->getById($projectId); + if (empty($project) || $project->getArchived()) { + throw new NotFoundException(Lang::get('project_x_not_found', $projectId)); + } + + $type = $this->getParam('type', 'branch'); + $id = $this->getParam('id'); + $debug = (boolean)$this->getParam('debug', false); + + $environment = null; + $branch = null; + + switch($type) { + case 'environment': + $environment = $id; + break; + case 'branch': + $branch = $id; + break; + } + + if (empty($branch)) { + $branch = $project->getBranch(); + } + + $extra = null; + + if ($debug && $this->currentUserIsAdmin()) { + $extra = [ + 'debug' => true, + ]; + } + + /** @var PHPCensor\Model\User $user */ + $user = $_SESSION['php-censor-user']; + $build = $this->buildService->createBuild( + $project, + $environment, + '', + $branch, + null, + $user->getEmail(), + null, + Build::SOURCE_MANUAL_WEB, + $user->getId(), + $extra + ); + + if ($this->buildService->queueError) { + $_SESSION['global_error'] = Lang::get('add_to_queue_failed'); + } + + $response = new RedirectResponse(); + $response->setHeader('Location', APP_URL.'build/view/' . $build->getId()); + + return $response; + } + + /** + * Delete a project. + */ + public function delete($projectId) + { + $this->requireAdmin(); + + $project = $this->projectStore->getById($projectId); + $this->projectService->deleteProject($project); + + $response = new RedirectResponse(); + $response->setHeader('Location', APP_URL); + + return $response; + } + + /** + * Render latest builds for project as HTML table. + * + * @param int $projectId + * @param string $branch A urldecoded branch name. + * @param string $environment A urldecoded environment name. + * @param int $start + * @param int $perPage + * + * @return array + */ + protected function getLatestBuildsHtml($projectId, $branch = '', $environment = '', $start = 0, $perPage = 10) + { + $criteria = ['project_id' => $projectId]; + + if (!empty($environment)) { + $criteria['environment'] = $environment; + } + + if (!empty($branch)) { + $criteria['branch'] = $branch; + } + + $order = ['id' => 'DESC']; + $builds = $this->buildStore->getWhere($criteria, $perPage, $start, [], $order); + $view = new b8\View('Project/ajax-builds'); + + foreach ($builds['items'] as &$build) { + $build = BuildFactory::getBuild($build); + } + + $view->builds = $builds['items']; + + return [ + $view->render(), + (integer)$builds['count'] + ]; + } + + /** + * Add a new project. Handles both the form, and processing. + */ + public function add() + { + $this->layout->title = Lang::get('add_project'); + $this->requireAdmin(); + + $method = $this->request->getMethod(); + $pub = null; + $values = $this->getParams(); + $values['branch'] = ''; + + if ($method != 'POST') { + $sshKey = new SshKey(); + $key = $sshKey->generate(); + + $values['key'] = $key['private_key']; + $values['pubkey'] = $key['public_key']; + $pub = $key['public_key']; + } + + $form = $this->projectForm($values); + + if ($method != 'POST' || ($method == 'POST' && !$form->validate())) { + $view = new b8\View('Project/edit'); + $view->type = 'add'; + $view->project = null; + $view->form = $form; + $view->key = $pub; + + return $view->render(); + } else { + $title = $this->getParam('title', 'New Project'); + $reference = $this->getParam('reference', null); + $type = $this->getParam('type', null); + + $options = [ + 'ssh_private_key' => $this->getParam('key', null), + 'ssh_public_key' => $this->getParam('pubkey', null), + 'build_config' => $this->getParam('build_config', null), + 'allow_public_status' => $this->getParam('allow_public_status', 0), + 'branch' => $this->getParam('branch', null), + 'default_branch_only' => $this->getParam('default_branch_only', 0), + 'group' => $this->getParam('group_id', null), + 'environments' => $this->getParam('environments', null), + ]; + + /** @var PHPCensor\Model\User $user */ + $user = $_SESSION['php-censor-user']; + $project = $this->projectService->createProject($title, $type, $reference, $user->getId(), $options); + + $response = new RedirectResponse(); + $response->setHeader('Location', APP_URL.'project/view/' . $project->getId()); + + return $response; + } + } + + /** + * Edit a project. Handles both the form and processing. + */ + public function edit($projectId) + { + $this->requireAdmin(); + + $method = $this->request->getMethod(); + $project = $this->projectStore->getById($projectId); + + if (empty($project)) { + throw new NotFoundException(Lang::get('project_x_not_found', $projectId)); + } + + $this->layout->title = $project->getTitle(); + $this->layout->subtitle = Lang::get('edit_project'); + + $values = $project->getDataArray(); + $values['key'] = $values['ssh_private_key']; + $values['pubkey'] = $values['ssh_public_key']; + $values['environments'] = $project->getEnvironments(); + + if ($values['type'] == 'gitlab') { + $accessInfo = $project->getAccessInformation(); + $reference = $accessInfo["user"] . '@' . $accessInfo["domain"] . ':' . $accessInfo["port"] . '/' . ltrim($project->getReference(), '/') . ".git"; + $values['reference'] = $reference; + } + + if ($method == 'POST') { + $values = $this->getParams(); + } + + $form = $this->projectForm($values, 'edit/' . $projectId); + + if ($method != 'POST' || ($method == 'POST' && !$form->validate())) { + $view = new b8\View('Project/edit'); + $view->type = 'edit'; + $view->project = $project; + $view->form = $form; + $view->key = $values['pubkey']; + + return $view->render(); + } + + $title = $this->getParam('title', Lang::get('new_project')); + $reference = $this->getParam('reference', null); + $type = $this->getParam('type', null); + + $options = [ + 'ssh_private_key' => $this->getParam('key', null), + 'ssh_public_key' => $this->getParam('pubkey', null), + 'build_config' => $this->getParam('build_config', null), + 'allow_public_status' => $this->getParam('allow_public_status', 0), + 'archived' => $this->getParam('archived', 0), + 'branch' => $this->getParam('branch', null), + 'default_branch_only' => $this->getParam('default_branch_only', 0), + 'group' => $this->getParam('group_id', null), + 'environments' => $this->getParam('environments', null), + ]; + + $project = $this->projectService->updateProject($project, $title, $type, $reference, $options); + + $response = new RedirectResponse(); + $response->setHeader('Location', APP_URL.'project/view/' . $project->getId()); + + return $response; + } + + /** + * Create add / edit project form. + */ + protected function projectForm($values, $type = 'add') + { + $form = new Form(); + + $form->setMethod('POST'); + $form->setAction(APP_URL.'project/' . $type); + + $form->addField(new Form\Element\Csrf('csrf')); + $form->addField(new Form\Element\Hidden('pubkey')); + + $options = [ + 'choose' => Lang::get('select_repository_type'), + 'github' => 'GitHub', + 'bitbucket' => 'Bitbucket (Git)', + 'bitbuckethg' => 'Bitbucket (Hg)', + 'gitlab' => 'GitLab', + 'gogs' => 'Gogs', + 'remote' => 'Git', + 'local' => Lang::get('local'), + 'hg' => 'Mercurial (Hg)', + 'svn' => 'SVN', + ]; + + $field = Form\Element\Select::create('type', Lang::get('where_hosted'), true); + $field->setPattern('^(github|bitbucket|bitbuckethg|gitlab|gogs|remote|local|hg|svn)'); + $field->setOptions($options); + $field->setClass('form-control')->setContainerClass('form-group'); + $form->addField($field); + + $container = new Form\ControlGroup('github-container'); + $container->setClass('github-container'); + + $field = Form\Element\Select::create('github', Lang::get('choose_github'), false); + $field->setClass('form-control')->setContainerClass('form-group'); + $container->addField($field); + $form->addField($container); + + $field = Form\Element\Text::create('reference', Lang::get('repo_name'), true); + $field->setValidator($this->getReferenceValidator($values)); + $field->setClass('form-control')->setContainerClass('form-group'); + $form->addField($field); + + $field = Form\Element\Text::create('title', Lang::get('project_title'), true); + $field->setClass('form-control')->setContainerClass('form-group'); + $form->addField($field); + + $field = Form\Element\Text::create('branch', Lang::get('default_branch'), false); + $field->setClass('form-control')->setContainerClass('form-group')->setValue(''); + $form->addField($field); + + $field = Form\Element\Checkbox::create( + 'default_branch_only', + Lang::get('default_branch_only'), + false + ); + $field->setContainerClass('form-group'); + $field->setCheckedValue(1); + $field->setValue(0); + $form->addField($field); + + $field = Form\Element\TextArea::create('key', Lang::get('project_private_key'), false); + $field->setClass('form-control')->setContainerClass('form-group'); + $field->setRows(6); + $form->addField($field); + + $field = Form\Element\TextArea::create('build_config', Lang::get('build_config'), false); + $field->setClass('form-control')->setContainerClass('form-group'); + $field->setRows(6); + $form->addField($field); + + $field = Form\Element\TextArea::create('environments', Lang::get('environments_label'), false); + $field->setClass('form-control')->setContainerClass('form-group'); + $field->setRows(6); + $form->addField($field); + + $field = Form\Element\Select::create('group_id', Lang::get('project_group'), true); + $field->setClass('form-control')->setContainerClass('form-group')->setValue(1); + + $groups = []; + $groupStore = b8\Store\Factory::getStore('ProjectGroup'); + $groupList = $groupStore->getWhere([], 100, 0, [], ['title' => 'ASC']); + + foreach ($groupList['items'] as $group) { + $groups[$group->getId()] = $group->getTitle(); + } + + $field->setOptions($groups); + $form->addField($field); + + $field = Form\Element\Checkbox::create('allow_public_status', Lang::get('allow_public_status'), false); + $field->setContainerClass('form-group'); + $field->setCheckedValue(1); + $field->setValue(0); + $form->addField($field); + + $field = Form\Element\Checkbox::create('archived', Lang::get('archived'), false); + $field->setContainerClass('form-group'); + $field->setCheckedValue(1); + $field->setValue(0); + $form->addField($field); + + $field = new Form\Element\Submit(); + $field->setValue(Lang::get('save_project')); + $field->setContainerClass('form-group'); + $field->setClass('btn-success'); + $form->addField($field); + + $form->setValues($values); + + return $form; + } + + /** + * Get the validator to use to check project references. + * @param $values + * @return callable + */ + protected function getReferenceValidator($values) + { + return function ($val) use ($values) { + $type = $values['type']; + + $validators = [ + 'hg' => [ + 'regex' => '/^(ssh|https?):\/\//', + 'message' => Lang::get('error_mercurial') + ], + 'remote' => [ + 'regex' => '/^(git|https?):\/\//', + 'message' => Lang::get('error_remote') + ], + 'gitlab' => [ + 'regex' => '`^(.*)@(.*):(.*)/(.*)\.git`', + 'message' => Lang::get('error_gitlab') + ], + 'github' => [ + 'regex' => '/^[a-zA-Z0-9_\-]+\/[a-zA-Z0-9_\-\.]+$/', + 'message' => Lang::get('error_github') + ], + 'bitbucket' => [ + 'regex' => '/^[a-zA-Z0-9_\-]+\/[a-zA-Z0-9_\-\.]+$/', + 'message' => Lang::get('error_bitbucket') + ], + 'bitbuckethg' => [ + 'regex' => '/^[a-zA-Z0-9_\-]+\/[a-zA-Z0-9_\-\.]+$/', + 'message' => Lang::get('error_bitbucket') + ], + ]; + + 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(Lang::get('error_path')); + } + + return true; + }; + } + + /** + * Get an array of repositories from Github's API. + */ + public function ajaxGithubRepositories() + { + $github = new Github(); + + $response = new b8\Http\Response\JsonResponse(); + $response->setContent($github->getRepositories()); + + return $response; + } +} diff --git a/src/PHPCensor/Controller/SessionController.php b/src/PHPCensor/Controller/SessionController.php new file mode 100644 index 0000000..fb60230 --- /dev/null +++ b/src/PHPCensor/Controller/SessionController.php @@ -0,0 +1,281 @@ + + */ +class SessionController extends Controller +{ + /** + * @var UserStore + */ + protected $userStore; + + /** + * @var Service + */ + protected $authentication; + + /** + * Initialise the controller, set up stores and services. + */ + public function init() + { + $this->response->disableLayout(); + + $this->userStore = b8\Store\Factory::getStore('User'); + $this->authentication = Service::getInstance(); + } + + /** + * Handles user login (form and processing) + */ + public function login() + { + if (!empty($_COOKIE['remember_key'])) { + $user = $this->userStore->getByRememberKey($_COOKIE['remember_key']); + if ($user) { + $_SESSION['php-censor-user-id'] = $user->getId(); + + $response = new b8\Http\Response\RedirectResponse(); + $response->setHeader('Location', $this->getLoginRedirect()); + + return $response; + } + } + + $isLoginFailure = false; + + if ($this->request->getMethod() == 'POST') { + $token = $this->getParam('token'); + if (!isset($token, $_SESSION['login_token']) || $token !== $_SESSION['login_token']) { + $isLoginFailure = true; + } else { + unset($_SESSION['login_token']); + + $email = $this->getParam('email'); + $password = $this->getParam('password', ''); + $rememberMe = (bool)$this->getParam('remember_me', 0); + $isLoginFailure = true; + + $user = $this->userStore->getByEmailOrName($email); + $providers = $this->authentication->getLoginPasswordProviders(); + + if (null !== $user) { + // Delegate password verification to the user provider, if found + $key = $user->getProviderKey(); + $isLoginFailure = !isset($providers[$key]) || !$providers[$key]->verifyPassword($user, $password); + } else { + // Ask each providers to provision the user + foreach ($providers as $provider) { + $user = $provider->provisionUser($email); + if ($user && $provider->verifyPassword($user, $password)) { + $this->userStore->save($user); + $isLoginFailure = false; + break; + } + } + } + + if (!$isLoginFailure) { + $_SESSION['php-censor-user-id'] = $user->getId(); + + if ($rememberMe) { + $rememberKey = md5(microtime(true)); + + $user->setRememberKey($rememberKey); + $this->userStore->save($user); + + setcookie( + 'remember_key', + $rememberKey, + (time() + 60 * 60 * 24 * 30), + null, + null, + null, + true + ); + } + + $response = new b8\Http\Response\RedirectResponse(); + $response->setHeader('Location', $this->getLoginRedirect()); + + return $response; + } + } + } + + $form = new b8\Form(); + $form->setMethod('POST'); + $form->setAction(APP_URL . 'session/login'); + + $email = new b8\Form\Element\Text('email'); + $email->setLabel(Lang::get('login')); + $email->setRequired(true); + $email->setContainerClass('form-group'); + $email->setClass('form-control'); + $form->addField($email); + + $pwd = new b8\Form\Element\Password('password'); + $pwd->setLabel(Lang::get('password')); + $pwd->setRequired(true); + $pwd->setContainerClass('form-group'); + $pwd->setClass('form-control'); + $form->addField($pwd); + + $remember = b8\Form\Element\Checkbox::create('remember_me', Lang::get('remember_me'), false); + $remember->setContainerClass('form-group'); + $remember->setCheckedValue(1); + $remember->setValue(0); + $form->addField($remember); + + $pwd = new b8\Form\Element\Submit(); + $pwd->setValue(Lang::get('log_in')); + $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(); + } + + /** + * Handles user logout. + */ + public function logout() + { + unset($_SESSION['php-censor-user']); + unset($_SESSION['php-censor-user-id']); + + session_destroy(); + + setcookie( + 'remember_key', + null, + (time() - 1), + null, + null, + null, + true + ); + + $response = new b8\Http\Response\RedirectResponse(); + $response->setHeader('Location', APP_URL); + return $response; + } + + /** + * Allows the user to request a password reset email. + * @return string + */ + public function forgotPassword() + { + if ($this->request->getMethod() == 'POST') { + $email = $this->getParam('email', null); + $user = $this->userStore->getByEmail($email); + + if (empty($user)) { + $this->view->error = Lang::get('reset_no_user_exists'); + return $this->view->render(); + } + + $key = md5(date('Y-m-d') . $user->getHash()); + $url = APP_URL; + + $message = Lang::get('reset_email_body', $user->getName(), $url, $user->getId(), $key); + + $email = new Email(); + $email->setEmailTo($user->getEmail(), $user->getName()); + $email->setSubject(Lang::get('reset_email_title', $user->getName())); + $email->setBody($message); + $email->send(); + + $this->view->emailed = true; + } + + return $this->view->render(); + } + + /** + * Allows the user to change their password after a password reset email. + * @param $userId + * @param $key + * @return string + */ + public function resetPassword($userId, $key) + { + $user = $this->userStore->getById($userId); + $userKey = md5(date('Y-m-d') . $user->getHash()); + + if (empty($user) || $key != $userKey) { + $this->view->error = Lang::get('reset_invalid'); + return $this->view->render(); + } + + if ($this->request->getMethod() == 'POST') { + $hash = password_hash($this->getParam('password'), PASSWORD_DEFAULT); + $user->setHash($hash); + + $_SESSION['php-censor-user'] = $this->userStore->save($user); + $_SESSION['php-censor-user-id'] = $user->getId(); + + $response = new b8\Http\Response\RedirectResponse(); + $response->setHeader('Location', APP_URL); + return $response; + } + + $this->view->id = $userId; + $this->view->key = $key; + + return $this->view->render(); + } + + /** + * Get the URL the user was trying to go to prior to being asked to log in. + * @return string + */ + protected function getLoginRedirect() + { + $rtn = APP_URL; + + if (!empty($_SESSION['php-censor-login-redirect'])) { + $rtn .= $_SESSION['php-censor-login-redirect']; + $_SESSION['php-censor-login-redirect'] = null; + } + + 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)); + } +} diff --git a/src/PHPCensor/Controller/UserController.php b/src/PHPCensor/Controller/UserController.php new file mode 100644 index 0000000..551a65d --- /dev/null +++ b/src/PHPCensor/Controller/UserController.php @@ -0,0 +1,304 @@ + + */ +class UserController extends Controller +{ + /** + * @var \PHPCensor\Store\UserStore + */ + protected $userStore; + + /** + * @var \PHPCensor\Service\UserService + */ + protected $userService; + + /** + * Initialise the controller, set up stores and services. + */ + public function init() + { + $this->userStore = b8\Store\Factory::getStore('User'); + $this->userService = new UserService($this->userStore); + } + + /** + * View user list. + */ + public function index() + { + $users = $this->userStore->getWhere([], 1000, 0, [], ['email' => 'ASC']); + $this->view->users = $users; + $this->layout->title = Lang::get('manage_users'); + + return $this->view->render(); + } + + /** + * Allows the user to edit their profile. + * @return string + */ + public function profile() + { + /** @var User $user */ + $user = $_SESSION['php-censor-user']; + + if ($this->request->getMethod() == 'POST') { + $name = $this->getParam('name', null); + $email = $this->getParam('email', null); + $password = $this->getParam('password', null); + + $language = $this->getParam('language', null); + if (!$language) { + $language = null; + } + + $perPage = $this->getParam('per_page', null); + if (!$perPage) { + $perPage = null; + } + + $_SESSION['php-censor-user'] = $this->userService->updateUser($user, $name, $email, $password, null, $language, $perPage); + $user = $_SESSION['php-censor-user']; + + $this->view->updated = 1; + } + + $this->layout->title = $user->getName(); + $this->layout->subtitle = Lang::get('edit_profile'); + + $form = new Form(); + $form->setAction(APP_URL.'user/profile'); + $form->setMethod('POST'); + + $name = new Form\Element\Text('name'); + $name->setClass('form-control'); + $name->setContainerClass('form-group'); + $name->setLabel(Lang::get('name')); + $name->setRequired(true); + $name->setValue($user->getName()); + $form->addField($name); + + $email = new Form\Element\Email('email'); + $email->setClass('form-control'); + $email->setContainerClass('form-group'); + $email->setLabel(Lang::get('email_address')); + $email->setRequired(true); + $email->setValue($user->getEmail()); + $form->addField($email); + + $password = new Form\Element\Password('password'); + $password->setClass('form-control'); + $password->setContainerClass('form-group'); + $password->setLabel(Lang::get('password_change')); + $password->setRequired(false); + $password->setValue(null); + $form->addField($password); + + $language = new Form\Element\Select('language'); + $language->setClass('form-control'); + $language->setContainerClass('form-group'); + $language->setLabel(Lang::get('language')); + $language->setRequired(true); + $language->setOptions(array_merge( + [null => Lang::get('default') . ' (' . b8\Config::getInstance()->get('php-censor.language') . ')'], + Lang::getLanguageOptions()) + ); + $language->setValue($user->getLanguage()); + $form->addField($language); + + $perPage = new Form\Element\Select('per_page'); + $perPage->setClass('form-control'); + $perPage->setContainerClass('form-group'); + $perPage->setLabel(Lang::get('per_page')); + $perPage->setRequired(true); + $perPage->setOptions([ + null => Lang::get('default') . ' (' . b8\Config::getInstance()->get('php-censor.per_page') . ')', + 10 => 10, + 25 => 25, + 50 => 50, + 100 => 100, + ]); + $perPage->setValue($user->getPerPage()); + $form->addField($perPage); + + $submit = new Form\Element\Submit(); + $submit->setClass('btn btn-success'); + $submit->setValue(Lang::get('save')); + $form->addField($submit); + + $this->view->form = $form; + + return $this->view->render(); + } + + /** + * Add a user - handles both form and processing. + */ + public function add() + { + $this->requireAdmin(); + + $this->layout->title = Lang::get('add_user'); + + $method = $this->request->getMethod(); + + if ($method == 'POST') { + $values = $this->getParams(); + } else { + $values = []; + } + + $form = $this->userForm($values); + + if ($method != 'POST' || ($method == 'POST' && !$form->validate())) { + $view = new b8\View('User/edit'); + $view->type = 'add'; + $view->user = null; + $view->form = $form; + + return $view->render(); + } + + + $name = $this->getParam('name', null); + $email = $this->getParam('email', null); + $password = $this->getParam('password', null); + $isAdmin = (int)$this->getParam('is_admin', 0); + + $this->userService->createUser($name, $email, 'internal', json_encode(['type' => 'internal']), $password, $isAdmin); + + $response = new b8\Http\Response\RedirectResponse(); + $response->setHeader('Location', APP_URL . 'user'); + return $response; + } + + /** + * Edit a user - handles both form and processing. + */ + public function edit($userId) + { + $this->requireAdmin(); + + $method = $this->request->getMethod(); + $user = $this->userStore->getById($userId); + + if (empty($user)) { + throw new NotFoundException(Lang::get('user_n_not_found', $userId)); + } + + $this->layout->title = $user->getName(); + $this->layout->subtitle = Lang::get('edit_user'); + + $values = array_merge($user->getDataArray(), $this->getParams()); + $form = $this->userForm($values, 'edit/' . $userId); + + if ($method != 'POST' || ($method == 'POST' && !$form->validate())) { + $view = new b8\View('User/edit'); + $view->type = 'edit'; + $view->user = $user; + $view->form = $form; + + return $view->render(); + } + + $name = $this->getParam('name', null); + $email = $this->getParam('email', null); + $password = $this->getParam('password', null); + $isAdmin = (int)$this->getParam('is_admin', 0); + + $this->userService->updateUser($user, $name, $email, $password, $isAdmin); + + $response = new b8\Http\Response\RedirectResponse(); + $response->setHeader('Location', APP_URL . 'user'); + return $response; + } + + /** + * Create user add / edit form. + */ + protected function userForm($values, $type = 'add') + { + $form = new Form(); + $form->setMethod('POST'); + $form->setAction(APP_URL.'user/' . $type); + $form->addField(new Form\Element\Csrf('csrf')); + + $field = new Form\Element\Email('email'); + $field->setRequired(true); + $field->setLabel(Lang::get('email_address')); + $field->setClass('form-control'); + $field->setContainerClass('form-group'); + $form->addField($field); + + $field = new Form\Element\Text('name'); + $field->setRequired(true); + $field->setLabel(Lang::get('name')); + $field->setClass('form-control'); + $field->setContainerClass('form-group'); + $form->addField($field); + + $field = new Form\Element\Password('password'); + + if ($type == 'add') { + $field->setRequired(true); + $field->setLabel(Lang::get('password')); + } else { + $field->setRequired(false); + $field->setLabel(Lang::get('password_change')); + } + + $field->setClass('form-control'); + $field->setContainerClass('form-group'); + $form->addField($field); + + $field = new Form\Element\Checkbox('is_admin'); + $field->setRequired(false); + $field->setCheckedValue(1); + $field->setLabel(Lang::get('is_user_admin')); + $field->setContainerClass('form-group'); + $form->addField($field); + + $field = new Form\Element\Submit(); + $field->setValue(Lang::get('save_user')); + $field->setClass('btn-success'); + $form->addField($field); + + $form->setValues($values); + return $form; + } + + /** + * Delete a user. + */ + public function delete($userId) + { + $this->requireAdmin(); + + $user = $this->userStore->getById($userId); + + if (empty($user)) { + throw new NotFoundException(Lang::get('user_n_not_found', $userId)); + } + + $this->userService->deleteUser($user); + + $response = new b8\Http\Response\RedirectResponse(); + $response->setHeader('Location', APP_URL . 'user'); + return $response; + } +} diff --git a/src/PHPCensor/Controller/WebhookController.php b/src/PHPCensor/Controller/WebhookController.php new file mode 100644 index 0000000..3ff9cdf --- /dev/null +++ b/src/PHPCensor/Controller/WebhookController.php @@ -0,0 +1,760 @@ + + * @author Sami Tikka + * @author Alex Russell + * @author Guillaume Perréal + * + */ +class WebhookController extends Controller +{ + /** + * @var BuildStore + */ + protected $buildStore; + + /** + * @var ProjectStore + */ + protected $projectStore; + + /** + * @var BuildService + */ + protected $buildService; + + /** + * Initialise the controller, set up stores and services. + */ + public function init() + { + $this->buildStore = Store\Factory::getStore('Build'); + $this->projectStore = Store\Factory::getStore('Project'); + $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(['status' => 'failed', 'error' => $ex->getMessage()]); + } + return $response; + } + + /** + * Called by Bitbucket. + */ + public function bitbucket($projectId) + { + $project = $this->fetchProject($projectId, ['bitbucket', 'bitbuckethg', 'remote']); + + // Support both old services and new webhooks + if ($payload = $this->getParam('payload')) { + return $this->bitbucketService(json_decode($payload, true), $project); + } + + $payload = json_decode(file_get_contents("php://input"), true); + + // Handle Pull Request webhooks: + if (!empty($payload['pullrequest'])) { + return $this->bitbucketPullRequest($project, $payload); + } + + // Handle Push (and Tag) webhooks: + if (!empty($payload['push']['changes'])) { + return $this->bitbucketCommitRequest($project, $payload); + } + + // Invalid event from bitbucket + return [ + 'status' => 'failed', + 'commits' => [] + ]; + } + + /** + * Handle the payload when Bitbucket sends a commit webhook. + * + * @param Project $project + * @param array $payload + * + * @return array + */ + protected function bitbucketCommitRequest(Project $project, array $payload) + { + $results = []; + $status = 'failed'; + foreach ($payload['push']['changes'] as $commit) { + try { + $email = $commit['new']['target']['author']['raw']; + if (strpos($email, '>') !== false) { + // In order not to loose email if it is RAW, w/o "<>" symbols + $email = substr($email, 0, strpos($email, '>')); + $email = substr($email, strpos($email, '<') + 1); + } + + $results[$commit['new']['target']['hash']] = $this->createBuild( + $project, + $commit['new']['target']['hash'], + $commit['new']['name'], + null, + $email, + $commit['new']['target']['message'] + ); + $status = 'ok'; + } catch (Exception $ex) { + $results[$commit['new']['target']['hash']] = ['status' => 'failed', 'error' => $ex->getMessage()]; + } + } + + return ['status' => $status, 'commits' => $results]; + } + + /** + * Handle the payload when Bitbucket sends a Pull Request webhook. + * + * @param Project $project + * @param array $payload + * + * @return array + * + * @throws Exception + */ + protected function bitbucketPullRequest(Project $project, array $payload) + { + // We only want to know about open pull requests: + if (!in_array($_SERVER['HTTP_X_EVENT_KEY'], ['pullrequest:created', 'pullrequest:updated'])) { + return ['status' => 'ok']; + } + + $headers = []; + $username = Config::getInstance()->get('php-censor.bitbucket.username'); + $appPassword = Config::getInstance()->get('php-censor.bitbucket.app_password'); + + if (empty($username) || empty($appPassword)) { + throw new Exception('Please provide Username and App Password of your Bitbucket account.'); + } + + $commitsUrl = $payload['pullrequest']['links']['commits']['href']; + + $client = new Client(); + $commitsResponse = $client->get($commitsUrl, [ + 'auth' => [$username, $appPassword], + ]); + $httpStatus = (integer)$commitsResponse->getStatusCode(); + + // Check we got a success response: + if ($httpStatus < 200 || $httpStatus >= 300) { + throw new Exception('Could not get commits, failed API request.'); + } + + $results = []; + $status = 'failed'; + $commits = json_decode($commitsResponse->getBody(), true)['values']; + foreach ($commits as $commit) { + // Skip all but the current HEAD commit ID: + $id = $commit['hash']; + if (strpos($id, $payload['pullrequest']['source']['commit']['hash']) !== 0) { + $results[$id] = ['status' => 'ignored', 'message' => 'not branch head']; + continue; + } + + try { + $branch = $payload['pullrequest']['destination']['branch']['name']; + $committer = $commit['author']['raw']; + if (strpos($committer, '>') !== false) { + // In order not to loose email if it is RAW, w/o "<>" symbols + $committer = substr($committer, 0, strpos($committer, '>')); + $committer = substr($committer, strpos($committer, '<') + 1); + } + $message = $commit['message']; + + $extra = [ + 'build_type' => 'pull_request', + 'pull_request_number' => $payload['pullrequest']['id'], + 'remote_branch' => $payload['pullrequest']['source']['branch']['name'], + 'remote_reference' => $payload['pullrequest']['source']['repository']['full_name'], + ]; + + $results[$id] = $this->createBuild($project, $id, $branch, null, $committer, $message, $extra); + $status = 'ok'; + } catch (Exception $ex) { + $results[$id] = ['status' => 'failed', 'error' => $ex->getMessage()]; + } + } + + return ['status' => $status, 'commits' => $results]; + } + + /** + * Bitbucket webhooks. + * + * @deprecated, for BC purpose + */ + protected function bitbucketWebhook($payload, $project) + { + return $this->bitbucketCommitRequest($project, $payload); + } + + /** + * Bitbucket POST service. + */ + protected function bitbucketService($payload, $project) + { + $payload = json_decode($this->getParam('payload'), true); + + $results = []; + $status = 'failed'; + foreach ($payload['commits'] as $commit) { + try { + $email = $commit['raw_author']; + $email = substr($email, 0, strpos($email, '>')); + $email = substr($email, strpos($email, '<') + 1); + + $results[$commit['raw_node']] = $this->createBuild( + $project, + $commit['raw_node'], + $commit['branch'], + null, + $email, + $commit['message'] + ); + $status = 'ok'; + } catch (Exception $ex) { + $results[$commit['raw_node']] = ['status' => 'failed', 'error' => $ex->getMessage()]; + } + } + + return ['status' => $status, 'commits' => $results]; + } + + /** + * Called by POSTing to /webhook/git/?branch=&commit= + * + * @param string $projectId + * + * @return array + */ + public function git($projectId) + { + $project = $this->fetchProject($projectId, ['local', 'remote']); + $branch = $this->getParam('branch', $project->getBranch()); + $commit = $this->getParam('commit'); + $commitMessage = $this->getParam('message'); + $committer = $this->getParam('committer'); + + return $this->createBuild($project, $commit, $branch, null, $committer, $commitMessage); + } + + /** + * Called by Github Webhooks: + */ + public function github($projectId) + { + $project = $this->fetchProject($projectId, ['github', 'remote']); + + 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: + return [ + 'status' => 'failed', + 'error' => 'Content type not supported.', + 'responseCode' => 401 + ]; + } + + // Handle Pull Request webhooks: + if (array_key_exists('pull_request', $payload)) { + return $this->githubPullRequest($project, $payload); + } + + // Handle Push (and Tag) webhooks: + if (array_key_exists('head_commit', $payload)) { + return $this->githubCommitRequest($project, $payload); + } + + return ['status' => 'ignored', 'message' => 'Unusable payload.']; + } + + /** + * Handle the payload when Github sends a commit webhook. + * + * @param Project $project + * @param array $payload + * + * @return array + */ + protected function githubCommitRequest(Project $project, array $payload) + { + // Github sends a payload when you close a pull request with a non-existent commit. We don't want this. + if ( + array_key_exists('after', $payload) && + $payload['after'] === '0000000000000000000000000000000000000000' + ) { + return ['status' => 'ignored']; + } + + if (isset($payload['head_commit']) && $payload['head_commit']) { + $isTag = (substr($payload['ref'], 0, 10) == 'refs/tags/') ? true : false; + $commit = $payload['head_commit']; + $results = []; + $status = 'failed'; + + if (!$commit['distinct']) { + $results[$commit['id']] = ['status' => 'ignored']; + } else { + try { + $tag = null; + if ($isTag) { + $tag = str_replace('refs/tags/', '', $payload['ref']); + $branch = str_replace('refs/heads/', '', $payload['base_ref']); + $committer = $payload['pusher']['email']; + } else { + $branch = str_replace('refs/heads/', '', $payload['ref']); + $committer = $commit['committer']['email']; + } + + $results[$commit['id']] = $this->createBuild( + $project, + $commit['id'], + $branch, + $tag, + $committer, + $commit['message'] + ); + + $status = 'ok'; + } catch (Exception $ex) { + $results[$commit['id']] = ['status' => 'failed', 'error' => $ex->getMessage()]; + } + } + + return ['status' => $status, 'commits' => $results]; + } + + return ['status' => 'ignored', 'message' => 'Unusable payload.']; + } + + /** + * Handle the payload when Github sends a Pull Request webhook. + * + * @param Project $project + * @param array $payload + * + * @return array + * + * @throws Exception + */ + protected function githubPullRequest(Project $project, array $payload) + { + // We only want to know about open pull requests: + if (!in_array($payload['action'], ['opened', 'synchronize', 'reopened'])) { + return ['status' => 'ok']; + } + + $headers = []; + $token = Config::getInstance()->get('php-censor.github.token'); + + if (!empty($token)) { + $headers['Authorization'] = 'token ' . $token; + } + + $url = $payload['pull_request']['commits_url']; + + //for large pull requests, allow grabbing more then the default number of commits + $custom_per_page = Config::getInstance()->get('php-censor.github.per_page'); + $params = []; + if ($custom_per_page) { + $params['per_page'] = $custom_per_page; + } + + $client = new Client(); + $response = $client->get($url, [ + 'headers' => $headers, + 'query' => $params, + ]); + $status = (integer)$response->getStatusCode(); + + // Check we got a success response: + if ($status < 200 || $status >= 300) { + throw new Exception('Could not get commits, failed API request.'); + } + + $results = []; + $status = 'failed'; + $commits = json_decode($response->getBody(), true); + foreach ($commits as $commit) { + // Skip all but the current HEAD commit ID: + $id = $commit['sha']; + if ($id != $payload['pull_request']['head']['sha']) { + $results[$id] = ['status' => 'ignored', 'message' => 'not branch head']; + continue; + } + + try { + $branch = str_replace('refs/heads/', '', $payload['pull_request']['base']['ref']); + $committer = $commit['commit']['author']['email']; + $message = $commit['commit']['message']; + + $remoteUrlKey = $payload['pull_request']['head']['repo']['private'] ? 'ssh_url' : 'clone_url'; + + $extra = [ + 'build_type' => 'pull_request', + 'pull_request_id' => $payload['pull_request']['id'], + 'pull_request_number' => $payload['number'], + 'remote_branch' => $payload['pull_request']['head']['ref'], + 'remote_url' => $payload['pull_request']['head']['repo'][$remoteUrlKey], + ]; + + $results[$id] = $this->createBuild($project, $id, $branch, null, $committer, $message, $extra); + $status = 'ok'; + } catch (Exception $ex) { + $results[$id] = ['status' => 'failed', 'error' => $ex->getMessage()]; + } + } + + return ['status' => $status, 'commits' => $results]; + } + + /** + * Called by Gitlab Webhooks: + */ + public function gitlab($projectId) + { + $project = $this->fetchProject($projectId, ['gitlab', 'remote']); + + $payloadString = file_get_contents("php://input"); + $payload = json_decode($payloadString, true); + + // 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']; + + return $this->createBuild($project, $commit['id'], $branch, null, $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: + + $results = []; + $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, + null, + $committer, + $commit['message'] + ); + $status = 'ok'; + } catch (Exception $ex) { + $results[$commit['id']] = ['status' => 'failed', 'error' => $ex->getMessage()]; + } + } + return ['status' => $status, 'commits' => $results]; + } + + return ['status' => 'ignored', 'message' => 'Unusable payload.']; + } + + + /** + * Called by POSTing to /webhook/svn/?branch=&commit= + * + * @author Sylvain Lévesque + * + * @param string $projectId + * + * @return array + */ + public function svn($projectId) + { + $project = $this->fetchProject($projectId, 'svn'); + $branch = $this->getParam('branch', $project->getBranch()); + $commit = $this->getParam('commit'); + $commitMessage = $this->getParam('message'); + $committer = $this->getParam('committer'); + + return $this->createBuild($project, $commit, $branch, null, $committer, $commitMessage); + } + + /** + * Called by Gogs Webhooks: + * + * @param string $projectId + * + * @return array + */ + public function gogs($projectId) + { + $project = $this->fetchProject($projectId, ['gogs', 'remote']); + 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: + return ['status' => 'failed', 'error' => 'Content type not supported.', 'responseCode' => 401]; + } + + // Handle Push web hooks: + if (array_key_exists('commits', $payload)) { + return $this->gogsCommitRequest($project, $payload); + } + + return ['status' => 'ignored', 'message' => 'Unusable payload.']; + } + + /** + * Handle the payload when Gogs sends a commit webhook. + * + * @param Project $project + * @param array $payload + * + * @return array + */ + protected function gogsCommitRequest(Project $project, array $payload) + { + 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 = []; + $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, + null, + $committer, + $commit['message'] + ); + $status = 'ok'; + } catch (Exception $ex) { + $results[$commit['id']] = ['status' => 'failed', 'error' => $ex->getMessage()]; + } + } + + return ['status' => $status, 'commits' => $results]; + } + + return ['status' => 'ignored', 'message' => 'Unusable payload.']; + } + + /** + * Wrapper for creating a new build. + * + * @param Project $project + * @param string $commitId + * @param string $branch + * @param string $tag + * @param string $committer + * @param string $commitMessage + * @param array $extra + * + * @return array + * + * @throws Exception + */ + protected function createBuild( + Project $project, + $commitId, + $branch, + $tag, + $committer, + $commitMessage, + array $extra = null + ) { + if ($project->getArchived()) { + throw new NotFoundException(Lang::get('project_x_not_found', $project->getId())); + } + + // Check if a build already exists for this commit ID: + $builds = $this->buildStore->getByProjectAndCommit($project->getId(), $commitId); + + $ignore_environments = []; + $ignore_tags = []; + if ($builds['count']) { + foreach($builds['items'] as $build) { + /** @var Build $build */ + $ignore_environments[$build->getId()] = $build->getEnvironment(); + $ignore_tags[$build->getId()] = $build->getTag(); + } + } + + // Check if this branch is to be built. + if ($project->getDefaultBranchOnly() && ($branch !== $project->getBranch())) { + return [ + 'status' => 'ignored', + 'message' => 'The branch is not a branch by default. Build is allowed only for the branch by default.' + ]; + } + + $environments = $project->getEnvironmentsObjects(); + if ($environments['count']) { + $created_builds = []; + $environment_names = $project->getEnvironmentsNamesByBranch($branch); + // use base branch from project + if (!empty($environment_names)) { + $duplicates = []; + foreach ($environment_names as $environment_name) { + if ( + !in_array($environment_name, $ignore_environments) || + ($tag && !in_array($tag, $ignore_tags, true)) + ) { + // If not, create a new build job for it: + $build = $this->buildService->createBuild( + $project, + $environment_name, + $commitId, + $project->getBranch(), + $tag, + $committer, + $commitMessage, + Build::SOURCE_WEBHOOK, + 0, + $extra + ); + + $created_builds[] = [ + 'id' => $build->getID(), + 'environment' => $environment_name, + ]; + } else { + $duplicates[] = array_search($environment_name, $ignore_environments); + } + } + if (!empty($created_builds)) { + if (empty($duplicates)) { + return ['status' => 'ok', 'builds' => $created_builds]; + } else { + return ['status' => 'ok', 'builds' => $created_builds, 'message' => sprintf('For this commit some builds already exists (%s)', implode(', ', $duplicates))]; + } + } else { + return ['status' => 'ignored', 'message' => sprintf('For this commit already created builds (%s)', implode(', ', $duplicates))]; + } + } else { + return ['status' => 'ignored', 'message' => 'Branch not assigned to any environment']; + } + } else { + $environment_name = null; + if ( + !in_array($environment_name, $ignore_environments, true) || + ($tag && !in_array($tag, $ignore_tags, true)) + ) { + $build = $this->buildService->createBuild( + $project, + null, + $commitId, + $branch, + $tag, + $committer, + $commitMessage, + Build::SOURCE_WEBHOOK, + 0, + $extra + ); + + return ['status' => 'ok', 'buildID' => $build->getID()]; + } else { + return [ + 'status' => 'ignored', + 'message' => sprintf('Duplicate of build #%d', array_search($environment_name, $ignore_environments)), + ]; + } + } + } + + /** + * Fetch a project and check its type. + * + * @param int|string $projectId id or title of project + * @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) + { + if (empty($projectId)) { + throw new Exception('Project does not exist: ' . $projectId); + } + + if (is_numeric($projectId)) { + $project = $this->projectStore->getById((integer)$projectId); + } else { + $projects = $this->projectStore->getByTitle($projectId, 2); + if ($projects['count'] < 1) { + throw new Exception('Project does not found: ' . $projectId); + } + if ($projects['count'] > 1) { + throw new Exception('Project id is ambiguous: ' . $projectId); + } + $project = reset($projects['items']); + } + + 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/src/PHPCensor/Controller/WidgetAllProjectsController.php b/src/PHPCensor/Controller/WidgetAllProjectsController.php new file mode 100644 index 0000000..f4013a4 --- /dev/null +++ b/src/PHPCensor/Controller/WidgetAllProjectsController.php @@ -0,0 +1,152 @@ +buildStore = Factory::getStore('Build'); + $this->projectStore = Factory::getStore('Project'); + $this->groupStore = Factory::getStore('ProjectGroup'); + } + + /** + * Display dashboard. + */ + public function index() + { + $this->view->groups = $this->getGroupInfo(); + + $this->response->disableLayout(); + $this->response->setContent($this->view->render()); + + return $this->response; + } + + /** + * Generate the HTML for the project overview section of the dashboard. + * + * @param Project[] $projects + * + * @return string + */ + protected function getSummaryHtml($projects) + { + $summaryBuilds = []; + $successes = []; + $failures = []; + $counts = []; + + foreach ($projects as $project) { + $summaryBuilds[$project->getId()] = $this->buildStore->getLatestBuilds($project->getId()); + + $count = $this->buildStore->getWhere( + ['project_id' => $project->getId()], + 1, + 0, + [], + ['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); + + $successes[$project->getId()] = $success; + $failures[$project->getId()] = $failure; + } + + $view = new View('WidgetAllProjects/index-projects'); + + $view->projects = $projects; + $view->builds = $summaryBuilds; + $view->successful = $successes; + $view->failed = $failures; + $view->counts = $counts; + + return $view->render(); + } + + /** + * Get a summary of the project groups we have, and what projects they have in them. + * + * @return array + */ + protected function getGroupInfo() + { + $rtn = []; + $groups = $this->groupStore->getWhere([], 100, 0, [], ['title' => 'ASC']); + + foreach ($groups['items'] as $group) { + $thisGroup = ['title' => $group->getTitle()]; + $projects = $this->projectStore->getByGroupId($group->getId(), false); + + $thisGroup['projects'] = $projects['items']; + $thisGroup['summary'] = $this->getSummaryHtml($thisGroup['projects']); + + $rtn[] = $thisGroup; + } + + return $rtn; + } + + /** + * @param integer $projectId + * + * @return Response + */ + public function update($projectId) + { + $count = $this->buildStore->getWhere( + ['project_id' => $projectId], + 1, + 0, + [], + ['id' => 'DESC'] + ); + $counts = $count['count']; + + $this->view->project = $this->projectStore->getById($projectId); + $this->view->builds = $this->buildStore->getLatestBuilds($projectId); + $this->view->successful = $this->buildStore->getLastBuildByStatus($projectId, Build::STATUS_SUCCESS); + $this->view->failed = $this->buildStore->getLastBuildByStatus($projectId, Build::STATUS_FAILED); + $this->view->counts = $counts; + + $this->response->disableLayout(); + $this->response->setContent($this->view->render()); + + return $this->response; + } +} diff --git a/src/PHPCensor/Controller/WidgetBuildErrorsController.php b/src/PHPCensor/Controller/WidgetBuildErrorsController.php new file mode 100644 index 0000000..7bde6c4 --- /dev/null +++ b/src/PHPCensor/Controller/WidgetBuildErrorsController.php @@ -0,0 +1,90 @@ +buildStore = Factory::getStore('Build'); + $this->projectStore = Factory::getStore('Project'); + } + + /** + * Display dashboard. + */ + public function index() + { + $view = new View('WidgetBuildErrors/update'); + + $this->view->projects = $this->renderAllProjectsLatestBuilds($view); + + $this->response->disableLayout(); + $this->response->setContent($this->view->render()); + + return $this->response; + } + + /** + * @return Response + */ + public function update() + { + $this->response->disableLayout(); + $this->response->setContent($this->renderAllProjectsLatestBuilds($this->view)); + + return $this->response; + } + + /** + * @param View $view + * + * @return string + */ + protected function renderAllProjectsLatestBuilds($view) + { + $builds = $this->buildStore->getAllProjectsLatestBuilds(); + + if (!empty($builds['projects'])) { + $view->builds = $builds['projects']; + $projects = $this->projectStore->getByIds(array_keys($builds['projects'])); + + $view_projects = []; + foreach($projects as $id => $project) { + if (!$project->getArchived()) { + $view_projects[$id] = $project; + } else { + unset($builds['projects'][$id]); + } + } + $view->projects = $view_projects; + } else { + $view = new View('WidgetBuildErrors/empty'); + } + + return $view->render(); + } +} diff --git a/src/PHPCensor/Controller/WidgetLastBuildsController.php b/src/PHPCensor/Controller/WidgetLastBuildsController.php new file mode 100644 index 0000000..0e0f2ea --- /dev/null +++ b/src/PHPCensor/Controller/WidgetLastBuildsController.php @@ -0,0 +1,70 @@ +buildStore = Factory::getStore('Build'); + } + + /** + * Display dashboard. + */ + public function index() + { + $builds = $this->buildStore->getLatestBuilds(null, 10); + + foreach ($builds as &$build) { + $build = BuildFactory::getBuild($build); + } + + $view = new View('WidgetLastBuilds/update'); + + $view->builds = $builds; + $this->view->timeline = $view->render(); + + $this->response->disableLayout(); + $this->response->setContent($this->view->render()); + + return $this->response; + } + + /** + * @return Response + */ + public function update() + { + $builds = $this->buildStore->getLatestBuilds(null, 10); + + foreach ($builds as &$build) { + $build = BuildFactory::getBuild($build); + } + + $this->view->builds = $builds; + + $this->response->disableLayout(); + $this->response->setContent($this->view->render()); + + return $this->response; + } +} diff --git a/src/PHPCensor/Helper/AnsiConverter.php b/src/PHPCensor/Helper/AnsiConverter.php new file mode 100644 index 0000000..709801e --- /dev/null +++ b/src/PHPCensor/Helper/AnsiConverter.php @@ -0,0 +1,46 @@ +convert($text); + } + + /** + * Do not instantiate this class. + */ + private function __construct() + { + } +} diff --git a/src/PHPCensor/Helper/Bitbucket.php b/src/PHPCensor/Helper/Bitbucket.php new file mode 100644 index 0000000..e585816 --- /dev/null +++ b/src/PHPCensor/Helper/Bitbucket.php @@ -0,0 +1,108 @@ +get('php-censor.bitbucket.username'); + $appPassword = Config::getInstance()->get('php-censor.bitbucket.app_password'); + + if (empty($username) || empty($appPassword)) { + return; + } + + $url = '/1.0/repositories/' . $repo . '/pullrequests/' . $pullId . '/comments/'; + $client = new Client(['base_uri' => 'https://api.bitbucket.org']); + $response = $client->post($url, [ + 'auth' => [$username, $appPassword], + 'headers' => [ + 'Content-Type' => 'application/json', + ], + 'json' => [ + 'content' => $comment, + 'anchor' => substr($commitId, 0, 12), + 'filename' => $file, + 'line_to' => $line, + ], + ]); + } + + /** + * Create a comment on a Bitbucket commit. + * + * @param $repo + * @param $commitId + * @param $file + * @param $line + * @param $comment + * @return null + */ + public function createCommitComment($repo, $commitId, $file, $line, $comment) + { + $username = Config::getInstance()->get('php-censor.bitbucket.username'); + $appPassword = Config::getInstance()->get('php-censor.bitbucket.app_password'); + + if (empty($username) || empty($appPassword)) { + return; + } + + $url = '/1.0/repositories/' . $repo . '/changesets/' . $commitId . '/comments'; + + $client = new Client(['base_uri' => 'https://api.bitbucket.org']); + $response = $client->post($url, [ + 'auth' => [$username, $appPassword], + 'headers' => [ + 'Content-Type' => 'application/json', + ], + 'json' => [ + 'content' => $comment, + 'filename' => $file, + 'line_to' => $line, + ], + ]); + } + + /** + * @param string $repo + * @param int $pullRequestId + * + * @return string + */ + public function getPullRequestDiff($repo, $pullRequestId) + { + $username = Config::getInstance()->get('php-censor.bitbucket.username'); + $appPassword = Config::getInstance()->get('php-censor.bitbucket.app_password'); + + if (empty($username) || empty($appPassword)) { + return; + } + + $url = '/2.0/repositories/' . $repo . '/pullrequests/' . $pullRequestId . '/diff'; + + $client = new Client(['base_uri' => 'https://api.bitbucket.org']); + + $response = $client->get($url, ['auth' => [$username, $appPassword]]); + + return (string)$response->getBody(); + } +} diff --git a/src/PHPCensor/Helper/Build.php b/src/PHPCensor/Helper/Build.php new file mode 100644 index 0000000..6484437 --- /dev/null +++ b/src/PHPCensor/Helper/Build.php @@ -0,0 +1,21 @@ + + */ +class Build +{ + /** + * Returns a more human-friendly version of a plugin name. + * @param $name + * @return mixed + */ + public function formatPluginName($name) + { + return str_replace('Php', 'PHP', ucwords(str_replace('_', ' ', $name))); + } +} diff --git a/src/PHPCensor/Helper/BuildInterpolator.php b/src/PHPCensor/Helper/BuildInterpolator.php new file mode 100644 index 0000000..f645805 --- /dev/null +++ b/src/PHPCensor/Helper/BuildInterpolator.php @@ -0,0 +1,85 @@ + value pairs that will be used for + * interpolation and environment variables + * @var mixed[] + * @see setupInterpolationVars() + */ + protected $interpolation_vars = []; + + /** + * Sets the variables that will be used for interpolation. + * + * @param BaseBuild $build + * @param string $buildPath + * @param string $url + */ + public function setupInterpolationVars(BaseBuild $build, $buildPath, $url) + { + $this->interpolation_vars = []; + $this->interpolation_vars['%PHPCI%'] = 1; + $this->interpolation_vars['%COMMIT%'] = $build->getCommitId(); + $this->interpolation_vars['%SHORT_COMMIT%'] = substr($build->getCommitId(), 0, 7); + $this->interpolation_vars['%COMMIT_EMAIL%'] = $build->getCommitterEmail(); + $this->interpolation_vars['%COMMIT_MESSAGE%'] = $build->getCommitMessage(); + $this->interpolation_vars['%COMMIT_URI%'] = $build->getCommitLink(); + $this->interpolation_vars['%BRANCH%'] = $build->getBranch(); + $this->interpolation_vars['%BRANCH_URI%'] = $build->getBranchLink(); + $this->interpolation_vars['%ENVIRONMENT%'] = $build->getEnvironment(); + $this->interpolation_vars['%PROJECT%'] = $build->getProjectId(); + $this->interpolation_vars['%BUILD%'] = $build->getId(); + $this->interpolation_vars['%PROJECT_TITLE%'] = $build->getProjectTitle(); + $this->interpolation_vars['%PROJECT_URI%'] = $url . "project/view/" . $build->getProjectId(); + $this->interpolation_vars['%BUILD_PATH%'] = $buildPath; + $this->interpolation_vars['%BUILD_URI%'] = $url . "build/view/" . $build->getId(); + $this->interpolation_vars['%PHPCI_COMMIT%'] = $this->interpolation_vars['%COMMIT%']; + $this->interpolation_vars['%PHPCI_SHORT_COMMIT%'] = $this->interpolation_vars['%SHORT_COMMIT%']; + $this->interpolation_vars['%PHPCI_COMMIT_MESSAGE%'] = $this->interpolation_vars['%COMMIT_MESSAGE%']; + $this->interpolation_vars['%PHPCI_COMMIT_EMAIL%'] = $this->interpolation_vars['%COMMIT_EMAIL%']; + $this->interpolation_vars['%PHPCI_COMMIT_URI%'] = $this->interpolation_vars['%COMMIT_URI%']; + $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_PROJECT_URI%'] = $this->interpolation_vars['%PROJECT_URI%']; + $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_SHORT_COMMIT=' . $this->interpolation_vars['%SHORT_COMMIT%']); + putenv('PHPCI_COMMIT_MESSAGE=' . $this->interpolation_vars['%COMMIT_MESSAGE%']); + putenv('PHPCI_COMMIT_EMAIL=' . $this->interpolation_vars['%COMMIT_EMAIL%']); + putenv('PHPCI_COMMIT_URI=' . $this->interpolation_vars['%COMMIT_URI%']); + 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_ENVIRONMENT=' . $this->interpolation_vars['%ENVIRONMENT%']); + } + + /** + * Replace every occurrence of the interpolation vars in the given string + * Example: "This is build %PHPCI_BUILD%" => "This is build 182" + * @param string $input + * @return string + */ + public function interpolate($input) + { + $keys = array_keys($this->interpolation_vars); + $values = array_values($this->interpolation_vars); + return str_replace($keys, $values, $input); + } +} diff --git a/src/PHPCensor/Helper/CommandExecutor.php b/src/PHPCensor/Helper/CommandExecutor.php new file mode 100644 index 0000000..b616606 --- /dev/null +++ b/src/PHPCensor/Helper/CommandExecutor.php @@ -0,0 +1,343 @@ +logger = $logger; + $this->quiet = $quiet; + $this->verbose = $verbose; + $this->lastOutput = []; + $this->rootDir = $rootDir; + } + + /** + * Executes shell commands. + * + * @param array $args + * + * @return bool Indicates success + */ + public function executeCommand($args = []) + { + $this->lastOutput = []; + + $this->logger->logDebug('Args: ' . json_encode($args)); + + $command = call_user_func_array('sprintf', $args); + + $this->logger->logDebug('Command: ' . $command); + + if ($this->quiet) { + $this->logger->log('Executing: ' . $command); + } + + $status = 0; + $descriptorSpec = [ + 0 => ["pipe", "r"], // stdin + 1 => ["pipe", "w"], // stdout + 2 => ["pipe", "w"], // stderr + ]; + + $pipes = []; + $process = proc_open($command, $descriptorSpec, $pipes, $this->buildPath, null); + + $this->lastOutput = ''; + $this->lastError = ''; + + if (is_resource($process)) { + fclose($pipes[0]); + + list($this->lastOutput, $this->lastError) = $this->readAlternating([$pipes[1], $pipes[2]]); + + $status = proc_close($process); + } + + mb_substitute_character(65533); + + $this->lastOutput = mb_convert_encoding($this->lastOutput, 'utf8', 'utf8'); + $this->lastOutput = array_filter(explode(PHP_EOL, $this->lastOutput)); + + $shouldOutput = ($this->logExecOutput && ($this->verbose || $status != 0)); + + if ($shouldOutput && !empty($this->lastOutput)) { + $this->logger->log($this->lastOutput); + } + + if (!empty($this->lastError)) { + $this->logger->log("\033[0;31m" . $this->lastError . "\033[0m", LogLevel::ERROR); + } + + $rtn = false; + if ($status == 0) { + $rtn = true; + } + + $this->logger->logDebug('Execution status: ' . $status); + + return $rtn; + } + + /** + * Reads from array of streams as data becomes available. + * + * @param array $descriptors + * + * @return string[] data read from each descriptor + */ + private function readAlternating(array $descriptors) + { + $outputs = []; + foreach ($descriptors as $key => $descriptor) { + stream_set_blocking($descriptor, false); + $outputs[$key] = ''; + } + do { + $read = $descriptors; + $write = null; + $except = null; + stream_select($read, $write, $except, null); + foreach ($read as $descriptor) { + $key = array_search($descriptor, $descriptors); + if (feof($descriptor)) { + fclose($descriptor); + unset($descriptors[$key]); + } else { + $outputs[$key] .= fgets($descriptor); + } + } + } while (count($descriptors) > 0); + return $outputs; + } + + /** + * Returns the output from the last command run. + * + * @return string + */ + public function getLastOutput() + { + return implode(PHP_EOL, $this->lastOutput); + } + + /** + * Returns the stderr output from the last command run. + * + * @return string + */ + public function getLastError() + { + return $this->lastError; + } + + /** + * @param string $composerBin + * @param string $binary + * + * @return false|string + */ + protected function findBinaryLocal($composerBin, $binary) + { + if (is_dir($composerBin) && is_file($composerBin . '/' . $binary)) { + $this->logger->logDebug(sprintf('Found in %s (local): %s', $composerBin, $binary)); + + return $composerBin . '/' . $binary; + } + + return false; + } + + /** + * @param string $binary + * + * @return false|string + */ + protected function findBinaryGlobal($binary) + { + if (is_file($this->rootDir . 'vendor/bin/' . $binary)) { + $this->logger->logDebug(sprintf('Found in %s (global): %s', 'vendor/bin', $binary)); + + return $this->rootDir . 'vendor/bin/' . $binary; + } + + return false; + } + + /** + * Uses 'which' to find a system binary by name + * + * @param string $binary + * + * @return false|string + */ + protected function findBinarySystem($binary) + { + $tempBinary = trim(shell_exec('which ' . $binary)); + if (is_file($tempBinary)) { + $this->logger->logDebug(sprintf('Found in %s (system): %s', '', $binary)); + + return $tempBinary; + } + + return false; + } + + /** + * Find a binary required by a plugin. + * + * @param string $binary + * @param bool $quiet Returns null instead of throwing an exception. + * @param string $priorityPath + * + * @return null|string + * + * @throws \Exception when no binary has been found and $quiet is false. + */ + public function findBinary($binary, $quiet = false, $priorityPath = 'local') + { + $composerBin = $this->getComposerBinDir(realpath($this->buildPath)); + + if (is_string($binary)) { + $binary = [$binary]; + } + + foreach ($binary as $bin) { + $this->logger->logDebug(sprintf('Looking for binary: %s', $bin)); + + if ('system' === $priorityPath) { + if ($binarySystem = $this->findBinarySystem($bin)) { + return $binarySystem; + } + + if ($binaryLocal = $this->findBinaryLocal($composerBin, $bin)) { + return $binaryLocal; + } + + if ($binaryGlobal = $this->findBinaryGlobal($bin)) { + return $binaryGlobal; + } + } elseif ('global' === $priorityPath) { + if ($binaryGlobal = $this->findBinaryGlobal($bin)) { + return $binaryGlobal; + } + + if ($binaryLocal = $this->findBinaryLocal($composerBin, $bin)) { + return $binaryLocal; + } + + if ($binarySystem = $this->findBinarySystem($bin)) { + return $binarySystem; + } + } else { + if ($binaryLocal = $this->findBinaryLocal($composerBin, $bin)) { + return $binaryLocal; + } + + if ($binaryGlobal = $this->findBinaryGlobal($bin)) { + return $binaryGlobal; + } + + if ($binarySystem = $this->findBinarySystem($bin)) { + return $binarySystem; + } + } + } + + if ($quiet) { + return null; + } + + throw new Exception(sprintf('Could not find %s', implode('/', $binary))); + } + + /** + * Try to load the composer.json file in the building project + * If the bin-dir is configured, return the full path to it + * + * @param string $path Current build path + * + * @return string|null + */ + public function getComposerBinDir($path) + { + if (is_dir($path)) { + $composer = $path . DIRECTORY_SEPARATOR . 'composer.json'; + if (is_file($composer)) { + $json = json_decode(file_get_contents($composer)); + + if (isset($json->config->{"bin-dir"})) { + return $path . DIRECTORY_SEPARATOR . $json->config->{"bin-dir"}; + } elseif (is_dir($path . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'bin')) { + return $path . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'bin'; + } + } + } + return null; + } + + /** + * Set the buildPath property. + * + * @param string $path + */ + public function setBuildPath($path) + { + $this->buildPath = $path; + } +} diff --git a/src/PHPCensor/Helper/CommandExecutorInterface.php b/src/PHPCensor/Helper/CommandExecutorInterface.php new file mode 100644 index 0000000..63a47b0 --- /dev/null +++ b/src/PHPCensor/Helper/CommandExecutorInterface.php @@ -0,0 +1,43 @@ +'; + + protected $emailTo = []; + protected $emailCc = []; + protected $subject = 'Email from PHP Censor'; + protected $body = ''; + protected $isHtml = false; + protected $config; + + /** + * Create a new email object. + */ + public function __construct() + { + $this->config = Config::getInstance(); + } + + /** + * Set the email's To: header. + * @param string $email + * @param string|null $name + * @return $this + */ + public function setEmailTo($email, $name = null) + { + $this->emailTo[$email] = $name; + + return $this; + } + + /** + * Add an address to the email's CC header. + * @param string $email + * @param string|null $name + * @return $this + */ + public function addCc($email, $name = null) + { + $this->emailCc[$email] = $name; + + return $this; + } + + /** + * Set the email subject. + * @param string $subject + * @return $this + */ + public function setSubject($subject) + { + $this->subject = $subject; + + return $this; + } + + /** + * Set the email body. + * @param string $body + * @return $this + */ + public function setBody($body) + { + $this->body = $body; + + return $this; + } + + /** + * Set whether or not the email body is HTML. + * @param bool $isHtml + * @return $this + */ + public function setHtml($isHtml = false) + { + $this->isHtml = $isHtml; + + return $this; + } + + /** + * Get the from address to use for the email. + * @return mixed|string + */ + protected function getFrom() + { + $from = $this->config->get( + 'php-censor.email_settings.from_address', + self::DEFAULT_FROM + ); + + if (strpos($from, '<') === false) { + return (string)$from; + } + + preg_match('#^(.*?)<(.*)>$#ui', $from, $fromParts); + + return [$fromParts[2] => $fromParts[1]]; + } + + /** + * Send the email. + * + * @param Builder $builder + * + * @return integer + */ + public function send(Builder $builder = null) + { + $smtpServer = $this->config->get('php-censor.email_settings.smtp_address'); + if (null !== $builder) { + $builder->logDebug(sprintf("SMTP: '%s'", !empty($smtpServer) ? 'true' : 'false')); + } + + $factory = new MailerFactory($this->config->get('php-censor')); + $mailer = $factory->getSwiftMailerFromConfig(); + + $message = \Swift_Message::newInstance($this->subject) + ->setFrom($this->getFrom()) + ->setTo($this->emailTo) + ->setBody($this->body); + + if ($this->isHtml) { + $message->setContentType('text/html'); + } + + if (is_array($this->emailCc) && count($this->emailCc)) { + $message->setCc($this->emailCc); + } + + return $mailer->send($message); + } +} diff --git a/src/PHPCensor/Helper/Github.php b/src/PHPCensor/Helper/Github.php new file mode 100644 index 0000000..3e19b84 --- /dev/null +++ b/src/PHPCensor/Helper/Github.php @@ -0,0 +1,170 @@ +get(('https://api.github.com' . $url), [ + 'query' => $params, + ]); + + $body = json_decode($response->getBody(), true); + $headers = $response->getHeaders(); + + foreach ($body as $item) { + $results[] = $item; + } + + foreach ($headers as $header_name => $header) { + if ( + 'Link' === $header_name && + preg_match('/^<([^>]+)>; rel="next"/', implode(', ', $header), $r) + ) { + $host = parse_url($r[1]); + + parse_str($host['query'], $params); + $results = $this->makeRecursiveRequest($host['path'], $params, $results); + + break; + } + } + + return $results; + } + + /** + * Get an array of repositories from Github's API. + */ + public function getRepositories() + { + $token = Config::getInstance()->get('php-censor.github.token'); + + if (!$token) { + return []; + } + + $cache = Cache::getCache(Cache::TYPE_APC); + $rtn = $cache->get('php-censor-github-repos'); + + if (!$rtn) { + $client = new Client(); + $response = $client->get('https://api.github.com/user/orgs', [ + 'query' => [ + 'access_token' => $token + ], + ]); + + $orgs = json_decode($response->getBody(), true); + + $params = ['type' => 'all', 'access_token' => $token]; + $repos = ['user' => []]; + $repos['user'] = $this->makeRecursiveRequest('/user/repos', $params); + + foreach ($orgs as $org) { + $repos[$org['login']] = $this->makeRecursiveRequest('/orgs/' . $org['login'] . '/repos', $params); + } + + $rtn = []; + foreach ($repos as $repoGroup) { + foreach ($repoGroup as $repo) { + $rtn['repos'][] = $repo['full_name']; + } + } + + $cache->set('php-censor-github-repos', $rtn); + } + + return $rtn; + } + + /** + * Create a comment on a specific file (and commit) in a Github Pull Request. + * @param $repo + * @param $pullId + * @param $commitId + * @param $file + * @param $line + * @param $comment + * @return null + */ + public function createPullRequestComment($repo, $pullId, $commitId, $file, $line, $comment) + { + $token = Config::getInstance()->get('php-censor.github.token'); + + if (!$token) { + return null; + } + + $url = '/repos/' . strtolower($repo) . '/pulls/' . $pullId . '/comments'; + + $params = [ + 'body' => $comment, + 'commit_id' => $commitId, + 'path' => $file, + 'position' => $line, + ]; + + $client = new Client(); + $client->post(('https://api.github.com' . $url), [ + 'headers' => [ + 'Authorization' => 'Basic ' . base64_encode($token . ':x-oauth-basic'), + 'Content-Type' => 'application/x-www-form-urlencoded' + ], + 'json' => $params, + ]); + } + + /** + * Create a comment on a Github commit. + * @param $repo + * @param $commitId + * @param $file + * @param $line + * @param $comment + * @return null + */ + public function createCommitComment($repo, $commitId, $file, $line, $comment) + { + $token = Config::getInstance()->get('php-censor.github.token'); + + if (!$token) { + return null; + } + + $url = '/repos/' . strtolower($repo) . '/commits/' . $commitId . '/comments'; + + $params = [ + 'body' => $comment, + 'path' => $file, + 'position' => $line, + ]; + + $client = new Client(); + $client->post(('https://api.github.com' . $url), [ + 'headers' => [ + 'Authorization' => 'Basic ' . base64_encode($token . ':x-oauth-basic'), + 'Content-Type' => 'application/x-www-form-urlencoded' + ], + 'json' => $params, + ]); + } +} diff --git a/src/PHPCensor/Helper/Lang.php b/src/PHPCensor/Helper/Lang.php new file mode 100644 index 0000000..1dbff3f --- /dev/null +++ b/src/PHPCensor/Helper/Lang.php @@ -0,0 +1,190 @@ +getLanguage(); + if ($user && self::setLanguage($language)) { + return; + } + } + + // Try the installation default language: + $language = $config->get('php-censor.language', self::DEFAULT_LANGUAGE); + if (self::setLanguage($language)) { + return; + } + } + + /** + * Load a specific language file. + * + * @param string $language + * + * @return string[]|null + */ + protected static function loadLanguage($language = null) + { + $language = $language ? $language : self::$language; + $langFile = SRC_DIR . 'Languages' . DIRECTORY_SEPARATOR . 'lang.' . $language . '.php'; + + if (!file_exists($langFile)) { + return null; + } + + $strings = include($langFile); + if (is_null($strings) || !is_array($strings) || !count($strings)) { + return null; + } + + return $strings; + } + + /** + * Load the names of all available languages. + */ + protected static function loadAvailableLanguages() + { + $matches = []; + foreach (glob(SRC_DIR . 'Languages' . DIRECTORY_SEPARATOR . 'lang.*.php') as $file) { + if (preg_match('/lang\.([a-z]{2}\-?[a-z]*)\.php/', $file, $matches)) { + self::$languages[] = $matches[1]; + } + } + } +} diff --git a/src/PHPCensor/Helper/LoginIsDisabled.php b/src/PHPCensor/Helper/LoginIsDisabled.php new file mode 100644 index 0000000..70ed348 --- /dev/null +++ b/src/PHPCensor/Helper/LoginIsDisabled.php @@ -0,0 +1,31 @@ + + */ +class LoginIsDisabled +{ + /** + * Checks if + * + * @param $method + * @param array $params + * + * @return mixed|null + */ + public function __call($method, $params = []) + { + unset($method, $params); + + $config = Config::getInstance(); + $disableAuth = (boolean)$config->get('php-censor.security.disable_auth', false); + + return $disableAuth; + } +} diff --git a/src/PHPCensor/Helper/MailerFactory.php b/src/PHPCensor/Helper/MailerFactory.php new file mode 100644 index 0000000..c6910b0 --- /dev/null +++ b/src/PHPCensor/Helper/MailerFactory.php @@ -0,0 +1,85 @@ +emailConfig = isset($config['email_settings']) ? $config['email_settings'] : []; + } + + /** + * Returns an instance of Swift_Mailer based on the config.s + * @return \Swift_Mailer + */ + public function getSwiftMailerFromConfig() + { + if ($this->getMailConfig('smtp_address')) { + $encryptionType = $this->getMailConfig('smtp_encryption'); + + // Workaround issue where smtp_encryption could == 1 in the past by + // checking it is a valid transport + if ($encryptionType && !in_array($encryptionType, stream_get_transports())) { + $encryptionType = null; + } + + /** @var \Swift_SmtpTransport $transport */ + $transport = \Swift_SmtpTransport::newInstance( + $this->getMailConfig('smtp_address'), + $this->getMailConfig('smtp_port'), + $encryptionType + ); + + $transport->setUsername($this->getMailConfig('smtp_username')); + $transport->setPassword($this->getMailConfig('smtp_password')); + } else { + $transport = \Swift_MailTransport::newInstance(null); + } + + return \Swift_Mailer::newInstance($transport); + } + + /** + * Return a specific configuration value by key. + * @param $configName + * @return null|string + */ + public function getMailConfig($configName) + { + if (isset($this->emailConfig[$configName]) && $this->emailConfig[$configName] != "") { + return $this->emailConfig[$configName]; + } else { + // Check defaults + + switch ($configName) { + case 'smtp_address': + return ""; + case 'default_mailto_address': + return null; + case 'smtp_port': + return '25'; + case 'smtp_encryption': + return null; + default: + return ""; + } + } + } +} diff --git a/src/PHPCensor/Helper/SshKey.php b/src/PHPCensor/Helper/SshKey.php new file mode 100644 index 0000000..86e30cc --- /dev/null +++ b/src/PHPCensor/Helper/SshKey.php @@ -0,0 +1,42 @@ + '', 'public_key' => '']; + + $output = @shell_exec('ssh-keygen -t rsa -b 2048 -f '.$keyFile.' -N "" -C "deploy@php-censor"'); + + if (!empty($output)) { + $pub = file_get_contents($keyFile . '.pub'); + $prv = file_get_contents($keyFile); + + if (!empty($pub)) { + $return['public_key'] = $pub; + } + + if (!empty($prv)) { + $return['private_key'] = $prv; + } + } + + return $return; + } +} diff --git a/src/PHPCensor/Helper/User.php b/src/PHPCensor/Helper/User.php new file mode 100644 index 0000000..6ca3d25 --- /dev/null +++ b/src/PHPCensor/Helper/User.php @@ -0,0 +1,32 @@ + + */ +class User +{ + /** + * Proxies method calls through to the current active user model. + * @param $method + * @param array $params + * @return mixed|null + */ + public function __call($method, $params = []) + { + if (empty($_SESSION['php-censor-user'])) { + return null; + } + + $user = $_SESSION['php-censor-user']; + + if (!is_object($user)) { + return null; + } + + return call_user_func_array([$user, $method], $params); + } +} diff --git a/src/PHPCensor/Languages/lang.da.php b/src/PHPCensor/Languages/lang.da.php new file mode 100644 index 0000000..dfea758 --- /dev/null +++ b/src/PHPCensor/Languages/lang.da.php @@ -0,0 +1,289 @@ + 'Dansk', + 'language' => 'Sprog', + + // Log in: + 'log_in_to_app' => 'Log ind i PHP Censor', + 'login_error' => 'Forkert email-adresse eller adgangskode', + 'forgotten_password_link' => 'Har du glemt din adgangskode?', + 'reset_emailed' => 'Vi har sendt dig en email med et link til at nulstille din adgangskode.', + 'reset_header' => 'Bare rolig!
Indtast blot din email-adresse, så sender +vi dig et link til at nulstille din adgangskode.', + 'reset_email_address' => 'Indtast din email-adresse:', + 'reset_send_email' => 'Send nulstillings-link', + 'reset_enter_password' => 'Indtast venligst en ny adgangskode', + 'reset_new_password' => 'Ny adgangskode:', + 'reset_change_password' => 'Skift adgangskode', + 'reset_no_user_exists' => 'Der findes ingen bruger med den email-adresse, prøv igen.', + 'reset_email_body' => 'Hej %s, + +Du modtager denne email fordi du eller en anden person har anmodet om at nulstille din adgangskode til PHP Censor. + +Hvis det var dig kan du klikke følgende link for at nulstille din adgangskode: %ssession/reset-password/%d/%s + +Hvis det ikke var dig kan du ignorere denne email og intet vil ske. + +Tak, + +PHP Censor', + + 'reset_email_title' => 'PHP Censor 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', + + + // Top Nav + 'toggle_navigation' => 'Vis/skjul navigation', + 'n_builds_pending' => '%d builds i køen', + 'n_builds_running' => '%d builds kører', + 'edit_profile' => 'Redigér profil', + 'sign_out' => 'Log ud', + 'branch_x' => 'Branch: %s', + 'created_x' => 'Oprettet: %s', + 'started_x' => 'Startet: %s', + + // Sidebar + 'hello_name' => 'Hej %s', + 'dashboard' => 'Dashboard', + 'admin_options' => 'Administrator-indstillinger', + 'add_project' => 'Tilføj projekt', + 'settings' => 'Indstillinger', + 'manage_users' => 'Administrér brugere', + 'plugins' => 'Plugins', + 'view' => 'Vis', + 'build_now' => 'Start build nu', + 'edit_project' => 'Redigér projekt', + 'delete_project' => 'Slet projekt', + + // Project Summary: + 'no_builds_yet' => 'Ingen builds pt.!', + 'x_of_x_failed' => '%d af de sidste %d builds fejlede.', + 'x_of_x_failed_short' => '%d / %d fejlede.', + 'last_successful_build' => 'Sidste succesfulde build var %s.', + 'never_built_successfully' => 'Dette projekt har indtil videre ingen succesfulde builds.', + 'all_builds_passed' => 'All de sidste %d builds fejlede.', + 'all_builds_passed_short' => '%d / %d lykkedes.', + 'last_failed_build' => 'Det sidste mislykkede build var %s', + 'never_failed_build' => 'Dette projekt er endnu ikke blevet kørt.', + 'view_project' => 'Vis Projekt', + + // Timeline: + 'latest_builds' => 'Nyeste Builds', + 'pending' => 'Venter', + 'running' => 'Kører', + 'success' => 'Succes', + 'failed' => 'Fejlede', + 'manual_build' => 'Manuelt Build', + + // Add/Edit Project: + 'new_project' => 'Nyt Projekt', + 'project_x_not_found' => 'Projektet med ID %d findes ikke.', + 'project_details' => 'Projekt-detaljer', + 'public_key_help' => 'For at gøre det lettere at starte har vi genereret en SSH-nøgle som du kan bruge til dette projekt. For at bruge den behøver du blot tilføje den følgende public key til "deployment keys" sektionen +i din foretrukne hosting-platform.', + 'select_repository_type' => 'Vælg repository-type...', + 'github' => 'GitHub', + 'bitbucket' => 'Bitbucket', + 'gitlab' => 'GitLab', + 'remote' => 'Ekstern URL', + 'local' => 'Lokalt filsystem', + 'hg' => 'Mercurial', + + 'where_hosted' => 'Hvor er dit projekt hosted?', + 'choose_github' => 'Vælg et GitHub-repository:', + + 'repo_name' => 'Repository-navn / URL (ekstern) eller filsystem-sti (lokal)', + 'project_title' => 'Projekt-titel', + 'project_private_key' => 'Privat nøgle med adgang til dette repository +(tom for lokal nøgle og/eller anonym adgang)', + 'build_config' => 'PHP Censor build-konfiguration for dette projekt +(hvis du ikke har mulighed for at tilføje en .php-censor.yml (.phpci.yml|phpci.yml) fil i projektets repository)', + 'default_branch' => 'Default branch navn', + 'allow_public_status' => 'Tillad offentlig status-side og -billede for dette projekt?', + 'archived' => 'Archived', + 'archived_menu' => 'Archived', + 'save_project' => 'Gem Projekt', + + 'error_mercurial' => 'Mercurial repository-URL skal starte med http:// eller https://', + 'error_remote' => 'Repository-URL skal starte med git://, http:// eller https://', + 'error_gitlab' => 'GitLab repository-navn skal være i formatet "user@domæne.tld:ejernavn/repositorynavn.git"', + 'error_github' => 'Repository-navn skal være i formatet "ejernavn/repositorynavn"', + 'error_bitbucket' => 'Repository-navn skal være i formatet "ejernavn/repositorynavn"', + 'error_path' => 'Stien du indtastede findes ikke.', + + // View Project: + 'all_branches' => 'Alle branches', + 'builds' => 'Builds', + 'id' => 'ID', + 'date' => 'Date', + 'project' => 'Projekt', + 'commit' => 'Commit', + 'branch' => 'Branch', + 'status' => 'Status', + 'prev_link' => '« Forrige', + 'next_link' => 'Næste »', + 'public_key' => 'Offentlig nøgle', + 'delete_build' => 'Slet Build', + + 'webhooks' => 'Webhooks', + 'webhooks_help_github' => 'For at køre dette build automatisk når nye commits bliver pushed skal du tilføje nedenstående +URL som nyt "Webhook" i Webhooks +and Services under dit GitHub-repository.', + + 'webhooks_help_gitlab' => 'For at køre dette build automatisk når nye commits bliver pushed kan du tilføje nedenstående URL +som en "WebHook URL" i Web Hooks-sektionen i dit GitLab-repository.', + + 'webhooks_help_bitbucket' => 'For at køre dette build automatisk når nye commits bliver pushed skal du tilføje nedenstående +URL som "POST" service i + +Services sektionen under dit Bitbucket-repository.', + + // View Build + 'build_x_not_found' => 'Build med ID %d findes ikke.', + 'build_n' => 'Build %d', + 'rebuild_now' => 'Gentag Build', + + + 'committed_by_x' => 'Committed af %s', + 'commit_id_x' => 'Commit: %s', + + 'chart_display' => 'Denne graf vises når buildet er færdigt.', + + 'build' => 'Build', + 'lines' => 'Linjer', + 'comment_lines' => 'Kommentar-linjer', + 'noncomment_lines' => 'Ikke-kommentar-linjer', + 'logical_lines' => 'Logiske linjer', + 'lines_of_code' => 'Kode-linjer', + 'build_log' => 'Build-log', + 'quality_trend' => 'Kvalitets-trend', + 'codeception_errors' => 'Codeception-fejl', + 'phpmd_warnings' => 'PHPMD-advarsler', + 'phpcs_warnings' => 'PHPCS-advarsler', + 'phpcs_errors' => 'PHPCS-fejl', + 'phplint_errors' => 'Lint-fejl', + 'phpunit_errors' => 'PHPUnit-fejl', + 'phpdoccheck_warnings' => 'Manglende Docblocks', + 'issues' => 'Sager', + + 'codeception' => 'Codeception', + 'phpcpd' => 'PHP Copy/Paste Detector', + 'phpcs' => 'PHP Code Sniffer', + 'phpdoccheck' => 'Manglende Docblocks', + 'phpmd' => 'PHP Mess Detector', + 'phpspec' => 'PHP Spec', + 'phpunit' => 'PHP Unit', + + 'file' => 'Fil', + 'line' => 'Linje', + 'class' => 'Klasse', + 'method' => 'Funktion', + 'message' => 'Besked', + 'start' => 'Start', + 'end' => 'Slut', + 'from' => 'Fra', + 'to' => 'Til', + 'result' => 'Resultat', + 'ok' => 'OK', + 'took_n_seconds' => 'Tog %d sekunder', + 'build_started' => 'Build Startet', + 'build_finished' => 'Build Afsluttet', + 'test_message' => 'Message', + 'test_no_message' => 'No message', + 'test_success' => 'Successful: %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', + 'password_change' => 'Adgangskode (tom hvis du ikke ønsker at ændre koden)', + 'save' => 'Gem »', + 'update_your_details' => 'Opdatér oplysninger', + 'your_details_updated' => 'Dine oplysninger blev gemt.', + 'add_user' => 'Tilføj bruger', + 'is_admin' => 'Administrator?', + 'yes' => 'Ja', + 'no' => 'Nej', + 'edit' => 'Redigér', + 'edit_user' => 'Redigér Bruger', + 'delete_user' => 'Slet Bruger', + 'user_n_not_found' => 'Brugeren med ID %d findes ikke.', + 'is_user_admin' => 'Er denne bruger en administrator?', + 'save_user' => 'Gem Bruger', + + // Settings: + 'settings_saved' => 'Dine indstillinger blev gemt.', + 'settings_check_perms' => 'Dine indstillinger kunne ikke gemmes, kontrollér rettighederne på din config.yml fil.', + 'settings_cannot_write' => 'PHP Censor kan ikke skrive til din config.yml fil, indstillinger bliver muligvis ikke gemt korrekt før dette problem løses.', + 'settings_github_linked' => 'Din GitHub-konto er nu tilsluttet.', + 'settings_github_not_linked' => 'Din GitHub-konto kunne ikke tilsluttes.', + 'build_settings' => 'Build-indstillinger', + 'github_application' => 'GitHub-applikation', + 'github_sign_in' => 'Før du kan bruge GitHub skal du logge ind og give PHP Censor +adgang til din konto.', + 'github_app_linked' => 'PHP Censor blev tilsluttet din GitHub-konto.', + 'github_where_to_find' => 'Hvor disse findes...', + 'github_where_help' => 'Hvis du ejer applikationen du ønsker at bruge kan du finde denne information i +applications under indstillinger.', + + 'email_settings' => 'Email-indstillinger', + 'email_settings_help' => 'Før PHP Censor kan sende build-notifikationer via email +skal du konfigurere nedenstående SMTP-indstillinger.', + + 'application_id' => 'Application ID', + 'application_secret' => 'Application Secret', + + 'smtp_server' => 'SMTP-server', + 'smtp_port' => 'SMTP-port', + 'smtp_username' => 'SMTP-brugernavn', + 'smtp_password' => 'SMTP-adgangskode', + 'from_email_address' => 'Fra email-adresse', + 'default_notification_address' => 'Default notifikations-email-adresse', + 'use_smtp_encryption' => 'Brug SMTP-kryptering?', + 'none' => 'Ingen', + 'ssl' => 'SSL', + 'tls' => 'TLS', + + 'failed_after' => 'Betragt et build som fejlet efter', + '5_mins' => '5 minutter', + '15_mins' => '15 minutter', + '30_mins' => '30 minutter', + '1_hour' => '1 time', + '3_hours' => '3 timer', + + // Plugins + 'cannot_update_composer' => 'PHP Censor kan ikke opdatere composer.json da filen ikke kan skrives.', + 'x_has_been_removed' => '%s er blevet slettet.', + 'x_has_been_added' => '%s blev tilføjet til composer.json for dig og vil blive installeret næste gang +du kører composer update.', + 'enabled_plugins' => 'Aktive plugins', + 'provided_by_package' => 'Via pakke', + 'installed_packages' => 'Installerede pakker', + 'suggested_packages' => 'Forslag til pakker', + 'title' => 'Titel', + 'description' => 'Beskrivelse', + 'version' => 'Version', + 'install' => 'Installér »', + 'remove' => 'Fjern »', + 'search_packagist_for_more' => 'Søg på Packagist efter flere pakker', + 'search' => 'Søg »', + + // Update + 'update_app' => 'Opdatér databasen med ændrede modeller', + 'updating_app' => 'Opdaterer PHP Censor-database:', + 'not_installed' => 'PHP Censor lader til ikke at være installeret.', + 'install_instead' => 'Installér venligst PHP Censor via php-censor:install istedet.', + + // Build Plugins: + 'passing_build' => 'Succesfuldt Build', + 'failing_build' => 'Fejlet Build', + 'log_output' => 'Log-output:', +]; diff --git a/src/PHPCensor/Languages/lang.de.php b/src/PHPCensor/Languages/lang.de.php new file mode 100644 index 0000000..270bfbe --- /dev/null +++ b/src/PHPCensor/Languages/lang.de.php @@ -0,0 +1,325 @@ + 'Deutsch', + 'language' => 'Sprache', + + // Log in: + 'log_in_to_app' => 'In PHP Censor einloggen', + 'login_error' => 'Fehlerhafte Emailadresse oder fehlerhaftes Passwort', + 'forgotten_password_link' => 'Passwort vergessen?', + 'reset_emailed' => 'Wir haben Ihnen einen Link geschickt, um Ihr Passwort zurückzusetzen', + 'reset_header' => 'Keine Panik!
Geben Sie einfach unten Ihre Emailadresse an + und wir senden Ihnen einen Link, um Ihr Passwort zurückzusetzen', + 'reset_email_address' => 'Geben Sie Ihre Emailadresse an:', + 'reset_send_email' => 'Link senden', + 'reset_enter_password' => 'Bitte geben Sie ein neues Passwort ein', + 'reset_new_password' => 'Neues Passwort:', + 'reset_change_password' => 'Passwort ändern', + 'reset_no_user_exists' => 'Es existiert kein User mit dieser Emailadresse, versuchen Sie es bitte noch einmal.', + 'reset_email_body' => 'Hallo %s, + +Sie haben diese Email erhalten, weil Sie, oder jemand anders, einen Link zum Zurücksetzen Ihres Passwortes für PHP Censor verlangt hat. + +Wenn Sie diesen Link verlangt haben, klicken Sie bitte hier, um Ihr Passwort zurückzusetzen: %ssession/reset-password/%d/%s + +Falls nicht, ignorieren Sie diese Email bitte, und es wird nichts geändert. + +Danke, + +PHP Censor', + + 'reset_email_title' => 'PHP Censor Passwort zurücksetzen für %s', + 'reset_invalid' => 'Fehlerhafte Anfrage für das Zurücksetzen eines Passwortes', + 'email_address' => 'Emailadresse', + 'login' => 'Login / Emailadresse', + 'password' => 'Passwort', + 'log_in' => 'Einloggen', + + + // Top Nav + 'toggle_navigation' => 'Navigation umschalten', + 'n_builds_pending' => '%d Builds ausstehend', + 'n_builds_running' => '%d Builds werden ausgeführt', + 'edit_profile' => 'Profil bearbeiten', + 'sign_out' => 'Ausloggen', + 'branch_x' => 'Branch: %s', + 'created_x' => 'Erstellt: %s', + 'started_x' => 'Gestartet: %s', + + // Sidebar + 'hello_name' => 'Hallo, %s', + 'dashboard' => 'Dashboard', + 'admin_options' => 'Administration', + 'add_project' => 'Projekt hinzufügen', + 'settings' => 'Einstellungen', + 'manage_users' => 'Benutzereinstellungen', + 'plugins' => 'Plugins', + 'view' => 'Ansehen', + 'build_now' => 'Jetzt bauen', + 'edit_project' => 'Projekt bearbeiten', + 'delete_project' => 'Projekt löschen', + + // Project Summary: + 'no_builds_yet' => 'Bisher noch keine Builds!', + 'x_of_x_failed' => '%d der letzten %d Builds sind fehlgeschlagen.', + 'x_of_x_failed_short' => '%d / %d fehlgeschlagen.', + 'last_successful_build' => ' Der letzte erfolgreiche Build war %s.', + 'never_built_successfully' => ' Dieses Projekt hatte bisher noch keinen erfolgreichen Build.', + 'all_builds_passed' => 'Jeder der letzten %d Builds war erfolgreich.', + 'all_builds_passed_short' => '%d / %d erfolgreich.', + 'last_failed_build' => ' Der letzte fehlgeschlagene Build war %s.', + 'never_failed_build' => ' Dieses Projekt hat keine fehlgeschlagenen Builds.', + 'view_project' => 'Projekt ansehen', + + // Timeline: + 'latest_builds' => 'Die neusten Builds', + 'pending' => 'Ausstehend', + 'running' => 'Wird ausgeführt', + 'success' => 'Erfolg', + 'failed' => 'Fehlgeschlagen', + 'manual_build' => 'Manueller Build', + + // Add/Edit Project: + 'new_project' => 'Neues Projekt', + 'project_x_not_found' => 'Projekt mit ID %d existiert nicht.', + 'project_details' => 'Projektdetails', + 'public_key_help' => 'Um Ihnen den Einstieg zu erleichtern, haben wir ein SSH-Key-Paar für dieses Projekt +generiert. Um es zu verwenden, fügen Sie einfach den folgenden Public Key im Abschnitt +"Deploy Keys" Ihrer bevorzugten Quellcodehostingplattform hinzu.', + 'select_repository_type' => 'Wählen Sie den Typ des Repositories...', + 'github' => 'GitHub', + 'bitbucket' => 'Bitbucket', + 'gitlab' => 'GitLab', + 'remote' => 'Externe URL', + 'local' => 'Lokaler Pfad', + 'hg' => 'Mercurial', + 'svn' => 'Subversion', + + 'where_hosted' => 'Wo wird Ihr Projekt gehostet?', + 'choose_github' => 'Wählen Sie ein GitHub Repository:', + + 'repo_name' => 'Name/URL (extern) oder Pfad (lokal) des Repositories', + 'project_title' => 'Projekttitel', + 'project_private_key' => 'Private Key für den Zugang zum Repository + (leer lassen für lokale und oder anonyme externe Zugriffe)', + 'build_config' => 'PHP Censor Buildkonfiguration für dieses Projekt + (falls Sie Ihrem Projektrepository kein .php-censor.yml (.phpci.yml|phpci.yml) hinzufügen können)', + 'default_branch' => 'Name des Standardbranches', + 'allow_public_status' => 'Öffentliche Statusseite und -bild für dieses Projekt einschalten?', + 'archived' => 'Archiviert', + 'archived_menu' => 'Archiviert', + 'save_project' => 'Projekt speichern', + + 'error_mercurial' => 'Mercurial Repository-URL muss mit http://, oder https:// beginnen', + 'error_remote' => 'Repository-URL muss mit git://, http://, oder https:// beginnen', + 'error_gitlab' => 'GitLab Repositoryname muss im Format "user@domain.tld:owner/repo.git" sein', + 'error_github' => 'Repositoryname muss im Format "besitzer/repo" sein', + 'error_bitbucket' => 'Repositoryname muss im Format "besitzer/repo" sein', + 'error_path' => 'Der angegebene Pfad existiert nicht', + + // View Project: + 'all_branches' => 'Alle Branches', + 'builds' => 'Builds', + 'id' => 'ID', + 'date' => 'Datum', + 'project' => 'Projekt', + 'commit' => 'Commit', + 'branch' => 'Branch', + 'status' => 'Status', + 'prev_link' => '« Vorherige', + 'next_link' => 'Nächste »', + 'public_key' => 'Public Key', + 'delete_build' => 'Build löschen', + + 'webhooks' => 'Webhooks', + 'webhooks_help_github' => 'Um für dieses Projekt automatisch einen Build zu starten, wenn neue Commits gepushed + werden, fügen Sie die untenstehende URL in der + Webhooks and Services-Sektion Ihres + GitHub Repositories als neuen "Webhook" hinzu.', + + 'webhooks_help_gitlab' => 'Um für dieses Projekt automatisch einen Build zu starten, wenn neue Commits gepushed werden, fügen Sie die untenstehende URL in der Web Hooks Sektion Ihres GitLab Repositories hinzu.', + + 'webhooks_help_bitbucket' => 'Um für dieses Projekt automatisch einen Build zu starten, wenn neue Commits gepushed werden, fügen Sie die untenstehende URL als "POST" Service in der Services-Sektion Ihres Bitbucket Repositories hinzu.', + + // View Build + 'errors' => 'Fehler', + 'information' => 'Information', + + 'build_x_not_found' => 'Build mit ID %d existiert nicht.', + 'build_n' => 'Build %d', + 'rebuild_now' => 'Build neu starten', + + + 'committed_by_x' => 'Committed von %s', + 'commit_id_x' => 'Commit: %s', + + 'chart_display' => 'Dieses Diagramm wird angezeigt, sobald der Build abgeschlossen ist.', + + 'build' => 'Build', + 'lines' => 'Zeilen', + 'comment_lines' => 'Kommentarzeilen', + 'noncomment_lines' => 'Nicht-Kommentarzeilen', + 'logical_lines' => 'Zeilen mit Logik', + 'lines_of_code' => 'Anzahl Codezeilen', + 'build_log' => 'Buildprotokoll', + 'quality_trend' => 'Qualitätstrend', + 'codeception_errors' => 'Codeception Errors', + 'phpmd_warnings' => 'PHPMD Warnings', + 'phpcs_warnings' => 'PHPCS Warnings', + 'phpcs_errors' => 'PHPCS Errors', + 'phplint_errors' => 'Lint Errors', + 'phpunit_errors' => 'PHPUnit Errors', + 'phpdoccheck_warnings' => 'Fehlende Docblocks', + 'issues' => 'Probleme', + + 'codeception' => 'Codeception', + 'phpcpd' => 'PHP Copy/Paste Detector', + 'phpcs' => 'PHP Code Sniffer', + 'phpdoccheck' => 'Fehlende Docblocks', + 'phpmd' => 'PHP Mess Detector', + 'phpspec' => 'PHP Spec', + 'phpunit' => 'PHP Unit', + 'technical_debt' => 'Technische Schulden', + 'behat' => 'Behat', + + 'codeception_feature' => 'Feature', + 'codeception_suite' => 'Suite', + 'codeception_time' => 'Zeit', + 'codeception_synopsis' => '%1$d Tests in %2$f Sekunden ausgeführt. + %3$d Fehler.', + + 'file' => 'Datei', + 'line' => 'Zeile', + 'class' => 'Klasse', + 'method' => 'Methode', + 'message' => 'Nachricht', + 'start' => 'Start', + 'end' => 'Ende', + 'from' => 'Von', + 'to' => 'Bis', + 'result' => 'Resultat', + 'ok' => 'OK', + 'took_n_seconds' => 'Benötigte %d Sekunden', + 'build_started' => 'Build gestartet', + 'build_finished' => 'Build abgeschlossen', + 'test_message' => 'Nachricht', + 'test_no_message' => 'Keine Nachricht', + 'test_success' => 'Erfolgreich: %d', + 'test_fail' => 'Fehlschläge: %d', + 'test_skipped' => 'Übersprungen: %d', + 'test_error' => 'Fehler: %d', + 'test_todo' => 'Todos: %d', + 'test_total' => '%d Test(s)', + + // Users + 'name' => 'Name', + 'password_change' => 'Passwort (leerlassen, wenn Sie es nicht ändern möchten)', + 'save' => 'Speichern »', + 'update_your_details' => 'Aktualisieren Sie Ihre Details', + 'your_details_updated' => 'Ihre Details wurden aktualisiert.', + 'add_user' => 'Benutzer hinzufügen', + 'is_admin' => 'Administrator?', + 'yes' => 'Ja', + 'no' => 'Nein', + 'edit' => 'Bearbeiten', + 'edit_user' => 'Benutzer bearbeiten', + 'delete_user' => 'Benutzer löschen', + 'user_n_not_found' => 'Benutzer mit ID %d existiert nicht.', + 'is_user_admin' => 'Ist dieser Benutzer Administrator?', + 'save_user' => 'Benutzer speichern', + + // Settings: + 'settings_saved' => 'Ihre Einstellungen wurden gespeichert.', + 'settings_check_perms' => 'Ihre Einstellungen konnten nicht gespeichert werden, bitte überprüfen Sie die + Berechtigungen Ihrer config.yml-Datei', + 'settings_cannot_write' => 'PHP Censor konnte config.yml nicht schreiben. Einstellungen könnten nicht richtig gespeichert werden, bis das Problem behoben ist.', + 'settings_github_linked' => 'Ihr GitHub-Konto wurde verknüpft.', + 'settings_github_not_linked' => 'Ihr GitHub-Konto konnte nicht verknüpft werden.', + 'build_settings' => 'Buildeinstellungen', + 'github_application' => 'GitHub-Applikation', + 'github_sign_in' => 'Bevor Sie anfangen GitHub zu verwenden, müssen Sie sich erst einloggen und PHP Censor Zugriff auf Ihr Nutzerkonto gewähren', + 'github_app_linked' => 'PHP Censor wurde erfolgreich mit Ihrem GitHub-Konto verknüpft.', + 'github_where_to_find' => 'Wo Sie diese finden...', + 'github_where_help' => 'Wenn Sie der Besitzer der Applikation sind, die Sie gerne verwenden möchten, können Sie + diese Einstellungen in Ihrem "applications + settings"-Bereich finden.', + + 'email_settings' => 'Emaileinstellungen', + 'email_settings_help' => 'Bevor PHP Censor E-Mails zum Buildstatus verschicken kann, + müssen Sie Ihre SMTP-Einstellungen unten konfigurieren', + + 'application_id' => 'Applikations-ID', + 'application_secret' => 'Applikations-Secret', + + 'smtp_server' => 'SMTP Server', + 'smtp_port' => 'SMTP Port', + 'smtp_username' => 'SMTP Benutzername', + 'smtp_password' => 'SMTP Passwort', + 'from_email_address' => 'Absenderadresse', + 'default_notification_address' => 'Standardadresse für Benachrichtigungen', + 'use_smtp_encryption' => 'SMTP-Verschlüsselung verwenden?', + 'none' => 'Keine', + 'ssl' => 'SSL', + 'tls' => 'TLS', + + 'failed_after' => 'Einen Build als fehlgeschlagen ansehen nach', + '5_mins' => '5 Minuten', + '15_mins' => '15 Minuten', + '30_mins' => '30 Minuten', + '1_hour' => '1 Stunde', + '3_hours' => '3 Stunden', + + // Plugins + 'cannot_update_composer' => 'PHP Censor kann composer.json nicht für Sie aktualisieren, da Schreibrechte benötigt werden.', + 'x_has_been_removed' => '%s wurde entfernt.', + 'x_has_been_added' => '%s wurde für Sie dem composer.json hinzugefügt und wird installiert, sobald Sie das nächste mal composer update ausführen.', + 'enabled_plugins' => 'Eingeschaltene Plugins', + 'provided_by_package' => 'Von Package bereitgestellt', + 'installed_packages' => 'Installierte Packages', + 'suggested_packages' => 'Vorgeschlagene Packages', + 'title' => 'Titel', + 'description' => 'Beschreibung', + 'version' => 'Version', + 'install' => 'Installieren »', + 'remove' => 'Entfernen »', + 'search_packagist_for_more' => 'Packagist nach mehr Packages durchsuchen', + 'search' => 'Suchen »', + + // Summary plugin + 'build-summary' => 'Zusammenfassung', + 'stage' => 'Abschnitt', + 'duration' => 'Dauer', + 'plugin' => 'Plugin', + 'stage_setup' => 'Vorbereitung', + 'stage_test' => 'Test', + 'stage_complete' => 'Vollständig', + 'stage_success' => 'Erfolg', + 'stage_failure' => 'Fehlschlag', + 'stage_broken' => 'Defekt', + 'stage_fixed' => 'Behoben', + + // Update + 'update_app' => 'Datenbank wird aktualisiert, um den Änderungen der Models zu entsprechen.', + 'updating_app' => 'Aktualisiere PHP Censor-Datenbank:', + 'not_installed' => 'PHP Censor scheint nicht installiert zu sein.', + 'install_instead' => 'Bitte installieren Sie PHP Censor stattdessen via php-censor:install.', + + // Build Plugins: + 'passing_build' => 'Durchlaufender Build', + 'failing_build' => 'Fehlschlagender Build', + 'log_output' => 'Protokollausgabe: ', + + // Error Levels: + 'critical' => 'Kritisch', + 'high' => 'Hoch', + 'normal' => 'Normal', + 'low' => 'Niedrig', + + // Plugins that generate errors: + 'php_mess_detector' => 'PHP Mess Detector', + 'php_code_sniffer' => 'PHP Code Sniffer', + 'php_unit' => 'PHP Unit', + 'php_cpd' => 'PHP Copy/Paste Detector', + 'php_docblock_checker' => 'PHP Docblock Checker', +]; diff --git a/src/PHPCensor/Languages/lang.el.php b/src/PHPCensor/Languages/lang.el.php new file mode 100644 index 0000000..c798cd7 --- /dev/null +++ b/src/PHPCensor/Languages/lang.el.php @@ -0,0 +1,291 @@ + 'Ελληνικά', + 'language' => 'Γλώσσα', + + // Log in: + 'log_in_to_app' => 'Είσοδος στο PHP Censor', + 'login_error' => 'Λάθος διεύθυνση e-mail ή κωδικός πρόσβασης', + 'forgotten_password_link' => 'Ξεχάσατε τον κωδικό σας;', + 'reset_emailed' => 'Σας έχουμε αποσταλεί ένα σύνδεσμο για να επαναφέρετε τον κωδικό πρόσβασής σας.', + 'reset_header' => ' Μην ανησυχείτε!
Απλά εισάγετε το email σας παρακάτω και θα θα σας αποστείλουμε ένα email +με ένα σύνδεσμο για να επαναφέρετε τον κωδικό πρόσβασής σας.', + 'reset_email_address' => 'Εισάγετε τη διεύθυνση e-mail σας:', + 'reset_send_email' => 'Email επαναφοράς κωδικού πρόσβασης', + 'reset_enter_password' => 'Παρακαλώ εισάγετε ένα νέο κωδικό πρόσβασης', + 'reset_new_password' => 'Νέος κωδικός πρόσβασης:', + 'reset_change_password' => 'Αλλαγή κωδικού πρόσβασης', + 'reset_no_user_exists' => 'Δεν υπάρχει χρήστης με αυτή την διεύθυνση ηλεκτρονικού ταχυδρομείου, παρακαλώ προσπαθήστε ξανά.', + 'reset_email_body' => 'Γεια %s, + +Έχετε λάβει αυτό το μήνυμα επειδή εσείς, ή κάποιος άλλος, ζήτησε επαναφορά κωδικού πρόσβασης για το PHP Censor. + +Αν ήσασταν εσείς, παρακαλώ κάντε κλικ στον παρακάτω σύνδεσμο για να επαναφέρετε τον κωδικό πρόσβασής σας: %ssession/reset-password/%d/%s + +Σε αντίθετη περίπτωση, παρακαλούμε να αγνοήσετε αυτό το μήνυμα και δεν πρόκεται να πραγματοποιηθεί η επαναφορά. + +Σας ευχαριστούμε, + +PHP Censor', + + 'reset_email_title' => 'PHP Censor Επαναφορά Κωδικού για %s', + 'reset_invalid' => 'Μη έγκυρο αίτημα επαναφοράς κωδικού πρόσβασης.', + 'email_address' => 'Διεύθυνση email', + 'login' => 'Login / Email Address', + 'password' => 'Κωδικός πρόσβασης', + 'log_in' => 'Είσοδος', + + + // Top Nav + 'toggle_navigation' => 'Εναλλαγή πλοήγησης', + 'n_builds_pending' => '%d κατασκευές σε εκκρεμότητα', + 'n_builds_running' => '%d τρέχοντες κατασκευές', + 'edit_profile' => 'Επεξεργασία Προφίλ', + 'sign_out' => 'Έξοδος', + 'branch_x' => 'Διακλάδωση: %s', + 'created_x' => 'Δημιουργήθηκε: %s', + 'started_x' => 'Ξεκίνησε: %s', + + // Sidebar + 'hello_name' => 'Γειά, %s', + 'dashboard' => 'Πίνακας ελέγχου', + 'admin_options' => 'Επιλογές Διαχειριστή', + 'add_project' => 'Προσθήκη έργου', + 'settings' => 'Ρυθμίσεις', + 'manage_users' => 'Διαχείριση χρηστών', + 'plugins' => 'Πρόσθετα', + 'view' => 'Προβολή', + 'build_now' => 'Κατασκευή τώρα', + 'edit_project' => 'Επεξεργασία Έργου', + 'delete_project' => 'Διαγραφή Έργου', + + // Project Summary: + 'no_builds_yet' => 'Καμία κατασκευή ακόμα!', + 'x_of_x_failed' => '%d από τις %d τελευταίες κατασκευές απέτυχαν', + 'x_of_x_failed_short' => '%d / %d απέτυχαν.', + 'last_successful_build' => 'Η τελευταία επιτυχής κατασκεύη ήταν %s.', + 'never_built_successfully' => 'Αυτό το έργο δεν έχει ποτέ κατασκευαστεί με επιτυχία.', + 'all_builds_passed' => 'Όλες από τις %d κατασκευές πέρασαν', + 'all_builds_passed_short' => '%d / %d πέρασαν.', + 'last_failed_build' => 'H τελευταία αποτυχημένη κατασκευή ήταν %s.', + 'never_failed_build' => 'Το έργο αυτό δεν παρέλειψε ποτέ μια κατασκευή.', + 'view_project' => 'Προβολή του έργου', + + // Timeline: + 'latest_builds' => 'Τελευταίες κατασκευές', + 'pending' => 'Σε εκκρεμότητα', + 'running' => 'Τρέχοντα', + 'success' => 'Επιτυχία', + 'failed' => 'Αποτυχία', + 'manual_build' => 'Χειροκίνητη κατασκευή', + + // Add/Edit Project: + 'new_project' => 'Νέο έργο', + 'project_x_not_found' => 'Το έργο με αριθμό %d δεν υπάρχει', + 'project_details' => 'Στοιχεία Έργου', + 'public_key_help' => 'Για να είναι πιο εύκολο να ξεκινήσετε, έχουμε δημιουργήσει ένα ζεύγος κλειδιών SSH για να χρησιμοποιήσετε +για το έργο αυτό. Για να τα χρησιμοποιήσετε, απλά προσθέστε το ακόλουθο δημόσιο κλειδί στο τμήμα "ανάπτυξη κλειδιών" +του επιλεγμένου πηγαίου κώδικα της πλατφόρμας φιλοξενίας σας.', + 'select_repository_type' => 'Επιλέξτε τον τύπο του αποθετηρίου...', + 'github' => 'GitHub', + 'bitbucket' => 'Bitbucket', + 'gitlab' => 'GitLab', + 'remote' => 'Απομακρυσμένη διεύθυνση URL', + 'local' => 'Τοπική Διαδρομή', + 'hg' => 'Ευμετάβλητο', + + 'where_hosted' => 'Πού φιλοξενείται το έργο σας;', + 'choose_github' => 'Επιλέξτε ένα αποθετήριο GitHub:', + + 'repo_name' => 'Αποθετήριο Όνομα / διεύθυνση URL (Απομακρυσμένα) ή Διαδρομή (Τοπικά)', + 'project_title' => 'Τίτλος Έργου', + 'project_private_key' => 'Ιδιωτικό κλειδί για πρόσβαση σε αποθετήριο +(αφήστε κενό για την τοπική ή / και ανώνυμα απομακρυσμένα)', + 'build_config' => 'Kατασκευή διαμόρφωσης PHP Censor για αυτό το έργο +(αν δεν μπορείτε να προσθέσετε ένα αρχείο .php-censor.yml (.phpci.yml|phpci.yml) στο αποθετήριο έργων)', + 'default_branch' => 'Προκαθορισμένο όνομα διακλάδωσης', + 'allow_public_status' => 'Ενεργοποίηση της σελίδας δημόσιας κατάστασης και την εικόνα για το έργο αυτό;', + 'archived' => 'Archived', + 'archived_menu' => 'Archived', + 'save_project' => 'Αποθήκευση έργου', + + 'error_mercurial' => 'Ο σύνδεσμος URL του ευμετάβλητου αποθετηρίου πρέπει να ξεκινάει με http:// ή https://', + 'error_remote' => 'Ο σύνδεσμος URL του αποθετηρίου πρέπει να ξεκινάει με git://, http:// ή https://', + 'error_gitlab' => 'Το όνομα του αποθετηρίου GitLab πρέπει να είναι της μορφής "user@domain.tld:owner/repo.git"', + 'error_github' => 'Το όνομα του αποθετηρίου θα πρέπει να είναι της μορφής "owner/repo" ιδιοκτήτης/αποθετήριο', + 'error_bitbucket' => 'Το όνομα του αποθετηρίου θα πρέπει να είναι της μορφής "owner/repo" ιδιοκτήτης/αποθετήριο', + 'error_path' => 'Η διαδρομή που καθορίσατε δεν υπάρχει.', + + // View Project: + 'all_branches' => 'Όλες οι διακλαδώσεις', + 'builds' => 'Κατασκευές', + 'id' => 'Αριθμός αναγνώρισης', + 'date' => 'Date', + 'project' => 'Έργο', + 'commit' => 'Συνεισφορά', + 'branch' => 'Διακλάδωση', + 'status' => 'Κατάσταση', + 'prev_link' => '« Προηγούμενο', + 'next_link' => 'Επόμενο «', + 'public_key' => 'Δημόσιο κλειδί', + 'delete_build' => 'Διαγραφή κλειδιού', + + 'webhooks' => 'Webhooks', + 'webhooks_help_github' => 'Για την αυτόματη κατασκευή αυτού του έργου όταν υπάρχουν νέες συνεισφορές, προσθέστε τη διεύθυνση URL παρακάτω +ως ένα νέο "Webhook" στο τμήμα Webhooks +and Services του GitHub αποθετηρίου σας.', + + 'webhooks_help_gitlab' => 'Για την αυτόματη κατασκευή αυτού του έργου όταν υπάρχουν νέες συνεισφορές, προσθέστε την διεύθυνση URL παρακάτω +σαν "WebHook URL" στο τμήμα Web Hooks του GitLab αποθετηρίου σας.', + + 'webhooks_help_bitbucket' => 'Για την αυτόματη κατασκευή αυτού του έργου όταν υπάρχουν νέες συνεισφορές, προσθέστε τη διεύθυνση URL παρακάτω +ως μια υπηρεσία "POST" στο τμήμα +Services του Bitbucket αποθετηρίου σας.', + + // View Build + 'build_x_not_found' => 'Η κατασκευή με αριθμό %d δεν υπάρχει', + 'build_n' => 'Κατασκευή %d', + 'rebuild_now' => 'Αναδόμηση τώρα', + + + 'committed_by_x' => 'Έγινε συνεισφορά από %s', + 'commit_id_x' => 'Συνεισφορά: %s', + + 'chart_display' => 'Αυτό το γράφημα θα εμφανιστεί μόλις η κατασκευή έχει ολοκληρωθεί.', + + 'build' => 'Κατασκευή', + 'lines' => 'Γραμμές', + 'comment_lines' => 'Γραμμές σχολίων', + 'noncomment_lines' => 'Μη σχολιασμένες γραμμές', + 'logical_lines' => 'Λογικές γραμμές', + 'lines_of_code' => 'Γραμμές Κώδικα', + 'build_log' => 'Αρχείο καταγραφής κατασκευών', + 'quality_trend' => 'Ποιότητα τρέντ', + 'codeception_errors' => 'Λάθη Codeception', + 'phpmd_warnings' => 'Προειδοποιήσεις PHPMD', + 'phpcs_warnings' => 'Προειδοποιήσεις PHPCS ', + 'codeception_errors' => 'Λάθη Codeception', + 'phpcs_errors' => 'Λάθη PHPCS', + 'phplint_errors' => 'Λάθη Lint', + 'phpunit_errors' => 'Λάθη PHPUnit ', + 'phpdoccheck_warnings' => 'Χαμένα Docblocks', + 'issues' => 'Θέματα', + + 'codeception' => 'Codeception', + 'phpcpd' => 'PHP Ανιχνευτής Αντιγραφής/Επικόλλησης', + 'phpcs' => 'Sniffer Κώδικα PHP', + 'phpdoccheck' => 'Χαμένα Docblocks', + 'phpmd' => 'Aνιχνευτής PHP Mess', + 'phpspec' => 'PHP Spec', + 'phpunit' => 'PHP Unit', + + 'file' => 'Αρχείο', + 'line' => 'Γραμμή', + 'class' => 'Κατηγορία', + 'method' => 'Μέθοδος', + 'message' => 'Μήνυμα', + 'start' => 'Έναρξη', + 'end' => 'Τέλος', + 'from' => 'Από', + 'to' => 'Προς', + 'result' => 'Αποτέλεσμα', + 'ok' => 'ΟΚ', + 'took_n_seconds' => 'Χρειάστηκαν %d δευτερόλεπτα', + 'build_started' => 'Η κατασκευή άρχισε', + 'build_finished' => 'Η κατασκευή ολοκληρώθηκε', + 'test_message' => 'Message', + 'test_no_message' => 'No message', + 'test_success' => 'Successful: %d', + 'test_fail' => 'Failures: %d', + 'test_skipped' => 'Skipped: %d', + 'test_error' => 'Errors: %d', + 'test_todo' => 'Todos: %d', + 'test_total' => '%d test(s)', + + // Users + 'name' => 'Όνομα', + 'password_change' => 'Κωδικός πρόσβασης (αφήστε κενό αν δεν θέλετε να αλλάξετε)', + 'save' => 'Αποθήκευση »', + 'update_your_details' => 'Ενημερώστε τα στοιχεία σας', + 'your_details_updated' => 'Τα στοιχεία σας έχουν ενημερωθεί.', + 'add_user' => 'Προσθήκη χρήστη', + 'is_admin' => 'Είναι διαχειριστής;', + 'yes' => 'Ναι', + 'no' => 'Όχι', + 'edit' => 'Επεξεργασία', + 'edit_user' => 'Επεξεργασία χρήστη', + 'delete_user' => 'Διαγραφή χρήστη', + 'user_n_not_found' => 'Ο χρήστης με αριθμό %d δεν υπάρχει.', + 'is_user_admin' => 'Είναι αυτός ο χρήστης διαχειριστής;', + 'save_user' => 'Αποθήκευση χρήστη', + + // Settings: + 'settings_saved' => 'Οι ρυθμίσεις σας έχουν αποθηκευτεί.', + 'settings_check_perms' => 'Οι ρυθμίσεις σας δεν αποθηκεύτηκαν, ελέγξτε τα δικαιώματα του αρχείου σας config.yml.', + 'settings_cannot_write' => 'Το PHP Censor δεν μπορεί να γράψει στο αρχείο config.yml, οι ρυθμίσεις ενδέχεται να μην αποθηκευτούν σωστά +μέχρι να διορθωθεί.', + 'settings_github_linked' => 'Ο λογαριασμός σας GitHub έχει συνδεθεί.', + 'settings_github_not_linked' => 'Ο λογαριασμός σας Github δεν μπόρεσε να συνδεθεί.', + 'build_settings' => 'Ρυθμίσεις κατασκευής', + 'github_application' => 'GitHub Εφαρμογή', + 'github_sign_in' => 'Πριν αρχίσετε να χρησιμοποιείτε το GitHub, θα πρέπει να συνδεθείται και να δώσει +το PHP Censor πρόσβαση στο λογαριασμό σας.', + 'github_app_linked' => 'Το PHP Censor συνδέθηκε με επιτυχία με το λογαριασμό Github.', + 'github_where_to_find' => 'Πού να βρείτε αυτά ...', + 'github_where_help' => 'Εάν έχετε στην κατοχή σας την εφαρμογή που θέλετε να χρησιμοποιήσετε, μπορείτε να βρείτε αυτές τις πληροφορίες στην περιοχή +Ρυθμίσεις εφαρμογών ', + + 'email_settings' => 'Ρυθμίσεις email', + 'email_settings_help' => 'Πριν το PHP Censor μπορεί να στείλει μηνύματα ηλεκτρονικού ταχυδρομείου για την κατάσταση κατασκευής, +θα πρέπει να διαμορφώσετε τις ρυθμίσεις SMTP παρακάτω.', + + 'application_id' => 'Αναγνωριστικό εφαρμογής', + 'application_secret' => 'Μυστική Εφαρμογή', + + 'smtp_server' => 'Διακομισής SMTP', + 'smtp_port' => 'Θύρα SMTP', + 'smtp_username' => 'Όνομα χρήστη SMTP', + 'smtp_password' => 'Κωδικός πρόσβασης SMTP', + 'from_email_address' => 'Εmail διεύθυνση αποστολέα', + 'default_notification_address' => 'Προεπιλεγμένη διεύθυνση ειδοποίησης ηλεκτρονικού ταχυδρομείου ', + 'use_smtp_encryption' => 'Εφαρμογή SMTP κρυπτογράφησης;', + 'none' => 'Κανένα', + 'ssl' => 'Κρυπτογράφηση SSL', + 'tls' => 'Κρυπτογράφηση TLS', + + 'failed_after' => 'Να θεωρηθεί μια κατασκευή αποτυχημένη μετά ', + '5_mins' => '5 λεπτά', + '15_mins' => '15 λεπτά', + '30_mins' => '30 λεπτά', + '1_hour' => '1 ώρα', + '3_hours' => '3 ώρες', + + // Plugins + 'cannot_update_composer' => 'To PHP Censor δεν μπορεί να ενημερώσει to composer.json για σας, γιατί δεν είναι εγγράψιμο.', + 'x_has_been_removed' => '%s έχει αφαιρεθεί.', + 'x_has_been_added' => '%s προσθέιηκε στο αρχείο composer.json για εσάς και θα εγκατασταθεί την επόμενη φορά +που θα τρέξετε την ενημέρωση για το composer.', + 'enabled_plugins' => 'Ενεργοποιημένα πρόσθετα', + 'provided_by_package' => 'Παρέχεται από πακέτο', + 'installed_packages' => 'Εγκατεστημένα πακέτα', + 'suggested_packages' => 'Προτεινόμενα πακέτα', + 'title' => 'Τίτλος', + 'description' => 'Περιγραφή', + 'version' => 'Έκδοση', + 'install' => 'Εγκατάσταση »', + 'remove' => 'Αφαίρεση »', + 'search_packagist_for_more' => 'Αναζήτηση στο Packagist για περισσότερα πακέτα', + 'search' => 'Αναζήτηση »', + + // Update + 'update_app' => 'Ενημέρωστε την βάση δεδομένων ώστε να αντικατοπτρίζει τροποποιημένα μοντέλα.', + 'updating_app' => 'Γίνεται ενημέρωση της βάσης δεδομένων PHP Censor:', + 'not_installed' => 'Το PHP Censor δεν φένεται να είναι εγκατεστημένο', + 'install_instead' => 'Παρακαλούμε εγκαταστήστε το PHP Censor καλύτερα με το php-censor:install αντ \'αυτού.', + + // Build Plugins: + 'passing_build' => 'Επιτυχημένη κατασκευή', + 'failing_build' => 'Αποτυχημένη κατασκευή', + 'log_output' => 'Σύνδεση εξόδου:', +]; diff --git a/src/PHPCensor/Languages/lang.en.php b/src/PHPCensor/Languages/lang.en.php new file mode 100644 index 0000000..a8b17bb --- /dev/null +++ b/src/PHPCensor/Languages/lang.en.php @@ -0,0 +1,426 @@ + 'English', + 'language' => 'Language', + 'per_page' => 'Items per page', + 'default' => 'Default', + + // Log in: + 'log_in_to_app' => 'Log in to PHP Censor', + 'login_error' => 'Incorrect email address or password', + 'forgotten_password_link' => 'Forgotten your password?', + 'reset_emailed' => 'We\'ve emailed you a link to reset your password.', + 'reset_header' => 'Don\'t worry!
Just enter your email address below and we\'ll email + you a link to reset your password.', + 'reset_email_address' => 'Enter your email address:', + 'reset_send_email' => 'Email password reset', + 'reset_enter_password' => 'Please enter a new password', + 'reset_new_password' => 'New password:', + 'reset_change_password' => 'Change password', + 'reset_no_user_exists' => 'No user exists with that email address, please try again.', + 'reset_email_body' => 'Hi %s, + +You have received this email because you, or someone else, has requested a password reset for PHP Censor. + +If this was you, please click the following link to reset your password: %ssession/reset-password/%d/%s + +Otherwise, please ignore this email and no action will be taken. + +Thank you, + +PHP Censor', + + 'reset_email_title' => 'PHP Censor Password Reset for %s', + 'reset_invalid' => 'Invalid password reset request.', + 'email_address' => 'Email Address', + 'login' => 'Login / Email Address', + 'password' => 'Password', + 'remember_me' => 'Remember me', + 'log_in' => 'Log in', + + + // Top Nav + 'toggle_navigation' => 'Toggle Navigation', + 'n_builds_pending' => '%d builds pending', + 'n_builds_running' => '%d builds running', + 'edit_profile' => 'Edit Profile', + 'sign_out' => 'Sign Out', + 'branch_x' => 'Branch: %s', + 'created_x' => 'Created: %s', + 'started_x' => 'Started: %s', + 'environment_x' => 'Environment: %s', + + // Sidebar + 'hello_name' => 'Hello, %s', + 'dashboard' => 'Dashboard', + 'admin_options' => 'Admin Options', + 'add_project' => 'Add Project', + 'project_groups' => 'Project Groups', + 'settings' => 'Settings', + 'manage_users' => 'Manage Users', + 'plugins' => 'Plugins', + 'view' => 'View', + 'build_now' => 'Build now', + 'build_now_debug' => 'Build now with debug', + 'edit_project' => 'Edit Project', + 'delete_project' => 'Delete Project', + + // Project Summary: + 'no_builds_yet' => 'No builds yet!', + 'x_of_x_failed' => '%d out of the last %d builds failed.', + 'x_of_x_failed_short' => '%d / %d failed.', + 'last_successful_build' => ' The last successful build was %s.', + 'never_built_successfully' => ' This project has never built successfully.', + 'all_builds_passed' => 'All of the last %d builds passed.', + 'all_builds_passed_short' => '%d / %d passed.', + 'last_failed_build' => ' The last failed build was %s.', + 'never_failed_build' => ' This project has never failed a build.', + 'view_project' => 'View Project', + 'projects_with_build_errors' => 'Build errors', + 'no_build_errors' => 'No build errors', + + // Timeline: + 'latest_builds' => 'Latest Builds', + 'pending' => 'Pending', + 'running' => 'Running', + 'success' => 'Success', + 'failed' => 'Failed', + 'failed_allowed' => 'Failed (Allowed)', + 'error' => 'Error', + 'skipped' => 'Skipped', + 'trace' => 'Stack trace', + 'manual_build' => 'Manual Build', + + // Add/Edit Project: + 'new_project' => 'New Project', + 'project_x_not_found' => 'Project with ID %d does not exist.', + 'project_details' => 'Project Details', + 'public_key_help' => 'To make it easier to get started, we\'ve generated an SSH 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 chosen source code hosting platform.', + 'select_repository_type' => 'Select repository type...', + 'github' => 'GitHub', + 'bitbucket' => 'Bitbucket', + 'gitlab' => 'GitLab', + 'remote' => 'Remote URL', + 'local' => 'Local Path', + 'hg' => 'Mercurial', + 'svn' => 'Subversion', + + 'where_hosted' => 'Where is your project hosted?', + 'choose_github' => 'Choose a GitHub repository:', + + 'repo_name' => 'Repository Name / URL (Remote) or Path (Local)', + 'project_title' => 'Project Title', + 'project_private_key' => 'Private key to use to access repository + (leave blank for local and/or anonymous remotes)', + 'build_config' => 'PHP Censor build config for this project + (if you cannot add a .php-censor.yml (.phpci.yml|phpci.yml) file in the project repository)', + 'default_branch' => 'Default branch name', + 'default_branch_only' => 'Build default branch only', + 'allow_public_status' => 'Enable public status page and image for this project?', + 'archived' => 'Archived', + 'archived_menu' => 'Archived', + 'save_project' => 'Save Project', + 'environments_label' => 'Environments (yaml)', + + 'error_mercurial' => 'Mercurial repository URL must be start with http:// or https://', + 'error_remote' => 'Repository URL must be start with git://, http:// or https://', + 'error_gitlab' => 'GitLab Repository name must be in the format "user@domain.tld:owner/repo.git"', + 'error_github' => 'Repository name must be in the format "owner/repo"', + 'error_bitbucket' => 'Repository name must be in the format "owner/repo"', + 'error_path' => 'The path you specified does not exist.', + + // View Project: + 'all_branches' => 'All Branches', + 'all' => 'All', + 'builds' => 'Builds', + 'id' => 'ID', + 'date' => 'Date', + 'project' => 'Project', + 'commit' => 'Commit', + 'branch' => 'Branch', + 'environment' => 'Environment', + 'status' => 'Status', + 'prev_link' => '« Prev', + 'next_link' => 'Next »', + 'public_key' => 'Public Key', + 'delete_build' => 'Delete Build', + 'build_source' => 'Build source', + + 'source_unknown' => 'Unknown', + 'source_manual_web' => 'Manual (from Web)', + 'source_manual_console' => 'Manual (from CLI)', + 'source_periodical' => 'Periodical', + 'source_webhook' => 'Webhook', + + 'webhooks' => 'Webhooks', + 'webhooks_help_github' => 'To automatically build this project when new commits are pushed, add the URL below + as a new "Webhook" in the Webhooks + and Services section of your GitHub repository.', + + 'webhooks_help_gitlab' => 'To automatically build this project when new commits are pushed, add the URL below + as a "WebHook URL" in the Web Hooks section of your GitLab repository.', + + 'webhooks_help_bitbucket' => 'To automatically build this project when new commits are pushed, add the URL below + as a "POST" service in the + + Services section of your Bitbucket repository.', + + // Project Groups + 'group_projects' => 'Project groups', + 'project_group' => 'Project group', + 'group_count' => 'Projects count', + 'group_edit' => 'Edit', + 'group_delete' => 'Delete', + 'group_add' => 'Add Group', + 'group_add_edit' => 'Add / Edit Group', + 'group_title' => 'Group Title', + 'group_save' => 'Save Group', + + // View Build + 'errors' => 'Errors', + 'information' => 'Information', + + 'build_x_not_found' => 'Build with ID %d does not exist.', + 'build_n' => 'Build %d', + 'rebuild_now' => 'Rebuild Now', + + + 'committed_by_x' => 'Committed by %s', + 'commit_id_x' => 'Commit: %s', + + 'chart_display' => 'This chart will display once the build has completed.', + + 'build' => 'Build', + 'lines' => 'Lines', + 'comment_lines' => 'Comment lines', + 'noncomment_lines' => 'Non-Comment lines', + 'logical_lines' => 'Logical Lines', + 'lines_of_code' => 'Lines of code', + 'build_log' => 'Build log', + 'quality_trend' => 'Quality trend', + 'codeception_errors' => 'Codeception errors', + 'phpmd_warnings' => 'PHPMD warnings', + 'phpcs_warnings' => 'PHPCS warnings', + 'phpcs_errors' => 'PHPCS errors', + 'phplint_errors' => 'Lint errors', + 'phpunit_errors' => 'PHPUnit errors', + 'phpcpd_warnings' => 'PHP Copy/Paste Detector warnings', + 'phpdoccheck_warnings' => 'Missing docblocks', + 'issues' => 'Issues', + 'merged_branches' => 'Merged branches', + + 'phpcpd' => 'PHP Copy/Paste Detector', + 'phpcs' => 'PHP Code Sniffer', + 'phpdoccheck' => 'Missing Docblocks', + 'phpmd' => 'PHP Mess Detector', + 'phpspec' => 'PHP Spec', + 'phpunit' => 'PHP Unit', + + 'codeception_feature' => 'Feature', + 'codeception_suite' => 'Suite', + 'codeception_time' => 'Time', + 'codeception_synopsis' => '%1$d tests carried out in %2$f seconds. + %3$d failures.', + 'suite' => 'Suite', + 'test' => 'Test', + 'file' => 'File', + 'line' => 'Line', + 'class' => 'Class', + 'method' => 'Method', + 'message' => 'Message', + 'start' => 'Start', + 'end' => 'End', + 'from' => 'From', + 'to' => 'To', + 'result' => 'Result', + 'ok' => 'OK', + 'took_n_seconds' => 'Took %d seconds', + 'build_started' => 'Build Started', + 'build_finished' => 'Build Finished', + 'test_message' => 'Message', + 'test_no_message' => 'No message', + 'test_success' => 'Successful: %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', + 'password_change' => 'Password (leave blank if you don\'t want to change)', + 'save' => 'Save »', + 'update_your_details' => 'Update your details', + 'your_details_updated' => 'Your details have been updated.', + 'add_user' => 'Add User', + 'is_admin' => 'Is Admin?', + 'yes' => 'Yes', + 'no' => 'No', + 'edit' => 'Edit', + 'edit_user' => 'Edit User', + 'delete_user' => 'Delete User', + 'user_n_not_found' => 'User with ID %d does not exist.', + 'is_user_admin' => 'Is this user an administrator?', + 'save_user' => 'Save User', + + // Settings: + 'settings_saved' => 'Your settings have been saved.', + 'settings_check_perms' => 'Your settings could not be saved, check the permissions of your config.yml file.', + 'settings_cannot_write' => 'PHP Censor cannot write to your config.yml file, settings may not be saved properly + until this is rectified.', + 'settings_github_linked' => 'Your GitHub account has been linked.', + 'settings_github_not_linked' => 'Your GitHub account could not be linked.', + 'build_settings' => 'Build Settings', + 'github_application' => 'GitHub Application', + 'github_sign_in' => 'Before you can start using GitHub, you need to sign in and grant + PHP Censor access to your account.', + 'github_app_linked' => 'PHP Censor is successfully linked to GitHub account.', + 'github_where_to_find' => 'Where to find these...', + 'github_where_help' => 'If you own the application you would like to use, you can find this information within your + applications settings area.', + + 'email_settings' => 'Email Settings', + 'email_settings_help' => 'Before PHP Censor can send build status emails, + you need to configure your SMTP settings below.', + + 'application_id' => 'Application ID', + 'application_secret' => 'Application Secret', + + 'smtp_server' => 'SMTP Server', + 'smtp_port' => 'SMTP Port', + 'smtp_username' => 'SMTP Username', + 'smtp_password' => 'SMTP Password', + 'from_email_address' => 'From Email Address', + 'default_notification_address' => 'Default Notification Email Address', + 'use_smtp_encryption' => 'Use SMTP Encryption?', + 'none' => 'None', + 'ssl' => 'SSL', + 'tls' => 'TLS', + + 'failed_after' => 'Consider a build failed after', + '5_mins' => '5 Minutes', + '15_mins' => '15 Minutes', + '30_mins' => '30 Minutes', + '1_hour' => '1 Hour', + '3_hours' => '3 Hours', + + // Plugins + 'cannot_update_composer' => 'PHP Censor cannot update composer.json for you as it is not writable.', + 'x_has_been_removed' => '%s has been removed.', + 'x_has_been_added' => '%s has been added to composer.json for you and will be installed next time + you run composer update.', + 'enabled_plugins' => 'Enabled Plugins', + 'provided_by_package' => 'Provided By Package', + 'installed_packages' => 'Installed Packages', + 'suggested_packages' => 'Suggested Packages', + 'title' => 'Title', + 'description' => 'Description', + 'version' => 'Version', + 'install' => 'Install »', + 'remove' => 'Remove »', + 'search_packagist_for_more' => 'Search Packagist for more packages', + 'search' => 'Search »', + + // Summary plugin + 'build-summary' => 'Summary', + 'stage' => 'Stage', + 'duration' => 'Duration', + 'seconds' => 'sec.', + 'plugin' => 'Plugin', + 'stage_setup' => 'Setup', + 'stage_test' => 'Test', + 'stage_deploy' => 'Deploy', + 'stage_complete' => 'Complete', + 'stage_success' => 'Success', + 'stage_failure' => 'Failure', + 'stage_broken' => 'Broken', + 'stage_fixed' => 'Fixed', + 'severity' => 'Severity', + + 'all_plugins' => 'All plugins', + 'all_severities' => 'All severities', + 'filters' => 'Filters', + 'errors_selected' => 'Errors selected', + + 'build_details' => 'Build Details', + 'commit_details' => 'Commit Details', + 'committer' => 'Committer', + 'commit_message' => 'Commit Message', + 'timing' => 'Timing', + 'created' => 'Created', + 'started' => 'Started', + 'finished' => 'Finished', + + // Update + 'update_app' => 'Update the database to reflect modified models.', + 'updating_app' => 'Updating PHP Censor database: ', + 'not_installed' => 'PHP Censor does not appear to be installed.', + 'install_instead' => 'Please install PHP Censor via php-censor:install instead.', + + // Create Build Command + 'add_to_queue_failed' => 'Build created successfully, but failed to add to build queue. This usually happens + when PHP Censor is set to use a beanstalkd server that does not exist, + or your beanstalkd server has stopped.', + + // Build Plugins: + 'passing_build' => 'Passing Build', + 'failing_build' => 'Failing Build', + 'log_output' => 'Log Output: ', + + + // Error Levels: + 'critical' => 'Critical', + 'high' => 'High', + 'normal' => 'Normal', + 'low' => 'Low', + + // Plugins that generate errors: + 'php_mess_detector' => 'PHP Mess Detector', + 'php_code_sniffer' => 'PHP Code Sniffer', + 'php_unit' => 'PHP Unit', + 'php_cpd' => 'PHP Copy/Paste Detector', + 'php_docblock_checker' => 'PHP Docblock Checker', + 'composer' => 'Composer', + 'php_loc' => 'PHP LOC', + 'php_parallel_lint' => 'PHP Parallel Lint', + 'email' => 'Email', + 'atoum' => 'Atoum', + 'behat' => 'Behat', + 'campfire' => 'Campfire', + 'clean_build' => 'Clean Build', + 'codeception' => 'Codeception', + 'copy_build' => 'Copy Build', + 'deployer' => 'Deployer', + 'env' => 'Env', + 'grunt' => 'Grunt', + 'hipchat_notify' => 'Hipchat', + 'irc' => 'IRC', + 'lint' => 'Lint', + 'mysql' => 'MySQL', + 'package_build' => 'Package Build', + 'pdepend' => 'PDepend', + 'pgsql' => 'PostgreSQL', + 'phar' => 'Phar', + 'phing' => 'Phing', + 'php_cs_fixer' => 'PHP Coding Standards Fixer', + 'php_spec' => 'PHP Spec', + 'shell' => 'Shell', + 'slack_notify' => 'Slack', + 'technical_debt' => 'Technical Debt', + 'xmpp' => 'XMPP', + 'security_checker' => 'SensioLabs Security Checker', + + 'confirm_message' => 'Item will be permanently deleted. Are you sure?', + 'confirm_title' => 'Item delete confirmation', + 'confirm_ok' => 'Delete', + 'confirm_cancel' => 'Cancel', + 'confirm_success' => 'Item successfully deleted.', + 'confirm_failed' => 'Deletion failed! Server says: ', + + 'public_status_title' => 'Public status', + 'public_status_image' => 'Status image', + 'public_status_page' => 'Public status page', +]; diff --git a/src/PHPCensor/Languages/lang.es.php b/src/PHPCensor/Languages/lang.es.php new file mode 100644 index 0000000..20a22b5 --- /dev/null +++ b/src/PHPCensor/Languages/lang.es.php @@ -0,0 +1,285 @@ + 'Español', + 'language' => 'Lenguaje', + + // Log in: + 'log_in_to_app' => 'Ingresar a PHP Censor', + '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 PHP Censor + +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, + +PHP Censor', + + 'reset_email_title' => 'Reiniciar contraseña de PHP Censor 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', + '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 PHP Censor para builds del proyecto + (en caso que no puedas agregar el archivo .php-censor.yml (.phpci.yml|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', + 'archived_menu' => 'Archivado', + '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_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' => 'PHP Censor 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 PHP Censor.', + 'github_app_linked' => 'PHP Censor 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 PHP Censor 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' => 'PHP Censor 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 »', + + // Update + 'update_app' => 'Actuliza la base de datos para reflejar los modelos actualizados.', + 'updating_app' => 'Actualizando base de datos PHP Censor: ', + 'not_installed' => 'PHP Censor no está instalado.', + 'install_instead' => 'Por favor, instala PHP Censor via php-censor:install.', + + // Build Plugins: + 'passing_build' => 'Build Exitoso', + 'failing_build' => 'Build Fallido', + 'log_output' => 'Log de Salida: ', +]; diff --git a/src/PHPCensor/Languages/lang.fr.php b/src/PHPCensor/Languages/lang.fr.php new file mode 100644 index 0000000..c4d56dd --- /dev/null +++ b/src/PHPCensor/Languages/lang.fr.php @@ -0,0 +1,416 @@ + 'Français', + 'language' => 'Langue', + 'per_page' => 'Item par page', + 'default' => 'Défaut', + + // Log in: + 'log_in_to_app' => 'Connectez-vous à PHP Censor', + 'login_error' => 'Adresse email ou mot de passe invalide', + 'forgotten_password_link' => 'Mot de passe oublié ?', + 'reset_emailed' => 'Nous vous avons envoyé un email avec un lien pour réinitialiser votre mot de passe.', + 'reset_header' => 'Pas d\'inquiétude
Entrez simplement votre adresse email ci-dessous + et nous vous enverrons un message avec un lien pour réinitialiser votre mot de passe.', + 'reset_email_address' => 'Entrez votre adresse email:', + 'reset_send_email' => 'Envoyer le mail', + 'reset_enter_password' => 'Veuillez entrer un nouveau mot de passe', + 'reset_new_password' => 'Nouveau mot de passe :', + 'reset_change_password' => 'Modifier le mot de passe', + 'reset_no_user_exists' => 'Il n\'existe aucun utilisateur avec cette adresse email, merci de réessayer.', + 'reset_email_body' => 'Bonjour %s, + +Vous avez reçu cet email parce qu\'une demande de réinitialisation de mot de passe a été faite pour votre compte PHP Censor. + +Si c\'est bien vous, merci de cliquer sur le lien suivant pour réinitialiser votre mot de passe : %ssession/reset-password/%d/%s + +Sinon, merci d\'ignorer ce message. + +Merci, + +PHP Censor', + + 'reset_email_title' => 'Réinitialisation du mot de passe PHP Censor 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', + + + // Top Nav + 'toggle_navigation' => 'Afficher/cacher la navigation', + 'n_builds_pending' => '%d builds en attente', + 'n_builds_running' => '%d builds en cours d\'exécution', + 'edit_profile' => 'Éditer le profil', + 'sign_out' => 'Déconnexion', + 'branch_x' => 'Branche : %s', + 'created_x' => 'Créé à : %s', + 'started_x' => 'Démarré à : %s', + + // Sidebar + 'hello_name' => 'Salut %s', + 'dashboard' => 'Tableau de bord', + 'admin_options' => 'Options d\'administration', + 'add_project' => 'Ajouter un projet', + 'project_groups' => 'Groupes de projets', + 'settings' => 'Paramètres', + 'manage_users' => 'Gérer les utilisateurs', + 'plugins' => 'Plugins', + 'view' => 'Voir', + 'build_now' => 'Démarrer le build', + 'edit_project' => 'Éditer le projet', + 'delete_project' => 'Supprimer le projet', + + // Project Summary: + 'no_builds_yet' => 'Aucun build pour le moment !', + 'x_of_x_failed' => '%d des %d derniers builds ont échoué.', + 'x_of_x_failed_short' => '%d échecs / %d.', + 'last_successful_build' => ' Le dernier build réussi date du %s.', + 'never_built_successfully' => ' Aucun build de ce projet n\'a réussi.', + 'all_builds_passed' => 'Les %d derniers builds ont réussi.', + 'all_builds_passed_short' => '%d réussites / %d.', + 'last_failed_build' => ' Le dernier build en échec date du %s.', + 'never_failed_build' => ' Aucun build de ce projet n\'a échoué.', + 'view_project' => 'Voir le projet', + + // Timeline: + 'latest_builds' => 'Derniers builds', + 'pending' => 'En attente', + 'running' => 'En cours', + 'success' => 'Terminé', + 'failed' => 'Échoué', + 'error' => 'Error', + 'skipped' => 'Skipped', + 'trace' => 'Stack trace', + 'manual_build' => 'Build manuel', + + // Add/Edit Project: + 'new_project' => 'Nouveau Projet', + 'project_x_not_found' => 'Il n\'existe pas de Projet avec l\'ID %d.', + 'project_details' => 'Détails du Projet', + 'public_key_help' => 'Pour pouvoir démarrer plus facilement, nous avons généré une paire de clés SSH à utiliser avec ce projet. + Pour l\'utiliser, il faut simplement ajouter la clé publique dans la section "Clés de déploiement" + de votre outil d\'hébergement de code.', + 'select_repository_type' => 'Sélectionnez le type de dépôt...', + 'github' => 'GitHub', + 'bitbucket' => 'Bitbucket', + 'gitlab' => 'GitLab', + 'remote' => 'URL distante', + 'local' => 'Chemin local', + 'hg' => 'Mercurial', + 'svn' => 'Subversion', + + 'where_hosted' => 'Où est hébergé votre projet ?', + 'choose_github' => 'Choisissez un dépôt GitHub :', + + 'repo_name' => 'Nom du dépôt / URL (distant) ou chemin (local)', + 'project_title' => 'Titre du projet', + 'project_private_key' => 'Clé privée à utiliser pour accéder au dépôt + (laissez le champ vide pour les dépôts locaux ou les URLs distantes anonymes)', + 'build_config' => 'Configuration PHP Censor spécifique pour ce projet + (si vous ne pouvez pas ajouter de fichier .php-censor.yml (.phpci.yml|phpci.yml) à la racine du dépôt)', + 'default_branch' => 'Nom de la branche par défaut', + 'allow_public_status' => 'Activer la page de statut publique et l\'image pour ce projet ?', + 'archived' => 'Archivé', + 'archived_menu' => 'Archivé', + 'save_project' => 'Enregistrer le projet', + + 'error_mercurial' => 'Les URLs de dépôt Mercurial doivent commencer par http:// ou https://', + 'error_remote' => 'Les URLs de dépôt doivent commencer par git://, http:// ou https://', + 'error_gitlab' => 'Le nom du dépôt GitLab doit avoir le format "user@domain.tld:owner/repo.git"', + 'error_github' => 'Le nom du dépôt doit être dans le format "proprietaire/dépôt"', + 'error_bitbucket' => 'Le nom du dépôt doit être dans le format "proprietaire/dépôt"', + 'error_path' => 'Le chemin que vous avez spécifié n\'existe pas.', + + // View Project: + 'all_branches' => 'Toutes les branches', + 'builds' => 'Builds', + 'id' => 'ID', + 'date' => 'Date', + 'project' => 'Projet', + 'commit' => 'Commit', + 'branch' => 'Branche', + 'status' => 'Statut', + 'prev_link' => '« Précédent', + 'next_link' => 'Suivant »', + 'public_key' => 'Clé Publique', + 'delete_build' => 'Supprimer le build', + + 'webhooks' => 'Webhooks', + 'webhooks_help_github' => 'Pour générer un build quand de nouveaux commits sont poussés, ajouter l\'url suivante + en tant que new "Webhook" dans la section Webhooks + and Services de votre dépôt GitHub.', + + 'webhooks_help_gitlab' => 'Pour générer un build quand de nouveaux commits sont poussés, ajouter l\'url suivante + and tant que "WebHook URL" dans la section Web Hooks de votre dépôt GitLab.', + + 'webhooks_help_bitbucket' => 'Pour générer un build quand de nouveaux commits sont poussés, ajouter l\'url suivante + en tant que service "POST" dans la section + + Services de votre dépôt Bitbucket.', + + // Project Groups + 'project_group' => 'Groupe du projet', + 'group_projects' => 'Groupes de projets', + 'group_count' => 'Nombre de projets', + 'group_edit' => 'Editer', + 'group_delete' => 'Supprimer', + 'group_add' => 'Ajouter un groupe', + 'group_add_edit' => 'Ajouter / Editer un Groupe', + 'group_title' => 'Titre du groupe', + 'group_save' => 'Sauvegarder le groupe', + + + // View Build + 'errors' => 'Erreurs', + 'information' => 'Informations', + + 'build_x_not_found' => 'Le Build avec l\'ID %d n\'existe pas.', + 'build_n' => 'Build %d', + 'rebuild_now' => 'Relancer maintenant', + + + 'committed_by_x' => 'Committé par %s', + 'commit_id_x' => 'Commit : %s', + + 'chart_display' => 'Ce graphique s\'affichera une fois que le build sera terminé.', + + 'build' => 'Build', + 'lines' => 'Lignes', + 'comment_lines' => 'Lignes de commentaires', + 'noncomment_lines' => 'Lignes qui ne sont pas des commentaires', + 'logical_lines' => 'Lignes logiques', + 'lines_of_code' => 'Lignes de code', + 'build_log' => 'Log du build', + 'quality_trend' => 'Tendance de la qualité', + 'codeception_errors' => 'Erreurs Codeception', + 'phpmd_warnings' => 'Alertes PHPMD', + 'phpcs_warnings' => 'Alertes PHPCS', + 'phpcs_errors' => 'Erreurs PHPCS', + 'phplint_errors' => 'Erreurs de Lint', + 'phpunit_errors' => 'Erreurs PHPUnit', + 'phpunit_fail_init' => 'Ni fichier de configuration, ni répertoire de test trouvé.', + 'phpcpd_warnings' => 'PHP Copy/Paste Detector warnings', + 'phpdoccheck_warnings' => 'Blocs de documentation manquants', + 'issues' => 'Tickets', + + 'phpcpd' => 'PHP Copy/Paste Detector', + 'phpcs' => 'PHP Code Sniffer', + 'phpdoccheck' => 'Missing Docblocks', + 'phpmd' => 'PHP Mess Detector', + 'phpspec' => 'PHP Spec', + 'phpunit' => 'PHP Unit', + + 'codeception_feature' => 'Feature', + 'codeception_suite' => 'Suite', + 'codeception_time' => 'Time', + 'codeception_synopsis' => '%1$d tests exécutés en %2$f secondes. + %3$d échecs.', + 'suite' => 'Suite', + 'test' => 'Test', + 'file' => 'Fichier', + 'line' => 'Ligne', + 'class' => 'Classe', + 'method' => 'Méthode', + 'message' => 'Message', + 'start' => 'Démarrage', + 'end' => 'Fin', + 'from' => 'À partir de', + 'to' => 'jusque', + 'result' => 'Resultat', + 'ok' => 'OK', + 'took_n_seconds' => 'Exécuté en %d secondes', + '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', + 'password_change' => 'Mot de passe (laissez vide si vous ne voulez pas le changer)', + 'save' => 'Sauvegarder »', + 'update_your_details' => 'Mettre à jour vos préférences', + 'your_details_updated' => 'Vos préférences ont été bien mises à jour.', + 'add_user' => 'Ajouter un utilisateur', + 'is_admin' => 'Est-il administrateur ?', + 'yes' => 'Oui', + 'no' => 'Non', + 'edit' => 'Éditer', + 'edit_user' => 'Éditer l\'utilisateur', + 'delete_user' => 'Supprimer l\'utilisateur', + 'user_n_not_found' => 'L\'utilisateur avec l\'ID %d n\'existe pas.', + 'is_user_admin' => 'Est-ce que cet utilisateur est administrateur ?', + 'save_user' => 'Sauvegarder l\'utilisateur', + + // Settings: + 'settings_saved' => 'Vos paramètres ont été sauvegardés.', + 'settings_check_perms' => 'Vos paramètres n\'ont pas pu être sauvegardés, vérifiez les permissions sur le fichier config.yml.', + 'settings_cannot_write' => 'PHP Censor ne peut pas écrire dans votre fichier config.yml, les paramètres ne pourront pas être sauvegardés correctement + tant que ce ne sera pas corrigé.', + 'settings_github_linked' => 'Votre compte GitHub n\'a pas été lié.', + 'settings_github_not_linked' => 'Votre compte GitHub ne peut pas être lié.', + 'build_settings' => 'Configuration du Build', + 'github_application' => 'Application GitHub', + 'github_sign_in' => 'Avant de commencer à utiliser GitHub, vous devez vous connecter et autoriser + PHP Censor à accéder à votre compte.', + 'github_app_linked' => 'PHP Censor s\'est connecté avec succès au compte GitHub.', + 'github_where_to_find' => 'Où trouver ces informations...', + 'github_where_help' => 'Si vous souhaitez utiliser une application qui vous appartient, vous pouvez trouver ces informations dans + la zone de paramètres applications.', + + 'email_settings' => 'Configuration Email', + 'email_settings_help' => 'Avant que PHP Censor puisse envoyer des emails concernant les statuts de build, + vous devez entrer les configurations SMTP ci-dessous.', + + 'application_id' => 'Identifiant d\'application', + 'application_secret' => 'Clé secrète de l\'application', + + 'smtp_server' => 'Serveur SMTP', + 'smtp_port' => 'Port SMTP', + 'smtp_username' => 'Nom d\'utilisateur SMTP', + 'smtp_password' => 'Mot de passe SMTP', + 'from_email_address' => 'Adresse à partir de laquelle sont envoyés les emails', + 'default_notification_address' => 'Adresse de notification par défaut', + 'use_smtp_encryption' => 'Est-ce que vous voulez utiliser le chiffrement SMTP', + 'none' => 'Non', + 'ssl' => 'SSL', + 'tls' => 'TLS', + + 'failed_after' => 'Considérer qu\'un build a échoué après', + '5_mins' => '5 Minutes', + '15_mins' => '15 Minutes', + '30_mins' => '30 Minutes', + '1_hour' => '1 Heure', + '3_hours' => '3 Heures', + + // Plugins + 'cannot_update_composer' => 'PHP Censor 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 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', + 'installed_packages' => 'Paquets installés', + 'suggested_packages' => 'Paquets suggérés', + 'title' => 'Titre', + 'description' => 'Description', + 'version' => 'Version', + 'install' => 'Installer »', + 'remove' => 'Supprimer »', + 'search_packagist_for_more' => 'Rechercher sur Packagist pour trouver plus de paquets', + 'search' => 'Rechercher »', + + // Summary plugin + 'build-summary' => 'Résumé', + 'stage' => 'Étape', + 'duration' => 'Durée', + 'seconds' => 'sec.', + 'plugin' => 'Plugin', + 'stage_setup' => 'Préparation', + 'stage_test' => 'Test', + 'stage_complete' => 'Terminé', + 'stage_success' => 'Succes', + 'stage_failure' => 'Échec', + 'stage_broken' => 'Cassé', + 'stage_fixed' => 'Réparé', + 'severity' => 'Gravité', + + 'build_details' => 'Détails du Build', + 'commit_details' => 'Détails du Commit', + 'committer' => 'Committer', + 'timing' => 'Timing', + 'created' => 'Crée', + 'started' => 'Demarré', + 'finished' => 'Terminé', + + // Update + 'update_app' => 'Mise à jour de la base de données pour refléter les modifications apportées aux modèles.', + 'updating_app' => 'Mise à jour de la base de données PHP Censor : ', + 'not_installed' => 'PHP Censor n\'a pas l\'air d\'être installé.', + 'install_instead' => 'Merci d\'installer PHP Censor grâce à la commande php-censor:install.', + + // Create Build Command + 'add_to_queue_failed' => 'Build créé avec succès mais échec de l\'ajouter à la file d\'attente des Builds. Cela arrive généralement + quand PHP Censor est configuré pour utiliser un serveur beanstalkd qui n\'existe pas ou qui n\'est pas démarré.', + + // Builder + 'missing_app_yml' => 'Ce projet ne contient pas de fichier .php-censor.yml (.phpci.yml|phpci.yml) ou il est vide.', + 'build_success' => 'BUILD RÉUSSI', + 'build_failed' => 'BUILD ÉCHOUÉ', + 'removing_build' => 'Suppression du build.', + 'exception' => 'Exception: ', + 'could_not_create_working' => 'Impossible de créer une copie de travail.', + 'working_copy_created' => 'Copie de travail créée: %s', + 'looking_for_binary' => 'Recherche du binaire: %s', + 'found_in_path' => 'Trouver dans %s: %s', + 'running_plugin' => 'EXÉCUTION DU PLUGIN: %s', + 'plugin_success' => 'PLUGIN: RÉUSSI', + 'plugin_failed' => 'PLUGIN: ÉCHOUÉ', + 'plugin_missing' => 'Le plugin n\'existe pas: %s', + 'failed_allowed' => 'Autorisé', + + // Build Plugins: + 'passing_build' => 'Build réussi', + 'failing_build' => 'Build en echec', + 'log_output' => 'Sortie de log : ', + + // Error Levels: + 'critical' => 'Critique', + 'high' => 'Haut', + 'normal' => 'Normal', + 'low' => 'Bas', + + // Plugins that generate errors: + 'php_mess_detector' => 'PHP Mess Detector', + 'php_code_sniffer' => 'PHP Code Sniffer', + 'php_unit' => 'PHP Unit', + 'php_cpd' => 'PHP Copy/Paste Detector', + 'php_docblock_checker' => 'PHP Docblock Checker', + 'composer' => 'Composer', + 'php_loc' => 'PHP LOC', + 'php_parallel_lint' => 'PHP Parallel Lint', + 'email' => 'Email', + 'atoum' => 'Atoum', + 'behat' => 'Behat', + 'campfire' => 'Campfire', + 'clean_build' => 'Clean Build', + 'codeception' => 'Codeception', + 'copy_build' => 'Copy Build', + 'deployer' => 'Deployer', + 'env' => 'Env', + 'grunt' => 'Grunt', + 'hipchat_notify' => 'Hipchat', + 'irc' => 'IRC', + 'lint' => 'Lint', + 'mysql' => 'MySQL', + 'package_build' => 'Package Build', + 'pdepend' => 'PDepend', + 'pgsql' => 'PostgreSQL', + 'phar' => 'Phar', + 'phing' => 'Phing', + 'php_cs_fixer' => 'PHP Coding Standards Fixer', + 'php_spec' => 'PHP Spec', + 'shell' => 'Shell', + 'slack_notify' => 'Slack', + 'technical_debt' => 'Technical Debt', + 'xmpp' => 'XMPP', + + 'confirm_message' => 'L\'article sera définitivement supprimé. Êtes-vous sûr ?', + 'confirm_title' => 'Confirmation de suppression d\'un article', + 'confirm_ok' => 'Supprimer', + 'confirm_cancel' => 'Annuler', + 'confirm_success' => 'L\'article a été supprimé avec succès.', + 'confirm_failed' => 'Echec de la suppresion! Le serveur a répondu: ', + + 'public_status_title' => 'Statut public', + 'public_status_image' => 'Image de statut', + 'public_status_page' => 'Page publique de statut', +]; diff --git a/src/PHPCensor/Languages/lang.it.php b/src/PHPCensor/Languages/lang.it.php new file mode 100644 index 0000000..e11ab29 --- /dev/null +++ b/src/PHPCensor/Languages/lang.it.php @@ -0,0 +1,291 @@ + 'Italiano', + 'language' => 'Lingua', + + // Log in: + 'log_in_to_app' => 'Accedi a PHP Censor', + 'login_error' => 'Indirizzo email o password errati', + 'forgotten_password_link' => 'Hai dimenticato la tua password?', + 'reset_emailed' => 'Ti abbiamo inviato un link via email per ripristinare la tua password.', + 'reset_header' => 'Non preoccuparti!
E\' sufficiente inserire il tuo indirizzo email di seguito e ti invieremo una email con il link per il ripristino della tua password.', + 'reset_email_address' => 'Inserisci il tuo indirizzo email:', + 'reset_send_email' => 'Invia il link di reset della password', + 'reset_enter_password' => 'Per favore inserisci la nuova password', + 'reset_new_password' => 'Nuova password:', + 'reset_change_password' => 'Cambia password', + 'reset_no_user_exists' => 'Non esiste nessun utente con questo indirizzo email, per favore prova ancora.', + 'reset_email_body' => 'Ciao %s, + +hai ricevuto questa email perché tu, o qualcun\'altro, ha richiesto un reset della password per PHP Censor. + +Se questa mail è tua, per favore apri il seguente link per ripristinare la tua password: %ssession/reset-password/%d/%s + +altrimenti, per favore, ignora questa email e nessuna azione verrà intrapresa. + +Grazie, + +PHP Censor', + + 'reset_email_title' => 'Ripristino della password di PHP Censor per %s', + 'reset_invalid' => 'Richeista di ripristino password non valida.', + 'email_address' => 'Indirizzo Email', + 'login' => 'Login / Email Address', + 'password' => 'Password', + 'log_in' => 'Accedi', + + // Top Nav + 'toggle_navigation' => 'Alterna navigazione', + 'n_builds_pending' => '%d build in attesa', + 'n_builds_running' => '%d build in corso', + 'edit_profile' => 'Modifica il Profilo', + 'sign_out' => 'Disconnettiti', + 'branch_x' => 'Branch: %s', + 'created_x' => 'Creato: %s', + 'started_x' => 'Avviato: %s', + + // Sidebar + 'hello_name' => 'Ciao, %s', + 'dashboard' => 'Dashboard', + 'admin_options' => 'Opzioni di amministrazione', + 'add_project' => 'Aggiungi un Progetto', + 'settings' => 'Impostazioni', + 'manage_users' => 'Gestisci Utenti', + 'plugins' => 'Plugins', + 'view' => 'Visualizzazione', + 'build_now' => 'Avvia una build ora', + 'edit_project' => 'Modifica il Progetto', + 'delete_project' => 'Cancella il Progetto', + + // Project Summary: + 'no_builds_yet' => 'Ancora nessuna build!', + 'x_of_x_failed' => '%d delle ultime %d build sono fallite.', + 'x_of_x_failed_short' => '%d / %d fallite.', + 'last_successful_build' => ' L\'ultima build è %s.', + 'never_built_successfully' => ' Questo progetto non ha nessuna build eseguita con successo.', + 'all_builds_passed' => 'Tutte le ultime %d build sono valide.', + 'all_builds_passed_short' => '%d / %d valide.', + 'last_failed_build' => ' L\'ultima build è %s.', + 'never_failed_build' => ' Questo progetto non ha nessuna build fallita.', + 'view_project' => 'Visualizza il Progetto', + + // Timeline: + 'latest_builds' => 'Ultime Build', + 'pending' => 'In attesa', + 'running' => 'In corso', + 'success' => 'Successo', + 'failed' => 'Fallita', + 'manual_build' => 'Build Manuale', + + // Add/Edit Project: + 'new_project' => 'Nuovo Progetto', + 'project_x_not_found' => 'Progetto con ID %d non esistente.', + 'project_details' => 'Dettagli del Progetto', + 'public_key_help' => 'Per rendere più facile la procedura, abbiamo generato una chiave SSH per te da + usare per questo progetto. Per usarla, aggiungi la chiave pubblica alle "deploy keys" + della piattaforma di gestione del codice che hai scelto.', + 'select_repository_type' => 'Seleziona il tipo di repository...', + 'github' => 'GitHub', + 'bitbucket' => 'Bitbucket', + 'gitlab' => 'GitLab', + 'remote' => 'URL Remoto', + 'local' => 'Percorso Locale', + 'hg' => 'Mercurial', + + 'where_hosted' => 'Dove è archiviato il tuo progetto?', + 'choose_github' => 'Scegli il repository di GitHub:', + + 'repo_name' => 'Nome del Repository / URL (Remoto) o Percorso (Locale)', + 'project_title' => 'Titolo del Progetto', + 'project_private_key' => 'Chiave provata da usare per accedere al repository + (lascia vuota per repository locali o remoti con accesso anonimo)', + 'build_config' => 'condigurazione della build di PHP Censor per questo progetto + (se non puoi aggiungere il file .php-censor.yml (.phpci.yml|phpci.yml) nel repository di questo progetto)', + 'default_branch' => 'Nome del branch di default', + 'allow_public_status' => 'Vuoi rendere pubblica la pagina dello stato e l\'immagine per questo progetto?', + 'archived' => 'Archived', + 'archived_menu' => 'Archived', + 'save_project' => 'Salva il Progetto', + + 'error_mercurial' => 'L\'URL del repository Mercurial URL deve iniziare con http:// o https://', + 'error_remote' => 'L\'URL del repository deve iniziare con git://, http:// o https://', + 'error_gitlab' => 'Il nome del repository di GitLab deve essere nel seguente formato "utente@dominio.tld:proprietario/repository.git"', + 'error_github' => 'Il nome del repository deve essere nel formato "proprietario/repository"', + 'error_bitbucket' => 'Il nome del repository deve essere nel formato "proprietario/repository"', + 'error_path' => 'The path you specified does not exist.', + 'error_path' => 'Il percorso che hai indicato non esiste.', + + // View Project: + 'all_branches' => 'Tutti i Branche', + 'builds' => 'Builds', + 'id' => 'ID', + 'date' => 'Data', + 'project' => 'Progetto', + 'commit' => 'Commit', + 'branch' => 'Branch', + 'status' => 'Stato', + 'prev_link' => '« Precedente', + 'next_link' => 'Successivo »', + 'public_key' => 'Chiave pubblica', + 'delete_build' => 'Rimuovi build', + + 'webhooks' => 'Webhooks', + 'webhooks_help_github' => 'Per effettuare la build automatica di questo progetto quando vengono inseriti nuovi commit, + aggiungi l\'URL seguente come "Webhook" nella sezione + Webhooks and Services del tuo + repository su GitHub.', + + 'webhooks_help_gitlab' => 'Per effettuare la build automatica di questo progetto quando vengono inseriti nuovi commit, + aggiungi l\'URL seguente come "Webhook URL" nella sezione "WebHook URL" del tuo + repository GitLab.', + + 'webhooks_help_bitbucket' => 'Per effettuare la build automatica di questo progetto quando vengono inseriti nuovi + commit, aggiungi l\'URL seguente come serizio "POST" nella sezione + Services del tuo repository su + BITBUCKET.', + + // View Build + 'build_x_not_found' => 'La build con ID %d non esite.', + 'build_n' => 'Build %d', + 'rebuild_now' => 'Esegui nuovamente la build ora', + + + 'committed_by_x' => 'Inviato da %s', + 'commit_id_x' => 'Commit: %s', + + 'chart_display' => 'Questo grafico verrà mostrato una volta terminata la build.', + + 'build' => 'Build', + 'lines' => 'Linee', + 'comment_lines' => 'Linee di commenti', + 'noncomment_lines' => 'Linee che non sono commenti', + 'logical_lines' => 'Linee di logica', + 'lines_of_code' => 'Linee di codice', + 'build_log' => 'Log della build', + 'quality_trend' => 'Trend della qualità', + 'codeception_errors' => 'Errori di Codeception', + 'phpmd_warnings' => 'Avvisi di PHPMD', + 'phpcs_warnings' => 'Avvisi di PHPCS', + 'phpcs_errors' => 'Errori di PHPCS', + 'phplint_errors' => 'Errori di Lint', + 'phpunit_errors' => 'Errori di PHPUnit', + 'phpdoccheck_warnings' => 'Docblocks mancanti', + 'issues' => 'Segnalazioni', + + 'codeception' => 'Codeception', + 'phpcpd' => 'PHP Copy/Paste Detector', + 'phpcs' => 'PHP Code Sniffer', + 'phpdoccheck' => 'Docblocks mancanti', + 'phpmd' => 'PHP Mess Detector', + 'phpspec' => 'PHP Spec', + 'phpunit' => 'PHP Unit', + + 'file' => 'File', + 'line' => 'Lina', + 'class' => 'Classe', + 'method' => 'Metodo', + 'message' => 'Messaggio', + 'start' => 'Inizia', + 'end' => 'Finisci', + 'from' => 'Da', + 'to' => 'A', + 'result' => 'Risultati', + 'ok' => 'OK', + 'took_n_seconds' => 'Sono stati impiegati %d seconds', + 'build_started' => 'Build Avviata', + 'build_finished' => 'Build Terminata', + 'test_message' => 'Message', + 'test_no_message' => 'No message', + 'test_success' => 'Successful: %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', + 'password_change' => 'Password (lascia vuota se non vuoi modificarla)', + 'save' => 'Salva »', + 'update_your_details' => 'Aggiorna le tue informazioni', + 'your_details_updated' => 'Le tue informazioni sono state aggiornate.', + 'add_user' => 'Aggiung utent', + 'is_admin' => 'E\' amministratore?', + 'yes' => 'Si', + 'no' => 'No', + 'edit' => 'Modifica', + 'edit_user' => 'Modifica utente', + 'delete_user' => 'Cancella utente', + 'user_n_not_found' => 'L\'utente con ID %d non esiste.', + 'is_user_admin' => 'Questo utente è un amministratore?', + 'save_user' => 'Salva utente', + + // Settings: + 'settings_saved' => 'Le configurazioni sono state salvate.', + 'settings_check_perms' => 'Le configurazioni non possono essere salvate, controlla i permessi del filer config.yml.', + 'settings_cannot_write' => 'PHP Censor non può scrivere il file config.yml, le configurazioni potrebbero non essere + salvate correttamente fintanto che il problema non verrà risolto.', + 'settings_github_linked' => 'Il tuo account GitHub è stato collegato.', + 'settings_github_not_linked' => 'Il tuo account GitHub non può essere collegato.', + 'build_settings' => 'Configurazioni della build', + 'github_application' => 'Applicazione GitHub', + 'github_sign_in' => 'Prima di poter iniziare ad usare GitHub, è necessario collegarsi e garantire + a PHP Censor l\'accesso al tuo account.', + 'github_app_linked' => 'PHP Censor è stato collegato correttamente al tuo account GitHub.', + 'github_where_to_find' => 'Dove trovare queste...', + 'github_where_help' => 'Se sei il proprietario dell\'applicazione, puoi trovare queste informazioni nell\'area delle + configurazioni dell\'applicazione.', + + 'email_settings' => 'Impostazioni Email', + 'email_settings_help' => 'Prima che possa inviare le email con lo status PHP Censor, devi configurare l\'SMTP qui sotto.', + + 'application_id' => 'ID dell\'Applicazione', + 'application_secret' => 'Secret dell\'Applicazione', + + 'smtp_server' => 'Server SMTP', + 'smtp_port' => 'Porta SMTP', + 'smtp_username' => 'Username SMTP', + 'smtp_password' => 'Password SMTP', + 'from_email_address' => 'Indirizzio Email del mittente', + 'default_notification_address' => 'Indirizzo email delle notifiche predefinito', + 'use_smtp_encryption' => 'Utilizzare l\'Encrypting per SMTP?', + 'none' => 'No', + 'ssl' => 'SSL', + 'tls' => 'TLS', + + 'failed_after' => 'Considera la build fallita dopo', + '5_mins' => '5 Minuti', + '15_mins' => '15 Minuti', + '30_mins' => '30 Minuti', + '1_hour' => '1 Ora', + '3_hours' => '3 Ore', + + // Plugins + 'cannot_update_composer' => 'PHP Censor non può aggiornare composer.json per te non essendo scrivibile.', + 'x_has_been_removed' => '%s è stato rimosso.', + 'x_has_been_added' => '%s è stato aggiunto al file composer.json per te, verrà installato la prossima volta che eseguirai + composer update.', + 'enabled_plugins' => 'Plugins attivati', + 'provided_by_package' => 'Fornito dal pacchetto', + 'installed_packages' => 'Pacchetti installati', + 'suggested_packages' => 'Paccehtti suggeriti', + 'title' => 'Titolo', + 'description' => 'Descrizione', + 'version' => 'Versione', + 'install' => 'Installa »', + 'remove' => 'Rimuovi »', + 'search_packagist_for_more' => 'Cerca altri pacchetti su Packagist', + 'search' => 'Cerca »', + + // Update + 'update_app' => 'Aggiorna il database per riflettere le modifiche ai model.', + 'updating_app' => 'Aggiornamenti del database di PHP Censor: ', + 'not_installed' => 'PHP Censor sembra non essere installato.', + 'install_instead' => 'Per favore installa PHP Censor tramite php-censor:install.', + + // Build Plugins: + 'passing_build' => 'Build passata', + 'failing_build' => 'Build fallita', + 'log_output' => 'Log: ', +]; diff --git a/src/PHPCensor/Languages/lang.nl.php b/src/PHPCensor/Languages/lang.nl.php new file mode 100644 index 0000000..d602a9d --- /dev/null +++ b/src/PHPCensor/Languages/lang.nl.php @@ -0,0 +1,291 @@ + 'Nederlands', + 'language' => 'Taal', + + // Log in: + 'log_in_to_app' => 'Log in op PHP Censor', + 'login_error' => 'Incorrect e-mailadres of wachtwoord', + 'forgotten_password_link' => 'Wachtwoord vergeten?', + 'reset_emailed' => 'We hebben je een link gemaild om je wachtwoord opnieuw in te stellen.', + 'reset_header' => 'Geen zorgen!
Vul hieronder gewoon je e-mailadres in en we sturen +je een link on je wachtwoord te resetten.', + 'reset_email_address' => 'Vul je e-mailadres in:', + 'reset_send_email' => 'Verstuur wachtwoord reset', + 'reset_enter_password' => 'Gelieve een nieuw wachtwoord in te voeren', + 'reset_new_password' => 'Nieuw wachtwoord:', + 'reset_change_password' => 'Wijzig wachtwoord', + 'reset_no_user_exists' => 'Er bestaat geen gebruiker met dit e-mailadres, gelieve opnieuw te proberen.', + 'reset_email_body' => 'Hallo %s, + +Je ontvangt deze email omdat jij, of iemand anders, je wachtwoord voor PHP Censor opnieuw wenst in te stellen. + +Indien jij dit was, klik op deze link op je wachtwoord opnieuw in te stellen: %ssession/reset-password/%d/%s + +Zoniet, negeer deze e-mail en er zal geen verdere actie ondernomen worden. + +Bedankt, + +PHP Censor', + + 'reset_email_title' => 'PHP Censor wachtwoord reset voor %s', + 'reset_invalid' => 'Ongeldig wachtwoord reset verzoek', + 'email_address' => 'E-mailadres', + 'login' => 'Login / Email Address', + 'password' => 'Wachtwoord', + 'log_in' => 'Log in', + + + // Top Nav + 'toggle_navigation' => 'Wissel Navigatie', + 'n_builds_pending' => '%d builds wachtend', + 'n_builds_running' => '%d builds lopende', + 'edit_profile' => 'Wijzig profiel', + 'sign_out' => 'Uitloggen', + 'branch_x' => 'Branch: %s', + 'created_x' => 'Aangemaakt: %s', + 'started_x' => 'Gestart: %s', + + // Sidebar + 'hello_name' => 'Hallo, %s', + 'dashboard' => 'Startpagina', + 'admin_options' => 'Administratie opties', + 'add_project' => 'Project toevoegen', + 'settings' => 'Instellingen', + 'manage_users' => 'Gebruikers beheren', + 'plugins' => 'Plugins', + 'view' => 'Bekijk', + 'build_now' => 'Build nu', + 'edit_project' => 'Wijzig project', + 'delete_project' => 'Verwijder project', + + // Project Summary: + 'no_builds_yet' => 'Nog geen builds!', + 'x_of_x_failed' => '%d van de laatste %d builds faalden.', + 'x_of_x_failed_short' => '%d / %d faalden.', + 'last_successful_build' => 'De laatste succesvolle build was %s.', + 'never_built_successfully' => 'Dit project heeft geen succesvolle build gehad.', + 'all_builds_passed' => 'Elk van de laatste %d builds slaagden.', + 'all_builds_passed_short' => '%d / %d slaagden.', + 'last_failed_build' => 'De laatste gefaalde build was %s.', + 'never_failed_build' => 'Dit project heeft geen gefaalde build gehad.', + 'view_project' => 'Bekijk project', + + // Timeline: + 'latest_builds' => 'Laatste builds', + 'pending' => 'In afwachting', + 'running' => 'Lopende', + 'success' => 'Succes', + 'failed' => 'Gefaald', + 'manual_build' => 'Manuele build', + + // Add/Edit Project: + 'new_project' => 'Nieuw project', + 'project_x_not_found' => 'Project met ID %d bestaat niet.', + 'project_details' => 'Project details', + 'public_key_help' => 'Om eenvoudiger te kunnen starten, hebben we een SSH sleutelpaar gegenereerd +voor dit project. Om het te gebruiken, voeg onderstaande public key toe aan de "deploy keys" sectie +van je gekozen source code hosting platform', + 'select_repository_type' => 'Selecteer repository type...', + 'github' => 'GitHub', + 'bitbucket' => 'Bitbucket', + 'gitlab' => 'GitLab', + 'remote' => 'Externe URL', + 'local' => 'Lokaal pad', + 'hg' => 'Mercurial', + + 'where_hosted' => 'Waar wordt je project gehost?', + 'choose_github' => 'Selecteer een GitHub repository:', + + 'repo_name' => 'Repository naam / URL (extern) of pad (lokaal)', + 'project_title' => 'Projecttitel', + 'project_private_key' => 'Private key voor toegang tot repository +(laat leeg voor lokaal en/of anonieme externen)', + 'build_config' => 'PHP Censor build configuratie voor dit project +(indien je geen .php-censor.yml (.phpci.yml|phpci.yml) bestand aan de project repository kan toevoegen)', + 'default_branch' => 'Standaard branch naam', + 'allow_public_status' => 'Publieke statuspagina en afbeelding beschikbaar maken voor dit project?', + 'archived' => 'Archived', + 'archived_menu' => 'Archived', + 'save_project' => 'Project opslaan', + + 'error_mercurial' => 'Mercurial repository URL dient te starten met http:// of https://', + 'error_remote' => 'Repository URL dient te starten met git://, http:// of https://', + 'error_gitlab' => 'GitLab repository naam dient in het formaat "gebruiker@domain.tld/eigenaar/repo.git" te zijn', + 'error_github' => 'Repository naam dient in het formaat "eigenaar/repo" te zijn', + 'error_bitbucket' => 'Repository naam dient in het formaat "eigenaar/repo" te zijn', + 'error_path' => 'Het opgegeven pad bestaat niet.', + + // View Project: + 'all_branches' => 'Alle brances', + 'builds' => 'Builds', + 'id' => 'ID', + 'date' => 'Datum', + 'project' => 'Project', + 'commit' => 'Commit', + 'branch' => 'Branch', + 'status' => 'Status', + 'prev_link' => '« Vorig', + 'next_link' => 'Volgend »', + 'public_key' => 'Public Key', + 'delete_build' => 'Verwijder build', + + 'webhooks' => 'Webhooks', + 'webhooks_help_github' => 'Voor automatische builds wanneer nieuwe commits worden gepusht, dient onderstaande URL +als nieuwe "Webhook" in de Webhooks +and Services sectie van je GitHub repository toegevoegd worden.', + + 'webhooks_help_gitlab' => 'Voor automatische builds wanneer nieuwe commits worden gepusht, dient onderstaande URL +als nieuwe "Webhook URL" in de Web Hooks sectie van je GitLab repository toegevoegd worden.', + + 'webhooks_help_bitbucket' => 'Voor automatische builds wanneer nieuwe commits worden gepusht, dient onderstaande URL +als "POST" service in de in de + +Services sectie van je Bitbucket repository toegevoegd worden.', + + // View Build + 'build_x_not_found' => 'Build met ID %d bestaat niet.', + 'build_n' => 'Build %d', + 'rebuild_now' => 'Rebuild nu', + + + 'committed_by_x' => 'Committed door %s', + 'commit_id_x' => 'Commit: %s', + + 'chart_display' => 'Deze grafiek wordt getoond zodra de build compleet is.', + + 'build' => 'Build', + 'lines' => 'Lijnen', + 'comment_lines' => 'Commentaarlijnen', + 'noncomment_lines' => 'Niet-commentaarlijnen', + 'logical_lines' => 'Logische lijnen', + 'lines_of_code' => 'Lijnen code', + 'build_log' => 'Build Log', + 'quality_trend' => 'Kwaliteitstrend', + 'codeception_errors' => 'Codeception Fouten', + 'phpmd_warnings' => 'PHPMD Waarschuwingen', + 'phpcs_warnings' => 'PHPCS Waarschuwingen', + 'phpcs_errors' => 'PHPCS Fouten', + 'phplint_errors' => 'Lint Fouten', + 'phpunit_errors' => 'PHPUnit Fouten', + 'phpdoccheck_warnings' => 'Ontbrekende Docblocks', + 'issues' => 'Problemen', + + 'codeception' => 'Codeception', + 'phpcpd' => 'PHP Copy/Paste Detector', + 'phpcs' => 'PHP Code Sniffer', + 'phpdoccheck' => 'Ontbrekende Docblocks', + 'phpmd' => 'PHP Mess Detector', + 'phpspec' => 'PHP Spec', + 'phpunit' => 'PHPUnit', + + 'file' => 'Bestand', + 'line' => 'Lijn', + 'class' => 'Class', + 'method' => 'Method', + 'message' => 'Boodschap', + 'start' => 'Start', + 'end' => 'Einde', + 'from' => 'Van', + 'to' => 'Tot', + 'result' => 'Resultaat', + 'ok' => 'OK', + 'took_n_seconds' => 'Duurde %d seconden', + 'build_started' => 'Build gestart', + 'build_finished' => 'Build beëindigd', + 'test_message' => 'Message', + 'test_no_message' => 'No message', + 'test_success' => 'Successful: %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', + 'password_change' => 'Wachtwoord (laat leeg indien je niet wenst te veranderen)', + 'save' => 'Opslaan »', + 'update_your_details' => 'Wijzig je gegevens', + 'your_details_updated' => 'Je gegevens werden gewijzigd', + 'add_user' => 'Gebruiker toevoegen', + 'is_admin' => 'Is administrator?', + 'yes' => 'Ja', + 'no' => 'Nee', + 'edit' => 'Wijzig', + 'edit_user' => 'Gebruiker wijzigen', + 'delete_user' => 'Gebruiker wissen', + 'user_n_not_found' => 'Gebruiker met ID %d bestaat niet.', + 'is_user_admin' => 'Is deze gebruiker administrator?', + 'save_user' => 'Gebruiker opslaan', + + // Settings: + 'settings_saved' => 'Je instellingen werden opgeslagen.', + 'settings_check_perms' => 'Je instellingen konden niet worden opgeslagen, controleer de permissies van je config.yml bestand.', + 'settings_cannot_write' => 'PHP Censor kan niet schrijven naar je config.yml bestand, instellingen worden mogelijks +niet goed opgeslagen tot dit opgelost is.', + 'settings_github_linked' => 'Je GitHub account werd gelinkt.', + 'settings_github_not_linked' => 'Je GitHub account kon niet gelinkt worden.', + 'build_settings' => 'Build instellingen', + 'github_application' => 'GitHub toepassing', + 'github_sign_in' => 'Vooraleer je GitHub kan gebruiken, dien je in te loggen en +PHP Censor toegang te verlenen tot je account.', + 'github_app_linked' => 'PHP werd succesvol gelinkt aan je GitHub account.', + 'github_where_to_find' => 'Waar zijn deze te vinden...', + 'github_where_help' => 'Indien je eigenaar bent van de toepassing die je wens te gebruiken, kan je deze informatie +in je applications instellingen pagina vinden.', + + 'email_settings' => 'E-mail instellingen', + 'email_settings_help' => 'Vooraleer PHP Censor je build status e-mails kan sturen, +dien je eerst je SMTP instellingen te configureren.', + + 'application_id' => 'Toepassings ID', + 'application_secret' => 'Toepassings geheime code', + + 'smtp_server' => 'SMTP Server', + 'smtp_port' => 'SMTP Poort', + 'smtp_username' => 'SMTP Gebruikersnaam', + 'smtp_password' => 'SMTP Wachtwoord', + 'from_email_address' => 'Van e-mailadres', + 'default_notification_address' => 'Standaard melding e-mailadres', + 'use_smtp_encryption' => 'SMTP Encryptie gebruiken?', + 'none' => 'Geen', + 'ssl' => 'SSL', + 'tls' => 'TLS', + + 'failed_after' => 'Beschouw een build gefaald na', + '5_mins' => '5 minuten', + '15_mins' => '15 minuten', + '30_mins' => '30 minuten', + '1_hour' => '1 uur', + '3_hours' => '3 uur', + + // Plugins + 'cannot_update_composer' => 'PHP Censor kan composer.json niet aanpassen gezien het niet schrijfbaar is.', + 'x_has_been_removed' => '%s werd verwijderd.', + 'x_has_been_added' => '%s werd toegevoegd aan composer.json en zal geïnstalleerd worden de volgende +keer je composer update uitvoert.', + 'enabled_plugins' => 'Ingeschakelde plugins', + 'provided_by_package' => 'Voorzien door package', + 'installed_packages' => 'Geinstalleerde packages', + 'suggested_packages' => 'Voorgestelde packages', + 'title' => 'Titel', + 'description' => 'Beschrijving', + 'version' => 'Versie', + 'install' => 'Installeer »', + 'remove' => 'Verwijder »', + 'search_packagist_for_more' => 'Doorzoek Packagist naar meer packages', + 'search' => 'Zoek »', + + // Update + 'update_app' => 'Update de database naar het beeld van gewijzigde modellen.', + 'updating_app' => 'PHP Censor database wordt geüpdatet:', + 'not_installed' => 'PHP Censor lijkt niet geïnstalleerd te zijn.', + 'install_instead' => 'Gelieve PHP Censor via php-censor:install te installeren.', + + // Build Plugins: + 'passing_build' => 'Slagende build', + 'failing_build' => 'Falende build', + 'log_output' => 'Log output:', +]; diff --git a/src/PHPCensor/Languages/lang.pl.php b/src/PHPCensor/Languages/lang.pl.php new file mode 100644 index 0000000..f492cc4 --- /dev/null +++ b/src/PHPCensor/Languages/lang.pl.php @@ -0,0 +1,292 @@ + 'Polski', + 'language' => 'Język', + + // Log in: + 'log_in_to_app' => 'Zaloguj się do PHP Censor', + 'login_error' => 'Nieprawidłowy email lub hasło', + 'forgotten_password_link' => 'Zapomniałeś hasła?', + 'reset_emailed' => 'Email z linkiem resetującym hasło został wysłany.', + 'reset_header' => 'Spokojnie!
Wpisz swój adres email w polu poniżej a my wyślemy Ci link +resetujący hasło.', + 'reset_email_address' => 'Podaj swój adres email:', + 'reset_send_email' => 'Wyślij reset hasła emailem', + 'reset_enter_password' => 'Wpisz nowe hasło', + 'reset_new_password' => 'Nowe hasło:', + 'reset_change_password' => 'Zmień hasło', + 'reset_no_user_exists' => 'Użytkownik o takim emailu nie istnieje. Spróbuj jeszcze raz.', + 'reset_email_body' => 'Witaj %s, + +Otrzymałeś ten email ponieważ Ty, lub ktoś inny, wysłał prośbę o zmianę hasła w PHP Censor. + +Jeśli to faktycznie Ty, kliknij w następujący link aby zresetować hasło: %ssession/reset-password/%d/%s + +Jeśli nie, zignoruj tego emaila i wszystko pozostanie bez zmian, + +Pozdrawiamy, + +PHP Censor', + + 'reset_email_title' => 'Reset Hasła PHP Censor 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ę', + + + // Top Nav + 'toggle_navigation' => 'Otwórz/zamknij nawigację', + 'n_builds_pending' => '%d budowań w kolejce', + 'n_builds_running' => '%d budowań w toku', + 'edit_profile' => 'Edytuj Profil', + 'sign_out' => 'Wyloguj się', + 'branch_x' => 'Gałąź: %s', + 'created_x' => 'Utworzono: %s', + 'started_x' => 'Rozpoczęto: %s', + + // Sidebar + 'hello_name' => 'Witaj, %s', + 'dashboard' => 'Panel administracyjny', + 'admin_options' => 'Opcje Administratora', + 'add_project' => 'Dodaj Projekt', + 'settings' => 'Ustawienia', + 'manage_users' => 'Zarządaj Uzytkownikami', + 'plugins' => 'Pluginy', + 'view' => 'Podgląd', + 'build_now' => 'Zbuduj', + 'edit_project' => 'Edytuj Projekt', + 'delete_project' => 'Usuń Projekt', + + // Project Summary: + 'no_builds_yet' => 'Brak budowań!', + 'x_of_x_failed' => '%d z ostatnich %d budowań nie powiodło się', + 'x_of_x_failed_short' => '%d / %d nie powiodło się', + 'last_successful_build' => 'Ostatnie budowanie zakończone sukesem odbyło się %s', + 'never_built_successfully' => 'Projekt nie został zbudowany z powodzeniem.', + 'all_builds_passed' => 'Wszystkie z ostatnich %d budowań przeszły.', + 'all_builds_passed_short' => '%d / %d przeszło.', + 'last_failed_build' => 'Ostatnie budowanie zakończone niepowodzeniam było %s.', + 'never_failed_build' => 'Ten projekt nigdy nie zakończył się niepowodzeniem budowania', + 'view_project' => 'Podgląd Projektu', + + // Timeline: + 'latest_builds' => 'Ostatnie Budowania', + 'pending' => 'Oczekujące', + 'running' => 'W toku', + 'success' => 'Sukces', + 'failed' => 'Nieudane', + 'manual_build' => 'Budowanie Manualne', + + // Add/Edit Project: + 'new_project' => 'Nowy Projekt', + 'project_x_not_found' => 'Projekt o ID %d nie istnieje.', + 'project_details' => 'Szczegóły Projektu', + 'public_key_help' => 'Aby łatwiej zacząć, wygenerowaliśmy parę kluczy SSH, które możesz użyć +do tego projektu. Żeby je użyć, wystarczy dodać następujący klucz publiczny do sekcji "wdrożyć klucze" +od wybranego kodu źródłowego platformy hostingowej.', + 'select_repository_type' => 'Wybierz typ repozytorium...', + 'github' => 'GitHub', + 'bitbucket' => 'Bitbucket', + 'gitlab' => 'GitLab', + 'remote' => 'Zdalny URL ', + 'local' => 'Lokalna Ścieżka ', + 'hg' => 'Mercurial', + 'svn' => 'Subversion', + + 'where_hosted' => 'Gdzie hostowany jest Twój projekt?', + 'choose_github' => 'Wybierz repozytorium GitHub:', + + 'repo_name' => 'Nazwa repozytorium / URL (Zdalne) lub Ścieżka (Lokalne)', + 'project_title' => 'Tytuł Projektu', + 'project_private_key' => 'Prywanty klucz dostępu do repozytoriów +(pozostaw puste pole dla zdalnych lokalnych i/lub anonimowych)', + 'build_config' => 'PHP Censor zbudowało config dla tego projektu +(jeśli nie możesz dodać pliku .php-censor.yml (.phpci.yml|phpci.yml) do repozytorium projektu)', + 'default_branch' => 'Domyślna nazwa gałęzi', + 'allow_public_status' => 'Włączyć status publiczny dla tego projektu?', + 'archived' => 'W archiwum', + 'archived_menu' => 'W archiwum', + 'save_project' => 'Zachowaj Projekt', + + 'error_mercurial' => 'URL repozytorium Mercurialnego powinno zaczynać się od http:// and https://', + 'error_remote' => 'URL repozytorium powinno zaczynać się od git://, http:// lub https://', + 'error_gitlab' => 'Nazwa Repozytorium GitLab powinna być w następującym formacie: "user@domain.tld:owner/repo.git"', + 'error_github' => 'Nazwa repozytorium powinna być w formacie: "użytkownik/repo"', + 'error_bitbucket' => 'Nazwa repozytorium powinna być w formacie: " użytkownik/repo\'', + 'error_path' => 'Wybrana sieżka nie istnieje', + + // View Project: + 'all_branches' => 'Wszystkie Gałęzie', + 'builds' => 'Budowania', + 'id' => 'ID', + 'date' => 'Data', + 'project' => 'Projekt', + 'commit' => 'Commit', + 'branch' => 'Gałąź', + 'status' => 'Status', + 'prev_link' => '« Poprzedni', + 'next_link' => 'Następny »', + 'public_key' => 'Klucz Publiczny', + 'delete_build' => 'Usuń Budowanie', + + 'webhooks' => 'Webhooks', + 'webhooks_help_github' => 'Aby automatycznie uruchomić nową budowę po wysłaniu commitów dodaj poniższy adres URL + jako nowy "WebHook" w sekcji Webhooks and Services + Twojego repozytoria GitLab.', + + 'webhooks_help_gitlab' => 'Aby automatycznie uruchomić nową budowę po wysłaniu commitów dodaj poniższy adres URL + jako "WebHook URL" w sekcji Web Hook Twojego repozytoria GitLab.', + + 'webhooks_help_bitbucket' => 'Aby automatycznie uruchomić nową budowę po wysłaniu commitów, dodaj poniższy adres URL + jako usługę "POST" w sekcji + +Services repozytoria Bitbucket.', + + // View Build + 'build_x_not_found' => 'Budowanie o ID %d nie istnieje.', + 'build_n' => 'Budowanie %d', + 'rebuild_now' => 'Przebuduj Teraz', + + + 'committed_by_x' => 'Commitowane przez %s', + 'commit_id_x' => 'Commit: %s', + + 'chart_display' => 'Ten wykres wyświetli się po zakończeniu budowy.', + + 'build' => 'Budowanie', + 'lines' => 'Linie', + 'comment_lines' => 'Linie Komentarza', + 'noncomment_lines' => 'Linie Bez Komentarza', + 'logical_lines' => 'Lokalne Linie', + 'lines_of_code' => 'Linie Kodu', + 'build_log' => 'Log Budowania', + 'quality_trend' => 'Trend Jakości', + 'codeception_errors' => 'Błędy Codeception', + 'phpmd_warnings' => 'Alerty PHPMD', + 'phpcs_warnings' => 'Alerty PHPCS', + 'phpcs_errors' => 'Błędy PHPCS', + 'phplint_errors' => 'Błędy Lint', + 'phpunit_errors' => 'Błędy PHPUnit', + 'phpdoccheck_warnings' => 'Brakuje sekcji DocBlock', + 'issues' => 'Problemy', + + 'codeception' => 'Codeception', + 'phpcpd' => 'PHP Copy/Paste Detector', + 'phpcs' => 'PHP Code Sniffer', + 'phpdoccheck' => 'Brakuje sekcji DocBlock', + 'phpmd' => 'PHP Mess Detector', + 'phpspec' => 'PHPSpec', + 'phpunit' => 'PHPUnit', + 'technical_debt' => 'Dług technologiczny', + 'behat' => 'Behat', + + 'file' => 'Plik', + 'line' => 'Linia', + 'class' => 'Klasa', + 'method' => 'Metoda', + 'message' => 'Wiadomość', + 'start' => 'Początek', + 'end' => 'Koniec', + 'from' => 'Od', + 'to' => 'Do', + 'result' => 'Wynik', + 'ok' => 'OK', + 'took_n_seconds' => 'Zajęło %d sekund', + 'build_started' => 'Budowanie Rozpoczęte', + 'build_finished' => 'Budowanie Zakończone', + 'test_message' => 'Wiadomość', + 'test_no_message' => 'Brak wiadomości', + 'test_success' => 'Powodzenie: %d', + 'test_fail' => 'Niepowodzenia: %d', + 'test_skipped' => 'Pominęte: %d', + 'test_error' => 'Błędy: %d', + 'test_todo' => 'Do zrobienia: %d', + 'test_total' => '%d test(ów)', + + // Users + 'name' => 'Nazwa', + 'password_change' => 'Hasło (pozostaw puste jeśli nie chcesz zmienić hasła)', + 'save' => 'Zapisz »', + 'update_your_details' => 'Aktualizuj swoje dane', + 'your_details_updated' => 'Twoje dane zostały zaktualizowane.', + 'add_user' => 'Dodaj Użytkownika', + 'is_admin' => 'Jest Adminem?', + 'yes' => 'Tak', + 'no' => 'Nie', + 'edit' => 'Edytuj', + 'edit_user' => 'Edytuj Użytkownika', + 'delete_user' => 'Usuń Użytkownika', + 'user_n_not_found' => 'Użytkownik z ID %d nie istnieje.', + 'is_user_admin' => 'Czy użytkownik jest administratorem?', + 'save_user' => 'Zapisz Użytkownika', + + // Settings: + 'settings_saved' => 'Ustawienia zostały zapisane.', + 'settings_check_perms' => 'Twoje ustawienia nie mogły zostać zapisane. Sprawdź uprawnienia do pliku config.yml.', + 'settings_cannot_write' => 'PHP Censor nie może zapisać do pliku config.yml. Dopóty nie będzie można poprawnie zachować ustawie, +dopóki nie będzie to naprawione.', + 'settings_github_linked' => 'Połaczono z Twoim kontem Github', + 'settings_github_not_linked' => 'Nie udało się połaczyć z Twoim kontem Github', + 'build_settings' => 'Ustawienia budowania', + 'github_application' => 'Aplikacja GitHub', + 'github_sign_in' => 'Zanim będzie można zacząć korzystać z GitHub, musisz najpierw Sign in, a następnie udzielić dostęp dla PHP Censor do Twojego konta.', + 'github_app_linked' => 'PHP Censor zostało pomyślnie połączone z konten GitHub.', + 'github_where_to_find' => 'Gdzie można znaleźć...', + 'github_where_help' => 'Jeśli to jest Twoja aplikacjia i chcesz jej użyć to więcej informacji znajdziesz w sekcji ustawień: + applications', + + 'email_settings' => 'Ustawienia Email', + 'email_settings_help' => 'Aby PHP Censor mógł wysyłać emaile z stanem budowy, musisz najpierw skonfigurować poniższe ustawienia SMTP.', + + 'application_id' => 'ID Aplikacji', + 'application_secret' => 'Klucz Secret aplikacji', + + 'smtp_server' => 'Serwer SMTP', + 'smtp_port' => 'Port SMTP', + 'smtp_username' => 'SMTP Login', + 'smtp_password' => 'Hasło SMTP', + 'from_email_address' => 'E-mail adres Od:', + 'default_notification_address' => 'Domyślny adres email powiadamiania', + 'use_smtp_encryption' => 'Użyć szyfrowane SMTP?', + 'none' => 'Żadne', + 'ssl' => 'SSL', + 'tls' => 'TLS', + + 'failed_after' => 'Uznaj, że budowanie nie powiodło się po', + '5_mins' => '5 Minutach', + '15_mins' => '15 Minutach', + '30_mins' => '30 Minutach', + '1_hour' => '1 Godzinie', + '3_hours' => '3 Godzinach', + + // Plugins + 'cannot_update_composer' => 'PHP Censor nie może zaktualizować copmposer.json, ponieważ nie ma uprawnień do zapisu.', + 'x_has_been_removed' => 'Usunięto %s. ', + 'x_has_been_added' => 'Dodano %s do composer.json. Zostanie zainstalowane po +wywołaniu polecenia composer update.', + 'enabled_plugins' => 'Aktywne Pluginy', + 'provided_by_package' => 'Dostarczone w pakiecie', + 'installed_packages' => 'Zainstalowane Pakiety', + 'suggested_packages' => 'Sugerowane Pakiety', + 'title' => 'Tytuł', + 'description' => 'Opis', + 'version' => 'Wersja', + 'install' => 'Zainstaluj »', + 'remove' => 'Usuń »', + 'search_packagist_for_more' => 'Przeszukaj Packagist po więcej pakietów', + 'search' => 'Szukaj »', + + // Update + 'update_app' => 'Zaktualizuj bazę danych zgodnie ze zmodyfikowanymi modelami.', + 'updating_app' => 'Aktualizacja bazy danych PHP Censor:', + 'not_installed' => 'Wygląda na to, że PHP Censor nie jest zainstalowane.', + 'install_instead' => 'Proszę zainstalować PHP Censor poprzez php-censor:install', + + // Build Plugins: + 'passing_build' => 'Pomijanie Budowania', + 'failing_build' => 'Niepowodzenie Budowania', + 'log_output' => 'Log Wyjściowy:', +]; diff --git a/src/PHPCensor/Languages/lang.pt-br.php b/src/PHPCensor/Languages/lang.pt-br.php new file mode 100644 index 0000000..c28eb22 --- /dev/null +++ b/src/PHPCensor/Languages/lang.pt-br.php @@ -0,0 +1,328 @@ + 'Português Brasil', + 'language' => 'Idioma', + + // Log in: + 'log_in_to_app' => 'Acessar o PHP Censor', + 'login_error' => 'Email ou senha incorretos', + 'forgotten_password_link' => 'Perdeu sua senha?', + 'reset_emailed' => 'We\'ve emailed you a link to reset your password.', + 'reset_header' => 'Não se preocupe!
Basta digitar o seu endereço de e-mail abaixo e nós lhe enviaremos um email com um link para redefinir sua senha.', + 'reset_email_address' => 'Digite seu endereço de e-mail:', + 'reset_send_email' => 'Solicitar nova senha', + 'reset_enter_password' => 'Please enter a new password', + 'reset_new_password' => 'New password:', + 'reset_change_password' => 'Change password', + 'reset_no_user_exists' => 'No user exists with that email address, please try again.', + 'reset_email_body' => 'Hi %s, + +You have received this email because you, or someone else, has requested a password reset for PHP Censor. + +If this was you, please click the following link to reset your password: %ssession/reset-password/%d/%s + +Otherwise, please ignore this email and no action will be taken. + +Thank you, + +PHP Censor', + + 'reset_email_title' => 'PHP Censor Password Reset for %s', + 'reset_invalid' => 'Invalid password reset request.', + 'email_address' => 'Endereço de e-mail', + 'login' => 'Login / Email Address', + 'password' => 'Senha', + 'log_in' => 'Acessar', + + + // Top Nav + 'toggle_navigation' => 'Toggle Navigation', + 'n_builds_pending' => '%d builds pending', + 'n_builds_running' => '%d builds running', + 'edit_profile' => 'Editar Perfil', + 'sign_out' => 'Sair', + 'branch_x' => 'Branch: %s', + 'created_x' => 'Criado: %s', + 'started_x' => 'Iniciado: %s', + + // Sidebar + 'hello_name' => 'Olá, %s', + 'dashboard' => 'Dashboard', + 'admin_options' => 'Opções do Admin', + 'add_project' => 'Adicionar Projeto', + 'settings' => 'Configurações', + 'manage_users' => 'Gerênciar Usuários', + 'plugins' => 'Plugins', + 'view' => 'View', + 'build_now' => 'Compilar Agora', + 'edit_project' => 'Editar Projeto', + 'delete_project' => 'Deletar Projeto', + + // Project Summary: + 'no_builds_yet' => 'Nenhuma compilação ainda!', + 'x_of_x_failed' => '%d out of the last %d builds failed.', + 'x_of_x_failed_short' => '%d / %d failed.', + 'last_successful_build' => ' The last successful build was %s.', + 'never_built_successfully' => ' This project has never built successfully.', + 'all_builds_passed' => 'All of the last %d builds passed.', + 'all_builds_passed_short' => '%d / %d passed.', + 'last_failed_build' => ' The last failed build was %s.', + 'never_failed_build' => ' This project has never failed a build.', + 'view_project' => 'Ver projeto', + + // Timeline: + 'latest_builds' => 'Últimas compilações', + 'pending' => 'Pendente', + 'running' => 'Correndo', + 'success' => 'Sucesso', + 'failed' => 'Fracassado', + 'manual_build' => 'Compilação manual', + + // Add/Edit Project: + 'new_project' => 'New Project', + 'project_x_not_found' => 'Project with ID %d does not exist.', + 'project_details' => 'Detalhes do Projeto', + 'public_key_help' => 'Para tornar mais fácil de começar, Geramos Um par de chaves SSH para você usar para este projeto. Para usá-lo, basta adicionar a seguinte chave pública na seção "deploy keys" no provedor onde hospeda seu código.', + 'select_repository_type' => 'Selecione o tipo de repositório...', + 'github' => 'GitHub', + 'bitbucket' => 'Bitbucket', + 'gitlab' => 'GitLab', + 'remote' => 'Remote URL', + 'local' => 'Local Path', + 'hg' => 'Mercurial', + 'svn' => 'Subversion', + + 'where_hosted' => 'Onde seu projeto está hospedado?', + 'choose_github' => 'Choose a GitHub repository:', + + 'repo_name' => 'Nome do repositório / URL (Remota) ou Caminho (Local)', + 'project_title' => 'Titulo do projeto', + 'project_private_key' => 'Chave privada usada para acessar o repositório + (Deixe em branco para controles remotos locais e/ou anônimos)', + 'build_config' => 'PHP Censor construir configuração para este projeto + (if you cannot add a .php-censor.yml (.phpci.yml|phpci.yml) file in the project repository)', + 'default_branch' => 'Nome padrão do branch', + 'allow_public_status' => 'Habilitar página de status pública e imagem para este projeto?', + 'archived' => 'Arquivado', + 'archived_menu' => 'Arquivado', + 'save_project' => 'Salvar Projeto', + + 'error_mercurial' => 'Mercurial repository URL must be start with http:// or https://', + 'error_remote' => 'Repository URL must be start with git://, http:// or https://', + 'error_gitlab' => 'GitLab Repository name must be in the format "user@domain.tld:owner/repo.git"', + 'error_github' => 'Repository name must be in the format "owner/repo"', + 'error_bitbucket' => 'Repository name must be in the format "owner/repo"', + 'error_path' => 'The path you specified does not exist.', + + // View Project: + 'all_branches' => 'All Branches', + 'builds' => 'Builds', + 'id' => 'ID', + 'date' => 'Date', + 'project' => 'Project', + 'commit' => 'Commit', + 'branch' => 'Branch', + 'status' => 'Status', + 'prev_link' => '« Prev', + 'next_link' => 'Next »', + 'public_key' => 'Public Key', + 'delete_build' => 'Delete Build', + + 'webhooks' => 'Webhooks', + 'webhooks_help_github' => 'To automatically build this project when new commits are pushed, add the URL below + as a new "Webhook" in the Webhooks + and Services section of your GitHub repository.', + + 'webhooks_help_gitlab' => 'To automatically build this project when new commits are pushed, add the URL below + as a "WebHook URL" in the Web Hooks section of your GitLab repository.', + + 'webhooks_help_bitbucket' => 'To automatically build this project when new commits are pushed, add the URL below + as a "POST" service in the + + Services section of your Bitbucket repository.', + + // View Build + 'errors' => 'Erros', + 'information' => 'Informação', + + 'build_x_not_found' => 'Build with ID %d does not exist.', + 'build_n' => 'Compilação %d', + 'rebuild_now' => 'Recompilar Agora', + + + 'committed_by_x' => 'Committed by %s', + 'commit_id_x' => 'Commit: %s', + + 'chart_display' => 'This chart will display once the build has completed.', + + 'build' => 'Build', + 'lines' => 'Lines', + 'comment_lines' => 'Comment lines', + 'noncomment_lines' => 'Non-Comment lines', + 'logical_lines' => 'Logical lines', + 'lines_of_code' => 'Lines of code', + 'build_log' => 'Log de compilação', + 'quality_trend' => 'Quality trend', + 'codeception_errors' => 'Codeception errors', + 'phpmd_warnings' => 'PHPMD warnings', + 'phpcs_warnings' => 'PHPCS warnings', + 'phpcs_errors' => 'PHPCS errors', + 'phplint_errors' => 'Lint errors', + 'phpunit_errors' => 'PHPUnit errors', + 'phpdoccheck_warnings' => 'Missing docblocks', + 'issues' => 'Issues', + + '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' => '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', + 'method' => 'Method', + 'message' => 'Message', + 'start' => 'Start', + 'end' => 'End', + 'from' => 'From', + 'to' => 'To', + 'result' => 'Result', + 'ok' => 'OK', + 'took_n_seconds' => 'Took %d seconds', + 'build_started' => 'Build Started', + 'build_finished' => 'Build Finished', + 'test_message' => 'Message', + 'test_no_message' => 'No message', + 'test_success' => 'Successful: %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', + 'password_change' => 'Password (leave blank if you don\'t want to change)', + 'save' => 'Save »', + 'update_your_details' => 'Update your details', + 'your_details_updated' => 'Your details have been updated.', + 'add_user' => 'Adicionar Usuário', + 'is_admin' => 'É Administrador?', + 'yes' => 'Sim', + 'no' => 'Não', + 'edit' => 'Edit.', + 'edit_user' => 'Editar Usuário', + 'delete_user' => 'Deletar Usuário', + 'user_n_not_found' => 'User with ID %d does not exist.', + 'is_user_admin' => 'Este usuário é um administrador?', + 'save_user' => 'Salvar Usuário', + + // Settings: + 'settings_saved' => 'Your settings have been saved.', + 'settings_check_perms' => 'Your settings could not be saved, check the permissions of your config.yml file.', + 'settings_cannot_write' => 'PHP Censor cannot write to your config.yml file, settings may not be saved properly + until this is rectified.', + 'settings_github_linked' => 'Your GitHub account has been linked.', + 'settings_github_not_linked' => 'Your GitHub account could not be linked.', + 'build_settings' => 'Build Settings', + 'github_application' => 'GitHub Application', + 'github_sign_in' => 'Before you can start using GitHub, you need to sign in and grant + PHP Censor access to your account.', + 'github_linked' => 'PHP Censor is successfully linked to GitHub account.', + 'github_where_to_find' => 'Where to find these...', + 'github_where_help' => 'If you own the application you would like to use, you can find this information within your + applications settings area.', + + 'email_settings' => 'Email Settings', + 'email_settings_help' => 'Before PHP Censor can send build status emails, + you need to configure your SMTP settings below.', + + 'application_id' => 'Application ID', + 'application_secret' => 'Application Secret', + + 'smtp_server' => 'SMTP Server', + 'smtp_port' => 'SMTP Port', + 'smtp_username' => 'SMTP Username', + 'smtp_password' => 'SMTP Password', + 'from_email_address' => 'From Email Address', + 'default_notification_address' => 'Default Notification Email Address', + 'use_smtp_encryption' => 'Use SMTP Encryption?', + 'none' => 'None', + 'ssl' => 'SSL', + 'tls' => 'TLS', + + 'failed_after' => 'Consider a build failed after', + '5_mins' => '5 Minutes', + '15_mins' => '15 Minutes', + '30_mins' => '30 Minutes', + '1_hour' => '1 Hour', + '3_hours' => '3 Hours', + + // Plugins + 'cannot_update_composer' => 'PHP Censor cannot update composer.json for you as it is not writable.', + 'x_has_been_removed' => '%s has been removed.', + 'x_has_been_added' => '%s has been added to composer.json for you and will be installed next time + you run composer update.', + 'enabled_plugins' => 'Plugins Habilitados', + 'provided_by_package' => 'Provided By Package', + 'installed_packages' => 'Pacotes instalados', + 'suggested_packages' => 'Suggested Packages', + 'title' => 'Title', + 'description' => 'Description', + 'version' => 'Version', + 'install' => 'Install »', + 'remove' => 'Remove »', + 'search_packagist_for_more' => 'Search Packagist for more packages', + 'search' => 'Search »', + + // Summary plugin + 'build-summary' => 'Summary', + 'stage' => 'Stage', + 'duration' => 'Duration', + 'plugin' => 'Plugin', + 'stage_setup' => 'Setup', + 'stage_test' => 'Test', + 'stage_complete' => 'Complete', + 'stage_success' => 'Success', + 'stage_failure' => 'Failure', + 'stage_broken' => 'Broken', + 'stage_fixed' => 'Fixed', + + // Update + 'update_app' => 'Update the database to reflect modified models.', + 'updating_app' => 'Updating PHP Censor database: ', + 'not_installed' => 'PHP Censor does not appear to be installed.', + 'install_instead' => 'Please install PHP Censor via php-censor:install instead.', + + // Build Plugins: + 'passing_build' => 'Passing Build', + 'failing_build' => 'Failing Build', + 'log_output' => 'Log Output: ', + + // Error Levels: + 'critical' => 'Critical', + 'high' => 'High', + 'normal' => 'Normal', + 'low' => 'Low', + + // Plugins that generate errors: + 'php_mess_detector' => 'PHP Mess Detector', + 'php_code_sniffer' => 'PHP Code Sniffer', + 'php_unit' => 'PHP Unit', + 'php_cpd' => 'PHP Copy/Paste Detector', + 'php_docblock_checker' => 'PHP Docblock Checker', + 'behat' => 'Behat', + 'technical_debt' => 'Technical Debt', +]; diff --git a/src/PHPCensor/Languages/lang.ru.php b/src/PHPCensor/Languages/lang.ru.php new file mode 100644 index 0000000..a4f680a --- /dev/null +++ b/src/PHPCensor/Languages/lang.ru.php @@ -0,0 +1,408 @@ + 'Русский', + 'language' => 'Язык', + 'per_page' => 'Количество элементов на странице', + 'default' => 'По умолчанию', + + // Log in: + 'log_in_to_app' => 'Войти в PHP Censor', + 'login_error' => 'Неправильный email или пароль', + 'forgotten_password_link' => 'Забыли пароль?', + 'reset_emailed' => 'Вы получите письмо со ссылкой на сброс пароля.', + 'reset_header' => 'Не волнуйтесь!
Просто введите ваш email, и вам придет письмо со ссылкой на сброс пароля.', + 'reset_email_address' => 'Введите ваш email:', + 'reset_send_email' => 'Сброс пароля', + 'reset_enter_password' => 'Пожалуйста, введите новый пароль', + 'reset_new_password' => 'Новый пароль:', + 'reset_change_password' => 'Сменить пароль', + 'reset_no_user_exists' => 'Пользователь с таким email-адресом не найден, пожалуйста, попробуйте еще раз.', + 'reset_email_body' => 'Привет %s, + +Вы получили это письмо, потому что вы или кто-то другой запросили сброс пароля в PHP Censor. + +Если это были вы, пожалуйста, перейдите по ссылке для сброса пароля: %ssession/reset-password/%d/%s, + +иначе игнорируйте это письмо и ничего не предпринимайте. + +Спасибо, + +PHP Censor', + + 'reset_email_title' => 'Сброс пароля PHP Censor для %s', + 'reset_invalid' => 'Некорректный запрос на сброс пароля.', + 'email_address' => 'Email', + 'login' => 'Логин / Email', + 'password' => 'Пароль', + 'remember_me' => 'Запомнить меня', + 'log_in' => 'Войти', + + // Top Nav + 'toggle_navigation' => 'Скрыть/показать панель навигации', + 'n_builds_pending' => 'Сборок ожидает: %d', + 'n_builds_running' => 'Сборок запущено: %d', + 'edit_profile' => 'Редактировать профиль', + 'sign_out' => 'Выйти', + 'branch_x' => 'Ветка: %s', + 'created_x' => 'Создан: %s', + 'started_x' => 'Запущен: %s', + 'environment_x' => 'Окружение: %s', + + // Sidebar + 'hello_name' => 'Привет, %s', + 'dashboard' => 'Панель управления', + 'admin_options' => 'Меню администратора', + 'add_project' => 'Добавить проект', + 'project_groups' => 'Группы проектов', + 'settings' => 'Настройки', + 'manage_users' => 'Пользователи', + 'plugins' => 'Плагины', + 'view' => 'Отчет', + 'build_now' => 'Собрать', + 'build_now_debug' => 'Собрать в режиме отладки', + 'edit_project' => 'Редактировать проект', + 'delete_project' => 'Удалить проект', + + // Project Summary: + 'no_builds_yet' => 'Нет сборок!', + 'x_of_x_failed' => '%d из последних %d сборок были провалены.', + 'x_of_x_failed_short' => '%d / %d провалены.', + 'last_successful_build' => ' Последняя успешная сборка была %s.', + 'never_built_successfully' => ' Этот проект никогда не собирался успешно.', + 'all_builds_passed' => 'Все последние сборки (%d) прошли успешно.', + 'all_builds_passed_short' => '%d / %d успешные.', + 'last_failed_build' => ' Последней проваленной сборкой была %s.', + 'never_failed_build' => ' У этого проекта никогда не было проваленных сборок.', + 'view_project' => 'Обзор проекта', + 'projects_with_build_errors' => 'Ошибки сборки', + + // Timeline: + 'latest_builds' => 'Последние сборки', + 'pending' => 'Ожидает', + 'running' => 'Запущена', + 'success' => 'Успешно', + 'failed' => 'Провал', + 'failed_allowed' => 'Провал (Допустим)', + 'error' => 'Ошибка', + 'skipped' => 'Пропущено', + 'trace' => 'Стек вызова', + 'manual_build' => 'Запущена вручную', + + // Add/Edit Project: + 'new_project' => 'Новый проект', + 'project_x_not_found' => 'Проекта с ID %d не существует.', + 'project_details' => 'Подробности проекта', + 'public_key_help' => 'Чтобы было легче начать, мы сгенерировали пару SSH-ключей для использования в вашем проекте. + Для их использования, просто добавьте эту публичную часть ключа в поле "deploy keys" на выбранном вами хостинге исходного кода.', + 'select_repository_type' => 'Выберите тип репозитория...', + 'github' => 'GitHub', + 'bitbucket' => 'Bitbucket', + 'gitlab' => 'GitLab', + 'remote' => 'Внешний URL', + 'local' => 'Локальный путь', + 'hg' => 'Mercurial', + 'svn' => 'Subversion', + + 'where_hosted' => 'Расположение проекта', + 'choose_github' => 'Выберите GitHub репозиторий:', + + 'repo_name' => 'Репозиторий / Внешний URL / Локальный путь', + 'project_title' => 'Название проекта', + 'project_private_key' => 'Приватный ключ для доступа к репозиторию + (оставьте поле пустым для локального использования и/или анонимного доступа)', + 'build_config' => 'Конфигурация сборки проекта для PHP Censor + (если вы не добавили файл .php-censor.yml (.phpci.yml|phpci.yml) в репозиторий вашего проекта)', + 'default_branch' => 'Ветка по умолчанию', + 'default_branch_only' => 'Собирать только ветку по умолчанию', + 'allow_public_status' => 'Разрешить публичный статус и изображение (статуса) для проекта', + 'archived' => 'Архивный', + 'archived_menu' => 'Архив', + 'save_project' => 'Сохранить проект', + 'environments_label' => 'Окружения (yaml)', + + 'error_mercurial' => 'URL репозитория Mercurial должен начинаться с http:// или https://', + 'error_remote' => 'URL репозитория должен начинаться с git://, http:// или https://', + 'error_gitlab' => 'Имя репозитория в GitLab должно иметь формат: "user@domain.tld:owner/repo.git"', + 'error_github' => 'Имя репозитория должно иметь формат: "owner/repo"', + 'error_bitbucket' => 'Имя репозитория должно иметь формат: "owner/repo"', + 'error_path' => 'Пути, который вы указали, не существует.', + + // View Project: + 'all_branches' => 'Все ветки', + 'all' => 'Все', + 'builds' => 'Сборки', + 'id' => 'ID', + 'date' => 'Дата', + 'project' => 'Проект', + 'commit' => 'Коммит', + 'branch' => 'Ветка', + 'environment' => 'Окружение', + 'status' => 'Статус', + 'prev_link' => '« Пред.', + 'next_link' => 'След. »', + 'public_key' => 'Публичный ключ', + 'delete_build' => 'Удалить сборку', + 'build_source' => 'Источник сборки', + + 'source_unknown' => 'Неизвестный', + 'source_manual_web' => 'Вручную (Через Web)', + 'source_manual_console' => 'Вручную (Через CLI)', + 'source_periodical' => 'Переодический', + 'source_webhook' => 'Webhook', + + 'webhooks' => 'Webhooks', + 'webhooks_help_github' => 'Чтобы Автоматически собирать этот проект при публикации новых коммитов, добавьте URL ниже в качестве нового хука в разделе настроек Webhooks + and Services вашего GitHub репозитория.', + + 'webhooks_help_gitlab' => 'Чтобы Автоматически собирать этот проект при публикации новых коммитов, добавьте URL ниже в качестве "WebHook URL" + в разделе "Web Hooks" вашего GitLab репозитория.', + + 'webhooks_help_bitbucket' => 'Чтобы Автоматически собирать этот проект при публикации новых коммитов, добавьте URL ниже как "POST" сервис в разделе + Services вашего Bitbucket репозитория.', + + // Project Groups + 'group_projects' => 'Группы проектов', + 'project_group' => 'Группа проекта', + 'group_count' => 'Количество проектов', + 'group_edit' => 'Редактировать', + 'group_delete' => 'Удалить', + 'group_add' => 'Добавить группу', + 'group_add_edit' => 'Добавить / изменить группу', + 'group_title' => 'Название группы', + 'group_save' => 'Сохранить группу', + + // View Build + 'errors' => 'Ошибки', + 'information' => 'Информация', + 'build_x_not_found' => 'Сборки с ID %d не существует.', + 'build_n' => 'Сборка %d', + 'rebuild_now' => 'Пересобрать сейчас', + + 'committed_by_x' => 'Отправил %s', + 'commit_id_x' => 'Коммит: %s', + + 'chart_display' => 'Этот график будет показан после окончания сборки.', + + 'build' => 'Сборка', + 'lines' => 'Строк', + 'comment_lines' => 'Строк комментариев', + 'noncomment_lines' => 'Строк некомментариев', + 'logical_lines' => 'Строк логики', + 'lines_of_code' => 'Строк кода', + 'build_log' => 'Лог сборки', + 'quality_trend' => 'Тенденция качества', + 'codeception_errors' => 'Ошибки Codeception', + 'phpmd_warnings' => 'Предупреждения PHPMD', + 'phpcs_warnings' => 'Предупреждения PHPCS', + 'phpcs_errors' => 'Ошибки PHPCS', + 'phplint_errors' => 'Ошибки Lint', + 'phpunit_errors' => 'Ошибки PHPUnit', + 'phpcpd_warnings' => 'Предупреждения PHP Copy/Paste Detector', + 'phpdoccheck_warnings' => 'Пропущенные Docblocks', + 'issues' => 'Проблемы', + 'merged_branches' => 'Объеденяемые ветки', + + 'phpcpd' => 'PHP Copy/Paste Detector', + 'phpcs' => 'PHP Code Sniffer', + 'phpdoccheck' => 'Missing Docblocks', + 'phpmd' => 'PHP Mess Detector', + 'phpspec' => 'PHP Spec', + 'phpunit' => 'PHP Unit', + + 'codeception_feature' => 'Свойство', + 'codeception_suite' => 'Набор', + 'codeception_time' => 'Время', + 'codeception_synopsis' => 'Тестов выполнено: %1$d (за %2$f сек.). Провалов: %3$d.', + + 'suite' => 'Набор', + 'test' => 'Тест', + 'file' => 'Файл', + 'line' => 'Строка', + 'class' => 'Класс', + 'method' => 'Метод', + 'message' => 'Сообщение', + 'start' => 'Начало', + 'end' => 'Конец', + 'from' => 'Из', + 'to' => 'В', + 'result' => 'Результат', + 'ok' => 'OK', + 'took_n_seconds' => 'Заняло секунд: %d', + 'build_started' => 'Сборка запущена', + 'build_finished' => 'Сборка окончена', + 'test_message' => 'Сообщение', + 'test_no_message' => 'Нет сообщений', + 'test_success' => 'Успешно: %d', + 'test_fail' => 'Провалено: %d', + 'test_skipped' => 'Пропущено: %d', + 'test_error' => 'Ошибок: %d', + 'test_todo' => 'Todo: %d', + 'test_total' => 'Тестов: %d', + + // Users + 'name' => 'Имя', + 'password_change' => 'Пароль (оставьте поле пустым, если не собираетесь менять его)', + 'save' => 'Сохранить »', + 'update_your_details' => 'Обновить свои данные', + 'your_details_updated' => 'Ваши данные были обновлены.', + 'add_user' => 'Добавить пользователя', + 'is_admin' => 'Является администратором', + 'yes' => 'Да', + 'no' => 'Нет', + 'edit' => 'Править', + 'edit_user' => 'Редактировать пользователя', + 'delete_user' => 'Удалить пользователя', + 'user_n_not_found' => 'Пользователя с ID %d не существует.', + 'is_user_admin' => 'Этот пользователь является администратором', + 'save_user' => 'Сохранить пользователя', + + // Settings: + 'settings_saved' => 'Ваши настройки были сохранены.', + 'settings_check_perms' => 'Ваши настройки не могут быть сохранены, проверьте права на файл настроек config.yml.', + 'settings_cannot_write' => 'PHP Censor не может записать config.yml файл, настройки не могут быть сохранены корректно, пока это не будет исправлено.', + 'settings_github_linked' => 'Ваш GitHub аккаунт привязан.', + 'settings_github_not_linked' => 'Ваш GitHub аккаунт не может быть привязан.', + 'build_settings' => 'Настройки сборки', + 'github_application' => 'GitHub приложение', + 'github_sign_in' => 'Перед тем как начать использовать GitHub аккаунт, вам необходимо войти и разрешить доступ для + PHP Censor до вашего аккаунта.', + 'github_app_linked' => 'PHP Censor успешно привязал GitHub аккаунт.', + 'github_where_to_find' => 'Где это найти...', + 'github_where_help' => 'Если вы владелец приложения, которое вы хотели бы использовать, то вы можете найти информацию об этом в разделе + applications настроек.', + + 'email_settings' => 'Настройки email', + 'email_settings_help' => 'Перед тем, как PHP Censor начнет отсылать статус сборок по почте, + вам необходимо настроить параметры SMTP ниже.', + + 'application_id' => 'ID приложения', + 'application_secret' => 'Секретный ключ приложения', + + 'smtp_server' => 'SMTP сервер', + 'smtp_port' => 'SMTP порт', + 'smtp_username' => 'SMTP пользователь', + 'smtp_password' => 'SMTP пароль', + 'from_email_address' => 'Отправлять с email', + 'default_notification_address' => 'Email по умолчанию для оповещений', + 'use_smtp_encryption' => 'Использовать SMTP шифрование', + 'none' => 'Нет', + 'ssl' => 'SSL', + 'tls' => 'TLS', + + 'failed_after' => 'Признать сборку проваленной по прошествии', + '5_mins' => '5 минут', + '15_mins' => '15 минут', + '30_mins' => '30 минут', + '1_hour' => '1 часа', + '3_hours' => '3 часов', + + // Plugins + 'cannot_update_composer' => 'PHP Censor не может обновить composer.json, если он недоступен на запись.', + 'x_has_been_removed' => '%s был удален.', + 'x_has_been_added' => '%s был добавлен в composer.json и будет установлен, как только вы запустите composer update.', + 'enabled_plugins' => 'Включенные плагины', + 'provided_by_package' => 'Предоставляется пакетом', + 'installed_packages' => 'Установленные пакеты', + 'suggested_packages' => 'Рекомендуемые пакеты', + 'title' => 'Название', + 'description' => 'Описание', + 'version' => 'Версия', + 'install' => 'Установить »', + 'remove' => 'Удалить »', + 'search_packagist_for_more' => 'Искать на Packagist', + 'search' => 'Искать »', + + // Summary plugin + 'build-summary' => 'Сводка', + 'stage' => 'Этап', + 'duration' => 'Продолжительность', + 'seconds' => 'сек.', + 'plugin' => 'Плагин', + 'stage_setup' => 'Установка', + 'stage_test' => 'Тестирование', + 'stage_complete' => 'Завершение', + 'stage_success' => 'Успешное завершение', + 'stage_failure' => 'Провал', + 'stage_broken' => 'Поломка', + 'stage_fixed' => 'Исправление', + 'severity' => 'Уровень', + + 'all_plugins' => 'Все плагины', + 'all_severities' => 'Все уровни', + 'filters' => 'Фильтры', + 'errors_selected' => 'Ошибок выбрано', + + 'build_details' => 'Информация о сборке', + 'commit_details' => 'Информация о коммитe', + 'committer' => 'Автор коммита', + 'commit_message' => 'Сообщение в коммите', + 'timing' => 'Тайминг', + 'created' => 'Создана', + 'started' => 'Началась', + 'finished' => 'Закончилась', + + // Update + 'update_app' => 'Обновите базу данных с учетом обновленных моделей.', + 'updating_app' => 'Обновление базы данных PHP Censor: ', + 'not_installed' => 'PHP Censor не может быть установлен.', + 'install_instead' => 'Пожалуйста, установите PHP Censor с помощью команды php-censor:install.', + + // Build Plugins: + 'passing_build' => 'Успех сборки', + 'failing_build' => 'Провал сборки', + 'log_output' => 'Вывод лога: ', + + // Error Levels: + 'critical' => 'Критичный', + 'high' => 'Высокий', + 'normal' => 'Нормальный', + 'low' => 'Низкий', + + // Plugins that generate errors: + 'php_mess_detector' => 'PHP Mess Detector', + 'php_code_sniffer' => 'PHP Code Sniffer', + 'php_unit' => 'PHP Unit', + 'php_cpd' => 'PHP Copy/Paste Detector', + 'php_docblock_checker' => 'PHP Docblock Checker', + 'composer' => 'Composer', + 'php_loc' => 'PHP LOC', + 'php_parallel_lint' => 'PHP Parallel Lint', + 'email' => 'Email', + 'atoum' => 'Atoum', + 'behat' => 'Behat', + 'campfire' => 'Campfire', + 'clean_build' => 'Clean Build', + 'codeception' => 'Codeception', + 'copy_build' => 'Copy Build', + 'deployer' => 'Deployer', + 'env' => 'Env', + 'grunt' => 'Grunt', + 'hipchat_notify' => 'Hipchat', + 'irc' => 'IRC', + 'lint' => 'Lint', + 'mysql' => 'MySQL', + 'package_build' => 'Package Build', + 'pdepend' => 'PDepend', + 'pgsql' => 'PostgreSQL', + 'phar' => 'Phar', + 'phing' => 'Phing', + 'php_cs_fixer' => 'PHP Coding Standards Fixer', + 'php_spec' => 'PHP Spec', + 'shell' => 'Shell', + 'slack_notify' => 'Slack', + 'technical_debt' => 'Technical Debt', + 'xmpp' => 'XMPP', + 'security_checker' => 'SensioLabs Security Checker', + + 'confirm_message' => 'Элемент будет удален навсегда. Вы уверены?', + 'confirm_title' => 'Подтвержение удаления', + 'confirm_ok' => 'Удалить', + 'confirm_cancel' => 'Отмена', + 'confirm_success' => 'Элемент успешно удален.', + 'confirm_failed' => 'Удаление провалилось! Ответ сервера: ', + + 'public_status_title' => 'Публичный статус', + 'public_status_image' => 'Иконка статуса', + 'public_status_page' => 'Страница публичного статуса', +]; diff --git a/src/PHPCensor/Languages/lang.uk.php b/src/PHPCensor/Languages/lang.uk.php new file mode 100644 index 0000000..def1391 --- /dev/null +++ b/src/PHPCensor/Languages/lang.uk.php @@ -0,0 +1,291 @@ + 'Українська', + 'language' => 'Мова', + + // Log in: + 'log_in_to_app' => 'Увійти до PHP Censor', + 'login_error' => 'Невірний email або пароль', + 'forgotten_password_link' => 'Забули свій пароль?', + 'reset_emailed' => 'Ми відправили вам посилання для скидання вашого паролю.', + 'reset_header' => 'Не хвилюйтесь!
Просто введіть ваш email +і вам буде надіслано листа із посиланням на скидання паролю.', + 'reset_email_address' => 'Введіть свою email адресу:', + 'reset_send_email' => 'Скидання пароля', + 'reset_enter_password' => 'Введіть, будь ласка, новий пароль', + 'reset_new_password' => 'Новий пароль:', + 'reset_change_password' => 'Змінити пароль', + 'reset_no_user_exists' => 'Не існує користувача з такою email адресою, будь ласка, повторіть знову.', + 'reset_email_body' => 'Привіт, %s, + +Ви отримали цей лист, тому що ви або хтось інший запросили скидання пароля в PHP Censor. + +Якщо це були ви, будь ласка, перейдіть за посиланням нижче для скидання пароля: %ssession/reset-password/%d/%s, + +або ж проігноруйте цей лист та нічого не робіть. + +Дякуємо, + +PHP Censor', + + 'reset_email_title' => 'Скидання пароль PHP Censor для %s', + 'reset_invalid' => 'Невірний запит скидання паролю.', + 'email_address' => 'Email адреса', + 'login' => 'Логин / Email адреса', + 'password' => 'Пароль', + 'log_in' => 'Увійти', + + + // Top Nav + 'toggle_navigation' => 'Сховати/відобразити панель навігації', + 'n_builds_pending' => '%d збірок очікує', + 'n_builds_running' => '%d збірок виконується', + 'edit_profile' => 'Редагувати профіль', + 'sign_out' => 'Вийти', + 'branch_x' => 'Гілка: %s', + 'created_x' => 'Створено: %s', + 'started_x' => 'Розпочато: %s', + + // Sidebar + 'hello_name' => 'Привіт, %s', + 'dashboard' => 'Панель управління', + 'admin_options' => 'Меню адміністратора', + 'add_project' => 'Додати проект', + 'settings' => 'Налаштування', + 'manage_users' => 'Управління користувачами', + 'plugins' => 'Плагіни', + 'view' => 'Переглянути', + 'build_now' => 'Зібрати', + 'edit_project' => 'Редагувати проект', + 'delete_project' => 'Видалити проект', + + // Project Summary: + 'no_builds_yet' => 'Немає збірок!', + 'x_of_x_failed' => '%d із останніх %d збірок були провалені.', + 'x_of_x_failed_short' => '%d / %d провалені.', + 'last_successful_build' => 'Останнью успішною збіркою була %s.', + 'never_built_successfully' => 'У цього проекта ніколи не було успішних збірок.', + 'all_builds_passed' => 'Усі із останніх %d збірок успішні.', + 'all_builds_passed_short' => '%d / %d успішні.', + 'last_failed_build' => 'Останньою проваленою збіркою була %s.', + 'never_failed_build' => 'У цього проекта ніколи не було провалених збірок.', + 'view_project' => 'Переглянути проект', + + // Timeline: + 'latest_builds' => 'Останні збірки', + 'pending' => 'Очікує', + 'running' => 'Виконується', + 'success' => 'Успіх', + 'failed' => 'Провалена', + 'manual_build' => 'Ручна збірка', + + // Add/Edit Project: + 'new_project' => 'Новий проект', + 'project_x_not_found' => 'Проект із ID %d не існує.', + 'project_details' => 'Деталі проекта', + 'public_key_help' => 'Для полегшення початку, ми згенерували пару SSH-ключів для вас для використання в цьому проекті. +Для їх використання - просто додайте наступний публічний ключ у розділ "deploy keys" обраної вами системи зберігання програмного коду.', + 'select_repository_type' => 'Оберіть тип репозиторію...', + 'github' => 'GitHub', + 'bitbucket' => 'Bitbucket', + 'gitlab' => 'GitLab', + 'remote' => 'Віддалений URL', + 'local' => 'Локальний шлях', + 'hg' => 'Mercurial', + + 'where_hosted' => 'Де зберігається ваш проект?', + 'choose_github' => 'Оберіть GitHub репозиторій:', + + 'repo_name' => 'Ім’я репозиторія / URL (зовнішній) / Шлях (локальний)', + 'project_title' => 'Заголовок проекту', + 'project_private_key' => 'Приватний ключ доступу до репозиторія +(залишити поле порожнім для локального використання та/або анонімного доступу)', + 'build_config' => 'Конфігурація збірки цього проекта для PHP Censor +(якщо ви не додали файл .php-censor.yml (.phpci.yml|phpci.yml) до репозиторію вашого проекту)', + 'default_branch' => 'Назва гілки за замовчуванням', + 'allow_public_status' => 'Увімкнути публічну сторінку статусу та зображення для цього проекта?', + 'archived' => 'Архівний', + 'archived_menu' => 'Архів', + 'save_project' => 'Зберегти проект', + + 'error_mercurial' => 'URL репозиторію Mercurial повинен починатись із http:// або https://', + 'error_remote' => 'URL репозиторію повинен починатись із git://, http:// або https://', + 'error_gitlab' => 'Ім’я репозиторія GitLab повинно бути у форматі "user@domain.tld:owner/repo.git"', + 'error_github' => 'Ім’я репозиторія повинно відповідати формату "owner/repo"', + 'error_bitbucket' => 'Ім’я репозиторія повинно відповідати формату "owner/repo"', + 'error_path' => 'Вказаний шлях не існує.', + + // View Project: + 'all_branches' => 'Усі гілки', + 'builds' => 'Збірки', + 'id' => 'ID', + 'date' => 'Дата', + 'project' => 'Проект', + 'commit' => 'Комміт', + 'branch' => 'Гілка', + 'status' => 'Статус', + 'prev_link' => '« Попер.', + 'next_link' => 'Наст. »', + 'public_key' => 'Публічний ключ', + 'delete_build' => 'Видалити збірку', + + 'webhooks' => 'Webhooks', + 'webhooks_help_github' => 'Для автоматичної збірки цього проекту, при надходженні нових комітів, додайте наступний URL +у якості нового "Webhook" у розділі налаштувань +Webhooks and Services +вашого GitHub репозиторію.', + + 'webhooks_help_gitlab' => 'Для автоматичної збірки цього проекту, при надходженні нових комітів, додайте наступний URL +у якості нового "WebHook URL" у розділі "Web Hooks" вашого GitLab репозиторію.', + + 'webhooks_help_bitbucket' => 'Для автоматичної збірки цього проекту, при надходженні нових комітів, додайте наступний URL +у якості нового "POST" сервісу у розділі +Services +вашого Bitbucket репозиторію.', + + // View Build + 'build_x_not_found' => 'Збірка із ID %d не існує.', + 'build_n' => 'Збірка %d', + 'rebuild_now' => 'Перезібрати зараз', + + + 'committed_by_x' => 'Комміт від %s', + 'commit_id_x' => 'Комміт: %s', + + 'chart_display' => 'Цей графік відобразиться після завершення збірки.', + + 'build' => 'Збірка', + 'lines' => 'Рядків', + 'comment_lines' => 'Рядків коментарів', + 'noncomment_lines' => 'Рядків не коментарів', + 'logical_lines' => 'Рядків логіки', + 'lines_of_code' => 'Рядки коду', + 'build_log' => 'Лог збірки', + 'quality_trend' => 'Тенденція якості', + 'codeception_errors' => 'Помилки Codeception', + 'phpmd_warnings' => 'Попередження PHPMD', + 'phpcs_warnings' => 'Попередження PHPCS', + 'phpcs_errors' => 'Помилки PHPCS', + 'phplint_errors' => 'Помилки Lint', + 'phpunit_errors' => 'Помилки PHPUnit', + 'phpdoccheck_warnings' => 'Відсутні Docblocks', + 'issues' => 'Проблеми', + + 'codeception' => 'Codeception', + 'phpcpd' => 'PHP Copy/Paste Detector', + 'phpcs' => 'PHP Code Sniffer', + 'phpdoccheck' => 'Відсутні Docblocks', + 'phpmd' => 'PHP Mess Detector', + 'phpspec' => 'PHP Spec', + 'phpunit' => 'PHP Unit', + + 'file' => 'Файл', + 'line' => 'Рядок', + 'class' => 'Клас', + 'method' => 'Метод', + 'message' => 'Повідомлення', + 'start' => 'Запуск', + 'end' => 'Кінець', + 'from' => 'Від', + 'to' => 'До', + 'result' => 'Результат', + 'ok' => 'OK', + 'took_n_seconds' => 'Зайняло %d секунд', + 'build_started' => 'Збірка розпочата', + 'build_finished' => 'Збірка завершена', + 'test_message' => 'Message', + 'test_no_message' => 'No message', + 'test_success' => 'Successful: %d', + 'test_fail' => 'Failures: %d', + 'test_skipped' => 'Skipped: %d', + 'test_error' => 'Errors: %d', + 'test_todo' => 'Todos: %d', + 'test_total' => '%d test(s)', + + // Users + 'name' => 'Ім’я', + 'password_change' => 'Пароль (залишити порожнім, якщо не бажаєте змінювати його)', + 'save' => 'Зберегти »', + 'update_your_details' => 'Оновити ваші деталі', + 'your_details_updated' => 'Ваші деталі були оновлені.', + 'add_user' => 'Додати користувача', + 'is_admin' => 'Адміністратор?', + 'yes' => 'Так', + 'no' => 'Ні', + 'edit' => 'Редагувати', + 'edit_user' => 'Редагувати користувача', + 'delete_user' => 'Видалити користувача', + 'user_n_not_found' => 'Користувач із ID %d не існує.', + 'is_user_admin' => 'Чи є цей користувач адміністратором?', + 'save_user' => 'Зберегти користувача', + + // Settings: + 'settings_saved' => 'Ваші налаштування були збережені.', + 'settings_check_perms' => 'Ваші налаштування не можуть бути збережені, перевірте права на ваш файл налаштувань config.yml.', + 'settings_cannot_write' => 'PHP Censor не може записати файл config.yml, налаштування не будуть коректно збережені, +доки це не буде виправлено.', + 'settings_github_linked' => 'Ваш GitHub аккаунт було підключено.', + 'settings_github_not_linked' => 'Ваш GitHub аккаунт не може бути підключеним.', + 'build_settings' => 'Налаштування збірки', + 'github_application' => 'GitHub додаток', + 'github_sign_in' => 'Перед початком користування GitHub, вам необхідно увійти та надати +доступ для PHP Censor до вашого аккаунту.', + 'github_app_linked' => 'PHP Censor успішно зв\'язаний з аккаунтом GitHub.', + 'github_where_to_find' => 'Де це знайти...', + 'github_where_help' => 'Якщо ви є власником додатку, який би ви хотіли використовувати, то ви можете знайти інформацію про це у розділі +налаштувань ваших додатків.', + + 'email_settings' => 'Налаштування Email', + 'email_settings_help' => 'Перед тим, як PHP Censor почне надсилати статуси збірок на email, +вам необхідно налаштувати параметри SMTP нижче.', + + 'application_id' => 'ID додатка', + 'application_secret' => 'Таємний ключ додатка', + + 'smtp_server' => 'Сервер SMTP', + 'smtp_port' => 'Порт SMTP', + 'smtp_username' => 'Ім’я користувача SMTP', + 'smtp_password' => 'Пароль SMTP', + 'from_email_address' => 'Відправляти з Email', + 'default_notification_address' => 'Email для повідомлень за замовчуванням', + 'use_smtp_encryption' => 'Використовувати SMTP шифрування?', + 'none' => 'Ні', + 'ssl' => 'SSL', + 'tls' => 'TLS', + + 'failed_after' => 'Вважати збірку проваленою після', + '5_mins' => '5 хвилин', + '15_mins' => '15 хвилин', + '30_mins' => '30 хвилин', + '1_hour' => '1 година', + '3_hours' => '3 години', + + // Plugins + 'cannot_update_composer' => 'PHP Censor не може оновити composer.json, оскільки він не є доступним для запису.', + 'x_has_been_removed' => '%s було видалено.', + 'x_has_been_added' => '%s був доданий до composer.json і буде встановлений, як тільки +ви виконаєте composer update.', + 'enabled_plugins' => 'Увімкнені плагіни', + 'provided_by_package' => 'Наданий пакетом', + 'installed_packages' => 'Встановлені пакети', + 'suggested_packages' => 'Запропоновані пакети', + 'title' => 'Заголовок', + 'description' => 'Опис', + 'version' => 'Версія', + 'install' => 'Встановити »', + 'remove' => 'Видалити »', + 'search_packagist_for_more' => 'Знайти більше пакетів на Packagist', + 'search' => 'Знайти »', + + // Update + 'update_app' => 'Оновити базу даних для відображення змінених моделей.', + 'updating_app' => 'Оновлення бази даних PHP Censor:', + 'not_installed' => 'Неможливо встановити PHP Censor.', + 'install_instead' => 'Будь ласка, встановіть PHP Censor через команду php-censor:install.', + + // Build Plugins: + 'passing_build' => 'Успішно збірка', + 'failing_build' => 'Невдала збірка', + 'log_output' => 'Вивід лога:', +]; diff --git a/src/PHPCensor/Languages/lang.zh.php b/src/PHPCensor/Languages/lang.zh.php new file mode 100644 index 0000000..fc96b00 --- /dev/null +++ b/src/PHPCensor/Languages/lang.zh.php @@ -0,0 +1,319 @@ + '简体中文', + 'language' => '语言选择', + + // Log in: + 'log_in_to_app' => '登录 PHP Censor', + 'login_error' => '邮箱或密码错误', + 'forgotten_password_link' => '忘记密码?', + 'reset_emailed' => '已发送重置密码邮件.', + 'reset_header' => '不用担心!
只需要输入您的邮箱地址,我们将会给您的邮箱发送含有重置密码链接的邮件。', + 'reset_email_address' => '输入您的邮箱地址:', + 'reset_send_email' => '邮件重设密码', + 'reset_enter_password' => '请输入新密码', + 'reset_new_password' => '新密码:', + 'reset_change_password' => '更改密码', + 'reset_no_user_exists' => '不存该该邮箱用户,请检查后重试!', + 'reset_email_body' => '您好 %s, + +您收到这封邮件,是因为您或者别人发起了来自PHP Censor的密码重置请求。 + +如果确实是您发起的,请点击链接去重置您的密码:%ssession/reset-password/%d/%s + +否则,请忽视这封邮件,这不会发生任何事情。 + +多谢, + +PHP Censor', + + 'reset_email_title' => '给 %s 来自 PHP Censor 的密码重置邮件', + 'reset_invalid' => '密码重置请求失败!', + 'email_address' => '邮箱地址', + 'password' => '密码', + 'log_in' => '登录', + + + // Top Nav + 'toggle_navigation' => '导航切换', + 'n_builds_pending' => '%d 等待构建', + 'n_builds_running' => '%d 正在构建', + 'edit_profile' => '编辑资料', + 'sign_out' => '注销', + 'branch_x' => '分支: %s', + 'created_x' => '创建于: %s', + 'started_x' => '开始于: %s', + + // Sidebar + 'hello_name' => '您好, %s', + 'dashboard' => '仪表盘', + 'admin_options' => '管理选项', + 'add_project' => '新增项目', + 'settings' => '设置', + 'manage_users' => '用户管理', + 'plugins' => '插件', + 'view' => '查看', + 'build_now' => '立即构建', + 'edit_project' => '编辑项目', + 'delete_project' => '删除项目', + + // Project Summary: + 'no_builds_yet' => '没有任何构建', + 'x_of_x_failed' => '%d 的最后一个 %d 构建失败!', + 'x_of_x_failed_short' => '%d / %d 失败。', + 'last_successful_build' => ' 最后一次构建成功的是 %s.', + 'never_built_successfully' => ' 该项目构建从来没有成功过!', + 'all_builds_passed' => '构建记录中 %d 次构建通过。', + 'all_builds_passed_short' => '%d / %d 通过。', + 'last_failed_build' => ' 最近一次构建失败的是 %s。', + 'never_failed_build' => ' 该项目构建从未失败过。', + 'view_project' => '查看项目', + + // Timeline: + 'latest_builds' => '最近构建', + 'pending' => '等待中', + 'running' => '构建中', + 'success' => '成功', + 'failed' => '失败', + 'manual_build' => 'Manual Build', + + // Add/Edit Project: + 'new_project' => '新项目', + 'project_x_not_found' => '项目 ID %d 不存在。', + 'project_details' => '项目详情', + 'public_key_help' => '为了帮助您更简单的开始项目构建,我们生成了以下 SSH key 对用于该项目。要使用它,请添加以下公钥 “deploy keys” 至您选择的源代码托管平台', + + 'select_repository_type' => '选择仓库类型...', + 'github' => 'GitHub', + 'bitbucket' => 'Bitbucket', + 'gitlab' => 'GitLab', + 'remote' => 'Remote URL', + 'local' => 'Local Path', + 'hg' => 'Mercurial', + 'svn' => 'Subversion', + + 'where_hosted' => '您的代码托管在?', + 'choose_github' => '选择一个 GitHub 仓库:', + + 'repo_name' => '仓库名称 / URL (本地) or Path (本地)', + 'project_title' => '项目标题', + 'project_private_key' => '访问仓库私有秘钥 + (本地或公共仓库可为空)', + 'build_config' => '该项目 PHP Censor 构建配置文件 + (如果您无法在该项目仓库创建 .php-censor.yml (.phpci.yml|phpci.yml) 文件)', + 'default_branch' => '默认分支名称', + 'allow_public_status' => '启用此项目的公共状态页和图像?', + 'archived' => '归档', + 'archived_menu' => '归档', + 'save_project' => '保存项目', + + 'error_mercurial' => 'Mercurial 仓库 URL 必须以 http:// or https:// 开始', + 'error_remote' => '仓库 URL 必须以 git://, http:// or https:// 开始', + 'error_gitlab' => 'GitLab 仓库名称必须符合 "user@domain.tld:owner/repo.git" 格式', + 'error_github' => '仓库名称必须符合 "owner/repo" 格式', + 'error_bitbucket' => '仓库名称必须符合 "owner/repo" 格式', + 'error_path' => '您制定的路径不存在', + + // View Project: + 'all_branches' => 'All Branches', + 'builds' => 'Builds', + 'id' => 'ID', + 'date' => '日期', + 'project' => '项目', + 'commit' => '提交', + 'branch' => '分支', + 'status' => '状态', + 'prev_link' => '« 上一个', + 'next_link' => '下一个 »', + 'public_key' => '公共秘钥', + 'delete_build' => '删除构建', + + 'webhooks' => 'Webhooks', + 'webhooks_help_github' => '要想当您的仓库由新的提交推送时自动构建,请在您的Github仓库的 + Webhooks and Services + 将该URL添加至新增 "Webhook" 中。', + + 'webhooks_help_gitlab' => '要想当您的仓库由新的提交推送时自动构建,请在您的GitLab仓库的 "WebHook URL" 添加该URL。', + + 'webhooks_help_bitbucket' => '要想当您的仓库由新的提交推送时自动构建,请在您的GitLab仓库的 + + Services 将该URL添加成 “POST服务”。', + + // View Build + 'errors' => 'Errors', + 'information' => 'Information', + + 'build_x_not_found' => '构建 ID %d 不存在。', + 'build_n' => 'Build %d', + 'rebuild_now' => '重新构建', + + + 'committed_by_x' => '由 %s 提交', + 'commit_id_x' => '提交: %s', + + 'chart_display' => '构建一旦完成该图表将会显示', + + 'build' => 'Build', + 'lines' => 'Lines', + 'comment_lines' => 'Comment lines', + 'noncomment_lines' => 'Non-Comment lines', + 'logical_lines' => 'Logical lines', + 'lines_of_code' => 'Lines of code', + 'build_log' => 'Build log', + 'quality_trend' => 'Quality trend', + 'codeception_errors' => 'Codeception errors', + 'phpmd_warnings' => 'PHPMD warnings', + 'phpcs_warnings' => 'PHPCS warnings', + 'phpcs_errors' => 'PHPCS errors', + 'phplint_errors' => 'Lint errors', + 'phpunit_errors' => 'PHPUnit errors', + 'phpdoccheck_warnings' => 'Missing docblocks', + 'issues' => 'Issues', + + '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' => 'Technical Debt', + 'behat' => 'Behat', + + 'codeception_feature' => 'Feature', + 'codeception_suite' => 'Suite', + 'codeception_time' => 'Time', + 'codeception_synopsis' => '%1$d 测试进行了 %2$f 秒. + %3$d 失败。', + + 'file' => 'File', + 'line' => 'Line', + 'class' => 'Class', + 'method' => 'Method', + 'message' => 'Message', + 'start' => 'Start', + 'end' => 'End', + 'from' => 'From', + 'to' => 'To', + 'result' => 'Result', + 'ok' => 'OK', + 'took_n_seconds' => 'Took %d seconds', + 'build_started' => 'Build Started', + 'build_finished' => 'Build Finished', + 'test_message' => 'Message', + 'test_no_message' => 'No message', + 'test_success' => 'Successful: %d', + 'test_fail' => 'Failures: %d', + 'test_skipped' => 'Skipped: %d', + 'test_error' => 'Errors: %d', + 'test_todo' => 'Todos: %d', + 'test_total' => '%d test(s)', + + // Users + 'name' => '名称', + 'password_change' => '密码 (如果要修改密码请填入新密码,否则留空空)', + 'save' => '保存 »', + 'update_your_details' => '更新您的个人信息', + 'your_details_updated' => '您的信息已经更新。', + 'add_user' => '新增用户', + 'is_admin' => '是否设为管理员?', + 'yes' => '是', + 'no' => '否', + 'edit' => '编辑', + 'edit_user' => '编辑用户', + 'delete_user' => '删除用户', + 'user_n_not_found' => '用户 ID %d 不存在。', + 'is_user_admin' => '该用户是否为管理员', + 'save_user' => '保存用户', + + // Settings: + 'settings_saved' => '您的设置已经保存。', + 'settings_check_perms' => '权限不足,您的设置无法保存, 请检查 config.yml 文件.', + 'settings_cannot_write' => 'PHP Censor 无法写入 config.yml 文件, 在这个问题解决前设置可能无法正常保存', + 'settings_github_linked' => '您的 GitHub 账户已经连接。', + 'settings_github_not_linked' => '您的 GitHub 无法连接。', + 'build_settings' => '构建设置', + 'github_application' => 'GitHub Application', + 'github_sign_in' => '在使用您的 GitHub 账号之前, 您需要登录 GitHub , 并允许 PHP Censor 访问您的账户。', + 'github_linked' => 'PHP Censor 成功连接到您的 GitHub 账户。', + 'github_where_to_find' => '在哪里可以找到...', + 'github_where_help' => '如果您想使用您自己的应用, 您可以在applications 的 setting 中 找到相关信息。', + + 'email_settings' => '邮箱设置', + 'email_settings_help' => 'PHP Censor在发送构建状态的邮件之前,您需要配置您的SMTP设置如下。', + + 'application_id' => 'Application ID', + 'application_secret' => 'Application Secret', + + 'smtp_server' => 'SMTP Server', + 'smtp_port' => 'SMTP Port', + 'smtp_username' => 'SMTP Username', + 'smtp_password' => 'SMTP Password', + 'from_email_address' => '邮件来自', + 'default_notification_address' => '通知默认邮件', + 'use_smtp_encryption' => 'SMTP 使用哪种方式加密?', + 'none' => 'None', + 'ssl' => 'SSL', + 'tls' => 'TLS', + + 'failed_after' => '构建失败后重新构建间隔', + '5_mins' => '5 分钟', + '15_mins' => '15 分钟', + '30_mins' => '30 分钟', + '1_hour' => '1 小时', + '3_hours' => '3 小时', + + // Plugins + 'cannot_update_composer' => '由于 composer.json 文件不可写 PHP Censor 无法为您更新该文件, ', + 'x_has_been_removed' => '%s 已经移除', + 'x_has_been_added' => '%s 已经为您添加至 composer.json , 当您下次执行 composer update 时相关库将会安装', + 'enabled_plugins' => '已启用插件', + 'provided_by_package' => '来自', + 'installed_packages' => '已安装插件', + 'suggested_packages' => '建议安装插件', + 'title' => '名称', + 'description' => '说明', + 'version' => '版本', + 'install' => '安装 »', + 'remove' => '移除 »', + 'search_packagist_for_more' => '搜索获取更多插件', + 'search' => '搜索 »', + + // Summary plugin + 'build-summary' => 'Summary', + 'stage' => 'Stage', + 'duration' => 'Duration', + 'plugin' => 'Plugin', + 'stage_setup' => 'Setup', + 'stage_test' => 'Test', + 'stage_complete' => 'Complete', + 'stage_success' => 'Success', + 'stage_failure' => 'Failure', + 'stage_broken' => 'Broken', + 'stage_fixed' => 'Fixed', + + // Update + 'update_app' => 'Update the database to reflect modified models.', + 'updating_app' => 'Updating PHP Censor database: ', + 'not_installed' => 'PHP Censor does not appear to be installed.', + 'install_instead' => 'Please install PHP Censor via php-censor:install instead.', + + // Build Plugins: + 'passing_build' => 'Passing Build', + 'failing_build' => 'Failing Build', + 'log_output' => 'Log Output: ', + + // Error Levels: + 'critical' => 'Critical', + 'high' => 'High', + 'normal' => 'Normal', + 'low' => 'Low', + + // Plugins that generate errors: + 'php_mess_detector' => 'PHP Mess Detector', + 'php_code_sniffer' => 'PHP Code Sniffer', + 'php_unit' => 'PHP Unit', + 'php_cpd' => 'PHP Copy/Paste Detector', + 'php_docblock_checker' => 'PHP Docblock Checker', +]; diff --git a/src/PHPCensor/Logging/BuildDBLogHandler.php b/src/PHPCensor/Logging/BuildDBLogHandler.php new file mode 100644 index 0000000..5cd3489 --- /dev/null +++ b/src/PHPCensor/Logging/BuildDBLogHandler.php @@ -0,0 +1,81 @@ +build = $build; + // We want to add to any existing saved log information. + $this->logValue = $build->getLog(); + } + + /** + * Destructor + */ + public function __destruct() + { + $this->flushData(); + } + + /** + * Flush buffered data + */ + protected function flushData() + { + $this->build->setLog($this->logValue); + Factory::getStore('Build')->save($this->build); + $this->flush_timestamp = time(); + } + + /** + * Write a log entry to the build log. + * @param array $record + */ + protected function write(array $record) + { + $message = (string)$record['message']; + $message = str_replace($this->build->currentBuildPath, './', $message); + + $this->logValue .= $message . PHP_EOL; + + if ($this->flush_timestamp < (time() - $this->flush_delay)) { + $this->flushData(); + } + } +} diff --git a/src/PHPCensor/Logging/BuildLogger.php b/src/PHPCensor/Logging/BuildLogger.php new file mode 100644 index 0000000..20c6361 --- /dev/null +++ b/src/PHPCensor/Logging/BuildLogger.php @@ -0,0 +1,114 @@ +logger = $logger; + $this->build = $build; + } + + /** + * 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 = []) + { + // Skip if no logger has been loaded. + if (!$this->logger) { + return; + } + + if (!is_array($message)) { + $message = [$message]; + } + + // The build is added to the context so the logger can use + // details from it if required. + $context['build'] = $this->build; + + foreach ($message as $item) { + $this->logger->log($level, $item, $context); + } + } + + /** + * 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 $message + * @param \Exception $exception The exception that caused the error. + */ + public function logFailure($message, \Exception $exception = null) + { + $context = []; + + // The psr3 log interface stipulates that exceptions should be passed + // as the exception key in the context array. + if ($exception) { + $context['exception'] = $exception; + $context['trace'] = $exception->getTrace(); + } + + $this->log("\033[0;31m" . $message . "\033[0m", LogLevel::ERROR, $context); + } + + /** + * Add a debug message to the log. + * @param string + */ + public function logDebug($message) + { + if ( + (defined('DEBUG_MODE') && DEBUG_MODE) || + ((boolean)$this->build->getExtra('debug')) + ) { + $this->log("\033[0;36m" . $message . "\033[0m"); + } + } + + /** + * Sets a logger instance on the object + * + * @param LoggerInterface $logger + * @return null + */ + public function setLogger(LoggerInterface $logger) + { + $this->logger = $logger; + } +} diff --git a/src/PHPCensor/Logging/Handler.php b/src/PHPCensor/Logging/Handler.php new file mode 100644 index 0000000..532b1aa --- /dev/null +++ b/src/PHPCensor/Logging/Handler.php @@ -0,0 +1,145 @@ + 'Warning', + E_NOTICE => 'Notice', + E_USER_ERROR => 'User Error', + E_USER_WARNING => 'User Warning', + E_USER_NOTICE => 'User Notice', + E_STRICT => 'Runtime Notice', + E_RECOVERABLE_ERROR => 'Catchable Fatal Error', + E_DEPRECATED => 'Deprecated', + E_USER_DEPRECATED => 'User Deprecated', + ]; + + /** + * @var LoggerInterface + */ + protected $logger; + + /** + * @param LoggerInterface $logger + */ + public function __construct(LoggerInterface $logger = null) + { + $this->logger = $logger; + } + + /** + * Register a new log handler. + * @param LoggerInterface $logger + */ + public static function register(LoggerInterface $logger = null) + { + $handler = new static($logger); + + set_error_handler([$handler, 'handleError']); + register_shutdown_function([$handler, 'handleFatalError']); + + set_exception_handler([$handler, 'handleException']); + } + + /** + * @param integer $level + * @param string $message + * @param string $file + * @param integer $line + * + * @throws \ErrorException + */ + public function handleError($level, $message, $file, $line) + { + if (error_reporting() & $level) { + $exception_level = isset($this->levels[$level]) ? $this->levels[$level] : $level; + + throw new \ErrorException( + sprintf('%s: %s in %s line %d', $exception_level, $message, $file, $line), + 0, + $level, + $file, + $line + ); + } + } + + /** + * @throws \ErrorException + */ + public function handleFatalError() + { + $fatal_error = error_get_last(); + + try { + if (($error = error_get_last()) !== null) { + $error = new \ErrorException( + sprintf( + '%s: %s in %s line %d', + $fatal_error['type'], + $fatal_error['message'], + $fatal_error['file'], + $fatal_error['line'] + ), + 0, + $fatal_error['type'], + $fatal_error['file'], + $fatal_error['line'] + ); + $this->log($error); + } + } catch (\Exception $e) { + $error = new \ErrorException( + sprintf( + '%s: %s in %s line %d', + $fatal_error['type'], + $fatal_error['message'], + $fatal_error['file'], + $fatal_error['line'] + ), + 0, + $fatal_error['type'], + $fatal_error['file'], + $fatal_error['line'] + ); + $this->log($error); + } + } + + /** + * @param \Exception $exception + */ + public function handleException(\Exception $exception) + { + $this->log($exception); + } + + /** + * Write to the build log. + * @param \Exception $exception + */ + protected function log(\Exception $exception) + { + if (null !== $this->logger) { + $message = sprintf( + '%s: %s (uncaught exception) at %s line %s', + get_class($exception), + $exception->getMessage(), + $exception->getFile(), + $exception->getLine() + ); + + $this->logger->error($message, ['exception' => $exception]); + } + } +} diff --git a/src/PHPCensor/Logging/LoggedBuildContextTidier.php b/src/PHPCensor/Logging/LoggedBuildContextTidier.php new file mode 100644 index 0000000..4afa477 --- /dev/null +++ b/src/PHPCensor/Logging/LoggedBuildContextTidier.php @@ -0,0 +1,38 @@ +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; + } +} diff --git a/src/PHPCensor/Logging/OutputLogHandler.php b/src/PHPCensor/Logging/OutputLogHandler.php new file mode 100644 index 0000000..dcbab04 --- /dev/null +++ b/src/PHPCensor/Logging/OutputLogHandler.php @@ -0,0 +1,41 @@ +output = $output; + } + + /** + * Write a log entry to the terminal. + * @param array $record + */ + protected function write(array $record) + { + $this->output->writeln((string)$record['formatted']); + } +} diff --git a/src/PHPCensor/Migrations/20140513143726_initial_migration.php b/src/PHPCensor/Migrations/20140513143726_initial_migration.php new file mode 100644 index 0000000..9c329a6 --- /dev/null +++ b/src/PHPCensor/Migrations/20140513143726_initial_migration.php @@ -0,0 +1,210 @@ +createBuildTable(); + $this->createBuildMetaTable(); + $this->createProjectTable(); + $this->createUserTable(); + + // Set up foreign keys: + $build = $this->table('build'); + + if (!$build->hasForeignKey('project_id')) { + $build->addForeignKey('project_id', 'project', 'id', ['delete'=> 'CASCADE', 'update' => 'CASCADE'])->save(); + } + + $buildMeta = $this->table('build_meta'); + + if (!$buildMeta->hasForeignKey('build_id')) { + $buildMeta->addForeignKey('build_id', 'build', 'id', ['delete'=> 'CASCADE', 'update' => 'CASCADE'])->save(); + } + + if (!$buildMeta->hasForeignKey('project_id')) { + $buildMeta->addForeignKey('project_id', 'project', 'id', ['delete'=> 'CASCADE', 'update' => 'CASCADE'])->save(); + } + } + + protected function createBuildTable() + { + $table = $this->table('build'); + + if (!$this->hasTable('build')) { + $table->create(); + } + + if (!$table->hasColumn('project_id')) { + $table->addColumn('project_id', 'integer')->save(); + } + + if (!$table->hasColumn('commit_id')) { + $table->addColumn('commit_id', 'string', ['limit' => 50])->save(); + } + + if (!$table->hasColumn('status')) { + $table->addColumn('status', 'integer', ['limit' => 4])->save(); + } + + if (!$table->hasColumn('log')) { + $table->addColumn('log', 'text')->save(); + } + + if (!$table->hasColumn('branch')) { + $table->addColumn('branch', 'string', ['limit' => 50])->save(); + } + + if (!$table->hasColumn('created')) { + $table->addColumn('created', 'datetime')->save(); + } + + if (!$table->hasColumn('started')) { + $table->addColumn('started', 'datetime')->save(); + } + + if (!$table->hasColumn('finished')) { + $table->addColumn('finished', 'datetime')->save(); + } + + if (!$table->hasColumn('committer_email')) { + $table->addColumn('committer_email', 'string', ['limit' => 250])->save(); + } + + if (!$table->hasColumn('commit_message')) { + $table->addColumn('commit_message', 'text')->save(); + } + + if (!$table->hasColumn('extra')) { + $table->addColumn('extra', 'text')->save(); + } + + if ($table->hasColumn('plugins')) { + $table->removeColumn('plugins')->save(); + } + + if (!$table->hasIndex(['project_id'])) { + $table->addIndex(['project_id'])->save(); + } + + if (!$table->hasIndex(['status'])) { + $table->addIndex(['status'])->save(); + } + } + + protected function createBuildMetaTable() + { + $table = $this->table('build_meta'); + + if (!$this->hasTable('build_meta')) { + $table->create(); + } + + if (!$table->hasColumn('project_id')) { + $table->addColumn('project_id', 'integer')->save(); + } + + if (!$table->hasColumn('build_id')) { + $table->addColumn('build_id', 'integer')->save(); + } + + if (!$table->hasColumn('meta_key')) { + $table->addColumn('meta_key', 'string', ['limit' => 250])->save(); + } + + if (!$table->hasColumn('meta_value')) { + $table->addColumn('meta_value', 'text')->save(); + } + + if (!$table->hasIndex(['build_id', 'meta_key'])) { + $table->addIndex(['build_id', 'meta_key'])->save(); + } + } + + protected function createProjectTable() + { + $table = $this->table('project'); + + if (!$this->hasTable('project')) { + $table->create(); + } + + if (!$table->hasColumn('title')) { + $table->addColumn('title', 'string', ['limit' => 250])->save(); + } + + if (!$table->hasColumn('reference')) { + $table->addColumn('reference', 'string', ['limit' => 250])->save(); + } + + if (!$table->hasColumn('git_key')) { + $table->addColumn('git_key', 'text')->save(); + } + + if (!$table->hasColumn('public_key')) { + $table->addColumn('public_key', 'text')->save(); + } + + if (!$table->hasColumn('type')) { + $table->addColumn('type', 'string', ['limit' => 50])->save(); + } + + if (!$table->hasColumn('access_information')) { + $table->addColumn('access_information', 'string', ['limit' => 250])->save(); + } + + if (!$table->hasColumn('last_commit')) { + $table->addColumn('last_commit', 'string', ['limit' => 250])->save(); + } + + if (!$table->hasColumn('build_config')) { + $table->addColumn('build_config', 'text')->save(); + } + + if (!$table->hasColumn('allow_public_status')) { + $table->addColumn('allow_public_status', 'integer')->save(); + } + + if ($table->hasColumn('token')) { + $table->removeColumn('token')->save(); + } + + if (!$table->hasIndex(['title'])) { + $table->addIndex(['title'])->save(); + } + } + + protected function createUserTable() + { + $table = $this->table('user'); + + if (!$this->hasTable('user')) { + $table->create(); + } + + if (!$table->hasColumn('email')) { + $table->addColumn('email', 'string', ['limit' => 250])->save(); + } + + if (!$table->hasColumn('hash')) { + $table->addColumn('hash', 'string', ['limit' => 250])->save(); + } + + if (!$table->hasColumn('name')) { + $table->addColumn('name', 'string', ['limit' => 250])->save(); + } + + if (!$table->hasColumn('is_admin')) { + $table->addColumn('is_admin', 'integer')->save(); + } + + if (!$table->hasIndex(['email'])) { + $table->addIndex(['email'])->save(); + } + } +} diff --git a/src/PHPCensor/Migrations/20140513153133_change_build_keys_migration.php b/src/PHPCensor/Migrations/20140513153133_change_build_keys_migration.php new file mode 100644 index 0000000..fd67bf4 --- /dev/null +++ b/src/PHPCensor/Migrations/20140513153133_change_build_keys_migration.php @@ -0,0 +1,32 @@ +table('project'); + + if (!$project->hasColumn('ssh_private_key') && $project->hasColumn('git_key')) { + $project->renameColumn('git_key', 'ssh_private_key'); + } + + if (!$project->hasColumn('ssh_public_key') && $project->hasColumn('public_key')) { + $project->renameColumn('public_key', 'ssh_public_key'); + } + } + + public function down() + { + $project = $this->table('project'); + + if (!$project->hasColumn('git_key') && $project->hasColumn('ssh_private_key')) { + $project->renameColumn('ssh_private_key', 'git_key'); + } + + if (!$project->hasColumn('public_key') && $project->hasColumn('ssh_public_key')) { + $project->renameColumn('ssh_public_key', 'public_key'); + } + } +} diff --git a/src/PHPCensor/Migrations/20140611170618_choose_branch.php b/src/PHPCensor/Migrations/20140611170618_choose_branch.php new file mode 100644 index 0000000..5d69ff0 --- /dev/null +++ b/src/PHPCensor/Migrations/20140611170618_choose_branch.php @@ -0,0 +1,24 @@ +table('project'); + + if (!$project->hasColumn('branch')) { + $project->addColumn('branch', 'string', ['after' => 'reference', 'limit' => 250])->save(); + } + } + + public function down() + { + $project = $this->table('project'); + + if ($project->hasColumn('branch')) { + $project->removeColumn('branch')->save(); + } + } +} diff --git a/src/PHPCensor/Migrations/20140730143702_fix_database_columns.php b/src/PHPCensor/Migrations/20140730143702_fix_database_columns.php new file mode 100644 index 0000000..11b3540 --- /dev/null +++ b/src/PHPCensor/Migrations/20140730143702_fix_database_columns.php @@ -0,0 +1,55 @@ +table('build'); + + $build->changeColumn('project_id', 'integer', ['null' => false]); + $build->changeColumn('commit_id', 'string', ['limit' => 50, 'null' => false]); + $build->changeColumn('status', 'integer', ['null' => false]); + $build->changeColumn('log', 'text', ['null' => true]); + $build->changeColumn('branch', 'string', ['limit' => 50, 'null' => false, 'default' => 'master']); + $build->changeColumn('created', 'datetime', ['null' => true]); + $build->changeColumn('started', 'datetime', ['null' => true]); + $build->changeColumn('finished', 'datetime', ['null' => true]); + $build->changeColumn('committer_email', 'string', ['limit' => 512, 'null' => true]); + $build->changeColumn('commit_message', 'text', ['null' => true]); + $build->changeColumn('extra', 'text', ['null' => true]); + + $buildMeta = $this->table('build_meta'); + + $buildMeta->changeColumn('project_id', 'integer', ['null' => false]); + $buildMeta->changeColumn('build_id', 'integer', ['null' => false]); + $buildMeta->changeColumn('meta_key', 'string', ['limit' => 250, 'null' => false]); + $buildMeta->changeColumn('meta_value', 'text', ['null' => false]); + + $project = $this->table('project'); + + $project->changeColumn('title', 'string', ['limit' => 250, 'null' => false]); + $project->changeColumn('reference', 'string', ['limit' => 250, 'null' => false]); + $project->changeColumn('branch', 'string', ['limit' => 50, 'null' => false, 'default' => 'master']); + $project->changeColumn('ssh_private_key', 'text', ['null' => true, 'default' => null]); + $project->changeColumn('ssh_public_key', 'text', ['null' => true, 'default' => null]); + $project->changeColumn('type', 'string', ['limit' => 50, 'null' => false]); + $project->changeColumn('access_information', 'string', ['limit' => 250, 'null' => true, 'default' => null]); + $project->changeColumn('last_commit', 'string', ['limit' => 250, 'null' => true, 'default' => null]); + $project->changeColumn('ssh_public_key', 'text', ['null' => true, 'default' => null]); + $project->changeColumn('allow_public_status', 'integer', ['null' => false, 'default' => 0]); + + $user = $this->table('user'); + + $user->changeColumn('email', 'string', ['limit' => 250, 'null' => false]); + $user->changeColumn('hash', 'string', ['limit' => 250, 'null' => false]); + $user->changeColumn('is_admin', 'integer', ['null' => false, 'default' => 0]); + $user->changeColumn('name', 'string', ['limit' => 250, 'null' => false]); + } + + public function down() + { + + } +} diff --git a/src/PHPCensor/Migrations/20150131075425_archive_project.php b/src/PHPCensor/Migrations/20150131075425_archive_project.php new file mode 100644 index 0000000..4d8bbfe --- /dev/null +++ b/src/PHPCensor/Migrations/20150131075425_archive_project.php @@ -0,0 +1,24 @@ +table('project'); + + if (!$project->hasColumn('archived')) { + $project->addColumn('archived', 'boolean', ['default' => false])->save(); + } + } + + public function down() + { + $project = $this->table('project'); + + if ($project->hasColumn('archived')) { + $project->removeColumn('archived')->save(); + } + } +} diff --git a/src/PHPCensor/Migrations/20150203105015_fix_column_types.php b/src/PHPCensor/Migrations/20150203105015_fix_column_types.php new file mode 100644 index 0000000..f13442b --- /dev/null +++ b/src/PHPCensor/Migrations/20150203105015_fix_column_types.php @@ -0,0 +1,28 @@ +table('build'); + + $build->changeColumn('log', 'text', ['null' => true]); + + $buildMeta = $this->table('build_meta'); + + $buildMeta->changeColumn('meta_value', 'text', ['null' => false]); + } + + public function down() + { + $build = $this->table('build'); + + $build->changeColumn('log', 'text', ['null' => true]); + + $buildMeta = $this->table('build_meta'); + + $buildMeta->changeColumn('meta_value', 'text', ['null' => false]); + } +} diff --git a/src/PHPCensor/Migrations/20150308074509_add_user_providers.php b/src/PHPCensor/Migrations/20150308074509_add_user_providers.php new file mode 100644 index 0000000..bae5ef8 --- /dev/null +++ b/src/PHPCensor/Migrations/20150308074509_add_user_providers.php @@ -0,0 +1,34 @@ +table('user') + // The provider name + ->addColumn('provider_key', 'string', [ + 'default' => 'internal', + 'limit' => 255 + ]) + // A data used by the provider + ->addColumn('provider_data', 'string', [ + 'null' => true, + 'limit' => 255 + ]) + ->save(); + } + + public function down() + { + // Remove the provider columns + $this + ->table('user') + ->removeColumn('provider_key') + ->removeColumn('provider_data') + ->save(); + } +} diff --git a/src/PHPCensor/Migrations/20150324174958_unique_email_and_name_user_fields.php b/src/PHPCensor/Migrations/20150324174958_unique_email_and_name_user_fields.php new file mode 100644 index 0000000..5a19ff1 --- /dev/null +++ b/src/PHPCensor/Migrations/20150324174958_unique_email_and_name_user_fields.php @@ -0,0 +1,32 @@ +table('user'); + + if (!$user_table->hasIndex('email', ['unique' => true])) { + $user_table->addIndex('email', ['unique' => true])->save(); + } + + if (!$user_table->hasIndex('name', ['unique' => true])) { + $user_table->addIndex('name', ['unique' => true])->save(); + } + } + + public function down() + { + $user_table = $this->table('user'); + + if ($user_table->hasIndex('email', ['unique' => true])) { + $user_table->removeIndex(['email'], ['unique' => true])->save(); + } + + if ($user_table->hasIndex('name', ['unique' => true])) { + $user_table->removeIndex(['name'], ['unique' => true])->save(); + } + } +} diff --git a/src/PHPCensor/Migrations/20151008140800_add_project_groups.php b/src/PHPCensor/Migrations/20151008140800_add_project_groups.php new file mode 100644 index 0000000..315b6f1 --- /dev/null +++ b/src/PHPCensor/Migrations/20151008140800_add_project_groups.php @@ -0,0 +1,49 @@ +table('project_group'); + + if (!$this->hasTable('project_group')) { + $table->create(); + } + + if (!$table->hasColumn('title')) { + $table + ->addColumn('title', 'string', ['limit' => 100, 'null' => false]) + ->save(); + } + + $table = $this->table('project'); + + if (!$table->hasColumn('group_id')) { + $table->addColumn('group_id', 'integer', ['signed' => true, 'null' => false, 'default' => 1,])->save(); + } + + if (!$table->hasForeignKey('group_id')) { + $table->addForeignKey('group_id', 'project_group', 'id', ['delete'=> 'RESTRICT', 'update' => 'CASCADE'])->save(); + } + } + + public function down() + { + $table = $this->table('project'); + + if ($table->hasForeignKey('group_id')) { + $table->dropForeignKey('group_id'); + } + + if ($table->hasColumn('group_id')) { + $table->removeColumn('group_id'); + } + + $table = $this->table('project_group'); + if ($this->hasTable('project_group')) { + $table->drop(); + } + } +} diff --git a/src/PHPCensor/Migrations/20151009100610_remove_unique_name_index.php b/src/PHPCensor/Migrations/20151009100610_remove_unique_name_index.php new file mode 100644 index 0000000..72c56a8 --- /dev/null +++ b/src/PHPCensor/Migrations/20151009100610_remove_unique_name_index.php @@ -0,0 +1,19 @@ +table('user'); + + if ($user->hasIndex('name', ['unique' => true])) { + $user->removeIndex(['name'], ['unique' => true])->save(); + } + + if (!$user->hasIndex('name', ['unique' => true])) { + $user->addIndex(['name'], ['unique' => false])->save(); + } + } +} diff --git a/src/PHPCensor/Migrations/20151014091859_errors_table.php b/src/PHPCensor/Migrations/20151014091859_errors_table.php new file mode 100644 index 0000000..6a8922d --- /dev/null +++ b/src/PHPCensor/Migrations/20151014091859_errors_table.php @@ -0,0 +1,68 @@ +table('build_error'); + + if (!$this->hasTable('build_error')) { + $table->create(); + } + + if (!$table->hasColumn('build_id')) { + $table->addColumn('build_id', 'integer', ['signed' => true])->save(); + } + + if (!$table->hasColumn('plugin')) { + $table->addColumn('plugin', 'string', ['limit' => 100])->save(); + } + + if (!$table->hasColumn('file')) { + $table->addColumn('file', 'string', ['limit' => 250, 'null' => true])->save(); + } + + if (!$table->hasColumn('line_start')) { + $table->addColumn('line_start', 'integer', ['signed' => false, 'null' => true])->save(); + } + + if (!$table->hasColumn('line_end')) { + $table->addColumn('line_end', 'integer', ['signed' => false, 'null' => true])->save(); + } + + if (!$table->hasColumn('severity')) { + $table->addColumn('severity', 'integer', ['signed' => false, 'limit' => 255])->save(); + } + + if (!$table->hasColumn('message')) { + $table->addColumn('message', 'string', ['limit' => 250])->save(); + } + + if (!$table->hasColumn('created_date')) { + $table->addColumn('created_date', 'datetime')->save(); + } + + if (!$table->hasIndex(['build_id', 'created_date'], ['unique' => false])) { + $table->addIndex(['build_id', 'created_date'], ['unique' => false])->save(); + } + + if (!$table->hasForeignKey('build_id')) { + $table->addForeignKey('build_id', 'build', 'id', ['delete'=> 'CASCADE', 'update' => 'CASCADE'])->save(); + } + } + + public function down() + { + $table = $this->table('build_error'); + + if ($table->hasForeignKey('build_id')) { + $table->dropForeignKey('build_id')->save(); + } + + if ($this->hasTable('build_error')) { + $table->drop(); + } + } +} diff --git a/src/PHPCensor/Migrations/20151015124825_convert_errors.php b/src/PHPCensor/Migrations/20151015124825_convert_errors.php new file mode 100644 index 0000000..7cb0768 --- /dev/null +++ b/src/PHPCensor/Migrations/20151015124825_convert_errors.php @@ -0,0 +1,182 @@ +metaStore = Factory::getStore('BuildMeta'); + $this->errorStore = Factory::getStore('BuildError'); + + while ($count === 100) { + $data = $this->metaStore->getErrorsForUpgrade(100); + $count = count($data); + + /** @var \PHPCensor\Model\BuildMeta $meta */ + foreach ($data as $meta) { + switch ($meta->getMetaKey()) { + case 'phpmd-data': + $this->processPhpMdMeta($meta); + break; + + case 'phpcs-data': + $this->processPhpCsMeta($meta); + break; + + case 'phpdoccheck-data': + $this->processPhpDocCheckMeta($meta); + break; + + case 'phpcpd-data': + $this->processPhpCpdMeta($meta); + break; + + case 'technical_debt-data': + $this->processTechnicalDebtMeta($meta); + break; + } + + $this->metaStore->delete($meta); + } + } + } + + protected function processPhpMdMeta(BuildMeta $meta) + { + $data = json_decode($meta->getMetaValue(), true); + + if (is_array($data) && count($data)) { + foreach ($data as $error) { + $buildError = new BuildError(); + $buildError->setBuildId($meta->getBuildId()); + $buildError->setPlugin('php_mess_detector'); + $buildError->setCreateDate(new \DateTime()); + $buildError->setFile($error['file']); + $buildError->setLineStart($error['line_start']); + $buildError->setLineEnd($error['line_end']); + $buildError->setSeverity(BuildError::SEVERITY_HIGH); + $buildError->setMessage($error['message']); + + $this->errorStore->save($buildError); + } + } + } + + protected function processPhpCsMeta(BuildMeta $meta) + { + $data = json_decode($meta->getMetaValue(), true); + + if (is_array($data) && count($data)) { + foreach ($data as $error) { + $buildError = new BuildError(); + $buildError->setBuildId($meta->getBuildId()); + $buildError->setPlugin('php_code_sniffer'); + $buildError->setCreateDate(new \DateTime()); + $buildError->setFile($error['file']); + $buildError->setLineStart($error['line']); + $buildError->setLineEnd($error['line']); + $buildError->setMessage($error['message']); + + switch ($error['type']) { + case 'ERROR': + $buildError->setSeverity(BuildError::SEVERITY_HIGH); + break; + + case 'WARNING': + $buildError->setSeverity(BuildError::SEVERITY_LOW); + break; + } + + $this->errorStore->save($buildError); + } + } + } + + protected function processPhpDocCheckMeta(BuildMeta $meta) + { + $data = json_decode($meta->getMetaValue(), true); + + if (is_array($data) && count($data)) { + foreach ($data as $error) { + $buildError = new BuildError(); + $buildError->setBuildId($meta->getBuildId()); + $buildError->setPlugin('php_docblock_checker'); + $buildError->setCreateDate(new \DateTime()); + $buildError->setFile($error['file']); + $buildError->setLineStart($error['line']); + $buildError->setLineEnd($error['line']); + + switch ($error['type']) { + case 'method': + $buildError->setMessage($error['class'] . '::' . $error['method'] . ' is missing a docblock.'); + $buildError->setSeverity(BuildError::SEVERITY_NORMAL); + break; + + case 'class': + $buildError->setMessage('Class ' . $error['class'] . ' is missing a docblock.'); + $buildError->setSeverity(BuildError::SEVERITY_LOW); + break; + } + + $this->errorStore->save($buildError); + } + } + } + + protected function processPhpCpdMeta(BuildMeta $meta) + { + $data = json_decode($meta->getMetaValue(), true); + + if (is_array($data) && count($data)) { + foreach ($data as $error) { + $buildError = new BuildError(); + $buildError->setBuildId($meta->getBuildId()); + $buildError->setPlugin('php_cpd'); + $buildError->setCreateDate(new \DateTime()); + $buildError->setFile($error['file']); + $buildError->setLineStart($error['line_start']); + $buildError->setLineEnd($error['line_end']); + $buildError->setSeverity(BuildError::SEVERITY_NORMAL); + $buildError->setMessage('Copy and paste detected.'); + + $this->errorStore->save($buildError); + } + } + } + + protected function processTechnicalDebtMeta(BuildMeta $meta) + { + $data = json_decode($meta->getMetaValue(), true); + + if (is_array($data) && count($data)) { + foreach ($data as $error) { + $buildError = new BuildError(); + $buildError->setBuildId($meta->getBuildId()); + $buildError->setPlugin('technical_debt'); + $buildError->setCreateDate(new \DateTime()); + $buildError->setFile($error['file']); + $buildError->setLineStart($error['line']); + $buildError->setSeverity(BuildError::SEVERITY_NORMAL); + $buildError->setMessage($error['message']); + + $this->errorStore->save($buildError); + } + } + } +} diff --git a/src/PHPCensor/Migrations/20160425162114_branch_column_length.php b/src/PHPCensor/Migrations/20160425162114_branch_column_length.php new file mode 100644 index 0000000..2dfa229 --- /dev/null +++ b/src/PHPCensor/Migrations/20160425162114_branch_column_length.php @@ -0,0 +1,24 @@ +table('build'); + $table->changeColumn('branch', 'string', ['limit' => 250, 'null' => false, 'default' => 'master']); + + $table = $this->table('project'); + $table->changeColumn('branch', 'string', ['limit' => 250, 'null' => false, 'default' => 'master']); + } + + public function down() + { + $table = $this->table('build'); + $table->changeColumn('branch', 'string', ['limit' => 50, 'null' => false, 'default' => 'master']); + + $table = $this->table('project'); + $table->changeColumn('branch', 'string', ['limit' => 50, 'null' => false, 'default' => 'master']); + } +} diff --git a/src/PHPCensor/Migrations/20160623100223_project_table_defaults.php b/src/PHPCensor/Migrations/20160623100223_project_table_defaults.php new file mode 100644 index 0000000..a25b993 --- /dev/null +++ b/src/PHPCensor/Migrations/20160623100223_project_table_defaults.php @@ -0,0 +1,13 @@ +table('project') + ->changeColumn('build_config', 'text', ['null' => true]) + ->save(); + } +} diff --git a/src/PHPCensor/Migrations/20170103163312_added_language_and_per_page_for_user.php b/src/PHPCensor/Migrations/20170103163312_added_language_and_per_page_for_user.php new file mode 100644 index 0000000..befc77f --- /dev/null +++ b/src/PHPCensor/Migrations/20170103163312_added_language_and_per_page_for_user.php @@ -0,0 +1,40 @@ +table('user'); + + if (!$table->hasColumn('language')) { + $table + ->addColumn('language', 'string', ['limit' => 5, 'null' => true]) + ->save(); + } + + if (!$table->hasColumn('per_page')) { + $table + ->addColumn('per_page', 'integer', ['null' => true]) + ->save(); + } + } + + public function down() + { + $table = $this->table('user'); + + if ($table->hasColumn('language')) { + $table + ->removeColumn('language') + ->save(); + } + + if ($table->hasColumn('per_page')) { + $table + ->removeColumn('per_page') + ->save(); + } + } +} diff --git a/src/PHPCensor/Migrations/20170218175400_fixed_build_error_message_column.php b/src/PHPCensor/Migrations/20170218175400_fixed_build_error_message_column.php new file mode 100644 index 0000000..c142fd5 --- /dev/null +++ b/src/PHPCensor/Migrations/20170218175400_fixed_build_error_message_column.php @@ -0,0 +1,18 @@ +table('build_error') + ->changeColumn('message', 'text') + ->save(); + } + + public function down() + { + } +} diff --git a/src/PHPCensor/Migrations/20170223113127_fixed_build_log_column_for_mysql.php b/src/PHPCensor/Migrations/20170223113127_fixed_build_log_column_for_mysql.php new file mode 100644 index 0000000..48b956a --- /dev/null +++ b/src/PHPCensor/Migrations/20170223113127_fixed_build_log_column_for_mysql.php @@ -0,0 +1,26 @@ +getAdapter(); + if ($adapter instanceof MysqlAdapter) { + $this + ->table('build') + ->changeColumn( + 'log', + MysqlAdapter::PHINX_TYPE_TEXT, + ['limit' => MysqlAdapter::TEXT_LONG] + ) + ->save(); + } + } + + public function down() + { + } +} diff --git a/src/PHPCensor/Migrations/20170226132922_fixed_build_log_column_for_mysql2.php b/src/PHPCensor/Migrations/20170226132922_fixed_build_log_column_for_mysql2.php new file mode 100644 index 0000000..7ff4e4f --- /dev/null +++ b/src/PHPCensor/Migrations/20170226132922_fixed_build_log_column_for_mysql2.php @@ -0,0 +1,26 @@ +getAdapter(); + if ($adapter instanceof MysqlAdapter) { + $this + ->table('build') + ->changeColumn( + 'log', + MysqlAdapter::PHINX_TYPE_TEXT, + ['limit' => MysqlAdapter::TEXT_LONG, 'null' => true] + ) + ->save(); + } + } + + public function down() + { + } +} diff --git a/src/PHPCensor/Migrations/20170321131931_add_environment.php b/src/PHPCensor/Migrations/20170321131931_add_environment.php new file mode 100644 index 0000000..5d2b860 --- /dev/null +++ b/src/PHPCensor/Migrations/20170321131931_add_environment.php @@ -0,0 +1,64 @@ +table('environment'); + + if (!$this->hasTable('environment')) { + $table->create(); + } + + if (!$table->hasColumn('project_id')) { + $table + ->addColumn('project_id', 'integer') + ->save(); + } + + if (!$table->hasColumn('name')) { + $table + ->addColumn('name', 'string', ['limit' => 250]) + ->save(); + } + + if (!$table->hasColumn('branches')) { + $table + ->addColumn('branches', 'text') + ->save(); + } + + if (!$table->hasIndex(['project_id', 'name'])) { + $table + ->addIndex(['project_id', 'name']) + ->save(); + } + + $table = $this->table('build'); + + if (!$table->hasColumn('environment')) { + $table + ->addColumn('environment', 'string', ['limit' => 250]) + ->save(); + } + } + + public function down() + { + $table = $this->table('environment'); + + if ($this->hasTable('environment')) { + $table->drop(); + } + + $table = $this->table('build'); + + if ($table->hasColumn('environment')) { + $table + ->removeColumn('environment') + ->save(); + } + } +} diff --git a/src/PHPCensor/Migrations/20170413131256_added_source_column_to_build_table.php b/src/PHPCensor/Migrations/20170413131256_added_source_column_to_build_table.php new file mode 100644 index 0000000..a63a3a1 --- /dev/null +++ b/src/PHPCensor/Migrations/20170413131256_added_source_column_to_build_table.php @@ -0,0 +1,33 @@ +table('build'); + + if (!$table->hasColumn('source')) { + $table + ->addColumn('source', 'integer', ['default' => Build::SOURCE_UNKNOWN]) + ->save(); + + $this->execute("UPDATE build SET source = 4"); + $this->execute("UPDATE build SET source = 1, commit_id = '', commit_message = '' WHERE commit_id = 'Manual'"); + $this->execute("UPDATE build SET source = 1, commit_message = '' WHERE commit_message = 'Manual'"); + } + } + + public function down() + { + $table = $this->table('build'); + + if ($table->hasColumn('source')) { + $table + ->removeColumn('source') + ->save(); + } + } +} diff --git a/src/PHPCensor/Migrations/20170416130610_fixed_environments.php b/src/PHPCensor/Migrations/20170416130610_fixed_environments.php new file mode 100644 index 0000000..8c2b96b --- /dev/null +++ b/src/PHPCensor/Migrations/20170416130610_fixed_environments.php @@ -0,0 +1,28 @@ +table('build'); + + if ($table->hasColumn('environment')) { + $table + ->changeColumn('environment', 'string', ['limit' => 250, 'null' => true]) + ->save(); + } + } + + public function down() + { + $table = $this->table('build'); + + if ($table->hasColumn('environment')) { + $table + ->changeColumn('environment', 'string', ['limit' => 250, 'null' => false]) + ->save(); + } + } +} diff --git a/src/PHPCensor/Migrations/20170420142131_added_tag_column_to_build_table.php b/src/PHPCensor/Migrations/20170420142131_added_tag_column_to_build_table.php new file mode 100644 index 0000000..39b752a --- /dev/null +++ b/src/PHPCensor/Migrations/20170420142131_added_tag_column_to_build_table.php @@ -0,0 +1,28 @@ +table('build'); + + if (!$table->hasColumn('tag')) { + $table + ->addColumn('tag', 'string', ['limit' => 250, 'null' => true]) + ->save(); + } + } + + public function down() + { + $table = $this->table('build'); + + if ($table->hasColumn('tag')) { + $table + ->removeColumn('tag') + ->save(); + } + } +} diff --git a/src/PHPCensor/Migrations/20170711112805_fixed_build_meta_for_mysql.php b/src/PHPCensor/Migrations/20170711112805_fixed_build_meta_for_mysql.php new file mode 100644 index 0000000..745f430 --- /dev/null +++ b/src/PHPCensor/Migrations/20170711112805_fixed_build_meta_for_mysql.php @@ -0,0 +1,26 @@ +getAdapter(); + if ($adapter instanceof MysqlAdapter) { + $this + ->table('build_meta') + ->changeColumn( + 'meta_value', + MysqlAdapter::PHINX_TYPE_TEXT, + ['limit' => MysqlAdapter::TEXT_LONG, 'null' => false] + ) + ->save(); + } + } + + public function down() + { + } +} diff --git a/src/PHPCensor/Migrations/20170828142020_added_remember_me_login.php b/src/PHPCensor/Migrations/20170828142020_added_remember_me_login.php new file mode 100644 index 0000000..da88adc --- /dev/null +++ b/src/PHPCensor/Migrations/20170828142020_added_remember_me_login.php @@ -0,0 +1,28 @@ +table('user'); + + if (!$table->hasColumn('remember_key')) { + $table + ->addColumn('remember_key', 'string', ['limit' => 32, 'null' => true]) + ->save(); + } + } + + public function down() + { + $table = $this->table('user'); + + if ($table->hasColumn('remember_key')) { + $table + ->removeColumn('remember_key') + ->save(); + } + } +} diff --git a/src/PHPCensor/Migrations/20170913141438_added_default_branch_only.php b/src/PHPCensor/Migrations/20170913141438_added_default_branch_only.php new file mode 100644 index 0000000..af12571 --- /dev/null +++ b/src/PHPCensor/Migrations/20170913141438_added_default_branch_only.php @@ -0,0 +1,28 @@ +table('project'); + + if (!$table->hasColumn('default_branch_only')) { + $table + ->addColumn('default_branch_only', 'integer', ['default' => 0]) + ->save(); + } + } + + public function down() + { + $table = $this->table('project'); + + if ($table->hasColumn('default_branch_only')) { + $table + ->removeColumn('default_branch_only') + ->save(); + } + } +} diff --git a/src/PHPCensor/Migrations/20171014173348_removed_project_id_from_build_meta.php b/src/PHPCensor/Migrations/20171014173348_removed_project_id_from_build_meta.php new file mode 100644 index 0000000..24f5794 --- /dev/null +++ b/src/PHPCensor/Migrations/20171014173348_removed_project_id_from_build_meta.php @@ -0,0 +1,39 @@ +table('build_meta'); + + if ($table->hasForeignKey('project_id')) { + $table->dropForeignKey('project_id'); + } + + if ($table->hasColumn('project_id')) { + $table + ->removeColumn('project_id') + ->save(); + } + } + + public function down() + { + $table = $this->table('build_meta'); + + if (!$table->hasColumn('project_id')) { + $table + ->addColumn( + 'project_id', + 'integer', [ + 'default' => 0, + 'after' => 'id' + ] + ) + ->addForeignKey('project_id', 'project', 'id', ['delete'=> 'CASCADE', 'update' => 'CASCADE']) + ->save(); + } + } +} diff --git a/src/PHPCensor/Migrations/20171015123827_added_additional_columns.php b/src/PHPCensor/Migrations/20171015123827_added_additional_columns.php new file mode 100644 index 0000000..b859289 --- /dev/null +++ b/src/PHPCensor/Migrations/20171015123827_added_additional_columns.php @@ -0,0 +1,64 @@ +table('build'); + + if (!$table->hasColumn('user_id')) { + $table + ->addColumn('user_id', 'integer', ['default' => 0]) + ->save(); + } + + if ($table->hasColumn('created')) { + $table + ->renameColumn('created', 'create_date') + ->save(); + } + + if ($table->hasColumn('started')) { + $table + ->renameColumn('started', 'start_date') + ->save(); + } + + if ($table->hasColumn('finished')) { + $table + ->renameColumn('finished', 'finish_date') + ->save(); + } + } + + public function down() + { + $table = $this->table('build'); + + if ($table->hasColumn('user_id')) { + $table + ->removeColumn('user_id') + ->save(); + } + + if ($table->hasColumn('create_date')) { + $table + ->renameColumn('create_date', 'created') + ->save(); + } + + if ($table->hasColumn('start_date')) { + $table + ->renameColumn('start_date', 'started') + ->save(); + } + + if ($table->hasColumn('finish_date')) { + $table + ->renameColumn('finish_date', 'finished') + ->save(); + } + } +} diff --git a/src/PHPCensor/Migrations/20171016143000_added_additional_columns2.php b/src/PHPCensor/Migrations/20171016143000_added_additional_columns2.php new file mode 100644 index 0000000..2516c6c --- /dev/null +++ b/src/PHPCensor/Migrations/20171016143000_added_additional_columns2.php @@ -0,0 +1,40 @@ +table('project_group'); + + if (!$table->hasColumn('create_date')) { + $table + ->addColumn('create_date', 'datetime', ['null' => true]) + ->save(); + } + + if (!$table->hasColumn('user_id')) { + $table + ->addColumn('user_id', 'integer', ['default' => 0]) + ->save(); + } + } + + public function down() + { + $table = $this->table('project_group'); + + if ($table->hasColumn('create_date')) { + $table + ->removeColumn('create_date') + ->save(); + } + + if ($table->hasColumn('user_id')) { + $table + ->removeColumn('user_id') + ->save(); + } + } +} diff --git a/src/PHPCensor/Migrations/20171019143346_added_additional_columns3.php b/src/PHPCensor/Migrations/20171019143346_added_additional_columns3.php new file mode 100644 index 0000000..cd305cd --- /dev/null +++ b/src/PHPCensor/Migrations/20171019143346_added_additional_columns3.php @@ -0,0 +1,56 @@ +table('build_error'); + + if ($table->hasColumn('created_date') && !$table->hasColumn('create_date')) { + $table + ->renameColumn('created_date', 'create_date') + ->save(); + } + + $table = $this->table('project'); + + if (!$table->hasColumn('create_date')) { + $table + ->addColumn('create_date', 'datetime', ['null' => true]) + ->save(); + } + + if (!$table->hasColumn('user_id')) { + $table + ->addColumn('user_id', 'integer', ['default' => 0]) + ->save(); + } + } + + public function down() + { + $table = $this->table('build_error'); + + if ($table->hasColumn('create_date') && !$table->hasColumn('created_date')) { + $table + ->renameColumn('create_date', 'created_date') + ->save(); + } + + $table = $this->table('project'); + + if ($table->hasColumn('create_date')) { + $table + ->removeColumn('create_date') + ->save(); + } + + if ($table->hasColumn('user_id')) { + $table + ->removeColumn('user_id') + ->save(); + } + } +} diff --git a/src/PHPCensor/Model.php b/src/PHPCensor/Model.php new file mode 100644 index 0000000..b11c4ad --- /dev/null +++ b/src/PHPCensor/Model.php @@ -0,0 +1,7 @@ + + */ +class Build extends Model +{ + const STAGE_SETUP = 'setup'; + const STAGE_TEST = 'test'; + const STAGE_DEPLOY = 'deploy'; + const STAGE_COMPLETE = 'complete'; + const STAGE_SUCCESS = 'success'; + const STAGE_FAILURE = 'failure'; + const STAGE_FIXED = 'fixed'; + const STAGE_BROKEN = 'broken'; + + const STATUS_PENDING = 0; + const STATUS_RUNNING = 1; + const STATUS_SUCCESS = 2; + const STATUS_FAILED = 3; + + const SOURCE_UNKNOWN = 0; + const SOURCE_MANUAL_WEB = 1; + const SOURCE_MANUAL_CONSOLE = 2; + const SOURCE_PERIODICAL = 3; + const SOURCE_WEBHOOK = 4; + + /** + * @var array + */ + public static $sleepable = []; + + /** + * @var string + */ + protected $tableName = 'build'; + + /** + * @var string + */ + protected $modelName = 'Build'; + + /** + * @var array + */ + protected $data = [ + 'id' => null, + 'project_id' => null, + 'commit_id' => null, + 'status' => null, + 'log' => null, + 'branch' => null, + 'tag' => null, + 'create_date' => null, + 'start_date' => null, + 'finish_date' => null, + 'committer_email' => null, + 'commit_message' => null, + 'extra' => null, + 'environment' => null, + 'source' => Build::SOURCE_UNKNOWN, + 'user_id' => 0, + ]; + + /** + * @var array + */ + protected $getters = [ + // Direct property getters: + 'id' => 'getId', + 'project_id' => 'getProjectId', + 'commit_id' => 'getCommitId', + 'status' => 'getStatus', + 'log' => 'getLog', + 'branch' => 'getBranch', + 'tag' => 'getTag', + 'create_date' => 'getCreateDate', + 'start_date' => 'getStartDate', + 'finish_date' => 'getFinishDate', + 'committer_email' => 'getCommitterEmail', + 'commit_message' => 'getCommitMessage', + 'extra' => 'getExtra', + 'environment' => 'getEnvironment', + 'source' => 'getSource', + 'user_id' => 'getUserId', + + // Foreign key getters: + 'Project' => 'getProject', + ]; + + /** + * @var array + */ + protected $setters = [ + // Direct property setters: + 'id' => 'setId', + 'project_id' => 'setProjectId', + 'commit_id' => 'setCommitId', + 'status' => 'setStatus', + 'log' => 'setLog', + 'branch' => 'setBranch', + 'setTag' => 'setTag', + 'create_date' => 'setCreateDate', + 'start_date' => 'setStartDate', + 'finish_date' => 'setFinishDate', + 'committer_email' => 'setCommitterEmail', + 'commit_message' => 'setCommitMessage', + 'extra' => 'setExtra', + 'environment' => 'setEnvironment', + 'source' => 'setSource', + 'user_id' => 'setUserId', + + // Foreign key setters: + 'Project' => 'setProject', + ]; + + /** + * @return integer + */ + public function getId() + { + $rtn = $this->data['id']; + + return (integer)$rtn; + } + + /** + * @param $value int + */ + public function setId($value) + { + $this->validateNotNull('id', $value); + $this->validateInt('id', $value); + + if ($this->data['id'] === $value) { + return; + } + + $this->data['id'] = $value; + + $this->setModified('id'); + } + + /** + * @return integer + */ + public function getProjectId() + { + $rtn = $this->data['project_id']; + + return (integer)$rtn; + } + + /** + * @param $value int + */ + public function setProjectId($value) + { + $this->validateNotNull('project_id', $value); + $this->validateInt('project_id', $value); + + if ($this->data['project_id'] === $value) { + return; + } + + $this->data['project_id'] = $value; + + $this->setModified('project_id'); + } + + /** + * @return string + */ + public function getCommitId() + { + $rtn = $this->data['commit_id']; + + return $rtn; + } + + /** + * @param $value string + */ + public function setCommitId($value) + { + $this->validateNotNull('commit_id', $value); + $this->validateString('commit_id', $value); + + if ($this->data['commit_id'] === $value) { + return; + } + + $this->data['commit_id'] = $value; + + $this->setModified('commit_id'); + } + + /** + * @return integer + */ + public function getStatus() + { + $rtn = $this->data['status']; + + return (integer)$rtn; + } + + /** + * @param $value int + */ + public function setStatus($value) + { + $this->validateNotNull('status', $value); + $this->validateInt('status', $value); + + if ($this->data['status'] === $value) { + return; + } + + $this->data['status'] = $value; + + $this->setModified('status'); + } + + /** + * @return string + */ + public function getLog() + { + $rtn = $this->data['log']; + + return $rtn; + } + + /** + * @param $value string + */ + public function setLog($value) + { + $this->validateString('log', $value); + + if ($this->data['log'] === $value) { + return; + } + + $this->data['log'] = $value; + + $this->setModified('log'); + } + + /** + * @return string + */ + public function getBranch() + { + $rtn = $this->data['branch']; + + return $rtn; + } + + /** + * @param $value string + */ + public function setBranch($value) + { + $this->validateNotNull('branch', $value); + $this->validateString('branch', $value); + + if ($this->data['branch'] === $value) { + return; + } + + $this->data['branch'] = $value; + + $this->setModified('branch'); + } + + /** + * @return \DateTime + */ + public function getCreateDate() + { + $rtn = $this->data['create_date']; + + if (!empty($rtn)) { + $rtn = new \DateTime($rtn); + } + + return $rtn; + } + + /** + * @param $value \DateTime + */ + public function setCreateDate($value) + { + $this->validateDate('create_date', $value); + + if ($this->data['create_date'] === $value) { + return; + } + + $this->data['create_date'] = $value; + + $this->setModified('create_date'); + } + + /** + * @return \DateTime + */ + public function getStartDate() + { + $rtn = $this->data['start_date']; + + if (!empty($rtn)) { + $rtn = new \DateTime($rtn); + } + + return $rtn; + } + + /** + * @param $value \DateTime + */ + public function setStartDate($value) + { + $this->validateDate('start_date', $value); + + if ($this->data['start_date'] === $value) { + return; + } + + $this->data['start_date'] = $value; + + $this->setModified('start_date'); + } + + /** + * @return \DateTime + */ + public function getFinishDate() + { + $rtn = $this->data['finish_date']; + + if (!empty($rtn)) { + $rtn = new \DateTime($rtn); + } + + return $rtn; + } + + /** + * @param $value \DateTime + */ + public function setFinishDate($value) + { + $this->validateDate('finish_date', $value); + + if ($this->data['finish_date'] === $value) { + return; + } + + $this->data['finish_date'] = $value; + + $this->setModified('finish_date'); + } + + /** + * @return string + */ + public function getCommitterEmail() + { + $rtn = $this->data['committer_email']; + + return $rtn; + } + + /** + * @param $value string + */ + public function setCommitterEmail($value) + { + $this->validateString('committer_email', $value); + + if ($this->data['committer_email'] === $value) { + return; + } + + $this->data['committer_email'] = $value; + + $this->setModified('committer_email'); + } + + /** + * @return string + */ + public function getCommitMessage() + { + $rtn = htmlspecialchars($this->data['commit_message']); + + return $rtn; + } + + /** + * @param $value string + */ + public function setCommitMessage($value) + { + $this->validateString('commit_message', $value); + + if ($this->data['commit_message'] === $value) { + return; + } + + $this->data['commit_message'] = $value; + + $this->setModified('commit_message'); + } + + /** + * @return string + */ + public function getTag() + { + $rtn = $this->data['tag']; + + return $rtn; + } + + /** + * @param $value string + */ + public function setTag($value) + { + $this->validateString('tag', $value); + + if ($this->data['tag'] === $value) { + return; + } + + $this->data['tag'] = $value; + + $this->setModified('tag'); + } + + /** + * @return string + */ + public function getSource() + { + $rtn = $this->data['source']; + + return (integer)$rtn; + } + + /** + * @param $value integer + */ + public function setSource($value) + { + $this->validateInt('source', $value); + + if ($this->data['source'] === $value) { + return; + } + + $this->data['source'] = $value; + + $this->setModified('source'); + } + + /** + * @return string + */ + public function getUserId() + { + $rtn = $this->data['user_id']; + + return (integer)$rtn; + } + + /** + * @param $value integer + */ + public function setUserId($value) + { + $this->validateNotNull('user_id', $value); + $this->validateInt('user_id', $value); + + if ($this->data['user_id'] === $value) { + return; + } + + $this->data['user_id'] = $value; + + $this->setModified('user_id'); + } + + /** + * @return string + */ + public function getEnvironment() + { + $rtn = $this->data['environment']; + + return $rtn; + } + + /** + * @param $value string + */ + public function setEnvironment($value) + { + $this->validateString('environment', $value); + + if ($this->data['environment'] === $value) { + return; + } + + $this->data['environment'] = $value; + + $this->setModified('environment'); + } + + /** + * Set the value of status only if it synced with db. Must not be null. + * + * @param $value int + * @return bool + */ + public function setStatusSync($value) + { + $this->validateNotNull('status', $value); + $this->validateInt('status', $value); + + if ($this->data['status'] !== $value) { + $store = Factory::getStore('Build'); + if ($store->updateStatusSync($this, $value)) { + $this->data['status'] = $value; + return true; + } + } + return false; + } + + /** + * Return a value from the build's "extra" JSON array. + * + * @param null $key + * + * @return mixed|null|string + */ + public function getExtra($key = null) + { + $data = json_decode($this->data['extra'], true); + + if (is_null($key)) { + $rtn = $data; + } elseif (isset($data[$key])) { + $rtn = $data[$key]; + } else { + $rtn = null; + } + + return $rtn; + } + + /** + * @param $value string + */ + public function setExtra($value) + { + $this->validateString('extra', $value); + + if ($this->data['extra'] === $value) { + return; + } + + $this->data['extra'] = $value; + + $this->setModified('extra'); + } + + /** + * Set the value of extra. + * + * @param $name string + * @param $value mixed + */ + public function setExtraValue($name, $value) + { + $extra = json_decode($this->data['extra'], true); + if ($extra === false) { + $extra = []; + } + $extra[$name] = $value; + $this->setExtra(json_encode($extra)); + } + + /** + * Set the values of extra. + * + * @param $values mixed + */ + public function setExtraValues($values) + { + $extra = json_decode($this->data['extra'], true); + if ($extra === false) { + $extra = []; + } + $extra = array_replace($extra, $values); + $this->setExtra(json_encode($extra)); + } + + /** + * Get the Project model for this Build by Id. + * + * @return \PHPCensor\Model\Project + */ + public function getProject() + { + $key = $this->getProjectId(); + + if (empty($key)) { + return null; + } + + return Factory::getStore('Project', 'PHPCensor')->getById($key); + } + + /** + * Set Project - Accepts an ID, an array representing a Project or a Project model. + * + * @param $value mixed + */ + public function setProject($value) + { + // Is this an instance of Project? + if ($value instanceof Project) { + return $this->setProjectObject($value); + } + + // Is this an array representing a Project item? + if (is_array($value) && !empty($value['id'])) { + return $this->setProjectId($value['id']); + } + + // Is this a scalar value representing the ID of this foreign key? + return $this->setProjectId($value); + } + + /** + * Set Project - Accepts a Project model. + * + * @param $value Project + */ + public function setProjectObject(Project $value) + { + return $this->setProjectId($value->getId()); + } + + /** + * Get BuildError models by BuildId for this Build. + * + * @return \PHPCensor\Model\BuildError[] + */ + public function getBuildBuildErrors() + { + return Factory::getStore('BuildError', 'PHPCensor')->getByBuildId($this->getId()); + } + + /** + * Get BuildMeta models by BuildId for this Build. + * + * @return \PHPCensor\Model\BuildMeta[] + */ + public function getBuildBuildMetas() + { + return Factory::getStore('BuildMeta', 'PHPCensor')->getByBuildId($this->getId()); + } + + public $currentBuildPath; + + /** + * Get link to commit from another source (i.e. Github) + */ + public function getCommitLink() + { + return '#'; + } + + /** + * Get link to branch from another source (i.e. Github) + */ + public function getBranchLink() + { + return '#'; + } + + /** + * Get link to tag from another source (i.e. Github) + */ + public function getTagLink() + { + return '#'; + } + + /** + * Return a template to use to generate a link to a specific file. + * + * @return null + */ + public function getFileLinkTemplate() + { + return null; + } + + /** + * Send status updates to any relevant third parties (i.e. Github) + */ + public function sendStatusPostback() + { + return false; + } + + /** + * @return string + */ + public function getProjectTitle() + { + $project = $this->getProject(); + return $project ? $project->getTitle() : ""; + } + + /** + * Store build metadata + */ + public function storeMeta($key, $value) + { + $value = json_encode($value); + Factory::getStore('Build')->setMeta($this->getId(), $key, $value); + } + + /** + * Is this build successful? + */ + public function isSuccessful() + { + return ($this->getStatus() === self::STATUS_SUCCESS); + } + + /** + * @param Builder $builder + * @param string $buildPath + * + * @return bool + */ + protected function handleConfig(Builder $builder, $buildPath) + { + $build_config = $this->getProject()->getBuildConfig(); + + if (empty($build_config)) { + if (file_exists($buildPath . '/.php-censor.yml')) { + $build_config = file_get_contents($buildPath . '/.php-censor.yml'); + } elseif (file_exists($buildPath . '/.phpci.yml')) { + $build_config = file_get_contents($buildPath . '/.phpci.yml'); + } elseif (file_exists($buildPath . '/phpci.yml')) { + $build_config = file_get_contents($buildPath . '/phpci.yml'); + } else { + $build_config = $this->getZeroConfigPlugins($builder); + } + } + + // for YAML configs from files/DB + if (is_string($build_config)) { + $yamlParser = new YamlParser(); + $build_config = $yamlParser->parse($build_config); + } + + $builder->setConfigArray($build_config); + + return true; + } + + /** + * Get an array of plugins to run if there's no .php-censor.yml file. + * @param Builder $builder + * @return array + */ + protected function getZeroConfigPlugins(Builder $builder) + { + $pluginDir = SRC_DIR . 'Plugin' . DIRECTORY_SEPARATOR; + $dir = new \DirectoryIterator($pluginDir); + + $config = [ + 'build_settings' => [ + 'ignore' => [ + 'vendor', + ] + ] + ]; + + foreach ($dir as $item) { + if ($item->isDot()) { + continue; + } + + if (!$item->isFile()) { + continue; + } + + if ($item->getExtension() != 'php') { + continue; + } + + $className = '\PHPCensor\Plugin\\'.$item->getBasename('.php'); + + $reflectedPlugin = new \ReflectionClass($className); + + if (!$reflectedPlugin->implementsInterface('\PHPCensor\ZeroConfigPluginInterface')) { + continue; + } + + foreach ([Build::STAGE_SETUP, Build::STAGE_TEST] as $stage) { + if ($className::canExecute($stage, $builder, $this)) { + $config[$stage][$className::pluginName()] = [ + 'zero_config' => true + ]; + } + } + } + + return $config; + } + + /** + * Allows specific build types (e.g. Github) to report violations back to their respective services. + * @param Builder $builder + * @param $plugin + * @param $message + * @param int $severity + * @param null $file + * @param null $lineStart + * @param null $lineEnd + */ + public function reportError( + Builder $builder, + $plugin, + $message, + $severity = BuildError::SEVERITY_NORMAL, + $file = null, + $lineStart = null, + $lineEnd = null + ) { + $writer = $builder->getBuildErrorWriter(); + $writer->write( + $plugin, + $message, + $severity, + $file, + $lineStart, + $lineEnd + ); + } + + /** + * Return the path to run this build into. + * + * @return string|null + */ + public function getBuildPath() + { + if (!$this->getId()) { + return null; + } + + if (empty($this->currentBuildPath)) { + $buildDirectory = $this->getId() . '_' . substr(md5(microtime(true)), 0, 5); + $this->currentBuildPath = + RUNTIME_DIR . + 'builds' . + DIRECTORY_SEPARATOR . + $buildDirectory . + DIRECTORY_SEPARATOR; + } + + return $this->currentBuildPath; + } + + /** + * Removes the build directory. + */ + public function removeBuildDirectory() + { + // Get the path and remove the trailing slash as this may prompt PHP + // to see this as a directory even if it's a link. + $buildPath = rtrim($this->getBuildPath(), '/'); + + if (!$buildPath || !is_dir($buildPath)) { + return; + } + + if (is_link($buildPath)) { + // Remove the symlink without using recursive. + exec(sprintf('rm "%s"', $buildPath)); + } else { + exec(sprintf('rm -Rf "%s"', $buildPath)); + } + } + + /** + * Get the number of seconds a build has been running for. + * + * @return int + */ + public function getDuration() + { + $start = $this->getStartDate(); + + if (empty($start)) { + return 0; + } + + $end = $this->getFinishDate(); + + if (empty($end)) { + $end = new \DateTime(); + } + + return $end->getTimestamp() - $start->getTimestamp(); + } + + /** + * get time a build has been running for in hour/minute/seconds format (e.g. 1h 21m 45s) + * + * @return string + */ + public function getPrettyDuration() + { + $start = $this->getStartDate(); + if (!$start) { + $start = new \DateTime(); + } + $end = $this->getFinishDate(); + if (!$end) { + $end = new \DateTime(); + } + + $diff = date_diff($start, $end); + $parts = []; + foreach (['y', 'm', 'd', 'h', 'i', 's'] as $time_part) { + if ($diff->{$time_part} != 0) { + $parts[] = $diff->{$time_part} . ($time_part == 'i' ? 'm' : $time_part); + } + } + + return implode(" ", $parts); + } + + /** + * Create a working copy by cloning, copying, or similar. + * + * @param Builder $builder + * @param string $buildPath + * + * @return boolean + */ + public function createWorkingCopy(Builder $builder, $buildPath) + { + return false; + } + + /** + * Create an SSH key file on disk for this build. + * + * @param string $cloneTo + * + * @return string + */ + protected function writeSshKey($cloneTo) + { + $keyPath = dirname($cloneTo . '/temp'); + $keyFile = $keyPath . '.key'; + + file_put_contents($keyFile, $this->getProject()->getSshPrivateKey()); + chmod($keyFile, 0600); + + return $keyFile; + } + + /** + * Create an SSH wrapper script for Svn to use, to disable host key checking, etc. + * + * @param string $cloneTo + * @param string $keyFile + * + * @return string + */ + protected function writeSshWrapper($cloneTo, $keyFile) + { + $path = dirname($cloneTo . '/temp'); + $wrapperFile = $path . '.sh'; + + $sshFlags = '-o CheckHostIP=no -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -o PasswordAuthentication=no'; + + // Write out the wrapper script for this build: + $script = <<getSource()) { + case Build::SOURCE_WEBHOOK: + return 'source_webhook'; + case Build::SOURCE_MANUAL_WEB: + return 'source_manual_web'; + case Build::SOURCE_MANUAL_CONSOLE: + return 'source_manual_console'; + case Build::SOURCE_PERIODICAL: + return 'source_periodical'; + case Build::SOURCE_UNKNOWN: + default: + return 'source_unknown'; + } + } +} diff --git a/src/PHPCensor/Model/Build/BitbucketBuild.php b/src/PHPCensor/Model/Build/BitbucketBuild.php new file mode 100644 index 0000000..7710295 --- /dev/null +++ b/src/PHPCensor/Model/Build/BitbucketBuild.php @@ -0,0 +1,288 @@ + + */ +class BitbucketBuild extends RemoteGitBuild +{ + /** + * Get link to commit from another source (i.e. BitBucket) + */ + public function getCommitLink() + { + return 'https://bitbucket.org/' . $this->getProject()->getReference() . '/commits/' . $this->getCommitId(); + } + + /** + * Get link to branch from another source (i.e. BitBucket) + */ + public function getBranchLink() + { + return 'https://bitbucket.org/' . $this->getProject()->getReference() . '/src/?at=' . $this->getBranch(); + } + + /** + * Get link to tag from another source (i.e. BitBucket) + */ + public function getTagLink() + { + return 'https://bitbucket.org/' . $this->getProject()->getReference() . '/src/?at=' . $this->getTag(); + } + + /** + * Send status updates to any relevant third parties (i.e. Bitbucket) + * + * @return bool + */ + public function sendStatusPostback() + { + if (Build::SOURCE_WEBHOOK !== $this->getSource()) { + return false; + } + + $project = $this->getProject(); + if (empty($project)) { + return false; + } + + $username = Config::getInstance()->get('php-censor.bitbucket.username'); + $appPassword = Config::getInstance()->get('php-censor.bitbucket.app_password'); + + if (empty($username) || empty($appPassword) || empty($this->data['id'])) { + return false; + } + + switch ($this->getStatus()) { + case 0: + case 1: + $status = 'INPROGRESS'; + $description = 'PHP Censor build running.'; + break; + case 2: + $status = 'SUCCESSFUL'; + $description = 'PHP Censor build passed.'; + break; + case 3: + $status = 'FAILED'; + $description = 'PHP Censor build failed.'; + break; + default: + $status = 'STOPPED'; + $description = 'PHP Censor build failed to complete.'; + break; + } + + $phpCensorUrl = Config::getInstance()->get('php-censor.url'); + + $url = sprintf( + '/2.0/repositories/%s/commit/%s/statuses/build', + $this->getExtra('build_type') == 'pull_request' + ? $this->getExtra('remote_reference') + : $project->getReference(), + $this->getCommitId() + ); + + $client = new Client([ + 'base_uri' => 'https://api.bitbucket.org', + 'http_errors' => false, + ]); + $response = $client->post($url, [ + 'auth' => [$username, $appPassword], + 'headers' => [ + 'Content-Type' => 'application/json', + ], + 'json' => [ + 'state' => $status, + 'key' => 'PHP-CENSOR', + 'url' => $phpCensorUrl . '/build/view/' . $this->getId(), + 'name' => 'PHP Censor Build #' . $this->getId(), + 'description' => $description, + ], + ]); + + $status = (integer)$response->getStatusCode(); + + return ($status >= 200 && $status < 300); + } + + /** + * Get the URL to be used to clone this remote repository. + */ + protected function getCloneUrl() + { + $key = trim($this->getProject()->getSshPrivateKey()); + + if (!empty($key)) { + return 'git@bitbucket.org:' . $this->getProject()->getReference() . '.git'; + } else { + return 'https://bitbucket.org/' . $this->getProject()->getReference() . '.git'; + } + } + + /** + * Get a template to use for generating links to files. + * + * @return string + */ + public function getFileLinkTemplate() + { + $reference = $this->getProject()->getReference(); + + if ($this->getExtra('build_type') == 'pull_request') { + $reference = $this->getExtra('remote_reference'); + } + + $link = 'https://bitbucket.org/' . $reference . '/'; + $link .= 'src/' . $this->getCommitId() . '/'; + $link .= '{FILE}'; + $link .= '#{BASEFILE}-{LINE}'; + + return $link; + } + + /** + * Handle any post-clone tasks, like applying a pull request patch on top of the branch. + * @param Builder $builder + * @param $cloneTo + * @param array $extra + * @return bool + */ + protected function postCloneSetup(Builder $builder, $cloneTo, array $extra = null) + { + $buildType = $this->getExtra('build_type'); + + $success = true; + $skipGitFinalization = false; + + try { + if (!empty($buildType) && $buildType == 'pull_request') { + $helper = new Bitbucket(); + $diff = $helper->getPullRequestDiff( + $this->getProject()->getReference(), + $this->getExtra('pull_request_number') + ); + + $diffFile = $this->writeDiff($builder->buildPath, $diff); + + $cmd = 'cd "%s" && git checkout -b php-censor/' . $this->getId() . ' && git apply "%s"'; + + $success = $builder->executeCommand($cmd, $cloneTo, $diffFile); + + unlink($diffFile); + $skipGitFinalization = true; + } + } catch (\Exception $ex) { + $success = false; + } + + if ($success && !$skipGitFinalization) { + $success = parent::postCloneSetup($builder, $cloneTo, $extra); + } + + return $success; + } + + /** + * Create an diff file on disk for this build. + * + * @param string $cloneTo + * + * @return string + */ + protected function writeDiff($cloneTo, $diff) + { + $filePath = dirname($cloneTo . '/temp'); + $diffFile = $filePath . '.patch'; + + file_put_contents($diffFile, $diff); + chmod($diffFile, 0600); + + return $diffFile; + } + + /** + * @inheritDoc + */ + public function reportError( + Builder $builder, + $plugin, + $message, + $severity = BuildError::SEVERITY_NORMAL, + $file = null, + $lineStart = null, + $lineEnd = null + ) { + $allowCommentCommit = (boolean)Config::getInstance()->get('php-censor.bitbucket.comments.commit', false); + $allowCommentPullRequest = (boolean)Config::getInstance()->get('php-censor.bitbucket.comments.pull_request', false); + //$file = $builder->buildPath.'test.php'; + if ($allowCommentCommit || $allowCommentPullRequest) { + $diffLineNumber = $this->getDiffLineNumber($builder, $file, $lineStart); + + if (!is_null($diffLineNumber)) { + $helper = new Bitbucket(); + + $repo = $this->getProject()->getReference(); + $prNumber = $this->getExtra('pull_request_number'); + $commit = $this->getCommitId(); + + if (!empty($prNumber)) { + if ($allowCommentPullRequest) { + $helper->createPullRequestComment($repo, $prNumber, $commit, $file, $lineStart, $message); + } + } else { + if ($allowCommentCommit) { + $helper->createCommitComment($repo, $commit, $file, $lineStart, $message); + } + } + } + } + + parent::reportError($builder, $plugin, $message, $severity, $file, $lineStart, $lineEnd); + } + + /** + * Uses git diff to figure out what the diff line position is, based on the error line number. + * @param Builder $builder + * @param $file + * @param $line + * @return int|null + */ + protected function getDiffLineNumber(Builder $builder, $file, $line) + { + $builder->logExecOutput(false); + + $line = (integer)$line; + $prNumber = $this->getExtra('pull_request_number'); + $path = $builder->buildPath; + + if (!empty($prNumber)) { + $builder->executeCommand('cd %s && git diff origin/%s "%s"', $path, $this->getBranch(), $file); + } else { + $commitId = $this->getCommitId(); + $compare = empty($commitId) ? 'HEAD' : $commitId; + + $builder->executeCommand('cd %s && git diff %s^^ "%s"', $path, $compare, $file); + } + + $builder->logExecOutput(true); + + $diff = $builder->getLastOutput(); + + $helper = new Diff(); + $lines = $helper->getLinePositions($diff); + + return isset($lines[$line]) ? $lines[$line] : null; + } +} diff --git a/src/PHPCensor/Model/Build/BitbucketHgBuild.php b/src/PHPCensor/Model/Build/BitbucketHgBuild.php new file mode 100644 index 0000000..37e832d --- /dev/null +++ b/src/PHPCensor/Model/Build/BitbucketHgBuild.php @@ -0,0 +1,65 @@ + + */ +class BitbucketHgBuild extends MercurialBuild +{ + /** + * Get link to commit from another source (i.e. BitBucket) + */ + public function getCommitLink() + { + return 'https://bitbucket.org/' . $this->getProject()->getReference() . '/commits/' . $this->getCommitId(); + } + + /** + * Get link to branch from another source (i.e. BitBucket) + */ + public function getBranchLink() + { + return 'https://bitbucket.org/' . $this->getProject()->getReference() . '/src/?at=' . $this->getBranch(); + } + + /** + * Get the URL to be used to clone this remote repository. + */ + protected function getCloneUrl() + { + $key = trim($this->getProject()->getSshPrivateKey()); + + if (!empty($key)) { + return 'ssh://hg@bitbucket.org/' . $this->getProject()->getReference(); + } else { + return 'https://bitbucket.org/' . $this->getProject()->getReference(); + } + } + + /** + * Get a template to use for generating links to files. + * + * @return string + */ + public function getFileLinkTemplate() + { + $reference = $this->getProject()->getReference(); + + if ($this->getExtra('build_type') == 'pull_request') { + $matches = []; + preg_match('/[\/:]([a-zA-Z0-9_\-]+\/[a-zA-Z0-9_\-]+)/', $this->getExtra('remote_url'), $matches); + + $reference = $matches[1]; + } + + $link = 'https://bitbucket.org/' . $reference . '/'; + $link .= 'src/' . $this->getCommitId() . '/'; + $link .= '{FILE}'; + $link .= '#{BASEFILE}-{LINE}'; + + return $link; + } +} diff --git a/src/PHPCensor/Model/Build/GithubBuild.php b/src/PHPCensor/Model/Build/GithubBuild.php new file mode 100644 index 0000000..b592be9 --- /dev/null +++ b/src/PHPCensor/Model/Build/GithubBuild.php @@ -0,0 +1,274 @@ + + */ +class GithubBuild extends RemoteGitBuild +{ + /** + * Get link to commit from another source (i.e. Github) + */ + public function getCommitLink() + { + return 'https://github.com/' . $this->getProject()->getReference() . '/commit/' . $this->getCommitId(); + } + + /** + * Get link to branch from another source (i.e. Github) + */ + public function getBranchLink() + { + return 'https://github.com/' . $this->getProject()->getReference() . '/tree/' . $this->getBranch(); + } + + /** + * Get link to tag from another source (i.e. Github) + */ + public function getTagLink() + { + return 'https://github.com/' . $this->getProject()->getReference() . '/tree/' . $this->getTag(); + } + + /** + * Send status updates to any relevant third parties (i.e. Github) + */ + public function sendStatusPostback() + { + if (Build::SOURCE_WEBHOOK !== $this->getSource()) { + return false; + } + + $project = $this->getProject(); + if (empty($project)) { + return false; + } + + $token = Config::getInstance()->get('php-censor.github.token'); + + if (empty($token) || empty($this->data['id'])) { + return false; + } + + switch ($this->getStatus()) { + case 0: + case 1: + $status = 'pending'; + $description = 'PHP Censor build running.'; + break; + case 2: + $status = 'success'; + $description = 'PHP Censor build passed.'; + break; + case 3: + $status = 'failure'; + $description = 'PHP Censor build failed.'; + break; + default: + $status = 'error'; + $description = 'PHP Censor build failed to complete.'; + break; + } + + $phpCensorUrl = Config::getInstance()->get('php-censor.url'); + + $url = 'https://api.github.com/repos/' . $project->getReference() . '/statuses/' . $this->getCommitId(); + $client = new Client(); + $client->post($url, [ + 'headers' => [ + 'Authorization' => 'token ' . $token, + 'Content-Type' => 'application/x-www-form-urlencoded' + ], + 'json' => [ + 'state' => $status, + 'target_url' => $phpCensorUrl . '/build/view/' . $this->getId(), + 'description' => $description, + 'context' => 'PHP Censor', + ] + ]); + + return true; + } + + /** + * Get the URL to be used to clone this remote repository. + */ + protected function getCloneUrl() + { + $key = trim($this->getProject()->getSshPrivateKey()); + + if (!empty($key)) { + return 'git@github.com:' . $this->getProject()->getReference() . '.git'; + } else { + return 'https://github.com/' . $this->getProject()->getReference() . '.git'; + } + } + + /** + * Get a parsed version of the commit message, with links to issues and commits. + * + * @return string + */ + public function getCommitMessage() + { + $rtn = parent::getCommitMessage(); + + $project = $this->getProject(); + + if (!is_null($project)) { + $reference = $project->getReference(); + $commitLink = '#$1'; + $rtn = preg_replace('/\#([0-9]+)/', $commitLink, $rtn); + $rtn = preg_replace('/\@([a-zA-Z0-9_]+)/', '@$1', $rtn); + } + + return $rtn; + } + + /** + * Get a template to use for generating links to files. + * + * @return string + */ + public function getFileLinkTemplate() + { + $reference = $this->getProject()->getReference(); + + if ($this->getExtra('build_type') == 'pull_request') { + $matches = []; + preg_match('/[\/:]([a-zA-Z0-9_\-]+\/[a-zA-Z0-9_\-]+)/', $this->getExtra('remote_url'), $matches); + + $reference = $matches[1]; + } + + $link = 'https://github.com/' . $reference . '/'; + $link .= 'blob/' . $this->getCommitId() . '/'; + $link .= '{FILE}'; + $link .= '#L{LINE}-L{LINE_END}'; + + return $link; + } + + /** + * Handle any post-clone tasks, like applying a pull request patch on top of the branch. + * @param Builder $builder + * @param $cloneTo + * @param array $extra + * @return bool + */ + protected function postCloneSetup(Builder $builder, $cloneTo, array $extra = null) + { + $buildType = $this->getExtra('build_type'); + + $success = true; + + try { + if (!empty($buildType) && $buildType == 'pull_request') { + $pullRequestId = $this->getExtra('pull_request_number'); + + $cmd = 'cd "%s" && git checkout -b php-censor/' . $this->getId() + . ' %s && git pull -q --no-edit origin pull/%s/head'; + if (!empty($extra['git_ssh_wrapper'])) { + $cmd = 'export GIT_SSH="'.$extra['git_ssh_wrapper'].'" && ' . $cmd; + } + $success = $builder->executeCommand($cmd, $cloneTo, $this->getBranch(), $pullRequestId); + } + } catch (\Exception $ex) { + $success = false; + } + + if ($success) { + $success = parent::postCloneSetup($builder, $cloneTo, $extra); + } + + return $success; + } + + /** + * @inheritDoc + */ + public function reportError( + Builder $builder, + $plugin, + $message, + $severity = BuildError::SEVERITY_NORMAL, + $file = null, + $lineStart = null, + $lineEnd = null + ) { + $allowCommentCommit = (boolean)Config::getInstance()->get('php-censor.github.comments.commit', false); + $allowCommentPullRequest = (boolean)Config::getInstance()->get('php-censor.github.comments.pull_request', false); + + if ($allowCommentCommit || $allowCommentPullRequest) { + $diffLineNumber = $this->getDiffLineNumber($builder, $file, $lineStart); + + if (!is_null($diffLineNumber)) { + $helper = new Github(); + + $repo = $this->getProject()->getReference(); + $prNumber = $this->getExtra('pull_request_number'); + $commit = $this->getCommitId(); + + $allowCommentCommit = (boolean)Config::getInstance()->get('php-censor.github.comments.commit', false); + $allowCommentPullRequest = (boolean)Config::getInstance()->get('php-censor.github.comments.pull_request', false); + + if (!empty($prNumber)) { + if ($allowCommentPullRequest) { + $helper->createPullRequestComment($repo, $prNumber, $commit, $file, $diffLineNumber, $message); + } + } else { + if ($allowCommentCommit) { + $helper->createCommitComment($repo, $commit, $file, $diffLineNumber, $message); + } + } + } + } + + parent::reportError($builder, $plugin, $message, $severity, $file, $lineStart, $lineEnd); + } + + /** + * Uses git diff to figure out what the diff line position is, based on the error line number. + * @param Builder $builder + * @param $file + * @param $line + * @return int|null + */ + protected function getDiffLineNumber(Builder $builder, $file, $line) + { + $builder->logExecOutput(false); + + $line = (integer)$line; + $prNumber = $this->getExtra('pull_request_number'); + $path = $builder->buildPath; + + if (!empty($prNumber)) { + $builder->executeCommand('cd %s && git diff origin/%s "%s"', $path, $this->getBranch(), $file); + } else { + $commitId = $this->getCommitId(); + $compare = empty($commitId) ? 'HEAD' : $commitId; + + $builder->executeCommand('cd %s && git diff %s^^ "%s"', $path, $compare, $file); + } + + $builder->logExecOutput(true); + + $diff = $builder->getLastOutput(); + + $helper = new Diff(); + $lines = $helper->getLinePositions($diff); + + return isset($lines[$line]) ? $lines[$line] : null; + } +} diff --git a/src/PHPCensor/Model/Build/GitlabBuild.php b/src/PHPCensor/Model/Build/GitlabBuild.php new file mode 100644 index 0000000..35525b3 --- /dev/null +++ b/src/PHPCensor/Model/Build/GitlabBuild.php @@ -0,0 +1,67 @@ + + */ +class GitlabBuild extends RemoteGitBuild +{ + + /** + * Get link to commit from another source (i.e. Github) + */ + public function getCommitLink() + { + $domain = $this->getProject()->getAccessInformation("domain"); + return 'http://' . $domain . '/' . $this->getProject()->getReference() . '/commit/' . $this->getCommitId(); + } + + /** + * Get link to branch from another source (i.e. Github) + */ + public function getBranchLink() + { + $domain = $this->getProject()->getAccessInformation("domain"); + return 'http://' . $domain . '/' . $this->getProject()->getReference() . '/tree/' . $this->getBranch(); + } + + /** + * Get link to specific file (and line) in a the repo's branch + */ + public function getFileLinkTemplate() + { + return sprintf( + 'http://%s/%s/blob/%s/{FILE}#L{LINE}', + $this->getProject()->getAccessInformation("domain"), + $this->getProject()->getReference(), + $this->getCommitId() + ); + } + + /** + * Get the URL to be used to clone this remote repository. + */ + protected function getCloneUrl() + { + $key = trim($this->getProject()->getSshPrivateKey()); + + if (!empty($key)) { + $user = $this->getProject()->getAccessInformation("user"); + $domain = $this->getProject()->getAccessInformation("domain"); + $port = $this->getProject()->getAccessInformation('port'); + + $url = $user . '@' . $domain . ':'; + + if (!empty($port)) { + $url .= $port . '/'; + } + + $url .= $this->getProject()->getReference() . '.git'; + + return $url; + } + } +} diff --git a/src/PHPCensor/Model/Build/GogsBuild.php b/src/PHPCensor/Model/Build/GogsBuild.php new file mode 100644 index 0000000..cab9ad5 --- /dev/null +++ b/src/PHPCensor/Model/Build/GogsBuild.php @@ -0,0 +1,36 @@ +getProject()->getReference() . '/commit/' . $this->getCommitId(); + } + + /** + * Get link to branch from Gogs repository + */ + public function getBranchLink() + { + return $this->getProject()->getReference() . '/src/' . $this->getBranch(); + } + /** + * Get link to specific file (and line) in a the repo's branch + */ + public function getFileLinkTemplate() + { + return sprintf( + '%s/src/%s/{FILE}#L{LINE}', + $this->getProject()->getReference(), + $this->getCommitId() + ); + } +} diff --git a/src/PHPCensor/Model/Build/LocalBuild.php b/src/PHPCensor/Model/Build/LocalBuild.php new file mode 100644 index 0000000..5e703a0 --- /dev/null +++ b/src/PHPCensor/Model/Build/LocalBuild.php @@ -0,0 +1,91 @@ + + */ +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) { + return $this->handleConfig($builder, $buildPath) !== false; + } + + $configHandled = $this->handleConfig($builder, $reference); + + if ($configHandled === false) { + return false; + } + + $buildSettings = $builder->getConfig('build_settings'); + + if (isset($buildSettings['prefer_symlink']) && $buildSettings['prefer_symlink'] === true) { + return $this->handleSymlink($builder, $reference, $buildPath); + } else { + $cmd = 'cp -Rf "%s" "%s/"'; + $builder->executeCommand($cmd, $reference, $buildPath); + } + + return true; + } + + /** + * Check if this is a "bare" git repository, and if so, unarchive it. + * @param Builder $builder + * @param $reference + * @param $buildPath + * @return bool + */ + 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']) { + $cmd = 'mkdir %2$s; git --git-dir="%1$s" archive %3$s | tar -x -C "%2$s"'; + $builder->executeCommand($cmd, $reference, $buildPath, $this->getBranch()); + return true; + } + + return false; + } + + /** + * Create a symlink if required. + * @param Builder $builder + * @param $reference + * @param $buildPath + * @return bool + */ + 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; + } +} diff --git a/src/PHPCensor/Model/Build/MercurialBuild.php b/src/PHPCensor/Model/Build/MercurialBuild.php new file mode 100644 index 0000000..1797620 --- /dev/null +++ b/src/PHPCensor/Model/Build/MercurialBuild.php @@ -0,0 +1,99 @@ + + */ +class MercurialBuild extends Build +{ + /** + * Get the URL to be used to clone this remote repository. + */ + protected function getCloneUrl() + { + return $this->getProject()->getReference(); + } + + /** + * Create a working copy by cloning, copying, or similar. + */ + public function createWorkingCopy(Builder $builder, $buildPath) + { + $key = trim($this->getProject()->getSshPrivateKey()); + + if (!empty($key)) { + $success = $this->cloneBySsh($builder, $buildPath); + } else { + $success = $this->cloneByHttp($builder, $buildPath); + } + + if (!$success) { + $builder->logFailure('Failed to clone remote hg repository.'); + + return false; + } + + return $this->handleConfig($builder, $buildPath); + } + + /** + * Use a HTTP-based hg clone. + */ + protected function cloneByHttp(Builder $builder, $cloneTo) + { + return $builder->executeCommand('hg clone %s "%s" -r %s', $this->getCloneUrl(), $cloneTo, $this->getBranch()); + } + + /** + * Use an SSH-based hg clone. + * + * @param Builder $builder + * @param string $cloneTo + * + * @return bool + */ + protected function cloneBySsh(Builder $builder, $cloneTo) + { + $keyFile = $this->writeSshKey($cloneTo); + + // Do the hg clone: + $cmd = 'hg clone --ssh "ssh -i '.$keyFile.'" %s "%s" -r %s'; + $success = $builder->executeCommand($cmd, $this->getCloneUrl(), $cloneTo, $this->getBranch()); + + if ($success) { + $success = $this->postCloneSetup($builder, $cloneTo); + } + + // Remove the key file: + unlink($keyFile); + + return $success; + } + + /** + * Handle post-clone tasks (switching branch, etc.) + * @param Builder $builder + * @param $cloneTo + * @param array $extra + * @return bool + */ + protected function postCloneSetup(Builder $builder, $cloneTo, array $extra = null) + { + $success = true; + $commitId = $this->getCommitId(); + + // Allow switching to a specific branch: + if (!empty($commitId)) { + $cmd = 'cd "%s" && hg checkout %s'; + $success = $builder->executeCommand($cmd, $cloneTo, $this->getBranch()); + } + + return $success; + } +} diff --git a/src/PHPCensor/Model/Build/RemoteGitBuild.php b/src/PHPCensor/Model/Build/RemoteGitBuild.php new file mode 100644 index 0000000..9330110 --- /dev/null +++ b/src/PHPCensor/Model/Build/RemoteGitBuild.php @@ -0,0 +1,162 @@ + + */ +class RemoteGitBuild extends Build +{ + /** + * Get the URL to be used to clone this remote repository. + */ + protected function getCloneUrl() + { + return $this->getProject()->getReference(); + } + + /** + * Create a working copy by cloning, copying, or similar. + */ + public function createWorkingCopy(Builder $builder, $buildPath) + { + $key = trim($this->getProject()->getSshPrivateKey()); + + if (!empty($key)) { + $success = $this->cloneBySsh($builder, $buildPath); + } else { + $success = $this->cloneByHttp($builder, $buildPath); + } + + if ($success) { + $success = $this->mergeBranches($builder, $buildPath); + } + + if (!$success) { + $builder->logFailure('Failed to clone remote git repository.'); + return false; + } + + return $this->handleConfig($builder, $buildPath); + } + + /** + * @param Builder $builder + * @param string $buildPath + * @return bool + */ + protected function mergeBranches(Builder $builder, $buildPath) + { + $branches = $this->getExtra('branches'); + if (!empty($branches)) { + $cmd = 'cd "%s" && git merge --quiet origin/%s'; + foreach ($branches as $branch) { + $success = $builder->executeCommand($cmd, $buildPath, $branch); + if (!$success) { + $builder->log('Fail merge branch origin/'.$branch, LogLevel::ERROR); + return false; + } + $builder->log('Merged branch origin/'.$branch, LogLevel::INFO); + } + } + return true; + } + + /** + * Use an HTTP-based git clone. + */ + protected function cloneByHttp(Builder $builder, $cloneTo) + { + $cmd = 'cd .. && git clone --recursive '; + + $depth = $builder->getConfig('clone_depth'); + + if (!is_null($depth)) { + $cmd .= ' --depth ' . intval($depth) . ' '; + } + + $cmd .= ' -b "%s" "%s" "%s"'; + $success = $builder->executeCommand($cmd, $this->getBranch(), $this->getCloneUrl(), $cloneTo); + + if ($success) { + $success = $this->postCloneSetup($builder, $cloneTo); + } + + return $success; + } + + /** + * Use an SSH-based git clone. + */ + protected function cloneBySsh(Builder $builder, $cloneTo) + { + $keyFile = $this->writeSshKey($cloneTo); + $gitSshWrapper = $this->writeSshWrapper($cloneTo, $keyFile); + + // Do the git clone: + $cmd = 'cd .. && git clone --recursive '; + + $depth = $builder->getConfig('clone_depth'); + + if (!is_null($depth)) { + $cmd .= ' --depth ' . intval($depth) . ' '; + } + + $cmd .= ' -b "%s" "%s" "%s"'; + $cmd = 'export GIT_SSH="'.$gitSshWrapper.'" && ' . $cmd; + + $success = $builder->executeCommand($cmd, $this->getBranch(), $this->getCloneUrl(), $cloneTo); + + if ($success) { + $extra = [ + 'git_ssh_wrapper' => $gitSshWrapper + ]; + + $success = $this->postCloneSetup($builder, $cloneTo, $extra); + } + + // Remove the key file and git wrapper: + unlink($keyFile); + unlink($gitSshWrapper); + + return $success; + } + + /** + * Handle any post-clone tasks, like switching branches. + * @param Builder $builder + * @param $cloneTo + * @param array $extra + * @return bool + */ + protected function postCloneSetup(Builder $builder, $cloneTo, array $extra = null) + { + $success = true; + $commitId = $this->getCommitId(); + $chdir = 'cd "%s"'; + + if (empty($this->getEnvironment()) && !empty($commitId)) { + $cmd = $chdir . ' && git checkout %s --quiet'; + $success = $builder->executeCommand($cmd, $cloneTo, $commitId); + } + + // Always update the commit hash with the actual HEAD hash + if ($builder->executeCommand($chdir . ' && git rev-parse HEAD', $cloneTo)) { + $commitId = trim($builder->getLastOutput()); + + $this->setCommitId($commitId); + + if ($builder->executeCommand($chdir . ' && git log -1 --pretty=format:%%s %s', $cloneTo, $commitId)) { + $this->setCommitMessage(trim($builder->getLastOutput())); + } + } + + return $success; + } +} diff --git a/src/PHPCensor/Model/Build/SubversionBuild.php b/src/PHPCensor/Model/Build/SubversionBuild.php new file mode 100644 index 0000000..68a2ff7 --- /dev/null +++ b/src/PHPCensor/Model/Build/SubversionBuild.php @@ -0,0 +1,127 @@ + + */ +class SubversionBuild extends Build +{ + protected $svnCommand = 'svn export -q --non-interactive '; + + /** + * Get the URL to be used to clone this remote repository. + */ + protected function getCloneUrl() + { + $url = rtrim($this->getProject()->getReference(), '/') . '/'; + $branch = ltrim($this->getBranch(), '/'); + + // For empty default branch or default branch name like "/trunk" or "trunk" (-> "trunk") + if (empty($branch) || $branch == 'trunk') { + $url .= 'trunk'; + // For default branch with standard default branch directory ("branches") like "/branch-1" or "branch-1" + // (-> "branches/branch-1") + } elseif (false === strpos($branch, '/')) { + $url .= 'branches/' . $branch; + // For default branch with non-standard branch directory like "/branch/branch-1" or "branch/branch-1" + // (-> "branch/branch-1") + } else { + $url .= $branch; + } + + return $url; + } + + /** + * @param Builder $builder + * + * @return void + */ + protected function extendSvnCommandFromConfig(Builder $builder) + { + $cmd = $this->svnCommand; + + $svn = $builder->getConfig('svn'); + if ($svn) { + foreach ($svn as $key => $value) { + $cmd .= " --$key $value "; + } + } + + $depth = $builder->getConfig('clone_depth'); + + if (!is_null($depth)) { + $cmd .= ' --depth ' . intval($depth) . ' '; + } + + $this->svnCommand = $cmd; + } + + /** + * Create a working copy by cloning, copying, or similar. + */ + public function createWorkingCopy(Builder $builder, $buildPath) + { + $this->handleConfig($builder, $buildPath); + + $this->extendSvnCommandFromConfig($builder); + + $key = trim($this->getProject()->getSshPrivateKey()); + + if (!empty($key)) { + $success = $this->cloneBySsh($builder, $buildPath); + } else { + $success = $this->cloneByHttp($builder, $buildPath); + } + + if (!$success) { + $builder->logFailure('Failed to export remote subversion repository.'); + return false; + } + + return $this->handleConfig($builder, $buildPath); + } + + /** + * Use an HTTP-based svn export. + */ + protected function cloneByHttp(Builder $builder, $cloneTo) + { + $cmd = $this->svnCommand; + + if (!empty($this->getCommitId())) { + $cmd .= ' -r %s %s "%s"'; + $success = $builder->executeCommand($cmd, $this->getCommitId(), $this->getCloneUrl(), $cloneTo); + } else { + $cmd .= ' %s "%s"'; + $success = $builder->executeCommand($cmd, $this->getCloneUrl(), $cloneTo); + } + + return $success; + } + + /** + * Use an SSH-based svn export. + */ + protected function cloneBySsh(Builder $builder, $cloneTo) + { + $cmd = $this->svnCommand . ' %s "%s"'; + $keyFile = $this->writeSshKey($cloneTo); + $sshWrapper = $this->writeSshWrapper($cloneTo, $keyFile); + $cmd = 'export SVN_SSH="' . $sshWrapper . '" && ' . $cmd; + + $success = $builder->executeCommand($cmd, $this->getCloneUrl(), $cloneTo); + + // Remove the key file and svn wrapper: + unlink($keyFile); + unlink($sshWrapper); + + return $success; + } +} diff --git a/src/PHPCensor/Model/BuildError.php b/src/PHPCensor/Model/BuildError.php new file mode 100644 index 0000000..3e8796d --- /dev/null +++ b/src/PHPCensor/Model/BuildError.php @@ -0,0 +1,449 @@ + null, + 'build_id' => null, + 'plugin' => null, + 'file' => null, + 'line_start' => null, + 'line_end' => null, + 'severity' => null, + 'message' => null, + 'create_date' => null, + ]; + + /** + * @var array + */ + protected $getters = [ + // Direct property getters: + 'id' => 'getId', + 'build_id' => 'getBuildId', + 'plugin' => 'getPlugin', + 'file' => 'getFile', + 'line_start' => 'getLineStart', + 'line_end' => 'getLineEnd', + 'severity' => 'getSeverity', + 'message' => 'getMessage', + 'create_date' => 'getCreateDate', + + // Foreign key getters: + 'Build' => 'getBuild', + ]; + + /** + * @var array + */ + protected $setters = [ + // Direct property setters: + 'id' => 'setId', + 'build_id' => 'setBuildId', + 'plugin' => 'setPlugin', + 'file' => 'setFile', + 'line_start' => 'setLineStart', + 'line_end' => 'setLineEnd', + 'severity' => 'setSeverity', + 'message' => 'setMessage', + 'create_date' => 'setCreateDate', + + // Foreign key setters: + 'Build' => 'setBuild', + ]; + + /** + * @return int + */ + public function getId() + { + $rtn = $this->data['id']; + + return $rtn; + } + + /** + * @return int + */ + public function getBuildId() + { + $rtn = $this->data['build_id']; + + return $rtn; + } + + /** + * @return string + */ + public function getPlugin() + { + $rtn = $this->data['plugin']; + + return $rtn; + } + + /** + * @return string + */ + public function getFile() + { + $rtn = $this->data['file']; + + return $rtn; + } + + /** + * @return int + */ + public function getLineStart() + { + $rtn = $this->data['line_start']; + + return $rtn; + } + + /** + * @return int + */ + public function getLineEnd() + { + $rtn = $this->data['line_end']; + + return $rtn; + } + + /** + * @return int + */ + public function getSeverity() + { + $rtn = $this->data['severity']; + + return $rtn; + } + + /** + * @return string + */ + public function getMessage() + { + $rtn = $this->data['message']; + + return $rtn; + } + + /** + * @return \DateTime + */ + public function getCreateDate() + { + $rtn = $this->data['create_date']; + + if (!empty($rtn)) { + $rtn = new \DateTime($rtn); + } + + return $rtn; + } + + /** + * @param $value int + */ + public function setId($value) + { + $this->validateNotNull('id', $value); + $this->validateInt('id', $value); + + if ($this->data['id'] === $value) { + return; + } + + $this->data['id'] = $value; + + $this->setModified('id'); + } + + /** + * @param $value int + */ + public function setBuildId($value) + { + $this->validateNotNull('build_id', $value); + $this->validateInt('build_id', $value); + + if ($this->data['build_id'] === $value) { + return; + } + + $this->data['build_id'] = $value; + + $this->setModified('build_id'); + } + + /** + * @param $value string + */ + public function setPlugin($value) + { + $this->validateNotNull('plugin', $value); + $this->validateString('plugin', $value); + + if ($this->data['plugin'] === $value) { + return; + } + + $this->data['plugin'] = $value; + + $this->setModified('plugin'); + } + + /** + * @param $value string + */ + public function setFile($value) + { + $this->validateString('file', $value); + + if ($this->data['file'] === $value) { + return; + } + + $this->data['file'] = $value; + + $this->setModified('file'); + } + + /** + * @param $value int + */ + public function setLineStart($value) + { + $this->validateInt('line_start', $value); + + if ($this->data['line_start'] === $value) { + return; + } + + $this->data['line_start'] = $value; + + $this->setModified('line_start'); + } + + /** + * @param $value int + */ + public function setLineEnd($value) + { + $this->validateInt('line_end', $value); + + if ($this->data['line_end'] === $value) { + return; + } + + $this->data['line_end'] = $value; + + $this->setModified('line_end'); + } + + /** + * @param $value int + */ + public function setSeverity($value) + { + $this->validateNotNull('severity', $value); + $this->validateInt('severity', $value); + + if ($this->data['severity'] === $value) { + return; + } + + $this->data['severity'] = $value; + + $this->setModified('severity'); + } + + /** + * @param $value string + */ + public function setMessage($value) + { + $this->validateNotNull('message', $value); + $this->validateString('message', $value); + + if ($this->data['message'] === $value) { + return; + } + + $this->data['message'] = $value; + + $this->setModified('message'); + } + + /** + * @param $value \DateTime + */ + public function setCreateDate($value) + { + $this->validateNotNull('create_date', $value); + $this->validateDate('create_date', $value); + + if ($this->data['create_date'] === $value) { + return; + } + + $this->data['create_date'] = $value; + + $this->setModified('create_date'); + } + + /** + * Get the Build model for this BuildError by Id. + * + * @return \PHPCensor\Model\Build + */ + public function getBuild() + { + $key = $this->getBuildId(); + + if (empty($key)) { + return null; + } + + $cacheKey = 'Cache.Build.' . $key; + $rtn = $this->cache->get($cacheKey, null); + + if (empty($rtn)) { + $rtn = Factory::getStore('Build', 'PHPCensor')->getById($key); + $this->cache->set($cacheKey, $rtn); + } + + return $rtn; + } + + /** + * Set Build - Accepts an ID, an array representing a Build or a Build model. + * + * @param $value mixed + */ + public function setBuild($value) + { + // Is this an instance of Build? + if ($value instanceof Build) { + return $this->setBuildObject($value); + } + + // Is this an array representing a Build item? + if (is_array($value) && !empty($value['id'])) { + return $this->setBuildId($value['id']); + } + + // Is this a scalar value representing the ID of this foreign key? + return $this->setBuildId($value); + } + + /** + * Set Build - Accepts a Build model. + * + * @param $value Build + */ + public function setBuildObject(Build $value) + { + return $this->setBuildId($value->getId()); + } + + /** + * Get the language string key for this error's severity level. + * + * @return string + */ + public function getSeverityString() + { + switch ($this->getSeverity()) { + case self::SEVERITY_CRITICAL: + return 'critical'; + + case self::SEVERITY_HIGH: + return 'high'; + + case self::SEVERITY_NORMAL: + return 'normal'; + + case self::SEVERITY_LOW: + return 'low'; + } + } + + /** + * Get the language string key for this error's severity level. + * + * @param integer $severity + * + * @return string + */ + public static function getSeverityName($severity) + { + switch ($severity) { + case self::SEVERITY_CRITICAL: + return 'critical'; + + case self::SEVERITY_HIGH: + return 'high'; + + case self::SEVERITY_NORMAL: + return 'normal'; + + case self::SEVERITY_LOW: + return 'low'; + } + } + + /** + * Get the class to apply to HTML elements representing this error. + * + * @return string + */ + public function getSeverityClass() + { + switch ($this->getSeverity()) { + case self::SEVERITY_CRITICAL: + return 'danger'; + + case self::SEVERITY_HIGH: + return 'warning'; + + case self::SEVERITY_NORMAL: + return 'info'; + + case self::SEVERITY_LOW: + return 'default'; + } + } +} diff --git a/src/PHPCensor/Model/BuildMeta.php b/src/PHPCensor/Model/BuildMeta.php new file mode 100644 index 0000000..0efa761 --- /dev/null +++ b/src/PHPCensor/Model/BuildMeta.php @@ -0,0 +1,225 @@ + null, + 'build_id' => null, + 'meta_key' => null, + 'meta_value' => null, + ]; + + /** + * @var array + */ + protected $getters = [ + // Direct property getters: + 'id' => 'getId', + 'build_id' => 'getBuildId', + 'meta_key' => 'getMetaKey', + 'meta_value' => 'getMetaValue', + + // Foreign key getters: + 'Build' => 'getBuild', + ]; + + /** + * @var array + */ + protected $setters = [ + // Direct property setters: + 'id' => 'setId', + 'build_id' => 'setBuildId', + 'meta_key' => 'setMetaKey', + 'meta_value' => 'setMetaValue', + + // Foreign key setters: + 'Build' => 'setBuild', + ]; + + /** + * @return int + */ + public function getId() + { + $rtn = $this->data['id']; + + return $rtn; + } + + /** + * @return int + */ + public function getBuildId() + { + $rtn = $this->data['build_id']; + + return $rtn; + } + + /** + * @return string + */ + public function getMetaKey() + { + $rtn = $this->data['meta_key']; + + return $rtn; + } + + /** + * @return string + */ + public function getMetaValue() + { + $rtn = $this->data['meta_value']; + + return $rtn; + } + + /** + * @param int $value + */ + public function setId($value) + { + $this->validateNotNull('id', $value); + $this->validateInt('id', $value); + + if ($this->data['id'] === $value) { + return; + } + + $this->data['id'] = $value; + + $this->setModified('id'); + } + + /** + * @param int $value + */ + public function setBuildId($value) + { + $this->validateNotNull('build_id', $value); + $this->validateInt('build_id', $value); + + if ($this->data['build_id'] === $value) { + return; + } + + $this->data['build_id'] = $value; + + $this->setModified('build_id'); + } + + /** + * @param $value string + */ + public function setMetaKey($value) + { + $this->validateNotNull('meta_key', $value); + $this->validateString('meta_key', $value); + + if ($this->data['meta_key'] === $value) { + return; + } + + $this->data['meta_key'] = $value; + + $this->setModified('meta_key'); + } + + /** + * @param $value string + */ + public function setMetaValue($value) + { + $this->validateNotNull('meta_value', $value); + $this->validateString('meta_value', $value); + + if ($this->data['meta_value'] === $value) { + return; + } + + $this->data['meta_value'] = $value; + + $this->setModified('meta_value'); + } + + /** + * Get the Build model for this BuildMeta by Id. + * + * @return \PHPCensor\Model\Build + */ + public function getBuild() + { + $key = $this->getBuildId(); + + if (empty($key)) { + return null; + } + + $cacheKey = 'Cache.Build.' . $key; + $rtn = $this->cache->get($cacheKey, null); + + if (empty($rtn)) { + $rtn = Factory::getStore('Build', 'PHPCensor')->getById($key); + $this->cache->set($cacheKey, $rtn); + } + + return $rtn; + } + + /** + * Set Build - Accepts an ID, an array representing a Build or a Build model. + * + * @param $value mixed + */ + public function setBuild($value) + { + // Is this an instance of Build? + if ($value instanceof Build) { + return $this->setBuildObject($value); + } + + // Is this an array representing a Build item? + if (is_array($value) && !empty($value['id'])) { + return $this->setBuildId($value['id']); + } + + // Is this a scalar value representing the ID of this foreign key? + return $this->setBuildId($value); + } + + /** + * Set Build - Accepts a Build model. + * + * @param $value Build + */ + public function setBuildObject(Build $value) + { + return $this->setBuildId($value->getId()); + } +} diff --git a/src/PHPCensor/Model/Environment.php b/src/PHPCensor/Model/Environment.php new file mode 100644 index 0000000..e70da4e --- /dev/null +++ b/src/PHPCensor/Model/Environment.php @@ -0,0 +1,161 @@ + null, + 'project_id' => null, + 'name' => null, + 'branches' => null, + ]; + + /** + * @var array + */ + protected $getters = [ + 'id' => 'getId', + 'project_id' => 'getProjectId', + 'name' => 'getName', + 'branches' => 'getBranches', + ]; + + /** + * @var array + */ + protected $setters = [ + 'id' => 'setId', + 'project_id' => 'setProjectId', + 'name' => 'setName', + 'branches' => 'setBranches', + ]; + + /** + * @return int + */ + public function getId() + { + $rtn = $this->data['id']; + + return $rtn; + } + + /** + * @return int + */ + public function getProjectId() + { + $rtn = $this->data['project_id']; + + return $rtn; + } + + /** + * @return string + */ + public function getName() + { + $rtn = $this->data['name']; + + return $rtn; + } + + /** + * @return array + */ + public function getBranches() + { + $rtn = array_filter(array_map('trim', explode("\n", $this->data['branches']))); + + return $rtn; + } + + /** + * @param $value int + */ + public function setId($value) + { + $this->validateNotNull('id', $value); + $this->validateInt('id', $value); + + if ($this->data['id'] === $value) { + return; + } + + $this->data['id'] = $value; + + $this->setModified('id'); + } + + /** + * @param $value int + */ + public function setProjectId($value) + { + $this->validateNotNull('project_id', $value); + $this->validateInt('project_id', $value); + + if ($this->data['project_id'] === $value) { + return; + } + + $this->data['project_id'] = $value; + + $this->setModified('project_id'); + } + + /** + * @param $value string + */ + public function setName($value) + { + $this->validateNotNull('name', $value); + $this->validateString('name', $value); + + if ($this->data['name'] === $value) { + return; + } + + $this->data['name'] = $value; + + $this->setModified('name'); + } + + /** + * @param $value array + */ + public function setBranches($value) + { + $this->validateNotNull('branches', $value); + $value = implode("\n", $value); + + if ($this->data['branches'] === $value) { + return; + } + + $this->data['branches'] = $value; + + $this->setModified('branches'); + } +} diff --git a/src/PHPCensor/Model/Project.php b/src/PHPCensor/Model/Project.php new file mode 100644 index 0000000..bf461b7 --- /dev/null +++ b/src/PHPCensor/Model/Project.php @@ -0,0 +1,869 @@ + + */ +class Project extends Model +{ + /** + * @var array + */ + public static $sleepable = []; + + /** + * @var string + */ + protected $tableName = 'project'; + + /** + * @var string + */ + protected $modelName = 'Project'; + + /** + * @var array + */ + protected $data = [ + 'id' => null, + 'title' => null, + 'reference' => null, + 'branch' => null, + 'default_branch_only' => null, + 'ssh_private_key' => null, + 'type' => null, + 'access_information' => null, + 'last_commit' => null, + 'build_config' => null, + 'ssh_public_key' => null, + 'allow_public_status' => null, + 'archived' => null, + 'group_id' => null, + 'create_date' => null, + 'user_id' => 0, + ]; + + /** + * @var array + */ + protected $getters = [ + // Direct property getters: + 'id' => 'getId', + 'title' => 'getTitle', + 'reference' => 'getReference', + 'branch' => 'getBranch', + 'default_branch_only' => 'getDefaultBranchOnly', + 'ssh_private_key' => 'getSshPrivateKey', + 'type' => 'getType', + 'access_information' => 'getAccessInformation', + 'last_commit' => 'getLastCommit', + 'build_config' => 'getBuildConfig', + 'ssh_public_key' => 'getSshPublicKey', + 'allow_public_status' => 'getAllowPublicStatus', + 'archived' => 'getArchived', + 'group_id' => 'getGroupId', + 'create_date' => 'getCreateDate', + 'user_id' => 'getUserId', + + // Foreign key getters: + 'Group' => 'getGroup', + ]; + + /** + * @var array + */ + protected $setters = [ + // Direct property setters: + 'id' => 'setId', + 'title' => 'setTitle', + 'reference' => 'setReference', + 'branch' => 'setBranch', + 'default_branch_only' => 'setDefaultBranchOnly', + 'ssh_private_key' => 'setSshPrivateKey', + 'type' => 'setType', + 'access_information' => 'setAccessInformation', + 'last_commit' => 'setLastCommit', + 'build_config' => 'setBuildConfig', + 'ssh_public_key' => 'setSshPublicKey', + 'allow_public_status' => 'setAllowPublicStatus', + 'archived' => 'setArchived', + 'group_id' => 'setGroupId', + 'create_date' => 'setCreateDate', + 'user_id' => 'setUserId', + + // Foreign key setters: + 'Group' => 'setGroup', + ]; + + /** + * @return int + */ + public function getId() + { + $rtn = $this->data['id']; + + return $rtn; + } + + /** + * @return string + */ + public function getTitle() + { + $rtn = $this->data['title']; + + return $rtn; + } + + /** + * @return string + */ + public function getReference() + { + $rtn = $this->data['reference']; + + return $rtn; + } + + /** + * @return string + */ + public function getSshPrivateKey() + { + $rtn = $this->data['ssh_private_key']; + + return $rtn; + } + + /** + * @return string + */ + public function getType() + { + $rtn = $this->data['type']; + + return $rtn; + } + + /** + * @return string + */ + public function getLastCommit() + { + $rtn = $this->data['last_commit']; + + return $rtn; + } + + /** + * @return string + */ + public function getBuildConfig() + { + $rtn = $this->data['build_config']; + + return $rtn; + } + + /** + * @return string + */ + public function getSshPublicKey() + { + $rtn = $this->data['ssh_public_key']; + + return $rtn; + } + + /** + * @return int + */ + public function getAllowPublicStatus() + { + $rtn = $this->data['allow_public_status']; + + return $rtn; + } + + /** + * @return int + */ + public function getArchived() + { + $rtn = $this->data['archived']; + + return $rtn; + } + + /** + * @return int + */ + public function getGroupId() + { + $rtn = $this->data['group_id']; + + return $rtn; + } + + /** + * @return int + */ + public function getDefaultBranchOnly() + { + $rtn = $this->data['default_branch_only']; + + return $rtn; + } + + /** + * @param $value int + */ + public function setId($value) + { + $this->validateNotNull('id', $value); + $this->validateInt('id', $value); + + if ($this->data['id'] === $value) { + return; + } + + $this->data['id'] = $value; + + $this->setModified('id'); + } + + /** + * @param $value string + */ + public function setTitle($value) + { + $this->validateNotNull('title', $value); + $this->validateString('title', $value); + + if ($this->data['title'] === $value) { + return; + } + + $this->data['title'] = $value; + + $this->setModified('title'); + } + + /** + * @param $value string + */ + public function setReference($value) + { + $this->validateNotNull('reference', $value); + $this->validateString('reference', $value); + + if ($this->data['reference'] === $value) { + return; + } + + $this->data['reference'] = $value; + + $this->setModified('reference'); + } + + /** + * @param $value string + */ + public function setBranch($value) + { + $this->validateNotNull('branch', $value); + $this->validateString('branch', $value); + + if ($this->data['branch'] === $value) { + return; + } + + $this->data['branch'] = $value; + + $this->setModified('branch'); + } + + /** + * @param $value int + */ + public function setDefaultBranchOnly($value) + { + $this->validateNotNull('default_branch_only', $value); + $this->validateInt('default_branch_only', $value); + + if ($this->data['default_branch_only'] === $value) { + return; + } + + $this->data['default_branch_only'] = $value; + + $this->setModified('default_branch_only'); + } + + /** + * @param $value string + */ + public function setSshPrivateKey($value) + { + $this->validateString('ssh_private_key', $value); + + if ($this->data['ssh_private_key'] === $value) { + return; + } + + $this->data['ssh_private_key'] = $value; + + $this->setModified('ssh_private_key'); + } + + /** + * @param $value string + */ + public function setType($value) + { + $this->validateNotNull('type', $value); + $this->validateString('type', $value); + + if ($this->data['type'] === $value) { + return; + } + + $this->data['type'] = $value; + + $this->setModified('type'); + } + + /** + * @param $value string + */ + public function setLastCommit($value) + { + $this->validateString('last_commit', $value); + + if ($this->data['last_commit'] === $value) { + return; + } + + $this->data['last_commit'] = $value; + + $this->setModified('last_commit'); + } + + /** + * @param $value string + */ + public function setBuildConfig($value) + { + $this->validateString('build_config', $value); + + if ($this->data['build_config'] === $value) { + return; + } + + $this->data['build_config'] = $value; + + $this->setModified('build_config'); + } + + /** + * @param $value string + */ + public function setSshPublicKey($value) + { + $this->validateString('ssh_public_key', $value); + + if ($this->data['ssh_public_key'] === $value) { + return; + } + + $this->data['ssh_public_key'] = $value; + + $this->setModified('ssh_public_key'); + } + + /** + * @param $value int + */ + public function setAllowPublicStatus($value) + { + $this->validateNotNull('allow_public_status', $value); + $this->validateInt('allow_public_status', $value); + + if ($this->data['allow_public_status'] === $value) { + return; + } + + $this->data['allow_public_status'] = $value; + + $this->setModified('allow_public_status'); + } + + /** + * @param $value int + */ + public function setArchived($value) + { + $this->validateNotNull('archived', $value); + $this->validateInt('archived', $value); + + if ($this->data['archived'] === $value) { + return; + } + + $this->data['archived'] = $value; + + $this->setModified('archived'); + } + + /** + * @param $value int + */ + public function setGroupId($value) + { + $this->validateNotNull('group_id', $value); + $this->validateInt('group_id', $value); + + if ($this->data['group_id'] === $value) { + return; + } + + $this->data['group_id'] = $value; + + $this->setModified('group_id'); + } + + /** + * Get the ProjectGroup model for this Project by Id. + * + * @return \PHPCensor\Model\ProjectGroup + */ + public function getGroup() + { + $key = $this->getGroupId(); + + if (empty($key)) { + return null; + } + + $cacheKey = 'Cache.ProjectGroup.' . $key; + $rtn = $this->cache->get($cacheKey, null); + + if (empty($rtn)) { + $rtn = Factory::getStore('ProjectGroup', 'PHPCensor')->getById($key); + $this->cache->set($cacheKey, $rtn); + } + + return $rtn; + } + + /** + * Set Group - Accepts an ID, an array representing a ProjectGroup or a ProjectGroup model. + * + * @param $value mixed + */ + public function setGroup($value) + { + // Is this an instance of ProjectGroup? + if ($value instanceof ProjectGroup) { + return $this->setGroupObject($value); + } + + // Is this an array representing a ProjectGroup item? + if (is_array($value) && !empty($value['id'])) { + return $this->setGroupId($value['id']); + } + + // Is this a scalar value representing the ID of this foreign key? + return $this->setGroupId($value); + } + + /** + * Set Group - Accepts a ProjectGroup model. + * + * @param $value ProjectGroup + */ + public function setGroupObject(ProjectGroup $value) + { + return $this->setGroupId($value->getId()); + } + + /** + * Get Build models by ProjectId for this Project. + * + * @return \PHPCensor\Model\Build[] + */ + public function getProjectBuilds() + { + return Factory::getStore('Build', 'PHPCensor')->getByProjectId($this->getId()); + } + + /** + * Return the latest build from a specific branch, of a specific status, for this project. + * + * @param string $branch + * @param null $status + * + * @return mixed|null + */ + public function getLatestBuild($branch = 'master', $status = null) + { + $criteria = ['branch' => $branch, 'project_id' => $this->getId()]; + + if (isset($status)) { + $criteria['status'] = $status; + } + + $order = ['id' => 'DESC']; + $builds = Store\Factory::getStore('Build')->getWhere($criteria, 1, 0, [], $order); + + if (is_array($builds['items']) && count($builds['items'])) { + $latest = array_shift($builds['items']); + + if (isset($latest) && $latest instanceof Build) { + return $latest; + } + } + + return null; + } + + /** + * Return the previous build from a specific branch, for this project. + * + * @param string $branch + * + * @return mixed|null + */ + public function getPreviousBuild($branch = 'master') + { + $criteria = ['branch' => $branch, 'project_id' => $this->getId()]; + $order = ['id' => 'DESC']; + $builds = Store\Factory::getStore('Build')->getWhere($criteria, 1, 1, [], $order); + + if (is_array($builds['items']) && count($builds['items'])) { + $previous = array_shift($builds['items']); + + if (isset($previous) && $previous instanceof Build) { + return $previous; + } + } + + return null; + } + + /** + * @param string|array $value + */ + public function setAccessInformation($value) + { + if (is_array($value)) { + $value = json_encode($value); + } + + $this->validateString('access_information', $value); + + if ($this->data['access_information'] === $value) { + return; + } + + $this->data['access_information'] = $value; + + $this->setModified('access_information'); + } + + /** + * Get this project's access_information data. Pass a specific key or null for all data. + * + * @param string|null $key + * + * @return mixed|null|string + */ + public function getAccessInformation($key = null) + { + $info = $this->data['access_information']; + + // Handle old-format (serialized) access information first: + if (!empty($info) && !in_array(substr($info, 0, 1), ['{', '['])) { + $data = unserialize($info); + } else { + $data = json_decode($info, true); + } + + if (is_null($key)) { + $rtn = $data; + } elseif (isset($data[$key])) { + $rtn = $data[$key]; + } else { + $rtn = null; + } + + return $rtn; + } + + /** + * @return \DateTime + */ + public function getCreateDate() + { + $rtn = $this->data['create_date']; + + if (!empty($rtn)) { + $rtn = new \DateTime($rtn); + } + + return $rtn; + } + + /** + * @param $value \DateTime + */ + public function setCreateDate($value) + { + $this->validateDate('create_date', $value); + + if ($this->data['create_date'] === $value) { + return; + } + + $this->data['create_date'] = $value; + + $this->setModified('create_date'); + } + + /** + * @return string + */ + public function getUserId() + { + $rtn = $this->data['user_id']; + + return (integer)$rtn; + } + + /** + * @param $value integer + */ + public function setUserId($value) + { + $this->validateNotNull('user_id', $value); + $this->validateInt('user_id', $value); + + if ($this->data['user_id'] === $value) { + return; + } + + $this->data['user_id'] = $value; + + $this->setModified('user_id'); + } + + /** + * Get the value of branch. + * + * @return string + */ + public function getBranch() + { + if (empty($this->data['branch'])) { + $projectType = $this->getType(); + switch ($projectType) { + case 'hg': + $branch = 'default'; + break; + case 'svn': + $branch = 'trunk'; + break; + default: + $branch = 'master'; + } + + return $branch; + } else { + return $this->data['branch']; + } + } + + /** + * Return the name of a FontAwesome icon to represent this project, depending on its type. + * + * @return string + */ + public function getIcon() + { + switch ($this->getType()) { + case 'github': + $icon = 'github'; + break; + + case 'bitbucket': + case 'bitbuckethg': + $icon = 'bitbucket'; + break; + + case 'remote': + case 'gitlab': + default: + $icon = 'code-fork'; + break; + } + + return $icon; + } + + /** + * @return EnvironmentStore + */ + protected function getEnvironmentStore() + { + /** @var EnvironmentStore $store */ + $store = Factory::getStore('Environment', 'PHPCensor'); + return $store; + } + + /** + * Get Environments + * + * @return array contain items with \PHPCensor\Model\Environment + */ + public function getEnvironmentsObjects() + { + $key = $this->getId(); + + if (empty($key)) { + return null; + } + + $cacheKey = 'Cache.ProjectEnvironments.' . $key; + $rtn = $this->cache->get($cacheKey, null); + + if (empty($rtn)) { + $store = $this->getEnvironmentStore(); + $rtn = $store->getByProjectId($key); + $this->cache->set($cacheKey, $rtn); + } + + return $rtn; + } + + /** + * Get Environments + * + * @return string[] + */ + public function getEnvironmentsNames() + { + $environments = $this->getEnvironmentsObjects(); + $environments_names = []; + foreach($environments['items'] as $environment) { + /** @var Environment $environment */ + $environments_names[] = $environment->getName(); + } + + return $environments_names; + } + + /** + * Get Environments + * + * @return string yaml + */ + public function getEnvironments() + { + $environments = $this->getEnvironmentsObjects(); + $environments_config = []; + foreach($environments['items'] as $environment) { + /** @var Environment $environment */ + $environments_config[$environment->getName()] = $environment->getBranches(); + } + + $yaml_dumper = new YamlDumper(); + $value = $yaml_dumper->dump($environments_config, 10, 0, true, false); + + return $value; + } + + /** + * Set Environments + * + * @param string $value yaml + */ + public function setEnvironments($value) + { + $yaml_parser = new YamlParser(); + $environments_config = $yaml_parser->parse($value); + $environments_names = !empty($environments_config) ? array_keys($environments_config) : []; + $current_environments = $this->getEnvironmentsObjects(); + $store = $this->getEnvironmentStore(); + foreach ($current_environments['items'] as $environment) { + /** @var Environment $environment */ + $key = array_search($environment->getName(), $environments_names); + if ($key !== false) { + // already exist + unset($environments_names[$key]); + $environment->setBranches(!empty($environments_config[$environment->getName()]) ? $environments_config[$environment->getName()] : []); + $store->save($environment); + } else { + // remove + $store->delete($environment); + } + } + + if (!empty($environments_names)) { + // add + foreach ($environments_names as $environment_name) { + $environment = new Environment(); + $environment->setProjectId($this->getId()); + $environment->setName($environment_name); + $environment->setBranches(!empty($environments_config[$environment->getName()]) ? $environments_config[$environment->getName()] : []); + $store->save($environment); + } + } + } + + /** + * @param string $branch + * + * @return string[] + */ + public function getEnvironmentsNamesByBranch($branch) + { + $environments_names = []; + $environments = $this->getEnvironmentsObjects(); + $default_branch = ($branch == $this->getBranch()); + foreach($environments['items'] as $environment) { + /** @var Environment $environment */ + if ($default_branch || in_array($branch, $environment->getBranches())) { + $environments_names[] = $environment->getName(); + } + } + + return $environments_names; + } + + /** + * @param string $environment_name + * + * @return string[] + */ + public function getBranchesByEnvironment($environment_name) + { + $branches = []; + $environments = $this->getEnvironmentsObjects(); + foreach($environments['items'] as $environment) { + /** @var Environment $environment */ + if ($environment_name == $environment->getName()) { + return $environment->getBranches(); + } + } + + return $branches; + } +} diff --git a/src/PHPCensor/Model/ProjectGroup.php b/src/PHPCensor/Model/ProjectGroup.php new file mode 100644 index 0000000..6882438 --- /dev/null +++ b/src/PHPCensor/Model/ProjectGroup.php @@ -0,0 +1,175 @@ + null, + 'title' => null, + 'create_date' => null, + 'user_id' => 0, + ]; + + /** + * @var array + */ + protected $getters = [ + 'id' => 'getId', + 'title' => 'getTitle', + 'create_date' => 'getCreateDate', + 'user_id' => 'getUserId', + ]; + + /** + * @var array + */ + protected $setters = [ + 'id' => 'setId', + 'title' => 'setTitle', + 'create_date' => 'setCreateDate', + 'user_id' => 'setUserId', + ]; + + /** + * @return int + */ + public function getId() + { + $rtn = $this->data['id']; + + return $rtn; + } + + /** + * @param $value int + */ + public function setId($value) + { + $this->validateNotNull('id', $value); + $this->validateInt('id', $value); + + if ($this->data['id'] === $value) { + return; + } + + $this->data['id'] = $value; + + $this->setModified('id'); + } + + /** + * @return string + */ + public function getTitle() + { + $rtn = $this->data['title']; + + return $rtn; + } + + /** + * @param $value string + */ + public function setTitle($value) + { + $this->validateNotNull('title', $value); + $this->validateString('title', $value); + + if ($this->data['title'] === $value) { + return; + } + + $this->data['title'] = $value; + + $this->setModified('title'); + } + + /** + * @return \DateTime + */ + public function getCreateDate() + { + $rtn = $this->data['create_date']; + + if (!empty($rtn)) { + $rtn = new \DateTime($rtn); + } + + return $rtn; + } + + /** + * @param $value \DateTime + */ + public function setCreateDate($value) + { + $this->validateDate('create_date', $value); + + if ($this->data['create_date'] === $value) { + return; + } + + $this->data['create_date'] = $value; + + $this->setModified('create_date'); + } + + /** + * @return string + */ + public function getUserId() + { + $rtn = $this->data['user_id']; + + return (integer)$rtn; + } + + /** + * @param $value integer + */ + public function setUserId($value) + { + $this->validateNotNull('user_id', $value); + $this->validateInt('user_id', $value); + + if ($this->data['user_id'] === $value) { + return; + } + + $this->data['user_id'] = $value; + + $this->setModified('user_id'); + } + + /** + * Get Project models by GroupId for this ProjectGroup. + * + * @return \PHPCensor\Model\Project[] + */ + public function getGroupProjects() + { + return Factory::getStore('Project', 'PHPCensor')->getByGroupId($this->getId(), false); + } +} diff --git a/src/PHPCensor/Model/User.php b/src/PHPCensor/Model/User.php new file mode 100644 index 0000000..d49c747 --- /dev/null +++ b/src/PHPCensor/Model/User.php @@ -0,0 +1,350 @@ + + */ +class User extends Model +{ + /** + * @var array + */ + public static $sleepable = []; + + /** + * @var string + */ + protected $tableName = 'user'; + + /** + * @var string + */ + protected $modelName = 'User'; + + /** + * @var array + */ + protected $data = [ + 'id' => null, + 'email' => null, + 'hash' => null, + 'is_admin' => null, + 'name' => null, + 'language' => null, + 'per_page' => null, + 'provider_key' => null, + 'provider_data' => null, + 'remember_key' => null, + ]; + + /** + * @var array + */ + protected $getters = [ + 'id' => 'getId', + 'email' => 'getEmail', + 'hash' => 'getHash', + 'is_admin' => 'getIsAdmin', + 'name' => 'getName', + 'language' => 'getLanguage', + 'per_page' => 'getPerPage', + 'provider_key' => 'getProviderKey', + 'provider_data' => 'getProviderData', + 'remember_key' => 'getRememberKey', + ]; + + /** + * @var array + */ + protected $setters = [ + 'id' => 'setId', + 'email' => 'setEmail', + 'hash' => 'setHash', + 'is_admin' => 'setIsAdmin', + 'name' => 'setName', + 'language' => 'setLanguage', + 'per_page' => 'setPerPage', + 'provider_key' => 'setProviderKey', + 'provider_data' => 'setProviderData', + 'remember_key' => 'setRememberKey', + ]; + + /** + * @return int + */ + public function getId() + { + $rtn = $this->data['id']; + + return $rtn; + } + + /** + * @return string + */ + public function getEmail() + { + $rtn = $this->data['email']; + + return $rtn; + } + + /** + * @return string + */ + public function getHash() + { + $rtn = $this->data['hash']; + + return $rtn; + } + + /** + * @return string + */ + public function getName() + { + $rtn = $this->data['name']; + + return $rtn; + } + + /** + * @return int + */ + public function getIsAdmin() + { + $rtn = $this->data['is_admin']; + + return $rtn; + } + + /** + * @return string + */ + public function getProviderKey() + { + $rtn = $this->data['provider_key']; + + return $rtn; + } + + /** + * @return string + */ + public function getProviderData() + { + $rtn = $this->data['provider_data']; + + return $rtn; + } + + /** + * @return string + */ + public function getRememberKey() + { + $rtn = $this->data['remember_key']; + + return $rtn; + } + + /** + * @return string + */ + public function getLanguage() + { + $rtn = $this->data['language']; + + return $rtn; + } + + /** + * @return string + */ + public function getPerPage() + { + $rtn = $this->data['per_page']; + + return $rtn; + } + + /** + * @param $value int + */ + public function setId($value) + { + $this->validateNotNull('id', $value); + $this->validateInt('id', $value); + + if ($this->data['id'] === $value) { + return; + } + + $this->data['id'] = $value; + + $this->setModified('id'); + } + + /** + * @param $value string + */ + public function setEmail($value) + { + $this->validateNotNull('email', $value); + $this->validateString('email', $value); + + if ($this->data['email'] === $value) { + return; + } + + $this->data['email'] = $value; + + $this->setModified('email'); + } + + /** + * @param $value string + */ + public function setHash($value) + { + $this->validateNotNull('hash', $value); + $this->validateString('hash', $value); + + if ($this->data['hash'] === $value) { + return; + } + + $this->data['hash'] = $value; + + $this->setModified('hash'); + } + + /** + * @param $value string + */ + public function setName($value) + { + $this->validateNotNull('name', $value); + $this->validateString('name', $value); + + if ($this->data['name'] === $value) { + return; + } + + $this->data['name'] = $value; + + $this->setModified('name'); + } + + /** + * @param $value int + */ + public function setIsAdmin($value) + { + $this->validateNotNull('is_admin', $value); + $this->validateInt('is_admin', $value); + + if ($this->data['is_admin'] === $value) { + return; + } + + $this->data['is_admin'] = $value; + + $this->setModified('is_admin'); + } + + /** + * @param $value string + */ + public function setProviderKey($value) + { + $this->validateNotNull('provider_key', $value); + $this->validateString('provider_key', $value); + + if ($this->data['provider_key'] === $value) { + return; + } + + $this->data['provider_key'] = $value; + + $this->setModified('provider_key'); + } + + /** + * @param $value string + */ + public function setProviderData($value) + { + $this->validateString('provider_data', $value); + + if ($this->data['provider_data'] === $value) { + return; + } + + $this->data['provider_data'] = $value; + + $this->setModified('provider_data'); + } + + /** + * @param $value string + */ + public function setRememberKey($value) + { + $this->validateString('remember_key', $value); + + if ($this->data['remember_key'] === $value) { + return; + } + + $this->data['remember_key'] = $value; + + $this->setModified('remember_key'); + } + + /** + * @param $value string + */ + public function setLanguage($value) + { + if ($this->data['language'] === $value) { + return; + } + + $this->data['language'] = $value; + + $this->setModified('language'); + } + + /** + * @param $value string + */ + public function setPerPage($value) + { + if ($this->data['per_page'] === $value) { + return; + } + + $this->data['per_page'] = $value; + + $this->setModified('per_page'); + } + + /** + * @return integer + */ + public function getFinalPerPage() + { + $perPage = $this->getPerPage(); + if ($perPage) { + return (integer)$perPage; + } + + return (integer)Config::getInstance()->get('php-censor.per_page', 10); + } +} diff --git a/src/PHPCensor/Plugin.php b/src/PHPCensor/Plugin.php new file mode 100644 index 0000000..0bb653b --- /dev/null +++ b/src/PHPCensor/Plugin.php @@ -0,0 +1,98 @@ + + */ +abstract class Plugin +{ + const STATUS_PENDING = 0; + const STATUS_RUNNING = 1; + const STATUS_SUCCESS = 2; + const STATUS_FAILED = 3; + const STATUS_FAILED_ALLOWED = 4; + /** + * @var \PHPCensor\Builder + */ + protected $builder; + + /** + * @var \PHPCensor\Model\Build + */ + protected $build; + + /** + * @var array + */ + protected $options; + + /** + * @var string + */ + protected $priorityPath = 'local'; + + /** + * @param Builder $builder + * @param Build $build + * @param array $options + */ + public function __construct(Builder $builder, Build $build, array $options = []) + { + $this->builder = $builder; + $this->build = $build; + $this->options = $options; + + if (!empty($options['priority_path']) && in_array($options['priority_path'], ['global', 'system'])) { + $this->priorityPath = $options['priority_path']; + } + + $this->builder->logDebug('Plugin options: ' . json_encode($options)); + } + + /** + * Find a binary required by a plugin. + * + * @param string $binary + * @param boolean $quiet Returns null instead of throwing an exception. + * + * @return null|string + * + * @throws \Exception when no binary has been found and $quiet is false. + */ + public function findBinary($binary, $quiet = false) + { + return $this->builder->findBinary($binary, $quiet, $this->priorityPath); + } + + /** + * @return Build + */ + public function getBuild() + { + return $this->build; + } + + /** + * @return Builder + */ + public function getBuilder() + { + return $this->builder; + } + + /** + * @return boolean + */ + abstract public function execute(); + + /** + * @return string + */ + public static function pluginName() + { + return ''; + } +} diff --git a/src/PHPCensor/Plugin/Atoum.php b/src/PHPCensor/Plugin/Atoum.php new file mode 100644 index 0000000..f62424b --- /dev/null +++ b/src/PHPCensor/Plugin/Atoum.php @@ -0,0 +1,109 @@ +executable = $this->builder->buildPath . DIRECTORY_SEPARATOR.$options['executable']; + } else { + $this->executable = $this->findBinary('atoum'); + } + + if (isset($options['args'])) { + $this->args = $options['args']; + } + + if (isset($options['config'])) { + $this->config = $options['config']; + } + + if (isset($options['directory'])) { + $this->directory = $options['directory']; + } + } + + /** + * Run the Atoum plugin. + * + * @return bool + */ + public function execute() + { + $cmd = $this->executable; + + if ($this->args !== null) { + $cmd .= " {$this->args}"; + } + + if ($this->config !== null) { + $cmd .= " -c '{$this->config}'"; + } + + if ($this->directory !== null) { + $dirPath = $this->builder->buildPath . DIRECTORY_SEPARATOR . $this->directory; + $cmd .= " -d '{$dirPath}'"; + } + + chdir($this->builder->buildPath); + + $output = ''; + $status = true; + + exec($cmd, $output); + + if (count(preg_grep("/Success \(/", $output)) == 0) { + $status = false; + $this->builder->log($output); + } + + if (count($output) == 0) { + $status = false; + $this->builder->log('No tests have been performed.'); + } + + return $status; + } +} diff --git a/src/PHPCensor/Plugin/Behat.php b/src/PHPCensor/Plugin/Behat.php new file mode 100644 index 0000000..c47be48 --- /dev/null +++ b/src/PHPCensor/Plugin/Behat.php @@ -0,0 +1,128 @@ + + */ +class Behat extends Plugin +{ + protected $features; + protected $executable; + + /** + * @return string + */ + public static function pluginName() + { + return 'behat'; + } + + /** + * {@inheritdoc} + */ + public function __construct(Builder $builder, Build $build, array $options = []) + { + parent::__construct($builder, $build, $options); + + $this->features = ''; + + if (isset($options['executable'])) { + $this->executable = $options['executable']; + } else { + $this->executable = $this->findBinary('behat'); + } + + if (!empty($options['features'])) { + $this->features = $options['features']; + } + } + + /** + * Runs Behat tests. + */ + public function execute() + { + $current_dir = getcwd(); + chdir($this->builder->buildPath); + + $behat = $this->executable; + + if (!$behat) { + $this->builder->logFailure(sprintf('Could not find %s', 'behat')); + + return false; + } + + $success = $this->builder->executeCommand($behat . ' %s', $this->features); + chdir($current_dir); + + list($errorCount, $data) = $this->parseBehatOutput(); + + $this->build->storeMeta('behat-warnings', $errorCount); + $this->build->storeMeta('behat-data', $data); + + return $success; + } + + /** + * Parse the behat output and return details on failures + * + * @return array + */ + public function parseBehatOutput() + { + $output = $this->builder->getLastOutput(); + + $parts = explode('---', $output); + + if (count($parts) <= 1) { + return [0, []]; + } + + $lines = explode(PHP_EOL, $parts[1]); + + $storeFailures = false; + $data = []; + + foreach ($lines as $line) { + $line = trim($line); + if ($line == 'Failed scenarios:') { + $storeFailures = true; + continue; + } + + if (strpos($line, ':') === false) { + $storeFailures = false; + } + + if ($storeFailures) { + $lineParts = explode(':', $line); + $data[] = [ + 'file' => $lineParts[0], + 'line' => $lineParts[1] + ]; + + $this->build->reportError( + $this->builder, + 'behat', + 'Behat scenario failed.', + BuildError::SEVERITY_HIGH, + $lineParts[0], + $lineParts[1] + ); + } + } + + $errorCount = count($data); + + return [$errorCount, $data]; + } +} diff --git a/src/PHPCensor/Plugin/Campfire.php b/src/PHPCensor/Plugin/Campfire.php new file mode 100644 index 0000000..d17d110 --- /dev/null +++ b/src/PHPCensor/Plugin/Campfire.php @@ -0,0 +1,151 @@ + + */ +class Campfire extends Plugin +{ + protected $url; + protected $authToken; + protected $userAgent; + protected $cookie; + protected $verbose; + protected $roomId; + protected $message; + + /** + * @return string + */ + public static function pluginName() + { + return 'campfire'; + } + + /** + * {@inheritdoc} + */ + public function __construct(Builder $builder, Build $build, array $options = []) + { + parent::__construct($builder, $build, $options); + + $this->message = $options['message']; + $this->userAgent = "PHP Censor/1.0"; + $this->cookie = "php-censor-cookie"; + + $buildSettings = $this->builder->getConfig('build_settings'); + + if (isset($buildSettings['campfire'])) { + $campfire = $buildSettings['campfire']; + $this->url = $campfire['url']; + $this->authToken = $campfire['authToken']; + $this->roomId = $campfire['roomId']; + } else { + throw new \Exception('No connection parameters given for Campfire plugin'); + } + } + + /** + * Run the Campfire plugin. + * @return bool|mixed + */ + public function execute() + { + $url = APP_URL . "build/view/" . $this->build->getId(); + $message = str_replace("%buildurl%", $url, $this->message); + $this->joinRoom($this->roomId); + $status = $this->speak($message, $this->roomId); + $this->leaveRoom($this->roomId); + + return $status; + + } + + /** + * Join a Campfire room. + * @param $roomId + */ + public function joinRoom($roomId) + { + $this->getPageByPost('/room/'.$roomId.'/join.json'); + } + + /** + * Leave a Campfire room. + * @param $roomId + */ + public function leaveRoom($roomId) + { + $this->getPageByPost('/room/'.$roomId.'/leave.json'); + } + + /** + * Send a message to a campfire room. + * @param $message + * @param $roomId + * @param bool $isPaste + * @return bool|mixed + */ + public function speak($message, $roomId, $isPaste = false) + { + $page = '/room/'.$roomId.'/speak.json'; + + if ($isPaste) { + $type = 'PasteMessage'; + } else { + $type = 'TextMessage'; + } + + return $this->getPageByPost($page, ['message' => ['type' => $type, 'body' => $message]]); + + } + + /** + * Make a request to Campfire. + * @param $page + * @param null $data + * @return bool|mixed + */ + private function getPageByPost($page, $data = null) + { + $url = $this->url . $page; + // The new API allows JSON, so we can pass + // PHP data structures instead of old school POST + $json = json_encode($data); + + // cURL init & config + $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, ["Content-type: application/json"]); + curl_setopt($handle, CURLOPT_COOKIEFILE, $this->cookie); + + curl_setopt($handle, CURLOPT_POSTFIELDS, $json); + $output = curl_exec($handle); + + curl_close($handle); + + // We tend to get one space with an otherwise blank response + $output = trim($output); + + if (strlen($output)) { + /* Responses are JSON. Decode it to a data structure */ + return json_decode($output); + } + + // Simple 200 OK response (such as for joining a room) + return true; + } +} diff --git a/src/PHPCensor/Plugin/CleanBuild.php b/src/PHPCensor/Plugin/CleanBuild.php new file mode 100644 index 0000000..01f05e0 --- /dev/null +++ b/src/PHPCensor/Plugin/CleanBuild.php @@ -0,0 +1,59 @@ + + */ +class CleanBuild extends Plugin +{ + protected $remove; + + /** + * @return string + */ + public static function pluginName() + { + return 'clean_build'; + } + + /** + * {@inheritdoc} + */ + public function __construct(Builder $builder, Build $build, array $options = []) + { + parent::__construct($builder, $build, $options); + + $this->remove = isset($options['remove']) && is_array($options['remove']) ? $options['remove'] : []; + } + + /** + * Executes Composer and runs a specified command (e.g. install / update) + */ + public function execute() + { + $cmd = 'rm -Rf "%s"'; + + $this->builder->executeCommand($cmd, $this->builder->buildPath . 'composer.phar'); + $this->builder->executeCommand($cmd, $this->builder->buildPath . 'composer.lock'); + + $success = true; + + foreach ($this->remove as $file) { + $ok = $this->builder->executeCommand($cmd, $this->builder->buildPath . $file); + + if (!$ok) { + $success = false; + } + } + + return $success; + } +} diff --git a/src/PHPCensor/Plugin/Codeception.php b/src/PHPCensor/Plugin/Codeception.php new file mode 100644 index 0000000..d5e8e1d --- /dev/null +++ b/src/PHPCensor/Plugin/Codeception.php @@ -0,0 +1,164 @@ + + * @author Igor Timoshenko + * @author Adam Cooper + */ +class Codeception extends Plugin implements ZeroConfigPluginInterface +{ + /** @var string */ + protected $args = ''; + + /** + * @var string $ymlConfigFile The path of a yml config for Codeception + */ + protected $ymlConfigFile; + + /** + * @var array $path The path to the codeception tests folder. + */ + protected $path = [ + 'tests/_output', + 'tests/_log', + ]; + + /** + * @return string + */ + public static function pluginName() + { + return 'codeception'; + } + + /** + * {@inheritdoc} + */ + public function __construct(Builder $builder, Build $build, array $options = []) + { + parent::__construct($builder, $build, $options); + + if (empty($options['config'])) { + $this->ymlConfigFile = self::findConfigFile($this->builder->buildPath); + } else { + $this->ymlConfigFile = $options['config']; + } + + if (isset($options['args'])) { + $this->args = (string) $options['args']; + } + + if (isset($options['path'])) { + array_unshift($this->path, $options['path']); + } + } + + /** + * @param $stage + * @param Builder $builder + * @param Build $build + * @return bool + */ + public static function canExecute($stage, Builder $builder, Build $build) + { + return $stage == Build::STAGE_TEST && !is_null(self::findConfigFile($builder->buildPath)); + } + + /** + * 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; + } + + /** + * Runs Codeception tests + */ + public function execute() + { + if (empty($this->ymlConfigFile)) { + throw new \Exception("No configuration file found"); + } + + // Run any config files first. This can be either a single value or an array. + return $this->runConfigFile($this->ymlConfigFile); + } + + /** + * Run tests from a Codeception config file. + * @param $configPath + * @return bool|mixed + * @throws \Exception + */ + protected function runConfigFile($configPath) + { + $codeception = $this->findBinary('codecept'); + + if (!$codeception) { + $this->builder->logFailure(sprintf('Could not find %s', 'codecept')); + + return false; + } + + $cmd = 'cd "%s" && ' . $codeception . ' run -c "%s" --xml ' . $this->args; + + $configPath = $this->builder->buildPath . $configPath; + $success = $this->builder->executeCommand($cmd, $this->builder->buildPath, $configPath); + + $parser = new YamlParser(); + $yaml = file_get_contents($configPath); + $config = (array)$parser->parse($yaml); + + $outputPath = null; + if ($config && isset($config['paths']['log'])) { + $outputPath = $this->builder->buildPath . $config['paths']['log'] . '/'; + } + + if (!file_exists($outputPath . 'report.xml')) { + foreach ($this->path as $path) { + $outputPath = $this->builder->buildPath . rtrim($path, '/\\') . '/'; + if (file_exists($outputPath . 'report.xml')) { + break; + } + } + } + + $xml = file_get_contents($outputPath . 'report.xml', false); + $parser = new Parser($this->builder, $xml); + $output = $parser->parse(); + + $meta = [ + '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()); + + return $success; + } +} diff --git a/src/PHPCensor/Plugin/Composer.php b/src/PHPCensor/Plugin/Composer.php new file mode 100644 index 0000000..56062b0 --- /dev/null +++ b/src/PHPCensor/Plugin/Composer.php @@ -0,0 +1,124 @@ + + */ +class Composer extends Plugin implements ZeroConfigPluginInterface +{ + protected $directory; + protected $action; + protected $preferDist; + protected $nodev; + protected $ignorePlatformReqs; + protected $preferSource; + + /** + * @return string + */ + public static function pluginName() + { + return 'composer'; + } + + /** + * {@inheritdoc} + */ + public function __construct(Builder $builder, Build $build, array $options = []) + { + parent::__construct($builder, $build, $options); + + $path = $this->builder->buildPath; + $this->directory = $path; + $this->action = 'install'; + $this->preferDist = false; + $this->preferSource = false; + $this->nodev = false; + $this->ignorePlatformReqs = false; + + if (array_key_exists('directory', $options)) { + $this->directory = $path . DIRECTORY_SEPARATOR . $options['directory']; + } + + if (array_key_exists('action', $options)) { + $this->action = $options['action']; + } + + if (array_key_exists('prefer_dist', $options)) { + $this->preferDist = (bool)$options['prefer_dist']; + } + + if (array_key_exists('prefer_source', $options)) { + $this->preferDist = false; + $this->preferSource = (bool)$options['prefer_source']; + } + + if (array_key_exists('no_dev', $options)) { + $this->nodev = (bool)$options['no_dev']; + } + + if (array_key_exists('ignore_platform_reqs', $options)) { + $this->ignorePlatformReqs = (bool)$options['ignore_platform_reqs']; + } + } + + /** + * Check if this plugin can be executed. + * @param $stage + * @param Builder $builder + * @param Build $build + * @return bool + */ + public static function canExecute($stage, Builder $builder, Build $build) + { + $path = $builder->buildPath . DIRECTORY_SEPARATOR . 'composer.json'; + + if (file_exists($path) && $stage == Build::STAGE_SETUP) { + return true; + } + + return false; + } + + /** + * Executes Composer and runs a specified command (e.g. install / update) + */ + public function execute() + { + $composerLocation = $this->findBinary(['composer', 'composer.phar']); + $cmd = $composerLocation . ' --no-ansi --no-interaction '; + + if ($this->preferDist) { + $this->builder->log('Using --prefer-dist flag'); + $cmd .= ' --prefer-dist'; + } + + if ($this->preferSource) { + $this->builder->log('Using --prefer-source flag'); + $cmd .= ' --prefer-source'; + } + + if ($this->nodev) { + $this->builder->log('Using --no-dev flag'); + $cmd .= ' --no-dev'; + } + + if ($this->ignorePlatformReqs) { + $this->builder->log('Using --ignore-platform-reqs flag'); + $cmd .= ' --ignore-platform-reqs'; + } + + $cmd .= ' --working-dir="%s" %s'; + + return $this->builder->executeCommand($cmd, $this->directory, $this->action); + } +} diff --git a/src/PHPCensor/Plugin/CopyBuild.php b/src/PHPCensor/Plugin/CopyBuild.php new file mode 100644 index 0000000..1c2a28c --- /dev/null +++ b/src/PHPCensor/Plugin/CopyBuild.php @@ -0,0 +1,91 @@ + + */ +class CopyBuild extends Plugin +{ + protected $directory; + protected $ignore; + protected $wipe; + + /** + * @return string + */ + public static function pluginName() + { + return 'copy_build'; + } + + /** + * {@inheritdoc} + */ + public function __construct(Builder $builder, Build $build, array $options = []) + { + parent::__construct($builder, $build, $options); + + $path = $this->builder->buildPath; + $this->directory = isset($options['directory']) ? $options['directory'] : $path; + $this->wipe = isset($options['wipe']) ? (bool)$options['wipe'] : false; + $this->ignore = isset($options['respect_ignore']) ? (bool)$options['respect_ignore'] : false; + } + + /** + * Copies files from the root of the build directory into the target folder + */ + public function execute() + { + $build = $this->builder->buildPath; + + if ($this->directory == $build) { + return false; + } + + $this->wipeExistingDirectory(); + + $cmd = 'mkdir -p "%s" && cp -R "%s" "%s"'; + + $success = $this->builder->executeCommand($cmd, $this->directory, $build, $this->directory); + + $this->deleteIgnoredFiles(); + + return $success; + } + + /** + * Wipe the destination directory if it already exists. + * @throws \Exception + */ + protected function wipeExistingDirectory() + { + if ($this->wipe === true && $this->directory != '/' && is_dir($this->directory)) { + $cmd = 'rm -Rf "%s*"'; + $success = $this->builder->executeCommand($cmd, $this->directory); + + if (!$success) { + throw new \Exception(sprintf('Failed to wipe existing directory %s before copy', $this->directory)); + } + } + } + + /** + * Delete any ignored files from the build prior to copying. + */ + protected function deleteIgnoredFiles() + { + if ($this->ignore) { + foreach ($this->builder->ignore as $file) { + $cmd = 'rm -Rf "%s/%s"'; + $this->builder->executeCommand($cmd, $this->directory, $file); + } + } + } +} diff --git a/src/PHPCensor/Plugin/Deployer.php b/src/PHPCensor/Plugin/Deployer.php new file mode 100644 index 0000000..c0e17ec --- /dev/null +++ b/src/PHPCensor/Plugin/Deployer.php @@ -0,0 +1,81 @@ + + */ +class Deployer extends Plugin +{ + protected $webhookUrl; + protected $reason; + protected $updateOnly; + + /** + * @return string + */ + public static function pluginName() + { + return 'deployer'; + } + + /** + * {@inheritdoc} + */ + public function __construct(Builder $builder, Build $build, array $options = []) + { + parent::__construct($builder, $build, $options); + + $this->reason = 'PHP Censor Build #%BUILD% - %COMMIT_MESSAGE%'; + if (isset($options['webhook_url'])) { + $this->webhookUrl = $options['webhook_url']; + } + + if (isset($options['reason'])) { + $this->reason = $options['reason']; + } + + $this->updateOnly = isset($options['update_only']) ? (bool) $options['update_only'] : true; + } + + /** + * Copies files from the root of the build directory into the target folder + */ + public function execute() + { + if (empty($this->webhookUrl)) { + $this->builder->logFailure('You must specify a webhook URL.'); + return false; + } + + $client = new Client(); + $response = $client->post( + $this->webhookUrl, + [ + 'form_params' => [ + 'reason' => $this->builder->interpolate($this->reason), + 'source' => 'PHP Censor', + 'url' => $this->builder->interpolate('%BUILD_URI%'), + 'branch' => $this->builder->interpolate('%BRANCH%'), + 'commit' => $this->builder->interpolate('%COMMIT%'), + 'update_only' => $this->updateOnly, + ] + ] + ); + + $status = (integer)$response->getStatusCode(); + + return ( + ($status >= 200 && $status < 300) + ? true + : false + ); + } +} diff --git a/src/PHPCensor/Plugin/Email.php b/src/PHPCensor/Plugin/Email.php new file mode 100644 index 0000000..c460bb4 --- /dev/null +++ b/src/PHPCensor/Plugin/Email.php @@ -0,0 +1,214 @@ + + */ +class Email extends Plugin +{ + /** + * @return string + */ + public static function pluginName() + { + return 'email'; + } + + /** + * Send a notification mail. + * + * @return boolean + */ + public function execute() + { + $addresses = $this->getEmailAddresses(); + + // Without some email addresses in the yml file then we + // can't do anything. + if (count($addresses) == 0) { + return false; + } + + $buildStatus = $this->build->isSuccessful() ? "Passing Build" : "Failing Build"; + $projectName = $this->build->getProject()->getTitle(); + + try { + $view = $this->getMailTemplate(); + } catch (\RuntimeException $e) { + $this->builder->log( + sprintf('Unknown mail template "%s", falling back to default.', $this->options['template']), + LogLevel::WARNING + ); + $view = $this->getDefaultMailTemplate(); + } + + $view->build = $this->build; + $view->project = $this->build->getProject(); + + $layout = new View('Email/layout'); + $layout->build = $this->build; + $layout->project = $this->build->getProject(); + $layout->content = $view->render(); + $body = $layout->render(); + + $sendFailures = $this->sendSeparateEmails( + $addresses, + sprintf("PHP Censor - %s - %s", $projectName, $buildStatus), + $body + ); + + // This is a success if we've not failed to send anything. + $this->builder->log(sprintf('%d emails sent.', (count($addresses) - $sendFailures))); + $this->builder->log(sprintf('%d emails failed to send.', $sendFailures)); + + return ($sendFailures === 0); + } + + /** + * @param string $toAddress Single address to send to + * @param string[] $ccList + * @param string $subject Email subject + * @param string $body Email body + * + * @return integer + */ + protected function sendEmail($toAddress, $ccList, $subject, $body) + { + $email = new EmailHelper(); + + $email->setEmailTo($toAddress, $toAddress); + $email->setSubject($subject); + $email->setBody($body); + $email->setHtml(true); + + if (is_array($ccList) && count($ccList)) { + foreach ($ccList as $address) { + $email->addCc($address, $address); + } + } + + return $email->send($this->builder); + } + + /** + * Send an email to a list of specified subjects. + * + * @param array $toAddresses + * List of destination addresses for message. + * @param string $subject + * Mail subject + * @param string $body + * Mail body + * + * @return int number of failed messages + */ + public function sendSeparateEmails(array $toAddresses, $subject, $body) + { + $failures = 0; + $ccList = $this->getCcAddresses(); + + foreach ($toAddresses as $address) { + if (!$this->sendEmail($address, $ccList, $subject, $body)) { + $failures++; + } + } + + return $failures; + } + + /** + * Get the list of email addresses to send to. + * @return array + */ + protected function getEmailAddresses() + { + $addresses = []; + $committer = $this->build->getCommitterEmail(); + + $this->builder->logDebug(sprintf("Committer email: '%s'", $committer)); + $this->builder->logDebug(sprintf( + "Committer option: '%s'", + (!empty($this->options['committer']) && $this->options['committer']) ? 'true' : 'false' + )); + + if (!empty($this->options['committer']) && $this->options['committer']) { + if ($committer) { + $addresses[] = $committer; + } + } + + $this->builder->logDebug(sprintf( + "Addresses option: '%s'", + (!empty($this->options['addresses']) && is_array($this->options['addresses'])) ? implode(', ', $this->options['addresses']) : 'false' + )); + + if (!empty($this->options['addresses']) && is_array($this->options['addresses'])) { + foreach ($this->options['addresses'] as $address) { + $addresses[] = $address; + } + } + + $this->builder->logDebug(sprintf( + "Default mailTo option: '%s'", + !empty($this->options['default_mailto_address']) ? $this->options['default_mailto_address'] : 'false' + )); + + if (empty($addresses) && !empty($this->options['default_mailto_address'])) { + $addresses[] = $this->options['default_mailto_address']; + } + + return array_unique($addresses); + } + + /** + * Get the list of email addresses to CC. + * + * @return array + */ + protected function getCcAddresses() + { + $ccAddresses = []; + + if (isset($this->options['cc'])) { + foreach ($this->options['cc'] as $address) { + $ccAddresses[] = $address; + } + } + + return $ccAddresses; + } + + /** + * Get the mail template used to sent the mail. + * + * @return View + */ + protected function getMailTemplate() + { + if (isset($this->options['template'])) { + return new View('Email/' . $this->options['template']); + } + + return $this->getDefaultMailTemplate(); + } + + /** + * Get the default mail template. + * + * @return View + */ + protected function getDefaultMailTemplate() + { + $template = $this->build->isSuccessful() ? 'short' : 'long'; + + return new View('Email/' . $template); + } +} diff --git a/src/PHPCensor/Plugin/Env.php b/src/PHPCensor/Plugin/Env.php new file mode 100644 index 0000000..cec0703 --- /dev/null +++ b/src/PHPCensor/Plugin/Env.php @@ -0,0 +1,46 @@ + + */ +class Env extends Plugin +{ + protected $env_vars; + + /** + * @return string + */ + public static function pluginName() + { + return 'env'; + } + + /** + * Adds the specified environment variables to the builder environment + */ + public function execute() + { + $success = true; + foreach ($this->options as $key => $value) { + if (is_numeric($key)) { + // This allows the developer to specify env vars like " - FOO=bar" or " - FOO: bar" + $env_var = is_array($value)? key($value).'='.current($value): $value; + } else { + // This allows the standard syntax: "FOO: bar" + $env_var = "$key=$value"; + } + + if (!putenv($this->builder->interpolate($env_var))) { + $success = false; + $this->builder->logFailure('Unable to set environment variable'); + } + } + return $success; + } +} diff --git a/src/PHPCensor/Plugin/FlowdockNotify.php b/src/PHPCensor/Plugin/FlowdockNotify.php new file mode 100644 index 0000000..de57770 --- /dev/null +++ b/src/PHPCensor/Plugin/FlowdockNotify.php @@ -0,0 +1,73 @@ + + */ +class FlowdockNotify extends Plugin +{ + protected $api_key; + protected $email; + protected $message; + + const MESSAGE_DEFAULT = 'Build %BUILD% has finished for commit %SHORT_COMMIT% + (%COMMIT_EMAIL%)> on branch %BRANCH%'; + + /** + * @return string + */ + public static function pluginName() + { + return 'flowdock_notify'; + } + + /** + * {@inheritdoc} + */ + public function __construct(Builder $builder, Build $build, array $options = []) + { + parent::__construct($builder, $build, $options); + + if (!is_array($options) || !isset($options['api_key'])) { + throw new \Exception('Please define the api_key for Flowdock Notify plugin!'); + } + $this->api_key = trim($options['api_key']); + $this->message = isset($options['message']) ? $options['message'] : self::MESSAGE_DEFAULT; + $this->email = isset($options['email']) ? $options['email'] : 'PHP Censor'; + } + + /** + * Run the Flowdock plugin. + * @return bool + * @throws \Exception + */ + public function execute() + { + + $message = $this->builder->interpolate($this->message); + $successfulBuild = $this->build->isSuccessful() ? 'Success' : 'Failed'; + $push = new Push($this->api_key); + $flowMessage = TeamInboxMessage::create() + ->setSource("PHPCensor") + ->setFromAddress($this->email) + ->setFromName($this->build->getProject()->getTitle()) + ->setSubject($successfulBuild) + ->setTags(['#ci']) + ->setLink($this->build->getBranchLink()) + ->setContent($message); + + if (!$push->sendTeamInboxMessage($flowMessage, ['connect_timeout' => 5000, 'timeout' => 5000])) { + throw new \Exception(sprintf('Flowdock Failed: %s', $flowMessage->getResponseErrors())); + } + return true; + } +} diff --git a/src/PHPCensor/Plugin/Git.php b/src/PHPCensor/Plugin/Git.php new file mode 100644 index 0000000..ab01b0a --- /dev/null +++ b/src/PHPCensor/Plugin/Git.php @@ -0,0 +1,157 @@ + + */ +class Git extends Plugin +{ + protected $actions = []; + + /** + * @return string + */ + public static function pluginName() + { + return 'git'; + } + + /** + * Run the Git plugin. + * @return bool + */ + public function execute() + { + $buildPath = $this->builder->buildPath; + + // Check if there are any actions to be run for the branch we're running on: + if (!array_key_exists($this->build->getBranch(), $this->actions)) { + return true; + } + + // If there are, run them: + $curdir = getcwd(); + chdir($buildPath); + + $success = true; + foreach ($this->actions[$this->build->getBranch()] as $action => $options) { + if (!$this->runAction($action, $options)) { + $success = false; + break; + } + } + + chdir($curdir); + + return $success; + } + + /** + * Determine which action to run, and run it. + * @param $action + * @param array $options + * @return bool + */ + protected function runAction($action, array $options = []) + { + switch ($action) { + case 'merge': + return $this->runMergeAction($options); + + case 'tag': + return $this->runTagAction($options); + + case 'pull': + return $this->runPullAction($options); + + case 'push': + return $this->runPushAction($options); + } + + + return false; + } + + /** + * Handle a merge action. + * @param $options + * @return bool + */ + protected function runMergeAction($options) + { + if (array_key_exists('branch', $options)) { + $cmd = 'cd "%s" && git checkout %s && git merge "%s"'; + $path = $this->builder->buildPath; + return $this->builder->executeCommand($cmd, $path, $options['branch'], $this->build->getBranch()); + } + } + + /** + * Handle a tag action. + * @param $options + * @return bool + */ + protected function runTagAction($options) + { + $tagName = date('Ymd-His'); + $message = sprintf('Tag created by PHP Censor: %s', date('Y-m-d H:i:s')); + + if (array_key_exists('name', $options)) { + $tagName = $this->builder->interpolate($options['name']); + } + + if (array_key_exists('message', $options)) { + $message = $this->builder->interpolate($options['message']); + } + + $cmd = 'git tag %s -m "%s"'; + return $this->builder->executeCommand($cmd, $tagName, $message); + } + + /** + * Handle a pull action. + * @param $options + * @return bool + */ + protected function runPullAction($options) + { + $branch = $this->build->getBranch(); + $remote = 'origin'; + + if (array_key_exists('branch', $options)) { + $branch = $this->builder->interpolate($options['branch']); + } + + if (array_key_exists('remote', $options)) { + $remote = $this->builder->interpolate($options['remote']); + } + + return $this->builder->executeCommand('git pull %s %s', $remote, $branch); + } + + /** + * Handle a push action. + * @param $options + * @return bool + */ + protected function runPushAction($options) + { + $branch = $this->build->getBranch(); + $remote = 'origin'; + + if (array_key_exists('branch', $options)) { + $branch = $this->builder->interpolate($options['branch']); + } + + if (array_key_exists('remote', $options)) { + $remote = $this->builder->interpolate($options['remote']); + } + + return $this->builder->executeCommand('git push %s %s', $remote, $branch); + } +} diff --git a/src/PHPCensor/Plugin/Grunt.php b/src/PHPCensor/Plugin/Grunt.php new file mode 100644 index 0000000..e5db5e2 --- /dev/null +++ b/src/PHPCensor/Plugin/Grunt.php @@ -0,0 +1,81 @@ + + */ +class Grunt extends Plugin +{ + protected $directory; + protected $task; + protected $preferDist; + protected $grunt; + protected $gruntfile; + + /** + * @return string + */ + public static function pluginName() + { + return 'grunt'; + } + + /** + * {@inheritdoc} + */ + public function __construct(Builder $builder, Build $build, array $options = []) + { + parent::__construct($builder, $build, $options); + + $path = $this->builder->buildPath; + $this->directory = $path; + $this->task = null; + $this->grunt = $this->findBinary('grunt'); + $this->gruntfile = 'Gruntfile.js'; + + // Handle options: + if (isset($options['directory'])) { + $this->directory = $path . DIRECTORY_SEPARATOR . $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']; + } + } + + /** + * 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 + $cmd = 'cd %s && npm install'; + if (!$this->builder->executeCommand($cmd, $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->builder->executeCommand($cmd, $this->directory, $this->gruntfile, $this->task); + } +} diff --git a/src/PHPCensor/Plugin/Gulp.php b/src/PHPCensor/Plugin/Gulp.php new file mode 100644 index 0000000..6c28cea --- /dev/null +++ b/src/PHPCensor/Plugin/Gulp.php @@ -0,0 +1,81 @@ + + */ +class Gulp extends Plugin +{ + protected $directory; + protected $task; + protected $preferDist; + protected $gulp; + protected $gulpfile; + + /** + * @return string + */ + public static function pluginName() + { + return 'gulp'; + } + + /** + * {@inheritdoc} + */ + public function __construct(Builder $builder, Build $build, array $options = []) + { + parent::__construct($builder, $build, $options); + + $path = $this->builder->buildPath; + $this->directory = $path; + $this->task = null; + $this->gulp = $this->findBinary('gulp'); + $this->gulpfile = 'gulpfile.js'; + + // Handle options: + if (isset($options['directory'])) { + $this->directory = $path . DIRECTORY_SEPARATOR . $options['directory']; + } + + if (isset($options['task'])) { + $this->task = $options['task']; + } + + if (isset($options['gulp'])) { + $this->gulp = $options['gulp']; + } + + if (isset($options['gulpfile'])) { + $this->gulpfile = $options['gulpfile']; + } + } + + /** + * Executes gulp and runs a specified command (e.g. install / update) + */ + public function execute() + { + // if npm does not work, we cannot use gulp, so we return false + $cmd = 'cd %s && npm install'; + if (!$this->builder->executeCommand($cmd, $this->directory)) { + return false; + } + + // build the gulp command + $cmd = 'cd %s && ' . $this->gulp; + $cmd .= ' --no-color'; + $cmd .= ' --gulpfile %s'; + $cmd .= ' %s'; // the task that will be executed + + // and execute it + return $this->builder->executeCommand($cmd, $this->directory, $this->gulpfile, $this->task); + } +} diff --git a/src/PHPCensor/Plugin/HipchatNotify.php b/src/PHPCensor/Plugin/HipchatNotify.php new file mode 100644 index 0000000..b855ddf --- /dev/null +++ b/src/PHPCensor/Plugin/HipchatNotify.php @@ -0,0 +1,93 @@ + + */ +class HipchatNotify extends Plugin +{ + protected $authToken; + protected $color; + protected $notify; + protected $userAgent; + protected $cookie; + protected $message; + protected $room; + + /** + * @return string + */ + public static function pluginName() + { + return 'hipchat_notify'; + } + + /** + * {@inheritdoc} + */ + public function __construct(Builder $builder, Build $build, array $options = []) + { + parent::__construct($builder, $build, $options); + + $this->userAgent = "PHP Censor/1.0"; + $this->cookie = "php-censor-cookie"; + + if (is_array($options) && isset($options['authToken']) && isset($options['room'])) { + $this->authToken = $options['authToken']; + $this->room = $options['room']; + + if (isset($options['message'])) { + $this->message = $options['message']; + } else { + $this->message = '%PROJECT_TITLE% built at %BUILD_URI%'; + } + + if (isset($options['color'])) { + $this->color = $options['color']; + } else { + $this->color = 'yellow'; + } + + if (isset($options['notify'])) { + $this->notify = $options['notify']; + } else { + $this->notify = false; + } + } else { + throw new \Exception('Please define room and authToken for hipchat_notify plugin.'); + } + } + + /** + * Run the HipChat plugin. + * @return bool + */ + public function execute() + { + $hipChat = new HipChat($this->authToken); + $message = $this->builder->interpolate($this->message); + + $result = true; + if (is_array($this->room)) { + foreach ($this->room as $room) { + if (!$hipChat->message_room($room, 'PHP Censor', $message, $this->notify, $this->color)) { + $result = false; + } + } + } else { + if (!$hipChat->message_room($this->room, 'PHP Censor', $message, $this->notify, $this->color)) { + $result = false; + } + } + + return $result; + } +} diff --git a/src/PHPCensor/Plugin/Irc.php b/src/PHPCensor/Plugin/Irc.php new file mode 100644 index 0000000..528b815 --- /dev/null +++ b/src/PHPCensor/Plugin/Irc.php @@ -0,0 +1,119 @@ + + */ +class Irc extends Plugin +{ + protected $message; + protected $server; + protected $port; + protected $room; + protected $nick; + + /** + * @return string + */ + public static function pluginName() + { + return 'irc'; + } + + /** + * {@inheritdoc} + */ + public function __construct(Builder $builder, Build $build, array $options = []) + { + parent::__construct($builder, $build, $options); + + $this->message = $options['message']; + $buildSettings = $this->builder->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']; + } + } + + /** + * Run IRC plugin. + * @return bool + */ + public function execute() + { + $msg = $this->builder->interpolate($this->message); + + if (empty($this->server) || empty($this->room) || empty($this->nick)) { + $this->builder->logFailure('You must configure a server, room and nick.'); + } + + if (empty($this->port)) { + $this->port = 6667; + } + + $sock = fsockopen($this->server, $this->port); + stream_set_timeout($sock, 1); + + $connectCommands = [ + 'USER ' . $this->nick . ' 0 * :' . $this->nick, + 'NICK ' . $this->nick, + ]; + $this->executeIrcCommands($sock, $connectCommands); + $this->executeIrcCommand($sock, 'JOIN ' . $this->room); + $this->executeIrcCommand($sock, 'PRIVMSG ' . $this->room . ' :' . $msg); + + fclose($sock); + + return true; + } + + /** + * @param resource $socket + * @param array $commands + * @return bool + */ + private function executeIrcCommands($socket, array $commands) + { + foreach ($commands as $command) { + fputs($socket, $command . "\n"); + } + + $pingBack = false; + + // almost all servers expect pingback! + while ($response = fgets($socket)) { + $matches = []; + if (preg_match('/^PING \\:([A-Z0-9]+)/', $response, $matches)) { + $pingBack = $matches[1]; + } + } + + if ($pingBack) { + $command = 'PONG :' . $pingBack . "\n"; + fputs($socket, $command); + } + } + + /** + * + * @param resource $socket + * @param string $command + * @return bool + */ + private function executeIrcCommand($socket, $command) + { + return $this->executeIrcCommands($socket, [$command]); + } +} diff --git a/src/PHPCensor/Plugin/Lint.php b/src/PHPCensor/Plugin/Lint.php new file mode 100644 index 0000000..9ef0796 --- /dev/null +++ b/src/PHPCensor/Plugin/Lint.php @@ -0,0 +1,140 @@ + + */ +class Lint extends Plugin +{ + protected $directories; + protected $recursive = true; + protected $ignore; + + /** + * @return string + */ + public static function pluginName() + { + return 'lint'; + } + + /** + * {@inheritdoc} + */ + public function __construct(Builder $builder, Build $build, array $options = []) + { + parent::__construct($builder, $build, $options); + + $this->directories = ['']; + $this->ignore = $this->builder->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->builder->quiet = true; + $success = true; + + $php = $this->findBinary('php'); + + foreach ($this->directories as $dir) { + if (!$this->lintDirectory($php, $dir)) { + $success = false; + } + } + + $this->builder->quiet = false; + + return $success; + } + + /** + * Lint an item (file or directory) by calling the appropriate method. + * @param $php + * @param $item + * @param $itemPath + * @return bool + */ + 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 . DIRECTORY_SEPARATOR)) { + $success = false; + } + + return $success; + } + + /** + * Run php -l against a directory of files. + * @param $php + * @param $path + * @return bool + */ + protected function lintDirectory($php, $path) + { + $success = true; + $directory = new \DirectoryIterator($this->builder->buildPath . $path); + + foreach ($directory as $item) { + if ($item->isDot()) { + continue; + } + + $itemPath = $path . $item->getFilename(); + + if (in_array($itemPath, $this->ignore)) { + continue; + } + + if (!$this->lintItem($php, $item, $itemPath)) { + $success = false; + } + } + + return $success; + } + + /** + * Run php -l against a specific file. + * @param $php + * @param $path + * @return bool + */ + protected function lintFile($php, $path) + { + $success = true; + + if (!$this->builder->executeCommand($php . ' -l "%s"', $this->builder->buildPath . $path)) { + $this->builder->logFailure($path); + $success = false; + } + + return $success; + } +} diff --git a/src/PHPCensor/Plugin/Mage.php b/src/PHPCensor/Plugin/Mage.php new file mode 100644 index 0000000..01018c3 --- /dev/null +++ b/src/PHPCensor/Plugin/Mage.php @@ -0,0 +1,116 @@ +getSystemConfig('mage'); + if (!empty($config['bin'])) { + $this->mage_bin = $config['bin']; + } + + if (isset($options['env'])) { + $this->mage_env = $builder->interpolate($options['env']); + } + } + + /** + * {@inheritdoc} + */ + public function execute() + { + if (empty($this->mage_env)) { + $this->builder->logFailure('You must specify environment.'); + return false; + } + + $result = $this->builder->executeCommand($this->mage_bin . ' deploy to:' . $this->mage_env); + + try { + $this->builder->log('########## MAGE LOG BEGIN ##########'); + $this->builder->log($this->getMageLog()); + $this->builder->log('########## MAGE LOG END ##########'); + } catch (\Exception $e) { + $this->builder->log($e->getMessage(), LogLevel::NOTICE); + } + + return $result; + } + + /** + * Get mage log lines + * @return array + * @throws \Exception + */ + protected function getMageLog() + { + $logs_dir = $this->build->getBuildPath() . '/.mage/logs'; + if (!is_dir($logs_dir)) { + throw new \Exception('Log directory not found'); + } + + $list = scandir($logs_dir); + if ($list === false) { + throw new \Exception('Log dir read fail'); + } + + $list = array_filter($list, function ($name) { + return preg_match('/^log-\d+-\d+\.log$/', $name); + }); + if (empty($list)) { + throw new \Exception('Log dir filter fail'); + } + + $res = sort($list); + if ($res === false) { + throw new \Exception('Logs sort fail'); + } + + $last_log_file = end($list); + if ($last_log_file === false) { + throw new \Exception('Get last Log name fail'); + } + + $log_content = file_get_contents($logs_dir . '/' . $last_log_file); + if ($log_content === false) { + throw new \Exception('Get last Log content fail'); + } + + $lines = explode("\n", $log_content); + $lines = array_map('trim', $lines); + $lines = array_filter($lines); + + return $lines; + } +} diff --git a/src/PHPCensor/Plugin/Mage3.php b/src/PHPCensor/Plugin/Mage3.php new file mode 100644 index 0000000..b3f9ac2 --- /dev/null +++ b/src/PHPCensor/Plugin/Mage3.php @@ -0,0 +1,121 @@ +getSystemConfig('mage3'); + if (!empty($config['bin'])) { + $this->mage_bin = $config['bin']; + } + + if (isset($options['env'])) { + $this->mage_env = $builder->interpolate($options['env']); + } + + if (isset($options['log_dir'])) { + $this->mage_log_dir = $builder->interpolate($options['log_dir']); + } + } + + /** + * {@inheritdoc} + */ + public function execute() + { + if (empty($this->mage_env)) { + $this->builder->logFailure('You must specify environment.'); + return false; + } + + $result = $this->builder->executeCommand($this->mage_bin . ' -n deploy ' . $this->mage_env); + + try { + $this->builder->log('########## MAGE LOG BEGIN ##########'); + $this->builder->log($this->getMageLog()); + $this->builder->log('########## MAGE LOG END ##########'); + } catch (\Exception $e) { + $this->builder->log($e->getMessage(), LogLevel::NOTICE); + } + + return $result; + } + + /** + * Get mage log lines + * @return array + * @throws \Exception + */ + protected function getMageLog() + { + $logs_dir = $this->build->getBuildPath() . (!empty($this->mage_log_dir) ? '/' . $this->mage_log_dir : ''); + if (!is_dir($logs_dir)) { + throw new \Exception('Log directory not found'); + } + + $list = scandir($logs_dir); + if ($list === false) { + throw new \Exception('Log dir read fail'); + } + + $list = array_filter($list, function ($name) { + return preg_match('/^\d+_\d+\.log$/', $name); + }); + if (empty($list)) { + throw new \Exception('Log dir filter fail'); + } + + $res = sort($list); + if ($res === false) { + throw new \Exception('Logs sort fail'); + } + + $last_log_file = end($list); + if ($last_log_file === false) { + throw new \Exception('Get last Log name fail'); + } + + $log_content = file_get_contents($logs_dir . '/' . $last_log_file); + if ($log_content === false) { + throw new \Exception('Get last Log content fail'); + } + + $lines = explode("\n", $log_content); + $lines = array_map('trim', $lines); + $lines = array_filter($lines); + + return $lines; + } +} diff --git a/src/PHPCensor/Plugin/Mysql.php b/src/PHPCensor/Plugin/Mysql.php new file mode 100644 index 0000000..a7bfbb2 --- /dev/null +++ b/src/PHPCensor/Plugin/Mysql.php @@ -0,0 +1,160 @@ + + * @author Steve Kamerman + */ +class Mysql extends Plugin +{ + /** + * @var string + */ + protected $host; + + /** + * @var string + */ + protected $user; + + /** + * @var string + */ + protected $pass; + + /** + * @return string + */ + public static function pluginName() + { + return 'mysql'; + } + + /** + * {@inheritdoc} + */ + public function __construct(Builder $builder, Build $build, array $options = []) + { + parent::__construct($builder, $build, $options); + + $config = Database::getConnection('write')->getDetails(); + + $this->host =(defined('DB_HOST')) ? DB_HOST : null; + $this->user = $config['user']; + $this->pass = $config['pass']; + + $buildSettings = $this->builder->getConfig('build_settings'); + + if (!isset($buildSettings['mysql'])) { + return; + } + + if (!empty($buildSettings['mysql']['host'])) { + $this->host = $this->builder->interpolate($buildSettings['mysql']['host']); + } + + if (!empty($buildSettings['mysql']['user'])) { + $this->user = $this->builder->interpolate($buildSettings['mysql']['user']); + } + + if (array_key_exists('pass', $buildSettings['mysql'])) { + $this->pass = $buildSettings['mysql']['pass']; + } + } + + /** + * Connects to MySQL and runs a specified set of queries. + * @return boolean + */ + public function execute() + { + try { + $opts = [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]; + $pdo = new PDO('mysql:host=' . $this->host, $this->user, $this->pass, $opts); + + foreach ($this->options as $query) { + if (!is_array($query)) { + // Simple query + $pdo->query($this->builder->interpolate($query)); + } elseif (isset($query['import'])) { + // SQL file execution + $this->executeFile($query['import']); + } else { + throw new \Exception('Invalid command.'); + } + } + } catch (\Exception $ex) { + $this->builder->logFailure($ex->getMessage()); + return false; + } + return true; + } + + /** + * @param string $query + * @return boolean + * @throws \Exception + */ + protected function executeFile($query) + { + if (!isset($query['file'])) { + throw new \Exception('Import statement must contain a \'file\' key'); + } + + $import_file = $this->builder->buildPath . $this->builder->interpolate($query['file']); + if (!is_readable($import_file)) { + throw new \Exception(sprintf('Cannot open SQL import file: %s', $import_file)); + } + + $database = isset($query['database']) ? $this->builder->interpolate($query['database']) : null; + + $import_command = $this->getImportCommand($import_file, $database); + if (!$this->builder->executeCommand($import_command)) { + throw new \Exception('Unable to execute SQL file'); + } + + return true; + } + + /** + * Builds the MySQL import command required to import/execute the specified file + * + * @param string $import_file Path to file, relative to the build root + * @param string $database If specified, this database is selected before execution + * + * @return string + */ + protected function getImportCommand($import_file, $database = null) + { + $decompression = [ + 'bz2' => '| bzip2 --decompress', + 'gz' => '| gzip --decompress', + ]; + + $extension = strtolower(pathinfo($import_file, PATHINFO_EXTENSION)); + $decomp_cmd = ''; + if (array_key_exists($extension, $decompression)) { + $decomp_cmd = $decompression[$extension]; + } + + $args = [ + ':import_file' => escapeshellarg($import_file), + ':decomp_cmd' => $decomp_cmd, + ':host' => escapeshellarg($this->host), + ':user' => escapeshellarg($this->user), + ':pass' => (!$this->pass) ? '' : '-p' . escapeshellarg($this->pass), + ':database' => ($database === null)? '': escapeshellarg($database), + ]; + + return strtr('cat :import_file :decomp_cmd | mysql -h:host -u:user :pass :database', $args); + } +} diff --git a/src/PHPCensor/Plugin/Option/PhpUnitOptions.php b/src/PHPCensor/Plugin/Option/PhpUnitOptions.php new file mode 100644 index 0000000..bb7cf6d --- /dev/null +++ b/src/PHPCensor/Plugin/Option/PhpUnitOptions.php @@ -0,0 +1,265 @@ + + */ +class PhpUnitOptions +{ + protected $options; + protected $arguments = []; + + public function __construct($options) + { + $this->options = $options; + } + + /** + * Remove a command argument + * + * @param $argumentName + * + * @return $this + */ + public function removeArgument($argumentName) + { + unset($this->arguments[$argumentName]); + return $this; + } + + /** + * Combine all the argument into a string for the phpunit command + * + * @return string + */ + public function buildArgumentString() + { + $argumentString = ''; + foreach ($this->getCommandArguments() as $argumentName => $argumentValues) { + $prefix = $argumentName[0] == '-' ? '' : '--'; + + if (!is_array($argumentValues)) { + $argumentValues = [$argumentValues]; + } + + foreach ($argumentValues as $argValue) { + $postfix = ' '; + if (!empty($argValue)) { + $postfix = ' "' . $argValue . '" '; + } + $argumentString .= $prefix . $argumentName . $postfix; + } + } + + return $argumentString; + } + + /** + * Get all the command arguments + * + * @return string[] + */ + public function getCommandArguments() + { + /* + * Return the full list of arguments + */ + return $this->parseArguments()->arguments; + } + + /** + * Parse the arguments from the config options + * + * @return $this + */ + private function parseArguments() + { + if (empty($this->arguments)) { + /* + * Parse the arguments from the YML options file + */ + if (isset($this->options['args'])) { + $rawArgs = $this->options['args']; + if (is_array($rawArgs)) { + $this->arguments = $rawArgs; + } else { + /* + * Try to parse old arguments in a single string + */ + preg_match_all('@--([a-z\-]+)([\s=]+)?[\'"]?((?!--)[-\w/.,\\\]+)?[\'"]?@', (string)$rawArgs, $argsMatch); + + if (!empty($argsMatch) && sizeof($argsMatch) > 2) { + foreach ($argsMatch[1] as $index => $argName) { + $this->addArgument($argName, $argsMatch[3][$index]); + } + } + } + } + + /* + * Handles command aliases outside of the args option + */ + if (isset($this->options['coverage'])) { + $this->addArgument('coverage-html', $this->options['coverage']); + } + + /* + * Handles command aliases outside of the args option + */ + if (isset($this->options['config'])) { + $this->addArgument('configuration', $this->options['config']); + } + } + + return $this; + } + + /** + * Add an argument to the collection + * Note: adding argument before parsing the options will prevent the other options from been parsed. + * + * @param string $argumentName + * @param string $argumentValue + */ + public function addArgument($argumentName, $argumentValue) + { + if (isset($this->arguments[$argumentName])) { + if (!is_array($this->arguments[$argumentName])) { + // Convert existing argument values into an array + $this->arguments[$argumentName] = [$this->arguments[$argumentName]]; + } + + // Appends the new argument to the list + $this->arguments[$argumentName][] = $argumentValue; + } else { + // Adds new argument + $this->arguments[$argumentName] = $argumentValue; + } + } + + /** + * Get the list of directory to run phpunit in + * + * @return string[] List of directories + */ + public function getDirectories() + { + $directories = $this->getOption('directory'); + + if (is_string($directories)) { + $directories = [$directories]; + } else { + if (is_null($directories)) { + $directories = []; + } + } + + return is_array($directories) ? $directories : [$directories]; + } + + /** + * Get an option if defined + * + * @param $optionName + * + * @return string[]|string|null + */ + public function getOption($optionName) + { + if (isset($this->options[$optionName])) { + return $this->options[$optionName]; + } + + return null; + } + + /** + * Get the directory to execute the command from + * + * @return mixed|null + */ + public function getRunFrom() + { + return $this->getOption('run_from'); + } + + /** + * Ge the directory name where tests file reside + * + * @return string|null + */ + public function getTestsPath() + { + return $this->getOption('path'); + } + + /** + * Get the PHPUnit configuration from the options, or the optional path + * + * @param string $altPath + * + * @return string[] path of files + */ + public function getConfigFiles($altPath = null) + { + $configFiles = $this->getArgument('configuration'); + if (empty($configFiles) && $altPath) { + $configFile = self::findConfigFile($altPath); + if ($configFile) { + $configFiles[] = $configFile; + } + } + + return $configFiles; + } + + /** + * Get options for a given argument + * + * @param $argumentName + * + * @return string[] All the options for given argument + */ + public function getArgument($argumentName) + { + $this->parseArguments(); + + if (isset($this->arguments[$argumentName])) { + return is_array( + $this->arguments[$argumentName] + ) ? $this->arguments[$argumentName] : [$this->arguments[$argumentName]]; + } + + return []; + } + + /** + * Find a PHPUnit configuration file in a directory + * + * @param string $buildPath The path to configuration file + * + * @return null|string + */ + public static function findConfigFile($buildPath) + { + $files = [ + 'phpunit.xml', + 'phpunit.mysql.xml', + 'phpunit.pgsql.xml', + 'phpunit.xml.dist', + 'tests/phpunit.xml', + 'tests/phpunit.xml.dist', + ]; + + foreach ($files as $file) { + if (file_exists($buildPath . $file)) { + return $file; + } + } + + return null; + } +} diff --git a/src/PHPCensor/Plugin/PackageBuild.php b/src/PHPCensor/Plugin/PackageBuild.php new file mode 100644 index 0000000..aa37cf6 --- /dev/null +++ b/src/PHPCensor/Plugin/PackageBuild.php @@ -0,0 +1,86 @@ + + */ +class PackageBuild extends Plugin +{ + protected $directory; + protected $filename; + protected $format; + + /** + * @return string + */ + public static function pluginName() + { + return 'package_build'; + } + + /** + * {@inheritdoc} + */ + public function __construct(Builder $builder, Build $build, array $options = []) + { + parent::__construct($builder, $build, $options); + + $path = $this->builder->buildPath; + $this->directory = isset($options['directory']) ? $options['directory'] : $path; + $this->filename = isset($options['filename']) ? $options['filename'] : 'build'; + $this->format = isset($options['format']) ? $options['format'] : 'zip'; + } + + /** + * Executes Composer and runs a specified command (e.g. install / update) + */ + public function execute() + { + $path = $this->builder->buildPath; + $build = $this->build; + + if ($this->directory == $path) { + return false; + } + + $filename = str_replace('%build.commit%', $build->getCommitId(), $this->filename); + $filename = str_replace('%build.id%', $build->getId(), $filename); + $filename = str_replace('%build.branch%', $build->getBranch(), $filename); + $filename = str_replace('%project.title%', $build->getProject()->getTitle(), $filename); + $filename = str_replace('%date%', date('Y-m-d'), $filename); + $filename = str_replace('%time%', date('Hi'), $filename); + $filename = preg_replace('/([^a-zA-Z0-9_-]+)/', '', $filename); + + $curdir = getcwd(); + chdir($this->builder->buildPath); + + if (!is_array($this->format)) { + $this->format = [$this->format]; + } + + foreach ($this->format as $format) { + switch ($format) { + case 'tar': + $cmd = 'tar cfz "%s/%s.tar.gz" ./*'; + break; + default: + case 'zip': + $cmd = 'zip -rq "%s/%s.zip" ./*'; + break; + } + + $success = $this->builder->executeCommand($cmd, $this->directory, $filename); + } + + chdir($curdir); + + return $success; + } +} diff --git a/src/PHPCensor/Plugin/Pdepend.php b/src/PHPCensor/Plugin/Pdepend.php new file mode 100644 index 0000000..1b93588 --- /dev/null +++ b/src/PHPCensor/Plugin/Pdepend.php @@ -0,0 +1,132 @@ + + */ +class Pdepend extends Plugin +{ + protected $args; + + /** + * @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; + + /** + * @return string + */ + public static function pluginName() + { + return 'pdepend'; + } + + /** + * {@inheritdoc} + */ + public function __construct(Builder $builder, Build $build, array $options = []) + { + parent::__construct($builder, $build, $options); + + $this->directory = isset($options['directory']) ? $options['directory'] : $this->builder->buildPath; + + $title = $this->builder->getBuildProjectTitle(); + $this->summary = $title . '-summary.xml'; + $this->pyramid = $title . '-pyramid.svg'; + $this->chart = $title . '-chart.svg'; + $this->location = $this->builder->buildPath . '..' . DIRECTORY_SEPARATOR . 'pdepend'; + } + + /** + * Runs Pdepend with the given criteria as arguments + */ + public function execute() + { + if (!file_exists($this->location)) { + mkdir($this->location); + } + if (!is_writable($this->location)) { + throw new \Exception(sprintf('The location %s is not writable or does not exist.', $this->location)); + } + + $pdepend = $this->findBinary('pdepend'); + + $cmd = $pdepend . ' --summary-xml="%s" --jdepend-chart="%s" --overview-pyramid="%s" %s "%s"'; + + $this->removeBuildArtifacts(); + + // If we need to ignore directories + if (count($this->builder->ignore)) { + $ignore = ' --ignore=' . implode(',', $this->builder->ignore); + } else { + $ignore = ''; + } + + $success = $this->builder->executeCommand( + $cmd, + $this->location . DIRECTORY_SEPARATOR . $this->summary, + $this->location . DIRECTORY_SEPARATOR . $this->chart, + $this->location . DIRECTORY_SEPARATOR . $this->pyramid, + $ignore, + $this->directory + ); + + $config = $this->builder->getSystemConfig('php-censor'); + + if ($success) { + $this->builder->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 + ) + ); + } + + return $success; + } + + /** + * Remove files created from previous builds + */ + protected function removeBuildArtifacts() + { + //Remove the created files first + foreach ([$this->summary, $this->chart, $this->pyramid] as $file) { + if (file_exists($this->location . DIRECTORY_SEPARATOR . $file)) { + unlink($this->location . DIRECTORY_SEPARATOR . $file); + } + } + } +} diff --git a/src/PHPCensor/Plugin/Pgsql.php b/src/PHPCensor/Plugin/Pgsql.php new file mode 100644 index 0000000..319d746 --- /dev/null +++ b/src/PHPCensor/Plugin/Pgsql.php @@ -0,0 +1,76 @@ + + */ +class Pgsql extends Plugin +{ + /** + * @var string + */ + protected $host; + + /** + * @var string + */ + protected $user; + + /** + * @var string + */ + protected $pass; + + /** + * @return string + */ + public static function pluginName() + { + return 'pgsql'; + } + + /** + * {@inheritdoc} + */ + public function __construct(Builder $builder, Build $build, array $options = []) + { + parent::__construct($builder, $build, $options); + + $buildSettings = $this->builder->getConfig('build_settings'); + + if (isset($buildSettings['pgsql'])) { + $sql = $buildSettings['pgsql']; + $this->host = $sql['host']; + $this->user = $sql['user']; + $this->pass = $sql['pass']; + } + } + + /** + * Connects to PgSQL and runs a specified set of queries. + * @return boolean + */ + public function execute() + { + try { + $opts = [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]; + $pdo = new PDO('pgsql:host=' . $this->host, $this->user, $this->pass, $opts); + + foreach ($this->options as $query) { + $pdo->query($this->builder->interpolate($query)); + } + } catch (\Exception $ex) { + $this->builder->logFailure($ex->getMessage()); + return false; + } + return true; + } +} diff --git a/src/PHPCensor/Plugin/Phar.php b/src/PHPCensor/Plugin/Phar.php new file mode 100644 index 0000000..b43b95e --- /dev/null +++ b/src/PHPCensor/Plugin/Phar.php @@ -0,0 +1,212 @@ +setDirectory($options['directory']); + } + + // Filename? + if (isset($options['filename'])) { + $this->setFilename($options['filename']); + } + + // RegExp? + if (isset($options['regexp'])) { + $this->setRegExp($options['regexp']); + } + + // Stub? + if (isset($options['stub'])) { + $this->setStub($options['stub']); + } + } + + /** + * Directory Setter + * + * @param string $directory Configuration Value + * @return Phar Fluent Interface + */ + public function setDirectory($directory) + { + $this->directory = $directory; + return $this; + } + + /** + * Directory Getter + * + * @return string Configurated or Default Value + */ + public function getDirectory() + { + if (!isset($this->directory)) { + $this->setDirectory($this->builder->buildPath); + } + return $this->directory; + } + + /** + * Filename Setter + * + * @param string $filename Configuration Value + * @return Phar Fluent Interface + */ + public function setFilename($filename) + { + $this->filename = $filename; + return $this; + } + + /** + * Filename Getter + * + * @return string Configurated or Default Value + */ + public function getFilename() + { + if (!isset($this->filename)) { + $this->setFilename('build.phar'); + } + return $this->filename; + } + + /** + * Regular Expression Setter + * + * @param string $regexp Configuration Value + * @return Phar Fluent Interface + */ + public function setRegExp($regexp) + { + $this->regexp = $regexp; + return $this; + } + + /** + * Regular Expression Getter + * + * @return string Configurated or Default Value + */ + public function getRegExp() + { + if (!isset($this->regexp)) { + $this->setRegExp('/\.php$/'); + } + return $this->regexp; + } + + /** + * Stub Filename Setter + * + * @param string $stub Configuration Value + * @return Phar Fluent Interface + */ + public function setStub($stub) + { + $this->stub = $stub; + return $this; + } + + /** + * Stub Filename Getter + * + * @return string Configurated Value + */ + public function getStub() + { + return $this->stub; + } + + /** + * Get stub content for the Phar file. + * @return string + */ + public function getStubContent() + { + $content = ''; + $filename = $this->getStub(); + if ($filename) { + $content = file_get_contents($this->builder->buildPath . DIRECTORY_SEPARATOR . $this->getStub()); + } + return $content; + } + + /** + * Run the phar plugin. + * @return bool + */ + public function execute() + { + $success = false; + + try { + $file = $this->getDirectory() . DIRECTORY_SEPARATOR . $this->getFilename(); + $phar = new PHPPhar($file, 0, $this->getFilename()); + $phar->buildFromDirectory($this->builder->buildPath, $this->getRegExp()); + + $stub = $this->getStubContent(); + if ($stub) { + $phar->setStub($stub); + } + + $success = true; + } catch (\Exception $e) { + $this->builder->log('Phar Plugin Internal Error'); + $this->builder->log($e->getMessage()); + } + + return $success; + } +} diff --git a/src/PHPCensor/Plugin/Phing.php b/src/PHPCensor/Plugin/Phing.php new file mode 100644 index 0000000..dc12dce --- /dev/null +++ b/src/PHPCensor/Plugin/Phing.php @@ -0,0 +1,237 @@ + + */ +class Phing extends Plugin +{ + protected $directory; + protected $buildFile = 'build.xml'; + protected $targets = ['build']; + protected $properties = []; + protected $propertyFile; + + /** + * @return string + */ + public static function pluginName() + { + return 'phing'; + } + + /** + * {@inheritdoc} + */ + public function __construct(Builder $builder, Build $build, array $options = []) + { + parent::__construct($builder, $build, $options); + + /* + * Set working directory + */ + if (isset($options['directory'])) { + $directory = $this->builder->buildPath . DIRECTORY_SEPARATOR . $options['directory']; + } else { + $directory = $this->builder->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']); + } + } + + /** + * Executes Phing and runs a specified targets + */ + public function execute() + { + $phingExecutable = $this->findBinary('phing'); + + $cmd[] = $phingExecutable . ' -f ' . $this->getBuildFilePath(); + + if ($this->getPropertyFile()) { + $cmd[] = '-propertyfile ' . $this->getPropertyFile(); + } + + $cmd[] = $this->propertiesToString(); + + $cmd[] = '-logger phing.listener.DefaultLogger'; + $cmd[] = $this->targetsToString(); + $cmd[] = '2>&1'; + + return $this->builder->executeCommand(implode(' ', $cmd), $this->directory, $this->targets); + } + + /** + * @return string + */ + public function getDirectory() + { + return $this->directory; + } + + /** + * @param string $directory + * + * @return $this + */ + public function setDirectory($directory) + { + $this->directory = $directory; + } + + /** + * @return string + */ + public function getTargets() + { + return $this->targets; + } + + /** + * Converts an array of targets into a string. + * @return string + */ + private function targetsToString() + { + return implode(' ', $this->targets); + } + + /** + * @param array|string $targets + * + * @return $this + */ + public function setTargets($targets) + { + if (is_string($targets)) { + $targets = [$targets]; + } + + $this->targets = $targets; + } + + /** + * @return string + */ + public function getBuildFile() + { + return $this->buildFile; + } + + /** + * @param mixed $buildFile + * + * @return $this + * @throws \Exception + */ + public function setBuildFile($buildFile) + { + if (!file_exists($this->getDirectory() . $buildFile)) { + throw new \Exception('Specified build file does not exist.'); + } + + $this->buildFile = $buildFile; + } + + /** + * Get phing build file path. + * @return string + */ + public function getBuildFilePath() + { + return $this->getDirectory() . $this->buildFile; + } + + /** + * @return mixed + */ + public function getProperties() + { + return $this->properties; + } + + /** + * @return string + */ + public function propertiesToString() + { + /** + * fix the problem when execute phing out of the build dir + * @ticket 748 + */ + if (!isset($this->properties['project.basedir'])) { + $this->properties['project.basedir'] = $this->getDirectory(); + } + + $propertiesString = []; + + 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 = [$properties]; + } + + $this->properties = $properties; + } + + /** + * @return string + */ + public function getPropertyFile() + { + return $this->propertyFile; + } + + /** + * @param string $propertyFile + * + * @return $this + * @throws \Exception + */ + public function setPropertyFile($propertyFile) + { + if (!file_exists($this->getDirectory() . DIRECTORY_SEPARATOR . $propertyFile)) { + throw new \Exception('Specified property file does not exist.'); + } + + $this->propertyFile = $propertyFile; + } +} diff --git a/src/PHPCensor/Plugin/PhpCodeSniffer.php b/src/PHPCensor/Plugin/PhpCodeSniffer.php new file mode 100644 index 0000000..6828acd --- /dev/null +++ b/src/PHPCensor/Plugin/PhpCodeSniffer.php @@ -0,0 +1,287 @@ + + */ +class PhpCodeSniffer extends Plugin implements ZeroConfigPluginInterface +{ + /** + * @var array + */ + protected $suffixes; + + /** + * @var string + */ + protected $directory; + + /** + * @var string + */ + protected $standard; + + /** + * @var string + */ + protected $tab_width; + + /** + * @var string + */ + protected $encoding; + + /** + * @var int + */ + protected $allowed_errors; + + /** + * @var int + */ + protected $allowed_warnings; + + /** + * @var string, based on the assumption the root may not hold the code to be tested, extends the base path + */ + protected $path; + + /** + * @var array - paths to ignore + */ + protected $ignore; + + /** + * @var int + */ + protected $severity = null; + /** + * @var null|int + */ + protected $error_severity = null; + + /** + * @var null|int + */ + protected $warning_severity = null; + + /** + * @return string + */ + public static function pluginName() + { + return 'php_code_sniffer'; + } + + /** + * {@inheritdoc} + */ + public function __construct(Builder $builder, Build $build, array $options = []) + { + parent::__construct($builder, $build, $options); + + $this->suffixes = ['php']; + $this->directory = $this->builder->buildPath; + $this->standard = 'PSR2'; + $this->tab_width = ''; + $this->encoding = ''; + $this->path = ''; + $this->ignore = $this->builder->ignore; + $this->allowed_warnings = 0; + $this->allowed_errors = 0; + + if (isset($options['zero_config']) && $options['zero_config']) { + $this->allowed_warnings = -1; + $this->allowed_errors = -1; + } + + if (!empty($options['allowed_errors']) && is_int($options['allowed_errors'])) { + $this->allowed_errors = $options['allowed_errors']; + } + + if (!empty($options['allowed_warnings']) && is_int($options['allowed_warnings'])) { + $this->allowed_warnings = $options['allowed_warnings']; + } + + if (isset($options['suffixes'])) { + $this->suffixes = (array)$options['suffixes']; + } + + if (!empty($options['tab_width'])) { + $this->tab_width = ' --tab-width='.$options['tab_width']; + } + + if (!empty($options['encoding'])) { + $this->encoding = ' --encoding=' . $options['encoding']; + } + + if (!empty($options['ignore'])) { + $this->ignore = (array)$options['ignore']; + } + + if (!empty($options['standard'])) { + $this->standard = $options['standard']; + } + + if (isset($options['severity']) && is_int($options['severity'])) { + $this->severity = $options['severity']; + } + + if (isset($options['error_severity']) && is_int($options['error_severity'])) { + $this->error_severity = $options['error_severity']; + } + + if (isset($options['warning_severity']) && is_int($options['warning_severity'])) { + $this->warning_severity = $options['warning_severity']; + } + } + + /** + * Check if this plugin can be executed. + * + * @param $stage + * @param Builder $builder + * @param Build $build + * + * @return bool + */ + public static function canExecute($stage, Builder $builder, Build $build) + { + if ($stage == Build::STAGE_TEST) { + return true; + } + + return false; + } + + /** + * Runs PHP Code Sniffer in a specified directory, to a specified standard. + */ + public function execute() + { + list($ignore, $standard, $suffixes, $severity, $errorSeverity, $warningSeverity) = $this->getFlags(); + + $phpcs = $this->findBinary('phpcs'); + + $this->builder->logExecOutput(false); + + $cmd = $phpcs . ' --report=json %s %s %s %s %s "%s" %s %s %s'; + $this->builder->executeCommand( + $cmd, + $standard, + $suffixes, + $ignore, + $this->tab_width, + $this->encoding, + $this->builder->buildPath . $this->path, + $severity, + $errorSeverity, + $warningSeverity + ); + + $output = $this->builder->getLastOutput(); + list($errors, $warnings) = $this->processReport($output); + + $this->builder->logExecOutput(true); + + $success = true; + $this->build->storeMeta('phpcs-warnings', $warnings); + $this->build->storeMeta('phpcs-errors', $errors); + + if ($this->allowed_warnings != -1 && $warnings > $this->allowed_warnings) { + $success = false; + } + + if ($this->allowed_errors != -1 && $errors > $this->allowed_errors) { + $success = false; + } + + return $success; + } + + /** + * Process options and produce an arguments string for PHPCS. + * @return array + */ + protected function getFlags() + { + $ignore = ''; + if (count($this->ignore)) { + $ignore = ' --ignore=' . implode(',', $this->ignore); + } + + if (strpos($this->standard, '/') !== false) { + $standard = ' --standard=' . $this->directory.$this->standard; + } else { + $standard = ' --standard=' . $this->standard; + } + + $suffixes = ''; + if (count($this->suffixes)) { + $suffixes = ' --extensions=' . implode(',', $this->suffixes); + } + + $severity = ''; + if ($this->severity !== null) { + $severity = ' --severity=' . $this->severity; + } + + $errorSeverity = ''; + if ($this->error_severity !== null) { + $errorSeverity = ' --error-severity=' . $this->error_severity; + } + + $warningSeverity = ''; + if ($this->warning_severity !== null) { + $warningSeverity = ' --warning-severity=' . $this->warning_severity; + } + + return [$ignore, $standard, $suffixes, $severity, $errorSeverity, $warningSeverity]; + } + + /** + * Process the PHPCS output report. + * @param $output + * @return array + * @throws \Exception + */ + protected function processReport($output) + { + $data = json_decode(trim($output), true); + + if (!is_array($data)) { + $this->builder->log($output); + throw new \Exception('Could not process the report generated by PHP Code Sniffer.'); + } + + $errors = $data['totals']['errors']; + $warnings = $data['totals']['warnings']; + + foreach ($data['files'] as $fileName => $file) { + $fileName = str_replace($this->builder->buildPath, '', $fileName); + + foreach ($file['messages'] as $message) { + $this->build->reportError( + $this->builder, + 'php_code_sniffer', + 'PHPCS: ' . $message['message'], + $message['type'] == 'ERROR' ? BuildError::SEVERITY_HIGH : BuildError::SEVERITY_LOW, + $fileName, + $message['line'] + ); + } + } + + return [$errors, $warnings]; + } +} diff --git a/src/PHPCensor/Plugin/PhpCpd.php b/src/PHPCensor/Plugin/PhpCpd.php new file mode 100644 index 0000000..4094d16 --- /dev/null +++ b/src/PHPCensor/Plugin/PhpCpd.php @@ -0,0 +1,164 @@ + + */ +class PhpCpd extends Plugin implements ZeroConfigPluginInterface +{ + protected $directory; + protected $args; + + /** + * @var string, based on the assumption the root may not hold the code to be + * tested, extends the base path + */ + protected $path; + + /** + * @var array - paths to ignore + */ + protected $ignore; + + /** + * @return string + */ + public static function pluginName() + { + return 'php_cpd'; + } + + /** + * {@inheritdoc} + */ + public function __construct(Builder $builder, Build $build, array $options = []) + { + parent::__construct($builder, $build, $options); + + $this->path = $this->builder->buildPath; + $this->ignore = $this->builder->ignore; + + if (!empty($options['path'])) { + $this->path = $this->builder->buildPath . $options['path']; + } + + if (!empty($options['ignore'])) { + $this->ignore = $options['ignore']; + } + } + + /** + * Check if this plugin can be executed. + * + * @param $stage + * @param Builder $builder + * @param Build $build + * + * @return bool + */ + public static function canExecute($stage, Builder $builder, Build $build) + { + if ($stage == Build::STAGE_TEST) { + return true; + } + + return false; + } + + /** + * Runs PHP Copy/Paste Detector in a specified directory. + */ + public function execute() + { + $ignore = ''; + $namesExclude = ' --names-exclude '; + + foreach ($this->ignore as $item) { + $item = rtrim($item, DIRECTORY_SEPARATOR); + if (is_file(rtrim($this->path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $item)) { + $ignoredFile = explode('/', $item); + $filesToIgnore[] = array_pop($ignoredFile); + } else { + $ignore .= ' --exclude ' . $item; + } + } + + if (isset($filesToIgnore)) { + $filesToIgnore = $namesExclude . implode(',', $filesToIgnore); + $ignore = $ignore . $filesToIgnore; + } + + $phpcpd = $this->findBinary('phpcpd'); + + $tmpFileName = tempnam('/tmp', 'phpcpd'); + + $cmd = $phpcpd . ' --log-pmd "%s" %s "%s"'; + $success = $this->builder->executeCommand($cmd, $tmpFileName, $ignore, $this->path); + + $errorCount = $this->processReport(file_get_contents($tmpFileName)); + + $this->build->storeMeta('phpcpd-warnings', $errorCount); + + unlink($tmpFileName); + + return $success; + } + + /** + * Process the PHPCPD XML report. + * + * @param $xmlString + * + * @return integer + * + * @throws \Exception + */ + protected function processReport($xmlString) + { + $xml = simplexml_load_string($xmlString); + + if ($xml === false) { + $this->builder->log($xmlString); + throw new \Exception('Could not process the report generated by PHPCpd.'); + } + + $warnings = 0; + foreach ($xml->duplication as $duplication) { + foreach ($duplication->file as $file) { + $fileName = (string)$file['path']; + $fileName = str_replace($this->builder->buildPath, '', $fileName); + + $message = <<codefragment} +``` +CPD; + + $this->build->reportError( + $this->builder, + 'php_cpd', + $message, + BuildError::SEVERITY_NORMAL, + $fileName, + (int)$file['line'], + (int)$file['line'] + (int)$duplication['lines'] + ); + } + + $warnings++; + } + + return $warnings; + } +} diff --git a/src/PHPCensor/Plugin/PhpCsFixer.php b/src/PHPCensor/Plugin/PhpCsFixer.php new file mode 100644 index 0000000..65047cd --- /dev/null +++ b/src/PHPCensor/Plugin/PhpCsFixer.php @@ -0,0 +1,98 @@ + + */ +class PhpCsFixer extends Plugin +{ + protected $directory = null; + protected $args = ''; + + protected $config = false; + protected $configs = [ + '.php_cs', + '.php_cs.dist', + ]; + + /** + * @return string + */ + public static function pluginName() + { + return 'php_cs_fixer'; + } + + /** + * {@inheritdoc} + */ + public function __construct(Builder $builder, Build $build, array $options = []) + { + parent::__construct($builder, $build, $options); + + if (!empty($options['args'])) { + $this->args = $options['args']; + } + + if (isset($options['verbose']) && $options['verbose']) { + $this->args .= ' --verbose'; + } + + if (isset($options['diff']) && $options['diff']) { + $this->args .= ' --diff'; + } + + if (isset($options['rules']) && $options['rules']) { + $this->args .= ' --rules=' . $options['rules']; + } + + if (isset($options['config']) && $options['config']) { + $this->config = true; + $this->args .= ' --config=' . $builder->interpolate($options['config']); + } + + if (isset($options['directory']) && $options['directory']) { + $this->directory = $builder->interpolate($options['directory']); + } + } + + /** + * Run PHP CS Fixer. + * + * @return boolean + */ + public function execute() + { + $directory = ''; + if (!empty($this->directory)) { + $directory = $this->directory; + } + + if (!$this->config) { + foreach ($this->configs as $config) { + if (file_exists($this->builder->buildPath . '/' . $config)) { + $this->config = true; + $this->args .= ' --config=./' . $config; + break; + } + } + } + + if (!$this->config && !$directory) { + $directory = '.'; + } + + $phpCsFixer = $this->findBinary('php-cs-fixer'); + $cmd = $phpCsFixer . ' fix ' . $directory . ' %s'; + $success = $this->builder->executeCommand($cmd, $this->args); + + return $success; + } +} diff --git a/src/PHPCensor/Plugin/PhpDocblockChecker.php b/src/PHPCensor/Plugin/PhpDocblockChecker.php new file mode 100644 index 0000000..19640ac --- /dev/null +++ b/src/PHPCensor/Plugin/PhpDocblockChecker.php @@ -0,0 +1,175 @@ + + */ +class PhpDocblockChecker extends Plugin implements ZeroConfigPluginInterface +{ + /** + * @var string Based on the assumption the root may not hold the code to be + * tested, extends the build path. + */ + protected $path; + + /** + * @var array - paths to ignore + */ + protected $ignore; + + protected $skipClasses = false; + protected $skipMethods = false; + + /** + * @var integer + */ + protected $allowed_warnings; + + /** + * @return string + */ + public static function pluginName() + { + return 'php_docblock_checker'; + } + + /** + * {@inheritdoc} + */ + public function __construct(Builder $builder, Build $build, array $options = []) + { + parent::__construct($builder, $build, $options); + + $this->ignore = $this->builder->ignore; + $this->path = ''; + $this->allowed_warnings = 0; + + if (isset($options['zero_config']) && $options['zero_config']) { + $this->allowed_warnings = -1; + } + + if (array_key_exists('skip_classes', $options)) { + $this->skipClasses = true; + } + + if (array_key_exists('skip_methods', $options)) { + $this->skipMethods = true; + } + + if (!empty($options['path'])) { + $this->path = $options['path']; + } + + if (array_key_exists('allowed_warnings', $options)) { + $this->allowed_warnings = (int)$options['allowed_warnings']; + } + } + + /** + * Check if this plugin can be executed. + * @param $stage + * @param Builder $builder + * @param Build $build + * @return bool + */ + public static function canExecute($stage, Builder $builder, Build $build) + { + if ($stage == Build::STAGE_TEST) { + return true; + } + + return false; + } + + /** + * Runs PHP Mess Detector in a specified directory. + */ + public function execute() + { + // Check that the binary exists: + $checker = $this->findBinary('phpdoccheck'); + + // Build ignore string: + $ignore = ''; + if (count($this->ignore)) { + $ignore = ' --exclude="' . implode(',', $this->ignore) . '"'; + } + + // Are we skipping any checks? + $add = ''; + if ($this->skipClasses) { + $add .= ' --skip-classes'; + } + + if ($this->skipMethods) { + $add .= ' --skip-methods'; + } + + // Build command string: + $path = $this->builder->buildPath . $this->path; + $cmd = $checker . ' --json --directory="%s"%s%s'; + + // Disable exec output logging, as we don't want the XML report in the log: + $this->builder->logExecOutput(false); + + // Run checker: + $this->builder->executeCommand( + $cmd, + $path, + $ignore, + $add + ); + + // Re-enable exec output logging: + $this->builder->logExecOutput(true); + + $output = json_decode($this->builder->getLastOutput(), true); + $errors = count($output); + $success = true; + + $this->build->storeMeta('phpdoccheck-warnings', $errors); + $this->reportErrors($output); + + if ($this->allowed_warnings != -1 && $errors > $this->allowed_warnings) { + $success = false; + } + + return $success; + } + + /** + * Report all of the errors we've encountered line-by-line. + * @param $output + */ + protected function reportErrors($output) + { + foreach ($output as $error) { + $message = 'Class ' . $error['class'] . ' is missing a docblock.'; + $severity = BuildError::SEVERITY_LOW; + + if ($error['type'] == 'method') { + $message = $error['class'] . '::' . $error['method'] . ' is missing a docblock.'; + $severity = BuildError::SEVERITY_NORMAL; + } + + $this->build->reportError( + $this->builder, + 'php_docblock_checker', + $message, + $severity, + $error['file'], + $error['line'] + ); + } + } +} diff --git a/src/PHPCensor/Plugin/PhpLoc.php b/src/PHPCensor/Plugin/PhpLoc.php new file mode 100644 index 0000000..597514a --- /dev/null +++ b/src/PHPCensor/Plugin/PhpLoc.php @@ -0,0 +1,93 @@ + + */ +class PhpLoc extends Plugin implements ZeroConfigPluginInterface +{ + /** + * @var string + */ + protected $directory; + + /** + * @return string + */ + public static function pluginName() + { + return 'php_loc'; + } + + /** + * Check if this plugin can be executed. + * @param $stage + * @param Builder $builder + * @param Build $build + * @return bool + */ + public static function canExecute($stage, Builder $builder, Build $build) + { + if ($stage == Build::STAGE_TEST) { + return true; + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function __construct(Builder $builder, Build $build, array $options = []) + { + parent::__construct($builder, $build, $options); + + $this->directory = $this->builder->buildPath; + + if (isset($options['directory'])) { + $this->directory .= $options['directory']; + } + } + + /** + * Runs PHP Copy/Paste Detector in a specified directory. + */ + public function execute() + { + $ignore = ''; + + if (count($this->builder->ignore)) { + $map = function ($item) { + return ' --exclude ' . rtrim($item, DIRECTORY_SEPARATOR); + }; + + $ignore = array_map($map, $this->builder->ignore); + $ignore = implode('', $ignore); + } + + $phploc = $this->findBinary('phploc'); + + $success = $this->builder->executeCommand($phploc . ' %s "%s"', $ignore, $this->directory); + $output = $this->builder->getLastOutput(); + + if (preg_match_all('/\((LOC|CLOC|NCLOC|LLOC)\)\s+([0-9]+)/', $output, $matches)) { + $data = []; + foreach ($matches[1] as $k => $v) { + $data[$v] = (int)$matches[2][$k]; + } + + $this->build->storeMeta('phploc', $data); + } + + return $success; + } +} diff --git a/src/PHPCensor/Plugin/PhpMessDetector.php b/src/PHPCensor/Plugin/PhpMessDetector.php new file mode 100644 index 0000000..30134f2 --- /dev/null +++ b/src/PHPCensor/Plugin/PhpMessDetector.php @@ -0,0 +1,256 @@ + + */ +class PhpMessDetector extends Plugin implements ZeroConfigPluginInterface +{ + /** + * @var array + */ + protected $suffixes; + + /** + * @var string, based on the assumption the root may not hold the code to be + * tested, extends the base path only if the provided path is relative. Absolute + * paths are used verbatim + */ + protected $path; + + /** + * @var array - paths to ignore + */ + protected $ignore; + + /** + * Array of PHPMD rules. Can be one of the builtins (codesize, unusedcode, naming, design, controversial) + * or a filename (detected by checking for a / in it), either absolute or relative to the project root. + * @var array + */ + protected $rules; + protected $allowed_warnings; + + /** + * @return string + */ + public static function pluginName() + { + return 'php_mess_detector'; + } + + /** + * {@inheritdoc} + */ + public function __construct(Builder $builder, Build $build, array $options = []) + { + parent::__construct($builder, $build, $options); + + $this->suffixes = ['php']; + $this->ignore = $this->builder->ignore; + $this->path = ''; + $this->rules = ['codesize', 'unusedcode', 'naming']; + $this->allowed_warnings = 0; + + if (isset($options['zero_config']) && $options['zero_config']) { + $this->allowed_warnings = -1; + } + + if (!empty($options['path'])) { + $this->path = $options['path']; + } + + if (array_key_exists('allowed_warnings', $options)) { + $this->allowed_warnings = (int)$options['allowed_warnings']; + } + + foreach (['rules', 'ignore', 'suffixes'] as $key) { + $this->overrideSetting($options, $key); + } + } + + /** + * Check if this plugin can be executed. + * @param $stage + * @param Builder $builder + * @param Build $build + * @return bool + */ + public static function canExecute($stage, Builder $builder, Build $build) + { + if ($stage == Build::STAGE_TEST) { + return true; + } + + return false; + } + + /** + * Runs PHP Mess Detector in a specified directory. + */ + public function execute() + { + if (!$this->tryAndProcessRules()) { + return false; + } + + $phpmdBinaryPath = $this->findBinary('phpmd'); + + $this->executePhpMd($phpmdBinaryPath); + + $errorCount = $this->processReport(trim($this->builder->getLastOutput())); + $this->build->storeMeta('phpmd-warnings', $errorCount); + + return $this->wasLastExecSuccessful($errorCount); + } + + /** + * Override a default setting. + * @param $options + * @param $key + */ + protected function overrideSetting($options, $key) + { + if (isset($options[$key]) && is_array($options[$key])) { + $this->{$key} = $options[$key]; + } + } + + /** + * Process PHPMD's XML output report. + * + * @param $xmlString + * + * @return integer + * + * @throws \Exception + */ + protected function processReport($xmlString) + { + $xml = simplexml_load_string($xmlString); + + if ($xml === false) { + $this->builder->log($xmlString); + throw new \Exception('Could not process PHPMD report XML.'); + } + + $warnings = 0; + + foreach ($xml->file as $file) { + $fileName = (string)$file['name']; + $fileName = str_replace($this->builder->buildPath, '', $fileName); + + foreach ($file->violation as $violation) { + $warnings++; + + $this->build->reportError( + $this->builder, + 'php_mess_detector', + (string)$violation, + PHPCensor\Model\BuildError::SEVERITY_HIGH, + $fileName, + (int)$violation['beginline'], + (int)$violation['endline'] + ); + } + } + + return $warnings; + } + + /** + * Try and process the rules parameter from .php-censor.yml. + * @return bool + */ + protected function tryAndProcessRules() + { + if (!empty($this->rules) && !is_array($this->rules)) { + $this->builder->logFailure('The "rules" option must be an array.'); + return false; + } + + foreach ($this->rules as &$rule) { + if (strpos($rule, '/') !== false) { + $rule = $this->builder->buildPath . $rule; + } + } + + return true; + } + + /** + * Execute PHP Mess Detector. + * @param $binaryPath + */ + protected function executePhpMd($binaryPath) + { + $cmd = $binaryPath . ' "%s" xml %s %s %s'; + + $path = $this->getTargetPath(); + + $ignore = ''; + if (count($this->ignore)) { + $ignore = ' --exclude ' . implode(',', $this->ignore); + } + + $suffixes = ''; + if (count($this->suffixes)) { + $suffixes = ' --suffixes ' . implode(',', $this->suffixes); + } + + // Disable exec output logging, as we don't want the XML report in the log: + $this->builder->logExecOutput(false); + + // Run PHPMD: + $this->builder->executeCommand( + $cmd, + $path, + implode(',', $this->rules), + $ignore, + $suffixes + ); + + // Re-enable exec output logging: + $this->builder->logExecOutput(true); + } + + /** + * Get the path PHPMD should be run against. + * @return string + */ + protected function getTargetPath() + { + $path = $this->builder->buildPath . $this->path; + if (!empty($this->path) && $this->path{0} == '/') { + $path = $this->path; + return $path; + } + return $path; + } + + /** + * Returns a boolean indicating if the error count can be considered a success. + * + * @param int $errorCount + * @return bool + */ + protected function wasLastExecSuccessful($errorCount) + { + $success = true; + + if ($this->allowed_warnings != -1 && $errorCount > $this->allowed_warnings) { + $success = false; + return $success; + } + return $success; + } +} diff --git a/src/PHPCensor/Plugin/PhpParallelLint.php b/src/PHPCensor/Plugin/PhpParallelLint.php new file mode 100644 index 0000000..908e23f --- /dev/null +++ b/src/PHPCensor/Plugin/PhpParallelLint.php @@ -0,0 +1,143 @@ + + */ +class PhpParallelLint extends Plugin implements ZeroConfigPluginInterface +{ + /** + * @var string + */ + protected $directory; + + /** + * @var array - paths to ignore + */ + protected $ignore; + + /** + * @var string - comma separated list of file extensions + */ + protected $extensions; + + /** + * @var bool - enable short tags + */ + protected $shortTag; + + /** + * @return string + */ + public static function pluginName() + { + return 'php_parallel_lint'; + } + + /** + * $options['directory'] Output Directory. Default: %BUILDPATH% + * $options['filename'] Phar Filename. Default: build.phar + * $options['extensions'] Filename extensions. Default: php + * $options['shorttags'] Enable short tags. Default: false + * $options['stub'] Stub Content. No Default Value + */ + public function __construct(Builder $builder, Build $build, array $options = []) + { + parent::__construct($builder, $build, $options); + + $this->directory = $this->builder->buildPath; + $this->ignore = $this->builder->ignore; + $this->extensions = 'php'; + $this->shortTag = false; + + if (isset($options['directory'])) { + $this->directory = $this->builder->buildPath.$options['directory']; + } + + if (isset($options['ignore'])) { + $this->ignore = $options['ignore']; + } + + if (isset($options['shorttags'])) { + $this->shortTag = (strtolower($options['shorttags']) == 'true'); + } + + if (isset($options['extensions'])) { + // Only use if this is a comma delimited list + $pattern = '/^([a-z]+)(,\ *[a-z]*)*$/'; + + if (preg_match($pattern, $options['extensions'])) { + $this->extensions = str_replace(' ', '', $options['extensions']); + } + } + } + + /** + * Check if this plugin can be executed. + * + * @param $stage + * @param Builder $builder + * @param Build $build + * + * @return bool + */ + public static function canExecute($stage, Builder $builder, Build $build) + { + if ($stage == Build::STAGE_TEST) { + return true; + } + + return false; + } + + /** + * Executes parallel lint + */ + public function execute() + { + list($ignore) = $this->getFlags(); + + $phplint = $this->findBinary('parallel-lint'); + + $cmd = $phplint . ' -e %s' . '%s %s "%s"'; + $success = $this->builder->executeCommand( + $cmd, + $this->extensions, + ($this->shortTag ? ' -s' : ''), + $ignore, + $this->directory + ); + + $output = $this->builder->getLastOutput(); + + $matches = []; + if (preg_match_all('/Parse error\:/', $output, $matches)) { + $this->build->storeMeta('phplint-errors', count($matches[0])); + } + + return $success; + } + + /** + * Produce an argument string for PHP Parallel Lint. + * @return array + */ + protected function getFlags() + { + $ignoreFlags = []; + foreach ($this->ignore as $ignoreDir) { + $ignoreFlags[] = '--exclude "' . $this->builder->buildPath . $ignoreDir . '"'; + } + $ignore = implode(' ', $ignoreFlags); + + return [$ignore]; + } +} diff --git a/src/PHPCensor/Plugin/PhpSpec.php b/src/PHPCensor/Plugin/PhpSpec.php new file mode 100644 index 0000000..97cafef --- /dev/null +++ b/src/PHPCensor/Plugin/PhpSpec.php @@ -0,0 +1,118 @@ + + */ +class PhpSpec extends Plugin +{ + /** + * @return string + */ + public static function pluginName() + { + return 'php_spec'; + } + + /** + * Runs PHP Spec tests. + */ + public function execute() + { + $curdir = getcwd(); + chdir($this->builder->buildPath); + + $phpspec = $this->findBinary(['phpspec', 'phpspec.php']); + + $success = $this->builder->executeCommand($phpspec . ' --format=junit --no-code-generation run'); + $output = $this->builder->getLastOutput(); + + chdir($curdir); + + /* + * process xml output + * + * + * + * + * + * attributes(); + $data = [ + 'time' => (float)$attr['time'], + 'tests' => (int)$attr['tests'], + 'failures' => (int)$attr['failures'], + 'errors' => (int)$attr['errors'], + // now all the tests + 'suites' => [] + ]; + + /** + * @var \SimpleXMLElement $group + */ + foreach ($xml->xpath('testsuite') as $group) { + $attr = $group->attributes(); + $suite = [ + 'name' => (String)$attr['name'], + 'time' => (float)$attr['time'], + 'tests' => (int)$attr['tests'], + 'failures' => (int)$attr['failures'], + 'errors' => (int)$attr['errors'], + 'skipped' => (int)$attr['skipped'], + // now the cases + 'cases' => [] + ]; + + /** + * @var \SimpleXMLElement $child + */ + foreach ($group->xpath('testcase') as $child) { + $attr = $child->attributes(); + $case = [ + 'name' => (String)$attr['name'], + 'classname' => (String)$attr['classname'], + 'time' => (float)$attr['time'], + 'status' => (String)$attr['status'], + ]; + + if ($case['status']=='failed') { + $error = []; + /* + * ok, sad, we had an error + * + * there should be one - foreach makes this easier + */ + foreach ($child->xpath('failure') as $failure) { + $attr = $failure->attributes(); + $error['type'] = (String)$attr['type']; + $error['message'] = (String)$attr['message']; + } + + foreach ($child->xpath('system-err') as $system_err) { + $error['raw'] = (String)$system_err; + } + + $case['error'] = $error; + } + + $suite['cases'][] = $case; + } + + $data['suites'][] = $suite; + } + + $this->build->storeMeta('phpspec', $data); + + + return $success; + } +} diff --git a/src/PHPCensor/Plugin/PhpTalLint.php b/src/PHPCensor/Plugin/PhpTalLint.php new file mode 100644 index 0000000..636df1e --- /dev/null +++ b/src/PHPCensor/Plugin/PhpTalLint.php @@ -0,0 +1,237 @@ + + */ +class PhpTalLint extends Plugin +{ + protected $directories; + protected $recursive = true; + protected $suffixes; + protected $ignore; + + /** + * @return string + */ + public static function pluginName() + { + return 'php_tal_lint'; + } + + /** + * @var string The path to a file contain custom phptal_tales_ functions + */ + protected $tales; + + /** + * @var int + */ + protected $allowed_warnings; + + /** + * @var int + */ + protected $allowed_errors; + + /** + * @var array The results of the lint scan + */ + protected $failedPaths = []; + + /** + * {@inheritdoc} + */ + public function __construct(Builder $builder, Build $build, array $options = []) + { + parent::__construct($builder, $build, $options); + + $this->directories = ['']; + $this->suffixes = ['zpt']; + $this->ignore = $this->builder->ignore; + + $this->allowed_warnings = 0; + $this->allowed_errors = 0; + + if (!empty($options['directory'])) { + $this->directories = [$options['directory']]; + } + + if (isset($options['suffixes'])) { + $this->suffixes = (array)$options['suffixes']; + } + } + + /** + * Executes phptal lint + */ + public function execute() + { + $this->builder->quiet = true; + $this->builder->logExecOutput(false); + + foreach ($this->directories as $dir) { + $this->lintDirectory($dir); + } + + $this->builder->quiet = false; + $this->builder->logExecOutput(true); + + $errors = 0; + $warnings = 0; + + foreach ($this->failedPaths as $path) { + if ($path['type'] == 'error') { + $errors++; + } else { + $warnings++; + } + } + + $this->build->storeMeta('phptallint-warnings', $warnings); + $this->build->storeMeta('phptallint-errors', $errors); + $this->build->storeMeta('phptallint-data', $this->failedPaths); + + $success = true; + + if ($this->allowed_warnings != -1 && $warnings > $this->allowed_warnings) { + $success = false; + } + + if ($this->allowed_errors != -1 && $errors > $this->allowed_errors) { + $success = false; + } + + return $success; + } + + /** + * Lint an item (file or directory) by calling the appropriate method. + * @param $item + * @param $itemPath + * @return bool + */ + protected function lintItem($item, $itemPath) + { + $success = true; + + if ($item->isFile() && in_array(strtolower($item->getExtension()), $this->suffixes)) { + if (!$this->lintFile($itemPath)) { + $success = false; + } + } elseif ($item->isDir() && $this->recursive && !$this->lintDirectory($itemPath . DIRECTORY_SEPARATOR)) { + $success = false; + } + + return $success; + } + + /** + * Run phptal lint against a directory of files. + * @param $path + * @return bool + */ + protected function lintDirectory($path) + { + $success = true; + $directory = new \DirectoryIterator($this->builder->buildPath . $path); + + foreach ($directory as $item) { + if ($item->isDot()) { + continue; + } + + $itemPath = $path . $item->getFilename(); + + if (in_array($itemPath, $this->ignore)) { + continue; + } + + if (!$this->lintItem($item, $itemPath)) { + $success = false; + } + } + + return $success; + } + + /** + * Run phptal lint against a specific file. + * @param $path + * @return bool + */ + protected function lintFile($path) + { + $success = true; + + list($suffixes, $tales) = $this->getFlags(); + + $lint = __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR; + $lint .= 'vendor' . DIRECTORY_SEPARATOR . 'phptal' . DIRECTORY_SEPARATOR . 'phptal' . DIRECTORY_SEPARATOR; + $lint .= 'tools' . DIRECTORY_SEPARATOR . 'phptal_lint.php'; + $cmd = '/usr/bin/env php ' . $lint . ' %s %s "%s"'; + + $this->builder->executeCommand($cmd, $suffixes, $tales, $this->builder->buildPath . $path); + + $output = $this->builder->getLastOutput(); + + if (preg_match('/Found (.+?) (error|warning)/i', $output, $matches)) { + $rows = explode(PHP_EOL, $output); + + unset($rows[0]); + unset($rows[1]); + unset($rows[2]); + unset($rows[3]); + + foreach ($rows as $row) { + $name = basename($path); + + $row = str_replace('(use -i to include your custom modifier functions)', '', $row); + $message = str_replace($name . ': ', '', $row); + + $parts = explode(' (line ', $message); + + $message = trim($parts[0]); + $line = str_replace(')', '', $parts[1]); + + $this->failedPaths[] = [ + 'file' => $path, + 'line' => $line, + 'type' => $matches[2], + 'message' => $message + ]; + } + + $success = false; + } + + return $success; + } + + /** + * Process options and produce an arguments string for PHPTAL Lint. + * @return array + */ + protected function getFlags() + { + $tales = ''; + if (!empty($this->tales)) { + $tales = ' -i ' . $this->builder->buildPath . $this->tales; + } + + $suffixes = ''; + if (count($this->suffixes)) { + $suffixes = ' -e ' . implode(',', $this->suffixes); + } + + return [$suffixes, $tales]; + } +} diff --git a/src/PHPCensor/Plugin/PhpUnit.php b/src/PHPCensor/Plugin/PhpUnit.php new file mode 100644 index 0000000..72e362b --- /dev/null +++ b/src/PHPCensor/Plugin/PhpUnit.php @@ -0,0 +1,177 @@ + + * @author Pablo Tejada + */ +class PhpUnit extends Plugin implements ZeroConfigPluginInterface +{ + /** @var string[] Raw options from the config file */ + protected $options = []; + + /** + * @return string + */ + public static function pluginName() + { + return 'php_unit'; + } + + /** + * Standard Constructor + * $options['config'] Path to a PHPUnit XML configuration file. + * $options['run_from'] The directory where the phpunit command will run from when using 'config'. + * $options['coverage'] Value for the --coverage-html command line flag. + * $options['directory'] Optional directory or list of directories to run PHPUnit on. + * $options['args'] Command line args (in string format) to pass to PHP Unit + * + * @param Builder $builder + * @param Build $build + * @param string[] $options + */ + public function __construct(Builder $builder, Build $build, array $options = []) + { + parent::__construct($builder, $build, $options); + + $this->options = new PhpUnitOptions($options); + } + + /** + * Check if the plugin can be executed without any configurations + * + * @param $stage + * @param Builder $builder + * @param Build $build + * + * @return bool + */ + public static function canExecute($stage, Builder $builder, Build $build) + { + if ($stage == Build::STAGE_TEST && !is_null(PhpUnitOptions::findConfigFile($build->getBuildPath()))) { + return true; + } + + return false; + } + + /** + * Runs PHP Unit tests in a specified directory, optionally using specified config file(s). + */ + public function execute() + { + $xmlConfigFiles = $this->options->getConfigFiles($this->build->getBuildPath()); + $directories = $this->options->getDirectories(); + if (empty($xmlConfigFiles) && empty($directories)) { + $this->builder->logFailure('Neither a configuration file nor a test directory found.'); + return false; + } + + $cmd = $this->findBinary('phpunit'); + // run without logging + $ret = null; + $lastLine = exec($cmd.' --log-json . --version'); + if (false !== strpos($lastLine, '--log-json')) { + $logFormat = 'junit'; // --log-json is not supported + } else { + $logFormat = 'json'; + } + + $success = []; + + // Run any directories + if (!empty($directories)) { + foreach ($directories as $directory) { + $success[] = $this->runConfig($directory, null, $logFormat); + } + } else { + // Run any config files + if (!empty($xmlConfigFiles)) { + foreach ($xmlConfigFiles as $configFile) { + $success[] = $this->runConfig($this->options->getTestsPath(), $configFile, $logFormat); + } + } + } + + return !in_array(false, $success); + } + + /** + * Run the tests defined in a PHPUnit config file or in a specific directory. + * + * @param $directory + * @param $configFile + * @param string $logFormat + * + * @return bool|mixed + */ + protected function runConfig($directory, $configFile, $logFormat) + { + $options = clone $this->options; + $buildPath = $this->build->getBuildPath(); + + // Save the results into a log file + $logFile = @tempnam($buildPath, 'jLog_'); + $options->addArgument('log-'.$logFormat, $logFile); + + // Removes any current configurations files + $options->removeArgument('configuration'); + if (null !== $configFile) { + // Only the add the configuration file been passed + $options->addArgument('configuration', $buildPath . $configFile); + } + + $arguments = $this->builder->interpolate($options->buildArgumentString()); + $cmd = $this->findBinary('phpunit') . ' %s %s'; + $success = $this->builder->executeCommand($cmd, $arguments, $directory); + + $this->processResults($logFile, $logFormat); + + return $success; + } + + /** + * Saves the test results + * + * @param string $logFile + * @param string $logFormat + * + * @throws \Exception If failed to parse the log file + */ + protected function processResults($logFile, $logFormat) + { + if (file_exists($logFile)) { + if ('json' === $logFormat) { + $parser = new PhpUnitResultJson($logFile, $this->build->getBuildPath()); + } else { + $parser = new PhpUnitResultJunit($logFile, $this->build->getBuildPath()); + } + + $this->build->storeMeta('phpunit-data', $parser->parse()->getResults()); + $this->build->storeMeta('phpunit-errors', $parser->getFailures()); + + foreach ($parser->getErrors() as $error) { + $severity = $error['severity'] == $parser::SEVERITY_ERROR ? BuildError::SEVERITY_CRITICAL : BuildError::SEVERITY_HIGH; + $this->build->reportError( + $this->builder, 'php_unit', $error['message'], $severity, $error['file'], $error['line'] + ); + } + @unlink($logFile); + } else { + throw new \Exception('log output file does not exist: ' . $logFile); + } + } +} diff --git a/src/PHPCensor/Plugin/SecurityChecker.php b/src/PHPCensor/Plugin/SecurityChecker.php new file mode 100644 index 0000000..1568ab7 --- /dev/null +++ b/src/PHPCensor/Plugin/SecurityChecker.php @@ -0,0 +1,98 @@ + + */ +class SecurityChecker extends Plugin implements ZeroConfigPluginInterface +{ + /** + * @var integer + */ + protected $allowed_warnings; + + /** + * @return string + */ + public static function pluginName() + { + return 'security_checker'; + } + + /** + * {@inheritdoc} + */ + public function __construct(Builder $builder, Build $build, array $options = []) + { + parent::__construct($builder, $build, $options); + + $this->allowed_warnings = 0; + + if (isset($options['zero_config']) && $options['zero_config']) { + $this->allowed_warnings = -1; + } + + if (array_key_exists('allowed_warnings', $options)) { + $this->allowed_warnings = (int)$options['allowed_warnings']; + } + } + + /** + * Check if this plugin can be executed. + * + * @param $stage + * @param Builder $builder + * @param Build $build + * + * @return bool + */ + public static function canExecute($stage, Builder $builder, Build $build) + { + $path = $builder->buildPath . DIRECTORY_SEPARATOR . 'composer.lock'; + + if (file_exists($path) && $stage == Build::STAGE_TEST) { + return true; + } + + return false; + } + + public function execute() + { + $success = true; + $checker = new BaseSecurityChecker(); + $warnings = $checker->check($this->builder->buildPath . DIRECTORY_SEPARATOR . 'composer.lock'); + + if ($warnings) { + foreach ($warnings as $library => $warning) { + foreach ($warning['advisories'] as $advisory => $data) { + $this->build->reportError( + $this->builder, + 'security_checker', + $library . ' (' . $warning['version'] . ")\n" . $data['cve'] . ': ' . $data['title'] . "\n" . $data['link'], + BuildError::SEVERITY_CRITICAL, + '-', + '-' + ); + } + } + + if ($this->allowed_warnings != -1 && ((int)$checker->getLastVulnerabilityCount() > $this->allowed_warnings)) { + $success = false; + } + } + + return $success; + } +} diff --git a/src/PHPCensor/Plugin/Shell.php b/src/PHPCensor/Plugin/Shell.php new file mode 100644 index 0000000..9ae7b1b --- /dev/null +++ b/src/PHPCensor/Plugin/Shell.php @@ -0,0 +1,77 @@ + + */ +class Shell extends Plugin +{ + /** + * @var array + */ + protected $args; + + /** + * @var string[] $commands The commands to be executed + */ + protected $commands = []; + + /** + * @return string + */ + public static function pluginName() + { + return 'shell'; + } + + /** + * {@inheritdoc} + */ + public function __construct(Builder $builder, Build $build, array $options = []) + { + parent::__construct($builder, $build, $options); + + if (isset($options['command'])) { + // Keeping this for backwards compatibility, new projects should use interpolation vars. + $options['command'] = str_replace("%buildpath%", $this->builder->buildPath, $options['command']); + $this->commands = [$options['command']]; + return; + } + + /* + * Support the new syntax: + * + * shell: + * - "cd /www" + * - "rm -f file.txt" + */ + if (is_array($options)) { + $this->commands = $options; + } + } + + /** + * Runs the shell command. + * + * @return bool + */ + public function execute() + { + foreach ($this->commands as $command) { + $command = $this->builder->interpolate($command); + + if (!$this->builder->executeCommand($command)) { + return false; + } + } + + return true; + } +} diff --git a/src/PHPCensor/Plugin/SlackNotify.php b/src/PHPCensor/Plugin/SlackNotify.php new file mode 100644 index 0000000..a3ff12b --- /dev/null +++ b/src/PHPCensor/Plugin/SlackNotify.php @@ -0,0 +1,137 @@ + + */ +class SlackNotify extends Plugin +{ + private $webHook; + private $room; + private $username; + private $message; + private $icon; + private $show_status; + + /** + * @return string + */ + public static function pluginName() + { + return 'slack_notify'; + } + + /** + * {@inheritdoc} + */ + public function __construct(Builder $builder, Build $build, array $options = []) + { + parent::__construct($builder, $build, $options); + + if (is_array($options) && isset($options['webhook_url'])) { + $this->webHook = trim($options['webhook_url']); + + if (isset($options['message'])) { + $this->message = $options['message']; + } else { + $this->message = '<%PROJECT_URI%|%PROJECT_TITLE%> - <%BUILD_URI%|Build #%BUILD%> has finished '; + $this->message .= 'for commit <%COMMIT_URI%|%SHORT_COMMIT% (%COMMIT_EMAIL%)> '; + $this->message .= 'on branch <%BRANCH_URI%|%BRANCH%>'; + } + + if (isset($options['room'])) { + $this->room = $options['room']; + } else { + $this->room = '#php-censor'; + } + + if (isset($options['username'])) { + $this->username = $options['username']; + } else { + $this->username = 'PHP Censor'; + } + + if (isset($options['show_status'])) { + $this->show_status = (bool) $options['show_status']; + } else { + $this->show_status = true; + } + + if (isset($options['icon'])) { + $this->icon = $options['icon']; + } + } else { + throw new \Exception('Please define the webhook_url for slack_notify plugin!'); + } + } + + /** + * Run the Slack plugin. + * @return bool + */ + public function execute() + { + $body = $this->builder->interpolate($this->message); + + $client = new Client($this->webHook); + + $message = $client->createMessage(); + + if (!empty($this->room)) { + $message->setChannel($this->room); + } + + if (!empty($this->username)) { + $message->setUsername($this->username); + } + + if (!empty($this->icon)) { + $message->setIcon($this->icon); + } + + // Include an attachment which shows the status and hide the message + if ($this->show_status) { + $successfulBuild = $this->build->isSuccessful(); + + if ($successfulBuild) { + $status = 'Success'; + $color = 'good'; + } else { + $status = 'Failed'; + $color = 'danger'; + } + + // Build up the attachment data + $attachment = new Attachment([ + 'fallback' => $body, + 'pretext' => $body, + 'color' => $color, + 'fields' => [ + new AttachmentField([ + 'title' => 'Status', + 'value' => $status, + 'short' => false + ]) + ] + ]); + + $message->attach($attachment); + + $body = ''; + } + + $message->send($body); + + return true; + } +} diff --git a/src/PHPCensor/Plugin/Sqlite.php b/src/PHPCensor/Plugin/Sqlite.php new file mode 100644 index 0000000..4418c7e --- /dev/null +++ b/src/PHPCensor/Plugin/Sqlite.php @@ -0,0 +1,69 @@ + + */ +class Sqlite extends Plugin +{ + /** + * @var array + */ + protected $queries = []; + + /** + * @var string + */ + protected $path; + + /** + * @return string + */ + public static function pluginName() + { + return 'sqlite'; + } + + /** + * {@inheritdoc} + */ + public function __construct(Builder $builder, Build $build, array $options = []) + { + parent::__construct($builder, $build, $options); + + $buildSettings = $this->builder->getConfig('build_settings'); + + if (isset($buildSettings['sqlite'])) { + $sql = $buildSettings['sqlite']; + $this->path = $sql['path']; + } + } + + /** + * Connects to SQLite and runs a specified set of queries. + * @return boolean + */ + public function execute() + { + try { + $opts = [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]; + $pdo = new PDO('sqlite:' . $this->path, $opts); + + foreach ($this->queries as $query) { + $pdo->query($this->builder->interpolate($query)); + } + } catch (\Exception $ex) { + $this->builder->logFailure($ex->getMessage()); + return false; + } + return true; + } +} diff --git a/src/PHPCensor/Plugin/TechnicalDebt.php b/src/PHPCensor/Plugin/TechnicalDebt.php new file mode 100644 index 0000000..4715f2d --- /dev/null +++ b/src/PHPCensor/Plugin/TechnicalDebt.php @@ -0,0 +1,202 @@ + + */ +class TechnicalDebt extends Plugin implements ZeroConfigPluginInterface +{ + /** + * @var array + */ + protected $suffixes; + + /** + * @var string + */ + protected $directory; + + /** + * @var int + */ + protected $allowed_errors; + + /** + * @var string, based on the assumption the root may not hold the code to be + * tested, extends the base path + */ + protected $path; + + /** + * @var array - paths to ignore + */ + protected $ignore; + + /** + * @var array - terms to search for + */ + protected $searches; + + /** + * @return string + */ + public static function pluginName() + { + return 'technical_debt'; + } + + /** + * {@inheritdoc} + */ + public function __construct(Builder $builder, Build $build, array $options = []) + { + parent::__construct($builder, $build, $options); + + $this->suffixes = ['php']; + $this->directory = $this->builder->buildPath; + $this->path = ''; + $this->ignore = $this->builder->ignore; + $this->allowed_errors = 0; + $this->searches = ['TODO', 'FIXME', 'TO DO', 'FIX ME']; + + if (isset($options['searches']) && is_array($options['searches'])) { + $this->searches = $options['searches']; + } + + if (isset($options['zero_config']) && $options['zero_config']) { + $this->allowed_errors = -1; + } + + $this->setOptions($options); + } + + /** + * Handle this plugin's options. + * @param $options + */ + protected function setOptions($options) + { + foreach (array('directory', 'path', 'ignore', 'allowed_errors') as $key) { + if (array_key_exists($key, $options)) { + $this->{$key} = $options[$key]; + } + } + } + + /** + * Check if this plugin can be executed. + * + * @param $stage + * @param Builder $builder + * @param Build $build + * @return bool + */ + public static function canExecute($stage, Builder $builder, Build $build) + { + if ($stage == Build::STAGE_TEST) { + return true; + } + + return false; + } + + /** + * Runs the plugin + */ + public function execute() + { + $success = true; + $this->builder->logExecOutput(false); + + $errorCount = $this->getErrorList(); + + $this->builder->log("Found $errorCount instances of " . implode(', ', $this->searches)); + + $this->build->storeMeta('technical_debt-warnings', $errorCount); + + if ($this->allowed_errors !== -1 && $errorCount > $this->allowed_errors) { + $success = false; + } + + return $success; + } + + /** + * Gets the number and list of errors returned from the search + * + * @return integer + */ + protected function getErrorList() + { + $dirIterator = new \RecursiveDirectoryIterator($this->directory); + $iterator = new \RecursiveIteratorIterator($dirIterator, \RecursiveIteratorIterator::SELF_FIRST); + $files = []; + + $ignores = $this->ignore; + $ignores[] = '.php-censor.yml'; + $ignores[] = 'phpci.yml'; + $ignores[] = '.phpci.yml'; + + foreach ($iterator as $file) { + $filePath = $file->getRealPath(); + $skipFile = false; + foreach ($ignores as $ignore) { + if (stripos($filePath, $ignore) !== false) { + $skipFile = true; + break; + } + } + + // Ignore hidden files, else .git, .sass_cache, etc. all get looped over + if (stripos($filePath, DIRECTORY_SEPARATOR . '.') !== false) { + $skipFile = true; + } + + if ($skipFile === false) { + $files[] = $file->getRealPath(); + } + } + + $files = array_filter(array_unique($files)); + $errorCount = 0; + + foreach ($files as $file) { + $handle = fopen($file, "r"); + $lineNumber = 1; + while (false === feof($handle)) { + $line = fgets($handle); + + foreach ($this->searches as $search) { + if ($technicalDeptLine = trim(strstr($line, $search))) { + $fileName = str_replace($this->directory, '', $file); + + $this->build->reportError( + $this->builder, + 'technical_debt', + $technicalDeptLine, + PHPCensor\Model\BuildError::SEVERITY_LOW, + $fileName, + $lineNumber + ); + + $errorCount++; + } + } + + $lineNumber++; + } + fclose ($handle); + } + + return $errorCount; + } +} diff --git a/src/PHPCensor/Plugin/Util/Executor.php b/src/PHPCensor/Plugin/Util/Executor.php new file mode 100644 index 0000000..c36bf7f --- /dev/null +++ b/src/PHPCensor/Plugin/Util/Executor.php @@ -0,0 +1,288 @@ +pluginFactory = $pluginFactory; + $this->logger = $logger; + $this->store = $store ?: StoreFactory::getStore('Build'); + } + + /** + * Execute a the appropriate set of plugins for a given build stage. + * + * @param array $config Configuration + * @param string $stage + * + * @return bool + */ + public function executePlugins($config, $stage) + { + $success = true; + $pluginsToExecute = []; + + // If we have global plugins to execute for this stage, add them to the list to be executed: + if (array_key_exists($stage, $config) && is_array($config[$stage])) { + $pluginsToExecute[] = $config[$stage]; + } + + $pluginsToExecute = $this->getBranchSpecificPlugins($config, $stage, $pluginsToExecute); + + foreach ($pluginsToExecute as $pluginSet) { + if (!$this->doExecutePlugins($pluginSet, $stage)) { + $success = false; + } + } + + return $success; + } + + /** + * @param array $config + * @param string $branch + * + * @return bool|array + */ + public function getBranchSpecificConfig($config, $branch) + { + $configSections = array_keys($config); + + foreach ($configSections as $configSection) { + if (0 === strpos($configSection, 'branch-')) { + if ($configSection === ('branch-' . $branch)) { + return $config[$configSection]; + } + + if (0 === strpos($configSection, 'branch-regex:')) { + $pattern = '#' . substr($configSection, 13) . '#u'; + preg_match($pattern, $branch, $matches); + if (!empty($matches[0])) { + return $config[$configSection]; + } + } + } + } + + return []; + } + + /** + * Check the config for any plugins specific to the branch we're currently building. + * + * @param array $config + * @param string $stage + * @param array $pluginsToExecute + * + * @return array + */ + protected function getBranchSpecificPlugins($config, $stage, $pluginsToExecute) + { + /** @var \PHPCensor\Model\Build $build */ + $build = $this->pluginFactory->getResourceFor('PHPCensor\Model\Build'); + $branch = $build->getBranch(); + $branchConfig = $this->getBranchSpecificConfig($config, $branch); + if (!$branchConfig) { + return $pluginsToExecute; + } + + $plugins = !empty($branchConfig[$stage]) ? $branchConfig[$stage] : []; + + $runOption = 'after'; + if (!empty($branchConfig['run-option'])) { + $runOption = $branchConfig['run-option']; + } + + switch ($runOption) { + // Replace standard plugin set for this stage with just the branch-specific ones: + case 'replace': + $pluginsToExecute = []; + $pluginsToExecute[] = $plugins; + break; + + // Run branch-specific plugins before standard plugins: + case 'before': + array_unshift($pluginsToExecute, $plugins); + break; + + // Run branch-specific plugins after standard plugins: + case 'after': + array_push($pluginsToExecute, $plugins); + break; + + default: + array_push($pluginsToExecute, $plugins); + break; + } + + return $pluginsToExecute; + } + + /** + * Execute the list of plugins found for a given testing stage. + * @param $plugins + * @param $stage + * @return bool + * @throws \Exception + */ + protected function doExecutePlugins(&$plugins, $stage) + { + $success = true; + + foreach ($plugins as $plugin => $options) { + $this->logger->log("\n" . + sprintf('RUNNING PLUGIN: %s', Lang::get($plugin)) . ' (' . + 'Stage' . ': ' . ucfirst($stage) . ')' + ); + + $this->setPluginStatus($stage, $plugin, Plugin::STATUS_RUNNING); + + // Try and execute it + if ($this->executePlugin($plugin, $options)) { + // Execution was successful + $this->logger->logSuccess('PLUGIN: SUCCESS'); + $this->setPluginStatus($stage, $plugin, Plugin::STATUS_SUCCESS); + } else { + $status = Plugin::STATUS_FAILED; + + if ($stage === Build::STAGE_SETUP) { + $this->logger->logFailure('PLUGIN: FAILED'); + // If we're in the "setup" stage, execution should not continue after + // a plugin has failed: + throw new Exception('Plugin failed: ' . $plugin); + } elseif ($stage === Build::STAGE_DEPLOY) { + $this->logger->logFailure('PLUGIN: FAILED'); + $success = false; + } else { + // If we're in the "test" stage and the plugin is not allowed to fail, + // then mark the build as failed: + if (empty($options['allow_failures']) && $stage === Build::STAGE_TEST) { + $this->logger->logFailure('PLUGIN: FAILED'); + $success = false; + } else { + $status = Plugin::STATUS_FAILED_ALLOWED; + + $this->logger->logFailure('PLUGIN: FAILED (ALLOWED)'); + } + } + + $this->setPluginStatus($stage, $plugin, $status); + } + } + + return $success; + } + + /** + * Executes a given plugin, with options and returns the result. + */ + public function executePlugin($plugin, $options) + { + $class = $plugin; + if (!class_exists($class)) { + $class = str_replace('_', ' ', $plugin); + $class = ucwords($class); + $class = 'PHPCensor\\Plugin\\' . str_replace(' ', '', $class); + + if (!class_exists($class)) { + $this->logger->logFailure(sprintf('Plugin does not exist: %s', $plugin)); + + return false; + } + } + + try { + // Build and run it + $obj = $this->pluginFactory->buildPlugin($class, (is_null($options) ? [] : $options)); + + return $obj->execute(); + } catch (\Exception $ex) { + $this->logger->logFailure('Exception: ' . $ex->getMessage(), $ex); + + return false; + } + } + + /** + * Change the status of a plugin for a given stage. + * + * @param string $stage The builder stage. + * @param string $plugin The plugin name. + * @param int $status The new status. + */ + protected function setPluginStatus($stage, $plugin, $status) + { + $summary = $this->getBuildSummary(); + + if (!isset($summary[$stage][$plugin])) { + $summary[$stage][$plugin] = []; + } + + $summary[$stage][$plugin]['status'] = $status; + + if ($status === Plugin::STATUS_RUNNING) { + $summary[$stage][$plugin]['started'] = time(); + } elseif ($status >= Plugin::STATUS_SUCCESS) { + $summary[$stage][$plugin]['ended'] = time(); + } + + $this->setBuildSummary($summary); + } + + /** + * Fetch the summary data of the current build. + * + * @return array + */ + private function getBuildSummary() + { + /** @var Build $build */ + $build = $this->pluginFactory->getResourceFor('PHPCensor\Model\Build'); + $metas = $this->store->getMeta('plugin-summary', $build->getProjectId(), $build->getId()); + return isset($metas[0]['meta_value']) ? $metas[0]['meta_value'] : []; + } + + /** + * Sets the summary data of the current build. + * + * @param array $summary + */ + private function setBuildSummary($summary) + { + /** @var Build $build */ + $build = $this->pluginFactory->getResourceFor('PHPCensor\Model\Build'); + $this->store->setMeta($build->getId(), 'plugin-summary', json_encode($summary)); + } +} diff --git a/src/PHPCensor/Plugin/Util/Factory.php b/src/PHPCensor/Plugin/Util/Factory.php new file mode 100644 index 0000000..ea55b1f --- /dev/null +++ b/src/PHPCensor/Plugin/Util/Factory.php @@ -0,0 +1,214 @@ +container = $container; + } else { + $this->container = new Container(); + } + } + + /** + * Trys to get a function from the file path specified. If the + * file returns a function then $this will be passed to it. + * This enables the config file to call any public methods. + * + * @param $configPath + * @return bool - true if the function exists else false. + */ + public function addConfigFromFile($configPath) + { + // The file is expected to return a function which can + // act on the pluginFactory to register any resources needed. + if (file_exists($configPath)) { + $configFunction = require($configPath); + if (is_callable($configFunction)) { + $configFunction($this); + return true; + } + } + return false; + } + + /** + * Get most recently used factory options. + * @return mixed + */ + public function getLastOptions() + { + return $this->currentPluginOptions; + } + + /** + * Builds an instance of plugin of class $className. $options will + * be passed along with any resources registered with the factory. + * + * @param $className + * @param array|null $options + * + * @throws \InvalidArgumentException if $className doesn't represent a valid plugin + * + * @return \PHPCensor\Plugin + */ + public function buildPlugin($className, $options = []) + { + $this->currentPluginOptions = $options; + + $reflectedPlugin = new \ReflectionClass($className); + + $constructor = $reflectedPlugin->getConstructor(); + + if ($constructor) { + $argsToUse = []; + foreach ($constructor->getParameters() as $param) { + if ('options' === $param->getName()) { + $argsToUse[] = $options; + } else { + $argsToUse = $this->addArgFromParam($argsToUse, $param); + } + } + /** @var Plugin $plugin */ + $plugin = $reflectedPlugin->newInstanceArgs($argsToUse); + } else { + /** @var Plugin $plugin */ + $plugin = $reflectedPlugin->newInstance(); + } + + return $plugin; + } + + /** + * @param callable $loader + * @param string|null $name + * @param string|null $type + * @throws \InvalidArgumentException + * @internal param mixed $resource + */ + public function registerResource( + $loader, + $name = null, + $type = null + ) { + if ($name === null && $type === null) { + throw new \InvalidArgumentException( + "Type or Name must be specified" + ); + } + + if (!($loader instanceof \Closure)) { + throw new \InvalidArgumentException( + '$loader is expected to be a function' + ); + } + + $resourceID = $this->getInternalID($type, $name); + + $this->container[$resourceID] = $loader; + } + + /** + * Get an internal resource ID. + * @param null $type + * @param null $name + * @return string + */ + private function getInternalID($type = null, $name = null) + { + $type = $type ? : ""; + $name = $name ? : ""; + return $type . "-" . $name; + } + + /** + * @param string $type + * @param string $name + * @return mixed + */ + public function getResourceFor($type = null, $name = null) + { + $fullId = $this->getInternalID($type, $name); + if (isset($this->container[$fullId])) { + return $this->container[$fullId]; + } + + $typeOnlyID = $this->getInternalID($type, null); + if (isset($this->container[$typeOnlyID])) { + return $this->container[$typeOnlyID]; + } + + $nameOnlyID = $this->getInternalID(null, $name); + if (isset($this->container[$nameOnlyID])) { + return $this->container[$nameOnlyID]; + } + + return null; + } + + /** + * @param \ReflectionParameter $param + * @return null|string + */ + private function getParamType(\ReflectionParameter $param) + { + $class = $param->getClass(); + if ($class) { + return $class->getName(); + } elseif ($param->isArray()) { + return self::TYPE_ARRAY; + } elseif (is_callable($param)) { + return self::TYPE_CALLABLE; + } else { + return null; + } + } + + /** + * @param $existingArgs + * @param \ReflectionParameter $param + * @return array + * @throws \DomainException + */ + private function addArgFromParam($existingArgs, \ReflectionParameter $param) + { + $name = $param->getName(); + $type = $this->getParamType($param); + $arg = $this->getResourceFor($type, $name); + + if ($arg !== null) { + $existingArgs[] = $arg; + } elseif ($arg === null && $param->isOptional()) { + $existingArgs[] = $param->getDefaultValue(); + } else { + throw new \DomainException( + "Unsatisfied dependency: " . $param->getName() + ); + } + + return $existingArgs; + } +} diff --git a/src/PHPCensor/Plugin/Util/PhpUnitResult.php b/src/PHPCensor/Plugin/Util/PhpUnitResult.php new file mode 100644 index 0000000..d412cda --- /dev/null +++ b/src/PHPCensor/Plugin/Util/PhpUnitResult.php @@ -0,0 +1,110 @@ + + */ +abstract class PhpUnitResult +{ + const SEVERITY_PASS = 'success'; + const SEVERITY_FAIL = 'fail'; + const SEVERITY_ERROR = 'error'; + const SEVERITY_SKIPPED = 'skipped'; + const SEVERITY_WARN = self::SEVERITY_PASS; + const SEVERITY_RISKY = self::SEVERITY_PASS; + + protected $outputFile; + protected $buildPath; + protected $results; + protected $failures = 0; + protected $errors = []; + + public function __construct($outputFile, $buildPath = '') + { + $this->outputFile = $outputFile; + $this->buildPath = $buildPath; + } + + /** + * Parse the results + * + * @return $this + * @throws \Exception If fails to parse the output + */ + abstract public function parse(); + + abstract protected function getSeverity($testcase); + + abstract protected function buildMessage($testcase); + + abstract protected function buildTrace($testcase); + + protected function getFileAndLine($testcase) + { + return $testcase; + } + + protected function getOutput($testcase) + { + return $testcase['output']; + } + + protected function parseTestcase($testcase) + { + $severity = $this->getSeverity($testcase); + $pass = isset(array_fill_keys([self::SEVERITY_PASS, self::SEVERITY_SKIPPED], true)[$severity]); + $data = [ + 'pass' => $pass, + 'severity' => $severity, + 'message' => $this->buildMessage($testcase), + 'trace' => $pass ? [] : $this->buildTrace($testcase), + 'output' => $this->getOutput($testcase), + ]; + + if (!$pass) { + $this->failures++; + $info = $this->getFileAndLine($testcase); + $this->errors[] = [ + 'message' => $data['message'], + 'severity' => $severity, + 'file' => $info['file'], + 'line' => $info['line'], + ]; + } + + $this->results[] = $data; + } + + /** + * Get the parse results + * + * @return string[] + */ + public function getResults() + { + return $this->results; + } + + /** + * Get the total number of failing tests + * + * @return int + */ + public function getFailures() + { + return $this->failures; + } + + /** + * Get the tests with failing status + * + * @return string[] + */ + public function getErrors() + { + return $this->errors; + } +} diff --git a/src/PHPCensor/Plugin/Util/PhpUnitResultJson.php b/src/PHPCensor/Plugin/Util/PhpUnitResultJson.php new file mode 100644 index 0000000..c99704c --- /dev/null +++ b/src/PHPCensor/Plugin/Util/PhpUnitResultJson.php @@ -0,0 +1,145 @@ + + */ +class PhpUnitResultJson extends PhpUnitResult +{ + const EVENT_TEST = 'test'; + const EVENT_TEST_START = 'testStart'; + const EVENT_SUITE_START = 'suiteStart'; + + protected $options; + protected $arguments = []; + + /** + * Parse the results + * + * @return $this + * @throws \Exception If fails to parse the output + */ + public function parse() + { + $rawResults = file_get_contents($this->outputFile); + + $events = []; + if ($rawResults && $rawResults[0] == '{') { + $fixedJson = '[' . str_replace('}{', '},{', $rawResults) . ']'; + $events = json_decode($fixedJson, true); + } elseif ($rawResults) { + $events = json_decode($rawResults, true); + } + + // Reset the parsing variables + $this->results = []; + $this->errors = []; + $this->failures = 0; + + if ($events) { + foreach ($events as $event) { + if (isset($event['event']) && $event['event'] == self::EVENT_TEST) { + $this->parseTestcase($event); + } + } + } + + return $this; + } + + + /** + * Build the severity of the event + * + * @param $event + * + * @return string The severity flags + * @throws \Exception + */ + protected function getSeverity($event) + { + $status = $event['status']; + switch ($status) { + case 'fail': + $severity = self::SEVERITY_FAIL; + break; + case 'error': + if (strpos($event['message'], 'Skipped') === 0 || strpos($event['message'], 'Incomplete') === 0) { + $severity = self::SEVERITY_SKIPPED; + } else { + $severity = self::SEVERITY_ERROR; + } + break; + case 'pass': + $severity = self::SEVERITY_PASS; + break; + case 'warning': + $severity = self::SEVERITY_PASS; + break; + default: + throw new \Exception("Unexpected PHPUnit test status: {$status}"); + break; + } + + return $severity; + } + + /** + * Build the message string for an event + * + * @param array $event + * + * @return string + */ + protected function buildMessage($event) + { + $message = $event['test']; + + if ($event['message']) { + $message .= PHP_EOL . $event ['message']; + } + + return $message; + } + + /** + * Build a string base trace of the failure + * + * @param array $event + * + * @return string[] + */ + protected function buildTrace($event) + { + $formattedTrace = []; + + if (!empty($event['trace'])) { + foreach ($event['trace'] as $step){ + $line = str_replace($this->buildPath, '', $step['file']) . ':' . $step['line']; + $formattedTrace[] = $line; + } + } + + return $formattedTrace; + } + + /** + * Saves additional info for a failing test + * + * @param array $data + * @param array $event + */ + protected function getFileAndLine($event) + { + $firstTrace = end($event['trace']); + reset($event['trace']); + + return [ + 'file' => str_replace($this->buildPath, '', $firstTrace['file']), + 'line' => $firstTrace['line'] + ]; + } +} diff --git a/src/PHPCensor/Plugin/Util/PhpUnitResultJunit.php b/src/PHPCensor/Plugin/Util/PhpUnitResultJunit.php new file mode 100644 index 0000000..3f41cb8 --- /dev/null +++ b/src/PHPCensor/Plugin/Util/PhpUnitResultJunit.php @@ -0,0 +1,130 @@ + + */ +class PhpUnitResultJunit extends PhpUnitResult +{ + /** + * Parse the results + * + * @return $this + * @throws \Exception If fails to parse the output + */ + public function parse() + { + $suites = simplexml_load_file($this->outputFile); + + // Reset the parsing variables + $this->results = []; + $this->errors = []; + $this->failures = 0; + + foreach ($suites->xpath('//testcase') as $testCase) { + $this->parseTestcase($testCase); + } + $suites['failures']; + $suites['errors']; + + return $this; + } + + protected function getSeverity($testCase) + { + $severity = self::SEVERITY_PASS; + foreach($testCase as $child) { + switch ($child->getName()) { + case 'failure': + $severity = self::SEVERITY_FAIL; + break 2; + case 'error': + if ('PHPUnit\Framework\RiskyTestError' == $child['type']) { // == because convertion to string is desired + $severity = self::SEVERITY_RISKY; + } else { + $severity = self::SEVERITY_ERROR; + } + break 2; + case 'skipped': + // skipped and ignored, can not distinguish + $severity = self::SEVERITY_SKIPPED; + break 2; + case 'warning': + $severity = self::SEVERITY_WARN; + break 2; + case 'system-out': + case 'system-err': + // not results + continue; + default: + $severity = 'UNKNOWN RESULT TYPE: '.$child->getName(); + break 2; + } + } + + return $severity; + } + + protected function buildMessage($testCase) + { + $tracePos = -1; + $msg = $this->getMessageTrace($testCase); + if ('' !== $msg) { + //strip trace + $trPos = strrpos($msg, "\n\n"); + if (false !== $trPos) { + $tracePos = $trPos; + $msg = substr($msg, 0, $trPos); + } + } + if ('' === $msg) { + $msg = $testCase['class'].'::'.$testCase['name']; + }; + $testCase['_tracePos'] = $tracePos; // will be converted to string + + return $msg; + } + + protected function getOutput($testCase) { + return (string)$testCase->{'system-out'}; + } + + protected function buildTrace($testCase) + { + if (!is_int($testCase['_tracePos'])) { + $this->buildMessage($testCase); + } + + if ($testCase['_tracePos'] >= 0) { + $stackStr = substr($this->getMessageTrace($testCase), (int)$testCase['_tracePos'] + 2, -1); + $trace = explode("\n", str_replace($this->buildPath, '.', $stackStr)); + } else { + $trace = array(); + } + + return $trace; + } + + private function getMessageTrace($testCase) { + $msg = ''; + foreach($testCase as $child) { + switch ($child->getName()) { + case 'system-out': + case 'system-err': + // not results + continue; + default: + $msg = (string)$child['message']; // according to xsd + if ('' === $msg) { + $msg = (string)$child; + } + break 2; + } + } + + return $msg; + } +} diff --git a/src/PHPCensor/Plugin/Util/TestResultParsers/Codeception.php b/src/PHPCensor/Plugin/Util/TestResultParsers/Codeception.php new file mode 100644 index 0000000..6c2594a --- /dev/null +++ b/src/PHPCensor/Plugin/Util/TestResultParsers/Codeception.php @@ -0,0 +1,110 @@ + + */ +class Codeception implements ParserInterface +{ + protected $builder; + protected $resultsXml; + protected $results; + protected $totalTests; + protected $totalTimeTaken; + protected $totalFailures; + protected $totalErrors; + + /** + * @param Builder $builder + * @param $resultsXml + */ + public function __construct(Builder $builder, $resultsXml) + { + $this->builder = $builder; + $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 = []; + $this->results = new \SimpleXMLElement($this->resultsXml); + + // calculate total results + foreach ($this->results->testsuite as $test_suite) { + $this->totalTests += (int)$test_suite['tests']; + $this->totalTimeTaken += (float)$test_suite['time']; + $this->totalFailures += (int)$test_suite['failures']; + $this->totalErrors += (int)$test_suite['errors']; + + foreach ($test_suite->testcase as $test_case) { + $test_result = [ + 'suite' => (string)$test_suite['name'], + 'file' => str_replace($this->builder->buildPath, '/', (string) $test_case['file']), + 'name' => (string)$test_case['name'], + 'feature' => (string)$test_case['feature'], + 'assertions' => (int)$test_case['assertions'], + 'time' => (float)$test_case['time'] + ]; + + if (isset($test_case['class'])) { + $test_result['class'] = (string) $test_case['class']; + } + + // PHPUnit testcases does not have feature field. Use class::method instead + if (!$test_result['feature']) { + $test_result['feature'] = sprintf('%s::%s', $test_result['class'], $test_result['name']); + } + + if (isset($test_case->failure) || isset($test_case->error)) { + $test_result['pass'] = false; + $test_result['message'] = isset($test_case->failure) ? (string)$test_case->failure : (string)$test_case->error; + } else { + $test_result['pass'] = true; + } + + $rtn[] = $test_result; + } + } + + 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 + $this->totalErrors; + } +} diff --git a/src/PHPCensor/Plugin/Util/TestResultParsers/ParserInterface.php b/src/PHPCensor/Plugin/Util/TestResultParsers/ParserInterface.php new file mode 100644 index 0000000..ccd2d1e --- /dev/null +++ b/src/PHPCensor/Plugin/Util/TestResultParsers/ParserInterface.php @@ -0,0 +1,15 @@ + + */ +class Wipe extends Plugin +{ + protected $directory; + + /** + * @return string + */ + public static function pluginName() + { + return 'wipe'; + } + + /** + * {@inheritdoc} + */ + public function __construct(Builder $builder, Build $build, array $options = []) + { + parent::__construct($builder, $build, $options); + + $path = $this->builder->buildPath; + $this->directory = isset($options['directory']) ? $this->builder->interpolate($options['directory']) : $path; + } + + /** + * Wipes a directory's contents + */ + public function execute() + { + $build = $this->builder->buildPath; + + if ($this->directory == $build || empty($this->directory)) { + return true; + } + if (is_dir($this->directory)) { + $cmd = 'rm -Rf "%s"'; + + return $this->builder->executeCommand($cmd, $this->directory); + } + + return true; + } +} diff --git a/src/PHPCensor/Plugin/Xmpp.php b/src/PHPCensor/Plugin/Xmpp.php new file mode 100644 index 0000000..9316367 --- /dev/null +++ b/src/PHPCensor/Plugin/Xmpp.php @@ -0,0 +1,196 @@ + + */ +class XMPP extends Plugin +{ + protected $directory; + + /** + * @var string, username of sender account xmpp + */ + protected $username; + + /** + * @var string, alias server of sender account xmpp + */ + protected $server; + + /** + * @var string, password of sender account xmpp + */ + protected $password; + + /** + * @var string, alias for sender + */ + protected $alias; + + /** + * @var string, use tls + */ + protected $tls; + + /** + * @var array, list of recipients xmpp accounts + */ + protected $recipients; + + /** + * @var string, mask to format date + */ + protected $date_format; + + /** + * @return string + */ + public static function pluginName() + { + return 'xmpp'; + } + + /** + * {@inheritdoc} + */ + public function __construct(Builder $builder, Build $build, array $options = []) + { + parent::__construct($builder, $build, $options); + + $this->username = ''; + $this->password = ''; + $this->server = ''; + $this->alias = ''; + $this->recipients = []; + $this->tls = false; + $this->date_format = '%c'; + + /* + * Set recipients list + */ + if (!empty($options['recipients'])) { + if (is_string($options['recipients'])) { + $this->recipients = [$options['recipients']]; + } elseif (is_array($options['recipients'])) { + $this->recipients = $options['recipients']; + } + } + } + + /** + * Get config format for sendxmpp config file + * + * @return string + */ + protected function getConfigFormat() + { + $conf = $this->username; + if (!empty($this->server)) { + $conf .= ';'.$this->server; + } + + $conf .= ' '.$this->password; + + if (!empty($this->alias)) { + $conf .= ' '.$this->alias; + } + + return $conf; + } + + /** + * Find config file for sendxmpp binary (default is .sendxmpprc) + */ + public function findConfigFile() + { + if (file_exists($this->builder->buildPath . DIRECTORY_SEPARATOR . '.sendxmpprc')) { + if (md5(file_get_contents($this->builder->buildPath . DIRECTORY_SEPARATOR . '.sendxmpprc')) + !== md5($this->getConfigFormat())) { + return null; + } + + return true; + } + + return null; + } + + /** + * Send notification message. + */ + public function execute() + { + $sendxmpp = $this->findBinary('sendxmpp'); + + /* + * Without recipients we can't send notification + */ + if (count($this->recipients) == 0) { + return false; + } + + /* + * Try to build conf file + */ + $config_file = $this->builder->buildPath . DIRECTORY_SEPARATOR . '.sendxmpprc'; + if (is_null($this->findConfigFile())) { + file_put_contents($config_file, $this->getConfigFormat()); + chmod($config_file, 0600); + } + + /* + * Enabled ssl for connection + */ + $tls = ''; + if ($this->tls) { + $tls = ' -t'; + } + + $message_file = $this->builder->buildPath . DIRECTORY_SEPARATOR . uniqid('xmppmessage'); + if ($this->buildMessage($message_file) === false) { + return false; + } + + /* + * Send XMPP notification for all recipients + */ + $cmd = $sendxmpp . "%s -f %s -m %s %s"; + $recipients = implode(' ', $this->recipients); + + $success = $this->builder->executeCommand($cmd, $tls, $config_file, $message_file, $recipients); + + print $this->builder->getLastOutput(); + + /* + * Remove temp message file + */ + $this->builder->executeCommand("rm -rf ".$message_file); + + return $success; + } + + /** + * @param $message_file + * @return int + */ + protected function buildMessage($message_file) + { + if ($this->build->isSuccessful()) { + $message = "✔ [".$this->build->getProjectTitle()."] Build #" . $this->build->getId()." successful"; + } else { + $message = "✘ [".$this->build->getProjectTitle()."] Build #" . $this->build->getId()." failure"; + } + + $message .= ' ('.strftime($this->date_format).')'; + + return file_put_contents($message_file, $message); + } +} diff --git a/src/PHPCensor/ProcessControl/Factory.php b/src/PHPCensor/ProcessControl/Factory.php new file mode 100644 index 0000000..b301e82 --- /dev/null +++ b/src/PHPCensor/ProcessControl/Factory.php @@ -0,0 +1,52 @@ + + */ +class Factory +{ + /** + * ProcessControl singleton. + * + * @var ProcessControlInterface + */ + protected static $instance = null; + + /** + * Returns the ProcessControl singleton. + * + * @return ProcessControlInterface + */ + public static function getInstance() + { + if (static::$instance === null) { + static::$instance = static::createProcessControl(); + } + return static::$instance; + } + + /** + * Create a ProcessControl depending on available extensions and the underlying OS. + * + * Check PosixProcessControl, WindowsProcessControl and UnixProcessControl, in that order. + * + * @return ProcessControlInterface + * + * @throws \Exception + */ + public static function createProcessControl() + { + switch (true) { + case PosixProcessControl::isAvailable(): + return new PosixProcessControl(); + case UnixProcessControl::isAvailable(): + return new UnixProcessControl(); + } + + throw new \Exception("No ProcessControl implementation available."); + } +} diff --git a/src/PHPCensor/ProcessControl/PosixProcessControl.php b/src/PHPCensor/ProcessControl/PosixProcessControl.php new file mode 100644 index 0000000..de99307 --- /dev/null +++ b/src/PHPCensor/ProcessControl/PosixProcessControl.php @@ -0,0 +1,42 @@ + + */ +class PosixProcessControl implements ProcessControlInterface +{ + /** + * @param integer $pid + * + * @return bool + */ + public function isRunning($pid) + { + // Signal "0" is not sent to the process, but posix_kill checks the process anyway; + return posix_kill($pid, 0); + } + + /** + * {@inheritdoc} + */ + public function kill($pid, $forcefully = false) + { + return posix_kill($pid, $forcefully ? 9 : 15); + } + + /** + * Check whether this posix_kill is available. + * + * @return bool + * + * @internal + */ + public static function isAvailable() + { + return function_exists('posix_kill'); + } +} diff --git a/src/PHPCensor/ProcessControl/ProcessControlInterface.php b/src/PHPCensor/ProcessControl/ProcessControlInterface.php new file mode 100644 index 0000000..1e58767 --- /dev/null +++ b/src/PHPCensor/ProcessControl/ProcessControlInterface.php @@ -0,0 +1,30 @@ + + */ +interface ProcessControlInterface +{ + /** + * Checks if a process exists. + * + * @param int $pid The process identifier. + * + * @return boolean true is the process is running, else false. + */ + public function isRunning($pid); + + /** + * Terminate a running process. + * + * @param int $pid The process identifier. + * @param bool $forcefully Whether to gently (false) or forcefully (true) terminate the process. + * + * @return boolean + */ + public function kill($pid, $forcefully = false); +} diff --git a/src/PHPCensor/ProcessControl/UnixProcessControl.php b/src/PHPCensor/ProcessControl/UnixProcessControl.php new file mode 100644 index 0000000..c65f860 --- /dev/null +++ b/src/PHPCensor/ProcessControl/UnixProcessControl.php @@ -0,0 +1,50 @@ + + */ +class UnixProcessControl implements ProcessControlInterface +{ + /** + * Check process using the "ps" command. + * + * @param int $pid + * + * @return boolean + */ + public function isRunning($pid) + { + $output = $exitCode = null; + exec(sprintf("ps %d", $pid), $output, $exitCode); + return $exitCode === 0; + } + + /** + * {@inheritdoc} + */ + public function kill($pid, $forcefully = false) + { + $output = []; + $result = 1; + + exec(sprintf("kill -%d %d", $forcefully ? 9 : 15, $pid), $output, $result); + + return !$result; + } + + /** + * Check whether the commands "ps" and "kill" are available. + * + * @return bool + * + * @internal + */ + public static function isAvailable() + { + return DIRECTORY_SEPARATOR === '/' && exec("which ps") && exec("which kill"); + } +} diff --git a/src/PHPCensor/Security/Authentication/LoginPasswordProviderInterface.php b/src/PHPCensor/Security/Authentication/LoginPasswordProviderInterface.php new file mode 100644 index 0000000..24e98cd --- /dev/null +++ b/src/PHPCensor/Security/Authentication/LoginPasswordProviderInterface.php @@ -0,0 +1,23 @@ + + */ +interface LoginPasswordProviderInterface extends UserProviderInterface +{ + /** + * Verify if the supplied password matches the user's one. + * + * @param User $user + * @param string $password + * + * @return boolean + */ + public function verifyPassword(User $user, $password); +} diff --git a/src/PHPCensor/Security/Authentication/Service.php b/src/PHPCensor/Security/Authentication/Service.php new file mode 100644 index 0000000..7b5aa0e --- /dev/null +++ b/src/PHPCensor/Security/Authentication/Service.php @@ -0,0 +1,106 @@ + + */ +class Service +{ + /** + * @var Service + */ + static private $instance; + + /** + * Return the service singleton. + * + * @return Service + */ + public static function getInstance() + { + if (self::$instance === null) { + $config = Config::getInstance()->get( + 'php-censor.security.auth_providers', + [ + 'internal' => [ + 'type' => 'internal' + ] + ] + ); + + $providers = []; + foreach ($config as $key => $providerConfig) { + $providers[$key] = self::buildProvider($key, $providerConfig); + } + self::$instance = new self($providers); + } + + return self::$instance; + } + + /** + * Create a provider from a given configuration. + * + * @param string $key + * @param string|array $config + * + * @return UserProviderInterface + */ + public static function buildProvider($key, $config) + { + $class = ucfirst($config['type']); + if (class_exists('\\PHPCensor\\Security\\Authentication\\UserProvider\\' . $class)) { + $class = '\\PHPCensor\\Security\\Authentication\\UserProvider\\' . $class; + } + + return new $class($key, $config); + } + + /** + * The table of providers. + * + * @var array + */ + private $providers; + + /** + * Initialize the service. + * + * @param array $providers + */ + public function __construct(array $providers) + { + $this->providers = $providers; + } + + /** + * Return all providers. + * + * @return UserProviderInterface[] + */ + public function getProviders() + { + return $this->providers; + } + + /** + * Return the user providers that allows password authentication. + * + * @return LoginPasswordProviderInterface[] + */ + public function getLoginPasswordProviders() + { + $providers = []; + foreach ($this->providers as $key => $provider) { + if ($provider instanceof LoginPasswordProviderInterface) { + $providers[$key] = $provider; + } + } + return $providers; + } +} diff --git a/src/PHPCensor/Security/Authentication/UserProvider/AbstractProvider.php b/src/PHPCensor/Security/Authentication/UserProvider/AbstractProvider.php new file mode 100644 index 0000000..3aabe3b --- /dev/null +++ b/src/PHPCensor/Security/Authentication/UserProvider/AbstractProvider.php @@ -0,0 +1,35 @@ + + */ +abstract class AbstractProvider implements UserProviderInterface +{ + /** + * @var string + */ + protected $key; + + /** + * @var array + */ + protected $config; + + /** + * AbstractProvider constructor + * + * @param string $key + * @param array $config + */ + public function __construct($key, array $config) + { + $this->key = $key; + $this->config = $config; + } +} diff --git a/src/PHPCensor/Security/Authentication/UserProvider/Internal.php b/src/PHPCensor/Security/Authentication/UserProvider/Internal.php new file mode 100644 index 0000000..5c05c1e --- /dev/null +++ b/src/PHPCensor/Security/Authentication/UserProvider/Internal.php @@ -0,0 +1,40 @@ + + */ +class Internal extends AbstractProvider implements LoginPasswordProviderInterface +{ + /** + * @param User $user + * @param string $password + * + * @return boolean + */ + public function verifyPassword(User $user, $password) + { + return password_verify($password, $user->getHash()); + } + + public function checkRequirements() + { + // Always fine + } + + /** + * @param string $identifier + * + * @return null + */ + public function provisionUser($identifier) + { + return null; + } +} diff --git a/src/PHPCensor/Security/Authentication/UserProvider/Ldap.php b/src/PHPCensor/Security/Authentication/UserProvider/Ldap.php new file mode 100644 index 0000000..6fa3470 --- /dev/null +++ b/src/PHPCensor/Security/Authentication/UserProvider/Ldap.php @@ -0,0 +1,84 @@ +config['data'])) { + $ldapData = $this->config['data']; + $ldapPort = !empty($ldapData['port']) ? $ldapData['port'] : null; + $ldapHost = !empty($ldapData['host']) ? $ldapData['host'] : 'localhost'; + $ldapBaseDn = !empty($ldapData['base_dn']) ? $ldapData['base_dn'] : 'dc=nodomain'; + $ldapMail = !empty($ldapData['mail_attribute']) ? $ldapData['mail_attribute'] : 'mail'; + + if ($ldapPort) { + $ldap = @ldap_connect($ldapHost, $ldapPort); + } else { + $ldap = @ldap_connect($ldapHost); + } + + if (false === $ldap) { + return false; + } + + ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, 3); + + $ls = @ldap_search($ldap, $ldapBaseDn, $ldapMail . '=' . $user->getEmail()); + if (false === $ls) { + return false; + } + + $le = @ldap_get_entries($ldap, $ls); + if (!$le['count']) { + return false; + } + + $dn = $le[0]['dn']; + + return @ldap_bind($ldap, $dn, $password); + } + + return false; + } + + public function checkRequirements() + { + // Always fine + } + + /** + * @param string $identifier + * + * @return User + */ + public function provisionUser($identifier) + { + /** @var UserStore $user */ + $user = Factory::getStore('User'); + $userService = new UserService($user); + + $parts = explode("@", $identifier); + $username = $parts[0]; + + return $userService->createUser($username, $identifier, $this->key, json_encode($this->config), '', false); + } +} diff --git a/src/PHPCensor/Security/Authentication/UserProviderInterface.php b/src/PHPCensor/Security/Authentication/UserProviderInterface.php new file mode 100644 index 0000000..148d6c6 --- /dev/null +++ b/src/PHPCensor/Security/Authentication/UserProviderInterface.php @@ -0,0 +1,30 @@ + + */ +interface UserProviderInterface +{ + + /** + * Check if all software requirements are met (libraries, extensions, ...) + * + * @throws \Exception + */ + public function checkRequirements(); + + /** + * Provision an new user for the given identifier. + * + * @param string $identifier The user identifier. + * + * @return User|null The new user or null if the provider does not know the user. + */ + public function provisionUser($identifier); +} diff --git a/src/PHPCensor/Service/BuildService.php b/src/PHPCensor/Service/BuildService.php new file mode 100644 index 0000000..6ebffa4 --- /dev/null +++ b/src/PHPCensor/Service/BuildService.php @@ -0,0 +1,193 @@ +buildStore = $buildStore; + } + + /** + * @param Project $project + * @param string $environment + * @param string $commitId + * @param string|null $branch + * @param string|null $tag + * @param string|null $committerEmail + * @param string|null $commitMessage + * @param integer $source + * @param integer $userId + * @param string|null $extra + * + * @return \PHPCensor\Model\Build + */ + public function createBuild( + Project $project, + $environment, + $commitId = '', + $branch = null, + $tag = null, + $committerEmail = null, + $commitMessage = null, + $source = Build::SOURCE_UNKNOWN, + $userId = 0, + $extra = null + ) { + $build = new Build(); + $build->setCreateDate(new \DateTime()); + $build->setProject($project); + $build->setStatus(Build::STATUS_PENDING); + $build->setEnvironment($environment); + + $branches = $project->getBranchesByEnvironment($environment); + $build->setExtraValue('branches', $branches); + + $build->setSource($source); + $build->setUserId($userId); + $build->setCommitId((string)$commitId); + + if (!empty($branch)) { + $build->setBranch($branch); + } else { + $build->setBranch($project->getBranch()); + } + + if (!empty($tag)) { + $build->setTag($tag); + } + + if (!empty($committerEmail)) { + $build->setCommitterEmail($committerEmail); + } + + if (!empty($commitMessage)) { + $build->setCommitMessage($commitMessage); + } + + if (!is_null($extra)) { + $build->setExtraValues($extra); + } + + /** @var Build $build */ + $build = $this->buildStore->save($build); + $buildId = $build->getId(); + + if (!empty($buildId)) { + $build = BuildFactory::getBuild($build); + $build->sendStatusPostback(); + $this->addBuildToQueue($build); + } + + return $build; + } + + /** + * @param Build $copyFrom + * + * @return \PHPCensor\Model\Build + */ + public function createDuplicateBuild(Build $copyFrom) + { + $data = $copyFrom->getDataArray(); + + // Clean up unwanted properties from the original build: + unset($data['id']); + unset($data['status']); + unset($data['log']); + unset($data['start_date']); + unset($data['finish_date']); + + $build = new Build(); + $build->setValues($data); + $build->setCreateDate(new \DateTime()); + $build->setStatus(Build::STATUS_PENDING); + + /** @var Build $build */ + $build = $this->buildStore->save($build); + + $buildId = $build->getId(); + + if (!empty($buildId)) { + $build = BuildFactory::getBuild($build); + $build->sendStatusPostback(); + $this->addBuildToQueue($build); + } + + return $build; + } + + /** + * Delete a given build. + * + * @param Build $build + * + * @return boolean + */ + public function deleteBuild(Build $build) + { + $build->removeBuildDirectory(); + return $this->buildStore->delete($build); + } + + /** + * Takes a build and puts it into the queue to be run (if using a queue) + * @param Build $build + */ + public function addBuildToQueue(Build $build) + { + $buildId = $build->getId(); + + if (empty($buildId)) { + return; + } + + $config = Config::getInstance(); + $settings = $config->get('php-censor.queue', []); + + if (!empty($settings['host']) && !empty($settings['name'])) { + try { + $jobData = [ + 'type' => 'php-censor.build', + 'build_id' => $build->getId(), + ]; + + $pheanstalk = new Pheanstalk($settings['host']); + $pheanstalk->useTube($settings['name']); + $pheanstalk->put( + json_encode($jobData), + PheanstalkInterface::DEFAULT_PRIORITY, + PheanstalkInterface::DEFAULT_DELAY, + $config->get('php-censor.queue.lifetime', 600) + ); + } catch (\Exception $ex) { + $this->queueError = true; + } + } + } +} diff --git a/src/PHPCensor/Service/BuildStatusService.php b/src/PHPCensor/Service/BuildStatusService.php new file mode 100644 index 0000000..7bd3a49 --- /dev/null +++ b/src/PHPCensor/Service/BuildStatusService.php @@ -0,0 +1,228 @@ +project = $project; + $this->branch = $branch; + $this->build = $build; + if ($this->build) { + $this->loadParentBuild($isParent); + } + if (defined('APP_URL')) { + $this->setUrl(APP_URL); + } + } + + /** + * @param string $url + */ + public function setUrl($url) + { + $this->url = $url; + } + + /** + * @return Build + */ + public function getBuild() + { + return $this->build; + } + + /** + * @param boolean $isParent + * + * @throws \Exception + */ + protected function loadParentBuild($isParent = true) + { + if ($isParent === false && !$this->isFinished()) { + $lastFinishedBuild = $this->project->getLatestBuild($this->branch, $this->finishedStatusIds); + + if ($lastFinishedBuild) { + $this->prevService = new BuildStatusService( + $this->branch, + $this->project, + $lastFinishedBuild, + true + ); + } + } + } + + /** + * @return string + */ + public function getActivity() + { + if (in_array($this->build->getStatus(), $this->finishedStatusIds)) { + return 'Sleeping'; + } elseif ($this->build->getStatus() == Build::STATUS_PENDING) { + return 'Pending'; + } elseif ($this->build->getStatus() == Build::STATUS_RUNNING) { + return 'Building'; + } + return 'Unknown'; + } + + /** + * @return string + */ + public function getName() + { + return $this->project->getTitle() . ' / ' . $this->branch; + } + + /** + * @return boolean + */ + public function isFinished() + { + if (in_array($this->build->getStatus(), $this->finishedStatusIds)) { + return true; + } + return false; + } + + /** + * @return null|Build + */ + public function getFinishedBuildInfo() + { + if ($this->isFinished()) { + return $this->build; + } elseif ($this->prevService) { + return $this->prevService->getBuild(); + } + return null; + } + + /** + * @return int|string + */ + public function getLastBuildLabel() + { + if ($buildInfo = $this->getFinishedBuildInfo()) { + return $buildInfo->getId(); + } + return ''; + } + + /** + * @return string + */ + public function getLastBuildTime() + { + $dateFormat = 'Y-m-d\\TH:i:sO'; + if ($buildInfo = $this->getFinishedBuildInfo()) { + return ($buildInfo->getFinishDate()) ? $buildInfo->getFinishDate()->format($dateFormat) : ''; + } + return ''; + } + + /** + * @param Build $build + * + * @return string + */ + public function getBuildStatus(Build $build) + { + switch ($build->getStatus()) { + case Build::STATUS_SUCCESS: + return 'Success'; + case Build::STATUS_FAILED: + return 'Failure'; + } + return 'Unknown'; + } + + /** + * @return string + */ + public function getLastBuildStatus() + { + if ($build = $this->getFinishedBuildInfo()) { + return $this->getBuildStatus($build); + } + return ''; + } + + /** + * @return string + */ + public function getBuildUrl() + { + return $this->url . 'build/view/' . $this->build->getId(); + } + + /** + * @return array + */ + public function toArray() + { + if (!$this->build) { + return []; + } + return [ + 'name' => $this->getName(), + 'activity' => $this->getActivity(), + 'lastBuildLabel' => $this->getLastBuildLabel(), + 'lastBuildStatus' => $this->getLastBuildStatus(), + 'lastBuildTime' => $this->getLastBuildTime(), + 'webUrl' => $this->getBuildUrl(), + ]; + } +} diff --git a/src/PHPCensor/Service/ProjectService.php b/src/PHPCensor/Service/ProjectService.php new file mode 100644 index 0000000..5d54a07 --- /dev/null +++ b/src/PHPCensor/Service/ProjectService.php @@ -0,0 +1,152 @@ +projectStore = $projectStore; + } + + /** + * Create a new project model and use the project store to save it. + * + * @param string $title + * @param string $type + * @param string $reference + * @param integer $userId + * @param array $options + * + * @return \PHPCensor\Model\Project + */ + public function createProject($title, $type, $reference, $userId, $options = []) + { + // Create base project and use updateProject() to set its properties: + $project = new Project(); + $project->setCreateDate(new \DateTime()); + $project->setUserId((integer)$userId); + + return $this->updateProject($project, $title, $type, $reference, $options); + } + + /** + * Update the properties of a given project. + * + * @param Project $project + * @param string $title + * @param string $type + * @param string $reference + * @param array $options + * + * @return \PHPCensor\Model\Project + */ + public function updateProject(Project $project, $title, $type, $reference, $options = []) + { + // Set basic properties: + $project->setTitle($title); + $project->setType($type); + $project->setReference($reference); + $project->setAllowPublicStatus(0); + $project->setDefaultBranchOnly(0); + + // Handle extra project options: + if (array_key_exists('ssh_private_key', $options)) { + $project->setSshPrivateKey($options['ssh_private_key']); + } + + if (array_key_exists('ssh_public_key', $options)) { + $project->setSshPublicKey($options['ssh_public_key']); + } + + if (array_key_exists('build_config', $options)) { + $project->setBuildConfig($options['build_config']); + } + + if (array_key_exists('allow_public_status', $options)) { + $project->setAllowPublicStatus((int)$options['allow_public_status']); + } + + if (array_key_exists('archived', $options)) { + $project->setArchived((bool)$options['archived']); + } + + if (array_key_exists('branch', $options)) { + $project->setBranch($options['branch']); + } + + if (array_key_exists('default_branch_only', $options)) { + $project->setDefaultBranchOnly((int)$options['default_branch_only']); + } + + if (array_key_exists('group', $options)) { + $project->setGroup($options['group']); + } + + // Allow certain project types to set access information: + $this->processAccessInformation($project); + + // Save and return the project: + /** @var Project $project */ + $project = $this->projectStore->save($project); + + if (array_key_exists('environments', $options)) { + $project->setEnvironments($options['environments']); + } + + return $project; + } + + /** + * Delete a given project. + * + * @param Project $project + * + * @return bool + */ + public function deleteProject(Project $project) + { + return $this->projectStore->delete($project); + } + + /** + * In circumstances where it is necessary, populate access information based on other project properties. + * + * @see ProjectService::createProject() + * + * @param Project $project + */ + protected function processAccessInformation(Project &$project) + { + $matches = []; + $reference = $project->getReference(); + + if ($project->getType() == 'gitlab') { + $info = []; + + if (preg_match('`^(.+)@(.+):([0-9]*)\/?(.+)\.git`', $reference, $matches)) { + $info['user'] = $matches[1]; + $info['domain'] = $matches[2]; + $info['port'] = $matches[3]; + + $project->setReference($matches[4]); + } + + $project->setAccessInformation($info); + } + } +} diff --git a/src/PHPCensor/Service/UserService.php b/src/PHPCensor/Service/UserService.php new file mode 100644 index 0000000..c3abd18 --- /dev/null +++ b/src/PHPCensor/Service/UserService.php @@ -0,0 +1,94 @@ +store = $store; + } + + /** + * Create a new user. + * + * @param string $name + * @param string $email + * @param string $providerKey + * @param string $providerData + * @param string $password + * @param boolean $isAdmin + * + * @return User + */ + public function createUser($name, $email, $providerKey, $providerData, $password, $isAdmin = false) + { + $user = new User(); + $user->setName($name); + $user->setEmail($email); + $user->setHash(password_hash($password, PASSWORD_DEFAULT)); + $user->setProviderKey($providerKey); + $user->setProviderData($providerData); + $user->setIsAdmin(($isAdmin ? 1 : 0)); + + return $this->store->save($user); + } + + /** + * Update a user. + * + * @param User $user + * @param string $name + * @param string $emailAddress + * @param string $password + * @param boolean $isAdmin + * @param string $language + * @param integer $perPage + * + * @return User + */ + public function updateUser(User $user, $name, $emailAddress, $password = null, $isAdmin = null, $language = null, $perPage = null) + { + $user->setName($name); + $user->setEmail($emailAddress); + + if (!empty($password)) { + $user->setHash(password_hash($password, PASSWORD_DEFAULT)); + } + + if (!is_null($isAdmin)) { + $user->setIsAdmin(($isAdmin ? 1 : 0)); + } + + $user->setLanguage($language); + $user->setPerPage($perPage); + + return $this->store->save($user); + } + + /** + * Delete a user. + * + * @param User $user + * + * @return bool + */ + public function deleteUser(User $user) + { + return $this->store->delete($user); + } +} diff --git a/src/PHPCensor/Store.php b/src/PHPCensor/Store.php new file mode 100644 index 0000000..5a9ce1d --- /dev/null +++ b/src/PHPCensor/Store.php @@ -0,0 +1,7 @@ +getById($key, $useConnection); + } + + /** + * Get a single BuildError by Id. + * + * @param integer $id + * @param string $useConnection + * + * @return null|BuildError + * + * @throws HttpException + */ + public function getById($id, $useConnection = 'read') + { + if (is_null($id)) { + throw new HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); + } + + $query = 'SELECT * FROM {{build_error}} WHERE {{id}} = :id LIMIT 1'; + $stmt = Database::getConnection($useConnection)->prepareCommon($query); + $stmt->bindValue(':id', $id); + + if ($stmt->execute()) { + if ($data = $stmt->fetch(\PDO::FETCH_ASSOC)) { + return new BuildError($data); + } + } + + return null; + } + + /** + * Get multiple BuildError by BuildId. + * + * @param integer $buildId + * @param integer $limit + * @param integer $offset + * @param string $plugin + * @param integer $severity + * + * @return array + * + * @throws HttpException + */ + public function getByBuildId($buildId, $limit = null, $offset = 0, $plugin = null, $severity = null) + { + if (is_null($buildId)) { + throw new HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); + } + + $query = 'SELECT * FROM {{build_error}} WHERE {{build_id}} = :build_id'; + if ($plugin) { + $query .= ' AND {{plugin}} = :plugin'; + } + if (null !== $severity) { + $query .= ' AND {{severity}} = :severity'; + } + $query .= ' ORDER BY plugin, severity'; + if (null !== $limit) { + $query .= ' LIMIT :limit'; + } + if ($offset) { + $query .= ' OFFSET :offset'; + } + $stmt = Database::getConnection()->prepareCommon($query); + $stmt->bindValue(':build_id', $buildId); + if ($plugin) { + $stmt->bindValue(':plugin', $plugin, \PDO::PARAM_STR); + } + if (null !== $severity) { + $stmt->bindValue(':severity', (integer)$severity, \PDO::PARAM_INT); + } + if (null !== $limit) { + $stmt->bindValue(':limit', (integer)$limit, \PDO::PARAM_INT); + } + if ($offset) { + $stmt->bindValue(':offset', (integer)$offset, \PDO::PARAM_INT); + } + + if ($stmt->execute()) { + $res = $stmt->fetchAll(\PDO::FETCH_ASSOC); + + $map = function ($item) { + return new BuildError($item); + }; + $rtn = array_map($map, $res); + + $count = count($rtn); + + return ['items' => $rtn, 'count' => $count]; + } else { + return ['items' => [], 'count' => 0]; + } + } + + /** + * Gets the total number of errors for a given build. + * + * @param integer $buildId + * @param string $plugin + * @param integer $severity + * + * @return integer + */ + public function getErrorTotalForBuild($buildId, $plugin = null, $severity = null) + { + $query = 'SELECT COUNT(*) AS {{total}} FROM {{build_error}} WHERE {{build_id}} = :build'; + if ($plugin) { + $query .= ' AND {{plugin}} = :plugin'; + } + if (null !== $severity) { + $query .= ' AND {{severity}} = :severity'; + } + + $stmt = Database::getConnection('read')->prepareCommon($query); + + $stmt->bindValue(':build', $buildId, \PDO::PARAM_INT); + if ($plugin) { + $stmt->bindValue(':plugin', $plugin, \PDO::PARAM_STR); + } + if (null !== $severity) { + $stmt->bindValue(':severity', (integer)$severity, \PDO::PARAM_INT); + } + + if ($stmt->execute()) { + $res = $stmt->fetch(\PDO::FETCH_ASSOC); + + return (integer)$res['total']; + } else { + return 0; + } + } + + /** + * @param integer $buildId + * + * @return array + */ + public function getKnownPlugins($buildId) + { + $query = 'SELECT DISTINCT {{plugin}} from {{build_error}} WHERE {{build_id}} = :build'; + $stmt = Database::getConnection('read')->prepareCommon($query); + $stmt->bindValue(':build', $buildId); + + if ($stmt->execute()) { + $res = $stmt->fetchAll(\PDO::FETCH_ASSOC); + + $map = function ($item) { + return $item['plugin']; + }; + $rtn = array_map($map, $res); + + return $rtn; + } else { + return []; + } + } + + /** + * @param integer $buildId + * @param string $plugin + * + * @return array + */ + public function getKnownSeverities($buildId, $plugin = '') + { + $query = 'SELECT DISTINCT {{severity}} from {{build_error}} WHERE {{build_id}} = :build'; + if ($plugin) { + $query .= ' AND {{plugin}} = :plugin'; + } + + $stmt = Database::getConnection('read')->prepareCommon($query); + $stmt->bindValue(':build', $buildId); + if ($plugin) { + $stmt->bindValue(':plugin', $plugin); + } + + if ($stmt->execute()) { + $res = $stmt->fetchAll(\PDO::FETCH_ASSOC); + + $map = function ($item) { + return (integer)$item['severity']; + }; + $rtn = array_map($map, $res); + + return $rtn; + } else { + return []; + } + } +} diff --git a/src/PHPCensor/Store/BuildErrorWriter.php b/src/PHPCensor/Store/BuildErrorWriter.php new file mode 100644 index 0000000..4e71b3c --- /dev/null +++ b/src/PHPCensor/Store/BuildErrorWriter.php @@ -0,0 +1,135 @@ +bufferSize = (integer)Config::getInstance()->get('php-censor.build.writer_buffer_size', 500); + $this->buildId = $buildId; + } + + /** + * Destructor + */ + public function __destruct() + { + $this->flush(); + } + + /** + * Write error + * + * @param string $plugin + * @param string $message + * @param integer $severity + * @param string $file + * @param integer $lineStart + * @param integer $lineEnd + * @param \DateTime $createdDate + */ + public function write( + $plugin, + $message, + $severity, + $file = null, + $lineStart = null, + $lineEnd = null, + $createdDate = null + ) { + if (is_null($createdDate)) { + $createdDate = new \DateTime(); + } + $this->errors[] = [ + 'plugin' => (string)$plugin, + 'message' => (string)$message, + 'severity' => (int)$severity, + 'file' => !is_null($file) ? (string)$file : null, + 'line_start' => !is_null($lineStart) ? (int)$lineStart : null, + 'line_end' => !is_null($lineEnd) ? (int)$lineEnd : null, + 'create_date' => $createdDate->format('Y-m-d H:i:s'), + ]; + + if (count($this->errors) >= $this->bufferSize) { + $this->flush(); + } + } + + /** + * Flush buffer + */ + public function flush() + { + if (empty($this->errors)) { + return; + } + + $insertValuesPlaceholders = []; + $insertValuesData = []; + foreach ($this->errors as $i => $error) { + $insertValuesPlaceholders[] = '( + :build_id' . $i . ', + :plugin' . $i . ', + :file' . $i . ', + :line_start' . $i . ', + :line_end' . $i . ', + :severity' . $i . ', + :message' . $i . ', + :create_date' . $i . ' + )'; + $insertValuesData['build_id' . $i] = $this->buildId; + $insertValuesData['plugin' . $i] = $error['plugin']; + $insertValuesData['file' . $i] = $error['file']; + $insertValuesData['line_start' . $i] = $error['line_start']; + $insertValuesData['line_end' . $i] = $error['line_end']; + $insertValuesData['severity' . $i] = $error['severity']; + $insertValuesData['message' . $i] = $error['message']; + $insertValuesData['create_date' . $i] = $error['create_date']; + } + $query = ' + INSERT INTO {{build_error}} ( + {{build_id}}, + {{plugin}}, + {{file}}, + {{line_start}}, + {{line_end}}, + {{severity}}, + {{message}}, + {{create_date}} + ) + VALUES ' . join(', ', $insertValuesPlaceholders) . ' + '; + $stmt = Database::getConnection('write')->prepareCommon($query); + $stmt->execute($insertValuesData); + $this->errors = []; + } +} diff --git a/src/PHPCensor/Store/BuildMetaStore.php b/src/PHPCensor/Store/BuildMetaStore.php new file mode 100644 index 0000000..0852988 --- /dev/null +++ b/src/PHPCensor/Store/BuildMetaStore.php @@ -0,0 +1,169 @@ +getById($key, $useConnection); + } + + /** + * Get a single BuildMeta by Id. + * + * @param integer $id + * @param string $useConnection + * + * @return null|BuildMeta + * + * @throws HttpException + */ + public function getById($id, $useConnection = 'read') + { + if (is_null($id)) { + throw new HttpException('id passed to ' . __FUNCTION__ . ' cannot be null.'); + } + + $query = 'SELECT * FROM {{build_meta}} WHERE {{id}} = :id LIMIT 1'; + $stmt = Database::getConnection($useConnection)->prepareCommon($query); + $stmt->bindValue(':id', $id); + + if ($stmt->execute()) { + if ($data = $stmt->fetch(\PDO::FETCH_ASSOC)) { + return new BuildMeta($data); + } + } + + return null; + } + + /** + * @param integer $buildId + * @param string $key + * + * @return null|BuildMeta + * + * @throws HttpException + */ + public function getByKey($buildId, $key) + { + if (is_null($buildId)) { + throw new HttpException('buildId passed to ' . __FUNCTION__ . ' cannot be null.'); + } + + if (!$key) { + throw new HttpException('key passed to ' . __FUNCTION__ . ' cannot be empty.'); + } + + $query = 'SELECT * FROM {{build_meta}} WHERE {{build_id}} = :build_id AND {{meta_key}} = :meta_key LIMIT 1'; + $stmt = Database::getConnection()->prepareCommon($query); + $stmt->bindValue(':build_id', $buildId); + $stmt->bindValue(':meta_key', $key); + + if ($stmt->execute()) { + if ($data = $stmt->fetch(\PDO::FETCH_ASSOC)) { + return new BuildMeta($data); + } + } + + return null; + } + + /** + * Get multiple BuildMeta by BuildId. + * + * @param integer $buildId + * @param integer $limit + * @param string $useConnection + * + * @return array + * + * @throws HttpException + */ + public function getByBuildId($buildId, $limit = 1000, $useConnection = 'read') + { + if (is_null($buildId)) { + throw new HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); + } + + $query = 'SELECT * FROM {{build_meta}} WHERE {{build_id}} = :build_id LIMIT :limit'; + $stmt = Database::getConnection($useConnection)->prepareCommon($query); + $stmt->bindValue(':build_id', $buildId); + $stmt->bindValue(':limit', (int)$limit, \PDO::PARAM_INT); + + if ($stmt->execute()) { + $res = $stmt->fetchAll(\PDO::FETCH_ASSOC); + + $map = function ($item) { + return new BuildMeta($item); + }; + $rtn = array_map($map, $res); + + $count = count($rtn); + + return ['items' => $rtn, 'count' => $count]; + } else { + return ['items' => [], 'count' => 0]; + } + } + + /** + * Only used by an upgrade migration to move errors from build_meta to build_error + * + * @param integer $limit + * + * @return array + */ + public function getErrorsForUpgrade($limit) + { + $query = 'SELECT * FROM {{build_meta}} + WHERE {{meta_key}} IN (\'phpmd-data\', \'phpcs-data\', \'phpdoccheck-data\', \'technical_debt-data\') + ORDER BY {{id}} ASC LIMIT :limit'; + + $stmt = Database::getConnection('read')->prepareCommon($query); + + $stmt->bindValue(':limit', $limit, \PDO::PARAM_INT); + + if ($stmt->execute()) { + $res = $stmt->fetchAll(\PDO::FETCH_ASSOC); + + $map = function ($item) { + return new BuildMeta($item); + }; + $rtn = array_map($map, $res); + + return $rtn; + } else { + return []; + } + } +} diff --git a/src/PHPCensor/Store/BuildStore.php b/src/PHPCensor/Store/BuildStore.php new file mode 100644 index 0000000..868249e --- /dev/null +++ b/src/PHPCensor/Store/BuildStore.php @@ -0,0 +1,501 @@ + + */ +class BuildStore extends Store +{ + /** + * @var string + */ + protected $tableName = 'build'; + + /** + * @var string + */ + protected $modelName = '\PHPCensor\Model\Build'; + + /** + * @var string + */ + protected $primaryKey = 'id'; + + /** + * Get a Build by primary key (Id) + * + * @param integer $key + * @param string $useConnection + * + * @return null|Build + */ + public function getByPrimaryKey($key, $useConnection = 'read') + { + return $this->getById($key, $useConnection); + } + + /** + * Get a single Build by Id. + * + * @param integer $id + * @param string $useConnection + * + * @return Build|null + * + * @throws HttpException + */ + public function getById($id, $useConnection = 'read') + { + if (is_null($id)) { + throw new HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); + } + + $query = 'SELECT * FROM {{build}} WHERE {{id}} = :id LIMIT 1'; + $stmt = Database::getConnection($useConnection)->prepareCommon($query); + $stmt->bindValue(':id', $id); + + if ($stmt->execute()) { + if ($data = $stmt->fetch(\PDO::FETCH_ASSOC)) { + return new Build($data); + } + } + + return null; + } + + /** + * Get multiple Build by ProjectId. + * + * @param integer $projectId + * @param integer $limit + * @param string $useConnection + * + * @return array + * + * @throws HttpException + */ + public function getByProjectId($projectId, $limit = 1000, $useConnection = 'read') + { + if (is_null($projectId)) { + throw new HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); + } + + $query = 'SELECT * FROM {{build}} WHERE {{project_id}} = :project_id LIMIT :limit'; + $stmt = Database::getConnection($useConnection)->prepareCommon($query); + $stmt->bindValue(':project_id', $projectId); + $stmt->bindValue(':limit', (int)$limit, \PDO::PARAM_INT); + + if ($stmt->execute()) { + $res = $stmt->fetchAll(\PDO::FETCH_ASSOC); + + $map = function ($item) { + return new Build($item); + }; + $rtn = array_map($map, $res); + + $count = count($rtn); + + return ['items' => $rtn, 'count' => $count]; + } else { + return ['items' => [], 'count' => 0]; + } + } + + /** + * Get multiple Build by Status. + * + * @param integer $status + * @param integer $limit + * @param string $useConnection + * + * @return array + * + * @throws HttpException + */ + public function getByStatus($status, $limit = 1000, $useConnection = 'read') + { + if (is_null($status)) { + throw new HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); + } + + $query = 'SELECT * FROM {{build}} WHERE {{status}} = :status LIMIT :limit'; + $stmt = Database::getConnection($useConnection)->prepareCommon($query); + $stmt->bindValue(':status', $status); + $stmt->bindValue(':limit', (int)$limit, \PDO::PARAM_INT); + + if ($stmt->execute()) { + $res = $stmt->fetchAll(\PDO::FETCH_ASSOC); + + $map = function ($item) { + return new Build($item); + }; + $rtn = array_map($map, $res); + + $count = count($rtn); + + return ['items' => $rtn, 'count' => $count]; + } else { + return ['items' => [], 'count' => 0]; + } + } + + /** + * @param integer $limit + * @param integer $offset + * + * @return array + */ + public function getBuilds($limit = 5, $offset = 0) + { + $query = 'SELECT * FROM {{build}} ORDER BY {{id}} DESC LIMIT :limit OFFSET :offset'; + $stmt = Database::getConnection('read')->prepareCommon($query); + + $stmt->bindValue(':limit', $limit, \PDO::PARAM_INT); + $stmt->bindValue(':offset', $offset, \PDO::PARAM_INT); + + if ($stmt->execute()) { + $res = $stmt->fetchAll(\PDO::FETCH_ASSOC); + + $map = function ($item) { + return new Build($item); + }; + $rtn = array_map($map, $res); + + return $rtn; + } else { + return []; + } + } + + /** + * Return an array of the latest builds for a given project. + * + * @param integer|null $projectId + * @param integer $limit + * + * @return array + */ + public function getLatestBuilds($projectId = null, $limit = 5) + { + if (!is_null($projectId)) { + $query = 'SELECT * FROM {{build}} WHERE {{project_id}} = :pid ORDER BY {{id}} DESC LIMIT :limit'; + } else { + $query = 'SELECT * FROM {{build}} ORDER BY {{id}} DESC LIMIT :limit'; + } + + $stmt = Database::getConnection('read')->prepareCommon($query); + + if (!is_null($projectId)) { + $stmt->bindValue(':pid', $projectId); + } + + $stmt->bindValue(':limit', $limit, \PDO::PARAM_INT); + + if ($stmt->execute()) { + $res = $stmt->fetchAll(\PDO::FETCH_ASSOC); + + $map = function ($item) { + return new Build($item); + }; + $rtn = array_map($map, $res); + + return $rtn; + } else { + return []; + } + } + + /** + * Return the latest build for a specific project, of a specific build status. + * + * @param integer|null $projectId + * @param integer $status + * + * @return array|Build + */ + public function getLastBuildByStatus($projectId = null, $status = Build::STATUS_SUCCESS) + { + $query = 'SELECT * FROM {{build}} WHERE {{project_id}} = :pid AND {{status}} = :status ORDER BY {{id}} DESC LIMIT 1'; + $stmt = Database::getConnection('read')->prepareCommon($query); + $stmt->bindValue(':pid', $projectId); + $stmt->bindValue(':status', $status); + + if ($stmt->execute()) { + if ($data = $stmt->fetch(\PDO::FETCH_ASSOC)) { + return new Build($data); + } + } else { + return []; + } + } + + /** + * Return an array of the latest builds for all projects. + * + * @param integer $limit_by_project + * @param integer $limit_all + * + * @return array + */ + public function getAllProjectsLatestBuilds($limit_by_project = 5, $limit_all = 10) + { + // don't fetch log field - contain many data + $query = ' + SELECT + {{id}}, + {{project_id}}, + {{commit_id}}, + {{status}}, + {{branch}}, + {{create_date}}, + {{start_date}}, + {{finish_date}}, + {{committer_email}}, + {{commit_message}}, + {{extra}}, + {{environment}}, + {{tag}} + FROM {{build}} + ORDER BY {{id}} DESC + LIMIT 10000 + '; + + $stmt = Database::getConnection('read')->prepareCommon($query); + + if ($stmt->execute()) { + $res = $stmt->fetchAll(\PDO::FETCH_ASSOC); + + $projects = []; + $latest = []; + foreach($res as $item) { + $project_id = $item['project_id']; + $environment = $item['environment']; + if (empty($projects[$project_id])) { + $projects[$project_id] = []; + } + if (empty($projects[$project_id][$environment])) { + $projects[$project_id][$environment] = [ + 'latest' => [], + 'success' => null, + 'failed' => null, + ]; + } + $build = null; + if (count($projects[$project_id][$environment]['latest']) < $limit_by_project) { + $build = new Build($item); + $projects[$project_id][$environment]['latest'][] = $build; + } + if (count($latest) < $limit_all) { + if (is_null($build)) { + $build = new Build($item); + } + $latest[] = $build; + } + if (empty($projects[$project_id][$environment]['success']) and ($item['status'] == Build::STATUS_SUCCESS)) { + if (is_null($build)) { + $build = new Build($item); + } + $projects[$project_id][$environment]['success'] = $build; + } + if (empty($projects[$project_id][$environment]['failed']) and ($item['status'] == Build::STATUS_FAILED)) { + if (is_null($build)) { + $build = new Build($item); + } + $projects[$project_id][$environment]['failed'] = $build; + } + } + foreach($projects as $idx => $project) { + $projects[$idx] = array_filter($project, function($val) { + return ($val['latest'][0]->getStatus() != Build::STATUS_SUCCESS); + }); + } + $projects = array_filter($projects); + + return ['projects' => $projects, 'latest' => $latest]; + } else { + return []; + } + } + + /** + * Return an array of builds for a given project and commit ID. + * + * @param integer $projectId + * @param string $commitId + * + * @return array + */ + public function getByProjectAndCommit($projectId, $commitId) + { + $query = 'SELECT * FROM {{build}} WHERE {{project_id}} = :project_id AND {{commit_id}} = :commit_id'; + $stmt = Database::getConnection('read')->prepareCommon($query); + + $stmt->bindValue(':project_id', $projectId); + $stmt->bindValue(':commit_id', $commitId); + + if ($stmt->execute()) { + $res = $stmt->fetchAll(\PDO::FETCH_ASSOC); + + $map = function ($item) { + return new Build($item); + }; + + $rtn = array_map($map, $res); + + return ['items' => $rtn, 'count' => count($rtn)]; + } else { + return ['items' => [], 'count' => 0]; + } + } + + /** + * Returns all registered branches for project + * + * @param integer $projectId + * + * @return array + * + * @throws \Exception + */ + public function getBuildBranches($projectId) + { + $query = 'SELECT DISTINCT {{branch}} FROM {{build}} WHERE {{project_id}} = :project_id'; + $stmt = Database::getConnection('read')->prepareCommon($query); + $stmt->bindValue(':project_id', $projectId); + + if ($stmt->execute()) { + $res = $stmt->fetchAll(\PDO::FETCH_COLUMN); + return $res; + } else { + return []; + } + } + + /** + * Return build metadata by key, project and optionally build id. + * + * @param string $key + * @param integer $projectId + * @param integer|null $buildId + * @param string|null $branch + * @param integer $numResults + * + * @return array|null + */ + public function getMeta($key, $projectId, $buildId = null, $branch = null, $numResults = 1) + { + $query = 'SELECT bm.build_id, bm.meta_key, bm.meta_value + FROM {{build_meta}} AS {{bm}} + LEFT JOIN {{build}} AS {{b}} ON b.id = bm.build_id + WHERE bm.meta_key = :key AND b.project_id = :projectId'; + + // If we're getting comparative meta data, include previous builds + // otherwise just include the specified build ID: + if ($numResults > 1) { + $query .= ' AND bm.build_id <= :buildId '; + } else { + $query .= ' AND bm.build_id = :buildId '; + } + + // Include specific branch information if required: + if (!is_null($branch)) { + $query .= ' AND b.branch = :branch '; + } + + $query .= ' ORDER BY bm.id DESC LIMIT :numResults'; + + $stmt = Database::getConnection('read')->prepareCommon($query); + $stmt->bindValue(':key', $key, \PDO::PARAM_STR); + $stmt->bindValue(':projectId', (int)$projectId, \PDO::PARAM_INT); + $stmt->bindValue(':buildId', (int)$buildId, \PDO::PARAM_INT); + $stmt->bindValue(':numResults', (int)$numResults, \PDO::PARAM_INT); + + if (!is_null($branch)) { + $stmt->bindValue(':branch', $branch, \PDO::PARAM_STR); + } + + if ($stmt->execute()) { + $rtn = $stmt->fetchAll(\PDO::FETCH_ASSOC); + + /** @var \PHPCensor\Store\BuildErrorStore $errorStore */ + $errorStore = Factory::getStore('BuildError'); + + $rtn = array_reverse($rtn); + $rtn = array_map(function ($item) use ($key, $errorStore, $buildId) { + $item['meta_value'] = json_decode($item['meta_value'], true); + if ('plugin-summary' === $key) { + foreach ($item['meta_value'] as $stage => $stageData) { + foreach ($stageData as $plugin => $pluginData) { + $item['meta_value'][$stage][$plugin]['errors'] = $errorStore->getErrorTotalForBuild( + $buildId, + $plugin + ); + } + } + } + + return $item; + }, $rtn); + + if (!count($rtn)) { + return null; + } else { + return $rtn; + } + } else { + return null; + } + } + + /** + * Set a metadata value for a given project and build ID. + * + * @param integer $buildId + * @param string $key + * @param string $value + */ + public function setMeta($buildId, $key, $value) + { + /** @var BuildMetaStore $store */ + $store = Factory::getStore('BuildMeta'); + $meta = $store->getByKey($buildId, $key); + if (is_null($meta)) { + $meta = new BuildMeta(); + $meta->setBuildId($buildId); + $meta->setMetaKey($key); + } + $meta->setMetaValue($value); + + $store->save($meta); + } + + /** + * Update status only if it synced with db + * + * @param Build $build + * @param integer $status + * + * @return boolean + */ + public function updateStatusSync($build, $status) + { + try { + $query = 'UPDATE {{build}} SET status = :status_new WHERE {{id}} = :id AND {{status}} = :status_current'; + $stmt = Database::getConnection('write')->prepareCommon($query); + $stmt->bindValue(':id', $build->getId(), \PDO::PARAM_INT); + $stmt->bindValue(':status_current', $build->getStatus(), \PDO::PARAM_INT); + $stmt->bindValue(':status_new', $status, \PDO::PARAM_INT); + return ($stmt->execute() && ($stmt->rowCount() == 1)); + } catch (\Exception $e) { + return false; + } + } +} diff --git a/src/PHPCensor/Store/EnvironmentStore.php b/src/PHPCensor/Store/EnvironmentStore.php new file mode 100644 index 0000000..bb9d1d9 --- /dev/null +++ b/src/PHPCensor/Store/EnvironmentStore.php @@ -0,0 +1,105 @@ +getById($key, $useConnection); + } + + /** + * Get a single Environment by Id. + * + * @param integer $id + * @param string $useConnection + * + * @return null|Environment + * + * @throws HttpException + */ + public function getById($id, $useConnection = 'read') + { + if (is_null($id)) { + throw new HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); + } + + $query = 'SELECT * FROM {{environment}} WHERE {{id}} = :id LIMIT 1'; + $stmt = Database::getConnection($useConnection)->prepareCommon($query); + $stmt->bindValue(':id', $id); + + if ($stmt->execute()) { + if ($data = $stmt->fetch(\PDO::FETCH_ASSOC)) { + return new Environment($data); + } + } + + return null; + } + + /** + * Get multiple Environment by Project id. + * + * @param integer $projectId + * @param string $useConnection + * + * @return array + * + * @throws \Exception + */ + public function getByProjectId($projectId, $useConnection = 'read') + { + if (is_null($projectId)) { + throw new \Exception('Value passed to ' . __FUNCTION__ . ' cannot be null.'); + } + + $query = 'SELECT * FROM {{environment}} WHERE {{project_id}} = :project_id'; + $stmt = Database::getConnection($useConnection)->prepareCommon($query); + + $stmt->bindValue(':project_id', $projectId); + + if ($stmt->execute()) { + $res = $stmt->fetchAll(\PDO::FETCH_ASSOC); + + $map = function ($item) { + return new Environment($item); + }; + $rtn = array_map($map, $res); + + $count = count($rtn); + + return ['items' => $rtn, 'count' => $count]; + } else { + return ['items' => [], 'count' => 0]; + } + } +} diff --git a/src/PHPCensor/Store/ProjectGroupStore.php b/src/PHPCensor/Store/ProjectGroupStore.php new file mode 100644 index 0000000..c53c8e1 --- /dev/null +++ b/src/PHPCensor/Store/ProjectGroupStore.php @@ -0,0 +1,99 @@ +getById($key, $useConnection); + } + + /** + * Get a single ProjectGroup by Id. + * + * @param integer $id + * @param string $useConnection + * + * @return ProjectGroup|null + * + * @throws HttpException + */ + public function getById($id, $useConnection = 'read') + { + if (is_null($id)) { + throw new HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); + } + + $query = 'SELECT * FROM {{project_group}} WHERE {{id}} = :id LIMIT 1'; + $stmt = Database::getConnection($useConnection)->prepareCommon($query); + + $stmt->bindValue(':id', $id); + + if ($stmt->execute()) { + if ($data = $stmt->fetch(\PDO::FETCH_ASSOC)) { + return new ProjectGroup($data); + } + } + + return null; + } + + /** + * Get a single ProjectGroup by title. + * + * @param integer $title + * @param string $useConnection + * + * @return ProjectGroup|null + * + * @throws HttpException + */ + public function getByTitle($title, $useConnection = 'read') + { + if (is_null($title)) { + throw new HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); + } + + $query = 'SELECT * FROM {{project_group}} WHERE {{title}} = :title LIMIT 1'; + $stmt = Database::getConnection($useConnection)->prepareCommon($query); + + $stmt->bindValue(':title', $title); + + if ($stmt->execute()) { + if ($data = $stmt->fetch(\PDO::FETCH_ASSOC)) { + return new ProjectGroup($data); + } + } + + return null; + } +} diff --git a/src/PHPCensor/Store/ProjectStore.php b/src/PHPCensor/Store/ProjectStore.php new file mode 100644 index 0000000..b0ca626 --- /dev/null +++ b/src/PHPCensor/Store/ProjectStore.php @@ -0,0 +1,241 @@ + + */ +class ProjectStore extends Store +{ + /** + * @var string + */ + protected $tableName = 'project'; + + /** + * @var string + */ + protected $modelName = '\PHPCensor\Model\Project'; + + /** + * @var string + */ + protected $primaryKey = 'id'; + + /** + * Get a Project by primary key (Id) + * + * @param integer $key + * @param string $useConnection + * + * @return null|Project + */ + public function getByPrimaryKey($key, $useConnection = 'read') + { + return $this->getById($key, $useConnection); + } + + /** + * Get a single Project by Id. + * + * @param integer $id + * @param string $useConnection + * + * @return null|Project + * + * @throws HttpException + */ + public function getById($id, $useConnection = 'read') + { + if (is_null($id)) { + throw new HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); + } + + $query = 'SELECT * FROM {{project}} WHERE {{id}} = :id LIMIT 1'; + $stmt = Database::getConnection($useConnection)->prepareCommon($query); + $stmt->bindValue(':id', $id); + + if ($stmt->execute()) { + if ($data = $stmt->fetch(\PDO::FETCH_ASSOC)) { + return new Project($data); + } + } + + return null; + } + + /** + * Get a single Project by Ids. + * + * @param integer[] $values + * @param string $useConnection + * + * @throws HttpException + * + * @return Project[] + */ + public function getByIds($values, $useConnection = 'read') + { + if (empty($values)) { + throw new HttpException('Values passed to ' . __FUNCTION__ . ' cannot be empty.'); + } + + $query = 'SELECT * FROM {{project}} WHERE {{id}} IN ('.implode(', ', array_map('intval', $values)).')'; + $stmt = Database::getConnection($useConnection)->prepareCommon($query); + + $rtn = []; + if ($stmt->execute()) { + while ($data = $stmt->fetch(\PDO::FETCH_ASSOC)) { + $rtn[$data['id']] = new Project($data); + } + } + + return $rtn; + } + + /** + * Get multiple Project by Title. + * + * @param string $title + * @param integer $limit + * @param string $useConnection + * + * @return array + * + * @throws HttpException + */ + public function getByTitle($title, $limit = 1000, $useConnection = 'read') + { + if (is_null($title)) { + throw new HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); + } + + + $query = 'SELECT * FROM {{project}} WHERE {{title}} = :title LIMIT :limit'; + $stmt = Database::getConnection($useConnection)->prepareCommon($query); + $stmt->bindValue(':title', $title); + $stmt->bindValue(':limit', (int)$limit, \PDO::PARAM_INT); + + if ($stmt->execute()) { + $res = $stmt->fetchAll(\PDO::FETCH_ASSOC); + + $map = function ($item) { + return new Project($item); + }; + $rtn = array_map($map, $res); + + $count = count($rtn); + + return ['items' => $rtn, 'count' => $count]; + } else { + return ['items' => [], 'count' => 0]; + } + } + + /** + * Returns a list of all branch names. + * + * @param $projectId + * + * @return array + */ + public function getKnownBranches($projectId) + { + $query = 'SELECT DISTINCT {{branch}} from {{build}} WHERE {{project_id}} = :pid'; + $stmt = Database::getConnection('read')->prepareCommon($query); + $stmt->bindValue(':pid', $projectId); + + if ($stmt->execute()) { + $res = $stmt->fetchAll(\PDO::FETCH_ASSOC); + + $map = function ($item) { + return $item['branch']; + }; + $rtn = array_map($map, $res); + + return $rtn; + } else { + return []; + } + } + + /** + * Get a list of all projects, ordered by their title. + * + * @param boolean $archived + * + * @return array + */ + public function getAll($archived = false) + { + $archived = (integer)$archived; + + $query = 'SELECT * FROM {{project}} WHERE {{archived}} = :archived ORDER BY {{title}} ASC'; + $stmt = Database::getConnection('read')->prepareCommon($query); + + $stmt->bindValue(':archived', $archived); + + if ($stmt->execute()) { + $res = $stmt->fetchAll(\PDO::FETCH_ASSOC); + + $map = function ($item) { + return new Project($item); + }; + $rtn = array_map($map, $res); + + $count = count($rtn); + + + return ['items' => $rtn, 'count' => $count]; + } else { + return ['items' => [], 'count' => 0]; + } + } + + /** + * Get multiple Project by GroupId. + * + * @param integer $groupId + * @param boolean $archived + * @param integer $limit + * @param string $useConnection + * + * @return array + * + * @throws \Exception + */ + public function getByGroupId($groupId, $archived = false, $limit = 1000, $useConnection = 'read') + { + if (is_null($groupId)) { + throw new \Exception('Value passed to ' . __FUNCTION__ . ' cannot be null.'); + } + $archived = (integer)$archived; + + $query = 'SELECT * FROM {{project}} WHERE {{group_id}} = :group_id AND {{archived}} = :archived ORDER BY {{title}} LIMIT :limit'; + $stmt = Database::getConnection($useConnection)->prepareCommon($query); + + $stmt->bindValue(':group_id', $groupId); + $stmt->bindValue(':archived', $archived); + $stmt->bindValue(':limit', (int)$limit, \PDO::PARAM_INT); + + if ($stmt->execute()) { + $res = $stmt->fetchAll(\PDO::FETCH_ASSOC); + + $map = function ($item) { + return new Project($item); + }; + $rtn = array_map($map, $res); + + $count = count($rtn); + + return ['items' => $rtn, 'count' => $count]; + } else { + return ['items' => [], 'count' => 0]; + } + } +} diff --git a/src/PHPCensor/Store/UserStore.php b/src/PHPCensor/Store/UserStore.php new file mode 100644 index 0000000..8bb75a7 --- /dev/null +++ b/src/PHPCensor/Store/UserStore.php @@ -0,0 +1,194 @@ + + */ +class UserStore extends Store +{ + /** + * @var string + */ + protected $tableName = 'user'; + + /** + * @var string + */ + protected $modelName = '\PHPCensor\Model\User'; + + /** + * @var string + */ + protected $primaryKey = 'id'; + + /** + * Get a User by primary key (Id) + * + * @param integer $key + * @param string $useConnection + * + * @return null|User + */ + public function getByPrimaryKey($key, $useConnection = 'read') + { + return $this->getById($key, $useConnection); + } + + /** + * Get a single User by Id. + * + * @param integer $id + * @param string $useConnection + * + * @return null|User + * + * @throws HttpException + */ + public function getById($id, $useConnection = 'read') + { + if (is_null($id)) { + throw new HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); + } + + $query = 'SELECT * FROM {{user}} WHERE {{id}} = :id LIMIT 1'; + $stmt = Database::getConnection($useConnection)->prepareCommon($query); + $stmt->bindValue(':id', $id); + + if ($stmt->execute()) { + if ($data = $stmt->fetch(\PDO::FETCH_ASSOC)) { + return new User($data); + } + } + + return null; + } + + /** + * Get a single User by Email. + * + * @param string $email + * + * @throws HttpException + * + * @return User + */ + public function getByEmail($email) + { + if (is_null($email)) { + throw new HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); + } + + $query = 'SELECT * FROM {{user}} WHERE {{email}} = :email LIMIT 1'; + $stmt = Database::getConnection()->prepareCommon($query); + + $stmt->bindValue(':email', $email); + + if ($stmt->execute()) { + if ($data = $stmt->fetch(\PDO::FETCH_ASSOC)) { + return new User($data); + } + } + + return null; + } + + /** + * Get a single User by Email or Name. + * + * @param string $emailOrName + * + * @throws HttpException + * + * @return User + */ + public function getByEmailOrName($emailOrName) + { + if (is_null($emailOrName)) { + throw new HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); + } + + $query = 'SELECT * FROM {{user}} WHERE {{email}} = :value OR {{name}} = :value LIMIT 1'; + $stmt = Database::getConnection()->prepareCommon($query); + $stmt->bindValue(':value', $emailOrName); + + if ($stmt->execute()) { + if ($data = $stmt->fetch(\PDO::FETCH_ASSOC)) { + return new User($data); + } + } + + return null; + } + + /** + * Get a single User by RememberKey. + * + * @param string $rememberKey + * + * @throws HttpException + * + * @return User + */ + public function getByRememberKey($rememberKey) + { + if (is_null($rememberKey)) { + throw new HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); + } + + $query = 'SELECT * FROM {{user}} WHERE {{remember_key}} = :remember_key LIMIT 1'; + $stmt = Database::getConnection()->prepareCommon($query); + $stmt->bindValue(':remember_key', $rememberKey); + + if ($stmt->execute()) { + if ($data = $stmt->fetch(\PDO::FETCH_ASSOC)) { + return new User($data); + } + } + + return null; + } + + /** + * Get multiple User by Name. + * + * @param string $name + * @param integer $limit + * @param string $useConnection + * + * @return array + * + * @throws HttpException + */ + public function getByName($name, $limit = 1000, $useConnection = 'read') + { + if (is_null($name)) { + throw new HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); + } + + $query = 'SELECT * FROM {{user}} WHERE {{name}} = :name LIMIT :limit'; + $stmt = Database::getConnection($useConnection)->prepareCommon($query); + $stmt->bindValue(':name', $name); + $stmt->bindValue(':limit', (int)$limit, \PDO::PARAM_INT); + + if ($stmt->execute()) { + $res = $stmt->fetchAll(\PDO::FETCH_ASSOC); + + $map = function ($item) { + return new User($item); + }; + $rtn = array_map($map, $res); + + $count = count($rtn); + + return ['items' => $rtn, 'count' => $count]; + } else { + return ['items' => [], 'count' => 0]; + } + } +} diff --git a/src/PHPCensor/View/Build/errors.phtml b/src/PHPCensor/View/Build/errors.phtml new file mode 100644 index 0000000..1db3403 --- /dev/null +++ b/src/PHPCensor/View/Build/errors.phtml @@ -0,0 +1,38 @@ +getFileLinkTemplate(); + +/** @var \PHPCensor\Model\BuildError[] $errors */ +foreach ($errors as $error): + + $link = str_replace('{BASEFILE}', basename($error->getFile()), $linkTemplate); + $link = str_replace('{FILE}', $error->getFile(), $link); + $link = str_replace('{LINE}', $error->getLineStart(), $link); + $link = str_replace('{LINE_END}', $error->getLineEnd(), $link); +?> + + + + + getSeverityString()); ?> + + + getPlugin()); ?> + getFile(); ?> + + + getLineStart() == $error->getLineEnd() || !$error->getLineEnd()) { + print $error->getLineStart(); + } else { + print $error->getLineStart() . ' - ' . $error->getLineEnd(); + } + ?> + + + getMessage())); ?> + + + + diff --git a/src/PHPCensor/View/Build/header-row.phtml b/src/PHPCensor/View/Build/header-row.phtml new file mode 100644 index 0000000..b2038c4 --- /dev/null +++ b/src/PHPCensor/View/Build/header-row.phtml @@ -0,0 +1,30 @@ + +
  • + + getCommitterEmail()): ?> +
    + +
    + + +

    + getProject()->getTitle(); ?> + + getStatus() == \PHPCensor\Model\Build::STATUS_PENDING): ?> + getCreateDate()->format('H:i')); ?> + getStatus() == \PHPCensor\Model\Build::STATUS_RUNNING): ?> + getStartDate()->format('H:i')); ?> + +

    +

    getBranch()); ?>

    +
    +
  • \ No newline at end of file diff --git a/src/PHPCensor/View/Build/view.phtml b/src/PHPCensor/View/Build/view.phtml new file mode 100644 index 0000000..69d2580 --- /dev/null +++ b/src/PHPCensor/View/Build/view.phtml @@ -0,0 +1,315 @@ + + +
    +
    +
    +
    +

    + +

    +
    + +
    + + + + + + + + + + + + + + + + + + + + +
    + + + getProject()->getTitle(); ?> + +
    + + getBranch(); ?> + + getTag()): ?> / + + + + +
    + getEnvironment(); + echo !empty($environment) ? (' ' . $environment) : '—' ; + ?> +
    + + getBranch(); ?> + + + getExtra('branches'); + if (!empty($branches)) { + foreach($branches as $branch) { + ?> +
    +
    +
    +
    + +
    +
    +
    +

    + +

    +
    + +
    + + + + + + + + + + + + + + + + + + + +
    + getSourceHumanize()); ?> +
    + + getCommitId(), 0, 7); ?> + +
    + getCommitterEmail(); ?> +
    + getCommitMessage(); ?> +
    +
    +
    +
    + +
    +
    +
    +

    + +

    +
    + +
    + + + + + + + + + + + + + + + + + + + + +
    + getCreateDate() ? $build->getCreateDate()->format('Y-m-d H:i:s') : ''); ?> +
    + getStartDate() ? $build->getStartDate()->format('Y-m-d H:i:s') : ''); ?> +
    + getFinishDate() ? $build->getFinishDate()->format('Y-m-d H:i:s') : ''); ?> +
    + getDuration(); ?> +
    +
    +
    +
    + +
    + + + + + + +' . PHP_EOL; +} +?> + + diff --git a/src/PHPCensor/View/BuildStatus/view.phtml b/src/PHPCensor/View/BuildStatus/view.phtml new file mode 100644 index 0000000..71d056b --- /dev/null +++ b/src/PHPCensor/View/BuildStatus/view.phtml @@ -0,0 +1,196 @@ + + + + <?php print $project->getTitle(); ?> - PHP Censor + + + + + + + + + + + + + + + + + +
    +
    + +
    +
    +
    +
    + + + getStatus()) { + case 0: + $statusClass = 'blue'; + $statusText = 'Pending'; + break; + case 1: + $statusClass = 'yellow'; + $statusText = 'Running'; + break; + case 2: + $statusClass = 'green'; + $statusText = 'Success'; + break; + case 3: + $statusClass = 'red'; + $statusText = 'Failed'; + break; + } + + ?> + +
    +
    +

    + getProject()->getTitle(); ?> #getId(); ?> () +

    +

    + getCommitMessage()): ?> + getCommitMessage(); ?>

    + + + Branch: getBranch(); ?>
    + Committer: getCommitterEmail(); ?> + + getCommitId())): ?> +
    Commit: getCommitId(); ?>
    + +

    +
    +
    + +
    +
    +
    + + +
    +

    Builds

    + + + + + + + + + + + + + + + + + + + + + + + + getStatus()) + { + case 0: + $class = 'info'; + $status = 'Pending'; + + break; + + case 1: + $class = 'warning'; + $status = 'Running'; + break; + + case 2: + $class = 'success'; + $status = 'Success'; + break; + + case 3: + $class = 'danger'; + $status = 'Failed'; + break; + } + ?> + + + + + + + + + + + + + + +
    IDStatusDateCommitBranchEnvironmentDuration
    No builds yet.
    #getId(), 6, '0', STR_PAD_LEFT); ?> + + getCreateDate()->format('Y-m-d H:i:s'); ?> + getCommitId())) { + print sprintf( + '%s %s', + $build->getCommitLink(), + substr($build->getCommitId(), 0, 7), + $build->getCommitterEmail() ? ('(' . $build->getCommitterEmail() . ')') : '' + ); + } else { + print '—'; + } + ?> + + getExtra('branches'); ?> + getBranch(); ?> + + getTag()): ?> / + + + + + + getEnvironment(); + echo !empty($environment) ? $environment : '—' ; + ?> + + getDuration(); ?> sec. +
    +
    +
    +
    +
    + +
    + + diff --git a/src/PHPCensor/View/Email/layout.phtml b/src/PHPCensor/View/Email/layout.phtml new file mode 100644 index 0000000..a97ac99 --- /dev/null +++ b/src/PHPCensor/View/Email/layout.phtml @@ -0,0 +1,28 @@ + + + + + +
    +
    +
    getTitle(); ?> - Build #getId(); ?>
    +
    +

    + Your commit getCommitId(); ?> generated a + isSuccessful() ? 'success' : 'failed'; ?> build in project + getTitle(); ?>. +

    + +
    + +
    +
    + + diff --git a/src/PHPCensor/View/Email/long.phtml b/src/PHPCensor/View/Email/long.phtml new file mode 100644 index 0000000..daef0aa --- /dev/null +++ b/src/PHPCensor/View/Email/long.phtml @@ -0,0 +1,2 @@ +

    getCommitMessage(); ?>

    +
    getLog()); ?>
    diff --git a/src/PHPCensor/View/Email/short.phtml b/src/PHPCensor/View/Email/short.phtml new file mode 100644 index 0000000..75df4f8 --- /dev/null +++ b/src/PHPCensor/View/Email/short.phtml @@ -0,0 +1 @@ +

    getCommitMessage(); ?>

    diff --git a/src/PHPCensor/View/Group/edit.phtml b/src/PHPCensor/View/Group/edit.phtml new file mode 100644 index 0000000..809fc59 --- /dev/null +++ b/src/PHPCensor/View/Group/edit.phtml @@ -0,0 +1,10 @@ + +
    +
    +

    +
    + +
    + +
    +
    \ No newline at end of file diff --git a/src/PHPCensor/View/Group/index.phtml b/src/PHPCensor/View/Group/index.phtml new file mode 100644 index 0000000..7bad7e8 --- /dev/null +++ b/src/PHPCensor/View/Group/index.phtml @@ -0,0 +1,48 @@ + + + +
    + + + + + + + + + + + + + + + + + +
    +
    + + User()->getIsAdmin() && (!count($group['projects']))): ?> + + + +
    +
    +
    + + \ No newline at end of file diff --git a/src/PHPCensor/View/Home/index.phtml b/src/PHPCensor/View/Home/index.phtml new file mode 100644 index 0000000..8593908 --- /dev/null +++ b/src/PHPCensor/View/Home/index.phtml @@ -0,0 +1,29 @@ + +
    +
    + $params) { ?> +
    +
    +
    + +
    + +
    + $params) { ?> +
    +
    +
    + +
    + +
    diff --git a/src/PHPCensor/View/Project/ajax-builds.phtml b/src/PHPCensor/View/Project/ajax-builds.phtml new file mode 100644 index 0000000..d683b13 --- /dev/null +++ b/src/PHPCensor/View/Project/ajax-builds.phtml @@ -0,0 +1,105 @@ + + + + + + + + + + +getStatus()) +{ + case 0: + $cls = 'active'; + $subcls = 'info'; + $status = Lang::get('pending'); + + break; + + case 1: + $cls = 'warning'; + $subcls = 'warning'; + $status = Lang::get('running'); + break; + + case 2: + $cls = 'success'; + $subcls = 'success'; + $status = Lang::get('success'); + break; + + case 3: + $cls = 'danger'; + $subcls = 'danger'; + $status = Lang::get('failed'); + break; +} + +$branches = $build->getExtra('branches'); +?> + + #getId(), 6, '0', STR_PAD_LEFT); ?> + + getCreateDate()->format('Y-m-d H:i:s'); ?> + getSourceHumanize()); ?> + + getCommitId())) { + print sprintf( + '%s %s', + $build->getCommitLink(), + substr($build->getCommitId(), 0, 7), + $build->getCommitterEmail() ? ('(' . $build->getCommitterEmail() . ')') : '' + ); + } else { + print '—'; + } + ?> + + + + getBranch(); ?> + + + getTag()): ?> / + + + + + + + getEnvironment(); + echo !empty($environment) ? $environment : '—' ; + ?> + + + getDuration(); ?> + + +
    + + User()->getIsAdmin()): ?> + + + +
    + + + + + diff --git a/src/PHPCensor/View/Project/edit.phtml b/src/PHPCensor/View/Project/edit.phtml new file mode 100644 index 0000000..69a12fc --- /dev/null +++ b/src/PHPCensor/View/Project/edit.phtml @@ -0,0 +1,47 @@ + + + + + + +
    + +
    +
    +
    +

    +
    + +
    + +
    +
    +
    + + +
    +
    +
    +

    + +
    +
    +
    + + +
    diff --git a/src/PHPCensor/View/Project/view.phtml b/src/PHPCensor/View/Project/view.phtml new file mode 100644 index 0000000..bf2dff7 --- /dev/null +++ b/src/PHPCensor/View/Project/view.phtml @@ -0,0 +1,222 @@ + + + +
    + + + + + + + + + getId(); + ?> +
    + getArchived()): ?> + User()->getIsAdmin()): ?> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + +
    +
    +
    + + +
    +
    +
    +
    +

    ()

    +
    + + + + + + + + + + + + + + + + + +
    +
    +
    + +
    +
    + +
    + + getAllowPublicStatus() && !$project->getArchived()): ?> +
    +
    +

    +
    + +
    +
    + +
    + + + + + +

    + :
    getId() . '?branch=master&label=PHPCensor&style=flat-square'; ?> + +

    + :
    getId() . '?branch=master'; ?> +
    +
    +
    + + + getType(), ['github', 'gitlab', 'bitbucket'])): ?> +
    +
    +

    +
    + +
    +
    + +
    + getType()) + { + case 'github': + $url = APP_URL . 'webhook/github/' . $project->getId(); + Lang::out('webhooks_help_github', $project->getReference()); + break; + + case 'gitlab': + $url = APP_URL. 'webhook/gitlab/' . $project->getId(); + Lang::out('webhooks_help_gitlab'); + break; + + case 'bitbucket': + case 'bitbuckethg': + $url = APP_URL . 'webhook/bitbucket/' . $project->getId(); + Lang::out('webhooks_help_bitbucket', $project->getReference()); + break; + } + ?> +

    +
    +
    + + + getSshPublicKey()): ?> +
    +
    +

    +
    + +
    +
    +
    getSshPublicKey(); ?>
    +
    + +
    +
    + + diff --git a/src/PHPCensor/View/Session.phtml b/src/PHPCensor/View/Session.phtml new file mode 100644 index 0000000..411c295 --- /dev/null +++ b/src/PHPCensor/View/Session.phtml @@ -0,0 +1,29 @@ + + + + + <?php Lang::out('log_in_to_app'); ?> + + + + + + + + +
    +
    + +
    + +
    +
    +
    + + + + + + diff --git a/src/PHPCensor/View/Session/forgotPassword.phtml b/src/PHPCensor/View/Session/forgotPassword.phtml new file mode 100644 index 0000000..75e1450 --- /dev/null +++ b/src/PHPCensor/View/Session/forgotPassword.phtml @@ -0,0 +1,28 @@ + + +

    + +

    + + +
    + +
    + +
    + +
    + +
    +
    +
    + + +
    + +
    + +
    +
    +
    + diff --git a/src/PHPCensor/View/Session/login.phtml b/src/PHPCensor/View/Session/login.phtml new file mode 100644 index 0000000..4ff7451 --- /dev/null +++ b/src/PHPCensor/View/Session/login.phtml @@ -0,0 +1,9 @@ + + +

    + + + + + + diff --git a/src/PHPCensor/View/Session/resetPassword.phtml b/src/PHPCensor/View/Session/resetPassword.phtml new file mode 100644 index 0000000..ee69378 --- /dev/null +++ b/src/PHPCensor/View/Session/resetPassword.phtml @@ -0,0 +1,23 @@ + + +
    + +
    + +
    +
    +
    + + +
    + +
    + +
    +
    +
    + +
    + +
    + diff --git a/src/PHPCensor/View/User/edit.phtml b/src/PHPCensor/View/User/edit.phtml new file mode 100644 index 0000000..96234e3 --- /dev/null +++ b/src/PHPCensor/View/User/edit.phtml @@ -0,0 +1,9 @@ +
    +
    +
    +
    + +
    +
    +
    +
    diff --git a/src/PHPCensor/View/User/index.phtml b/src/PHPCensor/View/User/index.phtml new file mode 100644 index 0000000..6eb7790 --- /dev/null +++ b/src/PHPCensor/View/User/index.phtml @@ -0,0 +1,70 @@ + +
    +
    + +
    +
    + +
    +
    +
    + + + + + + + + + + + + + getIsAdmin()) + { + case 0: + $cls = ''; + $status = Lang::get('no'); + break; + + case 1: + $cls = 'warning'; + $status = Lang::get('yes'); + break; + } + ?> + + + + + + + + +
    getEmail(); ?>getName()); ?> + User()->getIsAdmin()): ?> +
    + + + +
    + +
    +
    +
    +
    + + diff --git a/src/PHPCensor/View/User/profile.phtml b/src/PHPCensor/View/User/profile.phtml new file mode 100644 index 0000000..a9f9235 --- /dev/null +++ b/src/PHPCensor/View/User/profile.phtml @@ -0,0 +1,14 @@ + + +

    + + +
    +
    +

    +
    +
    + +
    +
    + diff --git a/src/PHPCensor/View/WidgetAllProjects/index-projects.phtml b/src/PHPCensor/View/WidgetAllProjects/index-projects.phtml new file mode 100644 index 0000000..1b046ba --- /dev/null +++ b/src/PHPCensor/View/WidgetAllProjects/index-projects.phtml @@ -0,0 +1,149 @@ +getId()])) { + // Get the most recent build status to determine the main block colour. + $last_build = $builds[$project->getId()][0]; + $status = $last_build->getStatus(); + switch($status) { + case 0: + $subcls = 'blue'; + break; + case 1: + $subcls = 'yellow'; + break; + case 2: + $subcls = 'green'; + break; + case 3: + $subcls = 'red'; + break; + } + // Use the last 5 builds to determine project health: + $failures = 0; + + foreach ($builds[$project->getId()] as $build) { + switch ($build->getStatus()) { + case 0: + $statuses[] = 'pending'; + break; + case 1: + $statuses[] = 'running'; + break; + case 2: + $statuses[] = 'ok'; + $success = is_null($success) && !is_null($build->getFinishDate()) ? $build->getFinishDate()->format('Y-m-d H:i:s') : $success; + break; + case 3: + $failures++; + $statuses[] = 'failed'; + $failure = is_null($failure) && !is_null($build->getFinishDate()) ? $build->getFinishDate()->format('Y-m-d H:i:s') : $failure; + break; + } + } + } + + $buildCount = count($builds[$project->getId()]); + $lastSuccess = $successful[$project->getId()]; + $lastFailure = $failed[$project->getId()]; + $message = Lang::get('no_builds_yet'); + $shortMessage = Lang::get('no_builds_yet'); + + if ($buildCount > 0) { + if ($failures > 0) { + $shortMessage = Lang::get('x_of_x_failed_short', $failures, $buildCount); + $message = Lang::get('x_of_x_failed', $failures, $buildCount); + + if (!is_null($lastSuccess) && !is_null($lastSuccess->getFinishDate())) { + $message .= Lang::get('last_successful_build', $lastSuccess->getFinishDate()->format('Y-m-d H:i:s')); + } else { + $message .= Lang::get('never_built_successfully'); + } + } else { + $message = Lang::get('all_builds_passed', $buildCount); + $shortMessage = Lang::get('all_builds_passed_short', $buildCount, $buildCount); + + if (!is_null($lastFailure) && !is_null($lastFailure->getFinishDate())) { + $message .= Lang::get('last_failed_build', $lastFailure->getFinishDate()->format('Y-m-d H:i:s')); + } else { + $message .= Lang::get('never_failed_build'); + } + } + } + +?> +
    +
    + +
    +

    + + getTitle(); ?> + +

    + +

    + +

    + +
    +
    + +
    + +
    + getAllowPublicStatus()): ?> + + + + +
    + (getId()]; ?>) +
    + + getId()][$idx])) { + echo ''; + } else { + $build = $builds[$project->getId()][$idx]; + $link = APP_URL . 'build/view/' . $build->id; + switch ($build->getStatus()) { + case 0: + $class = 'bg-blue'; + $icon = 'fa-clock-o'; + break; + case 1: + $class = 'bg-yellow'; + $icon = 'fa-cogs'; + break; + case 2: + $class = 'bg-green'; + $icon = 'fa-check'; + break; + case 3: + $class = 'bg-red'; + $icon = 'fa-times'; + break; + } + echo ''; + } + } ?> +
    +
    +
    + + diff --git a/src/PHPCensor/View/WidgetAllProjects/index.phtml b/src/PHPCensor/View/WidgetAllProjects/index.phtml new file mode 100644 index 0000000..7e614c9 --- /dev/null +++ b/src/PHPCensor/View/WidgetAllProjects/index.phtml @@ -0,0 +1,15 @@ + +
    +
    +

    +
    + +
    +
    +
    + +
    +
    + diff --git a/src/PHPCensor/View/WidgetAllProjects/update.phtml b/src/PHPCensor/View/WidgetAllProjects/update.phtml new file mode 100644 index 0000000..1f00e37 --- /dev/null +++ b/src/PHPCensor/View/WidgetAllProjects/update.phtml @@ -0,0 +1,145 @@ +getStatus(); + switch($status) { + case 0: + $subcls = 'blue'; + break; + case 1: + $subcls = 'yellow'; + break; + case 2: + $subcls = 'green'; + break; + case 3: + $subcls = 'red'; + break; + } + // Use the last 5 builds to determine project health: + $failures = 0; + + foreach ($builds as $build) { + switch ($build->getStatus()) { + case 0: + $statuses[] = 'pending'; + break; + case 1: + $statuses[] = 'running'; + break; + case 2: + $statuses[] = 'ok'; + $success = is_null($success) && !is_null($build->getFinishDate()) ? $build->getFinishDate()->format('Y-m-d H:i:s') : $success; + break; + case 3: + $failures++; + $statuses[] = 'failed'; + $failure = is_null($failure) && !is_null($build->getFinishDate()) ? $build->getFinishDate()->format('Y-m-d H:i:s') : $failure; + break; + } + } +} + +$buildCount = count($builds); +$lastSuccess = $successful; +$lastFailure = $failed; +$message = Lang::get('no_builds_yet'); +$shortMessage = Lang::get('no_builds_yet'); + +if ($buildCount > 0) { + if ($failures > 0) { + $shortMessage = Lang::get('x_of_x_failed_short', $failures, $buildCount); + $message = Lang::get('x_of_x_failed', $failures, $buildCount); + + if (!is_null($lastSuccess) && !is_null($lastSuccess->getFinishDate())) { + $message .= Lang::get('last_successful_build', $lastSuccess->getFinishDate()->format('Y-m-d H:i:s')); + } else { + $message .= Lang::get('never_built_successfully'); + } + } else { + $message = Lang::get('all_builds_passed', $buildCount); + $shortMessage = Lang::get('all_builds_passed_short', $buildCount, $buildCount); + + if (!is_null($lastFailure) && !is_null($lastFailure->getFinishDate())) { + $message .= Lang::get('last_failed_build', $lastFailure->getFinishDate()->format('Y-m-d H:i:s')); + } else { + $message .= Lang::get('never_failed_build'); + } + } +} + +?> + +
    + +
    +

    + + getTitle(); ?> + +

    + +

    + +

    + +
    +
    + +
    + +
    + getAllowPublicStatus()): ?> + + + + +
    + () +
    + + '; + } else { + $build = $builds[$idx]; + $link = APP_URL . 'build/view/' . $build->id; + switch ($build->getStatus()) { + case 0: + $class = 'bg-blue'; + $icon = 'fa-clock-o'; + break; + case 1: + $class = 'bg-yellow'; + $icon = 'fa-cogs'; + break; + case 2: + $class = 'bg-green'; + $icon = 'fa-check'; + break; + case 3: + $class = 'bg-red'; + $icon = 'fa-times'; + break; + } + echo ''; + } + } ?> +
    +
    diff --git a/src/PHPCensor/View/WidgetBuildErrors/empty.phtml b/src/PHPCensor/View/WidgetBuildErrors/empty.phtml new file mode 100644 index 0000000..cc08ea4 --- /dev/null +++ b/src/PHPCensor/View/WidgetBuildErrors/empty.phtml @@ -0,0 +1,5 @@ +
    \ No newline at end of file diff --git a/src/PHPCensor/View/WidgetBuildErrors/index.phtml b/src/PHPCensor/View/WidgetBuildErrors/index.phtml new file mode 100644 index 0000000..18595f3 --- /dev/null +++ b/src/PHPCensor/View/WidgetBuildErrors/index.phtml @@ -0,0 +1,13 @@ + +
    +
    +

    +
    +
    + +
    +
    diff --git a/src/PHPCensor/View/WidgetBuildErrors/update.phtml b/src/PHPCensor/View/WidgetBuildErrors/update.phtml new file mode 100644 index 0000000..47e4608 --- /dev/null +++ b/src/PHPCensor/View/WidgetBuildErrors/update.phtml @@ -0,0 +1,157 @@ + $project_envs): + if (!isset($projects[$project_id])) { + echo ''; + continue; + } + $project = $projects[$project_id]; + foreach($project_envs as $environment => $project_env): + $statuses = []; + $failures = 0; + $subcls = 'gray'; + $cls = ''; + $success = null; + $failure = null; + + // Get the most recent build status to determine the main block colour. + $last_build = $project_env['latest'][0]; + $status = $last_build->getStatus(); + switch($status) { + case 0: + $subcls = 'blue'; + break; + case 1: + $subcls = 'yellow'; + break; + case 2: + $subcls = 'green'; + break; + case 3: + $subcls = 'red'; + break; + } + // Use the last 5 builds to determine project health: + $failures = 0; + + foreach ($project_env['latest'] as $build) { + switch ($build->getStatus()) { + case 0: + $statuses[] = 'pending'; + break; + case 1: + $statuses[] = 'running'; + break; + case 2: + $statuses[] = 'ok'; + $success = is_null($success) && !is_null($build->getFinishDate()) ? $build->getFinishDate()->format('Y-m-d H:i:s') : $success; + break; + case 3: + $failures++; + $statuses[] = 'failed'; + $failure = is_null($failure) && !is_null($build->getFinishDate()) ? $build->getFinishDate()->format('Y-m-d H:i:s') : $failure; + break; + } + } + + $buildCount = count($project_env['latest']); + $lastSuccess = $project_env['success']; + $lastFailure = $project_env['failed']; + $message = Lang::get('no_builds_yet'); + $shortMessage = Lang::get('no_builds_yet'); + + if ($buildCount > 0) { + if ($failures > 0) { + $shortMessage = Lang::get('x_of_x_failed_short', $failures, $buildCount); + $message = Lang::get('x_of_x_failed', $failures, $buildCount); + + if (!is_null($lastSuccess) && !is_null($lastSuccess->getFinishDate())) { + $message .= Lang::get('last_successful_build', $lastSuccess->getFinishDate()->format('Y-m-d H:i:s')); + } else { + $message .= Lang::get('never_built_successfully'); + } + } else { + $message = Lang::get('all_builds_passed', $buildCount); + $shortMessage = Lang::get('all_builds_passed_short', $buildCount, $buildCount); + + if (!is_null($lastFailure) && !is_null($lastFailure->getFinishDate())) { + $message .= Lang::get('last_failed_build', $lastFailure->getFinishDate()->format('Y-m-d H:i:s')); + } else { + $message .= Lang::get('never_failed_build'); + } + } + } + +?> +
    +
    + +
    +

    + + getTitle(); ?> + + + + +

    + +

    + +

    + +
    +
    + +
    + +
    + getAllowPublicStatus()): ?> + + + + +
    + +
    + + '; + } else { + $build = $project_env['latest'][$idx]; + $link = APP_URL . 'build/view/' . $build->id; + switch ($build->getStatus()) { + case 0: + $class = 'bg-blue'; + $icon = 'fa-clock-o'; + break; + case 1: + $class = 'bg-yellow'; + $icon = 'fa-cogs'; + break; + case 2: + $class = 'bg-green'; + $icon = 'fa-check'; + break; + case 3: + $class = 'bg-red'; + $icon = 'fa-times'; + break; + } + echo ''; + } + } ?> +
    +
    +
    + + + diff --git a/src/PHPCensor/View/WidgetLastBuilds/index.phtml b/src/PHPCensor/View/WidgetLastBuilds/index.phtml new file mode 100644 index 0000000..24bc747 --- /dev/null +++ b/src/PHPCensor/View/WidgetLastBuilds/index.phtml @@ -0,0 +1,13 @@ + +
    +
    +

    +
    +
    + +
    +
    diff --git a/src/PHPCensor/View/WidgetLastBuilds/update.phtml b/src/PHPCensor/View/WidgetLastBuilds/update.phtml new file mode 100644 index 0000000..b903781 --- /dev/null +++ b/src/PHPCensor/View/WidgetLastBuilds/update.phtml @@ -0,0 +1,117 @@ + +
      + + + getEnvironment(); + $branches = $build->getExtra('branches'); + + switch ($build->getStatus()) { + case Build::STATUS_PENDING: + $updated = $build->getCreateDate(); + $label = Lang::get('pending'); + $color = 'blue'; + break; + + case Build::STATUS_RUNNING: + $updated = $build->getStartDate(); + $label = Lang::get('running'); + $color = 'yellow'; + break; + + case Build::STATUS_SUCCESS: + $updated = $build->getFinishDate(); + $label = Lang::get('success'); + $color = 'green'; + break; + + case Build::STATUS_FAILED: + $updated = $build->getFinishDate(); + $label = Lang::get('failed'); + $color = 'red'; + break; + } + + if (!$updated) { + $updated = $build->getCreateDate(); + } + + if ($updated->format('Y-m-d') != $last->format('Y-m-d')): $last = $updated; + ?> +
    • + + format('Y-m-d'); ?> + +
    • + + + + +
    • + +
      + + format('H:i:s'); + if ($build->getStatus() != Build::STATUS_PENDING) { + echo ' — ' . $build->getDuration(); ?> + +

      + + getProject()->getTitle(); ?> + + + — + + Build #getId(); ?> + + — + getSourceHumanize()); ?> +

      + +
      + getBranch(); ?> + + getTag()): ?> / + + + + + getCommitId())) { + echo ' — '; + echo sprintf( + '%s %s', + $build->getCommitLink(), + substr($build->getCommitId(), 0, 7), + $build->getCommitterEmail() ? ('(' . $build->getCommitterEmail() . ')') : '' + ); + if (!empty($build->getCommitMessage())) { + echo ' — '; + print $build->getCommitMessage(); + } + } + ?> +
      +
      +
    • + + + + +
    • + +
    • +
    diff --git a/src/PHPCensor/View/exception.phtml b/src/PHPCensor/View/exception.phtml new file mode 100644 index 0000000..69aeb12 --- /dev/null +++ b/src/PHPCensor/View/exception.phtml @@ -0,0 +1,16 @@ +
    +
    +

    Sorry, there was a problem

    +
    + + +
    + Message: getMessage(); ?>
    + File: getFile(); ?>
    + Line: getLine(); ?>
    + Trace: +
    getTraceAsString(); ?>
    +
    +
    \ No newline at end of file diff --git a/src/PHPCensor/View/layout.phtml b/src/PHPCensor/View/layout.phtml new file mode 100644 index 0000000..133af09 --- /dev/null +++ b/src/PHPCensor/View/layout.phtml @@ -0,0 +1,257 @@ + + + + + + <?php print $title; ?><?php print !empty($subtitle) ? ' - ' . $subtitle : ''; ?> + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + +
    + + + +
    + + + + +
    '; + } + ?> + + + + + + + diff --git a/src/PHPCensor/View/pagination.phtml b/src/PHPCensor/View/pagination.phtml new file mode 100644 index 0000000..c1bc12c --- /dev/null +++ b/src/PHPCensor/View/pagination.phtml @@ -0,0 +1,28 @@ + +
      + getPrevUrl()): ?> +
    • + + + getPages() as $pageArray): ?> + +
    • > + +
    • + +
    • + + + + getNextUrl()): ?> +
    • + +
    diff --git a/src/PHPCensor/Worker/BuildWorker.php b/src/PHPCensor/Worker/BuildWorker.php new file mode 100644 index 0000000..d88eb51 --- /dev/null +++ b/src/PHPCensor/Worker/BuildWorker.php @@ -0,0 +1,174 @@ +host = $host; + $this->queue = $queue; + $this->pheanstalk = new Pheanstalk($this->host); + } + + /** + * @param Logger $logger + */ + public function setLogger(Logger $logger) + { + $this->logger = $logger; + } + + /** + * Start the worker. + */ + public function startWorker() + { + $this->pheanstalk->watch($this->queue); + $this->pheanstalk->ignore('default'); + $buildStore = Factory::getStore('Build'); + + while ($this->run) { + // Get a job from the queue: + $job = $this->pheanstalk->reserve(); + + // Get the job data and run the job: + $jobData = json_decode($job->getData(), true); + + if (!$this->verifyJob($job, $jobData)) { + continue; + } + + $this->logger->addInfo('Received build #'.$jobData['build_id'].' from Beanstalkd'); + + try { + $build = BuildFactory::getBuildById($jobData['build_id']); + } catch (\Exception $ex) { + $this->logger->addWarning('Build #' . $jobData['build_id'] . ' does not exist in the database.'); + $this->pheanstalk->delete($job); + continue; + } + + // Logging relevant to this build should be stored + // against the build itself. + $buildDbLog = new BuildDBLogHandler($build, Logger::INFO); + $this->logger->pushHandler($buildDbLog); + + try { + $builder = new Builder($build, $this->logger); + $builder->execute(); + } catch (\PDOException $ex) { + // If we've caught a PDO Exception, it is probably not the fault of the build, but of a failed + // connection or similar. Release the job and kill the worker. + $this->run = false; + $this->pheanstalk->release($job); + unset($job); + } catch (\Exception $ex) { + $this->logger->addError($ex->getMessage()); + + $build->setStatus(Build::STATUS_FAILED); + $build->setFinishDate(new \DateTime()); + $build->setLog($build->getLog() . PHP_EOL . PHP_EOL . $ex->getMessage()); + $buildStore->save($build); + $build->sendStatusPostback(); + } + + // After execution we no longer want to record the information + // back to this specific build so the handler should be removed. + $this->logger->popHandler(); + // destructor implicitly call flush + unset($buildDbLog); + + // Delete the job when we're done: + if (!empty($job)) { + $this->pheanstalk->delete($job); + } + } + } + + /** + * Stops the worker after the current build. + */ + public function stopWorker() + { + $this->run = false; + } + + /** + * Checks that the job received is actually, and has a valid type. + * + * @param Job $job + * @param array $jobData + * + * @return boolean + */ + protected function verifyJob(Job $job, $jobData) + { + if (empty($jobData) || !is_array($jobData)) { + $this->pheanstalk->delete($job); + return false; + } + + if (!array_key_exists('type', $jobData) || $jobData['type'] !== 'php-censor.build') { + $this->pheanstalk->delete($job); + return false; + } + + return true; + } +} diff --git a/src/PHPCensor/ZeroConfigPluginInterface.php b/src/PHPCensor/ZeroConfigPluginInterface.php new file mode 100644 index 0000000..06ab844 --- /dev/null +++ b/src/PHPCensor/ZeroConfigPluginInterface.php @@ -0,0 +1,20 @@ + + */ +interface ZeroConfigPluginInterface +{ + /** + * @param string $stage + * @param Builder $builder + * @param Build $build + * + * @return mixed + */ + public static function canExecute($stage, Builder $builder, Build $build); +} From 8f2e9952a4b9f48544c7153a6054ccd50607a8eb Mon Sep 17 00:00:00 2001 From: Dmitry Khomutov Date: Sun, 10 Dec 2017 17:34:16 +0700 Subject: [PATCH 12/19] FIXME test --- src/PHPCensor/Worker/BuildWorker.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/PHPCensor/Worker/BuildWorker.php b/src/PHPCensor/Worker/BuildWorker.php index d88eb51..7f19842 100644 --- a/src/PHPCensor/Worker/BuildWorker.php +++ b/src/PHPCensor/Worker/BuildWorker.php @@ -61,6 +61,7 @@ class BuildWorker */ public function __construct($host, $queue) { + // string $host FIXME : string $host $this->host = $host; $this->queue = $queue; $this->pheanstalk = new Pheanstalk($this->host); From 6a82e4a600aa8c9f8f53380bb8e680b6ebd1b9c0 Mon Sep 17 00:00:00 2001 From: Dmitry Khomutov Date: Tue, 12 Dec 2017 20:42:48 +0700 Subject: [PATCH 13/19] FIXME test --- src/PHPCensor/Application.php | 172 --- src/PHPCensor/BuildFactory.php | 83 -- src/PHPCensor/Builder.php | 485 -------- src/PHPCensor/BuilderException.php | 9 - src/PHPCensor/Command/CreateAdminCommand.php | 96 -- src/PHPCensor/Command/CreateBuildCommand.php | 83 -- src/PHPCensor/Command/InstallCommand.php | 557 --------- src/PHPCensor/Command/RebuildCommand.php | 85 -- src/PHPCensor/Command/RebuildQueueCommand.php | 73 -- src/PHPCensor/Command/RunCommand.php | 173 --- .../Command/ScheduleBuildCommand.php | 101 -- src/PHPCensor/Command/WorkerCommand.php | 77 -- src/PHPCensor/Console/Application.php | 140 --- src/PHPCensor/Controller.php | 124 -- src/PHPCensor/Controller/BuildController.php | 371 ------ .../Controller/BuildStatusController.php | 236 ---- src/PHPCensor/Controller/GroupController.php | 126 -- src/PHPCensor/Controller/HomeController.php | 42 - .../Controller/ProjectController.php | 591 ---------- .../Controller/SessionController.php | 281 ----- src/PHPCensor/Controller/UserController.php | 304 ----- .../Controller/WebhookController.php | 760 ------------ .../WidgetAllProjectsController.php | 152 --- .../WidgetBuildErrorsController.php | 90 -- .../Controller/WidgetLastBuildsController.php | 70 -- src/PHPCensor/Helper/AnsiConverter.php | 46 - src/PHPCensor/Helper/Bitbucket.php | 108 -- src/PHPCensor/Helper/Build.php | 21 - src/PHPCensor/Helper/BuildInterpolator.php | 85 -- src/PHPCensor/Helper/CommandExecutor.php | 343 ------ .../Helper/CommandExecutorInterface.php | 43 - src/PHPCensor/Helper/Diff.php | 50 - src/PHPCensor/Helper/Email.php | 144 --- src/PHPCensor/Helper/Github.php | 170 --- src/PHPCensor/Helper/Lang.php | 190 --- src/PHPCensor/Helper/LoginIsDisabled.php | 31 - src/PHPCensor/Helper/MailerFactory.php | 85 -- src/PHPCensor/Helper/SshKey.php | 42 - src/PHPCensor/Helper/User.php | 32 - src/PHPCensor/Languages/lang.da.php | 289 ----- src/PHPCensor/Languages/lang.de.php | 325 ----- src/PHPCensor/Languages/lang.el.php | 291 ----- src/PHPCensor/Languages/lang.en.php | 426 ------- src/PHPCensor/Languages/lang.es.php | 285 ----- src/PHPCensor/Languages/lang.fr.php | 416 ------- src/PHPCensor/Languages/lang.it.php | 291 ----- src/PHPCensor/Languages/lang.nl.php | 291 ----- src/PHPCensor/Languages/lang.pl.php | 292 ----- src/PHPCensor/Languages/lang.pt-br.php | 328 ------ src/PHPCensor/Languages/lang.ru.php | 408 ------- src/PHPCensor/Languages/lang.uk.php | 291 ----- src/PHPCensor/Languages/lang.zh.php | 319 ----- src/PHPCensor/Logging/BuildDBLogHandler.php | 81 -- src/PHPCensor/Logging/BuildLogger.php | 114 -- src/PHPCensor/Logging/Handler.php | 145 --- .../Logging/LoggedBuildContextTidier.php | 38 - src/PHPCensor/Logging/OutputLogHandler.php | 41 - .../20140513143726_initial_migration.php | 210 ---- ...0513153133_change_build_keys_migration.php | 32 - .../20140611170618_choose_branch.php | 24 - .../20140730143702_fix_database_columns.php | 55 - .../20150131075425_archive_project.php | 24 - .../20150203105015_fix_column_types.php | 28 - .../20150308074509_add_user_providers.php | 34 - ...4958_unique_email_and_name_user_fields.php | 32 - .../20151008140800_add_project_groups.php | 49 - ...0151009100610_remove_unique_name_index.php | 19 - .../20151014091859_errors_table.php | 68 -- .../20151015124825_convert_errors.php | 182 --- .../20160425162114_branch_column_length.php | 24 - .../20160623100223_project_table_defaults.php | 13 - ...2_added_language_and_per_page_for_user.php | 40 - ...75400_fixed_build_error_message_column.php | 18 - ...13127_fixed_build_log_column_for_mysql.php | 26 - ...2922_fixed_build_log_column_for_mysql2.php | 26 - .../20170321131931_add_environment.php | 64 - ...256_added_source_column_to_build_table.php | 33 - .../20170416130610_fixed_environments.php | 28 - ...142131_added_tag_column_to_build_table.php | 28 - ...70711112805_fixed_build_meta_for_mysql.php | 26 - ...20170828142020_added_remember_me_login.php | 28 - ...170913141438_added_default_branch_only.php | 28 - ...348_removed_project_id_from_build_meta.php | 39 - ...0171015123827_added_additional_columns.php | 64 - ...171016143000_added_additional_columns2.php | 40 - ...171019143346_added_additional_columns3.php | 56 - src/PHPCensor/Model.php | 7 - src/PHPCensor/Model/Build.php | 1045 ----------------- src/PHPCensor/Model/Build/BitbucketBuild.php | 288 ----- .../Model/Build/BitbucketHgBuild.php | 65 - src/PHPCensor/Model/Build/GithubBuild.php | 274 ----- src/PHPCensor/Model/Build/GitlabBuild.php | 67 -- src/PHPCensor/Model/Build/GogsBuild.php | 36 - src/PHPCensor/Model/Build/LocalBuild.php | 91 -- src/PHPCensor/Model/Build/MercurialBuild.php | 99 -- src/PHPCensor/Model/Build/RemoteGitBuild.php | 162 --- src/PHPCensor/Model/Build/SubversionBuild.php | 127 -- src/PHPCensor/Model/BuildError.php | 449 ------- src/PHPCensor/Model/BuildMeta.php | 225 ---- src/PHPCensor/Model/Environment.php | 161 --- src/PHPCensor/Model/Project.php | 869 -------------- src/PHPCensor/Model/ProjectGroup.php | 175 --- src/PHPCensor/Model/User.php | 350 ------ src/PHPCensor/Plugin.php | 98 -- src/PHPCensor/Plugin/Atoum.php | 109 -- src/PHPCensor/Plugin/Behat.php | 128 -- src/PHPCensor/Plugin/Campfire.php | 151 --- src/PHPCensor/Plugin/CleanBuild.php | 59 - src/PHPCensor/Plugin/Codeception.php | 164 --- src/PHPCensor/Plugin/Composer.php | 124 -- src/PHPCensor/Plugin/CopyBuild.php | 91 -- src/PHPCensor/Plugin/Deployer.php | 81 -- src/PHPCensor/Plugin/Email.php | 214 ---- src/PHPCensor/Plugin/Env.php | 46 - src/PHPCensor/Plugin/FlowdockNotify.php | 73 -- src/PHPCensor/Plugin/Git.php | 157 --- src/PHPCensor/Plugin/Grunt.php | 81 -- src/PHPCensor/Plugin/Gulp.php | 81 -- src/PHPCensor/Plugin/HipchatNotify.php | 93 -- src/PHPCensor/Plugin/Irc.php | 119 -- src/PHPCensor/Plugin/Lint.php | 140 --- src/PHPCensor/Plugin/Mage.php | 116 -- src/PHPCensor/Plugin/Mage3.php | 121 -- src/PHPCensor/Plugin/Mysql.php | 160 --- .../Plugin/Option/PhpUnitOptions.php | 265 ----- src/PHPCensor/Plugin/PackageBuild.php | 86 -- src/PHPCensor/Plugin/Pdepend.php | 132 --- src/PHPCensor/Plugin/Pgsql.php | 76 -- src/PHPCensor/Plugin/Phar.php | 212 ---- src/PHPCensor/Plugin/Phing.php | 237 ---- src/PHPCensor/Plugin/PhpCodeSniffer.php | 287 ----- src/PHPCensor/Plugin/PhpCpd.php | 164 --- src/PHPCensor/Plugin/PhpCsFixer.php | 98 -- src/PHPCensor/Plugin/PhpDocblockChecker.php | 175 --- src/PHPCensor/Plugin/PhpLoc.php | 93 -- src/PHPCensor/Plugin/PhpMessDetector.php | 256 ---- src/PHPCensor/Plugin/PhpParallelLint.php | 143 --- src/PHPCensor/Plugin/PhpSpec.php | 118 -- src/PHPCensor/Plugin/PhpTalLint.php | 237 ---- src/PHPCensor/Plugin/PhpUnit.php | 177 --- src/PHPCensor/Plugin/SecurityChecker.php | 98 -- src/PHPCensor/Plugin/Shell.php | 77 -- src/PHPCensor/Plugin/SlackNotify.php | 137 --- src/PHPCensor/Plugin/Sqlite.php | 69 -- src/PHPCensor/Plugin/TechnicalDebt.php | 202 ---- src/PHPCensor/Plugin/Util/Executor.php | 288 ----- src/PHPCensor/Plugin/Util/Factory.php | 214 ---- src/PHPCensor/Plugin/Util/PhpUnitResult.php | 110 -- .../Plugin/Util/PhpUnitResultJson.php | 145 --- .../Plugin/Util/PhpUnitResultJunit.php | 130 -- .../Util/TestResultParsers/Codeception.php | 110 -- .../TestResultParsers/ParserInterface.php | 15 - src/PHPCensor/Plugin/Wipe.php | 55 - src/PHPCensor/Plugin/Xmpp.php | 196 ---- src/PHPCensor/ProcessControl/Factory.php | 52 - .../ProcessControl/PosixProcessControl.php | 42 - .../ProcessControlInterface.php | 30 - .../ProcessControl/UnixProcessControl.php | 50 - .../LoginPasswordProviderInterface.php | 23 - .../Security/Authentication/Service.php | 106 -- .../UserProvider/AbstractProvider.php | 35 - .../Authentication/UserProvider/Internal.php | 40 - .../Authentication/UserProvider/Ldap.php | 84 -- .../Authentication/UserProviderInterface.php | 30 - src/PHPCensor/Service/BuildService.php | 193 --- src/PHPCensor/Service/BuildStatusService.php | 228 ---- src/PHPCensor/Service/ProjectService.php | 152 --- src/PHPCensor/Service/UserService.php | 94 -- src/PHPCensor/Store.php | 7 - src/PHPCensor/Store/BuildErrorStore.php | 228 ---- src/PHPCensor/Store/BuildErrorWriter.php | 135 --- src/PHPCensor/Store/BuildMetaStore.php | 169 --- src/PHPCensor/Store/BuildStore.php | 501 -------- src/PHPCensor/Store/EnvironmentStore.php | 105 -- src/PHPCensor/Store/ProjectGroupStore.php | 99 -- src/PHPCensor/Store/ProjectStore.php | 241 ---- src/PHPCensor/Store/UserStore.php | 194 --- src/PHPCensor/View/Build/errors.phtml | 38 - src/PHPCensor/View/Build/header-row.phtml | 30 - src/PHPCensor/View/Build/view.phtml | 315 ----- src/PHPCensor/View/BuildStatus/view.phtml | 196 ---- src/PHPCensor/View/Email/layout.phtml | 28 - src/PHPCensor/View/Email/long.phtml | 2 - src/PHPCensor/View/Email/short.phtml | 1 - src/PHPCensor/View/Group/edit.phtml | 10 - src/PHPCensor/View/Group/index.phtml | 48 - src/PHPCensor/View/Home/index.phtml | 29 - src/PHPCensor/View/Project/ajax-builds.phtml | 105 -- src/PHPCensor/View/Project/edit.phtml | 47 - src/PHPCensor/View/Project/view.phtml | 222 ---- src/PHPCensor/View/Session.phtml | 29 - .../View/Session/forgotPassword.phtml | 28 - src/PHPCensor/View/Session/login.phtml | 9 - .../View/Session/resetPassword.phtml | 23 - src/PHPCensor/View/User/edit.phtml | 9 - src/PHPCensor/View/User/index.phtml | 70 -- src/PHPCensor/View/User/profile.phtml | 14 - .../WidgetAllProjects/index-projects.phtml | 149 --- .../View/WidgetAllProjects/index.phtml | 15 - .../View/WidgetAllProjects/update.phtml | 145 --- .../View/WidgetBuildErrors/empty.phtml | 5 - .../View/WidgetBuildErrors/index.phtml | 13 - .../View/WidgetBuildErrors/update.phtml | 157 --- .../View/WidgetLastBuilds/index.phtml | 13 - .../View/WidgetLastBuilds/update.phtml | 117 -- src/PHPCensor/View/exception.phtml | 16 - src/PHPCensor/View/layout.phtml | 257 ---- src/PHPCensor/View/pagination.phtml | 28 - src/PHPCensor/Worker/BuildWorker.php | 175 --- src/PHPCensor/ZeroConfigPluginInterface.php | 20 - 210 files changed, 29379 deletions(-) delete mode 100644 src/PHPCensor/Application.php delete mode 100644 src/PHPCensor/BuildFactory.php delete mode 100644 src/PHPCensor/Builder.php delete mode 100644 src/PHPCensor/BuilderException.php delete mode 100644 src/PHPCensor/Command/CreateAdminCommand.php delete mode 100644 src/PHPCensor/Command/CreateBuildCommand.php delete mode 100644 src/PHPCensor/Command/InstallCommand.php delete mode 100644 src/PHPCensor/Command/RebuildCommand.php delete mode 100644 src/PHPCensor/Command/RebuildQueueCommand.php delete mode 100644 src/PHPCensor/Command/RunCommand.php delete mode 100644 src/PHPCensor/Command/ScheduleBuildCommand.php delete mode 100644 src/PHPCensor/Command/WorkerCommand.php delete mode 100644 src/PHPCensor/Console/Application.php delete mode 100644 src/PHPCensor/Controller.php delete mode 100644 src/PHPCensor/Controller/BuildController.php delete mode 100644 src/PHPCensor/Controller/BuildStatusController.php delete mode 100644 src/PHPCensor/Controller/GroupController.php delete mode 100644 src/PHPCensor/Controller/HomeController.php delete mode 100644 src/PHPCensor/Controller/ProjectController.php delete mode 100644 src/PHPCensor/Controller/SessionController.php delete mode 100644 src/PHPCensor/Controller/UserController.php delete mode 100644 src/PHPCensor/Controller/WebhookController.php delete mode 100644 src/PHPCensor/Controller/WidgetAllProjectsController.php delete mode 100644 src/PHPCensor/Controller/WidgetBuildErrorsController.php delete mode 100644 src/PHPCensor/Controller/WidgetLastBuildsController.php delete mode 100644 src/PHPCensor/Helper/AnsiConverter.php delete mode 100644 src/PHPCensor/Helper/Bitbucket.php delete mode 100644 src/PHPCensor/Helper/Build.php delete mode 100644 src/PHPCensor/Helper/BuildInterpolator.php delete mode 100644 src/PHPCensor/Helper/CommandExecutor.php delete mode 100644 src/PHPCensor/Helper/CommandExecutorInterface.php delete mode 100644 src/PHPCensor/Helper/Diff.php delete mode 100644 src/PHPCensor/Helper/Email.php delete mode 100644 src/PHPCensor/Helper/Github.php delete mode 100644 src/PHPCensor/Helper/Lang.php delete mode 100644 src/PHPCensor/Helper/LoginIsDisabled.php delete mode 100644 src/PHPCensor/Helper/MailerFactory.php delete mode 100644 src/PHPCensor/Helper/SshKey.php delete mode 100644 src/PHPCensor/Helper/User.php delete mode 100644 src/PHPCensor/Languages/lang.da.php delete mode 100644 src/PHPCensor/Languages/lang.de.php delete mode 100644 src/PHPCensor/Languages/lang.el.php delete mode 100644 src/PHPCensor/Languages/lang.en.php delete mode 100644 src/PHPCensor/Languages/lang.es.php delete mode 100644 src/PHPCensor/Languages/lang.fr.php delete mode 100644 src/PHPCensor/Languages/lang.it.php delete mode 100644 src/PHPCensor/Languages/lang.nl.php delete mode 100644 src/PHPCensor/Languages/lang.pl.php delete mode 100644 src/PHPCensor/Languages/lang.pt-br.php delete mode 100644 src/PHPCensor/Languages/lang.ru.php delete mode 100644 src/PHPCensor/Languages/lang.uk.php delete mode 100644 src/PHPCensor/Languages/lang.zh.php delete mode 100644 src/PHPCensor/Logging/BuildDBLogHandler.php delete mode 100644 src/PHPCensor/Logging/BuildLogger.php delete mode 100644 src/PHPCensor/Logging/Handler.php delete mode 100644 src/PHPCensor/Logging/LoggedBuildContextTidier.php delete mode 100644 src/PHPCensor/Logging/OutputLogHandler.php delete mode 100644 src/PHPCensor/Migrations/20140513143726_initial_migration.php delete mode 100644 src/PHPCensor/Migrations/20140513153133_change_build_keys_migration.php delete mode 100644 src/PHPCensor/Migrations/20140611170618_choose_branch.php delete mode 100644 src/PHPCensor/Migrations/20140730143702_fix_database_columns.php delete mode 100644 src/PHPCensor/Migrations/20150131075425_archive_project.php delete mode 100644 src/PHPCensor/Migrations/20150203105015_fix_column_types.php delete mode 100644 src/PHPCensor/Migrations/20150308074509_add_user_providers.php delete mode 100644 src/PHPCensor/Migrations/20150324174958_unique_email_and_name_user_fields.php delete mode 100644 src/PHPCensor/Migrations/20151008140800_add_project_groups.php delete mode 100644 src/PHPCensor/Migrations/20151009100610_remove_unique_name_index.php delete mode 100644 src/PHPCensor/Migrations/20151014091859_errors_table.php delete mode 100644 src/PHPCensor/Migrations/20151015124825_convert_errors.php delete mode 100644 src/PHPCensor/Migrations/20160425162114_branch_column_length.php delete mode 100644 src/PHPCensor/Migrations/20160623100223_project_table_defaults.php delete mode 100644 src/PHPCensor/Migrations/20170103163312_added_language_and_per_page_for_user.php delete mode 100644 src/PHPCensor/Migrations/20170218175400_fixed_build_error_message_column.php delete mode 100644 src/PHPCensor/Migrations/20170223113127_fixed_build_log_column_for_mysql.php delete mode 100644 src/PHPCensor/Migrations/20170226132922_fixed_build_log_column_for_mysql2.php delete mode 100644 src/PHPCensor/Migrations/20170321131931_add_environment.php delete mode 100644 src/PHPCensor/Migrations/20170413131256_added_source_column_to_build_table.php delete mode 100644 src/PHPCensor/Migrations/20170416130610_fixed_environments.php delete mode 100644 src/PHPCensor/Migrations/20170420142131_added_tag_column_to_build_table.php delete mode 100644 src/PHPCensor/Migrations/20170711112805_fixed_build_meta_for_mysql.php delete mode 100644 src/PHPCensor/Migrations/20170828142020_added_remember_me_login.php delete mode 100644 src/PHPCensor/Migrations/20170913141438_added_default_branch_only.php delete mode 100644 src/PHPCensor/Migrations/20171014173348_removed_project_id_from_build_meta.php delete mode 100644 src/PHPCensor/Migrations/20171015123827_added_additional_columns.php delete mode 100644 src/PHPCensor/Migrations/20171016143000_added_additional_columns2.php delete mode 100644 src/PHPCensor/Migrations/20171019143346_added_additional_columns3.php delete mode 100644 src/PHPCensor/Model.php delete mode 100644 src/PHPCensor/Model/Build.php delete mode 100644 src/PHPCensor/Model/Build/BitbucketBuild.php delete mode 100644 src/PHPCensor/Model/Build/BitbucketHgBuild.php delete mode 100644 src/PHPCensor/Model/Build/GithubBuild.php delete mode 100644 src/PHPCensor/Model/Build/GitlabBuild.php delete mode 100644 src/PHPCensor/Model/Build/GogsBuild.php delete mode 100644 src/PHPCensor/Model/Build/LocalBuild.php delete mode 100644 src/PHPCensor/Model/Build/MercurialBuild.php delete mode 100644 src/PHPCensor/Model/Build/RemoteGitBuild.php delete mode 100644 src/PHPCensor/Model/Build/SubversionBuild.php delete mode 100644 src/PHPCensor/Model/BuildError.php delete mode 100644 src/PHPCensor/Model/BuildMeta.php delete mode 100644 src/PHPCensor/Model/Environment.php delete mode 100644 src/PHPCensor/Model/Project.php delete mode 100644 src/PHPCensor/Model/ProjectGroup.php delete mode 100644 src/PHPCensor/Model/User.php delete mode 100644 src/PHPCensor/Plugin.php delete mode 100644 src/PHPCensor/Plugin/Atoum.php delete mode 100644 src/PHPCensor/Plugin/Behat.php delete mode 100644 src/PHPCensor/Plugin/Campfire.php delete mode 100644 src/PHPCensor/Plugin/CleanBuild.php delete mode 100644 src/PHPCensor/Plugin/Codeception.php delete mode 100644 src/PHPCensor/Plugin/Composer.php delete mode 100644 src/PHPCensor/Plugin/CopyBuild.php delete mode 100644 src/PHPCensor/Plugin/Deployer.php delete mode 100644 src/PHPCensor/Plugin/Email.php delete mode 100644 src/PHPCensor/Plugin/Env.php delete mode 100644 src/PHPCensor/Plugin/FlowdockNotify.php delete mode 100644 src/PHPCensor/Plugin/Git.php delete mode 100644 src/PHPCensor/Plugin/Grunt.php delete mode 100644 src/PHPCensor/Plugin/Gulp.php delete mode 100644 src/PHPCensor/Plugin/HipchatNotify.php delete mode 100644 src/PHPCensor/Plugin/Irc.php delete mode 100644 src/PHPCensor/Plugin/Lint.php delete mode 100644 src/PHPCensor/Plugin/Mage.php delete mode 100644 src/PHPCensor/Plugin/Mage3.php delete mode 100644 src/PHPCensor/Plugin/Mysql.php delete mode 100644 src/PHPCensor/Plugin/Option/PhpUnitOptions.php delete mode 100644 src/PHPCensor/Plugin/PackageBuild.php delete mode 100644 src/PHPCensor/Plugin/Pdepend.php delete mode 100644 src/PHPCensor/Plugin/Pgsql.php delete mode 100644 src/PHPCensor/Plugin/Phar.php delete mode 100644 src/PHPCensor/Plugin/Phing.php delete mode 100644 src/PHPCensor/Plugin/PhpCodeSniffer.php delete mode 100644 src/PHPCensor/Plugin/PhpCpd.php delete mode 100644 src/PHPCensor/Plugin/PhpCsFixer.php delete mode 100644 src/PHPCensor/Plugin/PhpDocblockChecker.php delete mode 100644 src/PHPCensor/Plugin/PhpLoc.php delete mode 100644 src/PHPCensor/Plugin/PhpMessDetector.php delete mode 100644 src/PHPCensor/Plugin/PhpParallelLint.php delete mode 100644 src/PHPCensor/Plugin/PhpSpec.php delete mode 100644 src/PHPCensor/Plugin/PhpTalLint.php delete mode 100644 src/PHPCensor/Plugin/PhpUnit.php delete mode 100644 src/PHPCensor/Plugin/SecurityChecker.php delete mode 100644 src/PHPCensor/Plugin/Shell.php delete mode 100644 src/PHPCensor/Plugin/SlackNotify.php delete mode 100644 src/PHPCensor/Plugin/Sqlite.php delete mode 100644 src/PHPCensor/Plugin/TechnicalDebt.php delete mode 100644 src/PHPCensor/Plugin/Util/Executor.php delete mode 100644 src/PHPCensor/Plugin/Util/Factory.php delete mode 100644 src/PHPCensor/Plugin/Util/PhpUnitResult.php delete mode 100644 src/PHPCensor/Plugin/Util/PhpUnitResultJson.php delete mode 100644 src/PHPCensor/Plugin/Util/PhpUnitResultJunit.php delete mode 100644 src/PHPCensor/Plugin/Util/TestResultParsers/Codeception.php delete mode 100644 src/PHPCensor/Plugin/Util/TestResultParsers/ParserInterface.php delete mode 100644 src/PHPCensor/Plugin/Wipe.php delete mode 100644 src/PHPCensor/Plugin/Xmpp.php delete mode 100644 src/PHPCensor/ProcessControl/Factory.php delete mode 100644 src/PHPCensor/ProcessControl/PosixProcessControl.php delete mode 100644 src/PHPCensor/ProcessControl/ProcessControlInterface.php delete mode 100644 src/PHPCensor/ProcessControl/UnixProcessControl.php delete mode 100644 src/PHPCensor/Security/Authentication/LoginPasswordProviderInterface.php delete mode 100644 src/PHPCensor/Security/Authentication/Service.php delete mode 100644 src/PHPCensor/Security/Authentication/UserProvider/AbstractProvider.php delete mode 100644 src/PHPCensor/Security/Authentication/UserProvider/Internal.php delete mode 100644 src/PHPCensor/Security/Authentication/UserProvider/Ldap.php delete mode 100644 src/PHPCensor/Security/Authentication/UserProviderInterface.php delete mode 100644 src/PHPCensor/Service/BuildService.php delete mode 100644 src/PHPCensor/Service/BuildStatusService.php delete mode 100644 src/PHPCensor/Service/ProjectService.php delete mode 100644 src/PHPCensor/Service/UserService.php delete mode 100644 src/PHPCensor/Store.php delete mode 100644 src/PHPCensor/Store/BuildErrorStore.php delete mode 100644 src/PHPCensor/Store/BuildErrorWriter.php delete mode 100644 src/PHPCensor/Store/BuildMetaStore.php delete mode 100644 src/PHPCensor/Store/BuildStore.php delete mode 100644 src/PHPCensor/Store/EnvironmentStore.php delete mode 100644 src/PHPCensor/Store/ProjectGroupStore.php delete mode 100644 src/PHPCensor/Store/ProjectStore.php delete mode 100644 src/PHPCensor/Store/UserStore.php delete mode 100644 src/PHPCensor/View/Build/errors.phtml delete mode 100644 src/PHPCensor/View/Build/header-row.phtml delete mode 100644 src/PHPCensor/View/Build/view.phtml delete mode 100644 src/PHPCensor/View/BuildStatus/view.phtml delete mode 100644 src/PHPCensor/View/Email/layout.phtml delete mode 100644 src/PHPCensor/View/Email/long.phtml delete mode 100644 src/PHPCensor/View/Email/short.phtml delete mode 100644 src/PHPCensor/View/Group/edit.phtml delete mode 100644 src/PHPCensor/View/Group/index.phtml delete mode 100644 src/PHPCensor/View/Home/index.phtml delete mode 100644 src/PHPCensor/View/Project/ajax-builds.phtml delete mode 100644 src/PHPCensor/View/Project/edit.phtml delete mode 100644 src/PHPCensor/View/Project/view.phtml delete mode 100644 src/PHPCensor/View/Session.phtml delete mode 100644 src/PHPCensor/View/Session/forgotPassword.phtml delete mode 100644 src/PHPCensor/View/Session/login.phtml delete mode 100644 src/PHPCensor/View/Session/resetPassword.phtml delete mode 100644 src/PHPCensor/View/User/edit.phtml delete mode 100644 src/PHPCensor/View/User/index.phtml delete mode 100644 src/PHPCensor/View/User/profile.phtml delete mode 100644 src/PHPCensor/View/WidgetAllProjects/index-projects.phtml delete mode 100644 src/PHPCensor/View/WidgetAllProjects/index.phtml delete mode 100644 src/PHPCensor/View/WidgetAllProjects/update.phtml delete mode 100644 src/PHPCensor/View/WidgetBuildErrors/empty.phtml delete mode 100644 src/PHPCensor/View/WidgetBuildErrors/index.phtml delete mode 100644 src/PHPCensor/View/WidgetBuildErrors/update.phtml delete mode 100644 src/PHPCensor/View/WidgetLastBuilds/index.phtml delete mode 100644 src/PHPCensor/View/WidgetLastBuilds/update.phtml delete mode 100644 src/PHPCensor/View/exception.phtml delete mode 100644 src/PHPCensor/View/layout.phtml delete mode 100644 src/PHPCensor/View/pagination.phtml delete mode 100644 src/PHPCensor/Worker/BuildWorker.php delete mode 100644 src/PHPCensor/ZeroConfigPluginInterface.php diff --git a/src/PHPCensor/Application.php b/src/PHPCensor/Application.php deleted file mode 100644 index 495ffd6..0000000 --- a/src/PHPCensor/Application.php +++ /dev/null @@ -1,172 +0,0 @@ - - */ -class Application extends b8\Application -{ - /** - * @var \PHPCensor\Controller - */ - protected $controller; - - /** - * Initialise Application - Handles session verification, routing, etc. - */ - public function init() - { - $request =& $this->request; - $route = '/:controller/:action'; - $opts = ['controller' => 'Home', 'action' => 'index']; //FIXME : Initialise Application - Handles session verification, routing, etc. - - // Inlined as a closure to fix "using $this when not in object context" on 5.3 - $validateSession = function () { - if (!empty($_SESSION['php-censor-user-id'])) { - $user = b8\Store\Factory::getStore('User')->getByPrimaryKey($_SESSION['php-censor-user-id']); - - if ($user) { - $_SESSION['php-censor-user'] = $user; - return true; - } - - unset($_SESSION['php-censor-user-id']); - } - - return false; - }; - - $skipAuth = [$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) { - $skipValidation = in_array($route['controller'], ['session', 'webhook', 'build-status']); - - if (!$skipValidation && !$validateSession() && (!is_callable($skipAuth) || !$skipAuth())) { - if ($request->isAjax()) { - $response->setResponseCode(401); - $response->setContent(''); - } else { - $_SESSION['php-censor-login-redirect'] = substr($request->getPath(), 1); - $response = new RedirectResponse($response); - $response->setHeader('Location', APP_URL . 'session/login'); - } - - return false; - } - - return true; - }; - - $this->router->clearRoutes(); - $this->router->register($route, $opts, $routeHandler); - } - - /** - * Handle an incoming web request. - * - * @return Response - */ - public function handleRequest() - { - try { - $this->response = parent::handleRequest(); // FIX ME : Handle an incoming web request. - } catch (HttpException $ex) { - $this->config->set('page_title', 'Error'); - - $view = new View('exception'); - $view->exception = $ex; - - $this->response->setResponseCode($ex->getErrorCode()); - $this->response->setContent($view->render()); - } catch (\Exception $ex) { - $this->config->set('page_title', 'Error'); - - $view = new View('exception'); - $view->exception = $ex; - - $this->response->setResponseCode(500); - $this->response->setContent($view->render()); - } - - if ($this->response->hasLayout() && $this->controller && $this->controller->layout) { - $this->setLayoutVariables($this->controller->layout); - - $this->controller->layout->content = $this->response->getContent(); - $this->response->setContent($this->controller->layout->render()); - } - - return $this->response; - } - - /** - * Loads a particular controller, and injects our layout view into it. - * - * @param string $class - * - * @return b8\Controller - */ - protected function loadController($class) - { - $controller = parent::loadController($class); - $controller->layout = new View('layout'); - $controller->layout->title = 'PHP Censor'; - $controller->layout->breadcrumb = []; - - return $controller; - } - - /** - * Injects variables into the layout before rendering it. - * - * @param View $layout - */ - protected function setLayoutVariables(View &$layout) - { - $groups = []; - $groupStore = b8\Store\Factory::getStore('ProjectGroup'); - $groupList = $groupStore->getWhere([], 100, 0, [], ['title' => 'ASC']); - - foreach ($groupList['items'] as $group) { - $thisGroup = ['title' => $group->getTitle()]; - $projects = b8\Store\Factory::getStore('Project')->getByGroupId($group->getId(), false); - $thisGroup['projects'] = $projects['items']; - $groups[] = $thisGroup; - } - - $archived_projects = b8\Store\Factory::getStore('Project')->getAll(true); - $layout->archived_projects = $archived_projects['items']; - $layout->groups = $groups; - } - - /** - * Check whether we should skip auth (because it is disabled) - * - * @return boolean - */ - protected function shouldSkipAuth() - { - $config = b8\Config::getInstance(); - $disableAuth = (bool)$config->get('php-censor.security.disable_auth', false); - $defaultUserId = (integer)$config->get('php-censor.security.default_user_id', 1); - - if ($disableAuth && $defaultUserId) { - $user = b8\Store\Factory::getStore('User') - ->getByPrimaryKey($defaultUserId); - - if ($user) { - $_SESSION['php-censor-user'] = $user; - return true; - } - } - - return false; - } -} diff --git a/src/PHPCensor/BuildFactory.php b/src/PHPCensor/BuildFactory.php deleted file mode 100644 index 9569073..0000000 --- a/src/PHPCensor/BuildFactory.php +++ /dev/null @@ -1,83 +0,0 @@ - - */ -class BuildFactory -{ - /** - * @param $buildId - * - * @throws \Exception - * - * @return Build - */ - public static function getBuildById($buildId) - { - $build = Factory::getStore('Build')->getById($buildId); - - if (empty($build)) { - throw new \Exception('Build ID ' . $buildId . ' does not exist.'); - } - - return self::getBuild($build); - } - - /** - * Takes a generic build and returns a type-specific build model. - * - * @param Build $build The build from which to get a more specific build type. - * - * @return Build - */ - public static function getBuild(Build $build) - { - $project = $build->getProject(); - - if (!empty($project)) { - switch ($project->getType()) { - case 'remote': - $type = 'RemoteGitBuild'; - break; - case 'local': - $type = 'LocalBuild'; - break; - case 'github': - $type = 'GithubBuild'; - break; - case 'bitbucket': - $type = 'BitbucketBuild'; - break; - case 'bitbuckethg': - $type = 'BitbucketHgBuild'; - break; - case 'gitlab': - $type = 'GitlabBuild'; - break; - case 'hg': - $type = 'MercurialBuild'; - break; - case 'svn': - $type = 'SubversionBuild'; - break; - case 'gogs': - $type = 'GogsBuild'; - break; - default: - return $build; - } - - $class = '\\PHPCensor\\Model\\Build\\' . $type; - $build = new $class($build->getDataArray()); - } - - return $build; - } -} diff --git a/src/PHPCensor/Builder.php b/src/PHPCensor/Builder.php deleted file mode 100644 index 20ec48b..0000000 --- a/src/PHPCensor/Builder.php +++ /dev/null @@ -1,485 +0,0 @@ - - */ -class Builder implements LoggerAwareInterface -{ - /** - * @var string - */ - public $buildPath; - - /** - * @var string[] - */ - public $ignore = []; - - /** - * @var string - */ - protected $directory; - - /** - * @var bool - */ - protected $verbose = true; - - /** - * @var \PHPCensor\Model\Build - */ - protected $build; - - /** - * @var LoggerInterface - */ - protected $logger; - - /** - * @var array - */ - protected $config = []; - - /** - * @var string - */ - protected $lastOutput; - - /** - * @var BuildInterpolator - */ - protected $interpolator; - - /** - * @var \PHPCensor\Store\BuildStore - */ - protected $store; - - /** - * @var bool - */ - public $quiet = false; - - /** - * @var \PHPCensor\Plugin\Util\Executor - */ - protected $pluginExecutor; - - /** - * @var Helper\CommandExecutorInterface - */ - protected $commandExecutor; - - /** - * @var Logging\BuildLogger - */ - protected $buildLogger; - - /** - * @var BuildErrorWriter - */ - private $buildErrorWriter; - - /** - * Set up the builder. - * - * @param \PHPCensor\Model\Build $build - * @param LoggerInterface $logger - */ - public function __construct(Build $build, LoggerInterface $logger = null) - { - $this->build = $build; - $this->store = Factory::getStore('Build', 'PHPCensor'); - - $this->buildLogger = new BuildLogger($logger, $build); - $pluginFactory = $this->buildPluginFactory($build); - $this->pluginExecutor = new Plugin\Util\Executor($pluginFactory, $this->buildLogger); - - $executorClass = 'PHPCensor\Helper\CommandExecutor'; - $this->commandExecutor = new $executorClass( - $this->buildLogger, - ROOT_DIR, - $this->quiet, - $this->verbose - ); - - $this->interpolator = new BuildInterpolator(); - $this->buildErrorWriter = new BuildErrorWriter($this->build->getId()); - } - - /** - * Set the config array, as read from .php-censor.yml - * - * @param array $config - * - * @throws \Exception - */ - public function setConfigArray($config) - { - if (is_null($config) || !is_array($config)) { - throw new \Exception('This project does not contain a .php-censor.yml (.phpci.yml|phpci.yml) file, or it is empty.'); - } - - $this->logDebug('Config: ' . json_encode($config)); - $this->config = $config; - } - - /** - * Access a variable from the .php-censor.yml file. - * - * @param string $key - * - * @return mixed - */ - public function getConfig($key) - { - $rtn = null; - - if (isset($this->config[$key])) { - $rtn = $this->config[$key]; - } - - return $rtn; - } - - /** - * Access a variable from the config.yml - * - * @param string $key - * - * @return mixed - */ - public function getSystemConfig($key) - { - return Config::getInstance()->get($key); - } - - /** - * @return string The title of the project being built. - */ - public function getBuildProjectTitle() - { - return $this->build->getProject()->getTitle(); - } - - /** - * Run the active build. - */ - public function execute() - { - // check current status - if ($this->build->getStatus() != Build::STATUS_PENDING) { - throw new BuilderException('Can`t build - status is not pending', BuilderException::FAIL_START); - } - // set status only if current status pending - if (!$this->build->setStatusSync(Build::STATUS_RUNNING)) { - throw new BuilderException('Can`t build - unable change status to running', BuilderException::FAIL_START); - } - - // Update the build in the database, ping any external services. - $this->build->setStartDate(new \DateTime()); - $this->store->save($this->build); - $this->build->sendStatusPostback(); - $success = true; - - $previous_build = $this->build->getProject()->getPreviousBuild($this->build->getBranch()); - - $previous_state = Build::STATUS_PENDING; - - if ($previous_build) { - $previous_state = $previous_build->getStatus(); - } - - try { - // Set up the build: - $this->setupBuild(); - - // Run the core plugin stages: - foreach ([Build::STAGE_SETUP, Build::STAGE_TEST, Build::STAGE_DEPLOY] as $stage) { - $success &= $this->pluginExecutor->executePlugins($this->config, $stage); - if (!$success) { - break; - } - } - - // Set the status so this can be used by complete, success and failure - // stages. - if ($success) { - $this->build->setStatus(Build::STATUS_SUCCESS); - } else { - $this->build->setStatus(Build::STATUS_FAILED); - } - } catch (\Exception $ex) { - $success = false; - $this->build->setStatus(Build::STATUS_FAILED); - $this->buildLogger->logFailure('Exception: ' . $ex->getMessage(), $ex); - } - - try { - if ($success) { - $this->pluginExecutor->executePlugins($this->config, Build::STAGE_SUCCESS); - - if ($previous_state == Build::STATUS_FAILED) { - $this->pluginExecutor->executePlugins($this->config, Build::STAGE_FIXED); - } - } else { - $this->pluginExecutor->executePlugins($this->config, Build::STAGE_FAILURE); - - if ($previous_state == Build::STATUS_SUCCESS || $previous_state == Build::STATUS_PENDING) { - $this->pluginExecutor->executePlugins($this->config, Build::STAGE_BROKEN); - } - } - } catch (\Exception $ex) { - $this->buildLogger->logFailure('Exception: ' . $ex->getMessage(), $ex); - } - - if (Build::STATUS_FAILED === $this->build->getStatus()) { - $this->buildLogger->logFailure("\nBUILD FAILED"); - } else { - $this->buildLogger->logSuccess("\nBUILD SUCCESS"); - } - - try { - // Complete stage plugins are always run - $this->pluginExecutor->executePlugins($this->config, Build::STAGE_COMPLETE); - } catch (\Exception $ex) { - $this->buildLogger->logFailure('Exception: ' . $ex->getMessage()); - } - - // Update the build in the database, ping any external services, etc. - $this->build->sendStatusPostback(); - $this->build->setFinishDate(new \DateTime()); - - $removeBuilds = (bool)Config::getInstance()->get('php-censor.build.remove_builds', true); - if ($removeBuilds) { - // Clean up: - $this->buildLogger->log("\nRemoving Build."); - $this->build->removeBuildDirectory(); - } - - $this->buildErrorWriter->flush(); - $this->store->save($this->build); - } - - /** - * Used by this class, and plugins, to execute shell commands. - * - * @return boolean - */ - public function executeCommand() - { - return $this->commandExecutor->executeCommand(func_get_args()); - } - - /** - * Returns the output from the last command run. - * - * @return string - */ - public function getLastOutput() - { - return $this->commandExecutor->getLastOutput(); - } - - /** - * Specify whether exec output should be logged. - * - * @param boolean $enableLog - */ - public function logExecOutput($enableLog = true) - { - $this->commandExecutor->logExecOutput = $enableLog; - } - - /** - * Find a binary required by a plugin. - * - * @param string $binary - * @param bool $quiet Returns null instead of throwing an exception. - * @param string $priorityPath - * - * @return null|string - * - * @throws \Exception when no binary has been found and $quiet is false. - */ - public function findBinary($binary, $quiet = false, $priorityPath = 'local') - { - return $this->commandExecutor->findBinary($binary, $quiet, $priorityPath); - } - - /** - * Replace every occurrence of the interpolation vars in the given string - * Example: "This is build %PHPCI_BUILD%" => "This is build 182" - * - * @param string $input - * - * @return string - */ - public function interpolate($input) - { - return $this->interpolator->interpolate($input); - } - - /** - * Set up a working copy of the project for building. - * - * @throws \Exception - * - * @return boolean - */ - protected function setupBuild() - { - $this->buildPath = $this->build->getBuildPath(); - - $this->interpolator->setupInterpolationVars( - $this->build, - $this->buildPath, - APP_URL - ); - - $this->commandExecutor->setBuildPath($this->buildPath); - - // Create a working copy of the project: - if (!$this->build->createWorkingCopy($this, $this->buildPath)) { - throw new \Exception('Could not create a working copy.'); - } - - // Does the project's .php-censor.yml request verbose mode? - if (!isset($this->config['build_settings']['verbose']) || !$this->config['build_settings']['verbose']) { - $this->verbose = false; - } - - // Does the project have any paths it wants plugins to ignore? - if (isset($this->config['build_settings']['ignore'])) { - $this->ignore = $this->config['build_settings']['ignore']; - } - - $this->buildLogger->logSuccess(sprintf('Working copy created: %s', $this->buildPath)); - - return true; - } - - /** - * Sets a logger instance on the object - * - * @param LoggerInterface $logger - */ - public function setLogger(LoggerInterface $logger) - { - $this->buildLogger->setLogger($logger); - } - - /** - * Write to the build log. - * - * @param string $message - * @param string $level - * @param array $context - */ - public function log($message, $level = LogLevel::INFO, $context = []) - { - $this->buildLogger->log($message, $level, $context); - } - - /** - * Add a success-coloured message to the log. - * - * @param string - */ - public function logSuccess($message) - { - $this->buildLogger->logSuccess($message); - } - - /** - * Add a failure-coloured message to the log. - * - * @param string $message - * @param \Exception $exception The exception that caused the error. - */ - public function logFailure($message, \Exception $exception = null) - { - $this->buildLogger->logFailure($message, $exception); - } - - /** - * Add a debug message to the log. - * - * @param string - */ - public function logDebug($message) - { - $this->buildLogger->logDebug($message); - } - - /** - * Returns a configured instance of the plugin factory. - * - * @param Build $build - * - * @return PluginFactory - */ - private function buildPluginFactory(Build $build) - { - $pluginFactory = new PluginFactory(); - - $self = $this; - $pluginFactory->registerResource( - function () use ($self) { - return $self; - }, - null, - 'PHPCensor\Builder' - ); - - $pluginFactory->registerResource( - function () use ($build) { - return $build; - }, - null, - 'PHPCensor\Model\Build' - ); - - $logger = $this->logger; - $pluginFactory->registerResource( - function () use ($logger) { - return $logger; - }, - null, - 'Psr\Log\LoggerInterface' - ); - - $pluginFactory->registerResource( - function () use ($self) { - $factory = new MailerFactory($self->getSystemConfig('php-censor')); - return $factory->getSwiftMailerFromConfig(); - }, - null, - 'Swift_Mailer' - ); - - return $pluginFactory; - } - - /** - * @return BuildErrorWriter - */ - public function getBuildErrorWriter() - { - return $this->buildErrorWriter; - } -} diff --git a/src/PHPCensor/BuilderException.php b/src/PHPCensor/BuilderException.php deleted file mode 100644 index 67a05b9..0000000 --- a/src/PHPCensor/BuilderException.php +++ /dev/null @@ -1,9 +0,0 @@ -userStore = $userStore; - } - - protected function configure() - { - $this - ->setName('php-censor:create-admin') - - ->addOption('admin-name', null, InputOption::VALUE_OPTIONAL, 'Admin name') - ->addOption('admin-password', null, InputOption::VALUE_OPTIONAL, 'Admin password') - ->addOption('admin-email', null, InputOption::VALUE_OPTIONAL, 'Admin email') - - ->setDescription('Create an admin user'); - } - - /** - * Creates an admin user in the existing database - * - * {@inheritDoc} - */ - protected function execute(InputInterface $input, OutputInterface $output) - { - /** @var $helper QuestionHelper */ - $helper = $this->getHelperSet()->get('question'); - - // Function to validate email address. - $mailValidator = function ($answer) { - if (!filter_var($answer, FILTER_VALIDATE_EMAIL)) { - throw new \InvalidArgumentException('Must be a valid email address.'); - } - - return $answer; - }; - - if ($adminEmail = $input->getOption('admin-email')) { - $adminEmail = $mailValidator($adminEmail); - } else { - $questionEmail = new Question('Admin email: '); - $adminEmail = $helper->ask($input, $output, $questionEmail); - } - - if (!$adminName = $input->getOption('admin-name')) { - $questionName = new Question('Admin name: '); - $adminName = $helper->ask($input, $output, $questionName); - } - - if (!$adminPassword = $input->getOption('admin-password')) { - $questionPassword = new Question('Admin password: '); - $questionPassword->setHidden(true); - $questionPassword->setHiddenFallback(false); - $adminPassword = $helper->ask($input, $output, $questionPassword); - } - - try { - $userService = new UserService($this->userStore); - $userService->createUser($adminName, $adminEmail, 'internal', json_encode(['type' => 'internal']), $adminPassword, true); - - $output->writeln('User account created!'); - } catch (\Exception $ex) { - $output->writeln('PHP Censor failed to create your admin account!'); - $output->writeln('' . $ex->getMessage() . ''); - } - } -} diff --git a/src/PHPCensor/Command/CreateBuildCommand.php b/src/PHPCensor/Command/CreateBuildCommand.php deleted file mode 100644 index 28b9759..0000000 --- a/src/PHPCensor/Command/CreateBuildCommand.php +++ /dev/null @@ -1,83 +0,0 @@ -projectStore = $projectStore; - $this->buildService = $buildService; - } - - /** - * {@inheritDoc} - */ - protected function configure() - { - $this - ->setName('php-censor:create-build') - ->setDescription('Create a build for a project') - ->addArgument('projectId', InputArgument::REQUIRED, 'A project ID') - ->addOption('commit', null, InputOption::VALUE_OPTIONAL, 'Commit ID to build') - ->addOption('branch', null, InputOption::VALUE_OPTIONAL, 'Branch to build') - ->addOption('email', null, InputOption::VALUE_OPTIONAL, 'Committer email') - ->addOption('message', null, InputOption::VALUE_OPTIONAL, 'Commit message'); - } - - /** - * {@inheritDoc} - */ - public function execute(InputInterface $input, OutputInterface $output) - { - $projectId = $input->getArgument('projectId'); - $commitId = $input->getOption('commit'); - $branch = $input->getOption('branch'); - $environment = $input->hasOption('environment') ? $input->getOption('environment') : null; - $ciEmail = $input->getOption('email'); - $ciMessage = $input->getOption('message'); - - $project = $this->projectStore->getById($projectId); - if (empty($project) || $project->getArchived()) { - throw new \InvalidArgumentException('Project does not exist: ' . $projectId); - } - - try { - $this->buildService->createBuild($project, $environment, $commitId, $branch, null, $ciEmail, $ciMessage, Build::SOURCE_MANUAL_CONSOLE); - $output->writeln('Build Created'); - } catch (\Exception $e) { - $output->writeln('Failed'); - $output->writeln(sprintf('%s', $e->getMessage())); - } - } -} diff --git a/src/PHPCensor/Command/InstallCommand.php b/src/PHPCensor/Command/InstallCommand.php deleted file mode 100644 index 16e2537..0000000 --- a/src/PHPCensor/Command/InstallCommand.php +++ /dev/null @@ -1,557 +0,0 @@ - - */ -class InstallCommand extends Command -{ - /** - * @var string - */ - protected $configPath = APP_DIR . 'config.yml'; - - protected function configure() - { - $this - ->setName('php-censor:install') - - ->addOption('url', null, InputOption::VALUE_OPTIONAL, 'PHP Censor installation URL') - ->addOption('db-type', null, InputOption::VALUE_OPTIONAL, 'Database type') - ->addOption('db-host', null, InputOption::VALUE_OPTIONAL, 'Database host') - ->addOption('db-port', null, InputOption::VALUE_OPTIONAL, 'Database port') - ->addOption('db-name', null, InputOption::VALUE_OPTIONAL, 'Database name') - ->addOption('db-user', null, InputOption::VALUE_OPTIONAL, 'Database user') - ->addOption('db-password', null, InputOption::VALUE_OPTIONAL, 'Database password') - ->addOption('admin-name', null, InputOption::VALUE_OPTIONAL, 'Admin name') - ->addOption('admin-password', null, InputOption::VALUE_OPTIONAL, 'Admin password') - ->addOption('admin-email', null, InputOption::VALUE_OPTIONAL, 'Admin email') - ->addOption('queue-use', null, InputOption::VALUE_OPTIONAL, 'Don\'t ask for queue details', true) - ->addOption('queue-host', null, InputOption::VALUE_OPTIONAL, 'Beanstalkd queue server hostname') - ->addOption('queue-name', null, InputOption::VALUE_OPTIONAL, 'Beanstalkd queue name') - ->addOption('config-from-file', null, InputOption::VALUE_OPTIONAL, 'Take config from file and ignore options', false) - - ->setDescription('Install PHP Censor'); - } - - /** - * Installs PHP Censor - */ - protected function execute(InputInterface $input, OutputInterface $output) - { - $configFromFile = (boolean)$input->getOption('config-from-file'); - - if (!$configFromFile && !$this->verifyNotInstalled($output)) { - return; - } - - $output->writeln(''); - $output->writeln('***************************************'); - $output->writeln('* Welcome to PHP Censor installation *'); - $output->writeln('***************************************'); - $output->writeln(''); - - $this->checkRequirements($output); - - if (!$configFromFile) { - $output->writeln(''); - $output->writeln('Please answer the following questions:'); - $output->writeln('--------------------------------------'); - $output->writeln(''); - - $connectionVerified = false; - while (!$connectionVerified) { - $db = $this->getDatabaseInformation($input, $output); - $connectionVerified = $this->verifyDatabaseDetails($db, $output); - } - $output->writeln(''); - - $conf = []; - $conf['b8']['database'] = $db; - $conf['php-censor'] = $this->getConfigInformation($input, $output); - - $this->writeConfigFile($conf); - } - - $this->reloadConfig(); - $this->setupDatabase($output); - - $admin = $this->getAdminInformation($input, $output); - $this->createAdminUser($admin, $output); - - $this->createDefaultGroup($output); - } - - /** - * @param OutputInterface $output - * - * @return bool - */ - protected function verifyNotInstalled(OutputInterface $output) - { - if (file_exists($this->configPath)) { - $content = file_get_contents($this->configPath); - - if (!empty($content)) { - $output->writeln('The PHP Censor config file exists and is not empty. PHP Censor is already installed!'); - return false; - } - } - - return true; - } - - /** - * Check PHP version, required modules and for disabled functions. - * - * @param OutputInterface $output - * - * @throws \Exception - */ - protected function checkRequirements(OutputInterface $output) - { - $output->writeln('Checking requirements...'); - $errors = false; - - if (!(version_compare(PHP_VERSION, '5.6.0') >= 0)) { - $output->writeln(''); - $output->writeln('PHP Censor requires at least PHP 5.6.0! Installed PHP ' . PHP_VERSION . ''); - $errors = true; - } - - $requiredExtensions = ['PDO', 'xml', 'json', 'curl', 'openssl']; - - foreach ($requiredExtensions as $extension) { - if (!extension_loaded($extension)) { - $output->writeln(''); - $output->writeln('Extension required: ' . $extension . ''); - $errors = true; - } - } - - $requiredFunctions = ['exec', 'shell_exec', 'proc_open', 'password_hash']; - - foreach ($requiredFunctions as $function) { - if (!function_exists($function)) { - $output->writeln(''); - $output->writeln('PHP Censor needs to be able to call the ' . $function . '() function. Is it disabled in php.ini?'); - $errors = true; - } - } - - if ($errors) { - throw new Exception('PHP Censor cannot be installed, as not all requirements are met. Please review the errors above before continuing.'); - } - - $output->writeln(''); - $output->writeln('OK'); - } - - /** - * Load information for admin user form CLI options or ask info to user. - * - * @param InputInterface $input - * @param OutputInterface $output - * @return array - */ - protected function getAdminInformation(InputInterface $input, OutputInterface $output) - { - $admin = []; - - /** @var $helper QuestionHelper */ - $helper = $this->getHelperSet()->get('question'); - - // Function to validate email address. - $mailValidator = function ($answer) { - if (!filter_var($answer, FILTER_VALIDATE_EMAIL)) { - throw new \InvalidArgumentException('Must be a valid email address.'); - } - - return $answer; - }; - - if ($adminEmail = $input->getOption('admin-email')) { - $adminEmail = $mailValidator($adminEmail); - } else { - $questionEmail = new Question('Admin email: '); - $adminEmail = $helper->ask($input, $output, $questionEmail); - } - - if (!$adminName = $input->getOption('admin-name')) { - $questionName = new Question('Admin name: '); - $adminName = $helper->ask($input, $output, $questionName); - } - - if (!$adminPassword = $input->getOption('admin-password')) { - $questionPassword = new Question('Admin password: '); - $questionPassword->setHidden(true); - $questionPassword->setHiddenFallback(false); - $adminPassword = $helper->ask($input, $output, $questionPassword); - } - - $admin['email'] = $adminEmail; - $admin['name'] = $adminName; - $admin['password'] = $adminPassword; - - return $admin; - } - - /** - * Load configuration form CLI options or ask info to user. - * - * @param InputInterface $input - * @param OutputInterface $output - * - * @return array - */ - protected function getConfigInformation(InputInterface $input, OutputInterface $output) - { - /** @var $helper QuestionHelper */ - $helper = $this->getHelperSet()->get('question'); - - $urlValidator = function ($answer) { - if (!filter_var($answer, FILTER_VALIDATE_URL)) { - throw new Exception('Must be a valid URL.'); - } - - return rtrim($answer, '/'); - }; - - if ($url = $input->getOption('url')) { - $url = $urlValidator($url); - } else { - $question = new Question('Your PHP Censor URL ("http://php-censor.local" for example): '); - $question->setValidator($urlValidator); - $url = $helper->ask($input, $output, $question); - } - - $queueConfig = $this->getQueueInformation($input, $output); - - return [ - 'language' => 'en', - 'per_page' => 10, - 'url' => $url, - 'queue' => $queueConfig, - 'log' => [ - 'rotate' => false, - 'max_files' => 0, - ], - 'email_settings' => [ - 'from_address' => 'PHP Censor ', - 'smtp_address' => null, - 'smtp_port' => null, - 'smtp_username' => null, - 'smtp_password' => null, - 'smtp_encryption' => false, - ], - 'bitbucket' => [ - 'username' => null, - 'app_password' => null, - 'comments' => [ - 'commit' => false, - 'pull_request' => false, - ], - ], - 'github' => [ - 'token' => null, - 'comments' => [ - 'commit' => false, - 'pull_request' => false, - ], - ], - 'build' => [ - 'remove_builds' => true, - 'writer_buffer_size' => 500, - ], - 'security' => [ - 'disable_auth' => false, - 'default_user_id' => 1, - 'auth_providers' => [ - 'internal' => [ - 'type' => 'internal', - ], - ], - ], - 'dashboard_widgets' => [ - 'all_projects' => [ - 'side' => 'left', - ], - 'last_builds' => [ - 'side' => 'right', - ], - ], - ]; - } - - /** - * If the user wants to use a queue, get the necessary details. - * - * @param InputInterface $input - * @param OutputInterface $output - * - * @return array - */ - protected function getQueueInformation(InputInterface $input, OutputInterface $output) - { - $skipQueueConfig = [ - 'use_queue' => false, - 'host' => null, - 'name' => null, - 'lifetime' => 600, - ]; - - if (!$input->getOption('queue-use')) { - return $skipQueueConfig; - } - - $queueConfig = [ - 'use_queue' => true, - 'host' => null, - 'name' => null, - 'lifetime' => 600, - ]; - - $queueConfig['host'] = $input->getOption('queue-host'); - $queueConfig['name'] = $input->getOption('queue-name'); - - if (!$queueConfig['host'] && !$queueConfig['name']) { - /** @var $helper QuestionHelper */ - $helper = $this->getHelper('question'); - $question = new ConfirmationQuestion('Use beanstalkd to manage build queue? ', false); - - if (!$helper->ask($input, $output, $question)) { - $output->writeln('Skipping beanstalkd configuration.'); - - return $skipQueueConfig; - } - - $questionQueue = new Question('Enter your beanstalkd hostname [localhost]: ', 'localhost'); - $queueConfig['host'] = $helper->ask($input, $output, $questionQueue); - - $questionName = new Question('Enter the queue (tube) name to use [php-censor-queue]: ', 'php-censor-queue'); - $queueConfig['name'] = $helper->ask($input, $output, $questionName); - } - - return $queueConfig; - } - - /** - * Load configuration for database form CLI options or ask info to user. - * - * @param InputInterface $input - * @param OutputInterface $output - * - * @return array - */ - protected function getDatabaseInformation(InputInterface $input, OutputInterface $output) - { - $db = []; - - /** @var $helper QuestionHelper */ - $helper = $this->getHelperSet()->get('question'); - - if (!$dbType = $input->getOption('db-type')) { - $questionType = new Question('Please enter your database type (mysql or pgsql): '); - $dbType = $helper->ask($input, $output, $questionType); - } - - if (!$dbHost = $input->getOption('db-host')) { - $questionHost = new Question('Please enter your database host (default: localhost): ', 'localhost'); - $dbHost = $helper->ask($input, $output, $questionHost); - } - - if (!$dbPort = $input->getOption('db-port')) { - $questionPort = new Question('Please enter your database port (default: empty): '); - $dbPort = $helper->ask($input, $output, $questionPort); - } - - if (!$dbName = $input->getOption('db-name')) { - $questionDb = new Question('Please enter your database name (default: php-censor-db): ', 'php-censor-db'); - $dbName = $helper->ask($input, $output, $questionDb); - } - - if (!$dbUser = $input->getOption('db-user')) { - $questionUser = new Question('Please enter your DB user (default: php-censor-user): ', 'php-censor-user'); - $dbUser = $helper->ask($input, $output, $questionUser); - } - - if (!$dbPass = $input->getOption('db-password')) { - $questionPass = new Question('Please enter your database password: '); - $questionPass->setHidden(true); - $questionPass->setHiddenFallback(false); - $dbPass = $helper->ask($input, $output, $questionPass); - } - - $dbServers = [ - [ - 'host' => $dbHost, - ] - ]; - - $dbPort = (integer)$dbPort; - if ($dbPort) { - $dbServers[0]['port'] = $dbPort; - } - - $db['servers']['read'] = $dbServers; - $db['servers']['write'] = $dbServers; - - $db['type'] = $dbType; - $db['name'] = $dbName; - $db['username'] = $dbUser; - $db['password'] = $dbPass; - - return $db; - } - - /** - * Try and connect to DB using the details provided - * - * @param array $db - * @param OutputInterface $output - * - * @return bool - */ - protected function verifyDatabaseDetails(array $db, OutputInterface $output) - { - $dns = $db['type'] . ':host=' . $db['servers']['write'][0]['host']; - if (isset($db['servers']['write'][0]['port'])) { - $dns .= ';port=' . (integer)$db['servers']['write'][0]['port']; - } - $dns .= ';dbname=' . $db['name']; - - $pdoOptions = [ - \PDO::ATTR_PERSISTENT => false, - \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION, - \PDO::ATTR_TIMEOUT => 2, - ]; - if ('mysql' === $db['type']) { - $pdoOptions[\PDO::MYSQL_ATTR_INIT_COMMAND] = "SET NAMES 'UTF8'"; - } - - try { - $pdo = new PDO( - $dns, - $db['username'], - $db['password'], - $pdoOptions - ); - - unset($pdo); - - return true; - - } catch (Exception $ex) { - $output->writeln('PHP Censor could not connect to database with the details provided. Please try again.'); - $output->writeln('' . $ex->getMessage() . ''); - } - - return false; - } - - /** - * Write the config.yml file. - * @param array $config - */ - protected function writeConfigFile(array $config) - { - $dumper = new Dumper(); - $yaml = $dumper->dump($config, 4); - - file_put_contents($this->configPath, $yaml); - } - - protected function setupDatabase(OutputInterface $output) - { - $output->write('Setting up your database...'); - - $outputMigration = shell_exec(ROOT_DIR . 'bin/console php-censor-migrations:migrate'); - - $output->writeln(''); - $output->writeln($outputMigration); - $output->writeln('OK'); - } - - /** - * Create admin user using information loaded before. - * - * @param array $admin - * @param OutputInterface $output - */ - protected function createAdminUser($admin, $output) - { - try { - /** @var UserStore $userStore */ - $userStore = Factory::getStore('User'); - $adminUser = $userStore->getByEmail($admin['email']); - if ($adminUser) { - throw new \RuntimeException('Admin account already exists!'); - } - - $userService = new UserService($userStore); - $userService->createUser($admin['name'], $admin['email'], 'internal', json_encode(['type' => 'internal']), $admin['password'], true); - - $output->writeln('User account created!'); - } catch (\Exception $ex) { - $output->writeln('PHP Censor failed to create your admin account!'); - $output->writeln('' . $ex->getMessage() . ''); - } - } - - /** - * @param OutputInterface $output - */ - protected function createDefaultGroup($output) - { - try { - /** @var ProjectGroupStore $projectGroupStore */ - $projectGroupStore = Factory::getStore('ProjectGroup'); - $projectGroup = $projectGroupStore->getByTitle('Projects'); - if ($projectGroup) { - throw new \RuntimeException('Default project group already exists!'); - } - - $group = new ProjectGroup(); - $group->setTitle('Projects'); - $group->setCreateDate(new \DateTime()); - $group->setUserId(0); - - Factory::getStore('ProjectGroup')->save($group); - - $output->writeln('Default project group created!'); - } catch (\Exception $ex) { - $output->writeln('PHP Censor failed to create default project group!'); - $output->writeln('' . $ex->getMessage() . ''); - } - } - - protected function reloadConfig() - { - $config = Config::getInstance(); - - if (file_exists($this->configPath)) { - $config->loadYaml($this->configPath); - } - } -} diff --git a/src/PHPCensor/Command/RebuildCommand.php b/src/PHPCensor/Command/RebuildCommand.php deleted file mode 100644 index e62dcab..0000000 --- a/src/PHPCensor/Command/RebuildCommand.php +++ /dev/null @@ -1,85 +0,0 @@ - - */ -class RebuildCommand extends Command -{ - /** - * @var Logger - */ - protected $logger; - - /** - * @var OutputInterface - */ - protected $output; - - /** - * @var boolean - */ - protected $run; - - /** - * @var int - */ - protected $sleep; - - /** - * @param \Monolog\Logger $logger - * @param string $name - */ - public function __construct(Logger $logger, $name = null) - { - parent::__construct($name); - $this->logger = $logger; - } - - protected function configure() - { - $this - ->setName('php-censor:rebuild') - ->setDescription('Re-runs the last run build.'); - } - - /** - * Loops through running. - */ - protected function execute(InputInterface $input, OutputInterface $output) - { - $runner = new RunCommand($this->logger); - $runner->setMaxBuilds(1); - - /** @var \PHPCensor\Store\BuildStore $store */ - $store = Factory::getStore('Build'); - $service = new BuildService($store); - - $builds = $store->getLatestBuilds(null, 1); - $lastBuild = array_shift($builds); - $service->createDuplicateBuild($lastBuild); - - $runner->run(new ArgvInput([]), $output); - } - - /** - * Called when log entries are made in Builder / the plugins. - * - * @see \PHPCensor\Builder::log() - */ - public function logCallback($log) - { - $this->output->writeln($log); - } -} diff --git a/src/PHPCensor/Command/RebuildQueueCommand.php b/src/PHPCensor/Command/RebuildQueueCommand.php deleted file mode 100644 index 4783702..0000000 --- a/src/PHPCensor/Command/RebuildQueueCommand.php +++ /dev/null @@ -1,73 +0,0 @@ - - */ -class RebuildQueueCommand extends Command -{ - /** - * @var OutputInterface - */ - protected $output; - - /** - * @var Logger - */ - protected $logger; - - /** - * @param \Monolog\Logger $logger - * @param string $name - */ - public function __construct(Logger $logger, $name = null) - { - parent::__construct($name); - $this->logger = $logger; - } - - protected function configure() - { - $this - ->setName('php-censor:rebuild-queue') - ->setDescription('Rebuilds the PHP Censor worker queue.'); - } - - protected function execute(InputInterface $input, OutputInterface $output) - { - $this->output = $output; - - // For verbose mode we want to output all informational and above - // messages to the symphony output interface. - if ($input->hasOption('verbose') && $input->getOption('verbose')) { - $this->logger->pushHandler( - new OutputLogHandler($this->output, Logger::INFO) - ); - } - - $store = Factory::getStore('Build'); - $result = $store->getByStatus(0); - - $this->logger->addInfo(sprintf('Found %d builds', count($result['items']))); - - $buildService = new BuildService($store); - - while (count($result['items'])) { - $build = array_shift($result['items']); - $build = BuildFactory::getBuild($build); - - $this->logger->addInfo('Added build #' . $build->getId() . ' to queue.'); - $buildService->addBuildToQueue($build); - } - } -} diff --git a/src/PHPCensor/Command/RunCommand.php b/src/PHPCensor/Command/RunCommand.php deleted file mode 100644 index 2ff7f56..0000000 --- a/src/PHPCensor/Command/RunCommand.php +++ /dev/null @@ -1,173 +0,0 @@ - - */ -class RunCommand extends Command -{ - /** - * @var OutputInterface - */ - protected $output; - - /** - * @var Logger - */ - protected $logger; - - /** - * @var int - */ - protected $maxBuilds = 10; - - /** - * @param \Monolog\Logger $logger - * @param string $name - */ - public function __construct(Logger $logger, $name = null) - { - parent::__construct($name); - $this->logger = $logger; - } - - protected function configure() - { - $this - ->setName('php-censor:run-builds') - ->setDescription('Run all pending PHP Censor builds') - ->addOption('debug', null, null, 'Run PHP Censor in debug mode'); - } - - /** - * Pulls all pending builds from the database or queue and runs them. - */ - protected function execute(InputInterface $input, OutputInterface $output) - { - $this->output = $output; - - // For verbose mode we want to output all informational and above - // messages to the symphony output interface. - if ($input->hasOption('verbose') && $input->getOption('verbose')) { - $this->logger->pushHandler( - new OutputLogHandler($this->output, Logger::INFO) - ); - } - - // Allow PHP Censor to run in "debug mode" - if ($input->hasOption('debug') && $input->getOption('debug')) { - $output->writeln('Debug mode enabled.'); - define('DEBUG_MODE', true); - } - - $running = $this->validateRunningBuilds(); - - $this->logger->pushProcessor(new LoggedBuildContextTidier()); - $this->logger->addInfo('Finding builds to process'); - - /** @var BuildStore $buildStore */ - $buildStore = Factory::getStore('Build'); - $result = $buildStore->getByStatus(Build::STATUS_PENDING, $this->maxBuilds); - - $this->logger->addInfo(sprintf('Found %d builds', count($result['items']))); - - $builds = 0; - - while (count($result['items'])) { - $build = array_shift($result['items']); - $build = BuildFactory::getBuild($build); - - // Skip build (for now) if there's already a build running in that project: - if (!empty($running[$build->getProjectId()])) { - $this->logger->addInfo(sprintf('Skipping Build %d - Project build already in progress.', $build->getId())); - continue; - } - - $builds++; - - // Logging relevant to this build should be stored - // against the build itself. - $buildDbLog = new BuildDBLogHandler($build, Logger::INFO); - $this->logger->pushHandler($buildDbLog); - - try { - $builder = new Builder($build, $this->logger); - $builder->execute(); - } catch (\Exception $ex) { - $this->logger->addError($ex->getMessage()); - - $build->setStatus(Build::STATUS_FAILED); - $build->setFinishDate(new \DateTime()); - $build->setLog($build->getLog() . PHP_EOL . PHP_EOL . $ex->getMessage()); - $buildStore->save($build); - $build->sendStatusPostback(); - } - - // After execution we no longer want to record the information - // back to this specific build so the handler should be removed. - $this->logger->popHandler(); - // destructor implicitly call flush - unset($buildDbLog); - - // Re-run build validator: - $running = $this->validateRunningBuilds(); - } - - $this->logger->addInfo('Finished processing builds.'); - - return $builds; - } - - public function setMaxBuilds($numBuilds) - { - $this->maxBuilds = (int)$numBuilds; - } - - protected function validateRunningBuilds() - { - /** @var \PHPCensor\Store\BuildStore $store */ - $store = Factory::getStore('Build'); - $running = $store->getByStatus(Build::STATUS_RUNNING); - $rtn = []; - - $timeout = Config::getInstance()->get('php-censor.build.failed_after', 1800); - - foreach ($running['items'] as $build) { - /** @var \PHPCensor\Model\Build $build */ - $build = BuildFactory::getBuild($build); - - $now = time(); - $start = $build->getStartDate()->getTimestamp(); - - if (($now - $start) > $timeout) { - $this->logger->addInfo(sprintf('Build %d marked as failed due to timeout.', $build->getId())); - $build->setStatus(Build::STATUS_FAILED); - $build->setFinishDate(new \DateTime()); - $store->save($build); - $build->removeBuildDirectory(); - continue; - } - - $rtn[$build->getProjectId()] = true; - } - - return $rtn; - } -} diff --git a/src/PHPCensor/Command/ScheduleBuildCommand.php b/src/PHPCensor/Command/ScheduleBuildCommand.php deleted file mode 100644 index 57c02f9..0000000 --- a/src/PHPCensor/Command/ScheduleBuildCommand.php +++ /dev/null @@ -1,101 +0,0 @@ - - */ -class ScheduleBuildCommand extends Command -{ - /** - * @var ProjectStore - */ - protected $projectStore; - - /** - * @var BuildStore - */ - protected $buildStore; - - /** - * @var BuildService - */ - protected $buildService; - - /** - * @param ProjectStore $projectStore - * @param BuildStore $buildStore - * @param BuildService $buildService - */ - public function __construct(ProjectStore $projectStore, BuildStore $buildStore, BuildService $buildService) - { - parent::__construct(); - - $this->projectStore = $projectStore; - $this->buildService = $buildService; - $this->buildStore = $buildStore; - } - - /** - * {@inheritDoc} - */ - protected function configure() - { - $this - ->setName('php-censor:schedule-build') - ->setDescription('Schedules a build for active projects which have not been ran by X days') - ->addArgument('days', InputArgument::REQUIRED, 'Since specified days'); - } - - /** - * {@inheritDoc} - */ - public function execute(InputInterface $input, OutputInterface $output) - { - $sinceDays = $input->getArgument('days'); - $date = new \DateTime('now'); - $difference = new \DateInterval("P{$sinceDays}D"); - $date->sub($difference); - - $projects = $this->projectStore->getAll(); - $projects = $projects['items']; - /** @var Project $project */ - foreach ($projects as $project) { - $latestBuild = $this->buildStore->getLatestBuilds($project->getId(), 1); - - if ($latestBuild) { - /** @var Build $build */ - $build = $latestBuild[0]; - $status = (integer)$build->getStatus(); - if ($status === Build::STATUS_RUNNING || $status === Build::STATUS_PENDING) { - // If it's running or just created, we don't want to reschedule already. - continue; - } - if ($date < $build->getFinishDate()) { - // If finished date is newer then the specified since days, we don't want to reschedule - continue; - } - } - - try { - $this->buildService->createBuild($project, null, '', null, null, null, null, Build::SOURCE_PERIODICAL); - $output->writeln("Build Created for {$project->getTitle()}"); - } catch (\Exception $e) { - $output->writeln('Failed'); - $output->writeln(sprintf('%s', $e->getMessage())); - } - } - } -} diff --git a/src/PHPCensor/Command/WorkerCommand.php b/src/PHPCensor/Command/WorkerCommand.php deleted file mode 100644 index 3004c51..0000000 --- a/src/PHPCensor/Command/WorkerCommand.php +++ /dev/null @@ -1,77 +0,0 @@ - - */ -class WorkerCommand extends Command -{ - /** - * @var OutputInterface - */ - protected $output; - - /** - * @var Logger - */ - protected $logger; - - /** - * @param \Monolog\Logger $logger - * @param string $name - */ - public function __construct(Logger $logger, $name = null) - { - parent::__construct($name); - $this->logger = $logger; - } - - protected function configure() - { - $this - ->setName('php-censor:worker') - ->setDescription('Runs the PHP Censor build worker.') - ->addOption('debug', null, null, 'Run PHP Censor in Debug Mode'); - } - - protected function execute(InputInterface $input, OutputInterface $output) - { - $this->output = $output; - - // For verbose mode we want to output all informational and above - // messages to the symphony output interface. - if ($input->hasOption('verbose') && $input->getOption('verbose')) { - $this->logger->pushHandler( - new OutputLogHandler($this->output, Logger::INFO) - ); - } - - // Allow to run in "debug mode" - if ($input->hasOption('debug') && $input->getOption('debug')) { - $output->writeln('Debug mode enabled.'); - define('DEBUG_MODE', true); - } - - $config = Config::getInstance()->get('php-censor.queue', []); - - if (empty($config['host']) || empty($config['name'])) { - $error = 'The worker is not configured. You must set a host and queue in your config.yml file.'; - throw new \Exception($error); - } - - $worker = new BuildWorker($config['host'], $config['name']); - $worker->setLogger($this->logger); - $worker->startWorker(); - } -} diff --git a/src/PHPCensor/Console/Application.php b/src/PHPCensor/Console/Application.php deleted file mode 100644 index 66310a0..0000000 --- a/src/PHPCensor/Console/Application.php +++ /dev/null @@ -1,140 +0,0 @@ -get('php-censor.log.rotate', false); - $maxFiles = (int)$applicationConfig->get('php-censor.log.max_files', 0); - - $loggerHandlers = []; - if ($rotate) { - $loggerHandlers[] = new RotatingFileHandler(RUNTIME_DIR . 'console.log', $maxFiles, Logger::DEBUG); - } else { - $loggerHandlers[] = new StreamHandler(RUNTIME_DIR . 'console.log', Logger::DEBUG); - } - - $logger = new Logger('php-censor', $loggerHandlers); - Handler::register($logger); - - return $logger; - } - - /** - * Constructor. - * - * @param string $name The name of the application - * @param string $version The version of the application - */ - public function __construct($name = 'PHP Censor - Continuous Integration for PHP', $version = '') - { - parent::__construct($name, $version); - - $applicationConfig = Config::getInstance(); - $databaseSettings = $applicationConfig->get('b8.database', []); - - $phinxSettings = []; - if ($databaseSettings) { - $phinxSettings = [ - 'paths' => [ - 'migrations' => ROOT_DIR . 'src/PHPCensor/Migrations', - ], - 'environments' => [ - 'default_migration_table' => 'migration', - 'default_database' => 'php-censor', - 'php-censor' => [ - 'adapter' => $databaseSettings['type'], - 'host' => $databaseSettings['servers']['write'][0]['host'], - 'name' => $databaseSettings['name'], - 'user' => $databaseSettings['username'], - 'pass' => $databaseSettings['password'], - ], - ], - ]; - } - - if (!empty($databaseSettings['port'])) { - $phinxSettings['environments']['php-censor']['port'] = (integer)$databaseSettings['port']; - } - - $phinxConfig = new PhinxConfig($phinxSettings); - - $this->add( - (new Create()) - ->setConfig($phinxConfig) - ->setName('php-censor-migrations:create') - ); - $this->add( - (new Migrate()) - ->setConfig($phinxConfig) - ->setName('php-censor-migrations:migrate') - ); - $this->add( - (new Rollback()) - ->setConfig($phinxConfig) - ->setName('php-censor-migrations:rollback') - ); - $this->add( - (new Status()) - ->setConfig($phinxConfig) - ->setName('php-censor-migrations:status') - ); - - /** @var UserStore $userStore */ - $userStore = Factory::getStore('User'); - - /** @var ProjectStore $projectStore */ - $projectStore = Factory::getStore('Project'); - - /** @var BuildStore $buildStore */ - $buildStore = Factory::getStore('Build'); - - $logger = $this->initLogger($applicationConfig); - - $this->add(new RunCommand($logger)); - $this->add(new RebuildCommand($logger)); - $this->add(new InstallCommand()); - $this->add(new CreateAdminCommand($userStore)); - $this->add(new CreateBuildCommand($projectStore, new BuildService($buildStore))); - $this->add(new WorkerCommand($logger)); - $this->add(new RebuildQueueCommand($logger)); - $this->add(new ScheduleBuildCommand($projectStore, $buildStore, new BuildService($buildStore))); - } -} diff --git a/src/PHPCensor/Controller.php b/src/PHPCensor/Controller.php deleted file mode 100644 index e792af7..0000000 --- a/src/PHPCensor/Controller.php +++ /dev/null @@ -1,124 +0,0 @@ -className = substr(array_pop($class), 0, -10); - $this->setControllerView(); - } - - /** - * Set the view that this controller should use. - */ - protected function setControllerView() - { - if (View::exists($this->className)) { - $this->controllerView = new View($this->className); - } else { - $this->controllerView = new View\Template('{@content}'); - } - } - - /** - * Set the view that this controller action should use. - * - * @param string $action - */ - protected function setView($action) - { - if (View::exists($this->className . '/' . $action)) { - $this->view = new View($this->className . '/' . $action); - } - } - - /** - * Handle the incoming request. - * - * @param string $action - * @param array $actionParams - * - * @return Response - */ - public function handleAction($action, $actionParams) - { - $this->setView($action); - $response = parent::handleAction($action, $actionParams); - - if ($response instanceof Response) { - return $response; - } - - if (is_string($response)) { - $this->controllerView->content = $response; - } elseif (isset($this->view)) { - $this->controllerView->content = $this->view->render(); - } - - $this->response->setContent($this->controllerView->render()); - - return $this->response; - } - - /** - * Require that the currently logged in user is an administrator. - * - * @throws ForbiddenException - */ - protected function requireAdmin() - { - if (!$this->currentUserIsAdmin()) { - throw new ForbiddenException('You do not have permission to do that.'); - } - } - - /** - * Check if the currently logged in user is an administrator. - * - * @return boolean - */ - protected function currentUserIsAdmin() - { - return $_SESSION['php-censor-user']->getIsAdmin(); - } -} diff --git a/src/PHPCensor/Controller/BuildController.php b/src/PHPCensor/Controller/BuildController.php deleted file mode 100644 index fc2350c..0000000 --- a/src/PHPCensor/Controller/BuildController.php +++ /dev/null @@ -1,371 +0,0 @@ - - */ -class BuildController extends Controller -{ - /** - * @var \PHPCensor\Store\BuildStore - */ - protected $buildStore; - - /** - * @var \PHPCensor\Service\BuildService - */ - protected $buildService; - - /** - * Initialise the controller, set up stores and services. - */ - public function init() - { - $this->buildStore = b8\Store\Factory::getStore('Build'); - $this->buildService = new BuildService($this->buildStore); - } - - /** - * View a specific build. - * - * @param integer $buildId - * - * @throws NotFoundException - */ - public function view($buildId) - { - $page = (integer)$this->getParam('page', 1); - $plugin = $this->getParam('plugin', ''); - - $severity = $this->getParam('severity', null); - if (null !== $severity && '' !== $severity) { - $severity = (integer)$severity; - } else { - $severity = null; - } - - try { - $build = BuildFactory::getBuildById($buildId); - } catch (\Exception $ex) { - $build = null; - } - - if (empty($build)) { - throw new NotFoundException(Lang::get('build_x_not_found', $buildId)); - } - - /** @var User $user */ - $user = $_SESSION['php-censor-user']; - $perPage = $user->getFinalPerPage(); - $data = $this->getBuildData($build, $plugin, $severity, (($page - 1) * $perPage), $perPage); - $pages = ($data['errors'] === 0) - ? 1 - : (integer)ceil($data['errors'] / $perPage); - - if ($page > $pages) { - $page = $pages; - } - - /** @var \PHPCensor\Store\BuildErrorStore $errorStore */ - $errorStore = b8\Store\Factory::getStore('BuildError'); - - $this->view->uiPlugins = $this->getUiPlugins(); - $this->view->build = $build; - $this->view->data = $data; - - $this->view->plugin = urldecode($plugin); - $this->view->plugins = $errorStore->getKnownPlugins($buildId); - $this->view->severity = urldecode(null !== $severity ? $severity : ''); - $this->view->severities = $errorStore->getKnownSeverities($buildId, $plugin); - - $this->view->page = $page; - $this->view->perPage = $perPage; - $this->view->paginator = $this->getPaginatorHtml($buildId, $plugin, $severity, $data['errors'], $perPage, $page); - - $this->layout->title = Lang::get('build_n', $buildId); - $this->layout->subtitle = $build->getProjectTitle(); - - switch ($build->getStatus()) { - case 0: - $this->layout->skin = 'blue'; - break; - - case 1: - $this->layout->skin = 'yellow'; - break; - - case 2: - $this->layout->skin = 'green'; - break; - - case 3: - $this->layout->skin = 'red'; - break; - } - - $rebuild = Lang::get('rebuild_now'); - $rebuildLink = APP_URL . 'build/rebuild/' . $build->getId(); - - $delete = Lang::get('delete_build'); - $deleteLink = APP_URL . 'build/delete/' . $build->getId(); - - $project = b8\Store\Factory::getStore('Project')->getByPrimaryKey($build->getProjectId()); - - $actions = ''; - if (!$project->getArchived()) { - $actions .= "{$rebuild} "; - } - - if ($this->currentUserIsAdmin()) { - $actions .= " {$delete}"; - } - - $this->layout->actions = $actions; - } - - /** - * Returns an array of the JS plugins to include. - * @return array - */ - protected function getUiPlugins() - { - $rtn = []; - $path = PUBLIC_DIR . 'assets' . DIRECTORY_SEPARATOR . 'js' . DIRECTORY_SEPARATOR . 'build-plugins' . DIRECTORY_SEPARATOR; - $dir = opendir($path); - - while ($item = readdir($dir)) { - if (substr($item, 0, 1) == '.' || substr($item, -3) != '.js') { - continue; - } - - $rtn[] = $item; - } - - return $rtn; - } - - /** - * Get build data from database and json encode it. - * - * @param Build $build - * @param string $plugin - * @param integer $severity - * @param integer $start - * @param integer $perPage - * - * @return array - */ - protected function getBuildData(Build $build, $plugin, $severity, $start = 0, $perPage = 10) - { - $data = []; - $data['status'] = (int)$build->getStatus(); - $data['log'] = $this->cleanLog($build->getLog()); - $data['create_date'] = !is_null($build->getCreateDate()) ? $build->getCreateDate()->format('Y-m-d H:i:s') : null; - $data['start_date'] = !is_null($build->getStartDate()) ? $build->getStartDate()->format('Y-m-d H:i:s') : null; - $data['finish_date'] = !is_null($build->getFinishDate()) ? $build->getFinishDate()->format('Y-m-d H:i:s') : null; - $data['duration'] = $build->getDuration(); - - /** @var \PHPCensor\Store\BuildErrorStore $errorStore */ - $errorStore = b8\Store\Factory::getStore('BuildError'); - $errors = $errorStore->getByBuildId($build->getId(), $perPage, $start, $plugin, $severity); - - $errorView = new b8\View('Build/errors'); - $errorView->build = $build; - $errorView->errors = $errors['items']; - - $data['errors'] = $errorStore->getErrorTotalForBuild($build->getId(), $plugin, $severity); - $data['errors_total'] = $errorStore->getErrorTotalForBuild($build->getId()); - $data['error_html'] = $errorView->render(); - - return $data; - } - - /** - * @param integer $buildId - * @param string $plugin - * @param integer $severity - * @param integer $total - * @param integer $perPage - * @param integer $page - * - * @return string - */ - protected function getPaginatorHtml($buildId, $plugin, $severity, $total, $perPage, $page) - { - $view = new b8\View('pagination'); - - $urlPattern = APP_URL . 'build/view/' . $buildId; - $params = []; - if (!empty($plugin)) { - $params['plugin'] = $plugin; - } - - if (null !== $severity) { - $params['severity'] = $severity; - } - - $urlPattern = $urlPattern . '?' . str_replace('%28%3Anum%29', '(:num)', http_build_query(array_merge($params, ['page' => '(:num)']))) . '#errors'; - $paginator = new Paginator($total, $perPage, $page, $urlPattern); - - $view->paginator = $paginator; - - return $view->render(); - } - - /** - * Create a build using an existing build as a template: - */ - public function rebuild($buildId) - { - $copy = BuildFactory::getBuildById($buildId); - $project = b8\Store\Factory::getStore('Project')->getByPrimaryKey($copy->getProjectId()); - - if (empty($copy) || $project->getArchived()) { - throw new NotFoundException(Lang::get('build_x_not_found', $buildId)); - } - - $build = $this->buildService->createDuplicateBuild($copy); - - if ($this->buildService->queueError) { - $_SESSION['global_error'] = Lang::get('add_to_queue_failed'); - } - - $response = new b8\Http\Response\RedirectResponse(); - $response->setHeader('Location', APP_URL.'build/view/' . $build->getId()); - - return $response; - } - - /** - * Delete a build. - */ - public function delete($buildId) - { - $this->requireAdmin(); - - $build = BuildFactory::getBuildById($buildId); - - if (empty($build)) { - throw new NotFoundException(Lang::get('build_x_not_found', $buildId)); - } - - $this->buildService->deleteBuild($build); - - $response = new b8\Http\Response\RedirectResponse(); - $response->setHeader('Location', APP_URL.'project/view/' . $build->getProjectId()); - - return $response; - } - - /** - * Parse log for unix colours and replace with HTML. - */ - protected function cleanLog($log) - { - return AnsiConverter::convert($log); - } - - /** - * Formats a list of builds into rows suitable for the dropdowns in the header bar. - * - * @param $builds - * - * @return array - */ - protected function formatBuilds($builds) - { - Project::$sleepable = ['id', 'title', 'reference', 'type']; - - $rtn = ['count' => $builds['count'], 'items' => []]; - - foreach ($builds['items'] as $build) { - $item = $build->toArray(1); - - $header = new b8\View('Build/header-row'); - $header->build = $build; - - $item['header_row'] = $header->render(); - $rtn['items'][$item['id']] = $item; - } - - ksort($rtn['items']); - return $rtn; - } - - public function ajaxData($buildId) - { - $page = (integer)$this->getParam('page', 1); - $perPage = (integer)$this->getParam('per_page', 10); - $plugin = $this->getParam('plugin', ''); - - $severity = $this->getParam('severity', null); - if (null !== $severity && '' !== $severity) { - $severity = (integer)$severity; - } else { - $severity = null; - } - - $response = new JsonResponse(); - $build = BuildFactory::getBuildById($buildId); - - if (!$build) { - $response->setResponseCode(404); - $response->setContent([]); - - return $response; - } - - $data = $this->getBuildData($build, $plugin, $severity, (($page - 1) * $perPage), $perPage); - $data['paginator'] = $this->getPaginatorHtml($buildId, $plugin, $severity, $data['errors'], $perPage, $page); - - $response->setContent($data); - - return $response; - } - - public function ajaxMeta($buildId) - { - $build = BuildFactory::getBuildById($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, $build->getBranch(), $numBuilds); - } - - $response = new JsonResponse(); - $response->setContent($data); - - return $response; - } - - public function ajaxQueue() - { - $rtn = [ - 'pending' => $this->formatBuilds($this->buildStore->getByStatus(Build::STATUS_PENDING)), - 'running' => $this->formatBuilds($this->buildStore->getByStatus(Build::STATUS_RUNNING)), - ]; - - $response = new JsonResponse(); - $response->setContent($rtn); - - return $response; - } -} diff --git a/src/PHPCensor/Controller/BuildStatusController.php b/src/PHPCensor/Controller/BuildStatusController.php deleted file mode 100644 index b7f697f..0000000 --- a/src/PHPCensor/Controller/BuildStatusController.php +++ /dev/null @@ -1,236 +0,0 @@ - - */ -class BuildStatusController extends Controller -{ - /* @var \PHPCensor\Store\ProjectStore */ - protected $projectStore; - - /* @var \PHPCensor\Store\BuildStore */ - protected $buildStore; - - /** - * Initialise the controller, set up stores and services. - */ - public function init() - { - $this->response->disableLayout(); - - $this->buildStore = Store\Factory::getStore('Build'); - $this->projectStore = Store\Factory::getStore('Project'); - } - - /** - * Returns status of the last build - * - * @param $projectId - * - * @return string - */ - protected function getStatus($projectId) - { - $status = null; - $branch = $this->getParam('branch', 'master'); - - try { - $project = $this->projectStore->getById($projectId); - $status = 'passing'; - - if (isset($project) && $project instanceof Project) { - $build = $project->getLatestBuild($branch, [ - Build::STATUS_SUCCESS, - Build::STATUS_FAILED, - ]); - - if (isset($build) && $build instanceof Build && $build->getStatus() !== Build::STATUS_SUCCESS) { - $status = 'failed'; - } - } - } catch (\Exception $e) { - $status = 'error'; - } - - return $status; - } - - /** - * Displays projects information in ccmenu format - * - * @param $projectId - * - * @return bool - * - * @throws \Exception - * @throws b8\Exception\HttpException - */ - public function ccxml($projectId) - { - /* @var Project $project */ - $project = $this->projectStore->getById($projectId); - $xml = new \SimpleXMLElement(''); - - if (!$project instanceof Project || !$project->getAllowPublicStatus()) { - return $this->renderXml($xml); - } - - try { - $branchList = $this->buildStore->getBuildBranches($projectId); - - if (!$branchList) { - $branchList = [$project->getBranch()]; - } - - foreach ($branchList as $branch) { - $buildStatusService = new BuildStatusService($branch, $project, $project->getLatestBuild($branch)); - if ($attributes = $buildStatusService->toArray()) { - $projectXml = $xml->addChild('Project'); - foreach ($attributes as $attributeKey => $attributeValue) { - $projectXml->addAttribute($attributeKey, $attributeValue); - } - } - } - } catch (\Exception $e) { - $xml = new \SimpleXMLElement(''); - } - - return $this->renderXml($xml); - } - - /** - * @param \SimpleXMLElement $xml - * - * @return boolean - */ - protected function renderXml(\SimpleXMLElement $xml = null) - { - $this->response->setHeader('Content-Type', 'text/xml'); - $this->response->setContent($xml->asXML()); - $this->response->flush(); - echo $xml->asXML(); - - return true; - } - - /** - * Returns the appropriate build status image in SVG format for a given project. - * - * @param $projectId - * - * @return b8\Http\Response|b8\Http\Response\RedirectResponse - */ - public function image($projectId) - { - // plastic|flat|flat-squared|social - $style = $this->getParam('style', 'flat'); - $label = $this->getParam('label', 'build'); - - $optionalParams = [ - 'logo' => $this->getParam('logo'), - 'logoWidth' => $this->getParam('logoWidth'), - 'link' => $this->getParam('link'), - 'maxAge' => $this->getParam('maxAge'), - ]; - - $status = $this->getStatus($projectId); - - if (is_null($status)) { - $response = new b8\Http\Response\RedirectResponse(); - $response->setHeader('Location', '/'); - - return $response; - } - - $color = ($status == 'passing') ? 'green' : 'red'; - $imageUrl = sprintf( - 'http://img.shields.io/badge/%s-%s-%s.svg?style=%s', - $label, - $status, - $color, - $style - ); - - foreach ($optionalParams as $paramName => $param) { - if ($param) { - $imageUrl .= '&' . $paramName . '=' . $param; - } - } - - $cacheDir = RUNTIME_DIR . 'status_cache/'; - $cacheFile = $cacheDir . md5($imageUrl) . '.svg'; - if (!is_file($cacheFile)) { - $image = file_get_contents($imageUrl); - file_put_contents($cacheFile, $image); - } - - $image = file_get_contents($cacheFile); - - $this->response->disableLayout(); - $this->response->setHeader('Content-Type', 'image/svg+xml'); - $this->response->setContent($image); - return $this->response; - } - - /** - * View the public status page of a given project, if enabled. - * - * @param integer $projectId - * - * @return string - * - * @throws \b8\Exception\HttpException\NotFoundException - */ - public function view($projectId) - { - $project = $this->projectStore->getById($projectId); - - if (empty($project) || !$project->getAllowPublicStatus()) { - throw new NotFoundException('Project with id: ' . $projectId . ' not found'); - } - - $builds = $this->getLatestBuilds($projectId); - - if (count($builds)) { - $this->view->latest = $builds[0]; - } - - $this->view->builds = $builds; - $this->view->project = $project; - - return $this->view->render(); - } - - /** - * Render latest builds for project as HTML table. - * - * @param integer $projectId - * - * @return array - */ - protected function getLatestBuilds($projectId) - { - $criteria = ['project_id' => $projectId]; - $order = ['id' => 'DESC']; - $builds = $this->buildStore->getWhere($criteria, 10, 0, [], $order); - - foreach ($builds['items'] as &$build) { - $build = BuildFactory::getBuild($build); - } - - return $builds['items']; - } -} diff --git a/src/PHPCensor/Controller/GroupController.php b/src/PHPCensor/Controller/GroupController.php deleted file mode 100644 index fc54f81..0000000 --- a/src/PHPCensor/Controller/GroupController.php +++ /dev/null @@ -1,126 +0,0 @@ - - */ -class GroupController extends Controller -{ - /** - * @var \PHPCensor\Store\ProjectGroupStore - */ - protected $groupStore; - - /** - * Set up this controller. - */ - public function init() - { - $this->groupStore = b8\Store\Factory::getStore('ProjectGroup'); - } - - /** - * List project groups. - */ - public function index() - { - $this->requireAdmin(); - - $groups = []; - $groupList = $this->groupStore->getWhere([], 100, 0, [], ['title' => 'ASC']); - - foreach ($groupList['items'] as $group) { - $thisGroup = [ - 'title' => $group->getTitle(), - 'id' => $group->getId(), - ]; - $projects_active = b8\Store\Factory::getStore('Project')->getByGroupId($group->getId(), false); - $projects_archived = b8\Store\Factory::getStore('Project')->getByGroupId($group->getId(), true); - - $thisGroup['projects'] = array_merge($projects_active['items'], $projects_archived['items']); - $groups[] = $thisGroup; - } - - $this->layout->title = Lang::get('group_projects'); - $this->view->groups = $groups; - } - - /** - * Add or edit a project group. - * @param null $groupId - * @return void|b8\Http\Response\RedirectResponse - */ - public function edit($groupId = null) - { - $this->requireAdmin(); - - if (!is_null($groupId)) { - $group = $this->groupStore->getById($groupId); - } else { - $group = new ProjectGroup(); - } - - if ($this->request->getMethod() == 'POST') { - $group->setTitle($this->getParam('title')); - if (is_null($groupId)) { - /** @var User $user */ - $user = $_SESSION['php-censor-user']; - - $group->setCreateDate(new \DateTime()); - $group->setUserId($user->getId()); - } - - $this->groupStore->save($group); - - $response = new b8\Http\Response\RedirectResponse(); - $response->setHeader('Location', APP_URL.'group'); - - return $response; - } - - $form = new Form(); - $form->setMethod('POST'); - $form->setAction(APP_URL . 'group/edit' . (!is_null($groupId) ? '/' . $groupId : '')); - - $title = new Form\Element\Text('title'); - $title->setContainerClass('form-group'); - $title->setClass('form-control'); - $title->setLabel(Lang::get('group_title')); - $title->setValue($group->getTitle()); - - $submit = new Form\Element\Submit(); - $submit->setClass('btn btn-success'); - $submit->setValue(Lang::get('group_save')); - - $form->addField($title); - $form->addField($submit); - - $this->view->form = $form; - } - - /** - * Delete a project group. - * @param $groupId - * @return b8\Http\Response\RedirectResponse - */ - public function delete($groupId) - { - $this->requireAdmin(); - $group = $this->groupStore->getById($groupId); - - $this->groupStore->delete($group); - $response = new b8\Http\Response\RedirectResponse(); - $response->setHeader('Location', APP_URL.'group'); - return $response; - } -} diff --git a/src/PHPCensor/Controller/HomeController.php b/src/PHPCensor/Controller/HomeController.php deleted file mode 100644 index 3cfbc2d..0000000 --- a/src/PHPCensor/Controller/HomeController.php +++ /dev/null @@ -1,42 +0,0 @@ -layout->title = Lang::get('dashboard'); - - $widgets = [ - 'left' => [], - 'right' => [], - ]; - $widgets_config = b8\Config::getInstance()->get('php-censor.dashboard_widgets', [ - 'all_projects' => [ - 'side' => 'left', - ], - 'last_builds' => [ - 'side' => 'right', - ], - ]); - foreach($widgets_config as $name => $params) { - $side = (isset($params['side']) and ($params['side'] == 'right')) ? 'right' : 'left'; - $widgets[$side][$name] = $params; - } - - $this->view->widgets = $widgets; - - return $this->view->render(); - } -} diff --git a/src/PHPCensor/Controller/ProjectController.php b/src/PHPCensor/Controller/ProjectController.php deleted file mode 100644 index c2b0673..0000000 --- a/src/PHPCensor/Controller/ProjectController.php +++ /dev/null @@ -1,591 +0,0 @@ - - */ -class ProjectController extends PHPCensor\Controller -{ - /** - * @var \PHPCensor\Store\ProjectStore - */ - protected $projectStore; - - /** - * @var \PHPCensor\Service\ProjectService - */ - protected $projectService; - - /** - * @var \PHPCensor\Store\BuildStore - */ - protected $buildStore; - - /** - * @var \PHPCensor\Service\BuildService - */ - protected $buildService; - - /** - * Initialise the controller, set up stores and services. - */ - public function init() - { - $this->buildStore = Store\Factory::getStore('Build'); - $this->projectStore = Store\Factory::getStore('Project'); - $this->projectService = new ProjectService($this->projectStore); - $this->buildService = new BuildService($this->buildStore); - } - - /** - * @param int $projectId - * - * @return b8\Http\Response - */ - public function ajaxBuilds($projectId) - { - $branch = $this->getParam('branch', ''); - $environment = $this->getParam('environment', ''); - $page = (integer)$this->getParam('page', 1); - $perPage = (integer)$this->getParam('per_page', 10); - $builds = $this->getLatestBuildsHtml($projectId, $branch, $environment, (($page - 1) * $perPage), $perPage); - - $this->response->disableLayout(); - $this->response->setContent($builds[0]); - - return $this->response; - } - - /** - * View a specific project. - * - * @param integer $projectId - * - * @throws NotFoundException - * - * @return string - */ - public function view($projectId) - { - $branch = $this->getParam('branch', ''); - $environment = $this->getParam('environment', ''); - $page = (integer)$this->getParam('page', 1); - $project = $this->projectStore->getById($projectId); - - if (empty($project)) { - throw new NotFoundException(Lang::get('project_x_not_found', $projectId)); - } - - /** @var PHPCensor\Model\User $user */ - $user = $_SESSION['php-censor-user']; - $perPage = $user->getFinalPerPage(); - $builds = $this->getLatestBuildsHtml($projectId, $branch, $environment, (($page - 1) * $perPage), $perPage); - $pages = ($builds[1] === 0) - ? 1 - : (integer)ceil($builds[1] / $perPage); - - if ($page > $pages) { - $page = $pages; - } - - $this->view->builds = $builds[0]; - $this->view->total = $builds[1]; - $this->view->project = $project; - $this->view->branch = urldecode($branch); - $this->view->branches = $this->projectStore->getKnownBranches($projectId); - $this->view->environment = urldecode($environment); - $this->view->environments = $project->getEnvironmentsNames(); - $this->view->page = $page; - $this->view->perPage = $perPage; - $this->view->paginator = $this->getPaginatorHtml($projectId, $branch, $environment, $builds[1], $perPage, $page); - - $this->layout->title = $project->getTitle(); - $this->layout->subtitle = ''; - - if (!empty($this->view->environment)) { - $this->layout->subtitle = ' ' . $this->view->environment; - } elseif (!empty($this->view->branch)) { - $this->layout->subtitle = ' ' . $this->view->branch; - } - - return $this->view->render(); - } - - /** - * @param integer $projectId - * @param string $branch - * @param string $environment - * @param integer $total - * @param integer $perPage - * @param integer $page - * - * @return string - */ - protected function getPaginatorHtml($projectId, $branch, $environment, $total, $perPage, $page) - { - $view = new b8\View('pagination'); - - $urlPattern = APP_URL . 'project/view/' . $projectId; - $params = []; - if (!empty($branch)) { - $params['branch'] = $branch; - } - - if (!empty($environment)) { - $params['environment'] = $environment; - } - - $urlPattern = $urlPattern . '?' . str_replace('%28%3Anum%29', '(:num)', http_build_query(array_merge($params, ['page' => '(:num)']))); - $paginator = new Paginator($total, $perPage, $page, $urlPattern); - - $view->paginator = $paginator; - - return $view->render(); - } - - /** - * Create a new pending build for a project. - * - * @param integer $projectId - * - * @throws NotFoundException - * - * @return RedirectResponse - * - */ - public function build($projectId) - { - /* @var \PHPCensor\Model\Project $project */ - $project = $this->projectStore->getById($projectId); - if (empty($project) || $project->getArchived()) { - throw new NotFoundException(Lang::get('project_x_not_found', $projectId)); - } - - $type = $this->getParam('type', 'branch'); - $id = $this->getParam('id'); - $debug = (boolean)$this->getParam('debug', false); - - $environment = null; - $branch = null; - - switch($type) { - case 'environment': - $environment = $id; - break; - case 'branch': - $branch = $id; - break; - } - - if (empty($branch)) { - $branch = $project->getBranch(); - } - - $extra = null; - - if ($debug && $this->currentUserIsAdmin()) { - $extra = [ - 'debug' => true, - ]; - } - - /** @var PHPCensor\Model\User $user */ - $user = $_SESSION['php-censor-user']; - $build = $this->buildService->createBuild( - $project, - $environment, - '', - $branch, - null, - $user->getEmail(), - null, - Build::SOURCE_MANUAL_WEB, - $user->getId(), - $extra - ); - - if ($this->buildService->queueError) { - $_SESSION['global_error'] = Lang::get('add_to_queue_failed'); - } - - $response = new RedirectResponse(); - $response->setHeader('Location', APP_URL.'build/view/' . $build->getId()); - - return $response; - } - - /** - * Delete a project. - */ - public function delete($projectId) - { - $this->requireAdmin(); - - $project = $this->projectStore->getById($projectId); - $this->projectService->deleteProject($project); - - $response = new RedirectResponse(); - $response->setHeader('Location', APP_URL); - - return $response; - } - - /** - * Render latest builds for project as HTML table. - * - * @param int $projectId - * @param string $branch A urldecoded branch name. - * @param string $environment A urldecoded environment name. - * @param int $start - * @param int $perPage - * - * @return array - */ - protected function getLatestBuildsHtml($projectId, $branch = '', $environment = '', $start = 0, $perPage = 10) - { - $criteria = ['project_id' => $projectId]; - - if (!empty($environment)) { - $criteria['environment'] = $environment; - } - - if (!empty($branch)) { - $criteria['branch'] = $branch; - } - - $order = ['id' => 'DESC']; - $builds = $this->buildStore->getWhere($criteria, $perPage, $start, [], $order); - $view = new b8\View('Project/ajax-builds'); - - foreach ($builds['items'] as &$build) { - $build = BuildFactory::getBuild($build); - } - - $view->builds = $builds['items']; - - return [ - $view->render(), - (integer)$builds['count'] - ]; - } - - /** - * Add a new project. Handles both the form, and processing. - */ - public function add() - { - $this->layout->title = Lang::get('add_project'); - $this->requireAdmin(); - - $method = $this->request->getMethod(); - $pub = null; - $values = $this->getParams(); - $values['branch'] = ''; - - if ($method != 'POST') { - $sshKey = new SshKey(); - $key = $sshKey->generate(); - - $values['key'] = $key['private_key']; - $values['pubkey'] = $key['public_key']; - $pub = $key['public_key']; - } - - $form = $this->projectForm($values); - - if ($method != 'POST' || ($method == 'POST' && !$form->validate())) { - $view = new b8\View('Project/edit'); - $view->type = 'add'; - $view->project = null; - $view->form = $form; - $view->key = $pub; - - return $view->render(); - } else { - $title = $this->getParam('title', 'New Project'); - $reference = $this->getParam('reference', null); - $type = $this->getParam('type', null); - - $options = [ - 'ssh_private_key' => $this->getParam('key', null), - 'ssh_public_key' => $this->getParam('pubkey', null), - 'build_config' => $this->getParam('build_config', null), - 'allow_public_status' => $this->getParam('allow_public_status', 0), - 'branch' => $this->getParam('branch', null), - 'default_branch_only' => $this->getParam('default_branch_only', 0), - 'group' => $this->getParam('group_id', null), - 'environments' => $this->getParam('environments', null), - ]; - - /** @var PHPCensor\Model\User $user */ - $user = $_SESSION['php-censor-user']; - $project = $this->projectService->createProject($title, $type, $reference, $user->getId(), $options); - - $response = new RedirectResponse(); - $response->setHeader('Location', APP_URL.'project/view/' . $project->getId()); - - return $response; - } - } - - /** - * Edit a project. Handles both the form and processing. - */ - public function edit($projectId) - { - $this->requireAdmin(); - - $method = $this->request->getMethod(); - $project = $this->projectStore->getById($projectId); - - if (empty($project)) { - throw new NotFoundException(Lang::get('project_x_not_found', $projectId)); - } - - $this->layout->title = $project->getTitle(); - $this->layout->subtitle = Lang::get('edit_project'); - - $values = $project->getDataArray(); - $values['key'] = $values['ssh_private_key']; - $values['pubkey'] = $values['ssh_public_key']; - $values['environments'] = $project->getEnvironments(); - - if ($values['type'] == 'gitlab') { - $accessInfo = $project->getAccessInformation(); - $reference = $accessInfo["user"] . '@' . $accessInfo["domain"] . ':' . $accessInfo["port"] . '/' . ltrim($project->getReference(), '/') . ".git"; - $values['reference'] = $reference; - } - - if ($method == 'POST') { - $values = $this->getParams(); - } - - $form = $this->projectForm($values, 'edit/' . $projectId); - - if ($method != 'POST' || ($method == 'POST' && !$form->validate())) { - $view = new b8\View('Project/edit'); - $view->type = 'edit'; - $view->project = $project; - $view->form = $form; - $view->key = $values['pubkey']; - - return $view->render(); - } - - $title = $this->getParam('title', Lang::get('new_project')); - $reference = $this->getParam('reference', null); - $type = $this->getParam('type', null); - - $options = [ - 'ssh_private_key' => $this->getParam('key', null), - 'ssh_public_key' => $this->getParam('pubkey', null), - 'build_config' => $this->getParam('build_config', null), - 'allow_public_status' => $this->getParam('allow_public_status', 0), - 'archived' => $this->getParam('archived', 0), - 'branch' => $this->getParam('branch', null), - 'default_branch_only' => $this->getParam('default_branch_only', 0), - 'group' => $this->getParam('group_id', null), - 'environments' => $this->getParam('environments', null), - ]; - - $project = $this->projectService->updateProject($project, $title, $type, $reference, $options); - - $response = new RedirectResponse(); - $response->setHeader('Location', APP_URL.'project/view/' . $project->getId()); - - return $response; - } - - /** - * Create add / edit project form. - */ - protected function projectForm($values, $type = 'add') - { - $form = new Form(); - - $form->setMethod('POST'); - $form->setAction(APP_URL.'project/' . $type); - - $form->addField(new Form\Element\Csrf('csrf')); - $form->addField(new Form\Element\Hidden('pubkey')); - - $options = [ - 'choose' => Lang::get('select_repository_type'), - 'github' => 'GitHub', - 'bitbucket' => 'Bitbucket (Git)', - 'bitbuckethg' => 'Bitbucket (Hg)', - 'gitlab' => 'GitLab', - 'gogs' => 'Gogs', - 'remote' => 'Git', - 'local' => Lang::get('local'), - 'hg' => 'Mercurial (Hg)', - 'svn' => 'SVN', - ]; - - $field = Form\Element\Select::create('type', Lang::get('where_hosted'), true); - $field->setPattern('^(github|bitbucket|bitbuckethg|gitlab|gogs|remote|local|hg|svn)'); - $field->setOptions($options); - $field->setClass('form-control')->setContainerClass('form-group'); - $form->addField($field); - - $container = new Form\ControlGroup('github-container'); - $container->setClass('github-container'); - - $field = Form\Element\Select::create('github', Lang::get('choose_github'), false); - $field->setClass('form-control')->setContainerClass('form-group'); - $container->addField($field); - $form->addField($container); - - $field = Form\Element\Text::create('reference', Lang::get('repo_name'), true); - $field->setValidator($this->getReferenceValidator($values)); - $field->setClass('form-control')->setContainerClass('form-group'); - $form->addField($field); - - $field = Form\Element\Text::create('title', Lang::get('project_title'), true); - $field->setClass('form-control')->setContainerClass('form-group'); - $form->addField($field); - - $field = Form\Element\Text::create('branch', Lang::get('default_branch'), false); - $field->setClass('form-control')->setContainerClass('form-group')->setValue(''); - $form->addField($field); - - $field = Form\Element\Checkbox::create( - 'default_branch_only', - Lang::get('default_branch_only'), - false - ); - $field->setContainerClass('form-group'); - $field->setCheckedValue(1); - $field->setValue(0); - $form->addField($field); - - $field = Form\Element\TextArea::create('key', Lang::get('project_private_key'), false); - $field->setClass('form-control')->setContainerClass('form-group'); - $field->setRows(6); - $form->addField($field); - - $field = Form\Element\TextArea::create('build_config', Lang::get('build_config'), false); - $field->setClass('form-control')->setContainerClass('form-group'); - $field->setRows(6); - $form->addField($field); - - $field = Form\Element\TextArea::create('environments', Lang::get('environments_label'), false); - $field->setClass('form-control')->setContainerClass('form-group'); - $field->setRows(6); - $form->addField($field); - - $field = Form\Element\Select::create('group_id', Lang::get('project_group'), true); - $field->setClass('form-control')->setContainerClass('form-group')->setValue(1); - - $groups = []; - $groupStore = b8\Store\Factory::getStore('ProjectGroup'); - $groupList = $groupStore->getWhere([], 100, 0, [], ['title' => 'ASC']); - - foreach ($groupList['items'] as $group) { - $groups[$group->getId()] = $group->getTitle(); - } - - $field->setOptions($groups); - $form->addField($field); - - $field = Form\Element\Checkbox::create('allow_public_status', Lang::get('allow_public_status'), false); - $field->setContainerClass('form-group'); - $field->setCheckedValue(1); - $field->setValue(0); - $form->addField($field); - - $field = Form\Element\Checkbox::create('archived', Lang::get('archived'), false); - $field->setContainerClass('form-group'); - $field->setCheckedValue(1); - $field->setValue(0); - $form->addField($field); - - $field = new Form\Element\Submit(); - $field->setValue(Lang::get('save_project')); - $field->setContainerClass('form-group'); - $field->setClass('btn-success'); - $form->addField($field); - - $form->setValues($values); - - return $form; - } - - /** - * Get the validator to use to check project references. - * @param $values - * @return callable - */ - protected function getReferenceValidator($values) - { - return function ($val) use ($values) { - $type = $values['type']; - - $validators = [ - 'hg' => [ - 'regex' => '/^(ssh|https?):\/\//', - 'message' => Lang::get('error_mercurial') - ], - 'remote' => [ - 'regex' => '/^(git|https?):\/\//', - 'message' => Lang::get('error_remote') - ], - 'gitlab' => [ - 'regex' => '`^(.*)@(.*):(.*)/(.*)\.git`', - 'message' => Lang::get('error_gitlab') - ], - 'github' => [ - 'regex' => '/^[a-zA-Z0-9_\-]+\/[a-zA-Z0-9_\-\.]+$/', - 'message' => Lang::get('error_github') - ], - 'bitbucket' => [ - 'regex' => '/^[a-zA-Z0-9_\-]+\/[a-zA-Z0-9_\-\.]+$/', - 'message' => Lang::get('error_bitbucket') - ], - 'bitbuckethg' => [ - 'regex' => '/^[a-zA-Z0-9_\-]+\/[a-zA-Z0-9_\-\.]+$/', - 'message' => Lang::get('error_bitbucket') - ], - ]; - - 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(Lang::get('error_path')); - } - - return true; - }; - } - - /** - * Get an array of repositories from Github's API. - */ - public function ajaxGithubRepositories() - { - $github = new Github(); - - $response = new b8\Http\Response\JsonResponse(); - $response->setContent($github->getRepositories()); - - return $response; - } -} diff --git a/src/PHPCensor/Controller/SessionController.php b/src/PHPCensor/Controller/SessionController.php deleted file mode 100644 index fb60230..0000000 --- a/src/PHPCensor/Controller/SessionController.php +++ /dev/null @@ -1,281 +0,0 @@ - - */ -class SessionController extends Controller -{ - /** - * @var UserStore - */ - protected $userStore; - - /** - * @var Service - */ - protected $authentication; - - /** - * Initialise the controller, set up stores and services. - */ - public function init() - { - $this->response->disableLayout(); - - $this->userStore = b8\Store\Factory::getStore('User'); - $this->authentication = Service::getInstance(); - } - - /** - * Handles user login (form and processing) - */ - public function login() - { - if (!empty($_COOKIE['remember_key'])) { - $user = $this->userStore->getByRememberKey($_COOKIE['remember_key']); - if ($user) { - $_SESSION['php-censor-user-id'] = $user->getId(); - - $response = new b8\Http\Response\RedirectResponse(); - $response->setHeader('Location', $this->getLoginRedirect()); - - return $response; - } - } - - $isLoginFailure = false; - - if ($this->request->getMethod() == 'POST') { - $token = $this->getParam('token'); - if (!isset($token, $_SESSION['login_token']) || $token !== $_SESSION['login_token']) { - $isLoginFailure = true; - } else { - unset($_SESSION['login_token']); - - $email = $this->getParam('email'); - $password = $this->getParam('password', ''); - $rememberMe = (bool)$this->getParam('remember_me', 0); - $isLoginFailure = true; - - $user = $this->userStore->getByEmailOrName($email); - $providers = $this->authentication->getLoginPasswordProviders(); - - if (null !== $user) { - // Delegate password verification to the user provider, if found - $key = $user->getProviderKey(); - $isLoginFailure = !isset($providers[$key]) || !$providers[$key]->verifyPassword($user, $password); - } else { - // Ask each providers to provision the user - foreach ($providers as $provider) { - $user = $provider->provisionUser($email); - if ($user && $provider->verifyPassword($user, $password)) { - $this->userStore->save($user); - $isLoginFailure = false; - break; - } - } - } - - if (!$isLoginFailure) { - $_SESSION['php-censor-user-id'] = $user->getId(); - - if ($rememberMe) { - $rememberKey = md5(microtime(true)); - - $user->setRememberKey($rememberKey); - $this->userStore->save($user); - - setcookie( - 'remember_key', - $rememberKey, - (time() + 60 * 60 * 24 * 30), - null, - null, - null, - true - ); - } - - $response = new b8\Http\Response\RedirectResponse(); - $response->setHeader('Location', $this->getLoginRedirect()); - - return $response; - } - } - } - - $form = new b8\Form(); - $form->setMethod('POST'); - $form->setAction(APP_URL . 'session/login'); - - $email = new b8\Form\Element\Text('email'); - $email->setLabel(Lang::get('login')); - $email->setRequired(true); - $email->setContainerClass('form-group'); - $email->setClass('form-control'); - $form->addField($email); - - $pwd = new b8\Form\Element\Password('password'); - $pwd->setLabel(Lang::get('password')); - $pwd->setRequired(true); - $pwd->setContainerClass('form-group'); - $pwd->setClass('form-control'); - $form->addField($pwd); - - $remember = b8\Form\Element\Checkbox::create('remember_me', Lang::get('remember_me'), false); - $remember->setContainerClass('form-group'); - $remember->setCheckedValue(1); - $remember->setValue(0); - $form->addField($remember); - - $pwd = new b8\Form\Element\Submit(); - $pwd->setValue(Lang::get('log_in')); - $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(); - } - - /** - * Handles user logout. - */ - public function logout() - { - unset($_SESSION['php-censor-user']); - unset($_SESSION['php-censor-user-id']); - - session_destroy(); - - setcookie( - 'remember_key', - null, - (time() - 1), - null, - null, - null, - true - ); - - $response = new b8\Http\Response\RedirectResponse(); - $response->setHeader('Location', APP_URL); - return $response; - } - - /** - * Allows the user to request a password reset email. - * @return string - */ - public function forgotPassword() - { - if ($this->request->getMethod() == 'POST') { - $email = $this->getParam('email', null); - $user = $this->userStore->getByEmail($email); - - if (empty($user)) { - $this->view->error = Lang::get('reset_no_user_exists'); - return $this->view->render(); - } - - $key = md5(date('Y-m-d') . $user->getHash()); - $url = APP_URL; - - $message = Lang::get('reset_email_body', $user->getName(), $url, $user->getId(), $key); - - $email = new Email(); - $email->setEmailTo($user->getEmail(), $user->getName()); - $email->setSubject(Lang::get('reset_email_title', $user->getName())); - $email->setBody($message); - $email->send(); - - $this->view->emailed = true; - } - - return $this->view->render(); - } - - /** - * Allows the user to change their password after a password reset email. - * @param $userId - * @param $key - * @return string - */ - public function resetPassword($userId, $key) - { - $user = $this->userStore->getById($userId); - $userKey = md5(date('Y-m-d') . $user->getHash()); - - if (empty($user) || $key != $userKey) { - $this->view->error = Lang::get('reset_invalid'); - return $this->view->render(); - } - - if ($this->request->getMethod() == 'POST') { - $hash = password_hash($this->getParam('password'), PASSWORD_DEFAULT); - $user->setHash($hash); - - $_SESSION['php-censor-user'] = $this->userStore->save($user); - $_SESSION['php-censor-user-id'] = $user->getId(); - - $response = new b8\Http\Response\RedirectResponse(); - $response->setHeader('Location', APP_URL); - return $response; - } - - $this->view->id = $userId; - $this->view->key = $key; - - return $this->view->render(); - } - - /** - * Get the URL the user was trying to go to prior to being asked to log in. - * @return string - */ - protected function getLoginRedirect() - { - $rtn = APP_URL; - - if (!empty($_SESSION['php-censor-login-redirect'])) { - $rtn .= $_SESSION['php-censor-login-redirect']; - $_SESSION['php-censor-login-redirect'] = null; - } - - 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)); - } -} diff --git a/src/PHPCensor/Controller/UserController.php b/src/PHPCensor/Controller/UserController.php deleted file mode 100644 index 551a65d..0000000 --- a/src/PHPCensor/Controller/UserController.php +++ /dev/null @@ -1,304 +0,0 @@ - - */ -class UserController extends Controller -{ - /** - * @var \PHPCensor\Store\UserStore - */ - protected $userStore; - - /** - * @var \PHPCensor\Service\UserService - */ - protected $userService; - - /** - * Initialise the controller, set up stores and services. - */ - public function init() - { - $this->userStore = b8\Store\Factory::getStore('User'); - $this->userService = new UserService($this->userStore); - } - - /** - * View user list. - */ - public function index() - { - $users = $this->userStore->getWhere([], 1000, 0, [], ['email' => 'ASC']); - $this->view->users = $users; - $this->layout->title = Lang::get('manage_users'); - - return $this->view->render(); - } - - /** - * Allows the user to edit their profile. - * @return string - */ - public function profile() - { - /** @var User $user */ - $user = $_SESSION['php-censor-user']; - - if ($this->request->getMethod() == 'POST') { - $name = $this->getParam('name', null); - $email = $this->getParam('email', null); - $password = $this->getParam('password', null); - - $language = $this->getParam('language', null); - if (!$language) { - $language = null; - } - - $perPage = $this->getParam('per_page', null); - if (!$perPage) { - $perPage = null; - } - - $_SESSION['php-censor-user'] = $this->userService->updateUser($user, $name, $email, $password, null, $language, $perPage); - $user = $_SESSION['php-censor-user']; - - $this->view->updated = 1; - } - - $this->layout->title = $user->getName(); - $this->layout->subtitle = Lang::get('edit_profile'); - - $form = new Form(); - $form->setAction(APP_URL.'user/profile'); - $form->setMethod('POST'); - - $name = new Form\Element\Text('name'); - $name->setClass('form-control'); - $name->setContainerClass('form-group'); - $name->setLabel(Lang::get('name')); - $name->setRequired(true); - $name->setValue($user->getName()); - $form->addField($name); - - $email = new Form\Element\Email('email'); - $email->setClass('form-control'); - $email->setContainerClass('form-group'); - $email->setLabel(Lang::get('email_address')); - $email->setRequired(true); - $email->setValue($user->getEmail()); - $form->addField($email); - - $password = new Form\Element\Password('password'); - $password->setClass('form-control'); - $password->setContainerClass('form-group'); - $password->setLabel(Lang::get('password_change')); - $password->setRequired(false); - $password->setValue(null); - $form->addField($password); - - $language = new Form\Element\Select('language'); - $language->setClass('form-control'); - $language->setContainerClass('form-group'); - $language->setLabel(Lang::get('language')); - $language->setRequired(true); - $language->setOptions(array_merge( - [null => Lang::get('default') . ' (' . b8\Config::getInstance()->get('php-censor.language') . ')'], - Lang::getLanguageOptions()) - ); - $language->setValue($user->getLanguage()); - $form->addField($language); - - $perPage = new Form\Element\Select('per_page'); - $perPage->setClass('form-control'); - $perPage->setContainerClass('form-group'); - $perPage->setLabel(Lang::get('per_page')); - $perPage->setRequired(true); - $perPage->setOptions([ - null => Lang::get('default') . ' (' . b8\Config::getInstance()->get('php-censor.per_page') . ')', - 10 => 10, - 25 => 25, - 50 => 50, - 100 => 100, - ]); - $perPage->setValue($user->getPerPage()); - $form->addField($perPage); - - $submit = new Form\Element\Submit(); - $submit->setClass('btn btn-success'); - $submit->setValue(Lang::get('save')); - $form->addField($submit); - - $this->view->form = $form; - - return $this->view->render(); - } - - /** - * Add a user - handles both form and processing. - */ - public function add() - { - $this->requireAdmin(); - - $this->layout->title = Lang::get('add_user'); - - $method = $this->request->getMethod(); - - if ($method == 'POST') { - $values = $this->getParams(); - } else { - $values = []; - } - - $form = $this->userForm($values); - - if ($method != 'POST' || ($method == 'POST' && !$form->validate())) { - $view = new b8\View('User/edit'); - $view->type = 'add'; - $view->user = null; - $view->form = $form; - - return $view->render(); - } - - - $name = $this->getParam('name', null); - $email = $this->getParam('email', null); - $password = $this->getParam('password', null); - $isAdmin = (int)$this->getParam('is_admin', 0); - - $this->userService->createUser($name, $email, 'internal', json_encode(['type' => 'internal']), $password, $isAdmin); - - $response = new b8\Http\Response\RedirectResponse(); - $response->setHeader('Location', APP_URL . 'user'); - return $response; - } - - /** - * Edit a user - handles both form and processing. - */ - public function edit($userId) - { - $this->requireAdmin(); - - $method = $this->request->getMethod(); - $user = $this->userStore->getById($userId); - - if (empty($user)) { - throw new NotFoundException(Lang::get('user_n_not_found', $userId)); - } - - $this->layout->title = $user->getName(); - $this->layout->subtitle = Lang::get('edit_user'); - - $values = array_merge($user->getDataArray(), $this->getParams()); - $form = $this->userForm($values, 'edit/' . $userId); - - if ($method != 'POST' || ($method == 'POST' && !$form->validate())) { - $view = new b8\View('User/edit'); - $view->type = 'edit'; - $view->user = $user; - $view->form = $form; - - return $view->render(); - } - - $name = $this->getParam('name', null); - $email = $this->getParam('email', null); - $password = $this->getParam('password', null); - $isAdmin = (int)$this->getParam('is_admin', 0); - - $this->userService->updateUser($user, $name, $email, $password, $isAdmin); - - $response = new b8\Http\Response\RedirectResponse(); - $response->setHeader('Location', APP_URL . 'user'); - return $response; - } - - /** - * Create user add / edit form. - */ - protected function userForm($values, $type = 'add') - { - $form = new Form(); - $form->setMethod('POST'); - $form->setAction(APP_URL.'user/' . $type); - $form->addField(new Form\Element\Csrf('csrf')); - - $field = new Form\Element\Email('email'); - $field->setRequired(true); - $field->setLabel(Lang::get('email_address')); - $field->setClass('form-control'); - $field->setContainerClass('form-group'); - $form->addField($field); - - $field = new Form\Element\Text('name'); - $field->setRequired(true); - $field->setLabel(Lang::get('name')); - $field->setClass('form-control'); - $field->setContainerClass('form-group'); - $form->addField($field); - - $field = new Form\Element\Password('password'); - - if ($type == 'add') { - $field->setRequired(true); - $field->setLabel(Lang::get('password')); - } else { - $field->setRequired(false); - $field->setLabel(Lang::get('password_change')); - } - - $field->setClass('form-control'); - $field->setContainerClass('form-group'); - $form->addField($field); - - $field = new Form\Element\Checkbox('is_admin'); - $field->setRequired(false); - $field->setCheckedValue(1); - $field->setLabel(Lang::get('is_user_admin')); - $field->setContainerClass('form-group'); - $form->addField($field); - - $field = new Form\Element\Submit(); - $field->setValue(Lang::get('save_user')); - $field->setClass('btn-success'); - $form->addField($field); - - $form->setValues($values); - return $form; - } - - /** - * Delete a user. - */ - public function delete($userId) - { - $this->requireAdmin(); - - $user = $this->userStore->getById($userId); - - if (empty($user)) { - throw new NotFoundException(Lang::get('user_n_not_found', $userId)); - } - - $this->userService->deleteUser($user); - - $response = new b8\Http\Response\RedirectResponse(); - $response->setHeader('Location', APP_URL . 'user'); - return $response; - } -} diff --git a/src/PHPCensor/Controller/WebhookController.php b/src/PHPCensor/Controller/WebhookController.php deleted file mode 100644 index 3ff9cdf..0000000 --- a/src/PHPCensor/Controller/WebhookController.php +++ /dev/null @@ -1,760 +0,0 @@ - - * @author Sami Tikka - * @author Alex Russell - * @author Guillaume Perréal - * - */ -class WebhookController extends Controller -{ - /** - * @var BuildStore - */ - protected $buildStore; - - /** - * @var ProjectStore - */ - protected $projectStore; - - /** - * @var BuildService - */ - protected $buildService; - - /** - * Initialise the controller, set up stores and services. - */ - public function init() - { - $this->buildStore = Store\Factory::getStore('Build'); - $this->projectStore = Store\Factory::getStore('Project'); - $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(['status' => 'failed', 'error' => $ex->getMessage()]); - } - return $response; - } - - /** - * Called by Bitbucket. - */ - public function bitbucket($projectId) - { - $project = $this->fetchProject($projectId, ['bitbucket', 'bitbuckethg', 'remote']); - - // Support both old services and new webhooks - if ($payload = $this->getParam('payload')) { - return $this->bitbucketService(json_decode($payload, true), $project); - } - - $payload = json_decode(file_get_contents("php://input"), true); - - // Handle Pull Request webhooks: - if (!empty($payload['pullrequest'])) { - return $this->bitbucketPullRequest($project, $payload); - } - - // Handle Push (and Tag) webhooks: - if (!empty($payload['push']['changes'])) { - return $this->bitbucketCommitRequest($project, $payload); - } - - // Invalid event from bitbucket - return [ - 'status' => 'failed', - 'commits' => [] - ]; - } - - /** - * Handle the payload when Bitbucket sends a commit webhook. - * - * @param Project $project - * @param array $payload - * - * @return array - */ - protected function bitbucketCommitRequest(Project $project, array $payload) - { - $results = []; - $status = 'failed'; - foreach ($payload['push']['changes'] as $commit) { - try { - $email = $commit['new']['target']['author']['raw']; - if (strpos($email, '>') !== false) { - // In order not to loose email if it is RAW, w/o "<>" symbols - $email = substr($email, 0, strpos($email, '>')); - $email = substr($email, strpos($email, '<') + 1); - } - - $results[$commit['new']['target']['hash']] = $this->createBuild( - $project, - $commit['new']['target']['hash'], - $commit['new']['name'], - null, - $email, - $commit['new']['target']['message'] - ); - $status = 'ok'; - } catch (Exception $ex) { - $results[$commit['new']['target']['hash']] = ['status' => 'failed', 'error' => $ex->getMessage()]; - } - } - - return ['status' => $status, 'commits' => $results]; - } - - /** - * Handle the payload when Bitbucket sends a Pull Request webhook. - * - * @param Project $project - * @param array $payload - * - * @return array - * - * @throws Exception - */ - protected function bitbucketPullRequest(Project $project, array $payload) - { - // We only want to know about open pull requests: - if (!in_array($_SERVER['HTTP_X_EVENT_KEY'], ['pullrequest:created', 'pullrequest:updated'])) { - return ['status' => 'ok']; - } - - $headers = []; - $username = Config::getInstance()->get('php-censor.bitbucket.username'); - $appPassword = Config::getInstance()->get('php-censor.bitbucket.app_password'); - - if (empty($username) || empty($appPassword)) { - throw new Exception('Please provide Username and App Password of your Bitbucket account.'); - } - - $commitsUrl = $payload['pullrequest']['links']['commits']['href']; - - $client = new Client(); - $commitsResponse = $client->get($commitsUrl, [ - 'auth' => [$username, $appPassword], - ]); - $httpStatus = (integer)$commitsResponse->getStatusCode(); - - // Check we got a success response: - if ($httpStatus < 200 || $httpStatus >= 300) { - throw new Exception('Could not get commits, failed API request.'); - } - - $results = []; - $status = 'failed'; - $commits = json_decode($commitsResponse->getBody(), true)['values']; - foreach ($commits as $commit) { - // Skip all but the current HEAD commit ID: - $id = $commit['hash']; - if (strpos($id, $payload['pullrequest']['source']['commit']['hash']) !== 0) { - $results[$id] = ['status' => 'ignored', 'message' => 'not branch head']; - continue; - } - - try { - $branch = $payload['pullrequest']['destination']['branch']['name']; - $committer = $commit['author']['raw']; - if (strpos($committer, '>') !== false) { - // In order not to loose email if it is RAW, w/o "<>" symbols - $committer = substr($committer, 0, strpos($committer, '>')); - $committer = substr($committer, strpos($committer, '<') + 1); - } - $message = $commit['message']; - - $extra = [ - 'build_type' => 'pull_request', - 'pull_request_number' => $payload['pullrequest']['id'], - 'remote_branch' => $payload['pullrequest']['source']['branch']['name'], - 'remote_reference' => $payload['pullrequest']['source']['repository']['full_name'], - ]; - - $results[$id] = $this->createBuild($project, $id, $branch, null, $committer, $message, $extra); - $status = 'ok'; - } catch (Exception $ex) { - $results[$id] = ['status' => 'failed', 'error' => $ex->getMessage()]; - } - } - - return ['status' => $status, 'commits' => $results]; - } - - /** - * Bitbucket webhooks. - * - * @deprecated, for BC purpose - */ - protected function bitbucketWebhook($payload, $project) - { - return $this->bitbucketCommitRequest($project, $payload); - } - - /** - * Bitbucket POST service. - */ - protected function bitbucketService($payload, $project) - { - $payload = json_decode($this->getParam('payload'), true); - - $results = []; - $status = 'failed'; - foreach ($payload['commits'] as $commit) { - try { - $email = $commit['raw_author']; - $email = substr($email, 0, strpos($email, '>')); - $email = substr($email, strpos($email, '<') + 1); - - $results[$commit['raw_node']] = $this->createBuild( - $project, - $commit['raw_node'], - $commit['branch'], - null, - $email, - $commit['message'] - ); - $status = 'ok'; - } catch (Exception $ex) { - $results[$commit['raw_node']] = ['status' => 'failed', 'error' => $ex->getMessage()]; - } - } - - return ['status' => $status, 'commits' => $results]; - } - - /** - * Called by POSTing to /webhook/git/?branch=&commit= - * - * @param string $projectId - * - * @return array - */ - public function git($projectId) - { - $project = $this->fetchProject($projectId, ['local', 'remote']); - $branch = $this->getParam('branch', $project->getBranch()); - $commit = $this->getParam('commit'); - $commitMessage = $this->getParam('message'); - $committer = $this->getParam('committer'); - - return $this->createBuild($project, $commit, $branch, null, $committer, $commitMessage); - } - - /** - * Called by Github Webhooks: - */ - public function github($projectId) - { - $project = $this->fetchProject($projectId, ['github', 'remote']); - - 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: - return [ - 'status' => 'failed', - 'error' => 'Content type not supported.', - 'responseCode' => 401 - ]; - } - - // Handle Pull Request webhooks: - if (array_key_exists('pull_request', $payload)) { - return $this->githubPullRequest($project, $payload); - } - - // Handle Push (and Tag) webhooks: - if (array_key_exists('head_commit', $payload)) { - return $this->githubCommitRequest($project, $payload); - } - - return ['status' => 'ignored', 'message' => 'Unusable payload.']; - } - - /** - * Handle the payload when Github sends a commit webhook. - * - * @param Project $project - * @param array $payload - * - * @return array - */ - protected function githubCommitRequest(Project $project, array $payload) - { - // Github sends a payload when you close a pull request with a non-existent commit. We don't want this. - if ( - array_key_exists('after', $payload) && - $payload['after'] === '0000000000000000000000000000000000000000' - ) { - return ['status' => 'ignored']; - } - - if (isset($payload['head_commit']) && $payload['head_commit']) { - $isTag = (substr($payload['ref'], 0, 10) == 'refs/tags/') ? true : false; - $commit = $payload['head_commit']; - $results = []; - $status = 'failed'; - - if (!$commit['distinct']) { - $results[$commit['id']] = ['status' => 'ignored']; - } else { - try { - $tag = null; - if ($isTag) { - $tag = str_replace('refs/tags/', '', $payload['ref']); - $branch = str_replace('refs/heads/', '', $payload['base_ref']); - $committer = $payload['pusher']['email']; - } else { - $branch = str_replace('refs/heads/', '', $payload['ref']); - $committer = $commit['committer']['email']; - } - - $results[$commit['id']] = $this->createBuild( - $project, - $commit['id'], - $branch, - $tag, - $committer, - $commit['message'] - ); - - $status = 'ok'; - } catch (Exception $ex) { - $results[$commit['id']] = ['status' => 'failed', 'error' => $ex->getMessage()]; - } - } - - return ['status' => $status, 'commits' => $results]; - } - - return ['status' => 'ignored', 'message' => 'Unusable payload.']; - } - - /** - * Handle the payload when Github sends a Pull Request webhook. - * - * @param Project $project - * @param array $payload - * - * @return array - * - * @throws Exception - */ - protected function githubPullRequest(Project $project, array $payload) - { - // We only want to know about open pull requests: - if (!in_array($payload['action'], ['opened', 'synchronize', 'reopened'])) { - return ['status' => 'ok']; - } - - $headers = []; - $token = Config::getInstance()->get('php-censor.github.token'); - - if (!empty($token)) { - $headers['Authorization'] = 'token ' . $token; - } - - $url = $payload['pull_request']['commits_url']; - - //for large pull requests, allow grabbing more then the default number of commits - $custom_per_page = Config::getInstance()->get('php-censor.github.per_page'); - $params = []; - if ($custom_per_page) { - $params['per_page'] = $custom_per_page; - } - - $client = new Client(); - $response = $client->get($url, [ - 'headers' => $headers, - 'query' => $params, - ]); - $status = (integer)$response->getStatusCode(); - - // Check we got a success response: - if ($status < 200 || $status >= 300) { - throw new Exception('Could not get commits, failed API request.'); - } - - $results = []; - $status = 'failed'; - $commits = json_decode($response->getBody(), true); - foreach ($commits as $commit) { - // Skip all but the current HEAD commit ID: - $id = $commit['sha']; - if ($id != $payload['pull_request']['head']['sha']) { - $results[$id] = ['status' => 'ignored', 'message' => 'not branch head']; - continue; - } - - try { - $branch = str_replace('refs/heads/', '', $payload['pull_request']['base']['ref']); - $committer = $commit['commit']['author']['email']; - $message = $commit['commit']['message']; - - $remoteUrlKey = $payload['pull_request']['head']['repo']['private'] ? 'ssh_url' : 'clone_url'; - - $extra = [ - 'build_type' => 'pull_request', - 'pull_request_id' => $payload['pull_request']['id'], - 'pull_request_number' => $payload['number'], - 'remote_branch' => $payload['pull_request']['head']['ref'], - 'remote_url' => $payload['pull_request']['head']['repo'][$remoteUrlKey], - ]; - - $results[$id] = $this->createBuild($project, $id, $branch, null, $committer, $message, $extra); - $status = 'ok'; - } catch (Exception $ex) { - $results[$id] = ['status' => 'failed', 'error' => $ex->getMessage()]; - } - } - - return ['status' => $status, 'commits' => $results]; - } - - /** - * Called by Gitlab Webhooks: - */ - public function gitlab($projectId) - { - $project = $this->fetchProject($projectId, ['gitlab', 'remote']); - - $payloadString = file_get_contents("php://input"); - $payload = json_decode($payloadString, true); - - // 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']; - - return $this->createBuild($project, $commit['id'], $branch, null, $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: - - $results = []; - $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, - null, - $committer, - $commit['message'] - ); - $status = 'ok'; - } catch (Exception $ex) { - $results[$commit['id']] = ['status' => 'failed', 'error' => $ex->getMessage()]; - } - } - return ['status' => $status, 'commits' => $results]; - } - - return ['status' => 'ignored', 'message' => 'Unusable payload.']; - } - - - /** - * Called by POSTing to /webhook/svn/?branch=&commit= - * - * @author Sylvain Lévesque - * - * @param string $projectId - * - * @return array - */ - public function svn($projectId) - { - $project = $this->fetchProject($projectId, 'svn'); - $branch = $this->getParam('branch', $project->getBranch()); - $commit = $this->getParam('commit'); - $commitMessage = $this->getParam('message'); - $committer = $this->getParam('committer'); - - return $this->createBuild($project, $commit, $branch, null, $committer, $commitMessage); - } - - /** - * Called by Gogs Webhooks: - * - * @param string $projectId - * - * @return array - */ - public function gogs($projectId) - { - $project = $this->fetchProject($projectId, ['gogs', 'remote']); - 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: - return ['status' => 'failed', 'error' => 'Content type not supported.', 'responseCode' => 401]; - } - - // Handle Push web hooks: - if (array_key_exists('commits', $payload)) { - return $this->gogsCommitRequest($project, $payload); - } - - return ['status' => 'ignored', 'message' => 'Unusable payload.']; - } - - /** - * Handle the payload when Gogs sends a commit webhook. - * - * @param Project $project - * @param array $payload - * - * @return array - */ - protected function gogsCommitRequest(Project $project, array $payload) - { - 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 = []; - $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, - null, - $committer, - $commit['message'] - ); - $status = 'ok'; - } catch (Exception $ex) { - $results[$commit['id']] = ['status' => 'failed', 'error' => $ex->getMessage()]; - } - } - - return ['status' => $status, 'commits' => $results]; - } - - return ['status' => 'ignored', 'message' => 'Unusable payload.']; - } - - /** - * Wrapper for creating a new build. - * - * @param Project $project - * @param string $commitId - * @param string $branch - * @param string $tag - * @param string $committer - * @param string $commitMessage - * @param array $extra - * - * @return array - * - * @throws Exception - */ - protected function createBuild( - Project $project, - $commitId, - $branch, - $tag, - $committer, - $commitMessage, - array $extra = null - ) { - if ($project->getArchived()) { - throw new NotFoundException(Lang::get('project_x_not_found', $project->getId())); - } - - // Check if a build already exists for this commit ID: - $builds = $this->buildStore->getByProjectAndCommit($project->getId(), $commitId); - - $ignore_environments = []; - $ignore_tags = []; - if ($builds['count']) { - foreach($builds['items'] as $build) { - /** @var Build $build */ - $ignore_environments[$build->getId()] = $build->getEnvironment(); - $ignore_tags[$build->getId()] = $build->getTag(); - } - } - - // Check if this branch is to be built. - if ($project->getDefaultBranchOnly() && ($branch !== $project->getBranch())) { - return [ - 'status' => 'ignored', - 'message' => 'The branch is not a branch by default. Build is allowed only for the branch by default.' - ]; - } - - $environments = $project->getEnvironmentsObjects(); - if ($environments['count']) { - $created_builds = []; - $environment_names = $project->getEnvironmentsNamesByBranch($branch); - // use base branch from project - if (!empty($environment_names)) { - $duplicates = []; - foreach ($environment_names as $environment_name) { - if ( - !in_array($environment_name, $ignore_environments) || - ($tag && !in_array($tag, $ignore_tags, true)) - ) { - // If not, create a new build job for it: - $build = $this->buildService->createBuild( - $project, - $environment_name, - $commitId, - $project->getBranch(), - $tag, - $committer, - $commitMessage, - Build::SOURCE_WEBHOOK, - 0, - $extra - ); - - $created_builds[] = [ - 'id' => $build->getID(), - 'environment' => $environment_name, - ]; - } else { - $duplicates[] = array_search($environment_name, $ignore_environments); - } - } - if (!empty($created_builds)) { - if (empty($duplicates)) { - return ['status' => 'ok', 'builds' => $created_builds]; - } else { - return ['status' => 'ok', 'builds' => $created_builds, 'message' => sprintf('For this commit some builds already exists (%s)', implode(', ', $duplicates))]; - } - } else { - return ['status' => 'ignored', 'message' => sprintf('For this commit already created builds (%s)', implode(', ', $duplicates))]; - } - } else { - return ['status' => 'ignored', 'message' => 'Branch not assigned to any environment']; - } - } else { - $environment_name = null; - if ( - !in_array($environment_name, $ignore_environments, true) || - ($tag && !in_array($tag, $ignore_tags, true)) - ) { - $build = $this->buildService->createBuild( - $project, - null, - $commitId, - $branch, - $tag, - $committer, - $commitMessage, - Build::SOURCE_WEBHOOK, - 0, - $extra - ); - - return ['status' => 'ok', 'buildID' => $build->getID()]; - } else { - return [ - 'status' => 'ignored', - 'message' => sprintf('Duplicate of build #%d', array_search($environment_name, $ignore_environments)), - ]; - } - } - } - - /** - * Fetch a project and check its type. - * - * @param int|string $projectId id or title of project - * @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) - { - if (empty($projectId)) { - throw new Exception('Project does not exist: ' . $projectId); - } - - if (is_numeric($projectId)) { - $project = $this->projectStore->getById((integer)$projectId); - } else { - $projects = $this->projectStore->getByTitle($projectId, 2); - if ($projects['count'] < 1) { - throw new Exception('Project does not found: ' . $projectId); - } - if ($projects['count'] > 1) { - throw new Exception('Project id is ambiguous: ' . $projectId); - } - $project = reset($projects['items']); - } - - 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/src/PHPCensor/Controller/WidgetAllProjectsController.php b/src/PHPCensor/Controller/WidgetAllProjectsController.php deleted file mode 100644 index f4013a4..0000000 --- a/src/PHPCensor/Controller/WidgetAllProjectsController.php +++ /dev/null @@ -1,152 +0,0 @@ -buildStore = Factory::getStore('Build'); - $this->projectStore = Factory::getStore('Project'); - $this->groupStore = Factory::getStore('ProjectGroup'); - } - - /** - * Display dashboard. - */ - public function index() - { - $this->view->groups = $this->getGroupInfo(); - - $this->response->disableLayout(); - $this->response->setContent($this->view->render()); - - return $this->response; - } - - /** - * Generate the HTML for the project overview section of the dashboard. - * - * @param Project[] $projects - * - * @return string - */ - protected function getSummaryHtml($projects) - { - $summaryBuilds = []; - $successes = []; - $failures = []; - $counts = []; - - foreach ($projects as $project) { - $summaryBuilds[$project->getId()] = $this->buildStore->getLatestBuilds($project->getId()); - - $count = $this->buildStore->getWhere( - ['project_id' => $project->getId()], - 1, - 0, - [], - ['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); - - $successes[$project->getId()] = $success; - $failures[$project->getId()] = $failure; - } - - $view = new View('WidgetAllProjects/index-projects'); - - $view->projects = $projects; - $view->builds = $summaryBuilds; - $view->successful = $successes; - $view->failed = $failures; - $view->counts = $counts; - - return $view->render(); - } - - /** - * Get a summary of the project groups we have, and what projects they have in them. - * - * @return array - */ - protected function getGroupInfo() - { - $rtn = []; - $groups = $this->groupStore->getWhere([], 100, 0, [], ['title' => 'ASC']); - - foreach ($groups['items'] as $group) { - $thisGroup = ['title' => $group->getTitle()]; - $projects = $this->projectStore->getByGroupId($group->getId(), false); - - $thisGroup['projects'] = $projects['items']; - $thisGroup['summary'] = $this->getSummaryHtml($thisGroup['projects']); - - $rtn[] = $thisGroup; - } - - return $rtn; - } - - /** - * @param integer $projectId - * - * @return Response - */ - public function update($projectId) - { - $count = $this->buildStore->getWhere( - ['project_id' => $projectId], - 1, - 0, - [], - ['id' => 'DESC'] - ); - $counts = $count['count']; - - $this->view->project = $this->projectStore->getById($projectId); - $this->view->builds = $this->buildStore->getLatestBuilds($projectId); - $this->view->successful = $this->buildStore->getLastBuildByStatus($projectId, Build::STATUS_SUCCESS); - $this->view->failed = $this->buildStore->getLastBuildByStatus($projectId, Build::STATUS_FAILED); - $this->view->counts = $counts; - - $this->response->disableLayout(); - $this->response->setContent($this->view->render()); - - return $this->response; - } -} diff --git a/src/PHPCensor/Controller/WidgetBuildErrorsController.php b/src/PHPCensor/Controller/WidgetBuildErrorsController.php deleted file mode 100644 index 7bde6c4..0000000 --- a/src/PHPCensor/Controller/WidgetBuildErrorsController.php +++ /dev/null @@ -1,90 +0,0 @@ -buildStore = Factory::getStore('Build'); - $this->projectStore = Factory::getStore('Project'); - } - - /** - * Display dashboard. - */ - public function index() - { - $view = new View('WidgetBuildErrors/update'); - - $this->view->projects = $this->renderAllProjectsLatestBuilds($view); - - $this->response->disableLayout(); - $this->response->setContent($this->view->render()); - - return $this->response; - } - - /** - * @return Response - */ - public function update() - { - $this->response->disableLayout(); - $this->response->setContent($this->renderAllProjectsLatestBuilds($this->view)); - - return $this->response; - } - - /** - * @param View $view - * - * @return string - */ - protected function renderAllProjectsLatestBuilds($view) - { - $builds = $this->buildStore->getAllProjectsLatestBuilds(); - - if (!empty($builds['projects'])) { - $view->builds = $builds['projects']; - $projects = $this->projectStore->getByIds(array_keys($builds['projects'])); - - $view_projects = []; - foreach($projects as $id => $project) { - if (!$project->getArchived()) { - $view_projects[$id] = $project; - } else { - unset($builds['projects'][$id]); - } - } - $view->projects = $view_projects; - } else { - $view = new View('WidgetBuildErrors/empty'); - } - - return $view->render(); - } -} diff --git a/src/PHPCensor/Controller/WidgetLastBuildsController.php b/src/PHPCensor/Controller/WidgetLastBuildsController.php deleted file mode 100644 index 0e0f2ea..0000000 --- a/src/PHPCensor/Controller/WidgetLastBuildsController.php +++ /dev/null @@ -1,70 +0,0 @@ -buildStore = Factory::getStore('Build'); - } - - /** - * Display dashboard. - */ - public function index() - { - $builds = $this->buildStore->getLatestBuilds(null, 10); - - foreach ($builds as &$build) { - $build = BuildFactory::getBuild($build); - } - - $view = new View('WidgetLastBuilds/update'); - - $view->builds = $builds; - $this->view->timeline = $view->render(); - - $this->response->disableLayout(); - $this->response->setContent($this->view->render()); - - return $this->response; - } - - /** - * @return Response - */ - public function update() - { - $builds = $this->buildStore->getLatestBuilds(null, 10); - - foreach ($builds as &$build) { - $build = BuildFactory::getBuild($build); - } - - $this->view->builds = $builds; - - $this->response->disableLayout(); - $this->response->setContent($this->view->render()); - - return $this->response; - } -} diff --git a/src/PHPCensor/Helper/AnsiConverter.php b/src/PHPCensor/Helper/AnsiConverter.php deleted file mode 100644 index 709801e..0000000 --- a/src/PHPCensor/Helper/AnsiConverter.php +++ /dev/null @@ -1,46 +0,0 @@ -convert($text); - } - - /** - * Do not instantiate this class. - */ - private function __construct() - { - } -} diff --git a/src/PHPCensor/Helper/Bitbucket.php b/src/PHPCensor/Helper/Bitbucket.php deleted file mode 100644 index e585816..0000000 --- a/src/PHPCensor/Helper/Bitbucket.php +++ /dev/null @@ -1,108 +0,0 @@ -get('php-censor.bitbucket.username'); - $appPassword = Config::getInstance()->get('php-censor.bitbucket.app_password'); - - if (empty($username) || empty($appPassword)) { - return; - } - - $url = '/1.0/repositories/' . $repo . '/pullrequests/' . $pullId . '/comments/'; - $client = new Client(['base_uri' => 'https://api.bitbucket.org']); - $response = $client->post($url, [ - 'auth' => [$username, $appPassword], - 'headers' => [ - 'Content-Type' => 'application/json', - ], - 'json' => [ - 'content' => $comment, - 'anchor' => substr($commitId, 0, 12), - 'filename' => $file, - 'line_to' => $line, - ], - ]); - } - - /** - * Create a comment on a Bitbucket commit. - * - * @param $repo - * @param $commitId - * @param $file - * @param $line - * @param $comment - * @return null - */ - public function createCommitComment($repo, $commitId, $file, $line, $comment) - { - $username = Config::getInstance()->get('php-censor.bitbucket.username'); - $appPassword = Config::getInstance()->get('php-censor.bitbucket.app_password'); - - if (empty($username) || empty($appPassword)) { - return; - } - - $url = '/1.0/repositories/' . $repo . '/changesets/' . $commitId . '/comments'; - - $client = new Client(['base_uri' => 'https://api.bitbucket.org']); - $response = $client->post($url, [ - 'auth' => [$username, $appPassword], - 'headers' => [ - 'Content-Type' => 'application/json', - ], - 'json' => [ - 'content' => $comment, - 'filename' => $file, - 'line_to' => $line, - ], - ]); - } - - /** - * @param string $repo - * @param int $pullRequestId - * - * @return string - */ - public function getPullRequestDiff($repo, $pullRequestId) - { - $username = Config::getInstance()->get('php-censor.bitbucket.username'); - $appPassword = Config::getInstance()->get('php-censor.bitbucket.app_password'); - - if (empty($username) || empty($appPassword)) { - return; - } - - $url = '/2.0/repositories/' . $repo . '/pullrequests/' . $pullRequestId . '/diff'; - - $client = new Client(['base_uri' => 'https://api.bitbucket.org']); - - $response = $client->get($url, ['auth' => [$username, $appPassword]]); - - return (string)$response->getBody(); - } -} diff --git a/src/PHPCensor/Helper/Build.php b/src/PHPCensor/Helper/Build.php deleted file mode 100644 index 6484437..0000000 --- a/src/PHPCensor/Helper/Build.php +++ /dev/null @@ -1,21 +0,0 @@ - - */ -class Build -{ - /** - * Returns a more human-friendly version of a plugin name. - * @param $name - * @return mixed - */ - public function formatPluginName($name) - { - return str_replace('Php', 'PHP', ucwords(str_replace('_', ' ', $name))); - } -} diff --git a/src/PHPCensor/Helper/BuildInterpolator.php b/src/PHPCensor/Helper/BuildInterpolator.php deleted file mode 100644 index f645805..0000000 --- a/src/PHPCensor/Helper/BuildInterpolator.php +++ /dev/null @@ -1,85 +0,0 @@ - value pairs that will be used for - * interpolation and environment variables - * @var mixed[] - * @see setupInterpolationVars() - */ - protected $interpolation_vars = []; - - /** - * Sets the variables that will be used for interpolation. - * - * @param BaseBuild $build - * @param string $buildPath - * @param string $url - */ - public function setupInterpolationVars(BaseBuild $build, $buildPath, $url) - { - $this->interpolation_vars = []; - $this->interpolation_vars['%PHPCI%'] = 1; - $this->interpolation_vars['%COMMIT%'] = $build->getCommitId(); - $this->interpolation_vars['%SHORT_COMMIT%'] = substr($build->getCommitId(), 0, 7); - $this->interpolation_vars['%COMMIT_EMAIL%'] = $build->getCommitterEmail(); - $this->interpolation_vars['%COMMIT_MESSAGE%'] = $build->getCommitMessage(); - $this->interpolation_vars['%COMMIT_URI%'] = $build->getCommitLink(); - $this->interpolation_vars['%BRANCH%'] = $build->getBranch(); - $this->interpolation_vars['%BRANCH_URI%'] = $build->getBranchLink(); - $this->interpolation_vars['%ENVIRONMENT%'] = $build->getEnvironment(); - $this->interpolation_vars['%PROJECT%'] = $build->getProjectId(); - $this->interpolation_vars['%BUILD%'] = $build->getId(); - $this->interpolation_vars['%PROJECT_TITLE%'] = $build->getProjectTitle(); - $this->interpolation_vars['%PROJECT_URI%'] = $url . "project/view/" . $build->getProjectId(); - $this->interpolation_vars['%BUILD_PATH%'] = $buildPath; - $this->interpolation_vars['%BUILD_URI%'] = $url . "build/view/" . $build->getId(); - $this->interpolation_vars['%PHPCI_COMMIT%'] = $this->interpolation_vars['%COMMIT%']; - $this->interpolation_vars['%PHPCI_SHORT_COMMIT%'] = $this->interpolation_vars['%SHORT_COMMIT%']; - $this->interpolation_vars['%PHPCI_COMMIT_MESSAGE%'] = $this->interpolation_vars['%COMMIT_MESSAGE%']; - $this->interpolation_vars['%PHPCI_COMMIT_EMAIL%'] = $this->interpolation_vars['%COMMIT_EMAIL%']; - $this->interpolation_vars['%PHPCI_COMMIT_URI%'] = $this->interpolation_vars['%COMMIT_URI%']; - $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_PROJECT_URI%'] = $this->interpolation_vars['%PROJECT_URI%']; - $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_SHORT_COMMIT=' . $this->interpolation_vars['%SHORT_COMMIT%']); - putenv('PHPCI_COMMIT_MESSAGE=' . $this->interpolation_vars['%COMMIT_MESSAGE%']); - putenv('PHPCI_COMMIT_EMAIL=' . $this->interpolation_vars['%COMMIT_EMAIL%']); - putenv('PHPCI_COMMIT_URI=' . $this->interpolation_vars['%COMMIT_URI%']); - 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_ENVIRONMENT=' . $this->interpolation_vars['%ENVIRONMENT%']); - } - - /** - * Replace every occurrence of the interpolation vars in the given string - * Example: "This is build %PHPCI_BUILD%" => "This is build 182" - * @param string $input - * @return string - */ - public function interpolate($input) - { - $keys = array_keys($this->interpolation_vars); - $values = array_values($this->interpolation_vars); - return str_replace($keys, $values, $input); - } -} diff --git a/src/PHPCensor/Helper/CommandExecutor.php b/src/PHPCensor/Helper/CommandExecutor.php deleted file mode 100644 index b616606..0000000 --- a/src/PHPCensor/Helper/CommandExecutor.php +++ /dev/null @@ -1,343 +0,0 @@ -logger = $logger; - $this->quiet = $quiet; - $this->verbose = $verbose; - $this->lastOutput = []; - $this->rootDir = $rootDir; - } - - /** - * Executes shell commands. - * - * @param array $args - * - * @return bool Indicates success - */ - public function executeCommand($args = []) - { - $this->lastOutput = []; - - $this->logger->logDebug('Args: ' . json_encode($args)); - - $command = call_user_func_array('sprintf', $args); - - $this->logger->logDebug('Command: ' . $command); - - if ($this->quiet) { - $this->logger->log('Executing: ' . $command); - } - - $status = 0; - $descriptorSpec = [ - 0 => ["pipe", "r"], // stdin - 1 => ["pipe", "w"], // stdout - 2 => ["pipe", "w"], // stderr - ]; - - $pipes = []; - $process = proc_open($command, $descriptorSpec, $pipes, $this->buildPath, null); - - $this->lastOutput = ''; - $this->lastError = ''; - - if (is_resource($process)) { - fclose($pipes[0]); - - list($this->lastOutput, $this->lastError) = $this->readAlternating([$pipes[1], $pipes[2]]); - - $status = proc_close($process); - } - - mb_substitute_character(65533); - - $this->lastOutput = mb_convert_encoding($this->lastOutput, 'utf8', 'utf8'); - $this->lastOutput = array_filter(explode(PHP_EOL, $this->lastOutput)); - - $shouldOutput = ($this->logExecOutput && ($this->verbose || $status != 0)); - - if ($shouldOutput && !empty($this->lastOutput)) { - $this->logger->log($this->lastOutput); - } - - if (!empty($this->lastError)) { - $this->logger->log("\033[0;31m" . $this->lastError . "\033[0m", LogLevel::ERROR); - } - - $rtn = false; - if ($status == 0) { - $rtn = true; - } - - $this->logger->logDebug('Execution status: ' . $status); - - return $rtn; - } - - /** - * Reads from array of streams as data becomes available. - * - * @param array $descriptors - * - * @return string[] data read from each descriptor - */ - private function readAlternating(array $descriptors) - { - $outputs = []; - foreach ($descriptors as $key => $descriptor) { - stream_set_blocking($descriptor, false); - $outputs[$key] = ''; - } - do { - $read = $descriptors; - $write = null; - $except = null; - stream_select($read, $write, $except, null); - foreach ($read as $descriptor) { - $key = array_search($descriptor, $descriptors); - if (feof($descriptor)) { - fclose($descriptor); - unset($descriptors[$key]); - } else { - $outputs[$key] .= fgets($descriptor); - } - } - } while (count($descriptors) > 0); - return $outputs; - } - - /** - * Returns the output from the last command run. - * - * @return string - */ - public function getLastOutput() - { - return implode(PHP_EOL, $this->lastOutput); - } - - /** - * Returns the stderr output from the last command run. - * - * @return string - */ - public function getLastError() - { - return $this->lastError; - } - - /** - * @param string $composerBin - * @param string $binary - * - * @return false|string - */ - protected function findBinaryLocal($composerBin, $binary) - { - if (is_dir($composerBin) && is_file($composerBin . '/' . $binary)) { - $this->logger->logDebug(sprintf('Found in %s (local): %s', $composerBin, $binary)); - - return $composerBin . '/' . $binary; - } - - return false; - } - - /** - * @param string $binary - * - * @return false|string - */ - protected function findBinaryGlobal($binary) - { - if (is_file($this->rootDir . 'vendor/bin/' . $binary)) { - $this->logger->logDebug(sprintf('Found in %s (global): %s', 'vendor/bin', $binary)); - - return $this->rootDir . 'vendor/bin/' . $binary; - } - - return false; - } - - /** - * Uses 'which' to find a system binary by name - * - * @param string $binary - * - * @return false|string - */ - protected function findBinarySystem($binary) - { - $tempBinary = trim(shell_exec('which ' . $binary)); - if (is_file($tempBinary)) { - $this->logger->logDebug(sprintf('Found in %s (system): %s', '', $binary)); - - return $tempBinary; - } - - return false; - } - - /** - * Find a binary required by a plugin. - * - * @param string $binary - * @param bool $quiet Returns null instead of throwing an exception. - * @param string $priorityPath - * - * @return null|string - * - * @throws \Exception when no binary has been found and $quiet is false. - */ - public function findBinary($binary, $quiet = false, $priorityPath = 'local') - { - $composerBin = $this->getComposerBinDir(realpath($this->buildPath)); - - if (is_string($binary)) { - $binary = [$binary]; - } - - foreach ($binary as $bin) { - $this->logger->logDebug(sprintf('Looking for binary: %s', $bin)); - - if ('system' === $priorityPath) { - if ($binarySystem = $this->findBinarySystem($bin)) { - return $binarySystem; - } - - if ($binaryLocal = $this->findBinaryLocal($composerBin, $bin)) { - return $binaryLocal; - } - - if ($binaryGlobal = $this->findBinaryGlobal($bin)) { - return $binaryGlobal; - } - } elseif ('global' === $priorityPath) { - if ($binaryGlobal = $this->findBinaryGlobal($bin)) { - return $binaryGlobal; - } - - if ($binaryLocal = $this->findBinaryLocal($composerBin, $bin)) { - return $binaryLocal; - } - - if ($binarySystem = $this->findBinarySystem($bin)) { - return $binarySystem; - } - } else { - if ($binaryLocal = $this->findBinaryLocal($composerBin, $bin)) { - return $binaryLocal; - } - - if ($binaryGlobal = $this->findBinaryGlobal($bin)) { - return $binaryGlobal; - } - - if ($binarySystem = $this->findBinarySystem($bin)) { - return $binarySystem; - } - } - } - - if ($quiet) { - return null; - } - - throw new Exception(sprintf('Could not find %s', implode('/', $binary))); - } - - /** - * Try to load the composer.json file in the building project - * If the bin-dir is configured, return the full path to it - * - * @param string $path Current build path - * - * @return string|null - */ - public function getComposerBinDir($path) - { - if (is_dir($path)) { - $composer = $path . DIRECTORY_SEPARATOR . 'composer.json'; - if (is_file($composer)) { - $json = json_decode(file_get_contents($composer)); - - if (isset($json->config->{"bin-dir"})) { - return $path . DIRECTORY_SEPARATOR . $json->config->{"bin-dir"}; - } elseif (is_dir($path . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'bin')) { - return $path . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'bin'; - } - } - } - return null; - } - - /** - * Set the buildPath property. - * - * @param string $path - */ - public function setBuildPath($path) - { - $this->buildPath = $path; - } -} diff --git a/src/PHPCensor/Helper/CommandExecutorInterface.php b/src/PHPCensor/Helper/CommandExecutorInterface.php deleted file mode 100644 index 63a47b0..0000000 --- a/src/PHPCensor/Helper/CommandExecutorInterface.php +++ /dev/null @@ -1,43 +0,0 @@ -'; - - protected $emailTo = []; - protected $emailCc = []; - protected $subject = 'Email from PHP Censor'; - protected $body = ''; - protected $isHtml = false; - protected $config; - - /** - * Create a new email object. - */ - public function __construct() - { - $this->config = Config::getInstance(); - } - - /** - * Set the email's To: header. - * @param string $email - * @param string|null $name - * @return $this - */ - public function setEmailTo($email, $name = null) - { - $this->emailTo[$email] = $name; - - return $this; - } - - /** - * Add an address to the email's CC header. - * @param string $email - * @param string|null $name - * @return $this - */ - public function addCc($email, $name = null) - { - $this->emailCc[$email] = $name; - - return $this; - } - - /** - * Set the email subject. - * @param string $subject - * @return $this - */ - public function setSubject($subject) - { - $this->subject = $subject; - - return $this; - } - - /** - * Set the email body. - * @param string $body - * @return $this - */ - public function setBody($body) - { - $this->body = $body; - - return $this; - } - - /** - * Set whether or not the email body is HTML. - * @param bool $isHtml - * @return $this - */ - public function setHtml($isHtml = false) - { - $this->isHtml = $isHtml; - - return $this; - } - - /** - * Get the from address to use for the email. - * @return mixed|string - */ - protected function getFrom() - { - $from = $this->config->get( - 'php-censor.email_settings.from_address', - self::DEFAULT_FROM - ); - - if (strpos($from, '<') === false) { - return (string)$from; - } - - preg_match('#^(.*?)<(.*)>$#ui', $from, $fromParts); - - return [$fromParts[2] => $fromParts[1]]; - } - - /** - * Send the email. - * - * @param Builder $builder - * - * @return integer - */ - public function send(Builder $builder = null) - { - $smtpServer = $this->config->get('php-censor.email_settings.smtp_address'); - if (null !== $builder) { - $builder->logDebug(sprintf("SMTP: '%s'", !empty($smtpServer) ? 'true' : 'false')); - } - - $factory = new MailerFactory($this->config->get('php-censor')); - $mailer = $factory->getSwiftMailerFromConfig(); - - $message = \Swift_Message::newInstance($this->subject) - ->setFrom($this->getFrom()) - ->setTo($this->emailTo) - ->setBody($this->body); - - if ($this->isHtml) { - $message->setContentType('text/html'); - } - - if (is_array($this->emailCc) && count($this->emailCc)) { - $message->setCc($this->emailCc); - } - - return $mailer->send($message); - } -} diff --git a/src/PHPCensor/Helper/Github.php b/src/PHPCensor/Helper/Github.php deleted file mode 100644 index 3e19b84..0000000 --- a/src/PHPCensor/Helper/Github.php +++ /dev/null @@ -1,170 +0,0 @@ -get(('https://api.github.com' . $url), [ - 'query' => $params, - ]); - - $body = json_decode($response->getBody(), true); - $headers = $response->getHeaders(); - - foreach ($body as $item) { - $results[] = $item; - } - - foreach ($headers as $header_name => $header) { - if ( - 'Link' === $header_name && - preg_match('/^<([^>]+)>; rel="next"/', implode(', ', $header), $r) - ) { - $host = parse_url($r[1]); - - parse_str($host['query'], $params); - $results = $this->makeRecursiveRequest($host['path'], $params, $results); - - break; - } - } - - return $results; - } - - /** - * Get an array of repositories from Github's API. - */ - public function getRepositories() - { - $token = Config::getInstance()->get('php-censor.github.token'); - - if (!$token) { - return []; - } - - $cache = Cache::getCache(Cache::TYPE_APC); - $rtn = $cache->get('php-censor-github-repos'); - - if (!$rtn) { - $client = new Client(); - $response = $client->get('https://api.github.com/user/orgs', [ - 'query' => [ - 'access_token' => $token - ], - ]); - - $orgs = json_decode($response->getBody(), true); - - $params = ['type' => 'all', 'access_token' => $token]; - $repos = ['user' => []]; - $repos['user'] = $this->makeRecursiveRequest('/user/repos', $params); - - foreach ($orgs as $org) { - $repos[$org['login']] = $this->makeRecursiveRequest('/orgs/' . $org['login'] . '/repos', $params); - } - - $rtn = []; - foreach ($repos as $repoGroup) { - foreach ($repoGroup as $repo) { - $rtn['repos'][] = $repo['full_name']; - } - } - - $cache->set('php-censor-github-repos', $rtn); - } - - return $rtn; - } - - /** - * Create a comment on a specific file (and commit) in a Github Pull Request. - * @param $repo - * @param $pullId - * @param $commitId - * @param $file - * @param $line - * @param $comment - * @return null - */ - public function createPullRequestComment($repo, $pullId, $commitId, $file, $line, $comment) - { - $token = Config::getInstance()->get('php-censor.github.token'); - - if (!$token) { - return null; - } - - $url = '/repos/' . strtolower($repo) . '/pulls/' . $pullId . '/comments'; - - $params = [ - 'body' => $comment, - 'commit_id' => $commitId, - 'path' => $file, - 'position' => $line, - ]; - - $client = new Client(); - $client->post(('https://api.github.com' . $url), [ - 'headers' => [ - 'Authorization' => 'Basic ' . base64_encode($token . ':x-oauth-basic'), - 'Content-Type' => 'application/x-www-form-urlencoded' - ], - 'json' => $params, - ]); - } - - /** - * Create a comment on a Github commit. - * @param $repo - * @param $commitId - * @param $file - * @param $line - * @param $comment - * @return null - */ - public function createCommitComment($repo, $commitId, $file, $line, $comment) - { - $token = Config::getInstance()->get('php-censor.github.token'); - - if (!$token) { - return null; - } - - $url = '/repos/' . strtolower($repo) . '/commits/' . $commitId . '/comments'; - - $params = [ - 'body' => $comment, - 'path' => $file, - 'position' => $line, - ]; - - $client = new Client(); - $client->post(('https://api.github.com' . $url), [ - 'headers' => [ - 'Authorization' => 'Basic ' . base64_encode($token . ':x-oauth-basic'), - 'Content-Type' => 'application/x-www-form-urlencoded' - ], - 'json' => $params, - ]); - } -} diff --git a/src/PHPCensor/Helper/Lang.php b/src/PHPCensor/Helper/Lang.php deleted file mode 100644 index 1dbff3f..0000000 --- a/src/PHPCensor/Helper/Lang.php +++ /dev/null @@ -1,190 +0,0 @@ -getLanguage(); - if ($user && self::setLanguage($language)) { - return; - } - } - - // Try the installation default language: - $language = $config->get('php-censor.language', self::DEFAULT_LANGUAGE); - if (self::setLanguage($language)) { - return; - } - } - - /** - * Load a specific language file. - * - * @param string $language - * - * @return string[]|null - */ - protected static function loadLanguage($language = null) - { - $language = $language ? $language : self::$language; - $langFile = SRC_DIR . 'Languages' . DIRECTORY_SEPARATOR . 'lang.' . $language . '.php'; - - if (!file_exists($langFile)) { - return null; - } - - $strings = include($langFile); - if (is_null($strings) || !is_array($strings) || !count($strings)) { - return null; - } - - return $strings; - } - - /** - * Load the names of all available languages. - */ - protected static function loadAvailableLanguages() - { - $matches = []; - foreach (glob(SRC_DIR . 'Languages' . DIRECTORY_SEPARATOR . 'lang.*.php') as $file) { - if (preg_match('/lang\.([a-z]{2}\-?[a-z]*)\.php/', $file, $matches)) { - self::$languages[] = $matches[1]; - } - } - } -} diff --git a/src/PHPCensor/Helper/LoginIsDisabled.php b/src/PHPCensor/Helper/LoginIsDisabled.php deleted file mode 100644 index 70ed348..0000000 --- a/src/PHPCensor/Helper/LoginIsDisabled.php +++ /dev/null @@ -1,31 +0,0 @@ - - */ -class LoginIsDisabled -{ - /** - * Checks if - * - * @param $method - * @param array $params - * - * @return mixed|null - */ - public function __call($method, $params = []) - { - unset($method, $params); - - $config = Config::getInstance(); - $disableAuth = (boolean)$config->get('php-censor.security.disable_auth', false); - - return $disableAuth; - } -} diff --git a/src/PHPCensor/Helper/MailerFactory.php b/src/PHPCensor/Helper/MailerFactory.php deleted file mode 100644 index c6910b0..0000000 --- a/src/PHPCensor/Helper/MailerFactory.php +++ /dev/null @@ -1,85 +0,0 @@ -emailConfig = isset($config['email_settings']) ? $config['email_settings'] : []; - } - - /** - * Returns an instance of Swift_Mailer based on the config.s - * @return \Swift_Mailer - */ - public function getSwiftMailerFromConfig() - { - if ($this->getMailConfig('smtp_address')) { - $encryptionType = $this->getMailConfig('smtp_encryption'); - - // Workaround issue where smtp_encryption could == 1 in the past by - // checking it is a valid transport - if ($encryptionType && !in_array($encryptionType, stream_get_transports())) { - $encryptionType = null; - } - - /** @var \Swift_SmtpTransport $transport */ - $transport = \Swift_SmtpTransport::newInstance( - $this->getMailConfig('smtp_address'), - $this->getMailConfig('smtp_port'), - $encryptionType - ); - - $transport->setUsername($this->getMailConfig('smtp_username')); - $transport->setPassword($this->getMailConfig('smtp_password')); - } else { - $transport = \Swift_MailTransport::newInstance(null); - } - - return \Swift_Mailer::newInstance($transport); - } - - /** - * Return a specific configuration value by key. - * @param $configName - * @return null|string - */ - public function getMailConfig($configName) - { - if (isset($this->emailConfig[$configName]) && $this->emailConfig[$configName] != "") { - return $this->emailConfig[$configName]; - } else { - // Check defaults - - switch ($configName) { - case 'smtp_address': - return ""; - case 'default_mailto_address': - return null; - case 'smtp_port': - return '25'; - case 'smtp_encryption': - return null; - default: - return ""; - } - } - } -} diff --git a/src/PHPCensor/Helper/SshKey.php b/src/PHPCensor/Helper/SshKey.php deleted file mode 100644 index 86e30cc..0000000 --- a/src/PHPCensor/Helper/SshKey.php +++ /dev/null @@ -1,42 +0,0 @@ - '', 'public_key' => '']; - - $output = @shell_exec('ssh-keygen -t rsa -b 2048 -f '.$keyFile.' -N "" -C "deploy@php-censor"'); - - if (!empty($output)) { - $pub = file_get_contents($keyFile . '.pub'); - $prv = file_get_contents($keyFile); - - if (!empty($pub)) { - $return['public_key'] = $pub; - } - - if (!empty($prv)) { - $return['private_key'] = $prv; - } - } - - return $return; - } -} diff --git a/src/PHPCensor/Helper/User.php b/src/PHPCensor/Helper/User.php deleted file mode 100644 index 6ca3d25..0000000 --- a/src/PHPCensor/Helper/User.php +++ /dev/null @@ -1,32 +0,0 @@ - - */ -class User -{ - /** - * Proxies method calls through to the current active user model. - * @param $method - * @param array $params - * @return mixed|null - */ - public function __call($method, $params = []) - { - if (empty($_SESSION['php-censor-user'])) { - return null; - } - - $user = $_SESSION['php-censor-user']; - - if (!is_object($user)) { - return null; - } - - return call_user_func_array([$user, $method], $params); - } -} diff --git a/src/PHPCensor/Languages/lang.da.php b/src/PHPCensor/Languages/lang.da.php deleted file mode 100644 index dfea758..0000000 --- a/src/PHPCensor/Languages/lang.da.php +++ /dev/null @@ -1,289 +0,0 @@ - 'Dansk', - 'language' => 'Sprog', - - // Log in: - 'log_in_to_app' => 'Log ind i PHP Censor', - 'login_error' => 'Forkert email-adresse eller adgangskode', - 'forgotten_password_link' => 'Har du glemt din adgangskode?', - 'reset_emailed' => 'Vi har sendt dig en email med et link til at nulstille din adgangskode.', - 'reset_header' => 'Bare rolig!
    Indtast blot din email-adresse, så sender -vi dig et link til at nulstille din adgangskode.', - 'reset_email_address' => 'Indtast din email-adresse:', - 'reset_send_email' => 'Send nulstillings-link', - 'reset_enter_password' => 'Indtast venligst en ny adgangskode', - 'reset_new_password' => 'Ny adgangskode:', - 'reset_change_password' => 'Skift adgangskode', - 'reset_no_user_exists' => 'Der findes ingen bruger med den email-adresse, prøv igen.', - 'reset_email_body' => 'Hej %s, - -Du modtager denne email fordi du eller en anden person har anmodet om at nulstille din adgangskode til PHP Censor. - -Hvis det var dig kan du klikke følgende link for at nulstille din adgangskode: %ssession/reset-password/%d/%s - -Hvis det ikke var dig kan du ignorere denne email og intet vil ske. - -Tak, - -PHP Censor', - - 'reset_email_title' => 'PHP Censor 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', - - - // Top Nav - 'toggle_navigation' => 'Vis/skjul navigation', - 'n_builds_pending' => '%d builds i køen', - 'n_builds_running' => '%d builds kører', - 'edit_profile' => 'Redigér profil', - 'sign_out' => 'Log ud', - 'branch_x' => 'Branch: %s', - 'created_x' => 'Oprettet: %s', - 'started_x' => 'Startet: %s', - - // Sidebar - 'hello_name' => 'Hej %s', - 'dashboard' => 'Dashboard', - 'admin_options' => 'Administrator-indstillinger', - 'add_project' => 'Tilføj projekt', - 'settings' => 'Indstillinger', - 'manage_users' => 'Administrér brugere', - 'plugins' => 'Plugins', - 'view' => 'Vis', - 'build_now' => 'Start build nu', - 'edit_project' => 'Redigér projekt', - 'delete_project' => 'Slet projekt', - - // Project Summary: - 'no_builds_yet' => 'Ingen builds pt.!', - 'x_of_x_failed' => '%d af de sidste %d builds fejlede.', - 'x_of_x_failed_short' => '%d / %d fejlede.', - 'last_successful_build' => 'Sidste succesfulde build var %s.', - 'never_built_successfully' => 'Dette projekt har indtil videre ingen succesfulde builds.', - 'all_builds_passed' => 'All de sidste %d builds fejlede.', - 'all_builds_passed_short' => '%d / %d lykkedes.', - 'last_failed_build' => 'Det sidste mislykkede build var %s', - 'never_failed_build' => 'Dette projekt er endnu ikke blevet kørt.', - 'view_project' => 'Vis Projekt', - - // Timeline: - 'latest_builds' => 'Nyeste Builds', - 'pending' => 'Venter', - 'running' => 'Kører', - 'success' => 'Succes', - 'failed' => 'Fejlede', - 'manual_build' => 'Manuelt Build', - - // Add/Edit Project: - 'new_project' => 'Nyt Projekt', - 'project_x_not_found' => 'Projektet med ID %d findes ikke.', - 'project_details' => 'Projekt-detaljer', - 'public_key_help' => 'For at gøre det lettere at starte har vi genereret en SSH-nøgle som du kan bruge til dette projekt. For at bruge den behøver du blot tilføje den følgende public key til "deployment keys" sektionen -i din foretrukne hosting-platform.', - 'select_repository_type' => 'Vælg repository-type...', - 'github' => 'GitHub', - 'bitbucket' => 'Bitbucket', - 'gitlab' => 'GitLab', - 'remote' => 'Ekstern URL', - 'local' => 'Lokalt filsystem', - 'hg' => 'Mercurial', - - 'where_hosted' => 'Hvor er dit projekt hosted?', - 'choose_github' => 'Vælg et GitHub-repository:', - - 'repo_name' => 'Repository-navn / URL (ekstern) eller filsystem-sti (lokal)', - 'project_title' => 'Projekt-titel', - 'project_private_key' => 'Privat nøgle med adgang til dette repository -(tom for lokal nøgle og/eller anonym adgang)', - 'build_config' => 'PHP Censor build-konfiguration for dette projekt -(hvis du ikke har mulighed for at tilføje en .php-censor.yml (.phpci.yml|phpci.yml) fil i projektets repository)', - 'default_branch' => 'Default branch navn', - 'allow_public_status' => 'Tillad offentlig status-side og -billede for dette projekt?', - 'archived' => 'Archived', - 'archived_menu' => 'Archived', - 'save_project' => 'Gem Projekt', - - 'error_mercurial' => 'Mercurial repository-URL skal starte med http:// eller https://', - 'error_remote' => 'Repository-URL skal starte med git://, http:// eller https://', - 'error_gitlab' => 'GitLab repository-navn skal være i formatet "user@domæne.tld:ejernavn/repositorynavn.git"', - 'error_github' => 'Repository-navn skal være i formatet "ejernavn/repositorynavn"', - 'error_bitbucket' => 'Repository-navn skal være i formatet "ejernavn/repositorynavn"', - 'error_path' => 'Stien du indtastede findes ikke.', - - // View Project: - 'all_branches' => 'Alle branches', - 'builds' => 'Builds', - 'id' => 'ID', - 'date' => 'Date', - 'project' => 'Projekt', - 'commit' => 'Commit', - 'branch' => 'Branch', - 'status' => 'Status', - 'prev_link' => '« Forrige', - 'next_link' => 'Næste »', - 'public_key' => 'Offentlig nøgle', - 'delete_build' => 'Slet Build', - - 'webhooks' => 'Webhooks', - 'webhooks_help_github' => 'For at køre dette build automatisk når nye commits bliver pushed skal du tilføje nedenstående -URL som nyt "Webhook" i Webhooks -and Services under dit GitHub-repository.', - - 'webhooks_help_gitlab' => 'For at køre dette build automatisk når nye commits bliver pushed kan du tilføje nedenstående URL -som en "WebHook URL" i Web Hooks-sektionen i dit GitLab-repository.', - - 'webhooks_help_bitbucket' => 'For at køre dette build automatisk når nye commits bliver pushed skal du tilføje nedenstående -URL som "POST" service i - -Services sektionen under dit Bitbucket-repository.', - - // View Build - 'build_x_not_found' => 'Build med ID %d findes ikke.', - 'build_n' => 'Build %d', - 'rebuild_now' => 'Gentag Build', - - - 'committed_by_x' => 'Committed af %s', - 'commit_id_x' => 'Commit: %s', - - 'chart_display' => 'Denne graf vises når buildet er færdigt.', - - 'build' => 'Build', - 'lines' => 'Linjer', - 'comment_lines' => 'Kommentar-linjer', - 'noncomment_lines' => 'Ikke-kommentar-linjer', - 'logical_lines' => 'Logiske linjer', - 'lines_of_code' => 'Kode-linjer', - 'build_log' => 'Build-log', - 'quality_trend' => 'Kvalitets-trend', - 'codeception_errors' => 'Codeception-fejl', - 'phpmd_warnings' => 'PHPMD-advarsler', - 'phpcs_warnings' => 'PHPCS-advarsler', - 'phpcs_errors' => 'PHPCS-fejl', - 'phplint_errors' => 'Lint-fejl', - 'phpunit_errors' => 'PHPUnit-fejl', - 'phpdoccheck_warnings' => 'Manglende Docblocks', - 'issues' => 'Sager', - - 'codeception' => 'Codeception', - 'phpcpd' => 'PHP Copy/Paste Detector', - 'phpcs' => 'PHP Code Sniffer', - 'phpdoccheck' => 'Manglende Docblocks', - 'phpmd' => 'PHP Mess Detector', - 'phpspec' => 'PHP Spec', - 'phpunit' => 'PHP Unit', - - 'file' => 'Fil', - 'line' => 'Linje', - 'class' => 'Klasse', - 'method' => 'Funktion', - 'message' => 'Besked', - 'start' => 'Start', - 'end' => 'Slut', - 'from' => 'Fra', - 'to' => 'Til', - 'result' => 'Resultat', - 'ok' => 'OK', - 'took_n_seconds' => 'Tog %d sekunder', - 'build_started' => 'Build Startet', - 'build_finished' => 'Build Afsluttet', - 'test_message' => 'Message', - 'test_no_message' => 'No message', - 'test_success' => 'Successful: %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', - 'password_change' => 'Adgangskode (tom hvis du ikke ønsker at ændre koden)', - 'save' => 'Gem »', - 'update_your_details' => 'Opdatér oplysninger', - 'your_details_updated' => 'Dine oplysninger blev gemt.', - 'add_user' => 'Tilføj bruger', - 'is_admin' => 'Administrator?', - 'yes' => 'Ja', - 'no' => 'Nej', - 'edit' => 'Redigér', - 'edit_user' => 'Redigér Bruger', - 'delete_user' => 'Slet Bruger', - 'user_n_not_found' => 'Brugeren med ID %d findes ikke.', - 'is_user_admin' => 'Er denne bruger en administrator?', - 'save_user' => 'Gem Bruger', - - // Settings: - 'settings_saved' => 'Dine indstillinger blev gemt.', - 'settings_check_perms' => 'Dine indstillinger kunne ikke gemmes, kontrollér rettighederne på din config.yml fil.', - 'settings_cannot_write' => 'PHP Censor kan ikke skrive til din config.yml fil, indstillinger bliver muligvis ikke gemt korrekt før dette problem løses.', - 'settings_github_linked' => 'Din GitHub-konto er nu tilsluttet.', - 'settings_github_not_linked' => 'Din GitHub-konto kunne ikke tilsluttes.', - 'build_settings' => 'Build-indstillinger', - 'github_application' => 'GitHub-applikation', - 'github_sign_in' => 'Før du kan bruge GitHub skal du logge ind og give PHP Censor -adgang til din konto.', - 'github_app_linked' => 'PHP Censor blev tilsluttet din GitHub-konto.', - 'github_where_to_find' => 'Hvor disse findes...', - 'github_where_help' => 'Hvis du ejer applikationen du ønsker at bruge kan du finde denne information i -applications under indstillinger.', - - 'email_settings' => 'Email-indstillinger', - 'email_settings_help' => 'Før PHP Censor kan sende build-notifikationer via email -skal du konfigurere nedenstående SMTP-indstillinger.', - - 'application_id' => 'Application ID', - 'application_secret' => 'Application Secret', - - 'smtp_server' => 'SMTP-server', - 'smtp_port' => 'SMTP-port', - 'smtp_username' => 'SMTP-brugernavn', - 'smtp_password' => 'SMTP-adgangskode', - 'from_email_address' => 'Fra email-adresse', - 'default_notification_address' => 'Default notifikations-email-adresse', - 'use_smtp_encryption' => 'Brug SMTP-kryptering?', - 'none' => 'Ingen', - 'ssl' => 'SSL', - 'tls' => 'TLS', - - 'failed_after' => 'Betragt et build som fejlet efter', - '5_mins' => '5 minutter', - '15_mins' => '15 minutter', - '30_mins' => '30 minutter', - '1_hour' => '1 time', - '3_hours' => '3 timer', - - // Plugins - 'cannot_update_composer' => 'PHP Censor kan ikke opdatere composer.json da filen ikke kan skrives.', - 'x_has_been_removed' => '%s er blevet slettet.', - 'x_has_been_added' => '%s blev tilføjet til composer.json for dig og vil blive installeret næste gang -du kører composer update.', - 'enabled_plugins' => 'Aktive plugins', - 'provided_by_package' => 'Via pakke', - 'installed_packages' => 'Installerede pakker', - 'suggested_packages' => 'Forslag til pakker', - 'title' => 'Titel', - 'description' => 'Beskrivelse', - 'version' => 'Version', - 'install' => 'Installér »', - 'remove' => 'Fjern »', - 'search_packagist_for_more' => 'Søg på Packagist efter flere pakker', - 'search' => 'Søg »', - - // Update - 'update_app' => 'Opdatér databasen med ændrede modeller', - 'updating_app' => 'Opdaterer PHP Censor-database:', - 'not_installed' => 'PHP Censor lader til ikke at være installeret.', - 'install_instead' => 'Installér venligst PHP Censor via php-censor:install istedet.', - - // Build Plugins: - 'passing_build' => 'Succesfuldt Build', - 'failing_build' => 'Fejlet Build', - 'log_output' => 'Log-output:', -]; diff --git a/src/PHPCensor/Languages/lang.de.php b/src/PHPCensor/Languages/lang.de.php deleted file mode 100644 index 270bfbe..0000000 --- a/src/PHPCensor/Languages/lang.de.php +++ /dev/null @@ -1,325 +0,0 @@ - 'Deutsch', - 'language' => 'Sprache', - - // Log in: - 'log_in_to_app' => 'In PHP Censor einloggen', - 'login_error' => 'Fehlerhafte Emailadresse oder fehlerhaftes Passwort', - 'forgotten_password_link' => 'Passwort vergessen?', - 'reset_emailed' => 'Wir haben Ihnen einen Link geschickt, um Ihr Passwort zurückzusetzen', - 'reset_header' => 'Keine Panik!
    Geben Sie einfach unten Ihre Emailadresse an - und wir senden Ihnen einen Link, um Ihr Passwort zurückzusetzen', - 'reset_email_address' => 'Geben Sie Ihre Emailadresse an:', - 'reset_send_email' => 'Link senden', - 'reset_enter_password' => 'Bitte geben Sie ein neues Passwort ein', - 'reset_new_password' => 'Neues Passwort:', - 'reset_change_password' => 'Passwort ändern', - 'reset_no_user_exists' => 'Es existiert kein User mit dieser Emailadresse, versuchen Sie es bitte noch einmal.', - 'reset_email_body' => 'Hallo %s, - -Sie haben diese Email erhalten, weil Sie, oder jemand anders, einen Link zum Zurücksetzen Ihres Passwortes für PHP Censor verlangt hat. - -Wenn Sie diesen Link verlangt haben, klicken Sie bitte hier, um Ihr Passwort zurückzusetzen: %ssession/reset-password/%d/%s - -Falls nicht, ignorieren Sie diese Email bitte, und es wird nichts geändert. - -Danke, - -PHP Censor', - - 'reset_email_title' => 'PHP Censor Passwort zurücksetzen für %s', - 'reset_invalid' => 'Fehlerhafte Anfrage für das Zurücksetzen eines Passwortes', - 'email_address' => 'Emailadresse', - 'login' => 'Login / Emailadresse', - 'password' => 'Passwort', - 'log_in' => 'Einloggen', - - - // Top Nav - 'toggle_navigation' => 'Navigation umschalten', - 'n_builds_pending' => '%d Builds ausstehend', - 'n_builds_running' => '%d Builds werden ausgeführt', - 'edit_profile' => 'Profil bearbeiten', - 'sign_out' => 'Ausloggen', - 'branch_x' => 'Branch: %s', - 'created_x' => 'Erstellt: %s', - 'started_x' => 'Gestartet: %s', - - // Sidebar - 'hello_name' => 'Hallo, %s', - 'dashboard' => 'Dashboard', - 'admin_options' => 'Administration', - 'add_project' => 'Projekt hinzufügen', - 'settings' => 'Einstellungen', - 'manage_users' => 'Benutzereinstellungen', - 'plugins' => 'Plugins', - 'view' => 'Ansehen', - 'build_now' => 'Jetzt bauen', - 'edit_project' => 'Projekt bearbeiten', - 'delete_project' => 'Projekt löschen', - - // Project Summary: - 'no_builds_yet' => 'Bisher noch keine Builds!', - 'x_of_x_failed' => '%d der letzten %d Builds sind fehlgeschlagen.', - 'x_of_x_failed_short' => '%d / %d fehlgeschlagen.', - 'last_successful_build' => ' Der letzte erfolgreiche Build war %s.', - 'never_built_successfully' => ' Dieses Projekt hatte bisher noch keinen erfolgreichen Build.', - 'all_builds_passed' => 'Jeder der letzten %d Builds war erfolgreich.', - 'all_builds_passed_short' => '%d / %d erfolgreich.', - 'last_failed_build' => ' Der letzte fehlgeschlagene Build war %s.', - 'never_failed_build' => ' Dieses Projekt hat keine fehlgeschlagenen Builds.', - 'view_project' => 'Projekt ansehen', - - // Timeline: - 'latest_builds' => 'Die neusten Builds', - 'pending' => 'Ausstehend', - 'running' => 'Wird ausgeführt', - 'success' => 'Erfolg', - 'failed' => 'Fehlgeschlagen', - 'manual_build' => 'Manueller Build', - - // Add/Edit Project: - 'new_project' => 'Neues Projekt', - 'project_x_not_found' => 'Projekt mit ID %d existiert nicht.', - 'project_details' => 'Projektdetails', - 'public_key_help' => 'Um Ihnen den Einstieg zu erleichtern, haben wir ein SSH-Key-Paar für dieses Projekt -generiert. Um es zu verwenden, fügen Sie einfach den folgenden Public Key im Abschnitt -"Deploy Keys" Ihrer bevorzugten Quellcodehostingplattform hinzu.', - 'select_repository_type' => 'Wählen Sie den Typ des Repositories...', - 'github' => 'GitHub', - 'bitbucket' => 'Bitbucket', - 'gitlab' => 'GitLab', - 'remote' => 'Externe URL', - 'local' => 'Lokaler Pfad', - 'hg' => 'Mercurial', - 'svn' => 'Subversion', - - 'where_hosted' => 'Wo wird Ihr Projekt gehostet?', - 'choose_github' => 'Wählen Sie ein GitHub Repository:', - - 'repo_name' => 'Name/URL (extern) oder Pfad (lokal) des Repositories', - 'project_title' => 'Projekttitel', - 'project_private_key' => 'Private Key für den Zugang zum Repository - (leer lassen für lokale und oder anonyme externe Zugriffe)', - 'build_config' => 'PHP Censor Buildkonfiguration für dieses Projekt - (falls Sie Ihrem Projektrepository kein .php-censor.yml (.phpci.yml|phpci.yml) hinzufügen können)', - 'default_branch' => 'Name des Standardbranches', - 'allow_public_status' => 'Öffentliche Statusseite und -bild für dieses Projekt einschalten?', - 'archived' => 'Archiviert', - 'archived_menu' => 'Archiviert', - 'save_project' => 'Projekt speichern', - - 'error_mercurial' => 'Mercurial Repository-URL muss mit http://, oder https:// beginnen', - 'error_remote' => 'Repository-URL muss mit git://, http://, oder https:// beginnen', - 'error_gitlab' => 'GitLab Repositoryname muss im Format "user@domain.tld:owner/repo.git" sein', - 'error_github' => 'Repositoryname muss im Format "besitzer/repo" sein', - 'error_bitbucket' => 'Repositoryname muss im Format "besitzer/repo" sein', - 'error_path' => 'Der angegebene Pfad existiert nicht', - - // View Project: - 'all_branches' => 'Alle Branches', - 'builds' => 'Builds', - 'id' => 'ID', - 'date' => 'Datum', - 'project' => 'Projekt', - 'commit' => 'Commit', - 'branch' => 'Branch', - 'status' => 'Status', - 'prev_link' => '« Vorherige', - 'next_link' => 'Nächste »', - 'public_key' => 'Public Key', - 'delete_build' => 'Build löschen', - - 'webhooks' => 'Webhooks', - 'webhooks_help_github' => 'Um für dieses Projekt automatisch einen Build zu starten, wenn neue Commits gepushed - werden, fügen Sie die untenstehende URL in der - Webhooks and Services-Sektion Ihres - GitHub Repositories als neuen "Webhook" hinzu.', - - 'webhooks_help_gitlab' => 'Um für dieses Projekt automatisch einen Build zu starten, wenn neue Commits gepushed werden, fügen Sie die untenstehende URL in der Web Hooks Sektion Ihres GitLab Repositories hinzu.', - - 'webhooks_help_bitbucket' => 'Um für dieses Projekt automatisch einen Build zu starten, wenn neue Commits gepushed werden, fügen Sie die untenstehende URL als "POST" Service in der Services-Sektion Ihres Bitbucket Repositories hinzu.', - - // View Build - 'errors' => 'Fehler', - 'information' => 'Information', - - 'build_x_not_found' => 'Build mit ID %d existiert nicht.', - 'build_n' => 'Build %d', - 'rebuild_now' => 'Build neu starten', - - - 'committed_by_x' => 'Committed von %s', - 'commit_id_x' => 'Commit: %s', - - 'chart_display' => 'Dieses Diagramm wird angezeigt, sobald der Build abgeschlossen ist.', - - 'build' => 'Build', - 'lines' => 'Zeilen', - 'comment_lines' => 'Kommentarzeilen', - 'noncomment_lines' => 'Nicht-Kommentarzeilen', - 'logical_lines' => 'Zeilen mit Logik', - 'lines_of_code' => 'Anzahl Codezeilen', - 'build_log' => 'Buildprotokoll', - 'quality_trend' => 'Qualitätstrend', - 'codeception_errors' => 'Codeception Errors', - 'phpmd_warnings' => 'PHPMD Warnings', - 'phpcs_warnings' => 'PHPCS Warnings', - 'phpcs_errors' => 'PHPCS Errors', - 'phplint_errors' => 'Lint Errors', - 'phpunit_errors' => 'PHPUnit Errors', - 'phpdoccheck_warnings' => 'Fehlende Docblocks', - 'issues' => 'Probleme', - - 'codeception' => 'Codeception', - 'phpcpd' => 'PHP Copy/Paste Detector', - 'phpcs' => 'PHP Code Sniffer', - 'phpdoccheck' => 'Fehlende Docblocks', - 'phpmd' => 'PHP Mess Detector', - 'phpspec' => 'PHP Spec', - 'phpunit' => 'PHP Unit', - 'technical_debt' => 'Technische Schulden', - 'behat' => 'Behat', - - 'codeception_feature' => 'Feature', - 'codeception_suite' => 'Suite', - 'codeception_time' => 'Zeit', - 'codeception_synopsis' => '%1$d Tests in %2$f Sekunden ausgeführt. - %3$d Fehler.', - - 'file' => 'Datei', - 'line' => 'Zeile', - 'class' => 'Klasse', - 'method' => 'Methode', - 'message' => 'Nachricht', - 'start' => 'Start', - 'end' => 'Ende', - 'from' => 'Von', - 'to' => 'Bis', - 'result' => 'Resultat', - 'ok' => 'OK', - 'took_n_seconds' => 'Benötigte %d Sekunden', - 'build_started' => 'Build gestartet', - 'build_finished' => 'Build abgeschlossen', - 'test_message' => 'Nachricht', - 'test_no_message' => 'Keine Nachricht', - 'test_success' => 'Erfolgreich: %d', - 'test_fail' => 'Fehlschläge: %d', - 'test_skipped' => 'Übersprungen: %d', - 'test_error' => 'Fehler: %d', - 'test_todo' => 'Todos: %d', - 'test_total' => '%d Test(s)', - - // Users - 'name' => 'Name', - 'password_change' => 'Passwort (leerlassen, wenn Sie es nicht ändern möchten)', - 'save' => 'Speichern »', - 'update_your_details' => 'Aktualisieren Sie Ihre Details', - 'your_details_updated' => 'Ihre Details wurden aktualisiert.', - 'add_user' => 'Benutzer hinzufügen', - 'is_admin' => 'Administrator?', - 'yes' => 'Ja', - 'no' => 'Nein', - 'edit' => 'Bearbeiten', - 'edit_user' => 'Benutzer bearbeiten', - 'delete_user' => 'Benutzer löschen', - 'user_n_not_found' => 'Benutzer mit ID %d existiert nicht.', - 'is_user_admin' => 'Ist dieser Benutzer Administrator?', - 'save_user' => 'Benutzer speichern', - - // Settings: - 'settings_saved' => 'Ihre Einstellungen wurden gespeichert.', - 'settings_check_perms' => 'Ihre Einstellungen konnten nicht gespeichert werden, bitte überprüfen Sie die - Berechtigungen Ihrer config.yml-Datei', - 'settings_cannot_write' => 'PHP Censor konnte config.yml nicht schreiben. Einstellungen könnten nicht richtig gespeichert werden, bis das Problem behoben ist.', - 'settings_github_linked' => 'Ihr GitHub-Konto wurde verknüpft.', - 'settings_github_not_linked' => 'Ihr GitHub-Konto konnte nicht verknüpft werden.', - 'build_settings' => 'Buildeinstellungen', - 'github_application' => 'GitHub-Applikation', - 'github_sign_in' => 'Bevor Sie anfangen GitHub zu verwenden, müssen Sie sich erst einloggen und PHP Censor Zugriff auf Ihr Nutzerkonto gewähren', - 'github_app_linked' => 'PHP Censor wurde erfolgreich mit Ihrem GitHub-Konto verknüpft.', - 'github_where_to_find' => 'Wo Sie diese finden...', - 'github_where_help' => 'Wenn Sie der Besitzer der Applikation sind, die Sie gerne verwenden möchten, können Sie - diese Einstellungen in Ihrem "applications - settings"-Bereich finden.', - - 'email_settings' => 'Emaileinstellungen', - 'email_settings_help' => 'Bevor PHP Censor E-Mails zum Buildstatus verschicken kann, - müssen Sie Ihre SMTP-Einstellungen unten konfigurieren', - - 'application_id' => 'Applikations-ID', - 'application_secret' => 'Applikations-Secret', - - 'smtp_server' => 'SMTP Server', - 'smtp_port' => 'SMTP Port', - 'smtp_username' => 'SMTP Benutzername', - 'smtp_password' => 'SMTP Passwort', - 'from_email_address' => 'Absenderadresse', - 'default_notification_address' => 'Standardadresse für Benachrichtigungen', - 'use_smtp_encryption' => 'SMTP-Verschlüsselung verwenden?', - 'none' => 'Keine', - 'ssl' => 'SSL', - 'tls' => 'TLS', - - 'failed_after' => 'Einen Build als fehlgeschlagen ansehen nach', - '5_mins' => '5 Minuten', - '15_mins' => '15 Minuten', - '30_mins' => '30 Minuten', - '1_hour' => '1 Stunde', - '3_hours' => '3 Stunden', - - // Plugins - 'cannot_update_composer' => 'PHP Censor kann composer.json nicht für Sie aktualisieren, da Schreibrechte benötigt werden.', - 'x_has_been_removed' => '%s wurde entfernt.', - 'x_has_been_added' => '%s wurde für Sie dem composer.json hinzugefügt und wird installiert, sobald Sie das nächste mal composer update ausführen.', - 'enabled_plugins' => 'Eingeschaltene Plugins', - 'provided_by_package' => 'Von Package bereitgestellt', - 'installed_packages' => 'Installierte Packages', - 'suggested_packages' => 'Vorgeschlagene Packages', - 'title' => 'Titel', - 'description' => 'Beschreibung', - 'version' => 'Version', - 'install' => 'Installieren »', - 'remove' => 'Entfernen »', - 'search_packagist_for_more' => 'Packagist nach mehr Packages durchsuchen', - 'search' => 'Suchen »', - - // Summary plugin - 'build-summary' => 'Zusammenfassung', - 'stage' => 'Abschnitt', - 'duration' => 'Dauer', - 'plugin' => 'Plugin', - 'stage_setup' => 'Vorbereitung', - 'stage_test' => 'Test', - 'stage_complete' => 'Vollständig', - 'stage_success' => 'Erfolg', - 'stage_failure' => 'Fehlschlag', - 'stage_broken' => 'Defekt', - 'stage_fixed' => 'Behoben', - - // Update - 'update_app' => 'Datenbank wird aktualisiert, um den Änderungen der Models zu entsprechen.', - 'updating_app' => 'Aktualisiere PHP Censor-Datenbank:', - 'not_installed' => 'PHP Censor scheint nicht installiert zu sein.', - 'install_instead' => 'Bitte installieren Sie PHP Censor stattdessen via php-censor:install.', - - // Build Plugins: - 'passing_build' => 'Durchlaufender Build', - 'failing_build' => 'Fehlschlagender Build', - 'log_output' => 'Protokollausgabe: ', - - // Error Levels: - 'critical' => 'Kritisch', - 'high' => 'Hoch', - 'normal' => 'Normal', - 'low' => 'Niedrig', - - // Plugins that generate errors: - 'php_mess_detector' => 'PHP Mess Detector', - 'php_code_sniffer' => 'PHP Code Sniffer', - 'php_unit' => 'PHP Unit', - 'php_cpd' => 'PHP Copy/Paste Detector', - 'php_docblock_checker' => 'PHP Docblock Checker', -]; diff --git a/src/PHPCensor/Languages/lang.el.php b/src/PHPCensor/Languages/lang.el.php deleted file mode 100644 index c798cd7..0000000 --- a/src/PHPCensor/Languages/lang.el.php +++ /dev/null @@ -1,291 +0,0 @@ - 'Ελληνικά', - 'language' => 'Γλώσσα', - - // Log in: - 'log_in_to_app' => 'Είσοδος στο PHP Censor', - 'login_error' => 'Λάθος διεύθυνση e-mail ή κωδικός πρόσβασης', - 'forgotten_password_link' => 'Ξεχάσατε τον κωδικό σας;', - 'reset_emailed' => 'Σας έχουμε αποσταλεί ένα σύνδεσμο για να επαναφέρετε τον κωδικό πρόσβασής σας.', - 'reset_header' => ' Μην ανησυχείτε!
    Απλά εισάγετε το email σας παρακάτω και θα θα σας αποστείλουμε ένα email -με ένα σύνδεσμο για να επαναφέρετε τον κωδικό πρόσβασής σας.', - 'reset_email_address' => 'Εισάγετε τη διεύθυνση e-mail σας:', - 'reset_send_email' => 'Email επαναφοράς κωδικού πρόσβασης', - 'reset_enter_password' => 'Παρακαλώ εισάγετε ένα νέο κωδικό πρόσβασης', - 'reset_new_password' => 'Νέος κωδικός πρόσβασης:', - 'reset_change_password' => 'Αλλαγή κωδικού πρόσβασης', - 'reset_no_user_exists' => 'Δεν υπάρχει χρήστης με αυτή την διεύθυνση ηλεκτρονικού ταχυδρομείου, παρακαλώ προσπαθήστε ξανά.', - 'reset_email_body' => 'Γεια %s, - -Έχετε λάβει αυτό το μήνυμα επειδή εσείς, ή κάποιος άλλος, ζήτησε επαναφορά κωδικού πρόσβασης για το PHP Censor. - -Αν ήσασταν εσείς, παρακαλώ κάντε κλικ στον παρακάτω σύνδεσμο για να επαναφέρετε τον κωδικό πρόσβασής σας: %ssession/reset-password/%d/%s - -Σε αντίθετη περίπτωση, παρακαλούμε να αγνοήσετε αυτό το μήνυμα και δεν πρόκεται να πραγματοποιηθεί η επαναφορά. - -Σας ευχαριστούμε, - -PHP Censor', - - 'reset_email_title' => 'PHP Censor Επαναφορά Κωδικού για %s', - 'reset_invalid' => 'Μη έγκυρο αίτημα επαναφοράς κωδικού πρόσβασης.', - 'email_address' => 'Διεύθυνση email', - 'login' => 'Login / Email Address', - 'password' => 'Κωδικός πρόσβασης', - 'log_in' => 'Είσοδος', - - - // Top Nav - 'toggle_navigation' => 'Εναλλαγή πλοήγησης', - 'n_builds_pending' => '%d κατασκευές σε εκκρεμότητα', - 'n_builds_running' => '%d τρέχοντες κατασκευές', - 'edit_profile' => 'Επεξεργασία Προφίλ', - 'sign_out' => 'Έξοδος', - 'branch_x' => 'Διακλάδωση: %s', - 'created_x' => 'Δημιουργήθηκε: %s', - 'started_x' => 'Ξεκίνησε: %s', - - // Sidebar - 'hello_name' => 'Γειά, %s', - 'dashboard' => 'Πίνακας ελέγχου', - 'admin_options' => 'Επιλογές Διαχειριστή', - 'add_project' => 'Προσθήκη έργου', - 'settings' => 'Ρυθμίσεις', - 'manage_users' => 'Διαχείριση χρηστών', - 'plugins' => 'Πρόσθετα', - 'view' => 'Προβολή', - 'build_now' => 'Κατασκευή τώρα', - 'edit_project' => 'Επεξεργασία Έργου', - 'delete_project' => 'Διαγραφή Έργου', - - // Project Summary: - 'no_builds_yet' => 'Καμία κατασκευή ακόμα!', - 'x_of_x_failed' => '%d από τις %d τελευταίες κατασκευές απέτυχαν', - 'x_of_x_failed_short' => '%d / %d απέτυχαν.', - 'last_successful_build' => 'Η τελευταία επιτυχής κατασκεύη ήταν %s.', - 'never_built_successfully' => 'Αυτό το έργο δεν έχει ποτέ κατασκευαστεί με επιτυχία.', - 'all_builds_passed' => 'Όλες από τις %d κατασκευές πέρασαν', - 'all_builds_passed_short' => '%d / %d πέρασαν.', - 'last_failed_build' => 'H τελευταία αποτυχημένη κατασκευή ήταν %s.', - 'never_failed_build' => 'Το έργο αυτό δεν παρέλειψε ποτέ μια κατασκευή.', - 'view_project' => 'Προβολή του έργου', - - // Timeline: - 'latest_builds' => 'Τελευταίες κατασκευές', - 'pending' => 'Σε εκκρεμότητα', - 'running' => 'Τρέχοντα', - 'success' => 'Επιτυχία', - 'failed' => 'Αποτυχία', - 'manual_build' => 'Χειροκίνητη κατασκευή', - - // Add/Edit Project: - 'new_project' => 'Νέο έργο', - 'project_x_not_found' => 'Το έργο με αριθμό %d δεν υπάρχει', - 'project_details' => 'Στοιχεία Έργου', - 'public_key_help' => 'Για να είναι πιο εύκολο να ξεκινήσετε, έχουμε δημιουργήσει ένα ζεύγος κλειδιών SSH για να χρησιμοποιήσετε -για το έργο αυτό. Για να τα χρησιμοποιήσετε, απλά προσθέστε το ακόλουθο δημόσιο κλειδί στο τμήμα "ανάπτυξη κλειδιών" -του επιλεγμένου πηγαίου κώδικα της πλατφόρμας φιλοξενίας σας.', - 'select_repository_type' => 'Επιλέξτε τον τύπο του αποθετηρίου...', - 'github' => 'GitHub', - 'bitbucket' => 'Bitbucket', - 'gitlab' => 'GitLab', - 'remote' => 'Απομακρυσμένη διεύθυνση URL', - 'local' => 'Τοπική Διαδρομή', - 'hg' => 'Ευμετάβλητο', - - 'where_hosted' => 'Πού φιλοξενείται το έργο σας;', - 'choose_github' => 'Επιλέξτε ένα αποθετήριο GitHub:', - - 'repo_name' => 'Αποθετήριο Όνομα / διεύθυνση URL (Απομακρυσμένα) ή Διαδρομή (Τοπικά)', - 'project_title' => 'Τίτλος Έργου', - 'project_private_key' => 'Ιδιωτικό κλειδί για πρόσβαση σε αποθετήριο -(αφήστε κενό για την τοπική ή / και ανώνυμα απομακρυσμένα)', - 'build_config' => 'Kατασκευή διαμόρφωσης PHP Censor για αυτό το έργο -(αν δεν μπορείτε να προσθέσετε ένα αρχείο .php-censor.yml (.phpci.yml|phpci.yml) στο αποθετήριο έργων)', - 'default_branch' => 'Προκαθορισμένο όνομα διακλάδωσης', - 'allow_public_status' => 'Ενεργοποίηση της σελίδας δημόσιας κατάστασης και την εικόνα για το έργο αυτό;', - 'archived' => 'Archived', - 'archived_menu' => 'Archived', - 'save_project' => 'Αποθήκευση έργου', - - 'error_mercurial' => 'Ο σύνδεσμος URL του ευμετάβλητου αποθετηρίου πρέπει να ξεκινάει με http:// ή https://', - 'error_remote' => 'Ο σύνδεσμος URL του αποθετηρίου πρέπει να ξεκινάει με git://, http:// ή https://', - 'error_gitlab' => 'Το όνομα του αποθετηρίου GitLab πρέπει να είναι της μορφής "user@domain.tld:owner/repo.git"', - 'error_github' => 'Το όνομα του αποθετηρίου θα πρέπει να είναι της μορφής "owner/repo" ιδιοκτήτης/αποθετήριο', - 'error_bitbucket' => 'Το όνομα του αποθετηρίου θα πρέπει να είναι της μορφής "owner/repo" ιδιοκτήτης/αποθετήριο', - 'error_path' => 'Η διαδρομή που καθορίσατε δεν υπάρχει.', - - // View Project: - 'all_branches' => 'Όλες οι διακλαδώσεις', - 'builds' => 'Κατασκευές', - 'id' => 'Αριθμός αναγνώρισης', - 'date' => 'Date', - 'project' => 'Έργο', - 'commit' => 'Συνεισφορά', - 'branch' => 'Διακλάδωση', - 'status' => 'Κατάσταση', - 'prev_link' => '« Προηγούμενο', - 'next_link' => 'Επόμενο «', - 'public_key' => 'Δημόσιο κλειδί', - 'delete_build' => 'Διαγραφή κλειδιού', - - 'webhooks' => 'Webhooks', - 'webhooks_help_github' => 'Για την αυτόματη κατασκευή αυτού του έργου όταν υπάρχουν νέες συνεισφορές, προσθέστε τη διεύθυνση URL παρακάτω -ως ένα νέο "Webhook" στο τμήμα Webhooks -and Services του GitHub αποθετηρίου σας.', - - 'webhooks_help_gitlab' => 'Για την αυτόματη κατασκευή αυτού του έργου όταν υπάρχουν νέες συνεισφορές, προσθέστε την διεύθυνση URL παρακάτω -σαν "WebHook URL" στο τμήμα Web Hooks του GitLab αποθετηρίου σας.', - - 'webhooks_help_bitbucket' => 'Για την αυτόματη κατασκευή αυτού του έργου όταν υπάρχουν νέες συνεισφορές, προσθέστε τη διεύθυνση URL παρακάτω -ως μια υπηρεσία "POST" στο τμήμα -Services του Bitbucket αποθετηρίου σας.', - - // View Build - 'build_x_not_found' => 'Η κατασκευή με αριθμό %d δεν υπάρχει', - 'build_n' => 'Κατασκευή %d', - 'rebuild_now' => 'Αναδόμηση τώρα', - - - 'committed_by_x' => 'Έγινε συνεισφορά από %s', - 'commit_id_x' => 'Συνεισφορά: %s', - - 'chart_display' => 'Αυτό το γράφημα θα εμφανιστεί μόλις η κατασκευή έχει ολοκληρωθεί.', - - 'build' => 'Κατασκευή', - 'lines' => 'Γραμμές', - 'comment_lines' => 'Γραμμές σχολίων', - 'noncomment_lines' => 'Μη σχολιασμένες γραμμές', - 'logical_lines' => 'Λογικές γραμμές', - 'lines_of_code' => 'Γραμμές Κώδικα', - 'build_log' => 'Αρχείο καταγραφής κατασκευών', - 'quality_trend' => 'Ποιότητα τρέντ', - 'codeception_errors' => 'Λάθη Codeception', - 'phpmd_warnings' => 'Προειδοποιήσεις PHPMD', - 'phpcs_warnings' => 'Προειδοποιήσεις PHPCS ', - 'codeception_errors' => 'Λάθη Codeception', - 'phpcs_errors' => 'Λάθη PHPCS', - 'phplint_errors' => 'Λάθη Lint', - 'phpunit_errors' => 'Λάθη PHPUnit ', - 'phpdoccheck_warnings' => 'Χαμένα Docblocks', - 'issues' => 'Θέματα', - - 'codeception' => 'Codeception', - 'phpcpd' => 'PHP Ανιχνευτής Αντιγραφής/Επικόλλησης', - 'phpcs' => 'Sniffer Κώδικα PHP', - 'phpdoccheck' => 'Χαμένα Docblocks', - 'phpmd' => 'Aνιχνευτής PHP Mess', - 'phpspec' => 'PHP Spec', - 'phpunit' => 'PHP Unit', - - 'file' => 'Αρχείο', - 'line' => 'Γραμμή', - 'class' => 'Κατηγορία', - 'method' => 'Μέθοδος', - 'message' => 'Μήνυμα', - 'start' => 'Έναρξη', - 'end' => 'Τέλος', - 'from' => 'Από', - 'to' => 'Προς', - 'result' => 'Αποτέλεσμα', - 'ok' => 'ΟΚ', - 'took_n_seconds' => 'Χρειάστηκαν %d δευτερόλεπτα', - 'build_started' => 'Η κατασκευή άρχισε', - 'build_finished' => 'Η κατασκευή ολοκληρώθηκε', - 'test_message' => 'Message', - 'test_no_message' => 'No message', - 'test_success' => 'Successful: %d', - 'test_fail' => 'Failures: %d', - 'test_skipped' => 'Skipped: %d', - 'test_error' => 'Errors: %d', - 'test_todo' => 'Todos: %d', - 'test_total' => '%d test(s)', - - // Users - 'name' => 'Όνομα', - 'password_change' => 'Κωδικός πρόσβασης (αφήστε κενό αν δεν θέλετε να αλλάξετε)', - 'save' => 'Αποθήκευση »', - 'update_your_details' => 'Ενημερώστε τα στοιχεία σας', - 'your_details_updated' => 'Τα στοιχεία σας έχουν ενημερωθεί.', - 'add_user' => 'Προσθήκη χρήστη', - 'is_admin' => 'Είναι διαχειριστής;', - 'yes' => 'Ναι', - 'no' => 'Όχι', - 'edit' => 'Επεξεργασία', - 'edit_user' => 'Επεξεργασία χρήστη', - 'delete_user' => 'Διαγραφή χρήστη', - 'user_n_not_found' => 'Ο χρήστης με αριθμό %d δεν υπάρχει.', - 'is_user_admin' => 'Είναι αυτός ο χρήστης διαχειριστής;', - 'save_user' => 'Αποθήκευση χρήστη', - - // Settings: - 'settings_saved' => 'Οι ρυθμίσεις σας έχουν αποθηκευτεί.', - 'settings_check_perms' => 'Οι ρυθμίσεις σας δεν αποθηκεύτηκαν, ελέγξτε τα δικαιώματα του αρχείου σας config.yml.', - 'settings_cannot_write' => 'Το PHP Censor δεν μπορεί να γράψει στο αρχείο config.yml, οι ρυθμίσεις ενδέχεται να μην αποθηκευτούν σωστά -μέχρι να διορθωθεί.', - 'settings_github_linked' => 'Ο λογαριασμός σας GitHub έχει συνδεθεί.', - 'settings_github_not_linked' => 'Ο λογαριασμός σας Github δεν μπόρεσε να συνδεθεί.', - 'build_settings' => 'Ρυθμίσεις κατασκευής', - 'github_application' => 'GitHub Εφαρμογή', - 'github_sign_in' => 'Πριν αρχίσετε να χρησιμοποιείτε το GitHub, θα πρέπει να συνδεθείται και να δώσει -το PHP Censor πρόσβαση στο λογαριασμό σας.', - 'github_app_linked' => 'Το PHP Censor συνδέθηκε με επιτυχία με το λογαριασμό Github.', - 'github_where_to_find' => 'Πού να βρείτε αυτά ...', - 'github_where_help' => 'Εάν έχετε στην κατοχή σας την εφαρμογή που θέλετε να χρησιμοποιήσετε, μπορείτε να βρείτε αυτές τις πληροφορίες στην περιοχή -Ρυθμίσεις εφαρμογών ', - - 'email_settings' => 'Ρυθμίσεις email', - 'email_settings_help' => 'Πριν το PHP Censor μπορεί να στείλει μηνύματα ηλεκτρονικού ταχυδρομείου για την κατάσταση κατασκευής, -θα πρέπει να διαμορφώσετε τις ρυθμίσεις SMTP παρακάτω.', - - 'application_id' => 'Αναγνωριστικό εφαρμογής', - 'application_secret' => 'Μυστική Εφαρμογή', - - 'smtp_server' => 'Διακομισής SMTP', - 'smtp_port' => 'Θύρα SMTP', - 'smtp_username' => 'Όνομα χρήστη SMTP', - 'smtp_password' => 'Κωδικός πρόσβασης SMTP', - 'from_email_address' => 'Εmail διεύθυνση αποστολέα', - 'default_notification_address' => 'Προεπιλεγμένη διεύθυνση ειδοποίησης ηλεκτρονικού ταχυδρομείου ', - 'use_smtp_encryption' => 'Εφαρμογή SMTP κρυπτογράφησης;', - 'none' => 'Κανένα', - 'ssl' => 'Κρυπτογράφηση SSL', - 'tls' => 'Κρυπτογράφηση TLS', - - 'failed_after' => 'Να θεωρηθεί μια κατασκευή αποτυχημένη μετά ', - '5_mins' => '5 λεπτά', - '15_mins' => '15 λεπτά', - '30_mins' => '30 λεπτά', - '1_hour' => '1 ώρα', - '3_hours' => '3 ώρες', - - // Plugins - 'cannot_update_composer' => 'To PHP Censor δεν μπορεί να ενημερώσει to composer.json για σας, γιατί δεν είναι εγγράψιμο.', - 'x_has_been_removed' => '%s έχει αφαιρεθεί.', - 'x_has_been_added' => '%s προσθέιηκε στο αρχείο composer.json για εσάς και θα εγκατασταθεί την επόμενη φορά -που θα τρέξετε την ενημέρωση για το composer.', - 'enabled_plugins' => 'Ενεργοποιημένα πρόσθετα', - 'provided_by_package' => 'Παρέχεται από πακέτο', - 'installed_packages' => 'Εγκατεστημένα πακέτα', - 'suggested_packages' => 'Προτεινόμενα πακέτα', - 'title' => 'Τίτλος', - 'description' => 'Περιγραφή', - 'version' => 'Έκδοση', - 'install' => 'Εγκατάσταση »', - 'remove' => 'Αφαίρεση »', - 'search_packagist_for_more' => 'Αναζήτηση στο Packagist για περισσότερα πακέτα', - 'search' => 'Αναζήτηση »', - - // Update - 'update_app' => 'Ενημέρωστε την βάση δεδομένων ώστε να αντικατοπτρίζει τροποποιημένα μοντέλα.', - 'updating_app' => 'Γίνεται ενημέρωση της βάσης δεδομένων PHP Censor:', - 'not_installed' => 'Το PHP Censor δεν φένεται να είναι εγκατεστημένο', - 'install_instead' => 'Παρακαλούμε εγκαταστήστε το PHP Censor καλύτερα με το php-censor:install αντ \'αυτού.', - - // Build Plugins: - 'passing_build' => 'Επιτυχημένη κατασκευή', - 'failing_build' => 'Αποτυχημένη κατασκευή', - 'log_output' => 'Σύνδεση εξόδου:', -]; diff --git a/src/PHPCensor/Languages/lang.en.php b/src/PHPCensor/Languages/lang.en.php deleted file mode 100644 index a8b17bb..0000000 --- a/src/PHPCensor/Languages/lang.en.php +++ /dev/null @@ -1,426 +0,0 @@ - 'English', - 'language' => 'Language', - 'per_page' => 'Items per page', - 'default' => 'Default', - - // Log in: - 'log_in_to_app' => 'Log in to PHP Censor', - 'login_error' => 'Incorrect email address or password', - 'forgotten_password_link' => 'Forgotten your password?', - 'reset_emailed' => 'We\'ve emailed you a link to reset your password.', - 'reset_header' => 'Don\'t worry!
    Just enter your email address below and we\'ll email - you a link to reset your password.', - 'reset_email_address' => 'Enter your email address:', - 'reset_send_email' => 'Email password reset', - 'reset_enter_password' => 'Please enter a new password', - 'reset_new_password' => 'New password:', - 'reset_change_password' => 'Change password', - 'reset_no_user_exists' => 'No user exists with that email address, please try again.', - 'reset_email_body' => 'Hi %s, - -You have received this email because you, or someone else, has requested a password reset for PHP Censor. - -If this was you, please click the following link to reset your password: %ssession/reset-password/%d/%s - -Otherwise, please ignore this email and no action will be taken. - -Thank you, - -PHP Censor', - - 'reset_email_title' => 'PHP Censor Password Reset for %s', - 'reset_invalid' => 'Invalid password reset request.', - 'email_address' => 'Email Address', - 'login' => 'Login / Email Address', - 'password' => 'Password', - 'remember_me' => 'Remember me', - 'log_in' => 'Log in', - - - // Top Nav - 'toggle_navigation' => 'Toggle Navigation', - 'n_builds_pending' => '%d builds pending', - 'n_builds_running' => '%d builds running', - 'edit_profile' => 'Edit Profile', - 'sign_out' => 'Sign Out', - 'branch_x' => 'Branch: %s', - 'created_x' => 'Created: %s', - 'started_x' => 'Started: %s', - 'environment_x' => 'Environment: %s', - - // Sidebar - 'hello_name' => 'Hello, %s', - 'dashboard' => 'Dashboard', - 'admin_options' => 'Admin Options', - 'add_project' => 'Add Project', - 'project_groups' => 'Project Groups', - 'settings' => 'Settings', - 'manage_users' => 'Manage Users', - 'plugins' => 'Plugins', - 'view' => 'View', - 'build_now' => 'Build now', - 'build_now_debug' => 'Build now with debug', - 'edit_project' => 'Edit Project', - 'delete_project' => 'Delete Project', - - // Project Summary: - 'no_builds_yet' => 'No builds yet!', - 'x_of_x_failed' => '%d out of the last %d builds failed.', - 'x_of_x_failed_short' => '%d / %d failed.', - 'last_successful_build' => ' The last successful build was %s.', - 'never_built_successfully' => ' This project has never built successfully.', - 'all_builds_passed' => 'All of the last %d builds passed.', - 'all_builds_passed_short' => '%d / %d passed.', - 'last_failed_build' => ' The last failed build was %s.', - 'never_failed_build' => ' This project has never failed a build.', - 'view_project' => 'View Project', - 'projects_with_build_errors' => 'Build errors', - 'no_build_errors' => 'No build errors', - - // Timeline: - 'latest_builds' => 'Latest Builds', - 'pending' => 'Pending', - 'running' => 'Running', - 'success' => 'Success', - 'failed' => 'Failed', - 'failed_allowed' => 'Failed (Allowed)', - 'error' => 'Error', - 'skipped' => 'Skipped', - 'trace' => 'Stack trace', - 'manual_build' => 'Manual Build', - - // Add/Edit Project: - 'new_project' => 'New Project', - 'project_x_not_found' => 'Project with ID %d does not exist.', - 'project_details' => 'Project Details', - 'public_key_help' => 'To make it easier to get started, we\'ve generated an SSH 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 chosen source code hosting platform.', - 'select_repository_type' => 'Select repository type...', - 'github' => 'GitHub', - 'bitbucket' => 'Bitbucket', - 'gitlab' => 'GitLab', - 'remote' => 'Remote URL', - 'local' => 'Local Path', - 'hg' => 'Mercurial', - 'svn' => 'Subversion', - - 'where_hosted' => 'Where is your project hosted?', - 'choose_github' => 'Choose a GitHub repository:', - - 'repo_name' => 'Repository Name / URL (Remote) or Path (Local)', - 'project_title' => 'Project Title', - 'project_private_key' => 'Private key to use to access repository - (leave blank for local and/or anonymous remotes)', - 'build_config' => 'PHP Censor build config for this project - (if you cannot add a .php-censor.yml (.phpci.yml|phpci.yml) file in the project repository)', - 'default_branch' => 'Default branch name', - 'default_branch_only' => 'Build default branch only', - 'allow_public_status' => 'Enable public status page and image for this project?', - 'archived' => 'Archived', - 'archived_menu' => 'Archived', - 'save_project' => 'Save Project', - 'environments_label' => 'Environments (yaml)', - - 'error_mercurial' => 'Mercurial repository URL must be start with http:// or https://', - 'error_remote' => 'Repository URL must be start with git://, http:// or https://', - 'error_gitlab' => 'GitLab Repository name must be in the format "user@domain.tld:owner/repo.git"', - 'error_github' => 'Repository name must be in the format "owner/repo"', - 'error_bitbucket' => 'Repository name must be in the format "owner/repo"', - 'error_path' => 'The path you specified does not exist.', - - // View Project: - 'all_branches' => 'All Branches', - 'all' => 'All', - 'builds' => 'Builds', - 'id' => 'ID', - 'date' => 'Date', - 'project' => 'Project', - 'commit' => 'Commit', - 'branch' => 'Branch', - 'environment' => 'Environment', - 'status' => 'Status', - 'prev_link' => '« Prev', - 'next_link' => 'Next »', - 'public_key' => 'Public Key', - 'delete_build' => 'Delete Build', - 'build_source' => 'Build source', - - 'source_unknown' => 'Unknown', - 'source_manual_web' => 'Manual (from Web)', - 'source_manual_console' => 'Manual (from CLI)', - 'source_periodical' => 'Periodical', - 'source_webhook' => 'Webhook', - - 'webhooks' => 'Webhooks', - 'webhooks_help_github' => 'To automatically build this project when new commits are pushed, add the URL below - as a new "Webhook" in the Webhooks - and Services section of your GitHub repository.', - - 'webhooks_help_gitlab' => 'To automatically build this project when new commits are pushed, add the URL below - as a "WebHook URL" in the Web Hooks section of your GitLab repository.', - - 'webhooks_help_bitbucket' => 'To automatically build this project when new commits are pushed, add the URL below - as a "POST" service in the - - Services section of your Bitbucket repository.', - - // Project Groups - 'group_projects' => 'Project groups', - 'project_group' => 'Project group', - 'group_count' => 'Projects count', - 'group_edit' => 'Edit', - 'group_delete' => 'Delete', - 'group_add' => 'Add Group', - 'group_add_edit' => 'Add / Edit Group', - 'group_title' => 'Group Title', - 'group_save' => 'Save Group', - - // View Build - 'errors' => 'Errors', - 'information' => 'Information', - - 'build_x_not_found' => 'Build with ID %d does not exist.', - 'build_n' => 'Build %d', - 'rebuild_now' => 'Rebuild Now', - - - 'committed_by_x' => 'Committed by %s', - 'commit_id_x' => 'Commit: %s', - - 'chart_display' => 'This chart will display once the build has completed.', - - 'build' => 'Build', - 'lines' => 'Lines', - 'comment_lines' => 'Comment lines', - 'noncomment_lines' => 'Non-Comment lines', - 'logical_lines' => 'Logical Lines', - 'lines_of_code' => 'Lines of code', - 'build_log' => 'Build log', - 'quality_trend' => 'Quality trend', - 'codeception_errors' => 'Codeception errors', - 'phpmd_warnings' => 'PHPMD warnings', - 'phpcs_warnings' => 'PHPCS warnings', - 'phpcs_errors' => 'PHPCS errors', - 'phplint_errors' => 'Lint errors', - 'phpunit_errors' => 'PHPUnit errors', - 'phpcpd_warnings' => 'PHP Copy/Paste Detector warnings', - 'phpdoccheck_warnings' => 'Missing docblocks', - 'issues' => 'Issues', - 'merged_branches' => 'Merged branches', - - 'phpcpd' => 'PHP Copy/Paste Detector', - 'phpcs' => 'PHP Code Sniffer', - 'phpdoccheck' => 'Missing Docblocks', - 'phpmd' => 'PHP Mess Detector', - 'phpspec' => 'PHP Spec', - 'phpunit' => 'PHP Unit', - - 'codeception_feature' => 'Feature', - 'codeception_suite' => 'Suite', - 'codeception_time' => 'Time', - 'codeception_synopsis' => '%1$d tests carried out in %2$f seconds. - %3$d failures.', - 'suite' => 'Suite', - 'test' => 'Test', - 'file' => 'File', - 'line' => 'Line', - 'class' => 'Class', - 'method' => 'Method', - 'message' => 'Message', - 'start' => 'Start', - 'end' => 'End', - 'from' => 'From', - 'to' => 'To', - 'result' => 'Result', - 'ok' => 'OK', - 'took_n_seconds' => 'Took %d seconds', - 'build_started' => 'Build Started', - 'build_finished' => 'Build Finished', - 'test_message' => 'Message', - 'test_no_message' => 'No message', - 'test_success' => 'Successful: %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', - 'password_change' => 'Password (leave blank if you don\'t want to change)', - 'save' => 'Save »', - 'update_your_details' => 'Update your details', - 'your_details_updated' => 'Your details have been updated.', - 'add_user' => 'Add User', - 'is_admin' => 'Is Admin?', - 'yes' => 'Yes', - 'no' => 'No', - 'edit' => 'Edit', - 'edit_user' => 'Edit User', - 'delete_user' => 'Delete User', - 'user_n_not_found' => 'User with ID %d does not exist.', - 'is_user_admin' => 'Is this user an administrator?', - 'save_user' => 'Save User', - - // Settings: - 'settings_saved' => 'Your settings have been saved.', - 'settings_check_perms' => 'Your settings could not be saved, check the permissions of your config.yml file.', - 'settings_cannot_write' => 'PHP Censor cannot write to your config.yml file, settings may not be saved properly - until this is rectified.', - 'settings_github_linked' => 'Your GitHub account has been linked.', - 'settings_github_not_linked' => 'Your GitHub account could not be linked.', - 'build_settings' => 'Build Settings', - 'github_application' => 'GitHub Application', - 'github_sign_in' => 'Before you can start using GitHub, you need to sign in and grant - PHP Censor access to your account.', - 'github_app_linked' => 'PHP Censor is successfully linked to GitHub account.', - 'github_where_to_find' => 'Where to find these...', - 'github_where_help' => 'If you own the application you would like to use, you can find this information within your - applications settings area.', - - 'email_settings' => 'Email Settings', - 'email_settings_help' => 'Before PHP Censor can send build status emails, - you need to configure your SMTP settings below.', - - 'application_id' => 'Application ID', - 'application_secret' => 'Application Secret', - - 'smtp_server' => 'SMTP Server', - 'smtp_port' => 'SMTP Port', - 'smtp_username' => 'SMTP Username', - 'smtp_password' => 'SMTP Password', - 'from_email_address' => 'From Email Address', - 'default_notification_address' => 'Default Notification Email Address', - 'use_smtp_encryption' => 'Use SMTP Encryption?', - 'none' => 'None', - 'ssl' => 'SSL', - 'tls' => 'TLS', - - 'failed_after' => 'Consider a build failed after', - '5_mins' => '5 Minutes', - '15_mins' => '15 Minutes', - '30_mins' => '30 Minutes', - '1_hour' => '1 Hour', - '3_hours' => '3 Hours', - - // Plugins - 'cannot_update_composer' => 'PHP Censor cannot update composer.json for you as it is not writable.', - 'x_has_been_removed' => '%s has been removed.', - 'x_has_been_added' => '%s has been added to composer.json for you and will be installed next time - you run composer update.', - 'enabled_plugins' => 'Enabled Plugins', - 'provided_by_package' => 'Provided By Package', - 'installed_packages' => 'Installed Packages', - 'suggested_packages' => 'Suggested Packages', - 'title' => 'Title', - 'description' => 'Description', - 'version' => 'Version', - 'install' => 'Install »', - 'remove' => 'Remove »', - 'search_packagist_for_more' => 'Search Packagist for more packages', - 'search' => 'Search »', - - // Summary plugin - 'build-summary' => 'Summary', - 'stage' => 'Stage', - 'duration' => 'Duration', - 'seconds' => 'sec.', - 'plugin' => 'Plugin', - 'stage_setup' => 'Setup', - 'stage_test' => 'Test', - 'stage_deploy' => 'Deploy', - 'stage_complete' => 'Complete', - 'stage_success' => 'Success', - 'stage_failure' => 'Failure', - 'stage_broken' => 'Broken', - 'stage_fixed' => 'Fixed', - 'severity' => 'Severity', - - 'all_plugins' => 'All plugins', - 'all_severities' => 'All severities', - 'filters' => 'Filters', - 'errors_selected' => 'Errors selected', - - 'build_details' => 'Build Details', - 'commit_details' => 'Commit Details', - 'committer' => 'Committer', - 'commit_message' => 'Commit Message', - 'timing' => 'Timing', - 'created' => 'Created', - 'started' => 'Started', - 'finished' => 'Finished', - - // Update - 'update_app' => 'Update the database to reflect modified models.', - 'updating_app' => 'Updating PHP Censor database: ', - 'not_installed' => 'PHP Censor does not appear to be installed.', - 'install_instead' => 'Please install PHP Censor via php-censor:install instead.', - - // Create Build Command - 'add_to_queue_failed' => 'Build created successfully, but failed to add to build queue. This usually happens - when PHP Censor is set to use a beanstalkd server that does not exist, - or your beanstalkd server has stopped.', - - // Build Plugins: - 'passing_build' => 'Passing Build', - 'failing_build' => 'Failing Build', - 'log_output' => 'Log Output: ', - - - // Error Levels: - 'critical' => 'Critical', - 'high' => 'High', - 'normal' => 'Normal', - 'low' => 'Low', - - // Plugins that generate errors: - 'php_mess_detector' => 'PHP Mess Detector', - 'php_code_sniffer' => 'PHP Code Sniffer', - 'php_unit' => 'PHP Unit', - 'php_cpd' => 'PHP Copy/Paste Detector', - 'php_docblock_checker' => 'PHP Docblock Checker', - 'composer' => 'Composer', - 'php_loc' => 'PHP LOC', - 'php_parallel_lint' => 'PHP Parallel Lint', - 'email' => 'Email', - 'atoum' => 'Atoum', - 'behat' => 'Behat', - 'campfire' => 'Campfire', - 'clean_build' => 'Clean Build', - 'codeception' => 'Codeception', - 'copy_build' => 'Copy Build', - 'deployer' => 'Deployer', - 'env' => 'Env', - 'grunt' => 'Grunt', - 'hipchat_notify' => 'Hipchat', - 'irc' => 'IRC', - 'lint' => 'Lint', - 'mysql' => 'MySQL', - 'package_build' => 'Package Build', - 'pdepend' => 'PDepend', - 'pgsql' => 'PostgreSQL', - 'phar' => 'Phar', - 'phing' => 'Phing', - 'php_cs_fixer' => 'PHP Coding Standards Fixer', - 'php_spec' => 'PHP Spec', - 'shell' => 'Shell', - 'slack_notify' => 'Slack', - 'technical_debt' => 'Technical Debt', - 'xmpp' => 'XMPP', - 'security_checker' => 'SensioLabs Security Checker', - - 'confirm_message' => 'Item will be permanently deleted. Are you sure?', - 'confirm_title' => 'Item delete confirmation', - 'confirm_ok' => 'Delete', - 'confirm_cancel' => 'Cancel', - 'confirm_success' => 'Item successfully deleted.', - 'confirm_failed' => 'Deletion failed! Server says: ', - - 'public_status_title' => 'Public status', - 'public_status_image' => 'Status image', - 'public_status_page' => 'Public status page', -]; diff --git a/src/PHPCensor/Languages/lang.es.php b/src/PHPCensor/Languages/lang.es.php deleted file mode 100644 index 20a22b5..0000000 --- a/src/PHPCensor/Languages/lang.es.php +++ /dev/null @@ -1,285 +0,0 @@ - 'Español', - 'language' => 'Lenguaje', - - // Log in: - 'log_in_to_app' => 'Ingresar a PHP Censor', - '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 PHP Censor - -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, - -PHP Censor', - - 'reset_email_title' => 'Reiniciar contraseña de PHP Censor 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', - '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 PHP Censor para builds del proyecto - (en caso que no puedas agregar el archivo .php-censor.yml (.phpci.yml|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', - 'archived_menu' => 'Archivado', - '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_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' => 'PHP Censor 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 PHP Censor.', - 'github_app_linked' => 'PHP Censor 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 PHP Censor 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' => 'PHP Censor 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 »', - - // Update - 'update_app' => 'Actuliza la base de datos para reflejar los modelos actualizados.', - 'updating_app' => 'Actualizando base de datos PHP Censor: ', - 'not_installed' => 'PHP Censor no está instalado.', - 'install_instead' => 'Por favor, instala PHP Censor via php-censor:install.', - - // Build Plugins: - 'passing_build' => 'Build Exitoso', - 'failing_build' => 'Build Fallido', - 'log_output' => 'Log de Salida: ', -]; diff --git a/src/PHPCensor/Languages/lang.fr.php b/src/PHPCensor/Languages/lang.fr.php deleted file mode 100644 index c4d56dd..0000000 --- a/src/PHPCensor/Languages/lang.fr.php +++ /dev/null @@ -1,416 +0,0 @@ - 'Français', - 'language' => 'Langue', - 'per_page' => 'Item par page', - 'default' => 'Défaut', - - // Log in: - 'log_in_to_app' => 'Connectez-vous à PHP Censor', - 'login_error' => 'Adresse email ou mot de passe invalide', - 'forgotten_password_link' => 'Mot de passe oublié ?', - 'reset_emailed' => 'Nous vous avons envoyé un email avec un lien pour réinitialiser votre mot de passe.', - 'reset_header' => 'Pas d\'inquiétude
    Entrez simplement votre adresse email ci-dessous - et nous vous enverrons un message avec un lien pour réinitialiser votre mot de passe.', - 'reset_email_address' => 'Entrez votre adresse email:', - 'reset_send_email' => 'Envoyer le mail', - 'reset_enter_password' => 'Veuillez entrer un nouveau mot de passe', - 'reset_new_password' => 'Nouveau mot de passe :', - 'reset_change_password' => 'Modifier le mot de passe', - 'reset_no_user_exists' => 'Il n\'existe aucun utilisateur avec cette adresse email, merci de réessayer.', - 'reset_email_body' => 'Bonjour %s, - -Vous avez reçu cet email parce qu\'une demande de réinitialisation de mot de passe a été faite pour votre compte PHP Censor. - -Si c\'est bien vous, merci de cliquer sur le lien suivant pour réinitialiser votre mot de passe : %ssession/reset-password/%d/%s - -Sinon, merci d\'ignorer ce message. - -Merci, - -PHP Censor', - - 'reset_email_title' => 'Réinitialisation du mot de passe PHP Censor 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', - - - // Top Nav - 'toggle_navigation' => 'Afficher/cacher la navigation', - 'n_builds_pending' => '%d builds en attente', - 'n_builds_running' => '%d builds en cours d\'exécution', - 'edit_profile' => 'Éditer le profil', - 'sign_out' => 'Déconnexion', - 'branch_x' => 'Branche : %s', - 'created_x' => 'Créé à : %s', - 'started_x' => 'Démarré à : %s', - - // Sidebar - 'hello_name' => 'Salut %s', - 'dashboard' => 'Tableau de bord', - 'admin_options' => 'Options d\'administration', - 'add_project' => 'Ajouter un projet', - 'project_groups' => 'Groupes de projets', - 'settings' => 'Paramètres', - 'manage_users' => 'Gérer les utilisateurs', - 'plugins' => 'Plugins', - 'view' => 'Voir', - 'build_now' => 'Démarrer le build', - 'edit_project' => 'Éditer le projet', - 'delete_project' => 'Supprimer le projet', - - // Project Summary: - 'no_builds_yet' => 'Aucun build pour le moment !', - 'x_of_x_failed' => '%d des %d derniers builds ont échoué.', - 'x_of_x_failed_short' => '%d échecs / %d.', - 'last_successful_build' => ' Le dernier build réussi date du %s.', - 'never_built_successfully' => ' Aucun build de ce projet n\'a réussi.', - 'all_builds_passed' => 'Les %d derniers builds ont réussi.', - 'all_builds_passed_short' => '%d réussites / %d.', - 'last_failed_build' => ' Le dernier build en échec date du %s.', - 'never_failed_build' => ' Aucun build de ce projet n\'a échoué.', - 'view_project' => 'Voir le projet', - - // Timeline: - 'latest_builds' => 'Derniers builds', - 'pending' => 'En attente', - 'running' => 'En cours', - 'success' => 'Terminé', - 'failed' => 'Échoué', - 'error' => 'Error', - 'skipped' => 'Skipped', - 'trace' => 'Stack trace', - 'manual_build' => 'Build manuel', - - // Add/Edit Project: - 'new_project' => 'Nouveau Projet', - 'project_x_not_found' => 'Il n\'existe pas de Projet avec l\'ID %d.', - 'project_details' => 'Détails du Projet', - 'public_key_help' => 'Pour pouvoir démarrer plus facilement, nous avons généré une paire de clés SSH à utiliser avec ce projet. - Pour l\'utiliser, il faut simplement ajouter la clé publique dans la section "Clés de déploiement" - de votre outil d\'hébergement de code.', - 'select_repository_type' => 'Sélectionnez le type de dépôt...', - 'github' => 'GitHub', - 'bitbucket' => 'Bitbucket', - 'gitlab' => 'GitLab', - 'remote' => 'URL distante', - 'local' => 'Chemin local', - 'hg' => 'Mercurial', - 'svn' => 'Subversion', - - 'where_hosted' => 'Où est hébergé votre projet ?', - 'choose_github' => 'Choisissez un dépôt GitHub :', - - 'repo_name' => 'Nom du dépôt / URL (distant) ou chemin (local)', - 'project_title' => 'Titre du projet', - 'project_private_key' => 'Clé privée à utiliser pour accéder au dépôt - (laissez le champ vide pour les dépôts locaux ou les URLs distantes anonymes)', - 'build_config' => 'Configuration PHP Censor spécifique pour ce projet - (si vous ne pouvez pas ajouter de fichier .php-censor.yml (.phpci.yml|phpci.yml) à la racine du dépôt)', - 'default_branch' => 'Nom de la branche par défaut', - 'allow_public_status' => 'Activer la page de statut publique et l\'image pour ce projet ?', - 'archived' => 'Archivé', - 'archived_menu' => 'Archivé', - 'save_project' => 'Enregistrer le projet', - - 'error_mercurial' => 'Les URLs de dépôt Mercurial doivent commencer par http:// ou https://', - 'error_remote' => 'Les URLs de dépôt doivent commencer par git://, http:// ou https://', - 'error_gitlab' => 'Le nom du dépôt GitLab doit avoir le format "user@domain.tld:owner/repo.git"', - 'error_github' => 'Le nom du dépôt doit être dans le format "proprietaire/dépôt"', - 'error_bitbucket' => 'Le nom du dépôt doit être dans le format "proprietaire/dépôt"', - 'error_path' => 'Le chemin que vous avez spécifié n\'existe pas.', - - // View Project: - 'all_branches' => 'Toutes les branches', - 'builds' => 'Builds', - 'id' => 'ID', - 'date' => 'Date', - 'project' => 'Projet', - 'commit' => 'Commit', - 'branch' => 'Branche', - 'status' => 'Statut', - 'prev_link' => '« Précédent', - 'next_link' => 'Suivant »', - 'public_key' => 'Clé Publique', - 'delete_build' => 'Supprimer le build', - - 'webhooks' => 'Webhooks', - 'webhooks_help_github' => 'Pour générer un build quand de nouveaux commits sont poussés, ajouter l\'url suivante - en tant que new "Webhook" dans la section Webhooks - and Services de votre dépôt GitHub.', - - 'webhooks_help_gitlab' => 'Pour générer un build quand de nouveaux commits sont poussés, ajouter l\'url suivante - and tant que "WebHook URL" dans la section Web Hooks de votre dépôt GitLab.', - - 'webhooks_help_bitbucket' => 'Pour générer un build quand de nouveaux commits sont poussés, ajouter l\'url suivante - en tant que service "POST" dans la section - - Services de votre dépôt Bitbucket.', - - // Project Groups - 'project_group' => 'Groupe du projet', - 'group_projects' => 'Groupes de projets', - 'group_count' => 'Nombre de projets', - 'group_edit' => 'Editer', - 'group_delete' => 'Supprimer', - 'group_add' => 'Ajouter un groupe', - 'group_add_edit' => 'Ajouter / Editer un Groupe', - 'group_title' => 'Titre du groupe', - 'group_save' => 'Sauvegarder le groupe', - - - // View Build - 'errors' => 'Erreurs', - 'information' => 'Informations', - - 'build_x_not_found' => 'Le Build avec l\'ID %d n\'existe pas.', - 'build_n' => 'Build %d', - 'rebuild_now' => 'Relancer maintenant', - - - 'committed_by_x' => 'Committé par %s', - 'commit_id_x' => 'Commit : %s', - - 'chart_display' => 'Ce graphique s\'affichera une fois que le build sera terminé.', - - 'build' => 'Build', - 'lines' => 'Lignes', - 'comment_lines' => 'Lignes de commentaires', - 'noncomment_lines' => 'Lignes qui ne sont pas des commentaires', - 'logical_lines' => 'Lignes logiques', - 'lines_of_code' => 'Lignes de code', - 'build_log' => 'Log du build', - 'quality_trend' => 'Tendance de la qualité', - 'codeception_errors' => 'Erreurs Codeception', - 'phpmd_warnings' => 'Alertes PHPMD', - 'phpcs_warnings' => 'Alertes PHPCS', - 'phpcs_errors' => 'Erreurs PHPCS', - 'phplint_errors' => 'Erreurs de Lint', - 'phpunit_errors' => 'Erreurs PHPUnit', - 'phpunit_fail_init' => 'Ni fichier de configuration, ni répertoire de test trouvé.', - 'phpcpd_warnings' => 'PHP Copy/Paste Detector warnings', - 'phpdoccheck_warnings' => 'Blocs de documentation manquants', - 'issues' => 'Tickets', - - 'phpcpd' => 'PHP Copy/Paste Detector', - 'phpcs' => 'PHP Code Sniffer', - 'phpdoccheck' => 'Missing Docblocks', - 'phpmd' => 'PHP Mess Detector', - 'phpspec' => 'PHP Spec', - 'phpunit' => 'PHP Unit', - - 'codeception_feature' => 'Feature', - 'codeception_suite' => 'Suite', - 'codeception_time' => 'Time', - 'codeception_synopsis' => '%1$d tests exécutés en %2$f secondes. - %3$d échecs.', - 'suite' => 'Suite', - 'test' => 'Test', - 'file' => 'Fichier', - 'line' => 'Ligne', - 'class' => 'Classe', - 'method' => 'Méthode', - 'message' => 'Message', - 'start' => 'Démarrage', - 'end' => 'Fin', - 'from' => 'À partir de', - 'to' => 'jusque', - 'result' => 'Resultat', - 'ok' => 'OK', - 'took_n_seconds' => 'Exécuté en %d secondes', - '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', - 'password_change' => 'Mot de passe (laissez vide si vous ne voulez pas le changer)', - 'save' => 'Sauvegarder »', - 'update_your_details' => 'Mettre à jour vos préférences', - 'your_details_updated' => 'Vos préférences ont été bien mises à jour.', - 'add_user' => 'Ajouter un utilisateur', - 'is_admin' => 'Est-il administrateur ?', - 'yes' => 'Oui', - 'no' => 'Non', - 'edit' => 'Éditer', - 'edit_user' => 'Éditer l\'utilisateur', - 'delete_user' => 'Supprimer l\'utilisateur', - 'user_n_not_found' => 'L\'utilisateur avec l\'ID %d n\'existe pas.', - 'is_user_admin' => 'Est-ce que cet utilisateur est administrateur ?', - 'save_user' => 'Sauvegarder l\'utilisateur', - - // Settings: - 'settings_saved' => 'Vos paramètres ont été sauvegardés.', - 'settings_check_perms' => 'Vos paramètres n\'ont pas pu être sauvegardés, vérifiez les permissions sur le fichier config.yml.', - 'settings_cannot_write' => 'PHP Censor ne peut pas écrire dans votre fichier config.yml, les paramètres ne pourront pas être sauvegardés correctement - tant que ce ne sera pas corrigé.', - 'settings_github_linked' => 'Votre compte GitHub n\'a pas été lié.', - 'settings_github_not_linked' => 'Votre compte GitHub ne peut pas être lié.', - 'build_settings' => 'Configuration du Build', - 'github_application' => 'Application GitHub', - 'github_sign_in' => 'Avant de commencer à utiliser GitHub, vous devez vous connecter et autoriser - PHP Censor à accéder à votre compte.', - 'github_app_linked' => 'PHP Censor s\'est connecté avec succès au compte GitHub.', - 'github_where_to_find' => 'Où trouver ces informations...', - 'github_where_help' => 'Si vous souhaitez utiliser une application qui vous appartient, vous pouvez trouver ces informations dans - la zone de paramètres applications.', - - 'email_settings' => 'Configuration Email', - 'email_settings_help' => 'Avant que PHP Censor puisse envoyer des emails concernant les statuts de build, - vous devez entrer les configurations SMTP ci-dessous.', - - 'application_id' => 'Identifiant d\'application', - 'application_secret' => 'Clé secrète de l\'application', - - 'smtp_server' => 'Serveur SMTP', - 'smtp_port' => 'Port SMTP', - 'smtp_username' => 'Nom d\'utilisateur SMTP', - 'smtp_password' => 'Mot de passe SMTP', - 'from_email_address' => 'Adresse à partir de laquelle sont envoyés les emails', - 'default_notification_address' => 'Adresse de notification par défaut', - 'use_smtp_encryption' => 'Est-ce que vous voulez utiliser le chiffrement SMTP', - 'none' => 'Non', - 'ssl' => 'SSL', - 'tls' => 'TLS', - - 'failed_after' => 'Considérer qu\'un build a échoué après', - '5_mins' => '5 Minutes', - '15_mins' => '15 Minutes', - '30_mins' => '30 Minutes', - '1_hour' => '1 Heure', - '3_hours' => '3 Heures', - - // Plugins - 'cannot_update_composer' => 'PHP Censor 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 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', - 'installed_packages' => 'Paquets installés', - 'suggested_packages' => 'Paquets suggérés', - 'title' => 'Titre', - 'description' => 'Description', - 'version' => 'Version', - 'install' => 'Installer »', - 'remove' => 'Supprimer »', - 'search_packagist_for_more' => 'Rechercher sur Packagist pour trouver plus de paquets', - 'search' => 'Rechercher »', - - // Summary plugin - 'build-summary' => 'Résumé', - 'stage' => 'Étape', - 'duration' => 'Durée', - 'seconds' => 'sec.', - 'plugin' => 'Plugin', - 'stage_setup' => 'Préparation', - 'stage_test' => 'Test', - 'stage_complete' => 'Terminé', - 'stage_success' => 'Succes', - 'stage_failure' => 'Échec', - 'stage_broken' => 'Cassé', - 'stage_fixed' => 'Réparé', - 'severity' => 'Gravité', - - 'build_details' => 'Détails du Build', - 'commit_details' => 'Détails du Commit', - 'committer' => 'Committer', - 'timing' => 'Timing', - 'created' => 'Crée', - 'started' => 'Demarré', - 'finished' => 'Terminé', - - // Update - 'update_app' => 'Mise à jour de la base de données pour refléter les modifications apportées aux modèles.', - 'updating_app' => 'Mise à jour de la base de données PHP Censor : ', - 'not_installed' => 'PHP Censor n\'a pas l\'air d\'être installé.', - 'install_instead' => 'Merci d\'installer PHP Censor grâce à la commande php-censor:install.', - - // Create Build Command - 'add_to_queue_failed' => 'Build créé avec succès mais échec de l\'ajouter à la file d\'attente des Builds. Cela arrive généralement - quand PHP Censor est configuré pour utiliser un serveur beanstalkd qui n\'existe pas ou qui n\'est pas démarré.', - - // Builder - 'missing_app_yml' => 'Ce projet ne contient pas de fichier .php-censor.yml (.phpci.yml|phpci.yml) ou il est vide.', - 'build_success' => 'BUILD RÉUSSI', - 'build_failed' => 'BUILD ÉCHOUÉ', - 'removing_build' => 'Suppression du build.', - 'exception' => 'Exception: ', - 'could_not_create_working' => 'Impossible de créer une copie de travail.', - 'working_copy_created' => 'Copie de travail créée: %s', - 'looking_for_binary' => 'Recherche du binaire: %s', - 'found_in_path' => 'Trouver dans %s: %s', - 'running_plugin' => 'EXÉCUTION DU PLUGIN: %s', - 'plugin_success' => 'PLUGIN: RÉUSSI', - 'plugin_failed' => 'PLUGIN: ÉCHOUÉ', - 'plugin_missing' => 'Le plugin n\'existe pas: %s', - 'failed_allowed' => 'Autorisé', - - // Build Plugins: - 'passing_build' => 'Build réussi', - 'failing_build' => 'Build en echec', - 'log_output' => 'Sortie de log : ', - - // Error Levels: - 'critical' => 'Critique', - 'high' => 'Haut', - 'normal' => 'Normal', - 'low' => 'Bas', - - // Plugins that generate errors: - 'php_mess_detector' => 'PHP Mess Detector', - 'php_code_sniffer' => 'PHP Code Sniffer', - 'php_unit' => 'PHP Unit', - 'php_cpd' => 'PHP Copy/Paste Detector', - 'php_docblock_checker' => 'PHP Docblock Checker', - 'composer' => 'Composer', - 'php_loc' => 'PHP LOC', - 'php_parallel_lint' => 'PHP Parallel Lint', - 'email' => 'Email', - 'atoum' => 'Atoum', - 'behat' => 'Behat', - 'campfire' => 'Campfire', - 'clean_build' => 'Clean Build', - 'codeception' => 'Codeception', - 'copy_build' => 'Copy Build', - 'deployer' => 'Deployer', - 'env' => 'Env', - 'grunt' => 'Grunt', - 'hipchat_notify' => 'Hipchat', - 'irc' => 'IRC', - 'lint' => 'Lint', - 'mysql' => 'MySQL', - 'package_build' => 'Package Build', - 'pdepend' => 'PDepend', - 'pgsql' => 'PostgreSQL', - 'phar' => 'Phar', - 'phing' => 'Phing', - 'php_cs_fixer' => 'PHP Coding Standards Fixer', - 'php_spec' => 'PHP Spec', - 'shell' => 'Shell', - 'slack_notify' => 'Slack', - 'technical_debt' => 'Technical Debt', - 'xmpp' => 'XMPP', - - 'confirm_message' => 'L\'article sera définitivement supprimé. Êtes-vous sûr ?', - 'confirm_title' => 'Confirmation de suppression d\'un article', - 'confirm_ok' => 'Supprimer', - 'confirm_cancel' => 'Annuler', - 'confirm_success' => 'L\'article a été supprimé avec succès.', - 'confirm_failed' => 'Echec de la suppresion! Le serveur a répondu: ', - - 'public_status_title' => 'Statut public', - 'public_status_image' => 'Image de statut', - 'public_status_page' => 'Page publique de statut', -]; diff --git a/src/PHPCensor/Languages/lang.it.php b/src/PHPCensor/Languages/lang.it.php deleted file mode 100644 index e11ab29..0000000 --- a/src/PHPCensor/Languages/lang.it.php +++ /dev/null @@ -1,291 +0,0 @@ - 'Italiano', - 'language' => 'Lingua', - - // Log in: - 'log_in_to_app' => 'Accedi a PHP Censor', - 'login_error' => 'Indirizzo email o password errati', - 'forgotten_password_link' => 'Hai dimenticato la tua password?', - 'reset_emailed' => 'Ti abbiamo inviato un link via email per ripristinare la tua password.', - 'reset_header' => 'Non preoccuparti!
    E\' sufficiente inserire il tuo indirizzo email di seguito e ti invieremo una email con il link per il ripristino della tua password.', - 'reset_email_address' => 'Inserisci il tuo indirizzo email:', - 'reset_send_email' => 'Invia il link di reset della password', - 'reset_enter_password' => 'Per favore inserisci la nuova password', - 'reset_new_password' => 'Nuova password:', - 'reset_change_password' => 'Cambia password', - 'reset_no_user_exists' => 'Non esiste nessun utente con questo indirizzo email, per favore prova ancora.', - 'reset_email_body' => 'Ciao %s, - -hai ricevuto questa email perché tu, o qualcun\'altro, ha richiesto un reset della password per PHP Censor. - -Se questa mail è tua, per favore apri il seguente link per ripristinare la tua password: %ssession/reset-password/%d/%s - -altrimenti, per favore, ignora questa email e nessuna azione verrà intrapresa. - -Grazie, - -PHP Censor', - - 'reset_email_title' => 'Ripristino della password di PHP Censor per %s', - 'reset_invalid' => 'Richeista di ripristino password non valida.', - 'email_address' => 'Indirizzo Email', - 'login' => 'Login / Email Address', - 'password' => 'Password', - 'log_in' => 'Accedi', - - // Top Nav - 'toggle_navigation' => 'Alterna navigazione', - 'n_builds_pending' => '%d build in attesa', - 'n_builds_running' => '%d build in corso', - 'edit_profile' => 'Modifica il Profilo', - 'sign_out' => 'Disconnettiti', - 'branch_x' => 'Branch: %s', - 'created_x' => 'Creato: %s', - 'started_x' => 'Avviato: %s', - - // Sidebar - 'hello_name' => 'Ciao, %s', - 'dashboard' => 'Dashboard', - 'admin_options' => 'Opzioni di amministrazione', - 'add_project' => 'Aggiungi un Progetto', - 'settings' => 'Impostazioni', - 'manage_users' => 'Gestisci Utenti', - 'plugins' => 'Plugins', - 'view' => 'Visualizzazione', - 'build_now' => 'Avvia una build ora', - 'edit_project' => 'Modifica il Progetto', - 'delete_project' => 'Cancella il Progetto', - - // Project Summary: - 'no_builds_yet' => 'Ancora nessuna build!', - 'x_of_x_failed' => '%d delle ultime %d build sono fallite.', - 'x_of_x_failed_short' => '%d / %d fallite.', - 'last_successful_build' => ' L\'ultima build è %s.', - 'never_built_successfully' => ' Questo progetto non ha nessuna build eseguita con successo.', - 'all_builds_passed' => 'Tutte le ultime %d build sono valide.', - 'all_builds_passed_short' => '%d / %d valide.', - 'last_failed_build' => ' L\'ultima build è %s.', - 'never_failed_build' => ' Questo progetto non ha nessuna build fallita.', - 'view_project' => 'Visualizza il Progetto', - - // Timeline: - 'latest_builds' => 'Ultime Build', - 'pending' => 'In attesa', - 'running' => 'In corso', - 'success' => 'Successo', - 'failed' => 'Fallita', - 'manual_build' => 'Build Manuale', - - // Add/Edit Project: - 'new_project' => 'Nuovo Progetto', - 'project_x_not_found' => 'Progetto con ID %d non esistente.', - 'project_details' => 'Dettagli del Progetto', - 'public_key_help' => 'Per rendere più facile la procedura, abbiamo generato una chiave SSH per te da - usare per questo progetto. Per usarla, aggiungi la chiave pubblica alle "deploy keys" - della piattaforma di gestione del codice che hai scelto.', - 'select_repository_type' => 'Seleziona il tipo di repository...', - 'github' => 'GitHub', - 'bitbucket' => 'Bitbucket', - 'gitlab' => 'GitLab', - 'remote' => 'URL Remoto', - 'local' => 'Percorso Locale', - 'hg' => 'Mercurial', - - 'where_hosted' => 'Dove è archiviato il tuo progetto?', - 'choose_github' => 'Scegli il repository di GitHub:', - - 'repo_name' => 'Nome del Repository / URL (Remoto) o Percorso (Locale)', - 'project_title' => 'Titolo del Progetto', - 'project_private_key' => 'Chiave provata da usare per accedere al repository - (lascia vuota per repository locali o remoti con accesso anonimo)', - 'build_config' => 'condigurazione della build di PHP Censor per questo progetto - (se non puoi aggiungere il file .php-censor.yml (.phpci.yml|phpci.yml) nel repository di questo progetto)', - 'default_branch' => 'Nome del branch di default', - 'allow_public_status' => 'Vuoi rendere pubblica la pagina dello stato e l\'immagine per questo progetto?', - 'archived' => 'Archived', - 'archived_menu' => 'Archived', - 'save_project' => 'Salva il Progetto', - - 'error_mercurial' => 'L\'URL del repository Mercurial URL deve iniziare con http:// o https://', - 'error_remote' => 'L\'URL del repository deve iniziare con git://, http:// o https://', - 'error_gitlab' => 'Il nome del repository di GitLab deve essere nel seguente formato "utente@dominio.tld:proprietario/repository.git"', - 'error_github' => 'Il nome del repository deve essere nel formato "proprietario/repository"', - 'error_bitbucket' => 'Il nome del repository deve essere nel formato "proprietario/repository"', - 'error_path' => 'The path you specified does not exist.', - 'error_path' => 'Il percorso che hai indicato non esiste.', - - // View Project: - 'all_branches' => 'Tutti i Branche', - 'builds' => 'Builds', - 'id' => 'ID', - 'date' => 'Data', - 'project' => 'Progetto', - 'commit' => 'Commit', - 'branch' => 'Branch', - 'status' => 'Stato', - 'prev_link' => '« Precedente', - 'next_link' => 'Successivo »', - 'public_key' => 'Chiave pubblica', - 'delete_build' => 'Rimuovi build', - - 'webhooks' => 'Webhooks', - 'webhooks_help_github' => 'Per effettuare la build automatica di questo progetto quando vengono inseriti nuovi commit, - aggiungi l\'URL seguente come "Webhook" nella sezione - Webhooks and Services del tuo - repository su GitHub.', - - 'webhooks_help_gitlab' => 'Per effettuare la build automatica di questo progetto quando vengono inseriti nuovi commit, - aggiungi l\'URL seguente come "Webhook URL" nella sezione "WebHook URL" del tuo - repository GitLab.', - - 'webhooks_help_bitbucket' => 'Per effettuare la build automatica di questo progetto quando vengono inseriti nuovi - commit, aggiungi l\'URL seguente come serizio "POST" nella sezione - Services del tuo repository su - BITBUCKET.', - - // View Build - 'build_x_not_found' => 'La build con ID %d non esite.', - 'build_n' => 'Build %d', - 'rebuild_now' => 'Esegui nuovamente la build ora', - - - 'committed_by_x' => 'Inviato da %s', - 'commit_id_x' => 'Commit: %s', - - 'chart_display' => 'Questo grafico verrà mostrato una volta terminata la build.', - - 'build' => 'Build', - 'lines' => 'Linee', - 'comment_lines' => 'Linee di commenti', - 'noncomment_lines' => 'Linee che non sono commenti', - 'logical_lines' => 'Linee di logica', - 'lines_of_code' => 'Linee di codice', - 'build_log' => 'Log della build', - 'quality_trend' => 'Trend della qualità', - 'codeception_errors' => 'Errori di Codeception', - 'phpmd_warnings' => 'Avvisi di PHPMD', - 'phpcs_warnings' => 'Avvisi di PHPCS', - 'phpcs_errors' => 'Errori di PHPCS', - 'phplint_errors' => 'Errori di Lint', - 'phpunit_errors' => 'Errori di PHPUnit', - 'phpdoccheck_warnings' => 'Docblocks mancanti', - 'issues' => 'Segnalazioni', - - 'codeception' => 'Codeception', - 'phpcpd' => 'PHP Copy/Paste Detector', - 'phpcs' => 'PHP Code Sniffer', - 'phpdoccheck' => 'Docblocks mancanti', - 'phpmd' => 'PHP Mess Detector', - 'phpspec' => 'PHP Spec', - 'phpunit' => 'PHP Unit', - - 'file' => 'File', - 'line' => 'Lina', - 'class' => 'Classe', - 'method' => 'Metodo', - 'message' => 'Messaggio', - 'start' => 'Inizia', - 'end' => 'Finisci', - 'from' => 'Da', - 'to' => 'A', - 'result' => 'Risultati', - 'ok' => 'OK', - 'took_n_seconds' => 'Sono stati impiegati %d seconds', - 'build_started' => 'Build Avviata', - 'build_finished' => 'Build Terminata', - 'test_message' => 'Message', - 'test_no_message' => 'No message', - 'test_success' => 'Successful: %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', - 'password_change' => 'Password (lascia vuota se non vuoi modificarla)', - 'save' => 'Salva »', - 'update_your_details' => 'Aggiorna le tue informazioni', - 'your_details_updated' => 'Le tue informazioni sono state aggiornate.', - 'add_user' => 'Aggiung utent', - 'is_admin' => 'E\' amministratore?', - 'yes' => 'Si', - 'no' => 'No', - 'edit' => 'Modifica', - 'edit_user' => 'Modifica utente', - 'delete_user' => 'Cancella utente', - 'user_n_not_found' => 'L\'utente con ID %d non esiste.', - 'is_user_admin' => 'Questo utente è un amministratore?', - 'save_user' => 'Salva utente', - - // Settings: - 'settings_saved' => 'Le configurazioni sono state salvate.', - 'settings_check_perms' => 'Le configurazioni non possono essere salvate, controlla i permessi del filer config.yml.', - 'settings_cannot_write' => 'PHP Censor non può scrivere il file config.yml, le configurazioni potrebbero non essere - salvate correttamente fintanto che il problema non verrà risolto.', - 'settings_github_linked' => 'Il tuo account GitHub è stato collegato.', - 'settings_github_not_linked' => 'Il tuo account GitHub non può essere collegato.', - 'build_settings' => 'Configurazioni della build', - 'github_application' => 'Applicazione GitHub', - 'github_sign_in' => 'Prima di poter iniziare ad usare GitHub, è necessario collegarsi e garantire - a PHP Censor l\'accesso al tuo account.', - 'github_app_linked' => 'PHP Censor è stato collegato correttamente al tuo account GitHub.', - 'github_where_to_find' => 'Dove trovare queste...', - 'github_where_help' => 'Se sei il proprietario dell\'applicazione, puoi trovare queste informazioni nell\'area delle - configurazioni dell\'applicazione.', - - 'email_settings' => 'Impostazioni Email', - 'email_settings_help' => 'Prima che possa inviare le email con lo status PHP Censor, devi configurare l\'SMTP qui sotto.', - - 'application_id' => 'ID dell\'Applicazione', - 'application_secret' => 'Secret dell\'Applicazione', - - 'smtp_server' => 'Server SMTP', - 'smtp_port' => 'Porta SMTP', - 'smtp_username' => 'Username SMTP', - 'smtp_password' => 'Password SMTP', - 'from_email_address' => 'Indirizzio Email del mittente', - 'default_notification_address' => 'Indirizzo email delle notifiche predefinito', - 'use_smtp_encryption' => 'Utilizzare l\'Encrypting per SMTP?', - 'none' => 'No', - 'ssl' => 'SSL', - 'tls' => 'TLS', - - 'failed_after' => 'Considera la build fallita dopo', - '5_mins' => '5 Minuti', - '15_mins' => '15 Minuti', - '30_mins' => '30 Minuti', - '1_hour' => '1 Ora', - '3_hours' => '3 Ore', - - // Plugins - 'cannot_update_composer' => 'PHP Censor non può aggiornare composer.json per te non essendo scrivibile.', - 'x_has_been_removed' => '%s è stato rimosso.', - 'x_has_been_added' => '%s è stato aggiunto al file composer.json per te, verrà installato la prossima volta che eseguirai - composer update.', - 'enabled_plugins' => 'Plugins attivati', - 'provided_by_package' => 'Fornito dal pacchetto', - 'installed_packages' => 'Pacchetti installati', - 'suggested_packages' => 'Paccehtti suggeriti', - 'title' => 'Titolo', - 'description' => 'Descrizione', - 'version' => 'Versione', - 'install' => 'Installa »', - 'remove' => 'Rimuovi »', - 'search_packagist_for_more' => 'Cerca altri pacchetti su Packagist', - 'search' => 'Cerca »', - - // Update - 'update_app' => 'Aggiorna il database per riflettere le modifiche ai model.', - 'updating_app' => 'Aggiornamenti del database di PHP Censor: ', - 'not_installed' => 'PHP Censor sembra non essere installato.', - 'install_instead' => 'Per favore installa PHP Censor tramite php-censor:install.', - - // Build Plugins: - 'passing_build' => 'Build passata', - 'failing_build' => 'Build fallita', - 'log_output' => 'Log: ', -]; diff --git a/src/PHPCensor/Languages/lang.nl.php b/src/PHPCensor/Languages/lang.nl.php deleted file mode 100644 index d602a9d..0000000 --- a/src/PHPCensor/Languages/lang.nl.php +++ /dev/null @@ -1,291 +0,0 @@ - 'Nederlands', - 'language' => 'Taal', - - // Log in: - 'log_in_to_app' => 'Log in op PHP Censor', - 'login_error' => 'Incorrect e-mailadres of wachtwoord', - 'forgotten_password_link' => 'Wachtwoord vergeten?', - 'reset_emailed' => 'We hebben je een link gemaild om je wachtwoord opnieuw in te stellen.', - 'reset_header' => 'Geen zorgen!
    Vul hieronder gewoon je e-mailadres in en we sturen -je een link on je wachtwoord te resetten.', - 'reset_email_address' => 'Vul je e-mailadres in:', - 'reset_send_email' => 'Verstuur wachtwoord reset', - 'reset_enter_password' => 'Gelieve een nieuw wachtwoord in te voeren', - 'reset_new_password' => 'Nieuw wachtwoord:', - 'reset_change_password' => 'Wijzig wachtwoord', - 'reset_no_user_exists' => 'Er bestaat geen gebruiker met dit e-mailadres, gelieve opnieuw te proberen.', - 'reset_email_body' => 'Hallo %s, - -Je ontvangt deze email omdat jij, of iemand anders, je wachtwoord voor PHP Censor opnieuw wenst in te stellen. - -Indien jij dit was, klik op deze link op je wachtwoord opnieuw in te stellen: %ssession/reset-password/%d/%s - -Zoniet, negeer deze e-mail en er zal geen verdere actie ondernomen worden. - -Bedankt, - -PHP Censor', - - 'reset_email_title' => 'PHP Censor wachtwoord reset voor %s', - 'reset_invalid' => 'Ongeldig wachtwoord reset verzoek', - 'email_address' => 'E-mailadres', - 'login' => 'Login / Email Address', - 'password' => 'Wachtwoord', - 'log_in' => 'Log in', - - - // Top Nav - 'toggle_navigation' => 'Wissel Navigatie', - 'n_builds_pending' => '%d builds wachtend', - 'n_builds_running' => '%d builds lopende', - 'edit_profile' => 'Wijzig profiel', - 'sign_out' => 'Uitloggen', - 'branch_x' => 'Branch: %s', - 'created_x' => 'Aangemaakt: %s', - 'started_x' => 'Gestart: %s', - - // Sidebar - 'hello_name' => 'Hallo, %s', - 'dashboard' => 'Startpagina', - 'admin_options' => 'Administratie opties', - 'add_project' => 'Project toevoegen', - 'settings' => 'Instellingen', - 'manage_users' => 'Gebruikers beheren', - 'plugins' => 'Plugins', - 'view' => 'Bekijk', - 'build_now' => 'Build nu', - 'edit_project' => 'Wijzig project', - 'delete_project' => 'Verwijder project', - - // Project Summary: - 'no_builds_yet' => 'Nog geen builds!', - 'x_of_x_failed' => '%d van de laatste %d builds faalden.', - 'x_of_x_failed_short' => '%d / %d faalden.', - 'last_successful_build' => 'De laatste succesvolle build was %s.', - 'never_built_successfully' => 'Dit project heeft geen succesvolle build gehad.', - 'all_builds_passed' => 'Elk van de laatste %d builds slaagden.', - 'all_builds_passed_short' => '%d / %d slaagden.', - 'last_failed_build' => 'De laatste gefaalde build was %s.', - 'never_failed_build' => 'Dit project heeft geen gefaalde build gehad.', - 'view_project' => 'Bekijk project', - - // Timeline: - 'latest_builds' => 'Laatste builds', - 'pending' => 'In afwachting', - 'running' => 'Lopende', - 'success' => 'Succes', - 'failed' => 'Gefaald', - 'manual_build' => 'Manuele build', - - // Add/Edit Project: - 'new_project' => 'Nieuw project', - 'project_x_not_found' => 'Project met ID %d bestaat niet.', - 'project_details' => 'Project details', - 'public_key_help' => 'Om eenvoudiger te kunnen starten, hebben we een SSH sleutelpaar gegenereerd -voor dit project. Om het te gebruiken, voeg onderstaande public key toe aan de "deploy keys" sectie -van je gekozen source code hosting platform', - 'select_repository_type' => 'Selecteer repository type...', - 'github' => 'GitHub', - 'bitbucket' => 'Bitbucket', - 'gitlab' => 'GitLab', - 'remote' => 'Externe URL', - 'local' => 'Lokaal pad', - 'hg' => 'Mercurial', - - 'where_hosted' => 'Waar wordt je project gehost?', - 'choose_github' => 'Selecteer een GitHub repository:', - - 'repo_name' => 'Repository naam / URL (extern) of pad (lokaal)', - 'project_title' => 'Projecttitel', - 'project_private_key' => 'Private key voor toegang tot repository -(laat leeg voor lokaal en/of anonieme externen)', - 'build_config' => 'PHP Censor build configuratie voor dit project -(indien je geen .php-censor.yml (.phpci.yml|phpci.yml) bestand aan de project repository kan toevoegen)', - 'default_branch' => 'Standaard branch naam', - 'allow_public_status' => 'Publieke statuspagina en afbeelding beschikbaar maken voor dit project?', - 'archived' => 'Archived', - 'archived_menu' => 'Archived', - 'save_project' => 'Project opslaan', - - 'error_mercurial' => 'Mercurial repository URL dient te starten met http:// of https://', - 'error_remote' => 'Repository URL dient te starten met git://, http:// of https://', - 'error_gitlab' => 'GitLab repository naam dient in het formaat "gebruiker@domain.tld/eigenaar/repo.git" te zijn', - 'error_github' => 'Repository naam dient in het formaat "eigenaar/repo" te zijn', - 'error_bitbucket' => 'Repository naam dient in het formaat "eigenaar/repo" te zijn', - 'error_path' => 'Het opgegeven pad bestaat niet.', - - // View Project: - 'all_branches' => 'Alle brances', - 'builds' => 'Builds', - 'id' => 'ID', - 'date' => 'Datum', - 'project' => 'Project', - 'commit' => 'Commit', - 'branch' => 'Branch', - 'status' => 'Status', - 'prev_link' => '« Vorig', - 'next_link' => 'Volgend »', - 'public_key' => 'Public Key', - 'delete_build' => 'Verwijder build', - - 'webhooks' => 'Webhooks', - 'webhooks_help_github' => 'Voor automatische builds wanneer nieuwe commits worden gepusht, dient onderstaande URL -als nieuwe "Webhook" in de Webhooks -and Services sectie van je GitHub repository toegevoegd worden.', - - 'webhooks_help_gitlab' => 'Voor automatische builds wanneer nieuwe commits worden gepusht, dient onderstaande URL -als nieuwe "Webhook URL" in de Web Hooks sectie van je GitLab repository toegevoegd worden.', - - 'webhooks_help_bitbucket' => 'Voor automatische builds wanneer nieuwe commits worden gepusht, dient onderstaande URL -als "POST" service in de in de - -Services sectie van je Bitbucket repository toegevoegd worden.', - - // View Build - 'build_x_not_found' => 'Build met ID %d bestaat niet.', - 'build_n' => 'Build %d', - 'rebuild_now' => 'Rebuild nu', - - - 'committed_by_x' => 'Committed door %s', - 'commit_id_x' => 'Commit: %s', - - 'chart_display' => 'Deze grafiek wordt getoond zodra de build compleet is.', - - 'build' => 'Build', - 'lines' => 'Lijnen', - 'comment_lines' => 'Commentaarlijnen', - 'noncomment_lines' => 'Niet-commentaarlijnen', - 'logical_lines' => 'Logische lijnen', - 'lines_of_code' => 'Lijnen code', - 'build_log' => 'Build Log', - 'quality_trend' => 'Kwaliteitstrend', - 'codeception_errors' => 'Codeception Fouten', - 'phpmd_warnings' => 'PHPMD Waarschuwingen', - 'phpcs_warnings' => 'PHPCS Waarschuwingen', - 'phpcs_errors' => 'PHPCS Fouten', - 'phplint_errors' => 'Lint Fouten', - 'phpunit_errors' => 'PHPUnit Fouten', - 'phpdoccheck_warnings' => 'Ontbrekende Docblocks', - 'issues' => 'Problemen', - - 'codeception' => 'Codeception', - 'phpcpd' => 'PHP Copy/Paste Detector', - 'phpcs' => 'PHP Code Sniffer', - 'phpdoccheck' => 'Ontbrekende Docblocks', - 'phpmd' => 'PHP Mess Detector', - 'phpspec' => 'PHP Spec', - 'phpunit' => 'PHPUnit', - - 'file' => 'Bestand', - 'line' => 'Lijn', - 'class' => 'Class', - 'method' => 'Method', - 'message' => 'Boodschap', - 'start' => 'Start', - 'end' => 'Einde', - 'from' => 'Van', - 'to' => 'Tot', - 'result' => 'Resultaat', - 'ok' => 'OK', - 'took_n_seconds' => 'Duurde %d seconden', - 'build_started' => 'Build gestart', - 'build_finished' => 'Build beëindigd', - 'test_message' => 'Message', - 'test_no_message' => 'No message', - 'test_success' => 'Successful: %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', - 'password_change' => 'Wachtwoord (laat leeg indien je niet wenst te veranderen)', - 'save' => 'Opslaan »', - 'update_your_details' => 'Wijzig je gegevens', - 'your_details_updated' => 'Je gegevens werden gewijzigd', - 'add_user' => 'Gebruiker toevoegen', - 'is_admin' => 'Is administrator?', - 'yes' => 'Ja', - 'no' => 'Nee', - 'edit' => 'Wijzig', - 'edit_user' => 'Gebruiker wijzigen', - 'delete_user' => 'Gebruiker wissen', - 'user_n_not_found' => 'Gebruiker met ID %d bestaat niet.', - 'is_user_admin' => 'Is deze gebruiker administrator?', - 'save_user' => 'Gebruiker opslaan', - - // Settings: - 'settings_saved' => 'Je instellingen werden opgeslagen.', - 'settings_check_perms' => 'Je instellingen konden niet worden opgeslagen, controleer de permissies van je config.yml bestand.', - 'settings_cannot_write' => 'PHP Censor kan niet schrijven naar je config.yml bestand, instellingen worden mogelijks -niet goed opgeslagen tot dit opgelost is.', - 'settings_github_linked' => 'Je GitHub account werd gelinkt.', - 'settings_github_not_linked' => 'Je GitHub account kon niet gelinkt worden.', - 'build_settings' => 'Build instellingen', - 'github_application' => 'GitHub toepassing', - 'github_sign_in' => 'Vooraleer je GitHub kan gebruiken, dien je in te loggen en -PHP Censor toegang te verlenen tot je account.', - 'github_app_linked' => 'PHP werd succesvol gelinkt aan je GitHub account.', - 'github_where_to_find' => 'Waar zijn deze te vinden...', - 'github_where_help' => 'Indien je eigenaar bent van de toepassing die je wens te gebruiken, kan je deze informatie -in je applications instellingen pagina vinden.', - - 'email_settings' => 'E-mail instellingen', - 'email_settings_help' => 'Vooraleer PHP Censor je build status e-mails kan sturen, -dien je eerst je SMTP instellingen te configureren.', - - 'application_id' => 'Toepassings ID', - 'application_secret' => 'Toepassings geheime code', - - 'smtp_server' => 'SMTP Server', - 'smtp_port' => 'SMTP Poort', - 'smtp_username' => 'SMTP Gebruikersnaam', - 'smtp_password' => 'SMTP Wachtwoord', - 'from_email_address' => 'Van e-mailadres', - 'default_notification_address' => 'Standaard melding e-mailadres', - 'use_smtp_encryption' => 'SMTP Encryptie gebruiken?', - 'none' => 'Geen', - 'ssl' => 'SSL', - 'tls' => 'TLS', - - 'failed_after' => 'Beschouw een build gefaald na', - '5_mins' => '5 minuten', - '15_mins' => '15 minuten', - '30_mins' => '30 minuten', - '1_hour' => '1 uur', - '3_hours' => '3 uur', - - // Plugins - 'cannot_update_composer' => 'PHP Censor kan composer.json niet aanpassen gezien het niet schrijfbaar is.', - 'x_has_been_removed' => '%s werd verwijderd.', - 'x_has_been_added' => '%s werd toegevoegd aan composer.json en zal geïnstalleerd worden de volgende -keer je composer update uitvoert.', - 'enabled_plugins' => 'Ingeschakelde plugins', - 'provided_by_package' => 'Voorzien door package', - 'installed_packages' => 'Geinstalleerde packages', - 'suggested_packages' => 'Voorgestelde packages', - 'title' => 'Titel', - 'description' => 'Beschrijving', - 'version' => 'Versie', - 'install' => 'Installeer »', - 'remove' => 'Verwijder »', - 'search_packagist_for_more' => 'Doorzoek Packagist naar meer packages', - 'search' => 'Zoek »', - - // Update - 'update_app' => 'Update de database naar het beeld van gewijzigde modellen.', - 'updating_app' => 'PHP Censor database wordt geüpdatet:', - 'not_installed' => 'PHP Censor lijkt niet geïnstalleerd te zijn.', - 'install_instead' => 'Gelieve PHP Censor via php-censor:install te installeren.', - - // Build Plugins: - 'passing_build' => 'Slagende build', - 'failing_build' => 'Falende build', - 'log_output' => 'Log output:', -]; diff --git a/src/PHPCensor/Languages/lang.pl.php b/src/PHPCensor/Languages/lang.pl.php deleted file mode 100644 index f492cc4..0000000 --- a/src/PHPCensor/Languages/lang.pl.php +++ /dev/null @@ -1,292 +0,0 @@ - 'Polski', - 'language' => 'Język', - - // Log in: - 'log_in_to_app' => 'Zaloguj się do PHP Censor', - 'login_error' => 'Nieprawidłowy email lub hasło', - 'forgotten_password_link' => 'Zapomniałeś hasła?', - 'reset_emailed' => 'Email z linkiem resetującym hasło został wysłany.', - 'reset_header' => 'Spokojnie!
    Wpisz swój adres email w polu poniżej a my wyślemy Ci link -resetujący hasło.', - 'reset_email_address' => 'Podaj swój adres email:', - 'reset_send_email' => 'Wyślij reset hasła emailem', - 'reset_enter_password' => 'Wpisz nowe hasło', - 'reset_new_password' => 'Nowe hasło:', - 'reset_change_password' => 'Zmień hasło', - 'reset_no_user_exists' => 'Użytkownik o takim emailu nie istnieje. Spróbuj jeszcze raz.', - 'reset_email_body' => 'Witaj %s, - -Otrzymałeś ten email ponieważ Ty, lub ktoś inny, wysłał prośbę o zmianę hasła w PHP Censor. - -Jeśli to faktycznie Ty, kliknij w następujący link aby zresetować hasło: %ssession/reset-password/%d/%s - -Jeśli nie, zignoruj tego emaila i wszystko pozostanie bez zmian, - -Pozdrawiamy, - -PHP Censor', - - 'reset_email_title' => 'Reset Hasła PHP Censor 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ę', - - - // Top Nav - 'toggle_navigation' => 'Otwórz/zamknij nawigację', - 'n_builds_pending' => '%d budowań w kolejce', - 'n_builds_running' => '%d budowań w toku', - 'edit_profile' => 'Edytuj Profil', - 'sign_out' => 'Wyloguj się', - 'branch_x' => 'Gałąź: %s', - 'created_x' => 'Utworzono: %s', - 'started_x' => 'Rozpoczęto: %s', - - // Sidebar - 'hello_name' => 'Witaj, %s', - 'dashboard' => 'Panel administracyjny', - 'admin_options' => 'Opcje Administratora', - 'add_project' => 'Dodaj Projekt', - 'settings' => 'Ustawienia', - 'manage_users' => 'Zarządaj Uzytkownikami', - 'plugins' => 'Pluginy', - 'view' => 'Podgląd', - 'build_now' => 'Zbuduj', - 'edit_project' => 'Edytuj Projekt', - 'delete_project' => 'Usuń Projekt', - - // Project Summary: - 'no_builds_yet' => 'Brak budowań!', - 'x_of_x_failed' => '%d z ostatnich %d budowań nie powiodło się', - 'x_of_x_failed_short' => '%d / %d nie powiodło się', - 'last_successful_build' => 'Ostatnie budowanie zakończone sukesem odbyło się %s', - 'never_built_successfully' => 'Projekt nie został zbudowany z powodzeniem.', - 'all_builds_passed' => 'Wszystkie z ostatnich %d budowań przeszły.', - 'all_builds_passed_short' => '%d / %d przeszło.', - 'last_failed_build' => 'Ostatnie budowanie zakończone niepowodzeniam było %s.', - 'never_failed_build' => 'Ten projekt nigdy nie zakończył się niepowodzeniem budowania', - 'view_project' => 'Podgląd Projektu', - - // Timeline: - 'latest_builds' => 'Ostatnie Budowania', - 'pending' => 'Oczekujące', - 'running' => 'W toku', - 'success' => 'Sukces', - 'failed' => 'Nieudane', - 'manual_build' => 'Budowanie Manualne', - - // Add/Edit Project: - 'new_project' => 'Nowy Projekt', - 'project_x_not_found' => 'Projekt o ID %d nie istnieje.', - 'project_details' => 'Szczegóły Projektu', - 'public_key_help' => 'Aby łatwiej zacząć, wygenerowaliśmy parę kluczy SSH, które możesz użyć -do tego projektu. Żeby je użyć, wystarczy dodać następujący klucz publiczny do sekcji "wdrożyć klucze" -od wybranego kodu źródłowego platformy hostingowej.', - 'select_repository_type' => 'Wybierz typ repozytorium...', - 'github' => 'GitHub', - 'bitbucket' => 'Bitbucket', - 'gitlab' => 'GitLab', - 'remote' => 'Zdalny URL ', - 'local' => 'Lokalna Ścieżka ', - 'hg' => 'Mercurial', - 'svn' => 'Subversion', - - 'where_hosted' => 'Gdzie hostowany jest Twój projekt?', - 'choose_github' => 'Wybierz repozytorium GitHub:', - - 'repo_name' => 'Nazwa repozytorium / URL (Zdalne) lub Ścieżka (Lokalne)', - 'project_title' => 'Tytuł Projektu', - 'project_private_key' => 'Prywanty klucz dostępu do repozytoriów -(pozostaw puste pole dla zdalnych lokalnych i/lub anonimowych)', - 'build_config' => 'PHP Censor zbudowało config dla tego projektu -(jeśli nie możesz dodać pliku .php-censor.yml (.phpci.yml|phpci.yml) do repozytorium projektu)', - 'default_branch' => 'Domyślna nazwa gałęzi', - 'allow_public_status' => 'Włączyć status publiczny dla tego projektu?', - 'archived' => 'W archiwum', - 'archived_menu' => 'W archiwum', - 'save_project' => 'Zachowaj Projekt', - - 'error_mercurial' => 'URL repozytorium Mercurialnego powinno zaczynać się od http:// and https://', - 'error_remote' => 'URL repozytorium powinno zaczynać się od git://, http:// lub https://', - 'error_gitlab' => 'Nazwa Repozytorium GitLab powinna być w następującym formacie: "user@domain.tld:owner/repo.git"', - 'error_github' => 'Nazwa repozytorium powinna być w formacie: "użytkownik/repo"', - 'error_bitbucket' => 'Nazwa repozytorium powinna być w formacie: " użytkownik/repo\'', - 'error_path' => 'Wybrana sieżka nie istnieje', - - // View Project: - 'all_branches' => 'Wszystkie Gałęzie', - 'builds' => 'Budowania', - 'id' => 'ID', - 'date' => 'Data', - 'project' => 'Projekt', - 'commit' => 'Commit', - 'branch' => 'Gałąź', - 'status' => 'Status', - 'prev_link' => '« Poprzedni', - 'next_link' => 'Następny »', - 'public_key' => 'Klucz Publiczny', - 'delete_build' => 'Usuń Budowanie', - - 'webhooks' => 'Webhooks', - 'webhooks_help_github' => 'Aby automatycznie uruchomić nową budowę po wysłaniu commitów dodaj poniższy adres URL - jako nowy "WebHook" w sekcji Webhooks and Services - Twojego repozytoria GitLab.', - - 'webhooks_help_gitlab' => 'Aby automatycznie uruchomić nową budowę po wysłaniu commitów dodaj poniższy adres URL - jako "WebHook URL" w sekcji Web Hook Twojego repozytoria GitLab.', - - 'webhooks_help_bitbucket' => 'Aby automatycznie uruchomić nową budowę po wysłaniu commitów, dodaj poniższy adres URL - jako usługę "POST" w sekcji - -Services repozytoria Bitbucket.', - - // View Build - 'build_x_not_found' => 'Budowanie o ID %d nie istnieje.', - 'build_n' => 'Budowanie %d', - 'rebuild_now' => 'Przebuduj Teraz', - - - 'committed_by_x' => 'Commitowane przez %s', - 'commit_id_x' => 'Commit: %s', - - 'chart_display' => 'Ten wykres wyświetli się po zakończeniu budowy.', - - 'build' => 'Budowanie', - 'lines' => 'Linie', - 'comment_lines' => 'Linie Komentarza', - 'noncomment_lines' => 'Linie Bez Komentarza', - 'logical_lines' => 'Lokalne Linie', - 'lines_of_code' => 'Linie Kodu', - 'build_log' => 'Log Budowania', - 'quality_trend' => 'Trend Jakości', - 'codeception_errors' => 'Błędy Codeception', - 'phpmd_warnings' => 'Alerty PHPMD', - 'phpcs_warnings' => 'Alerty PHPCS', - 'phpcs_errors' => 'Błędy PHPCS', - 'phplint_errors' => 'Błędy Lint', - 'phpunit_errors' => 'Błędy PHPUnit', - 'phpdoccheck_warnings' => 'Brakuje sekcji DocBlock', - 'issues' => 'Problemy', - - 'codeception' => 'Codeception', - 'phpcpd' => 'PHP Copy/Paste Detector', - 'phpcs' => 'PHP Code Sniffer', - 'phpdoccheck' => 'Brakuje sekcji DocBlock', - 'phpmd' => 'PHP Mess Detector', - 'phpspec' => 'PHPSpec', - 'phpunit' => 'PHPUnit', - 'technical_debt' => 'Dług technologiczny', - 'behat' => 'Behat', - - 'file' => 'Plik', - 'line' => 'Linia', - 'class' => 'Klasa', - 'method' => 'Metoda', - 'message' => 'Wiadomość', - 'start' => 'Początek', - 'end' => 'Koniec', - 'from' => 'Od', - 'to' => 'Do', - 'result' => 'Wynik', - 'ok' => 'OK', - 'took_n_seconds' => 'Zajęło %d sekund', - 'build_started' => 'Budowanie Rozpoczęte', - 'build_finished' => 'Budowanie Zakończone', - 'test_message' => 'Wiadomość', - 'test_no_message' => 'Brak wiadomości', - 'test_success' => 'Powodzenie: %d', - 'test_fail' => 'Niepowodzenia: %d', - 'test_skipped' => 'Pominęte: %d', - 'test_error' => 'Błędy: %d', - 'test_todo' => 'Do zrobienia: %d', - 'test_total' => '%d test(ów)', - - // Users - 'name' => 'Nazwa', - 'password_change' => 'Hasło (pozostaw puste jeśli nie chcesz zmienić hasła)', - 'save' => 'Zapisz »', - 'update_your_details' => 'Aktualizuj swoje dane', - 'your_details_updated' => 'Twoje dane zostały zaktualizowane.', - 'add_user' => 'Dodaj Użytkownika', - 'is_admin' => 'Jest Adminem?', - 'yes' => 'Tak', - 'no' => 'Nie', - 'edit' => 'Edytuj', - 'edit_user' => 'Edytuj Użytkownika', - 'delete_user' => 'Usuń Użytkownika', - 'user_n_not_found' => 'Użytkownik z ID %d nie istnieje.', - 'is_user_admin' => 'Czy użytkownik jest administratorem?', - 'save_user' => 'Zapisz Użytkownika', - - // Settings: - 'settings_saved' => 'Ustawienia zostały zapisane.', - 'settings_check_perms' => 'Twoje ustawienia nie mogły zostać zapisane. Sprawdź uprawnienia do pliku config.yml.', - 'settings_cannot_write' => 'PHP Censor nie może zapisać do pliku config.yml. Dopóty nie będzie można poprawnie zachować ustawie, -dopóki nie będzie to naprawione.', - 'settings_github_linked' => 'Połaczono z Twoim kontem Github', - 'settings_github_not_linked' => 'Nie udało się połaczyć z Twoim kontem Github', - 'build_settings' => 'Ustawienia budowania', - 'github_application' => 'Aplikacja GitHub', - 'github_sign_in' => 'Zanim będzie można zacząć korzystać z GitHub, musisz najpierw Sign in, a następnie udzielić dostęp dla PHP Censor do Twojego konta.', - 'github_app_linked' => 'PHP Censor zostało pomyślnie połączone z konten GitHub.', - 'github_where_to_find' => 'Gdzie można znaleźć...', - 'github_where_help' => 'Jeśli to jest Twoja aplikacjia i chcesz jej użyć to więcej informacji znajdziesz w sekcji ustawień: - applications', - - 'email_settings' => 'Ustawienia Email', - 'email_settings_help' => 'Aby PHP Censor mógł wysyłać emaile z stanem budowy, musisz najpierw skonfigurować poniższe ustawienia SMTP.', - - 'application_id' => 'ID Aplikacji', - 'application_secret' => 'Klucz Secret aplikacji', - - 'smtp_server' => 'Serwer SMTP', - 'smtp_port' => 'Port SMTP', - 'smtp_username' => 'SMTP Login', - 'smtp_password' => 'Hasło SMTP', - 'from_email_address' => 'E-mail adres Od:', - 'default_notification_address' => 'Domyślny adres email powiadamiania', - 'use_smtp_encryption' => 'Użyć szyfrowane SMTP?', - 'none' => 'Żadne', - 'ssl' => 'SSL', - 'tls' => 'TLS', - - 'failed_after' => 'Uznaj, że budowanie nie powiodło się po', - '5_mins' => '5 Minutach', - '15_mins' => '15 Minutach', - '30_mins' => '30 Minutach', - '1_hour' => '1 Godzinie', - '3_hours' => '3 Godzinach', - - // Plugins - 'cannot_update_composer' => 'PHP Censor nie może zaktualizować copmposer.json, ponieważ nie ma uprawnień do zapisu.', - 'x_has_been_removed' => 'Usunięto %s. ', - 'x_has_been_added' => 'Dodano %s do composer.json. Zostanie zainstalowane po -wywołaniu polecenia composer update.', - 'enabled_plugins' => 'Aktywne Pluginy', - 'provided_by_package' => 'Dostarczone w pakiecie', - 'installed_packages' => 'Zainstalowane Pakiety', - 'suggested_packages' => 'Sugerowane Pakiety', - 'title' => 'Tytuł', - 'description' => 'Opis', - 'version' => 'Wersja', - 'install' => 'Zainstaluj »', - 'remove' => 'Usuń »', - 'search_packagist_for_more' => 'Przeszukaj Packagist po więcej pakietów', - 'search' => 'Szukaj »', - - // Update - 'update_app' => 'Zaktualizuj bazę danych zgodnie ze zmodyfikowanymi modelami.', - 'updating_app' => 'Aktualizacja bazy danych PHP Censor:', - 'not_installed' => 'Wygląda na to, że PHP Censor nie jest zainstalowane.', - 'install_instead' => 'Proszę zainstalować PHP Censor poprzez php-censor:install', - - // Build Plugins: - 'passing_build' => 'Pomijanie Budowania', - 'failing_build' => 'Niepowodzenie Budowania', - 'log_output' => 'Log Wyjściowy:', -]; diff --git a/src/PHPCensor/Languages/lang.pt-br.php b/src/PHPCensor/Languages/lang.pt-br.php deleted file mode 100644 index c28eb22..0000000 --- a/src/PHPCensor/Languages/lang.pt-br.php +++ /dev/null @@ -1,328 +0,0 @@ - 'Português Brasil', - 'language' => 'Idioma', - - // Log in: - 'log_in_to_app' => 'Acessar o PHP Censor', - 'login_error' => 'Email ou senha incorretos', - 'forgotten_password_link' => 'Perdeu sua senha?', - 'reset_emailed' => 'We\'ve emailed you a link to reset your password.', - 'reset_header' => 'Não se preocupe!
    Basta digitar o seu endereço de e-mail abaixo e nós lhe enviaremos um email com um link para redefinir sua senha.', - 'reset_email_address' => 'Digite seu endereço de e-mail:', - 'reset_send_email' => 'Solicitar nova senha', - 'reset_enter_password' => 'Please enter a new password', - 'reset_new_password' => 'New password:', - 'reset_change_password' => 'Change password', - 'reset_no_user_exists' => 'No user exists with that email address, please try again.', - 'reset_email_body' => 'Hi %s, - -You have received this email because you, or someone else, has requested a password reset for PHP Censor. - -If this was you, please click the following link to reset your password: %ssession/reset-password/%d/%s - -Otherwise, please ignore this email and no action will be taken. - -Thank you, - -PHP Censor', - - 'reset_email_title' => 'PHP Censor Password Reset for %s', - 'reset_invalid' => 'Invalid password reset request.', - 'email_address' => 'Endereço de e-mail', - 'login' => 'Login / Email Address', - 'password' => 'Senha', - 'log_in' => 'Acessar', - - - // Top Nav - 'toggle_navigation' => 'Toggle Navigation', - 'n_builds_pending' => '%d builds pending', - 'n_builds_running' => '%d builds running', - 'edit_profile' => 'Editar Perfil', - 'sign_out' => 'Sair', - 'branch_x' => 'Branch: %s', - 'created_x' => 'Criado: %s', - 'started_x' => 'Iniciado: %s', - - // Sidebar - 'hello_name' => 'Olá, %s', - 'dashboard' => 'Dashboard', - 'admin_options' => 'Opções do Admin', - 'add_project' => 'Adicionar Projeto', - 'settings' => 'Configurações', - 'manage_users' => 'Gerênciar Usuários', - 'plugins' => 'Plugins', - 'view' => 'View', - 'build_now' => 'Compilar Agora', - 'edit_project' => 'Editar Projeto', - 'delete_project' => 'Deletar Projeto', - - // Project Summary: - 'no_builds_yet' => 'Nenhuma compilação ainda!', - 'x_of_x_failed' => '%d out of the last %d builds failed.', - 'x_of_x_failed_short' => '%d / %d failed.', - 'last_successful_build' => ' The last successful build was %s.', - 'never_built_successfully' => ' This project has never built successfully.', - 'all_builds_passed' => 'All of the last %d builds passed.', - 'all_builds_passed_short' => '%d / %d passed.', - 'last_failed_build' => ' The last failed build was %s.', - 'never_failed_build' => ' This project has never failed a build.', - 'view_project' => 'Ver projeto', - - // Timeline: - 'latest_builds' => 'Últimas compilações', - 'pending' => 'Pendente', - 'running' => 'Correndo', - 'success' => 'Sucesso', - 'failed' => 'Fracassado', - 'manual_build' => 'Compilação manual', - - // Add/Edit Project: - 'new_project' => 'New Project', - 'project_x_not_found' => 'Project with ID %d does not exist.', - 'project_details' => 'Detalhes do Projeto', - 'public_key_help' => 'Para tornar mais fácil de começar, Geramos Um par de chaves SSH para você usar para este projeto. Para usá-lo, basta adicionar a seguinte chave pública na seção "deploy keys" no provedor onde hospeda seu código.', - 'select_repository_type' => 'Selecione o tipo de repositório...', - 'github' => 'GitHub', - 'bitbucket' => 'Bitbucket', - 'gitlab' => 'GitLab', - 'remote' => 'Remote URL', - 'local' => 'Local Path', - 'hg' => 'Mercurial', - 'svn' => 'Subversion', - - 'where_hosted' => 'Onde seu projeto está hospedado?', - 'choose_github' => 'Choose a GitHub repository:', - - 'repo_name' => 'Nome do repositório / URL (Remota) ou Caminho (Local)', - 'project_title' => 'Titulo do projeto', - 'project_private_key' => 'Chave privada usada para acessar o repositório - (Deixe em branco para controles remotos locais e/ou anônimos)', - 'build_config' => 'PHP Censor construir configuração para este projeto - (if you cannot add a .php-censor.yml (.phpci.yml|phpci.yml) file in the project repository)', - 'default_branch' => 'Nome padrão do branch', - 'allow_public_status' => 'Habilitar página de status pública e imagem para este projeto?', - 'archived' => 'Arquivado', - 'archived_menu' => 'Arquivado', - 'save_project' => 'Salvar Projeto', - - 'error_mercurial' => 'Mercurial repository URL must be start with http:// or https://', - 'error_remote' => 'Repository URL must be start with git://, http:// or https://', - 'error_gitlab' => 'GitLab Repository name must be in the format "user@domain.tld:owner/repo.git"', - 'error_github' => 'Repository name must be in the format "owner/repo"', - 'error_bitbucket' => 'Repository name must be in the format "owner/repo"', - 'error_path' => 'The path you specified does not exist.', - - // View Project: - 'all_branches' => 'All Branches', - 'builds' => 'Builds', - 'id' => 'ID', - 'date' => 'Date', - 'project' => 'Project', - 'commit' => 'Commit', - 'branch' => 'Branch', - 'status' => 'Status', - 'prev_link' => '« Prev', - 'next_link' => 'Next »', - 'public_key' => 'Public Key', - 'delete_build' => 'Delete Build', - - 'webhooks' => 'Webhooks', - 'webhooks_help_github' => 'To automatically build this project when new commits are pushed, add the URL below - as a new "Webhook" in the Webhooks - and Services section of your GitHub repository.', - - 'webhooks_help_gitlab' => 'To automatically build this project when new commits are pushed, add the URL below - as a "WebHook URL" in the Web Hooks section of your GitLab repository.', - - 'webhooks_help_bitbucket' => 'To automatically build this project when new commits are pushed, add the URL below - as a "POST" service in the - - Services section of your Bitbucket repository.', - - // View Build - 'errors' => 'Erros', - 'information' => 'Informação', - - 'build_x_not_found' => 'Build with ID %d does not exist.', - 'build_n' => 'Compilação %d', - 'rebuild_now' => 'Recompilar Agora', - - - 'committed_by_x' => 'Committed by %s', - 'commit_id_x' => 'Commit: %s', - - 'chart_display' => 'This chart will display once the build has completed.', - - 'build' => 'Build', - 'lines' => 'Lines', - 'comment_lines' => 'Comment lines', - 'noncomment_lines' => 'Non-Comment lines', - 'logical_lines' => 'Logical lines', - 'lines_of_code' => 'Lines of code', - 'build_log' => 'Log de compilação', - 'quality_trend' => 'Quality trend', - 'codeception_errors' => 'Codeception errors', - 'phpmd_warnings' => 'PHPMD warnings', - 'phpcs_warnings' => 'PHPCS warnings', - 'phpcs_errors' => 'PHPCS errors', - 'phplint_errors' => 'Lint errors', - 'phpunit_errors' => 'PHPUnit errors', - 'phpdoccheck_warnings' => 'Missing docblocks', - 'issues' => 'Issues', - - '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' => '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', - 'method' => 'Method', - 'message' => 'Message', - 'start' => 'Start', - 'end' => 'End', - 'from' => 'From', - 'to' => 'To', - 'result' => 'Result', - 'ok' => 'OK', - 'took_n_seconds' => 'Took %d seconds', - 'build_started' => 'Build Started', - 'build_finished' => 'Build Finished', - 'test_message' => 'Message', - 'test_no_message' => 'No message', - 'test_success' => 'Successful: %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', - 'password_change' => 'Password (leave blank if you don\'t want to change)', - 'save' => 'Save »', - 'update_your_details' => 'Update your details', - 'your_details_updated' => 'Your details have been updated.', - 'add_user' => 'Adicionar Usuário', - 'is_admin' => 'É Administrador?', - 'yes' => 'Sim', - 'no' => 'Não', - 'edit' => 'Edit.', - 'edit_user' => 'Editar Usuário', - 'delete_user' => 'Deletar Usuário', - 'user_n_not_found' => 'User with ID %d does not exist.', - 'is_user_admin' => 'Este usuário é um administrador?', - 'save_user' => 'Salvar Usuário', - - // Settings: - 'settings_saved' => 'Your settings have been saved.', - 'settings_check_perms' => 'Your settings could not be saved, check the permissions of your config.yml file.', - 'settings_cannot_write' => 'PHP Censor cannot write to your config.yml file, settings may not be saved properly - until this is rectified.', - 'settings_github_linked' => 'Your GitHub account has been linked.', - 'settings_github_not_linked' => 'Your GitHub account could not be linked.', - 'build_settings' => 'Build Settings', - 'github_application' => 'GitHub Application', - 'github_sign_in' => 'Before you can start using GitHub, you need to sign in and grant - PHP Censor access to your account.', - 'github_linked' => 'PHP Censor is successfully linked to GitHub account.', - 'github_where_to_find' => 'Where to find these...', - 'github_where_help' => 'If you own the application you would like to use, you can find this information within your - applications settings area.', - - 'email_settings' => 'Email Settings', - 'email_settings_help' => 'Before PHP Censor can send build status emails, - you need to configure your SMTP settings below.', - - 'application_id' => 'Application ID', - 'application_secret' => 'Application Secret', - - 'smtp_server' => 'SMTP Server', - 'smtp_port' => 'SMTP Port', - 'smtp_username' => 'SMTP Username', - 'smtp_password' => 'SMTP Password', - 'from_email_address' => 'From Email Address', - 'default_notification_address' => 'Default Notification Email Address', - 'use_smtp_encryption' => 'Use SMTP Encryption?', - 'none' => 'None', - 'ssl' => 'SSL', - 'tls' => 'TLS', - - 'failed_after' => 'Consider a build failed after', - '5_mins' => '5 Minutes', - '15_mins' => '15 Minutes', - '30_mins' => '30 Minutes', - '1_hour' => '1 Hour', - '3_hours' => '3 Hours', - - // Plugins - 'cannot_update_composer' => 'PHP Censor cannot update composer.json for you as it is not writable.', - 'x_has_been_removed' => '%s has been removed.', - 'x_has_been_added' => '%s has been added to composer.json for you and will be installed next time - you run composer update.', - 'enabled_plugins' => 'Plugins Habilitados', - 'provided_by_package' => 'Provided By Package', - 'installed_packages' => 'Pacotes instalados', - 'suggested_packages' => 'Suggested Packages', - 'title' => 'Title', - 'description' => 'Description', - 'version' => 'Version', - 'install' => 'Install »', - 'remove' => 'Remove »', - 'search_packagist_for_more' => 'Search Packagist for more packages', - 'search' => 'Search »', - - // Summary plugin - 'build-summary' => 'Summary', - 'stage' => 'Stage', - 'duration' => 'Duration', - 'plugin' => 'Plugin', - 'stage_setup' => 'Setup', - 'stage_test' => 'Test', - 'stage_complete' => 'Complete', - 'stage_success' => 'Success', - 'stage_failure' => 'Failure', - 'stage_broken' => 'Broken', - 'stage_fixed' => 'Fixed', - - // Update - 'update_app' => 'Update the database to reflect modified models.', - 'updating_app' => 'Updating PHP Censor database: ', - 'not_installed' => 'PHP Censor does not appear to be installed.', - 'install_instead' => 'Please install PHP Censor via php-censor:install instead.', - - // Build Plugins: - 'passing_build' => 'Passing Build', - 'failing_build' => 'Failing Build', - 'log_output' => 'Log Output: ', - - // Error Levels: - 'critical' => 'Critical', - 'high' => 'High', - 'normal' => 'Normal', - 'low' => 'Low', - - // Plugins that generate errors: - 'php_mess_detector' => 'PHP Mess Detector', - 'php_code_sniffer' => 'PHP Code Sniffer', - 'php_unit' => 'PHP Unit', - 'php_cpd' => 'PHP Copy/Paste Detector', - 'php_docblock_checker' => 'PHP Docblock Checker', - 'behat' => 'Behat', - 'technical_debt' => 'Technical Debt', -]; diff --git a/src/PHPCensor/Languages/lang.ru.php b/src/PHPCensor/Languages/lang.ru.php deleted file mode 100644 index a4f680a..0000000 --- a/src/PHPCensor/Languages/lang.ru.php +++ /dev/null @@ -1,408 +0,0 @@ - 'Русский', - 'language' => 'Язык', - 'per_page' => 'Количество элементов на странице', - 'default' => 'По умолчанию', - - // Log in: - 'log_in_to_app' => 'Войти в PHP Censor', - 'login_error' => 'Неправильный email или пароль', - 'forgotten_password_link' => 'Забыли пароль?', - 'reset_emailed' => 'Вы получите письмо со ссылкой на сброс пароля.', - 'reset_header' => 'Не волнуйтесь!
    Просто введите ваш email, и вам придет письмо со ссылкой на сброс пароля.', - 'reset_email_address' => 'Введите ваш email:', - 'reset_send_email' => 'Сброс пароля', - 'reset_enter_password' => 'Пожалуйста, введите новый пароль', - 'reset_new_password' => 'Новый пароль:', - 'reset_change_password' => 'Сменить пароль', - 'reset_no_user_exists' => 'Пользователь с таким email-адресом не найден, пожалуйста, попробуйте еще раз.', - 'reset_email_body' => 'Привет %s, - -Вы получили это письмо, потому что вы или кто-то другой запросили сброс пароля в PHP Censor. - -Если это были вы, пожалуйста, перейдите по ссылке для сброса пароля: %ssession/reset-password/%d/%s, - -иначе игнорируйте это письмо и ничего не предпринимайте. - -Спасибо, - -PHP Censor', - - 'reset_email_title' => 'Сброс пароля PHP Censor для %s', - 'reset_invalid' => 'Некорректный запрос на сброс пароля.', - 'email_address' => 'Email', - 'login' => 'Логин / Email', - 'password' => 'Пароль', - 'remember_me' => 'Запомнить меня', - 'log_in' => 'Войти', - - // Top Nav - 'toggle_navigation' => 'Скрыть/показать панель навигации', - 'n_builds_pending' => 'Сборок ожидает: %d', - 'n_builds_running' => 'Сборок запущено: %d', - 'edit_profile' => 'Редактировать профиль', - 'sign_out' => 'Выйти', - 'branch_x' => 'Ветка: %s', - 'created_x' => 'Создан: %s', - 'started_x' => 'Запущен: %s', - 'environment_x' => 'Окружение: %s', - - // Sidebar - 'hello_name' => 'Привет, %s', - 'dashboard' => 'Панель управления', - 'admin_options' => 'Меню администратора', - 'add_project' => 'Добавить проект', - 'project_groups' => 'Группы проектов', - 'settings' => 'Настройки', - 'manage_users' => 'Пользователи', - 'plugins' => 'Плагины', - 'view' => 'Отчет', - 'build_now' => 'Собрать', - 'build_now_debug' => 'Собрать в режиме отладки', - 'edit_project' => 'Редактировать проект', - 'delete_project' => 'Удалить проект', - - // Project Summary: - 'no_builds_yet' => 'Нет сборок!', - 'x_of_x_failed' => '%d из последних %d сборок были провалены.', - 'x_of_x_failed_short' => '%d / %d провалены.', - 'last_successful_build' => ' Последняя успешная сборка была %s.', - 'never_built_successfully' => ' Этот проект никогда не собирался успешно.', - 'all_builds_passed' => 'Все последние сборки (%d) прошли успешно.', - 'all_builds_passed_short' => '%d / %d успешные.', - 'last_failed_build' => ' Последней проваленной сборкой была %s.', - 'never_failed_build' => ' У этого проекта никогда не было проваленных сборок.', - 'view_project' => 'Обзор проекта', - 'projects_with_build_errors' => 'Ошибки сборки', - - // Timeline: - 'latest_builds' => 'Последние сборки', - 'pending' => 'Ожидает', - 'running' => 'Запущена', - 'success' => 'Успешно', - 'failed' => 'Провал', - 'failed_allowed' => 'Провал (Допустим)', - 'error' => 'Ошибка', - 'skipped' => 'Пропущено', - 'trace' => 'Стек вызова', - 'manual_build' => 'Запущена вручную', - - // Add/Edit Project: - 'new_project' => 'Новый проект', - 'project_x_not_found' => 'Проекта с ID %d не существует.', - 'project_details' => 'Подробности проекта', - 'public_key_help' => 'Чтобы было легче начать, мы сгенерировали пару SSH-ключей для использования в вашем проекте. - Для их использования, просто добавьте эту публичную часть ключа в поле "deploy keys" на выбранном вами хостинге исходного кода.', - 'select_repository_type' => 'Выберите тип репозитория...', - 'github' => 'GitHub', - 'bitbucket' => 'Bitbucket', - 'gitlab' => 'GitLab', - 'remote' => 'Внешний URL', - 'local' => 'Локальный путь', - 'hg' => 'Mercurial', - 'svn' => 'Subversion', - - 'where_hosted' => 'Расположение проекта', - 'choose_github' => 'Выберите GitHub репозиторий:', - - 'repo_name' => 'Репозиторий / Внешний URL / Локальный путь', - 'project_title' => 'Название проекта', - 'project_private_key' => 'Приватный ключ для доступа к репозиторию - (оставьте поле пустым для локального использования и/или анонимного доступа)', - 'build_config' => 'Конфигурация сборки проекта для PHP Censor - (если вы не добавили файл .php-censor.yml (.phpci.yml|phpci.yml) в репозиторий вашего проекта)', - 'default_branch' => 'Ветка по умолчанию', - 'default_branch_only' => 'Собирать только ветку по умолчанию', - 'allow_public_status' => 'Разрешить публичный статус и изображение (статуса) для проекта', - 'archived' => 'Архивный', - 'archived_menu' => 'Архив', - 'save_project' => 'Сохранить проект', - 'environments_label' => 'Окружения (yaml)', - - 'error_mercurial' => 'URL репозитория Mercurial должен начинаться с http:// или https://', - 'error_remote' => 'URL репозитория должен начинаться с git://, http:// или https://', - 'error_gitlab' => 'Имя репозитория в GitLab должно иметь формат: "user@domain.tld:owner/repo.git"', - 'error_github' => 'Имя репозитория должно иметь формат: "owner/repo"', - 'error_bitbucket' => 'Имя репозитория должно иметь формат: "owner/repo"', - 'error_path' => 'Пути, который вы указали, не существует.', - - // View Project: - 'all_branches' => 'Все ветки', - 'all' => 'Все', - 'builds' => 'Сборки', - 'id' => 'ID', - 'date' => 'Дата', - 'project' => 'Проект', - 'commit' => 'Коммит', - 'branch' => 'Ветка', - 'environment' => 'Окружение', - 'status' => 'Статус', - 'prev_link' => '« Пред.', - 'next_link' => 'След. »', - 'public_key' => 'Публичный ключ', - 'delete_build' => 'Удалить сборку', - 'build_source' => 'Источник сборки', - - 'source_unknown' => 'Неизвестный', - 'source_manual_web' => 'Вручную (Через Web)', - 'source_manual_console' => 'Вручную (Через CLI)', - 'source_periodical' => 'Переодический', - 'source_webhook' => 'Webhook', - - 'webhooks' => 'Webhooks', - 'webhooks_help_github' => 'Чтобы Автоматически собирать этот проект при публикации новых коммитов, добавьте URL ниже в качестве нового хука в разделе настроек Webhooks - and Services вашего GitHub репозитория.', - - 'webhooks_help_gitlab' => 'Чтобы Автоматически собирать этот проект при публикации новых коммитов, добавьте URL ниже в качестве "WebHook URL" - в разделе "Web Hooks" вашего GitLab репозитория.', - - 'webhooks_help_bitbucket' => 'Чтобы Автоматически собирать этот проект при публикации новых коммитов, добавьте URL ниже как "POST" сервис в разделе - Services вашего Bitbucket репозитория.', - - // Project Groups - 'group_projects' => 'Группы проектов', - 'project_group' => 'Группа проекта', - 'group_count' => 'Количество проектов', - 'group_edit' => 'Редактировать', - 'group_delete' => 'Удалить', - 'group_add' => 'Добавить группу', - 'group_add_edit' => 'Добавить / изменить группу', - 'group_title' => 'Название группы', - 'group_save' => 'Сохранить группу', - - // View Build - 'errors' => 'Ошибки', - 'information' => 'Информация', - 'build_x_not_found' => 'Сборки с ID %d не существует.', - 'build_n' => 'Сборка %d', - 'rebuild_now' => 'Пересобрать сейчас', - - 'committed_by_x' => 'Отправил %s', - 'commit_id_x' => 'Коммит: %s', - - 'chart_display' => 'Этот график будет показан после окончания сборки.', - - 'build' => 'Сборка', - 'lines' => 'Строк', - 'comment_lines' => 'Строк комментариев', - 'noncomment_lines' => 'Строк некомментариев', - 'logical_lines' => 'Строк логики', - 'lines_of_code' => 'Строк кода', - 'build_log' => 'Лог сборки', - 'quality_trend' => 'Тенденция качества', - 'codeception_errors' => 'Ошибки Codeception', - 'phpmd_warnings' => 'Предупреждения PHPMD', - 'phpcs_warnings' => 'Предупреждения PHPCS', - 'phpcs_errors' => 'Ошибки PHPCS', - 'phplint_errors' => 'Ошибки Lint', - 'phpunit_errors' => 'Ошибки PHPUnit', - 'phpcpd_warnings' => 'Предупреждения PHP Copy/Paste Detector', - 'phpdoccheck_warnings' => 'Пропущенные Docblocks', - 'issues' => 'Проблемы', - 'merged_branches' => 'Объеденяемые ветки', - - 'phpcpd' => 'PHP Copy/Paste Detector', - 'phpcs' => 'PHP Code Sniffer', - 'phpdoccheck' => 'Missing Docblocks', - 'phpmd' => 'PHP Mess Detector', - 'phpspec' => 'PHP Spec', - 'phpunit' => 'PHP Unit', - - 'codeception_feature' => 'Свойство', - 'codeception_suite' => 'Набор', - 'codeception_time' => 'Время', - 'codeception_synopsis' => 'Тестов выполнено: %1$d (за %2$f сек.). Провалов: %3$d.', - - 'suite' => 'Набор', - 'test' => 'Тест', - 'file' => 'Файл', - 'line' => 'Строка', - 'class' => 'Класс', - 'method' => 'Метод', - 'message' => 'Сообщение', - 'start' => 'Начало', - 'end' => 'Конец', - 'from' => 'Из', - 'to' => 'В', - 'result' => 'Результат', - 'ok' => 'OK', - 'took_n_seconds' => 'Заняло секунд: %d', - 'build_started' => 'Сборка запущена', - 'build_finished' => 'Сборка окончена', - 'test_message' => 'Сообщение', - 'test_no_message' => 'Нет сообщений', - 'test_success' => 'Успешно: %d', - 'test_fail' => 'Провалено: %d', - 'test_skipped' => 'Пропущено: %d', - 'test_error' => 'Ошибок: %d', - 'test_todo' => 'Todo: %d', - 'test_total' => 'Тестов: %d', - - // Users - 'name' => 'Имя', - 'password_change' => 'Пароль (оставьте поле пустым, если не собираетесь менять его)', - 'save' => 'Сохранить »', - 'update_your_details' => 'Обновить свои данные', - 'your_details_updated' => 'Ваши данные были обновлены.', - 'add_user' => 'Добавить пользователя', - 'is_admin' => 'Является администратором', - 'yes' => 'Да', - 'no' => 'Нет', - 'edit' => 'Править', - 'edit_user' => 'Редактировать пользователя', - 'delete_user' => 'Удалить пользователя', - 'user_n_not_found' => 'Пользователя с ID %d не существует.', - 'is_user_admin' => 'Этот пользователь является администратором', - 'save_user' => 'Сохранить пользователя', - - // Settings: - 'settings_saved' => 'Ваши настройки были сохранены.', - 'settings_check_perms' => 'Ваши настройки не могут быть сохранены, проверьте права на файл настроек config.yml.', - 'settings_cannot_write' => 'PHP Censor не может записать config.yml файл, настройки не могут быть сохранены корректно, пока это не будет исправлено.', - 'settings_github_linked' => 'Ваш GitHub аккаунт привязан.', - 'settings_github_not_linked' => 'Ваш GitHub аккаунт не может быть привязан.', - 'build_settings' => 'Настройки сборки', - 'github_application' => 'GitHub приложение', - 'github_sign_in' => 'Перед тем как начать использовать GitHub аккаунт, вам необходимо войти и разрешить доступ для - PHP Censor до вашего аккаунта.', - 'github_app_linked' => 'PHP Censor успешно привязал GitHub аккаунт.', - 'github_where_to_find' => 'Где это найти...', - 'github_where_help' => 'Если вы владелец приложения, которое вы хотели бы использовать, то вы можете найти информацию об этом в разделе - applications настроек.', - - 'email_settings' => 'Настройки email', - 'email_settings_help' => 'Перед тем, как PHP Censor начнет отсылать статус сборок по почте, - вам необходимо настроить параметры SMTP ниже.', - - 'application_id' => 'ID приложения', - 'application_secret' => 'Секретный ключ приложения', - - 'smtp_server' => 'SMTP сервер', - 'smtp_port' => 'SMTP порт', - 'smtp_username' => 'SMTP пользователь', - 'smtp_password' => 'SMTP пароль', - 'from_email_address' => 'Отправлять с email', - 'default_notification_address' => 'Email по умолчанию для оповещений', - 'use_smtp_encryption' => 'Использовать SMTP шифрование', - 'none' => 'Нет', - 'ssl' => 'SSL', - 'tls' => 'TLS', - - 'failed_after' => 'Признать сборку проваленной по прошествии', - '5_mins' => '5 минут', - '15_mins' => '15 минут', - '30_mins' => '30 минут', - '1_hour' => '1 часа', - '3_hours' => '3 часов', - - // Plugins - 'cannot_update_composer' => 'PHP Censor не может обновить composer.json, если он недоступен на запись.', - 'x_has_been_removed' => '%s был удален.', - 'x_has_been_added' => '%s был добавлен в composer.json и будет установлен, как только вы запустите composer update.', - 'enabled_plugins' => 'Включенные плагины', - 'provided_by_package' => 'Предоставляется пакетом', - 'installed_packages' => 'Установленные пакеты', - 'suggested_packages' => 'Рекомендуемые пакеты', - 'title' => 'Название', - 'description' => 'Описание', - 'version' => 'Версия', - 'install' => 'Установить »', - 'remove' => 'Удалить »', - 'search_packagist_for_more' => 'Искать на Packagist', - 'search' => 'Искать »', - - // Summary plugin - 'build-summary' => 'Сводка', - 'stage' => 'Этап', - 'duration' => 'Продолжительность', - 'seconds' => 'сек.', - 'plugin' => 'Плагин', - 'stage_setup' => 'Установка', - 'stage_test' => 'Тестирование', - 'stage_complete' => 'Завершение', - 'stage_success' => 'Успешное завершение', - 'stage_failure' => 'Провал', - 'stage_broken' => 'Поломка', - 'stage_fixed' => 'Исправление', - 'severity' => 'Уровень', - - 'all_plugins' => 'Все плагины', - 'all_severities' => 'Все уровни', - 'filters' => 'Фильтры', - 'errors_selected' => 'Ошибок выбрано', - - 'build_details' => 'Информация о сборке', - 'commit_details' => 'Информация о коммитe', - 'committer' => 'Автор коммита', - 'commit_message' => 'Сообщение в коммите', - 'timing' => 'Тайминг', - 'created' => 'Создана', - 'started' => 'Началась', - 'finished' => 'Закончилась', - - // Update - 'update_app' => 'Обновите базу данных с учетом обновленных моделей.', - 'updating_app' => 'Обновление базы данных PHP Censor: ', - 'not_installed' => 'PHP Censor не может быть установлен.', - 'install_instead' => 'Пожалуйста, установите PHP Censor с помощью команды php-censor:install.', - - // Build Plugins: - 'passing_build' => 'Успех сборки', - 'failing_build' => 'Провал сборки', - 'log_output' => 'Вывод лога: ', - - // Error Levels: - 'critical' => 'Критичный', - 'high' => 'Высокий', - 'normal' => 'Нормальный', - 'low' => 'Низкий', - - // Plugins that generate errors: - 'php_mess_detector' => 'PHP Mess Detector', - 'php_code_sniffer' => 'PHP Code Sniffer', - 'php_unit' => 'PHP Unit', - 'php_cpd' => 'PHP Copy/Paste Detector', - 'php_docblock_checker' => 'PHP Docblock Checker', - 'composer' => 'Composer', - 'php_loc' => 'PHP LOC', - 'php_parallel_lint' => 'PHP Parallel Lint', - 'email' => 'Email', - 'atoum' => 'Atoum', - 'behat' => 'Behat', - 'campfire' => 'Campfire', - 'clean_build' => 'Clean Build', - 'codeception' => 'Codeception', - 'copy_build' => 'Copy Build', - 'deployer' => 'Deployer', - 'env' => 'Env', - 'grunt' => 'Grunt', - 'hipchat_notify' => 'Hipchat', - 'irc' => 'IRC', - 'lint' => 'Lint', - 'mysql' => 'MySQL', - 'package_build' => 'Package Build', - 'pdepend' => 'PDepend', - 'pgsql' => 'PostgreSQL', - 'phar' => 'Phar', - 'phing' => 'Phing', - 'php_cs_fixer' => 'PHP Coding Standards Fixer', - 'php_spec' => 'PHP Spec', - 'shell' => 'Shell', - 'slack_notify' => 'Slack', - 'technical_debt' => 'Technical Debt', - 'xmpp' => 'XMPP', - 'security_checker' => 'SensioLabs Security Checker', - - 'confirm_message' => 'Элемент будет удален навсегда. Вы уверены?', - 'confirm_title' => 'Подтвержение удаления', - 'confirm_ok' => 'Удалить', - 'confirm_cancel' => 'Отмена', - 'confirm_success' => 'Элемент успешно удален.', - 'confirm_failed' => 'Удаление провалилось! Ответ сервера: ', - - 'public_status_title' => 'Публичный статус', - 'public_status_image' => 'Иконка статуса', - 'public_status_page' => 'Страница публичного статуса', -]; diff --git a/src/PHPCensor/Languages/lang.uk.php b/src/PHPCensor/Languages/lang.uk.php deleted file mode 100644 index def1391..0000000 --- a/src/PHPCensor/Languages/lang.uk.php +++ /dev/null @@ -1,291 +0,0 @@ - 'Українська', - 'language' => 'Мова', - - // Log in: - 'log_in_to_app' => 'Увійти до PHP Censor', - 'login_error' => 'Невірний email або пароль', - 'forgotten_password_link' => 'Забули свій пароль?', - 'reset_emailed' => 'Ми відправили вам посилання для скидання вашого паролю.', - 'reset_header' => 'Не хвилюйтесь!
    Просто введіть ваш email -і вам буде надіслано листа із посиланням на скидання паролю.', - 'reset_email_address' => 'Введіть свою email адресу:', - 'reset_send_email' => 'Скидання пароля', - 'reset_enter_password' => 'Введіть, будь ласка, новий пароль', - 'reset_new_password' => 'Новий пароль:', - 'reset_change_password' => 'Змінити пароль', - 'reset_no_user_exists' => 'Не існує користувача з такою email адресою, будь ласка, повторіть знову.', - 'reset_email_body' => 'Привіт, %s, - -Ви отримали цей лист, тому що ви або хтось інший запросили скидання пароля в PHP Censor. - -Якщо це були ви, будь ласка, перейдіть за посиланням нижче для скидання пароля: %ssession/reset-password/%d/%s, - -або ж проігноруйте цей лист та нічого не робіть. - -Дякуємо, - -PHP Censor', - - 'reset_email_title' => 'Скидання пароль PHP Censor для %s', - 'reset_invalid' => 'Невірний запит скидання паролю.', - 'email_address' => 'Email адреса', - 'login' => 'Логин / Email адреса', - 'password' => 'Пароль', - 'log_in' => 'Увійти', - - - // Top Nav - 'toggle_navigation' => 'Сховати/відобразити панель навігації', - 'n_builds_pending' => '%d збірок очікує', - 'n_builds_running' => '%d збірок виконується', - 'edit_profile' => 'Редагувати профіль', - 'sign_out' => 'Вийти', - 'branch_x' => 'Гілка: %s', - 'created_x' => 'Створено: %s', - 'started_x' => 'Розпочато: %s', - - // Sidebar - 'hello_name' => 'Привіт, %s', - 'dashboard' => 'Панель управління', - 'admin_options' => 'Меню адміністратора', - 'add_project' => 'Додати проект', - 'settings' => 'Налаштування', - 'manage_users' => 'Управління користувачами', - 'plugins' => 'Плагіни', - 'view' => 'Переглянути', - 'build_now' => 'Зібрати', - 'edit_project' => 'Редагувати проект', - 'delete_project' => 'Видалити проект', - - // Project Summary: - 'no_builds_yet' => 'Немає збірок!', - 'x_of_x_failed' => '%d із останніх %d збірок були провалені.', - 'x_of_x_failed_short' => '%d / %d провалені.', - 'last_successful_build' => 'Останнью успішною збіркою була %s.', - 'never_built_successfully' => 'У цього проекта ніколи не було успішних збірок.', - 'all_builds_passed' => 'Усі із останніх %d збірок успішні.', - 'all_builds_passed_short' => '%d / %d успішні.', - 'last_failed_build' => 'Останньою проваленою збіркою була %s.', - 'never_failed_build' => 'У цього проекта ніколи не було провалених збірок.', - 'view_project' => 'Переглянути проект', - - // Timeline: - 'latest_builds' => 'Останні збірки', - 'pending' => 'Очікує', - 'running' => 'Виконується', - 'success' => 'Успіх', - 'failed' => 'Провалена', - 'manual_build' => 'Ручна збірка', - - // Add/Edit Project: - 'new_project' => 'Новий проект', - 'project_x_not_found' => 'Проект із ID %d не існує.', - 'project_details' => 'Деталі проекта', - 'public_key_help' => 'Для полегшення початку, ми згенерували пару SSH-ключів для вас для використання в цьому проекті. -Для їх використання - просто додайте наступний публічний ключ у розділ "deploy keys" обраної вами системи зберігання програмного коду.', - 'select_repository_type' => 'Оберіть тип репозиторію...', - 'github' => 'GitHub', - 'bitbucket' => 'Bitbucket', - 'gitlab' => 'GitLab', - 'remote' => 'Віддалений URL', - 'local' => 'Локальний шлях', - 'hg' => 'Mercurial', - - 'where_hosted' => 'Де зберігається ваш проект?', - 'choose_github' => 'Оберіть GitHub репозиторій:', - - 'repo_name' => 'Ім’я репозиторія / URL (зовнішній) / Шлях (локальний)', - 'project_title' => 'Заголовок проекту', - 'project_private_key' => 'Приватний ключ доступу до репозиторія -(залишити поле порожнім для локального використання та/або анонімного доступу)', - 'build_config' => 'Конфігурація збірки цього проекта для PHP Censor -(якщо ви не додали файл .php-censor.yml (.phpci.yml|phpci.yml) до репозиторію вашого проекту)', - 'default_branch' => 'Назва гілки за замовчуванням', - 'allow_public_status' => 'Увімкнути публічну сторінку статусу та зображення для цього проекта?', - 'archived' => 'Архівний', - 'archived_menu' => 'Архів', - 'save_project' => 'Зберегти проект', - - 'error_mercurial' => 'URL репозиторію Mercurial повинен починатись із http:// або https://', - 'error_remote' => 'URL репозиторію повинен починатись із git://, http:// або https://', - 'error_gitlab' => 'Ім’я репозиторія GitLab повинно бути у форматі "user@domain.tld:owner/repo.git"', - 'error_github' => 'Ім’я репозиторія повинно відповідати формату "owner/repo"', - 'error_bitbucket' => 'Ім’я репозиторія повинно відповідати формату "owner/repo"', - 'error_path' => 'Вказаний шлях не існує.', - - // View Project: - 'all_branches' => 'Усі гілки', - 'builds' => 'Збірки', - 'id' => 'ID', - 'date' => 'Дата', - 'project' => 'Проект', - 'commit' => 'Комміт', - 'branch' => 'Гілка', - 'status' => 'Статус', - 'prev_link' => '« Попер.', - 'next_link' => 'Наст. »', - 'public_key' => 'Публічний ключ', - 'delete_build' => 'Видалити збірку', - - 'webhooks' => 'Webhooks', - 'webhooks_help_github' => 'Для автоматичної збірки цього проекту, при надходженні нових комітів, додайте наступний URL -у якості нового "Webhook" у розділі налаштувань -Webhooks and Services -вашого GitHub репозиторію.', - - 'webhooks_help_gitlab' => 'Для автоматичної збірки цього проекту, при надходженні нових комітів, додайте наступний URL -у якості нового "WebHook URL" у розділі "Web Hooks" вашого GitLab репозиторію.', - - 'webhooks_help_bitbucket' => 'Для автоматичної збірки цього проекту, при надходженні нових комітів, додайте наступний URL -у якості нового "POST" сервісу у розділі -Services -вашого Bitbucket репозиторію.', - - // View Build - 'build_x_not_found' => 'Збірка із ID %d не існує.', - 'build_n' => 'Збірка %d', - 'rebuild_now' => 'Перезібрати зараз', - - - 'committed_by_x' => 'Комміт від %s', - 'commit_id_x' => 'Комміт: %s', - - 'chart_display' => 'Цей графік відобразиться після завершення збірки.', - - 'build' => 'Збірка', - 'lines' => 'Рядків', - 'comment_lines' => 'Рядків коментарів', - 'noncomment_lines' => 'Рядків не коментарів', - 'logical_lines' => 'Рядків логіки', - 'lines_of_code' => 'Рядки коду', - 'build_log' => 'Лог збірки', - 'quality_trend' => 'Тенденція якості', - 'codeception_errors' => 'Помилки Codeception', - 'phpmd_warnings' => 'Попередження PHPMD', - 'phpcs_warnings' => 'Попередження PHPCS', - 'phpcs_errors' => 'Помилки PHPCS', - 'phplint_errors' => 'Помилки Lint', - 'phpunit_errors' => 'Помилки PHPUnit', - 'phpdoccheck_warnings' => 'Відсутні Docblocks', - 'issues' => 'Проблеми', - - 'codeception' => 'Codeception', - 'phpcpd' => 'PHP Copy/Paste Detector', - 'phpcs' => 'PHP Code Sniffer', - 'phpdoccheck' => 'Відсутні Docblocks', - 'phpmd' => 'PHP Mess Detector', - 'phpspec' => 'PHP Spec', - 'phpunit' => 'PHP Unit', - - 'file' => 'Файл', - 'line' => 'Рядок', - 'class' => 'Клас', - 'method' => 'Метод', - 'message' => 'Повідомлення', - 'start' => 'Запуск', - 'end' => 'Кінець', - 'from' => 'Від', - 'to' => 'До', - 'result' => 'Результат', - 'ok' => 'OK', - 'took_n_seconds' => 'Зайняло %d секунд', - 'build_started' => 'Збірка розпочата', - 'build_finished' => 'Збірка завершена', - 'test_message' => 'Message', - 'test_no_message' => 'No message', - 'test_success' => 'Successful: %d', - 'test_fail' => 'Failures: %d', - 'test_skipped' => 'Skipped: %d', - 'test_error' => 'Errors: %d', - 'test_todo' => 'Todos: %d', - 'test_total' => '%d test(s)', - - // Users - 'name' => 'Ім’я', - 'password_change' => 'Пароль (залишити порожнім, якщо не бажаєте змінювати його)', - 'save' => 'Зберегти »', - 'update_your_details' => 'Оновити ваші деталі', - 'your_details_updated' => 'Ваші деталі були оновлені.', - 'add_user' => 'Додати користувача', - 'is_admin' => 'Адміністратор?', - 'yes' => 'Так', - 'no' => 'Ні', - 'edit' => 'Редагувати', - 'edit_user' => 'Редагувати користувача', - 'delete_user' => 'Видалити користувача', - 'user_n_not_found' => 'Користувач із ID %d не існує.', - 'is_user_admin' => 'Чи є цей користувач адміністратором?', - 'save_user' => 'Зберегти користувача', - - // Settings: - 'settings_saved' => 'Ваші налаштування були збережені.', - 'settings_check_perms' => 'Ваші налаштування не можуть бути збережені, перевірте права на ваш файл налаштувань config.yml.', - 'settings_cannot_write' => 'PHP Censor не може записати файл config.yml, налаштування не будуть коректно збережені, -доки це не буде виправлено.', - 'settings_github_linked' => 'Ваш GitHub аккаунт було підключено.', - 'settings_github_not_linked' => 'Ваш GitHub аккаунт не може бути підключеним.', - 'build_settings' => 'Налаштування збірки', - 'github_application' => 'GitHub додаток', - 'github_sign_in' => 'Перед початком користування GitHub, вам необхідно увійти та надати -доступ для PHP Censor до вашого аккаунту.', - 'github_app_linked' => 'PHP Censor успішно зв\'язаний з аккаунтом GitHub.', - 'github_where_to_find' => 'Де це знайти...', - 'github_where_help' => 'Якщо ви є власником додатку, який би ви хотіли використовувати, то ви можете знайти інформацію про це у розділі -налаштувань ваших додатків.', - - 'email_settings' => 'Налаштування Email', - 'email_settings_help' => 'Перед тим, як PHP Censor почне надсилати статуси збірок на email, -вам необхідно налаштувати параметри SMTP нижче.', - - 'application_id' => 'ID додатка', - 'application_secret' => 'Таємний ключ додатка', - - 'smtp_server' => 'Сервер SMTP', - 'smtp_port' => 'Порт SMTP', - 'smtp_username' => 'Ім’я користувача SMTP', - 'smtp_password' => 'Пароль SMTP', - 'from_email_address' => 'Відправляти з Email', - 'default_notification_address' => 'Email для повідомлень за замовчуванням', - 'use_smtp_encryption' => 'Використовувати SMTP шифрування?', - 'none' => 'Ні', - 'ssl' => 'SSL', - 'tls' => 'TLS', - - 'failed_after' => 'Вважати збірку проваленою після', - '5_mins' => '5 хвилин', - '15_mins' => '15 хвилин', - '30_mins' => '30 хвилин', - '1_hour' => '1 година', - '3_hours' => '3 години', - - // Plugins - 'cannot_update_composer' => 'PHP Censor не може оновити composer.json, оскільки він не є доступним для запису.', - 'x_has_been_removed' => '%s було видалено.', - 'x_has_been_added' => '%s був доданий до composer.json і буде встановлений, як тільки -ви виконаєте composer update.', - 'enabled_plugins' => 'Увімкнені плагіни', - 'provided_by_package' => 'Наданий пакетом', - 'installed_packages' => 'Встановлені пакети', - 'suggested_packages' => 'Запропоновані пакети', - 'title' => 'Заголовок', - 'description' => 'Опис', - 'version' => 'Версія', - 'install' => 'Встановити »', - 'remove' => 'Видалити »', - 'search_packagist_for_more' => 'Знайти більше пакетів на Packagist', - 'search' => 'Знайти »', - - // Update - 'update_app' => 'Оновити базу даних для відображення змінених моделей.', - 'updating_app' => 'Оновлення бази даних PHP Censor:', - 'not_installed' => 'Неможливо встановити PHP Censor.', - 'install_instead' => 'Будь ласка, встановіть PHP Censor через команду php-censor:install.', - - // Build Plugins: - 'passing_build' => 'Успішно збірка', - 'failing_build' => 'Невдала збірка', - 'log_output' => 'Вивід лога:', -]; diff --git a/src/PHPCensor/Languages/lang.zh.php b/src/PHPCensor/Languages/lang.zh.php deleted file mode 100644 index fc96b00..0000000 --- a/src/PHPCensor/Languages/lang.zh.php +++ /dev/null @@ -1,319 +0,0 @@ - '简体中文', - 'language' => '语言选择', - - // Log in: - 'log_in_to_app' => '登录 PHP Censor', - 'login_error' => '邮箱或密码错误', - 'forgotten_password_link' => '忘记密码?', - 'reset_emailed' => '已发送重置密码邮件.', - 'reset_header' => '不用担心!
    只需要输入您的邮箱地址,我们将会给您的邮箱发送含有重置密码链接的邮件。', - 'reset_email_address' => '输入您的邮箱地址:', - 'reset_send_email' => '邮件重设密码', - 'reset_enter_password' => '请输入新密码', - 'reset_new_password' => '新密码:', - 'reset_change_password' => '更改密码', - 'reset_no_user_exists' => '不存该该邮箱用户,请检查后重试!', - 'reset_email_body' => '您好 %s, - -您收到这封邮件,是因为您或者别人发起了来自PHP Censor的密码重置请求。 - -如果确实是您发起的,请点击链接去重置您的密码:%ssession/reset-password/%d/%s - -否则,请忽视这封邮件,这不会发生任何事情。 - -多谢, - -PHP Censor', - - 'reset_email_title' => '给 %s 来自 PHP Censor 的密码重置邮件', - 'reset_invalid' => '密码重置请求失败!', - 'email_address' => '邮箱地址', - 'password' => '密码', - 'log_in' => '登录', - - - // Top Nav - 'toggle_navigation' => '导航切换', - 'n_builds_pending' => '%d 等待构建', - 'n_builds_running' => '%d 正在构建', - 'edit_profile' => '编辑资料', - 'sign_out' => '注销', - 'branch_x' => '分支: %s', - 'created_x' => '创建于: %s', - 'started_x' => '开始于: %s', - - // Sidebar - 'hello_name' => '您好, %s', - 'dashboard' => '仪表盘', - 'admin_options' => '管理选项', - 'add_project' => '新增项目', - 'settings' => '设置', - 'manage_users' => '用户管理', - 'plugins' => '插件', - 'view' => '查看', - 'build_now' => '立即构建', - 'edit_project' => '编辑项目', - 'delete_project' => '删除项目', - - // Project Summary: - 'no_builds_yet' => '没有任何构建', - 'x_of_x_failed' => '%d 的最后一个 %d 构建失败!', - 'x_of_x_failed_short' => '%d / %d 失败。', - 'last_successful_build' => ' 最后一次构建成功的是 %s.', - 'never_built_successfully' => ' 该项目构建从来没有成功过!', - 'all_builds_passed' => '构建记录中 %d 次构建通过。', - 'all_builds_passed_short' => '%d / %d 通过。', - 'last_failed_build' => ' 最近一次构建失败的是 %s。', - 'never_failed_build' => ' 该项目构建从未失败过。', - 'view_project' => '查看项目', - - // Timeline: - 'latest_builds' => '最近构建', - 'pending' => '等待中', - 'running' => '构建中', - 'success' => '成功', - 'failed' => '失败', - 'manual_build' => 'Manual Build', - - // Add/Edit Project: - 'new_project' => '新项目', - 'project_x_not_found' => '项目 ID %d 不存在。', - 'project_details' => '项目详情', - 'public_key_help' => '为了帮助您更简单的开始项目构建,我们生成了以下 SSH key 对用于该项目。要使用它,请添加以下公钥 “deploy keys” 至您选择的源代码托管平台', - - 'select_repository_type' => '选择仓库类型...', - 'github' => 'GitHub', - 'bitbucket' => 'Bitbucket', - 'gitlab' => 'GitLab', - 'remote' => 'Remote URL', - 'local' => 'Local Path', - 'hg' => 'Mercurial', - 'svn' => 'Subversion', - - 'where_hosted' => '您的代码托管在?', - 'choose_github' => '选择一个 GitHub 仓库:', - - 'repo_name' => '仓库名称 / URL (本地) or Path (本地)', - 'project_title' => '项目标题', - 'project_private_key' => '访问仓库私有秘钥 - (本地或公共仓库可为空)', - 'build_config' => '该项目 PHP Censor 构建配置文件 - (如果您无法在该项目仓库创建 .php-censor.yml (.phpci.yml|phpci.yml) 文件)', - 'default_branch' => '默认分支名称', - 'allow_public_status' => '启用此项目的公共状态页和图像?', - 'archived' => '归档', - 'archived_menu' => '归档', - 'save_project' => '保存项目', - - 'error_mercurial' => 'Mercurial 仓库 URL 必须以 http:// or https:// 开始', - 'error_remote' => '仓库 URL 必须以 git://, http:// or https:// 开始', - 'error_gitlab' => 'GitLab 仓库名称必须符合 "user@domain.tld:owner/repo.git" 格式', - 'error_github' => '仓库名称必须符合 "owner/repo" 格式', - 'error_bitbucket' => '仓库名称必须符合 "owner/repo" 格式', - 'error_path' => '您制定的路径不存在', - - // View Project: - 'all_branches' => 'All Branches', - 'builds' => 'Builds', - 'id' => 'ID', - 'date' => '日期', - 'project' => '项目', - 'commit' => '提交', - 'branch' => '分支', - 'status' => '状态', - 'prev_link' => '« 上一个', - 'next_link' => '下一个 »', - 'public_key' => '公共秘钥', - 'delete_build' => '删除构建', - - 'webhooks' => 'Webhooks', - 'webhooks_help_github' => '要想当您的仓库由新的提交推送时自动构建,请在您的Github仓库的 - Webhooks and Services - 将该URL添加至新增 "Webhook" 中。', - - 'webhooks_help_gitlab' => '要想当您的仓库由新的提交推送时自动构建,请在您的GitLab仓库的 "WebHook URL" 添加该URL。', - - 'webhooks_help_bitbucket' => '要想当您的仓库由新的提交推送时自动构建,请在您的GitLab仓库的 - - Services 将该URL添加成 “POST服务”。', - - // View Build - 'errors' => 'Errors', - 'information' => 'Information', - - 'build_x_not_found' => '构建 ID %d 不存在。', - 'build_n' => 'Build %d', - 'rebuild_now' => '重新构建', - - - 'committed_by_x' => '由 %s 提交', - 'commit_id_x' => '提交: %s', - - 'chart_display' => '构建一旦完成该图表将会显示', - - 'build' => 'Build', - 'lines' => 'Lines', - 'comment_lines' => 'Comment lines', - 'noncomment_lines' => 'Non-Comment lines', - 'logical_lines' => 'Logical lines', - 'lines_of_code' => 'Lines of code', - 'build_log' => 'Build log', - 'quality_trend' => 'Quality trend', - 'codeception_errors' => 'Codeception errors', - 'phpmd_warnings' => 'PHPMD warnings', - 'phpcs_warnings' => 'PHPCS warnings', - 'phpcs_errors' => 'PHPCS errors', - 'phplint_errors' => 'Lint errors', - 'phpunit_errors' => 'PHPUnit errors', - 'phpdoccheck_warnings' => 'Missing docblocks', - 'issues' => 'Issues', - - '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' => 'Technical Debt', - 'behat' => 'Behat', - - 'codeception_feature' => 'Feature', - 'codeception_suite' => 'Suite', - 'codeception_time' => 'Time', - 'codeception_synopsis' => '%1$d 测试进行了 %2$f 秒. - %3$d 失败。', - - 'file' => 'File', - 'line' => 'Line', - 'class' => 'Class', - 'method' => 'Method', - 'message' => 'Message', - 'start' => 'Start', - 'end' => 'End', - 'from' => 'From', - 'to' => 'To', - 'result' => 'Result', - 'ok' => 'OK', - 'took_n_seconds' => 'Took %d seconds', - 'build_started' => 'Build Started', - 'build_finished' => 'Build Finished', - 'test_message' => 'Message', - 'test_no_message' => 'No message', - 'test_success' => 'Successful: %d', - 'test_fail' => 'Failures: %d', - 'test_skipped' => 'Skipped: %d', - 'test_error' => 'Errors: %d', - 'test_todo' => 'Todos: %d', - 'test_total' => '%d test(s)', - - // Users - 'name' => '名称', - 'password_change' => '密码 (如果要修改密码请填入新密码,否则留空空)', - 'save' => '保存 »', - 'update_your_details' => '更新您的个人信息', - 'your_details_updated' => '您的信息已经更新。', - 'add_user' => '新增用户', - 'is_admin' => '是否设为管理员?', - 'yes' => '是', - 'no' => '否', - 'edit' => '编辑', - 'edit_user' => '编辑用户', - 'delete_user' => '删除用户', - 'user_n_not_found' => '用户 ID %d 不存在。', - 'is_user_admin' => '该用户是否为管理员', - 'save_user' => '保存用户', - - // Settings: - 'settings_saved' => '您的设置已经保存。', - 'settings_check_perms' => '权限不足,您的设置无法保存, 请检查 config.yml 文件.', - 'settings_cannot_write' => 'PHP Censor 无法写入 config.yml 文件, 在这个问题解决前设置可能无法正常保存', - 'settings_github_linked' => '您的 GitHub 账户已经连接。', - 'settings_github_not_linked' => '您的 GitHub 无法连接。', - 'build_settings' => '构建设置', - 'github_application' => 'GitHub Application', - 'github_sign_in' => '在使用您的 GitHub 账号之前, 您需要登录 GitHub , 并允许 PHP Censor 访问您的账户。', - 'github_linked' => 'PHP Censor 成功连接到您的 GitHub 账户。', - 'github_where_to_find' => '在哪里可以找到...', - 'github_where_help' => '如果您想使用您自己的应用, 您可以在applications 的 setting 中 找到相关信息。', - - 'email_settings' => '邮箱设置', - 'email_settings_help' => 'PHP Censor在发送构建状态的邮件之前,您需要配置您的SMTP设置如下。', - - 'application_id' => 'Application ID', - 'application_secret' => 'Application Secret', - - 'smtp_server' => 'SMTP Server', - 'smtp_port' => 'SMTP Port', - 'smtp_username' => 'SMTP Username', - 'smtp_password' => 'SMTP Password', - 'from_email_address' => '邮件来自', - 'default_notification_address' => '通知默认邮件', - 'use_smtp_encryption' => 'SMTP 使用哪种方式加密?', - 'none' => 'None', - 'ssl' => 'SSL', - 'tls' => 'TLS', - - 'failed_after' => '构建失败后重新构建间隔', - '5_mins' => '5 分钟', - '15_mins' => '15 分钟', - '30_mins' => '30 分钟', - '1_hour' => '1 小时', - '3_hours' => '3 小时', - - // Plugins - 'cannot_update_composer' => '由于 composer.json 文件不可写 PHP Censor 无法为您更新该文件, ', - 'x_has_been_removed' => '%s 已经移除', - 'x_has_been_added' => '%s 已经为您添加至 composer.json , 当您下次执行 composer update 时相关库将会安装', - 'enabled_plugins' => '已启用插件', - 'provided_by_package' => '来自', - 'installed_packages' => '已安装插件', - 'suggested_packages' => '建议安装插件', - 'title' => '名称', - 'description' => '说明', - 'version' => '版本', - 'install' => '安装 »', - 'remove' => '移除 »', - 'search_packagist_for_more' => '搜索获取更多插件', - 'search' => '搜索 »', - - // Summary plugin - 'build-summary' => 'Summary', - 'stage' => 'Stage', - 'duration' => 'Duration', - 'plugin' => 'Plugin', - 'stage_setup' => 'Setup', - 'stage_test' => 'Test', - 'stage_complete' => 'Complete', - 'stage_success' => 'Success', - 'stage_failure' => 'Failure', - 'stage_broken' => 'Broken', - 'stage_fixed' => 'Fixed', - - // Update - 'update_app' => 'Update the database to reflect modified models.', - 'updating_app' => 'Updating PHP Censor database: ', - 'not_installed' => 'PHP Censor does not appear to be installed.', - 'install_instead' => 'Please install PHP Censor via php-censor:install instead.', - - // Build Plugins: - 'passing_build' => 'Passing Build', - 'failing_build' => 'Failing Build', - 'log_output' => 'Log Output: ', - - // Error Levels: - 'critical' => 'Critical', - 'high' => 'High', - 'normal' => 'Normal', - 'low' => 'Low', - - // Plugins that generate errors: - 'php_mess_detector' => 'PHP Mess Detector', - 'php_code_sniffer' => 'PHP Code Sniffer', - 'php_unit' => 'PHP Unit', - 'php_cpd' => 'PHP Copy/Paste Detector', - 'php_docblock_checker' => 'PHP Docblock Checker', -]; diff --git a/src/PHPCensor/Logging/BuildDBLogHandler.php b/src/PHPCensor/Logging/BuildDBLogHandler.php deleted file mode 100644 index 5cd3489..0000000 --- a/src/PHPCensor/Logging/BuildDBLogHandler.php +++ /dev/null @@ -1,81 +0,0 @@ -build = $build; - // We want to add to any existing saved log information. - $this->logValue = $build->getLog(); - } - - /** - * Destructor - */ - public function __destruct() - { - $this->flushData(); - } - - /** - * Flush buffered data - */ - protected function flushData() - { - $this->build->setLog($this->logValue); - Factory::getStore('Build')->save($this->build); - $this->flush_timestamp = time(); - } - - /** - * Write a log entry to the build log. - * @param array $record - */ - protected function write(array $record) - { - $message = (string)$record['message']; - $message = str_replace($this->build->currentBuildPath, './', $message); - - $this->logValue .= $message . PHP_EOL; - - if ($this->flush_timestamp < (time() - $this->flush_delay)) { - $this->flushData(); - } - } -} diff --git a/src/PHPCensor/Logging/BuildLogger.php b/src/PHPCensor/Logging/BuildLogger.php deleted file mode 100644 index 20c6361..0000000 --- a/src/PHPCensor/Logging/BuildLogger.php +++ /dev/null @@ -1,114 +0,0 @@ -logger = $logger; - $this->build = $build; - } - - /** - * 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 = []) - { - // Skip if no logger has been loaded. - if (!$this->logger) { - return; - } - - if (!is_array($message)) { - $message = [$message]; - } - - // The build is added to the context so the logger can use - // details from it if required. - $context['build'] = $this->build; - - foreach ($message as $item) { - $this->logger->log($level, $item, $context); - } - } - - /** - * 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 $message - * @param \Exception $exception The exception that caused the error. - */ - public function logFailure($message, \Exception $exception = null) - { - $context = []; - - // The psr3 log interface stipulates that exceptions should be passed - // as the exception key in the context array. - if ($exception) { - $context['exception'] = $exception; - $context['trace'] = $exception->getTrace(); - } - - $this->log("\033[0;31m" . $message . "\033[0m", LogLevel::ERROR, $context); - } - - /** - * Add a debug message to the log. - * @param string - */ - public function logDebug($message) - { - if ( - (defined('DEBUG_MODE') && DEBUG_MODE) || - ((boolean)$this->build->getExtra('debug')) - ) { - $this->log("\033[0;36m" . $message . "\033[0m"); - } - } - - /** - * Sets a logger instance on the object - * - * @param LoggerInterface $logger - * @return null - */ - public function setLogger(LoggerInterface $logger) - { - $this->logger = $logger; - } -} diff --git a/src/PHPCensor/Logging/Handler.php b/src/PHPCensor/Logging/Handler.php deleted file mode 100644 index 532b1aa..0000000 --- a/src/PHPCensor/Logging/Handler.php +++ /dev/null @@ -1,145 +0,0 @@ - 'Warning', - E_NOTICE => 'Notice', - E_USER_ERROR => 'User Error', - E_USER_WARNING => 'User Warning', - E_USER_NOTICE => 'User Notice', - E_STRICT => 'Runtime Notice', - E_RECOVERABLE_ERROR => 'Catchable Fatal Error', - E_DEPRECATED => 'Deprecated', - E_USER_DEPRECATED => 'User Deprecated', - ]; - - /** - * @var LoggerInterface - */ - protected $logger; - - /** - * @param LoggerInterface $logger - */ - public function __construct(LoggerInterface $logger = null) - { - $this->logger = $logger; - } - - /** - * Register a new log handler. - * @param LoggerInterface $logger - */ - public static function register(LoggerInterface $logger = null) - { - $handler = new static($logger); - - set_error_handler([$handler, 'handleError']); - register_shutdown_function([$handler, 'handleFatalError']); - - set_exception_handler([$handler, 'handleException']); - } - - /** - * @param integer $level - * @param string $message - * @param string $file - * @param integer $line - * - * @throws \ErrorException - */ - public function handleError($level, $message, $file, $line) - { - if (error_reporting() & $level) { - $exception_level = isset($this->levels[$level]) ? $this->levels[$level] : $level; - - throw new \ErrorException( - sprintf('%s: %s in %s line %d', $exception_level, $message, $file, $line), - 0, - $level, - $file, - $line - ); - } - } - - /** - * @throws \ErrorException - */ - public function handleFatalError() - { - $fatal_error = error_get_last(); - - try { - if (($error = error_get_last()) !== null) { - $error = new \ErrorException( - sprintf( - '%s: %s in %s line %d', - $fatal_error['type'], - $fatal_error['message'], - $fatal_error['file'], - $fatal_error['line'] - ), - 0, - $fatal_error['type'], - $fatal_error['file'], - $fatal_error['line'] - ); - $this->log($error); - } - } catch (\Exception $e) { - $error = new \ErrorException( - sprintf( - '%s: %s in %s line %d', - $fatal_error['type'], - $fatal_error['message'], - $fatal_error['file'], - $fatal_error['line'] - ), - 0, - $fatal_error['type'], - $fatal_error['file'], - $fatal_error['line'] - ); - $this->log($error); - } - } - - /** - * @param \Exception $exception - */ - public function handleException(\Exception $exception) - { - $this->log($exception); - } - - /** - * Write to the build log. - * @param \Exception $exception - */ - protected function log(\Exception $exception) - { - if (null !== $this->logger) { - $message = sprintf( - '%s: %s (uncaught exception) at %s line %s', - get_class($exception), - $exception->getMessage(), - $exception->getFile(), - $exception->getLine() - ); - - $this->logger->error($message, ['exception' => $exception]); - } - } -} diff --git a/src/PHPCensor/Logging/LoggedBuildContextTidier.php b/src/PHPCensor/Logging/LoggedBuildContextTidier.php deleted file mode 100644 index 4afa477..0000000 --- a/src/PHPCensor/Logging/LoggedBuildContextTidier.php +++ /dev/null @@ -1,38 +0,0 @@ -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; - } -} diff --git a/src/PHPCensor/Logging/OutputLogHandler.php b/src/PHPCensor/Logging/OutputLogHandler.php deleted file mode 100644 index dcbab04..0000000 --- a/src/PHPCensor/Logging/OutputLogHandler.php +++ /dev/null @@ -1,41 +0,0 @@ -output = $output; - } - - /** - * Write a log entry to the terminal. - * @param array $record - */ - protected function write(array $record) - { - $this->output->writeln((string)$record['formatted']); - } -} diff --git a/src/PHPCensor/Migrations/20140513143726_initial_migration.php b/src/PHPCensor/Migrations/20140513143726_initial_migration.php deleted file mode 100644 index 9c329a6..0000000 --- a/src/PHPCensor/Migrations/20140513143726_initial_migration.php +++ /dev/null @@ -1,210 +0,0 @@ -createBuildTable(); - $this->createBuildMetaTable(); - $this->createProjectTable(); - $this->createUserTable(); - - // Set up foreign keys: - $build = $this->table('build'); - - if (!$build->hasForeignKey('project_id')) { - $build->addForeignKey('project_id', 'project', 'id', ['delete'=> 'CASCADE', 'update' => 'CASCADE'])->save(); - } - - $buildMeta = $this->table('build_meta'); - - if (!$buildMeta->hasForeignKey('build_id')) { - $buildMeta->addForeignKey('build_id', 'build', 'id', ['delete'=> 'CASCADE', 'update' => 'CASCADE'])->save(); - } - - if (!$buildMeta->hasForeignKey('project_id')) { - $buildMeta->addForeignKey('project_id', 'project', 'id', ['delete'=> 'CASCADE', 'update' => 'CASCADE'])->save(); - } - } - - protected function createBuildTable() - { - $table = $this->table('build'); - - if (!$this->hasTable('build')) { - $table->create(); - } - - if (!$table->hasColumn('project_id')) { - $table->addColumn('project_id', 'integer')->save(); - } - - if (!$table->hasColumn('commit_id')) { - $table->addColumn('commit_id', 'string', ['limit' => 50])->save(); - } - - if (!$table->hasColumn('status')) { - $table->addColumn('status', 'integer', ['limit' => 4])->save(); - } - - if (!$table->hasColumn('log')) { - $table->addColumn('log', 'text')->save(); - } - - if (!$table->hasColumn('branch')) { - $table->addColumn('branch', 'string', ['limit' => 50])->save(); - } - - if (!$table->hasColumn('created')) { - $table->addColumn('created', 'datetime')->save(); - } - - if (!$table->hasColumn('started')) { - $table->addColumn('started', 'datetime')->save(); - } - - if (!$table->hasColumn('finished')) { - $table->addColumn('finished', 'datetime')->save(); - } - - if (!$table->hasColumn('committer_email')) { - $table->addColumn('committer_email', 'string', ['limit' => 250])->save(); - } - - if (!$table->hasColumn('commit_message')) { - $table->addColumn('commit_message', 'text')->save(); - } - - if (!$table->hasColumn('extra')) { - $table->addColumn('extra', 'text')->save(); - } - - if ($table->hasColumn('plugins')) { - $table->removeColumn('plugins')->save(); - } - - if (!$table->hasIndex(['project_id'])) { - $table->addIndex(['project_id'])->save(); - } - - if (!$table->hasIndex(['status'])) { - $table->addIndex(['status'])->save(); - } - } - - protected function createBuildMetaTable() - { - $table = $this->table('build_meta'); - - if (!$this->hasTable('build_meta')) { - $table->create(); - } - - if (!$table->hasColumn('project_id')) { - $table->addColumn('project_id', 'integer')->save(); - } - - if (!$table->hasColumn('build_id')) { - $table->addColumn('build_id', 'integer')->save(); - } - - if (!$table->hasColumn('meta_key')) { - $table->addColumn('meta_key', 'string', ['limit' => 250])->save(); - } - - if (!$table->hasColumn('meta_value')) { - $table->addColumn('meta_value', 'text')->save(); - } - - if (!$table->hasIndex(['build_id', 'meta_key'])) { - $table->addIndex(['build_id', 'meta_key'])->save(); - } - } - - protected function createProjectTable() - { - $table = $this->table('project'); - - if (!$this->hasTable('project')) { - $table->create(); - } - - if (!$table->hasColumn('title')) { - $table->addColumn('title', 'string', ['limit' => 250])->save(); - } - - if (!$table->hasColumn('reference')) { - $table->addColumn('reference', 'string', ['limit' => 250])->save(); - } - - if (!$table->hasColumn('git_key')) { - $table->addColumn('git_key', 'text')->save(); - } - - if (!$table->hasColumn('public_key')) { - $table->addColumn('public_key', 'text')->save(); - } - - if (!$table->hasColumn('type')) { - $table->addColumn('type', 'string', ['limit' => 50])->save(); - } - - if (!$table->hasColumn('access_information')) { - $table->addColumn('access_information', 'string', ['limit' => 250])->save(); - } - - if (!$table->hasColumn('last_commit')) { - $table->addColumn('last_commit', 'string', ['limit' => 250])->save(); - } - - if (!$table->hasColumn('build_config')) { - $table->addColumn('build_config', 'text')->save(); - } - - if (!$table->hasColumn('allow_public_status')) { - $table->addColumn('allow_public_status', 'integer')->save(); - } - - if ($table->hasColumn('token')) { - $table->removeColumn('token')->save(); - } - - if (!$table->hasIndex(['title'])) { - $table->addIndex(['title'])->save(); - } - } - - protected function createUserTable() - { - $table = $this->table('user'); - - if (!$this->hasTable('user')) { - $table->create(); - } - - if (!$table->hasColumn('email')) { - $table->addColumn('email', 'string', ['limit' => 250])->save(); - } - - if (!$table->hasColumn('hash')) { - $table->addColumn('hash', 'string', ['limit' => 250])->save(); - } - - if (!$table->hasColumn('name')) { - $table->addColumn('name', 'string', ['limit' => 250])->save(); - } - - if (!$table->hasColumn('is_admin')) { - $table->addColumn('is_admin', 'integer')->save(); - } - - if (!$table->hasIndex(['email'])) { - $table->addIndex(['email'])->save(); - } - } -} diff --git a/src/PHPCensor/Migrations/20140513153133_change_build_keys_migration.php b/src/PHPCensor/Migrations/20140513153133_change_build_keys_migration.php deleted file mode 100644 index fd67bf4..0000000 --- a/src/PHPCensor/Migrations/20140513153133_change_build_keys_migration.php +++ /dev/null @@ -1,32 +0,0 @@ -table('project'); - - if (!$project->hasColumn('ssh_private_key') && $project->hasColumn('git_key')) { - $project->renameColumn('git_key', 'ssh_private_key'); - } - - if (!$project->hasColumn('ssh_public_key') && $project->hasColumn('public_key')) { - $project->renameColumn('public_key', 'ssh_public_key'); - } - } - - public function down() - { - $project = $this->table('project'); - - if (!$project->hasColumn('git_key') && $project->hasColumn('ssh_private_key')) { - $project->renameColumn('ssh_private_key', 'git_key'); - } - - if (!$project->hasColumn('public_key') && $project->hasColumn('ssh_public_key')) { - $project->renameColumn('ssh_public_key', 'public_key'); - } - } -} diff --git a/src/PHPCensor/Migrations/20140611170618_choose_branch.php b/src/PHPCensor/Migrations/20140611170618_choose_branch.php deleted file mode 100644 index 5d69ff0..0000000 --- a/src/PHPCensor/Migrations/20140611170618_choose_branch.php +++ /dev/null @@ -1,24 +0,0 @@ -table('project'); - - if (!$project->hasColumn('branch')) { - $project->addColumn('branch', 'string', ['after' => 'reference', 'limit' => 250])->save(); - } - } - - public function down() - { - $project = $this->table('project'); - - if ($project->hasColumn('branch')) { - $project->removeColumn('branch')->save(); - } - } -} diff --git a/src/PHPCensor/Migrations/20140730143702_fix_database_columns.php b/src/PHPCensor/Migrations/20140730143702_fix_database_columns.php deleted file mode 100644 index 11b3540..0000000 --- a/src/PHPCensor/Migrations/20140730143702_fix_database_columns.php +++ /dev/null @@ -1,55 +0,0 @@ -table('build'); - - $build->changeColumn('project_id', 'integer', ['null' => false]); - $build->changeColumn('commit_id', 'string', ['limit' => 50, 'null' => false]); - $build->changeColumn('status', 'integer', ['null' => false]); - $build->changeColumn('log', 'text', ['null' => true]); - $build->changeColumn('branch', 'string', ['limit' => 50, 'null' => false, 'default' => 'master']); - $build->changeColumn('created', 'datetime', ['null' => true]); - $build->changeColumn('started', 'datetime', ['null' => true]); - $build->changeColumn('finished', 'datetime', ['null' => true]); - $build->changeColumn('committer_email', 'string', ['limit' => 512, 'null' => true]); - $build->changeColumn('commit_message', 'text', ['null' => true]); - $build->changeColumn('extra', 'text', ['null' => true]); - - $buildMeta = $this->table('build_meta'); - - $buildMeta->changeColumn('project_id', 'integer', ['null' => false]); - $buildMeta->changeColumn('build_id', 'integer', ['null' => false]); - $buildMeta->changeColumn('meta_key', 'string', ['limit' => 250, 'null' => false]); - $buildMeta->changeColumn('meta_value', 'text', ['null' => false]); - - $project = $this->table('project'); - - $project->changeColumn('title', 'string', ['limit' => 250, 'null' => false]); - $project->changeColumn('reference', 'string', ['limit' => 250, 'null' => false]); - $project->changeColumn('branch', 'string', ['limit' => 50, 'null' => false, 'default' => 'master']); - $project->changeColumn('ssh_private_key', 'text', ['null' => true, 'default' => null]); - $project->changeColumn('ssh_public_key', 'text', ['null' => true, 'default' => null]); - $project->changeColumn('type', 'string', ['limit' => 50, 'null' => false]); - $project->changeColumn('access_information', 'string', ['limit' => 250, 'null' => true, 'default' => null]); - $project->changeColumn('last_commit', 'string', ['limit' => 250, 'null' => true, 'default' => null]); - $project->changeColumn('ssh_public_key', 'text', ['null' => true, 'default' => null]); - $project->changeColumn('allow_public_status', 'integer', ['null' => false, 'default' => 0]); - - $user = $this->table('user'); - - $user->changeColumn('email', 'string', ['limit' => 250, 'null' => false]); - $user->changeColumn('hash', 'string', ['limit' => 250, 'null' => false]); - $user->changeColumn('is_admin', 'integer', ['null' => false, 'default' => 0]); - $user->changeColumn('name', 'string', ['limit' => 250, 'null' => false]); - } - - public function down() - { - - } -} diff --git a/src/PHPCensor/Migrations/20150131075425_archive_project.php b/src/PHPCensor/Migrations/20150131075425_archive_project.php deleted file mode 100644 index 4d8bbfe..0000000 --- a/src/PHPCensor/Migrations/20150131075425_archive_project.php +++ /dev/null @@ -1,24 +0,0 @@ -table('project'); - - if (!$project->hasColumn('archived')) { - $project->addColumn('archived', 'boolean', ['default' => false])->save(); - } - } - - public function down() - { - $project = $this->table('project'); - - if ($project->hasColumn('archived')) { - $project->removeColumn('archived')->save(); - } - } -} diff --git a/src/PHPCensor/Migrations/20150203105015_fix_column_types.php b/src/PHPCensor/Migrations/20150203105015_fix_column_types.php deleted file mode 100644 index f13442b..0000000 --- a/src/PHPCensor/Migrations/20150203105015_fix_column_types.php +++ /dev/null @@ -1,28 +0,0 @@ -table('build'); - - $build->changeColumn('log', 'text', ['null' => true]); - - $buildMeta = $this->table('build_meta'); - - $buildMeta->changeColumn('meta_value', 'text', ['null' => false]); - } - - public function down() - { - $build = $this->table('build'); - - $build->changeColumn('log', 'text', ['null' => true]); - - $buildMeta = $this->table('build_meta'); - - $buildMeta->changeColumn('meta_value', 'text', ['null' => false]); - } -} diff --git a/src/PHPCensor/Migrations/20150308074509_add_user_providers.php b/src/PHPCensor/Migrations/20150308074509_add_user_providers.php deleted file mode 100644 index bae5ef8..0000000 --- a/src/PHPCensor/Migrations/20150308074509_add_user_providers.php +++ /dev/null @@ -1,34 +0,0 @@ -table('user') - // The provider name - ->addColumn('provider_key', 'string', [ - 'default' => 'internal', - 'limit' => 255 - ]) - // A data used by the provider - ->addColumn('provider_data', 'string', [ - 'null' => true, - 'limit' => 255 - ]) - ->save(); - } - - public function down() - { - // Remove the provider columns - $this - ->table('user') - ->removeColumn('provider_key') - ->removeColumn('provider_data') - ->save(); - } -} diff --git a/src/PHPCensor/Migrations/20150324174958_unique_email_and_name_user_fields.php b/src/PHPCensor/Migrations/20150324174958_unique_email_and_name_user_fields.php deleted file mode 100644 index 5a19ff1..0000000 --- a/src/PHPCensor/Migrations/20150324174958_unique_email_and_name_user_fields.php +++ /dev/null @@ -1,32 +0,0 @@ -table('user'); - - if (!$user_table->hasIndex('email', ['unique' => true])) { - $user_table->addIndex('email', ['unique' => true])->save(); - } - - if (!$user_table->hasIndex('name', ['unique' => true])) { - $user_table->addIndex('name', ['unique' => true])->save(); - } - } - - public function down() - { - $user_table = $this->table('user'); - - if ($user_table->hasIndex('email', ['unique' => true])) { - $user_table->removeIndex(['email'], ['unique' => true])->save(); - } - - if ($user_table->hasIndex('name', ['unique' => true])) { - $user_table->removeIndex(['name'], ['unique' => true])->save(); - } - } -} diff --git a/src/PHPCensor/Migrations/20151008140800_add_project_groups.php b/src/PHPCensor/Migrations/20151008140800_add_project_groups.php deleted file mode 100644 index 315b6f1..0000000 --- a/src/PHPCensor/Migrations/20151008140800_add_project_groups.php +++ /dev/null @@ -1,49 +0,0 @@ -table('project_group'); - - if (!$this->hasTable('project_group')) { - $table->create(); - } - - if (!$table->hasColumn('title')) { - $table - ->addColumn('title', 'string', ['limit' => 100, 'null' => false]) - ->save(); - } - - $table = $this->table('project'); - - if (!$table->hasColumn('group_id')) { - $table->addColumn('group_id', 'integer', ['signed' => true, 'null' => false, 'default' => 1,])->save(); - } - - if (!$table->hasForeignKey('group_id')) { - $table->addForeignKey('group_id', 'project_group', 'id', ['delete'=> 'RESTRICT', 'update' => 'CASCADE'])->save(); - } - } - - public function down() - { - $table = $this->table('project'); - - if ($table->hasForeignKey('group_id')) { - $table->dropForeignKey('group_id'); - } - - if ($table->hasColumn('group_id')) { - $table->removeColumn('group_id'); - } - - $table = $this->table('project_group'); - if ($this->hasTable('project_group')) { - $table->drop(); - } - } -} diff --git a/src/PHPCensor/Migrations/20151009100610_remove_unique_name_index.php b/src/PHPCensor/Migrations/20151009100610_remove_unique_name_index.php deleted file mode 100644 index 72c56a8..0000000 --- a/src/PHPCensor/Migrations/20151009100610_remove_unique_name_index.php +++ /dev/null @@ -1,19 +0,0 @@ -table('user'); - - if ($user->hasIndex('name', ['unique' => true])) { - $user->removeIndex(['name'], ['unique' => true])->save(); - } - - if (!$user->hasIndex('name', ['unique' => true])) { - $user->addIndex(['name'], ['unique' => false])->save(); - } - } -} diff --git a/src/PHPCensor/Migrations/20151014091859_errors_table.php b/src/PHPCensor/Migrations/20151014091859_errors_table.php deleted file mode 100644 index 6a8922d..0000000 --- a/src/PHPCensor/Migrations/20151014091859_errors_table.php +++ /dev/null @@ -1,68 +0,0 @@ -table('build_error'); - - if (!$this->hasTable('build_error')) { - $table->create(); - } - - if (!$table->hasColumn('build_id')) { - $table->addColumn('build_id', 'integer', ['signed' => true])->save(); - } - - if (!$table->hasColumn('plugin')) { - $table->addColumn('plugin', 'string', ['limit' => 100])->save(); - } - - if (!$table->hasColumn('file')) { - $table->addColumn('file', 'string', ['limit' => 250, 'null' => true])->save(); - } - - if (!$table->hasColumn('line_start')) { - $table->addColumn('line_start', 'integer', ['signed' => false, 'null' => true])->save(); - } - - if (!$table->hasColumn('line_end')) { - $table->addColumn('line_end', 'integer', ['signed' => false, 'null' => true])->save(); - } - - if (!$table->hasColumn('severity')) { - $table->addColumn('severity', 'integer', ['signed' => false, 'limit' => 255])->save(); - } - - if (!$table->hasColumn('message')) { - $table->addColumn('message', 'string', ['limit' => 250])->save(); - } - - if (!$table->hasColumn('created_date')) { - $table->addColumn('created_date', 'datetime')->save(); - } - - if (!$table->hasIndex(['build_id', 'created_date'], ['unique' => false])) { - $table->addIndex(['build_id', 'created_date'], ['unique' => false])->save(); - } - - if (!$table->hasForeignKey('build_id')) { - $table->addForeignKey('build_id', 'build', 'id', ['delete'=> 'CASCADE', 'update' => 'CASCADE'])->save(); - } - } - - public function down() - { - $table = $this->table('build_error'); - - if ($table->hasForeignKey('build_id')) { - $table->dropForeignKey('build_id')->save(); - } - - if ($this->hasTable('build_error')) { - $table->drop(); - } - } -} diff --git a/src/PHPCensor/Migrations/20151015124825_convert_errors.php b/src/PHPCensor/Migrations/20151015124825_convert_errors.php deleted file mode 100644 index 7cb0768..0000000 --- a/src/PHPCensor/Migrations/20151015124825_convert_errors.php +++ /dev/null @@ -1,182 +0,0 @@ -metaStore = Factory::getStore('BuildMeta'); - $this->errorStore = Factory::getStore('BuildError'); - - while ($count === 100) { - $data = $this->metaStore->getErrorsForUpgrade(100); - $count = count($data); - - /** @var \PHPCensor\Model\BuildMeta $meta */ - foreach ($data as $meta) { - switch ($meta->getMetaKey()) { - case 'phpmd-data': - $this->processPhpMdMeta($meta); - break; - - case 'phpcs-data': - $this->processPhpCsMeta($meta); - break; - - case 'phpdoccheck-data': - $this->processPhpDocCheckMeta($meta); - break; - - case 'phpcpd-data': - $this->processPhpCpdMeta($meta); - break; - - case 'technical_debt-data': - $this->processTechnicalDebtMeta($meta); - break; - } - - $this->metaStore->delete($meta); - } - } - } - - protected function processPhpMdMeta(BuildMeta $meta) - { - $data = json_decode($meta->getMetaValue(), true); - - if (is_array($data) && count($data)) { - foreach ($data as $error) { - $buildError = new BuildError(); - $buildError->setBuildId($meta->getBuildId()); - $buildError->setPlugin('php_mess_detector'); - $buildError->setCreateDate(new \DateTime()); - $buildError->setFile($error['file']); - $buildError->setLineStart($error['line_start']); - $buildError->setLineEnd($error['line_end']); - $buildError->setSeverity(BuildError::SEVERITY_HIGH); - $buildError->setMessage($error['message']); - - $this->errorStore->save($buildError); - } - } - } - - protected function processPhpCsMeta(BuildMeta $meta) - { - $data = json_decode($meta->getMetaValue(), true); - - if (is_array($data) && count($data)) { - foreach ($data as $error) { - $buildError = new BuildError(); - $buildError->setBuildId($meta->getBuildId()); - $buildError->setPlugin('php_code_sniffer'); - $buildError->setCreateDate(new \DateTime()); - $buildError->setFile($error['file']); - $buildError->setLineStart($error['line']); - $buildError->setLineEnd($error['line']); - $buildError->setMessage($error['message']); - - switch ($error['type']) { - case 'ERROR': - $buildError->setSeverity(BuildError::SEVERITY_HIGH); - break; - - case 'WARNING': - $buildError->setSeverity(BuildError::SEVERITY_LOW); - break; - } - - $this->errorStore->save($buildError); - } - } - } - - protected function processPhpDocCheckMeta(BuildMeta $meta) - { - $data = json_decode($meta->getMetaValue(), true); - - if (is_array($data) && count($data)) { - foreach ($data as $error) { - $buildError = new BuildError(); - $buildError->setBuildId($meta->getBuildId()); - $buildError->setPlugin('php_docblock_checker'); - $buildError->setCreateDate(new \DateTime()); - $buildError->setFile($error['file']); - $buildError->setLineStart($error['line']); - $buildError->setLineEnd($error['line']); - - switch ($error['type']) { - case 'method': - $buildError->setMessage($error['class'] . '::' . $error['method'] . ' is missing a docblock.'); - $buildError->setSeverity(BuildError::SEVERITY_NORMAL); - break; - - case 'class': - $buildError->setMessage('Class ' . $error['class'] . ' is missing a docblock.'); - $buildError->setSeverity(BuildError::SEVERITY_LOW); - break; - } - - $this->errorStore->save($buildError); - } - } - } - - protected function processPhpCpdMeta(BuildMeta $meta) - { - $data = json_decode($meta->getMetaValue(), true); - - if (is_array($data) && count($data)) { - foreach ($data as $error) { - $buildError = new BuildError(); - $buildError->setBuildId($meta->getBuildId()); - $buildError->setPlugin('php_cpd'); - $buildError->setCreateDate(new \DateTime()); - $buildError->setFile($error['file']); - $buildError->setLineStart($error['line_start']); - $buildError->setLineEnd($error['line_end']); - $buildError->setSeverity(BuildError::SEVERITY_NORMAL); - $buildError->setMessage('Copy and paste detected.'); - - $this->errorStore->save($buildError); - } - } - } - - protected function processTechnicalDebtMeta(BuildMeta $meta) - { - $data = json_decode($meta->getMetaValue(), true); - - if (is_array($data) && count($data)) { - foreach ($data as $error) { - $buildError = new BuildError(); - $buildError->setBuildId($meta->getBuildId()); - $buildError->setPlugin('technical_debt'); - $buildError->setCreateDate(new \DateTime()); - $buildError->setFile($error['file']); - $buildError->setLineStart($error['line']); - $buildError->setSeverity(BuildError::SEVERITY_NORMAL); - $buildError->setMessage($error['message']); - - $this->errorStore->save($buildError); - } - } - } -} diff --git a/src/PHPCensor/Migrations/20160425162114_branch_column_length.php b/src/PHPCensor/Migrations/20160425162114_branch_column_length.php deleted file mode 100644 index 2dfa229..0000000 --- a/src/PHPCensor/Migrations/20160425162114_branch_column_length.php +++ /dev/null @@ -1,24 +0,0 @@ -table('build'); - $table->changeColumn('branch', 'string', ['limit' => 250, 'null' => false, 'default' => 'master']); - - $table = $this->table('project'); - $table->changeColumn('branch', 'string', ['limit' => 250, 'null' => false, 'default' => 'master']); - } - - public function down() - { - $table = $this->table('build'); - $table->changeColumn('branch', 'string', ['limit' => 50, 'null' => false, 'default' => 'master']); - - $table = $this->table('project'); - $table->changeColumn('branch', 'string', ['limit' => 50, 'null' => false, 'default' => 'master']); - } -} diff --git a/src/PHPCensor/Migrations/20160623100223_project_table_defaults.php b/src/PHPCensor/Migrations/20160623100223_project_table_defaults.php deleted file mode 100644 index a25b993..0000000 --- a/src/PHPCensor/Migrations/20160623100223_project_table_defaults.php +++ /dev/null @@ -1,13 +0,0 @@ -table('project') - ->changeColumn('build_config', 'text', ['null' => true]) - ->save(); - } -} diff --git a/src/PHPCensor/Migrations/20170103163312_added_language_and_per_page_for_user.php b/src/PHPCensor/Migrations/20170103163312_added_language_and_per_page_for_user.php deleted file mode 100644 index befc77f..0000000 --- a/src/PHPCensor/Migrations/20170103163312_added_language_and_per_page_for_user.php +++ /dev/null @@ -1,40 +0,0 @@ -table('user'); - - if (!$table->hasColumn('language')) { - $table - ->addColumn('language', 'string', ['limit' => 5, 'null' => true]) - ->save(); - } - - if (!$table->hasColumn('per_page')) { - $table - ->addColumn('per_page', 'integer', ['null' => true]) - ->save(); - } - } - - public function down() - { - $table = $this->table('user'); - - if ($table->hasColumn('language')) { - $table - ->removeColumn('language') - ->save(); - } - - if ($table->hasColumn('per_page')) { - $table - ->removeColumn('per_page') - ->save(); - } - } -} diff --git a/src/PHPCensor/Migrations/20170218175400_fixed_build_error_message_column.php b/src/PHPCensor/Migrations/20170218175400_fixed_build_error_message_column.php deleted file mode 100644 index c142fd5..0000000 --- a/src/PHPCensor/Migrations/20170218175400_fixed_build_error_message_column.php +++ /dev/null @@ -1,18 +0,0 @@ -table('build_error') - ->changeColumn('message', 'text') - ->save(); - } - - public function down() - { - } -} diff --git a/src/PHPCensor/Migrations/20170223113127_fixed_build_log_column_for_mysql.php b/src/PHPCensor/Migrations/20170223113127_fixed_build_log_column_for_mysql.php deleted file mode 100644 index 48b956a..0000000 --- a/src/PHPCensor/Migrations/20170223113127_fixed_build_log_column_for_mysql.php +++ /dev/null @@ -1,26 +0,0 @@ -getAdapter(); - if ($adapter instanceof MysqlAdapter) { - $this - ->table('build') - ->changeColumn( - 'log', - MysqlAdapter::PHINX_TYPE_TEXT, - ['limit' => MysqlAdapter::TEXT_LONG] - ) - ->save(); - } - } - - public function down() - { - } -} diff --git a/src/PHPCensor/Migrations/20170226132922_fixed_build_log_column_for_mysql2.php b/src/PHPCensor/Migrations/20170226132922_fixed_build_log_column_for_mysql2.php deleted file mode 100644 index 7ff4e4f..0000000 --- a/src/PHPCensor/Migrations/20170226132922_fixed_build_log_column_for_mysql2.php +++ /dev/null @@ -1,26 +0,0 @@ -getAdapter(); - if ($adapter instanceof MysqlAdapter) { - $this - ->table('build') - ->changeColumn( - 'log', - MysqlAdapter::PHINX_TYPE_TEXT, - ['limit' => MysqlAdapter::TEXT_LONG, 'null' => true] - ) - ->save(); - } - } - - public function down() - { - } -} diff --git a/src/PHPCensor/Migrations/20170321131931_add_environment.php b/src/PHPCensor/Migrations/20170321131931_add_environment.php deleted file mode 100644 index 5d2b860..0000000 --- a/src/PHPCensor/Migrations/20170321131931_add_environment.php +++ /dev/null @@ -1,64 +0,0 @@ -table('environment'); - - if (!$this->hasTable('environment')) { - $table->create(); - } - - if (!$table->hasColumn('project_id')) { - $table - ->addColumn('project_id', 'integer') - ->save(); - } - - if (!$table->hasColumn('name')) { - $table - ->addColumn('name', 'string', ['limit' => 250]) - ->save(); - } - - if (!$table->hasColumn('branches')) { - $table - ->addColumn('branches', 'text') - ->save(); - } - - if (!$table->hasIndex(['project_id', 'name'])) { - $table - ->addIndex(['project_id', 'name']) - ->save(); - } - - $table = $this->table('build'); - - if (!$table->hasColumn('environment')) { - $table - ->addColumn('environment', 'string', ['limit' => 250]) - ->save(); - } - } - - public function down() - { - $table = $this->table('environment'); - - if ($this->hasTable('environment')) { - $table->drop(); - } - - $table = $this->table('build'); - - if ($table->hasColumn('environment')) { - $table - ->removeColumn('environment') - ->save(); - } - } -} diff --git a/src/PHPCensor/Migrations/20170413131256_added_source_column_to_build_table.php b/src/PHPCensor/Migrations/20170413131256_added_source_column_to_build_table.php deleted file mode 100644 index a63a3a1..0000000 --- a/src/PHPCensor/Migrations/20170413131256_added_source_column_to_build_table.php +++ /dev/null @@ -1,33 +0,0 @@ -table('build'); - - if (!$table->hasColumn('source')) { - $table - ->addColumn('source', 'integer', ['default' => Build::SOURCE_UNKNOWN]) - ->save(); - - $this->execute("UPDATE build SET source = 4"); - $this->execute("UPDATE build SET source = 1, commit_id = '', commit_message = '' WHERE commit_id = 'Manual'"); - $this->execute("UPDATE build SET source = 1, commit_message = '' WHERE commit_message = 'Manual'"); - } - } - - public function down() - { - $table = $this->table('build'); - - if ($table->hasColumn('source')) { - $table - ->removeColumn('source') - ->save(); - } - } -} diff --git a/src/PHPCensor/Migrations/20170416130610_fixed_environments.php b/src/PHPCensor/Migrations/20170416130610_fixed_environments.php deleted file mode 100644 index 8c2b96b..0000000 --- a/src/PHPCensor/Migrations/20170416130610_fixed_environments.php +++ /dev/null @@ -1,28 +0,0 @@ -table('build'); - - if ($table->hasColumn('environment')) { - $table - ->changeColumn('environment', 'string', ['limit' => 250, 'null' => true]) - ->save(); - } - } - - public function down() - { - $table = $this->table('build'); - - if ($table->hasColumn('environment')) { - $table - ->changeColumn('environment', 'string', ['limit' => 250, 'null' => false]) - ->save(); - } - } -} diff --git a/src/PHPCensor/Migrations/20170420142131_added_tag_column_to_build_table.php b/src/PHPCensor/Migrations/20170420142131_added_tag_column_to_build_table.php deleted file mode 100644 index 39b752a..0000000 --- a/src/PHPCensor/Migrations/20170420142131_added_tag_column_to_build_table.php +++ /dev/null @@ -1,28 +0,0 @@ -table('build'); - - if (!$table->hasColumn('tag')) { - $table - ->addColumn('tag', 'string', ['limit' => 250, 'null' => true]) - ->save(); - } - } - - public function down() - { - $table = $this->table('build'); - - if ($table->hasColumn('tag')) { - $table - ->removeColumn('tag') - ->save(); - } - } -} diff --git a/src/PHPCensor/Migrations/20170711112805_fixed_build_meta_for_mysql.php b/src/PHPCensor/Migrations/20170711112805_fixed_build_meta_for_mysql.php deleted file mode 100644 index 745f430..0000000 --- a/src/PHPCensor/Migrations/20170711112805_fixed_build_meta_for_mysql.php +++ /dev/null @@ -1,26 +0,0 @@ -getAdapter(); - if ($adapter instanceof MysqlAdapter) { - $this - ->table('build_meta') - ->changeColumn( - 'meta_value', - MysqlAdapter::PHINX_TYPE_TEXT, - ['limit' => MysqlAdapter::TEXT_LONG, 'null' => false] - ) - ->save(); - } - } - - public function down() - { - } -} diff --git a/src/PHPCensor/Migrations/20170828142020_added_remember_me_login.php b/src/PHPCensor/Migrations/20170828142020_added_remember_me_login.php deleted file mode 100644 index da88adc..0000000 --- a/src/PHPCensor/Migrations/20170828142020_added_remember_me_login.php +++ /dev/null @@ -1,28 +0,0 @@ -table('user'); - - if (!$table->hasColumn('remember_key')) { - $table - ->addColumn('remember_key', 'string', ['limit' => 32, 'null' => true]) - ->save(); - } - } - - public function down() - { - $table = $this->table('user'); - - if ($table->hasColumn('remember_key')) { - $table - ->removeColumn('remember_key') - ->save(); - } - } -} diff --git a/src/PHPCensor/Migrations/20170913141438_added_default_branch_only.php b/src/PHPCensor/Migrations/20170913141438_added_default_branch_only.php deleted file mode 100644 index af12571..0000000 --- a/src/PHPCensor/Migrations/20170913141438_added_default_branch_only.php +++ /dev/null @@ -1,28 +0,0 @@ -table('project'); - - if (!$table->hasColumn('default_branch_only')) { - $table - ->addColumn('default_branch_only', 'integer', ['default' => 0]) - ->save(); - } - } - - public function down() - { - $table = $this->table('project'); - - if ($table->hasColumn('default_branch_only')) { - $table - ->removeColumn('default_branch_only') - ->save(); - } - } -} diff --git a/src/PHPCensor/Migrations/20171014173348_removed_project_id_from_build_meta.php b/src/PHPCensor/Migrations/20171014173348_removed_project_id_from_build_meta.php deleted file mode 100644 index 24f5794..0000000 --- a/src/PHPCensor/Migrations/20171014173348_removed_project_id_from_build_meta.php +++ /dev/null @@ -1,39 +0,0 @@ -table('build_meta'); - - if ($table->hasForeignKey('project_id')) { - $table->dropForeignKey('project_id'); - } - - if ($table->hasColumn('project_id')) { - $table - ->removeColumn('project_id') - ->save(); - } - } - - public function down() - { - $table = $this->table('build_meta'); - - if (!$table->hasColumn('project_id')) { - $table - ->addColumn( - 'project_id', - 'integer', [ - 'default' => 0, - 'after' => 'id' - ] - ) - ->addForeignKey('project_id', 'project', 'id', ['delete'=> 'CASCADE', 'update' => 'CASCADE']) - ->save(); - } - } -} diff --git a/src/PHPCensor/Migrations/20171015123827_added_additional_columns.php b/src/PHPCensor/Migrations/20171015123827_added_additional_columns.php deleted file mode 100644 index b859289..0000000 --- a/src/PHPCensor/Migrations/20171015123827_added_additional_columns.php +++ /dev/null @@ -1,64 +0,0 @@ -table('build'); - - if (!$table->hasColumn('user_id')) { - $table - ->addColumn('user_id', 'integer', ['default' => 0]) - ->save(); - } - - if ($table->hasColumn('created')) { - $table - ->renameColumn('created', 'create_date') - ->save(); - } - - if ($table->hasColumn('started')) { - $table - ->renameColumn('started', 'start_date') - ->save(); - } - - if ($table->hasColumn('finished')) { - $table - ->renameColumn('finished', 'finish_date') - ->save(); - } - } - - public function down() - { - $table = $this->table('build'); - - if ($table->hasColumn('user_id')) { - $table - ->removeColumn('user_id') - ->save(); - } - - if ($table->hasColumn('create_date')) { - $table - ->renameColumn('create_date', 'created') - ->save(); - } - - if ($table->hasColumn('start_date')) { - $table - ->renameColumn('start_date', 'started') - ->save(); - } - - if ($table->hasColumn('finish_date')) { - $table - ->renameColumn('finish_date', 'finished') - ->save(); - } - } -} diff --git a/src/PHPCensor/Migrations/20171016143000_added_additional_columns2.php b/src/PHPCensor/Migrations/20171016143000_added_additional_columns2.php deleted file mode 100644 index 2516c6c..0000000 --- a/src/PHPCensor/Migrations/20171016143000_added_additional_columns2.php +++ /dev/null @@ -1,40 +0,0 @@ -table('project_group'); - - if (!$table->hasColumn('create_date')) { - $table - ->addColumn('create_date', 'datetime', ['null' => true]) - ->save(); - } - - if (!$table->hasColumn('user_id')) { - $table - ->addColumn('user_id', 'integer', ['default' => 0]) - ->save(); - } - } - - public function down() - { - $table = $this->table('project_group'); - - if ($table->hasColumn('create_date')) { - $table - ->removeColumn('create_date') - ->save(); - } - - if ($table->hasColumn('user_id')) { - $table - ->removeColumn('user_id') - ->save(); - } - } -} diff --git a/src/PHPCensor/Migrations/20171019143346_added_additional_columns3.php b/src/PHPCensor/Migrations/20171019143346_added_additional_columns3.php deleted file mode 100644 index cd305cd..0000000 --- a/src/PHPCensor/Migrations/20171019143346_added_additional_columns3.php +++ /dev/null @@ -1,56 +0,0 @@ -table('build_error'); - - if ($table->hasColumn('created_date') && !$table->hasColumn('create_date')) { - $table - ->renameColumn('created_date', 'create_date') - ->save(); - } - - $table = $this->table('project'); - - if (!$table->hasColumn('create_date')) { - $table - ->addColumn('create_date', 'datetime', ['null' => true]) - ->save(); - } - - if (!$table->hasColumn('user_id')) { - $table - ->addColumn('user_id', 'integer', ['default' => 0]) - ->save(); - } - } - - public function down() - { - $table = $this->table('build_error'); - - if ($table->hasColumn('create_date') && !$table->hasColumn('created_date')) { - $table - ->renameColumn('create_date', 'created_date') - ->save(); - } - - $table = $this->table('project'); - - if ($table->hasColumn('create_date')) { - $table - ->removeColumn('create_date') - ->save(); - } - - if ($table->hasColumn('user_id')) { - $table - ->removeColumn('user_id') - ->save(); - } - } -} diff --git a/src/PHPCensor/Model.php b/src/PHPCensor/Model.php deleted file mode 100644 index b11c4ad..0000000 --- a/src/PHPCensor/Model.php +++ /dev/null @@ -1,7 +0,0 @@ - - */ -class Build extends Model -{ - const STAGE_SETUP = 'setup'; - const STAGE_TEST = 'test'; - const STAGE_DEPLOY = 'deploy'; - const STAGE_COMPLETE = 'complete'; - const STAGE_SUCCESS = 'success'; - const STAGE_FAILURE = 'failure'; - const STAGE_FIXED = 'fixed'; - const STAGE_BROKEN = 'broken'; - - const STATUS_PENDING = 0; - const STATUS_RUNNING = 1; - const STATUS_SUCCESS = 2; - const STATUS_FAILED = 3; - - const SOURCE_UNKNOWN = 0; - const SOURCE_MANUAL_WEB = 1; - const SOURCE_MANUAL_CONSOLE = 2; - const SOURCE_PERIODICAL = 3; - const SOURCE_WEBHOOK = 4; - - /** - * @var array - */ - public static $sleepable = []; - - /** - * @var string - */ - protected $tableName = 'build'; - - /** - * @var string - */ - protected $modelName = 'Build'; - - /** - * @var array - */ - protected $data = [ - 'id' => null, - 'project_id' => null, - 'commit_id' => null, - 'status' => null, - 'log' => null, - 'branch' => null, - 'tag' => null, - 'create_date' => null, - 'start_date' => null, - 'finish_date' => null, - 'committer_email' => null, - 'commit_message' => null, - 'extra' => null, - 'environment' => null, - 'source' => Build::SOURCE_UNKNOWN, - 'user_id' => 0, - ]; - - /** - * @var array - */ - protected $getters = [ - // Direct property getters: - 'id' => 'getId', - 'project_id' => 'getProjectId', - 'commit_id' => 'getCommitId', - 'status' => 'getStatus', - 'log' => 'getLog', - 'branch' => 'getBranch', - 'tag' => 'getTag', - 'create_date' => 'getCreateDate', - 'start_date' => 'getStartDate', - 'finish_date' => 'getFinishDate', - 'committer_email' => 'getCommitterEmail', - 'commit_message' => 'getCommitMessage', - 'extra' => 'getExtra', - 'environment' => 'getEnvironment', - 'source' => 'getSource', - 'user_id' => 'getUserId', - - // Foreign key getters: - 'Project' => 'getProject', - ]; - - /** - * @var array - */ - protected $setters = [ - // Direct property setters: - 'id' => 'setId', - 'project_id' => 'setProjectId', - 'commit_id' => 'setCommitId', - 'status' => 'setStatus', - 'log' => 'setLog', - 'branch' => 'setBranch', - 'setTag' => 'setTag', - 'create_date' => 'setCreateDate', - 'start_date' => 'setStartDate', - 'finish_date' => 'setFinishDate', - 'committer_email' => 'setCommitterEmail', - 'commit_message' => 'setCommitMessage', - 'extra' => 'setExtra', - 'environment' => 'setEnvironment', - 'source' => 'setSource', - 'user_id' => 'setUserId', - - // Foreign key setters: - 'Project' => 'setProject', - ]; - - /** - * @return integer - */ - public function getId() - { - $rtn = $this->data['id']; - - return (integer)$rtn; - } - - /** - * @param $value int - */ - public function setId($value) - { - $this->validateNotNull('id', $value); - $this->validateInt('id', $value); - - if ($this->data['id'] === $value) { - return; - } - - $this->data['id'] = $value; - - $this->setModified('id'); - } - - /** - * @return integer - */ - public function getProjectId() - { - $rtn = $this->data['project_id']; - - return (integer)$rtn; - } - - /** - * @param $value int - */ - public function setProjectId($value) - { - $this->validateNotNull('project_id', $value); - $this->validateInt('project_id', $value); - - if ($this->data['project_id'] === $value) { - return; - } - - $this->data['project_id'] = $value; - - $this->setModified('project_id'); - } - - /** - * @return string - */ - public function getCommitId() - { - $rtn = $this->data['commit_id']; - - return $rtn; - } - - /** - * @param $value string - */ - public function setCommitId($value) - { - $this->validateNotNull('commit_id', $value); - $this->validateString('commit_id', $value); - - if ($this->data['commit_id'] === $value) { - return; - } - - $this->data['commit_id'] = $value; - - $this->setModified('commit_id'); - } - - /** - * @return integer - */ - public function getStatus() - { - $rtn = $this->data['status']; - - return (integer)$rtn; - } - - /** - * @param $value int - */ - public function setStatus($value) - { - $this->validateNotNull('status', $value); - $this->validateInt('status', $value); - - if ($this->data['status'] === $value) { - return; - } - - $this->data['status'] = $value; - - $this->setModified('status'); - } - - /** - * @return string - */ - public function getLog() - { - $rtn = $this->data['log']; - - return $rtn; - } - - /** - * @param $value string - */ - public function setLog($value) - { - $this->validateString('log', $value); - - if ($this->data['log'] === $value) { - return; - } - - $this->data['log'] = $value; - - $this->setModified('log'); - } - - /** - * @return string - */ - public function getBranch() - { - $rtn = $this->data['branch']; - - return $rtn; - } - - /** - * @param $value string - */ - public function setBranch($value) - { - $this->validateNotNull('branch', $value); - $this->validateString('branch', $value); - - if ($this->data['branch'] === $value) { - return; - } - - $this->data['branch'] = $value; - - $this->setModified('branch'); - } - - /** - * @return \DateTime - */ - public function getCreateDate() - { - $rtn = $this->data['create_date']; - - if (!empty($rtn)) { - $rtn = new \DateTime($rtn); - } - - return $rtn; - } - - /** - * @param $value \DateTime - */ - public function setCreateDate($value) - { - $this->validateDate('create_date', $value); - - if ($this->data['create_date'] === $value) { - return; - } - - $this->data['create_date'] = $value; - - $this->setModified('create_date'); - } - - /** - * @return \DateTime - */ - public function getStartDate() - { - $rtn = $this->data['start_date']; - - if (!empty($rtn)) { - $rtn = new \DateTime($rtn); - } - - return $rtn; - } - - /** - * @param $value \DateTime - */ - public function setStartDate($value) - { - $this->validateDate('start_date', $value); - - if ($this->data['start_date'] === $value) { - return; - } - - $this->data['start_date'] = $value; - - $this->setModified('start_date'); - } - - /** - * @return \DateTime - */ - public function getFinishDate() - { - $rtn = $this->data['finish_date']; - - if (!empty($rtn)) { - $rtn = new \DateTime($rtn); - } - - return $rtn; - } - - /** - * @param $value \DateTime - */ - public function setFinishDate($value) - { - $this->validateDate('finish_date', $value); - - if ($this->data['finish_date'] === $value) { - return; - } - - $this->data['finish_date'] = $value; - - $this->setModified('finish_date'); - } - - /** - * @return string - */ - public function getCommitterEmail() - { - $rtn = $this->data['committer_email']; - - return $rtn; - } - - /** - * @param $value string - */ - public function setCommitterEmail($value) - { - $this->validateString('committer_email', $value); - - if ($this->data['committer_email'] === $value) { - return; - } - - $this->data['committer_email'] = $value; - - $this->setModified('committer_email'); - } - - /** - * @return string - */ - public function getCommitMessage() - { - $rtn = htmlspecialchars($this->data['commit_message']); - - return $rtn; - } - - /** - * @param $value string - */ - public function setCommitMessage($value) - { - $this->validateString('commit_message', $value); - - if ($this->data['commit_message'] === $value) { - return; - } - - $this->data['commit_message'] = $value; - - $this->setModified('commit_message'); - } - - /** - * @return string - */ - public function getTag() - { - $rtn = $this->data['tag']; - - return $rtn; - } - - /** - * @param $value string - */ - public function setTag($value) - { - $this->validateString('tag', $value); - - if ($this->data['tag'] === $value) { - return; - } - - $this->data['tag'] = $value; - - $this->setModified('tag'); - } - - /** - * @return string - */ - public function getSource() - { - $rtn = $this->data['source']; - - return (integer)$rtn; - } - - /** - * @param $value integer - */ - public function setSource($value) - { - $this->validateInt('source', $value); - - if ($this->data['source'] === $value) { - return; - } - - $this->data['source'] = $value; - - $this->setModified('source'); - } - - /** - * @return string - */ - public function getUserId() - { - $rtn = $this->data['user_id']; - - return (integer)$rtn; - } - - /** - * @param $value integer - */ - public function setUserId($value) - { - $this->validateNotNull('user_id', $value); - $this->validateInt('user_id', $value); - - if ($this->data['user_id'] === $value) { - return; - } - - $this->data['user_id'] = $value; - - $this->setModified('user_id'); - } - - /** - * @return string - */ - public function getEnvironment() - { - $rtn = $this->data['environment']; - - return $rtn; - } - - /** - * @param $value string - */ - public function setEnvironment($value) - { - $this->validateString('environment', $value); - - if ($this->data['environment'] === $value) { - return; - } - - $this->data['environment'] = $value; - - $this->setModified('environment'); - } - - /** - * Set the value of status only if it synced with db. Must not be null. - * - * @param $value int - * @return bool - */ - public function setStatusSync($value) - { - $this->validateNotNull('status', $value); - $this->validateInt('status', $value); - - if ($this->data['status'] !== $value) { - $store = Factory::getStore('Build'); - if ($store->updateStatusSync($this, $value)) { - $this->data['status'] = $value; - return true; - } - } - return false; - } - - /** - * Return a value from the build's "extra" JSON array. - * - * @param null $key - * - * @return mixed|null|string - */ - public function getExtra($key = null) - { - $data = json_decode($this->data['extra'], true); - - if (is_null($key)) { - $rtn = $data; - } elseif (isset($data[$key])) { - $rtn = $data[$key]; - } else { - $rtn = null; - } - - return $rtn; - } - - /** - * @param $value string - */ - public function setExtra($value) - { - $this->validateString('extra', $value); - - if ($this->data['extra'] === $value) { - return; - } - - $this->data['extra'] = $value; - - $this->setModified('extra'); - } - - /** - * Set the value of extra. - * - * @param $name string - * @param $value mixed - */ - public function setExtraValue($name, $value) - { - $extra = json_decode($this->data['extra'], true); - if ($extra === false) { - $extra = []; - } - $extra[$name] = $value; - $this->setExtra(json_encode($extra)); - } - - /** - * Set the values of extra. - * - * @param $values mixed - */ - public function setExtraValues($values) - { - $extra = json_decode($this->data['extra'], true); - if ($extra === false) { - $extra = []; - } - $extra = array_replace($extra, $values); - $this->setExtra(json_encode($extra)); - } - - /** - * Get the Project model for this Build by Id. - * - * @return \PHPCensor\Model\Project - */ - public function getProject() - { - $key = $this->getProjectId(); - - if (empty($key)) { - return null; - } - - return Factory::getStore('Project', 'PHPCensor')->getById($key); - } - - /** - * Set Project - Accepts an ID, an array representing a Project or a Project model. - * - * @param $value mixed - */ - public function setProject($value) - { - // Is this an instance of Project? - if ($value instanceof Project) { - return $this->setProjectObject($value); - } - - // Is this an array representing a Project item? - if (is_array($value) && !empty($value['id'])) { - return $this->setProjectId($value['id']); - } - - // Is this a scalar value representing the ID of this foreign key? - return $this->setProjectId($value); - } - - /** - * Set Project - Accepts a Project model. - * - * @param $value Project - */ - public function setProjectObject(Project $value) - { - return $this->setProjectId($value->getId()); - } - - /** - * Get BuildError models by BuildId for this Build. - * - * @return \PHPCensor\Model\BuildError[] - */ - public function getBuildBuildErrors() - { - return Factory::getStore('BuildError', 'PHPCensor')->getByBuildId($this->getId()); - } - - /** - * Get BuildMeta models by BuildId for this Build. - * - * @return \PHPCensor\Model\BuildMeta[] - */ - public function getBuildBuildMetas() - { - return Factory::getStore('BuildMeta', 'PHPCensor')->getByBuildId($this->getId()); - } - - public $currentBuildPath; - - /** - * Get link to commit from another source (i.e. Github) - */ - public function getCommitLink() - { - return '#'; - } - - /** - * Get link to branch from another source (i.e. Github) - */ - public function getBranchLink() - { - return '#'; - } - - /** - * Get link to tag from another source (i.e. Github) - */ - public function getTagLink() - { - return '#'; - } - - /** - * Return a template to use to generate a link to a specific file. - * - * @return null - */ - public function getFileLinkTemplate() - { - return null; - } - - /** - * Send status updates to any relevant third parties (i.e. Github) - */ - public function sendStatusPostback() - { - return false; - } - - /** - * @return string - */ - public function getProjectTitle() - { - $project = $this->getProject(); - return $project ? $project->getTitle() : ""; - } - - /** - * Store build metadata - */ - public function storeMeta($key, $value) - { - $value = json_encode($value); - Factory::getStore('Build')->setMeta($this->getId(), $key, $value); - } - - /** - * Is this build successful? - */ - public function isSuccessful() - { - return ($this->getStatus() === self::STATUS_SUCCESS); - } - - /** - * @param Builder $builder - * @param string $buildPath - * - * @return bool - */ - protected function handleConfig(Builder $builder, $buildPath) - { - $build_config = $this->getProject()->getBuildConfig(); - - if (empty($build_config)) { - if (file_exists($buildPath . '/.php-censor.yml')) { - $build_config = file_get_contents($buildPath . '/.php-censor.yml'); - } elseif (file_exists($buildPath . '/.phpci.yml')) { - $build_config = file_get_contents($buildPath . '/.phpci.yml'); - } elseif (file_exists($buildPath . '/phpci.yml')) { - $build_config = file_get_contents($buildPath . '/phpci.yml'); - } else { - $build_config = $this->getZeroConfigPlugins($builder); - } - } - - // for YAML configs from files/DB - if (is_string($build_config)) { - $yamlParser = new YamlParser(); - $build_config = $yamlParser->parse($build_config); - } - - $builder->setConfigArray($build_config); - - return true; - } - - /** - * Get an array of plugins to run if there's no .php-censor.yml file. - * @param Builder $builder - * @return array - */ - protected function getZeroConfigPlugins(Builder $builder) - { - $pluginDir = SRC_DIR . 'Plugin' . DIRECTORY_SEPARATOR; - $dir = new \DirectoryIterator($pluginDir); - - $config = [ - 'build_settings' => [ - 'ignore' => [ - 'vendor', - ] - ] - ]; - - foreach ($dir as $item) { - if ($item->isDot()) { - continue; - } - - if (!$item->isFile()) { - continue; - } - - if ($item->getExtension() != 'php') { - continue; - } - - $className = '\PHPCensor\Plugin\\'.$item->getBasename('.php'); - - $reflectedPlugin = new \ReflectionClass($className); - - if (!$reflectedPlugin->implementsInterface('\PHPCensor\ZeroConfigPluginInterface')) { - continue; - } - - foreach ([Build::STAGE_SETUP, Build::STAGE_TEST] as $stage) { - if ($className::canExecute($stage, $builder, $this)) { - $config[$stage][$className::pluginName()] = [ - 'zero_config' => true - ]; - } - } - } - - return $config; - } - - /** - * Allows specific build types (e.g. Github) to report violations back to their respective services. - * @param Builder $builder - * @param $plugin - * @param $message - * @param int $severity - * @param null $file - * @param null $lineStart - * @param null $lineEnd - */ - public function reportError( - Builder $builder, - $plugin, - $message, - $severity = BuildError::SEVERITY_NORMAL, - $file = null, - $lineStart = null, - $lineEnd = null - ) { - $writer = $builder->getBuildErrorWriter(); - $writer->write( - $plugin, - $message, - $severity, - $file, - $lineStart, - $lineEnd - ); - } - - /** - * Return the path to run this build into. - * - * @return string|null - */ - public function getBuildPath() - { - if (!$this->getId()) { - return null; - } - - if (empty($this->currentBuildPath)) { - $buildDirectory = $this->getId() . '_' . substr(md5(microtime(true)), 0, 5); - $this->currentBuildPath = - RUNTIME_DIR . - 'builds' . - DIRECTORY_SEPARATOR . - $buildDirectory . - DIRECTORY_SEPARATOR; - } - - return $this->currentBuildPath; - } - - /** - * Removes the build directory. - */ - public function removeBuildDirectory() - { - // Get the path and remove the trailing slash as this may prompt PHP - // to see this as a directory even if it's a link. - $buildPath = rtrim($this->getBuildPath(), '/'); - - if (!$buildPath || !is_dir($buildPath)) { - return; - } - - if (is_link($buildPath)) { - // Remove the symlink without using recursive. - exec(sprintf('rm "%s"', $buildPath)); - } else { - exec(sprintf('rm -Rf "%s"', $buildPath)); - } - } - - /** - * Get the number of seconds a build has been running for. - * - * @return int - */ - public function getDuration() - { - $start = $this->getStartDate(); - - if (empty($start)) { - return 0; - } - - $end = $this->getFinishDate(); - - if (empty($end)) { - $end = new \DateTime(); - } - - return $end->getTimestamp() - $start->getTimestamp(); - } - - /** - * get time a build has been running for in hour/minute/seconds format (e.g. 1h 21m 45s) - * - * @return string - */ - public function getPrettyDuration() - { - $start = $this->getStartDate(); - if (!$start) { - $start = new \DateTime(); - } - $end = $this->getFinishDate(); - if (!$end) { - $end = new \DateTime(); - } - - $diff = date_diff($start, $end); - $parts = []; - foreach (['y', 'm', 'd', 'h', 'i', 's'] as $time_part) { - if ($diff->{$time_part} != 0) { - $parts[] = $diff->{$time_part} . ($time_part == 'i' ? 'm' : $time_part); - } - } - - return implode(" ", $parts); - } - - /** - * Create a working copy by cloning, copying, or similar. - * - * @param Builder $builder - * @param string $buildPath - * - * @return boolean - */ - public function createWorkingCopy(Builder $builder, $buildPath) - { - return false; - } - - /** - * Create an SSH key file on disk for this build. - * - * @param string $cloneTo - * - * @return string - */ - protected function writeSshKey($cloneTo) - { - $keyPath = dirname($cloneTo . '/temp'); - $keyFile = $keyPath . '.key'; - - file_put_contents($keyFile, $this->getProject()->getSshPrivateKey()); - chmod($keyFile, 0600); - - return $keyFile; - } - - /** - * Create an SSH wrapper script for Svn to use, to disable host key checking, etc. - * - * @param string $cloneTo - * @param string $keyFile - * - * @return string - */ - protected function writeSshWrapper($cloneTo, $keyFile) - { - $path = dirname($cloneTo . '/temp'); - $wrapperFile = $path . '.sh'; - - $sshFlags = '-o CheckHostIP=no -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -o PasswordAuthentication=no'; - - // Write out the wrapper script for this build: - $script = <<getSource()) { - case Build::SOURCE_WEBHOOK: - return 'source_webhook'; - case Build::SOURCE_MANUAL_WEB: - return 'source_manual_web'; - case Build::SOURCE_MANUAL_CONSOLE: - return 'source_manual_console'; - case Build::SOURCE_PERIODICAL: - return 'source_periodical'; - case Build::SOURCE_UNKNOWN: - default: - return 'source_unknown'; - } - } -} diff --git a/src/PHPCensor/Model/Build/BitbucketBuild.php b/src/PHPCensor/Model/Build/BitbucketBuild.php deleted file mode 100644 index 7710295..0000000 --- a/src/PHPCensor/Model/Build/BitbucketBuild.php +++ /dev/null @@ -1,288 +0,0 @@ - - */ -class BitbucketBuild extends RemoteGitBuild -{ - /** - * Get link to commit from another source (i.e. BitBucket) - */ - public function getCommitLink() - { - return 'https://bitbucket.org/' . $this->getProject()->getReference() . '/commits/' . $this->getCommitId(); - } - - /** - * Get link to branch from another source (i.e. BitBucket) - */ - public function getBranchLink() - { - return 'https://bitbucket.org/' . $this->getProject()->getReference() . '/src/?at=' . $this->getBranch(); - } - - /** - * Get link to tag from another source (i.e. BitBucket) - */ - public function getTagLink() - { - return 'https://bitbucket.org/' . $this->getProject()->getReference() . '/src/?at=' . $this->getTag(); - } - - /** - * Send status updates to any relevant third parties (i.e. Bitbucket) - * - * @return bool - */ - public function sendStatusPostback() - { - if (Build::SOURCE_WEBHOOK !== $this->getSource()) { - return false; - } - - $project = $this->getProject(); - if (empty($project)) { - return false; - } - - $username = Config::getInstance()->get('php-censor.bitbucket.username'); - $appPassword = Config::getInstance()->get('php-censor.bitbucket.app_password'); - - if (empty($username) || empty($appPassword) || empty($this->data['id'])) { - return false; - } - - switch ($this->getStatus()) { - case 0: - case 1: - $status = 'INPROGRESS'; - $description = 'PHP Censor build running.'; - break; - case 2: - $status = 'SUCCESSFUL'; - $description = 'PHP Censor build passed.'; - break; - case 3: - $status = 'FAILED'; - $description = 'PHP Censor build failed.'; - break; - default: - $status = 'STOPPED'; - $description = 'PHP Censor build failed to complete.'; - break; - } - - $phpCensorUrl = Config::getInstance()->get('php-censor.url'); - - $url = sprintf( - '/2.0/repositories/%s/commit/%s/statuses/build', - $this->getExtra('build_type') == 'pull_request' - ? $this->getExtra('remote_reference') - : $project->getReference(), - $this->getCommitId() - ); - - $client = new Client([ - 'base_uri' => 'https://api.bitbucket.org', - 'http_errors' => false, - ]); - $response = $client->post($url, [ - 'auth' => [$username, $appPassword], - 'headers' => [ - 'Content-Type' => 'application/json', - ], - 'json' => [ - 'state' => $status, - 'key' => 'PHP-CENSOR', - 'url' => $phpCensorUrl . '/build/view/' . $this->getId(), - 'name' => 'PHP Censor Build #' . $this->getId(), - 'description' => $description, - ], - ]); - - $status = (integer)$response->getStatusCode(); - - return ($status >= 200 && $status < 300); - } - - /** - * Get the URL to be used to clone this remote repository. - */ - protected function getCloneUrl() - { - $key = trim($this->getProject()->getSshPrivateKey()); - - if (!empty($key)) { - return 'git@bitbucket.org:' . $this->getProject()->getReference() . '.git'; - } else { - return 'https://bitbucket.org/' . $this->getProject()->getReference() . '.git'; - } - } - - /** - * Get a template to use for generating links to files. - * - * @return string - */ - public function getFileLinkTemplate() - { - $reference = $this->getProject()->getReference(); - - if ($this->getExtra('build_type') == 'pull_request') { - $reference = $this->getExtra('remote_reference'); - } - - $link = 'https://bitbucket.org/' . $reference . '/'; - $link .= 'src/' . $this->getCommitId() . '/'; - $link .= '{FILE}'; - $link .= '#{BASEFILE}-{LINE}'; - - return $link; - } - - /** - * Handle any post-clone tasks, like applying a pull request patch on top of the branch. - * @param Builder $builder - * @param $cloneTo - * @param array $extra - * @return bool - */ - protected function postCloneSetup(Builder $builder, $cloneTo, array $extra = null) - { - $buildType = $this->getExtra('build_type'); - - $success = true; - $skipGitFinalization = false; - - try { - if (!empty($buildType) && $buildType == 'pull_request') { - $helper = new Bitbucket(); - $diff = $helper->getPullRequestDiff( - $this->getProject()->getReference(), - $this->getExtra('pull_request_number') - ); - - $diffFile = $this->writeDiff($builder->buildPath, $diff); - - $cmd = 'cd "%s" && git checkout -b php-censor/' . $this->getId() . ' && git apply "%s"'; - - $success = $builder->executeCommand($cmd, $cloneTo, $diffFile); - - unlink($diffFile); - $skipGitFinalization = true; - } - } catch (\Exception $ex) { - $success = false; - } - - if ($success && !$skipGitFinalization) { - $success = parent::postCloneSetup($builder, $cloneTo, $extra); - } - - return $success; - } - - /** - * Create an diff file on disk for this build. - * - * @param string $cloneTo - * - * @return string - */ - protected function writeDiff($cloneTo, $diff) - { - $filePath = dirname($cloneTo . '/temp'); - $diffFile = $filePath . '.patch'; - - file_put_contents($diffFile, $diff); - chmod($diffFile, 0600); - - return $diffFile; - } - - /** - * @inheritDoc - */ - public function reportError( - Builder $builder, - $plugin, - $message, - $severity = BuildError::SEVERITY_NORMAL, - $file = null, - $lineStart = null, - $lineEnd = null - ) { - $allowCommentCommit = (boolean)Config::getInstance()->get('php-censor.bitbucket.comments.commit', false); - $allowCommentPullRequest = (boolean)Config::getInstance()->get('php-censor.bitbucket.comments.pull_request', false); - //$file = $builder->buildPath.'test.php'; - if ($allowCommentCommit || $allowCommentPullRequest) { - $diffLineNumber = $this->getDiffLineNumber($builder, $file, $lineStart); - - if (!is_null($diffLineNumber)) { - $helper = new Bitbucket(); - - $repo = $this->getProject()->getReference(); - $prNumber = $this->getExtra('pull_request_number'); - $commit = $this->getCommitId(); - - if (!empty($prNumber)) { - if ($allowCommentPullRequest) { - $helper->createPullRequestComment($repo, $prNumber, $commit, $file, $lineStart, $message); - } - } else { - if ($allowCommentCommit) { - $helper->createCommitComment($repo, $commit, $file, $lineStart, $message); - } - } - } - } - - parent::reportError($builder, $plugin, $message, $severity, $file, $lineStart, $lineEnd); - } - - /** - * Uses git diff to figure out what the diff line position is, based on the error line number. - * @param Builder $builder - * @param $file - * @param $line - * @return int|null - */ - protected function getDiffLineNumber(Builder $builder, $file, $line) - { - $builder->logExecOutput(false); - - $line = (integer)$line; - $prNumber = $this->getExtra('pull_request_number'); - $path = $builder->buildPath; - - if (!empty($prNumber)) { - $builder->executeCommand('cd %s && git diff origin/%s "%s"', $path, $this->getBranch(), $file); - } else { - $commitId = $this->getCommitId(); - $compare = empty($commitId) ? 'HEAD' : $commitId; - - $builder->executeCommand('cd %s && git diff %s^^ "%s"', $path, $compare, $file); - } - - $builder->logExecOutput(true); - - $diff = $builder->getLastOutput(); - - $helper = new Diff(); - $lines = $helper->getLinePositions($diff); - - return isset($lines[$line]) ? $lines[$line] : null; - } -} diff --git a/src/PHPCensor/Model/Build/BitbucketHgBuild.php b/src/PHPCensor/Model/Build/BitbucketHgBuild.php deleted file mode 100644 index 37e832d..0000000 --- a/src/PHPCensor/Model/Build/BitbucketHgBuild.php +++ /dev/null @@ -1,65 +0,0 @@ - - */ -class BitbucketHgBuild extends MercurialBuild -{ - /** - * Get link to commit from another source (i.e. BitBucket) - */ - public function getCommitLink() - { - return 'https://bitbucket.org/' . $this->getProject()->getReference() . '/commits/' . $this->getCommitId(); - } - - /** - * Get link to branch from another source (i.e. BitBucket) - */ - public function getBranchLink() - { - return 'https://bitbucket.org/' . $this->getProject()->getReference() . '/src/?at=' . $this->getBranch(); - } - - /** - * Get the URL to be used to clone this remote repository. - */ - protected function getCloneUrl() - { - $key = trim($this->getProject()->getSshPrivateKey()); - - if (!empty($key)) { - return 'ssh://hg@bitbucket.org/' . $this->getProject()->getReference(); - } else { - return 'https://bitbucket.org/' . $this->getProject()->getReference(); - } - } - - /** - * Get a template to use for generating links to files. - * - * @return string - */ - public function getFileLinkTemplate() - { - $reference = $this->getProject()->getReference(); - - if ($this->getExtra('build_type') == 'pull_request') { - $matches = []; - preg_match('/[\/:]([a-zA-Z0-9_\-]+\/[a-zA-Z0-9_\-]+)/', $this->getExtra('remote_url'), $matches); - - $reference = $matches[1]; - } - - $link = 'https://bitbucket.org/' . $reference . '/'; - $link .= 'src/' . $this->getCommitId() . '/'; - $link .= '{FILE}'; - $link .= '#{BASEFILE}-{LINE}'; - - return $link; - } -} diff --git a/src/PHPCensor/Model/Build/GithubBuild.php b/src/PHPCensor/Model/Build/GithubBuild.php deleted file mode 100644 index b592be9..0000000 --- a/src/PHPCensor/Model/Build/GithubBuild.php +++ /dev/null @@ -1,274 +0,0 @@ - - */ -class GithubBuild extends RemoteGitBuild -{ - /** - * Get link to commit from another source (i.e. Github) - */ - public function getCommitLink() - { - return 'https://github.com/' . $this->getProject()->getReference() . '/commit/' . $this->getCommitId(); - } - - /** - * Get link to branch from another source (i.e. Github) - */ - public function getBranchLink() - { - return 'https://github.com/' . $this->getProject()->getReference() . '/tree/' . $this->getBranch(); - } - - /** - * Get link to tag from another source (i.e. Github) - */ - public function getTagLink() - { - return 'https://github.com/' . $this->getProject()->getReference() . '/tree/' . $this->getTag(); - } - - /** - * Send status updates to any relevant third parties (i.e. Github) - */ - public function sendStatusPostback() - { - if (Build::SOURCE_WEBHOOK !== $this->getSource()) { - return false; - } - - $project = $this->getProject(); - if (empty($project)) { - return false; - } - - $token = Config::getInstance()->get('php-censor.github.token'); - - if (empty($token) || empty($this->data['id'])) { - return false; - } - - switch ($this->getStatus()) { - case 0: - case 1: - $status = 'pending'; - $description = 'PHP Censor build running.'; - break; - case 2: - $status = 'success'; - $description = 'PHP Censor build passed.'; - break; - case 3: - $status = 'failure'; - $description = 'PHP Censor build failed.'; - break; - default: - $status = 'error'; - $description = 'PHP Censor build failed to complete.'; - break; - } - - $phpCensorUrl = Config::getInstance()->get('php-censor.url'); - - $url = 'https://api.github.com/repos/' . $project->getReference() . '/statuses/' . $this->getCommitId(); - $client = new Client(); - $client->post($url, [ - 'headers' => [ - 'Authorization' => 'token ' . $token, - 'Content-Type' => 'application/x-www-form-urlencoded' - ], - 'json' => [ - 'state' => $status, - 'target_url' => $phpCensorUrl . '/build/view/' . $this->getId(), - 'description' => $description, - 'context' => 'PHP Censor', - ] - ]); - - return true; - } - - /** - * Get the URL to be used to clone this remote repository. - */ - protected function getCloneUrl() - { - $key = trim($this->getProject()->getSshPrivateKey()); - - if (!empty($key)) { - return 'git@github.com:' . $this->getProject()->getReference() . '.git'; - } else { - return 'https://github.com/' . $this->getProject()->getReference() . '.git'; - } - } - - /** - * Get a parsed version of the commit message, with links to issues and commits. - * - * @return string - */ - public function getCommitMessage() - { - $rtn = parent::getCommitMessage(); - - $project = $this->getProject(); - - if (!is_null($project)) { - $reference = $project->getReference(); - $commitLink = '#$1'; - $rtn = preg_replace('/\#([0-9]+)/', $commitLink, $rtn); - $rtn = preg_replace('/\@([a-zA-Z0-9_]+)/', '@$1', $rtn); - } - - return $rtn; - } - - /** - * Get a template to use for generating links to files. - * - * @return string - */ - public function getFileLinkTemplate() - { - $reference = $this->getProject()->getReference(); - - if ($this->getExtra('build_type') == 'pull_request') { - $matches = []; - preg_match('/[\/:]([a-zA-Z0-9_\-]+\/[a-zA-Z0-9_\-]+)/', $this->getExtra('remote_url'), $matches); - - $reference = $matches[1]; - } - - $link = 'https://github.com/' . $reference . '/'; - $link .= 'blob/' . $this->getCommitId() . '/'; - $link .= '{FILE}'; - $link .= '#L{LINE}-L{LINE_END}'; - - return $link; - } - - /** - * Handle any post-clone tasks, like applying a pull request patch on top of the branch. - * @param Builder $builder - * @param $cloneTo - * @param array $extra - * @return bool - */ - protected function postCloneSetup(Builder $builder, $cloneTo, array $extra = null) - { - $buildType = $this->getExtra('build_type'); - - $success = true; - - try { - if (!empty($buildType) && $buildType == 'pull_request') { - $pullRequestId = $this->getExtra('pull_request_number'); - - $cmd = 'cd "%s" && git checkout -b php-censor/' . $this->getId() - . ' %s && git pull -q --no-edit origin pull/%s/head'; - if (!empty($extra['git_ssh_wrapper'])) { - $cmd = 'export GIT_SSH="'.$extra['git_ssh_wrapper'].'" && ' . $cmd; - } - $success = $builder->executeCommand($cmd, $cloneTo, $this->getBranch(), $pullRequestId); - } - } catch (\Exception $ex) { - $success = false; - } - - if ($success) { - $success = parent::postCloneSetup($builder, $cloneTo, $extra); - } - - return $success; - } - - /** - * @inheritDoc - */ - public function reportError( - Builder $builder, - $plugin, - $message, - $severity = BuildError::SEVERITY_NORMAL, - $file = null, - $lineStart = null, - $lineEnd = null - ) { - $allowCommentCommit = (boolean)Config::getInstance()->get('php-censor.github.comments.commit', false); - $allowCommentPullRequest = (boolean)Config::getInstance()->get('php-censor.github.comments.pull_request', false); - - if ($allowCommentCommit || $allowCommentPullRequest) { - $diffLineNumber = $this->getDiffLineNumber($builder, $file, $lineStart); - - if (!is_null($diffLineNumber)) { - $helper = new Github(); - - $repo = $this->getProject()->getReference(); - $prNumber = $this->getExtra('pull_request_number'); - $commit = $this->getCommitId(); - - $allowCommentCommit = (boolean)Config::getInstance()->get('php-censor.github.comments.commit', false); - $allowCommentPullRequest = (boolean)Config::getInstance()->get('php-censor.github.comments.pull_request', false); - - if (!empty($prNumber)) { - if ($allowCommentPullRequest) { - $helper->createPullRequestComment($repo, $prNumber, $commit, $file, $diffLineNumber, $message); - } - } else { - if ($allowCommentCommit) { - $helper->createCommitComment($repo, $commit, $file, $diffLineNumber, $message); - } - } - } - } - - parent::reportError($builder, $plugin, $message, $severity, $file, $lineStart, $lineEnd); - } - - /** - * Uses git diff to figure out what the diff line position is, based on the error line number. - * @param Builder $builder - * @param $file - * @param $line - * @return int|null - */ - protected function getDiffLineNumber(Builder $builder, $file, $line) - { - $builder->logExecOutput(false); - - $line = (integer)$line; - $prNumber = $this->getExtra('pull_request_number'); - $path = $builder->buildPath; - - if (!empty($prNumber)) { - $builder->executeCommand('cd %s && git diff origin/%s "%s"', $path, $this->getBranch(), $file); - } else { - $commitId = $this->getCommitId(); - $compare = empty($commitId) ? 'HEAD' : $commitId; - - $builder->executeCommand('cd %s && git diff %s^^ "%s"', $path, $compare, $file); - } - - $builder->logExecOutput(true); - - $diff = $builder->getLastOutput(); - - $helper = new Diff(); - $lines = $helper->getLinePositions($diff); - - return isset($lines[$line]) ? $lines[$line] : null; - } -} diff --git a/src/PHPCensor/Model/Build/GitlabBuild.php b/src/PHPCensor/Model/Build/GitlabBuild.php deleted file mode 100644 index 35525b3..0000000 --- a/src/PHPCensor/Model/Build/GitlabBuild.php +++ /dev/null @@ -1,67 +0,0 @@ - - */ -class GitlabBuild extends RemoteGitBuild -{ - - /** - * Get link to commit from another source (i.e. Github) - */ - public function getCommitLink() - { - $domain = $this->getProject()->getAccessInformation("domain"); - return 'http://' . $domain . '/' . $this->getProject()->getReference() . '/commit/' . $this->getCommitId(); - } - - /** - * Get link to branch from another source (i.e. Github) - */ - public function getBranchLink() - { - $domain = $this->getProject()->getAccessInformation("domain"); - return 'http://' . $domain . '/' . $this->getProject()->getReference() . '/tree/' . $this->getBranch(); - } - - /** - * Get link to specific file (and line) in a the repo's branch - */ - public function getFileLinkTemplate() - { - return sprintf( - 'http://%s/%s/blob/%s/{FILE}#L{LINE}', - $this->getProject()->getAccessInformation("domain"), - $this->getProject()->getReference(), - $this->getCommitId() - ); - } - - /** - * Get the URL to be used to clone this remote repository. - */ - protected function getCloneUrl() - { - $key = trim($this->getProject()->getSshPrivateKey()); - - if (!empty($key)) { - $user = $this->getProject()->getAccessInformation("user"); - $domain = $this->getProject()->getAccessInformation("domain"); - $port = $this->getProject()->getAccessInformation('port'); - - $url = $user . '@' . $domain . ':'; - - if (!empty($port)) { - $url .= $port . '/'; - } - - $url .= $this->getProject()->getReference() . '.git'; - - return $url; - } - } -} diff --git a/src/PHPCensor/Model/Build/GogsBuild.php b/src/PHPCensor/Model/Build/GogsBuild.php deleted file mode 100644 index cab9ad5..0000000 --- a/src/PHPCensor/Model/Build/GogsBuild.php +++ /dev/null @@ -1,36 +0,0 @@ -getProject()->getReference() . '/commit/' . $this->getCommitId(); - } - - /** - * Get link to branch from Gogs repository - */ - public function getBranchLink() - { - return $this->getProject()->getReference() . '/src/' . $this->getBranch(); - } - /** - * Get link to specific file (and line) in a the repo's branch - */ - public function getFileLinkTemplate() - { - return sprintf( - '%s/src/%s/{FILE}#L{LINE}', - $this->getProject()->getReference(), - $this->getCommitId() - ); - } -} diff --git a/src/PHPCensor/Model/Build/LocalBuild.php b/src/PHPCensor/Model/Build/LocalBuild.php deleted file mode 100644 index 5e703a0..0000000 --- a/src/PHPCensor/Model/Build/LocalBuild.php +++ /dev/null @@ -1,91 +0,0 @@ - - */ -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) { - return $this->handleConfig($builder, $buildPath) !== false; - } - - $configHandled = $this->handleConfig($builder, $reference); - - if ($configHandled === false) { - return false; - } - - $buildSettings = $builder->getConfig('build_settings'); - - if (isset($buildSettings['prefer_symlink']) && $buildSettings['prefer_symlink'] === true) { - return $this->handleSymlink($builder, $reference, $buildPath); - } else { - $cmd = 'cp -Rf "%s" "%s/"'; - $builder->executeCommand($cmd, $reference, $buildPath); - } - - return true; - } - - /** - * Check if this is a "bare" git repository, and if so, unarchive it. - * @param Builder $builder - * @param $reference - * @param $buildPath - * @return bool - */ - 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']) { - $cmd = 'mkdir %2$s; git --git-dir="%1$s" archive %3$s | tar -x -C "%2$s"'; - $builder->executeCommand($cmd, $reference, $buildPath, $this->getBranch()); - return true; - } - - return false; - } - - /** - * Create a symlink if required. - * @param Builder $builder - * @param $reference - * @param $buildPath - * @return bool - */ - 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; - } -} diff --git a/src/PHPCensor/Model/Build/MercurialBuild.php b/src/PHPCensor/Model/Build/MercurialBuild.php deleted file mode 100644 index 1797620..0000000 --- a/src/PHPCensor/Model/Build/MercurialBuild.php +++ /dev/null @@ -1,99 +0,0 @@ - - */ -class MercurialBuild extends Build -{ - /** - * Get the URL to be used to clone this remote repository. - */ - protected function getCloneUrl() - { - return $this->getProject()->getReference(); - } - - /** - * Create a working copy by cloning, copying, or similar. - */ - public function createWorkingCopy(Builder $builder, $buildPath) - { - $key = trim($this->getProject()->getSshPrivateKey()); - - if (!empty($key)) { - $success = $this->cloneBySsh($builder, $buildPath); - } else { - $success = $this->cloneByHttp($builder, $buildPath); - } - - if (!$success) { - $builder->logFailure('Failed to clone remote hg repository.'); - - return false; - } - - return $this->handleConfig($builder, $buildPath); - } - - /** - * Use a HTTP-based hg clone. - */ - protected function cloneByHttp(Builder $builder, $cloneTo) - { - return $builder->executeCommand('hg clone %s "%s" -r %s', $this->getCloneUrl(), $cloneTo, $this->getBranch()); - } - - /** - * Use an SSH-based hg clone. - * - * @param Builder $builder - * @param string $cloneTo - * - * @return bool - */ - protected function cloneBySsh(Builder $builder, $cloneTo) - { - $keyFile = $this->writeSshKey($cloneTo); - - // Do the hg clone: - $cmd = 'hg clone --ssh "ssh -i '.$keyFile.'" %s "%s" -r %s'; - $success = $builder->executeCommand($cmd, $this->getCloneUrl(), $cloneTo, $this->getBranch()); - - if ($success) { - $success = $this->postCloneSetup($builder, $cloneTo); - } - - // Remove the key file: - unlink($keyFile); - - return $success; - } - - /** - * Handle post-clone tasks (switching branch, etc.) - * @param Builder $builder - * @param $cloneTo - * @param array $extra - * @return bool - */ - protected function postCloneSetup(Builder $builder, $cloneTo, array $extra = null) - { - $success = true; - $commitId = $this->getCommitId(); - - // Allow switching to a specific branch: - if (!empty($commitId)) { - $cmd = 'cd "%s" && hg checkout %s'; - $success = $builder->executeCommand($cmd, $cloneTo, $this->getBranch()); - } - - return $success; - } -} diff --git a/src/PHPCensor/Model/Build/RemoteGitBuild.php b/src/PHPCensor/Model/Build/RemoteGitBuild.php deleted file mode 100644 index 9330110..0000000 --- a/src/PHPCensor/Model/Build/RemoteGitBuild.php +++ /dev/null @@ -1,162 +0,0 @@ - - */ -class RemoteGitBuild extends Build -{ - /** - * Get the URL to be used to clone this remote repository. - */ - protected function getCloneUrl() - { - return $this->getProject()->getReference(); - } - - /** - * Create a working copy by cloning, copying, or similar. - */ - public function createWorkingCopy(Builder $builder, $buildPath) - { - $key = trim($this->getProject()->getSshPrivateKey()); - - if (!empty($key)) { - $success = $this->cloneBySsh($builder, $buildPath); - } else { - $success = $this->cloneByHttp($builder, $buildPath); - } - - if ($success) { - $success = $this->mergeBranches($builder, $buildPath); - } - - if (!$success) { - $builder->logFailure('Failed to clone remote git repository.'); - return false; - } - - return $this->handleConfig($builder, $buildPath); - } - - /** - * @param Builder $builder - * @param string $buildPath - * @return bool - */ - protected function mergeBranches(Builder $builder, $buildPath) - { - $branches = $this->getExtra('branches'); - if (!empty($branches)) { - $cmd = 'cd "%s" && git merge --quiet origin/%s'; - foreach ($branches as $branch) { - $success = $builder->executeCommand($cmd, $buildPath, $branch); - if (!$success) { - $builder->log('Fail merge branch origin/'.$branch, LogLevel::ERROR); - return false; - } - $builder->log('Merged branch origin/'.$branch, LogLevel::INFO); - } - } - return true; - } - - /** - * Use an HTTP-based git clone. - */ - protected function cloneByHttp(Builder $builder, $cloneTo) - { - $cmd = 'cd .. && git clone --recursive '; - - $depth = $builder->getConfig('clone_depth'); - - if (!is_null($depth)) { - $cmd .= ' --depth ' . intval($depth) . ' '; - } - - $cmd .= ' -b "%s" "%s" "%s"'; - $success = $builder->executeCommand($cmd, $this->getBranch(), $this->getCloneUrl(), $cloneTo); - - if ($success) { - $success = $this->postCloneSetup($builder, $cloneTo); - } - - return $success; - } - - /** - * Use an SSH-based git clone. - */ - protected function cloneBySsh(Builder $builder, $cloneTo) - { - $keyFile = $this->writeSshKey($cloneTo); - $gitSshWrapper = $this->writeSshWrapper($cloneTo, $keyFile); - - // Do the git clone: - $cmd = 'cd .. && git clone --recursive '; - - $depth = $builder->getConfig('clone_depth'); - - if (!is_null($depth)) { - $cmd .= ' --depth ' . intval($depth) . ' '; - } - - $cmd .= ' -b "%s" "%s" "%s"'; - $cmd = 'export GIT_SSH="'.$gitSshWrapper.'" && ' . $cmd; - - $success = $builder->executeCommand($cmd, $this->getBranch(), $this->getCloneUrl(), $cloneTo); - - if ($success) { - $extra = [ - 'git_ssh_wrapper' => $gitSshWrapper - ]; - - $success = $this->postCloneSetup($builder, $cloneTo, $extra); - } - - // Remove the key file and git wrapper: - unlink($keyFile); - unlink($gitSshWrapper); - - return $success; - } - - /** - * Handle any post-clone tasks, like switching branches. - * @param Builder $builder - * @param $cloneTo - * @param array $extra - * @return bool - */ - protected function postCloneSetup(Builder $builder, $cloneTo, array $extra = null) - { - $success = true; - $commitId = $this->getCommitId(); - $chdir = 'cd "%s"'; - - if (empty($this->getEnvironment()) && !empty($commitId)) { - $cmd = $chdir . ' && git checkout %s --quiet'; - $success = $builder->executeCommand($cmd, $cloneTo, $commitId); - } - - // Always update the commit hash with the actual HEAD hash - if ($builder->executeCommand($chdir . ' && git rev-parse HEAD', $cloneTo)) { - $commitId = trim($builder->getLastOutput()); - - $this->setCommitId($commitId); - - if ($builder->executeCommand($chdir . ' && git log -1 --pretty=format:%%s %s', $cloneTo, $commitId)) { - $this->setCommitMessage(trim($builder->getLastOutput())); - } - } - - return $success; - } -} diff --git a/src/PHPCensor/Model/Build/SubversionBuild.php b/src/PHPCensor/Model/Build/SubversionBuild.php deleted file mode 100644 index 68a2ff7..0000000 --- a/src/PHPCensor/Model/Build/SubversionBuild.php +++ /dev/null @@ -1,127 +0,0 @@ - - */ -class SubversionBuild extends Build -{ - protected $svnCommand = 'svn export -q --non-interactive '; - - /** - * Get the URL to be used to clone this remote repository. - */ - protected function getCloneUrl() - { - $url = rtrim($this->getProject()->getReference(), '/') . '/'; - $branch = ltrim($this->getBranch(), '/'); - - // For empty default branch or default branch name like "/trunk" or "trunk" (-> "trunk") - if (empty($branch) || $branch == 'trunk') { - $url .= 'trunk'; - // For default branch with standard default branch directory ("branches") like "/branch-1" or "branch-1" - // (-> "branches/branch-1") - } elseif (false === strpos($branch, '/')) { - $url .= 'branches/' . $branch; - // For default branch with non-standard branch directory like "/branch/branch-1" or "branch/branch-1" - // (-> "branch/branch-1") - } else { - $url .= $branch; - } - - return $url; - } - - /** - * @param Builder $builder - * - * @return void - */ - protected function extendSvnCommandFromConfig(Builder $builder) - { - $cmd = $this->svnCommand; - - $svn = $builder->getConfig('svn'); - if ($svn) { - foreach ($svn as $key => $value) { - $cmd .= " --$key $value "; - } - } - - $depth = $builder->getConfig('clone_depth'); - - if (!is_null($depth)) { - $cmd .= ' --depth ' . intval($depth) . ' '; - } - - $this->svnCommand = $cmd; - } - - /** - * Create a working copy by cloning, copying, or similar. - */ - public function createWorkingCopy(Builder $builder, $buildPath) - { - $this->handleConfig($builder, $buildPath); - - $this->extendSvnCommandFromConfig($builder); - - $key = trim($this->getProject()->getSshPrivateKey()); - - if (!empty($key)) { - $success = $this->cloneBySsh($builder, $buildPath); - } else { - $success = $this->cloneByHttp($builder, $buildPath); - } - - if (!$success) { - $builder->logFailure('Failed to export remote subversion repository.'); - return false; - } - - return $this->handleConfig($builder, $buildPath); - } - - /** - * Use an HTTP-based svn export. - */ - protected function cloneByHttp(Builder $builder, $cloneTo) - { - $cmd = $this->svnCommand; - - if (!empty($this->getCommitId())) { - $cmd .= ' -r %s %s "%s"'; - $success = $builder->executeCommand($cmd, $this->getCommitId(), $this->getCloneUrl(), $cloneTo); - } else { - $cmd .= ' %s "%s"'; - $success = $builder->executeCommand($cmd, $this->getCloneUrl(), $cloneTo); - } - - return $success; - } - - /** - * Use an SSH-based svn export. - */ - protected function cloneBySsh(Builder $builder, $cloneTo) - { - $cmd = $this->svnCommand . ' %s "%s"'; - $keyFile = $this->writeSshKey($cloneTo); - $sshWrapper = $this->writeSshWrapper($cloneTo, $keyFile); - $cmd = 'export SVN_SSH="' . $sshWrapper . '" && ' . $cmd; - - $success = $builder->executeCommand($cmd, $this->getCloneUrl(), $cloneTo); - - // Remove the key file and svn wrapper: - unlink($keyFile); - unlink($sshWrapper); - - return $success; - } -} diff --git a/src/PHPCensor/Model/BuildError.php b/src/PHPCensor/Model/BuildError.php deleted file mode 100644 index 3e8796d..0000000 --- a/src/PHPCensor/Model/BuildError.php +++ /dev/null @@ -1,449 +0,0 @@ - null, - 'build_id' => null, - 'plugin' => null, - 'file' => null, - 'line_start' => null, - 'line_end' => null, - 'severity' => null, - 'message' => null, - 'create_date' => null, - ]; - - /** - * @var array - */ - protected $getters = [ - // Direct property getters: - 'id' => 'getId', - 'build_id' => 'getBuildId', - 'plugin' => 'getPlugin', - 'file' => 'getFile', - 'line_start' => 'getLineStart', - 'line_end' => 'getLineEnd', - 'severity' => 'getSeverity', - 'message' => 'getMessage', - 'create_date' => 'getCreateDate', - - // Foreign key getters: - 'Build' => 'getBuild', - ]; - - /** - * @var array - */ - protected $setters = [ - // Direct property setters: - 'id' => 'setId', - 'build_id' => 'setBuildId', - 'plugin' => 'setPlugin', - 'file' => 'setFile', - 'line_start' => 'setLineStart', - 'line_end' => 'setLineEnd', - 'severity' => 'setSeverity', - 'message' => 'setMessage', - 'create_date' => 'setCreateDate', - - // Foreign key setters: - 'Build' => 'setBuild', - ]; - - /** - * @return int - */ - public function getId() - { - $rtn = $this->data['id']; - - return $rtn; - } - - /** - * @return int - */ - public function getBuildId() - { - $rtn = $this->data['build_id']; - - return $rtn; - } - - /** - * @return string - */ - public function getPlugin() - { - $rtn = $this->data['plugin']; - - return $rtn; - } - - /** - * @return string - */ - public function getFile() - { - $rtn = $this->data['file']; - - return $rtn; - } - - /** - * @return int - */ - public function getLineStart() - { - $rtn = $this->data['line_start']; - - return $rtn; - } - - /** - * @return int - */ - public function getLineEnd() - { - $rtn = $this->data['line_end']; - - return $rtn; - } - - /** - * @return int - */ - public function getSeverity() - { - $rtn = $this->data['severity']; - - return $rtn; - } - - /** - * @return string - */ - public function getMessage() - { - $rtn = $this->data['message']; - - return $rtn; - } - - /** - * @return \DateTime - */ - public function getCreateDate() - { - $rtn = $this->data['create_date']; - - if (!empty($rtn)) { - $rtn = new \DateTime($rtn); - } - - return $rtn; - } - - /** - * @param $value int - */ - public function setId($value) - { - $this->validateNotNull('id', $value); - $this->validateInt('id', $value); - - if ($this->data['id'] === $value) { - return; - } - - $this->data['id'] = $value; - - $this->setModified('id'); - } - - /** - * @param $value int - */ - public function setBuildId($value) - { - $this->validateNotNull('build_id', $value); - $this->validateInt('build_id', $value); - - if ($this->data['build_id'] === $value) { - return; - } - - $this->data['build_id'] = $value; - - $this->setModified('build_id'); - } - - /** - * @param $value string - */ - public function setPlugin($value) - { - $this->validateNotNull('plugin', $value); - $this->validateString('plugin', $value); - - if ($this->data['plugin'] === $value) { - return; - } - - $this->data['plugin'] = $value; - - $this->setModified('plugin'); - } - - /** - * @param $value string - */ - public function setFile($value) - { - $this->validateString('file', $value); - - if ($this->data['file'] === $value) { - return; - } - - $this->data['file'] = $value; - - $this->setModified('file'); - } - - /** - * @param $value int - */ - public function setLineStart($value) - { - $this->validateInt('line_start', $value); - - if ($this->data['line_start'] === $value) { - return; - } - - $this->data['line_start'] = $value; - - $this->setModified('line_start'); - } - - /** - * @param $value int - */ - public function setLineEnd($value) - { - $this->validateInt('line_end', $value); - - if ($this->data['line_end'] === $value) { - return; - } - - $this->data['line_end'] = $value; - - $this->setModified('line_end'); - } - - /** - * @param $value int - */ - public function setSeverity($value) - { - $this->validateNotNull('severity', $value); - $this->validateInt('severity', $value); - - if ($this->data['severity'] === $value) { - return; - } - - $this->data['severity'] = $value; - - $this->setModified('severity'); - } - - /** - * @param $value string - */ - public function setMessage($value) - { - $this->validateNotNull('message', $value); - $this->validateString('message', $value); - - if ($this->data['message'] === $value) { - return; - } - - $this->data['message'] = $value; - - $this->setModified('message'); - } - - /** - * @param $value \DateTime - */ - public function setCreateDate($value) - { - $this->validateNotNull('create_date', $value); - $this->validateDate('create_date', $value); - - if ($this->data['create_date'] === $value) { - return; - } - - $this->data['create_date'] = $value; - - $this->setModified('create_date'); - } - - /** - * Get the Build model for this BuildError by Id. - * - * @return \PHPCensor\Model\Build - */ - public function getBuild() - { - $key = $this->getBuildId(); - - if (empty($key)) { - return null; - } - - $cacheKey = 'Cache.Build.' . $key; - $rtn = $this->cache->get($cacheKey, null); - - if (empty($rtn)) { - $rtn = Factory::getStore('Build', 'PHPCensor')->getById($key); - $this->cache->set($cacheKey, $rtn); - } - - return $rtn; - } - - /** - * Set Build - Accepts an ID, an array representing a Build or a Build model. - * - * @param $value mixed - */ - public function setBuild($value) - { - // Is this an instance of Build? - if ($value instanceof Build) { - return $this->setBuildObject($value); - } - - // Is this an array representing a Build item? - if (is_array($value) && !empty($value['id'])) { - return $this->setBuildId($value['id']); - } - - // Is this a scalar value representing the ID of this foreign key? - return $this->setBuildId($value); - } - - /** - * Set Build - Accepts a Build model. - * - * @param $value Build - */ - public function setBuildObject(Build $value) - { - return $this->setBuildId($value->getId()); - } - - /** - * Get the language string key for this error's severity level. - * - * @return string - */ - public function getSeverityString() - { - switch ($this->getSeverity()) { - case self::SEVERITY_CRITICAL: - return 'critical'; - - case self::SEVERITY_HIGH: - return 'high'; - - case self::SEVERITY_NORMAL: - return 'normal'; - - case self::SEVERITY_LOW: - return 'low'; - } - } - - /** - * Get the language string key for this error's severity level. - * - * @param integer $severity - * - * @return string - */ - public static function getSeverityName($severity) - { - switch ($severity) { - case self::SEVERITY_CRITICAL: - return 'critical'; - - case self::SEVERITY_HIGH: - return 'high'; - - case self::SEVERITY_NORMAL: - return 'normal'; - - case self::SEVERITY_LOW: - return 'low'; - } - } - - /** - * Get the class to apply to HTML elements representing this error. - * - * @return string - */ - public function getSeverityClass() - { - switch ($this->getSeverity()) { - case self::SEVERITY_CRITICAL: - return 'danger'; - - case self::SEVERITY_HIGH: - return 'warning'; - - case self::SEVERITY_NORMAL: - return 'info'; - - case self::SEVERITY_LOW: - return 'default'; - } - } -} diff --git a/src/PHPCensor/Model/BuildMeta.php b/src/PHPCensor/Model/BuildMeta.php deleted file mode 100644 index 0efa761..0000000 --- a/src/PHPCensor/Model/BuildMeta.php +++ /dev/null @@ -1,225 +0,0 @@ - null, - 'build_id' => null, - 'meta_key' => null, - 'meta_value' => null, - ]; - - /** - * @var array - */ - protected $getters = [ - // Direct property getters: - 'id' => 'getId', - 'build_id' => 'getBuildId', - 'meta_key' => 'getMetaKey', - 'meta_value' => 'getMetaValue', - - // Foreign key getters: - 'Build' => 'getBuild', - ]; - - /** - * @var array - */ - protected $setters = [ - // Direct property setters: - 'id' => 'setId', - 'build_id' => 'setBuildId', - 'meta_key' => 'setMetaKey', - 'meta_value' => 'setMetaValue', - - // Foreign key setters: - 'Build' => 'setBuild', - ]; - - /** - * @return int - */ - public function getId() - { - $rtn = $this->data['id']; - - return $rtn; - } - - /** - * @return int - */ - public function getBuildId() - { - $rtn = $this->data['build_id']; - - return $rtn; - } - - /** - * @return string - */ - public function getMetaKey() - { - $rtn = $this->data['meta_key']; - - return $rtn; - } - - /** - * @return string - */ - public function getMetaValue() - { - $rtn = $this->data['meta_value']; - - return $rtn; - } - - /** - * @param int $value - */ - public function setId($value) - { - $this->validateNotNull('id', $value); - $this->validateInt('id', $value); - - if ($this->data['id'] === $value) { - return; - } - - $this->data['id'] = $value; - - $this->setModified('id'); - } - - /** - * @param int $value - */ - public function setBuildId($value) - { - $this->validateNotNull('build_id', $value); - $this->validateInt('build_id', $value); - - if ($this->data['build_id'] === $value) { - return; - } - - $this->data['build_id'] = $value; - - $this->setModified('build_id'); - } - - /** - * @param $value string - */ - public function setMetaKey($value) - { - $this->validateNotNull('meta_key', $value); - $this->validateString('meta_key', $value); - - if ($this->data['meta_key'] === $value) { - return; - } - - $this->data['meta_key'] = $value; - - $this->setModified('meta_key'); - } - - /** - * @param $value string - */ - public function setMetaValue($value) - { - $this->validateNotNull('meta_value', $value); - $this->validateString('meta_value', $value); - - if ($this->data['meta_value'] === $value) { - return; - } - - $this->data['meta_value'] = $value; - - $this->setModified('meta_value'); - } - - /** - * Get the Build model for this BuildMeta by Id. - * - * @return \PHPCensor\Model\Build - */ - public function getBuild() - { - $key = $this->getBuildId(); - - if (empty($key)) { - return null; - } - - $cacheKey = 'Cache.Build.' . $key; - $rtn = $this->cache->get($cacheKey, null); - - if (empty($rtn)) { - $rtn = Factory::getStore('Build', 'PHPCensor')->getById($key); - $this->cache->set($cacheKey, $rtn); - } - - return $rtn; - } - - /** - * Set Build - Accepts an ID, an array representing a Build or a Build model. - * - * @param $value mixed - */ - public function setBuild($value) - { - // Is this an instance of Build? - if ($value instanceof Build) { - return $this->setBuildObject($value); - } - - // Is this an array representing a Build item? - if (is_array($value) && !empty($value['id'])) { - return $this->setBuildId($value['id']); - } - - // Is this a scalar value representing the ID of this foreign key? - return $this->setBuildId($value); - } - - /** - * Set Build - Accepts a Build model. - * - * @param $value Build - */ - public function setBuildObject(Build $value) - { - return $this->setBuildId($value->getId()); - } -} diff --git a/src/PHPCensor/Model/Environment.php b/src/PHPCensor/Model/Environment.php deleted file mode 100644 index e70da4e..0000000 --- a/src/PHPCensor/Model/Environment.php +++ /dev/null @@ -1,161 +0,0 @@ - null, - 'project_id' => null, - 'name' => null, - 'branches' => null, - ]; - - /** - * @var array - */ - protected $getters = [ - 'id' => 'getId', - 'project_id' => 'getProjectId', - 'name' => 'getName', - 'branches' => 'getBranches', - ]; - - /** - * @var array - */ - protected $setters = [ - 'id' => 'setId', - 'project_id' => 'setProjectId', - 'name' => 'setName', - 'branches' => 'setBranches', - ]; - - /** - * @return int - */ - public function getId() - { - $rtn = $this->data['id']; - - return $rtn; - } - - /** - * @return int - */ - public function getProjectId() - { - $rtn = $this->data['project_id']; - - return $rtn; - } - - /** - * @return string - */ - public function getName() - { - $rtn = $this->data['name']; - - return $rtn; - } - - /** - * @return array - */ - public function getBranches() - { - $rtn = array_filter(array_map('trim', explode("\n", $this->data['branches']))); - - return $rtn; - } - - /** - * @param $value int - */ - public function setId($value) - { - $this->validateNotNull('id', $value); - $this->validateInt('id', $value); - - if ($this->data['id'] === $value) { - return; - } - - $this->data['id'] = $value; - - $this->setModified('id'); - } - - /** - * @param $value int - */ - public function setProjectId($value) - { - $this->validateNotNull('project_id', $value); - $this->validateInt('project_id', $value); - - if ($this->data['project_id'] === $value) { - return; - } - - $this->data['project_id'] = $value; - - $this->setModified('project_id'); - } - - /** - * @param $value string - */ - public function setName($value) - { - $this->validateNotNull('name', $value); - $this->validateString('name', $value); - - if ($this->data['name'] === $value) { - return; - } - - $this->data['name'] = $value; - - $this->setModified('name'); - } - - /** - * @param $value array - */ - public function setBranches($value) - { - $this->validateNotNull('branches', $value); - $value = implode("\n", $value); - - if ($this->data['branches'] === $value) { - return; - } - - $this->data['branches'] = $value; - - $this->setModified('branches'); - } -} diff --git a/src/PHPCensor/Model/Project.php b/src/PHPCensor/Model/Project.php deleted file mode 100644 index bf461b7..0000000 --- a/src/PHPCensor/Model/Project.php +++ /dev/null @@ -1,869 +0,0 @@ - - */ -class Project extends Model -{ - /** - * @var array - */ - public static $sleepable = []; - - /** - * @var string - */ - protected $tableName = 'project'; - - /** - * @var string - */ - protected $modelName = 'Project'; - - /** - * @var array - */ - protected $data = [ - 'id' => null, - 'title' => null, - 'reference' => null, - 'branch' => null, - 'default_branch_only' => null, - 'ssh_private_key' => null, - 'type' => null, - 'access_information' => null, - 'last_commit' => null, - 'build_config' => null, - 'ssh_public_key' => null, - 'allow_public_status' => null, - 'archived' => null, - 'group_id' => null, - 'create_date' => null, - 'user_id' => 0, - ]; - - /** - * @var array - */ - protected $getters = [ - // Direct property getters: - 'id' => 'getId', - 'title' => 'getTitle', - 'reference' => 'getReference', - 'branch' => 'getBranch', - 'default_branch_only' => 'getDefaultBranchOnly', - 'ssh_private_key' => 'getSshPrivateKey', - 'type' => 'getType', - 'access_information' => 'getAccessInformation', - 'last_commit' => 'getLastCommit', - 'build_config' => 'getBuildConfig', - 'ssh_public_key' => 'getSshPublicKey', - 'allow_public_status' => 'getAllowPublicStatus', - 'archived' => 'getArchived', - 'group_id' => 'getGroupId', - 'create_date' => 'getCreateDate', - 'user_id' => 'getUserId', - - // Foreign key getters: - 'Group' => 'getGroup', - ]; - - /** - * @var array - */ - protected $setters = [ - // Direct property setters: - 'id' => 'setId', - 'title' => 'setTitle', - 'reference' => 'setReference', - 'branch' => 'setBranch', - 'default_branch_only' => 'setDefaultBranchOnly', - 'ssh_private_key' => 'setSshPrivateKey', - 'type' => 'setType', - 'access_information' => 'setAccessInformation', - 'last_commit' => 'setLastCommit', - 'build_config' => 'setBuildConfig', - 'ssh_public_key' => 'setSshPublicKey', - 'allow_public_status' => 'setAllowPublicStatus', - 'archived' => 'setArchived', - 'group_id' => 'setGroupId', - 'create_date' => 'setCreateDate', - 'user_id' => 'setUserId', - - // Foreign key setters: - 'Group' => 'setGroup', - ]; - - /** - * @return int - */ - public function getId() - { - $rtn = $this->data['id']; - - return $rtn; - } - - /** - * @return string - */ - public function getTitle() - { - $rtn = $this->data['title']; - - return $rtn; - } - - /** - * @return string - */ - public function getReference() - { - $rtn = $this->data['reference']; - - return $rtn; - } - - /** - * @return string - */ - public function getSshPrivateKey() - { - $rtn = $this->data['ssh_private_key']; - - return $rtn; - } - - /** - * @return string - */ - public function getType() - { - $rtn = $this->data['type']; - - return $rtn; - } - - /** - * @return string - */ - public function getLastCommit() - { - $rtn = $this->data['last_commit']; - - return $rtn; - } - - /** - * @return string - */ - public function getBuildConfig() - { - $rtn = $this->data['build_config']; - - return $rtn; - } - - /** - * @return string - */ - public function getSshPublicKey() - { - $rtn = $this->data['ssh_public_key']; - - return $rtn; - } - - /** - * @return int - */ - public function getAllowPublicStatus() - { - $rtn = $this->data['allow_public_status']; - - return $rtn; - } - - /** - * @return int - */ - public function getArchived() - { - $rtn = $this->data['archived']; - - return $rtn; - } - - /** - * @return int - */ - public function getGroupId() - { - $rtn = $this->data['group_id']; - - return $rtn; - } - - /** - * @return int - */ - public function getDefaultBranchOnly() - { - $rtn = $this->data['default_branch_only']; - - return $rtn; - } - - /** - * @param $value int - */ - public function setId($value) - { - $this->validateNotNull('id', $value); - $this->validateInt('id', $value); - - if ($this->data['id'] === $value) { - return; - } - - $this->data['id'] = $value; - - $this->setModified('id'); - } - - /** - * @param $value string - */ - public function setTitle($value) - { - $this->validateNotNull('title', $value); - $this->validateString('title', $value); - - if ($this->data['title'] === $value) { - return; - } - - $this->data['title'] = $value; - - $this->setModified('title'); - } - - /** - * @param $value string - */ - public function setReference($value) - { - $this->validateNotNull('reference', $value); - $this->validateString('reference', $value); - - if ($this->data['reference'] === $value) { - return; - } - - $this->data['reference'] = $value; - - $this->setModified('reference'); - } - - /** - * @param $value string - */ - public function setBranch($value) - { - $this->validateNotNull('branch', $value); - $this->validateString('branch', $value); - - if ($this->data['branch'] === $value) { - return; - } - - $this->data['branch'] = $value; - - $this->setModified('branch'); - } - - /** - * @param $value int - */ - public function setDefaultBranchOnly($value) - { - $this->validateNotNull('default_branch_only', $value); - $this->validateInt('default_branch_only', $value); - - if ($this->data['default_branch_only'] === $value) { - return; - } - - $this->data['default_branch_only'] = $value; - - $this->setModified('default_branch_only'); - } - - /** - * @param $value string - */ - public function setSshPrivateKey($value) - { - $this->validateString('ssh_private_key', $value); - - if ($this->data['ssh_private_key'] === $value) { - return; - } - - $this->data['ssh_private_key'] = $value; - - $this->setModified('ssh_private_key'); - } - - /** - * @param $value string - */ - public function setType($value) - { - $this->validateNotNull('type', $value); - $this->validateString('type', $value); - - if ($this->data['type'] === $value) { - return; - } - - $this->data['type'] = $value; - - $this->setModified('type'); - } - - /** - * @param $value string - */ - public function setLastCommit($value) - { - $this->validateString('last_commit', $value); - - if ($this->data['last_commit'] === $value) { - return; - } - - $this->data['last_commit'] = $value; - - $this->setModified('last_commit'); - } - - /** - * @param $value string - */ - public function setBuildConfig($value) - { - $this->validateString('build_config', $value); - - if ($this->data['build_config'] === $value) { - return; - } - - $this->data['build_config'] = $value; - - $this->setModified('build_config'); - } - - /** - * @param $value string - */ - public function setSshPublicKey($value) - { - $this->validateString('ssh_public_key', $value); - - if ($this->data['ssh_public_key'] === $value) { - return; - } - - $this->data['ssh_public_key'] = $value; - - $this->setModified('ssh_public_key'); - } - - /** - * @param $value int - */ - public function setAllowPublicStatus($value) - { - $this->validateNotNull('allow_public_status', $value); - $this->validateInt('allow_public_status', $value); - - if ($this->data['allow_public_status'] === $value) { - return; - } - - $this->data['allow_public_status'] = $value; - - $this->setModified('allow_public_status'); - } - - /** - * @param $value int - */ - public function setArchived($value) - { - $this->validateNotNull('archived', $value); - $this->validateInt('archived', $value); - - if ($this->data['archived'] === $value) { - return; - } - - $this->data['archived'] = $value; - - $this->setModified('archived'); - } - - /** - * @param $value int - */ - public function setGroupId($value) - { - $this->validateNotNull('group_id', $value); - $this->validateInt('group_id', $value); - - if ($this->data['group_id'] === $value) { - return; - } - - $this->data['group_id'] = $value; - - $this->setModified('group_id'); - } - - /** - * Get the ProjectGroup model for this Project by Id. - * - * @return \PHPCensor\Model\ProjectGroup - */ - public function getGroup() - { - $key = $this->getGroupId(); - - if (empty($key)) { - return null; - } - - $cacheKey = 'Cache.ProjectGroup.' . $key; - $rtn = $this->cache->get($cacheKey, null); - - if (empty($rtn)) { - $rtn = Factory::getStore('ProjectGroup', 'PHPCensor')->getById($key); - $this->cache->set($cacheKey, $rtn); - } - - return $rtn; - } - - /** - * Set Group - Accepts an ID, an array representing a ProjectGroup or a ProjectGroup model. - * - * @param $value mixed - */ - public function setGroup($value) - { - // Is this an instance of ProjectGroup? - if ($value instanceof ProjectGroup) { - return $this->setGroupObject($value); - } - - // Is this an array representing a ProjectGroup item? - if (is_array($value) && !empty($value['id'])) { - return $this->setGroupId($value['id']); - } - - // Is this a scalar value representing the ID of this foreign key? - return $this->setGroupId($value); - } - - /** - * Set Group - Accepts a ProjectGroup model. - * - * @param $value ProjectGroup - */ - public function setGroupObject(ProjectGroup $value) - { - return $this->setGroupId($value->getId()); - } - - /** - * Get Build models by ProjectId for this Project. - * - * @return \PHPCensor\Model\Build[] - */ - public function getProjectBuilds() - { - return Factory::getStore('Build', 'PHPCensor')->getByProjectId($this->getId()); - } - - /** - * Return the latest build from a specific branch, of a specific status, for this project. - * - * @param string $branch - * @param null $status - * - * @return mixed|null - */ - public function getLatestBuild($branch = 'master', $status = null) - { - $criteria = ['branch' => $branch, 'project_id' => $this->getId()]; - - if (isset($status)) { - $criteria['status'] = $status; - } - - $order = ['id' => 'DESC']; - $builds = Store\Factory::getStore('Build')->getWhere($criteria, 1, 0, [], $order); - - if (is_array($builds['items']) && count($builds['items'])) { - $latest = array_shift($builds['items']); - - if (isset($latest) && $latest instanceof Build) { - return $latest; - } - } - - return null; - } - - /** - * Return the previous build from a specific branch, for this project. - * - * @param string $branch - * - * @return mixed|null - */ - public function getPreviousBuild($branch = 'master') - { - $criteria = ['branch' => $branch, 'project_id' => $this->getId()]; - $order = ['id' => 'DESC']; - $builds = Store\Factory::getStore('Build')->getWhere($criteria, 1, 1, [], $order); - - if (is_array($builds['items']) && count($builds['items'])) { - $previous = array_shift($builds['items']); - - if (isset($previous) && $previous instanceof Build) { - return $previous; - } - } - - return null; - } - - /** - * @param string|array $value - */ - public function setAccessInformation($value) - { - if (is_array($value)) { - $value = json_encode($value); - } - - $this->validateString('access_information', $value); - - if ($this->data['access_information'] === $value) { - return; - } - - $this->data['access_information'] = $value; - - $this->setModified('access_information'); - } - - /** - * Get this project's access_information data. Pass a specific key or null for all data. - * - * @param string|null $key - * - * @return mixed|null|string - */ - public function getAccessInformation($key = null) - { - $info = $this->data['access_information']; - - // Handle old-format (serialized) access information first: - if (!empty($info) && !in_array(substr($info, 0, 1), ['{', '['])) { - $data = unserialize($info); - } else { - $data = json_decode($info, true); - } - - if (is_null($key)) { - $rtn = $data; - } elseif (isset($data[$key])) { - $rtn = $data[$key]; - } else { - $rtn = null; - } - - return $rtn; - } - - /** - * @return \DateTime - */ - public function getCreateDate() - { - $rtn = $this->data['create_date']; - - if (!empty($rtn)) { - $rtn = new \DateTime($rtn); - } - - return $rtn; - } - - /** - * @param $value \DateTime - */ - public function setCreateDate($value) - { - $this->validateDate('create_date', $value); - - if ($this->data['create_date'] === $value) { - return; - } - - $this->data['create_date'] = $value; - - $this->setModified('create_date'); - } - - /** - * @return string - */ - public function getUserId() - { - $rtn = $this->data['user_id']; - - return (integer)$rtn; - } - - /** - * @param $value integer - */ - public function setUserId($value) - { - $this->validateNotNull('user_id', $value); - $this->validateInt('user_id', $value); - - if ($this->data['user_id'] === $value) { - return; - } - - $this->data['user_id'] = $value; - - $this->setModified('user_id'); - } - - /** - * Get the value of branch. - * - * @return string - */ - public function getBranch() - { - if (empty($this->data['branch'])) { - $projectType = $this->getType(); - switch ($projectType) { - case 'hg': - $branch = 'default'; - break; - case 'svn': - $branch = 'trunk'; - break; - default: - $branch = 'master'; - } - - return $branch; - } else { - return $this->data['branch']; - } - } - - /** - * Return the name of a FontAwesome icon to represent this project, depending on its type. - * - * @return string - */ - public function getIcon() - { - switch ($this->getType()) { - case 'github': - $icon = 'github'; - break; - - case 'bitbucket': - case 'bitbuckethg': - $icon = 'bitbucket'; - break; - - case 'remote': - case 'gitlab': - default: - $icon = 'code-fork'; - break; - } - - return $icon; - } - - /** - * @return EnvironmentStore - */ - protected function getEnvironmentStore() - { - /** @var EnvironmentStore $store */ - $store = Factory::getStore('Environment', 'PHPCensor'); - return $store; - } - - /** - * Get Environments - * - * @return array contain items with \PHPCensor\Model\Environment - */ - public function getEnvironmentsObjects() - { - $key = $this->getId(); - - if (empty($key)) { - return null; - } - - $cacheKey = 'Cache.ProjectEnvironments.' . $key; - $rtn = $this->cache->get($cacheKey, null); - - if (empty($rtn)) { - $store = $this->getEnvironmentStore(); - $rtn = $store->getByProjectId($key); - $this->cache->set($cacheKey, $rtn); - } - - return $rtn; - } - - /** - * Get Environments - * - * @return string[] - */ - public function getEnvironmentsNames() - { - $environments = $this->getEnvironmentsObjects(); - $environments_names = []; - foreach($environments['items'] as $environment) { - /** @var Environment $environment */ - $environments_names[] = $environment->getName(); - } - - return $environments_names; - } - - /** - * Get Environments - * - * @return string yaml - */ - public function getEnvironments() - { - $environments = $this->getEnvironmentsObjects(); - $environments_config = []; - foreach($environments['items'] as $environment) { - /** @var Environment $environment */ - $environments_config[$environment->getName()] = $environment->getBranches(); - } - - $yaml_dumper = new YamlDumper(); - $value = $yaml_dumper->dump($environments_config, 10, 0, true, false); - - return $value; - } - - /** - * Set Environments - * - * @param string $value yaml - */ - public function setEnvironments($value) - { - $yaml_parser = new YamlParser(); - $environments_config = $yaml_parser->parse($value); - $environments_names = !empty($environments_config) ? array_keys($environments_config) : []; - $current_environments = $this->getEnvironmentsObjects(); - $store = $this->getEnvironmentStore(); - foreach ($current_environments['items'] as $environment) { - /** @var Environment $environment */ - $key = array_search($environment->getName(), $environments_names); - if ($key !== false) { - // already exist - unset($environments_names[$key]); - $environment->setBranches(!empty($environments_config[$environment->getName()]) ? $environments_config[$environment->getName()] : []); - $store->save($environment); - } else { - // remove - $store->delete($environment); - } - } - - if (!empty($environments_names)) { - // add - foreach ($environments_names as $environment_name) { - $environment = new Environment(); - $environment->setProjectId($this->getId()); - $environment->setName($environment_name); - $environment->setBranches(!empty($environments_config[$environment->getName()]) ? $environments_config[$environment->getName()] : []); - $store->save($environment); - } - } - } - - /** - * @param string $branch - * - * @return string[] - */ - public function getEnvironmentsNamesByBranch($branch) - { - $environments_names = []; - $environments = $this->getEnvironmentsObjects(); - $default_branch = ($branch == $this->getBranch()); - foreach($environments['items'] as $environment) { - /** @var Environment $environment */ - if ($default_branch || in_array($branch, $environment->getBranches())) { - $environments_names[] = $environment->getName(); - } - } - - return $environments_names; - } - - /** - * @param string $environment_name - * - * @return string[] - */ - public function getBranchesByEnvironment($environment_name) - { - $branches = []; - $environments = $this->getEnvironmentsObjects(); - foreach($environments['items'] as $environment) { - /** @var Environment $environment */ - if ($environment_name == $environment->getName()) { - return $environment->getBranches(); - } - } - - return $branches; - } -} diff --git a/src/PHPCensor/Model/ProjectGroup.php b/src/PHPCensor/Model/ProjectGroup.php deleted file mode 100644 index 6882438..0000000 --- a/src/PHPCensor/Model/ProjectGroup.php +++ /dev/null @@ -1,175 +0,0 @@ - null, - 'title' => null, - 'create_date' => null, - 'user_id' => 0, - ]; - - /** - * @var array - */ - protected $getters = [ - 'id' => 'getId', - 'title' => 'getTitle', - 'create_date' => 'getCreateDate', - 'user_id' => 'getUserId', - ]; - - /** - * @var array - */ - protected $setters = [ - 'id' => 'setId', - 'title' => 'setTitle', - 'create_date' => 'setCreateDate', - 'user_id' => 'setUserId', - ]; - - /** - * @return int - */ - public function getId() - { - $rtn = $this->data['id']; - - return $rtn; - } - - /** - * @param $value int - */ - public function setId($value) - { - $this->validateNotNull('id', $value); - $this->validateInt('id', $value); - - if ($this->data['id'] === $value) { - return; - } - - $this->data['id'] = $value; - - $this->setModified('id'); - } - - /** - * @return string - */ - public function getTitle() - { - $rtn = $this->data['title']; - - return $rtn; - } - - /** - * @param $value string - */ - public function setTitle($value) - { - $this->validateNotNull('title', $value); - $this->validateString('title', $value); - - if ($this->data['title'] === $value) { - return; - } - - $this->data['title'] = $value; - - $this->setModified('title'); - } - - /** - * @return \DateTime - */ - public function getCreateDate() - { - $rtn = $this->data['create_date']; - - if (!empty($rtn)) { - $rtn = new \DateTime($rtn); - } - - return $rtn; - } - - /** - * @param $value \DateTime - */ - public function setCreateDate($value) - { - $this->validateDate('create_date', $value); - - if ($this->data['create_date'] === $value) { - return; - } - - $this->data['create_date'] = $value; - - $this->setModified('create_date'); - } - - /** - * @return string - */ - public function getUserId() - { - $rtn = $this->data['user_id']; - - return (integer)$rtn; - } - - /** - * @param $value integer - */ - public function setUserId($value) - { - $this->validateNotNull('user_id', $value); - $this->validateInt('user_id', $value); - - if ($this->data['user_id'] === $value) { - return; - } - - $this->data['user_id'] = $value; - - $this->setModified('user_id'); - } - - /** - * Get Project models by GroupId for this ProjectGroup. - * - * @return \PHPCensor\Model\Project[] - */ - public function getGroupProjects() - { - return Factory::getStore('Project', 'PHPCensor')->getByGroupId($this->getId(), false); - } -} diff --git a/src/PHPCensor/Model/User.php b/src/PHPCensor/Model/User.php deleted file mode 100644 index d49c747..0000000 --- a/src/PHPCensor/Model/User.php +++ /dev/null @@ -1,350 +0,0 @@ - - */ -class User extends Model -{ - /** - * @var array - */ - public static $sleepable = []; - - /** - * @var string - */ - protected $tableName = 'user'; - - /** - * @var string - */ - protected $modelName = 'User'; - - /** - * @var array - */ - protected $data = [ - 'id' => null, - 'email' => null, - 'hash' => null, - 'is_admin' => null, - 'name' => null, - 'language' => null, - 'per_page' => null, - 'provider_key' => null, - 'provider_data' => null, - 'remember_key' => null, - ]; - - /** - * @var array - */ - protected $getters = [ - 'id' => 'getId', - 'email' => 'getEmail', - 'hash' => 'getHash', - 'is_admin' => 'getIsAdmin', - 'name' => 'getName', - 'language' => 'getLanguage', - 'per_page' => 'getPerPage', - 'provider_key' => 'getProviderKey', - 'provider_data' => 'getProviderData', - 'remember_key' => 'getRememberKey', - ]; - - /** - * @var array - */ - protected $setters = [ - 'id' => 'setId', - 'email' => 'setEmail', - 'hash' => 'setHash', - 'is_admin' => 'setIsAdmin', - 'name' => 'setName', - 'language' => 'setLanguage', - 'per_page' => 'setPerPage', - 'provider_key' => 'setProviderKey', - 'provider_data' => 'setProviderData', - 'remember_key' => 'setRememberKey', - ]; - - /** - * @return int - */ - public function getId() - { - $rtn = $this->data['id']; - - return $rtn; - } - - /** - * @return string - */ - public function getEmail() - { - $rtn = $this->data['email']; - - return $rtn; - } - - /** - * @return string - */ - public function getHash() - { - $rtn = $this->data['hash']; - - return $rtn; - } - - /** - * @return string - */ - public function getName() - { - $rtn = $this->data['name']; - - return $rtn; - } - - /** - * @return int - */ - public function getIsAdmin() - { - $rtn = $this->data['is_admin']; - - return $rtn; - } - - /** - * @return string - */ - public function getProviderKey() - { - $rtn = $this->data['provider_key']; - - return $rtn; - } - - /** - * @return string - */ - public function getProviderData() - { - $rtn = $this->data['provider_data']; - - return $rtn; - } - - /** - * @return string - */ - public function getRememberKey() - { - $rtn = $this->data['remember_key']; - - return $rtn; - } - - /** - * @return string - */ - public function getLanguage() - { - $rtn = $this->data['language']; - - return $rtn; - } - - /** - * @return string - */ - public function getPerPage() - { - $rtn = $this->data['per_page']; - - return $rtn; - } - - /** - * @param $value int - */ - public function setId($value) - { - $this->validateNotNull('id', $value); - $this->validateInt('id', $value); - - if ($this->data['id'] === $value) { - return; - } - - $this->data['id'] = $value; - - $this->setModified('id'); - } - - /** - * @param $value string - */ - public function setEmail($value) - { - $this->validateNotNull('email', $value); - $this->validateString('email', $value); - - if ($this->data['email'] === $value) { - return; - } - - $this->data['email'] = $value; - - $this->setModified('email'); - } - - /** - * @param $value string - */ - public function setHash($value) - { - $this->validateNotNull('hash', $value); - $this->validateString('hash', $value); - - if ($this->data['hash'] === $value) { - return; - } - - $this->data['hash'] = $value; - - $this->setModified('hash'); - } - - /** - * @param $value string - */ - public function setName($value) - { - $this->validateNotNull('name', $value); - $this->validateString('name', $value); - - if ($this->data['name'] === $value) { - return; - } - - $this->data['name'] = $value; - - $this->setModified('name'); - } - - /** - * @param $value int - */ - public function setIsAdmin($value) - { - $this->validateNotNull('is_admin', $value); - $this->validateInt('is_admin', $value); - - if ($this->data['is_admin'] === $value) { - return; - } - - $this->data['is_admin'] = $value; - - $this->setModified('is_admin'); - } - - /** - * @param $value string - */ - public function setProviderKey($value) - { - $this->validateNotNull('provider_key', $value); - $this->validateString('provider_key', $value); - - if ($this->data['provider_key'] === $value) { - return; - } - - $this->data['provider_key'] = $value; - - $this->setModified('provider_key'); - } - - /** - * @param $value string - */ - public function setProviderData($value) - { - $this->validateString('provider_data', $value); - - if ($this->data['provider_data'] === $value) { - return; - } - - $this->data['provider_data'] = $value; - - $this->setModified('provider_data'); - } - - /** - * @param $value string - */ - public function setRememberKey($value) - { - $this->validateString('remember_key', $value); - - if ($this->data['remember_key'] === $value) { - return; - } - - $this->data['remember_key'] = $value; - - $this->setModified('remember_key'); - } - - /** - * @param $value string - */ - public function setLanguage($value) - { - if ($this->data['language'] === $value) { - return; - } - - $this->data['language'] = $value; - - $this->setModified('language'); - } - - /** - * @param $value string - */ - public function setPerPage($value) - { - if ($this->data['per_page'] === $value) { - return; - } - - $this->data['per_page'] = $value; - - $this->setModified('per_page'); - } - - /** - * @return integer - */ - public function getFinalPerPage() - { - $perPage = $this->getPerPage(); - if ($perPage) { - return (integer)$perPage; - } - - return (integer)Config::getInstance()->get('php-censor.per_page', 10); - } -} diff --git a/src/PHPCensor/Plugin.php b/src/PHPCensor/Plugin.php deleted file mode 100644 index 0bb653b..0000000 --- a/src/PHPCensor/Plugin.php +++ /dev/null @@ -1,98 +0,0 @@ - - */ -abstract class Plugin -{ - const STATUS_PENDING = 0; - const STATUS_RUNNING = 1; - const STATUS_SUCCESS = 2; - const STATUS_FAILED = 3; - const STATUS_FAILED_ALLOWED = 4; - /** - * @var \PHPCensor\Builder - */ - protected $builder; - - /** - * @var \PHPCensor\Model\Build - */ - protected $build; - - /** - * @var array - */ - protected $options; - - /** - * @var string - */ - protected $priorityPath = 'local'; - - /** - * @param Builder $builder - * @param Build $build - * @param array $options - */ - public function __construct(Builder $builder, Build $build, array $options = []) - { - $this->builder = $builder; - $this->build = $build; - $this->options = $options; - - if (!empty($options['priority_path']) && in_array($options['priority_path'], ['global', 'system'])) { - $this->priorityPath = $options['priority_path']; - } - - $this->builder->logDebug('Plugin options: ' . json_encode($options)); - } - - /** - * Find a binary required by a plugin. - * - * @param string $binary - * @param boolean $quiet Returns null instead of throwing an exception. - * - * @return null|string - * - * @throws \Exception when no binary has been found and $quiet is false. - */ - public function findBinary($binary, $quiet = false) - { - return $this->builder->findBinary($binary, $quiet, $this->priorityPath); - } - - /** - * @return Build - */ - public function getBuild() - { - return $this->build; - } - - /** - * @return Builder - */ - public function getBuilder() - { - return $this->builder; - } - - /** - * @return boolean - */ - abstract public function execute(); - - /** - * @return string - */ - public static function pluginName() - { - return ''; - } -} diff --git a/src/PHPCensor/Plugin/Atoum.php b/src/PHPCensor/Plugin/Atoum.php deleted file mode 100644 index f62424b..0000000 --- a/src/PHPCensor/Plugin/Atoum.php +++ /dev/null @@ -1,109 +0,0 @@ -executable = $this->builder->buildPath . DIRECTORY_SEPARATOR.$options['executable']; - } else { - $this->executable = $this->findBinary('atoum'); - } - - if (isset($options['args'])) { - $this->args = $options['args']; - } - - if (isset($options['config'])) { - $this->config = $options['config']; - } - - if (isset($options['directory'])) { - $this->directory = $options['directory']; - } - } - - /** - * Run the Atoum plugin. - * - * @return bool - */ - public function execute() - { - $cmd = $this->executable; - - if ($this->args !== null) { - $cmd .= " {$this->args}"; - } - - if ($this->config !== null) { - $cmd .= " -c '{$this->config}'"; - } - - if ($this->directory !== null) { - $dirPath = $this->builder->buildPath . DIRECTORY_SEPARATOR . $this->directory; - $cmd .= " -d '{$dirPath}'"; - } - - chdir($this->builder->buildPath); - - $output = ''; - $status = true; - - exec($cmd, $output); - - if (count(preg_grep("/Success \(/", $output)) == 0) { - $status = false; - $this->builder->log($output); - } - - if (count($output) == 0) { - $status = false; - $this->builder->log('No tests have been performed.'); - } - - return $status; - } -} diff --git a/src/PHPCensor/Plugin/Behat.php b/src/PHPCensor/Plugin/Behat.php deleted file mode 100644 index c47be48..0000000 --- a/src/PHPCensor/Plugin/Behat.php +++ /dev/null @@ -1,128 +0,0 @@ - - */ -class Behat extends Plugin -{ - protected $features; - protected $executable; - - /** - * @return string - */ - public static function pluginName() - { - return 'behat'; - } - - /** - * {@inheritdoc} - */ - public function __construct(Builder $builder, Build $build, array $options = []) - { - parent::__construct($builder, $build, $options); - - $this->features = ''; - - if (isset($options['executable'])) { - $this->executable = $options['executable']; - } else { - $this->executable = $this->findBinary('behat'); - } - - if (!empty($options['features'])) { - $this->features = $options['features']; - } - } - - /** - * Runs Behat tests. - */ - public function execute() - { - $current_dir = getcwd(); - chdir($this->builder->buildPath); - - $behat = $this->executable; - - if (!$behat) { - $this->builder->logFailure(sprintf('Could not find %s', 'behat')); - - return false; - } - - $success = $this->builder->executeCommand($behat . ' %s', $this->features); - chdir($current_dir); - - list($errorCount, $data) = $this->parseBehatOutput(); - - $this->build->storeMeta('behat-warnings', $errorCount); - $this->build->storeMeta('behat-data', $data); - - return $success; - } - - /** - * Parse the behat output and return details on failures - * - * @return array - */ - public function parseBehatOutput() - { - $output = $this->builder->getLastOutput(); - - $parts = explode('---', $output); - - if (count($parts) <= 1) { - return [0, []]; - } - - $lines = explode(PHP_EOL, $parts[1]); - - $storeFailures = false; - $data = []; - - foreach ($lines as $line) { - $line = trim($line); - if ($line == 'Failed scenarios:') { - $storeFailures = true; - continue; - } - - if (strpos($line, ':') === false) { - $storeFailures = false; - } - - if ($storeFailures) { - $lineParts = explode(':', $line); - $data[] = [ - 'file' => $lineParts[0], - 'line' => $lineParts[1] - ]; - - $this->build->reportError( - $this->builder, - 'behat', - 'Behat scenario failed.', - BuildError::SEVERITY_HIGH, - $lineParts[0], - $lineParts[1] - ); - } - } - - $errorCount = count($data); - - return [$errorCount, $data]; - } -} diff --git a/src/PHPCensor/Plugin/Campfire.php b/src/PHPCensor/Plugin/Campfire.php deleted file mode 100644 index d17d110..0000000 --- a/src/PHPCensor/Plugin/Campfire.php +++ /dev/null @@ -1,151 +0,0 @@ - - */ -class Campfire extends Plugin -{ - protected $url; - protected $authToken; - protected $userAgent; - protected $cookie; - protected $verbose; - protected $roomId; - protected $message; - - /** - * @return string - */ - public static function pluginName() - { - return 'campfire'; - } - - /** - * {@inheritdoc} - */ - public function __construct(Builder $builder, Build $build, array $options = []) - { - parent::__construct($builder, $build, $options); - - $this->message = $options['message']; - $this->userAgent = "PHP Censor/1.0"; - $this->cookie = "php-censor-cookie"; - - $buildSettings = $this->builder->getConfig('build_settings'); - - if (isset($buildSettings['campfire'])) { - $campfire = $buildSettings['campfire']; - $this->url = $campfire['url']; - $this->authToken = $campfire['authToken']; - $this->roomId = $campfire['roomId']; - } else { - throw new \Exception('No connection parameters given for Campfire plugin'); - } - } - - /** - * Run the Campfire plugin. - * @return bool|mixed - */ - public function execute() - { - $url = APP_URL . "build/view/" . $this->build->getId(); - $message = str_replace("%buildurl%", $url, $this->message); - $this->joinRoom($this->roomId); - $status = $this->speak($message, $this->roomId); - $this->leaveRoom($this->roomId); - - return $status; - - } - - /** - * Join a Campfire room. - * @param $roomId - */ - public function joinRoom($roomId) - { - $this->getPageByPost('/room/'.$roomId.'/join.json'); - } - - /** - * Leave a Campfire room. - * @param $roomId - */ - public function leaveRoom($roomId) - { - $this->getPageByPost('/room/'.$roomId.'/leave.json'); - } - - /** - * Send a message to a campfire room. - * @param $message - * @param $roomId - * @param bool $isPaste - * @return bool|mixed - */ - public function speak($message, $roomId, $isPaste = false) - { - $page = '/room/'.$roomId.'/speak.json'; - - if ($isPaste) { - $type = 'PasteMessage'; - } else { - $type = 'TextMessage'; - } - - return $this->getPageByPost($page, ['message' => ['type' => $type, 'body' => $message]]); - - } - - /** - * Make a request to Campfire. - * @param $page - * @param null $data - * @return bool|mixed - */ - private function getPageByPost($page, $data = null) - { - $url = $this->url . $page; - // The new API allows JSON, so we can pass - // PHP data structures instead of old school POST - $json = json_encode($data); - - // cURL init & config - $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, ["Content-type: application/json"]); - curl_setopt($handle, CURLOPT_COOKIEFILE, $this->cookie); - - curl_setopt($handle, CURLOPT_POSTFIELDS, $json); - $output = curl_exec($handle); - - curl_close($handle); - - // We tend to get one space with an otherwise blank response - $output = trim($output); - - if (strlen($output)) { - /* Responses are JSON. Decode it to a data structure */ - return json_decode($output); - } - - // Simple 200 OK response (such as for joining a room) - return true; - } -} diff --git a/src/PHPCensor/Plugin/CleanBuild.php b/src/PHPCensor/Plugin/CleanBuild.php deleted file mode 100644 index 01f05e0..0000000 --- a/src/PHPCensor/Plugin/CleanBuild.php +++ /dev/null @@ -1,59 +0,0 @@ - - */ -class CleanBuild extends Plugin -{ - protected $remove; - - /** - * @return string - */ - public static function pluginName() - { - return 'clean_build'; - } - - /** - * {@inheritdoc} - */ - public function __construct(Builder $builder, Build $build, array $options = []) - { - parent::__construct($builder, $build, $options); - - $this->remove = isset($options['remove']) && is_array($options['remove']) ? $options['remove'] : []; - } - - /** - * Executes Composer and runs a specified command (e.g. install / update) - */ - public function execute() - { - $cmd = 'rm -Rf "%s"'; - - $this->builder->executeCommand($cmd, $this->builder->buildPath . 'composer.phar'); - $this->builder->executeCommand($cmd, $this->builder->buildPath . 'composer.lock'); - - $success = true; - - foreach ($this->remove as $file) { - $ok = $this->builder->executeCommand($cmd, $this->builder->buildPath . $file); - - if (!$ok) { - $success = false; - } - } - - return $success; - } -} diff --git a/src/PHPCensor/Plugin/Codeception.php b/src/PHPCensor/Plugin/Codeception.php deleted file mode 100644 index d5e8e1d..0000000 --- a/src/PHPCensor/Plugin/Codeception.php +++ /dev/null @@ -1,164 +0,0 @@ - - * @author Igor Timoshenko - * @author Adam Cooper - */ -class Codeception extends Plugin implements ZeroConfigPluginInterface -{ - /** @var string */ - protected $args = ''; - - /** - * @var string $ymlConfigFile The path of a yml config for Codeception - */ - protected $ymlConfigFile; - - /** - * @var array $path The path to the codeception tests folder. - */ - protected $path = [ - 'tests/_output', - 'tests/_log', - ]; - - /** - * @return string - */ - public static function pluginName() - { - return 'codeception'; - } - - /** - * {@inheritdoc} - */ - public function __construct(Builder $builder, Build $build, array $options = []) - { - parent::__construct($builder, $build, $options); - - if (empty($options['config'])) { - $this->ymlConfigFile = self::findConfigFile($this->builder->buildPath); - } else { - $this->ymlConfigFile = $options['config']; - } - - if (isset($options['args'])) { - $this->args = (string) $options['args']; - } - - if (isset($options['path'])) { - array_unshift($this->path, $options['path']); - } - } - - /** - * @param $stage - * @param Builder $builder - * @param Build $build - * @return bool - */ - public static function canExecute($stage, Builder $builder, Build $build) - { - return $stage == Build::STAGE_TEST && !is_null(self::findConfigFile($builder->buildPath)); - } - - /** - * 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; - } - - /** - * Runs Codeception tests - */ - public function execute() - { - if (empty($this->ymlConfigFile)) { - throw new \Exception("No configuration file found"); - } - - // Run any config files first. This can be either a single value or an array. - return $this->runConfigFile($this->ymlConfigFile); - } - - /** - * Run tests from a Codeception config file. - * @param $configPath - * @return bool|mixed - * @throws \Exception - */ - protected function runConfigFile($configPath) - { - $codeception = $this->findBinary('codecept'); - - if (!$codeception) { - $this->builder->logFailure(sprintf('Could not find %s', 'codecept')); - - return false; - } - - $cmd = 'cd "%s" && ' . $codeception . ' run -c "%s" --xml ' . $this->args; - - $configPath = $this->builder->buildPath . $configPath; - $success = $this->builder->executeCommand($cmd, $this->builder->buildPath, $configPath); - - $parser = new YamlParser(); - $yaml = file_get_contents($configPath); - $config = (array)$parser->parse($yaml); - - $outputPath = null; - if ($config && isset($config['paths']['log'])) { - $outputPath = $this->builder->buildPath . $config['paths']['log'] . '/'; - } - - if (!file_exists($outputPath . 'report.xml')) { - foreach ($this->path as $path) { - $outputPath = $this->builder->buildPath . rtrim($path, '/\\') . '/'; - if (file_exists($outputPath . 'report.xml')) { - break; - } - } - } - - $xml = file_get_contents($outputPath . 'report.xml', false); - $parser = new Parser($this->builder, $xml); - $output = $parser->parse(); - - $meta = [ - '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()); - - return $success; - } -} diff --git a/src/PHPCensor/Plugin/Composer.php b/src/PHPCensor/Plugin/Composer.php deleted file mode 100644 index 56062b0..0000000 --- a/src/PHPCensor/Plugin/Composer.php +++ /dev/null @@ -1,124 +0,0 @@ - - */ -class Composer extends Plugin implements ZeroConfigPluginInterface -{ - protected $directory; - protected $action; - protected $preferDist; - protected $nodev; - protected $ignorePlatformReqs; - protected $preferSource; - - /** - * @return string - */ - public static function pluginName() - { - return 'composer'; - } - - /** - * {@inheritdoc} - */ - public function __construct(Builder $builder, Build $build, array $options = []) - { - parent::__construct($builder, $build, $options); - - $path = $this->builder->buildPath; - $this->directory = $path; - $this->action = 'install'; - $this->preferDist = false; - $this->preferSource = false; - $this->nodev = false; - $this->ignorePlatformReqs = false; - - if (array_key_exists('directory', $options)) { - $this->directory = $path . DIRECTORY_SEPARATOR . $options['directory']; - } - - if (array_key_exists('action', $options)) { - $this->action = $options['action']; - } - - if (array_key_exists('prefer_dist', $options)) { - $this->preferDist = (bool)$options['prefer_dist']; - } - - if (array_key_exists('prefer_source', $options)) { - $this->preferDist = false; - $this->preferSource = (bool)$options['prefer_source']; - } - - if (array_key_exists('no_dev', $options)) { - $this->nodev = (bool)$options['no_dev']; - } - - if (array_key_exists('ignore_platform_reqs', $options)) { - $this->ignorePlatformReqs = (bool)$options['ignore_platform_reqs']; - } - } - - /** - * Check if this plugin can be executed. - * @param $stage - * @param Builder $builder - * @param Build $build - * @return bool - */ - public static function canExecute($stage, Builder $builder, Build $build) - { - $path = $builder->buildPath . DIRECTORY_SEPARATOR . 'composer.json'; - - if (file_exists($path) && $stage == Build::STAGE_SETUP) { - return true; - } - - return false; - } - - /** - * Executes Composer and runs a specified command (e.g. install / update) - */ - public function execute() - { - $composerLocation = $this->findBinary(['composer', 'composer.phar']); - $cmd = $composerLocation . ' --no-ansi --no-interaction '; - - if ($this->preferDist) { - $this->builder->log('Using --prefer-dist flag'); - $cmd .= ' --prefer-dist'; - } - - if ($this->preferSource) { - $this->builder->log('Using --prefer-source flag'); - $cmd .= ' --prefer-source'; - } - - if ($this->nodev) { - $this->builder->log('Using --no-dev flag'); - $cmd .= ' --no-dev'; - } - - if ($this->ignorePlatformReqs) { - $this->builder->log('Using --ignore-platform-reqs flag'); - $cmd .= ' --ignore-platform-reqs'; - } - - $cmd .= ' --working-dir="%s" %s'; - - return $this->builder->executeCommand($cmd, $this->directory, $this->action); - } -} diff --git a/src/PHPCensor/Plugin/CopyBuild.php b/src/PHPCensor/Plugin/CopyBuild.php deleted file mode 100644 index 1c2a28c..0000000 --- a/src/PHPCensor/Plugin/CopyBuild.php +++ /dev/null @@ -1,91 +0,0 @@ - - */ -class CopyBuild extends Plugin -{ - protected $directory; - protected $ignore; - protected $wipe; - - /** - * @return string - */ - public static function pluginName() - { - return 'copy_build'; - } - - /** - * {@inheritdoc} - */ - public function __construct(Builder $builder, Build $build, array $options = []) - { - parent::__construct($builder, $build, $options); - - $path = $this->builder->buildPath; - $this->directory = isset($options['directory']) ? $options['directory'] : $path; - $this->wipe = isset($options['wipe']) ? (bool)$options['wipe'] : false; - $this->ignore = isset($options['respect_ignore']) ? (bool)$options['respect_ignore'] : false; - } - - /** - * Copies files from the root of the build directory into the target folder - */ - public function execute() - { - $build = $this->builder->buildPath; - - if ($this->directory == $build) { - return false; - } - - $this->wipeExistingDirectory(); - - $cmd = 'mkdir -p "%s" && cp -R "%s" "%s"'; - - $success = $this->builder->executeCommand($cmd, $this->directory, $build, $this->directory); - - $this->deleteIgnoredFiles(); - - return $success; - } - - /** - * Wipe the destination directory if it already exists. - * @throws \Exception - */ - protected function wipeExistingDirectory() - { - if ($this->wipe === true && $this->directory != '/' && is_dir($this->directory)) { - $cmd = 'rm -Rf "%s*"'; - $success = $this->builder->executeCommand($cmd, $this->directory); - - if (!$success) { - throw new \Exception(sprintf('Failed to wipe existing directory %s before copy', $this->directory)); - } - } - } - - /** - * Delete any ignored files from the build prior to copying. - */ - protected function deleteIgnoredFiles() - { - if ($this->ignore) { - foreach ($this->builder->ignore as $file) { - $cmd = 'rm -Rf "%s/%s"'; - $this->builder->executeCommand($cmd, $this->directory, $file); - } - } - } -} diff --git a/src/PHPCensor/Plugin/Deployer.php b/src/PHPCensor/Plugin/Deployer.php deleted file mode 100644 index c0e17ec..0000000 --- a/src/PHPCensor/Plugin/Deployer.php +++ /dev/null @@ -1,81 +0,0 @@ - - */ -class Deployer extends Plugin -{ - protected $webhookUrl; - protected $reason; - protected $updateOnly; - - /** - * @return string - */ - public static function pluginName() - { - return 'deployer'; - } - - /** - * {@inheritdoc} - */ - public function __construct(Builder $builder, Build $build, array $options = []) - { - parent::__construct($builder, $build, $options); - - $this->reason = 'PHP Censor Build #%BUILD% - %COMMIT_MESSAGE%'; - if (isset($options['webhook_url'])) { - $this->webhookUrl = $options['webhook_url']; - } - - if (isset($options['reason'])) { - $this->reason = $options['reason']; - } - - $this->updateOnly = isset($options['update_only']) ? (bool) $options['update_only'] : true; - } - - /** - * Copies files from the root of the build directory into the target folder - */ - public function execute() - { - if (empty($this->webhookUrl)) { - $this->builder->logFailure('You must specify a webhook URL.'); - return false; - } - - $client = new Client(); - $response = $client->post( - $this->webhookUrl, - [ - 'form_params' => [ - 'reason' => $this->builder->interpolate($this->reason), - 'source' => 'PHP Censor', - 'url' => $this->builder->interpolate('%BUILD_URI%'), - 'branch' => $this->builder->interpolate('%BRANCH%'), - 'commit' => $this->builder->interpolate('%COMMIT%'), - 'update_only' => $this->updateOnly, - ] - ] - ); - - $status = (integer)$response->getStatusCode(); - - return ( - ($status >= 200 && $status < 300) - ? true - : false - ); - } -} diff --git a/src/PHPCensor/Plugin/Email.php b/src/PHPCensor/Plugin/Email.php deleted file mode 100644 index c460bb4..0000000 --- a/src/PHPCensor/Plugin/Email.php +++ /dev/null @@ -1,214 +0,0 @@ - - */ -class Email extends Plugin -{ - /** - * @return string - */ - public static function pluginName() - { - return 'email'; - } - - /** - * Send a notification mail. - * - * @return boolean - */ - public function execute() - { - $addresses = $this->getEmailAddresses(); - - // Without some email addresses in the yml file then we - // can't do anything. - if (count($addresses) == 0) { - return false; - } - - $buildStatus = $this->build->isSuccessful() ? "Passing Build" : "Failing Build"; - $projectName = $this->build->getProject()->getTitle(); - - try { - $view = $this->getMailTemplate(); - } catch (\RuntimeException $e) { - $this->builder->log( - sprintf('Unknown mail template "%s", falling back to default.', $this->options['template']), - LogLevel::WARNING - ); - $view = $this->getDefaultMailTemplate(); - } - - $view->build = $this->build; - $view->project = $this->build->getProject(); - - $layout = new View('Email/layout'); - $layout->build = $this->build; - $layout->project = $this->build->getProject(); - $layout->content = $view->render(); - $body = $layout->render(); - - $sendFailures = $this->sendSeparateEmails( - $addresses, - sprintf("PHP Censor - %s - %s", $projectName, $buildStatus), - $body - ); - - // This is a success if we've not failed to send anything. - $this->builder->log(sprintf('%d emails sent.', (count($addresses) - $sendFailures))); - $this->builder->log(sprintf('%d emails failed to send.', $sendFailures)); - - return ($sendFailures === 0); - } - - /** - * @param string $toAddress Single address to send to - * @param string[] $ccList - * @param string $subject Email subject - * @param string $body Email body - * - * @return integer - */ - protected function sendEmail($toAddress, $ccList, $subject, $body) - { - $email = new EmailHelper(); - - $email->setEmailTo($toAddress, $toAddress); - $email->setSubject($subject); - $email->setBody($body); - $email->setHtml(true); - - if (is_array($ccList) && count($ccList)) { - foreach ($ccList as $address) { - $email->addCc($address, $address); - } - } - - return $email->send($this->builder); - } - - /** - * Send an email to a list of specified subjects. - * - * @param array $toAddresses - * List of destination addresses for message. - * @param string $subject - * Mail subject - * @param string $body - * Mail body - * - * @return int number of failed messages - */ - public function sendSeparateEmails(array $toAddresses, $subject, $body) - { - $failures = 0; - $ccList = $this->getCcAddresses(); - - foreach ($toAddresses as $address) { - if (!$this->sendEmail($address, $ccList, $subject, $body)) { - $failures++; - } - } - - return $failures; - } - - /** - * Get the list of email addresses to send to. - * @return array - */ - protected function getEmailAddresses() - { - $addresses = []; - $committer = $this->build->getCommitterEmail(); - - $this->builder->logDebug(sprintf("Committer email: '%s'", $committer)); - $this->builder->logDebug(sprintf( - "Committer option: '%s'", - (!empty($this->options['committer']) && $this->options['committer']) ? 'true' : 'false' - )); - - if (!empty($this->options['committer']) && $this->options['committer']) { - if ($committer) { - $addresses[] = $committer; - } - } - - $this->builder->logDebug(sprintf( - "Addresses option: '%s'", - (!empty($this->options['addresses']) && is_array($this->options['addresses'])) ? implode(', ', $this->options['addresses']) : 'false' - )); - - if (!empty($this->options['addresses']) && is_array($this->options['addresses'])) { - foreach ($this->options['addresses'] as $address) { - $addresses[] = $address; - } - } - - $this->builder->logDebug(sprintf( - "Default mailTo option: '%s'", - !empty($this->options['default_mailto_address']) ? $this->options['default_mailto_address'] : 'false' - )); - - if (empty($addresses) && !empty($this->options['default_mailto_address'])) { - $addresses[] = $this->options['default_mailto_address']; - } - - return array_unique($addresses); - } - - /** - * Get the list of email addresses to CC. - * - * @return array - */ - protected function getCcAddresses() - { - $ccAddresses = []; - - if (isset($this->options['cc'])) { - foreach ($this->options['cc'] as $address) { - $ccAddresses[] = $address; - } - } - - return $ccAddresses; - } - - /** - * Get the mail template used to sent the mail. - * - * @return View - */ - protected function getMailTemplate() - { - if (isset($this->options['template'])) { - return new View('Email/' . $this->options['template']); - } - - return $this->getDefaultMailTemplate(); - } - - /** - * Get the default mail template. - * - * @return View - */ - protected function getDefaultMailTemplate() - { - $template = $this->build->isSuccessful() ? 'short' : 'long'; - - return new View('Email/' . $template); - } -} diff --git a/src/PHPCensor/Plugin/Env.php b/src/PHPCensor/Plugin/Env.php deleted file mode 100644 index cec0703..0000000 --- a/src/PHPCensor/Plugin/Env.php +++ /dev/null @@ -1,46 +0,0 @@ - - */ -class Env extends Plugin -{ - protected $env_vars; - - /** - * @return string - */ - public static function pluginName() - { - return 'env'; - } - - /** - * Adds the specified environment variables to the builder environment - */ - public function execute() - { - $success = true; - foreach ($this->options as $key => $value) { - if (is_numeric($key)) { - // This allows the developer to specify env vars like " - FOO=bar" or " - FOO: bar" - $env_var = is_array($value)? key($value).'='.current($value): $value; - } else { - // This allows the standard syntax: "FOO: bar" - $env_var = "$key=$value"; - } - - if (!putenv($this->builder->interpolate($env_var))) { - $success = false; - $this->builder->logFailure('Unable to set environment variable'); - } - } - return $success; - } -} diff --git a/src/PHPCensor/Plugin/FlowdockNotify.php b/src/PHPCensor/Plugin/FlowdockNotify.php deleted file mode 100644 index de57770..0000000 --- a/src/PHPCensor/Plugin/FlowdockNotify.php +++ /dev/null @@ -1,73 +0,0 @@ - - */ -class FlowdockNotify extends Plugin -{ - protected $api_key; - protected $email; - protected $message; - - const MESSAGE_DEFAULT = 'Build %BUILD% has finished for commit %SHORT_COMMIT% - (%COMMIT_EMAIL%)> on branch %BRANCH%'; - - /** - * @return string - */ - public static function pluginName() - { - return 'flowdock_notify'; - } - - /** - * {@inheritdoc} - */ - public function __construct(Builder $builder, Build $build, array $options = []) - { - parent::__construct($builder, $build, $options); - - if (!is_array($options) || !isset($options['api_key'])) { - throw new \Exception('Please define the api_key for Flowdock Notify plugin!'); - } - $this->api_key = trim($options['api_key']); - $this->message = isset($options['message']) ? $options['message'] : self::MESSAGE_DEFAULT; - $this->email = isset($options['email']) ? $options['email'] : 'PHP Censor'; - } - - /** - * Run the Flowdock plugin. - * @return bool - * @throws \Exception - */ - public function execute() - { - - $message = $this->builder->interpolate($this->message); - $successfulBuild = $this->build->isSuccessful() ? 'Success' : 'Failed'; - $push = new Push($this->api_key); - $flowMessage = TeamInboxMessage::create() - ->setSource("PHPCensor") - ->setFromAddress($this->email) - ->setFromName($this->build->getProject()->getTitle()) - ->setSubject($successfulBuild) - ->setTags(['#ci']) - ->setLink($this->build->getBranchLink()) - ->setContent($message); - - if (!$push->sendTeamInboxMessage($flowMessage, ['connect_timeout' => 5000, 'timeout' => 5000])) { - throw new \Exception(sprintf('Flowdock Failed: %s', $flowMessage->getResponseErrors())); - } - return true; - } -} diff --git a/src/PHPCensor/Plugin/Git.php b/src/PHPCensor/Plugin/Git.php deleted file mode 100644 index ab01b0a..0000000 --- a/src/PHPCensor/Plugin/Git.php +++ /dev/null @@ -1,157 +0,0 @@ - - */ -class Git extends Plugin -{ - protected $actions = []; - - /** - * @return string - */ - public static function pluginName() - { - return 'git'; - } - - /** - * Run the Git plugin. - * @return bool - */ - public function execute() - { - $buildPath = $this->builder->buildPath; - - // Check if there are any actions to be run for the branch we're running on: - if (!array_key_exists($this->build->getBranch(), $this->actions)) { - return true; - } - - // If there are, run them: - $curdir = getcwd(); - chdir($buildPath); - - $success = true; - foreach ($this->actions[$this->build->getBranch()] as $action => $options) { - if (!$this->runAction($action, $options)) { - $success = false; - break; - } - } - - chdir($curdir); - - return $success; - } - - /** - * Determine which action to run, and run it. - * @param $action - * @param array $options - * @return bool - */ - protected function runAction($action, array $options = []) - { - switch ($action) { - case 'merge': - return $this->runMergeAction($options); - - case 'tag': - return $this->runTagAction($options); - - case 'pull': - return $this->runPullAction($options); - - case 'push': - return $this->runPushAction($options); - } - - - return false; - } - - /** - * Handle a merge action. - * @param $options - * @return bool - */ - protected function runMergeAction($options) - { - if (array_key_exists('branch', $options)) { - $cmd = 'cd "%s" && git checkout %s && git merge "%s"'; - $path = $this->builder->buildPath; - return $this->builder->executeCommand($cmd, $path, $options['branch'], $this->build->getBranch()); - } - } - - /** - * Handle a tag action. - * @param $options - * @return bool - */ - protected function runTagAction($options) - { - $tagName = date('Ymd-His'); - $message = sprintf('Tag created by PHP Censor: %s', date('Y-m-d H:i:s')); - - if (array_key_exists('name', $options)) { - $tagName = $this->builder->interpolate($options['name']); - } - - if (array_key_exists('message', $options)) { - $message = $this->builder->interpolate($options['message']); - } - - $cmd = 'git tag %s -m "%s"'; - return $this->builder->executeCommand($cmd, $tagName, $message); - } - - /** - * Handle a pull action. - * @param $options - * @return bool - */ - protected function runPullAction($options) - { - $branch = $this->build->getBranch(); - $remote = 'origin'; - - if (array_key_exists('branch', $options)) { - $branch = $this->builder->interpolate($options['branch']); - } - - if (array_key_exists('remote', $options)) { - $remote = $this->builder->interpolate($options['remote']); - } - - return $this->builder->executeCommand('git pull %s %s', $remote, $branch); - } - - /** - * Handle a push action. - * @param $options - * @return bool - */ - protected function runPushAction($options) - { - $branch = $this->build->getBranch(); - $remote = 'origin'; - - if (array_key_exists('branch', $options)) { - $branch = $this->builder->interpolate($options['branch']); - } - - if (array_key_exists('remote', $options)) { - $remote = $this->builder->interpolate($options['remote']); - } - - return $this->builder->executeCommand('git push %s %s', $remote, $branch); - } -} diff --git a/src/PHPCensor/Plugin/Grunt.php b/src/PHPCensor/Plugin/Grunt.php deleted file mode 100644 index e5db5e2..0000000 --- a/src/PHPCensor/Plugin/Grunt.php +++ /dev/null @@ -1,81 +0,0 @@ - - */ -class Grunt extends Plugin -{ - protected $directory; - protected $task; - protected $preferDist; - protected $grunt; - protected $gruntfile; - - /** - * @return string - */ - public static function pluginName() - { - return 'grunt'; - } - - /** - * {@inheritdoc} - */ - public function __construct(Builder $builder, Build $build, array $options = []) - { - parent::__construct($builder, $build, $options); - - $path = $this->builder->buildPath; - $this->directory = $path; - $this->task = null; - $this->grunt = $this->findBinary('grunt'); - $this->gruntfile = 'Gruntfile.js'; - - // Handle options: - if (isset($options['directory'])) { - $this->directory = $path . DIRECTORY_SEPARATOR . $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']; - } - } - - /** - * 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 - $cmd = 'cd %s && npm install'; - if (!$this->builder->executeCommand($cmd, $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->builder->executeCommand($cmd, $this->directory, $this->gruntfile, $this->task); - } -} diff --git a/src/PHPCensor/Plugin/Gulp.php b/src/PHPCensor/Plugin/Gulp.php deleted file mode 100644 index 6c28cea..0000000 --- a/src/PHPCensor/Plugin/Gulp.php +++ /dev/null @@ -1,81 +0,0 @@ - - */ -class Gulp extends Plugin -{ - protected $directory; - protected $task; - protected $preferDist; - protected $gulp; - protected $gulpfile; - - /** - * @return string - */ - public static function pluginName() - { - return 'gulp'; - } - - /** - * {@inheritdoc} - */ - public function __construct(Builder $builder, Build $build, array $options = []) - { - parent::__construct($builder, $build, $options); - - $path = $this->builder->buildPath; - $this->directory = $path; - $this->task = null; - $this->gulp = $this->findBinary('gulp'); - $this->gulpfile = 'gulpfile.js'; - - // Handle options: - if (isset($options['directory'])) { - $this->directory = $path . DIRECTORY_SEPARATOR . $options['directory']; - } - - if (isset($options['task'])) { - $this->task = $options['task']; - } - - if (isset($options['gulp'])) { - $this->gulp = $options['gulp']; - } - - if (isset($options['gulpfile'])) { - $this->gulpfile = $options['gulpfile']; - } - } - - /** - * Executes gulp and runs a specified command (e.g. install / update) - */ - public function execute() - { - // if npm does not work, we cannot use gulp, so we return false - $cmd = 'cd %s && npm install'; - if (!$this->builder->executeCommand($cmd, $this->directory)) { - return false; - } - - // build the gulp command - $cmd = 'cd %s && ' . $this->gulp; - $cmd .= ' --no-color'; - $cmd .= ' --gulpfile %s'; - $cmd .= ' %s'; // the task that will be executed - - // and execute it - return $this->builder->executeCommand($cmd, $this->directory, $this->gulpfile, $this->task); - } -} diff --git a/src/PHPCensor/Plugin/HipchatNotify.php b/src/PHPCensor/Plugin/HipchatNotify.php deleted file mode 100644 index b855ddf..0000000 --- a/src/PHPCensor/Plugin/HipchatNotify.php +++ /dev/null @@ -1,93 +0,0 @@ - - */ -class HipchatNotify extends Plugin -{ - protected $authToken; - protected $color; - protected $notify; - protected $userAgent; - protected $cookie; - protected $message; - protected $room; - - /** - * @return string - */ - public static function pluginName() - { - return 'hipchat_notify'; - } - - /** - * {@inheritdoc} - */ - public function __construct(Builder $builder, Build $build, array $options = []) - { - parent::__construct($builder, $build, $options); - - $this->userAgent = "PHP Censor/1.0"; - $this->cookie = "php-censor-cookie"; - - if (is_array($options) && isset($options['authToken']) && isset($options['room'])) { - $this->authToken = $options['authToken']; - $this->room = $options['room']; - - if (isset($options['message'])) { - $this->message = $options['message']; - } else { - $this->message = '%PROJECT_TITLE% built at %BUILD_URI%'; - } - - if (isset($options['color'])) { - $this->color = $options['color']; - } else { - $this->color = 'yellow'; - } - - if (isset($options['notify'])) { - $this->notify = $options['notify']; - } else { - $this->notify = false; - } - } else { - throw new \Exception('Please define room and authToken for hipchat_notify plugin.'); - } - } - - /** - * Run the HipChat plugin. - * @return bool - */ - public function execute() - { - $hipChat = new HipChat($this->authToken); - $message = $this->builder->interpolate($this->message); - - $result = true; - if (is_array($this->room)) { - foreach ($this->room as $room) { - if (!$hipChat->message_room($room, 'PHP Censor', $message, $this->notify, $this->color)) { - $result = false; - } - } - } else { - if (!$hipChat->message_room($this->room, 'PHP Censor', $message, $this->notify, $this->color)) { - $result = false; - } - } - - return $result; - } -} diff --git a/src/PHPCensor/Plugin/Irc.php b/src/PHPCensor/Plugin/Irc.php deleted file mode 100644 index 528b815..0000000 --- a/src/PHPCensor/Plugin/Irc.php +++ /dev/null @@ -1,119 +0,0 @@ - - */ -class Irc extends Plugin -{ - protected $message; - protected $server; - protected $port; - protected $room; - protected $nick; - - /** - * @return string - */ - public static function pluginName() - { - return 'irc'; - } - - /** - * {@inheritdoc} - */ - public function __construct(Builder $builder, Build $build, array $options = []) - { - parent::__construct($builder, $build, $options); - - $this->message = $options['message']; - $buildSettings = $this->builder->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']; - } - } - - /** - * Run IRC plugin. - * @return bool - */ - public function execute() - { - $msg = $this->builder->interpolate($this->message); - - if (empty($this->server) || empty($this->room) || empty($this->nick)) { - $this->builder->logFailure('You must configure a server, room and nick.'); - } - - if (empty($this->port)) { - $this->port = 6667; - } - - $sock = fsockopen($this->server, $this->port); - stream_set_timeout($sock, 1); - - $connectCommands = [ - 'USER ' . $this->nick . ' 0 * :' . $this->nick, - 'NICK ' . $this->nick, - ]; - $this->executeIrcCommands($sock, $connectCommands); - $this->executeIrcCommand($sock, 'JOIN ' . $this->room); - $this->executeIrcCommand($sock, 'PRIVMSG ' . $this->room . ' :' . $msg); - - fclose($sock); - - return true; - } - - /** - * @param resource $socket - * @param array $commands - * @return bool - */ - private function executeIrcCommands($socket, array $commands) - { - foreach ($commands as $command) { - fputs($socket, $command . "\n"); - } - - $pingBack = false; - - // almost all servers expect pingback! - while ($response = fgets($socket)) { - $matches = []; - if (preg_match('/^PING \\:([A-Z0-9]+)/', $response, $matches)) { - $pingBack = $matches[1]; - } - } - - if ($pingBack) { - $command = 'PONG :' . $pingBack . "\n"; - fputs($socket, $command); - } - } - - /** - * - * @param resource $socket - * @param string $command - * @return bool - */ - private function executeIrcCommand($socket, $command) - { - return $this->executeIrcCommands($socket, [$command]); - } -} diff --git a/src/PHPCensor/Plugin/Lint.php b/src/PHPCensor/Plugin/Lint.php deleted file mode 100644 index 9ef0796..0000000 --- a/src/PHPCensor/Plugin/Lint.php +++ /dev/null @@ -1,140 +0,0 @@ - - */ -class Lint extends Plugin -{ - protected $directories; - protected $recursive = true; - protected $ignore; - - /** - * @return string - */ - public static function pluginName() - { - return 'lint'; - } - - /** - * {@inheritdoc} - */ - public function __construct(Builder $builder, Build $build, array $options = []) - { - parent::__construct($builder, $build, $options); - - $this->directories = ['']; - $this->ignore = $this->builder->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->builder->quiet = true; - $success = true; - - $php = $this->findBinary('php'); - - foreach ($this->directories as $dir) { - if (!$this->lintDirectory($php, $dir)) { - $success = false; - } - } - - $this->builder->quiet = false; - - return $success; - } - - /** - * Lint an item (file or directory) by calling the appropriate method. - * @param $php - * @param $item - * @param $itemPath - * @return bool - */ - 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 . DIRECTORY_SEPARATOR)) { - $success = false; - } - - return $success; - } - - /** - * Run php -l against a directory of files. - * @param $php - * @param $path - * @return bool - */ - protected function lintDirectory($php, $path) - { - $success = true; - $directory = new \DirectoryIterator($this->builder->buildPath . $path); - - foreach ($directory as $item) { - if ($item->isDot()) { - continue; - } - - $itemPath = $path . $item->getFilename(); - - if (in_array($itemPath, $this->ignore)) { - continue; - } - - if (!$this->lintItem($php, $item, $itemPath)) { - $success = false; - } - } - - return $success; - } - - /** - * Run php -l against a specific file. - * @param $php - * @param $path - * @return bool - */ - protected function lintFile($php, $path) - { - $success = true; - - if (!$this->builder->executeCommand($php . ' -l "%s"', $this->builder->buildPath . $path)) { - $this->builder->logFailure($path); - $success = false; - } - - return $success; - } -} diff --git a/src/PHPCensor/Plugin/Mage.php b/src/PHPCensor/Plugin/Mage.php deleted file mode 100644 index 01018c3..0000000 --- a/src/PHPCensor/Plugin/Mage.php +++ /dev/null @@ -1,116 +0,0 @@ -getSystemConfig('mage'); - if (!empty($config['bin'])) { - $this->mage_bin = $config['bin']; - } - - if (isset($options['env'])) { - $this->mage_env = $builder->interpolate($options['env']); - } - } - - /** - * {@inheritdoc} - */ - public function execute() - { - if (empty($this->mage_env)) { - $this->builder->logFailure('You must specify environment.'); - return false; - } - - $result = $this->builder->executeCommand($this->mage_bin . ' deploy to:' . $this->mage_env); - - try { - $this->builder->log('########## MAGE LOG BEGIN ##########'); - $this->builder->log($this->getMageLog()); - $this->builder->log('########## MAGE LOG END ##########'); - } catch (\Exception $e) { - $this->builder->log($e->getMessage(), LogLevel::NOTICE); - } - - return $result; - } - - /** - * Get mage log lines - * @return array - * @throws \Exception - */ - protected function getMageLog() - { - $logs_dir = $this->build->getBuildPath() . '/.mage/logs'; - if (!is_dir($logs_dir)) { - throw new \Exception('Log directory not found'); - } - - $list = scandir($logs_dir); - if ($list === false) { - throw new \Exception('Log dir read fail'); - } - - $list = array_filter($list, function ($name) { - return preg_match('/^log-\d+-\d+\.log$/', $name); - }); - if (empty($list)) { - throw new \Exception('Log dir filter fail'); - } - - $res = sort($list); - if ($res === false) { - throw new \Exception('Logs sort fail'); - } - - $last_log_file = end($list); - if ($last_log_file === false) { - throw new \Exception('Get last Log name fail'); - } - - $log_content = file_get_contents($logs_dir . '/' . $last_log_file); - if ($log_content === false) { - throw new \Exception('Get last Log content fail'); - } - - $lines = explode("\n", $log_content); - $lines = array_map('trim', $lines); - $lines = array_filter($lines); - - return $lines; - } -} diff --git a/src/PHPCensor/Plugin/Mage3.php b/src/PHPCensor/Plugin/Mage3.php deleted file mode 100644 index b3f9ac2..0000000 --- a/src/PHPCensor/Plugin/Mage3.php +++ /dev/null @@ -1,121 +0,0 @@ -getSystemConfig('mage3'); - if (!empty($config['bin'])) { - $this->mage_bin = $config['bin']; - } - - if (isset($options['env'])) { - $this->mage_env = $builder->interpolate($options['env']); - } - - if (isset($options['log_dir'])) { - $this->mage_log_dir = $builder->interpolate($options['log_dir']); - } - } - - /** - * {@inheritdoc} - */ - public function execute() - { - if (empty($this->mage_env)) { - $this->builder->logFailure('You must specify environment.'); - return false; - } - - $result = $this->builder->executeCommand($this->mage_bin . ' -n deploy ' . $this->mage_env); - - try { - $this->builder->log('########## MAGE LOG BEGIN ##########'); - $this->builder->log($this->getMageLog()); - $this->builder->log('########## MAGE LOG END ##########'); - } catch (\Exception $e) { - $this->builder->log($e->getMessage(), LogLevel::NOTICE); - } - - return $result; - } - - /** - * Get mage log lines - * @return array - * @throws \Exception - */ - protected function getMageLog() - { - $logs_dir = $this->build->getBuildPath() . (!empty($this->mage_log_dir) ? '/' . $this->mage_log_dir : ''); - if (!is_dir($logs_dir)) { - throw new \Exception('Log directory not found'); - } - - $list = scandir($logs_dir); - if ($list === false) { - throw new \Exception('Log dir read fail'); - } - - $list = array_filter($list, function ($name) { - return preg_match('/^\d+_\d+\.log$/', $name); - }); - if (empty($list)) { - throw new \Exception('Log dir filter fail'); - } - - $res = sort($list); - if ($res === false) { - throw new \Exception('Logs sort fail'); - } - - $last_log_file = end($list); - if ($last_log_file === false) { - throw new \Exception('Get last Log name fail'); - } - - $log_content = file_get_contents($logs_dir . '/' . $last_log_file); - if ($log_content === false) { - throw new \Exception('Get last Log content fail'); - } - - $lines = explode("\n", $log_content); - $lines = array_map('trim', $lines); - $lines = array_filter($lines); - - return $lines; - } -} diff --git a/src/PHPCensor/Plugin/Mysql.php b/src/PHPCensor/Plugin/Mysql.php deleted file mode 100644 index a7bfbb2..0000000 --- a/src/PHPCensor/Plugin/Mysql.php +++ /dev/null @@ -1,160 +0,0 @@ - - * @author Steve Kamerman - */ -class Mysql extends Plugin -{ - /** - * @var string - */ - protected $host; - - /** - * @var string - */ - protected $user; - - /** - * @var string - */ - protected $pass; - - /** - * @return string - */ - public static function pluginName() - { - return 'mysql'; - } - - /** - * {@inheritdoc} - */ - public function __construct(Builder $builder, Build $build, array $options = []) - { - parent::__construct($builder, $build, $options); - - $config = Database::getConnection('write')->getDetails(); - - $this->host =(defined('DB_HOST')) ? DB_HOST : null; - $this->user = $config['user']; - $this->pass = $config['pass']; - - $buildSettings = $this->builder->getConfig('build_settings'); - - if (!isset($buildSettings['mysql'])) { - return; - } - - if (!empty($buildSettings['mysql']['host'])) { - $this->host = $this->builder->interpolate($buildSettings['mysql']['host']); - } - - if (!empty($buildSettings['mysql']['user'])) { - $this->user = $this->builder->interpolate($buildSettings['mysql']['user']); - } - - if (array_key_exists('pass', $buildSettings['mysql'])) { - $this->pass = $buildSettings['mysql']['pass']; - } - } - - /** - * Connects to MySQL and runs a specified set of queries. - * @return boolean - */ - public function execute() - { - try { - $opts = [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]; - $pdo = new PDO('mysql:host=' . $this->host, $this->user, $this->pass, $opts); - - foreach ($this->options as $query) { - if (!is_array($query)) { - // Simple query - $pdo->query($this->builder->interpolate($query)); - } elseif (isset($query['import'])) { - // SQL file execution - $this->executeFile($query['import']); - } else { - throw new \Exception('Invalid command.'); - } - } - } catch (\Exception $ex) { - $this->builder->logFailure($ex->getMessage()); - return false; - } - return true; - } - - /** - * @param string $query - * @return boolean - * @throws \Exception - */ - protected function executeFile($query) - { - if (!isset($query['file'])) { - throw new \Exception('Import statement must contain a \'file\' key'); - } - - $import_file = $this->builder->buildPath . $this->builder->interpolate($query['file']); - if (!is_readable($import_file)) { - throw new \Exception(sprintf('Cannot open SQL import file: %s', $import_file)); - } - - $database = isset($query['database']) ? $this->builder->interpolate($query['database']) : null; - - $import_command = $this->getImportCommand($import_file, $database); - if (!$this->builder->executeCommand($import_command)) { - throw new \Exception('Unable to execute SQL file'); - } - - return true; - } - - /** - * Builds the MySQL import command required to import/execute the specified file - * - * @param string $import_file Path to file, relative to the build root - * @param string $database If specified, this database is selected before execution - * - * @return string - */ - protected function getImportCommand($import_file, $database = null) - { - $decompression = [ - 'bz2' => '| bzip2 --decompress', - 'gz' => '| gzip --decompress', - ]; - - $extension = strtolower(pathinfo($import_file, PATHINFO_EXTENSION)); - $decomp_cmd = ''; - if (array_key_exists($extension, $decompression)) { - $decomp_cmd = $decompression[$extension]; - } - - $args = [ - ':import_file' => escapeshellarg($import_file), - ':decomp_cmd' => $decomp_cmd, - ':host' => escapeshellarg($this->host), - ':user' => escapeshellarg($this->user), - ':pass' => (!$this->pass) ? '' : '-p' . escapeshellarg($this->pass), - ':database' => ($database === null)? '': escapeshellarg($database), - ]; - - return strtr('cat :import_file :decomp_cmd | mysql -h:host -u:user :pass :database', $args); - } -} diff --git a/src/PHPCensor/Plugin/Option/PhpUnitOptions.php b/src/PHPCensor/Plugin/Option/PhpUnitOptions.php deleted file mode 100644 index bb7cf6d..0000000 --- a/src/PHPCensor/Plugin/Option/PhpUnitOptions.php +++ /dev/null @@ -1,265 +0,0 @@ - - */ -class PhpUnitOptions -{ - protected $options; - protected $arguments = []; - - public function __construct($options) - { - $this->options = $options; - } - - /** - * Remove a command argument - * - * @param $argumentName - * - * @return $this - */ - public function removeArgument($argumentName) - { - unset($this->arguments[$argumentName]); - return $this; - } - - /** - * Combine all the argument into a string for the phpunit command - * - * @return string - */ - public function buildArgumentString() - { - $argumentString = ''; - foreach ($this->getCommandArguments() as $argumentName => $argumentValues) { - $prefix = $argumentName[0] == '-' ? '' : '--'; - - if (!is_array($argumentValues)) { - $argumentValues = [$argumentValues]; - } - - foreach ($argumentValues as $argValue) { - $postfix = ' '; - if (!empty($argValue)) { - $postfix = ' "' . $argValue . '" '; - } - $argumentString .= $prefix . $argumentName . $postfix; - } - } - - return $argumentString; - } - - /** - * Get all the command arguments - * - * @return string[] - */ - public function getCommandArguments() - { - /* - * Return the full list of arguments - */ - return $this->parseArguments()->arguments; - } - - /** - * Parse the arguments from the config options - * - * @return $this - */ - private function parseArguments() - { - if (empty($this->arguments)) { - /* - * Parse the arguments from the YML options file - */ - if (isset($this->options['args'])) { - $rawArgs = $this->options['args']; - if (is_array($rawArgs)) { - $this->arguments = $rawArgs; - } else { - /* - * Try to parse old arguments in a single string - */ - preg_match_all('@--([a-z\-]+)([\s=]+)?[\'"]?((?!--)[-\w/.,\\\]+)?[\'"]?@', (string)$rawArgs, $argsMatch); - - if (!empty($argsMatch) && sizeof($argsMatch) > 2) { - foreach ($argsMatch[1] as $index => $argName) { - $this->addArgument($argName, $argsMatch[3][$index]); - } - } - } - } - - /* - * Handles command aliases outside of the args option - */ - if (isset($this->options['coverage'])) { - $this->addArgument('coverage-html', $this->options['coverage']); - } - - /* - * Handles command aliases outside of the args option - */ - if (isset($this->options['config'])) { - $this->addArgument('configuration', $this->options['config']); - } - } - - return $this; - } - - /** - * Add an argument to the collection - * Note: adding argument before parsing the options will prevent the other options from been parsed. - * - * @param string $argumentName - * @param string $argumentValue - */ - public function addArgument($argumentName, $argumentValue) - { - if (isset($this->arguments[$argumentName])) { - if (!is_array($this->arguments[$argumentName])) { - // Convert existing argument values into an array - $this->arguments[$argumentName] = [$this->arguments[$argumentName]]; - } - - // Appends the new argument to the list - $this->arguments[$argumentName][] = $argumentValue; - } else { - // Adds new argument - $this->arguments[$argumentName] = $argumentValue; - } - } - - /** - * Get the list of directory to run phpunit in - * - * @return string[] List of directories - */ - public function getDirectories() - { - $directories = $this->getOption('directory'); - - if (is_string($directories)) { - $directories = [$directories]; - } else { - if (is_null($directories)) { - $directories = []; - } - } - - return is_array($directories) ? $directories : [$directories]; - } - - /** - * Get an option if defined - * - * @param $optionName - * - * @return string[]|string|null - */ - public function getOption($optionName) - { - if (isset($this->options[$optionName])) { - return $this->options[$optionName]; - } - - return null; - } - - /** - * Get the directory to execute the command from - * - * @return mixed|null - */ - public function getRunFrom() - { - return $this->getOption('run_from'); - } - - /** - * Ge the directory name where tests file reside - * - * @return string|null - */ - public function getTestsPath() - { - return $this->getOption('path'); - } - - /** - * Get the PHPUnit configuration from the options, or the optional path - * - * @param string $altPath - * - * @return string[] path of files - */ - public function getConfigFiles($altPath = null) - { - $configFiles = $this->getArgument('configuration'); - if (empty($configFiles) && $altPath) { - $configFile = self::findConfigFile($altPath); - if ($configFile) { - $configFiles[] = $configFile; - } - } - - return $configFiles; - } - - /** - * Get options for a given argument - * - * @param $argumentName - * - * @return string[] All the options for given argument - */ - public function getArgument($argumentName) - { - $this->parseArguments(); - - if (isset($this->arguments[$argumentName])) { - return is_array( - $this->arguments[$argumentName] - ) ? $this->arguments[$argumentName] : [$this->arguments[$argumentName]]; - } - - return []; - } - - /** - * Find a PHPUnit configuration file in a directory - * - * @param string $buildPath The path to configuration file - * - * @return null|string - */ - public static function findConfigFile($buildPath) - { - $files = [ - 'phpunit.xml', - 'phpunit.mysql.xml', - 'phpunit.pgsql.xml', - 'phpunit.xml.dist', - 'tests/phpunit.xml', - 'tests/phpunit.xml.dist', - ]; - - foreach ($files as $file) { - if (file_exists($buildPath . $file)) { - return $file; - } - } - - return null; - } -} diff --git a/src/PHPCensor/Plugin/PackageBuild.php b/src/PHPCensor/Plugin/PackageBuild.php deleted file mode 100644 index aa37cf6..0000000 --- a/src/PHPCensor/Plugin/PackageBuild.php +++ /dev/null @@ -1,86 +0,0 @@ - - */ -class PackageBuild extends Plugin -{ - protected $directory; - protected $filename; - protected $format; - - /** - * @return string - */ - public static function pluginName() - { - return 'package_build'; - } - - /** - * {@inheritdoc} - */ - public function __construct(Builder $builder, Build $build, array $options = []) - { - parent::__construct($builder, $build, $options); - - $path = $this->builder->buildPath; - $this->directory = isset($options['directory']) ? $options['directory'] : $path; - $this->filename = isset($options['filename']) ? $options['filename'] : 'build'; - $this->format = isset($options['format']) ? $options['format'] : 'zip'; - } - - /** - * Executes Composer and runs a specified command (e.g. install / update) - */ - public function execute() - { - $path = $this->builder->buildPath; - $build = $this->build; - - if ($this->directory == $path) { - return false; - } - - $filename = str_replace('%build.commit%', $build->getCommitId(), $this->filename); - $filename = str_replace('%build.id%', $build->getId(), $filename); - $filename = str_replace('%build.branch%', $build->getBranch(), $filename); - $filename = str_replace('%project.title%', $build->getProject()->getTitle(), $filename); - $filename = str_replace('%date%', date('Y-m-d'), $filename); - $filename = str_replace('%time%', date('Hi'), $filename); - $filename = preg_replace('/([^a-zA-Z0-9_-]+)/', '', $filename); - - $curdir = getcwd(); - chdir($this->builder->buildPath); - - if (!is_array($this->format)) { - $this->format = [$this->format]; - } - - foreach ($this->format as $format) { - switch ($format) { - case 'tar': - $cmd = 'tar cfz "%s/%s.tar.gz" ./*'; - break; - default: - case 'zip': - $cmd = 'zip -rq "%s/%s.zip" ./*'; - break; - } - - $success = $this->builder->executeCommand($cmd, $this->directory, $filename); - } - - chdir($curdir); - - return $success; - } -} diff --git a/src/PHPCensor/Plugin/Pdepend.php b/src/PHPCensor/Plugin/Pdepend.php deleted file mode 100644 index 1b93588..0000000 --- a/src/PHPCensor/Plugin/Pdepend.php +++ /dev/null @@ -1,132 +0,0 @@ - - */ -class Pdepend extends Plugin -{ - protected $args; - - /** - * @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; - - /** - * @return string - */ - public static function pluginName() - { - return 'pdepend'; - } - - /** - * {@inheritdoc} - */ - public function __construct(Builder $builder, Build $build, array $options = []) - { - parent::__construct($builder, $build, $options); - - $this->directory = isset($options['directory']) ? $options['directory'] : $this->builder->buildPath; - - $title = $this->builder->getBuildProjectTitle(); - $this->summary = $title . '-summary.xml'; - $this->pyramid = $title . '-pyramid.svg'; - $this->chart = $title . '-chart.svg'; - $this->location = $this->builder->buildPath . '..' . DIRECTORY_SEPARATOR . 'pdepend'; - } - - /** - * Runs Pdepend with the given criteria as arguments - */ - public function execute() - { - if (!file_exists($this->location)) { - mkdir($this->location); - } - if (!is_writable($this->location)) { - throw new \Exception(sprintf('The location %s is not writable or does not exist.', $this->location)); - } - - $pdepend = $this->findBinary('pdepend'); - - $cmd = $pdepend . ' --summary-xml="%s" --jdepend-chart="%s" --overview-pyramid="%s" %s "%s"'; - - $this->removeBuildArtifacts(); - - // If we need to ignore directories - if (count($this->builder->ignore)) { - $ignore = ' --ignore=' . implode(',', $this->builder->ignore); - } else { - $ignore = ''; - } - - $success = $this->builder->executeCommand( - $cmd, - $this->location . DIRECTORY_SEPARATOR . $this->summary, - $this->location . DIRECTORY_SEPARATOR . $this->chart, - $this->location . DIRECTORY_SEPARATOR . $this->pyramid, - $ignore, - $this->directory - ); - - $config = $this->builder->getSystemConfig('php-censor'); - - if ($success) { - $this->builder->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 - ) - ); - } - - return $success; - } - - /** - * Remove files created from previous builds - */ - protected function removeBuildArtifacts() - { - //Remove the created files first - foreach ([$this->summary, $this->chart, $this->pyramid] as $file) { - if (file_exists($this->location . DIRECTORY_SEPARATOR . $file)) { - unlink($this->location . DIRECTORY_SEPARATOR . $file); - } - } - } -} diff --git a/src/PHPCensor/Plugin/Pgsql.php b/src/PHPCensor/Plugin/Pgsql.php deleted file mode 100644 index 319d746..0000000 --- a/src/PHPCensor/Plugin/Pgsql.php +++ /dev/null @@ -1,76 +0,0 @@ - - */ -class Pgsql extends Plugin -{ - /** - * @var string - */ - protected $host; - - /** - * @var string - */ - protected $user; - - /** - * @var string - */ - protected $pass; - - /** - * @return string - */ - public static function pluginName() - { - return 'pgsql'; - } - - /** - * {@inheritdoc} - */ - public function __construct(Builder $builder, Build $build, array $options = []) - { - parent::__construct($builder, $build, $options); - - $buildSettings = $this->builder->getConfig('build_settings'); - - if (isset($buildSettings['pgsql'])) { - $sql = $buildSettings['pgsql']; - $this->host = $sql['host']; - $this->user = $sql['user']; - $this->pass = $sql['pass']; - } - } - - /** - * Connects to PgSQL and runs a specified set of queries. - * @return boolean - */ - public function execute() - { - try { - $opts = [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]; - $pdo = new PDO('pgsql:host=' . $this->host, $this->user, $this->pass, $opts); - - foreach ($this->options as $query) { - $pdo->query($this->builder->interpolate($query)); - } - } catch (\Exception $ex) { - $this->builder->logFailure($ex->getMessage()); - return false; - } - return true; - } -} diff --git a/src/PHPCensor/Plugin/Phar.php b/src/PHPCensor/Plugin/Phar.php deleted file mode 100644 index b43b95e..0000000 --- a/src/PHPCensor/Plugin/Phar.php +++ /dev/null @@ -1,212 +0,0 @@ -setDirectory($options['directory']); - } - - // Filename? - if (isset($options['filename'])) { - $this->setFilename($options['filename']); - } - - // RegExp? - if (isset($options['regexp'])) { - $this->setRegExp($options['regexp']); - } - - // Stub? - if (isset($options['stub'])) { - $this->setStub($options['stub']); - } - } - - /** - * Directory Setter - * - * @param string $directory Configuration Value - * @return Phar Fluent Interface - */ - public function setDirectory($directory) - { - $this->directory = $directory; - return $this; - } - - /** - * Directory Getter - * - * @return string Configurated or Default Value - */ - public function getDirectory() - { - if (!isset($this->directory)) { - $this->setDirectory($this->builder->buildPath); - } - return $this->directory; - } - - /** - * Filename Setter - * - * @param string $filename Configuration Value - * @return Phar Fluent Interface - */ - public function setFilename($filename) - { - $this->filename = $filename; - return $this; - } - - /** - * Filename Getter - * - * @return string Configurated or Default Value - */ - public function getFilename() - { - if (!isset($this->filename)) { - $this->setFilename('build.phar'); - } - return $this->filename; - } - - /** - * Regular Expression Setter - * - * @param string $regexp Configuration Value - * @return Phar Fluent Interface - */ - public function setRegExp($regexp) - { - $this->regexp = $regexp; - return $this; - } - - /** - * Regular Expression Getter - * - * @return string Configurated or Default Value - */ - public function getRegExp() - { - if (!isset($this->regexp)) { - $this->setRegExp('/\.php$/'); - } - return $this->regexp; - } - - /** - * Stub Filename Setter - * - * @param string $stub Configuration Value - * @return Phar Fluent Interface - */ - public function setStub($stub) - { - $this->stub = $stub; - return $this; - } - - /** - * Stub Filename Getter - * - * @return string Configurated Value - */ - public function getStub() - { - return $this->stub; - } - - /** - * Get stub content for the Phar file. - * @return string - */ - public function getStubContent() - { - $content = ''; - $filename = $this->getStub(); - if ($filename) { - $content = file_get_contents($this->builder->buildPath . DIRECTORY_SEPARATOR . $this->getStub()); - } - return $content; - } - - /** - * Run the phar plugin. - * @return bool - */ - public function execute() - { - $success = false; - - try { - $file = $this->getDirectory() . DIRECTORY_SEPARATOR . $this->getFilename(); - $phar = new PHPPhar($file, 0, $this->getFilename()); - $phar->buildFromDirectory($this->builder->buildPath, $this->getRegExp()); - - $stub = $this->getStubContent(); - if ($stub) { - $phar->setStub($stub); - } - - $success = true; - } catch (\Exception $e) { - $this->builder->log('Phar Plugin Internal Error'); - $this->builder->log($e->getMessage()); - } - - return $success; - } -} diff --git a/src/PHPCensor/Plugin/Phing.php b/src/PHPCensor/Plugin/Phing.php deleted file mode 100644 index dc12dce..0000000 --- a/src/PHPCensor/Plugin/Phing.php +++ /dev/null @@ -1,237 +0,0 @@ - - */ -class Phing extends Plugin -{ - protected $directory; - protected $buildFile = 'build.xml'; - protected $targets = ['build']; - protected $properties = []; - protected $propertyFile; - - /** - * @return string - */ - public static function pluginName() - { - return 'phing'; - } - - /** - * {@inheritdoc} - */ - public function __construct(Builder $builder, Build $build, array $options = []) - { - parent::__construct($builder, $build, $options); - - /* - * Set working directory - */ - if (isset($options['directory'])) { - $directory = $this->builder->buildPath . DIRECTORY_SEPARATOR . $options['directory']; - } else { - $directory = $this->builder->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']); - } - } - - /** - * Executes Phing and runs a specified targets - */ - public function execute() - { - $phingExecutable = $this->findBinary('phing'); - - $cmd[] = $phingExecutable . ' -f ' . $this->getBuildFilePath(); - - if ($this->getPropertyFile()) { - $cmd[] = '-propertyfile ' . $this->getPropertyFile(); - } - - $cmd[] = $this->propertiesToString(); - - $cmd[] = '-logger phing.listener.DefaultLogger'; - $cmd[] = $this->targetsToString(); - $cmd[] = '2>&1'; - - return $this->builder->executeCommand(implode(' ', $cmd), $this->directory, $this->targets); - } - - /** - * @return string - */ - public function getDirectory() - { - return $this->directory; - } - - /** - * @param string $directory - * - * @return $this - */ - public function setDirectory($directory) - { - $this->directory = $directory; - } - - /** - * @return string - */ - public function getTargets() - { - return $this->targets; - } - - /** - * Converts an array of targets into a string. - * @return string - */ - private function targetsToString() - { - return implode(' ', $this->targets); - } - - /** - * @param array|string $targets - * - * @return $this - */ - public function setTargets($targets) - { - if (is_string($targets)) { - $targets = [$targets]; - } - - $this->targets = $targets; - } - - /** - * @return string - */ - public function getBuildFile() - { - return $this->buildFile; - } - - /** - * @param mixed $buildFile - * - * @return $this - * @throws \Exception - */ - public function setBuildFile($buildFile) - { - if (!file_exists($this->getDirectory() . $buildFile)) { - throw new \Exception('Specified build file does not exist.'); - } - - $this->buildFile = $buildFile; - } - - /** - * Get phing build file path. - * @return string - */ - public function getBuildFilePath() - { - return $this->getDirectory() . $this->buildFile; - } - - /** - * @return mixed - */ - public function getProperties() - { - return $this->properties; - } - - /** - * @return string - */ - public function propertiesToString() - { - /** - * fix the problem when execute phing out of the build dir - * @ticket 748 - */ - if (!isset($this->properties['project.basedir'])) { - $this->properties['project.basedir'] = $this->getDirectory(); - } - - $propertiesString = []; - - 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 = [$properties]; - } - - $this->properties = $properties; - } - - /** - * @return string - */ - public function getPropertyFile() - { - return $this->propertyFile; - } - - /** - * @param string $propertyFile - * - * @return $this - * @throws \Exception - */ - public function setPropertyFile($propertyFile) - { - if (!file_exists($this->getDirectory() . DIRECTORY_SEPARATOR . $propertyFile)) { - throw new \Exception('Specified property file does not exist.'); - } - - $this->propertyFile = $propertyFile; - } -} diff --git a/src/PHPCensor/Plugin/PhpCodeSniffer.php b/src/PHPCensor/Plugin/PhpCodeSniffer.php deleted file mode 100644 index 6828acd..0000000 --- a/src/PHPCensor/Plugin/PhpCodeSniffer.php +++ /dev/null @@ -1,287 +0,0 @@ - - */ -class PhpCodeSniffer extends Plugin implements ZeroConfigPluginInterface -{ - /** - * @var array - */ - protected $suffixes; - - /** - * @var string - */ - protected $directory; - - /** - * @var string - */ - protected $standard; - - /** - * @var string - */ - protected $tab_width; - - /** - * @var string - */ - protected $encoding; - - /** - * @var int - */ - protected $allowed_errors; - - /** - * @var int - */ - protected $allowed_warnings; - - /** - * @var string, based on the assumption the root may not hold the code to be tested, extends the base path - */ - protected $path; - - /** - * @var array - paths to ignore - */ - protected $ignore; - - /** - * @var int - */ - protected $severity = null; - /** - * @var null|int - */ - protected $error_severity = null; - - /** - * @var null|int - */ - protected $warning_severity = null; - - /** - * @return string - */ - public static function pluginName() - { - return 'php_code_sniffer'; - } - - /** - * {@inheritdoc} - */ - public function __construct(Builder $builder, Build $build, array $options = []) - { - parent::__construct($builder, $build, $options); - - $this->suffixes = ['php']; - $this->directory = $this->builder->buildPath; - $this->standard = 'PSR2'; - $this->tab_width = ''; - $this->encoding = ''; - $this->path = ''; - $this->ignore = $this->builder->ignore; - $this->allowed_warnings = 0; - $this->allowed_errors = 0; - - if (isset($options['zero_config']) && $options['zero_config']) { - $this->allowed_warnings = -1; - $this->allowed_errors = -1; - } - - if (!empty($options['allowed_errors']) && is_int($options['allowed_errors'])) { - $this->allowed_errors = $options['allowed_errors']; - } - - if (!empty($options['allowed_warnings']) && is_int($options['allowed_warnings'])) { - $this->allowed_warnings = $options['allowed_warnings']; - } - - if (isset($options['suffixes'])) { - $this->suffixes = (array)$options['suffixes']; - } - - if (!empty($options['tab_width'])) { - $this->tab_width = ' --tab-width='.$options['tab_width']; - } - - if (!empty($options['encoding'])) { - $this->encoding = ' --encoding=' . $options['encoding']; - } - - if (!empty($options['ignore'])) { - $this->ignore = (array)$options['ignore']; - } - - if (!empty($options['standard'])) { - $this->standard = $options['standard']; - } - - if (isset($options['severity']) && is_int($options['severity'])) { - $this->severity = $options['severity']; - } - - if (isset($options['error_severity']) && is_int($options['error_severity'])) { - $this->error_severity = $options['error_severity']; - } - - if (isset($options['warning_severity']) && is_int($options['warning_severity'])) { - $this->warning_severity = $options['warning_severity']; - } - } - - /** - * Check if this plugin can be executed. - * - * @param $stage - * @param Builder $builder - * @param Build $build - * - * @return bool - */ - public static function canExecute($stage, Builder $builder, Build $build) - { - if ($stage == Build::STAGE_TEST) { - return true; - } - - return false; - } - - /** - * Runs PHP Code Sniffer in a specified directory, to a specified standard. - */ - public function execute() - { - list($ignore, $standard, $suffixes, $severity, $errorSeverity, $warningSeverity) = $this->getFlags(); - - $phpcs = $this->findBinary('phpcs'); - - $this->builder->logExecOutput(false); - - $cmd = $phpcs . ' --report=json %s %s %s %s %s "%s" %s %s %s'; - $this->builder->executeCommand( - $cmd, - $standard, - $suffixes, - $ignore, - $this->tab_width, - $this->encoding, - $this->builder->buildPath . $this->path, - $severity, - $errorSeverity, - $warningSeverity - ); - - $output = $this->builder->getLastOutput(); - list($errors, $warnings) = $this->processReport($output); - - $this->builder->logExecOutput(true); - - $success = true; - $this->build->storeMeta('phpcs-warnings', $warnings); - $this->build->storeMeta('phpcs-errors', $errors); - - if ($this->allowed_warnings != -1 && $warnings > $this->allowed_warnings) { - $success = false; - } - - if ($this->allowed_errors != -1 && $errors > $this->allowed_errors) { - $success = false; - } - - return $success; - } - - /** - * Process options and produce an arguments string for PHPCS. - * @return array - */ - protected function getFlags() - { - $ignore = ''; - if (count($this->ignore)) { - $ignore = ' --ignore=' . implode(',', $this->ignore); - } - - if (strpos($this->standard, '/') !== false) { - $standard = ' --standard=' . $this->directory.$this->standard; - } else { - $standard = ' --standard=' . $this->standard; - } - - $suffixes = ''; - if (count($this->suffixes)) { - $suffixes = ' --extensions=' . implode(',', $this->suffixes); - } - - $severity = ''; - if ($this->severity !== null) { - $severity = ' --severity=' . $this->severity; - } - - $errorSeverity = ''; - if ($this->error_severity !== null) { - $errorSeverity = ' --error-severity=' . $this->error_severity; - } - - $warningSeverity = ''; - if ($this->warning_severity !== null) { - $warningSeverity = ' --warning-severity=' . $this->warning_severity; - } - - return [$ignore, $standard, $suffixes, $severity, $errorSeverity, $warningSeverity]; - } - - /** - * Process the PHPCS output report. - * @param $output - * @return array - * @throws \Exception - */ - protected function processReport($output) - { - $data = json_decode(trim($output), true); - - if (!is_array($data)) { - $this->builder->log($output); - throw new \Exception('Could not process the report generated by PHP Code Sniffer.'); - } - - $errors = $data['totals']['errors']; - $warnings = $data['totals']['warnings']; - - foreach ($data['files'] as $fileName => $file) { - $fileName = str_replace($this->builder->buildPath, '', $fileName); - - foreach ($file['messages'] as $message) { - $this->build->reportError( - $this->builder, - 'php_code_sniffer', - 'PHPCS: ' . $message['message'], - $message['type'] == 'ERROR' ? BuildError::SEVERITY_HIGH : BuildError::SEVERITY_LOW, - $fileName, - $message['line'] - ); - } - } - - return [$errors, $warnings]; - } -} diff --git a/src/PHPCensor/Plugin/PhpCpd.php b/src/PHPCensor/Plugin/PhpCpd.php deleted file mode 100644 index 4094d16..0000000 --- a/src/PHPCensor/Plugin/PhpCpd.php +++ /dev/null @@ -1,164 +0,0 @@ - - */ -class PhpCpd extends Plugin implements ZeroConfigPluginInterface -{ - protected $directory; - protected $args; - - /** - * @var string, based on the assumption the root may not hold the code to be - * tested, extends the base path - */ - protected $path; - - /** - * @var array - paths to ignore - */ - protected $ignore; - - /** - * @return string - */ - public static function pluginName() - { - return 'php_cpd'; - } - - /** - * {@inheritdoc} - */ - public function __construct(Builder $builder, Build $build, array $options = []) - { - parent::__construct($builder, $build, $options); - - $this->path = $this->builder->buildPath; - $this->ignore = $this->builder->ignore; - - if (!empty($options['path'])) { - $this->path = $this->builder->buildPath . $options['path']; - } - - if (!empty($options['ignore'])) { - $this->ignore = $options['ignore']; - } - } - - /** - * Check if this plugin can be executed. - * - * @param $stage - * @param Builder $builder - * @param Build $build - * - * @return bool - */ - public static function canExecute($stage, Builder $builder, Build $build) - { - if ($stage == Build::STAGE_TEST) { - return true; - } - - return false; - } - - /** - * Runs PHP Copy/Paste Detector in a specified directory. - */ - public function execute() - { - $ignore = ''; - $namesExclude = ' --names-exclude '; - - foreach ($this->ignore as $item) { - $item = rtrim($item, DIRECTORY_SEPARATOR); - if (is_file(rtrim($this->path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $item)) { - $ignoredFile = explode('/', $item); - $filesToIgnore[] = array_pop($ignoredFile); - } else { - $ignore .= ' --exclude ' . $item; - } - } - - if (isset($filesToIgnore)) { - $filesToIgnore = $namesExclude . implode(',', $filesToIgnore); - $ignore = $ignore . $filesToIgnore; - } - - $phpcpd = $this->findBinary('phpcpd'); - - $tmpFileName = tempnam('/tmp', 'phpcpd'); - - $cmd = $phpcpd . ' --log-pmd "%s" %s "%s"'; - $success = $this->builder->executeCommand($cmd, $tmpFileName, $ignore, $this->path); - - $errorCount = $this->processReport(file_get_contents($tmpFileName)); - - $this->build->storeMeta('phpcpd-warnings', $errorCount); - - unlink($tmpFileName); - - return $success; - } - - /** - * Process the PHPCPD XML report. - * - * @param $xmlString - * - * @return integer - * - * @throws \Exception - */ - protected function processReport($xmlString) - { - $xml = simplexml_load_string($xmlString); - - if ($xml === false) { - $this->builder->log($xmlString); - throw new \Exception('Could not process the report generated by PHPCpd.'); - } - - $warnings = 0; - foreach ($xml->duplication as $duplication) { - foreach ($duplication->file as $file) { - $fileName = (string)$file['path']; - $fileName = str_replace($this->builder->buildPath, '', $fileName); - - $message = <<codefragment} -``` -CPD; - - $this->build->reportError( - $this->builder, - 'php_cpd', - $message, - BuildError::SEVERITY_NORMAL, - $fileName, - (int)$file['line'], - (int)$file['line'] + (int)$duplication['lines'] - ); - } - - $warnings++; - } - - return $warnings; - } -} diff --git a/src/PHPCensor/Plugin/PhpCsFixer.php b/src/PHPCensor/Plugin/PhpCsFixer.php deleted file mode 100644 index 65047cd..0000000 --- a/src/PHPCensor/Plugin/PhpCsFixer.php +++ /dev/null @@ -1,98 +0,0 @@ - - */ -class PhpCsFixer extends Plugin -{ - protected $directory = null; - protected $args = ''; - - protected $config = false; - protected $configs = [ - '.php_cs', - '.php_cs.dist', - ]; - - /** - * @return string - */ - public static function pluginName() - { - return 'php_cs_fixer'; - } - - /** - * {@inheritdoc} - */ - public function __construct(Builder $builder, Build $build, array $options = []) - { - parent::__construct($builder, $build, $options); - - if (!empty($options['args'])) { - $this->args = $options['args']; - } - - if (isset($options['verbose']) && $options['verbose']) { - $this->args .= ' --verbose'; - } - - if (isset($options['diff']) && $options['diff']) { - $this->args .= ' --diff'; - } - - if (isset($options['rules']) && $options['rules']) { - $this->args .= ' --rules=' . $options['rules']; - } - - if (isset($options['config']) && $options['config']) { - $this->config = true; - $this->args .= ' --config=' . $builder->interpolate($options['config']); - } - - if (isset($options['directory']) && $options['directory']) { - $this->directory = $builder->interpolate($options['directory']); - } - } - - /** - * Run PHP CS Fixer. - * - * @return boolean - */ - public function execute() - { - $directory = ''; - if (!empty($this->directory)) { - $directory = $this->directory; - } - - if (!$this->config) { - foreach ($this->configs as $config) { - if (file_exists($this->builder->buildPath . '/' . $config)) { - $this->config = true; - $this->args .= ' --config=./' . $config; - break; - } - } - } - - if (!$this->config && !$directory) { - $directory = '.'; - } - - $phpCsFixer = $this->findBinary('php-cs-fixer'); - $cmd = $phpCsFixer . ' fix ' . $directory . ' %s'; - $success = $this->builder->executeCommand($cmd, $this->args); - - return $success; - } -} diff --git a/src/PHPCensor/Plugin/PhpDocblockChecker.php b/src/PHPCensor/Plugin/PhpDocblockChecker.php deleted file mode 100644 index 19640ac..0000000 --- a/src/PHPCensor/Plugin/PhpDocblockChecker.php +++ /dev/null @@ -1,175 +0,0 @@ - - */ -class PhpDocblockChecker extends Plugin implements ZeroConfigPluginInterface -{ - /** - * @var string Based on the assumption the root may not hold the code to be - * tested, extends the build path. - */ - protected $path; - - /** - * @var array - paths to ignore - */ - protected $ignore; - - protected $skipClasses = false; - protected $skipMethods = false; - - /** - * @var integer - */ - protected $allowed_warnings; - - /** - * @return string - */ - public static function pluginName() - { - return 'php_docblock_checker'; - } - - /** - * {@inheritdoc} - */ - public function __construct(Builder $builder, Build $build, array $options = []) - { - parent::__construct($builder, $build, $options); - - $this->ignore = $this->builder->ignore; - $this->path = ''; - $this->allowed_warnings = 0; - - if (isset($options['zero_config']) && $options['zero_config']) { - $this->allowed_warnings = -1; - } - - if (array_key_exists('skip_classes', $options)) { - $this->skipClasses = true; - } - - if (array_key_exists('skip_methods', $options)) { - $this->skipMethods = true; - } - - if (!empty($options['path'])) { - $this->path = $options['path']; - } - - if (array_key_exists('allowed_warnings', $options)) { - $this->allowed_warnings = (int)$options['allowed_warnings']; - } - } - - /** - * Check if this plugin can be executed. - * @param $stage - * @param Builder $builder - * @param Build $build - * @return bool - */ - public static function canExecute($stage, Builder $builder, Build $build) - { - if ($stage == Build::STAGE_TEST) { - return true; - } - - return false; - } - - /** - * Runs PHP Mess Detector in a specified directory. - */ - public function execute() - { - // Check that the binary exists: - $checker = $this->findBinary('phpdoccheck'); - - // Build ignore string: - $ignore = ''; - if (count($this->ignore)) { - $ignore = ' --exclude="' . implode(',', $this->ignore) . '"'; - } - - // Are we skipping any checks? - $add = ''; - if ($this->skipClasses) { - $add .= ' --skip-classes'; - } - - if ($this->skipMethods) { - $add .= ' --skip-methods'; - } - - // Build command string: - $path = $this->builder->buildPath . $this->path; - $cmd = $checker . ' --json --directory="%s"%s%s'; - - // Disable exec output logging, as we don't want the XML report in the log: - $this->builder->logExecOutput(false); - - // Run checker: - $this->builder->executeCommand( - $cmd, - $path, - $ignore, - $add - ); - - // Re-enable exec output logging: - $this->builder->logExecOutput(true); - - $output = json_decode($this->builder->getLastOutput(), true); - $errors = count($output); - $success = true; - - $this->build->storeMeta('phpdoccheck-warnings', $errors); - $this->reportErrors($output); - - if ($this->allowed_warnings != -1 && $errors > $this->allowed_warnings) { - $success = false; - } - - return $success; - } - - /** - * Report all of the errors we've encountered line-by-line. - * @param $output - */ - protected function reportErrors($output) - { - foreach ($output as $error) { - $message = 'Class ' . $error['class'] . ' is missing a docblock.'; - $severity = BuildError::SEVERITY_LOW; - - if ($error['type'] == 'method') { - $message = $error['class'] . '::' . $error['method'] . ' is missing a docblock.'; - $severity = BuildError::SEVERITY_NORMAL; - } - - $this->build->reportError( - $this->builder, - 'php_docblock_checker', - $message, - $severity, - $error['file'], - $error['line'] - ); - } - } -} diff --git a/src/PHPCensor/Plugin/PhpLoc.php b/src/PHPCensor/Plugin/PhpLoc.php deleted file mode 100644 index 597514a..0000000 --- a/src/PHPCensor/Plugin/PhpLoc.php +++ /dev/null @@ -1,93 +0,0 @@ - - */ -class PhpLoc extends Plugin implements ZeroConfigPluginInterface -{ - /** - * @var string - */ - protected $directory; - - /** - * @return string - */ - public static function pluginName() - { - return 'php_loc'; - } - - /** - * Check if this plugin can be executed. - * @param $stage - * @param Builder $builder - * @param Build $build - * @return bool - */ - public static function canExecute($stage, Builder $builder, Build $build) - { - if ($stage == Build::STAGE_TEST) { - return true; - } - - return false; - } - - /** - * {@inheritdoc} - */ - public function __construct(Builder $builder, Build $build, array $options = []) - { - parent::__construct($builder, $build, $options); - - $this->directory = $this->builder->buildPath; - - if (isset($options['directory'])) { - $this->directory .= $options['directory']; - } - } - - /** - * Runs PHP Copy/Paste Detector in a specified directory. - */ - public function execute() - { - $ignore = ''; - - if (count($this->builder->ignore)) { - $map = function ($item) { - return ' --exclude ' . rtrim($item, DIRECTORY_SEPARATOR); - }; - - $ignore = array_map($map, $this->builder->ignore); - $ignore = implode('', $ignore); - } - - $phploc = $this->findBinary('phploc'); - - $success = $this->builder->executeCommand($phploc . ' %s "%s"', $ignore, $this->directory); - $output = $this->builder->getLastOutput(); - - if (preg_match_all('/\((LOC|CLOC|NCLOC|LLOC)\)\s+([0-9]+)/', $output, $matches)) { - $data = []; - foreach ($matches[1] as $k => $v) { - $data[$v] = (int)$matches[2][$k]; - } - - $this->build->storeMeta('phploc', $data); - } - - return $success; - } -} diff --git a/src/PHPCensor/Plugin/PhpMessDetector.php b/src/PHPCensor/Plugin/PhpMessDetector.php deleted file mode 100644 index 30134f2..0000000 --- a/src/PHPCensor/Plugin/PhpMessDetector.php +++ /dev/null @@ -1,256 +0,0 @@ - - */ -class PhpMessDetector extends Plugin implements ZeroConfigPluginInterface -{ - /** - * @var array - */ - protected $suffixes; - - /** - * @var string, based on the assumption the root may not hold the code to be - * tested, extends the base path only if the provided path is relative. Absolute - * paths are used verbatim - */ - protected $path; - - /** - * @var array - paths to ignore - */ - protected $ignore; - - /** - * Array of PHPMD rules. Can be one of the builtins (codesize, unusedcode, naming, design, controversial) - * or a filename (detected by checking for a / in it), either absolute or relative to the project root. - * @var array - */ - protected $rules; - protected $allowed_warnings; - - /** - * @return string - */ - public static function pluginName() - { - return 'php_mess_detector'; - } - - /** - * {@inheritdoc} - */ - public function __construct(Builder $builder, Build $build, array $options = []) - { - parent::__construct($builder, $build, $options); - - $this->suffixes = ['php']; - $this->ignore = $this->builder->ignore; - $this->path = ''; - $this->rules = ['codesize', 'unusedcode', 'naming']; - $this->allowed_warnings = 0; - - if (isset($options['zero_config']) && $options['zero_config']) { - $this->allowed_warnings = -1; - } - - if (!empty($options['path'])) { - $this->path = $options['path']; - } - - if (array_key_exists('allowed_warnings', $options)) { - $this->allowed_warnings = (int)$options['allowed_warnings']; - } - - foreach (['rules', 'ignore', 'suffixes'] as $key) { - $this->overrideSetting($options, $key); - } - } - - /** - * Check if this plugin can be executed. - * @param $stage - * @param Builder $builder - * @param Build $build - * @return bool - */ - public static function canExecute($stage, Builder $builder, Build $build) - { - if ($stage == Build::STAGE_TEST) { - return true; - } - - return false; - } - - /** - * Runs PHP Mess Detector in a specified directory. - */ - public function execute() - { - if (!$this->tryAndProcessRules()) { - return false; - } - - $phpmdBinaryPath = $this->findBinary('phpmd'); - - $this->executePhpMd($phpmdBinaryPath); - - $errorCount = $this->processReport(trim($this->builder->getLastOutput())); - $this->build->storeMeta('phpmd-warnings', $errorCount); - - return $this->wasLastExecSuccessful($errorCount); - } - - /** - * Override a default setting. - * @param $options - * @param $key - */ - protected function overrideSetting($options, $key) - { - if (isset($options[$key]) && is_array($options[$key])) { - $this->{$key} = $options[$key]; - } - } - - /** - * Process PHPMD's XML output report. - * - * @param $xmlString - * - * @return integer - * - * @throws \Exception - */ - protected function processReport($xmlString) - { - $xml = simplexml_load_string($xmlString); - - if ($xml === false) { - $this->builder->log($xmlString); - throw new \Exception('Could not process PHPMD report XML.'); - } - - $warnings = 0; - - foreach ($xml->file as $file) { - $fileName = (string)$file['name']; - $fileName = str_replace($this->builder->buildPath, '', $fileName); - - foreach ($file->violation as $violation) { - $warnings++; - - $this->build->reportError( - $this->builder, - 'php_mess_detector', - (string)$violation, - PHPCensor\Model\BuildError::SEVERITY_HIGH, - $fileName, - (int)$violation['beginline'], - (int)$violation['endline'] - ); - } - } - - return $warnings; - } - - /** - * Try and process the rules parameter from .php-censor.yml. - * @return bool - */ - protected function tryAndProcessRules() - { - if (!empty($this->rules) && !is_array($this->rules)) { - $this->builder->logFailure('The "rules" option must be an array.'); - return false; - } - - foreach ($this->rules as &$rule) { - if (strpos($rule, '/') !== false) { - $rule = $this->builder->buildPath . $rule; - } - } - - return true; - } - - /** - * Execute PHP Mess Detector. - * @param $binaryPath - */ - protected function executePhpMd($binaryPath) - { - $cmd = $binaryPath . ' "%s" xml %s %s %s'; - - $path = $this->getTargetPath(); - - $ignore = ''; - if (count($this->ignore)) { - $ignore = ' --exclude ' . implode(',', $this->ignore); - } - - $suffixes = ''; - if (count($this->suffixes)) { - $suffixes = ' --suffixes ' . implode(',', $this->suffixes); - } - - // Disable exec output logging, as we don't want the XML report in the log: - $this->builder->logExecOutput(false); - - // Run PHPMD: - $this->builder->executeCommand( - $cmd, - $path, - implode(',', $this->rules), - $ignore, - $suffixes - ); - - // Re-enable exec output logging: - $this->builder->logExecOutput(true); - } - - /** - * Get the path PHPMD should be run against. - * @return string - */ - protected function getTargetPath() - { - $path = $this->builder->buildPath . $this->path; - if (!empty($this->path) && $this->path{0} == '/') { - $path = $this->path; - return $path; - } - return $path; - } - - /** - * Returns a boolean indicating if the error count can be considered a success. - * - * @param int $errorCount - * @return bool - */ - protected function wasLastExecSuccessful($errorCount) - { - $success = true; - - if ($this->allowed_warnings != -1 && $errorCount > $this->allowed_warnings) { - $success = false; - return $success; - } - return $success; - } -} diff --git a/src/PHPCensor/Plugin/PhpParallelLint.php b/src/PHPCensor/Plugin/PhpParallelLint.php deleted file mode 100644 index 908e23f..0000000 --- a/src/PHPCensor/Plugin/PhpParallelLint.php +++ /dev/null @@ -1,143 +0,0 @@ - - */ -class PhpParallelLint extends Plugin implements ZeroConfigPluginInterface -{ - /** - * @var string - */ - protected $directory; - - /** - * @var array - paths to ignore - */ - protected $ignore; - - /** - * @var string - comma separated list of file extensions - */ - protected $extensions; - - /** - * @var bool - enable short tags - */ - protected $shortTag; - - /** - * @return string - */ - public static function pluginName() - { - return 'php_parallel_lint'; - } - - /** - * $options['directory'] Output Directory. Default: %BUILDPATH% - * $options['filename'] Phar Filename. Default: build.phar - * $options['extensions'] Filename extensions. Default: php - * $options['shorttags'] Enable short tags. Default: false - * $options['stub'] Stub Content. No Default Value - */ - public function __construct(Builder $builder, Build $build, array $options = []) - { - parent::__construct($builder, $build, $options); - - $this->directory = $this->builder->buildPath; - $this->ignore = $this->builder->ignore; - $this->extensions = 'php'; - $this->shortTag = false; - - if (isset($options['directory'])) { - $this->directory = $this->builder->buildPath.$options['directory']; - } - - if (isset($options['ignore'])) { - $this->ignore = $options['ignore']; - } - - if (isset($options['shorttags'])) { - $this->shortTag = (strtolower($options['shorttags']) == 'true'); - } - - if (isset($options['extensions'])) { - // Only use if this is a comma delimited list - $pattern = '/^([a-z]+)(,\ *[a-z]*)*$/'; - - if (preg_match($pattern, $options['extensions'])) { - $this->extensions = str_replace(' ', '', $options['extensions']); - } - } - } - - /** - * Check if this plugin can be executed. - * - * @param $stage - * @param Builder $builder - * @param Build $build - * - * @return bool - */ - public static function canExecute($stage, Builder $builder, Build $build) - { - if ($stage == Build::STAGE_TEST) { - return true; - } - - return false; - } - - /** - * Executes parallel lint - */ - public function execute() - { - list($ignore) = $this->getFlags(); - - $phplint = $this->findBinary('parallel-lint'); - - $cmd = $phplint . ' -e %s' . '%s %s "%s"'; - $success = $this->builder->executeCommand( - $cmd, - $this->extensions, - ($this->shortTag ? ' -s' : ''), - $ignore, - $this->directory - ); - - $output = $this->builder->getLastOutput(); - - $matches = []; - if (preg_match_all('/Parse error\:/', $output, $matches)) { - $this->build->storeMeta('phplint-errors', count($matches[0])); - } - - return $success; - } - - /** - * Produce an argument string for PHP Parallel Lint. - * @return array - */ - protected function getFlags() - { - $ignoreFlags = []; - foreach ($this->ignore as $ignoreDir) { - $ignoreFlags[] = '--exclude "' . $this->builder->buildPath . $ignoreDir . '"'; - } - $ignore = implode(' ', $ignoreFlags); - - return [$ignore]; - } -} diff --git a/src/PHPCensor/Plugin/PhpSpec.php b/src/PHPCensor/Plugin/PhpSpec.php deleted file mode 100644 index 97cafef..0000000 --- a/src/PHPCensor/Plugin/PhpSpec.php +++ /dev/null @@ -1,118 +0,0 @@ - - */ -class PhpSpec extends Plugin -{ - /** - * @return string - */ - public static function pluginName() - { - return 'php_spec'; - } - - /** - * Runs PHP Spec tests. - */ - public function execute() - { - $curdir = getcwd(); - chdir($this->builder->buildPath); - - $phpspec = $this->findBinary(['phpspec', 'phpspec.php']); - - $success = $this->builder->executeCommand($phpspec . ' --format=junit --no-code-generation run'); - $output = $this->builder->getLastOutput(); - - chdir($curdir); - - /* - * process xml output - * - * - * - * - * - * attributes(); - $data = [ - 'time' => (float)$attr['time'], - 'tests' => (int)$attr['tests'], - 'failures' => (int)$attr['failures'], - 'errors' => (int)$attr['errors'], - // now all the tests - 'suites' => [] - ]; - - /** - * @var \SimpleXMLElement $group - */ - foreach ($xml->xpath('testsuite') as $group) { - $attr = $group->attributes(); - $suite = [ - 'name' => (String)$attr['name'], - 'time' => (float)$attr['time'], - 'tests' => (int)$attr['tests'], - 'failures' => (int)$attr['failures'], - 'errors' => (int)$attr['errors'], - 'skipped' => (int)$attr['skipped'], - // now the cases - 'cases' => [] - ]; - - /** - * @var \SimpleXMLElement $child - */ - foreach ($group->xpath('testcase') as $child) { - $attr = $child->attributes(); - $case = [ - 'name' => (String)$attr['name'], - 'classname' => (String)$attr['classname'], - 'time' => (float)$attr['time'], - 'status' => (String)$attr['status'], - ]; - - if ($case['status']=='failed') { - $error = []; - /* - * ok, sad, we had an error - * - * there should be one - foreach makes this easier - */ - foreach ($child->xpath('failure') as $failure) { - $attr = $failure->attributes(); - $error['type'] = (String)$attr['type']; - $error['message'] = (String)$attr['message']; - } - - foreach ($child->xpath('system-err') as $system_err) { - $error['raw'] = (String)$system_err; - } - - $case['error'] = $error; - } - - $suite['cases'][] = $case; - } - - $data['suites'][] = $suite; - } - - $this->build->storeMeta('phpspec', $data); - - - return $success; - } -} diff --git a/src/PHPCensor/Plugin/PhpTalLint.php b/src/PHPCensor/Plugin/PhpTalLint.php deleted file mode 100644 index 636df1e..0000000 --- a/src/PHPCensor/Plugin/PhpTalLint.php +++ /dev/null @@ -1,237 +0,0 @@ - - */ -class PhpTalLint extends Plugin -{ - protected $directories; - protected $recursive = true; - protected $suffixes; - protected $ignore; - - /** - * @return string - */ - public static function pluginName() - { - return 'php_tal_lint'; - } - - /** - * @var string The path to a file contain custom phptal_tales_ functions - */ - protected $tales; - - /** - * @var int - */ - protected $allowed_warnings; - - /** - * @var int - */ - protected $allowed_errors; - - /** - * @var array The results of the lint scan - */ - protected $failedPaths = []; - - /** - * {@inheritdoc} - */ - public function __construct(Builder $builder, Build $build, array $options = []) - { - parent::__construct($builder, $build, $options); - - $this->directories = ['']; - $this->suffixes = ['zpt']; - $this->ignore = $this->builder->ignore; - - $this->allowed_warnings = 0; - $this->allowed_errors = 0; - - if (!empty($options['directory'])) { - $this->directories = [$options['directory']]; - } - - if (isset($options['suffixes'])) { - $this->suffixes = (array)$options['suffixes']; - } - } - - /** - * Executes phptal lint - */ - public function execute() - { - $this->builder->quiet = true; - $this->builder->logExecOutput(false); - - foreach ($this->directories as $dir) { - $this->lintDirectory($dir); - } - - $this->builder->quiet = false; - $this->builder->logExecOutput(true); - - $errors = 0; - $warnings = 0; - - foreach ($this->failedPaths as $path) { - if ($path['type'] == 'error') { - $errors++; - } else { - $warnings++; - } - } - - $this->build->storeMeta('phptallint-warnings', $warnings); - $this->build->storeMeta('phptallint-errors', $errors); - $this->build->storeMeta('phptallint-data', $this->failedPaths); - - $success = true; - - if ($this->allowed_warnings != -1 && $warnings > $this->allowed_warnings) { - $success = false; - } - - if ($this->allowed_errors != -1 && $errors > $this->allowed_errors) { - $success = false; - } - - return $success; - } - - /** - * Lint an item (file or directory) by calling the appropriate method. - * @param $item - * @param $itemPath - * @return bool - */ - protected function lintItem($item, $itemPath) - { - $success = true; - - if ($item->isFile() && in_array(strtolower($item->getExtension()), $this->suffixes)) { - if (!$this->lintFile($itemPath)) { - $success = false; - } - } elseif ($item->isDir() && $this->recursive && !$this->lintDirectory($itemPath . DIRECTORY_SEPARATOR)) { - $success = false; - } - - return $success; - } - - /** - * Run phptal lint against a directory of files. - * @param $path - * @return bool - */ - protected function lintDirectory($path) - { - $success = true; - $directory = new \DirectoryIterator($this->builder->buildPath . $path); - - foreach ($directory as $item) { - if ($item->isDot()) { - continue; - } - - $itemPath = $path . $item->getFilename(); - - if (in_array($itemPath, $this->ignore)) { - continue; - } - - if (!$this->lintItem($item, $itemPath)) { - $success = false; - } - } - - return $success; - } - - /** - * Run phptal lint against a specific file. - * @param $path - * @return bool - */ - protected function lintFile($path) - { - $success = true; - - list($suffixes, $tales) = $this->getFlags(); - - $lint = __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR; - $lint .= 'vendor' . DIRECTORY_SEPARATOR . 'phptal' . DIRECTORY_SEPARATOR . 'phptal' . DIRECTORY_SEPARATOR; - $lint .= 'tools' . DIRECTORY_SEPARATOR . 'phptal_lint.php'; - $cmd = '/usr/bin/env php ' . $lint . ' %s %s "%s"'; - - $this->builder->executeCommand($cmd, $suffixes, $tales, $this->builder->buildPath . $path); - - $output = $this->builder->getLastOutput(); - - if (preg_match('/Found (.+?) (error|warning)/i', $output, $matches)) { - $rows = explode(PHP_EOL, $output); - - unset($rows[0]); - unset($rows[1]); - unset($rows[2]); - unset($rows[3]); - - foreach ($rows as $row) { - $name = basename($path); - - $row = str_replace('(use -i to include your custom modifier functions)', '', $row); - $message = str_replace($name . ': ', '', $row); - - $parts = explode(' (line ', $message); - - $message = trim($parts[0]); - $line = str_replace(')', '', $parts[1]); - - $this->failedPaths[] = [ - 'file' => $path, - 'line' => $line, - 'type' => $matches[2], - 'message' => $message - ]; - } - - $success = false; - } - - return $success; - } - - /** - * Process options and produce an arguments string for PHPTAL Lint. - * @return array - */ - protected function getFlags() - { - $tales = ''; - if (!empty($this->tales)) { - $tales = ' -i ' . $this->builder->buildPath . $this->tales; - } - - $suffixes = ''; - if (count($this->suffixes)) { - $suffixes = ' -e ' . implode(',', $this->suffixes); - } - - return [$suffixes, $tales]; - } -} diff --git a/src/PHPCensor/Plugin/PhpUnit.php b/src/PHPCensor/Plugin/PhpUnit.php deleted file mode 100644 index 72e362b..0000000 --- a/src/PHPCensor/Plugin/PhpUnit.php +++ /dev/null @@ -1,177 +0,0 @@ - - * @author Pablo Tejada - */ -class PhpUnit extends Plugin implements ZeroConfigPluginInterface -{ - /** @var string[] Raw options from the config file */ - protected $options = []; - - /** - * @return string - */ - public static function pluginName() - { - return 'php_unit'; - } - - /** - * Standard Constructor - * $options['config'] Path to a PHPUnit XML configuration file. - * $options['run_from'] The directory where the phpunit command will run from when using 'config'. - * $options['coverage'] Value for the --coverage-html command line flag. - * $options['directory'] Optional directory or list of directories to run PHPUnit on. - * $options['args'] Command line args (in string format) to pass to PHP Unit - * - * @param Builder $builder - * @param Build $build - * @param string[] $options - */ - public function __construct(Builder $builder, Build $build, array $options = []) - { - parent::__construct($builder, $build, $options); - - $this->options = new PhpUnitOptions($options); - } - - /** - * Check if the plugin can be executed without any configurations - * - * @param $stage - * @param Builder $builder - * @param Build $build - * - * @return bool - */ - public static function canExecute($stage, Builder $builder, Build $build) - { - if ($stage == Build::STAGE_TEST && !is_null(PhpUnitOptions::findConfigFile($build->getBuildPath()))) { - return true; - } - - return false; - } - - /** - * Runs PHP Unit tests in a specified directory, optionally using specified config file(s). - */ - public function execute() - { - $xmlConfigFiles = $this->options->getConfigFiles($this->build->getBuildPath()); - $directories = $this->options->getDirectories(); - if (empty($xmlConfigFiles) && empty($directories)) { - $this->builder->logFailure('Neither a configuration file nor a test directory found.'); - return false; - } - - $cmd = $this->findBinary('phpunit'); - // run without logging - $ret = null; - $lastLine = exec($cmd.' --log-json . --version'); - if (false !== strpos($lastLine, '--log-json')) { - $logFormat = 'junit'; // --log-json is not supported - } else { - $logFormat = 'json'; - } - - $success = []; - - // Run any directories - if (!empty($directories)) { - foreach ($directories as $directory) { - $success[] = $this->runConfig($directory, null, $logFormat); - } - } else { - // Run any config files - if (!empty($xmlConfigFiles)) { - foreach ($xmlConfigFiles as $configFile) { - $success[] = $this->runConfig($this->options->getTestsPath(), $configFile, $logFormat); - } - } - } - - return !in_array(false, $success); - } - - /** - * Run the tests defined in a PHPUnit config file or in a specific directory. - * - * @param $directory - * @param $configFile - * @param string $logFormat - * - * @return bool|mixed - */ - protected function runConfig($directory, $configFile, $logFormat) - { - $options = clone $this->options; - $buildPath = $this->build->getBuildPath(); - - // Save the results into a log file - $logFile = @tempnam($buildPath, 'jLog_'); - $options->addArgument('log-'.$logFormat, $logFile); - - // Removes any current configurations files - $options->removeArgument('configuration'); - if (null !== $configFile) { - // Only the add the configuration file been passed - $options->addArgument('configuration', $buildPath . $configFile); - } - - $arguments = $this->builder->interpolate($options->buildArgumentString()); - $cmd = $this->findBinary('phpunit') . ' %s %s'; - $success = $this->builder->executeCommand($cmd, $arguments, $directory); - - $this->processResults($logFile, $logFormat); - - return $success; - } - - /** - * Saves the test results - * - * @param string $logFile - * @param string $logFormat - * - * @throws \Exception If failed to parse the log file - */ - protected function processResults($logFile, $logFormat) - { - if (file_exists($logFile)) { - if ('json' === $logFormat) { - $parser = new PhpUnitResultJson($logFile, $this->build->getBuildPath()); - } else { - $parser = new PhpUnitResultJunit($logFile, $this->build->getBuildPath()); - } - - $this->build->storeMeta('phpunit-data', $parser->parse()->getResults()); - $this->build->storeMeta('phpunit-errors', $parser->getFailures()); - - foreach ($parser->getErrors() as $error) { - $severity = $error['severity'] == $parser::SEVERITY_ERROR ? BuildError::SEVERITY_CRITICAL : BuildError::SEVERITY_HIGH; - $this->build->reportError( - $this->builder, 'php_unit', $error['message'], $severity, $error['file'], $error['line'] - ); - } - @unlink($logFile); - } else { - throw new \Exception('log output file does not exist: ' . $logFile); - } - } -} diff --git a/src/PHPCensor/Plugin/SecurityChecker.php b/src/PHPCensor/Plugin/SecurityChecker.php deleted file mode 100644 index 1568ab7..0000000 --- a/src/PHPCensor/Plugin/SecurityChecker.php +++ /dev/null @@ -1,98 +0,0 @@ - - */ -class SecurityChecker extends Plugin implements ZeroConfigPluginInterface -{ - /** - * @var integer - */ - protected $allowed_warnings; - - /** - * @return string - */ - public static function pluginName() - { - return 'security_checker'; - } - - /** - * {@inheritdoc} - */ - public function __construct(Builder $builder, Build $build, array $options = []) - { - parent::__construct($builder, $build, $options); - - $this->allowed_warnings = 0; - - if (isset($options['zero_config']) && $options['zero_config']) { - $this->allowed_warnings = -1; - } - - if (array_key_exists('allowed_warnings', $options)) { - $this->allowed_warnings = (int)$options['allowed_warnings']; - } - } - - /** - * Check if this plugin can be executed. - * - * @param $stage - * @param Builder $builder - * @param Build $build - * - * @return bool - */ - public static function canExecute($stage, Builder $builder, Build $build) - { - $path = $builder->buildPath . DIRECTORY_SEPARATOR . 'composer.lock'; - - if (file_exists($path) && $stage == Build::STAGE_TEST) { - return true; - } - - return false; - } - - public function execute() - { - $success = true; - $checker = new BaseSecurityChecker(); - $warnings = $checker->check($this->builder->buildPath . DIRECTORY_SEPARATOR . 'composer.lock'); - - if ($warnings) { - foreach ($warnings as $library => $warning) { - foreach ($warning['advisories'] as $advisory => $data) { - $this->build->reportError( - $this->builder, - 'security_checker', - $library . ' (' . $warning['version'] . ")\n" . $data['cve'] . ': ' . $data['title'] . "\n" . $data['link'], - BuildError::SEVERITY_CRITICAL, - '-', - '-' - ); - } - } - - if ($this->allowed_warnings != -1 && ((int)$checker->getLastVulnerabilityCount() > $this->allowed_warnings)) { - $success = false; - } - } - - return $success; - } -} diff --git a/src/PHPCensor/Plugin/Shell.php b/src/PHPCensor/Plugin/Shell.php deleted file mode 100644 index 9ae7b1b..0000000 --- a/src/PHPCensor/Plugin/Shell.php +++ /dev/null @@ -1,77 +0,0 @@ - - */ -class Shell extends Plugin -{ - /** - * @var array - */ - protected $args; - - /** - * @var string[] $commands The commands to be executed - */ - protected $commands = []; - - /** - * @return string - */ - public static function pluginName() - { - return 'shell'; - } - - /** - * {@inheritdoc} - */ - public function __construct(Builder $builder, Build $build, array $options = []) - { - parent::__construct($builder, $build, $options); - - if (isset($options['command'])) { - // Keeping this for backwards compatibility, new projects should use interpolation vars. - $options['command'] = str_replace("%buildpath%", $this->builder->buildPath, $options['command']); - $this->commands = [$options['command']]; - return; - } - - /* - * Support the new syntax: - * - * shell: - * - "cd /www" - * - "rm -f file.txt" - */ - if (is_array($options)) { - $this->commands = $options; - } - } - - /** - * Runs the shell command. - * - * @return bool - */ - public function execute() - { - foreach ($this->commands as $command) { - $command = $this->builder->interpolate($command); - - if (!$this->builder->executeCommand($command)) { - return false; - } - } - - return true; - } -} diff --git a/src/PHPCensor/Plugin/SlackNotify.php b/src/PHPCensor/Plugin/SlackNotify.php deleted file mode 100644 index a3ff12b..0000000 --- a/src/PHPCensor/Plugin/SlackNotify.php +++ /dev/null @@ -1,137 +0,0 @@ - - */ -class SlackNotify extends Plugin -{ - private $webHook; - private $room; - private $username; - private $message; - private $icon; - private $show_status; - - /** - * @return string - */ - public static function pluginName() - { - return 'slack_notify'; - } - - /** - * {@inheritdoc} - */ - public function __construct(Builder $builder, Build $build, array $options = []) - { - parent::__construct($builder, $build, $options); - - if (is_array($options) && isset($options['webhook_url'])) { - $this->webHook = trim($options['webhook_url']); - - if (isset($options['message'])) { - $this->message = $options['message']; - } else { - $this->message = '<%PROJECT_URI%|%PROJECT_TITLE%> - <%BUILD_URI%|Build #%BUILD%> has finished '; - $this->message .= 'for commit <%COMMIT_URI%|%SHORT_COMMIT% (%COMMIT_EMAIL%)> '; - $this->message .= 'on branch <%BRANCH_URI%|%BRANCH%>'; - } - - if (isset($options['room'])) { - $this->room = $options['room']; - } else { - $this->room = '#php-censor'; - } - - if (isset($options['username'])) { - $this->username = $options['username']; - } else { - $this->username = 'PHP Censor'; - } - - if (isset($options['show_status'])) { - $this->show_status = (bool) $options['show_status']; - } else { - $this->show_status = true; - } - - if (isset($options['icon'])) { - $this->icon = $options['icon']; - } - } else { - throw new \Exception('Please define the webhook_url for slack_notify plugin!'); - } - } - - /** - * Run the Slack plugin. - * @return bool - */ - public function execute() - { - $body = $this->builder->interpolate($this->message); - - $client = new Client($this->webHook); - - $message = $client->createMessage(); - - if (!empty($this->room)) { - $message->setChannel($this->room); - } - - if (!empty($this->username)) { - $message->setUsername($this->username); - } - - if (!empty($this->icon)) { - $message->setIcon($this->icon); - } - - // Include an attachment which shows the status and hide the message - if ($this->show_status) { - $successfulBuild = $this->build->isSuccessful(); - - if ($successfulBuild) { - $status = 'Success'; - $color = 'good'; - } else { - $status = 'Failed'; - $color = 'danger'; - } - - // Build up the attachment data - $attachment = new Attachment([ - 'fallback' => $body, - 'pretext' => $body, - 'color' => $color, - 'fields' => [ - new AttachmentField([ - 'title' => 'Status', - 'value' => $status, - 'short' => false - ]) - ] - ]); - - $message->attach($attachment); - - $body = ''; - } - - $message->send($body); - - return true; - } -} diff --git a/src/PHPCensor/Plugin/Sqlite.php b/src/PHPCensor/Plugin/Sqlite.php deleted file mode 100644 index 4418c7e..0000000 --- a/src/PHPCensor/Plugin/Sqlite.php +++ /dev/null @@ -1,69 +0,0 @@ - - */ -class Sqlite extends Plugin -{ - /** - * @var array - */ - protected $queries = []; - - /** - * @var string - */ - protected $path; - - /** - * @return string - */ - public static function pluginName() - { - return 'sqlite'; - } - - /** - * {@inheritdoc} - */ - public function __construct(Builder $builder, Build $build, array $options = []) - { - parent::__construct($builder, $build, $options); - - $buildSettings = $this->builder->getConfig('build_settings'); - - if (isset($buildSettings['sqlite'])) { - $sql = $buildSettings['sqlite']; - $this->path = $sql['path']; - } - } - - /** - * Connects to SQLite and runs a specified set of queries. - * @return boolean - */ - public function execute() - { - try { - $opts = [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]; - $pdo = new PDO('sqlite:' . $this->path, $opts); - - foreach ($this->queries as $query) { - $pdo->query($this->builder->interpolate($query)); - } - } catch (\Exception $ex) { - $this->builder->logFailure($ex->getMessage()); - return false; - } - return true; - } -} diff --git a/src/PHPCensor/Plugin/TechnicalDebt.php b/src/PHPCensor/Plugin/TechnicalDebt.php deleted file mode 100644 index 4715f2d..0000000 --- a/src/PHPCensor/Plugin/TechnicalDebt.php +++ /dev/null @@ -1,202 +0,0 @@ - - */ -class TechnicalDebt extends Plugin implements ZeroConfigPluginInterface -{ - /** - * @var array - */ - protected $suffixes; - - /** - * @var string - */ - protected $directory; - - /** - * @var int - */ - protected $allowed_errors; - - /** - * @var string, based on the assumption the root may not hold the code to be - * tested, extends the base path - */ - protected $path; - - /** - * @var array - paths to ignore - */ - protected $ignore; - - /** - * @var array - terms to search for - */ - protected $searches; - - /** - * @return string - */ - public static function pluginName() - { - return 'technical_debt'; - } - - /** - * {@inheritdoc} - */ - public function __construct(Builder $builder, Build $build, array $options = []) - { - parent::__construct($builder, $build, $options); - - $this->suffixes = ['php']; - $this->directory = $this->builder->buildPath; - $this->path = ''; - $this->ignore = $this->builder->ignore; - $this->allowed_errors = 0; - $this->searches = ['TODO', 'FIXME', 'TO DO', 'FIX ME']; - - if (isset($options['searches']) && is_array($options['searches'])) { - $this->searches = $options['searches']; - } - - if (isset($options['zero_config']) && $options['zero_config']) { - $this->allowed_errors = -1; - } - - $this->setOptions($options); - } - - /** - * Handle this plugin's options. - * @param $options - */ - protected function setOptions($options) - { - foreach (array('directory', 'path', 'ignore', 'allowed_errors') as $key) { - if (array_key_exists($key, $options)) { - $this->{$key} = $options[$key]; - } - } - } - - /** - * Check if this plugin can be executed. - * - * @param $stage - * @param Builder $builder - * @param Build $build - * @return bool - */ - public static function canExecute($stage, Builder $builder, Build $build) - { - if ($stage == Build::STAGE_TEST) { - return true; - } - - return false; - } - - /** - * Runs the plugin - */ - public function execute() - { - $success = true; - $this->builder->logExecOutput(false); - - $errorCount = $this->getErrorList(); - - $this->builder->log("Found $errorCount instances of " . implode(', ', $this->searches)); - - $this->build->storeMeta('technical_debt-warnings', $errorCount); - - if ($this->allowed_errors !== -1 && $errorCount > $this->allowed_errors) { - $success = false; - } - - return $success; - } - - /** - * Gets the number and list of errors returned from the search - * - * @return integer - */ - protected function getErrorList() - { - $dirIterator = new \RecursiveDirectoryIterator($this->directory); - $iterator = new \RecursiveIteratorIterator($dirIterator, \RecursiveIteratorIterator::SELF_FIRST); - $files = []; - - $ignores = $this->ignore; - $ignores[] = '.php-censor.yml'; - $ignores[] = 'phpci.yml'; - $ignores[] = '.phpci.yml'; - - foreach ($iterator as $file) { - $filePath = $file->getRealPath(); - $skipFile = false; - foreach ($ignores as $ignore) { - if (stripos($filePath, $ignore) !== false) { - $skipFile = true; - break; - } - } - - // Ignore hidden files, else .git, .sass_cache, etc. all get looped over - if (stripos($filePath, DIRECTORY_SEPARATOR . '.') !== false) { - $skipFile = true; - } - - if ($skipFile === false) { - $files[] = $file->getRealPath(); - } - } - - $files = array_filter(array_unique($files)); - $errorCount = 0; - - foreach ($files as $file) { - $handle = fopen($file, "r"); - $lineNumber = 1; - while (false === feof($handle)) { - $line = fgets($handle); - - foreach ($this->searches as $search) { - if ($technicalDeptLine = trim(strstr($line, $search))) { - $fileName = str_replace($this->directory, '', $file); - - $this->build->reportError( - $this->builder, - 'technical_debt', - $technicalDeptLine, - PHPCensor\Model\BuildError::SEVERITY_LOW, - $fileName, - $lineNumber - ); - - $errorCount++; - } - } - - $lineNumber++; - } - fclose ($handle); - } - - return $errorCount; - } -} diff --git a/src/PHPCensor/Plugin/Util/Executor.php b/src/PHPCensor/Plugin/Util/Executor.php deleted file mode 100644 index c36bf7f..0000000 --- a/src/PHPCensor/Plugin/Util/Executor.php +++ /dev/null @@ -1,288 +0,0 @@ -pluginFactory = $pluginFactory; - $this->logger = $logger; - $this->store = $store ?: StoreFactory::getStore('Build'); - } - - /** - * Execute a the appropriate set of plugins for a given build stage. - * - * @param array $config Configuration - * @param string $stage - * - * @return bool - */ - public function executePlugins($config, $stage) - { - $success = true; - $pluginsToExecute = []; - - // If we have global plugins to execute for this stage, add them to the list to be executed: - if (array_key_exists($stage, $config) && is_array($config[$stage])) { - $pluginsToExecute[] = $config[$stage]; - } - - $pluginsToExecute = $this->getBranchSpecificPlugins($config, $stage, $pluginsToExecute); - - foreach ($pluginsToExecute as $pluginSet) { - if (!$this->doExecutePlugins($pluginSet, $stage)) { - $success = false; - } - } - - return $success; - } - - /** - * @param array $config - * @param string $branch - * - * @return bool|array - */ - public function getBranchSpecificConfig($config, $branch) - { - $configSections = array_keys($config); - - foreach ($configSections as $configSection) { - if (0 === strpos($configSection, 'branch-')) { - if ($configSection === ('branch-' . $branch)) { - return $config[$configSection]; - } - - if (0 === strpos($configSection, 'branch-regex:')) { - $pattern = '#' . substr($configSection, 13) . '#u'; - preg_match($pattern, $branch, $matches); - if (!empty($matches[0])) { - return $config[$configSection]; - } - } - } - } - - return []; - } - - /** - * Check the config for any plugins specific to the branch we're currently building. - * - * @param array $config - * @param string $stage - * @param array $pluginsToExecute - * - * @return array - */ - protected function getBranchSpecificPlugins($config, $stage, $pluginsToExecute) - { - /** @var \PHPCensor\Model\Build $build */ - $build = $this->pluginFactory->getResourceFor('PHPCensor\Model\Build'); - $branch = $build->getBranch(); - $branchConfig = $this->getBranchSpecificConfig($config, $branch); - if (!$branchConfig) { - return $pluginsToExecute; - } - - $plugins = !empty($branchConfig[$stage]) ? $branchConfig[$stage] : []; - - $runOption = 'after'; - if (!empty($branchConfig['run-option'])) { - $runOption = $branchConfig['run-option']; - } - - switch ($runOption) { - // Replace standard plugin set for this stage with just the branch-specific ones: - case 'replace': - $pluginsToExecute = []; - $pluginsToExecute[] = $plugins; - break; - - // Run branch-specific plugins before standard plugins: - case 'before': - array_unshift($pluginsToExecute, $plugins); - break; - - // Run branch-specific plugins after standard plugins: - case 'after': - array_push($pluginsToExecute, $plugins); - break; - - default: - array_push($pluginsToExecute, $plugins); - break; - } - - return $pluginsToExecute; - } - - /** - * Execute the list of plugins found for a given testing stage. - * @param $plugins - * @param $stage - * @return bool - * @throws \Exception - */ - protected function doExecutePlugins(&$plugins, $stage) - { - $success = true; - - foreach ($plugins as $plugin => $options) { - $this->logger->log("\n" . - sprintf('RUNNING PLUGIN: %s', Lang::get($plugin)) . ' (' . - 'Stage' . ': ' . ucfirst($stage) . ')' - ); - - $this->setPluginStatus($stage, $plugin, Plugin::STATUS_RUNNING); - - // Try and execute it - if ($this->executePlugin($plugin, $options)) { - // Execution was successful - $this->logger->logSuccess('PLUGIN: SUCCESS'); - $this->setPluginStatus($stage, $plugin, Plugin::STATUS_SUCCESS); - } else { - $status = Plugin::STATUS_FAILED; - - if ($stage === Build::STAGE_SETUP) { - $this->logger->logFailure('PLUGIN: FAILED'); - // If we're in the "setup" stage, execution should not continue after - // a plugin has failed: - throw new Exception('Plugin failed: ' . $plugin); - } elseif ($stage === Build::STAGE_DEPLOY) { - $this->logger->logFailure('PLUGIN: FAILED'); - $success = false; - } else { - // If we're in the "test" stage and the plugin is not allowed to fail, - // then mark the build as failed: - if (empty($options['allow_failures']) && $stage === Build::STAGE_TEST) { - $this->logger->logFailure('PLUGIN: FAILED'); - $success = false; - } else { - $status = Plugin::STATUS_FAILED_ALLOWED; - - $this->logger->logFailure('PLUGIN: FAILED (ALLOWED)'); - } - } - - $this->setPluginStatus($stage, $plugin, $status); - } - } - - return $success; - } - - /** - * Executes a given plugin, with options and returns the result. - */ - public function executePlugin($plugin, $options) - { - $class = $plugin; - if (!class_exists($class)) { - $class = str_replace('_', ' ', $plugin); - $class = ucwords($class); - $class = 'PHPCensor\\Plugin\\' . str_replace(' ', '', $class); - - if (!class_exists($class)) { - $this->logger->logFailure(sprintf('Plugin does not exist: %s', $plugin)); - - return false; - } - } - - try { - // Build and run it - $obj = $this->pluginFactory->buildPlugin($class, (is_null($options) ? [] : $options)); - - return $obj->execute(); - } catch (\Exception $ex) { - $this->logger->logFailure('Exception: ' . $ex->getMessage(), $ex); - - return false; - } - } - - /** - * Change the status of a plugin for a given stage. - * - * @param string $stage The builder stage. - * @param string $plugin The plugin name. - * @param int $status The new status. - */ - protected function setPluginStatus($stage, $plugin, $status) - { - $summary = $this->getBuildSummary(); - - if (!isset($summary[$stage][$plugin])) { - $summary[$stage][$plugin] = []; - } - - $summary[$stage][$plugin]['status'] = $status; - - if ($status === Plugin::STATUS_RUNNING) { - $summary[$stage][$plugin]['started'] = time(); - } elseif ($status >= Plugin::STATUS_SUCCESS) { - $summary[$stage][$plugin]['ended'] = time(); - } - - $this->setBuildSummary($summary); - } - - /** - * Fetch the summary data of the current build. - * - * @return array - */ - private function getBuildSummary() - { - /** @var Build $build */ - $build = $this->pluginFactory->getResourceFor('PHPCensor\Model\Build'); - $metas = $this->store->getMeta('plugin-summary', $build->getProjectId(), $build->getId()); - return isset($metas[0]['meta_value']) ? $metas[0]['meta_value'] : []; - } - - /** - * Sets the summary data of the current build. - * - * @param array $summary - */ - private function setBuildSummary($summary) - { - /** @var Build $build */ - $build = $this->pluginFactory->getResourceFor('PHPCensor\Model\Build'); - $this->store->setMeta($build->getId(), 'plugin-summary', json_encode($summary)); - } -} diff --git a/src/PHPCensor/Plugin/Util/Factory.php b/src/PHPCensor/Plugin/Util/Factory.php deleted file mode 100644 index ea55b1f..0000000 --- a/src/PHPCensor/Plugin/Util/Factory.php +++ /dev/null @@ -1,214 +0,0 @@ -container = $container; - } else { - $this->container = new Container(); - } - } - - /** - * Trys to get a function from the file path specified. If the - * file returns a function then $this will be passed to it. - * This enables the config file to call any public methods. - * - * @param $configPath - * @return bool - true if the function exists else false. - */ - public function addConfigFromFile($configPath) - { - // The file is expected to return a function which can - // act on the pluginFactory to register any resources needed. - if (file_exists($configPath)) { - $configFunction = require($configPath); - if (is_callable($configFunction)) { - $configFunction($this); - return true; - } - } - return false; - } - - /** - * Get most recently used factory options. - * @return mixed - */ - public function getLastOptions() - { - return $this->currentPluginOptions; - } - - /** - * Builds an instance of plugin of class $className. $options will - * be passed along with any resources registered with the factory. - * - * @param $className - * @param array|null $options - * - * @throws \InvalidArgumentException if $className doesn't represent a valid plugin - * - * @return \PHPCensor\Plugin - */ - public function buildPlugin($className, $options = []) - { - $this->currentPluginOptions = $options; - - $reflectedPlugin = new \ReflectionClass($className); - - $constructor = $reflectedPlugin->getConstructor(); - - if ($constructor) { - $argsToUse = []; - foreach ($constructor->getParameters() as $param) { - if ('options' === $param->getName()) { - $argsToUse[] = $options; - } else { - $argsToUse = $this->addArgFromParam($argsToUse, $param); - } - } - /** @var Plugin $plugin */ - $plugin = $reflectedPlugin->newInstanceArgs($argsToUse); - } else { - /** @var Plugin $plugin */ - $plugin = $reflectedPlugin->newInstance(); - } - - return $plugin; - } - - /** - * @param callable $loader - * @param string|null $name - * @param string|null $type - * @throws \InvalidArgumentException - * @internal param mixed $resource - */ - public function registerResource( - $loader, - $name = null, - $type = null - ) { - if ($name === null && $type === null) { - throw new \InvalidArgumentException( - "Type or Name must be specified" - ); - } - - if (!($loader instanceof \Closure)) { - throw new \InvalidArgumentException( - '$loader is expected to be a function' - ); - } - - $resourceID = $this->getInternalID($type, $name); - - $this->container[$resourceID] = $loader; - } - - /** - * Get an internal resource ID. - * @param null $type - * @param null $name - * @return string - */ - private function getInternalID($type = null, $name = null) - { - $type = $type ? : ""; - $name = $name ? : ""; - return $type . "-" . $name; - } - - /** - * @param string $type - * @param string $name - * @return mixed - */ - public function getResourceFor($type = null, $name = null) - { - $fullId = $this->getInternalID($type, $name); - if (isset($this->container[$fullId])) { - return $this->container[$fullId]; - } - - $typeOnlyID = $this->getInternalID($type, null); - if (isset($this->container[$typeOnlyID])) { - return $this->container[$typeOnlyID]; - } - - $nameOnlyID = $this->getInternalID(null, $name); - if (isset($this->container[$nameOnlyID])) { - return $this->container[$nameOnlyID]; - } - - return null; - } - - /** - * @param \ReflectionParameter $param - * @return null|string - */ - private function getParamType(\ReflectionParameter $param) - { - $class = $param->getClass(); - if ($class) { - return $class->getName(); - } elseif ($param->isArray()) { - return self::TYPE_ARRAY; - } elseif (is_callable($param)) { - return self::TYPE_CALLABLE; - } else { - return null; - } - } - - /** - * @param $existingArgs - * @param \ReflectionParameter $param - * @return array - * @throws \DomainException - */ - private function addArgFromParam($existingArgs, \ReflectionParameter $param) - { - $name = $param->getName(); - $type = $this->getParamType($param); - $arg = $this->getResourceFor($type, $name); - - if ($arg !== null) { - $existingArgs[] = $arg; - } elseif ($arg === null && $param->isOptional()) { - $existingArgs[] = $param->getDefaultValue(); - } else { - throw new \DomainException( - "Unsatisfied dependency: " . $param->getName() - ); - } - - return $existingArgs; - } -} diff --git a/src/PHPCensor/Plugin/Util/PhpUnitResult.php b/src/PHPCensor/Plugin/Util/PhpUnitResult.php deleted file mode 100644 index d412cda..0000000 --- a/src/PHPCensor/Plugin/Util/PhpUnitResult.php +++ /dev/null @@ -1,110 +0,0 @@ - - */ -abstract class PhpUnitResult -{ - const SEVERITY_PASS = 'success'; - const SEVERITY_FAIL = 'fail'; - const SEVERITY_ERROR = 'error'; - const SEVERITY_SKIPPED = 'skipped'; - const SEVERITY_WARN = self::SEVERITY_PASS; - const SEVERITY_RISKY = self::SEVERITY_PASS; - - protected $outputFile; - protected $buildPath; - protected $results; - protected $failures = 0; - protected $errors = []; - - public function __construct($outputFile, $buildPath = '') - { - $this->outputFile = $outputFile; - $this->buildPath = $buildPath; - } - - /** - * Parse the results - * - * @return $this - * @throws \Exception If fails to parse the output - */ - abstract public function parse(); - - abstract protected function getSeverity($testcase); - - abstract protected function buildMessage($testcase); - - abstract protected function buildTrace($testcase); - - protected function getFileAndLine($testcase) - { - return $testcase; - } - - protected function getOutput($testcase) - { - return $testcase['output']; - } - - protected function parseTestcase($testcase) - { - $severity = $this->getSeverity($testcase); - $pass = isset(array_fill_keys([self::SEVERITY_PASS, self::SEVERITY_SKIPPED], true)[$severity]); - $data = [ - 'pass' => $pass, - 'severity' => $severity, - 'message' => $this->buildMessage($testcase), - 'trace' => $pass ? [] : $this->buildTrace($testcase), - 'output' => $this->getOutput($testcase), - ]; - - if (!$pass) { - $this->failures++; - $info = $this->getFileAndLine($testcase); - $this->errors[] = [ - 'message' => $data['message'], - 'severity' => $severity, - 'file' => $info['file'], - 'line' => $info['line'], - ]; - } - - $this->results[] = $data; - } - - /** - * Get the parse results - * - * @return string[] - */ - public function getResults() - { - return $this->results; - } - - /** - * Get the total number of failing tests - * - * @return int - */ - public function getFailures() - { - return $this->failures; - } - - /** - * Get the tests with failing status - * - * @return string[] - */ - public function getErrors() - { - return $this->errors; - } -} diff --git a/src/PHPCensor/Plugin/Util/PhpUnitResultJson.php b/src/PHPCensor/Plugin/Util/PhpUnitResultJson.php deleted file mode 100644 index c99704c..0000000 --- a/src/PHPCensor/Plugin/Util/PhpUnitResultJson.php +++ /dev/null @@ -1,145 +0,0 @@ - - */ -class PhpUnitResultJson extends PhpUnitResult -{ - const EVENT_TEST = 'test'; - const EVENT_TEST_START = 'testStart'; - const EVENT_SUITE_START = 'suiteStart'; - - protected $options; - protected $arguments = []; - - /** - * Parse the results - * - * @return $this - * @throws \Exception If fails to parse the output - */ - public function parse() - { - $rawResults = file_get_contents($this->outputFile); - - $events = []; - if ($rawResults && $rawResults[0] == '{') { - $fixedJson = '[' . str_replace('}{', '},{', $rawResults) . ']'; - $events = json_decode($fixedJson, true); - } elseif ($rawResults) { - $events = json_decode($rawResults, true); - } - - // Reset the parsing variables - $this->results = []; - $this->errors = []; - $this->failures = 0; - - if ($events) { - foreach ($events as $event) { - if (isset($event['event']) && $event['event'] == self::EVENT_TEST) { - $this->parseTestcase($event); - } - } - } - - return $this; - } - - - /** - * Build the severity of the event - * - * @param $event - * - * @return string The severity flags - * @throws \Exception - */ - protected function getSeverity($event) - { - $status = $event['status']; - switch ($status) { - case 'fail': - $severity = self::SEVERITY_FAIL; - break; - case 'error': - if (strpos($event['message'], 'Skipped') === 0 || strpos($event['message'], 'Incomplete') === 0) { - $severity = self::SEVERITY_SKIPPED; - } else { - $severity = self::SEVERITY_ERROR; - } - break; - case 'pass': - $severity = self::SEVERITY_PASS; - break; - case 'warning': - $severity = self::SEVERITY_PASS; - break; - default: - throw new \Exception("Unexpected PHPUnit test status: {$status}"); - break; - } - - return $severity; - } - - /** - * Build the message string for an event - * - * @param array $event - * - * @return string - */ - protected function buildMessage($event) - { - $message = $event['test']; - - if ($event['message']) { - $message .= PHP_EOL . $event ['message']; - } - - return $message; - } - - /** - * Build a string base trace of the failure - * - * @param array $event - * - * @return string[] - */ - protected function buildTrace($event) - { - $formattedTrace = []; - - if (!empty($event['trace'])) { - foreach ($event['trace'] as $step){ - $line = str_replace($this->buildPath, '', $step['file']) . ':' . $step['line']; - $formattedTrace[] = $line; - } - } - - return $formattedTrace; - } - - /** - * Saves additional info for a failing test - * - * @param array $data - * @param array $event - */ - protected function getFileAndLine($event) - { - $firstTrace = end($event['trace']); - reset($event['trace']); - - return [ - 'file' => str_replace($this->buildPath, '', $firstTrace['file']), - 'line' => $firstTrace['line'] - ]; - } -} diff --git a/src/PHPCensor/Plugin/Util/PhpUnitResultJunit.php b/src/PHPCensor/Plugin/Util/PhpUnitResultJunit.php deleted file mode 100644 index 3f41cb8..0000000 --- a/src/PHPCensor/Plugin/Util/PhpUnitResultJunit.php +++ /dev/null @@ -1,130 +0,0 @@ - - */ -class PhpUnitResultJunit extends PhpUnitResult -{ - /** - * Parse the results - * - * @return $this - * @throws \Exception If fails to parse the output - */ - public function parse() - { - $suites = simplexml_load_file($this->outputFile); - - // Reset the parsing variables - $this->results = []; - $this->errors = []; - $this->failures = 0; - - foreach ($suites->xpath('//testcase') as $testCase) { - $this->parseTestcase($testCase); - } - $suites['failures']; - $suites['errors']; - - return $this; - } - - protected function getSeverity($testCase) - { - $severity = self::SEVERITY_PASS; - foreach($testCase as $child) { - switch ($child->getName()) { - case 'failure': - $severity = self::SEVERITY_FAIL; - break 2; - case 'error': - if ('PHPUnit\Framework\RiskyTestError' == $child['type']) { // == because convertion to string is desired - $severity = self::SEVERITY_RISKY; - } else { - $severity = self::SEVERITY_ERROR; - } - break 2; - case 'skipped': - // skipped and ignored, can not distinguish - $severity = self::SEVERITY_SKIPPED; - break 2; - case 'warning': - $severity = self::SEVERITY_WARN; - break 2; - case 'system-out': - case 'system-err': - // not results - continue; - default: - $severity = 'UNKNOWN RESULT TYPE: '.$child->getName(); - break 2; - } - } - - return $severity; - } - - protected function buildMessage($testCase) - { - $tracePos = -1; - $msg = $this->getMessageTrace($testCase); - if ('' !== $msg) { - //strip trace - $trPos = strrpos($msg, "\n\n"); - if (false !== $trPos) { - $tracePos = $trPos; - $msg = substr($msg, 0, $trPos); - } - } - if ('' === $msg) { - $msg = $testCase['class'].'::'.$testCase['name']; - }; - $testCase['_tracePos'] = $tracePos; // will be converted to string - - return $msg; - } - - protected function getOutput($testCase) { - return (string)$testCase->{'system-out'}; - } - - protected function buildTrace($testCase) - { - if (!is_int($testCase['_tracePos'])) { - $this->buildMessage($testCase); - } - - if ($testCase['_tracePos'] >= 0) { - $stackStr = substr($this->getMessageTrace($testCase), (int)$testCase['_tracePos'] + 2, -1); - $trace = explode("\n", str_replace($this->buildPath, '.', $stackStr)); - } else { - $trace = array(); - } - - return $trace; - } - - private function getMessageTrace($testCase) { - $msg = ''; - foreach($testCase as $child) { - switch ($child->getName()) { - case 'system-out': - case 'system-err': - // not results - continue; - default: - $msg = (string)$child['message']; // according to xsd - if ('' === $msg) { - $msg = (string)$child; - } - break 2; - } - } - - return $msg; - } -} diff --git a/src/PHPCensor/Plugin/Util/TestResultParsers/Codeception.php b/src/PHPCensor/Plugin/Util/TestResultParsers/Codeception.php deleted file mode 100644 index 6c2594a..0000000 --- a/src/PHPCensor/Plugin/Util/TestResultParsers/Codeception.php +++ /dev/null @@ -1,110 +0,0 @@ - - */ -class Codeception implements ParserInterface -{ - protected $builder; - protected $resultsXml; - protected $results; - protected $totalTests; - protected $totalTimeTaken; - protected $totalFailures; - protected $totalErrors; - - /** - * @param Builder $builder - * @param $resultsXml - */ - public function __construct(Builder $builder, $resultsXml) - { - $this->builder = $builder; - $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 = []; - $this->results = new \SimpleXMLElement($this->resultsXml); - - // calculate total results - foreach ($this->results->testsuite as $test_suite) { - $this->totalTests += (int)$test_suite['tests']; - $this->totalTimeTaken += (float)$test_suite['time']; - $this->totalFailures += (int)$test_suite['failures']; - $this->totalErrors += (int)$test_suite['errors']; - - foreach ($test_suite->testcase as $test_case) { - $test_result = [ - 'suite' => (string)$test_suite['name'], - 'file' => str_replace($this->builder->buildPath, '/', (string) $test_case['file']), - 'name' => (string)$test_case['name'], - 'feature' => (string)$test_case['feature'], - 'assertions' => (int)$test_case['assertions'], - 'time' => (float)$test_case['time'] - ]; - - if (isset($test_case['class'])) { - $test_result['class'] = (string) $test_case['class']; - } - - // PHPUnit testcases does not have feature field. Use class::method instead - if (!$test_result['feature']) { - $test_result['feature'] = sprintf('%s::%s', $test_result['class'], $test_result['name']); - } - - if (isset($test_case->failure) || isset($test_case->error)) { - $test_result['pass'] = false; - $test_result['message'] = isset($test_case->failure) ? (string)$test_case->failure : (string)$test_case->error; - } else { - $test_result['pass'] = true; - } - - $rtn[] = $test_result; - } - } - - 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 + $this->totalErrors; - } -} diff --git a/src/PHPCensor/Plugin/Util/TestResultParsers/ParserInterface.php b/src/PHPCensor/Plugin/Util/TestResultParsers/ParserInterface.php deleted file mode 100644 index ccd2d1e..0000000 --- a/src/PHPCensor/Plugin/Util/TestResultParsers/ParserInterface.php +++ /dev/null @@ -1,15 +0,0 @@ - - */ -class Wipe extends Plugin -{ - protected $directory; - - /** - * @return string - */ - public static function pluginName() - { - return 'wipe'; - } - - /** - * {@inheritdoc} - */ - public function __construct(Builder $builder, Build $build, array $options = []) - { - parent::__construct($builder, $build, $options); - - $path = $this->builder->buildPath; - $this->directory = isset($options['directory']) ? $this->builder->interpolate($options['directory']) : $path; - } - - /** - * Wipes a directory's contents - */ - public function execute() - { - $build = $this->builder->buildPath; - - if ($this->directory == $build || empty($this->directory)) { - return true; - } - if (is_dir($this->directory)) { - $cmd = 'rm -Rf "%s"'; - - return $this->builder->executeCommand($cmd, $this->directory); - } - - return true; - } -} diff --git a/src/PHPCensor/Plugin/Xmpp.php b/src/PHPCensor/Plugin/Xmpp.php deleted file mode 100644 index 9316367..0000000 --- a/src/PHPCensor/Plugin/Xmpp.php +++ /dev/null @@ -1,196 +0,0 @@ - - */ -class XMPP extends Plugin -{ - protected $directory; - - /** - * @var string, username of sender account xmpp - */ - protected $username; - - /** - * @var string, alias server of sender account xmpp - */ - protected $server; - - /** - * @var string, password of sender account xmpp - */ - protected $password; - - /** - * @var string, alias for sender - */ - protected $alias; - - /** - * @var string, use tls - */ - protected $tls; - - /** - * @var array, list of recipients xmpp accounts - */ - protected $recipients; - - /** - * @var string, mask to format date - */ - protected $date_format; - - /** - * @return string - */ - public static function pluginName() - { - return 'xmpp'; - } - - /** - * {@inheritdoc} - */ - public function __construct(Builder $builder, Build $build, array $options = []) - { - parent::__construct($builder, $build, $options); - - $this->username = ''; - $this->password = ''; - $this->server = ''; - $this->alias = ''; - $this->recipients = []; - $this->tls = false; - $this->date_format = '%c'; - - /* - * Set recipients list - */ - if (!empty($options['recipients'])) { - if (is_string($options['recipients'])) { - $this->recipients = [$options['recipients']]; - } elseif (is_array($options['recipients'])) { - $this->recipients = $options['recipients']; - } - } - } - - /** - * Get config format for sendxmpp config file - * - * @return string - */ - protected function getConfigFormat() - { - $conf = $this->username; - if (!empty($this->server)) { - $conf .= ';'.$this->server; - } - - $conf .= ' '.$this->password; - - if (!empty($this->alias)) { - $conf .= ' '.$this->alias; - } - - return $conf; - } - - /** - * Find config file for sendxmpp binary (default is .sendxmpprc) - */ - public function findConfigFile() - { - if (file_exists($this->builder->buildPath . DIRECTORY_SEPARATOR . '.sendxmpprc')) { - if (md5(file_get_contents($this->builder->buildPath . DIRECTORY_SEPARATOR . '.sendxmpprc')) - !== md5($this->getConfigFormat())) { - return null; - } - - return true; - } - - return null; - } - - /** - * Send notification message. - */ - public function execute() - { - $sendxmpp = $this->findBinary('sendxmpp'); - - /* - * Without recipients we can't send notification - */ - if (count($this->recipients) == 0) { - return false; - } - - /* - * Try to build conf file - */ - $config_file = $this->builder->buildPath . DIRECTORY_SEPARATOR . '.sendxmpprc'; - if (is_null($this->findConfigFile())) { - file_put_contents($config_file, $this->getConfigFormat()); - chmod($config_file, 0600); - } - - /* - * Enabled ssl for connection - */ - $tls = ''; - if ($this->tls) { - $tls = ' -t'; - } - - $message_file = $this->builder->buildPath . DIRECTORY_SEPARATOR . uniqid('xmppmessage'); - if ($this->buildMessage($message_file) === false) { - return false; - } - - /* - * Send XMPP notification for all recipients - */ - $cmd = $sendxmpp . "%s -f %s -m %s %s"; - $recipients = implode(' ', $this->recipients); - - $success = $this->builder->executeCommand($cmd, $tls, $config_file, $message_file, $recipients); - - print $this->builder->getLastOutput(); - - /* - * Remove temp message file - */ - $this->builder->executeCommand("rm -rf ".$message_file); - - return $success; - } - - /** - * @param $message_file - * @return int - */ - protected function buildMessage($message_file) - { - if ($this->build->isSuccessful()) { - $message = "✔ [".$this->build->getProjectTitle()."] Build #" . $this->build->getId()." successful"; - } else { - $message = "✘ [".$this->build->getProjectTitle()."] Build #" . $this->build->getId()." failure"; - } - - $message .= ' ('.strftime($this->date_format).')'; - - return file_put_contents($message_file, $message); - } -} diff --git a/src/PHPCensor/ProcessControl/Factory.php b/src/PHPCensor/ProcessControl/Factory.php deleted file mode 100644 index b301e82..0000000 --- a/src/PHPCensor/ProcessControl/Factory.php +++ /dev/null @@ -1,52 +0,0 @@ - - */ -class Factory -{ - /** - * ProcessControl singleton. - * - * @var ProcessControlInterface - */ - protected static $instance = null; - - /** - * Returns the ProcessControl singleton. - * - * @return ProcessControlInterface - */ - public static function getInstance() - { - if (static::$instance === null) { - static::$instance = static::createProcessControl(); - } - return static::$instance; - } - - /** - * Create a ProcessControl depending on available extensions and the underlying OS. - * - * Check PosixProcessControl, WindowsProcessControl and UnixProcessControl, in that order. - * - * @return ProcessControlInterface - * - * @throws \Exception - */ - public static function createProcessControl() - { - switch (true) { - case PosixProcessControl::isAvailable(): - return new PosixProcessControl(); - case UnixProcessControl::isAvailable(): - return new UnixProcessControl(); - } - - throw new \Exception("No ProcessControl implementation available."); - } -} diff --git a/src/PHPCensor/ProcessControl/PosixProcessControl.php b/src/PHPCensor/ProcessControl/PosixProcessControl.php deleted file mode 100644 index de99307..0000000 --- a/src/PHPCensor/ProcessControl/PosixProcessControl.php +++ /dev/null @@ -1,42 +0,0 @@ - - */ -class PosixProcessControl implements ProcessControlInterface -{ - /** - * @param integer $pid - * - * @return bool - */ - public function isRunning($pid) - { - // Signal "0" is not sent to the process, but posix_kill checks the process anyway; - return posix_kill($pid, 0); - } - - /** - * {@inheritdoc} - */ - public function kill($pid, $forcefully = false) - { - return posix_kill($pid, $forcefully ? 9 : 15); - } - - /** - * Check whether this posix_kill is available. - * - * @return bool - * - * @internal - */ - public static function isAvailable() - { - return function_exists('posix_kill'); - } -} diff --git a/src/PHPCensor/ProcessControl/ProcessControlInterface.php b/src/PHPCensor/ProcessControl/ProcessControlInterface.php deleted file mode 100644 index 1e58767..0000000 --- a/src/PHPCensor/ProcessControl/ProcessControlInterface.php +++ /dev/null @@ -1,30 +0,0 @@ - - */ -interface ProcessControlInterface -{ - /** - * Checks if a process exists. - * - * @param int $pid The process identifier. - * - * @return boolean true is the process is running, else false. - */ - public function isRunning($pid); - - /** - * Terminate a running process. - * - * @param int $pid The process identifier. - * @param bool $forcefully Whether to gently (false) or forcefully (true) terminate the process. - * - * @return boolean - */ - public function kill($pid, $forcefully = false); -} diff --git a/src/PHPCensor/ProcessControl/UnixProcessControl.php b/src/PHPCensor/ProcessControl/UnixProcessControl.php deleted file mode 100644 index c65f860..0000000 --- a/src/PHPCensor/ProcessControl/UnixProcessControl.php +++ /dev/null @@ -1,50 +0,0 @@ - - */ -class UnixProcessControl implements ProcessControlInterface -{ - /** - * Check process using the "ps" command. - * - * @param int $pid - * - * @return boolean - */ - public function isRunning($pid) - { - $output = $exitCode = null; - exec(sprintf("ps %d", $pid), $output, $exitCode); - return $exitCode === 0; - } - - /** - * {@inheritdoc} - */ - public function kill($pid, $forcefully = false) - { - $output = []; - $result = 1; - - exec(sprintf("kill -%d %d", $forcefully ? 9 : 15, $pid), $output, $result); - - return !$result; - } - - /** - * Check whether the commands "ps" and "kill" are available. - * - * @return bool - * - * @internal - */ - public static function isAvailable() - { - return DIRECTORY_SEPARATOR === '/' && exec("which ps") && exec("which kill"); - } -} diff --git a/src/PHPCensor/Security/Authentication/LoginPasswordProviderInterface.php b/src/PHPCensor/Security/Authentication/LoginPasswordProviderInterface.php deleted file mode 100644 index 24e98cd..0000000 --- a/src/PHPCensor/Security/Authentication/LoginPasswordProviderInterface.php +++ /dev/null @@ -1,23 +0,0 @@ - - */ -interface LoginPasswordProviderInterface extends UserProviderInterface -{ - /** - * Verify if the supplied password matches the user's one. - * - * @param User $user - * @param string $password - * - * @return boolean - */ - public function verifyPassword(User $user, $password); -} diff --git a/src/PHPCensor/Security/Authentication/Service.php b/src/PHPCensor/Security/Authentication/Service.php deleted file mode 100644 index 7b5aa0e..0000000 --- a/src/PHPCensor/Security/Authentication/Service.php +++ /dev/null @@ -1,106 +0,0 @@ - - */ -class Service -{ - /** - * @var Service - */ - static private $instance; - - /** - * Return the service singleton. - * - * @return Service - */ - public static function getInstance() - { - if (self::$instance === null) { - $config = Config::getInstance()->get( - 'php-censor.security.auth_providers', - [ - 'internal' => [ - 'type' => 'internal' - ] - ] - ); - - $providers = []; - foreach ($config as $key => $providerConfig) { - $providers[$key] = self::buildProvider($key, $providerConfig); - } - self::$instance = new self($providers); - } - - return self::$instance; - } - - /** - * Create a provider from a given configuration. - * - * @param string $key - * @param string|array $config - * - * @return UserProviderInterface - */ - public static function buildProvider($key, $config) - { - $class = ucfirst($config['type']); - if (class_exists('\\PHPCensor\\Security\\Authentication\\UserProvider\\' . $class)) { - $class = '\\PHPCensor\\Security\\Authentication\\UserProvider\\' . $class; - } - - return new $class($key, $config); - } - - /** - * The table of providers. - * - * @var array - */ - private $providers; - - /** - * Initialize the service. - * - * @param array $providers - */ - public function __construct(array $providers) - { - $this->providers = $providers; - } - - /** - * Return all providers. - * - * @return UserProviderInterface[] - */ - public function getProviders() - { - return $this->providers; - } - - /** - * Return the user providers that allows password authentication. - * - * @return LoginPasswordProviderInterface[] - */ - public function getLoginPasswordProviders() - { - $providers = []; - foreach ($this->providers as $key => $provider) { - if ($provider instanceof LoginPasswordProviderInterface) { - $providers[$key] = $provider; - } - } - return $providers; - } -} diff --git a/src/PHPCensor/Security/Authentication/UserProvider/AbstractProvider.php b/src/PHPCensor/Security/Authentication/UserProvider/AbstractProvider.php deleted file mode 100644 index 3aabe3b..0000000 --- a/src/PHPCensor/Security/Authentication/UserProvider/AbstractProvider.php +++ /dev/null @@ -1,35 +0,0 @@ - - */ -abstract class AbstractProvider implements UserProviderInterface -{ - /** - * @var string - */ - protected $key; - - /** - * @var array - */ - protected $config; - - /** - * AbstractProvider constructor - * - * @param string $key - * @param array $config - */ - public function __construct($key, array $config) - { - $this->key = $key; - $this->config = $config; - } -} diff --git a/src/PHPCensor/Security/Authentication/UserProvider/Internal.php b/src/PHPCensor/Security/Authentication/UserProvider/Internal.php deleted file mode 100644 index 5c05c1e..0000000 --- a/src/PHPCensor/Security/Authentication/UserProvider/Internal.php +++ /dev/null @@ -1,40 +0,0 @@ - - */ -class Internal extends AbstractProvider implements LoginPasswordProviderInterface -{ - /** - * @param User $user - * @param string $password - * - * @return boolean - */ - public function verifyPassword(User $user, $password) - { - return password_verify($password, $user->getHash()); - } - - public function checkRequirements() - { - // Always fine - } - - /** - * @param string $identifier - * - * @return null - */ - public function provisionUser($identifier) - { - return null; - } -} diff --git a/src/PHPCensor/Security/Authentication/UserProvider/Ldap.php b/src/PHPCensor/Security/Authentication/UserProvider/Ldap.php deleted file mode 100644 index 6fa3470..0000000 --- a/src/PHPCensor/Security/Authentication/UserProvider/Ldap.php +++ /dev/null @@ -1,84 +0,0 @@ -config['data'])) { - $ldapData = $this->config['data']; - $ldapPort = !empty($ldapData['port']) ? $ldapData['port'] : null; - $ldapHost = !empty($ldapData['host']) ? $ldapData['host'] : 'localhost'; - $ldapBaseDn = !empty($ldapData['base_dn']) ? $ldapData['base_dn'] : 'dc=nodomain'; - $ldapMail = !empty($ldapData['mail_attribute']) ? $ldapData['mail_attribute'] : 'mail'; - - if ($ldapPort) { - $ldap = @ldap_connect($ldapHost, $ldapPort); - } else { - $ldap = @ldap_connect($ldapHost); - } - - if (false === $ldap) { - return false; - } - - ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, 3); - - $ls = @ldap_search($ldap, $ldapBaseDn, $ldapMail . '=' . $user->getEmail()); - if (false === $ls) { - return false; - } - - $le = @ldap_get_entries($ldap, $ls); - if (!$le['count']) { - return false; - } - - $dn = $le[0]['dn']; - - return @ldap_bind($ldap, $dn, $password); - } - - return false; - } - - public function checkRequirements() - { - // Always fine - } - - /** - * @param string $identifier - * - * @return User - */ - public function provisionUser($identifier) - { - /** @var UserStore $user */ - $user = Factory::getStore('User'); - $userService = new UserService($user); - - $parts = explode("@", $identifier); - $username = $parts[0]; - - return $userService->createUser($username, $identifier, $this->key, json_encode($this->config), '', false); - } -} diff --git a/src/PHPCensor/Security/Authentication/UserProviderInterface.php b/src/PHPCensor/Security/Authentication/UserProviderInterface.php deleted file mode 100644 index 148d6c6..0000000 --- a/src/PHPCensor/Security/Authentication/UserProviderInterface.php +++ /dev/null @@ -1,30 +0,0 @@ - - */ -interface UserProviderInterface -{ - - /** - * Check if all software requirements are met (libraries, extensions, ...) - * - * @throws \Exception - */ - public function checkRequirements(); - - /** - * Provision an new user for the given identifier. - * - * @param string $identifier The user identifier. - * - * @return User|null The new user or null if the provider does not know the user. - */ - public function provisionUser($identifier); -} diff --git a/src/PHPCensor/Service/BuildService.php b/src/PHPCensor/Service/BuildService.php deleted file mode 100644 index 6ebffa4..0000000 --- a/src/PHPCensor/Service/BuildService.php +++ /dev/null @@ -1,193 +0,0 @@ -buildStore = $buildStore; - } - - /** - * @param Project $project - * @param string $environment - * @param string $commitId - * @param string|null $branch - * @param string|null $tag - * @param string|null $committerEmail - * @param string|null $commitMessage - * @param integer $source - * @param integer $userId - * @param string|null $extra - * - * @return \PHPCensor\Model\Build - */ - public function createBuild( - Project $project, - $environment, - $commitId = '', - $branch = null, - $tag = null, - $committerEmail = null, - $commitMessage = null, - $source = Build::SOURCE_UNKNOWN, - $userId = 0, - $extra = null - ) { - $build = new Build(); - $build->setCreateDate(new \DateTime()); - $build->setProject($project); - $build->setStatus(Build::STATUS_PENDING); - $build->setEnvironment($environment); - - $branches = $project->getBranchesByEnvironment($environment); - $build->setExtraValue('branches', $branches); - - $build->setSource($source); - $build->setUserId($userId); - $build->setCommitId((string)$commitId); - - if (!empty($branch)) { - $build->setBranch($branch); - } else { - $build->setBranch($project->getBranch()); - } - - if (!empty($tag)) { - $build->setTag($tag); - } - - if (!empty($committerEmail)) { - $build->setCommitterEmail($committerEmail); - } - - if (!empty($commitMessage)) { - $build->setCommitMessage($commitMessage); - } - - if (!is_null($extra)) { - $build->setExtraValues($extra); - } - - /** @var Build $build */ - $build = $this->buildStore->save($build); - $buildId = $build->getId(); - - if (!empty($buildId)) { - $build = BuildFactory::getBuild($build); - $build->sendStatusPostback(); - $this->addBuildToQueue($build); - } - - return $build; - } - - /** - * @param Build $copyFrom - * - * @return \PHPCensor\Model\Build - */ - public function createDuplicateBuild(Build $copyFrom) - { - $data = $copyFrom->getDataArray(); - - // Clean up unwanted properties from the original build: - unset($data['id']); - unset($data['status']); - unset($data['log']); - unset($data['start_date']); - unset($data['finish_date']); - - $build = new Build(); - $build->setValues($data); - $build->setCreateDate(new \DateTime()); - $build->setStatus(Build::STATUS_PENDING); - - /** @var Build $build */ - $build = $this->buildStore->save($build); - - $buildId = $build->getId(); - - if (!empty($buildId)) { - $build = BuildFactory::getBuild($build); - $build->sendStatusPostback(); - $this->addBuildToQueue($build); - } - - return $build; - } - - /** - * Delete a given build. - * - * @param Build $build - * - * @return boolean - */ - public function deleteBuild(Build $build) - { - $build->removeBuildDirectory(); - return $this->buildStore->delete($build); - } - - /** - * Takes a build and puts it into the queue to be run (if using a queue) - * @param Build $build - */ - public function addBuildToQueue(Build $build) - { - $buildId = $build->getId(); - - if (empty($buildId)) { - return; - } - - $config = Config::getInstance(); - $settings = $config->get('php-censor.queue', []); - - if (!empty($settings['host']) && !empty($settings['name'])) { - try { - $jobData = [ - 'type' => 'php-censor.build', - 'build_id' => $build->getId(), - ]; - - $pheanstalk = new Pheanstalk($settings['host']); - $pheanstalk->useTube($settings['name']); - $pheanstalk->put( - json_encode($jobData), - PheanstalkInterface::DEFAULT_PRIORITY, - PheanstalkInterface::DEFAULT_DELAY, - $config->get('php-censor.queue.lifetime', 600) - ); - } catch (\Exception $ex) { - $this->queueError = true; - } - } - } -} diff --git a/src/PHPCensor/Service/BuildStatusService.php b/src/PHPCensor/Service/BuildStatusService.php deleted file mode 100644 index 7bd3a49..0000000 --- a/src/PHPCensor/Service/BuildStatusService.php +++ /dev/null @@ -1,228 +0,0 @@ -project = $project; - $this->branch = $branch; - $this->build = $build; - if ($this->build) { - $this->loadParentBuild($isParent); - } - if (defined('APP_URL')) { - $this->setUrl(APP_URL); - } - } - - /** - * @param string $url - */ - public function setUrl($url) - { - $this->url = $url; - } - - /** - * @return Build - */ - public function getBuild() - { - return $this->build; - } - - /** - * @param boolean $isParent - * - * @throws \Exception - */ - protected function loadParentBuild($isParent = true) - { - if ($isParent === false && !$this->isFinished()) { - $lastFinishedBuild = $this->project->getLatestBuild($this->branch, $this->finishedStatusIds); - - if ($lastFinishedBuild) { - $this->prevService = new BuildStatusService( - $this->branch, - $this->project, - $lastFinishedBuild, - true - ); - } - } - } - - /** - * @return string - */ - public function getActivity() - { - if (in_array($this->build->getStatus(), $this->finishedStatusIds)) { - return 'Sleeping'; - } elseif ($this->build->getStatus() == Build::STATUS_PENDING) { - return 'Pending'; - } elseif ($this->build->getStatus() == Build::STATUS_RUNNING) { - return 'Building'; - } - return 'Unknown'; - } - - /** - * @return string - */ - public function getName() - { - return $this->project->getTitle() . ' / ' . $this->branch; - } - - /** - * @return boolean - */ - public function isFinished() - { - if (in_array($this->build->getStatus(), $this->finishedStatusIds)) { - return true; - } - return false; - } - - /** - * @return null|Build - */ - public function getFinishedBuildInfo() - { - if ($this->isFinished()) { - return $this->build; - } elseif ($this->prevService) { - return $this->prevService->getBuild(); - } - return null; - } - - /** - * @return int|string - */ - public function getLastBuildLabel() - { - if ($buildInfo = $this->getFinishedBuildInfo()) { - return $buildInfo->getId(); - } - return ''; - } - - /** - * @return string - */ - public function getLastBuildTime() - { - $dateFormat = 'Y-m-d\\TH:i:sO'; - if ($buildInfo = $this->getFinishedBuildInfo()) { - return ($buildInfo->getFinishDate()) ? $buildInfo->getFinishDate()->format($dateFormat) : ''; - } - return ''; - } - - /** - * @param Build $build - * - * @return string - */ - public function getBuildStatus(Build $build) - { - switch ($build->getStatus()) { - case Build::STATUS_SUCCESS: - return 'Success'; - case Build::STATUS_FAILED: - return 'Failure'; - } - return 'Unknown'; - } - - /** - * @return string - */ - public function getLastBuildStatus() - { - if ($build = $this->getFinishedBuildInfo()) { - return $this->getBuildStatus($build); - } - return ''; - } - - /** - * @return string - */ - public function getBuildUrl() - { - return $this->url . 'build/view/' . $this->build->getId(); - } - - /** - * @return array - */ - public function toArray() - { - if (!$this->build) { - return []; - } - return [ - 'name' => $this->getName(), - 'activity' => $this->getActivity(), - 'lastBuildLabel' => $this->getLastBuildLabel(), - 'lastBuildStatus' => $this->getLastBuildStatus(), - 'lastBuildTime' => $this->getLastBuildTime(), - 'webUrl' => $this->getBuildUrl(), - ]; - } -} diff --git a/src/PHPCensor/Service/ProjectService.php b/src/PHPCensor/Service/ProjectService.php deleted file mode 100644 index 5d54a07..0000000 --- a/src/PHPCensor/Service/ProjectService.php +++ /dev/null @@ -1,152 +0,0 @@ -projectStore = $projectStore; - } - - /** - * Create a new project model and use the project store to save it. - * - * @param string $title - * @param string $type - * @param string $reference - * @param integer $userId - * @param array $options - * - * @return \PHPCensor\Model\Project - */ - public function createProject($title, $type, $reference, $userId, $options = []) - { - // Create base project and use updateProject() to set its properties: - $project = new Project(); - $project->setCreateDate(new \DateTime()); - $project->setUserId((integer)$userId); - - return $this->updateProject($project, $title, $type, $reference, $options); - } - - /** - * Update the properties of a given project. - * - * @param Project $project - * @param string $title - * @param string $type - * @param string $reference - * @param array $options - * - * @return \PHPCensor\Model\Project - */ - public function updateProject(Project $project, $title, $type, $reference, $options = []) - { - // Set basic properties: - $project->setTitle($title); - $project->setType($type); - $project->setReference($reference); - $project->setAllowPublicStatus(0); - $project->setDefaultBranchOnly(0); - - // Handle extra project options: - if (array_key_exists('ssh_private_key', $options)) { - $project->setSshPrivateKey($options['ssh_private_key']); - } - - if (array_key_exists('ssh_public_key', $options)) { - $project->setSshPublicKey($options['ssh_public_key']); - } - - if (array_key_exists('build_config', $options)) { - $project->setBuildConfig($options['build_config']); - } - - if (array_key_exists('allow_public_status', $options)) { - $project->setAllowPublicStatus((int)$options['allow_public_status']); - } - - if (array_key_exists('archived', $options)) { - $project->setArchived((bool)$options['archived']); - } - - if (array_key_exists('branch', $options)) { - $project->setBranch($options['branch']); - } - - if (array_key_exists('default_branch_only', $options)) { - $project->setDefaultBranchOnly((int)$options['default_branch_only']); - } - - if (array_key_exists('group', $options)) { - $project->setGroup($options['group']); - } - - // Allow certain project types to set access information: - $this->processAccessInformation($project); - - // Save and return the project: - /** @var Project $project */ - $project = $this->projectStore->save($project); - - if (array_key_exists('environments', $options)) { - $project->setEnvironments($options['environments']); - } - - return $project; - } - - /** - * Delete a given project. - * - * @param Project $project - * - * @return bool - */ - public function deleteProject(Project $project) - { - return $this->projectStore->delete($project); - } - - /** - * In circumstances where it is necessary, populate access information based on other project properties. - * - * @see ProjectService::createProject() - * - * @param Project $project - */ - protected function processAccessInformation(Project &$project) - { - $matches = []; - $reference = $project->getReference(); - - if ($project->getType() == 'gitlab') { - $info = []; - - if (preg_match('`^(.+)@(.+):([0-9]*)\/?(.+)\.git`', $reference, $matches)) { - $info['user'] = $matches[1]; - $info['domain'] = $matches[2]; - $info['port'] = $matches[3]; - - $project->setReference($matches[4]); - } - - $project->setAccessInformation($info); - } - } -} diff --git a/src/PHPCensor/Service/UserService.php b/src/PHPCensor/Service/UserService.php deleted file mode 100644 index c3abd18..0000000 --- a/src/PHPCensor/Service/UserService.php +++ /dev/null @@ -1,94 +0,0 @@ -store = $store; - } - - /** - * Create a new user. - * - * @param string $name - * @param string $email - * @param string $providerKey - * @param string $providerData - * @param string $password - * @param boolean $isAdmin - * - * @return User - */ - public function createUser($name, $email, $providerKey, $providerData, $password, $isAdmin = false) - { - $user = new User(); - $user->setName($name); - $user->setEmail($email); - $user->setHash(password_hash($password, PASSWORD_DEFAULT)); - $user->setProviderKey($providerKey); - $user->setProviderData($providerData); - $user->setIsAdmin(($isAdmin ? 1 : 0)); - - return $this->store->save($user); - } - - /** - * Update a user. - * - * @param User $user - * @param string $name - * @param string $emailAddress - * @param string $password - * @param boolean $isAdmin - * @param string $language - * @param integer $perPage - * - * @return User - */ - public function updateUser(User $user, $name, $emailAddress, $password = null, $isAdmin = null, $language = null, $perPage = null) - { - $user->setName($name); - $user->setEmail($emailAddress); - - if (!empty($password)) { - $user->setHash(password_hash($password, PASSWORD_DEFAULT)); - } - - if (!is_null($isAdmin)) { - $user->setIsAdmin(($isAdmin ? 1 : 0)); - } - - $user->setLanguage($language); - $user->setPerPage($perPage); - - return $this->store->save($user); - } - - /** - * Delete a user. - * - * @param User $user - * - * @return bool - */ - public function deleteUser(User $user) - { - return $this->store->delete($user); - } -} diff --git a/src/PHPCensor/Store.php b/src/PHPCensor/Store.php deleted file mode 100644 index 5a9ce1d..0000000 --- a/src/PHPCensor/Store.php +++ /dev/null @@ -1,7 +0,0 @@ -getById($key, $useConnection); - } - - /** - * Get a single BuildError by Id. - * - * @param integer $id - * @param string $useConnection - * - * @return null|BuildError - * - * @throws HttpException - */ - public function getById($id, $useConnection = 'read') - { - if (is_null($id)) { - throw new HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); - } - - $query = 'SELECT * FROM {{build_error}} WHERE {{id}} = :id LIMIT 1'; - $stmt = Database::getConnection($useConnection)->prepareCommon($query); - $stmt->bindValue(':id', $id); - - if ($stmt->execute()) { - if ($data = $stmt->fetch(\PDO::FETCH_ASSOC)) { - return new BuildError($data); - } - } - - return null; - } - - /** - * Get multiple BuildError by BuildId. - * - * @param integer $buildId - * @param integer $limit - * @param integer $offset - * @param string $plugin - * @param integer $severity - * - * @return array - * - * @throws HttpException - */ - public function getByBuildId($buildId, $limit = null, $offset = 0, $plugin = null, $severity = null) - { - if (is_null($buildId)) { - throw new HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); - } - - $query = 'SELECT * FROM {{build_error}} WHERE {{build_id}} = :build_id'; - if ($plugin) { - $query .= ' AND {{plugin}} = :plugin'; - } - if (null !== $severity) { - $query .= ' AND {{severity}} = :severity'; - } - $query .= ' ORDER BY plugin, severity'; - if (null !== $limit) { - $query .= ' LIMIT :limit'; - } - if ($offset) { - $query .= ' OFFSET :offset'; - } - $stmt = Database::getConnection()->prepareCommon($query); - $stmt->bindValue(':build_id', $buildId); - if ($plugin) { - $stmt->bindValue(':plugin', $plugin, \PDO::PARAM_STR); - } - if (null !== $severity) { - $stmt->bindValue(':severity', (integer)$severity, \PDO::PARAM_INT); - } - if (null !== $limit) { - $stmt->bindValue(':limit', (integer)$limit, \PDO::PARAM_INT); - } - if ($offset) { - $stmt->bindValue(':offset', (integer)$offset, \PDO::PARAM_INT); - } - - if ($stmt->execute()) { - $res = $stmt->fetchAll(\PDO::FETCH_ASSOC); - - $map = function ($item) { - return new BuildError($item); - }; - $rtn = array_map($map, $res); - - $count = count($rtn); - - return ['items' => $rtn, 'count' => $count]; - } else { - return ['items' => [], 'count' => 0]; - } - } - - /** - * Gets the total number of errors for a given build. - * - * @param integer $buildId - * @param string $plugin - * @param integer $severity - * - * @return integer - */ - public function getErrorTotalForBuild($buildId, $plugin = null, $severity = null) - { - $query = 'SELECT COUNT(*) AS {{total}} FROM {{build_error}} WHERE {{build_id}} = :build'; - if ($plugin) { - $query .= ' AND {{plugin}} = :plugin'; - } - if (null !== $severity) { - $query .= ' AND {{severity}} = :severity'; - } - - $stmt = Database::getConnection('read')->prepareCommon($query); - - $stmt->bindValue(':build', $buildId, \PDO::PARAM_INT); - if ($plugin) { - $stmt->bindValue(':plugin', $plugin, \PDO::PARAM_STR); - } - if (null !== $severity) { - $stmt->bindValue(':severity', (integer)$severity, \PDO::PARAM_INT); - } - - if ($stmt->execute()) { - $res = $stmt->fetch(\PDO::FETCH_ASSOC); - - return (integer)$res['total']; - } else { - return 0; - } - } - - /** - * @param integer $buildId - * - * @return array - */ - public function getKnownPlugins($buildId) - { - $query = 'SELECT DISTINCT {{plugin}} from {{build_error}} WHERE {{build_id}} = :build'; - $stmt = Database::getConnection('read')->prepareCommon($query); - $stmt->bindValue(':build', $buildId); - - if ($stmt->execute()) { - $res = $stmt->fetchAll(\PDO::FETCH_ASSOC); - - $map = function ($item) { - return $item['plugin']; - }; - $rtn = array_map($map, $res); - - return $rtn; - } else { - return []; - } - } - - /** - * @param integer $buildId - * @param string $plugin - * - * @return array - */ - public function getKnownSeverities($buildId, $plugin = '') - { - $query = 'SELECT DISTINCT {{severity}} from {{build_error}} WHERE {{build_id}} = :build'; - if ($plugin) { - $query .= ' AND {{plugin}} = :plugin'; - } - - $stmt = Database::getConnection('read')->prepareCommon($query); - $stmt->bindValue(':build', $buildId); - if ($plugin) { - $stmt->bindValue(':plugin', $plugin); - } - - if ($stmt->execute()) { - $res = $stmt->fetchAll(\PDO::FETCH_ASSOC); - - $map = function ($item) { - return (integer)$item['severity']; - }; - $rtn = array_map($map, $res); - - return $rtn; - } else { - return []; - } - } -} diff --git a/src/PHPCensor/Store/BuildErrorWriter.php b/src/PHPCensor/Store/BuildErrorWriter.php deleted file mode 100644 index 4e71b3c..0000000 --- a/src/PHPCensor/Store/BuildErrorWriter.php +++ /dev/null @@ -1,135 +0,0 @@ -bufferSize = (integer)Config::getInstance()->get('php-censor.build.writer_buffer_size', 500); - $this->buildId = $buildId; - } - - /** - * Destructor - */ - public function __destruct() - { - $this->flush(); - } - - /** - * Write error - * - * @param string $plugin - * @param string $message - * @param integer $severity - * @param string $file - * @param integer $lineStart - * @param integer $lineEnd - * @param \DateTime $createdDate - */ - public function write( - $plugin, - $message, - $severity, - $file = null, - $lineStart = null, - $lineEnd = null, - $createdDate = null - ) { - if (is_null($createdDate)) { - $createdDate = new \DateTime(); - } - $this->errors[] = [ - 'plugin' => (string)$plugin, - 'message' => (string)$message, - 'severity' => (int)$severity, - 'file' => !is_null($file) ? (string)$file : null, - 'line_start' => !is_null($lineStart) ? (int)$lineStart : null, - 'line_end' => !is_null($lineEnd) ? (int)$lineEnd : null, - 'create_date' => $createdDate->format('Y-m-d H:i:s'), - ]; - - if (count($this->errors) >= $this->bufferSize) { - $this->flush(); - } - } - - /** - * Flush buffer - */ - public function flush() - { - if (empty($this->errors)) { - return; - } - - $insertValuesPlaceholders = []; - $insertValuesData = []; - foreach ($this->errors as $i => $error) { - $insertValuesPlaceholders[] = '( - :build_id' . $i . ', - :plugin' . $i . ', - :file' . $i . ', - :line_start' . $i . ', - :line_end' . $i . ', - :severity' . $i . ', - :message' . $i . ', - :create_date' . $i . ' - )'; - $insertValuesData['build_id' . $i] = $this->buildId; - $insertValuesData['plugin' . $i] = $error['plugin']; - $insertValuesData['file' . $i] = $error['file']; - $insertValuesData['line_start' . $i] = $error['line_start']; - $insertValuesData['line_end' . $i] = $error['line_end']; - $insertValuesData['severity' . $i] = $error['severity']; - $insertValuesData['message' . $i] = $error['message']; - $insertValuesData['create_date' . $i] = $error['create_date']; - } - $query = ' - INSERT INTO {{build_error}} ( - {{build_id}}, - {{plugin}}, - {{file}}, - {{line_start}}, - {{line_end}}, - {{severity}}, - {{message}}, - {{create_date}} - ) - VALUES ' . join(', ', $insertValuesPlaceholders) . ' - '; - $stmt = Database::getConnection('write')->prepareCommon($query); - $stmt->execute($insertValuesData); - $this->errors = []; - } -} diff --git a/src/PHPCensor/Store/BuildMetaStore.php b/src/PHPCensor/Store/BuildMetaStore.php deleted file mode 100644 index 0852988..0000000 --- a/src/PHPCensor/Store/BuildMetaStore.php +++ /dev/null @@ -1,169 +0,0 @@ -getById($key, $useConnection); - } - - /** - * Get a single BuildMeta by Id. - * - * @param integer $id - * @param string $useConnection - * - * @return null|BuildMeta - * - * @throws HttpException - */ - public function getById($id, $useConnection = 'read') - { - if (is_null($id)) { - throw new HttpException('id passed to ' . __FUNCTION__ . ' cannot be null.'); - } - - $query = 'SELECT * FROM {{build_meta}} WHERE {{id}} = :id LIMIT 1'; - $stmt = Database::getConnection($useConnection)->prepareCommon($query); - $stmt->bindValue(':id', $id); - - if ($stmt->execute()) { - if ($data = $stmt->fetch(\PDO::FETCH_ASSOC)) { - return new BuildMeta($data); - } - } - - return null; - } - - /** - * @param integer $buildId - * @param string $key - * - * @return null|BuildMeta - * - * @throws HttpException - */ - public function getByKey($buildId, $key) - { - if (is_null($buildId)) { - throw new HttpException('buildId passed to ' . __FUNCTION__ . ' cannot be null.'); - } - - if (!$key) { - throw new HttpException('key passed to ' . __FUNCTION__ . ' cannot be empty.'); - } - - $query = 'SELECT * FROM {{build_meta}} WHERE {{build_id}} = :build_id AND {{meta_key}} = :meta_key LIMIT 1'; - $stmt = Database::getConnection()->prepareCommon($query); - $stmt->bindValue(':build_id', $buildId); - $stmt->bindValue(':meta_key', $key); - - if ($stmt->execute()) { - if ($data = $stmt->fetch(\PDO::FETCH_ASSOC)) { - return new BuildMeta($data); - } - } - - return null; - } - - /** - * Get multiple BuildMeta by BuildId. - * - * @param integer $buildId - * @param integer $limit - * @param string $useConnection - * - * @return array - * - * @throws HttpException - */ - public function getByBuildId($buildId, $limit = 1000, $useConnection = 'read') - { - if (is_null($buildId)) { - throw new HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); - } - - $query = 'SELECT * FROM {{build_meta}} WHERE {{build_id}} = :build_id LIMIT :limit'; - $stmt = Database::getConnection($useConnection)->prepareCommon($query); - $stmt->bindValue(':build_id', $buildId); - $stmt->bindValue(':limit', (int)$limit, \PDO::PARAM_INT); - - if ($stmt->execute()) { - $res = $stmt->fetchAll(\PDO::FETCH_ASSOC); - - $map = function ($item) { - return new BuildMeta($item); - }; - $rtn = array_map($map, $res); - - $count = count($rtn); - - return ['items' => $rtn, 'count' => $count]; - } else { - return ['items' => [], 'count' => 0]; - } - } - - /** - * Only used by an upgrade migration to move errors from build_meta to build_error - * - * @param integer $limit - * - * @return array - */ - public function getErrorsForUpgrade($limit) - { - $query = 'SELECT * FROM {{build_meta}} - WHERE {{meta_key}} IN (\'phpmd-data\', \'phpcs-data\', \'phpdoccheck-data\', \'technical_debt-data\') - ORDER BY {{id}} ASC LIMIT :limit'; - - $stmt = Database::getConnection('read')->prepareCommon($query); - - $stmt->bindValue(':limit', $limit, \PDO::PARAM_INT); - - if ($stmt->execute()) { - $res = $stmt->fetchAll(\PDO::FETCH_ASSOC); - - $map = function ($item) { - return new BuildMeta($item); - }; - $rtn = array_map($map, $res); - - return $rtn; - } else { - return []; - } - } -} diff --git a/src/PHPCensor/Store/BuildStore.php b/src/PHPCensor/Store/BuildStore.php deleted file mode 100644 index 868249e..0000000 --- a/src/PHPCensor/Store/BuildStore.php +++ /dev/null @@ -1,501 +0,0 @@ - - */ -class BuildStore extends Store -{ - /** - * @var string - */ - protected $tableName = 'build'; - - /** - * @var string - */ - protected $modelName = '\PHPCensor\Model\Build'; - - /** - * @var string - */ - protected $primaryKey = 'id'; - - /** - * Get a Build by primary key (Id) - * - * @param integer $key - * @param string $useConnection - * - * @return null|Build - */ - public function getByPrimaryKey($key, $useConnection = 'read') - { - return $this->getById($key, $useConnection); - } - - /** - * Get a single Build by Id. - * - * @param integer $id - * @param string $useConnection - * - * @return Build|null - * - * @throws HttpException - */ - public function getById($id, $useConnection = 'read') - { - if (is_null($id)) { - throw new HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); - } - - $query = 'SELECT * FROM {{build}} WHERE {{id}} = :id LIMIT 1'; - $stmt = Database::getConnection($useConnection)->prepareCommon($query); - $stmt->bindValue(':id', $id); - - if ($stmt->execute()) { - if ($data = $stmt->fetch(\PDO::FETCH_ASSOC)) { - return new Build($data); - } - } - - return null; - } - - /** - * Get multiple Build by ProjectId. - * - * @param integer $projectId - * @param integer $limit - * @param string $useConnection - * - * @return array - * - * @throws HttpException - */ - public function getByProjectId($projectId, $limit = 1000, $useConnection = 'read') - { - if (is_null($projectId)) { - throw new HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); - } - - $query = 'SELECT * FROM {{build}} WHERE {{project_id}} = :project_id LIMIT :limit'; - $stmt = Database::getConnection($useConnection)->prepareCommon($query); - $stmt->bindValue(':project_id', $projectId); - $stmt->bindValue(':limit', (int)$limit, \PDO::PARAM_INT); - - if ($stmt->execute()) { - $res = $stmt->fetchAll(\PDO::FETCH_ASSOC); - - $map = function ($item) { - return new Build($item); - }; - $rtn = array_map($map, $res); - - $count = count($rtn); - - return ['items' => $rtn, 'count' => $count]; - } else { - return ['items' => [], 'count' => 0]; - } - } - - /** - * Get multiple Build by Status. - * - * @param integer $status - * @param integer $limit - * @param string $useConnection - * - * @return array - * - * @throws HttpException - */ - public function getByStatus($status, $limit = 1000, $useConnection = 'read') - { - if (is_null($status)) { - throw new HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); - } - - $query = 'SELECT * FROM {{build}} WHERE {{status}} = :status LIMIT :limit'; - $stmt = Database::getConnection($useConnection)->prepareCommon($query); - $stmt->bindValue(':status', $status); - $stmt->bindValue(':limit', (int)$limit, \PDO::PARAM_INT); - - if ($stmt->execute()) { - $res = $stmt->fetchAll(\PDO::FETCH_ASSOC); - - $map = function ($item) { - return new Build($item); - }; - $rtn = array_map($map, $res); - - $count = count($rtn); - - return ['items' => $rtn, 'count' => $count]; - } else { - return ['items' => [], 'count' => 0]; - } - } - - /** - * @param integer $limit - * @param integer $offset - * - * @return array - */ - public function getBuilds($limit = 5, $offset = 0) - { - $query = 'SELECT * FROM {{build}} ORDER BY {{id}} DESC LIMIT :limit OFFSET :offset'; - $stmt = Database::getConnection('read')->prepareCommon($query); - - $stmt->bindValue(':limit', $limit, \PDO::PARAM_INT); - $stmt->bindValue(':offset', $offset, \PDO::PARAM_INT); - - if ($stmt->execute()) { - $res = $stmt->fetchAll(\PDO::FETCH_ASSOC); - - $map = function ($item) { - return new Build($item); - }; - $rtn = array_map($map, $res); - - return $rtn; - } else { - return []; - } - } - - /** - * Return an array of the latest builds for a given project. - * - * @param integer|null $projectId - * @param integer $limit - * - * @return array - */ - public function getLatestBuilds($projectId = null, $limit = 5) - { - if (!is_null($projectId)) { - $query = 'SELECT * FROM {{build}} WHERE {{project_id}} = :pid ORDER BY {{id}} DESC LIMIT :limit'; - } else { - $query = 'SELECT * FROM {{build}} ORDER BY {{id}} DESC LIMIT :limit'; - } - - $stmt = Database::getConnection('read')->prepareCommon($query); - - if (!is_null($projectId)) { - $stmt->bindValue(':pid', $projectId); - } - - $stmt->bindValue(':limit', $limit, \PDO::PARAM_INT); - - if ($stmt->execute()) { - $res = $stmt->fetchAll(\PDO::FETCH_ASSOC); - - $map = function ($item) { - return new Build($item); - }; - $rtn = array_map($map, $res); - - return $rtn; - } else { - return []; - } - } - - /** - * Return the latest build for a specific project, of a specific build status. - * - * @param integer|null $projectId - * @param integer $status - * - * @return array|Build - */ - public function getLastBuildByStatus($projectId = null, $status = Build::STATUS_SUCCESS) - { - $query = 'SELECT * FROM {{build}} WHERE {{project_id}} = :pid AND {{status}} = :status ORDER BY {{id}} DESC LIMIT 1'; - $stmt = Database::getConnection('read')->prepareCommon($query); - $stmt->bindValue(':pid', $projectId); - $stmt->bindValue(':status', $status); - - if ($stmt->execute()) { - if ($data = $stmt->fetch(\PDO::FETCH_ASSOC)) { - return new Build($data); - } - } else { - return []; - } - } - - /** - * Return an array of the latest builds for all projects. - * - * @param integer $limit_by_project - * @param integer $limit_all - * - * @return array - */ - public function getAllProjectsLatestBuilds($limit_by_project = 5, $limit_all = 10) - { - // don't fetch log field - contain many data - $query = ' - SELECT - {{id}}, - {{project_id}}, - {{commit_id}}, - {{status}}, - {{branch}}, - {{create_date}}, - {{start_date}}, - {{finish_date}}, - {{committer_email}}, - {{commit_message}}, - {{extra}}, - {{environment}}, - {{tag}} - FROM {{build}} - ORDER BY {{id}} DESC - LIMIT 10000 - '; - - $stmt = Database::getConnection('read')->prepareCommon($query); - - if ($stmt->execute()) { - $res = $stmt->fetchAll(\PDO::FETCH_ASSOC); - - $projects = []; - $latest = []; - foreach($res as $item) { - $project_id = $item['project_id']; - $environment = $item['environment']; - if (empty($projects[$project_id])) { - $projects[$project_id] = []; - } - if (empty($projects[$project_id][$environment])) { - $projects[$project_id][$environment] = [ - 'latest' => [], - 'success' => null, - 'failed' => null, - ]; - } - $build = null; - if (count($projects[$project_id][$environment]['latest']) < $limit_by_project) { - $build = new Build($item); - $projects[$project_id][$environment]['latest'][] = $build; - } - if (count($latest) < $limit_all) { - if (is_null($build)) { - $build = new Build($item); - } - $latest[] = $build; - } - if (empty($projects[$project_id][$environment]['success']) and ($item['status'] == Build::STATUS_SUCCESS)) { - if (is_null($build)) { - $build = new Build($item); - } - $projects[$project_id][$environment]['success'] = $build; - } - if (empty($projects[$project_id][$environment]['failed']) and ($item['status'] == Build::STATUS_FAILED)) { - if (is_null($build)) { - $build = new Build($item); - } - $projects[$project_id][$environment]['failed'] = $build; - } - } - foreach($projects as $idx => $project) { - $projects[$idx] = array_filter($project, function($val) { - return ($val['latest'][0]->getStatus() != Build::STATUS_SUCCESS); - }); - } - $projects = array_filter($projects); - - return ['projects' => $projects, 'latest' => $latest]; - } else { - return []; - } - } - - /** - * Return an array of builds for a given project and commit ID. - * - * @param integer $projectId - * @param string $commitId - * - * @return array - */ - public function getByProjectAndCommit($projectId, $commitId) - { - $query = 'SELECT * FROM {{build}} WHERE {{project_id}} = :project_id AND {{commit_id}} = :commit_id'; - $stmt = Database::getConnection('read')->prepareCommon($query); - - $stmt->bindValue(':project_id', $projectId); - $stmt->bindValue(':commit_id', $commitId); - - if ($stmt->execute()) { - $res = $stmt->fetchAll(\PDO::FETCH_ASSOC); - - $map = function ($item) { - return new Build($item); - }; - - $rtn = array_map($map, $res); - - return ['items' => $rtn, 'count' => count($rtn)]; - } else { - return ['items' => [], 'count' => 0]; - } - } - - /** - * Returns all registered branches for project - * - * @param integer $projectId - * - * @return array - * - * @throws \Exception - */ - public function getBuildBranches($projectId) - { - $query = 'SELECT DISTINCT {{branch}} FROM {{build}} WHERE {{project_id}} = :project_id'; - $stmt = Database::getConnection('read')->prepareCommon($query); - $stmt->bindValue(':project_id', $projectId); - - if ($stmt->execute()) { - $res = $stmt->fetchAll(\PDO::FETCH_COLUMN); - return $res; - } else { - return []; - } - } - - /** - * Return build metadata by key, project and optionally build id. - * - * @param string $key - * @param integer $projectId - * @param integer|null $buildId - * @param string|null $branch - * @param integer $numResults - * - * @return array|null - */ - public function getMeta($key, $projectId, $buildId = null, $branch = null, $numResults = 1) - { - $query = 'SELECT bm.build_id, bm.meta_key, bm.meta_value - FROM {{build_meta}} AS {{bm}} - LEFT JOIN {{build}} AS {{b}} ON b.id = bm.build_id - WHERE bm.meta_key = :key AND b.project_id = :projectId'; - - // If we're getting comparative meta data, include previous builds - // otherwise just include the specified build ID: - if ($numResults > 1) { - $query .= ' AND bm.build_id <= :buildId '; - } else { - $query .= ' AND bm.build_id = :buildId '; - } - - // Include specific branch information if required: - if (!is_null($branch)) { - $query .= ' AND b.branch = :branch '; - } - - $query .= ' ORDER BY bm.id DESC LIMIT :numResults'; - - $stmt = Database::getConnection('read')->prepareCommon($query); - $stmt->bindValue(':key', $key, \PDO::PARAM_STR); - $stmt->bindValue(':projectId', (int)$projectId, \PDO::PARAM_INT); - $stmt->bindValue(':buildId', (int)$buildId, \PDO::PARAM_INT); - $stmt->bindValue(':numResults', (int)$numResults, \PDO::PARAM_INT); - - if (!is_null($branch)) { - $stmt->bindValue(':branch', $branch, \PDO::PARAM_STR); - } - - if ($stmt->execute()) { - $rtn = $stmt->fetchAll(\PDO::FETCH_ASSOC); - - /** @var \PHPCensor\Store\BuildErrorStore $errorStore */ - $errorStore = Factory::getStore('BuildError'); - - $rtn = array_reverse($rtn); - $rtn = array_map(function ($item) use ($key, $errorStore, $buildId) { - $item['meta_value'] = json_decode($item['meta_value'], true); - if ('plugin-summary' === $key) { - foreach ($item['meta_value'] as $stage => $stageData) { - foreach ($stageData as $plugin => $pluginData) { - $item['meta_value'][$stage][$plugin]['errors'] = $errorStore->getErrorTotalForBuild( - $buildId, - $plugin - ); - } - } - } - - return $item; - }, $rtn); - - if (!count($rtn)) { - return null; - } else { - return $rtn; - } - } else { - return null; - } - } - - /** - * Set a metadata value for a given project and build ID. - * - * @param integer $buildId - * @param string $key - * @param string $value - */ - public function setMeta($buildId, $key, $value) - { - /** @var BuildMetaStore $store */ - $store = Factory::getStore('BuildMeta'); - $meta = $store->getByKey($buildId, $key); - if (is_null($meta)) { - $meta = new BuildMeta(); - $meta->setBuildId($buildId); - $meta->setMetaKey($key); - } - $meta->setMetaValue($value); - - $store->save($meta); - } - - /** - * Update status only if it synced with db - * - * @param Build $build - * @param integer $status - * - * @return boolean - */ - public function updateStatusSync($build, $status) - { - try { - $query = 'UPDATE {{build}} SET status = :status_new WHERE {{id}} = :id AND {{status}} = :status_current'; - $stmt = Database::getConnection('write')->prepareCommon($query); - $stmt->bindValue(':id', $build->getId(), \PDO::PARAM_INT); - $stmt->bindValue(':status_current', $build->getStatus(), \PDO::PARAM_INT); - $stmt->bindValue(':status_new', $status, \PDO::PARAM_INT); - return ($stmt->execute() && ($stmt->rowCount() == 1)); - } catch (\Exception $e) { - return false; - } - } -} diff --git a/src/PHPCensor/Store/EnvironmentStore.php b/src/PHPCensor/Store/EnvironmentStore.php deleted file mode 100644 index bb9d1d9..0000000 --- a/src/PHPCensor/Store/EnvironmentStore.php +++ /dev/null @@ -1,105 +0,0 @@ -getById($key, $useConnection); - } - - /** - * Get a single Environment by Id. - * - * @param integer $id - * @param string $useConnection - * - * @return null|Environment - * - * @throws HttpException - */ - public function getById($id, $useConnection = 'read') - { - if (is_null($id)) { - throw new HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); - } - - $query = 'SELECT * FROM {{environment}} WHERE {{id}} = :id LIMIT 1'; - $stmt = Database::getConnection($useConnection)->prepareCommon($query); - $stmt->bindValue(':id', $id); - - if ($stmt->execute()) { - if ($data = $stmt->fetch(\PDO::FETCH_ASSOC)) { - return new Environment($data); - } - } - - return null; - } - - /** - * Get multiple Environment by Project id. - * - * @param integer $projectId - * @param string $useConnection - * - * @return array - * - * @throws \Exception - */ - public function getByProjectId($projectId, $useConnection = 'read') - { - if (is_null($projectId)) { - throw new \Exception('Value passed to ' . __FUNCTION__ . ' cannot be null.'); - } - - $query = 'SELECT * FROM {{environment}} WHERE {{project_id}} = :project_id'; - $stmt = Database::getConnection($useConnection)->prepareCommon($query); - - $stmt->bindValue(':project_id', $projectId); - - if ($stmt->execute()) { - $res = $stmt->fetchAll(\PDO::FETCH_ASSOC); - - $map = function ($item) { - return new Environment($item); - }; - $rtn = array_map($map, $res); - - $count = count($rtn); - - return ['items' => $rtn, 'count' => $count]; - } else { - return ['items' => [], 'count' => 0]; - } - } -} diff --git a/src/PHPCensor/Store/ProjectGroupStore.php b/src/PHPCensor/Store/ProjectGroupStore.php deleted file mode 100644 index c53c8e1..0000000 --- a/src/PHPCensor/Store/ProjectGroupStore.php +++ /dev/null @@ -1,99 +0,0 @@ -getById($key, $useConnection); - } - - /** - * Get a single ProjectGroup by Id. - * - * @param integer $id - * @param string $useConnection - * - * @return ProjectGroup|null - * - * @throws HttpException - */ - public function getById($id, $useConnection = 'read') - { - if (is_null($id)) { - throw new HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); - } - - $query = 'SELECT * FROM {{project_group}} WHERE {{id}} = :id LIMIT 1'; - $stmt = Database::getConnection($useConnection)->prepareCommon($query); - - $stmt->bindValue(':id', $id); - - if ($stmt->execute()) { - if ($data = $stmt->fetch(\PDO::FETCH_ASSOC)) { - return new ProjectGroup($data); - } - } - - return null; - } - - /** - * Get a single ProjectGroup by title. - * - * @param integer $title - * @param string $useConnection - * - * @return ProjectGroup|null - * - * @throws HttpException - */ - public function getByTitle($title, $useConnection = 'read') - { - if (is_null($title)) { - throw new HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); - } - - $query = 'SELECT * FROM {{project_group}} WHERE {{title}} = :title LIMIT 1'; - $stmt = Database::getConnection($useConnection)->prepareCommon($query); - - $stmt->bindValue(':title', $title); - - if ($stmt->execute()) { - if ($data = $stmt->fetch(\PDO::FETCH_ASSOC)) { - return new ProjectGroup($data); - } - } - - return null; - } -} diff --git a/src/PHPCensor/Store/ProjectStore.php b/src/PHPCensor/Store/ProjectStore.php deleted file mode 100644 index b0ca626..0000000 --- a/src/PHPCensor/Store/ProjectStore.php +++ /dev/null @@ -1,241 +0,0 @@ - - */ -class ProjectStore extends Store -{ - /** - * @var string - */ - protected $tableName = 'project'; - - /** - * @var string - */ - protected $modelName = '\PHPCensor\Model\Project'; - - /** - * @var string - */ - protected $primaryKey = 'id'; - - /** - * Get a Project by primary key (Id) - * - * @param integer $key - * @param string $useConnection - * - * @return null|Project - */ - public function getByPrimaryKey($key, $useConnection = 'read') - { - return $this->getById($key, $useConnection); - } - - /** - * Get a single Project by Id. - * - * @param integer $id - * @param string $useConnection - * - * @return null|Project - * - * @throws HttpException - */ - public function getById($id, $useConnection = 'read') - { - if (is_null($id)) { - throw new HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); - } - - $query = 'SELECT * FROM {{project}} WHERE {{id}} = :id LIMIT 1'; - $stmt = Database::getConnection($useConnection)->prepareCommon($query); - $stmt->bindValue(':id', $id); - - if ($stmt->execute()) { - if ($data = $stmt->fetch(\PDO::FETCH_ASSOC)) { - return new Project($data); - } - } - - return null; - } - - /** - * Get a single Project by Ids. - * - * @param integer[] $values - * @param string $useConnection - * - * @throws HttpException - * - * @return Project[] - */ - public function getByIds($values, $useConnection = 'read') - { - if (empty($values)) { - throw new HttpException('Values passed to ' . __FUNCTION__ . ' cannot be empty.'); - } - - $query = 'SELECT * FROM {{project}} WHERE {{id}} IN ('.implode(', ', array_map('intval', $values)).')'; - $stmt = Database::getConnection($useConnection)->prepareCommon($query); - - $rtn = []; - if ($stmt->execute()) { - while ($data = $stmt->fetch(\PDO::FETCH_ASSOC)) { - $rtn[$data['id']] = new Project($data); - } - } - - return $rtn; - } - - /** - * Get multiple Project by Title. - * - * @param string $title - * @param integer $limit - * @param string $useConnection - * - * @return array - * - * @throws HttpException - */ - public function getByTitle($title, $limit = 1000, $useConnection = 'read') - { - if (is_null($title)) { - throw new HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); - } - - - $query = 'SELECT * FROM {{project}} WHERE {{title}} = :title LIMIT :limit'; - $stmt = Database::getConnection($useConnection)->prepareCommon($query); - $stmt->bindValue(':title', $title); - $stmt->bindValue(':limit', (int)$limit, \PDO::PARAM_INT); - - if ($stmt->execute()) { - $res = $stmt->fetchAll(\PDO::FETCH_ASSOC); - - $map = function ($item) { - return new Project($item); - }; - $rtn = array_map($map, $res); - - $count = count($rtn); - - return ['items' => $rtn, 'count' => $count]; - } else { - return ['items' => [], 'count' => 0]; - } - } - - /** - * Returns a list of all branch names. - * - * @param $projectId - * - * @return array - */ - public function getKnownBranches($projectId) - { - $query = 'SELECT DISTINCT {{branch}} from {{build}} WHERE {{project_id}} = :pid'; - $stmt = Database::getConnection('read')->prepareCommon($query); - $stmt->bindValue(':pid', $projectId); - - if ($stmt->execute()) { - $res = $stmt->fetchAll(\PDO::FETCH_ASSOC); - - $map = function ($item) { - return $item['branch']; - }; - $rtn = array_map($map, $res); - - return $rtn; - } else { - return []; - } - } - - /** - * Get a list of all projects, ordered by their title. - * - * @param boolean $archived - * - * @return array - */ - public function getAll($archived = false) - { - $archived = (integer)$archived; - - $query = 'SELECT * FROM {{project}} WHERE {{archived}} = :archived ORDER BY {{title}} ASC'; - $stmt = Database::getConnection('read')->prepareCommon($query); - - $stmt->bindValue(':archived', $archived); - - if ($stmt->execute()) { - $res = $stmt->fetchAll(\PDO::FETCH_ASSOC); - - $map = function ($item) { - return new Project($item); - }; - $rtn = array_map($map, $res); - - $count = count($rtn); - - - return ['items' => $rtn, 'count' => $count]; - } else { - return ['items' => [], 'count' => 0]; - } - } - - /** - * Get multiple Project by GroupId. - * - * @param integer $groupId - * @param boolean $archived - * @param integer $limit - * @param string $useConnection - * - * @return array - * - * @throws \Exception - */ - public function getByGroupId($groupId, $archived = false, $limit = 1000, $useConnection = 'read') - { - if (is_null($groupId)) { - throw new \Exception('Value passed to ' . __FUNCTION__ . ' cannot be null.'); - } - $archived = (integer)$archived; - - $query = 'SELECT * FROM {{project}} WHERE {{group_id}} = :group_id AND {{archived}} = :archived ORDER BY {{title}} LIMIT :limit'; - $stmt = Database::getConnection($useConnection)->prepareCommon($query); - - $stmt->bindValue(':group_id', $groupId); - $stmt->bindValue(':archived', $archived); - $stmt->bindValue(':limit', (int)$limit, \PDO::PARAM_INT); - - if ($stmt->execute()) { - $res = $stmt->fetchAll(\PDO::FETCH_ASSOC); - - $map = function ($item) { - return new Project($item); - }; - $rtn = array_map($map, $res); - - $count = count($rtn); - - return ['items' => $rtn, 'count' => $count]; - } else { - return ['items' => [], 'count' => 0]; - } - } -} diff --git a/src/PHPCensor/Store/UserStore.php b/src/PHPCensor/Store/UserStore.php deleted file mode 100644 index 8bb75a7..0000000 --- a/src/PHPCensor/Store/UserStore.php +++ /dev/null @@ -1,194 +0,0 @@ - - */ -class UserStore extends Store -{ - /** - * @var string - */ - protected $tableName = 'user'; - - /** - * @var string - */ - protected $modelName = '\PHPCensor\Model\User'; - - /** - * @var string - */ - protected $primaryKey = 'id'; - - /** - * Get a User by primary key (Id) - * - * @param integer $key - * @param string $useConnection - * - * @return null|User - */ - public function getByPrimaryKey($key, $useConnection = 'read') - { - return $this->getById($key, $useConnection); - } - - /** - * Get a single User by Id. - * - * @param integer $id - * @param string $useConnection - * - * @return null|User - * - * @throws HttpException - */ - public function getById($id, $useConnection = 'read') - { - if (is_null($id)) { - throw new HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); - } - - $query = 'SELECT * FROM {{user}} WHERE {{id}} = :id LIMIT 1'; - $stmt = Database::getConnection($useConnection)->prepareCommon($query); - $stmt->bindValue(':id', $id); - - if ($stmt->execute()) { - if ($data = $stmt->fetch(\PDO::FETCH_ASSOC)) { - return new User($data); - } - } - - return null; - } - - /** - * Get a single User by Email. - * - * @param string $email - * - * @throws HttpException - * - * @return User - */ - public function getByEmail($email) - { - if (is_null($email)) { - throw new HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); - } - - $query = 'SELECT * FROM {{user}} WHERE {{email}} = :email LIMIT 1'; - $stmt = Database::getConnection()->prepareCommon($query); - - $stmt->bindValue(':email', $email); - - if ($stmt->execute()) { - if ($data = $stmt->fetch(\PDO::FETCH_ASSOC)) { - return new User($data); - } - } - - return null; - } - - /** - * Get a single User by Email or Name. - * - * @param string $emailOrName - * - * @throws HttpException - * - * @return User - */ - public function getByEmailOrName($emailOrName) - { - if (is_null($emailOrName)) { - throw new HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); - } - - $query = 'SELECT * FROM {{user}} WHERE {{email}} = :value OR {{name}} = :value LIMIT 1'; - $stmt = Database::getConnection()->prepareCommon($query); - $stmt->bindValue(':value', $emailOrName); - - if ($stmt->execute()) { - if ($data = $stmt->fetch(\PDO::FETCH_ASSOC)) { - return new User($data); - } - } - - return null; - } - - /** - * Get a single User by RememberKey. - * - * @param string $rememberKey - * - * @throws HttpException - * - * @return User - */ - public function getByRememberKey($rememberKey) - { - if (is_null($rememberKey)) { - throw new HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); - } - - $query = 'SELECT * FROM {{user}} WHERE {{remember_key}} = :remember_key LIMIT 1'; - $stmt = Database::getConnection()->prepareCommon($query); - $stmt->bindValue(':remember_key', $rememberKey); - - if ($stmt->execute()) { - if ($data = $stmt->fetch(\PDO::FETCH_ASSOC)) { - return new User($data); - } - } - - return null; - } - - /** - * Get multiple User by Name. - * - * @param string $name - * @param integer $limit - * @param string $useConnection - * - * @return array - * - * @throws HttpException - */ - public function getByName($name, $limit = 1000, $useConnection = 'read') - { - if (is_null($name)) { - throw new HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); - } - - $query = 'SELECT * FROM {{user}} WHERE {{name}} = :name LIMIT :limit'; - $stmt = Database::getConnection($useConnection)->prepareCommon($query); - $stmt->bindValue(':name', $name); - $stmt->bindValue(':limit', (int)$limit, \PDO::PARAM_INT); - - if ($stmt->execute()) { - $res = $stmt->fetchAll(\PDO::FETCH_ASSOC); - - $map = function ($item) { - return new User($item); - }; - $rtn = array_map($map, $res); - - $count = count($rtn); - - return ['items' => $rtn, 'count' => $count]; - } else { - return ['items' => [], 'count' => 0]; - } - } -} diff --git a/src/PHPCensor/View/Build/errors.phtml b/src/PHPCensor/View/Build/errors.phtml deleted file mode 100644 index 1db3403..0000000 --- a/src/PHPCensor/View/Build/errors.phtml +++ /dev/null @@ -1,38 +0,0 @@ -getFileLinkTemplate(); - -/** @var \PHPCensor\Model\BuildError[] $errors */ -foreach ($errors as $error): - - $link = str_replace('{BASEFILE}', basename($error->getFile()), $linkTemplate); - $link = str_replace('{FILE}', $error->getFile(), $link); - $link = str_replace('{LINE}', $error->getLineStart(), $link); - $link = str_replace('{LINE_END}', $error->getLineEnd(), $link); -?> - - - - - getSeverityString()); ?> - - - getPlugin()); ?> - getFile(); ?> - - - getLineStart() == $error->getLineEnd() || !$error->getLineEnd()) { - print $error->getLineStart(); - } else { - print $error->getLineStart() . ' - ' . $error->getLineEnd(); - } - ?> - - - getMessage())); ?> - - - - diff --git a/src/PHPCensor/View/Build/header-row.phtml b/src/PHPCensor/View/Build/header-row.phtml deleted file mode 100644 index b2038c4..0000000 --- a/src/PHPCensor/View/Build/header-row.phtml +++ /dev/null @@ -1,30 +0,0 @@ - -
  • - - getCommitterEmail()): ?> -
    - -
    - - -

    - getProject()->getTitle(); ?> - - getStatus() == \PHPCensor\Model\Build::STATUS_PENDING): ?> - getCreateDate()->format('H:i')); ?> - getStatus() == \PHPCensor\Model\Build::STATUS_RUNNING): ?> - getStartDate()->format('H:i')); ?> - -

    -

    getBranch()); ?>

    -
    -
  • \ No newline at end of file diff --git a/src/PHPCensor/View/Build/view.phtml b/src/PHPCensor/View/Build/view.phtml deleted file mode 100644 index 69d2580..0000000 --- a/src/PHPCensor/View/Build/view.phtml +++ /dev/null @@ -1,315 +0,0 @@ - - -
    -
    -
    -
    -

    - -

    -
    - -
    - - - - - - - - - - - - - - - - - - - - -
    - - - getProject()->getTitle(); ?> - -
    - - getBranch(); ?> - - getTag()): ?> / - - - - -
    - getEnvironment(); - echo !empty($environment) ? (' ' . $environment) : '—' ; - ?> -
    - - getBranch(); ?> - - + getExtra('branches'); - if (!empty($branches)) { - foreach($branches as $branch) { - ?> -
    -
    -
    -
    - -
    -
    -
    -

    - -

    -
    - -
    - - - - - - - - - - - - - - - - - - - -
    - getSourceHumanize()); ?> -
    - - getCommitId(), 0, 7); ?> - -
    - getCommitterEmail(); ?> -
    - getCommitMessage(); ?> -
    -
    -
    -
    - -
    -
    -
    -

    - -

    -
    - -
    - - - - - - - - - - - - - - - - - - - - -
    - getCreateDate() ? $build->getCreateDate()->format('Y-m-d H:i:s') : ''); ?> -
    - getStartDate() ? $build->getStartDate()->format('Y-m-d H:i:s') : ''); ?> -
    - getFinishDate() ? $build->getFinishDate()->format('Y-m-d H:i:s') : ''); ?> -
    - getDuration(); ?> -
    -
    -
    -
    - -
    - - - - - - -' . PHP_EOL; -} -?> - - diff --git a/src/PHPCensor/View/BuildStatus/view.phtml b/src/PHPCensor/View/BuildStatus/view.phtml deleted file mode 100644 index 71d056b..0000000 --- a/src/PHPCensor/View/BuildStatus/view.phtml +++ /dev/null @@ -1,196 +0,0 @@ - - - - <?php print $project->getTitle(); ?> - PHP Censor - - - - - - - - - - - - - - - - - -
    -
    - -
    -
    -
    -
    - - - getStatus()) { - case 0: - $statusClass = 'blue'; - $statusText = 'Pending'; - break; - case 1: - $statusClass = 'yellow'; - $statusText = 'Running'; - break; - case 2: - $statusClass = 'green'; - $statusText = 'Success'; - break; - case 3: - $statusClass = 'red'; - $statusText = 'Failed'; - break; - } - - ?> - -
    -
    -

    - getProject()->getTitle(); ?> #getId(); ?> () -

    -

    - getCommitMessage()): ?> - getCommitMessage(); ?>

    - - - Branch: getBranch(); ?>
    - Committer: getCommitterEmail(); ?> - - getCommitId())): ?> -
    Commit: getCommitId(); ?>
    - -

    -
    -
    - -
    -
    -
    - - -
    -

    Builds

    - - - - - - - - - - - - - - - - - - - - - - - - getStatus()) - { - case 0: - $class = 'info'; - $status = 'Pending'; - - break; - - case 1: - $class = 'warning'; - $status = 'Running'; - break; - - case 2: - $class = 'success'; - $status = 'Success'; - break; - - case 3: - $class = 'danger'; - $status = 'Failed'; - break; - } - ?> - - - - - - - - - - - - - - -
    IDStatusDateCommitBranchEnvironmentDuration
    No builds yet.
    #getId(), 6, '0', STR_PAD_LEFT); ?> - - getCreateDate()->format('Y-m-d H:i:s'); ?> - getCommitId())) { - print sprintf( - '%s %s', - $build->getCommitLink(), - substr($build->getCommitId(), 0, 7), - $build->getCommitterEmail() ? ('(' . $build->getCommitterEmail() . ')') : '' - ); - } else { - print '—'; - } - ?> - - getExtra('branches'); ?> - getBranch(); ?> - - getTag()): ?> / - - - - - - getEnvironment(); - echo !empty($environment) ? $environment : '—' ; - ?> - - getDuration(); ?> sec. -
    -
    -
    -
    -
    - -
    - - diff --git a/src/PHPCensor/View/Email/layout.phtml b/src/PHPCensor/View/Email/layout.phtml deleted file mode 100644 index a97ac99..0000000 --- a/src/PHPCensor/View/Email/layout.phtml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - -
    -
    -
    getTitle(); ?> - Build #getId(); ?>
    -
    -

    - Your commit getCommitId(); ?> generated a - isSuccessful() ? 'success' : 'failed'; ?> build in project - getTitle(); ?>. -

    - -
    - -
    -
    - - diff --git a/src/PHPCensor/View/Email/long.phtml b/src/PHPCensor/View/Email/long.phtml deleted file mode 100644 index daef0aa..0000000 --- a/src/PHPCensor/View/Email/long.phtml +++ /dev/null @@ -1,2 +0,0 @@ -

    getCommitMessage(); ?>

    -
    getLog()); ?>
    diff --git a/src/PHPCensor/View/Email/short.phtml b/src/PHPCensor/View/Email/short.phtml deleted file mode 100644 index 75df4f8..0000000 --- a/src/PHPCensor/View/Email/short.phtml +++ /dev/null @@ -1 +0,0 @@ -

    getCommitMessage(); ?>

    diff --git a/src/PHPCensor/View/Group/edit.phtml b/src/PHPCensor/View/Group/edit.phtml deleted file mode 100644 index 809fc59..0000000 --- a/src/PHPCensor/View/Group/edit.phtml +++ /dev/null @@ -1,10 +0,0 @@ - -
    -
    -

    -
    - -
    - -
    -
    \ No newline at end of file diff --git a/src/PHPCensor/View/Group/index.phtml b/src/PHPCensor/View/Group/index.phtml deleted file mode 100644 index 7bad7e8..0000000 --- a/src/PHPCensor/View/Group/index.phtml +++ /dev/null @@ -1,48 +0,0 @@ - - - -
    - - - - - - - - - - - - - - - - - -
    -
    - - User()->getIsAdmin() && (!count($group['projects']))): ?> - - - -
    -
    -
    - - \ No newline at end of file diff --git a/src/PHPCensor/View/Home/index.phtml b/src/PHPCensor/View/Home/index.phtml deleted file mode 100644 index 8593908..0000000 --- a/src/PHPCensor/View/Home/index.phtml +++ /dev/null @@ -1,29 +0,0 @@ - -
    -
    - $params) { ?> -
    -
    -
    - -
    - -
    - $params) { ?> -
    -
    -
    - -
    - -
    diff --git a/src/PHPCensor/View/Project/ajax-builds.phtml b/src/PHPCensor/View/Project/ajax-builds.phtml deleted file mode 100644 index d683b13..0000000 --- a/src/PHPCensor/View/Project/ajax-builds.phtml +++ /dev/null @@ -1,105 +0,0 @@ - - - - - - - - - - -getStatus()) -{ - case 0: - $cls = 'active'; - $subcls = 'info'; - $status = Lang::get('pending'); - - break; - - case 1: - $cls = 'warning'; - $subcls = 'warning'; - $status = Lang::get('running'); - break; - - case 2: - $cls = 'success'; - $subcls = 'success'; - $status = Lang::get('success'); - break; - - case 3: - $cls = 'danger'; - $subcls = 'danger'; - $status = Lang::get('failed'); - break; -} - -$branches = $build->getExtra('branches'); -?> - - #getId(), 6, '0', STR_PAD_LEFT); ?> - - getCreateDate()->format('Y-m-d H:i:s'); ?> - getSourceHumanize()); ?> - - getCommitId())) { - print sprintf( - '%s %s', - $build->getCommitLink(), - substr($build->getCommitId(), 0, 7), - $build->getCommitterEmail() ? ('(' . $build->getCommitterEmail() . ')') : '' - ); - } else { - print '—'; - } - ?> - - - - getBranch(); ?> - - - getTag()): ?> / - - - - - - - getEnvironment(); - echo !empty($environment) ? $environment : '—' ; - ?> - - - getDuration(); ?> - - -
    - - User()->getIsAdmin()): ?> - - - -
    - - - - - diff --git a/src/PHPCensor/View/Project/edit.phtml b/src/PHPCensor/View/Project/edit.phtml deleted file mode 100644 index 69a12fc..0000000 --- a/src/PHPCensor/View/Project/edit.phtml +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - -
    - -
    -
    -
    -

    -
    - -
    - -
    -
    -
    - - -
    -
    -
    -

    - -
    -
    -
    - - -
    diff --git a/src/PHPCensor/View/Project/view.phtml b/src/PHPCensor/View/Project/view.phtml deleted file mode 100644 index bf2dff7..0000000 --- a/src/PHPCensor/View/Project/view.phtml +++ /dev/null @@ -1,222 +0,0 @@ - - - -
    - - - - - - - - - getId(); - ?> -
    - getArchived()): ?> - User()->getIsAdmin()): ?> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    - - - -
    -
    -
    - - -
    -
    -
    -
    -

    ()

    -
    - - - - - - - - - - - - - - - - - -
    -
    -
    - -
    -
    - -
    - - getAllowPublicStatus() && !$project->getArchived()): ?> -
    -
    -

    -
    - -
    -
    - -
    - - - - - -

    - :
    getId() . '?branch=master&label=PHPCensor&style=flat-square'; ?> - -

    - :
    getId() . '?branch=master'; ?> -
    -
    -
    - - - getType(), ['github', 'gitlab', 'bitbucket'])): ?> -
    -
    -

    -
    - -
    -
    - -
    - getType()) - { - case 'github': - $url = APP_URL . 'webhook/github/' . $project->getId(); - Lang::out('webhooks_help_github', $project->getReference()); - break; - - case 'gitlab': - $url = APP_URL. 'webhook/gitlab/' . $project->getId(); - Lang::out('webhooks_help_gitlab'); - break; - - case 'bitbucket': - case 'bitbuckethg': - $url = APP_URL . 'webhook/bitbucket/' . $project->getId(); - Lang::out('webhooks_help_bitbucket', $project->getReference()); - break; - } - ?> -

    -
    -
    - - - getSshPublicKey()): ?> -
    -
    -

    -
    - -
    -
    -
    getSshPublicKey(); ?>
    -
    - -
    -
    - - diff --git a/src/PHPCensor/View/Session.phtml b/src/PHPCensor/View/Session.phtml deleted file mode 100644 index 411c295..0000000 --- a/src/PHPCensor/View/Session.phtml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - <?php Lang::out('log_in_to_app'); ?> - - - - - - - - -
    -
    - -
    - -
    -
    -
    - - - - - - diff --git a/src/PHPCensor/View/Session/forgotPassword.phtml b/src/PHPCensor/View/Session/forgotPassword.phtml deleted file mode 100644 index 75e1450..0000000 --- a/src/PHPCensor/View/Session/forgotPassword.phtml +++ /dev/null @@ -1,28 +0,0 @@ - - -

    - -

    - - -
    - -
    - -
    - -
    - -
    -
    -
    - - -
    - -
    - -
    -
    -
    - diff --git a/src/PHPCensor/View/Session/login.phtml b/src/PHPCensor/View/Session/login.phtml deleted file mode 100644 index 4ff7451..0000000 --- a/src/PHPCensor/View/Session/login.phtml +++ /dev/null @@ -1,9 +0,0 @@ - - -

    - - - - - - diff --git a/src/PHPCensor/View/Session/resetPassword.phtml b/src/PHPCensor/View/Session/resetPassword.phtml deleted file mode 100644 index ee69378..0000000 --- a/src/PHPCensor/View/Session/resetPassword.phtml +++ /dev/null @@ -1,23 +0,0 @@ - - -
    - -
    - -
    -
    -
    - - -
    - -
    - -
    -
    -
    - -
    - -
    - diff --git a/src/PHPCensor/View/User/edit.phtml b/src/PHPCensor/View/User/edit.phtml deleted file mode 100644 index 96234e3..0000000 --- a/src/PHPCensor/View/User/edit.phtml +++ /dev/null @@ -1,9 +0,0 @@ -
    -
    -
    -
    - -
    -
    -
    -
    diff --git a/src/PHPCensor/View/User/index.phtml b/src/PHPCensor/View/User/index.phtml deleted file mode 100644 index 6eb7790..0000000 --- a/src/PHPCensor/View/User/index.phtml +++ /dev/null @@ -1,70 +0,0 @@ - -
    -
    - -
    -
    - -
    -
    -
    - - - - - - - - - - - - - getIsAdmin()) - { - case 0: - $cls = ''; - $status = Lang::get('no'); - break; - - case 1: - $cls = 'warning'; - $status = Lang::get('yes'); - break; - } - ?> - - - - - - - - -
    getEmail(); ?>getName()); ?> - User()->getIsAdmin()): ?> -
    - - - -
    - -
    -
    -
    -
    - - diff --git a/src/PHPCensor/View/User/profile.phtml b/src/PHPCensor/View/User/profile.phtml deleted file mode 100644 index a9f9235..0000000 --- a/src/PHPCensor/View/User/profile.phtml +++ /dev/null @@ -1,14 +0,0 @@ - - -

    - - -
    -
    -

    -
    -
    - -
    -
    - diff --git a/src/PHPCensor/View/WidgetAllProjects/index-projects.phtml b/src/PHPCensor/View/WidgetAllProjects/index-projects.phtml deleted file mode 100644 index 1b046ba..0000000 --- a/src/PHPCensor/View/WidgetAllProjects/index-projects.phtml +++ /dev/null @@ -1,149 +0,0 @@ -getId()])) { - // Get the most recent build status to determine the main block colour. - $last_build = $builds[$project->getId()][0]; - $status = $last_build->getStatus(); - switch($status) { - case 0: - $subcls = 'blue'; - break; - case 1: - $subcls = 'yellow'; - break; - case 2: - $subcls = 'green'; - break; - case 3: - $subcls = 'red'; - break; - } - // Use the last 5 builds to determine project health: - $failures = 0; - - foreach ($builds[$project->getId()] as $build) { - switch ($build->getStatus()) { - case 0: - $statuses[] = 'pending'; - break; - case 1: - $statuses[] = 'running'; - break; - case 2: - $statuses[] = 'ok'; - $success = is_null($success) && !is_null($build->getFinishDate()) ? $build->getFinishDate()->format('Y-m-d H:i:s') : $success; - break; - case 3: - $failures++; - $statuses[] = 'failed'; - $failure = is_null($failure) && !is_null($build->getFinishDate()) ? $build->getFinishDate()->format('Y-m-d H:i:s') : $failure; - break; - } - } - } - - $buildCount = count($builds[$project->getId()]); - $lastSuccess = $successful[$project->getId()]; - $lastFailure = $failed[$project->getId()]; - $message = Lang::get('no_builds_yet'); - $shortMessage = Lang::get('no_builds_yet'); - - if ($buildCount > 0) { - if ($failures > 0) { - $shortMessage = Lang::get('x_of_x_failed_short', $failures, $buildCount); - $message = Lang::get('x_of_x_failed', $failures, $buildCount); - - if (!is_null($lastSuccess) && !is_null($lastSuccess->getFinishDate())) { - $message .= Lang::get('last_successful_build', $lastSuccess->getFinishDate()->format('Y-m-d H:i:s')); - } else { - $message .= Lang::get('never_built_successfully'); - } - } else { - $message = Lang::get('all_builds_passed', $buildCount); - $shortMessage = Lang::get('all_builds_passed_short', $buildCount, $buildCount); - - if (!is_null($lastFailure) && !is_null($lastFailure->getFinishDate())) { - $message .= Lang::get('last_failed_build', $lastFailure->getFinishDate()->format('Y-m-d H:i:s')); - } else { - $message .= Lang::get('never_failed_build'); - } - } - } - -?> -
    -
    - -
    -

    - - getTitle(); ?> - -

    - -

    - -

    - -
    -
    - -
    - -
    - getAllowPublicStatus()): ?> - - - - -
    - (getId()]; ?>) -
    - - getId()][$idx])) { - echo ''; - } else { - $build = $builds[$project->getId()][$idx]; - $link = APP_URL . 'build/view/' . $build->id; - switch ($build->getStatus()) { - case 0: - $class = 'bg-blue'; - $icon = 'fa-clock-o'; - break; - case 1: - $class = 'bg-yellow'; - $icon = 'fa-cogs'; - break; - case 2: - $class = 'bg-green'; - $icon = 'fa-check'; - break; - case 3: - $class = 'bg-red'; - $icon = 'fa-times'; - break; - } - echo ''; - } - } ?> -
    -
    -
    - - diff --git a/src/PHPCensor/View/WidgetAllProjects/index.phtml b/src/PHPCensor/View/WidgetAllProjects/index.phtml deleted file mode 100644 index 7e614c9..0000000 --- a/src/PHPCensor/View/WidgetAllProjects/index.phtml +++ /dev/null @@ -1,15 +0,0 @@ - -
    -
    -

    -
    - -
    -
    -
    - -
    -
    - diff --git a/src/PHPCensor/View/WidgetAllProjects/update.phtml b/src/PHPCensor/View/WidgetAllProjects/update.phtml deleted file mode 100644 index 1f00e37..0000000 --- a/src/PHPCensor/View/WidgetAllProjects/update.phtml +++ /dev/null @@ -1,145 +0,0 @@ -getStatus(); - switch($status) { - case 0: - $subcls = 'blue'; - break; - case 1: - $subcls = 'yellow'; - break; - case 2: - $subcls = 'green'; - break; - case 3: - $subcls = 'red'; - break; - } - // Use the last 5 builds to determine project health: - $failures = 0; - - foreach ($builds as $build) { - switch ($build->getStatus()) { - case 0: - $statuses[] = 'pending'; - break; - case 1: - $statuses[] = 'running'; - break; - case 2: - $statuses[] = 'ok'; - $success = is_null($success) && !is_null($build->getFinishDate()) ? $build->getFinishDate()->format('Y-m-d H:i:s') : $success; - break; - case 3: - $failures++; - $statuses[] = 'failed'; - $failure = is_null($failure) && !is_null($build->getFinishDate()) ? $build->getFinishDate()->format('Y-m-d H:i:s') : $failure; - break; - } - } -} - -$buildCount = count($builds); -$lastSuccess = $successful; -$lastFailure = $failed; -$message = Lang::get('no_builds_yet'); -$shortMessage = Lang::get('no_builds_yet'); - -if ($buildCount > 0) { - if ($failures > 0) { - $shortMessage = Lang::get('x_of_x_failed_short', $failures, $buildCount); - $message = Lang::get('x_of_x_failed', $failures, $buildCount); - - if (!is_null($lastSuccess) && !is_null($lastSuccess->getFinishDate())) { - $message .= Lang::get('last_successful_build', $lastSuccess->getFinishDate()->format('Y-m-d H:i:s')); - } else { - $message .= Lang::get('never_built_successfully'); - } - } else { - $message = Lang::get('all_builds_passed', $buildCount); - $shortMessage = Lang::get('all_builds_passed_short', $buildCount, $buildCount); - - if (!is_null($lastFailure) && !is_null($lastFailure->getFinishDate())) { - $message .= Lang::get('last_failed_build', $lastFailure->getFinishDate()->format('Y-m-d H:i:s')); - } else { - $message .= Lang::get('never_failed_build'); - } - } -} - -?> - -
    - -
    -

    - - getTitle(); ?> - -

    - -

    - -

    - -
    -
    - -
    - -
    - getAllowPublicStatus()): ?> - - - - -
    - () -
    - - '; - } else { - $build = $builds[$idx]; - $link = APP_URL . 'build/view/' . $build->id; - switch ($build->getStatus()) { - case 0: - $class = 'bg-blue'; - $icon = 'fa-clock-o'; - break; - case 1: - $class = 'bg-yellow'; - $icon = 'fa-cogs'; - break; - case 2: - $class = 'bg-green'; - $icon = 'fa-check'; - break; - case 3: - $class = 'bg-red'; - $icon = 'fa-times'; - break; - } - echo ''; - } - } ?> -
    -
    diff --git a/src/PHPCensor/View/WidgetBuildErrors/empty.phtml b/src/PHPCensor/View/WidgetBuildErrors/empty.phtml deleted file mode 100644 index cc08ea4..0000000 --- a/src/PHPCensor/View/WidgetBuildErrors/empty.phtml +++ /dev/null @@ -1,5 +0,0 @@ -
    \ No newline at end of file diff --git a/src/PHPCensor/View/WidgetBuildErrors/index.phtml b/src/PHPCensor/View/WidgetBuildErrors/index.phtml deleted file mode 100644 index 18595f3..0000000 --- a/src/PHPCensor/View/WidgetBuildErrors/index.phtml +++ /dev/null @@ -1,13 +0,0 @@ - -
    -
    -

    -
    -
    - -
    -
    diff --git a/src/PHPCensor/View/WidgetBuildErrors/update.phtml b/src/PHPCensor/View/WidgetBuildErrors/update.phtml deleted file mode 100644 index 47e4608..0000000 --- a/src/PHPCensor/View/WidgetBuildErrors/update.phtml +++ /dev/null @@ -1,157 +0,0 @@ - $project_envs): - if (!isset($projects[$project_id])) { - echo ''; - continue; - } - $project = $projects[$project_id]; - foreach($project_envs as $environment => $project_env): - $statuses = []; - $failures = 0; - $subcls = 'gray'; - $cls = ''; - $success = null; - $failure = null; - - // Get the most recent build status to determine the main block colour. - $last_build = $project_env['latest'][0]; - $status = $last_build->getStatus(); - switch($status) { - case 0: - $subcls = 'blue'; - break; - case 1: - $subcls = 'yellow'; - break; - case 2: - $subcls = 'green'; - break; - case 3: - $subcls = 'red'; - break; - } - // Use the last 5 builds to determine project health: - $failures = 0; - - foreach ($project_env['latest'] as $build) { - switch ($build->getStatus()) { - case 0: - $statuses[] = 'pending'; - break; - case 1: - $statuses[] = 'running'; - break; - case 2: - $statuses[] = 'ok'; - $success = is_null($success) && !is_null($build->getFinishDate()) ? $build->getFinishDate()->format('Y-m-d H:i:s') : $success; - break; - case 3: - $failures++; - $statuses[] = 'failed'; - $failure = is_null($failure) && !is_null($build->getFinishDate()) ? $build->getFinishDate()->format('Y-m-d H:i:s') : $failure; - break; - } - } - - $buildCount = count($project_env['latest']); - $lastSuccess = $project_env['success']; - $lastFailure = $project_env['failed']; - $message = Lang::get('no_builds_yet'); - $shortMessage = Lang::get('no_builds_yet'); - - if ($buildCount > 0) { - if ($failures > 0) { - $shortMessage = Lang::get('x_of_x_failed_short', $failures, $buildCount); - $message = Lang::get('x_of_x_failed', $failures, $buildCount); - - if (!is_null($lastSuccess) && !is_null($lastSuccess->getFinishDate())) { - $message .= Lang::get('last_successful_build', $lastSuccess->getFinishDate()->format('Y-m-d H:i:s')); - } else { - $message .= Lang::get('never_built_successfully'); - } - } else { - $message = Lang::get('all_builds_passed', $buildCount); - $shortMessage = Lang::get('all_builds_passed_short', $buildCount, $buildCount); - - if (!is_null($lastFailure) && !is_null($lastFailure->getFinishDate())) { - $message .= Lang::get('last_failed_build', $lastFailure->getFinishDate()->format('Y-m-d H:i:s')); - } else { - $message .= Lang::get('never_failed_build'); - } - } - } - -?> -
    -
    - -
    -

    - - getTitle(); ?> - - - - -

    - -

    - -

    - -
    -
    - -
    - -
    - getAllowPublicStatus()): ?> - - - - -
    - -
    - - '; - } else { - $build = $project_env['latest'][$idx]; - $link = APP_URL . 'build/view/' . $build->id; - switch ($build->getStatus()) { - case 0: - $class = 'bg-blue'; - $icon = 'fa-clock-o'; - break; - case 1: - $class = 'bg-yellow'; - $icon = 'fa-cogs'; - break; - case 2: - $class = 'bg-green'; - $icon = 'fa-check'; - break; - case 3: - $class = 'bg-red'; - $icon = 'fa-times'; - break; - } - echo ''; - } - } ?> -
    -
    -
    - - - diff --git a/src/PHPCensor/View/WidgetLastBuilds/index.phtml b/src/PHPCensor/View/WidgetLastBuilds/index.phtml deleted file mode 100644 index 24bc747..0000000 --- a/src/PHPCensor/View/WidgetLastBuilds/index.phtml +++ /dev/null @@ -1,13 +0,0 @@ - -
    -
    -

    -
    -
    - -
    -
    diff --git a/src/PHPCensor/View/WidgetLastBuilds/update.phtml b/src/PHPCensor/View/WidgetLastBuilds/update.phtml deleted file mode 100644 index b903781..0000000 --- a/src/PHPCensor/View/WidgetLastBuilds/update.phtml +++ /dev/null @@ -1,117 +0,0 @@ - -
      - - - getEnvironment(); - $branches = $build->getExtra('branches'); - - switch ($build->getStatus()) { - case Build::STATUS_PENDING: - $updated = $build->getCreateDate(); - $label = Lang::get('pending'); - $color = 'blue'; - break; - - case Build::STATUS_RUNNING: - $updated = $build->getStartDate(); - $label = Lang::get('running'); - $color = 'yellow'; - break; - - case Build::STATUS_SUCCESS: - $updated = $build->getFinishDate(); - $label = Lang::get('success'); - $color = 'green'; - break; - - case Build::STATUS_FAILED: - $updated = $build->getFinishDate(); - $label = Lang::get('failed'); - $color = 'red'; - break; - } - - if (!$updated) { - $updated = $build->getCreateDate(); - } - - if ($updated->format('Y-m-d') != $last->format('Y-m-d')): $last = $updated; - ?> -
    • - - format('Y-m-d'); ?> - -
    • - - - - -
    • - -
      - - format('H:i:s'); - if ($build->getStatus() != Build::STATUS_PENDING) { - echo ' — ' . $build->getDuration(); ?> - -

      - - getProject()->getTitle(); ?> - - - — - - Build #getId(); ?> - - — - getSourceHumanize()); ?> -

      - -
      - getBranch(); ?> - - getTag()): ?> / - - - - - getCommitId())) { - echo ' — '; - echo sprintf( - '%s %s', - $build->getCommitLink(), - substr($build->getCommitId(), 0, 7), - $build->getCommitterEmail() ? ('(' . $build->getCommitterEmail() . ')') : '' - ); - if (!empty($build->getCommitMessage())) { - echo ' — '; - print $build->getCommitMessage(); - } - } - ?> -
      -
      -
    • - - - - -
    • - -
    • -
    diff --git a/src/PHPCensor/View/exception.phtml b/src/PHPCensor/View/exception.phtml deleted file mode 100644 index 69aeb12..0000000 --- a/src/PHPCensor/View/exception.phtml +++ /dev/null @@ -1,16 +0,0 @@ -
    -
    -

    Sorry, there was a problem

    -
    - - -
    - Message: getMessage(); ?>
    - File: getFile(); ?>
    - Line: getLine(); ?>
    - Trace: -
    getTraceAsString(); ?>
    -
    -
    \ No newline at end of file diff --git a/src/PHPCensor/View/layout.phtml b/src/PHPCensor/View/layout.phtml deleted file mode 100644 index 133af09..0000000 --- a/src/PHPCensor/View/layout.phtml +++ /dev/null @@ -1,257 +0,0 @@ - - - - - - <?php print $title; ?><?php print !empty($subtitle) ? ' - ' . $subtitle : ''; ?> - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    - - -
    - - - -
    - - - - -
    '; - } - ?> - - - - - - - diff --git a/src/PHPCensor/View/pagination.phtml b/src/PHPCensor/View/pagination.phtml deleted file mode 100644 index c1bc12c..0000000 --- a/src/PHPCensor/View/pagination.phtml +++ /dev/null @@ -1,28 +0,0 @@ - -
      - getPrevUrl()): ?> -
    • - - - getPages() as $pageArray): ?> - -
    • > - -
    • - -
    • - - - - getNextUrl()): ?> -
    • - -
    diff --git a/src/PHPCensor/Worker/BuildWorker.php b/src/PHPCensor/Worker/BuildWorker.php deleted file mode 100644 index 7f19842..0000000 --- a/src/PHPCensor/Worker/BuildWorker.php +++ /dev/null @@ -1,175 +0,0 @@ -host = $host; - $this->queue = $queue; - $this->pheanstalk = new Pheanstalk($this->host); - } - - /** - * @param Logger $logger - */ - public function setLogger(Logger $logger) - { - $this->logger = $logger; - } - - /** - * Start the worker. - */ - public function startWorker() - { - $this->pheanstalk->watch($this->queue); - $this->pheanstalk->ignore('default'); - $buildStore = Factory::getStore('Build'); - - while ($this->run) { - // Get a job from the queue: - $job = $this->pheanstalk->reserve(); - - // Get the job data and run the job: - $jobData = json_decode($job->getData(), true); - - if (!$this->verifyJob($job, $jobData)) { - continue; - } - - $this->logger->addInfo('Received build #'.$jobData['build_id'].' from Beanstalkd'); - - try { - $build = BuildFactory::getBuildById($jobData['build_id']); - } catch (\Exception $ex) { - $this->logger->addWarning('Build #' . $jobData['build_id'] . ' does not exist in the database.'); - $this->pheanstalk->delete($job); - continue; - } - - // Logging relevant to this build should be stored - // against the build itself. - $buildDbLog = new BuildDBLogHandler($build, Logger::INFO); - $this->logger->pushHandler($buildDbLog); - - try { - $builder = new Builder($build, $this->logger); - $builder->execute(); - } catch (\PDOException $ex) { - // If we've caught a PDO Exception, it is probably not the fault of the build, but of a failed - // connection or similar. Release the job and kill the worker. - $this->run = false; - $this->pheanstalk->release($job); - unset($job); - } catch (\Exception $ex) { - $this->logger->addError($ex->getMessage()); - - $build->setStatus(Build::STATUS_FAILED); - $build->setFinishDate(new \DateTime()); - $build->setLog($build->getLog() . PHP_EOL . PHP_EOL . $ex->getMessage()); - $buildStore->save($build); - $build->sendStatusPostback(); - } - - // After execution we no longer want to record the information - // back to this specific build so the handler should be removed. - $this->logger->popHandler(); - // destructor implicitly call flush - unset($buildDbLog); - - // Delete the job when we're done: - if (!empty($job)) { - $this->pheanstalk->delete($job); - } - } - } - - /** - * Stops the worker after the current build. - */ - public function stopWorker() - { - $this->run = false; - } - - /** - * Checks that the job received is actually, and has a valid type. - * - * @param Job $job - * @param array $jobData - * - * @return boolean - */ - protected function verifyJob(Job $job, $jobData) - { - if (empty($jobData) || !is_array($jobData)) { - $this->pheanstalk->delete($job); - return false; - } - - if (!array_key_exists('type', $jobData) || $jobData['type'] !== 'php-censor.build') { - $this->pheanstalk->delete($job); - return false; - } - - return true; - } -} diff --git a/src/PHPCensor/ZeroConfigPluginInterface.php b/src/PHPCensor/ZeroConfigPluginInterface.php deleted file mode 100644 index 06ab844..0000000 --- a/src/PHPCensor/ZeroConfigPluginInterface.php +++ /dev/null @@ -1,20 +0,0 @@ - - */ -interface ZeroConfigPluginInterface -{ - /** - * @param string $stage - * @param Builder $builder - * @param Build $build - * - * @return mixed - */ - public static function canExecute($stage, Builder $builder, Build $build); -} From 9f9a26452708ff832a644f49ecb77fde8b59c8a7 Mon Sep 17 00:00:00 2001 From: "alexey.boyko" Date: Wed, 13 Dec 2017 21:10:38 +0300 Subject: [PATCH 14/19] Permissions test --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index fe21bf1..bbf9f51 100644 --- a/README.md +++ b/README.md @@ -3,3 +3,4 @@ PHP Censor test repository PHP Censor test repository. + From 3d278445e97ef033331ee81aef4e4bb81d0efb00 Mon Sep 17 00:00:00 2001 From: Dmitry Khomutov Date: Sat, 20 Jan 2018 12:59:20 +0700 Subject: [PATCH 15/19] Added pdepend. --- .php-censor.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.php-censor.yml b/.php-censor.yml index a760f43..2379348 100644 --- a/.php-censor.yml +++ b/.php-censor.yml @@ -13,6 +13,9 @@ test: config: - phpunit.xml + pdepend: + directory: ./src + php_mess_detector: allow_failures: true From 7230272c5d4e1966c6286c02cb04f2fdf78b604d Mon Sep 17 00:00:00 2001 From: Dmitry Khomutov Date: Sat, 8 Dec 2018 12:56:57 +0700 Subject: [PATCH 16/19] Testing --- tests/src/ApplicationTest.php | 84 +++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/tests/src/ApplicationTest.php b/tests/src/ApplicationTest.php index 8cb00ae..8794925 100644 --- a/tests/src/ApplicationTest.php +++ b/tests/src/ApplicationTest.php @@ -9,6 +9,90 @@ class ApplicationTest extends \PHPUnit_Framework_TestCase public function testSample() { $application = new Application(); + + sleep(10); + + $this->assertInstanceOf('PHPCensorTest\\Application', $application); + } + + public function testSample2() + { + $application = new Application(); + + sleep(10); + + $this->assertInstanceOf('PHPCensorTest\\Application', $application); + } + + public function testSample3() + { + $application = new Application(); + + sleep(10); + + $this->assertInstanceOf('PHPCensorTest\\Application', $application); + } + + public function testSample4() + { + $application = new Application(); + + sleep(10); + + $this->assertInstanceOf('PHPCensorTest\\Application', $application); + } + + public function testSample5() + { + $application = new Application(); + + sleep(10); + + $this->assertInstanceOf('PHPCensorTest\\Application', $application); + } + + public function testSample6() + { + $application = new Application(); + + sleep(10); + + $this->assertInstanceOf('PHPCensorTest\\Application', $application); + } + + public function testSample7() + { + $application = new Application(); + + sleep(10); + + $this->assertInstanceOf('PHPCensorTest\\Application', $application); + } + + public function testSample8() + { + $application = new Application(); + + sleep(10); + + $this->assertInstanceOf('PHPCensorTest\\Application', $application); + } + + public function testSample9() + { + $application = new Application(); + + sleep(10); + + $this->assertInstanceOf('PHPCensorTest\\Application', $application); + } + + public function testSample10() + { + $application = new Application(); + + sleep(10); + $this->assertInstanceOf('PHPCensorTest\\Application', $application); } } From 1b3c79c7dbd61a3b5bdc1167ee31f61a7e032491 Mon Sep 17 00:00:00 2001 From: Dmitry Khomutov Date: Fri, 8 Feb 2019 21:10:28 +0700 Subject: [PATCH 17/19] Added incomplete tests. --- tests/src/ApplicationTest.php | 39 +++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/tests/src/ApplicationTest.php b/tests/src/ApplicationTest.php index 8794925..77c3d70 100644 --- a/tests/src/ApplicationTest.php +++ b/tests/src/ApplicationTest.php @@ -95,4 +95,43 @@ public function testSample10() $this->assertInstanceOf('PHPCensorTest\\Application', $application); } + + public function testSampleIncomplete() + { + $this->markTestIncomplete( + 'Incomplete test "testSampleIncomplete"!' + ); + + $application = new Application(); + + sleep(10); + + $this->assertInstanceOf('PHPCensorTest\\Application', $application); + } + + public function testSampleIncomplete2() + { + $this->markTestIncomplete( + 'Incomplete test "testSampleIncomplete2"!' + ); + + $application = new Application(); + + sleep(10); + + $this->assertInstanceOf('PHPCensorTest\\Application', $application); + } + + public function testSampleIncomplete3() + { + $this->markTestIncomplete( + 'Incomplete test "testSampleIncomplete2"!' + ); + + $application = new Application(); + + sleep(10); + + $this->assertInstanceOf('PHPCensorTest\\Application', $application); + } } From 8706a577153feca623c5fccb7eb11de1920b44ec Mon Sep 17 00:00:00 2001 From: Dmitry Khomutov Date: Sun, 1 Aug 2021 16:16:19 +0700 Subject: [PATCH 18/19] Fixed email plugin name in .php-censor.yml (email -> email_notify). --- .php-censor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.php-censor.yml b/.php-censor.yml index 2379348..a6a9904 100644 --- a/.php-censor.yml +++ b/.php-censor.yml @@ -43,5 +43,5 @@ test: allow_failures: true complete: - email: + email_notify: default_mailto_address: poisoncorpsee@gmail.com From 3c42d07e156f0ed27fd034f157ec6b332243e702 Mon Sep 17 00:00:00 2001 From: Dmitry Khomutov Date: Sun, 1 Aug 2021 23:32:13 +0700 Subject: [PATCH 19/19] Updated dependencies for CI (PHP Censor v2). --- composer.json | 9 +- composer.lock | 3867 +++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 3273 insertions(+), 603 deletions(-) diff --git a/composer.json b/composer.json index 2b5262c..d60ce93 100644 --- a/composer.json +++ b/composer.json @@ -18,6 +18,13 @@ "php": ">=5.6.0" }, "require-dev": { - "phpunit/phpunit": "5.7.*" + "phpunit/phpunit": "~5.7.0", + "codeception/codeception": "~2.3.0", + "phpmd/phpmd": "~2.6.0", + "sebastian/phpcpd": "~2.0.0", + "squizlabs/php_codesniffer": "~3.5.0", + "php-censor/phpdoc-checker": "^1.0", + "phploc/phploc": "~4.0.0", + "jakub-onderka/php-parallel-lint": "~0.9.0" } } diff --git a/composer.lock b/composer.lock index 841c413..7cf602e 100644 --- a/composer.lock +++ b/composer.lock @@ -1,45 +1,46 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "29ce96e9fdb0f8ffa5496125cdd05985", + "content-hash": "07f7aa8ce27f598e0f0f528cdfe4ccfa", "packages": [], "packages-dev": [ { - "name": "doctrine/instantiator", - "version": "1.0.5", + "name": "behat/gherkin", + "version": "v4.4.5", "source": { "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" + "url": "https://github.com/Behat/Gherkin.git", + "reference": "5c14cff4f955b17d20d088dec1bde61c0539ec74" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", - "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", + "url": "https://api.github.com/repos/Behat/Gherkin/zipball/5c14cff4f955b17d20d088dec1bde61c0539ec74", + "reference": "5c14cff4f955b17d20d088dec1bde61c0539ec74", "shasum": "" }, "require": { - "php": ">=5.3,<8.0-DEV" + "php": ">=5.3.1" }, "require-dev": { - "athletic/athletic": "~0.1.8", - "ext-pdo": "*", - "ext-phar": "*", - "phpunit/phpunit": "~4.0", - "squizlabs/php_codesniffer": "~2.0" + "phpunit/phpunit": "~4.5|~5", + "symfony/phpunit-bridge": "~2.7|~3", + "symfony/yaml": "~2.3|~3" + }, + "suggest": { + "symfony/yaml": "If you want to parse features, represented in YAML files" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "4.4-dev" } }, "autoload": { - "psr-4": { - "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + "psr-0": { + "Behat\\Gherkin": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -48,145 +49,195 @@ ], "authors": [ { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "http://ocramius.github.com/" + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" } ], - "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://github.com/doctrine/instantiator", + "description": "Gherkin DSL parser for PHP 5.3", + "homepage": "http://behat.org/", "keywords": [ - "constructor", - "instantiate" - ], - "time": "2015-06-14T21:17:01+00:00" + "BDD", + "Behat", + "Cucumber", + "DSL", + "gherkin", + "parser" + ], + "support": { + "issues": "https://github.com/Behat/Gherkin/issues", + "source": "https://github.com/Behat/Gherkin/tree/master" + }, + "time": "2016-10-30T11:50:56+00:00" }, { - "name": "myclabs/deep-copy", - "version": "1.6.0", + "name": "codeception/codeception", + "version": "2.3.9", "source": { "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "5a5a9fc8025a08d8919be87d6884d5a92520cefe" + "url": "https://github.com/Codeception/Codeception.git", + "reference": "104f46fa0bde339f1bcc3a375aac21eb36e65a1e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/5a5a9fc8025a08d8919be87d6884d5a92520cefe", - "reference": "5a5a9fc8025a08d8919be87d6884d5a92520cefe", + "url": "https://api.github.com/repos/Codeception/Codeception/zipball/104f46fa0bde339f1bcc3a375aac21eb36e65a1e", + "reference": "104f46fa0bde339f1bcc3a375aac21eb36e65a1e", "shasum": "" }, "require": { - "php": ">=5.4.0" + "behat/gherkin": "~4.4.0", + "codeception/stub": "^1.0", + "ext-json": "*", + "ext-mbstring": "*", + "facebook/webdriver": ">=1.1.3 <2.0", + "guzzlehttp/guzzle": ">=4.1.4 <7.0", + "guzzlehttp/psr7": "~1.0", + "php": ">=5.4.0 <8.0", + "phpunit/php-code-coverage": ">=2.2.4 <6.0", + "phpunit/phpunit": ">=4.8.28 <5.0.0 || >=5.6.3 <7.0", + "sebastian/comparator": ">1.1 <3.0", + "sebastian/diff": ">=1.4 <3.0", + "symfony/browser-kit": ">=2.7 <5.0", + "symfony/console": ">=2.7 <5.0", + "symfony/css-selector": ">=2.7 <5.0", + "symfony/dom-crawler": ">=2.7 <5.0", + "symfony/event-dispatcher": ">=2.7 <5.0", + "symfony/finder": ">=2.7 <5.0", + "symfony/yaml": ">=2.7 <5.0" }, "require-dev": { - "doctrine/collections": "1.*", - "phpunit/phpunit": "~4.1" + "codeception/specify": "~0.3", + "facebook/graph-sdk": "~5.3", + "flow/jsonpath": "~0.2", + "monolog/monolog": "~1.8", + "pda/pheanstalk": "~3.0", + "php-amqplib/php-amqplib": "~2.4", + "predis/predis": "^1.0", + "squizlabs/php_codesniffer": "~2.0", + "symfony/process": ">=2.7 <5.0", + "vlucas/phpdotenv": "^2.4.0" + }, + "suggest": { + "aws/aws-sdk-php": "For using AWS Auth in REST module and Queue module", + "codeception/phpbuiltinserver": "Start and stop PHP built-in web server for your tests", + "codeception/specify": "BDD-style code blocks", + "codeception/verify": "BDD-style assertions", + "flow/jsonpath": "For using JSONPath in REST module", + "league/factory-muffin": "For DataFactory module", + "league/factory-muffin-faker": "For Faker support in DataFactory module", + "phpseclib/phpseclib": "for SFTP option in FTP Module", + "stecman/symfony-console-completion": "For BASH autocompletion", + "symfony/phpunit-bridge": "For phpunit-bridge support" }, + "bin": [ + "codecept" + ], "type": "library", + "extra": { + "branch-alias": [] + }, "autoload": { "psr-4": { - "DeepCopy\\": "src/DeepCopy/" + "Codeception\\": "src\\Codeception", + "Codeception\\Extension\\": "ext" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "description": "Create deep copies (clones) of your objects", - "homepage": "https://github.com/myclabs/DeepCopy", + "authors": [ + { + "name": "Michael Bodnarchuk", + "email": "davert@mail.ua", + "homepage": "http://codegyre.com" + } + ], + "description": "BDD-style testing framework", + "homepage": "http://codeception.com/", "keywords": [ - "clone", - "copy", - "duplicate", - "object", - "object graph" + "BDD", + "TDD", + "acceptance testing", + "functional testing", + "unit testing" ], - "time": "2017-01-26T22:05:40+00:00" + "support": { + "issues": "https://github.com/Codeception/Codeception/issues", + "source": "https://github.com/Codeception/Codeception/tree/2.3.9" + }, + "time": "2018-02-26T23:29:41+00:00" }, { - "name": "phpdocumentor/reflection-common", - "version": "1.0", + "name": "codeception/stub", + "version": "1.0.4", "source": { "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c" + "url": "https://github.com/Codeception/Stub.git", + "reference": "681b62348837a5ef07d10d8a226f5bc358cc8805" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/144c307535e82c8fdcaacbcfc1d6d8eeb896687c", - "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c", + "url": "https://api.github.com/repos/Codeception/Stub/zipball/681b62348837a5ef07d10d8a226f5bc358cc8805", + "reference": "681b62348837a5ef07d10d8a226f5bc358cc8805", "shasum": "" }, "require": { - "php": ">=5.5" + "phpunit/phpunit-mock-objects": ">2.3 <7.0" }, "require-dev": { - "phpunit/phpunit": "^4.6" + "phpunit/phpunit": ">=4.8 <8.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src" - ] + "Codeception\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "authors": [ - { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" - } - ], - "description": "Common reflection classes used by phpdocumentor to reflect the code structure", - "homepage": "http://www.phpdoc.org", - "keywords": [ - "FQSEN", - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" - ], - "time": "2015-12-27T11:43:31+00:00" + "description": "Flexible Stub wrapper for PHPUnit's Mock Builder", + "support": { + "issues": "https://github.com/Codeception/Stub/issues", + "source": "https://github.com/Codeception/Stub/tree/master" + }, + "time": "2018-05-17T09:31:08+00:00" }, { - "name": "phpdocumentor/reflection-docblock", - "version": "3.1.1", + "name": "doctrine/instantiator", + "version": "1.0.5", "source": { "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "8331b5efe816ae05461b7ca1e721c01b46bafb3e" + "url": "https://github.com/doctrine/instantiator.git", + "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/8331b5efe816ae05461b7ca1e721c01b46bafb3e", - "reference": "8331b5efe816ae05461b7ca1e721c01b46bafb3e", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", + "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", "shasum": "" }, "require": { - "php": ">=5.5", - "phpdocumentor/reflection-common": "^1.0@dev", - "phpdocumentor/type-resolver": "^0.2.0", - "webmozart/assert": "^1.0" + "php": ">=5.3,<8.0-DEV" }, "require-dev": { - "mockery/mockery": "^0.9.4", - "phpunit/phpunit": "^4.4" + "athletic/athletic": "~0.1.8", + "ext-pdo": "*", + "ext-phar": "*", + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "~2.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" } }, "notification-url": "https://packagist.org/downloads/", @@ -195,95 +246,131 @@ ], "authors": [ { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "http://ocramius.github.com/" } ], - "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2016-09-30T07:12:33+00:00" + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://github.com/doctrine/instantiator", + "keywords": [ + "constructor", + "instantiate" + ], + "support": { + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/master" + }, + "time": "2015-06-14T21:17:01+00:00" }, { - "name": "phpdocumentor/type-resolver", - "version": "0.2.1", + "name": "facebook/webdriver", + "version": "1.7.1", "source": { "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb" + "url": "https://github.com/php-webdriver/php-webdriver-archive.git", + "reference": "e43de70f3c7166169d0f14a374505392734160e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb", - "reference": "e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb", + "url": "https://api.github.com/repos/php-webdriver/php-webdriver-archive/zipball/e43de70f3c7166169d0f14a374505392734160e5", + "reference": "e43de70f3c7166169d0f14a374505392734160e5", "shasum": "" }, "require": { - "php": ">=5.5", - "phpdocumentor/reflection-common": "^1.0" + "ext-curl": "*", + "ext-json": "*", + "ext-mbstring": "*", + "ext-zip": "*", + "php": "^5.6 || ~7.0", + "symfony/process": "^2.8 || ^3.1 || ^4.0" }, "require-dev": { - "mockery/mockery": "^0.9.4", - "phpunit/phpunit": "^5.2||^4.8.24" + "friendsofphp/php-cs-fixer": "^2.0", + "jakub-onderka/php-parallel-lint": "^0.9.2", + "php-coveralls/php-coveralls": "^2.0", + "php-mock/php-mock-phpunit": "^1.1", + "phpunit/phpunit": "^5.7", + "sebastian/environment": "^1.3.4 || ^2.0 || ^3.0", + "squizlabs/php_codesniffer": "^2.6", + "symfony/var-dumper": "^3.3 || ^4.0" + }, + "suggest": { + "ext-SimpleXML": "For Firefox profile creation" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-community": "1.5-dev" } }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] + "Facebook\\WebDriver\\": "lib/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } + "Apache-2.0" ], - "time": "2016-11-25T06:54:22+00:00" + "description": "A PHP client for Selenium WebDriver", + "homepage": "https://github.com/facebook/php-webdriver", + "keywords": [ + "facebook", + "php", + "selenium", + "webdriver" + ], + "support": { + "forum": "https://www.facebook.com/groups/phpwebdriver/", + "issues": "https://github.com/facebook/php-webdriver/issues", + "source": "https://github.com/facebook/php-webdriver" + }, + "abandoned": "php-webdriver/webdriver", + "time": "2019-06-13T08:02:18+00:00" }, { - "name": "phpspec/prophecy", - "version": "v1.6.2", + "name": "guzzlehttp/guzzle", + "version": "6.5.5", "source": { "type": "git", - "url": "https://github.com/phpspec/prophecy.git", - "reference": "6c52c2722f8460122f96f86346600e1077ce22cb" + "url": "https://github.com/guzzle/guzzle.git", + "reference": "9d4290de1cfd701f38099ef7e183b64b4b7b0c5e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/6c52c2722f8460122f96f86346600e1077ce22cb", - "reference": "6c52c2722f8460122f96f86346600e1077ce22cb", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/9d4290de1cfd701f38099ef7e183b64b4b7b0c5e", + "reference": "9d4290de1cfd701f38099ef7e183b64b4b7b0c5e", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.0.2", - "php": "^5.3|^7.0", - "phpdocumentor/reflection-docblock": "^2.0|^3.0.2", - "sebastian/comparator": "^1.1", - "sebastian/recursion-context": "^1.0|^2.0" + "ext-json": "*", + "guzzlehttp/promises": "^1.0", + "guzzlehttp/psr7": "^1.6.1", + "php": ">=5.5", + "symfony/polyfill-intl-idn": "^1.17.0" }, "require-dev": { - "phpspec/phpspec": "^2.0", - "phpunit/phpunit": "^4.8 || ^5.6.5" + "ext-curl": "*", + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0", + "psr/log": "^1.1" + }, + "suggest": { + "psr/log": "Required for using the Log middleware" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.6.x-dev" + "dev-master": "6.5-dev" } }, "autoload": { - "psr-0": { - "Prophecy\\": "src/" - } + "psr-4": { + "GuzzleHttp\\": "src/" + }, + "files": [ + "src/functions_include.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -291,253 +378,293 @@ ], "authors": [ { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - }, - { - "name": "Marcello Duarte", - "email": "marcello.duarte@gmail.com" + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" } ], - "description": "Highly opinionated mocking framework for PHP 5.3+", - "homepage": "https://github.com/phpspec/prophecy", + "description": "Guzzle is a PHP HTTP client library", + "homepage": "http://guzzlephp.org/", "keywords": [ - "Double", - "Dummy", - "fake", - "mock", - "spy", - "stub" - ], - "time": "2016-11-21T14:58:47+00:00" + "client", + "curl", + "framework", + "http", + "http client", + "rest", + "web service" + ], + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/6.5" + }, + "time": "2020-06-16T21:01:06+00:00" }, { - "name": "phpunit/php-code-coverage", - "version": "4.0.5", + "name": "guzzlehttp/promises", + "version": "1.4.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "c19cfc7cbb0e9338d8c469c7eedecc2a428b0971" + "url": "https://github.com/guzzle/promises.git", + "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/c19cfc7cbb0e9338d8c469c7eedecc2a428b0971", - "reference": "c19cfc7cbb0e9338d8c469c7eedecc2a428b0971", + "url": "https://api.github.com/repos/guzzle/promises/zipball/8e7d04f1f6450fef59366c399cfad4b9383aa30d", + "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0", - "phpunit/php-file-iterator": "~1.3", - "phpunit/php-text-template": "~1.2", - "phpunit/php-token-stream": "^1.4.2", - "sebastian/code-unit-reverse-lookup": "~1.0", - "sebastian/environment": "^1.3.2 || ^2.0", - "sebastian/version": "~1.0|~2.0" + "php": ">=5.5" }, "require-dev": { - "ext-xdebug": ">=2.1.4", - "phpunit/phpunit": "^5.4" - }, - "suggest": { - "ext-dom": "*", - "ext-xdebug": ">=2.4.0", - "ext-xmlwriter": "*" + "symfony/phpunit-bridge": "^4.4 || ^5.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0.x-dev" + "dev-master": "1.4-dev" } }, "autoload": { - "classmap": [ - "src/" + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + }, + "files": [ + "src/functions_include.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" } ], - "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", - "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "description": "Guzzle promises library", "keywords": [ - "coverage", - "testing", - "xunit" + "promise" ], - "time": "2017-01-20T15:06:43+00:00" + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/1.4.1" + }, + "time": "2021-03-07T09:25:29+00:00" }, { - "name": "phpunit/php-file-iterator", - "version": "1.4.2", + "name": "guzzlehttp/psr7", + "version": "1.8.2", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5" + "url": "https://github.com/guzzle/psr7.git", + "reference": "dc960a912984efb74d0a90222870c72c87f10c91" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/3cc8f69b3028d0f96a9078e6295d86e9bf019be5", - "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/dc960a912984efb74d0a90222870c72c87f10c91", + "reference": "dc960a912984efb74d0a90222870c72c87f10c91", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": ">=5.4.0", + "psr/http-message": "~1.0", + "ralouphie/getallheaders": "^2.0.5 || ^3.0.0" + }, + "provide": { + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "ext-zlib": "*", + "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.10" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4.x-dev" + "dev-master": "1.7-dev" } }, "autoload": { - "classmap": [ - "src/" + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + }, + "files": [ + "src/functions_include.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Schultze", + "homepage": "https://github.com/Tobion" } ], - "description": "FilterIterator implementation that filters files based on a list of suffixes.", - "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "description": "PSR-7 message implementation that also provides common utility methods", "keywords": [ - "filesystem", - "iterator" - ], - "time": "2016-10-03T07:40:28+00:00" + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ], + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/1.8.2" + }, + "time": "2021-04-26T09:17:50+00:00" }, { - "name": "phpunit/php-text-template", - "version": "1.2.1", + "name": "jakub-onderka/php-parallel-lint", + "version": "v0.9.2", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" + "url": "https://github.com/JakubOnderka/PHP-Parallel-Lint.git", + "reference": "2ead2e4043ab125bee9554f356e0a86742c2d4fa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", - "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "url": "https://api.github.com/repos/JakubOnderka/PHP-Parallel-Lint/zipball/2ead2e4043ab125bee9554f356e0a86742c2d4fa", + "reference": "2ead2e4043ab125bee9554f356e0a86742c2d4fa", "shasum": "" }, "require": { "php": ">=5.3.3" }, + "require-dev": { + "jakub-onderka/php-console-highlighter": "~0.3", + "nette/tester": "~1.3" + }, + "suggest": { + "jakub-onderka/php-console-highlighter": "Highlight syntax in code snippet" + }, + "bin": [ + "parallel-lint" + ], "type": "library", "autoload": { "classmap": [ - "src/" + "./" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "BSD-2-Clause" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Jakub Onderka", + "email": "jakub.onderka@gmail.com" } ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", - "keywords": [ - "template" - ], - "time": "2015-06-21T13:50:34+00:00" + "description": "This tool check syntax of PHP files about 20x faster than serial check.", + "homepage": "https://github.com/JakubOnderka/PHP-Parallel-Lint", + "support": { + "issues": "https://github.com/JakubOnderka/PHP-Parallel-Lint/issues", + "source": "https://github.com/JakubOnderka/PHP-Parallel-Lint/tree/master" + }, + "abandoned": "php-parallel-lint/php-parallel-lint", + "time": "2015-12-15T10:42:16+00:00" }, { - "name": "phpunit/php-timer", - "version": "1.0.8", + "name": "myclabs/deep-copy", + "version": "1.7.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "38e9124049cf1a164f1e4537caf19c99bf1eb260" + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/38e9124049cf1a164f1e4537caf19c99bf1eb260", - "reference": "38e9124049cf1a164f1e4537caf19c99bf1eb260", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", + "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": "^5.6 || ^7.0" }, "require-dev": { - "phpunit/phpunit": "~4|~5" + "doctrine/collections": "^1.0", + "doctrine/common": "^2.6", + "phpunit/phpunit": "^4.1" }, "type": "library", "autoload": { - "classmap": [ - "src/" + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + }, + "files": [ + "src/DeepCopy/deep_copy.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" - } + "MIT" ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", + "description": "Create deep copies (clones) of your objects", "keywords": [ - "timer" + "clone", + "copy", + "duplicate", + "object", + "object graph" ], - "time": "2016-05-12T18:03:57+00:00" + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.x" + }, + "time": "2017-10-19T19:58:43+00:00" }, { - "name": "phpunit/php-token-stream", - "version": "1.4.9", + "name": "nikic/php-parser", + "version": "v3.1.5", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "3b402f65a4cc90abf6e1104e388b896ce209631b" + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "bb87e28e7d7b8d9a7fda231d37457c9210faf6ce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/3b402f65a4cc90abf6e1104e388b896ce209631b", - "reference": "3b402f65a4cc90abf6e1104e388b896ce209631b", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/bb87e28e7d7b8d9a7fda231d37457c9210faf6ce", + "reference": "bb87e28e7d7b8d9a7fda231d37457c9210faf6ce", "shasum": "" }, "require": { "ext-tokenizer": "*", - "php": ">=5.3.3" + "php": ">=5.5" }, "require-dev": { - "phpunit/phpunit": "~4.2" + "phpunit/phpunit": "~4.0|~5.0" }, + "bin": [ + "bin/php-parse" + ], "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4-dev" + "dev-master": "3.0-dev" } }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -545,343 +672,380 @@ ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "Nikita Popov" } ], - "description": "Wrapper around PHP's tokenizer extension.", - "homepage": "https://github.com/sebastianbergmann/php-token-stream/", + "description": "A PHP parser written in PHP", "keywords": [ - "tokenizer" + "parser", + "php" ], - "time": "2016-11-15T14:06:22+00:00" + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v3.1.5" + }, + "time": "2018-02-28T20:30:58+00:00" }, { - "name": "phpunit/phpunit", - "version": "5.7.13", + "name": "paragonie/random_compat", + "version": "v2.0.20", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "60ebeed87a35ea46fd7f7d8029df2d6f013ebb34" + "url": "https://github.com/paragonie/random_compat.git", + "reference": "0f1f60250fccffeaf5dda91eea1c018aed1adc2a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/60ebeed87a35ea46fd7f7d8029df2d6f013ebb34", - "reference": "60ebeed87a35ea46fd7f7d8029df2d6f013ebb34", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/0f1f60250fccffeaf5dda91eea1c018aed1adc2a", + "reference": "0f1f60250fccffeaf5dda91eea1c018aed1adc2a", "shasum": "" }, "require": { - "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-xml": "*", - "myclabs/deep-copy": "~1.3", - "php": "^5.6 || ^7.0", - "phpspec/prophecy": "^1.6.2", - "phpunit/php-code-coverage": "^4.0.4", - "phpunit/php-file-iterator": "~1.4", - "phpunit/php-text-template": "~1.2", - "phpunit/php-timer": "^1.0.6", - "phpunit/phpunit-mock-objects": "^3.2", - "sebastian/comparator": "^1.2.4", - "sebastian/diff": "~1.2", - "sebastian/environment": "^1.3.4 || ^2.0", - "sebastian/exporter": "~2.0", - "sebastian/global-state": "^1.1", - "sebastian/object-enumerator": "~2.0", - "sebastian/resource-operations": "~1.0", - "sebastian/version": "~1.0|~2.0", - "symfony/yaml": "~2.1|~3.0" - }, - "conflict": { - "phpdocumentor/reflection-docblock": "3.0.2" + "php": ">=5.2.0" }, "require-dev": { - "ext-pdo": "*" + "phpunit/phpunit": "4.*|5.*" }, "suggest": { - "ext-xdebug": "*", - "phpunit/php-invoker": "~1.1" + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." }, - "bin": [ - "phpunit" - ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.7.x-dev" - } - }, "autoload": { - "classmap": [ - "src/" + "files": [ + "lib/random.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" } ], - "description": "The PHP Unit Testing framework.", - "homepage": "https://phpunit.de/", + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", "keywords": [ - "phpunit", - "testing", - "xunit" + "csprng", + "polyfill", + "pseudorandom", + "random" ], - "time": "2017-02-10T09:05:10+00:00" + "support": { + "email": "info@paragonie.com", + "issues": "https://github.com/paragonie/random_compat/issues", + "source": "https://github.com/paragonie/random_compat" + }, + "time": "2021-04-17T09:33:01+00:00" }, { - "name": "phpunit/phpunit-mock-objects", - "version": "3.4.3", + "name": "pdepend/pdepend", + "version": "2.10.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "3ab72b65b39b491e0c011e2e09bb2206c2aa8e24" + "url": "https://github.com/pdepend/pdepend.git", + "reference": "1fd30f4352b630ad53fec3fd5e8b8ba760f85596" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/3ab72b65b39b491e0c011e2e09bb2206c2aa8e24", - "reference": "3ab72b65b39b491e0c011e2e09bb2206c2aa8e24", + "url": "https://api.github.com/repos/pdepend/pdepend/zipball/1fd30f4352b630ad53fec3fd5e8b8ba760f85596", + "reference": "1fd30f4352b630ad53fec3fd5e8b8ba760f85596", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.0.2", - "php": "^5.6 || ^7.0", - "phpunit/php-text-template": "^1.2", - "sebastian/exporter": "^1.2 || ^2.0" - }, - "conflict": { - "phpunit/phpunit": "<5.4.0" + "php": ">=5.3.7", + "symfony/config": "^2.3.0|^3|^4|^5", + "symfony/dependency-injection": "^2.3.0|^3|^4|^5", + "symfony/filesystem": "^2.3.0|^3|^4|^5" }, "require-dev": { - "phpunit/phpunit": "^5.4" - }, - "suggest": { - "ext-soap": "*" + "easy-doc/easy-doc": "0.0.0|^1.2.3", + "gregwar/rst": "^1.0", + "phpunit/phpunit": "^4.8.36|^5.7.27", + "squizlabs/php_codesniffer": "^2.0.0" }, + "bin": [ + "src/bin/pdepend" + ], "type": "library", "extra": { "branch-alias": { - "dev-master": "3.2.x-dev" + "dev-master": "2.x-dev" } }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "PDepend\\": "src/main/php/PDepend" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], - "authors": [ + "description": "Official version of pdepend to be handled with Composer", + "support": { + "issues": "https://github.com/pdepend/pdepend/issues", + "source": "https://github.com/pdepend/pdepend/tree/2.10.0" + }, + "funding": [ { - "name": "Sebastian Bergmann", - "email": "sb@sebastian-bergmann.de", - "role": "lead" + "url": "https://tidelift.com/funding/github/packagist/pdepend/pdepend", + "type": "tidelift" } ], - "description": "Mock Object library for PHPUnit", - "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", - "keywords": [ - "mock", - "xunit" - ], - "time": "2016-12-08T20:27:08+00:00" + "time": "2021-07-20T09:56:09+00:00" }, { - "name": "sebastian/code-unit-reverse-lookup", - "version": "1.0.0", + "name": "php-censor/phpdoc-checker", + "version": "1.0.3", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "c36f5e7cfce482fde5bf8d10d41a53591e0198fe" + "url": "https://github.com/php-censor/phpdoc-checker.git", + "reference": "fbe68791942b7a4bd86b85793eb178ae7b4ec95d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/c36f5e7cfce482fde5bf8d10d41a53591e0198fe", - "reference": "c36f5e7cfce482fde5bf8d10d41a53591e0198fe", + "url": "https://api.github.com/repos/php-censor/phpdoc-checker/zipball/fbe68791942b7a4bd86b85793eb178ae7b4ec95d", + "reference": "fbe68791942b7a4bd86b85793eb178ae7b4ec95d", "shasum": "" }, "require": { - "php": ">=5.6" - }, - "require-dev": { - "phpunit/phpunit": "~5" + "nikic/php-parser": "3.*", + "php": ">=5.6.0", + "symfony/console": "~3.4.0" }, + "bin": [ + "bin/phpdoc-checker" + ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "PhpDocChecker\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "BSD-2-Clause" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "Dan Cryer", + "email": "dan.cryer@block8.co.uk", + "homepage": "http://www.block8.co.uk", + "role": "PHP DocBlocks Checker developer" + }, + { + "name": "Dmitry Khomutov", + "email": "poisoncorpsee@gmail.com", + "homepage": "http://corpsee.com", + "role": "PHPDoc Checker developer" } ], - "description": "Looks up which function or method a line of code belongs to", - "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "time": "2016-02-13T06:45:14+00:00" + "description": "A simple tool for checking that your PHP classes and methods use PHPDocs (PHP DocBlocks Checker fork).", + "homepage": "https://github.com/php-censor/phpdoc-checker", + "keywords": [ + "checker", + "code quality", + "comment", + "docblock", + "php", + "php-censor", + "phpdoc", + "testing" + ], + "support": { + "issues": "https://github.com/php-censor/phpdoc-checker/issues", + "source": "https://github.com/php-censor/phpdoc-checker" + }, + "time": "2018-12-26T13:53:59+00:00" }, { - "name": "sebastian/comparator", - "version": "1.2.4", + "name": "phpdocumentor/reflection-common", + "version": "1.0.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be" + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2b7424b55f5047b47ac6e5ccb20b2aea4011d9be", - "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", + "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", "shasum": "" }, "require": { - "php": ">=5.3.3", - "sebastian/diff": "~1.2", - "sebastian/exporter": "~1.2 || ~2.0" + "php": ">=5.5" }, "require-dev": { - "phpunit/phpunit": "~4.4" + "phpunit/phpunit": "^4.6" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2.x-dev" + "dev-master": "1.0.x-dev" } }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src" + ] + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" } ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "http://www.github.com/sebastianbergmann/comparator", + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", "keywords": [ - "comparator", - "compare", - "equality" + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" ], - "time": "2017-01-29T09:50:25+00:00" + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/master" + }, + "time": "2017-09-11T18:02:19+00:00" }, { - "name": "sebastian/diff", - "version": "1.4.1", + "name": "phpdocumentor/reflection-docblock", + "version": "3.3.2", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e" + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "bf329f6c1aadea3299f08ee804682b7c45b326a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/13edfd8706462032c2f52b4b862974dd46b71c9e", - "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/bf329f6c1aadea3299f08ee804682b7c45b326a2", + "reference": "bf329f6c1aadea3299f08ee804682b7c45b326a2", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": "^5.6 || ^7.0", + "phpdocumentor/reflection-common": "^1.0.0", + "phpdocumentor/type-resolver": "^0.4.0", + "webmozart/assert": "^1.0" }, "require-dev": { - "phpunit/phpunit": "~4.8" + "mockery/mockery": "^0.9.4", + "phpunit/phpunit": "^4.4" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.4-dev" - } - }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src/" + ] + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "Mike van Riel", + "email": "me@mikevanriel.com" } ], - "description": "Diff implementation", - "homepage": "https://github.com/sebastianbergmann/diff", - "keywords": [ - "diff" - ], - "time": "2015-12-08T07:14:41+00:00" + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/release/3.x" + }, + "time": "2017-11-10T14:09:06+00:00" }, { - "name": "sebastian/environment", - "version": "2.0.0", + "name": "phpdocumentor/type-resolver", + "version": "0.4.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "5795ffe5dc5b02460c3e34222fee8cbe245d8fac" + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/5795ffe5dc5b02460c3e34222fee8cbe245d8fac", - "reference": "5795ffe5dc5b02460c3e34222fee8cbe245d8fac", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7", + "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": "^5.5 || ^7.0", + "phpdocumentor/reflection-common": "^1.0" }, "require-dev": { - "phpunit/phpunit": "^5.0" + "mockery/mockery": "^0.9.4", + "phpunit/phpunit": "^5.2||^4.8.24" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/master" + }, + "time": "2017-07-14T14:27:02+00:00" + }, + { + "name": "phploc/phploc", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phploc.git", + "reference": "6a8a9416517b82d6326ac9c2d040ad53c13654eb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phploc/zipball/6a8a9416517b82d6326ac9c2d040ad53c13654eb", + "reference": "6a8a9416517b82d6326ac9c2d040ad53c13654eb", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0", + "sebastian/finder-facade": "^1.1", + "sebastian/version": "^2.0", + "symfony/console": "^2.7|^3.0|^4.0" + }, + "bin": [ + "phploc" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" } }, "autoload": { @@ -896,44 +1060,192 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "http://www.github.com/sebastianbergmann/environment", + "description": "A tool for quickly measuring the size of a PHP project.", + "homepage": "https://github.com/sebastianbergmann/phploc", + "support": { + "issues": "https://github.com/sebastianbergmann/phploc/issues", + "source": "https://github.com/sebastianbergmann/phploc/tree/master" + }, + "time": "2017-11-18T17:35:43+00:00" + }, + { + "name": "phpmd/phpmd", + "version": "2.6.1", + "source": { + "type": "git", + "url": "https://github.com/phpmd/phpmd.git", + "reference": "7425e155cf22cdd2b4dd3458a7da4cf6c0201562" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpmd/phpmd/zipball/7425e155cf22cdd2b4dd3458a7da4cf6c0201562", + "reference": "7425e155cf22cdd2b4dd3458a7da4cf6c0201562", + "shasum": "" + }, + "require": { + "ext-xml": "*", + "pdepend/pdepend": "^2.5", + "php": ">=5.3.9" + }, + "require-dev": { + "phpunit/phpunit": "^4.0", + "squizlabs/php_codesniffer": "^2.0" + }, + "bin": [ + "src/bin/phpmd" + ], + "type": "project", + "autoload": { + "psr-0": { + "PHPMD\\": "src/main/php" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Manuel Pichler", + "email": "github@manuel-pichler.de", + "homepage": "https://github.com/manuelpichler", + "role": "Project Founder" + }, + { + "name": "Other contributors", + "homepage": "https://github.com/phpmd/phpmd/graphs/contributors", + "role": "Contributors" + }, + { + "name": "Marc Würth", + "email": "ravage@bluewin.ch", + "homepage": "https://github.com/ravage84", + "role": "Project Maintainer" + } + ], + "description": "PHPMD is a spin-off project of PHP Depend and aims to be a PHP equivalent of the well known Java tool PMD.", + "homepage": "http://phpmd.org/", "keywords": [ - "Xdebug", - "environment", - "hhvm" + "mess detection", + "mess detector", + "pdepend", + "phpmd", + "pmd" + ], + "support": { + "irc": "irc://irc.freenode.org/phpmd", + "issues": "https://github.com/phpmd/phpmd/issues", + "source": "https://github.com/phpmd/phpmd/tree/2.6.1" + }, + "time": "2019-07-05T23:07:02+00:00" + }, + { + "name": "phpspec/prophecy", + "version": "v1.10.3", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy.git", + "reference": "451c3cd1418cf640de218914901e51b064abb093" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/451c3cd1418cf640de218914901e51b064abb093", + "reference": "451c3cd1418cf640de218914901e51b064abb093", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.0.2", + "php": "^5.3|^7.0", + "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0", + "sebastian/comparator": "^1.2.3|^2.0|^3.0|^4.0", + "sebastian/recursion-context": "^1.0|^2.0|^3.0|^4.0" + }, + "require-dev": { + "phpspec/phpspec": "^2.5 || ^3.2", + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10.x-dev" + } + }, + "autoload": { + "psr-4": { + "Prophecy\\": "src/Prophecy" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" ], - "time": "2016-11-26T07:53:53+00:00" + "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": "https://github.com/phpspec/prophecy", + "keywords": [ + "Double", + "Dummy", + "fake", + "mock", + "spy", + "stub" + ], + "support": { + "issues": "https://github.com/phpspec/prophecy/issues", + "source": "https://github.com/phpspec/prophecy/tree/v1.10.3" + }, + "time": "2020-03-05T15:02:03+00:00" }, { - "name": "sebastian/exporter", - "version": "2.0.0", + "name": "phpunit/php-code-coverage", + "version": "4.0.8", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4" + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "ef7b2f56815df854e66ceaee8ebe9393ae36a40d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4", - "reference": "ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ef7b2f56815df854e66ceaee8ebe9393ae36a40d", + "reference": "ef7b2f56815df854e66ceaee8ebe9393ae36a40d", "shasum": "" }, "require": { - "php": ">=5.3.3", - "sebastian/recursion-context": "~2.0" + "ext-dom": "*", + "ext-xmlwriter": "*", + "php": "^5.6 || ^7.0", + "phpunit/php-file-iterator": "^1.3", + "phpunit/php-text-template": "^1.2", + "phpunit/php-token-stream": "^1.4.2 || ^2.0", + "sebastian/code-unit-reverse-lookup": "^1.0", + "sebastian/environment": "^1.3.2 || ^2.0", + "sebastian/version": "^1.0 || ^2.0" }, "require-dev": { - "ext-mbstring": "*", - "phpunit/phpunit": "~4.4" + "ext-xdebug": "^2.1.4", + "phpunit/phpunit": "^5.7" + }, + "suggest": { + "ext-xdebug": "^2.5.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "4.0.x-dev" } }, "autoload": { @@ -947,298 +1259,2584 @@ ], "authors": [ { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, + "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" + ], + "support": { + "irc": "irc://irc.freenode.net/phpunit", + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/4.0" + }, + "time": "2017-04-02T07:44:40+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "1.4.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/730b01bc3e867237eaac355e06a36b85dd93a8b4", + "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, + "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" + ], + "support": { + "irc": "irc://irc.freenode.net/phpunit", + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/1.4.5" + }, + "time": "2017-11-27T13:52:08+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/1.2.1" + }, + "time": "2015-06-21T13:50:34+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "1.0.9", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", + "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "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" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/master" + }, + "time": "2017-02-26T11:10:40+00:00" + }, + { + "name": "phpunit/php-token-stream", + "version": "1.4.12", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-token-stream.git", + "reference": "1ce90ba27c42e4e44e6d8458241466380b51fa16" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/1ce90ba27c42e4e44e6d8458241466380b51fa16", + "reference": "1ce90ba27c42e4e44e6d8458241466380b51fa16", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Wrapper around PHP's tokenizer extension.", + "homepage": "https://github.com/sebastianbergmann/php-token-stream/", + "keywords": [ + "tokenizer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-token-stream/issues", + "source": "https://github.com/sebastianbergmann/php-token-stream/tree/1.4" + }, + "abandoned": true, + "time": "2017-12-04T08:55:13+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "5.7.27", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "b7803aeca3ccb99ad0a506fa80b64cd6a56bbc0c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b7803aeca3ccb99ad0a506fa80b64cd6a56bbc0c", + "reference": "b7803aeca3ccb99ad0a506fa80b64cd6a56bbc0c", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "myclabs/deep-copy": "~1.3", + "php": "^5.6 || ^7.0", + "phpspec/prophecy": "^1.6.2", + "phpunit/php-code-coverage": "^4.0.4", + "phpunit/php-file-iterator": "~1.4", + "phpunit/php-text-template": "~1.2", + "phpunit/php-timer": "^1.0.6", + "phpunit/phpunit-mock-objects": "^3.2", + "sebastian/comparator": "^1.2.4", + "sebastian/diff": "^1.4.3", + "sebastian/environment": "^1.3.4 || ^2.0", + "sebastian/exporter": "~2.0", + "sebastian/global-state": "^1.1", + "sebastian/object-enumerator": "~2.0", + "sebastian/resource-operations": "~1.0", + "sebastian/version": "^1.0.6|^2.0.1", + "symfony/yaml": "~2.1|~3.0|~4.0" + }, + "conflict": { + "phpdocumentor/reflection-docblock": "3.0.2" + }, + "require-dev": { + "ext-pdo": "*" + }, + "suggest": { + "ext-xdebug": "*", + "phpunit/php-invoker": "~1.1" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.7.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" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "source": "https://github.com/sebastianbergmann/phpunit/tree/5.7.27" + }, + "time": "2018-02-01T05:50:59+00:00" + }, + { + "name": "phpunit/phpunit-mock-objects", + "version": "3.4.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", + "reference": "a23b761686d50a560cc56233b9ecf49597cc9118" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/a23b761686d50a560cc56233b9ecf49597cc9118", + "reference": "a23b761686d50a560cc56233b9ecf49597cc9118", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.0.2", + "php": "^5.6 || ^7.0", + "phpunit/php-text-template": "^1.2", + "sebastian/exporter": "^1.2 || ^2.0" + }, + "conflict": { + "phpunit/phpunit": "<5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.4" + }, + "suggest": { + "ext-soap": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "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" + ], + "support": { + "irc": "irc://irc.freenode.net/phpunit", + "issues": "https://github.com/sebastianbergmann/phpunit-mock-objects/issues", + "source": "https://github.com/sebastianbergmann/phpunit-mock-objects/tree/3.4" + }, + "abandoned": true, + "time": "2017-06-30T09:13:00+00:00" + }, + { + "name": "psr/container", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/master" + }, + "time": "2017-02-14T16:28:37+00:00" + }, + { + "name": "psr/http-message", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/master" + }, + "time": "2016-08-06T14:39:51+00:00" + }, + { + "name": "psr/log", + "version": "1.1.4", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/1.1.4" + }, + "time": "2021-05-03T11:20:27+00:00" + }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/1de8cd5c010cb153fcd68b8d0f64606f523f7619", + "reference": "1de8cd5c010cb153fcd68b8d0f64606f523f7619", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "phpunit/phpunit": "^8.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/1.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-30T08:15:22+00:00" + }, + { + "name": "sebastian/comparator", + "version": "1.2.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2b7424b55f5047b47ac6e5ccb20b2aea4011d9be", + "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "sebastian/diff": "~1.2", + "sebastian/exporter": "~1.2 || ~2.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "http://www.github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "source": "https://github.com/sebastianbergmann/comparator/tree/1.2" + }, + "time": "2017-01-29T09:50:25+00:00" + }, + { + "name": "sebastian/diff", + "version": "1.4.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7f066a26a962dbe58ddea9f72a4e82874a3975a4", + "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "source": "https://github.com/sebastianbergmann/diff/tree/1.4" + }, + "time": "2017-05-22T07:24:03+00:00" + }, + { + "name": "sebastian/environment", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "5795ffe5dc5b02460c3e34222fee8cbe245d8fac" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/5795ffe5dc5b02460c3e34222fee8cbe245d8fac", + "reference": "5795ffe5dc5b02460c3e34222fee8cbe245d8fac", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "source": "https://github.com/sebastianbergmann/environment/tree/master" + }, + "time": "2016-11-26T07:53:53+00:00" + }, + { + "name": "sebastian/exporter", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4", + "reference": "ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "sebastian/recursion-context": "~2.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "~4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "http://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "source": "https://github.com/sebastianbergmann/exporter/tree/master" + }, + "time": "2016-11-19T08:54:04+00:00" + }, + { + "name": "sebastian/finder-facade", + "version": "1.2.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/finder-facade.git", + "reference": "4a3174709c2dc565fe5fb26fcf827f6a1fc7b09f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/finder-facade/zipball/4a3174709c2dc565fe5fb26fcf827f6a1fc7b09f", + "reference": "4a3174709c2dc565fe5fb26fcf827f6a1fc7b09f", + "shasum": "" + }, + "require": { + "symfony/finder": "~2.3|~3.0|~4.0", + "theseer/fdomdocument": "~1.3" + }, + "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", + "support": { + "issues": "https://github.com/sebastianbergmann/finder-facade/issues", + "source": "https://github.com/sebastianbergmann/finder-facade/tree/master" + }, + "abandoned": true, + "time": "2017-11-18T17:31:49+00:00" + }, + { + "name": "sebastian/global-state", + "version": "1.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4", + "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.2" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "source": "https://github.com/sebastianbergmann/global-state/tree/1.1.1" + }, + "time": "2015-10-12T03:26:01+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "1311872ac850040a79c3c058bea3e22d0f09cbb7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/1311872ac850040a79c3c058bea3e22d0f09cbb7", + "reference": "1311872ac850040a79c3c058bea3e22d0f09cbb7", + "shasum": "" + }, + "require": { + "php": ">=5.6", + "sebastian/recursion-context": "~2.0" + }, + "require-dev": { + "phpunit/phpunit": "~5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/master" + }, + "time": "2017-02-18T15:18:39+00:00" + }, + { + "name": "sebastian/phpcpd", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpcpd.git", + "reference": "24d9a880deadb0b8c9680e9cfe78e30b704225db" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpcpd/zipball/24d9a880deadb0b8c9680e9cfe78e30b704225db", + "reference": "24d9a880deadb0b8c9680e9cfe78e30b704225db", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "phpunit/php-timer": ">=1.0.6", + "sebastian/finder-facade": "~1.1", + "sebastian/version": "~1.0|~2.0", + "symfony/console": "~2.7|^3.0", + "theseer/fdomdocument": "~1.4" + }, + "bin": [ + "phpcpd" + ], + "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": "Copy/Paste Detector (CPD) for PHP code.", + "homepage": "https://github.com/sebastianbergmann/phpcpd", + "support": { + "issues": "https://github.com/sebastianbergmann/phpcpd/issues", + "source": "https://github.com/sebastianbergmann/phpcpd/tree/2.0.4" + }, + "time": "2016-04-17T19:32:49+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "2c3ba150cbec723aa057506e73a8d33bdb286c9a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/2c3ba150cbec723aa057506e73a8d33bdb286c9a", + "reference": "2c3ba150cbec723aa057506e73a8d33bdb286c9a", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/master" + }, + "time": "2016-11-19T07:33:16+00:00" + }, + { + "name": "sebastian/resource-operations", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", + "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", + "shasum": "" + }, + "require": { + "php": ">=5.6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "support": { + "issues": "https://github.com/sebastianbergmann/resource-operations/issues", + "source": "https://github.com/sebastianbergmann/resource-operations/tree/master" + }, + "abandoned": true, + "time": "2015-07-28T20:34:47+00:00" + }, + { + "name": "sebastian/version", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", + "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "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", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/master" + }, + "time": "2016-10-03T07:35:21+00:00" + }, + { + "name": "squizlabs/php_codesniffer", + "version": "3.5.8", + "source": { + "type": "git", + "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", + "reference": "9d583721a7157ee997f235f327de038e7ea6dac4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/9d583721a7157ee997f235f327de038e7ea6dac4", + "reference": "9d583721a7157ee997f235f327de038e7ea6dac4", + "shasum": "" + }, + "require": { + "ext-simplexml": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" + }, + "bin": [ + "bin/phpcs", + "bin/phpcbf" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Greg Sherwood", + "role": "lead" + } + ], + "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", + "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", + "keywords": [ + "phpcs", + "standards" + ], + "support": { + "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", + "source": "https://github.com/squizlabs/PHP_CodeSniffer", + "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" + }, + "time": "2020-10-23T02:01:07+00:00" + }, + { + "name": "symfony/browser-kit", + "version": "v3.4.47", + "source": { + "type": "git", + "url": "https://github.com/symfony/browser-kit.git", + "reference": "9590bd3d3f9fa2f28d34b713ed4765a8cc8ad15c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/9590bd3d3f9fa2f28d34b713ed4765a8cc8ad15c", + "reference": "9590bd3d3f9fa2f28d34b713ed4765a8cc8ad15c", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "symfony/dom-crawler": "~2.8|~3.0|~4.0" + }, + "require-dev": { + "symfony/css-selector": "~2.8|~3.0|~4.0", + "symfony/process": "~2.8|~3.0|~4.0" + }, + "suggest": { + "symfony/process": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\BrowserKit\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony BrowserKit Component", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/browser-kit/tree/v3.4.47" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-10-24T10:57:07+00:00" + }, + { + "name": "symfony/config", + "version": "v3.4.47", + "source": { + "type": "git", + "url": "https://github.com/symfony/config.git", + "reference": "bc6b3fd3930d4b53a60b42fe2ed6fc466b75f03f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/config/zipball/bc6b3fd3930d4b53a60b42fe2ed6fc466b75f03f", + "reference": "bc6b3fd3930d4b53a60b42fe2ed6fc466b75f03f", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "symfony/filesystem": "~2.8|~3.0|~4.0", + "symfony/polyfill-ctype": "~1.8" + }, + "conflict": { + "symfony/dependency-injection": "<3.3", + "symfony/finder": "<3.3" + }, + "require-dev": { + "symfony/dependency-injection": "~3.3|~4.0", + "symfony/event-dispatcher": "~3.3|~4.0", + "symfony/finder": "~3.3|~4.0", + "symfony/yaml": "~3.0|~4.0" + }, + "suggest": { + "symfony/yaml": "To use the yaml reference dumper" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Config\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Config Component", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/config/tree/v3.4.47" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-10-24T10:57:07+00:00" + }, + { + "name": "symfony/console", + "version": "v3.4.47", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "a10b1da6fc93080c180bba7219b5ff5b7518fe81" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/a10b1da6fc93080c180bba7219b5ff5b7518fe81", + "reference": "a10b1da6fc93080c180bba7219b5ff5b7518fe81", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "symfony/debug": "~2.8|~3.0|~4.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/dependency-injection": "<3.4", + "symfony/process": "<3.3" + }, + "provide": { + "psr/log-implementation": "1.0" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "~3.3|~4.0", + "symfony/dependency-injection": "~3.4|~4.0", + "symfony/event-dispatcher": "~2.8|~3.0|~4.0", + "symfony/lock": "~3.4|~4.0", + "symfony/process": "~3.3|~4.0" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/lock": "", + "symfony/process": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Console Component", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/console/tree/v3.4.47" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-10-24T10:57:07+00:00" + }, + { + "name": "symfony/css-selector", + "version": "v3.4.47", + "source": { + "type": "git", + "url": "https://github.com/symfony/css-selector.git", + "reference": "da3d9da2ce0026771f5fe64cb332158f1bd2bc33" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/da3d9da2ce0026771f5fe64cb332158f1bd2bc33", + "reference": "da3d9da2ce0026771f5fe64cb332158f1bd2bc33", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\CssSelector\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Jean-François Simon", + "email": "jeanfrancois.simon@sensiolabs.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony CssSelector Component", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/css-selector/tree/v3.4.47" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-10-24T10:57:07+00:00" + }, + { + "name": "symfony/debug", + "version": "v3.4.47", + "source": { + "type": "git", + "url": "https://github.com/symfony/debug.git", + "reference": "ab42889de57fdfcfcc0759ab102e2fd4ea72dcae" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/debug/zipball/ab42889de57fdfcfcc0759ab102e2fd4ea72dcae", + "reference": "ab42889de57fdfcfcc0759ab102e2fd4ea72dcae", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "psr/log": "~1.0" + }, + "conflict": { + "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2" + }, + "require-dev": { + "symfony/http-kernel": "~2.8|~3.0|~4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Debug\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Debug Component", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/debug/tree/v3.4.47" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-10-24T10:57:07+00:00" + }, + { + "name": "symfony/dependency-injection", + "version": "v3.4.47", + "source": { + "type": "git", + "url": "https://github.com/symfony/dependency-injection.git", + "reference": "51d2a2708c6ceadad84393f8581df1dcf9e5e84b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/51d2a2708c6ceadad84393f8581df1dcf9e5e84b", + "reference": "51d2a2708c6ceadad84393f8581df1dcf9e5e84b", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "psr/container": "^1.0" + }, + "conflict": { + "symfony/config": "<3.3.7", + "symfony/finder": "<3.3", + "symfony/proxy-manager-bridge": "<3.4", + "symfony/yaml": "<3.4" + }, + "provide": { + "psr/container-implementation": "1.0" + }, + "require-dev": { + "symfony/config": "~3.3|~4.0", + "symfony/expression-language": "~2.8|~3.0|~4.0", + "symfony/yaml": "~3.4|~4.0" + }, + "suggest": { + "symfony/config": "", + "symfony/expression-language": "For using expressions in service container configuration", + "symfony/finder": "For using double-star glob patterns or when GLOB_BRACE portability is required", + "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them", + "symfony/yaml": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\DependencyInjection\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony DependencyInjection Component", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/dependency-injection/tree/v3.4.47" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-10-24T10:57:07+00:00" + }, + { + "name": "symfony/dom-crawler", + "version": "v3.4.47", + "source": { + "type": "git", + "url": "https://github.com/symfony/dom-crawler.git", + "reference": "ef97bcfbae5b384b4ca6c8d57b617722f15241a6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/ef97bcfbae5b384b4ca6c8d57b617722f15241a6", + "reference": "ef97bcfbae5b384b4ca6c8d57b617722f15241a6", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.0" + }, + "require-dev": { + "symfony/css-selector": "~2.8|~3.0|~4.0" + }, + "suggest": { + "symfony/css-selector": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\DomCrawler\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony DomCrawler Component", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/dom-crawler/tree/v3.4.47" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-10-24T10:57:07+00:00" + }, + { + "name": "symfony/event-dispatcher", + "version": "v3.4.47", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "31fde73757b6bad247c54597beef974919ec6860" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/31fde73757b6bad247c54597beef974919ec6860", + "reference": "31fde73757b6bad247c54597beef974919ec6860", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8" + }, + "conflict": { + "symfony/dependency-injection": "<3.3" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "~2.8|~3.0|~4.0", + "symfony/debug": "~3.4|~4.4", + "symfony/dependency-injection": "~3.3|~4.0", + "symfony/expression-language": "~2.8|~3.0|~4.0", + "symfony/stopwatch": "~2.8|~3.0|~4.0" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony EventDispatcher Component", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/event-dispatcher/tree/v3.4.47" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-10-24T10:57:07+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v3.4.47", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "e58d7841cddfed6e846829040dca2cca0ebbbbb3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/e58d7841cddfed6e846829040dca2cca0ebbbbb3", + "reference": "e58d7841cddfed6e846829040dca2cca0ebbbbb3", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "symfony/polyfill-ctype": "~1.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Filesystem Component", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v3.4.47" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-10-24T10:57:07+00:00" + }, + { + "name": "symfony/finder", + "version": "v3.4.47", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "b6b6ad3db3edb1b4b1c1896b1975fb684994de6e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/b6b6ad3db3edb1b4b1c1896b1975fb684994de6e", + "reference": "b6b6ad3db3edb1b4b1c1896b1975fb684994de6e", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Finder Component", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v3.4.47" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-11-16T17:02:08+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.19.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "aed596913b70fae57be53d86faa2e9ef85a2297b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/aed596913b70fae57be53d86faa2e9ef85a2297b", + "reference": "aed596913b70fae57be53d86faa2e9ef85a2297b", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.19-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.19.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-10-23T09:01:57+00:00" + }, + { + "name": "symfony/polyfill-intl-idn", + "version": "v1.19.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-idn.git", + "reference": "4ad5115c0f5d5172a9fe8147675ec6de266d8826" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/4ad5115c0f5d5172a9fe8147675ec6de266d8826", + "reference": "4ad5115c0f5d5172a9fe8147675ec6de266d8826", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "symfony/polyfill-intl-normalizer": "^1.10", + "symfony/polyfill-php70": "^1.10", + "symfony/polyfill-php72": "^1.10" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.19-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Intl\\Idn\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Laurent Bassin", + "email": "laurent@bassin.info" }, { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "Trevor Rowbotham", + "email": "trevor.rowbotham@pm.me" }, { - "name": "Adam Harvey", - "email": "aharvey@php.net" + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "http://www.github.com/sebastianbergmann/exporter", + "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", + "homepage": "https://symfony.com", "keywords": [ - "export", - "exporter" + "compatibility", + "idn", + "intl", + "polyfill", + "portable", + "shim" ], - "time": "2016-11-19T08:54:04+00:00" + "support": { + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.19.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-10-21T09:57:48+00:00" }, { - "name": "sebastian/global-state", - "version": "1.1.1", + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.19.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4" + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "8db0ae7936b42feb370840cf24de1a144fb0ef27" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4", - "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8db0ae7936b42feb370840cf24de1a144fb0ef27", + "reference": "8db0ae7936b42feb370840cf24de1a144fb0ef27", "shasum": "" }, "require": { "php": ">=5.3.3" }, - "require-dev": { - "phpunit/phpunit": "~4.2" - }, "suggest": { - "ext-uopz": "*" + "ext-intl": "For best performance" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-main": "1.19-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" } }, "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "files": [ + "bootstrap.php" + ], "classmap": [ - "src/" + "Resources/stubs" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Snapshotting of global state", - "homepage": "http://www.github.com/sebastianbergmann/global-state", + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", "keywords": [ - "global state" + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" ], - "time": "2015-10-12T03:26:01+00:00" + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.19.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-10-23T09:01:57+00:00" }, { - "name": "sebastian/object-enumerator", - "version": "2.0.0", + "name": "symfony/polyfill-mbstring", + "version": "v1.19.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "96f8a3f257b69e8128ad74d3a7fd464bcbaa3b35" + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "b5f7b932ee6fa802fc792eabd77c4c88084517ce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/96f8a3f257b69e8128ad74d3a7fd464bcbaa3b35", - "reference": "96f8a3f257b69e8128ad74d3a7fd464bcbaa3b35", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/b5f7b932ee6fa802fc792eabd77c4c88084517ce", + "reference": "b5f7b932ee6fa802fc792eabd77c4c88084517ce", "shasum": "" }, "require": { - "php": ">=5.6", - "sebastian/recursion-context": "~2.0" + "php": ">=5.3.3" }, - "require-dev": { - "phpunit/phpunit": "~5" + "suggest": { + "ext-mbstring": "For best performance" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-main": "1.19-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" } }, "autoload": { - "classmap": [ - "src/" + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Traverses array structures and object graphs to enumerate all referenced objects", - "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "time": "2016-11-19T07:35:10+00:00" + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.19.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-10-23T09:01:57+00:00" }, { - "name": "sebastian/recursion-context", - "version": "2.0.0", + "name": "symfony/polyfill-php70", + "version": "v1.19.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "2c3ba150cbec723aa057506e73a8d33bdb286c9a" + "url": "https://github.com/symfony/polyfill-php70.git", + "reference": "3fe414077251a81a1b15b1c709faf5c2fbae3d4e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/2c3ba150cbec723aa057506e73a8d33bdb286c9a", - "reference": "2c3ba150cbec723aa057506e73a8d33bdb286c9a", + "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/3fe414077251a81a1b15b1c709faf5c2fbae3d4e", + "reference": "3fe414077251a81a1b15b1c709faf5c2fbae3d4e", "shasum": "" }, "require": { + "paragonie/random_compat": "~1.0|~2.0|~9.99", "php": ">=5.3.3" }, - "require-dev": { - "phpunit/phpunit": "~4.4" - }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-main": "1.19-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" } }, "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php70\\": "" + }, + "files": [ + "bootstrap.php" + ], "classmap": [ - "src/" + "Resources/stubs" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 7.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php70/tree/v1.19.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" }, { - "name": "Adam Harvey", - "email": "aharvey@php.net" + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" } ], - "description": "Provides functionality to recursively process PHP variables", - "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2016-11-19T07:33:16+00:00" + "time": "2020-10-23T09:01:57+00:00" }, { - "name": "sebastian/resource-operations", - "version": "1.0.0", + "name": "symfony/polyfill-php72", + "version": "v1.19.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52" + "url": "https://github.com/symfony/polyfill-php72.git", + "reference": "beecef6b463b06954638f02378f52496cb84bacc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", - "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/beecef6b463b06954638f02378f52496cb84bacc", + "reference": "beecef6b463b06954638f02378f52496cb84bacc", "shasum": "" }, "require": { - "php": ">=5.6.0" + "php": ">=5.3.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-main": "1.19-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" } }, "autoload": { - "classmap": [ - "src/" + "psr-4": { + "Symfony\\Polyfill\\Php72\\": "" + }, + "files": [ + "bootstrap.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Provides a list of PHP built-in functions that operate on resources", - "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "time": "2015-07-28T20:34:47+00:00" + "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php72/tree/v1.19.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-10-23T09:01:57+00:00" }, { - "name": "sebastian/version", - "version": "2.0.1", + "name": "symfony/process", + "version": "v3.4.47", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" + "url": "https://github.com/symfony/process.git", + "reference": "b8648cf1d5af12a44a51d07ef9bf980921f15fca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", - "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", + "url": "https://api.github.com/repos/symfony/process/zipball/b8648cf1d5af12a44a51d07ef9bf980921f15fca", + "reference": "b8648cf1d5af12a44a51d07ef9bf980921f15fca", "shasum": "" }, "require": { - "php": ">=5.6" + "php": "^5.5.9|>=7.0.8" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, "autoload": { - "classmap": [ - "src/" + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", - "time": "2016-10-03T07:35:21+00:00" + "description": "Symfony Process Component", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v3.4.47" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-10-24T10:57:07+00:00" }, { "name": "symfony/yaml", - "version": "v3.2.3", + "version": "v3.4.47", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "e1718c6bf57e1efbb8793ada951584b2ab27775b" + "reference": "88289caa3c166321883f67fe5130188ebbb47094" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/e1718c6bf57e1efbb8793ada951584b2ab27775b", - "reference": "e1718c6bf57e1efbb8793ada951584b2ab27775b", + "url": "https://api.github.com/repos/symfony/yaml/zipball/88289caa3c166321883f67fe5130188ebbb47094", + "reference": "88289caa3c166321883f67fe5130188ebbb47094", "shasum": "" }, "require": { - "php": ">=5.5.9" + "php": "^5.5.9|>=7.0.8", + "symfony/polyfill-ctype": "~1.8" + }, + "conflict": { + "symfony/console": "<3.4" }, "require-dev": { - "symfony/console": "~2.8|~3.0" + "symfony/console": "~3.4|~4.0" }, "suggest": { "symfony/console": "For validating YAML files using the lint command" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.2-dev" - } - }, "autoload": { "psr-4": { "Symfony\\Component\\Yaml\\": "" @@ -1263,35 +3861,95 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2017-01-21T17:06:35+00:00" + "support": { + "source": "https://github.com/symfony/yaml/tree/v3.4.47" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-10-24T10:57:07+00:00" + }, + { + "name": "theseer/fdomdocument", + "version": "1.6.6", + "source": { + "type": "git", + "url": "https://github.com/theseer/fDOMDocument.git", + "reference": "6e8203e40a32a9c770bcb62fe37e68b948da6dca" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/fDOMDocument/zipball/6e8203e40a32a9c770bcb62fe37e68b948da6dca", + "reference": "6e8203e40a32a9c770bcb62fe37e68b948da6dca", + "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 convenience and to simplify the usage of DOM.", + "homepage": "https://github.com/theseer/fDOMDocument", + "support": { + "issues": "https://github.com/theseer/fDOMDocument/issues", + "source": "https://github.com/theseer/fDOMDocument/tree/master" + }, + "time": "2017-06-30T11:53:12+00:00" }, { "name": "webmozart/assert", - "version": "1.2.0", + "version": "1.9.1", "source": { "type": "git", - "url": "https://github.com/webmozart/assert.git", - "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f" + "url": "https://github.com/webmozarts/assert.git", + "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/2db61e59ff05fe5126d152bd0655c9ea113e550f", - "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/bafc69caeb4d49c39fd0779086c03a3738cbb389", + "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0" + "php": "^5.3.3 || ^7.0 || ^8.0", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<3.9.1" }, "require-dev": { - "phpunit/phpunit": "^4.6", - "sebastian/version": "^1.0.1" + "phpunit/phpunit": "^4.8.36 || ^7.5.13" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.3-dev" - } - }, "autoload": { "psr-4": { "Webmozart\\Assert\\": "src/" @@ -1313,7 +3971,11 @@ "check", "validate" ], - "time": "2016-11-23T20:04:58+00:00" + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.9.1" + }, + "time": "2020-07-08T17:02:28+00:00" } ], "aliases": [], @@ -1324,5 +3986,6 @@ "platform": { "php": ">=5.6.0" }, - "platform-dev": [] + "platform-dev": [], + "plugin-api-version": "2.1.0" }