php-censor/src/PHPCensor/Controller/BuildController.php
2017-03-12 11:40:40 +07:00

292 lines
8.2 KiB
PHP

<?php
namespace PHPCensor\Controller;
use b8;
use b8\Exception\HttpException\NotFoundException;
use b8\Http\Response\JsonResponse;
use PHPCensor\BuildFactory;
use PHPCensor\Helper\AnsiConverter;
use PHPCensor\Helper\Lang;
use PHPCensor\Model\Build;
use PHPCensor\Model\Project;
use PHPCensor\Service\BuildService;
use PHPCensor\Controller;
/**
* Build Controller - Allows users to run and view builds.
*
* @author Dan Cryer <dan@block8.co.uk>
*/
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.
*/
public function view($buildId)
{
try {
$build = BuildFactory::getBuildById($buildId);
} catch (\Exception $ex) {
$build = null;
}
if (empty($build)) {
throw new NotFoundException(Lang::get('build_x_not_found', $buildId));
}
$this->view->plugins = $this->getUiPlugins();
$this->view->build = $build;
$this->view->data = $this->getBuildData($build);
$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 .= "<a class=\"btn btn-default\" href=\"{$rebuildLink}\">{$rebuild}</a> ";
}
if ($this->currentUserIsAdmin()) {
$actions .= " <a class=\"btn btn-danger\" id=\"delete-build\" href=\"{$deleteLink}\">{$delete}</a>";
}
$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:
*/
protected function getBuildData(Build $build)
{
$data = [];
$data['status'] = (int)$build->getStatus();
$data['log'] = $this->cleanLog($build->getLog());
$data['created'] = !is_null($build->getCreated()) ? $build->getCreated()->format('Y-m-d H:i:s') : null;
$data['started'] = !is_null($build->getStarted()) ? $build->getStarted()->format('Y-m-d H:i:s') : null;
$data['finished'] = !is_null($build->getFinished()) ? $build->getFinished()->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->getErrorsForBuild($build->getId());
$errorView = new b8\View('Build/errors');
$errorView->build = $build;
$errorView->errors = $errors;
$data['errors'] = $errorStore->getErrorTotalForBuild($build->getId());
$data['error_html'] = $errorView->render();
$data['since'] = (new \DateTime())->format('Y-m-d H:i:s');
return $data;
}
/**
* 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 PHPCI 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)
{
$response = new JsonResponse();
$build = BuildFactory::getBuildById($buildId);
if (!$build) {
$response->setResponseCode(404);
$response->setContent([]);
return $response;
}
$response->setContent($this->getBuildData($build));
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;
}
public function ajaxTimeline()
{
$builds = $this->buildStore->getLatestBuilds(null, 10);
foreach ($builds as &$build) {
$build = BuildFactory::getBuild($build);
}
$view = new b8\View('Home/ajax-timeline');
$view->builds = $builds;
$this->response->disableLayout();
$this->response->setContent($view->render());
return $this->response;
}
}