From 74d313862c8ddda141a9f6a9d09771bec2c6be24 Mon Sep 17 00:00:00 2001 From: Dan Cryer Date: Thu, 8 Oct 2015 16:33:01 +0100 Subject: [PATCH] Adding project groups --- PHPCI/Application.php | 20 ++- PHPCI/Controller/GroupController.php | 104 +++++++++++ PHPCI/Controller/HomeController.php | 43 +++-- PHPCI/Controller/ProjectController.php | 16 ++ .../20151008140800_add_project_groups.php | 29 +++ PHPCI/Model/Base/ProjectBase.php | 112 +++++++++++- PHPCI/Model/Base/ProjectGroupBase.php | 168 ++++++++++++++++++ PHPCI/Model/ProjectGroup.php | 18 ++ PHPCI/Service/ProjectService.php | 4 + PHPCI/Store/Base/ProjectGroupStoreBase.php | 46 +++++ PHPCI/Store/Base/ProjectStoreBase.php | 49 ++--- PHPCI/Store/ProjectGroupStore.php | 18 ++ PHPCI/View/Group/edit.phtml | 9 + PHPCI/View/Group/index.phtml | 41 +++++ PHPCI/View/Home/index.phtml | 12 +- PHPCI/View/Project/view.phtml | 10 +- PHPCI/View/SummaryTable.phtml | 4 +- PHPCI/View/layout.phtml | 64 ++++--- 18 files changed, 695 insertions(+), 72 deletions(-) create mode 100644 PHPCI/Controller/GroupController.php create mode 100644 PHPCI/Migrations/20151008140800_add_project_groups.php create mode 100644 PHPCI/Model/Base/ProjectGroupBase.php create mode 100644 PHPCI/Model/ProjectGroup.php create mode 100644 PHPCI/Store/Base/ProjectGroupStoreBase.php create mode 100644 PHPCI/Store/ProjectGroupStore.php create mode 100644 PHPCI/View/Group/edit.phtml create mode 100644 PHPCI/View/Group/index.phtml diff --git a/PHPCI/Application.php b/PHPCI/Application.php index 8dcdf76d..f2d40e0d 100644 --- a/PHPCI/Application.php +++ b/PHPCI/Application.php @@ -135,15 +135,17 @@ class Application extends b8\Application */ protected function setLayoutVariables(View &$layout) { - /** @var \PHPCI\Store\ProjectStore $projectStore */ - $projectStore = b8\Store\Factory::getStore('Project'); - $layout->projects = $projectStore->getWhere( - array('archived' => (int)isset($_GET['archived'])), - 50, - 0, - array(), - array('title' => 'ASC') - ); + $groups = array(); + $groupList = b8\Store\Factory::getStore('ProjectGroup')->getWhere(array(), 100, 0, array(), array('title' => 'ASC')); + + foreach ($groupList['items'] as $group) { + $thisGroup = array('title' => $group->getTitle()); + $projects = b8\Store\Factory::getStore('Project')->getByGroupId($group->getId()); + $thisGroup['projects'] = $projects['items']; + $groups[] = $thisGroup; + } + + $layout->groups = $groups; } /** diff --git a/PHPCI/Controller/GroupController.php b/PHPCI/Controller/GroupController.php new file mode 100644 index 00000000..970a240c --- /dev/null +++ b/PHPCI/Controller/GroupController.php @@ -0,0 +1,104 @@ + + * @package PHPCI + * @subpackage Web + */ +class GroupController extends Controller +{ + /** + * @var \PHPCI\Store\ProjectGroupStore + */ + protected $groupStore; + + public function init() + { + $this->groupStore = b8\Store\Factory::getStore('ProjectGroup'); + } + + public function index() + { + $this->requireAdmin(); + + $groups = array(); + $groupList = $this->groupStore->getWhere(array(), 100, 0, array(), array('title' => 'ASC')); + + foreach ($groupList['items'] as $group) { + $thisGroup = array( + 'title' => $group->getTitle(), + 'id' => $group->getId(), + ); + $projects = b8\Store\Factory::getStore('Project')->getByGroupId($group->getId()); + $thisGroup['projects'] = $projects['items']; + $groups[] = $thisGroup; + } + + $this->view->groups = $groups; + } + + public function edit($id = null) + { + $this->requireAdmin(); + + if (!is_null($id)) { + $group = $this->groupStore->getById($id); + } else { + $group = new ProjectGroup(); + } + + if ($this->request->getMethod() == 'POST') { + $group->setTitle($this->getParam('title')); + $this->groupStore->save($group); + + $response = new b8\Http\Response\RedirectResponse(); + $response->setHeader('Location', PHPCI_URL.'group'); + return $response; + } + + $form = new Form(); + $form->setMethod('POST'); + $form->setAction(PHPCI_URL . 'group/edit' . (!is_null($id) ? '/' . $id : '')); + + $title = new Form\Element\Text('title'); + $title->setContainerClass('form-group'); + $title->setClass('form-control'); + $title->setLabel('Group Title'); + $title->setValue($group->getTitle()); + + $submit = new Form\Element\Submit(); + $submit->setValue('Save Group'); + + $form->addField($title); + $form->addField($submit); + + $this->view->form = $form; + } + + public function delete($id) + { + $this->requireAdmin(); + $group = $this->groupStore->getById($id); + + $this->groupStore->delete($group); + $response = new b8\Http\Response\RedirectResponse(); + $response->setHeader('Location', PHPCI_URL.'group'); + return $response; + } +} diff --git a/PHPCI/Controller/HomeController.php b/PHPCI/Controller/HomeController.php index d0e5a14b..e6c27e14 100644 --- a/PHPCI/Controller/HomeController.php +++ b/PHPCI/Controller/HomeController.php @@ -23,15 +23,20 @@ use PHPCI\Model\Build; class HomeController extends \PHPCI\Controller { /** - * @var \b8\Store\BuildStore + * @var \PHPCI\Store\BuildStore */ protected $buildStore; /** - * @var \b8\Store\ProjectStore + * @var \PHPCI\Store\ProjectStore */ protected $projectStore; + /** + * @var \PHPCI\Store\ProjectGroupStore + */ + protected $groupStore; + /** * Initialise the controller, set up stores and services. */ @@ -39,6 +44,7 @@ class HomeController extends \PHPCI\Controller { $this->buildStore = b8\Store\Factory::getStore('Build'); $this->projectStore = b8\Store\Factory::getStore('Project'); + $this->groupStore = b8\Store\Factory::getStore('ProjectGroup'); } /** @@ -47,15 +53,6 @@ class HomeController extends \PHPCI\Controller public function index() { $this->layout->title = Lang::get('dashboard'); - - $projects = $this->projectStore->getWhere( - array('archived' => (int)isset($_GET['archived'])), - 50, - 0, - array(), - array('title' => 'ASC') - ); - $builds = $this->buildStore->getLatestBuilds(null, 10); foreach ($builds as &$build) { @@ -63,8 +60,7 @@ class HomeController extends \PHPCI\Controller } $this->view->builds = $builds; - $this->view->projects = $projects['items']; - $this->view->summary = $this->getSummaryHtml($projects); + $this->view->groups = $this->getGroupInfo(); return $this->view->render(); } @@ -102,7 +98,7 @@ class HomeController extends \PHPCI\Controller $failures = array(); $counts = array(); - foreach ($projects['items'] as $project) { + foreach ($projects as $project) { $summaryBuilds[$project->getId()] = $this->buildStore->getLatestBuilds($project->getId()); $count = $this->buildStore->getWhere( @@ -122,7 +118,7 @@ class HomeController extends \PHPCI\Controller } $summaryView = new b8\View('SummaryTable'); - $summaryView->projects = $projects['items']; + $summaryView->projects = $projects; $summaryView->builds = $summaryBuilds; $summaryView->successful = $successes; $summaryView->failed = $failures; @@ -147,4 +143,21 @@ class HomeController extends \PHPCI\Controller return $view->render(); } + + protected function getGroupInfo() + { + $rtn = array(); + $groups = $this->groupStore->getWhere(array(), 100, 0, array(), array('title' => 'ASC')); + + foreach ($groups['items'] as $group) { + $thisGroup = array('title' => $group->getTitle()); + $projects = $this->projectStore->getByGroupId($group->getId()); + $thisGroup['projects'] = $projects['items']; + $thisGroup['summary'] = $this->getSummaryHtml($thisGroup['projects']); + $rtn[] = $thisGroup; + } + + return $rtn; + } + } diff --git a/PHPCI/Controller/ProjectController.php b/PHPCI/Controller/ProjectController.php index 3a55f49b..be5dee19 100644 --- a/PHPCI/Controller/ProjectController.php +++ b/PHPCI/Controller/ProjectController.php @@ -220,6 +220,7 @@ class ProjectController extends PHPCI\Controller 'build_config' => $this->getParam('build_config', null), 'allow_public_status' => $this->getParam('allow_public_status', 0), 'branch' => $this->getParam('branch', null), + 'group' => $this->getParam('group_id', null), ); $project = $this->projectService->createProject($title, $type, $reference, $options); @@ -284,6 +285,7 @@ class ProjectController extends PHPCI\Controller 'allow_public_status' => $this->getParam('allow_public_status', 0), 'archived' => $this->getParam('archived', 0), 'branch' => $this->getParam('branch', null), + 'group' => $this->getParam('group_id', null), ); $project = $this->projectService->updateProject($project, $title, $type, $reference, $options); @@ -352,6 +354,20 @@ class ProjectController extends PHPCI\Controller $field->setClass('form-control')->setContainerClass('form-group')->setValue('master'); $form->addField($field); + $field = Form\Element\Select::create('group_id', 'Project Group', true); + $field->setClass('form-control')->setContainerClass('form-group')->setValue(1); + + $groups = array(); + $groupStore = b8\Store\Factory::getStore('ProjectGroup'); + $groupList = $groupStore->getWhere(array(), 100, 0, array(), array('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); diff --git a/PHPCI/Migrations/20151008140800_add_project_groups.php b/PHPCI/Migrations/20151008140800_add_project_groups.php new file mode 100644 index 00000000..f6035014 --- /dev/null +++ b/PHPCI/Migrations/20151008140800_add_project_groups.php @@ -0,0 +1,29 @@ +table('project_group'); + $table->addColumn('title', 'string', array('limit' => 100, 'null' => false)); + $table->save(); + + $group = new \PHPCI\Model\ProjectGroup(); + $group->setTitle('Projects'); + + /** @var \PHPCI\Model\ProjectGroup $group */ + $group = \b8\Store\Factory::getStore('ProjectGroup')->save($group); + + $table = $this->table('project'); + $table->addColumn('group_id', 'integer', array( + 'signed' => true, + 'null' => false, + 'default' => $group->getId(), + )); + + $table->addForeignKey('group_id', 'project_group', 'id', array('delete'=> 'RESTRICT', 'update' => 'CASCADE')); + $table->save(); + } +} diff --git a/PHPCI/Model/Base/ProjectBase.php b/PHPCI/Model/Base/ProjectBase.php index 0ea77b78..0dc0c0eb 100644 --- a/PHPCI/Model/Base/ProjectBase.php +++ b/PHPCI/Model/Base/ProjectBase.php @@ -45,6 +45,7 @@ class ProjectBase extends Model 'ssh_public_key' => null, 'allow_public_status' => null, 'archived' => null, + 'group_id' => null, ); /** @@ -64,8 +65,10 @@ class ProjectBase extends Model 'ssh_public_key' => 'getSshPublicKey', 'allow_public_status' => 'getAllowPublicStatus', 'archived' => 'getArchived', + 'group_id' => 'getGroupId', // Foreign key getters: + 'Group' => 'getGroup', ); /** @@ -85,8 +88,10 @@ class ProjectBase extends Model 'ssh_public_key' => 'setSshPublicKey', 'allow_public_status' => 'setAllowPublicStatus', 'archived' => 'setArchived', + 'group_id' => 'setGroupId', // Foreign key setters: + 'Group' => 'setGroup', ); /** @@ -153,10 +158,14 @@ class ProjectBase extends Model ), 'archived' => array( 'type' => 'tinyint', - 'length' => 4, - 'nullable' => true, + 'length' => 1, 'default' => null, ), + 'group_id' => array( + 'type' => 'int', + 'length' => 11, + 'default' => 1, + ), ); /** @@ -165,12 +174,20 @@ class ProjectBase extends Model public $indexes = array( 'PRIMARY' => array('unique' => true, 'columns' => 'id'), 'idx_project_title' => array('columns' => 'title'), + 'group_id' => array('columns' => 'group_id'), ); /** * @var array */ public $foreignKeys = array( + 'project_ibfk_1' => array( + 'local_col' => 'group_id', + 'update' => 'CASCADE', + 'delete' => '', + 'table' => 'project_group', + 'col' => 'id' + ), ); /** @@ -317,6 +334,18 @@ class ProjectBase extends Model return $rtn; } + /** + * Get the value of GroupId / group_id. + * + * @return int + */ + public function getGroupId() + { + $rtn = $this->data['group_id']; + + return $rtn; + } + /** * Set the value of Id / id. * @@ -530,10 +559,12 @@ class ProjectBase extends Model /** * Set the value of Archived / archived. * + * Must not be null. * @param $value int */ public function setArchived($value) { + $this->_validateNotNull('Archived', $value); $this->_validateInt('Archived', $value); if ($this->data['archived'] === $value) { @@ -545,6 +576,83 @@ class ProjectBase extends Model $this->_setModified('archived'); } + /** + * Set the value of GroupId / group_id. + * + * Must not be null. + * @param $value int + */ + public function setGroupId($value) + { + $this->_validateNotNull('GroupId', $value); + $this->_validateInt('GroupId', $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. + * + * @uses \PHPCI\Store\ProjectGroupStore::getById() + * @uses \PHPCI\Model\ProjectGroup + * @return \PHPCI\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', 'PHPCI')->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 \PHPCI\Model\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 \PHPCI\Model\ProjectGroup + */ + public function setGroupObject(\PHPCI\Model\ProjectGroup $value) + { + return $this->setGroupId($value->getId()); + } + /** * Get Build models by ProjectId for this Project. * diff --git a/PHPCI/Model/Base/ProjectGroupBase.php b/PHPCI/Model/Base/ProjectGroupBase.php new file mode 100644 index 00000000..3b5eed69 --- /dev/null +++ b/PHPCI/Model/Base/ProjectGroupBase.php @@ -0,0 +1,168 @@ + null, + 'title' => null, + ); + + /** + * @var array + */ + protected $getters = array( + // Direct property getters: + 'id' => 'getId', + 'title' => 'getTitle', + + // Foreign key getters: + ); + + /** + * @var array + */ + protected $setters = array( + // Direct property setters: + 'id' => 'setId', + 'title' => 'setTitle', + + // Foreign key setters: + ); + + /** + * @var array + */ + public $columns = array( + 'id' => array( + 'type' => 'int', + 'length' => 11, + 'primary_key' => true, + 'auto_increment' => true, + 'default' => null, + ), + 'title' => array( + 'type' => 'varchar', + 'length' => 100, + 'default' => null, + ), + ); + + /** + * @var array + */ + public $indexes = array( + 'PRIMARY' => array('unique' => true, 'columns' => 'id'), + ); + + /** + * @var array + */ + public $foreignKeys = array( + ); + + /** + * Get the value of Id / id. + * + * @return int + */ + public function getId() + { + $rtn = $this->data['id']; + + return $rtn; + } + + /** + * Get the value of Title / title. + * + * @return string + */ + public function getTitle() + { + $rtn = $this->data['title']; + + return $rtn; + } + + /** + * Set the value of Id / id. + * + * Must not be null. + * @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'); + } + + /** + * Set the value of Title / title. + * + * Must not be null. + * @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'); + } + + /** + * Get Project models by GroupId for this ProjectGroup. + * + * @uses \PHPCI\Store\ProjectStore::getByGroupId() + * @uses \PHPCI\Model\Project + * @return \PHPCI\Model\Project[] + */ + public function getGroupProjects() + { + return Factory::getStore('Project', 'PHPCI')->getByGroupId($this->getId()); + } +} diff --git a/PHPCI/Model/ProjectGroup.php b/PHPCI/Model/ProjectGroup.php new file mode 100644 index 00000000..f85e6339 --- /dev/null +++ b/PHPCI/Model/ProjectGroup.php @@ -0,0 +1,18 @@ +setBranch($options['branch']); } + if (array_key_exists('group', $options)) { + $project->setGroup($options['group']); + } + // Allow certain project types to set access information: $this->processAccessInformation($project); diff --git a/PHPCI/Store/Base/ProjectGroupStoreBase.php b/PHPCI/Store/Base/ProjectGroupStoreBase.php new file mode 100644 index 00000000..5b2730ea --- /dev/null +++ b/PHPCI/Store/Base/ProjectGroupStoreBase.php @@ -0,0 +1,46 @@ +getById($value, $useConnection); + } + + public function getById($value, $useConnection = 'read') + { + if (is_null($value)) { + throw new HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); + } + + $query = 'SELECT * FROM `project_group` WHERE `id` = :id LIMIT 1'; + $stmt = Database::getConnection($useConnection)->prepare($query); + $stmt->bindValue(':id', $value); + + if ($stmt->execute()) { + if ($data = $stmt->fetch(\PDO::FETCH_ASSOC)) { + return new ProjectGroup($data); + } + } + + return null; + } +} diff --git a/PHPCI/Store/Base/ProjectStoreBase.php b/PHPCI/Store/Base/ProjectStoreBase.php index 562afba2..c764238d 100644 --- a/PHPCI/Store/Base/ProjectStoreBase.php +++ b/PHPCI/Store/Base/ProjectStoreBase.php @@ -20,24 +20,11 @@ class ProjectStoreBase extends Store protected $modelName = '\PHPCI\Model\Project'; protected $primaryKey = 'id'; - /** - * Returns a Project model by primary key. - * @param mixed $value - * @param string $useConnection - * @return \@appNamespace\Model\Project|null - */ public function getByPrimaryKey($value, $useConnection = 'read') { return $this->getById($value, $useConnection); } - /** - * Returns a Project model by Id. - * @param mixed $value - * @param string $useConnection - * @throws HttpException - * @return \@appNamespace\Model\Project|null - */ public function getById($value, $useConnection = 'read') { if (is_null($value)) { @@ -57,14 +44,6 @@ class ProjectStoreBase extends Store return null; } - /** - * Returns an array of Project models by Title. - * @param mixed $value - * @param int $limit - * @param string $useConnection - * @throws HttpException - * @return array - */ public function getByTitle($value, $limit = 1000, $useConnection = 'read') { if (is_null($value)) { @@ -92,4 +71,32 @@ class ProjectStoreBase extends Store return array('items' => array(), 'count' => 0); } } + + public function getByGroupId($value, $limit = 1000, $useConnection = 'read') + { + if (is_null($value)) { + throw new HttpException('Value passed to ' . __FUNCTION__ . ' cannot be null.'); + } + + + $query = 'SELECT * FROM `project` WHERE `group_id` = :group_id LIMIT :limit'; + $stmt = Database::getConnection($useConnection)->prepare($query); + $stmt->bindValue(':group_id', $value); + $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 array('items' => $rtn, 'count' => $count); + } else { + return array('items' => array(), 'count' => 0); + } + } } diff --git a/PHPCI/Store/ProjectGroupStore.php b/PHPCI/Store/ProjectGroupStore.php new file mode 100644 index 00000000..fa254e3e --- /dev/null +++ b/PHPCI/Store/ProjectGroupStore.php @@ -0,0 +1,18 @@ + +
+

Add / Edit Group

+
+ +
+ +
+ \ No newline at end of file diff --git a/PHPCI/View/Group/index.phtml b/PHPCI/View/Group/index.phtml new file mode 100644 index 00000000..e07c9af1 --- /dev/null +++ b/PHPCI/View/Group/index.phtml @@ -0,0 +1,41 @@ +
+ + Add Group + +
+ + +
+
+

Project Groups

+
+ + + + + + + + + + + + + + + + + + +
TitleProjects
+ + Edit + + + + + Delete + + +
+
\ No newline at end of file diff --git a/PHPCI/View/Home/index.phtml b/PHPCI/View/Home/index.phtml index 251bb9ee..6bce4be8 100644 --- a/PHPCI/View/Home/index.phtml +++ b/PHPCI/View/Home/index.phtml @@ -2,7 +2,17 @@
- + +
+
+

+
+ +
+ +
+
+
diff --git a/PHPCI/View/Project/view.phtml b/PHPCI/View/Project/view.phtml index ee21abe9..e66daa27 100644 --- a/PHPCI/View/Project/view.phtml +++ b/PHPCI/View/Project/view.phtml @@ -5,8 +5,16 @@
+ + + + + + + +
- + diff --git a/PHPCI/View/SummaryTable.phtml b/PHPCI/View/SummaryTable.phtml index 9ddaabb0..5e2192b4 100644 --- a/PHPCI/View/SummaryTable.phtml +++ b/PHPCI/View/SummaryTable.phtml @@ -83,7 +83,7 @@ foreach($projects as $project): ?> 10) { + if (count($projects) > 5) { $containerClass = 'small-box-minimal'; } else { $containerClass = 'small-box-full'; @@ -92,7 +92,7 @@ foreach($projects as $project):
- 10): ?> + 5): ?>

diff --git a/PHPCI/View/layout.phtml b/PHPCI/View/layout.phtml index 82eabfe5..cbd98161 100644 --- a/PHPCI/View/layout.phtml +++ b/PHPCI/View/layout.phtml @@ -159,6 +159,12 @@ +
  • + + Project Groups + +
  • +
  • @@ -180,41 +186,57 @@
  • - +
  • - - getTitle(); ?> + + + + */ ?>
  • - + + +