diff --git a/db/update1.3.sql b/db/update1.3.sql index 98887a6f27..95213fd7ce 100644 --- a/db/update1.3.sql +++ b/db/update1.3.sql @@ -34,3 +34,4 @@ ADD `lastEditedDate` DATETIME NOT NULL AFTER `lastEditedBy` ; UPDATE zt_task SET assignedTo = owner ; ALTER TABLE `zt_task` DROP `owner`; ALTER TABLE `zt_task` CHANGE `status` `status` ENUM( 'wait', 'doing', 'done', 'cancel', 'closed' ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'wait'; +ALTER TABLE `zt_task` ADD `closedReason` VARCHAR( 30 ) NOT NULL AFTER `closedDate` ; diff --git a/module/common/lang/en.php b/module/common/lang/en.php index 969a6f5b91..0f9710fcda 100644 --- a/module/common/lang/en.php +++ b/module/common/lang/en.php @@ -228,6 +228,7 @@ $lang->error->length = array("『%s』length should be『%s』", "『%s $lang->error->reg = "『%s』should like『%s』"; $lang->error->unique = "『%s』has『%s』already."; $lang->error->notempty = "『%s』can not be empty."; +$lang->error->empty = "『%s』 must be empty."; $lang->error->equal = "『%s』must be『%s』."; $lang->error->int = array("『%s』should be interger", "『%s』should between『%s-%s』."); $lang->error->float = "『%s』should be a interger or float."; diff --git a/module/common/lang/zh-cn.php b/module/common/lang/zh-cn.php index 56fcb9f6e1..4010279237 100644 --- a/module/common/lang/zh-cn.php +++ b/module/common/lang/zh-cn.php @@ -118,7 +118,6 @@ $lang->my->menu->project = '我的项目|my|project|'; $lang->my->menu->profile = array('link' => '我的档案|my|profile|', 'alias' => 'editprofile'); $lang->todo->menu = $lang->my->menu; - /* 产品视图设置。*/ $lang->product->menu->list = '%s'; $lang->product->menu->story = array('link' => '需求列表|product|browse|productID=%s', 'subModule' => 'story'); @@ -229,6 +228,7 @@ $lang->error->length = array("『%s』长度错误,应当为『%s』" $lang->error->reg = "『%s』不符合格式,应当为:『%s』。"; $lang->error->unique = "『%s』已经有『%s』这条记录了。"; $lang->error->notempty = "『%s』不能为空。"; +$lang->error->empty = "『%s』必须为空。"; $lang->error->equal = "『%s』必须为『%s』。"; $lang->error->int = array("『%s』应当是数字。", "『%s』应当介于『%s-%s』之间。"); $lang->error->float = "『%s』应当是数字,可以是小数。"; diff --git a/module/task/lang/en.php b/module/task/lang/en.php index 0b615f2107..43021d25d9 100644 --- a/module/task/lang/en.php +++ b/module/task/lang/en.php @@ -53,12 +53,14 @@ $lang->task->canceledBy = 'Canceled By'; $lang->task->canceledDate = 'Canceled Date'; $lang->task->closedBy = 'Closed By'; $lang->task->closedDate = 'Closed Date'; -$lang->task->lasteEdited = 'Last Edited'; +$lang->task->closedReason = 'Closed Reason'; +$lang->task->lastEdited = 'Last Edited'; $lang->task->statusList['wait'] = 'Waiting'; $lang->task->statusList['doing'] = 'Doing'; $lang->task->statusList['done'] = 'Done'; $lang->task->statusList['cancel'] = 'Canceled'; +$lang->task->statusList['closed'] = 'Closed'; $lang->task->typeList[''] = ''; $lang->task->typeList['design'] = 'Design'; @@ -76,6 +78,10 @@ $lang->task->priList[1] = '1'; $lang->task->priList[2] = '2'; $lang->task->priList[4] = '4'; +$lang->task->reasonList[''] = ''; +$lang->task->reasonList['done'] = 'Done'; +$lang->task->reasonList['cancel'] = 'Canceled'; + $lang->task->afterChoices['continueAdding'] = 'Continue add task for this story'; $lang->task->afterChoices['toTastList'] = 'To task list'; $lang->task->afterChoices['toStoryList'] = 'To story list'; @@ -92,6 +98,7 @@ $lang->task->buttonDone = 'Done'; $lang->task->legendBasic = 'Basic info'; $lang->task->legendEffort = 'Effort'; +$lang->task->legendLife = 'Lifetime'; $lang->task->legendDesc = 'Desc'; $lang->task->legendAction = 'Action'; diff --git a/module/task/lang/zh-cn.php b/module/task/lang/zh-cn.php index 9624d4e2f7..35a27c08a9 100644 --- a/module/task/lang/zh-cn.php +++ b/module/task/lang/zh-cn.php @@ -53,12 +53,14 @@ $lang->task->canceledBy = '由谁取消'; $lang->task->canceledDate = '取消时间'; $lang->task->closedBy = '由谁关闭'; $lang->task->closedDate = '关闭时间'; -$lang->task->lasteEdited = '最后编辑'; +$lang->task->closedReason = '关闭原因'; +$lang->task->lastEdited = '最后编辑'; $lang->task->statusList['wait'] = '未开始'; $lang->task->statusList['doing'] = '进行中'; $lang->task->statusList['done'] = '已完成'; $lang->task->statusList['cancel'] = '已取消'; +$lang->task->statusList['closed'] = '已关闭'; $lang->task->typeList[''] = ''; $lang->task->typeList['design'] = '设计'; @@ -76,6 +78,10 @@ $lang->task->priList[1] = '1'; $lang->task->priList[2] = '2'; $lang->task->priList[4] = '4'; +$lang->task->reasonList[''] = ''; +$lang->task->reasonList['done'] = '已完成'; +$lang->task->reasonList['cancel'] = '已取消'; + $lang->task->afterChoices['continueAdding'] = '继续为该需求添加任务'; $lang->task->afterChoices['toTastList'] = '返回任务列表'; $lang->task->afterChoices['toStoryList'] = '返回需求列表'; @@ -92,6 +98,7 @@ $lang->task->buttonDone = '完成'; $lang->task->legendBasic = '基本信息'; $lang->task->legendEffort = '工时信息'; +$lang->task->legendLife = '任务的一生'; $lang->task->legendDesc = '任务描述'; $lang->task->legendAction = '操作'; diff --git a/module/task/model.php b/module/task/model.php index f9482400c1..f7c83ac814 100644 --- a/module/task/model.php +++ b/module/task/model.php @@ -13,7 +13,7 @@ getById($taskID); - $task = fixer::input('post') + $now = helper::now(); + $task = fixer::input('post') ->striptags('name') ->setDefault('story, estimate, left, consumed', 0) + ->setDefault('deadline', '0000-00-00') ->setIF($this->post->story != false and $this->post->story != $oldTask->story, 'storyVersion', $this->loadModel('story')->getVersion($this->post->story)) + ->setIF($this->post->status == 'done', 'left', 0) + ->setIF($this->post->status == 'done' and !$this->post->finishedBy, 'finishedBy', $this->app->user->account) + ->setIF($this->post->status == 'done' and !$this->post->finishedDate, 'finishedDate', $now) + + ->setIF($this->post->status == 'cancel' and !$this->post->canceledBy, 'canceledBy', $this->app->user->account) + ->setIF($this->post->status == 'cancel' and !$this->post->canceledDate, 'canceledDate', $now) + ->setIF($this->post->status == 'cancel', 'closedBy', $this->post->canceledBy ? $this->post->canceledBy : $this->app->user->account) + ->setIF($this->post->status == 'cancel', 'closedDate', $this->post->canceledDate? $this->post->canceledDate : $now) + ->setIF($this->post->status == 'cancel', 'closedReason', 'cancel') + + ->setIF($this->post->status == 'closed' and !$this->post->closedBy, 'closedBy', $this->app->user->account) + ->setIF($this->post->status == 'closed' and !$this->post->closedDate, 'closedDate', $now) ->setIF($this->post->consumed > 0 and $this->post->left > 0 and $this->post->status == 'wait', 'status', 'doing') + + ->add('lastEditedBy', $this->app->user->account) + ->add('lastEditedDate', $now) ->remove('comment,files,labels') ->get(); $task->statusCustom = strpos(self::CUSTOM_STATUS_ORDER, $task->status) + 1; @@ -79,12 +96,23 @@ class taskModel extends model $this->dao->update(TABLE_TASK)->data($task) ->autoCheck() ->batchCheckIF($task->status != 'cancel', $this->config->task->edit->requiredFields, 'notempty') + ->checkIF($task->estimate != false, 'estimate', 'float') ->checkIF($task->left != false, 'left', 'float') ->checkIF($task->consumed != false, 'consumed', 'float') + ->checkIF($task->left == 0 and $task->status != 'cancel' and $task->status != 'closed', 'status', 'equal', 'done') + + ->batchCheckIF($task->status == 'waiting' or $task->status == 'doing', 'finishedBy, finishedDate, closedBy, closedDate, closedReason', 'empty') + ->checkIF($task->status == 'done', 'consumed', 'notempty') - ->checkIF($task->left == 0 and $task->status != 'cancel', 'status', 'equal', 'done') + ->checkIF($task->status == 'done' and $task->closedReason, 'closedReason', 'equal', 'done') + + ->checkIF($task->status == 'cancel', 'finishedBy', 'empty') + ->checkIF($task->status == 'cancel', 'finishedDate','empty') + + ->checkIF($task->status == 'closed', 'closedReason', 'notempty') ->where('id')->eq((int)$taskID)->exec(); + if($this->post->story != false) $this->loadModel('story')->setStage($this->post->story); if(!dao::isError()) return common::createChanges($oldTask, $task); } @@ -136,6 +164,7 @@ class taskModel extends model ->where('t1.id')->eq((int)$taskID) ->fetch(); if(!$task) return false; + foreach($task as $key => $value) if(strpos($key, 'Date') !== false and !(int)substr($value, 0, 4)) $task->$key = ''; if($task->mailto) { $task->mailto = ltrim(trim($task->mailto), ','); // remove the first , diff --git a/module/task/view/edit.html.php b/module/task/view/edit.html.php index 517fe13a46..b5688cdcf2 100644 --- a/module/task/view/edit.html.php +++ b/module/task/view/edit.html.php @@ -116,6 +116,43 @@ $(function() { +
+ task->legendLife;?> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
task->openedBy;?>openedBy];?>
task->finishedBy;?>finishedBy, 'class="select-1"');?>
task->finishedDate;?>finishedDate, 'class="text-1"');?>
task->canceledBy;?>canceledBy, 'class="select-1"');?>
task->canceledDate;?>canceledDate, 'class="text-1"');?>
task->closedBy;?>closedBy, 'class="select-1"');?>
task->closedReason;?>task->reasonList, $task->closedReason, 'class="select-1"');?>
task->closedDate;?>closedDate, 'class="text-1"');?>
+
diff --git a/module/task/view/view.html.php b/module/task/view/view.html.php index dfbf1b1b31..fe3799a698 100644 --- a/module/task/view/view.html.php +++ b/module/task/view/view.html.php @@ -19,16 +19,15 @@
session->taskList != false ? $app->session->taskList : $this->createLink('project', 'browse', "projectID=$task->project"); - //if(!($task->status != 'closed' and $task->status != 'cancel' and common::printLink('task', 'logEfforts', "taskID=$task->id", $lang->task->buttonLogEfforts))) echo $lang->task->buttonLogEfforts . ' '; - //if(!($task->status != 'closed' and $task->status != 'cancel' and common::printLink('task', 'close', "taskID=$task->id", $lang->task->buttonClose))) echo $lang->task->buttonClose . ' '; - //if(!($task->status != 'closed' and $task->status != 'cancel' and common::printLink('task', 'cancel', "taskID=$task->id", $lang->task->buttonCancel))) echo $lang->task->buttonCancel . ' '; - //if(!($task->status == 'closed' or $task->status == 'cancel' and common::printLink('task', 'activate', "taskID=$task->id", $lang->task->buttonActivate))) echo $lang->task->buttonActivate . ' '; if(!$task->deleted) { - if(!common::printLink('task', 'edit', "taskID=$task->id", $lang->task->buttonEdit)) echo $lang->task->buttonEdit . ' '; + //if(!($task->status != 'closed' and $task->status != 'cancel' and common::printLink('task', 'logEfforts', "taskID=$task->id", $lang->task->buttonLogEfforts))) echo $lang->task->buttonLogEfforts . ' '; if(!(($task->status == 'wait' or $task->status == 'cancel') and common::printLink('task', 'start', "taskID=$task->id", $lang->task->buttonStart))) echo $lang->task->buttonStart . ' '; if(!(($task->status == 'wait' or $task->status == 'doing') and common::printLink('task', 'complete', "taskID=$task->id", $lang->task->buttonDone))) echo $lang->task->buttonDone . ' '; if(!(($task->status == 'wait' or $task->status == 'doing') and common::printLink('task', 'cancel', "taskID=$task->id", $lang->task->buttonCancel))) echo $lang->task->buttonCancel . ' '; + if(!($task->status != 'closed' and $task->status != 'cancel' and common::printLink('task', 'close', "taskID=$task->id", $lang->task->buttonClose))) echo $lang->task->buttonClose . ' '; + if(!($task->status == 'closed' or $task->status == 'cancel' and common::printLink('task', 'activate', "taskID=$task->id", $lang->task->buttonActivate))) echo $lang->task->buttonActivate . ' '; + if(!common::printLink('task', 'edit', "taskID=$task->id", $lang->task->buttonEdit)) echo $lang->task->buttonEdit . ' '; if(!common::printLink('task', 'delete',"projectID=$task->project&taskID=$task->id", $lang->task->buttonDelete, 'hiddenwin')) echo $lang->task->buttonDelete . ' '; } echo html::a($browseLink, $lang->goback); @@ -48,17 +47,16 @@
status != 'closed' and $task->status != 'cancel' and common::printLink('task', 'logEfforts', "taskID=$task->id", $lang->task->buttonLogEfforts))) echo $lang->task->buttonLogEfforts . ' '; - //if(!($task->status != 'closed' and $task->status != 'cancel' and common::printLink('task', 'close', "taskID=$task->id", $lang->task->buttonClose))) echo $lang->task->buttonClose . ' '; - //if(!($task->status != 'closed' and $task->status != 'cancel' and common::printLink('task', 'cancel', "taskID=$task->id", $lang->task->buttonCancel))) echo $lang->task->buttonCancel . ' '; - //if(!($task->status == 'closed' or $task->status == 'cancel' and common::printLink('task', 'activate', "taskID=$task->id", $lang->task->buttonActivate))) echo $lang->task->buttonActivate . ' '; if(!$task->deleted) { - if(!common::printLink('task', 'edit', "taskID=$task->id", $lang->task->buttonEdit)) echo $lang->task->buttonEdit . ' '; - if(!($task->status == 'wait' and common::printLink('task', 'start', "taskID=$task->id", $lang->task->buttonStart))) echo $lang->task->buttonStart . ' '; - if(!(($task->status == 'wait' or $task->status == 'doing') and common::printLink('task', 'complete', "taskID=$task->id", $lang->task->buttonDone))) echo $lang->task->buttonDone . ' '; - if(!(($task->status == 'wait' or $task->status == 'doing') and common::printLink('task', 'cancel', "taskID=$task->id", $lang->task->buttonCancel))) echo $lang->task->buttonCancel . ' '; + if(!(($task->status == 'wait' or $task->status == 'cancel') and common::printLink('task', 'start', "taskID=$task->id", $lang->task->buttonStart))) echo $lang->task->buttonStart . ' '; + if(!(($task->status == 'wait' or $task->status == 'doing') and common::printLink('task', 'complete', "taskID=$task->id", $lang->task->buttonDone))) echo $lang->task->buttonDone . ' '; + if(!(($task->status == 'wait' or $task->status == 'doing') and common::printLink('task', 'cancel', "taskID=$task->id", $lang->task->buttonCancel))) echo $lang->task->buttonCancel . ' '; + if(!($task->status != 'closed' and $task->status != 'cancel' and common::printLink('task', 'close', "taskID=$task->id", $lang->task->buttonClose))) echo $lang->task->buttonClose . ' '; + if(!($task->status == 'closed' or $task->status == 'cancel' and common::printLink('task', 'activate', "taskID=$task->id", $lang->task->buttonActivate))) echo $lang->task->buttonActivate . ' '; + if(!common::printLink('task', 'edit', "taskID=$task->id", $lang->task->buttonEdit)) echo $lang->task->buttonEdit . ' '; if(!common::printLink('task', 'delete',"projectID=$task->project&taskID=$task->id", $lang->task->buttonDelete, 'hiddenwin')) echo $lang->task->buttonDelete . ' '; + } echo html::a($browseLink, $lang->goback); ?> @@ -89,7 +87,7 @@ task->assignedTo;?> - assignedToRealName;?> + assignedToRealName . $lang->at . $task->assignedDate;?> task->type;?> @@ -107,7 +105,7 @@ task->mailto;?> mailto)); foreach($mailto as $account) echo ' ' . $users[$account]; ?> - +
task->legendEffort;?> @@ -136,6 +134,35 @@
+
+ task->legendLife;?> + + + + + + + + + + + + + + + + + + + + + + + + + +
task->openedBy;?>openedBy) echo $users[$task->openedBy] . $lang->at . $task->openedDate;?>
task->finishedBy;?>finishedBy) echo $users[$task->finishedBy] . $lang->at . $task->finishedDate;?>
task->canceledBy;?>canceledBy) echo $users[$task->canceledBy] . $lang->at . $task->canceledDate;?>
task->closedBy;?>closedBy) echo $users[$task->closedBy] . $lang->at . $task->closedDate;?>
task->closedReason;?>task->reasonList[$task->closedReason];?>
task->lastEdited;?>lastEditedBy) echo $users[$task->lastEditedBy] . $lang->at . $task->lastEditedDate;?>
+
diff --git a/module/upgrade/model.php b/module/upgrade/model.php index 6d21102a5d..3ab2e6708b 100644 --- a/module/upgrade/model.php +++ b/module/upgrade/model.php @@ -390,6 +390,7 @@ class upgradeModel extends model { $this->execSQL($this->getUpgradeFile('1.3')); $this->updateNL1_3(); + $this->updateTasks(); if(!$this->isError()) $this->setting->updateVersion('1.4'); } @@ -528,6 +529,86 @@ class upgradeModel extends model } } + /** + * Update task fields. + * + * @access public + * @return void + */ + public function updateTasks() + { + /* Get all actions of tasks. */ + $actions = $this->dao->select('*')->from(TABLE_ACTION) + ->where('objectType')->eq('task') + ->orderBy('id') + ->fetchAll('id'); + + /* Get histories about status field. */ + $histories = $this->dao->select()->from(TABLE_HISTORY) + ->where('action')->in(array_keys($actions)) + ->andWhere('field')->eq('status') + ->orderBy('id') + ->fetchGroup('action'); + + $tasks = array(); + foreach($actions as $action) + { + if(!isset($tasks[$action->objectID])) + { + $tasks[$action->objectID] = new stdclass; + } + $task = $tasks[$action->objectID]; + + $task->id = $action->objectID; + $actionType = strtolower($action->action); + + /* Set the openedBy info. */ + if($actionType == 'opened') + { + $task->openedBy = $action->actor; + $task->openedDate = $action->date; + } + else + { + if(!isset($histories[$action->id])) continue; + + $actionHistories = $histories[$action->id]; + foreach($actionHistories as $history) + { + /* Finished by. */ + if($history->new == 'done') + { + $task->finishedBy = $action->actor; + $task->finishedDate = $action->date; + $action->action = 'finished'; + } + /* Canceled By. */ + elseif($history->new == 'cancel') + { + $task->canceledBy = $action->actor; + $task->canceledDate = $action->date; + $action->action = 'canceled'; + } + } + + /* Last edited by .*/ + $task->lastEditedBy = $action->actor; + $task->lastEditedDate = $action->date; + + /* Update action type. */ + $this->dao->update(TABLE_ACTION)->set('action')->eq($action->action)->where('id')->eq($action->id)->exec(false); + } + } + + /* Update db. */ + foreach($tasks as $task) + { + $this->dao->update(TABLE_TASK)->data($task, false)->where('id')->eq($task->id)->exec(false); + } + + /* Update action name. */ + } + /** * Get the upgrade sql file. *