diff --git a/module/task/config/form.php b/module/task/config/form.php index 1dfb929eee..a90903c359 100644 --- a/module/task/config/form.php +++ b/module/task/config/form.php @@ -16,7 +16,7 @@ $config->task->form->create['color'] = array('type' => 'string', 'require $config->task->form->create['name'] = array('type' => 'string', 'required' => true, 'default' => ''); $config->task->form->create['pri'] = array('type' => 'int', 'required' => false, 'default' => $config->task->default->pri); $config->task->form->create['estimate'] = array('type' => 'float', 'required' => false, 'default' => 0); -$config->task->form->create['desc'] = array('type' => 'string', 'required' => false); +$config->task->form->create['desc'] = array('type' => 'string', 'required' => false, 'control' => 'editor'); $config->task->form->create['estStarted'] = array('type' => 'string', 'required' => false, 'default' => ''); $config->task->form->create['deadline'] = array('type' => 'string', 'required' => false, 'default' => ''); $config->task->form->create['vision'] = array('type' => 'string', 'required' => false, 'default' => $config->vision); @@ -137,6 +137,7 @@ $config->task->form->start['status'] = array('type' => 'string', 'requir $config->task->form->start['consumed'] = array('type' => 'float', 'required' => false, 'default' => 0); $config->task->form->start['left'] = array('type' => 'float', 'required' => false, 'default' => 0); $config->task->form->start['assignedTo'] = array('type' => 'string', 'required' => false, 'default' => ''); +$config->task->form->start['comment'] = array('type' => 'string', 'required' => false, 'control' => 'editor'); $config->task->form->start['realStarted'] = array('type' => 'string', 'required' => false, 'default' => ''); $config->task->form->start['lastEditedBy'] = array('type' => 'string', 'required' => false, 'default' => $app->user->account); $config->task->form->start['lastEditedDate'] = array('type' => 'string', 'required' => false, 'default' => $now); diff --git a/module/task/control.php b/module/task/control.php index 44c0a61e19..7e45c22809 100755 --- a/module/task/control.php +++ b/module/task/control.php @@ -35,16 +35,16 @@ class task extends control * @param int $moduleID * @param int $taskID * @param int $todoID - * @param string $extra + * @param string $cardPosition * @param int $bugID * @access public * @return void */ - public function create(int $executionID = 0, int $storyID = 0, int $moduleID = 0, int $taskID = 0, int $todoID = 0, string $extra = '', int $bugID = 0) + public function create(int $executionID = 0, int $storyID = 0, int $moduleID = 0, int $taskID = 0, int $todoID = 0, string $cardPosition = '', int $bugID = 0) { /* Analytic parameter. */ - $extra = str_replace(array(',', ' '), array('&', ''), $extra); - parse_str($extra, $output); + $cardPosition = str_replace(array(',', ' '), array('&', ''), $cardPosition); + parse_str($cardPosition, $output); /* If you do not have permission to access any execution, go to the create execution page. */ if(!$this->execution->checkPriv($executionID)) $this->locate($this->createLink('execution', 'create')); @@ -123,15 +123,15 @@ class task extends control * @param int $moduleID * @param int $taskID * @param string $iframe - * @param string $extra + * @param string $cardPosition * @access public * @return void */ - public function batchCreate(int $executionID, int $storyID = 0, int $moduleID = 0, int $taskID = 0, string $extra = '') + public function batchCreate(int $executionID, int $storyID = 0, int $moduleID = 0, int $taskID = 0, string $cardPosition = '') { /* Init vars. */ - $extra = str_replace(array(',', ' '), array('&', ''), $extra); - parse_str($extra, $output); + $cardPosition = str_replace(array(',', ' '), array('&', ''), $cardPosition); + parse_str($cardPosition, $output); /* 判断不能访问的执行。 Judge execution without access. */ if($this->taskZen->isLimitedInExecution($executionID)) @@ -455,21 +455,21 @@ class task extends control if(!empty($_POST)) { /* Prepare the data information before start the task. */ - $newTask = $this->taskZen->prepareStart($task); + $taskData = $this->taskZen->buildTaskForStart($task); + if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); + + /* Record task effort. */ + $effort = $this->buildEffortForStart($task, $taskData); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); /* Start a task. */ - $changes = $this->task->start($task, $newTask, $output); + $changes = $this->task->start($task, $taskData, $output); /* If there is an error, return an error message. */ - if(dao::isError()) - { - if($this->viewType == 'json' or (defined('RUN_MODE') && RUN_MODE == 'api')) return $this->send(array('result' => 'fail', 'message' => dao::getError())); - return array('result' => 'fail', 'message' => dao::getError()); - } + if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); /* Update other data related to the task after it is started. */ - $result = $this->task->afterStart($task, $newTask, $changes, $this->post->left, $this->post->comment, $output); + $result = $this->task->afterStart($task, $taskData, $changes, $this->post->left, $this->post->comment, $output); if(is_array($result)) $this->send($result); /* Get the information returned after a task is started. */ @@ -480,12 +480,13 @@ class task extends control /* Shows the variables needed to start the task page. */ $assignedTo = empty($task->assignedTo) ? $this->app->user->account : $task->assignedTo; - if(!empty($task->team)) $assignedTo = $this->task->getAssignedTo4Multi($task->team, $task); - $this->view->title = $this->view->execution->name . $this->lang->colon .$this->lang->task->start; - $this->view->users = $this->loadModel('user')->getPairs('noletter'); - $this->view->members = $this->user->getTeamMemberPairs($task->execution, 'execution', 'nodeleted'); - $this->view->assignedTo = $assignedTo; + $this->view->title = $this->view->execution->name . $this->lang->colon .$this->lang->task->start; + $this->view->users = $this->loadModel('user')->getPairs('noletter'); + $this->view->members = $this->user->getTeamMemberPairs($task->execution, 'execution', 'nodeleted'); + $this->view->assignedTo = !empty($task->team) ? $this->task->getAssignedTo4Multi($task->team, $task) : $assignedTo; + $this->view->canRecordEffort = $this->taskZen->checkRecordEffort($task); + $this->view->currentTeam = empty($task->team) ? $this->task->getTeamByAccount($task->team) : ''; $this->display(); } diff --git a/module/task/css/start.ui.css b/module/task/css/start.ui.css new file mode 100644 index 0000000000..45f31075f6 --- /dev/null +++ b/module/task/css/start.ui.css @@ -0,0 +1 @@ +#taskStartForm .no-background .input-group-addon {background: none;} diff --git a/module/task/test/model/computemultiplehours.php b/module/task/test/model/computemultiplehours.php index 91b81241f0..433fe1bfa5 100755 --- a/module/task/test/model/computemultiplehours.php +++ b/module/task/test/model/computemultiplehours.php @@ -70,14 +70,14 @@ cid=1 */ $task = new taskTest(); -r($task->computeMultipleHoursTest($oldTasks[0])) && p('id,assignedTo,status,estimate,consumed,left') && e('1,admin,doing,5,34,4'); // taskID 1 只有老task计算多人工时 -r($task->computeMultipleHoursTest($oldTasks[1])) && p('id,assignedTo,status,estimate,consumed,left') && e('2,user1,done,13,201,0'); // taskID 2 只有老task计算多人工时 -r($task->computeMultipleHoursTest($oldTasks[2])) && p('id,assignedTo,status,estimate,consumed,left') && e('3,admin,done,15,0,4'); // taskID 3 只有老task计算多人工时 -r($task->computeMultipleHoursTest($oldTasks[3])) && p('id,assignedTo,status,estimate,consumed,left') && e('4,user1,pause,17,0,2'); // taskID 4 只有老task计算多人工时 -r($task->computeMultipleHoursTest($oldTasks[4])) && p('id,assignedTo,status,estimate,consumed,left') && e('5,admin,cancel,0,0,0'); // taskID 5 只有老task计算多人工时 -r($task->computeMultipleHoursTest($oldTasks[0], $tasks[0])) && p('id,assignedTo,status,estimate,consumed,left') && e('1,admin,doing,5,34,4'); // taskID 1 有传入task计算多人工时 -r($task->computeMultipleHoursTest($oldTasks[1], $tasks[1])) && p('id,assignedTo,status,estimate,consumed,left') && e('2,user1,done,13,201,0'); // taskID 2 有传入task计算多人工时 -r($task->computeMultipleHoursTest($oldTasks[0], $tasks[0], $members[0])) && p('id,assignedTo,status,estimate,consumed,left') && e('1,admin,doing,3,34,3'); // taskID 1 有传入task 传入members计算多人工时 -r($task->computeMultipleHoursTest($oldTasks[1], $tasks[1], $members[1])) && p('id,assignedTo,status,estimate,consumed,left') && e('2,user1,doing,3,201,3'); // taskID 2 有传入task 传入members计算多人工时 -r($task->computeMultipleHoursTest($oldTasks[0], $tasks[0], $members[0], false)) && p('id,assignedTo,status,estimate,consumed,left') && e('1,admin,doing,3,34,3'); // taskID 1 有传入task 传入members 不自动更新状态计算多人工时 -r($task->computeMultipleHoursTest($oldTasks[1], $tasks[1], $members[1], false)) && p('id,assignedTo,status,estimate,consumed,left') && e('2,user1,doing,3,201,3'); // taskID 2 有传入task 传入members 不自动更新状态计算多人工时 +r($task->computeMultipleHoursTest($oldTasks[0])) && p('id,assignedTo,status,estimate,consumed,left') && e('1,admin,doing,5,0,4'); // taskID 1 只有老task计算多人工时 +r($task->computeMultipleHoursTest($oldTasks[1])) && p('id,assignedTo,status,estimate,consumed,left') && e('2,user1,done,13,0,0'); // taskID 2 只有老task计算多人工时 +r($task->computeMultipleHoursTest($oldTasks[2])) && p('id,assignedTo,status,estimate,consumed,left') && e('3,admin,done,15,0,4'); // taskID 3 只有老task计算多人工时 +r($task->computeMultipleHoursTest($oldTasks[3])) && p('id,assignedTo,status,estimate,consumed,left') && e('4,user1,pause,17,0,2'); // taskID 4 只有老task计算多人工时 +r($task->computeMultipleHoursTest($oldTasks[4])) && p('id,assignedTo,status,estimate,consumed,left') && e('5,admin,cancel,0,0,0'); // taskID 5 只有老task计算多人工时 +r($task->computeMultipleHoursTest($oldTasks[0], $tasks[0])) && p('id,assignedTo,status,estimate,consumed,left') && e('1,admin,doing,5,0,4'); // taskID 1 有传入task计算多人工时 +r($task->computeMultipleHoursTest($oldTasks[1], $tasks[1])) && p('id,assignedTo,status,estimate,consumed,left') && e('2,user1,done,13,0,0'); // taskID 2 有传入task计算多人工时 +r($task->computeMultipleHoursTest($oldTasks[0], $tasks[0], $members[0])) && p('id,assignedTo,status,estimate,consumed,left') && e('1,admin,doing,3,0,3'); // taskID 1 有传入task 传入members计算多人工时 +r($task->computeMultipleHoursTest($oldTasks[1], $tasks[1], $members[1])) && p('id,assignedTo,status,estimate,consumed,left') && e('2,user1,done,3,0,3'); // taskID 2 有传入task 传入members计算多人工时 +r($task->computeMultipleHoursTest($oldTasks[0], $tasks[0], $members[0], false)) && p('id,assignedTo,status,estimate,consumed,left') && e('1,admin,doing,3,0,3'); // taskID 1 有传入task 传入members 不自动更新状态计算多人工时 +r($task->computeMultipleHoursTest($oldTasks[1], $tasks[1], $members[1], false)) && p('id,assignedTo,status,estimate,consumed,left') && e('2,user1,done,3,0,3'); // taskID 2 有传入task 传入members 不自动更新状态计算多人工时 diff --git a/module/task/test/model/getteambyaccount.php b/module/task/test/model/getteambyaccount.php index cd02747f28..08e3674c50 100644 --- a/module/task/test/model/getteambyaccount.php +++ b/module/task/test/model/getteambyaccount.php @@ -10,27 +10,24 @@ title=taskModel->getTeamByAccount(); cid=1 pid=1 -获取未开始的指定账号的团队信息 >> admin_wait -获取进行中的指定账号的团队信息 >> admin_doing -过滤已完成的成员获取的指定账号的团队信息 >> admin_wait -不过滤已完成的成员获取的指定账号的团队信息 >> admin_done -获取的不存在账号 >> _ - */ $user = new stdclass(); +$user->task = 1; $user->account = 'admin'; $user->status = 'wait'; $user->order = '0'; $users[] = $user; $user = new stdclass(); +$user->task = 1; $user->account = 'dev1'; $user->status = 'wait'; $user->order = '1'; $users[] = $user; $user = new stdclass(); +$user->task = 1; $user->account = 'admin'; $user->status = 'wait'; $user->order = '2'; diff --git a/module/task/test/task.class.php b/module/task/test/task.class.php index ae532b086c..0ed2e486b2 100644 --- a/module/task/test/task.class.php +++ b/module/task/test/task.class.php @@ -191,7 +191,7 @@ class taskTest public function startTest(int $taskID, array $param = array()): array { $task = new stdclass(); - $startFields = array('id' => $taskID, 'status' => 'doing', 'assignedTo' => '', 'realstarted' => '', 'left' => 0, 'consumed' => 0); + $startFields = array('id' => $taskID, 'status' => 'doing', 'assignedTo' => '', 'realstarted' => helper::now(), 'left' => 0, 'consumed' => 0); foreach($startFields as $field => $defaultvalue) $task->{$field} = $defaultvalue; foreach($param as $key => $value) $task->{$key} = $value; @@ -1811,7 +1811,7 @@ class taskTest * @access public * @return object|array */ - public function createObject($param = array()): object|array + public function createObject(array $param = array()): object|array { $createFields = array( 'module' => 0, diff --git a/module/task/ui/create.html.php b/module/task/ui/create.html.php index d0c93c4836..7f7b50fa05 100644 --- a/module/task/ui/create.html.php +++ b/module/task/ui/create.html.php @@ -33,7 +33,7 @@ $requiredFields = array(); foreach(explode(',', $config->task->create->requiredFields) as $field) { if($field) $requiredFields[$field] = ''; - if($field and strpos($showFields, $field) === false) $showFields .= ',' . $field; + if($field && strpos($showFields, $field) === false) $showFields .= ',' . $field; } jsVar('showFields', $showFields); diff --git a/module/task/ui/start.html.php b/module/task/ui/start.html.php new file mode 100644 index 0000000000..e6b5ef9a9c --- /dev/null +++ b/module/task/ui/start.html.php @@ -0,0 +1,112 @@ + + * @package task + * @link https://www.zentao.net + */ +namespace zin; +/* ====== Preparing and processing page data ====== */ + +/* zin: Set variables to define control for form. */ +if($task->mode == 'linear') +{ + $assignedToControl = inputGroup( + set::class('no-background'), + zget($members, $assignedTo), + input + ( + set::class('hidden'), + set::name('assignedTo'), + set::value($assignedTo), + ) + ); +} +else +{ + $assignedToControl = select( + set::name('assignedTo'), + set::value($assignedTo), + set::items($members), + ); +} + +/* ====== Define the page structure with zin widgets ====== */ + +if(!$canRecordEffort) +{ + if($task->assignedTo != $app->user->account && $task->mode == 'linear') + { + $deniedNotice = sprintf($lang->task->deniedNotice, $task->assignedToRealName, $lang->task->start); + } + else + { + $deniedNotice = sprintf($lang->task->deniedNotice, $lang->task->teamMember, $lang->task->start); + } + + div + ( + set::class('alert with-icon'), + icon('exclamation-sign'), + div + ( + set::class('content'), + p + ( + set::class('font-bold'), + $deniedNotice + ) + ) + ); +} +else +{ + formPanel + ( + set::id('taskStartForm'), + formGroup + ( + set::class($task->mode == 'multi' ? 'hidden' : ''), + set::width('1/3'), + set::label($lang->task->assignedTo), + $assignedToControl, + ), + formGroup + ( + set::width('1/3'), + set::label($lang->task->realStarted), + set::name('realStarted'), + set::control('date'), + set::value(helper::isZeroDate($task->realStarted) ? helper::now() : $task->realStarted) + ), + formRow + ( + formGroup + ( + set::width('1/3'), + set::label($task->mode == 'linear' ? $lang->task->myConsumed : $lang->task->consumed), + set::name('consumed'), + set::value(!empty($currentTeam) ? (float)$currentTeam->consumed : $task->consumed), + ), + formGroup + ( + set::width('1/3'), + set::label($lang->task->left), + set::name('left'), + set::value(!empty($currentTeam) ? (float)$currentTeam->left : $task->left), + ), + ), + formGroup + ( + set::label($lang->comment), + set::name('comment'), + set::control('editor'), + ), + ); +} + +/* ====== Render page ====== */ +render(); diff --git a/module/task/zen.php b/module/task/zen.php index c6f9b3e3b8..ea4c1b2cc4 100644 --- a/module/task/zen.php +++ b/module/task/zen.php @@ -1092,7 +1092,6 @@ class taskZen extends task ->setIF(empty($postData->multiple) || count($team) < 1, 'mode', '') ->setIF($this->task->isNoStoryExecution($execution), 'story', 0) ->setIF(!empty($postData->assignedTo), 'assignedDate', helper::now()) - ->stripTags($this->config->task->editor->create['id'], $this->config->allowedTags) ->get(); if(empty($postData->estStarted)) unset($task->estStarted); @@ -1148,46 +1147,19 @@ class taskZen extends task return $testTasks; } - /** - * 准备开始任务前的数据信息。 - * Prepare the data before start the task. - * - * @param object $oldTask - * @access protected - * @return false|object - */ - protected function prepareStart(object $oldTask): false|object - { - /* Process the request data for the creation task. */ - $task = $this->buildTaskForStart($oldTask); - $currentTeam = !empty($oldTask->team) ? $this->task->getTeamByAccount($oldTask->team) : array(); - - /* Check if the input post data meets the requirements. */ - $result = $this->checkStart($oldTask, $task, $currentTeam); - if(!$result) return false; - - /* Record task effort. */ - $effort = $this->prepareEffort4Start($oldTask, $task, $currentTeam); - if($effort->consumed > 0) $effortID = $this->task->addTaskEffort($effort); - if($oldTask->mode == 'linear' && !empty($effortID)) $this->task->updateEstimateOrder($effortID, $currentTeam->order); - - return $task; - } - /** * 处理开始任务的请求数据。 * Process the request data for the start task. * * @param object $oldTask * @access protected - * @return object + * @return false|object */ - protected function buildTaskForStart(object $oldTask): object + protected function buildTaskForStart(object $oldTask): false|object { $now = helper::now(); $task = form::data($this->config->task->form->start)->add('id', $oldTask->id) ->setIF($oldTask->assignedTo != $this->app->user->account, 'assignedDate', $now) - ->stripTags($this->config->task->editor->start['id'], $this->config->allowedTags) ->get(); $task = $this->loadModel('file')->processImgURL($task, $this->config->task->editor->start['id'], $this->post->uid); @@ -1198,6 +1170,13 @@ class taskZen extends task $task->finishedDate = $now; $task->assignedTo = $oldTask->openedBy; } + + $currentTeam = !empty($oldTask->team) ? $this->task->getTeamByAccount($oldTask->team) : array(); + + /* Check if the input post data meets the requirements. */ + $result = $this->checkStart($oldTask, $task, $currentTeam); + if(!$result) return false; + return $task; } @@ -1207,12 +1186,13 @@ class taskZen extends task * * @param object $oldTask * @param object $task - * @param array $currentTeam * @access protected - * @return object + * @return bool */ - protected function prepareEffort4Start(object $oldTask, object $task, array $currentTeam): object + protected function buildEffortForStart(object $oldTask, object $task): bool { + $currentTeam = !empty($oldTask->team) ? $this->task->getTeamByAccount($oldTask->team) : array(); + $effort = new stdclass(); $effort->date = helper::today(); $effort->task = $task->id; @@ -1223,7 +1203,10 @@ class taskZen extends task $effort->consumed = !empty($oldTask->team) && $currentTeam ? $effort->consumed - $currentTeam->consumed : $effort->consumed - $oldTask->consumed; if($this->post->comment) $effort->work = $this->post->comment; - return $effort; + if($effort->consumed > 0) $effortID = $this->task->addTaskEffort($effort); + if($oldTask->mode == 'linear' && !empty($effortID)) $this->task->updateEstimateOrder($effortID, $currentTeam->order); + + return !dao::isError(); } /** @@ -1235,19 +1218,20 @@ class taskZen extends task * @access protected * @return bool */ - protected function checkStart(object $oldTask, object $task, $currentTeam): bool + protected function checkStart(object $oldTask, object $task): bool { + $currentTeam = !empty($oldTask->team) ? $this->task->getTeamByAccount($oldTask->team) : array(); if(!empty($oldTask->team)) { - if($currentTeam && $task->consumed < $currentTeam->consumed) dao::$errors['consumed'] = $this->lang->oldTask->error->consumedSmall; - if($currentTeam && $currentTeam->status == 'doing' && $oldTask->status == 'doing') dao::$errors[] = $this->lang->oldTask->error->alreadyStarted; + if($currentTeam && $task->consumed < $currentTeam->consumed) dao::$errors['consumed'] = $this->lang->task->error->consumedSmall; + if($currentTeam && $currentTeam->status == 'doing' && $oldTask->status == 'doing') dao::$errors[] = $this->lang->task->error->alreadyStarted; } else { - if($task->consumed < $oldTask->consumed) dao::$errors['consumed'] = $this->lang->oldTask->error->consumedSmall; - if($oldTask->status == 'doing') dao::$errors[] = $this->lang->oldTask->error->alreadyStarted; + if($task->consumed < $oldTask->consumed) dao::$errors['consumed'] = $this->lang->task->error->consumedSmall; + if($oldTask->status == 'doing') dao::$errors[] = $this->lang->task->error->alreadyStarted; } - if(!$task->left && !$task->consumed) dao::$errors[] = sprintf($this->lang->error->notempty, $this->lang->oldTask->consumed); + if(!$task->left && !$task->consumed) dao::$errors['consumed'] = sprintf($this->lang->error->notempty, $this->lang->task->consumed); return !dao::isError(); } @@ -1429,4 +1413,20 @@ class taskZen extends task return $this->loadModel('file')->processImgURL($task, $this->config->task->editor->activate['id'], $taskData->field('uid')); } + + /** + * 检查是否能记录任务的日志。 + * Check if the task effort can be recorded. + * + * @param object $task + * @access protected + * @return bool + */ + protected function checkRecordEffort(object $task): bool + { + if(empty($task->team)) return true; + if($task->assignedTo != $this->app->user->account && $task->mode == 'linear') return false; + if(!isset($task->members[$this->app->user->account])) return false; + return true; + } }