diff --git a/module/custom/lang/de.php b/module/custom/lang/de.php index 4b99e271d9..ceba0d6862 100644 --- a/module/custom/lang/de.php +++ b/module/custom/lang/de.php @@ -249,5 +249,5 @@ $lang->custom->conceptOptions->hourPoint['2'] = 'Function Point'; $lang->custom->scrum = new stdclass(); $lang->custom->scrum->setConcept = 'Set concept'; -$lang->custom->reviewRules[1] = 'All passed'; -$lang->custom->reviewRules[0] = 'More than half passed'; +$lang->custom->reviewRules['allpass'] = 'All passed'; +$lang->custom->reviewRules['halfpass'] = 'More than half passed'; diff --git a/module/custom/lang/en.php b/module/custom/lang/en.php index 70485943fa..6f27bf910f 100644 --- a/module/custom/lang/en.php +++ b/module/custom/lang/en.php @@ -250,5 +250,5 @@ $lang->custom->conceptOptions->hourPoint['2'] = 'Function Point'; $lang->custom->scrum = new stdclass(); $lang->custom->scrum->setConcept = 'Set concept'; -$lang->custom->reviewRules[1] = 'All passed'; -$lang->custom->reviewRules[0] = 'More than half passed'; +$lang->custom->reviewRules['allpass'] = 'All passed'; +$lang->custom->reviewRules['halfpass'] = 'More than half passed'; diff --git a/module/custom/lang/fr.php b/module/custom/lang/fr.php index 57e344b47b..298a9108c0 100644 --- a/module/custom/lang/fr.php +++ b/module/custom/lang/fr.php @@ -249,5 +249,5 @@ $lang->custom->conceptOptions->hourPoint['2'] = 'Function Point'; $lang->custom->scrum = new stdclass(); $lang->custom->scrum->setConcept = 'Set concept'; -$lang->custom->reviewRules[1] = 'All passed'; -$lang->custom->reviewRules[0] = 'More than half passed'; +$lang->custom->reviewRules['allpass'] = 'All passed'; +$lang->custom->reviewRules['halfpass'] = 'More than half passed'; diff --git a/module/custom/lang/vi.php b/module/custom/lang/vi.php index fbfb48e835..9bca073025 100644 --- a/module/custom/lang/vi.php +++ b/module/custom/lang/vi.php @@ -249,5 +249,5 @@ $lang->custom->conceptOptions->hourPoint['2'] = 'Function Point'; $lang->custom->scrum = new stdclass(); $lang->custom->scrum->setConcept = 'Set concept'; -$lang->custom->reviewRules[1] = 'All passed'; -$lang->custom->reviewRules[0] = 'More than half passed'; +$lang->custom->reviewRules['allpass'] = 'All passed'; +$lang->custom->reviewRules['halfpass'] = 'More than half passed'; diff --git a/module/custom/lang/zh-cn.php b/module/custom/lang/zh-cn.php index 18540b48d6..60b1e10b0b 100644 --- a/module/custom/lang/zh-cn.php +++ b/module/custom/lang/zh-cn.php @@ -251,5 +251,5 @@ $lang->custom->conceptOptions->hourPoint['2'] = '功能点'; $lang->custom->scrum = new stdclass(); $lang->custom->scrum->setConcept = '设置项目概念'; -$lang->custom->reviewRules[1] = '全部通过通过'; -$lang->custom->reviewRules[0] = '半数以上通过通过'; +$lang->custom->reviewRules['allpass'] = '全部通过通过'; +$lang->custom->reviewRules['halfpass'] = '半数以上通过通过'; diff --git a/module/custom/lang/zh-tw.php b/module/custom/lang/zh-tw.php index 63b6f736b9..f9c73dc8fa 100644 --- a/module/custom/lang/zh-tw.php +++ b/module/custom/lang/zh-tw.php @@ -250,5 +250,5 @@ $lang->custom->conceptOptions->hourPoint['2'] = '功能點'; $lang->custom->scrum = new stdclass(); $lang->custom->scrum->setConcept = '設置項目概念'; -$lang->custom->reviewRules[1] = '全部通過通過'; -$lang->custom->reviewRules[0] = '半數以上通過通過'; +$lang->custom->reviewRules['allpass'] = '全部通過通過'; +$lang->custom->reviewRules['halfpass'] = '半數以上通過通過'; diff --git a/module/my/lang/de.php b/module/my/lang/de.php index ba6886e58e..b02897afcd 100644 --- a/module/my/lang/de.php +++ b/module/my/lang/de.php @@ -60,6 +60,7 @@ $lang->my->taskMenu->canceledByMe = 'CancelledByMe'; $lang->my->storyMenu = new stdclass(); $lang->my->storyMenu->assignedToMe = 'AssignedToMe'; +$lang->my->storyMenu->reviewByMe = 'ReviewByMe'; $lang->my->storyMenu->openedByMe = 'CreatedByMe'; $lang->my->storyMenu->reviewedByMe = 'ReviewedByMe'; $lang->my->storyMenu->closedByMe = 'ClosedByMe'; diff --git a/module/my/lang/en.php b/module/my/lang/en.php index c59b5a3bda..d050c58b80 100644 --- a/module/my/lang/en.php +++ b/module/my/lang/en.php @@ -67,6 +67,7 @@ $lang->my->taskMenu->canceledByMe = 'CancelledByMe'; $lang->my->storyMenu = new stdclass(); $lang->my->storyMenu->assignedToMe = 'AssignedToMe'; +$lang->my->storyMenu->reviewByMe = 'ReviewByMe'; $lang->my->storyMenu->openedByMe = 'CreatedByMe'; $lang->my->storyMenu->reviewedByMe = 'ReviewedByMe'; $lang->my->storyMenu->closedByMe = 'ClosedByMe'; diff --git a/module/my/lang/fr.php b/module/my/lang/fr.php index ba6886e58e..b02897afcd 100644 --- a/module/my/lang/fr.php +++ b/module/my/lang/fr.php @@ -60,6 +60,7 @@ $lang->my->taskMenu->canceledByMe = 'CancelledByMe'; $lang->my->storyMenu = new stdclass(); $lang->my->storyMenu->assignedToMe = 'AssignedToMe'; +$lang->my->storyMenu->reviewByMe = 'ReviewByMe'; $lang->my->storyMenu->openedByMe = 'CreatedByMe'; $lang->my->storyMenu->reviewedByMe = 'ReviewedByMe'; $lang->my->storyMenu->closedByMe = 'ClosedByMe'; diff --git a/module/my/lang/vi.php b/module/my/lang/vi.php index ba6886e58e..b02897afcd 100644 --- a/module/my/lang/vi.php +++ b/module/my/lang/vi.php @@ -60,6 +60,7 @@ $lang->my->taskMenu->canceledByMe = 'CancelledByMe'; $lang->my->storyMenu = new stdclass(); $lang->my->storyMenu->assignedToMe = 'AssignedToMe'; +$lang->my->storyMenu->reviewByMe = 'ReviewByMe'; $lang->my->storyMenu->openedByMe = 'CreatedByMe'; $lang->my->storyMenu->reviewedByMe = 'ReviewedByMe'; $lang->my->storyMenu->closedByMe = 'ClosedByMe'; diff --git a/module/my/lang/zh-cn.php b/module/my/lang/zh-cn.php index fe057d4eaa..e5697ee35e 100644 --- a/module/my/lang/zh-cn.php +++ b/module/my/lang/zh-cn.php @@ -67,6 +67,7 @@ $lang->my->taskMenu->canceledByMe = '由我取消'; $lang->my->storyMenu = new stdclass(); $lang->my->storyMenu->assignedToMe = '指派给我'; +$lang->my->storyMenu->reviewByMe = '待我评审'; $lang->my->storyMenu->openedByMe = '由我创建'; $lang->my->storyMenu->reviewedByMe = '由我评审'; $lang->my->storyMenu->closedByMe = '由我关闭'; diff --git a/module/my/lang/zh-tw.php b/module/my/lang/zh-tw.php index 9f42ee7ad2..fd9b1ea349 100644 --- a/module/my/lang/zh-tw.php +++ b/module/my/lang/zh-tw.php @@ -67,6 +67,7 @@ $lang->my->taskMenu->canceledByMe = '由我取消'; $lang->my->storyMenu = new stdclass(); $lang->my->storyMenu->assignedToMe = '指派給我'; +$lang->my->storyMenu->reviewByMe = '待我評審'; $lang->my->storyMenu->openedByMe = '由我創建'; $lang->my->storyMenu->reviewedByMe = '由我評審'; $lang->my->storyMenu->closedByMe = '由我關閉'; diff --git a/module/my/view/story.html.php b/module/my/view/story.html.php index 243b9213d3..f762af38c6 100644 --- a/module/my/view/story.html.php +++ b/module/my/view/story.html.php @@ -24,6 +24,11 @@ echo html::a(inlink($app->rawMethod, "mode=story&type=reviewedBy&orderBy=$orderBy&recTotal=$recTotal&recPerPage=$recPerPage&pagerID=$pageID"), "{$lang->my->storyMenu->reviewedByMe}" . ($type == 'reviewedBy' ? $recTotalLabel : ''), '', "class='btn btn-link" . ($type == 'reviewedBy' ? ' btn-active-text' : '') . "'"); echo html::a(inlink($app->rawMethod, "mode=story&type=closedBy&orderBy=$orderBy&recTotal=$recTotal&recPerPage=$recPerPage&pagerID=$pageID"), "{$lang->my->storyMenu->closedByMe}" . ($type == 'closedBy' ? $recTotalLabel : ''), '', "class='btn btn-link" . ($type == 'closedBy' ? ' btn-active-text' : '') . "'"); } + else + { + echo html::a(inlink($app->rawMethod, "mode=story&type=assignedTo&orderBy=$orderBy&recTotal=$recTotal&recPerPage=$recPerPage&pagerID=$pageID"), "{$lang->my->storyMenu->assignedToMe}" . ($type == 'assignedTo' ? $recTotalLabel : ''), '', "class='btn btn-link" . ($type == 'assignedTo' ? ' btn-active-text' : '') . "'"); + echo html::a(inlink($app->rawMethod, "mode=story&type=reviewBy&orderBy=$orderBy&recTotal=$recTotal&recPerPage=$recPerPage&pagerID=$pageID"), "{$lang->my->storyMenu->reviewByMe}" . ($type == 'reviewBy' ? $recTotalLabel : ''), '', "class='btn btn-link" . ($type == 'reviewBy' ? ' btn-active-text' : '') . "'"); + } ?> diff --git a/module/story/config.php b/module/story/config.php index e798b121d1..b8c301f2a6 100644 --- a/module/story/config.php +++ b/module/story/config.php @@ -16,7 +16,7 @@ $config->story->review = new stdclass(); $config->story->create->requiredFields = 'title'; $config->story->change->requiredFields = 'title'; $config->story->close->requiredFields = 'closedReason'; -$config->story->review->requiredFields = 'assignedTo,reviewedBy'; +$config->story->review->requiredFields = 'assignedTo'; $config->story->editor = new stdclass(); $config->story->editor->create = array('id' => 'spec,verify', 'tools' => 'simpleTools'); diff --git a/module/story/control.php b/module/story/control.php index b00a7aa1ca..762dc8bb12 100644 --- a/module/story/control.php +++ b/module/story/control.php @@ -820,11 +820,16 @@ class story extends control $this->app->loadLang('testcase'); $this->app->loadLang('execution'); + $story = $this->story->getById($storyID); + $reviewer = $this->dao->select('reviewer')->from(TABLE_STORYREVIEW)->where('story')->eq($storyID)->andWhere('version')->eq($story->version)->fetchAll('reviewer'); + /* Assign. */ $this->view->title = $this->lang->story->change . "STORY" . $this->lang->colon . $this->view->story->title; $this->view->users = $this->user->getPairs('pofirst|nodeleted', $this->view->story->assignedTo); $this->view->position[] = $this->lang->story->change; $this->view->needReview = ($this->app->user->account == $this->view->product->PO || $this->config->story->needReview == 0) ? "checked='checked'" : ""; + $this->view->reviewer = implode(',', array_keys($reviewer)); + $this->display(); } @@ -2187,8 +2192,6 @@ class story extends control { $oldStory = $this->dao->findById((int)$params['storyID'])->from(TABLE_STORY)->fetch(); $status = $oldStory->status; - if($params['result'] == 'pass' and $oldStory->status == 'draft') $status = 'active'; - if($params['result'] == 'pass' and $oldStory->status == 'changed') $status = 'active'; if($params['result'] == 'revert') $status = 'active'; if($params['result'] == 'reject') $status = 'closed'; } diff --git a/module/story/js/change.js b/module/story/js/change.js index b8d766e61e..fcdf2de956 100644 --- a/module/story/js/change.js +++ b/module/story/js/change.js @@ -2,7 +2,7 @@ $(function() { $('#needNotReview').on('change', function() { - $('#reviewedBy').attr('disabled', $(this).is(':checked') ? 'disabled' : null).trigger('chosen:updated'); + $('#reviewer').attr('disabled', $(this).is(':checked') ? 'disabled' : null).trigger('chosen:updated'); getStatus('change', "storyID=" + storyID + ",changed=" + changed + ",needNotReview=" + ($(this).prop('checked') ? 1 : 0)); }); $('#needNotReview').change(); diff --git a/module/story/js/create.js b/module/story/js/create.js index 63418b7f26..e02e864943 100644 --- a/module/story/js/create.js +++ b/module/story/js/create.js @@ -2,7 +2,7 @@ $(function() { $('#needNotReview').on('change', function() { - $('#reviewedBy').attr('disabled', $(this).is(':checked') ? 'disabled' : null).trigger('chosen:updated'); + $('#reviewer').attr('disabled', $(this).is(':checked') ? 'disabled' : null).trigger('chosen:updated'); getStatus('create', "product=" + $('#product').val() + ",execution=" + executionID + ",needNotReview=" + ($(this).prop('checked') ? 1 : 0)); }); $('#needNotReview').change(); diff --git a/module/story/model.php b/module/story/model.php index 1d6a1e43e2..cd966a486b 100644 --- a/module/story/model.php +++ b/module/story/model.php @@ -186,7 +186,8 @@ class storyModel extends model */ public function create($executionID = 0, $bugID = 0, $from = '') { - if(!$this->post->needNotReview and empty(array_filter($_POST['reviewedBy']))) + if(isset($_POST['reviewer'])) $_POST['reviewer'] = array_filter($_POST['reviewer']); + if(!$this->post->needNotReview and empty($_POST['reviewer'])) { dao::$errors[] = $this->lang->story->errorEmptyReviewedBy; return false; @@ -202,7 +203,6 @@ class storyModel extends model ->setDefault('plan,verify', '') ->setDefault('openedBy', $this->app->user->account) ->setDefault('openedDate', $now) - ->setDefault('reviewedBy', '') ->setIF($this->post->assignedTo != '', 'assignedDate', $now) ->setIF($this->post->needNotReview or $executionID > 0, 'status', 'active') ->setIF($this->post->plan > 0, 'stage', 'planned') @@ -210,9 +210,8 @@ class storyModel extends model ->setIF($executionID > 0, 'stage', 'projected') ->setIF($bugID > 0, 'fromBug', $bugID) ->join('mailto', ',') - ->join('reviewedBy', ',') ->stripTags($this->config->story->editor->create['id'], $this->config->allowedTags) - ->remove('files,labels,needNotReview,newStory,uid,contactListMenu,URS') + ->remove('files,labels,reviewer,needNotReview,newStory,uid,contactListMenu,URS') ->get(); /* Check repeat story. */ @@ -251,6 +250,19 @@ class storyModel extends model $data->verify = $story->verify; $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) + { + $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 and $story->status != 'draft') { @@ -528,7 +540,8 @@ class storyModel extends model return false; } - if(!$this->post->needNotReview and empty(array_filter($_POST['reviewedBy']))) + if(isset($_POST['reviewer'])) $_POST['reviewer'] = array_filter($_POST['reviewer']); + if(!$this->post->needNotReview and empty($_POST['reviewer'])) { dao::$errors[] = $this->lang->story->errorEmptyReviewedBy; return false; @@ -541,7 +554,6 @@ class storyModel extends model $story = fixer::input('post') ->callFunc('title', 'trim') ->setDefault('lastEditedBy', $this->app->user->account) - ->setDefault('reviewedBy', '') ->add('lastEditedDate', $now) ->setIF($specChanged, 'version', $oldStory->version + 1) ->setIF($specChanged and $oldStory->status == 'active' and $this->post->needNotReview == false, 'status', 'changed') @@ -552,8 +564,7 @@ class storyModel extends model ->setIF($specChanged and $oldStory->reviewedBy, 'reviewedDate', '0000-00-00') ->setIF($specChanged and $oldStory->closedBy, 'closedDate', '0000-00-00') ->stripTags($this->config->story->editor->change['id'], $this->config->allowedTags) - ->join('reviewedBy', ',') - ->remove('files,labels,comment,needNotReview,uid') + ->remove('files,labels,reviewer,comment,needNotReview,uid') ->get(); if($specChanged and $story->status == 'active' and $this->checkForceReview()) $story->status = 'changed'; $story = $this->loadModel('file')->processImgURL($story, $this->config->story->editor->change['id'], $this->post->uid); @@ -561,6 +572,7 @@ class storyModel extends model ->autoCheck() ->batchCheck($this->config->story->change->requiredFields, 'notempty') ->where('id')->eq((int)$storyID)->exec(); + if(!dao::isError()) { if($specChanged) @@ -597,7 +609,23 @@ class storyModel extends model { unset($story->spec); unset($oldStory->spec); + + $this->dao->delete()->from(TABLE_STORYREVIEW)->where('story')->eq($storyID)->andWhere('version')->eq($oldStory->version)->andWhere('reviewer')->notin(implode(',', $_POST['reviewer']))->exec(); } + + /* Update the reviewer. */ + $oldReviewerList = $this->dao->select('reviewer')->from(TABLE_STORYREVIEW)->where('story')->eq($storyID)->andWhere('version')->eq($oldStory->version)->fetchPairs('reviewer'); + foreach($_POST['reviewer'] as $reviewer) + { + if(!$specChanged and in_array($reviewer, $oldReviewerList)) continue; + + $reviewData = new stdclass(); + $reviewData->story = $storyID; + $reviewData->version = $specChanged ? $story->version : $oldStory->version; + $reviewData->reviewer = $reviewer; + $this->dao->insert(TABLE_STORYREVIEW)->data($reviewData)->exec(); + } + $this->file->updateObjectID($this->post->uid, $storyID, 'story'); return common::createChanges($oldStory, $story); } @@ -1120,12 +1148,10 @@ class storyModel extends model $now = helper::now(); $date = helper::today(); $story = fixer::input('post') + ->add('reviewedBy', $oldStory->reviewedBy . ',' . $this->app->user->account) ->setDefault('reviewedDate', $date) ->setDefault('lastEditedBy', $this->app->user->account) ->setDefault('lastEditedDate', $now) - ->setDefault('reviewedBy', '') - ->setIF($this->post->result == 'pass' and $oldStory->status == 'draft', 'status', 'active') - ->setIF($this->post->result == 'pass' and $oldStory->status == 'changed', 'status', 'active') ->setIF($this->post->result == 'reject', 'closedBy', $this->app->user->account) ->setIF($this->post->result == 'reject', 'closedDate', $now) ->setIF($this->post->result == 'reject', 'assignedTo', 'closed') @@ -1137,13 +1163,22 @@ class storyModel extends model ->removeIF($this->post->result != 'reject', 'closedReason, duplicateStory, childStories') ->removeIF($this->post->result == 'reject' and $this->post->closedReason != 'duplicate', 'duplicateStory') ->removeIF($this->post->result == 'reject' and $this->post->closedReason != 'subdivided', 'childStories') - ->join('reviewedBy', ',') ->remove('result,preVersion,comment') ->get(); - /* fix bug #671. */ + /* Fix bug #671. */ $this->lang->story->closedReason = $this->lang->story->rejectedReason; + $this->dao->update(TABLE_STORYREVIEW)->set('result')->eq($this->post->result)->set('reviewDate')->eq($now)->where('story')->eq($storyID)->andWhere('version')->eq($oldStory->version)->andWhere('reviewer')->eq($this->app->user->account)->exec(); + + /* Update the story status by review rules. */ + $passCount = 0; + $reviewRule = $this->config->story->reviewRules; + $reviewerList = $this->dao->select('reviewer,result')->from(TABLE_STORYREVIEW)->where('story')->eq($storyID)->andWhere('version')->eq($oldStory->version)->fetchPairs('reviewer', 'result'); + foreach($reviewerList as $reviewer => $result) $passCount = $result == 'pass' ? $passCount + 1 : $passCount; + if($reviewRule == 'allpass' and $passCount == count($reviewerList)) $story->status = 'active'; + if($reviewRule == 'halfpass' and $passCount >= floor(count($reviewerList) / 2) + 1) $story->status = 'active'; + $this->dao->update(TABLE_STORY)->data($story) ->autoCheck() ->batchCheck($this->config->story->review->requiredFields, 'notempty') @@ -2532,13 +2567,16 @@ class storyModel extends model */ public function getUserStories($account, $type = 'assignedTo', $orderBy = 'id_desc', $pager = null, $storyType = 'story') { - $stories = $this->dao->select('t1.*, t2.name as productTitle')->from(TABLE_STORY)->alias('t1') - ->leftJoin(TABLE_PRODUCT)->alias('t2')->on('t1.product = t2.id') - ->where('t1.deleted')->eq(0) + $sql = $this->dao->select('t1.*, t2.name as productTitle')->from(TABLE_STORY)->alias('t1') + ->leftJoin(TABLE_PRODUCT)->alias('t2')->on('t1.product = t2.id'); + if($type == 'reviewBy') $sql = $sql->leftJoin(TABLE_STORYREVIEW)->alias('t3')->on('t1.id = t3.story'); + + $stories = $sql->where('t1.deleted')->eq(0) ->andWhere('t1.type')->eq($storyType) ->beginIF($type != 'closedBy' and $this->app->moduleName == 'block')->andWhere('t1.status')->ne('closed')->fi() ->beginIF($type != 'all') ->beginIF($type == 'assignedTo')->andWhere('assignedTo')->eq($account)->fi() + ->beginIF($type == 'reviewBy')->andWhere('t3.reviewer')->eq($account)->andWhere('t3.result')->eq('')->fi() ->beginIF($type == 'openedBy')->andWhere('openedBy')->eq($account)->fi() ->beginIF($type == 'reviewedBy')->andWhere("CONCAT(',', reviewedBy, ',')")->like("%,$account,%")->fi() ->beginIF($type == 'closedBy')->andWhere('closedBy')->eq($account)->fi() diff --git a/module/story/view/change.html.php b/module/story/view/change.html.php index a3c57110e2..add9531f91 100644 --- a/module/story/view/change.html.php +++ b/module/story/view/change.html.php @@ -26,7 +26,7 @@ story->reviewedBy;?>
- reviewedBy, 'class="form-control chosen" multiple');?> + story->checkForceReview()):?> story->needNotReview, '', "id='needNotReview' {$needReview}");?> diff --git a/module/story/view/create.html.php b/module/story/view/create.html.php index fc23f53d9b..406f7b12ff 100644 --- a/module/story/view/create.html.php +++ b/module/story/view/create.html.php @@ -104,7 +104,7 @@ >
- PO : '', "class='form-control chosen' multiple");?> + PO : '', "class='form-control chosen' multiple");?>
story->checkForceReview()):?>
diff --git a/module/story/view/review.html.php b/module/story/view/review.html.php index e58d1ea720..35b328bbd4 100644 --- a/module/story/view/review.html.php +++ b/module/story/view/review.html.php @@ -69,10 +69,6 @@ var assignedTo = 'lastEditedBy ? print($story->lastEditedBy) : pri status);?> printExtendFields($story, 'table');?> - - story->reviewedBy;?> - user->account, "class='form-control chosen' multiple data-placeholder='{$lang->story->chosen->reviewedBy}'");?> - story->comment;?>