diff --git a/api/v1/entries/builds.php b/api/v1/entries/builds.php
index 3709a0463c..e303c9a4e1 100644
--- a/api/v1/entries/builds.php
+++ b/api/v1/entries/builds.php
@@ -21,6 +21,7 @@ class buildsEntry extends entry
public function get($projectID = 0)
{
if(empty($projectID)) $projectID = $this->param('project', 0);
+ if(empty($projectID)) return $this->sendError(400, "Need project id.");
$control = $this->loadController('project', 'build');
$control->build($projectID, $this->param('type', 'all'), $this->param('param', 0), $this->param('order', 't1.date_desc,t1.id_desc'));
diff --git a/api/v1/entries/execution.php b/api/v1/entries/execution.php
index 306d6f374b..c5cffb4fc0 100644
--- a/api/v1/entries/execution.php
+++ b/api/v1/entries/execution.php
@@ -64,6 +64,14 @@ class executionEntry extends Entry
{
switch($field)
{
+ case 'modules':
+ $control = $this->loadController('tree', 'browsetask');
+ $control->browsetask($executionID);
+ $data = $this->getData();
+ if(isset($data->status) and $data->status == 'success')
+ {
+ $execution->modules = $data->data->tree;
+ }
case 'builds':
$execution->builds = $this->loadModel('build')->getBuildPairs($productID, 'all', 'noempty,noterminate,nodone', $executionID, 'execution');
break;
diff --git a/api/v1/entries/executionmembers.php b/api/v1/entries/executionmembers.php
deleted file mode 100644
index 6b7a680b93..0000000000
--- a/api/v1/entries/executionmembers.php
+++ /dev/null
@@ -1,25 +0,0 @@
-
- * @package entries
- * @version 1
- * @link http://www.zentao.net
- */
-class executionMembersEntry extends entry
-{
- public function get($executionID = 0)
- {
- if(!$executionID) $executionID = $this->param('execution', 0);
- if(empty($executionID)) return $this->sendError(400, 'Need execution id.');
- $members = $this->loadModel('execution')->getTeamMembers($executionID);
- if(isset($members))
- {
- foreach($members as $member) $result[] = $this->format($member, 'join:date');
- }
- return $this->send(200, array('members' => $result));
- }
-}
diff --git a/api/v1/entries/executionstories.php b/api/v1/entries/executionstories.php
index d2f74e1126..4f32cb527c 100644
--- a/api/v1/entries/executionstories.php
+++ b/api/v1/entries/executionstories.php
@@ -34,7 +34,6 @@ class executionStoriesEntry extends entry
$result = array();
foreach($stories as $story)
{
- $story->name =$story->title;
$result[] = $this->format($story, 'openedBy:user,openedDate:time,assignedTo:user,assignedDate:time,reviewedBy:user,reviewedDate:time,lastEditedBy:user,lastEditedDate:time,closedBy:user,closedDate:time,deleted:bool,mailto:userList');
}
return $this->send(200, array('page' => $pager->pageID, 'total' => $pager->recTotal, 'limit' => $pager->recPerPage, 'stories' => $result));
diff --git a/api/v1/entries/modulestories.php b/api/v1/entries/modulestories.php
index 03a29b2f2f..16ac618148 100644
--- a/api/v1/entries/modulestories.php
+++ b/api/v1/entries/modulestories.php
@@ -15,16 +15,15 @@ class moduleStoriesEntry extends Entry
{
$executionID = $this->param('executionID', 0);
if(empty($executionID)) return $this->sendError(400, 'Need execution id.');
- $control = $this->loadController('story', 'ajaxGetExecutionStories');
- $control->ajaxGetExecutionStories($executionID, $this->param('productID', 0), $this->param('branchID', 0), $moduleID);
+
+ $control = $this->loadController('execution', 'story');
+ $control->story($executionID, 'order_desc', 'byModule', $moduleID);
$data = $this->getData();
- $this->loadModel('story');
+
$result = [];
- foreach($data as $storyID => $storyName)
+ foreach($data->data->stories as $story)
{
- if(empty($storyID)) continue;
- $story = $this->story->getById($storyID);
$result[] = $this->filterFields($story, 'id,title,module,pri,status,stage,estimate');
}
return $this->send(200, array('stories' => $result));
diff --git a/api/v1/entries/productplans.php b/api/v1/entries/productplans.php
index e903903f4c..f56cf3ae96 100644
--- a/api/v1/entries/productplans.php
+++ b/api/v1/entries/productplans.php
@@ -36,7 +36,6 @@ class productplansEntry extends entry
foreach($plans as $plan)
{
- $plan->name = $plan->title;
if($plan->parent > 0 and isset($result[$plan->parent]))
{
$parentPlan = $result[$plan->parent];
diff --git a/api/v1/entries/stories.php b/api/v1/entries/stories.php
index d92f3ddd6e..90223294ed 100644
--- a/api/v1/entries/stories.php
+++ b/api/v1/entries/stories.php
@@ -35,7 +35,6 @@ class storiesEntry extends entry
$result = array();
foreach($stories as $story)
{
- $story->name =$story->title;
if(isset($story->children)) $story->children = array_values((array)$story->children);
$result[] = $this->format($story, 'openedBy:user,openedDate:time,assignedTo:user,assignedDate:time,reviewedBy:user,reviewedDate:time,lastEditedBy:user,lastEditedDate:time,closedBy:user,closedDate:time,deleted:bool,mailto:userList');
}
@@ -55,7 +54,7 @@ class storiesEntry extends entry
if(!$productID and isset($this->requestBody->product)) $productID = $this->requestBody->product;
if(!$productID) return $this->sendError(400, 'Need product id.');
- $fields = 'title,spec,verify,reviewer,type,plan,module,moduleOptionMenu,source,sourceNote,category,pri,estimate,mailto,keywords';
+ $fields = 'title,spec,verify,reviewer,type,plan,module,moduleOptionMenu,source,sourceNote,category,pri,estimate,mailto,keywords,notifyemail,uid';
$this->batchSetPost($fields);
/* If reviewer is not post, set needNotReview. */
diff --git a/api/v1/entries/story.php b/api/v1/entries/story.php
index 77889a3dbc..10580c0f8a 100644
--- a/api/v1/entries/story.php
+++ b/api/v1/entries/story.php
@@ -95,7 +95,7 @@ class storyEntry extends Entry
$oldStory = $this->loadModel('story')->getByID($storyID);
/* Set $_POST variables. */
- $fields = 'title,product,reviewer,type,plan,module,source,sourceNote,category,pri,estimate,mailto,keywords';
+ $fields = 'title,product,reviewer,type,plan,module,source,sourceNote,category,pri,estimate,mailto,keywords,uid';
$this->batchSetPost($fields, $oldStory);
$this->setPost('parent', 0);
diff --git a/api/v1/entries/storyrecall.php b/api/v1/entries/storyrecall.php
new file mode 100644
index 0000000000..99e06cc210
--- /dev/null
+++ b/api/v1/entries/storyrecall.php
@@ -0,0 +1,30 @@
+
+ * @package entries
+ * @version 1
+ * @link http://www.zentao.net
+ */
+class storyRecallEntry extends Entry
+{
+ /**
+ * Delete method.
+ *
+ * @param int $storyID
+ * @access public
+ * @return void
+ */
+ public function delete($storyID)
+ {
+ $control = $this->loadController('story', 'recall');
+ $control->recall($storyID);
+
+ $this->getData();
+ $this->sendSuccess(200, 'success');
+ }
+}
+
diff --git a/api/v1/entries/storyreview.php b/api/v1/entries/storyreview.php
new file mode 100644
index 0000000000..0148229dc4
--- /dev/null
+++ b/api/v1/entries/storyreview.php
@@ -0,0 +1,42 @@
+
+ * @package entries
+ * @version 1
+ * @link http://www.zentao.net
+ **/
+
+class storyReviewEntry extends Entry
+{
+ /**
+ * POST method.
+ *
+ * @param int $storyID
+ * @access public
+ * @return void
+ */
+ public function post($storyID)
+ {
+ $oldStory = $this->loadModel('story')->getByID($storyID);
+
+ $fields = 'reviewedDate,result,closedReason,pri,estimate,';
+ $this->batchSetPost($fields);
+
+ $control = $this->loadController('story', 'review');
+
+ $control->review($storyID);
+
+ $data = $this->getData();
+ if(!$data or !isset($data->status)) return $this->send400('error');
+ if(isset($data->status) and $data->status == 'fail') return $this->sendError(zget($data, 'code', 400), $data->message);
+
+ $story = $this->loadModel('story')->getByID($storyID);
+
+ $this->send(200, $story);
+ }
+}
+
diff --git a/api/v1/entries/tabs.php b/api/v1/entries/tabs.php
index 71c245e10a..162293dcb3 100644
--- a/api/v1/entries/tabs.php
+++ b/api/v1/entries/tabs.php
@@ -50,7 +50,10 @@ class tabsEntry extends baseEntry
if($menuKey == 'requirement' and empty($this->config->URAndSR)) continue;
if(isset($this->lang->product->menu->$menuKey))
{
- list($label, $module, $method) = explode('|', $this->lang->product->menu->$menuKey['link']);
+ $menuName = $this->lang->product->menu->$menuKey;
+ if(!isset($menuName['link'])) continue;
+
+ list($label, $module, $method) = explode('|', $menuName['link']);
if(!common::hasPriv($module, $method)) continue;
}
else
diff --git a/api/v1/entries/task.php b/api/v1/entries/task.php
index b06bd8f677..15cd194d6e 100644
--- a/api/v1/entries/task.php
+++ b/api/v1/entries/task.php
@@ -102,7 +102,7 @@ class taskEntry extends Entry
$oldTask = $this->loadModel('task')->getByID($taskID);
/* Set $_POST variables. */
- $fields = 'name,type,desc,assignedTo,pri,estimate,left,consumed,story,parent,execution,module,closedReason,status,estStarted,deadline,team,teamEstimate,multiple,mailto';
+ $fields = 'name,type,desc,assignedTo,pri,estimate,left,consumed,story,parent,execution,module,closedReason,status,estStarted,deadline,team,teamEstimate,multiple,mailto,uid';
$this->batchSetPost($fields, $oldTask);
$control = $this->loadController('task', 'edit');
diff --git a/api/v1/entries/taskbatchcreate.php b/api/v1/entries/taskbatchcreate.php
index deda4e24ef..22731a0cc1 100644
--- a/api/v1/entries/taskbatchcreate.php
+++ b/api/v1/entries/taskbatchcreate.php
@@ -56,7 +56,7 @@ class taskBatchCreateEntry extends Entry
$estStarted[] = isset($task->estStarted) ? $task->estStarted : 0;
$deadlines[] = isset($task->deadline) ? $task->deadline : null;
$desc[] = isset($task->desc) ? $task->desc : '';
- $pri[] = isset($task->pri) ? $task->pri : '';
+ $pri[] = isset($task->pri) ? $task->pri : 0;
$stories[] = isset($task->story) ? $task->story : $storyID;
}
$this->setPost('module', $modules);
@@ -78,11 +78,9 @@ class taskBatchCreateEntry extends Entry
if(!$data) return $this->send400('error');
if(isset($data->status) and $data->status == 'fail') return $this->sendError(zget($data, 'code', 400), $data->message);
- $this->send(200, array());
+ if(!$taskID) return $this->send(200, array());
+ $task = $this->loadModel('task')->getById($taskID);
+ return $this->send(200, array('task' => $task));
}
- public function setArrayPost($field)
- {
- $_POST[$field] = array('0' => $_POST[$field]);
- }
}
diff --git a/api/v1/entries/taskcomponent.php b/api/v1/entries/taskcomponent.php
new file mode 100644
index 0000000000..50b83afef8
--- /dev/null
+++ b/api/v1/entries/taskcomponent.php
@@ -0,0 +1,45 @@
+
+ * @package entries
+ * @version 1
+ * @link http://www.zentao.net
+ */
+class taskComponentEntry extends Entry
+{
+ /**
+ * POST method.
+ *
+ * @param int $taskID
+ * @access public
+ * @return void
+ */
+ public function post($taskID)
+ {
+ $fields = 'name,color,type,assignedTo,parent,estimate,story,module,pri,desc,estStarted,deadline';
+ $this->batchSetPost($fields);
+
+ $fields = explode(',', $fields);
+ foreach($fields as $field) $this->setArrayPost($field);
+ $task = $this->loadModel('task')->getById($taskID);
+
+ $control = $this->loadController('task', 'batchCreate');
+ $control->batchCreate($task->execution, 0, 0, $taskID);
+
+ $data = $this->getData();
+ if(!$data) return $this->send400('error');
+ if(isset($data->status) and $data->status == 'fail') return $this->sendError(zget($data, 'code', 400), $data->message);
+
+ $task = $this->task->getById($data->idList[0]);
+ $this->send(200, $this->format($task, 'deadline:date,openedBy:user,openedDate:time,assignedTo:user,assignedDate:time,realStarted:time,finishedBy:user,finishedDate:time,closedBy:user,closedDate:time,canceledBy:user,canceledDate:time,lastEditedBy:user,lastEditedDate:time,deleted:bool,mailto:userList'));
+ }
+
+ public function setArrayPost($field)
+ {
+ $_POST[$field] = array('0' => $_POST[$field]);
+ }
+}
diff --git a/api/v1/entries/taskrecordestimate.php b/api/v1/entries/taskrecordestimate.php
index 178452030c..f3e1f9c518 100644
--- a/api/v1/entries/taskrecordestimate.php
+++ b/api/v1/entries/taskrecordestimate.php
@@ -11,6 +11,37 @@
*/
class taskRecordEstimateEntry extends Entry
{
+ /**
+ * GET method.
+ *
+ * @param int $taskID
+ * @access public
+ * @return void
+ */
+ public function get($taskID)
+ {
+ if($this->config->edition != 'open')
+ {
+ $control = $this->loadController('effort', 'createForObject');
+ $control->createForObject('task', $taskID);
+ }
+ else
+ {
+ $control = $this->loadController('task', 'recordEstimate');
+ $control->recordEstimate($taskID);
+ }
+
+ $data = $this->getData();
+ if(!$data) return $this->error('error');
+ if(isset($data->status) and $data->status == 'fail') return $this->sendError(zget($data, 'code', 400), $data->message);
+
+ $effort = array();
+ if($this->config->edition != 'open' and $data->data->efforts) $effort = $data->data->efforts;
+ if($this->config->edition == 'open' and $data->data->esitimates) $effort = $data->data->esitimates;
+ $this->send(200, array('effort' => $effort));
+
+ }
+
/**
* POST method.
*
diff --git a/api/v1/entries/testcases.php b/api/v1/entries/testcases.php
index fef888623b..7b680a8a8d 100644
--- a/api/v1/entries/testcases.php
+++ b/api/v1/entries/testcases.php
@@ -80,9 +80,11 @@ class testcasesEntry extends entry
$stepType = array();
foreach($this->requestBody->steps as $step)
{
+ $type = isset($step->type) ? $step->type : 'step';
+
$steps[] = $step->desc;
- $expects[] = $step->expect;
- $stepType[] = 'item';
+ $expects[] = $type == 'group' ? '' : $step->expect;
+ $stepType[] = $type;
}
$this->setPost('steps', $steps);
$this->setPost('expects', $expects);
diff --git a/config/routes.php b/config/routes.php
index a484abdcda..fcfe3dd41e 100644
--- a/config/routes.php
+++ b/config/routes.php
@@ -50,6 +50,8 @@ $routes['/stories/:id/active'] = 'storyActive';
$routes['/stories/:id/assign'] = 'storyAssignto';
$routes['/stories/:id/recordEstimate'] = 'storyRecordEstimate';
$routes['/stories/:id/child'] = 'storyChild';
+$routes['/stories/:id/recall'] = 'storyRecall';
+$routes['/stories/:id/review'] = 'storyReview';
$routes['/module/:id/stories'] = 'moduleStories';
@@ -80,6 +82,7 @@ $routes['/tasks/:id/pause'] = 'taskPause';
$routes['/tasks/:id/finish'] = 'taskFinish';
$routes['/tasks/:id/close'] = 'taskClose';
$routes['/tasks/:id/recordEstimate'] = 'taskRecordEstimate';
+$routes['/tasks/:id/getEstimate'] = 'taskRecordEstimate';
$routes['/users'] = 'users';
$routes['/users/:id'] = 'user';
diff --git a/db/update16.4.sql b/db/update16.4.sql
index 82969492c5..32f3287cb8 100644
--- a/db/update16.4.sql
+++ b/db/update16.4.sql
@@ -23,6 +23,7 @@ ALTER TABLE `zt_story` ADD `activatedDate` datetime NOT NULL AFTER `closedReason
ALTER TABLE `zt_task` MODIFY `activatedDate` datetime NOT NULL AFTER `lastEditedDate`;
ALTER TABLE `zt_kanbancard` ADD `progress` float unsigned NOT NULL DEFAULT '0' AFTER `estimate`;
+UPDATE `zt_kanbancard` SET `progress`='100' WHERE `status` = 'done' and `fromtype` = '';
ALTER TABLE `zt_user` ADD `visions` varchar(20) NOT NULL DEFAULT 'rnd,lite' AFTER `visits`;
UPDATE `zt_user` SET `visions`='rnd,lite';
diff --git a/extension/lite/execution/ext/view/kanban.lite.html.hook.php b/extension/lite/execution/ext/view/kanban.lite.html.hook.php
index 8d5ff1682c..0f3b49248b 100644
--- a/extension/lite/execution/ext/view/kanban.lite.html.hook.php
+++ b/extension/lite/execution/ext/view/kanban.lite.html.hook.php
@@ -7,4 +7,8 @@
diff --git a/extension/lite/project/ext/config/lite.php b/extension/lite/project/ext/config/lite.php
index c2c824cc1b..f68ccc4780 100644
--- a/extension/lite/project/ext/config/lite.php
+++ b/extension/lite/project/ext/config/lite.php
@@ -1,2 +1,4 @@
project->datatable->fieldList['actions']['width'] = '120';
+
+$config->project->list->exportFields = 'id,code,name,status,budget,PM,desc';
diff --git a/extension/lite/task/ext/view/create.html.php b/extension/lite/task/ext/view/create.html.php
index 57fc60a6f7..11d1d0dcfc 100644
--- a/extension/lite/task/ext/view/create.html.php
+++ b/extension/lite/task/ext/view/create.html.php
@@ -337,8 +337,8 @@
id);?>
';?>