Added ajax for the dashboard and timeline on main page

This commit is contained in:
Dmitry Khomutov 2017-01-15 01:11:32 +07:00
parent 3f80c8e4ba
commit 40b5de70e5
No known key found for this signature in database
GPG key ID: 7EB36C9576F9ECB9
15 changed files with 415 additions and 212 deletions

View file

@ -23,6 +23,9 @@ var PHPCensor = {
PHPCensor.intervals.getProjectBuilds = setInterval(PHPCensor.getProjectBuilds, 10000);
}
PHPCensor.intervals.getDashboard = setInterval(PHPCensor.getDashboard, 10000);
PHPCensor.intervals.getTimeline = setInterval(PHPCensor.getTimeline, 10000);
PHPCensor.uiUpdated();
});
@ -33,7 +36,7 @@ var PHPCensor = {
getBuilds: function () {
$.ajax({
url: APP_URL + 'build/latest',
url: APP_URL + 'build/ajax-queue',
success: function (data) {
$(window).trigger('builds-updated', [data]);
@ -45,7 +48,7 @@ var PHPCensor = {
getProjectBuilds: function () {
$.ajax({
url: APP_URL + 'project/builds/' + PROJECT_ID + '?branch=' + PROJECT_BRANCH + '&per_page=' + PER_PAGE,
url: APP_URL + 'project/ajax-builds/' + PROJECT_ID + '?branch=' + PROJECT_BRANCH + '&per_page=' + PER_PAGE,
success: function (data) {
$('#latest-builds').html(data);
@ -55,6 +58,34 @@ var PHPCensor = {
});
},
getDashboard: function () {
$('.project-box').each(function(index) {
var projectId = this.id.substring(12);
$.ajax({
url: APP_URL + 'project/ajax-dashboard-project/' + projectId,
success: function (data) {
$(('#project-box-' + projectId)).html(data);
},
error: PHPCensor.handleFailedAjax
});
});
},
getTimeline: function () {
$.ajax({
url: APP_URL + 'build/ajax-timeline',
success: function (data) {
$('#timeline-box').html(data);
},
error: PHPCensor.handleFailedAjax
});
},
updateHeaderBuilds: function (data) {
$('.app-pending-list').empty();
$('.app-running-list').empty();
@ -411,7 +442,7 @@ function setupProjectForm()
$.ajax({
dataType: "json",
url: window.APP_URL + 'project/github-repositories',
url: window.APP_URL + 'project/ajax-github-repositories',
success: function (data) {
$('#loading').hide();

View file

@ -72,14 +72,14 @@ var Build = Class.extend({
registerQuery: function(name, seconds, query) {
var self = this;
var uri = 'build/meta/' + self.buildId;
var uri = 'build/ajax-meta/' + self.buildId;
var query = query || {};
var cb = function() {
var fullUri = window.APP_URL + uri;
if (name == 'build-updated') {
fullUri = window.APP_URL + 'build/data/' + self.buildId;
fullUri = window.APP_URL + 'build/ajax-data/' + self.buildId;
}
$.ajax({

View file

@ -29,8 +29,8 @@ class Router
public function __construct(Application $application, Request $request, Config $config)
{
$this->application = $application;
$this->request = $request;
$this->config = $config;
$this->request = $request;
$this->config = $config;
}
public function clearRoutes()

View file

@ -124,43 +124,6 @@ class BuildController extends Controller
return $rtn;
}
/**
* AJAX call to get build data:
*/
public function data($buildId)
{
$response = new JsonResponse();
$build = BuildFactory::getBuildById($buildId);
if (!$build) {
$response->setResponseCode(404);
$response->setContent([]);
return $response;
}
$response->setContent($this->getBuildData($build));
return $response;
}
/**
* AJAX call to get build meta:
*/
public function meta($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;
}
/**
* Get build data from database and json encode it:
*/
@ -208,6 +171,7 @@ class BuildController extends Controller
$response = new b8\Http\Response\RedirectResponse();
$response->setHeader('Location', APP_URL.'build/view/' . $build->getId());
return $response;
}
@ -228,6 +192,7 @@ class BuildController extends Controller
$response = new b8\Http\Response\RedirectResponse();
$response->setHeader('Location', APP_URL.'project/view/' . $build->getProjectId());
return $response;
}
@ -239,21 +204,6 @@ class BuildController extends Controller
return AnsiConverter::convert($log);
}
/**
* Allows the UI to poll for the latest running and pending builds.
*/
public function latest()
{
$rtn = [
'pending' => $this->formatBuilds($this->buildStore->getByStatus(Build::STATUS_NEW)),
'running' => $this->formatBuilds($this->buildStore->getByStatus(Build::STATUS_RUNNING)),
];
$response = new JsonResponse();
$response->setContent($rtn);
return $response;
}
/**
* Formats a list of builds into rows suitable for the dropdowns in the PHPCI header bar.
* @param $builds
@ -278,4 +228,64 @@ class BuildController extends Controller
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_NEW)),
'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;
}
}

View file

@ -44,8 +44,8 @@ class HomeController extends Controller
*/
public function init()
{
$this->buildStore = b8\Store\Factory::getStore('Build');
$this->projectStore = b8\Store\Factory::getStore('Project');
$this->buildStore = b8\Store\Factory::getStore('Build');
$this->projectStore = b8\Store\Factory::getStore('Project');
$this->groupStore = b8\Store\Factory::getStore('ProjectGroup');
}
@ -61,33 +61,12 @@ class HomeController extends Controller
$build = BuildFactory::getBuild($build);
}
$this->view->builds = $builds;
$this->view->builds = $builds;
$this->view->groups = $this->getGroupInfo();
return $this->view->render();
}
/**
* AJAX get latest builds table (HTML)
*/
public function latest()
{
$this->response->disableLayout();
$this->response->setContent($this->getLatestBuildsHtml());
return $this->response;
}
/**
* Ajax request for the project overview section of the dashboard.
*/
public function summary()
{
$this->response->disableLayout();
$projects = $this->projectStore->getWhere([], 50, 0, [], ['title' => 'ASC']);
$this->response->setContent($this->getSummaryHtml($projects));
return $this->response;
}
/**
* Generate the HTML for the project overview section of the dashboard.
* @param $projects
@ -119,29 +98,12 @@ class HomeController extends Controller
$failures[$project->getId()] = $failure;
}
$summaryView = new b8\View('SummaryTable');
$summaryView->projects = $projects;
$summaryView->builds = $summaryBuilds;
$summaryView->successful = $successes;
$summaryView->failed = $failures;
$summaryView->counts = $counts;
return $summaryView->render();
}
/**
* Get latest builds and render as a table.
*/
protected function getLatestBuildsHtml()
{
$builds = $this->buildStore->getWhere([], 5, 0, [], ['id' => 'DESC']);
$view = new b8\View('BuildsTable');
foreach ($builds['items'] as &$build) {
$build = BuildFactory::getBuild($build);
}
$view->builds = $builds['items'];
$view = new b8\View('Home/dashboard-projects');
$view->projects = $projects;
$view->builds = $summaryBuilds;
$view->successful = $successes;
$view->failed = $failures;
$view->counts = $counts;
return $view->render();
}
@ -152,15 +114,15 @@ class HomeController extends Controller
*/
protected function getGroupInfo()
{
$rtn = [];
$rtn = [];
$groups = $this->groupStore->getWhere([], 100, 0, [], ['title' => 'ASC']);
foreach ($groups['items'] as $group) {
$thisGroup = ['title' => $group->getTitle()];
$projects = $this->projectStore->getByGroupId($group->getId());
$thisGroup = ['title' => $group->getTitle()];
$projects = $this->projectStore->getByGroupId($group->getId());
$thisGroup['projects'] = $projects['items'];
$thisGroup['summary'] = $this->getSummaryHtml($thisGroup['projects']);
$rtn[] = $thisGroup;
$thisGroup['summary'] = $this->getSummaryHtml($thisGroup['projects']);
$rtn[] = $thisGroup;
}
return $rtn;

View file

@ -142,24 +142,6 @@ class ProjectController extends PHPCensor\Controller
return $response;
}
/**
* AJAX get latest builds.
*
* @param int $projectId
*
* @return b8\Http\Response
*/
public function builds($projectId)
{
$branch = $this->getParam('branch', '');
$perPage = (integer)$this->getParam('per_page', 10);
$builds = $this->getLatestBuildsHtml($projectId, urldecode($branch), 0, $perPage);
$this->response->disableLayout();
$this->response->setContent($builds[0]);
return $this->response;
}
/**
* Render latest builds for project as HTML table.
*
@ -179,7 +161,7 @@ class ProjectController extends PHPCensor\Controller
$order = ['id' => 'DESC'];
$builds = $this->buildStore->getWhere($criteria, $perPage, $start, [], $order);
$view = new b8\View('BuildsTable');
$view = new b8\View('Project/ajax-builds');
foreach ($builds['items'] as &$build) {
$build = BuildFactory::getBuild($build);
@ -190,6 +172,35 @@ class ProjectController extends PHPCensor\Controller
return [$view->render(), $builds['count']];
}
/**
* Render latest builds for project as HTML table.
*
* @param int $projectId
*
* @return array
*/
protected function getDashboardProjectHtml($projectId)
{
$count = $this->buildStore->getWhere(
['project_id' => $projectId],
1,
0,
[],
['id' => 'DESC']
);
$counts = $count['count'];
$view = new b8\View('Home/ajax-dashboard-project');
$view->project = $this->projectStore->getById($projectId);
$view->builds = $this->buildStore->getLatestBuilds($projectId);
$view->successful = $this->buildStore->getLastBuildByStatus($projectId, PHPCensor\Model\Build::STATUS_SUCCESS);
$view->failed = $this->buildStore->getLastBuildByStatus($projectId, PHPCensor\Model\Build::STATUS_FAILED);
$view->counts = $counts;
return $view->render();
}
/**
* Add a new project. Handles both the form, and processing.
*/
@ -215,7 +226,7 @@ class ProjectController extends PHPCensor\Controller
$form = $this->projectForm($values);
if ($method != 'POST' || ($method == 'POST' && !$form->validate())) {
$view = new b8\View('ProjectForm');
$view = new b8\View('Project/edit');
$view->type = 'add';
$view->project = null;
$view->form = $form;
@ -278,7 +289,7 @@ class ProjectController extends PHPCensor\Controller
$form = $this->projectForm($values, 'edit/' . $projectId);
if ($method != 'POST' || ($method == 'POST' && !$form->validate())) {
$view = new b8\View('ProjectForm');
$view = new b8\View('Project/edit');
$view->type = 'edit';
$view->project = $project;
$view->form = $form;
@ -403,18 +414,6 @@ class ProjectController extends PHPCensor\Controller
return $form;
}
/**
* Get an array of repositories from Github's API.
*/
protected function githubRepositories()
{
$github = new Github();
$response = new b8\Http\Response\JsonResponse();
$response->setContent($github->getRepositories());
return $response;
}
/**
* Get the validator to use to check project references.
* @param $values
@ -457,4 +456,48 @@ class ProjectController extends PHPCensor\Controller
return true;
};
}
/**
* @param int $projectId
*
* @return b8\Http\Response
*/
public function ajaxBuilds($projectId)
{
$branch = $this->getParam('branch', '');
$perPage = (integer)$this->getParam('per_page', 10);
$builds = $this->getLatestBuildsHtml($projectId, urldecode($branch), 0, $perPage);
$this->response->disableLayout();
$this->response->setContent($builds[0]);
return $this->response;
}
/**
* @param int $projectId
*
* @return b8\Http\Response
*/
public function ajaxDashboardProject($projectId)
{
$builds = $this->getDashboardProjectHtml($projectId);
$this->response->disableLayout();
$this->response->setContent($builds);
return $this->response;
}
/**
* 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;
}
}

View file

@ -166,7 +166,7 @@ class UserController extends Controller
$form = $this->userForm($values);
if ($method != 'POST' || ($method == 'POST' && !$form->validate())) {
$view = new b8\View('UserForm');
$view = new b8\View('User/edit');
$view->type = 'add';
$view->user = null;
$view->form = $form;
@ -208,7 +208,7 @@ class UserController extends Controller
$form = $this->userForm($values, 'edit/' . $userId);
if ($method != 'POST' || ($method == 'POST' && !$form->validate())) {
$view = new b8\View('UserForm');
$view = new b8\View('User/edit');
$view->type = 'edit';
$view->user = $user;
$view->form = $form;

View file

@ -0,0 +1,132 @@
<?php
use PHPCensor\Helper\Lang;
$statuses = [];
$failures = 0;
$subcls = 'yellow';
$cls = '';
$success = null;
$failure = null;
if (count($builds)) {
// Get the most recent build status to determine the main block colour.
$last_build = $builds[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 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->getFinished()) ? Lang::formatDateTime($build->getFinished()) : $success;
break;
case 3:
$failures++;
$statuses[] = 'failed';
$failure = is_null($failure) && !is_null($build->getFinished()) ? Lang::formatDateTime($build->getFinished()) : $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->getFinished())) {
$message .= Lang::get('last_successful_build', Lang::formatDateTime($lastSuccess->getFinished()));
} 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->getFinished())) {
$message .= Lang::get('last_failed_build', Lang::formatDateTime($lastFailure->getFinished()));
} else {
$message .= Lang::get('never_failed_build');
}
}
}
?>
<div class="small-box small-box-full bg-<?php print $subcls; ?>">
<div class="inner">
<h3>
<a href="<?php print APP_URL; ?>project/view/<?php print $project->getId(); ?>">
<?php print $project->getTitle(); ?>
</a>
</h3>
<p>
<?php print $message; ?>
</p>
</div>
<div class="icon">
<i class="fa fa-<?php print $project->getIcon(); ?>"></i>
</div>
<a href="<?php print APP_URL; ?>project/view/<?php print $project->getId(); ?>" class="small-box-footer small-box-footer-project">
<?php Lang::out('view_project'); ?> (<?php print $counts; ?>) <i class="fa fa-arrow-circle-right"></i>
</a>
<?php for ($idx=0; $idx < 5; $idx++) {
if (empty($builds[$idx])) {
echo '<span class="small-box-footer-build small-box-footer bg-blue"><i class="fa fa-minus"></i></span>';
} else {
$build = $builds[$idx];
$link = APP_URL . 'build/view/' . $build->id;
switch ($build->getStatus()) {
case 0:
$class = 'bg-blue';
$icon = 'fa-minus';
break;
case 1:
$class = 'bg-yellow';
$icon = 'fa-clock-o';
break;
case 2:
$class = 'bg-green';
$icon = 'fa-check';
break;
case 3:
$class = 'bg-red';
$icon = 'fa-times';
break;
}
echo '<a href="' . $link .'" class="small-box-footer-build small-box-footer ' . $class . '"><i class="fa ' . $icon . '"></i></a>';
}
} ?>
<div style="clear: both;"></div>
</div>

View file

@ -0,0 +1,88 @@
<?php use PHPCensor\Helper\Lang; ?>
<ul class="timeline">
<?php $last = new \DateTime('-1 Year'); ?>
<?php
foreach ($builds as $build):
switch ($build->getStatus()) {
case \PHPCensor\Model\Build::STATUS_NEW:
$updated = $build->getCreated();
$label = Lang::get('pending');
$color = 'blue';
break;
case \PHPCensor\Model\Build::STATUS_RUNNING:
$updated = $build->getStarted();
$label = Lang::get('running');
$color = 'yellow';
break;
case \PHPCensor\Model\Build::STATUS_SUCCESS:
$updated = $build->getFinished();
$label = Lang::get('success');
$color = 'green';
break;
case \PHPCensor\Model\Build::STATUS_FAILED:
$updated = $build->getFinished();
$label = Lang::get('failed');
$color = 'red';
break;
}
if (!$updated) {
$updated = $build->getCreated();
}
if ($updated->format('Y-m-d') != $last->format('Y-m-d')): $last = $updated;
?>
<li class="time-label">
<span class="bg-gray">
<?php print Lang::formatDateTime($last, 'll'); ?>
</span>
</li>
<?php endif; ?>
<!-- /.timeline-label -->
<!-- timeline item -->
<li>
<i class="fa fa-<?php print $build->getProject()->getIcon(); ?> bg-<?php print $color; ?>"></i>
<div class="timeline-item">
<span class="time"><i class="fa fa-clock-o"></i> <?php print Lang::formatDateTime($updated, 'LT'); ?></span>
<h3 class="timeline-header">
<a href="<?php print APP_URL; ?>project/view/<?php print $build->getProjectId(); ?>">
<?php print $build->getProject()->getTitle(); ?>
</a>
-
<a href="<?php print APP_URL; ?>build/view/<?php print $build->getId(); ?>">
Build #<?php print $build->getId(); ?>
</a>
-
<?php print $label; ?>
</h3>
<div class="timeline-body">
<?php
if ($build->getCommitId() !== 'Manual') {
print sprintf(
'<a href="%s" target="_blank">%s (%s)</a>',
$build->getCommitLink(),
substr($build->getCommitId(), 0, 7),
$build->getCommitterEmail()
);
} else {
print Lang::get('manual_build');
}
?>
- <?php print $build->getCommitMessage(); ?>
</div>
</div>
</li>
<!-- END timeline item -->
<?php endforeach; ?>
<li>
<i class="fa fa-clock-o"></i>
</li>
</ul>

View file

@ -80,7 +80,7 @@ foreach($projects as $project):
}
?>
<div class="project-box" id="project-box-<?php print $project->getId(); ?>">
<div class="small-box small-box-full bg-<?php print $subcls; ?>">
<div class="inner">
@ -131,5 +131,6 @@ foreach($projects as $project):
} ?>
<div style="clear: both;"></div>
</div>
</div>
<?php endforeach; ?>

View file

@ -21,7 +21,7 @@
<h3 class="box-title"><?php Lang::out('latest_builds'); ?></h3>
</div>
<div class="box-body">
<div class="box-body" id="timeline-box">
<ul class="timeline">
<?php $last = new \DateTime('-1 Year'); ?>

View file

@ -1,64 +0,0 @@
<?php use PHPCensor\Helper\Lang; ?>
<div class="alert alert-danger">
<p><strong>Adding requirements to the PHP Censor composer.json file is no longer recommended as a method of installing your required testing tools.</strong><br>
For this reason, we have removed the ability for PHP Censor to modify the composer.json file for you.
We recommend that you install testing tools using your project's own composer.json file, by adding them to the "require-dev" section of the file.</p>
</div>
<div class="row">
<div class="col-md-6">
<div class="box">
<div class="box-header">
<h3 class="box-title"><?php Lang::out('enabled_plugins'); ?></h3>
</div>
<table class="table">
<thead>
<tr>
<th><?php Lang::out('name'); ?></th>
<th><?php Lang::out('class'); ?></th>
<th><?php Lang::out('provided_by_package'); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($plugins as $plugin): ?>
<tr>
<td><?php print $plugin->name; ?></td>
<td><?php print $plugin->class; ?></td>
<td><?php print $plugin->source; ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
<div class="col-md-6">
<div class="box">
<div class="box-header">
<h3 class="box-title"><?php Lang::out('installed_packages'); ?></h3>
</div>
<table class="table">
<thead>
<tr>
<th><?php Lang::out('title'); ?></th>
<th><?php Lang::out('version'); ?></th>
<th width="1"></th>
</tr>
</thead>
<tbody>
<?php foreach ($installedPackages as $package => $version): ?>
<tr>
<td><?php echo $package; ?></td>
<td><?php echo $version; ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>