[sync story #73251] Sync code to this branch from feature/deliverable.

This commit is contained in:
wangyidong
2025-05-12 10:42:45 +08:00
committed by 刘刚
parent aa4bd17b2b
commit 918abd43d4
14 changed files with 364 additions and 170 deletions
+131 -62
View File
@@ -70,6 +70,7 @@ class taskZen extends task
$this->view->showFields = $this->config->task->custom->createFields;
$this->view->gobackLink = (isset($output['from']) && $output['from'] == 'global') ? $this->createLink('execution', 'task', "executionID={$executionID}") : '';
$this->view->execution = $execution;
$this->view->project = $this->loadModel('project')->fetchById($execution->project);
$this->view->storyID = $storyID;
$this->view->blockID = helper::isAjaxRequest('modal') ? $this->loadModel('block')->getSpecifiedBlockID('my', 'assigntome', 'assigntome') : 0;
$this->view->hideStory = $this->task->isNoStoryExecution($execution);
@@ -195,6 +196,7 @@ class taskZen extends task
$this->view->title = $execution->name . $this->lang->hyphen . $this->lang->task->batchEdit;
$this->view->execution = $execution;
$this->view->project = $this->loadModel('project')->fetchById($execution->project);
$this->view->modules = $this->tree->getTaskOptionMenu($executionID, 0, !empty($this->config->task->allModule) ? 'allModule' : '');
}
else
@@ -247,6 +249,18 @@ class taskZen extends task
}
list($childTasks, $nonStoryChildTasks) = $this->task->getChildTasksByList(array_keys($tasks));
$childrenDateLimit = array();
foreach($childTasks as $parent => $children)
{
$childDateLimit = array('estStarted' => '', 'deadline' => '');
foreach($children as $child)
{
if(!helper::isZeroDate($child->estStarted) && (empty($childDateLimit['estStarted']) || $childDateLimit['estStarted'] > $child->estStarted)) $childDateLimit['estStarted'] = $child->estStarted;
if(!helper::isZeroDate($child->deadline) && (empty($childDateLimit['deadline']) || $childDateLimit['deadline'] < $child->deadline)) $childDateLimit['deadline'] = $child->deadline;
}
$childrenDateLimit[$parent] = $childDateLimit;
}
$storyPairs = $this->story->getExecutionStoryPairs($executionID, 0, 'all', '', 'full', 'active', 'story', false);;
$storyList = $this->story->getByList(array_keys($storyPairs));
$stories = array();
@@ -268,6 +282,7 @@ class taskZen extends task
$this->view->moduleGroup = $moduleGroup;
$this->view->childTasks = $childTasks;
$this->view->nonStoryChildTasks = $nonStoryChildTasks;
$this->view->childrenDateLimit = $childrenDateLimit;
$this->view->stories = $stories;
$this->view->parentTasks = $this->task->getByIdList($parentTaskIdList);
$this->view->noSprintPairs = $this->loadModel('project')->getProjectExecutionPairs();
@@ -301,7 +316,7 @@ class taskZen extends task
$executions = !empty($task->project) ? $this->execution->getByProject($task->project, 'all', 0, true) : array();
/* Get task members. */
$taskMembers = array();
$taskMembers = $this->view->members;
if(!empty($task->team))
{
foreach($task->members as $teamAccount)
@@ -310,10 +325,6 @@ class taskZen extends task
$taskMembers[$teamAccount] = $this->view->members[$teamAccount];
}
}
else
{
$taskMembers = $this->view->members;
}
/* Get execution stories. */
$moduleID = $task->module;
@@ -324,29 +335,34 @@ class taskZen extends task
}
$stories = $this->story->getExecutionStoryPairs($this->view->execution->id, 0, 'all', $moduleID, 'full', 'active', 'story', false);
$syncChildren = array();
$syncChildren = array();
$childDateLimit = array('estStarted' => '', 'deadline' => '');
if(!empty($task->children))
{
foreach($task->children as $child)
{
if(empty($child->story)) $syncChildren[] = $child->id;
if(!helper::isZeroDate($child->estStarted) && (empty($childDateLimit['estStarted']) || $childDateLimit['estStarted'] > $child->estStarted)) $childDateLimit['estStarted'] = $child->estStarted;
if(!helper::isZeroDate($child->deadline) && (empty($childDateLimit['deadline']) || $childDateLimit['deadline'] < $child->deadline)) $childDateLimit['deadline'] = $child->deadline;
}
}
if($this->view->execution->multiple) $manageLink = common::hasPriv('execution', 'manageMembers') ? $this->createLink('execution', 'manageMembers', "execution={$this->view->execution->id}") : '';
if(!$this->view->execution->multiple) $manageLink = common::hasPriv('project', 'manageMembers') ? $this->createLink('project', 'manageMembers', "projectID={$this->view->execution->project}") : '';
$this->view->title = $this->lang->task->edit . 'TASK' . $this->lang->hyphen . $this->view->task->name;
$this->view->stories = $this->story->addGradeLabel($stories);
$this->view->tasks = $tasks;
$this->view->taskMembers = $taskMembers;
$this->view->users = $this->loadModel('user')->getPairs('nodeleted|noclosed', "{$task->openedBy},{$task->canceledBy},{$task->closedBy}");
$this->view->showAllModule = isset($this->config->execution->task->allModule) ? $this->config->execution->task->allModule : '';
$this->view->modules = $this->tree->getTaskOptionMenu($task->execution, 0, $this->view->showAllModule ? 'allModule' : '');
$this->view->executions = $executions;
$this->view->syncChildren = $syncChildren;
$this->view->parentTask = !empty($task->parent) ? $this->task->getById($task->parent) : null;
$this->view->manageLink = $manageLink;
$this->view->title = $this->lang->task->edit . 'TASK' . $this->lang->hyphen . $this->view->task->name;
$this->view->stories = $this->story->addGradeLabel($stories);
$this->view->tasks = $tasks;
$this->view->taskMembers = $taskMembers;
$this->view->users = $this->loadModel('user')->getPairs('nodeleted|noclosed', "{$task->openedBy},{$task->canceledBy},{$task->closedBy}");
$this->view->showAllModule = isset($this->config->execution->task->allModule) ? $this->config->execution->task->allModule : '';
$this->view->modules = $this->tree->getTaskOptionMenu($task->execution, 0, $this->view->showAllModule ? 'allModule' : '');
$this->view->executions = $executions;
$this->view->syncChildren = $syncChildren;
$this->view->childDateLimit = $childDateLimit;
$this->view->parentTask = !empty($task->parent) ? $this->task->getById($task->parent) : null;
$this->view->manageLink = $manageLink;
$this->view->project = $task->project ? $this->loadModel('project')->fetchById($task->project) : null;
$this->display();
}
@@ -437,6 +453,7 @@ class taskZen extends task
$this->view->title = $this->lang->task->batchCreate;
$this->view->execution = $execution;
$this->view->project = $this->loadModel('project')->fetchById($execution->project);
$this->view->modules = $modules;
$this->view->parent = $taskID;
$this->view->storyID = $storyID;
@@ -558,12 +575,13 @@ class taskZen extends task
->stripTags($this->config->task->editor->edit['id'], $this->config->allowedTags)
->get();
$project = $this->loadModel('project')->fetchById($oldTask->project);
$parents = $this->getParentEstStartedAndDeadline(array($task->parent));
$this->checkLegallyDate($task, $project->taskDateLimit == 'limit', isset($parents[$task->parent]) ? $parents[$task->parent] : null);
$team = $this->post->team ? array_filter($this->post->team) : array();
if($task->mode && empty($team))
{
dao::$errors['assignedTo'] = $this->lang->task->teamNotEmpty;
return false;
}
if($task->mode && empty($team)) dao::$errors['assignedTo'] = $this->lang->task->teamNotEmpty;
if(dao::isError()) return false;
return $this->loadModel('file')->processImgURL($task, $this->config->task->editor->edit['id'], (string)$this->post->uid);
}
@@ -1006,6 +1024,31 @@ class taskZen extends task
$this->setMenu($this->view->execution->id);
}
/**
* 检查任务的开始时间和截止时间是否合法。
* Check if the start and end time of the task is legal.
*
* @param object $task
* @param bool $isDateLimit
* @param object $parent
* @param int|null $rowID
* @access public
* @return void
*/
public function checkLegallyDate(object $task, bool $isDateLimit, object|null $parent, int|null $rowID = null): void
{
$beginIndex = $rowID === null ? 'estStarted' : "estStarted[$rowID]";
$endIndex = $rowID === null ? 'deadline' : "deadline[$rowID]";
$beginIsZeroDate = helper::isZeroDate($task->estStarted);
$endIsZeroDate = helper::isZeroDate($task->deadline);
if(!$beginIsZeroDate and !$endIsZeroDate and $task->deadline < $task->estStarted) dao::$errors[$endIndex] = $this->lang->task->error->deadlineSmall;
if(!$isDateLimit || empty($parent)) return;
if(!$beginIsZeroDate && !helper::isZeroDate($parent->estStarted) && $task->estStarted < $parent->estStarted) dao::$errors[$beginIndex] = sprintf($this->lang->task->overParentEsStarted, $parent->estStarted);
if(!$endIsZeroDate && !helper::isZeroDate($parent->deadline) && $task->deadline > $parent->deadline) dao::$errors[$endIndex] = sprintf($this->lang->task->overParentDeadline, $parent->deadline);
}
/**
* 检查传入的创建数据是否符合要求。
* Check if the input post meets the requirements.
@@ -1018,17 +1061,9 @@ class taskZen extends task
protected function checkCreateTask(object $task, array $team): bool
{
/* Check if the estimate is positive. */
if($task->estimate < 0)
{
dao::$errors['estimate'] = $this->lang->task->error->recordMinus;
return false;
}
if($this->post->multiple && empty($team))
{
dao::$errors['assignedTo'] = $this->lang->task->teamNotEmpty;
return false;
}
if($task->estimate < 0) dao::$errors['estimate'] = $this->lang->task->error->recordMinus;
if($this->post->multiple && empty($team)) dao::$errors['assignedTo'] = $this->lang->task->teamNotEmpty;
if(dao::isError()) return false;
/* If the task start and end date must be between the execution start and end date, check if the task start and end date accord with the conditions. */
if(!empty($this->config->limitTaskDate))
@@ -1037,12 +1072,9 @@ class taskZen extends task
if(dao::isError()) return false;
}
/* Check start and end date. */
if(!helper::isZeroDate($task->deadline) && $task->estStarted > $task->deadline)
{
dao::$errors['deadline'] = $this->lang->task->error->deadlineSmall;
return false;
}
$project = $this->dao->findById($task->project)->from(TABLE_PROJECT)->fetch();
$parents = $this->getParentEstStartedAndDeadline(array($task->parent));
$this->checkLegallyDate($task, $project->taskDateLimit == 'limit', isset($parents[$task->parent]) ? $parents[$task->parent] : null);
return !dao::isError();
}
@@ -1060,26 +1092,26 @@ class taskZen extends task
{
/* Set required fields. */
$requiredFields = $this->config->task->create->requiredFields;
$execution = $this->loadModel('execution')->getById($executionID);
$execution = $this->loadModel('execution')->fetchById($executionID);
if($this->task->isNoStoryExecution($execution)) $requiredFields = str_replace(',story,', ',', ',' . $requiredFields . ',');
$requiredFields = array_filter(explode(',', $requiredFields));
$levels = array();
$project = $this->loadModel('project')->fetchById($execution->project);
$parentIdList = array_filter(array_column($tasks, 'parent', 'parent'));
$parents = $this->getParentEstStartedAndDeadline($parentIdList);
foreach($tasks as $rowIndex => $task)
{
if(mb_strlen($task->name) > 255)
{
dao::$errors["name[$rowIndex]"] = sprintf($this->lang->task->error->length, 255);
}
$levels[$task->level] = $rowIndex;
if(mb_strlen($task->name) > 255) dao::$errors["name[$rowIndex]"] = sprintf($this->lang->task->error->length, 255);
if(!empty($this->post->estimate[$rowIndex]) and !preg_match("/^[0-9]+(.[0-9]+)?$/", (string)$this->post->estimate[$rowIndex]))
{
dao::$errors["estimate[$rowIndex]"] = $this->lang->task->error->estimateNumber;
}
/* If the task start and end date must be between the execution start and end date, check if the task start and end date accord with the conditions. */
if(!empty($this->config->limitTaskDate))
{
$this->task->checkEstStartedAndDeadline($executionID, (string)$task->estStarted, (string)$task->deadline);
}
if(!empty($this->config->limitTaskDate)) $this->task->checkEstStartedAndDeadline($executionID, (string)$task->estStarted, (string)$task->deadline);
/* Check start and end date. */
if(!helper::isZeroDate($task->deadline) && $task->deadline < $task->estStarted)
@@ -1087,24 +1119,21 @@ class taskZen extends task
dao::$errors["deadline[$rowIndex]"] = $this->lang->task->error->deadlineSmall;
}
$parentTask = isset($parents[$task->parent]) ? $parents[$task->parent] : null;
if($task->level > 0 && isset($levels[$task->level - 1])) $parentTask = zget($tasks, $levels[$task->level - 1], null);
$this->checkLegallyDate($task, $project->taskDateLimit == 'limit', $parentTask, $rowIndex);
/* Check if the estimate is positive. */
if($task->estimate < 0)
{
dao::$errors["estimate[$rowIndex]"] = $this->lang->task->error->recordMinus;
}
if($task->estimate < 0) dao::$errors["estimate[$rowIndex]"] = $this->lang->task->error->recordMinus;
/* Check if the required fields are empty. */
foreach($requiredFields as $field)
{
if(empty($task->$field))
{
dao::$errors[$field . "[$rowIndex]"] = sprintf($this->lang->error->notempty, $this->lang->task->$field);
}
if(empty($task->$field)) dao::$errors[$field . "[$rowIndex]"] = sprintf($this->lang->error->notempty, $this->lang->task->$field);
}
}
if(dao::isError()) return false;
return true;
return !dao::isError();
}
/**
@@ -1118,6 +1147,10 @@ class taskZen extends task
*/
protected function checkBatchEditTask(array $tasks, array $oldTasks): bool
{
$oldTask = reset($oldTasks);
$project = $this->loadModel('project')->fetchById($oldTask->project);
$parentIdList = array_filter(array_column($tasks, 'parent', 'parent'));
$parents = $this->getParentEstStartedAndDeadline($parentIdList);
foreach($tasks as $taskID => $task)
{
$oldTask = $oldTasks[$taskID];
@@ -1131,11 +1164,12 @@ class taskZen extends task
if($task->consumed < 0 ) dao::$errors["consumed[{$taskID}]"] = (array)sprintf($this->lang->task->error->recordMinus, $this->lang->task->consumedThisTime);
if($task->left < 0) dao::$errors["left[$taskID]"] = (array)sprintf($this->lang->task->error->recordMinus, $this->lang->task->leftAB);
if(!empty($this->config->limitTaskDate)) $this->task->checkEstStartedAndDeadline($oldTask->execution, (string)$task->estStarted, (string)$task->deadline, "task:{$taskID} ");
if(!empty($this->config->limitTaskDate)) $this->task->checkEstStartedAndDeadline($oldTask->execution, (string)$task->estStarted, (string)$task->deadline, $taskID);
if($task->status == 'cancel') continue;
if($task->status == 'done' && !$task->consumed) dao::$errors["consumed[{$taskID}]"] = (array)sprintf($this->lang->error->notempty, $this->lang->task->consumedThisTime);
if(!empty($task->deadline) && $task->estStarted > $task->deadline) dao::$errors["deadline[{$taskID}]"] = (array)$this->lang->task->error->deadlineSmall;
$this->checkLegallyDate($task, $project->taskDateLimit == 'limit', isset($parents[$task->parent]) ? $parents[$task->parent] : null, $taskID);
}
return !dao::isError();
}
@@ -1485,11 +1519,11 @@ class taskZen extends task
unset($task->team);
}
if($task->isParent)
if($task->isParent && strpos($task->name, "[{$this->lang->task->parentAB}]") === false)
{
$task->name = '[' . $this->lang->task->parentAB . '] ' . $task->name;
}
elseif($task->parent > 0)
elseif($task->parent > 0 && strpos($task->name, "[{$this->lang->task->childrenAB}]") === false)
{
$task->name = '[' . $this->lang->task->childrenAB . '] ' . $task->name;
}
@@ -2047,4 +2081,39 @@ class taskZen extends task
return $options;
}
/**
* 获取父任务的开始时间和截止时间。
* Get the start and end time of the parent task.
*
* @param array $parentIdList
* @access protected
* @return array
*/
protected function getParentEstStartedAndDeadline(array $parentIdList): array
{
$pathPairs = $this->dao->select('id,path')->from(TABLE_TASK)->where('id')->in($parentIdList)->fetchPairs();
if(empty($pathPairs)) return array();
$allParentIdList = array_filter(array_unique(explode(',', implode(',', $pathPairs))));
$allParents = $this->dao->select('id,estStarted,deadline')->from(TABLE_TASK)->where('id')->in($allParentIdList)->fetchAll('id');
$parents = array();
foreach($pathPairs as $parentID => $path)
{
$parent = new stdClass();
$parent->estStarted = null;
$parent->deadline = null;
foreach(array_reverse(array_filter(explode(',', $path))) as $taskID)
{
if(!isset($allParents[$taskID])) continue;
$task = $allParents[$taskID];
if(empty($parent->estStarted) && !helper::isZeroDate($task->estStarted)) $parent->estStarted = $task->estStarted;
if(empty($parent->deadline) && !helper::isZeroDate($task->deadline)) $parent->deadline = $task->deadline;
if(!empty($parent->estStarted) && !empty($parent->deadline)) break;
}
$parents[$parentID] = $parent;
}
return $parents;
}
}