Merge branch 'ztflow-sunguangming-main#sunguangming' of zentao/zentaopms (#576)
This commit is contained in:
@@ -68,7 +68,7 @@ class storiesEntry extends entry
|
||||
if(!$productID and isset($this->requestBody->product)) $productID = $this->requestBody->product;
|
||||
if(!$productID) return $this->sendError(400, 'Need product id.');
|
||||
|
||||
$fields = 'title,spec,verify,module,reviewer,type,parent,moduleOptionMenu,source,sourceNote,category,pri,estimate,mailto,keywords,notifyemail,uid,URS,status';
|
||||
$fields = 'title,spec,verify,module,reviewer,type,parent,moduleOptionMenu,source,sourceNote,category,pri,estimate,mailto,keywords,notifyemail,uid,status';
|
||||
$this->batchSetPost($fields);
|
||||
|
||||
$this->setPost('plans', array($this->request('plan', 0)));
|
||||
@@ -85,7 +85,7 @@ class storiesEntry extends entry
|
||||
$control = $this->loadController('story', 'create');
|
||||
$this->requireFields('title,spec,pri,category');
|
||||
|
||||
$control->create($productID, $this->param('branch', 0), $this->param('moduleID', 0), $this->param('storyID', 0), $this->param('objectID', 0), $this->param('bugID', 0));
|
||||
$control->create($productID, $this->param('branch', 0), $this->param('moduleID', 0), $this->param('storyID', 0), $this->param('objectID', 0), $this->param('bugID', 0), 0, 0, '', $this->param('type', 'story'));
|
||||
|
||||
$data = $this->getData();
|
||||
if(isset($data->status) and $data->status == 'fail') return $this->sendError(zget($data, 'code', 400), $data->message);
|
||||
|
||||
@@ -20,7 +20,7 @@ class storyCloseEntry extends entry
|
||||
*/
|
||||
public function post($storyID)
|
||||
{
|
||||
$fields = 'closedReason,duplicateStory,childStories,comment';
|
||||
$fields = 'closedReason,duplicateStory,comment';
|
||||
$this->batchSetPost($fields);
|
||||
|
||||
$control = $this->loadController('story', 'close');
|
||||
|
||||
+138
-120
@@ -46,6 +46,8 @@ $filter->product = new stdclass();
|
||||
$filter->branch = new stdclass();
|
||||
$filter->qa = new stdclass();
|
||||
$filter->story = new stdclass();
|
||||
$filter->epic = new stdclass();
|
||||
$filter->requirement = new stdclass();
|
||||
$filter->task = new stdclass();
|
||||
$filter->execution = new stdclass();
|
||||
$filter->testcase = new stdclass();
|
||||
@@ -77,121 +79,126 @@ $filter->projectplan = new stdclass();
|
||||
$filter->kanban = new stdclass();
|
||||
$filter->group = new stdclass();
|
||||
|
||||
$filter->index->index = new stdclass();
|
||||
$filter->block->default = new stdclass();
|
||||
$filter->block->create = new stdclass();
|
||||
$filter->block->edit = new stdclass();
|
||||
$filter->my->work = new stdclass();
|
||||
$filter->my->contribute = new stdclass();
|
||||
$filter->bug->batchcreate = new stdclass();
|
||||
$filter->bug->browse = new stdclass();
|
||||
$filter->bug->default = new stdclass();
|
||||
$filter->bug->create = new stdclass();
|
||||
$filter->bug->export = new stdclass();
|
||||
$filter->bug->ajaxgetproductcases = new stdclass();
|
||||
$filter->bug->ajaxgetproductbugs = new stdclass();
|
||||
$filter->caselib->create = new stdclass();
|
||||
$filter->doc->create = new stdclass();
|
||||
$filter->doc->browse = new stdclass();
|
||||
$filter->doc->view = new stdclass();
|
||||
$filter->doc->tablecontents = new stdclass();
|
||||
$filter->doc->productspace = new stdclass();
|
||||
$filter->doc->projectspace = new stdclass();
|
||||
$filter->doc->showfiles = new stdclass();
|
||||
$filter->doc->default = new stdclass();
|
||||
$filter->api->default = new stdClass();
|
||||
$filter->api->index = new stdClass();
|
||||
$filter->api->create = new stdClass();
|
||||
$filter->api->edit = new stdClass();
|
||||
$filter->mail->ztcloud = new stdclass();
|
||||
$filter->mail->batchdelete = new stdclass();
|
||||
$filter->misc->checkupdate = new stdclass();
|
||||
$filter->file->download = new stdclass();
|
||||
$filter->product->browse = new stdclass();
|
||||
$filter->product->all = new stdclass();
|
||||
$filter->product->default = new stdclass();
|
||||
$filter->product->index = new stdclass();
|
||||
$filter->product->export = new stdclass();
|
||||
$filter->product->project = new stdclass();
|
||||
$filter->product->roadmap = new stdclass();
|
||||
$filter->product->dynamic = new stdclass();
|
||||
$filter->branch->default = new stdclass();
|
||||
$filter->program->default = new stdclass();
|
||||
$filter->program->pgmproject = new stdclass();
|
||||
$filter->program->prjbrowse = new stdclass();
|
||||
$filter->program->project = new stdclass();
|
||||
$filter->program->product = new stdclass();
|
||||
$filter->program->browse = new stdclass();
|
||||
$filter->program->export = new stdclass();
|
||||
$filter->program->pgmbrowse = new stdclass();
|
||||
$filter->program->export = new stdclass();
|
||||
$filter->program->ajaxgetdropmenu = new stdclass();
|
||||
$filter->project->default = new stdclass();
|
||||
$filter->project->browse = new stdclass();
|
||||
$filter->project->story = new stdclass();
|
||||
$filter->project->export = new stdclass();
|
||||
$filter->project->task = new stdclass();
|
||||
$filter->project->execution = new stdclass();
|
||||
$filter->project->testcase = new stdclass();
|
||||
$filter->projectstory->story = new stdclass();
|
||||
$filter->qa->default = new stdclass();
|
||||
$filter->story->create = new stdclass();
|
||||
$filter->story->export = new stdclass();
|
||||
$filter->story->batchcreate = new stdclass();
|
||||
$filter->story->track = new stdclass();
|
||||
$filter->sso->getbindusers = new stdclass();
|
||||
$filter->sso->gettodolist = new stdclass();
|
||||
$filter->sso->getuserpairs = new stdclass();
|
||||
$filter->sso->login = new stdclass();
|
||||
$filter->sso->logout = new stdclass();
|
||||
$filter->git->cat = new stdclass();
|
||||
$filter->git->diff = new stdclass();
|
||||
$filter->svn->cat = new stdclass();
|
||||
$filter->svn->diff = new stdclass();
|
||||
$filter->task->create = new stdclass();
|
||||
$filter->task->export = new stdclass();
|
||||
$filter->task->recordestimate = new stdclass();
|
||||
$filter->execution->default = new stdclass();
|
||||
$filter->execution->story = new stdclass();
|
||||
$filter->testcase->default = new stdclass();
|
||||
$filter->testcase->create = new stdclass();
|
||||
$filter->testcase->browse = new stdclass();
|
||||
$filter->testcase->export = new stdclass();
|
||||
$filter->testcase->groupcase = new stdclass();
|
||||
$filter->testreport->default = new stdclass();
|
||||
$filter->testsuite->default = new stdclass();
|
||||
$filter->testsuite->library = new stdclass();
|
||||
$filter->testtask->default = new stdclass();
|
||||
$filter->testtask->browse = new stdclass();
|
||||
$filter->testtask->create = new stdclass();
|
||||
$filter->testtask->cases = new stdclass();
|
||||
$filter->todo->export = new stdclass();
|
||||
$filter->upgrade->license = new stdclass();
|
||||
$filter->user->login = new stdclass();
|
||||
$filter->user->edit = new stdclass();
|
||||
$filter->user->export = new stdclass();
|
||||
$filter->webhook->bind = new stdclass();
|
||||
$filter->user->ajaxgetmore = new stdclass();
|
||||
$filter->user->export = new stdclass();
|
||||
$filter->repo->ajaxsynccommit = new stdclass();
|
||||
$filter->repo->apigetrepobyurl = new stdclass();
|
||||
$filter->search->index = new stdclass();
|
||||
$filter->gitlab->webhook = new stdclass();
|
||||
$filter->gitlab->importissue = new stdclass();
|
||||
$filter->mr->diff = new stdclass();
|
||||
$filter->mr->browse = new stdclass();
|
||||
$filter->ci->checkCompileStatus = new stdclass();
|
||||
$filter->execution->export = new stdclass();
|
||||
$filter->tree->browse = new stdclass();
|
||||
$filter->productplan->browse = new stdclass();
|
||||
$filter->projectplan->browse = new stdclass();
|
||||
$filter->kanban->space = new stdclass();
|
||||
$filter->execution->kanban = new stdclass();
|
||||
$filter->execution->all = new stdclass();
|
||||
$filter->execution->tree = new stdclass();
|
||||
$filter->group->editmanagepriv = new stdclass();
|
||||
$filter->group->default = new stdclass();
|
||||
$filter->caselib->default = new stdclass();
|
||||
$filter->index->index = new stdclass();
|
||||
$filter->block->default = new stdclass();
|
||||
$filter->block->create = new stdclass();
|
||||
$filter->block->edit = new stdclass();
|
||||
$filter->my->work = new stdclass();
|
||||
$filter->my->contribute = new stdclass();
|
||||
$filter->bug->batchcreate = new stdclass();
|
||||
$filter->bug->browse = new stdclass();
|
||||
$filter->bug->default = new stdclass();
|
||||
$filter->bug->create = new stdclass();
|
||||
$filter->bug->export = new stdclass();
|
||||
$filter->bug->ajaxgetproductcases = new stdclass();
|
||||
$filter->bug->ajaxgetproductbugs = new stdclass();
|
||||
$filter->caselib->create = new stdclass();
|
||||
$filter->doc->create = new stdclass();
|
||||
$filter->doc->browse = new stdclass();
|
||||
$filter->doc->view = new stdclass();
|
||||
$filter->doc->tablecontents = new stdclass();
|
||||
$filter->doc->productspace = new stdclass();
|
||||
$filter->doc->projectspace = new stdclass();
|
||||
$filter->doc->showfiles = new stdclass();
|
||||
$filter->doc->default = new stdclass();
|
||||
$filter->api->default = new stdClass();
|
||||
$filter->api->index = new stdClass();
|
||||
$filter->api->create = new stdClass();
|
||||
$filter->api->edit = new stdClass();
|
||||
$filter->mail->ztcloud = new stdclass();
|
||||
$filter->mail->batchdelete = new stdclass();
|
||||
$filter->misc->checkupdate = new stdclass();
|
||||
$filter->file->download = new stdclass();
|
||||
$filter->product->browse = new stdclass();
|
||||
$filter->product->all = new stdclass();
|
||||
$filter->product->default = new stdclass();
|
||||
$filter->product->index = new stdclass();
|
||||
$filter->product->export = new stdclass();
|
||||
$filter->product->project = new stdclass();
|
||||
$filter->product->roadmap = new stdclass();
|
||||
$filter->product->dynamic = new stdclass();
|
||||
$filter->branch->default = new stdclass();
|
||||
$filter->program->default = new stdclass();
|
||||
$filter->program->pgmproject = new stdclass();
|
||||
$filter->program->prjbrowse = new stdclass();
|
||||
$filter->program->project = new stdclass();
|
||||
$filter->program->product = new stdclass();
|
||||
$filter->program->browse = new stdclass();
|
||||
$filter->program->export = new stdclass();
|
||||
$filter->program->pgmbrowse = new stdclass();
|
||||
$filter->program->export = new stdclass();
|
||||
$filter->program->ajaxgetdropmenu = new stdclass();
|
||||
$filter->project->default = new stdclass();
|
||||
$filter->project->browse = new stdclass();
|
||||
$filter->project->story = new stdclass();
|
||||
$filter->project->export = new stdclass();
|
||||
$filter->project->task = new stdclass();
|
||||
$filter->project->execution = new stdclass();
|
||||
$filter->project->testcase = new stdclass();
|
||||
$filter->projectstory->story = new stdclass();
|
||||
$filter->qa->default = new stdclass();
|
||||
$filter->story->create = new stdclass();
|
||||
$filter->story->export = new stdclass();
|
||||
$filter->story->batchchangeparent = new stdclass();
|
||||
$filter->story->batchcreate = new stdclass();
|
||||
$filter->story->track = new stdclass();
|
||||
$filter->epic->batchchangeparent = new stdclass();
|
||||
$filter->epic->export = new stdclass();
|
||||
$filter->sso->getbindusers = new stdclass();
|
||||
$filter->sso->gettodolist = new stdclass();
|
||||
$filter->sso->getuserpairs = new stdclass();
|
||||
$filter->sso->login = new stdclass();
|
||||
$filter->sso->logout = new stdclass();
|
||||
$filter->git->cat = new stdclass();
|
||||
$filter->git->diff = new stdclass();
|
||||
$filter->svn->cat = new stdclass();
|
||||
$filter->svn->diff = new stdclass();
|
||||
$filter->task->create = new stdclass();
|
||||
$filter->task->export = new stdclass();
|
||||
$filter->task->recordestimate = new stdclass();
|
||||
$filter->execution->default = new stdclass();
|
||||
$filter->execution->story = new stdclass();
|
||||
$filter->testcase->default = new stdclass();
|
||||
$filter->testcase->create = new stdclass();
|
||||
$filter->testcase->browse = new stdclass();
|
||||
$filter->testcase->export = new stdclass();
|
||||
$filter->testcase->groupcase = new stdclass();
|
||||
$filter->testreport->default = new stdclass();
|
||||
$filter->testsuite->default = new stdclass();
|
||||
$filter->testsuite->library = new stdclass();
|
||||
$filter->testtask->default = new stdclass();
|
||||
$filter->testtask->browse = new stdclass();
|
||||
$filter->testtask->create = new stdclass();
|
||||
$filter->testtask->cases = new stdclass();
|
||||
$filter->todo->export = new stdclass();
|
||||
$filter->upgrade->license = new stdclass();
|
||||
$filter->user->login = new stdclass();
|
||||
$filter->user->edit = new stdclass();
|
||||
$filter->user->export = new stdclass();
|
||||
$filter->webhook->bind = new stdclass();
|
||||
$filter->user->ajaxgetmore = new stdclass();
|
||||
$filter->user->export = new stdclass();
|
||||
$filter->repo->ajaxsynccommit = new stdclass();
|
||||
$filter->repo->apigetrepobyurl = new stdclass();
|
||||
$filter->search->index = new stdclass();
|
||||
$filter->gitlab->webhook = new stdclass();
|
||||
$filter->gitlab->importissue = new stdclass();
|
||||
$filter->mr->diff = new stdclass();
|
||||
$filter->mr->browse = new stdclass();
|
||||
$filter->ci->checkCompileStatus = new stdclass();
|
||||
$filter->execution->export = new stdclass();
|
||||
$filter->tree->browse = new stdclass();
|
||||
$filter->productplan->browse = new stdclass();
|
||||
$filter->projectplan->browse = new stdclass();
|
||||
$filter->kanban->space = new stdclass();
|
||||
$filter->execution->kanban = new stdclass();
|
||||
$filter->execution->all = new stdclass();
|
||||
$filter->execution->tree = new stdclass();
|
||||
$filter->group->editmanagepriv = new stdclass();
|
||||
$filter->group->default = new stdclass();
|
||||
$filter->caselib->default = new stdclass();
|
||||
$filter->requirement->batchchangeparent = new stdclass();
|
||||
$filter->requirement->export = new stdclass();
|
||||
|
||||
$filter->index->index->get['open'] = 'reg::base64';
|
||||
|
||||
@@ -201,6 +208,7 @@ $filter->my->work->cookie['pagerMyStory'] = 'int';
|
||||
$filter->my->work->cookie['pagerMyBug'] = 'int';
|
||||
$filter->my->work->cookie['pagerMyTestcase'] = 'int';
|
||||
$filter->my->work->cookie['pagerMyTesttask'] = 'int';
|
||||
$filter->my->work->cookie['storyViewType'] = 'code';
|
||||
|
||||
$filter->my->contribute->cookie['pagerMyTask'] = 'int';
|
||||
$filter->my->contribute->cookie['pagerMyRequirement'] = 'int';
|
||||
@@ -209,6 +217,7 @@ $filter->my->contribute->cookie['pagerMyBug'] = 'int';
|
||||
$filter->my->contribute->cookie['pagerMyTestcase'] = 'int';
|
||||
$filter->my->contribute->cookie['pagerMyTesttask'] = 'int';
|
||||
$filter->my->contribute->cookie['pagerMyDoc'] = 'int';
|
||||
$filter->my->contribute->cookie['storyViewType'] = 'code';
|
||||
|
||||
$filter->bug->batchcreate->cookie['preBranch'] = 'reg::word';
|
||||
$filter->bug->browse->cookie['bugModule'] = 'int';
|
||||
@@ -266,6 +275,7 @@ $filter->product->browse->cookie['productStoryOrder'] = 'reg::orderBy';
|
||||
$filter->product->browse->cookie['storyModule'] = 'int';
|
||||
$filter->product->browse->cookie['storyBranch'] = 'int';
|
||||
$filter->product->browse->cookie['treeBranch'] = 'reg::word';
|
||||
$filter->product->browse->cookie['storyViewType'] = 'code';
|
||||
$filter->product->all->cookie['showProductBatchEdit'] = 'int';
|
||||
$filter->product->default->cookie['lastProduct'] = 'int';
|
||||
$filter->product->default->cookie['preProductID'] = 'int';
|
||||
@@ -316,16 +326,24 @@ $filter->project->testcase->cookie['onlyScene'] = 'code';
|
||||
|
||||
$filter->projectstory->story->cookie['storyModuleParam'] = 'int';
|
||||
$filter->projectstory->story->cookie['pagerProductBrowse'] = 'int';
|
||||
$filter->projectstory->story->cookie['storyViewType'] = 'code';
|
||||
|
||||
$filter->qa->default->cookie['lastProduct'] = 'int';
|
||||
$filter->qa->default->cookie['preBranch'] = 'reg::word';
|
||||
$filter->qa->default->cookie['preProductID'] = 'int';
|
||||
|
||||
$filter->story->create->cookie['lastStoryModule'] = 'int';
|
||||
$filter->story->batchcreate->cookie['preProductID'] = 'int';
|
||||
$filter->story->export->cookie['checkedItem'] = 'reg::checked';
|
||||
$filter->story->track->cookie['preBranch'] = 'reg::word';
|
||||
$filter->story->track->cookie['preProductID'] = 'int';
|
||||
$filter->story->create->cookie['lastStoryModule'] = 'int';
|
||||
$filter->story->batchcreate->cookie['preProductID'] = 'int';
|
||||
$filter->story->export->cookie['checkedItem'] = 'reg::checked';
|
||||
$filter->story->batchchangeparent->cookie['checkedItem'] = 'reg::checked';
|
||||
$filter->story->track->cookie['preBranch'] = 'reg::word';
|
||||
$filter->story->track->cookie['preProductID'] = 'int';
|
||||
|
||||
$filter->epic->batchchangeparent->cookie['checkedItem'] = 'reg::checked';
|
||||
$filter->epic->export->cookie['checkedItem'] = 'reg::checked';
|
||||
|
||||
$filter->requirement->batchchangeparent->cookie['checkedItem'] = 'reg::checked';
|
||||
$filter->requirement->export->cookie['checkedItem'] = 'reg::checked';
|
||||
|
||||
$filter->productplan->browse->cookie['viewType'] = 'code';
|
||||
$filter->projectplan->browse->cookie['viewType'] = 'code';
|
||||
|
||||
+27
-4
@@ -119,6 +119,26 @@ $config->executionCommonList['pt'][1] = 'Sprint';
|
||||
$config->executionCommonList['pt'][2] = 'Fase';
|
||||
|
||||
/* Story common list. */
|
||||
$config->storyCommonList['zh-cn']['epic'] = '业务需求';
|
||||
$config->storyCommonList['zh-cn']['requirement'] = '用户需求';
|
||||
$config->storyCommonList['zh-cn']['story'] = '软件需求';
|
||||
|
||||
$config->storyCommonList['zh-tw']['epic'] = '業務需求';
|
||||
$config->storyCommonList['zh-tw']['requirement'] = '用戶需求';
|
||||
$config->storyCommonList['zh-tw']['story'] = '軟件需求';
|
||||
|
||||
$config->storyCommonList['en']['epic'] = 'Epic';
|
||||
$config->storyCommonList['en']['requirement'] = 'Requirement';
|
||||
$config->storyCommonList['en']['story'] = 'Story';
|
||||
|
||||
$config->storyCommonList['de']['epic'] = 'Epic';
|
||||
$config->storyCommonList['de']['requirement'] = 'Requirement';
|
||||
$config->storyCommonList['de']['story'] = 'Story';
|
||||
|
||||
$config->storyCommonList['fr']['epic'] = 'Epic';
|
||||
$config->storyCommonList['fr']['requirement'] = 'Requirement';
|
||||
$config->storyCommonList['fr']['story'] = 'Story';
|
||||
|
||||
$config->hourPointCommonList['zh-cn'][0] = '工时';
|
||||
$config->hourPointCommonList['zh-cn'][1] = '故事点';
|
||||
$config->hourPointCommonList['zh-cn'][2] = '功能点';
|
||||
@@ -254,6 +274,7 @@ $config->openMethods[] = 'search.buildzinform';
|
||||
$config->openMethods[] = 'search.buildzinquery';
|
||||
$config->openMethods[] = 'search.savezinquery';
|
||||
$config->openMethods[] = 'space.createapplication';
|
||||
$config->openMethods[] = 'story.storyview';
|
||||
$config->openMethods[] = 'ai.adminindex';
|
||||
$config->openMethods[] = 'sonarqube.create';
|
||||
$config->openMethods[] = 'sonarqube.edit';
|
||||
@@ -372,6 +393,7 @@ define('TABLE_STORYSPEC', '`' . $config->db->prefix . 'storyspec`');
|
||||
define('TABLE_STORYREVIEW', '`' . $config->db->prefix . 'storyreview`');
|
||||
define('TABLE_STORYSTAGE', '`' . $config->db->prefix . 'storystage`');
|
||||
define('TABLE_STORYESTIMATE', '`' . $config->db->prefix . 'storyestimate`');
|
||||
define('TABLE_STORYGRADE', '`' . $config->db->prefix . 'storygrade`');
|
||||
define('TABLE_PRODUCTPLAN', '`' . $config->db->prefix . 'productplan`');
|
||||
define('TABLE_PLANSTORY', '`' . $config->db->prefix . 'planstory`');
|
||||
define('TABLE_RELEASE', '`' . $config->db->prefix . 'release`');
|
||||
@@ -603,6 +625,7 @@ if(!defined('TABLE_MARKETRESEARCH')) define('TABLE_MARKETRESEARCH', '`' . $confi
|
||||
$config->objectTables['dept'] = TABLE_DEPT;
|
||||
$config->objectTables['product'] = TABLE_PRODUCT;
|
||||
$config->objectTables['productplan'] = TABLE_PRODUCTPLAN;
|
||||
$config->objectTables['epic'] = TABLE_STORY;
|
||||
$config->objectTables['story'] = TABLE_STORY;
|
||||
$config->objectTables['requirement'] = TABLE_STORY;
|
||||
$config->objectTables['release'] = TABLE_RELEASE;
|
||||
@@ -698,8 +721,8 @@ $config->pipelineTypeList = array('gitlab', 'gogs', 'gitea', 'jenkins', 'sonarqu
|
||||
|
||||
/* Program privs.*/
|
||||
$config->programPriv = new stdclass();
|
||||
$config->programPriv->noSprint = array('task', 'story', 'tree', 'project', 'execution', 'projectbuild', 'bug', 'testcase', 'testreport', 'doc', 'repo', 'stakeholder', 'projectrelease', 'requirement');
|
||||
$config->programPriv->scrum = array('story', 'requirement', 'productplan', 'tree', 'projectplan', 'projectstory', 'projectrelease', 'project', 'execution', 'projectbuild', 'bug', 'testcase', 'testreport', 'doc', 'repo', 'meeting', 'stakeholder', 'testtask');
|
||||
$config->programPriv->noSprint = array('task', 'story', 'requirement', 'epic', 'tree', 'project', 'execution', 'projectbuild', 'bug', 'testcase', 'testreport', 'doc', 'repo', 'stakeholder', 'projectrelease');
|
||||
$config->programPriv->scrum = array('story', 'requirement', 'epic', 'productplan', 'tree', 'projectplan', 'projectstory', 'projectrelease', 'project', 'execution', 'projectbuild', 'bug', 'testcase', 'testreport', 'doc', 'repo', 'meeting', 'stakeholder', 'testtask');
|
||||
$config->programPriv->waterfall = array_merge($config->programPriv->scrum, array('workestimation', 'durationestimation', 'budget', 'programplan', 'review', 'reviewissue', 'weekly', 'cm', 'milestone', 'design', 'issue', 'risk', 'opportunity', 'measrecord', 'auditplan', 'trainplan', 'gapanalysis', 'pssp', 'researchplan', 'researchreport'));
|
||||
$config->programPriv->agileplus = $config->programPriv->scrum;
|
||||
$config->programPriv->waterfallplus = $config->programPriv->waterfall;
|
||||
@@ -711,14 +734,14 @@ $config->waterfallModules = array('workestimation', 'durationestimation', 'budge
|
||||
$config->showMainMenu = true;
|
||||
$config->maxPriValue = '256';
|
||||
|
||||
$config->importWhiteList = array('user', 'task', 'story', 'bug', 'testcase', 'feedback', 'ticket');
|
||||
$config->importWhiteList = array('user', 'task', 'epic', 'requirement', 'story', 'bug', 'testcase', 'feedback', 'ticket');
|
||||
|
||||
$config->dtable = new stdclass();
|
||||
$config->dtable->colVars = array('width', 'minWidth', 'type', 'flex', 'fixed', 'sortType', 'checkbox', 'nestedToggle', 'statusMap', 'actionsMap', 'group');
|
||||
|
||||
$config->featureGroup = new stdclass();
|
||||
$config->featureGroup->my = array('score');
|
||||
$config->featureGroup->product = array('roadmap', 'track', 'UR');
|
||||
$config->featureGroup->product = array('roadmap', 'track', 'UR', 'ER');
|
||||
$config->featureGroup->scrum = array();
|
||||
$config->featureGroup->waterfall = array();
|
||||
$config->featureGroup->agileplus = array();
|
||||
|
||||
@@ -18,6 +18,19 @@ ALTER TABLE `zt_user` MODIFY `ip` varchar(255) NOT NULL DEFAULT '';
|
||||
|
||||
DELETE FROM `zt_config` WHERE `owner`='system' AND `module`='custom' AND `key`='productProject';
|
||||
|
||||
INSERT INTO `zt_metric` (`purpose`, `scope`, `object`, `stage`, `type`, `name`, `code`, `alias`, `unit`, `desc`, `definition`, `when`, `createdBy`, `createdDate`, `builtin`, `deleted`, `dateType`) VALUES
|
||||
('scale', 'product', 'feedback', 'released', 'php', '按产品统计的每周新增反馈数', 'count_of_weekly_created_feedback_in_product', '新增反馈数', 'count', '按产品统计的每周新增反馈数是指在一个周内收集到的用户反馈的数量。这个度量项可以帮助团队了解用户对产品的发展趋势和需求变化,并进行产品策略的调整和优化', '产品中创建时间为某个周的反馈的个数求和\r\n过滤已删除的反馈\r\n过滤已删除的产品', 'realtime', 'system', '2024-05-07 08:00:00', '1', '0', 'week'),
|
||||
('scale', 'product', 'feedback', 'released', 'php', '按产品统计的处理中的反馈数', 'count_of_doing_feedback_in_product', '处理中反馈数', 'count', '按产品统计的处理中的反馈数表示产品中状态为处理中的反馈数量之和。该数值越大,说明团队并行处理的反馈越多,可以帮助团队了解当前的工作负载情况', '产品中所有反馈个数求和\r\n状态为处理中\r\n过滤已删除的反馈\r\n过滤已删除的产品', 'realtime', 'system', '2024-05-07 08:00:00', '1', '0', 'nodate'),
|
||||
('scale', 'product', 'feedback', 'released', 'php', '按产品统计的已处理的反馈数', 'count_of_done_feedback_in_product', '已处理反馈数', 'count', '按产品统计的已处理的反馈数表示产品中状态为已处理的反馈数量之和。该数值越大,说明团队成员处理的反馈越多,有利于提高用户满意度', '产品中所有反馈个数求和\r\n状态为已处理\r\n过滤已删除的反馈\r\n过滤已删除的产品', 'realtime', 'system', '2024-05-07 08:00:00', '1', '0', 'nodate'),
|
||||
('scale', 'product', 'feedback', 'released', 'php', '按产品统计的待完善的反馈数', 'count_of_clarify_feedback_in_product', '待完善反馈数', 'count', '按产品统计的待完善的反馈数表示产品中状态为待完善的反馈数量之和。该数值越大,说明有较多的反馈信息不清晰或比较复杂。需要反馈者更多的澄清和解释', '产品中所有反馈个数求和\r\n状态为待完善\r\n过滤已删除的反馈\r\n过滤已删除的产品', 'realtime', 'system', '2024-05-07 08:00:00', '1', '0', 'nodate'),
|
||||
('scale', 'product', 'feedback', 'released', 'php', '按产品统计的待处理的反馈数', 'count_of_wait_feedback_in_product', '待处理反馈数', 'count', '按产品统计的待处理的反馈数表示产品中状态为待处理的反馈数量之和。该度量项可能暗示产品团队的反馈处理效率,待处理反馈数越多,可能会导致客户满意度降低', '产品中所有反馈个数求和\r\n状态为待处理\r\n过滤已删除的反馈\r\n过滤已删除的产品', 'realtime', 'system', '2024-05-07 08:00:00', '1', '0', 'nodate'),
|
||||
('scale', 'product', 'feedback', 'released', 'php', '按产品统计的追问中的反馈数', 'count_of_asked_feedback_in_product', '追问中反馈数', 'count', '按产品统计的追问中的反馈数表示产品中状态为追问中的反馈数量之和。该度量项可能暗示着反馈的复杂性或对处理方案的疑惑,追问中的反馈数量越多,可能意味着团队需要更多时间和资源来回复并解决这些问题', '产品中所有反馈个数求和\r\n状态为追问中\r\n过滤已删除的反馈\r\n过滤已删除的产品', 'realtime', 'system', '2024-05-07 08:00:00', '1', '0', 'nodate'),
|
||||
('scale', 'product', 'feedback', 'released', 'php', '按产品统计的未关闭的反馈数', 'count_of_unclosed_feedback_in_product', '未关闭反馈数', 'count', '按产品统计的未关闭的反馈数表示产品中状态为未关闭的反馈数量之和。这个度量项可以一定程度反映产品团队响应用户反馈的效率和及时处理用户问题的能力', '产品中所有反馈个数求和\r\n过滤状态为已关闭的反馈\r\n过滤已删除的反馈\r\n过滤已删除的产品', 'realtime', 'system', '2024-05-07 08:00:00', '1', '0', 'nodate'),
|
||||
('scale', 'user', 'ticket', 'released', 'php', '按人员统计的待处理工单数', 'count_of_assigned_ticket_in_user', '待处理工单数', 'count', '按人员统计的待处理工单数表示每个人待处理的工单数量之和。反映了每个人员需要处理的工单数量的规模。该数值越大,说明需要处理的反馈任务越多。', '所有工单个数求和\r\n指派给为某人\r\n过滤已删除的工单\r\n过滤已删除产品的工单', 'realtime', 'system', '2024-05-07 08:00:00', '1', '0', 'nodate'),
|
||||
('scale', 'user', 'qa', 'released', 'php', '按人员统计的待处理QA数', 'count_of_assigned_qa_in_user', '待处理QA数', 'count', '按人员统计的待处理QA数表示每个人待处理的质量保证问题之和。反映了每个人员需要处理的质量保证问题的规模。该数值越大,说明需要处理的质量保证问题越多。', '所有待处理的QA个数求和(包含:待处理质量保证计划、待处理不符合项)\r\n指派给为某人\r\n质量保证计划状态为待检查、不符合项状态为待解决\r\n过滤已删除的质量保证计划和不符合项\r\n过滤已删除项目的质量保证计划和不符合项', 'realtime', 'system', '2024-05-07 08:00:00', '1', '0', 'nodate'),
|
||||
('scale', 'user', 'risk', 'released', 'php', '按人员统计的待处理风险数', 'count_of_assigned_risk_in_user', '待处理风险数', 'count', '按人员统计的待处理用户需求数表示每个人待处理的用户需求数量之和。反映了每个人员需要处理的用户需求数量的规模。该数值越大,说明需要投入越多的时间处理用户需求。', '所有风险个数求和\r\n指派给为某人\r\n过滤已删除的风险\r\n过滤已关闭的风险\r\n过滤已删除项目的风险', 'realtime', 'system', '2024-05-07 08:00:00', '1', '0', 'nodate'),
|
||||
('scale', 'user', 'issue', 'released', 'php', '按人员统计的待处理问题数', 'count_of_assigned_issue_in_user', '待处理问题数', 'count', '按人员统计的待处理问题数表示每个人待处理的问题数量之和。反映了每个人员需要处理的问题数量的规模。该数值越大,项目存在问题越多,需要投入越多的时间处理问题。', '所有问题个数求和\r\n指派给为某人\r\n过滤已删除的问题\r\n过滤已删除项目的问题', 'realtime', 'system', '2024-05-07 08:00:00', '1', '0', 'nodate');
|
||||
|
||||
ALTER TABLE `zt_chart` ADD `code` varchar(255) not NULL default '' AFTER `name`;
|
||||
ALTER TABLE `zt_pivot` ADD `code` varchar(255) not NULL default '' AFTER `group`;
|
||||
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
REPLACE INTO `zt_lang` (`lang`, `module`, `section`, `key`, `value`, `system`, `vision`) VALUES
|
||||
('zh-cn', 'custom', 'URSRList', '1', '{\"ERName\":\"\\u4e1a\\u52a1\\u9700\\u6c42\",\"SRName\":\"\\u8f6f\\u4ef6\\u9700\\u6c42\",\"URName\":\"\\u7528\\u6237\\u9700\\u6c42\"}', '0', 'rnd'),
|
||||
('zh-cn', 'custom', 'URSRList', '2', '{\"ERName\":\"\\u4e1a\\u52a1\\u9700\\u6c42\",\"SRName\":\"\\u7814\\u53d1\\u9700\\u6c42\",\"URName\":\"\\u7528\\u6237\\u9700\\u6c42\"}', '0', 'rnd'),
|
||||
('zh-cn', 'custom', 'URSRList', '3', '{\"ERName\":\"\\u4e1a\\u9700\",\"SRName\":\"\\u8f6f\\u9700\",\"URName\":\"\\u7528\\u9700\"}', '0', 'rnd'),
|
||||
('zh-cn', 'custom', 'URSRList', '4', '{\"ERName\":\"\\u53f2\\u8bd7\",\"SRName\":\"\\u6545\\u4e8b\",\"URName\":\"\\u7279\\u6027\"}', '0', 'rnd'),
|
||||
('zh-cn', 'custom', 'URSRList', '5', '{\"ERName\":\"\\u4e1a\\u52a1\\u9700\\u6c42\",\"SRName\":\"\\u9700\\u6c42\",\"URName\":\"\\u7528\\u6237\\u9700\\u6c42\"}', '0', 'rnd'),
|
||||
('zh-cn', 'custom', 'URSRList', '6', '{\"ERName\":\"Epic\",\"SRName\":\"Story\",\"URName\":\"Feature\"}', '0', 'rnd'),
|
||||
('en', 'custom', 'URSRList', '1', '{\"ERName\":\"Epic\",\"SRName\":\"Story\",\"URName\":\"Feature\"}', '0', 'rnd'),
|
||||
('en', 'custom', 'URSRList', '2', '{\"ERName\":\"Epic\",\"SRName\":\"Story\",\"URName\":\"Requirement\"}', '0', 'rnd'),
|
||||
('en', 'custom', 'URSRList', '3', '{\"ERName\":\"Epic\",\"SRName\":\"Story\",\"URName\":\"Feature\"}', '0', 'rnd'),
|
||||
('fr', 'custom', 'URSRList', '1', '{\"ERName\":\"Epic\",\"SRName\":\"Story\",\"URName\":\"Feature\"}', '0', 'rnd'),
|
||||
('fr', 'custom', 'URSRList', '2', '{\"ERName\":\"Epic\",\"SRName\":\"Story\",\"URName\":\"Requirement\"}', '0', 'rnd'),
|
||||
('fr', 'custom', 'URSRList', '3', '{\"ERName\":\"Epic\",\"SRName\":\"Story\",\"URName\":\"Feature\"}', '0', 'rnd'),
|
||||
('de', 'custom', 'URSRList', '1', '{\"ERName\":\"Epic\",\"SRName\":\"Story\",\"URName\":\"Feature\"}', '0', 'rnd'),
|
||||
('de', 'custom', 'URSRList', '2', '{\"ERName\":\"Epic\",\"SRName\":\"Story\",\"URName\":\"Requirement\"}', '0', 'rnd'),
|
||||
('de', 'custom', 'URSRList', '3', '{\"ERName\":\"Epic\",\"SRName\":\"Story\",\"URName\":\"Feature\"}', '0', 'rnd'),
|
||||
('zh-tw', 'custom', 'URSRList', '1', '{\"ERName\":\"\\u696d\\u52d9\\u9700\\u6c42\",\"SRName\":\"\\u8edf\\u4ef6\\u9700\\u6c42\",\"URName\":\"\\u7528\\u6236\\u9700\\u6c42\"}', '0', 'rnd'),
|
||||
('zh-tw', 'custom', 'URSRList', '2', '{\"ERName\":\"\\u696d\\u52d9\\u9700\\u6c42\",\"SRName\":\"\\u7814\\u767c\\u9700\\u6c42\",\"URName\":\"\\u7528\\u6236\\u9700\\u6c42\"}', '0', 'rnd'),
|
||||
('zh-tw', 'custom', 'URSRList', '3', '{\"ERName\":\"\\u696d\\u9700\",\"SRName\":\"\\u8edf\\u9700\",\"URName\":\"\\u7528\\u9700\"}', '0', 'rnd'),
|
||||
('zh-tw', 'custom', 'URSRList', '4', '{\"ERName\":\"\\u53f2\\u8a69\",\"SRName\":\"\\u6545\\u4e8b\",\"URName\":\"\\u7279\\u6027\"}', '0', 'rnd'),
|
||||
('zh-tw', 'custom', 'URSRList', '5', '{\"ERName\":\"\\u696d\\u52d9\\u9700\\u6c42\",\"SRName\":\"\\u9700\\u6c42\",\"URName\":\"\\u7528\\u6236\\u9700\\u6c42\"}', '0', 'rnd'),
|
||||
('zh-tw', 'custom', 'URSRList', '6', '{\"ERName\":\"Epic\",\"SRName\":\"Story\",\"URName\":\"Feature\"}', '0', 'rnd');
|
||||
|
||||
CREATE TABLE `zt_storygrade` (
|
||||
`type` enum('story','requirement','epic') NOT NULL,
|
||||
`grade` smallint NOT NULL,
|
||||
`name` char(30) NOT NULL,
|
||||
`status` char(30) NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
INSERT INTO `zt_storygrade` (`type`, `grade`, `name`, `status`) VALUES
|
||||
('requirement', 1, 'UR', 'enable'),
|
||||
('epic', 1, 'BR', 'enable'),
|
||||
('story', 1, 'SR', 'enable'),
|
||||
('story', 2, '子', 'enable');
|
||||
|
||||
ALTER TABLE `zt_story` ADD `isParent` enum('0','1') NOT NULL DEFAULT '0' AFTER `parent`;
|
||||
ALTER TABLE `zt_story` ADD `root` mediumint NOT NULL DEFAULT '0' AFTER `isParent`;
|
||||
ALTER TABLE `zt_story` ADD `path` text NULL AFTER `root`;
|
||||
ALTER TABLE `zt_story` ADD `grade` smallint(6) NOT NULL DEFAULT '0' AFTER `path`;
|
||||
ALTER TABLE `zt_story` ADD `parentVersion` smallint NOT NULL DEFAULT '0' AFTER `version`;
|
||||
ALTER TABLE `zt_story` CHANGE `stage` `stage` enum('','wait','defining','planning','planned','projected','designing','designed','developing','developed','testing','tested','verified','rejected','delivering','pending','released','closed') NOT NULL DEFAULT 'wait';
|
||||
ALTER TABLE `zt_story` DROP `childStories`;
|
||||
CREATE INDEX `root` ON `zt_story` (`root`);
|
||||
UPDATE `zt_story` SET stage = 'defining' WHERE stage = 'wait' AND (parent = -1 OR type != 'story');
|
||||
UPDATE `zt_story` SET stage = 'planning' WHERE stage in ('planned', 'projected') AND (parent = -1 OR type != 'story');
|
||||
UPDATE `zt_story` SET stage = 'developing' WHERE stage in ('developed', 'testing', 'tested') AND (parent = -1 OR type != 'story');
|
||||
UPDATE `zt_story` SET stage = 'delivering' WHERE stage in ('verified', 'released') AND (parent = -1 OR type != 'story');
|
||||
UPDATE `zt_story` SET isParent = '1' WHERE parent = -1;
|
||||
UPDATE `zt_story` SET grade = 1, parent = 0, root = id, path = concat(',', id, ',') WHERE type != 'story';
|
||||
UPDATE `zt_story` SET grade = 1, parent = 0, root = id, path = concat(',', id, ',') WHERE type = 'story' AND parent <= 0;
|
||||
UPDATE `zt_story` SET grade = 2, root = parent, path = concat(',', parent, ',', id, ',') WHERE type = 'story' AND parent > 0;
|
||||
|
||||
INSERT INTO `zt_config` (`owner`, `module`, `section`, `key`, `value`) VALUES ('system', 'story', '', 'gradeRule', 'stepwise');
|
||||
INSERT INTO `zt_config` (`owner`, `module`, `section`, `key`, `value`) VALUES ('system', 'requirement', '', 'gradeRule', 'stepwise');
|
||||
INSERT INTO `zt_config` (`owner`, `module`, `section`, `key`, `value`) VALUES ('system', 'epic', '', 'gradeRule', 'stepwise');
|
||||
INSERT INTO `zt_config` (`owner`, `module`, `section`, `key`, `value`) VALUES ('system', 'custom', '', 'enableER', '1');
|
||||
|
||||
ALTER TABLE `zt_product`
|
||||
ADD `draftRequirements` mediumint(8) NOT NULL DEFAULT '0' AFTER `reviewer`,
|
||||
ADD `activeRequirements` mediumint(8) NOT NULL DEFAULT '0' AFTER `draftRequirements`,
|
||||
ADD `changingRequirements` mediumint(8) NOT NULL DEFAULT '0' AFTER `activeRequirements`,
|
||||
ADD `reviewingRequirements` mediumint(8) NOT NULL DEFAULT '0' AFTER `changingRequirements`,
|
||||
ADD `finishedRequirements` mediumint NOT NULL DEFAULT '0' AFTER `reviewingRequirements`,
|
||||
ADD `closedRequirements` mediumint(8) NOT NULL DEFAULT '0' AFTER `finishedRequirements`,
|
||||
ADD `totalRequirements` mediumint(8) NOT NULL DEFAULT '0' AFTER `closedRequirements`;
|
||||
|
||||
ALTER TABLE `zt_product`
|
||||
ADD `draftEpics` mediumint(8) NOT NULL DEFAULT '0' AFTER `reviewer`,
|
||||
ADD `activeEpics` mediumint(8) NOT NULL DEFAULT '0' AFTER `draftEpics`,
|
||||
ADD `changingEpics` mediumint(8) NOT NULL DEFAULT '0' AFTER `activeEpics`,
|
||||
ADD `reviewingEpics` mediumint(8) NOT NULL DEFAULT '0' AFTER `changingEpics`,
|
||||
ADD `finishedEpics` mediumint NOT NULL DEFAULT '0' AFTER `reviewingEpics`,
|
||||
ADD `closedEpics` mediumint(8) NOT NULL DEFAULT '0' AFTER `finishedEpics`,
|
||||
ADD `totalEpics` mediumint(8) NOT NULL DEFAULT '0' AFTER `closedEpics`;
|
||||
|
||||
ALTER TABLE `zt_project` ADD `storyType` char(30) NULL DEFAULT 'story' AFTER `auth`;
|
||||
UPDATE `zt_project` SET storyType = 'story' WHERE storyType = '';
|
||||
|
||||
DROP VIEW IF EXISTS `view_datasource_2`;
|
||||
DROP VIEW IF EXISTS `view_datasource_3`;
|
||||
DROP VIEW IF EXISTS `view_datasource_4`;
|
||||
|
||||
CREATE VIEW `view_datasource_2` AS select `id`,`title` from `zt_story` where `deleted` = '0' and type = 'epic';
|
||||
CREATE VIEW `view_datasource_3` AS select `id`,`title` from `zt_story` where `deleted` = '0' and type = 'requirement';
|
||||
CREATE VIEW `view_datasource_4` AS select `id`,`title` from `zt_story` where `deleted` = '0' and type = 'story';
|
||||
|
||||
INSERT INTO `zt_workflowdatasource` (`type`, `name`, `code`, `buildin`, `vision`, `createdBy`, `createdDate`, `datasource`, `view`, `keyField`, `valueField`) VALUES
|
||||
('sql', '用户需求', 'requirements', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'select id,title from zt_story where deleted=\"0\" and type=\"requirement\"', 'view_datasource_3', 'id', 'title'),
|
||||
('sql', '业务需求', 'epics', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'select id,title from zt_story where deleted=\"0\" type=\"epic\"', 'view_datasource_2', 'id', 'title');
|
||||
|
||||
UPDATE `zt_workflowdatasource` SET `datasource` = 'select id,title from zt_story where deleted=\"0\" and type=\"story\"' WHERE `code` = 'stories';
|
||||
+70
-21
@@ -1363,6 +1363,20 @@ CREATE TABLE IF NOT EXISTS `zt_product` (
|
||||
`whitelist` text NULL,
|
||||
`reviewer` text NULL,
|
||||
`PMT` text NULL,
|
||||
`draftEpics` mediumint(8) NOT NULL DEFAULT '0',
|
||||
`activeEpics` mediumint(8) NOT NULL DEFAULT '0',
|
||||
`changingEpics` mediumint(8) NOT NULL DEFAULT '0',
|
||||
`reviewingEpics` mediumint(8) NOT NULL DEFAULT '0',
|
||||
`finishedEpics` mediumint(8) NOT NULL DEFAULT '0',
|
||||
`closedEpics` mediumint(8) NOT NULL DEFAULT '0',
|
||||
`totalEpics` mediumint(8) NOT NULL DEFAULT '0',
|
||||
`draftRequirements` mediumint(8) NOT NULL DEFAULT '0',
|
||||
`activeRequirements` mediumint(8) NOT NULL DEFAULT '0',
|
||||
`changingRequirements` mediumint(8) NOT NULL DEFAULT '0',
|
||||
`reviewingRequirements` mediumint(8) NOT NULL DEFAULT '0',
|
||||
`finishedRequirements` mediumint(8) NOT NULL DEFAULT '0',
|
||||
`closedRequirements` mediumint(8) NOT NULL DEFAULT '0',
|
||||
`totalRequirements` mediumint(8) NOT NULL DEFAULT '0',
|
||||
`draftStories` mediumint(8) NOT NULL DEFAULT '0',
|
||||
`activeStories` mediumint(8) NOT NULL DEFAULT '0',
|
||||
`changingStories` mediumint(8) NOT NULL DEFAULT '0',
|
||||
@@ -1427,6 +1441,7 @@ CREATE TABLE IF NOT EXISTS `zt_project` (
|
||||
`milestone` enum('0','1') NOT NULL DEFAULT '0',
|
||||
`output` text NULL,
|
||||
`auth` char(30) NOT NULL DEFAULT '',
|
||||
`storyType` char(30) NOT NULL DEFAULT '',
|
||||
`parent` mediumint(8) unsigned NOT NULL DEFAULT '0',
|
||||
`path` varchar(255) NOT NULL DEFAULT '',
|
||||
`grade` tinyint(3) unsigned NOT NULL DEFAULT '0',
|
||||
@@ -1765,6 +1780,10 @@ CREATE TABLE IF NOT EXISTS `zt_story` (
|
||||
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`vision` varchar(10) NOT NULL DEFAULT 'rnd',
|
||||
`parent` mediumint(9) NOT NULL DEFAULT '0',
|
||||
`isParent` enum('0', '1') NOT NULL DEFAULT '0',
|
||||
`root` mediumint(9) NOT NULL DEFAULT '0',
|
||||
`path` text NULL,
|
||||
`grade` smallint(6) NOT NULL DEFAULT '0',
|
||||
`product` mediumint(8) unsigned NOT NULL DEFAULT '0',
|
||||
`branch` mediumint(8) unsigned NOT NULL DEFAULT '0',
|
||||
`module` mediumint(8) unsigned NOT NULL DEFAULT '0',
|
||||
@@ -1782,7 +1801,7 @@ CREATE TABLE IF NOT EXISTS `zt_story` (
|
||||
`status` enum('','changing','active','draft','closed','reviewing','launched','developing') NOT NULL DEFAULT '',
|
||||
`subStatus` varchar(30) NOT NULL DEFAULT '',
|
||||
`color` char(7) NOT NULL DEFAULT '',
|
||||
`stage` enum('','wait','planned','projected','developing','developed','testing','tested','verified','released','closed') NOT NULL DEFAULT 'wait',
|
||||
`stage` enum('','wait','defining','planning','planned','projected','designing','designed','developing','developed','testing','tested','verified','rejected','delivering','pending','released','closed') NOT NULL DEFAULT 'wait',
|
||||
`stagedBy` char(30) NOT NULL DEFAULT '',
|
||||
`mailto` text NULL,
|
||||
`lib` mediumint(8) unsigned NOT NULL DEFAULT '0',
|
||||
@@ -1805,12 +1824,12 @@ CREATE TABLE IF NOT EXISTS `zt_story` (
|
||||
`closedReason` varchar(30) NOT NULL DEFAULT '',
|
||||
`activatedDate` datetime NULL,
|
||||
`toBug` mediumint(8) unsigned NOT NULL DEFAULT '0',
|
||||
`childStories` varchar(255) NOT NULL DEFAULT '',
|
||||
`linkStories` varchar(255) NOT NULL DEFAULT '',
|
||||
`linkRequirements` varchar(255) NOT NULL DEFAULT '',
|
||||
`twins` varchar(255) NOT NULL DEFAULT '',
|
||||
`duplicateStory` mediumint(8) unsigned NOT NULL DEFAULT '0',
|
||||
`version` smallint(6) NOT NULL DEFAULT '1',
|
||||
`parentVersion` smallint(6) NOT NULL DEFAULT '0',
|
||||
`storyChanged` enum('0','1') NOT NULL DEFAULT '0',
|
||||
`feedbackBy` varchar(100) NOT NULL DEFAULT '',
|
||||
`notifyEmail` varchar(100) NOT NULL DEFAULT '',
|
||||
@@ -1828,9 +1847,18 @@ CREATE TABLE IF NOT EXISTS `zt_story` (
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
CREATE INDEX `product` ON `zt_story` (`product`);
|
||||
CREATE INDEX `root` ON `zt_story` (`root`);
|
||||
CREATE INDEX `status` ON `zt_story` (`status`);
|
||||
CREATE INDEX `assignedTo` ON `zt_story` (`assignedTo`);
|
||||
|
||||
-- DROP TABLE IF EXISTS `zt_storygrade`;
|
||||
CREATE TABLE `zt_storygrade` (
|
||||
`type` enum('story','requirement','epic') NOT NULL,
|
||||
`grade` smallint NOT NULL,
|
||||
`name` char(30) NOT NULL,
|
||||
`status` char(30) NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
-- DROP TABLE IF EXISTS `zt_storyreview`;
|
||||
CREATE TABLE IF NOT EXISTS `zt_storyreview` (
|
||||
`story` mediumint(9) NOT NULL DEFAULT '0',
|
||||
@@ -2343,23 +2371,34 @@ REPLACE INTO `zt_group` (`vision`, `name`, `role`, `desc`) VALUES ('or', 'IPDDEM
|
||||
REPLACE INTO `zt_group` (`vision`, `name`, `role`, `desc`) VALUES ('or', 'IPDPMT', 'IPDPMT', '');
|
||||
REPLACE INTO `zt_group` (`vision`, `name`, `role`, `desc`) VALUES ('or', 'IPDADMIN', 'IPDADMIN', '');
|
||||
|
||||
REPLACE INTO `zt_lang` (`lang`, `module`, `section`, `key`, `value`, `system`) VALUES
|
||||
('zh-cn', 'custom', 'URSRList', '1', '{\"SRName\":\"\\u8f6f\\u4ef6\\u9700\\u6c42\",\"URName\":\"\\u7528\\u6237\\u9700\\u6c42\"}', '1'),
|
||||
('zh-cn', 'custom', 'URSRList', '2', '{\"SRName\":\"\\u7814\\u53d1\\u9700\\u6c42\",\"URName\":\"\\u7528\\u6237\\u9700\\u6c42\"}', '1'),
|
||||
('zh-cn', 'custom', 'URSRList', '3', '{\"SRName\":\"\\u8f6f\\u9700\",\"URName\":\"\\u7528\\u9700\"}', '1'),
|
||||
('zh-cn', 'custom', 'URSRList', '4', '{\"SRName\":\"\\u6545\\u4e8b\",\"URName\":\"\\u53f2\\u8bd7\"}', '1'),
|
||||
('zh-cn', 'custom', 'URSRList', '5', '{\"SRName\":\"\\u9700\\u6c42\",\"URName\":\"\\u7528\\u6237\\u9700\\u6c42\"}', '1'),
|
||||
('zh-tw', 'custom', 'URSRList', '1', '{"SRName":"\\u8edf\\u4ef6\\u9700\\u6c42","URName":"\\u7528\\u6236\\u9700\\u6c42"}', '0'),
|
||||
('zh-tw', 'custom', 'URSRList', '2', '{"SRName":"\\u7814\\u767c\\u9700\\u6c42","URName":"\\u7528\\u6236\\u9700\\u6c42"}', '0'),
|
||||
('zh-tw', 'custom', 'URSRList', '3', '{"SRName":"\\u8edf\\u9700","URName":"\\u7528\\u9700"}', '0'),
|
||||
('zh-tw', 'custom', 'URSRList', '4', '{"SRName":"\\u6545\\u4e8b","URName":"\\u53f2\\u8a69"}', '0'),
|
||||
('zh-tw', 'custom', 'URSRList', '5', '{"SRName":"\\u9700\\u6c42","URName":"\\u7528\\u6236\\u9700\\u6c42"}', '0'),
|
||||
('en', 'custom', 'URSRList', '1', '{\"SRName\":\"Story\",\"URName\":\"Epic\"}', '0'),
|
||||
('en', 'custom', 'URSRList', '2', '{\"SRName\":\"Story\",\"URName\":\"Requirement\"}', '0'),
|
||||
('fr', 'custom', 'URSRList', '1', '{\"SRName\":\"Story\",\"URName\":\"Epic\"}', '0'),
|
||||
('fr', 'custom', 'URSRList', '2', '{\"SRName\":\"Story\",\"URName\":\"Requirement\"}', '0'),
|
||||
('de', 'custom', 'URSRList', '1', '{\"SRName\":\"Story\",\"URName\":\"Epic\"}', '0'),
|
||||
('de', 'custom', 'URSRList', '2', '{\"SRName\":\"Story\",\"URName\":\"Requirement\"}', '0');
|
||||
REPLACE INTO `zt_lang` (`lang`, `module`, `section`, `key`, `value`, `system`, `vision`) VALUES
|
||||
('zh-cn', 'custom', 'URSRList', '1', '{\"ERName\":\"\\u4e1a\\u52a1\\u9700\\u6c42\",\"SRName\":\"\\u8f6f\\u4ef6\\u9700\\u6c42\",\"URName\":\"\\u7528\\u6237\\u9700\\u6c42\"}', '0', 'rnd'),
|
||||
('zh-cn', 'custom', 'URSRList', '2', '{\"ERName\":\"\\u4e1a\\u52a1\\u9700\\u6c42\",\"SRName\":\"\\u7814\\u53d1\\u9700\\u6c42\",\"URName\":\"\\u7528\\u6237\\u9700\\u6c42\"}', '0', 'rnd'),
|
||||
('zh-cn', 'custom', 'URSRList', '3', '{\"ERName\":\"\\u4e1a\\u9700\",\"SRName\":\"\\u8f6f\\u9700\",\"URName\":\"\\u7528\\u9700\"}', '0', 'rnd'),
|
||||
('zh-cn', 'custom', 'URSRList', '4', '{\"ERName\":\"\\u53f2\\u8bd7\",\"SRName\":\"\\u6545\\u4e8b\",\"URName\":\"\\u7279\\u6027\"}', '0', 'rnd'),
|
||||
('zh-cn', 'custom', 'URSRList', '5', '{\"ERName\":\"\\u4e1a\\u52a1\\u9700\\u6c42\",\"SRName\":\"\\u9700\\u6c42\",\"URName\":\"\\u7528\\u6237\\u9700\\u6c42\"}', '0', 'rnd'),
|
||||
('zh-cn', 'custom', 'URSRList', '6', '{\"ERName\":\"Epic\",\"SRName\":\"Story\",\"URName\":\"Feature\"}', '0', 'rnd'),
|
||||
('en', 'custom', 'URSRList', '1', '{\"ERName\":\"Epic\",\"SRName\":\"Story\",\"URName\":\"Feature\"}', '0', 'rnd'),
|
||||
('en', 'custom', 'URSRList', '2', '{\"ERName\":\"Epic\",\"SRName\":\"Story\",\"URName\":\"Requirement\"}', '0', 'rnd'),
|
||||
('en', 'custom', 'URSRList', '3', '{\"ERName\":\"Epic\",\"SRName\":\"Story\",\"URName\":\"Feature\"}', '0', 'rnd'),
|
||||
('fr', 'custom', 'URSRList', '1', '{\"ERName\":\"Epic\",\"SRName\":\"Story\",\"URName\":\"Feature\"}', '0', 'rnd'),
|
||||
('fr', 'custom', 'URSRList', '2', '{\"ERName\":\"Epic\",\"SRName\":\"Story\",\"URName\":\"Requirement\"}', '0', 'rnd'),
|
||||
('fr', 'custom', 'URSRList', '3', '{\"ERName\":\"Epic\",\"SRName\":\"Story\",\"URName\":\"Feature\"}', '0', 'rnd'),
|
||||
('de', 'custom', 'URSRList', '1', '{\"ERName\":\"Epic\",\"SRName\":\"Story\",\"URName\":\"Feature\"}', '0', 'rnd'),
|
||||
('de', 'custom', 'URSRList', '2', '{\"ERName\":\"Epic\",\"SRName\":\"Story\",\"URName\":\"Requirement\"}', '0', 'rnd'),
|
||||
('de', 'custom', 'URSRList', '3', '{\"ERName\":\"Epic\",\"SRName\":\"Story\",\"URName\":\"Feature\"}', '0', 'rnd'),
|
||||
('zh-tw', 'custom', 'URSRList', '1', '{\"ERName\":\"\\u696d\\u52d9\\u9700\\u6c42\",\"SRName\":\"\\u8edf\\u4ef6\\u9700\\u6c42\",\"URName\":\"\\u7528\\u6236\\u9700\\u6c42\"}', '0', 'rnd'),
|
||||
('zh-tw', 'custom', 'URSRList', '2', '{\"ERName\":\"\\u696d\\u52d9\\u9700\\u6c42\",\"SRName\":\"\\u7814\\u767c\\u9700\\u6c42\",\"URName\":\"\\u7528\\u6236\\u9700\\u6c42\"}', '0', 'rnd'),
|
||||
('zh-tw', 'custom', 'URSRList', '3', '{\"ERName\":\"\\u696d\\u9700\",\"SRName\":\"\\u8edf\\u9700\",\"URName\":\"\\u7528\\u9700\"}', '0', 'rnd'),
|
||||
('zh-tw', 'custom', 'URSRList', '4', '{\"ERName\":\"\\u53f2\\u8a69\",\"SRName\":\"\\u6545\\u4e8b\",\"URName\":\"\\u7279\\u6027\"}', '0', 'rnd'),
|
||||
('zh-tw', 'custom', 'URSRList', '5', '{\"ERName\":\"\\u696d\\u52d9\\u9700\\u6c42\",\"SRName\":\"\\u9700\\u6c42\",\"URName\":\"\\u7528\\u6236\\u9700\\u6c42\"}', '0', 'rnd'),
|
||||
('zh-tw', 'custom', 'URSRList', '6', '{\"ERName\":\"Epic\",\"SRName\":\"Story\",\"URName\":\"Feature\"}', '0', 'rnd');
|
||||
|
||||
INSERT INTO `zt_storygrade` (`type`, `grade`, `name`, `status`) VALUES
|
||||
('requirement', 1, 'UR', 'enable'),
|
||||
('epic', 1, 'BR', 'enable'),
|
||||
('story', 1, 'SR', 'enable'),
|
||||
('story', 2, '子', 'enable');
|
||||
|
||||
REPLACE INTO `zt_stage` (`name`,`percent`,`type`,`projectType`,`createdBy`,`createdDate`,`editedBy`,`editedDate`,`deleted`) VALUES
|
||||
('需求','10','request','waterfall','admin','2020-02-08 21:08:30','admin','2020-02-12 13:50:27','0'),
|
||||
@@ -2379,11 +2418,15 @@ INSERT INTO `zt_config` (`owner`, `module`, `section`, `key`, `value`) VALUES ('
|
||||
INSERT INTO `zt_config` (`owner`, `module`, `section`, `key`, `value`) VALUES ('system', 'common', '', 'CRProduct', '1');
|
||||
INSERT INTO `zt_config` (`owner`, `module`, `section`, `key`, `value`) VALUES ('system', 'common', '', 'CRExecution', '1');
|
||||
INSERT INTO `zt_config` (`owner`, `module`, `section`, `key`, `value`) VALUES ('system', 'custom', '', 'URSR', '2');
|
||||
INSERT INTO `zt_config` (`owner`, `module`, `section`, `key`, `value`) VALUES ('system', 'custom', '', 'enableER', '0');
|
||||
INSERT INTO `zt_config` (`owner`, `module`, `section`, `key`, `value`) VALUES ('system', 'common', 'global', 'mode', 'ALM');
|
||||
INSERT INTO `zt_config` (`owner`, `module`, `section`, `key`, `value`) VALUES ('system', 'project', '', 'unitList', 'CNY,USD');
|
||||
INSERT INTO `zt_config` (`owner`, `module`, `section`, `key`, `value`) VALUES ('system', 'project', '', 'defaultCurrency', 'CNY');
|
||||
INSERT INTO `zt_config` (`owner`, `module`, `section`, `key`, `value`) VALUES ('system', 'story', '', 'reviewRules', 'allpass');
|
||||
INSERT INTO `zt_config` (`owner`, `module`, `section`, `key`, `value`) VALUES ('system', 'common', 'global', 'syncProduct', '{"feedback":{},"ticket":{}}');
|
||||
INSERT INTO `zt_config` (`owner`, `module`, `section`, `key`, `value`) VALUES ('system', 'story', '', 'gradeRule', 'cross');
|
||||
INSERT INTO `zt_config` (`owner`, `module`, `section`, `key`, `value`) VALUES ('system', 'requirement', '', 'gradeRule', 'cross');
|
||||
INSERT INTO `zt_config` (`owner`, `module`, `section`, `key`, `value`) VALUES ('system', 'epic', '', 'gradeRule', 'cross');
|
||||
-- DROP TABLE IF EXISTS `zt_im_chat`;
|
||||
CREATE TABLE IF NOT EXISTS `zt_im_chat` (
|
||||
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
|
||||
@@ -13590,7 +13633,9 @@ INSERT INTO `zt_workflowdatasource` (`type`, `name`, `code`, `buildin`, `vision`
|
||||
('system', '产品', 'products', '1', 'rnd', 'admin', '1970-01-01 00:00:01', '{\"app\":\"system\",\"module\":\"product\",\"method\":\"getPairs\",\"methodDesc\":\"Get product pairs.\",\"params\":[{\"name\":\"mode\",\"type\":\"string\",\"desc\":\"\",\"value\":\"all\"}]}', '', '', ''),
|
||||
('system', '项目', 'projects', '1', 'rnd', 'admin', '1970-01-01 00:00:01', '{\"app\":\"system\",\"module\":\"project\",\"method\":\"getPairsByModel\",\"methodDesc\":\"Get project pairs by model and project.\",\"params\":[{\"name\":\"model\",\"type\":\"string\",\"desc\":\"all|scrum|waterfall\",\"value\":\"all\"},{\"name\":\"programID\",\"type\":\"int\",\"desc\":\"\",\"value\":\"0\"},{\"name\":\"param\",\"type\":\"\",\"desc\":\"\",\"value\":\"\"}]}', '', '', ''),
|
||||
('system', '产品线', 'productLines', '1', 'rnd', 'admin', '1970-01-01 00:00:01', '{\"app\":\"system\",\"module\":\"product\",\"method\":\"getLinePairs\",\"methodDesc\":\"Get line pairs.\",\"params\":[{\"name\":\"useShort\",\"type\":\"bool\",\"desc\":\"\",\"value\":\"\"}]}', '', '', ''),
|
||||
('sql', '需求', 'stories', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'select id,title from zt_story where deleted=\"0\"', 'view_datasource_4', 'id', 'title'),
|
||||
('sql', '软件需求', 'stories', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'select id,title from zt_story where deleted=\"0\" and type=\"story\"', 'view_datasource_4', 'id', 'title'),
|
||||
('sql', '用户需求', 'requirements', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'select id,title from zt_story where deleted=\"0\" and type=\"requirement\"', 'view_datasource_3', 'id', 'title'),
|
||||
('sql', '业务需求', 'epics', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'select id,title from zt_story where deleted=\"0\" type=\"epic\"', 'view_datasource_2', 'id', 'title'),
|
||||
('sql', '任务', 'tasks', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'select id,name from zt_task where deleted=\"0\" and vision=\"rnd\"', 'view_datasource_5', 'id', 'name'),
|
||||
('sql', 'Bug', 'bugs', '1', 'rnd', 'admin', '1970-01-01 00:00:01', 'select id,title from zt_bug where deleted=\"0\"', 'view_datasource_6', 'id', 'title'),
|
||||
('system', '权限分组', 'groups', '1', 'rnd', 'admin', '1970-01-01 00:00:01', '{\"app\":\"system\",\"module\":\"group\",\"method\":\"getPairs\",\"methodDesc\":\"\",\"params\":[]}', '', '', ''),
|
||||
@@ -13667,6 +13712,8 @@ INSERT INTO `zt_workflowdatasource` (`type`, `name`, `code`, `buildin`, `vision`
|
||||
('lang', '需求管理周期', 'demandDuration', '1', 'or', 'admin', '1970-01-01 00:00:01', 'demandDuration', '', '', ''),
|
||||
('lang', '需求BSA', 'demandBSA', '1', 'or', 'admin', '1970-01-01 00:00:01', 'demandBSA', '', '', '');
|
||||
|
||||
DROP VIEW IF EXISTS `view_datasource_2`;
|
||||
DROP VIEW IF EXISTS `view_datasource_3`;
|
||||
DROP VIEW IF EXISTS `view_datasource_4`;
|
||||
DROP VIEW IF EXISTS `view_datasource_5`;
|
||||
DROP VIEW IF EXISTS `view_datasource_6`;
|
||||
@@ -13677,7 +13724,9 @@ DROP VIEW IF EXISTS `view_datasource_41`;
|
||||
DROP VIEW IF EXISTS `view_datasource_54`;
|
||||
DROP VIEW IF EXISTS `view_datasource_55`;
|
||||
|
||||
CREATE VIEW `view_datasource_4` AS select `id`,`title` from `zt_story` where `deleted` = '0';
|
||||
CREATE VIEW `view_datasource_2` AS select `id`,`title` from `zt_story` where `deleted` = '0' and type = 'epic';
|
||||
CREATE VIEW `view_datasource_3` AS select `id`,`title` from `zt_story` where `deleted` = '0' and type = 'requirement';
|
||||
CREATE VIEW `view_datasource_4` AS select `id`,`title` from `zt_story` where `deleted` = '0' and type = 'story';
|
||||
CREATE VIEW `view_datasource_5` AS select `id`,`name` from `zt_task` where `deleted` = '0' and vision = 'rnd';
|
||||
CREATE VIEW `view_datasource_6` AS select `id`,`title` from `zt_bug` where `deleted` = '0';
|
||||
CREATE VIEW `view_datasource_10` AS select `id`,`name` from `zt_build` where `deleted` = '0';
|
||||
|
||||
@@ -1925,6 +1925,11 @@ IPD版:
|
||||
50408 瀑布项目仪表盘区块没有提示文案
|
||||
50411 专业研发看板中新建需求时选择其他区域保存后显示在了默认区域
|
||||
50425 安装首页鼠标悬浮禅道软件公司时的链接不对
|
||||
50431 新手教程点击创建项目按钮报错
|
||||
50436 合并请求列表中开放中筛选标签不能正常筛选
|
||||
50437 无迭代项目下的bug转任务跳转到了添加执行表单
|
||||
50450 升级新版本时,有代码报错
|
||||
50453 多分支产品的关联项目数显示不对
|
||||
50426 新手教程创建账号页面没有引导
|
||||
50427 新手教程创建账号页面没有按顺序提示
|
||||
50428 新手教程创建账号页面页面有多余提示框
|
||||
@@ -1970,6 +1975,7 @@ IPD版:
|
||||
50547 待处理用例编辑页保存后跳转不对
|
||||
50551 搜索没有权限的执行的任务ID有报错
|
||||
50552 提测版本关联了主干+分支时,测试下点测试单有报错
|
||||
>>>>>>> master
|
||||
|
||||
2024-04-30 20.0
|
||||
完成的需求
|
||||
|
||||
@@ -519,8 +519,8 @@ class control extends baseControl
|
||||
if($this->config->edition == 'open') return $fields;
|
||||
if(!empty($this->app->installing) || !empty($this->app->upgrading)) return $fields;
|
||||
|
||||
$moduleName = $moduleName ? $moduleName : $this->app->getModuleName();
|
||||
$methodName = $methodName ? $moduleName : $this->app->getMethodName();
|
||||
$moduleName = $moduleName ? $moduleName : $this->app->rawModule;
|
||||
$methodName = $methodName ? $moduleName : $this->app->rawMethod;
|
||||
|
||||
$flow = $this->loadModel('workflow')->getByModule($moduleName);
|
||||
if(!$flow) return $fields;
|
||||
@@ -546,8 +546,8 @@ class control extends baseControl
|
||||
if($this->config->edition == 'open') return '';
|
||||
if(!empty($this->app->installing) || !empty($this->app->upgrading)) return '';
|
||||
|
||||
$moduleName = $moduleName ? $moduleName : $this->app->getModuleName();
|
||||
$methodName = $methodName ? $methodName : $this->app->getMethodName();
|
||||
$moduleName = $moduleName ? $moduleName : $this->app->rawModule;
|
||||
$methodName = $methodName ? $methodName : $this->app->rawMethod;
|
||||
|
||||
$flow = $this->loadModel('workflow')->getByModule($moduleName);
|
||||
if(!$flow) return '';
|
||||
@@ -585,8 +585,8 @@ class control extends baseControl
|
||||
if($this->config->edition == 'open') return array();
|
||||
if(!empty($this->app->installing) || !empty($this->app->upgrading)) return array();
|
||||
|
||||
$moduleName = $moduleName ? $moduleName : $this->app->getModuleName();
|
||||
$methodName = $methodName ? $moduleName : $this->app->getMethodName();
|
||||
$moduleName = $moduleName ? $moduleName : $this->app->rawModule;
|
||||
$methodName = $methodName ? $moduleName : $this->app->rawMethod;
|
||||
|
||||
if(!$object) $object = new stdclass();
|
||||
|
||||
|
||||
@@ -290,6 +290,7 @@ class model extends baseModel
|
||||
*/
|
||||
public function processStatus($module, $record)
|
||||
{
|
||||
if($module == 'story') $module = $record->type;
|
||||
if($this->config->edition == 'open' or empty($record->subStatus)) return zget($this->lang->$module->statusList, $record->status);
|
||||
|
||||
return $this->loadModel('workflowfield')->processSubStatus($module, $record);
|
||||
|
||||
@@ -258,7 +258,7 @@ class router extends baseRouter
|
||||
|
||||
try
|
||||
{
|
||||
$commonSettings = $this->dbQuery('SELECT `section`, `key`, `value` FROM' . TABLE_CONFIG . "WHERE `owner`='system' AND (`module`='custom' or `module`='common') and `key` in ('sprintConcept', 'hourPoint', 'URSR', 'mode', 'URAndSR', 'scoreStatus', 'disabledFeatures', 'closedFeatures')")->fetchAll();
|
||||
$commonSettings = $this->dbQuery('SELECT `section`, `key`, `value` FROM' . TABLE_CONFIG . "WHERE `owner`='system' AND (`module`='custom' or `module`='common') and `key` in ('sprintConcept', 'hourPoint', 'URSR', 'mode', 'URAndSR', 'enableER', 'scoreStatus', 'disabledFeatures', 'closedFeatures')")->fetchAll();
|
||||
}
|
||||
catch (PDOException $exception)
|
||||
{
|
||||
@@ -266,7 +266,7 @@ class router extends baseRouter
|
||||
}
|
||||
}
|
||||
|
||||
$hourKey = $planKey = $URSR = $URAndSR = 0;
|
||||
$hourKey = $planKey = $URSR = $URAndSR = $enableER = 0;
|
||||
|
||||
$mode = 'ALM';
|
||||
$score = '0';
|
||||
@@ -280,6 +280,7 @@ class router extends baseRouter
|
||||
if($setting->key == 'hourPoint') $hourKey = $setting->value;
|
||||
if($setting->key == 'URSR') $URSR = $setting->value;
|
||||
if($setting->key == 'URAndSR') $URAndSR = $setting->value;
|
||||
if($setting->key == 'enableER') $enableER = $setting->value;
|
||||
if($setting->key == 'mode' and $setting->section == 'global') $mode = $setting->value;
|
||||
if($setting->key == 'scoreStatus' and $setting->section == 'global') $score = $setting->value;
|
||||
if($setting->key == 'disabledFeatures') $disabledFeatures = $setting->value;
|
||||
@@ -313,7 +314,8 @@ class router extends baseRouter
|
||||
|
||||
/* User preference init. */
|
||||
$config->URSR = $URSR;
|
||||
$config->URAndSR = ($URAndSR and !str_contains(",{$config->disabledFeatures},", ',productUR,'));
|
||||
$config->URAndSR = ($URAndSR and !str_contains(",{$config->disabledFeatures},", ',productUR,'));
|
||||
$config->enableER = ($enableER and !str_contains(",{$config->disabledFeatures},", ',productER,'));
|
||||
$config->programLink = 'program-browse';
|
||||
$config->productLink = 'product-all';
|
||||
$config->projectLink = 'project-browse';
|
||||
@@ -338,8 +340,9 @@ class router extends baseRouter
|
||||
if($setting->key == 'executionLink') $config->executionLink = $setting->value;
|
||||
}
|
||||
|
||||
$lang->URCommon = '';
|
||||
$lang->SRCommon = '';
|
||||
$lang->ERCommon = $config->storyCommonList[$this->clientLang]['epic'];
|
||||
$lang->URCommon = $config->storyCommonList[$this->clientLang]['requirement'];
|
||||
$lang->SRCommon = $config->storyCommonList[$this->clientLang]['story'];
|
||||
if($this->dbh and !empty($this->config->db->name))
|
||||
{
|
||||
if(!$this->upgrading)
|
||||
@@ -350,16 +353,19 @@ class router extends baseRouter
|
||||
if(empty($URSRList)) $URSRList = $this->dbQuery('SELECT `key`, `value` FROM' . TABLE_LANG . "WHERE module = 'custom' and `section` = 'URSRList' and `key` = '{$config->URSR}'")->fetchAll();
|
||||
|
||||
/* Get UR pairs and SR pairs. */
|
||||
$ERPairs = array();
|
||||
$URPairs = array();
|
||||
$SRPairs = array();
|
||||
foreach($URSRList as $id => $value)
|
||||
{
|
||||
$URSR = json_decode((string) $value->value);
|
||||
$URPairs[$value->key] = $URSR->URName;
|
||||
$SRPairs[$value->key] = $URSR->SRName;
|
||||
$URSRObj = json_decode((string) $value->value);
|
||||
$ERPairs[$value->key] = $URSRObj->ERName;
|
||||
$URPairs[$value->key] = $URSRObj->URName;
|
||||
$SRPairs[$value->key] = $URSRObj->SRName;
|
||||
}
|
||||
|
||||
/* Set default story concept and init UR and SR concept. */
|
||||
$lang->ERCommon = $ERPairs[$config->URSR] ?? reset($ERPairs);
|
||||
$lang->URCommon = $URPairs[$config->URSR] ?? reset($URPairs);
|
||||
$lang->SRCommon = $SRPairs[$config->URSR] ?? reset($SRPairs);
|
||||
|
||||
@@ -370,7 +376,13 @@ class router extends baseRouter
|
||||
$customMenus = $this->dbQuery('SELECT * FROM' . TABLE_LANG . "WHERE `module`='common' AND `lang`='{$this->clientLang}' AND `section`='' AND `vision`='{$config->vision}'")->fetchAll();
|
||||
}
|
||||
catch(PDOException){}
|
||||
foreach($customMenus as $menu) if(isset($lang->{$menu->key})) $lang->{$menu->key} = $menu->value;
|
||||
|
||||
foreach($customMenus as $menu)
|
||||
{
|
||||
/* Modified the language item of dev story-related navigation, which will only affect users whose personalized value is the same as the story concept default value.*/
|
||||
if(in_array($menu->key, array('ERCommon', 'URCommon', 'SRCommon')) && $URSR != $config->URSR) continue;
|
||||
if(isset($lang->{$menu->key})) $lang->{$menu->key} = $menu->value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1191,6 +1191,8 @@ EOT;
|
||||
$jsConfig->themeRoot = $themeRoot;
|
||||
$jsConfig->currentModule = $moduleName;
|
||||
$jsConfig->currentMethod = $methodName;
|
||||
$jsConfig->rawModule = $app->rawModule;
|
||||
$jsConfig->rawMethod = $app->rawMethod;
|
||||
$jsConfig->clientLang = $clientLang;
|
||||
$jsConfig->requiredFields = $requiredFields;
|
||||
$jsConfig->router = $app->server->SCRIPT_NAME;
|
||||
|
||||
@@ -110,8 +110,8 @@ class form extends fixer
|
||||
global $app, $config;
|
||||
if($config->edition == 'open' || !empty($app->installing)) return $configObject;
|
||||
|
||||
$moduleName = $moduleName ? $moduleName : $app->getModuleName();
|
||||
$methodName = $methodName ? $moduleName : $app->getMethodName();
|
||||
$moduleName = $moduleName ? $moduleName : $app->rawModule;
|
||||
$methodName = $methodName ? $moduleName : $app->rawMethod;
|
||||
|
||||
$flow = $app->control->loadModel('workflow')->getByModule($moduleName);
|
||||
if(!$flow) return $configObject;
|
||||
@@ -193,8 +193,9 @@ class form extends fixer
|
||||
{
|
||||
global $app, $config;
|
||||
|
||||
$module = $app->getModuleName();
|
||||
$module = $app->getModuleName() == 'story' ? $app->rawModule : $app->getModuleName();
|
||||
$method = $app->getMethodName();
|
||||
|
||||
if($method == 'batchcreate') $method = 'create';
|
||||
if($method == 'batchedit') $method = 'edit';
|
||||
|
||||
|
||||
@@ -59,8 +59,7 @@ class node implements \JsonSerializable
|
||||
|
||||
public function __construct(mixed ...$args)
|
||||
{
|
||||
global $config;
|
||||
$this->gid = (isset($config->clientCache) && $config->clientCache) ? static::nextGid() : 'zin_' . uniqid();
|
||||
$this->gid = 'zin_' . uniqid();
|
||||
$this->props = new props();
|
||||
|
||||
disableGlobalRender();
|
||||
|
||||
@@ -19,7 +19,16 @@ window.cancelFormSetting = function(e)
|
||||
|
||||
window.revertDefaultFields = function(e)
|
||||
{
|
||||
$.getJSON($(e.target).closest('button').data('url'), function(response)
|
||||
const $panel = $(e.target).closest('.panel-form');
|
||||
const $form = $(e.target).closest('form');
|
||||
const $globalBox = $panel.find('.form-actions input[name=global]');
|
||||
const submitUrl = $(e.target).closest('button').data('url');
|
||||
|
||||
postData = new FormData();
|
||||
postData.append('action', 'reset');
|
||||
if($globalBox.length > 0) postData.append('global', $globalBox.prop('checked') ? '1' : '0');
|
||||
|
||||
$.post(submitUrl, postData, function(response)
|
||||
{
|
||||
const customFields = response.customFields.split(',');
|
||||
const showFields = response.showFields.split(',');
|
||||
@@ -41,9 +50,14 @@ window.onSubmitFormtSetting = function(e)
|
||||
{
|
||||
const customFields = [];
|
||||
const showFields = [];
|
||||
const $panel = $(e.target).closest('.panel-form');
|
||||
const $form = $(e.target).closest('form');
|
||||
const $globalBox = $panel.find('.form-actions input[name=global]');
|
||||
|
||||
$.post($form.attr('action'), new FormData($form[0]), function()
|
||||
postData = new FormData($form[0]);
|
||||
if($globalBox.length > 0) postData.append('global', $globalBox.prop('checked') ? '1' : '0');
|
||||
|
||||
$.post($form.attr('action'), postData, function()
|
||||
{
|
||||
$form.find('input[type="checkbox"]').each(function()
|
||||
{
|
||||
|
||||
@@ -15,8 +15,13 @@ namespace zin;
|
||||
class formSettingBtn extends wg
|
||||
{
|
||||
protected static array $defaultProps = array(
|
||||
'customFields' => array(),
|
||||
'urlParams' => ''
|
||||
'customFields' => array(),
|
||||
'urlParams' => '',
|
||||
'canGlobal' => false,
|
||||
'submitCallBack' => '',
|
||||
'restoreCallBack' => '',
|
||||
'noCancel' => false,
|
||||
'text' => ''
|
||||
);
|
||||
|
||||
public static function getPageCSS(): ?string
|
||||
@@ -52,18 +57,26 @@ class formSettingBtn extends wg
|
||||
|
||||
protected function build()
|
||||
{
|
||||
list($urlParams, $text, $submitCallback, $restoreCallback, $canGlobal, $noCancel) = $this->prop(array('urlParams', 'text', 'submitCallback', 'restoreCallback', 'canGlobal', 'noCancel'));
|
||||
$customFields = $this->prop('customFields', array());
|
||||
$urlParams = $this->prop('urlParams', '');
|
||||
|
||||
global $lang;
|
||||
|
||||
$customLink = createLink('custom', 'ajaxSaveCustomFields', $this->prop('urlParams', ''));
|
||||
$cancelLink = createLink('custom', 'ajaxGetCustomFields', $this->prop('urlParams', ''));
|
||||
if($canGlobal)
|
||||
{
|
||||
global $app;
|
||||
$app->loadLang('datatable'); // Use lang->datatable->setGlobal variable.
|
||||
}
|
||||
|
||||
$customLink = createLink('custom', 'ajaxSaveCustomFields', $urlParams);
|
||||
$cancelLink = createLink('custom', 'ajaxGetCustomFields', $urlParams);
|
||||
return dropdown
|
||||
(
|
||||
set::arrow('false'),
|
||||
set::placement('bottom-end'),
|
||||
set::id('formSettingBtn'),
|
||||
to::trigger(btn(set::icon('cog-outline'), setClass('ghost'), set::caret(false))),
|
||||
to::trigger(btn(set::icon('cog-outline'), $text, setClass('ghost'), set::caret(false))),
|
||||
to::menu(menu
|
||||
(
|
||||
setClass('dropdown-menu'),
|
||||
@@ -76,11 +89,15 @@ class formSettingBtn extends wg
|
||||
set::showExtra(false),
|
||||
set::actions(array
|
||||
(
|
||||
btn(set::text($lang->save), setClass('primary'), on::click('onSubmitFormtSetting')),
|
||||
btn(set::text($lang->cancel), set::btnType('button'), on::click('cancelFormSetting'), set('data-url', $cancelLink)),
|
||||
btn(set::text($lang->restore), setClass('text-primary ghost'), set::href('#'), set('data-url', $customLink), on::click('revertDefaultFields'))
|
||||
$canGlobal ? checkList(set::name('global'), setClass('whitespace-nowrap'), set::style(array('padding' => 0)), set::inline(true), set::items(array(array('text' => $lang->datatable->setGlobal, 'value' => '1')))) : null,
|
||||
btn(set::text($lang->save), setClass('primary'), on::click('onSubmitFormtSetting'), $submitCallback ? on::click($submitCallback) : null),
|
||||
$noCancel ? null : btn(set::text($lang->cancel), set::btnType('button'), on::click('cancelFormSetting'), set('data-url', $cancelLink)),
|
||||
btn(set::text($lang->restore), setClass('text-primary ghost'), set('data-url', $customLink), on::click('revertDefaultFields'), $restoreCallback ? on::click($restoreCallback) : null)
|
||||
)),
|
||||
to::headingActions(array
|
||||
(
|
||||
btn(set::icon('close'), setClass('ghost'), set::size('sm'), on::click('closeCustomPopupMenu'))
|
||||
)),
|
||||
to::headingActions(array(btn(set::icon('close'), setClass('ghost'), set::size('sm'), on::click('closeCustomPopupMenu')))),
|
||||
$this->buildCustomFields($customFields)
|
||||
)
|
||||
))
|
||||
|
||||
@@ -18,8 +18,8 @@ window.getGlobalSearchType = function()
|
||||
return pageWindow.globalSearchType;
|
||||
}
|
||||
|
||||
const moduleName = pageWindow.config.currentModule;
|
||||
const methodName = pageWindow.config.currentMethod;
|
||||
const moduleName = pageWindow.config.rawModule;
|
||||
const methodName = pageWindow.config.rawMethod;
|
||||
|
||||
if(moduleName == 'product' && methodName == 'browse') return 'story';
|
||||
if(moduleName == 'my' || moduleName == 'user') return methodName;
|
||||
|
||||
@@ -10,7 +10,6 @@ class linkedStoryList extends storyList
|
||||
protected static array $defineProps = array
|
||||
(
|
||||
'story' => '?object', // 被关联的需求。
|
||||
'relationType' => '?string', // 关联类型。
|
||||
'unlinkBtn' => '?array|bool', // 取消关联按钮。
|
||||
'unlinkStoryTip' => '?string', // 取消关联提示信息。
|
||||
'unlinkUrl' => '?string', // 取消关联需求链接
|
||||
@@ -42,21 +41,12 @@ class linkedStoryList extends storyList
|
||||
|
||||
protected null|array|bool $unlinkBtn = null;
|
||||
|
||||
protected ?string $relationType = null;
|
||||
|
||||
protected function beforeBuild()
|
||||
{
|
||||
$story = $this->prop('story');
|
||||
if(!$story) $story = data('story');
|
||||
if(!$story) return;
|
||||
|
||||
$relationType = $this->prop('relationType');
|
||||
if(!$relationType) $relationType = $story->type == 'story' ? 'requirement' : 'story';
|
||||
|
||||
$viewUrl = $this->prop('viewUrl');
|
||||
if($viewUrl === null) $viewUrl = hasPriv($relationType, 'view', null, "storyType=$relationType");
|
||||
if($viewUrl === true) $viewUrl = createLink('story', 'view', "id={id}&version=0¶m=0&storyType=$relationType");
|
||||
|
||||
$unlinkUrl = $this->prop('unlinkUrl');
|
||||
if($unlinkUrl === null) $unlinkUrl = createLink('story', 'linkStory', "storyID={$story->id}&type=remove&linkedID={id}&browseType=&queryID=0&storyType=$story->type");
|
||||
|
||||
@@ -64,11 +54,9 @@ class linkedStoryList extends storyList
|
||||
$unlinkBtn = $this->prop('unlinkBtn');
|
||||
if($unlinkBtn === null) $unlinkBtn = $canLink;
|
||||
|
||||
$this->story = $story;
|
||||
$this->relationType = $relationType;
|
||||
$this->viewUrl = $viewUrl;
|
||||
$this->unlinkUrl = $unlinkUrl;
|
||||
$this->unlinkBtn = $unlinkBtn;
|
||||
$this->story = $story;
|
||||
$this->unlinkUrl = $unlinkUrl;
|
||||
$this->unlinkBtn = $unlinkBtn;
|
||||
}
|
||||
|
||||
protected function getItem(object $story): array
|
||||
@@ -90,7 +78,7 @@ class linkedStoryList extends storyList
|
||||
'data-url' => str_replace('{id}', "$story->id", $this->unlinkUrl),
|
||||
'data-params' => 'event',
|
||||
'data-call' => 'unlinkStory',
|
||||
'hint' => $story->type == 'requirement' ? str_replace($lang->SRCommon, $lang->URCommon, $lang->story->unlinkStory) : $lang->story->unlinkStory
|
||||
'hint' => $lang->story->unlink
|
||||
);
|
||||
|
||||
if(is_array($unlinkBtn)) $btn = array_merge($btn, $unlinkBtn);
|
||||
@@ -135,7 +123,7 @@ class linkedStoryList extends storyList
|
||||
setData(array('toggle' => 'modal', 'size' => 'lg')),
|
||||
setID('linkButton'),
|
||||
is_array($newLinkBtn) ? set($newLinkBtn) : null,
|
||||
$lang->story->link . ($story->type == 'story' ? $lang->story->requirement : $lang->story->story)
|
||||
$lang->story->linkStory
|
||||
);
|
||||
return array($list, $btn);
|
||||
}
|
||||
|
||||
@@ -76,6 +76,7 @@ class priPicker extends wg
|
||||
$moduleName = $app->getModuleName();
|
||||
if(isset($lang->$moduleName->priList)) $props['items'] = $lang->$moduleName->priList;
|
||||
}
|
||||
if(isset($props['items'][0])) unset($props['items'][0]);
|
||||
return zui::priPicker
|
||||
(
|
||||
set::_class('form-group-wrapper'),
|
||||
|
||||
@@ -9,7 +9,8 @@ class searchToggle extends wg
|
||||
'open?: bool',
|
||||
'module?: string',
|
||||
'url?: string',
|
||||
'searchUrl?: string'
|
||||
'searchUrl?: string',
|
||||
'text?: string'
|
||||
);
|
||||
|
||||
public static function getPageCSS(): ?string
|
||||
@@ -29,10 +30,11 @@ class searchToggle extends wg
|
||||
protected function build()
|
||||
{
|
||||
global $lang, $app, $config;
|
||||
list($target, $module, $open, $url, $searchUrl) = $this->prop(array('target', 'module', 'open', 'url', 'searchUrl'));
|
||||
list($target, $module, $open, $url, $searchUrl, $text) = $this->prop(array('target', 'module', 'open', 'url', 'searchUrl', 'text'));
|
||||
|
||||
if(is_null($open) && !empty($_GET['browseType'])) $open = $_GET['browseType'] === 'bySearch';
|
||||
if(is_null($module)) $module = $app->rawModule;
|
||||
if(empty($text)) $text = $lang->searchAB;
|
||||
|
||||
if(isset($config->zin->mode) && $config->zin->mode == 'compatible')
|
||||
{
|
||||
@@ -45,7 +47,7 @@ class searchToggle extends wg
|
||||
set::className('search-form-toggle rounded-full gray-300-outline size-sm'),
|
||||
set::icon('search'),
|
||||
set::active($open),
|
||||
set::text($lang->searchAB),
|
||||
set::text($text),
|
||||
toggle::searchform(array('module' => $module, 'target' => $target, 'url' => $url, 'searchUrl' => $searchUrl)),
|
||||
$open ? on::init()->call('zui.toggleSearchForm', array('module' => $module, 'target' => $target, 'show' => $open, 'url' => $url, 'searchUrl' => $searchUrl)) : on::init()->do('$element.closest(".show-search-form").removeClass("show-search-form").find(".search-form[data-module=' . $module . ']").remove()')
|
||||
);
|
||||
|
||||
@@ -78,6 +78,8 @@ class storyBasicInfo extends wg
|
||||
$hiddenPlan = $this->prop('hiddenPlan', data('hiddenPlan'));
|
||||
$statusText = $this->prop('statusText', $story->status);
|
||||
$users = $this->prop('users', data('users'));
|
||||
$gradePairs = $this->prop('gradePairs', data('gradePairs'));
|
||||
$showGrade = $this->prop('showGrade', data('showGrade'));
|
||||
$items = array();
|
||||
|
||||
if(!$product->shadow)
|
||||
@@ -93,7 +95,22 @@ class storyBasicInfo extends wg
|
||||
'control' => 'breadcrumb',
|
||||
'items' => $this->getModuleItems($story, $product)
|
||||
);
|
||||
if($story->type != 'requirement' and $story->parent != -1 and !$hiddenPlan)
|
||||
if(isset($story->parentName))
|
||||
{
|
||||
$items[$lang->story->parent] = array(
|
||||
'control' => 'text',
|
||||
'content' => $story->parentName
|
||||
);
|
||||
}
|
||||
if($showGrade)
|
||||
{
|
||||
$items[$lang->story->grade] = array
|
||||
(
|
||||
'control' => 'text',
|
||||
'content' => zget($gradePairs, $story->grade)
|
||||
);
|
||||
}
|
||||
if(!$hiddenPlan)
|
||||
{
|
||||
$planTitleItems = array();
|
||||
if(isset($story->planTitle) && $story->planTitle)
|
||||
@@ -104,7 +121,7 @@ class storyBasicInfo extends wg
|
||||
(
|
||||
'control' => 'link',
|
||||
'url' => createLink('productplan', 'view', "planID=$planID"),
|
||||
'text' => $planTitle
|
||||
'text' => $planTitle . ' '
|
||||
) : $planTitle;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,4 +10,53 @@ class storyList extends entitylist
|
||||
(
|
||||
'type' => 'story'
|
||||
);
|
||||
|
||||
protected function getItem(object $entity): array
|
||||
{
|
||||
$item = array
|
||||
(
|
||||
'innerClass' => 'px-0 relative group',
|
||||
'leading' => array(),
|
||||
'innerTag' => 'div',
|
||||
'titleClass' => 'flex gap-2 items-center flex-auto min-w-0',
|
||||
'textClass' => 'flex-none',
|
||||
'actionsClass' => $this->compact ? 'absolute top-0.5 right-0' : null,
|
||||
'hint' => $entity->title,
|
||||
'title' => span(setClass('text-clip'), $entity->title),
|
||||
'leading' => idLabel::create($entity->id)
|
||||
);
|
||||
|
||||
if(hasPriv($entity->type, 'view'))
|
||||
{
|
||||
$item['titleAttrs'] = array('data-toggle' => 'modal', 'data-size' => 'lg');
|
||||
$item['url'] = createLink($entity->type, 'view', "id=$entity->id");
|
||||
}
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
protected function getItems()
|
||||
{
|
||||
global $lang;
|
||||
$items = $this->prop('items', array());
|
||||
$list = array();
|
||||
|
||||
foreach($items as $key => $entity)
|
||||
{
|
||||
if(is_object($entity))
|
||||
{
|
||||
$list[] = $this->getItem($entity);
|
||||
}
|
||||
elseif(is_array($entity))
|
||||
{
|
||||
$list[] = array('id' => $key, 'title' => $lang->story->typeList[$key]);
|
||||
foreach($entity as $subEntity)
|
||||
{
|
||||
$list[] = $this->getItem($subEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,6 @@ class storyRelatedList extends relatedList
|
||||
$cases = $this->prop('cases', data('cases'));
|
||||
$builds = $this->prop('builds', data('builds'));
|
||||
$releases = $this->prop('releases', data('releases'));
|
||||
$storyProducts = $this->prop('storyProducts', data('storyProducts'));
|
||||
$linkedMRs = $this->prop('linkedMRs', data('linkedMRs'));
|
||||
$linkedCommits = $this->prop('linkedCommits', data('linkedCommits'));
|
||||
$data = array();
|
||||
@@ -90,26 +89,6 @@ class storyRelatedList extends relatedList
|
||||
);
|
||||
}
|
||||
|
||||
$data['story'] = array
|
||||
(
|
||||
'title' => $lang->story->linkStories,
|
||||
'items' => isset($story->linkStoryList) ? $story->linkStoryList : array(),
|
||||
'url' => false,
|
||||
'statusList' => $lang->story->statusList,
|
||||
'onRender' => function($item, $linkedStory) use($storyProducts, $story, $app)
|
||||
{
|
||||
$storyID = $linkedStory->id;
|
||||
$hasPriv = ($app->user->admin || str_contains(",{$app->user->view->products},", ",{$storyProducts[$storyID]},"));
|
||||
$item['url'] = $hasPriv ? createLink('story', 'view', "storyID=$storyID&version=0¶m=0&storyType=$story->type") : false;
|
||||
if($hasPriv)
|
||||
{
|
||||
$item['data-toggle'] = 'modal';
|
||||
$item['data-size'] = 'lg';
|
||||
}
|
||||
return $item;
|
||||
}
|
||||
);
|
||||
|
||||
if($isStoryType && helper::hasFeature('devops'))
|
||||
{
|
||||
$data['mr'] = array
|
||||
|
||||
Vendored
+1
-3
@@ -628,7 +628,7 @@ pipeline {
|
||||
def j = job.newJob()
|
||||
j.SaveHeadReport('./.head.md')
|
||||
ximNotify(title: "禅道镜像构建成功", contentFile: "report.md", contentHeaderFile: './.head.md')
|
||||
|
||||
|
||||
if (env.DEPLOY_ZENTAOPAAS=='true') {
|
||||
deploy.upgradePaas(env.ZENTAOPAAS_SERVER, env.CNE_API_TOKEN)
|
||||
ximNotify(title: "禅道平台版DEMO部署成功", content: "访问地址 ["+env.ZENTAOPAAS_SERVER+"]("+env.ZENTAOPAAS_SERVER+")")
|
||||
@@ -701,5 +701,3 @@ pipeline {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<?php
|
||||
$config->action->objectNameFields['product'] = 'name';
|
||||
$config->action->objectNameFields['epic'] = 'title';
|
||||
$config->action->objectNameFields['story'] = 'title';
|
||||
$config->action->objectNameFields['requirement'] = 'title';
|
||||
$config->action->objectNameFields['productplan'] = 'title';
|
||||
@@ -82,7 +83,7 @@ $config->action->majorList['execution'] = array('opened', 'edited');
|
||||
$config->action->majorList['doc'] = array('releaseddoc');
|
||||
|
||||
$config->action->needGetProjectType = 'build,task,bug,case,testcase,caselib,testtask,testsuite,testreport,doc,issue,release,risk,design,opportunity,trainplan,gapanalysis,researchplan,researchreport,';
|
||||
$config->action->needGetRelateField = ',branch,story,productplan,release,task,build,bug,testcase,case,testtask,testreport,doc,doclib,issue,risk,opportunity,trainplan,gapanalysis,team,whitelist,researchplan,researchreport,meeting,kanbanlane,kanbancolumn,module,review,';
|
||||
$config->action->needGetRelateField = ',branch,story,epic,requirement,productplan,release,task,build,bug,testcase,case,testtask,testreport,doc,doclib,issue,risk,opportunity,trainplan,gapanalysis,team,whitelist,researchplan,researchreport,meeting,kanbanlane,kanbancolumn,module,review,';
|
||||
$config->action->noLinkModules = ',doclib,module,webhook,gitlab,instance,gitea,gogs,sonarqube,pipeline,jenkins,kanban,kanbanspace,kanbancolumn,kanbanlane,kanbanregion,kanbancard,execution,project,traincategory,apistruct,program,product,user,entry,repo,pivot,scene,boardspace,auditplan,auditresult,';
|
||||
$config->action->ignoreObjectType4Dynamic = 'kanbanregion,kanbanlane,kanbancolumn';
|
||||
$config->action->ignoreActions4Dynamic = 'disconnectxuanxuan,reconnectxuanxuan,loginxuanxuan,logoutxuanxuan,editmr,removemr';
|
||||
|
||||
@@ -104,6 +104,7 @@ $lang->action->dynamic->search = 'Suche';
|
||||
|
||||
$lang->action->objectTypes['product'] = $lang->productCommon;
|
||||
$lang->action->objectTypes['branch'] = 'Branch';
|
||||
$lang->action->objectTypes['epic'] = $lang->ERCommon;
|
||||
$lang->action->objectTypes['story'] = $lang->SRCommon;
|
||||
$lang->action->objectTypes['requirement'] = $lang->URCommon;
|
||||
$lang->action->objectTypes['design'] = 'Design';
|
||||
@@ -199,6 +200,7 @@ $lang->action->desc->edited = '$date, bearbeitet von <strong>$acto
|
||||
$lang->action->desc->assigned = '$date, <strong>$actor</strong> zugewisen an <strong>$extra</strong>.' . "\n";
|
||||
$lang->action->desc->closed = '$date, geschlossen von <strong>$actor</strong> .' . "\n";
|
||||
$lang->action->desc->closedbysystem = '$date, closed by system.' . "\n";
|
||||
$lang->action->desc->closedbyparent = '$date, closed by system.' . "\n";
|
||||
$lang->action->desc->deleted = '$date, gelöscht von <strong>$actor</strong> .' . "\n";
|
||||
$lang->action->desc->deletedfile = '$date, <strong>$actor</strong> löschte <strong><i>$extra</i></strong>.' . "\n";
|
||||
$lang->action->desc->editfile = '$date, <strong>$actor</strong> bearbeitete <strong><i>$extra</i></strong>.' . "\n";
|
||||
@@ -442,6 +444,7 @@ $lang->action->label->unlinkchildrenstory = "unlinked a child story";
|
||||
$lang->action->label->linkparentstory = "linked a parent story";
|
||||
$lang->action->label->unlinkparentstory = "unlink from parent story";
|
||||
$lang->action->label->deletechildrenstory = "delete children story";
|
||||
$lang->action->label->syncgrade = 'synced grade';
|
||||
$lang->action->label->tracked = 'tracked';
|
||||
$lang->action->label->hangup = 'hangup';
|
||||
$lang->action->label->run = 'run';
|
||||
|
||||
@@ -104,6 +104,7 @@ $lang->action->dynamic->search = 'Search';
|
||||
|
||||
$lang->action->objectTypes['product'] = $lang->productCommon;
|
||||
$lang->action->objectTypes['branch'] = 'Branch';
|
||||
$lang->action->objectTypes['epic'] = $lang->ERCommon;
|
||||
$lang->action->objectTypes['story'] = $lang->SRCommon;
|
||||
$lang->action->objectTypes['requirement'] = $lang->URCommon;
|
||||
$lang->action->objectTypes['design'] = 'Design';
|
||||
@@ -199,6 +200,7 @@ $lang->action->desc->edited = '$date, edited by <strong>$actor</st
|
||||
$lang->action->desc->assigned = '$date, <strong>$actor</strong> assigned to <strong>$extra</strong>.' . "\n";
|
||||
$lang->action->desc->closed = '$date, closed by <strong>$actor</strong> .' . "\n";
|
||||
$lang->action->desc->closedbysystem = '$date, closed by system.' . "\n";
|
||||
$lang->action->desc->closedbyparent = '$date, closed by system.' . "\n";
|
||||
$lang->action->desc->deleted = '$date, deleted by <strong>$actor</strong> .' . "\n";
|
||||
$lang->action->desc->deletedfile = '$date, <strong>$actor</strong> deleted <strong><i>$extra</i></strong>.' . "\n";
|
||||
$lang->action->desc->editfile = '$date, <strong>$actor</strong> edited <strong><i>$extra</i></strong>.' . "\n";
|
||||
@@ -442,6 +444,7 @@ $lang->action->label->unlinkchildrenstory = "unlinked a child story";
|
||||
$lang->action->label->linkparentstory = "linked a parent story";
|
||||
$lang->action->label->unlinkparentstory = "unlink from parent story";
|
||||
$lang->action->label->deletechildrenstory = "delete children story";
|
||||
$lang->action->label->syncgrade = 'synced grade';
|
||||
$lang->action->label->tracked = 'tracked';
|
||||
$lang->action->label->hangup = 'hangup';
|
||||
$lang->action->label->run = 'run';
|
||||
|
||||
@@ -104,6 +104,7 @@ $lang->action->dynamic->search = 'Rechercher';
|
||||
|
||||
$lang->action->objectTypes['product'] = $lang->productCommon;
|
||||
$lang->action->objectTypes['branch'] = 'Branche';
|
||||
$lang->action->objectTypes['epic'] = $lang->ERCommon;
|
||||
$lang->action->objectTypes['story'] = $lang->SRCommon;
|
||||
$lang->action->objectTypes['requirement'] = $lang->URCommon;
|
||||
$lang->action->objectTypes['design'] = 'Design';
|
||||
@@ -199,6 +200,7 @@ $lang->action->desc->edited = '$date, édité par <strong>$actor</
|
||||
$lang->action->desc->assigned = '$date, <strong>$actor</strong> affecté à <strong>$extra</strong>.' . "\n";
|
||||
$lang->action->desc->closed = '$date, fermé par <strong>$actor</strong> .' . "\n";
|
||||
$lang->action->desc->closedbysystem = '$date, closed by system.' . "\n";
|
||||
$lang->action->desc->closedbyparent = '$date, closed by system.' . "\n";
|
||||
$lang->action->desc->deleted = '$date, supprimé par <strong>$actor</strong> .' . "\n";
|
||||
$lang->action->desc->deletedfile = '$date, <strong>$actor</strong> a supprimé <strong><i>$extra</i></strong>.' . "\n";
|
||||
$lang->action->desc->editfile = '$date, <strong>$actor</strong> a édité <strong><i>$extra</i></strong>.' . "\n";
|
||||
@@ -442,6 +444,7 @@ $lang->action->label->unlinkchildrenstory = "a décroché de la sous-story";
|
||||
$lang->action->label->linkparentstory = "a raccroché à une story parent";
|
||||
$lang->action->label->unlinkparentstory = "a décroché de la story parent";
|
||||
$lang->action->label->deletechildrenstory = "a supprimé la sous-story";
|
||||
$lang->action->label->syncgrade = 'synced grade';
|
||||
$lang->action->label->tracked = 'tracked';
|
||||
$lang->action->label->hangup = 'hangup';
|
||||
$lang->action->label->run = 'run';
|
||||
|
||||
@@ -104,6 +104,7 @@ $lang->action->dynamic->search = '搜索';
|
||||
|
||||
$lang->action->objectTypes['product'] = $lang->productCommon;
|
||||
$lang->action->objectTypes['branch'] = '分支';
|
||||
$lang->action->objectTypes['epic'] = $lang->ERCommon;
|
||||
$lang->action->objectTypes['story'] = $lang->SRCommon;
|
||||
$lang->action->objectTypes['requirement'] = $lang->URCommon;
|
||||
$lang->action->objectTypes['design'] = '设计';
|
||||
@@ -199,6 +200,7 @@ $lang->action->desc->edited = '$date, 由 <strong>$actor</strong>
|
||||
$lang->action->desc->assigned = '$date, 由 <strong>$actor</strong> 指派给 <strong>$extra</strong>。' . "\n";
|
||||
$lang->action->desc->closed = '$date, 由 <strong>$actor</strong> 关闭。' . "\n";
|
||||
$lang->action->desc->closedbysystem = '$date, 由系统关闭。' . "\n";
|
||||
$lang->action->desc->closedbyparent = '$date, 由系统关闭。' . "\n";
|
||||
$lang->action->desc->deleted = '$date, 由 <strong>$actor</strong> 删除。' . "\n";
|
||||
$lang->action->desc->deletedfile = '$date, 由 <strong>$actor</strong> 删除了附件:<strong><i>$extra</i></strong>。' . "\n";
|
||||
$lang->action->desc->editfile = '$date, 由 <strong>$actor</strong> 编辑了附件:<strong><i>$extra</i></strong>。' . "\n";
|
||||
@@ -402,8 +404,8 @@ $lang->action->label->linkrelatedbug = "关联了相关Bug";
|
||||
$lang->action->label->unlinkrelatedbug = "移除了相关Bug";
|
||||
$lang->action->label->linkrelatedcase = "关联了相关用例";
|
||||
$lang->action->label->unlinkrelatedcase = "移除了相关用例";
|
||||
$lang->action->label->linkrelatedstory = "关联了相关{$lang->SRCommon}";
|
||||
$lang->action->label->unlinkrelatedstory = "移除了相关{$lang->SRCommon}";
|
||||
$lang->action->label->linkrelatedstory = "关联了相关需求";
|
||||
$lang->action->label->unlinkrelatedstory = "移除了相关需求";
|
||||
$lang->action->label->subdividestory = "细分了{$lang->SRCommon}";
|
||||
$lang->action->label->unlinkchildstory = "移除了细分{$lang->SRCommon}";
|
||||
$lang->action->label->started = '开始了';
|
||||
@@ -442,6 +444,7 @@ $lang->action->label->unlinkchildrenstory = "取消关联了子需求";
|
||||
$lang->action->label->linkparentstory = "关联到了父需求";
|
||||
$lang->action->label->unlinkparentstory = "从父需求取消关联了";
|
||||
$lang->action->label->deletechildrenstory = "删除了子需求";
|
||||
$lang->action->label->syncgrade = '同步了层级';
|
||||
$lang->action->label->tracked = '跟踪了';
|
||||
$lang->action->label->hangup = '挂起了';
|
||||
$lang->action->label->run = '执行了';
|
||||
|
||||
+30
-13
@@ -197,16 +197,17 @@ class actionModel extends model
|
||||
if(($actionName == 'finished' && $objectType == 'todo') || ($actionName == 'closed' && in_array($action->objectType, array('story', 'demand'))) || ($actionName == 'resolved' && $action->objectType == 'bug')) $this->actionTao->processAppendLinkByExtra($action);
|
||||
if($actionName == 'distributed' && $objectType == 'story') $this->actionTao->processActionExtra(TABLE_DEMAND, $action, 'title', 'demand', 'view');
|
||||
|
||||
if(in_array($actionName, array('retracted', 'restored')) && $action->objectType != 'demand') $this->actionTao->processActionExtra(TABLE_STORY, $action, 'title', 'story', 'view');
|
||||
if(in_array($actionName, array('retracted', 'restored')) && $action->objectType != 'demand') $this->actionTao->processActionExtra(TABLE_STORY, $action, 'title', 'story', 'storyView');
|
||||
if(in_array($actionName, array('totask', 'linkchildtask', 'unlinkchildrentask', 'linkparenttask', 'unlinkparenttask', 'deletechildrentask', 'converttotask')) && $action->objectType != 'feedback') $this->actionTao->processActionExtra(TABLE_TASK, $action, 'name', 'task', 'view');;
|
||||
if(in_array($actionName, array('linkchildstory', 'unlinkchildrenstory', 'linkparentstory', 'unlinkparentstory', 'deletechildrenstory'))) $this->actionTao->processActionExtra(TABLE_STORY, $action, 'title', 'story', 'view');
|
||||
if(in_array($actionName, array('linkchildstory', 'unlinkchildrenstory', 'linkparentstory', 'unlinkparentstory', 'deletechildrenstory'))) $this->actionTao->processActionExtra(TABLE_STORY, $action, 'title', 'story', 'storyView');
|
||||
if(in_array($actionName, array('testtaskopened', 'testtaskstarted', 'testtaskclosed'))) $this->actionTao->processActionExtra(TABLE_TESTTASK, $action, 'name', 'testtask', 'view');
|
||||
if(in_array($actionName, array('importfromstorylib', 'importfromrisklib', 'importfromissuelib', 'importfromopportunitylib')) && in_array($this->config->edition, array('max', 'ipd'))) $this->actionTao->processActionExtra(TABLE_ASSETLIB, $action, 'name', 'assetlib', $action->objectType);
|
||||
if(in_array($actionName, array('opened', 'managed', 'edited')) && in_array($objectType, array('execution', 'project'))) $this->processExecutionAndProjectActionExtra($action);
|
||||
if(in_array($actionName, array('linkstory', 'unlinkstory', 'createchildrenstory', 'linkur', 'unlinkur'))) $this->actionTao->processLinkStoryAndBugActionExtra($action, 'story', 'view');
|
||||
if(in_array($actionName, array('linkstory', 'unlinkstory', 'createchildrenstory', 'linkur', 'unlinkur', 'linkrelatedstory', 'unlinkrelatedstory'))) $this->actionTao->processLinkStoryAndBugActionExtra($action, 'story', 'storyView');
|
||||
if(in_array($actionName, array('linkbug', 'unlinkbug'))) $this->actionTao->processLinkStoryAndBugActionExtra($action, 'bug', 'view');
|
||||
if($actionName == 'repocreated') $action->extra = str_replace("class='iframe'", 'data-app="devops"', $action->extra);
|
||||
if($actionName == 'createdsnapshot' && in_array($action->objectType, array('vm', 'zanode')) && $action->extra == 'defaultSnap') $action->actor = $this->lang->action->system;
|
||||
if($actionName == 'syncgrade') $this->actionTao->processStoryGradeActionExtra($action);
|
||||
|
||||
$action->history = zget($histories, $actionID, array());
|
||||
foreach($action->history as $history)
|
||||
@@ -610,7 +611,19 @@ class actionModel extends model
|
||||
$extra = strtolower($action->extra);
|
||||
|
||||
/* Fix bug #741. */
|
||||
if(isset($desc['extra'])) $desc['extra'] = $this->lang->{$objectType}->{$desc['extra']};
|
||||
if(isset($desc['extra']))
|
||||
{
|
||||
if($objectType == 'story')
|
||||
{
|
||||
$story = $this->fetchByID($action->objectID, $objectType);
|
||||
if($story->type != 'story') $this->app->loadLang($story->type);
|
||||
$desc['extra'] = $this->lang->{$story->type}->{$desc['extra']};
|
||||
}
|
||||
else
|
||||
{
|
||||
$desc['extra'] = $this->lang->{$objectType}->{$desc['extra']};
|
||||
}
|
||||
}
|
||||
|
||||
$actionDesc = '';
|
||||
if(isset($desc['extra'][$extra]))
|
||||
@@ -956,7 +969,7 @@ class actionModel extends model
|
||||
|
||||
/* 通过action获取对象名称,所属项目以及需求。 */
|
||||
/* Get object names, object projects and requirements by actions. */
|
||||
list($objectNames, $relatedProjects, $requirements) = $this->getRelatedDataByActions($actions);
|
||||
list($objectNames, $relatedProjects, $requirements, $epics) = $this->getRelatedDataByActions($actions);
|
||||
|
||||
$projectIdList = array();
|
||||
foreach($relatedProjects as $objectType => $idList) $projectIdList = array_merge($projectIdList, $idList);
|
||||
@@ -998,7 +1011,7 @@ class actionModel extends model
|
||||
$action->date = date(DT_MONTHTIME2, strtotime($action->date));
|
||||
$action->actionLabel = isset($this->lang->{$objectType}->{$actionType}) ? $this->lang->{$objectType}->{$actionType} : $action->action;
|
||||
$action->actionLabel = isset($this->lang->action->label->{$actionType}) ? $this->lang->action->label->{$actionType} : $action->actionLabel;
|
||||
$action->objectLabel = $this->getObjectLabel($objectType, $action->objectID, $actionType, $requirements);
|
||||
$action->objectLabel = $this->getObjectLabel($objectType, $action->objectID, $actionType, $requirements, $epics);
|
||||
$action->major = isset($this->config->action->majorList[$action->objectType]) && in_array($action->action, $this->config->action->majorList[$action->objectType]) ? 1 : 0;
|
||||
if($actionType == 'svncommited' || $actionType == 'gitcommited') $action->actor = zget($commiters, $action->actor);
|
||||
|
||||
@@ -1028,7 +1041,7 @@ class actionModel extends model
|
||||
if(isset($objectTypes['branch'])) $this->app->loadLang('branch');
|
||||
$users = isset($objectTypes['gapanalysis']) || isset($objectTypes['stakeholder']) ? $this->loadModel('user')->getPairs('noletter') : array();
|
||||
|
||||
$objectNames = $relatedProjects = $requirements = array();
|
||||
$objectNames = $relatedProjects = $requirements = $epics = array();
|
||||
foreach($objectTypes as $objectType => $objectIdList)
|
||||
{
|
||||
if(!isset($this->config->objectTables[$objectType]) && $objectType != 'makeup') continue; // If no defination for this type, omit it.
|
||||
@@ -1039,7 +1052,7 @@ class actionModel extends model
|
||||
if(empty($field)) continue;
|
||||
|
||||
/* Get object name, related projects, requirements. */
|
||||
list($objectName, $relatedProject, $requirements) = $this->getObjectRelatedData($table, $objectType, $objectIdList, $field, $users, $requirements);
|
||||
list($objectName, $relatedProject, $requirements, $epics) = $this->getObjectRelatedData($table, $objectType, $objectIdList, $field, $users, $requirements, $epics);
|
||||
if($objectType == 'branch' && in_array(BRANCH_MAIN, $objectIdList)) $objectName[BRANCH_MAIN] = $this->lang->branch->main;
|
||||
|
||||
$objectNames[$objectType] = $objectName;
|
||||
@@ -1048,7 +1061,7 @@ class actionModel extends model
|
||||
|
||||
$objectNames['user'][0] = 'guest'; // Add guest account.
|
||||
|
||||
return array($objectNames, $relatedProjects, $requirements);
|
||||
return array($objectNames, $relatedProjects, $requirements, $epics);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1059,10 +1072,11 @@ class actionModel extends model
|
||||
* @param int $objectID
|
||||
* @param string $actionType
|
||||
* @param array $requirements
|
||||
* @param array $epics
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getObjectLabel(string $objectType, int $objectID, string $actionType, array $requirements): string
|
||||
public function getObjectLabel(string $objectType, int $objectID, string $actionType, array $requirements, array $epics): string
|
||||
{
|
||||
$actionObjectLabel = $objectType;
|
||||
if(isset($this->lang->action->label->{$objectType}))
|
||||
@@ -1072,6 +1086,7 @@ class actionModel extends model
|
||||
/* 用户故事替换为需求。 */
|
||||
/* Replace story to requirement. */
|
||||
if(isset($requirements[$objectID]) && is_string($objectLabel)) $objectLabel = str_replace($this->lang->SRCommon, $this->lang->URCommon, $objectLabel);
|
||||
if(isset($epics[$objectID]) && is_string($objectLabel)) $objectLabel = str_replace($this->lang->SRCommon, $this->lang->ERCommon, $objectLabel);
|
||||
|
||||
if(!is_array($objectLabel)) $actionObjectLabel = $objectLabel;
|
||||
if(is_array($objectLabel) && isset($objectLabel[$actionType])) $actionObjectLabel = $objectLabel[$actionType];
|
||||
@@ -1843,10 +1858,11 @@ class actionModel extends model
|
||||
* @param string $field
|
||||
* @param array $users
|
||||
* @param array $requirements
|
||||
* @param array $epics
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getObjectRelatedData(string $table, string $objectType, array $objectIdList, string $field, array $users, array $requirements): array
|
||||
public function getObjectRelatedData(string $table, string $objectType, array $objectIdList, string $field, array $users, array $requirements, array $epics): array
|
||||
{
|
||||
$objectName = array();
|
||||
$relatedProject = array();
|
||||
@@ -1878,7 +1894,8 @@ class actionModel extends model
|
||||
{
|
||||
$objectName[$object->id] = $object->title;
|
||||
if($object->type == 'requirement') $requirements[$object->id] = $object->id;
|
||||
if($object->type == 'project') $relatedProject[$object->id] = $object->id;
|
||||
if($object->type == 'epic') $epics[$object->id] = $object->id;
|
||||
if($object->type == 'project') $relatedProject[$object->id] = $object->id;
|
||||
}
|
||||
}
|
||||
elseif($objectType == 'stakeholder') // Get stakeholder realname.
|
||||
@@ -1890,7 +1907,7 @@ class actionModel extends model
|
||||
{
|
||||
$objectName = $this->dao->select("id, {$field} AS name")->from($table)->where('id')->in($objectIdList)->orderBy('id_asc')->fetchPairs();
|
||||
}
|
||||
return array($objectName, $relatedProject, $requirements);
|
||||
return array($objectName, $relatedProject, $requirements, $epics);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+22
-1
@@ -449,6 +449,24 @@ class actionTao extends actionModel
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理需求层级。
|
||||
* Process story grade.
|
||||
*
|
||||
* @param object $action
|
||||
* @access public
|
||||
* @return object
|
||||
*/
|
||||
public function processStoryGradeActionExtra($action): object
|
||||
{
|
||||
$action->extra = $this->dao->select('t1.name')->from(TABLE_STORYGRADE)->alias('t1')
|
||||
->leftJoin(TABLE_STORY)->alias('t2')->on('t1.grade = t2.grade AND t1.type = t2.type')
|
||||
->where('t2.id')->eq($action->objectID)
|
||||
->fetch('name');
|
||||
|
||||
return $action;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理属性。
|
||||
* Process attribute.
|
||||
@@ -488,6 +506,9 @@ class actionTao extends actionModel
|
||||
break;
|
||||
case 'execution':
|
||||
case 'kanban':
|
||||
case 'story':
|
||||
$paramString = "{$type}ID={$action->extra}";
|
||||
break;
|
||||
case 'task':
|
||||
case 'story':
|
||||
case 'charter':
|
||||
@@ -593,7 +614,7 @@ class actionTao extends actionModel
|
||||
public function processLinkStoryAndBugActionExtra(object $action, string $module, string $method): void
|
||||
{
|
||||
$extra = '';
|
||||
foreach(explode(',', $action->extra) as $id) $extra .= common::hasPriv($module, $method) ? html::a(helper::createLink($module, $method, "{$module}ID={$id}"), "#{$id} ", '', "data-size='lg' data-load='modal'") . ', ' : "#{$id}, ";
|
||||
foreach(explode(',', $action->extra) as $id) $extra .= common::hasPriv($module, $method) ? html::a(helper::createLink($module, $method, "{$module}ID={$id}"), "#{$id} ", '', "data-size='lg' data-toggle='modal'") . ', ' : "#{$id}, ";
|
||||
$action->extra = trim(trim($extra), ',');
|
||||
}
|
||||
|
||||
|
||||
@@ -20,8 +20,8 @@ $config->admin->menuGroup['ai'] = array('ai|adminindex', 'ai|prompts', 'a
|
||||
|
||||
$config->admin->menuModuleGroup['model']['custom|set'] = array('project', 'issue', 'risk', 'opportunity', 'nc');
|
||||
$config->admin->menuModuleGroup['model']['custom|required'] = array('project', 'build');
|
||||
$config->admin->menuModuleGroup['feature']['custom|set'] = array('todo', 'block', 'story', 'task', 'bug', 'testcase', 'testtask', 'feedback', 'user', 'ticket');
|
||||
$config->admin->menuModuleGroup['feature']['custom|required'] = array('bug', 'doc', 'product', 'story', 'productplan', 'release', 'task', 'testcase', 'testsuite', 'testtask', 'testreport', 'caselib', 'doc', 'feedback', 'user', 'execution');
|
||||
$config->admin->menuModuleGroup['feature']['custom|set'] = array('todo', 'block', 'epic', 'requirement', 'story', 'task', 'bug', 'testcase', 'testtask', 'feedback', 'user', 'ticket');
|
||||
$config->admin->menuModuleGroup['feature']['custom|required'] = array('bug', 'doc', 'product', 'epic', 'requirement', 'story', 'productplan', 'release', 'task', 'testcase', 'testsuite', 'testtask', 'testreport', 'caselib', 'doc', 'feedback', 'user', 'execution');
|
||||
$config->admin->menuModuleGroup['template']['custom|set'] = array('baseline');
|
||||
if($config->vision == 'lite')
|
||||
{
|
||||
@@ -90,7 +90,7 @@ $config->admin->helpURL['platform'] = 'https://www.zentao.net/book/devops/1072.
|
||||
$config->admin->helpURL['ai'] = 'https://www.zentao.net/book/zentaopms/1097.html';
|
||||
|
||||
$config->admin->navsGroup['feature']['my'] = ',todo,block,';
|
||||
$config->admin->navsGroup['feature']['product'] = ',product,story,productplan,release,';
|
||||
$config->admin->navsGroup['feature']['product'] = ',product,epic,requirement,story,productplan,release,';
|
||||
$config->admin->navsGroup['feature']['project'] = ',project,';
|
||||
$config->admin->navsGroup['feature']['execution'] = ',execution,task,';
|
||||
$config->admin->navsGroup['feature']['project'] = ',project,story,';
|
||||
|
||||
@@ -262,9 +262,14 @@ class admin extends control
|
||||
}
|
||||
}
|
||||
|
||||
$enableER = $this->config->edition == 'ipd' ? 1 : zget($data->module, 'productER', 0);
|
||||
$URAndSR = $this->config->edition == 'ipd' ? 1 : zget($data->module, 'productUR', 0);
|
||||
if($enableER && !$URAndSR) $this->send(array('result' => 'fail', 'message' => $this->lang->admin->notice->openUR));
|
||||
|
||||
$this->setting->setItem('system.common.closedFeatures', implode(',', $closedFeatures));
|
||||
$this->setting->setItem('system.common.global.scoreStatus', zget($data->module, 'myScore', 0));
|
||||
$this->setting->setItem('system.custom.URAndSR', $this->config->edition == 'ipd' ? 1 : zget($data->module, 'productUR', 0));
|
||||
$this->setting->setItem('system.custom.enableER', $enableER);
|
||||
$this->setting->setItem('system.custom.URAndSR', $URAndSR);
|
||||
$this->setting->setItem('system.common.setCode', in_array('otherSetCode', $closedFeatures) ? 0 : 1);
|
||||
$this->loadModel('custom')->processMeasrecordCron();
|
||||
$this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'callback' => '$.apps.updateAppsMenu(true);loadCurrentPage();'));
|
||||
|
||||
@@ -72,6 +72,7 @@ $lang->admin->notice = new stdclass();
|
||||
$lang->admin->notice->register = "Hinweiß: Sie haben sich nicht bei ZenTao (www.zentao.pm) registriert. %s um die aktuellen Updates und News zu erhalten.";
|
||||
$lang->admin->notice->ignore = "Ignorieren";
|
||||
$lang->admin->notice->int = "『%s』sollte eine positive Zahl sein.";
|
||||
$lang->admin->notice->openUR = "Please enable {$lang->URCommon} first before you can use {$lang->ERCommon}.";
|
||||
|
||||
$lang->admin->registerNotice = new stdclass();
|
||||
$lang->admin->registerNotice->common = 'Register Account';
|
||||
@@ -114,6 +115,7 @@ $lang->admin->setModule->auditplan = 'QA';
|
||||
$lang->admin->setModule->meeting = 'Meeting';
|
||||
$lang->admin->setModule->roadmap = 'Roadmap';
|
||||
$lang->admin->setModule->track = 'Track';
|
||||
$lang->admin->setModule->ER = $lang->ERCommon;
|
||||
$lang->admin->setModule->UR = $lang->URCommon;
|
||||
$lang->admin->setModule->researchplan = 'Researchplan';
|
||||
$lang->admin->setModule->gapanalysis = 'Gapanalysis';
|
||||
|
||||
@@ -72,6 +72,7 @@ $lang->admin->notice = new stdclass();
|
||||
$lang->admin->notice->register = "Note: You haven't registered in ZenTao official website(www.zentao.pm). %s then get the Latest ZenTao Upgrades and News.";
|
||||
$lang->admin->notice->ignore = "Ignore";
|
||||
$lang->admin->notice->int = "『%s』should be a positive integer.";
|
||||
$lang->admin->notice->openUR = "Please enable {$lang->URCommon} first before you can use {$lang->ERCommon}.";
|
||||
|
||||
$lang->admin->registerNotice = new stdclass();
|
||||
$lang->admin->registerNotice->common = 'Register Account';
|
||||
@@ -114,6 +115,7 @@ $lang->admin->setModule->auditplan = 'QA';
|
||||
$lang->admin->setModule->meeting = 'Meeting';
|
||||
$lang->admin->setModule->roadmap = 'Roadmap';
|
||||
$lang->admin->setModule->track = 'Track';
|
||||
$lang->admin->setModule->ER = $lang->ERCommon;
|
||||
$lang->admin->setModule->UR = $lang->URCommon;
|
||||
$lang->admin->setModule->researchplan = 'Researchplan';
|
||||
$lang->admin->setModule->gapanalysis = 'Gapanalysis';
|
||||
|
||||
@@ -72,6 +72,7 @@ $lang->admin->notice = new stdclass();
|
||||
$lang->admin->notice->register = "Note: Vous n'êtes pas enregistré sur le site officiel de ZenTao (www.zentao.pm). %s vous pourrez ensuite obtenir les dernières Mises à Jour de ZenTao et des informations.";
|
||||
$lang->admin->notice->ignore = "Ignorer";
|
||||
$lang->admin->notice->int = "『 %s 』 devrait être un entier positif.";
|
||||
$lang->admin->notice->openUR = "Please enable {$lang->URCommon} first before you can use {$lang->ERCommon}.";
|
||||
|
||||
$lang->admin->registerNotice = new stdclass();
|
||||
$lang->admin->registerNotice->common = 'Register Account';
|
||||
@@ -114,6 +115,7 @@ $lang->admin->setModule->auditplan = 'QA';
|
||||
$lang->admin->setModule->meeting = 'Meeting';
|
||||
$lang->admin->setModule->roadmap = 'Roadmap';
|
||||
$lang->admin->setModule->track = 'Track';
|
||||
$lang->admin->setModule->ER = $lang->ERCommon;
|
||||
$lang->admin->setModule->UR = $lang->URCommon;
|
||||
$lang->admin->setModule->researchplan = 'Researchplan';
|
||||
$lang->admin->setModule->gapanalysis = 'Gapanalysis';
|
||||
|
||||
@@ -116,6 +116,8 @@ $lang->admin->menuList->feature['tabMenu']['my']['todo'] = array('link' => "{$l
|
||||
$lang->admin->menuList->feature['tabMenu']['my']['block'] = array('link' => "{$lang->block->common}|custom|set|module=block&field=closed", 'exclude' => 'custom-set');
|
||||
|
||||
$lang->admin->menuList->feature['tabMenu']['product']['product'] = array('link' => "{$lang->productCommon}|custom|required|module=product", 'links' => array("custom|browsestoryconcept|", 'custom|product|'), 'alias' => 'browsestoryconcept,product', 'exclude' => 'custom-required');
|
||||
if($config->enableER) $lang->admin->menuList->feature['tabMenu']['product']['epic'] = array('link' => "{$lang->epic->common}|custom|required|module=epic", 'links' => array("custom|set|module=epic&field=categoryList"), 'exclude' => 'custom-required,custom-set');
|
||||
if($config->URAndSR) $lang->admin->menuList->feature['tabMenu']['product']['requirement'] = array('link' => "{$lang->URCommon}|custom|required|module=requirement", 'links' => array("custom|set|module=requirement&field=categoryList"), 'exclude' => 'custom-required,custom-set');
|
||||
$lang->admin->menuList->feature['tabMenu']['product']['story'] = array('link' => "{$lang->SRCommon}|custom|required|module=story", 'links' => array("custom|set|module=story&field=categoryList"), 'exclude' => 'custom-required,custom-set');
|
||||
$lang->admin->menuList->feature['tabMenu']['product']['productplan'] = array('link' => "{$lang->productplan->shortCommon}|custom|required|module=productplan", 'exclude' => 'custom-required');
|
||||
$lang->admin->menuList->feature['tabMenu']['product']['release'] = array('link' => "{$lang->release->common}|custom|required|module=release", 'exclude' => 'custom-required');
|
||||
@@ -133,9 +135,11 @@ $lang->admin->menuList->feature['tabMenu']['qa']['caselib'] = array('link' =>
|
||||
$lang->admin->menuList->feature['tabMenu']['menuOrder']['my']['5'] = 'todo';
|
||||
$lang->admin->menuList->feature['tabMenu']['menuOrder']['my']['10'] = 'block';
|
||||
$lang->admin->menuList->feature['tabMenu']['menuOrder']['product']['5'] = 'product';
|
||||
$lang->admin->menuList->feature['tabMenu']['menuOrder']['product']['10'] = 'story';
|
||||
$lang->admin->menuList->feature['tabMenu']['menuOrder']['product']['15'] = 'productplan';
|
||||
$lang->admin->menuList->feature['tabMenu']['menuOrder']['product']['20'] = 'release';
|
||||
if($config->enableER) $lang->admin->menuList->feature['tabMenu']['menuOrder']['product']['10'] = 'epic';
|
||||
if($config->URAndSR) $lang->admin->menuList->feature['tabMenu']['menuOrder']['product']['15'] = 'requirement';
|
||||
$lang->admin->menuList->feature['tabMenu']['menuOrder']['product']['20'] = 'story';
|
||||
$lang->admin->menuList->feature['tabMenu']['menuOrder']['product']['25'] = 'productplan';
|
||||
$lang->admin->menuList->feature['tabMenu']['menuOrder']['product']['30'] = 'release';
|
||||
$lang->admin->menuList->feature['tabMenu']['menuOrder']['execution']['5'] = 'execution';
|
||||
$lang->admin->menuList->feature['tabMenu']['menuOrder']['execution']['10'] = 'task';
|
||||
$lang->admin->menuList->feature['tabMenu']['menuOrder']['qa']['5'] = 'bug';
|
||||
|
||||
@@ -72,6 +72,7 @@ $lang->admin->notice = new stdclass();
|
||||
$lang->admin->notice->register = "可%s禅道社区 www.zentao.net,及时获得禅道最新信息。";
|
||||
$lang->admin->notice->ignore = "不再提示";
|
||||
$lang->admin->notice->int = "『%s』应当是正整数。";
|
||||
$lang->admin->notice->openUR = "请先开启{$lang->URCommon},才能使用{$lang->ERCommon}。";
|
||||
|
||||
$lang->admin->registerNotice = new stdclass();
|
||||
$lang->admin->registerNotice->common = '注册新帐号';
|
||||
@@ -115,6 +116,7 @@ $lang->admin->setModule->meeting = '会议';
|
||||
$lang->admin->setModule->roadmap = '路线图';
|
||||
$lang->admin->setModule->track = '矩阵';
|
||||
$lang->admin->setModule->UR = $lang->URCommon;
|
||||
$lang->admin->setModule->ER = $lang->ERCommon;
|
||||
$lang->admin->setModule->researchplan = '调研';
|
||||
$lang->admin->setModule->gapanalysis = '培训';
|
||||
$lang->admin->setModule->storylib = '需求库';
|
||||
|
||||
@@ -4144,3 +4144,101 @@ $config->bi->builtin->metrics[] = array
|
||||
'desc' => '按项目统计的已完成用户需求数是指状态为已关闭且关闭原因为已完成的用户需求的数量。反映了项目团队在满足用户期望和需求方面的已经实现的任务和计划。已完成用户需求数量的增加表示项目团队已经成功完成了一定数量的用户需求工作,并取得了一定的成果',
|
||||
'definition' => "项目中用户需求的个数求和\n状态为已关闭\n关闭原因为已完成\n过滤已删除的用户需求\n过滤已删除的项目"
|
||||
);
|
||||
|
||||
$config->bi->builtin->metrics[] = array
|
||||
(
|
||||
'name' => '按人员统计的被指派的业务需求数',
|
||||
'alias' => '被指派的业务需求数',
|
||||
'code' => 'count_of_assigned_epic_in_user',
|
||||
'purpose' => 'scale',
|
||||
'scope' => 'user',
|
||||
'object' => 'epic',
|
||||
'unit' => 'count',
|
||||
'dateType' => 'nodate',
|
||||
'desc' => '按人员统计的被指派业务需求数表示每个人待处理的业务需求数量之和。反映了每个人员需要处理的业务需求数量的规模。该数值越大,说明需要投入越多的时间处理业务需求。',
|
||||
'definition' => "所有业务需求个数求和\n指派给为某人\n过滤已删除的业务需求\n过滤已删除产品的业务需求"
|
||||
);
|
||||
|
||||
$config->bi->builtin->metrics[] = array
|
||||
(
|
||||
'name' => '按人员统计的被指派的用户需求数',
|
||||
'alias' => '被指派的用户需求数',
|
||||
'code' => 'count_of_assigned_requirement_in_user',
|
||||
'purpose' => 'scale',
|
||||
'scope' => 'user',
|
||||
'object' => 'requirement',
|
||||
'unit' => 'count',
|
||||
'dateType' => 'nodate',
|
||||
'desc' => '按人员统计的被指派用户需求数表示每个人待处理的用户需求数量之和。反映了每个人员需要处理的用户需求数量的规模。该数值越大,说明需要投入越多的时间处理用户需求。',
|
||||
'definition' => "所有用户需求个数求和\n指派给为某人\n过滤已删除的用户需求\n过滤已删除产品的用户需求"
|
||||
);
|
||||
|
||||
$config->bi->builtin->metrics[] = array
|
||||
(
|
||||
'name' => '按项目统计的业务需求总数',
|
||||
'alias' => '业务需求总数',
|
||||
'code' => 'count_of_epic_in_project',
|
||||
'purpose' => 'scale',
|
||||
'scope' => 'project',
|
||||
'object' => 'epic',
|
||||
'unit' => 'count',
|
||||
'dateType' => 'nodate',
|
||||
'desc' => '按项目统计的业务需求总数是指项目中创建或关联的所有业务需求的数量,反映了项目的规模和复杂度,提供了关于业务需求管理、进度控制、资源规划、风险评估和质量控制的有用信息',
|
||||
'definition' => "项目中业务需求个数求和\r\n过滤已删除的业务需求\r\n过滤已删除的项目"
|
||||
);
|
||||
|
||||
$config->bi->builtin->metrics[] = array
|
||||
(
|
||||
'name' => '按项目统计的已关闭业务需求数',
|
||||
'alias' => '已关闭业务需求数',
|
||||
'code' => 'count_of_closed_epic_in_project',
|
||||
'purpose' => 'scale',
|
||||
'scope' => 'project',
|
||||
'object' => 'epic',
|
||||
'unit' => 'count',
|
||||
'dateType' => 'nodate',
|
||||
'desc' => '按项目统计的已关闭业务需求数是指项目中状态为已关闭的业务需求的数量,反映了项目团队在满足组织业务目标和需求方面的已经实现的任务和计划。已关闭业务需求数量的增加表示项目团队已经成功完成了一定数量的业务需求工作,并取得了一定的成果。',
|
||||
'definition' => "项目中业务需求个数求和\r\n过滤已删除的业务需求\r\n状态为已关闭\r\n过滤已删除的项目"
|
||||
);
|
||||
|
||||
$config->bi->builtin->metrics[] = array
|
||||
(
|
||||
'name' => '按项目统计的未关闭业务需求数',
|
||||
'alias' => '未关闭业务需求数',
|
||||
'code' => 'count_of_unclosed_epic_in_project',
|
||||
'purpose' => 'scale',
|
||||
'scope' => 'project',
|
||||
'object' => 'epic',
|
||||
'unit' => 'count',
|
||||
'dateType' => 'nodate',
|
||||
'desc' => '按项目统计的未关闭业务需求数是指项目中尚未满足或处理的业务需求的数量,反映了项目团队在满足组织业务目标和需求方面的进行中任务和计划。未关闭业务需求数量的增加表示项目团队尚未完成的业务需求工作较多,需要进一步跟进和处理,以确保项目能够满足组织的业务目标',
|
||||
'definition' => "复用:\r\n按项目统计的业务需求总数\r\n按项目统计的已关闭业务需求数\r\n公式:\r\n按项目统计的未关闭业务需求数=按项目统计的业务需求总数-按项目统计的已关闭业务需求数"
|
||||
);
|
||||
|
||||
$config->bi->builtin->metrics[] = array
|
||||
(
|
||||
'name' => '按项目统计的已完成业务需求数',
|
||||
'alias' => '已完成业务需求数',
|
||||
'code' => 'count_of_finished_epic_in_project',
|
||||
'purpose' => 'scale',
|
||||
'scope' => 'project',
|
||||
'object' => 'epic',
|
||||
'unit' => 'count',
|
||||
'dateType' => 'nodate',
|
||||
'desc' => '按项目统计的已完成业务需求数是指状态为已关闭且关闭原因为已完成的业务需求的数量。反映了项目团队在满足组织业务目标和需求方面的已经实现的任务和计划。已完成业务需求数量的增加表示项目团队已经成功完成了一定数量的业务需求工作,并取得了一定的成果',
|
||||
'definition' => "项目中业务需求的个数求和\r\n状态为已关闭\r\n关闭原因为已完成\r\n过滤已删除的业务需求\r\n过滤已删除的项目"
|
||||
);
|
||||
|
||||
$config->bi->builtin->metrics[] = array
|
||||
(
|
||||
'name' => '按人员统计的被指派的QA数',
|
||||
'alias' => '被指派的QA数',
|
||||
'code' => 'count_of_assigned_qa_in_user',
|
||||
'purpose' => 'scale',
|
||||
'scope' => 'user',
|
||||
'object' => 'qa',
|
||||
'unit' => 'count',
|
||||
'dateType' => 'nodate',
|
||||
'desc' => '按人员统计的被指派的QA数表示每个人被指派的质量保证问题之和。反映了每个人员需要处理的质量保证问题的规模。该数值越大,说明需要处理的质量保证问题越多',
|
||||
'definition' => "所有待处理的QA个数求和(包含:待处理质量保证计划、待处理不符合项)\n指派给为某人\n质量保证计划状态为待检查、不符合项状态为待解决\n过滤已删除的质量保证计划和不符合项\n过滤已删除项目的质量保证计划和不符合项"
|
||||
);
|
||||
|
||||
@@ -543,7 +543,8 @@ $lang->block->welcome->assignList['task'] = 'Task';
|
||||
if($config->vision != 'or') $lang->block->welcome->assignList['bug'] = 'Bug';
|
||||
if($config->vision != 'or') $lang->block->welcome->assignList['story'] = 'SRStroy';
|
||||
$lang->block->welcome->assignList['testcase'] = 'TestCase';
|
||||
if($config->vision != 'or') $lang->block->welcome->assignList['requirement'] = 'URStroy';
|
||||
if($config->URAndSR && $config->vision != 'or') $lang->block->welcome->assignList['requirement'] = "{$lang->URCommon}";
|
||||
if($config->enableER && $config->vision != 'or') $lang->block->welcome->assignList['epic'] = "{$lang->ERCommon}";
|
||||
|
||||
$lang->block->customModeTip = new stdClass();
|
||||
$lang->block->customModeTip->common = 'There are 2 running modes of ZenTao: Light Mode and ALM Mode.';
|
||||
|
||||
@@ -543,7 +543,8 @@ $lang->block->welcome->assignList['task'] = 'Task';
|
||||
if($config->vision != 'or') $lang->block->welcome->assignList['bug'] = 'Bug';
|
||||
if($config->vision != 'or') $lang->block->welcome->assignList['story'] = 'SRStroy';
|
||||
$lang->block->welcome->assignList['testcase'] = 'TestCase';
|
||||
if($config->vision != 'or') $lang->block->welcome->assignList['requirement'] = 'URStroy';
|
||||
if($config->URAndSR && $config->vision != 'or') $lang->block->welcome->assignList['requirement'] = "{$lang->URCommon}";
|
||||
if($config->enableER && $config->vision != 'or') $lang->block->welcome->assignList['epic'] = "{$lang->ERCommon}";
|
||||
|
||||
$lang->block->customModeTip = new stdClass();
|
||||
$lang->block->customModeTip->common = 'There are 2 running modes of ZenTao: Light Mode and ALM Mode.';
|
||||
|
||||
@@ -543,7 +543,8 @@ $lang->block->welcome->assignList['task'] = 'Task';
|
||||
if($config->vision != 'or') $lang->block->welcome->assignList['bug'] = 'Bug';
|
||||
if($config->vision != 'or') $lang->block->welcome->assignList['story'] = 'SRStroy';
|
||||
$lang->block->welcome->assignList['testcase'] = 'TestCase';
|
||||
if($config->vision != 'or') $lang->block->welcome->assignList['requirement'] = 'URStroy';
|
||||
if($config->URAndSR && $config->vision != 'or') $lang->block->welcome->assignList['requirement'] = "{$lang->URCommon}";
|
||||
if($config->enableER && $config->vision != 'or') $lang->block->welcome->assignList['epic'] = "{$lang->ERCommon}";
|
||||
|
||||
$lang->block->customModeTip = new stdClass();
|
||||
$lang->block->customModeTip->common = 'There are 2 running modes of ZenTao: Light Mode and ALM Mode.';
|
||||
|
||||
@@ -541,9 +541,10 @@ $lang->block->welcome->reviewList['reviewByMe'] = '待我评审数';
|
||||
$lang->block->welcome->assignList = array();
|
||||
$lang->block->welcome->assignList['task'] = '任务数';
|
||||
if($config->vision != 'or') $lang->block->welcome->assignList['bug'] = 'BUG数';
|
||||
if($config->vision != 'or') $lang->block->welcome->assignList['story'] = $lang->SRCommon . '数';
|
||||
if($config->vision != 'or') $lang->block->welcome->assignList['story'] = "{$lang->SRCommon}数";
|
||||
$lang->block->welcome->assignList['testcase'] = '用例数';
|
||||
if($config->vision != 'or') $lang->block->welcome->assignList['requirement'] = $lang->URCommon . '数';
|
||||
if($config->URAndSR && $config->vision != 'or') $lang->block->welcome->assignList['requirement'] = "{$lang->URCommon}数";
|
||||
if($config->enableER && $config->vision != 'or') $lang->block->welcome->assignList['epic'] = "{$lang->ERCommon}数";
|
||||
|
||||
$lang->block->customModeTip = new stdClass();
|
||||
$lang->block->customModeTip->common = '禅道运行模式分为【轻量级管理模式】和【全生命周期管理模式】。';
|
||||
@@ -571,7 +572,7 @@ $lang->block->projectoverview->thisYear = '今年完成';
|
||||
$lang->block->projectoverview->lastThreeYear = '近三年完成的项目数量分布';
|
||||
|
||||
$lang->block->projectstatistic = new stdclass();
|
||||
$lang->block->projectstatistic->story = $lang->SRCommon;
|
||||
$lang->block->projectstatistic->story = '需求';
|
||||
$lang->block->projectstatistic->cost = '投入';
|
||||
$lang->block->projectstatistic->task = '任务';
|
||||
$lang->block->projectstatistic->bug = 'Bug';
|
||||
|
||||
@@ -86,6 +86,12 @@ if(!$bug->deleted)
|
||||
}
|
||||
$actions[$key] = $action;
|
||||
}
|
||||
if($action['icon'] == 'edit' && isInModal())
|
||||
{
|
||||
$action['data-load'] = 'modal';
|
||||
$action['data-size'] = 'lg';
|
||||
$actions[$key] = $action;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+13
-2
@@ -1021,7 +1021,7 @@ class bugZen extends bug
|
||||
|
||||
if($executionID)
|
||||
{
|
||||
$stories = $this->story->getExecutionStoryPairs($executionID, $productID, $branch);
|
||||
$stories = $this->story->getExecutionStoryPairs($executionID, $productID, $branch, '', 'full', 'all', 'story', false);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1033,6 +1033,8 @@ class bugZen extends bug
|
||||
$bugStory ? $stories[$bug->storyID] = $bugStory->title : $bug->storyID = 0;
|
||||
}
|
||||
|
||||
$stories = $this->story->addGradeLabel($stories);
|
||||
|
||||
return $this->updateBug($bug, array('stories' => $stories));
|
||||
}
|
||||
|
||||
@@ -1237,13 +1239,22 @@ class bugZen extends bug
|
||||
|
||||
$this->config->moreLinks['case'] = inlink('ajaxGetProductCases', "bugID={$bug->id}");
|
||||
|
||||
if($bug->execution)
|
||||
{
|
||||
$stories = $this->story->getExecutionStoryPairs($bug->execution, 0, 'all', '', 'full', 'all', 'story', false);
|
||||
}
|
||||
else
|
||||
{
|
||||
$stories = $this->story->getProductStoryPairs($bug->product, $bug->branch, 0, 'active,closed', 'id_desc', 0, 'full', 'story', false);
|
||||
}
|
||||
|
||||
$resolvedBuildPairs = $this->build->getBuildPairs(array($bug->product), $bug->branch, 'noempty');
|
||||
$this->view->resolvedBuildPairs = $resolvedBuildPairs;
|
||||
$this->view->resolvedBuilds = $this->build->addReleaseLabelForBuilds($bug->product, $resolvedBuildPairs);
|
||||
|
||||
$this->view->openedBuilds = $openedBuilds;
|
||||
$this->view->plans = $this->loadModel('productplan')->getPairs($bug->product, $bug->branch, '', true);
|
||||
$this->view->stories = $bug->execution ? $this->story->getExecutionStoryPairs($bug->execution) : $this->story->getProductStoryPairs($bug->product, $bug->branch, 0, 'all', 'id_desc', 0, 'full', 'story', false);
|
||||
$this->view->stories = $this->story->addGradeLabel($stories);
|
||||
$this->view->tasks = $this->task->getExecutionTaskPairs($bug->execution);
|
||||
$this->view->testtasks = $this->loadModel('testtask')->getPairs($bug->product, $bug->execution, $bug->testtask);
|
||||
$this->view->cases = $cases;
|
||||
|
||||
@@ -185,6 +185,8 @@ class build extends control
|
||||
$this->view->actions = $this->loadModel('action')->getList('build', $buildID);
|
||||
$this->view->link = $link;
|
||||
$this->view->orderBy = $orderBy;
|
||||
$this->view->grades = $this->loadModel('story')->getGradePairs('story', 'all');
|
||||
$this->view->showGrade = $this->config->edition == 'ipd';
|
||||
$this->view->execution = $this->loadModel('execution')->getByID((int)$build->execution);
|
||||
$this->view->childBuilds = empty($build->builds) ? array() : $this->build->getByList(explode(',', $build->builds));
|
||||
$this->view->productID = $build->product;
|
||||
@@ -436,13 +438,14 @@ class build extends control
|
||||
|
||||
$this->loadModel('story');
|
||||
$executionID = $build->execution ? (int)$build->execution : (int)$build->project;
|
||||
$excludeStoryIdList = $this->buildZen->getExcludeStoryIdList($build);
|
||||
if($browseType == 'bySearch')
|
||||
{
|
||||
$allStories = $this->story->getBySearch($build->product, $build->branch, (int)$param, $orderBy, $executionID, 'story', $build->allStories, '', $pager);
|
||||
$allStories = $this->story->getBySearch($build->product, $build->branch, (int)$param, $orderBy, $executionID, 'story', $excludeStoryIdList, '', $pager);
|
||||
}
|
||||
else
|
||||
{
|
||||
$allStories = $this->story->getExecutionStories($executionID, $build->product, $orderBy, 'byBranch', $build->branch, 'story', $build->allStories, $pager);
|
||||
$allStories = $this->story->getExecutionStories($executionID, $build->product, $orderBy, 'byBranch', $build->branch, 'story', $excludeStoryIdList, $pager);
|
||||
}
|
||||
|
||||
$checkedRows = array();
|
||||
@@ -459,6 +462,8 @@ class build extends control
|
||||
$this->view->browseType = $browseType;
|
||||
$this->view->param = $param;
|
||||
$this->view->pager = $pager;
|
||||
$this->view->grades = $this->story->getGradePairs('story', 'all');
|
||||
$this->view->showGrade = $this->config->edition == 'ipd';
|
||||
$this->view->orderBy = $orderBy;
|
||||
$this->display();
|
||||
}
|
||||
|
||||
@@ -4,7 +4,8 @@ window.renderStoryCell = function(result, info)
|
||||
if(info.col.name == 'title' && result)
|
||||
{
|
||||
let html = '';
|
||||
if(story.parent > 0) html += "<span class='label gray-pale rounded-xl'>" + childrenAB + "</span>";
|
||||
let gradeLabel = (showGrade || story.grade >= 2) ? grades[story.grade] : '';
|
||||
if(gradeLabel) html += "<span class='label gray-pale rounded-xl'>" + gradeLabel + "</span>";
|
||||
if(html) result.unshift({html});
|
||||
}
|
||||
|
||||
|
||||
@@ -77,3 +77,17 @@ $(function()
|
||||
{
|
||||
if(initLink == 'true') window.showLink(type, linkParams);
|
||||
});
|
||||
|
||||
window.renderStoryCell = function(result, info)
|
||||
{
|
||||
const story = info.row.data;
|
||||
if(info.col.name == 'title' && result)
|
||||
{
|
||||
let html = '';
|
||||
let gradeLabel = (showGrade || story.grade >= 2) ? grades[story.grade] : '';
|
||||
if(gradeLabel) html += "<span class='label gray-pale rounded-xl'>" + gradeLabel + "</span>";
|
||||
if(html) result.unshift({html});
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
@@ -27,6 +27,8 @@ foreach($allStories as $story)
|
||||
}
|
||||
|
||||
jsVar('childrenAB', $lang->story->childrenAB);
|
||||
jsVar('grades', $grades);
|
||||
jsVar('showGrade', $showGrade);
|
||||
|
||||
searchForm
|
||||
(
|
||||
|
||||
@@ -81,6 +81,8 @@ jsVar('confirmDelete', $lang->build->confirmDelete);
|
||||
jsVar('currentAccount', $app->user->account);
|
||||
jsVar('buildProduct', $build->product);
|
||||
jsVar('buildModule', $buildModule);
|
||||
jsVar('grades', $grades);
|
||||
jsVar('showGrade', $showGrade);
|
||||
|
||||
/* Story's batch btn. */
|
||||
$canBatchUnlinkStory = $canBeChanged && common::hasPriv($buildModule, 'batchUnlinkStory');
|
||||
@@ -184,6 +186,7 @@ detailBody
|
||||
set::canRowCheckable(jsRaw("function(rowID){return this.getRowInfo(rowID).data.noCheckBox ? 'disabled' : true;}")),
|
||||
set::sortLink(createLink($buildModule, 'view', "buildID={$build->id}&type=story&link={$link}¶m={$param}&orderBy={name}_{sortType}")),
|
||||
set::orderBy($orderBy),
|
||||
set::onRenderCell(jsRaw('window.renderStoryCell')),
|
||||
set::extraHeight('+144'),
|
||||
set::footToolbar($storyFootToolbar),
|
||||
set::footPager(usePager('storyPager', '', array(
|
||||
|
||||
@@ -228,6 +228,31 @@ class buildZen extends build
|
||||
$this->view->objectID = $objectID;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取版本不可关联的需求ID列表。
|
||||
* Get the story ID list that cannot be linked to the build.
|
||||
*
|
||||
* @param object $build
|
||||
* @access protected
|
||||
* @return array
|
||||
*/
|
||||
public function getExcludeStoryIdList(object $build)
|
||||
{
|
||||
$parentIdList = $this->dao->select('id')->from(TABLE_STORY)
|
||||
->where('product')->eq($build->product)
|
||||
->andWhere('type')->eq('story')
|
||||
->andWhere('isParent')->eq('1')
|
||||
->fetchPairs();
|
||||
|
||||
foreach(explode(',', $build->allStories) as $storyID)
|
||||
{
|
||||
if(!$storyID) continue;
|
||||
if(!isset($parentIdList[$storyID])) $parentIdList[$storyID] = $storyID;
|
||||
}
|
||||
|
||||
return $parentIdList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成关联需求的搜索表单数据。
|
||||
* Generate the search form data for the build view page.
|
||||
|
||||
@@ -14,6 +14,7 @@ $lang->scrum = new stdclass();
|
||||
$lang->waterfall = new stdclass();
|
||||
$lang->execution = new stdclass();
|
||||
$lang->kanban = new stdclass();
|
||||
$lang->epic = new stdclass();
|
||||
$lang->story = new stdclass();
|
||||
$lang->requirement = new stdclass();
|
||||
$lang->release = new stdclass();
|
||||
|
||||
@@ -224,7 +224,8 @@ $lang->metric->common = 'Metric';
|
||||
$lang->report->common = 'Report';
|
||||
$lang->system->common = 'System';
|
||||
$lang->admin->common = 'Admin';
|
||||
$lang->story->common = 'Story';
|
||||
$lang->epic->common = $lang->ERCommon;
|
||||
$lang->story->common = $lang->SRCommon;
|
||||
$lang->task->common = 'Task';
|
||||
$lang->bug->common = 'Bug';
|
||||
$lang->testcase->common = 'Testcase';
|
||||
@@ -437,7 +438,8 @@ $lang->admin->cache = 'Cache';
|
||||
|
||||
$lang->convert->importJira = 'Import Jira';
|
||||
|
||||
$lang->storyConcept = 'Story Concpet';
|
||||
$lang->storyConcept = 'Story Concpet';
|
||||
$lang->defaultERName = 'Epic';
|
||||
|
||||
$lang->searchTips = '';
|
||||
$lang->searchAB = 'Search';
|
||||
@@ -445,7 +447,9 @@ $lang->searchAB = 'Search';
|
||||
/* Object list in search form. */
|
||||
$lang->searchObjects['all'] = 'All';
|
||||
$lang->searchObjects['bug'] = 'Bug';
|
||||
$lang->searchObjects['story'] = 'Story';
|
||||
$lang->searchObjects['story'] = $lang->SRCommon;
|
||||
if($config->enableER) $lang->searchObjects['epic'] = $lang->ERCommon;
|
||||
if($config->URAndSR) $lang->searchObjects['requirement'] = $lang->URCommon;
|
||||
$lang->searchObjects['task'] = 'Aufgaben';
|
||||
$lang->searchObjects['testcase'] = 'Fall';
|
||||
$lang->searchObjects['product'] = $lang->productCommon;
|
||||
|
||||
@@ -224,7 +224,8 @@ $lang->metric->common = 'Metric';
|
||||
$lang->report->common = 'Report';
|
||||
$lang->system->common = 'System';
|
||||
$lang->admin->common = 'Admin';
|
||||
$lang->story->common = 'Story';
|
||||
$lang->epic->common = $lang->ERCommon;
|
||||
$lang->story->common = $lang->SRCommon;
|
||||
$lang->task->common = 'Task';
|
||||
$lang->bug->common = 'Bug';
|
||||
$lang->testcase->common = 'Testcase';
|
||||
@@ -437,7 +438,8 @@ $lang->admin->cache = 'Cache';
|
||||
|
||||
$lang->convert->importJira = 'Import Jira';
|
||||
|
||||
$lang->storyConcept = 'Story Concpet';
|
||||
$lang->storyConcept = 'Story Concpet';
|
||||
$lang->defaultERName = 'Epic';
|
||||
|
||||
$lang->searchTips = '';
|
||||
$lang->searchAB = 'Search';
|
||||
@@ -445,7 +447,9 @@ $lang->searchAB = 'Search';
|
||||
/* Object list in search form. */
|
||||
$lang->searchObjects['all'] = 'All';
|
||||
$lang->searchObjects['bug'] = 'Bug';
|
||||
$lang->searchObjects['story'] = 'Story';
|
||||
$lang->searchObjects['story'] = $lang->SRCommon;
|
||||
if($config->enableER) $lang->searchObjects['epic'] = $lang->ERCommon;
|
||||
if($config->URAndSR) $lang->searchObjects['requirement'] = $lang->URCommon;
|
||||
$lang->searchObjects['task'] = 'Task';
|
||||
$lang->searchObjects['testcase'] = 'Case';
|
||||
$lang->searchObjects['product'] = $lang->productCommon;
|
||||
|
||||
@@ -224,7 +224,8 @@ $lang->metric->common = 'Metric';
|
||||
$lang->report->common = 'Report';
|
||||
$lang->system->common = 'System';
|
||||
$lang->admin->common = 'Admin';
|
||||
$lang->story->common = 'Story';
|
||||
$lang->epic->common = $lang->ERCommon;
|
||||
$lang->story->common = $lang->SRCommon;
|
||||
$lang->task->common = 'Task';
|
||||
$lang->bug->common = 'Bug';
|
||||
$lang->testcase->common = 'Testcase';
|
||||
@@ -437,7 +438,8 @@ $lang->admin->cache = 'Cache';
|
||||
|
||||
$lang->convert->importJira = 'Import Jira';
|
||||
|
||||
$lang->storyConcept = 'Story Concpet';
|
||||
$lang->storyConcept = 'Story Concpet';
|
||||
$lang->defaultERName = 'Epic';
|
||||
|
||||
$lang->searchTips = '';
|
||||
$lang->searchAB = 'Search';
|
||||
@@ -445,7 +447,9 @@ $lang->searchAB = 'Search';
|
||||
/* Object list in search form. */
|
||||
$lang->searchObjects['all'] = 'All';
|
||||
$lang->searchObjects['bug'] = 'Bug';
|
||||
$lang->searchObjects['story'] = 'Story';
|
||||
$lang->searchObjects['story'] = $lang->SRCommon;
|
||||
if($config->enableER) $lang->searchObjects['epic'] = $lang->ERCommon;
|
||||
if($config->URAndSR) $lang->searchObjects['requirement'] = $lang->URCommon;
|
||||
$lang->searchObjects['task'] = 'Tâche';
|
||||
$lang->searchObjects['testcase'] = 'CasTest';
|
||||
$lang->searchObjects['product'] = $lang->productCommon;
|
||||
|
||||
+31
-37
@@ -112,6 +112,7 @@ $lang->my->menu->work['subMenu'] = new stdclass();
|
||||
$lang->my->menu->work['subMenu']->task = array('link' => "{$lang->task->common}|my|work|mode=task", 'subModule' => 'task', 'alias' => 'task');
|
||||
$lang->my->menu->work['subMenu']->story = array('link' => "$lang->SRCommon|my|work|mode=story", 'alias' => 'story');
|
||||
$lang->my->menu->work['subMenu']->requirement = array('link' => "$lang->URCommon|my|work|mode=requirement", 'alias' => 'requirement');
|
||||
$lang->my->menu->work['subMenu']->epic = array('link' => "$lang->ERCommon|my|work|mode=epic", 'alias' => 'epic');
|
||||
$lang->my->menu->work['subMenu']->bug = array('link' => "{$lang->bug->common}|my|work|mode=bug", 'subModule' => 'bug', 'alias' => 'bug');
|
||||
$lang->my->menu->work['subMenu']->testcase = array('link' => "{$lang->testcase->common}|my|work|mode=testcase", 'subModule' => 'testtask,testcase', 'alias' => 'testcase');
|
||||
$lang->my->menu->work['subMenu']->testtask = array('link' => "{$lang->testtask->common}|my|work|mode=testtask", 'alias' => 'testtask');
|
||||
@@ -119,17 +120,20 @@ $lang->my->menu->work['subMenu']->testtask = array('link' => "{$lang->testtas
|
||||
$lang->my->menu->work['menuOrder'][5] = 'task';
|
||||
$lang->my->menu->work['menuOrder'][10] = 'story';
|
||||
$lang->my->menu->work['menuOrder'][15] = 'requirement';
|
||||
$lang->my->menu->work['menuOrder'][17] = 'epic';
|
||||
$lang->my->menu->work['menuOrder'][20] = 'bug';
|
||||
$lang->my->menu->work['menuOrder'][25] = 'testcase';
|
||||
$lang->my->menu->work['menuOrder'][30] = 'testtask';
|
||||
$lang->my->menu->work['menuOrder'][35] = 'audit';
|
||||
|
||||
if(!$config->URAndSR) unset($lang->my->menu->work['subMenu']->requirement, $lang->my->menu->work['menuOrder'][10]);
|
||||
if(!$config->enableER) unset($lang->my->menu->work['subMenu']->epic, $lang->my->menu->work['menuOrder'][12]);
|
||||
if(!$config->URAndSR) unset($lang->my->menu->work['subMenu']->requirement, $lang->my->menu->work['menuOrder'][15]);
|
||||
|
||||
$lang->my->menu->contribute['subMenu'] = new stdclass();
|
||||
$lang->my->menu->contribute['subMenu']->task = array('link' => "{$lang->task->common}|my|contribute|mode=task", 'alias' => 'task');
|
||||
$lang->my->menu->contribute['subMenu']->story = array('link' => "$lang->SRCommon|my|contribute|mode=story", 'alias' => 'story');
|
||||
$lang->my->menu->contribute['subMenu']->requirement = array('link' => "$lang->URCommon|my|contribute|mode=requirement", 'alias' => 'requirement');
|
||||
$lang->my->menu->contribute['subMenu']->epic = array('link' => "$lang->ERCommon|my|contribute|mode=epic", 'alias' => 'epic');
|
||||
$lang->my->menu->contribute['subMenu']->bug = array('link' => "{$lang->bug->common}|my|contribute|mode=bug", 'alias' => 'bug');
|
||||
$lang->my->menu->contribute['subMenu']->testcase = array('link' => "{$lang->testcase->shortCommon}|my|contribute|mode=testcase", 'alias' => 'testcase');
|
||||
$lang->my->menu->contribute['subMenu']->testtask = array('link' => "{$lang->testtask->common}|my|contribute|mode=testtask", 'alias' => 'testtask');
|
||||
@@ -139,13 +143,15 @@ $lang->my->menu->contribute['subMenu']->doc = array('link' => "{$lang->d
|
||||
$lang->my->menu->contribute['menuOrder'][5] = 'task';
|
||||
$lang->my->menu->contribute['menuOrder'][10] = 'story';
|
||||
$lang->my->menu->contribute['menuOrder'][15] = 'requirement';
|
||||
$lang->my->menu->contribute['menuOrder'][17] = 'epic';
|
||||
$lang->my->menu->contribute['menuOrder'][20] = 'bug';
|
||||
$lang->my->menu->contribute['menuOrder'][25] = 'testcase';
|
||||
$lang->my->menu->contribute['menuOrder'][30] = 'testtask';
|
||||
$lang->my->menu->contribute['menuOrder'][35] = 'audit';
|
||||
$lang->my->menu->contribute['menuOrder'][40] = 'doc';
|
||||
|
||||
if(!$config->URAndSR) unset($lang->my->menu->contribute['subMenu']->requirement, $lang->my->menu->contribute['menuOrder'][10]);
|
||||
if(!$config->enableER) unset($lang->my->menu->contribute['subMenu']->epic, $lang->my->menu->contribute['menuOrder'][12]);
|
||||
if(!$config->URAndSR) unset($lang->my->menu->contribute['subMenu']->requirement, $lang->my->menu->contribute['menuOrder'][15]);
|
||||
|
||||
$lang->my->dividerMenu = ',work,dynamic,';
|
||||
|
||||
@@ -180,12 +186,13 @@ $lang->product->homeMenu->kanban = array('link' => "{$lang->product->kanban}|pro
|
||||
|
||||
$lang->product->menu = new stdclass();
|
||||
$lang->product->menu->dashboard = array('link' => "{$lang->dashboard}|product|dashboard|productID=%s");
|
||||
$lang->product->menu->story = array('link' => "$lang->SRCommon|product|browse|productID=%s", 'alias' => 'batchedit', 'subModule' => 'story', 'exclude' => (isset($_GET['storyType']) ? ($_GET['storyType'] == 'requirement' ? 'browse,story-report,story-create,story-batchcreate' : '') : '') . ',batchtotask');
|
||||
$lang->product->menu->epic = array('link' => "{$lang->ERCommon}|product|browse|productID=%s&branch=&browseType=unclosed¶m=0&storyType=epic", 'subModule' => 'story');
|
||||
$lang->product->menu->requirement = array('link' => "{$lang->URCommon}|product|browse|productID=%s&branch=&browseType=unclosed¶m=0&storyType=requirement", 'alias' => 'batchedit', 'subModule' => 'story');
|
||||
$lang->product->menu->story = array('link' => "{$lang->SRCommon}|product|browse|productID=%s", 'alias' => 'batchedit', 'subModule' => 'story', 'exclude' => (isset($_GET['storyType']) ? ($_GET['storyType'] == 'requirement' ? 'browse,story-report,story-create,story-batchcreate' : '') : '') . ',batchtotask');
|
||||
$lang->product->menu->plan = array('link' => "{$lang->productplan->shortCommon}|productplan|browse|productID=%s", 'subModule' => 'productplan,bug');
|
||||
$lang->product->menu->project = array('link' => "{$lang->projectCommon}|product|project|status=all&productID=%s");
|
||||
$lang->product->menu->release = array('link' => "{$lang->release->common}|release|browse|productID=%s", 'subModule' => 'release');
|
||||
$lang->product->menu->roadmap = array('link' => "{$lang->roadmap}|product|roadmap|productID=%s");
|
||||
$lang->product->menu->requirement = array('link' => "{$lang->URCommon}|product|browse|productID=%s&branch=&browseType=unclosed¶m=0&storyType=requirement", 'alias' => 'batchedit', 'subModule' => 'story');
|
||||
$lang->product->menu->track = array('link' => "{$lang->track}|product|track|productID=%s");
|
||||
$lang->product->menu->doc = array('link' => "{$lang->doc->common}|doc|productSpace|objectID=%s", 'subModule' => 'doc,api');
|
||||
$lang->product->menu->dynamic = array('link' => "{$lang->dynamic}|product|dynamic|productID=%s");
|
||||
@@ -193,18 +200,19 @@ $lang->product->menu->settings = array('link' => "{$lang->settings}|product|v
|
||||
|
||||
/* Product menu order. */
|
||||
$lang->product->menuOrder[5] = 'dashboard';
|
||||
$lang->product->menuOrder[10] = 'story';
|
||||
$lang->product->menuOrder[15] = 'plan';
|
||||
$lang->product->menuOrder[20] = 'project';
|
||||
$lang->product->menuOrder[25] = 'release';
|
||||
$lang->product->menuOrder[30] = 'roadmap';
|
||||
$lang->product->menuOrder[35] = 'requirement';
|
||||
$lang->product->menuOrder[40] = 'track';
|
||||
$lang->product->menuOrder[45] = 'doc';
|
||||
$lang->product->menuOrder[50] = 'dynamic';
|
||||
$lang->product->menuOrder[55] = 'settings';
|
||||
$lang->product->menuOrder[60] = 'create';
|
||||
$lang->product->menuOrder[65] = 'all';
|
||||
$lang->product->menuOrder[10] = 'epic';
|
||||
$lang->product->menuOrder[15] = 'requirement';
|
||||
$lang->product->menuOrder[20] = 'story';
|
||||
$lang->product->menuOrder[25] = 'plan';
|
||||
$lang->product->menuOrder[30] = 'project';
|
||||
$lang->product->menuOrder[35] = 'release';
|
||||
$lang->product->menuOrder[40] = 'roadmap';
|
||||
$lang->product->menuOrder[45] = 'track';
|
||||
$lang->product->menuOrder[50] = 'doc';
|
||||
$lang->product->menuOrder[55] = 'dynamic';
|
||||
$lang->product->menuOrder[60] = 'settings';
|
||||
$lang->product->menuOrder[65] = 'create';
|
||||
$lang->product->menuOrder[70] = 'all';
|
||||
|
||||
$lang->product->menu->settings['subMenu'] = new stdclass();
|
||||
$lang->product->menu->settings['subMenu']->view = array('link' => "{$lang->overview}|product|view|productID=%s", 'alias' => 'edit');
|
||||
@@ -212,7 +220,7 @@ $lang->product->menu->settings['subMenu']->module = array('link' => "{$lang->
|
||||
$lang->product->menu->settings['subMenu']->branch = array('link' => "@branch@|branch|manage|product=%s", 'subModule' => 'branch');
|
||||
$lang->product->menu->settings['subMenu']->whitelist = array('link' => "{$lang->whitelist}|product|whitelist|product=%s", 'subModule' => 'personnel');
|
||||
|
||||
$lang->product->dividerMenu = $config->URAndSR ? ',story,requirement,settings,' : ',story,track,settings,';
|
||||
$lang->product->dividerMenu = ',epic,track,settings,';
|
||||
|
||||
/* Project menu. */
|
||||
$lang->project->homeMenu = new stdclass();
|
||||
@@ -223,8 +231,7 @@ $lang->project->homeMenu->kanban = array('link' => "{$lang->project->kanban}|pro
|
||||
$lang->scrum->menu = new stdclass();
|
||||
$lang->scrum->menu->index = array('link' => "{$lang->dashboard}|project|index|project=%s");
|
||||
$lang->scrum->menu->execution = array('link' => "$lang->executionCommon|project|execution|status=undone&projectID=%s", 'exclude' => 'execution-testreport', 'subModule' => 'task');
|
||||
$lang->scrum->menu->storyGroup = array('link' => "{$lang->common->story}|projectstory|story|projectID=%s&product=%s",'class' => 'dropdown dropdown-hover', 'exclude' => 'tree-browse');
|
||||
$lang->scrum->menu->story = array('link' => "$lang->SRCommon|projectstory|story|projectID=%s", 'subModule' => 'projectstory,tree', 'alias' => 'story,track', 'exclude' => 'tree-browse');
|
||||
$lang->scrum->menu->story = array('link' => "{$lang->common->story}|projectstory|story|projectID=%s", 'subModule' => 'projectstory,tree,epic,requirement', 'alias' => 'story,track', 'exclude' => 'tree-browse');
|
||||
$lang->scrum->menu->projectplan = array('link' => "{$lang->productplan->shortCommon}|projectplan|browse|productID=%s", 'subModule' => 'productplan');
|
||||
$lang->scrum->menu->doc = array('link' => "{$lang->doc->common}|doc|projectSpace|objectID=%s", 'subModule' => 'doc,api');
|
||||
$lang->scrum->menu->qa = array('link' => "{$lang->qa->common}|project|bug|projectID=%s", 'subModule' => 'testcase,testtask,bug,testreport', 'alias' => 'bug,testtask,testcase,testreport', 'exclude' => 'execution-create,execution-batchedit');
|
||||
@@ -234,17 +241,12 @@ $lang->scrum->menu->release = array('link' => "{$lang->release->common}|proj
|
||||
$lang->scrum->menu->dynamic = array('link' => "$lang->dynamic|project|dynamic|project=%s");
|
||||
$lang->scrum->menu->settings = array('link' => "$lang->settings|project|view|project=%s", 'subModule' => 'tree,stakeholder', 'alias' => 'edit,manageproducts,group,managemembers,manageview,managepriv,whitelist,addwhitelist,team', 'exclude' => 'tree-browsetask');
|
||||
|
||||
$lang->scrum->menu->storyGroup['dropMenu'] = new stdclass();
|
||||
$lang->scrum->menu->storyGroup['dropMenu']->story = array('link' => "{$lang->SRCommon}|projectstory|story|projectID=%s&productID=%s", 'subModule' => 'tree,story');
|
||||
$lang->scrum->menu->storyGroup['dropMenu']->requirement = array('link' => "{$lang->URCommon}|projectstory|story|projectID=%s&productID=%s&branch=0&browseType=¶m=0&storyType=requirement", 'subModule' => 'tree,story');
|
||||
|
||||
$lang->scrum->dividerMenu = ',execution,programplan,doc,settings,';
|
||||
|
||||
/* Scrum menu order. */
|
||||
$lang->scrum->menuOrder[5] = 'index';
|
||||
$lang->scrum->menuOrder[10] = 'execution';
|
||||
$lang->scrum->menuOrder[15] = 'story';
|
||||
$lang->scrum->menuOrder[16] = 'storyGroup';
|
||||
$lang->scrum->menuOrder[18] = 'projectplan';
|
||||
$lang->scrum->menuOrder[20] = 'qa';
|
||||
$lang->scrum->menuOrder[25] = 'devops';
|
||||
@@ -281,8 +283,7 @@ $lang->scrum->menu->devops['menuOrder'][15] = 'mr';
|
||||
$lang->waterfall->menu = new stdclass();
|
||||
$lang->waterfall->menu->index = array('link' => "$lang->dashboard|project|index|project=%s");
|
||||
$lang->waterfall->menu->execution = array('link' => "{$lang->stage->common}|project|execution|status=undone&projectID=%s", 'subModule' => 'programplan,task');
|
||||
$lang->waterfall->menu->storyGroup = array('link' => "{$lang->common->story}|projectstory|story|projectID=%s",'class' => 'dropdown dropdown-hover', 'exclude' => 'tree-browse,projectstory-track');
|
||||
$lang->waterfall->menu->story = array('link' => "$lang->SRCommon|projectstory|story|projectID=%s", 'subModule' => 'projectstory,tree', 'alias' => 'story', 'exclude' => 'projectstory-track,tree-browse');
|
||||
$lang->waterfall->menu->story = array('link' => "{$lang->common->story}|projectstory|story|projectID=%s", 'subModule' => 'projectstory,tree', 'alias' => 'story', 'exclude' => 'projectstory-track,tree-browse');
|
||||
$lang->waterfall->menu->design = array('link' => "{$lang->design->common}|design|browse|project=%s");
|
||||
$lang->waterfall->menu->qa = array('link' => "{$lang->qa->common}|project|bug|projectID=%s", 'subModule' => 'testcase,testtask,bug,testreport', 'alias' => 'bug,testtask,testcase,testreport');
|
||||
$lang->waterfall->menu->doc = array('link' => "{$lang->doc->common}|doc|projectSpace|objectID=%s");
|
||||
@@ -294,14 +295,11 @@ $lang->waterfall->menu->dynamic = array('link' => "$lang->dynamic|project|dyn
|
||||
$lang->waterfall->menu->settings = $lang->scrum->menu->settings;
|
||||
$lang->waterfall->dividerMenu = ',programplan,build,dynamic,';
|
||||
|
||||
$lang->waterfall->menu->storyGroup['dropMenu'] = $lang->scrum->menu->storyGroup['dropMenu'];
|
||||
|
||||
/* Waterfall menu order. */
|
||||
$lang->waterfall->menuOrder[5] = 'index';
|
||||
$lang->waterfall->menuOrder[15] = 'programplan';
|
||||
$lang->waterfall->menuOrder[20] = 'execution';
|
||||
$lang->waterfall->menuOrder[25] = 'story';
|
||||
$lang->waterfall->menuOrder[26] = 'storyGroup';
|
||||
$lang->waterfall->menuOrder[30] = 'design';
|
||||
$lang->waterfall->menuOrder[35] = 'devops';
|
||||
$lang->waterfall->menuOrder[55] = 'qa';
|
||||
@@ -367,8 +365,7 @@ $lang->execution->menu->view = array('link' => "$lang->view|execution|grouptas
|
||||
|
||||
if($config->edition != 'open') $lang->execution->menu->view = array('link' => "$lang->view|execution|gantt|executionID=%s", 'alias' => 'grouptask,tree,taskeffort,gantt,calendar,relation,maintainrelation');
|
||||
|
||||
$lang->execution->menu->storyGroup = array('link' => "{$lang->common->story}|execution|story|executionID=%s",'class' => 'dropdown dropdown-hover', 'subModule' => 'story', 'alias' => 'batchcreate,storykanban,batchtotask');
|
||||
$lang->execution->menu->story = array('link' => "$lang->SRCommon|execution|story|executionID=%s", 'subModule' => 'story', 'alias' => 'storyview,storykanban,linkstory,batchtotask');
|
||||
$lang->execution->menu->story = array('link' => "{$lang->common->story}|execution|story|executionID=%s", 'subModule' => 'story,requirement,epic', 'alias' => 'storyview,storykanban,linkstory,batchtotask');
|
||||
$lang->execution->menu->qa = array('link' => "{$lang->qa->common}|execution|bug|executionID=%s", 'subModule' => 'bug,testcase,testtask,testreport', 'alias' => 'qa,bug,testcase,testtask,testreport');
|
||||
if(helper::hasFeature('devops')) $lang->execution->menu->devops = array('link' => "{$lang->devops->common}|repo|browse|repoID=0&branchID=&objectID=%s", 'subModule' => 'repo,mr', 'alias' => 'create');
|
||||
$lang->execution->menu->doc = array('link' => "{$lang->doc->common}|execution|doc|objectID=%s", 'subModule' => 'doc');
|
||||
@@ -377,10 +374,6 @@ $lang->execution->menu->action = array('link' => "$lang->dynamic|execution|d
|
||||
$lang->execution->menu->settings = array('link' => "$lang->settings|execution|view|executionID=%s", 'subModule' => 'personnel', 'alias' => 'edit,manageproducts,team,whitelist,addwhitelist,managemembers', 'class' => 'dropdown dropdown-hover');
|
||||
$lang->execution->menu->more = array('link' => "$lang->more|execution|more|%s");
|
||||
|
||||
$lang->execution->menu->storyGroup['dropMenu'] = new stdclass();
|
||||
$lang->execution->menu->storyGroup['dropMenu']->story = array('link' => "{$lang->SRCommon}|execution|story|executionID=%s", 'subModule' => 'story');
|
||||
$lang->execution->menu->storyGroup['dropMenu']->requirement = array('link' => "{$lang->URCommon}|execution|story|executionID=%s&storyType=requirement", 'subModule' => 'story');
|
||||
|
||||
/* Execution menu order. */
|
||||
$lang->execution->menuOrder[5] = 'task';
|
||||
$lang->execution->menuOrder[10] = 'kanban';
|
||||
@@ -436,7 +429,6 @@ $lang->project->noMultiple->scrum->menu->kanban = $lang->execution->menu->ka
|
||||
$lang->project->noMultiple->scrum->menu->burn = $lang->execution->menu->burn;
|
||||
$lang->project->noMultiple->scrum->menu->view = $lang->execution->menu->view;
|
||||
$lang->project->noMultiple->scrum->menu->story = $lang->execution->menu->story;
|
||||
$lang->project->noMultiple->scrum->menu->storyGroup = $lang->execution->menu->storyGroup;
|
||||
$lang->project->noMultiple->scrum->menu->qa = $lang->scrum->menu->qa;
|
||||
$lang->project->noMultiple->scrum->menu->devops = $lang->scrum->menu->devops;
|
||||
$lang->project->noMultiple->scrum->menu->doc = $lang->scrum->menu->doc;
|
||||
@@ -606,6 +598,7 @@ $lang->navGroup->product = 'product';
|
||||
$lang->navGroup->productplan = 'product';
|
||||
$lang->navGroup->release = 'product';
|
||||
$lang->navGroup->branch = 'product';
|
||||
$lang->navGroup->epic = 'product';
|
||||
$lang->navGroup->story = 'product';
|
||||
$lang->navGroup->requirement = 'product';
|
||||
$lang->navGroup->productsettings = 'product';
|
||||
@@ -760,7 +753,8 @@ $lang->navGroup->index = 'index';
|
||||
$lang->navGroup->misc = 'misc';
|
||||
$lang->navGroup->upgrade = 'upgrade';
|
||||
|
||||
if(!$config->URAndSR) unset($lang->product->menu->requirement, $lang->product->menuOrder[35]);
|
||||
if(!$config->enableER) unset($lang->product->menu->epic, $lang->product->menuOrder[10]);
|
||||
if(!$config->URAndSR) unset($lang->product->menu->requirement, $lang->product->menuOrder[15]);
|
||||
if(!helper::hasFeature('product_roadmap')) unset($lang->product->menu->roadmap, $lang->product->menuOrder[30]);
|
||||
if(!helper::hasFeature('product_track'))
|
||||
{
|
||||
|
||||
@@ -224,6 +224,7 @@ $lang->metric->common = '度量项';
|
||||
$lang->report->common = '统计';
|
||||
$lang->system->common = '组织';
|
||||
$lang->admin->common = '后台';
|
||||
$lang->epic->common = $lang->ERCommon;
|
||||
$lang->story->common = $lang->SRCommon;
|
||||
$lang->task->common = '任务';
|
||||
$lang->bug->common = 'Bug';
|
||||
@@ -437,7 +438,8 @@ $lang->admin->cache = '缓存';
|
||||
|
||||
$lang->convert->importJira = '导入Jira数据';
|
||||
|
||||
$lang->storyConcept = '需求概念';
|
||||
$lang->storyConcept = '需求概念';
|
||||
$lang->defaultERName = '业务需求';
|
||||
|
||||
$lang->searchTips = '';
|
||||
$lang->searchAB = '搜索';
|
||||
@@ -445,7 +447,9 @@ $lang->searchAB = '搜索';
|
||||
/* 查询中可以选择的对象列表。*/
|
||||
$lang->searchObjects['all'] = '全部';
|
||||
$lang->searchObjects['bug'] = 'Bug';
|
||||
$lang->searchObjects['story'] = '需求';
|
||||
$lang->searchObjects['story'] = $lang->SRCommon;
|
||||
if($config->enableER) $lang->searchObjects['epic'] = $lang->ERCommon;
|
||||
if($config->URAndSR) $lang->searchObjects['requirement'] = $lang->URCommon;
|
||||
$lang->searchObjects['task'] = '任务';
|
||||
$lang->searchObjects['testcase'] = '用例';
|
||||
$lang->searchObjects['product'] = $lang->productCommon;
|
||||
|
||||
@@ -2446,7 +2446,7 @@ eof;
|
||||
{
|
||||
if(!empty($actionData['url']) && is_array($actionData['url']))
|
||||
{
|
||||
$moduleName = $actionData['url']['module'];
|
||||
$moduleName = ($actionData['url']['module'] == 'story' && $moduleName == 'story') ? $data->type : $actionData['url']['module'];
|
||||
$methodName = $actionData['url']['method'];
|
||||
$params = $actionData['url']['params'];
|
||||
if(!common::hasPriv($moduleName, $methodName)) return false;
|
||||
@@ -2454,7 +2454,7 @@ eof;
|
||||
}
|
||||
else if(!empty($actionData['data-url']) && is_array($actionData['data-url']))
|
||||
{
|
||||
$moduleName = $actionData['data-url']['module'];
|
||||
$moduleName = ($actionData['data-url']['module'] == 'story' && $moduleName == 'story') ? $data->type : $actionData['data-url']['module'];
|
||||
$methodName = $actionData['data-url']['method'];
|
||||
$params = $actionData['data-url']['params'];
|
||||
if(!common::hasPriv($moduleName, $methodName)) return false;
|
||||
|
||||
+26
-16
@@ -1,21 +1,25 @@
|
||||
<?php
|
||||
$config->custom = new stdClass();
|
||||
$config->custom->canAdd['story'] = 'reasonList,sourceList,priList,categoryList';
|
||||
$config->custom->canAdd['task'] = 'priList,typeList,reasonList';
|
||||
$config->custom->canAdd['bug'] = 'priList,severityList,osList,browserList,typeList,resolutionList';
|
||||
$config->custom->canAdd['testcase'] = 'priList,typeList,stageList,resultList,statusList';
|
||||
$config->custom->canAdd['testtask'] = 'priList,typeList';
|
||||
$config->custom->canAdd['todo'] = 'priList,typeList';
|
||||
$config->custom->canAdd['user'] = 'roleList';
|
||||
$config->custom->canAdd['block'] = '';
|
||||
$config->custom->canAdd['project'] = 'unitList';
|
||||
$config->custom->canAdd['epic'] = 'reasonList,sourceList,priList,categoryList';
|
||||
$config->custom->canAdd['story'] = 'reasonList,sourceList,priList,categoryList';
|
||||
$config->custom->canAdd['requirement'] = 'reasonList,sourceList,priList,categoryList';
|
||||
$config->custom->canAdd['task'] = 'priList,typeList,reasonList';
|
||||
$config->custom->canAdd['bug'] = 'priList,severityList,osList,browserList,typeList,resolutionList';
|
||||
$config->custom->canAdd['testcase'] = 'priList,typeList,stageList,resultList,statusList';
|
||||
$config->custom->canAdd['testtask'] = 'priList,typeList';
|
||||
$config->custom->canAdd['todo'] = 'priList,typeList';
|
||||
$config->custom->canAdd['user'] = 'roleList';
|
||||
$config->custom->canAdd['block'] = '';
|
||||
$config->custom->canAdd['project'] = 'unitList';
|
||||
|
||||
$config->custom->noModuleMenu = array();
|
||||
|
||||
$config->custom->requiredModules[10] = 'product';
|
||||
$config->custom->requiredModules[15] = 'story';
|
||||
$config->custom->requiredModules[20] = 'productplan';
|
||||
$config->custom->requiredModules[25] = 'release';
|
||||
$config->custom->requiredModules[5] = 'product';
|
||||
$config->custom->requiredModules[10] = 'epic';
|
||||
$config->custom->requiredModules[15] = 'requirement';
|
||||
$config->custom->requiredModules[20] = 'story';
|
||||
$config->custom->requiredModules[25] = 'productplan';
|
||||
$config->custom->requiredModules[30] = 'release';
|
||||
|
||||
$config->custom->requiredModules[30] = 'project';
|
||||
$config->custom->requiredModules[35] = 'execution';
|
||||
@@ -23,7 +27,7 @@ $config->custom->requiredModules[40] = 'task';
|
||||
$config->custom->requiredModules[45] = 'build';
|
||||
|
||||
$config->custom->allFeatures = array('program', 'productRR', 'productUR', 'productLine', 'projectScrum', 'projectWaterfall', 'projectKanban', 'projectAgileplus', 'projectWaterfallplus', 'execution', 'qa', 'devops', 'ai', 'report', 'kanban', 'doc', 'system', 'admin', 'vision');
|
||||
$config->custom->dataFeatures = array('productUR', 'waterfall', 'waterfallplus');
|
||||
$config->custom->dataFeatures = array('productER', 'productUR', 'waterfall', 'waterfallplus');
|
||||
$config->custom->scrumFeatures = array();
|
||||
|
||||
$config->custom->requiredModules[50] = 'bug';
|
||||
@@ -32,9 +36,7 @@ $config->custom->requiredModules[60] = 'testsuite';
|
||||
$config->custom->requiredModules[65] = 'testreport';
|
||||
$config->custom->requiredModules[70] = 'caselib';
|
||||
$config->custom->requiredModules[75] = 'testtask';
|
||||
|
||||
$config->custom->requiredModules[80] = 'doc';
|
||||
|
||||
$config->custom->requiredModules[85] = 'user';
|
||||
|
||||
$config->custom->fieldList['program']['create'] = 'budget,PM,desc';
|
||||
@@ -43,10 +45,18 @@ $config->custom->fieldList['project']['create'] = 'budget,PM,desc';
|
||||
$config->custom->fieldList['project']['edit'] = 'budget,PM,desc';
|
||||
$config->custom->fieldList['product']['create'] = 'PO,QD,RD,type,desc';
|
||||
$config->custom->fieldList['product']['edit'] = 'PO,QD,RD,type,desc,status';
|
||||
$config->custom->fieldList['epic']['create'] = 'module,plan,source,pri,estimate,keywords,spec,verify';
|
||||
$config->custom->fieldList['epic']['change'] = 'comment,spec,verify';
|
||||
$config->custom->fieldList['epic']['close'] = 'comment';
|
||||
$config->custom->fieldList['epic']['review'] = 'reviewedDate,comment';
|
||||
$config->custom->fieldList['story']['create'] = 'module,plan,source,pri,estimate,keywords,spec,verify';
|
||||
$config->custom->fieldList['story']['change'] = 'comment,spec,verify';
|
||||
$config->custom->fieldList['story']['close'] = 'comment';
|
||||
$config->custom->fieldList['story']['review'] = 'reviewedDate,comment';
|
||||
$config->custom->fieldList['requirement']['create'] = 'module,plan,source,pri,estimate,keywords,spec,verify';
|
||||
$config->custom->fieldList['requirement']['change'] = 'comment,spec,verify';
|
||||
$config->custom->fieldList['requirement']['close'] = 'comment';
|
||||
$config->custom->fieldList['requirement']['review'] = 'reviewedDate,comment';
|
||||
$config->custom->fieldList['productplan'] = 'begin,end,desc';
|
||||
$config->custom->fieldList['release']['create'] = 'desc';
|
||||
$config->custom->fieldList['release']['edit'] = 'desc';
|
||||
|
||||
@@ -3,15 +3,22 @@ $config->custom->browseStoryConcept->dtable = new stdclass();
|
||||
$config->custom->browseStoryConcept->dtable->fieldList['default']['type'] = 'id';
|
||||
$config->custom->browseStoryConcept->dtable->fieldList['default']['sortType'] = false;
|
||||
|
||||
if($config->enableER)
|
||||
{
|
||||
$config->custom->browseStoryConcept->dtable->fieldList['ERName']['title'] = $lang->custom->ERConcept;
|
||||
$config->custom->browseStoryConcept->dtable->fieldList['ERName']['type'] = 'category';
|
||||
$config->custom->browseStoryConcept->dtable->fieldList['ERName']['sortType'] = false;
|
||||
}
|
||||
|
||||
if($config->URAndSR)
|
||||
{
|
||||
$config->custom->browseStoryConcept->dtable->fieldList['URName']['title'] = $lang->custom->URConcept;
|
||||
$config->custom->browseStoryConcept->dtable->fieldList['URName']['type'] = 'title';
|
||||
$config->custom->browseStoryConcept->dtable->fieldList['URName']['type'] = 'category';
|
||||
$config->custom->browseStoryConcept->dtable->fieldList['URName']['sortType'] = false;
|
||||
}
|
||||
|
||||
$config->custom->browseStoryConcept->dtable->fieldList['SRName']['title'] = $lang->custom->SRConcept;
|
||||
$config->custom->browseStoryConcept->dtable->fieldList['SRName']['type'] = 'title';
|
||||
$config->custom->browseStoryConcept->dtable->fieldList['SRName']['type'] = 'category';
|
||||
$config->custom->browseStoryConcept->dtable->fieldList['SRName']['sortType'] = false;
|
||||
|
||||
$config->custom->browseStoryConcept->dtable->fieldList['actions']['type'] = 'actions';
|
||||
|
||||
@@ -60,7 +60,6 @@ class custom extends control
|
||||
|
||||
$this->view->title = $this->lang->custom->common . $this->lang->hyphen . $this->lang->$module->common;
|
||||
$this->view->field = $field;
|
||||
$this->view->lang2Set = str_replace('_', '-', $lang);
|
||||
$this->view->module = $module;
|
||||
$this->view->currentLang = $currentLang;
|
||||
$this->view->canAdd = strpos($this->config->custom->canAdd[$module], $field) !== false;
|
||||
@@ -254,6 +253,66 @@ class custom extends control
|
||||
return $this->send(array('result' => 'success', 'load' => inlink('browseStoryConcept')));
|
||||
}
|
||||
|
||||
/**
|
||||
* 停用需求等级。
|
||||
* Close story grade.
|
||||
*
|
||||
* @param string $type
|
||||
* @param int $gradeID
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function closeGrade(string $type = 'story', int $gradeID = 0)
|
||||
{
|
||||
if($gradeID) $this->dao->update(TABLE_STORYGRADE)->set('status')->eq('disable')->where('grade')->eq($gradeID)->andWhere('type')->eq($type)->exec();
|
||||
return $this->sendSuccess(array('load' => true));
|
||||
}
|
||||
|
||||
/**
|
||||
* 启用需求等级。
|
||||
* Activate story grade.
|
||||
*
|
||||
* @param string $type
|
||||
* @param int $gradeID
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function activateGrade(string $type = 'story', int $gradeID = 0)
|
||||
{
|
||||
if($gradeID) $this->dao->update(TABLE_STORYGRADE)->set('status')->eq('enable')->where('grade')->eq($gradeID)->andWhere('type')->eq($type)->exec();
|
||||
return $this->sendSuccess(array('load' => true));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除需求等级。
|
||||
* Delete story grade.
|
||||
*
|
||||
* @param string $type
|
||||
* @param int $gradeID
|
||||
* @param string $confirm
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function deleteGrade(string $type = 'story', int $gradeID = 0, string $confirm = 'no')
|
||||
{
|
||||
$story = $this->dao->select('id')->from(TABLE_STORY)->where('grade')->eq($gradeID)->andWhere('type')->eq($type)->limit(1)->fetch('id');
|
||||
if($story) return $this->send(array('result' => 'fail', 'callback' => "zui.Modal.alert('{$this->lang->custom->notice->gradeNotEmpty}');"));
|
||||
|
||||
if($confirm == 'no')
|
||||
{
|
||||
$formUrl = $this->createLink('custom', 'deleteGrade', "type=$type&gradeID=$gradeID&confirm=yes");
|
||||
return $this->send(array('result' => 'fail', 'callback' => "zui.Modal.confirm({message: '{$this->lang->custom->notice->deleteGrade}'}).then((res) => {if(res) $.ajaxSubmit({url: '{$formUrl}'});});"));
|
||||
}
|
||||
|
||||
if($gradeID)
|
||||
{
|
||||
$grades = $this->dao->select('*')->from(TABLE_STORYGRADE)->where('grade')->gt($gradeID)->andWhere('type')->eq($type)->orderBy('grade_asc')->fetchAll();
|
||||
$this->dao->delete()->from(TABLE_STORYGRADE)->where('grade')->eq($gradeID)->andWhere('type')->eq($type)->exec();
|
||||
foreach($grades as $grade) $this->dao->update(TABLE_STORYGRADE)->set('grade')->eq($grade->grade - 1)->where('grade')->eq($grade->grade)->andWhere('type')->eq($type)->exec();
|
||||
}
|
||||
return $this->sendSuccess(array('load' => true));
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行关闭设置。
|
||||
* Set whether the closed execution is read-only.
|
||||
@@ -396,19 +455,25 @@ class custom extends control
|
||||
if(!isset($this->config->custom->customFields[$module][$section])) return;
|
||||
if(!in_array($key, $this->config->custom->customFields[$module][$section])) return;
|
||||
|
||||
$this->loadModel('setting');
|
||||
$global = $this->post->global;
|
||||
$action = $this->post->action;
|
||||
$account = $this->app->user->account;
|
||||
if($this->server->request_method == 'POST')
|
||||
if($global) $account = 'system';
|
||||
if($this->server->request_method == 'POST' && $action != 'reset')
|
||||
{
|
||||
$fields = $this->post->fields;
|
||||
if(is_array($fields)) $fields = implode(',', $fields);
|
||||
if($module == 'execution' && $section == 'custom' && $key == 'createFields' && strpos(",{$fields},", ',team,') !== false) $fields .= ',teams';
|
||||
$this->loadModel('setting')->setItem("{$account}.{$module}.{$section}.{$key}", $fields);
|
||||
|
||||
$this->setting->setItem("{$account}.{$module}.{$section}.{$key}", $fields);
|
||||
|
||||
if(in_array($module, array('story', 'task', 'testcase')) && $section == 'custom' && $key == 'batchCreateFields') return;
|
||||
if($module == 'bug' && $section == 'custom' && $key == 'batchCreateFields') return;
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->loadModel('setting')->deleteItems("owner={$account}&module={$module}§ion={$section}&key={$key}");
|
||||
$this->setting->deleteItems("owner={$account}&module={$module}§ion={$section}&key={$key}");
|
||||
}
|
||||
|
||||
$this->loadModel('common')->loadConfigFromDB();
|
||||
@@ -416,8 +481,8 @@ class custom extends control
|
||||
$this->app->loadConfig($module);
|
||||
|
||||
if($module == 'programplan' && $section == 'custom') $key = 'createFields';
|
||||
$customFields = $this->config->$module->list->{'custom' . ucfirst($key)};
|
||||
$showFields = $this->config->$module->custom->$key;
|
||||
$customFields = zget(zget($this->config->$module, 'list', array()), $section . ucfirst($key), '');
|
||||
$showFields = zget(zget($this->config->$module, $section, array()), $key, '');
|
||||
if($module == 'marketresearch') return print(js::reload('parent'));
|
||||
return $this->send(array('result' => 'success', 'key' => $key, 'callback' => 'loadCurrentPage', 'customFields' => $customFields, 'showFields' => $showFields));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
#requiredForm .form-row .form-group {padding-left: 8rem;}
|
||||
#requiredForm .form-row .form-label {width: 8rem;}
|
||||
:root {--form-horz-label-width: 7rem}
|
||||
@@ -4,3 +4,16 @@
|
||||
.lang-row .form-group {padding-left: 0; justify-content: center;}
|
||||
.lang-row .form-group > .form-label {width: var(--form-horz-label-width);}
|
||||
.unit-box .checkbox-primary {flex: 0 1 15%;}
|
||||
#gradeList {width: 50%}
|
||||
.gradeRuleNotice {margin-left: 4.25rem}
|
||||
td.required:after {
|
||||
--tw-translate-y: 0.125rem;
|
||||
--tw-scale-x: 1.25;
|
||||
--tw-scale-y: 1.25;
|
||||
--tw-text-opacity: 1;
|
||||
color: rgba(var(--color-danger-500-rgb), var(--tw-text-opacity));
|
||||
content: "*";
|
||||
display: inline-block;
|
||||
margin-left: 0.25rem;
|
||||
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
|
||||
}
|
||||
|
||||
@@ -89,3 +89,26 @@ window.savaTestcaseReview = function()
|
||||
|
||||
$.ajaxSubmit({url, data:form});
|
||||
}
|
||||
|
||||
window.deleteGrade = function()
|
||||
{
|
||||
$(this).closest('tr').remove();
|
||||
$('#gradeList tbody').find('tr').last().find('.btn-add-grade').removeClass('hidden');
|
||||
}
|
||||
|
||||
window.addGrade = function()
|
||||
{
|
||||
let newRow = $(this).closest('tr').clone();
|
||||
let maxIndex = $(this).closest('tbody').find('tr').length;
|
||||
newIndex = parseInt(maxIndex);
|
||||
|
||||
newRow.find('input').val('');
|
||||
newRow.find('.btn-delete-grade').attr('href', 'javascript:void').removeClass('ajax-submit hidden').on('click', deleteGrade);
|
||||
newRow.find('.btn-close').remove();
|
||||
newRow.find("input[type=hidden]").val(newIndex);
|
||||
newRow.find('td.index').text(newIndex);
|
||||
newRow.find('td.grade-status').text(enableLang);
|
||||
$(this).closest('tbody').append(newRow);
|
||||
|
||||
$(this).addClass('hidden');
|
||||
};
|
||||
|
||||
@@ -40,6 +40,7 @@ $lang->custom->setStoryConcept = "Set Story Concept";
|
||||
$lang->custom->setDefaultConcept = "Set Default Concept";
|
||||
$lang->custom->browseStoryConcept = "List of story concepts";
|
||||
$lang->custom->deleteStoryConcept = "Delete story Concept";
|
||||
$lang->custom->ERConcept = "ER Concept";
|
||||
$lang->custom->URConcept = "UR Concept";
|
||||
$lang->custom->SRConcept = "SR Concept";
|
||||
$lang->custom->reviewRule = 'Review Rules';
|
||||
@@ -63,7 +64,14 @@ $lang->custom->setPercent = "Enable or Disable {$lang->stage->percent}
|
||||
$lang->custom->beginAndEndDate = 'Begin & End';
|
||||
$lang->custom->beginAndEndDateRange = 'The Range Of Begin & End';
|
||||
$lang->custom->limitTaskDateAction = 'Set start and end date required';
|
||||
$lang->custom->closeGrade = 'Close Story Grade';
|
||||
$lang->custom->activateGrade = 'Activate Story Grade';
|
||||
$lang->custom->deleteGrade = 'Delete Story Grade';
|
||||
$lang->custom->closeSetting = 'Close Setting';
|
||||
$lang->custom->gradeRule = 'Allow cross-level segmentation';
|
||||
|
||||
$lang->custom->gradeRuleList['cross'] = 'Yes';
|
||||
$lang->custom->gradeRuleList['stepwise'] = 'No';
|
||||
|
||||
$lang->custom->unitList['efficiency'] = 'Working Hours/';
|
||||
$lang->custom->unitList['manhour'] = 'Man-hour/';
|
||||
@@ -95,6 +103,9 @@ $lang->custom->closedExecution = 'Closed ' . $lang->executionCommon;
|
||||
$lang->custom->closedKanban = 'Closed ' . $lang->custom->kanban;
|
||||
$lang->custom->closedProduct = 'Closed ' . $lang->productCommon;
|
||||
|
||||
$lang->custom->gradeStatusList['enable'] = 'Normal';
|
||||
$lang->custom->gradeStatusList['disable'] = 'Disabled';
|
||||
|
||||
$lang->custom->block = new stdclass();
|
||||
$lang->custom->block->fields['closed'] = 'Closed Block';
|
||||
|
||||
@@ -125,6 +136,9 @@ $lang->custom->story->fields['reviewRules'] = 'Review Rules';
|
||||
$lang->custom->story->fields['reviewResultList'] = 'Review Result';
|
||||
$lang->custom->story->fields['review'] = 'Review Required';
|
||||
|
||||
$lang->custom->epic = clone $lang->custom->story;
|
||||
$lang->custom->requirement = clone $lang->custom->story;
|
||||
|
||||
$lang->custom->task = new stdClass();
|
||||
$lang->custom->task->fields['required'] = $lang->custom->required;
|
||||
$lang->custom->task->fields['priList'] = 'Priority';
|
||||
@@ -214,6 +228,11 @@ $lang->custom->notice->selectAllTip = 'After selecting all people, the re
|
||||
$lang->custom->notice->repeatKey = 'Repeat Key %s';
|
||||
$lang->custom->notice->readOnlyOfCode = "A code is a management term that exists for secrecy or as an antonym. When code management is enabled, the code information of {$lang->productCommon}, {$lang->projectCommon}, and execution in the system will be displayed in the creation, editing, detail, and list pages.";
|
||||
$lang->custom->notice->readOnlyOfPercent = "The \"Workload Ratio\" is used to divide the workload of a {$lang->projectCommon} into different stages. The sum of the percentages of the same level stages cannot exceed 100%. After enabling the \"Workload Ratio\", users have to fill in the ratio fields when setting up the stages in the Waterfall {$lang->projectCommon} and Waterfall Plus {$lang->projectCommon} management models.";
|
||||
$lang->custom->notice->closeGrade = "After closing, the grade cannot be used to create new stories.";
|
||||
$lang->custom->notice->activateGrade = "After activating, the grade can be used to create new stories.";
|
||||
$lang->custom->notice->gradeNotEmpty = 'There are stories in this grade, and it cannot be deleted!';
|
||||
$lang->custom->notice->deleteGrade = 'Are you sure you want to delete this grade?';
|
||||
$lang->custom->notice->gradeRule = 'Cross-lavel segmentation: Requirements can be created from any requirement system and support cross-system associated parent relationships. For example: you can create third-level requirements directly under first-level requirements.';
|
||||
|
||||
$lang->custom->notice->indexPage['product'] = "ZenTao 8.2+ has Product Homepage. Do you want to go to Product Homepage?";
|
||||
$lang->custom->notice->indexPage['project'] = "ZenTao 8.2+ has {$lang->projectCommon} Home. Do you want to go to {$lang->projectCommon} Home?";
|
||||
|
||||
@@ -40,6 +40,7 @@ $lang->custom->setStoryConcept = "Set Story Concept";
|
||||
$lang->custom->setDefaultConcept = "Set Default Concept";
|
||||
$lang->custom->browseStoryConcept = "List of story concepts";
|
||||
$lang->custom->deleteStoryConcept = "Delete story Concept";
|
||||
$lang->custom->ERConcept = "ER Concept";
|
||||
$lang->custom->URConcept = "UR Concept";
|
||||
$lang->custom->SRConcept = "SR Concept";
|
||||
$lang->custom->reviewRule = 'Review Rules';
|
||||
@@ -63,7 +64,14 @@ $lang->custom->setPercent = "Enable or Disable {$lang->stage->percent}
|
||||
$lang->custom->beginAndEndDate = 'Begin & End';
|
||||
$lang->custom->beginAndEndDateRange = 'The Range Of Begin & End';
|
||||
$lang->custom->limitTaskDateAction = 'Set start and end date required';
|
||||
$lang->custom->closeGrade = 'Close Story Grade';
|
||||
$lang->custom->activateGrade = 'Activate Story Grade';
|
||||
$lang->custom->deleteGrade = 'Delete Story Grade';
|
||||
$lang->custom->closeSetting = 'Close Setting';
|
||||
$lang->custom->gradeRule = 'Allow cross-level segmentation';
|
||||
|
||||
$lang->custom->gradeRuleList['cross'] = 'Yes';
|
||||
$lang->custom->gradeRuleList['stepwise'] = 'No';
|
||||
|
||||
$lang->custom->unitList['efficiency'] = 'Working Hours/';
|
||||
$lang->custom->unitList['manhour'] = 'Man-hour/';
|
||||
@@ -95,6 +103,9 @@ $lang->custom->closedExecution = 'Closed ' . $lang->executionCommon;
|
||||
$lang->custom->closedKanban = 'Closed ' . $lang->custom->kanban;
|
||||
$lang->custom->closedProduct = 'Closed ' . $lang->productCommon;
|
||||
|
||||
$lang->custom->gradeStatusList['enable'] = 'Normal';
|
||||
$lang->custom->gradeStatusList['disable'] = 'Disabled';
|
||||
|
||||
$lang->custom->block = new stdclass();
|
||||
$lang->custom->block->fields['closed'] = 'Closed Block';
|
||||
|
||||
@@ -125,6 +136,9 @@ $lang->custom->story->fields['reviewRules'] = 'Review Rules';
|
||||
$lang->custom->story->fields['reviewResultList'] = 'Review Result';
|
||||
$lang->custom->story->fields['review'] = 'Need Review';
|
||||
|
||||
$lang->custom->epic = clone $lang->custom->story;
|
||||
$lang->custom->requirement = clone $lang->custom->story;
|
||||
|
||||
$lang->custom->task = new stdClass();
|
||||
$lang->custom->task->fields['required'] = $lang->custom->required;
|
||||
$lang->custom->task->fields['priList'] = 'Priority';
|
||||
@@ -214,6 +228,11 @@ $lang->custom->notice->selectAllTip = 'After selecting all people, the re
|
||||
$lang->custom->notice->repeatKey = 'Repeat Key %s';
|
||||
$lang->custom->notice->readOnlyOfCode = "A code is a management term that exists for secrecy or as an antonym. When code management is enabled, the code information of {$lang->productCommon}, {$lang->projectCommon}, and execution in the system will be displayed in the creation, editing, detail, and list pages.";
|
||||
$lang->custom->notice->readOnlyOfPercent = "The \"Workload Ratio\" is used to divide the workload of a {$lang->projectCommon} into different stages. The sum of the percentages of the same level stages cannot exceed 100%. After enabling the \"Workload Ratio\", users have to fill in the ratio fields when setting up the stages in the Waterfall {$lang->projectCommon} and Waterfall Plus {$lang->projectCommon} management models.";
|
||||
$lang->custom->notice->closeGrade = "After closing, the grade cannot be used to create new stories.";
|
||||
$lang->custom->notice->activateGrade = "After activating, the grade can be used to create new stories.";
|
||||
$lang->custom->notice->gradeNotEmpty = 'There are stories in this grade, and it cannot be deleted!';
|
||||
$lang->custom->notice->deleteGrade = 'Are you sure you want to delete this grade?';
|
||||
$lang->custom->notice->gradeRule = 'Cross-lavel segmentation: Requirements can be created from any requirement system and support cross-system associated parent relationships. For example: you can create third-level requirements directly under first-level requirements.';
|
||||
|
||||
$lang->custom->notice->indexPage['product'] = "ZenTao 8.2+ has Product Home. Do you want to go to Product Home?";
|
||||
$lang->custom->notice->indexPage['project'] = "ZenTao 8.2+ has {$lang->projectCommon} Home. Do you want to go to {$lang->projectCommon} Home?";
|
||||
|
||||
@@ -40,6 +40,7 @@ $lang->custom->setStoryConcept = "Set Story Concept";
|
||||
$lang->custom->setDefaultConcept = "Set Default Concept";
|
||||
$lang->custom->browseStoryConcept = "List of story concepts";
|
||||
$lang->custom->deleteStoryConcept = "Delete story Concept";
|
||||
$lang->custom->ERConcept = "ER Concept";
|
||||
$lang->custom->URConcept = "UR Concept";
|
||||
$lang->custom->SRConcept = "SR Concept";
|
||||
$lang->custom->reviewRule = 'Review Rules';
|
||||
@@ -63,7 +64,14 @@ $lang->custom->setPercent = "Enable or Disable {$lang->stage->percent}
|
||||
$lang->custom->beginAndEndDate = 'Begin & End';
|
||||
$lang->custom->beginAndEndDateRange = 'The Range Of Begin & End';
|
||||
$lang->custom->limitTaskDateAction = 'Set start and end date required';
|
||||
$lang->custom->closeGrade = 'Close Story Grade';
|
||||
$lang->custom->activateGrade = 'Activate Story Grade';
|
||||
$lang->custom->deleteGrade = 'Delete Story Grade';
|
||||
$lang->custom->closeSetting = 'Close Setting';
|
||||
$lang->custom->gradeRule = 'Allow cross-level segmentation';
|
||||
|
||||
$lang->custom->gradeRuleList['cross'] = 'Yes';
|
||||
$lang->custom->gradeRuleList['stepwise'] = 'No';
|
||||
|
||||
$lang->custom->unitList['efficiency'] = 'Working Hours/';
|
||||
$lang->custom->unitList['manhour'] = 'Man-hour/';
|
||||
@@ -95,6 +103,9 @@ $lang->custom->closedExecution = 'Closed ' . $lang->executionCommon;
|
||||
$lang->custom->closedKanban = 'Closed ' . $lang->custom->kanban;
|
||||
$lang->custom->closedProduct = 'Closed ' . $lang->productCommon;
|
||||
|
||||
$lang->custom->gradeStatusList['enable'] = 'Normal';
|
||||
$lang->custom->gradeStatusList['disable'] = 'Disabled';
|
||||
|
||||
$lang->custom->block = new stdclass();
|
||||
$lang->custom->block->fields['closed'] = 'Bloc Fermé';
|
||||
|
||||
@@ -125,6 +136,9 @@ $lang->custom->story->fields['reviewRules'] = 'Review Rules';
|
||||
$lang->custom->story->fields['reviewResultList'] = 'Valider Résultats';
|
||||
$lang->custom->story->fields['review'] = 'Validation Requise';
|
||||
|
||||
$lang->custom->epic = clone $lang->custom->story;
|
||||
$lang->custom->requirement = clone $lang->custom->story;
|
||||
|
||||
$lang->custom->task = new stdClass();
|
||||
$lang->custom->task->fields['required'] = $lang->custom->required;
|
||||
$lang->custom->task->fields['priList'] = 'Priorité';
|
||||
@@ -214,6 +228,11 @@ $lang->custom->notice->selectAllTip = 'After selecting all people, the re
|
||||
$lang->custom->notice->repeatKey = 'Repeat Key %s';
|
||||
$lang->custom->notice->readOnlyOfCode = "A code is a management term that exists for secrecy or as an antonym. When code management is enabled, the code information of {$lang->productCommon}, {$lang->projectCommon}, and execution in the system will be displayed in the creation, editing, detail, and list pages.";
|
||||
$lang->custom->notice->readOnlyOfPercent = "The \"Workload Ratio\" is used to divide the workload of a {$lang->projectCommon} into different stages. The sum of the percentages of the same level stages cannot exceed 100%. After enabling the \"Workload Ratio\", users have to fill in the ratio fields when setting up the stages in the Waterfall {$lang->projectCommon} and Waterfall Plus {$lang->projectCommon} management models.";
|
||||
$lang->custom->notice->closeGrade = "After closing, the grade cannot be used to create new stories.";
|
||||
$lang->custom->notice->activateGrade = "After activating, the grade can be used to create new stories.";
|
||||
$lang->custom->notice->gradeNotEmpty = 'There are stories in this grade, and it cannot be deleted!';
|
||||
$lang->custom->notice->deleteGrade = 'Are you sure you want to delete this grade?';
|
||||
$lang->custom->notice->gradeRule = 'Cross-lavel segmentation: Requirements can be created from any requirement system and support cross-system associated parent relationships. For example: you can create third-level requirements directly under first-level requirements.';
|
||||
|
||||
$lang->custom->notice->indexPage['product'] = "ZenTao 8.2+ possède une page d'accueil. Voulez-vous consulter la page d'accueil du produit ?";
|
||||
$lang->custom->notice->indexPage['project'] = "ZenTao 8.2+ has {$lang->projectCommon} Home. Do you want to go to {$lang->projectCommon} Home?";
|
||||
|
||||
@@ -40,6 +40,7 @@ $lang->custom->setStoryConcept = "设置需求概念";
|
||||
$lang->custom->setDefaultConcept = "设置默认概念";
|
||||
$lang->custom->browseStoryConcept = "需求概念列表";
|
||||
$lang->custom->deleteStoryConcept = "删除需求概念";
|
||||
$lang->custom->ERConcept = "业需概念";
|
||||
$lang->custom->URConcept = "用需概念";
|
||||
$lang->custom->SRConcept = "软需概念";
|
||||
$lang->custom->reviewRule = "评审规则";
|
||||
@@ -63,7 +64,14 @@ $lang->custom->setPercent = "是否启用{$lang->stage->percent}";
|
||||
$lang->custom->beginAndEndDate = '起止日期';
|
||||
$lang->custom->beginAndEndDateRange = '起止日期范围';
|
||||
$lang->custom->limitTaskDateAction = '设置起止日期必填';
|
||||
$lang->custom->closeGrade = '停用需求层级';
|
||||
$lang->custom->activateGrade = '启用需求层级';
|
||||
$lang->custom->deleteGrade = '删除需求层级';
|
||||
$lang->custom->closeSetting = '关闭设置';
|
||||
$lang->custom->gradeRule = '是否允许跨层级细分';
|
||||
|
||||
$lang->custom->gradeRuleList['cross'] = '是';
|
||||
$lang->custom->gradeRuleList['stepwise'] = '否';
|
||||
|
||||
$lang->custom->unitList['efficiency'] = '工时/';
|
||||
$lang->custom->unitList['manhour'] = '人时/';
|
||||
@@ -95,6 +103,9 @@ $lang->custom->closedExecution = '已关闭' . $lang->custom->executionCommon;
|
||||
$lang->custom->closedKanban = '已关闭' . $lang->custom->kanban;
|
||||
$lang->custom->closedProduct = '已关闭' . $lang->productCommon;
|
||||
|
||||
$lang->custom->gradeStatusList['enable'] = '正常';
|
||||
$lang->custom->gradeStatusList['disable'] = '停用';
|
||||
|
||||
$lang->custom->block = new stdclass();
|
||||
$lang->custom->block->fields['closed'] = '关闭的区块';
|
||||
|
||||
@@ -125,6 +136,9 @@ $lang->custom->story->fields['reviewRules'] = '评审规则';
|
||||
$lang->custom->story->fields['reviewResultList'] = '评审结果';
|
||||
$lang->custom->story->fields['review'] = '评审流程';
|
||||
|
||||
$lang->custom->epic = clone $lang->custom->story;
|
||||
$lang->custom->requirement = clone $lang->custom->story;
|
||||
|
||||
$lang->custom->task = new stdClass();
|
||||
$lang->custom->task->fields['required'] = $lang->custom->required;
|
||||
$lang->custom->task->fields['priList'] = '优先级';
|
||||
@@ -214,6 +228,11 @@ $lang->custom->notice->selectAllTip = '勾选所有人员后,会清空
|
||||
$lang->custom->notice->repeatKey = '%s键重复';
|
||||
$lang->custom->notice->readOnlyOfCode = "代号是一种管理话术,主要便于保密或作为别名存在。启用代号管理后,系统中的{$lang->productCommon}、{$lang->projectCommon}、执行在创建、编辑、详情、列表等页面均会展示代号信息。";
|
||||
$lang->custom->notice->readOnlyOfPercent = "工作量占比用于划分{$lang->projectCommon}中存在多个阶段时的工作量的占比,同一级阶段的百分比之和最高为100%。启用工作量占比后,系统中的瀑布{$lang->projectCommon}和融合瀑布{$lang->projectCommon}模型中设置阶段时需要维护阶段的工作量占比。";
|
||||
$lang->custom->notice->closeGrade = "停用后,该层级无法再创建新需求。";
|
||||
$lang->custom->notice->activateGrade = "启用后,该层级可以创建新需求。";
|
||||
$lang->custom->notice->gradeNotEmpty = '系统内存在该层级的需求,不能删除!';
|
||||
$lang->custom->notice->deleteGrade = '层级删除后无法恢复!';
|
||||
$lang->custom->notice->gradeRule = '跨层细分:可从任意需求层级创建需求,并支持跨层级关联父子关系。例如:您可以在第一层需求下直接创建第三层需求。';
|
||||
|
||||
$lang->custom->notice->indexPage['product'] = "从8.2版本起增加了产品主页视图,是否默认进入产品主页?";
|
||||
$lang->custom->notice->indexPage['project'] = "从8.2版本起增加了{$lang->projectCommon}主页视图,是否默认进入{$lang->projectCommon}主页?";
|
||||
|
||||
+28
-5
@@ -634,7 +634,9 @@ class customModel extends model
|
||||
foreach($langData as $content)
|
||||
{
|
||||
$value = json_decode($content->value);
|
||||
$URSRPairs[$content->key] = $this->config->URAndSR ? $value->URName . '/' . $value->SRName : $value->SRName;
|
||||
if(!$this->config->URAndSR && !$this->config->enableER) $URSRPairs[$content->key] = $value->SRName;
|
||||
if($this->config->URAndSR && !$this->config->enableER) $URSRPairs[$content->key] = $value->URName . '/' . $value->SRName;
|
||||
if($this->config->URAndSR && $this->config->enableER) $URSRPairs[$content->key] = $value->ERName . '/' . $value->URName . '/' . $value->SRName;
|
||||
}
|
||||
|
||||
return $URSRPairs;
|
||||
@@ -722,6 +724,7 @@ class customModel extends model
|
||||
$value = json_decode($URSRData->value);
|
||||
$URSRList[$URSRData->key] = new stdclass();
|
||||
$URSRList[$URSRData->key]->key = $URSRData->key;
|
||||
$URSRList[$URSRData->key]->ERName = $value->ERName;
|
||||
$URSRList[$URSRData->key]->SRName = $value->SRName;
|
||||
$URSRList[$URSRData->key]->URName = $value->URName;
|
||||
$URSRList[$URSRData->key]->system = $URSRData->system;
|
||||
@@ -833,11 +836,13 @@ class customModel extends model
|
||||
/* If has custom UR and SR name. */
|
||||
foreach($data['SRName'] as $key => $SRName)
|
||||
{
|
||||
if(isset($data['URName'])) $URName = zget($data['URName'], $key, '');
|
||||
if(!isset($data['URName'])) $URName = $this->lang->URCommon;
|
||||
if(!$URName || !$SRName) continue;
|
||||
$URName = zget($data['URName'], $key, $this->lang->URCommon);
|
||||
$ERName = zget($data['ERName'], $key, $this->lang->ERCommon);
|
||||
|
||||
if(!$URName || !$SRName || !$ERName) continue;
|
||||
|
||||
$URSRList = new stdclass();
|
||||
$URSRList->ERName = $ERName;
|
||||
$URSRList->SRName = $SRName;
|
||||
$URSRList->URName = $URName;
|
||||
|
||||
@@ -863,7 +868,7 @@ class customModel extends model
|
||||
public function updateURAndSR(int $key = 0, string $lang = '', array $data = array()): bool
|
||||
{
|
||||
if(empty($lang)) $lang = $this->app->getClientLang();
|
||||
if(empty($data['SRName']) || empty($data['URName'])) return false;
|
||||
if(empty($data['SRName']) || empty($data['URName']) || empty($data['ERName'])) return false;
|
||||
|
||||
$oldValue = $this->getURSRConcept($key, $lang);
|
||||
$oldValue = json_decode($oldValue);
|
||||
@@ -871,8 +876,10 @@ class customModel extends model
|
||||
if(!$oldValue) return false;
|
||||
|
||||
$URSRList = new stdclass();
|
||||
$URSRList->defaultERName = zget($oldValue, 'defaultERName', $oldValue->ERName);
|
||||
$URSRList->defaultSRName = zget($oldValue, 'defaultSRName', $oldValue->SRName);
|
||||
$URSRList->defaultURName = zget($oldValue, 'defaultURName', $oldValue->URName);
|
||||
$URSRList->ERName = empty($data['ERName']) ? $URSRList->defaultERName : $data['ERName'];
|
||||
$URSRList->SRName = empty($data['SRName']) ? $URSRList->defaultSRName : $data['SRName'];
|
||||
$URSRList->URName = empty($data['URName']) ? $URSRList->defaultURName : $data['URName'];
|
||||
|
||||
@@ -1032,9 +1039,25 @@ class customModel extends model
|
||||
$URAndSR = strpos(",$disabledFeatures,", ',productUR,') === false ? '1' : '0';
|
||||
$this->setting->setItem('system.custom.URAndSR', $URAndSR);
|
||||
|
||||
$disableER = strpos(",$disabledFeatures,", ',productER,') !== false;
|
||||
$enableER = (!$URAndSR || $disableER) ? '0' : '1';
|
||||
$this->setting->setItem('system.custom.enableER', $enableER);
|
||||
|
||||
$this->processMeasrecordCron();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查系统中是否有业务需求数据。
|
||||
* Check whether there is epic data in the system.
|
||||
*
|
||||
* @access public
|
||||
* @return int
|
||||
*/
|
||||
public function hasProductERData(): int
|
||||
{
|
||||
return (int)$this->dao->select('*')->from(TABLE_STORY)->where('type')->eq('epic')->andWhere('deleted')->eq('0')->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查系统中是否有用户需求数据。
|
||||
* Check whether there is requirement data in the system.
|
||||
|
||||
Executable
+48
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
include dirname(__FILE__, 5) . '/test/lib/init.php';
|
||||
|
||||
zdTable('storygrade');
|
||||
su('admin');
|
||||
|
||||
/**
|
||||
|
||||
title=测试 customModel->setStoryGrade();
|
||||
timeout=0
|
||||
cid=1
|
||||
|
||||
- 查看插入的等级数量 @5
|
||||
- 查看插入的等级1详情
|
||||
- 第0条的grade属性 @1
|
||||
- 第0条的name属性 @一级
|
||||
- 第0条的status属性 @enable
|
||||
- 查看插入的等级5详情
|
||||
- 第4条的grade属性 @5
|
||||
- 第4条的name属性 @五级
|
||||
- 第4条的status属性 @enable
|
||||
|
||||
*/
|
||||
|
||||
$data['grade'] = array(
|
||||
0 => 1,
|
||||
1 => 2,
|
||||
2 => 3,
|
||||
3 => 4,
|
||||
4 => 5
|
||||
);
|
||||
|
||||
$data['gradeName'] = array(
|
||||
0 => '一级',
|
||||
1 => '二级',
|
||||
2 => '三级',
|
||||
3 => '四级',
|
||||
4 => '五级'
|
||||
);
|
||||
|
||||
global $tester;
|
||||
$tester->loadModel('custom')->setStoryGrade('story', $data);
|
||||
$gradeSetting = $tester->loadModel('story')->getGradeList();
|
||||
|
||||
r(count($gradeSetting)) && p() && e('5'); // 查看插入的等级数量
|
||||
r($gradeSetting) && p('0:grade,name,status') && e('1,一级,enable'); // 查看插入的等级1详情
|
||||
r($gradeSetting) && p('4:grade,name,status') && e('5,五级,enable'); // 查看插入的等级5详情
|
||||
@@ -16,10 +16,20 @@ formPanel
|
||||
set::title($lang->custom->editStoryConcept),
|
||||
formRow
|
||||
(
|
||||
$config->enableER ? formGroup
|
||||
(
|
||||
set::width('1/3'),
|
||||
setClass('justify-center'),
|
||||
span
|
||||
(
|
||||
setClass('text-md font-bold'),
|
||||
$lang->custom->ERConcept
|
||||
)
|
||||
) : null,
|
||||
$config->URAndSR ? formGroup
|
||||
(
|
||||
set::width('1/2'),
|
||||
setClass('justify-center mr-8'),
|
||||
set::width('1/3'),
|
||||
setClass('justify-center'),
|
||||
span
|
||||
(
|
||||
setClass('text-md font-bold'),
|
||||
@@ -28,7 +38,7 @@ formPanel
|
||||
) : null,
|
||||
formGroup
|
||||
(
|
||||
set::width('1/2'),
|
||||
set::width('1/3'),
|
||||
setClass('justify-center'),
|
||||
span
|
||||
(
|
||||
@@ -41,15 +51,23 @@ formPanel
|
||||
(
|
||||
formGroup
|
||||
(
|
||||
setClass('mr-8'),
|
||||
set::width('1/3'),
|
||||
setClass('mr-2'),
|
||||
set::name('ERName'),
|
||||
set::value($URSR->ERName),
|
||||
setClass(!$this->config->enableER ? 'hidden' : '')
|
||||
),
|
||||
formGroup
|
||||
(
|
||||
setClass('mr-2'),
|
||||
setClass(!$this->config->URAndSR ? 'hidden' : ''),
|
||||
set::width('1/2'),
|
||||
set::width('1/3'),
|
||||
set::name('URName'),
|
||||
set::value($URSR->URName)
|
||||
),
|
||||
formGroup
|
||||
(
|
||||
set::width('1/2'),
|
||||
set::width('1/3'),
|
||||
set::name('SRName'),
|
||||
set::value($URSR->SRName)
|
||||
)
|
||||
|
||||
+117
-32
@@ -49,7 +49,7 @@ if($module == 'project' && $field == 'unitList')
|
||||
);
|
||||
$actionWidth = 'w-full';
|
||||
}
|
||||
elseif(in_array($module, array('story', 'demand')) && $field == 'reviewRules')
|
||||
elseif(in_array($module, array('story', 'requirement', 'epic', 'demand')) && $field == 'reviewRules')
|
||||
{
|
||||
$formItems[] = formGroup
|
||||
(
|
||||
@@ -71,7 +71,24 @@ elseif(in_array($module, array('story', 'demand')) && $field == 'reviewRules')
|
||||
set::items($users)
|
||||
);
|
||||
}
|
||||
elseif(in_array($module, array('story', 'demand', 'testcase')) && $field == 'review')
|
||||
elseif(in_array($module, array('story', 'requirement', 'epic')) && $field == 'gradeRule')
|
||||
{
|
||||
$formItems[] = formGroup
|
||||
(
|
||||
set::width('1/2'),
|
||||
set::label($lang->custom->gradeRule),
|
||||
set::name('gradeRule'),
|
||||
set::value($gradeRule),
|
||||
set::control('radioListInline'),
|
||||
set::items($lang->custom->gradeRuleList)
|
||||
);
|
||||
$formItems[] = div
|
||||
(
|
||||
setClass('gradeRuleNotice'),
|
||||
div($lang->custom->notice->gradeRule)
|
||||
);
|
||||
}
|
||||
elseif(in_array($module, array('epic', 'story', 'requirement', 'demand', 'testcase')) && $field == 'review')
|
||||
{
|
||||
$formItems[] = formGroup
|
||||
(
|
||||
@@ -84,7 +101,7 @@ elseif(in_array($module, array('story', 'demand', 'testcase')) && $field == 'rev
|
||||
on::change('changeReview')
|
||||
);
|
||||
|
||||
if($module == 'story' || $module == 'demand')
|
||||
if(in_array($module, array('story', 'requirement', 'epic', 'demand')))
|
||||
{
|
||||
$formItems[] = formRow
|
||||
(
|
||||
@@ -540,35 +557,103 @@ else
|
||||
}
|
||||
}
|
||||
|
||||
div
|
||||
(
|
||||
setID('mainContent'),
|
||||
setClass('row has-sidebar-left'),
|
||||
isset($sidebarMenu) ? $sidebarMenu : null,
|
||||
formPanel
|
||||
(
|
||||
set::formID('settingForm'),
|
||||
set::headingClass('justify-start'),
|
||||
setClass('flex-auto'),
|
||||
setClass(!empty($sidebarMenu) ? 'ml-0.5' : null),
|
||||
set::actionsClass($actionWidth),
|
||||
set::actions($formActions),
|
||||
div
|
||||
(
|
||||
setClass('flex items-center'),
|
||||
span
|
||||
(
|
||||
setClass('text-md font-bold'),
|
||||
$lang->custom->$module->fields[$field]
|
||||
),
|
||||
span
|
||||
(
|
||||
setClass('ml-2'),
|
||||
$headingTips
|
||||
if(in_array($module, array('story', 'requirement', 'epic')) && $field == 'grade')
|
||||
{
|
||||
jsVar('enableLang', $lang->custom->gradeStatusList['enable']);
|
||||
$tbody = array();
|
||||
foreach($storyGrades as $grade)
|
||||
{
|
||||
$items = array();
|
||||
$hidden = ($grade->grade == 1) ? 'hidden' : '';
|
||||
$hideAdd = (end($storyGrades) == $grade) ? '' : 'hidden';
|
||||
$items[] = array('icon' => 'plus', 'class' => "btn ghost btn-add-grade $hideAdd");
|
||||
if($grade->status == 'enable' && common::hasPriv('custom', 'closeGrade')) $items[] = array('icon' => 'off', 'class' => "btn ghost btn-close ajax-submit $hidden", 'url' => inlink('closeGrade', "type={$module}&id={$grade->grade}"), 'data-confirm' => $lang->custom->notice->closeGrade);
|
||||
if($grade->status == 'disable' && common::hasPriv('custom', 'activateGrade')) $items[] = array('icon' => 'magic', 'class' => "btn ghost btn-active ajax-submit", 'url' => inlink('activateGrade', "type={$module}&id={$grade->grade}"), 'data-confirm' => $lang->custom->notice->activateGrade);
|
||||
if(common::hasPriv('custom', 'deleteGrade')) $items[] = array('icon' => 'trash', 'class' => "btn ghost btn-delete-grade ajax-submit $hidden", 'url' => inlink('deleteGrade', "type={$module}&id={$grade->grade}"));
|
||||
|
||||
$tbody[] = h::tr(
|
||||
formHidden('grade[]', $grade->grade),
|
||||
h::td($grade->grade, set::width('100px'), setClass('index')),
|
||||
h::td(input(set::name('gradeName[]'), set::value($grade->name))),
|
||||
h::td(zget($lang->custom->gradeStatusList, $grade->status), set::width('80px'), setClass('grade-status')),
|
||||
h::td(
|
||||
set::width('100px'),
|
||||
btnGroup
|
||||
(
|
||||
set::items($items)
|
||||
)
|
||||
)
|
||||
),
|
||||
$formItems
|
||||
)
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
div
|
||||
(
|
||||
setID('mainContent'),
|
||||
setClass('row has-sidebar-left'),
|
||||
$sidebarMenu,
|
||||
formPanel(
|
||||
set::title($lang->custom->story->fields['grade']),
|
||||
setClass('ml-0.5'),
|
||||
on::click('.btn-add-grade', 'addGrade'),
|
||||
set::actions(array()),
|
||||
h::table(
|
||||
setID('gradeList'),
|
||||
setClass('table table-form borderless'),
|
||||
h::tr(
|
||||
h::td($lang->story->grade),
|
||||
h::td($lang->story->gradeName, setClass('required')),
|
||||
h::td($lang->story->statusAB),
|
||||
h::td($lang->actions)
|
||||
),
|
||||
$tbody,
|
||||
h::tfoot(
|
||||
h::tr(
|
||||
h::td(),
|
||||
h::td(
|
||||
btn(
|
||||
$lang->save,
|
||||
setClass('primary btn-wide'),
|
||||
set::btnType('submit')
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
div
|
||||
(
|
||||
setID('mainContent'),
|
||||
setClass('row has-sidebar-left'),
|
||||
isset($sidebarMenu) ? $sidebarMenu : null,
|
||||
formPanel
|
||||
(
|
||||
set::formID('settingForm'),
|
||||
set::headingClass('justify-start'),
|
||||
setClass('flex-auto'),
|
||||
setClass(!empty($sidebarMenu) ? 'ml-0.5' : null),
|
||||
set::actionsClass($actionWidth),
|
||||
set::actions($formActions),
|
||||
div
|
||||
(
|
||||
setClass('flex items-center'),
|
||||
span
|
||||
(
|
||||
setClass('text-md font-bold'),
|
||||
$lang->custom->$module->fields[$field]
|
||||
),
|
||||
span
|
||||
(
|
||||
setClass('ml-2'),
|
||||
$headingTips
|
||||
)
|
||||
),
|
||||
$formItems
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
render();
|
||||
|
||||
@@ -16,6 +16,12 @@ formBatchPanel
|
||||
set::title($lang->custom->setStoryConcept),
|
||||
set::minRows(1),
|
||||
set::maxRows(1),
|
||||
$config->enableER ? formBatchItem
|
||||
(
|
||||
set::width('1/3'),
|
||||
set::label($lang->custom->ERConcept),
|
||||
set::name('ERName')
|
||||
) : null,
|
||||
$config->URAndSR ? formBatchItem
|
||||
(
|
||||
set::width('1/3'),
|
||||
|
||||
+38
-5
@@ -57,6 +57,7 @@ class customZen extends custom
|
||||
}
|
||||
$this->view->fieldList = $fieldList;
|
||||
$this->view->dbFields = $dbFields;
|
||||
$this->view->lang2Set = str_replace('_', '-', $lang);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -80,18 +81,27 @@ class customZen extends custom
|
||||
$this->view->unitList = explode(',', $unitList);
|
||||
$this->view->defaultCurrency = zget($this->config->$module, 'defaultCurrency', 'CNY');
|
||||
}
|
||||
if(in_array($module, array('story', 'demand')) && $field == 'reviewRules')
|
||||
if(in_array($module, array('story', 'demand', 'requirement', 'epic')) && $field == 'reviewRules')
|
||||
{
|
||||
$this->app->loadConfig($module);
|
||||
$this->view->reviewRule = zget($this->config->$module, 'reviewRules', 'allpass');
|
||||
$this->view->users = $this->loadModel('user')->getPairs('noclosed|nodeleted');
|
||||
$this->view->superReviewers = zget($this->config->$module, 'superReviewers', '');
|
||||
}
|
||||
if(in_array($module, array('story', 'testcase', 'demand')) && $field == 'review')
|
||||
if(in_array($module, array('story', 'requirement', 'epic')) && $field == 'gradeRule')
|
||||
{
|
||||
$this->app->loadConfig($module);
|
||||
$this->view->gradeRule = zget($this->config->$module, 'gradeRule', '');
|
||||
}
|
||||
if(in_array($module, array('story', 'requirement', 'epic')) && $field == 'grade')
|
||||
{
|
||||
$this->view->storyGrades = $this->loadModel('story')->getGradeList($module);
|
||||
}
|
||||
if(in_array($module, array('story', 'testcase', 'demand', 'requirement', 'epic')) && $field == 'review')
|
||||
{
|
||||
$this->app->loadConfig($module);
|
||||
$this->loadModel('user');
|
||||
if(in_array($module, array('story', 'demand')))
|
||||
if(in_array($module, array('story', 'requirement', 'demand', 'epic')))
|
||||
{
|
||||
$this->view->depts = $this->loadModel('dept')->getDeptPairs();
|
||||
$this->view->forceReviewRoles = zget($this->config->$module, 'forceReviewRoles', '');
|
||||
@@ -142,11 +152,19 @@ class customZen extends custom
|
||||
$data['unitList'] = implode(',', $data['unitList']);
|
||||
$this->loadModel('setting')->setItems("system.$module", $data);
|
||||
}
|
||||
elseif(($module == 'story' || $module == 'demand') && $field == 'review')
|
||||
elseif(in_array($module, array('story', 'demand', 'requirement', 'epic')) && $field == 'review')
|
||||
{
|
||||
$this->setStoryReview($module, $data);
|
||||
}
|
||||
elseif(($module == 'story' || $module == 'demand') && $field == 'reviewRules')
|
||||
elseif(in_array($module, array('story', 'requirement', 'epic')) && $field == 'gradeRule')
|
||||
{
|
||||
$this->setGradeRule($module, $data);
|
||||
}
|
||||
elseif(in_array($module, array('story', 'requirement', 'epic')) && $field == 'grade')
|
||||
{
|
||||
$this->custom->setStoryGrade($module, $data);
|
||||
}
|
||||
elseif(in_array($module, array('story', 'demand', 'requirement', 'epic')) && $field == 'reviewRules')
|
||||
{
|
||||
if(!isset($data['superReviewers'])) $data['superReviewers'] = array();
|
||||
$data['superReviewers'] = implode(',', $data['superReviewers']);
|
||||
@@ -331,6 +349,21 @@ class customZen extends custom
|
||||
return !dao::isError();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置需求细分流程。
|
||||
* Set the rule of story grade.
|
||||
*
|
||||
* @param string $module
|
||||
* @param array $data
|
||||
* @access protected
|
||||
* @return bool
|
||||
*/
|
||||
public function setGradeRule(string $module, array $data): bool
|
||||
{
|
||||
$this->loadModel('setting')->setItems("system.{$module}", $data);
|
||||
return !dao::isError();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置用例评审规则。
|
||||
* Set testcase review rules.
|
||||
|
||||
@@ -82,6 +82,8 @@ class datatable extends control
|
||||
if($account == 'guest') return $this->send(array('result' => 'fail', 'message' => 'guest.'));
|
||||
|
||||
$rawModule = zget($this->config->datatable->moduleAlias, "$module-$method", $module);
|
||||
if($rawModule == 'story' && $extra && $rawModule != $extra) $rawModule = $extra;
|
||||
|
||||
$fieldList = $this->datatable->getFieldList($rawModule, $method);
|
||||
$postFields = json_decode($this->post->fields);
|
||||
|
||||
@@ -102,7 +104,7 @@ class datatable extends control
|
||||
$value = json_encode($fields);
|
||||
|
||||
/* Split story and requirement custom fields. */
|
||||
if(strpos(',product-browse,execution-story,', ",$module-$method,") !== false && strpos(',story,requirement,', ",$extra,") !== false) $name = 'datatable.' . $module . ucfirst($method) . ucfirst($extra) . '.cols';
|
||||
if(("$module-$method" == 'product-browse') && in_array($extra, array('story', 'requirement', 'epic'))) $name = 'datatable.' . $module . ucfirst($method) . ucfirst($extra) . '.cols';
|
||||
|
||||
/* 保存个人配置信息。 */
|
||||
$this->loadModel('setting')->setItem($account . '.' . $name, $value);
|
||||
@@ -144,12 +146,13 @@ class datatable extends control
|
||||
if(zget($this->config->datatable->moduleAlias, "$module-$method", $module) == 'story')
|
||||
{
|
||||
unset($cols['product'], $cols['module']);
|
||||
if($extra != 'requirement') unset($cols['SRS']);
|
||||
if($extra == 'requirement')
|
||||
if($extra != 'story')
|
||||
{
|
||||
foreach(array('plan', 'stage', 'taskCount', 'bugCount', 'caseCount', 'URS') as $field) unset($cols[$field]);
|
||||
$cols['title']['title'] = str_replace($this->lang->SRCommon, $this->lang->URCommon, $this->lang->story->title);
|
||||
foreach(array('taskCount', 'bugCount', 'caseCount') as $field) unset($cols[$field]);
|
||||
$cols['title']['title'] = $cols['title']['title'] = $this->lang->story->name;
|
||||
}
|
||||
|
||||
if($this->app->tab != 'product') $cols['title']['title'] = $this->lang->story->name;
|
||||
}
|
||||
|
||||
if(($module == 'productplan' && $method == 'browse') || ($module == 'project' && $method == 'bug'))
|
||||
|
||||
@@ -84,10 +84,11 @@ class datatableModel extends model
|
||||
if(!$method) $method = $this->app->getMethodName();
|
||||
$datatableId = $module . ucfirst($method);
|
||||
|
||||
/* Split story and requirement custom fields. */
|
||||
if(strpos(',product-browse,execution-story,', ",$module-$method,") !== false && strpos(',story,requirement,', $extra) !== false) $datatableId .= ucfirst($extra);
|
||||
/* Split story,requirement and epic custom fields. */
|
||||
if(("$module-$method" == 'product-browse') && in_array($extra, array('story', 'requirement', 'epic'))) $datatableId .= ucfirst($extra);
|
||||
|
||||
$module = zget($this->config->datatable->moduleAlias, "$module-$method", $module);
|
||||
if($module == 'story' && $extra && $module != $extra) $module = $extra;
|
||||
|
||||
if(!isset($this->config->$module)) $this->loadModel($module);
|
||||
if(isset($this->config->datatable->$datatableId->cols)) $setting = json_decode($this->config->datatable->$datatableId->cols, true);
|
||||
|
||||
+11
-11
@@ -88,7 +88,7 @@ class design extends control
|
||||
|
||||
$products = $this->product->getProductPairsByProject($projectID);
|
||||
$productIdList = $productID ? $productID : array_keys($products);
|
||||
$stories = $this->loadModel('story')->getProductStoryPairs($productIdList, 'all', 0, 'active', 'id_desc', 0, 'full', 'story', false);
|
||||
$stories = $this->loadModel('story')->getProductStoryPairs($productIdList, 'all', 0, 'active', 'id_desc', 0, 'full', 'full');
|
||||
$queryID = $type == 'bySearch' ? $param : 0;
|
||||
|
||||
/* Build Search Form. */
|
||||
@@ -150,10 +150,11 @@ class design extends control
|
||||
|
||||
$products = $this->product->getProductPairsByProject($projectID);
|
||||
$productIdList = $productID ? $productID : array_keys($products);
|
||||
$stories = $this->loadModel('story')->getProductStoryPairs($productIdList, 'all', 0, 'active', 'id_desc', 0, 'full', 'full');
|
||||
|
||||
$this->view->title = $this->lang->design->common . $this->lang->hyphen . $this->lang->design->create;
|
||||
$this->view->users = $this->loadModel('user')->getPairs('noclosed');
|
||||
$this->view->stories = $this->loadModel('story')->getProductStoryPairs($productIdList, 'all', 0, 'active', 'id_desc', 0, 'full', 'story', false);
|
||||
$this->view->stories = $this->story->addGradeLabel($stories);
|
||||
$this->view->productID = $productID;
|
||||
$this->view->projectID = $projectID;
|
||||
$this->view->type = $type;
|
||||
@@ -187,11 +188,12 @@ class design extends control
|
||||
|
||||
$products = $this->product->getProductPairsByProject($projectID);
|
||||
$productIdList = $productID ? $productID : array_keys($products);
|
||||
$stories = $this->loadModel('story')->getProductStoryPairs($productIdList, 'all', 0, 'active', 'id_desc', 0, 'full', 'full');
|
||||
|
||||
$project = $this->loadModel('project')->getByID($projectID);
|
||||
|
||||
$this->view->title = $this->lang->design->common . $this->lang->hyphen . $this->lang->design->batchCreate;
|
||||
$this->view->stories = $this->loadModel('story')->getProductStoryPairs($productIdList);
|
||||
$this->view->stories = $this->story->addGradeLabel($stories);
|
||||
$this->view->users = $this->loadModel('user')->getPairs('noclosed');
|
||||
$this->view->type = $type;
|
||||
$this->view->typeList = $project->model == 'waterfall' ? $this->lang->design->typeList : $this->lang->design->plusTypeList;
|
||||
@@ -222,7 +224,7 @@ class design extends control
|
||||
|
||||
$this->view->title = $this->lang->design->common . $this->lang->hyphen . $this->lang->design->view;
|
||||
$this->view->design = $design;
|
||||
$this->view->stories = $this->loadModel('story')->getProductStoryPairs($productIdList);
|
||||
$this->view->stories = $this->loadModel('story')->getProductStoryPairs($productIdList, 'all', 0, 'active', 'id_desc', 0, 'full', 'full');
|
||||
$this->view->users = $this->loadModel('user')->getPairs('noletter');
|
||||
$this->view->actions = $this->loadModel('action')->getList('design', $design->id);
|
||||
$this->view->repos = $this->loadModel('repo')->getRepoPairs('project', $design->project);
|
||||
@@ -265,11 +267,12 @@ class design extends control
|
||||
$products = $this->product->getProductPairsByProject($design->project);
|
||||
$productIdList = $design->product ? $design->product : array_keys($products);
|
||||
$project = $this->loadModel('project')->getByID($design->project);
|
||||
$stories = $this->loadModel('story')->getProductStoryPairs($productIdList, 'all', 0, 'active', 'id_desc', 0, 'full', 'full');
|
||||
|
||||
$this->view->title = $this->lang->design->common . $this->lang->hyphen . $this->lang->design->edit;
|
||||
$this->view->design = $design;
|
||||
$this->view->project = $project;
|
||||
$this->view->stories = $this->loadModel('story')->getProductStoryPairs($productIdList);
|
||||
$this->view->stories = $this->story->addGradeLabel($stories);
|
||||
$this->view->users = $this->loadModel('user')->getPairs('noclosed|noletter');
|
||||
$this->view->typeList = $project->model == 'waterfall' ? $this->lang->design->typeList : $this->lang->design->plusTypeList;
|
||||
|
||||
@@ -441,15 +444,12 @@ class design extends control
|
||||
*/
|
||||
public function ajaxGetProductStories(int $productID, int $projectID, string $status = 'all', string $hasParent = 'true')
|
||||
{
|
||||
$hasParent = $hasParent == 'true';
|
||||
$products = $this->product->getProductPairsByProject($projectID);
|
||||
$productIdList = $productID ? $productID : array_keys($products);
|
||||
$stories = $this->loadModel('story')->getProductStoryPairs($productIdList, 'all', 0, $status, 'id_desc', 0, 'full', 'story', $hasParent);
|
||||
$stories = $this->loadModel('story')->getProductStoryPairs($productIdList, 'all', 0, $status, 'id_desc', 0, 'full', 'full', $hasParent);
|
||||
|
||||
$items = array();
|
||||
foreach($stories as $storyID => $storyTitle)
|
||||
{
|
||||
$items[] = array('value' => $storyID, 'text' => $storyTitle, 'keys' => $storyTitle);
|
||||
}
|
||||
$items = $this->story->addGradeLabel($stories);
|
||||
return print(json_encode($items));
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ function loadStory(e)
|
||||
{
|
||||
const productID = $(e.target).val();
|
||||
const storyID = $('input[name=story]').val();
|
||||
const link = $.createLink('design', 'ajaxGetProductStories', 'productID=' + productID + '&projectID=' + projectID + '&status=active&hasParent=false');
|
||||
const link = $.createLink('design', 'ajaxGetProductStories', 'productID=' + productID + '&projectID=' + projectID + '&status=active&hasParent=true');
|
||||
$.getJSON(link, function(data)
|
||||
{
|
||||
const $storyPicker = $('input[name=story]').zui('picker');
|
||||
|
||||
@@ -253,6 +253,8 @@ class designModel extends model
|
||||
$design->commit .= html::a(helper::createLink('design', 'revision', "revisionID={$relation->BID}&projectID={$design->project}"), "# {$revision}", '', "title='{$revision}' class='flex clip'");
|
||||
}
|
||||
|
||||
if($design->story > 0) $design->storyInfo = $this->loadModel('story')->fetchByID((int)$design->story);
|
||||
|
||||
return $this->loadModel('file')->replaceImgURL($design, 'desc');
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ foreach($config->design->view->operateList['common'] as $operate)
|
||||
$commonActions[] = $config->design->actionList[$operate];
|
||||
}
|
||||
|
||||
$moduleName = empty($project->hasProduct) ? 'projectstory' : 'story';
|
||||
$moduleName = empty($project->hasProduct) ? 'projectstory' : (isset($design->storyInfo) ? $design->storyInfo->type : 'story');
|
||||
$storyName = zget($stories, $design->story, '');
|
||||
if($common::hasPriv($moduleName, 'view'))
|
||||
{
|
||||
|
||||
@@ -191,7 +191,7 @@ class dev extends control
|
||||
}
|
||||
|
||||
$key = '';
|
||||
if($type == 'common') $key = '&key=projectCommon,productCommon,executionCommon';
|
||||
if($type == 'common') $key = '&key=projectCommon,productCommon,executionCommon,ERCommon,URCommon,SRCommon';
|
||||
|
||||
$this->loadModel('custom')->deleteItems("lang={$language}&module={$module}&vision={$this->config->vision}{$section}{$key}");
|
||||
if($this->config->vision == 'rnd' and $type == 'common' and $this->config->custom->URSR)
|
||||
@@ -201,6 +201,7 @@ class dev extends control
|
||||
{
|
||||
$oldValue = json_decode($oldValue);
|
||||
$setting = array(
|
||||
'ERName' => zget($oldValue, 'defaultERName', $oldValue->ERName),
|
||||
'SRName' => zget($oldValue, 'defaultSRName', $oldValue->SRName),
|
||||
'URName' => zget($oldValue, 'defaultURName', $oldValue->URName)
|
||||
);
|
||||
|
||||
@@ -25,7 +25,8 @@ $lang->dev->language = 'Language: %s';
|
||||
$lang->dev->default = 'Default';
|
||||
$lang->dev->currentLang = 'Current Language';
|
||||
$lang->dev->change = 'Change';
|
||||
$lang->dev->UR = 'Epic';
|
||||
$lang->dev->ER = 'Epic';
|
||||
$lang->dev->UR = 'Feature';
|
||||
$lang->dev->SR = 'Story';
|
||||
$lang->dev->branch = 'Branch';
|
||||
|
||||
|
||||
@@ -25,7 +25,8 @@ $lang->dev->language = 'Language: %s';
|
||||
$lang->dev->default = 'Default';
|
||||
$lang->dev->currentLang = 'Current Language';
|
||||
$lang->dev->change = 'Change';
|
||||
$lang->dev->UR = 'Epic';
|
||||
$lang->dev->ER = 'Epic';
|
||||
$lang->dev->UR = 'Feature';
|
||||
$lang->dev->SR = 'Story';
|
||||
$lang->dev->branch = 'Branch';
|
||||
|
||||
|
||||
@@ -25,7 +25,8 @@ $lang->dev->language = 'Language: %s';
|
||||
$lang->dev->default = 'Default';
|
||||
$lang->dev->currentLang = 'Current Language';
|
||||
$lang->dev->change = 'Change';
|
||||
$lang->dev->UR = 'Epic';
|
||||
$lang->dev->ER = 'Epic';
|
||||
$lang->dev->UR = 'Feature';
|
||||
$lang->dev->SR = 'Story';
|
||||
$lang->dev->branch = 'Branch';
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ $lang->dev->language = '语言:%s';
|
||||
$lang->dev->default = '默认值';
|
||||
$lang->dev->currentLang = '当前语言';
|
||||
$lang->dev->change = '修改值';
|
||||
$lang->dev->ER = '业务需求';
|
||||
$lang->dev->UR = '用户需求';
|
||||
$lang->dev->SR = '软件需求';
|
||||
$lang->dev->branch = '平台/分支';
|
||||
|
||||
+11
-4
@@ -410,6 +410,7 @@ class devModel extends model
|
||||
$originalLangs['productCommon'] = $this->config->productCommonList[$language][PRODUCT_KEY];
|
||||
$originalLangs['projectCommon'] = $this->config->projectCommonList[$language][PROJECT_KEY];
|
||||
$originalLangs['executionCommon'] = $this->config->executionCommonList[$language][$projectKey];
|
||||
$originalLangs['ERCommon'] = $this->lang->dev->ER;
|
||||
$originalLangs['URCommon'] = $this->lang->dev->UR;
|
||||
$originalLangs['SRCommon'] = $this->lang->dev->SR;
|
||||
|
||||
@@ -419,10 +420,12 @@ class devModel extends model
|
||||
if($URSRList)
|
||||
{
|
||||
$URSRList = json_decode($URSRList->value);
|
||||
$originalLangs['ERCommon'] = isset($URSRList->defaultERName) ? $URSRList->defaultERName : $URSRList->ERName;
|
||||
$originalLangs['URCommon'] = isset($URSRList->defaultURName) ? $URSRList->defaultURName : $URSRList->URName;
|
||||
$originalLangs['SRCommon'] = isset($URSRList->defaultSRName) ? $URSRList->defaultSRName : $URSRList->SRName;
|
||||
}
|
||||
if(!$this->config->URAndSR) unset($originalLangs['URCommon']);
|
||||
if(!$this->config->URAndSR) unset($originalLangs['URCommon']);
|
||||
if(!$this->config->enableER) unset($originalLangs['ERCommon']);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -507,12 +510,15 @@ class devModel extends model
|
||||
if($URSRList)
|
||||
{
|
||||
$URSRList = json_decode($URSRList->value);
|
||||
$defaultERName = isset($URSRList->defaultERName) ? $URSRList->defaultERName : $URSRList->ERName;
|
||||
$defaultURName = isset($URSRList->defaultURName) ? $URSRList->defaultURName : $URSRList->URName;
|
||||
$defaultSRName = isset($URSRList->defaultSRName) ? $URSRList->defaultSRName : $URSRList->SRName;
|
||||
$customedLangs['ERCommon'] = $defaultERName == $URSRList->ERName ? '' : $URSRList->ERName;
|
||||
$customedLangs['URCommon'] = $defaultURName == $URSRList->URName ? '' : $URSRList->URName;
|
||||
$customedLangs['SRCommon'] = $defaultSRName == $URSRList->SRName ? '' : $URSRList->SRName;
|
||||
}
|
||||
if(!$this->config->URAndSR) unset($customedLangs['URCommon']);
|
||||
if(!$this->config->enableER) unset($customedLangs['ERCommon']);
|
||||
if(!$this->config->URAndSR) unset($customedLangs['URCommon']);
|
||||
break;
|
||||
case 'first':
|
||||
$customeds = $this->loadModel('custom')->getItems("lang={$language}&module=common§ion=mainNav&vision={$this->config->vision}");
|
||||
@@ -591,6 +597,7 @@ class devModel extends model
|
||||
if(empty($langFilesToLoad)) return false;
|
||||
|
||||
$lang = $module == 'common' ? new language() : $this->defaultLang;
|
||||
$lang->ERCommon = '$ERCOMMON';
|
||||
$lang->URCommon = '$URCOMMON';
|
||||
$lang->SRCommon = '$SRCOMMON';
|
||||
$lang->productCommon = '$PRODUCTCOMMON';
|
||||
@@ -1014,7 +1021,6 @@ class devModel extends model
|
||||
$this->loadModel('custom')->deleteItems("lang={$language}&module={$moduleName}&vision={$this->config->vision}{$section}{$key}");
|
||||
|
||||
$data = fixer::input('post')->get();
|
||||
if($type == 'common') unset($data->common_SRCommon, $data->common_URCommon);
|
||||
foreach($data as $langKey => $customedLang)
|
||||
{
|
||||
if(strpos($langKey, "{$moduleName}_") !== 0) continue;
|
||||
@@ -1044,7 +1050,8 @@ class devModel extends model
|
||||
|
||||
$setting = array(
|
||||
'SRName' => $this->post->common_SRCommon !== false ? $this->post->common_SRCommon : zget($oldValue, 'defaultSRName', $oldValue->SRName),
|
||||
'URName' => $this->post->common_URCommon !== false ? $this->post->common_URCommon : zget($oldValue, 'defaultURName', $oldValue->URName)
|
||||
'URName' => $this->post->common_URCommon !== false ? $this->post->common_URCommon : zget($oldValue, 'defaultURName', $oldValue->URName),
|
||||
'ERName' => $this->post->common_ERCommon !== false ? $this->post->common_ERCommon : zget($oldValue, 'defaultERName', $oldValue->ERName)
|
||||
);
|
||||
$this->custom->updateURAndSR($this->config->custom->URSR, $language, $setting);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<?php
|
||||
global $app;
|
||||
$app->loadConfig('story');
|
||||
$config->epic = clone $config->story;
|
||||
@@ -0,0 +1,435 @@
|
||||
<?php
|
||||
class epic extends control
|
||||
{
|
||||
/**
|
||||
* Create a epic.
|
||||
*
|
||||
* @param int $productID
|
||||
* @param int $branch
|
||||
* @param int $moduleID
|
||||
* @param int $storyID
|
||||
* @param int $objectID projectID|executionID
|
||||
* @param int $bugID
|
||||
* @param int $planID
|
||||
* @param int $todoID
|
||||
* @param string $extra for example feedbackID=0
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function create(int $productID = 0, string $branch = '', int $moduleID = 0, int $storyID = 0, int $objectID = 0, int $bugID = 0, int $planID = 0, int $todoID = 0, string $extra = '')
|
||||
{
|
||||
echo $this->fetch('story', 'create', "productID=$productID&branch=$branch&moduleID=$moduleID&storyID=$storyID&objectID=$objectID&bugID=$bugID&planID=$planID&todoID=$todoID&extra=$extra&storyType=epic");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a batch stories.
|
||||
*
|
||||
* @param int $productID
|
||||
* @param string $branch
|
||||
* @param int $moduleID
|
||||
* @param int $storyID
|
||||
* @param int $executionID projectID|executionID
|
||||
* @param int $plan
|
||||
* @param string $storyType
|
||||
* @param string $extra for example feedbackID=0
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function batchCreate(int $productID = 0, string $branch = '', int $moduleID = 0, int $storyID = 0, int $executionID = 0, int $plan = 0, string $storyType = 'epic', string $extra = '')
|
||||
{
|
||||
echo $this->fetch('story', 'batchCreate', "productID=$productID&branch=$branch&moduleID=$moduleID&storyID=$storyID&executionID=$executionID&plan=$plan&storyType=epic&extra=$extra");
|
||||
}
|
||||
|
||||
/**
|
||||
* View a epic.
|
||||
*
|
||||
* @param int $storyID
|
||||
* @param int $version
|
||||
* @param int $param executionID|projectID
|
||||
* @param string $storyType
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function view(int $storyID, int $version = 0, int $param = 0)
|
||||
{
|
||||
echo $this->fetch('story', 'view', "storyID=$storyID&version=$version¶m=$param&storyType=epic");
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit a epic.
|
||||
*
|
||||
* @param int $storyID
|
||||
* @param string $kanbanGroup
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function edit(int $storyID, string $kanbanGroup = 'default')
|
||||
{
|
||||
echo $this->fetch('story', 'edit', "storyID=$storyID&kanbanGroup=$kanbanGroup&storyType=epic");
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch edit epic.
|
||||
*
|
||||
* @param int $productID
|
||||
* @param int $executionID
|
||||
* @param int $branch
|
||||
* @param string $storyType
|
||||
* @param string $from
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function batchEdit(int $productID = 0, int $executionID = 0, string $branch = '', string $storyType = 'epic', string $from = '')
|
||||
{
|
||||
echo $this->fetch('story', 'batchEdit', "productID=$productID&executionID=$executionID&branch=$branch&storyType=epic&from=$from");
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联Epic。
|
||||
* Link related epics.
|
||||
*
|
||||
* @param int $storyID
|
||||
* @param string $browseType
|
||||
* @param string $excludeStories
|
||||
* @param int $param
|
||||
* @param int $recTotal
|
||||
* @param int $recPerPage
|
||||
* @param int $pageID
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function linkStories(int $storyID, string $browseType = '', string $excludeStories = '', int $param = 0, int $recTotal = 0, int $recPerPage = 20, int $pageID = 1)
|
||||
{
|
||||
echo $this->fetch('story', 'linkStories', "storyID=$storyID&browseType=$browseType&excludeStories=$excludeStories¶m=$param&recTotal=$recTotal&recPerPage=$recPerPage&pageID=$pageID");
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联用户需求。
|
||||
* Link related requirements.
|
||||
*
|
||||
* @param int $storyID
|
||||
* @param string $browseType
|
||||
* @param string $excludeStories
|
||||
* @param int $param
|
||||
* @param int $recTotal
|
||||
* @param int $recPerPage
|
||||
* @param int $pageID
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function linkRequirements(int $storyID, string $browseType = '', string $excludeStories = '', int $param = 0, int $recTotal = 0, int $recPerPage = 20, int $pageID = 1)
|
||||
{
|
||||
echo $this->fetch('story', 'linkRequirements', "storyID=$storyID&browseType=$browseType&excludeStories=$excludeStories¶m=$param&recTotal=$recTotal&recPerPage=$recPerPage&pageID=$pageID");
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出需求数据。
|
||||
* Get the data of the requiremens to export.
|
||||
*
|
||||
* @param int $productID
|
||||
* @param string $orderBy
|
||||
* @param int $executionID
|
||||
* @param string $browseType
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function export(int $productID, string $orderBy, int $executionID = 0, string $browseType = '')
|
||||
{
|
||||
echo $this->fetch('story', 'export', "productID=$productID&orderBy=$orderBy&executionID=$executionID&browseType=$browseType&storyType=epic");
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入需求数据。
|
||||
* Import the data of the requiremens.
|
||||
*
|
||||
* @param int $productID
|
||||
* @param int $branch
|
||||
* @param string $storyType
|
||||
* @param int $projectID
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function import($productID, $branch = 0, $storyType = 'epic', $projectID = 0)
|
||||
{
|
||||
echo $this->fetch('story', 'import', "productID=$productID&branch=$branch&storyType=epic&projectID=$projectID");
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示导入需求数据的页面。
|
||||
* Show the page of importing the data of the requiremens.
|
||||
*
|
||||
* @param int $productID
|
||||
* @param int $branch
|
||||
* @param string $type
|
||||
* @param int $projectID
|
||||
* @param int $pagerID
|
||||
* @param int $maxImport
|
||||
* @param string $insert
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function showImport($productID, $branch = 0, $type = 'epic', $projectID = 0, $pagerID = 1, $maxImport = 0, $insert = '')
|
||||
{
|
||||
echo $this->fetch('story', 'showImport', "productID=$productID&branch=$branch&type=epic&projectID=$projectID&pagerID=$pagerID&maxImport=$maxImport&insert=$insert");
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出需求模板。
|
||||
* Export the template of the requiremens.
|
||||
*
|
||||
* @param int $productID
|
||||
* @param int $branch
|
||||
* @param string $storyType
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function exportTemplate($productID, $branch = 0, $storyType = 'epic')
|
||||
{
|
||||
echo $this->fetch('story', 'exportTemplate', "productID=$productID&branch=$branch&storyType=epic");
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a epic.
|
||||
*
|
||||
* @param int $storyID
|
||||
* @param string $confirm yes|no
|
||||
* @param string $from taskkanban
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function delete(int $storyID, string $confirm = 'no', string $from = '')
|
||||
{
|
||||
echo $this->fetch('story', 'delete', "storyID=$storyID&confirm=$confirm&from=$from&storyType=epic");
|
||||
}
|
||||
|
||||
/**
|
||||
* Change a epic.
|
||||
*
|
||||
* @param int $storyID
|
||||
* @param string $from
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function change(int $storyID, string $from = '')
|
||||
{
|
||||
echo $this->fetch('story', 'change', "storyID=$storyID&from=$from&storyType=epic");
|
||||
}
|
||||
|
||||
/**
|
||||
* Review a epic.
|
||||
*
|
||||
* @param int $storyID
|
||||
* @param string $from product|project
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function review(int $storyID, string $from = 'product')
|
||||
{
|
||||
echo $this->fetch('story', 'review', "storyID=$storyID&from=$from&storyType=epic");
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit review.
|
||||
*
|
||||
* @param int $storyID
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function submitReview(int $storyID)
|
||||
{
|
||||
echo $this->fetch('story', 'submitReview', "storyID=$storyID&storyType=epic");
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch review epics.
|
||||
*
|
||||
* @param string $result
|
||||
* @param string $reason
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function batchReview(string $result, string $reason = '')
|
||||
{
|
||||
echo $this->fetch('story', 'batchReview', "result=$result&reason=$reason&storyType=epic");
|
||||
}
|
||||
|
||||
/**
|
||||
* Recall the epic review or epic change.
|
||||
*
|
||||
* @param int $storyID
|
||||
* @param string $from list
|
||||
* @param string $confirm no|yes
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function recall(int $storyID, string $from = 'list', string $confirm = 'no')
|
||||
{
|
||||
echo $this->fetch('story', 'recall', "storyID=$storyID&from=$from&confirm=$confirm&storyType=epic");
|
||||
}
|
||||
|
||||
/**
|
||||
* 需求的指派给页面。
|
||||
* Assign the epic to a user.
|
||||
*
|
||||
* @param int $storyID
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function assignTo(int $storyID)
|
||||
{
|
||||
echo $this->fetch('story', 'assignTo', "storyID=$storyID");
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭需求。
|
||||
* Close the epic.
|
||||
*
|
||||
* @param int $storyID
|
||||
* @param string $from taskkanban
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function close(int $storyID, string $from = '')
|
||||
{
|
||||
echo $this->fetch('story', 'close', "storyID=$storyID&from=$from&storyType=epic");
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量关闭需求。
|
||||
* Batch close the requiremens.
|
||||
*
|
||||
* @param int $productID
|
||||
* @param int $executionID
|
||||
* @param string $storyType
|
||||
* @param string $from contribute|work
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function batchClose(int $productID = 0, int $executionID = 0, string $storyType = 'epic', string $from = '')
|
||||
{
|
||||
echo $this->fetch('story', 'batchClose', "productID=$productID&executionID=$executionID&storyType=epic&from=$from");
|
||||
}
|
||||
|
||||
/**
|
||||
* 激活需求。
|
||||
* Activate a epic.
|
||||
*
|
||||
* @param int $storyID
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function activate(int $storyID)
|
||||
{
|
||||
echo $this->fetch('story', 'activate', "storyID=$storyID&storyType=epic");
|
||||
}
|
||||
|
||||
/**
|
||||
* 查看需求的报告。
|
||||
* The report page.
|
||||
*
|
||||
* @param int $productID
|
||||
* @param int $branchID
|
||||
* @param string $storyType
|
||||
* @param string $browseType
|
||||
* @param int $moduleID
|
||||
* @param string $chartType
|
||||
* @param int $projectID
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function report(int $productID, int $branchID, string $storyType = 'epic', string $browseType = 'unclosed', int $moduleID = 0, string $chartType = 'pie', int $projectID = 0)
|
||||
{
|
||||
echo $this->fetch('story', 'report', "productID=$productID&branchID=$branchID&storyType=epic&browseType=$browseType&moduleID=$moduleID&chartType=$chartType&projectID=$projectID");
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch change branch.
|
||||
*
|
||||
* @param int $branchID
|
||||
* @param string $confirm yes|no
|
||||
* @param string $storyIdList
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function batchChangeBranch(int $branchID, string $confirm = '', string $storyIdList = '')
|
||||
{
|
||||
echo $this->fetch('story', 'batchChangeBranch', "branchID=$branchID&confirm=$confirm&storyIdList=$storyIdList&storyType=epic");
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch assign to.
|
||||
*
|
||||
* @param string $storyType story|epic
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function batchAssignTo(string $storyType = 'epic', string $assignedTo = '')
|
||||
{
|
||||
echo $this->fetch('story', 'batchAssignTo', "storyType=epic&assignedTo=$assignedTo");
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch change the module of story.
|
||||
*
|
||||
* @param int $moduleID
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function batchChangeModule(int $moduleID)
|
||||
{
|
||||
echo $this->fetch('story', 'batchChangeModule', "moduleID=$moduleID&storyType=epic");
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch change parent.
|
||||
*
|
||||
* @param int $productID
|
||||
* @param string $storyType
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function batchChangeParent(int $productID = 0, string $storyType = 'epic')
|
||||
{
|
||||
echo $this->fetch('story', 'batchChangeParent', "productID=$productID&storyType=epic");
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch change grade.
|
||||
*
|
||||
* @param int $grade
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function batchChangeGrade(int $grade)
|
||||
{
|
||||
echo $this->fetch('story', 'batchChangeGrade', "grade=$grade&storyType=requirement");
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch change plan.
|
||||
*
|
||||
* @param int $planID
|
||||
* @param int $oldPlanID
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function batchChangePlan(int $planID, int $oldPlanID = 0)
|
||||
{
|
||||
echo $this->fetch('story', 'batchChangePlan', "planID=$planID&oldPlanID=$oldPlanID&storyType=epic");
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajax get the stories of a user.
|
||||
*
|
||||
* @param int $userID
|
||||
* @param string $id
|
||||
* @param int $appendID
|
||||
* @param string $storyType
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
public function ajaxGetUserStories(int $userID = 0, string $id = '', int $appendID = 0, $storyType = 'epic')
|
||||
{
|
||||
echo $this->fetch('story', 'ajaxGetUserStories', "userID=$userID&id=$id&appendID=$appendID&storyType=epic");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
global $app;
|
||||
$app->loadLang('story');
|
||||
$lang->epic = clone $lang->story;
|
||||
|
||||
foreach($lang->epic as $key => $value)
|
||||
{
|
||||
if(!is_string($value)) continue;
|
||||
if(strpos($value, $lang->SRCommon) !== false) $lang->epic->$key = str_replace($lang->SRCommon, $lang->ERCommon, $value);
|
||||
}
|
||||
|
||||
$lang->epic->common = $lang->ERCommon;
|
||||
|
||||
$lang->epic->stageList = array();
|
||||
$lang->epic->stageList[''] = '';
|
||||
$lang->epic->stageList['defining'] = 'Defining';
|
||||
$lang->epic->stageList['planning'] = 'Planning';
|
||||
$lang->epic->stageList['developing'] = 'Developing';
|
||||
$lang->epic->stageList['delivering'] = 'Delivering';
|
||||
$lang->epic->stageList['closed'] = 'Closed';
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
global $app;
|
||||
$app->loadLang('story');
|
||||
$lang->epic = clone $lang->story;
|
||||
|
||||
foreach($lang->epic as $key => $value)
|
||||
{
|
||||
if(!is_string($value)) continue;
|
||||
if(strpos($value, $lang->SRCommon) !== false) $lang->epic->$key = str_replace($lang->SRCommon, $lang->ERCommon, $value);
|
||||
}
|
||||
|
||||
$lang->epic->common = $lang->ERCommon;
|
||||
|
||||
$lang->epic->stageList = array();
|
||||
$lang->epic->stageList[''] = '';
|
||||
$lang->epic->stageList['defining'] = 'Defining';
|
||||
$lang->epic->stageList['planning'] = 'Planning';
|
||||
$lang->epic->stageList['developing'] = 'Developing';
|
||||
$lang->epic->stageList['delivering'] = 'Delivering';
|
||||
$lang->epic->stageList['closed'] = 'Closed';
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
global $app;
|
||||
$app->loadLang('story');
|
||||
$lang->epic = clone $lang->story;
|
||||
|
||||
foreach($lang->epic as $key => $value)
|
||||
{
|
||||
if(!is_string($value)) continue;
|
||||
if(strpos($value, $lang->SRCommon) !== false) $lang->epic->$key = str_replace($lang->SRCommon, $lang->ERCommon, $value);
|
||||
}
|
||||
|
||||
$lang->epic->common = $lang->ERCommon;
|
||||
|
||||
$lang->epic->stageList = array();
|
||||
$lang->epic->stageList[''] = '';
|
||||
$lang->epic->stageList['defining'] = 'Defining';
|
||||
$lang->epic->stageList['planning'] = 'Planning';
|
||||
$lang->epic->stageList['developing'] = 'Developing';
|
||||
$lang->epic->stageList['delivering'] = 'Delivering';
|
||||
$lang->epic->stageList['closed'] = 'Closed';
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
global $app;
|
||||
$app->loadLang('story');
|
||||
$lang->epic = clone $lang->story;
|
||||
|
||||
foreach($lang->epic as $key => $value)
|
||||
{
|
||||
if(!is_string($value)) continue;
|
||||
if(strpos($value, $lang->SRCommon) !== false) $lang->epic->$key = str_replace($lang->SRCommon, $lang->ERCommon, $value);
|
||||
}
|
||||
|
||||
$lang->epic->common = $lang->ERCommon;
|
||||
|
||||
$lang->epic->stageList = array();
|
||||
$lang->epic->stageList[''] = '';
|
||||
$lang->epic->stageList['defining'] = '定义中';
|
||||
$lang->epic->stageList['planning'] = '规划中';
|
||||
$lang->epic->stageList['developing'] = '研发中';
|
||||
$lang->epic->stageList['delivering'] = '交付中';
|
||||
$lang->epic->stageList['closed'] = '已关闭';
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
class epicModel extends model
|
||||
{
|
||||
public static function isClickable(object $data, string $action): bool
|
||||
{
|
||||
global $app;
|
||||
$app->control->loadModel('story');
|
||||
return call_user_func_array(array('storyModel', 'isClickable'), array($data, $action));
|
||||
}
|
||||
}
|
||||
@@ -248,15 +248,16 @@ $config->execution->linkStory->dtable->fieldList['id']['sortType'] = true;
|
||||
$config->execution->linkStory->dtable->fieldList['id']['fixed'] = 'left';
|
||||
$config->execution->linkStory->dtable->fieldList['id']['group'] = 1;
|
||||
|
||||
$config->execution->linkStory->dtable->fieldList['title']['title'] = $lang->story->title;
|
||||
$config->execution->linkStory->dtable->fieldList['title']['type'] = 'title';
|
||||
$config->execution->linkStory->dtable->fieldList['title']['link'] = helper::createLink('story', 'view', 'storyID={id}');
|
||||
$config->execution->linkStory->dtable->fieldList['title']['sortType'] = true;
|
||||
$config->execution->linkStory->dtable->fieldList['title']['fixed'] = 'left';
|
||||
$config->execution->linkStory->dtable->fieldList['title']['group'] = 2;
|
||||
$config->execution->linkStory->dtable->fieldList['title']['data-app'] = $app->tab;
|
||||
$config->execution->linkStory->dtable->fieldList['title']['data-toggle'] = 'modal';
|
||||
$config->execution->linkStory->dtable->fieldList['title']['data-size'] = 'lg';
|
||||
$config->execution->linkStory->dtable->fieldList['title']['title'] = $lang->story->name;
|
||||
$config->execution->linkStory->dtable->fieldList['title']['type'] = 'title';
|
||||
$config->execution->linkStory->dtable->fieldList['title']['link'] = helper::createLink('story', 'storyview', 'storyID={id}');
|
||||
$config->execution->linkStory->dtable->fieldList['title']['sortType'] = true;
|
||||
$config->execution->linkStory->dtable->fieldList['title']['fixed'] = 'left';
|
||||
$config->execution->linkStory->dtable->fieldList['title']['group'] = 2;
|
||||
$config->execution->linkStory->dtable->fieldList['title']['nestedToggle'] = true;
|
||||
$config->execution->linkStory->dtable->fieldList['title']['data-app'] = $app->tab;
|
||||
$config->execution->linkStory->dtable->fieldList['title']['data-toggle'] = 'modal';
|
||||
$config->execution->linkStory->dtable->fieldList['title']['data-size'] = 'lg';
|
||||
|
||||
$config->execution->linkStory->dtable->fieldList['pri']['title'] = $lang->story->pri;
|
||||
$config->execution->linkStory->dtable->fieldList['pri']['type'] = 'pri';
|
||||
|
||||
@@ -429,6 +429,8 @@ class execution extends control
|
||||
{
|
||||
/* Load these models. */
|
||||
$this->loadModel('story');
|
||||
$this->loadModel('requirement');
|
||||
$this->loadModel('epic');
|
||||
$this->loadModel('product');
|
||||
|
||||
/* Change for requirement story title. */
|
||||
@@ -455,6 +457,7 @@ class execution extends control
|
||||
|
||||
$execution = $this->commonAction($executionID);
|
||||
$executionID = $execution->id;
|
||||
$project = $this->loadModel('project')->fetchById($execution->project);
|
||||
|
||||
/* Build the search form. */
|
||||
$products = $this->product->getProducts($executionID);
|
||||
@@ -466,7 +469,7 @@ class execution extends control
|
||||
if($this->app->getViewType() == 'xhtml') $recPerPage = 10;
|
||||
$pager = new pager($recTotal, $recPerPage, $pageID);
|
||||
|
||||
$stories = $this->story->getExecutionStories($executionID, 0, $sort, $type, (string)$param, $storyType, '', $pager);
|
||||
$stories = $this->story->getExecutionStories($executionID, 0, $sort, $type, (string)$param, $project->storyType, '', $pager);
|
||||
$this->loadModel('common')->saveQueryCondition($this->dao->get(), 'story', false);
|
||||
|
||||
if(!empty($stories)) $stories = $this->story->mergeReviewer($stories);
|
||||
@@ -475,6 +478,7 @@ class execution extends control
|
||||
$this->executionZen->assignCountForStory($executionID, $stories, $storyType);
|
||||
$this->executionZen->assignRelationForStory($execution, $products, $productID, $type, $storyType, $param, $orderBy, $pager);
|
||||
|
||||
$this->view->project = $project;
|
||||
$this->display();
|
||||
}
|
||||
|
||||
@@ -1012,7 +1016,7 @@ class execution extends control
|
||||
if(!empty($_POST))
|
||||
{
|
||||
/* Filter empty plans. */
|
||||
if(isset($_POST['attribute']) and in_array($_POST['attribute'], array('request', 'design', 'review'))) unset($_POST['plans']);
|
||||
if(isset($_POST['attribute']) and $_POST['attribute'] == 'review') unset($_POST['plans']);
|
||||
if(!empty($_POST['plans']))
|
||||
{
|
||||
foreach($_POST['plans'] as $key => $planItem) $_POST['plans'][$key] = array_filter($_POST['plans'][$key]);
|
||||
@@ -1656,6 +1660,8 @@ class execution extends control
|
||||
public function kanban(int $executionID, string $browseType = 'all', string $orderBy = 'id_asc', string $groupBy = 'default')
|
||||
{
|
||||
$this->app->loadLang('bug');
|
||||
$this->app->loadLang('epic');
|
||||
$this->app->loadLang('requirement');
|
||||
$this->app->loadLang('kanban');
|
||||
|
||||
if($this->config->vision != 'lite') $this->lang->execution->menu = new stdclass();
|
||||
@@ -1718,6 +1724,8 @@ class execution extends control
|
||||
{
|
||||
if(!$this->loadModel('common')->checkPrivByObject('execution', $executionID)) return $this->execution->accessDenied();
|
||||
/* Load model and language. */
|
||||
$this->app->loadLang('epic');
|
||||
$this->app->loadLang('requirement');
|
||||
$this->app->loadLang('task');
|
||||
$this->app->loadLang('bug');
|
||||
$this->loadModel('kanban');
|
||||
@@ -1744,12 +1752,12 @@ class execution extends control
|
||||
$this->session->set('execLaneType', $browseType);
|
||||
|
||||
/* Get kanban data. */
|
||||
$orderBy = $groupBy == 'story' && $browseType == 'task' && !isset($this->lang->kanban->orderList[$orderBy]) ? 'id_asc' : $orderBy;
|
||||
$kanbanGroup = $this->kanban->getExecutionKanban($executionID, $browseType, $groupBy, '', $orderBy);
|
||||
$orderBy = $groupBy == 'story' && $browseType == 'task' && !isset($this->lang->kanban->orderList[$orderBy]) ? 'id_asc' : $orderBy;
|
||||
list($kanbanGroup, $links) = $this->kanban->getExecutionKanban($executionID, $browseType, $groupBy, '', $orderBy);
|
||||
if(empty($kanbanGroup))
|
||||
{
|
||||
$this->kanban->createExecutionLane($executionID, $browseType);
|
||||
$kanbanGroup = $this->kanban->getExecutionKanban($executionID, $browseType, $groupBy, '', $orderBy);
|
||||
list($kanbanGroup, $links) = $this->kanban->getExecutionKanban($executionID, $browseType, $groupBy, '', $orderBy);
|
||||
}
|
||||
|
||||
/* Show lanes of the attribute: no story and bug in request, no bug in design. */
|
||||
@@ -1765,6 +1773,7 @@ class execution extends control
|
||||
$this->view->browseType = $browseType;
|
||||
$this->view->features = $features;
|
||||
$this->view->kanbanGroup = $kanbanGroup;
|
||||
$this->view->links = $links;
|
||||
$this->view->groupBy = $groupBy;
|
||||
|
||||
$this->display();
|
||||
@@ -1895,15 +1904,17 @@ class execution extends control
|
||||
|
||||
$execution = $this->execution->getById($executionID);
|
||||
$project = $this->project->getById($execution->project);
|
||||
if($execution->lifetime == 'ops') unset($this->lang->execution->treeLevel['story']);
|
||||
|
||||
if($execution->lifetime == 'ops') unset($this->lang->execution->treeLevel['story']);
|
||||
if($this->app->tab == 'project') $this->view->projectID = $execution->project;
|
||||
|
||||
$gradeGroup = $this->loadModel('story')->getGradeGroup();
|
||||
|
||||
$this->view->title = $this->lang->execution->tree;
|
||||
$this->view->execution = $execution;
|
||||
$this->view->executionID = $executionID;
|
||||
$this->view->level = $type;
|
||||
$this->view->tree = $this->execution->buildTree($tree, (bool)$project->hasProduct);
|
||||
$this->view->tree = $this->execution->buildTree($tree, (bool)$project->hasProduct, $gradeGroup);
|
||||
$this->view->features = $this->execution->getExecutionFeatures($execution);
|
||||
$this->display();
|
||||
}
|
||||
@@ -2228,7 +2239,7 @@ class execution extends control
|
||||
$object = $this->project->getByID($objectID, 'project,sprint,stage,kanban');
|
||||
|
||||
$browseLink = $this->createLink('execution', 'story', "executionID=$objectID&storyType=$storyType");
|
||||
if($this->app->tab == 'project' and $object->multiple) $browseLink = $this->createLink('projectstory', 'story', "objectID=$objectID&productID=0&branch=0&browseType=¶m=0&storyType=$storyType");
|
||||
if($this->app->tab == 'project' and $object->multiple) $browseLink = $this->createLink('projectstory', 'story', "objectID=$objectID");
|
||||
|
||||
if($object->type == 'kanban' && !$object->hasProduct) $this->lang->productCommon = $this->lang->project->common;
|
||||
|
||||
@@ -2277,29 +2288,21 @@ class execution extends control
|
||||
}
|
||||
}
|
||||
|
||||
if($storyType == 'requirement')
|
||||
{
|
||||
$this->app->loadLang('projectstory');
|
||||
$this->lang->story->title = str_replace($this->lang->SRCommon, $this->lang->URCommon, $this->lang->story->title);
|
||||
$this->lang->projectstory->whyNoStories = str_replace($this->lang->SRCommon, $this->lang->URCommon, $this->lang->projectstory->whyNoStories);
|
||||
$this->lang->execution->linkStory = str_replace($this->lang->SRCommon, $this->lang->URCommon, $this->lang->story->linkStory);
|
||||
if(isset($this->config->product->search['fields']['stage'])) unset($this->config->product->search['fields']['stage']);
|
||||
}
|
||||
|
||||
/* Build the search form. */
|
||||
$actionURL = $this->createLink($this->app->rawModule, 'linkStory', "objectID=$objectID&browseType=bySearch&queryID=myQueryID&orderBy=$orderBy&recPerPage=$recPerPage&pageID=$pageID&extra=$extra&storyType=$storyType");
|
||||
$branchGroups = $this->loadModel('branch')->getByProducts(array_keys($products));
|
||||
$queryID = ($browseType == 'bySearch') ? (int)$param : 0;
|
||||
$this->execution->buildStorySearchForm($products, $branchGroups, $modules, $queryID, $actionURL, 'linkStory', $object);
|
||||
|
||||
if($browseType == 'bySearch') $allStories = $this->story->getBySearch('all', '', $queryID, $orderBy, $objectID, $storyType);
|
||||
if($browseType != 'bySearch') $allStories = $this->story->getProductStories(implode(',', array_keys($products)), $branchIDList, '0', 'active', $storyType, $orderBy, false, '', null);
|
||||
$linkedStories = $this->story->getExecutionStoryPairs($objectID, 0, 'all', 0, 'full', 'all', $storyType);
|
||||
$project = (strpos('sprint,stage,kanban', $object->type) !== false) ? $this->loadModel('project')->getByID($object->project) : $object;
|
||||
$storyType = (($object->type == 'stage' && in_array($object->attribute, array('mix', 'request', 'design'))) || $object->type == 'project' || !$object->multiple) ? $project->storyType : 'story';
|
||||
|
||||
if($browseType == 'bySearch') $allStories = $this->story->getBySearch('all', '', $queryID, $orderBy, $objectID, $storyType);
|
||||
if($browseType != 'bySearch') $allStories = $this->story->getProductStories(implode(',', array_keys($products)), $branchIDList, '0', 'active,launched', $storyType, $orderBy, true, '', null);
|
||||
$linkedStories = $this->story->getExecutionStoryPairs($objectID, 0, 'all', 0, 'full', 'all', $storyType);
|
||||
foreach($allStories as $id => $story)
|
||||
{
|
||||
if(isset($linkedStories[$story->id])) unset($allStories[$id]);
|
||||
if($story->parent < 0) unset($allStories[$id]);
|
||||
|
||||
if(!isset($modules[$story->module]))
|
||||
{
|
||||
@@ -2311,14 +2314,15 @@ class execution extends control
|
||||
$story->estimate = $story->estimate . $this->config->hourUnit;
|
||||
}
|
||||
|
||||
$gradeGroup = array();
|
||||
$gradeList = $this->story->getGradeList('');
|
||||
foreach($gradeList as $grade) $gradeGroup[$grade->type][$grade->grade] = $grade->name;
|
||||
|
||||
/* Set the pager. */
|
||||
$this->app->loadClass('pager', true);
|
||||
$pager = new pager(count($allStories), $recPerPage, $pageID);
|
||||
$allStories = array_chunk($allStories, $pager->recPerPage);
|
||||
|
||||
$project = $object;
|
||||
if(strpos('sprint,stage,kanban', $object->type) !== false) $project = $this->loadModel('project')->getByID($object->project);
|
||||
|
||||
$productPairs = array();
|
||||
foreach($products as $id => $product) $productPairs[$id] = $product->name;
|
||||
|
||||
@@ -2328,6 +2332,8 @@ class execution extends control
|
||||
$this->view->extra = $extra;
|
||||
$this->view->object = $object;
|
||||
$this->view->orderBy = $orderBy;
|
||||
$this->view->gradeGroup = $gradeGroup;
|
||||
$this->view->showGrade = $this->config->edition == 'ipd';
|
||||
$this->view->productPairs = $productPairs;
|
||||
$this->view->allStories = empty($allStories) ? $allStories : $allStories[$pageID - 1];
|
||||
$this->view->pager = $pager;
|
||||
@@ -2338,7 +2344,6 @@ class execution extends control
|
||||
$this->view->branchGroups = $branchGroups;
|
||||
$this->view->browseLink = $browseLink;
|
||||
|
||||
/* NOT used in zin. */
|
||||
$this->view->project = $project;
|
||||
$this->view->executionID = $object->id;
|
||||
$this->view->projectID = $object->id;
|
||||
@@ -3045,6 +3050,9 @@ class execution extends control
|
||||
*/
|
||||
public function treeStory(int $storyID, int $version = 0)
|
||||
{
|
||||
$this->loadModel('requirement');
|
||||
$this->loadModel('epic');
|
||||
|
||||
$story = $this->loadModel('story')->getById($storyID, $version, true);
|
||||
$product = $this->dao->findById($story->product)->from(TABLE_PRODUCT)->fields('name, id, `type`, shadow')->fetch();
|
||||
|
||||
@@ -3150,7 +3158,14 @@ class execution extends control
|
||||
$enterTime = date('Y-m-d H:i:s', $enterTime);
|
||||
if(in_array(true, array(empty($lastEditedTime), strtotime($lastEditedTime) < 0, $lastEditedTime > $enterTime, $groupBy != 'default', !empty($searchValue))))
|
||||
{
|
||||
$kanbanGroup = $from == 'taskkanban' ? $this->kanban->getExecutionKanban($executionID, $browseType, $groupBy, $searchValue, $orderBy) : $this->kanban->getRDKanban($executionID, $browseType, $orderBy, 0, $groupBy, $searchValue);
|
||||
if($from == 'taskkanban')
|
||||
{
|
||||
list($kanbanGroup, $links) = $this->kanban->getExecutionKanban($executionID, $browseType, $groupBy, $searchValue, $orderBy);
|
||||
}
|
||||
else
|
||||
{
|
||||
$kanbanGroup = $this->kanban->getRDKanban($executionID, $browseType, $orderBy, 0, $groupBy, $searchValue);
|
||||
}
|
||||
return print(json_encode($kanbanGroup));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,3 +2,4 @@
|
||||
.kanban-header-title .text-danger {color:rgb(255, 93, 93); opacity: 1;}
|
||||
.as-subtitle {opacity: 0.5; text-wrap: nowrap; font-weight: 700;}
|
||||
.is-collapsed .icon-angle-top:before { content: "\e313"; }
|
||||
.card.selected .child-item .icon {color: var(--color-primary-500)}
|
||||
|
||||
@@ -3,3 +3,4 @@
|
||||
.toolbar .kanbanSetting.btn-link .caret{display: none;}
|
||||
.dropdown-menu .menu-divider a{display:none;}
|
||||
.as-subtitle {opacity: 0.5; text-wrap: nowrap; font-weight: 700;}
|
||||
.card.selected .child-item .icon {color: var(--color-primary-500)}
|
||||
|
||||
@@ -8,5 +8,4 @@
|
||||
|
||||
.actions-menu {bottom: 12px;}
|
||||
#mainContent {margin-bottom: 60px;}
|
||||
|
||||
#mainContent .desc-box {max-height: 6rem;}
|
||||
|
||||
@@ -8,6 +8,7 @@ window.getLane = function(lane)
|
||||
window.getCol = function(col)
|
||||
{
|
||||
/* 计算WIP。*/
|
||||
if(ERURColumn.includes(col.type)) return;
|
||||
const limit = col.limit == -1 ? "<i class='icon icon-md icon-infinite'></i>" : col.limit;
|
||||
const cards = col.cards;
|
||||
|
||||
@@ -91,7 +92,7 @@ window.buildColActions = function(col)
|
||||
let actions = [];
|
||||
|
||||
if(col.actionList && col.actionList.includes('setColumn')) actions.push({text: kanbanLang.setColumn, url: $.createLink('kanban', 'setColumn', `columnID=${col.id}&executionID=${executionID}&from=RDKanban`), 'data-toggle': 'modal', 'icon': 'edit'});
|
||||
if(col.actionList && col.actionList.includes('setWIP')) actions.push({text: kanbanLang.setWIP, url: $.createLink('kanban', 'setWIP', `columnID=${col.id}&executionID=${executionID}&from=RDKanban`), 'data-toggle': 'modal', 'icon': 'alert'});
|
||||
if(col.actionList && col.actionList.includes('setWIP') && !ERURColumn.includes(col.type)) actions.push({text: kanbanLang.setWIP, url: $.createLink('kanban', 'setWIP', `columnID=${col.id}&executionID=${executionID}&from=RDKanban`), 'data-toggle': 'modal', 'icon': 'alert'});
|
||||
|
||||
return actions;
|
||||
}
|
||||
@@ -139,7 +140,7 @@ window.getItem = function(info)
|
||||
const avatar = renderAvatar(info.item);
|
||||
|
||||
info.item.titleAttrs = {};
|
||||
if(info.item.cardType == 'story')
|
||||
if(['story', 'parentStory'].includes(info.item.cardType))
|
||||
{
|
||||
info.item.icon = 'product';
|
||||
if(priv.canViewStory)
|
||||
@@ -208,9 +209,14 @@ window.getItem = function(info)
|
||||
window.renderAvatar = function(item)
|
||||
{
|
||||
let assignLink = '';
|
||||
if(item.cardType == 'story' && priv.canAssignStory) assignLink = $.createLink('story', 'assignTo', "id=" + item.id);
|
||||
if(item.cardType == 'bug' && priv.canAssignBug) assignLink = $.createLink('bug', 'assignTo', "id=" + item.id);
|
||||
if(item.cardType == 'task' && priv.canAssignTask) assignLink = $.createLink('task', 'assignTo', "executionID=" + executionID + "&id=" + item.id);
|
||||
if(item.cardType == 'parentStory')
|
||||
{
|
||||
return '<div class="avatar child-item rounded-full size-xs ml-1" title="' + storyLang.children + '" style="background: #ccc; color: #fff; cursor: pointer;"><i class="icon icon-split"></i></div>';
|
||||
}
|
||||
|
||||
if(item.cardType == 'story' && priv.canAssignStory) assignLink = $.createLink('story', 'assignTo', "id=" + item.id);
|
||||
if(item.cardType == 'bug' && priv.canAssignBug) assignLink = $.createLink('bug', 'assignTo', "id=" + item.id);
|
||||
if(item.cardType == 'task' && priv.canAssignTask) assignLink = $.createLink('task', 'assignTo', "executionID=" + executionID + "&id=" + item.id);
|
||||
|
||||
if(item.assignedTo.length == 0)
|
||||
{
|
||||
@@ -227,9 +233,10 @@ window.renderAvatar = function(item)
|
||||
window.getItemActions = function(item)
|
||||
{
|
||||
let actions = [];
|
||||
if(item.cardType == 'story') actions = buildStoryActions(item);
|
||||
else if(item.cardType == 'bug') actions = buildBugActions(item);
|
||||
else if(item.cardType == 'task') actions = buildTaskActions(item);
|
||||
if(item.cardType == 'story') actions = buildStoryActions(item);
|
||||
else if(item.cardType == 'parentStory') actions = buildParentStoryActions(item);
|
||||
else if(item.cardType == 'bug') actions = buildBugActions(item);
|
||||
else if(item.cardType == 'task') actions = buildTaskActions(item);
|
||||
|
||||
if(actions.length)
|
||||
{
|
||||
@@ -242,6 +249,19 @@ window.getItemActions = function(item)
|
||||
}
|
||||
}
|
||||
|
||||
window.buildParentStoryActions = function(item)
|
||||
{
|
||||
let actions = [];
|
||||
|
||||
if(priv.canEditStory) actions.push({text: storyLang.edit, icon: 'edit', url: $.createLink('story', 'edit', 'storyID=' + item.id + '&kanbanGroup=' + groupBy), 'data-toggle': 'modal', 'data-size': 'lg'});
|
||||
if(priv.canChangeStory && item.status == 'active') actions.push({text: storyLang.change, icon: 'change', url: $.createLink('story', 'change', 'storyID=' + item.id), 'data-toggle': 'modal', 'data-size': 'lg'});
|
||||
if(priv.canActivateStory && item.status == 'closed') actions.push({text: executionLang.activate, icon: 'magic', url: $.createLink('story', 'activate', 'storyID=' + item.id), 'data-toggle': 'modal', 'data-size': 'lg'});
|
||||
if(priv.canUnlinkStory) actions.push({text: executionLang.unlinkStory, icon: 'unlink', url: $.createLink('execution', 'unlinkStory', 'executionID=' + executionID + '&storyID=' + item.id + '&confirm=no&from=' + '&laneID=' + item.lane + '&columnID=' + item.column), 'innerClass' : 'ajax-submit'});
|
||||
if(priv.canDeleteStory) actions.push({text: storyLang.delete, icon: 'trash', url: $.createLink('story', 'delete', 'storyID=' + item.id), 'innerClass': 'ajax-submit'});
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
window.buildStoryActions = function(item)
|
||||
{
|
||||
let actions = [];
|
||||
@@ -345,10 +365,11 @@ if(!window.kanbanDropRules)
|
||||
{
|
||||
backlog: ['ready', 'backlog'],
|
||||
ready: ['backlog', 'ready'],
|
||||
tested: ['verified'],
|
||||
verified: ['tested', 'released'],
|
||||
tested: ['verified', 'rejected'],
|
||||
verified: ['tested', 'pending', 'released'],
|
||||
pending: ['verified', 'released'],
|
||||
released: ['verified', 'closed'],
|
||||
closed: ['released'],
|
||||
closed: ['released']
|
||||
},
|
||||
bug:
|
||||
{
|
||||
|
||||
@@ -35,11 +35,12 @@ window.onRenderLinkStoryCell = function(result, {row, col})
|
||||
{
|
||||
if(col.name == 'title')
|
||||
{
|
||||
if(row.data.parent > 0)
|
||||
{
|
||||
html = "<span class='label gray-pale rounded-xl clip'>"+ childrenAB +"</span> ";
|
||||
result.unshift({html});
|
||||
}
|
||||
const story = row.data;
|
||||
const gradeLabel = (showGrade || story.grade >= 2) ? gradeGroup[story.type][story.grade] : '';
|
||||
|
||||
let html = '';
|
||||
if(gradeLabel) html += "<span class='label gray-pale rounded-xl clip'>" + gradeLabel + "</span> ";
|
||||
if(html) result.unshift({html});
|
||||
}
|
||||
|
||||
if(col.name !== 'branch')
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user