diff --git a/api/v1/entries/modules.php b/api/v1/entries/modules.php
new file mode 100644
index 0000000000..f38e90383a
--- /dev/null
+++ b/api/v1/entries/modules.php
@@ -0,0 +1,43 @@
+
+ * @package entries
+ * @version 1
+ * @link http://www.zentao.net
+ */
+class modulesEntry extends Entry
+{
+ /**
+ * Get method.
+ *
+ * @access public
+ * @return void
+ */
+ public function get()
+ {
+ $objectType = $this->param('type', '');
+ $objectID = $this->param('id', '');
+
+ if(!$objectType or !$objectID) return $this->sendError(400, 'Need id and type.');
+ if(!in_array($objectType, array('story', 'task', 'bug', 'case'))) return $this->sendError(400, 'Type is not allowed');
+
+ if($objectType == 'task')
+ {
+ $control = $this->loadController('tree', 'browsetask');
+ $control->browseTask($objectID);
+ }
+ else
+ {
+ $control = $this->loadController('tree', 'browse');
+ $control->browse($objectID, $objectType);
+ }
+ $data = $this->getData();
+ if(isset($data->status) and $data->status == 'success') return $this->send(200, array('modules' => $data->data->tree));
+
+ return $this->send400(isset($data->message) ? $data->message: 'error');
+ }
+}
diff --git a/config/routes.php b/config/routes.php
index 324765ae4d..a484abdcda 100644
--- a/config/routes.php
+++ b/config/routes.php
@@ -140,6 +140,8 @@ $routes['/docs/:id'] = 'doc';
$routes['/repos'] = 'repos';
$routes['/repos/rules'] = 'reporules';
+$routes['/modules'] = 'modules';
+
$routes['/reports'] = 'reports';
$routes['/z/folders'] = 'zfolders';
diff --git a/module/bug/view/create.html.php b/module/bug/view/create.html.php
index c30415c621..def2c34a4b 100644
--- a/module/bug/view/create.html.php
+++ b/module/bug/view/create.html.php
@@ -124,9 +124,7 @@ if($this->app->tab == 'project') js::set('objectID', $projectID);
diff --git a/module/execution/control.php b/module/execution/control.php
index 964df20efa..f248464ce5 100644
--- a/module/execution/control.php
+++ b/module/execution/control.php
@@ -1317,7 +1317,7 @@ class execution extends control
$this->view->title = $this->lang->execution->tips;
$this->view->tips = $this->fetch('execution', 'tips', "executionID=$executionID");
- $this->view->defaultURL = isset($this->session->closeTipsList) ? $this->session->closeTipsList : $this->createLink('execution', 'task', 'executionID=' . $executionID);
+ $this->view->defaultURL = $this->session->closeTipsList ? $this->session->closeTipsList : $this->createLink('execution', 'task', 'executionID=' . $executionID);
$this->view->executionID = $executionID;
$this->view->projectID = $projectID;
$this->view->project = $project;
diff --git a/module/kanban/css/createspace.css b/module/kanban/css/createspace.css
index ac6894793f..f5ad743eb1 100644
--- a/module/kanban/css/createspace.css
+++ b/module/kanban/css/createspace.css
@@ -1 +1 @@
-#mainContent {padding-left: 0px;}
+#mainContent {padding-left: 0px; height: 500px;}
diff --git a/module/kanban/css/editspace.css b/module/kanban/css/editspace.css
index ac6894793f..f5ad743eb1 100644
--- a/module/kanban/css/editspace.css
+++ b/module/kanban/css/editspace.css
@@ -1 +1 @@
-#mainContent {padding-left: 0px;}
+#mainContent {padding-left: 0px; height: 500px;}
diff --git a/module/kanban/js/view.js b/module/kanban/js/view.js
index 346329d0d7..531b664c2c 100644
--- a/module/kanban/js/view.js
+++ b/module/kanban/js/view.js
@@ -1587,3 +1587,8 @@ function resetRegionHeight(fold)
$('.region').css('height', regionHeaderHeight);
}
}
+
+$('.dropdown-menu').click(function()
+{
+ $.zui.ContextMenu.hide();
+})
diff --git a/module/kanban/model.php b/module/kanban/model.php
index b6a9ce85dd..f88d8b4f7d 100644
--- a/module/kanban/model.php
+++ b/module/kanban/model.php
@@ -1647,14 +1647,14 @@ class kanbanModel extends model
public function getSpacePairs($browseType = 'private')
{
$account = $this->app->user->account;
- $spaceIdList = $this->getCanViewObjects('kanbanspace');
+ $spaceIdList = $this->getCanViewObjects('kanbanspace', $browseType);
return $this->dao->select('id,name')->from(TABLE_KANBANSPACE)
->where('deleted')->eq(0)
+ ->andWhere('id')->in($spaceIdList)
->beginIF(in_array($browseType, array('private', 'cooperation', 'public')))->andWhere('type')->eq($browseType)->fi()
->beginIF($browseType == 'involved')->andWhere('owner')->ne($account)->fi()
->beginIF($this->cookie->showClosed == 0 and $browseType != 'showClosed')->andWhere('status')->ne('closed')->fi()
- ->beginIF(!$this->app->user->admin)->andWhere('id')->in($spaceIdList)->fi()
->orderBy('id_desc')
->fetchPairs('id');
}
@@ -1694,7 +1694,7 @@ class kanbanModel extends model
$spaceList = $objectType == 'kanban' ? $this->dao->select('id,owner,type')->from(TABLE_KANBANSPACE)->fetchAll('id') : array();
- if($this->app->user->admin and strpos('private,involved', $param) === false) return array_keys($objects);
+ if($param and $this->app->user->admin and strpos('private,involved', $param) === false) return array_keys($objects);
$account = $this->app->user->account;
foreach($objects as $objectID => $object)
diff --git a/module/kanban/view/editcard.html.php b/module/kanban/view/editcard.html.php
index 26f9bd2d4c..22c6b4eb4a 100644
--- a/module/kanban/view/editcard.html.php
+++ b/module/kanban/view/editcard.html.php
@@ -71,6 +71,7 @@
+ progress):?>
| kanbancard->progress;?> |
@@ -80,6 +81,7 @@
|
+
diff --git a/module/kanban/view/viewcard.html.php b/module/kanban/view/viewcard.html.php
index 24399cc0e3..57c58922cb 100644
--- a/module/kanban/view/viewcard.html.php
+++ b/module/kanban/view/viewcard.html.php
@@ -114,10 +114,12 @@
kanbancard->estimate;?> |
estimate, 2) . ' ' . $lang->kanbancard->lblHour;?> |
+ progress):?>
| kanbancard->progress;?> |
progress, 2) . ' %';?> |
+
diff --git a/module/tree/control.php b/module/tree/control.php
index 878886378a..330004dece 100644
--- a/module/tree/control.php
+++ b/module/tree/control.php
@@ -50,7 +50,12 @@ class tree extends control
if(strpos('story|bug|case', $viewType) !== false)
{
$product = $this->loadModel('product')->getById($rootID);
- if(empty($product)) $this->locate($this->createLink('product', 'create'));
+ if(empty($product))
+ {
+ if($this->viewType == 'json' or (defined('RUN_MODE') && RUN_MODE == 'api')) return $this->send(array('result' => 'fail', 'message' => 'No product.'));
+ $this->locate($this->createLink('product', 'create'));
+ }
+
if(!empty($product->type) && $product->type != 'normal')
{
$branches = $this->loadModel('branch')->getPairs($product->id);
diff --git a/test/class/bug.class.php b/test/class/bug.class.php
index 8167405ecb..6e9777b89f 100644
--- a/test/class/bug.class.php
+++ b/test/class/bug.class.php
@@ -1841,4 +1841,118 @@ class bugTest
return $array;
}
}
+
+ /**
+ * Test form customed bugs.
+ *
+ * @param array $bugIDList
+ * @access public
+ * @return array
+ */
+ public function formCustomedBugsTest($bugIDList)
+ {
+ $bugs = $this->objectModel->getByList($bugIDList);
+ $array = $this->objectModel->formCustomedBugs($bugs);
+
+ if(dao::isError())
+ {
+ return dao::getError();
+ }
+ else
+ {
+ return $array;
+ }
+ }
+
+ /**
+ * Test adjust the action is clickable.
+ *
+ * @param object $bug
+ * @param string $action
+ * @access public
+ * @return int
+ */
+ public function isClickableTest($bug, $action)
+ {
+ $object = $this->objectModel->isClickable($bug, $action);
+
+ if(dao::isError())
+ {
+ return dao::getError();
+ }
+ else
+ {
+ return $object ? 1 : 2;
+ }
+ }
+
+ /**
+ * Test link bug to build and release.
+ *
+ * @param array $bugIDList
+ * @param int $resolvedBuild
+ * @access public
+ * @return object
+ */
+ public function linkBugToBuildTest($bugIDList, $resolvedBuild)
+ {
+ $this->objectModel->linkBugToBuild($bugIDList, $resolvedBuild);
+
+ global $tester;
+ $release = $tester->dao->select('id,bugs')->from(TABLE_RELEASE)->where('build')->eq($resolvedBuild)->andWhere('deleted')->eq('0')->fetch();
+
+ if(dao::isError())
+ {
+ return dao::getError();
+ }
+ else
+ {
+ return $release;
+ }
+ }
+
+ /**
+ * Test get project list.
+ *
+ * @param int $productID
+ * @access public
+ * @return string
+ */
+ public function getProjectsTest($productID)
+ {
+ $array = $this->objectModel->getProjects($productID);
+
+ $title = '';
+ foreach($array as $id => $project) $title .= ',' . $project;
+ $title = trim($title, ',');
+
+ if(dao::isError())
+ {
+ return dao::getError();
+ }
+ else
+ {
+ return $title;
+ }
+ }
+
+ /**
+ * Test get id list of all projects.
+ *
+ * @access public
+ * @return array
+ */
+ public function getAllProjectIdsTest()
+ {
+ $array = $this->objectModel->getAllProjectIds();
+
+ if(dao::isError())
+ {
+ return dao::getError();
+ }
+ else
+ {
+ return $array;
+ }
+ }
}
diff --git a/test/model/bug/formcustomedbugs.php b/test/model/bug/formcustomedbugs.php
new file mode 100755
index 0000000000..b962f4bb80
--- /dev/null
+++ b/test/model/bug/formcustomedbugs.php
@@ -0,0 +1,32 @@
+#!/usr/bin/env php
+formCustomedBugs();
+cid=1
+pid=1
+
+获取bug 1 2的module story task case execution名称 >> 产品模块1,软件需求2,0,0,迭代1;产品模块2,软件需求6,0,0,迭代1
+获取bug 3 4的module story task case execution名称 >> 产品模块3,软件需求10,0,0,迭代1;产品模块5,软件需求14,0,0,迭代2
+获取bug 5 6的module story task case execution名称 >> 产品模块6,软件需求18,0,0,迭代2;产品模块7,软件需求22,0,0,迭代2
+获取bug 7 8的module story task case execution名称 >> 产品模块11,软件需求26,0,0,迭代3;产品模块12,软件需求30,0,0,迭代3
+获取bug 9 10的module story task case execution名称 >> 产品模块13,软件需求34,0,0,迭代3;0,软件需求38,0,0,迭代4
+
+*/
+
+$bugIDList1 = array('1', '2');
+$bugIDList2 = array('3', '4');
+$bugIDList3 = array('5', '6');
+$bugIDList4 = array('7', '8');
+$bugIDList5 = array('9', '10');
+
+$bug = new bugTest();
+r($bug->formCustomedBugsTest($bugIDList1)) && p('1:module,story,task,case,execution;2:module,story,task,case,execution') && e('产品模块1,软件需求2,0,0,迭代1;产品模块2,软件需求6,0,0,迭代1'); // 获取bug 1 2的module story task case execution名称
+r($bug->formCustomedBugsTest($bugIDList2)) && p('3:module,story,task,case,execution;4:module,story,task,case,execution') && e('产品模块3,软件需求10,0,0,迭代1;产品模块5,软件需求14,0,0,迭代2'); // 获取bug 3 4的module story task case execution名称
+r($bug->formCustomedBugsTest($bugIDList3)) && p('5:module,story,task,case,execution;6:module,story,task,case,execution') && e('产品模块6,软件需求18,0,0,迭代2;产品模块7,软件需求22,0,0,迭代2'); // 获取bug 5 6的module story task case execution名称
+r($bug->formCustomedBugsTest($bugIDList4)) && p('7:module,story,task,case,execution;8:module,story,task,case,execution') && e('产品模块11,软件需求26,0,0,迭代3;产品模块12,软件需求30,0,0,迭代3'); // 获取bug 7 8的module story task case execution名称
+r($bug->formCustomedBugsTest($bugIDList5)) && p('9:module,story,task,case,execution;10:module,story,task,case,execution') && e('产品模块13,软件需求34,0,0,迭代3;0,软件需求38,0,0,迭代'); // 获取bug 9 10的module story task case execution名称
diff --git a/test/model/bug/getallprojectids.php b/test/model/bug/getallprojectids.php
new file mode 100755
index 0000000000..dd6ff6101d
--- /dev/null
+++ b/test/model/bug/getallprojectids.php
@@ -0,0 +1,29 @@
+#!/usr/bin/env php
+getAllProjectIds();
+cid=1
+pid=1
+
+测试projectId为11的项目 >> 11
+测试projectId为12的项目 >> 12
+测试projectId为13的项目 >> 13
+测试projectId为41的项目 >> 41
+测试projectId为51的项目 >> 51
+测试projectId为91的项目 >> 91
+
+*/
+
+$projectIDList = array('11', '12', '13', '41', '51', '91');
+
+$bug=new bugTest();
+r($bug->getAllProjectIdsTest()) && p("$projectIDList[0]") && e('11'); // 测试projectId为11的项目
+r($bug->getAllProjectIdsTest()) && p("$projectIDList[1]") && e('12'); // 测试projectId为12的项目
+r($bug->getAllProjectIdsTest()) && p("$projectIDList[2]") && e('13'); // 测试projectId为13的项目
+r($bug->getAllProjectIdsTest()) && p("$projectIDList[3]") && e('41'); // 测试projectId为41的项目
+r($bug->getAllProjectIdsTest()) && p("$projectIDList[4]") && e('51'); // 测试projectId为51的项目
+r($bug->getAllProjectIdsTest()) && p("$projectIDList[5]") && e('91'); // 测试projectId为91的项目
diff --git a/test/model/bug/getprojects.php b/test/model/bug/getprojects.php
new file mode 100755
index 0000000000..d5d899256c
--- /dev/null
+++ b/test/model/bug/getprojects.php
@@ -0,0 +1,32 @@
+#!/usr/bin/env php
+getProjects();
+cid=1
+pid=1
+
+测试获取productID为1的项目 >> 项目1,项目11
+测试获取productID为2的项目 >> 项目2,项目12
+测试获取productID为3的项目 >> 项目3,项目13
+测试获取productID为4的项目 >> 项目4,项目14
+测试获取productID为5的项目 >> 项目5,项目15
+测试获取productID为6的项目 >> 项目6,项目16
+测试获取不存在的product的项目 >> 0
+
+*/
+
+
+$productIDList = array('1', '2', '3', '4','5', '6', '1000001');
+
+$bug=new bugTest();
+r($bug->getProjectsTest($productIDList[0])) && p() && e('项目1,项目11'); // 测试获取productID为1的项目
+r($bug->getProjectsTest($productIDList[1])) && p() && e('项目2,项目12'); // 测试获取productID为2的项目
+r($bug->getProjectsTest($productIDList[2])) && p() && e('项目3,项目13'); // 测试获取productID为3的项目
+r($bug->getProjectsTest($productIDList[3])) && p() && e('项目4,项目14'); // 测试获取productID为4的项目
+r($bug->getProjectsTest($productIDList[4])) && p() && e('项目5,项目15'); // 测试获取productID为5的项目
+r($bug->getProjectsTest($productIDList[5])) && p() && e('项目6,项目16'); // 测试获取productID为6的项目
+r($bug->getProjectsTest($productIDList[6])) && p() && e('0'); // 测试获取不存在的product的项目
diff --git a/test/model/bug/isclickable.php b/test/model/bug/isclickable.php
new file mode 100755
index 0000000000..18ae54ae11
--- /dev/null
+++ b/test/model/bug/isclickable.php
@@ -0,0 +1,82 @@
+#!/usr/bin/env php
+isClickable();
+cid=1
+pid=1
+
+状态为active confirmed为0的bug能否执行confirmbug动作 >> 1
+状态为active confirmed为0的bug能否执行resolve动作 >> 1
+状态为active confirmed为0的bug能否执行close动作 >> 2
+状态为active confirmed为0的bug能否执行activate动作 >> 2
+状态为active confirmed为0的bug能否执行tostory动作 >> 1
+状态为active confirmed为0的bug能否执行test动作 >> 1
+状态为active confirmed为1的bug能否执行confirmbug动作 >> 2
+状态为active confirmed为1的bug能否执行resolve动作 >> 1
+状态为active confirmed为1的bug能否执行close动作 >> 2
+状态为active confirmed为1的bug能否执行activate动作 >> 2
+状态为active confirmed为1的bug能否执行tostory动作 >> 1
+状态为active confirmed为1的bug能否执行test动作 >> 1
+状态为resolved的bug能否执行confirmbug动作 >> 2
+状态为resolved的bug能否执行resolve动作 >> 2
+状态为resolved的bug能否执行close动作 >> 1
+状态为resolved的bug能否执行activate动作 >> 1
+状态为resolved的bug能否执行tostory动作 >> 2
+状态为resolved的bug能否执行test动作 >> 1
+状态为closed的bug能否执行confirmbug动作 >> 2
+状态为closed的bug能否执行resolve动作 >> 2
+状态为closed的bug能否执行close动作 >> 2
+状态为closed的bug能否执行activate动作 >> 1
+状态为closed的bug能否执行tostory动作 >> 2
+状态为closed的bug能否执行test动作 >> 1
+
+*/
+
+$object1 = new stdclass();
+$object1->status = 'active';
+$object1->confirmed = 0;
+
+
+$object2 = new stdclass();
+$object2->status = 'active';
+$object2->confirmed = 1;
+
+$object3 = new stdclass();
+$object3->status = 'resolved';
+
+$object4 = new stdclass();
+$object4->status = 'closed';
+
+$actionList = array('confirmbug', 'resolve', 'close', 'activate', 'tostory', 'test');
+
+$bug=new bugTest();
+
+r($bug->isClickableTest($object1, $actionList[0])) && p() && e('1'); // 状态为active confirmed为0的bug能否执行confirmbug动作
+r($bug->isClickableTest($object1, $actionList[1])) && p() && e('1'); // 状态为active confirmed为0的bug能否执行resolve动作
+r($bug->isClickableTest($object1, $actionList[2])) && p() && e('2'); // 状态为active confirmed为0的bug能否执行close动作
+r($bug->isClickableTest($object1, $actionList[3])) && p() && e('2'); // 状态为active confirmed为0的bug能否执行activate动作
+r($bug->isClickableTest($object1, $actionList[4])) && p() && e('1'); // 状态为active confirmed为0的bug能否执行tostory动作
+r($bug->isClickableTest($object1, $actionList[5])) && p() && e('1'); // 状态为active confirmed为0的bug能否执行test动作
+r($bug->isClickableTest($object2, $actionList[0])) && p() && e('2'); // 状态为active confirmed为1的bug能否执行confirmbug动作
+r($bug->isClickableTest($object2, $actionList[1])) && p() && e('1'); // 状态为active confirmed为1的bug能否执行resolve动作
+r($bug->isClickableTest($object2, $actionList[2])) && p() && e('2'); // 状态为active confirmed为1的bug能否执行close动作
+r($bug->isClickableTest($object2, $actionList[3])) && p() && e('2'); // 状态为active confirmed为1的bug能否执行activate动作
+r($bug->isClickableTest($object2, $actionList[4])) && p() && e('1'); // 状态为active confirmed为1的bug能否执行tostory动作
+r($bug->isClickableTest($object2, $actionList[5])) && p() && e('1'); // 状态为active confirmed为1的bug能否执行test动作
+r($bug->isClickableTest($object3, $actionList[0])) && p() && e('2'); // 状态为resolved的bug能否执行confirmbug动作
+r($bug->isClickableTest($object3, $actionList[1])) && p() && e('2'); // 状态为resolved的bug能否执行resolve动作
+r($bug->isClickableTest($object3, $actionList[2])) && p() && e('1'); // 状态为resolved的bug能否执行close动作
+r($bug->isClickableTest($object3, $actionList[3])) && p() && e('1'); // 状态为resolved的bug能否执行activate动作
+r($bug->isClickableTest($object3, $actionList[4])) && p() && e('2'); // 状态为resolved的bug能否执行tostory动作
+r($bug->isClickableTest($object3, $actionList[5])) && p() && e('1'); // 状态为resolved的bug能否执行test动作
+r($bug->isClickableTest($object4, $actionList[0])) && p() && e('2'); // 状态为closed的bug能否执行confirmbug动作
+r($bug->isClickableTest($object4, $actionList[1])) && p() && e('2'); // 状态为closed的bug能否执行resolve动作
+r($bug->isClickableTest($object4, $actionList[2])) && p() && e('2'); // 状态为closed的bug能否执行close动作
+r($bug->isClickableTest($object4, $actionList[3])) && p() && e('1'); // 状态为closed的bug能否执行activate动作
+r($bug->isClickableTest($object4, $actionList[4])) && p() && e('2'); // 状态为closed的bug能否执行tostory动作
+r($bug->isClickableTest($object4, $actionList[5])) && p() && e('1'); // 状态为closed的bug能否执行test动作
diff --git a/test/model/bug/linkbugtobuild.php b/test/model/bug/linkbugtobuild.php
new file mode 100755
index 0000000000..83b22681f5
--- /dev/null
+++ b/test/model/bug/linkbugtobuild.php
@@ -0,0 +1,36 @@
+#!/usr/bin/env php
+linkBugToBuild();
+cid=1
+pid=1
+
+把bug 1 2关联到build 1 >> 1,2
+把bug 3 4关联到build 3 >> 3,4
+把bug 5 6关联到build 4 >> 5,6
+把bug 7 8关联到build 1 >> 1,2,7,8
+把bug 9 10关联到build 1 >> 1,2,7,8,9,10
+
+
+*/
+
+$bugIDList1 = array('1', '2');
+$bugIDList2 = array('3', '4');
+$bugIDList3 = array('5', '6');
+$bugIDList4 = array('7', '8');
+$bugIDList5 = array('9', '10');
+
+$buildList = array('1', '3', '5');
+
+$bug = new bugTest();
+r($bug->linkBugToBuildTest($bugIDList1, $buildList[0])) && p('bugs') && e('1,2'); // 把bug 1 2关联到build 1
+r($bug->linkBugToBuildTest($bugIDList2, $buildList[1])) && p('bugs') && e('3,4'); // 把bug 3 4关联到build 3
+r($bug->linkBugToBuildTest($bugIDList3, $buildList[2])) && p('bugs') && e('5,6'); // 把bug 5 6关联到build 5
+r($bug->linkBugToBuildTest($bugIDList4, $buildList[0])) && p('bugs') && e('1,2,7,8'); // 把bug 5 6关联到build 1
+r($bug->linkBugToBuildTest($bugIDList5, $buildList[0])) && p('bugs') && e('1,2,7,8,9,10'); // 把bug 5 6关联到build 1
+system("./ztest init");
diff --git a/www/js/my.full.js b/www/js/my.full.js
index c3b7069b6b..630874d86e 100644
--- a/www/js/my.full.js
+++ b/www/js/my.full.js
@@ -900,5 +900,12 @@ $(document).ready(function()
$('#globalCreate').hover(function()
{
$(this).prev().removeClass('open');
+ $(this).addClass('dropdown-hover');
+ });
+
+ /* Hide create button when global create menu is clicked. */
+ $('#globalCreate').click(function()
+ {
+ $(this).removeClass('dropdown-hover');
});
});