diff --git a/PHPCI/Application.php b/PHPCI/Application.php index 646645af..16fab7cf 100644 --- a/PHPCI/Application.php +++ b/PHPCI/Application.php @@ -14,6 +14,7 @@ use b8\Exception\HttpException; use b8\Http\Response; use b8\Http\Response\RedirectResponse; use b8\View; +use PHPCI\Model\Build; /** * PHPCI Front Controller @@ -91,18 +92,30 @@ class Application extends b8\Application $this->response->setContent($view->render()); } - if (View::exists('layout') && $this->response->hasLayout()) { - $view = new View('layout'); - $pageTitle = $this->config->get('page_title', null); + if ($this->response->hasLayout()) { + $this->setLayoutVariables($this->controller->layout); - if (!is_null($pageTitle)) { - $view->title = $pageTitle; - } - - $view->content = $this->response->getContent(); - $this->response->setContent($view->render()); + $this->controller->layout->content = $this->response->getContent(); + $this->response->setContent($this->controller->layout->render()); } return $this->response; } + + protected function loadController($class) + { + $controller = parent::loadController($class); + $controller->layout = new View('layout'); + $controller->layout->title = 'PHPCI'; + $controller->layout->breadcrumb = array(); + + return $controller; + } + + protected function setLayoutVariables(View &$layout) + { + /** @var \PHPCI\Store\ProjectStore $projectStore */ + $projectStore = b8\Store\Factory::getStore('Project'); + $layout->projects = $projectStore->getAll(); + } } diff --git a/PHPCI/Command/RunCommand.php b/PHPCI/Command/RunCommand.php index f6aa1288..f1c02360 100644 --- a/PHPCI/Command/RunCommand.php +++ b/PHPCI/Command/RunCommand.php @@ -62,8 +62,7 @@ class RunCommand extends Command { $this ->setName('phpci:run-builds') - ->setDescription('Run all pending PHPCI builds.') - ->addOption('verbose', 'v', InputOption::VALUE_NONE); + ->setDescription('Run all pending PHPCI builds.'); } /** @@ -75,7 +74,7 @@ class RunCommand extends Command // For verbose mode we want to output all informational and above // messages to the symphony output interface. - if ($input->getOption('verbose')) { + if ($input->hasOption('verbose') && $input->getOption('verbose')) { $this->logger->pushHandler( new OutputLogHandler($this->output, Logger::INFO) ); @@ -117,6 +116,7 @@ class RunCommand extends Command $this->logger->popHandler($buildDbLog); } catch (\Exception $ex) { $build->setStatus(Build::STATUS_FAILED); + $build->setFinished(new \DateTime()); $build->setLog($build->getLog() . PHP_EOL . PHP_EOL . $ex->getMessage()); $store->save($build); } diff --git a/PHPCI/Controller/BuildController.php b/PHPCI/Controller/BuildController.php index 79e9eb94..2d0ea73a 100644 --- a/PHPCI/Controller/BuildController.php +++ b/PHPCI/Controller/BuildController.php @@ -13,6 +13,7 @@ use b8; use b8\Exception\HttpException\NotFoundException; use PHPCI\BuildFactory; use PHPCI\Model\Build; +use PHPCI\Model\Project; use PHPCI\Service\BuildService; /** @@ -58,8 +59,22 @@ class BuildController extends \PHPCI\Controller $this->view->build = $build; $this->view->data = $this->getBuildData($build); - $title = 'Build #' . $build->getId() . ' - ' . $build->getProjectTitle(); - $this->config->set('page_title', $title); + $this->layout->title = 'Build #' . $build->getId(); + $this->layout->subtitle = $build->getProjectTitle(); + + $nav = array( + 'title' => 'Build '.$build->getId(), + 'icon' => 'cog', + 'links' => array( + 'build/rebuild/' . $build->getId() => 'Rebuild Now', + ), + ); + + if ($_SESSION['phpci_user']->getIsAdmin()) { + $nav['links']['build/delete/' . $build->getId()] = 'Delete Build'; + } + + $this->layout->nav = $nav; } protected function getUiPlugins() @@ -168,4 +183,36 @@ class BuildController extends \PHPCI\Controller return $log; } + + public function latest() + { + $rtn = array( + 'pending' => $this->formatBuilds($this->buildStore->getByStatus(Build::STATUS_NEW)), + 'running' => $this->formatBuilds($this->buildStore->getByStatus(Build::STATUS_RUNNING)), + ); + + if ($this->request->isAjax()) { + die(json_encode($rtn)); + } + } + + protected function formatBuilds($builds) + { + Project::$sleepable = array('id', 'title', 'reference', 'type'); + + $rtn = array('count' => $builds['count'], 'items' => array()); + + 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; + } } diff --git a/PHPCI/Controller/HomeController.php b/PHPCI/Controller/HomeController.php index a4f8003d..3d42775d 100644 --- a/PHPCI/Controller/HomeController.php +++ b/PHPCI/Controller/HomeController.php @@ -11,6 +11,7 @@ namespace PHPCI\Controller; use b8; use PHPCI\BuildFactory; +use PHPCI\Model\Build; /** * Home Controller - Displays the PHPCI Dashboard. @@ -41,14 +42,14 @@ class HomeController extends \PHPCI\Controller */ public function index() { + $this->layout->title = 'Dashboard'; + $projects = $this->projectStore->getWhere(array(), 50, 0, array(), array('title' => 'ASC')); - $this->view->builds = $this->getLatestBuildsHtml(); + $this->view->builds = $this->buildStore->getLatestBuilds(null, 10); $this->view->projects = $projects['items']; $this->view->summary = $this->getSummaryHtml($projects); - $this->config->set('page_title', 'Dashboard'); - return $this->view->render(); } diff --git a/PHPCI/Controller/PluginController.php b/PHPCI/Controller/PluginController.php index 5cbb662d..b4281626 100644 --- a/PHPCI/Controller/PluginController.php +++ b/PHPCI/Controller/PluginController.php @@ -24,6 +24,10 @@ use PHPCI\Plugin\Util\PluginInformationCollection; class PluginController extends \PHPCI\Controller { protected $required = array( + 'php', + 'ext-mcrypt', + 'ext-pdo', + 'ext-pdo_mysql', 'block8/b8framework', 'ircmaxell/password-compat', 'swiftmailer/swiftmailer', @@ -31,7 +35,8 @@ class PluginController extends \PHPCI\Controller 'symfony/console', 'psr/log', 'monolog/monolog', - 'pimple/pimple' + 'pimple/pimple', + 'robmorgan/phinx', ); protected $canInstall; @@ -60,7 +65,7 @@ class PluginController extends \PHPCI\Controller $this->view->plugins = $pluginInfo->getInstalledPlugins(); - $this->config->set('page_title', 'Plugins'); + $this->layout->title = 'Plugins'; return $this->view->render(); } diff --git a/PHPCI/Controller/ProjectController.php b/PHPCI/Controller/ProjectController.php index 1e2c68d1..5b594b96 100644 --- a/PHPCI/Controller/ProjectController.php +++ b/PHPCI/Controller/ProjectController.php @@ -62,8 +62,9 @@ class ProjectController extends \PHPCI\Controller /** * View a specific project. */ - public function view($projectId, $branch = '') + public function view($projectId) { + $branch = $this->getParam('branch', ''); $project = $this->projectStore->getById($projectId); if (empty($project)) { @@ -87,7 +88,8 @@ class ProjectController extends \PHPCI\Controller $this->view->page = $page; $this->view->pages = $pages; - $this->config->set('page_title', $project->getTitle()); + $this->layout->title = $project->getTitle(); + $this->layout->subtitle = $this->view->branch; return $this->view->render(); } @@ -134,8 +136,9 @@ class ProjectController extends \PHPCI\Controller /** * AJAX get latest builds. */ - public function builds($projectId, $branch = '') + public function builds($projectId) { + $branch = $this->getParam('branch', ''); $builds = $this->getLatestBuildsHtml($projectId, urldecode($branch)); die($builds[0]); } @@ -173,7 +176,7 @@ class ProjectController extends \PHPCI\Controller */ public function add() { - $this->config->set('page_title', 'Add Project'); + $this->layout->title = 'Add Project'; $this->requireAdmin(); $method = $this->request->getMethod(); diff --git a/PHPCI/Controller/SessionController.php b/PHPCI/Controller/SessionController.php index a9b60333..f7bfa982 100644 --- a/PHPCI/Controller/SessionController.php +++ b/PHPCI/Controller/SessionController.php @@ -40,7 +40,7 @@ class SessionController extends \PHPCI\Controller if ($this->request->getMethod() == 'POST') { $user = $this->userStore->getByEmail($this->getParam('email')); - + if ($user && password_verify($this->getParam('password', ''), $user->getHash())) { $_SESSION['phpci_user_id'] = $user->getId(); header('Location: ' . $this->getLoginRedirect()); diff --git a/PHPCI/Controller/SettingsController.php b/PHPCI/Controller/SettingsController.php index bc5558ba..4cd9f0be 100644 --- a/PHPCI/Controller/SettingsController.php +++ b/PHPCI/Controller/SettingsController.php @@ -38,6 +38,7 @@ class SettingsController extends Controller public function index() { + $this->layout->title = 'Settings'; $this->view->settings = $this->settings; $emailSettings = array(); diff --git a/PHPCI/Controller/UserController.php b/PHPCI/Controller/UserController.php index 182cc3f0..3240edce 100644 --- a/PHPCI/Controller/UserController.php +++ b/PHPCI/Controller/UserController.php @@ -49,7 +49,7 @@ class UserController extends Controller $users = $this->userStore->getWhere(array(), 1000, 0, array(), array('email' => 'ASC')); $this->view->users = $users; - $this->config->set('page_title', 'Users'); + $this->layout->title = 'Users'; return $this->view->render(); } @@ -58,6 +58,8 @@ class UserController extends Controller { $user = $_SESSION['phpci_user']; + $this->layout->title = 'Edit Profile'; + if ($this->request->getMethod() == 'POST') { $name = $this->getParam('name', null); $email = $this->getParam('email', null); @@ -65,6 +67,8 @@ class UserController extends Controller $_SESSION['phpci_user'] = $this->userService->updateUser($user, $name, $email, $password); $user = $_SESSION['phpci_user']; + + $this->view->updated = 1; } $values = $user->getDataArray(); @@ -115,7 +119,7 @@ class UserController extends Controller throw new ForbiddenException('You do not have permission to do that.'); } - $this->config->set('page_title', 'Add User'); + $this->layout->title = 'Add User'; $method = $this->request->getMethod(); @@ -164,6 +168,9 @@ class UserController extends Controller throw new NotFoundException('User with ID: ' . $userId . ' does not exist.'); } + $this->layout->title = $user->getName(); + $this->layout->subtitle = 'Edit User'; + $values = array_merge($user->getDataArray(), $this->getParams()); $form = $this->userForm($values, 'edit/' . $userId); diff --git a/PHPCI/Model/Base/ProjectBase.php b/PHPCI/Model/Base/ProjectBase.php index 5e1f4f37..0161243c 100644 --- a/PHPCI/Model/Base/ProjectBase.php +++ b/PHPCI/Model/Base/ProjectBase.php @@ -36,13 +36,12 @@ class ProjectBase extends Model 'id' => null, 'title' => null, 'reference' => null, - 'branch' => null, 'ssh_private_key' => null, - 'ssh_public_key' => null, 'type' => null, 'access_information' => null, 'last_commit' => null, 'build_config' => null, + 'ssh_public_key' => null, 'allow_public_status' => null, ); @@ -54,13 +53,12 @@ class ProjectBase extends Model 'id' => 'getId', 'title' => 'getTitle', 'reference' => 'getReference', - 'branch' => 'getBranch', 'ssh_private_key' => 'getSshPrivateKey', - 'ssh_public_key' => 'getSshPublicKey', 'type' => 'getType', 'access_information' => 'getAccessInformation', 'last_commit' => 'getLastCommit', 'build_config' => 'getBuildConfig', + 'ssh_public_key' => 'getSshPublicKey', 'allow_public_status' => 'getAllowPublicStatus', // Foreign key getters: @@ -74,13 +72,12 @@ class ProjectBase extends Model 'id' => 'setId', 'title' => 'setTitle', 'reference' => 'setReference', - 'branch' => 'setBranch', 'ssh_private_key' => 'setSshPrivateKey', - 'ssh_public_key' => 'setSshPublicKey', 'type' => 'setType', 'access_information' => 'setAccessInformation', 'last_commit' => 'setLastCommit', 'build_config' => 'setBuildConfig', + 'ssh_public_key' => 'setSshPublicKey', 'allow_public_status' => 'setAllowPublicStatus', // Foreign key setters: @@ -107,21 +104,11 @@ class ProjectBase extends Model 'length' => 250, 'default' => null, ), - 'branch' => array( - 'type' => 'varchar', - 'length' => 250, - 'default' => null, - ), 'ssh_private_key' => array( 'type' => 'text', 'nullable' => true, 'default' => null, ), - 'ssh_public_key' => array( - 'type' => 'text', - 'nullable' => true, - 'default' => null, - ), 'type' => array( 'type' => 'varchar', 'length' => 50, @@ -144,9 +131,15 @@ class ProjectBase extends Model 'nullable' => true, 'default' => null, ), + 'ssh_public_key' => array( + 'type' => 'text', + 'nullable' => true, + 'default' => null, + ), 'allow_public_status' => array( 'type' => 'tinyint', 'length' => 4, + 'default' => null, ), ); @@ -200,18 +193,6 @@ class ProjectBase extends Model return $rtn; } - /** - * Get the value of Branch / branch. - * - * @return string - */ - public function getBranch() - { - $rtn = $this->data['branch']; - - return $rtn; - } - /** * Get the value of SshPrivateKey / ssh_private_key. * @@ -224,18 +205,6 @@ class ProjectBase extends Model return $rtn; } - /** - * Get the value of SshPublicKey / ssh_public_key. - * - * @return string - */ - public function getSshPublicKey() - { - $rtn = $this->data['ssh_public_key']; - - return $rtn; - } - /** * Get the value of Type / type. * @@ -284,6 +253,18 @@ class ProjectBase extends Model return $rtn; } + /** + * Get the value of SshPublicKey / ssh_public_key. + * + * @return string + */ + public function getSshPublicKey() + { + $rtn = $this->data['ssh_public_key']; + + return $rtn; + } + /** * Get the value of AllowPublicStatus / allow_public_status. * @@ -356,26 +337,6 @@ class ProjectBase extends Model $this->_setModified('reference'); } - /** - * Set the value of Branch / branch. - * - * Must not be null. - * @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'); - } - /** * Set the value of SshPrivateKey / ssh_private_key. * @@ -394,24 +355,6 @@ class ProjectBase extends Model $this->_setModified('ssh_private_key'); } - /** - * Set the value of SshPublicKey / ssh_public_key. - * - * @param $value string - */ - public function setSshPublicKey($value) - { - $this->_validateString('SshPublicKey', $value); - - if ($this->data['ssh_public_key'] === $value) { - return; - } - - $this->data['ssh_public_key'] = $value; - - $this->_setModified('ssh_public_key'); - } - /** * Set the value of Type / type. * @@ -486,6 +429,24 @@ class ProjectBase extends Model $this->_setModified('build_config'); } + /** + * Set the value of SshPublicKey / ssh_public_key. + * + * @param $value string + */ + public function setSshPublicKey($value) + { + $this->_validateString('SshPublicKey', $value); + + if ($this->data['ssh_public_key'] === $value) { + return; + } + + $this->data['ssh_public_key'] = $value; + + $this->_setModified('ssh_public_key'); + } + /** * Set the value of AllowPublicStatus / allow_public_status. * diff --git a/PHPCI/Model/Project.php b/PHPCI/Model/Project.php index 702d9c89..98283d77 100644 --- a/PHPCI/Model/Project.php +++ b/PHPCI/Model/Project.php @@ -88,4 +88,28 @@ class Project extends ProjectBase return $this->data['branch']; } } + + public function getIcon() + { + switch ($this->getType()) { + case 'github': + $icon = 'github'; + break; + + case 'bitbucket': + $icon = 'bitbucket'; + break; + + case 'git': + case 'gitlab': + $icon = 'git'; + break; + + default: + $icon = 'cog'; + break; + } + + return $icon; + } } diff --git a/PHPCI/Store/Base/BuildMetaStoreBase.php b/PHPCI/Store/Base/BuildMetaStoreBase.php index bc1589cc..f6158c13 100644 --- a/PHPCI/Store/Base/BuildMetaStoreBase.php +++ b/PHPCI/Store/Base/BuildMetaStoreBase.php @@ -56,7 +56,6 @@ class BuildMetaStoreBase extends Store $add .= ' LIMIT ' . $limit; } - $count = null; $query = 'SELECT * FROM `build_meta` WHERE `project_id` = :project_id' . $add; $stmt = Database::getConnection($useConnection)->prepare($query); @@ -70,6 +69,9 @@ class BuildMetaStoreBase extends Store }; $rtn = array_map($map, $res); + $count = count($rtn); + + return array('items' => $rtn, 'count' => $count); } else { return array('items' => array(), 'count' => 0); @@ -88,7 +90,6 @@ class BuildMetaStoreBase extends Store $add .= ' LIMIT ' . $limit; } - $count = null; $query = 'SELECT * FROM `build_meta` WHERE `build_id` = :build_id' . $add; $stmt = Database::getConnection($useConnection)->prepare($query); @@ -102,6 +103,9 @@ class BuildMetaStoreBase extends Store }; $rtn = array_map($map, $res); + $count = count($rtn); + + return array('items' => $rtn, 'count' => $count); } else { return array('items' => array(), 'count' => 0); diff --git a/PHPCI/Store/Base/BuildStoreBase.php b/PHPCI/Store/Base/BuildStoreBase.php index b67d5f73..20927caa 100644 --- a/PHPCI/Store/Base/BuildStoreBase.php +++ b/PHPCI/Store/Base/BuildStoreBase.php @@ -56,7 +56,6 @@ class BuildStoreBase extends Store $add .= ' LIMIT ' . $limit; } - $count = null; $query = 'SELECT * FROM `build` WHERE `project_id` = :project_id' . $add; $stmt = Database::getConnection($useConnection)->prepare($query); @@ -70,6 +69,9 @@ class BuildStoreBase extends Store }; $rtn = array_map($map, $res); + $count = count($rtn); + + return array('items' => $rtn, 'count' => $count); } else { return array('items' => array(), 'count' => 0); @@ -88,7 +90,6 @@ class BuildStoreBase extends Store $add .= ' LIMIT ' . $limit; } - $count = null; $query = 'SELECT * FROM `build` WHERE `status` = :status' . $add; $stmt = Database::getConnection($useConnection)->prepare($query); @@ -102,6 +103,9 @@ class BuildStoreBase extends Store }; $rtn = array_map($map, $res); + $count = count($rtn); + + return array('items' => $rtn, 'count' => $count); } else { return array('items' => array(), 'count' => 0); diff --git a/PHPCI/Store/Base/ProjectStoreBase.php b/PHPCI/Store/Base/ProjectStoreBase.php index 410a305e..dda946a5 100644 --- a/PHPCI/Store/Base/ProjectStoreBase.php +++ b/PHPCI/Store/Base/ProjectStoreBase.php @@ -56,7 +56,6 @@ class ProjectStoreBase extends Store $add .= ' LIMIT ' . $limit; } - $count = null; $query = 'SELECT * FROM `project` WHERE `title` = :title' . $add; $stmt = Database::getConnection($useConnection)->prepare($query); @@ -70,6 +69,9 @@ class ProjectStoreBase extends Store }; $rtn = array_map($map, $res); + $count = count($rtn); + + return array('items' => $rtn, 'count' => $count); } else { return array('items' => array(), 'count' => 0); diff --git a/PHPCI/Store/BuildStore.php b/PHPCI/Store/BuildStore.php index a1da7cb1..8c36b3b1 100644 --- a/PHPCI/Store/BuildStore.php +++ b/PHPCI/Store/BuildStore.php @@ -10,6 +10,7 @@ namespace PHPCI\Store; use b8\Database; +use PHPCI\BuildFactory; use PHPCI\Model\Build; use PHPCI\Store\Base\BuildStoreBase; @@ -21,11 +22,22 @@ use PHPCI\Store\Base\BuildStoreBase; */ class BuildStore extends BuildStoreBase { - public function getLatestBuilds($projectId) + public function getLatestBuilds($projectId = null, $limit = 5) { - $query = 'SELECT * FROM build WHERE project_id = :pid ORDER BY id DESC LIMIT 5'; + $where = ''; + + if (!is_null($projectId)) { + $where = ' WHERE `project_id` = :pid '; + } + + $query = 'SELECT * FROM build '.$where.' ORDER BY id DESC LIMIT :limit'; $stmt = Database::getConnection('read')->prepare($query); - $stmt->bindValue(':pid', $projectId); + + if (!is_null($projectId)) { + $stmt->bindValue(':pid', $projectId); + } + + $stmt->bindValue(':limit', $limit, \PDO::PARAM_INT); if ($stmt->execute()) { $res = $stmt->fetchAll(\PDO::FETCH_ASSOC); diff --git a/PHPCI/Store/ProjectStore.php b/PHPCI/Store/ProjectStore.php index 6f81b8f4..4657fb8d 100644 --- a/PHPCI/Store/ProjectStore.php +++ b/PHPCI/Store/ProjectStore.php @@ -10,6 +10,7 @@ namespace PHPCI\Store; use b8\Database; +use PHPCI\Model\Project; use PHPCI\Store\Base\ProjectStoreBase; /** @@ -39,4 +40,26 @@ class ProjectStore extends ProjectStoreBase return array(); } } + + public function getAll() + { + $query = 'SELECT * FROM `project` ORDER BY `title` ASC'; + $stmt = Database::getConnection('read')->prepare($query); + + 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 array('items' => $rtn, 'count' => $count); + } else { + return array('items' => array(), 'count' => 0); + } + } } diff --git a/PHPCI/View/Build/header-row.phtml b/PHPCI/View/Build/header-row.phtml new file mode 100644 index 00000000..3dab6c80 --- /dev/null +++ b/PHPCI/View/Build/header-row.phtml @@ -0,0 +1,20 @@ +
Branch: getBranch(); ?>
+ +