diff --git a/framework/helper.class.php b/framework/helper.class.php index b65646c555..f6b7216618 100644 --- a/framework/helper.class.php +++ b/framework/helper.class.php @@ -388,6 +388,9 @@ class helper extends baseHelper return (array)$value; case 'object': return (object)$value; + case 'datetime': + case 'date': + return $value ? (string)$value : null; case 'string': default: return (string)$value; diff --git a/lib/base/dao/dao.class.php b/lib/base/dao/dao.class.php index ce06850680..08fd73271a 100644 --- a/lib/base/dao/dao.class.php +++ b/lib/base/dao/dao.class.php @@ -622,7 +622,16 @@ class baseDAO { if(strpos($skipFields, ",$field,") !== false) continue; $fields .= "`{$field}`,"; - if(is_string($value) or $value === null) $value = $this->sqlobj->quote($value); + + if(is_string($value)) + { + $value = $this->sqlobj->quote($value); + } + elseif($value === null) + { + $value = 'NULL'; + } + $values .= $value . ','; } $fields = substr($fields, 0, -1); diff --git a/lib/form/form.class.php b/lib/form/form.class.php index b7d486f0ce..310f3af613 100644 --- a/lib/form/form.class.php +++ b/lib/form/form.class.php @@ -188,6 +188,7 @@ class form extends fixer $this->errors[$rowIndex][$field][] = sprintf($app->lang->error->notempty, $fieldName); } } + $rowDataList[$rowIndex] = $rowData; } diff --git a/module/task/config/form.php b/module/task/config/form.php index 7a6147834f..b9c53e8903 100644 --- a/module/task/config/form.php +++ b/module/task/config/form.php @@ -110,12 +110,10 @@ $config->task->form->batchCreate = common::formConfig('task', 'batchCreate'); $config->task->form->batchCreate['module'] = array('type' => 'int', 'required' => false, 'default' => 0); $config->task->form->batchCreate['parent'] = array('type' => 'int', 'required' => false, 'default' => 0); $config->task->form->batchCreate['story'] = array('type' => 'int', 'required' => false, 'default' => 0); -$config->task->form->batchCreate['storyEstimate'] = array('type' => 'float', 'required' => false, 'default' => 0); -$config->task->form->batchCreate['storyDesc'] = array('type' => 'string', 'required' => false, 'default' => ''); -$config->task->form->batchCreate['storyPri'] = array('type' => 'int', 'required' => false, 'default' => 0); $config->task->form->batchCreate['name'] = array('type' => 'string', 'required' => false, 'default' => '', 'base' => true); $config->task->form->batchCreate['color'] = array('type' => 'string', 'required' => false, 'default' => ''); $config->task->form->batchCreate['type'] = array('type' => 'string', 'required' => false, 'default' => ''); +$config->task->form->batchCreate['version'] = array('type' => 'int', 'required' => false, 'default' => 1); $config->task->form->batchCreate['assignedTo'] = array('type' => 'string', 'required' => false, 'default' => ''); $config->task->form->batchCreate['estimate'] = array('type' => 'float', 'required' => false, 'default' => 0); $config->task->form->batchCreate['estStarted'] = array('type' => 'date', 'required' => false, 'default' => ''); diff --git a/module/task/control.php b/module/task/control.php index 64ac3e0330..f5ad7e2682 100755 --- a/module/task/control.php +++ b/module/task/control.php @@ -145,18 +145,18 @@ class task extends control if(!empty($_POST)) { /* 批量创建任务。 Batch create tasks. */ - $taskData = $this->taskZen->buildTasksForBatchCreate($execution, $taskID); - if(dao::isError()) return print(js::error(dao::getError())); + $taskData = $this->taskZen->buildTasksForBatchCreate($executionID, $taskID); + if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); $taskIdList = $this->task->batchCreate($execution, $taskData, $taskID, $output); - if(dao::isError()) return print(js::error(dao::getError())); + if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); /* 接口调用返回任务编号列表。 Return task id list when call the API. */ if($this->viewType == 'json' or (defined('RUN_MODE') && RUN_MODE == 'api')) return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'idList' => $taskIdList)); /* 生成跳转链接。 Generate jump link. */ $jumpLink = $this->taskZen->getJumpLink($execution); - if(!isonlybody()) return print(js::locate($jumpLink, 'parent')); + if(!isonlybody()) return $this->send(array('result' => 'success', 'load' => $jumpLink)); /* 执行应用下或者在运营管理界面下更新看板数据。 */ /* Update kanban data under the execution application or under the operation management interface. */ @@ -166,7 +166,8 @@ class task extends control if($execution->type == 'kanban') return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban($kanbanData, 0)")); return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban(\"task\", $kanbanData)")); } - return print(js::reload('parent.parent')); + + return $this->send(array('result' => 'success', 'load' => 'true')); } $this->taskZen->setMenu($executionID); diff --git a/module/task/model.php b/module/task/model.php index a66c40d6fc..9660144a43 100755 --- a/module/task/model.php +++ b/module/task/model.php @@ -2144,6 +2144,26 @@ class taskModel extends model return $this->taskTao->batchComputeProgress($tasks); } + /** + * Get task list of stories. + * + * @param int[] $storyIdList + * @param int $executionID + * @param int $projectID + * @access public + * @return object[] + */ + public function getListByStories(array $storyIdList, int $executionID = 0, int $projectID = 0): array + { + return $this->dao->select('id, story, parent, name, assignedTo, pri, status, estimate, consumed, closedReason, `left`') + ->from(TABLE_TASK) + ->where('story')->in($storyIdList) + ->andWhere('deleted')->eq(0) + ->beginIF($executionID)->andWhere('execution')->eq($executionID)->fi() + ->beginIF($projectID)->andWhere('project')->eq($projectID)->fi() + ->fetchAll('id'); + } + /** * Get counts of some stories' tasks. * @@ -3156,7 +3176,7 @@ class taskModel extends model echo round($task->progress, 2) . '%'; break; case 'deadline': - if(substr($task->deadline, 0, 4) > 0) echo '' . substr($task->deadline, 5, 6) . ''; + if($task->deadline && substr($task->deadline, 0, 4) > 0) echo '' . substr($task->deadline, 5, 6) . ''; break; case 'openedBy': echo zget($users, $task->openedBy); diff --git a/module/task/zen.php b/module/task/zen.php index 0684c8e6db..addee8e5d9 100644 --- a/module/task/zen.php +++ b/module/task/zen.php @@ -477,97 +477,71 @@ class taskZen extends task * 处理批量创建任务的请求数据。 * Process the request data for the batch create tasks. * - * @param object $execution - * @param int $taskID + * @param int $executionID + * @param int $taskID * @access protected - * @return object[]|false + * @return object[] */ - protected function buildTasksForBatchCreate(object $execution, int $taskID): array|false + protected function buildTasksForBatchCreate(int $executionID, int $taskID): array { $tasks = form::batchData($this->config->task->form->batchCreate)->get(); - - /* 去除重复数据。 Deduplicated data. */ - $tasks = $this->removeDuplicate4BatchCreate($execution, $tasks); - if(!$tasks) return false; - - /* Init. */ - $story = 0; - $module = 0; - $type = ''; - $assignedTo = ''; - $estStarted = null; - $deadline = null; - - /* Get task data. */ - $this->loadModel('common'); - $extendFields = $this->task->getFlowExtendFields(); - $data = array(); - foreach($tasks->name as $i => $name) + foreach($tasks as $task) { - if(empty($name)) continue; - - /* 给同上的变量赋值。 Assign values to ditto fields. */ - $story = !isset($tasks->story[$i]) || $tasks->story[$i] == 'ditto' ? $story : $tasks->story[$i]; - $module = !isset($tasks->module[$i]) || $tasks->module[$i] == 'ditto' ? $module : $tasks->module[$i]; - $type = !isset($tasks->type[$i]) || $tasks->type[$i] == 'ditto' ? $type : $tasks->type[$i]; - $assignedTo = !isset($tasks->assignedTo[$i]) || $tasks->assignedTo[$i] == 'ditto' ? $assignedTo : $tasks->assignedTo[$i]; - $estStarted = !isset($tasks->estStarted[$i]) || isset($tasks->estStartedDitto[$i]) ? $estStarted : $tasks->estStarted[$i]; - $deadline = !isset($tasks->deadline[$i]) || isset($tasks->deadlineDitto[$i]) ? $deadline : $tasks->deadline[$i]; - - - $dittoFields = array('story' => $story, 'module' => $module, 'type' => $type, 'assignedTo' => $assignedTo, 'estStarted' => $estStarted, 'deadline' => $deadline); - $data[$i] = $this->buildData4BatchCreate($execution, $tasks, $i, $dittoFields, $extendFields, $taskID); + $task->execution = $executionID; + $task->left = $task->estimate; } - return $data; + /* 去除重复数据。 Deduplicated data. */ + return $this->removeDuplicate4BatchCreate($executionID, $tasks); } /** * 在批量创建之前移除post数据中重复的数据。 * Remove the duplicate data before batch create tasks. * - * @param object $execution - * @param object $tasks + * @param int $executionID + * @param array $tasks * @access protected - * @return object|false + * @return array */ - protected function removeDuplicate4BatchCreate(object $execution, object $tasks): object|false + protected function removeDuplicate4BatchCreate(int $executionID, array $tasks): array { - $storyIdList = array(); - $taskNames = array(); - $prevStory = 0; - - foreach($tasks->story as $key => $storyID) + /* 1. 检查表单是否有重复。 Check duplicate in form data. */ + $duplicateTasks = array(); + $storyIdList = array(); + foreach($tasks as $rowIndex => $task) { - /* 过滤事务型和任务名称为空的数据。 Filter affair type tasks and empty task name data. */ - if(empty($tasks->name[$key])) continue; - if($tasks->type[$key] == 'affair') continue; - if($tasks->type[$key] == 'ditto' && isset($tasks->type[$key - 1]) && $tasks->type[$key - 1] == 'affair') continue; + if(empty($task->story)) continue; - if($storyID == 'ditto') $storyID = $prevStory; - $prevStory = $storyID; + /* 事务型任务可能有多个指派人,不需要检查是否重名。 Tasks of Affair type no need to check duplicate name. */ + if($task->type == 'affair') continue; - if(!isset($tasks->story[$key - 1]) && $key > 1 && !empty($tasks->name[$key - 1])) + /* 表单的任务名称+不能有重复。 The name of post tasks must be unique. */ + + /* 检查Post传过来的任务有没有重复数据,不能有相同需求的同名任务。 Check whether the post tasks have duplicate data. */ + $duplicateKey = (string)$task->story . '-' . $task->name; + if(isset($duplicateTasks[$duplicateKey])) { - $storyIdList[] = 0; - $taskNames[] = $tasks->name[$key - 1]; + dao::$errors["name[$rowIndex]"] = sprintf($this->lang->duplicate, $this->lang->task->common) . ' ' . $task->name; + return array(); } - - /* 检查Post传过来的任务有没有重复数据。 Check whether the task passed by Post has duplicate data. */ - $hasExistsName = in_array($tasks->name[$key], $taskNames); - if($hasExistsName && in_array($storyID, $storyIdList)) - { - dao::$errors['message'][] = sprintf($this->lang->duplicate, $this->lang->task->common) . ' ' . $tasks->name[$key]; - return false; - } - - $storyIdList[] = $storyID; - $taskNames[] = $tasks->name[$key]; + $duplicateTasks[$duplicateKey] = array('rowIndex' => $rowIndex, 'name' => $task->name); + $storyIdList[$task->story] = $task->story; } - $querySQL = "execution={$execution->id} and story " . helper::dbIN($storyIdList); - $result = $this->loadModel('common')->removeDuplicate('task', $tasks, $querySQL); - return $result['data']; + /* 2. 检查数据库是否有重复数据。 Check duplicate in db. */ + $existTasks = $this->task->getListByStories($storyIdList, $executionID); + foreach($existTasks as $task) + { + $duplicateKey = (string)$task->story . '-' . $task->name; + if(isset($duplicateTasks[$duplicateKey])) + { + $rowIndex = $duplicateTasks[$duplicateKey]['rowIndex']; + unset($tasks[$rowIndex]); + } + } + + return $tasks; } /**