Merge branch 'master' into 'zentaopms_289'

# Conflicts:
#   db/update18.2.sql
This commit is contained in:
王怡栋
2023-03-06 03:26:34 +00:00
49 changed files with 355 additions and 178 deletions
+1
View File
@@ -1,2 +1,3 @@
ALTER TABLE `zt_lang` MODIFY COLUMN `section` varchar(50) NOT NULL;
DELETE FROM `zt_grouppriv` WHERE `module` = 'dev' AND `method` = 'editor';
REPLACE INTO `zt_config` (`owner`, `module`, `section`, `key`, `value`) VALUES ('system','common','','setPercent','1');
@@ -36,7 +36,7 @@
}
unset($visibleFields['module']);
?>
<form method='post' class='load-indicator main-form' enctype='multipart/form-data' target='hiddenwin' id="batchCreateForm">
<form method='post' class='load-indicator main-form form-ajax' enctype='multipart/form-data' id="batchCreateForm">
<div class="table-responsive">
<table class="table table-form">
<thead>
+1 -1
View File
@@ -10,7 +10,7 @@ if(!isset($config->safe->weak)) $config->safe->weak = '123456,password,12345,123
$config->admin->menuGroup['system'] = array('custom|mode', 'backup', 'cron', 'action|trash', 'admin|xuanxuan', 'setting|xuanxuan', 'admin|license', 'admin|checkweak', 'admin|resetpwdsetting', 'admin|safe', 'custom|timezone', 'search|buildindex', 'admin|tableengine', 'ldap', 'custom|libreoffice', 'conference', 'client');
$config->admin->menuGroup['company'] = array('dept', 'company', 'user', 'group', 'tutorial');
$config->admin->menuGroup['switch'] = array('admin|setmodule');
$config->admin->menuGroup['model'] = array('auditcl', 'stage', 'design', 'cmcl', 'reviewcl', 'custom|required', 'custom|set', 'custom|flow', 'custom|code', 'custom|estimate', 'custom|hours', 'subject', 'process', 'activity', 'zoutput', 'classify', 'holiday', 'reviewsetting');
$config->admin->menuGroup['model'] = array('auditcl', 'stage', 'design', 'cmcl', 'reviewcl', 'custom|required', 'custom|set', 'custom|flow', 'custom|code', 'custom|percent','custom|estimate', 'custom|hours', 'subject', 'process', 'activity', 'zoutput', 'classify', 'holiday', 'reviewsetting');
$config->admin->menuGroup['feature'] = array('custom|set', 'custom|product', 'custom|execution', 'custom|required', 'custom|kanban', 'approvalflow', 'measurement', 'meetingroom', 'custom|browsestoryconcept', 'custom|kanban', 'sqlbuilder', 'report');
$config->admin->menuGroup['template'] = array('custom|set', 'baseline');
$config->admin->menuGroup['message'] = array('mail', 'webhook', 'sms', 'message');
+3 -1
View File
@@ -89,15 +89,17 @@ if(helper::hasFeature('waterfall') or helper::hasFeature('waterfallplus')) $lang
$lang->admin->menuList->model['tabMenu']['common']['build'] = array('link' => "{$lang->build->common}|custom|required|module=build", 'alias' => 'set', 'exclude' => 'custom');
$lang->admin->menuList->model['tabMenu']['common']['flow'] = array('link' => "{$lang->custom->flow}|custom|flow|", 'divider' => true);
$lang->admin->menuList->model['tabMenu']['common']['code'] = array('link' => "{$lang->code}|custom|code|");
$lang->admin->menuList->model['tabMenu']['common']['percent'] = array('link' => "{$lang->stage->percent}|custom|percent|");
$lang->admin->menuList->model['tabMenu']['common']['hours'] = array('link' => "{$lang->workingHour}|custom|hours|", 'subModule' => 'holiday', 'links' => array('holiday|browse|', 'custom|hours|'));
if(helper::hasFeature('waterfall')) $lang->admin->menuList->model['tabMenu']['waterfall']['stage'] = array('link' => "{$lang->stage->list}|stage|browse|", 'subModule' => 'stage', 'exclude' => 'stage-plusbrowse');
if(helper::hasFeature('waterfallplus')) $lang->admin->menuList->model['tabMenu']['waterfallplus']['stage'] = array('link' => "{$lang->stage->list}|stage|plusbrowse|", 'subModule' => 'stage', 'exclude' => 'stage-browse');
$lang->admin->menuList->model['tabMenu']['menuOrder']['common']['5'] = 'project';
if(helper::hasFeature('waterfall') or helper::hasFeature('waterfallplus')) $lang->admin->menuList->model['tabMenu']['menuOrder']['common']['7'] = 'stage';
$lang->admin->menuList->model['tabMenu']['menuOrder']['common']['10'] = 'build';
$lang->admin->menuList->model['tabMenu']['menuOrder']['common']['35'] = 'flow';
$lang->admin->menuList->model['tabMenu']['menuOrder']['common']['40'] = 'code';
$lang->admin->menuList->model['tabMenu']['menuOrder']['common']['43'] = 'percent';
$lang->admin->menuList->model['tabMenu']['menuOrder']['common']['45'] = 'hours';
if(helper::hasFeature('waterfall')) $lang->admin->menuList->model['tabMenu']['menuOrder']['waterfall']['5'] = 'stage';
if(helper::hasFeature('waterfallplus')) $lang->admin->menuList->model['tabMenu']['menuOrder']['waterfallplus']['5'] = 'stage';
@@ -205,7 +205,7 @@ $(function()
<div class='col-7'>
<div class='progress progress-text-left'>
<div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="<?php echo $project->executions[0]->hours->progress;?>" aria-valuemin="0" aria-valuemax="100" style="width: <?php echo $project->executions[0]->hours->progress;?>%">
<span class='progress-text'><?php echo $project->executions[0]->hours->progress . '%';?></span>
<span class='progress-text'><?php echo !empty($project->executions[0]->hours->progress) ? $project->executions[0]->hours->progress . '%' : '0%';?></span>
</div>
</div>
</div>
+4 -1
View File
@@ -1165,6 +1165,9 @@ class bug extends control
$productBugs = $this->bug->getProductBugPairs($productID, $branch);
unset($productBugs[$bugID]);
$executions = array(0 => '') + $this->product->getExecutionPairsByProduct($bug->product, $bug->branch, 'id_desc', $bug->project);
if(!empty($bug->execution) and empty($executions[$bug->execution])) $executions[$execution->id] = $execution->name;
if($product->shadow) $this->view->project = $this->loadModel('project')->getByShadowProduct($bug->product);
$this->view->bug = $bug;
@@ -1177,7 +1180,7 @@ class bug extends control
$this->view->projectExecutionPairs = $this->loadModel('project')->getProjectExecutionPairs();
$this->view->moduleOptionMenu = $moduleOptionMenu;
$this->view->currentModuleID = $currentModuleID;
$this->view->executions = array(0 => '') + $this->product->getExecutionPairsByProduct($bug->product, $bug->branch, 'id_desc', $bug->project);
$this->view->executions = $executions;
$this->view->stories = $bug->execution ? $this->story->getExecutionStoryPairs($bug->execution) : $this->story->getProductStoryPairs($bug->product, $bug->branch, 0, 'all', 'id_desc', 0, 'full', 'story', false);
$this->view->branchOption = $branchOption;
$this->view->branchTagOption = $branchTagOption;
+7 -5
View File
@@ -66,6 +66,7 @@ class build extends control
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $this->createLink('build', 'view', "buildID=$buildID") . "#app={$this->app->tab}"));
}
$status = empty($this->config->CRProduct) ? 'noclosed' : '';
$this->loadModel('product');
$this->loadModel('project');
/* Set menu. */
@@ -74,7 +75,7 @@ class build extends control
$this->project->setMenu($projectID);
$executions = $this->execution->getPairs($projectID, 'all', 'stagefilter|leaf|order_asc');
$executionID = empty($executionID) ? key($executions) : $executionID;
$productGroups = $executionID ? $this->product->getProducts($executionID) : array();
$productGroups = $executionID ? $this->product->getProducts($executionID, $status) : array();
$branchGroups = $executionID ? $this->project->getBranchesByProject($executionID) : array();
$this->session->set('project', $projectID);
}
@@ -83,7 +84,7 @@ class build extends control
$execution = $this->execution->getByID($executionID);
$executions = $this->execution->getPairs($execution->project, 'all', 'stagefilter|leaf|order_asc');
$projectID = $execution->project;
$productGroups = $this->product->getProducts($executionID);
$productGroups = $this->product->getProducts($executionID, $status);
$branchGroups = $this->project->getBranchesByProject($executionID);
$this->execution->setMenu($executionID);
$this->session->set('project', $execution->project);
@@ -93,7 +94,7 @@ class build extends control
$execution = $this->execution->getByID($executionID);
$projectID = $execution ? $execution->project : 0;
$executions = $this->execution->getPairs($projectID, 'all', 'stagefilter|leaf');
$productGroups = $this->product->getProducts($executionID);
$productGroups = $this->product->getProducts($executionID, $status);
$branchGroups = $this->project->getBranchesByProject($executionID);
}
@@ -171,14 +172,15 @@ class build extends control
if($this->app->tab == 'execution') $this->execution->setMenu($build->execution);
$builds = array();
$status = empty($this->config->CRProduct) ? 'noclosed' : '';
if($build->execution)
{
$productGroups = $this->product->getProducts($build->execution);
$productGroups = $this->product->getProducts($build->execution, $status);
$branches = $this->loadModel('branch')->getList($build->product, $build->execution, 'all');
}
else
{
$productGroups = $this->product->getProducts($build->project);
$productGroups = $this->product->getProducts($build->project, $status);
$branches = $this->loadModel('branch')->getList($build->product, $build->project, 'all');
$builds = $this->build->getBuildPairs($build->product, 'all', 'noempty,notrunk,singled,separate', $build->project, 'project', $build->builds, false);
}
+1
View File
@@ -207,6 +207,7 @@ $lang->design->ADS = 'ADS';
$lang->stage->common = 'Stage';
$lang->stage->type = 'Stage Type';
$lang->stage->list = 'Stage List';
$lang->stage->percent = 'Workload Ratio';
$lang->execution->list = "{$lang->executionCommon} List";
$lang->kanban->common = 'Kanban';
$lang->backup->common = 'Backup';
+1
View File
@@ -207,6 +207,7 @@ $lang->design->ADS = 'Interface Design';
$lang->stage->common = 'Stage';
$lang->stage->type = 'Stage Type';
$lang->stage->list = 'Stage List';
$lang->stage->percent = 'Workload Ratio';
$lang->execution->list = "{$lang->executionCommon} List";
$lang->kanban->common = 'Kanban';
$lang->backup->common = 'Backup';
+1
View File
@@ -207,6 +207,7 @@ $lang->design->ADS = 'ADS';
$lang->stage->common = 'Stage';
$lang->stage->type = 'Stage Type';
$lang->stage->list = 'Stage List';
$lang->stage->percent = 'Workload Ratio';
$lang->execution->list = "{$lang->executionCommon} List";
$lang->kanban->common = 'Kanban';
$lang->backup->common = 'Backup';
+1
View File
@@ -207,6 +207,7 @@ $lang->design->ADS = '接口设计';
$lang->stage->common = '阶段';
$lang->stage->type = '阶段类型';
$lang->stage->list = '阶段列表';
$lang->stage->percent = '工作量占比';
$lang->execution->list = "{$lang->executionCommon}列表";
$lang->kanban->common = '看板';
$lang->backup->common = '备份';
+2 -2
View File
@@ -43,8 +43,8 @@ $config->custom->fieldList['project']['create'] = 'budget,PM,desc';
$config->custom->fieldList['project']['edit'] = 'budget,PM,desc';
$config->custom->fieldList['product']['create'] = 'PO,QD,RD,type,desc';
$config->custom->fieldList['product']['edit'] = 'PO,QD,RD,type,desc,status';
$config->custom->fieldList['story']['create'] = 'module,plan,source,pri,estimate,keywords,spec';
$config->custom->fieldList['story']['change'] = 'comment,spec';
$config->custom->fieldList['story']['create'] = 'module,plan,source,pri,estimate,keywords,spec,verify';
$config->custom->fieldList['story']['change'] = 'comment,spec,verify';
$config->custom->fieldList['story']['close'] = 'comment';
$config->custom->fieldList['story']['review'] = 'reviewedDate,comment';
$config->custom->fieldList['productplan'] = 'begin,end,desc';
+19
View File
@@ -862,6 +862,25 @@ class custom extends control
$this->display();
}
/**
* Set stage percent.
*
* @access public
* @return void
*/
public function percent()
{
if($_POST)
{
$this->loadModel('setting')->setItem('system.common.setPercent', $this->post->percent);
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => 'reload'));
}
$this->view->title = $this->lang->stage->percent;
$this->display();
}
/**
* Set hours and weekend
*
+3
View File
@@ -0,0 +1,3 @@
.table-form>tbody>tr>th.c-setPercent {width: 135px;}
#readOnlyOfPercent {font-size: 13px; color: #5e626d; display: flex; width: 200%;}
#readOnlyOfPercent i {font-size: 14px; color: #0075ff; margin-right: 3px;}
+4 -1
View File
@@ -59,6 +59,8 @@ $lang->custom->executionCommon = 'Execution';
$lang->custom->selectDefaultProgram = 'Please select default program';
$lang->custom->defaultProgram = 'Default program';
$lang->custom->modeManagement = 'Mode Management';
$lang->custom->percent = $lang->stage->percent;
$lang->custom->setPercent = "Enable or Disable {$lang->stage->percent}";
$lang->custom->unitList['efficiency'] = 'Working Hours/';
$lang->custom->unitList['manhour'] = 'Man-hour/';
@@ -188,7 +190,7 @@ $lang->custom->notice->noClosedBlock = 'You have no blocks that are closed
$lang->custom->notice->required = 'The selected field is required.';
$lang->custom->notice->conceptResult = 'According to your preference, <b> %s-%s </b> is set for you. Use <b>%s</b> + <b> %s</b>。';
$lang->custom->notice->conceptPath = 'Go to Admin -> Custom -> Concept to set it.';
$lang->custom->notice->readOnlyOfProduct = 'If Change Forbidden, any change on stories, bugs, cases, efforts, releases and plans of the closed product is also forbidden.';
$lang->custom->notice->readOnlyOfProduct = 'If Change Forbidden, any change on stories, bugs, cases, efforts, releases, plans and builds of the closed product is also forbidden.';
$lang->custom->notice->readOnlyOfExecution = "If Change Forbidden, any change on tasks, builds, efforts and stories of the closed {$lang->executionCommon} is also forbidden.";
$lang->custom->notice->readOnlyOfKanban = "If Change Forbidden, any change on kanban card and related operations of {$lang->custom->kanban} is also forbidden.";
$lang->custom->notice->URSREmpty = 'Custom requirement name can not be empty!';
@@ -199,6 +201,7 @@ $lang->custom->notice->storyReviewTip = 'After selecting by individual, pos
$lang->custom->notice->selectAllTip = 'After selecting all people, the reviewers will be emptied and grayed out while hiding their positions and departments.';
$lang->custom->notice->repeatKey = 'Repeat Key %s';
$lang->custom->notice->readOnlyOfCode = 'A code is a management term that exists for secrecy or as an antonym. When code management is enabled, the code information of product, project, and execution in the system will be displayed in the creation, editing, detail, and list pages.';
$lang->custom->notice->readOnlyOfPercent = 'The "Workload Ratio" is used to divide the workload of a project into different stages. The sum of the percentages of the same level stages cannot exceed 100%. After enabling the "Workload Ratio", users have to fill in the ratio fields when setting up the stages in the Waterfall project and Waterfall Plus project management models.';
$lang->custom->notice->indexPage['product'] = "ZenTao 8.2+ has Product Homepage. Do you want to go to Product Homepage?";
$lang->custom->notice->indexPage['project'] = "ZenTao 8.2+ has Project Homepage. Do you want to go to Project Homepage?";
+4 -1
View File
@@ -59,6 +59,8 @@ $lang->custom->executionCommon = 'Execution';
$lang->custom->selectDefaultProgram = 'Please select default program';
$lang->custom->defaultProgram = 'Default program';
$lang->custom->modeManagement = 'Mode Management';
$lang->custom->percent = $lang->stage->percent;
$lang->custom->setPercent = "Enable or Disable {$lang->stage->percent}";
$lang->custom->unitList['efficiency'] = 'Working Hours/';
$lang->custom->unitList['manhour'] = 'Man-hour/';
@@ -188,7 +190,7 @@ $lang->custom->notice->noClosedBlock = 'You have no blocks that are closed
$lang->custom->notice->required = 'The selected field is required.';
$lang->custom->notice->conceptResult = 'According to your preference, <b> %s-%s </b> is set for you. Use <b>%s</b> + <b> %s</b>.';
$lang->custom->notice->conceptPath = 'Go to Admin -> Custom -> Concept to set it.';
$lang->custom->notice->readOnlyOfProduct = 'If Change Forbidden, any change on stories, bugs, cases, efforts, releases and plans of the closed product is also forbidden.';
$lang->custom->notice->readOnlyOfProduct = 'If Change Forbidden, any change on stories, bugs, cases, efforts, releases, plans and builds of the closed product is also forbidden.';
$lang->custom->notice->readOnlyOfExecution = "If Change Forbidden, any change on tasks, builds, efforts and stories of the closed {$lang->executionCommon} is also forbidden.";
$lang->custom->notice->readOnlyOfKanban = "If Change Forbidden, any change on kanban card and related operations of {$lang->custom->kanban} is also forbidden.";
$lang->custom->notice->URSREmpty = 'Custom requirement name can not be empty!';
@@ -199,6 +201,7 @@ $lang->custom->notice->storyReviewTip = 'After selecting by individual, pos
$lang->custom->notice->selectAllTip = 'After selecting all people, the reviewers will be emptied and grayed out while hiding their positions and departments.';
$lang->custom->notice->repeatKey = 'Repeat Key %s';
$lang->custom->notice->readOnlyOfCode = 'A code is a management term that exists for secrecy or as an antonym. When code management is enabled, the code information of product, project, and execution in the system will be displayed in the creation, editing, detail, and list pages.';
$lang->custom->notice->readOnlyOfPercent = 'The "Workload Ratio" is used to divide the workload of a project into different stages. The sum of the percentages of the same level stages cannot exceed 100%. After enabling the "Workload Ratio", users have to fill in the ratio fields when setting up the stages in the Waterfall project and Waterfall Plus project management models.';
$lang->custom->notice->indexPage['product'] = "ZenTao 8.2+ has Product Home. Do you want to go to Product Home?";
$lang->custom->notice->indexPage['project'] = "ZenTao 8.2+ has Project Home. Do you want to go to Project Home?";
+4 -1
View File
@@ -59,6 +59,8 @@ $lang->custom->executionCommon = 'Execution';
$lang->custom->selectDefaultProgram = 'Please select default program';
$lang->custom->defaultProgram = 'Default program';
$lang->custom->modeManagement = 'Mode Management';
$lang->custom->percent = $lang->stage->percent;
$lang->custom->setPercent = "Enable or Disable {$lang->stage->percent}";
$lang->custom->unitList['efficiency'] = 'Working Hours/';
$lang->custom->unitList['manhour'] = 'Man-hour/';
@@ -188,7 +190,7 @@ $lang->custom->notice->noClosedBlock = "Vous n'avez aucun bloc fermé déf
$lang->custom->notice->required = 'Le champ sélectionné est obligatoire.';
$lang->custom->notice->conceptResult = 'Selon votre préférence, <b> %s-%s </b> peut être fixé pour vous. Utilisez <b>%s</b> + <b> %s</b>。';
$lang->custom->notice->conceptPath = 'Allez à Admin -> Custom -> Concept pour le paramétrer.';
$lang->custom->notice->readOnlyOfProduct = 'If Change Forbidden, any change on stories, bugs, cases, efforts, releases and plans of the closed product is also forbidden.';
$lang->custom->notice->readOnlyOfProduct = 'If Change Forbidden, any change on stories, bugs, cases, efforts, releases, plans and builds of the closed product is also forbidden.';
$lang->custom->notice->readOnlyOfExecution = "If Change Forbidden, any change on tasks, builds, efforts and stories of the closed {$lang->executionCommon} is also forbidden.";
$lang->custom->notice->readOnlyOfKanban = "If Change Forbidden, any change on kanban card and related operations of {$lang->custom->kanban} is also forbidden.";
$lang->custom->notice->URSREmpty = 'Custom requirement name can not be empty!';
@@ -199,6 +201,7 @@ $lang->custom->notice->storyReviewTip = 'After selecting by individual, pos
$lang->custom->notice->selectAllTip = 'After selecting all people, the reviewers will be emptied and grayed out while hiding their positions and departments.';
$lang->custom->notice->repeatKey = 'Repeat Key %s';
$lang->custom->notice->readOnlyOfCode = "Le code est un terme de gestion utilisé pour la confidentialité ou comme alias. Lorsque la gestion du code est activée, le produit, le projet et l'exécution dans le système afficheront les informations de code sur les pages de création, de modification, de détails et de liste.";
$lang->custom->notice->readOnlyOfPercent = 'The "Workload Ratio" is used to divide the workload of a project into different stages. The sum of the percentages of the same level stages cannot exceed 100%. After enabling the "Workload Ratio", users have to fill in the ratio fields when setting up the stages in the Waterfall project and Waterfall Plus project management models.';
$lang->custom->notice->indexPage['product'] = "ZenTao 8.2+ possède une page d'accueil. Voulez-vous consulter la page d'accueil du produit ?";
$lang->custom->notice->indexPage['project'] = "ZenTao 8.2+ possède une page d'accueil. Voulez-vous consulter la page d'accueil du produit ?";
+4 -1
View File
@@ -59,6 +59,8 @@ $lang->custom->executionCommon = '执行';
$lang->custom->selectDefaultProgram = '请选择一个默认项目集';
$lang->custom->defaultProgram = '默认项目集';
$lang->custom->modeManagement = '模式管理';
$lang->custom->percent = $lang->stage->percent;
$lang->custom->setPercent = "是否启用{$lang->stage->percent}";
$lang->custom->unitList['efficiency'] = '工时/';
$lang->custom->unitList['manhour'] = '人时/';
@@ -188,7 +190,7 @@ $lang->custom->notice->noClosedBlock = '没有永久关闭的区块';
$lang->custom->notice->required = '页面提交时,选中的字段必填';
$lang->custom->notice->conceptResult = '我们已经根据您的选择为您设置了<b> %s-%s </b>模式,使用<b>%s</b> + <b> %s</b>。';
$lang->custom->notice->conceptPath = '您可以在:后台 -> 自定义 -> 流程页面修改。';
$lang->custom->notice->readOnlyOfProduct = '禁止修改后,已关闭' . $lang->productCommon . '下的' . $lang->SRCommon . '、Bug、用例、日志、发布、计划都禁止修改。';
$lang->custom->notice->readOnlyOfProduct = '禁止修改后,已关闭' . $lang->productCommon . '下的' . $lang->SRCommon . '、Bug、用例、日志、发布、计划、版本都禁止修改。';
$lang->custom->notice->readOnlyOfExecution = "禁止修改后,已关闭{$lang->custom->executionCommon}下的任务、版本、日志以及关联需求都禁止修改。";
$lang->custom->notice->readOnlyOfKanban = "禁止修改后,已关闭{$lang->custom->kanban}下的卡片以及相关设置都禁止修改。";
$lang->custom->notice->URSREmpty = '自定义需求名称不能为空!';
@@ -199,6 +201,7 @@ $lang->custom->notice->storyReviewTip = '按人员、职位、部门勾选
$lang->custom->notice->selectAllTip = '勾选所有人员后,会清空并置灰评审人员,同时隐藏职位、部门。';
$lang->custom->notice->repeatKey = '%s键重复';
$lang->custom->notice->readOnlyOfCode = '代号是一种管理话术,主要便于保密或作为别名存在。启用代号管理后,系统中的产品、项目、执行在创建、编辑、详情、列表等页面均会展示代号信息。';
$lang->custom->notice->readOnlyOfPercent = '工作量占比用于划分项目中存在多个阶段时的工作量的占比,同一级阶段的百分比之和最高为100%。启用工作量占比后,系统中的瀑布项目和融合瀑布项目模型中设置阶段时需要维护阶段的工作量占比。';
$lang->custom->notice->indexPage['product'] = "从8.2版本起增加了产品主页视图,是否默认进入产品主页?";
$lang->custom->notice->indexPage['project'] = "从8.2版本起增加了项目主页视图,是否默认进入项目主页?";
+42
View File
@@ -0,0 +1,42 @@
<?php
/**
* The percent view file of custom module of ZenTaoPMS.
* @copyright Copyright 2009-2022 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
* @author Shujie Tian <tianshujie@cnezsoft.com>
* @package custom
* @version $Id$
* @link https://www.zentao.net
*/
?>
<?php include $app->getModuleRoot() . 'common/view/header.html.php';?>
<div id='mainContent' class='main-content'>
<form class="load-indicator main-form form-ajax" method='post'>
<table class='table table-form'>
<tr>
<th class='c-setPercent'><?php echo $lang->custom->setPercent;?></th>
<td class='c-percent text-left'>
<?php $checkedKey = isset($config->setPercent) ? $config->setPercent : 0;?>
<?php foreach($lang->custom->conceptOptions->URAndSR as $key => $value):?>
<label class="radio-inline"><input type="radio" name="percent" value="<?php echo $key?>"<?php echo $key == $checkedKey ? " checked='checked'" : ''?> id="percent<?php echo $key;?>"><?php echo $value;?></label>
<?php endforeach;?>
</td>
<td></td>
</tr>
<tr>
<th></th>
<td colspan="2" id="readOnlyOfPercent">
<div class="inline-block"><i class="icon-exclamation-sign"></i>&nbsp;</div>
<div class="inline-block"><?php echo $lang->custom->notice->readOnlyOfPercent;?></div>
</td>
</tr>
<tr>
<th></th>
<td class='form-actions'>
<?php echo html::submitButton();?>
</td>
</tr>
</table>
</form>
</div>
<?php include '../../common/view/footer.html.php';?>
+3 -3
View File
@@ -384,7 +384,7 @@ class executionModel extends model
$type = 'sprint';
if($project) $type = zget($this->config->execution->modelList, $project->model, 'sprint');
if($project->model == 'waterfall' or $project->model == 'waterfallplus')
if(($project->model == 'waterfall' or $project->model == 'waterfallplus') and isset($this->config->setPercent) and $this->config->setPercent == 1)
{
$this->checkWorkload('create', $_POST['percent'], $project);
if(dao::isError()) return false;
@@ -433,7 +433,7 @@ class executionModel extends model
}
/* Check the workload format and total. */
if(!empty($sprint->percent)) $this->checkWorkload('create', $sprint->percent, $sprint->project);
if(!empty($sprint->percent) and isset($this->config->setPercent) and $this->config->setPercent == 1) $this->checkWorkload('create', $sprint->percent, $sprint->project);
/* Set planDuration and realDuration. */
if($this->config->edition == 'max')
@@ -622,7 +622,7 @@ class executionModel extends model
$execution = $this->loadModel('file')->processImgURL($execution, $this->config->execution->editor->edit['id'], $this->post->uid);
/* Check the workload format and total. */
if(!empty($execution->percent)) $this->checkWorkload('update', $execution->percent, $oldExecution);
if(!empty($execution->percent) and isset($this->config->setPercent) and $this->config->setPercent == 1) $this->checkWorkload('update', $execution->percent, $oldExecution);
/* Set planDuration and realDuration. */
if($this->config->edition == 'max')
+1 -1
View File
@@ -124,7 +124,7 @@
<td class='muted' colspan='2'><div id='lifeTimeTips'><?php echo $lang->execution->typeDesc;?></div></td>
</tr>
<?php endif;?>
<?php if($isStage):?>
<?php if($isStage and isset($config->setPercent) and $config->setPercent == 1):?>
<tr>
<th><?php echo $lang->stage->percent;?></th>
<td class='required'>
+1 -1
View File
@@ -144,7 +144,7 @@
</div>
</td>
</tr>
<?php if($execution->type == 'stage'):?>
<?php if($execution->type == 'stage' and isset($config->setPercent) and $config->setPercent == 1):?>
<tr>
<th><?php echo $lang->stage->percent;?></th>
<td>
+2
View File
@@ -1446,6 +1446,7 @@ $lang->resource->custom->deleteStoryConcept = 'deleteStoryConcept';
$lang->resource->custom->kanban = 'kanban';
$lang->resource->custom->code = 'code';
$lang->resource->custom->hours = 'hours';
$lang->resource->custom->percent = 'percent';
$lang->custom->methodOrder[5] = 'index';
$lang->custom->methodOrder[10] = 'set';
@@ -1464,6 +1465,7 @@ $lang->custom->methodOrder[70] = 'deleteStoryConcept';
$lang->custom->methodOrder[75] = 'kanban';
$lang->custom->methodOrder[80] = 'code';
$lang->custom->methodOrder[85] = 'hours';
$lang->custom->methodOrder[90] = 'percent';
$lang->resource->datatable = new stdclass();
$lang->resource->datatable->setGlobal = 'setGlobal';
+2 -1
View File
@@ -914,7 +914,8 @@ class product extends control
{
$this->loadModel('build');
if(!$executionID) return print(html::select('product', array(), '', "class='form-control chosen' required"));
$products = $this->product->getProductPairsByProject($executionID);
$status = empty($this->config->CRProduct) ? 'noclosed' : '';
$products = $this->product->getProductPairsByProject($executionID, $status);
if(empty($products))
{
return printf($this->lang->build->noProduct, $this->createLink('execution', 'manageproducts', "executionID=$executionID&from=buildCreate", '', 'true'), 'project');
+26 -8
View File
@@ -5,7 +5,14 @@ $config->programplan->create->requiredFields = 'name,begin,end';
$config->programplan->edit->requiredFields = 'name,begin,end';
$config->programplan->datatable = new stdclass();
$config->programplan->datatable->defaultField = array('id', 'name', 'percent', 'attribute', 'begin', 'end', 'realBegan', 'realEnd', 'actions');
if(isset($config->setPercent) and $config->setPercent == 1)
{
$config->programplan->datatable->defaultField = array('id', 'name', 'percent', 'attribute', 'begin', 'end', 'realBegan', 'realEnd', 'actions');
}
else
{
$config->programplan->datatable->defaultField = array('id', 'name', 'attribute', 'begin', 'end', 'realBegan', 'realEnd', 'actions');
}
$config->programplan->datatable->fieldList['id']['title'] = 'idAB';
$config->programplan->datatable->fieldList['id']['fixed'] = 'left';
@@ -17,10 +24,13 @@ $config->programplan->datatable->fieldList['name']['fixed'] = 'left';
$config->programplan->datatable->fieldList['name']['width'] = 'auto';
$config->programplan->datatable->fieldList['name']['required'] = 'yes';
$config->programplan->datatable->fieldList['percent']['title'] = 'percent';
$config->programplan->datatable->fieldList['percent']['fixed'] = 'no';
$config->programplan->datatable->fieldList['percent']['width'] = '100';
$config->programplan->datatable->fieldList['percent']['required'] = 'no';
if(isset($config->setPercent) and $config->setPercent == 1)
{
$config->programplan->datatable->fieldList['percent']['title'] = 'percent';
$config->programplan->datatable->fieldList['percent']['fixed'] = 'no';
$config->programplan->datatable->fieldList['percent']['width'] = '100';
$config->programplan->datatable->fieldList['percent']['required'] = 'no';
}
$config->programplan->datatable->fieldList['attribute']['title'] = 'attribute';
$config->programplan->datatable->fieldList['attribute']['fixed'] = 'no';
@@ -53,10 +63,18 @@ $config->programplan->datatable->fieldList['actions']['width'] = '150';
$config->programplan->datatable->fieldList['actions']['required'] = 'yes';
$config->programplan->datatable->fieldList['actions']['sort'] = 'no';
$config->programplan->customCreateFields = 'PM,percent,attribute,acl,milestone,realBegan,realEnd';
$config->programplan->custom = new stdclass();
$config->programplan->custom->createFields = 'PM,percent,attribute,acl,milestone';
if(isset($config->setPercent) and $config->setPercent == 1)
{
$config->programplan->custom->createFields = 'PM,percent,attribute,acl,milestone';
$config->programplan->customCreateFields = 'PM,percent,attribute,acl,milestone,realBegan,realEnd';
}
else
{
$config->programplan->custom->createFields = 'PM,attribute,acl,milestone';
$config->programplan->customCreateFields = 'PM,attribute,acl,milestone,realBegan,realEnd';
}
$config->programplan->customAgilePlusCreateFields = 'PM,milestone,acl,desc,attribute';
+1
View File
@@ -205,6 +205,7 @@ class programplan extends control
if(strpos(",{$this->config->programplan->$customCreateFields},", ",{$field},") !== false) $visibleFields[$field] = '';
}
}
if(empty($this->config->setPercent)) unset($visibleFields['percent'], $requiredFields['percent']);
if($executionType != 'stage') unset($this->lang->execution->typeList[''], $this->lang->execution->typeList['stage']);
+27 -17
View File
@@ -201,7 +201,7 @@ class programplanModel extends model
$data->type = 'plan';
$data->text = empty($plan->milestone) ? $plan->name : $plan->name . $isMilestone ;
$data->name = $plan->name;
$data->percent = $plan->percent;
if(isset($this->config->setPercent) and $this->config->setPercent == 1) $data->percent = $plan->percent;
$data->attribute = zget($this->lang->stage->typeList, $plan->attribute);
$data->milestone = zget($this->lang->programplan->milestoneList, $plan->milestone);
$data->owner_id = $plan->PM;
@@ -717,6 +717,7 @@ class programplanModel extends model
$setCode = (isset($this->config->setCode) and $this->config->setCode == 1) ? true : false;
$sameCodes = $setCode ? $this->checkCodeUnique($codes, isset($planIDList) ? $planIDList : '') : false;
$setPercent = (isset($this->config->setPercent) and $this->config->setPercent == 1) ? true : false;
$datas = array();
foreach($names as $key => $name)
{
@@ -728,8 +729,8 @@ class programplanModel extends model
$plan->project = $projectID;
$plan->parent = $parentID ? $parentID : $projectID;
$plan->name = $names[$key];
if($setCode) $plan->code = $codes[$key];
$plan->percent = $percents[$key];
if($setCode) $plan->code = $codes[$key];
if($setPercent) $plan->percent = $percents[$key];
$plan->attribute = (empty($parentID) or $parentAttribute == 'mix') ? $attributes[$key] : $parentAttribute;
$plan->milestone = $milestone[$key];
$plan->begin = empty($begin[$key]) ? '0000-00-00' : $begin[$key];
@@ -753,7 +754,7 @@ class programplanModel extends model
if(!empty($sameNames) and in_array($plan->name, $sameNames)) dao::$errors[$index]['name'] = empty($type) ? $this->lang->programplan->error->sameName : str_replace($this->lang->execution->stage, '', $this->lang->programplan->error->sameName);
if($setCode and $sameCodes !== true and !empty($sameCodes) and in_array($plan->code, $sameCodes)) dao::$errors[$index]['code'] = sprintf($this->lang->error->repeat, $plan->type == 'stage' ? $this->lang->execution->code : $this->lang->code, $plan->code);
if($plan->percent and !preg_match("/^[0-9]+(.[0-9]{1,3})?$/", $plan->percent))
if($setPercent and $plan->percent and !preg_match("/^[0-9]+(.[0-9]{1,3})?$/", $plan->percent))
{
dao::$errors[$index]['percent'] = $this->lang->programplan->error->percentNumber;
}
@@ -809,13 +810,16 @@ class programplanModel extends model
}
}
$plan->percent = (float)$plan->percent;
$totalPercent += $plan->percent;
if($setPercent)
{
$plan->percent = (float)$plan->percent;
$totalPercent += $plan->percent;
}
if($plan->milestone) $milestone = 1;
}
if($totalPercent > 100) dao::$errors['percent'] = $this->lang->programplan->error->percentOver;
if($setPercent and $totalPercent > 100) dao::$errors['percent'] = $this->lang->programplan->error->percentOver;
if(dao::isError()) return false;
$this->loadModel('action');
@@ -880,7 +884,7 @@ class programplanModel extends model
$this->dao->update(TABLE_PROJECT)->data($data)
->autoCheck()
->batchCheck($this->config->programplan->edit->requiredFields, 'notempty')
->checkIF($plan->percent != '', 'percent', 'float')
->checkIF($plan->percent != '' and $setPercent, 'percent', 'float')
->where('id')->eq($stageID)
->exec();
@@ -937,7 +941,7 @@ class programplanModel extends model
$this->dao->insert(TABLE_PROJECT)->data($data)
->autoCheck()
->batchCheck($this->config->programplan->create->requiredFields, 'notempty')
->checkIF($plan->percent != '', 'percent', 'float')
->checkIF($plan->percent != '' and $setPercent, 'percent', 'float')
->exec();
if(!dao::isError())
@@ -1101,15 +1105,18 @@ class programplanModel extends model
$planChanged = ($oldPlan->name != $plan->name || $oldPlan->milestone != $plan->milestone || $oldPlan->begin != $plan->begin || $oldPlan->end != $plan->end);
$setPercent = isset($this->config->setPercent) and $this->config->setPercent == 1 ? true : false;
if($plan->parent > 0)
{
$plan->attribute = $parentStage->attribute == 'mix' ? $plan->attribute : $parentStage->attribute;
$plan->acl = $parentStage->acl;
$parentPercent = $parentStage->percent;
$childrenTotalPercent = $this->getTotalPercent($parentStage, true);
$childrenTotalPercent = $plan->parent == $oldPlan->parent ? ($childrenTotalPercent - $oldPlan->percent + $plan->percent) : ($childrenTotalPercent + $plan->percent);
if($childrenTotalPercent > 100) return dao::$errors['percent'][] = $this->lang->programplan->error->percentOver;
if($setPercent)
{
$parentPercent = $parentStage->percent;
$childrenTotalPercent = $this->getTotalPercent($parentStage, true);
$childrenTotalPercent = $plan->parent == $oldPlan->parent ? ($childrenTotalPercent - $oldPlan->percent + $plan->percent) : ($childrenTotalPercent + $plan->percent);
if($childrenTotalPercent > 100) return dao::$errors['percent'][] = $this->lang->programplan->error->percentOver;
}
/* If child plan has milestone, update parent plan set milestone eq 0 . */
if($plan->milestone and $parentStage->milestone) $this->dao->update(TABLE_PROJECT)->set('milestone')->eq(0)->where('id')->eq($oldPlan->parent)->exec();
@@ -1122,9 +1129,12 @@ class programplanModel extends model
/* The workload of the parent plan cannot exceed 100%. */
$oldPlan->parent = $plan->parent;
$totalPercent = $this->getTotalPercent($oldPlan);
$totalPercent = $totalPercent + $plan->percent;
if($totalPercent > 100) return dao::$errors['percent'][] = $this->lang->programplan->error->percentOver;
if($setPercent)
{
$totalPercent = $this->getTotalPercent($oldPlan);
$totalPercent = $totalPercent + $plan->percent;
if($totalPercent > 100) return dao::$errors['percent'][] = $this->lang->programplan->error->percentOver;
}
}
/* Set planDuration and realDuration. */
+2
View File
@@ -46,6 +46,7 @@
<th><?php echo $lang->programplan->PM;?> </th>
<td colspan='2'><?php echo html::select('PM', $PMUsers, '', "class='form-control picker-select'");?></td>
</tr>
<?php if(isset($config->setPercent) and $config->setPercent == 1):?>
<tr>
<th><?php echo $lang->programplan->percent;?> </th>
<td colspan='2'>
@@ -55,6 +56,7 @@
</div>
</td>
</tr>
<?php endif;?>
<tr id="attributeType">
<th><?php echo $lang->programplan->attribute;?></th>
<td colspan='2'>
+12 -5
View File
@@ -62,6 +62,13 @@ $(function()
bootbox.alert(unLinkProductTip.replace("%s", allProducts[last]));
}
}
var chosenProducts = 0;
$(".productsBox select[name^='products']").each(function()
{
if($(this).val() > 0) chosenProducts ++;
});
if(chosenProducts > 1) $('.division').removeClass('hide');
if(chosenProducts <= 1) $('.division').addClass('hide');
});
$(document).on('change', '[name*=branch]', function()
@@ -198,7 +205,7 @@ function loadBranches(product)
}
});
(chosenProducts.length > 1 && model == 'waterfall') ? $('.division').removeClass('hide') : $('.division').addClass('hide');
(chosenProducts.length > 1 && model == 'waterfall' && model == 'waterfallplus') ? $('.division').removeClass('hide') : $('.division').addClass('hide');
var $tableRow = $(product).closest('.table-row');
var index = $tableRow.find('select:first').attr('id').replace('products' , '');
@@ -255,15 +262,15 @@ function loadPlans(product, branch)
/**
* Add new line for link product.
*
* @param obj $obj
*
* @param obj $obj
* @access public
* @return void
*/
function addNewLine(obj)
{
var newLine = $(obj).closest('tr').clone();
var index = 0;
var index = 0;
$(".productsBox select[name^='products']").each(function()
{
var id = $(this).attr('id').replace('products' , '');
@@ -294,5 +301,5 @@ function removeLine(obj)
{
$(obj).closest('tr').remove();
disableSelectedProduct();
if($("select[name^='products']").length < 2) $('.division').addClass('hide');
if($("select[name^='products']").length < 2) $('.division').addClass('hide');
}
+11 -3
View File
@@ -1,5 +1,13 @@
<?php
<?php
$config->stage->create = new stdclass();
$config->stage->edit = new stdclass();
$config->stage->create->requiredFields = 'name,percent,type';
$config->stage->edit->requiredFields = 'name,percent,type';
if(isset($config->setPercent) and $config->setPercent == 1)
{
$config->stage->create->requiredFields = 'name,percent,type';
$config->stage->edit->requiredFields = 'name,percent,type';
}
else
{
$config->stage->create->requiredFields = 'name,type';
$config->stage->edit->requiredFields = 'name,type';
}
+18 -10
View File
@@ -27,10 +27,13 @@ class stageModel extends model
->add('createdDate', helper::today())
->get();
$totalPercent = $this->getTotalPercent($type);
if(isset($this->config->setPercent) and $this->config->setPercent == 1)
{
$totalPercent = $this->getTotalPercent($type);
if(!is_numeric($stage->percent)) return dao::$errors['message'][] = $this->lang->stage->error->notNum;
if(round($totalPercent + $stage->percent) > 100) return dao::$errors['message'][] = $this->lang->stage->error->percentOver;
if(!is_numeric($stage->percent)) return dao::$errors['message'][] = $this->lang->stage->error->notNum;
if(round($totalPercent + $stage->percent) > 100) return dao::$errors['message'][] = $this->lang->stage->error->percentOver;
}
$this->dao->insert(TABLE_STAGE)
->data($stage)
@@ -53,9 +56,12 @@ class stageModel extends model
{
$data = fixer::input('post')->get();
$totalPercent = $this->getTotalPercent($type);
if(round($totalPercent + array_sum($data->percent)) > 100) return dao::$errors['message'][] = $this->lang->stage->error->percentOver;
$setPercent = (isset($this->config->setPercent) and $this->config->setPercent == 1) ? true : false;
if($setPercent)
{
$totalPercent = $this->getTotalPercent($type);
if(round($totalPercent + array_sum($data->percent)) > 100) return dao::$errors['message'][] = $this->lang->stage->error->percentOver;
}
$this->loadModel('action');
foreach($data->name as $i => $name)
@@ -64,7 +70,7 @@ class stageModel extends model
$stage = new stdclass();
$stage->name = $name;
$stage->percent = $data->percent[$i];
if($setPercent) $stage->percent = $data->percent[$i];
$stage->type = $data->type[$i];
$stage->projectType = $type;
$stage->createdBy = $this->app->user->account;
@@ -100,9 +106,11 @@ class stageModel extends model
->add('editedDate', helper::today())
->get();
$totalPercent = $this->getTotalPercent($oldStage->projectType);
if(round($totalPercent + $stage->percent - $oldStage->percent) > 100) return dao::$errors['message'][] = $this->lang->stage->error->percentOver;
if(isset($this->config->setPercent) and $this->config->setPercent == 1)
{
$totalPercent = $this->getTotalPercent($oldStage->projectType);
if(round($totalPercent + $stage->percent - $oldStage->percent) > 100) return dao::$errors['message'][] = $this->lang->stage->error->percentOver;
}
$this->dao->update(TABLE_STAGE)
->data($stage)
+4
View File
@@ -22,7 +22,9 @@
<tr>
<th class='c-id'><?php echo $lang->stage->id;?></th>
<th class='required'><?php echo $lang->stage->name;?></th>
<?php if(isset($config->setPercent) and $config->setPercent == 1):?>
<th class='c-percent required'><?php echo $lang->stage->percent;?></th>
<?php endif;?>
<th class='c-type required'><?php echo $lang->stage->type;?></th>
</tr>
</thead>
@@ -31,7 +33,9 @@
<tr>
<td><?php echo $i;?></td>
<td><?php echo html::input("name[$i]", '', "class='form-control'");?></td>
<?php if(isset($config->setPercent) and $config->setPercent == 1):?>
<td><?php echo html::input("percent[$i]", '', "class='form-control'");?></td>
<?php endif;?>
<td><?php echo html::select("type[$i]", array('' => '') + $lang->stage->typeList, '', "class='form-control chosen'");?></td>
</tr>
<?php endfor;?>
+4
View File
@@ -38,7 +38,9 @@
<tr>
<th class='text-left w-60px'><?php common::printOrderLink('id', $orderBy, $vars, $lang->stage->id);?></th>
<th class='text-left'><?php common::printOrderLink('name', $orderBy, $vars, $lang->stage->name);?></th>
<?php if(isset($config->setPercent) and $config->setPercent == 1):?>
<th class='w-100px'><?php common::printOrderLink('percent', $orderBy, $vars, $lang->stage->percent);?></th>
<?php endif;?>
<th class='w-120px'><?php common::printOrderLink('type', $orderBy, $vars, $lang->stage->type);?></th>
<th class='c-actions-2'><?php echo $lang->actions;?></th>
</tr>
@@ -48,7 +50,9 @@
<tr>
<td><?php echo $stage->id;?></td>
<td><?php echo $stage->name;?></td>
<?php if(isset($config->setPercent) and $config->setPercent == 1):?>
<td class='text-center'><?php echo $stage->percent;?></td>
<?php endif;?>
<td><?php echo zget($lang->stage->typeList, $stage->type);?></td>
<td class="c-actions">
<?php
+3 -1
View File
@@ -25,15 +25,17 @@
<td></td>
<td></td>
</tr>
<?php if(isset($config->setPercent) and $config->setPercent == 1):?>
<tr>
<th><?php echo $lang->stage->percent;?></th>
<td>
<div class='input-group'>
<?php echo html::input('percent', '', "class='form-control'");?>
<span class='input-group-addon'>%</span>
<span class='input-group-addon'>%</span>
</div>
</td>
</tr>
<?php endif;?>
<tr>
<th><?php echo $lang->stage->type;?></th>
<td><?php echo html::select('type', $lang->stage->typeList, '', "class='form-control chosen'");?></td>
+3 -1
View File
@@ -25,15 +25,17 @@
<td></td>
<td></td>
</tr>
<?php if(isset($config->setPercent) and $config->setPercent == 1):?>
<tr>
<th><?php echo $lang->stage->percent;?></th>
<td>
<div class='input-group'>
<?php echo html::input('percent', $stage->percent, "class='form-control'");?>
<span class='input-group-addon'>%</span>
<span class='input-group-addon'>%</span>
</div>
</td>
</tr>
<?php endif;?>
<tr>
<th><?php echo $lang->stage->type;?></th>
<td><?php echo html::select('type', $lang->stage->typeList, $stage->type, "class='form-control chosen'");?></td>
+15 -13
View File
@@ -578,7 +578,7 @@ class story extends control
if(!empty($_POST))
{
$mails = $this->story->batchCreate($productID, $branch, $storyType);
if(dao::isError()) return print(js::error(dao::getError()));
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
$stories = array();
foreach($mails as $mail) $stories[] = $mail->storyID;
@@ -602,7 +602,7 @@ class story extends control
if($storyID and !empty($mails))
{
$this->story->subdivide($storyID, $stories);
if(dao::isError()) return print(js::error(dao::getError()));
if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError()));
}
if($this->viewType == 'json') return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'idList' => $stories));
@@ -621,7 +621,7 @@ class story extends control
$rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : '';
$kanbanData = $this->loadModel('kanban')->getRDKanban($executionID, $execLaneType, 'id_desc', 0, $execGroupBy, $rdSearchValue);
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban($kanbanData)"));
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'closeModal' => true, 'callback' => "parent.parent.updateKanban($kanbanData)"));
}
else
{
@@ -630,12 +630,12 @@ class story extends control
$kanbanType = $execLaneType == 'all' ? 'story' : key($kanbanData);
$kanbanData = $kanbanData[$kanbanType];
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban(\"story\", $kanbanData)"));
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'closeModal' => true, 'callback' => "parent.parent.updateKanban(\"story\", $kanbanData)"));
}
}
else
{
return print(js::reload('parent.parent'));
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'closeModal' => true, 'callback' => 'reloadByAjaxForm()'));
}
}
@@ -643,24 +643,26 @@ class story extends control
{
if($this->app->tab == 'product')
{
return print(js::locate(inlink('view', "storyID=$storyID&version=0&param=0&storyType=$storyType"), 'parent'));
$locateLink = $this->inlink('view', "storyID=$storyID&version=0&param=0&storyType=$storyType");
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $locateLink));
}
else
{
/* Lite. */
$locateLink = $this->session->storyList ? $this->session->storyList : $this->createLink('projectstory', 'view', "storyID=$storyID");
return print(js::locate($locateLink, 'parent'));
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $locateLink));
}
}
elseif($executionID)
{
setcookie('storyModuleParam', 0, 0, $this->config->webRoot, '', $this->config->cookieSecure, false);
return print(js::locate($this->session->storyList, 'parent'));
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $this->session->storyList));
}
else
{
setcookie('storyModule', 0, 0, $this->config->webRoot, '', $this->config->cookieSecure, false);
$locateLink = $this->session->storyList ? $this->session->storyList : $this->createLink('product', 'browse', "productID=$productID&branch=$branch&browseType=unclosed&queryID=0&storyType=$storyType");
return print(js::locate($locateLink, 'parent'));
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => $locateLink));
}
}
@@ -1213,7 +1215,7 @@ class story extends control
if(dao::isError())
{
if(defined('RUN_MODE') && RUN_MODE == 'api') return $this->send(array('status' => 'fail', 'message' => dao::getError()));
return print(js::error(dao::getError()));
return $this->send(array('result' => 'fail', 'message' => dao::getError()));
}
if($this->post->comment != '' or !empty($changes))
@@ -1243,7 +1245,7 @@ class story extends control
$rdSearchValue = $this->session->rdSearchValue ? $this->session->rdSearchValue : '';
$kanbanData = $this->loadModel('kanban')->getRDKanban($this->session->execution, $execLaneType, 'id_desc', 0, $execGroupBy, $rdSearchValue);
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban($kanbanData)"));
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'closeModal' => true, 'callback' => "parent.parent.updateKanban($kanbanData)"));
}
if($from == 'taskkanban')
{
@@ -1252,12 +1254,12 @@ class story extends control
$kanbanType = $execLaneType == 'all' ? 'story' : key($kanbanData);
$kanbanData = $kanbanData[$kanbanType];
$kanbanData = json_encode($kanbanData);
return print(js::closeModal('parent.parent', '', "parent.parent.updateKanban(\"story\", $kanbanData)"));
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'closeModal' => true, 'callback' => "parent.parent.updateKanban(\"story\", $kanbanData)"));
}
}
else
{
return print(js::reload('parent.parent'));
return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'closeModal' => true, 'callback' => 'reloadByAjaxForm()'));
}
}
+2
View File
@@ -11,3 +11,5 @@
#batchCreateForm .c-keywords {width: 140px;}
.body-modal .main-header > h2 {max-width: 80%;}
#batchCreateForm .reviewBox{width: 200px !important;}
.table tbody>tr>td {vertical-align: top;}
+11
View File
@@ -176,3 +176,14 @@ function popoverCancel(index)
$('[data-id="' + index + '"]').addClass('hide');
}
/**
* Reload parent window When operating in a pop-up window.
*
* @access public
* @return void
*/
function reloadByAjaxForm()
{
parent.location.reload();
}
+82 -92
View File
@@ -541,8 +541,7 @@ class storyModel extends model
{
if(empty($title) and $this->common->checkValidRow('story', $stories, $i))
{
dao::$errors['message'][] = sprintf($this->lang->error->notempty, $this->lang->story->title);
return false;
dao::$errors["title$i"][] = sprintf($this->lang->error->notempty, $this->lang->story->title);
}
$module = $stories->module[$i] == 'ditto' ? $module : $stories->module[$i];
@@ -571,8 +570,7 @@ class storyModel extends model
$_POST['reviewer'][$i] = $reviewers;
if(empty($stories->reviewer[$i]) and $forceReview)
{
dao::$errors[] = $this->lang->story->errorEmptyReviewedBy;
return false;
dao::$errors["reviewer$i"][] = $this->lang->story->errorEmptyReviewedBy;
}
$story = new stdclass();
@@ -587,6 +585,7 @@ class storyModel extends model
$story->pri = $stories->pri[$i];
$story->estimate = $stories->estimate[$i];
$story->spec = $stories->spec[$i];
$story->verify = $stories->verify[$i];
$story->status = $saveDraft ? 'draft' : ((empty($stories->reviewer[$i]) and !$forceReview) ? 'active' : 'reviewing');
$story->stage = ($this->app->tab == 'project' or $this->app->tab == 'execution') ? 'projected' : 'wait';
$story->keywords = $stories->keywords[$i];
@@ -605,8 +604,7 @@ class storyModel extends model
$story->{$extendField->field} = htmlSpecialString($story->{$extendField->field});
if(empty($story->{$extendField->field}))
{
dao::$errors[] = sprintf($this->lang->error->notempty, $extendField->name);
return false;
dao::$errors["{$extendField->field}$i"][] = sprintf($this->lang->error->notempty, $extendField->name);
}
}
@@ -620,8 +618,7 @@ class storyModel extends model
if(!empty($story->$field)) continue;
if($field == 'estimate' and strlen(trim($story->estimate)) != 0) continue;
dao::$errors['message'][] = sprintf($this->lang->error->notempty, $this->lang->story->$field);
return false;
dao::$errors["{$field}$i"][] = sprintf($this->lang->error->notempty, $this->lang->story->$field);
}
$data[$i] = $story;
}
@@ -629,96 +626,95 @@ class storyModel extends model
$link2Plans = array();
foreach($data as $i => $story)
{
$this->dao->insert(TABLE_STORY)->data($story, 'spec')->autoCheck()->checkFlow()->exec();
if(dao::isError())
$this->dao->insert(TABLE_STORY)->data($story, 'spec,verify')->autoCheck()->checkFlow()->exec();
if(!dao::isError())
{
echo js::error(dao::getError());
return print(js::reload('parent'));
}
$storyID = $this->dao->lastInsertID();
$this->setStage($storyID);
$storyID = $this->dao->lastInsertID();
$this->setStage($storyID);
/* Update product plan stories order. */
if($story->plan)
{
$this->updateStoryOrderOfPlan($storyID, $story->plan);
$link2Plans[$story->plan] = empty($link2Plans[$story->plan]) ? $storyID : "{$link2Plans[$story->plan]},$storyID";
}
$specData = new stdclass();
$specData->story = $storyID;
$specData->version = 1;
$specData->title = $stories->title[$i];
$specData->spec = '';
$specData->verify = '';
if(!empty($stories->spec[$i])) $specData->spec = nl2br($stories->spec[$i]);
if(!empty($stories->verify[$i]))$specData->verify = nl2br($stories->verify[$i]);
if(!empty($stories->uploadImage[$i]))
{
$fileName = $stories->uploadImage[$i];
$file = $this->session->storyImagesFile[$fileName];
$realPath = $file['realpath'];
unset($file['realpath']);
if(!is_dir($this->file->savePath)) mkdir($this->file->savePath, 0777, true);
if($realPath and rename($realPath, $this->file->savePath . $this->file->getSaveName($file['pathname'])))
/* Update product plan stories order. */
if($story->plan)
{
$file['addedBy'] = $this->app->user->account;
$file['addedDate'] = $now;
$file['objectType'] = 'story';
$file['objectID'] = $storyID;
if(in_array($file['extension'], $this->config->file->imageExtensions))
{
$file['extra'] = 'editor';
$this->dao->insert(TABLE_FILE)->data($file)->exec();
$this->updateStoryOrderOfPlan($storyID, $story->plan);
$link2Plans[$story->plan] = empty($link2Plans[$story->plan]) ? $storyID : "{$link2Plans[$story->plan]},$storyID";
}
$fileID = $this->dao->lastInsertID();
$specData->spec .= '<img src="{' . $fileID . '.' . $file['extension'] . '}" alt="" />';
}
else
$specData = new stdclass();
$specData->story = $storyID;
$specData->version = 1;
$specData->title = $stories->title[$i];
$specData->spec = '';
$specData->verify = '';
if(!empty($stories->spec[$i])) $specData->spec = nl2br($stories->spec[$i]);
if(!empty($stories->verify[$i]))$specData->verify = nl2br($stories->verify[$i]);
if(!empty($stories->uploadImage[$i]) and $stories->uploadImage[$i] !== 'undefined')
{
$fileName = $stories->uploadImage[$i];
$file = $this->session->storyImagesFile[$fileName];
$realPath = $file['realpath'];
unset($file['realpath']);
if(!is_dir($this->file->savePath)) mkdir($this->file->savePath, 0777, true);
if($realPath and rename($realPath, $this->file->savePath . $this->file->getSaveName($file['pathname'])))
{
$this->dao->insert(TABLE_FILE)->data($file)->exec();
$file['addedBy'] = $this->app->user->account;
$file['addedDate'] = $now;
$file['objectType'] = 'story';
$file['objectID'] = $storyID;
if(in_array($file['extension'], $this->config->file->imageExtensions))
{
$file['extra'] = 'editor';
$this->dao->insert(TABLE_FILE)->data($file)->exec();
$fileID = $this->dao->lastInsertID();
$specData->spec .= '<img src="{' . $fileID . '.' . $file['extension'] . '}" alt="" />';
}
else
{
$this->dao->insert(TABLE_FILE)->data($file)->exec();
}
}
}
$this->dao->insert(TABLE_STORYSPEC)->data($specData)->exec();
/* Save the story reviewer to storyreview table. */
foreach($_POST['reviewer'][$i] as $reviewer)
{
if(empty($reviewer)) continue;
$reviewData = new stdclass();
$reviewData->story = $storyID;
$reviewData->version = 1;
$reviewData->reviewer = $reviewer;
$this->dao->insert(TABLE_STORYREVIEW)->data($reviewData)->exec();
}
$this->executeHooks($storyID);
$actionID = $this->action->create('story', $storyID, 'Opened', '');
if(!dao::isError()) $this->loadModel('score')->create('story', 'create',$storyID);
$mails[$i] = new stdclass();
$mails[$i]->storyID = $storyID;
$mails[$i]->actionID = $actionID;
}
$this->dao->insert(TABLE_STORYSPEC)->data($specData)->exec();
/* Save the story reviewer to storyreview table. */
foreach($_POST['reviewer'][$i] as $reviewer)
{
if(empty($reviewer)) continue;
$reviewData = new stdclass();
$reviewData->story = $storyID;
$reviewData->version = 1;
$reviewData->reviewer = $reviewer;
$this->dao->insert(TABLE_STORYREVIEW)->data($reviewData)->exec();
}
$this->executeHooks($storyID);
$actionID = $this->action->create('story', $storyID, 'Opened', '');
if(!dao::isError()) $this->loadModel('score')->create('story', 'create',$storyID);
$mails[$i] = new stdclass();
$mails[$i]->storyID = $storyID;
$mails[$i]->actionID = $actionID;
}
/* Remove upload image file and session. */
if(!empty($stories->uploadImage) and $this->session->storyImagesFile)
{
$classFile = $this->app->loadClass('zfile');
$file = current($_SESSION['storyImagesFile']);
$realPath = dirname($file['realpath']);
if(is_dir($realPath)) $classFile->removeDir($realPath);
unset($_SESSION['storyImagesFile']);
}
if(!dao::isError())
{
/* Remove upload image file and session. */
if(!empty($stories->uploadImage) and $this->session->storyImagesFile)
{
$classFile = $this->app->loadClass('zfile');
$file = current($_SESSION['storyImagesFile']);
$realPath = dirname($file['realpath']);
if(is_dir($realPath)) $classFile->removeDir($realPath);
unset($_SESSION['storyImagesFile']);
}
$this->loadModel('score')->create('ajax', 'batchCreate');
foreach($link2Plans as $planID => $stories) $this->action->create('productplan', $planID, 'linkstory', '', $stories);
}
@@ -4073,12 +4069,6 @@ class storyModel extends model
$property = '';
}
$storyPairs[$story->id] = $story->id . ':' . $story->title . ' ' . $property;
if($limit > 0 && ++$i > $limit)
{
$storyPairs['showmore'] = $this->lang->more . $this->lang->ellipsis;
break;
}
}
return $storyPairs;
}
@@ -4688,7 +4678,7 @@ class storyModel extends model
$menu .= "</ul></div>";
}
if($this->app->tab == 'execution' and $story->status == 'active') $menu .= $this->buildMenu('task', 'create', "execution={$this->session->execution}&{$params}&moduleID=$story->module", $story, $type, 'plus', '', 'showinonlybody');
if(($this->app->tab == 'execution' || (!empty($execution) and $execution->multiple === '0')) and $story->status == 'active') $menu .= $this->buildMenu('task', 'create', "execution={$this->session->execution}&{$params}&moduleID=$story->module", $story, $type, 'plus', '', 'showinonlybody');
$menu .= "<div class='divider'></div>";
$menu .= $this->buildFlowMenu('story', $story, $type, 'direct');
+1 -1
View File
@@ -46,7 +46,7 @@
}
unset($visibleFields['module']);
?>
<form method='post' class='load-indicator main-form' enctype='multipart/form-data' target='hiddenwin' id="batchCreateForm">
<form method='post' class='load-indicator main-form form-ajax' enctype='multipart/form-data' id="batchCreateForm">
<div class="table-responsive">
<table class="table table-form">
<thead>
+1 -1
View File
@@ -21,7 +21,7 @@
<small><?php echo $lang->arrow . ' ' . $lang->story->change;?></small>
</h2>
</div>
<form class="main-form" method='post' enctype='multipart/form-data' target='hiddenwin' id='dataform'>
<form class="main-form form-ajax" method='post' enctype='multipart/form-data' id='dataform'>
<table class='table table-form'>
<tr>
<th class='w-80px'><?php echo $lang->story->reviewedBy;?></th>
+1
View File
@@ -6,3 +6,4 @@
#modalTeam .modal-content {padding: 20px 13px 0; height: 340px;max-height: 296px!important; overflow-y: scroll!important;}
#taskTeamEditor tr .input-control {display: inline-block; width: 130px;}
#taskTeamEditor h2 {margin-top:0;}
+1
View File
@@ -276,6 +276,7 @@ $lang->task->error->consumedThisTime = 'Bitte geben Sie die Stunden an';
$lang->task->error->left = 'Bitte geben Sie die verbleibenden Stunden an"';
$lang->task->error->work = '"Bemerkung" muss kleiner als %d Zeichen sein.';
$lang->task->error->teamMember = 'Team members must be at least 2 people';
$lang->task->error->teamCantOperate = 'Please activate the closed, suspended, and canceled tasks before setting the team.';
$lang->task->error->skipClose = 'Aufgabe: %s ist nicht “Erledigt” oder “Abgebrochen”. Möchten Sie die Aufgabe jetzt schließen?';
$lang->task->error->closeParent = 'Task: %s is the Parent Task, which is automatically closed after all subtasks under the Parent Task are closed and cannot be closed manually.';
$lang->task->error->consumed = 'Aufgabe: %s Hour must be more than 0. Ignore changes to this Task.';
+1
View File
@@ -276,6 +276,7 @@ $lang->task->error->consumedThisTime = 'Please enter "Hours Cost"';
$lang->task->error->left = 'Please enter "Hours Left"';
$lang->task->error->work = '"Comment" must be < %d characters.';
$lang->task->error->teamMember = 'Team members must be at least 2 people';
$lang->task->error->teamCantOperate = 'Please activate the closed, suspended, and canceled tasks before setting the team.';
$lang->task->error->skipClose = 'Task: %s is not “Finished” or “Cancelled”. Do you want to close it?';
$lang->task->error->closeParent = 'Task: %s is the Parent Task, which is automatically closed after all subtasks under the Parent Task are closed and cannot be closed manually.';
$lang->task->error->consumed = 'Task: %s hour must be < 0. Ignore changes to this task.';
+1
View File
@@ -276,6 +276,7 @@ $lang->task->error->consumedThisTime = 'Entrez le "Coût en Heures"';
$lang->task->error->left = 'Entrez les "Heures Restantes"';
$lang->task->error->work = '"Commentaire" doit être < %d caractères.';
$lang->task->error->teamMember = 'Team members must be at least 2 people';
$lang->task->error->teamCantOperate = 'Please activate the closed, suspended, and canceled tasks before setting the team.';
$lang->task->error->skipClose = 'Tâche: %s non “Finie” ou “Annulée”. Voulez-vous la fermer malgré tout ?';
$lang->task->error->closeParent = 'Task: %s is the Parent Task, which is automatically closed after all subtasks under the Parent Task are closed and cannot be closed manually.';
$lang->task->error->consumed = 'Tâche: %s heures doivent être < 0. Ignorer changements de cette tâche.';
+1
View File
@@ -276,6 +276,7 @@ $lang->task->error->consumedThisTime = '请填写"消耗"';
$lang->task->error->left = '请填写"剩余"';
$lang->task->error->work = '"备注"必须小于%d个字符';
$lang->task->error->teamMember = '团队成员必须大于1人';
$lang->task->error->teamCantOperate = '已关闭、已暂停、已取消的任务请激活后再维护团队。';
$lang->task->error->skipClose = '任务:%s 不是“已完成”或“已取消”状态,确定要关闭吗?';
$lang->task->error->closeParent = '任务:%s 是父任务,父任务在所有子任务关闭后会自动关闭,无法手动关闭。';
$lang->task->error->consumed = '任务:%s工时不能小于0,忽略该任务工时的改动';
+10 -3
View File
@@ -285,14 +285,21 @@ foreach(explode(',', $config->task->edit->requiredFields) as $field)
</button>
<h4 class="modal-title"><?php echo $lang->task->team?></h4>
</div>
<div class="modal-content with-padding" id='taskTeamEditor'>
<div class="modal-content with-padding" id='taskTeamEditor'>
<?php if(strpos('|closed|cancel|pause|', $task->status) !== false):?>
<h2 class='label label-info'>
<?php echo $this->lang->task->error->teamCantOperate;?>
</h2>
<?php endif;?>
<table class='table table-form'>
<tbody class="sortable">
<?php include dirname(__FILE__) . DS . 'taskteam.html.php';?>
</tbody>
<tfoot>
<tfoot>
<?php if(strpos('|closed|cancel|pause|', $task->status) === false):?>
<tr><td colspan='3' class='text-center form-actions'><?php echo html::a('javascript:void(0)', $lang->confirm, '', "id='confirmButton' class='btn btn-primary btn-wide'");?></td></tr>
</tfoot>
<?php endif;?>
</tfoot>
</table>
</div>
</div>
+1 -1
View File
@@ -26,7 +26,7 @@ $memberStatus = $member->status;
if($memberStatus == 'done') $memberDisabled = true;
if($memberStatus != 'wait' and $task->mode == 'linear') $sortDisabled = true;
if($memberStatus == 'done' and $task->mode == 'multi') $sortDisabled = true;
if($task->mode == 'linear' and strpos('|closed|cancel|pause|', $task->status) !== false and $app->rawMethod != 'activate')
if(strpos('|closed|cancel|pause|', $task->status) !== false and $app->rawMethod != 'activate')
{
$memberStatus = $task->status;
$memberDisabled = true;
+3
View File
@@ -590,6 +590,9 @@ class upgradeModel extends model
case '18_1':
$this->insertMixStage();
break;
case '18_2':
$this->loadModel('setting')->setSN();
break;
}
$this->deletePatch();