diff --git a/db/update16.1.sql b/db/update16.1.sql new file mode 100644 index 0000000000..f759fffb73 --- /dev/null +++ b/db/update16.1.sql @@ -0,0 +1,15 @@ +CREATE TABLE `zt_kanbancardgroup` ( + `id` int(8) NOT NULL AUTO_INCREMENT, + `kanban` mediumint(8) NOT NULL, + `objectType` char(30) NOT NULL, + `objectID` mediumint(8) NOT NULL, + `lane` mediumint(8) NOT NULL, + `column` mediumint(8) NOT NULL, + `order` mediumint(8) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `card_group` (`kanban`,`objectType`,`objectID`,`lane`,`column`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +ALTER TABLE `zt_kanban` ADD `displayCards` smallint(6) NOT NULL AFTER `order`; +ALTER TABLE `zt_kanbancard` DROP COLUMN `lane`; +ALTER TABLE `zt_kanbancard` DROP COLUMN `column`; diff --git a/db/zentao.sql b/db/zentao.sql index c733278613..f687d2b7be 100644 --- a/db/zentao.sql +++ b/db/zentao.sql @@ -677,6 +677,7 @@ CREATE TABLE `zt_kanban` ( `archived` enum('0', '1') NOT NULL DEFAULT '0', `status` enum('active','closed') NOT NULL default 'active', `order` mediumint(8) NOT NULL DEFAULT '0', + `displayCards` smallint(6) NOT NULL, `createdBy` char(30) NOT NULL, `createdDate` datetime NOT NULL, `lastEditedBy` char(30) NOT NULL, @@ -731,6 +732,18 @@ CREATE TABLE `zt_kanbancard` ( `deleted` enum('0', '1') NOT NULL DEFAULT '0', PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; +-- DROP TABLE IF EXISTS `zt_kanbancardgroup`; +CREATE TABLE `zt_kanbancardgroup` ( + `id` int(8) NOT NULL AUTO_INCREMENT, + `kanban` mediumint(8) NOT NULL, + `objectType` char(30) NOT NULL, + `objectID` mediumint(8) NOT NULL, + `lane` mediumint(8) NOT NULL, + `column` mediumint(8) NOT NULL, + `order` mediumint(8) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `card_group` (`kanban`,`objectType`,`objectID`,`lane`,`column`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; -- DROP TABLE IF EXISTS `zt_kanbangroup`; CREATE TABLE `zt_kanbangroup` ( `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, diff --git a/module/common/lang/menu.php b/module/common/lang/menu.php index 3bf77d2863..95959733ca 100644 --- a/module/common/lang/menu.php +++ b/module/common/lang/menu.php @@ -289,6 +289,11 @@ $lang->waterfall->menu->design['subMenu']->dbds = array('link' => "{$lang->d $lang->waterfall->menu->design['subMenu']->ads = array('link' => "{$lang->design->ADS}|design|browse|projectID=%s&productID=0&browseType=ADS"); $lang->waterfall->menu->design['subMenu']->bysearch = array('link' => ' ' . $lang->searchAB . ''); +/* Kanban project menu. */ +$lang->kanban->menu = new stdclass(); +$lang->kanban->menuOrder = array(); +$lang->kanban->dividerMenu = ''; + /* Execution menu. */ $lang->execution->homeMenu = new stdclass(); $lang->execution->homeMenu->all = array('link' => "{$lang->execution->all}|execution|all|", 'alias' => 'batchedit'); diff --git a/module/common/model.php b/module/common/model.php index fe1993ad7a..814602a66d 100644 --- a/module/common/model.php +++ b/module/common/model.php @@ -528,7 +528,7 @@ class commonModel extends model $attr = "class='iframe' data-width='650px'"; break; case 'project': - if(isset($config->maxVersion) and!defined('TUTORIAL')) + if(!defined('TUTORIAL')) { $params = "programID=0©ProjectID=0&extra=from=global"; $createMethod = 'createGuide'; diff --git a/module/execution/model.php b/module/execution/model.php index 549b58d0d2..e6663c158b 100644 --- a/module/execution/model.php +++ b/module/execution/model.php @@ -2478,6 +2478,23 @@ class executionModel extends model ->fetchAll('account'); } + /** + * Get members by execution id list. + * + * @param array $executionIdList + * @access public + * @return void + */ + public function getMembersByIdList($executionIdList) + { + return $this->dao->select("t1.root, t1.account, t2.realname")->from(TABLE_TEAM)->alias('t1') + ->leftJoin(TABLE_USER)->alias('t2')->on('t1.account = t2.account') + ->where('t1.root')->in($executionIdList) + ->andWhere('t1.type')->eq('execution') + ->andWhere('t2.deleted')->eq('0') + ->fetchGroup('root'); + } + /** * Get the skip members of the team. * diff --git a/module/project/control.php b/module/project/control.php index fb5a0cfbce..2851aa5228 100644 --- a/module/project/control.php +++ b/module/project/control.php @@ -222,10 +222,11 @@ class project extends control * Project index view. * * @param int $projectID + * @param string $browseType * @access public * @return void */ - public function index($projectID = 0) + public function index($projectID = 0, $browseType = 'all') { $projectID = $this->project->saveState($projectID, $this->project->getPairsByProgram()); @@ -240,6 +241,15 @@ class project extends control if(!$projectID) $this->locate($this->createLink('project', 'browse')); setCookie("lastProject", $projectID, $this->config->cookieLife, $this->config->webRoot, '', false, true); + if($project->model == 'kanban') + { + $kanbanList = $this->loadModel('execution')->getList($projectID, 'all', $browseType); + $this->view->kanbanList = $kanbanList; + $this->view->browseType = $browseType; + $this->view->memberGroup = $this->execution->getMembersByIdList(array_keys($kanbanList)); + $this->view->usersAvatar = $this->loadModel('user')->getAvatarPairs(); + } + $this->view->title = $this->lang->project->common . $this->lang->colon . $this->lang->project->index; $this->view->position[] = $this->lang->project->index; $this->view->project = $project; diff --git a/module/project/css/index.css b/module/project/css/index.css new file mode 100644 index 0000000000..c32fcc5b4f --- /dev/null +++ b/module/project/css/index.css @@ -0,0 +1,24 @@ +#cards > .col {width: 25% !important;} +#cards .panel {margin: 0 0; border: 1px solid #DCDCDC; border-radius: 4px; box-shadow: none; cursor: pointer; height: 160px; margin-bottom: 5px;} +#cards .panel:hover {border-color: #006AF1; box-shadow: 0 0 10px 0 rgba(0,0,100,.25);} +#cards .panel-heading {padding: 4px 2px 10px 16px;} +#cards .kanban-name {overflow: hidden; white-space:nowrap; width: 88%; float: left; padding-top: 8px;} +#cards .panel .label-closed {background: #AAA !important; color: #FFF; margin-top:-1px !important;} +#cards .kanban-actions {float: right; visibility: hidden;} +#cards .kanban-actions .dropdown-menu.pull-right {top:31px; right: -84px; left: auto;} +#cards .kanban-actions .dropdown-menu.pull-left {top:31px; right:-1px; left: auto;} +#cards .panel .label-closed {background: #AAA !important; color: #FFF; margin-top:-6px;} +#cards .panel-body {padding: 0 16px 16px;} +#cards .kanban-desc {color: #838a9d; word-break:break-all; height: 70px; overflow: hidden; display: -webkit-box; float:left;} +#cards .kanban-footer {position: absolute; bottom: 10px; right: 10px; left: 15px;} +#cards .kanban-members {float: left; height: 24px; line-height: 24px;} +#cards .kanban-members > div {display: inline-block; height: 24px;} +#cards .kanban-members > div + div {margin-left: -5px;} +#cards .kanban-members > div > .avatar {display: inline-block; width: 24px; height: 24px; line-height: 24px; margin-right: 1px;} +#cards .kanban-members > span {display: inline-block; color: transparent; width: 2px; height: 2px; background-color: #8990a2; position: relative; border-radius: 50%; top: 3px; margin: 0 3px;} +#cards .kanban-members > span:before, +#cards .kanban-members > span:after {content: ''; display: block; position: absolute; width: 2px; height: 2px; background-color: #8990a2; top: 0; border-radius: 50%} +#cards .kanban-members > span:before {left: -4px;} +#cards .kanban-members > span:after {right: -4px;} +#cards .kanban-members-total {display: inline-block; margin-left: 6px; position: relative; top: 3px} +#cards .kanbanAcl {position: absolute; right: 0px; bottom: 2px; color: #838a9d;} diff --git a/module/project/lang/en.php b/module/project/lang/en.php index 2df5ddb34c..bcc353b63e 100644 --- a/module/project/lang/en.php +++ b/module/project/lang/en.php @@ -155,6 +155,8 @@ $lang->project->etc = " , etc"; $lang->project->product = 'Product'; $lang->project->branch = 'Branch'; $lang->project->plan = 'Plan'; +$lang->project->kanban = 'Kanban'; +$lang->project->createKanban = 'Create Kanban'; /* Project Kanban. */ $lang->project->typeList = array(); @@ -220,6 +222,7 @@ $lang->project->currencySymbol['SGD'] = 'S$'; $lang->project->modelList['scrum'] = "Scrum"; $lang->project->modelList['waterfall'] = "CMMI"; +$lang->project->modelList['kanban'] = "Kanban"; $lang->project->featureBar['all'] = 'All'; $lang->project->featureBar['doing'] = 'Going'; @@ -252,6 +255,10 @@ $lang->project->endList[186] = 'Half yearly'; $lang->project->endList[365] = 'One year'; $lang->project->endList[999] = 'Longtime'; +$lang->project->scrumTitle = 'Agile Development Management'; +$lang->project->waterfallTitle = 'Waterfall Project Management'; +$lang->project->kanbanTitle = 'Kanban Project Management'; + $lang->project->empty = 'No project.'; $lang->project->nextStep = 'Next step'; $lang->project->hoursUnit = '%s hours'; @@ -260,7 +267,6 @@ $lang->project->lastIteration = "Recent {$lang->executionCommon}"; $lang->project->ongoingStage = 'Ongoing stage'; $lang->project->scrum = 'Scrum'; $lang->project->waterfall = 'Waterfall'; -$lang->project->waterfallTitle = 'Waterfall Project Management'; $lang->project->cannotCreateChild = 'It is not empty, so you cannot add a child. You can add a parent for it, and then create a child.'; $lang->project->confirmDelete = "Do you want to delete [%s]?"; $lang->project->emptyPM = 'No manager'; @@ -278,7 +284,6 @@ $lang->project->programTitle['end'] = 'End-level project only'; $lang->project->accessDenied = 'Access denied to this project'; $lang->project->chooseProgramType = 'Select management type'; -$lang->project->scrumTitle = 'Agile Development Management'; $lang->project->cannotCreateChild = 'The project has contents, so you cannot add a child project. You can create a parent project for this one and then add a child project for the parent project.'; $lang->project->hasChildren = 'This project has a child project, so it cannot be deleted.'; $lang->project->confirmDelete = 'Do you want to delete this project?'; diff --git a/module/project/lang/zh-cn.php b/module/project/lang/zh-cn.php index 643d7f02d8..c0e8a48e9b 100644 --- a/module/project/lang/zh-cn.php +++ b/module/project/lang/zh-cn.php @@ -155,6 +155,8 @@ $lang->project->etc = "等"; $lang->project->product = '所属产品'; $lang->project->branch = '所属分支'; $lang->project->plan = '所属计划'; +$lang->project->createKanban = '创建看板'; +$lang->project->kanban = '看板'; /* Project Kanban. */ $lang->project->typeList = array(); @@ -220,6 +222,7 @@ $lang->project->currencySymbol['SGD'] = 'S$'; $lang->project->modelList['scrum'] = "Scrum"; $lang->project->modelList['waterfall'] = "瀑布"; +$lang->project->modelList['kanban'] = "看板"; $lang->project->featureBar['all'] = '所有'; $lang->project->featureBar['doing'] = '进行中'; @@ -252,6 +255,10 @@ $lang->project->endList[186] = '半年'; $lang->project->endList[365] = '一年'; $lang->project->endList[999] = '长期'; +$lang->project->scrumTitle = '敏捷开发全流程项目管理'; +$lang->project->waterfallTitle = '瀑布式项目管理'; +$lang->project->kanbanTitle = '专业研发看板项目管理'; + $lang->project->empty = '暂时没有项目'; $lang->project->nextStep = '下一步'; $lang->project->hoursUnit = '%s 工时'; @@ -260,7 +267,6 @@ $lang->project->lastIteration = "近期{$lang->executionCommon}"; $lang->project->ongoingStage = '进行中的阶段'; $lang->project->scrum = 'Scrum'; $lang->project->waterfall = '瀑布'; -$lang->project->waterfallTitle = '瀑布式项目管理'; $lang->project->cannotCreateChild = '该项目已经有实际的内容,无法直接添加子项目。您可以为当前项目创建一个父项目,然后在新的父项目下面添加子项目。'; $lang->project->confirmDelete = "您确定要删除吗?"; $lang->project->emptyPM = '暂无'; @@ -278,7 +284,6 @@ $lang->project->programTitle['end'] = '只显示最后一级项目集'; $lang->project->accessDenied = '您无权访问该项目!'; $lang->project->chooseProgramType = '选择项目管理方式'; -$lang->project->scrumTitle = '敏捷开发全流程项目管理'; $lang->project->cannotCreateChild = '该项目已经有实际的内容,无法直接添加子项目。您可以为当前项目创建一个父项目,然后在新的父项目下面添加子项目。'; $lang->project->hasChildren = '该项目有子项目存在,不能删除。'; $lang->project->confirmDelete = "您确定删除项目[%s]吗?"; diff --git a/module/project/model.php b/module/project/model.php index c01a4e8c2c..d47fb02ab0 100644 --- a/module/project/model.php +++ b/module/project/model.php @@ -632,7 +632,7 @@ class projectModel extends model /** * Get project pairs by model and project. * - * @param string $model all|scrum|waterfall + * @param string $model all|scrum|waterfall|kanban * @param int $programID * @access public * @return array @@ -1776,7 +1776,7 @@ class projectModel extends model $type = 'project'; if($this->config->systemMode == 'new') { - if($project->type == 'sprint' or $project->type == 'stage') $type = 'execution'; + if($project->type == 'sprint' or $project->type == 'stage' or $project->type == 'kanban') $type = 'execution'; } $members = $this->dao->select("t1.account, if(t2.deleted='0', t2.realname, t1.account) as realname")->from(TABLE_TEAM)->alias('t1') diff --git a/module/project/view/ajaxgetdropmenu.html.php b/module/project/view/ajaxgetdropmenu.html.php index 4aa753f687..648ed20b7f 100644 --- a/module/project/view/ajaxgetdropmenu.html.php +++ b/module/project/view/ajaxgetdropmenu.html.php @@ -72,9 +72,12 @@ foreach($projects as $programID => $programProjects) foreach($programProjects as $index => $project) { - $selected = $project->id == $projectID ? 'selected' : ''; - $link = helper::createLink('project', 'index', "projectID=%s", '', '', $project->id); - $projectName = $project->model == 'scrum' ? ' ' . $project->name : ' ' . $project->name; + $selected = $project->id == $projectID ? 'selected' : ''; + $link = helper::createLink('project', 'index', "projectID=%s", '', '', $project->id); + $icon = ' '; + + if($project->model != 'scrum') $icon = " "; + $projectName = $icon . $project->name; if($project->status != 'done' and $project->status != 'closed' and $project->PM == $this->app->user->account) { diff --git a/module/project/view/createguide.html.php b/module/project/view/createguide.html.php index 42f5a457b9..ee90dc000c 100644 --- a/module/project/view/createguide.html.php +++ b/module/project/view/createguide.html.php @@ -15,20 +15,27 @@

project->chooseProgramType; ?>

-
+
createLink("project", "create", "model=scrum&programID=$programID©ProjectID=0&extra=productID=$productID,branchID=$branchID"), "", '', "data-app='project' class='createButton'")?>

project->scrum; ?>

project->scrumTitle; ?>

-
+
createLink("project", "create", "model=waterfall&programID=$programID©ProjectID=0&extra=productID=$productID,branchID=$branchID"), "", '', "data-app='project' class='createButton'")?>

project->waterfall; ?>

project->waterfallTitle; ?>

+
+
+ createLink("project", "create", "model=kanban&programID=$programID©ProjectID=0&extra=productID=$productID,branchID=$branchID"), "", '', "data-app='project' class='createButton'")?> +

project->kanban;?>

+

project->kanbanTitle;?>

+
+
diff --git a/module/project/view/index.html.php b/module/project/view/index.html.php index a21c11d5f4..83900d0e7e 100644 --- a/module/project/view/index.html.php +++ b/module/project/view/index.html.php @@ -10,5 +10,74 @@ */ ?> +model != 'kanban'):?> fetch('block', 'dashboard', "module=project&type={$project->model}&projectID={$project->id}");?> + + +
+
+ +
+

+ noData;?> + id", ' ' . $lang->project->createKanban, '', 'class="btn btn-info"');?> +

+
+ + $kanban):?> +
+
+
+
+ execution->statusList, $kanban->status);?> + name;?> +
+
+
+
desc));?>
+ +
+
+
+ + +
+
+