This commit is contained in:
lanzongjun
2022-11-02 16:02:42 +08:00
parent f980579b0a
commit e523f6390f
2 changed files with 226 additions and 262 deletions
+69 -122
View File
@@ -122,56 +122,89 @@ class story extends control
setcookie('lastStoryModule', (int)$this->post->module, $this->config->cookieLife, $this->config->webRoot, '', $this->config->cookieSecure, false);
if($this->post->branches and $this->post->modules)
$storyResult = $this->story->create($objectID, $bugID, $from = isset($fromObjectIDKey) ? $fromObjectIDKey : '', $extra);
if(!$storyResult or dao::isError())
{
foreach($this->post->branches as $key => $postBranch)
$response['result'] = 'fail';
$response['message'] = dao::getError();
return $this->send($response);
}
$storyID = $storyResult['id'];
$productID = $this->post->product ? $this->post->product : $productID;
if($storyResult['status'] == 'exists')
{
$response['message'] = sprintf($this->lang->duplicate, $this->lang->story->common);
if($objectID == 0)
{
if(empty($this->post->modules[$key]))
{
$response['result'] = 'fail';
$response['message'] = array(sprintf($this->lang->error->notempty, $this->lang->story->branch));
return $this->send($response);
}
$response['locate'] = $this->createLink('story', 'view', "storyID={$storyID}&version=0&param=0&storyType=$storyType");
}
$storyIds = array();
$checkDuplicate = 'yes';
foreach($this->post->branches as $key => $postBranchID)
else
{
$postModuleID = $this->post->modules[$key];
$postPlanID = $this->post->plans[$key];
$_POST['branch'] = $postBranchID;
$_POST['module'] = $postModuleID;
$_POST['plan'] = $postPlanID;
$extra .= ",checkDuplicate=$checkDuplicate,";
$storyID = $this->postStory($objectID, $bugID, $productID, $todoID, $storyType, $fromObjectIDKey = isset($fromObjectIDKey) ? $fromObjectIDKey : '', $fromObjectAction = isset($fromObjectAction) ? $fromObjectAction: '', $extra);
$checkDuplicate = 'no';
$storyIds[] = $storyID;
$execution = $this->dao->findById((int)$objectID)->from(TABLE_EXECUTION)->fetch();
$moduleName = $execution->type == 'project' ? 'projectstory' : 'execution';
$param = $execution->type == 'project' ? "projectID=$objectID&productID=$productID" : "executionID=$objectID";
$response['locate'] = $this->createLink($moduleName, 'story', $param);
}
return $this->send($response);
}
/* bind siblings story id */
if(count($storyIds) > 1)
/* bind siblings story id */
$storyIDs = $storyResult['ids'];
if(count($storyIDs) > 1)
{
foreach($storyIDs as $siblingsStoryID)
{
foreach($storyIds as $siblingsStoryID)
$siblingsArr = array();
foreach($storyIDs as $idItem)
{
$siblingsArr = array();
foreach($storyIds as $id)
{
if($id != $siblingsStoryID) $siblingsArr[] = $id;
}
if($idItem != $siblingsStoryID) $siblingsArr[] = $idItem;
$siblings = ',' . implode(',', $siblingsArr) . ',';
$this->dao->update(TABLE_STORY)->set('siblings')->eq($siblings)->where('id')->eq($siblingsStoryID)->exec();
}
}
}
else
$action = $bugID == 0 ? 'Opened' : 'Frombug';
$extra = $bugID == 0 ? '' : $bugID;
/* Record related action, for example FromFeedback. */
if(isset($fromObjectID))
{
$this->postStory($objectID, $bugID, $productID, $todoID, $storyType, $fromObjectIDKey = isset($fromObjectIDKey) ? $fromObjectIDKey : '', $fromObjectAction = isset($fromObjectAction) ? $fromObjectAction: '', $extra);
$action = $fromObjectAction;
$extra = $fromObjectID;
}
/* Create actions. */
foreach($storyIDs as $idItem) $actionID = $this->action->create('task', $idItem, $action, '', $extra);
if($objectID != 0)
{
$object = $this->dao->findById((int)$objectID)->from(TABLE_PROJECT)->fetch();
if($object->type != 'project')
{
if($this->config->systemMode == 'new') $this->action->create('story', $storyID, 'linked2project', '', $object->project);
$actionType = $object->type == 'kanban' ? 'linked2kanban' : 'linked2execution';
$this->action->create('story', $storyID, $actionType, '', $objectID);
}
else
{
$this->action->create('story', $storyID, 'linked2project', '', $objectID);
}
}
if($todoID > 0)
{
$this->dao->update(TABLE_TODO)->set('status')->eq('done')->where('id')->eq($todoID)->exec();
$this->action->create('todo', $todoID, 'finished', '', "STORY:$storyID");
if($this->config->edition == 'biz' || $this->config->edition == 'max')
{
$todo = $this->dao->select('type, idvalue')->from(TABLE_TODO)->where('id')->eq($todoID)->fetch();
if($todo->type == 'feedback' && $todo->idvalue) $this->loadModel('feedback')->updateStatus('todo', $todo->idvalue, 'done');
}
}
$message = $this->executeHooks($storyID);
@@ -419,92 +452,6 @@ class story extends control
$this->display();
}
/**
* post a story.
*
* @param int $objectID
* @param int $bugID
* @param int $productID
* @param int $todoID
* @param string $storyType requirement|story
* @param string $fromObjectIDKey
* @param string $fromObjectAction
* @param string $extra for example feedbackID=0
* @param string $storyType requirement|story
* @access public
* @return void
*/
private function postStory($objectID, $bugID, $productID, $todoID, $storyType, $fromObjectIDKey, $fromObjectAction, $extra)
{
$storyResult = $this->story->create($objectID, $bugID, $from = isset($fromObjectIDKey) ? $fromObjectIDKey : '', $extra);
if(!$storyResult or dao::isError())
{
$response['result'] = 'fail';
$response['message'] = dao::getError();
return $this->send($response);
}
$storyID = $storyResult['id'];
$productID = $this->post->product ? $this->post->product : $productID;
if($storyResult['status'] == 'exists')
{
$response['message'] = sprintf($this->lang->duplicate, $this->lang->story->common);
if($objectID == 0)
{
$response['locate'] = $this->createLink('story', 'view', "storyID={$storyID}&version=0&param=0&storyType=$storyType");
}
else
{
$execution = $this->dao->findById((int)$objectID)->from(TABLE_EXECUTION)->fetch();
$moduleName = $execution->type == 'project' ? 'projectstory' : 'execution';
$param = $execution->type == 'project' ? "projectID=$objectID&productID=$productID" : "executionID=$objectID";
$response['locate'] = $this->createLink($moduleName, 'story', $param);
}
return $this->send($response);
}
$action = $bugID == 0 ? 'Opened' : 'Frombug';
$extra = $bugID == 0 ? '' : $bugID;
/* Record related action, for example FromFeedback. */
if(isset($fromObjectID))
{
$action = $fromObjectAction;
$extra = $fromObjectID;
}
$actionID = $this->action->create('story', $storyID, $action, '', $extra);
if($objectID != 0)
{
$object = $this->dao->findById((int)$objectID)->from(TABLE_PROJECT)->fetch();
if($object->type != 'project')
{
if($this->config->systemMode == 'new') $this->action->create('story', $storyID, 'linked2project', '', $object->project);
$actionType = $object->type == 'kanban' ? 'linked2kanban' : 'linked2execution';
$this->action->create('story', $storyID, $actionType, '', $objectID);
}
else
{
$this->action->create('story', $storyID, 'linked2project', '', $objectID);
}
}
if($todoID > 0)
{
$this->dao->update(TABLE_TODO)->set('status')->eq('done')->where('id')->eq($todoID)->exec();
$this->action->create('todo', $todoID, 'finished', '', "STORY:$storyID");
if($this->config->edition == 'biz' || $this->config->edition == 'max')
{
$todo = $this->dao->select('type, idvalue')->from(TABLE_TODO)->where('id')->eq($todoID)->fetch();
if($todo->type == 'feedback' && $todo->idvalue) $this->loadModel('feedback')->updateStatus('todo', $todo->idvalue, 'done');
}
}
return $storyID;
}
/**
* Create a batch stories.
*
+157 -140
View File
@@ -229,173 +229,190 @@ class storyModel extends model
->remove('files,labels,reviewer,needNotReview,newStory,uid,contactListMenu,URS,region,lane,ticket,branches,modules,plans')
->get();
if(!empty($output['checkDuplicate']) and $output['checkDuplicate'] == 'yes')
{
/* Check repeat story. */
$result = $this->loadModel('common')->removeDuplicate('story', $story, "product={$story->product}");
if(isset($result['stop']) and $result['stop']) return array('status' => 'exists', 'id' => $result['duplicate']);
}
/* Check repeat story. */
$result = $this->loadModel('common')->removeDuplicate('story', $story, "product={$story->product}");
if(isset($result['stop']) and $result['stop']) return array('status' => 'exists', 'id' => $result['duplicate']);
if($story->status != 'draft' and $this->checkForceReview()) $story->status = 'reviewing';
if(strpos('draft,reviewing', $story->status) !== false) $story->stage = $this->post->plan > 0 ? 'planned' : 'wait';
$story = $this->loadModel('file')->processImgURL($story, $this->config->story->editor->create['id'], $this->post->uid);
$requiredFields = "," . $this->config->story->create->requiredFields . ",";
if($story->type == 'requirement') $requiredFields = str_replace(',plan,', ',', $requiredFields);
if(strpos($requiredFields, ',estimate,') !== false)
$product = $this->loadModel('product')->getById($story->product);
if($product->type == 'normal' or $story->type == 'requirement')
{
if(strlen(trim($story->estimate)) == 0) dao::$errors['estimate'] = sprintf($this->lang->error->notempty, $this->lang->story->estimate);
$requiredFields = str_replace(',estimate,', ',', $requiredFields);
$this->post->branches = array(isset($story->branch) ? $story->branch : 0);
$this->post->modules = array(isset($story->module) ? $story->module : 0);
$this->post->plans = array(isset($story->plan) ? $story->plan : 0);
}
$requiredFields = trim($requiredFields, ',');
$this->dao->insert(TABLE_STORY)->data($story, 'spec,verify')
->autoCheck()
->checkIF($story->notifyEmail, 'notifyEmail', 'email')
->batchCheck($requiredFields, 'notempty')
->checkFlow()
->exec();
if(!dao::isError())
$storyIds = array();
$mainStoryID = 0;
foreach($this->post->branches as $key => $postBranch)
{
$storyID = $this->dao->lastInsertID();
$story->branch = $postBranch;
$story->module = $this->post->modules[$key];
$story->plan = $this->post->plans[$key];
/* Fix bug #21992, user story have no parent story. */
if(isset($story->parent) and $story->parent)
if(strpos('draft,reviewing', $story->status) !== false) $story->stage = $this->post->plan > 0 ? 'planned' : 'wait';
$requiredFields = "," . $this->config->story->create->requiredFields . ",";
if($story->type == 'requirement') $requiredFields = str_replace(',plan,', ',', $requiredFields);
if(strpos($requiredFields, ',estimate,') !== false)
{
$stories = array($storyID);
$this->subdivide($story->parent, $stories);
if(strlen(trim($story->estimate)) == 0) dao::$errors['estimate'] = sprintf($this->lang->error->notempty, $this->lang->story->estimate);
$requiredFields = str_replace(',estimate,', ',', $requiredFields);
}
if(!empty($story->plan))
$requiredFields = trim($requiredFields, ',');
$this->dao->insert(TABLE_STORY)->data($story, 'spec,verify')
->autoCheck()
->checkIF($story->notifyEmail, 'notifyEmail', 'email')
->batchCheck($requiredFields, 'notempty')
->checkFlow()
->exec();
if(dao::isError()) return false;
if(!dao::isError())
{
$this->updateStoryOrderOfPlan($storyID, $story->plan); // Set story order in this plan.
$this->loadModel('action')->create('productplan', $story->plan, 'linkstory', '', $storyID);
}
$storyID = $this->dao->lastInsertID();
$this->file->updateObjectID($this->post->uid, $storyID, $story->type);
$files = $this->file->saveUpload($story->type, $storyID, 1);
$data = new stdclass();
$data->story = $storyID;
$data->version = 1;
$data->title = $story->title;
$data->spec = $story->spec;
$data->verify = $story->verify;
$data->files = join(',', array_keys($files));
$this->dao->insert(TABLE_STORYSPEC)->data($data)->exec();
/* Save the story reviewer to storyreview table. */
if(isset($_POST['reviewer']))
{
foreach($this->post->reviewer as $reviewer)
/* Fix bug #21992, user story have no parent story. */
if(isset($story->parent) and $story->parent)
{
if(empty($reviewer)) continue;
$reviewData = new stdclass();
$reviewData->story = $storyID;
$reviewData->version = 1;
$reviewData->reviewer = $reviewer;
$this->dao->insert(TABLE_STORYREVIEW)->data($reviewData)->exec();
$stories = array($storyID);
$this->subdivide($story->parent, $stories);
}
}
/* Project or execution linked story. */
if($executionID != 0)
{
$this->linkStory($executionID, $this->post->product, $storyID);
if($this->config->systemMode == 'new' and $executionID != $this->session->project) $this->linkStory($this->session->project, $this->post->product, $storyID);
$this->loadModel('kanban');
$laneID = isset($output['laneID']) ? $output['laneID'] : 0;
if(isset($_POST['lane'])) $laneID = $_POST['lane'];
$columnID = $this->kanban->getColumnIDByLaneID($laneID, 'backlog');
if(empty($columnID)) $columnID = isset($output['columnID']) ? $output['columnID'] : 0;
if(!empty($laneID) and !empty($columnID)) $this->kanban->addKanbanCell($executionID, $laneID, $columnID, 'story', $storyID);
if(empty($laneID) or empty($columnID)) $this->kanban->updateLane($executionID, 'story');
}
if(is_array($this->post->URS))
{
foreach($this->post->URS as $URID)
if(!empty($story->plan))
{
$requirement = $this->getByID($URID);
$data = new stdclass();
$data->product = $story->product;
$data->AType = 'requirement';
$data->relation = 'subdivideinto';
$data->BType = 'story';
$data->AID = $URID;
$data->BID = $storyID;
$data->AVersion = $requirement->version;
$data->BVersion = 1;
$data->extra = 1;
$this->dao->insert(TABLE_RELATION)->data($data)->autoCheck()->exec();
$data->AType = 'story';
$data->relation = 'subdividedfrom';
$data->BType = 'requirement';
$data->AID = $storyID;
$data->BID = $URID;
$data->AVersion = 1;
$data->BVersion = $requirement->version;
$this->dao->insert(TABLE_RELATION)->data($data)->autoCheck()->exec();
$this->updateStoryOrderOfPlan($storyID, $story->plan); // Set story order in this plan.
$this->loadModel('action')->create('productplan', $story->plan, 'linkstory', '', $storyID);
}
}
if($bugID > 0)
{
if(($this->config->edition == 'biz' || $this->config->edition == 'max')) $oldBug = $this->dao->select('feedback, status')->from(TABLE_BUG)->where('id')->eq($bugID)->fetch();
$this->file->updateObjectID($this->post->uid, $storyID, $story->type);
$files = $this->file->saveUpload($story->type, $storyID, 1);
$bug = new stdclass();
$bug->toStory = $storyID;
$bug->status = 'closed';
$bug->resolution = 'tostory';
$bug->resolvedBy = $this->app->user->account;
$bug->resolvedDate = $now;
$bug->closedBy = $this->app->user->account;
$bug->closedDate = $now;
$bug->assignedTo = 'closed';
$bug->assignedDate = $now;
$this->dao->update(TABLE_BUG)->data($bug)->where('id')->eq($bugID)->exec();
$data = new stdclass();
$data->story = $storyID;
$data->version = 1;
$data->title = $story->title;
$data->spec = $story->spec;
$data->verify = $story->verify;
$data->files = join(',', array_keys($files));
$this->dao->insert(TABLE_STORYSPEC)->data($data)->exec();
$this->loadModel('action')->create('bug', $bugID, 'ToStory', '', $storyID);
$this->action->create('bug', $bugID, 'Closed');
if(($this->config->edition == 'biz' || $this->config->edition == 'max') && !dao::isError() && $oldBug->feedback) $this->loadModel('feedback')->updateStatus('bug', $oldBug->feedback, 'closed', $oldBug->status);
/* add files to story from bug. */
$files = $this->dao->select('*')->from(TABLE_FILE)
->where('objectType')->eq('bug')
->andWhere('objectID')->eq($bugID)
->fetchAll();
if(!empty($files))
/* Save the story reviewer to storyreview table. */
if(isset($_POST['reviewer']))
{
foreach($files as $file)
foreach($this->post->reviewer as $reviewer)
{
$file->objectType = 'story';
$file->objectID = $storyID;
unset($file->id);
$this->dao->insert(TABLE_FILE)->data($file)->exec();
if(empty($reviewer)) continue;
$reviewData = new stdclass();
$reviewData->story = $storyID;
$reviewData->version = 1;
$reviewData->reviewer = $reviewer;
$this->dao->insert(TABLE_STORYREVIEW)->data($reviewData)->exec();
}
}
/* Project or execution linked story. */
if($executionID != 0)
{
$this->linkStory($executionID, $this->post->product, $storyID);
if($this->config->systemMode == 'new' and $executionID != $this->session->project) $this->linkStory($this->session->project, $this->post->product, $storyID);
$this->loadModel('kanban');
$laneID = isset($output['laneID']) ? $output['laneID'] : 0;
if(isset($_POST['lane'])) $laneID = $_POST['lane'];
$columnID = $this->kanban->getColumnIDByLaneID($laneID, 'backlog');
if(empty($columnID)) $columnID = isset($output['columnID']) ? $output['columnID'] : 0;
if(!empty($laneID) and !empty($columnID)) $this->kanban->addKanbanCell($executionID, $laneID, $columnID, 'story', $storyID);
if(empty($laneID) or empty($columnID)) $this->kanban->updateLane($executionID, 'story');
}
if(is_array($this->post->URS))
{
foreach($this->post->URS as $URID)
{
$requirement = $this->getByID($URID);
$data = new stdclass();
$data->product = $story->product;
$data->AType = 'requirement';
$data->relation = 'subdivideinto';
$data->BType = 'story';
$data->AID = $URID;
$data->BID = $storyID;
$data->AVersion = $requirement->version;
$data->BVersion = 1;
$data->extra = 1;
$this->dao->insert(TABLE_RELATION)->data($data)->autoCheck()->exec();
$data->AType = 'story';
$data->relation = 'subdividedfrom';
$data->BType = 'requirement';
$data->AID = $storyID;
$data->BID = $URID;
$data->AVersion = 1;
$data->BVersion = $requirement->version;
$this->dao->insert(TABLE_RELATION)->data($data)->autoCheck()->exec();
}
}
if($bugID > 0)
{
if(($this->config->edition == 'biz' || $this->config->edition == 'max')) $oldBug = $this->dao->select('feedback, status')->from(TABLE_BUG)->where('id')->eq($bugID)->fetch();
$bug = new stdclass();
$bug->toStory = $storyID;
$bug->status = 'closed';
$bug->resolution = 'tostory';
$bug->resolvedBy = $this->app->user->account;
$bug->resolvedDate = $now;
$bug->closedBy = $this->app->user->account;
$bug->closedDate = $now;
$bug->assignedTo = 'closed';
$bug->assignedDate = $now;
$this->dao->update(TABLE_BUG)->data($bug)->where('id')->eq($bugID)->exec();
$this->loadModel('action')->create('bug', $bugID, 'ToStory', '', $storyID);
$this->action->create('bug', $bugID, 'Closed');
if(($this->config->edition == 'biz' || $this->config->edition == 'max') && !dao::isError() && $oldBug->feedback) $this->loadModel('feedback')->updateStatus('bug', $oldBug->feedback, 'closed', $oldBug->status);
/* add files to story from bug. */
$files = $this->dao->select('*')->from(TABLE_FILE)
->where('objectType')->eq('bug')
->andWhere('objectID')->eq($bugID)
->fetchAll();
if(!empty($files))
{
foreach($files as $file)
{
$file->objectType = 'story';
$file->objectID = $storyID;
unset($file->id);
$this->dao->insert(TABLE_FILE)->data($file)->exec();
}
}
}
if(!defined('TUTORIAL')) $this->setStage($storyID);
if(!dao::isError()) $this->loadModel('score')->create('story', 'create',$storyID);
/* Callback the callable method to process the related data for object that is transfered to story. */
if($from && is_callable(array($this, $this->config->story->fromObjects[$from]['callback']))) call_user_func(array($this, $this->config->story->fromObjects[$from]['callback']), $storyID);
$storyIds[] = $storyID;
if(empty($mainStoryID)) $mainStoryID = $storyID;
}
if(!defined('TUTORIAL')) $this->setStage($storyID);
if(!dao::isError()) $this->loadModel('score')->create('story', 'create',$storyID);
/* Callback the callable method to process the related data for object that is transfered to story. */
if($from && is_callable(array($this, $this->config->story->fromObjects[$from]['callback']))) call_user_func(array($this, $this->config->story->fromObjects[$from]['callback']), $storyID);
return array('status' => 'created', 'id' => $storyID);
}
return false;
return array('status' => 'created', 'id' => $mainStoryID, 'ids' => $storyIds);
}
/**