diff --git a/module/story/control.php b/module/story/control.php index ccc7e7beb0..af045d1167 100644 --- a/module/story/control.php +++ b/module/story/control.php @@ -908,69 +908,65 @@ class story extends control * * @param int $storyID * @param string $type - * @param string $stories * @param string $browseType * @param int $param * @access public * @return void */ - public function linkStory($storyID, $type = '', $stories = '', $browseType = '', $param = 0) + public function linkStory($storyID, $type = 'linkStories', $browseType = '', $param = 0) { $this->commonAction($storyID); - /* Link stories. */ - if(!empty($_POST)) - { - $stories = $this->story->linkStories($storyID, $type, $stories); - if(isonlybody()) die(js::closeModal('parent.parent', '', "function(){parent.parent.loadLinkedStories('$storyID', '$type', '$stories')}")); - die(js::locate($this->createLink('story', 'edit', "storyID=$storyID"), 'parent')); - } - /* Get story, product, products, and queryID. */ $story = $this->story->getById($storyID); $products = $this->product->getPairs(); $queryID = ($browseType == 'bySearch') ? (int)$param : 0; + /* Link stories. */ + if(!empty($_POST)) + { + $this->story->linkStories($storyID, $type); + if(isonlybody()) die(js::closeModal('parent.parent', '', "function(){parent.parent.loadLinkedStories('$storyID', '$type')}")); + die(js::locate($this->createLink('story', 'edit', "storyID=$storyID"), 'parent')); + } + /* Build search form. */ - $actionURL = $this->createLink('story', 'linkStory', "storyID=$storyID&type=$type&stories=$stories&browseType=bySearch&queryID=myQueryID", '', true); + $actionURL = $this->createLink('story', 'linkStory', "storyID=$storyID&type=$type&browseType=bySearch&queryID=myQueryID", '', true); $this->loadModel('product')->buildSearchForm($story->product, $products, $queryID, $actionURL); - $this->loadModel('search')->setSearchParams($this->config->product->search); /* Get stories to link. */ - $allStories = array(); - if($browseType == 'bySearch') $allStories = $this->story->getBySearch($story->product, $queryID, 'id', null); + $stories2Link = $this->story->getStories2Link($storyID, $type, $browseType, $queryID); /* Assign. */ - $this->view->title = $this->lang->story->linkStory . "STORY" . $this->lang->colon .$this->lang->story->linkStory; - $this->view->position[] = $this->lang->story->linkStory; - $this->view->type = $type; - $this->view->allStories = $allStories; - $this->view->users = $this->loadModel('user')->getPairs('noletter'); - $this->view->actionURL = $this->config->product->search; + $this->view->title = $this->lang->story->linkStory . "STORY" . $this->lang->colon .$this->lang->story->linkStory; + $this->view->position[] = $this->lang->story->linkStory; + $this->view->type = $type; + $this->view->stories2Link = $stories2Link; + $this->view->users = $this->loadModel('user')->getPairs('noletter'); $this->display(); } /** - * AJAX: If type is linkStories, get related stories else get child stories. + * AJAX: if type is linkStories, get related stories else get child stories. * * @param int $storyID * @param string $type - * @param string $linkedStories * @access public * @return string */ - public function ajaxGetLinkedStories($storyID, $type = '', $linkedStories = '') + public function ajaxGetLinkedStories($storyID, $type = '') { - $stories = $this->story->getLinkedStories($linkedStories); - $story = $this->story->getById($storyID); + /* Get linked stories. */ + $stories = $this->story->getLinkedStories($storyID, $type); + /* Build linked stories list. */ $output = ''; foreach($stories as $storyId => $storyTitle) { $output .= '
  • '; - $output .= html::a(inlink('view', "storyID=$storyId"), "#$storyId " . $storyTitle); - $output .= html::a("javascript:deleteLinkedStory($storyID, \"$type\", $storyId)", '', '', "title='{$this->lang->unlink}' style='float:right'"); + $output .= html::a(inlink('view', "storyID=$storyId"), "#$storyId " . $storyTitle, '_blank'); + $output .= html::a("javascript:unlinkStory($storyID, \"$type\", $storyId)", '', '', "title='{$this->lang->unlink}' style='float:right'"); $output .= '
  • '; } @@ -978,25 +974,29 @@ class story extends control } /** - * AJAX: Delete linked story. + * AJAX: unlink story. * * @param int $storyID * @param string $type - * @param int $deleteStory + * @param int $story2Unlink * @access public * @return string */ - public function ajaxDeleteLinkedStory($storyID, $type = '', $deleteStory = 0) + public function ajaxUnlinkStory($storyID, $type = '', $story2Unlink = 0) { - $stories = $this->story->deleteLinkedStory($storyID, $type, $deleteStory); - $story = $this->story->getById($storyID); + /* Unlink related story if type is linkStories else unlink child story. */ + $this->story->unlinkStory($storyID, $type, $story2Unlink); - $output = ""; + /* Get linkStories if type is linkStories else get childStories. */ + $stories = $this->story->getLinkedStories($storyID, $type); + + /* Build linked stories list from. */ + $output = ''; foreach($stories as $storyId => $storyTitle) { $output .= '
  • '; - $output .= html::a(inlink('view', "storyID=$storyId"), "#$storyId " . $storyTitle); - $output .= html::a("javascript:deleteLinkedStory($storyID, \"$type\", $storyId)", '', '', "title='{$this->lang->unlink}' style='float:right'"); + $output .= html::a(inlink('view', "storyID=$storyId"), "#$storyId " . $storyTitle, '_blank'); + $output .= html::a("javascript:unlinkStory($storyID, \"$type\", $storyId)", '', '', "title='{$this->lang->unlink}' style='float:right'"); $output .= '
  • '; } diff --git a/module/story/css/linkstory.css b/module/story/css/linkstory.css index ae1bc8a0f2..3ce3480f99 100644 --- a/module/story/css/linkstory.css +++ b/module/story/css/linkstory.css @@ -1 +1 @@ -#linkStoryForm {min-height: 200px} +#linkStoryForm {min-height: 300px} diff --git a/module/story/js/edit.js b/module/story/js/edit.js index 3cb2e95edb..fd382520bf 100644 --- a/module/story/js/edit.js +++ b/module/story/js/edit.js @@ -1,17 +1,17 @@ /** - * Delete linked story. + * Unlink story. * * @param int $storyID * @param string $linkType - * @param int deleteStory + * @param int story2Unlink * @access public * @return void */ -function deleteLinkedStory(storyID, linkType, deleteStory) +function unlinkStory(storyID, linkType, story2Unlink) { - deleteLink = createLink('story', 'ajaxDeleteLinkedStory', 'storyID=' + storyID + '&type=' + linkType + '&deleteStory=' + deleteStory); - if(linkType == 'linkStories') $('#linkStoriesBox').load(deleteLink); - if(linkType == 'childStories') $('#childStoriesBox').load(deleteLink); + link = createLink('story', 'ajaxUnlinkStory', 'storyID=' + storyID + '&type=' + linkType + '&story2Unlink=' + story2Unlink); + if(linkType == 'linkStories') $('#linkStoriesBox').load(link); + if(linkType == 'childStories') $('#childStoriesBox').load(link); } /** @@ -19,13 +19,12 @@ function deleteLinkedStory(storyID, linkType, deleteStory) * * @param int $storyID * @param string $linkType - * @param string $linkedStories * @access public * @return void */ -function loadLinkedStories(storyID, linkType, linkedStories) +function loadLinkedStories(storyID, linkType) { - storyLink = createLink('story', 'ajaxGetLinkedStories', 'storyID=' + storyID + '&type=' + linkType + '&linkedStories=' + linkedStories); - if(linkType == 'linkStories') $('#linkStoriesBox').load(storyLink); + storyLink = createLink('story', 'ajaxGetLinkedStories', 'storyID=' + storyID + '&type=' + linkType); + if(linkType == 'linkStories') $('#linkStoriesBox').load(storyLink); if(linkType == 'childStories') $('#childStoriesBox').load(storyLink); } diff --git a/module/story/lang/en.php b/module/story/lang/en.php index 5a3b705dfb..390a78b67f 100644 --- a/module/story/lang/en.php +++ b/module/story/lang/en.php @@ -177,6 +177,7 @@ $lang->story->confirmDelete = "Are you sure to delete this story?"; $lang->story->confirmBatchClose = "Are you sure to close those stories?"; $lang->story->errorFormat = 'Error format'; $lang->story->errorEmptyTitle = "Title can't be empty"; +$lang->story->errorEmptyChildStory = '『Child story』can not be empty.'; $lang->story->mustChooseResult = 'Must choose s result'; $lang->story->mustChoosePreVersion = 'Must select an version to revert'; $lang->story->ajaxGetProjectStories = "API:{$lang->projectCommon} stories"; @@ -196,10 +197,10 @@ $lang->story->action->linked2plan = array('main' => '$date, linked to pl $lang->story->action->unlinkedfromplan = array('main' => '$date, removed from $extra> by $actor'); $lang->story->action->linked2project = array('main' => '$date, linked to ' . $lang->projectCommon . ' $extra by $actor.'); $lang->story->action->unlinkedfromproject = array('main' => '$date, removed from ' . $lang->projectCommon . ' $extra by $actor.'); -$lang->story->action->linkstories = array('main' => '$date, linked stories $extra by $actor.'); -$lang->story->action->childstories = array('main' => '$date, subdivied to $extra by $actor.'); -$lang->story->action->mvchildstories = array('main' => '$date, removed child stories $extra by $actor.'); -$lang->story->action->mvlinkstories = array('main' => '$date, removed link stories $extra by $actor.'); +$lang->story->action->linkrelatedstory = array('main' => '$date, link related story $extra by $actor.'); +$lang->story->action->subdividestory = array('main' => '$date, subdivide to $extra by $actor.'); +$lang->story->action->unlinkrelatedstory = array('main' => '$date, unlink related story $extra by $actor.'); +$lang->story->action->unlinkchildstory = array('main' => '$date, unlink child story $extra by $actor.'); /* Report*/ $lang->story->report = new stdclass(); diff --git a/module/story/lang/zh-cn.php b/module/story/lang/zh-cn.php index 4020a30272..48deedfab4 100644 --- a/module/story/lang/zh-cn.php +++ b/module/story/lang/zh-cn.php @@ -177,6 +177,7 @@ $lang->story->confirmDelete = "您确认删除该需求吗?"; $lang->story->confirmBatchClose = "您确认关闭这些需求吗?"; $lang->story->errorFormat = '需求数据有误'; $lang->story->errorEmptyTitle = '标题不能为空'; +$lang->story->errorEmptyChildStory = '『细分需求』不能为空。'; $lang->story->mustChooseResult = '必须选择评审结果'; $lang->story->mustChoosePreVersion = '必须选择回溯的版本'; $lang->story->ajaxGetProjectStories = "接口:获取{$lang->projectCommon}需求列表"; @@ -196,10 +197,10 @@ $lang->story->action->linked2plan = array('main' => '$date, 由 $lang->story->action->unlinkedfromplan = array('main' => '$date, 由 $actor 从计划 $extra 移除。'); $lang->story->action->linked2project = array('main' => '$date, 由 $actor 关联到' . $lang->projectCommon . ' $extra。'); $lang->story->action->unlinkedfromproject = array('main' => '$date, 由 $actor 从' . $lang->projectCommon . ' $extra 移除。'); -$lang->story->action->linkstories = array('main' => '$date, 由 $actor 关联相关需求 $extra。'); -$lang->story->action->childstories = array('main' => '$date, 由 $actor 细分为需求 $extra。'); -$lang->story->action->mvchildstories = array('main' => '$date, 由 $actor 移除细分需求 $extra。'); -$lang->story->action->mvlinkstories = array('main' => '$date, 由 $actor 移除相关需求 $extra。'); +$lang->story->action->linkrelatedstory = array('main' => '$date, 由 $actor 关联相关需求 $extra。'); +$lang->story->action->subdividestory = array('main' => '$date, 由 $actor 细分为需求 $extra。'); +$lang->story->action->unlinkrelatedstory = array('main' => '$date, 由 $actor 移除相关需求 $extra。'); +$lang->story->action->unlinkchildstory = array('main' => '$date, 由 $actor 移除细分需求 $extra。'); /* 统计报表。*/ $lang->story->report = new stdclass(); diff --git a/module/story/model.php b/module/story/model.php index cbf145e7f9..0087518777 100644 --- a/module/story/model.php +++ b/module/story/model.php @@ -426,9 +426,14 @@ class storyModel extends model ->setIF($this->post->closedReason != false and $this->post->closedBy == false, 'closedBy', $this->app->user->account) ->join('reviewedBy', ',') ->join('mailto', ',') - ->remove('files,labels,comment') + ->remove('linkStories,childStories,files,labels,comment') ->get(); if(is_array($story->plan)) $story->plan = trim(join(',', $story->plan), ','); + if($story->closedReason == 'subdivided' and $oldStory->childStories == '') + { + dao::$errors[] = $this->lang->story->errorEmptyChildStory; + return false; + } $this->dao->update(TABLE_STORY) ->data($story) @@ -1040,60 +1045,90 @@ class storyModel extends model * * @param int $storyID * @param string $type - * @param string $stories * @access public - * @return string + * @return void */ - public function linkStories($storyID, $type = 'linkStories', $stories = '') + public function linkStories($storyID, $type = 'linkStories') { - if($this->post->stories == false) return $stories; + if($this->post->stories == false) return; - $stories = implode(',', $this->post->stories) . ',' . trim($stories, ','); - $this->dao->update(TABLE_STORY)->set($type)->eq(trim($stories,','))->where('id')->eq($storyID)->exec(); + $story = $this->getById($storyID); + $stories2Link = $this->post->stories; + + $stories = implode(',', $stories2Link) . ',' . trim($story->$type, ','); + $this->dao->update(TABLE_STORY)->set($type)->eq(trim($stories, ','))->where('id')->eq($storyID)->exec(); if(dao::isError()) die(js::error(dao::getError())); - $this->loadModel('action')->create('story', $storyID, $type, '', implode(',', $this->post->stories)); - - return $stories; + $action = ($type == 'linkStories') ? 'linkRelatedStory' : 'subdivideStory'; + $this->loadModel('action')->create('story', $storyID, $action, '', implode(',', $stories2Link)); } /** - * Delete linked story. + * Get stories to link. * * @param int $storyID * @param string $type - * @param int $deleteStory + * @param string $browseType + * @param int $queryID * @access public * @return array */ - public function deleteLinkedStory($storyID, $type, $deleteStory) + public function getStories2Link($storyID, $type = 'linkStories', $browseType = 'bySearch', $queryID = 0) { - $story = $this->getById($storyID); - - $stories = explode(',', trim($story->$type, ',')); - foreach($stories as $key => $storyId) + if($browseType == 'bySearch') { - if($storyId == $deleteStory) unset($stories[$key]); + $story = $this->getById($storyID); + $stories2Link = $this->getBySearch($story->product, $queryID, 'id', null); + foreach($stories2Link as $key => $story2Link) + { + if($story2Link->id == $storyID) unset($stories2Link[$key]); + if(in_array($story2Link->id, explode(',', $story->$type))) unset($stories2Link[$key]); + } + return $stories2Link; } - $stories = implode(',', $stories); + else + { + return array(); + } + } + + /** + * Unlink story. + * + * @param int $storyID + * @param string $type + * @param int $story2Unlink + * @access public + * @return void + */ + public function unlinkStory($storyID, $type = 'linkStories', $story2Unlink = 0) + { + $story = $this->getById($storyID); + + $oldLinkedStories = explode(',', trim($story->$type, ',')); + foreach($oldLinkedStories as $key => $storyId) + { + if($storyId == $story2Unlink) unset($oldLinkedStories[$key]); + } + $stories = implode(',', $oldLinkedStories); $this->dao->update(TABLE_STORY)->set($type)->eq($stories)->where('id')->eq($storyID)->exec(); if(dao::isError()) die(js::error(dao::getError())); - $action = ($type == 'linkStories') ? 'mvLinkStories' : 'mvChildStories'; - $this->loadModel('action')->create('story', $storyID, $action, '', $deleteStory); - - return $this->getLinkedStories($stories); + $action = ($type == 'linkStories') ? 'unlinkRelatedStory' : 'unlinkChildStory'; + $this->loadModel('action')->create('story', $storyID, $action, '', $story2Unlink); } /** * Get linked stories. * - * @param string $stories + * @param string $storyID + * @param string $type * @access public * @return array */ - public function getLinkedStories($stories) + public function getLinkedStories($storyID, $type = 'linkStories') { - return $this->dao->select('id, title')->from(TABLE_STORY)->where('id')->in($stories)->fetchPairs(); + $story = $this->getById($storyID); + return $this->dao->select('id, title')->from(TABLE_STORY)->where('id')->in($story->$type)->fetchPairs(); } /** diff --git a/module/story/view/edit.html.php b/module/story/view/edit.html.php index 0c88c69103..d1ac368f6f 100644 --- a/module/story/view/edit.html.php +++ b/module/story/view/edit.html.php @@ -156,7 +156,7 @@ story->closedReason;?> - story->reasonList, $story->closedReason, 'class="form-control"');?> + story->reasonList, $story->closedReason, "class='form-control' onchange='setStory(this.value)'");?> @@ -165,14 +165,16 @@
    story->legendMisc;?> - + status == 'closed'):?> + + - + + status == 'closed'):?> +
    story->duplicateStory;?> duplicateStory, "class='form-control'");?>
    story->linkStories;?>story->linkStories;?> - createLink('story', 'linkStory', "storyID=$story->id&type=linkStories&stories=$story->linkStories", '', true), $lang->story->linkStory, '', "data-toggle='modal' data-type='iframe' data-width='85%'");?> + createLink('story', 'linkStory', "storyID=$story->id&type=linkStories", '', true), $lang->story->linkStory, '', "data-toggle='modal' data-type='iframe' data-width='95%'");?>
      linkStories); @@ -181,8 +183,8 @@ if(isset($story->extraStories[$linkStoryID])) { echo '
    • '; - echo html::a(inlink('view', "storyID=$linkStoryID"), "#$linkStoryID " . $story->extraStories[$linkStoryID]); - echo html::a("javascript:deleteLinkedStory($story->id, \"linkStories\", $linkStoryID)", '', '', "title='{$lang->unlink}' style='float:right'"); + echo html::a(inlink('view', "storyID=$linkStoryID"), "#$linkStoryID " . $story->extraStories[$linkStoryID], '_blank'); + echo html::a("javascript:unlinkStory($story->id, \"linkStories\", $linkStoryID)", '', '', "title='{$lang->unlink}' style='float:right'"); echo '
    • '; } } @@ -190,10 +192,11 @@
    story->childStories;?> - createLink('story', 'linkStory', "storyID=$story->id&type=childStories&stories=$story->childStories", '', true), $lang->story->linkStory, '', "data-toggle='modal' data-type='iframe' data-width='85%'");?> + createLink('story', 'linkStory', "storyID=$story->id&type=childStories", '', true), $lang->story->linkStory, '', "data-toggle='modal' data-type='iframe' data-width='95%'");?>
      childStories); @@ -202,8 +205,8 @@ if(isset($story->extraStories[$childStoryID])) { echo '
    • '; - echo html::a(inlink('view', "storyID=$childStoryID"), "#$childStoryID" . $story->extraStories[$childStoryID]); - echo html::a("javascript:deleteLinkedStory($story->id, \"childStories\", $childStoryID)", '', '', "title='{$lang->unlink}' style='float:right'"); + echo html::a(inlink('view', "storyID=$childStoryID"), "#$childStoryID" . $story->extraStories[$childStoryID], '_blank'); + echo html::a("javascript:unlinkStory($story->id, \"childStories\", $childStoryID)", '', '', "title='{$lang->unlink}' style='float:right'"); echo '
    • '; } } @@ -211,6 +214,7 @@
    diff --git a/module/story/view/linkstory.html.php b/module/story/view/linkstory.html.php index 08626708fb..9d77400cf2 100644 --- a/module/story/view/linkstory.html.php +++ b/module/story/view/linkstory.html.php @@ -22,7 +22,7 @@
    - + @@ -36,21 +36,19 @@ - - id, explode(',', $story->$type))) continue;?> - id == $story->id) continue;?> - createLink('story', 'view', "storyID=$storyDetail->id");?> + + createLink('story', 'view', "storyID=$story2Link->id");?> - - - - - - + + + + + + @@ -63,6 +61,9 @@ + + +
    idAB;?>
    - - id));?> + + id));?> story->priList, $storyDetail->pri, $storyDetail->pri)?>'>story->priList, $storyDetail->pri, $storyDetail->pri);?>createLink('product', 'browse', "productID=$storyDetail->product&branch=$storyDetail->branch"), $products[$storyDetail->product], '_blank');?>title, '_blank');?>planTitle;?>openedBy];?>estimate;?>story->priList, $story2Link->pri, $story2Link->pri)?>'>story->priList, $story2Link->pri, $story2Link->pri);?>createLink('product', 'browse', "productID=$story2Link->product&branch=$story2Link->branch"), $products[$story2Link->product], '_blank');?>title, '_blank');?>planTitle;?>openedBy];?>estimate;?>