This commit is contained in:
wangyidong
2022-04-26 16:43:28 +08:00
31 changed files with 223 additions and 31 deletions
+4
View File
@@ -99,6 +99,10 @@ class executionEntry extends Entry
$dynamics = $data->data->dynamics;
$execution->dynamics = $this->loadModel('action')->processDynamicForAPI($dynamics);
break;
case 'chartdata':
list($dateList, $interval) = $this->loadModel('execution')->getDateList($execution->begin, $execution->end, 'noweekend', '0', 'Y-m-d');
$execution->chartData = $this->execution->buildBurnData($executionID, $dateList, 'noweekend', 'left');
break;
}
}
+127
View File
@@ -0,0 +1,127 @@
<?php
/**
* The executions entry point of ZenTaoPMS.
*
* @copyright Copyright 2009-2021 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
* @license ZPL (http://zpl.pub/page/zplv12.html)
* @author Chunsheng Wang <chunsheng@cnezsoft.com>
* @package entries
* @version 1
* @link http://www.zentao.net
*/
class feedbacksEntry extends entry
{
/**
* GET method.
*
* @param int $projectID
* @access public
* @return void
*/
public function get()
{
$control = $this->loadController('feedback', 'admin');
$control->admin($this->param('status', 'unclosed'), 0, $this->param('orderBy', 'id_desc'), 0, $this->param('limit', 20), $this->param('page', 1));
$data = $this->getData();
if(!$data or !isset($data->status)) return $this->sendError(400, 'error');
if(isset($data->status) and $data->status == 'fail') return $this->sendError(400, $data->message);
$feedbacks = $data->data->feedbacks;
$pager = $data->data->pager;
$result = array();
foreach($feedbacks as $feedback)
{
$result[] = $this->format($feedback, 'openedBy:user,openedDate:time,reviewedBy:user,reviewedDate:time,processedBy:user,processedDate:time,closedBy:user,closedDate:time,editedBy:user,editedDate:time,mailto:userList,deleted:bool');
}
$data = array();
$data['page'] = $pager->pageID;
$data['total'] = $pager->recTotal;
$data['limit'] = $pager->recPerPage;
$data['feedbacks'] = $result;
return $this->send(200, $data);
}
/**
* POST method.
*
* @param int $projectID
* @access public
* @return void
*/
public function post($projectID = 0)
{
$fields = 'project,code,name,begin,end,lifetime,desc,days';
$this->batchSetPost($fields);
$projectID = $this->param('project', $projectID);
$this->setPost('project', $projectID);
$this->setPost('acl', $this->request('acl', 'private'));
$this->setPost('PO', $this->request('PO', ''));
$this->setPost('PM', $this->request('PM', ''));
$this->setPost('QD', $this->request('QD', ''));
$this->setPost('RD', $this->request('RD', ''));
$this->setPost('whitelist', $this->request('whitelist', array()));
$this->setPost('products', $this->request('products', array()));
$this->setPost('plans', $this->request('plans', array()));
$control = $this->loadController('execution', 'create'); $this->requireFields('name,code,begin,end,days');
$control->create($projectID);
$data = $this->getData();
if(isset($data->result) and $data->result == 'fail') return $this->sendError(400, $data->message);
$execution = $this->loadModel('execution')->getByID($data->id);
$this->send(201, $this->format($execution, 'openedBy:user,openedDate:time,lastEditedBy:user,lastEditedDate:time,closedBy:user,closedDate:time,canceledBy:user,canceledDate:time,PM:user,PO:user,RD:user,QD:user,whitelist:userList,begin:date,end:date,realBegan:date,realEnd:date,deleted:bool'));
}
/**
* Get drop menu.
*
* @access public
* @return void
*/
public function getDropMenu()
{
$control = $this->loadController('execution', 'ajaxGetDropMenu');
$control->ajaxGetDropMenu($this->request('executionID', 0), $this->request('module', 'execution'), $this->request('method', 'task'), $this->request('extra', ''));
$data = $this->getData();
if(isset($data->result) and $data->result == 'fail') return $this->sendError(400, $data->message);
$account = $this->app->user->account;
$projects = $data->data->projects;
$dropMenu = array('involved' => array(), 'other' => array(), 'closed' => array());
foreach($data->data->executions as $projectID => $projectExecutions)
{
foreach($projectExecutions as $execution)
{
if(helper::diffDate(date('Y-m-d'), $execution->end) > 0) $execution->delay = true;
$teams = $execution->teams;
$execution = $this->filterFields($execution, 'id,project,model,type,name,code,status,PM,delay');
$projectName = zget($projects, $execution->project, '');
if($projectName) $execution->name = $projectName . '/' . $execution->name;
if($execution->status == 'closed')
{
$dropMenu['closed'][] = $execution;
}
elseif($execution->status != 'done' and $execution->status != 'closed' and ($execution->PM == $account or isset($teams->$account)))
{
$dropMenu['involved'][] = $execution;
}
else
{
$dropMenu['other'][] = $execution;
}
}
}
$this->send(200, $dropMenu);
}
}
+2
View File
@@ -16,6 +16,8 @@ $routes['/tabs/:module'] = 'tabs';
$routes['/files'] = 'files';
$routes['/files/:id'] = 'file';
$routes['/feedbacks'] = 'feedbacks';
$routes['/options/:type'] = 'options';
$routes['/configurations'] = 'configs';
+1
View File
@@ -43,6 +43,7 @@ ALTER TABLE `zt_opportunity` MODIFY COLUMN `prevention` mediumtext NOT NULL;
ALTER TABLE `zt_opportunity` MODIFY COLUMN `resolution` mediumtext NOT NULL;
ALTER TABLE `zt_trainplan` MODIFY COLUMN `summary` mediumtext NOT NULL;
ALTER TABLE `zt_gapanalysis` MODIFY COLUMN `analysis` mediumtext NOT NULL;
ALTER TABLE `zt_bug` MODIFY COLUMN `entry` text NOT NULL;
ALTER TABLE `zt_file` MODIFY COLUMN `title` varchar(255) NOT NULL;
+1 -1
View File
@@ -206,7 +206,7 @@ CREATE TABLE IF NOT EXISTS `zt_bug` (
`result` mediumint(8) unsigned NOT NULL,
`repo` mediumint(8) unsigned NOT NULL,
`mr` mediumint(8) unsigned NOT NULL,
`entry` varchar(255) NOT NULL,
`entry` text NOT NULL,
`lines` varchar(10) NOT NULL,
`v1` varchar(40) NOT NULL,
`v2` varchar(40) NOT NULL,
+4 -1
View File
@@ -415,7 +415,10 @@ try {
},
del(data, index) {
if(data.length <= 1) return;
data.splice(index, 1)
for(let i = index+1; i < data.length; i++){
if(data[i].sub == data[index].sub) return data.splice(index, i - index)
}
data.splice(index, data.length - index)
},
changeType() {
if (!this.params[this.structType] || this.params[this.structType].length > 0) {
+1 -1
View File
@@ -48,7 +48,7 @@ js::set('struct', $struct);
<tr>
<td class="w-110px"></td>
<td colspan="2">
<body-field @change="changeAttr" :attr="struct.attribute" :struct-type="struct.type"></body-field>
<body-field @change="changeAttr" :attr="struct.attribute" :struct-type="struct.structType"></body-field>
<input type="hidden" name="attribute" v-model="attr">
</td>
</tr>
+1 -1
View File
@@ -60,7 +60,7 @@ $lang->dev->tableList['projectstory'] = '项目' . $lang->SRCommon;
$lang->dev->tableList['execution'] = $lang->executionCommon;
$lang->dev->tableList['release'] = '发布';
$lang->dev->tableList['story'] = $lang->SRCommon;
$lang->dev->tableList['storyspec'] = "{$lang->SRCommon}描述";
$lang->dev->tableList['storyspec'] = "需求描述";
$lang->dev->tableList['task'] = '任务';
$lang->dev->tableList['taskestimate'] = '任务预计';
$lang->dev->tableList['team'] = '团队';
+1 -1
View File
@@ -590,7 +590,7 @@ class doc extends control
if($from == 'lib')
{
$response['locate'] = $this->createLink('doc', 'objectLibs', "type=$objectType");
$response['locate'] = $this->createLink('doc', 'objectLibs', "type=$objectType&objectID=0&libID={$doc->lib}");
}
}
return $this->send($response);
+4
View File
@@ -131,6 +131,7 @@
</td>
</tr>
<?php endif;?>
<?php if(!in_array($execution->attribute, array('request', 'design', 'review'))): ?>
<tr>
<th><?php echo $lang->execution->manageProducts;?></th>
<td class='text-left' id='productsBox' colspan="2">
@@ -181,6 +182,9 @@
</div>
</td>
</tr>
<?php else: ?>
<?php echo html::hidden("products[]", key($linkedProducts));?>
<?php endif; ?>
<tr>
<th><?php echo $lang->execution->team;?></th>
<td colspan='2'><?php echo html::select('teamMembers[]', $users, array_keys($teamMembers), "class='form-control chosen' multiple"); ?></td>
+2
View File
@@ -190,6 +190,7 @@
</p>
</div>
</div>
<?php if(!in_array($execution->attribute, array('request', 'design', 'review'))): ?>
<div class="detail">
<div class="detail-title">
<strong><?php echo $lang->execution->manageProducts;?></strong>
@@ -208,6 +209,7 @@
</div>
</div>
</div>
<?php endif; ?>
<div class="detail">
<div class="detail-title"><strong><?php echo $lang->execution->linkPlan;?></strong></div>
<div class="detail-content">
+1
View File
@@ -57,6 +57,7 @@ function switchEncode(fileType)
if(fileType == 'word')
{
$('#tplBox').closest('tr').addClass('hidden');
$('#customFields').addClass('hidden');
}
else
{
+1
View File
@@ -79,3 +79,4 @@ $lang->programplan->error->planFinishSmall = 'The "End" date must be > the "Begi
$lang->programplan->error->percentOver = 'The sum of "Workload %" cannot exceed 100%.';
$lang->programplan->error->createdTask = 'The task is decomposed. Sub stages cannot be added.';
$lang->programplan->error->parentWorkload = 'The sum of the workload in the sub stage cannot be > that in the parent stage: %s.';
$lang->programplan->error->sameName = 'Stage name cannot be the same!';
+1
View File
@@ -80,3 +80,4 @@ $lang->programplan->error->percentOver = '工作量占比累计不应当超
$lang->programplan->error->createdTask = '已分解任务,不可添加子阶段';
$lang->programplan->error->parentWorkload = '子阶段的工作量之和不能大于父阶段的工作量:%s';
$lang->programplan->error->parentDuration = '子阶段计划开始、计划完成不能超过父阶段';
$lang->programplan->error->sameName = '阶段名称不能相同!';
+3
View File
@@ -433,6 +433,9 @@ class programplanModel extends model
$parentACL = $parentStage->acl;
}
$names = array_filter($names);
if(count(array_unique($names)) != count($names)) return dao::$errors['message'][] = $this->lang->programplan->error->sameName;
$datas = array();
foreach($names as $key => $name)
{
+1
View File
@@ -1960,6 +1960,7 @@ class projectModel extends model
->where('t1.root')->eq((int)$projectID)
->andWhere('t1.type')->eq($type)
->andWhere('t2.deleted')->eq('0')
->beginIF($this->config->vision)->andWhere("CONCAT(',', t2.visions, ',')")->like("%,{$this->config->vision},%")->fi()
->fetchPairs('account', 'realname');
return array('' => '') + $members;
+1 -1
View File
@@ -1657,7 +1657,7 @@ class repoModel extends model
$this->post->set('realStarted', date('Y-m-d'));
$this->post->set('currentConsumed', $this->post->consumed);
$this->post->set('consumed', $this->post->consumed + $task->consumed);
$changes = $this->task->finish($taskID);
$changes = $this->task->finish($taskID, 'DEVOPS');
foreach($this->createActionChanges($log, $repoRoot, $scm) as $change) $changes[] = $change;
if($changes)
{
+5
View File
@@ -0,0 +1,5 @@
<?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';
+1 -1
View File
@@ -21,7 +21,7 @@ $lang->stage->view = '阶段详情';
$lang->stage->common = '阶段';
$lang->stage->id = '编号';
$lang->stage->name = '阶段名称';
$lang->stage->type = '阶段分类';
$lang->stage->type = '阶段类型';
$lang->stage->percent = '工作量占比';
$lang->stage->setType = '阶段类型';
+11 -2
View File
@@ -34,6 +34,7 @@ class stageModel extends model
$this->dao->insert(TABLE_STAGE)
->data($stage)
->autoCheck()
->batchCheck($this->config->stage->create->requiredFields, 'notempty')
->checkIF($stage->percent != '', 'percent', 'float')
->exec();
@@ -67,7 +68,10 @@ class stageModel extends model
$stage->createdBy = $this->app->user->account;
$stage->createdDate = helper::today();
$this->dao->insert(TABLE_STAGE)->data($stage)->autoCheck()->checkIF($stage->percent != '', 'percent', 'float')->exec();
$this->dao->insert(TABLE_STAGE)->data($stage)->autoCheck()
->batchCheck($this->config->stage->create->requiredFields, 'notempty')
->checkIF($stage->percent != '', 'percent', 'float')
->exec();
if(dao::isError()) return false;
@@ -98,7 +102,12 @@ class stageModel extends model
if(round($totalPercent + $stage->percent - $oldStage->percent) > 100) return dao::$errors['message'][] = $this->lang->stage->error->percentOver;
$this->dao->update(TABLE_STAGE)->data($stage)->autoCheck()->checkIF($stage->percent != '', 'percent', 'float')->where('id')->eq((int)$stageID)->exec();
$this->dao->update(TABLE_STAGE)
->data($stage)
->autoCheck()
->batchCheck($this->config->stage->edit->requiredFields, 'notempty')
->checkIF($stage->percent != '', 'percent', 'float')->where('id')->eq((int)$stageID)
->exec();
if(!dao::isError()) return common::createChanges($oldStage, $stage);
return false;
+4 -3
View File
@@ -11,6 +11,7 @@
*/
?>
<?php include '../../common/view/header.html.php';?>
<style>.c-percent, .c-type {width:150px;}</style>
<div id="mainContent" class="main-content fade">
<div class="main-header">
<h2><?php echo $lang->stage->batchCreate;?></h2>
@@ -19,10 +20,10 @@
<table class="table table-form">
<thead>
<tr>
<th class='w-50px'><?php echo $lang->stage->id;?></th>
<th class='c-id'><?php echo $lang->stage->id;?></th>
<th><?php echo $lang->stage->name;?></th>
<th class='w-200px'><?php echo $lang->stage->percent;?></th>
<th class='w-200px'><?php echo $lang->stage->type;?></th>
<th class='c-percent required'><?php echo $lang->stage->percent;?></th>
<th class='c-type required'><?php echo $lang->stage->type;?></th>
</tr>
</thead>
<tbody>
+1 -1
View File
@@ -978,7 +978,7 @@ class story extends control
$version = $this->dao->findById($storyID)->from(TABLE_STORY)->fetch('version');
$files = $this->loadModel('file')->saveUpload($story->type, $storyID, $version);
if(empty($files) and $this->post->uid != '') $files = $this->file->getPairs($_SESSION['album']['used'][$this->post->uid]);
if(empty($files) and $this->post->uid != '' and isset($_SESSION['album']['used'][$this->post->uid])) $files = $this->file->getPairs($_SESSION['album']['used'][$this->post->uid]);
if($this->post->comment != '' or !empty($changes) or !empty($files))
{
+1
View File
@@ -0,0 +1 @@
.table td {text-align: center;}
+5 -5
View File
@@ -457,14 +457,14 @@ class storyModel extends model
$stories = $result['data'];
$module = 0;
$plan = 0;
$plan = '';
$pri = 0;
$source = '';
foreach($stories->title as $i => $title)
{
$module = $stories->module[$i] == 'ditto' ? $module : $stories->module[$i];
$plan = isset($stories->plan[$i]) ? ($stories->plan[$i] == 'ditto' ? $plan : $stories->plan[$i]) : 0;
$plan = isset($stories->plan[$i]) ? ($stories->plan[$i] == 'ditto' ? $plan : $stories->plan[$i]) : '';
$pri = $stories->pri[$i] == 'ditto' ? $pri : $stories->pri[$i];
$source = $stories->source[$i] == 'ditto' ? $source : $stories->source[$i];
$stories->module[$i] = (int)$module;
@@ -1520,7 +1520,7 @@ class storyModel extends model
$newStory = new stdClass();
$newStory->parent = '-1';
$newStory->plan = 0;
$newStory->plan = '';
$newStory->lastEditedBy = $this->app->user->account;
$newStory->lastEditedDate = $now;
$newStory->childStories = trim($oldStory->childStories . ',' . $childStories, ',');
@@ -1624,7 +1624,7 @@ class storyModel extends model
$story->duplicateStory = $data->duplicateStoryIDList[$storyID] ? $data->duplicateStoryIDList[$storyID] : $oldStory->duplicateStory;
$story->childStories = $data->childStoriesIDList[$storyID] ? $data->childStoriesIDList[$storyID] : $oldStory->childStories;
if($story->closedReason != 'done') $story->plan = 0;
if($story->closedReason != 'done') $story->plan = '';
$stories[$storyID] = $story;
unset($story);
@@ -2690,7 +2690,7 @@ class storyModel extends model
{
$storyQuery = str_replace($allBranch, '1', $storyQuery);
}
elseif($branch !== 'all')
elseif($branch !== 'all' and $queryProductID != 'all')
{
if($branch and strpos($storyQuery, '`branch` =') === false) $storyQuery .= " AND `branch` in($branch)";
}
+2 -5
View File
@@ -40,11 +40,9 @@
<?php endif;?>
</th>
<?php endif;?>
<?php if($config->URAndSR):?>
<th><?php echo $lang->story->story;?></th>
<?php else:?>
<?php if($this->app->rawModule == 'projectstory'): ?>
<th <?php echo $style;?>>
<?php if($this->app->rawModule == 'projectstory'): ?>
<div class="dropdown">
<?php echo html::a('javascript:;', "<i class='icon icon-product'></i>". $projectProducts[$productID]->name . '<span class="caret"></span>', '', 'class="dropdown-toggle" data-toggle="dropdown"');?>
<ul class="dropdown-menu">
@@ -53,7 +51,6 @@
<?php endforeach;?>
</ul>
</div>
<?php endif;?>
</th>
<?php endif;?>
<th><?php echo $lang->story->tasks;?></th>
@@ -84,7 +81,7 @@
<?php $i = 1;?>
<?php foreach($track as $storyID => $story):?>
<?php if($i != 1) echo '<tr>';?>
<td style='padding-left: 10px;'>
<td>
<?php if(isset($story->parent) and $story->parent > 0):?><span class="label label-badge label-light" title="<?php echo $this->lang->story->children;?>"><?php echo $this->lang->story->childrenAB;?></span><?php endif;?>
<?php echo html::a($this->createLink($module, 'view', "storyID=$storyID"), $story->title, '',"title='$story->title' data-app='$tab'");?>
</td>
+1 -1
View File
@@ -465,7 +465,7 @@ class task extends control
$changes = $this->task->update($taskID);
if(dao::isError()) return print(js::error(dao::getError()));
$files = $this->loadModel('file')->saveUpload('task', $taskID);
if(empty($files) and $this->post->uid != '') $files = $this->file->getPairs($_SESSION['album']['used'][$this->post->uid]);
if(empty($files) and $this->post->uid != '' and isset($_SESSION['album']['used'][$this->post->uid])) $files = $this->file->getPairs($_SESSION['album']['used'][$this->post->uid]);
}
$task = $this->task->getById($taskID);
+1 -1
View File
@@ -1709,7 +1709,7 @@ class taskModel extends model
$now = helper::now();
$today = helper::today();
if(strpos($this->config->task->finish->requiredFields, 'comment') !== false and !$this->post->comment)
if($extra != 'DEVOPS' and strpos($this->config->task->finish->requiredFields, 'comment') !== false and !$this->post->comment)
{
dao::$errors[] = sprintf($this->lang->error->notempty, $this->lang->comment);
return false;
+1
View File
@@ -6,3 +6,4 @@
.cycleConfig .tab-pane .input-group {width: 380px;}
.nameBox {display: grid;}
.pri-selector > .btn {padding: 5px 12px;}
#assignedTo_chosen.chosen-container .chosen-results {max-height: 180px;}
+1 -1
View File
@@ -642,7 +642,7 @@ class upgrade extends control
$this->dao->update(TABLE_PROJECT)->set('name')->eq($projectName)->where('id')->eq($projectID)->exec();
}
return print(js::closeModal('parent.parent', ''));
return print(js::reload('parent.parent', ''));
}
$objectGroup = array();
+1 -1
View File
@@ -366,7 +366,7 @@ $(function()
$(target).removeClass('hidden');
/* Replace program name. */
if($("[id^='productLines\[" + currentLine +"\]'").prop('checked')) $('#programName').val($(this).text());
if(!$('#programName').val() && $("[id^='productLines\[" + currentLine +"\]'").prop('checked')) $('#programName').val($(this).text());
/* Replace project name. */
var productID = $(target).find('.lineGroup .productList input[name*="product"]').val();
+32 -4
View File
@@ -4528,6 +4528,34 @@ class upgradeModel extends model
if(isset($data->projectName) and $data->projectType == 'execution' and empty($data->projectName)) return print(js::alert(sprintf($this->lang->error->notempty, $this->lang->upgrade->projectName)));
if($data->projectType == 'project')
{
$projectPairs = $this->dao->select('id,name')->from(TABLE_EXECUTION)
->where('deleted')->eq('0')
->andWhere('id')->in($projectIdList)
->fetchPairs();
$projectNames = array();
$duplicateList = '';
foreach($projectPairs as $projectID => $projectName)
{
if(isset($projectNames[$projectName]))
{
$duplicateList .= "$projectID,";
$duplicateList .= "{$projectNames[$projectName]},";
continue;
}
$projectNames[$projectName] = $projectID;
}
if($duplicateList)
{
echo "<script>new parent.$.zui.ModalTrigger({url: parent.$.createLink('upgrade', 'renameObject', 'type=project&duplicateList=$duplicateList', '', 1), type: 'iframe', width:'40%'}).show();</script>";
die;
}
}
/* Insert program. */
$program = new stdclass();
$program->name = $data->programName;
@@ -6183,22 +6211,22 @@ class upgradeModel extends model
}
}
/**
/**
* Update the project status.
*
* @access public
* @return void
*/
*/
public function updateProjectStatus()
{
$projects = $this->dao->select('*')->from(TABLE_PROJECT)
$projects = $this->dao->select('*')->from(TABLE_PROJECT)
->where('deleted')->eq('0')
->andWhere('status')->eq('doing')
->andWhere('type')->eq('project')
->andWhere('realBegan')->eq('0000-00-00')
->fetchAll('id');
if(empty($projects)) return;
if(empty($projects)) return;
$projectIDList = array_keys($projects);