diff --git a/module/story/config.php b/module/story/config.php index b5d3c30e7c..fd76c42a38 100644 --- a/module/story/config.php +++ b/module/story/config.php @@ -53,3 +53,120 @@ $config->story->custom->batchCreateFields = 'module,plan,spec,pri,estimate,revie $config->story->custom->batchEditFields = 'branch,module,plan,estimate,pri,source,stage,closedBy,closedReason'; $config->story->excludeCheckFields = ',uploadImage,category,reviewer,reviewDitto,lanes,regions,branch,pri,'; + +global $app, $lang; +$config->story->actionList = array(); +$config->story->actionList['change']['icon'] = 'alert'; +$config->story->actionList['change']['text'] = $lang->story->change; +$config->story->actionList['change']['hint'] = $lang->story->change; +$config->story->actionList['change']['url'] = array('module' => 'story', 'method' => 'change', 'params' => 'storyID={id}'); +$config->story->actionList['change']['data-app'] = $app->tab; + +$config->story->actionList['submitReview']['icon'] = 'confirm'; +$config->story->actionList['submitReview']['text'] = $lang->story->submitReview; +$config->story->actionList['submitReview']['hint'] = $lang->story->submitReview; +$config->story->actionList['submitReview']['url'] = array('module' => 'story', 'method' => 'submitReview', 'params' => 'storyID={id}&storyType={type}'); +$config->story->actionList['submitReview']['data-toggle'] = 'modal'; + +$config->story->actionList['recall']['icon'] = 'undo'; +$config->story->actionList['recall']['text'] = $lang->story->recall; +$config->story->actionList['recall']['hint'] = $lang->story->recall; +$config->story->actionList['recall']['url'] = array('module' => 'story', 'method' => 'recall', 'params' => 'storyID={id}&from=view&confirm=no&storyType={type}'); +$config->story->actionList['recall']['data-app'] = $app->tab; +$config->story->actionList['recall']['className'] = 'ajax-submit'; + +$config->story->actionList['recallChange']['icon'] = 'undo'; +$config->story->actionList['recallChange']['text'] = $lang->story->recallChange; +$config->story->actionList['recallChange']['hint'] = $lang->story->recallChange; +$config->story->actionList['recallChange']['url'] = array('module' => 'story', 'method' => 'recall', 'params' => 'storyID={id}&from=view&confirm=no&storyType={type}'); +$config->story->actionList['recallChange']['data-app'] = $app->tab; +$config->story->actionList['recallChange']['className'] = 'ajax-submit'; + +$config->story->actionList['review']['icon'] = 'alert'; +$config->story->actionList['review']['text'] = $lang->story->review; +$config->story->actionList['review']['hint'] = $lang->story->review; +$config->story->actionList['review']['url'] = array('module' => 'story', 'method' => 'review', 'params' => 'storyID={id}&from=' . $app->tab . '&storyType={type}'); +$config->story->actionList['review']['data-app'] = $app->tab; + +$config->story->actionList['subdivide']['icon'] = 'split'; +$config->story->actionList['subdivide']['text'] = $lang->story->subdivide; +$config->story->actionList['subdivide']['hint'] = $lang->story->subdivide; +$config->story->actionList['subdivide']['url'] = array('module' => 'story', 'method' => 'batchCreate', 'params' => 'productID={product}&branch={branch}&moduleID={module}&storyID={id}&executionID={executionID}&plan=0&storyType=story'); +$config->story->actionList['subdivide']['data-app'] = $app->tab; + +$config->story->actionList['assignTo']['icon'] = 'hand-right'; +$config->story->actionList['assignTo']['text'] = $lang->story->assignTo; +$config->story->actionList['assignTo']['hint'] = $lang->story->assignTo; +$config->story->actionList['assignTo']['url'] = array('module' => 'story', 'method' => 'assignTo', 'params' => 'storyID={id}&kanbanGroup=default&from=&storyType={type}'); +$config->story->actionList['assignTo']['data-toggle'] = 'modal'; + +$config->story->actionList['close']['icon'] = 'off'; +$config->story->actionList['close']['text'] = $lang->story->close; +$config->story->actionList['close']['hint'] = $lang->story->close; +$config->story->actionList['close']['url'] = array('module' => 'story', 'method' => 'close', 'params' => 'storyID={id}&from=&storyType={type}'); +$config->story->actionList['close']['data-toggle'] = 'modal'; + +$config->story->actionList['activate']['icon'] = 'magic'; +$config->story->actionList['activate']['text'] = $lang->story->activate; +$config->story->actionList['activate']['hint'] = $lang->story->activate; +$config->story->actionList['activate']['url'] = array('module' => 'story', 'method' => 'activate', 'params' => 'storyID={id}&storyType={type}'); +$config->story->actionList['activate']['data-toggle'] = 'modal'; + +$config->story->actionList['importToLib']['icon'] = 'assets'; +$config->story->actionList['importToLib']['text'] = $lang->story->importToLib; +$config->story->actionList['importToLib']['hint'] = $lang->story->importToLib; +$config->story->actionList['importToLib']['url'] = '#importToLib'; +$config->story->actionList['importToLib']['data-toggle'] = 'modal'; + +$app->loadLang('testcase'); +$config->story->actionList['createTestcase']['text'] = $lang->story->create; +$config->story->actionList['createTestcase']['hint'] = $lang->story->create; +$config->story->actionList['createTestcase']['url'] = array('module' => 'testcase', 'method' => 'create', 'params' => 'product={product}&branch={branch}&moduleID=0&from=¶m=0&storyid={id}'); +$config->story->actionList['createTestcase']['data-size'] = 'lg'; +$config->story->actionList['createTestcase']['data-toggle'] = 'modal'; + +$config->story->actionList['batchCreateTestcase']['text'] = $lang->story->batchCreate; +$config->story->actionList['batchCreateTestcase']['hint'] = $lang->story->batchCreate; +$config->story->actionList['batchCreateTestcase']['url'] = array('module' => 'testcase', 'method' => 'batchCreate', 'params' => 'product={product}&branch={branch}&moduleID=0&storyid={id}'); +$config->story->actionList['batchCreateTestcase']['data-size'] = 'lg'; +$config->story->actionList['batchCreateTestcase']['data-toggle'] = 'modal'; + +$config->story->actionList['testcase']['type'] = 'dropdown'; +$config->story->actionList['testcase']['text'] = $lang->testcase->common; +$config->story->actionList['testcase']['hint'] = $lang->testcase->common; +$config->story->actionList['testcase']['caret'] = 'up'; +$config->story->actionList['testcase']['placement'] = 'top-end'; +$config->story->actionList['testcase']['items'] = array('createTestcase', 'batchCreateTestcase'); + +$app->loadLang('task'); +$config->story->actionList['createTask']['icon'] = 'plus'; +$config->story->actionList['createTask']['text'] = $lang->task->create; +$config->story->actionList['createTask']['hint'] = $lang->task->create; +$config->story->actionList['createTask']['url'] = array('module' => 'task', 'method' => 'create', 'params' => 'executionID={execution}&storyID={id}&moduleID={module}'); +$config->story->actionList['createTask']['data-size'] = 'lg'; +$config->story->actionList['createTask']['data-toggle'] = 'modal'; + +$config->story->actionList['edit']['icon'] = 'edit'; +$config->story->actionList['edit']['text'] = $lang->story->edit; +$config->story->actionList['edit']['hint'] = $lang->story->edit; +$config->story->actionList['edit']['url'] = array('module' => 'story', 'method' => 'edit', 'params' => 'storyID={id}&kanbanGroup=default&storyType={type}'); +$config->story->actionList['edit']['data-app'] = $app->tab; +$config->story->actionList['edit']['notInModal'] = true; + +$config->story->actionList['copy']['icon'] = 'copy'; +$config->story->actionList['copy']['text'] = $lang->story->copy; +$config->story->actionList['copy']['hint'] = $lang->story->copy; +$config->story->actionList['copy']['url'] = array('module' => 'story', 'method' => 'create', 'params' => 'product={product}&branch={branch}&moduleID={module}&storyID={id}&executionID=0&bugID=0&planID=0&todoID=0&extra=&storyType={type}'); +$config->story->actionList['copy']['data-app'] = $app->tab; +$config->story->actionList['copy']['notInModal'] = true; + +$config->story->actionList['delete']['icon'] = 'trash'; +$config->story->actionList['delete']['text'] = $lang->task->delete; +$config->story->actionList['delete']['hint'] = $lang->task->delete; +$config->story->actionList['delete']['url'] = array('module' => 'story', 'method' => 'delete', 'params' => 'storyID={id}'); +$config->story->actionList['delete']['className'] = 'ajax-submit'; + +$config->story->actions = new stdclass(); +$config->story->actions->view = array(); +$config->story->actions->view['mainActions'] = array('change', 'submitReview', 'recall', 'recallChange', 'review', 'subdivide', 'assignTo', 'close', 'activate', 'importToLib', 'testcase', 'createTask'); +$config->story->actions->view['suffixActions'] = array('edit', 'copy', 'delete'); diff --git a/module/story/model.php b/module/story/model.php index 42160c3ca0..af559f5aab 100644 --- a/module/story/model.php +++ b/module/story/model.php @@ -3157,14 +3157,30 @@ class storyModel extends model global $app, $config; $action = strtolower($action); - if($action == 'recall') return strpos('reviewing,changing', $story->status) !== false; - if($action == 'close') return $story->status != 'closed'; - if($action == 'activate') return $story->status == 'closed'; - if($action == 'assignto') return $story->status != 'closed'; - if($action == 'batchcreate' and $story->parent > 0) return false; - if($action == 'batchcreate' and !empty($story->twins)) return false; - if($action == 'batchcreate' and $story->type == 'requirement' and $story->status != 'closed') return strpos('draft,reviewing,changing', $story->status) === false; - if($action == 'submitreview' and strpos('draft,changing', $story->status) === false) return false; + if($action == 'recallchange') return $story->status == 'changing'; + if($action == 'recall') return $story->status == 'reviewing'; + if($action == 'close') return $story->status != 'closed'; + if($action == 'activate') return $story->status == 'closed'; + if($action == 'assignto') return $story->status != 'closed'; + if($action == 'batchcreate' && $story->parent > 0) return false; + if($action == 'batchcreate' && !empty($story->twins)) return false; + if($action == 'submitreview' && strpos('draft,changing', $story->status) === false) return false; + if($action == 'subdivide' && (!helper::isAjaxRequest('modal') || $config->vision != 'lite')) return false; + if($action == 'batchcreate' && $story->type == 'requirement' && $story->status != 'closed') return strpos('draft,reviewing,changing', $story->status) === false; + if($action == 'createtestcase' || $action == 'batchcreatetestcase') return $config->vision != 'lite' && $story->parent >= 0 && $story->type != 'requirement'; + + if($action == 'createtask') + { + if($app->tab == 'project' && !empty($_SESSION['project'])) + { + global $dao; + $project = $dao->findByID($_SESSION['project'])->from(TABLE_PROJECT)->fetch(); + } + return ($app->tab == 'execution' || (!empty($project) && $project->multiple == '0')) && $story->status == 'active' && $story->type == 'story'; + } + + $disabledFeatures = ",{$config->disabledFeatures},"; + if($action == 'importToLib') return in_array($config->edition, array('max', 'ipd')) && $app->tab == 'project' && common::hasPriv('story', 'importToLib') && strpos($disabledFeatures, ',assetlibStorylib,') === false && strpos($disabledFeatures, ',assetlib,') === false; static $shadowProducts = array(); static $taskGroups = array(); @@ -3176,7 +3192,7 @@ class storyModel extends model foreach($stmt as $row) $shadowProducts[$row->id] = $row->id; } - if($hasShadow and empty($taskGroups[$story->id])) $taskGroups[$story->id] = $app->dbQuery('SELECT id FROM ' . TABLE_TASK . " WHERE story = $story->id")->fetch(); + if($hasShadow && empty($taskGroups[$story->id])) $taskGroups[$story->id] = $app->dbQuery('SELECT id FROM ' . TABLE_TASK . " WHERE story = $story->id")->fetch(); if(isset($story->parent) && $story->parent < 0 && strpos($config->story->list->actionsOperatedParentStory, ",$action,") === false) return false; @@ -3193,8 +3209,8 @@ class storyModel extends model $story->notReview = isset($story->notReview) ? $story->notReview : array(); $isSuperReviewer = strpos(',' . trim(zget($config->story, 'superReviewers', ''), ',') . ',', ',' . $app->user->account . ','); - if($action == 'change') return (($isSuperReviewer !== false or count($story->reviewer) == 0 or count($story->notReview) == 0) and $story->status == 'active'); - if($action == 'review') return (($isSuperReviewer !== false or in_array($app->user->account, $story->notReview)) and $story->status == 'reviewing'); + if($action == 'change') return (($isSuperReviewer !== false || count($story->reviewer) == 0 || count($story->notReview) == 0) && $story->status == 'active'); + if($action == 'review') return (($isSuperReviewer !== false || in_array($app->user->account, $story->notReview)) && $story->status == 'reviewing'); return true; } diff --git a/module/story/ui/view.html.php b/module/story/ui/view.html.php index 51ed4438b6..a327a04562 100644 --- a/module/story/ui/view.html.php +++ b/module/story/ui/view.html.php @@ -17,9 +17,6 @@ include($this->app->getModuleRoot() . 'ai/ui/promptmenu.html.php'); $isInModal = isInModal(); $isRequirement = $story->type == 'requirement'; $isStoryType = $story->type == 'story'; -$actions = $this->story->buildOperateMenu($story, 'view', $project ? $project : null); -if($story->deleted) $actions = array(); -if($isInModal) $actions = false; /* 版本列表。Version list. */ $versions = array(); @@ -173,6 +170,10 @@ $versionBtn = count($versions) > 1 ? to::title(dropdown set::items($versions) )) : null; +$actions = $story->deleted || $isInModal ? array() : $this->loadModel('common')->buildOperateMenu($story); +$hasDivider = !empty($actions['mainActions']) && !empty($actions['suffixActions']); +$actions = array_merge($actions['mainActions'], array(array('type' => 'divider')), $actions['suffixActions']); +if(!$hasDivider) unset($actions['type']); detail ( set::objectType('story'),