diff --git a/module/group/lang/resource.php b/module/group/lang/resource.php index ebabdcb63d..1d3935a7a3 100644 --- a/module/group/lang/resource.php +++ b/module/group/lang/resource.php @@ -424,26 +424,27 @@ $lang->branch->methodOrder[10] = 'delete'; /* Story. */ $lang->resource->story = new stdclass(); -$lang->resource->story->create = 'create'; -$lang->resource->story->batchCreate = 'batchCreate'; -$lang->resource->story->edit = 'editAction'; -$lang->resource->story->linkStory = 'linkStory'; -$lang->resource->story->batchEdit = 'batchEdit'; -$lang->resource->story->export = 'exportAction'; -$lang->resource->story->delete = 'deleteAction'; -$lang->resource->story->view = 'view'; -$lang->resource->story->change = 'changeAction'; -$lang->resource->story->review = 'reviewAction'; -$lang->resource->story->batchReview = 'batchReview'; -$lang->resource->story->assignTo = 'assignAction'; -$lang->resource->story->close = 'closeAction'; -$lang->resource->story->batchClose = 'batchClose'; -$lang->resource->story->activate = 'activateAction'; -$lang->resource->story->tasks = 'tasks'; -$lang->resource->story->bugs = 'bugs'; -$lang->resource->story->cases = 'cases'; -$lang->resource->story->zeroCase = 'zeroCase'; -$lang->resource->story->report = 'reportAction'; +$lang->resource->story->create = 'create'; +$lang->resource->story->batchCreate = 'batchCreate'; +$lang->resource->story->edit = 'editAction'; +$lang->resource->story->linkStory = 'linkStory'; +$lang->resource->story->batchEdit = 'batchEdit'; +$lang->resource->story->export = 'exportAction'; +$lang->resource->story->delete = 'deleteAction'; +$lang->resource->story->view = 'view'; +$lang->resource->story->change = 'changeAction'; +$lang->resource->story->review = 'reviewAction'; +$lang->resource->story->batchReview = 'batchReview'; +$lang->resource->story->recall = 'recall'; +$lang->resource->story->assignTo = 'assignAction'; +$lang->resource->story->close = 'closeAction'; +$lang->resource->story->batchClose = 'batchClose'; +$lang->resource->story->activate = 'activateAction'; +$lang->resource->story->tasks = 'tasks'; +$lang->resource->story->bugs = 'bugs'; +$lang->resource->story->cases = 'cases'; +$lang->resource->story->zeroCase = 'zeroCase'; +$lang->resource->story->report = 'reportAction'; $lang->resource->story->batchChangePlan = 'batchChangePlan'; $lang->resource->story->batchChangeBranch = 'batchChangeBranch'; $lang->resource->story->batchChangeStage = 'batchChangeStage'; @@ -462,24 +463,25 @@ $lang->story->methodOrder[30] = 'view'; $lang->story->methodOrder[35] = 'change'; $lang->story->methodOrder[40] = 'review'; $lang->story->methodOrder[45] = 'batchReview'; -$lang->story->methodOrder[50] = 'close'; -$lang->story->methodOrder[55] = 'batchClose'; -$lang->story->methodOrder[60] = 'batchChangePlan'; -$lang->story->methodOrder[65] = 'batchChangeStage'; -$lang->story->methodOrder[70] = 'assignTo'; -$lang->story->methodOrder[75] = 'batchAssignTo'; -$lang->story->methodOrder[80] = 'activate'; -$lang->story->methodOrder[85] = 'tasks'; -$lang->story->methodOrder[90] = 'bugs'; -$lang->story->methodOrder[95] = 'cases'; -$lang->story->methodOrder[100] = 'zeroCase'; -$lang->story->methodOrder[105] = 'report'; -$lang->story->methodOrder[110] = 'linkStory'; -$lang->story->methodOrder[115] = 'batchChangeBranch'; -$lang->story->methodOrder[120] = 'batchChangeModule'; -$lang->story->methodOrder[125] = 'batchToTask'; -$lang->story->methodOrder[130] = 'track'; -$lang->story->methodOrder[135] = 'processStoryChange'; +$lang->story->methodOrder[50] = 'recall'; +$lang->story->methodOrder[55] = 'close'; +$lang->story->methodOrder[60] = 'batchClose'; +$lang->story->methodOrder[65] = 'batchChangePlan'; +$lang->story->methodOrder[70] = 'batchChangeStage'; +$lang->story->methodOrder[75] = 'assignTo'; +$lang->story->methodOrder[80] = 'batchAssignTo'; +$lang->story->methodOrder[85] = 'activate'; +$lang->story->methodOrder[90] = 'tasks'; +$lang->story->methodOrder[95] = 'bugs'; +$lang->story->methodOrder[100] = 'cases'; +$lang->story->methodOrder[105] = 'zeroCase'; +$lang->story->methodOrder[110] = 'report'; +$lang->story->methodOrder[115] = 'linkStory'; +$lang->story->methodOrder[120] = 'batchChangeBranch'; +$lang->story->methodOrder[125] = 'batchChangeModule'; +$lang->story->methodOrder[130] = 'batchToTask'; +$lang->story->methodOrder[135] = 'track'; +$lang->story->methodOrder[140] = 'processStoryChange'; /* Product plan. */ $lang->resource->productplan = new stdclass(); diff --git a/module/message/config.php b/module/message/config.php index d39fb726e9..6cd71ac977 100644 --- a/module/message/config.php +++ b/module/message/config.php @@ -10,6 +10,7 @@ $config->message->objectTypes['case'] = array('opened', 'edited', 'commen $config->message->objectTypes['testtask'] = array('opened', 'edited', 'started', 'blocked', 'closed', 'activated'); $config->message->objectTypes['todo'] = array('opened', 'edited'); $config->message->objectTypes['doc'] = array('created', 'edited'); +$config->message->objectTypes['release'] = array('opened', 'edited'); $config->message->available = array(); $config->message->available['mail']['story'] = $config->message->objectTypes['story']; @@ -17,6 +18,7 @@ $config->message->available['mail']['task'] = $config->message->objectTypes[ $config->message->available['mail']['bug'] = $config->message->objectTypes['bug']; $config->message->available['mail']['testtask'] = array('opened', 'edited', 'closed'); $config->message->available['mail']['doc'] = $config->message->objectTypes['doc']; +$config->message->available['mail']['release'] = $config->message->objectTypes['release']; $config->message->available['webhook'] = $config->message->objectTypes; @@ -26,6 +28,7 @@ $config->message->available['message']['task'] = $config->message->objectTyp $config->message->available['message']['testtask'] = $config->message->objectTypes['testtask']; $config->message->available['message']['todo'] = $config->message->objectTypes['todo']; $config->message->available['message']['doc'] = $config->message->objectTypes['doc']; +$config->message->available['message']['release'] = $config->message->objectTypes['release']; $config->message->typeLink = array(); $config->message->typeLink['mail'] = 'mail|index'; diff --git a/module/message/model.php b/module/message/model.php index 60c375ef8c..c8cfb2baad 100644 --- a/module/message/model.php +++ b/module/message/model.php @@ -164,6 +164,17 @@ class messageModel extends model if(empty($toList) and $objectType == 'todo') $toList = $object->account; if(empty($toList) and $objectType == 'testtask') $toList = $object->owner; if(empty($toList) and $objectType == 'meeting') $toList = $object->host . $object->participant; + if(empty($toList) and $objectType == 'release') + { + /* Get notifiy persons. */ + $notifyPersons = array(); + if(!empty($toList->notify)) $notifyPersons = $this->loadModel('release')->getNotifyPersons($object->notify, $object->product, $object->build); + + foreach($notifyPersons as $account) + { + if(strpos($object->mailto . ',', ",{$account},") === false) $toList .= ',' . $account; + } + } if($toList == 'closed') $toList = ''; return $toList; diff --git a/module/my/lang/de.php b/module/my/lang/de.php index a59df1e681..686967d4f7 100644 --- a/module/my/lang/de.php +++ b/module/my/lang/de.php @@ -102,9 +102,8 @@ $lang->my->projectLinkList['project-index'] = 'By default, go to the most re $lang->my->executionLinkList = array(); if($config->systemMode == 'new') { -$lang->my->executionLinkList['execution-index'] = 'Enter the execution dashboard by default, you can understand all the execution statistics and overview'; -$lang->my->executionLinkList['execution-all'] = 'Enter the execution list by default, you can view all executions'; -$lang->my->executionLinkList['execution-task'] = 'By default, enter the list of the most recently executed task, and you can view the task information under the current iteration'; + $lang->my->executionLinkList['execution-task'] = 'By default, enter the list of the most recently executed task, and you can view the task information under the current iteration'; + $lang->my->executionLinkList['execution-executionkanban'] = 'By default, you can enter the execution Kanban to view the execution status of projects in progress'; } if($config->systemMode == 'classic') $lang->my->executionLinkList['execution-task'] = "By default, enter the list of the most recently {$lang->executionCommon} task, and you can view the task information under the current {$lang->executionCommon}"; diff --git a/module/my/lang/en.php b/module/my/lang/en.php index 508c139de4..62b0311a4e 100644 --- a/module/my/lang/en.php +++ b/module/my/lang/en.php @@ -109,9 +109,8 @@ $lang->my->projectLinkList['project-index'] = 'By default, go to the most re $lang->my->executionLinkList = array(); if($config->systemMode == 'new') { -$lang->my->executionLinkList['execution-index'] = 'Enter the execution dashboard by default, you can understand all the execution statistics and overview'; -$lang->my->executionLinkList['execution-all'] = 'Enter the execution list by default, you can view all executions'; -$lang->my->executionLinkList['execution-task'] = 'By default, enter the list of the most recently executed task, and you can view the task information under the current iteration'; + $lang->my->executionLinkList['execution-task'] = 'By default, enter the list of the most recently executed task, and you can view the task information under the current iteration'; + $lang->my->executionLinkList['execution-executionkanban'] = 'By default, you can enter the execution Kanban to view the execution status of projects in progress'; } if($config->systemMode == 'classic') $lang->my->executionLinkList['execution-task'] = "By default, enter the list of the most recently {$lang->executionCommon} task, and you can view the task information under the current {$lang->executionCommon}"; diff --git a/module/my/lang/fr.php b/module/my/lang/fr.php index a59df1e681..686967d4f7 100644 --- a/module/my/lang/fr.php +++ b/module/my/lang/fr.php @@ -102,9 +102,8 @@ $lang->my->projectLinkList['project-index'] = 'By default, go to the most re $lang->my->executionLinkList = array(); if($config->systemMode == 'new') { -$lang->my->executionLinkList['execution-index'] = 'Enter the execution dashboard by default, you can understand all the execution statistics and overview'; -$lang->my->executionLinkList['execution-all'] = 'Enter the execution list by default, you can view all executions'; -$lang->my->executionLinkList['execution-task'] = 'By default, enter the list of the most recently executed task, and you can view the task information under the current iteration'; + $lang->my->executionLinkList['execution-task'] = 'By default, enter the list of the most recently executed task, and you can view the task information under the current iteration'; + $lang->my->executionLinkList['execution-executionkanban'] = 'By default, you can enter the execution Kanban to view the execution status of projects in progress'; } if($config->systemMode == 'classic') $lang->my->executionLinkList['execution-task'] = "By default, enter the list of the most recently {$lang->executionCommon} task, and you can view the task information under the current {$lang->executionCommon}"; diff --git a/module/my/lang/vi.php b/module/my/lang/vi.php index a59df1e681..686967d4f7 100644 --- a/module/my/lang/vi.php +++ b/module/my/lang/vi.php @@ -102,9 +102,8 @@ $lang->my->projectLinkList['project-index'] = 'By default, go to the most re $lang->my->executionLinkList = array(); if($config->systemMode == 'new') { -$lang->my->executionLinkList['execution-index'] = 'Enter the execution dashboard by default, you can understand all the execution statistics and overview'; -$lang->my->executionLinkList['execution-all'] = 'Enter the execution list by default, you can view all executions'; -$lang->my->executionLinkList['execution-task'] = 'By default, enter the list of the most recently executed task, and you can view the task information under the current iteration'; + $lang->my->executionLinkList['execution-task'] = 'By default, enter the list of the most recently executed task, and you can view the task information under the current iteration'; + $lang->my->executionLinkList['execution-executionkanban'] = 'By default, you can enter the execution Kanban to view the execution status of projects in progress'; } if($config->systemMode == 'classic') $lang->my->executionLinkList['execution-task'] = "By default, enter the list of the most recently {$lang->executionCommon} task, and you can view the task information under the current {$lang->executionCommon}"; diff --git a/module/my/lang/zh-cn.php b/module/my/lang/zh-cn.php index 04e2e9a325..5a20a1e225 100644 --- a/module/my/lang/zh-cn.php +++ b/module/my/lang/zh-cn.php @@ -110,7 +110,7 @@ $lang->my->executionLinkList = array(); if($config->systemMode == 'new') { $lang->my->executionLinkList['execution-task'] = '默认进入最近一个执行的任务列表,可以查看当前迭代下的任务信息'; - $lang->my->executionLinkList['execution-executionkanban'] = '默认进入执行看板,可以查看所有项目的执行情况'; + $lang->my->executionLinkList['execution-executionkanban'] = '默认进入执行看板,可以查看进行中项目的执行情况'; } if($config->systemMode == 'classic') $lang->my->executionLinkList['execution-task'] = "默认进入最近一个{$lang->executionCommon}的任务列表,可以查看当前{$lang->executionCommon}下的任务信息"; diff --git a/module/my/lang/zh-tw.php b/module/my/lang/zh-tw.php index c5f70b4e15..35b1995570 100644 --- a/module/my/lang/zh-tw.php +++ b/module/my/lang/zh-tw.php @@ -109,9 +109,8 @@ $lang->my->projectLinkList['project-index'] = '預設進入最近一個項 $lang->my->executionLinkList = array(); if($config->systemMode == 'new') { - $lang->my->executionLinkList['execution-task'] = '預設進入最近一個執行的任務列表,可以查看當前迭代下的任務信息'; - $lang->my->executionLinkList['execution-index'] = '預設進入執行儀表盤,可以瞭解所有執行的統計數據和概況'; - $lang->my->executionLinkList['execution-all'] = '預設進入執行列表,可以查看所有的執行'; + $lang->my->executionLinkList['execution-task'] = '預設進入最近一個執行的任務列表,可以查看當前迭代下的任務信息'; + $lang->my->executionLinkList['execution-executionkanban'] = '默認進入執行看板,可以查看進行中項目的執行情况'; } if($config->systemMode == 'classic') $lang->my->executionLinkList['execution-task'] = "預設進入最近一個{$lang->executionCommon}的任務列表,可以查看當前{$lang->executionCommon}下的任務信息"; diff --git a/module/my/view/requirement.html.php b/module/my/view/requirement.html.php index 80f50d034a..891d7f6926 100644 --- a/module/my/view/requirement.html.php +++ b/module/my/view/requirement.html.php @@ -65,7 +65,7 @@ story->estimateAB);?> statusAB);?> story->stageAB);?> - actions;?> + actions;?> @@ -99,10 +99,11 @@ if($canBeChanged) { $vars = "story={$story->id}"; - echo common::buildIconButton('story', 'change', $vars, $story, 'list', 'alter', '', 'iframe', true); - echo common::buildIconButton('story', 'review', $vars, $story, 'list', 'search', '', 'iframe', true); - echo common::buildIconButton('story', 'close', $vars, $story, 'list', '', '', 'iframe', true); - echo common::buildIconButton('story', 'edit', $vars, $story, 'list', '', '', 'iframe', true, "data-width='95%'"); + echo common::buildIconButton('story', 'change', $vars, $story, 'list', 'alter', '', 'iframe', true); + echo common::buildIconButton('story', 'review', $vars, $story, 'list', 'search', '', 'iframe', true); + echo common::buildIconButton('story', 'recall', $vars, $story, 'list', 'back', 'hiddenwin', '', '', '', $lang->story->recall); + echo common::buildIconButton('story', 'close', $vars, $story, 'list', '', '', 'iframe', true); + echo common::buildIconButton('story', 'edit', $vars, $story, 'list', '', '', 'iframe', true, "data-width='95%'"); } ?> diff --git a/module/my/view/story.html.php b/module/my/view/story.html.php index 2385c4cde1..8e2d3c486a 100644 --- a/module/my/view/story.html.php +++ b/module/my/view/story.html.php @@ -66,7 +66,7 @@ story->estimateAB);?> statusAB);?> story->stageAB);?> - actions;?> + actions;?> @@ -101,10 +101,11 @@ if($canBeChanged) { $vars = "story={$story->id}"; - echo common::buildIconButton('story', 'change', $vars, $story, 'list', 'alter', '', 'iframe', true); - echo common::buildIconButton('story', 'review', $vars, $story, 'list', 'search', '', 'iframe', true); - echo common::buildIconButton('story', 'close', $vars, $story, 'list', '', '', 'iframe', true); - echo common::buildIconButton('story', 'edit', $vars, $story, 'list', '', '', 'iframe', true, "data-width='95%'"); + echo common::buildIconButton('story', 'change', $vars, $story, 'list', 'alter', '', 'iframe', true); + echo common::buildIconButton('story', 'review', $vars, $story, 'list', 'search', '', 'iframe', true); + echo common::buildIconButton('story', 'recall', $vars, $story, 'list', 'back', 'hiddenwin', '', '', '', $lang->story->recall); + echo common::buildIconButton('story', 'close', $vars, $story, 'list', '', '', 'iframe', true); + echo common::buildIconButton('story', 'edit', $vars, $story, 'list', '', '', 'iframe', true, "data-width='95%'"); echo common::buildIconButton('story', 'createCase', "productID=$story->product&branch=$story->branch&module=0&from=¶m=0&$vars", $story, 'list', 'sitemap', '', 'iframe', true, "data-width='95%'"); } ?> diff --git a/module/project/js/managemembers.js b/module/project/js/managemembers.js index 232f686a34..2e1301af1d 100644 --- a/module/project/js/managemembers.js +++ b/module/project/js/managemembers.js @@ -39,6 +39,7 @@ function addItem(obj) */ function deleteItem(obj) { + if($('.table').find('.addedItem').length <= 1) return; $(obj).closest('tr').remove(); } diff --git a/module/projectrelease/control.php b/module/projectrelease/control.php index 33e567de47..71380e8af9 100644 --- a/module/projectrelease/control.php +++ b/module/projectrelease/control.php @@ -98,6 +98,8 @@ class projectrelease extends control */ public function create($projectID) { + /* Load module and config. */ + $this->loadModel('build'); $this->app->loadConfig('release'); $this->config->projectrelease->create = $this->config->release->create; @@ -114,9 +116,7 @@ class projectrelease extends control } /* Get the builds that can select. */ - $productPairs = $this->loadModel('product')->getProductPairsByProject($projectID); - $productIdList = array_keys($productPairs); - $builds = $this->loadModel('build')->getProductBuildPairs($productIdList, 0, 0, 'notrunk'); + $builds = $this->build->getProjectBuildPairs($projectID, 0, 0, 'notrunk|withbranch'); $releaseBuilds = $this->projectrelease->getReleaseBuilds($projectID); foreach($releaseBuilds as $build) unset($builds[$build]); unset($builds['trunk']); @@ -128,6 +128,7 @@ class projectrelease extends control $this->view->position[] = $this->lang->release->create; $this->view->builds = $builds; $this->view->lastRelease = $this->projectrelease->getLast($projectID); + $this->view->users = $this->loadModel('user')->getPairs('noletter|noclosed'); $this->display(); } @@ -140,6 +141,10 @@ class projectrelease extends control */ public function edit($releaseID) { + /* Load module and config. */ + $this->loadModel('story'); + $this->loadModel('bug'); + $this->loadModel('build'); $this->app->loadConfig('release'); $this->config->projectrelease->create = $this->config->release->create; @@ -158,15 +163,21 @@ class projectrelease extends control $this->executeHooks($releaseID); return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => inlink('view', "releaseID=$releaseID"))); } - $this->loadModel('story'); - $this->loadModel('bug'); - $this->loadModel('build'); /* Get release and build. */ $release = $this->projectrelease->getById((int)$releaseID); $this->commonAction($release->project, $release->product, $release->branch); $build = $this->build->getById($release->build); + /* Get the builds that can select. */ + $builds = $this->build->getProjectBuildPairs($release->project, $release->product, $release->branch, 'notrunk|withbranch'); + $releaseBuilds = $this->projectrelease->getReleaseBuilds($release->project); + foreach($releaseBuilds as $releaseBuild) + { + if($releaseBuild != $build->id) unset($builds[$releaseBuild]); + } + unset($builds['trunk']); + /* Set project menu. */ $this->project->setMenu($release->project); @@ -174,7 +185,9 @@ class projectrelease extends control $this->view->position[] = $this->lang->release->edit; $this->view->release = $release; $this->view->build = $build; - $this->view->builds = $this->loadModel('build')->getProjectBuildPairs($release->project, $release->product, $release->branch, 'notrunk|withbranch'); + $this->view->builds = $builds; + $this->view->users = $this->loadModel('user')->getPairs('noletter|noclosed'); + $this->display(); } diff --git a/module/projectrelease/css/create.css b/module/projectrelease/css/create.css new file mode 100644 index 0000000000..5878d6c75f --- /dev/null +++ b/module/projectrelease/css/create.css @@ -0,0 +1 @@ +.checkbox-primary {display: inline-block; margin-left: 10px;} diff --git a/module/projectrelease/css/edit.css b/module/projectrelease/css/edit.css index 76ca9a7478..dec7f697cc 100644 --- a/module/projectrelease/css/edit.css +++ b/module/projectrelease/css/edit.css @@ -1,3 +1,4 @@ .table-form > tbody > tr > td > .row > [class*="col-"] .panel-heading {line-height: 16px;} .contentDiv {height: 190px; overflow-y: auto; margin-bottom: 10px !important;} .contentDiv .panel-heading {line-height: 14px;} +.checkbox-primary {display: inline-block; margin-left: 10px;} diff --git a/module/projectrelease/model.php b/module/projectrelease/model.php index e252327854..46eb6c0aae 100644 --- a/module/projectrelease/model.php +++ b/module/projectrelease/model.php @@ -102,6 +102,7 @@ class projectreleaseModel extends model */ public function create() { + /* Init vars. */ $productID = $this->post->product; $branch = $this->post->branch; $buildID = 0; @@ -126,6 +127,8 @@ class projectreleaseModel extends model ->setDefault('stories', '') ->join('stories', ',') ->join('bugs', ',') + ->join('mailto', ',') + ->join('notify', ',') ->setIF($this->post->build == false, 'build', $buildID) ->setIF($productID, 'product', $productID) ->setIF($branch, 'branch', $branch) @@ -213,16 +216,20 @@ class projectreleaseModel extends model */ public function update($releaseID) { + /* Init vars. */ $releaseID = (int)$releaseID; $oldRelease = $this->dao->select('*')->from(TABLE_RELEASE)->where('id')->eq($releaseID)->fetch(); $branch = $this->dao->select('branch')->from(TABLE_BUILD)->where('id')->eq((int)$this->post->build)->fetch('branch'); $release = fixer::input('post')->stripTags($this->config->release->editor->edit['id'], $this->config->allowedTags) ->add('branch', (int)$branch) + ->join('mailto', ',') + ->join('notify', ',') ->setIF(!$this->post->marker, 'marker', 0) ->cleanInt('product') ->remove('files,labels,allchecker,uid') ->get(); + $release = $this->loadModel('file')->processImgURL($release, $this->config->release->editor->edit['id'], $this->post->uid); $this->dao->update(TABLE_RELEASE)->data($release) ->autoCheck() @@ -315,4 +322,17 @@ class projectreleaseModel extends model $this->loadModel('action'); foreach($this->post->bugs as $bugID) $this->action->create('bug', $bugID, 'linked2release', '', $releaseID); } + + /** + * Send mail. + * + * @param int $releaseID + * @param int $actionID + * @access public + * @return void + */ + public function sendmail($releaseID, $actionID) + { + $this->loadModel('release')->sendmail($releaseID, $actionID); + } } diff --git a/module/projectrelease/view/create.html.php b/module/projectrelease/view/create.html.php index 174c83ec00..254435a174 100644 --- a/module/projectrelease/view/create.html.php +++ b/module/projectrelease/view/create.html.php @@ -59,6 +59,18 @@ release->desc;?> + + release->notify;?> + release->notifyList);?> + + + release->mailto;?> + +
+ chooseUsersToMail}' multiple");?> +
+ + files;?> fetch('file', 'buildform');?> diff --git a/module/projectrelease/view/edit.html.php b/module/projectrelease/view/edit.html.php index bf94003bc4..e195cc8f4d 100644 --- a/module/projectrelease/view/edit.html.php +++ b/module/projectrelease/view/edit.html.php @@ -52,6 +52,18 @@ release->desc;?> desc), "rows=10 class='form-control kindeditor' hidefocus='true'");?> + + release->notify;?> + release->notifyList, $release->notify);?> + + + release->mailto;?> + +
+ mailto, "class='form-control chosen' data-placeholder='{$lang->chooseUsersToMail}' multiple");?> +
+ + files;?> fetch('file', 'buildform');?> diff --git a/module/release/control.php b/module/release/control.php index 76f0d2f213..c90635efaa 100644 --- a/module/release/control.php +++ b/module/release/control.php @@ -85,7 +85,9 @@ class release extends control $this->view->position[] = $this->lang->release->create; $this->view->productID = $productID; $this->view->builds = $builds; + $this->view->users = $this->loadModel('user')->getPairs('noletter|noclosed'); $this->view->lastRelease = $this->release->getLast($productID, $branch); + $this->display(); } @@ -122,11 +124,20 @@ class release extends control $this->commonAction($release->product, $release->branch); $build = $this->build->getById($release->build); + $builds = $this->loadModel('build')->getProductBuildPairs($release->product, $release->branch, 'notrunk|withbranch', false); + $releaseBuilds = $this->release->getReleaseBuilds($release->product, $release->branch); + foreach($releaseBuilds as $releaseBuild) + { + if($releaseBuild != $build->id) unset($builds[$releaseBuild]); + } + unset($builds['trunk']); + $this->view->title = $this->view->product->name . $this->lang->colon . $this->lang->release->edit; $this->view->position[] = $this->lang->release->edit; $this->view->release = $release; $this->view->build = $build; - $this->view->builds = $this->loadModel('build')->getProductBuildPairs($release->product, $release->branch, 'notrunk|withbranch', false); + $this->view->builds = $builds; + $this->view->users = $this->loadModel('user')->getPairs('noletter|noclosed'); $this->display(); } diff --git a/module/release/css/create.css b/module/release/css/create.css new file mode 100644 index 0000000000..5878d6c75f --- /dev/null +++ b/module/release/css/create.css @@ -0,0 +1 @@ +.checkbox-primary {display: inline-block; margin-left: 10px;} diff --git a/module/release/css/edit.css b/module/release/css/edit.css index 76ca9a7478..dec7f697cc 100644 --- a/module/release/css/edit.css +++ b/module/release/css/edit.css @@ -1,3 +1,4 @@ .table-form > tbody > tr > td > .row > [class*="col-"] .panel-heading {line-height: 16px;} .contentDiv {height: 190px; overflow-y: auto; margin-bottom: 10px !important;} .contentDiv .panel-heading {line-height: 14px;} +.checkbox-primary {display: inline-block; margin-left: 10px;} diff --git a/module/release/lang/de.php b/module/release/lang/de.php index 7ad42950d8..031c95cf46 100644 --- a/module/release/lang/de.php +++ b/module/release/lang/de.php @@ -56,6 +56,8 @@ $lang->release->createdBugs = 'Erstellte %s Bugs'; $lang->release->export = 'Export as HTML'; $lang->release->yesterday = 'Gestern veröffentlicht'; $lang->release->all = 'All'; +$lang->release->notify = 'Notify'; +$lang->release->mailto = 'Mailto'; $lang->release->filePath = 'Download : '; $lang->release->scmPath = 'SCM Pfad : '; @@ -74,3 +76,9 @@ $lang->release->changeStatusList['terminate'] = 'Terminiert'; $lang->release->action = new stdclass(); $lang->release->action->changestatus = array('main' => '$date, 由 $actor $extra。', 'extra' => 'changeStatusList'); + +$lang->release->notifyList['PO'] = "{$lang->productCommon} Owner"; +$lang->release->notifyList['QD'] = 'QA Manager'; +$lang->release->notifyList['SC'] = 'Story Creator'; +$lang->release->notifyList['ET'] = "{$lang->execution->common} Team Members"; +$lang->release->notifyList['PT'] = "Project Team Members"; diff --git a/module/release/lang/en.php b/module/release/lang/en.php index 614f409cc5..a77a316989 100644 --- a/module/release/lang/en.php +++ b/module/release/lang/en.php @@ -56,6 +56,8 @@ $lang->release->createdBugs = 'Unresolved %s Bug'; $lang->release->export = 'Export as HTML'; $lang->release->yesterday = 'Released Yesterday'; $lang->release->all = 'All'; +$lang->release->notify = 'Notify'; +$lang->release->mailto = 'Mailto'; $lang->release->filePath = 'Download : '; $lang->release->scmPath = 'SCM Path : '; @@ -74,3 +76,9 @@ $lang->release->changeStatusList['terminate'] = 'Terminated'; $lang->release->action = new stdclass(); $lang->release->action->changestatus = array('main' => '$date, $extra by $actor', 'extra' => 'changeStatusList'); + +$lang->release->notifyList['PO'] = "{$lang->productCommon} Owner"; +$lang->release->notifyList['QD'] = 'QA Manager'; +$lang->release->notifyList['SC'] = 'Story Creator'; +$lang->release->notifyList['ET'] = "{$lang->execution->common} Team Members"; +$lang->release->notifyList['PT'] = "Project Team Members"; diff --git a/module/release/lang/fr.php b/module/release/lang/fr.php index 794a18a0cb..ffc16ca3dc 100644 --- a/module/release/lang/fr.php +++ b/module/release/lang/fr.php @@ -56,6 +56,8 @@ $lang->release->createdBugs = '%s Bugs non résolus'; $lang->release->export = 'Export HTML'; $lang->release->yesterday = 'Versionné Hier'; $lang->release->all = 'Tout'; +$lang->release->notify = 'Notify'; +$lang->release->mailto = 'Mailto'; $lang->release->filePath = 'Télecharger : '; $lang->release->scmPath = 'SCM Path : '; @@ -74,3 +76,9 @@ $lang->release->changeStatusList['terminate'] = 'Terminée'; $lang->release->action = new stdclass(); $lang->release->action->changestatus = array('main' => '$date, $extra par $actor', 'extra' => 'changeStatusList'); + +$lang->release->notifyList['PO'] = "{$lang->productCommon} Owner"; +$lang->release->notifyList['QD'] = 'QA Manager'; +$lang->release->notifyList['SC'] = 'Story Creator'; +$lang->release->notifyList['ET'] = "{$lang->execution->common} Team Members"; +$lang->release->notifyList['PT'] = "Project Team Members"; diff --git a/module/release/lang/vi.php b/module/release/lang/vi.php index 57607a8d2a..8d273a0dac 100644 --- a/module/release/lang/vi.php +++ b/module/release/lang/vi.php @@ -56,6 +56,8 @@ $lang->release->createdBugs = 'Bug %s chưa được giải quyết'; $lang->release->export = 'Xuất ra HTML'; $lang->release->yesterday = 'Phát hành hôm qua'; $lang->release->all = 'Tất cả'; +$lang->release->notify = 'Notify'; +$lang->release->mailto = 'Mailto'; $lang->release->filePath = 'Tải về : '; $lang->release->scmPath = 'SCM Path : '; @@ -74,3 +76,9 @@ $lang->release->changeStatusList['terminate'] = 'Hoàn thành'; $lang->release->action = new stdclass(); $lang->release->action->changestatus = array('main' => '$date, $extra bởi $actor', 'extra' => 'changeStatusList'); + +$lang->release->notifyList['PO'] = "{$lang->productCommon} Owner"; +$lang->release->notifyList['QD'] = 'QA Manager'; +$lang->release->notifyList['SC'] = 'Story Creator'; +$lang->release->notifyList['ET'] = "{$lang->execution->common} Team Members"; +$lang->release->notifyList['PT'] = "Project Team Members"; diff --git a/module/release/lang/zh-cn.php b/module/release/lang/zh-cn.php index e108611d91..bcdafcdb9c 100644 --- a/module/release/lang/zh-cn.php +++ b/module/release/lang/zh-cn.php @@ -56,6 +56,8 @@ $lang->release->createdBugs = '本次共遗留 %s 个Bug'; $lang->release->export = '导出HTML'; $lang->release->yesterday = '昨日发布'; $lang->release->all = '所有'; +$lang->release->notify = '通知人员'; +$lang->release->mailto = '抄送给'; $lang->release->filePath = '下载地址:'; $lang->release->scmPath = '版本库地址:'; @@ -74,3 +76,9 @@ $lang->release->changeStatusList['terminate'] = '停止维护'; $lang->release->action = new stdclass(); $lang->release->action->changestatus = array('main' => '$date, 由 $actor $extra。', 'extra' => 'changeStatusList'); + +$lang->release->notifyList['PO'] = "{$lang->productCommon}负责人"; +$lang->release->notifyList['QD'] = '测试负责人'; +$lang->release->notifyList['SC'] = '需求提交人'; +$lang->release->notifyList['ET'] = "所在{$lang->execution->common}团队成员"; +$lang->release->notifyList['PT'] = "所在项目团队成员"; diff --git a/module/release/model.php b/module/release/model.php index 48d9dbac41..e0074e0cb1 100644 --- a/module/release/model.php +++ b/module/release/model.php @@ -110,6 +110,7 @@ class releaseModel extends model */ public function create($productID, $branch = 0) { + /* Init vars. */ $productID = (int)$productID; $branch = (int)$branch; $buildID = 0; @@ -126,6 +127,8 @@ class releaseModel extends model ->setDefault('stories', '') ->join('stories', ',') ->join('bugs', ',') + ->join('mailto', ',') + ->join('notify', ',') ->setIF($this->post->build == false, 'build', $buildID) ->stripTags($this->config->release->editor->create['id'], $this->config->allowedTags) ->remove('allchecker,files,labels,uid') @@ -216,16 +219,20 @@ class releaseModel extends model */ public function update($releaseID) { + /* Init vars. */ $releaseID = (int)$releaseID; $oldRelease = $this->dao->select('*')->from(TABLE_RELEASE)->where('id')->eq($releaseID)->fetch(); $branch = $this->dao->select('branch')->from(TABLE_BUILD)->where('id')->eq((int)$this->post->build)->fetch('branch'); $release = fixer::input('post')->stripTags($this->config->release->editor->edit['id'], $this->config->allowedTags) ->add('branch', (int)$branch) + ->join('mailto', ',') + ->join('notify', ',') ->setIF(!$this->post->marker, 'marker', 0) ->cleanInt('product') ->remove('files,labels,allchecker,uid') ->get(); + $release = $this->loadModel('file')->processImgURL($release, $this->config->release->editor->edit['id'], $this->post->uid); /* update release project and branch */ @@ -249,6 +256,67 @@ class releaseModel extends model } } + /** + * Get notify persons. + * + * @param string $notfiyList + * @param int $productID + * @param int $buildID + * @access public + * @return array + */ + public function getNotifyPersons($notifyList = '', $productID = 0, $buildID = 0) + { + if(empty($notifyList)) return array(); + + /* Init vars. */ + $notifyPersons = array(); + $managers = ''; + $notifyList = explode($notifyList, ','); + + foreach($notifyList as $notify) + { + if($notify == 'PO' or $notify == 'QD' or $notify == 'feedback') + { + $managers .= $notify . ','; + } + elseif($notify == 'SC' and !empty($buildID)) + { + $stories = $this->dao->select('stories')->from(TABLE_BUILD)->where('id')->eq($buildID)->fetch('stories'); + $stories = trim($stories, ','); + + if(empty($stories)) continue; + + $createUsers = $this->dao->select('openedBy')->from(TABLE_STORY)->where('id')->in($stories)->fetchPairs(); + $notifyPersons += $createUsers; + } + elseif(($notify == 'ET' or $notify == 'PT') and !empty($buildID)) + { + $type = $notify == 'ET' ? 'execution' : 'project'; + $members = $this->dao->select('t2.account')->from(TABLE_BUILD)->alias('t1') + ->leftJoin(TABLE_TEAM)->alias('t2')->on('t1.' . $type .'=t2.root') + ->where('t2.type')->eq($type) + ->fetchPairs(); + + if(empty($members)) continue; + + $notifyPersons += $members; + } + } + + if(!empty($managers)) + { + $managers = trim($managers, ','); + $managerUsers = $this->dao->select($managers)->from(TABLE_PRODUCT)->where('id')->eq($productID)->fetch(); + foreach($managerUsers as $account) + { + if(!isset($notifyPersons[$account])) $notifyPersons += array($account => $account); + } + } + + return $notifyPersons; + } + /** * Link stories * @@ -405,4 +473,94 @@ class releaseModel extends model $this->dao->update(TABLE_RELEASE)->set('status')->eq($status)->where('id')->eq($releaseID)->exec(); return dao::isError(); } + + /** + * Send mail. + * + * @param int $releaseID + * @param int $actionID + * @access public + * @return void + */ + public function sendmail($releaseID, $actionID) + { + $this->loadModel('mail'); + $release = $this->getByID($releaseID); + $users = $this->loadModel('user')->getPairs('noletter'); + + /* Get action info. */ + $action = $this->loadModel('action')->getById($actionID); + $history = $this->action->getHistory($actionID); + $action->history = isset($history[$actionID]) ? $history[$actionID] : array(); + $action->appendLink = ''; + + /* Get mail content. */ + $modulePath = $this->app->getModulePath($appName = '', 'release'); + $oldcwd = getcwd(); + $viewFile = $modulePath . 'view/sendmail.html.php'; + chdir($modulePath . 'view'); + if(file_exists($modulePath . 'ext/view/sendmail.html.php')) + { + $viewFile = $modulePath . 'ext/view/sendmail.html.php'; + chdir($modulePath . 'ext/view'); + } + ob_start(); + include $viewFile; + foreach(glob($modulePath . 'ext/view/sendmail.*.html.hook.php') as $hookFile) include $hookFile; + $mailContent = ob_get_contents(); + ob_end_clean(); + chdir($oldcwd); + + $sendUsers = $this->getToAndCcList($release); + if(!$sendUsers) return; + list($toList, $ccList) = $sendUsers; + $subject = 'RELEASE #' . $release->id . ' ' . $release->name; + + /* Send it. */ + $this->mail->send($toList, $subject, $mailContent, $ccList); + if($this->mail->isError()) error_log(join("\n", $this->mail->getError())); + } + + /** + * Get toList and ccList. + * + * @param object $release + * @access public + * @return bool|array + */ + public function getToAndCcList($release) + { + /* Set toList and ccList. */ + $toList = $this->app->user->account; + $ccList = $release->mailto . ','; + + /* Get notifiy persons. */ + $notifyPersons = array(); + if(!empty($release->notify)) $notifyPersons = $this->getNotifyPersons($release->notify, $release->product, $release->build); + + foreach($notifyPersons as $account) + { + if(strpos($ccList, ",{$account},") === false) $ccList .= $account . ','; + } + + $ccList = trim($ccList, ','); + + if(empty($toList)) + { + if(empty($ccList)) return false; + if(strpos($ccList, ',') === false) + { + $toList = $ccList; + $ccList = ''; + } + else + { + $commaPos = strpos($ccList, ','); + $toList = substr($ccList, 0, $commaPos); + $ccList = substr($ccList, $commaPos + 1); + } + } + + return array($toList, $ccList); + } } diff --git a/module/release/view/create.html.php b/module/release/view/create.html.php index 32d21a049f..6a14e4d930 100644 --- a/module/release/view/create.html.php +++ b/module/release/view/create.html.php @@ -49,6 +49,18 @@ release->desc;?> + + release->notify;?> + release->notifyList);?> + + + release->mailto;?> + +
+ chooseUsersToMail}' multiple");?> +
+ + files;?> fetch('file', 'buildform');?> diff --git a/module/release/view/edit.html.php b/module/release/view/edit.html.php index c47c639e62..8b00e012d1 100644 --- a/module/release/view/edit.html.php +++ b/module/release/view/edit.html.php @@ -52,6 +52,18 @@ release->desc;?> desc), "rows=10 class='form-control kindeditor' hidefocus='true'");?> + + release->notify;?> + release->notifyList, $release->notify);?> + + + release->mailto;?> + +
+ mailto, "class='form-control chosen' data-placeholder='{$lang->chooseUsersToMail}' multiple");?> +
+ + files;?> fetch('file', 'buildform');?> diff --git a/module/release/view/sendmail.html.php b/module/release/view/sendmail.html.php new file mode 100644 index 0000000000..d33dcb10a2 --- /dev/null +++ b/module/release/view/sendmail.html.php @@ -0,0 +1,35 @@ + + * @package release + * @version $Id: sendmail.html.php 867 2021-08-12 13:37:58Z $ + * @link http://www.zentao.net + */ +?> +id . ' ' . $release->name;?> +app->openApp == 'product' ? 'release' : 'projectrelease';?> +app->getModuleRoot() . 'common/view/mail.header.html.php';?> + + + + + + +
+ config->mail, 'domain', common::getSysURL()) . helper::createLink($module, 'view', "releaseID=$release->id", 'html'), $mailTitle, '', "text-decoration: underline;'");?> +
+ + + + +
+ lang->release->desc;?> +
desc;?>
+
+ + +app->getModuleRoot() . 'common/view/mail.footer.html.php';?> diff --git a/module/story/config.php b/module/story/config.php index 247094693c..7fb95e824d 100644 --- a/module/story/config.php +++ b/module/story/config.php @@ -199,5 +199,5 @@ $config->story->datatable->fieldList['caseCount']['name'] = $lang->story->ca $config->story->datatable->fieldList['actions']['title'] = 'actions'; $config->story->datatable->fieldList['actions']['fixed'] = 'right'; -$config->story->datatable->fieldList['actions']['width'] = $app->rawModule == 'projectstory' ? '200' : '180'; +$config->story->datatable->fieldList['actions']['width'] = '200'; $config->story->datatable->fieldList['actions']['required'] = 'yes'; diff --git a/module/story/control.php b/module/story/control.php index e15c2be946..32eba07ae1 100644 --- a/module/story/control.php +++ b/module/story/control.php @@ -1103,6 +1103,21 @@ class story extends control die(js::reload('parent')); } + /** + * Recall the story review. + * + * @param int $storyID + * @access public + * @return void + */ + public function recall($storyID) + { + $this->story->recall($storyID); + $this->loadModel('action')->create('story', $storyID, 'Recalled'); + + die(js::locate('parent.parent')); + } + /** * Close a story. * diff --git a/module/story/lang/de.php b/module/story/lang/de.php index 7acc0c162a..33b654af4a 100644 --- a/module/story/lang/de.php +++ b/module/story/lang/de.php @@ -27,6 +27,7 @@ $lang->story->changed = 'Geändert'; $lang->story->assignTo = 'Assign'; $lang->story->assignAction = 'Assign Story'; $lang->story->review = 'Prüfen'; +$lang->story->recall = 'Revoke'; $lang->story->reviewAction = 'Review Story'; $lang->story->needReview = 'Need Review'; $lang->story->batchReview = 'Mehere prüfen'; @@ -286,6 +287,7 @@ $lang->story->action->reviewed = array('main' => '$date, geprüft v $lang->story->action->passreviewed = array('main' => '$date, determined by the System. The result is Pass.'); $lang->story->action->clarifyreviewed = array('main' => '$date, determined by the System. The result is Review Failed. Please re-initiate the review after changing.'); $lang->story->action->rejectreviewed = array('main' => '$date, geprüft von $actor. Das Ergebnis ist $extra, Der Grund ist $reason.', 'extra' => 'reviewResultList', 'reason' => 'reasonList'); +$lang->story->action->recalled = array('main' => '$date, recalled by $actor.'); $lang->story->action->closed = array('main' => '$date, geschlossen von $actor. Der Grund ist $extra $appendLink.', 'extra' => 'reasonList'); $lang->story->action->reviewclosed = array('main' => '$date, closed by System. The reasion is Review Failed.'); $lang->story->action->linked2plan = array('main' => '$date, verknüpft von $actor mit Plan $extra'); diff --git a/module/story/lang/en.php b/module/story/lang/en.php index d18f18bb0d..9876a7b8a8 100644 --- a/module/story/lang/en.php +++ b/module/story/lang/en.php @@ -20,53 +20,54 @@ $lang->story->createStory = 'Create ' . $lang->story->story; $lang->story->createRequirement = 'Create ' . $lang->story->requirement; $lang->story->affectedStories = "Affected {$lang->story->story}"; -$lang->story->batchCreate = "Batch Create"; -$lang->story->change = "Change"; -$lang->story->changed = 'Change'; -$lang->story->assignTo = 'Assign'; -$lang->story->review = 'Review'; -$lang->story->needReview = 'Need Review'; -$lang->story->batchReview = 'Batch Review'; -$lang->story->edit = "Edit Story"; -$lang->story->batchEdit = "Batch Edit"; -$lang->story->subdivide = 'Decompose'; -$lang->story->link = 'Link'; -$lang->story->unlink = 'Unlink'; -$lang->story->track = 'Track'; -$lang->story->trackAB = 'Track'; -$lang->story->processStoryChange= 'Process Story Change'; -$lang->story->splitRequirent = 'Decompose'; -$lang->story->close = 'Close'; -$lang->story->batchClose = 'Batch Close'; -$lang->story->activate = 'Activate'; -$lang->story->delete = "Delete"; -$lang->story->view = "Story Detail"; -$lang->story->setting = "Settings"; -$lang->story->tasks = "Linked Tasks"; -$lang->story->bugs = "Linked Bugs"; -$lang->story->cases = "Linked Cases"; -$lang->story->taskCount = 'Tasks'; -$lang->story->bugCount = 'Bugs'; -$lang->story->caseCount = 'Cases'; -$lang->story->taskCountAB = 'T'; -$lang->story->bugCountAB = 'B'; -$lang->story->caseCountAB = 'C'; -$lang->story->linkStory = 'Link Story'; -$lang->story->unlinkStory = 'UnLinked'; -$lang->story->export = "Export Data"; -$lang->story->zeroCase = "Stories without cases"; -$lang->story->zeroTask = "Only list stories without tasks"; -$lang->story->reportChart = "Report"; -$lang->story->copyTitle = "Copy Title"; -$lang->story->batchChangePlan = "Batch Change Plans"; -$lang->story->batchChangeBranch = "Batch Change Branches"; -$lang->story->batchChangeStage = "Batch Change Phases"; -$lang->story->batchAssignTo = "Batch Assign"; -$lang->story->batchChangeModule = "Batch Change Modules"; -$lang->story->viewAll = "See All"; -$lang->story->toTask = 'Convert to Task'; -$lang->story->batchToTask = 'Batch Convert to Task'; -$lang->story->convertRelations = 'Convert Relations'; +$lang->story->batchCreate = "Batch Create"; +$lang->story->change = "Change"; +$lang->story->changed = 'Change'; +$lang->story->assignTo = 'Assign'; +$lang->story->review = 'Review'; +$lang->story->recall = 'Revoke'; +$lang->story->needReview = 'Need Review'; +$lang->story->batchReview = 'Batch Review'; +$lang->story->edit = "Edit Story"; +$lang->story->batchEdit = "Batch Edit"; +$lang->story->subdivide = 'Decompose'; +$lang->story->link = 'Link'; +$lang->story->unlink = 'Unlink'; +$lang->story->track = 'Track'; +$lang->story->trackAB = 'Track'; +$lang->story->processStoryChange = 'Process Story Change'; +$lang->story->splitRequirent = 'Decompose'; +$lang->story->close = 'Close'; +$lang->story->batchClose = 'Batch Close'; +$lang->story->activate = 'Activate'; +$lang->story->delete = "Delete"; +$lang->story->view = "Story Detail"; +$lang->story->setting = "Settings"; +$lang->story->tasks = "Linked Tasks"; +$lang->story->bugs = "Linked Bugs"; +$lang->story->cases = "Linked Cases"; +$lang->story->taskCount = 'Tasks'; +$lang->story->bugCount = 'Bugs'; +$lang->story->caseCount = 'Cases'; +$lang->story->taskCountAB = 'T'; +$lang->story->bugCountAB = 'B'; +$lang->story->caseCountAB = 'C'; +$lang->story->linkStory = 'Link Story'; +$lang->story->unlinkStory = 'UnLinked'; +$lang->story->export = "Export Data"; +$lang->story->zeroCase = "Stories without cases"; +$lang->story->zeroTask = "Only list stories without tasks"; +$lang->story->reportChart = "Report"; +$lang->story->copyTitle = "Copy Title"; +$lang->story->batchChangePlan = "Batch Change Plans"; +$lang->story->batchChangeBranch = "Batch Change Branches"; +$lang->story->batchChangeStage = "Batch Change Phases"; +$lang->story->batchAssignTo = "Batch Assign"; +$lang->story->batchChangeModule = "Batch Change Modules"; +$lang->story->viewAll = "See All"; +$lang->story->toTask = 'Convert to Task'; +$lang->story->batchToTask = 'Batch Convert to Task'; +$lang->story->convertRelations = 'Convert Relations'; $lang->story->editAction = "Edit {$lang->SRCommon}"; $lang->story->changeAction = "Change {$lang->SRCommon}"; @@ -287,6 +288,7 @@ $lang->story->action->reviewed = array('main' => '$date, recorded b $lang->story->action->passreviewed = array('main' => '$date, determined by the System. The result is Pass.'); $lang->story->action->clarifyreviewed = array('main' => '$date, determined by the System. The result is Review Failed. Please re-initiate the review after changing.'); $lang->story->action->rejectreviewed = array('main' => '$date, recorded by $actor. The result is $extra. The reasion is $reason.', 'extra' => 'reviewResultList', 'reason' => 'reasonList'); +$lang->story->action->recalled = array('main' => '$date, recalled by $actor.'); $lang->story->action->closed = array('main' => '$date, closed by $actor. The reasion is $extra $appendLink.', 'extra' => 'reasonList'); $lang->story->action->reviewclosed = array('main' => '$date, closed by System. The reasion is Review Failed.'); $lang->story->action->linked2plan = array('main' => '$date, linked by $actor to Plan $extra'); diff --git a/module/story/lang/fr.php b/module/story/lang/fr.php index 3cad39afcc..74eafeec2a 100644 --- a/module/story/lang/fr.php +++ b/module/story/lang/fr.php @@ -27,6 +27,7 @@ $lang->story->changed = 'Changée'; $lang->story->assignTo = 'Affecter'; $lang->story->assignAction = 'Affecter Story'; $lang->story->review = 'Valider'; +$lang->story->recall = 'Revoke'; $lang->story->reviewAction = 'Valider Story'; $lang->story->needReview = 'Validation requise'; $lang->story->batchReview = 'Validation par lot'; @@ -286,6 +287,7 @@ $lang->story->action->reviewed = array('main' => '$date, validée p $lang->story->action->passreviewed = array('main' => '$date, determined by the System. The result is Pass.'); $lang->story->action->clarifyreviewed = array('main' => '$date, determined by the System. The result is Review Failed. Please re-initiate the review after changing.'); $lang->story->action->rejectreviewed = array('main' => '$date, validée par $actor. Le résultat est $extra. La raison est $reason.', 'extra' => 'reviewResultList', 'reason' => 'reasonList'); +$lang->story->action->recalled = array('main' => '$date, recalled by $actor.'); $lang->story->action->closed = array('main' => '$date, Fermée par $actor. La raison est $extra $appendLink.', 'extra' => 'reasonList'); $lang->story->action->reviewclosed = array('main' => '$date, closed by System. The reasion is Review Failed.'); $lang->story->action->linked2plan = array('main' => '$date, planifiée par $actor au Plan $extra'); diff --git a/module/story/lang/vi.php b/module/story/lang/vi.php index 204c3aaac8..6254d9455d 100644 --- a/module/story/lang/vi.php +++ b/module/story/lang/vi.php @@ -27,6 +27,7 @@ $lang->story->changed = "Đã đổi"; $lang->story->assignTo = 'Giao cho'; $lang->story->assignAction = 'Bàn giao câu chuyện'; $lang->story->review = 'Xét duyệt'; +$lang->story->recall = 'Revoke'; $lang->story->reviewAction = 'Duyệt câu chuyện'; $lang->story->needReview = 'Duyệt nhu cầu'; $lang->story->batchReview = 'Duyệt hàng loạt'; @@ -286,6 +287,7 @@ $lang->story->action->reviewed = array('main' => '$date, ghi nhận $lang->story->action->passreviewed = array('main' => '$date, determined by the System. The result is Pass.'); $lang->story->action->clarifyreviewed = array('main' => '$date, determined by the System. The result is Review Failed. Please re-initiate the review after changing.'); $lang->story->action->rejectreviewed = array('main' => '$date, ghi nhận bởi $actor. Kết quả là $extra. Lý do là $reason', 'extra' => 'reviewResultList', 'reason' => 'reasonList'); +$lang->story->action->recalled = array('main' => '$date, recalled by $actor.'); $lang->story->action->closed = array('main' => '$date, được đóng bởi $actor. Lý do là $extra $appendLink.', 'extra' => 'reasonList'); $lang->story->action->reviewclosed = array('main' => '$date, closed by System. The reasion is Review Failed.'); $lang->story->action->linked2plan = array('main' => '$date, liên kết bởi $actor tới kế hoạch $extra'); diff --git a/module/story/lang/zh-cn.php b/module/story/lang/zh-cn.php index e893d74bbe..6284468bf3 100644 --- a/module/story/lang/zh-cn.php +++ b/module/story/lang/zh-cn.php @@ -25,6 +25,7 @@ $lang->story->change = "变更"; $lang->story->changed = "{$lang->SRCommon}变更"; $lang->story->assignTo = '指派'; $lang->story->review = '评审'; +$lang->story->recall = '撤销评审'; $lang->story->needReview = '需要评审'; $lang->story->batchReview = '批量评审'; $lang->story->edit = "编辑"; @@ -287,6 +288,7 @@ $lang->story->action->reviewed = array('main' => '$date, 由 story->action->passreviewed = array('main' => '$date, 由 系统 判定,结果为 确认通过。'); $lang->story->action->clarifyreviewed = array('main' => '$date, 由 系统 判定,结果为 评审不通过,请变更后重新发起评审。'); $lang->story->action->rejectreviewed = array('main' => '$date, 由 $actor 记录评审结果,结果为 $extra,原因为 $reason。', 'extra' => 'reviewResultList', 'reason' => 'reasonList'); +$lang->story->action->recalled = array('main' => '$date, 由 $actor 撤销评审。'); $lang->story->action->closed = array('main' => '$date, 由 $actor 关闭,原因为 $extra $appendLink。', 'extra' => 'reasonList'); $lang->story->action->reviewclosed = array('main' => '$date, 由 系统 关闭,原因为 评审失败。'); $lang->story->action->linked2plan = array('main' => '$date, 由 $actor 关联到计划 $extra。'); diff --git a/module/story/lang/zh-tw.php b/module/story/lang/zh-tw.php index 6daa4cb252..356ffb26f8 100644 --- a/module/story/lang/zh-tw.php +++ b/module/story/lang/zh-tw.php @@ -25,6 +25,7 @@ $lang->story->change = "變更"; $lang->story->changed = "{$lang->SRCommon}變更"; $lang->story->assignTo = '指派'; $lang->story->review = '評審'; +$lang->story->recall = '撤銷評審'; $lang->story->needReview = '需要評審'; $lang->story->batchReview = '批量評審'; $lang->story->edit = "編輯"; @@ -287,6 +288,7 @@ $lang->story->action->reviewed = array('main' => '$date, 由 story->action->passreviewed = array('main' => '$date, 由 系統 判定,結果為 確認通過。'); $lang->story->action->clarifyreviewed = array('main' => '$date, 由 系統 判定,結果為 評審不通過,請變更後重新發起評審。'); $lang->story->action->rejectreviewed = array('main' => '$date, 由 $actor 記錄評審結果,結果為 $extra,原因為 $reason。', 'extra' => 'reviewResultList', 'reason' => 'reasonList'); +$lang->story->action->recalled = array('main' => '$date, 由 $actor 撤銷評審。'); $lang->story->action->closed = array('main' => '$date, 由 $actor 關閉,原因為 $extra $appendLink。', 'extra' => 'reasonList'); $lang->story->action->reviewclosed = array('main' => '$date, 由 系統 關閉,原因為 評審失敗。'); $lang->story->action->linked2plan = array('main' => '$date, 由 $actor 關聯到計劃 $extra。'); diff --git a/module/story/model.php b/module/story/model.php index 2afa046068..49f2660573 100644 --- a/module/story/model.php +++ b/module/story/model.php @@ -1398,6 +1398,20 @@ class storyModel extends model return $actions; } + /** + * Recall the story review. + * + * @param int $storyID + * @access public + * @return void + */ + public function recall($storyID) + { + $story = $this->getById($storyID); + $this->dao->update(TABLE_STORY)->set('status')->eq('draft')->where('id')->eq($storyID)->exec(); + $this->dao->delete()->from(TABLE_STORYREVIEW)->where('story')->eq($storyID)->andWhere('version')->eq($story->version)->exec(); + } + /** * Subdivide story * @@ -3377,6 +3391,7 @@ class storyModel extends model if($action == 'change') return (count($story->reviewer) == 0 || count($story->notReview) == 0) and $story->status != 'closed'; if($action == 'review') return in_array($app->user->account, $story->notReview) and ($story->status == 'draft' or $story->status == 'changed'); + if($action == 'recall') return empty($story->reviewedBy) and strpos('draft,changed', $story->status) !== false and !empty($story->reviewer); if($action == 'close') return $story->status != 'closed'; if($action == 'activate') return $story->status == 'closed'; if($action == 'assignto') return $story->status != 'closed'; @@ -3499,7 +3514,10 @@ class storyModel extends model } } - $allReviewers = $this->dao->select('story,reviewer,result')->from(TABLE_STORYREVIEW)->where('story')->in($storyIdList)->orderBy('version')->fetchGroup('story', 'reviewer'); + $allReviewers = $this->dao->select('story,reviewer,result')->from(TABLE_STORY)->alias('t1') + ->leftJoin(TABLE_STORYREVIEW)->alias('t2')->on('t1.version=t2.version and t1.id=t2.story') + ->where('story')->in($storyIdList) + ->fetchGroup('story', 'reviewer'); foreach($allReviewers as $storyID => $reviewerList) { @@ -3782,6 +3800,7 @@ class storyModel extends model common::printIcon('story', 'change', $vars . "&from=$story->from", $story, 'list', 'alter', '', '', false, "data-group=$story->from"); common::printIcon('story', 'review', $vars . "&from=$story->from", $story, 'list', 'search', '', '', false, "data-group=$story->from"); + if($this->app->openApp != 'project') common::printIcon('story', 'recall', $vars, $story, 'list', 'back', 'hiddenwin', '', '', '', $this->lang->story->recall); common::printIcon('story', 'close', $vars, $story, 'list', '', '', 'iframe', true); common::printIcon('story', 'edit', $vars . "&from=$story->from", $story, 'list', '', '', '', false, "data-group=$story->from"); if($story->type != 'requirement') common::printIcon('story', 'createCase', "productID=$story->product&branch=$story->branch&module=0&from=¶m=0&$vars", $story, 'list', 'sitemap', '', '', false, "data-app='qa'"); diff --git a/www/js/zui/min.js b/www/js/zui/min.js index eb2e2bec9a..f4a140ce23 100644 --- a/www/js/zui/min.js +++ b/www/js/zui/min.js @@ -1,5 +1,5 @@ /*! - * ZUI: ZUI for Zentao - v1.9.2 - 2021-08-11 + * ZUI: ZUI for Zentao - v1.9.2 - 2021-08-12 * http://openzui.com * GitHub: https://github.com/easysoft/zui.git * Copyright (c) 2021 cnezsoft.com; Licensed MIT @@ -75,5 +75,5 @@ function(t){"function"==typeof define&&define.amd?define(["jquery"],t):"object"= * Binny V A, http://www.openjs.com/scripts/events/keyboard_shortcuts/ */ function(t){function e(e){if("string"==typeof e.data){var i=e.handler,n=e.data.toLowerCase().split(" ");e.handler=function(e){if(this===e.target||!/textarea|select/i.test(e.target.nodeName)&&"text"!==e.target.type){var o="keypress"!==e.type&&t.hotkeys.specialKeys[e.which],a=String.fromCharCode(e.which).toLowerCase(),s="",r={};e.altKey&&"alt"!==o&&(s+="alt+"),e.ctrlKey&&"ctrl"!==o&&(s+="ctrl+"),e.metaKey&&!e.ctrlKey&&"meta"!==o&&(s+="meta+"),e.shiftKey&&"shift"!==o&&(s+="shift+"),o?r[s+o]=!0:(r[s+a]=!0,r[s+t.hotkeys.shiftNums[a]]=!0,"shift+"===s&&(r[t.hotkeys.shiftNums[a]]=!0));for(var l=0,h=n.length;l","/":"?","\\":"|"}},t.each(["keydown","keyup","keypress"],function(){t.event.special[this]={add:e}})}(jQuery),function(t){"use strict";function e(e,i){if(e===!1)return e;if(!e)return i;e===!0?e={add:!0,"delete":!0,edit:!0,sort:!0}:"string"==typeof e&&(e=e.split(","));var n;return Array.isArray(e)&&(n={},t.each(e,function(e,i){t.isPlainObject(i)?n[i.action]=i:n[i]=!0}),e=n),t.isPlainObject(e)&&(n={},t.each(e,function(e,i){i?n[e]=t.extend({type:e},s[e],t.isPlainObject(i)?i:null):n[e]=!1}),e=n),i?t.extend(!0,{},i,e):e}function i(e,i,n){return i=i||e.type,t(n||e.template).addClass("tree-action").attr(t.extend({"data-type":i,title:e.title||""},e.attr)).data("action",e)}var n="zui.tree",o=0,a=function(e,i){this.name=n,this.$=t(e),this.getOptions(i),this._init()},s={sort:{template:''},add:{template:''},edit:{template:''},"delete":{template:''}};a.DEFAULTS={animate:null,initialState:"normal",toggleTemplate:''},a.prototype.add=function(e,i,n,o,a){var s,r=t(e),l=this.options;if(r.is("li")?(s=r.children("ul"),s.length||(s=t("
");i.$.addClass("load-indicator loading"),s.load(window.location.href+" #"+o,function(r){if(a===o)i.$.empty().html(s.children().html()),i.$.find('[data-ride="pager"]').pager();else{i.$.find("#"+o).empty().html(s.children().html());try{var l=t(r),h=l.find("#"+o).closest('[data-ride="table"],#'+a);if(h.length){var c=h.find(".table-statistic");c.length&&(i.defaultStatistic=c.html());var d=i.$.find('[data-ride="pager"]').data("zui.pager"),u=h.find('[data-ride="pager"]');d&&u.length&&d.set(u.data())}}catch(f){console.error(f)}}i.$.removeClass("load-indicator loading").trigger("beforeTableReload"),delete i.defaultStatistic,i.updateStatistic(),i.initModals(),i.$.datepickerAll();var p=i.$.find("tbody>tr"),g=!1;t.each(i.checkItems,function(t,e){e&&(i.checkRow(p.filter('[data-id="'+t+'"]'),!0,!0),g=!0)}),g&&i.updateCheckUI(),i.$.trigger("tableReload");var m=t("#mainMenu>.btn-toolbar>.btn-active-text>.label");if(m.length){var u=i.$.find(".pager[data-rec-total]"),v=u.length?u.attr("data-rec-total"):i.getTable().find("tbody:first>tr:not(.table-children)").length;m.text(v)}e&&e(),n.afterReload&&n.afterReload()})},r.prototype.initModals=function(){var e=this,i=e.options,n=e.$.find(i.iframeModalTrigger);if(n.length){var o={type:"iframe",onHide:i.replaceId?function(){var n=t.cookie("selfClose");(1==n||i.hot)&&(t("#triggerModal").data("cancel-reload",1),e.reload(function(){t.cookie("selfClose",0)}))}:null};n.modalTrigger(o)}},r.prototype.getTable=function(){var t=this.$;if(this.isDataTable)return t.find("div.datatable");var e=t.is("table")?t:t.find("table:not(.fixed-header-copy)").first();return e.is(".datatable")&&(this.isDataTable=!0,e.data("zui.datatable")||window.initDatatable(e),e=t.find("div.datatable")),e},r.prototype.toggleGroups=function(e){var i=this,n={};i.$.find("tbody>tr").each(function(){var o=t(this).closest("tr").data("id");n[o]||i.toggleRowGroup(o,e)})},r.prototype.toggleRowGroup=function(i,n){var o=this.$.find('tbody>tr[data-id="'+i+'"]'),a=o.filter(".group-summary"),s=n===e?!a.hasClass("hidden"):!!n; -o.not(".group-summary").toggleClass("hidden",!s),a.toggleClass("hidden",s),t("body").toggleClass("table-group-collapsed",!this.$.find("tbody>tr.group-summary.hidden").length)},r.prototype.updateStatistic=function(){var i=this,n=i.$.find(".table-statistic");if(n.length){if(i.defaultStatistic===e&&(i.defaultStatistic=n.html()),i.options.statisticCreator)return void n.html(i.options.statisticCreator(i)||i.defaultStatistic);var o=i.statisticCols;if(!o&&o!==!1){o={};var a=!1;i.getTable().find("thead th").each(function(e){var i=t(this),n=i.data("statistic");n&&(a=!0,o[e]={format:n,name:i.text()})}),i.statisticCols=!!a&&o}var s=0;o&&t.each(o,function(t){o[t].total=0,o[t].checkedTotal=0}),i.$.find(i.isDataTable?".fixed-left tbody>tr":"tbody>tr").each(function(){var e=t(this),i=e.hasClass("checked"),n=e.children("td");i&&s++,o&&t.each(o,function(t){var e=parseFloat(n.eq(t).text());isNaN(e)&&(e=0),o[t].total+=e,i&&(o[t].checkedTotal+=e)})});var r=[];if(s)r.push(i.lang.selectedItems.format(s));else if(i.defaultStatistic)return void n.html(i.defaultStatistic);o&&t.each(o,function(t){var e=o[t],n=e[s?"checkedTotal":"total"];e.format&&(n=e.format.format(n)),r.push(i.lang.attrTotal.format(e.name,n))}),n.html(r.join(", "))}},r.prototype.updateFixUI=function(e){var i=this,n=(new Date).getTime();if(!e&&(i.lastUpdateCall&&clearTimeout(i.lastUpdateCall),!i.lastUpdateTime||n-i.lastUpdateTime
').append(t('
').addClass(i.attr("class")).append(n.clone())).insertAfter(i)),c){var d=h[0].getBoundingClientRect();l.css({left:d.left,width:h.width()}),l.find(".fixed-header-copy").css({left:o.left-d.left,position:"relative",minWidth:i.width()}),a||h.data("fixHeaderScroll")||(h.data("fixHeaderScroll",1),i.width()>h.width()&&h.on("scroll",function(){e.fixHeader()}))}else l.css({left:o.left,width:o.width});var u=l.find("th");n.find("th").each(function(e){u.eq(e).css("width",t(this).outerWidth())})}else l.remove()},r.prototype.fixFooter=function(){var e,i=this,n=i.getTable(),o=i.$.find(".table-footer");if(i.isDataTable)e=n[0].getBoundingClientRect();else{var a=n.find("tbody");if(!a.length)return;e=a[0].getBoundingClientRect()}var s=i.options.fixFooter;o.toggleClass("fixed-footer",!!r);var r="function"==typeof s?s(e,o):e.bottom>window.innerHeight-50-("number"==typeof s?s:i.pageFooterHeight||5);o.toggleClass("fixed-footer",!!r),n.toggleClass("with-footer-fixed",!!r),n.trigger("fixFooter",r);var l=t("body"),h=l.hasClass("body-modal");if(r){var c=n.parent(),d=c.is(".table-responsive");o.css({bottom:i.pageFooterHeight||0,left:d?c[0].getBoundingClientRect().left:e.left,width:d?c.width():e.width}),h&&l.css("padding-bottom",40)}else o.css({width:"",left:0,bottom:0}),h&&l.css("padding-bottom",0)},r.prototype.checkAll=function(e){var i=this,n=i.$.find(i.isDataTable?".fixed-left tbody>tr":"tbody>tr");n.each(function(){i.checkRow(t(this),e,!0)}),i.updateCheckUI()},r.prototype.checkRow=function(t,i,n){var o=this;o.isDataTable&&!t.is(".datatable-row-left")&&(t=o.getTable().find('.datatable-row-left[data-index="'+t.data("index")+'"]'));var a=t.find('input[type="checkbox"]');a.length&&!a.is(":disabled")&&(i===e&&(i=!a.is(":checked")),o.isDataTable?o.getTable().find('.datatable-row[data-index="'+t.data("index")+'"]').toggleClass("checked",i):t.toggleClass("checked",i),this.checkItems[t.data("id")]=i,a.prop("checked",i).trigger("change"),n||o.updateCheckUI())},r.prototype.updateCheckUI=function(){var e=this,i=e.getTable(),n=i.find(e.isDataTable?".fixed-left tbody>tr":"tbody>tr").not(".group-summary"),o=!1,a=null,s=0,r=!1,l=n.length;n.each(function(n){var h=t(this),c=h.find('input[type="checkbox"]');r=c.is(":checked");var d=e.isDataTable?i.find('.datatable-row[data-index="'+h.data("index")+'"]'):h;d.toggleClass("checked",r),d.toggleClass("row-check-begin",r&&!o),a&&a.toggleClass("row-check-end",!r&&o),r&&(s+=1),a=d,o=r,l===n+1&&d.toggleClass("row-check-end",r)}),e.$.toggleClass("has-row-checked",s>0).find(".check-all").toggleClass("checked",!(!l||s!==l)),e.updateStatistic(),e.options.onCheckChange&&e.options.onCheckChange()},r.DEFAULTS={checkable:!0,checkOnClickRow:!0,ajaxForm:!1,selectable:!0,fixHeader:!a,fixFooter:!a,iframeWidth:900,replaceId:"self",nestLevelIndent:18,nested:!1,preserveNested:!0,hot:!1,iframeModalTrigger:".iframe"},t.fn.table=function(e){return this.each(function(){var n=t(this),o=n.data(i),a="object"==typeof e&&e;o||n.data(i,o=new r(this,a)),"string"==typeof e&&o[e]()})},r.NAME=i,t.fn.table.Constructor=r,t(function(){t('[data-ride="table"]').table()})}(jQuery,void 0),function(t,e,i){t.fn._ajaxForm=t.fn.ajaxForm;var n={timeout:e.config?e.config.timeout:0,dataType:"json",method:"post"},o="";t.fn.enableForm=function(e,n,o){return e===i&&(e=!0),this.each(function(){var i=t(this);n||i.find('[type="submit"]').attr("disabled",e?null:"disabled"),!o&&i.hasClass("load-indicator")&&i.toggleClass("loading",!e),i.toggleClass("form-disabled",!e)})},t.enableForm=function(e,i,n,o){"string"==typeof e||e instanceof t?e=t(e):(o=n,n=i,i=e,e=t("form")),e.enableForm(i!==!1,n,o)},t.disableForm=function(e,i,n){t.enableForm(e,!1,i,n)};var a=function(e,i,n){"string"==typeof i&&(n=i,i=null),n=n||"show",t.zui.messager?t.zui.messager[n](e,i):alert(e)};t.ajaxForm=function(s,r){var l=t(s);if(l.length>1)return l.each(function(){t.ajaxForm(this,r)});"function"==typeof r&&(r={complete:r}),r=t.extend({},n,l.data(),r);var h=r.beforeSubmit,c=r.error,d=r.success,u=r.finish;delete r.finish,delete r.success,delete r.onError,delete r.beforeSubmit,r=t.extend({beforeSubmit:function(n,a,s){if(l.removeClass("form-watched").enableForm(!1),(h&&h(n,a,s))!==!1){var r={},c=a.find('[type="file"]');r.fileapi=c.length&&c[0].files!==i,r.formdata=e.FormData!==i;var d=r.fileapi&&a.find('input[type="file"]:enabled').filter(function(){return""!==t(this).val()}),u=d.length,f="multipart/form-data",p=a.attr("enctype")==f||a.attr("encoding")==f,g=r.fileapi&&r.formdata,m=u&&!g||p&&!r.formdata;m&&(""==o&&(o=s.url),s.url!=o&&(s.url=o),s.url=s.url.indexOf("&")>=0?s.url+"&HTTP_X_REQUESTED_WITH=XMLHttpRequest":s.url+"?HTTP_X_REQUESTED_WITH=XMLHttpRequest")}},success:function(i,n,o){if((d&&d(i,n,o,l))!==!1){try{"string"==typeof i&&(i=JSON.parse(i))}catch(s){}if(null===i||"object"!=typeof i)return i?alert(i):a("No response.","danger");var h=r.responser?t(r.responser):l.find(".form-responser");h.length||(h=t("#responser"));var c=i.message,f=function(){var n=i.callback;if(n){var o=n.indexOf("("),a=(o>0?n.substr(0,o):n).split("."),s=e,r=a[0];a.length>1&&(r=a[1],"top"===a[0]?s=e.top:"parent"===a[0]&&(s=e.parent));var h=s[r];if("function"==typeof h){var c=[];return o>0&&")"==n[n.length-1]&&(c=t.parseJSON("["+n.substring(o+1,n.length-1)+"]")),c.push(i),h.apply(l,c)}}};if("success"===i.result){var p=r.locate||i.locate;if(l.enableForm(!0,!!p),c){var g=l.find('[type="submit"]').first(),m=!1;g.length&&(g.popover({container:"body",trigger:"manual",content:c,tipClass:"popover-in-modal popover-success popover-form-result",placement:i.placement||r.popoverPlacement||"right"}).popover("show"),setTimeout(function(){g.popover("destroy")},r.popoverTime||2e3),m=!0),h.length&&(h.html(''+c+"").show().delay(3e3).fadeOut(100),m=!0),m||a(c,"success")}if(u)return u(i,!0,l);if((r.closeModal||i.closeModal)&&setTimeout(t.zui.closeModal,r.closeModalTime||2e3),f()===!1)return;if(p)if("loadInModal"==p){var v=t(".modal");setTimeout(function(){v.load(v.attr("ref"),function(){t(this).find(".modal-dialog").css("width",t(this).data("width")),t.zui.ajustModalPosition()})},1e3)}else"parent"===p||"top"===p?e[p]&&setTimeout(function(){e[p].location.reload()},1200):"reload"===p?setTimeout(function(){e.location.href=e.location.href},1200):setTimeout(function(){t.tabs?t.tabs.open(p):e.location.href=p},1200);var y=r.ajaxReload||i.ajaxReload;if(y){var b=t(y);b.length&&b.load(e.location.href+" "+y,function(){b.find('[data-toggle="modal"]').modalTrigger()})}}else{if(l.enableForm(),"string"==typeof c)h.length?h.html(''+c+"").show().delay(3e3).fadeOut(100):a(c,"danger");else if("object"==typeof c){var w=!1,x=[];t.each(c,function(e,i){var n=t.isArray(i)?i.join(""):i,o=t("#"+e);if(!o.length)return void x.push(n);var a=e+"Label",s=t("#"+a);if(!s.length){var r=o.closest(".input-group").length,l=o.closest("td").length;s=t('
').appendTo(l?o.closest("td"):r?o.closest(".input-group").parent():o.parent())}s.empty().append(n),o.addClass("has-error");var h=function(){var e=t("#"+a);if(e.length)return e.remove(),o.removeClass("has-error"),!0};o.on("change input mousedown",h);var c=t("#"+e+"_chosen");if(c.length&&c.find(".chosen-single,.chosen-choices").addClass("has-error").on("mousedown",function(){h()===!0&&t(this).removeClass("has-error")}),!w){if(o.hasClass("chosen"))o.trigger("chosen:activate");else if(o.is("textarea")&&o.data("keditor")){var d=o.data("keditor");d.focus(),d.edit.doc.body.focus()}else o.focus();w=!0}}),x.length&&a(x.join(";"),"danger")}if(u)return u(i,!1,l);if(f()===!1)return}}},error:function(t,i,n){if((c&&c(t,i,n,l))!==!1){l.enableForm();var o="timeout"==i||"error"==i?e.lang?e.lang.timeout:i:t.responseText+i+n;a(o,"danger")}}},r),l._ajaxForm(r).data("zui.ajaxform",!0),l.on("click","[data-form-action]",function(){l.attr("action",t(this).data("formAction")).submit()})},t.setAjaxForm=function(e,i,n){t.ajaxForm(e,t.isPlainObject(i)?i:{onFinish:i,beforeSubmit:n})},t.fn.ajaxForm=function(e){return this.each(function(){t.ajaxForm(this,e)})},t.fn.setInputRequired=function(){return this.each(function(){var e=t(this),i=e.parent();i.is(".input-control,td")?i.addClass("required"):e.is(".chosen")?e.attr("required",null).next(".chosen-container").addClass("required"):i.addClass("required"),e.attr("required",null);var n=i.closest(".input-group");n.length&&1===n.find(".required,input[required],select[required]").length&&n.addClass("required")})},t(function(){t('.form-ajax,form[data-type="ajax"]').ajaxForm(),setTimeout(function(){var i=e.config.requiredFields,n=t("form");i&&(i=i.split(",")),i&&i.length&&t.each(i,function(t,e){n.find("#"+e).attr("required","required")}),n.find("input[required],select[required],textarea[required]").setInputRequired()},400),t("#hiddenwin"),t('form[target="hiddenwin"]').on("submit",function(){var e=t(this);e.data("zui.ajaxform")||e.enableForm(!1).data("disabledTime",(new Date).getTime())}).on("click",function(){var e=t(this),i=e.data("disabledTime");i&&(new Date).getTime()-i>1e4&&e.enableForm(!0).data("disabledTime",null)})})}(jQuery,window,void 0),function(t){"use strict";var e="zui.searchList",i=function(t,e){if(t&&t.length)for(var i=0;i
').append(s)),i.$menu.append(s),i.$menu.removeClass("loading"),i.isLoaded=!0,e&&e(!0)},error:function(){i.$menu.removeClass("loading").append('
'+(n.errorText||window.lang&&window.lang.timeout)+"
"),e&&e(!1)}},n.ajax))},n.prototype.scrollTo=function(t){t.length&&t[0].scrollIntoViewIfNeeded&&t[0].scrollIntoViewIfNeeded({behavior:"smooth"})},n.prototype.getItems=function(){return this.$.find(this.options.selector).addClass("search-list-item")},n.prototype.getActiveItem=function(){return this.getItems().filter(".active:first")},n.prototype.search=function(e){var n=this,o=void 0===e||null===e||""===e;n.$.toggleClass("has-search-text",!o);var a=n.getItems().removeClass("active");if(o)a.removeClass("hidden");else{var s=t.trim(e).split(" ");a.each(function(){var e=t(this),n=e.text()+" "+(e.data("key")||e.data("filter"));e.toggleClass("hidden",!i(s,n))})}n.scrollTo(a.not(".hidden").first().addClass("active"))},n.DEFAULTS={selector:".list-group a:not(.not-list-item)",searchBox:".search-box",onSelectItem:null},t.fn.searchList=function(i){return this.each(function(){var o=t(this),a=o.data(e),s="object"==typeof i&&i;a||o.data(e,a=new n(this,s)),"string"==typeof i&&a[i]()})},n.NAME=e,t.fn.searchList.Constructor=n,t(function(){t('[data-ride="searchList"]').searchList()})}(jQuery),function(t){"use strict";var e="zui.labelSelector",i=function(n,o){var a=this;a.name=e,a.$=t(n),o=a.options=t.extend({},i.DEFAULTS,this.$.data(),o),a.$.hide(),a.update()};i.prototype.select=function(t){t+="",this.$wrapper.find(".label.active").removeClass("active"),this.$wrapper.find('.label[data-value="'+t+'"]').addClass("active"),this.$.val(t).trigger("change")},i.prototype.update=function(){var e=this,i=e.options,n=e.$wrapper;if(!n){if(i.wrapper)n=t(i.wrapper);else{var o=e.$.next();n=o.hasClass(".label-selector")?o:t('
')}n.parent().length||e.$.after(n),e.$wrapper=n,n.on("click",".label",function(i){var n=e.$.val(),o=t(this).data("value");e.hasEmptyValue!==!1&&o==n&&(o=e.hasEmptyValue),e.select(o),i.preventDefault()})}n.empty();var a=e.$.val();e.hasEmptyValue=!1,e.$.children("option").each(function(){var e=t(this),o={label:e.text(),value:e.val()},s=""===o.value||"0"===o.value,r=t(i.labelTemplate||'');i.labelClass&&!s&&r.addClass(i.labelClass),i.labelCreator?r=i.labelCreator(r):(r.data("option",o).attr("data-value",o.value),s&&!o.label?r.addClass("empty").append(''):r.text(o.label).toggleClass("active",a===o.value)),n.append(r)})},i.DEFAULTS={},t.fn.labelSelector=function(n){return this.each(function(){var o=t(this),a=o.data(e),s="object"==typeof n&&n;a||o.data(e,a=new i(this,s)),"string"==typeof n&&a[n]()})},i.NAME=e,t.fn.labelSelector.Constructor=i,t(function(){t('[data-provide="labelSelector"]').labelSelector()})}(jQuery),function(t){"use strict";var e="zui.fileInput",i=t.BYTE_UNITS={B:1,KB:1024,MB:1048576,GB:1073741824,TB:1099511627776},n=t.formatBytes=function(t,e,n){return void 0===e&&(e=2),n||(n=ts.fileMaxSize&&(h.val(""),(window.bootbox||window).alert(s.fileSizeError.format(n(s.fileMaxSize)))),r.update()}),r.update()};a.prototype.getFile=function(){var t=this.$input.prop("files");return t&&t[0]},a.prototype.update=function(){var t=this,e=t.$,i=t.getFile(),o=!i;e.toggleClass("normal",!o).toggleClass("empty",o),i?(t.oldName=i.name,e.find(".file-title").text(i.name).attr("title",i.name),e.find(".file-size").text(n(i.size)),e.find(".file-editbox").val(i.name).attr("size",i.name.length),t.options.onSelect&&t.options.onSelect(i,t)):e.find(".file-editbox").val("")},a.DEFAULTS={fileMaxSize:0,fileSizeError:"无法上传大于 {0} 的文件。"},t.fn.fileInput=function(i){return this.each(function(){var n=t(this),o=n.data(e),s="object"==typeof i&&i;o||n.data(e,o=new a(this,s)),"string"==typeof i&&o[i]()})},a.NAME=e,t.fn.fileInput.Constructor=a,t(function(){t('[data-provide="fileInput"]').fileInput()});var s="zui.fileInputList",r=function(e,i){var n=this;n.name=s;var o=n.$=t(e);i=n.options=t.extend({},r.DEFAULTS,this.$.data(),i),n.$template=o.find(".file-input").detach(),n.add()};r.prototype.add=function(){var t=this,e=t.options,i=t.$template.clone();"before"===e.appendWay?t.$.prepend(i):t.$.append(i),i.fileInput({fileMaxSize:e.eachFileMaxSize,fileSizeError:e.fileSizeError,onDelete:function(e){e.$.remove(),t.options.onDelete&&t.options.onDelete(e,t)},onSelect:function(e,i){t.add(),t.options.onSelect&&t.options.onSelect(e,i,t)}})},r.DEFAULTS={fileMaxSize:0,eachFileMaxSize:0,appendWay:"after",fileSizeError:"无法上传大于 {0} 的文件。"},t.fn.fileInputList=function(e){return this.each(function(){var i=t(this),n=i.data(s),o="object"==typeof e&&e;n||i.data(s,n=new r(this,o)),"string"==typeof e&&n[e]()})},r.NAME=s,t.fn.fileInputList.Constructor=r,t(function(){t('[data-provide="fileInputList"]').fileInputList()})}(jQuery),function(t){window.config||(window.config={}),t.createLink=window.createLink=function(e,n,o,a,s,r,l){if("object"==typeof e)return t.createLink(e.moduleName,e.methodName,e.vars,e.viewType,e.isOnlyBody,e.prj,e.hash);if(a||(a=config.defaultView),s||(s=!1),o)for("string"==typeof o&&(o=o.split("&")),i=0;i0?"&":"?")+"PRJ="+r),c+(l?("#"===l[0]?"":"#")+l:"")},t(function(){var e=t("#main,#mainContent,#mainRow,.auto-fade-in");e.length&&e.hasClass("fade")&&setTimeout(function(){e.addClass("in")},e.data("fadeTime")||200)}),t.ajaxSendScore=function(e){t.get(t.createLink("score","ajax","method="+e))};var e=function(t){var e=0;if(t){var i=t.split(":");e+=60*parseInt(i[0]),e+=parseInt(i[1])}return e},n=function(t){t%=1440;var e=Math.floor(t/60),i=t%60;return e<10&&(e="0"+e),i<10&&(i="0"+i),e+":"+i},o=function(t){if("string"==typeof t&&(t=e(t)),"number"==typeof t)if(t<1e5){var i=new Date;i.setHours(Math.floor(t/60)%24),i.setMinutes(t%60),t=i}else t=new Date(t);return t},a=function(t,i){for(var a=i?o(i):new Date,s=a.getHours(),r=10*Math.floor(a.getMinutes()/10)+10,l=0;l<24;++l){var h=(l+s)%24;if(!(h<5))for(var c=0;c<6;++c){var d=n(60*h+10*c+r);t.append('")}}t.val()||(time=e(a.format("hh:mm")),time=time-time%10+10,t.val(n(time)))};t.fn.timeSpanControl=function(i){return this.each(function(){var s=t(this),r=t.extend({},i,s.data()),l=s.find('[name="begin"],.control-time-begin'),h=s.find('[name="end"],.control-time-end'),c=function(){var t=l.val();if(s.find(".hide-empty-begin").toggleClass("hide",!t),t){var i=n(e(t)+30);h.find('option[value="'+i+'"]').length&&h.val(i),r.onChange&&r.onChange(h,i)}};if(s.data("timeSpanControlInit")){if(r.begin){var d=o(r.begin).format("hh:mm");l.find('option[value="'+d+'"]').length&&l.val(d),r.onChange&&r.onChange(l,d)}if(r.end){var u=o(r.end).format("hh:mm");h.find('option[value="'+u+'"]').length&&h.val(u),r.onChange&&r.onChange(h,u)}}else l.on("change",c),a(l,r.begin),a(h,r.end),s.data("timeSpanControlInit",!0);r.end||c()})},t.timeSpanControl={convertTimeToNum:e,convertNumToTime:n,initTimeSelect:a,createTime:o};var s=t.setSearchType=function(e,i){var n=t("#searchType");e||(e=n.val()),e=e||"bug",n.val(e);var o=t("#searchTypeMenu");o.find("li.selected").removeClass("selected");var a=o.find('a[data-value="'+e+'"]'),s=a.text();a.parent().addClass("selected"),t("#searchTypeName").text(s),i||t("#searchInput").focus()};t.gotoObject=function(e,i){if(e||(e=t("#searchType").val()),i||(i=t("#searchInput").val()),i&&e)if(i=i.replace(/[^\d]/g,"")){var n=e.split("-");e=n[0];var o=n.length>1?n[1]:"testsuite"===e?"library":"view",a=t.createLink(e,o,"id="+i);t.apps?t.apps.open(a):window.location.href=a}else{var s={zh_cn:"请输入数字ID进行搜索",zh_tw:"請輸入數值ID行搜索"};alert(lang.searchTip||s[t.zui.clientLang()]||"Please enter a numberic id to search")}t("#searchInput").val(i).focus()},t(function(){s(null,!0),t(document).on("keydown",function(e){e.ctrlKey&&71===e.keyCode&&(t("#searchInput").val("").focus(),e.stopPropagation(),e.preventDefault())})}),t.removeAnchor=window.removeAnchor=function(t){var e=t.lastIndexOf("#");return e>-1?t.substr(0,e):t},t.refreshPage=function(t){t?window.parent.location.reload():window.location.reload()},t.selectLang=window.selectLang=function(e){t.cookie("lang",e,{expires:config.cookieLife,path:config.webRoot}),t.ajaxSendScore("selectLang"),t.refreshPage(1)},t.selectTheme=window.selectTheme=function(e){t.cookie("theme",e,{expires:config.cookieLife,path:config.webRoot}),t.ajaxSendScore("selectTheme"),t.refreshPage(1)},t.zui.Picker&&t.zui.Picker.enableChosen(),t.chosenDefaultOptions={middle_highlight:!0,disable_search_threshold:1,compact_search:!0,allow_single_deselect:!0,placeholder_text_single:" ",placeholder_text_multiple:" ",search_contains:!0,max_drop_width:500,max_drop_height:245,no_wrap:!0,drop_direction:function(){var e=t(this.container).closest(".table-responsive:not(.scroll-none)");if(e.length){if(this.drop_directionFixed)return this.drop_directionFixed;e.css("position","relative");var i="down",n=this.container.find(".chosen-drop"),o=this.container.position(),a=n.outerHeight();return o.top>=a&&o.top+31+a>e.outerHeight()&&(i="up"),this.drop_directionFixed=i,i}return"auto"}},t.chosenSimpleOptions=t.extend({},t.chosenDefaultOptions,{disable_search_threshold:6}),t.fn._chosen=t.fn.chosen,t.fn.chosen=function(e){return"string"==typeof e?this._chosen(e):this.each(function(){var i=t(this).addClass("chosen-controled");return i._chosen(t.extend({},i.hasClass("chosen-simple")?t.chosenSimpleOptions:t.chosenDefaultOptions,i.data(),e))})},t.fn.chosen.Constructor=t.fn._chosen.Constructor,t(function(){t(".chosen,.chosen-simple").each(function(){var e=t(this);e.closest(".template").length||e.chosen()})}),t.extend(t.fn.pager.Constructor.DEFAULTS,{maxNavCount:8,prevIcon:"icon-angle-left",nextIcon:"icon-angle-right",firstIcon:"icon-first-page",lastIcon:"icon-last-page",navEllipsisItem:"…",menuDirection:"dropup",pageSizeOptions:[5,10,15,20,25,30,35,40,45,50,100,200,500,1e3,2e3],elements:["total_text","size_menu","first_icon","prev_icon",'
{page}/{totalPage}
',"next_icon","last_icon"],onPageChange:function(e,i){e.recPerPage!==i.recPerPage&&t.cookie(this.options.pageCookie,e.recPerPage,{expires:config.cookieLife,path:config.webRoot}),e.recPerPage!==i.recPerPage&&(window.location.href=this.createLink())}}),t.extend(!0,t.zui.Messager.DEFAULTS,{cssClass:"messagger-zt",icons:{success:"check-circle",info:"chat-line",warning:"exclamation-sign",danger:"exclamation-sign"}}),t.fn.reverseOrder=function(){return this.each(function(){var e=t(this);e.prependTo(e.parent())})};var r=function(e,i){var n=t(e);if(!n.data("historiesInited")){n.data("historiesInited",1),i=t.extend({},n.data(),i);var o=n.find(".histories-list"),a=!0,s=!1;n.on("click",".btn-reverse",function(){o.children("li").reverseOrder(),a=!a,t(this).find(".icon").toggleClass("icon-arrow-up",a).toggleClass("icon-arrow-down",!a);var e="#lastComment",i=t(e);i.length&&window.KindEditor&&(window.KindEditor.remove(e),i.kindeditor())}).on("click",".btn-expand-all",function(){var e=t(this).find(".icon");s=!s,e.toggleClass("icon-plus",!s).toggleClass("icon-minus",s),o.children("li").toggleClass("show-changes",s)}).on("click",".btn-expand",function(){t(this).closest("li").toggleClass("show-changes")}).on("click",".btn-strip",function(){var e=t(this),n=e.find(".icon"),o=n.hasClass("icon-code");n.toggleClass("icon-code",!o).toggleClass("icon-text",o),e.attr("title",o?i.original:i.textdiff),e.closest("li").toggleClass("show-original",o)}),o.find(".btn-strip").attr("title",i.original);var r=n.find(".modal-comment").modal({show:!1}).on("shown.zui.modal",function(){var t=r.find("#comment");t.length&&(t.focus(),window.editor&&window.editor.comment&&window.editor.comment.focus())}).on("show.zui.modal",function(){var e=r.find("#comment");e.length&&!e.data("keditor")&&t.fn.kindeditor&&e.kindeditor()});n.on("click",".btn-comment",function(t){r.modal("toggle"),t.preventDefault()}).on("click",".btn-edit-comment,.btn-hide-form",function(){t(this).closest("li").toggleClass("show-form")});var l=n.find(".comment-edit-form");l.ajaxForm({success:function(t,e,i,n){setTimeout(function(){l.closest("li").removeClass("show-form")},2e3)}})}};t.fn.histories=function(t){return this.each(function(){r(this,t)})},t(function(){t(".histories").histories()});var l=0,h=0;t.toggleSidebar=function(e){var i=t("#sidebar");if(i.length){var n=t("main");if(void 0===e)e=n.hasClass("hide-sidebar");else if(e&&!n.hasClass("hide-sidebar"))return;n.toggleClass("hide-sidebar",!e),clearTimeout(l),t.zui.store.set(h,e);var o=i.children(".cell"),a={overflow:"visible",maxHeight:"initial"};e?(i.addClass("showing"),l=setTimeout(function(){i.removeClass("showing"),i.trigger("sidebar.toggle",e)},210)):(i.trigger("sidebar.toggle",e),t(window).width()<1900&&(a={overflow:"hidden",maxHeight:t(window).height()-45})),o.css(a)}};var c=t.initSidebar=function(){var e=t("#sidebar");if(e.length){if(e.data("init"))return!0;h="sidebar:"+(e.data("id")||config.currentModule+"/"+config.currentMethod);var i=t("main");if(i.length){i.on("click",".sidebar-toggle",function(){t.toggleSidebar(i.hasClass("hide-sidebar"))});var n=t.zui.store.get(h,e.data("hide")!==!1);n===!1&&e.addClass("no-animate"),t.toggleSidebar(n),n===!1&&setTimeout(function(){e.removeClass("no-animate")},500);var o=e.find(".sidebar-toggle");if(o.length){var a=function(){var e=o[0].getBoundingClientRect(),i=t(window).height(),n=Math.max(0,Math.floor(Math.min(i-40,e.top+e.height)-Math.max(e.top,0))/2)+(e.top<0?0-e.top:0);o.removeClass("fade").find(".icon").css("top",n+(t.zui.browser.isIE()?(i-80)/2:0))};a(),e.data("init",1).on("sidebar.toggle",a);var s=t.zui.browser.isIE()?1500:0,r=0,l=null,c=function(){var t=Date.now();return l&&(clearTimeout(l),l=null),t-rtr input[type="checkbox"]:checked');i.each(function(){var i=parseInt(t(this).val(),10);NaN!==i&&e.push(i)}),t.cookie("checkedItem",e.join(","),{expires:config.cookieLife,path:config.webRoot})},t.extend(t.fn.modal.bs.Constructor.DEFAULTS,{scrollInside:!0,backdrop:"static",headerHeight:100}),t.extend(t.zui.ModalTrigger.DEFAULTS,{scrollInside:!0,backdrop:"static",headerHeight:40}),t.fn.initIframeModal=function(){return this.each(function(){var e=t(this);if(!e.parents('[data-ride="table"],.skip-iframe-modal').length){var i={type:"iframe"};e.hasClass("export")&&t.extend(i,{width:800,shown:setCheckedCookie},e.data()),e.modalTrigger(i)}})},t(function(){t("a.iframe,.export").initIframeModal()});var d=function(){var e,i,n=t(this),o=t.extend({limitSize:40,suffix:"…"},n.data()),a=n.text();if(a.length>o.limitSize){e=a,i=a.substr(0,o.limitSize)+o.suffix,n.text(i).addClass("limit-text-on");var s=o.toggleBtn?t(o.toggleBtn):n.next(".text-limit-toggle");s.text(s.data("textExpand")),s.on("click",function(){var t=n.toggleClass("limit-text-on").hasClass("limit-text-on");n.text(t?i:e),s.text(s.data(t?"textExpand":"textCollapse"))})}else(o.toggleBtn?t(o.toggleBtn):n.next(".text-limit-toggle")).hide()};t.fn.textLimit=function(){return this.each(d)},t(function(){t(".text-limit").textLimit()}),t.fixedTableHead=window.fixedTableHead=function(e,i){var n=t(e);if(n.is("table")||(n=n.find("table")),n.length){var o=t(i||window),a=null,s=function(){var e=n.children("thead"),i=e[0].getBoundingClientRect(),o=n.next(".fixed-head-table");if(i.top<0){var s=e.width();if(o.length){if(a!==s){a=s;var r=o.find("th");e.find("th").each(function(e){r.eq(e).width(t(this).width())})}}else{var o=t("
").addClass(n.attr("class")),l=e.clone(),r=l.find("th"); -e.find("th").each(function(e){r.eq(e).width(t(this).width())}),o.append(l).insertAfter(n)}o.css({left:i.left,width:i.width}).show()}else o.hide()};o.on("scroll",s).on("resize",s),s()}},t(document).on("click","tr[data-url]",function(){var e=t(this),i=e.data("href")||e.data("url");i&&(window.location.href=i)}),"yes"===config.onlybody&&self===parent&&(window.location.href=window.location.href.replace("?onlybody=yes","").replace("&onlybody=yes","")),t(function(){t("body").addClass("m-{currentModule}-{currentMethod}".format(config))});var u,f,p,g,m,v=function(){u||(u=t("#subNavbar"),f=t("#pageNav"),p=t("#pageActions"),g=u.children(".nav"),m=g.outerWidth());var e=u.outerWidth(),i=f.outerWidth()||0,n=p.outerWidth()||0;if(i=i?i+15:0,n=n?n+15:0,!i&&!n)return void g.css({maxWidth:null,left:null,position:"static"});var o=Math.max(300,e-i-n),a=Math.min(o,m),s=(e-a)/2,r=i&&s.btn-toolbar");if(e.length){var i,n,o=e.children(),a=o.length,s=!1,r=null;if(a)for(o.each(function(e){i=t(this),n=i.is(".divider"),n&&!r&&i.hide(),s||n||(s=!0),r=n?null:i,!n||e!==a-1&&0!==e||i.hide()});i.length&&i.is(".divider");)i=i.hide().prev();s||e.hide()}};t(function(){t(".input-group,.btn-group").fixInputGroup(),k()}),window.holders&&t.each(window.holders,function(e){var i=t("#"+e);i.length&&i.is("input")&&i.attr("placeholder",window.holders[e])}),t(function(){var e=t(".table-responsive"),i=t.fixTableResponsive=function(){e.each(function(){this.scrollHeight-3<=this.clientHeight&&this.scrollWidth-3<=this.clientWidth?t(this).addClass("scroll-none").css("overflow","visible"):t(this).removeClass("scroll-none").css("overflow","auto")})};e.length&&(t(window).on("resize",i),setTimeout(i,100))});var T=function(){var e=this,i=t(e),n=i.closest("tr").find("textarea");if(n.length){var o=32;n.each(function(){var e=t(this).closest("td"),i=e.css("height");e.css("height",this.style.height),this.style.height="auto";var n=this.value?this.scrollHeight+2:32;o=Math.max(o,n),e.css("height",i)}),n.css("height",o)}else{e.style.height="auto";var a=e.value?e.scrollHeight+2:32;e.style.height=a+"px"}};t.autoResizeTextarea=function(e){t(e).each(T)},t(function(){t("textarea.autosize").each(T),t(document).on("input paste change","textarea.autosize",T)}),t(function(){var e=t("#dropMenu,.drop-menu");e.length&&e.on("click",".toggle-right-col",function(e){var i=t(this).closest("#dropMenu,.drop-menu").toggleClass("show-right-col");i.find(".search-box").width(i.hasClass("show-right-col")?i.find(".col-left").width()-20:"auto"),e.stopPropagation(),e.preventDefault()})});var S="undefined"!=typeof InstallTrigger;t.zui.browser.firefox=S,t("html").toggleClass("is-firefox",S).toggleClass("not-firefox",!S),t(function(){var e=t("#mainContent>.main-col"),i=e.children(".main-actions");if(i.length){var n=i.prev();if(i.length&&n.length){t('
').css("height",i.outerHeight()).insertAfter(i);var o=0,a=function(){var e=n[0].getBoundingClientRect(),s=e.top+e.height+120>t(window).height();if(t("body").toggleClass("main-actions-fixed",s),s){const r=n.width();r?i.width(r):o<10&&setTimeout(a,1e3)}o++};t.resetToolbarPosition=a,a(),t(window).on("resize scroll",a)}}}),t(document).on("show.zui.modal",function(e){t("body.body-modal").length&&window.parent&&window.parent!==window&&t(e.target).is(".modal")&&window.parent.$("body").addClass("hide-modal-close")}).on("hidden.zui.modal",function(){t("body.body-modal").length&&window.parent&&window.parent!==window&&window.parent.$("body").removeClass("hide-modal-close")}),t(function(){var e=t(".dropdown-menu.with-search");e.length&&(e.find(".menu-search").on("click",function(t){return t.stopPropagation(),!1}),e.on("keyup change paste","input",function(){var e=t(this),i=e.closest(".dropdown-menu.with-search"),n=e.val().toLowerCase(),o=i.find(".option");""==n?o.removeClass("hide"):o.each(function(){var e=t(this);e.toggleClass("hide",e.text().toString().toLowerCase().indexOf(n)<0&&e.data("key").toString().toLowerCase().indexOf(n)<0)})}),e.parents(".dropdown-submenu").one("mouseenter",function(){var e=t(this).find(".dropdown-list")[0];e&&e.getBoundingClientRect&&setTimeout(function(){var t=270,i=e.getBoundingClientRect();i.top<0&&(t=Math.min(270,i.height)+i.top),e.style.maxHeight=Math.min(270,t)+"px"},50)})),t(".dropdown-menu.with-search .menu-search").on("click",function(t){return t.stopPropagation(),!1})})}(jQuery),function(t){function e(){var e=window.parent,i=config.currentModule,n=config.currentMethod,o="index"===i&&"index"===n,a="#_single"===location.hash||o||!t("#mainHeader,#editorNav").length||"tutorial"===i||"install"===i||"upgrade"===i||"user"===i&&("login"===n||"deny"===n)||"my"===i&&"changepassword"===n||t("body").hasClass("allow-self-open"),s=location.href;if(e===window&&!a){var r=location.pathname+location.search+location.hash,l="";return location.href=t.createLink("index","index",l)+"#app="+encodeURIComponent(r)}if(e!==window&&e.$.apps){o&&e.location.reload();var h=window.name;if(0===h.indexOf("app-")){t.apps=window.apps=e.$.apps;var c=h.substr(4);t.appCode=c,t(document).on("click",function(t){var i=e.document.getElementById(window.name);if(i){var n=e.document.getElementById(i.name)||i;n&&n.dispatchEvent(new Event(t.type,{bubbles:!0}))}}).on("click","a,.open-in-app,.show-in-app",function(e){var i=t(this);if(!i.is("[data-modal],[data-toggle],[data-ride],[data-tab],.iframe,.not-in-app,[target]")&&!i.data("zui.modaltrigger")){var n=i.hasClass("show-in-app")?"":i.attr("href")||(i.is("a")?"":i.data("url")),o=i.data("app")||i.data("group");if(n){if(0===n.indexOf("javascript:")||"#"===n[0])return;var a=t.parseLink(n);if("index"===a.moduleName&&"index"===a.methodName)return window.location.reload(),void e.preventDefault()}else if(!o)return;o||(o=t.apps.getAppCode(n)),o&&("help"===o&&(t.apps.appsMap.help.text=i.text()),t.apps.open(n,o)&&e.preventDefault())}}),t.apps.updateUrl(c,s,document.title)}}}function i(){var e=t("#navbar>.nav");if(e.length){var i=t("#heading"),n=+i.css("left").replace("px",""),o=i.outerWidth(),a=e.width(),s=t("#mainHeader>.container").width()-2*n,r=Math.floor((s-a)/2);e.css("marginLeft",r>>0;if(0===o)return!1;for(var a=0|e,s=Math.max(a>=0?a:o-Math.abs(a),0);s1&&("?"===e[0]&&(e=e.substr(1)),e.split("&").forEach(function(t){var e=t.split("=",2);if(e.length>1)try{i[e[0]]=decodeURIComponent(e[1])}catch(n){i[e[0]]=""}else i[e[0]]=""})),t?i[t]:i},t.parseLink=function(e){if(!e)return{};var i=e.split("#"),n=i[0].split("?"),o=n[1],a=o?t.getSearchParam("",o):{},s=n[0],r={isOnlyBody:"yes"===a.onlybody,vars:[],hash:i[1]||"",params:a};if("GET"===config.requestType){r.moduleName=a[config.moduleVar]||"index",r.methodName=a[config.methodVar]||"index",r.viewType=a[config.viewVar]||config.defaultView;for(var l in a)l!==config.moduleVar&&l!==config.methodVar&&l!==config.viewVar&&"onlybody"!==l&&r.vars.push([l,a[l]])}else{var h=s.lastIndexOf("/");h===s.length-1&&(s=s.substr(0,h),h=s.lastIndexOf("/")),h>=0&&(s=s.substr(h+1));var c=s.lastIndexOf(".");c>=0?(r.viewType=s.substr(c+1),s=s.substr(0,c)):r.viewType=config.defaultView;var d=s.split(config.requestFix);if(r.moduleName=d[0]||"index",r.methodName=d[1]||"index",d.length>2)for(var u=2;u.nav>li").length>10&&(i(),t(window).on("resize",i)),setTimeout(n,1e3)})}(jQuery),$.zui.lang("de",{"zui.pager":{pageOfText:"Seite {0}",prev:"Zurück",next:"Nächste Seite",first:"Erste Seite",last:"Letzte Seite","goto":"Goto",pageOf:"Seite {page}",totalPage:"{totalPage} Seiten",totalCount:"Total: {recTotal} Artikel",pageSize:"{recPerPage} Artikel pro Seite",itemsRange:"Seiten {start} bis {end}",pageOfTotal:"Seite {page}/{totalPage}"},"zui.boards":{append2end:"Gehen Sie zum Ende"},"zui.browser":{tip:"Online. Sorgenfrei. Aktualisiere deinen Browser noch heute!"},"zui.calendar":{weekNames:["Son","Mon","Die","Mit","Don","Fri","Sam"],monthNames:["Jan","Feb","Mär","Apr","Mai","Jun","Jul","Aug","Sep","Okt","Nov","Dez"],today:"Heute",year:"{0}Jahr",month:"{0}Monat",yearMonth:"{0}-{1}"},"zui.chosenIcons":{emptyIcon:"[Kein Icon]",commonIcons:"Gemeinsame Symbole",webIcons:"Web-Symbol",editorIcons:"Editor-Symbol",directionalIcons:"Pfeil Zusammenfluss",otherIcons:"Andere Symbole"},"zui.colorPicker":{errorTip:"Kein gültiger Farbwert"},"zui.datagrid":{errorCannotGetDataFromRemote:"Daten vom Remote-Server ({0}) können nicht abgerufen werden.",errorCannotHandleRemoteData:"Die vom Remote-Server zurückgegebenen Daten können nicht verarbeitet werden."},"zui.guideViewer":{prevStep:"Vorheriger Schritt",nextStep:"Nächster Schritt"},"zui.tabs":{reload:"Neu laden",close:"Schliessen",closeOthers:"Schließen Sie andere Registerkarten",closeRight:"Schließen Sie die rechte Registerkarte",reopenLast:"Letzten geschlossenen Tab wiederherstellen",errorCannotFetchFromRemote:"Inhalt kann nicht vom Remote-Server abgerufen werden ({0})."},"zui.uploader":{},datetimepicker:{days:["Sonntag","Montag","Diensteg","Mittwoch","Donnerstag","Freitag","Samstag"],daysShort:["Son","Mon","Die","Mit","Don","Fri","Sam"],daysMin:["Son","Mon","Die","Mit","Don","Fri","Sam"],months:["Jan","Feb","Mär","Apr","Mai","Jun","Jul","Aug","Sep","Okt","Nov","Dez"],monthsShort:["Jan","Feb","Mär","Apr","Mai","Jun","Jul","Aug","Sep","Okt","Nov","Dez"],today:"Heute",suffix:[],meridiem:[]},chosen:{no_results_text:"Nicht gefunden"},bootbox:{OK:"OK",CANCEL:"Stornieren",CONFIRM:"Bestätigen"}}),$.zui.lang("fr",{"zui.pager":{pageOfText:"Page {0}",prev:"Prev",next:"Suivant",first:"First",last:"Last","goto":"Goto",pageOf:"Page {page}",totalPage:"{totalPage} pages",totalCount:"Total: {recTotal} items",pageSize:"{recPerPage} per page",itemsRange:"De {start} à {end}",pageOfTotal:"Page {page} de {totalPage}"},"zui.boards":{append2end:"Aller jusqu'au bout"},"zui.browser":{tip:"Naviguez sans crainte sur Internet. Mettez votre navigateur à jour dès aujourd'hui!"},"zui.calendar":{weekNames:["Dim","Lun","Mar","Mer","Jeu","Ven","Sam"],monthNames:["Jan","Fév","Mar","Avr","Mai","Juin","Juil","Août","Sep","Oct","Nov","Déc"],today:"Aujourd'hui",year:"{0} Année",month:"{0} Mois",yearMonth:"{0}-{1}"},"zui.chosenIcons":{emptyIcon:"[Aucune icône]",commonIcons:"Icônes communes",webIcons:"Icône Web",editorIcons:"Icône de l'éditeur",directionalIcons:"Flèche confluence",otherIcons:"Autres icônes"},"zui.colorPicker":{errorTip:"Pas une valeur de couleur valide"},"zui.datagrid":{errorCannotGetDataFromRemote:"Impossible d'obtenir les données du serveur distant ({0}).",errorCannotHandleRemoteData:"Impossible de traiter les données renvoyées par le serveur distant."},"zui.guideViewer":{prevStep:"Étape précédente",nextStep:"Prochaine étape"},"zui.tabs":{reload:"Recharger",close:"Fermer",closeOthers:"Fermez les autres onglets",closeRight:"Fermer l'onglet de droite",reopenLast:"Restaurer le dernier onglet fermé",errorCannotFetchFromRemote:"Impossible d'obtenir le contenu du serveur distant ({0})."},"zui.uploader":{},datetimepicker:{days:["Dimanche","Lundi","Mardi","Mercredi","Jeudi","Vendredi","Samedi"],daysShort:["Dim","Lun","Mar","Mer","Jeu","Ven","Sam"],daysMin:["Dim","Lun","Mar","Mer","Jeu","Ven","Sam"],months:["Jan","Fév","Mar","Avr","Mai","Juin","Juil","Août","Sep","Oct","Nov","Déc"],monthsShort:["Jan","Fév","Mar","Avr","Mai","Juin","Juil","Août","Sep","Oct","Nov","Déc"],today:"Aujourd'hui",suffix:[],meridiem:[]},chosen:{no_results_text:"Pas trouvé"},bootbox:{OK:"D'accord",CANCEL:"Annuler",CONFIRM:"Confirmer"}}); \ No newline at end of file +o.not(".group-summary").toggleClass("hidden",!s),a.toggleClass("hidden",s),t("body").toggleClass("table-group-collapsed",!this.$.find("tbody>tr.group-summary.hidden").length)},r.prototype.updateStatistic=function(){var i=this,n=i.$.find(".table-statistic");if(n.length){if(i.defaultStatistic===e&&(i.defaultStatistic=n.html()),i.options.statisticCreator)return void n.html(i.options.statisticCreator(i)||i.defaultStatistic);var o=i.statisticCols;if(!o&&o!==!1){o={};var a=!1;i.getTable().find("thead th").each(function(e){var i=t(this),n=i.data("statistic");n&&(a=!0,o[e]={format:n,name:i.text()})}),i.statisticCols=!!a&&o}var s=0;o&&t.each(o,function(t){o[t].total=0,o[t].checkedTotal=0}),i.$.find(i.isDataTable?".fixed-left tbody>tr":"tbody>tr").each(function(){var e=t(this),i=e.hasClass("checked"),n=e.children("td");i&&s++,o&&t.each(o,function(t){var e=parseFloat(n.eq(t).text());isNaN(e)&&(e=0),o[t].total+=e,i&&(o[t].checkedTotal+=e)})});var r=[];if(s)r.push(i.lang.selectedItems.format(s));else if(i.defaultStatistic)return void n.html(i.defaultStatistic);o&&t.each(o,function(t){var e=o[t],n=e[s?"checkedTotal":"total"];e.format&&(n=e.format.format(n)),r.push(i.lang.attrTotal.format(e.name,n))}),n.html(r.join(", "))}},r.prototype.updateFixUI=function(e){var i=this,n=(new Date).getTime();if(!e&&(i.lastUpdateCall&&clearTimeout(i.lastUpdateCall),!i.lastUpdateTime||n-i.lastUpdateTime